@master/css-language-server 2.0.0-rc.75 → 2.0.0-rc.76

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/README.md CHANGED
@@ -73,7 +73,9 @@ import CSSLanguageServer from '@master/css-language-server'
73
73
  const languageServer = new CSSLanguageServer(connection, customSettings)
74
74
  ```
75
75
 
76
- `CSSLanguageServer` wraps `@master/css-language-service` with LSP workspace lifecycle, completion, hover, document color, color presentation, and semantic token handlers.
76
+ `CSSLanguageServer` wraps `@master/css-language-service` with LSP workspace lifecycle, completion, hover, document color, color presentation, semantic token, and directive formatting handlers.
77
+
78
+ Diagnostics are intentionally limited to language-server-owned behavior: CSS directive diagnostics and project manifest loading diagnostics. Class syntax and policy rules such as sorting, canonical class preference, conflict detection, invalid classes, and raw-value approval are owned by `@master/eslint-plugin-css`.
77
79
 
78
80
  ## Semantic tokens
79
81
 
@@ -87,6 +89,12 @@ The language server supports full-document and active-position semantic tokens.
87
89
 
88
90
  CSS directive syntax is highlighted by the shared TextMate grammar. Server semantic tokens cover Master CSS class-list spans and directive class-list spans such as `@compose` and `@safelist`.
89
91
 
92
+ ## Formatting
93
+
94
+ The server advertises `documentFormattingProvider` and `documentRangeFormattingProvider` when `formatDirectives` is enabled. Formatting returns directive-only edits for CSS, SCSS, and LESS documents, plus CSS-family `<style>` blocks in Vue, Svelte, and Astro documents.
95
+
96
+ Directive formatting normalizes safe directive spacing and repairs class-list important markers such as `bg:transparent !` to `bg:transparent!`. It does not run the compiler and does not change generated CSS output.
97
+
90
98
  ## Settings
91
99
 
92
100
  ```js
@@ -97,6 +105,7 @@ import { settings } from '@master/css-language-server'
97
105
  | --- | --- | --- | --- |
98
106
  | `workspaces` | `FastGlobPattern[] \| 'auto'` | `'auto'` | Workspace roots where independent language services are created. The root workspace is always included. |
99
107
  | `verbose` | `boolean` | `false` | Print server logs. |
108
+ | `formatDirectives` | `boolean` | `true` | Enables LSP document and range formatting for Master CSS directives. |
100
109
  | `embeddedSyntaxHighlighting` | `'active' \| 'always' \| 'off'` | `'active'` | Inherited language-service semantic token mode. |
101
110
 
102
- With `workspaces: 'auto'`, the server creates workspaces from CSS files containing `@master entry;` or `@import '@master/css'`, and from `package.json` files that declare Master CSS package dependencies.
111
+ With `workspaces: 'auto'`, the server creates workspaces from CSS files importing `@master/css`, CSS files containing the lightweight `@master entry;` marker, and `package.json` files that declare Master CSS package dependencies.
package/dist/core.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- import { ClientCapabilities, ColorPresentationParams, CompletionParams, Connection, DidChangeConfigurationParams, DocumentColorParams, HoverParams, InitializeParams, InitializeResult, RemoteConsole, SemanticTokensParams, TextDocumentChangeEvent, TextDocumentPositionParams, TextDocuments, WorkspaceFolder } from "vscode-languageserver/node";
1
+ import { ClientCapabilities, ColorPresentationParams, CompletionParams, Connection, DidChangeConfigurationParams, DocumentColorParams, DocumentFormattingParams, DocumentRangeFormattingParams, HoverParams, InitializeParams, InitializeResult, RemoteConsole, SemanticTokensParams, TextDocumentChangeEvent, TextDocumentPositionParams, TextDocuments, TextEdit, WorkspaceFolder } from "vscode-languageserver/node";
2
2
  import { TextDocument } from "vscode-languageserver-textdocument";
3
- import CSSLanguageService, { Settings } from "@master/css-language-service";
3
+ import CSSLanguageService from "@master/css-language-service";
4
4
  import { MasterCSSWorkspacePackageResolution } from "@master/css-project/workspace";
5
5
  import { CSSLanguageRuntime } from "@master/css-language";
