@master/css-language-service 2.0.0-rc.69 → 2.0.0-rc.70

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 (72) hide show
  1. package/README.md +57 -6
  2. package/dist/common.d.ts +1 -9
  3. package/dist/common.mjs +1 -48
  4. package/dist/core.d.ts +11 -10
  5. package/dist/core.mjs +70 -200
  6. package/dist/features/inspect-syntax.mjs +18 -32
  7. package/dist/features/render-semantic-tokens.d.ts +5 -0
  8. package/dist/features/render-semantic-tokens.mjs +10 -0
  9. package/dist/features/render-syntax-colors.mjs +23 -8
  10. package/dist/features/suggest-syntax.d.ts +2 -1
  11. package/dist/features/suggest-syntax.mjs +2 -2
  12. package/dist/index.mjs +1 -1
  13. package/dist/settings.d.ts +4 -7
  14. package/dist/settings.mjs +12 -41
  15. package/dist/utils/beautify-css.mjs +1 -1
  16. package/dist/utils/completion-index.d.ts +38 -0
  17. package/dist/utils/completion-index.mjs +268 -0
  18. package/dist/utils/convert-color-token.d.ts +1 -1
  19. package/dist/utils/convert-color-token.mjs +1 -1
  20. package/dist/utils/create-css-markdown-documentation.d.ts +5 -0
  21. package/dist/utils/create-css-markdown-documentation.mjs +15 -0
  22. package/dist/utils/get-class-completion-items.d.ts +4 -0
  23. package/dist/utils/get-class-completion-items.mjs +20 -0
  24. package/dist/utils/get-color-completion-items.d.ts +1 -1
  25. package/dist/utils/get-color-completion-items.mjs +6 -9
  26. package/dist/utils/get-pseudo-class-completion-items.d.ts +3 -2
  27. package/dist/utils/get-pseudo-class-completion-items.mjs +45 -42
  28. package/dist/utils/get-pseudo-element-completion-items.d.ts +3 -2
  29. package/dist/utils/get-pseudo-element-completion-items.mjs +17 -28
  30. package/dist/utils/get-query-completion-items.d.ts +3 -2
  31. package/dist/utils/get-query-completion-items.mjs +19 -32
  32. package/dist/utils/get-utility-info.d.ts +2 -5
  33. package/dist/utils/get-utility-info.mjs +4 -16
  34. package/dist/utils/get-value-completion-items.d.ts +3 -2
  35. package/dist/utils/get-value-completion-items.mjs +145 -123
  36. package/dist/utils/query-syntax-completions.d.ts +3 -2
  37. package/dist/utils/query-syntax-completions.mjs +25 -44
  38. package/package.json +1 -1
  39. package/dist/common.cjs +0 -57
  40. package/dist/core.cjs +0 -246
  41. package/dist/features/edit-syntax-colors.cjs +0 -19
  42. package/dist/features/inspect-syntax.cjs +0 -70
  43. package/dist/features/render-syntax-colors.cjs +0 -89
  44. package/dist/features/suggest-syntax.cjs +0 -20
  45. package/dist/index.cjs +0 -29
  46. package/dist/settings.cjs +0 -84
  47. package/dist/utils/beautify-css.cjs +0 -13
  48. package/dist/utils/convert-color-token.cjs +0 -60
  49. package/dist/utils/create-doc.cjs +0 -26
  50. package/dist/utils/css-data-provider.cjs +0 -9
  51. package/dist/utils/css-data-provider.d.ts +0 -3
  52. package/dist/utils/css-data-provider.mjs +0 -7
  53. package/dist/utils/find-matching-brackets.cjs +0 -29
  54. package/dist/utils/find-matching-brackets.d.ts +0 -1
  55. package/dist/utils/find-matching-brackets.mjs +0 -27
  56. package/dist/utils/get-color-completion-items.cjs +0 -29
  57. package/dist/utils/get-css-data-documentation.cjs +0 -88
  58. package/dist/utils/get-css-data-documentation.d.ts +0 -5
  59. package/dist/utils/get-css-data-documentation.mjs +0 -86
  60. package/dist/utils/get-main-completion-items.cjs +0 -106
  61. package/dist/utils/get-main-completion-items.d.ts +0 -3
  62. package/dist/utils/get-main-completion-items.mjs +0 -104
  63. package/dist/utils/get-position.cjs +0 -12
  64. package/dist/utils/get-pseudo-class-completion-items.cjs +0 -67
  65. package/dist/utils/get-pseudo-element-completion-items.cjs +0 -56
  66. package/dist/utils/get-query-completion-items.cjs +0 -92
  67. package/dist/utils/get-range.cjs +0 -23
  68. package/dist/utils/get-utility-info.cjs +0 -33
  69. package/dist/utils/get-value-completion-items.cjs +0 -183
  70. package/dist/utils/query-syntax-completions.cjs +0 -115
  71. package/dist/utils/regex.cjs +0 -5
  72. package/dist/utils/sort-completion-items.cjs +0 -13
