@opensumi/ide-editor 3.8.3-next-1745907516.0 → 3.8.3-next-1746586819.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 (67) hide show
  1. package/lib/browser/editor-collection.service.d.ts +4 -0
  2. package/lib/browser/editor-collection.service.d.ts.map +1 -1
  3. package/lib/browser/editor-collection.service.js +8 -0
  4. package/lib/browser/editor-collection.service.js.map +1 -1
  5. package/lib/browser/editor.contribution.d.ts +1 -0
  6. package/lib/browser/editor.contribution.d.ts.map +1 -1
  7. package/lib/browser/editor.contribution.js +22 -0
  8. package/lib/browser/editor.contribution.js.map +1 -1
  9. package/lib/browser/editor.module.less +6 -0
  10. package/lib/browser/editor.view.d.ts.map +1 -1
  11. package/lib/browser/editor.view.js +18 -0
  12. package/lib/browser/editor.view.js.map +1 -1
  13. package/lib/browser/index.d.ts +4 -1
  14. package/lib/browser/index.d.ts.map +1 -1
  15. package/lib/browser/index.js +20 -1
  16. package/lib/browser/index.js.map +1 -1
  17. package/lib/browser/multi-diff/multi-diff-editor.d.ts +46 -0
  18. package/lib/browser/multi-diff/multi-diff-editor.d.ts.map +1 -0
  19. package/lib/browser/multi-diff/multi-diff-editor.js +209 -0
  20. package/lib/browser/multi-diff/multi-diff-editor.js.map +1 -0
  21. package/lib/browser/multi-diff/multi-diff-resolver.d.ts +10 -0
  22. package/lib/browser/multi-diff/multi-diff-resolver.d.ts.map +1 -0
  23. package/lib/browser/multi-diff/multi-diff-resolver.js +42 -0
  24. package/lib/browser/multi-diff/multi-diff-resolver.js.map +1 -0
  25. package/lib/browser/multi-diff/multi-diff-resource.d.ts +23 -0
  26. package/lib/browser/multi-diff/multi-diff-resource.d.ts.map +1 -0
  27. package/lib/browser/multi-diff/multi-diff-resource.js +67 -0
  28. package/lib/browser/multi-diff/multi-diff-resource.js.map +1 -0
  29. package/lib/browser/multi-diff/multi-diff.contribution.d.ts +13 -0
  30. package/lib/browser/multi-diff/multi-diff.contribution.d.ts.map +1 -0
  31. package/lib/browser/multi-diff/multi-diff.contribution.js +51 -0
  32. package/lib/browser/multi-diff/multi-diff.contribution.js.map +1 -0
  33. package/lib/browser/multi-diff/resolver.service.d.ts +9 -0
  34. package/lib/browser/multi-diff/resolver.service.d.ts.map +1 -0
  35. package/lib/browser/multi-diff/resolver.service.js +35 -0
  36. package/lib/browser/multi-diff/resolver.service.js.map +1 -0
  37. package/lib/browser/types.d.ts +5 -0
  38. package/lib/browser/types.d.ts.map +1 -1
  39. package/lib/browser/types.js +2 -1
  40. package/lib/browser/types.js.map +1 -1
  41. package/lib/browser/workbench-editor.service.d.ts +10 -0
  42. package/lib/browser/workbench-editor.service.d.ts.map +1 -1
  43. package/lib/browser/workbench-editor.service.js +36 -0
  44. package/lib/browser/workbench-editor.service.js.map +1 -1
  45. package/lib/common/editor.d.ts +12 -1
  46. package/lib/common/editor.d.ts.map +1 -1
  47. package/lib/common/editor.js +1 -0
  48. package/lib/common/editor.js.map +1 -1
  49. package/lib/common/multi-diff.d.ts +61 -0
  50. package/lib/common/multi-diff.d.ts.map +1 -0
  51. package/lib/common/multi-diff.js +22 -0
  52. package/lib/common/multi-diff.js.map +1 -0
  53. package/package.json +14 -14
  54. package/src/browser/editor-collection.service.ts +11 -1
  55. package/src/browser/editor.contribution.ts +36 -1
  56. package/src/browser/editor.module.less +6 -0
  57. package/src/browser/editor.view.tsx +22 -0
  58. package/src/browser/index.ts +19 -1
  59. package/src/browser/multi-diff/multi-diff-editor.ts +249 -0
  60. package/src/browser/multi-diff/multi-diff-resolver.ts +44 -0
  61. package/src/browser/multi-diff/multi-diff-resource.ts +59 -0
  62. package/src/browser/multi-diff/multi-diff.contribution.ts +54 -0
  63. package/src/browser/multi-diff/resolver.service.ts +35 -0
  64. package/src/browser/types.ts +7 -0
  65. package/src/browser/workbench-editor.service.ts +51 -0
  66. package/src/common/editor.ts +14 -0
  67. package/src/common/multi-diff.ts +87 -0
