src/vs/editor/common/diff/legacyLinesDiffComputer.ts

691 LOC · 677 covered · 14 uncovered · 160 ranges · 394 concepts · 63 introducers · 182 tests

File neighbourhood

The centred file is linked to every concept that introduces one of its ranges, every test that runs code from the file, and the gray connector concepts standing between those tests and the file's own introducer concepts. Undirected links join concepts to every file where they introduce source and concepts to the tests they introduce; arrows show specialization between the displayed concepts and bridge only concepts omitted from this view. Concept colors match the source ranges below; connector concepts have no source color and are shown in gray.

Focused file, its introducer and connector concepts, their introduced files, and tests that run code from the file

In the embedded map, ordinary wheel input scrolls the page; use the visible controls to zoom and drag to pan. Open the full-screen map for canvas navigation: wheel pans, Ctrl/Command plus wheel zooms, and arrow keys pan when this region is focused. On touch screens, open the full-screen map to pan or pinch. If JavaScript or WebGL is unavailable, use the related-file, concept, and source links on this page.

Graph controls are ready.

Interactive rendering requires JavaScript and WebGL. Use the related-file, concept, and source links on this page while the interactive map is unavailable.

1 > /*--------------------------------------------------------------------------------------------- legacyLinesDiffComputer.ts ×28
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 >
6 > import { CharCode } from '../../../base/common/charCode.js';
7 > import { IDiffChange, ISequence, LcsDiff, IDiffResult } from '../../../base/common/diff/diff.js';
8 > import { ILinesDiffComputer, ILinesDiffComputerOptions, LinesDiff } from './linesDiffComputer.js';
9 > import { RangeMapping, DetailedLineRangeMapping } from './rangeMapping.js';
10 > import * as strings from '../../../base/common/strings.js';
11 > import { Range } from '../core/range.js';
12 > import { assertFn, checkAdjacentItems } from '../../../base/common/assert.js';
13 > import { LineRange } from '../core/ranges/lineRange.js';
14 >
15 > const MINIMUM_MATCHING_CHARACTER_LENGTH = 3;
16 >
17 > export class LegacyLinesDiffComputer implements ILinesDiffComputer {
18 > computeDiff(originalLines: string[], modifiedLines: string[], options: ILinesDiffComputerOptions): LinesDiff {
19 > const diffComputer = new DiffComputer(originalLines, modifiedLines, { legacyLinesDiffComputer.ts ×5
20 > maxComputationTime: options.maxComputationTimeMs,
21 > shouldIgnoreTrimWhitespace: options.ignoreTrimWhitespace,
22 > shouldComputeCharChanges: true,
23 > shouldMakePrettyDiff: true,
24 > shouldPostProcessCharChanges: true,
25 > });
26 > const result = diffComputer.computeDiff();
27 > const changes: DetailedLineRangeMapping[] = [];
28 > let lastChange: DetailedLineRangeMapping | null = null;
29 >
30 >
31 > for (const c of result.changes) {
32 > let originalRange: LineRange; legacyLinesDiffComputer.ts ×7
33 > if (c.originalEndLineNumber === 0) {
34 > // Insertion legacyLinesDiffComputer.ts ×1
35 > originalRange = new LineRange(c.originalStartLineNumber + 1, c.originalStartLineNumber + 1);
37 > originalRange = new LineRange(c.originalStartLineNumber, c.originalEndLineNumber + 1); legacyLinesDiffComputer.ts ×1
38 > }
40 > let modifiedRange: LineRange;
41 > if (c.modifiedEndLineNumber === 0) {
43 > modifiedRange = new LineRange(c.modifiedStartLineNumber + 1, c.modifiedStartLineNumber + 1);
45 > modifiedRange = new LineRange(c.modifiedStartLineNumber, c.modifiedEndLineNumber + 1); legacyLinesDiffComputer.ts ×1
46 > }
48 > let change = new DetailedLineRangeMapping(originalRange, modifiedRange, c.charChanges?.map(c => new RangeMapping(
49 > new Range(c.originalStartLineNumber, c.originalStartColumn, c.originalEndLineNumber, c.originalEndColumn), legacyLinesDiffComputer.ts ×1
50 > new Range(c.modifiedStartLineNumber, c.modifiedStartColumn, c.modifiedEndLineNumber, c.modifiedEndColumn),
52 > if (lastChange) {
53 > if (lastChange.modified.endLineNumberExclusive === change.modified.startLineNumber legacyLinesDiffComputer.ts ×2
54 > || lastChange.original.endLineNumberExclusive === change.original.startLineNumber) {
55 > // join touching diffs. Probably moving diffs up/down in the algorithm causes touching diffs. legacyLinesDiffComputer.ts ×1
56 > change = new DetailedLineRangeMapping(
57 > lastChange.original.join(change.original),
58 > lastChange.modified.join(change.modified),
59 > lastChange.innerChanges && change.innerChanges ?
60 > lastChange.innerChanges.concat(change.innerChanges) : undefined
61 > );
62 > changes.pop();
63 > }
66 > changes.push(change);
67 > lastChange = change;
68 > }
70 > assertFn(() => {
71 > return checkAdjacentItems(changes,
72 > (m1, m2) => m2.original.startLineNumber - m1.original.endLineNumberExclusive === m2.modified.startLineNumber - m1.modified.endLineNumberExclusive &&
73 > // There has to be an unchanged line in between (otherwise both diffs should have been joined) legacyLinesDiffComputer.ts ×1
74 > m1.original.endLineNumberExclusive < m2.original.startLineNumber &&
75 > m1.modified.endLineNumberExclusive < m2.modified.startLineNumber,
77 > });
78 >
79 > return new LinesDiff(changes, [], result.quitEarly);
80 > }
82 >
83 > export interface IDiffComputationResult {
84 > quitEarly: boolean;
85 > identical: boolean;
86 >
87 > /**
88 > * The changes as (legacy) line change array.
89 > * @deprecated Use `changes2` instead.
90 > */
91 > changes: ILineChange[];
92 >
93 > /**
94 > * The changes as (modern) line range mapping array.
95 > */
96 > changes2: readonly DetailedLineRangeMapping[];
97 > }
98 >
99 > /**
100 > * A change
101 > */
102 > export interface IChange {
103 > readonly originalStartLineNumber: number;
104 > readonly originalEndLineNumber: number;
105 > readonly modifiedStartLineNumber: number;
106 > readonly modifiedEndLineNumber: number;
107 > }
108 >
109 > /**
110 > * A character level change.
111 > */
112 > export interface ICharChange extends IChange {
113 > readonly originalStartColumn: number;
114 > readonly originalEndColumn: number;
115 > readonly modifiedStartColumn: number;
116 > readonly modifiedEndColumn: number;
117 > }
118 >
119 > /**
120 > * A line change
121 > */
122 > export interface ILineChange extends IChange {
123 > readonly charChanges: ICharChange[] | undefined;
124 > }
125 >
126 > export interface IDiffComputerResult {
127 > quitEarly: boolean;
128 > changes: ILineChange[];
129 > }
130 >
131 > function computeDiff(originalSequence: ISequence, modifiedSequence: ISequence, continueProcessingPredicate: () => boolean, pretty: boolean): IDiffResult { diff.ts ×3
132 > const diffAlgo = new LcsDiff(originalSequence, modifiedSequence, continueProcessingPredicate);
133 > return diffAlgo.ComputeDiff(pretty);
134 > }
136 > class LineSequence implements ISequence {
137 >
138 > public readonly lines: string[];
139 > private readonly _startColumns: number[];
140 > private readonly _endColumns: number[];
141 >
142 > constructor(lines: string[]) {
143 > const startColumns: number[] = []; legacyLinesDiffComputer.ts ×8
144 > const endColumns: number[] = [];
145 > for (let i = 0, length = lines.length; i < length; i++) {
146 > startColumns[i] = getFirstNonBlankColumn(lines[i], 1);
147 > endColumns[i] = getLastNonBlankColumn(lines[i], 1);
148 > }
149 > this.lines = lines;
150 > this._startColumns = startColumns;
151 > this._endColumns = endColumns;
152 > }
154 > public getElements(): Int32Array | number[] | string[] {
155 > const elements: string[] = []; diff.ts ×3
156 > for (let i = 0, len = this.lines.length; i < len; i++) {
157 > elements[i] = this.lines[i].substring(this._startColumns[i] - 1, this._endColumns[i] - 1);
158 > }
159 > return elements;
160 > }
162 > public getStrictElement(index: number): string {
163 > return this.lines[index]; diff.ts ×1
164 > }
166 > public getStartLineNumber(i: number): number {
167 > return i + 1; legacyLinesDiffComputer.ts ×8
168 > }
170 > public getEndLineNumber(i: number): number {
171 > return i + 1; legacyLinesDiffComputer.ts ×8
172 > }
174 > public createCharSequence(shouldIgnoreTrimWhitespace: boolean, startIndex: number, endIndex: number): CharSequence {
175 > const charCodes: number[] = []; legacyLinesDiffComputer.ts ×6
176 > const lineNumbers: number[] = [];
177 > const columns: number[] = [];
178 > let len = 0;
179 > for (let index = startIndex; index <= endIndex; index++) {
180 > const lineContent = this.lines[index];
181 > const startColumn = (shouldIgnoreTrimWhitespace ? this._startColumns[index] : 1);
182 > const endColumn = (shouldIgnoreTrimWhitespace ? this._endColumns[index] : lineContent.length + 1);
183 > for (let col = startColumn; col < endColumn; col++) {
184 > charCodes[len] = lineContent.charCodeAt(col - 1);
185 > lineNumbers[len] = index + 1;
186 > columns[len] = col;
187 > len++;
188 > }
189 > if (!shouldIgnoreTrimWhitespace && index < endIndex) {
190 > // Add \n if trim whitespace is not ignored legacyLinesDiffComputer.ts ×1
191 > charCodes[len] = CharCode.LineFeed;
192 > lineNumbers[len] = index + 1;
193 > columns[len] = lineContent.length + 1;
194 > len++;
195 > }
197 > return new CharSequence(charCodes, lineNumbers, columns);
198 > }
200 >
201 > class CharSequence implements ISequence {
202 >
203 > private readonly _charCodes: number[];
204 > private readonly _lineNumbers: number[];
205 > private readonly _columns: number[];
206 >
207 > constructor(charCodes: number[], lineNumbers: number[], columns: number[]) {
208 > this._charCodes = charCodes; legacyLinesDiffComputer.ts ×6
209 > this._lineNumbers = lineNumbers;
210 > this._columns = columns;
211 > }
213 > public toString() {
214 return (
215 '[' + this._charCodes.map((s, idx) => (s === CharCode.LineFeed ? '\\n' : String.fromCharCode(s)) + `-(${this._lineNumbers[idx]},${this._columns[idx]})`).join(', ') + ']'
216 );
217 }
219 > private _assertIndex(index: number, arr: number[]): void {
220 > if (index < 0 || index >= arr.length) { legacyLinesDiffComputer.ts ×15
221 throw new Error(`Illegal index`);
222 }
225 > public getElements(): Int32Array | number[] | string[] {
226 > return this._charCodes; legacyLinesDiffComputer.ts ×6
227 > }
229 > public getStartLineNumber(i: number): number {
230 > if (i > 0 && i === this._lineNumbers.length) { legacyLinesDiffComputer.ts ×15
231 > // the start line number of the element after the last element legacyLinesDiffComputer.ts ×2
232 > // is the end line number of the last element
233 > return this.getEndLineNumber(i - 1);
234 > }
235 > this._assertIndex(i, this._lineNumbers); legacyLinesDiffComputer.ts ×15
236 >
237 > return this._lineNumbers[i];
238 > }
240 > public getEndLineNumber(i: number): number {
241 > if (i === -1) { legacyLinesDiffComputer.ts ×15
242 > // the end line number of the element before the first element legacyLinesDiffComputer.ts ×2
243 > // is the start line number of the first element
244 > return this.getStartLineNumber(i + 1);
245 > }
246 > this._assertIndex(i, this._lineNumbers); legacyLinesDiffComputer.ts ×15
247 >
248 > if (this._charCodes[i] === CharCode.LineFeed) {
249 > return this._lineNumbers[i] + 1; legacyLinesDiffComputer.ts ×2
250 > }
251 > return this._lineNumbers[i]; legacyLinesDiffComputer.ts ×15
252 > }
254 > public getStartColumn(i: number): number {
255 > if (i > 0 && i === this._columns.length) { legacyLinesDiffComputer.ts ×15
256 > // the start column of the element after the last element legacyLinesDiffComputer.ts ×2
257 > // is the end column of the last element
258 > return this.getEndColumn(i - 1);
259 > }
260 > this._assertIndex(i, this._columns); legacyLinesDiffComputer.ts ×15
261 > return this._columns[i];
262 > }
264 > public getEndColumn(i: number): number {
265 > if (i === -1) { legacyLinesDiffComputer.ts ×15
266 > // the end column of the element before the first element legacyLinesDiffComputer.ts ×2
267 > // is the start column of the first element
268 > return this.getStartColumn(i + 1);
269 > }
270 > this._assertIndex(i, this._columns); legacyLinesDiffComputer.ts ×15
271 >
272 > if (this._charCodes[i] === CharCode.LineFeed) {
274 > }
275 > return this._columns[i] + 1; legacyLinesDiffComputer.ts ×15
276 > }
278 >
279 > class CharChange implements ICharChange {
280 >
281 > public originalStartLineNumber: number;
282 > public originalStartColumn: number;
283 > public originalEndLineNumber: number;
284 > public originalEndColumn: number;
285 >
286 > public modifiedStartLineNumber: number;
287 > public modifiedStartColumn: number;
288 > public modifiedEndLineNumber: number;
289 > public modifiedEndColumn: number;
290 >
291 > constructor(
292 > originalStartLineNumber: number, legacyLinesDiffComputer.ts ×1
293 > originalStartColumn: number,
294 > originalEndLineNumber: number,
295 > originalEndColumn: number,
296 > modifiedStartLineNumber: number,
297 > modifiedStartColumn: number,
298 > modifiedEndLineNumber: number,
299 > modifiedEndColumn: number
300 > ) {
301 > this.originalStartLineNumber = originalStartLineNumber;
302 > this.originalStartColumn = originalStartColumn;
303 > this.originalEndLineNumber = originalEndLineNumber;
304 > this.originalEndColumn = originalEndColumn;
305 > this.modifiedStartLineNumber = modifiedStartLineNumber;
306 > this.modifiedStartColumn = modifiedStartColumn;
307 > this.modifiedEndLineNumber = modifiedEndLineNumber;
308 > this.modifiedEndColumn = modifiedEndColumn;
309 > }
311 > public static createFromDiffChange(diffChange: IDiffChange, originalCharSequence: CharSequence, modifiedCharSequence: CharSequence): CharChange {
312 > const originalStartLineNumber = originalCharSequence.getStartLineNumber(diffChange.originalStart); legacyLinesDiffComputer.ts ×15
313 > const originalStartColumn = originalCharSequence.getStartColumn(diffChange.originalStart);
314 > const originalEndLineNumber = originalCharSequence.getEndLineNumber(diffChange.originalStart + diffChange.originalLength - 1);
315 > const originalEndColumn = originalCharSequence.getEndColumn(diffChange.originalStart + diffChange.originalLength - 1);
316 >
317 > const modifiedStartLineNumber = modifiedCharSequence.getStartLineNumber(diffChange.modifiedStart);
318 > const modifiedStartColumn = modifiedCharSequence.getStartColumn(diffChange.modifiedStart);
319 > const modifiedEndLineNumber = modifiedCharSequence.getEndLineNumber(diffChange.modifiedStart + diffChange.modifiedLength - 1);
320 > const modifiedEndColumn = modifiedCharSequence.getEndColumn(diffChange.modifiedStart + diffChange.modifiedLength - 1);
321 >
322 > return new CharChange(
323 > originalStartLineNumber, originalStartColumn, originalEndLineNumber, originalEndColumn,
324 > modifiedStartLineNumber, modifiedStartColumn, modifiedEndLineNumber, modifiedEndColumn,
325 > );
326 > }
328 >
329 > function postProcessCharChanges(rawChanges: IDiffChange[]): IDiffChange[] { legacyLinesDiffComputer.ts ×2
330 > if (rawChanges.length <= 1) {
331 > return rawChanges; legacyLinesDiffComputer.ts ×1
332 > }
334 > const result = [rawChanges[0]];
335 > let prevChange = result[0];
336 >
337 > for (let i = 1, len = rawChanges.length; i < len; i++) {
338 > const currChange = rawChanges[i];
339 >
340 > const originalMatchingLength = currChange.originalStart - (prevChange.originalStart + prevChange.originalLength);
341 > const modifiedMatchingLength = currChange.modifiedStart - (prevChange.modifiedStart + prevChange.modifiedLength);
342 > // Both of the above should be equal, but the continueProcessingPredicate may prevent this from being true
343 > const matchingLength = Math.min(originalMatchingLength, modifiedMatchingLength);
344 >
345 > if (matchingLength < MINIMUM_MATCHING_CHARACTER_LENGTH) {
346 > // Merge the current change into the previous one legacyLinesDiffComputer.ts ×1
347 > prevChange.originalLength = (currChange.originalStart + currChange.originalLength) - prevChange.originalStart;
348 > prevChange.modifiedLength = (currChange.modifiedStart + currChange.modifiedLength) - prevChange.modifiedStart;
350 > // Add the current change legacyLinesDiffComputer.ts ×1
351 > result.push(currChange);
352 > prevChange = currChange;
353 > }
355 >
356 > return result;
357 > }
359 > class LineChange implements ILineChange {
360 > public originalStartLineNumber: number;
361 > public originalEndLineNumber: number;
362 > public modifiedStartLineNumber: number;
363 > public modifiedEndLineNumber: number;
364 > public charChanges: CharChange[] | undefined;
365 >
366 > constructor(
367 > originalStartLineNumber: number, legacyLinesDiffComputer.ts ×1
368 > originalEndLineNumber: number,
369 > modifiedStartLineNumber: number,
370 > modifiedEndLineNumber: number,
371 > charChanges: CharChange[] | undefined
372 > ) {
373 > this.originalStartLineNumber = originalStartLineNumber;
374 > this.originalEndLineNumber = originalEndLineNumber;
375 > this.modifiedStartLineNumber = modifiedStartLineNumber;
376 > this.modifiedEndLineNumber = modifiedEndLineNumber;
377 > this.charChanges = charChanges;
378 > }
380 > public static createFromDiffResult(shouldIgnoreTrimWhitespace: boolean, diffChange: IDiffChange, originalLineSequence: LineSequence, modifiedLineSequence: LineSequence, continueCharDiff: () => boolean, shouldComputeCharChanges: boolean, shouldPostProcessCharChanges: boolean): LineChange {
381 > let originalStartLineNumber: number; legacyLinesDiffComputer.ts ×8
382 > let originalEndLineNumber: number;
383 > let modifiedStartLineNumber: number;
384 > let modifiedEndLineNumber: number;
385 > let charChanges: CharChange[] | undefined = undefined;
386 >
387 > if (diffChange.originalLength === 0) {
388 > originalStartLineNumber = originalLineSequence.getStartLineNumber(diffChange.originalStart) - 1; legacyLinesDiffComputer.ts ×1
389 > originalEndLineNumber = 0;
391 > originalStartLineNumber = originalLineSequence.getStartLineNumber(diffChange.originalStart); legacyLinesDiffComputer.ts ×1
392 > originalEndLineNumber = originalLineSequence.getEndLineNumber(diffChange.originalStart + diffChange.originalLength - 1);
393 > }
395 > if (diffChange.modifiedLength === 0) {
396 > modifiedStartLineNumber = modifiedLineSequence.getStartLineNumber(diffChange.modifiedStart) - 1; legacyLinesDiffComputer.ts ×1
397 > modifiedEndLineNumber = 0;
399 > modifiedStartLineNumber = modifiedLineSequence.getStartLineNumber(diffChange.modifiedStart); legacyLinesDiffComputer.ts ×1
400 > modifiedEndLineNumber = modifiedLineSequence.getEndLineNumber(diffChange.modifiedStart + diffChange.modifiedLength - 1);
401 > }
403 > if (shouldComputeCharChanges && diffChange.originalLength > 0 && diffChange.originalLength < 20 && diffChange.modifiedLength > 0 && diffChange.modifiedLength < 20 && continueCharDiff()) {
404 > // Compute character changes for diff chunks of at most 20 lines... legacyLinesDiffComputer.ts ×6
405 > const originalCharSequence = originalLineSequence.createCharSequence(shouldIgnoreTrimWhitespace, diffChange.originalStart, diffChange.originalStart + diffChange.originalLength - 1);
406 > const modifiedCharSequence = modifiedLineSequence.createCharSequence(shouldIgnoreTrimWhitespace, diffChange.modifiedStart, diffChange.modifiedStart + diffChange.modifiedLength - 1);
407 >
408 > if (originalCharSequence.getElements().length > 0 && modifiedCharSequence.getElements().length > 0) {
409 > let rawChanges = computeDiff(originalCharSequence, modifiedCharSequence, continueCharDiff, true).changes; legacyLinesDiffComputer.ts ×15
410 >
411 > if (shouldPostProcessCharChanges) {
412 > rawChanges = postProcessCharChanges(rawChanges); legacyLinesDiffComputer.ts ×2
413 > }
415 > charChanges = [];
416 > for (let i = 0, length = rawChanges.length; i < length; i++) {
417 > charChanges.push(CharChange.createFromDiffChange(rawChanges[i], originalCharSequence, modifiedCharSequence));
418 > }
419 > }
422 > return new LineChange(originalStartLineNumber, originalEndLineNumber, modifiedStartLineNumber, modifiedEndLineNumber, charChanges);
423 > }
425 >
426 > export interface IDiffComputerOpts {
427 > shouldComputeCharChanges: boolean;
428 > shouldPostProcessCharChanges: boolean;
429 > shouldIgnoreTrimWhitespace: boolean;
430 > shouldMakePrettyDiff: boolean;
431 > maxComputationTime: number;
432 > }
433 >
434 > export class DiffComputer {
435 >
436 > private readonly shouldComputeCharChanges: boolean;
437 > private readonly shouldPostProcessCharChanges: boolean;
438 > private readonly shouldIgnoreTrimWhitespace: boolean;
439 > private readonly shouldMakePrettyDiff: boolean;
440 > private readonly originalLines: string[];
441 > private readonly modifiedLines: string[];
442 > private readonly original: LineSequence;
443 > private readonly modified: LineSequence;
444 > private readonly continueLineDiff: () => boolean;
445 > private readonly continueCharDiff: () => boolean;
446 >
447 > constructor(originalLines: string[], modifiedLines: string[], opts: IDiffComputerOpts) {
448 > this.shouldComputeCharChanges = opts.shouldComputeCharChanges; legacyLinesDiffComputer.ts ×8
449 > this.shouldPostProcessCharChanges = opts.shouldPostProcessCharChanges;
450 > this.shouldIgnoreTrimWhitespace = opts.shouldIgnoreTrimWhitespace;
451 > this.shouldMakePrettyDiff = opts.shouldMakePrettyDiff;
452 > this.originalLines = originalLines;
453 > this.modifiedLines = modifiedLines;
454 > this.original = new LineSequence(originalLines);
455 > this.modified = new LineSequence(modifiedLines);
456 >
457 > this.continueLineDiff = createContinueProcessingPredicate(opts.maxComputationTime);
458 > this.continueCharDiff = createContinueProcessingPredicate(opts.maxComputationTime === 0 ? 0 : Math.min(opts.maxComputationTime, 5000)); // never run after 5s for character changes...
459 > }
461 > public computeDiff(): IDiffComputerResult {
463 > if (this.original.lines.length === 1 && this.original.lines[0].length === 0) {
464 > // empty original => fast path legacyLinesDiffComputer.ts ×1
465 > if (this.modified.lines.length === 1 && this.modified.lines[0].length === 0) {
467 > quitEarly: false,
468 > changes: []
469 > };
470 > }
472 > return {
473 > quitEarly: false,
474 > changes: [{
475 > originalStartLineNumber: 1,
476 > originalEndLineNumber: 1,
477 > modifiedStartLineNumber: 1,
478 > modifiedEndLineNumber: this.modified.lines.length,
479 > charChanges: undefined
480 > }]
481 > };
482 > }
484 > if (this.modified.lines.length === 1 && this.modified.lines[0].length === 0) { legacyLinesDiffComputer.ts ×8
485 > // empty modified => fast path legacyLinesDiffComputer.ts ×1
486 > return {
487 > quitEarly: false,
488 > changes: [{
489 > originalStartLineNumber: 1,
490 > originalEndLineNumber: this.original.lines.length,
491 > modifiedStartLineNumber: 1,
492 > modifiedEndLineNumber: 1,
493 > charChanges: undefined
494 > }]
495 > };
496 > }
497 > diff.ts ×3
498 > const diffResult = computeDiff(this.original, this.modified, this.continueLineDiff, this.shouldMakePrettyDiff);
499 > const rawChanges = diffResult.changes;
500 > const quitEarly = diffResult.quitEarly;
501 >
502 > // The diff is always computed with ignoring trim whitespace
503 > // This ensures we get the prettiest diff
504 >
505 > if (this.shouldIgnoreTrimWhitespace) {
506 > const lineChanges: LineChange[] = []; legacyLinesDiffComputer.ts ×1
507 > for (let i = 0, length = rawChanges.length; i < length; i++) {
508 > lineChanges.push(LineChange.createFromDiffResult(this.shouldIgnoreTrimWhitespace, rawChanges[i], this.original, this.modified, this.continueCharDiff, this.shouldComputeCharChanges, this.shouldPostProcessCharChanges));
509 > }
510 > return {
511 > quitEarly: quitEarly,
512 > changes: lineChanges
513 > };
514 > }
516 > // Need to post-process and introduce changes where the trim whitespace is different
517 > // Note that we are looping starting at -1 to also cover the lines before the first change
518 > const result: LineChange[] = [];
519 >
520 > let originalLineIndex = 0;
521 > let modifiedLineIndex = 0;
522 > for (let i = -1 /* !!!! */, len = rawChanges.length; i < len; i++) {
523 > const nextChange = (i + 1 < len ? rawChanges[i + 1] : null);
524 > const originalStop = (nextChange ? nextChange.originalStart : this.originalLines.length);
525 > const modifiedStop = (nextChange ? nextChange.modifiedStart : this.modifiedLines.length);
526 >
527 > while (originalLineIndex < originalStop && modifiedLineIndex < modifiedStop) {
528 > const originalLine = this.originalLines[originalLineIndex]; legacyLinesDiffComputer.ts ×2
529 > const modifiedLine = this.modifiedLines[modifiedLineIndex];
530 >
531 > if (originalLine !== modifiedLine) {
532 > // These lines differ only in trim whitespace legacyLinesDiffComputer.ts ×12
533 >
534 > // Check the leading whitespace
535 > {
536 > let originalStartColumn = getFirstNonBlankColumn(originalLine, 1);
537 > let modifiedStartColumn = getFirstNonBlankColumn(modifiedLine, 1);
538 > while (originalStartColumn > 1 && modifiedStartColumn > 1) {
539 > const originalChar = originalLine.charCodeAt(originalStartColumn - 2); legacyLinesDiffComputer.ts ×1
540 > const modifiedChar = modifiedLine.charCodeAt(modifiedStartColumn - 2);
541 > if (originalChar !== modifiedChar) {
543 > }
544 > originalStartColumn--; legacyLinesDiffComputer.ts ×1
545 > modifiedStartColumn--;
546 > }
548 > if (originalStartColumn > 1 || modifiedStartColumn > 1) {
549 > this._pushTrimWhitespaceCharChange(result, legacyLinesDiffComputer.ts ×1
550 > originalLineIndex + 1, 1, originalStartColumn,
551 > modifiedLineIndex + 1, 1, modifiedStartColumn
552 > );
553 > }
555 >
556 > // Check the trailing whitespace
557 > {
558 > let originalEndColumn = getLastNonBlankColumn(originalLine, 1);
559 > let modifiedEndColumn = getLastNonBlankColumn(modifiedLine, 1);
560 > const originalMaxColumn = originalLine.length + 1;
561 > const modifiedMaxColumn = modifiedLine.length + 1;
562 > while (originalEndColumn < originalMaxColumn && modifiedEndColumn < modifiedMaxColumn) {
563 const originalChar = originalLine.charCodeAt(originalEndColumn - 1);
564 const modifiedChar = originalLine.charCodeAt(modifiedEndColumn - 1);
565 if (originalChar !== modifiedChar) {
566 break;
567 }
568 originalEndColumn++;
569 modifiedEndColumn++;
570 }
572 > if (originalEndColumn < originalMaxColumn || modifiedEndColumn < modifiedMaxColumn) {
573 > this._pushTrimWhitespaceCharChange(result, legacyLinesDiffComputer.ts ×1
574 > originalLineIndex + 1, originalEndColumn, originalMaxColumn,
575 > modifiedLineIndex + 1, modifiedEndColumn, modifiedMaxColumn
576 > );
577 > }
579 > }
580 > originalLineIndex++; legacyLinesDiffComputer.ts ×2
581 > modifiedLineIndex++;
582 > }
584 > if (nextChange) {
585 > // Emit the actual change legacyLinesDiffComputer.ts ×1
586 > result.push(LineChange.createFromDiffResult(this.shouldIgnoreTrimWhitespace, nextChange, this.original, this.modified, this.continueCharDiff, this.shouldComputeCharChanges, this.shouldPostProcessCharChanges));
587 >
588 > originalLineIndex += nextChange.originalLength;
589 > modifiedLineIndex += nextChange.modifiedLength;
590 > }
592 >
593 > return {
594 > quitEarly: quitEarly,
595 > changes: result
596 > };
599 > private _pushTrimWhitespaceCharChange(
600 > result: LineChange[], legacyLinesDiffComputer.ts ×12
601 > originalLineNumber: number, originalStartColumn: number, originalEndColumn: number,
602 > modifiedLineNumber: number, modifiedStartColumn: number, modifiedEndColumn: number
603 > ): void {
604 > if (this._mergeTrimWhitespaceCharChange(result, originalLineNumber, originalStartColumn, originalEndColumn, modifiedLineNumber, modifiedStartColumn, modifiedEndColumn)) {
605 > // Merged into previous legacyLinesDiffComputer.ts ×1
606 > return;
607 > }
609 > let charChanges: CharChange[] | undefined = undefined;
610 > if (this.shouldComputeCharChanges) {
611 > charChanges = [new CharChange( legacyLinesDiffComputer.ts ×1
612 > originalLineNumber, originalStartColumn, originalLineNumber, originalEndColumn,
613 > modifiedLineNumber, modifiedStartColumn, modifiedLineNumber, modifiedEndColumn
614 > )];
615 > }
616 > result.push(new LineChange( legacyLinesDiffComputer.ts ×2
617 > originalLineNumber, originalLineNumber,
618 > modifiedLineNumber, modifiedLineNumber,
619 > charChanges
620 > ));
623 > private _mergeTrimWhitespaceCharChange(
624 > result: LineChange[], legacyLinesDiffComputer.ts ×12
625 > originalLineNumber: number, originalStartColumn: number, originalEndColumn: number,
626 > modifiedLineNumber: number, modifiedStartColumn: number, modifiedEndColumn: number
627 > ): boolean {
628 > const len = result.length;
629 > if (len === 0) {
630 > return false; legacyLinesDiffComputer.ts ×1
631 > }
633 > const prevChange = result[len - 1];
634 >
635 > if (prevChange.originalEndLineNumber === 0 || prevChange.modifiedEndLineNumber === 0) { legacyLinesDiffComputer.ts ×12
636 > // Don't merge with inserts/deletes legacyLinesDiffComputer.ts ×1
637 > return false;
638 > }
640 > if (prevChange.originalEndLineNumber === originalLineNumber && prevChange.modifiedEndLineNumber === modifiedLineNumber) { legacyLinesDiffComputer.ts ×12
641 > if (this.shouldComputeCharChanges && prevChange.charChanges) { legacyLinesDiffComputer.ts ×1
642 > prevChange.charChanges.push(new CharChange(
643 > originalLineNumber, originalStartColumn, originalLineNumber, originalEndColumn,
644 > modifiedLineNumber, modifiedStartColumn, modifiedLineNumber, modifiedEndColumn
645 > ));
646 > }
647 > return true;
648 > }
650 > if (prevChange.originalEndLineNumber + 1 === originalLineNumber && prevChange.modifiedEndLineNumber + 1 === modifiedLineNumber) { legacyLinesDiffComputer.ts ×12
651 > prevChange.originalEndLineNumber = originalLineNumber; legacyLinesDiffComputer.ts ×2
652 > prevChange.modifiedEndLineNumber = modifiedLineNumber;
653 > if (this.shouldComputeCharChanges && prevChange.charChanges) {
654 > prevChange.charChanges.push(new CharChange( legacyLinesDiffComputer.ts ×1
655 > originalLineNumber, originalStartColumn, originalLineNumber, originalEndColumn,
656 > modifiedLineNumber, modifiedStartColumn, modifiedLineNumber, modifiedEndColumn
657 > ));
658 > }
659 > return true; legacyLinesDiffComputer.ts ×2
660 > }
662 > return false;
665 >
666 > function getFirstNonBlankColumn(txt: string, defaultValue: number): number { legacyLinesDiffComputer.ts ×8
667 > const r = strings.firstNonWhitespaceIndex(txt);
668 > if (r === -1) {
669 > return defaultValue; legacyLinesDiffComputer.ts ×2
670 > }
671 > return r + 1; legacyLinesDiffComputer.ts ×2
672 > }
674 > function getLastNonBlankColumn(txt: string, defaultValue: number): number { legacyLinesDiffComputer.ts ×8
675 > const r = strings.lastNonWhitespaceIndex(txt);
676 > if (r === -1) {
677 > return defaultValue; legacyLinesDiffComputer.ts ×2
678 > }
679 > return r + 2; legacyLinesDiffComputer.ts ×2
680 > }
682 > function createContinueProcessingPredicate(maximumRuntime: number): () => boolean { legacyLinesDiffComputer.ts ×8
683 > if (maximumRuntime === 0) {
684 > return () => true; legacyLinesDiffComputer.ts ×1
685 > }
687 > const startTime = Date.now();
688 > return () => {
689 > return Date.now() - startTime < maximumRuntime; legacyLinesDiffComputer.ts ×1
690 > };