@opensumi/ide-editor 2.21.13 → 2.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (238) hide show
  1. package/lib/browser/breadcrumb/default.js.map +1 -1
  2. package/lib/browser/breadcrumb/document-symbol.js.map +1 -1
  3. package/lib/browser/breadcrumb/index.js.map +1 -1
  4. package/lib/browser/component.js.map +1 -1
  5. package/lib/browser/decoration-applier.js.map +1 -1
  6. package/lib/browser/diff/compare.js.map +1 -1
  7. package/lib/browser/diff/index.d.ts.map +1 -1
  8. package/lib/browser/diff/index.js +1 -1
  9. package/lib/browser/diff/index.js.map +1 -1
  10. package/lib/browser/doc-cache/empty-doc-cache.js.map +1 -1
  11. package/lib/browser/doc-cache/local-storage-cache.js.map +1 -1
  12. package/lib/browser/doc-model/editor-document-model-service.js +3 -3
  13. package/lib/browser/doc-model/editor-document-model-service.js.map +1 -1
  14. package/lib/browser/doc-model/editor-document-model.d.ts +8 -0
  15. package/lib/browser/doc-model/editor-document-model.d.ts.map +1 -1
  16. package/lib/browser/doc-model/editor-document-model.js +61 -11
  17. package/lib/browser/doc-model/editor-document-model.js.map +1 -1
  18. package/lib/browser/doc-model/editor-document-registry.js.map +1 -1
  19. package/lib/browser/doc-model/override.js.map +1 -1
  20. package/lib/browser/doc-model/save-task.js +1 -1
  21. package/lib/browser/doc-model/save-task.js.map +1 -1
  22. package/lib/browser/doc-model/saveParticipants.d.ts +7 -7
  23. package/lib/browser/doc-model/saveParticipants.d.ts.map +1 -1
  24. package/lib/browser/doc-model/saveParticipants.js +69 -38
  25. package/lib/browser/doc-model/saveParticipants.js.map +1 -1
  26. package/lib/browser/doc-model/types.d.ts +1 -1
  27. package/lib/browser/doc-model/types.d.ts.map +1 -1
  28. package/lib/browser/editor-collection.service.d.ts +8 -3
  29. package/lib/browser/editor-collection.service.d.ts.map +1 -1
  30. package/lib/browser/editor-collection.service.js +75 -66
  31. package/lib/browser/editor-collection.service.js.map +1 -1
  32. package/lib/browser/editor-opener.js.map +1 -1
  33. package/lib/browser/editor.contribution.d.ts.map +1 -1
  34. package/lib/browser/editor.contribution.js +31 -3
  35. package/lib/browser/editor.contribution.js.map +1 -1
  36. package/lib/browser/editor.decoration.service.d.ts +5 -6
  37. package/lib/browser/editor.decoration.service.d.ts.map +1 -1
  38. package/lib/browser/editor.decoration.service.js +9 -1
  39. package/lib/browser/editor.decoration.service.js.map +1 -1
  40. package/lib/browser/editor.override.js +2 -2
  41. package/lib/browser/editor.override.js.map +1 -1
  42. package/lib/browser/editor.status-bar.service.js.map +1 -1
  43. package/lib/browser/editor.view.d.ts.map +1 -1
  44. package/lib/browser/editor.view.js +20 -23
  45. package/lib/browser/editor.view.js.map +1 -1
  46. package/lib/browser/error.d.ts +1 -1
  47. package/lib/browser/error.d.ts.map +1 -1
  48. package/lib/browser/feature.js.map +1 -1
  49. package/lib/browser/format/format.service.js.map +1 -1
  50. package/lib/browser/format/formatterSelect.js.map +1 -1
  51. package/lib/browser/fs-resource/file-tree-set.js +6 -6
  52. package/lib/browser/fs-resource/file-tree-set.js.map +1 -1
  53. package/lib/browser/fs-resource/fs-editor-doc.js +1 -1
  54. package/lib/browser/fs-resource/fs-editor-doc.js.map +1 -1
  55. package/lib/browser/fs-resource/fs-resource.js.map +1 -1
  56. package/lib/browser/fs-resource/index.js.map +1 -1
  57. package/lib/browser/history/index.d.ts.map +1 -1
  58. package/lib/browser/history/index.js +2 -1
  59. package/lib/browser/history/index.js.map +1 -1
  60. package/lib/browser/index.d.ts.map +1 -1
  61. package/lib/browser/index.js +2 -0
  62. package/lib/browser/index.js.map +1 -1
  63. package/lib/browser/language/diagnostic-collection.d.ts +5 -5
  64. package/lib/browser/language/diagnostic-collection.d.ts.map +1 -1
  65. package/lib/browser/language/diagnostic-collection.js +1 -1
  66. package/lib/browser/language/diagnostic-collection.js.map +1 -1
  67. package/lib/browser/language/language-status.contribution.js.map +1 -1
  68. package/lib/browser/language/language-status.service.js.map +1 -1
  69. package/lib/browser/language/language.service.d.ts +4 -3
  70. package/lib/browser/language/language.service.d.ts.map +1 -1
  71. package/lib/browser/language/language.service.js +8 -2
  72. package/lib/browser/language/language.service.js.map +1 -1
  73. package/lib/browser/menu/editor.context.js +3 -3
  74. package/lib/browser/menu/editor.context.js.map +1 -1
  75. package/lib/browser/menu/editor.menu.d.ts +2 -2
  76. package/lib/browser/menu/editor.menu.d.ts.map +1 -1
  77. package/lib/browser/menu/editor.menu.js +6 -8
  78. package/lib/browser/menu/editor.menu.js.map +1 -1
  79. package/lib/browser/menu/open-type-menu.contribution.js +9 -9
  80. package/lib/browser/menu/open-type-menu.contribution.js.map +1 -1
  81. package/lib/browser/menu/title-context.menu.js.map +1 -1
  82. package/lib/browser/merge-editor/merge-editor.contribution.d.ts +8 -0
  83. package/lib/browser/merge-editor/merge-editor.contribution.d.ts.map +1 -0
  84. package/lib/browser/merge-editor/merge-editor.contribution.js +29 -0
  85. package/lib/browser/merge-editor/merge-editor.contribution.js.map +1 -0
  86. package/lib/browser/merge-editor/merge-editor.provider.d.ts +8 -0
  87. package/lib/browser/merge-editor/merge-editor.provider.d.ts.map +1 -0
  88. package/lib/browser/merge-editor/merge-editor.provider.js +44 -0
  89. package/lib/browser/merge-editor/merge-editor.provider.js.map +1 -0
  90. package/lib/browser/monaco-contrib/callHierarchy/callHierarchy.contribution.js.map +1 -1
  91. package/lib/browser/monaco-contrib/callHierarchy/callHierarchy.service.js +7 -7
  92. package/lib/browser/monaco-contrib/callHierarchy/callHierarchy.service.js.map +1 -1
  93. package/lib/browser/monaco-contrib/command/command.service.d.ts +1 -1
  94. package/lib/browser/monaco-contrib/command/command.service.d.ts.map +1 -1
  95. package/lib/browser/monaco-contrib/command/command.service.js.map +1 -1
  96. package/lib/browser/monaco-contrib/tokenizer/textmate-registry.js.map +1 -1
  97. package/lib/browser/monaco-contrib/tokenizer/textmate.service.d.ts +2 -1
  98. package/lib/browser/monaco-contrib/tokenizer/textmate.service.d.ts.map +1 -1
  99. package/lib/browser/monaco-contrib/tokenizer/textmate.service.js +5 -2
  100. package/lib/browser/monaco-contrib/tokenizer/textmate.service.js.map +1 -1
  101. package/lib/browser/monaco-contrib/typeHierarchy/typeHierarchy.contribution.js.map +1 -1
  102. package/lib/browser/monaco-contrib/typeHierarchy/typeHierarchy.service.js +7 -7
  103. package/lib/browser/monaco-contrib/typeHierarchy/typeHierarchy.service.js.map +1 -1
  104. package/lib/browser/navigation.module.less +1 -1
  105. package/lib/browser/navigation.view.d.ts.map +1 -1
  106. package/lib/browser/navigation.view.js +26 -15
  107. package/lib/browser/navigation.view.js.map +1 -1
  108. package/lib/browser/preference/contribution.js.map +1 -1
  109. package/lib/browser/preference/converter.d.ts +3 -3
  110. package/lib/browser/preference/converter.d.ts.map +1 -1
  111. package/lib/browser/preference/schema.d.ts +1 -1
  112. package/lib/browser/preference/schema.d.ts.map +1 -1
  113. package/lib/browser/preference/schema.js +8 -8
  114. package/lib/browser/preference/schema.js.map +1 -1
  115. package/lib/browser/quick-open/go-to-line.js.map +1 -1
  116. package/lib/browser/quick-open/workspace-symbol-quickopen.d.ts.map +1 -1
  117. package/lib/browser/quick-open/workspace-symbol-quickopen.js +2 -2
  118. package/lib/browser/quick-open/workspace-symbol-quickopen.js.map +1 -1
  119. package/lib/browser/resource.service.js.map +1 -1
  120. package/lib/browser/tab.view.d.ts +1 -1
  121. package/lib/browser/tab.view.d.ts.map +1 -1
  122. package/lib/browser/tab.view.js +2 -2
  123. package/lib/browser/tab.view.js.map +1 -1
  124. package/lib/browser/types.d.ts +5 -5
  125. package/lib/browser/types.d.ts.map +1 -1
  126. package/lib/browser/untitled-resource.js +1 -1
  127. package/lib/browser/untitled-resource.js.map +1 -1
  128. package/lib/browser/view/suggest-widget.js.map +1 -1
  129. package/lib/browser/workbench-editor.service.d.ts +17 -0
  130. package/lib/browser/workbench-editor.service.d.ts.map +1 -1
  131. package/lib/browser/workbench-editor.service.js +259 -176
  132. package/lib/browser/workbench-editor.service.js.map +1 -1
  133. package/lib/common/doc-cache.d.ts +2 -2
  134. package/lib/common/doc-cache.d.ts.map +1 -1
  135. package/lib/common/editor.d.ts +20 -6
  136. package/lib/common/editor.d.ts.map +1 -1
  137. package/lib/common/editor.js +12 -1
  138. package/lib/common/editor.js.map +1 -1
  139. package/lib/common/language.d.ts +18 -7
  140. package/lib/common/language.d.ts.map +1 -1
  141. package/lib/common/language.js +15 -7
  142. package/lib/common/language.js.map +1 -1
  143. package/lib/common/mocks/workbench-editor.service.js.map +1 -1
  144. package/lib/common/resource.d.ts +8 -2
  145. package/lib/common/resource.d.ts.map +1 -1
  146. package/lib/common/resource.js +1 -0
  147. package/lib/common/resource.js.map +1 -1
  148. package/package.json +17 -16
  149. package/src/browser/breadcrumb/default.ts +299 -0
  150. package/src/browser/breadcrumb/document-symbol.ts +187 -0
  151. package/src/browser/breadcrumb/index.ts +96 -0
  152. package/src/browser/component.ts +204 -0
  153. package/src/browser/decoration-applier.ts +256 -0
  154. package/src/browser/diff/compare.ts +99 -0
  155. package/src/browser/diff/index.ts +81 -0
  156. package/src/browser/doc-cache/empty-doc-cache.ts +26 -0
  157. package/src/browser/doc-cache/index.ts +2 -0
  158. package/src/browser/doc-cache/local-storage-cache.ts +67 -0
  159. package/src/browser/doc-model/editor-document-error.ts +10 -0
  160. package/src/browser/doc-model/editor-document-model-service.ts +346 -0
  161. package/src/browser/doc-model/editor-document-model.ts +690 -0
  162. package/src/browser/doc-model/editor-document-registry.ts +119 -0
  163. package/src/browser/doc-model/editor-is-fn.ts +9 -0
  164. package/src/browser/doc-model/main.ts +4 -0
  165. package/src/browser/doc-model/override.ts +49 -0
  166. package/src/browser/doc-model/save-task.ts +88 -0
  167. package/src/browser/doc-model/saveParticipants.ts +227 -0
  168. package/src/browser/doc-model/types.ts +350 -0
  169. package/src/browser/editor-collection.service.ts +790 -0
  170. package/src/browser/editor-opener.ts +44 -0
  171. package/src/browser/editor.contribution.ts +1438 -0
  172. package/src/browser/editor.decoration.service.ts +247 -0
  173. package/src/browser/editor.less +4 -0
  174. package/src/browser/editor.module.less +548 -0
  175. package/src/browser/editor.override.ts +133 -0
  176. package/src/browser/editor.status-bar.service.ts +116 -0
  177. package/src/browser/editor.view.tsx +623 -0
  178. package/src/browser/error.ts +21 -0
  179. package/src/browser/feature.ts +63 -0
  180. package/src/browser/format/format.service.ts +95 -0
  181. package/src/browser/format/formatterSelect.ts +82 -0
  182. package/src/browser/fs-resource/file-tree-set.ts +126 -0
  183. package/src/browser/fs-resource/fs-editor-doc.ts +213 -0
  184. package/src/browser/fs-resource/fs-resource.ts +247 -0
  185. package/src/browser/fs-resource/index.ts +27 -0
  186. package/src/browser/grid/grid.service.ts +288 -0
  187. package/src/browser/history/index.ts +228 -0
  188. package/src/browser/index.ts +236 -0
  189. package/src/browser/language/diagnostic-collection.ts +83 -0
  190. package/src/browser/language/language-status.contribution.ts +81 -0
  191. package/src/browser/language/language-status.service.ts +32 -0
  192. package/src/browser/language/language.service.ts +185 -0
  193. package/src/browser/menu/editor.context.ts +186 -0
  194. package/src/browser/menu/editor.menu.ts +45 -0
  195. package/src/browser/menu/open-type-menu.contribution.ts +90 -0
  196. package/src/browser/menu/title-context.menu.ts +54 -0
  197. package/src/browser/merge-editor/merge-editor.contribution.ts +25 -0
  198. package/src/browser/merge-editor/merge-editor.provider.ts +36 -0
  199. package/src/browser/monaco-contrib/callHierarchy/callHierarchy.contribution.ts +78 -0
  200. package/src/browser/monaco-contrib/callHierarchy/callHierarchy.service.ts +160 -0
  201. package/src/browser/monaco-contrib/command/command.service.ts +438 -0
  202. package/src/browser/monaco-contrib/index.ts +4 -0
  203. package/src/browser/monaco-contrib/tokenizer/textmate-registry.ts +107 -0
  204. package/src/browser/monaco-contrib/tokenizer/textmate-tokenizer.ts +104 -0
  205. package/src/browser/monaco-contrib/tokenizer/textmate.service.ts +925 -0
  206. package/src/browser/monaco-contrib/typeHierarchy/typeHierarchy.contribution.ts +78 -0
  207. package/src/browser/monaco-contrib/typeHierarchy/typeHierarchy.service.ts +154 -0
  208. package/src/browser/navigation.module.less +96 -0
  209. package/src/browser/navigation.view.tsx +254 -0
  210. package/src/browser/preference/contribution.ts +8 -0
  211. package/src/browser/preference/converter.ts +793 -0
  212. package/src/browser/preference/schema.ts +1896 -0
  213. package/src/browser/preference/util.ts +14 -0
  214. package/src/browser/quick-open/go-to-line.ts +164 -0
  215. package/src/browser/quick-open/workspace-symbol-quickopen.ts +276 -0
  216. package/src/browser/resource.service.ts +263 -0
  217. package/src/browser/tab.view.tsx +514 -0
  218. package/src/browser/types.ts +467 -0
  219. package/src/browser/untitled-resource.ts +223 -0
  220. package/src/browser/view/editor.react.tsx +82 -0
  221. package/src/browser/view/react-hook.tsx +7 -0
  222. package/src/browser/view/suggest-widget.ts +77 -0
  223. package/src/browser/view/topPadding.ts +18 -0
  224. package/src/browser/workbench-editor.service.ts +2291 -0
  225. package/src/common/doc-cache.ts +117 -0
  226. package/src/common/editor.ts +799 -0
  227. package/src/common/index.ts +6 -0
  228. package/src/common/language-status.ts +33 -0
  229. package/src/common/language.ts +206 -0
  230. package/src/common/mocks/workbench-editor.service.ts +52 -0
  231. package/src/common/resource.ts +147 -0
  232. package/src/common/utils.ts +10 -0
  233. package/src/index.ts +1 -0
  234. package/lib/browser/component/scroll/scroll.d.ts +0 -2
  235. package/lib/browser/component/scroll/scroll.d.ts.map +0 -1
  236. package/lib/browser/component/scroll/scroll.js +0 -5
  237. package/lib/browser/component/scroll/scroll.js.map +0 -1
  238. package/lib/browser/component/scroll/scroll.module.less +0 -86
