@theia/scm 1.64.0-next.17 → 1.64.0-next.35

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 (125) hide show
  1. package/lib/browser/decorations/scm-decorations-service.d.ts.map +1 -1
  2. package/lib/browser/decorations/scm-decorations-service.js +19 -1
  3. package/lib/browser/decorations/scm-decorations-service.js.map +1 -1
  4. package/lib/browser/merge-editor/merge-editor-contribution.d.ts +34 -0
  5. package/lib/browser/merge-editor/merge-editor-contribution.d.ts.map +1 -0
  6. package/lib/browser/merge-editor/merge-editor-contribution.js +335 -0
  7. package/lib/browser/merge-editor/merge-editor-contribution.js.map +1 -0
  8. package/lib/browser/merge-editor/merge-editor-dev-contribution.d.ts +31 -0
  9. package/lib/browser/merge-editor/merge-editor-dev-contribution.d.ts.map +1 -0
  10. package/lib/browser/merge-editor/merge-editor-dev-contribution.js +151 -0
  11. package/lib/browser/merge-editor/merge-editor-dev-contribution.js.map +1 -0
  12. package/lib/browser/merge-editor/merge-editor-module.d.ts +24 -0
  13. package/lib/browser/merge-editor/merge-editor-module.d.ts.map +1 -0
  14. package/lib/browser/merge-editor/merge-editor-module.js +109 -0
  15. package/lib/browser/merge-editor/merge-editor-module.js.map +1 -0
  16. package/lib/browser/merge-editor/merge-editor.d.ts +122 -0
  17. package/lib/browser/merge-editor/merge-editor.d.ts.map +1 -0
  18. package/lib/browser/merge-editor/merge-editor.js +560 -0
  19. package/lib/browser/merge-editor/merge-editor.js.map +1 -0
  20. package/lib/browser/merge-editor/model/line-range.d.ts +37 -0
  21. package/lib/browser/merge-editor/model/line-range.d.ts.map +1 -0
  22. package/lib/browser/merge-editor/model/line-range.js +111 -0
  23. package/lib/browser/merge-editor/model/line-range.js.map +1 -0
  24. package/lib/browser/merge-editor/model/live-diff.d.ts +26 -0
  25. package/lib/browser/merge-editor/model/live-diff.d.ts.map +1 -0
  26. package/lib/browser/merge-editor/model/live-diff.js +85 -0
  27. package/lib/browser/merge-editor/model/live-diff.js.map +1 -0
  28. package/lib/browser/merge-editor/model/merge-editor-model.d.ts +116 -0
  29. package/lib/browser/merge-editor/model/merge-editor-model.d.ts.map +1 -0
  30. package/lib/browser/merge-editor/model/merge-editor-model.js +507 -0
  31. package/lib/browser/merge-editor/model/merge-editor-model.js.map +1 -0
  32. package/lib/browser/merge-editor/model/merge-range.d.ts +50 -0
  33. package/lib/browser/merge-editor/model/merge-range.d.ts.map +1 -0
  34. package/lib/browser/merge-editor/model/merge-range.js +215 -0
  35. package/lib/browser/merge-editor/model/merge-range.js.map +1 -0
  36. package/lib/browser/merge-editor/model/range-editing.d.ts +21 -0
  37. package/lib/browser/merge-editor/model/range-editing.d.ts.map +1 -0
  38. package/lib/browser/merge-editor/model/range-editing.js +68 -0
  39. package/lib/browser/merge-editor/model/range-editing.js.map +1 -0
  40. package/lib/browser/merge-editor/model/range-mapping.d.ts +106 -0
  41. package/lib/browser/merge-editor/model/range-mapping.d.ts.map +1 -0
  42. package/lib/browser/merge-editor/model/range-mapping.js +252 -0
  43. package/lib/browser/merge-editor/model/range-mapping.js.map +1 -0
  44. package/lib/browser/merge-editor/model/range-mapping.spec.d.ts +2 -0
  45. package/lib/browser/merge-editor/model/range-mapping.spec.d.ts.map +1 -0
  46. package/lib/browser/merge-editor/model/range-mapping.spec.js +48 -0
  47. package/lib/browser/merge-editor/model/range-mapping.spec.js.map +1 -0
  48. package/lib/browser/merge-editor/model/range-utils.d.ts +25 -0
  49. package/lib/browser/merge-editor/model/range-utils.d.ts.map +1 -0
  50. package/lib/browser/merge-editor/model/range-utils.js +118 -0
  51. package/lib/browser/merge-editor/model/range-utils.js.map +1 -0
  52. package/lib/browser/merge-editor/view/diff-spacers.d.ts +50 -0
  53. package/lib/browser/merge-editor/view/diff-spacers.d.ts.map +1 -0
  54. package/lib/browser/merge-editor/view/diff-spacers.js +133 -0
  55. package/lib/browser/merge-editor/view/diff-spacers.js.map +1 -0
  56. package/lib/browser/merge-editor/view/merge-editor-panes/index.d.ts +6 -0
  57. package/lib/browser/merge-editor/view/merge-editor-panes/index.d.ts.map +1 -0
  58. package/lib/browser/merge-editor/view/merge-editor-panes/index.js +24 -0
  59. package/lib/browser/merge-editor/view/merge-editor-panes/index.js.map +1 -0
  60. package/lib/browser/merge-editor/view/merge-editor-panes/merge-editor-base-pane.d.ts +12 -0
  61. package/lib/browser/merge-editor/view/merge-editor-panes/merge-editor-base-pane.d.ts.map +1 -0
  62. package/lib/browser/merge-editor/view/merge-editor-panes/merge-editor-base-pane.js +65 -0
  63. package/lib/browser/merge-editor/view/merge-editor-panes/merge-editor-base-pane.js.map +1 -0
  64. package/lib/browser/merge-editor/view/merge-editor-panes/merge-editor-pane-header.d.ts +30 -0
  65. package/lib/browser/merge-editor/view/merge-editor-panes/merge-editor-pane-header.d.ts.map +1 -0
  66. package/lib/browser/merge-editor/view/merge-editor-panes/merge-editor-pane-header.js +102 -0
  67. package/lib/browser/merge-editor/view/merge-editor-panes/merge-editor-pane-header.js.map +1 -0
  68. package/lib/browser/merge-editor/view/merge-editor-panes/merge-editor-pane.d.ts +49 -0
  69. package/lib/browser/merge-editor/view/merge-editor-panes/merge-editor-pane.d.ts.map +1 -0
  70. package/lib/browser/merge-editor/view/merge-editor-panes/merge-editor-pane.js +214 -0
  71. package/lib/browser/merge-editor/view/merge-editor-panes/merge-editor-pane.js.map +1 -0
  72. package/lib/browser/merge-editor/view/merge-editor-panes/merge-editor-result-pane.d.ts +16 -0
  73. package/lib/browser/merge-editor/view/merge-editor-panes/merge-editor-result-pane.d.ts.map +1 -0
  74. package/lib/browser/merge-editor/view/merge-editor-panes/merge-editor-result-pane.js +107 -0
  75. package/lib/browser/merge-editor/view/merge-editor-panes/merge-editor-result-pane.js.map +1 -0
  76. package/lib/browser/merge-editor/view/merge-editor-panes/merge-editor-side-pane.d.ts +27 -0
  77. package/lib/browser/merge-editor/view/merge-editor-panes/merge-editor-side-pane.d.ts.map +1 -0
  78. package/lib/browser/merge-editor/view/merge-editor-panes/merge-editor-side-pane.js +135 -0
  79. package/lib/browser/merge-editor/view/merge-editor-panes/merge-editor-side-pane.js.map +1 -0
  80. package/lib/browser/merge-editor/view/merge-editor-scroll-sync.d.ts +19 -0
  81. package/lib/browser/merge-editor/view/merge-editor-scroll-sync.d.ts.map +1 -0
  82. package/lib/browser/merge-editor/view/merge-editor-scroll-sync.js +191 -0
  83. package/lib/browser/merge-editor/view/merge-editor-scroll-sync.js.map +1 -0
  84. package/lib/browser/merge-editor/view/merge-editor-view-zones.d.ts +57 -0
  85. package/lib/browser/merge-editor/view/merge-editor-view-zones.d.ts.map +1 -0
  86. package/lib/browser/merge-editor/view/merge-editor-view-zones.js +218 -0
  87. package/lib/browser/merge-editor/view/merge-editor-view-zones.js.map +1 -0
  88. package/lib/browser/merge-editor/view/merge-range-actions.d.ts +23 -0
  89. package/lib/browser/merge-editor/view/merge-range-actions.d.ts.map +1 -0
  90. package/lib/browser/merge-editor/view/merge-range-actions.js +142 -0
  91. package/lib/browser/merge-editor/view/merge-range-actions.js.map +1 -0
  92. package/lib/browser/scm-colors.d.ts +2 -0
  93. package/lib/browser/scm-colors.d.ts.map +1 -1
  94. package/lib/browser/scm-colors.js +2 -0
  95. package/lib/browser/scm-colors.js.map +1 -1
  96. package/lib/browser/scm-frontend-module.d.ts.map +1 -1
  97. package/lib/browser/scm-frontend-module.js +2 -0
  98. package/lib/browser/scm-frontend-module.js.map +1 -1
  99. package/package.json +6 -6
  100. package/src/browser/decorations/scm-decorations-service.ts +18 -1
  101. package/src/browser/merge-editor/merge-editor-contribution.ts +346 -0
  102. package/src/browser/merge-editor/merge-editor-dev-contribution.ts +154 -0
  103. package/src/browser/merge-editor/merge-editor-module.ts +134 -0
  104. package/src/browser/merge-editor/merge-editor.ts +643 -0
  105. package/src/browser/merge-editor/model/line-range.ts +128 -0
  106. package/src/browser/merge-editor/model/live-diff.ts +111 -0
  107. package/src/browser/merge-editor/model/merge-editor-model.ts +623 -0
  108. package/src/browser/merge-editor/model/merge-range.ts +268 -0
  109. package/src/browser/merge-editor/model/range-editing.ts +81 -0
  110. package/src/browser/merge-editor/model/range-mapping.spec.ts +52 -0
  111. package/src/browser/merge-editor/model/range-mapping.ts +396 -0
  112. package/src/browser/merge-editor/model/range-utils.ts +115 -0
  113. package/src/browser/merge-editor/view/diff-spacers.ts +160 -0
  114. package/src/browser/merge-editor/view/merge-editor-panes/index.ts +21 -0
  115. package/src/browser/merge-editor/view/merge-editor-panes/merge-editor-base-pane.ts +71 -0
  116. package/src/browser/merge-editor/view/merge-editor-panes/merge-editor-pane-header.tsx +106 -0
  117. package/src/browser/merge-editor/view/merge-editor-panes/merge-editor-pane.ts +246 -0
  118. package/src/browser/merge-editor/view/merge-editor-panes/merge-editor-result-pane.ts +115 -0
  119. package/src/browser/merge-editor/view/merge-editor-panes/merge-editor-side-pane.ts +139 -0
  120. package/src/browser/merge-editor/view/merge-editor-scroll-sync.ts +215 -0
  121. package/src/browser/merge-editor/view/merge-editor-view-zones.ts +264 -0
  122. package/src/browser/merge-editor/view/merge-range-actions.ts +159 -0
  123. package/src/browser/scm-colors.ts +2 -0
  124. package/src/browser/scm-frontend-module.ts +4 -0
  125. package/src/browser/style/merge-editor.css +221 -0
