volar-service-css 0.0.29 → 0.0.31

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 (3) hide show
  1. package/index.d.ts +16 -5
  2. package/index.js +206 -104
  3. package/package.json +5 -5
package/index.d.ts CHANGED
@@ -1,9 +1,20 @@
1
- import type { ServicePlugin } from '@volar/language-service';
1
+ import type { Disposable, DocumentSelector, Result, ServiceContext, ServicePlugin } from '@volar/language-service';
2
2
  import * as css from 'vscode-css-languageservice';
3
- import type { TextDocument } from 'vscode-languageserver-textdocument';
3
+ import { TextDocument } from 'vscode-languageserver-textdocument';
4
4
  export interface Provide {
5
- 'css/stylesheet': (document: TextDocument) => css.Stylesheet | undefined;
6
- 'css/languageService': (languageId: string) => css.LanguageService | undefined;
5
+ 'css/stylesheet': (document: TextDocument, ls: css.LanguageService) => css.Stylesheet;
6
+ 'css/languageService': (document: TextDocument) => css.LanguageService | undefined;
7
7
  }
8
- export declare function create(): ServicePlugin;
8
+ export declare function create({ cssDocumentSelector, scssDocumentSelector, lessDocumentSelector, useDefaultDataProvider, getDocumentContext, isFormattingEnabled, getFormatConfiguration, getLanguageSettings, getCustomData, onDidChangeCustomData, }?: {
9
+ cssDocumentSelector?: DocumentSelector;
10
+ scssDocumentSelector?: DocumentSelector;
11
+ lessDocumentSelector?: DocumentSelector;
12
+ useDefaultDataProvider?: boolean;
13
+ getDocumentContext?(context: ServiceContext): css.DocumentContext;
14
+ isFormattingEnabled?(document: TextDocument, context: ServiceContext): Result<boolean>;
15
+ getFormatConfiguration?(document: TextDocument, context: ServiceContext): Result<css.CSSFormatConfiguration | undefined>;
16
+ getLanguageSettings?(document: TextDocument, context: ServiceContext): Result<css.LanguageSettings | undefined>;
17
+ getCustomData?(context: ServiceContext): Result<css.ICSSDataProvider[]>;
18
+ onDidChangeCustomData?(listener: () => void, context: ServiceContext): Disposable;
19
+ }): ServicePlugin;
9
20
  //# sourceMappingURL=index.d.ts.map
package/index.js CHANGED
@@ -2,70 +2,88 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.create = void 0;
4
4
  const css = require("vscode-css-languageservice");
5
+ const vscode_languageserver_textdocument_1 = require("vscode-languageserver-textdocument");
5
6
  const vscode_uri_1 = require("vscode-uri");
