@difizen/libro-language-client 0.1.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +0 -0
- package/es/common/api.d.ts +38 -0
- package/es/common/api.d.ts.map +1 -0
- package/es/common/api.js +9 -0
- package/es/common/callHierarchy.d.ts +39 -0
- package/es/common/callHierarchy.d.ts.map +1 -0
- package/es/common/callHierarchy.js +139 -0
- package/es/common/client.d.ts +482 -0
- package/es/common/client.d.ts.map +1 -0
- package/es/common/client.js +2731 -0
- package/es/common/codeAction.d.ts +22 -0
- package/es/common/codeAction.d.ts.map +1 -0
- package/es/common/codeAction.js +149 -0
- package/es/common/codeConverter.d.ts +81 -0
- package/es/common/codeConverter.d.ts.map +1 -0
- package/es/common/codeConverter.js +1040 -0
- package/es/common/codeLens.d.ts +26 -0
- package/es/common/codeLens.d.ts.map +1 -0
- package/es/common/codeLens.js +125 -0
- package/es/common/colorProvider.d.ts +27 -0
- package/es/common/colorProvider.d.ts.map +1 -0
- package/es/common/colorProvider.js +104 -0
- package/es/common/completion.d.ts +22 -0
- package/es/common/completion.d.ts.map +1 -0
- package/es/common/completion.js +130 -0
- package/es/common/configuration.d.ts +71 -0
- package/es/common/configuration.d.ts.map +1 -0
- package/es/common/configuration.js +292 -0
- package/es/common/declaration.d.ts +18 -0
- package/es/common/declaration.d.ts.map +1 -0
- package/es/common/declaration.js +88 -0
- package/es/common/definition.d.ts +18 -0
- package/es/common/definition.d.ts.map +1 -0
- package/es/common/definition.js +80 -0
- package/es/common/diagnostic.d.ts +125 -0
- package/es/common/diagnostic.d.ts.map +1 -0
- package/es/common/diagnostic.js +1442 -0
- package/es/common/documentHighlight.d.ts +17 -0
- package/es/common/documentHighlight.d.ts.map +1 -0
- package/es/common/documentHighlight.js +73 -0
- package/es/common/documentLink.d.ts +21 -0
- package/es/common/documentLink.d.ts.map +1 -0
- package/es/common/documentLink.js +90 -0
- package/es/common/documentSymbol.d.ts +20 -0
- package/es/common/documentSymbol.d.ts.map +1 -0
- package/es/common/documentSymbol.js +134 -0
- package/es/common/executeCommand.d.ts +22 -0
- package/es/common/executeCommand.d.ts.map +1 -0
- package/es/common/executeCommand.js +117 -0
- package/es/common/features.d.ts +421 -0
- package/es/common/features.d.ts.map +1 -0
- package/es/common/features.js +576 -0
- package/es/common/fileOperations.d.ts +118 -0
- package/es/common/fileOperations.d.ts.map +1 -0
- package/es/common/fileOperations.js +705 -0
- package/es/common/fileSystemWatcher.d.ts +19 -0
- package/es/common/fileSystemWatcher.d.ts.map +1 -0
- package/es/common/fileSystemWatcher.js +173 -0
- package/es/common/foldingRange.d.ts +22 -0
- package/es/common/foldingRange.d.ts.map +1 -0
- package/es/common/foldingRange.js +127 -0
- package/es/common/formatting.d.ts +41 -0
- package/es/common/formatting.d.ts.map +1 -0
- package/es/common/formatting.js +233 -0
- package/es/common/hover.d.ts +18 -0
- package/es/common/hover.d.ts.map +1 -0
- package/es/common/hover.js +80 -0
- package/es/common/implementation.d.ts +18 -0
- package/es/common/implementation.d.ts.map +1 -0
- package/es/common/implementation.js +88 -0
- package/es/common/inlayHint.d.ts +23 -0
- package/es/common/inlayHint.d.ts.map +1 -0
- package/es/common/inlayHint.js +187 -0
- package/es/common/inlineCompletion.d.ts +20 -0
- package/es/common/inlineCompletion.d.ts.map +1 -0
- package/es/common/inlineCompletion.js +74 -0
- package/es/common/inlineValue.d.ts +21 -0
- package/es/common/inlineValue.d.ts.map +1 -0
- package/es/common/inlineValue.js +124 -0
- package/es/common/linkedEditingRange.d.ts +23 -0
- package/es/common/linkedEditingRange.d.ts.map +1 -0
- package/es/common/linkedEditingRange.js +94 -0
- package/es/common/notebook.d.ts +97 -0
- package/es/common/notebook.d.ts.map +1 -0
- package/es/common/notebook.js +1444 -0
- package/es/common/progress.d.ts +12 -0
- package/es/common/progress.d.ts.map +1 -0
- package/es/common/progress.js +75 -0
- package/es/common/progressPart.d.ts +25 -0
- package/es/common/progressPart.d.ts.map +1 -0
- package/es/common/progressPart.js +147 -0
- package/es/common/protocolCallHierarchyItem.d.ts +9 -0
- package/es/common/protocolCallHierarchyItem.d.ts.map +1 -0
- package/es/common/protocolCallHierarchyItem.js +34 -0
- package/es/common/protocolCodeAction.d.ts +7 -0
- package/es/common/protocolCodeAction.d.ts.map +1 -0
- package/es/common/protocolCodeAction.js +32 -0
- package/es/common/protocolCodeLens.d.ts +7 -0
- package/es/common/protocolCodeLens.d.ts.map +1 -0
- package/es/common/protocolCodeLens.js +29 -0
- package/es/common/protocolCompletionItem.d.ts +13 -0
- package/es/common/protocolCompletionItem.d.ts.map +1 -0
- package/es/common/protocolCompletionItem.js +29 -0
- package/es/common/protocolConverter.d.ts +174 -0
- package/es/common/protocolConverter.d.ts.map +1 -0
- package/es/common/protocolConverter.js +1982 -0
- package/es/common/protocolDiagnostic.d.ts +20 -0
- package/es/common/protocolDiagnostic.d.ts.map +1 -0
- package/es/common/protocolDiagnostic.js +46 -0
- package/es/common/protocolDocumentLink.d.ts +8 -0
- package/es/common/protocolDocumentLink.d.ts.map +1 -0
- package/es/common/protocolDocumentLink.js +29 -0
- package/es/common/protocolInlayHint.d.ts +8 -0
- package/es/common/protocolInlayHint.d.ts.map +1 -0
- package/es/common/protocolInlayHint.js +29 -0
- package/es/common/protocolTypeHierarchyItem.d.ts +9 -0
- package/es/common/protocolTypeHierarchyItem.d.ts.map +1 -0
- package/es/common/protocolTypeHierarchyItem.js +34 -0
- package/es/common/protocolWorkspaceSymbol.d.ts +9 -0
- package/es/common/protocolWorkspaceSymbol.d.ts.map +1 -0
- package/es/common/protocolWorkspaceSymbol.js +36 -0
- package/es/common/reference.d.ts +22 -0
- package/es/common/reference.d.ts.map +1 -0
- package/es/common/reference.js +78 -0
- package/es/common/rename.d.ts +29 -0
- package/es/common/rename.d.ts.map +1 -0
- package/es/common/rename.js +132 -0
- package/es/common/selectionRange.d.ts +18 -0
- package/es/common/selectionRange.d.ts.map +1 -0
- package/es/common/selectionRange.js +108 -0
- package/es/common/semanticTokens.d.ts +36 -0
- package/es/common/semanticTokens.d.ts.map +1 -0
- package/es/common/semanticTokens.js +226 -0
- package/es/common/signatureHelp.d.ts +18 -0
- package/es/common/signatureHelp.d.ts.map +1 -0
- package/es/common/signatureHelp.js +103 -0
- package/es/common/textSynchronization.d.ts +104 -0
- package/es/common/textSynchronization.d.ts.map +1 -0
- package/es/common/textSynchronization.js +771 -0
- package/es/common/typeDefinition.d.ts +18 -0
- package/es/common/typeDefinition.d.ts.map +1 -0
- package/es/common/typeDefinition.js +89 -0
- package/es/common/typeHierarchy.d.ts +33 -0
- package/es/common/typeHierarchy.d.ts.map +1 -0
- package/es/common/typeHierarchy.js +138 -0
- package/es/common/utils/async.d.ts +42 -0
- package/es/common/utils/async.d.ts.map +1 -0
- package/es/common/utils/async.js +441 -0
- package/es/common/utils/is.d.ts +13 -0
- package/es/common/utils/is.d.ts.map +1 -0
- package/es/common/utils/is.js +52 -0
- package/es/common/utils/uuid.d.ts +23 -0
- package/es/common/utils/uuid.d.ts.map +1 -0
- package/es/common/utils/uuid.js +85 -0
- package/es/common/vscodeAdaptor/convertor.d.ts +7 -0
- package/es/common/vscodeAdaptor/convertor.d.ts.map +1 -0
- package/es/common/vscodeAdaptor/convertor.js +66 -0
- package/es/common/vscodeAdaptor/diagnosticCollection.d.ts +33 -0
- package/es/common/vscodeAdaptor/diagnosticCollection.d.ts.map +1 -0
- package/es/common/vscodeAdaptor/diagnosticCollection.js +310 -0
- package/es/common/vscodeAdaptor/extHostTypes.d.ts +1496 -0
- package/es/common/vscodeAdaptor/extHostTypes.d.ts.map +1 -0
- package/es/common/vscodeAdaptor/extHostTypes.js +3825 -0
- package/es/common/vscodeAdaptor/fileWatcher.d.ts +19 -0
- package/es/common/vscodeAdaptor/fileWatcher.d.ts.map +1 -0
- package/es/common/vscodeAdaptor/fileWatcher.js +45 -0
- package/es/common/vscodeAdaptor/hostTypeUtil.d.ts +192 -0
- package/es/common/vscodeAdaptor/hostTypeUtil.d.ts.map +1 -0
- package/es/common/vscodeAdaptor/hostTypeUtil.js +566 -0
- package/es/common/vscodeAdaptor/libro-fs.d.ts +21 -0
- package/es/common/vscodeAdaptor/libro-fs.d.ts.map +1 -0
- package/es/common/vscodeAdaptor/libro-fs.js +64 -0
- package/es/common/vscodeAdaptor/libroWindow.d.ts +21 -0
- package/es/common/vscodeAdaptor/libroWindow.d.ts.map +1 -0
- package/es/common/vscodeAdaptor/libroWindow.js +75 -0
- package/es/common/vscodeAdaptor/libroWorkspace.d.ts +33 -0
- package/es/common/vscodeAdaptor/libroWorkspace.d.ts.map +1 -0
- package/es/common/vscodeAdaptor/libroWorkspace.js +250 -0
- package/es/common/vscodeAdaptor/lspEnv.d.ts +8 -0
- package/es/common/vscodeAdaptor/lspEnv.d.ts.map +1 -0
- package/es/common/vscodeAdaptor/lspEnv.js +31 -0
- package/es/common/vscodeAdaptor/monaco-converter.d.ts +229 -0
- package/es/common/vscodeAdaptor/monaco-converter.d.ts.map +1 -0
- package/es/common/vscodeAdaptor/monaco-converter.js +1613 -0
- package/es/common/vscodeAdaptor/monacoLanguages.d.ts +48 -0
- package/es/common/vscodeAdaptor/monacoLanguages.d.ts.map +1 -0
- package/es/common/vscodeAdaptor/monacoLanguages.js +484 -0
- package/es/common/vscodeAdaptor/services.d.ts +85 -0
- package/es/common/vscodeAdaptor/services.d.ts.map +1 -0
- package/es/common/vscodeAdaptor/services.js +3 -0
- package/es/common/vscodeAdaptor/typings.d.ts +10 -0
- package/es/common/vscodeAdaptor/util.d.ts +3 -0
- package/es/common/vscodeAdaptor/util.d.ts.map +1 -0
- package/es/common/vscodeAdaptor/util.js +6 -0
- package/es/common/vscodeAdaptor/vscodeAdaptor.d.ts +77 -0
- package/es/common/vscodeAdaptor/vscodeAdaptor.d.ts.map +1 -0
- package/es/common/vscodeAdaptor/vscodeAdaptor.js +124 -0
- package/es/common/workspaceFolder.d.ts +32 -0
- package/es/common/workspaceFolder.d.ts.map +1 -0
- package/es/common/workspaceFolder.js +204 -0
- package/es/common/workspaceSymbol.d.ts +21 -0
- package/es/common/workspaceSymbol.d.ts.map +1 -0
- package/es/common/workspaceSymbol.js +101 -0
- package/es/constants.d.ts +2 -0
- package/es/constants.d.ts.map +1 -0
- package/es/constants.js +1 -0
- package/es/index.d.ts +6 -0
- package/es/index.d.ts.map +1 -0
- package/es/index.js +5 -0
- package/es/libro-language-client-contribution.d.ts +10 -0
- package/es/libro-language-client-contribution.d.ts.map +1 -0
- package/es/libro-language-client-contribution.js +143 -0
- package/es/libro-language-client-manager.d.ts +34 -0
- package/es/libro-language-client-manager.d.ts.map +1 -0
- package/es/libro-language-client-manager.js +277 -0
- package/es/libro-language-client.d.ts +27 -0
- package/es/libro-language-client.d.ts.map +1 -0
- package/es/libro-language-client.js +141 -0
- package/es/module.d.ts +3 -0
- package/es/module.d.ts.map +1 -0
- package/es/module.js +13 -0
- package/package.json +69 -0
- package/src/common/api.ts +155 -0
- package/src/common/callHierarchy.ts +269 -0
- package/src/common/client.ts +3192 -0
- package/src/common/codeAction.ts +237 -0
- package/src/common/codeConverter.ts +1409 -0
- package/src/common/codeLens.ts +188 -0
- package/src/common/colorProvider.ts +192 -0
- package/src/common/completion.ts +281 -0
- package/src/common/configuration.ts +338 -0
- package/src/common/declaration.ts +140 -0
- package/src/common/definition.ts +138 -0
- package/src/common/diagnostic.ts +1408 -0
- package/src/common/documentHighlight.ts +140 -0
- package/src/common/documentLink.ts +180 -0
- package/src/common/documentSymbol.ts +186 -0
- package/src/common/executeCommand.ts +129 -0
- package/src/common/features.ts +1157 -0
- package/src/common/fileOperations.ts +635 -0
- package/src/common/fileSystemWatcher.ts +184 -0
- package/src/common/foldingRange.ts +160 -0
- package/src/common/formatting.ts +465 -0
- package/src/common/hover.ts +133 -0
- package/src/common/implementation.ts +142 -0
- package/src/common/inlayHint.ts +201 -0
- package/src/common/inlineCompletion.ts +160 -0
- package/src/common/inlineValue.ts +158 -0
- package/src/common/linkedEditingRange.ts +141 -0
- package/src/common/notebook.ts +1443 -0
- package/src/common/progress.ts +61 -0
- package/src/common/progressPart.ts +151 -0
- package/src/common/protocolCallHierarchyItem.ts +29 -0
- package/src/common/protocolCodeAction.ts +17 -0
- package/src/common/protocolCodeLens.ts +15 -0
- package/src/common/protocolCompletionItem.ts +22 -0
- package/src/common/protocolConverter.ts +2627 -0
- package/src/common/protocolDiagnostic.ts +47 -0
- package/src/common/protocolDocumentLink.ts +17 -0
- package/src/common/protocolInlayHint.ts +21 -0
- package/src/common/protocolTypeHierarchyItem.ts +29 -0
- package/src/common/protocolWorkspaceSymbol.ts +39 -0
- package/src/common/reference.ts +144 -0
- package/src/common/rename.ts +230 -0
- package/src/common/selectionRange.ts +136 -0
- package/src/common/semanticTokens.ts +383 -0
- package/src/common/signatureHelp.ts +170 -0
- package/src/common/textSynchronization.ts +819 -0
- package/src/common/typeDefinition.ts +146 -0
- package/src/common/typeHierarchy.ts +248 -0
- package/src/common/utils/async.ts +354 -0
- package/src/common/utils/is.ts +63 -0
- package/src/common/utils/uuid.ts +136 -0
- package/src/common/vscodeAdaptor/convertor.ts +73 -0
- package/src/common/vscodeAdaptor/diagnosticCollection.ts +238 -0
- package/src/common/vscodeAdaptor/extHostTypes.ts +4498 -0
- package/src/common/vscodeAdaptor/fileWatcher.ts +36 -0
- package/src/common/vscodeAdaptor/hostTypeUtil.ts +539 -0
- package/src/common/vscodeAdaptor/libro-fs.ts +51 -0
- package/src/common/vscodeAdaptor/libroWindow.ts +85 -0
- package/src/common/vscodeAdaptor/libroWorkspace.ts +261 -0
- package/src/common/vscodeAdaptor/lspEnv.ts +16 -0
- package/src/common/vscodeAdaptor/monaco-converter.ts +1800 -0
- package/src/common/vscodeAdaptor/monacoLanguages.ts +511 -0
- package/src/common/vscodeAdaptor/services.ts +278 -0
- package/src/common/vscodeAdaptor/typings.d.ts +10 -0
- package/src/common/vscodeAdaptor/util.ts +7 -0
- package/src/common/vscodeAdaptor/vscodeAdaptor.ts +122 -0
- package/src/common/workspaceFolder.ts +236 -0
- package/src/common/workspaceSymbol.ts +166 -0
- package/src/constants.ts +1 -0
- package/src/index.spec.ts +7 -0
- package/src/index.ts +5 -0
- package/src/libro-language-client-contribution.ts +49 -0
- package/src/libro-language-client-manager.ts +131 -0
- package/src/libro-language-client.ts +100 -0
- package/src/module.ts +19 -0
|
@@ -0,0 +1,1443 @@
|
|
|
1
|
+
/* eslint-disable no-inner-declarations */
|
|
2
|
+
/* --------------------------------------------------------------------------------------------
|
|
3
|
+
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
4
|
+
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
5
|
+
* ------------------------------------------------------------------------------------------ */
|
|
6
|
+
|
|
7
|
+
import * as proto from '@difizen/vscode-languageserver-protocol';
|
|
8
|
+
import type {
|
|
9
|
+
StaticRegistrationOptions,
|
|
10
|
+
NotebookDocumentFilter,
|
|
11
|
+
TextDocumentItem,
|
|
12
|
+
NotebookCellTextDocumentFilter,
|
|
13
|
+
LSPAny,
|
|
14
|
+
} from '@difizen/vscode-languageserver-protocol';
|
|
15
|
+
import * as minimatch from 'minimatch';
|
|
16
|
+
import type {
|
|
17
|
+
TextDocument,
|
|
18
|
+
Disposable,
|
|
19
|
+
NotebookDocument,
|
|
20
|
+
NotebookCell,
|
|
21
|
+
TextDocumentChangeEvent,
|
|
22
|
+
NotebookCellExecutionSummary,
|
|
23
|
+
DocumentSelector,
|
|
24
|
+
NotebookDocumentChangeEvent,
|
|
25
|
+
} from 'vscode';
|
|
26
|
+
|
|
27
|
+
import { LibroCellURIScheme } from '../constants.js';
|
|
28
|
+
|
|
29
|
+
import type * as _c2p from './codeConverter.js';
|
|
30
|
+
import type {
|
|
31
|
+
DynamicFeature,
|
|
32
|
+
FeatureClient,
|
|
33
|
+
RegistrationData,
|
|
34
|
+
FeatureState,
|
|
35
|
+
} from './features.js';
|
|
36
|
+
import * as Is from './utils/is.js';
|
|
37
|
+
import * as UUID from './utils/uuid.js';
|
|
38
|
+
import { NotebookCellKind } from './vscodeAdaptor/vscodeAdaptor.js';
|
|
39
|
+
import { workspace, languages } from './vscodeAdaptor/vscodeAdaptor.js';
|
|
40
|
+
|
|
41
|
+
function ensure<T, K extends keyof T>(target: T, key: K): T[K] {
|
|
42
|
+
if (target[key] === void 0) {
|
|
43
|
+
target[key] = {} as any;
|
|
44
|
+
}
|
|
45
|
+
return target[key];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
type $LSPObject = { [key: string]: LSPAny };
|
|
49
|
+
type $LSPArray = LSPAny[];
|
|
50
|
+
|
|
51
|
+
namespace Converter {
|
|
52
|
+
export namespace c2p {
|
|
53
|
+
export function asVersionedNotebookDocumentIdentifier(
|
|
54
|
+
notebookDocument: NotebookDocument,
|
|
55
|
+
base: _c2p.Converter,
|
|
56
|
+
): proto.VersionedNotebookDocumentIdentifier {
|
|
57
|
+
return {
|
|
58
|
+
version: notebookDocument.version,
|
|
59
|
+
uri: base.asUri(notebookDocument.uri),
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
export function asNotebookDocument(
|
|
63
|
+
notebookDocument: NotebookDocument,
|
|
64
|
+
cells: NotebookCell[],
|
|
65
|
+
base: _c2p.Converter,
|
|
66
|
+
): proto.NotebookDocument {
|
|
67
|
+
const result = proto.NotebookDocument.create(
|
|
68
|
+
base.asUri(notebookDocument.uri),
|
|
69
|
+
notebookDocument.notebookType,
|
|
70
|
+
notebookDocument.version,
|
|
71
|
+
asNotebookCells(cells, base),
|
|
72
|
+
);
|
|
73
|
+
if (Object.keys(notebookDocument.metadata).length > 0) {
|
|
74
|
+
result.metadata = asMetadata(notebookDocument.metadata);
|
|
75
|
+
}
|
|
76
|
+
return result;
|
|
77
|
+
}
|
|
78
|
+
export function asNotebookCells(
|
|
79
|
+
cells: NotebookCell[],
|
|
80
|
+
base: _c2p.Converter,
|
|
81
|
+
): proto.NotebookCell[] {
|
|
82
|
+
return cells.map((cell) => asNotebookCell(cell, base));
|
|
83
|
+
}
|
|
84
|
+
export function asMetadata(metadata: { [key: string]: any }): $LSPObject {
|
|
85
|
+
const seen: Set<any> = new Set();
|
|
86
|
+
return deepCopy(seen, metadata);
|
|
87
|
+
}
|
|
88
|
+
export function asNotebookCell(
|
|
89
|
+
cell: NotebookCell,
|
|
90
|
+
base: _c2p.Converter,
|
|
91
|
+
): proto.NotebookCell {
|
|
92
|
+
const result = proto.NotebookCell.create(
|
|
93
|
+
asNotebookCellKind(cell.kind),
|
|
94
|
+
base.asUri(cell.document.uri),
|
|
95
|
+
);
|
|
96
|
+
if (Object.keys(cell.metadata).length > 0) {
|
|
97
|
+
result.metadata = asMetadata(cell.metadata);
|
|
98
|
+
}
|
|
99
|
+
if (
|
|
100
|
+
cell.executionSummary !== undefined &&
|
|
101
|
+
Is.number(cell.executionSummary.executionOrder) &&
|
|
102
|
+
Is.boolean(cell.executionSummary.success)
|
|
103
|
+
) {
|
|
104
|
+
result.executionSummary = {
|
|
105
|
+
executionOrder: cell.executionSummary.executionOrder,
|
|
106
|
+
success: cell.executionSummary.success,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
return result;
|
|
110
|
+
}
|
|
111
|
+
function asNotebookCellKind(kind: NotebookCellKind): proto.NotebookCellKind {
|
|
112
|
+
switch (kind) {
|
|
113
|
+
case NotebookCellKind.Markup:
|
|
114
|
+
return proto.NotebookCellKind.Markup;
|
|
115
|
+
case NotebookCellKind.Code:
|
|
116
|
+
return proto.NotebookCellKind.Code;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
function deepCopy(seen: Set<any>, value: { [key: string]: any }): $LSPObject;
|
|
120
|
+
function deepCopy(seen: Set<any>, value: any[]): $LSPArray;
|
|
121
|
+
function deepCopy(
|
|
122
|
+
seen: Set<any>,
|
|
123
|
+
value: { [key: string]: any } | any[],
|
|
124
|
+
): $LSPArray | $LSPObject {
|
|
125
|
+
if (seen.has(value)) {
|
|
126
|
+
throw new Error(`Can't deep copy cyclic structures.`);
|
|
127
|
+
}
|
|
128
|
+
if (Array.isArray(value)) {
|
|
129
|
+
const result: $LSPArray = [];
|
|
130
|
+
for (const elem of value) {
|
|
131
|
+
if ((elem !== null && typeof elem === 'object') || Array.isArray(elem)) {
|
|
132
|
+
result.push(deepCopy(seen, elem));
|
|
133
|
+
} else {
|
|
134
|
+
if (elem instanceof RegExp) {
|
|
135
|
+
throw new Error(`Can't transfer regular expressions to the server`);
|
|
136
|
+
}
|
|
137
|
+
result.push(elem);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return result;
|
|
141
|
+
} else {
|
|
142
|
+
const props = Object.keys(value);
|
|
143
|
+
const result: $LSPObject = Object.create(null);
|
|
144
|
+
for (const prop of props) {
|
|
145
|
+
const elem = value[prop];
|
|
146
|
+
if ((elem !== null && typeof elem === 'object') || Array.isArray(elem)) {
|
|
147
|
+
result[prop] = deepCopy(seen, elem);
|
|
148
|
+
} else {
|
|
149
|
+
if (elem instanceof RegExp) {
|
|
150
|
+
throw new Error(`Can't transfer regular expressions to the server`);
|
|
151
|
+
}
|
|
152
|
+
result[prop] = elem;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return result;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
type TextContent = Required<
|
|
159
|
+
Required<Required<proto.NotebookDocumentChangeEvent>['cells']>['textContent']
|
|
160
|
+
>[0];
|
|
161
|
+
export function asTextContentChange(
|
|
162
|
+
event: TextDocumentChangeEvent,
|
|
163
|
+
base: _c2p.Converter,
|
|
164
|
+
): TextContent {
|
|
165
|
+
const params = base.asChangeTextDocumentParams(
|
|
166
|
+
event,
|
|
167
|
+
event.document.uri,
|
|
168
|
+
event.document.version,
|
|
169
|
+
);
|
|
170
|
+
return { document: params.textDocument, changes: params.contentChanges };
|
|
171
|
+
}
|
|
172
|
+
export function asNotebookDocumentChangeEvent(
|
|
173
|
+
event: VNotebookDocumentChangeEvent,
|
|
174
|
+
base: _c2p.Converter,
|
|
175
|
+
): proto.NotebookDocumentChangeEvent {
|
|
176
|
+
const result: proto.NotebookDocumentChangeEvent = Object.create(null);
|
|
177
|
+
if (event.metadata) {
|
|
178
|
+
result.metadata = Converter.c2p.asMetadata(event.metadata);
|
|
179
|
+
}
|
|
180
|
+
if (event.cells !== undefined) {
|
|
181
|
+
const cells: Required<proto.NotebookDocumentChangeEvent>['cells'] =
|
|
182
|
+
Object.create(null);
|
|
183
|
+
const changedCells = event.cells;
|
|
184
|
+
if (changedCells.structure) {
|
|
185
|
+
cells.structure = {
|
|
186
|
+
array: {
|
|
187
|
+
start: changedCells.structure.array.start,
|
|
188
|
+
deleteCount: changedCells.structure.array.deleteCount,
|
|
189
|
+
cells:
|
|
190
|
+
changedCells.structure.array.cells !== undefined
|
|
191
|
+
? changedCells.structure.array.cells.map((cell) =>
|
|
192
|
+
Converter.c2p.asNotebookCell(cell, base),
|
|
193
|
+
)
|
|
194
|
+
: undefined,
|
|
195
|
+
},
|
|
196
|
+
didOpen:
|
|
197
|
+
changedCells.structure.didOpen !== undefined
|
|
198
|
+
? changedCells.structure.didOpen.map(
|
|
199
|
+
(cell) => base.asOpenTextDocumentParams(cell.document).textDocument,
|
|
200
|
+
)
|
|
201
|
+
: undefined,
|
|
202
|
+
didClose:
|
|
203
|
+
changedCells.structure.didClose !== undefined
|
|
204
|
+
? changedCells.structure.didClose.map(
|
|
205
|
+
(cell) =>
|
|
206
|
+
base.asCloseTextDocumentParams(cell.document).textDocument,
|
|
207
|
+
)
|
|
208
|
+
: undefined,
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
if (changedCells.data !== undefined) {
|
|
212
|
+
cells.data = changedCells.data.map((cell) =>
|
|
213
|
+
Converter.c2p.asNotebookCell(cell, base),
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
if (changedCells.textContent !== undefined) {
|
|
217
|
+
cells.textContent = changedCells.textContent.map((event) =>
|
|
218
|
+
Converter.c2p.asTextContentChange(event, base),
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
if (Object.keys(cells).length > 0) {
|
|
222
|
+
result.cells = cells;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return result;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
namespace $NotebookCell {
|
|
231
|
+
type ComputeDiffReturnType = {
|
|
232
|
+
start: number;
|
|
233
|
+
deleteCount: number;
|
|
234
|
+
cells?: NotebookCell[];
|
|
235
|
+
};
|
|
236
|
+
export function computeDiff(
|
|
237
|
+
originalCells: NotebookCell[],
|
|
238
|
+
modifiedCells: NotebookCell[],
|
|
239
|
+
compareMetadata: boolean,
|
|
240
|
+
): ComputeDiffReturnType | undefined {
|
|
241
|
+
const originalLength = originalCells.length;
|
|
242
|
+
const modifiedLength = modifiedCells.length;
|
|
243
|
+
let startIndex = 0;
|
|
244
|
+
while (
|
|
245
|
+
startIndex < modifiedLength &&
|
|
246
|
+
startIndex < originalLength &&
|
|
247
|
+
equals(originalCells[startIndex], modifiedCells[startIndex], compareMetadata)
|
|
248
|
+
) {
|
|
249
|
+
startIndex++;
|
|
250
|
+
}
|
|
251
|
+
if (startIndex < modifiedLength && startIndex < originalLength) {
|
|
252
|
+
let originalEndIndex = originalLength - 1;
|
|
253
|
+
let modifiedEndIndex = modifiedLength - 1;
|
|
254
|
+
while (
|
|
255
|
+
originalEndIndex >= 0 &&
|
|
256
|
+
modifiedEndIndex >= 0 &&
|
|
257
|
+
equals(
|
|
258
|
+
originalCells[originalEndIndex],
|
|
259
|
+
modifiedCells[modifiedEndIndex],
|
|
260
|
+
compareMetadata,
|
|
261
|
+
)
|
|
262
|
+
) {
|
|
263
|
+
originalEndIndex--;
|
|
264
|
+
modifiedEndIndex--;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
const deleteCount = originalEndIndex + 1 - startIndex;
|
|
268
|
+
const newCells =
|
|
269
|
+
startIndex === modifiedEndIndex + 1
|
|
270
|
+
? undefined
|
|
271
|
+
: modifiedCells.slice(startIndex, modifiedEndIndex + 1);
|
|
272
|
+
return newCells !== undefined
|
|
273
|
+
? { start: startIndex, deleteCount, cells: newCells }
|
|
274
|
+
: { start: startIndex, deleteCount };
|
|
275
|
+
} else if (startIndex < modifiedLength) {
|
|
276
|
+
return {
|
|
277
|
+
start: startIndex,
|
|
278
|
+
deleteCount: 0,
|
|
279
|
+
cells: modifiedCells.slice(startIndex),
|
|
280
|
+
};
|
|
281
|
+
} else if (startIndex < originalLength) {
|
|
282
|
+
return { start: startIndex, deleteCount: originalLength - startIndex };
|
|
283
|
+
} else {
|
|
284
|
+
// The two arrays are the same.
|
|
285
|
+
return undefined;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* We only sync kind, document, execution and metadata to the server. So we only need to compare those.
|
|
291
|
+
*/
|
|
292
|
+
function equals(
|
|
293
|
+
one: NotebookCell,
|
|
294
|
+
other: NotebookCell,
|
|
295
|
+
compareMetaData = true,
|
|
296
|
+
): boolean {
|
|
297
|
+
if (
|
|
298
|
+
one.kind !== other.kind ||
|
|
299
|
+
one.document.uri.toString() !== other.document.uri.toString() ||
|
|
300
|
+
one.document.languageId !== other.document.languageId ||
|
|
301
|
+
!equalsExecution(one.executionSummary, other.executionSummary)
|
|
302
|
+
) {
|
|
303
|
+
return false;
|
|
304
|
+
}
|
|
305
|
+
return (
|
|
306
|
+
!compareMetaData ||
|
|
307
|
+
(compareMetaData && equalsMetadata(one.metadata, other.metadata))
|
|
308
|
+
);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
function equalsExecution(
|
|
312
|
+
one: NotebookCellExecutionSummary | undefined,
|
|
313
|
+
other: NotebookCellExecutionSummary | undefined,
|
|
314
|
+
): boolean {
|
|
315
|
+
if (one === other) {
|
|
316
|
+
return true;
|
|
317
|
+
}
|
|
318
|
+
if (one === undefined || other === undefined) {
|
|
319
|
+
return false;
|
|
320
|
+
}
|
|
321
|
+
return (
|
|
322
|
+
one.executionOrder === other.executionOrder &&
|
|
323
|
+
one.success === other.success &&
|
|
324
|
+
equalsTiming(one.timing, other.timing)
|
|
325
|
+
);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
function equalsTiming(
|
|
329
|
+
one: { startTime: number; endTime: number } | undefined,
|
|
330
|
+
other: { startTime: number; endTime: number } | undefined,
|
|
331
|
+
): boolean {
|
|
332
|
+
if (one === other) {
|
|
333
|
+
return true;
|
|
334
|
+
}
|
|
335
|
+
if (one === undefined || other === undefined) {
|
|
336
|
+
return false;
|
|
337
|
+
}
|
|
338
|
+
return one.startTime === other.startTime && one.endTime === other.endTime;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
function equalsMetadata(one: any, other: any | undefined): boolean {
|
|
342
|
+
if (one === other) {
|
|
343
|
+
return true;
|
|
344
|
+
}
|
|
345
|
+
if (one === null || one === undefined || other === null || other === undefined) {
|
|
346
|
+
return false;
|
|
347
|
+
}
|
|
348
|
+
if (typeof one !== typeof other) {
|
|
349
|
+
return false;
|
|
350
|
+
}
|
|
351
|
+
if (typeof one !== 'object') {
|
|
352
|
+
return false;
|
|
353
|
+
}
|
|
354
|
+
const oneArray = Array.isArray(one);
|
|
355
|
+
const otherArray = Array.isArray(other);
|
|
356
|
+
if (oneArray !== otherArray) {
|
|
357
|
+
return false;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
if (oneArray && otherArray) {
|
|
361
|
+
if (one.length !== other.length) {
|
|
362
|
+
return false;
|
|
363
|
+
}
|
|
364
|
+
for (let i = 0; i < one.length; i++) {
|
|
365
|
+
if (!equalsMetadata(one[i], other[i])) {
|
|
366
|
+
return false;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
if (isObjectLiteral(one) && isObjectLiteral(other)) {
|
|
371
|
+
const oneKeys = Object.keys(one);
|
|
372
|
+
const otherKeys = Object.keys(other);
|
|
373
|
+
|
|
374
|
+
if (oneKeys.length !== otherKeys.length) {
|
|
375
|
+
return false;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
oneKeys.sort();
|
|
379
|
+
otherKeys.sort();
|
|
380
|
+
if (!equalsMetadata(oneKeys, otherKeys)) {
|
|
381
|
+
return false;
|
|
382
|
+
}
|
|
383
|
+
for (let i = 0; i < oneKeys.length; i++) {
|
|
384
|
+
const prop = oneKeys[i];
|
|
385
|
+
if (!equalsMetadata((one as $LSPObject)[prop], (other as $LSPObject)[prop])) {
|
|
386
|
+
return false;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
return true;
|
|
390
|
+
}
|
|
391
|
+
return false;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
export function isObjectLiteral(value: any): value is object {
|
|
395
|
+
return value !== null && typeof value === 'object';
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
namespace $NotebookDocumentFilter {
|
|
400
|
+
export function matchNotebook(
|
|
401
|
+
filter: string | NotebookDocumentFilter,
|
|
402
|
+
notebookDocument: NotebookDocument,
|
|
403
|
+
): boolean {
|
|
404
|
+
if (typeof filter === 'string') {
|
|
405
|
+
return filter === '*' || notebookDocument.notebookType === filter;
|
|
406
|
+
}
|
|
407
|
+
if (
|
|
408
|
+
filter.notebookType !== undefined &&
|
|
409
|
+
filter.notebookType !== '*' &&
|
|
410
|
+
notebookDocument.notebookType !== filter.notebookType
|
|
411
|
+
) {
|
|
412
|
+
return false;
|
|
413
|
+
}
|
|
414
|
+
const uri = notebookDocument.uri;
|
|
415
|
+
if (
|
|
416
|
+
filter.scheme !== undefined &&
|
|
417
|
+
filter.scheme !== '*' &&
|
|
418
|
+
uri.scheme !== filter.scheme
|
|
419
|
+
) {
|
|
420
|
+
return false;
|
|
421
|
+
}
|
|
422
|
+
if (filter.pattern !== undefined) {
|
|
423
|
+
const matcher = new minimatch.Minimatch(filter.pattern, { noext: true });
|
|
424
|
+
if (!matcher.makeRe()) {
|
|
425
|
+
return false;
|
|
426
|
+
}
|
|
427
|
+
if (!matcher.match(uri.fsPath)) {
|
|
428
|
+
return false;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
return true;
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
namespace $NotebookDocumentSyncOptions {
|
|
436
|
+
export function asDocumentSelector(
|
|
437
|
+
options: proto.NotebookDocumentSyncOptions,
|
|
438
|
+
): proto.DocumentSelector {
|
|
439
|
+
const selector = options.notebookSelector;
|
|
440
|
+
const result: proto.DocumentSelector = [];
|
|
441
|
+
for (const element of selector) {
|
|
442
|
+
const notebookType =
|
|
443
|
+
(typeof element.notebook === 'string'
|
|
444
|
+
? element.notebook
|
|
445
|
+
: element.notebook?.notebookType) ?? '*';
|
|
446
|
+
const scheme =
|
|
447
|
+
typeof element.notebook === 'string' ? undefined : element.notebook?.scheme;
|
|
448
|
+
const pattern =
|
|
449
|
+
typeof element.notebook === 'string' ? undefined : element.notebook?.pattern;
|
|
450
|
+
if (element.cells !== undefined) {
|
|
451
|
+
for (const cell of element.cells) {
|
|
452
|
+
result.push(asDocumentFilter(notebookType, scheme, pattern, cell.language));
|
|
453
|
+
}
|
|
454
|
+
} else {
|
|
455
|
+
result.push(asDocumentFilter(notebookType, scheme, pattern, undefined));
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
return result;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
function asDocumentFilter(
|
|
462
|
+
notebookType: string,
|
|
463
|
+
scheme: string | undefined,
|
|
464
|
+
pattern: string | undefined,
|
|
465
|
+
language: string | undefined,
|
|
466
|
+
): proto.NotebookCellTextDocumentFilter {
|
|
467
|
+
return scheme === undefined && pattern === undefined
|
|
468
|
+
? { notebook: notebookType, language }
|
|
469
|
+
: { notebook: { notebookType, scheme, pattern }, language };
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
type SyncInfo = {
|
|
474
|
+
/**
|
|
475
|
+
* The synced VS Code notebook cells.
|
|
476
|
+
*/
|
|
477
|
+
cells: NotebookCell[];
|
|
478
|
+
|
|
479
|
+
/**
|
|
480
|
+
* A set of VS Code URIs of the synced
|
|
481
|
+
* VS Code notebook cell text documents.
|
|
482
|
+
*/
|
|
483
|
+
uris: Set<string>;
|
|
484
|
+
};
|
|
485
|
+
|
|
486
|
+
namespace SyncInfo {
|
|
487
|
+
export function create(cells: NotebookCell[]): SyncInfo {
|
|
488
|
+
return {
|
|
489
|
+
cells,
|
|
490
|
+
uris: new Set(cells.map((cell) => cell.document.uri.toString())),
|
|
491
|
+
};
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
export type VNotebookDocumentChangeEvent = {
|
|
496
|
+
/**
|
|
497
|
+
* The notebook document
|
|
498
|
+
*/
|
|
499
|
+
notebook: NotebookDocument;
|
|
500
|
+
|
|
501
|
+
/**
|
|
502
|
+
* The changed meta data if any.
|
|
503
|
+
*/
|
|
504
|
+
metadata?: { [key: string]: any };
|
|
505
|
+
|
|
506
|
+
/**
|
|
507
|
+
* Changes to cells.
|
|
508
|
+
*/
|
|
509
|
+
cells?: {
|
|
510
|
+
/**
|
|
511
|
+
* Changes to the cell structure to add or
|
|
512
|
+
* remove cells.
|
|
513
|
+
*/
|
|
514
|
+
structure?: {
|
|
515
|
+
/**
|
|
516
|
+
* The change to the cell array.
|
|
517
|
+
*/
|
|
518
|
+
array: { start: number; deleteCount: number; cells?: NotebookCell[] };
|
|
519
|
+
|
|
520
|
+
/**
|
|
521
|
+
* Additional opened cell text documents.
|
|
522
|
+
*/
|
|
523
|
+
didOpen?: NotebookCell[];
|
|
524
|
+
|
|
525
|
+
/**
|
|
526
|
+
* Additional closed cell text documents.
|
|
527
|
+
*/
|
|
528
|
+
didClose?: NotebookCell[];
|
|
529
|
+
};
|
|
530
|
+
|
|
531
|
+
/**
|
|
532
|
+
* Changes to notebook cells properties like its
|
|
533
|
+
* kind or metadata.
|
|
534
|
+
*/
|
|
535
|
+
data?: NotebookCell[];
|
|
536
|
+
|
|
537
|
+
/**
|
|
538
|
+
* Changes to the text content of notebook cells.
|
|
539
|
+
*/
|
|
540
|
+
textContent?: TextDocumentChangeEvent[];
|
|
541
|
+
};
|
|
542
|
+
};
|
|
543
|
+
|
|
544
|
+
export type NotebookDocumentOptions = {
|
|
545
|
+
filterCells?(
|
|
546
|
+
notebookDocument: NotebookDocument,
|
|
547
|
+
cells: NotebookCell[],
|
|
548
|
+
): NotebookCell[];
|
|
549
|
+
};
|
|
550
|
+
|
|
551
|
+
export type $NotebookDocumentOptions = {
|
|
552
|
+
notebookDocumentOptions?: NotebookDocumentOptions;
|
|
553
|
+
};
|
|
554
|
+
|
|
555
|
+
export type NotebookDocumentMiddleware = {
|
|
556
|
+
notebooks?: {
|
|
557
|
+
didOpen?: (
|
|
558
|
+
this: void,
|
|
559
|
+
notebookDocument: NotebookDocument,
|
|
560
|
+
cells: NotebookCell[],
|
|
561
|
+
next: (
|
|
562
|
+
this: void,
|
|
563
|
+
notebookDocument: NotebookDocument,
|
|
564
|
+
cells: NotebookCell[],
|
|
565
|
+
) => Promise<void>,
|
|
566
|
+
) => Promise<void>;
|
|
567
|
+
didSave?: (
|
|
568
|
+
this: void,
|
|
569
|
+
notebookDocument: NotebookDocument,
|
|
570
|
+
next: (this: void, notebookDocument: NotebookDocument) => Promise<void>,
|
|
571
|
+
) => Promise<void>;
|
|
572
|
+
didChange?: (
|
|
573
|
+
this: void,
|
|
574
|
+
event: VNotebookDocumentChangeEvent,
|
|
575
|
+
next: (this: void, event: VNotebookDocumentChangeEvent) => Promise<void>,
|
|
576
|
+
) => Promise<void>;
|
|
577
|
+
didClose?: (
|
|
578
|
+
this: void,
|
|
579
|
+
notebookDocument: NotebookDocument,
|
|
580
|
+
cells: NotebookCell[],
|
|
581
|
+
next: (
|
|
582
|
+
this: void,
|
|
583
|
+
notebookDocument: NotebookDocument,
|
|
584
|
+
cells: NotebookCell[],
|
|
585
|
+
) => Promise<void>,
|
|
586
|
+
) => Promise<void>;
|
|
587
|
+
};
|
|
588
|
+
};
|
|
589
|
+
|
|
590
|
+
export interface NotebookDocumentSyncFeatureShape {
|
|
591
|
+
sendDidOpenNotebookDocument(notebookDocument: NotebookDocument): Promise<void>;
|
|
592
|
+
sendDidSaveNotebookDocument(notebookDocument: NotebookDocument): Promise<void>;
|
|
593
|
+
sendDidChangeNotebookDocument(event: VNotebookDocumentChangeEvent): Promise<void>;
|
|
594
|
+
sendDidCloseNotebookDocument(notebookDocument: NotebookDocument): Promise<void>;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
class NotebookDocumentSyncFeatureProvider implements NotebookDocumentSyncFeatureShape {
|
|
598
|
+
private readonly client: FeatureClient<
|
|
599
|
+
NotebookDocumentMiddleware,
|
|
600
|
+
$NotebookDocumentOptions
|
|
601
|
+
>;
|
|
602
|
+
private readonly options: proto.NotebookDocumentSyncOptions;
|
|
603
|
+
private readonly notebookSyncInfo: Map<string, SyncInfo>;
|
|
604
|
+
private readonly notebookDidOpen: Set<string>;
|
|
605
|
+
private readonly disposables: Disposable[];
|
|
606
|
+
private readonly selector: DocumentSelector;
|
|
607
|
+
|
|
608
|
+
constructor(
|
|
609
|
+
client: FeatureClient<NotebookDocumentMiddleware, $NotebookDocumentOptions>,
|
|
610
|
+
options: proto.NotebookDocumentSyncOptions,
|
|
611
|
+
) {
|
|
612
|
+
this.client = client;
|
|
613
|
+
this.options = options;
|
|
614
|
+
this.notebookSyncInfo = new Map();
|
|
615
|
+
this.notebookDidOpen = new Set();
|
|
616
|
+
this.disposables = [];
|
|
617
|
+
this.selector = client.protocol2CodeConverter.asDocumentSelector(
|
|
618
|
+
$NotebookDocumentSyncOptions.asDocumentSelector(options),
|
|
619
|
+
);
|
|
620
|
+
|
|
621
|
+
// open
|
|
622
|
+
workspace.onDidOpenNotebookDocument(
|
|
623
|
+
(notebookDocument) => {
|
|
624
|
+
this.notebookDidOpen.add(notebookDocument.uri.toString());
|
|
625
|
+
this.didOpen(notebookDocument);
|
|
626
|
+
},
|
|
627
|
+
undefined,
|
|
628
|
+
this.disposables,
|
|
629
|
+
);
|
|
630
|
+
for (const notebookDocument of workspace.notebookDocuments) {
|
|
631
|
+
this.notebookDidOpen.add(notebookDocument.uri.toString());
|
|
632
|
+
this.didOpen(notebookDocument);
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
// Notebook document changed.
|
|
636
|
+
workspace.onDidChangeNotebookDocument(
|
|
637
|
+
(event) => this.didChangeNotebookDocument(event),
|
|
638
|
+
undefined,
|
|
639
|
+
this.disposables,
|
|
640
|
+
);
|
|
641
|
+
|
|
642
|
+
//save
|
|
643
|
+
if (this.options.save === true) {
|
|
644
|
+
workspace.onDidSaveNotebookDocument(
|
|
645
|
+
(notebookDocument) => this.didSave(notebookDocument),
|
|
646
|
+
undefined,
|
|
647
|
+
this.disposables,
|
|
648
|
+
);
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
// close
|
|
652
|
+
workspace.onDidCloseNotebookDocument(
|
|
653
|
+
(notebookDocument) => {
|
|
654
|
+
this.didClose(notebookDocument);
|
|
655
|
+
this.notebookDidOpen.delete(notebookDocument.uri.toString());
|
|
656
|
+
},
|
|
657
|
+
undefined,
|
|
658
|
+
this.disposables,
|
|
659
|
+
);
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
public getState(): FeatureState {
|
|
663
|
+
for (const notebook of workspace.notebookDocuments) {
|
|
664
|
+
const matchingCells = this.getMatchingCells(notebook);
|
|
665
|
+
if (matchingCells !== undefined) {
|
|
666
|
+
return {
|
|
667
|
+
kind: 'document',
|
|
668
|
+
id: '$internal',
|
|
669
|
+
registrations: true,
|
|
670
|
+
matches: true,
|
|
671
|
+
};
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
return { kind: 'document', id: '$internal', registrations: true, matches: false };
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
public get mode(): 'notebook' {
|
|
678
|
+
return 'notebook';
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
public handles(textDocument: TextDocument): boolean {
|
|
682
|
+
return languages.match(this.selector, textDocument) > 0;
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
public didOpenNotebookCellTextDocument(
|
|
686
|
+
notebookDocument: NotebookDocument,
|
|
687
|
+
cell: NotebookCell,
|
|
688
|
+
): void {
|
|
689
|
+
if (languages.match(this.selector, cell.document) === 0) {
|
|
690
|
+
return;
|
|
691
|
+
}
|
|
692
|
+
if (!this.notebookDidOpen.has(notebookDocument.uri.toString())) {
|
|
693
|
+
// We have never received an open notification for the notebook document.
|
|
694
|
+
// VS Code guarantees that we first get cell document open and then
|
|
695
|
+
// notebook open. So simply wait for the notebook open.
|
|
696
|
+
return;
|
|
697
|
+
}
|
|
698
|
+
const syncInfo = this.notebookSyncInfo.get(notebookDocument.uri.toString());
|
|
699
|
+
// In VS Code we receive a notebook open before a cell document open.
|
|
700
|
+
// The document and the cell is synced.
|
|
701
|
+
const cellMatches = this.cellMatches(notebookDocument, cell);
|
|
702
|
+
if (syncInfo !== undefined) {
|
|
703
|
+
const cellIsSynced = syncInfo.uris.has(cell.document.uri.toString());
|
|
704
|
+
if ((cellMatches && cellIsSynced) || (!cellMatches && !cellIsSynced)) {
|
|
705
|
+
// The cell doesn't match and was not synced or it matches and is synced.
|
|
706
|
+
// In both cases nothing to do.
|
|
707
|
+
//
|
|
708
|
+
// Note that if the language mode of a document changes we remove the
|
|
709
|
+
// cell and add it back to update the language mode on the server side.
|
|
710
|
+
return;
|
|
711
|
+
}
|
|
712
|
+
if (cellMatches) {
|
|
713
|
+
// don't use cells from above since there might be more matching cells in the notebook
|
|
714
|
+
// Since we had a matching cell above we will have matching cells now.
|
|
715
|
+
const matchingCells = this.getMatchingCells(notebookDocument);
|
|
716
|
+
if (matchingCells !== undefined) {
|
|
717
|
+
const event = this.asNotebookDocumentChangeEvent(
|
|
718
|
+
notebookDocument,
|
|
719
|
+
undefined,
|
|
720
|
+
syncInfo,
|
|
721
|
+
matchingCells,
|
|
722
|
+
);
|
|
723
|
+
if (event !== undefined) {
|
|
724
|
+
this.doSendChange(event, matchingCells).catch(() => {
|
|
725
|
+
/* handled in send change */
|
|
726
|
+
});
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
} else {
|
|
731
|
+
// No sync info. But we have a open event for the notebook document
|
|
732
|
+
// itself. If the cell matches then we need to send an open with
|
|
733
|
+
// exactly that cell.
|
|
734
|
+
if (cellMatches) {
|
|
735
|
+
this.doSendOpen(notebookDocument, [cell]).catch(() => {
|
|
736
|
+
/* handled in open */
|
|
737
|
+
});
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
public didChangeNotebookCellTextDocument(
|
|
743
|
+
notebookDocument: NotebookDocument,
|
|
744
|
+
event: TextDocumentChangeEvent,
|
|
745
|
+
): void {
|
|
746
|
+
// No match with the selector
|
|
747
|
+
if (languages.match(this.selector, event.document) === 0) {
|
|
748
|
+
return;
|
|
749
|
+
}
|
|
750
|
+
this.doSendChange(
|
|
751
|
+
{
|
|
752
|
+
notebook: notebookDocument,
|
|
753
|
+
cells: { textContent: [event] },
|
|
754
|
+
},
|
|
755
|
+
undefined,
|
|
756
|
+
).catch(() => {
|
|
757
|
+
/* error handled in doSendChange */
|
|
758
|
+
});
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
public didCloseNotebookCellTextDocument(
|
|
762
|
+
notebookDocument: NotebookDocument,
|
|
763
|
+
cell: NotebookCell,
|
|
764
|
+
): void {
|
|
765
|
+
const syncInfo = this.notebookSyncInfo.get(notebookDocument.uri.toString());
|
|
766
|
+
if (syncInfo === undefined) {
|
|
767
|
+
// The notebook document got never synced. So it doesn't matter if a cell
|
|
768
|
+
// document closes.
|
|
769
|
+
return;
|
|
770
|
+
}
|
|
771
|
+
const cellUri = cell.document.uri;
|
|
772
|
+
const index = syncInfo.cells.findIndex(
|
|
773
|
+
(item) => item.document.uri.toString() === cellUri.toString(),
|
|
774
|
+
);
|
|
775
|
+
if (index === -1) {
|
|
776
|
+
// The cell never got synced or it got deleted and we now received the document
|
|
777
|
+
// close event.
|
|
778
|
+
return;
|
|
779
|
+
}
|
|
780
|
+
if (index === 0 && syncInfo.cells.length === 1) {
|
|
781
|
+
// The last cell. Close the notebook document in the server.
|
|
782
|
+
this.doSendClose(notebookDocument, syncInfo.cells).catch(() => {
|
|
783
|
+
/* error handled in doSendClose */
|
|
784
|
+
});
|
|
785
|
+
} else {
|
|
786
|
+
const newCells = syncInfo.cells.slice();
|
|
787
|
+
const deleted = newCells.splice(index, 1);
|
|
788
|
+
this.doSendChange(
|
|
789
|
+
{
|
|
790
|
+
notebook: notebookDocument,
|
|
791
|
+
cells: {
|
|
792
|
+
structure: {
|
|
793
|
+
array: { start: index, deleteCount: 1 },
|
|
794
|
+
didClose: deleted,
|
|
795
|
+
},
|
|
796
|
+
},
|
|
797
|
+
},
|
|
798
|
+
newCells,
|
|
799
|
+
).catch(() => {
|
|
800
|
+
/* error handled in doSendChange */
|
|
801
|
+
});
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
public dispose(): void {
|
|
806
|
+
for (const disposable of this.disposables) {
|
|
807
|
+
disposable.dispose();
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
private didOpen(
|
|
812
|
+
notebookDocument: NotebookDocument,
|
|
813
|
+
matchingCells: NotebookCell[] | undefined = this.getMatchingCells(notebookDocument),
|
|
814
|
+
syncInfo: SyncInfo | undefined = this.notebookSyncInfo.get(
|
|
815
|
+
notebookDocument.uri.toString(),
|
|
816
|
+
),
|
|
817
|
+
): void {
|
|
818
|
+
if (syncInfo !== undefined) {
|
|
819
|
+
if (matchingCells !== undefined) {
|
|
820
|
+
const event = this.asNotebookDocumentChangeEvent(
|
|
821
|
+
notebookDocument,
|
|
822
|
+
undefined,
|
|
823
|
+
syncInfo,
|
|
824
|
+
matchingCells,
|
|
825
|
+
);
|
|
826
|
+
if (event !== undefined) {
|
|
827
|
+
this.doSendChange(event, matchingCells).catch(() => {
|
|
828
|
+
/* handled in send change */
|
|
829
|
+
});
|
|
830
|
+
}
|
|
831
|
+
} else {
|
|
832
|
+
this.doSendClose(notebookDocument, []).catch(() => {
|
|
833
|
+
/* handled in send close */
|
|
834
|
+
});
|
|
835
|
+
}
|
|
836
|
+
} else {
|
|
837
|
+
// Check if we need to sync the notebook document.
|
|
838
|
+
if (matchingCells === undefined) {
|
|
839
|
+
return;
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
this.doSendOpen(notebookDocument, matchingCells).catch(() => {
|
|
843
|
+
/* error handled in doSendOpen */
|
|
844
|
+
});
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
private didChangeNotebookDocument(event: NotebookDocumentChangeEvent): void {
|
|
849
|
+
const notebookDocument = event.notebook;
|
|
850
|
+
const syncInfo = this.notebookSyncInfo.get(notebookDocument.uri.toString());
|
|
851
|
+
if (syncInfo === undefined) {
|
|
852
|
+
// We have no changes to the cells. Since the notebook wasn't synced
|
|
853
|
+
// it will not be synced now.
|
|
854
|
+
if (event.contentChanges.length === 0) {
|
|
855
|
+
return;
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
// Check if we have new matching cells.
|
|
859
|
+
const cells = this.getMatchingCells(notebookDocument);
|
|
860
|
+
|
|
861
|
+
// No matching cells and the notebook never synced. So still no need
|
|
862
|
+
// to sync it.
|
|
863
|
+
if (cells === undefined) {
|
|
864
|
+
return;
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
// Open the notebook document and ignore the rest of the changes
|
|
868
|
+
// this the notebooks will be synced with the correct settings.
|
|
869
|
+
this.didOpen(notebookDocument, cells, syncInfo);
|
|
870
|
+
} else {
|
|
871
|
+
// The notebook is synced. First check if we have no matching
|
|
872
|
+
// cells anymore and if so close the notebook
|
|
873
|
+
const cells = this.getMatchingCells(notebookDocument);
|
|
874
|
+
if (cells === undefined) {
|
|
875
|
+
this.didClose(notebookDocument, syncInfo);
|
|
876
|
+
return;
|
|
877
|
+
}
|
|
878
|
+
const newEvent = this.asNotebookDocumentChangeEvent(
|
|
879
|
+
event.notebook,
|
|
880
|
+
event,
|
|
881
|
+
syncInfo,
|
|
882
|
+
cells,
|
|
883
|
+
);
|
|
884
|
+
if (newEvent !== undefined) {
|
|
885
|
+
this.doSendChange(newEvent, cells).catch(() => {
|
|
886
|
+
/* error handled in doSendChange */
|
|
887
|
+
});
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
private didSave(notebookDocument: NotebookDocument): void {
|
|
893
|
+
const syncInfo = this.notebookSyncInfo.get(notebookDocument.uri.toString());
|
|
894
|
+
if (syncInfo === undefined) {
|
|
895
|
+
return;
|
|
896
|
+
}
|
|
897
|
+
this.doSendSave(notebookDocument).catch(() => {
|
|
898
|
+
/* error handled in doSendSave */
|
|
899
|
+
});
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
private didClose(
|
|
903
|
+
notebookDocument: NotebookDocument,
|
|
904
|
+
syncInfo: SyncInfo | undefined = this.notebookSyncInfo.get(
|
|
905
|
+
notebookDocument.uri.toString(),
|
|
906
|
+
),
|
|
907
|
+
): void {
|
|
908
|
+
if (syncInfo === undefined) {
|
|
909
|
+
return;
|
|
910
|
+
}
|
|
911
|
+
const syncedCells = notebookDocument
|
|
912
|
+
.getCells()
|
|
913
|
+
.filter((cell) => syncInfo.uris.has(cell.document.uri.toString()));
|
|
914
|
+
this.doSendClose(notebookDocument, syncedCells).catch(() => {
|
|
915
|
+
/* error handled in doSendClose */
|
|
916
|
+
});
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
public async sendDidOpenNotebookDocument(
|
|
920
|
+
notebookDocument: NotebookDocument,
|
|
921
|
+
): Promise<void> {
|
|
922
|
+
const cells = this.getMatchingCells(notebookDocument);
|
|
923
|
+
if (cells === undefined) {
|
|
924
|
+
return;
|
|
925
|
+
}
|
|
926
|
+
return this.doSendOpen(notebookDocument, cells);
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
private async doSendOpen(
|
|
930
|
+
notebookDocument: NotebookDocument,
|
|
931
|
+
cells: NotebookCell[],
|
|
932
|
+
): Promise<void> {
|
|
933
|
+
const send = async (
|
|
934
|
+
notebookDocument: NotebookDocument,
|
|
935
|
+
cells: NotebookCell[],
|
|
936
|
+
): Promise<void> => {
|
|
937
|
+
const nb = Converter.c2p.asNotebookDocument(
|
|
938
|
+
notebookDocument,
|
|
939
|
+
cells,
|
|
940
|
+
this.client.code2ProtocolConverter,
|
|
941
|
+
);
|
|
942
|
+
const cellDocuments: TextDocumentItem[] = cells.map((cell) =>
|
|
943
|
+
this.client.code2ProtocolConverter.asTextDocumentItem(cell.document),
|
|
944
|
+
);
|
|
945
|
+
try {
|
|
946
|
+
await this.client.sendNotification(
|
|
947
|
+
proto.DidOpenNotebookDocumentNotification.type,
|
|
948
|
+
{
|
|
949
|
+
notebookDocument: nb,
|
|
950
|
+
cellTextDocuments: cellDocuments,
|
|
951
|
+
},
|
|
952
|
+
);
|
|
953
|
+
} catch (error) {
|
|
954
|
+
this.client.error('Sending DidOpenNotebookDocumentNotification failed', error);
|
|
955
|
+
throw error;
|
|
956
|
+
}
|
|
957
|
+
};
|
|
958
|
+
const middleware = this.client.middleware?.notebooks;
|
|
959
|
+
this.notebookSyncInfo.set(notebookDocument.uri.toString(), SyncInfo.create(cells));
|
|
960
|
+
return middleware?.didOpen !== undefined
|
|
961
|
+
? middleware.didOpen(notebookDocument, cells, send)
|
|
962
|
+
: send(notebookDocument, cells);
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
public async sendDidChangeNotebookDocument(
|
|
966
|
+
event: VNotebookDocumentChangeEvent,
|
|
967
|
+
): Promise<void> {
|
|
968
|
+
return this.doSendChange(event, undefined);
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
private async doSendChange(
|
|
972
|
+
event: VNotebookDocumentChangeEvent,
|
|
973
|
+
cells: NotebookCell[] | undefined = this.getMatchingCells(event.notebook),
|
|
974
|
+
): Promise<void> {
|
|
975
|
+
const send = async (event: VNotebookDocumentChangeEvent): Promise<void> => {
|
|
976
|
+
try {
|
|
977
|
+
await this.client.sendNotification(
|
|
978
|
+
proto.DidChangeNotebookDocumentNotification.type,
|
|
979
|
+
{
|
|
980
|
+
notebookDocument: Converter.c2p.asVersionedNotebookDocumentIdentifier(
|
|
981
|
+
event.notebook,
|
|
982
|
+
this.client.code2ProtocolConverter,
|
|
983
|
+
),
|
|
984
|
+
change: Converter.c2p.asNotebookDocumentChangeEvent(
|
|
985
|
+
event,
|
|
986
|
+
this.client.code2ProtocolConverter,
|
|
987
|
+
),
|
|
988
|
+
},
|
|
989
|
+
);
|
|
990
|
+
} catch (error) {
|
|
991
|
+
this.client.error(
|
|
992
|
+
'Sending DidChangeNotebookDocumentNotification failed',
|
|
993
|
+
error,
|
|
994
|
+
);
|
|
995
|
+
throw error;
|
|
996
|
+
}
|
|
997
|
+
};
|
|
998
|
+
const middleware = this.client.middleware?.notebooks;
|
|
999
|
+
if (event.cells?.structure !== undefined) {
|
|
1000
|
+
this.notebookSyncInfo.set(
|
|
1001
|
+
event.notebook.uri.toString(),
|
|
1002
|
+
SyncInfo.create(cells ?? []),
|
|
1003
|
+
);
|
|
1004
|
+
}
|
|
1005
|
+
return middleware?.didChange !== undefined
|
|
1006
|
+
? middleware?.didChange(event, send)
|
|
1007
|
+
: send(event);
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
public async sendDidSaveNotebookDocument(
|
|
1011
|
+
notebookDocument: NotebookDocument,
|
|
1012
|
+
): Promise<void> {
|
|
1013
|
+
return this.doSendSave(notebookDocument);
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
private async doSendSave(notebookDocument: NotebookDocument): Promise<void> {
|
|
1017
|
+
const send = async (notebookDocument: NotebookDocument): Promise<void> => {
|
|
1018
|
+
try {
|
|
1019
|
+
await this.client.sendNotification(
|
|
1020
|
+
proto.DidSaveNotebookDocumentNotification.type,
|
|
1021
|
+
{
|
|
1022
|
+
notebookDocument: {
|
|
1023
|
+
uri: this.client.code2ProtocolConverter.asUri(notebookDocument.uri),
|
|
1024
|
+
},
|
|
1025
|
+
},
|
|
1026
|
+
);
|
|
1027
|
+
} catch (error) {
|
|
1028
|
+
this.client.error('Sending DidSaveNotebookDocumentNotification failed', error);
|
|
1029
|
+
throw error;
|
|
1030
|
+
}
|
|
1031
|
+
};
|
|
1032
|
+
const middleware = this.client.middleware?.notebooks;
|
|
1033
|
+
return middleware?.didSave !== undefined
|
|
1034
|
+
? middleware.didSave(notebookDocument, send)
|
|
1035
|
+
: send(notebookDocument);
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
public async sendDidCloseNotebookDocument(
|
|
1039
|
+
notebookDocument: NotebookDocument,
|
|
1040
|
+
): Promise<void> {
|
|
1041
|
+
return this.doSendClose(
|
|
1042
|
+
notebookDocument,
|
|
1043
|
+
this.getMatchingCells(notebookDocument) ?? [],
|
|
1044
|
+
);
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
private async doSendClose(
|
|
1048
|
+
notebookDocument: NotebookDocument,
|
|
1049
|
+
cells: NotebookCell[],
|
|
1050
|
+
): Promise<void> {
|
|
1051
|
+
const send = async (
|
|
1052
|
+
notebookDocument: NotebookDocument,
|
|
1053
|
+
cells: NotebookCell[],
|
|
1054
|
+
): Promise<void> => {
|
|
1055
|
+
try {
|
|
1056
|
+
await this.client.sendNotification(
|
|
1057
|
+
proto.DidCloseNotebookDocumentNotification.type,
|
|
1058
|
+
{
|
|
1059
|
+
notebookDocument: {
|
|
1060
|
+
uri: this.client.code2ProtocolConverter.asUri(notebookDocument.uri),
|
|
1061
|
+
},
|
|
1062
|
+
cellTextDocuments: cells.map((cell) =>
|
|
1063
|
+
this.client.code2ProtocolConverter.asTextDocumentIdentifier(
|
|
1064
|
+
cell.document,
|
|
1065
|
+
),
|
|
1066
|
+
),
|
|
1067
|
+
},
|
|
1068
|
+
);
|
|
1069
|
+
} catch (error) {
|
|
1070
|
+
this.client.error('Sending DidCloseNotebookDocumentNotification failed', error);
|
|
1071
|
+
throw error;
|
|
1072
|
+
}
|
|
1073
|
+
};
|
|
1074
|
+
const middleware = this.client.middleware?.notebooks;
|
|
1075
|
+
this.notebookSyncInfo.delete(notebookDocument.uri.toString());
|
|
1076
|
+
return middleware?.didClose !== undefined
|
|
1077
|
+
? middleware.didClose(notebookDocument, cells, send)
|
|
1078
|
+
: send(notebookDocument, cells);
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
private asNotebookDocumentChangeEvent(
|
|
1082
|
+
notebook: NotebookDocument,
|
|
1083
|
+
event: NotebookDocumentChangeEvent | undefined,
|
|
1084
|
+
syncInfo: SyncInfo,
|
|
1085
|
+
matchingCells: NotebookCell[],
|
|
1086
|
+
): VNotebookDocumentChangeEvent | undefined {
|
|
1087
|
+
if (event !== undefined && event.notebook !== notebook) {
|
|
1088
|
+
throw new Error('Notebook must be identical');
|
|
1089
|
+
}
|
|
1090
|
+
const result: VNotebookDocumentChangeEvent = {
|
|
1091
|
+
notebook: notebook,
|
|
1092
|
+
};
|
|
1093
|
+
|
|
1094
|
+
if (event?.metadata !== undefined) {
|
|
1095
|
+
result.metadata = Converter.c2p.asMetadata(event.metadata);
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
let matchingCellsSet: Set<string> | undefined;
|
|
1099
|
+
if (event?.cellChanges !== undefined && event.cellChanges.length > 0) {
|
|
1100
|
+
const data: NotebookCell[] = [];
|
|
1101
|
+
// Only consider the new matching cells.
|
|
1102
|
+
matchingCellsSet = new Set(
|
|
1103
|
+
matchingCells.map((cell) => cell.document.uri.toString()),
|
|
1104
|
+
);
|
|
1105
|
+
for (const cellChange of event.cellChanges) {
|
|
1106
|
+
if (
|
|
1107
|
+
matchingCellsSet.has(cellChange.cell.document.uri.toString()) &&
|
|
1108
|
+
(cellChange.executionSummary !== undefined ||
|
|
1109
|
+
cellChange.metadata !== undefined)
|
|
1110
|
+
) {
|
|
1111
|
+
data.push(cellChange.cell);
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
if (data.length > 0) {
|
|
1115
|
+
result.cells = result.cells ?? {};
|
|
1116
|
+
result.cells.data = data;
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
if (
|
|
1121
|
+
((event?.contentChanges !== undefined && event.contentChanges.length > 0) ||
|
|
1122
|
+
event === undefined) &&
|
|
1123
|
+
syncInfo !== undefined &&
|
|
1124
|
+
matchingCells !== undefined
|
|
1125
|
+
) {
|
|
1126
|
+
// We still have matching cells. Check if the cell changes
|
|
1127
|
+
// affect the notebook on the server side.
|
|
1128
|
+
const oldCells = syncInfo.cells;
|
|
1129
|
+
const newCells = matchingCells;
|
|
1130
|
+
|
|
1131
|
+
// meta data changes are reported using on the cell itself. So we can ignore comparing
|
|
1132
|
+
// it which has a positive effect on performance.
|
|
1133
|
+
const diff = $NotebookCell.computeDiff(oldCells, newCells, false);
|
|
1134
|
+
let addedCells: Map<string, NotebookCell> | undefined;
|
|
1135
|
+
let removedCells: Map<string, NotebookCell> | undefined;
|
|
1136
|
+
if (diff !== undefined) {
|
|
1137
|
+
addedCells =
|
|
1138
|
+
diff.cells === undefined
|
|
1139
|
+
? new Map()
|
|
1140
|
+
: new Map(diff.cells.map((cell) => [cell.document.uri.toString(), cell]));
|
|
1141
|
+
removedCells =
|
|
1142
|
+
diff.deleteCount === 0
|
|
1143
|
+
? new Map()
|
|
1144
|
+
: new Map(
|
|
1145
|
+
oldCells
|
|
1146
|
+
.slice(diff.start, diff.start + diff.deleteCount)
|
|
1147
|
+
.map((cell) => [cell.document.uri.toString(), cell]),
|
|
1148
|
+
);
|
|
1149
|
+
|
|
1150
|
+
// Remove the onces that got deleted and inserted again.
|
|
1151
|
+
for (const key of Array.from(removedCells.keys())) {
|
|
1152
|
+
if (addedCells.has(key)) {
|
|
1153
|
+
removedCells.delete(key);
|
|
1154
|
+
addedCells.delete(key);
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
result.cells = result.cells ?? {};
|
|
1158
|
+
type structure = Required<
|
|
1159
|
+
Required<Required<VNotebookDocumentChangeEvent>['cells']>['structure']
|
|
1160
|
+
>;
|
|
1161
|
+
const didOpen: structure['didOpen'] = [];
|
|
1162
|
+
const didClose: structure['didClose'] = [];
|
|
1163
|
+
if (addedCells.size > 0 || removedCells.size > 0) {
|
|
1164
|
+
for (const cell of addedCells.values()) {
|
|
1165
|
+
didOpen.push(cell);
|
|
1166
|
+
}
|
|
1167
|
+
for (const cell of removedCells.values()) {
|
|
1168
|
+
didClose.push(cell);
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
1171
|
+
result.cells.structure = {
|
|
1172
|
+
array: diff,
|
|
1173
|
+
didOpen,
|
|
1174
|
+
didClose,
|
|
1175
|
+
};
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
// The notebook is a property as well.
|
|
1179
|
+
return Object.keys(result).length > 1 ? result : undefined;
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
private getMatchingCells(
|
|
1183
|
+
notebookDocument: NotebookDocument,
|
|
1184
|
+
cells: NotebookCell[] = notebookDocument.getCells(),
|
|
1185
|
+
): NotebookCell[] | undefined {
|
|
1186
|
+
if (this.options.notebookSelector === undefined) {
|
|
1187
|
+
return undefined;
|
|
1188
|
+
}
|
|
1189
|
+
for (const item of this.options.notebookSelector) {
|
|
1190
|
+
if (
|
|
1191
|
+
item.notebook === undefined ||
|
|
1192
|
+
$NotebookDocumentFilter.matchNotebook(item.notebook, notebookDocument)
|
|
1193
|
+
) {
|
|
1194
|
+
const filtered = this.filterCells(notebookDocument, cells, item.cells);
|
|
1195
|
+
return filtered.length === 0 ? undefined : filtered;
|
|
1196
|
+
}
|
|
1197
|
+
}
|
|
1198
|
+
return undefined;
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
private cellMatches(notebookDocument: NotebookDocument, cell: NotebookCell) {
|
|
1202
|
+
const cells = this.getMatchingCells(notebookDocument, [cell]);
|
|
1203
|
+
return cells !== undefined && cells[0] === cell;
|
|
1204
|
+
}
|
|
1205
|
+
|
|
1206
|
+
private filterCells(
|
|
1207
|
+
notebookDocument: NotebookDocument,
|
|
1208
|
+
cells: NotebookCell[],
|
|
1209
|
+
cellSelector: undefined | { language: string }[],
|
|
1210
|
+
): NotebookCell[] {
|
|
1211
|
+
const filtered =
|
|
1212
|
+
cellSelector !== undefined
|
|
1213
|
+
? cells.filter((cell) => {
|
|
1214
|
+
const cellLanguage = cell.document.languageId;
|
|
1215
|
+
return cellSelector.some(
|
|
1216
|
+
(filter) => filter.language === '*' || cellLanguage === filter.language,
|
|
1217
|
+
);
|
|
1218
|
+
})
|
|
1219
|
+
: cells;
|
|
1220
|
+
return typeof this.client.clientOptions.notebookDocumentOptions?.filterCells ===
|
|
1221
|
+
'function'
|
|
1222
|
+
? this.client.clientOptions.notebookDocumentOptions.filterCells(
|
|
1223
|
+
notebookDocument,
|
|
1224
|
+
filtered,
|
|
1225
|
+
)
|
|
1226
|
+
: filtered;
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
export type $NotebookCellTextDocumentFilter = NotebookCellTextDocumentFilter & {
|
|
1231
|
+
sync: true;
|
|
1232
|
+
};
|
|
1233
|
+
|
|
1234
|
+
export type NotebookDocumentProviderShape = {
|
|
1235
|
+
getProvider(notebookCell: NotebookCell): NotebookDocumentSyncFeatureShape | undefined;
|
|
1236
|
+
};
|
|
1237
|
+
|
|
1238
|
+
export class NotebookDocumentSyncFeature
|
|
1239
|
+
implements
|
|
1240
|
+
DynamicFeature<proto.NotebookDocumentSyncRegistrationOptions>,
|
|
1241
|
+
NotebookDocumentProviderShape
|
|
1242
|
+
{
|
|
1243
|
+
public static readonly CellScheme: string = LibroCellURIScheme;
|
|
1244
|
+
|
|
1245
|
+
private readonly client: FeatureClient<
|
|
1246
|
+
NotebookDocumentMiddleware,
|
|
1247
|
+
$NotebookDocumentOptions
|
|
1248
|
+
>;
|
|
1249
|
+
private readonly registrations: Map<string, NotebookDocumentSyncFeatureProvider>;
|
|
1250
|
+
private dedicatedChannel: DocumentSelector | undefined;
|
|
1251
|
+
|
|
1252
|
+
constructor(
|
|
1253
|
+
client: FeatureClient<NotebookDocumentMiddleware, $NotebookDocumentOptions>,
|
|
1254
|
+
) {
|
|
1255
|
+
this.client = client;
|
|
1256
|
+
this.registrations = new Map();
|
|
1257
|
+
this.registrationType = proto.NotebookDocumentSyncRegistrationType.type;
|
|
1258
|
+
// We don't receive an event for cells where the document changes its language mode
|
|
1259
|
+
// Since we allow servers to filter on the language mode we fire such an event ourselves.
|
|
1260
|
+
workspace.onDidOpenTextDocument((textDocument) => {
|
|
1261
|
+
if (textDocument.uri.scheme !== NotebookDocumentSyncFeature.CellScheme) {
|
|
1262
|
+
return;
|
|
1263
|
+
}
|
|
1264
|
+
const [notebookDocument, notebookCell] =
|
|
1265
|
+
this.findNotebookDocumentAndCell(textDocument);
|
|
1266
|
+
if (notebookDocument === undefined || notebookCell === undefined) {
|
|
1267
|
+
return;
|
|
1268
|
+
}
|
|
1269
|
+
for (const provider of this.registrations.values()) {
|
|
1270
|
+
if (provider instanceof NotebookDocumentSyncFeatureProvider) {
|
|
1271
|
+
provider.didOpenNotebookCellTextDocument(notebookDocument, notebookCell);
|
|
1272
|
+
}
|
|
1273
|
+
}
|
|
1274
|
+
});
|
|
1275
|
+
workspace.onDidChangeTextDocument((event) => {
|
|
1276
|
+
if (event.contentChanges.length === 0) {
|
|
1277
|
+
return;
|
|
1278
|
+
}
|
|
1279
|
+
const textDocument = event.document;
|
|
1280
|
+
if (textDocument.uri.scheme !== NotebookDocumentSyncFeature.CellScheme) {
|
|
1281
|
+
return;
|
|
1282
|
+
}
|
|
1283
|
+
const [notebookDocument] = this.findNotebookDocumentAndCell(textDocument);
|
|
1284
|
+
if (notebookDocument === undefined) {
|
|
1285
|
+
return;
|
|
1286
|
+
}
|
|
1287
|
+
for (const provider of this.registrations.values()) {
|
|
1288
|
+
if (provider instanceof NotebookDocumentSyncFeatureProvider) {
|
|
1289
|
+
provider.didChangeNotebookCellTextDocument(notebookDocument, event);
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
});
|
|
1293
|
+
workspace.onDidCloseTextDocument((textDocument) => {
|
|
1294
|
+
if (textDocument.uri.scheme !== NotebookDocumentSyncFeature.CellScheme) {
|
|
1295
|
+
return;
|
|
1296
|
+
}
|
|
1297
|
+
// There are two cases when we receive a close for a text document
|
|
1298
|
+
// 1: the cell got removed. This is handled in `onDidChangeNotebookCells`
|
|
1299
|
+
// 2: the language mode of a cell changed. This keeps the URI stable so
|
|
1300
|
+
// we will still find the cell and the notebook document.
|
|
1301
|
+
const [notebookDocument, notebookCell] =
|
|
1302
|
+
this.findNotebookDocumentAndCell(textDocument);
|
|
1303
|
+
if (notebookDocument === undefined || notebookCell === undefined) {
|
|
1304
|
+
return;
|
|
1305
|
+
}
|
|
1306
|
+
for (const provider of this.registrations.values()) {
|
|
1307
|
+
if (provider instanceof NotebookDocumentSyncFeatureProvider) {
|
|
1308
|
+
provider.didCloseNotebookCellTextDocument(notebookDocument, notebookCell);
|
|
1309
|
+
}
|
|
1310
|
+
}
|
|
1311
|
+
});
|
|
1312
|
+
}
|
|
1313
|
+
|
|
1314
|
+
getState(): FeatureState {
|
|
1315
|
+
if (this.registrations.size === 0) {
|
|
1316
|
+
return {
|
|
1317
|
+
kind: 'document',
|
|
1318
|
+
id: this.registrationType.method,
|
|
1319
|
+
registrations: false,
|
|
1320
|
+
matches: false,
|
|
1321
|
+
};
|
|
1322
|
+
}
|
|
1323
|
+
for (const provider of this.registrations.values()) {
|
|
1324
|
+
const state = provider.getState();
|
|
1325
|
+
if (
|
|
1326
|
+
state.kind === 'document' &&
|
|
1327
|
+
state.registrations === true &&
|
|
1328
|
+
state.matches === true
|
|
1329
|
+
) {
|
|
1330
|
+
return {
|
|
1331
|
+
kind: 'document',
|
|
1332
|
+
id: this.registrationType.method,
|
|
1333
|
+
registrations: true,
|
|
1334
|
+
matches: true,
|
|
1335
|
+
};
|
|
1336
|
+
}
|
|
1337
|
+
}
|
|
1338
|
+
return {
|
|
1339
|
+
kind: 'document',
|
|
1340
|
+
id: this.registrationType.method,
|
|
1341
|
+
registrations: true,
|
|
1342
|
+
matches: false,
|
|
1343
|
+
};
|
|
1344
|
+
}
|
|
1345
|
+
|
|
1346
|
+
public readonly registrationType: proto.RegistrationType<proto.NotebookDocumentSyncRegistrationOptions>;
|
|
1347
|
+
|
|
1348
|
+
public fillClientCapabilities(capabilities: proto.ClientCapabilities): void {
|
|
1349
|
+
const synchronization = ensure(
|
|
1350
|
+
ensure(capabilities, 'notebookDocument')!,
|
|
1351
|
+
'synchronization',
|
|
1352
|
+
)!;
|
|
1353
|
+
synchronization.dynamicRegistration = true;
|
|
1354
|
+
synchronization.executionSummarySupport = true;
|
|
1355
|
+
}
|
|
1356
|
+
|
|
1357
|
+
public preInitialize(capabilities: proto.ServerCapabilities<any>): void {
|
|
1358
|
+
const options = capabilities.notebookDocumentSync;
|
|
1359
|
+
if (options === undefined) {
|
|
1360
|
+
return;
|
|
1361
|
+
}
|
|
1362
|
+
this.dedicatedChannel = this.client.protocol2CodeConverter.asDocumentSelector(
|
|
1363
|
+
$NotebookDocumentSyncOptions.asDocumentSelector(options),
|
|
1364
|
+
);
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
public initialize(capabilities: proto.ServerCapabilities<any>): void {
|
|
1368
|
+
const options = capabilities.notebookDocumentSync;
|
|
1369
|
+
if (options === undefined) {
|
|
1370
|
+
return;
|
|
1371
|
+
}
|
|
1372
|
+
const id = (options as StaticRegistrationOptions).id ?? UUID.generateUuid();
|
|
1373
|
+
this.register({ id, registerOptions: options });
|
|
1374
|
+
}
|
|
1375
|
+
|
|
1376
|
+
public register(
|
|
1377
|
+
data: RegistrationData<proto.NotebookDocumentSyncRegistrationOptions>,
|
|
1378
|
+
): void {
|
|
1379
|
+
const provider = new NotebookDocumentSyncFeatureProvider(
|
|
1380
|
+
this.client,
|
|
1381
|
+
data.registerOptions,
|
|
1382
|
+
);
|
|
1383
|
+
this.registrations.set(data.id, provider);
|
|
1384
|
+
}
|
|
1385
|
+
|
|
1386
|
+
public unregister(id: string): void {
|
|
1387
|
+
const provider = this.registrations.get(id);
|
|
1388
|
+
if (provider !== undefined) {
|
|
1389
|
+
this.registrations.delete(id);
|
|
1390
|
+
provider.dispose();
|
|
1391
|
+
}
|
|
1392
|
+
}
|
|
1393
|
+
|
|
1394
|
+
public clear(): void {
|
|
1395
|
+
for (const provider of this.registrations.values()) {
|
|
1396
|
+
provider.dispose();
|
|
1397
|
+
}
|
|
1398
|
+
this.registrations.clear();
|
|
1399
|
+
}
|
|
1400
|
+
|
|
1401
|
+
public handles(textDocument: TextDocument): boolean {
|
|
1402
|
+
if (textDocument.uri.scheme !== NotebookDocumentSyncFeature.CellScheme) {
|
|
1403
|
+
return false;
|
|
1404
|
+
}
|
|
1405
|
+
if (
|
|
1406
|
+
this.dedicatedChannel !== undefined &&
|
|
1407
|
+
languages.match(this.dedicatedChannel, textDocument) > 0
|
|
1408
|
+
) {
|
|
1409
|
+
return true;
|
|
1410
|
+
}
|
|
1411
|
+
for (const provider of this.registrations.values()) {
|
|
1412
|
+
if (provider.handles(textDocument)) {
|
|
1413
|
+
return true;
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1416
|
+
return false;
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1419
|
+
public getProvider(
|
|
1420
|
+
notebookCell: NotebookCell,
|
|
1421
|
+
): NotebookDocumentSyncFeatureShape | undefined {
|
|
1422
|
+
for (const provider of this.registrations.values()) {
|
|
1423
|
+
if (provider.handles(notebookCell.document)) {
|
|
1424
|
+
return provider;
|
|
1425
|
+
}
|
|
1426
|
+
}
|
|
1427
|
+
return undefined;
|
|
1428
|
+
}
|
|
1429
|
+
|
|
1430
|
+
private findNotebookDocumentAndCell(
|
|
1431
|
+
textDocument: TextDocument,
|
|
1432
|
+
): [NotebookDocument | undefined, NotebookCell | undefined] {
|
|
1433
|
+
const uri = textDocument.uri.toString();
|
|
1434
|
+
for (const notebookDocument of workspace.notebookDocuments) {
|
|
1435
|
+
for (const cell of notebookDocument.getCells()) {
|
|
1436
|
+
if (cell.document.uri.toString() === uri) {
|
|
1437
|
+
return [notebookDocument, cell];
|
|
1438
|
+
}
|
|
1439
|
+
}
|
|
1440
|
+
}
|
|
1441
|
+
return [undefined, undefined];
|
|
1442
|
+
}
|
|
1443
|
+
}
|