3
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
4
*--------------------------------------------------------------------------------------------*/
5
5
6
+ import { BugIndicatingError } from 'vs/base/common/errors' ;
6
7
import { Range } from 'vs/editor/common/core/range' ;
7
8
import { ICharChange , IDiffComputationResult , ILineChange } from 'vs/editor/common/diff/diffComputer' ;
8
9
import { ITextModel } from 'vs/editor/common/model' ;
9
10
import { IEditorWorkerService } from 'vs/editor/common/services/editorWorker' ;
10
- import { LineRange , LineRangeMapping , RangeMapping } from 'vs/workbench/contrib/mergeEditor/browser/model' ;
11
+ import { LineRange , DetailedLineRangeMapping , RangeMapping } from 'vs/workbench/contrib/mergeEditor/browser/model' ;
11
12
12
13
export interface IDiffComputer {
13
14
computeDiff ( textModel1 : ITextModel , textModel2 : ITextModel ) : Promise < IDiffComputerResult > ;
14
15
}
15
16
16
17
export interface IDiffComputerResult {
17
- diffs : LineRangeMapping [ ] | null ;
18
+ diffs : DetailedLineRangeMapping [ ] | null ;
18
19
}
19
20
20
21
export class EditorWorkerServiceDiffComputer implements IDiffComputer {
@@ -28,12 +29,12 @@ export class EditorWorkerServiceDiffComputer implements IDiffComputer {
28
29
return { diffs : EditorWorkerServiceDiffComputer . fromDiffComputationResult ( diffs , textModel1 , textModel2 ) } ;
29
30
}
30
31
31
- public static fromDiffComputationResult ( result : IDiffComputationResult , textModel1 : ITextModel , textModel2 : ITextModel ) : LineRangeMapping [ ] {
32
+ public static fromDiffComputationResult ( result : IDiffComputationResult , textModel1 : ITextModel , textModel2 : ITextModel ) : DetailedLineRangeMapping [ ] {
32
33
return result . changes . map ( ( c ) => fromLineChange ( c , textModel1 , textModel2 ) ) ;
33
34
}
34
35
}
35
36
36
- function fromLineChange ( lineChange : ILineChange , originalTextModel : ITextModel , modifiedTextModel : ITextModel ) : LineRangeMapping {
37
+ function fromLineChange ( lineChange : ILineChange , originalTextModel : ITextModel , modifiedTextModel : ITextModel ) : DetailedLineRangeMapping {
37
38
let originalRange : LineRange ;
38
39
if ( lineChange . originalEndLineNumber === 0 ) {
39
40
// Insertion
@@ -50,16 +51,16 @@ function fromLineChange(lineChange: ILineChange, originalTextModel: ITextModel,
50
51
modifiedRange = new LineRange ( lineChange . modifiedStartLineNumber , lineChange . modifiedEndLineNumber - lineChange . modifiedStartLineNumber + 1 ) ;
51
52
}
52
53
53
- let innerDiffs = lineChange . charChanges ?. map ( c => rangeMappingFromCharChange ( c ) ) ;
54
+ let innerDiffs = lineChange . charChanges ?. map ( c => rangeMappingFromCharChange ( c , originalTextModel , modifiedTextModel ) ) ;
54
55
if ( ! innerDiffs ) {
55
56
innerDiffs = [ rangeMappingFromLineRanges ( originalRange , modifiedRange ) ] ;
56
57
}
57
58
58
- return new LineRangeMapping (
59
- originalTextModel ,
59
+ return new DetailedLineRangeMapping (
60
60
originalRange ,
61
- modifiedTextModel ,
61
+ originalTextModel ,
62
62
modifiedRange ,
63
+ modifiedTextModel ,
63
64
innerDiffs
64
65
) ;
65
66
}
@@ -81,9 +82,80 @@ function rangeMappingFromLineRanges(originalRange: LineRange, modifiedRange: Lin
81
82
) ;
82
83
}
83
84
84
- function rangeMappingFromCharChange ( charChange : ICharChange ) : RangeMapping {
85
- return new RangeMapping (
85
+ function rangeMappingFromCharChange ( charChange : ICharChange , inputTextModel : ITextModel , modifiedTextModel : ITextModel ) : RangeMapping {
86
+ return normalizeRangeMapping ( new RangeMapping (
86
87
new Range ( charChange . originalStartLineNumber , charChange . originalStartColumn , charChange . originalEndLineNumber , charChange . originalEndColumn ) ,
87
88
new Range ( charChange . modifiedStartLineNumber , charChange . modifiedStartColumn , charChange . modifiedEndLineNumber , charChange . modifiedEndColumn )
89
+ ) , inputTextModel , modifiedTextModel ) ;
90
+ }
91
+
92
+ function normalizeRangeMapping ( rangeMapping : RangeMapping , inputTextModel : ITextModel , outputTextModel : ITextModel ) : RangeMapping {
93
+ const inputRangeEmpty = rangeMapping . inputRange . isEmpty ( ) ;
94
+ const outputRangeEmpty = rangeMapping . outputRange . isEmpty ( ) ;
95
+
96
+ if ( inputRangeEmpty && outputRangeEmpty ) {
97
+ throw new BugIndicatingError ( ) ; // This case makes no sense, but it is an edge case we need to rule out
98
+ }
99
+
100
+ const originalStartsAtEndOfLine = isAtEndOfLine ( rangeMapping . inputRange . startLineNumber , rangeMapping . inputRange . startColumn , inputTextModel ) ;
101
+ const modifiedStartsAtEndOfLine = isAtEndOfLine ( rangeMapping . outputRange . startLineNumber , rangeMapping . outputRange . startColumn , outputTextModel ) ;
102
+
103
+ if ( ! inputRangeEmpty && ! outputRangeEmpty && originalStartsAtEndOfLine && modifiedStartsAtEndOfLine ) {
104
+ // a b c [\n] x y z \n
105
+ // d e f [\n a] \n
106
+ // ->
107
+ // a b c \n [] x y z \n
108
+ // d e f \n [a] \n
109
+
110
+ return new RangeMapping (
111
+ rangeMapping . inputRange . setStartPosition ( rangeMapping . inputRange . startLineNumber + 1 , 1 ) ,
112
+
113
+ rangeMapping . outputRange . setStartPosition ( rangeMapping . outputRange . startLineNumber + 1 , 1 ) ,
114
+ ) ;
115
+ }
116
+
117
+ if (
118
+ modifiedStartsAtEndOfLine &&
119
+ originalStartsAtEndOfLine &&
120
+ ( ( inputRangeEmpty && rangeEndsAtEndOfLine ( rangeMapping . outputRange , outputTextModel ) ) ||
121
+ ( outputRangeEmpty && rangeEndsAtEndOfLine ( rangeMapping . inputRange , inputTextModel ) ) )
122
+ ) {
123
+ // o: a b c [] \n x y z \n
124
+ // m: d e f [\n a] \n
125
+ // ->
126
+ // o: a b c \n [] x y z \n
127
+ // m: d e f \n [a \n]
128
+
129
+ // or
130
+
131
+ // a b c [\n x y z] \n
132
+ // d e f [] \n a \n
133
+ // ->
134
+ // a b c \n [x y z \n]
135
+ // d e f \n [] a \n
136
+
137
+ return new RangeMapping (
138
+ moveRange ( rangeMapping . inputRange ) ,
139
+ moveRange ( rangeMapping . outputRange )
140
+ ) ;
141
+ }
142
+
143
+ return rangeMapping ;
144
+ }
145
+
146
+ function isAtEndOfLine ( lineNumber : number , column : number , model : ITextModel ) : boolean {
147
+ return column >= model . getLineMaxColumn ( lineNumber ) ;
148
+ }
149
+
150
+ function rangeEndsAtEndOfLine ( range : Range , model : ITextModel , ) : boolean {
151
+ return isAtEndOfLine ( range . endLineNumber , range . endColumn , model ) ;
152
+ }
153
+
154
+ function moveRange ( range : Range ) : Range {
155
+ return new Range (
156
+ range . startLineNumber + 1 ,
157
+ 1 ,
158
+ range . endLineNumber + 1 ,
159
+ 1 ,
88
160
) ;
89
161
}
0 commit comments