css-variable-lsp 1.0.6 → 1.0.8-beta.1

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/out/server.js CHANGED
@@ -3,13 +3,119 @@
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
4
  const node_1 = require("vscode-languageserver/node");
5
5
  const vscode_languageserver_textdocument_1 = require("vscode-languageserver-textdocument");
6
+ const path = require("path");
7
+ const vscode_uri_1 = require("vscode-uri");
6
8
  const cssVariableManager_1 = require("./cssVariableManager");
7
9
  const specificity_1 = require("./specificity");
8
10
  const colorService_1 = require("./colorService");
9
11
  // Parse command-line arguments
10
12
  const args = process.argv.slice(2);
11
- const ENABLE_COLOR_PROVIDER = !args.includes('--no-color-preview');
12
- const COLOR_ONLY_ON_VARIABLES = args.includes('--color-only-variables') || process.env.CSS_LSP_COLOR_ONLY_VARIABLES === '1';
13
+ function getArgValue(name) {
14
+ const flag = `--${name}`;
15
+ const directIndex = args.indexOf(flag);
16
+ if (directIndex !== -1) {
17
+ const candidate = args[directIndex + 1];
18
+ if (candidate && !candidate.startsWith("-")) {
19
+ return candidate;
20
+ }
21
+ return null;
22
+ }
23
+ const prefix = `${flag}=`;
24
+ const withEquals = args.find((arg) => arg.startsWith(prefix));
25
+ if (withEquals) {
26
+ return withEquals.slice(prefix.length);
27
+ }
28
+ return null;
29
+ }
30
+ function parseOptionalInt(value) {
31
+ if (!value) {
32
+ return null;
33
+ }
34
+ const parsed = Number.parseInt(value, 10);
35
+ if (Number.isNaN(parsed)) {
36
+ return null;
37
+ }
38
+ return parsed;
39
+ }
40
+ function normalizePathDisplayMode(value) {
41
+ if (!value) {
42
+ return null;
43
+ }
44
+ switch (value.toLowerCase()) {
45
+ case "relative":
46
+ return "relative";
47
+ case "absolute":
48
+ return "absolute";
49
+ case "abbreviated":
50
+ case "abbr":
51
+ case "fish":
52
+ return "abbreviated";
53
+ default:
54
+ return null;
55
+ }
56
+ }
57
+ function parsePathDisplay(value) {
58
+ if (!value) {
59
+ return { mode: null, abbrevLength: null };
60
+ }
61
+ const [modePart, lengthPart] = value.split(":", 2);
62
+ const mode = normalizePathDisplayMode(modePart);
63
+ const abbrevLength = parseOptionalInt(lengthPart);
64
+ return { mode, abbrevLength };
65
+ }
66
+ function splitLookupList(value) {
67
+ return value
68
+ .split(",")
69
+ .map((entry) => entry.trim())
70
+ .filter(Boolean);
71
+ }
72
+ function resolveLookupFiles(argv) {
73
+ const cliFiles = [];
74
+ for (let i = 0; i < argv.length; i++) {
75
+ const arg = argv[i];
76
+ if (arg === "--lookup-files" && argv[i + 1]) {
77
+ cliFiles.push(...splitLookupList(argv[i + 1]));
78
+ i++;
79
+ continue;
80
+ }
81
+ if (arg.startsWith("--lookup-files=")) {
82
+ cliFiles.push(...splitLookupList(arg.slice("--lookup-files=".length)));
83
+ continue;
84
+ }
85
+ if (arg === "--lookup-file" && argv[i + 1]) {
86
+ cliFiles.push(argv[i + 1]);
87
+ i++;
88
+ continue;
89
+ }
90
+ if (arg.startsWith("--lookup-file=")) {
91
+ cliFiles.push(arg.slice("--lookup-file=".length));
92
+ }
93
+ }
94
+ if (cliFiles.length > 0) {
95
+ return cliFiles;
96
+ }
97
+ const envValue = process.env.CSS_LSP_LOOKUP_FILES;
98
+ if (envValue) {
99
+ const envFiles = splitLookupList(envValue);
100
+ if (envFiles.length > 0) {
101
+ return envFiles;
102
+ }
103
+ }
104
+ return undefined;
105
+ }
106
+ const ENABLE_COLOR_PROVIDER = !args.includes("--no-color-preview");
107
+ const COLOR_ONLY_ON_VARIABLES = args.includes("--color-only-variables") ||
108
+ process.env.CSS_LSP_COLOR_ONLY_VARIABLES === "1";
109
+ const LOOKUP_FILES = resolveLookupFiles(args);
110
+ const pathDisplayArg = getArgValue("path-display");
111
+ const pathDisplayEnv = process.env.CSS_LSP_PATH_DISPLAY;
112
+ const parsedPathDisplay = parsePathDisplay(pathDisplayArg ?? pathDisplayEnv);
113
+ const PATH_DISPLAY_MODE = parsedPathDisplay.mode ?? "relative";
114
+ const pathDisplayLengthArg = getArgValue("path-display-length");
115
+ const pathDisplayLengthEnv = process.env.CSS_LSP_PATH_DISPLAY_LENGTH;
116
+ const abbrevLengthRaw = parseOptionalInt(pathDisplayLengthArg ?? pathDisplayLengthEnv) ??
117
+ parsedPathDisplay.abbrevLength;
118
+ const PATH_DISPLAY_ABBREV_LENGTH = Math.max(0, abbrevLengthRaw ?? 1);
13
119
  // Create a connection for the server, using Node's IPC as a transport.
