@master/css-language-service 2.0.0-rc.26 → 2.0.0-rc.27
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/dist/common.cjs +57 -0
- package/dist/common.d.ts +9 -0
- package/dist/common.mjs +48 -0
- package/dist/core.cjs +188 -0
- package/dist/core.d.ts +30 -0
- package/dist/core.mjs +186 -0
- package/dist/features/edit-syntax-colors.cjs +19 -0
- package/dist/features/edit-syntax-colors.d.ts +4 -0
- package/dist/features/edit-syntax-colors.mjs +17 -0
- package/dist/features/inspect-syntax.cjs +70 -0
- package/dist/features/inspect-syntax.d.ts +4 -0
- package/dist/features/inspect-syntax.mjs +68 -0
- package/dist/features/render-syntax-colors.cjs +88 -0
- package/dist/features/render-syntax-colors.d.ts +4 -0
- package/dist/features/render-syntax-colors.mjs +86 -0
- package/dist/features/suggest-syntax.cjs +20 -0
- package/dist/features/suggest-syntax.d.ts +4 -0
- package/dist/features/suggest-syntax.mjs +18 -0
- package/dist/index.cjs +23 -15
- package/dist/index.d.ts +7 -5
- package/dist/index.mjs +7 -5
- package/dist/settings.cjs +130 -29
- package/dist/settings.d.ts +16 -8
- package/dist/settings.mjs +130 -29
- package/dist/utils/beautify-css.cjs +13 -0
- package/dist/utils/beautify-css.d.ts +1 -0
- package/dist/utils/beautify-css.mjs +11 -0
- package/dist/utils/convert-color-space.cjs +40 -0
- package/dist/utils/convert-color-space.d.ts +2 -0
- package/dist/utils/convert-color-space.mjs +38 -0
- package/dist/utils/convert-color-token.cjs +28 -0
- package/dist/utils/convert-color-token.d.ts +3 -0
- package/dist/utils/convert-color-token.mjs +26 -0
- package/dist/utils/create-doc.cjs +26 -0
- package/dist/utils/create-doc.d.ts +16 -0
- package/dist/utils/create-doc.mjs +21 -0
- package/dist/utils/css-data-provider.cjs +9 -0
- package/dist/utils/css-data-provider.d.ts +3 -0
- package/dist/utils/css-data-provider.mjs +7 -0
- package/dist/utils/find-matching-brackets.cjs +29 -0
- package/dist/utils/find-matching-brackets.d.ts +1 -0
- package/dist/utils/find-matching-brackets.mjs +27 -0
- package/dist/utils/get-color-completion-items.cjs +30 -0
- package/dist/utils/get-color-completion-items.d.ts +3 -0
- package/dist/utils/get-color-completion-items.mjs +28 -0
- package/dist/utils/get-css-data-documentation.cjs +90 -0
- package/dist/utils/get-css-data-documentation.d.ts +5 -0
- package/dist/utils/get-css-data-documentation.mjs +88 -0
- package/dist/utils/get-main-completion-items.cjs +105 -0
- package/dist/utils/get-main-completion-items.d.ts +3 -0
- package/dist/utils/get-main-completion-items.mjs +103 -0
- package/dist/utils/get-position.cjs +12 -0
- package/dist/utils/get-position.d.ts +1 -0
- package/dist/utils/get-position.mjs +10 -0
- package/dist/utils/get-pseudo-class-completion-items.cjs +65 -0
- package/dist/utils/get-pseudo-class-completion-items.d.ts +3 -0
- package/dist/utils/get-pseudo-class-completion-items.mjs +63 -0
- package/dist/utils/get-pseudo-element-completion-items.cjs +56 -0
- package/dist/utils/get-pseudo-element-completion-items.d.ts +3 -0
- package/dist/utils/get-pseudo-element-completion-items.mjs +54 -0
- package/dist/utils/get-query-completion-items.cjs +102 -0
- package/dist/utils/get-query-completion-items.d.ts +3 -0
- package/dist/utils/get-query-completion-items.mjs +100 -0
- package/dist/utils/get-range.cjs +21 -0
- package/dist/utils/get-range.d.ts +2 -0
- package/dist/utils/get-range.mjs +19 -0
- package/dist/utils/get-utility-info.cjs +32 -0
- package/dist/utils/get-utility-info.d.ts +7 -0
- package/dist/utils/get-utility-info.mjs +30 -0
- package/dist/utils/get-value-completion-items.cjs +185 -0
- package/dist/utils/get-value-completion-items.d.ts +3 -0
- package/dist/utils/get-value-completion-items.mjs +183 -0
- package/dist/utils/query-syntax-completions.cjs +115 -0
- package/dist/utils/query-syntax-completions.d.ts +3 -0
- package/dist/utils/query-syntax-completions.mjs +113 -0
- package/dist/utils/regex.cjs +1 -1
- package/dist/utils/regex.mjs +1 -1
- package/dist/utils/sort-completion-items.cjs +11 -0
- package/dist/utils/sort-completion-items.d.ts +2 -0
- package/dist/utils/sort-completion-items.mjs +9 -0
- package/package.json +1 -1
- package/dist/constant.cjs +0 -3064
- package/dist/constant.d.ts +0 -33
- package/dist/constant.mjs +0 -3055
- package/dist/position-check.cjs +0 -37
- package/dist/position-check.d.ts +0 -7
- package/dist/position-check.mjs +0 -35
- package/dist/providers/color.cjs +0 -319
- package/dist/providers/color.d.ts +0 -5
- package/dist/providers/color.mjs +0 -315
- package/dist/providers/completion.cjs +0 -309
- package/dist/providers/completion.d.ts +0 -14
- package/dist/providers/completion.mjs +0 -303
- package/dist/providers/hover.cjs +0 -65
- package/dist/providers/hover.d.ts +0 -2
- package/dist/providers/hover.mjs +0 -63
- package/dist/utils/get-css-entry-markdown-description.cjs +0 -77
- package/dist/utils/get-css-entry-markdown-description.d.ts +0 -1
- package/dist/utils/get-css-entry-markdown-description.mjs +0 -75
- package/dist/utils/hex-to-rgb.cjs +0 -13
- package/dist/utils/hex-to-rgb.d.ts +0 -2
- package/dist/utils/hex-to-rgb.mjs +0 -11
- package/dist/utils/oklab-to-rgb.cjs +0 -33
- package/dist/utils/oklab-to-rgb.d.ts +0 -1
- package/dist/utils/oklab-to-rgb.mjs +0 -31
- package/dist/utils/oklch-to-oklab.cjs +0 -11
- package/dist/utils/oklch-to-oklab.d.ts +0 -1
- package/dist/utils/oklch-to-oklab.mjs +0 -9
- package/dist/utils/to-two-digit-hex.cjs +0 -8
- package/dist/utils/to-two-digit-hex.d.ts +0 -1
- package/dist/utils/to-two-digit-hex.mjs +0 -6
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { CompletionItemKind } from 'vscode-languageserver-protocol';
|
|
2
|
+
import cssDataProvider from './css-data-provider.mjs';
|
|
3
|
+
import { Layer, generateCSS, isCoreRule, MasterCSS } from '@master/css';
|
|
4
|
+
import { getCSSDataDocumentation } from './get-css-data-documentation.mjs';
|
|
5
|
+
import sortCompletionItems from './sort-completion-items.mjs';
|
|
6
|
+
|
|
7
|
+
const SCOPED_VARIABLE_PRIORITY = 'aaaa';
|
|
8
|
+
const AMBIGUOUS_PRIORITY = 'bbbb';
|
|
9
|
+
const NATIVE_PRIORITY = 'ccccc';
|
|
10
|
+
const GLOBAL_VARIABLE_PRIORITY = 'zzzz';
|
|
11
|
+
function getValueCompletionItems(css = new MasterCSS(), ruleKey) {
|
|
12
|
+
const nativeProperties = cssDataProvider.provideProperties();
|
|
13
|
+
const completionItems = [];
|
|
14
|
+
const nativeKey = css.Rules.find(({ keys })=>keys.includes(ruleKey))?.id;
|
|
15
|
+
const nativePropertyData = nativeProperties.find(({ name })=>name === nativeKey);
|
|
16
|
+
const generateVariableCompletionItem = (variable, { scoped } = {
|
|
17
|
+
scoped: false
|
|
18
|
+
})=>{
|
|
19
|
+
const eachNativePropertyData = nativeProperties.find((x)=>x.name === variable.group) || nativePropertyData;
|
|
20
|
+
const appliedValue = scoped ? variable.key : variable.name;
|
|
21
|
+
const documentation = getCSSDataDocumentation(eachNativePropertyData, {
|
|
22
|
+
generatedCSS: generateCSS([
|
|
23
|
+
ruleKey + ':' + appliedValue
|
|
24
|
+
], css),
|
|
25
|
+
docs: eachNativePropertyData?.name || 'variables'
|
|
26
|
+
});
|
|
27
|
+
const completionItem = {
|
|
28
|
+
label: variable.name,
|
|
29
|
+
kind: CompletionItemKind.Value
|
|
30
|
+
};
|
|
31
|
+
const conflicted = completionItems.find(({ label })=>label === variable.name);
|
|
32
|
+
if (conflicted) {
|
|
33
|
+
completionItem.label = '$(' + completionItem.label + ')';
|
|
34
|
+
completionItem.documentation = getCSSDataDocumentation(eachNativePropertyData, {
|
|
35
|
+
generatedCSS: generateCSS([
|
|
36
|
+
ruleKey + ':' + completionItem.label
|
|
37
|
+
], css),
|
|
38
|
+
docs: eachNativePropertyData?.name || 'variables'
|
|
39
|
+
});
|
|
40
|
+
} else {
|
|
41
|
+
completionItem.documentation = getCSSDataDocumentation(eachNativePropertyData, {
|
|
42
|
+
generatedCSS: generateCSS([
|
|
43
|
+
ruleKey + ':' + appliedValue
|
|
44
|
+
], css),
|
|
45
|
+
docs: eachNativePropertyData?.name || 'variables'
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
if (variable.type === 'color') {
|
|
49
|
+
if (Object.keys(variable.modes || {}).length) {
|
|
50
|
+
completionItem.kind = CompletionItemKind.Color;
|
|
51
|
+
completionItem.detail = variable.name;
|
|
52
|
+
} else {
|
|
53
|
+
// todo: packages/css should support getTextByVariable(variable)
|
|
54
|
+
const configKey = 'variables.' + (variable.group ? variable.group + '.' + variable.key : variable.name);
|
|
55
|
+
const valueToken = variable.space && variable.value ? `${variable.space}(${variable.value.split(' ').join(',')})` : variable.value;
|
|
56
|
+
// detail is shown in the detail pane
|
|
57
|
+
// todo: variable.token should be recorded as original config variable
|
|
58
|
+
completionItem.detail = String(configKey);
|
|
59
|
+
completionItem.documentation = {
|
|
60
|
+
kind: 'markdown',
|
|
61
|
+
value: valueToken + '\n\n' + documentation?.value
|
|
62
|
+
};
|
|
63
|
+
completionItem.kind = CompletionItemKind.Color;
|
|
64
|
+
completionItem.sortText = 'color-' + variable.name.replace(/(.+?)-(\d+)/, (match, prefix, num)=>prefix + num.padStart(10, '0'));
|
|
65
|
+
}
|
|
66
|
+
} else if (variable.type === 'number') {
|
|
67
|
+
if (variable.name.startsWith('-') && eachNativePropertyData?.syntax?.includes('absolute')) return;
|
|
68
|
+
completionItem.detail = String(variable.name);
|
|
69
|
+
completionItem.sortText = (variable.group || '') + (variable.value >= 0 ? String(variable.value).padStart(10, '0') : '-' + String(Math.abs(variable.value)).padStart(10, '0'));
|
|
70
|
+
} else {
|
|
71
|
+
completionItem.detail = String(variable.value || variable.name);
|
|
72
|
+
}
|
|
73
|
+
return completionItem;
|
|
74
|
+
};
|
|
75
|
+
for (const EachRule of css.Rules){
|
|
76
|
+
/**
|
|
77
|
+
* Scoped variables
|
|
78
|
+
* @example box: + content -> box-sizing:content
|
|
79
|
+
*/ if (EachRule.definition.key === ruleKey || EachRule.definition.subkey === ruleKey || EachRule.definition.ambiguousKeys?.includes(ruleKey)) {
|
|
80
|
+
for(const variableName in EachRule.variables){
|
|
81
|
+
if (completionItems.find(({ label })=>label === variableName)) continue;
|
|
82
|
+
const variable = EachRule.variables[variableName];
|
|
83
|
+
const completionItem = generateVariableCompletionItem(variable, {
|
|
84
|
+
scoped: true
|
|
85
|
+
});
|
|
86
|
+
if (completionItem) {
|
|
87
|
+
completionItem.label = variableName;
|
|
88
|
+
completionItem.sortText = SCOPED_VARIABLE_PRIORITY + variableName;
|
|
89
|
+
completionItem.detail = '(scope) ' + completionItem.detail;
|
|
90
|
+
completionItems.push(completionItem);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* @example animation:fade
|
|
96
|
+
*/ if (EachRule.keys.includes(ruleKey) && EachRule.definition.includeAnimations) {
|
|
97
|
+
for(const animationName in css.animations){
|
|
98
|
+
const isNative = EachRule.definition.layer && [
|
|
99
|
+
Layer.Native,
|
|
100
|
+
Layer.NativeShorthand
|
|
101
|
+
].includes(EachRule.definition.layer);
|
|
102
|
+
completionItems.push({
|
|
103
|
+
label: animationName,
|
|
104
|
+
kind: CompletionItemKind.Value,
|
|
105
|
+
documentation: getCSSDataDocumentation(undefined, {
|
|
106
|
+
generatedCSS: generateCSS([
|
|
107
|
+
ruleKey + ':' + animationName
|
|
108
|
+
], css),
|
|
109
|
+
docs: isCoreRule(EachRule.id) && EachRule.id
|
|
110
|
+
}),
|
|
111
|
+
detail: isNative ? EachRule.id + ': ' + animationName : animationName
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Ambiguous values
|
|
117
|
+
* @example text: -> center, left, right, justify
|
|
118
|
+
* @example t: -> center, left, right, justify
|
|
119
|
+
*/ if (EachRule.definition.ambiguousKeys?.includes(ruleKey) && EachRule.definition.ambiguousValues?.length) {
|
|
120
|
+
const nativePropertyData = nativeProperties.find((x)=>x.name === EachRule.id);
|
|
121
|
+
for (const ambiguousValue of EachRule.definition.ambiguousValues){
|
|
122
|
+
if (typeof ambiguousValue !== 'string') continue;
|
|
123
|
+
const nativeValueData = nativePropertyData?.values?.find((x)=>x.name === ambiguousValue);
|
|
124
|
+
const isNative = EachRule.definition.layer && [
|
|
125
|
+
Layer.Native,
|
|
126
|
+
Layer.NativeShorthand
|
|
127
|
+
].includes(EachRule.definition.layer);
|
|
128
|
+
completionItems.push({
|
|
129
|
+
label: ambiguousValue,
|
|
130
|
+
kind: CompletionItemKind.Value,
|
|
131
|
+
sortText: AMBIGUOUS_PRIORITY + ambiguousValue,
|
|
132
|
+
documentation: getCSSDataDocumentation({
|
|
133
|
+
...nativeValueData || {},
|
|
134
|
+
// use nativePropertyData.reference because nativeValueData does not have references
|
|
135
|
+
references: nativePropertyData?.references
|
|
136
|
+
}, {
|
|
137
|
+
generatedCSS: generateCSS([
|
|
138
|
+
ruleKey + ':' + ambiguousValue
|
|
139
|
+
], css),
|
|
140
|
+
docs: isCoreRule(EachRule.id) && EachRule.id
|
|
141
|
+
}),
|
|
142
|
+
detail: isNative ? EachRule.id + ': ' + ambiguousValue : ambiguousValue
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Native values
|
|
149
|
+
*/ if (nativeKey) {
|
|
150
|
+
nativePropertyData?.values?.forEach((value)=>{
|
|
151
|
+
if (completionItems.find((x)=>x.label === value.name) || nativePropertyData.name === 'font' && typeof +value.name === 'number' || value.name.includes(' ')) return;
|
|
152
|
+
completionItems.push({
|
|
153
|
+
label: value.name,
|
|
154
|
+
kind: CompletionItemKind.Value,
|
|
155
|
+
sortText: NATIVE_PRIORITY + (value.name.startsWith('-') ? 'zz' + value.name.slice(1) : value.name),
|
|
156
|
+
documentation: getCSSDataDocumentation({
|
|
157
|
+
...value,
|
|
158
|
+
// use nativePropertyData.reference because nativeValueData does not have references
|
|
159
|
+
references: nativePropertyData?.references
|
|
160
|
+
}, {
|
|
161
|
+
generatedCSS: generateCSS([
|
|
162
|
+
ruleKey + ':' + value.name
|
|
163
|
+
], css),
|
|
164
|
+
docs: nativeKey
|
|
165
|
+
}),
|
|
166
|
+
detail: nativeKey + ': ' + value.name
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
// global variables
|
|
171
|
+
for(const variableName in css.variables){
|
|
172
|
+
const variable = css.variables[variableName];
|
|
173
|
+
const completionItem = generateVariableCompletionItem(variable);
|
|
174
|
+
if (completionItem) {
|
|
175
|
+
completionItem.sortText = GLOBAL_VARIABLE_PRIORITY + (completionItem.sortText || completionItem.label);
|
|
176
|
+
completionItem.detail = '(global) ' + completionItem.detail;
|
|
177
|
+
completionItems.push(completionItem);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return sortCompletionItems(completionItems);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export { getValueCompletionItems as default };
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var vscodeLanguageserverProtocol = require('vscode-languageserver-protocol');
|
|
4
|
+
var getPseudoClassCompletionItems = require('./get-pseudo-class-completion-items.cjs');
|
|
5
|
+
var getPseudoElementCompletionItems = require('./get-pseudo-element-completion-items.cjs');
|
|
6
|
+
var css = require('@master/css');
|
|
7
|
+
var common = require('../common.cjs');
|
|
8
|
+
var getMainCompletionItems = require('./get-main-completion-items.cjs');
|
|
9
|
+
var getValueCompletionItems = require('./get-value-completion-items.cjs');
|
|
10
|
+
var getQueryCompletionItems = require('./get-query-completion-items.cjs');
|
|
11
|
+
var getCssDataDocumentation = require('./get-css-data-documentation.cjs');
|
|
12
|
+
|
|
13
|
+
function querySyntaxCompletions(q = '', css$1 = new css.MasterCSS()) {
|
|
14
|
+
const fields = q.split(' ');
|
|
15
|
+
let field = fields[fields.length - 1];
|
|
16
|
+
const triggerCharacter = q.charAt(q.length - 1);
|
|
17
|
+
const invoked = triggerCharacter === ' ' || field.length === 0;
|
|
18
|
+
if (invoked || field === common.GROUP_TRIGGER_CHARACTER || triggerCharacter === ';') {
|
|
19
|
+
return getMainCompletionItems(css$1);
|
|
20
|
+
}
|
|
21
|
+
const isGroup = field.startsWith(common.GROUP_TRIGGER_CHARACTER);
|
|
22
|
+
if (isGroup) {
|
|
23
|
+
const declarationSeparatorMatches = Array.from(field.matchAll(/;(?=(?:[^'"]|'[^']*'|"[^"]*")*$)/g));
|
|
24
|
+
if (declarationSeparatorMatches.length) {
|
|
25
|
+
field = field.slice(declarationSeparatorMatches[declarationSeparatorMatches.length - 1].index + 1);
|
|
26
|
+
} else {
|
|
27
|
+
field = field.slice(1);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
const signs = css$1.Rules.filter(({ definition })=>definition.sign).map(({ definition })=>definition.sign);
|
|
31
|
+
const keyMatch = field.match(new RegExp(`^[^${signs}'":\\s]+:`));
|
|
32
|
+
const atMatches = Array.from(field.matchAll(/@(?=(?:[^'"]|'[^']*'|"[^"]*")*$)/g));
|
|
33
|
+
const valueSeparatorMatch = field.match(/\|(?=(?:[^'"]|'[^']*'|"[^"]*")*$)/g);
|
|
34
|
+
const atInvoked = atMatches.findIndex(({ index })=>index !== 0) !== -1;
|
|
35
|
+
const firstColonIndex = keyMatch ? keyMatch[0].length - 1 : -1;
|
|
36
|
+
const selectorInvokedRegex = new RegExp(`[${css.SELECTOR_SIGNS.join('')}](?=(?:[^'"]|'[^']*'|"[^"]*")*$)`);
|
|
37
|
+
const key = keyMatch ? keyMatch[0].slice(0, firstColonIndex) : undefined;
|
|
38
|
+
const styleNames = Object.keys(css$1.config.styles || {});
|
|
39
|
+
const utilityNames = Object.keys(css$1.config.utilities || {});
|
|
40
|
+
const isStyle = !!styleNames.find((eachStyleName)=>new RegExp(`^${eachStyleName}(?:\\b|_)`).test(field));
|
|
41
|
+
const isUtility = !!utilityNames.find((eachUtilityName)=>new RegExp(`^${eachUtilityName}(?:\\b|_)`).test(field));
|
|
42
|
+
// check by utilities and styles
|
|
43
|
+
if (!isStyle && !isUtility) {
|
|
44
|
+
if (key === undefined && !valueSeparatorMatch) {
|
|
45
|
+
/**
|
|
46
|
+
* The server capability sets '@' '~' as the trigger characters for at and adjacent selectors,
|
|
47
|
+
* but these two characters are also the prefix symbols of `animation` and `transition`,
|
|
48
|
+
* and should be filtered to prevent hints all completions items.
|
|
49
|
+
* @example class="@"
|
|
50
|
+
* @example class="~"
|
|
51
|
+
*/ if (field.startsWith(css.ANIMATION_SIGN) || field.startsWith(css.TRANSITION_SIGN)) {
|
|
52
|
+
return getMainCompletionItems(css$1).filter((completionItem)=>completionItem.label.startsWith(field)).map((completionItem)=>({
|
|
53
|
+
...completionItem,
|
|
54
|
+
label: completionItem.label.slice(1)
|
|
55
|
+
}));
|
|
56
|
+
}
|
|
57
|
+
return getMainCompletionItems(css$1);
|
|
58
|
+
}
|
|
59
|
+
if (!atInvoked && !selectorInvokedRegex.test(field.slice(firstColonIndex + 1))) {
|
|
60
|
+
if (field.startsWith(css.ANIMATION_SIGN) && firstColonIndex === -1) {
|
|
61
|
+
return getValueCompletionItems(css$1, 'animation');
|
|
62
|
+
} else if (field.startsWith(css.TRANSITION_SIGN) && firstColonIndex === -1) {
|
|
63
|
+
return getValueCompletionItems(css$1, 'transition');
|
|
64
|
+
} else if (key && firstColonIndex !== -1) {
|
|
65
|
+
return getValueCompletionItems(css$1, key);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Not support conditional btn:hover and group
|
|
71
|
+
*/ if (isStyle || isGroup) return;
|
|
72
|
+
if (atInvoked && [
|
|
73
|
+
css.AT_SIGN,
|
|
74
|
+
...css.QUERY_COMPARISON_OPERATORS,
|
|
75
|
+
...css.QUERY_LOGICAL_OPERATORS
|
|
76
|
+
].includes(triggerCharacter)) {
|
|
77
|
+
return getQueryCompletionItems(css$1, triggerCharacter, field);
|
|
78
|
+
}
|
|
79
|
+
if (!atInvoked && common.SELECTOR_TRIGGER_CHARACTERS.includes(triggerCharacter)) {
|
|
80
|
+
const pseudoElementCompletionItems = getPseudoElementCompletionItems(css$1, field);
|
|
81
|
+
if (field.endsWith('::')) {
|
|
82
|
+
/**
|
|
83
|
+
* Consider trigger characters and fix insertText.
|
|
84
|
+
* @incorrect class="text:center::" -> class="text:center::::after"
|
|
85
|
+
* @correct class="text:center::" -> class="text:center::after"
|
|
86
|
+
*/ pseudoElementCompletionItems.forEach((completionItem)=>completionItem.insertText = completionItem.label.slice(2));
|
|
87
|
+
return pseudoElementCompletionItems;
|
|
88
|
+
} else {
|
|
89
|
+
const completionItems = getPseudoClassCompletionItems(css$1, field);
|
|
90
|
+
completionItems.push(...pseudoElementCompletionItems);
|
|
91
|
+
/**
|
|
92
|
+
* Consider trigger characters and fix insertText.
|
|
93
|
+
* @incorrect class="text:center:" -> class="text:center::active"
|
|
94
|
+
* @correct class="text:center:" -> class="text:center:active"
|
|
95
|
+
*/ if (triggerCharacter === ':') {
|
|
96
|
+
completionItems.forEach((completionItem)=>completionItem.insertText = completionItem.label.slice(1));
|
|
97
|
+
} else {
|
|
98
|
+
completionItems.push({
|
|
99
|
+
label: '.<class>',
|
|
100
|
+
insertText: '.',
|
|
101
|
+
documentation: getCssDataDocumentation.getCSSDataDocumentation(undefined, {
|
|
102
|
+
generatedCSS: css.generateCSS([
|
|
103
|
+
field + '.class'
|
|
104
|
+
], css$1),
|
|
105
|
+
docs: 'selectors'
|
|
106
|
+
}),
|
|
107
|
+
kind: vscodeLanguageserverProtocol.CompletionItemKind.Class
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
return completionItems;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
module.exports = querySyntaxCompletions;
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { CompletionItemKind } from 'vscode-languageserver-protocol';
|
|
2
|
+
import getPseudoClassCompletionItems from './get-pseudo-class-completion-items.mjs';
|
|
3
|
+
import getPseudoElementCompletionItems from './get-pseudo-element-completion-items.mjs';
|
|
4
|
+
import { SELECTOR_SIGNS, ANIMATION_SIGN, TRANSITION_SIGN, AT_SIGN, QUERY_COMPARISON_OPERATORS, QUERY_LOGICAL_OPERATORS, generateCSS, MasterCSS } from '@master/css';
|
|
5
|
+
import { GROUP_TRIGGER_CHARACTER, SELECTOR_TRIGGER_CHARACTERS } from '../common.mjs';
|
|
6
|
+
import getMainCompletionItems from './get-main-completion-items.mjs';
|
|
7
|
+
import getValueCompletionItems from './get-value-completion-items.mjs';
|
|
8
|
+
import getQueryCompletionItems from './get-query-completion-items.mjs';
|
|
9
|
+
import { getCSSDataDocumentation } from './get-css-data-documentation.mjs';
|
|
10
|
+
|
|
11
|
+
function querySyntaxCompletions(q = '', css = new MasterCSS()) {
|
|
12
|
+
const fields = q.split(' ');
|
|
13
|
+
let field = fields[fields.length - 1];
|
|
14
|
+
const triggerCharacter = q.charAt(q.length - 1);
|
|
15
|
+
const invoked = triggerCharacter === ' ' || field.length === 0;
|
|
16
|
+
if (invoked || field === GROUP_TRIGGER_CHARACTER || triggerCharacter === ';') {
|
|
17
|
+
return getMainCompletionItems(css);
|
|
18
|
+
}
|
|
19
|
+
const isGroup = field.startsWith(GROUP_TRIGGER_CHARACTER);
|
|
20
|
+
if (isGroup) {
|
|
21
|
+
const declarationSeparatorMatches = Array.from(field.matchAll(/;(?=(?:[^'"]|'[^']*'|"[^"]*")*$)/g));
|
|
22
|
+
if (declarationSeparatorMatches.length) {
|
|
23
|
+
field = field.slice(declarationSeparatorMatches[declarationSeparatorMatches.length - 1].index + 1);
|
|
24
|
+
} else {
|
|
25
|
+
field = field.slice(1);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
const signs = css.Rules.filter(({ definition })=>definition.sign).map(({ definition })=>definition.sign);
|
|
29
|
+
const keyMatch = field.match(new RegExp(`^[^${signs}'":\\s]+:`));
|
|
30
|
+
const atMatches = Array.from(field.matchAll(/@(?=(?:[^'"]|'[^']*'|"[^"]*")*$)/g));
|
|
31
|
+
const valueSeparatorMatch = field.match(/\|(?=(?:[^'"]|'[^']*'|"[^"]*")*$)/g);
|
|
32
|
+
const atInvoked = atMatches.findIndex(({ index })=>index !== 0) !== -1;
|
|
33
|
+
const firstColonIndex = keyMatch ? keyMatch[0].length - 1 : -1;
|
|
34
|
+
const selectorInvokedRegex = new RegExp(`[${SELECTOR_SIGNS.join('')}](?=(?:[^'"]|'[^']*'|"[^"]*")*$)`);
|
|
35
|
+
const key = keyMatch ? keyMatch[0].slice(0, firstColonIndex) : undefined;
|
|
36
|
+
const styleNames = Object.keys(css.config.styles || {});
|
|
37
|
+
const utilityNames = Object.keys(css.config.utilities || {});
|
|
38
|
+
const isStyle = !!styleNames.find((eachStyleName)=>new RegExp(`^${eachStyleName}(?:\\b|_)`).test(field));
|
|
39
|
+
const isUtility = !!utilityNames.find((eachUtilityName)=>new RegExp(`^${eachUtilityName}(?:\\b|_)`).test(field));
|
|
40
|
+
// check by utilities and styles
|
|
41
|
+
if (!isStyle && !isUtility) {
|
|
42
|
+
if (key === undefined && !valueSeparatorMatch) {
|
|
43
|
+
/**
|
|
44
|
+
* The server capability sets '@' '~' as the trigger characters for at and adjacent selectors,
|
|
45
|
+
* but these two characters are also the prefix symbols of `animation` and `transition`,
|
|
46
|
+
* and should be filtered to prevent hints all completions items.
|
|
47
|
+
* @example class="@"
|
|
48
|
+
* @example class="~"
|
|
49
|
+
*/ if (field.startsWith(ANIMATION_SIGN) || field.startsWith(TRANSITION_SIGN)) {
|
|
50
|
+
return getMainCompletionItems(css).filter((completionItem)=>completionItem.label.startsWith(field)).map((completionItem)=>({
|
|
51
|
+
...completionItem,
|
|
52
|
+
label: completionItem.label.slice(1)
|
|
53
|
+
}));
|
|
54
|
+
}
|
|
55
|
+
return getMainCompletionItems(css);
|
|
56
|
+
}
|
|
57
|
+
if (!atInvoked && !selectorInvokedRegex.test(field.slice(firstColonIndex + 1))) {
|
|
58
|
+
if (field.startsWith(ANIMATION_SIGN) && firstColonIndex === -1) {
|
|
59
|
+
return getValueCompletionItems(css, 'animation');
|
|
60
|
+
} else if (field.startsWith(TRANSITION_SIGN) && firstColonIndex === -1) {
|
|
61
|
+
return getValueCompletionItems(css, 'transition');
|
|
62
|
+
} else if (key && firstColonIndex !== -1) {
|
|
63
|
+
return getValueCompletionItems(css, key);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Not support conditional btn:hover and group
|
|
69
|
+
*/ if (isStyle || isGroup) return;
|
|
70
|
+
if (atInvoked && [
|
|
71
|
+
AT_SIGN,
|
|
72
|
+
...QUERY_COMPARISON_OPERATORS,
|
|
73
|
+
...QUERY_LOGICAL_OPERATORS
|
|
74
|
+
].includes(triggerCharacter)) {
|
|
75
|
+
return getQueryCompletionItems(css, triggerCharacter, field);
|
|
76
|
+
}
|
|
77
|
+
if (!atInvoked && SELECTOR_TRIGGER_CHARACTERS.includes(triggerCharacter)) {
|
|
78
|
+
const pseudoElementCompletionItems = getPseudoElementCompletionItems(css, field);
|
|
79
|
+
if (field.endsWith('::')) {
|
|
80
|
+
/**
|
|
81
|
+
* Consider trigger characters and fix insertText.
|
|
82
|
+
* @incorrect class="text:center::" -> class="text:center::::after"
|
|
83
|
+
* @correct class="text:center::" -> class="text:center::after"
|
|
84
|
+
*/ pseudoElementCompletionItems.forEach((completionItem)=>completionItem.insertText = completionItem.label.slice(2));
|
|
85
|
+
return pseudoElementCompletionItems;
|
|
86
|
+
} else {
|
|
87
|
+
const completionItems = getPseudoClassCompletionItems(css, field);
|
|
88
|
+
completionItems.push(...pseudoElementCompletionItems);
|
|
89
|
+
/**
|
|
90
|
+
* Consider trigger characters and fix insertText.
|
|
91
|
+
* @incorrect class="text:center:" -> class="text:center::active"
|
|
92
|
+
* @correct class="text:center:" -> class="text:center:active"
|
|
93
|
+
*/ if (triggerCharacter === ':') {
|
|
94
|
+
completionItems.forEach((completionItem)=>completionItem.insertText = completionItem.label.slice(1));
|
|
95
|
+
} else {
|
|
96
|
+
completionItems.push({
|
|
97
|
+
label: '.<class>',
|
|
98
|
+
insertText: '.',
|
|
99
|
+
documentation: getCSSDataDocumentation(undefined, {
|
|
100
|
+
generatedCSS: generateCSS([
|
|
101
|
+
field + '.class'
|
|
102
|
+
], css),
|
|
103
|
+
docs: 'selectors'
|
|
104
|
+
}),
|
|
105
|
+
kind: CompletionItemKind.Class
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
return completionItems;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export { querySyntaxCompletions as default };
|
package/dist/utils/regex.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const instancePattern = /[^\s:]+:\w*\(?((?<!\\)["'`])((?:\\\1|(?:(?!\1))
|
|
3
|
+
const instancePattern = /[^\s:]+:\w*\(?((?<!\\)["'`])((?:\\\1|(?:(?!\1))\S)*)((?<!\\)\1)\)?|[^\s"'`]+/g;
|
|
4
4
|
|
|
5
5
|
exports.instancePattern = instancePattern;
|
package/dist/utils/regex.mjs
CHANGED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/* localeCompare equals to the real vscode completion items sorting */ function sortCompletionItems(items) {
|
|
4
|
+
return items.sort((a, b)=>{
|
|
5
|
+
const sortTextA = a.sortText || a.label;
|
|
6
|
+
const sortTextB = b.sortText || b.label;
|
|
7
|
+
return sortTextA.localeCompare(sortTextB);
|
|
8
|
+
});
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
module.exports = sortCompletionItems;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/* localeCompare equals to the real vscode completion items sorting */ function sortCompletionItems(items) {
|
|
2
|
+
return items.sort((a, b)=>{
|
|
3
|
+
const sortTextA = a.sortText || a.label;
|
|
4
|
+
const sortTextB = b.sortText || b.label;
|
|
5
|
+
return sortTextA.localeCompare(sortTextB);
|
|
6
|
+
});
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export { sortCompletionItems as default };
|
package/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"name":"@master/css-language-service","type":"module","scripts":{"build":"techor build \"src/**/*.ts\"","dev":"pnpm build --watch","type-check":"tsc --noEmit","lint":"eslint src"},"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":{"@babel/parser":"^7.
|
|
1
|
+
{"name":"@master/css-language-service","type":"module","scripts":{"build":"techor build \"src/**/*.ts\"","dev":"pnpm build --watch","type-check":"tsc --noEmit","lint":"eslint src","test":"jest"},"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":{"@babel/parser":"^7.24.1","@master/css":"^2.0.0-rc.27","@techor/extend":"^3.0.18","color-convert":"^2.0.1","color-rgba":"^3.0.0","color-space":"^2.0.1","culori":"^4.0.1","js-beautify":"^1.15.1","lodash.escaperegexp":"^4.1.2","minimatch":"^9.0.3","pex-color":"^2.1.0","vscode-css-languageservice":"^6.2.13","vscode-languageserver-protocol":"^3.17.5","vscode-languageserver-textdocument":"^1.0.11"},"devDependencies":{"@types/chroma-js":"^2.4.4","@types/color-convert":"^2.0.3","@types/color-rgba":"^2.1.2","@types/culori":"^2.1.0","@types/lodash.escaperegexp":"^4.1.9","@types/vscode":"^1.87.0","@master/css-shared":"^2.0.0-rc.27","vscode-languageserver":"^7.0.0"},"sideEffects":false,"main":"./dist/index.cjs","module":"./dist/index.mjs","jsnext:main":"./dist/index.mjs","esnext":"./dist/index.mjs","types":"./dist/index.d.ts","exports":{".":{"import":"./dist/index.mjs","require":"./dist/index.cjs","types":"./dist/index.d.ts"}},"files":["dist"],"publishConfig":{"access":"public","provenance":true},"version":"2.0.0-rc.27"}
|