@master/css-language-service 2.0.0-rc.73 → 2.0.0-rc.75

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 (79) hide show
  1. package/LICENSE +21 -0
  2. package/dist/common.d.ts +2 -1
  3. package/dist/common.js +2 -0
  4. package/dist/core.d.ts +35 -32
  5. package/dist/core.js +107 -0
  6. package/dist/features/edit-syntax-colors.d.ts +8 -4
  7. package/dist/features/edit-syntax-colors.js +16 -0
  8. package/dist/features/inspect-syntax.d.ts +8 -4
  9. package/dist/features/inspect-syntax.js +37 -0
  10. package/dist/features/render-semantic-tokens.d.ts +9 -5
  11. package/dist/features/render-semantic-tokens.js +10 -0
  12. package/dist/features/render-syntax-colors.d.ts +8 -4
  13. package/dist/features/render-syntax-colors.js +93 -0
  14. package/dist/features/suggest-syntax.d.ts +9 -5
  15. package/dist/features/suggest-syntax.js +11 -0
  16. package/dist/index.d.ts +8 -7
  17. package/dist/index.js +8 -0
  18. package/dist/settings.d.ts +14 -11
  19. package/dist/settings.js +58 -0
  20. package/dist/utils/beautify-css.d.ts +4 -1
  21. package/dist/utils/beautify-css.js +10 -0
  22. package/dist/utils/completion-index.d.ts +37 -34
  23. package/dist/utils/completion-index.js +196 -0
  24. package/dist/utils/convert-color-token.d.ts +8 -4
  25. package/dist/utils/convert-color-token.js +47 -0
  26. package/dist/utils/create-css-markdown-documentation.d.ts +7 -4
  27. package/dist/utils/create-css-markdown-documentation.js +13 -0
  28. package/dist/utils/create-doc.d.ts +19 -15
  29. package/dist/utils/create-doc.js +21 -0
  30. package/dist/utils/get-class-completion-items.d.ts +8 -4
  31. package/dist/utils/get-class-completion-items.js +15 -0
  32. package/dist/utils/get-color-completion-items.d.ts +7 -3
  33. package/dist/utils/get-color-completion-items.js +22 -0
  34. package/dist/utils/get-position.d.ts +4 -1
  35. package/dist/utils/get-position.js +9 -0
  36. package/dist/utils/get-pseudo-class-completion-items.d.ts +8 -4
  37. package/dist/utils/get-pseudo-class-completion-items.js +64 -0
  38. package/dist/utils/get-pseudo-element-completion-items.d.ts +8 -4
  39. package/dist/utils/get-pseudo-element-completion-items.js +39 -0
  40. package/dist/utils/get-query-completion-items.d.ts +8 -4
  41. package/dist/utils/get-query-completion-items.js +62 -0
  42. package/dist/utils/get-range.d.ts +6 -2
  43. package/dist/utils/get-range.js +21 -0
  44. package/dist/utils/get-utility-info.d.ts +7 -3
  45. package/dist/utils/get-utility-info.js +16 -0
  46. package/dist/utils/get-value-completion-items.d.ts +8 -4
  47. package/dist/utils/get-value-completion-items.js +161 -0
  48. package/dist/utils/query-syntax-completions.d.ts +8 -4
  49. package/dist/utils/query-syntax-completions.js +78 -0
  50. package/dist/utils/regex.d.ts +4 -1
  51. package/dist/utils/{regex.mjs → regex.js} +2 -1
  52. package/dist/utils/sort-completion-items.d.ts +6 -2
  53. package/dist/utils/sort-completion-items.js +10 -0
  54. package/package.json +61 -1
  55. package/dist/common.mjs +0 -1
  56. package/dist/core.mjs +0 -116
  57. package/dist/features/edit-syntax-colors.mjs +0 -17
  58. package/dist/features/inspect-syntax.mjs +0 -54
  59. package/dist/features/render-semantic-tokens.mjs +0 -10
  60. package/dist/features/render-syntax-colors.mjs +0 -101
  61. package/dist/features/suggest-syntax.mjs +0 -18
  62. package/dist/index.mjs +0 -7
  63. package/dist/settings.mjs +0 -53
  64. package/dist/utils/beautify-css.mjs +0 -11
  65. package/dist/utils/completion-index.mjs +0 -269
  66. package/dist/utils/convert-color-token.mjs +0 -55
  67. package/dist/utils/create-css-markdown-documentation.mjs +0 -15
  68. package/dist/utils/create-doc.mjs +0 -21
  69. package/dist/utils/get-class-completion-items.mjs +0 -19
  70. package/dist/utils/get-color-completion-items.mjs +0 -24
  71. package/dist/utils/get-position.mjs +0 -10
  72. package/dist/utils/get-pseudo-class-completion-items.mjs +0 -68
  73. package/dist/utils/get-pseudo-element-completion-items.mjs +0 -43
  74. package/dist/utils/get-query-completion-items.mjs +0 -77
  75. package/dist/utils/get-range.mjs +0 -21
  76. package/dist/utils/get-utility-info.mjs +0 -19
  77. package/dist/utils/get-value-completion-items.mjs +0 -202
  78. package/dist/utils/query-syntax-completions.mjs +0 -94
  79. package/dist/utils/sort-completion-items.mjs +0 -11