14
120
  // Also include all preview / proposed LSP features.
15
121
  const connection = (0, node_1.createConnection)(node_1.ProposedFeatures.all);
@@ -20,14 +126,59 @@ function logDebug(label, payload) {
20
126
  connection.console.log(message);
21
127
  }
22
128
  }
129
+ function toNormalizedFsPath(uri) {
130
+ try {
131
+ const fsPath = vscode_uri_1.URI.parse(uri).fsPath;
132
+ return fsPath ? path.normalize(fsPath) : null;
133
+ }
134
+ catch {
135
+ return null;
136
+ }
137
+ }
138
+ function updateWorkspaceFolderPaths(folders) {
139
+ if (!folders) {
140
+ workspaceFolderPaths = [];
141
+ return;
142
+ }
143
+ const paths = folders
144
+ .map((folder) => toNormalizedFsPath(folder.uri))
145
+ .filter((folderPath) => Boolean(folderPath));
146
+ workspaceFolderPaths = paths.toSorted((a, b) => b.length - a.length);
147
+ }
148
+ function formatUriForDisplay(uri) {
149
+ const fsPath = toNormalizedFsPath(uri);
150
+ if (!fsPath) {
151
+ return uri;
152
+ }
153
+ const roots = workspaceFolderPaths.length
154
+ ? workspaceFolderPaths
155
+ : rootFolderPath
156
+ ? [rootFolderPath]
157
+ : [];
158
+ let bestRelative = null;
159
+ for (const root of roots) {
160
+ const relativePath = path.relative(root, fsPath);
161
+ if (!relativePath ||
162
+ relativePath.startsWith("..") ||
163
+ path.isAbsolute(relativePath)) {
164
+ continue;
165
+ }
166
+ if (!bestRelative || relativePath.length < bestRelative.length) {
167
+ bestRelative = relativePath;
168
+ }
169
+ }
170
+ return bestRelative || fsPath;
171
+ }
23
172
  // Create a simple text document manager.
24
173
  const documents = new node_1.TextDocuments(vscode_languageserver_textdocument_1.TextDocument);
25
- const cssVariableManager = new cssVariableManager_1.CssVariableManager(connection.console);
174
+ const cssVariableManager = new cssVariableManager_1.CssVariableManager(connection.console, LOOKUP_FILES);
26
175
  let hasConfigurationCapability = false;
27
176
  let hasWorkspaceFolderCapability = false;
28
177
  let hasDiagnosticRelatedInformationCapability = false;