6
- function create() {
7
+ function create({ cssDocumentSelector = ['css'], scssDocumentSelector = ['scss'], lessDocumentSelector = ['less'], useDefaultDataProvider = true, getDocumentContext = context => {
8
+ return {
9
+ resolveReference(ref, base) {
10
+ if (ref.match(/^\w[\w\d+.-]*:/)) {
11
+ // starts with a schema
12
+ return ref;
13
+ }
14
+ if (ref[0] === '/') { // resolve absolute path against the current workspace folder
15
+ let folderUri = context.env.workspaceFolder;
16
+ if (!folderUri.endsWith('/')) {
17
+ folderUri += '/';
18
+ }
19
+ return folderUri + ref.substring(1);
20
+ }
21
+ const baseUri = vscode_uri_1.URI.parse(base);
22
+ const baseUriDir = baseUri.path.endsWith('/') ? baseUri : vscode_uri_1.Utils.dirname(baseUri);
23
+ return vscode_uri_1.Utils.resolvePath(baseUriDir, ref).toString(true);
24
+ },
25
+ };
26
+ }, isFormattingEnabled = async (document, context) => {
27
+ return await context.env.getConfiguration?.(document.languageId + '.format.enable') ?? true;
28
+ }, getFormatConfiguration = async (document, context) => {
29
+ return await context.env.getConfiguration?.(document.languageId + '.format');
30
+ }, getLanguageSettings = async (document, context) => {
31
+ return await context.env.getConfiguration?.(document.languageId);
32
+ }, getCustomData = async (context) => {
33
+ const customData = await context.env.getConfiguration?.('css.customData') ?? [];
34
+ const newData = [];
35
+ for (const customDataPath of customData) {
36
+ const uri = vscode_uri_1.Utils.resolvePath(vscode_uri_1.URI.parse(context.env.workspaceFolder), customDataPath);
37
+ const json = await context.env.fs?.readFile?.(uri.toString());
38
+ if (json) {
39
+ try {
40
+ const data = JSON.parse(json);
41
+ newData.push(css.newCSSDataProvider(data));
42
+ }
43
+ catch (error) {
44
+ console.error(error);
45
+ }
46
+ }
47
+ }
48
+ return newData;
49
+ }, onDidChangeCustomData = (listener, context) => {
50
+ const disposable = context.env.onDidChangeConfiguration?.(listener);
51
+ return {
52
+ dispose() {
53
+ disposable?.dispose();
54
+ },
55
+ };
56
+ }, } = {}) {
7
57
  return {
8
58
  name: 'css',
9
59
  // https://github.com/microsoft/vscode/blob/09850876e652688fb142e2e19fd00fd38c0bc4ba/extensions/css-language-features/server/src/cssServer.ts#L97
10
60
  triggerCharacters: ['/', '-', ':'],
11
61
  create(context) {
12
- let inited = false;
13
62
  const stylesheets = new WeakMap();
14
63
  const fileSystemProvider = {
15
- stat: async (uri) => await context.env.fs?.stat(uri) ?? {
16
- type: css.FileType.Unknown,
17
- ctime: 0,
18
- mtime: 0,
19
- size: 0,
20
- },
64
+ stat: async (uri) => await context.env.fs?.stat(uri)
65
+ ?? { type: css.FileType.Unknown, ctime: 0, mtime: 0, size: 0 },
21
66
  readDirectory: async (uri) => await context.env.fs?.readDirectory(uri) ?? [],
22
67
  };
23
- const documentContext = {
24
- resolveReference(ref, base) {
25
- if (ref.match(/^\w[\w\d+.-]*:/)) {
26
- // starts with a schema
27
- return ref;
28
- }
29
- if (ref[0] === '/') { // resolve absolute path against the current workspace folder
30
- return base + ref;
31
- }
32
- const baseUri = vscode_uri_1.URI.parse(base);
33
- const baseUriDir = baseUri.path.endsWith('/') ? baseUri : vscode_uri_1.Utils.dirname(baseUri);
34
- return vscode_uri_1.Utils.resolvePath(baseUriDir, ref).toString(true);
35
- },
36
- };
37
- const cssLs = css.getCSSLanguageService({
38
- fileSystemProvider,
39
- clientCapabilities: context.env.clientCapabilities,
40
- });
41
- const scssLs = css.getSCSSLanguageService({
42
- fileSystemProvider,
43
- clientCapabilities: context.env.clientCapabilities,
44
- });
45
- const lessLs = css.getLESSLanguageService({
46
- fileSystemProvider,
47
- clientCapabilities: context.env.clientCapabilities,
48
- });
49
- const postcssLs = {
50
- ...scssLs,
51
- doValidation: (document, stylesheet, documentSettings) => {
52
- let errors = scssLs.doValidation(document, stylesheet, documentSettings);
53
- errors = errors.filter(error => error.code !== 'css-semicolonexpected');
54
- errors = errors.filter(error => error.code !== 'css-ruleorselectorexpected');
55
- errors = errors.filter(error => error.code !== 'unknownAtRules');
56
- return errors;
57
- },
58
- };
68
+ const documentContext = getDocumentContext(context);
69
+ const disposable = onDidChangeCustomData(() => initializing = undefined, context);
70
+ let cssLs;
71
+ let scssLs;
72
+ let lessLs;
73
+ let customData = [];
74
+ let initializing;
59
75
  return {
76
+ dispose() {
77
+ disposable.dispose();
78
+ },
60
79
  provide: {
61
80
  'css/stylesheet': getStylesheet,
62
81
  'css/languageService': getCssLs,
63
82
  },
64
83
  async provideCompletionItems(document, position) {
65
84
  return worker(document, async (stylesheet, cssLs) => {
66
- const settings = await context.env.getConfiguration?.(document.languageId);
67
- const cssResult = await cssLs.doComplete2(document, position, stylesheet, documentContext, settings?.completion);
68
- return cssResult;
85
+ const settings = await getLanguageSettings(document, context);
86
+ return await cssLs.doComplete2(document, position, stylesheet, documentContext, settings?.completion);
69
87
  });
70
88
  },
71
89
  provideRenameRange(document, position) {
@@ -97,13 +115,13 @@ function create() {
97
115
  },
98
116
  async provideDiagnostics(document) {
99
117
  return worker(document, async (stylesheet, cssLs) => {
100
- const settings = await context.env.getConfiguration?.(document.languageId);
118
+ const settings = await getLanguageSettings(document, context);
101
119
  return cssLs.doValidation(document, stylesheet, settings);
102
120
  });
103
121
  },
104
122
  async provideHover(document, position) {
105
123
  return worker(document, async (stylesheet, cssLs) => {
106
- const settings = await context.env.getConfiguration?.(document.languageId);
124
+ const settings = await getLanguageSettings(document, context);
107
125
  return cssLs.doHover(document, position, stylesheet, settings?.hover);
108
126
  });
109
127
  },
@@ -138,8 +156,8 @@ function create() {
138
156
  });
139
157
  },
140
158
  provideFoldingRanges(document) {
141
- return worker(document, (stylesheet, cssLs) => {
142
- return cssLs.getFoldingRanges(document, stylesheet);
159
+ return worker(document, (_stylesheet, cssLs) => {
160
+ return cssLs.getFoldingRanges(document, context.env.clientCapabilities?.textDocument?.foldingRange);
143
161
  });
144
162
  },
145
163
  provideSelectionRanges(document, positions) {
@@ -147,59 +165,142 @@ function create() {
147
165
  return cssLs.getSelectionRanges(document, positions, stylesheet);
148
166
  });
149
167
  },
150
- async provideDocumentFormattingEdits(document, formatRange, options) {
168
+ async provideDocumentFormattingEdits(document, formatRange, options, codeOptions) {
151
169
  return worker(document, async (_stylesheet, cssLs) => {
152
- const options_2 = await context.env.getConfiguration?.(document.languageId + '.format');
153
- if (options_2?.enable === false) {
170
+ if (!await isFormattingEnabled(document, context)) {
154
171
  return;
155
172
  }
156
- return cssLs.format(document, formatRange, {
157
- ...options_2,
173
+ const formatSettings = await getFormatConfiguration(document, context);
174
+ const formatOptions = {
158
175
  ...options,
159
- });
176
+ ...formatSettings,
177
+ };
178
+ let formatDocument = document;
179
+ let prefixes = [];
180
+ let suffixes = [];
181
+ if (codeOptions?.initialIndentLevel) {
182
+ for (let i = 0; i < codeOptions.initialIndentLevel; i++) {
183
+ if (i === codeOptions.initialIndentLevel - 1) {
184
+ prefixes.push('_', '{');
185
+ suffixes.unshift('}');
186
+ }
187
+ else {
188
+ prefixes.push('_', '{\n');
189
+ suffixes.unshift('\n}');
190
+ }
191
+ }
192
+ formatDocument = vscode_languageserver_textdocument_1.TextDocument.create(document.uri, document.languageId, document.version, prefixes.join('') + document.getText() + suffixes.join(''));
193
+ formatRange = {
194
+ start: formatDocument.positionAt(0),
195
+ end: formatDocument.positionAt(formatDocument.getText().length),
196
+ };
197
+ }
198
+ let edits = cssLs.format(formatDocument, formatRange, formatOptions);
199
+ if (codeOptions) {
200
+ let newText = vscode_languageserver_textdocument_1.TextDocument.applyEdits(formatDocument, edits);
201
+ for (const prefix of prefixes) {
202
+ newText = newText.trimStart().slice(prefix.trim().length);
203
+ }
204
+ for (const suffix of suffixes.reverse()) {
205
+ newText = newText.trimEnd().slice(0, -suffix.trim().length);
206
+ }
207
+ if (!codeOptions.initialIndentLevel && codeOptions.level > 0) {
208
+ newText = ensureNewLines(newText);
209
+ }
210
+ edits = [{
211
+ range: {
212
+ start: document.positionAt(0),
213
+ end: document.positionAt(document.getText().length),
214
+ },
215
+ newText,
216
+ }];
217
+ }
218
+ return edits;
219
+ function ensureNewLines(newText) {
220
+ const verifyDocument = vscode_languageserver_textdocument_1.TextDocument.create(document.uri, document.languageId, document.version, '_ {' + newText + '}');
221
+ const verifyEdits = cssLs.format(verifyDocument, undefined, formatOptions);
222
+ let verifyText = vscode_languageserver_textdocument_1.TextDocument.applyEdits(verifyDocument, verifyEdits);
223
+ verifyText = verifyText.trimStart().slice('_'.length);
224
+ verifyText = verifyText.trim().slice('{'.length, -'}'.length);
225
+ if (startWithNewLine(verifyText) !== startWithNewLine(newText)) {
226
+ if (startWithNewLine(verifyText)) {
227
+ newText = '\n' + newText;
228
+ }
229
+ else if (newText.startsWith('\n')) {
230
+ newText = newText.slice(1);
231
+ }
232
+ else if (newText.startsWith('\r\n')) {
233
+ newText = newText.slice(2);
234
+ }
235
+ }
236
+ if (endWithNewLine(verifyText) !== endWithNewLine(newText)) {
237
+ if (endWithNewLine(verifyText)) {
238
+ newText = newText + '\n';
239
+ }
240
+ else if (newText.endsWith('\n')) {
241
+ newText = newText.slice(0, -1);
242
+ }
243
+ else if (newText.endsWith('\r\n')) {
244
+ newText = newText.slice(0, -2);
245
+ }
246
+ }
247
+ return newText;
248
+ }
249
+ function startWithNewLine(text) {
250
+ return text.startsWith('\n') || text.startsWith('\r\n');
251
+ }
252
+ function endWithNewLine(text) {
253
+ return text.endsWith('\n') || text.endsWith('\r\n');
254
+ }
160
255
  });
161
256
  },
162
257
  };
163
- async function initCustomData() {
164
- if (!inited) {
165
- context.env.onDidChangeConfiguration?.(async () => {
166
- const customData = await getCustomData();
167
- cssLs.setDataProviders(true, customData);
168
- scssLs.setDataProviders(true, customData);
169
- lessLs.setDataProviders(true, customData);
170
- });
171
- const customData = await getCustomData();
172
- cssLs.setDataProviders(true, customData);
173
- scssLs.setDataProviders(true, customData);
174
- lessLs.setDataProviders(true, customData);
175
- inited = true;
258
+ function getCssLs(document) {
259
+ if (matchDocument(cssDocumentSelector, document)) {
260
+ if (!cssLs) {
261
+ cssLs = css.getCSSLanguageService({
262
+ fileSystemProvider,
263
+ clientCapabilities: context.env.clientCapabilities,
264
+ useDefaultDataProvider,
265
+ customDataProviders: customData,
266
+ });
267
+ cssLs.setDataProviders(useDefaultDataProvider, customData);
268
+ }
269
+ return cssLs;
176
270
  }
177
- }
178
- async function getCustomData() {
179
- const customData = await context.env.getConfiguration?.('css.customData') ?? [];
180
- const newData = [];
181
- for (const customDataPath of customData) {
182
- try {
183
- const pathModuleName = 'path'; // avoid bundle
184
- const { posix: path } = require(pathModuleName);
185
- const jsonPath = path.resolve(customDataPath);
186
- newData.push(css.newCSSDataProvider(require(jsonPath)));
271
+ else if (matchDocument(scssDocumentSelector, document)) {
272
+ if (!scssLs) {
273
+ scssLs = css.getSCSSLanguageService({
274
+ fileSystemProvider,
275
+ clientCapabilities: context.env.clientCapabilities,
276
+ useDefaultDataProvider,
277
+ customDataProviders: customData,
278
+ });
279
+ scssLs.setDataProviders(useDefaultDataProvider, customData);
187
280
  }
188
- catch (error) {
189
- console.error(error);
281
+ return scssLs;
282
+ }
283
+ else if (matchDocument(lessDocumentSelector, document)) {
284
+ if (!lessLs) {
285
+ lessLs = css.getLESSLanguageService({
286
+ fileSystemProvider,
287
+ clientCapabilities: context.env.clientCapabilities,
288
+ useDefaultDataProvider,
289
+ customDataProviders: customData,
290
+ });
291
+ lessLs.setDataProviders(useDefaultDataProvider, customData);
190
292
  }
293
+ return lessLs;
191
294
  }
192
- return newData;
193
295
  }
194
- function getCssLs(lang) {
195
- switch (lang) {
196
- case 'css': return cssLs;
197
- case 'scss': return scssLs;
198
- case 'less': return lessLs;
199
- case 'postcss': return postcssLs;
200
- }
296
+ async function worker(document, callback) {
297
+ const cssLs = getCssLs(document);
298
+ if (!cssLs)
299
+ return;
300
+ await (initializing ??= initialize());
301
+ return callback(getStylesheet(document, cssLs), cssLs);
201
302
  }
202
- function getStylesheet(document) {
303
+ function getStylesheet(document, ls) {
203
304
  const cache = stylesheets.get(document);
204
305
  if (cache) {
205
306
  const [cacheVersion, cacheStylesheet] = cache;
@@ -207,25 +308,26 @@ function create() {
207
308
  return cacheStylesheet;
208
309
  }
209
310
  }
210
- const cssLs = getCssLs(document.languageId);
211
- if (!cssLs)
212
- return;
213
- const stylesheet = cssLs.parseStylesheet(document);
311
+ const stylesheet = ls.parseStylesheet(document);
214
312
  stylesheets.set(document, [document.version, stylesheet]);
215
313
  return stylesheet;
216
314
  }
217
- async function worker(document, callback) {
218
- const stylesheet = getStylesheet(document);
219
- if (!stylesheet)
220
- return;
221
- const cssLs = getCssLs(document.languageId);
222
- if (!cssLs)
223
- return;
224
- await initCustomData();
225
- return callback(stylesheet, cssLs);
315
+ async function initialize() {
316
+ customData = await getCustomData(context);
317
+ cssLs?.setDataProviders(useDefaultDataProvider, customData);
318
+ scssLs?.setDataProviders(useDefaultDataProvider, customData);
319
+ lessLs?.setDataProviders(useDefaultDataProvider, customData);
226
320
  }
227
321
  },
228
322
  };
229
323
  }
230
324
  exports.create = create;
325
+ function matchDocument(selector, document) {
326
+ for (const sel of selector) {
327
+ if (sel === document.languageId || (typeof sel === 'object' && sel.language === document.languageId)) {
328
+ return true;
329
+ }
330
+ }
331
+ return false;
332
+ }
231
333
  //# sourceMappingURL=index.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "volar-service-css",
3
- "version": "0.0.29",
3
+ "version": "0.0.31",
4
4
  "description": "Integrate vscode-css-languageservice into Volar",
5
5
  "homepage": "https://github.com/volarjs/services/tree/master/packages/css",
6
6
  "bugs": "https://github.com/volarjs/services/issues",
@@ -25,19 +25,19 @@
25
25
  },
26
26
  "dependencies": {
27
27
  "vscode-css-languageservice": "^6.2.10",
28
+ "vscode-languageserver-textdocument": "^1.0.11",
28
29
  "vscode-uri": "^3.0.8"
29
30
  },
30
31
  "devDependencies": {
31
- "@types/node": "latest",
32
- "vscode-languageserver-textdocument": "^1.0.11"
32
+ "@types/node": "latest"
33
33
  },
34
34
  "peerDependencies": {
35
- "@volar/language-service": "~2.0.1"
35
+ "@volar/language-service": "~2.1.0"
36
36
  },
37
37
  "peerDependenciesMeta": {
38
38
  "@volar/language-service": {
39
39
  "optional": true
40
40
  }
41
41
  },
42
- "gitHead": "6927645293abcc249e7a39c98e52b9f5a2c08469"
42
+ "gitHead": "f7005aef724767786ee9fe943fa976231cc79bf1"
43
43
  }