mirror of
https://github.com/microsoft/vscode.git
synced 2026-04-21 09:08:53 +01:00
don't duplicate lines, only keep prefix sums
This commit is contained in:
@@ -9,29 +9,23 @@ import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { ExtHostNotebookDocument, ExtHostNotebookController, ExtHostCell } from 'vs/workbench/api/common/extHostNotebook';
|
||||
import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments';
|
||||
import { PrefixSumComputer } from 'vs/editor/common/viewModel/prefixSumComputer';
|
||||
import { ExtHostDocumentData } from 'vs/workbench/api/common/extHostDocumentData';
|
||||
import { Range, LanguageSelector } from 'vs/workbench/api/common/extHostTypeConverters';
|
||||
import { DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { score } from 'vs/editor/common/modes/languageSelector';
|
||||
import { CellKind } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { NotImplementedProxy } from 'vs/base/common/types';
|
||||
import { MainThreadDocumentsShape } from 'vs/workbench/api/common/extHost.protocol';
|
||||
|
||||
|
||||
//todo@jrieken ConcatDiagnosticsCollection...
|
||||
import { isEqual } from 'vs/base/common/resources';
|
||||
|
||||
export class ExtHostNotebookConcatDocument implements vscode.NotebookConcatTextDocument {
|
||||
|
||||
private _disposables = new DisposableStore();
|
||||
|
||||
private _cells!: ExtHostCell[];
|
||||
private _cellLengths!: PrefixSumComputer;
|
||||
private _cellLines!: PrefixSumComputer;
|
||||
private _versionId = 0;
|
||||
|
||||
private readonly _onDidChange = new Emitter<this>();
|
||||
readonly onDidChange: Event<this> = this._onDidChange.event;
|
||||
|
||||
private _versionId = 0;
|
||||
private _delegate!: ExtHostDocumentData;
|
||||
private _selectedCells!: ExtHostCell[];
|
||||
private _cellStarts!: PrefixSumComputer;
|
||||
|
||||
constructor(
|
||||
extHostNotebooks: ExtHostNotebookController,
|
||||
extHostDocuments: ExtHostDocuments,
|
||||
@@ -40,133 +34,119 @@ export class ExtHostNotebookConcatDocument implements vscode.NotebookConcatTextD
|
||||
) {
|
||||
this._init();
|
||||
|
||||
extHostDocuments.onDidChangeDocument(e => {
|
||||
const cellIdx = this._selectedCells.findIndex(candidate => candidate.uri.toString() === e.document.uri.toString());
|
||||
if (cellIdx < 0) {
|
||||
return;
|
||||
this._disposables.add(extHostDocuments.onDidChangeDocument(e => {
|
||||
let cellIdx = this._cells.findIndex(cell => isEqual(cell.uri, e.document.uri));
|
||||
if (cellIdx >= 0) {
|
||||
this._cellLengths.changeValue(cellIdx, this._cells[cellIdx].document.getText().length + 1);
|
||||
this._cellLines.changeValue(cellIdx, this._cells[cellIdx].document.lineCount);
|
||||
this._versionId += 1;
|
||||
this._onDidChange.fire(this);
|
||||
}
|
||||
// todo@jrieken reuse raw event!
|
||||
this._versionId += 1;
|
||||
this._delegate.onEvents({
|
||||
versionId: this._versionId,
|
||||
eol: '\n',
|
||||
changes: e.contentChanges.map(change => {
|
||||
return {
|
||||
range: Range.from(change.range),
|
||||
rangeOffset: change.rangeOffset,
|
||||
rangeLength: change.rangeLength,
|
||||
text: change.text,
|
||||
};
|
||||
})
|
||||
});
|
||||
this._cellStarts.changeValue(cellIdx, e.document.getText().length + 1);
|
||||
|
||||
this._onDidChange.fire(this);
|
||||
|
||||
}, undefined, this._disposables);
|
||||
|
||||
extHostNotebooks.onDidChangeNotebookDocument(e => {
|
||||
if (e.document !== this._notebook) {
|
||||
return;
|
||||
}));
|
||||
this._disposables.add(extHostNotebooks.onDidChangeNotebookDocument(e => {
|
||||
if (e.document === this._notebook) {
|
||||
this._init();
|
||||
this._versionId += 1;
|
||||
this._onDidChange.fire(this);
|
||||
}
|
||||
//todo@jrieken update instead of flushing...
|
||||
this._versionId += 1;
|
||||
this._init();
|
||||
this._onDidChange.fire(this);
|
||||
}, undefined, this._disposables);
|
||||
}));
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this._disposables.dispose();
|
||||
this._delegate.dispose();
|
||||
}
|
||||
|
||||
private _init() {
|
||||
|
||||
// only allow Code-cells and those that are selected by the language selector
|
||||
this._selectedCells = this._notebook.cells
|
||||
.filter(cell => cell.cellKind === CellKind.Code && (!this._selector || score(LanguageSelector.from(this._selector), cell.uri, cell.language, true)));
|
||||
|
||||
const lines: string[] = [];
|
||||
const cellLengths = new Uint32Array(this._selectedCells.length);
|
||||
|
||||
for (let i = 0; i < this._selectedCells.length; i++) {
|
||||
const cell = this._selectedCells[i];
|
||||
// update prefix sum
|
||||
cellLengths[i] = cell.document.getText().length + 1; // 1 is newline
|
||||
//todo@jrieken reuse lines!
|
||||
for (let line = 0; line < cell.document.lineCount; line++) {
|
||||
lines.push(cell.document.lineAt(line).text);
|
||||
this._cells = [];
|
||||
const cellLengths: number[] = [];
|
||||
const cellLineCounts: number[] = [];
|
||||
for (let cell of this._notebook.cells) {
|
||||
if (cell.cellKind === CellKind.Code && (!this._selector || score(this._selector, cell.uri, cell.language, true))) {
|
||||
this._cells.push(cell);
|
||||
cellLengths.push(cell.document.getText().length + 1);
|
||||
cellLineCounts.push(cell.document.lineCount);
|
||||
}
|
||||
}
|
||||
|
||||
this._cellStarts = new PrefixSumComputer(cellLengths);
|
||||
this._delegate = new ExtHostDocumentData(
|
||||
new class extends NotImplementedProxy<MainThreadDocumentsShape>('MainThreadDocumentsShape') { },
|
||||
this._notebook.uri.with({ scheme: 'vscode-concatdoc' }),
|
||||
lines,
|
||||
'\n',
|
||||
this._notebook.languages[0],
|
||||
this._versionId,
|
||||
false
|
||||
);
|
||||
this._cellLengths = new PrefixSumComputer(new Uint32Array(cellLengths));
|
||||
this._cellLines = new PrefixSumComputer(new Uint32Array(cellLineCounts));
|
||||
}
|
||||
|
||||
get version() {
|
||||
get version(): number {
|
||||
return this._versionId;
|
||||
}
|
||||
|
||||
getText(range?: vscode.Range) {
|
||||
return this._delegate.document.getText(range);
|
||||
getText(range?: vscode.Range): string {
|
||||
if (!range) {
|
||||
let result = '';
|
||||
for (let cell of this._cells) {
|
||||
result += cell.document.getText() + '\n';
|
||||
}
|
||||
// remove last newline again
|
||||
result = result.slice(0, -1);
|
||||
return result;
|
||||
}
|
||||
|
||||
if (range.isEmpty) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// get start and end locations and create substrings
|
||||
const start = this.locationAt(range.start);
|
||||
const end = this.locationAt(range.end);
|
||||
const startCell = this._cells.find(cell => isEqual(cell.uri, start.uri));
|
||||
const endCell = this._cells.find(cell => isEqual(cell.uri, end.uri));
|
||||
|
||||
if (!startCell || !endCell) {
|
||||
return '';
|
||||
} else if (startCell === endCell) {
|
||||
return startCell.document.getText(new types.Range(start.range.start, end.range.end));
|
||||
} else {
|
||||
let a = startCell.document.getText(new types.Range(start.range.start, new types.Position(startCell.document.lineCount, 0)));
|
||||
let b = endCell.document.getText(new types.Range(new types.Position(0, 0), end.range.end));
|
||||
return a + '\n' + b;
|
||||
}
|
||||
}
|
||||
|
||||
locationAt(positionOrRange: vscode.Position | vscode.Range): vscode.Location {
|
||||
offsetAt(position: vscode.Position): number {
|
||||
const idx = this._cellLines.getIndexOf(position.line);
|
||||
const offset1 = this._cellLengths.getAccumulatedValue(idx.index - 1);
|
||||
const offset2 = this._cells[idx.index].document.offsetAt(position.with(idx.remainder));
|
||||
return offset1 + offset2;
|
||||
}
|
||||
|
||||
positionAt(locationOrOffset: vscode.Location | number): vscode.Position {
|
||||
if (typeof locationOrOffset === 'number') {
|
||||
const idx = this._cellLengths.getIndexOf(locationOrOffset);
|
||||
const lineCount = this._cellLines.getAccumulatedValue(idx.index - 1);
|
||||
return this._cells[idx.index].document.positionAt(idx.remainder).translate(lineCount);
|
||||
}
|
||||
|
||||
const idx = this._cells.findIndex(cell => isEqual(cell.uri, locationOrOffset.uri));
|
||||
if (idx >= 0) {
|
||||
let line = this._cellLines.getAccumulatedValue(idx - 1);
|
||||
return new types.Position(line + locationOrOffset.range.start.line, locationOrOffset.range.start.character);
|
||||
}
|
||||
// do better?
|
||||
// return undefined;
|
||||
return new types.Position(0, 0);
|
||||
}
|
||||
|
||||
locationAt(positionOrRange: vscode.Range | vscode.Position): types.Location {
|
||||
if (!types.Range.isRange(positionOrRange)) {
|
||||
positionOrRange = new types.Range(<types.Position>positionOrRange, <types.Position>positionOrRange);
|
||||
}
|
||||
|
||||
const start = this._delegate.document.offsetAt(positionOrRange.start);
|
||||
const startIndex = this._cellStarts.getIndexOf(start);
|
||||
const startCell = this._selectedCells[startIndex.index];
|
||||
if (!startCell) {
|
||||
// do better? throw an error insead? return undefined?
|
||||
return new types.Location(this._notebook.uri, new types.Position(0, 0));
|
||||
}
|
||||
|
||||
let endCell = startCell;
|
||||
let endIndex = startIndex;
|
||||
const startIdx = this._cellLines.getIndexOf(positionOrRange.start.line);
|
||||
let endIdx = startIdx;
|
||||
if (!positionOrRange.isEmpty) {
|
||||
const end = this._delegate.document.offsetAt(positionOrRange.end);
|
||||
endIndex = this._cellStarts.getIndexOf(end);
|
||||
endCell = this._selectedCells[endIndex.index];
|
||||
endIdx = this._cellLines.getIndexOf(positionOrRange.end.line);
|
||||
}
|
||||
|
||||
const startPos = startCell.document.positionAt(startIndex.remainder);
|
||||
let endPos = startPos;
|
||||
if (endCell && endCell.handle === startCell.handle) {
|
||||
endPos = endCell.document.positionAt(endIndex.remainder);
|
||||
}
|
||||
let startPos = new types.Position(startIdx.remainder, positionOrRange.start.character);
|
||||
let endPos = new types.Position(endIdx.remainder, positionOrRange.end.character);
|
||||
let range = new types.Range(startPos, endPos);
|
||||
|
||||
return new types.Location(startCell.uri, new types.Range(startPos.line, startPos.character, endPos.line, endPos.character));
|
||||
}
|
||||
|
||||
positionAt(offsetOrLocation: number | vscode.Location): vscode.Position {
|
||||
if (typeof offsetOrLocation === 'number') {
|
||||
return this._delegate.document.positionAt(offsetOrLocation);
|
||||
}
|
||||
const idx = this._selectedCells.findIndex(candidate => candidate.uri.toString() === offsetOrLocation.uri.toString());
|
||||
if (idx < 0) {
|
||||
// do better?
|
||||
// return undefined;
|
||||
return new types.Position(0, 0);
|
||||
}
|
||||
const docOffset = this._selectedCells[idx].document.offsetAt(offsetOrLocation.range.start);
|
||||
const cellOffset = this._cellStarts.getAccumulatedValue(idx - 1);
|
||||
return this._delegate.document.positionAt(docOffset + cellOffset);
|
||||
}
|
||||
|
||||
offsetAt(position: vscode.Position): number {
|
||||
return this._delegate.document.offsetAt(position);
|
||||
const startCell = this._cells[startIdx.index];
|
||||
return new types.Location(startCell.uri, <types.Range>startCell.document.validateRange(range));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user