Skip to content

Commit 2c15f88

Browse files
committed
Have line mappings be computed in batches
1 parent 93fecc1 commit 2c15f88

4 files changed

Lines changed: 94 additions & 24 deletions

File tree

src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { WrappingIndent, IComputedEditorOptions, EditorOption } from 'vs/editor/
99
import { CharacterClassifier } from 'vs/editor/common/core/characterClassifier';
1010
import { toUint32Array } from 'vs/base/common/uint';
1111
import { PrefixSumComputer } from 'vs/editor/common/viewModel/prefixSumComputer';
12-
import { ILineMapperFactory, ILineMapping, OutputPosition } from 'vs/editor/common/viewModel/splitLinesCollection';
12+
import { ILineMapperFactory, ILineMapping, OutputPosition, ILineMappingComputer } from 'vs/editor/common/viewModel/splitLinesCollection';
1313

1414
const enum CharacterClass {
1515
NONE = 0,
@@ -82,7 +82,23 @@ export class CharacterHardWrappingLineMapperFactory implements ILineMapperFactor
8282
return currentVisibleColumn + columnSize;
8383
}
8484

85-
public createLineMapping(lineText: string, tabSize: number, breakingColumn: number, columnsForFullWidthChar: number, hardWrappingIndent: WrappingIndent): ILineMapping | null {
85+
public createLineMappingComputer(tabSize: number, wrappingColumn: number, columnsForFullWidthChar: number, wrappingIndent: WrappingIndent): ILineMappingComputer {
86+
let requests: string[] = [];
87+
return {
88+
addRequest: (lineText: string) => {
89+
requests.push(lineText);
90+
},
91+
finalize: () => {
92+
let result: (ILineMapping | null)[] = [];
93+
for (let i = 0, len = requests.length; i < len; i++) {
94+
result[i] = this._createLineMapping(requests[i], tabSize, wrappingColumn, columnsForFullWidthChar, wrappingIndent);
95+
}
96+
return result;
97+
}
98+
};
99+
}
100+
101+
private _createLineMapping(lineText: string, tabSize: number, breakingColumn: number, columnsForFullWidthChar: number, hardWrappingIndent: WrappingIndent): ILineMapping | null {
86102
if (breakingColumn === -1) {
87103
return null;
88104
}

src/vs/editor/common/viewModel/splitLinesCollection.ts

Lines changed: 44 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,13 @@ export interface ILineMapping {
3333
getOutputPositionOfInputOffset(inputOffset: number): OutputPosition;
3434
}
3535

36+
export interface ILineMappingComputer {
37+
addRequest(lineText: string): void;
38+
finalize(): (ILineMapping | null)[];
39+
}
40+
3641
export interface ILineMapperFactory {
37-
createLineMapping(lineText: string, tabSize: number, wrappingColumn: number, columnsForFullWidthChar: number, wrappingIndent: WrappingIndent): ILineMapping | null;
42+
createLineMappingComputer(tabSize: number, wrappingColumn: number, columnsForFullWidthChar: number, wrappingIndent: WrappingIndent): ILineMappingComputer;
3843
}
3944

4045
export interface ISimpleModel {
@@ -71,10 +76,11 @@ export interface IViewModelLinesCollection extends IDisposable {
7176
getHiddenAreas(): Range[];
7277
setHiddenAreas(_ranges: Range[]): boolean;
7378

79+
createLineMappingComputer(): ILineMappingComputer;
7480
onModelFlushed(): void;
7581
onModelLinesDeleted(versionId: number, fromLineNumber: number, toLineNumber: number): viewEvents.ViewLinesDeletedEvent | null;
76-
onModelLinesInserted(versionId: number, fromLineNumber: number, toLineNumber: number, text: string[]): viewEvents.ViewLinesInsertedEvent | null;
77-
onModelLineChanged(versionId: number, lineNumber: number, newText: string): [boolean, viewEvents.ViewLinesChangedEvent | null, viewEvents.ViewLinesInsertedEvent | null, viewEvents.ViewLinesDeletedEvent | null];
82+
onModelLinesInserted(versionId: number, fromLineNumber: number, toLineNumber: number, linesMappings: (ILineMapping | null)[]): viewEvents.ViewLinesInsertedEvent | null;
83+
onModelLineChanged(versionId: number, lineNumber: number, lineMapping: ILineMapping | null): [boolean, viewEvents.ViewLinesChangedEvent | null, viewEvents.ViewLinesInsertedEvent | null, viewEvents.ViewLinesDeletedEvent | null];
7884
acceptVersionId(versionId: number): void;
7985

8086
getViewLineCount(): number;
@@ -201,7 +207,13 @@ export class SplitLinesCollection implements IViewModelLinesCollection {
201207
}
202208

203209
let linesContent = this.model.getLinesContent();
204-
let lineCount = linesContent.length;
210+
const lineCount = linesContent.length;
211+
const lineMappingComputer = this.linePositionMapperFactory.createLineMappingComputer(this.tabSize, this.wrappingColumn, this.columnsForFullWidthChar, this.wrappingIndent);
212+
for (let i = 0; i < lineCount; i++) {
213+
lineMappingComputer.addRequest(linesContent[i]);
214+
}
215+
const lineMappings = lineMappingComputer.finalize();
216+
205217
let values = new Uint32Array(lineCount);
206218

207219
let hiddenAreas = this.hiddenAreasIds.map((areaId) => this.model.getDecorationRange(areaId)!).sort(Range.compareRangesUsingStarts);
@@ -220,7 +232,7 @@ export class SplitLinesCollection implements IViewModelLinesCollection {
220232
}
221233

222234
let isInHiddenArea = (lineNumber >= hiddenAreaStart && lineNumber <= hiddenAreaEnd);
223-
let line = createSplitLine(this.linePositionMapperFactory, linesContent[i], this.tabSize, this.wrappingColumn, this.columnsForFullWidthChar, this.wrappingIndent, !isInHiddenArea);
235+
let line = createSplitLine(lineMappings[i], !isInHiddenArea);
224236
values[i] = line.getViewLineCount();
225237
this.lines[i] = line;
226238
}
@@ -370,6 +382,10 @@ export class SplitLinesCollection implements IViewModelLinesCollection {
370382
return true;
371383
}
372384

385+
public createLineMappingComputer(): ILineMappingComputer {
386+
return this.linePositionMapperFactory.createLineMappingComputer(this.tabSize, this.wrappingColumn, this.columnsForFullWidthChar, this.wrappingIndent);
387+
}
388+
373389
public onModelFlushed(): void {
374390
this._constructLines(true);
375391
}
@@ -390,7 +406,7 @@ export class SplitLinesCollection implements IViewModelLinesCollection {
390406
return new viewEvents.ViewLinesDeletedEvent(outputFromLineNumber, outputToLineNumber);
391407
}
392408

393-
public onModelLinesInserted(versionId: number, fromLineNumber: number, _toLineNumber: number, text: string[]): viewEvents.ViewLinesInsertedEvent | null {
409+
public onModelLinesInserted(versionId: number, fromLineNumber: number, _toLineNumber: number, linesMappings: (ILineMapping | null)[]): viewEvents.ViewLinesInsertedEvent | null {
394410
if (versionId <= this._validModelVersionId) {
395411
// Here we check for versionId in case the lines were reconstructed in the meantime.
396412
// We don't want to apply stale change events on top of a newer read model state.
@@ -411,10 +427,10 @@ export class SplitLinesCollection implements IViewModelLinesCollection {
411427

412428
let totalOutputLineCount = 0;
413429
let insertLines: ISplitLine[] = [];
414-
let insertPrefixSumValues = new Uint32Array(text.length);
430+
let insertPrefixSumValues = new Uint32Array(linesMappings.length);
415431

416-
for (let i = 0, len = text.length; i < len; i++) {
417-
let line = createSplitLine(this.linePositionMapperFactory, text[i], this.tabSize, this.wrappingColumn, this.columnsForFullWidthChar, this.wrappingIndent, !isInHiddenArea);
432+
for (let i = 0, len = linesMappings.length; i < len; i++) {
433+
let line = createSplitLine(linesMappings[i], !isInHiddenArea);
418434
insertLines.push(line);
419435

420436
let outputLineCount = line.getViewLineCount();
@@ -430,7 +446,7 @@ export class SplitLinesCollection implements IViewModelLinesCollection {
430446
return new viewEvents.ViewLinesInsertedEvent(outputFromLineNumber, outputFromLineNumber + totalOutputLineCount - 1);
431447
}
432448

433-
public onModelLineChanged(versionId: number, lineNumber: number, newText: string): [boolean, viewEvents.ViewLinesChangedEvent | null, viewEvents.ViewLinesInsertedEvent | null, viewEvents.ViewLinesDeletedEvent | null] {
449+
public onModelLineChanged(versionId: number, lineNumber: number, lineMapping: ILineMapping | null): [boolean, viewEvents.ViewLinesChangedEvent | null, viewEvents.ViewLinesInsertedEvent | null, viewEvents.ViewLinesDeletedEvent | null] {
434450
if (versionId <= this._validModelVersionId) {
435451
// Here we check for versionId in case the lines were reconstructed in the meantime.
436452
// We don't want to apply stale change events on top of a newer read model state.
@@ -441,7 +457,7 @@ export class SplitLinesCollection implements IViewModelLinesCollection {
441457

442458
let oldOutputLineCount = this.lines[lineIndex].getViewLineCount();
443459
let isVisible = this.lines[lineIndex].isVisible();
444-
let line = createSplitLine(this.linePositionMapperFactory, newText, this.tabSize, this.wrappingColumn, this.columnsForFullWidthChar, this.wrappingIndent, isVisible);
460+
let line = createSplitLine(lineMapping, isVisible);
445461
this.lines[lineIndex] = line;
446462
let newOutputLineCount = this.lines[lineIndex].getViewLineCount();
447463

@@ -1199,16 +1215,15 @@ export class SplitLine implements ISplitLine {
11991215
}
12001216
}
12011217

1202-
function createSplitLine(linePositionMapperFactory: ILineMapperFactory, text: string, tabSize: number, wrappingColumn: number, columnsForFullWidthChar: number, wrappingIndent: WrappingIndent, isVisible: boolean): ISplitLine {
1203-
let positionMapper = linePositionMapperFactory.createLineMapping(text, tabSize, wrappingColumn, columnsForFullWidthChar, wrappingIndent);
1204-
if (positionMapper === null) {
1218+
function createSplitLine(lineMapping: ILineMapping | null, isVisible: boolean): ISplitLine {
1219+
if (lineMapping === null) {
12051220
// No mapping needed
12061221
if (isVisible) {
12071222
return VisibleIdentitySplitLine.INSTANCE;
12081223
}
12091224
return InvisibleIdentitySplitLine.INSTANCE;
12101225
} else {
1211-
return new SplitLine(positionMapper, isVisible);
1226+
return new SplitLine(lineMapping, isVisible);
12121227
}
12131228
}
12141229

@@ -1298,18 +1313,30 @@ export class IdentityLinesCollection implements IViewModelLinesCollection {
12981313
return false;
12991314
}
13001315

1316+
public createLineMappingComputer(): ILineMappingComputer {
1317+
let result: null[] = [];
1318+
return {
1319+
addRequest: (lineText: string) => {
1320+
result.push(null);
1321+
},
1322+
finalize: () => {
1323+
return result;
1324+
}
1325+
};
1326+
}
1327+
13011328
public onModelFlushed(): void {
13021329
}
13031330

13041331
public onModelLinesDeleted(_versionId: number, fromLineNumber: number, toLineNumber: number): viewEvents.ViewLinesDeletedEvent | null {
13051332
return new viewEvents.ViewLinesDeletedEvent(fromLineNumber, toLineNumber);
13061333
}
13071334

1308-
public onModelLinesInserted(_versionId: number, fromLineNumber: number, toLineNumber: number, _text: string[]): viewEvents.ViewLinesInsertedEvent | null {
1335+
public onModelLinesInserted(_versionId: number, fromLineNumber: number, toLineNumber: number, linesMappings: (ILineMapping | null)[]): viewEvents.ViewLinesInsertedEvent | null {
13091336
return new viewEvents.ViewLinesInsertedEvent(fromLineNumber, toLineNumber);
13101337
}
13111338

1312-
public onModelLineChanged(_versionId: number, lineNumber: number, _newText: string): [boolean, viewEvents.ViewLinesChangedEvent | null, viewEvents.ViewLinesInsertedEvent | null, viewEvents.ViewLinesDeletedEvent | null] {
1339+
public onModelLineChanged(_versionId: number, lineNumber: number, lineMapping: ILineMapping | null): [boolean, viewEvents.ViewLinesChangedEvent | null, viewEvents.ViewLinesInsertedEvent | null, viewEvents.ViewLinesDeletedEvent | null] {
13131340
return [false, new viewEvents.ViewLinesChangedEvent(lineNumber, lineNumber), null, null];
13141341
}
13151342

src/vs/editor/common/viewModel/viewModelImpl.ts

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -196,8 +196,26 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
196196
const changes = e.changes;
197197
const versionId = e.versionId;
198198

199-
for (let j = 0, lenJ = changes.length; j < lenJ; j++) {
200-
const change = changes[j];
199+
// Do a first pass to compute line mappings, and a second pass to actually interpret them
200+
const lineMappingComputer = this.lines.createLineMappingComputer();
201+
for (const change of changes) {
202+
switch (change.changeType) {
203+
case textModelEvents.RawContentChangedType.LinesInserted: {
204+
for (const line of change.detail) {
205+
lineMappingComputer.addRequest(line);
206+
}
207+
break;
208+
}
209+
case textModelEvents.RawContentChangedType.LineChanged: {
210+
lineMappingComputer.addRequest(change.detail);
211+
break;
212+
}
213+
}
214+
}
215+
const lineMappings = lineMappingComputer.finalize();
216+
let lineMappingsOffset = 0;
217+
218+
for (const change of changes) {
201219

202220
switch (change.changeType) {
203221
case textModelEvents.RawContentChangedType.Flush: {
@@ -218,7 +236,10 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
218236
break;
219237
}
220238
case textModelEvents.RawContentChangedType.LinesInserted: {
221-
const linesInsertedEvent = this.lines.onModelLinesInserted(versionId, change.fromLineNumber, change.toLineNumber, change.detail);
239+
const insertedLinesMappings = lineMappings.slice(lineMappingsOffset, lineMappingsOffset + change.detail.length);
240+
lineMappingsOffset += change.detail.length;
241+
242+
const linesInsertedEvent = this.lines.onModelLinesInserted(versionId, change.fromLineNumber, change.toLineNumber, insertedLinesMappings);
222243
if (linesInsertedEvent !== null) {
223244
eventsCollector.emit(linesInsertedEvent);
224245
this.viewLayout.onLinesInserted(linesInsertedEvent.fromLineNumber, linesInsertedEvent.toLineNumber);
@@ -227,7 +248,10 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
227248
break;
228249
}
229250
case textModelEvents.RawContentChangedType.LineChanged: {
230-
const [lineMappingChanged, linesChangedEvent, linesInsertedEvent, linesDeletedEvent] = this.lines.onModelLineChanged(versionId, change.lineNumber, change.detail);
251+
const changedLineMapping = lineMappings[lineMappingsOffset];
252+
lineMappingsOffset++;
253+
254+
const [lineMappingChanged, linesChangedEvent, linesInsertedEvent, linesDeletedEvent] = this.lines.onModelLineChanged(versionId, change.lineNumber, changedLineMapping);
231255
hadModelLineChangeThatChangedLineMapping = lineMappingChanged;
232256
if (linesChangedEvent) {
233257
eventsCollector.emit(linesChangedEvent);

src/vs/editor/test/common/viewModel/characterHardWrappingLineMapper.test.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,10 @@ function assertLineMapping(factory: ILineMapperFactory, tabSize: number, breakAf
2121
}
2222
}
2323

24-
const mapper = factory.createLineMapping(rawText, tabSize, breakAfter, 2, wrappingIndent);
24+
const lineMappingComputer = factory.createLineMappingComputer(tabSize, breakAfter, 2, wrappingIndent);
25+
lineMappingComputer.addRequest(rawText);
26+
const lineMappings = lineMappingComputer.finalize();
27+
const mapper = lineMappings[0];
2528

2629
// Insert line break markers again, according to algorithm
2730
let actualAnnotatedText = '';

0 commit comments

Comments
 (0)