package/README.md CHANGED
@@ -48,16 +48,67 @@
48
48
  <img alt="Follow @mastercorg" src="https://img.shields.io/static/v1?label=%20&message=twitter&color=f6f7f8&logo=twitter&style=for-the-badge">
49
49
  </picture>
50
50
  </a>
51
- <a aria-label="Github Actions" href="https://github.com/master-co/css/actions/workflows/release.yml">
51
+ <a aria-label="Github Actions" href="https://github.com/master-co/css/actions/workflows/ci-release.yml">
52
52
  <picture>
53
- <source media="(prefers-color-scheme: dark)" srcset="https://img.shields.io/github/actions/workflow/status/master-co/css/release.yml?branch=rc&label=%20&message=twitter&color=212022&logo=githubactions&style=for-the-badge">
54
- <source media="(prefers-color-scheme: light)" srcset="https://img.shields.io/github/actions/workflow/status/master-co/css/release.yml?branch=rc&label=%20&message=twitter&color=f6f7f8&logo=githubactions&style=for-the-badge&logoColor=%23000">
55
- <img alt="Github release actions" src="https://img.shields.io/github/actions/workflow/status/master-co/css/release.yml?branch=rc&label=%20&message=twitter&color=f6f7f8&logo=githubactions&style=for-the-badge&logoColor=%23000">
53
+ <source media="(prefers-color-scheme: dark)" srcset="https://img.shields.io/github/actions/workflow/status/master-co/css/ci-release.yml?branch=rc&label=%20&message=twitter&color=212022&logo=githubactions&style=for-the-badge">
54
+ <source media="(prefers-color-scheme: light)" srcset="https://img.shields.io/github/actions/workflow/status/master-co/css/ci-release.yml?branch=rc&label=%20&message=twitter&color=f6f7f8&logo=githubactions&style=for-the-badge&logoColor=%23000">
55
+ <img alt="Github release actions" src="https://img.shields.io/github/actions/workflow/status/master-co/css/ci-release.yml?branch=rc&label=%20&message=twitter&color=f6f7f8&logo=githubactions&style=for-the-badge&logoColor=%23000">
56
56
  </picture>
57
57
  </a>
58
58
  </p>
59
59
 
60
60
  </div>
61
61
 
