24
24
* This can be used to ship a processor with your package, to be used if no
25
25
* processor is found locally.
26
26
* If this isn’t passed, a warning is shown if `processorName` can’t be found.
27
+ * @property {string } configurationSection
28
+ * This option will be used to give the client a hint of which configuration
29
+ * section to use.
30
+ * For example VSCode extensions use this to pick only settings that use this
31
+ * as a prefix in order to prevent conflicts and reduce the amount of data
32
+ * sent to the language server.
27
33
*
28
34
* @typedef {EngineFields & LanguageServerFields } Options
35
+ *
36
+ * @typedef UnifiedLanguageServerSettings
37
+ * @property {boolean } [requireConfig=false]
38
+ * If true, files will only be checked if a configuration file is present.
29
39
*/
30
40
31
41
import path from 'node:path'
@@ -42,6 +52,7 @@ import {
42
52
CodeActionKind ,
43
53
Diagnostic ,
44
54
DiagnosticSeverity ,
55
+ DidChangeConfigurationNotification ,
45
56
Position ,
46
57
ProposedFeatures ,
47
58
Range ,
@@ -122,6 +133,7 @@ function lspDocumentToVfile(document, cwd) {
122
133
* Configuration for `unified-engine` and the language server.
123
134
*/
124
135
export function createUnifiedLanguageServer ( {
136
+ configurationSection,
125
137
ignoreName,
126
138
packageField,
127
139
pluginPrefix,
@@ -135,14 +147,49 @@ export function createUnifiedLanguageServer({
135
147
const documents = new TextDocuments ( TextDocument )
136
148
/** @type {Set<string> } */
137
149
const workspaces = new Set ( )
150
+ /** @type {UnifiedLanguageServerSettings } */
151
+ const globalSettings = { requireConfig : false }
152
+ /** @type {Map<string, Promise<UnifiedLanguageServerSettings>> } */
153
+ const documentSettings = new Map ( )
138
154
let hasWorkspaceFolderCapability = false
155
+ let hasConfigurationCapability = false
156
+
157
+ /**
158
+ * @param {string } scopeUri
159
+ * @returns {Promise<UnifiedLanguageServerSettings> }
160
+ */
161
+ async function getDocumentSettings ( scopeUri ) {
162
+ if ( ! hasConfigurationCapability ) {
163
+ return globalSettings
164
+ }
165
+
166
+ let result = documentSettings . get ( scopeUri )
167
+ if ( ! result ) {
168
+ result = connection . workspace
169
+ . getConfiguration ( { scopeUri, section : configurationSection } )
170
+ . then (
171
+ /** @param {Record<string, unknown> } raw */
172
+ ( raw ) => ( { requireConfig : Boolean ( raw . requireConfig ) } )
173
+ )
174
+ documentSettings . set ( scopeUri , result )
175
+ }
176
+
177
+ return result
178
+ }
139
179
140
180
/**
141
181
* @param {string } cwd
142
182
* @param {VFile[] } files
143
183
* @param {boolean } alwaysStringify
184
+ * @param {boolean } ignoreUnconfigured
185
+ * @returns {Promise<VFile[]> }
144
186
*/
145
- async function processWorkspace ( cwd , files , alwaysStringify ) {
187
+ async function processWorkspace (
188
+ cwd ,
189
+ files ,
190
+ alwaysStringify ,
191
+ ignoreUnconfigured
192
+ ) {
146
193
/** @type {EngineOptions['processor'] } */
147
194
let processor
148
195
@@ -190,6 +237,7 @@ export function createUnifiedLanguageServer({
190
237
cwd,
191
238
files,
192
239
ignoreName,
240
+ ignoreUnconfigured,
193
241
packageField,
194
242
pluginPrefix,
195
243
plugins,
@@ -233,6 +281,8 @@ export function createUnifiedLanguageServer({
233
281
. sort ( ( a , b ) => b . length - a . length )
234
282
/** @type {Map<string, Array<VFile>> } */
235
283
const workspacePathToFiles = new Map ( )
284
+ /** @type {Map<string, Array<VFile>> } */
285
+ const workspacePathToFilesRequireConfig = new Map ( )
236
286
237
287
await Promise . all (
238
288
textDocuments . map ( async ( textDocument ) => {
@@ -269,18 +319,28 @@ export function createUnifiedLanguageServer({
269
319
270
320
if ( ! cwd ) return
271
321
322
+ const configuration = await getDocumentSettings ( textDocument . uri )
323
+
272
324
const file = lspDocumentToVfile ( textDocument , cwd )
273
325
274
- const files = workspacePathToFiles . get ( cwd ) || [ ]
275
- workspacePathToFiles . set ( cwd , [ ...files , file ] )
326
+ const filesMap = configuration . requireConfig
327
+ ? workspacePathToFilesRequireConfig
328
+ : workspacePathToFiles
329
+ const files = filesMap . get ( cwd ) || [ ]
330
+ files . push ( file )
331
+ filesMap . set ( cwd , files )
276
332
} )
277
333
)
278
334
279
335
/** @type {Array<Promise<Array<VFile>>> } */
280
336
const promises = [ ]
281
337
282
338
for ( const [ cwd , files ] of workspacePathToFiles ) {
283
- promises . push ( processWorkspace ( cwd , files , alwaysStringify ) )
339
+ promises . push ( processWorkspace ( cwd , files , alwaysStringify , false ) )
340
+ }
341
+
342
+ for ( const [ cwd , files ] of workspacePathToFilesRequireConfig ) {
343
+ promises . push ( processWorkspace ( cwd , files , alwaysStringify , true ) )
284
344
}
285
345
286
346
const listsOfFiles = await Promise . all ( promises )
@@ -324,6 +384,9 @@ export function createUnifiedLanguageServer({
324
384
workspaces . add ( event . rootUri )
325
385
}
326
386
387
+ hasConfigurationCapability = Boolean (
388
+ event . capabilities . workspace && event . capabilities . workspace . configuration
389
+ )
327
390
hasWorkspaceFolderCapability = Boolean (
328
391
event . capabilities . workspace &&
329
392
event . capabilities . workspace . workspaceFolders
@@ -345,6 +408,10 @@ export function createUnifiedLanguageServer({
345
408
} )
346
409
347
410
connection . onInitialized ( ( ) => {
411
+ if ( hasConfigurationCapability ) {
412
+ connection . client . register ( DidChangeConfigurationNotification . type )
413
+ }
414
+
348
415
if ( hasWorkspaceFolderCapability ) {
349
416
connection . workspace . onDidChangeWorkspaceFolders ( ( event ) => {
350
417
for ( const workspace of event . removed ) {
@@ -399,13 +466,30 @@ export function createUnifiedLanguageServer({
399
466
version,
400
467
diagnostics : [ ]
401
468
} )
469
+ documentSettings . delete ( uri )
402
470
} )
403
471
404
472
// Check everything again if the file system watched by the client changes.
405
473
connection . onDidChangeWatchedFiles ( ( ) => {
406
474
checkDocuments ( ...documents . all ( ) )
407
475
} )
408
476
477
+ connection . onDidChangeConfiguration ( ( change ) => {
478
+ if ( hasConfigurationCapability ) {
479
+ // Reset all cached document settings
480
+ documentSettings . clear ( )
481
+ } else {
482
+ globalSettings . requireConfig = Boolean (
483
+ /** @type {Omit<typeof change, 'settings'> & { settings: Record<string, unknown> } } */ (
484
+ change
485
+ ) . settings . requireConfig
486
+ )
487
+ }
488
+
489
+ // Revalidate all open text documents
490
+ checkDocuments ( ...documents . all ( ) )
491
+ } )
492
+
409
493
connection . onCodeAction ( ( event ) => {
410
494
/** @type {CodeAction[] } */
411
495
const codeActions = [ ]
0 commit comments