@@ -0,0 +1,2291 @@
1
+ import { observable } from 'mobx';
2
+
3
+ import { Injectable, Autowired, Injector, INJECTOR_TOKEN } from '@opensumi/di';
4
+ import {
5
+ FILE_COMMANDS,
6
+ ResizeEvent,
7
+ getSlotLocation,
8
+ AppConfig,
9
+ IContextKeyService,
10
+ ServiceNames,
11
+ IScopedContextKeyService,
12
+ IContextKey,
13
+ RecentFilesManager,
14
+ PreferenceService,
15
+ IOpenerService,
16
+ toMarkdown,
17
+ } from '@opensumi/ide-core-browser';
18
+ import { ResourceContextKey } from '@opensumi/ide-core-browser/lib/contextkey/resource';
19
+ import { IMergeEditorEditor, MergeEditorInputData } from '@opensumi/ide-core-browser/lib/monaco/merge-editor-widget';
20
+ import {
21
+ isUndefinedOrNull,
22
+ Schemes,
23
+ REPORT_NAME,
24
+ match,
25
+ localize,
26
+ MessageType,
27
+ debounce,
28
+ } from '@opensumi/ide-core-common';
29
+ import {
30
+ CommandService,
31
+ URI,
32
+ getDebugLogger,
33
+ MaybeNull,
34
+ Deferred,
35
+ Emitter as EventEmitter,
36
+ Event,
37
+ WithEventBus,
38
+ OnEvent,
39
+ StorageProvider,
40
+ IStorage,
41
+ STORAGE_NAMESPACE,
42
+ ContributionProvider,
43
+ Emitter,
44
+ formatLocalize,
45
+ IReporterService,
46
+ ILogger,
47
+ ReadyEvent,
48
+ IDisposable,
49
+ Disposable,
50
+ makeRandomHexString,
51
+ } from '@opensumi/ide-core-common';
52
+ import { IDialogService, IMessageService } from '@opensumi/ide-overlay';
53
+ import * as monaco from '@opensumi/monaco-editor-core/esm/vs/editor/editor.api';
54
+
55
+ import {
56
+ WorkbenchEditorService,
57
+ EditorCollectionService,
58
+ ICodeEditor,
59
+ IResource,
60
+ ResourceService,
61
+ IResourceOpenOptions,
62
+ IDiffEditor,
63
+ IDiffResource,
64
+ IEditor,
65
+ CursorStatus,
66
+ IEditorOpenType,
67
+ EditorGroupSplitAction,
68
+ IEditorGroup,
69
+ IOpenResourceResult,
70
+ IEditorGroupState,
71
+ ResourceDecorationChangeEvent,
72
+ IUntitledOptions,
73
+ SaveReason,
74
+ getSplitActionFromDragDrop,
75
+ } from '../common';
76
+
77
+ import { IEditorDocumentModelService, IEditorDocumentModelRef } from './doc-model/types';
78
+ import { EditorTabChangedError, isEditorError } from './error';
79
+ import { IGridEditorGroup, EditorGrid, SplitDirection, IEditorGridState } from './grid/grid.service';
80
+ import {
81
+ EditorComponentRegistry,
82
+ IEditorComponent,
83
+ GridResizeEvent,
84
+ DragOverPosition,
85
+ EditorGroupOpenEvent,
86
+ EditorGroupChangeEvent,
87
+ EditorSelectionChangeEvent,
88
+ EditorVisibleChangeEvent,
89
+ EditorConfigurationChangedEvent,
90
+ EditorGroupIndexChangedEvent,
91
+ EditorComponentRenderMode,
92
+ EditorGroupCloseEvent,
93
+ EditorGroupDisposeEvent,
94
+ BrowserEditorContribution,
95
+ ResourceOpenTypeChangedEvent,
96
+ EditorComponentDisposeEvent,
97
+ EditorActiveResourceStateChangedEvent,
98
+ CodeEditorDidVisibleEvent,
99
+ RegisterEditorComponentEvent,
100
+ AskSaveResult,
101
+ IMergeEditorResource,
102
+ EditorOpenType,
103
+ } from './types';
104
+ import { UntitledDocumentIdCounter } from './untitled-resource';
105
+
106
+ const MAX_CONFIRM_RESOURCES = 10;
107
+
108
+ const couldRevive = (r: IResource): boolean => !!(r.supportsRevive && !r.deleted);
109
+
110
+ @Injectable()
111
+ export class WorkbenchEditorServiceImpl extends WithEventBus implements WorkbenchEditorService {
112
+ editorGroups: EditorGroup[] = [];
113
+
114
+ _onDidEditorGroupsChanged = new EventEmitter<void>();
115
+ onDidEditorGroupsChanged: Event<void> = this._onDidEditorGroupsChanged.event;
116
+
117
+ private _sortedEditorGroups: EditorGroup[] | undefined = [];
118
+
119
+ @Autowired(INJECTOR_TOKEN)
120
+ private injector!: Injector;
121
+
122
+ @Autowired(ResourceService)
123
+ private resourceService: ResourceService;
124
+
125
+ private readonly _onActiveResourceChange = new EventEmitter<MaybeNull<IResource>>();
126
+ public readonly onActiveResourceChange: Event<MaybeNull<IResource>> = this._onActiveResourceChange.event;
127
+
128
+ private readonly _onActiveEditorUriChange = new EventEmitter<MaybeNull<URI>>();
129
+ public readonly onActiveEditorUriChange: Event<MaybeNull<URI>> = this._onActiveEditorUriChange.event;
130
+
131
+ private readonly _onCursorChange = new EventEmitter<CursorStatus>();
132
+ public readonly onCursorChange: Event<CursorStatus> = this._onCursorChange.event;
133
+
134
+ public topGrid: EditorGrid;
135
+
136
+ private _currentEditorGroup: IEditorGroup;
137
+
138
+ private _onDidCurrentEditorGroupChanged = new EventEmitter<IEditorGroup>();
139
+ onDidCurrentEditorGroupChanged: Event<IEditorGroup> = this._onDidCurrentEditorGroupChanged.event;
140
+
141
+ @Autowired(StorageProvider)
142
+ getStorage: StorageProvider;
143
+
144
+ @Autowired(IDialogService)
145
+ protected dialogService: IDialogService;
146
+
147
+ openedResourceState: IStorage;
148
+
149
+ private _restoring = true;
150
+
151
+ public contributionsReady = new Deferred<void>();
152
+
153
+ private initializing: Promise<any>;
154
+
155
+ public editorContextKeyService: IScopedContextKeyService;
156
+
157
+ private _domNode: HTMLElement;
158
+
159
+ @Autowired(IOpenerService)
160
+ openner: IOpenerService;
161
+
162
+ @Autowired(BrowserEditorContribution)
163
+ private readonly contributions: ContributionProvider<BrowserEditorContribution>;
164
+
165
+ @Autowired(IEditorDocumentModelService)
166
+ protected documentModelManager: IEditorDocumentModelService;
167
+
168
+ @Autowired()
169
+ private untitledIndex: UntitledDocumentIdCounter;
170
+
171
+ private untitledCloseIndex: number[] = [];
172
+
173
+ public gridReady = false;
174
+ private _onDidGridReady = new Emitter<void>();
175
+ public onDidGridReady = this._onDidGridReady.event;
176
+
177
+ constructor() {
178
+ super();
179
+ this.initialize();
180
+ }
181
+
182
+ setEditorContextKeyService(contextKeyService: IScopedContextKeyService): void {
183
+ this.editorContextKeyService = contextKeyService;
184
+ }
185
+
186
+ setCurrentGroup(editorGroup: EditorGroup) {
187
+ if (editorGroup) {
188
+ if (this._currentEditorGroup === editorGroup) {
189
+ return;
190
+ }
191
+ this._currentEditorGroup = editorGroup;
192
+ this._onActiveResourceChange.fire(editorGroup.currentResource);
193
+ this.eventBus.fire(
194
+ new EditorActiveResourceStateChangedEvent({
195
+ resource: editorGroup.currentResource,
196
+ openType: editorGroup.currentOpenType,
197
+ editorUri: this.currentEditor?.currentUri,
198
+ }),
199
+ );
200
+ this._onDidCurrentEditorGroupChanged.fire(this._currentEditorGroup);
201
+ }
202
+ }
203
+
204
+ @OnEvent(EditorGroupChangeEvent)
205
+ onEditorGroupChangeEvent(e: EditorGroupChangeEvent) {
206
+ if (e.payload.group === this.currentEditorGroup) {
207
+ this.eventBus.fire(
208
+ new EditorActiveResourceStateChangedEvent({
209
+ resource: e.payload.newResource,
210
+ openType: e.payload.newOpenType,
211
+ editorUri: this.currentEditor?.currentUri,
212
+ }),
213
+ );
214
+ }
215
+ }
216
+
217
+ getAllOpenedUris() {
218
+ const uris: URI[] = [];
219
+ for (const group of this.editorGroups) {
220
+ for (const resource of group.resources) {
221
+ const index = uris.findIndex((u) => u.isEqual(resource.uri));
222
+ if (index === -1) {
223
+ uris.push(resource.uri);
224
+ }
225
+ }
226
+ }
227
+ return uris;
228
+ }
229
+
230
+ async saveAll(includeUntitled?: boolean, reason?: SaveReason) {
231
+ for (const editorGroup of this.editorGroups) {
232
+ await editorGroup.saveAll(includeUntitled, reason);
233
+ }
234
+ }
235
+
236
+ hasDirty(): boolean {
237
+ for (const editorGroup of this.editorGroups) {
238
+ if (editorGroup.hasDirty()) {
239
+ return true;
240
+ }
241
+ }
242
+ return false;
243
+ }
244
+
245
+ calcDirtyCount(): number {
246
+ const countedUris = new Set<string>();
247
+ return this.editorGroups.reduce((pre, cur) => pre + cur.calcDirtyCount(countedUris), 0);
248
+ }
249
+
250
+ createEditorGroup(): EditorGroup {
251
+ const editorGroup = this.injector.get(EditorGroup, [this.generateRandomEditorGroupName()]);
252
+ this.editorGroups.push(editorGroup);
253
+ const currentWatchDisposer = new Disposable(
254
+ editorGroup.onDidEditorGroupBodyChanged(() => {
255
+ if (editorGroup === this.currentEditorGroup) {
256
+ if (!editorGroup.currentOpenType && editorGroup.currentResource) {
257
+ // 暂时状态,不发事件
258
+ } else {
259
+ this._onActiveResourceChange.fire(editorGroup.currentResource);
260
+ }
261
+ }
262
+ }),
263
+ editorGroup.onDidEditorFocusChange(() => {
264
+ if (editorGroup === this.currentEditorGroup) {
265
+ if (!editorGroup.currentOpenType && editorGroup.currentResource) {
266
+ // 暂时状态,不发事件
267
+ } else {
268
+ this._onActiveEditorUriChange.fire(editorGroup.currentOrPreviousFocusedEditor?.currentUri);
269
+ }
270
+ }
271
+ }),
272
+ );
273
+ editorGroup.addDispose({
274
+ dispose: () => {
275
+ currentWatchDisposer.dispose();
276
+ },
277
+ });
278
+ const groupChangeDisposer = editorGroup.onDidEditorGroupTabChanged(() => {
279
+ this.saveOpenedResourceState();
280
+ });
281
+ editorGroup.addDispose({
282
+ dispose: () => {
283
+ groupChangeDisposer.dispose();
284
+ },
285
+ });
286
+ editorGroup.onCurrentEditorCursorChange((e) => {
287
+ if (this._currentEditorGroup === editorGroup) {
288
+ this._onCursorChange.fire(e);
289
+ }
290
+ });
291
+
292
+ return editorGroup;
293
+ }
294
+
295
+ /**
296
+ * 随机生成一个不重复的editor Group
297
+ */
298
+ private generateRandomEditorGroupName() {
299
+ let name = makeRandomHexString(5);
300
+ while (this.editorGroups.findIndex((g) => g.name === name) !== -1) {
301
+ name = makeRandomHexString(5);
302
+ }
303
+ return name;
304
+ }
305
+
306
+ public initialize() {
307
+ if (!this.initializing) {
308
+ this.initializing = this.doInitialize();
309
+ }
310
+ return this.initializing;
311
+ }
312
+
313
+ private async doInitialize() {
314
+ this.openedResourceState = await this.initializeState();
315
+ await this.contributionsReady.promise;
316
+ await this.restoreState();
317
+ this._currentEditorGroup = this.editorGroups[0];
318
+ }
319
+
320
+ private async initializeState() {
321
+ const state = await this.getStorage(STORAGE_NAMESPACE.WORKBENCH);
322
+ return state;
323
+ }
324
+
325
+ public get currentEditor(): IEditor | null {
326
+ return this.currentEditorGroup && this.currentEditorGroup.currentEditor;
327
+ }
328
+
329
+ public get currentCodeEditor(): ICodeEditor | null {
330
+ return this.currentEditorGroup.currentCodeEditor;
331
+ }
332
+
333
+ public get currentEditorGroup(): EditorGroup {
334
+ return this._currentEditorGroup as any;
335
+ }
336
+
337
+ async open(uri: URI, options?: IResourceOpenOptions) {
338
+ await this.initialize();
339
+ let group = this.currentEditorGroup;
340
+ let groupIndex: number | undefined;
341
+ if (options && typeof options.groupIndex !== 'undefined') {
342
+ groupIndex = options.groupIndex;
343
+ } else if (options && options.relativeGroupIndex) {
344
+ groupIndex = this.currentEditorGroup.index + options.relativeGroupIndex;
345
+ }
346
+ if (typeof groupIndex === 'number' && groupIndex >= 0) {
347
+ if (groupIndex >= this.editorGroups.length) {
348
+ return group.open(uri, Object.assign({}, options, { split: EditorGroupSplitAction.Right }));
349
+ } else {
350
+ group = this.sortedEditorGroups[groupIndex] || this.currentEditorGroup;
351
+ }
352
+ }
353
+ return group.open(uri, options);
354
+ }
355
+
356
+ async openUris(uris: URI[]) {
357
+ await this.initialize();
358
+ await this.currentEditorGroup.openUris(uris);
359
+ return;
360
+ }
361
+
362
+ getEditorGroup(name: string): EditorGroup | undefined {
363
+ return this.editorGroups.find((g) => g.name === name);
364
+ }
365
+
366
+ get currentResource(): MaybeNull<IResource> {
367
+ if (!this.currentEditorGroup) {
368
+ return null;
369
+ }
370
+ return this.currentEditorGroup.currentResource;
371
+ }
372
+
373
+ removeGroup(group: EditorGroup) {
374
+ const index = this.editorGroups.findIndex((e) => e === group);
375
+ if (index !== -1) {
376
+ if (this.editorGroups.length === 1) {
377
+ return;
378
+ }
379
+ this.editorGroups.splice(index, 1);
380
+ if (this.currentEditorGroup === group) {
381
+ this.setCurrentGroup(this.editorGroups[0]);
382
+ }
383
+ for (let i = index; i < this.editorGroups.length; i++) {
384
+ this.eventBus.fire(
385
+ new EditorGroupIndexChangedEvent({
386
+ group: this.editorGroups[i],
387
+ index: i,
388
+ }),
389
+ );
390
+ }
391
+ }
392
+ }
393
+
394
+ public async saveOpenedResourceState() {
395
+ if (this._restoring) {
396
+ return;
397
+ }
398
+ const state: IEditorGridState = this.topGrid.serialize()!;
399
+ await this.openedResourceState.set('grid', state);
400
+ }
401
+
402
+ prepareContextKeyService() {
403
+ // contextKeys
404
+ const getLanguageFromModel = (uri: URI) => {
405
+ let result: string | null = null;
406
+ const modelRef = this.documentModelManager.getModelReference(uri, 'resourceContextKey');
407
+ if (modelRef) {
408
+ result = modelRef.instance.languageId;
409
+ modelRef.dispose();
410
+ }
411
+ return result;
412
+ };
413
+ const resourceContext = new ResourceContextKey(this.editorContextKeyService, (uri: URI) => {
414
+ const res = getLanguageFromModel(uri);
415
+ if (res) {
416
+ return res!;
417
+ } else {
418
+ return getLanguageFromModel(uri);
419
+ }
420
+ });
421
+ this.onActiveResourceChange((resource) => {
422
+ if (this.currentEditor && this.currentEditor.currentUri) {
423
+ resourceContext.set(this.currentEditor.currentUri);
424
+ } else {
425
+ if (resource) {
426
+ resourceContext.set(resource.uri);
427
+ } else {
428
+ resourceContext.reset();
429
+ }
430
+ }
431
+ });
432
+
433
+ if (this.currentEditor && this.currentEditor.currentUri) {
434
+ resourceContext.set(this.currentEditor.currentUri);
435
+ } else {
436
+ if (this.currentResource) {
437
+ resourceContext.set(this.currentResource.uri);
438
+ } else {
439
+ resourceContext.reset();
440
+ }
441
+ }
442
+ }
443
+
444
+ onDomCreated(domNode: HTMLElement) {
445
+ this._domNode = domNode;
446
+ if (this.editorContextKeyService) {
447
+ this.editorContextKeyService.attachToDomNode(domNode);
448
+ }
449
+ }
450
+
451
+ private notifyGroupChanged() {
452
+ this._sortedEditorGroups = undefined;
453
+ this._onDidEditorGroupsChanged.fire();
454
+ }
455
+
456
+ public async restoreState() {
457
+ let state: IEditorGridState = { editorGroup: { uris: [], previewIndex: -1 } };
458
+ state = this.openedResourceState.get<IEditorGridState>('grid', state);
459
+ this.topGrid = new EditorGrid();
460
+ this.topGrid.onDidGridAndDesendantStateChange(() => {
461
+ this._sortedEditorGroups = undefined;
462
+ this._onDidEditorGroupsChanged.fire();
463
+ });
464
+ const editorRestorePromises = [];
465
+ const promise = this.topGrid
466
+ .deserialize(state, () => this.createEditorGroup(), editorRestorePromises)
467
+ .then(() => {
468
+ if (this.topGrid.children.length === 0 && !this.topGrid.editorGroup) {
469
+ this.topGrid.setEditorGroup(this.createEditorGroup());
470
+ }
471
+ this.gridReady = true;
472
+ this._onDidGridReady.fire();
473
+ this.notifyGroupChanged();
474
+ });
475
+ Promise.all(editorRestorePromises).then(() => {
476
+ this._restoring = false;
477
+ for (const contribution of this.contributions.getContributions()) {
478
+ if (contribution.onDidRestoreState) {
479
+ contribution.onDidRestoreState();
480
+ }
481
+ }
482
+ });
483
+ return promise;
484
+ }
485
+
486
+ async closeAll(uri?: URI, force?: boolean) {
487
+ for (const group of this.editorGroups.slice(0)) {
488
+ if (uri) {
489
+ await group.close(uri, { force });
490
+ } else {
491
+ await group.closeAll();
492
+ }
493
+ }
494
+ }
495
+ /**
496
+ * Return true in order to prevent exit.
497
+ */
498
+ async closeAllOnlyConfirmOnce() {
499
+ const resources = [] as IResource<any>[];
500
+ for (const group of this.editorGroups) {
501
+ for (const resource of group.resources) {
502
+ resources.push(resource);
503
+ }
504
+ }
505
+ const shouldClose = await Promise.all(
506
+ resources.map(async (resource) => ({
507
+ shouldClose: await this.resourceService.shouldCloseResourceWithoutConfirm(resource),
508
+ resource,
509
+ })),
510
+ );
511
+
512
+ const toClose = shouldClose.filter((v) => v.shouldClose);
513
+ if (toClose.length === 0) {
514
+ return false;
515
+ }
516
+
517
+ // 询问用户是否保存
518
+ const buttons = {
519
+ [localize('file.prompt.dontSave', '不保存')]: AskSaveResult.REVERT,
520
+ [localize('file.prompt.save', '保存')]: AskSaveResult.SAVE,
521
+ [localize('file.prompt.cancel', '取消')]: AskSaveResult.CANCEL,
522
+ };
523
+ const files = toClose.slice(0, MAX_CONFIRM_RESOURCES);
524
+ let filesDetail = files.map((v) => v.resource.name).join('、');
525
+ if (toClose.length > MAX_CONFIRM_RESOURCES) {
526
+ if (toClose.length - MAX_CONFIRM_RESOURCES === 1) {
527
+ filesDetail += localize('file.prompt.more.one');
528
+ } else {
529
+ filesDetail += formatLocalize('file.prompt.more.number', toClose.length - MAX_CONFIRM_RESOURCES);
530
+ }
531
+ }
532
+ const selection = await this.dialogService.open(
533
+ toMarkdown(formatLocalize('saveNFilesChangesMessage', toClose.length, filesDetail), this.openner),
534
+ MessageType.Info,
535
+ Object.keys(buttons),
536
+ );
537
+ const result = buttons[selection!];
538
+ if (result === AskSaveResult.SAVE) {
539
+ await Promise.all(toClose.map((v) => this.resourceService.close?.(v.resource, AskSaveResult.SAVE)));
540
+ return false;
541
+ } else if (result === AskSaveResult.REVERT) {
542
+ await Promise.all(toClose.map((v) => this.resourceService.close?.(v.resource, AskSaveResult.REVERT)));
543
+ return false;
544
+ }
545
+ return true;
546
+ }
547
+
548
+ async close(uri: URI, force?: boolean) {
549
+ return this.closeAll(uri, force);
550
+ }
551
+
552
+ get sortedEditorGroups() {
553
+ if (!this._sortedEditorGroups) {
554
+ this._sortedEditorGroups = [];
555
+ this.topGrid.sortEditorGroups(this._sortedEditorGroups);
556
+ }
557
+ return this._sortedEditorGroups;
558
+ }
559
+
560
+ @OnEvent(EditorGroupCloseEvent)
561
+ handleOnCloseUntitledResource(e: EditorGroupCloseEvent) {
562
+ if (e.payload.resource.uri.scheme === Schemes.untitled) {
563
+ const { index } = e.payload.resource.uri.getParsedQuery();
564
+ this.untitledCloseIndex.push(parseInt(index, 10));
565
+ // 升序排序,每次可以去到最小的 index
566
+ this.untitledCloseIndex.sort((a, b) => a - b);
567
+ }
568
+ }
569
+
570
+ private createUntitledURI() {
571
+ // 优先从已删除的 index 中获取
572
+ const index = this.untitledCloseIndex.shift() || this.untitledIndex.id;
573
+ return new URI().withScheme(Schemes.untitled).withQuery(`name=Untitled-${index}&index=${index}`);
574
+ }
575
+
576
+ createUntitledResource(
577
+ options: IUntitledOptions = {
578
+ uri: this.createUntitledURI(),
579
+ },
580
+ ) {
581
+ return this.open(options.uri, {
582
+ preview: false,
583
+ focus: true,
584
+ ...options.resourceOpenOptions,
585
+ });
586
+ }
587
+ }
588
+
589
+ export interface IEditorCurrentState {
590
+ currentResource: IResource;
591
+
592
+ currentOpenType: IEditorOpenType;
593
+ }
594
+
595
+ /**
596
+ * Editor Group是一个可视的编辑区域
597
+ * 它由tab,editor,diff-editor,富组件container组成
598
+ */
599
+ @Injectable({ multiple: true })
600
+ export class EditorGroup extends WithEventBus implements IGridEditorGroup {
601
+ @Autowired()
602
+ collectionService!: EditorCollectionService;
603
+
604
+ @Autowired()
605
+ resourceService: ResourceService;
606
+
607
+ @Autowired()
608
+ editorComponentRegistry: EditorComponentRegistry;
609
+
610
+ @Autowired(WorkbenchEditorService)
611
+ workbenchEditorService: WorkbenchEditorServiceImpl;
612
+
613
+ @Autowired(IEditorDocumentModelService)
614
+ protected documentModelManager: IEditorDocumentModelService;
615
+
616
+ @Autowired(CommandService)
617
+ private commands: CommandService;
618
+
619
+ @Autowired(PreferenceService)
620
+ protected readonly preferenceService: PreferenceService;
621
+
622
+ @Autowired(RecentFilesManager)
623
+ private readonly recentFilesManager: RecentFilesManager;
624
+
625
+ @Autowired(IMessageService)
626
+ private messageService: IMessageService;
627
+
628
+ @Autowired(IReporterService)
629
+ private reporterService: IReporterService;
630
+
631
+ @Autowired(AppConfig)
632
+ config: AppConfig;
633
+
634
+ @Autowired(IOpenerService)
635
+ private readonly openerService: IOpenerService;
636
+
637
+ @Autowired(ILogger)
638
+ logger: ILogger;
639
+
640
+ codeEditor!: ICodeEditor;
641
+
642
+ diffEditor!: IDiffEditor;
643
+
644
+ mergeEditor!: IMergeEditorEditor;
645
+
646
+ private openingPromise: Map<string, Promise<IOpenResourceResult>> = new Map();
647
+
648
+ _onDidEditorFocusChange = this.registerDispose(new EventEmitter<void>());
649
+ onDidEditorFocusChange: Event<void> = this._onDidEditorFocusChange.event;
650
+
651
+ /**
652
+ * 当编辑器的tab部分发生变更
653
+ */
654
+ _onDidEditorGroupTabChanged = new EventEmitter<void>();
655
+ onDidEditorGroupTabChanged: Event<void> = this._onDidEditorGroupTabChanged.event;
656
+
657
+ /**
658
+ * 当编辑器的主体部分发生变更
659
+ */
660
+ _onDidEditorGroupBodyChanged = new EventEmitter<void>();
661
+ onDidEditorGroupBodyChanged: Event<void> = this._onDidEditorGroupBodyChanged.event;
662
+
663
+ /**
664
+ * 当编辑器有内容处于加载状态
665
+ */
666
+ _onDidEditorGroupContentLoading = new EventEmitter<IResource>();
667
+ onDidEditorGroupContentLoading: Event<IResource> = this._onDidEditorGroupContentLoading.event;
668
+
669
+ /**
670
+ * 每个group只能有一个preview
671
+ */
672
+ public previewURI: URI | null = null;
673
+
674
+ /**
675
+ * 当前打开的所有resource
676
+ */
677
+ // @observable.shallow
678
+ resources: IResource[] = [];
679
+
680
+ resourceStatus: Map<IResource, Promise<void>> = new Map();
681
+
682
+ // @observable.ref
683
+ _currentResource: IResource | null;
684
+
685
+ _currentOpenType: IEditorOpenType | null;
686
+
687
+ /**
688
+ * 当前resource的打开方式
689
+ */
690
+ private cachedResourcesActiveOpenTypes = new Map<string, IEditorOpenType>();
691
+
692
+ private cachedResourcesOpenTypes = new Map<string, IEditorOpenType[]>();
693
+
694
+ @observable.shallow
695
+ availableOpenTypes: IEditorOpenType[] = [];
696
+
697
+ activeComponents = new Map<IEditorComponent, IResource[]>();
698
+
699
+ activateComponentsProps = new Map<IEditorComponent, any>();
700
+
701
+ public grid: EditorGrid;
702
+
703
+ private holdDocumentModelRefs: Map<string, IEditorDocumentModelRef> = new Map();
704
+
705
+ private readonly toDispose: monaco.IDisposable[] = [];
706
+
707
+ private _contextKeyService: IContextKeyService;
708
+
709
+ private _resourceContext: ResourceContextKey;
710
+
711
+ private _editorLangIDContextKey: IContextKey<string>;
712
+
713
+ private _isInDiffEditorContextKey: IContextKey<boolean>;
714
+
715
+ private _diffResourceContextKey: ResourceContextKey;
716
+
717
+ private _isInDiffRightEditorContextKey: IContextKey<boolean>;
718
+
719
+ private _isInEditorComponentContextKey: IContextKey<boolean>;
720
+
721
+ private _prevDomHeight = 0;
722
+ private _prevDomWidth = 0;
723
+
724
+ private _codeEditorPendingLayout = false;
725
+ private _diffEditorPendingLayout = false;
726
+ private _mergeEditorPendingLayout = false;
727
+
728
+ // 当前为EditorComponent,且monaco光标变化时触发
729
+ private _onCurrentEditorCursorChange = new EventEmitter<CursorStatus>();
730
+ public onCurrentEditorCursorChange = this._onCurrentEditorCursorChange.event;
731
+
732
+ private resourceOpenHistory: URI[] = [];
733
+
734
+ private _domNode: MaybeNull<HTMLElement> = null;
735
+ private _diffEditorDomNode: MaybeNull<HTMLElement> = null;
736
+ private _diffEditorDomNodeAttached = false;
737
+ private _mergeEditorDomNode: MaybeNull<HTMLElement> = null;
738
+ private _mergeEditorDomNodeAttached = false;
739
+
740
+ private codeEditorReady = new ReadyEvent();
741
+
742
+ private diffEditorReady = new ReadyEvent();
743
+ private diffEditorDomReady = new ReadyEvent();
744
+
745
+ private mergeEditorReady = new ReadyEvent();
746
+ private mergeEditorDomReady = new ReadyEvent();
747
+
748
+ private _restoringState = false;
749
+
750
+ private updateContextKeyWhenEditorChangesFocusDisposer: IDisposable;
751
+
752
+ private _currentOrPreviousFocusedEditor: IEditor | null;
753
+
754
+ constructor(public readonly name: string) {
755
+ super();
756
+ this.eventBus.on(ResizeEvent, (e: ResizeEvent) => {
757
+ if (e.payload.slotLocation === getSlotLocation('@opensumi/ide-editor', this.config.layoutConfig)) {
758
+ this.doLayoutEditors();
759
+ }
760
+ });
761
+ this.eventBus.on(GridResizeEvent, (e: GridResizeEvent) => {
762
+ if (e.payload.gridId === this.grid.uid) {
763
+ this.doLayoutEditors();
764
+ }
765
+ });
766
+ this.eventBus.on(EditorComponentDisposeEvent, (e: EditorComponentDisposeEvent) => {
767
+ this.activeComponents.delete(e.payload);
768
+ this.activateComponentsProps.delete(e.payload);
769
+ });
770
+
771
+ this.listenToExplorerAutoRevealConfig();
772
+ }
773
+
774
+ private explorerAutoRevealConfig: boolean;
775
+ private listenToExplorerAutoRevealConfig() {
776
+ this.explorerAutoRevealConfig = !!this.preferenceService.get<boolean>('explorer.autoReveal');
777
+ this.disposables.push(
778
+ this.preferenceService.onPreferenceChanged((change) => {
779
+ if (change.preferenceName === 'explorer.autoReveal') {
780
+ this.explorerAutoRevealConfig = change.newValue;
781
+ }
782
+ }),
783
+ );
784
+ }
785
+
786
+ attachDiffEditorDom(domNode: HTMLElement | null | undefined) {
787
+ if (!this._diffEditorDomNodeAttached) {
788
+ this._diffEditorDomNode = domNode;
789
+ this.diffEditorDomReady.ready();
790
+ this._diffEditorDomNodeAttached = true;
791
+ }
792
+ }
793
+
794
+ attachMergeEditorDom(domNode: HTMLElement | null | undefined) {
795
+ if (!this._mergeEditorDomNodeAttached) {
796
+ this._mergeEditorDomNode = domNode;
797
+ this.mergeEditorDomReady.ready();
798
+ this._mergeEditorDomNodeAttached = true;
799
+ }
800
+ }
801
+
802
+ attachToDom(domNode: HTMLElement | null | undefined) {
803
+ this._domNode = domNode;
804
+ if (domNode) {
805
+ (this.contextKeyService as IScopedContextKeyService).attachToDomNode(domNode);
806
+ this.layoutEditors();
807
+ }
808
+ }
809
+
810
+ layoutEditors() {
811
+ if (this._domNode) {
812
+ const currentWidth = this._domNode.offsetWidth;
813
+ const currentHeight = this._domNode.offsetHeight;
814
+ if (currentWidth !== this._prevDomWidth || currentHeight !== this._prevDomHeight) {
815
+ this.doLayoutEditors();
816
+ }
817
+ this._prevDomWidth = currentWidth;
818
+ this._prevDomHeight = currentHeight;
819
+ }
820
+ }
821
+
822
+ @debounce(100)
823
+ doLayoutEditors() {
824
+ if (this.codeEditor) {
825
+ if (this.currentOpenType && this.currentOpenType.type === EditorOpenType.code) {
826
+ this.codeEditor.layout();
827
+ this._codeEditorPendingLayout = false;
828
+ } else {
829
+ this._codeEditorPendingLayout = true;
830
+ }
831
+ }
832
+ if (this.diffEditor) {
833
+ if (this.currentOpenType && this.currentOpenType.type === EditorOpenType.diff) {
834
+ this.diffEditor.layout();
835
+ this._diffEditorPendingLayout = false;
836
+ } else {
837
+ this._diffEditorPendingLayout = true;
838
+ }
839
+ }
840
+ if (this.mergeEditor) {
841
+ if (this.currentOpenType && this.currentOpenType.type === EditorOpenType.mergeEditor) {
842
+ // this.mergeEditor.layout();
843
+ this._mergeEditorPendingLayout = false;
844
+ } else {
845
+ this._mergeEditorPendingLayout = true;
846
+ }
847
+ }
848
+ }
849
+
850
+ setContextKeys() {
851
+ if (!this._resourceContext) {
852
+ const getLanguageFromModel = (uri: URI) => {
853
+ let result: string | null = null;
854
+ const modelRef = this.documentModelManager.getModelReference(uri, 'resourceContextKey');
855
+ if (modelRef) {
856
+ if (modelRef) {
857
+ result = modelRef.instance.languageId;
858
+ }
859
+ modelRef.dispose();
860
+ }
861
+ return result;
862
+ };
863
+ this._resourceContext = new ResourceContextKey(this.contextKeyService, (uri: URI) => {
864
+ const res = getLanguageFromModel(uri);
865
+ if (res) {
866
+ return res;
867
+ } else {
868
+ return getLanguageFromModel(uri);
869
+ }
870
+ });
871
+ this._diffResourceContextKey = new ResourceContextKey(
872
+ this.contextKeyService,
873
+ (uri: URI) => {
874
+ const res = getLanguageFromModel(uri);
875
+ if (res) {
876
+ return res;
877
+ } else {
878
+ return getLanguageFromModel(uri);
879
+ }
880
+ },
881
+ 'diffResource',
882
+ );
883
+ this._editorLangIDContextKey = this.contextKeyService.createKey<string>('editorLangId', '');
884
+ this._isInDiffEditorContextKey = this.contextKeyService.createKey<boolean>('isInDiffEditor', false);
885
+ this._isInDiffRightEditorContextKey = this.contextKeyService.createKey<boolean>('isInDiffRightEditor', false);
886
+ this._isInEditorComponentContextKey = this.contextKeyService.createKey<boolean>('inEditorComponent', false);
887
+ }
888
+ if (this.currentOrPreviousFocusedEditor && this.currentOrPreviousFocusedEditor.currentUri) {
889
+ this._resourceContext.set(this.currentOrPreviousFocusedEditor.currentUri);
890
+ if (this.currentOrPreviousFocusedEditor.currentDocumentModel) {
891
+ this._editorLangIDContextKey.set(this.currentOrPreviousFocusedEditor.currentDocumentModel.languageId);
892
+ }
893
+ } else if (this.currentEditor && this.currentEditor.currentUri) {
894
+ this._resourceContext.set(this.currentEditor.currentUri);
895
+ if (this.currentEditor.currentDocumentModel) {
896
+ this._editorLangIDContextKey.set(this.currentEditor.currentDocumentModel.languageId);
897
+ }
898
+ } else {
899
+ if (this.currentResource) {
900
+ this._resourceContext.set(this.currentResource.uri);
901
+ } else {
902
+ this._resourceContext.reset();
903
+ }
904
+ this._editorLangIDContextKey.reset();
905
+ }
906
+ this._isInDiffEditorContextKey.set(this.isDiffEditorMode());
907
+ // 没有 focus 的时候默认添加在 RightDiffEditor
908
+ this._isInDiffRightEditorContextKey.set(this.isDiffEditorMode());
909
+ this._isInEditorComponentContextKey.set(this.isComponentMode());
910
+ if (this.isDiffEditorMode()) {
911
+ this._diffResourceContextKey.set(this.currentResource?.uri);
912
+ }
913
+ this.updateContextKeyWhenDiffEditorChangesFocus();
914
+ }
915
+
916
+ private updateContextKeyWhenDiffEditorChangesFocus() {
917
+ if (this.updateContextKeyWhenEditorChangesFocusDisposer || !this.diffEditor) {
918
+ return;
919
+ }
920
+ const emitIfNoEditorFocused = () => {
921
+ if (!this.currentFocusedEditor) {
922
+ this.setContextKeys();
923
+ this._onDidEditorFocusChange.fire();
924
+ }
925
+ };
926
+ this.updateContextKeyWhenEditorChangesFocusDisposer = new Disposable(
927
+ this.diffEditor.modifiedEditor.onFocus(() => {
928
+ this._currentOrPreviousFocusedEditor = this.diffEditor.modifiedEditor;
929
+ this.setContextKeys();
930
+ this._onDidEditorFocusChange.fire();
931
+ }),
932
+ this.diffEditor.originalEditor.onFocus(() => {
933
+ this._currentOrPreviousFocusedEditor = this.diffEditor.originalEditor;
934
+ this.setContextKeys();
935
+ this._onDidEditorFocusChange.fire();
936
+ }),
937
+ this.codeEditor.onFocus(() => {
938
+ if (this.codeEditor.currentUri) {
939
+ this.locateInFileTree(this.codeEditor.currentUri);
940
+ }
941
+ this._currentOrPreviousFocusedEditor = this.codeEditor;
942
+ this.setContextKeys();
943
+ this._onDidEditorFocusChange.fire();
944
+ }),
945
+ this.codeEditor.onBlur(emitIfNoEditorFocused),
946
+ this.diffEditor.originalEditor.onBlur(emitIfNoEditorFocused),
947
+ this.diffEditor.modifiedEditor.onBlur(emitIfNoEditorFocused),
948
+ );
949
+ this.addDispose(this.updateContextKeyWhenEditorChangesFocusDisposer);
950
+ }
951
+
952
+ get contextKeyService() {
953
+ if (!this._contextKeyService) {
954
+ this._contextKeyService = this.workbenchEditorService.editorContextKeyService.createScoped();
955
+ }
956
+ return this._contextKeyService;
957
+ }
958
+
959
+ get index(): number {
960
+ return this.workbenchEditorService.sortedEditorGroups.indexOf(this);
961
+ }
962
+
963
+ @OnEvent(ResourceDecorationChangeEvent)
964
+ onResourceDecorationChangeEvent(e: ResourceDecorationChangeEvent) {
965
+ if (e.payload.decoration.dirty) {
966
+ if (this.previewURI && this.previewURI.isEqual(e.payload.uri)) {
967
+ this.pinPreviewed();
968
+ }
969
+ }
970
+ const existingResource = this.resources.find((r) => r.uri.isEqual(e.payload.uri));
971
+ if (existingResource) {
972
+ this.notifyTabChanged();
973
+ }
974
+ }
975
+
976
+ @OnEvent(ResourceOpenTypeChangedEvent)
977
+ oResourceOpenTypeChangedEvent(e: ResourceOpenTypeChangedEvent) {
978
+ const uri = e.payload;
979
+ if (this.cachedResourcesOpenTypes.has(uri.toString())) {
980
+ this.cachedResourcesOpenTypes.delete(uri.toString());
981
+ }
982
+ if (this.currentResource && this.currentResource.uri.isEqual(uri)) {
983
+ this._currentOpenType = null;
984
+ this.notifyBodyChanged();
985
+ this.displayResourceComponent(this.currentResource, {});
986
+ }
987
+ }
988
+
989
+ @OnEvent(RegisterEditorComponentEvent)
990
+ async onRegisterEditorComponentEvent() {
991
+ if (this.currentResource) {
992
+ const resource = this.currentResource;
993
+ const openTypes = await this.editorComponentRegistry.resolveEditorComponent(resource);
994
+ if (this.currentResource === resource) {
995
+ this.availableOpenTypes = openTypes;
996
+ this.cachedResourcesOpenTypes.set(resource.uri.toString(), openTypes);
997
+ }
998
+ }
999
+ }
1000
+
1001
+ pinPreviewed(uri?: URI) {
1002
+ const previous = this.previewURI;
1003
+ if (uri === undefined) {
1004
+ this.previewURI = null;
1005
+ } else if (this.previewURI && this.previewURI.isEqual(uri)) {
1006
+ this.previewURI = null;
1007
+ }
1008
+ if (previous !== this.previewURI) {
1009
+ this.notifyTabChanged();
1010
+ }
1011
+ }
1012
+
1013
+ private notifyTabChanged() {
1014
+ if (this._restoringState) {
1015
+ return;
1016
+ }
1017
+
1018
+ this._onDidEditorGroupTabChanged.fire();
1019
+ }
1020
+
1021
+ private notifyBodyChanged() {
1022
+ this._onDidEditorGroupBodyChanged.fire();
1023
+ }
1024
+
1025
+ private notifyTabLoading(resource: IResource) {
1026
+ this._onDidEditorGroupContentLoading.fire(resource);
1027
+ }
1028
+
1029
+ get currentEditor(): IEditor | null {
1030
+ if (this.currentOpenType) {
1031
+ if (this.currentOpenType.type === EditorOpenType.code) {
1032
+ return this.codeEditor;
1033
+ } else if (this.currentOpenType.type === EditorOpenType.diff) {
1034
+ return this.diffEditor.modifiedEditor;
1035
+ } else {
1036
+ return null;
1037
+ }
1038
+ } else {
1039
+ return null;
1040
+ }
1041
+ }
1042
+
1043
+ get currentOrPreviousFocusedEditor(): IEditor | null {
1044
+ return this._currentOrPreviousFocusedEditor || this.currentEditor;
1045
+ }
1046
+
1047
+ get currentFocusedEditor() {
1048
+ if (this.currentOpenType) {
1049
+ if (this.currentOpenType.type === EditorOpenType.code) {
1050
+ if (this.codeEditor.monacoEditor.hasWidgetFocus()) {
1051
+ return this.codeEditor;
1052
+ }
1053
+ } else if (this.currentOpenType.type === EditorOpenType.diff) {
1054
+ if (this.diffEditor.modifiedEditor.monacoEditor.hasTextFocus()) {
1055
+ return this.diffEditor.modifiedEditor;
1056
+ } else if (this.diffEditor.originalEditor.monacoEditor.hasTextFocus()) {
1057
+ return this.diffEditor.originalEditor;
1058
+ }
1059
+ if (this.diffEditor.modifiedEditor.monacoEditor.hasWidgetFocus()) {
1060
+ return this.diffEditor.modifiedEditor;
1061
+ } else if (this.diffEditor.originalEditor.monacoEditor.hasWidgetFocus()) {
1062
+ return this.diffEditor.originalEditor;
1063
+ }
1064
+ }
1065
+ }
1066
+ return null;
1067
+ }
1068
+
1069
+ get currentCodeEditor(): ICodeEditor | null {
1070
+ if (this.currentOpenType) {
1071
+ if (this.currentOpenType.type === EditorOpenType.code) {
1072
+ return this.codeEditor;
1073
+ } else {
1074
+ return null;
1075
+ }
1076
+ } else {
1077
+ return null;
1078
+ }
1079
+ }
1080
+
1081
+ createEditor(dom: HTMLElement) {
1082
+ this.codeEditor = this.collectionService.createCodeEditor(
1083
+ dom,
1084
+ {},
1085
+ {
1086
+ [ServiceNames.CONTEXT_KEY_SERVICE]: this.contextKeyService.contextKeyService,
1087
+ },
1088
+ );
1089
+ setTimeout(() => {
1090
+ this.codeEditor.layout();
1091
+ });
1092
+ this.toDispose.push(
1093
+ this.codeEditor.onCursorPositionChanged((e) => {
1094
+ this._onCurrentEditorCursorChange.fire(e);
1095
+ }),
1096
+ );
1097
+ this.toDispose.push(
1098
+ this.codeEditor.onSelectionsChanged((e) => {
1099
+ if (this.currentOpenType && this.currentOpenType.type === EditorOpenType.code) {
1100
+ this.eventBus.fire(
1101
+ new EditorSelectionChangeEvent({
1102
+ group: this,
1103
+ resource: this.currentResource!,
1104
+ selections: e.selections,
1105
+ source: e.source,
1106
+ editorUri: this.codeEditor.currentUri!,
1107
+ }),
1108
+ );
1109
+ }
1110
+ }),
1111
+ );
1112
+ this.toDispose.push(
1113
+ this.codeEditor.onVisibleRangesChanged((e) => {
1114
+ if (this.currentOpenType && this.currentOpenType.type === EditorOpenType.code) {
1115
+ this.eventBus.fire(
1116
+ new EditorVisibleChangeEvent({
1117
+ group: this,
1118
+ resource: this.currentResource!,
1119
+ visibleRanges: e,
1120
+ editorUri: this.codeEditor.currentUri!,
1121
+ }),
1122
+ );
1123
+ }
1124
+ }),
1125
+ );
1126
+ this.toDispose.push(
1127
+ this.codeEditor.onConfigurationChanged(() => {
1128
+ if (this.currentOpenType && this.currentOpenType.type === EditorOpenType.code) {
1129
+ this.eventBus.fire(
1130
+ new EditorConfigurationChangedEvent({
1131
+ group: this,
1132
+ resource: this.currentResource!,
1133
+ editorUri: this.codeEditor.currentUri!,
1134
+ }),
1135
+ );
1136
+ }
1137
+ }),
1138
+ );
1139
+ this.eventBus.fire(
1140
+ new CodeEditorDidVisibleEvent({
1141
+ groupName: this.name,
1142
+ type: EditorOpenType.code,
1143
+ editorId: this.codeEditor.getId(),
1144
+ }),
1145
+ );
1146
+ this.codeEditorReady.ready();
1147
+ }
1148
+
1149
+ createMergeEditor(dom: HTMLElement) {
1150
+ this.mergeEditor = this.collectionService.createMergeEditor(
1151
+ dom,
1152
+ {},
1153
+ {
1154
+ [ServiceNames.CONTEXT_KEY_SERVICE]: this.contextKeyService.contextKeyService,
1155
+ },
1156
+ );
1157
+ this.mergeEditorReady.ready();
1158
+ }
1159
+
1160
+ createDiffEditor(dom: HTMLElement) {
1161
+ this.diffEditor = this.collectionService.createDiffEditor(
1162
+ dom,
1163
+ {},
1164
+ {
1165
+ [ServiceNames.CONTEXT_KEY_SERVICE]: this.contextKeyService.contextKeyService,
1166
+ },
1167
+ );
1168
+ setTimeout(() => {
1169
+ this.diffEditor.layout();
1170
+ });
1171
+
1172
+ this.addDiffEditorEventListeners(this.diffEditor.originalEditor, 'original');
1173
+ this.addDiffEditorEventListeners(this.diffEditor.modifiedEditor, 'modified');
1174
+
1175
+ this.eventBus.fire(
1176
+ new CodeEditorDidVisibleEvent({
1177
+ groupName: this.name,
1178
+ type: EditorOpenType.diff,
1179
+ editorId: this.diffEditor.modifiedEditor.getId(),
1180
+ }),
1181
+ );
1182
+
1183
+ this.eventBus.fire(
1184
+ new CodeEditorDidVisibleEvent({
1185
+ groupName: this.name,
1186
+ type: EditorOpenType.diff,
1187
+ editorId: this.diffEditor.originalEditor.getId(),
1188
+ }),
1189
+ );
1190
+ this.diffEditorReady.ready();
1191
+ }
1192
+
1193
+ private addDiffEditorEventListeners(editor: IEditor, side?: 'modified' | 'original') {
1194
+ this.toDispose.push(
1195
+ editor.onSelectionsChanged((e) => {
1196
+ if (this.currentOpenType && this.currentOpenType.type === EditorOpenType.diff) {
1197
+ this.eventBus.fire(
1198
+ new EditorSelectionChangeEvent({
1199
+ group: this,
1200
+ resource: this.currentResource!,
1201
+ selections: e.selections,
1202
+ source: e.source,
1203
+ editorUri: URI.from(editor.monacoEditor.getModel()?.uri!),
1204
+ side,
1205
+ }),
1206
+ );
1207
+ }
1208
+ }),
1209
+ );
1210
+
1211
+ this.toDispose.push(
1212
+ editor.onVisibleRangesChanged((e) => {
1213
+ if (this.currentOpenType && this.currentOpenType.type === EditorOpenType.diff) {
1214
+ this.eventBus.fire(
1215
+ new EditorVisibleChangeEvent({
1216
+ group: this,
1217
+ resource: this.currentResource!,
1218
+ visibleRanges: e,
1219
+ editorUri: editor.currentUri!,
1220
+ }),
1221
+ );
1222
+ }
1223
+ }),
1224
+ );
1225
+
1226
+ this.toDispose.push(
1227
+ editor.onConfigurationChanged(() => {
1228
+ if (this.currentOpenType && this.currentOpenType.type === EditorOpenType.diff) {
1229
+ this.eventBus.fire(
1230
+ new EditorConfigurationChangedEvent({
1231
+ group: this,
1232
+ resource: this.currentResource!,
1233
+ editorUri: editor.currentUri!,
1234
+ }),
1235
+ );
1236
+ }
1237
+ }),
1238
+ );
1239
+ }
1240
+
1241
+ async split(action: EditorGroupSplitAction, uri: URI, options?: IResourceOpenOptions) {
1242
+ const editorGroup = this.workbenchEditorService.createEditorGroup();
1243
+ const direction =
1244
+ action === EditorGroupSplitAction.Left || action === EditorGroupSplitAction.Right
1245
+ ? SplitDirection.Horizontal
1246
+ : SplitDirection.Vertical;
1247
+ const before = action === EditorGroupSplitAction.Left || action === EditorGroupSplitAction.Top ? true : false;
1248
+ this.grid.split(direction, editorGroup, before);
1249
+
1250
+ // 对于同一个编辑器分栏的场景,希望保留原本的滚动状态,与 VS Code 保持一致
1251
+ if (options && !options.scrollTop) {
1252
+ options.scrollTop = this.currentEditor?.monacoEditor.getScrollTop();
1253
+ }
1254
+ if (options && !options.scrollLeft) {
1255
+ options.scrollLeft = this.currentEditor?.monacoEditor.getScrollLeft();
1256
+ }
1257
+
1258
+ if (options && !options?.range) {
1259
+ const selection = this.currentCodeEditor?.monacoEditor.getSelection();
1260
+ if (selection) {
1261
+ options.range = new monaco.Range(
1262
+ selection.startLineNumber,
1263
+ selection.startColumn,
1264
+ selection.endLineNumber,
1265
+ selection.endColumn,
1266
+ );
1267
+ }
1268
+ }
1269
+
1270
+ return editorGroup.open(uri, { ...options, preview: false, revealRangeInCenter: false });
1271
+ }
1272
+
1273
+ async open(uri: URI, options: IResourceOpenOptions = {}): Promise<IOpenResourceResult> {
1274
+ if (uri.scheme === Schemes.file) {
1275
+ // 只记录 file 类型的
1276
+ this.recentFilesManager.setMostRecentlyOpenedFile!(uri.withoutFragment().toString());
1277
+ }
1278
+ if (options && options.split) {
1279
+ return this.split(options.split, uri, Object.assign({}, options, { split: undefined, preview: false }));
1280
+ }
1281
+ if (!this.openingPromise.has(uri.toString())) {
1282
+ const promise = this.doOpen(uri, options);
1283
+ this.openingPromise.set(uri.toString(), promise);
1284
+ promise.then(
1285
+ () => {
1286
+ this.openingPromise.delete(uri.toString());
1287
+ },
1288
+ () => {
1289
+ this.openingPromise.delete(uri.toString());
1290
+ },
1291
+ );
1292
+ }
1293
+ const previewMode =
1294
+ this.preferenceService.get('editor.previewMode') && (isUndefinedOrNull(options.preview) ? true : options.preview);
1295
+ if (!previewMode) {
1296
+ this.openingPromise.get(uri.toString())!.then(() => {
1297
+ this.pinPreviewed(uri);
1298
+ });
1299
+ }
1300
+ return this.openingPromise.get(uri.toString())!;
1301
+ }
1302
+
1303
+ async pin(uri: URI) {
1304
+ return this.pinPreviewed(uri);
1305
+ }
1306
+
1307
+ async doOpen(
1308
+ uri: URI,
1309
+ options: IResourceOpenOptions = {},
1310
+ ): Promise<{ group: IEditorGroup; resource: IResource } | false> {
1311
+ if (!this.resourceService.handlesUri(uri)) {
1312
+ this.openerService.open(uri);
1313
+ return false;
1314
+ }
1315
+ let resourceReady: Deferred<void> | undefined;
1316
+ try {
1317
+ const previewMode =
1318
+ this.preferenceService.get('editor.previewMode') &&
1319
+ (isUndefinedOrNull(options.preview) ? true : options.preview);
1320
+ if (this.currentResource && this.currentResource.uri.isEqual(uri)) {
1321
+ // 就是当前打开的resource
1322
+ if (options.focus && this.currentEditor) {
1323
+ this._domNode?.focus();
1324
+ this.currentEditor.monacoEditor.focus();
1325
+ }
1326
+ if (options.range && this.currentEditor) {
1327
+ this.currentEditor.monacoEditor.setSelection(options.range as monaco.IRange);
1328
+ setTimeout(() => {
1329
+ this.currentEditor?.monacoEditor.revealRangeInCenter(options.range as monaco.IRange, 0);
1330
+ }, 0);
1331
+ }
1332
+ if ((options && options.disableNavigate) || (options && options.backend)) {
1333
+ // no-op
1334
+ } else {
1335
+ this.locateInFileTree(uri);
1336
+ }
1337
+ this.notifyTabChanged();
1338
+ return {
1339
+ group: this,
1340
+ resource: this.currentResource,
1341
+ };
1342
+ } else {
1343
+ const oldOpenType = this._currentOpenType;
1344
+ const oldResource = this._currentResource;
1345
+ let resource: IResource | null | undefined = this.resources.find((r) => r.uri.toString() === uri.toString());
1346
+ if (!resource) {
1347
+ // open new resource
1348
+ resource = await this.resourceService.getResource(uri);
1349
+ if (!resource) {
1350
+ throw new Error('This uri cannot be opened!: ' + uri);
1351
+ }
1352
+ if (resource.deleted) {
1353
+ if (options.deletedPolicy === 'fail') {
1354
+ throw new Error('resource deleted ' + uri);
1355
+ } else if (options.deletedPolicy === 'skip') {
1356
+ return false;
1357
+ }
1358
+ }
1359
+ if (options && options.label) {
1360
+ resource.name = options.label;
1361
+ }
1362
+ let replaceResource: IResource | null = null;
1363
+ if (options && options.index !== undefined && options.index < this.resources.length) {
1364
+ replaceResource = this.resources[options.index];
1365
+ this.resources.splice(options.index, 0, resource);
1366
+ } else {
1367
+ if (this.currentResource) {
1368
+ const currentIndex = this.resources.indexOf(this.currentResource);
1369
+ this.resources.splice(currentIndex + 1, 0, resource);
1370
+ replaceResource = this.currentResource;
1371
+ } else {
1372
+ this.resources.push(resource);
1373
+ }
1374
+ }
1375
+ if (previewMode) {
1376
+ if (this.previewURI) {
1377
+ await this.close(this.previewURI, { treatAsNotCurrent: true, force: options.forceClose });
1378
+ }
1379
+ this.previewURI = resource.uri;
1380
+ }
1381
+ if (options.replace && replaceResource) {
1382
+ await this.close(replaceResource.uri, { treatAsNotCurrent: true, force: options.forceClose });
1383
+ }
1384
+ }
1385
+ if (options.backend) {
1386
+ this.notifyTabChanged();
1387
+ return false;
1388
+ }
1389
+ if (oldResource && this.resourceOpenHistory[this.resourceOpenHistory.length - 1] !== oldResource.uri) {
1390
+ this.resourceOpenHistory.push(oldResource.uri);
1391
+ const oldResourceSelections = this.currentCodeEditor?.getSelections();
1392
+ if (oldResourceSelections && oldResourceSelections.length > 0) {
1393
+ this.recentFilesManager.updateMostRecentlyOpenedFile(oldResource.uri.toString(), {
1394
+ lineNumber: oldResourceSelections[0].selectionStartLineNumber,
1395
+ column: oldResourceSelections[0].selectionStartColumn,
1396
+ });
1397
+ }
1398
+ }
1399
+ this._currentOpenType = null;
1400
+ this._currentResource = resource;
1401
+
1402
+ // 只有真正打开的文件才会走到这里,backend模式的只更新了tab,文件内容并未加载
1403
+ const reportTimer = this.reporterService.time(REPORT_NAME.EDITOR_REACTIVE);
1404
+ resourceReady = new Deferred<void>();
1405
+ this.resourceStatus.set(resource, resourceReady.promise);
1406
+ // 超过60ms loading时间的才展示加载
1407
+ const delayTimer = setTimeout(() => {
1408
+ this.notifyTabLoading(resource!);
1409
+ }, 60);
1410
+ await this.displayResourceComponent(resource, options);
1411
+ this._currentOrPreviousFocusedEditor = this.currentEditor;
1412
+ this.notifyTabChanged();
1413
+ this.notifyBodyChanged();
1414
+
1415
+ clearTimeout(delayTimer);
1416
+ resourceReady.resolve();
1417
+ reportTimer.timeEnd(resource.uri.toString());
1418
+ this._onDidEditorFocusChange.fire();
1419
+ this.setContextKeys();
1420
+ this.eventBus.fire(
1421
+ new EditorGroupOpenEvent({
1422
+ group: this,
1423
+ resource,
1424
+ }),
1425
+ );
1426
+ if ((options && options.disableNavigate) || (options && options.backend)) {
1427
+ // no-op
1428
+ } else {
1429
+ this.locateInFileTree(uri);
1430
+ }
1431
+ this.eventBus.fire(
1432
+ new EditorGroupChangeEvent({
1433
+ group: this,
1434
+ newOpenType: this.currentOpenType,
1435
+ newResource: this.currentResource,
1436
+ oldOpenType,
1437
+ oldResource,
1438
+ }),
1439
+ );
1440
+ return {
1441
+ group: this,
1442
+ resource,
1443
+ };
1444
+ }
1445
+ } catch (e) {
1446
+ getDebugLogger().error(e);
1447
+ resourceReady && resourceReady.reject();
1448
+ if (!isEditorError(e, EditorTabChangedError)) {
1449
+ this.messageService.error(formatLocalize('editor.failToOpen', uri.displayName, e.message), [], true);
1450
+ }
1451
+ return false;
1452
+ // todo 给用户显示error
1453
+ }
1454
+ }
1455
+
1456
+ private locateInFileTree(uri: URI) {
1457
+ if (this.explorerAutoRevealConfig) {
1458
+ this.commands.tryExecuteCommand(FILE_COMMANDS.LOCATION.id, uri);
1459
+ }
1460
+ }
1461
+
1462
+ async openUris(uris: URI[]): Promise<void> {
1463
+ for (const uri of uris) {
1464
+ await this.open(uri);
1465
+ }
1466
+ }
1467
+
1468
+ async getDocumentModelRef(uri: URI): Promise<IEditorDocumentModelRef> {
1469
+ if (!this.holdDocumentModelRefs.has(uri.toString())) {
1470
+ this.holdDocumentModelRefs.set(
1471
+ uri.toString(),
1472
+ await this.documentModelManager.createModelReference(uri, 'editor-group-' + this.name),
1473
+ );
1474
+ }
1475
+ return this.holdDocumentModelRefs.get(uri.toString())!;
1476
+ }
1477
+
1478
+ disposeDocumentRef(uri: URI) {
1479
+ if (uri.scheme === 'diff') {
1480
+ const query = uri.getParsedQuery();
1481
+ this.doDisposeDocRef(new URI(query.original));
1482
+ this.doDisposeDocRef(new URI(query.modified));
1483
+ } else {
1484
+ this.doDisposeDocRef(uri);
1485
+ }
1486
+ }
1487
+
1488
+ protected doDisposeDocRef(uri: URI) {
1489
+ if (this.holdDocumentModelRefs.has(uri.toString())) {
1490
+ this.holdDocumentModelRefs.get(uri.toString())!.dispose();
1491
+ this.holdDocumentModelRefs.delete(uri.toString());
1492
+ }
1493
+ }
1494
+
1495
+ private async openCodeEditor(resource: IResource, options: IResourceOpenOptions) {
1496
+ const documentRef = await this.getDocumentModelRef(resource.uri);
1497
+ this.resolveTabChanged(resource, this.currentResource);
1498
+ await this.codeEditorReady.onceReady(async () => {
1499
+ await this.codeEditor.open(documentRef);
1500
+
1501
+ if (options.range) {
1502
+ const range = new monaco.Range(
1503
+ options.range.startLineNumber!,
1504
+ options.range.startColumn!,
1505
+ options.range.endLineNumber!,
1506
+ options.range.endColumn!,
1507
+ );
1508
+ // 这里使用 setTimeout 在下一次事件循环时将编辑器滚动到指定位置
1509
+ // 原因是在打开新文件的情况下
1510
+ // setModel 后立即调用 revealRangeInCenter 编辑器无法获取到 viewport 宽高
1511
+ // 导致无法正确计算滚动位置
1512
+ this.codeEditor.monacoEditor.setSelection(range);
1513
+ if (options.revealRangeInCenter) {
1514
+ setTimeout(() => {
1515
+ this.codeEditor.monacoEditor.revealRangeInCenter(range, 1);
1516
+ });
1517
+ }
1518
+ }
1519
+
1520
+ // 同上
1521
+ queueMicrotask(() => {
1522
+ if (options.scrollTop) {
1523
+ this.codeEditor.monacoEditor.setScrollTop(options.scrollTop!);
1524
+ }
1525
+ if (options.scrollLeft) {
1526
+ this.codeEditor.monacoEditor.setScrollLeft(options.scrollLeft!);
1527
+ }
1528
+ });
1529
+
1530
+ if (options.focus) {
1531
+ this._domNode?.focus();
1532
+ // monaco 编辑器的 focus 多了一步检查,由于此时其实对应编辑器的 dom 的 display 为 none (需要等 React 下一次渲染才会改变为 block),
1533
+ // 会引起 document.activeElement !== editor.textArea.domNode,进而会导致focus失败
1534
+ // 需要等待真正 append 之后再
1535
+ const disposer = this.eventBus.on(CodeEditorDidVisibleEvent, (e) => {
1536
+ if (e.payload.groupName === this.name && e.payload.type === EditorOpenType.code) {
1537
+ disposer.dispose();
1538
+ // 此处必须多做一些检查以免不必要的 focus
1539
+ if (this.disposed) {
1540
+ return;
1541
+ }
1542
+ if (this !== this.workbenchEditorService.currentEditorGroup) {
1543
+ return;
1544
+ }
1545
+ if (this.currentEditor === this.codeEditor && this.codeEditor.currentUri?.isEqual(resource.uri)) {
1546
+ try {
1547
+ this.codeEditor.focus();
1548
+ } catch (e) {
1549
+ // noop
1550
+ }
1551
+ }
1552
+ }
1553
+ });
1554
+ }
1555
+ });
1556
+
1557
+ // 可能在diff Editor中修改导致为脏
1558
+ if (documentRef.instance!.dirty) {
1559
+ this.pinPreviewed(resource.uri);
1560
+ }
1561
+ }
1562
+
1563
+ private async openDiffEditor(resource: IResource, options: IResourceOpenOptions) {
1564
+ if (!this.diffEditor) {
1565
+ await this.diffEditorDomReady.onceReady(() => {
1566
+ const container = document.createElement('div');
1567
+ this._diffEditorDomNode?.appendChild(container);
1568
+ this.createDiffEditor(container);
1569
+ });
1570
+ }
1571
+
1572
+ const diffResource = resource as IDiffResource;
1573
+ const [original, modified] = await Promise.all([
1574
+ this.getDocumentModelRef(diffResource.metadata!.original),
1575
+ this.getDocumentModelRef(diffResource.metadata!.modified),
1576
+ ]);
1577
+ await this.diffEditorReady.onceReady(async () => {
1578
+ await this.diffEditor.compare(original, modified, options, resource.uri);
1579
+ if (options.focus) {
1580
+ this._domNode?.focus();
1581
+ // 理由见上方 codeEditor.focus 部分
1582
+
1583
+ const disposer = this.eventBus.on(CodeEditorDidVisibleEvent, (e) => {
1584
+ if (e.payload.groupName === this.name && e.payload.type === EditorOpenType.diff) {
1585
+ disposer.dispose();
1586
+ if (this.disposed) {
1587
+ return;
1588
+ }
1589
+ if (this !== this.workbenchEditorService.currentEditorGroup) {
1590
+ return;
1591
+ }
1592
+ if (this.currentEditor === this.diffEditor.modifiedEditor) {
1593
+ try {
1594
+ this.diffEditor.focus();
1595
+ } catch (e) {
1596
+ // noop
1597
+ }
1598
+ }
1599
+ }
1600
+ });
1601
+ }
1602
+ });
1603
+ }
1604
+
1605
+ private async openMergeEditor(resource: IResource) {
1606
+ const { metadata } = resource as IMergeEditorResource;
1607
+ if (!metadata) {
1608
+ return;
1609
+ }
1610
+
1611
+ if (!this.mergeEditor) {
1612
+ await this.mergeEditorDomReady.onceReady(() => {
1613
+ const container = document.createElement('div');
1614
+ this._mergeEditorDomNode?.appendChild(container);
1615
+ this.createMergeEditor(container);
1616
+ });
1617
+ }
1618
+
1619
+ const { ancestor, input1, input2, output } = metadata;
1620
+ const input1Data = MergeEditorInputData.from(input1);
1621
+ const input2Data = MergeEditorInputData.from(input2);
1622
+
1623
+ const [ancestorRef, input1Ref, outputRef, input2Ref] = await Promise.all([
1624
+ this.getDocumentModelRef(URI.parse(ancestor)),
1625
+ this.getDocumentModelRef(input1Data.uri),
1626
+ this.getDocumentModelRef(URI.parse(output)),
1627
+ this.getDocumentModelRef(input2Data.uri),
1628
+ ]);
1629
+
1630
+ await this.mergeEditorReady.onceReady(async () => {
1631
+ await this.mergeEditor.open({
1632
+ ancestor: {
1633
+ uri: URI.parse(metadata.ancestor),
1634
+ textModel: ancestorRef.instance.getMonacoModel(),
1635
+ },
1636
+ input1: input1Data.setTextModel(input1Ref.instance.getMonacoModel()),
1637
+ input2: input2Data.setTextModel(input2Ref.instance.getMonacoModel()),
1638
+ output: {
1639
+ uri: URI.parse(metadata.output),
1640
+ textModel: outputRef.instance.getMonacoModel(),
1641
+ },
1642
+ });
1643
+ });
1644
+ }
1645
+
1646
+ private async openCustomEditor(resource: IResource, componentId?: string) {
1647
+ const component = this.editorComponentRegistry.getEditorComponent(componentId as string);
1648
+ const initialProps = this.editorComponentRegistry.getEditorInitialProps(componentId as string);
1649
+ if (!component) {
1650
+ throw new Error('Cannot find Editor Component with id: ' + componentId);
1651
+ } else {
1652
+ this.activateComponentsProps.set(component, initialProps);
1653
+ if (component.renderMode === EditorComponentRenderMode.ONE_PER_RESOURCE) {
1654
+ const openedResources = this.activeComponents.get(component) || [];
1655
+ const index = openedResources.findIndex((r) => r.uri.toString() === resource.uri.toString());
1656
+ if (index === -1) {
1657
+ openedResources.push(resource);
1658
+ }
1659
+ this.activeComponents.set(component, openedResources);
1660
+ } else if (component.renderMode === EditorComponentRenderMode.ONE_PER_GROUP) {
1661
+ this.activeComponents.set(component, [resource]);
1662
+ } else if (component.renderMode === EditorComponentRenderMode.ONE_PER_WORKBENCH) {
1663
+ const promises: Promise<any>[] = [];
1664
+ this.workbenchEditorService.editorGroups.forEach((g) => {
1665
+ if (g === this) {
1666
+ return;
1667
+ }
1668
+ const r = g.resources.find((r) => r.uri.isEqual(resource.uri));
1669
+ if (r) {
1670
+ promises.push(g.close(r.uri));
1671
+ }
1672
+ });
1673
+ await Promise.all(promises).catch(getDebugLogger().error);
1674
+ this.activeComponents.set(component, [resource]);
1675
+ }
1676
+ }
1677
+ // 打开非编辑器的component时需要手动触发
1678
+ this._onCurrentEditorCursorChange.fire({
1679
+ position: null,
1680
+ selectionLength: 0,
1681
+ });
1682
+ }
1683
+
1684
+ private async displayResourceComponent(resource: IResource, options: IResourceOpenOptions) {
1685
+ if (options.revealRangeInCenter === undefined) {
1686
+ options.revealRangeInCenter = true;
1687
+ }
1688
+
1689
+ const _resource = resource;
1690
+ const result = await this.resolveOpenType(resource, options);
1691
+ if (result) {
1692
+ const { activeOpenType, openTypes } = result;
1693
+ this.availableOpenTypes = openTypes;
1694
+
1695
+ if (options.preserveFocus) {
1696
+ options.focus = false;
1697
+ }
1698
+
1699
+ switch (activeOpenType.type) {
1700
+ case EditorOpenType.code:
1701
+ await this.openCodeEditor(resource, options);
1702
+ break;
1703
+ case EditorOpenType.diff:
1704
+ await this.openDiffEditor(resource, options);
1705
+ break;
1706
+ case EditorOpenType.mergeEditor:
1707
+ await this.openMergeEditor(resource);
1708
+ break;
1709
+ case EditorOpenType.component:
1710
+ await this.openCustomEditor(resource, activeOpenType.componentId);
1711
+ break;
1712
+ default:
1713
+ return;
1714
+ }
1715
+
1716
+ this.resolveTabChanged(_resource, this.currentResource);
1717
+ this._currentOpenType = activeOpenType;
1718
+ this.notifyBodyChanged();
1719
+
1720
+ if (
1721
+ (!this._codeEditorPendingLayout && activeOpenType.type === EditorOpenType.code) ||
1722
+ (!this._diffEditorPendingLayout && activeOpenType.type === EditorOpenType.diff) ||
1723
+ (!this._mergeEditorPendingLayout && activeOpenType.type === EditorOpenType.mergeEditor)
1724
+ ) {
1725
+ this.doLayoutEditors();
1726
+ }
1727
+
1728
+ this.cachedResourcesActiveOpenTypes.set(resource.uri.toString(), activeOpenType);
1729
+ }
1730
+ }
1731
+
1732
+ private resolveTabChanged(lastResource: IResource, curResource: MaybeNull<IResource>): void {
1733
+ if (lastResource !== curResource) {
1734
+ // 打开过程中改变了tab
1735
+ throw new EditorTabChangedError();
1736
+ }
1737
+ }
1738
+
1739
+ private async resolveOpenType(
1740
+ resource: IResource,
1741
+ options: IResourceOpenOptions,
1742
+ ): Promise<{ activeOpenType: IEditorOpenType; openTypes: IEditorOpenType[] } | null> {
1743
+ const openTypes =
1744
+ this.cachedResourcesOpenTypes.get(resource.uri.toString()) ||
1745
+ (await this.editorComponentRegistry.resolveEditorComponent(resource));
1746
+ const editorAssociations = this.preferenceService.get<{ [key in string]: string }>('workbench.editorAssociations');
1747
+ const activeOpenType = findSuitableOpenType(
1748
+ openTypes,
1749
+ this.cachedResourcesActiveOpenTypes.get(resource.uri.toString()),
1750
+ resource,
1751
+ editorAssociations,
1752
+ options.forceOpenType,
1753
+ );
1754
+ this.cachedResourcesOpenTypes.set(resource.uri.toString(), openTypes);
1755
+ return { activeOpenType, openTypes };
1756
+ }
1757
+
1758
+ public async close(
1759
+ uri: URI,
1760
+ {
1761
+ treatAsNotCurrent,
1762
+ force,
1763
+ }: {
1764
+ treatAsNotCurrent?: boolean;
1765
+ force?: boolean;
1766
+ } = {},
1767
+ ) {
1768
+ const index = this.resources.findIndex((r) => r.uri.toString() === uri.toString());
1769
+ if (index !== -1) {
1770
+ const resource = this.resources[index];
1771
+ if (!force) {
1772
+ if (!(await this.shouldClose(resource))) {
1773
+ return;
1774
+ }
1775
+ }
1776
+ this.resources.splice(index, 1);
1777
+ this.eventBus.fire(
1778
+ new EditorGroupCloseEvent({
1779
+ group: this,
1780
+ resource,
1781
+ }),
1782
+ );
1783
+ if (this.previewURI && this.previewURI.isEqual(uri)) {
1784
+ this.previewURI = null;
1785
+ }
1786
+ // 优先打开用户打开历史中的uri,
1787
+ // 如果历史中的不可打开,打开去除当前关闭目标uri后相同位置的uri, 如果没有,则一直往前找到第一个可用的uri
1788
+ if (resource === this.currentResource && !treatAsNotCurrent) {
1789
+ let nextUri: URI | undefined;
1790
+ while (this.resourceOpenHistory.length > 0) {
1791
+ if (
1792
+ this.resources.findIndex((r) => r.uri === this.resourceOpenHistory[this.resourceOpenHistory.length - 1]) !==
1793
+ -1
1794
+ ) {
1795
+ nextUri = this.resourceOpenHistory.pop();
1796
+ break;
1797
+ } else {
1798
+ this.resourceOpenHistory.pop();
1799
+ }
1800
+ }
1801
+ if (nextUri) {
1802
+ this.open(nextUri);
1803
+ } else {
1804
+ let i = index;
1805
+ while (i > 0 && !this.resources[i]) {
1806
+ i--;
1807
+ }
1808
+ if (this.resources[i]) {
1809
+ this.open(this.resources[i].uri);
1810
+ } else {
1811
+ this.backToEmpty();
1812
+ }
1813
+ }
1814
+ } else {
1815
+ this.notifyTabChanged();
1816
+ }
1817
+ for (const resources of this.activeComponents.values()) {
1818
+ const i = resources.indexOf(resource);
1819
+ if (i !== -1) {
1820
+ resources.splice(i, 1);
1821
+ }
1822
+ }
1823
+ this.disposeDocumentRef(uri);
1824
+ }
1825
+ if (this.resources.length === 0) {
1826
+ if (this.grid.parent) {
1827
+ // 当前不是最后一个 editor Group
1828
+ this.dispose();
1829
+ }
1830
+ this.availableOpenTypes = [];
1831
+ }
1832
+ }
1833
+
1834
+ private async shouldClose(resource: IResource): Promise<boolean> {
1835
+ // TODO: 自定义打开方式如果存在保存能力,也要能阻止关闭
1836
+ const openedResources = this.workbenchEditorService.editorGroups.map((group) => group.resources);
1837
+ if (!(await this.resourceService.shouldCloseResource(resource, openedResources))) {
1838
+ return false;
1839
+ } else {
1840
+ let count = 0;
1841
+ for (const group of openedResources) {
1842
+ for (const res of group) {
1843
+ if (res.uri.isEqual(resource.uri)) {
1844
+ count++;
1845
+ if (count >= 2) {
1846
+ break;
1847
+ }
1848
+ }
1849
+ }
1850
+ }
1851
+ if (count <= 1) {
1852
+ this.resourceService.disposeResource(resource);
1853
+ }
1854
+ return true;
1855
+ }
1856
+ }
1857
+
1858
+ private backToEmpty() {
1859
+ const oldOpenType = this._currentOpenType;
1860
+ const oldResource = this._currentResource;
1861
+
1862
+ this._currentResource = null;
1863
+ this._currentOpenType = null;
1864
+ this.notifyTabChanged();
1865
+ this.notifyBodyChanged();
1866
+ this._currentOrPreviousFocusedEditor = null;
1867
+ this._onDidEditorFocusChange.fire();
1868
+ // 关闭最后一个时,应该发送一个 EditorGroupChangeEvent
1869
+ this.eventBus.fire(
1870
+ new EditorGroupChangeEvent({
1871
+ group: this,
1872
+ newOpenType: this.currentOpenType,
1873
+ newResource: this.currentResource,
1874
+ oldOpenType,
1875
+ oldResource,
1876
+ }),
1877
+ );
1878
+ }
1879
+
1880
+ /**
1881
+ * 关闭全部
1882
+ */
1883
+ async closeAll() {
1884
+ for (const resource of this.resources) {
1885
+ if (!(await this.shouldClose(resource))) {
1886
+ return;
1887
+ }
1888
+ }
1889
+ const closed = this.resources.splice(0, this.resources.length);
1890
+ closed.forEach((resource) => {
1891
+ this.clearResourceOnClose(resource);
1892
+ });
1893
+ this.activeComponents.clear();
1894
+ if (this.workbenchEditorService.editorGroups.length > 1) {
1895
+ this.dispose();
1896
+ }
1897
+ this.previewURI = null;
1898
+ this.backToEmpty();
1899
+ }
1900
+
1901
+ /**
1902
+ * 关闭已保存(非dirty)
1903
+ */
1904
+ async closeSaved() {
1905
+ const saved = this.resources.filter((r) => {
1906
+ const decoration = this.resourceService.getResourceDecoration(r.uri);
1907
+ if (!decoration || !decoration.dirty) {
1908
+ return true;
1909
+ }
1910
+ });
1911
+ for (const resource of saved) {
1912
+ if (!(await this.shouldClose(resource))) {
1913
+ return;
1914
+ }
1915
+ }
1916
+ for (const resource of saved) {
1917
+ await this.close(resource.uri);
1918
+ }
1919
+ }
1920
+
1921
+ /**
1922
+ * 关闭向右的tab
1923
+ * @param uri
1924
+ */
1925
+ async closeToRight(uri: URI) {
1926
+ const index = this.resources.findIndex((r) => r.uri.toString() === uri.toString());
1927
+ if (index !== -1) {
1928
+ const resourcesToClose = this.resources.slice(index + 1);
1929
+ for (const resource of resourcesToClose) {
1930
+ if (!(await this.shouldClose(resource))) {
1931
+ return;
1932
+ }
1933
+ }
1934
+ this.resources.splice(index + 1);
1935
+ for (const resource of resourcesToClose) {
1936
+ this.clearResourceOnClose(resource);
1937
+ }
1938
+ this.open(uri);
1939
+ }
1940
+ }
1941
+
1942
+ clearResourceOnClose(resource: IResource) {
1943
+ this.eventBus.fire(
1944
+ new EditorGroupCloseEvent({
1945
+ group: this,
1946
+ resource,
1947
+ }),
1948
+ );
1949
+ for (const resources of this.activeComponents.values()) {
1950
+ const i = resources.indexOf(resource);
1951
+ if (i !== -1) {
1952
+ resources.splice(i, 1);
1953
+ }
1954
+ }
1955
+ }
1956
+
1957
+ async closeOthers(uri: URI) {
1958
+ const index = this.resources.findIndex((r) => r.uri.toString() === uri.toString());
1959
+ if (index !== -1) {
1960
+ const resourcesToClose = this.resources.filter((v, i) => i !== index);
1961
+ for (const resource of resourcesToClose) {
1962
+ if (!(await this.shouldClose(resource))) {
1963
+ return;
1964
+ }
1965
+ }
1966
+ this.resources = [this.resources[index]];
1967
+ for (const resource of resourcesToClose) {
1968
+ this.clearResourceOnClose(resource);
1969
+ }
1970
+ await this.open(uri);
1971
+ }
1972
+ }
1973
+
1974
+ /**
1975
+ * 当前打开的resource
1976
+ */
1977
+ get currentResource(): MaybeNull<IResource> {
1978
+ return this._currentResource;
1979
+ }
1980
+
1981
+ get currentOpenType(): MaybeNull<IEditorOpenType> {
1982
+ return this._currentOpenType;
1983
+ }
1984
+
1985
+ async changeOpenType(id: string) {
1986
+ const type = this.availableOpenTypes.find((a) => a.type === id || a.componentId === id);
1987
+ if (!type) {
1988
+ return;
1989
+ }
1990
+
1991
+ if (!this.currentResource) {
1992
+ return;
1993
+ }
1994
+ if (openTypeSimilar(type, this.currentOpenType!)) {
1995
+ return;
1996
+ }
1997
+ const oldOpenType = this.currentOpenType;
1998
+ await this.displayResourceComponent(this.currentResource!, { forceOpenType: type });
1999
+ this.eventBus.fire(
2000
+ new EditorGroupChangeEvent({
2001
+ group: this,
2002
+ newOpenType: this.currentOpenType,
2003
+ newResource: this.currentResource,
2004
+ oldOpenType,
2005
+ oldResource: this.currentResource,
2006
+ }),
2007
+ );
2008
+ }
2009
+
2010
+ /**
2011
+ * 拖拽drop方法
2012
+ */
2013
+ public async dropUri(uri: URI, position: DragOverPosition, sourceGroup?: EditorGroup, targetResource?: IResource) {
2014
+ if (position !== DragOverPosition.CENTER) {
2015
+ await this.split(getSplitActionFromDragDrop(position), uri, { preview: false, focus: true });
2016
+ } else {
2017
+ // 扔在本体或者tab上
2018
+ if (!targetResource) {
2019
+ await this.open(uri, { preview: false, focus: true });
2020
+ } else {
2021
+ const targetIndex = this.resources.indexOf(targetResource);
2022
+ if (targetIndex === -1) {
2023
+ await this.open(uri, { preview: false, focus: true });
2024
+ } else {
2025
+ const sourceIndex = this.resources.findIndex((resource) => resource.uri.toString() === uri.toString());
2026
+ if (sourceIndex === -1) {
2027
+ await this.open(uri, {
2028
+ index: targetIndex,
2029
+ preview: false,
2030
+ });
2031
+ } else {
2032
+ // just move
2033
+ const sourceResource = this.resources[sourceIndex];
2034
+ if (sourceIndex > targetIndex) {
2035
+ this.resources.splice(sourceIndex, 1);
2036
+ this.resources.splice(targetIndex, 0, sourceResource);
2037
+ await this.open(uri, { preview: false });
2038
+ } else if (sourceIndex < targetIndex) {
2039
+ this.resources.splice(targetIndex + 1, 0, sourceResource);
2040
+ this.resources.splice(sourceIndex, 1);
2041
+ await this.open(uri, { preview: false });
2042
+ }
2043
+ }
2044
+ }
2045
+ }
2046
+ }
2047
+
2048
+ if (sourceGroup) {
2049
+ if (sourceGroup !== this) {
2050
+ // 从其他group拖动过来
2051
+ await sourceGroup.close(uri);
2052
+ } else if (position !== DragOverPosition.CENTER) {
2053
+ // split行为
2054
+ await this.close(uri);
2055
+ }
2056
+ }
2057
+ }
2058
+
2059
+ gainFocus() {
2060
+ this.workbenchEditorService.setCurrentGroup(this);
2061
+ }
2062
+
2063
+ focus() {
2064
+ this.gainFocus();
2065
+ if (this.currentOpenType && this.currentOpenType.type === EditorOpenType.code) {
2066
+ this.codeEditor.focus();
2067
+ }
2068
+ if (this.currentOpenType && this.currentOpenType.type === EditorOpenType.diff) {
2069
+ this.diffEditor.focus();
2070
+ }
2071
+ }
2072
+
2073
+ dispose() {
2074
+ this.grid?.dispose();
2075
+ this.workbenchEditorService.removeGroup(this);
2076
+ super.dispose();
2077
+ this.codeEditor && this.codeEditor.dispose();
2078
+ this.diffEditor && this.diffEditor.dispose();
2079
+ this.toDispose.forEach((disposable) => disposable.dispose());
2080
+ this.eventBus.fire(
2081
+ new EditorGroupDisposeEvent({
2082
+ group: this,
2083
+ }),
2084
+ );
2085
+ }
2086
+
2087
+ getState(): IEditorGroupState {
2088
+ const uris = this.resources.filter(couldRevive).map((r) => r.uri.toString());
2089
+ return {
2090
+ uris,
2091
+ current:
2092
+ this.currentResource && couldRevive(this.currentResource) ? this.currentResource.uri.toString() : undefined,
2093
+ previewIndex: this.previewURI ? uris.indexOf(this.previewURI.toString()) : -1,
2094
+ };
2095
+ }
2096
+
2097
+ isCodeEditorMode() {
2098
+ return !!this.currentOpenType && this.currentOpenType.type === EditorOpenType.code;
2099
+ }
2100
+
2101
+ isDiffEditorMode() {
2102
+ return !!this.currentOpenType && this.currentOpenType.type === EditorOpenType.diff;
2103
+ }
2104
+
2105
+ isComponentMode() {
2106
+ return !!this.currentOpenType && this.currentOpenType.type === EditorOpenType.component;
2107
+ }
2108
+
2109
+ async restoreState(state: IEditorGroupState) {
2110
+ this._restoringState = true;
2111
+ this.previewURI = state.uris[state.previewIndex] ? new URI(state.uris[state.previewIndex]) : null;
2112
+ await Promise.all(
2113
+ state.uris.map(async (uri) => {
2114
+ await this.doOpen(new URI(uri), {
2115
+ disableNavigate: true,
2116
+ backend: true,
2117
+ preview: false,
2118
+ deletedPolicy: 'skip',
2119
+ });
2120
+ }),
2121
+ );
2122
+
2123
+ let targetUri: URI | undefined;
2124
+ if (state.current) {
2125
+ targetUri = new URI(state.current);
2126
+ } else {
2127
+ if (state.uris.length > 0) {
2128
+ targetUri = new URI(state.uris[state.uris.length - 1]!);
2129
+ }
2130
+ }
2131
+ if (targetUri) {
2132
+ if (!(await this.open(targetUri, { deletedPolicy: 'skip' }))) {
2133
+ if (this.resources[0]) {
2134
+ await this.open(this.resources[0].uri);
2135
+ }
2136
+ }
2137
+ }
2138
+ this._restoringState = false;
2139
+ this.notifyTabChanged();
2140
+ }
2141
+
2142
+ async saveAll(includeUntitled?: boolean, reason?: SaveReason) {
2143
+ for (const r of this.resources) {
2144
+ // 不保存无标题文件
2145
+ if (!includeUntitled && r.uri.scheme === Schemes.untitled) {
2146
+ continue;
2147
+ }
2148
+ await this.saveResource(r, reason);
2149
+ }
2150
+ }
2151
+
2152
+ async saveResource(resource: IResource, reason: SaveReason = SaveReason.Manual) {
2153
+ // 尝试使用 openType 提供的保存方法保存
2154
+ if (await this.saveByOpenType(resource, reason)) {
2155
+ return;
2156
+ }
2157
+
2158
+ // 否则使用 document 进行保存 (如果有)
2159
+ const docRef = this.documentModelManager.getModelReference(resource.uri);
2160
+ if (docRef) {
2161
+ if (docRef.instance.dirty) {
2162
+ await docRef.instance.save(undefined, reason);
2163
+ }
2164
+ docRef.dispose();
2165
+ }
2166
+ }
2167
+
2168
+ async saveByOpenType(resource: IResource, reason: SaveReason): Promise<boolean> {
2169
+ const openType = this.cachedResourcesActiveOpenTypes.get(resource.uri.toString());
2170
+ if (openType && openType.saveResource) {
2171
+ try {
2172
+ await openType.saveResource(resource, reason);
2173
+ return true;
2174
+ } catch (e) {
2175
+ this.logger.error(e);
2176
+ }
2177
+ }
2178
+ return false;
2179
+ }
2180
+
2181
+ async saveCurrent(reason: SaveReason = SaveReason.Manual) {
2182
+ const resource = this.currentResource;
2183
+ if (!resource) {
2184
+ return;
2185
+ }
2186
+ if (await this.saveByOpenType(resource, reason)) {
2187
+ return;
2188
+ }
2189
+ if (this.currentEditor) {
2190
+ return this.currentEditor.save();
2191
+ }
2192
+ }
2193
+
2194
+ hasDirty(): boolean {
2195
+ for (const r of this.resources) {
2196
+ const docRef = this.documentModelManager.getModelReference(r.uri);
2197
+ if (docRef) {
2198
+ const isDirty = docRef.instance.dirty;
2199
+ docRef.dispose();
2200
+ if (isDirty) {
2201
+ return true;
2202
+ }
2203
+ }
2204
+ }
2205
+ return false;
2206
+ }
2207
+
2208
+ /**
2209
+ * 计算 dirty 数量
2210
+ */
2211
+ calcDirtyCount(countedUris: Set<string> = new Set<string>()): number {
2212
+ let count = 0;
2213
+ for (const r of this.resources) {
2214
+ const docRef = this.documentModelManager.getModelReference(r.uri, 'calc-dirty-count');
2215
+ if (countedUris.has(r.uri.toString())) {
2216
+ continue;
2217
+ }
2218
+ countedUris.add(r.uri.toString());
2219
+ if (docRef) {
2220
+ const isDirty = docRef.instance.dirty;
2221
+ docRef.dispose();
2222
+ if (isDirty) {
2223
+ count += 1;
2224
+ }
2225
+ }
2226
+ }
2227
+ return count;
2228
+ }
2229
+
2230
+ componentUndo() {
2231
+ const currentOpenType = this.currentOpenType;
2232
+ if (currentOpenType?.undo) {
2233
+ currentOpenType.undo(this.currentResource!);
2234
+ }
2235
+ }
2236
+
2237
+ componentRedo() {
2238
+ const currentOpenType = this.currentOpenType;
2239
+ if (currentOpenType?.redo) {
2240
+ currentOpenType.redo(this.currentResource!);
2241
+ }
2242
+ }
2243
+
2244
+ /**
2245
+ * 防止作为参数被抛入插件进程时出错
2246
+ */
2247
+ toJSON() {
2248
+ return {
2249
+ name: this.name,
2250
+ };
2251
+ }
2252
+ }
2253
+
2254
+ function findSuitableOpenType(
2255
+ currentAvailable: IEditorOpenType[],
2256
+ prev: IEditorOpenType | undefined,
2257
+ resource: IResource,
2258
+ editorAssociations: { [key in string]: string } | undefined,
2259
+ forceOpenType?: IEditorOpenType,
2260
+ ) {
2261
+ if (forceOpenType) {
2262
+ return currentAvailable.find((p) => openTypeSimilar(p, forceOpenType)) || currentAvailable[0];
2263
+ } else if (prev) {
2264
+ return currentAvailable.find((p) => openTypeSimilar(p, prev)) || currentAvailable[0];
2265
+ }
2266
+
2267
+ if (editorAssociations) {
2268
+ // 如果配置了 workbench.editorAssociations 且 priority 为 option 的情况下符合规则的默认打开方式行为
2269
+ const matchAvailableType = currentAvailable.find((p) => {
2270
+ const matchAssKey = Object.keys(editorAssociations).find(
2271
+ (r) => match(r, resource.uri.path.toString().toLowerCase()) || match(r, resource.uri.path.base.toLowerCase()),
2272
+ );
2273
+ const viewType = matchAssKey && editorAssociations[matchAssKey];
2274
+ if (!viewType) {
2275
+ return false;
2276
+ }
2277
+
2278
+ return p.componentId === viewType;
2279
+ });
2280
+
2281
+ if (matchAvailableType) {
2282
+ return matchAvailableType;
2283
+ }
2284
+ }
2285
+
2286
+ return currentAvailable[0];
2287
+ }
2288
+
2289
+ function openTypeSimilar(a: IEditorOpenType, b: IEditorOpenType) {
2290
+ return a.type === b.type && (a.type !== EditorOpenType.component || a.componentId === b.componentId);
2291
+ }