@master/css-language-service 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 +15 -0
- package/dist/core.d.ts +2 -0
- package/dist/core.js +4 -0
- package/dist/features/format-directives.d.ts +7 -0
- package/dist/features/format-directives.js +72 -0
- package/dist/settings.d.ts +1 -0
- package/dist/settings.js +1 -0
- package/dist/utils/get-value-completion-items.js +8 -4
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -91,6 +91,20 @@ const semanticTokens = languageService.renderSemanticTokensAtPosition(textDocume
|
|
|
91
91
|
|
|
92
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
93
|
|
|
94
|
+
### Directive formatting
|
|
95
|
+
|
|
96
|
+
```ts
|
|
97
|
+
const edits = languageService.formatDirectives(textDocument)
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Directive formatting returns LSP text edits for Master CSS directive source. It supports CSS, SCSS, and LESS documents, plus CSS-family `<style>` blocks in Vue, Svelte, and Astro documents. The formatter repairs directive class-list important markers such as `bg:transparent !` to `bg:transparent!` and does not change compiler semantics or generated CSS.
|
|
101
|
+
|
|
102
|
+
Pass an LSP range as the second argument for range formatting:
|
|
103
|
+
|
|
104
|
+
```ts
|
|
105
|
+
const edits = languageService.formatDirectives(textDocument, range)
|
|
106
|
+
```
|
|
107
|
+
|
|
94
108
|
## Settings
|
|
95
109
|
|
|
96
110
|
```js
|
|
@@ -109,6 +123,7 @@ import { settings } from '@master/css-language-service'
|
|
|
109
123
|
| `inspectSyntax` | `boolean` | `true` | Enables hover inspection and generated CSS previews. |
|
|
110
124
|
| `renderSyntaxColors` | `boolean` | `true` | Enables color rendering. |
|
|
111
125
|
| `editSyntaxColors` | `boolean` | `true` | Enables color editing. |
|
|
126
|
+
| `formatDirectives` | `boolean` | `true` | Enables Master CSS directive formatting. |
|
|
112
127
|
| `embeddedSyntaxHighlighting` | `'active' \| 'always' \| 'off'` | `'active'` | Controls embedded semantic token highlighting. |
|
|
113
128
|
|
|
114
129
|
Use `@master/css-language` directly for browser helpers, Shiki integration, TextMate grammar assets, and raw class-position scanning.
|
package/dist/core.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { Settings } from "./settings.js";
|
|
|
6
6
|
import inspectSyntax from "./features/inspect-syntax.js";
|
|
7
7
|
import renderSyntaxColors from "./features/render-syntax-colors.js";
|
|
8
8
|
import editSyntaxColors from "./features/edit-syntax-colors.js";
|
|
9
|
+
import formatDirectives from "./features/format-directives.js";
|
|
9
10
|
import renderSemanticTokens$1, { renderSemanticTokensAtPosition as renderSemanticTokensAtPosition$1 } from "./features/render-semantic-tokens.js";
|
|
10
11
|
import suggestSyntax from "./features/suggest-syntax.js";
|
|
11
12
|
|
|
@@ -25,6 +26,7 @@ declare class CSSLanguageService extends EventEmitter {
|
|
|
25
26
|
inspectSyntax(...params: Parameters<typeof inspectSyntax>): import("vscode-languageserver-protocol").Hover | undefined;
|
|
26
27
|
renderSyntaxColors(...params: Parameters<typeof renderSyntaxColors>): Promise<import("vscode-languageserver-protocol").ColorInformation[] | undefined> | undefined;
|
|
27
28
|
editSyntaxColors(...params: Parameters<typeof editSyntaxColors>): import("vscode-languageserver-protocol").ColorPresentation[] | undefined;
|
|
29
|
+
formatDirectives(...params: Parameters<typeof formatDirectives>): import("vscode-languageserver-protocol").TextEdit[] | undefined;
|
|
28
30
|
renderSemanticTokens(...params: Parameters<typeof renderSemanticTokens$1>): import("vscode-languageserver-protocol").SemanticTokens | undefined;
|
|
29
31
|
renderSemanticTokensAtPosition(...params: Parameters<typeof renderSemanticTokensAtPosition$1>): import("vscode-languageserver-protocol").SemanticTokens | undefined;
|
|
30
32
|
suggestSyntax(document: Parameters<typeof suggestSyntax>[0], position: Parameters<typeof suggestSyntax>[1], context: Parameters<typeof suggestSyntax>[2]): import("vscode-languageserver-protocol").CompletionItem[] | undefined;
|
package/dist/core.js
CHANGED
|
@@ -2,6 +2,7 @@ import settings from "./settings.js";
|
|
|
2
2
|
import inspectSyntax from "./features/inspect-syntax.js";
|
|
3
3
|
import renderSyntaxColors from "./features/render-syntax-colors.js";
|
|
4
4
|
import editSyntaxColors from "./features/edit-syntax-colors.js";
|
|
5
|
+
import formatDirectives from "./features/format-directives.js";
|
|
5
6
|
import renderSemanticTokens$1, { renderSemanticTokensAtPosition as renderSemanticTokensAtPosition$1 } from "./features/render-semantic-tokens.js";
|
|
6
7
|
import { createCompletionIndex } from "./utils/completion-index.js";
|
|
7
8
|
import suggestSyntax from "./features/suggest-syntax.js";
|
|
@@ -40,6 +41,9 @@ var CSSLanguageService = class extends EventEmitter {
|
|
|
40
41
|
editSyntaxColors(...params) {
|
|
41
42
|
if (this.settings.editSyntaxColors && this.isDocumentAccepted(params[0])) return editSyntaxColors?.call(this, ...params);
|
|
42
43
|
}
|
|
44
|
+
formatDirectives(...params) {
|
|
45
|
+
if (this.settings.formatDirectives && this.isDocumentAccepted(params[0])) return formatDirectives?.call(this, ...params);
|
|
46
|
+
}
|
|
43
47
|
renderSemanticTokens(...params) {
|
|
44
48
|
if (this.isDocumentAccepted(params[0])) return renderSemanticTokens$1?.call(this, ...params);
|
|
45
49
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Range, TextEdit } from "vscode-languageserver-protocol";
|
|
2
|
+
import { TextDocument } from "vscode-languageserver-textdocument";
|
|
3
|
+
|
|
4
|
+
//#region src/features/format-directives.d.ts
|
|
5
|
+
declare function formatDirectives(document: TextDocument, range?: Range): TextEdit[] | undefined;
|
|
6
|
+
//#endregion
|
|
7
|
+
export { formatDirectives as default };
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { formatMasterCSSDirectives } from "@master/css-language";
|
|
2
|
+
//#region src/features/format-directives.ts
|
|
3
|
+
const CSS_FORMAT_LANGUAGE_IDS = /* @__PURE__ */ new Set([
|
|
4
|
+
"css",
|
|
5
|
+
"scss",
|
|
6
|
+
"less"
|
|
7
|
+
]);
|
|
8
|
+
const SFC_FORMAT_LANGUAGE_IDS = /* @__PURE__ */ new Set([
|
|
9
|
+
"vue",
|
|
10
|
+
"svelte",
|
|
11
|
+
"astro"
|
|
12
|
+
]);
|
|
13
|
+
const STYLE_BLOCK_RE = /<style\b([^>]*)>([\s\S]*?)<\/style>/gi;
|
|
14
|
+
function getSFCStyleLanguage(attributes) {
|
|
15
|
+
const match = /\blang\s*=\s*(?:"([^"]+)"|'([^']+)'|([^\s>]+))/i.exec(attributes);
|
|
16
|
+
return (match?.[1] || match?.[2] || match?.[3] || "css").toLowerCase();
|
|
17
|
+
}
|
|
18
|
+
function toOffsetRange(document, range) {
|
|
19
|
+
if (!range) return;
|
|
20
|
+
return {
|
|
21
|
+
start: document.offsetAt(range.start),
|
|
22
|
+
end: document.offsetAt(range.end)
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
function intersects(a, b) {
|
|
26
|
+
return a.start < b.end && b.start < a.end;
|
|
27
|
+
}
|
|
28
|
+
function toTextEdit(document, edit) {
|
|
29
|
+
return {
|
|
30
|
+
range: {
|
|
31
|
+
start: document.positionAt(edit.start),
|
|
32
|
+
end: document.positionAt(edit.end)
|
|
33
|
+
},
|
|
34
|
+
newText: edit.newText
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
function collectCSSDocumentFormatEdits(document, range) {
|
|
38
|
+
return formatMasterCSSDirectives(document.getText(), { range: toOffsetRange(document, range) }).map(toTextEdit.bind(void 0, document));
|
|
39
|
+
}
|
|
40
|
+
function collectSFCFormatEdits(document, range) {
|
|
41
|
+
const source = document.getText();
|
|
42
|
+
const offsetRange = toOffsetRange(document, range);
|
|
43
|
+
const edits = [];
|
|
44
|
+
STYLE_BLOCK_RE.lastIndex = 0;
|
|
45
|
+
for (const match of source.matchAll(STYLE_BLOCK_RE)) {
|
|
46
|
+
const styleLanguage = getSFCStyleLanguage(match[1]);
|
|
47
|
+
if (!CSS_FORMAT_LANGUAGE_IDS.has(styleLanguage)) continue;
|
|
48
|
+
const styleText = match[2];
|
|
49
|
+
const styleStart = (match.index || 0) + match[0].indexOf(styleText);
|
|
50
|
+
const styleEnd = styleStart + styleText.length;
|
|
51
|
+
if (offsetRange && !intersects(offsetRange, {
|
|
52
|
+
start: styleStart,
|
|
53
|
+
end: styleEnd
|
|
54
|
+
})) continue;
|
|
55
|
+
const relativeRange = offsetRange ? {
|
|
56
|
+
start: Math.max(offsetRange.start, styleStart) - styleStart,
|
|
57
|
+
end: Math.min(offsetRange.end, styleEnd) - styleStart
|
|
58
|
+
} : void 0;
|
|
59
|
+
for (const edit of formatMasterCSSDirectives(styleText, { range: relativeRange })) edits.push({
|
|
60
|
+
start: styleStart + edit.start,
|
|
61
|
+
end: styleStart + edit.end,
|
|
62
|
+
newText: edit.newText
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
return edits.map(toTextEdit.bind(void 0, document));
|
|
66
|
+
}
|
|
67
|
+
function formatDirectives(document, range) {
|
|
68
|
+
if (CSS_FORMAT_LANGUAGE_IDS.has(document.languageId)) return collectCSSDocumentFormatEdits(document, range);
|
|
69
|
+
if (SFC_FORMAT_LANGUAGE_IDS.has(document.languageId)) return collectSFCFormatEdits(document, range);
|
|
70
|
+
}
|
|
71
|
+
//#endregion
|
|
72
|
+
export { formatDirectives as default };
|
package/dist/settings.d.ts
CHANGED
package/dist/settings.js
CHANGED
|
@@ -24,10 +24,14 @@ function getVariableKeyByNamespace(variableName, namespace) {
|
|
|
24
24
|
const key = positiveName === namespace ? "" : positiveName.slice(namespace.length + 1);
|
|
25
25
|
return negative ? "-" + key : key;
|
|
26
26
|
}
|
|
27
|
+
function uniqueKeys(...keys) {
|
|
28
|
+
return [...new Set(keys)];
|
|
29
|
+
}
|
|
27
30
|
function getValueCompletionItems(css = createDefaultCSS(), ruleKey, valuePrefix = "", completionIndex = createCompletionIndex(css)) {
|
|
28
31
|
const completionItems = [];
|
|
29
32
|
const addedLabels = /* @__PURE__ */ new Set();
|
|
30
33
|
const canonicalRuleKey = completionIndex.keyAliases[ruleKey] || ruleKey;
|
|
34
|
+
const valueLookupKeys = uniqueKeys(ruleKey, canonicalRuleKey);
|
|
31
35
|
const nativeKey = completionIndex.nativeUtilityKeys.get(canonicalRuleKey) || (getMdnPropertySyntax(canonicalRuleKey) ? canonicalRuleKey : void 0);
|
|
32
36
|
const nativePropertyValues = getMdnPropertyValueNames(nativeKey);
|
|
33
37
|
const pushCompletionItem = (completionItem) => {
|
|
@@ -97,14 +101,14 @@ function getValueCompletionItems(css = createDefaultCSS(), ruleKey, valuePrefix
|
|
|
97
101
|
* Scoped variables
|
|
98
102
|
* @example box: + content -> box-sizing:content
|
|
99
103
|
*/
|
|
100
|
-
for (const { name, variable } of completionIndex.scopedVariablesByKey.get(
|
|
101
|
-
for (const { value } of completionIndex.patternValuesByKey.get(
|
|
104
|
+
for (const valueLookupKey of valueLookupKeys) for (const { name, variable } of completionIndex.scopedVariablesByKey.get(valueLookupKey) || []) addScopedVariableCompletionItem(variable, name);
|
|
105
|
+
for (const valueLookupKey of valueLookupKeys) for (const { value } of completionIndex.patternValuesByKey.get(valueLookupKey) || []) {
|
|
102
106
|
if (addedLabels.has(value)) continue;
|
|
103
107
|
pushCompletionItem({
|
|
104
108
|
label: value,
|
|
105
109
|
kind: CompletionItemKind.Value,
|
|
106
110
|
documentation: createCSSMarkdownDocumentation(generateCSS([ruleKey + ":" + value], css, completionIndex.runtime)),
|
|
107
|
-
detail:
|
|
111
|
+
detail: valueLookupKey + ": " + value
|
|
108
112
|
});
|
|
109
113
|
}
|
|
110
114
|
/**
|
|
@@ -128,7 +132,7 @@ function getValueCompletionItems(css = createDefaultCSS(), ruleKey, valuePrefix
|
|
|
128
132
|
});
|
|
129
133
|
});
|
|
130
134
|
const usedKeys = /* @__PURE__ */ new Set();
|
|
131
|
-
for (const variableNamespace of completionIndex.nativeVariableNamespacesByProperty.get(
|
|
135
|
+
for (const valueLookupKey of valueLookupKeys) for (const variableNamespace of completionIndex.nativeVariableNamespacesByProperty.get(valueLookupKey) || []) for (const variable of completionIndex.variables) {
|
|
132
136
|
const variableName = getVariableKeyByNamespace(variable.name, variableNamespace);
|
|
133
137
|
if (variableName === void 0 || usedKeys.has(variableName)) continue;
|
|
134
138
|
usedKeys.add(variableName);
|
package/package.json
CHANGED
|
@@ -16,14 +16,14 @@
|
|
|
16
16
|
"minimatch": "^10.2.5",
|
|
17
17
|
"vscode-languageserver-protocol": "^3.18.0",
|
|
18
18
|
"vscode-languageserver-textdocument": "^1.0.12",
|
|
19
|
-
"@master/css": "^2.0.0-rc.
|
|
20
|
-
"@master/css-engine": "^2.0.0-rc.
|
|
21
|
-
"@master/css-language": "^2.0.0-rc.
|
|
19
|
+
"@master/css": "^2.0.0-rc.76",
|
|
20
|
+
"@master/css-engine": "^2.0.0-rc.76",
|
|
21
|
+
"@master/css-language": "^2.0.0-rc.76"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
24
|
"@types/vscode": "^1.125.0",
|
|
25
25
|
"vscode-languageserver": "^10.0.0",
|
|
26
|
-
"@master/css-schema": "^2.0.0-rc.
|
|
26
|
+
"@master/css-schema": "^2.0.0-rc.76"
|
|
27
27
|
},
|
|
28
28
|
"sideEffects": false,
|
|
29
29
|
"main": "./dist/index.js",
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"access": "public",
|
|
51
51
|
"provenance": true
|
|
52
52
|
},
|
|
53
|
-
"version": "2.0.0-rc.
|
|
53
|
+
"version": "2.0.0-rc.76",
|
|
54
54
|
"scripts": {
|
|
55
55
|
"build": "tsdown --config ../../shared/tsdown-package.config.ts",
|
|
56
56
|
"dev": "tsdown --config ../../shared/tsdown-package.config.ts --watch",
|