Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/editor/common/model/mirrorTextModel.ts
3294 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 { splitLines } from '../../../base/common/strings.js';
7
import { URI } from '../../../base/common/uri.js';
8
import { Position } from '../core/position.js';
9
import { IRange } from '../core/range.js';
10
import { PrefixSumComputer } from './prefixSumComputer.js';
11
12
export interface IModelContentChange {
13
/**
14
* The old range that got replaced.
15
*/
16
readonly range: IRange;
17
/**
18
* The offset of the range that got replaced.
19
*/
20
readonly rangeOffset: number;
21
/**
22
* The length of the range that got replaced.
23
*/
24
readonly rangeLength: number;
25
/**
26
* The new text for the range.
27
*/
28
readonly text: string;
29
}
30
31
export interface IModelChangedEvent {
32
/**
33
* The actual changes.
34
*/
35
readonly changes: IModelContentChange[];
36
/**
37
* The (new) end-of-line character.
38
*/
39
readonly eol: string;
40
/**
41
* The new version id the model has transitioned to.
42
*/
43
readonly versionId: number;
44
/**
45
* Flag that indicates that this event was generated while undoing.
46
*/
47
readonly isUndoing: boolean;
48
/**
49
* Flag that indicates that this event was generated while redoing.
50
*/
51
readonly isRedoing: boolean;
52
}
53
54
export interface IMirrorTextModel {
55
readonly version: number;
56
}
57
58
export class MirrorTextModel implements IMirrorTextModel {
59
60
protected _uri: URI;
61
protected _lines: string[];
62
protected _eol: string;
63
protected _versionId: number;
64
protected _lineStarts: PrefixSumComputer | null;
65
private _cachedTextValue: string | null;
66
67
constructor(uri: URI, lines: string[], eol: string, versionId: number) {
68
this._uri = uri;
69
this._lines = lines;
70
this._eol = eol;
71
this._versionId = versionId;
72
this._lineStarts = null;
73
this._cachedTextValue = null;
74
}
75
76
dispose(): void {
77
this._lines.length = 0;
78
}
79
80
get version(): number {
81
return this._versionId;
82
}
83
84
getText(): string {
85
if (this._cachedTextValue === null) {
86
this._cachedTextValue = this._lines.join(this._eol);
87
}
88
return this._cachedTextValue;
89
}
90
91
onEvents(e: IModelChangedEvent): void {
92
if (e.eol && e.eol !== this._eol) {
93
this._eol = e.eol;
94
this._lineStarts = null;
95
}
96
97
// Update my lines
98
const changes = e.changes;
99
for (const change of changes) {
100
this._acceptDeleteRange(change.range);
101
this._acceptInsertText(new Position(change.range.startLineNumber, change.range.startColumn), change.text);
102
}
103
104
this._versionId = e.versionId;
105
this._cachedTextValue = null;
106
}
107
108
protected _ensureLineStarts(): void {
109
if (!this._lineStarts) {
110
const eolLength = this._eol.length;
111
const linesLength = this._lines.length;
112
const lineStartValues = new Uint32Array(linesLength);
113
for (let i = 0; i < linesLength; i++) {
114
lineStartValues[i] = this._lines[i].length + eolLength;
115
}
116
this._lineStarts = new PrefixSumComputer(lineStartValues);
117
}
118
}
119
120
/**
121
* All changes to a line's text go through this method
122
*/
123
private _setLineText(lineIndex: number, newValue: string): void {
124
this._lines[lineIndex] = newValue;
125
if (this._lineStarts) {
126
// update prefix sum
127
this._lineStarts.setValue(lineIndex, this._lines[lineIndex].length + this._eol.length);
128
}
129
}
130
131
private _acceptDeleteRange(range: IRange): void {
132
133
if (range.startLineNumber === range.endLineNumber) {
134
if (range.startColumn === range.endColumn) {
135
// Nothing to delete
136
return;
137
}
138
// Delete text on the affected line
139
this._setLineText(range.startLineNumber - 1,
140
this._lines[range.startLineNumber - 1].substring(0, range.startColumn - 1)
141
+ this._lines[range.startLineNumber - 1].substring(range.endColumn - 1)
142
);
143
return;
144
}
145
146
// Take remaining text on last line and append it to remaining text on first line
147
this._setLineText(range.startLineNumber - 1,
148
this._lines[range.startLineNumber - 1].substring(0, range.startColumn - 1)
149
+ this._lines[range.endLineNumber - 1].substring(range.endColumn - 1)
150
);
151
152
// Delete middle lines
153
this._lines.splice(range.startLineNumber, range.endLineNumber - range.startLineNumber);
154
if (this._lineStarts) {
155
// update prefix sum
156
this._lineStarts.removeValues(range.startLineNumber, range.endLineNumber - range.startLineNumber);
157
}
158
}
159
160
private _acceptInsertText(position: Position, insertText: string): void {
161
if (insertText.length === 0) {
162
// Nothing to insert
163
return;
164
}
165
const insertLines = splitLines(insertText);
166
if (insertLines.length === 1) {
167
// Inserting text on one line
168
this._setLineText(position.lineNumber - 1,
169
this._lines[position.lineNumber - 1].substring(0, position.column - 1)
170
+ insertLines[0]
171
+ this._lines[position.lineNumber - 1].substring(position.column - 1)
172
);
173
return;
174
}
175
176
// Append overflowing text from first line to the end of text to insert
177
insertLines[insertLines.length - 1] += this._lines[position.lineNumber - 1].substring(position.column - 1);
178
179
// Delete overflowing text from first line and insert text on first line
180
this._setLineText(position.lineNumber - 1,
181
this._lines[position.lineNumber - 1].substring(0, position.column - 1)
182
+ insertLines[0]
183
);
184
185
// Insert new lines & store lengths
186
const newLengths = new Uint32Array(insertLines.length - 1);
187
for (let i = 1; i < insertLines.length; i++) {
188
this._lines.splice(position.lineNumber + i - 1, 0, insertLines[i]);
189
newLengths[i - 1] = insertLines[i].length + this._eol.length;
190
}
191
192
if (this._lineStarts) {
193
// update prefix sum
194
this._lineStarts.insertValues(position.lineNumber, newLengths);
195
}
196
}
197
}
198
199