Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/copilot/src/extension/inlineEdits/common/informationDelta.tsx
13399 views
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 { StringEdit } from '../../../util/vs/editor/common/core/edits/stringEdit';
7
import { OffsetRange } from '../../../util/vs/editor/common/core/ranges/offsetRange';
8
9
const N_GRAM_UNDO_RATIO_TO_FILTER_OUT = 0.7;
10
11
/**
12
* Represents information loss/gain (4-grams) via an edit.
13
*/
14
export class InformationDelta {
15
16
constructor(
17
public readonly inserted: Set<string> = new Set<string>(),
18
public readonly deleted: Set<string> = new Set<string>()
19
) { }
20
21
combine(other: InformationDelta) {
22
return new InformationDelta(setUnion(this.inserted, other.inserted), setUnion(this.deleted, other.deleted));
23
}
24
25
isUndoneBy(other: InformationDelta) {
26
const otherReallyNewInsertions = setMinus(other.inserted, other.deleted);
27
const otherReallyDeleted = setMinus(other.deleted, other.inserted);
28
29
const otherReallyDeletesMyInserts = setIntersectionCount(otherReallyDeleted, this.inserted);
30
const otherReallyInsertsMyDeletes = setIntersectionCount(otherReallyNewInsertions, this.deleted);
31
32
if (otherReallyDeleted.size > 6 && otherReallyDeletesMyInserts / otherReallyDeleted.size > N_GRAM_UNDO_RATIO_TO_FILTER_OUT) {
33
return true;
34
}
35
36
if (otherReallyNewInsertions.size > 6 && otherReallyInsertsMyDeletes / otherReallyNewInsertions.size > N_GRAM_UNDO_RATIO_TO_FILTER_OUT) {
37
return true;
38
}
39
40
return false;
41
}
42
}
43
44
export function getInformationDelta(source: string, edit: StringEdit): InformationDelta {
45
const inserted = new Set<string>();
46
const deleted = new Set<string>();
47
const tryAddDeleted = (deletedRange: OffsetRange | undefined) => {
48
if (!deletedRange) {
49
return;
50
}
51
const deletedText = source.substring(deletedRange.start, deletedRange.endExclusive);
52
for (let line of deletedText.split(/\r\n|\r|\n/)) {
53
line = line.trim();
54
for (const piece of to4grams(line)) {
55
deleted.add(piece);
56
}
57
}
58
};
59
const tryAddInserted = (insertedText: string) => {
60
for (let line of insertedText.split(/\r\n|\r|\n/)) {
61
line = line.trim();
62
for (const piece of to4grams(line)) {
63
inserted.add(piece);
64
}
65
}
66
};
67
for (const e of edit.replacements) {
68
const e1 = e.removeCommonPrefix(source).removeCommonSuffix(source);
69
const e2 = e.removeCommonSuffix(source).removeCommonPrefix(source);
70
if (e1.isEmpty) {
71
continue;
72
}
73
tryAddDeleted(e1.replaceRange);
74
tryAddDeleted(e2.replaceRange);
75
tryAddDeleted(e1.replaceRange.intersect(e2.replaceRange));
76
77
// tryAddInserted(e1.newText);
78
// tryAddInserted(e2.newText);
79
// e1 might have a suffix overlap with the prefix of e1
80
tryAddInserted(trimOverlap(e1.newText, e2.newText));
81
}
82
return new InformationDelta(inserted, deleted);
83
}
84
85
function trimOverlap(stringToEliminateEnd: string, stringToEliminateStart: string): string {
86
const length = Math.min(stringToEliminateEnd.length, stringToEliminateStart.length);
87
for (let trimLength = 0; trimLength < length; trimLength++) {
88
const str1 = stringToEliminateEnd.slice(0, stringToEliminateEnd.length - trimLength);
89
const str2 = stringToEliminateStart.slice(trimLength);
90
if (str1 === str2) {
91
return str1;
92
}
93
}
94
return '';
95
}
96
97
function to4grams(text: string) {
98
const result: string[] = [];
99
for (let i = 4; i < text.length; i++) {
100
const ngram = text.slice(i - 4, i);
101
result.push(ngram);
102
}
103
return result;
104
}
105
106
function setUnion(a: Set<string>, b: Set<string>): Set<string> {
107
const result = new Set<string>();
108
for (const el of a) {
109
result.add(el);
110
}
111
for (const el of b) {
112
result.add(el);
113
}
114
return result;
115
}
116
117
function setMinus(a: Set<string>, b: Set<string>): Set<string> {
118
const result = new Set<string>();
119
for (const el of a) {
120
if (!b.has(el)) {
121
result.add(el);
122
}
123
}
124
return result;
125
}
126
127
function setIntersectionCount(a: Set<string>, b: Set<string>): number {
128
let result = 0;
129
for (const el of a) {
130
if (b.has(el)) {
131
result++;
132
}
133
}
134
return result;
135
}
136
137