Skip to content

Commit cda677d

Browse files
committed
make NotebookConcatTextDocument extends "normal" TextDocument, microsoft#100186
1 parent 0980b4d commit cda677d

3 files changed

Lines changed: 109 additions & 11 deletions

File tree

src/vs/vscode.proposed.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1454,7 +1454,7 @@ declare module 'vscode' {
14541454
metadata: NotebookDocumentMetadata;
14551455
}
14561456

1457-
export interface NotebookConcatTextDocument {
1457+
export interface NotebookConcatTextDocument extends TextDocument {
14581458
isClosed: boolean;
14591459
dispose(): void;
14601460
onDidChange: Event<void>;

src/vs/workbench/api/common/extHostNotebookConcatDocument.ts

Lines changed: 84 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,32 +12,45 @@ import { PrefixSumComputer } from 'vs/editor/common/viewModel/prefixSumComputer'
1212
import { DisposableStore } from 'vs/base/common/lifecycle';
1313
import { score } from 'vs/editor/common/modes/languageSelector';
1414
import { CellKind } from 'vs/workbench/contrib/notebook/common/notebookCommon';
15-
import { isEqual } from 'vs/base/common/resources';
15+
import { basename } from 'vs/base/common/resources';
16+
import { ResourceMap } from 'vs/base/common/map';
17+
import { ExtHostDocumentLine } from 'vs/workbench/api/common/extHostDocumentData';
1618

17-
export class ExtHostNotebookConcatDocument implements vscode.NotebookConcatTextDocument {
19+
export class ExtHostNotebookConcatDocument implements vscode.NotebookConcatTextDocument, vscode.TextDocument {
1820

1921
private _disposables = new DisposableStore();
2022
private _isClosed = false;
2123

2224
private _cells!: ExtHostCell[];
25+
private _cellByUri!: ResourceMap<number>;
2326
private _cellLengths!: PrefixSumComputer;
2427
private _cellLines!: PrefixSumComputer;
2528
private _versionId = 0;
2629

2730
private readonly _onDidChange = new Emitter<void>();
2831
readonly onDidChange: Event<void> = this._onDidChange.event;
2932

33+
readonly uri: vscode.Uri;
34+
readonly fileName: string;
35+
readonly languageId: string;
36+
readonly isUntitled: boolean = false;
37+
readonly isDirty: boolean = false;
38+
3039
constructor(
3140
extHostNotebooks: ExtHostNotebookController,
3241
extHostDocuments: ExtHostDocuments,
3342
private readonly _notebook: vscode.NotebookDocument,
3443
private readonly _selector: vscode.DocumentSelector | undefined,
3544
) {
45+
this.uri = _notebook.uri.with({ scheme: 'vscode-notebook-concat-doc' });
46+
this.fileName = basename(this.uri);
47+
this.languageId = this._createLanguageId();
48+
3649
this._init();
3750

3851
this._disposables.add(extHostDocuments.onDidChangeDocument(e => {
39-
let cellIdx = this._cells.findIndex(cell => isEqual(cell.uri, e.document.uri));
40-
if (cellIdx >= 0) {
52+
const cellIdx = this._cellByUri.get(e.document.uri);
53+
if (typeof cellIdx === 'number') {
4154
this._cellLengths.changeValue(cellIdx, this._cells[cellIdx].document.getText().length + 1);
4255
this._cellLines.changeValue(cellIdx, this._cells[cellIdx].document.lineCount);
4356
this._versionId += 1;
@@ -68,10 +81,12 @@ export class ExtHostNotebookConcatDocument implements vscode.NotebookConcatTextD
6881

6982
private _init() {
7083
this._cells = [];
84+
this._cellByUri = new ResourceMap();
7185
const cellLengths: number[] = [];
7286
const cellLineCounts: number[] = [];
7387
for (let cell of this._notebook.cells) {
7488
if (cell.cellKind === CellKind.Code && (!this._selector || score(this._selector, cell.uri, cell.language, true))) {
89+
this._cellByUri.set(cell.uri, this._cells.length);
7590
this._cells.push(<ExtHostCell>cell);
7691
cellLengths.push(cell.document.getText().length + 1);
7792
cellLineCounts.push(cell.document.lineCount);
@@ -81,6 +96,67 @@ export class ExtHostNotebookConcatDocument implements vscode.NotebookConcatTextD
8196
this._cellLines = new PrefixSumComputer(new Uint32Array(cellLineCounts));
8297
}
8398

99+
private _createLanguageId(): string {
100+
const languageIds = new Set<string>();
101+
(function fillInLanguageIds(selector: vscode.DocumentSelector | undefined) {
102+
if (Array.isArray(selector)) {
103+
selector.forEach(fillInLanguageIds);
104+
} else if (typeof selector === 'string') {
105+
languageIds.add(selector);
106+
} else if (selector?.language) {
107+
languageIds.add(selector.language);
108+
}
109+
})(this._selector);
110+
111+
if (languageIds.size === 0) {
112+
return 'unknown';
113+
}
114+
return [...languageIds.values()].sort().join(';');
115+
}
116+
117+
save(): Thenable<boolean> {
118+
// todo@jrieken throw error instead?
119+
return Promise.resolve(false);
120+
}
121+
122+
get eol(): vscode.EndOfLine {
123+
return types.EndOfLine.LF;
124+
}
125+
126+
get lineCount(): number {
127+
let total = 0;
128+
for (let cell of this._cells) {
129+
total += cell.document.lineCount;
130+
}
131+
return total;
132+
}
133+
134+
lineAt(lineOrPosition: number | vscode.Position): vscode.TextLine {
135+
const line = typeof lineOrPosition === 'number' ? lineOrPosition : lineOrPosition.line;
136+
const cellIdx = this._cellLines.getIndexOf(line);
137+
return new ExtHostDocumentLine(
138+
line,
139+
this._cells[cellIdx.index].document.lineAt(cellIdx.remainder).text,
140+
line >= this.lineCount
141+
);
142+
}
143+
144+
getWordRangeAtPosition(position: vscode.Position, regex?: RegExp | undefined): vscode.Range | undefined {
145+
const cellIdx = this._cellLines.getIndexOf(position.line);
146+
return this._cells[cellIdx.index].document.getWordRangeAtPosition(position.with({ line: cellIdx.remainder }), regex);
147+
}
148+
149+
validateRange(range: vscode.Range): vscode.Range {
150+
const start = this.validatePosition(range.start);
151+
const end = this.validatePosition(range.end);
152+
return range.with({ start, end });
153+
}
154+
155+
validatePosition(position: vscode.Position): vscode.Position {
156+
const cellIdx = this._cellLines.getIndexOf(position.line);
157+
return this._cells[cellIdx.index].document.validatePosition(position.with({ line: cellIdx.remainder }));
158+
}
159+
84160
get version(): number {
85161
return this._versionId;
86162
}
@@ -103,8 +179,8 @@ export class ExtHostNotebookConcatDocument implements vscode.NotebookConcatTextD
103179
// get start and end locations and create substrings
104180
const start = this.locationAt(range.start);
105181
const end = this.locationAt(range.end);
106-
const startCell = this._cells.find(cell => isEqual(cell.uri, start.uri));
107-
const endCell = this._cells.find(cell => isEqual(cell.uri, end.uri));
182+
const startCell = this._cells[this._cellByUri.get(start.uri) ?? -1];
183+
const endCell = this._cells[this._cellByUri.get(end.uri) ?? -1];
108184

109185
if (!startCell || !endCell) {
110186
return '';
@@ -131,8 +207,8 @@ export class ExtHostNotebookConcatDocument implements vscode.NotebookConcatTextD
131207
return this._cells[idx.index].document.positionAt(idx.remainder).translate(lineCount);
132208
}
133209

134-
const idx = this._cells.findIndex(cell => isEqual(cell.uri, locationOrOffset.uri));
135-
if (idx >= 0) {
210+
const idx = this._cellByUri.get(locationOrOffset.uri);
211+
if (typeof idx === 'number') {
136212
let line = this._cellLines.getAccumulatedValue(idx - 1);
137213
return new types.Position(line + locationOrOffset.range.start.line, locationOrOffset.range.start.character);
138214
}

src/vs/workbench/test/browser/api/extHostNotebookConcatDocument.test.ts

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import { ExtHostNotebookConcatDocument } from 'vs/workbench/api/common/extHostNo
1212
import { ExtHostNotebookDocument, ExtHostNotebookController } from 'vs/workbench/api/common/extHostNotebook';
1313
import { URI } from 'vs/base/common/uri';
1414
import { CellKind, CellUri, NotebookCellsChangeType } from 'vs/workbench/contrib/notebook/common/notebookCommon';
15-
import { Position, Location, Range } from 'vs/workbench/api/common/extHostTypes';
15+
import { Position, Location, Range, EndOfLine } from 'vs/workbench/api/common/extHostTypes';
1616
import { ExtHostCommands } from 'vs/workbench/api/common/extHostCommands';
1717
import { nullExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
1818
import * as vscode from 'vscode';
@@ -28,7 +28,7 @@ suite('NotebookConcatDocument', function () {
2828
let extHostDocumentsAndEditors: ExtHostDocumentsAndEditors;
2929
let extHostDocuments: ExtHostDocuments;
3030
let extHostNotebooks: ExtHostNotebookController;
31-
const notebookUri = URI.parse('test:///notebook.file');
31+
const notebookUri = URI.parse('test:/path/notebook.file');
3232
const disposables = new DisposableStore();
3333

3434
setup(async function () {
@@ -80,6 +80,19 @@ suite('NotebookConcatDocument', function () {
8080
disposables.add(extHostDocuments);
8181
});
8282

83+
test('basics', async function () {
84+
let doc = new ExtHostNotebookConcatDocument(extHostNotebooks, extHostDocuments, notebook, undefined);
85+
assert.equal(doc.uri.toString(), 'vscode-notebook-concat-doc:/path/notebook.file');
86+
assert.equal(doc.fileName, 'notebook.file');
87+
assert.equal(doc.isUntitled, false);
88+
assert.equal(doc.isDirty, false);
89+
assert.equal(await doc.save(), false);
90+
assert.equal(doc.isClosed, false);
91+
assert.equal(doc.eol, EndOfLine.LF);
92+
doc.dispose();
93+
assert.equal(doc.isClosed, true);
94+
});
95+
8396
test('empty', function () {
8497
let doc = new ExtHostNotebookConcatDocument(extHostNotebooks, extHostDocuments, notebook, undefined);
8598
assert.equal(doc.getText(), '');
@@ -109,6 +122,11 @@ suite('NotebookConcatDocument', function () {
109122
function assertLines(doc: vscode.NotebookConcatTextDocument, ...lines: string[]) {
110123
let actual = doc.getText().split(/\r\n|\n|\r/);
111124
assert.deepStrictEqual(actual, lines);
125+
assert.equal(doc.lineCount, lines.length);
126+
for (let i = 0; i < lines.length; i++) {
127+
assert.equal(doc.lineAt(i).text, lines[i]);
128+
assert.equal(doc.lineAt(i).range.start.line, i);
129+
}
112130
}
113131

114132
test('location, position mapping', function () {
@@ -302,6 +320,10 @@ suite('NotebookConcatDocument', function () {
302320
const fooLangDoc = new ExtHostNotebookConcatDocument(extHostNotebooks, extHostDocuments, notebook, 'fooLang');
303321
const barLangDoc = new ExtHostNotebookConcatDocument(extHostNotebooks, extHostDocuments, notebook, 'barLang');
304322

323+
assert.equal(mixedDoc.languageId, 'unknown');
324+
assert.equal(fooLangDoc.languageId, 'fooLang');
325+
assert.equal(barLangDoc.languageId, 'barLang');
326+
305327
assertLines(mixedDoc, 'fooLang-document', 'barLang-document');
306328
assertLines(fooLangDoc, 'fooLang-document');
307329
assertLines(barLangDoc, 'barLang-document');

0 commit comments

Comments
 (0)