@opensumi/ide-editor 2.27.3-rc-1714116491.0 → 2.27.3-rc-1714982362.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 (89) hide show
  1. package/lib/browser/doc-model/editor-document-model-service.d.ts +3 -1
  2. package/lib/browser/doc-model/editor-document-model-service.d.ts.map +1 -1
  3. package/lib/browser/doc-model/editor-document-model-service.js +6 -1
  4. package/lib/browser/doc-model/editor-document-model-service.js.map +1 -1
  5. package/lib/browser/doc-model/types.d.ts +1 -0
  6. package/lib/browser/doc-model/types.d.ts.map +1 -1
  7. package/lib/browser/doc-model/types.js.map +1 -1
  8. package/lib/browser/editor-collection.service.d.ts +6 -6
  9. package/lib/browser/editor-collection.service.d.ts.map +1 -1
  10. package/lib/browser/editor-collection.service.js.map +1 -1
  11. package/lib/browser/editor-electron.contribution.d.ts +2 -2
  12. package/lib/browser/editor-electron.contribution.d.ts.map +1 -1
  13. package/lib/browser/editor-electron.contribution.js +1 -1
  14. package/lib/browser/editor-electron.contribution.js.map +1 -1
  15. package/lib/browser/editor.context.d.ts +7 -0
  16. package/lib/browser/editor.context.d.ts.map +1 -0
  17. package/lib/browser/editor.context.js +10 -0
  18. package/lib/browser/editor.context.js.map +1 -0
  19. package/lib/browser/editor.contribution.d.ts.map +1 -1
  20. package/lib/browser/editor.contribution.js +0 -5
  21. package/lib/browser/editor.contribution.js.map +1 -1
  22. package/lib/browser/editor.decoration.service.d.ts +12 -1
  23. package/lib/browser/editor.decoration.service.d.ts.map +1 -1
  24. package/lib/browser/editor.decoration.service.js +59 -30
  25. package/lib/browser/editor.decoration.service.js.map +1 -1
  26. package/lib/browser/editor.module.less +0 -54
  27. package/lib/browser/editor.view.d.ts.map +1 -1
  28. package/lib/browser/editor.view.js +70 -51
  29. package/lib/browser/editor.view.js.map +1 -1
  30. package/lib/browser/hooks/useEditor.d.ts +4 -0
  31. package/lib/browser/hooks/useEditor.d.ts.map +1 -0
  32. package/lib/browser/hooks/useEditor.js +31 -0
  33. package/lib/browser/hooks/useEditor.js.map +1 -0
  34. package/lib/browser/hooks/useInMergeChanges.d.ts +3 -0
  35. package/lib/browser/hooks/useInMergeChanges.d.ts.map +1 -0
  36. package/lib/browser/hooks/useInMergeChanges.js +27 -0
  37. package/lib/browser/hooks/useInMergeChanges.js.map +1 -0
  38. package/lib/browser/merge-conflict/conflict-parser.d.ts +46 -0
  39. package/lib/browser/merge-conflict/conflict-parser.d.ts.map +1 -0
  40. package/lib/browser/merge-conflict/conflict-parser.js +209 -0
  41. package/lib/browser/merge-conflict/conflict-parser.js.map +1 -0
  42. package/lib/browser/merge-conflict/index.d.ts +3 -0
  43. package/lib/browser/merge-conflict/index.d.ts.map +1 -0
  44. package/lib/browser/merge-conflict/index.js +6 -0
  45. package/lib/browser/merge-conflict/index.js.map +1 -0
  46. package/lib/browser/merge-conflict/types.d.ts +60 -0
  47. package/lib/browser/merge-conflict/types.d.ts.map +1 -0
  48. package/lib/browser/merge-conflict/types.js +15 -0
  49. package/lib/browser/merge-conflict/types.js.map +1 -0
  50. package/lib/browser/merge-editor/MergeEditorFloatComponents.d.ts.map +1 -1
  51. package/lib/browser/merge-editor/MergeEditorFloatComponents.js +73 -49
  52. package/lib/browser/merge-editor/MergeEditorFloatComponents.js.map +1 -1
  53. package/lib/browser/merge-editor/merge-editor.module.less +71 -0
  54. package/lib/browser/merge-editor/merge-editor.provider.d.ts.map +1 -1
  55. package/lib/browser/merge-editor/merge-editor.provider.js +3 -0
  56. package/lib/browser/merge-editor/merge-editor.provider.js.map +1 -1
  57. package/lib/browser/tab.view.d.ts.map +1 -1
  58. package/lib/browser/tab.view.js +6 -5
  59. package/lib/browser/tab.view.js.map +1 -1
  60. package/lib/browser/types.d.ts +6 -0
  61. package/lib/browser/types.d.ts.map +1 -1
  62. package/lib/browser/types.js.map +1 -1
  63. package/lib/browser/workbench-editor.service.d.ts +1 -1
  64. package/lib/browser/workbench-editor.service.js +1 -1
  65. package/lib/common/editor.d.ts +3 -1
  66. package/lib/common/editor.d.ts.map +1 -1
  67. package/lib/common/editor.js.map +1 -1
  68. package/package.json +14 -14
  69. package/src/browser/doc-model/editor-document-model-service.ts +11 -1
  70. package/src/browser/doc-model/types.ts +4 -0
  71. package/src/browser/editor-collection.service.ts +7 -7
  72. package/src/browser/editor-electron.contribution.ts +1 -2
  73. package/src/browser/editor.context.ts +11 -0
  74. package/src/browser/editor.contribution.ts +0 -6
  75. package/src/browser/editor.decoration.service.ts +75 -34
  76. package/src/browser/editor.module.less +0 -54
  77. package/src/browser/editor.view.tsx +102 -77
  78. package/src/browser/hooks/useEditor.ts +34 -0
  79. package/src/browser/hooks/useInMergeChanges.ts +30 -0
  80. package/src/browser/merge-conflict/conflict-parser.ts +323 -0
  81. package/src/browser/merge-conflict/index.ts +2 -0
  82. package/src/browser/merge-conflict/types.ts +73 -0
  83. package/src/browser/merge-editor/MergeEditorFloatComponents.tsx +111 -73
  84. package/src/browser/merge-editor/merge-editor.module.less +71 -0
  85. package/src/browser/merge-editor/merge-editor.provider.ts +3 -0
  86. package/src/browser/tab.view.tsx +7 -6
  87. package/src/browser/types.ts +8 -0
  88. package/src/browser/workbench-editor.service.ts +1 -1
  89. package/src/common/editor.ts +3 -1
