Skip to content

Commit 0856c00

Browse files
committed
1 parent cb7c025 commit 0856c00

7 files changed

Lines changed: 125 additions & 40 deletions

File tree

src/vs/editor/common/core/edits/lineEdit.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ export class LineEdit {
2020
return new LineEdit(data.map(e => LineReplacement.deserialize(e)));
2121
}
2222

23-
public static fromEdit(edit: BaseStringEdit, initialValue: AbstractText): LineEdit {
23+
public static fromStringEdit(edit: BaseStringEdit, initialValue: AbstractText): LineEdit {
2424
const textEdit = TextEdit.fromStringEdit(edit, initialValue);
2525
return LineEdit.fromTextEdit(textEdit, initialValue);
2626
}

src/vs/editor/common/core/edits/stringEdit.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ export abstract class BaseStringReplacement<T extends BaseStringReplacement<T> =
199199
getNewLength(): number { return this.newText.length; }
200200

201201
override toString(): string {
202-
return `${this.replaceRange} -> "${this.newText}"`;
202+
return `${this.replaceRange} -> ${JSON.stringify(this.newText)}`;
203203
}
204204

205205
replace(str: string): string {

src/vs/editor/common/services/editorWebWorker.ts

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import { computeDefaultDocumentColors } from '../languages/defaultDocumentColors
2828
import { FindSectionHeaderOptions, SectionHeader, findSectionHeaders } from './findSectionHeaders.js';
2929
import { IRawModelData, IWorkerTextModelSyncChannelServer } from './textModelSync/textModelSync.protocol.js';
3030
import { ICommonModel, WorkerTextModelSyncServer } from './textModelSync/textModelSync.impl.js';
31-
import { ISerializedStringEdit } from '../core/edits/stringEdit.js';
31+
import { ISerializedStringEdit, StringEdit } from '../core/edits/stringEdit.js';
3232
import { StringText } from '../core/text/abstractText.js';
3333
import { ensureDependenciesAreSet } from '../core/text/positionToOffset.js';
3434

@@ -205,21 +205,7 @@ export class EditorWorker implements IDisposable, IWorkerTextModelSyncChannelSer
205205
}
206206

207207
public $computeStringDiff(original: string, modified: string, options: { maxComputationTimeMs: number }, algorithm: DiffAlgorithmName): ISerializedStringEdit {
208-
const diffAlgorithm: ILinesDiffComputer = algorithm === 'advanced' ? linesDiffComputers.getDefault() : linesDiffComputers.getLegacy();
209-
210-
ensureDependenciesAreSet();
211-
212-
const originalText = new StringText(original);
213-
const originalLines = originalText.getLines();
214-
const modifiedText = new StringText(modified);
215-
const modifiedLines = modifiedText.getLines();
216-
217-
const result = diffAlgorithm.computeDiff(originalLines, modifiedLines, { ignoreTrimWhitespace: false, maxComputationTimeMs: options.maxComputationTimeMs, computeMoves: false, extendToSubwords: false });
218-
219-
const textEdit = DetailedLineRangeMapping.toTextEdit(result.changes, modifiedText);
220-
const strEdit = originalText.getTransformer().getStringEdit(textEdit);
221-
222-
return strEdit.toJson();
208+
return computeStringDiff(original, modified, options, algorithm).toJson();
223209
}
224210

225211
// ---- END diff --------------------------------------------------------------------------
@@ -549,3 +535,24 @@ if (typeof importScripts === 'function') {
549535
// Running in a web worker
550536
globalThis.monaco = createMonacoBaseAPI();
551537
}
538+
539+
/**
540+
* @internal
541+
*/
542+
export function computeStringDiff(original: string, modified: string, options: { maxComputationTimeMs: number }, algorithm: DiffAlgorithmName): StringEdit {
543+
const diffAlgorithm: ILinesDiffComputer = algorithm === 'advanced' ? linesDiffComputers.getDefault() : linesDiffComputers.getLegacy();
544+
545+
ensureDependenciesAreSet();
546+
547+
const originalText = new StringText(original);
548+
const originalLines = originalText.getLines();
549+
const modifiedText = new StringText(modified);
550+
const modifiedLines = modifiedText.getLines();
551+
552+
const result = diffAlgorithm.computeDiff(originalLines, modifiedLines, { ignoreTrimWhitespace: false, maxComputationTimeMs: options.maxComputationTimeMs, computeMoves: false, extendToSubwords: false });
553+
554+
const textEdit = DetailedLineRangeMapping.toTextEdit(result.changes, modifiedText);
555+
const strEdit = originalText.getTransformer().getStringEdit(textEdit);
556+
557+
return strEdit;
558+
}

src/vs/editor/common/textModelEditSource.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ export class EditDeltaInfo {
206206
}
207207

208208
public static fromEdit(edit: BaseStringEdit, originalString: StringText): EditDeltaInfo {
209-
const lineEdit = LineEdit.fromEdit(edit, originalString);
209+
const lineEdit = LineEdit.fromStringEdit(edit, originalString);
210210
const linesAdded = sumBy(lineEdit.replacements, r => r.newLines.length);
211211
const linesRemoved = sumBy(lineEdit.replacements, r => r.lineRange.length);
212212
const charsAdded = sumBy(edit.replacements, r => r.getNewLength());

src/vs/workbench/contrib/editTelemetry/browser/telemetry/arcTelemetrySender.ts

Lines changed: 3 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,19 @@
33
* Licensed under the MIT License. See License.txt in the project root for license information.
44
*--------------------------------------------------------------------------------------------*/
55

6-
import { sumBy } from '../../../../../base/common/arrays.js';
76
import { TimeoutTimer } from '../../../../../base/common/async.js';
87
import { onUnexpectedError } from '../../../../../base/common/errors.js';
98
import { Disposable, DisposableStore, toDisposable } from '../../../../../base/common/lifecycle.js';
109
import { IObservableWithChange, runOnChange } from '../../../../../base/common/observable.js';
1110
import { generateUuid } from '../../../../../base/common/uuid.js';
12-
import { LineEdit } from '../../../../../editor/common/core/edits/lineEdit.js';
1311
import { AnnotatedStringEdit, BaseStringEdit } from '../../../../../editor/common/core/edits/stringEdit.js';
1412
import { StringText } from '../../../../../editor/common/core/text/abstractText.js';
1513
import { EditDeltaInfo, EditSuggestionId, ITextModelEditSourceMetadata } from '../../../../../editor/common/textModelEditSource.js';
1614
import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js';
1715
import { ITelemetryService } from '../../../../../platform/telemetry/common/telemetry.js';
1816
import { EditSourceData, IDocumentWithAnnotatedEdits, createDocWithJustReason } from '../helpers/documentWithAnnotatedEdits.js';
1917
import { IAiEditTelemetryService } from './aiEditTelemetry/aiEditTelemetryService.js';
20-
import { ArcTracker } from './arcTracker.js';
18+
import { ArcTracker } from '../../common/arcTracker.js';
2119
import type { ScmRepoBridge } from './editSourceTrackingImpl.js';
2220
import { forwardToChannelIf, isCopilotLikeExtension } from './forwardingTelemetryService.js';
2321

@@ -302,7 +300,7 @@ export class ArcTelemetryReporter {
302300
}
303301
}));
304302

