@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,1408 @@
|
|
|
1
|
+
/* eslint-disable promise/always-return */
|
|
2
|
+
/* eslint-disable promise/catch-or-return */
|
|
3
|
+
/* --------------------------------------------------------------------------------------------
|
|
4
|
+
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
5
|
+
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
6
|
+
* ------------------------------------------------------------------------------------------ */
|
|
7
|
+
|
|
8
|
+
import type {
|
|
9
|
+
ClientCapabilities,
|
|
10
|
+
ServerCapabilities,
|
|
11
|
+
DocumentSelector,
|
|
12
|
+
PreviousResultId,
|
|
13
|
+
DiagnosticRegistrationOptions,
|
|
14
|
+
DocumentDiagnosticParams,
|
|
15
|
+
WorkspaceDocumentDiagnosticReport,
|
|
16
|
+
WorkspaceDiagnosticParams,
|
|
17
|
+
DiagnosticOptions,
|
|
18
|
+
} from '@difizen/vscode-languageserver-protocol';
|
|
19
|
+
import {
|
|
20
|
+
DidOpenTextDocumentNotification,
|
|
21
|
+
DidChangeTextDocumentNotification,
|
|
22
|
+
DidSaveTextDocumentNotification,
|
|
23
|
+
DidCloseTextDocumentNotification,
|
|
24
|
+
LinkedMap,
|
|
25
|
+
Touch,
|
|
26
|
+
RAL,
|
|
27
|
+
TextDocumentFilter,
|
|
28
|
+
DiagnosticServerCancellationData,
|
|
29
|
+
DocumentDiagnosticRequest,
|
|
30
|
+
DocumentDiagnosticReportKind,
|
|
31
|
+
WorkspaceDiagnosticRequest,
|
|
32
|
+
DiagnosticRefreshRequest,
|
|
33
|
+
} from '@difizen/vscode-languageserver-protocol';
|
|
34
|
+
import * as minimatch from 'minimatch';
|
|
35
|
+
import type {
|
|
36
|
+
CancellationToken,
|
|
37
|
+
ProviderResult,
|
|
38
|
+
Diagnostic as VDiagnostic,
|
|
39
|
+
TextDocument,
|
|
40
|
+
Event as VEvent,
|
|
41
|
+
DiagnosticCollection,
|
|
42
|
+
TabChangeEvent,
|
|
43
|
+
Event,
|
|
44
|
+
} from 'vscode';
|
|
45
|
+
|
|
46
|
+
import type { FeatureClient } from './features.js';
|
|
47
|
+
import { TextDocumentLanguageFeature, LSPCancellationError } from './features.js';
|
|
48
|
+
import { generateUuid } from './utils/uuid.js';
|
|
49
|
+
import {
|
|
50
|
+
languages as Languages,
|
|
51
|
+
window as Window,
|
|
52
|
+
workspace as Workspace,
|
|
53
|
+
CancellationTokenSource,
|
|
54
|
+
CancellationError,
|
|
55
|
+
EventEmitter,
|
|
56
|
+
Uri,
|
|
57
|
+
TabInputText,
|
|
58
|
+
TabInputTextDiff,
|
|
59
|
+
TabInputCustom,
|
|
60
|
+
workspace,
|
|
61
|
+
Disposable,
|
|
62
|
+
} from './vscodeAdaptor/vscodeAdaptor.js';
|
|
63
|
+
|
|
64
|
+
function ensure<T, K extends keyof T>(target: T, key: K): T[K] {
|
|
65
|
+
if (target[key] === void 0) {
|
|
66
|
+
target[key] = {} as any;
|
|
67
|
+
}
|
|
68
|
+
return target[key];
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export namespace vsdiag {
|
|
72
|
+
export enum DocumentDiagnosticReportKind {
|
|
73
|
+
full = 'full',
|
|
74
|
+
unChanged = 'unChanged',
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export interface FullDocumentDiagnosticReport {
|
|
78
|
+
kind: DocumentDiagnosticReportKind.full;
|
|
79
|
+
resultId?: string;
|
|
80
|
+
items: VDiagnostic[];
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export interface RelatedFullDocumentDiagnosticReport
|
|
84
|
+
extends FullDocumentDiagnosticReport {
|
|
85
|
+
relatedDocuments?: {
|
|
86
|
+
[uri: string /** DocumentUri */]:
|
|
87
|
+
| FullDocumentDiagnosticReport
|
|
88
|
+
| UnchangedDocumentDiagnosticReport;
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export interface UnchangedDocumentDiagnosticReport {
|
|
93
|
+
kind: DocumentDiagnosticReportKind.unChanged;
|
|
94
|
+
resultId: string;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export interface RelatedUnchangedDocumentDiagnosticReport
|
|
98
|
+
extends UnchangedDocumentDiagnosticReport {
|
|
99
|
+
relatedDocuments?: {
|
|
100
|
+
[uri: string /** DocumentUri */]:
|
|
101
|
+
| FullDocumentDiagnosticReport
|
|
102
|
+
| UnchangedDocumentDiagnosticReport;
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
export type DocumentDiagnosticReport =
|
|
106
|
+
| RelatedFullDocumentDiagnosticReport
|
|
107
|
+
| RelatedUnchangedDocumentDiagnosticReport;
|
|
108
|
+
|
|
109
|
+
export type PreviousResultId = {
|
|
110
|
+
uri: Uri;
|
|
111
|
+
value: string;
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
export interface WorkspaceFullDocumentDiagnosticReport
|
|
115
|
+
extends FullDocumentDiagnosticReport {
|
|
116
|
+
uri: Uri;
|
|
117
|
+
version: number | null;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export interface WorkspaceUnchangedDocumentDiagnosticReport
|
|
121
|
+
extends UnchangedDocumentDiagnosticReport {
|
|
122
|
+
uri: Uri;
|
|
123
|
+
version: number | null;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export type WorkspaceDocumentDiagnosticReport =
|
|
127
|
+
| WorkspaceFullDocumentDiagnosticReport
|
|
128
|
+
| WorkspaceUnchangedDocumentDiagnosticReport;
|
|
129
|
+
|
|
130
|
+
export interface WorkspaceDiagnosticReport {
|
|
131
|
+
items: WorkspaceDocumentDiagnosticReport[];
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export interface WorkspaceDiagnosticReportPartialResult {
|
|
135
|
+
items: WorkspaceDocumentDiagnosticReport[];
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export interface ResultReporter {
|
|
139
|
+
(chunk: WorkspaceDiagnosticReportPartialResult | null): void;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export interface DiagnosticProvider {
|
|
143
|
+
onDidChangeDiagnostics: VEvent<void>;
|
|
144
|
+
provideDiagnostics(
|
|
145
|
+
document: TextDocument | Uri,
|
|
146
|
+
previousResultId: string | undefined,
|
|
147
|
+
token: CancellationToken,
|
|
148
|
+
): ProviderResult<DocumentDiagnosticReport>;
|
|
149
|
+
provideWorkspaceDiagnostics?(
|
|
150
|
+
resultIds: PreviousResultId[],
|
|
151
|
+
token: CancellationToken,
|
|
152
|
+
resultReporter: ResultReporter,
|
|
153
|
+
): ProviderResult<WorkspaceDiagnosticReport>;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export type ProvideDiagnosticSignature = (
|
|
158
|
+
this: void,
|
|
159
|
+
document: TextDocument | Uri,
|
|
160
|
+
previousResultId: string | undefined,
|
|
161
|
+
token: CancellationToken,
|
|
162
|
+
) => ProviderResult<vsdiag.DocumentDiagnosticReport>;
|
|
163
|
+
|
|
164
|
+
export type ProvideWorkspaceDiagnosticSignature = (
|
|
165
|
+
this: void,
|
|
166
|
+
resultIds: vsdiag.PreviousResultId[],
|
|
167
|
+
token: CancellationToken,
|
|
168
|
+
resultReporter: vsdiag.ResultReporter,
|
|
169
|
+
) => ProviderResult<vsdiag.WorkspaceDiagnosticReport>;
|
|
170
|
+
|
|
171
|
+
export type DiagnosticProviderMiddleware = {
|
|
172
|
+
provideDiagnostics?: (
|
|
173
|
+
this: void,
|
|
174
|
+
document: TextDocument | Uri,
|
|
175
|
+
previousResultId: string | undefined,
|
|
176
|
+
token: CancellationToken,
|
|
177
|
+
next: ProvideDiagnosticSignature,
|
|
178
|
+
) => ProviderResult<vsdiag.DocumentDiagnosticReport>;
|
|
179
|
+
provideWorkspaceDiagnostics?: (
|
|
180
|
+
this: void,
|
|
181
|
+
resultIds: vsdiag.PreviousResultId[],
|
|
182
|
+
token: CancellationToken,
|
|
183
|
+
resultReporter: vsdiag.ResultReporter,
|
|
184
|
+
next: ProvideWorkspaceDiagnosticSignature,
|
|
185
|
+
) => ProviderResult<vsdiag.WorkspaceDiagnosticReport>;
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
export enum DiagnosticPullMode {
|
|
189
|
+
onType = 'onType',
|
|
190
|
+
onSave = 'onSave',
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export type DiagnosticPullOptions = {
|
|
194
|
+
/**
|
|
195
|
+
* Whether to pull for diagnostics on document change.
|
|
196
|
+
*/
|
|
197
|
+
onChange?: boolean;
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Whether to pull for diagnostics on document save.
|
|
201
|
+
*/
|
|
202
|
+
onSave?: boolean;
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* An optional filter method that is consulted when triggering a
|
|
206
|
+
* diagnostic pull during document change or document save.
|
|
207
|
+
*
|
|
208
|
+
* The document gets filtered if the method returns `true`.
|
|
209
|
+
*
|
|
210
|
+
* @param document The document that changed or got saved.
|
|
211
|
+
* @param mode The pull mode.
|
|
212
|
+
* @returns whether the document should be filtered (`true`) or not.
|
|
213
|
+
*/
|
|
214
|
+
filter?(document: TextDocument, mode: DiagnosticPullMode): boolean;
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Whether to pull for diagnostics on resources of non instantiated
|
|
218
|
+
* tabs. If it is set to true it is highly recommended to provide
|
|
219
|
+
* a match method as well. Otherwise the client will not pull for
|
|
220
|
+
* tabs if the used document selector specifies a language property
|
|
221
|
+
* since the language value is not known for resources.
|
|
222
|
+
*/
|
|
223
|
+
onTabs?: boolean;
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* An optional match method that is consulted when pulling for diagnostics
|
|
227
|
+
* when only a URI is known (e.g. for not instantiated tabs)
|
|
228
|
+
*
|
|
229
|
+
* The method should return `true` if the document selector matches the
|
|
230
|
+
* given resource. See also the `vscode.languages.match` function.
|
|
231
|
+
*
|
|
232
|
+
* @param documentSelector The document selector.
|
|
233
|
+
* @param resource The resource.
|
|
234
|
+
* @returns whether the resource is matched by the given document selector.
|
|
235
|
+
*/
|
|
236
|
+
match?(documentSelector: DocumentSelector, resource: Uri): boolean;
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
export type $DiagnosticPullOptions = {
|
|
240
|
+
diagnosticPullOptions?: DiagnosticPullOptions;
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
enum RequestStateKind {
|
|
244
|
+
active = 'open',
|
|
245
|
+
reschedule = 'reschedule',
|
|
246
|
+
outDated = 'drop',
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
type RequestState =
|
|
250
|
+
| {
|
|
251
|
+
state: RequestStateKind.active;
|
|
252
|
+
document: TextDocument | Uri;
|
|
253
|
+
version: number | undefined;
|
|
254
|
+
tokenSource: CancellationTokenSource;
|
|
255
|
+
}
|
|
256
|
+
| {
|
|
257
|
+
state: RequestStateKind.reschedule;
|
|
258
|
+
document: TextDocument | Uri;
|
|
259
|
+
}
|
|
260
|
+
| {
|
|
261
|
+
state: RequestStateKind.outDated;
|
|
262
|
+
document: TextDocument | Uri;
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Manages the open tabs. We don't directly use the tab API since for
|
|
267
|
+
* diagnostics we need to de-dupe tabs that show the same resources since
|
|
268
|
+
* we pull on the model not the UI.
|
|
269
|
+
*/
|
|
270
|
+
class Tabs {
|
|
271
|
+
private open: Set<string>;
|
|
272
|
+
private readonly _onOpen: EventEmitter<Set<Uri>>;
|
|
273
|
+
private readonly _onClose: EventEmitter<Set<Uri>>;
|
|
274
|
+
private readonly disposable: Disposable;
|
|
275
|
+
|
|
276
|
+
constructor() {
|
|
277
|
+
this.open = new Set();
|
|
278
|
+
this._onOpen = new EventEmitter();
|
|
279
|
+
this._onClose = new EventEmitter();
|
|
280
|
+
Tabs.fillTabResources(this.open);
|
|
281
|
+
const openTabsHandler = (event: TabChangeEvent) => {
|
|
282
|
+
if (event.closed.length === 0 && event.opened.length === 0) {
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
const oldTabs = this.open;
|
|
286
|
+
const currentTabs: Set<string> = new Set();
|
|
287
|
+
Tabs.fillTabResources(currentTabs);
|
|
288
|
+
|
|
289
|
+
const closed: Set<string> = new Set();
|
|
290
|
+
const opened: Set<string> = new Set(currentTabs);
|
|
291
|
+
for (const tab of oldTabs.values()) {
|
|
292
|
+
if (currentTabs.has(tab)) {
|
|
293
|
+
opened.delete(tab);
|
|
294
|
+
} else {
|
|
295
|
+
closed.add(tab);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
this.open = currentTabs;
|
|
299
|
+
if (closed.size > 0) {
|
|
300
|
+
const toFire: Set<Uri> = new Set();
|
|
301
|
+
for (const item of closed) {
|
|
302
|
+
toFire.add(Uri.parse(item));
|
|
303
|
+
}
|
|
304
|
+
this._onClose.fire(toFire);
|
|
305
|
+
}
|
|
306
|
+
if (opened.size > 0) {
|
|
307
|
+
const toFire: Set<Uri> = new Set();
|
|
308
|
+
for (const item of opened) {
|
|
309
|
+
toFire.add(Uri.parse(item));
|
|
310
|
+
}
|
|
311
|
+
this._onOpen.fire(toFire);
|
|
312
|
+
}
|
|
313
|
+
};
|
|
314
|
+
|
|
315
|
+
if (Window.tabGroups.onDidChangeTabs !== undefined) {
|
|
316
|
+
this.disposable = Window.tabGroups.onDidChangeTabs(openTabsHandler);
|
|
317
|
+
} else {
|
|
318
|
+
this.disposable = {
|
|
319
|
+
dispose: () => {
|
|
320
|
+
return;
|
|
321
|
+
},
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
public get onClose(): Event<Set<Uri>> {
|
|
327
|
+
return this._onClose.event;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
public get onOpen(): Event<Set<Uri>> {
|
|
331
|
+
return this._onOpen.event;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
public dispose(): void {
|
|
335
|
+
this.disposable.dispose();
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
public isActive(document: TextDocument | Uri): boolean {
|
|
339
|
+
return document instanceof Uri
|
|
340
|
+
? Window.activeTextEditor?.document.uri === document
|
|
341
|
+
: Window.activeTextEditor?.document === document;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
public isVisible(document: TextDocument | Uri): boolean {
|
|
345
|
+
const uri = document instanceof Uri ? document : document.uri;
|
|
346
|
+
return this.open.has(uri.toString());
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
public getTabResources(): Set<Uri> {
|
|
350
|
+
const result: Set<Uri> = new Set();
|
|
351
|
+
Tabs.fillTabResources(new Set(), result);
|
|
352
|
+
return result;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
private static fillTabResources(
|
|
356
|
+
strings: Set<string> | undefined,
|
|
357
|
+
uris?: Set<Uri>,
|
|
358
|
+
): void {
|
|
359
|
+
const seen = strings ?? new Set();
|
|
360
|
+
for (const group of Window.tabGroups.all) {
|
|
361
|
+
for (const tab of group.tabs) {
|
|
362
|
+
const input = tab.input;
|
|
363
|
+
let uri: Uri | undefined;
|
|
364
|
+
if (input instanceof TabInputText) {
|
|
365
|
+
uri = input.uri;
|
|
366
|
+
} else if (input instanceof TabInputTextDiff) {
|
|
367
|
+
uri = input.modified;
|
|
368
|
+
} else if (input instanceof TabInputCustom) {
|
|
369
|
+
uri = input.uri;
|
|
370
|
+
}
|
|
371
|
+
if (uri !== undefined && !seen.has(uri.toString())) {
|
|
372
|
+
seen.add(uri.toString());
|
|
373
|
+
uris !== undefined && uris.add(uri);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
type DocumentPullState = {
|
|
381
|
+
document: Uri;
|
|
382
|
+
pulledVersion: number | undefined;
|
|
383
|
+
resultId: string | undefined;
|
|
384
|
+
};
|
|
385
|
+
|
|
386
|
+
enum PullState {
|
|
387
|
+
document = 1,
|
|
388
|
+
workspace = 2,
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
namespace DocumentOrUri {
|
|
392
|
+
export function asKey(document: TextDocument | Uri): string {
|
|
393
|
+
return document instanceof Uri ? document.toString() : document.uri.toString();
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
class DocumentPullStateTracker {
|
|
398
|
+
private readonly documentPullStates: Map<string, DocumentPullState>;
|
|
399
|
+
private readonly workspacePullStates: Map<string, DocumentPullState>;
|
|
400
|
+
|
|
401
|
+
constructor() {
|
|
402
|
+
this.documentPullStates = new Map();
|
|
403
|
+
this.workspacePullStates = new Map();
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
public track(kind: PullState, textDocument: TextDocument): DocumentPullState;
|
|
407
|
+
public track(
|
|
408
|
+
kind: PullState,
|
|
409
|
+
uri: Uri,
|
|
410
|
+
version: number | undefined,
|
|
411
|
+
): DocumentPullState;
|
|
412
|
+
public track(
|
|
413
|
+
kind: PullState,
|
|
414
|
+
document: TextDocument | Uri,
|
|
415
|
+
arg1?: number | undefined,
|
|
416
|
+
): DocumentPullState {
|
|
417
|
+
const states =
|
|
418
|
+
kind === PullState.document ? this.documentPullStates : this.workspacePullStates;
|
|
419
|
+
const [key, uri, version] =
|
|
420
|
+
document instanceof Uri
|
|
421
|
+
? [document.toString(), document, arg1 as number | undefined]
|
|
422
|
+
: [document.uri.toString(), document.uri, document.version];
|
|
423
|
+
let state = states.get(key);
|
|
424
|
+
if (state === undefined) {
|
|
425
|
+
state = { document: uri, pulledVersion: version, resultId: undefined };
|
|
426
|
+
states.set(key, state);
|
|
427
|
+
}
|
|
428
|
+
return state;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
public update(
|
|
432
|
+
kind: PullState,
|
|
433
|
+
textDocument: TextDocument,
|
|
434
|
+
resultId: string | undefined,
|
|
435
|
+
): void;
|
|
436
|
+
public update(
|
|
437
|
+
kind: PullState,
|
|
438
|
+
uri: Uri,
|
|
439
|
+
version: number | undefined,
|
|
440
|
+
resultId: string | undefined,
|
|
441
|
+
): void;
|
|
442
|
+
public update(
|
|
443
|
+
kind: PullState,
|
|
444
|
+
document: TextDocument | Uri,
|
|
445
|
+
arg1: string | number | undefined,
|
|
446
|
+
arg2?: string | undefined,
|
|
447
|
+
): void {
|
|
448
|
+
const states =
|
|
449
|
+
kind === PullState.document ? this.documentPullStates : this.workspacePullStates;
|
|
450
|
+
const [key, uri, version, resultId] =
|
|
451
|
+
document instanceof Uri
|
|
452
|
+
? [document.toString(), document, arg1 as number | undefined, arg2]
|
|
453
|
+
: [
|
|
454
|
+
document.uri.toString(),
|
|
455
|
+
document.uri,
|
|
456
|
+
document.version,
|
|
457
|
+
arg1 as string | undefined,
|
|
458
|
+
];
|
|
459
|
+
let state = states.get(key);
|
|
460
|
+
if (state === undefined) {
|
|
461
|
+
state = { document: uri, pulledVersion: version, resultId };
|
|
462
|
+
states.set(key, state);
|
|
463
|
+
} else {
|
|
464
|
+
state.pulledVersion = version;
|
|
465
|
+
state.resultId = resultId;
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
public unTrack(kind: PullState, document: TextDocument | Uri): void {
|
|
470
|
+
const key = DocumentOrUri.asKey(document);
|
|
471
|
+
const states =
|
|
472
|
+
kind === PullState.document ? this.documentPullStates : this.workspacePullStates;
|
|
473
|
+
states.delete(key);
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
public tracks(kind: PullState, document: TextDocument | Uri): boolean {
|
|
477
|
+
const key = DocumentOrUri.asKey(document);
|
|
478
|
+
const states =
|
|
479
|
+
kind === PullState.document ? this.documentPullStates : this.workspacePullStates;
|
|
480
|
+
return states.has(key);
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
public getResultId(
|
|
484
|
+
kind: PullState,
|
|
485
|
+
document: TextDocument | Uri,
|
|
486
|
+
): string | undefined {
|
|
487
|
+
const key = DocumentOrUri.asKey(document);
|
|
488
|
+
const states =
|
|
489
|
+
kind === PullState.document ? this.documentPullStates : this.workspacePullStates;
|
|
490
|
+
return states.get(key)?.resultId;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
public getAllResultIds(): PreviousResultId[] {
|
|
494
|
+
const result: PreviousResultId[] = [];
|
|
495
|
+
// eslint-disable-next-line prefer-const
|
|
496
|
+
for (let [uri, value] of this.workspacePullStates) {
|
|
497
|
+
if (this.documentPullStates.has(uri)) {
|
|
498
|
+
value = this.documentPullStates.get(uri)!;
|
|
499
|
+
}
|
|
500
|
+
if (value.resultId !== undefined) {
|
|
501
|
+
result.push({ uri, value: value.resultId });
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
return result;
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
class DiagnosticRequestor implements Disposable {
|
|
509
|
+
private isDisposed: boolean;
|
|
510
|
+
private readonly client: FeatureClient<
|
|
511
|
+
DiagnosticProviderMiddleware,
|
|
512
|
+
$DiagnosticPullOptions
|
|
513
|
+
>;
|
|
514
|
+
private readonly tabs: Tabs;
|
|
515
|
+
private readonly options: DiagnosticRegistrationOptions;
|
|
516
|
+
|
|
517
|
+
public readonly onDidChangeDiagnosticsEmitter: EventEmitter<void>;
|
|
518
|
+
public readonly provider: vsdiag.DiagnosticProvider;
|
|
519
|
+
private readonly diagnostics: DiagnosticCollection;
|
|
520
|
+
private readonly openRequests: Map<string, RequestState>;
|
|
521
|
+
private readonly documentStates: DocumentPullStateTracker;
|
|
522
|
+
|
|
523
|
+
private workspaceErrorCounter: number;
|
|
524
|
+
private workspaceCancellation: CancellationTokenSource | undefined;
|
|
525
|
+
private workspaceTimeout: Disposable | undefined;
|
|
526
|
+
|
|
527
|
+
public constructor(
|
|
528
|
+
client: FeatureClient<DiagnosticProviderMiddleware, $DiagnosticPullOptions>,
|
|
529
|
+
tabs: Tabs,
|
|
530
|
+
options: DiagnosticRegistrationOptions,
|
|
531
|
+
) {
|
|
532
|
+
this.client = client;
|
|
533
|
+
this.tabs = tabs;
|
|
534
|
+
this.options = options;
|
|
535
|
+
|
|
536
|
+
this.isDisposed = false;
|
|
537
|
+
this.onDidChangeDiagnosticsEmitter = new EventEmitter<void>();
|
|
538
|
+
this.provider = this.createProvider();
|
|
539
|
+
|
|
540
|
+
this.diagnostics = Languages.createDiagnosticCollection(options.identifier);
|
|
541
|
+
this.openRequests = new Map();
|
|
542
|
+
this.documentStates = new DocumentPullStateTracker();
|
|
543
|
+
this.workspaceErrorCounter = 0;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
public knows(kind: PullState, document: TextDocument | Uri): boolean {
|
|
547
|
+
const uri = document instanceof Uri ? document : document.uri;
|
|
548
|
+
return (
|
|
549
|
+
this.documentStates.tracks(kind, document) ||
|
|
550
|
+
this.openRequests.has(uri.toString())
|
|
551
|
+
);
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
public forget(kind: PullState, document: TextDocument | Uri): void {
|
|
555
|
+
this.documentStates.unTrack(kind, document);
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
public pull(document: TextDocument | Uri, cb?: () => void): void {
|
|
559
|
+
if (this.isDisposed) {
|
|
560
|
+
return;
|
|
561
|
+
}
|
|
562
|
+
const uri = document instanceof Uri ? document : document.uri;
|
|
563
|
+
this.pullAsync(document).then(
|
|
564
|
+
() => {
|
|
565
|
+
if (cb) {
|
|
566
|
+
cb();
|
|
567
|
+
}
|
|
568
|
+
},
|
|
569
|
+
(error) => {
|
|
570
|
+
this.client.error(
|
|
571
|
+
`Document pull failed for text document ${uri.toString()}`,
|
|
572
|
+
error,
|
|
573
|
+
false,
|
|
574
|
+
);
|
|
575
|
+
},
|
|
576
|
+
);
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
private async pullAsync(
|
|
580
|
+
document: TextDocument | Uri,
|
|
581
|
+
version?: number | undefined,
|
|
582
|
+
): Promise<void> {
|
|
583
|
+
if (this.isDisposed) {
|
|
584
|
+
return;
|
|
585
|
+
}
|
|
586
|
+
const isUri = document instanceof Uri;
|
|
587
|
+
const uri = isUri ? document : document.uri;
|
|
588
|
+
const key = uri.toString();
|
|
589
|
+
version = isUri ? version : document.version;
|
|
590
|
+
const currentRequestState = this.openRequests.get(key);
|
|
591
|
+
const documentState = isUri
|
|
592
|
+
? this.documentStates.track(PullState.document, document, version)
|
|
593
|
+
: this.documentStates.track(PullState.document, document);
|
|
594
|
+
if (currentRequestState === undefined) {
|
|
595
|
+
const tokenSource = new CancellationTokenSource();
|
|
596
|
+
this.openRequests.set(key, {
|
|
597
|
+
state: RequestStateKind.active,
|
|
598
|
+
document: document,
|
|
599
|
+
version: version,
|
|
600
|
+
tokenSource,
|
|
601
|
+
});
|
|
602
|
+
let report: vsdiag.DocumentDiagnosticReport | undefined;
|
|
603
|
+
let afterState: RequestState | undefined;
|
|
604
|
+
try {
|
|
605
|
+
report = (await this.provider.provideDiagnostics(
|
|
606
|
+
document,
|
|
607
|
+
documentState.resultId,
|
|
608
|
+
tokenSource.token,
|
|
609
|
+
)) ?? { kind: vsdiag.DocumentDiagnosticReportKind.full, items: [] };
|
|
610
|
+
} catch (error) {
|
|
611
|
+
if (
|
|
612
|
+
error instanceof LSPCancellationError &&
|
|
613
|
+
DiagnosticServerCancellationData.is(error.data) &&
|
|
614
|
+
error.data.retriggerRequest === false
|
|
615
|
+
) {
|
|
616
|
+
afterState = { state: RequestStateKind.outDated, document };
|
|
617
|
+
}
|
|
618
|
+
if (afterState === undefined && error instanceof CancellationError) {
|
|
619
|
+
afterState = { state: RequestStateKind.reschedule, document };
|
|
620
|
+
} else {
|
|
621
|
+
throw error;
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
afterState = afterState ?? this.openRequests.get(key);
|
|
625
|
+
if (afterState === undefined) {
|
|
626
|
+
// This shouldn't happen. Log it
|
|
627
|
+
this.client.error(
|
|
628
|
+
`Lost request state in diagnostic pull model. Clearing diagnostics for ${key}`,
|
|
629
|
+
);
|
|
630
|
+
this.diagnostics.delete(uri);
|
|
631
|
+
return;
|
|
632
|
+
}
|
|
633
|
+
this.openRequests.delete(key);
|
|
634
|
+
if (!this.tabs.isVisible(document)) {
|
|
635
|
+
this.documentStates.unTrack(PullState.document, document);
|
|
636
|
+
return;
|
|
637
|
+
}
|
|
638
|
+
if (afterState.state === RequestStateKind.outDated) {
|
|
639
|
+
return;
|
|
640
|
+
}
|
|
641
|
+
// report is only undefined if the request has thrown.
|
|
642
|
+
if (report !== undefined) {
|
|
643
|
+
if (report.kind === vsdiag.DocumentDiagnosticReportKind.full) {
|
|
644
|
+
this.diagnostics.set(uri, report.items);
|
|
645
|
+
}
|
|
646
|
+
documentState.pulledVersion = version;
|
|
647
|
+
documentState.resultId = report.resultId;
|
|
648
|
+
}
|
|
649
|
+
if (afterState.state === RequestStateKind.reschedule) {
|
|
650
|
+
this.pull(document);
|
|
651
|
+
}
|
|
652
|
+
} else {
|
|
653
|
+
if (currentRequestState.state === RequestStateKind.active) {
|
|
654
|
+
// Cancel the current request and reschedule a new one when the old one returned.
|
|
655
|
+
currentRequestState.tokenSource.cancel();
|
|
656
|
+
this.openRequests.set(key, {
|
|
657
|
+
state: RequestStateKind.reschedule,
|
|
658
|
+
document: currentRequestState.document,
|
|
659
|
+
});
|
|
660
|
+
} else if (currentRequestState.state === RequestStateKind.outDated) {
|
|
661
|
+
this.openRequests.set(key, {
|
|
662
|
+
state: RequestStateKind.reschedule,
|
|
663
|
+
document: currentRequestState.document,
|
|
664
|
+
});
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
public forgetDocument(document: TextDocument | Uri): void {
|
|
670
|
+
const uri = document instanceof Uri ? document : document.uri;
|
|
671
|
+
const key = uri.toString();
|
|
672
|
+
const request = this.openRequests.get(key);
|
|
673
|
+
if (this.options.workspaceDiagnostics) {
|
|
674
|
+
// If we run workspace diagnostic pull a last time for the diagnostics
|
|
675
|
+
// and the rely on getting them from the workspace result.
|
|
676
|
+
if (request !== undefined) {
|
|
677
|
+
this.openRequests.set(key, {
|
|
678
|
+
state: RequestStateKind.reschedule,
|
|
679
|
+
document: document,
|
|
680
|
+
});
|
|
681
|
+
} else {
|
|
682
|
+
this.pull(document, () => {
|
|
683
|
+
this.forget(PullState.document, document);
|
|
684
|
+
});
|
|
685
|
+
}
|
|
686
|
+
} else {
|
|
687
|
+
// We have normal pull or inter file dependencies. In this case we
|
|
688
|
+
// clear the diagnostics (to have the same start as after startup).
|
|
689
|
+
// We also cancel outstanding requests.
|
|
690
|
+
if (request !== undefined) {
|
|
691
|
+
if (request.state === RequestStateKind.active) {
|
|
692
|
+
request.tokenSource.cancel();
|
|
693
|
+
}
|
|
694
|
+
this.openRequests.set(key, {
|
|
695
|
+
state: RequestStateKind.outDated,
|
|
696
|
+
document: document,
|
|
697
|
+
});
|
|
698
|
+
}
|
|
699
|
+
this.diagnostics.delete(uri);
|
|
700
|
+
this.forget(PullState.document, document);
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
public pullWorkspace(): void {
|
|
705
|
+
if (this.isDisposed) {
|
|
706
|
+
return;
|
|
707
|
+
}
|
|
708
|
+
this.pullWorkspaceAsync().then(
|
|
709
|
+
() => {
|
|
710
|
+
this.workspaceTimeout = RAL().timer.setTimeout(() => {
|
|
711
|
+
this.pullWorkspace();
|
|
712
|
+
}, 2000);
|
|
713
|
+
},
|
|
714
|
+
(error) => {
|
|
715
|
+
if (
|
|
716
|
+
!(error instanceof LSPCancellationError) &&
|
|
717
|
+
!DiagnosticServerCancellationData.is(error.data)
|
|
718
|
+
) {
|
|
719
|
+
this.client.error(`Workspace diagnostic pull failed.`, error, false);
|
|
720
|
+
this.workspaceErrorCounter++;
|
|
721
|
+
}
|
|
722
|
+
if (this.workspaceErrorCounter <= 5) {
|
|
723
|
+
this.workspaceTimeout = RAL().timer.setTimeout(() => {
|
|
724
|
+
this.pullWorkspace();
|
|
725
|
+
}, 2000);
|
|
726
|
+
}
|
|
727
|
+
},
|
|
728
|
+
);
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
private async pullWorkspaceAsync(): Promise<void> {
|
|
732
|
+
if (!this.provider.provideWorkspaceDiagnostics || this.isDisposed) {
|
|
733
|
+
return;
|
|
734
|
+
}
|
|
735
|
+
if (this.workspaceCancellation !== undefined) {
|
|
736
|
+
this.workspaceCancellation.cancel();
|
|
737
|
+
this.workspaceCancellation = undefined;
|
|
738
|
+
}
|
|
739
|
+
this.workspaceCancellation = new CancellationTokenSource();
|
|
740
|
+
const previousResultIds: vsdiag.PreviousResultId[] = this.documentStates
|
|
741
|
+
.getAllResultIds()
|
|
742
|
+
.map((item) => {
|
|
743
|
+
return {
|
|
744
|
+
uri: this.client.protocol2CodeConverter.asUri(item.uri),
|
|
745
|
+
value: item.value,
|
|
746
|
+
};
|
|
747
|
+
});
|
|
748
|
+
await this.provider.provideWorkspaceDiagnostics(
|
|
749
|
+
previousResultIds,
|
|
750
|
+
this.workspaceCancellation.token,
|
|
751
|
+
(chunk) => {
|
|
752
|
+
if (!chunk || this.isDisposed) {
|
|
753
|
+
return;
|
|
754
|
+
}
|
|
755
|
+
for (const item of chunk.items) {
|
|
756
|
+
if (item.kind === vsdiag.DocumentDiagnosticReportKind.full) {
|
|
757
|
+
// Favour document pull result over workspace results. So skip if it is tracked
|
|
758
|
+
// as a document result.
|
|
759
|
+
if (!this.documentStates.tracks(PullState.document, item.uri)) {
|
|
760
|
+
this.diagnostics.set(item.uri, item.items);
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
this.documentStates.update(
|
|
764
|
+
PullState.workspace,
|
|
765
|
+
item.uri,
|
|
766
|
+
item.version ?? undefined,
|
|
767
|
+
item.resultId,
|
|
768
|
+
);
|
|
769
|
+
}
|
|
770
|
+
},
|
|
771
|
+
);
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
private createProvider(): vsdiag.DiagnosticProvider {
|
|
775
|
+
const result: vsdiag.DiagnosticProvider = {
|
|
776
|
+
onDidChangeDiagnostics: this.onDidChangeDiagnosticsEmitter.event,
|
|
777
|
+
provideDiagnostics: (document, previousResultId, token) => {
|
|
778
|
+
const provideDiagnostics: ProvideDiagnosticSignature = (
|
|
779
|
+
document,
|
|
780
|
+
previousResultId,
|
|
781
|
+
token,
|
|
782
|
+
) => {
|
|
783
|
+
const params: DocumentDiagnosticParams = {
|
|
784
|
+
identifier: this.options.identifier,
|
|
785
|
+
textDocument: {
|
|
786
|
+
uri: this.client.code2ProtocolConverter.asUri(
|
|
787
|
+
document instanceof Uri ? document : document.uri,
|
|
788
|
+
),
|
|
789
|
+
},
|
|
790
|
+
previousResultId: previousResultId,
|
|
791
|
+
};
|
|
792
|
+
if (this.isDisposed === true || !this.client.isRunning()) {
|
|
793
|
+
return { kind: vsdiag.DocumentDiagnosticReportKind.full, items: [] };
|
|
794
|
+
}
|
|
795
|
+
return this.client
|
|
796
|
+
.sendRequest(DocumentDiagnosticRequest.type, params, token)
|
|
797
|
+
.then(
|
|
798
|
+
async (result) => {
|
|
799
|
+
if (
|
|
800
|
+
result === undefined ||
|
|
801
|
+
result === null ||
|
|
802
|
+
this.isDisposed ||
|
|
803
|
+
token.isCancellationRequested
|
|
804
|
+
) {
|
|
805
|
+
return { kind: vsdiag.DocumentDiagnosticReportKind.full, items: [] };
|
|
806
|
+
}
|
|
807
|
+
if (result.kind === DocumentDiagnosticReportKind.Full) {
|
|
808
|
+
return {
|
|
809
|
+
kind: vsdiag.DocumentDiagnosticReportKind.full,
|
|
810
|
+
resultId: result.resultId,
|
|
811
|
+
items: await this.client.protocol2CodeConverter.asDiagnostics(
|
|
812
|
+
result.items,
|
|
813
|
+
token,
|
|
814
|
+
),
|
|
815
|
+
};
|
|
816
|
+
} else {
|
|
817
|
+
return {
|
|
818
|
+
kind: vsdiag.DocumentDiagnosticReportKind.unChanged,
|
|
819
|
+
resultId: result.resultId,
|
|
820
|
+
};
|
|
821
|
+
}
|
|
822
|
+
},
|
|
823
|
+
(error) => {
|
|
824
|
+
return this.client.handleFailedRequest(
|
|
825
|
+
DocumentDiagnosticRequest.type,
|
|
826
|
+
token,
|
|
827
|
+
error,
|
|
828
|
+
{ kind: vsdiag.DocumentDiagnosticReportKind.full, items: [] },
|
|
829
|
+
);
|
|
830
|
+
},
|
|
831
|
+
);
|
|
832
|
+
};
|
|
833
|
+
const middleware: DiagnosticProviderMiddleware = this.client.middleware;
|
|
834
|
+
return middleware.provideDiagnostics
|
|
835
|
+
? middleware.provideDiagnostics(
|
|
836
|
+
document,
|
|
837
|
+
previousResultId,
|
|
838
|
+
token,
|
|
839
|
+
provideDiagnostics,
|
|
840
|
+
)
|
|
841
|
+
: provideDiagnostics(document, previousResultId, token);
|
|
842
|
+
},
|
|
843
|
+
};
|
|
844
|
+
if (this.options.workspaceDiagnostics) {
|
|
845
|
+
result.provideWorkspaceDiagnostics = (
|
|
846
|
+
resultIds,
|
|
847
|
+
token,
|
|
848
|
+
resultReporter,
|
|
849
|
+
): ProviderResult<vsdiag.WorkspaceDiagnosticReport> => {
|
|
850
|
+
const convertReport = async (
|
|
851
|
+
report: WorkspaceDocumentDiagnosticReport,
|
|
852
|
+
): Promise<vsdiag.WorkspaceDocumentDiagnosticReport> => {
|
|
853
|
+
if (report.kind === DocumentDiagnosticReportKind.Full) {
|
|
854
|
+
return {
|
|
855
|
+
kind: vsdiag.DocumentDiagnosticReportKind.full,
|
|
856
|
+
uri: this.client.protocol2CodeConverter.asUri(report.uri),
|
|
857
|
+
resultId: report.resultId,
|
|
858
|
+
version: report.version,
|
|
859
|
+
items: await this.client.protocol2CodeConverter.asDiagnostics(
|
|
860
|
+
report.items,
|
|
861
|
+
token,
|
|
862
|
+
),
|
|
863
|
+
};
|
|
864
|
+
} else {
|
|
865
|
+
return {
|
|
866
|
+
kind: vsdiag.DocumentDiagnosticReportKind.unChanged,
|
|
867
|
+
uri: this.client.protocol2CodeConverter.asUri(report.uri),
|
|
868
|
+
resultId: report.resultId,
|
|
869
|
+
version: report.version,
|
|
870
|
+
};
|
|
871
|
+
}
|
|
872
|
+
};
|
|
873
|
+
const convertPreviousResultIds = (
|
|
874
|
+
resultIds: vsdiag.PreviousResultId[],
|
|
875
|
+
): PreviousResultId[] => {
|
|
876
|
+
const converted: PreviousResultId[] = [];
|
|
877
|
+
for (const item of resultIds) {
|
|
878
|
+
converted.push({
|
|
879
|
+
uri: this.client.code2ProtocolConverter.asUri(item.uri),
|
|
880
|
+
value: item.value,
|
|
881
|
+
});
|
|
882
|
+
}
|
|
883
|
+
return converted;
|
|
884
|
+
};
|
|
885
|
+
const provideDiagnostics: ProvideWorkspaceDiagnosticSignature = (
|
|
886
|
+
resultIds,
|
|
887
|
+
token,
|
|
888
|
+
): ProviderResult<vsdiag.WorkspaceDiagnosticReport> => {
|
|
889
|
+
const partialResultToken: string = generateUuid();
|
|
890
|
+
const disposable = this.client.onProgress(
|
|
891
|
+
WorkspaceDiagnosticRequest.partialResult,
|
|
892
|
+
partialResultToken,
|
|
893
|
+
async (partialResult) => {
|
|
894
|
+
if (partialResult === undefined || partialResult === null) {
|
|
895
|
+
resultReporter(null);
|
|
896
|
+
return;
|
|
897
|
+
}
|
|
898
|
+
const converted: vsdiag.WorkspaceDiagnosticReportPartialResult = {
|
|
899
|
+
items: [],
|
|
900
|
+
};
|
|
901
|
+
for (const item of partialResult.items) {
|
|
902
|
+
try {
|
|
903
|
+
converted.items.push(await convertReport(item));
|
|
904
|
+
} catch (error) {
|
|
905
|
+
this.client.error(`Converting workspace diagnostics failed.`, error);
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
resultReporter(converted);
|
|
909
|
+
},
|
|
910
|
+
);
|
|
911
|
+
const params: WorkspaceDiagnosticParams = {
|
|
912
|
+
identifier: this.options.identifier,
|
|
913
|
+
previousResultIds: convertPreviousResultIds(resultIds),
|
|
914
|
+
partialResultToken: partialResultToken,
|
|
915
|
+
};
|
|
916
|
+
if (this.isDisposed === true || !this.client.isRunning()) {
|
|
917
|
+
return { items: [] };
|
|
918
|
+
}
|
|
919
|
+
return this.client
|
|
920
|
+
.sendRequest(WorkspaceDiagnosticRequest.type, params, token)
|
|
921
|
+
.then(
|
|
922
|
+
async (result): Promise<vsdiag.WorkspaceDiagnosticReport> => {
|
|
923
|
+
if (token.isCancellationRequested) {
|
|
924
|
+
return { items: [] };
|
|
925
|
+
}
|
|
926
|
+
const converted: vsdiag.WorkspaceDiagnosticReport = {
|
|
927
|
+
items: [],
|
|
928
|
+
};
|
|
929
|
+
for (const item of result.items) {
|
|
930
|
+
converted.items.push(await convertReport(item));
|
|
931
|
+
}
|
|
932
|
+
disposable.dispose();
|
|
933
|
+
resultReporter(converted);
|
|
934
|
+
return { items: [] };
|
|
935
|
+
},
|
|
936
|
+
(error) => {
|
|
937
|
+
disposable.dispose();
|
|
938
|
+
return this.client.handleFailedRequest(
|
|
939
|
+
DocumentDiagnosticRequest.type,
|
|
940
|
+
token,
|
|
941
|
+
error,
|
|
942
|
+
{ items: [] },
|
|
943
|
+
);
|
|
944
|
+
},
|
|
945
|
+
);
|
|
946
|
+
};
|
|
947
|
+
const middleware: DiagnosticProviderMiddleware = this.client.middleware;
|
|
948
|
+
return middleware.provideWorkspaceDiagnostics
|
|
949
|
+
? middleware.provideWorkspaceDiagnostics(
|
|
950
|
+
resultIds,
|
|
951
|
+
token,
|
|
952
|
+
resultReporter,
|
|
953
|
+
provideDiagnostics,
|
|
954
|
+
)
|
|
955
|
+
: provideDiagnostics(resultIds, token, resultReporter);
|
|
956
|
+
};
|
|
957
|
+
}
|
|
958
|
+
return result;
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
public dispose(): void {
|
|
962
|
+
this.isDisposed = true;
|
|
963
|
+
|
|
964
|
+
// Cancel and clear workspace pull if present.
|
|
965
|
+
this.workspaceCancellation?.cancel();
|
|
966
|
+
this.workspaceTimeout?.dispose();
|
|
967
|
+
|
|
968
|
+
// Cancel all request and mark open requests as outdated.
|
|
969
|
+
for (const [key, request] of this.openRequests) {
|
|
970
|
+
if (request.state === RequestStateKind.active) {
|
|
971
|
+
request.tokenSource.cancel();
|
|
972
|
+
}
|
|
973
|
+
this.openRequests.set(key, {
|
|
974
|
+
state: RequestStateKind.outDated,
|
|
975
|
+
document: request.document,
|
|
976
|
+
});
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
// cleanup old diagnostics
|
|
980
|
+
this.diagnostics.dispose();
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
export type DiagnosticProviderShape = {
|
|
985
|
+
onDidChangeDiagnosticsEmitter: EventEmitter<void>;
|
|
986
|
+
diagnostics: vsdiag.DiagnosticProvider;
|
|
987
|
+
};
|
|
988
|
+
|
|
989
|
+
class BackgroundScheduler implements Disposable {
|
|
990
|
+
private readonly diagnosticRequestor: DiagnosticRequestor;
|
|
991
|
+
private endDocument: TextDocument | Uri | undefined;
|
|
992
|
+
private readonly documents: LinkedMap<string, TextDocument | Uri>;
|
|
993
|
+
private intervalHandle: Disposable | undefined;
|
|
994
|
+
// The problem is that there could be outstanding diagnostic requests
|
|
995
|
+
// when we shutdown which when we receive the result will trigger a
|
|
996
|
+
// reschedule. So we remember if the background scheduler got disposed
|
|
997
|
+
// and ignore those re-schedules
|
|
998
|
+
private isDisposed: boolean;
|
|
999
|
+
|
|
1000
|
+
public constructor(diagnosticRequestor: DiagnosticRequestor) {
|
|
1001
|
+
this.diagnosticRequestor = diagnosticRequestor;
|
|
1002
|
+
this.documents = new LinkedMap();
|
|
1003
|
+
this.isDisposed = false;
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
public add(document: TextDocument | Uri): void {
|
|
1007
|
+
if (this.isDisposed === true) {
|
|
1008
|
+
return;
|
|
1009
|
+
}
|
|
1010
|
+
const key = DocumentOrUri.asKey(document);
|
|
1011
|
+
if (this.documents.has(key)) {
|
|
1012
|
+
return;
|
|
1013
|
+
}
|
|
1014
|
+
this.documents.set(key, document, Touch.Last);
|
|
1015
|
+
this.trigger();
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
public remove(document: TextDocument | Uri): void {
|
|
1019
|
+
const key = DocumentOrUri.asKey(document);
|
|
1020
|
+
this.documents.delete(key);
|
|
1021
|
+
// No more documents. Stop background activity.
|
|
1022
|
+
if (this.documents.size === 0) {
|
|
1023
|
+
this.stop();
|
|
1024
|
+
} else if (key === this.endDocumentKey()) {
|
|
1025
|
+
// Make sure we have a correct last document. It could have
|
|
1026
|
+
this.endDocument = this.documents.last;
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
public trigger(): void {
|
|
1031
|
+
if (this.isDisposed === true) {
|
|
1032
|
+
return;
|
|
1033
|
+
}
|
|
1034
|
+
// We have a round running. So simply make sure we run up to the
|
|
1035
|
+
// last document
|
|
1036
|
+
if (this.intervalHandle !== undefined) {
|
|
1037
|
+
this.endDocument = this.documents.last;
|
|
1038
|
+
return;
|
|
1039
|
+
}
|
|
1040
|
+
this.endDocument = this.documents.last;
|
|
1041
|
+
this.intervalHandle = RAL().timer.setInterval(() => {
|
|
1042
|
+
const document = this.documents.first;
|
|
1043
|
+
if (document !== undefined) {
|
|
1044
|
+
const key = DocumentOrUri.asKey(document);
|
|
1045
|
+
this.diagnosticRequestor.pull(document);
|
|
1046
|
+
this.documents.set(key, document, Touch.Last);
|
|
1047
|
+
if (key === this.endDocumentKey()) {
|
|
1048
|
+
this.stop();
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
}, 200);
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
public dispose(): void {
|
|
1055
|
+
this.isDisposed = true;
|
|
1056
|
+
this.stop();
|
|
1057
|
+
this.documents.clear();
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
private stop(): void {
|
|
1061
|
+
this.intervalHandle?.dispose();
|
|
1062
|
+
this.intervalHandle = undefined;
|
|
1063
|
+
this.endDocument = undefined;
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
private endDocumentKey(): string | undefined {
|
|
1067
|
+
return this.endDocument !== undefined
|
|
1068
|
+
? DocumentOrUri.asKey(this.endDocument)
|
|
1069
|
+
: undefined;
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
class DiagnosticFeatureProviderImpl implements DiagnosticProviderShape {
|
|
1074
|
+
public readonly disposable: Disposable;
|
|
1075
|
+
private readonly diagnosticRequestor: DiagnosticRequestor;
|
|
1076
|
+
private activeTextDocument: TextDocument | undefined;
|
|
1077
|
+
private readonly backgroundScheduler: BackgroundScheduler;
|
|
1078
|
+
|
|
1079
|
+
constructor(
|
|
1080
|
+
client: FeatureClient<DiagnosticProviderMiddleware, $DiagnosticPullOptions>,
|
|
1081
|
+
tabs: Tabs,
|
|
1082
|
+
options: DiagnosticRegistrationOptions,
|
|
1083
|
+
) {
|
|
1084
|
+
const diagnosticPullOptions = client.clientOptions.diagnosticPullOptions ?? {
|
|
1085
|
+
onChange: true,
|
|
1086
|
+
onSave: false,
|
|
1087
|
+
};
|
|
1088
|
+
const documentSelector = client.protocol2CodeConverter.asDocumentSelector(
|
|
1089
|
+
options.documentSelector!,
|
|
1090
|
+
);
|
|
1091
|
+
const disposables: Disposable[] = [];
|
|
1092
|
+
|
|
1093
|
+
const matchResource = (resource: Uri) => {
|
|
1094
|
+
const selector = options.documentSelector!;
|
|
1095
|
+
if (diagnosticPullOptions.match !== undefined) {
|
|
1096
|
+
return diagnosticPullOptions.match(selector!, resource);
|
|
1097
|
+
}
|
|
1098
|
+
for (const filter of selector) {
|
|
1099
|
+
if (!TextDocumentFilter.is(filter)) {
|
|
1100
|
+
continue;
|
|
1101
|
+
}
|
|
1102
|
+
// The filter is a language id. We can't determine if it matches
|
|
1103
|
+
// so we return false.
|
|
1104
|
+
if (typeof filter === 'string') {
|
|
1105
|
+
return false;
|
|
1106
|
+
}
|
|
1107
|
+
if (filter.language !== undefined && filter.language !== '*') {
|
|
1108
|
+
return false;
|
|
1109
|
+
}
|
|
1110
|
+
if (
|
|
1111
|
+
filter.scheme !== undefined &&
|
|
1112
|
+
filter.scheme !== '*' &&
|
|
1113
|
+
filter.scheme !== resource.scheme
|
|
1114
|
+
) {
|
|
1115
|
+
return false;
|
|
1116
|
+
}
|
|
1117
|
+
if (filter.pattern !== undefined) {
|
|
1118
|
+
const matcher = new minimatch.Minimatch(filter.pattern, { noext: true });
|
|
1119
|
+
if (!matcher.makeRe()) {
|
|
1120
|
+
return false;
|
|
1121
|
+
}
|
|
1122
|
+
if (!matcher.match(resource.fsPath)) {
|
|
1123
|
+
return false;
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1126
|
+
}
|
|
1127
|
+
return true;
|
|
1128
|
+
};
|
|
1129
|
+
|
|
1130
|
+
const matches = (document: TextDocument | Uri): boolean => {
|
|
1131
|
+
return document instanceof Uri
|
|
1132
|
+
? matchResource(document)
|
|
1133
|
+
: Languages.match(documentSelector, document) > 0 && tabs.isVisible(document);
|
|
1134
|
+
};
|
|
1135
|
+
|
|
1136
|
+
const isActiveDocument = (document: TextDocument | Uri): boolean => {
|
|
1137
|
+
return document instanceof Uri
|
|
1138
|
+
? this.activeTextDocument?.uri.toString() === document.toString()
|
|
1139
|
+
: this.activeTextDocument === document;
|
|
1140
|
+
};
|
|
1141
|
+
|
|
1142
|
+
this.diagnosticRequestor = new DiagnosticRequestor(client, tabs, options);
|
|
1143
|
+
this.backgroundScheduler = new BackgroundScheduler(this.diagnosticRequestor);
|
|
1144
|
+
|
|
1145
|
+
const addToBackgroundIfNeeded = (document: TextDocument | Uri): void => {
|
|
1146
|
+
if (
|
|
1147
|
+
!matches(document) ||
|
|
1148
|
+
!options.interFileDependencies ||
|
|
1149
|
+
isActiveDocument(document)
|
|
1150
|
+
) {
|
|
1151
|
+
return;
|
|
1152
|
+
}
|
|
1153
|
+
this.backgroundScheduler.add(document);
|
|
1154
|
+
};
|
|
1155
|
+
|
|
1156
|
+
this.activeTextDocument = Window.activeTextEditor?.document;
|
|
1157
|
+
Window.onDidChangeActiveTextEditor((editor) => {
|
|
1158
|
+
const oldActive = this.activeTextDocument;
|
|
1159
|
+
this.activeTextDocument = editor?.document;
|
|
1160
|
+
if (oldActive !== undefined) {
|
|
1161
|
+
addToBackgroundIfNeeded(oldActive);
|
|
1162
|
+
}
|
|
1163
|
+
if (this.activeTextDocument !== undefined) {
|
|
1164
|
+
this.backgroundScheduler.remove(this.activeTextDocument);
|
|
1165
|
+
}
|
|
1166
|
+
});
|
|
1167
|
+
|
|
1168
|
+
// For pull model diagnostics we pull for documents visible in the UI.
|
|
1169
|
+
// From an eventing point of view we still rely on open document events
|
|
1170
|
+
// and filter the documents that are not visible in the UI instead of
|
|
1171
|
+
// listening to Tab events. Major reason is event timing since we need
|
|
1172
|
+
// to ensure that the pull is send after the document open has reached
|
|
1173
|
+
// the server.
|
|
1174
|
+
|
|
1175
|
+
// We always pull on open.
|
|
1176
|
+
const openFeature = client.getFeature(DidOpenTextDocumentNotification.method);
|
|
1177
|
+
disposables.push(
|
|
1178
|
+
openFeature.onNotificationSent((event) => {
|
|
1179
|
+
const textDocument = event.textDocument;
|
|
1180
|
+
// We already know about this document. This can happen via a tab open.
|
|
1181
|
+
if (this.diagnosticRequestor.knows(PullState.document, textDocument)) {
|
|
1182
|
+
return;
|
|
1183
|
+
}
|
|
1184
|
+
if (matches(textDocument)) {
|
|
1185
|
+
this.diagnosticRequestor.pull(textDocument, () => {
|
|
1186
|
+
addToBackgroundIfNeeded(textDocument);
|
|
1187
|
+
});
|
|
1188
|
+
}
|
|
1189
|
+
}),
|
|
1190
|
+
);
|
|
1191
|
+
|
|
1192
|
+
disposables.push(
|
|
1193
|
+
tabs.onOpen((opened) => {
|
|
1194
|
+
for (const resource of opened) {
|
|
1195
|
+
// We already know about this document. This can happen via a document open.
|
|
1196
|
+
if (this.diagnosticRequestor.knows(PullState.document, resource)) {
|
|
1197
|
+
continue;
|
|
1198
|
+
}
|
|
1199
|
+
const uriStr = resource.toString();
|
|
1200
|
+
let textDocument: TextDocument | undefined;
|
|
1201
|
+
for (const item of workspace.textDocuments) {
|
|
1202
|
+
if (uriStr === item.uri.toString()) {
|
|
1203
|
+
textDocument = item;
|
|
1204
|
+
break;
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
1207
|
+
// In VS Code the event timing is as follows:
|
|
1208
|
+
// 1. tab events are fired.
|
|
1209
|
+
// 2. open document events are fired and internal data structures like
|
|
1210
|
+
// workspace.textDocuments and Window.activeTextEditor are updated.
|
|
1211
|
+
//
|
|
1212
|
+
// This means: for newly created tab/editors we don't find the underlying
|
|
1213
|
+
// document yet. So we do nothing an rely on the underlying open document event
|
|
1214
|
+
// to be fired.
|
|
1215
|
+
if (textDocument !== undefined && matches(textDocument)) {
|
|
1216
|
+
this.diagnosticRequestor.pull(textDocument, () => {
|
|
1217
|
+
addToBackgroundIfNeeded(textDocument!);
|
|
1218
|
+
});
|
|
1219
|
+
}
|
|
1220
|
+
}
|
|
1221
|
+
}),
|
|
1222
|
+
);
|
|
1223
|
+
|
|
1224
|
+
// Pull all diagnostics for documents that are already open
|
|
1225
|
+
const pulledTextDocuments: Set<string> = new Set();
|
|
1226
|
+
for (const textDocument of Workspace.textDocuments) {
|
|
1227
|
+
if (matches(textDocument)) {
|
|
1228
|
+
this.diagnosticRequestor.pull(textDocument, () => {
|
|
1229
|
+
addToBackgroundIfNeeded(textDocument);
|
|
1230
|
+
});
|
|
1231
|
+
pulledTextDocuments.add(textDocument.uri.toString());
|
|
1232
|
+
}
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1235
|
+
// Pull all tabs if not already pulled as text document
|
|
1236
|
+
if (diagnosticPullOptions.onTabs === true) {
|
|
1237
|
+
for (const resource of tabs.getTabResources()) {
|
|
1238
|
+
if (!pulledTextDocuments.has(resource.toString()) && matches(resource)) {
|
|
1239
|
+
this.diagnosticRequestor.pull(resource, () => {
|
|
1240
|
+
addToBackgroundIfNeeded(resource);
|
|
1241
|
+
});
|
|
1242
|
+
}
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
|
|
1246
|
+
// We don't need to pull on tab open since we will receive a document open as well later on
|
|
1247
|
+
// and that event allows us to use a document for a match check which will have a set
|
|
1248
|
+
// language id.
|
|
1249
|
+
|
|
1250
|
+
if (diagnosticPullOptions.onChange === true) {
|
|
1251
|
+
const changeFeature = client.getFeature(DidChangeTextDocumentNotification.method);
|
|
1252
|
+
disposables.push(
|
|
1253
|
+
changeFeature.onNotificationSent(async (event) => {
|
|
1254
|
+
const textDocument = event.textDocument;
|
|
1255
|
+
if (
|
|
1256
|
+
(diagnosticPullOptions.filter === undefined ||
|
|
1257
|
+
!diagnosticPullOptions.filter(textDocument, DiagnosticPullMode.onType)) &&
|
|
1258
|
+
this.diagnosticRequestor.knows(PullState.document, textDocument)
|
|
1259
|
+
) {
|
|
1260
|
+
this.diagnosticRequestor.pull(textDocument, () => {
|
|
1261
|
+
this.backgroundScheduler.trigger();
|
|
1262
|
+
});
|
|
1263
|
+
}
|
|
1264
|
+
}),
|
|
1265
|
+
);
|
|
1266
|
+
}
|
|
1267
|
+
|
|
1268
|
+
if (diagnosticPullOptions.onSave === true) {
|
|
1269
|
+
const saveFeature = client.getFeature(DidSaveTextDocumentNotification.method);
|
|
1270
|
+
disposables.push(
|
|
1271
|
+
saveFeature.onNotificationSent((event) => {
|
|
1272
|
+
const textDocument = event.textDocument;
|
|
1273
|
+
if (
|
|
1274
|
+
(diagnosticPullOptions.filter === undefined ||
|
|
1275
|
+
!diagnosticPullOptions.filter(textDocument, DiagnosticPullMode.onSave)) &&
|
|
1276
|
+
this.diagnosticRequestor.knows(PullState.document, textDocument)
|
|
1277
|
+
) {
|
|
1278
|
+
this.diagnosticRequestor.pull(event.textDocument, () => {
|
|
1279
|
+
this.backgroundScheduler.trigger();
|
|
1280
|
+
});
|
|
1281
|
+
}
|
|
1282
|
+
}),
|
|
1283
|
+
);
|
|
1284
|
+
}
|
|
1285
|
+
|
|
1286
|
+
// When the document closes clear things up
|
|
1287
|
+
const closeFeature = client.getFeature(DidCloseTextDocumentNotification.method);
|
|
1288
|
+
disposables.push(
|
|
1289
|
+
closeFeature.onNotificationSent((event) => {
|
|
1290
|
+
this.cleanUpDocument(event.textDocument);
|
|
1291
|
+
}),
|
|
1292
|
+
);
|
|
1293
|
+
|
|
1294
|
+
// Same when a tabs closes.
|
|
1295
|
+
tabs.onClose((closed) => {
|
|
1296
|
+
for (const document of closed) {
|
|
1297
|
+
this.cleanUpDocument(document);
|
|
1298
|
+
}
|
|
1299
|
+
});
|
|
1300
|
+
|
|
1301
|
+
// We received a did change from the server.
|
|
1302
|
+
this.diagnosticRequestor.onDidChangeDiagnosticsEmitter.event(() => {
|
|
1303
|
+
for (const textDocument of Workspace.textDocuments) {
|
|
1304
|
+
if (matches(textDocument)) {
|
|
1305
|
+
this.diagnosticRequestor.pull(textDocument);
|
|
1306
|
+
}
|
|
1307
|
+
}
|
|
1308
|
+
});
|
|
1309
|
+
|
|
1310
|
+
// da348dc5-c30a-4515-9d98-31ff3be38d14 is the test UUID to test the middle ware. So don't auto trigger pulls.
|
|
1311
|
+
if (
|
|
1312
|
+
options.workspaceDiagnostics === true &&
|
|
1313
|
+
options.identifier !== 'da348dc5-c30a-4515-9d98-31ff3be38d14'
|
|
1314
|
+
) {
|
|
1315
|
+
this.diagnosticRequestor.pullWorkspace();
|
|
1316
|
+
}
|
|
1317
|
+
|
|
1318
|
+
this.disposable = Disposable.from(
|
|
1319
|
+
...disposables,
|
|
1320
|
+
this.backgroundScheduler,
|
|
1321
|
+
this.diagnosticRequestor,
|
|
1322
|
+
);
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1325
|
+
public get onDidChangeDiagnosticsEmitter(): EventEmitter<void> {
|
|
1326
|
+
return this.diagnosticRequestor.onDidChangeDiagnosticsEmitter;
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1329
|
+
public get diagnostics(): vsdiag.DiagnosticProvider {
|
|
1330
|
+
return this.diagnosticRequestor.provider;
|
|
1331
|
+
}
|
|
1332
|
+
|
|
1333
|
+
private cleanUpDocument(document: TextDocument | Uri): void {
|
|
1334
|
+
if (this.diagnosticRequestor.knows(PullState.document, document)) {
|
|
1335
|
+
this.diagnosticRequestor.forgetDocument(document);
|
|
1336
|
+
this.backgroundScheduler.remove(document);
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
|
|
1341
|
+
export class DiagnosticFeature extends TextDocumentLanguageFeature<
|
|
1342
|
+
DiagnosticOptions,
|
|
1343
|
+
DiagnosticRegistrationOptions,
|
|
1344
|
+
DiagnosticProviderShape,
|
|
1345
|
+
DiagnosticProviderMiddleware,
|
|
1346
|
+
$DiagnosticPullOptions
|
|
1347
|
+
> {
|
|
1348
|
+
private tabs: Tabs | undefined;
|
|
1349
|
+
|
|
1350
|
+
constructor(
|
|
1351
|
+
client: FeatureClient<DiagnosticProviderMiddleware, $DiagnosticPullOptions>,
|
|
1352
|
+
) {
|
|
1353
|
+
super(client, DocumentDiagnosticRequest.type);
|
|
1354
|
+
}
|
|
1355
|
+
|
|
1356
|
+
public fillClientCapabilities(capabilities: ClientCapabilities): void {
|
|
1357
|
+
const capability = ensure(ensure(capabilities, 'textDocument')!, 'diagnostic')!;
|
|
1358
|
+
capability.dynamicRegistration = true;
|
|
1359
|
+
// We first need to decide how a UI will look with related documents.
|
|
1360
|
+
// An easy implementation would be to only show related diagnostics for
|
|
1361
|
+
// the active editor.
|
|
1362
|
+
capability.relatedDocumentSupport = false;
|
|
1363
|
+
|
|
1364
|
+
ensure(ensure(capabilities, 'workspace')!, 'diagnostics')!.refreshSupport = true;
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
public initialize(
|
|
1368
|
+
capabilities: ServerCapabilities,
|
|
1369
|
+
documentSelector: DocumentSelector,
|
|
1370
|
+
): void {
|
|
1371
|
+
const client = this._client;
|
|
1372
|
+
client.onRequest(DiagnosticRefreshRequest.type, async () => {
|
|
1373
|
+
for (const provider of this.getAllProviders()) {
|
|
1374
|
+
provider.onDidChangeDiagnosticsEmitter.fire();
|
|
1375
|
+
}
|
|
1376
|
+
});
|
|
1377
|
+
const [id, options] = this.getRegistration(
|
|
1378
|
+
documentSelector,
|
|
1379
|
+
capabilities.diagnosticProvider,
|
|
1380
|
+
);
|
|
1381
|
+
if (!id || !options) {
|
|
1382
|
+
return;
|
|
1383
|
+
}
|
|
1384
|
+
this.register({ id: id, registerOptions: options });
|
|
1385
|
+
}
|
|
1386
|
+
|
|
1387
|
+
public override clear(): void {
|
|
1388
|
+
if (this.tabs !== undefined) {
|
|
1389
|
+
this.tabs.dispose();
|
|
1390
|
+
this.tabs = undefined;
|
|
1391
|
+
}
|
|
1392
|
+
super.clear();
|
|
1393
|
+
}
|
|
1394
|
+
|
|
1395
|
+
protected registerLanguageProvider(
|
|
1396
|
+
options: DiagnosticRegistrationOptions,
|
|
1397
|
+
): [Disposable, DiagnosticProviderShape] {
|
|
1398
|
+
if (this.tabs === undefined) {
|
|
1399
|
+
this.tabs = new Tabs();
|
|
1400
|
+
}
|
|
1401
|
+
const provider = new DiagnosticFeatureProviderImpl(
|
|
1402
|
+
this._client,
|
|
1403
|
+
this.tabs,
|
|
1404
|
+
options,
|
|
1405
|
+
);
|
|
1406
|
+
return [provider.disposable, provider];
|
|
1407
|
+
}
|
|
1408
|
+
}
|