@@ -0,0 +1,62 @@
1
+ import createCSSMarkdownDocumentation from "./create-css-markdown-documentation.js";
2
+ import sortCompletionItems from "./sort-completion-items.js";
3
+ import { createCompletionIndex } from "./completion-index.js";
4
+ import { AT_SIGN, QUERY_COMPARISON_OPERATORS, QUERY_LOGICAL_OPERATORS, createDefaultCSS, generateAt, generateCSS, getSingleAtNumberRuleNode, parseAt } from "@master/css-language";
5
+ import { CompletionItemKind } from "vscode-languageserver-protocol";
6
+ //#region src/utils/get-query-completion-items.ts
7
+ function getQueryCompletionItems(css = createDefaultCSS(), triggerCharacter = AT_SIGN, syntax, completionIndex = createCompletionIndex(css)) {
8
+ const completionItems = [];
9
+ if (!QUERY_COMPARISON_OPERATORS.includes(triggerCharacter)) [
10
+ "media",
11
+ "container",
12
+ "supports"
13
+ ].forEach((name) => {
14
+ completionItems.push({
15
+ label: triggerCharacter + name + "()",
16
+ filterText: name + "()",
17
+ insertText: name + "()",
18
+ sortText: name + "()",
19
+ documentation: createCSSMarkdownDocumentation(generateCSS([syntax + name + "()"], css, completionIndex.runtime))
20
+ });
21
+ });
22
+ const handleNumberNodes = (atRule, token) => {
23
+ const numberNode = getSingleAtNumberRuleNode(atRule.nodes);
24
+ if (numberNode) {
25
+ const prevComparisonCharacter = syntax.charAt(syntax.length - 2);
26
+ const comparisonCharacter = prevComparisonCharacter === ">" || prevComparisonCharacter === "<" ? prevComparisonCharacter + triggerCharacter : triggerCharacter;
27
+ const text = generateAt(parseAt(comparisonCharacter + token, css));
28
+ completionItems.push({
29
+ label: comparisonCharacter + token,
30
+ sortText: CompletionItemKind.Keyword + comparisonCharacter + String(Math.round(numberNode.value)).padStart(12, "0"),
31
+ filterText: token,
32
+ insertText: token,
33
+ detail: text,
34
+ kind: CompletionItemKind.Keyword,
35
+ documentation: createCSSMarkdownDocumentation(generateCSS([syntax + token], css, completionIndex.runtime))
36
+ });
37
+ return true;
38
+ }
39
+ };
40
+ for (const [token, atRule] of completionIndex.atRules) {
41
+ const completionItem = {
42
+ filterText: token,
43
+ insertText: token,
44
+ documentation: createCSSMarkdownDocumentation(generateCSS([syntax + token], css, completionIndex.runtime))
45
+ };
46
+ if ([AT_SIGN, ...QUERY_LOGICAL_OPERATORS].includes(triggerCharacter)) {
47
+ if (handleNumberNodes(atRule, token)) continue;
48
+ const text = generateAt(parseAt(triggerCharacter + token, css));
49
+ completionItems.push({
50
+ ...completionItem,
51
+ label: triggerCharacter + token,
52
+ detail: text,
53
+ sortText: CompletionItemKind.Keyword + token,
54
+ kind: CompletionItemKind.Keyword,
55
+ documentation: createCSSMarkdownDocumentation(generateCSS([syntax + token], css, completionIndex.runtime))
56
+ });
57
+ } else if (QUERY_COMPARISON_OPERATORS.includes(triggerCharacter)) handleNumberNodes(atRule, token);
58
+ }
59
+ return sortCompletionItems(completionItems);
60
+ }
61
+ //#endregion
62
+ export { getQueryCompletionItems as default };
@@ -1,2 +1,6 @@
1
- import { TextDocument, Range } from 'vscode-languageserver-textdocument';
2
- export default function getRange(searchString: string, doc: TextDocument): Range;
1
+ import { Range, TextDocument } from "vscode-languageserver-textdocument";
2
+
3
+ //#region src/utils/get-range.d.ts
4
+ declare function getRange(searchString: string, doc: TextDocument): Range;
5
+ //#endregion
6
+ export { getRange as default };
@@ -0,0 +1,21 @@
1
+ //#region src/utils/get-range.ts
2
+ function getRange(searchString, doc) {
3
+ const text = doc.getText();
4
+ const regex = new RegExp(`(\\b(?:className|class)\\s*=\\s*(['"\`]))(.*?)\\2`, "gs");
5
+ let match;
6
+ while ((match = regex.exec(text)) !== null) {
7
+ match[0];
8
+ const attrStartIndex = match.index + match[1].length;
9
+ const relativeIndex = match[3].indexOf(searchString);
10
+ if (relativeIndex !== -1) {
11
+ const absoluteIndex = attrStartIndex + relativeIndex;
12
+ return {
13
+ start: doc.positionAt(absoluteIndex),
14
+ end: doc.positionAt(absoluteIndex + searchString.length)
15
+ };
16
+ }
17
+ }
18
+ throw new Error(`Cannot find "${searchString}" in any class attribute.`);
19
+ }
20
+ //#endregion
21
+ export { getRange as default };
@@ -1,4 +1,8 @@
1
- import { type CompiledUtility } from '@master/css-language';
2
- export default function getUtilityInfo(utility: CompiledUtility): {
3
- detail: string | undefined;
1
+ import { CompiledUtility } from "@master/css-language";
2
+
3
+ //#region src/utils/get-utility-info.d.ts
4
+ declare function getUtilityInfo(utility: CompiledUtility): {
5
+ detail: string | undefined;
4
6
  };
7
+ //#endregion
8
+ export { getUtilityInfo as default };
@@ -0,0 +1,16 @@
1
+ import { getStaticUtilityDeclarations } from "@master/css-language";
2
+ //#region src/utils/get-utility-info.ts
3
+ function getUtilityInfo(utility) {
4
+ const declarations = getStaticUtilityDeclarations(utility);
5
+ const propsLength = Object.keys(declarations || {}).length;
6
+ const propName = Object.keys(declarations || {})[0];
7
+ const propValue = declarations?.[propName];
8
+ let detail;
9
+ /**
10
+ * Remaps to native CSS properties when only one property is declared
11
+ * */
12
+ if (propsLength === 1) detail = `${propName}: ${propValue}`;
13
+ return { detail };
14
+ }
15
+ //#endregion
16
+ export { getUtilityInfo as default };
@@ -1,4 +1,8 @@
1
- import { type CompletionItem } from 'vscode-languageserver-protocol';
2
- import { MasterCSS } from '@master/css-language';
3
- import { type CompletionIndex } from './completion-index';
4
- export default function getValueCompletionItems(css: MasterCSS | undefined, ruleKey: string, valuePrefix?: string, completionIndex?: CompletionIndex): CompletionItem[];
1
+ import { MasterCSS } from "@master/css-language";
2
+ import { CompletionItem } from "vscode-languageserver-protocol";
3
+ import { CompletionIndex } from "./completion-index.js";
4
+
5
+ //#region src/utils/get-value-completion-items.d.ts
6
+ declare function getValueCompletionItems(css: MasterCSS | undefined, ruleKey: string, valuePrefix?: string, completionIndex?: CompletionIndex): CompletionItem[];
7
+ //#endregion
8
+ export { getValueCompletionItems as default };
@@ -0,0 +1,161 @@
1
+ import createCSSMarkdownDocumentation from "./create-css-markdown-documentation.js";
2
+ import sortCompletionItems from "./sort-completion-items.js";
3
+ import { createCompletionIndex } from "./completion-index.js";
4
+ import { createDefaultCSS, generateCSS, getMdnPropertySyntax, getMdnPropertyValueNames } from "@master/css-language";
5
+ import { CompletionItemKind } from "vscode-languageserver-protocol";
6
+ //#region src/utils/get-value-completion-items.ts
7
+ const SCOPED_VARIABLE_PRIORITY = "aaaa";
8
+ const NATIVE_PRIORITY = "ccccc";
9
+ const GLOBAL_VARIABLE_PRIORITY = "zzzz";
10
+ const ANIMATION_REFERENCE_PROPERTIES = /* @__PURE__ */ new Set(["animation", "animation-name"]);
11
+ function getNumericSortValue(variable, rootSize) {
12
+ if (variable.numeric) switch (variable.numeric.unit) {
13
+ case "rem": return variable.numeric.value * rootSize;
14
+ case void 0:
15
+ case "":
16
+ case "px": return variable.numeric.value;
17
+ }
18
+ return typeof variable.value === "number" ? variable.value : 0;
19
+ }
20
+ function getVariableKeyByNamespace(variableName, namespace) {
21
+ const negative = variableName.startsWith("-");
22
+ const positiveName = negative ? variableName.slice(1) : variableName;
23
+ if (positiveName !== namespace && !positiveName.startsWith(namespace + "-")) return;
24
+ const key = positiveName === namespace ? "" : positiveName.slice(namespace.length + 1);
25
+ return negative ? "-" + key : key;
26
+ }
27
+ function getValueCompletionItems(css = createDefaultCSS(), ruleKey, valuePrefix = "", completionIndex = createCompletionIndex(css)) {
28
+ const completionItems = [];
29
+ const addedLabels = /* @__PURE__ */ new Set();
30
+ const canonicalRuleKey = completionIndex.keyAliases[ruleKey] || ruleKey;
31
+ const nativeKey = completionIndex.nativeUtilityKeys.get(canonicalRuleKey) || (getMdnPropertySyntax(canonicalRuleKey) ? canonicalRuleKey : void 0);
32
+ const nativePropertyValues = getMdnPropertyValueNames(nativeKey);
33
+ const pushCompletionItem = (completionItem) => {
34
+ completionItems.push(completionItem);
35
+ addedLabels.add(completionItem.label);
36
+ };
37
+ const generateVariableCompletionItem = (variable, { appliedValue, label, negative, scoped } = {}) => {
38
+ const nativePropertySyntax = getMdnPropertySyntax(variable.namespace) || getMdnPropertySyntax(nativeKey);
39
+ if (variable.type === "number" && variable.name.startsWith("-") && nativePropertySyntax?.includes("absolute")) return;
40
+ const valueToken = appliedValue ?? (scoped ? variable.key : variable.name);
41
+ const documentation = createCSSMarkdownDocumentation(generateCSS([ruleKey + ":" + valueToken], css, completionIndex.runtime));
42
+ const completionItemLabel = label ?? variable.name;
43
+ const conflicted = addedLabels.has(completionItemLabel);
44
+ const completionItem = {
45
+ label: conflicted ? "var(--" + completionItemLabel + ")" : completionItemLabel,
46
+ kind: CompletionItemKind.Value
47
+ };
48
+ if (conflicted) completionItem.documentation = createCSSMarkdownDocumentation(generateCSS([ruleKey + ":" + completionItem.label], css, completionIndex.runtime));
49
+ else completionItem.documentation = createCSSMarkdownDocumentation(generateCSS([ruleKey + ":" + valueToken], css, completionIndex.runtime));
50
+ if (variable.namespace?.startsWith("color")) if (Object.keys(variable.modes || {}).length) {
51
+ completionItem.kind = CompletionItemKind.Color;
52
+ completionItem.detail = variable.name;
53
+ } else {
54
+ const configKey = "variables." + (variable.namespace ? variable.namespace + "." + variable.key : variable.name);
55
+ const valueToken = variable.value ?? variable.name;
56
+ completionItem.detail = String(configKey);
57
+ completionItem.documentation = {
58
+ kind: "markdown",
59
+ value: valueToken + "\n\n" + documentation?.value
60
+ };
61
+ completionItem.kind = CompletionItemKind.Color;
62
+ completionItem.sortText = "color-" + variable.name.replace(/(.+?)-(\d+)/, (match, prefix, num) => prefix + num.padStart(10, "0"));
63
+ }
64
+ else if (variable.type === "number") {
65
+ completionItem.detail = String(variable.name);
66
+ const value = getNumericSortValue(variable, css.settings.rootSize);
67
+ const sortValue = negative ? -Math.abs(value) : value;
68
+ completionItem.sortText = (variable.namespace || "") + (sortValue >= 0 ? String(sortValue).padStart(10, "0") : "-" + String(Math.abs(sortValue)).padStart(10, "0"));
69
+ } else completionItem.detail = String(variable.value || variable.name);
70
+ return completionItem;
71
+ };
72
+ const addScopedVariableCompletionItem = (variable, variableName) => {
73
+ if (addedLabels.has(variableName)) return;
74
+ const completionItem = generateVariableCompletionItem(variable, { scoped: true });
75
+ if (completionItem) {
76
+ completionItem.label = variableName;
77
+ completionItem.sortText = SCOPED_VARIABLE_PRIORITY + (completionItem.sortText || variableName);
78
+ completionItem.detail = "(scope) " + completionItem.detail;
79
+ pushCompletionItem(completionItem);
80
+ }
81
+ const negativeVariableName = "-" + variableName;
82
+ if (valuePrefix.startsWith("-") && variableName[0] !== "-" && variable.type === "number" && !addedLabels.has(negativeVariableName)) {
83
+ const negativeCompletionItem = generateVariableCompletionItem(variable, {
84
+ appliedValue: negativeVariableName,
85
+ label: negativeVariableName,
86
+ negative: true,
87
+ scoped: true
88
+ });
89
+ if (negativeCompletionItem) {
90
+ negativeCompletionItem.sortText = SCOPED_VARIABLE_PRIORITY + (negativeCompletionItem.sortText || negativeVariableName);
91
+ negativeCompletionItem.detail = "(scope) " + negativeCompletionItem.detail;
92
+ pushCompletionItem(negativeCompletionItem);
93
+ }
94
+ }
95
+ };
96
+ /**
97
+ * Scoped variables
98
+ * @example box: + content -> box-sizing:content
99
+ */
100
+ for (const { name, variable } of completionIndex.scopedVariablesByKey.get(canonicalRuleKey) || []) addScopedVariableCompletionItem(variable, name);
101
+ for (const { value } of completionIndex.patternValuesByKey.get(canonicalRuleKey) || []) {
102
+ if (addedLabels.has(value)) continue;
103
+ pushCompletionItem({
104
+ label: value,
105
+ kind: CompletionItemKind.Value,
106
+ documentation: createCSSMarkdownDocumentation(generateCSS([ruleKey + ":" + value], css, completionIndex.runtime)),
107
+ detail: canonicalRuleKey + ": " + value
108
+ });
109
+ }
110
+ /**
111
+ * @example animation:fade|fast animate:fade animation-name:fade
112
+ */
113
+ for (const animationUtility of completionIndex.animationUtilitiesByKey.get(canonicalRuleKey) || []) css.animations.forEach((_, animationName) => {
114
+ pushCompletionItem({
115
+ label: animationName,
116
+ kind: CompletionItemKind.Value,
117
+ documentation: createCSSMarkdownDocumentation(generateCSS([ruleKey + ":" + animationName], css, completionIndex.runtime)),
118
+ detail: animationUtility.detailPrefix ? animationUtility.detailPrefix + ": " + animationName : animationName
119
+ });
120
+ });
121
+ if (ANIMATION_REFERENCE_PROPERTIES.has(canonicalRuleKey)) css.animations.forEach((_, animationName) => {
122
+ if (addedLabels.has(animationName)) return;
123
+ pushCompletionItem({
124
+ label: animationName,
125
+ kind: CompletionItemKind.Value,
126
+ documentation: createCSSMarkdownDocumentation(generateCSS([ruleKey + ":" + animationName], css, completionIndex.runtime)),
127
+ detail: canonicalRuleKey + ": " + animationName
128
+ });
129
+ });
130
+ const usedKeys = /* @__PURE__ */ new Set();
131
+ for (const variableNamespace of completionIndex.nativeVariableNamespacesByProperty.get(canonicalRuleKey) || []) for (const variable of completionIndex.variables) {
132
+ const variableName = getVariableKeyByNamespace(variable.name, variableNamespace);
133
+ if (variableName === void 0 || usedKeys.has(variableName)) continue;
134
+ usedKeys.add(variableName);
135
+ addScopedVariableCompletionItem(variable, variableName);
136
+ }
137
+ /**
138
+ * Native values
139
+ */
140
+ if (nativeKey) nativePropertyValues.forEach((value) => {
141
+ if (addedLabels.has(value) || nativeKey === "font" && typeof +value === "number" || value.includes(" ")) return;
142
+ pushCompletionItem({
143
+ label: value,
144
+ kind: CompletionItemKind.Value,
145
+ sortText: NATIVE_PRIORITY + (value.startsWith("-") ? "zz" + value.slice(1) : value),
146
+ documentation: createCSSMarkdownDocumentation(generateCSS([ruleKey + ":" + value], css, completionIndex.runtime)),
147
+ detail: nativeKey + ": " + value
148
+ });
149
+ });
150
+ completionIndex.variables.forEach((variable) => {
151
+ const completionItem = generateVariableCompletionItem(variable);
152
+ if (completionItem) {
153
+ completionItem.sortText = GLOBAL_VARIABLE_PRIORITY + (completionItem.sortText || completionItem.label);
154
+ completionItem.detail = "(global) " + completionItem.detail;
155
+ pushCompletionItem(completionItem);
156
+ }
157
+ });
158
+ return sortCompletionItems(completionItems);
159
+ }
160
+ //#endregion
161
+ export { getValueCompletionItems as default };
@@ -1,4 +1,8 @@
1
- import { type CompletionItem } from 'vscode-languageserver-protocol';
2
- import { MasterCSS } from '@master/css-language';
3
- import { type CompletionIndex } from './completion-index';
4
- export default function querySyntaxCompletions(q?: string, css?: MasterCSS, completionIndex?: CompletionIndex): CompletionItem[] | undefined;
1
+ import { MasterCSS } from "@master/css-language";
2
+ import { CompletionItem } from "vscode-languageserver-protocol";
3
+ import { CompletionIndex } from "./completion-index.js";
4
+
5
+ //#region src/utils/query-syntax-completions.d.ts
6
+ declare function querySyntaxCompletions(q?: string, css?: MasterCSS, completionIndex?: CompletionIndex): CompletionItem[] | undefined;
7
+ //#endregion
8
+ export { querySyntaxCompletions as default };
@@ -0,0 +1,78 @@
1
+ import { GROUP_TRIGGER_CHARACTER, SELECTOR_TRIGGER_CHARACTERS } from "../common.js";
2
+ import createCSSMarkdownDocumentation from "./create-css-markdown-documentation.js";
3
+ import { createCompletionIndex, matchesIndexedName } from "./completion-index.js";
4
+ import getPseudoClassCompletionItems from "./get-pseudo-class-completion-items.js";
5
+ import getPseudoElementCompletionItems from "./get-pseudo-element-completion-items.js";
6
+ import getClassCompletionItems from "./get-class-completion-items.js";
7
+ import getValueCompletionItems from "./get-value-completion-items.js";
8
+ import getQueryCompletionItems from "./get-query-completion-items.js";
9
+ import { AT_SIGN, QUERY_COMPARISON_OPERATORS, QUERY_LOGICAL_OPERATORS, SELECTOR_SIGNS, createDefaultCSS, generateCSS } from "@master/css-language";
10
+ import { CompletionItemKind } from "vscode-languageserver-protocol";
11
+ //#region src/utils/query-syntax-completions.ts
12
+ function querySyntaxCompletions(q = "", css = createDefaultCSS(), completionIndex = createCompletionIndex(css)) {
13
+ const fields = q.split(" ");
14
+ let field = fields[fields.length - 1];
15
+ const triggerCharacter = q.charAt(q.length - 1);
16
+ if (triggerCharacter === " " || field.length === 0 || field === GROUP_TRIGGER_CHARACTER || triggerCharacter === ";") return getClassCompletionItems(css, completionIndex);
17
+ const isGroup = field.startsWith(GROUP_TRIGGER_CHARACTER);
18
+ if (isGroup) {
19
+ const declarationSeparatorMatches = Array.from(field.matchAll(/;(?=(?:[^'"]|'[^']*'|"[^"]*")*$)/g));
20
+ if (declarationSeparatorMatches.length) field = field.slice(declarationSeparatorMatches[declarationSeparatorMatches.length - 1].index + 1);
21
+ else field = field.slice(1);
22
+ }
23
+ if (field.startsWith(AT_SIGN) || field.startsWith("~")) return [];
24
+ const keyMatch = field.match(/^[^'":\s]+:/);
25
+ const atMatches = Array.from(field.matchAll(/@(?=(?:[^'"]|'[^']*'|"[^"]*")*$)/g));
26
+ const valueSeparatorMatch = field.match(/\|(?=(?:[^'"]|'[^']*'|"[^"]*")*$)/g);
27
+ const atInvoked = atMatches.findIndex(({ index }) => index !== 0) !== -1;
28
+ const firstColonIndex = keyMatch ? keyMatch[0].length - 1 : -1;
29
+ const selectorInvokedRegex = new RegExp(`[${SELECTOR_SIGNS.join("")}](?=(?:[^'"]|'[^']*'|"[^"]*")*$)`);
30
+ const key = keyMatch ? keyMatch[0].slice(0, firstColonIndex) : void 0;
31
+ const isStyle = completionIndex.componentNames.some((eachStyleName) => matchesIndexedName(field, eachStyleName));
32
+ const isUtility = completionIndex.utilityNames.some((eachUtilityName) => matchesIndexedName(field, eachUtilityName));
33
+ if (!isStyle && !isUtility) {
34
+ if (key === void 0 && !valueSeparatorMatch) return getClassCompletionItems(css, completionIndex);
35
+ if (!atInvoked && !selectorInvokedRegex.test(field.slice(firstColonIndex + 1))) {
36
+ if (key && firstColonIndex !== -1) return getValueCompletionItems(css, key, field.slice(firstColonIndex + 1), completionIndex);
37
+ }
38
+ }
39
+ /**
40
+ * Not support conditional btn:hover and group
41
+ */
42
+ if (isStyle || isGroup) return;
43
+ if (atInvoked && [
44
+ AT_SIGN,
45
+ ...QUERY_COMPARISON_OPERATORS,
46
+ ...QUERY_LOGICAL_OPERATORS
47
+ ].includes(triggerCharacter)) return getQueryCompletionItems(css, triggerCharacter, field, completionIndex);
48
+ if (!atInvoked && SELECTOR_TRIGGER_CHARACTERS.includes(triggerCharacter)) {
49
+ const pseudoElementCompletionItems = getPseudoElementCompletionItems(css, field, completionIndex);
50
+ if (field.endsWith("::")) {
51
+ /**
52
+ * Consider trigger characters and fix insertText.
53
+ * @incorrect class="text-center::" -> class="text-center::::after"
54
+ * @correct class="text-center::" -> class="text-center::after"
55
+ */
56
+ pseudoElementCompletionItems.forEach((completionItem) => completionItem.insertText = completionItem.label.slice(2));
57
+ return pseudoElementCompletionItems;
58
+ } else {
59
+ const completionItems = getPseudoClassCompletionItems(css, field, completionIndex);
60
+ completionItems.push(...pseudoElementCompletionItems);
61
+ /**
62
+ * Consider trigger characters and fix insertText.
63
+ * @incorrect class="text-center:" -> class="text-center::active"
64
+ * @correct class="text-center:" -> class="text-center:active"
65
+ */
66
+ if (triggerCharacter === ":") completionItems.forEach((completionItem) => completionItem.insertText = completionItem.label.slice(1));
67
+ else completionItems.push({
68
+ label: ".<class>",
69
+ insertText: ".",
70
+ documentation: createCSSMarkdownDocumentation(generateCSS([field + ".class"], css, completionIndex.runtime)),
71
+ kind: CompletionItemKind.Class
72
+ });
73
+ return completionItems;
74
+ }
75
+ }
76
+ }
77
+ //#endregion
78
+ export { querySyntaxCompletions as default };
@@ -1 +1,4 @@
1
- export declare const instancePattern: RegExp;
1
+ //#region src/utils/regex.d.ts
2
+ declare const instancePattern: RegExp;
3
+ //#endregion
4
+ export { instancePattern };
@@ -1,3 +1,4 @@
1
+ //#region src/utils/regex.ts
1
2
  const instancePattern = /[^\s:]+:\w*\(?((?<!\\)["'`])((?:\\\1|(?:(?!\1))\S)*)((?<!\\)\1)\)?|[^\s"'`]+/g;
2
-
3
+ //#endregion
3
4
  export { instancePattern };
@@ -1,2 +1,6 @@
1
- import type { CompletionItem } from 'vscode-languageserver-protocol';
2
- export default function sortCompletionItems(items: CompletionItem[]): CompletionItem[];
1
+ import { CompletionItem } from "vscode-languageserver-protocol";
2
+
3
+ //#region src/utils/sort-completion-items.d.ts
4
+ declare function sortCompletionItems(items: CompletionItem[]): CompletionItem[];
5
+ //#endregion
6
+ export { sortCompletionItems as default };
@@ -0,0 +1,10 @@
1
+ //#region src/utils/sort-completion-items.ts
2
+ function sortCompletionItems(items) {
3
+ return items.sort((a, b) => {
4
+ const sortTextA = a.sortText || a.label;
5
+ const sortTextB = b.sortText || b.label;
6
+ return sortTextA.localeCompare(sortTextB, void 0, { numeric: true });
7
+ });
8
+ }
9
+ //#endregion
10
+ export { sortCompletionItems as default };
package/package.json CHANGED
@@ -1 +1,61 @@
1
- {"name":"@master/css-language-service","type":"module","scripts":{"build":"techor build \"src/**/*.ts\" --formats esm","dev":"pnpm build --watch","type-check":"tsc --noEmit","lint":"eslint","test":"vitest"},"description":"Master CSS Language Service","author":"Aoyue Design LLC.","license":"MIT","repository":{"type":"git","url":"https://github.com/master-co/css.git","directory":"packages/language-service"},"dependencies":{"@master/css":"^2.0.0-rc.73","@master/css-engine":"^2.0.0-rc.73","@master/css-language":"^2.0.0-rc.73","@techor/extend":"^3.2.5","colorjs.io":"^0.6.1","js-beautify":"^1.15.4","minimatch":"^10.2.5","vscode-languageserver-protocol":"^3.18.0","vscode-languageserver-textdocument":"^1.0.12"},"devDependencies":{"@types/vscode":"^1.125.0","vscode-languageserver":"^10.0.0","@master/css-schema":"^2.0.0-rc.73"},"sideEffects":false,"main":"./dist/index.mjs","module":"./dist/index.mjs","jsnext:main":"./dist/index.mjs","esnext":"./dist/index.mjs","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","default":"./dist/index.mjs"},"./common":{"types":"./dist/common.d.ts","import":"./dist/common.mjs","default":"./dist/common.mjs"}},"files":["dist"],"publishConfig":{"access":"public","provenance":true},"version":"2.0.0-rc.73"}
1
+ {
2
+ "name": "@master/css-language-service",
3
+ "type": "module",
4
+ "description": "Master CSS Language Service",
5
+ "author": "Aoyue Design LLC.",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/master-co/css.git",
10
+ "directory": "packages/language-service"
11
+ },
12
+ "dependencies": {
13
+ "colorjs.io": "^0.6.1",
14
+ "defu": "^6.1.7",
15
+ "js-beautify": "^1.15.4",
16
+ "minimatch": "^10.2.5",
17
+ "vscode-languageserver-protocol": "^3.18.0",
18
+ "vscode-languageserver-textdocument": "^1.0.12",
19
+ "@master/css": "^2.0.0-rc.75",
20
+ "@master/css-engine": "^2.0.0-rc.75",
21
+ "@master/css-language": "^2.0.0-rc.75"
22
+ },
23
+ "devDependencies": {
24
+ "@types/vscode": "^1.125.0",
25
+ "vscode-languageserver": "^10.0.0",
26
+ "@master/css-schema": "^2.0.0-rc.75"
27
+ },
28
+ "sideEffects": false,
29
+ "main": "./dist/index.js",
30
+ "module": "./dist/index.js",
31
+ "jsnext:main": "./dist/index.js",
32
+ "esnext": "./dist/index.js",
33
+ "types": "./dist/index.d.ts",
34
+ "exports": {
35
+ ".": {
36
+ "types": "./dist/index.d.ts",
37
+ "import": "./dist/index.js",
38
+ "default": "./dist/index.js"
39
+ },
40
+ "./common": {
41
+ "types": "./dist/common.d.ts",
42
+ "import": "./dist/common.js",
43
+ "default": "./dist/common.js"
44
+ }
45
+ },
46
+ "files": [
47
+ "dist"
48
+ ],
49
+ "publishConfig": {
50
+ "access": "public",
51
+ "provenance": true
52
+ },
53
+ "version": "2.0.0-rc.75",
54
+ "scripts": {
55
+ "build": "tsdown --config ../../shared/tsdown-package.config.ts",
56
+ "dev": "tsdown --config ../../shared/tsdown-package.config.ts --watch",
57
+ "type-check": "tsc -b tsconfig.typecheck.json",
58
+ "lint": "eslint",
59
+ "test": "vitest"
60
+ }
61
+ }
package/dist/common.mjs DELETED
@@ -1 +0,0 @@
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.mjs DELETED
@@ -1,116 +0,0 @@
1
- import { ClassPositionCache, defaultCSSLanguageRuntime, getClassPositions } from '@master/css-language';
2
- import extend from '@techor/extend';
3
- import EventEmitter from 'node:events';
4
- import settings from './settings.mjs';
5
- import { minimatch } from 'minimatch';
6
- import inspectSyntax from './features/inspect-syntax.mjs';
7
- import renderSyntaxColors from './features/render-syntax-colors.mjs';
8
- import editSyntaxColors from './features/edit-syntax-colors.mjs';
9
- import renderSemanticTokens, { renderSemanticTokensAtPosition } from './features/render-semantic-tokens.mjs';
10
- import suggestSyntax from './features/suggest-syntax.mjs';
11
- import { createCompletionIndex } from './utils/completion-index.mjs';
12
-
13
- class CSSLanguageService extends EventEmitter {
14
- customSettings;
15
- css;
16
- runtime;
17
- settings;
18
- completionIndex;
19
- classPositionCache = new ClassPositionCache();
20
- constructor(customSettings, options = {}){
21
- super(), this.customSettings = customSettings;
22
- this.runtime = options.runtime ?? defaultCSSLanguageRuntime;
23
- this.settings = extend(settings, customSettings);
24
- this.css = this.runtime.MasterCSS.create({
25
- manifest: this.settings.manifest || this.runtime.defaultManifest,
26
- nativeDeclarationMatcher: this.runtime.nativeDeclarationMatcher
27
- });
28
- }
29
- getCompletionIndex() {
30
- this.completionIndex ??= createCompletionIndex(this.css, this.runtime);
31
- return this.completionIndex;
32
- }
33
- inspectSyntax(...params) {
34
- if (this.settings.inspectSyntax && this.isDocumentAccepted(params[0])) return inspectSyntax?.call(this, ...params);
35
- }
36
- renderSyntaxColors(...params) {
37
- if (this.settings.renderSyntaxColors && this.isDocumentAccepted(params[0])) return renderSyntaxColors?.call(this, ...params);
38
- }
39
- editSyntaxColors(...params) {
40
- if (this.settings.editSyntaxColors && this.isDocumentAccepted(params[0])) return editSyntaxColors?.call(this, ...params);
41
- }
42
- renderSemanticTokens(...params) {
43
- if (this.isDocumentAccepted(params[0])) return renderSemanticTokens?.call(this, ...params);
44
- }
45
- renderSemanticTokensAtPosition(...params) {
46
- if (this.isDocumentAccepted(params[0])) return renderSemanticTokensAtPosition?.call(this, ...params);
47
- }
48
- suggestSyntax(document, position, context) {
49
- if (this.settings.suggestSyntax && this.isDocumentAccepted(document)) return suggestSyntax?.call(this, document, position, context, this.getCompletionIndex());
50
- }
51
- getClassPositions(textDocument) {
52
- return getClassPositions(textDocument, this.settings, {
53
- cache: this.classPositionCache
54
- });
55
- }
56
- getClassPosition(textDocument, position) {
57
- const cachedOxcClassPosition = getClassPositions(textDocument, this.settings, {
58
- position,
59
- includeEmpty: true,
60
- provider: 'oxc',
61
- oxcMode: 'cache-only',
62
- cache: this.classPositionCache
63
- })[0];
64
- if (cachedOxcClassPosition) return cachedOxcClassPosition;
65
- const regexClassPosition = getClassPositions(textDocument, this.settings, {
66
- position,
67
- includeEmpty: true,
68
- provider: 'regex'
69
- })[0];
70
- if (regexClassPosition && !regexClassPosition.raw.includes('${')) {
71
- return regexClassPosition;
72
- }
73
- return getClassPositions(textDocument, this.settings, {
74
- position,
75
- includeEmpty: true,
76
- provider: 'oxc',
77
- cache: this.classPositionCache
78
- })[0] ?? regexClassPosition;
79
- }
80
- getClassContextPositions(textDocument, position) {
81
- const cachedOxcClassPositions = getClassPositions(textDocument, this.settings, {
82
- position,
83
- provider: 'oxc',
84
- oxcMode: 'cache-only',
85
- positionMatch: 'context',
86
- cache: this.classPositionCache
87
- });
88
- if (cachedOxcClassPositions.length) return cachedOxcClassPositions;
89
- const regexClassPositions = getClassPositions(textDocument, this.settings, {
90
- position,
91
- provider: 'regex',
92
- positionMatch: 'context'
93
- });
94
- if (regexClassPositions.length && regexClassPositions.every(({ raw })=>!raw.includes('${'))) {
95
- return regexClassPositions;
96
- }
97
- const oxcClassPositions = getClassPositions(textDocument, this.settings, {
98
- position,
99
- provider: 'oxc',
100
- positionMatch: 'context',
101
- cache: this.classPositionCache
102
- });
103
- return oxcClassPositions.length ? oxcClassPositions : regexClassPositions;
104
- }
105
- isDocumentAccepted(doc) {
106
- if (!this.settings.exclude) return true;
107
- for (const exclude of this.settings.exclude){
108
- if (minimatch(doc.uri, exclude)) {
109
- return false;
110
- }
111
- }
112
- return true;
113
- }
114
- }
115
-
116
- export { CSSLanguageService as default };
@@ -1,17 +0,0 @@
1
- import convertColorByToken from '../utils/convert-color-token.mjs';
2
-
3
- function editSyntaxColors(document, color, range) {
4
- const colorPresentations = [];
5
- const selectedColorToken = document.getText(range);
6
- const targetColorToken = convertColorByToken(color, selectedColorToken, this.css);
7
- if (targetColorToken) colorPresentations.push({
8
- label: targetColorToken,
9
- textEdit: {
10
- range,
11
- newText: targetColorToken
12
- }
13
- });
14
- return colorPresentations;
15
- }
16
-
17
- export { editSyntaxColors as default };