@@ -0,0 +1,623 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2025 1C-Soft LLC and others.
3
+ //
4
+ // This program and the accompanying materials are made available under the
5
+ // terms of the Eclipse Public License v. 2.0 which is available at
6
+ // http://www.eclipse.org/legal/epl-2.0.
7
+ //
8
+ // This Source Code may also be made available under the following Secondary
9
+ // Licenses when the conditions for such availability set forth in the Eclipse
10
+ // Public License v. 2.0 are satisfied: GNU General Public License, version 2
11
+ // with the GNU Classpath Exception which is available at
12
+ // https://www.gnu.org/software/classpath/license.html.
13
+ //
14
+ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
+ // *****************************************************************************
16
+ /*---------------------------------------------------------------------------------------------
17
+ * Copyright (c) Microsoft Corporation. All rights reserved.
18
+ * Licensed under the MIT License. See License.txt in the project root for license information.
19
+ *--------------------------------------------------------------------------------------------*/
20
+ // copied and modified from https://github.com/microsoft/vscode/blob/1.96.3/src/vs/workbench/contrib/mergeEditor/browser/model/mergeEditorModel.ts,
21
+ // https://github.com/microsoft/vscode/blob/1.96.3/src/vs/workbench/contrib/mergeEditor/browser/view/viewModel.ts
22
+
23
+ import { inject, injectable, postConstruct } from '@theia/core/shared/inversify';
24
+ import { ArrayUtils, Disposable, DisposableCollection } from '@theia/core';
25
+ import { Autorun, DerivedObservable, Observable, ObservableUtils, SettableObservable } from '@theia/core/lib/common/observable';
26
+ import { DiffComputer } from '@theia/core/lib/common/diff';
27
+ import { Range } from '@theia/core/shared/vscode-languageserver-protocol';
28
+ import { MonacoEditor } from '@theia/monaco/lib/browser/monaco-editor';
29
+ import { MonacoEditorModel } from '@theia/monaco/lib/browser/monaco-editor-model';
30
+ import { MonacoToProtocolConverter } from '@theia/monaco/lib/browser/monaco-to-protocol-converter';
31
+ import { MergeRange, MergeRangeAcceptedState, MergeRangeResultState, MergeSide } from './merge-range';
32
+ import { DetailedLineRangeMapping, DocumentLineRangeMap, DocumentRangeMap, LineRangeMapping, RangeMapping } from './range-mapping';
33
+ import { LiveDiff, LiveDiffState } from './live-diff';
34
+ import { LineRange } from './line-range';
35
+ import { LineRangeEdit } from './range-editing';
36
+
37
+ export const MergeEditorModelProps = Symbol('MergeEditorModelProps');
38
+ export interface MergeEditorModelProps {
39
+ readonly baseEditor: MonacoEditor;
40
+ readonly side1Editor: MonacoEditor;
41
+ readonly side2Editor: MonacoEditor;
42
+ readonly resultEditor: MonacoEditor;
43
+ readonly options?: {
44
+ readonly resetResult?: boolean;
45
+ }
46
+ }
47
+
48
+ @injectable()
49
+ export class MergeEditorModel implements Disposable {
50
+
51
+ @inject(MergeEditorModelProps)
52
+ protected readonly props: MergeEditorModelProps;
53
+
54
+ @inject(DiffComputer)
55
+ protected readonly diffComputer: DiffComputer;
56
+
57
+ @inject(MonacoToProtocolConverter)
58
+ private readonly m2p: MonacoToProtocolConverter;
59
+
60
+ protected readonly toDispose = new DisposableCollection();
61
+
62
+ protected side1LiveDiff: LiveDiff;
63
+ protected side2LiveDiff: LiveDiff;
64
+ protected resultLiveDiff: LiveDiff;
65
+
66
+ protected shouldRecomputeHandledState = true;
67
+
68
+ protected readonly mergeRangesObservable = DerivedObservable.create(() => this.computeMergeRanges());
69
+ get mergeRanges(): readonly MergeRange[] {
70
+ return this.mergeRangesObservable.get();
71
+ }
72
+
73
+ protected readonly mergeRangesDataObservable = DerivedObservable.create(() => new Map(
74
+ this.mergeRanges.map(mergeRange => [mergeRange, this.newMergeRangeData()])
75
+ ));
76
+
77
+ // #region Line Range Mapping
78
+ protected readonly side1ToResultLineRangeMapObservable = DerivedObservable.create(() => this.newDocumentLineRangeMap(
79
+ this.computeSideToResultDiff(this.side1Changes, this.resultChanges)
80
+ ));
81
+ get side1ToResultLineRangeMap(): DocumentLineRangeMap {
82
+ return this.side1ToResultLineRangeMapObservable.get();
83
+ }
84
+
85
+ protected readonly resultToSide1LineRangeMapObservable = DerivedObservable.create(() => this.side1ToResultLineRangeMap.reverse());
86
+ get resultToSide1LineRangeMap(): DocumentLineRangeMap {
87
+ return this.resultToSide1LineRangeMapObservable.get();
88
+ }
89
+
90
+ protected readonly side2ToResultLineRangeMapObservable = DerivedObservable.create(() => this.newDocumentLineRangeMap(
91
+ this.computeSideToResultDiff(this.side2Changes, this.resultChanges)
92
+ ));
93
+ get side2ToResultLineRangeMap(): DocumentLineRangeMap {
94
+ return this.side2ToResultLineRangeMapObservable.get();
95
+ }
96
+
97
+ protected readonly resultToSide2LineRangeMapObservable = DerivedObservable.create(() => this.side2ToResultLineRangeMap.reverse());
98
+ get resultToSide2LineRangeMap(): DocumentLineRangeMap {
99
+ return this.resultToSide2LineRangeMapObservable.get();
100
+ }
101
+
102
+ protected readonly baseToSide1LineRangeMapObservable = DerivedObservable.create(() => this.newDocumentLineRangeMap(this.side1Changes));
103
+ get baseToSide1LineRangeMap(): DocumentLineRangeMap {
104
+ return this.baseToSide1LineRangeMapObservable.get();
105
+ }
106
+
107
+ protected readonly side1ToBaseLineRangeMapObservable = DerivedObservable.create(() => this.baseToSide1LineRangeMap.reverse());
108
+ get side1ToBaseLineRangeMap(): DocumentLineRangeMap {
109
+ return this.side1ToBaseLineRangeMapObservable.get();
110
+ }
111
+
112
+ protected readonly baseToSide2LineRangeMapObservable = DerivedObservable.create(() => this.newDocumentLineRangeMap(this.side2Changes));
113
+ get baseToSide2LineRangeMap(): DocumentLineRangeMap {
114
+ return this.baseToSide2LineRangeMapObservable.get();
115
+ }
116
+
117
+ protected readonly side2ToBaseLineRangeMapObservable = DerivedObservable.create(() => this.baseToSide2LineRangeMap.reverse());
118
+ get side2ToBaseLineRangeMap(): DocumentLineRangeMap {
119
+ return this.side2ToBaseLineRangeMapObservable.get();
120
+ }
121
+
122
+ protected readonly baseToResultLineRangeMapObservable = DerivedObservable.create(() => this.newDocumentLineRangeMap(this.resultChanges));
123
+ get baseToResultLineRangeMap(): DocumentLineRangeMap {
124
+ return this.baseToResultLineRangeMapObservable.get();
125
+ }
126
+
127
+ protected readonly resultToBaseLineRangeMapObservable = DerivedObservable.create(() => this.baseToResultLineRangeMap.reverse());
128
+ get resultToBaseLineRangeMap(): DocumentLineRangeMap {
129
+ return this.resultToBaseLineRangeMapObservable.get();
130
+ }
131
+ // #endregion
132
+
133
+ // #region Range Mapping
134
+ protected readonly baseToSide1RangeMapObservable = DerivedObservable.create(() => this.newDocumentRangeMap(
135
+ this.side1Changes.flatMap(change => change.rangeMappings)
136
+ ));
137
+ get baseToSide1RangeMap(): DocumentRangeMap {
138
+ return this.baseToSide1RangeMapObservable.get();
139
+ }
140
+
141
+ protected readonly side1ToBaseRangeMapObservable = DerivedObservable.create(() => this.baseToSide1RangeMap.reverse());
142
+ get side1ToBaseRangeMap(): DocumentRangeMap {
143
+ return this.side1ToBaseRangeMapObservable.get();
144
+ }
145
+
146
+ protected readonly baseToSide2RangeMapObservable = DerivedObservable.create(() => this.newDocumentRangeMap(
147
+ this.side2Changes.flatMap(change => change.rangeMappings)
148
+ ));
149
+ get baseToSide2RangeMap(): DocumentRangeMap {
150
+ return this.baseToSide2RangeMapObservable.get();
151
+ }
152
+
153
+ protected readonly side2ToBaseRangeMapObservable = DerivedObservable.create(() => this.baseToSide2RangeMap.reverse());
154
+ get side2ToBaseRangeMap(): DocumentRangeMap {
155
+ return this.side2ToBaseRangeMapObservable.get();
156
+ }
157
+
158
+ protected readonly baseToResultRangeMapObservable = DerivedObservable.create(() => this.newDocumentRangeMap(
159
+ this.resultChanges.flatMap(change => change.rangeMappings)
160
+ ));
161
+ get baseToResultRangeMap(): DocumentRangeMap {
162
+ return this.baseToResultRangeMapObservable.get();
163
+ }
164
+
165
+ protected readonly resultToBaseRangeMapObservable = DerivedObservable.create(() => this.baseToResultRangeMap.reverse());
166
+ get resultToBaseRangeMap(): DocumentRangeMap {
167
+ return this.resultToBaseRangeMapObservable.get();
168
+ }
169
+ // #endregion
170
+
171
+ protected readonly diffComputingStateObservable = DerivedObservable.create(() => this.getDiffComputingState(this.side1LiveDiff, this.side2LiveDiff, this.resultLiveDiff));
172
+ protected readonly diffComputingStateForSidesObservable = DerivedObservable.create(() => this.getDiffComputingState(this.side1LiveDiff, this.side2LiveDiff));
173
+
174
+ readonly isUpToDateObservable = DerivedObservable.create(() => this.diffComputingStateObservable.get() === DiffComputingState.UpToDate);
175
+
176
+ protected readonly unhandledMergeRangesCountObservable = DerivedObservable.create(() => {
177
+ let result = 0;
178
+ const mergeRangesData = this.mergeRangesDataObservable.get();
179
+ for (const mergeRangeData of mergeRangesData.values()) {
180
+ if (!mergeRangeData.isHandledObservable.get()) {
181
+ result++;
182
+ }
183
+ }
184
+ return result;
185
+ });
186
+ get unhandledMergeRangesCount(): number {
187
+ return this.unhandledMergeRangesCountObservable.get();
188
+ }
189
+
190
+ protected _onInitialized: Promise<void>;
191
+ get onInitialized(): Promise<void> {
192
+ return this._onInitialized;
193
+ }
194
+
195
+ get baseDocument(): MonacoEditorModel {
196
+ return this.props.baseEditor.document;
197
+ }
198
+
199
+ get side1Document(): MonacoEditorModel {
200
+ return this.props.side1Editor.document;
201
+ }
202
+
203
+ get side2Document(): MonacoEditorModel {
204
+ return this.props.side2Editor.document;
205
+ }
206
+
207
+ get resultDocument(): MonacoEditorModel {
208
+ return this.props.resultEditor.document;
209
+ }
210
+
211
+ protected get resultEditor(): MonacoEditor {
212
+ return this.props.resultEditor;
213
+ }
214
+
215
+ get side1Changes(): readonly DetailedLineRangeMapping[] {
216
+ return this.side1LiveDiff.changes;
217
+ }
218
+
219
+ get side2Changes(): readonly DetailedLineRangeMapping[] {
220
+ return this.side2LiveDiff.changes;
221
+ }
222
+
223
+ get resultChanges(): readonly DetailedLineRangeMapping[] {
224
+ return this.resultLiveDiff.changes;
225
+ }
226
+
227
+ @postConstruct()
228
+ protected init(): void {
229
+ this.toDispose.push(this.side1LiveDiff = this.newLiveDiff(this.baseDocument, this.side1Document));
230
+ this.toDispose.push(this.side2LiveDiff = this.newLiveDiff(this.baseDocument, this.side2Document));
231
+ this.toDispose.push(this.resultLiveDiff = this.newLiveDiff(this.baseDocument, this.resultDocument));
232
+
233
+ this.toDispose.push(Observable.keepObserved(this.mergeRangesDataObservable));
234
+
235
+ this.toDispose.push(Observable.keepObserved(this.side1ToResultLineRangeMapObservable));
236
+ this.toDispose.push(Observable.keepObserved(this.resultToSide1LineRangeMapObservable));
237
+
238
+ this.toDispose.push(Observable.keepObserved(this.side2ToResultLineRangeMapObservable));
239
+ this.toDispose.push(Observable.keepObserved(this.resultToSide2LineRangeMapObservable));
240
+
241
+ this.toDispose.push(Observable.keepObserved(this.baseToSide1LineRangeMapObservable));
242
+ this.toDispose.push(Observable.keepObserved(this.side1ToBaseLineRangeMapObservable));
243
+
244
+ this.toDispose.push(Observable.keepObserved(this.baseToSide2LineRangeMapObservable));
245
+ this.toDispose.push(Observable.keepObserved(this.side2ToBaseLineRangeMapObservable));
246
+
247
+ this.toDispose.push(Observable.keepObserved(this.baseToResultLineRangeMapObservable));
248
+ this.toDispose.push(Observable.keepObserved(this.resultToBaseLineRangeMapObservable));
249
+
250
+ this.toDispose.push(Observable.keepObserved(this.baseToSide1RangeMapObservable));
251
+ this.toDispose.push(Observable.keepObserved(this.side1ToBaseRangeMapObservable));
252
+
253
+ this.toDispose.push(Observable.keepObserved(this.baseToSide2RangeMapObservable));
254
+ this.toDispose.push(Observable.keepObserved(this.side2ToBaseRangeMapObservable));
255
+
256
+ this.toDispose.push(Observable.keepObserved(this.baseToResultRangeMapObservable));
257
+ this.toDispose.push(Observable.keepObserved(this.resultToBaseRangeMapObservable));
258
+
259
+ const initializePromise = this.doInit();
260
+
261
+ this._onInitialized = ObservableUtils.waitForState(this.isUpToDateObservable).then(() => initializePromise);
262
+
263
+ initializePromise.then(() => {
264
+ this.toDispose.push(Autorun.create(() => {
265
+ if (!this.isUpToDateObservable.get()) {
266
+ return;
267
+ }
268
+ Observable.update(() => {
269
+ const mergeRangesData = this.mergeRangesDataObservable.get();
270
+
271
+ for (const [mergeRange, mergeRangeData] of mergeRangesData) {
272
+ const state = this.computeMergeRangeStateFromResult(mergeRange);
273
+ mergeRangeData.resultStateObservable.set(state);
274
+ if (this.shouldRecomputeHandledState) {
275
+ mergeRangeData.isHandledObservable.set(state !== 'Base');
276
+ }
277
+ }
278
+
279
+ this.shouldRecomputeHandledState = false;
280
+ });
281
+ }, {
282
+ willHandleChange: ctx => {
283
+ if (ctx.isChangeOf(this.mergeRangesDataObservable)) {
284
+ this.shouldRecomputeHandledState = true;
285
+ }
286
+ return true;
287
+ }
288
+ }));
289
+
290
+ const attachedHistory = new AttachedHistory(this.resultDocument);
291
+ this.toDispose.push(attachedHistory);
292
+ this.toDispose.push(this.resultDocument.textEditorModel.onDidChangeContent(event => {
293
+ if (event.isRedoing || event.isUndoing) {
294
+ return;
295
+ }
296
+
297
+ // Mark merge ranges affected by content changes as handled.
298
+ const mergeRanges: MergeRange[] = [];
299
+
300
+ for (const change of event.changes) {
301
+ const { start, end } = this.translateResultRangeToBase(this.m2p.asRange(change.range));
302
+ const affectedMergeRanges = this.findMergeRanges(new LineRange(start.line, end.line - start.line));
303
+ for (const mergeRange of affectedMergeRanges) {
304
+ if (!this.isMergeRangeHandled(mergeRange)) {
305
+ mergeRanges.push(mergeRange);
306
+ }
307
+ }
308
+ }
309
+
310
+ if (mergeRanges.length === 0) {
311
+ return;
312
+ }
313
+
314
+ const markMergeRangesAsHandled = (handled: boolean) => {
315
+ Observable.update(() => {
316
+ const mergeRangesData = this.mergeRangesDataObservable.get();
317
+ for (const mergeRange of mergeRanges) {
318
+ const mergeRangeData = mergeRangesData.get(mergeRange);
319
+ if (mergeRangeData) {
320
+ mergeRangeData.isHandledObservable.set(handled);
321
+ }
322
+ }
323
+ });
324
+ };
325
+ const element: IAttachedHistoryElement = {
326
+ redo: () => {
327
+ markMergeRangesAsHandled(true);
328
+ },
329
+ undo: () => {
330
+ markMergeRangesAsHandled(false);
331
+ }
332
+ };
333
+ attachedHistory.pushAttachedHistoryElement(element);
334
+ element.redo();
335
+ }));
336
+ });
337
+ }
338
+
339
+ protected computeMergeRangeStateFromResult(mergeRange: MergeRange): MergeRangeResultState {
340
+
341
+ const resultRange = this.getLineRangeInResult(mergeRange);
342
+ const existingLines = resultRange.getLines(this.resultDocument);
343
+
344
+ const states: MergeRangeAcceptedState[] = [
345
+ 'Base',
346
+ 'Side1',
347
+ 'Side2',
348
+ 'Side1Side2Smart',
349
+ 'Side2Side1Smart',
350
+ 'Side1Side2',
351
+ 'Side2Side1'
352
+ ];
353
+
354
+ for (const state of states) {
355
+ const edit = mergeRange.getBaseRangeEdit(state);
356
+ if (ArrayUtils.equals(edit.newLines, existingLines)) {
357
+ return state;
358
+ }
359
+ }
360
+
361
+ return 'Unrecognized';
362
+ }
363
+
364
+ protected async doInit(): Promise<void> {
365
+ if (this.props.options?.resetResult) {
366
+ await this.reset();
367
+ }
368
+ }
369
+
370
+ dispose(): void {
371
+ this.toDispose.dispose();
372
+ }
373
+
374
+ isDisposed(): boolean {
375
+ return this.toDispose.disposed;
376
+ }
377
+
378
+ async reset(): Promise<void> {
379
+ await ObservableUtils.waitForState(this.diffComputingStateForSidesObservable, state => state === DiffComputingState.UpToDate);
380
+
381
+ this.shouldRecomputeHandledState = true;
382
+ this.resultDocument.textEditorModel.setValue(this.computeAutoMergedResult());
383
+ }
384
+
385
+ protected computeAutoMergedResult(): string {
386
+ const baseLines = this.baseDocument.textEditorModel.getLinesContent();
387
+ const side1Lines = this.side1Document.textEditorModel.getLinesContent();
388
+ const side2Lines = this.side2Document.textEditorModel.getLinesContent();
389
+
390
+ const resultLines: string[] = [];
391
+
392
+ function appendLinesToResult(documentLines: string[], lineRange: LineRange): void {
393
+ for (let i = lineRange.startLineNumber; i < lineRange.endLineNumberExclusive; i++) {
394
+ resultLines.push(documentLines[i]);
395
+ }
396
+ }
397
+
398
+ let baseStartLineNumber = 0;
399
+
400
+ for (const mergeRange of this.mergeRanges) {
401
+ appendLinesToResult(baseLines, LineRange.fromLineNumbers(baseStartLineNumber, mergeRange.baseRange.startLineNumber));
402
+
403
+ if (mergeRange.side1Changes.length === 0) {
404
+ appendLinesToResult(side2Lines, mergeRange.side2Range);
405
+ } else if (mergeRange.side2Changes.length === 0) {
406
+ appendLinesToResult(side1Lines, mergeRange.side1Range);
407
+ } else if (mergeRange.isEqualChange) {
408
+ appendLinesToResult(side1Lines, mergeRange.side1Range);
409
+ } else {
410
+ appendLinesToResult(baseLines, mergeRange.baseRange);
411
+ }
412
+
413
+ baseStartLineNumber = mergeRange.baseRange.endLineNumberExclusive;
414
+ }
415
+
416
+ appendLinesToResult(baseLines, LineRange.fromLineNumbers(baseStartLineNumber, baseLines.length));
417
+
418
+ return resultLines.join(this.resultDocument.textEditorModel.getEOL());
419
+ }
420
+
421
+ protected computeMergeRanges(): MergeRange[] {
422
+ return MergeRange.computeMergeRanges(this.side1Changes, this.side2Changes, this.baseDocument, this.side1Document, this.side2Document);
423
+ }
424
+
425
+ hasMergeRange(mergeRange: MergeRange): boolean {
426
+ return this.mergeRangesDataObservable.get().has(mergeRange);
427
+ }
428
+
429
+ protected getMergeRangeData(mergeRange: MergeRange): MergeRangeData {
430
+ const mergeRangeData = this.mergeRangesDataObservable.get().get(mergeRange);
431
+ if (!mergeRangeData) {
432
+ throw new Error('Unknown merge range');
433
+ }
434
+ return mergeRangeData;
435
+ }
436
+
437
+ getMergeRangeResultState(mergeRange: MergeRange): MergeRangeResultState {
438
+ return this.getMergeRangeData(mergeRange).resultStateObservable.get();
439
+ }
440
+
441
+ applyMergeRangeAcceptedState(mergeRange: MergeRange, state: MergeRangeAcceptedState): void {
442
+ if (!this.isUpToDateObservable.get()) {
443
+ throw new Error('Cannot apply merge range accepted state while updating');
444
+ }
445
+ if (state !== 'Base' && this.getMergeRangeResultState(mergeRange) === 'Unrecognized') {
446
+ throw new Error('Cannot apply merge range accepted state to an unrecognized result state');
447
+ }
448
+
449
+ const { originalRange: baseRange, modifiedRange: resultRange } = this.getResultLineRangeMapping(mergeRange);
450
+ let newLines: string[];
451
+ if (state === 'Base') {
452
+ newLines = baseRange.getLines(this.baseDocument);
453
+ } else {
454
+ if (!baseRange.equals(mergeRange.baseRange)) {
455
+ throw new Error('Assertion error');
456
+ }
457
+ newLines = mergeRange.getBaseRangeEdit(state).newLines;
458
+ }
459
+ const resultEdit = new LineRangeEdit(resultRange, newLines);
460
+ const editOperation = resultEdit.toRangeEdit(this.resultDocument.lineCount).toMonacoEdit();
461
+
462
+ const cursorState = this.resultEditor.getControl().getSelections();
463
+ this.resultDocument.textEditorModel.pushStackElement();
464
+ this.resultDocument.textEditorModel.pushEditOperations(cursorState, [editOperation], () => cursorState);
465
+ this.resultDocument.textEditorModel.pushStackElement();
466
+ }
467
+
468
+ isMergeRangeHandled(mergeRange: MergeRange): boolean {
469
+ return this.getMergeRangeData(mergeRange).isHandledObservable.get();
470
+ }
471
+
472
+ getLineRangeInResult(mergeRange: MergeRange): LineRange {
473
+ return this.getResultLineRangeMapping(mergeRange).modifiedRange;
474
+ }
475
+
476
+ protected getResultLineRangeMapping(mergeRange: MergeRange): LineRangeMapping {
477
+ const projectLine = (lineNumber: number): number | LineRangeMapping => {
478
+ let offset = 0;
479
+ const changes = this.resultChanges;
480
+ for (const change of changes) {
481
+ const { originalRange } = change;
482
+ if (originalRange.containsLine(lineNumber) || originalRange.endLineNumberExclusive === lineNumber) {
483
+ return change;
484
+ } else if (originalRange.endLineNumberExclusive < lineNumber) {
485
+ offset = change.modifiedRange.endLineNumberExclusive - originalRange.endLineNumberExclusive;
486
+ } else {
487
+ break;
488
+ }
489
+ }
490
+ return lineNumber + offset;
491
+ };
492
+ let startBase = mergeRange.baseRange.startLineNumber;
493
+ let startResult = projectLine(startBase);
494
+ if (typeof startResult !== 'number') {
495
+ startBase = startResult.originalRange.startLineNumber;
496
+ startResult = startResult.modifiedRange.startLineNumber;
497
+ }
498
+ let endExclusiveBase = mergeRange.baseRange.endLineNumberExclusive;
499
+ let endExclusiveResult = projectLine(endExclusiveBase);
500
+ if (typeof endExclusiveResult !== 'number') {
501
+ endExclusiveBase = endExclusiveResult.originalRange.endLineNumberExclusive;
502
+ endExclusiveResult = endExclusiveResult.modifiedRange.endLineNumberExclusive;
503
+ }
504
+ return new LineRangeMapping(LineRange.fromLineNumbers(startBase, endExclusiveBase), LineRange.fromLineNumbers(startResult, endExclusiveResult));
505
+ }
506
+
507
+ translateBaseRangeToSide(range: Range, side: MergeSide): Range {
508
+ const rangeMap = side === 1 ? this.baseToSide1RangeMap : this.baseToSide2RangeMap;
509
+ return rangeMap.projectRange(range).modifiedRange;
510
+ }
511
+
512
+ translateSideRangeToBase(range: Range, side: MergeSide): Range {
513
+ const rangeMap = side === 1 ? this.side1ToBaseRangeMap : this.side2ToBaseRangeMap;
514
+ return rangeMap.projectRange(range).modifiedRange;
515
+ }
516
+
517
+ translateBaseRangeToResult(range: Range): Range {
518
+ return this.baseToResultRangeMap.projectRange(range).modifiedRange;
519
+ }
520
+
521
+ translateResultRangeToBase(range: Range): Range {
522
+ return this.resultToBaseRangeMap.projectRange(range).modifiedRange;
523
+ }
524
+
525
+ findMergeRanges(baseRange: LineRange): MergeRange[] {
526
+ return this.mergeRanges.filter(mergeRange => mergeRange.baseRange.touches(baseRange));
527
+ }
528
+
529
+ protected computeSideToResultDiff(sideChanges: readonly LineRangeMapping[], resultChanges: readonly LineRangeMapping[]): readonly LineRangeMapping[] {
530
+ return DocumentLineRangeMap.betweenModifiedSides(sideChanges, resultChanges).lineRangeMappings;
531
+ }
532
+
533
+ protected newMergeRangeData(): MergeRangeData {
534
+ return new MergeRangeData();
535
+ }
536
+
537
+ protected newLiveDiff(originalDocument: MonacoEditorModel, modifiedDocument: MonacoEditorModel): LiveDiff {
538
+ return new LiveDiff(originalDocument, modifiedDocument, this.diffComputer);
539
+ }
540
+
541
+ protected newDocumentLineRangeMap(lineRangeMappings: readonly LineRangeMapping[]): DocumentLineRangeMap {
542
+ return new DocumentLineRangeMap(lineRangeMappings);
543
+ }
544
+
545
+ protected newDocumentRangeMap(rangeMappings: readonly RangeMapping[]): DocumentRangeMap {
546
+ return new DocumentRangeMap(rangeMappings);
547
+ }
548
+
549
+ protected getDiffComputingState(...liveDiffs: LiveDiff[]): DiffComputingState {
550
+ const liveDiffStates = liveDiffs.map(liveDiff => liveDiff.state);
551
+
552
+ if (liveDiffStates.some(state => state === LiveDiffState.Initializing)) {
553
+ return DiffComputingState.Initializing;
554
+ }
555
+
556
+ if (liveDiffStates.some(state => state === LiveDiffState.Updating)) {
557
+ return DiffComputingState.Updating;
558
+ }
559
+
560
+ return DiffComputingState.UpToDate;
561
+ }
562
+ }
563
+
564
+ export const enum DiffComputingState {
565
+ Initializing,
566
+ UpToDate,
567
+ Updating
568
+ }
569
+
570
+ export class MergeRangeData {
571
+ readonly resultStateObservable = SettableObservable.create<MergeRangeResultState>('Base');
572
+ readonly isHandledObservable = SettableObservable.create(false);
573
+ }
574
+
575
+ class AttachedHistory implements Disposable {
576
+ private readonly toDispose = new DisposableCollection();
577
+ private readonly attachedHistory: { element: IAttachedHistoryElement; altId: number }[] = [];
578
+
579
+ constructor(private readonly model: MonacoEditorModel) {
580
+ let previousAltId = this.model.textEditorModel.getAlternativeVersionId();
581
+ this.toDispose.push(model.textEditorModel.onDidChangeContent(event => {
582
+ const currentAltId = model.textEditorModel.getAlternativeVersionId();
583
+
584
+ if (event.isRedoing) {
585
+ for (const item of this.attachedHistory) {
586
+ if (previousAltId < item.altId && item.altId <= currentAltId) {
587
+ item.element.redo();
588
+ }
589
+ }
590
+ } else if (event.isUndoing) {
591
+ for (let i = this.attachedHistory.length - 1; i >= 0; i--) {
592
+ const item = this.attachedHistory[i];
593
+ if (currentAltId < item.altId && item.altId <= previousAltId) {
594
+ item.element.undo();
595
+ }
596
+ }
597
+ } else {
598
+ // The user destroyed the redo stack by performing a non redo/undo operation.
599
+ while (
600
+ this.attachedHistory.length > 0
601
+ && this.attachedHistory[this.attachedHistory.length - 1].altId > previousAltId
602
+ ) {
603
+ this.attachedHistory.pop();
604
+ }
605
+ }
606
+
607
+ previousAltId = currentAltId;
608
+ }));
609
+ }
610
+
611
+ dispose(): void {
612
+ this.toDispose.dispose();
613
+ }
614
+
615
+ pushAttachedHistoryElement(element: IAttachedHistoryElement): void {
616
+ this.attachedHistory.push({ altId: this.model.textEditorModel.getAlternativeVersionId(), element });
617
+ }
618
+ }
619
+
620
+ interface IAttachedHistoryElement {
621
+ undo(): void;
622
+ redo(): void;
623
+ }