提交 c8b27b9d 编写于 作者: J Johannes Rieken

Merge branch 'joh/concat-doc'

......@@ -1625,6 +1625,8 @@ declare module 'vscode' {
export interface NotebookCell {
readonly uri: Uri;
readonly cellKind: CellKind;
readonly document: TextDocument;
// API remove `source` or doc it as shorthand for document.getText()
readonly source: string;
language: string;
outputs: CellOutput[];
......@@ -1665,6 +1667,17 @@ declare module 'vscode' {
languages: string[];
displayOrder?: GlobPattern[];
metadata: NotebookDocumentMetadata;
}
export interface NotebookConcatTextDocument {
version: number;
getText(): string;
getText(range: Range): string;
offsetAt(position: Position): number;
positionAt(offset: number): Position;
locationAt(positionOrRange: Position | Range): Location;
positionAt(location: Location): Position;
}
export interface NotebookEditorCellEdit {
......
......@@ -181,6 +181,10 @@ export class ExtHostDocumentData extends MirrorTextModel {
throw new Error('Invalid argument');
}
if (this._lines.length === 0) {
return position.with(0, 0);
}
let { line, character } = position;
let hasChanged = false;
......
......@@ -10,12 +10,14 @@ import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecyc
import { ISplice } from 'vs/base/common/sequence';
import { URI, UriComponents } from 'vs/base/common/uri';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { CellKind, CellOutputKind, ExtHostNotebookShape, IMainContext, MainContext, MainThreadNotebookShape, NotebookCellOutputsSplice } from 'vs/workbench/api/common/extHost.protocol';
import { CellKind, CellOutputKind, ExtHostNotebookShape, IMainContext, MainContext, MainThreadNotebookShape, NotebookCellOutputsSplice, MainThreadDocumentsShape } from 'vs/workbench/api/common/extHost.protocol';
import { ExtHostCommands } from 'vs/workbench/api/common/extHostCommands';
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
import { CellEditType, CellUri, diff, ICellEditOperation, ICellInsertEdit, IErrorOutput, INotebookDisplayOrder, INotebookEditData, IOrderedMimeType, IStreamOutput, ITransformedDisplayOutputDto, mimeTypeSupportedByCore, NotebookCellsChangedEvent, NotebookCellsSplice2, sortMimeTypes, ICellDeleteEdit, notebookDocumentMetadataDefaults } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { Disposable as VSCodeDisposable } from './extHostTypes';
import { CancellationToken } from 'vs/base/common/cancellation';
import { ExtHostDocumentData } from 'vs/workbench/api/common/extHostDocumentData';
import { NotImplementedProxy } from 'vs/base/common/types';
interface IObservable<T> {
proxy: T;
......@@ -40,39 +42,48 @@ function getObservable<T extends Object>(obj: T): IObservable<T> {
export class ExtHostCell extends Disposable implements vscode.NotebookCell {
private originalSource: string[];
// private originalSource: string[];
private _outputs: any[];
private _onDidChangeOutputs = new Emitter<ISplice<vscode.CellOutput>[]>();
onDidChangeOutputs: Event<ISplice<vscode.CellOutput>[]> = this._onDidChangeOutputs.event;
private _textDocument: vscode.TextDocument | undefined;
private _initalVersion: number = -1;
// private _textDocument: vscode.TextDocument | undefined;
// private _initalVersion: number = -1;
private _outputMapping = new Set<vscode.CellOutput>();
private _metadata: vscode.NotebookCellMetadata;
private _metadataChangeListener: IDisposable;
private _documentData: ExtHostDocumentData;
get document(): vscode.TextDocument {
return this._documentData.document;
}
get source() {
if (this._textDocument && this._initalVersion !== this._textDocument?.version) {
return this._textDocument.getText();
} else {
return this.originalSource.join('\n');
}
// todo@jrieken remove this
return this._documentData.getText();
}
constructor(
private viewType: string,
private documentUri: URI,
private readonly viewType: string,
private readonly documentUri: URI,
readonly handle: number,
readonly uri: URI,
private _content: string,
content: string,
public readonly cellKind: CellKind,
public language: string,
outputs: any[],
_metadata: vscode.NotebookCellMetadata | undefined,
private _proxy: MainThreadNotebookShape
private _proxy: MainThreadNotebookShape,
) {
super();
this.originalSource = this._content.split(/\r|\n|\r\n/g);
this._documentData = new ExtHostDocumentData(
new class extends NotImplementedProxy<MainThreadDocumentsShape>('document') { },
uri,
content.split(/\r|\n|\r\n/g), '\n',
language, 0, false
);
this._outputs = outputs;
const observableMetadata = getObservable(_metadata || {});
......@@ -125,26 +136,19 @@ export class ExtHostCell extends Disposable implements vscode.NotebookCell {
return this._proxy.$updateNotebookCellMetadata(this.viewType, this.documentUri, this.handle, this._metadata);
}
getContent(): string {
if (this._textDocument && this._initalVersion !== this._textDocument?.version) {
return this._textDocument.getText();
} else {
return this.originalSource.join('\n');
}
}
attachTextDocument(document: vscode.TextDocument) {
this._textDocument = document;
this._initalVersion = this._textDocument.version;
attachTextDocument(document: ExtHostDocumentData) {
this._documentData = document;
// this._initalVersion = this._documentData.version;
}
detachTextDocument() {
if (this._textDocument && this._textDocument.version !== this._initalVersion) {
this.originalSource = this._textDocument.getText().split(/\r|\n|\r\n/g);
}
// no-op? keep stale document until new comes along?
this._textDocument = undefined;
this._initalVersion = -1;
// if (this._textDocument && this._textDocument.version !== this._initalVersion) {
// this.originalSource = this._textDocument.getText().split(/\r|\n|\r\n/g);
// }
// this._textDocument = undefined;
// this._initalVersion = -1;
}
}
......@@ -256,10 +260,10 @@ export class ExtHostNotebookDocument extends Disposable implements vscode.Notebo
let cellDtos = splice[2];
let newCells = cellDtos.map(cell => {
const extCell = new ExtHostCell(this.viewType, this.uri, cell.handle, URI.revive(cell.uri), cell.source.join('\n'), cell.cellKind, cell.language, cell.outputs, cell.metadata, this._proxy);
const document = this._documentsAndEditors.getDocument(URI.revive(cell.uri));
const documentData = this._documentsAndEditors.getDocument(URI.revive(cell.uri));
if (document) {
extCell.attachTextDocument(document.document);
if (documentData) {
extCell.attachTextDocument(documentData);
}
if (!this._cellDisposableMapping.has(extCell.handle)) {
......@@ -366,15 +370,15 @@ export class ExtHostNotebookDocument extends Disposable implements vscode.Notebo
return this.cells.find(cell => cell.handle === cellHandle);
}
attachCellTextDocument(textDocument: vscode.TextDocument) {
let cell = this.cells.find(cell => cell.uri.toString() === textDocument.uri.toString());
attachCellTextDocument(textDocument: ExtHostDocumentData) {
let cell = this.cells.find(cell => cell.uri.toString() === textDocument.document.uri.toString());
if (cell) {
cell.attachTextDocument(textDocument);
}
}
detachCellTextDocument(textDocument: vscode.TextDocument) {
let cell = this.cells.find(cell => cell.uri.toString() === textDocument.uri.toString());
detachCellTextDocument(textDocument: ExtHostDocumentData) {
let cell = this.cells.find(cell => cell.uri.toString() === textDocument.document.uri.toString());
if (cell) {
cell.detachTextDocument();
}
......@@ -468,22 +472,22 @@ export class ExtHostNotebookEditor extends Disposable implements vscode.Notebook
) {
super();
this._register(this._documentsAndEditors.onDidAddDocuments(documents => {
for (const { document: textDocument } of documents) {
let data = CellUri.parse(textDocument.uri);
for (const documentData of documents) {
let data = CellUri.parse(documentData.document.uri);
if (data) {
if (this.document.uri.toString() === data.notebook.toString()) {
document.attachCellTextDocument(textDocument);
document.attachCellTextDocument(documentData);
}
}
}
}));
this._register(this._documentsAndEditors.onDidRemoveDocuments(documents => {
for (const { document: textDocument } of documents) {
let data = CellUri.parse(textDocument.uri);
for (const documentData of documents) {
let data = CellUri.parse(documentData.document.uri);
if (data) {
if (this.document.uri.toString() === data.notebook.toString()) {
document.detachCellTextDocument(textDocument);
document.detachCellTextDocument(documentData);
}
}
}
......@@ -593,6 +597,10 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
private readonly _documents = new Map<string, ExtHostNotebookDocument>();
private readonly _editors = new Map<string, { editor: ExtHostNotebookEditor, onDidReceiveMessage: Emitter<any> }>();
private readonly _notebookOutputRenderers = new Map<number, ExtHostNotebookOutputRenderer>();
private readonly _onDidChangeNotebookDocument = new Emitter<{ document: ExtHostNotebookDocument, changes: NotebookCellsSplice2[] }>();
readonly onDidChangeNotebookDocument: Event<{ document: ExtHostNotebookDocument, changes: NotebookCellsSplice2[] }> = this._onDidChangeNotebookDocument.event;
private _outputDisplayOrder: INotebookDisplayOrder | undefined;
get outputDisplayOrder(): INotebookDisplayOrder | undefined {
......@@ -782,6 +790,10 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
if (editor) {
editor.editor.document.accpetModelChanged(event);
this._onDidChangeNotebookDocument.fire({
document: editor.editor.document,
changes: event.changes
});
}
}
......
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as types from 'vs/workbench/api/common/extHostTypes';
import * as vscode from 'vscode';
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 { DisposableStore } from 'vs/base/common/lifecycle';
import { score } from 'vs/editor/common/modes/languageSelector';
import { CellKind } from 'vs/workbench/contrib/notebook/common/notebookCommon';
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;
constructor(
extHostNotebooks: ExtHostNotebookController,
extHostDocuments: ExtHostDocuments,
private readonly _notebook: ExtHostNotebookDocument,
private readonly _selector: vscode.DocumentSelector | undefined,
) {
this._init();
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);
}
}));
this._disposables.add(extHostNotebooks.onDidChangeNotebookDocument(e => {
if (e.document === this._notebook) {
this._init();
this._versionId += 1;
this._onDidChange.fire(this);
}
}));
}
dispose(): void {
this._disposables.dispose();
}
private _init() {
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._cellLengths = new PrefixSumComputer(new Uint32Array(cellLengths));
this._cellLines = new PrefixSumComputer(new Uint32Array(cellLineCounts));
}
get version(): number {
return this._versionId;
}
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;
}
}
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 startIdx = this._cellLines.getIndexOf(positionOrRange.start.line);
let endIdx = startIdx;
if (!positionOrRange.isEmpty) {
endIdx = this._cellLines.getIndexOf(positionOrRange.end.line);
}
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);
const startCell = this._cells[startIdx.index];
return new types.Location(startCell.uri, <types.Range>startCell.document.validateRange(range));
}
}
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import { TestRPCProtocol } from 'vs/workbench/test/browser/api/testRPCProtocol';
import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments';
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
import { NullLogService } from 'vs/platform/log/common/log';
import { ExtHostNotebookConcatDocument } from 'vs/workbench/api/common/extHostNotebookConcatDocument';
import { ExtHostNotebookDocument, ExtHostNotebookController } from 'vs/workbench/api/common/extHostNotebook';
import { URI } from 'vs/base/common/uri';
import { CellKind, CellUri } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { Position, Location, Range } from 'vs/workbench/api/common/extHostTypes';
import { ExtHostCommands } from 'vs/workbench/api/common/extHostCommands';
import { nullExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
import * as vscode from 'vscode';
import { mock } from 'vs/workbench/test/common/workbenchTestServices';
import { MainContext, MainThreadCommandsShape, MainThreadNotebookShape } from 'vs/workbench/api/common/extHost.protocol';
import { DisposableStore } from 'vs/base/common/lifecycle';
suite('NotebookConcatDocument', function () {
let rpcProtocol: TestRPCProtocol;
let notebook: ExtHostNotebookDocument;
let extHostDocumentsAndEditors: ExtHostDocumentsAndEditors;
let extHostDocuments: ExtHostDocuments;
let extHostNotebooks: ExtHostNotebookController;
const notebookUri = URI.parse('test:///notebook.file');
const disposables = new DisposableStore();
setup(async function () {
disposables.clear();
rpcProtocol = new TestRPCProtocol();
rpcProtocol.set(MainContext.MainThreadCommands, new class extends mock<MainThreadCommandsShape>() {
$registerCommand() { }
});
rpcProtocol.set(MainContext.MainThreadNotebook, new class extends mock<MainThreadNotebookShape>() {
async $registerNotebookProvider() { }
async $unregisterNotebookProvider() { }
async $createNotebookDocument() { }
});
extHostDocumentsAndEditors = new ExtHostDocumentsAndEditors(rpcProtocol, new NullLogService());
extHostDocuments = new ExtHostDocuments(rpcProtocol, extHostDocumentsAndEditors);
extHostNotebooks = new ExtHostNotebookController(rpcProtocol, new ExtHostCommands(rpcProtocol, new NullLogService()), extHostDocumentsAndEditors);
let reg = extHostNotebooks.registerNotebookProvider(nullExtensionDescription, 'test', new class extends mock<vscode.NotebookProvider>() {
async resolveNotebook() { }
});
await extHostNotebooks.$resolveNotebook('test', notebookUri);
extHostNotebooks.$acceptModelChanged(notebookUri, {
versionId: 0,
changes: [[0, 0, [{
handle: 0,
uri: CellUri.generate(notebookUri, 0),
source: ['### Heading'],
language: 'markdown',
cellKind: CellKind.Markdown,
outputs: [],
}]]]
});
await extHostNotebooks.$updateActiveEditor('test', notebookUri);
notebook = extHostNotebooks.activeNotebookDocument!;
disposables.add(reg);
disposables.add(notebook);
disposables.add(extHostDocuments);
});
test('empty', function () {
let doc = new ExtHostNotebookConcatDocument(extHostNotebooks, extHostDocuments, notebook, undefined);
assert.equal(doc.getText(), '');
assert.equal(doc.version, 0);
// assert.equal(doc.locationAt(new Position(0, 0)), undefined);
// assert.equal(doc.positionAt(SOME_FAKE_LOCATION?), undefined);
});
function assertLocation(doc: vscode.NotebookConcatTextDocument, pos: Position, expected: Location, reverse = true) {
const actual = doc.locationAt(pos);
assert.equal(actual.uri.toString(), expected.uri.toString());
assert.equal(actual.range.isEqual(expected.range), true);
if (reverse) {
// reverse - offset
const offset = doc.offsetAt(pos);
assert.equal(doc.positionAt(offset).isEqual(pos), true);
// reverse - pos
const actualPosition = doc.positionAt(actual);
assert.equal(actualPosition.isEqual(pos), true);
}
}
function assertLines(doc: vscode.NotebookConcatTextDocument, ...lines: string[]) {
let actual = doc.getText().split(/\r\n|\n|\r/);
assert.deepStrictEqual(actual, lines);
}
test('location, position mapping', function () {
extHostNotebooks.$acceptModelChanged(notebookUri, {
versionId: notebook.versionId + 1,
changes: [[0, 0, [{
handle: 1,
uri: CellUri.generate(notebook.uri, 1),
source: ['Hello', 'World', 'Hello World!'],
language: 'test',
cellKind: CellKind.Code,
outputs: [],
}, {
handle: 2,
uri: CellUri.generate(notebook.uri, 2),
source: ['Hallo', 'Welt', 'Hallo Welt!'],
language: 'test',
cellKind: CellKind.Code,
outputs: [],
}]]]
});
assert.equal(notebook.cells.length, 1 + 2); // markdown and code
let doc = new ExtHostNotebookConcatDocument(extHostNotebooks, extHostDocuments, notebook, undefined);
assertLines(doc, 'Hello', 'World', 'Hello World!', 'Hallo', 'Welt', 'Hallo Welt!');
assertLocation(doc, new Position(0, 0), new Location(notebook.cells[0].uri, new Position(0, 0)));
assertLocation(doc, new Position(4, 0), new Location(notebook.cells[1].uri, new Position(1, 0)));
assertLocation(doc, new Position(4, 3), new Location(notebook.cells[1].uri, new Position(1, 3)));
assertLocation(doc, new Position(5, 11), new Location(notebook.cells[1].uri, new Position(2, 11)));
assertLocation(doc, new Position(5, 12), new Location(notebook.cells[1].uri, new Position(2, 11)), false); // don't check identity because position will be clamped
});
test('location, position mapping, cell changes', function () {
let doc = new ExtHostNotebookConcatDocument(extHostNotebooks, extHostDocuments, notebook, undefined);
// UPDATE 1
extHostNotebooks.$acceptModelChanged(notebookUri, {
versionId: notebook.versionId + 1,
changes: [[0, 0, [{
handle: 1,
uri: CellUri.generate(notebook.uri, 1),
source: ['Hello', 'World', 'Hello World!'],
language: 'test',
cellKind: CellKind.Code,
outputs: [],
}]]]
});
assert.equal(notebook.cells.length, 1 + 1);
assert.equal(doc.version, 1);
assertLines(doc, 'Hello', 'World', 'Hello World!');
assertLocation(doc, new Position(0, 0), new Location(notebook.cells[0].uri, new Position(0, 0)));
assertLocation(doc, new Position(2, 2), new Location(notebook.cells[0].uri, new Position(2, 2)));
assertLocation(doc, new Position(4, 0), new Location(notebook.cells[0].uri, new Position(2, 12)), false); // clamped
// UPDATE 2
extHostNotebooks.$acceptModelChanged(notebookUri, {
versionId: notebook.versionId + 1,
changes: [[1, 0, [{
handle: 2,
uri: CellUri.generate(notebook.uri, 2),
source: ['Hallo', 'Welt', 'Hallo Welt!'],
language: 'test',
cellKind: CellKind.Code,
outputs: [],
}]]]
});
assert.equal(notebook.cells.length, 1 + 2);
assert.equal(doc.version, 2);
assertLines(doc, 'Hello', 'World', 'Hello World!', 'Hallo', 'Welt', 'Hallo Welt!');
assertLocation(doc, new Position(0, 0), new Location(notebook.cells[0].uri, new Position(0, 0)));
assertLocation(doc, new Position(4, 0), new Location(notebook.cells[1].uri, new Position(1, 0)));
assertLocation(doc, new Position(4, 3), new Location(notebook.cells[1].uri, new Position(1, 3)));
assertLocation(doc, new Position(5, 11), new Location(notebook.cells[1].uri, new Position(2, 11)));
assertLocation(doc, new Position(5, 12), new Location(notebook.cells[1].uri, new Position(2, 11)), false); // don't check identity because position will be clamped
// UPDATE 3 (remove cell #2 again)
extHostNotebooks.$acceptModelChanged(notebookUri, {
versionId: notebook.versionId + 1,
changes: [[1, 1, []]]
});
assert.equal(notebook.cells.length, 1 + 1);
assert.equal(doc.version, 3);
assertLines(doc, 'Hello', 'World', 'Hello World!');
assertLocation(doc, new Position(0, 0), new Location(notebook.cells[0].uri, new Position(0, 0)));
assertLocation(doc, new Position(2, 2), new Location(notebook.cells[0].uri, new Position(2, 2)));
assertLocation(doc, new Position(4, 0), new Location(notebook.cells[0].uri, new Position(2, 12)), false); // clamped
});
test('location, position mapping, cell-document changes', function () {
let doc = new ExtHostNotebookConcatDocument(extHostNotebooks, extHostDocuments, notebook, undefined);
// UPDATE 1
extHostNotebooks.$acceptModelChanged(notebookUri, {
versionId: notebook.versionId + 1,
changes: [[0, 0, [{
handle: 1,
uri: CellUri.generate(notebook.uri, 1),
source: ['Hello', 'World', 'Hello World!'],
language: 'test',
cellKind: CellKind.Code,
outputs: [],
}, {
handle: 2,
uri: CellUri.generate(notebook.uri, 2),
source: ['Hallo', 'Welt', 'Hallo Welt!'],
language: 'test',
cellKind: CellKind.Code,
outputs: [],
}]]]
});
assert.equal(notebook.cells.length, 1 + 2);
assert.equal(doc.version, 1);
assertLines(doc, 'Hello', 'World', 'Hello World!', 'Hallo', 'Welt', 'Hallo Welt!');
assertLocation(doc, new Position(0, 0), new Location(notebook.cells[0].uri, new Position(0, 0)));
assertLocation(doc, new Position(2, 2), new Location(notebook.cells[0].uri, new Position(2, 2)));
assertLocation(doc, new Position(2, 12), new Location(notebook.cells[0].uri, new Position(2, 12)));
assertLocation(doc, new Position(4, 0), new Location(notebook.cells[1].uri, new Position(1, 0)));
assertLocation(doc, new Position(4, 3), new Location(notebook.cells[1].uri, new Position(1, 3)));
// offset math
let cell1End = doc.offsetAt(new Position(2, 12));
assert.equal(doc.positionAt(cell1End).isEqual(new Position(2, 12)), true);
extHostDocumentsAndEditors.$acceptDocumentsAndEditorsDelta({
addedDocuments: [{
uri: notebook.cells[0].uri,
versionId: 1,
lines: ['Hello', 'World', 'Hello World!'],
EOL: '\n',
modeId: '',
isDirty: false
}]
});
extHostDocuments.$acceptModelChanged(notebook.cells[0].uri, {
versionId: 0,
eol: '\n',
changes: [{
range: { startLineNumber: 3, startColumn: 1, endLineNumber: 3, endColumn: 6 },
rangeLength: 6,
rangeOffset: 12,
text: 'Hi'
}]
}, false);
assertLines(doc, 'Hello', 'World', 'Hi World!', 'Hallo', 'Welt', 'Hallo Welt!');
assertLocation(doc, new Position(2, 12), new Location(notebook.cells[0].uri, new Position(2, 9)), false);
assert.equal(doc.positionAt(cell1End).isEqual(new Position(3, 2)), true);
});
test('selector', function () {
extHostNotebooks.$acceptModelChanged(notebookUri, {
versionId: notebook.versionId + 1,
changes: [[0, 0, [{
handle: 1,
uri: CellUri.generate(notebook.uri, 1),
source: ['fooLang-document'],
language: 'fooLang',
cellKind: CellKind.Code,
outputs: [],
}, {
handle: 2,
uri: CellUri.generate(notebook.uri, 2),
source: ['barLang-document'],
language: 'barLang',
cellKind: CellKind.Code,
outputs: [],
}]]]
});
const mixedDoc = new ExtHostNotebookConcatDocument(extHostNotebooks, extHostDocuments, notebook, undefined);
const fooLangDoc = new ExtHostNotebookConcatDocument(extHostNotebooks, extHostDocuments, notebook, 'fooLang');
const barLangDoc = new ExtHostNotebookConcatDocument(extHostNotebooks, extHostDocuments, notebook, 'barLang');
assertLines(mixedDoc, 'fooLang-document', 'barLang-document');
assertLines(fooLangDoc, 'fooLang-document');
assertLines(barLangDoc, 'barLang-document');
extHostNotebooks.$acceptModelChanged(notebookUri, {
versionId: notebook.versionId + 1,
changes: [[2, 0, [{
handle: 3,
uri: CellUri.generate(notebook.uri, 3),
source: ['barLang-document2'],
language: 'barLang',
cellKind: CellKind.Code,
outputs: [],
}]]]
});
assertLines(mixedDoc, 'fooLang-document', 'barLang-document', 'barLang-document2');
assertLines(fooLangDoc, 'fooLang-document');
assertLines(barLangDoc, 'barLang-document', 'barLang-document2');
});
function assertOffsetAtPosition(doc: vscode.NotebookConcatTextDocument, offset: number, expected: { line: number, character: number }, reverse = true) {
const actual = doc.positionAt(offset);
assert.equal(actual.line, expected.line);
assert.equal(actual.character, expected.character);
if (reverse) {
const actualOffset = doc.offsetAt(actual);
assert.equal(actualOffset, offset);
}
}
test('offsetAt(position) <-> positionAt(offset)', function () {
extHostNotebooks.$acceptModelChanged(notebookUri, {
versionId: notebook.versionId + 1,
changes: [[0, 0, [{
handle: 1,
uri: CellUri.generate(notebook.uri, 1),
source: ['Hello', 'World', 'Hello World!'],
language: 'test',
cellKind: CellKind.Code,
outputs: [],
}, {
handle: 2,
uri: CellUri.generate(notebook.uri, 2),
source: ['Hallo', 'Welt', 'Hallo Welt!'],
language: 'test',
cellKind: CellKind.Code,
outputs: [],
}]]]
});
assert.equal(notebook.cells.length, 1 + 2); // markdown and code
let doc = new ExtHostNotebookConcatDocument(extHostNotebooks, extHostDocuments, notebook, undefined);
assertLines(doc, 'Hello', 'World', 'Hello World!', 'Hallo', 'Welt', 'Hallo Welt!');
assertOffsetAtPosition(doc, 0, { line: 0, character: 0 });
assertOffsetAtPosition(doc, 1, { line: 0, character: 1 });
assertOffsetAtPosition(doc, 9, { line: 1, character: 3 });
assertOffsetAtPosition(doc, 32, { line: 4, character: 1 });
assertOffsetAtPosition(doc, 47, { line: 5, character: 11 });
});
function assertLocationAtPosition(doc: vscode.NotebookConcatTextDocument, pos: { line: number, character: number }, expected: { uri: URI, line: number, character: number }, reverse = true) {
const actual = doc.locationAt(new Position(pos.line, pos.character));
assert.equal(actual.uri.toString(), expected.uri.toString());
assert.equal(actual.range.start.line, expected.line);
assert.equal(actual.range.end.line, expected.line);
assert.equal(actual.range.start.character, expected.character);
assert.equal(actual.range.end.character, expected.character);
if (reverse) {
const actualPos = doc.positionAt(actual);
assert.equal(actualPos.line, pos.line);
assert.equal(actualPos.character, pos.character);
}
}
test('locationAt(position) <-> positionAt(location)', function () {
extHostNotebooks.$acceptModelChanged(notebookUri, {
versionId: notebook.versionId + 1,
changes: [[0, 0, [{
handle: 1,
uri: CellUri.generate(notebook.uri, 1),
source: ['Hello', 'World', 'Hello World!'],
language: 'test',
cellKind: CellKind.Code,
outputs: [],
}, {
handle: 2,
uri: CellUri.generate(notebook.uri, 2),
source: ['Hallo', 'Welt', 'Hallo Welt!'],
language: 'test',
cellKind: CellKind.Code,
outputs: [],
}]]]
});
assert.equal(notebook.cells.length, 1 + 2); // markdown and code
let doc = new ExtHostNotebookConcatDocument(extHostNotebooks, extHostDocuments, notebook, undefined);
assertLines(doc, 'Hello', 'World', 'Hello World!', 'Hallo', 'Welt', 'Hallo Welt!');
assertLocationAtPosition(doc, { line: 0, character: 0 }, { uri: notebook.cells[0].uri, line: 0, character: 0 });
assertLocationAtPosition(doc, { line: 2, character: 0 }, { uri: notebook.cells[0].uri, line: 2, character: 0 });
assertLocationAtPosition(doc, { line: 2, character: 12 }, { uri: notebook.cells[0].uri, line: 2, character: 12 });
assertLocationAtPosition(doc, { line: 3, character: 0 }, { uri: notebook.cells[1].uri, line: 0, character: 0 });
assertLocationAtPosition(doc, { line: 5, character: 0 }, { uri: notebook.cells[1].uri, line: 2, character: 0 });
assertLocationAtPosition(doc, { line: 5, character: 11 }, { uri: notebook.cells[1].uri, line: 2, character: 11 });
});
test('getText(range)', function () {
extHostNotebooks.$acceptModelChanged(notebookUri, {
versionId: notebook.versionId + 1,
changes: [[0, 0, [{
handle: 1,
uri: CellUri.generate(notebook.uri, 1),
source: ['Hello', 'World', 'Hello World!'],
language: 'test',
cellKind: CellKind.Code,
outputs: [],
}, {
handle: 2,
uri: CellUri.generate(notebook.uri, 2),
source: ['Hallo', 'Welt', 'Hallo Welt!'],
language: 'test',
cellKind: CellKind.Code,
outputs: [],
}]]]
});
assert.equal(notebook.cells.length, 1 + 2); // markdown and code
let doc = new ExtHostNotebookConcatDocument(extHostNotebooks, extHostDocuments, notebook, undefined);
assertLines(doc, 'Hello', 'World', 'Hello World!', 'Hallo', 'Welt', 'Hallo Welt!');
assert.equal(doc.getText(new Range(0, 0, 0, 0)), '');
assert.equal(doc.getText(new Range(0, 0, 1, 0)), 'Hello\n');
assert.equal(doc.getText(new Range(2, 0, 4, 0)), 'Hello World!\nHallo\n');
});
});
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册