6
6
  import { Settings as Settings$1 } from "./settings.js";
@@ -10,10 +10,11 @@ declare interface Workspace {
10
10
  uri: string;
11
11
  openedTextDocuments: TextDocument[];
12
12
  languageService?: CSSLanguageService;
13
- languageServiceSettings: Settings;
13
+ languageServiceSettings: Settings$1;
14
14
  languageRuntime?: CSSLanguageRuntime;
15
15
  languageRuntimeResolution?: MasterCSSWorkspacePackageResolution;
16
16
  languageRuntimeSource?: 'workspace' | 'bundled';
17
+ manifestErrors?: unknown[];
17
18
  planEntries?: string[];
18
19
  }
19
20
  declare const ACTIVE_SEMANTIC_TOKENS_REQUEST = "masterCSS/renderActiveSemanticTokens";
@@ -38,6 +39,9 @@ declare class CSSLanguageServer {
38
39
  onCompletion(params: CompletionParams): Promise<import("vscode-languageserver-protocol").CompletionItem[] | undefined>;
39
40
  onDocumentColor(params: DocumentColorParams): Promise<import("vscode-languageserver-protocol").ColorInformation[]>;
40
41
  onColorPresentation(params: ColorPresentationParams): Promise<import("vscode-languageserver-protocol").ColorPresentation[]>;
42
+ private getWorkspaceDocument;
43
+ onDocumentFormatting(params: DocumentFormattingParams): Promise<TextEdit[]>;
44
+ onDocumentRangeFormatting(params: DocumentRangeFormattingParams): Promise<TextEdit[]>;
41
45
  onSemanticTokens(params: SemanticTokensParams): Promise<import("vscode-languageserver-protocol").SemanticTokens>;
42
46
  onDocumentSemanticTokens(params: SemanticTokensParams): Promise<import("vscode-languageserver-protocol").SemanticTokens>;
43
47
  onActiveSemanticTokens(params: TextDocumentPositionParams): Promise<import("vscode-languageserver-protocol").SemanticTokens>;
@@ -55,7 +59,9 @@ declare class CSSLanguageServer {
55
59
  private importWorkspaceModule;
56
60
  destroyLanguageService(workspace: Workspace): void;
57
61
  findClosestWorkspace(textDocumentURI: string): Workspace;
58
- private publishCSSDirectiveDiagnostics;
62
+ private publishDiagnostics;
63
+ private createCSSDirectiveDiagnostics;
64
+ private createManifestLoadingDiagnostics;
59
65
  private createCSSDirectiveDiagnostic;
60
66
  private createCSSDirectiveRelatedInformation;
61
67
  private createCSSDirectiveRange;
package/dist/core.js CHANGED
@@ -35,6 +35,8 @@ const SERVER_CAPABILITIES = {
35
35
  },
36
36
  colorProvider: true,
37
37
  hoverProvider: true,
38
+ documentFormattingProvider: true,
39
+ documentRangeFormattingProvider: true,
38
40
  semanticTokensProvider: {
39
41
  legend: SEMANTIC_TOKENS_LEGEND,
40
42
  full: true
@@ -137,7 +139,7 @@ var CSSLanguageServer = class {
137
139
  } });
138
140
  }
