252 lines
12 KiB
JavaScript
252 lines
12 KiB
JavaScript
/* --------------------------------------------------------------------------------------------
|
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
* ------------------------------------------------------------------------------------------ */
|
|
'use strict';
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.NotebookDocuments = exports.NotebookSyncFeature = void 0;
|
|
const vscode_languageserver_protocol_1 = require("vscode-languageserver-protocol");
|
|
const textDocuments_1 = require("./textDocuments");
|
|
const NotebookSyncFeature = (Base) => {
|
|
return class extends Base {
|
|
get synchronization() {
|
|
return {
|
|
onDidOpenNotebookDocument: (handler) => {
|
|
return this.connection.onNotification(vscode_languageserver_protocol_1.DidOpenNotebookDocumentNotification.type, (params) => {
|
|
handler(params);
|
|
});
|
|
},
|
|
onDidChangeNotebookDocument: (handler) => {
|
|
return this.connection.onNotification(vscode_languageserver_protocol_1.DidChangeNotebookDocumentNotification.type, (params) => {
|
|
handler(params);
|
|
});
|
|
},
|
|
onDidSaveNotebookDocument: (handler) => {
|
|
return this.connection.onNotification(vscode_languageserver_protocol_1.DidSaveNotebookDocumentNotification.type, (params) => {
|
|
handler(params);
|
|
});
|
|
},
|
|
onDidCloseNotebookDocument: (handler) => {
|
|
return this.connection.onNotification(vscode_languageserver_protocol_1.DidCloseNotebookDocumentNotification.type, (params) => {
|
|
handler(params);
|
|
});
|
|
}
|
|
};
|
|
}
|
|
};
|
|
};
|
|
exports.NotebookSyncFeature = NotebookSyncFeature;
|
|
class CellTextDocumentConnection {
|
|
onDidOpenTextDocument(handler) {
|
|
this.openHandler = handler;
|
|
return vscode_languageserver_protocol_1.Disposable.create(() => { this.openHandler = undefined; });
|
|
}
|
|
openTextDocument(params) {
|
|
this.openHandler && this.openHandler(params);
|
|
}
|
|
onDidChangeTextDocument(handler) {
|
|
this.changeHandler = handler;
|
|
return vscode_languageserver_protocol_1.Disposable.create(() => { this.changeHandler = handler; });
|
|
}
|
|
changeTextDocument(params) {
|
|
this.changeHandler && this.changeHandler(params);
|
|
}
|
|
onDidCloseTextDocument(handler) {
|
|
this.closeHandler = handler;
|
|
return vscode_languageserver_protocol_1.Disposable.create(() => { this.closeHandler = undefined; });
|
|
}
|
|
closeTextDocument(params) {
|
|
this.closeHandler && this.closeHandler(params);
|
|
}
|
|
onWillSaveTextDocument() {
|
|
return CellTextDocumentConnection.NULL_DISPOSE;
|
|
}
|
|
onWillSaveTextDocumentWaitUntil() {
|
|
return CellTextDocumentConnection.NULL_DISPOSE;
|
|
}
|
|
onDidSaveTextDocument() {
|
|
return CellTextDocumentConnection.NULL_DISPOSE;
|
|
}
|
|
}
|
|
CellTextDocumentConnection.NULL_DISPOSE = Object.freeze({ dispose: () => { } });
|
|
class NotebookDocuments {
|
|
constructor(configurationOrTextDocuments) {
|
|
if (configurationOrTextDocuments instanceof textDocuments_1.TextDocuments) {
|
|
this._cellTextDocuments = configurationOrTextDocuments;
|
|
}
|
|
else {
|
|
this._cellTextDocuments = new textDocuments_1.TextDocuments(configurationOrTextDocuments);
|
|
}
|
|
this.notebookDocuments = new Map();
|
|
this.notebookCellMap = new Map();
|
|
this._onDidOpen = new vscode_languageserver_protocol_1.Emitter();
|
|
this._onDidChange = new vscode_languageserver_protocol_1.Emitter();
|
|
this._onDidSave = new vscode_languageserver_protocol_1.Emitter();
|
|
this._onDidClose = new vscode_languageserver_protocol_1.Emitter();
|
|
}
|
|
get cellTextDocuments() {
|
|
return this._cellTextDocuments;
|
|
}
|
|
getCellTextDocument(cell) {
|
|
return this._cellTextDocuments.get(cell.document);
|
|
}
|
|
getNotebookDocument(uri) {
|
|
return this.notebookDocuments.get(uri);
|
|
}
|
|
getNotebookCell(uri) {
|
|
const value = this.notebookCellMap.get(uri);
|
|
return value && value[0];
|
|
}
|
|
findNotebookDocumentForCell(cell) {
|
|
const key = typeof cell === 'string' ? cell : cell.document;
|
|
const value = this.notebookCellMap.get(key);
|
|
return value && value[1];
|
|
}
|
|
get onDidOpen() {
|
|
return this._onDidOpen.event;
|
|
}
|
|
get onDidSave() {
|
|
return this._onDidSave.event;
|
|
}
|
|
get onDidChange() {
|
|
return this._onDidChange.event;
|
|
}
|
|
get onDidClose() {
|
|
return this._onDidClose.event;
|
|
}
|
|
/**
|
|
* Listens for `low level` notification on the given connection to
|
|
* update the notebook documents managed by this instance.
|
|
*
|
|
* Please note that the connection only provides handlers not an event model. Therefore
|
|
* listening on a connection will overwrite the following handlers on a connection:
|
|
* `onDidOpenNotebookDocument`, `onDidChangeNotebookDocument`, `onDidSaveNotebookDocument`,
|
|
* and `onDidCloseNotebookDocument`.
|
|
*
|
|
* @param connection The connection to listen on.
|
|
*/
|
|
listen(connection) {
|
|
const cellTextDocumentConnection = new CellTextDocumentConnection();
|
|
const disposables = [];
|
|
disposables.push(this.cellTextDocuments.listen(cellTextDocumentConnection));
|
|
disposables.push(connection.notebooks.synchronization.onDidOpenNotebookDocument((params) => {
|
|
this.notebookDocuments.set(params.notebookDocument.uri, params.notebookDocument);
|
|
for (const cellTextDocument of params.cellTextDocuments) {
|
|
cellTextDocumentConnection.openTextDocument({ textDocument: cellTextDocument });
|
|
}
|
|
this.updateCellMap(params.notebookDocument);
|
|
this._onDidOpen.fire(params.notebookDocument);
|
|
}));
|
|
disposables.push(connection.notebooks.synchronization.onDidChangeNotebookDocument((params) => {
|
|
const notebookDocument = this.notebookDocuments.get(params.notebookDocument.uri);
|
|
if (notebookDocument === undefined) {
|
|
return;
|
|
}
|
|
notebookDocument.version = params.notebookDocument.version;
|
|
const oldMetadata = notebookDocument.metadata;
|
|
let metadataChanged = false;
|
|
const change = params.change;
|
|
if (change.metadata !== undefined) {
|
|
metadataChanged = true;
|
|
notebookDocument.metadata = change.metadata;
|
|
}
|
|
const opened = [];
|
|
const closed = [];
|
|
const data = [];
|
|
const text = [];
|
|
if (change.cells !== undefined) {
|
|
const changedCells = change.cells;
|
|
if (changedCells.structure !== undefined) {
|
|
const array = changedCells.structure.array;
|
|
notebookDocument.cells.splice(array.start, array.deleteCount, ...(array.cells !== undefined ? array.cells : []));
|
|
// Additional open cell text documents.
|
|
if (changedCells.structure.didOpen !== undefined) {
|
|
for (const open of changedCells.structure.didOpen) {
|
|
cellTextDocumentConnection.openTextDocument({ textDocument: open });
|
|
opened.push(open.uri);
|
|
}
|
|
}
|
|
// Additional closed cell test documents.
|
|
if (changedCells.structure.didClose) {
|
|
for (const close of changedCells.structure.didClose) {
|
|
cellTextDocumentConnection.closeTextDocument({ textDocument: close });
|
|
closed.push(close.uri);
|
|
}
|
|
}
|
|
}
|
|
if (changedCells.data !== undefined) {
|
|
const cellUpdates = new Map(changedCells.data.map(cell => [cell.document, cell]));
|
|
for (let i = 0; i <= notebookDocument.cells.length; i++) {
|
|
const change = cellUpdates.get(notebookDocument.cells[i].document);
|
|
if (change !== undefined) {
|
|
const old = notebookDocument.cells.splice(i, 1, change);
|
|
data.push({ old: old[0], new: change });
|
|
cellUpdates.delete(change.document);
|
|
if (cellUpdates.size === 0) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (changedCells.textContent !== undefined) {
|
|
for (const cellTextDocument of changedCells.textContent) {
|
|
cellTextDocumentConnection.changeTextDocument({ textDocument: cellTextDocument.document, contentChanges: cellTextDocument.changes });
|
|
text.push(cellTextDocument.document.uri);
|
|
}
|
|
}
|
|
}
|
|
// Update internal data structure.
|
|
this.updateCellMap(notebookDocument);
|
|
const changeEvent = { notebookDocument };
|
|
if (metadataChanged) {
|
|
changeEvent.metadata = { old: oldMetadata, new: notebookDocument.metadata };
|
|
}
|
|
const added = [];
|
|
for (const open of opened) {
|
|
added.push(this.getNotebookCell(open));
|
|
}
|
|
const removed = [];
|
|
for (const close of closed) {
|
|
removed.push(this.getNotebookCell(close));
|
|
}
|
|
const textContent = [];
|
|
for (const change of text) {
|
|
textContent.push(this.getNotebookCell(change));
|
|
}
|
|
if (added.length > 0 || removed.length > 0 || data.length > 0 || textContent.length > 0) {
|
|
changeEvent.cells = { added, removed, changed: { data, textContent } };
|
|
}
|
|
if (changeEvent.metadata !== undefined || changeEvent.cells !== undefined) {
|
|
this._onDidChange.fire(changeEvent);
|
|
}
|
|
}));
|
|
disposables.push(connection.notebooks.synchronization.onDidSaveNotebookDocument((params) => {
|
|
const notebookDocument = this.notebookDocuments.get(params.notebookDocument.uri);
|
|
if (notebookDocument === undefined) {
|
|
return;
|
|
}
|
|
this._onDidSave.fire(notebookDocument);
|
|
}));
|
|
disposables.push(connection.notebooks.synchronization.onDidCloseNotebookDocument((params) => {
|
|
const notebookDocument = this.notebookDocuments.get(params.notebookDocument.uri);
|
|
if (notebookDocument === undefined) {
|
|
return;
|
|
}
|
|
this._onDidClose.fire(notebookDocument);
|
|
for (const cellTextDocument of params.cellTextDocuments) {
|
|
cellTextDocumentConnection.closeTextDocument({ textDocument: cellTextDocument });
|
|
}
|
|
this.notebookDocuments.delete(params.notebookDocument.uri);
|
|
for (const cell of notebookDocument.cells) {
|
|
this.notebookCellMap.delete(cell.document);
|
|
}
|
|
}));
|
|
return vscode_languageserver_protocol_1.Disposable.create(() => { disposables.forEach(disposable => disposable.dispose()); });
|
|
}
|
|
updateCellMap(notebookDocument) {
|
|
for (const cell of notebookDocument.cells) {
|
|
this.notebookCellMap.set(cell.document, [cell, notebookDocument]);
|
|
}
|
|
}
|
|
}
|
|
exports.NotebookDocuments = NotebookDocuments;
|