178
+ let workspaceFolderPaths = [];
179
+ let rootFolderPath = null;
29
180
  connection.onInitialize((params) => {
30
- logDebug('initialize', {
181
+ logDebug("initialize", {
31
182
  rootUri: params.rootUri,
32
183
  // rootPath is deprecated and optional in InitializeParams
33
184
  rootPath: params.rootPath,
@@ -42,13 +193,25 @@ connection.onInitialize((params) => {
42
193
  hasDiagnosticRelatedInformationCapability = !!(capabilities.textDocument &&
43
194
  capabilities.textDocument.publishDiagnostics &&
44
195
  capabilities.textDocument.publishDiagnostics.relatedInformation);
196
+ if (params.rootUri) {
197
+ try {
198
+ rootFolderPath = path.normalize(vscode_uri_1.URI.parse(params.rootUri).fsPath);
199
+ }
200
+ catch {
201
+ rootFolderPath = null;
202
+ }
203
+ }
204
+ else if (params.rootPath) {
205
+ rootFolderPath = path.normalize(params.rootPath);
206
+ }
207
+ updateWorkspaceFolderPaths(params.workspaceFolders || undefined);
45
208
  const result = {
46
209
  capabilities: {
47
210
  textDocumentSync: node_1.TextDocumentSyncKind.Incremental,
48
211
  // Tell the client that this server supports code completion.
49
212
  completionProvider: {
50
213
  resolveProvider: true,
51
- triggerCharacters: ['-']
214
+ triggerCharacters: ["-"],
52
215
  },
53
216
  definitionProvider: true,
54
217
  hoverProvider: true,
@@ -56,14 +219,14 @@ connection.onInitialize((params) => {
56
219
  renameProvider: true,
57
220
  documentSymbolProvider: true,
58
221
  workspaceSymbolProvider: true,
59
- colorProvider: ENABLE_COLOR_PROVIDER
60
- }
222
+ colorProvider: ENABLE_COLOR_PROVIDER,
223
+ },
61
224
  };
62
225
  if (hasWorkspaceFolderCapability) {
63
226
  result.capabilities.workspace = {
64
227
  workspaceFolders: {
65
- supported: true
66
- }
228
+ supported: true,
229
+ },
67
230
  };
68
231
  }
69
232
  return result;
@@ -74,15 +237,19 @@ connection.onInitialized(async () => {
74
237
  connection.client.register(node_1.DidChangeConfigurationNotification.type, undefined);
75
238
  }
76
239
  if (hasWorkspaceFolderCapability) {
77
- connection.workspace.onDidChangeWorkspaceFolders(_event => {
78
- connection.console.log('Workspace folder change event received.');
240
+ connection.workspace.onDidChangeWorkspaceFolders((_event) => {
241
+ connection.console.log("Workspace folder change event received.");
242
+ void connection.workspace.getWorkspaceFolders().then((folders) => {
243
+ updateWorkspaceFolderPaths(folders || undefined);
244
+ });
79
245
  });
80
246
  }
81
247
  // Scan workspace for CSS variables on initialization with progress reporting
82
248
  const workspaceFolders = await connection.workspace.getWorkspaceFolders();
83
249
  if (workspaceFolders) {
84
- connection.console.log('Scanning workspace for CSS variables...');
85
- const folderUris = workspaceFolders.map(f => f.uri);
250
+ updateWorkspaceFolderPaths(workspaceFolders || undefined);
251
+ connection.console.log("Scanning workspace for CSS variables...");
252
+ const folderUris = workspaceFolders.map((f) => f.uri);
86
253
  // Scan with progress callback that logs to console
87
254
  let lastLoggedPercentage = 0;
88
255
  await cssVariableManager.scanWorkspace(folderUris, (current, total) => {
@@ -103,7 +270,7 @@ const defaultSettings = { maxNumberOfProblems: 1000 };
103
270
  let globalSettings = defaultSettings;
104
271
  // Cache the settings of all open documents
105
272
  const documentSettings = new Map();
106
- connection.onDidChangeConfiguration(change => {
273
+ connection.onDidChangeConfiguration((change) => {
107
274
  if (hasConfigurationCapability) {
108
275
  // Reset all cached document settings
109
276
  documentSettings.clear();
@@ -122,7 +289,7 @@ function getDocumentSettings(resource) {
122
289
  if (!result) {
123
290
  result = connection.workspace.getConfiguration({
124
291
  scopeUri: resource,
125
- section: 'cssVariableLsp'
292
+ section: "cssVariableLsp",
126
293
  });
127
294
  documentSettings.set(resource, result);
128
295
  }
@@ -143,7 +310,7 @@ const validationTimeouts = new Map();
143
310
  // when the text document first opened or when its content has changed.
144
311
  // Note: We don't need a separate onDidOpen handler because onDidChangeContent
145
312
  // already fires when a document is first opened, avoiding double-parsing.
146
- documents.onDidChangeContent(change => {
313
+ documents.onDidChangeContent((change) => {
147
314
  // Parse immediately (needed for completion/hover)
148
315
  cssVariableManager.parseDocument(change.document);
149
316
  // Debounce validation to avoid excessive diagnostic updates while typing
@@ -161,7 +328,6 @@ documents.onDidChangeContent(change => {
161
328
  validationTimeouts.set(uri, timeout);
162
329
  });
163
330
  async function validateTextDocument(textDocument) {
164
- const settings = await getDocumentSettings(textDocument.uri);
165
331
  const text = textDocument.getText();
166
332
  const diagnostics = [];
167
333
  // Find all var(--variable) usages
@@ -178,10 +344,10 @@ async function validateTextDocument(textDocument) {
178
344
  severity: node_1.DiagnosticSeverity.Warning,
179
345
  range: {
180
346
  start: startPos,
181
- end: endPos
347
+ end: endPos,
182
348
  },
183
349
  message: `CSS variable '${variableName}' is not defined in the workspace`,
184
- source: 'css-variable-lsp'
350
+ source: "css-variable-lsp",
185
351
  };
186
352
  if (hasDiagnosticRelatedInformationCapability) {
187
353
  diagnostic.relatedInformation = [];
@@ -194,8 +360,8 @@ async function validateTextDocument(textDocument) {
194
360
  }
195
361
  connection.onDidChangeWatchedFiles(async (change) => {
196
362
  // Monitored files have changed in the client
197
- connection.console.log('Received file change event');
198
- logDebug('didChangeWatchedFiles', change);
363
+ connection.console.log("Received file change event");
364
+ logDebug("didChangeWatchedFiles", change);
199
365
  for (const fileEvent of change.changes) {
200
366
  if (fileEvent.type === node_1.FileChangeType.Deleted) {
201
367
  cssVariableManager.removeFile(fileEvent.uri);
@@ -225,26 +391,32 @@ function getPropertyNameFromContext(beforeCursor) {
225
391
  let lastBracePos = -1;
226
392
  for (let i = beforeCursor.length - 1; i >= 0; i--) {
227
393
  const char = beforeCursor[i];
228
- if (char === ')')
394
+ if (char === ")")
229
395
  inParens++;
230
- else if (char === '(') {
396
+ else if (char === "(") {
231
397
  inParens--;
232
398
  if (inParens < 0)
233
399
  break;
234
400
  }
235
- else if (char === '}')
401
+ else if (char === "}")
236
402
  inBraces++;
237
- else if (char === '{') {
403
+ else if (char === "{") {
238
404
  inBraces--;
239
405
  if (inBraces < 0) {
240
406
  lastBracePos = i;
241
407
  break;
242
408
  }
243
409
  }
244
- else if (char === ':' && inParens === 0 && inBraces === 0 && lastColonPos === -1) {
410
+ else if (char === ":" &&
411
+ inParens === 0 &&
412
+ inBraces === 0 &&
413
+ lastColonPos === -1) {
245
414
  lastColonPos = i;
246
415
  }
247
- else if (char === ';' && inParens === 0 && inBraces === 0 && lastSemicolonPos === -1) {
416
+ else if (char === ";" &&
417
+ inParens === 0 &&
418
+ inBraces === 0 &&
419
+ lastSemicolonPos === -1) {
248
420
  lastSemicolonPos = i;
249
421
  }
250
422
  }
@@ -268,65 +440,116 @@ function scoreVariableRelevance(varName, propertyName) {
268
440
  }
269
441
  const lowerVarName = varName.toLowerCase();
270
442
  // Color-related properties
271
- const colorProperties = ['color', 'background-color', 'background', 'border-color', 'outline-color', 'text-decoration-color', 'fill', 'stroke'];
443
+ const colorProperties = [
444
+ "color",
445
+ "background-color",
446
+ "background",
447
+ "border-color",
448
+ "outline-color",
449
+ "text-decoration-color",
450
+ "fill",
451
+ "stroke",
452
+ ];
272
453
  if (colorProperties.includes(propertyName)) {
273
454
  // High relevance: variable name contains color-related keywords
274
- if (lowerVarName.includes('color') || lowerVarName.includes('bg') || lowerVarName.includes('background') ||
275
- lowerVarName.includes('primary') || lowerVarName.includes('secondary') || lowerVarName.includes('accent') ||
276
- lowerVarName.includes('text') || lowerVarName.includes('border') || lowerVarName.includes('link')) {
455
+ if (lowerVarName.includes("color") ||
456
+ lowerVarName.includes("bg") ||
457
+ lowerVarName.includes("background") ||
458
+ lowerVarName.includes("primary") ||
459
+ lowerVarName.includes("secondary") ||
460
+ lowerVarName.includes("accent") ||
461
+ lowerVarName.includes("text") ||
462
+ lowerVarName.includes("border") ||
463
+ lowerVarName.includes("link")) {
277
464
  return 10;
278
465
  }
279
466
  // Low relevance for non-color variables
280
- if (lowerVarName.includes('spacing') || lowerVarName.includes('margin') || lowerVarName.includes('padding') ||
281
- lowerVarName.includes('size') || lowerVarName.includes('width') || lowerVarName.includes('height') ||
282
- lowerVarName.includes('font') || lowerVarName.includes('weight') || lowerVarName.includes('radius')) {
467
+ if (lowerVarName.includes("spacing") ||
468
+ lowerVarName.includes("margin") ||
469
+ lowerVarName.includes("padding") ||
470
+ lowerVarName.includes("size") ||
471
+ lowerVarName.includes("width") ||
472
+ lowerVarName.includes("height") ||
473
+ lowerVarName.includes("font") ||
474
+ lowerVarName.includes("weight") ||
475
+ lowerVarName.includes("radius")) {
283
476
  return 0;
284
477
  }
285
478
  // Medium relevance: might be a color
286
479
  return 5;
287
480
  }
288
481
  // Spacing-related properties
289
- const spacingProperties = ['margin', 'margin-top', 'margin-right', 'margin-bottom', 'margin-left',
290
- 'padding', 'padding-top', 'padding-right', 'padding-bottom', 'padding-left',
291
- 'gap', 'row-gap', 'column-gap'];
482
+ const spacingProperties = [
483
+ "margin",
484
+ "margin-top",
485
+ "margin-right",
486
+ "margin-bottom",
487
+ "margin-left",
488
+ "padding",
489
+ "padding-top",
490
+ "padding-right",
491
+ "padding-bottom",
492
+ "padding-left",
493
+ "gap",
494
+ "row-gap",
495
+ "column-gap",
496
+ ];
292
497
  if (spacingProperties.includes(propertyName)) {
293
- if (lowerVarName.includes('spacing') || lowerVarName.includes('margin') || lowerVarName.includes('padding') ||
294
- lowerVarName.includes('gap')) {
498
+ if (lowerVarName.includes("spacing") ||
499
+ lowerVarName.includes("margin") ||
500
+ lowerVarName.includes("padding") ||
501
+ lowerVarName.includes("gap")) {
295
502
  return 10;
296
503
  }
297
- if (lowerVarName.includes('color') || lowerVarName.includes('bg') || lowerVarName.includes('background')) {
504
+ if (lowerVarName.includes("color") ||
505
+ lowerVarName.includes("bg") ||
506
+ lowerVarName.includes("background")) {
298
507
  return 0;
299
508
  }
300
509
  return 5;
301
510
  }
302
511
  // Size-related properties
303
- const sizeProperties = ['width', 'height', 'max-width', 'max-height', 'min-width', 'min-height', 'font-size'];
512
+ const sizeProperties = [
513
+ "width",
514
+ "height",
515
+ "max-width",
516
+ "max-height",
517
+ "min-width",
518
+ "min-height",
519
+ "font-size",
520
+ ];
304
521
  if (sizeProperties.includes(propertyName)) {
305
- if (lowerVarName.includes('width') || lowerVarName.includes('height') || lowerVarName.includes('size')) {
522
+ if (lowerVarName.includes("width") ||
523
+ lowerVarName.includes("height") ||
524
+ lowerVarName.includes("size")) {
306
525
  return 10;
307
526
  }
308
- if (lowerVarName.includes('color') || lowerVarName.includes('bg') || lowerVarName.includes('background')) {
527
+ if (lowerVarName.includes("color") ||
528
+ lowerVarName.includes("bg") ||
529
+ lowerVarName.includes("background")) {
309
530
  return 0;
310
531
  }
311
532
  return 5;
312
533
  }
313
534
  // Border-radius properties
314
- if (propertyName.includes('radius')) {
315
- if (lowerVarName.includes('radius') || lowerVarName.includes('rounded')) {
535
+ if (propertyName.includes("radius")) {
536
+ if (lowerVarName.includes("radius") || lowerVarName.includes("rounded")) {
316
537
  return 10;
317
538
  }
318
- if (lowerVarName.includes('color') || lowerVarName.includes('bg') || lowerVarName.includes('background')) {
539
+ if (lowerVarName.includes("color") ||
540
+ lowerVarName.includes("bg") ||
541
+ lowerVarName.includes("background")) {
319
542
  return 0;
320
543
  }
321
544
  return 5;
322
545
  }
323
546
  // Font-related properties
324
- const fontProperties = ['font-family', 'font-weight', 'font-style'];
547
+ const fontProperties = ["font-family", "font-weight", "font-style"];
325
548
  if (fontProperties.includes(propertyName)) {
326
- if (lowerVarName.includes('font')) {
549
+ if (lowerVarName.includes("font")) {
327
550
  return 10;
328
551
  }
329
- if (lowerVarName.includes('color') || lowerVarName.includes('spacing')) {
552
+ if (lowerVarName.includes("color") || lowerVarName.includes("spacing")) {
330
553
  return 0;
331
554
  }
332
555
  return 5;
@@ -359,26 +582,32 @@ function isInCssValueContext(document, position) {
359
582
  for (let i = beforeCursor.length - 1; i >= 0; i--) {
360
583
  const char = beforeCursor[i];
361
584
  // Track nesting (scanning backwards)
362
- if (char === ')')
585
+ if (char === ")")
363
586
  inParens++;
364
- else if (char === '(') {
587
+ else if (char === "(") {
365
588
  inParens--;
366
589
  if (inParens < 0)
367
590
  break; // We've left the current context
368
591
  }
369
- else if (char === '}')
592
+ else if (char === "}")
370
593
  inBraces++;
371
- else if (char === '{') {
594
+ else if (char === "{") {
372
595
  inBraces--;
373
596
  if (inBraces < 0) {
374
597
  lastBracePos = i;
375
598
  break; // Found the opening brace of our block
376
599
  }
377
600
  }
378
- else if (char === ':' && inParens === 0 && inBraces === 0 && lastColonPos === -1) {
601
+ else if (char === ":" &&
602
+ inParens === 0 &&
603
+ inBraces === 0 &&
604
+ lastColonPos === -1) {
379
605
  lastColonPos = i;
380
606
  }
381
- else if (char === ';' && inParens === 0 && inBraces === 0 && lastSemicolonPos === -1) {
607
+ else if (char === ";" &&
608
+ inParens === 0 &&
609
+ inBraces === 0 &&
610
+ lastSemicolonPos === -1) {
382
611
  lastSemicolonPos = i;
383
612
  }
384
613
  }
@@ -416,19 +645,19 @@ connection.onCompletion((textDocumentPosition) => {
416
645
  const variables = cssVariableManager.getAllVariables();
417
646
  // Deduplicate by name
418
647
  const uniqueVars = new Map();
419
- variables.forEach(v => {
648
+ variables.forEach((v) => {
420
649
  if (!uniqueVars.has(v.name)) {
421
650
  uniqueVars.set(v.name, v);
422
651
  }
423
652
  });
424
653
  // Score and filter variables based on property context
425
- const scoredVars = Array.from(uniqueVars.values()).map(v => ({
654
+ const scoredVars = Array.from(uniqueVars.values()).map((v) => ({
426
655
  variable: v,
427
- score: scoreVariableRelevance(v.name, propertyName)
656
+ score: scoreVariableRelevance(v.name, propertyName),
428
657
  }));
429
658
  // Filter out score 0 (not relevant) and sort by score (higher first)
430
659
  const filteredAndSorted = scoredVars
431
- .filter(sv => sv.score !== 0)
660
+ .filter((sv) => sv.score !== 0)
432
661
  .sort((a, b) => {
433
662
  // Sort by score (descending)
434
663
  if (a.score !== b.score) {
@@ -437,11 +666,11 @@ connection.onCompletion((textDocumentPosition) => {
437
666
  // Same score: alphabetical order
438
667
  return a.variable.name.localeCompare(b.variable.name);
439
668
  });
440
- return filteredAndSorted.map(sv => ({
669
+ return filteredAndSorted.map((sv) => ({
441
670
  label: sv.variable.name,
442
671
  kind: node_1.CompletionItemKind.Variable,
443
672
  detail: sv.variable.value,
444
- documentation: `Defined in ${sv.variable.uri}`
673
+ documentation: `Defined in ${formatUriForDisplay(sv.variable.uri)}`,
445
674
  }));
446
675
  });
447
676
  // This handler resolves additional information for the item selected in
@@ -464,17 +693,19 @@ connection.onHover((params) => {
464
693
  return undefined;
465
694
  }
466
695
  const word = left[0] + right[0];
467
- if (word.startsWith('--')) {
696
+ if (word.startsWith("--")) {
468
697
  const variables = cssVariableManager.getVariables(word);
469
698
  if (variables.length === 0) {
470
699
  return undefined;
471
700
  }
472
701
  // Get all usages to find context if hovering over a usage
473
702
  const usages = cssVariableManager.getVariableUsages(word);
474
- const hoverUsage = usages.find(u => document.positionAt(document.offsetAt(u.range.start)) === params.position ||
475
- (offset >= document.offsetAt(u.range.start) && offset <= document.offsetAt(u.range.end)));
476
- const usageContext = hoverUsage?.usageContext || '';
477
- const isInlineStyle = usageContext === 'inline-style';
703
+ const hoverUsage = usages.find((u) => document.positionAt(document.offsetAt(u.range.start)) ===
704
+ params.position ||
705
+ (offset >= document.offsetAt(u.range.start) &&
706
+ offset <= document.offsetAt(u.range.end)));
707
+ const usageContext = hoverUsage?.usageContext || "";
708
+ const isInlineStyle = usageContext === "inline-style";
478
709
  // Get DOM tree and node if available (for HTML documents)
479
710
  const domTree = cssVariableManager.getDOMTree(document.uri);
480
711
  const domNode = hoverUsage?.domNode;
@@ -515,12 +746,13 @@ connection.onHover((params) => {
515
746
  }
516
747
  else {
517
748
  // Multiple definitions - show full cascade
518
- hoverText += '**Definitions** (CSS cascade order):\n\n';
749
+ hoverText += "**Definitions** (CSS cascade order):\n\n";
519
750
  sortedVars.forEach((v, index) => {
520
751
  const spec = (0, specificity_1.calculateSpecificity)(v.selector);
521
752
  // Use DOM-aware matching if available, otherwise fall back to simple matching
522
- const isApplicable = usageContext ?
523
- (0, specificity_1.matchesContext)(v.selector, usageContext, domTree, domNode) : true;
753
+ const isApplicable = usageContext
754
+ ? (0, specificity_1.matchesContext)(v.selector, usageContext, domTree, domNode)
755
+ : true;
524
756
  const isWinner = index === 0 && (isApplicable || isInlineStyle);
525
757
  let line = `${index + 1}. \`${v.value}\``;
526
758
  if (v.important) {
@@ -532,39 +764,39 @@ connection.onHover((params) => {
532
764
  }
533
765
  if (isWinner && usageContext) {
534
766
  if (v.important) {
535
- line += ' ✓ **Wins (!important)**';
767
+ line += " ✓ **Wins (!important)**";
536
768
  }
537
769
  else if (isInlineStyle) {
538
- line += ' ✓ **Would apply (inline style)**';
770
+ line += " ✓ **Would apply (inline style)**";
539
771
  }
540
772
  else if (domTree && domNode) {
541
- line += ' ✓ **Applies (DOM match)**';
773
+ line += " ✓ **Applies (DOM match)**";
542
774
  }
543
775
  else {
544
- line += ' ✓ **Applies here**';
776
+ line += " ✓ **Applies here**";
545
777
  }
546
778
  }
547
779
  else if (!isApplicable && usageContext && !isInlineStyle) {
548
- line += ' _(selector doesn\'t match)_';
780
+ line += " _(selector doesn't match)_";
549
781
  }
550
782
  else if (index > 0 && usageContext) {
551
783
  // Explain why it doesn't win
552
784
  const winner = sortedVars[0];
553
785
  if (winner.important && !v.important) {
554
- line += ' _(overridden by !important)_';
786
+ line += " _(overridden by !important)_";
555
787
  }
556
788
  else {
557
789
  const winnerSpec = (0, specificity_1.calculateSpecificity)(winner.selector);
558
790
  const cmp = (0, specificity_1.compareSpecificity)(winnerSpec, spec);
559
791
  if (cmp > 0) {
560
- line += ' _(lower specificity)_';
792
+ line += " _(lower specificity)_";
561
793
  }
562
794
  else if (cmp === 0) {
563
- line += ' _(earlier in source)_';
795
+ line += " _(earlier in source)_";
564
796
  }
565
797
  }
566
798
  }
567
- hoverText += line + '\n';
799
+ hoverText += line + "\n";
568
800
  });
569
801
  if (usageContext) {
570
802
  if (isInlineStyle) {
@@ -580,9 +812,9 @@ connection.onHover((params) => {
580
812
  }
581
813
  return {
582
814
  contents: {
583
- kind: 'markdown',
584
- value: hoverText
585
- }
815
+ kind: "markdown",
816
+ value: hoverText,
817
+ },
586
818
  };
587
819
  }
588
820
  return undefined;
@@ -600,12 +832,12 @@ connection.onDefinition((params) => {
600
832
  return undefined;
601
833
  }
602
834
  const word = left[0] + right[0];
603
- if (word.startsWith('--')) {
835
+ if (word.startsWith("--")) {
604
836
  const variables = cssVariableManager.getVariables(word);
605
837
  if (variables.length > 0) {
606
838
  return {
607
839
  uri: variables[0].uri,
608
- range: variables[0].range
840
+ range: variables[0].range,
609
841
  };
610
842
  }
611
843
  }
@@ -625,9 +857,9 @@ connection.onReferences((params) => {
625
857
  return [];
626
858
  }
627
859
  const word = left[0] + right[0];
628
- if (word.startsWith('--')) {
860
+ if (word.startsWith("--")) {
629
861
  const references = cssVariableManager.getReferences(word);
630
- return references.map(ref => node_1.Location.create(ref.uri, ref.range));
862
+ return references.map((ref) => node_1.Location.create(ref.uri, ref.range));
631
863
  }
632
864
  return [];
633
865
  });
@@ -645,7 +877,7 @@ connection.onRenameRequest((params) => {
645
877
  return null;
646
878
  }
647
879
  const word = left[0] + right[0];
648
- if (word.startsWith('--')) {
880
+ if (word.startsWith("--")) {
649
881
  const references = cssVariableManager.getReferences(word);
650
882
  const changes = {};
651
883
  for (const ref of references) {
@@ -656,9 +888,9 @@ connection.onRenameRequest((params) => {
656
888
  // For usages in var(), replace just the variable name part
657
889
  const edit = {
658
890
  range: ref.range,
659
- newText: 'value' in ref
891
+ newText: "value" in ref
660
892
  ? `${params.newName}: ${ref.value};` // Definition
661
- : `var(${params.newName})` // Usage
893
+ : `var(${params.newName})`, // Usage
662
894
  };
663
895
  changes[ref.uri].push(edit);
664
896
  }
@@ -673,16 +905,16 @@ connection.onDocumentSymbol((params) => {
673
905
  return [];
674
906
  }
675
907
  const variables = cssVariableManager.getDocumentDefinitions(document.uri);
676
- return variables.map(v => node_1.DocumentSymbol.create(v.name, v.value, node_1.SymbolKind.Variable, v.range, v.range));
908
+ return variables.map((v) => node_1.DocumentSymbol.create(v.name, v.value, node_1.SymbolKind.Variable, v.range, v.range));
677
909
  });
678
910
  // Workspace symbols handler
679
911
  connection.onWorkspaceSymbol((params) => {
680
912
  const query = params.query.toLowerCase();
681
913
  const allVariables = cssVariableManager.getAllDefinitions();
682
914
  const filtered = query
683
- ? allVariables.filter(v => v.name.toLowerCase().includes(query))
915
+ ? allVariables.filter((v) => v.name.toLowerCase().includes(query))
684
916
  : allVariables;
685
- return filtered.map(v => node_1.WorkspaceSymbol.create(v.name, node_1.SymbolKind.Variable, v.uri, v.range));
917
+ return filtered.map((v) => node_1.WorkspaceSymbol.create(v.name, node_1.SymbolKind.Variable, v.uri, v.range));
686
918
  });
687
919
  // Color Provider: Document Colors
688
920
  connection.onDocumentColor((params) => {
@@ -707,24 +939,27 @@ connection.onDocumentColor((params) => {
707
939
  if (def.valueRange) {
708
940
  colors.push({
709
941
  range: def.valueRange,
710
- color: color
942
+ color: color,
711
943
  });
712
944
  }
713
945
  else {
714
946
  // Fallback: find the value within the declaration text
715
947
  // This handles cases where valueRange wasn't captured (shouldn't happen normally)
716
948
  const defText = text.substring(document.offsetAt(def.range.start), document.offsetAt(def.range.end));
717
- const colonIndex = defText.indexOf(':');
949
+ const colonIndex = defText.indexOf(":");
718
950
  if (colonIndex !== -1) {
719
951
  const afterColon = defText.substring(colonIndex + 1);
720
952
  const valueIndex = afterColon.indexOf(def.value.trim());
721
953
  if (valueIndex !== -1) {
722
- const absoluteValueStart = document.offsetAt(def.range.start) + colonIndex + 1 + valueIndex;
954
+ const absoluteValueStart = document.offsetAt(def.range.start) +
955
+ colonIndex +
956
+ 1 +
957
+ valueIndex;
723
958
  const start = document.positionAt(absoluteValueStart);
724
959
  const end = document.positionAt(absoluteValueStart + def.value.trim().length);
725
960
  colors.push({
726
961
  range: { start, end },
727
- color: color
962
+ color: color,
728
963
  });
729
964
  }
730
965
  }
@@ -744,7 +979,7 @@ connection.onDocumentColor((params) => {
744
979
  const end = document.positionAt(match.index + match[0].length);
745
980
  colors.push({
746
981
  range: { start, end },
747
- color: color
982
+ color: color,
748
983
  });
749
984
  }
750
985
  }