139
141
  start() {
140
- this.disposables.push(this.documents.onDidSave(this.onDidSave.bind(this)), this.documents.onDidOpen(this.onDidOpen.bind(this)), this.documents.onDidChangeContent(this.onDidChangeContent.bind(this)), this.documents.onDidClose(this.onDidClose.bind(this)), this.documents.listen(this.connection), this.connection.onDidChangeConfiguration(this.onDidChangeConfiguration.bind(this)), this.connection.onHover(this.onHover.bind(this)), this.connection.onCompletion(this.onCompletion.bind(this)), this.connection.onDocumentColor(this.onDocumentColor.bind(this)), this.connection.onColorPresentation(this.onColorPresentation.bind(this)), this.connection.languages.semanticTokens.on(this.onSemanticTokens.bind(this)), this.connection.onRequest(DOCUMENT_SEMANTIC_TOKENS_REQUEST, this.onDocumentSemanticTokens.bind(this)), this.connection.onRequest(ACTIVE_SEMANTIC_TOKENS_REQUEST, this.onActiveSemanticTokens.bind(this)), this.connection.onInitialize(this.onInitialize.bind(this)), this.connection.onInitialized(() => this.init()));
142
+ this.disposables.push(this.documents.onDidSave(this.onDidSave.bind(this)), this.documents.onDidOpen(this.onDidOpen.bind(this)), this.documents.onDidChangeContent(this.onDidChangeContent.bind(this)), this.documents.onDidClose(this.onDidClose.bind(this)), this.documents.listen(this.connection), this.connection.onDidChangeConfiguration(this.onDidChangeConfiguration.bind(this)), this.connection.onHover(this.onHover.bind(this)), this.connection.onCompletion(this.onCompletion.bind(this)), this.connection.onDocumentColor(this.onDocumentColor.bind(this)), this.connection.onColorPresentation(this.onColorPresentation.bind(this)), this.connection.onDocumentFormatting(this.onDocumentFormatting.bind(this)), this.connection.onDocumentRangeFormatting(this.onDocumentRangeFormatting.bind(this)), this.connection.languages.semanticTokens.on(this.onSemanticTokens.bind(this)), this.connection.onRequest(DOCUMENT_SEMANTIC_TOKENS_REQUEST, this.onDocumentSemanticTokens.bind(this)), this.connection.onRequest(ACTIVE_SEMANTIC_TOKENS_REQUEST, this.onActiveSemanticTokens.bind(this)), this.connection.onInitialize(this.onInitialize.bind(this)), this.connection.onInitialized(() => this.init()));
141
143
  this.connection.listen();
142
144
  }