@@ -27,6 +27,7 @@ import {
27
27
  IDecorationApplyOptions,
28
28
  IDiffEditor,
29
29
  IEditor,
30
+ IResource,
30
31
  IResourceOpenOptions,
31
32
  IUndoStopOptions,
32
33
  ResourceDecorationNeedChangeEvent,
@@ -36,8 +37,9 @@ import { IEditorDocumentModel, IEditorDocumentModelRef, isTextEditorViewState }
36
37
  import { MonacoEditorDecorationApplier } from './decoration-applier';
37
38
  import { EditorDocumentModelContentChangedEvent, IEditorDocumentModelService } from './doc-model/types';
38
39
  import { EditorFeatureRegistryImpl } from './feature';
40
+ import { BrowserMultiDiffEditor } from './multi-diff/multi-diff-editor';
39
41
  import { getConvertedMonacoOptions, isDiffEditorOption, isEditorOption } from './preference/converter';
40
- import { IEditorFeatureRegistry } from './types';
42
+ import { IConvertedMonacoOptions, IEditorFeatureRegistry } from './types';
41
43
 
42
44
  import type {
43
45
  ICodeEditor as IMonacoCodeEditor,
@@ -152,6 +154,14 @@ export class EditorCollectionServiceImpl extends WithEventBus implements EditorC
152
154
  return editor;
153
155
  }
154
156
 
157
+ createMultiDiffEditor(dom: HTMLElement, options?: any, overrides?: { [key: string]: any }) {
158
+ const convertedOptions = getConvertedMonacoOptions(this.configurationService);
159
+ const monacoMultiDiffEditorWidget = this.monacoService.createMultiDiffEditorWidget(dom, overrides);
160
+ const mergedOptions: IConvertedMonacoOptions = { ...convertedOptions.diffOptions, ...options };
161
+ const editor = this.injector.get(BrowserMultiDiffEditor, [monacoMultiDiffEditorWidget, mergedOptions]);
162
+ return editor;
163
+ }
164
+
155
165
  public createMergeEditor(dom: HTMLElement, options?: any, overrides?: { [key: string]: any }) {
156
166
  const convertedOptions = getConvertedMonacoOptions(this.configurationService);
157
167
  const mergedOptions: IDiffEditorConstructionOptions = {
@@ -44,7 +44,7 @@ import { IMenuRegistry, MenuContribution, MenuId } from '@opensumi/ide-core-brow
44
44
  import { AbstractContextMenuService } from '@opensumi/ide-core-browser/lib/menu/next/menu.interface';
45
45
  import { ICtxMenuRenderer } from '@opensumi/ide-core-browser/lib/menu/next/renderer/ctxmenu/base';
46
46
  import { IRelaxedOpenMergeEditorArgs } from '@opensumi/ide-core-browser/lib/monaco/merge-editor-widget';
47
- import { IDisposable, ILogger, PreferenceScope, isWindows } from '@opensumi/ide-core-common';
47
+ import { IDisposable, ILogger, PreferenceScope, UriComponents, isWindows } from '@opensumi/ide-core-common';
48
48
  import { MergeEditorService } from '@opensumi/ide-monaco/lib/browser/contrib/merge-editor/merge-editor.service';
49
49
  import { ITextmateTokenizer, ITextmateTokenizerService } from '@opensumi/ide-monaco/lib/browser/contrib/tokenizer';
50
50
  import { EOL } from '@opensumi/ide-monaco/lib/browser/monaco-api/types';
@@ -68,6 +68,7 @@ import {
68
68
  WorkbenchEditorService,
69
69
  } from '../common';
70
70
  import { AUTO_SAVE_MODE } from '../common/editor';
71
+ import { MultiDiffEditorItem } from '../common/multi-diff';
71
72
 
72
73
  import { MonacoTextModelService } from './doc-model/override';
73
74
  import { IEditorDocumentModelContentRegistry, IEditorDocumentModelService } from './doc-model/types';
@@ -79,6 +80,7 @@ import { DocumentFormatService } from './format/format.service';
79
80
  import { FormattingSelector } from './format/formatter-selector';
80
81
  import { EditorHistoryService } from './history';
81
82
  import { EditorContextMenuController } from './menu/editor.context';
83
+ import { MultiDiffResolver } from './multi-diff/multi-diff-resolver';
82
84
  import { NavigationMenuContainer } from './navigation.view';
83
85
  import { GoToLineQuickOpenHandler } from './quick-open/go-to-line';
84
86
  import { WorkspaceSymbolQuickOpenHandler } from './quick-open/workspace-symbol-quickopen';
@@ -231,6 +233,9 @@ export class EditorContribution
231
233
  @Autowired(ICtxMenuRenderer)
232
234
  private readonly contextMenuRenderer: ICtxMenuRenderer;
233
235
 
236
+ @Autowired(MultiDiffResolver)
237
+ private readonly multiDiffResolver: MultiDiffResolver;
238
+
234
239
  registerMonacoDefaultFormattingSelector(register: (selector: IFormattingEditProviderSelector) => IDisposable): void {
235
240
  const formatSelector = this.injector.get(FormattingSelector);
236
241
  this.addDispose(register(formatSelector.selectFormatter.bind(formatSelector)));
@@ -1231,6 +1236,36 @@ export class EditorContribution
1231
1236
  formatService.formatSelectionWith();
1232
1237
  },
1233
1238
  });
1239
+
1240
+ commands.registerCommand(
1241
+ {
1242
+ id: EDITOR_COMMANDS.VSCODE_OPEN_MULTI_DIFF_EDITOR_COMMAND_ID,
1243
+ },
1244
+ {
1245
+ execute: async (options: {
1246
+ title: string;
1247
+ multiDiffSourceUri: UriComponents;
1248
+ resources: { originalUri?: UriComponents; modifiedUri?: UriComponents }[];
1249
+ }) => {
1250
+ const sources: MultiDiffEditorItem[] = [];
1251
+ for (const { originalUri, modifiedUri } of options.resources) {
1252
+ sources.push(
1253
+ new MultiDiffEditorItem(
1254
+ originalUri ? URI.from(originalUri) : undefined,
1255
+ modifiedUri ? URI.from(modifiedUri) : undefined,
1256
+ modifiedUri ? URI.from(modifiedUri) : undefined,
1257
+ ),
1258
+ );
1259
+ }
1260
+ const multiDiffSourceUri = URI.from(options.multiDiffSourceUri);
1261
+ // 提前注册到 resolver 内,相同的multiDiffSourceUri支持更新
1262
+ this.multiDiffResolver.registerSources(multiDiffSourceUri, sources);
1263
+ await this.workbenchEditorService.open(multiDiffSourceUri, {
1264
+ label: options.title,
1265
+ });
1266
+ },
1267
+ },
1268
+ );
1234
1269
  }