62
- ## Documentation
63
- Check out the official [documentation](https://rc.css.master.co/reference/language-service).
62
+ ## Installation
63
+
64
+ ```bash
65
+ npm install @master/css-language-service
66
+ ```
67
+
68
+ ## Usage
69
+
70
+ ```js
71
+ import CSSLanguageService from '@master/css-language-service'
72
+
73
+ const languageService = new CSSLanguageService(customSettings)
74
+ ```
75
+
76
+ `CSSLanguageService` provides stateful completion, hover, color, color presentation, and semantic token features around the editor-neutral primitives in `@master/css-language`.
77
+
78
+ ### Semantic tokens
79
+
80
+ ```ts
81
+ const semanticTokens = languageService.renderSemanticTokens(textDocument)
82
+ ```
83
+
84
+ Semantic tokens are generated with the project manifest, so custom variables, components, utilities, modes, and other manifest-dependent tokens can be classified after the caller loads the manifest.
85
+
86
+ Use `renderSemanticTokensAtPosition()` when a client wants active-only highlighting for the embedded class context at the current editor position.
87
+
88
+ ```ts
89
+ const semanticTokens = languageService.renderSemanticTokensAtPosition(textDocument, position)
90
+ ```
91
+
92
+ CSS directive syntax in CSS-family documents is highlighted by the shared TextMate grammar. Semantic tokens are only added for directive class-list spans such as bare `@compose` preludes and quoted `@safelist` strings.
93
+
94
+ ## Settings
95
+
96
+ ```js
97
+ import { settings } from '@master/css-language-service'
98
+ ```
99
+
100
+ | Setting | Type | Default | Description |
101
+ | --- | --- | --- | --- |
102
+ | `includedLanguages` | `string[]` | Common web and template languages | Language IDs that receive language-service features. |
103
+ | `exclude` | `string[]` | `["**/.git/**", "**/node_modules/**", "**/.hg/**"]` | Glob patterns excluded from all features. |
104
+ | `classAttributes` | `string[]` | `["class", "className"]` | Quoted markup attributes that contain Master CSS class strings. |
105
+ | `classAttributeBindings` | `Record<string, [string, string] \| false>` | Framework binding defaults | Bound attributes such as `:class`, `[ngClass]`, and `class:list`. |
106
+ | `classFunctions` | `string[]` | Common class helpers | Functions and methods whose string arguments contain classes, such as `clsx()` and `classList.add()`. |
107
+ | `classDeclarations` | `string[]` | `[]` | Variable declarations or object properties whose string values contain classes. |
108
+ | `suggestSyntax` | `boolean` | `true` | Enables syntax suggestions. |
109
+ | `inspectSyntax` | `boolean` | `true` | Enables hover inspection and generated CSS previews. |
110
+ | `renderSyntaxColors` | `boolean` | `true` | Enables color rendering. |
111
+ | `editSyntaxColors` | `boolean` | `true` | Enables color editing. |
112
+ | `embeddedSyntaxHighlighting` | `'active' \| 'always' \| 'off'` | `'active'` | Controls embedded semantic token highlighting. |
113
+
114
+ Use `@master/css-language` directly for browser helpers, Shiki integration, TextMate grammar assets, and raw class-position scanning.
package/dist/common.d.ts CHANGED
@@ -1,9 +1 @@
1
- import { type ServerCapabilities } from 'vscode-languageserver-protocol';
2
- export declare const INVOKED_TRIGGER_CHARACTERS: string[];
3
- export declare const VALUE_TRIGGER_CHARACTERS: string[];
4
- export declare const SELECTOR_TRIGGER_CHARACTERS: string[];
5
- export declare const AT_TRIGGER_CHARACTER = "@";
6
- export declare const QUERY_TRIGGER_CHARACTERS: string[];
7
- export declare const GROUP_TRIGGER_CHARACTER = "{";
8
- export declare const DECLARATION_SEPARATOR_TRIGGER_CHARACTER = ";";
9
- export declare const SERVER_CAPABILITIES: ServerCapabilities;
1
+ export { AT_TRIGGER_CHARACTER, DECLARATION_SEPARATOR_TRIGGER_CHARACTER, GROUP_TRIGGER_CHARACTER, INVOKED_TRIGGER_CHARACTERS, QUERY_TRIGGER_CHARACTERS, SELECTOR_TRIGGER_CHARACTERS, VALUE_TRIGGER_CHARACTERS } from '@master/css-language';
package/dist/common.mjs CHANGED
@@ -1,48 +1 @@
1
- import { TextDocumentSyncKind } from 'vscode-languageserver-protocol';
2
- import { QUERY_COMPARISON_OPERATORS, QUERY_LOGICAL_OPERATORS, SEPARATOR_SIGN, DELIMITER_SIGN, SELECTOR_SIGNS, AT_SIGN } from '@master/css';
3
-
4
- const INVOKED_TRIGGER_CHARACTERS = [
5
- '"',
6
- ' ',
7
- '\''
8
- ];
9
- const VALUE_TRIGGER_CHARACTERS = [
10
- SEPARATOR_SIGN,
11
- DELIMITER_SIGN
12
- ];
13
- const SELECTOR_TRIGGER_CHARACTERS = SELECTOR_SIGNS;
14
- const AT_TRIGGER_CHARACTER = AT_SIGN;
15
- const QUERY_TRIGGER_CHARACTERS = [
16
- ...QUERY_COMPARISON_OPERATORS,
17
- ...QUERY_LOGICAL_OPERATORS
18
- ];
19
- const GROUP_TRIGGER_CHARACTER = '{';
20
- const DECLARATION_SEPARATOR_TRIGGER_CHARACTER = ';';
21
- const SERVER_CAPABILITIES = {
22
- textDocumentSync: TextDocumentSyncKind.Incremental,
23
- // Tell the client that this server supports code completion.
24
- completionProvider: {
25
- resolveProvider: false,
26
- workDoneProgress: false,
27
- triggerCharacters: [
28
- ...new Set([
29
- ...INVOKED_TRIGGER_CHARACTERS,
30
- ...VALUE_TRIGGER_CHARACTERS,
31
- ...SELECTOR_TRIGGER_CHARACTERS,
32
- ...QUERY_TRIGGER_CHARACTERS,
33
- DECLARATION_SEPARATOR_TRIGGER_CHARACTER,
34
- AT_TRIGGER_CHARACTER,
35
- GROUP_TRIGGER_CHARACTER
36
- ])
37
- ]
38
- },
39
- colorProvider: true,
40
- hoverProvider: true,
41
- workspace: {
42
- workspaceFolders: {
43
- supported: true
44
- }
45
- }
46
- };
47
-
48
- export { AT_TRIGGER_CHARACTER, DECLARATION_SEPARATOR_TRIGGER_CHARACTER, GROUP_TRIGGER_CHARACTER, INVOKED_TRIGGER_CHARACTERS, QUERY_TRIGGER_CHARACTERS, SELECTOR_TRIGGER_CHARACTERS, SERVER_CAPABILITIES, VALUE_TRIGGER_CHARACTERS };
1
+ export { AT_TRIGGER_CHARACTER, DECLARATION_SEPARATOR_TRIGGER_CHARACTER, GROUP_TRIGGER_CHARACTER, INVOKED_TRIGGER_CHARACTERS, QUERY_TRIGGER_CHARACTERS, SELECTOR_TRIGGER_CHARACTERS, VALUE_TRIGGER_CHARACTERS } from '@master/css-language';
package/dist/core.d.ts CHANGED
@@ -1,29 +1,30 @@
1
- import { MasterCSS } from '@master/css';
1
+ import { MasterCSS, type ClassPosition } from '@master/css-language';
2
2
  import EventEmitter from 'node:events';
3
3
  import type { Position } from 'vscode-languageserver-protocol';
4
4
  import { type Settings } from './settings';
5
5
  import inspectSyntax from './features/inspect-syntax';
6
6
  import renderSyntaxColors from './features/render-syntax-colors';
7
7
  import editSyntaxColors from './features/edit-syntax-colors';
8
+ import renderSemanticTokens, { renderSemanticTokensAtPosition } from './features/render-semantic-tokens';
8
9
  import suggestSyntax from './features/suggest-syntax';
9
10
  import { TextDocument } from 'vscode-languageserver-textdocument';
10
- export interface ClassPosition {
11
- range: {
12
- start: number;
13
- end: number;
14
- };
15
- raw: string;
16
- token: string;
17
- }
11
+ export type { ClassPosition };
18
12
  export default class CSSLanguageService extends EventEmitter {
19
13
  customSettings?: Settings | undefined;
20
14
  css: MasterCSS;
21
15
  settings: Settings;
16
+ private completionIndex?;
17
+ private classPositionCache;
22
18
  constructor(customSettings?: Settings | undefined);
19
+ private getCompletionIndex;
23
20
  inspectSyntax(...params: Parameters<typeof inspectSyntax>): import("vscode-languageserver-protocol").Hover | undefined;
24
21
  renderSyntaxColors(...params: Parameters<typeof renderSyntaxColors>): Promise<import("vscode-languageserver-protocol").ColorInformation[] | undefined> | undefined;
25
22
  editSyntaxColors(...params: Parameters<typeof editSyntaxColors>): import("vscode-languageserver-protocol").ColorPresentation[] | undefined;
26
- suggestSyntax(...params: Parameters<typeof suggestSyntax>): import("vscode-languageserver-protocol").CompletionItem[] | undefined;
23
+ renderSemanticTokens(...params: Parameters<typeof renderSemanticTokens>): import("vscode-languageserver-protocol").SemanticTokens | undefined;
24
+ renderSemanticTokensAtPosition(...params: Parameters<typeof renderSemanticTokensAtPosition>): import("vscode-languageserver-protocol").SemanticTokens | undefined;
25
+ suggestSyntax(document: Parameters<typeof suggestSyntax>[0], position: Parameters<typeof suggestSyntax>[1], context: Parameters<typeof suggestSyntax>[2]): import("vscode-languageserver-protocol").CompletionItem[] | undefined;
26
+ getClassPositions(textDocument: TextDocument): ClassPosition[];
27
27
  getClassPosition(textDocument: TextDocument, position: Position): ClassPosition | undefined;
28
+ getClassContextPositions(textDocument: TextDocument, position: Position): ClassPosition[];
28
29
  isDocumentAccepted(doc: TextDocument): boolean;
29
30
  }
package/dist/core.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { createCSS } from '@master/css';
1
+ import { ClassPositionCache, MasterCSS, matchesLanguageServiceNativeDeclaration, defaultManifest, getClassPositions } from '@master/css-language';
2
2
  import extend from '@techor/extend';
3
3
  import EventEmitter from 'node:events';
4
4
  import settings from './settings.mjs';
@@ -6,18 +6,27 @@ import { minimatch } from 'minimatch';
6
6
  import inspectSyntax from './features/inspect-syntax.mjs';
7
7
  import renderSyntaxColors from './features/render-syntax-colors.mjs';
8
8
  import editSyntaxColors from './features/edit-syntax-colors.mjs';
9
+ import renderSemanticTokens, { renderSemanticTokensAtPosition } from './features/render-semantic-tokens.mjs';
9
10
  import suggestSyntax from './features/suggest-syntax.mjs';
10
- import findMatchingPairs from './utils/find-matching-brackets.mjs';
11
- import escapeRegexp from 'lodash.escaperegexp';
11
+ import { createCompletionIndex } from './utils/completion-index.mjs';
12
12
 
13
13
  class CSSLanguageService extends EventEmitter {
14
14
  customSettings;
15
15
  css;
16
16
  settings;
17
+ completionIndex;
18
+ classPositionCache = new ClassPositionCache();
17
19
  constructor(customSettings){
18
20
  super(), this.customSettings = customSettings;
19
21
  this.settings = extend(settings, customSettings);
20
- this.css = createCSS(this.settings.config);
22
+ this.css = MasterCSS.create({
23
+ manifest: this.settings.manifest || defaultManifest,
24
+ nativeDeclarationMatcher: matchesLanguageServiceNativeDeclaration
25
+ });
26
+ }
27
+ getCompletionIndex() {
28
+ this.completionIndex ??= createCompletionIndex(this.css);
29
+ return this.completionIndex;
21
30
  }
22
31
  inspectSyntax(...params) {
23
32
  if (this.settings.inspectSyntax && this.isDocumentAccepted(params[0])) return inspectSyntax?.call(this, ...params);
@@ -28,207 +37,68 @@ class CSSLanguageService extends EventEmitter {
28
37
  editSyntaxColors(...params) {
29
38
  if (this.settings.editSyntaxColors && this.isDocumentAccepted(params[0])) return editSyntaxColors?.call(this, ...params);
30
39
  }
31
- suggestSyntax(...params) {
32
- if (this.settings.suggestSyntax && this.isDocumentAccepted(params[0])) return suggestSyntax?.call(this, ...params);
40
+ renderSemanticTokens(...params) {
41
+ if (this.isDocumentAccepted(params[0])) return renderSemanticTokens?.call(this, ...params);
33
42
  }
34
- getClassPosition(textDocument, position) {
35
- const positionIndex = textDocument.offsetAt(position);
36
- const startIndex = textDocument.offsetAt({
37
- line: position.line - 100,
38
- character: 0
39
- }) ?? 0;
40
- const endIndex = textDocument.offsetAt({
41
- line: position.line + 100,
42
- character: 0
43
- }) ?? undefined;
44
- const text = textDocument.getText().substring(startIndex, endIndex);
45
- const { classAttributeBindings, classAttributes, classDeclarations, classFunctions } = this.settings;
46
- const normalizeClassNamePosition = (className, attrStart, escapeCharacter)=>{
47
- /**
48
- * Matching classes, including empty string after white space
49
- * @example <div class="class-a class-b "></div>
50
- * @url https://regex101.com/r/LnBLAV/1
51
- */ for (const eachClassMatch of className.matchAll(/(?:[^\s]+)?/g)){
52
- if (eachClassMatch.index === undefined) continue;
53
- const raw = eachClassMatch[0];
54
- const classStartIndex = attrStart + eachClassMatch.index;
55
- const classEndIndex = classStartIndex + raw.length;
56
- if (classStartIndex <= positionIndex - startIndex && positionIndex - startIndex <= classEndIndex) {
57
- return {
58
- range: {
59
- start: classStartIndex,
60
- end: startIndex + classEndIndex
61
- },
62
- raw,
63
- token: raw.replace(new RegExp('\\' + escapeCharacter, 'g'), escapeCharacter)
64
- };
65
- } else if (attrStart === classEndIndex) {
66
- return {
67
- range: {
68
- start: attrStart,
69
- end: attrStart
70
- },
71
- raw: '',
72
- token: ''
73
- };
74
- }
75
- }
76
- };
77
- const resolve = (classMatchers, handle)=>{
78
- for (const eachClassMatcher of classMatchers){
79
- let [eachPattern, start, end] = eachClassMatcher;
80
- end = end ?? start;
81
- for (const eachClassPostioinMatch of text.matchAll(new RegExp(`(?:\\s|\\.)${eachPattern}${escapeRegexp(start)}`, 'g'))){
82
- if (eachClassPostioinMatch.index === undefined) continue;
83
- const eachClassAttributeString = eachClassPostioinMatch[0];
84
- const eachAttrStart = eachClassPostioinMatch.index + eachClassAttributeString.length;
85
- const eachClassPositionEnd = findMatchingPairs(text, eachAttrStart, start, end);
86
- if (!eachClassPositionEnd) continue;
87
- if ((eachAttrStart <= positionIndex - startIndex && eachClassPositionEnd >= positionIndex - startIndex) === true) {
88
- const classPosition = handle(eachAttrStart, eachClassPositionEnd, eachClassMatcher);
89
- if (classPosition) return classPosition;
90
- } else if (eachClassPostioinMatch.index > positionIndex - startIndex) {
91
- break;
92
- }
93
- }
94
- }
95
- };
96
- const stringExpressions = [];
97
- const assignmentExpressions = [];
98
- classAttributes?.forEach((eachClassAttribute)=>{
99
- stringExpressions.push([
100
- eachClassAttribute + '=',
101
- '"',
102
- '"'
103
- ], [
104
- eachClassAttribute + '=',
105
- '\'',
106
- '\''
107
- ]);
43
+ renderSemanticTokensAtPosition(...params) {
44
+ if (this.isDocumentAccepted(params[0])) return renderSemanticTokensAtPosition?.call(this, ...params);
45
+ }
46
+ suggestSyntax(document, position, context) {
47
+ if (this.settings.suggestSyntax && this.isDocumentAccepted(document)) return suggestSyntax?.call(this, document, position, context, this.getCompletionIndex());
48
+ }
49
+ getClassPositions(textDocument) {
50
+ return getClassPositions(textDocument, this.settings, {
51
+ cache: this.classPositionCache
108
52
  });
109
- classDeclarations?.forEach((eachClassDeclaration)=>{
110
- stringExpressions.push([
111
- eachClassDeclaration + '(?:\\s+)?(?::|=)(?:\\s+)?',
112
- '"',
113
- '"'
114
- ], [
115
- eachClassDeclaration + '(?:\\s+)?(?::|=)(?:\\s+)?',
116
- '`',
117
- '`'
118
- ], [
119
- eachClassDeclaration + '(?:\\s+)?(?::|=)(?:\\s+)?',
120
- '\'',
121
- '\''
122
- ]);
123
- assignmentExpressions.push([
124
- eachClassDeclaration + '(?:\\s+)?(?::|=)(?:\\s+)?',
125
- '{',
126
- '}'
127
- ]);
53
+ }
54
+ getClassPosition(textDocument, position) {
55
+ const cachedOxcClassPosition = getClassPositions(textDocument, this.settings, {
56
+ position,
57
+ includeEmpty: true,
58
+ provider: 'oxc',
59
+ oxcMode: 'cache-only',
60
+ cache: this.classPositionCache
61
+ })[0];
62
+ if (cachedOxcClassPosition) return cachedOxcClassPosition;
63
+ const regexClassPosition = getClassPositions(textDocument, this.settings, {
64
+ position,
65
+ includeEmpty: true,
66
+ provider: 'regex'
67
+ })[0];
68
+ if (regexClassPosition && !regexClassPosition.raw.includes('${')) {
69
+ return regexClassPosition;
70
+ }
71
+ return getClassPositions(textDocument, this.settings, {
72
+ position,
73
+ includeEmpty: true,
74
+ provider: 'oxc',
75
+ cache: this.classPositionCache
76
+ })[0] ?? regexClassPosition;
77
+ }
78
+ getClassContextPositions(textDocument, position) {
79
+ const cachedOxcClassPositions = getClassPositions(textDocument, this.settings, {
80
+ position,
81
+ provider: 'oxc',
82
+ oxcMode: 'cache-only',
83
+ positionMatch: 'context',
84
+ cache: this.classPositionCache
128
85
  });
129
- classFunctions?.forEach((eachClassFunction)=>{
130
- /**
131
- * @example clsx`class-a class-b`
132
- */ stringExpressions.push([
133
- eachClassFunction,
134
- '`',
135
- '`'
136
- ]);
137
- /**
138
- * @example clsx('class-a class-b')
139
- */ assignmentExpressions.push([
140
- eachClassFunction,
141
- '(',
142
- ')'
143
- ]);
86
+ if (cachedOxcClassPositions.length) return cachedOxcClassPositions;
87
+ const regexClassPositions = getClassPositions(textDocument, this.settings, {
88
+ position,
89
+ provider: 'regex',
90
+ positionMatch: 'context'
144
91
  });
145
- /**
146
- * @example <div class=""></div>
147
- * */ if (stringExpressions?.length) {
148
- const classPosition = resolve(stringExpressions, (eachAttrStart, eachClassPositionEnd, [eachPattern, pair])=>{
149
- const eachClassName = text.substring(eachAttrStart, eachClassPositionEnd);
150
- /**
151
- * @example <div class=""></div>
152
- */ if (eachClassName === undefined) {
153
- return {
154
- range: {
155
- start: eachAttrStart,
156
- end: eachAttrStart
157
- },
158
- token: '',
159
- raw: ''
160
- };
161
- }
162
- const classNameStart = eachAttrStart;
163
- const classPosition = normalizeClassNamePosition(eachClassName, classNameStart, pair);
164
- if (classPosition) return classPosition;
165
- });
166
- if (classPosition) return classPosition;
167
- }
168
- if (classAttributeBindings) {
169
- for(const eachClassAttribute in classAttributeBindings){
170
- const eachClassAttributeBinding = classAttributeBindings[eachClassAttribute];
171
- if (eachClassAttributeBinding === false) continue;
172
- assignmentExpressions.push([
173
- eachClassAttribute + '=',
174
- eachClassAttributeBinding[0],
175
- eachClassAttributeBinding[1]
176
- ]);
177
- }
178
- }
179
- /**
180
- * attribute binding
181
- * @example <div className={''}></div>
182
- * @example <div :class="isActive ? 'block' : 'hidden'"></div>
183
- * */ if (assignmentExpressions?.length) {
184
- const classPosition = resolve(assignmentExpressions, (eachAttrStart, eachClassPositionEnd, eachClassMatcher)=>{
185
- const eachClassPositionExpression = text.substring(eachAttrStart, eachClassPositionEnd);
186
- /**
187
- * @example <div className={""}></div>
188
- */ if ([
189
- '""',
190
- '\'\'',
191
- '``'
192
- ].includes(eachClassPositionExpression)) {
193
- return {
194
- range: {
195
- start: eachAttrStart + 1,
196
- end: eachAttrStart + 1
197
- },
198
- raw: '',
199
- token: ''
200
- };
201
- }
202
- // 1. "
203
- for (const classExpressionMatch of eachClassPositionExpression.matchAll(/(?<!\\)"([\s\S]*?)(?<!\\)"/g)){
204
- if (classExpressionMatch.index === undefined) continue;
205
- const eachClassName = classExpressionMatch[1];
206
- const classNameStart = eachAttrStart + classExpressionMatch.index + 1 // " length
207
- ;
208
- const classPosition = normalizeClassNamePosition(eachClassName, classNameStart, '"');
209
- if (classPosition) return classPosition;
210
- }
211
- // 2. '
212
- for (const classExpressionMatch of eachClassPositionExpression.matchAll(/(?<!\\)'([\s\S]*?)(?<!\\)'/g)){
213
- if (classExpressionMatch.index === undefined) continue;
214
- const eachClassName = classExpressionMatch[1];
215
- const classNameStart = eachAttrStart + classExpressionMatch.index + 1 // ' length
216
- ;
217
- const classPosition = normalizeClassNamePosition(eachClassName, classNameStart, '\'');
218
- if (classPosition) return classPosition;
219
- }
220
- // 3. `
221
- for (const classExpressionMatch of eachClassPositionExpression.matchAll(/(?<!\\)`([\s\S]*?)(?<!\\)`/g)){
222
- if (classExpressionMatch.index === undefined) continue;
223
- const eachClassName = classExpressionMatch[1];
224
- const classNameStart = eachAttrStart + classExpressionMatch.index + 1 // ` length
225
- ;
226
- const classPosition = normalizeClassNamePosition(eachClassName, classNameStart, '`');
227
- if (classPosition) return classPosition;
228
- }
229
- });
230
- if (classPosition) return classPosition;
92
+ if (regexClassPositions.length && regexClassPositions.every(({ raw })=>!raw.includes('${'))) {
93
+ return regexClassPositions;
231
94
  }
95
+ const oxcClassPositions = getClassPositions(textDocument, this.settings, {
96
+ position,
97
+ provider: 'oxc',
98
+ positionMatch: 'context',
99
+ cache: this.classPositionCache
100
+ });
101
+ return oxcClassPositions.length ? oxcClassPositions : regexClassPositions;
232
102
  }
233
103
  isDocumentAccepted(doc) {
234
104
  if (!this.settings.exclude) return true;
@@ -1,7 +1,5 @@
1
- import { getCSSDataDocumentation } from '../utils/get-css-data-documentation.mjs';
2
- import { generateCSS, SyntaxRuleType, isCoreRule } from '@master/css';
3
- import cssDataProvider from '../utils/css-data-provider.mjs';
4
- import getUtilityInfo from '../utils/get-utility-info.mjs';
1
+ import createCSSMarkdownDocumentation from '../utils/create-css-markdown-documentation.mjs';
2
+ import { UtilityType, generateCSS } from '@master/css-language';
5
3
 
6
4
  function inspectSyntax(document, position) {
7
5
  const classPosition = this.getClassPosition(document, position);
@@ -11,34 +9,27 @@ function inspectSyntax(document, position) {
11
9
  start: document.positionAt(classPosition.range.start),
12
10
  end: document.positionAt(classPosition.range.end)
13
11
  };
14
- const componentClasses = this.css.components.get(token);
15
- if (componentClasses) {
16
- const documentation = getCSSDataDocumentation({}, {
17
- generatedCSS: generateCSS([
18
- token
19
- ], this.css),
20
- docs: '/guide/components'
21
- });
12
+ const utilities = this.css.generate(token);
13
+ const component = utilities.find((utility)=>utility.type === UtilityType.Semantic && utility.layerName === 'components');
14
+ if (component) {
15
+ const documentation = createCSSMarkdownDocumentation(generateCSS([
16
+ token
17
+ ], this.css));
22
18
  if (documentation) {
23
19
  return {
24
20
  contents: {
25
21
  kind: documentation.kind,
26
- value: `(style) ` + componentClasses.join(' ') + '\n' + documentation.value
22
+ value: `(components) ` + documentation.value
27
23
  }
28
24
  };
29
25
  }
30
26
  } else {
31
- const rules = this.css.generate(token);
32
- const rule = rules[0];
33
- if (rule) {
34
- if (rule.type === SyntaxRuleType.Utility) {
35
- const { data, docs } = getUtilityInfo(rule.registeredSyntax, this.css);
36
- const documentation = getCSSDataDocumentation(data, {
37
- generatedCSS: generateCSS([
38
- token
39
- ], this.css),
40
- docs
41
- });
27
+ const utility = utilities[0];
28
+ if (utility) {
29
+ if (utility.type === UtilityType.Semantic) {
30
+ const documentation = createCSSMarkdownDocumentation(generateCSS([
31
+ token
32
+ ], this.css));
42
33
  if (documentation) {
43
34
  return {
44
35
  contents: documentation,
@@ -46,14 +37,9 @@ function inspectSyntax(document, position) {
46
37
  };
47
38
  }
48
39
  } else {
49
- const nativeProperties = cssDataProvider.provideProperties();
50
- const nativeCSSPropertyData = nativeProperties.find(({ name })=>name === rule.id);
51
- const documentation = getCSSDataDocumentation(nativeCSSPropertyData, {
52
- generatedCSS: generateCSS([
53
- token
54
- ], this.css),
55
- docs: '/reference/' + (isCoreRule(rule.id) && rule.id)
56
- });
40
+ const documentation = createCSSMarkdownDocumentation(generateCSS([
41
+ token
42
+ ], this.css));
57
43
  if (documentation) {
58
44
  return {
59
45
  contents: documentation,
@@ -0,0 +1,5 @@
1
+ import type CSSLanguageService from '../core';
2
+ import type { TextDocument } from 'vscode-languageserver-textdocument';
3
+ import type { SemanticTokens } from 'vscode-languageserver-protocol';
4
+ export declare function renderSemanticTokensAtPosition(this: CSSLanguageService, document: TextDocument, position: Parameters<CSSLanguageService['getClassPosition']>[1]): SemanticTokens | undefined;
5
+ export default function renderSemanticTokens(this: CSSLanguageService, document: TextDocument): SemanticTokens | undefined;
@@ -0,0 +1,10 @@
1
+ import { renderSemanticTokens as renderSemanticTokens$1, renderSemanticTokensAtPosition as renderSemanticTokensAtPosition$1 } from '@master/css-language';
2
+
3
+ function renderSemanticTokensAtPosition(document, position) {
4
+ return renderSemanticTokensAtPosition$1(this.css, document, this.getClassContextPositions(document, position), position, this.settings);
5
+ }
6
+ function renderSemanticTokens(document) {
7
+ return renderSemanticTokens$1(this.css, document, this.getClassPositions(document), this.settings);
8
+ }
9
+
10
+ export { renderSemanticTokens as default, renderSemanticTokensAtPosition };
@@ -1,7 +1,19 @@
1
1
  import { instancePattern } from '../utils/regex.mjs';
2
2
  import Color from 'colorjs.io';
3
- import { SyntaxRuleType } from '@master/css';
3
+ import { UtilityType } from '@master/css-language';
4
4
 
5
+ function resolveVariableColorValue(variable, variables) {
6
+ let current = variable;
7
+ for(let i = 0; i < 8; i++){
8
+ const value = current?.value;
9
+ if (value === undefined) return;
10
+ const text = String(value);
11
+ const alias = /^\$([a-zA-Z0-9-]+)(?: ?\/ ?.+?)?$|^var\(--([_a-zA-Z0-9-]+)\)$/.exec(text);
12
+ const aliasName = alias?.[1] ?? alias?.[2];
13
+ if (!aliasName) return text;
14
+ current = variables.get(aliasName);
15
+ }
16
+ }
5
17
  async function renderSyntaxColors(document) {
6
18
  const text = document.getText() ?? '';
7
19
  const colorInformations = [];
@@ -11,13 +23,13 @@ async function renderSyntaxColors(document) {
11
23
  const instanceStartIndex = instanceMatch.index;
12
24
  const syntax = instanceMatch[0];
13
25
  const rule = this.css.generate(syntax)[0];
14
- if (rule && rule.type !== SyntaxRuleType.Utility) {
26
+ if (rule && rule.type !== UtilityType.Semantic) {
15
27
  const keyTokenLength = rule.keyToken?.length ?? 0;
16
28
  let currentLength = 0;
17
29
  const resolveValueComponent = (valueComponent)=>{
18
30
  if (valueComponent.text === undefined) return; // text is always existing, just for type
19
31
  const startOffset = instanceStartIndex + keyTokenLength + currentLength;
20
- // TODO: check number mt:30
32
+ // TODO: check number mt:1.875rem
21
33
  const valueComponentTokenLength = valueComponent.token.length;
22
34
  let color;
23
35
  switch(valueComponent.type){
@@ -41,8 +53,11 @@ async function renderSyntaxColors(document) {
41
53
  }
42
54
  break;
43
55
  case 'variable':
44
- if (valueComponent.variable?.type === 'color') {
45
- color = new Color(valueComponent.text);
56
+ if (valueComponent.variable?.namespace?.startsWith('color') && valueComponent.variable.value !== undefined) {
57
+ color = new Color(resolveVariableColorValue(valueComponent.variable, this.css.variables) || String(valueComponent.variable.value));
58
+ if (valueComponent.alpha !== undefined) {
59
+ color.alpha *= valueComponent.alpha;
60
+ }
46
61
  }
47
62
  break;
48
63
  case 'string':
@@ -60,9 +75,9 @@ async function renderSyntaxColors(document) {
60
75
  end: document.positionAt(startOffset + valueComponentTokenLength)
61
76
  },
62
77
  color: {
63
- red: rgbaColor.r,
64
- green: rgbaColor.g,
65
- blue: rgbaColor.b,
78
+ red: rgbaColor.r ?? 0,
79
+ green: rgbaColor.g ?? 0,
80
+ blue: rgbaColor.b ?? 0,
66
81
  alpha: Number(rgbaColor.alpha)
67
82
  }
68
83
  });
@@ -1,4 +1,5 @@
1
1
  import type { CompletionItem, CompletionParams } from 'vscode-languageserver-protocol';
2
2
  import type { TextDocument } from 'vscode-languageserver-textdocument';
3
3
  import CSSLanguageService from '../core';
4
- export default function suggestSyntax(this: CSSLanguageService, document: TextDocument, position: CompletionParams['position'], context: CompletionParams['context']): CompletionItem[] | undefined;
4
+ import type { CompletionIndex } from '../utils/completion-index';
5
+ export default function suggestSyntax(this: CSSLanguageService, document: TextDocument, position: CompletionParams['position'], context: CompletionParams['context'], completionIndex?: CompletionIndex): CompletionItem[] | undefined;