@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.
Files changed (298) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +0 -0
  3. package/es/common/api.d.ts +38 -0
  4. package/es/common/api.d.ts.map +1 -0
  5. package/es/common/api.js +9 -0
  6. package/es/common/callHierarchy.d.ts +39 -0
  7. package/es/common/callHierarchy.d.ts.map +1 -0
  8. package/es/common/callHierarchy.js +139 -0
  9. package/es/common/client.d.ts +482 -0
  10. package/es/common/client.d.ts.map +1 -0
  11. package/es/common/client.js +2731 -0
  12. package/es/common/codeAction.d.ts +22 -0
  13. package/es/common/codeAction.d.ts.map +1 -0
  14. package/es/common/codeAction.js +149 -0
  15. package/es/common/codeConverter.d.ts +81 -0
  16. package/es/common/codeConverter.d.ts.map +1 -0
  17. package/es/common/codeConverter.js +1040 -0
  18. package/es/common/codeLens.d.ts +26 -0
  19. package/es/common/codeLens.d.ts.map +1 -0
  20. package/es/common/codeLens.js +125 -0
  21. package/es/common/colorProvider.d.ts +27 -0
  22. package/es/common/colorProvider.d.ts.map +1 -0
  23. package/es/common/colorProvider.js +104 -0
  24. package/es/common/completion.d.ts +22 -0
  25. package/es/common/completion.d.ts.map +1 -0
  26. package/es/common/completion.js +130 -0
  27. package/es/common/configuration.d.ts +71 -0
  28. package/es/common/configuration.d.ts.map +1 -0
  29. package/es/common/configuration.js +292 -0
  30. package/es/common/declaration.d.ts +18 -0
  31. package/es/common/declaration.d.ts.map +1 -0
  32. package/es/common/declaration.js +88 -0
  33. package/es/common/definition.d.ts +18 -0
  34. package/es/common/definition.d.ts.map +1 -0
  35. package/es/common/definition.js +80 -0
  36. package/es/common/diagnostic.d.ts +125 -0
  37. package/es/common/diagnostic.d.ts.map +1 -0
  38. package/es/common/diagnostic.js +1442 -0
  39. package/es/common/documentHighlight.d.ts +17 -0
  40. package/es/common/documentHighlight.d.ts.map +1 -0
  41. package/es/common/documentHighlight.js +73 -0
  42. package/es/common/documentLink.d.ts +21 -0
  43. package/es/common/documentLink.d.ts.map +1 -0
  44. package/es/common/documentLink.js +90 -0
  45. package/es/common/documentSymbol.d.ts +20 -0
  46. package/es/common/documentSymbol.d.ts.map +1 -0
  47. package/es/common/documentSymbol.js +134 -0
  48. package/es/common/executeCommand.d.ts +22 -0
  49. package/es/common/executeCommand.d.ts.map +1 -0
  50. package/es/common/executeCommand.js +117 -0
  51. package/es/common/features.d.ts +421 -0
  52. package/es/common/features.d.ts.map +1 -0
  53. package/es/common/features.js +576 -0
  54. package/es/common/fileOperations.d.ts +118 -0
  55. package/es/common/fileOperations.d.ts.map +1 -0
  56. package/es/common/fileOperations.js +705 -0
  57. package/es/common/fileSystemWatcher.d.ts +19 -0
  58. package/es/common/fileSystemWatcher.d.ts.map +1 -0
  59. package/es/common/fileSystemWatcher.js +173 -0
  60. package/es/common/foldingRange.d.ts +22 -0
  61. package/es/common/foldingRange.d.ts.map +1 -0
  62. package/es/common/foldingRange.js +127 -0
  63. package/es/common/formatting.d.ts +41 -0
  64. package/es/common/formatting.d.ts.map +1 -0
  65. package/es/common/formatting.js +233 -0
  66. package/es/common/hover.d.ts +18 -0
  67. package/es/common/hover.d.ts.map +1 -0
  68. package/es/common/hover.js +80 -0
  69. package/es/common/implementation.d.ts +18 -0
  70. package/es/common/implementation.d.ts.map +1 -0
  71. package/es/common/implementation.js +88 -0
  72. package/es/common/inlayHint.d.ts +23 -0
  73. package/es/common/inlayHint.d.ts.map +1 -0
  74. package/es/common/inlayHint.js +187 -0
  75. package/es/common/inlineCompletion.d.ts +20 -0
  76. package/es/common/inlineCompletion.d.ts.map +1 -0
  77. package/es/common/inlineCompletion.js +74 -0
  78. package/es/common/inlineValue.d.ts +21 -0
  79. package/es/common/inlineValue.d.ts.map +1 -0
  80. package/es/common/inlineValue.js +124 -0
  81. package/es/common/linkedEditingRange.d.ts +23 -0
  82. package/es/common/linkedEditingRange.d.ts.map +1 -0
  83. package/es/common/linkedEditingRange.js +94 -0
  84. package/es/common/notebook.d.ts +97 -0
  85. package/es/common/notebook.d.ts.map +1 -0
  86. package/es/common/notebook.js +1444 -0
  87. package/es/common/progress.d.ts +12 -0
  88. package/es/common/progress.d.ts.map +1 -0
  89. package/es/common/progress.js +75 -0
  90. package/es/common/progressPart.d.ts +25 -0
  91. package/es/common/progressPart.d.ts.map +1 -0
  92. package/es/common/progressPart.js +147 -0
  93. package/es/common/protocolCallHierarchyItem.d.ts +9 -0
  94. package/es/common/protocolCallHierarchyItem.d.ts.map +1 -0
  95. package/es/common/protocolCallHierarchyItem.js +34 -0
  96. package/es/common/protocolCodeAction.d.ts +7 -0
  97. package/es/common/protocolCodeAction.d.ts.map +1 -0
  98. package/es/common/protocolCodeAction.js +32 -0
  99. package/es/common/protocolCodeLens.d.ts +7 -0
  100. package/es/common/protocolCodeLens.d.ts.map +1 -0
  101. package/es/common/protocolCodeLens.js +29 -0
  102. package/es/common/protocolCompletionItem.d.ts +13 -0
  103. package/es/common/protocolCompletionItem.d.ts.map +1 -0
  104. package/es/common/protocolCompletionItem.js +29 -0
  105. package/es/common/protocolConverter.d.ts +174 -0
  106. package/es/common/protocolConverter.d.ts.map +1 -0
  107. package/es/common/protocolConverter.js +1982 -0
  108. package/es/common/protocolDiagnostic.d.ts +20 -0
  109. package/es/common/protocolDiagnostic.d.ts.map +1 -0
  110. package/es/common/protocolDiagnostic.js +46 -0
  111. package/es/common/protocolDocumentLink.d.ts +8 -0
  112. package/es/common/protocolDocumentLink.d.ts.map +1 -0
  113. package/es/common/protocolDocumentLink.js +29 -0
  114. package/es/common/protocolInlayHint.d.ts +8 -0
  115. package/es/common/protocolInlayHint.d.ts.map +1 -0
  116. package/es/common/protocolInlayHint.js +29 -0
  117. package/es/common/protocolTypeHierarchyItem.d.ts +9 -0
  118. package/es/common/protocolTypeHierarchyItem.d.ts.map +1 -0
  119. package/es/common/protocolTypeHierarchyItem.js +34 -0
  120. package/es/common/protocolWorkspaceSymbol.d.ts +9 -0
  121. package/es/common/protocolWorkspaceSymbol.d.ts.map +1 -0
  122. package/es/common/protocolWorkspaceSymbol.js +36 -0
  123. package/es/common/reference.d.ts +22 -0
  124. package/es/common/reference.d.ts.map +1 -0
  125. package/es/common/reference.js +78 -0
  126. package/es/common/rename.d.ts +29 -0
  127. package/es/common/rename.d.ts.map +1 -0
  128. package/es/common/rename.js +132 -0
  129. package/es/common/selectionRange.d.ts +18 -0
  130. package/es/common/selectionRange.d.ts.map +1 -0
  131. package/es/common/selectionRange.js +108 -0
  132. package/es/common/semanticTokens.d.ts +36 -0
  133. package/es/common/semanticTokens.d.ts.map +1 -0
  134. package/es/common/semanticTokens.js +226 -0
  135. package/es/common/signatureHelp.d.ts +18 -0
  136. package/es/common/signatureHelp.d.ts.map +1 -0
  137. package/es/common/signatureHelp.js +103 -0
  138. package/es/common/textSynchronization.d.ts +104 -0
  139. package/es/common/textSynchronization.d.ts.map +1 -0
  140. package/es/common/textSynchronization.js +771 -0
  141. package/es/common/typeDefinition.d.ts +18 -0
  142. package/es/common/typeDefinition.d.ts.map +1 -0
  143. package/es/common/typeDefinition.js +89 -0
  144. package/es/common/typeHierarchy.d.ts +33 -0
  145. package/es/common/typeHierarchy.d.ts.map +1 -0
  146. package/es/common/typeHierarchy.js +138 -0
  147. package/es/common/utils/async.d.ts +42 -0
  148. package/es/common/utils/async.d.ts.map +1 -0
  149. package/es/common/utils/async.js +441 -0
  150. package/es/common/utils/is.d.ts +13 -0
  151. package/es/common/utils/is.d.ts.map +1 -0
  152. package/es/common/utils/is.js +52 -0
  153. package/es/common/utils/uuid.d.ts +23 -0
  154. package/es/common/utils/uuid.d.ts.map +1 -0
  155. package/es/common/utils/uuid.js +85 -0
  156. package/es/common/vscodeAdaptor/convertor.d.ts +7 -0
  157. package/es/common/vscodeAdaptor/convertor.d.ts.map +1 -0
  158. package/es/common/vscodeAdaptor/convertor.js +66 -0
  159. package/es/common/vscodeAdaptor/diagnosticCollection.d.ts +33 -0
  160. package/es/common/vscodeAdaptor/diagnosticCollection.d.ts.map +1 -0
  161. package/es/common/vscodeAdaptor/diagnosticCollection.js +310 -0
  162. package/es/common/vscodeAdaptor/extHostTypes.d.ts +1496 -0
  163. package/es/common/vscodeAdaptor/extHostTypes.d.ts.map +1 -0
  164. package/es/common/vscodeAdaptor/extHostTypes.js +3825 -0
  165. package/es/common/vscodeAdaptor/fileWatcher.d.ts +19 -0
  166. package/es/common/vscodeAdaptor/fileWatcher.d.ts.map +1 -0
  167. package/es/common/vscodeAdaptor/fileWatcher.js +45 -0
  168. package/es/common/vscodeAdaptor/hostTypeUtil.d.ts +192 -0
  169. package/es/common/vscodeAdaptor/hostTypeUtil.d.ts.map +1 -0
  170. package/es/common/vscodeAdaptor/hostTypeUtil.js +566 -0
  171. package/es/common/vscodeAdaptor/libro-fs.d.ts +21 -0
  172. package/es/common/vscodeAdaptor/libro-fs.d.ts.map +1 -0
  173. package/es/common/vscodeAdaptor/libro-fs.js +64 -0
  174. package/es/common/vscodeAdaptor/libroWindow.d.ts +21 -0
  175. package/es/common/vscodeAdaptor/libroWindow.d.ts.map +1 -0
  176. package/es/common/vscodeAdaptor/libroWindow.js +75 -0
  177. package/es/common/vscodeAdaptor/libroWorkspace.d.ts +33 -0
  178. package/es/common/vscodeAdaptor/libroWorkspace.d.ts.map +1 -0
  179. package/es/common/vscodeAdaptor/libroWorkspace.js +250 -0
  180. package/es/common/vscodeAdaptor/lspEnv.d.ts +8 -0
  181. package/es/common/vscodeAdaptor/lspEnv.d.ts.map +1 -0
  182. package/es/common/vscodeAdaptor/lspEnv.js +31 -0
  183. package/es/common/vscodeAdaptor/monaco-converter.d.ts +229 -0
  184. package/es/common/vscodeAdaptor/monaco-converter.d.ts.map +1 -0
  185. package/es/common/vscodeAdaptor/monaco-converter.js +1613 -0
  186. package/es/common/vscodeAdaptor/monacoLanguages.d.ts +48 -0
  187. package/es/common/vscodeAdaptor/monacoLanguages.d.ts.map +1 -0
  188. package/es/common/vscodeAdaptor/monacoLanguages.js +484 -0
  189. package/es/common/vscodeAdaptor/services.d.ts +85 -0
  190. package/es/common/vscodeAdaptor/services.d.ts.map +1 -0
  191. package/es/common/vscodeAdaptor/services.js +3 -0
  192. package/es/common/vscodeAdaptor/typings.d.ts +10 -0
  193. package/es/common/vscodeAdaptor/util.d.ts +3 -0
  194. package/es/common/vscodeAdaptor/util.d.ts.map +1 -0
  195. package/es/common/vscodeAdaptor/util.js +6 -0
  196. package/es/common/vscodeAdaptor/vscodeAdaptor.d.ts +77 -0
  197. package/es/common/vscodeAdaptor/vscodeAdaptor.d.ts.map +1 -0
  198. package/es/common/vscodeAdaptor/vscodeAdaptor.js +124 -0
  199. package/es/common/workspaceFolder.d.ts +32 -0
  200. package/es/common/workspaceFolder.d.ts.map +1 -0
  201. package/es/common/workspaceFolder.js +204 -0
  202. package/es/common/workspaceSymbol.d.ts +21 -0
  203. package/es/common/workspaceSymbol.d.ts.map +1 -0
  204. package/es/common/workspaceSymbol.js +101 -0
  205. package/es/constants.d.ts +2 -0
  206. package/es/constants.d.ts.map +1 -0
  207. package/es/constants.js +1 -0
  208. package/es/index.d.ts +6 -0
  209. package/es/index.d.ts.map +1 -0
  210. package/es/index.js +5 -0
  211. package/es/libro-language-client-contribution.d.ts +10 -0
  212. package/es/libro-language-client-contribution.d.ts.map +1 -0
  213. package/es/libro-language-client-contribution.js +143 -0
  214. package/es/libro-language-client-manager.d.ts +34 -0
  215. package/es/libro-language-client-manager.d.ts.map +1 -0
  216. package/es/libro-language-client-manager.js +277 -0
  217. package/es/libro-language-client.d.ts +27 -0
  218. package/es/libro-language-client.d.ts.map +1 -0
  219. package/es/libro-language-client.js +141 -0
  220. package/es/module.d.ts +3 -0
  221. package/es/module.d.ts.map +1 -0
  222. package/es/module.js +13 -0
  223. package/package.json +69 -0
  224. package/src/common/api.ts +155 -0
  225. package/src/common/callHierarchy.ts +269 -0
  226. package/src/common/client.ts +3192 -0
  227. package/src/common/codeAction.ts +237 -0
  228. package/src/common/codeConverter.ts +1409 -0
  229. package/src/common/codeLens.ts +188 -0
  230. package/src/common/colorProvider.ts +192 -0
  231. package/src/common/completion.ts +281 -0
  232. package/src/common/configuration.ts +338 -0
  233. package/src/common/declaration.ts +140 -0
  234. package/src/common/definition.ts +138 -0
  235. package/src/common/diagnostic.ts +1408 -0
  236. package/src/common/documentHighlight.ts +140 -0
  237. package/src/common/documentLink.ts +180 -0
  238. package/src/common/documentSymbol.ts +186 -0
  239. package/src/common/executeCommand.ts +129 -0
  240. package/src/common/features.ts +1157 -0
  241. package/src/common/fileOperations.ts +635 -0
  242. package/src/common/fileSystemWatcher.ts +184 -0
  243. package/src/common/foldingRange.ts +160 -0
  244. package/src/common/formatting.ts +465 -0
  245. package/src/common/hover.ts +133 -0
  246. package/src/common/implementation.ts +142 -0
  247. package/src/common/inlayHint.ts +201 -0
  248. package/src/common/inlineCompletion.ts +160 -0
  249. package/src/common/inlineValue.ts +158 -0
  250. package/src/common/linkedEditingRange.ts +141 -0
  251. package/src/common/notebook.ts +1443 -0
  252. package/src/common/progress.ts +61 -0
  253. package/src/common/progressPart.ts +151 -0
  254. package/src/common/protocolCallHierarchyItem.ts +29 -0
  255. package/src/common/protocolCodeAction.ts +17 -0
  256. package/src/common/protocolCodeLens.ts +15 -0
  257. package/src/common/protocolCompletionItem.ts +22 -0
  258. package/src/common/protocolConverter.ts +2627 -0
  259. package/src/common/protocolDiagnostic.ts +47 -0
  260. package/src/common/protocolDocumentLink.ts +17 -0
  261. package/src/common/protocolInlayHint.ts +21 -0
  262. package/src/common/protocolTypeHierarchyItem.ts +29 -0
  263. package/src/common/protocolWorkspaceSymbol.ts +39 -0
  264. package/src/common/reference.ts +144 -0
  265. package/src/common/rename.ts +230 -0
  266. package/src/common/selectionRange.ts +136 -0
  267. package/src/common/semanticTokens.ts +383 -0
  268. package/src/common/signatureHelp.ts +170 -0
  269. package/src/common/textSynchronization.ts +819 -0
  270. package/src/common/typeDefinition.ts +146 -0
  271. package/src/common/typeHierarchy.ts +248 -0
  272. package/src/common/utils/async.ts +354 -0
  273. package/src/common/utils/is.ts +63 -0
  274. package/src/common/utils/uuid.ts +136 -0
  275. package/src/common/vscodeAdaptor/convertor.ts +73 -0
  276. package/src/common/vscodeAdaptor/diagnosticCollection.ts +238 -0
  277. package/src/common/vscodeAdaptor/extHostTypes.ts +4498 -0
  278. package/src/common/vscodeAdaptor/fileWatcher.ts +36 -0
  279. package/src/common/vscodeAdaptor/hostTypeUtil.ts +539 -0
  280. package/src/common/vscodeAdaptor/libro-fs.ts +51 -0
  281. package/src/common/vscodeAdaptor/libroWindow.ts +85 -0
  282. package/src/common/vscodeAdaptor/libroWorkspace.ts +261 -0
  283. package/src/common/vscodeAdaptor/lspEnv.ts +16 -0
  284. package/src/common/vscodeAdaptor/monaco-converter.ts +1800 -0
  285. package/src/common/vscodeAdaptor/monacoLanguages.ts +511 -0
  286. package/src/common/vscodeAdaptor/services.ts +278 -0
  287. package/src/common/vscodeAdaptor/typings.d.ts +10 -0
  288. package/src/common/vscodeAdaptor/util.ts +7 -0
  289. package/src/common/vscodeAdaptor/vscodeAdaptor.ts +122 -0
  290. package/src/common/workspaceFolder.ts +236 -0
  291. package/src/common/workspaceSymbol.ts +166 -0
  292. package/src/constants.ts +1 -0
  293. package/src/index.spec.ts +7 -0
  294. package/src/index.ts +5 -0
  295. package/src/libro-language-client-contribution.ts +49 -0
  296. package/src/libro-language-client-manager.ts +131 -0
  297. package/src/libro-language-client.ts +100 -0
  298. 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
+ }