Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/copilot/src/extension/prompts/node/inline/summarizedDocument/fragments.ts
13406 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 { Lazy } from '../../../../../util/vs/base/common/lazy';
7
import { StringEdit, StringReplacement } from '../../../../../util/vs/editor/common/core/edits/stringEdit';
8
import { OffsetRange } from '../../../../../util/vs/editor/common/core/ranges/offsetRange';
9
import { TextLength } from '../../../../../util/vs/editor/common/core/text/textLength';
10
11
export abstract class StringFragment {
12
abstract get length(): number;
13
14
abstract get textLength(): TextLength;
15
16
abstract get text(): string;
17
18
toString() { return this.text; }
19
20
toEditFromOriginal(originalLength: number): StringEdit {
21
const replacements: StringReplacement[] = [];
22
let lastOriginalIdx = 0;
23
let text = '';
24
25
function emit(originalPos: number) {
26
if (lastOriginalIdx !== originalPos || text.length > 0) {
27
replacements.push(new StringReplacement(
28
new OffsetRange(lastOriginalIdx, originalPos),
29
text
30
));
31
text = '';
32
}
33
}
34
35
function process(fragment: StringFragment) {
36
if (fragment instanceof ConcatenatedStringFragment) {
37
for (const f of fragment.fragments) {
38
process(f);
39
}
40
} else if (fragment instanceof LiteralStringFragment) {
41
text += fragment.text;
42
} else if (fragment instanceof OriginalStringFragment) {
43
emit(fragment.range.start);
44
lastOriginalIdx = fragment.range.endExclusive;
45
}
46
}
47
48
process(this);
49
emit(originalLength);
50
return new StringEdit(replacements);
51
}
52
}
53
54
export class LiteralStringFragment extends StringFragment {
55
constructor(
56
public readonly text: string
57
) {
58
super();
59
}
60
61
get length(): number { return this.text.length; }
62
63
private readonly _textLength = new Lazy(() => TextLength.ofText(this.text));
64
65
get textLength() { return this._textLength.value; }
66
}
67
68
export class OriginalStringFragment extends StringFragment {
69
constructor(
70
public readonly range: OffsetRange,
71
public readonly originalText: string
72
) {
73
super();
74
}
75
76
get length(): number { return this.range.length; }
77
78
get text(): string { return this.range.substring(this.originalText); }
79
80
trimStart(): OriginalStringFragment {
81
const trimmed = this.text.trimStart();
82
if (trimmed.length === this.length) { return this; }
83
return new OriginalStringFragment(new OffsetRange(this.range.endExclusive - trimmed.length, this.range.endExclusive), this.originalText);
84
}
85
86
trimEnd(): OriginalStringFragment {
87
const trimmed = this.text.trimEnd();
88
if (trimmed.length === this.length) { return this; }
89
return new OriginalStringFragment(new OffsetRange(this.range.start, this.range.start + trimmed.length), this.originalText);
90
}
91
92
startsWith(str: string): boolean { return this.text.startsWith(str); }
93
endsWith(str: string): boolean { return this.text.endsWith(str); }
94
95
tryJoin(other: OriginalStringFragment): OriginalStringFragment | null {
96
if (this.range.endExclusive === other.range.start) {
97
return new OriginalStringFragment(new OffsetRange(this.range.start, other.range.endExclusive), this.originalText);
98
}
99
return null;
100
}
101
102
private readonly _textLength = new Lazy(() => TextLength.ofSubstr(this.originalText, this.range));
103
104
get textLength() { return this._textLength.value; }
105
}
106
107
export class ConcatenatedStringFragment extends StringFragment {
108
static from(result: StringFragment[]): StringFragment {
109
if (result.length === 0) {
110
return new LiteralStringFragment('');
111
}
112
if (result.length === 1) {
113
return result[0];
114
}
115
return new ConcatenatedStringFragment(result);
116
}
117
118
readonly length = this.fragments.reduce((prev, cur) => prev + cur.length, 0);
119
120
constructor(
121
public readonly fragments: readonly StringFragment[]
122
) {
123
super();
124
}
125
126
get text(): string {
127
return this.fragments.map(f => f.text).join('');
128
}
129
130
private readonly _textLength = new Lazy(() => TextLength.sum(this.fragments, f => f.textLength));
131
132
get textLength() { return this._textLength.value; }
133
}
134
135
export function pushFragment(fragments: StringFragment[], fragment: StringFragment): void {
136
if (fragment.length === 0) { return; }
137
const last = fragments[fragments.length - 1];
138
if (last && last instanceof OriginalStringFragment && fragment instanceof OriginalStringFragment) {
139
const joined = last.tryJoin(fragment);
140
if (joined) {
141
fragments[fragments.length - 1] = joined;
142
return;
143
}
144
}
145
fragments.push(fragment);
146
}
147
148