143
145
  init() {
@@ -158,6 +160,10 @@ var CSSLanguageServer = class {
158
160
  if (params.workspaceFolders?.length) this.workspaceFolders = params.workspaceFolders;
159
161
  const capabilities = { ...SERVER_CAPABILITIES };
160
162
  if (this.settings?.embeddedSyntaxHighlighting !== "always") delete capabilities.semanticTokensProvider;
163
+ if (!this.settings?.formatDirectives) {
164
+ delete capabilities.documentFormattingProvider;
165
+ delete capabilities.documentRangeFormattingProvider;
166
+ }
161
167
  return { capabilities };
162
168
  }
163
169
  async onHover(params) {
@@ -194,13 +200,30 @@ var CSSLanguageServer = class {
194
200
  }
195
201
  return [];
196
202
  }
203
+ getWorkspaceDocument(uri) {
204
+ const workspace = this.findClosestWorkspace(uri);
205
+ if (!workspace?.languageService) return;
206
+ const document = this.documents.get(uri) ?? workspace.openedTextDocuments.find((document) => document.uri === uri);
207
+ if (!document) return;
208
+ return {
209
+ workspace,
210
+ document
211
+ };
212
+ }
213
+ async onDocumentFormatting(params) {
214
+ await this.init();
215
+ const context = this.getWorkspaceDocument(params.textDocument.uri);
216
+ return context?.workspace.languageService?.formatDirectives(context.document) ?? [];
217
+ }
218
+ async onDocumentRangeFormatting(params) {
219
+ await this.init();
220
+ const context = this.getWorkspaceDocument(params.textDocument.uri);
221
+ return context?.workspace.languageService?.formatDirectives(context.document, params.range) ?? [];
222
+ }
197
223
  async onSemanticTokens(params) {
198
224
  await this.init();
199
- const workspace = this.findClosestWorkspace(params.textDocument.uri);
200
- if (workspace?.languageService) {
201
- const document = this.documents.get(params.textDocument.uri) ?? workspace.openedTextDocuments.find((document) => document.uri === params.textDocument.uri);
202
- if (document) return workspace.languageService.renderSemanticTokens(document) ?? { data: [] };
203
- }
225
+ const context = this.getWorkspaceDocument(params.textDocument.uri);
226
+ if (context) return context.workspace.languageService?.renderSemanticTokens(context.document) ?? { data: [] };
204
227
  return { data: [] };
205
228
  }
206
229
  async onDocumentSemanticTokens(params) {
@@ -208,11 +231,8 @@ var CSSLanguageServer = class {
208
231
  }
209
232
  async onActiveSemanticTokens(params) {
210
233
  await this.init();
211
- const workspace = this.findClosestWorkspace(params.textDocument.uri);
212
- if (workspace?.languageService) {
213
- const document = this.documents.get(params.textDocument.uri) ?? workspace.openedTextDocuments.find((document) => document.uri === params.textDocument.uri);
214
- if (document) return workspace.languageService.renderSemanticTokensAtPosition(document, params.position) ?? { data: [] };
215
- }
234
+ const context = this.getWorkspaceDocument(params.textDocument.uri);
235
+ if (context) return context.workspace.languageService?.renderSemanticTokensAtPosition(context.document, params.position) ?? { data: [] };
216
236
  return { data: [] };
217
237
  }
218
238
  async onDidOpen(params) {
@@ -220,18 +240,18 @@ var CSSLanguageServer = class {
220
240
  const workspace = this.findClosestWorkspace(params.document.uri);
221
241
  if (!workspace) return;
222
242
  if (workspace.openedTextDocuments.includes(params.document)) {
223
- this.publishCSSDirectiveDiagnostics(params.document, workspace);
243
+ this.publishDiagnostics(params.document, workspace);
224
244
  return;
225
245
  }
226
246
  if (!workspace.openedTextDocuments.length) await this.initWorkspaceLanguageService(workspace);
227
247
  workspace.openedTextDocuments.push(params.document);
228
- this.publishCSSDirectiveDiagnostics(params.document, workspace);
248
+ this.publishDiagnostics(params.document, workspace);
229
249
  }
230
250
  async onDidChangeContent(params) {
231
251
  await this.init();
232
252
  const workspace = this.findClosestWorkspace(params.document.uri);
233
253
  if (!workspace) return;
234
- this.publishCSSDirectiveDiagnostics(params.document, workspace);
254
+ this.publishDiagnostics(params.document, workspace);
235
255
  }
236
256
  async onDidClose(params) {
237
257
  await this.init();
@@ -248,7 +268,7 @@ var CSSLanguageServer = class {
248
268
  await this.init();
249
269
  const workspace = this.findClosestWorkspace(params.document.uri);
250
270
  if (!workspace) return;
251
- this.publishCSSDirectiveDiagnostics(params.document, workspace);
271
+ this.publishDiagnostics(params.document, workspace);
252
272
  if (path.basename(URI.parse(params.document.uri).fsPath).endsWith(".css")) {
253
273
  this.refreshSemanticTokens();
254
274
  this.connection.sendRequest("masterCSS/restart", { title: "Updating Master CSS configuration" });
@@ -263,7 +283,7 @@ var CSSLanguageServer = class {
263
283
  this.globalWorkspace.languageServiceSettings = this.settings;
264
284
  this.refreshSemanticTokens();
265
285
  this.connection.sendRequest("masterCSS/restart", { title: "Updating Master CSS settings" });
266
- for (const workspace of [this.globalWorkspace, ...this.workspaces.values()]) for (const document of workspace.openedTextDocuments) this.publishCSSDirectiveDiagnostics(document, workspace);
286
+ for (const workspace of [this.globalWorkspace, ...this.workspaces.values()]) for (const document of workspace.openedTextDocuments) this.publishDiagnostics(document, workspace);
267
287
  }
268
288
  }
269
289
  async initWorkspaceFolder(workspaceFolderURI) {
@@ -298,10 +318,12 @@ var CSSLanguageServer = class {
298
318
  async initWorkspaceLanguageService(workspace) {
299
319
  workspace.languageRuntime = await this.loadWorkspaceLanguageRuntime(workspace);
300
320
  let workspacePlan;
321
+ workspace.manifestErrors = [];
301
322
  if (workspace !== this.globalWorkspace) {
302
323
  try {
303
324
  workspacePlan = await this.loadWorkspacePlan(workspace);
304
325
  } catch (e) {
326
+ workspace.manifestErrors = [e];
305
327
  this.console.info(`Failed to load manifest from ${workspace.uri}`);
306
328
  this.console.error(e instanceof Error ? e.stack : e.toString());
307
329
  }
@@ -396,8 +418,17 @@ var CSSLanguageServer = class {
396
418
  this.console.info(`This is an external document ${textDocumentURI} with the global workspace`);
397
419
  return this.globalWorkspace;
398
420
  }
399
- publishCSSDirectiveDiagnostics(textDocument, workspace) {
400
- if (!isCSSDiagnosticDocument(textDocument)) return;
421
+ publishDiagnostics(textDocument, workspace) {
422
+ const diagnostics = [];
423
+ diagnostics.push(...this.createManifestLoadingDiagnostics(textDocument, workspace));
424
+ diagnostics.push(...this.createCSSDirectiveDiagnostics(textDocument, workspace));
425
+ this.connection.sendDiagnostics({
426
+ uri: textDocument.uri,
427
+ diagnostics
428
+ });
429
+ }
430
+ createCSSDirectiveDiagnostics(textDocument, workspace) {
431
+ if (!isCSSDiagnosticDocument(textDocument)) return [];
401
432
  const diagnostics = [];
402
433
  const documentFile = path.resolve(URI.parse(textDocument.uri).fsPath);
403
434
  for (const { source, offset } of getCSSDiagnosticSources(textDocument)) try {
@@ -410,9 +441,31 @@ var CSSLanguageServer = class {
410
441
  const diagnostic = this.createCSSDirectiveDiagnostic(error, textDocument, documentFile, offset);
411
442
  if (diagnostic) diagnostics.push(diagnostic);
412
443
  }
413
- this.connection.sendDiagnostics({
414
- uri: textDocument.uri,
415
- diagnostics
444
+ return diagnostics;
445
+ }
446
+ createManifestLoadingDiagnostics(textDocument, workspace) {
447
+ if (!workspace.manifestErrors?.length) return [];
448
+ const documentFile = path.resolve(URI.parse(textDocument.uri).fsPath);
449
+ return workspace.manifestErrors.map((error) => {
450
+ const directiveError = isCSSDirectiveError(error) ? error : void 0;
451
+ const source = directiveError?.source;
452
+ return {
453
+ range: source && (!source.file || path.resolve(source.file) === documentFile) ? this.createCSSDirectiveRange(source, textDocument, documentFile, 0) : {
454
+ start: {
455
+ line: 0,
456
+ character: 0
457
+ },
458
+ end: {
459
+ line: 0,
460
+ character: 0
461
+ }
462
+ },
463
+ severity: DiagnosticSeverity.Error,
464
+ code: "manifest-loading-error",
465
+ source: "Master CSS",
466
+ message: `Failed to load Master CSS manifest: ${error instanceof Error ? error.message : String(error)}`,
467
+ relatedInformation: directiveError ? this.createCSSDirectiveRelatedInformation(directiveError.related, textDocument, documentFile, 0) : void 0
468
+ };
416
469
  });
417
470
  }
418
471
  createCSSDirectiveDiagnostic(error, textDocument, documentFile, sourceOffset) {
package/package.json CHANGED
@@ -17,12 +17,12 @@
17
17
  "vscode-languageserver": "^10.0.0",
18
18
  "vscode-languageserver-textdocument": "^1.0.12",
19
19
  "vscode-uri": "^3.1.0",
20
- "@master/css": "^2.0.0-rc.75",
21
- "@master/css-compiler": "^2.0.0-rc.75",
22
- "@master/css-language": "^2.0.0-rc.75",
23
- "@master/css-project": "^2.0.0-rc.75",
24
- "@master/css-language-service": "^2.0.0-rc.75",
25
- "@master/css-schema": "^2.0.0-rc.75"
20
+ "@master/css-language": "^2.0.0-rc.76",
21
+ "@master/css": "^2.0.0-rc.76",
22
+ "@master/css-compiler": "^2.0.0-rc.76",
23
+ "@master/css-project": "^2.0.0-rc.76",
24
+ "@master/css-language-service": "^2.0.0-rc.76",
25
+ "@master/css-schema": "^2.0.0-rc.76"
26
26
  },
27
27
  "sideEffects": false,
28
28
  "main": "./dist/index.js",
@@ -49,7 +49,7 @@
49
49
  "access": "public",
50
50
  "provenance": true
51
51
  },
52
- "version": "2.0.0-rc.75",
52
+ "version": "2.0.0-rc.76",
53
53
  "scripts": {
54
54
  "build": "tsdown --config ../../shared/tsdown-package.config.ts",
55
55
  "dev": "tsdown --config ../../shared/tsdown-package.config.ts --watch",