1235
1270
 
1236
1271
  registerMenus(menus: IMenuRegistry) {
@@ -6,6 +6,12 @@
6
6
  background-color: var(--editor-background);
7
7
  display: flex;
8
8
  border-top: 1px solid var(--tab-border);
9
+
10
+ .kt_editor_multi_diff_editor {
11
+ height: 100%;
12
+ width: 100%;
13
+ position: relative;
14
+ }
9
15
  }
10
16
  .kt_editor_main_wrapper {
11
17
  flex-grow: 1;
@@ -316,6 +316,7 @@ export const EditorGroupBody = ({ group }: { group: EditorGroup }) => {
316
316
  const components: React.ReactNode[] = [];
317
317
  const codeEditorRef = React.useRef<HTMLDivElement>(null);
318
318
  const diffEditorRef = React.useRef<HTMLDivElement>(null);
319
+ const multiDiffEditorRef = React.useRef<HTMLDivElement>(null);
319
320
  const mergeEditorRef = React.useRef<HTMLDivElement>(null);
320
321
  const [, updateState] = React.useState<any>();
321
322
  const forceUpdate = React.useCallback(() => updateState({}), []);
@@ -367,6 +368,21 @@ export const EditorGroupBody = ({ group }: { group: EditorGroup }) => {
367
368
  };
368
369
  }, []);
369
370
 