@@ -10,6 +10,7 @@ import {
10
10
  ComponentRegistry,
11
11
  ConfigContext,
12
12
  ConfigProvider,
13
+ DisposableStore,
13
14
  ErrorBoundary,
14
15
  IEventBus,
15
16
  MaybeNull,
@@ -27,10 +28,12 @@ import {
27
28
  } from '@opensumi/ide-core-browser/lib/components';
28
29
  import { VIEW_CONTAINERS } from '@opensumi/ide-core-browser/lib/layout/view-id';
29
30
  import { useInjectable, useUpdateOnEventBusEvent } from '@opensumi/ide-core-browser/lib/react-hooks';
31
+ import { monaco } from '@opensumi/ide-monaco/lib/browser/monaco-api';
30
32
 
31
33
  import { IResource, WorkbenchEditorService } from '../common';
32
34
 
33
35
  import { EditorComponentRegistryImpl } from './component';
36
+ import { EditorContext, IEditorContext, defaultEditorContext } from './editor.context';
34
37
  import styles from './editor.module.less';
35
38
  import { EditorGrid, SplitDirection } from './grid/grid.service';
36
39
  import { NavigationBar } from './navigation.view';
@@ -324,6 +327,8 @@ export const EditorGroupView = observer(({ group }: { group: EditorGroup }) => {
324
327
  });
325
328
 
326
329
  export const EditorGroupBody = observer(({ group }: { group: EditorGroup }) => {
330
+ const [context, setContext] = React.useState<IEditorContext>(defaultEditorContext);
331
+
327
332
  const editorBodyRef = React.useRef<HTMLDivElement>(null);
328
333
  const editorService = useInjectable(WorkbenchEditorService) as WorkbenchEditorServiceImpl;
329
334
  const eventBus = useInjectable(IEventBus) as IEventBus;
@@ -336,6 +341,14 @@ export const EditorGroupBody = observer(({ group }: { group: EditorGroup }) => {
336
341
  const forceUpdate = React.useCallback(() => updateState({}), []);
337
342
 
338
343
  React.useEffect(() => {
344
+ const disposables = new DisposableStore();
345
+
346
+ disposables.add(
347
+ group.onDidEditorGroupBodyChanged(() => {
348
+ forceUpdate();
349
+ }),
350
+ );
351
+
339
352
  if (codeEditorRef.current) {
340
353
  if (cachedEditor[group.name]) {
341
354
  cachedEditor[group.name].remove();
@@ -345,6 +358,20 @@ export const EditorGroupBody = observer(({ group }: { group: EditorGroup }) => {
345
358
  codeEditorRef.current.appendChild(container);
346
359
  cachedEditor[group.name] = container;
347
360
  group.createEditor(container);
361
+ const minimapWith = group.codeEditor.monacoEditor.getOption(monaco.editor.EditorOption.layoutInfo).minimap
362
+ .minimapWidth;
363
+ setContext({ minimapWidth: minimapWith });
364
+
365
+ disposables.add(
366
+ group.codeEditor.monacoEditor.onDidChangeConfiguration((e) => {
367
+ if (e.hasChanged(monaco.editor.EditorOption.layoutInfo)) {
368
+ setContext({
369
+ minimapWidth: group.codeEditor.monacoEditor.getOption(monaco.editor.EditorOption.layoutInfo).minimap
370
+ .minimapWidth,
371
+ });
372
+ }
373
+ }),
374
+ );
348
375
  }
349
376
  }
350
377
 
@@ -354,15 +381,11 @@ export const EditorGroupBody = observer(({ group }: { group: EditorGroup }) => {
354
381
  if (mergeEditorRef.current) {
355
382
  group.attachMergeEditorDom(mergeEditorRef.current);
356
383
  }
357
- }, [codeEditorRef.current, diffEditorRef.current, mergeEditorRef.current]);
358
384
 
359
- useDisposable(
360
- () =>
361
- group.onDidEditorGroupBodyChanged(() => {
362
- forceUpdate();
363
- }),
364
- [],
365
- );
385
+ return () => {
386
+ disposables.dispose();
387
+ };
388
+ }, []);
366
389
 
367
390
  group.activeComponents.forEach((resources, component) => {
368
391
  const initialProps = group.activateComponentsProps.get(component);
@@ -410,79 +433,81 @@ export const EditorGroupBody = observer(({ group }: { group: EditorGroup }) => {
410
433
  });
411
434
 
412
435
  return (
413
- <div
414
- id={VIEW_CONTAINERS.EDITOR}
415
- ref={editorBodyRef}
416
- className={styles.kt_editor_body}
417
- onDragOver={(e) => {
418
- e.preventDefault();
419
- if (editorBodyRef.current) {
420
- const position = getDragOverPosition(e.nativeEvent, editorBodyRef.current);
421
- decorateDragOverElement(editorBodyRef.current, position);
422
- }
423
- }}
424
- onDragLeave={(e) => {
425
- if (editorBodyRef.current) {
426
- removeDecorationDragOverElement(editorBodyRef.current);
427
- }
428
- }}
429
- onDrop={(e) => {
430
- if (editorBodyRef.current) {
431
- removeDecorationDragOverElement(editorBodyRef.current);
432
- if (e.dataTransfer.getData('uri')) {
433
- const uri = new URI(e.dataTransfer.getData('uri'));
434
- let sourceGroup: EditorGroup | undefined;
435
- if (e.dataTransfer.getData('uri-source-group')) {
436
- sourceGroup = editorService.getEditorGroup(e.dataTransfer.getData('uri-source-group'));
437
- }
438
- group.dropUri(uri, getDragOverPosition(e.nativeEvent, editorBodyRef.current), sourceGroup);
436
+ <EditorContext.Provider value={context}>
437
+ <div
438
+ id={VIEW_CONTAINERS.EDITOR}
439
+ ref={editorBodyRef}
440
+ className={styles.kt_editor_body}
441
+ onDragOver={(e) => {
442
+ e.preventDefault();
443
+ if (editorBodyRef.current) {
444
+ const position = getDragOverPosition(e.nativeEvent, editorBodyRef.current);
445
+ decorateDragOverElement(editorBodyRef.current, position);
439
446
  }
440
- if (e.dataTransfer.files.length > 0) {
441
- eventBus.fire(
442
- new EditorGroupFileDropEvent({
443
- group,
444
- files: e.dataTransfer.files,
445
- position: getDragOverPosition(e.nativeEvent, editorBodyRef.current),
446
- }),
447
- );
447
+ }}
448
+ onDragLeave={(e) => {
449
+ if (editorBodyRef.current) {
450
+ removeDecorationDragOverElement(editorBodyRef.current);
448
451
  }
449
- }
450
- }}
451
- >
452
- {group.currentResource && <EditorSideView side={'top'} resource={group.currentResource}></EditorSideView>}
453
- {!editorHasNoTab && <NavigationBar editorGroup={group} />}
454
- <div className={styles.kt_editor_components}>
455
- <div
456
- className={cls({
457
- [styles_kt_editor_component]: true,
458
- [styles.kt_hidden]: !group.currentOpenType || group.currentOpenType.type !== EditorOpenType.component,
459
- })}
460
- >
461
- {components}
452
+ }}
453
+ onDrop={(e) => {
454
+ if (editorBodyRef.current) {
455
+ removeDecorationDragOverElement(editorBodyRef.current);
456
+ if (e.dataTransfer.getData('uri')) {
457
+ const uri = new URI(e.dataTransfer.getData('uri'));
458
+ let sourceGroup: EditorGroup | undefined;
459
+ if (e.dataTransfer.getData('uri-source-group')) {
460
+ sourceGroup = editorService.getEditorGroup(e.dataTransfer.getData('uri-source-group'));
461
+ }
462
+ group.dropUri(uri, getDragOverPosition(e.nativeEvent, editorBodyRef.current), sourceGroup);
463
+ }
464
+ if (e.dataTransfer.files.length > 0) {
465
+ eventBus.fire(
466
+ new EditorGroupFileDropEvent({
467
+ group,
468
+ files: e.dataTransfer.files,
469
+ position: getDragOverPosition(e.nativeEvent, editorBodyRef.current),
470
+ }),
471
+ );
472
+ }
473
+ }
474
+ }}
475
+ >
476
+ {group.currentResource && <EditorSideView side={'top'} resource={group.currentResource}></EditorSideView>}
477
+ {!editorHasNoTab && <NavigationBar editorGroup={group} />}
478
+ <div className={styles.kt_editor_components}>
479
+ <div
480
+ className={cls({
481
+ [styles_kt_editor_component]: true,
482
+ [styles.kt_hidden]: !group.currentOpenType || group.currentOpenType.type !== EditorOpenType.component,
483
+ })}
484
+ >
485
+ {components}
486
+ </div>
487
+ <div
488
+ className={cls({
489
+ [styles.kt_editor_code_editor]: true,
490
+ [styles_kt_editor_component]: true,
491
+ [styles.kt_hidden]: !group.currentOpenType || group.currentOpenType.type !== EditorOpenType.code,
492
+ })}
493
+ ref={codeEditorRef}
494
+ />
495
+ <div
496
+ className={cls(styles.kt_editor_diff_editor, styles_kt_editor_component, {
497
+ [styles.kt_hidden]: !group.currentOpenType || group.currentOpenType.type !== EditorOpenType.diff,
498
+ })}
499
+ ref={diffEditorRef}
500
+ />
501
+ <div
502
+ className={cls(styles.kt_editor_diff_3_editor, styles_kt_editor_component, {
503
+ [styles.kt_hidden]: !group.currentOpenType || group.currentOpenType.type !== EditorOpenType.mergeEditor,
504
+ })}
505
+ ref={mergeEditorRef}
506
+ />
462
507
  </div>
463
- <div
464
- className={cls({
465
- [styles.kt_editor_code_editor]: true,
466
- [styles_kt_editor_component]: true,
467
- [styles.kt_hidden]: !group.currentOpenType || group.currentOpenType.type !== EditorOpenType.code,
468
- })}
469
- ref={codeEditorRef}
470
- />
471
- <div
472
- className={cls(styles.kt_editor_diff_editor, styles_kt_editor_component, {
473
- [styles.kt_hidden]: !group.currentOpenType || group.currentOpenType.type !== EditorOpenType.diff,
474
- })}
475
- ref={diffEditorRef}
476
- />
477
- <div
478
- className={cls(styles.kt_editor_diff_3_editor, styles_kt_editor_component, {
479
- [styles.kt_hidden]: !group.currentOpenType || group.currentOpenType.type !== EditorOpenType.mergeEditor,
480
- })}
481
- ref={mergeEditorRef}
482
- />
508
+ {group.currentResource && <EditorSideView side={'bottom'} resource={group.currentResource}></EditorSideView>}
483
509
  </div>
484
- {group.currentResource && <EditorSideView side={'bottom'} resource={group.currentResource}></EditorSideView>}
485
- </div>
510
+ </EditorContext.Provider>
486
511
  );
487
512
  });
488
513
 
@@ -0,0 +1,34 @@
1
+ import { useEffect, useState } from 'react';
2
+
3
+ import { URI, useInjectable } from '@opensumi/ide-core-browser';
4
+
5
+ import { IEditorDocumentModelService } from '../doc-model/types';
6
+ import { IEditorDocumentModelRef } from '../types';
7
+
8
+ export function useEditorDocumentModelRef(uri: URI) {
9
+ const documentService: IEditorDocumentModelService = useInjectable(IEditorDocumentModelService);
10
+ const [ref, setRef] = useState<IEditorDocumentModelRef | null>(null);
11
+
12
+ useEffect(() => {
13
+ const run = () => {
14
+ const ref = documentService.getModelReference(uri);
15
+ if (ref) {
16
+ setRef(ref);
17
+ }
18
+ };
19
+
20
+ const toDispose = documentService.onDocumentModelCreated(uri.toString(), () => {
21
+ run();
22
+ });
23
+
24
+ run();
25
+ return () => {
26
+ toDispose.dispose();
27
+ if (ref) {
28
+ ref.dispose();
29
+ }
30
+ };
31
+ }, [uri]);
32
+
33
+ return ref;
34
+ }
@@ -0,0 +1,30 @@
1
+ import { useEffect, useState } from 'react';
2
+
3
+ import { IContextKeyService, useInjectable } from '@opensumi/ide-core-browser';
4
+
5
+ const contextKey = 'git.mergeChangesObj';
6
+
7
+ export const gitMergeChangesSet = new Set([contextKey]);
8
+
9
+ export function useInMergeChanges(uriStr: string) {
10
+ const contextKeyService = useInjectable<IContextKeyService>(IContextKeyService);
11
+
12
+ const [inMergeChanges, setInMergeChanges] = useState(false);
13
+
14
+ useEffect(() => {
15
+ function run() {
16
+ const mergeChanges = contextKeyService.getValue<Record<string, boolean>>(contextKey) || {};
17
+ setInMergeChanges(mergeChanges[uriStr] || false);
18
+ }
19
+ run();
20
+
21
+ const disposed = contextKeyService.onDidChangeContext(({ payload }) => {
22
+ if (payload.affectsSome(gitMergeChangesSet)) {
23
+ run();
24
+ }
25
+ });
26
+ return () => disposed.dispose();
27
+ }, [uriStr]);
28
+
29
+ return inMergeChanges;
30
+ }
@@ -0,0 +1,323 @@
1
+ /* ---------------------------------------------------------------------------------------------
2
+ * Copyright (c) Microsoft Corporation. All rights reserved.
3
+ * Licensed under the MIT License. See License.txt in the project root for license information.
4
+ *--------------------------------------------------------------------------------------------*/
5
+ // Some code copied and modified from https://github.com/microsoft/vscode/blob/main/extensions/merge-conflict/src/mergeConflictParser.ts
6
+
7
+ import { Injectable } from '@opensumi/di';
8
+ import { Disposable, LRUCache, uuid } from '@opensumi/ide-core-common';
9
+ import * as monaco from '@opensumi/ide-monaco';
10
+
11
+ import { ICacheDocumentMergeConflict, IDocumentMergeConflictDescriptor, IMergeRegion } from './types';
12
+
13
+ const startHeaderMarker = '<<<<<<<';
14
+ const commonAncestorsMarker = '|||||||';
15
+ const splitterMarker = '=======';
16
+ const endFooterMarker = '>>>>>>>';
17
+
18
+ interface IScanMergedConflict {
19
+ startHeader: TextLine;
20
+ commonAncestors: TextLine[];
21
+ splitter?: TextLine;
22
+ endFooter?: TextLine;
23
+ }
24
+
25
+ export interface IConflictCache {
26
+ id: string;
27
+ range: monaco.Range;
28
+ text: string;
29
+ isResolved: boolean;
30
+ }
31
+
32
+ export class TextLine {
33
+ lineNumber: number;
34
+ text: string;
35
+ range: monaco.Range;
36
+ rangeIncludingLineBreak: monaco.Range;
37
+ firstNonWhitespaceCharacterIndex: number;
38
+ isEmptyOrWhitespace: boolean;
39
+ constructor(document: monaco.editor.ITextModel, line: number) {
40
+ if (typeof line !== 'number' || line <= 0 || line > document.getLineCount()) {
41
+ throw new Error('Illegal value for `line`');
42
+ }
43
+ this.text = document.getLineContent(line);
44
+ this.firstNonWhitespaceCharacterIndex = /^(\s*)/.exec(this.text)![1].length;
45
+ this.range = new monaco.Range(line, 1, line, this.text.length + 1);
46
+ this.rangeIncludingLineBreak =
47
+ line <= document.getLineCount() ? new monaco.Range(line, 1, line + 1, 1) : this.range;
48
+ this.lineNumber = line;
49
+ this.isEmptyOrWhitespace = this.firstNonWhitespaceCharacterIndex === this.text.length;
50
+ }
51
+ }
52
+
53
+ @Injectable()
54
+ export class MergeConflictParser extends Disposable {
55
+ cache = new LRUCache<string, DocumentMergeConflict[]>(100);
56
+
57
+ private _conflictTextCaches = new Map<string, string>();
58
+
59
+ private _conflictRangeCaches = new Map<string, IConflictCache[]>();
60
+
61
+ private static createCacheKey(document: monaco.editor.ITextModel) {
62
+ return `${document.uri.toString()}-${document.getAlternativeVersionId()}`;
63
+ }
64
+
65
+ scanDocument(document: monaco.editor.ITextModel) {
66
+ const cacheKey = MergeConflictParser.createCacheKey(document);
67
+ if (this.cache.has(cacheKey)) {
68
+ return this.cache.get(cacheKey)!;
69
+ }
70
+
71
+ // Scan each line in the document, we already know there is at least a <<<<<<< and
72
+ // >>>>>> marker within the document, we need to group these into conflict ranges.
73
+ // We initially build a scan match, that references the lines of the header, splitter
74
+ // and footer. This is then converted into a full descriptor containing all required
75
+ // ranges.
76
+
77
+ let currentConflict: IScanMergedConflict | null = null;
78
+ const conflictDescriptors: IDocumentMergeConflictDescriptor[] = [];
79
+ const cacheConflictDescriptors = this._conflictTextCaches.get(document.uri.toString());
80
+
81
+ for (let i = 0; i < document.getLineCount(); i++) {
82
+ const line = new TextLine(document, i + 1);
83
+ // Ignore empty lines
84
+ if (!line || line.isEmptyOrWhitespace) {
85
+ continue;
86
+ }
87
+
88
+ // Is this a start line? <<<<<<<
89
+ if (line.text.startsWith(startHeaderMarker)) {
90
+ if (currentConflict !== null) {
91
+ // Error, we should not see a startMarker before we've seen an endMarker
92
+ currentConflict = null;
93
+
94
+ // Give up parsing, anything matched up this to this point will be decorated
95
+ // anything after will not
96
+ break;
97
+ }
98
+
99
+ // Create a new conflict starting at this line
100
+ currentConflict = { startHeader: line, commonAncestors: [] };
101
+ }
102
+ // Are we within a conflict block and is this a common ancestors marker? |||||||
103
+ else if (currentConflict && !currentConflict.splitter && line.text.startsWith(commonAncestorsMarker)) {
104
+ currentConflict.commonAncestors.push(line);
105
+ }
106
+ // Are we within a conflict block and is this a splitter? =======
107
+ else if (currentConflict && !currentConflict.splitter && line.text === splitterMarker) {
108
+ currentConflict.splitter = line;
109
+ }
110
+ // Are we within a conflict block and is this a footer? >>>>>>>
111
+ else if (currentConflict && line.text.startsWith(endFooterMarker)) {
112
+ currentConflict.endFooter = line;
113
+
114
+ // Create a full descriptor from the lines that we matched. This can return
115
+ // null if the descriptor could not be completed.
116
+ const completeDescriptor = scanItemTolMergeConflictDescriptor(document, currentConflict);
117
+
118
+ if (completeDescriptor !== null) {
119
+ conflictDescriptors.push(completeDescriptor);
120
+ }
121
+
122
+ // Reset the current conflict to be empty, so we can match the next
123
+ // starting header marker.
124
+ currentConflict = null;
125
+ }
126
+ }
127
+ if (!cacheConflictDescriptors && conflictDescriptors.length) {
128
+ this._conflictTextCaches.set(document.uri.toString(), document.getValue());
129
+ const conflictRanges: IConflictCache[] = [];
130
+ conflictDescriptors.filter(Boolean).forEach((descriptor) => {
131
+ const range = descriptor.range;
132
+ conflictRanges.push({
133
+ id: uuid(),
134
+ range,
135
+ text: document.getValueInRange(range),
136
+ isResolved: false,
137
+ });
138
+ });
139
+ this._conflictRangeCaches.set(document.uri.toString(), conflictRanges);
140
+ }
141
+
142
+ const result = conflictDescriptors?.filter(Boolean).map((descriptor) => new DocumentMergeConflict(descriptor));
143
+ this.cache.set(cacheKey, result);
144
+
145
+ return result;
146
+ }
147
+ getConflictText(uri: string) {
148
+ return this._conflictTextCaches.get(uri);
149
+ }
150
+ getAllConflictsByUri(uri: string) {
151
+ return this._conflictRangeCaches.get(uri);
152
+ }
153
+
154
+ getAllConflicts() {
155
+ return this._conflictRangeCaches;
156
+ }
157
+
158
+ setConflictResolved(uri: string, id: string) {
159
+ const conflictRanges = this._conflictRangeCaches.get(uri);
160
+ if (conflictRanges) {
161
+ const conflictRange = conflictRanges.find((item) => item.id === id);
162
+ if (conflictRange) {
163
+ conflictRange.isResolved = true;
164
+ }
165
+ }
166
+ }
167
+
168
+ deleteConflictText(uri: string) {
169
+ this._conflictTextCaches.delete(uri);
170
+ }
171
+ dispose() {
172
+ this._conflictTextCaches.clear();
173
+ this._conflictRangeCaches.clear();
174
+ }
175
+ }
176
+
177
+ function scanItemTolMergeConflictDescriptor(
178
+ document: monaco.editor.ITextModel,
179
+ scanned: IScanMergedConflict,
180
+ ): IDocumentMergeConflictDescriptor | null {
181
+ // Validate we have all the required lines within the scan item.
182
+ if (!scanned.startHeader || !scanned.splitter || !scanned.endFooter) {
183
+ return null;
184
+ }
185
+
186
+ const tokenAfterCurrentBlock: TextLine = scanned.commonAncestors[0] || scanned.splitter;
187
+
188
+ // Assume that descriptor.current.header, descriptor.incoming.header and descriptor.splitter
189
+ // have valid ranges, fill in content and total ranges from these parts.
190
+ // NOTE: We need to shift the decorator range back one character so the splitter does not end up with
191
+ // two decoration colors (current and splitter), if we take the new line from the content into account
192
+ // the decorator will wrap to the next line.
193
+ return {
194
+ current: {
195
+ header: scanned.startHeader.range,
196
+ decoratorContent: new monaco.Range(
197
+ scanned.startHeader.rangeIncludingLineBreak.endLineNumber,
198
+ scanned.startHeader.rangeIncludingLineBreak.endColumn,
199
+ shiftBackOneCharacter(
200
+ document,
201
+ tokenAfterCurrentBlock.range.getStartPosition(),
202
+ scanned.startHeader.rangeIncludingLineBreak.getEndPosition(),
203
+ ).lineNumber,
204
+ shiftBackOneCharacter(
205
+ document,
206
+ tokenAfterCurrentBlock.range.getStartPosition(),
207
+ scanned.startHeader.rangeIncludingLineBreak.getEndPosition(),
208
+ ).column,
209
+ ),
210
+ // Current content is range between header (shifted for linebreak) and splitter or common ancestors mark start
211
+ content: new monaco.Range(
212
+ scanned.startHeader.rangeIncludingLineBreak.endLineNumber,
213
+ scanned.startHeader.rangeIncludingLineBreak.endColumn,
214
+ tokenAfterCurrentBlock.range.startLineNumber,
215
+ tokenAfterCurrentBlock.range.startColumn,
216
+ ),
217
+ name: scanned.startHeader.text.substring(startHeaderMarker.length + 1),
218
+ },
219
+ commonAncestors: scanned.commonAncestors.map((currentTokenLine, index, commonAncestors) => {
220
+ const nextTokenLine = commonAncestors[index + 1] || scanned.splitter;
221
+ return {
222
+ header: currentTokenLine.range,
223
+ decoratorContent: new monaco.Range(
224
+ currentTokenLine.rangeIncludingLineBreak.endLineNumber,
225
+ currentTokenLine.rangeIncludingLineBreak.endColumn,
226
+
227
+ shiftBackOneCharacter(
228
+ document,
229
+ nextTokenLine.range.getStartPosition(),
230
+ currentTokenLine.rangeIncludingLineBreak.getEndPosition(),
231
+ ).lineNumber,
232
+ shiftBackOneCharacter(
233
+ document,
234
+ nextTokenLine.range.getStartPosition(),
235
+ currentTokenLine.rangeIncludingLineBreak.getEndPosition(),
236
+ ).lineNumber,
237
+ ),
238
+ // Each common ancestors block is range between one common ancestors token
239
+ // (shifted for linebreak) and start of next common ancestors token or splitter
240
+ content: new monaco.Range(
241
+ currentTokenLine.rangeIncludingLineBreak.endLineNumber,
242
+ currentTokenLine.rangeIncludingLineBreak.endColumn,
243
+ nextTokenLine.range.startLineNumber,
244
+ nextTokenLine.range.startColumn,
245
+ ),
246
+ name: currentTokenLine.text.substring(commonAncestorsMarker.length + 1),
247
+ };
248
+ }),
249
+ splitter: scanned.splitter.range,
250
+ incoming: {
251
+ header: scanned.endFooter.range,
252
+ decoratorContent: new monaco.Range(
253
+ scanned.splitter.rangeIncludingLineBreak.endLineNumber,
254
+ scanned.splitter.rangeIncludingLineBreak.endColumn,
255
+ shiftBackOneCharacter(
256
+ document,
257
+ scanned.endFooter.range.getStartPosition(),
258
+ scanned.splitter.rangeIncludingLineBreak.getEndPosition(),
259
+ ).lineNumber,
260
+ shiftBackOneCharacter(
261
+ document,
262
+ scanned.endFooter.range.getStartPosition(),
263
+ scanned.splitter.rangeIncludingLineBreak.getEndPosition(),
264
+ ).column,
265
+ ),
266
+ // Incoming content is range between splitter (shifted for linebreak) and footer start
267
+ content: new monaco.Range(
268
+ scanned.splitter.rangeIncludingLineBreak.endLineNumber,
269
+ scanned.splitter.rangeIncludingLineBreak.endColumn,
270
+ scanned.endFooter.range.startLineNumber,
271
+ scanned.endFooter.range.startColumn,
272
+ ),
273
+ name: scanned.endFooter.text.substring(endFooterMarker.length + 1),
274
+ },
275
+ // Entire range is between current header start and incoming header end (including line break)
276
+ range: new monaco.Range(
277
+ scanned.startHeader.range.startLineNumber,
278
+ scanned.startHeader.range.startColumn,
279
+ scanned.endFooter.range.endLineNumber,
280
+ scanned.endFooter.range.endColumn,
281
+ ),
282
+ };
283
+ }
284
+
285
+ function shiftBackOneCharacter(
286
+ document: monaco.editor.ITextModel,
287
+ range: monaco.Position,
288
+ unlessEqual: monaco.Position,
289
+ ): monaco.Position {
290
+ if (range.equals(unlessEqual)) {
291
+ return range;
292
+ }
293
+
294
+ let line = range.lineNumber;
295
+ let character = range.column - 1;
296
+
297
+ if (character < 0) {
298
+ line--;
299
+ character = new TextLine(document, line).range.endColumn;
300
+ }
301
+ return new monaco.Position(line, character);
302
+ }
303
+
304
+ export class DocumentMergeConflict implements ICacheDocumentMergeConflict {
305
+ public range: monaco.Range;
306
+ public current: IMergeRegion;
307
+ public incoming: IMergeRegion;
308
+ public commonAncestors: IMergeRegion[];
309
+ public splitter: monaco.Range;
310
+ public incomingContent: string;
311
+ public currentContent: string;
312
+ public bothContent: string;
313
+ public aiContent?: string;
314
+ public defaultContent: string;
315
+ private applied = false;
316
+ constructor(descriptor: IDocumentMergeConflictDescriptor) {
317
+ this.range = descriptor.range;
318
+ this.current = descriptor.current;
319
+ this.incoming = descriptor.incoming;
320
+ this.commonAncestors = descriptor.commonAncestors;
321
+ this.splitter = descriptor.splitter;
322
+ }
323
+ }
@@ -0,0 +1,2 @@
1
+ export * from './conflict-parser';
2
+ export * from './types';