305-
this._initialLineCounts = this._getLineCountInfo();
303+
this._initialLineCounts = this._arcTracker.getLineCountInfo();
306304

307305
this._initialBranchName = this._gitRepo?.headBranchNameObs.get();
308306

@@ -319,17 +317,6 @@ export class ArcTelemetryReporter {
319317
}
320318
}
321319

322-
private _getLineCountInfo(): { deletedLineCounts: number; insertedLineCounts: number } {
323-
const e = this._arcTracker.getTrackedEdit();
324-
const le = LineEdit.fromEdit(e, this._documentValueBeforeTrackedEdit);
325-
const deletedLineCount = sumBy(le.replacements, r => r.lineRange.length);
326-
const insertedLineCount = sumBy(le.getNewLineRanges(), r => r.length);
327-
return {
328-
deletedLineCounts: deletedLineCount,
329-
insertedLineCounts: insertedLineCount,
330-
};
331-
}
332-
333320
private _reportAfter(timeoutMs: number, cb?: () => void) {
334321
const timer = new TimeoutTimer(() => {
335322
this._report(timeoutMs);
@@ -344,7 +331,7 @@ export class ArcTelemetryReporter {
344331
private _report(timeMs: number): void {
345332
const currentBranch = this._gitRepo?.headBranchNameObs.get();
346333
const didBranchChange = currentBranch !== this._initialBranchName;
347-
const currentLineCounts = this._getLineCountInfo();
334+
const currentLineCounts = this._arcTracker.getLineCountInfo();
348335

349336
this._sendTelemetryEvent({
350337
telemetryService: this._telemetryService,

src/vs/workbench/contrib/editTelemetry/browser/telemetry/arcTracker.ts renamed to src/vs/workbench/contrib/editTelemetry/common/arcTracker.ts

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,17 @@
33
* Licensed under the MIT License. See License.txt in the project root for license information.
44
*--------------------------------------------------------------------------------------------*/
55

6-
import { sumBy } from '../../../../../base/common/arrays.js';
7-
import { AnnotatedStringEdit, BaseStringEdit, IEditData, StringEdit } from '../../../../../editor/common/core/edits/stringEdit.js';
8-
import { AbstractText } from '../../../../../editor/common/core/text/abstractText.js';
6+
import { sumBy } from '../../../../base/common/arrays.js';
7+
import { LineEdit } from '../../../../editor/common/core/edits/lineEdit.js';
8+
import { AnnotatedStringEdit, BaseStringEdit, IEditData, StringEdit } from '../../../../editor/common/core/edits/stringEdit.js';
9+
import { AbstractText } from '../../../../editor/common/core/text/abstractText.js';
910

1011
/**
1112
* The ARC (accepted and retained characters) counts how many characters inserted by the initial suggestion (trackedEdit)
1213
* stay unmodified after a certain amount of time after acceptance.
1314
*/
1415
export class ArcTracker {
16+
/** Invariant: applies to valueBeforeTrackedEdit. */
1517
private _updatedTrackedEdit: AnnotatedStringEdit<IsTrackedEditData>;
1618

1719
constructor(
@@ -24,8 +26,12 @@ export class ArcTracker {
2426

2527
handleEdits(edit: BaseStringEdit): void {
2628
const e = edit.mapData(_d => new IsTrackedEditData(false));
27-
const composedEdit = this._updatedTrackedEdit.compose(e);
28-
const onlyTrackedEdit = composedEdit.decomposeSplit(e => !e.data.isTrackedEdit).e2;
29+
const composedEdit = this._updatedTrackedEdit.compose(e); // (still) applies to valueBeforeTrackedEdit
30+
31+
// decomposeSplit computes e1 and e2 such that all replacements in e1 have the given property
32+
// and no replacement in e2 has the property, and such that e1.compose(e2).equals(composedEdit).
33+
// Thus, e1 applies to valueBeforeTrackedEdit.
34+
const onlyTrackedEdit = composedEdit.decomposeSplit(e => e.data.isTrackedEdit).e1;
2935
this._updatedTrackedEdit = onlyTrackedEdit;
3036
}
3137

@@ -51,6 +57,17 @@ export class ArcTracker {
5157
}))
5258
};
5359
}
60+
61+
public getLineCountInfo(): { deletedLineCounts: number; insertedLineCounts: number } {
62+
const e = this.getTrackedEdit();
63+
const le = LineEdit.fromStringEdit(e, this.valueBeforeTrackedEdit);
64+
const deletedLineCount = sumBy(le.replacements, r => r.lineRange.length);
65+
const insertedLineCount = sumBy(le.getNewLineRanges(), r => r.length);
66+
return {
67+
deletedLineCounts: deletedLineCount,
68+
insertedLineCounts: insertedLineCount,
69+
};
70+
}
5471
}
5572

5673
export class IsTrackedEditData implements IEditData<IsTrackedEditData> {
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
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+
6+
import assert from 'assert';
7+
import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js';
8+
import { StringText } from '../../../../../editor/common/core/text/abstractText.js';
9+
import { computeStringDiff } from '../../../../../editor/common/services/editorWebWorker.js';
10+
import { ArcTracker } from '../../common/arcTracker.js';
11+
12+
suite('Debug - AbstractDebugAdapter', () => {
13+
ensureNoDisposablesAreLeakedInTestSuite();
14+
15+
test('test1', () => {
16+
17+
const states = [
18+
`TODO: Add Charlie
19+
Alpha
20+
Bravo
21+
Delta`,
22+
`Alpha
23+
Bravo
24+
Delta
25+
Charlie`,
26+
`* Alpha
27+
* Bravo
28+
* Delta
29+
* Charlie`,
30+
`ICAO spelling alphabet:
31+
* Alpha
32+
* Bravo
33+
* Delta
34+
* Charlie`
35+
];
36+
37+
const edits = compareAdjacentItems(states, (a, b) => computeStringDiff(a, b, { maxComputationTimeMs: 0 }, 'advanced'));
38+
39+
const t = new ArcTracker(
40+
new StringText(states[0]),
41+
edits[0]
42+
);
43+
44+
const data: unknown[] = [];
45+
data.push(t.getLineCountInfo());
46+
47+
for (let i = 1; i < edits.length; i++) {
48+
t.handleEdits(edits[i]);
49+
data.push(t.getLineCountInfo());
50+
}
51+
assert.deepStrictEqual(data, ([
52+
{
53+
deletedLineCounts: 1,
54+
insertedLineCounts: 1
55+
},
56+
{
57+
deletedLineCounts: 0,
58+
insertedLineCounts: 1
59+
},
60+
{
61+
deletedLineCounts: 0,
62+
insertedLineCounts: 1
63+
}
64+
]));
65+
});
66+
});
67+
68+
function compareAdjacentItems<T, TResult>(arr: T[], comparator: (a: T, b: T) => TResult): TResult[] {
69+
const result: TResult[] = [];
70+
for (let i = 0; i < arr.length - 1; i++) {
71+
result.push(comparator(arr[i], arr[i + 1]));
72+
}
73+
return result;
74+
}

0 commit comments

Comments
 (0)