371
+ React.useEffect(() => {
372
+ if (multiDiffEditorRef.current) {
373
+ group.attachMultiDiffEditorDom(multiDiffEditorRef.current);
374
+ const observer = new ResizeObserver((entries) => {
375
+ const entry = entries[0];
376
+ if (entry && entry.contentRect.height > 0) {
377
+ group.doLayoutEditors();
378
+ observer.disconnect();
379
+ }
380
+ });
381
+ observer.observe(multiDiffEditorRef.current);
382
+ return () => observer.disconnect();
383
+ }
384
+ }, [multiDiffEditorRef.current]);
385
+
370
386
  group.activeComponents.forEach((resources, component) => {
371
387
  const initialProps = group.activateComponentsProps.get(component);
372
388
  components.push(
@@ -478,6 +494,12 @@ export const EditorGroupBody = ({ group }: { group: EditorGroup }) => {
478
494
  })}
479
495
  ref={diffEditorRef}
480
496
  />
497
+ <div
498
+ className={cls(styles.kt_editor_multi_diff_editor, styles_kt_editor_component, {
499
+ [styles.kt_hidden]: !group.currentOpenType || group.currentOpenType.type !== EditorOpenType.multiDiff,
500
+ })}
501
+ ref={multiDiffEditorRef}
502
+ />
481
503
  <div
482
504
  className={cls(styles.kt_editor_diff_3_editor, styles_kt_editor_component, {
483
505
  [styles.kt_hidden]: !group.currentOpenType || group.currentOpenType.type !== EditorOpenType.mergeEditor,
@@ -18,6 +18,7 @@ import { ITypeHierarchyService } from '@opensumi/ide-monaco/lib/browser/contrib/
18
18
 
19
19
  import { EditorCollectionService, ILanguageService, ResourceService, WorkbenchEditorService } from '../common';
20
20
  import { IDocPersistentCacheProvider } from '../common/doc-cache';
21
+ import { IMultiDiffSourceResolverService } from '../common/multi-diff';
21
22
 
22
23
  import { BreadCrumbServiceImpl } from './breadcrumb';
23
24
  import { EditorComponentRegistryImpl } from './component';
@@ -52,6 +53,8 @@ import {
52
53
  MonacoCommandService,
53
54
  } from './monaco-contrib/command/command.service';
54
55
  import { TextmateService } from './monaco-contrib/tokenizer/textmate.service';
56
+ import { MultiDiffEditorContribution } from './multi-diff/multi-diff.contribution';
57
+ import { MultiDiffSourceResolverService } from './multi-diff/resolver.service';
55
58
  import { NotebookService } from './notebook.service';
56
59
  import { EditorPreferenceContribution } from './preference/contribution';
57
60
  import { EditorPreferences, editorPreferenceSchema } from './preference/schema';
@@ -67,6 +70,7 @@ import {
67
70
  IEditorTabService,
68
71
  ILanguageStatusService,
69
72
  INotebookService,
73
+ MultiDiffSourceContribution,
70
74
  } from './types';
71
75
  import { UntitledDocumentIdCounter } from './untitled-resource';
72
76
  import { WorkbenchEditorServiceImpl } from './workbench-editor.service';
@@ -180,8 +184,13 @@ export class EditorModule extends BrowserModule {
180
184
  token: INotebookService,
181
185
  useClass: NotebookService,
182
186
  },
187
+ {
188
+ token: IMultiDiffSourceResolverService,
189
+ useClass: MultiDiffSourceResolverService,
190
+ },
183
191
  EditorPreferenceContribution,
184
192
  DefaultDiffEditorContribution,
193
+ MultiDiffEditorContribution,
185
194
  MergeEditorContribution,
186
195
  EditorClientAppContribution,
187
196
  EditorContribution,
@@ -195,7 +204,7 @@ export class EditorModule extends BrowserModule {
195
204
  OpenTypeMenuContribution,
196
205
  ];
197
206
  electronProviders = [EditorElectronContribution];
198
- contributionProvider = BrowserEditorContribution;
207
+ contributionProvider = [BrowserEditorContribution, MultiDiffSourceContribution];
199
208
  }
200
209
 
201
210
  @Domain(ClientAppContribution)
@@ -224,9 +233,15 @@ export class EditorClientAppContribution implements ClientAppContribution {
224
233
  @Autowired(IEditorDocumentModelService)
225
234
  modelService: EditorDocumentModelServiceImpl;
226
235
 
236
+ @Autowired(IMultiDiffSourceResolverService)
237
+ multiDiffSourceResolverService: IMultiDiffSourceResolverService;
238
+
227
239
  @Autowired(BrowserEditorContribution)
228
240
  private readonly contributions: ContributionProvider<BrowserEditorContribution>;
229
241
 
242
+ @Autowired(MultiDiffSourceContribution)
243
+ private readonly multiDiffSourceContribution: ContributionProvider<MultiDiffSourceContribution>;
244
+
230
245
  async initialize() {
231
246
  for (const contribution of this.contributions.getContributions()) {
232
247
  if (contribution.registerResource) {
@@ -245,6 +260,9 @@ export class EditorClientAppContribution implements ClientAppContribution {
245
260
  contribution.registerEditorFeature(this.editorFeatureRegistry);
246
261
  }
247
262
  }
263
+ for (const contribution of this.multiDiffSourceContribution.getContributions()) {
264
+ contribution.registerMultiDiffSourceResolver(this.multiDiffSourceResolverService);
265
+ }
248
266
  this.workbenchEditorService.contributionsReady.resolve();
249
267
  await Promise.all([this.workbenchEditorService.initialize(), this.modelService.initialize()]);
250
268
  }
@@ -0,0 +1,249 @@
1
+ import { Autowired, INJECTOR_TOKEN, Injectable, Injector } from '@opensumi/di';
2
+ import { PreferenceService } from '@opensumi/ide-core-browser';
3
+ import {
4
+ Disposable,
5
+ DisposableStore,
6
+ Emitter,
7
+ IDisposable,
8
+ OnEvent,
9
+ URI,
10
+ WithEventBus,
11
+ } from '@opensumi/ide-core-common';
12
+ import {
13
+ ValueWithChangeEventFromObservable,
14
+ constObservable,
15
+ derived,
16
+ mapObservableArrayCached,
17
+ observableFromValueWithChangeEvent,
18
+ observableValue,
19
+ recomputeInitiallyAndOnChange,
20
+ } from '@opensumi/ide-monaco/lib/common/observable';
21
+ import { IMessageService } from '@opensumi/ide-overlay';
22
+ import { Dimension } from '@opensumi/monaco-editor-core/esm/vs/base/browser/dom';
23
+ import { ValueWithChangeEvent } from '@opensumi/monaco-editor-core/esm/vs/base/common/event';
24
+ import { ICodeEditor } from '@opensumi/monaco-editor-core/esm/vs/editor/browser/editorBrowser';
25
+ import { RefCounted } from '@opensumi/monaco-editor-core/esm/vs/editor/browser/widget/diffEditor/utils';
26
+ import {
27
+ IDocumentDiffItem,
28
+ IMultiDiffEditorModel,
29
+ } from '@opensumi/monaco-editor-core/esm/vs/editor/browser/widget/multiDiffEditor/model';
30
+ import { MultiDiffEditorWidget } from '@opensumi/monaco-editor-core/esm/vs/editor/browser/widget/multiDiffEditor/multiDiffEditorWidget';
31
+ import { IMultiDiffResourceId } from '@opensumi/monaco-editor-core/esm/vs/editor/browser/widget/multiDiffEditor/multiDiffEditorWidgetImpl';
32
+ import { Range } from '@opensumi/monaco-editor-core/esm/vs/editor/common/core/range';
33
+ import { IDiffEditor } from '@opensumi/monaco-editor-core/esm/vs/editor/common/editorCommon';
34
+
35
+ import { IEditorDocumentModelRef, IResourceOpenOptions } from '../../common/editor';
36
+ import { IMultiDiffEditor, IMultiDiffSourceResolverService, IResolvedMultiDiffSource } from '../../common/multi-diff';
37
+ import { EditorDocumentModelContentChangedEvent, IEditorDocumentModelService } from '../doc-model/types';
38
+ import { IConvertedMonacoOptions, IResource, ResourceDecorationNeedChangeEvent } from '../types';
39
+
40
+ @Injectable({ multiple: true })
41
+ export class BrowserMultiDiffEditor extends WithEventBus implements IMultiDiffEditor {
42
+ @Autowired(INJECTOR_TOKEN)
43
+ protected readonly injector: Injector;
44
+
45
+ @Autowired(IMessageService)
46
+ private readonly messageService: IMessageService;
47
+
48
+ @Autowired(IEditorDocumentModelService)
49
+ documentModelManager: IEditorDocumentModelService;
50
+
51
+ @Autowired(PreferenceService)
52
+ private readonly preferenceService: PreferenceService;
53
+
54
+ @Autowired(IMultiDiffSourceResolverService)
55
+ private readonly multiDiffSourceResolverService: IMultiDiffSourceResolverService;
56
+
57
+ private multiDiffModelChangeEmitter = new Emitter<IMultiDiffEditorModel>();
58
+ public readonly onMultiDiffModelChange = this.multiDiffModelChangeEmitter.event;
59
+
60
+ private viewStateMap: Map<string, any> = new Map();
61
+ private currentUri: URI | undefined;
62
+
63
+ private multiDiffModel: IMultiDiffEditorModel & IDisposable;
64
+
65
+ constructor(private multiDiffWidget: MultiDiffEditorWidget, private convertedOptions: IConvertedMonacoOptions) {
66
+ super();
67
+ }
68
+
69
+ @OnEvent(EditorDocumentModelContentChangedEvent)
70
+ onDocModelContentChangedEvent(e: EditorDocumentModelContentChangedEvent) {
71
+ if (!this.currentUri) {
72
+ return;
73
+ }
74
+ // TODO: 目前的设计下,不支持动态修改 resource 的 name,diff 数量变化时会有问题
75
+ if (
76
+ this.multiDiffModel?.documents.value === 'loading' ||
77
+ !this.multiDiffModel.documents.value.some(
78
+ (document) => document.object.modified?.uri.toString() === e.payload.uri.codeUri.toString(),
79
+ )
80
+ ) {
81
+ return;
82
+ }
83
+ this.eventBus.fire(
84
+ new ResourceDecorationNeedChangeEvent({
85
+ uri: this.currentUri,
86
+ decoration: {
87
+ dirty: !!e.payload.dirty,
88
+ readOnly: !!e.payload.readonly,
89
+ },
90
+ }),
91
+ );
92
+ }
93
+
94
+ private saveViewState(uri: URI) {
95
+ if (!uri) {
96
+ return;
97
+ }
98
+ const key = uri.toString();
99
+ const state = this.multiDiffWidget.getViewState();
100
+ if (state) {
101
+ this.viewStateMap.set(key, state);
102
+ }
103
+ }
104
+
105
+ private restoreViewState(uri: URI) {
106
+ if (!uri) {
107
+ return;
108
+ }
109
+ const key = uri.toString();
110
+ const state = this.viewStateMap.get(key);
111
+ if (state) {
112
+ this.multiDiffWidget.setViewState(state);
113
+ }
114
+ }
115
+
116
+ async compareMultiple(resource: IResource, options?: IResourceOpenOptions): Promise<void> {
117
+ // Save current view state before changing
118
+ if (this.currentUri) {
119
+ this.saveViewState(this.currentUri);
120
+ }
121
+
122
+ const source: IResolvedMultiDiffSource | undefined = resource.metadata.sources?.length
123
+ ? { resources: ValueWithChangeEvent.const(resource.metadata.sources) }
124
+ : await this.multiDiffSourceResolverService.resolve(resource.uri);
125
+ const resources = source ? observableFromValueWithChangeEvent(this, source.resources) : constObservable([]);
126
+
127
+ const documentsWithPromises = mapObservableArrayCached(
128
+ this,
129
+ resources,
130
+ async (r, store) => {
131
+ /** @description documentsWithPromises */
132
+ let original: IEditorDocumentModelRef | undefined;
133
+ let modified: IEditorDocumentModelRef | undefined;
134
+
135
+ const multiDiffItemStore = new DisposableStore();
136
+
137
+ try {
138
+ [original, modified] = await Promise.all([
139
+ r.originalUri ? this.documentModelManager.createModelReference(r.originalUri) : undefined,
140
+ r.modifiedUri ? this.documentModelManager.createModelReference(r.modifiedUri) : undefined,
141
+ ]);
142
+ // console.log('original', original, 'modified', modified);
143
+ if (original) {
144
+ multiDiffItemStore.add(original);
145
+ }
146
+ if (modified) {
147
+ multiDiffItemStore.add(modified);
148
+ }
149
+ } catch (e) {
150
+ // e.g. "File seems to be binary and cannot be opened as text"
151
+ this.messageService.error(e.message);
152
+ // eslint-disable-next-line no-console
153
+ console.error(e);
154
+ return undefined;
155
+ }
156
+ const uri = (r.modifiedUri ?? r.originalUri)!;
157
+ const result = {
158
+ multiDiffEditorItem: r,
159
+ original: original?.instance.getMonacoModel(),
160
+ modified: modified?.instance.getMonacoModel(),
161
+ contextKeys: r.contextKeys,
162
+ options: {
163
+ readOnly: modified?.instance.readonly,
164
+ // TODO: codelens,wordWrap options
165
+ ...this.convertedOptions.diffOptions,
166
+ },
167
+ // TODO: 监听配置变化验证
168
+ onOptionsDidChange: (h) =>
169
+ this.preferenceService.onPreferenceChanged((e) => {
170
+ if (e.affects(uri.toString()) && e.preferenceName.includes('editor')) {
171
+ h();
172
+ }
173
+ }),
174
+ };
175
+ return store.add(RefCounted.createOfNonDisposable(result, multiDiffItemStore, this));
176
+ },
177
+ (i) => JSON.stringify([i.modifiedUri?.toString(), i.originalUri?.toString()]),
178
+ );
179
+
180
+ const documents = observableValue<readonly RefCounted<IDocumentDiffItem>[] | 'loading'>('documents', 'loading');
181
+
182
+ const updateDocuments = derived(async (reader) => {
183
+ /** @description Update documents */
184
+ const docsPromises = documentsWithPromises.read(reader);
185
+ const docs = await Promise.all(docsPromises);
186
+ const newDocuments = docs.filter((item) => item !== undefined);
187
+ documents.set(newDocuments, undefined);
188
+ });
189
+
190
+ const a = recomputeInitiallyAndOnChange(updateDocuments);
191
+ await updateDocuments.get();
192
+ this.multiDiffModel?.dispose();
193
+ this.multiDiffModel = {
194
+ dispose: () => a.dispose(),
195
+ documents: new ValueWithChangeEventFromObservable(documents),
196
+ contextKeys: source?.contextKeys,
197
+ };
198
+
199
+ const viewModel = this.multiDiffWidget.createViewModel(this.multiDiffModel);
200
+ await viewModel.waitForDiffs();
201
+ this.multiDiffWidget.setViewModel(viewModel);
202
+
203
+ // Update current URI and restore view state
204
+ this.currentUri = resource.uri;
205
+ this.restoreViewState(resource.uri);
206
+
207
+ this.multiDiffModelChangeEmitter.fire(this.multiDiffModel);
208
+ }
209
+
210
+ getDiffEntry(uri: URI): IDocumentDiffItem | undefined {
211
+ return this.multiDiffWidget.findDocumentDiffItem(uri.codeUri);
212
+ }
213
+
214
+ getCurrentDiffEntry(): IDocumentDiffItem | undefined {
215
+ const activeControl = this.multiDiffWidget.getActiveControl();
216
+ if (!activeControl) {
217
+ return undefined;
218
+ }
219
+ const originalUri = activeControl.getOriginalEditor().getModel()?.uri;
220
+ if (!originalUri) {
221
+ return undefined;
222
+ }
223
+ return this.multiDiffWidget.findDocumentDiffItem(originalUri);
224
+ }
225
+
226
+ reveal(resource: IMultiDiffResourceId, options?: { range?: Range; highlight: boolean }): void {
227
+ this.multiDiffWidget.reveal(resource, options);
228
+ }
229
+
230
+ layout(dimension: Dimension): void {
231
+ this.multiDiffWidget.layout(dimension);
232
+ }
233
+
234
+ focus(): void {
235
+ const activeControl = this.multiDiffWidget.getActiveControl();
236
+ if (activeControl) {
237
+ activeControl.focus();
238
+ }
239
+ }
240
+
241
+ tryGetCodeEditor(uri: URI): { diffEditor: IDiffEditor; editor: ICodeEditor } | undefined {
242
+ return this.multiDiffWidget.tryGetCodeEditor(uri.codeUri);
243
+ }
244
+
245
+ dispose(): void {
246
+ super.dispose();
247
+ this.multiDiffWidget.dispose();
248
+ }
249
+ }
@@ -0,0 +1,44 @@
1
+ import { Injectable } from '@opensumi/di';
2
+ import { URI } from '@opensumi/ide-core-common';
3
+ import { ValueWithChangeEvent } from '@opensumi/monaco-editor-core/esm/vs/base/common/event';
4
+
5
+ import {
6
+ IMultiDiffSourceResolver,
7
+ IResolvedMultiDiffSource,
8
+ MULTI_DIFF_SCHEME,
9
+ MultiDiffEditorItem,
10
+ } from '../../common/multi-diff';
11
+
12
+ @Injectable()
13
+ export class MultiDiffResolver implements IMultiDiffSourceResolver {
14
+ private readonly sourceValues = new Map<string, ValueWithChangeEvent<MultiDiffEditorItem[]>>();
15
+
16
+ registerSources(uri: URI, sources: MultiDiffEditorItem[]) {
17
+ const key = uri.toString();
18
+ const saved = this.sourceValues.get(key);
19
+ if (saved) {
20
+ saved.value = sources;
21
+ } else {
22
+ this.sourceValues.set(key, new ValueWithChangeEvent(sources));
23
+ }
24
+ }
25
+
26
+ canHandleUri(uri: URI): boolean {
27
+ return uri.scheme === MULTI_DIFF_SCHEME;
28
+ }
29
+
30
+ async resolveDiffSource(uri: URI): Promise<IResolvedMultiDiffSource | undefined> {
31
+ const value = this.sourceValues.get(uri.toString());
32
+ if (!value) {
33
+ return undefined;
34
+ }
35
+
36
+ return {
37
+ resources: value,
38
+ };
39
+ }
40
+
41
+ dispose() {
42
+ this.sourceValues.clear();
43
+ }
44
+ }
@@ -0,0 +1,59 @@
1
+ import { Autowired, Injectable } from '@opensumi/di';
2
+ import { AppConfig, URI, WithEventBus, getIcon } from '@opensumi/ide-core-browser';
3
+ import { LabelService } from '@opensumi/ide-core-browser/lib/services';
4
+ import { IFileServiceClient } from '@opensumi/ide-file-service';
5
+
6
+ import { IResourceProvider, ResourceService } from '../../common';
7
+ import { IMultiDiffSourceResolverService } from '../../common/multi-diff';
8
+
9
+ @Injectable()
10
+ export class MultiDiffResourceProvider extends WithEventBus implements IResourceProvider {
11
+ @Autowired()
12
+ labelService: LabelService;
13
+
14
+ @Autowired(ResourceService)
15
+ resourceService: ResourceService;
16
+
17
+ @Autowired(IFileServiceClient)
18
+ protected fileServiceClient: IFileServiceClient;
19
+
20
+ @Autowired(AppConfig)
21
+ protected readonly appConfig: AppConfig;
22
+
23
+ @Autowired(IMultiDiffSourceResolverService)
24
+ private readonly multiDiffSourceResolverService: IMultiDiffSourceResolverService;
25
+
26
+ handlesUri(uri: URI): number {
27
+ const resolvers = this.multiDiffSourceResolverService.getResolvers();
28
+ for (const resolver of resolvers) {
29
+ if (resolver.canHandleUri(uri)) {
30
+ return 10;
31
+ }
32
+ }
33
+ return -1;
34
+ }
35
+
36
+ async provideResource(uri: URI) {
37
+ const { name, sources } = uri.getParsedQuery();
38
+ const parsedSources = sources ? JSON.parse(sources) : [];
39
+
40
+ // Get icon from the first modified file
41
+ const firstModifiedUri = parsedSources.length > 0 ? new URI(parsedSources[0].modifiedUri) : undefined;
42
+ const icon = firstModifiedUri ? this.labelService.getIcon(firstModifiedUri) : undefined;
43
+
44
+ return {
45
+ name: `Multi-Diff: ${name || parsedSources.length + ' files'}`,
46
+ icon: icon || getIcon('diff'),
47
+ uri,
48
+ supportsRevive: this.appConfig.enableDiffRevive ?? false,
49
+ metadata: {
50
+ sources: parsedSources,
51
+ },
52
+ };
53
+ }
54
+
55
+ async shouldCloseResource(resource, openedResources): Promise<boolean> {
56
+ // TODO: For now, always allow closing
57
+ return true;
58
+ }
59
+ }
@@ -0,0 +1,54 @@
1
+ import { Autowired } from '@opensumi/di';
2
+ import { Domain, IDisposable, URI } from '@opensumi/ide-core-browser';
3
+
4
+ import { ResourceService } from '../../common';
5
+ import { IMultiDiffSourceResolverService, IResolvedMultiDiffSource, MULTI_DIFF_SCHEME } from '../../common/multi-diff';
6
+ import {
7
+ BrowserEditorContribution,
8
+ EditorComponentRegistry,
9
+ EditorOpenType,
10
+ MultiDiffSourceContribution,
11
+ } from '../types';
12
+
13
+ import { MultiDiffResolver } from './multi-diff-resolver';
14
+ import { MultiDiffResourceProvider } from './multi-diff-resource';
15
+
16
+ @Domain(BrowserEditorContribution, MultiDiffSourceContribution)
17
+ export class MultiDiffEditorContribution implements BrowserEditorContribution, MultiDiffSourceContribution {
18
+ @Autowired(IMultiDiffSourceResolverService)
19
+ private readonly multiDiffSourceResolverService: IMultiDiffSourceResolverService;
20
+
21
+ @Autowired(MultiDiffResolver)
22
+ private readonly multiDiffResolver: MultiDiffResolver;
23
+
24
+ @Autowired()
25
+ private readonly multiDiffResourceProvider: MultiDiffResourceProvider;
26
+
27
+ registerMultiDiffSourceResolver(resolverService: IMultiDiffSourceResolverService): IDisposable {
28
+ // 内置实现,通过 command 使用
29
+ return resolverService.registerResolver(this.multiDiffResolver);
30
+ }
31
+
32
+ registerResource(resourceService: ResourceService): void {
33
+ resourceService.registerResourceProvider(this.multiDiffResourceProvider);
34
+ }
35
+
36
+ registerEditorComponent(registry: EditorComponentRegistry) {
37
+ registry.registerEditorComponentResolver(
38
+ (scheme) => {
39
+ const resolvers = this.multiDiffSourceResolverService.getResolvers();
40
+ for (const resolver of resolvers) {
41
+ if (resolver.canHandleUri(new URI(`${scheme}:/empty`))) {
42
+ return 10;
43
+ }
44
+ }
45
+ return -1;
46
+ },
47
+ (resource, results) => {
48
+ results.push({
49
+ type: EditorOpenType.multiDiff,
50
+ });
51
+ },
52
+ );
53
+ }
54
+ }
@@ -0,0 +1,35 @@
1
+ import { Injectable } from '@opensumi/di';
2
+ import { IDisposable, URI, toDisposable } from '@opensumi/ide-utils';
3
+
4
+ import {
5
+ IMultiDiffSourceResolver,
6
+ IMultiDiffSourceResolverService,
7
+ IResolvedMultiDiffSource,
8
+ } from '../../common/multi-diff';
9
+
10
+ @Injectable()
11
+ export class MultiDiffSourceResolverService implements IMultiDiffSourceResolverService {
12
+ private readonly _resolvers = new Set<IMultiDiffSourceResolver>();
13
+
14
+ registerResolver(resolver: IMultiDiffSourceResolver): IDisposable {
15
+ // throw on duplicate
16
+ if (this._resolvers.has(resolver)) {
17
+ throw new Error('Duplicate resolver');
18
+ }
19
+ this._resolvers.add(resolver);
20
+ return toDisposable(() => this._resolvers.delete(resolver));
21
+ }
22
+
23
+ resolve(uri: URI): Promise<IResolvedMultiDiffSource | undefined> {
24
+ for (const resolver of this._resolvers) {
25
+ if (resolver.canHandleUri(uri)) {
26
+ return resolver.resolveDiffSource(uri);
27
+ }
28
+ }
29
+ return Promise.resolve(undefined);
30
+ }
31
+
32
+ getResolvers(): IMultiDiffSourceResolver[] {
33
+ return Array.from(this._resolvers);
34
+ }
35
+ }