Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/copilot/src/platform/notebook/common/alternativeNotebookDocument.ts
13401 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 type { NotebookCell, NotebookDocument, TextLine } from 'vscode';
7
import { DEFAULT_WORD_REGEXP, getWordAtText } from '../../../util/vs/editor/common/core/wordHelper';
8
import { Position, Range } from '../../../vscodeTypes';
9
import { PositionOffsetTransformer } from '../../editing/common/positionOffsetTransformer';
10
import { SnapshotDocumentLine } from '../../editing/common/textDocumentSnapshot';
11
12
13
export abstract class AlternativeNotebookDocument {
14
private _transformer: PositionOffsetTransformer | null = null;
15
private get transformer(): PositionOffsetTransformer {
16
if (!this._transformer) {
17
this._transformer = new PositionOffsetTransformer(this._text);
18
}
19
return this._transformer;
20
}
21
22
getText(range?: Range): string {
23
return range ? this._getTextInRange(range) : this._text;
24
}
25
26
private _getTextInRange(_range: Range): string {
27
const range = this.validateRange(_range);
28
29
if (range.isEmpty) {
30
return '';
31
}
32
33
const offsetRange = this.transformer.toOffsetRange(range);
34
return this._text.substring(offsetRange.start, offsetRange.endExclusive);
35
}
36
37
constructor(protected readonly _text: string, protected readonly notebook: NotebookDocument) {
38
39
}
40
41
protected positionToOffset(position: Position): number {
42
position = this.validatePosition(position);
43
return this.transformer.getOffset(position);
44
}
45
46
/**
47
* Translates a position in the notebook document to the corresponding alternative position.
48
*/
49
abstract fromCellPosition(cell: NotebookCell, position: Position): Position;
50
51
/**
52
* Translates a position in the alternative document to the corresponding cell index and position in the notebook document.
53
*/
54
abstract toCellPosition(position: Position): { cell: NotebookCell; position: Position } | undefined;
55
56
getWordRangeAtPosition(_position: Position): Range | undefined {
57
const position = this.validatePosition(_position);
58
59
const wordAtText = getWordAtText(
60
position.character + 1,
61
DEFAULT_WORD_REGEXP,
62
this.lines[position.line],
63
0
64
);
65
66
if (wordAtText) {
67
return new Range(position.line, wordAtText.startColumn - 1, position.line, wordAtText.endColumn - 1);
68
}
69
return undefined;
70
}
71
72
73
private _lines: string[] | null = null;
74
75
get lines(): string[] {
76
if (!this._lines) {
77
this._lines = this._text.split(/\r\n|\r|\n/g);
78
}
79
return this._lines;
80
}
81
82
get lineCount(): number {
83
return this.lines.length;
84
}
85
86
lineAt(line: number): TextLine;
87
lineAt(position: Position): TextLine;
88
lineAt(lineOrPosition: number | Position): TextLine {
89
let line: number | undefined;
90
if (lineOrPosition instanceof Position) {
91
line = lineOrPosition.line;
92
} else if (typeof lineOrPosition === 'number') {
93
line = lineOrPosition;
94
} else {
95
throw new Error(`Invalid argument`);
96
}
97
if (line < 0 || line >= this.lines.length) {
98
throw new Error('Illegal value for `line`');
99
}
100
101
return new SnapshotDocumentLine(line, this.lines[line], line === this.lines.length - 1);
102
}
103
offsetAt(position: Position): number {
104
return this.transformer.getOffset(position);
105
}
106
107
positionAt(offset: number): Position {
108
offset = Math.floor(offset);
109
offset = Math.max(0, offset);
110
111
return this.transformer.getPosition(offset);
112
}
113
validateRange(range: Range): Range {
114
const start = this.validatePosition(range.start);
115
const end = this.validatePosition(range.end);
116
117
if (start === range.start && end === range.end) {
118
return range;
119
}
120
return new Range(start.line, start.character, end.line, end.character);
121
}
122
123
validatePosition(position: Position): Position {
124
if (this._text.length === 0) {
125
return position.with(0, 0);
126
}
127
128
let { line, character } = position;
129
let hasChanged = false;
130
131
if (line < 0) {
132
line = 0;
133
character = 0;
134
hasChanged = true;
135
} else if (line >= this.lines.length) {
136
line = this.lines.length - 1;
137
character = this.lines[line].length;
138
hasChanged = true;
139
} else {
140
const maxCharacter = this.lines[line].length;
141
if (character < 0) {
142
character = 0;
143
hasChanged = true;
144
} else if (character > maxCharacter) {
145
character = maxCharacter;
146
hasChanged = true;
147
}
148
}
149
150
if (!hasChanged) {
151
return position;
152
}
153
return new Position(line, character);
154
}
155
}
156
157