vscode-css-languageservice 6.0.1 → 6.1.0

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 (87) hide show
  1. package/CHANGELOG.md +3 -1
  2. package/SECURITY.md +41 -0
  3. package/lib/esm/beautify/beautify-css.js +11 -4
  4. package/lib/esm/cssLanguageService.d.ts +38 -37
  5. package/lib/esm/cssLanguageService.js +73 -72
  6. package/lib/esm/cssLanguageTypes.d.ts +238 -238
  7. package/lib/esm/cssLanguageTypes.js +42 -42
  8. package/lib/esm/data/webCustomData.js +22089 -21959
  9. package/lib/esm/languageFacts/builtinData.js +142 -142
  10. package/lib/esm/languageFacts/colors.js +469 -469
  11. package/lib/esm/languageFacts/dataManager.js +88 -88
  12. package/lib/esm/languageFacts/dataProvider.js +73 -73
  13. package/lib/esm/languageFacts/entry.js +137 -137
  14. package/lib/esm/languageFacts/facts.js +8 -8
  15. package/lib/esm/parser/cssErrors.js +48 -48
  16. package/lib/esm/parser/cssNodes.js +1511 -1502
  17. package/lib/esm/parser/cssParser.js +1606 -1534
  18. package/lib/esm/parser/cssScanner.js +592 -592
  19. package/lib/esm/parser/cssSymbolScope.js +311 -311
  20. package/lib/esm/parser/lessParser.js +715 -714
  21. package/lib/esm/parser/lessScanner.js +57 -57
  22. package/lib/esm/parser/scssErrors.js +18 -18
  23. package/lib/esm/parser/scssParser.js +806 -796
  24. package/lib/esm/parser/scssScanner.js +95 -95
  25. package/lib/esm/services/cssCodeActions.js +77 -77
  26. package/lib/esm/services/cssCompletion.js +1054 -1054
  27. package/lib/esm/services/cssFolding.js +190 -190
  28. package/lib/esm/services/cssFormatter.js +136 -136
  29. package/lib/esm/services/cssHover.js +148 -148
  30. package/lib/esm/services/cssNavigation.js +441 -378
  31. package/lib/esm/services/cssSelectionRange.js +47 -47
  32. package/lib/esm/services/cssValidation.js +41 -41
  33. package/lib/esm/services/lessCompletion.js +378 -378
  34. package/lib/esm/services/lint.js +518 -518
  35. package/lib/esm/services/lintRules.js +76 -76
  36. package/lib/esm/services/lintUtil.js +196 -196
  37. package/lib/esm/services/pathCompletion.js +157 -157
  38. package/lib/esm/services/scssCompletion.js +354 -354
  39. package/lib/esm/services/scssNavigation.js +82 -82
  40. package/lib/esm/services/selectorPrinting.js +492 -492
  41. package/lib/esm/utils/arrays.js +40 -40
  42. package/lib/esm/utils/objects.js +11 -11
  43. package/lib/esm/utils/resources.js +11 -11
  44. package/lib/esm/utils/strings.js +102 -102
  45. package/lib/umd/beautify/beautify-css.js +11 -4
  46. package/lib/umd/cssLanguageService.d.ts +38 -37
  47. package/lib/umd/cssLanguageService.js +104 -99
  48. package/lib/umd/cssLanguageTypes.d.ts +238 -238
  49. package/lib/umd/cssLanguageTypes.js +89 -89
  50. package/lib/umd/data/webCustomData.js +22102 -21972
  51. package/lib/umd/languageFacts/builtinData.js +154 -154
  52. package/lib/umd/languageFacts/colors.js +492 -492
  53. package/lib/umd/languageFacts/dataManager.js +101 -101
  54. package/lib/umd/languageFacts/dataProvider.js +86 -86
  55. package/lib/umd/languageFacts/entry.js +152 -152
  56. package/lib/umd/languageFacts/facts.js +33 -29
  57. package/lib/umd/parser/cssErrors.js +61 -61
  58. package/lib/umd/parser/cssNodes.js +1597 -1587
  59. package/lib/umd/parser/cssParser.js +1619 -1547
  60. package/lib/umd/parser/cssScanner.js +606 -606
  61. package/lib/umd/parser/cssSymbolScope.js +328 -328
  62. package/lib/umd/parser/lessParser.js +728 -727
  63. package/lib/umd/parser/lessScanner.js +70 -70
  64. package/lib/umd/parser/scssErrors.js +31 -31
  65. package/lib/umd/parser/scssParser.js +819 -809
  66. package/lib/umd/parser/scssScanner.js +108 -108
  67. package/lib/umd/services/cssCodeActions.js +90 -90
  68. package/lib/umd/services/cssCompletion.js +1067 -1067
  69. package/lib/umd/services/cssFolding.js +203 -203
  70. package/lib/umd/services/cssFormatter.js +150 -150
  71. package/lib/umd/services/cssHover.js +161 -161
  72. package/lib/umd/services/cssNavigation.js +454 -391
  73. package/lib/umd/services/cssSelectionRange.js +60 -60
  74. package/lib/umd/services/cssValidation.js +54 -54
  75. package/lib/umd/services/lessCompletion.js +391 -391
  76. package/lib/umd/services/lint.js +531 -531
  77. package/lib/umd/services/lintRules.js +91 -91
  78. package/lib/umd/services/lintUtil.js +210 -210
  79. package/lib/umd/services/pathCompletion.js +171 -171
  80. package/lib/umd/services/scssCompletion.js +367 -367
  81. package/lib/umd/services/scssNavigation.js +95 -95
  82. package/lib/umd/services/selectorPrinting.js +510 -510
  83. package/lib/umd/utils/arrays.js +55 -55
  84. package/lib/umd/utils/objects.js +25 -25
  85. package/lib/umd/utils/resources.js +26 -26
  86. package/lib/umd/utils/strings.js +120 -120
  87. package/package.json +13 -12
@@ -1,492 +1,492 @@
1
- /*---------------------------------------------------------------------------------------------
2
- * Copyright (c) Microsoft Corporation. All rights reserved.
3
- * Licensed under the MIT License. See License.txt in the project root for license information.
4
- *--------------------------------------------------------------------------------------------*/
5
- 'use strict';
6
- import * as nodes from '../parser/cssNodes';
7
- import { Scanner } from '../parser/cssScanner';
8
- import * as nls from 'vscode-nls';
9
- const localize = nls.loadMessageBundle();
10
- export class Element {
11
- constructor() {
12
- this.parent = null;
13
- this.children = null;
14
- this.attributes = null;
15
- }
16
- findAttribute(name) {
17
- if (this.attributes) {
18
- for (const attribute of this.attributes) {
19
- if (attribute.name === name) {
20
- return attribute.value;
21
- }
22
- }
23
- }
24
- return null;
25
- }
26
- addChild(child) {
27
- if (child instanceof Element) {
28
- child.parent = this;
29
- }
30
- if (!this.children) {
31
- this.children = [];
32
- }
33
- this.children.push(child);
34
- }
35
- append(text) {
36
- if (this.attributes) {
37
- const last = this.attributes[this.attributes.length - 1];
38
- last.value = last.value + text;
39
- }
40
- }
41
- prepend(text) {
42
- if (this.attributes) {
43
- const first = this.attributes[0];
44
- first.value = text + first.value;
45
- }
46
- }
47
- findRoot() {
48
- let curr = this;
49
- while (curr.parent && !(curr.parent instanceof RootElement)) {
50
- curr = curr.parent;
51
- }
52
- return curr;
53
- }
54
- removeChild(child) {
55
- if (this.children) {
56
- const index = this.children.indexOf(child);
57
- if (index !== -1) {
58
- this.children.splice(index, 1);
59
- return true;
60
- }
61
- }
62
- return false;
63
- }
64
- addAttr(name, value) {
65
- if (!this.attributes) {
66
- this.attributes = [];
67
- }
68
- for (const attribute of this.attributes) {
69
- if (attribute.name === name) {
70
- attribute.value += ' ' + value;
71
- return;
72
- }
73
- }
74
- this.attributes.push({ name, value });
75
- }
76
- clone(cloneChildren = true) {
77
- const elem = new Element();
78
- if (this.attributes) {
79
- elem.attributes = [];
80
- for (const attribute of this.attributes) {
81
- elem.addAttr(attribute.name, attribute.value);
82
- }
83
- }
84
- if (cloneChildren && this.children) {
85
- elem.children = [];
86
- for (let index = 0; index < this.children.length; index++) {
87
- elem.addChild(this.children[index].clone());
88
- }
89
- }
90
- return elem;
91
- }
92
- cloneWithParent() {
93
- const clone = this.clone(false);
94
- if (this.parent && !(this.parent instanceof RootElement)) {
95
- const parentClone = this.parent.cloneWithParent();
96
- parentClone.addChild(clone);
97
- }
98
- return clone;
99
- }
100
- }
101
- export class RootElement extends Element {
102
- }
103
- export class LabelElement extends Element {
104
- constructor(label) {
105
- super();
106
- this.addAttr('name', label);
107
- }
108
- }
109
- class MarkedStringPrinter {
110
- constructor(quote) {
111
- this.quote = quote;
112
- this.result = [];
113
- // empty
114
- }
115
- print(element) {
116
- this.result = [];
117
- if (element instanceof RootElement) {
118
- if (element.children) {
119
- this.doPrint(element.children, 0);
120
- }
121
- }
122
- else {
123
- this.doPrint([element], 0);
124
- }
125
- const value = this.result.join('\n');
126
- return [{ language: 'html', value }];
127
- }
128
- doPrint(elements, indent) {
129
- for (const element of elements) {
130
- this.doPrintElement(element, indent);
131
- if (element.children) {
132
- this.doPrint(element.children, indent + 1);
133
- }
134
- }
135
- }
136
- writeLine(level, content) {
137
- const indent = new Array(level + 1).join(' ');
138
- this.result.push(indent + content);
139
- }
140
- doPrintElement(element, indent) {
141
- const name = element.findAttribute('name');
142
- // special case: a simple label
143
- if (element instanceof LabelElement || name === '\u2026') {
144
- this.writeLine(indent, name);
145
- return;
146
- }
147
- // the real deal
148
- const content = ['<'];
149
- // element name
150
- if (name) {
151
- content.push(name);
152
- }
153
- else {
154
- content.push('element');
155
- }
156
- // attributes
157
- if (element.attributes) {
158
- for (const attr of element.attributes) {
159
- if (attr.name !== 'name') {
160
- content.push(' ');
161
- content.push(attr.name);
162
- const value = attr.value;
163
- if (value) {
164
- content.push('=');
165
- content.push(quotes.ensure(value, this.quote));
166
- }
167
- }
168
- }
169
- }
170
- content.push('>');
171
- this.writeLine(indent, content.join(''));
172
- }
173
- }
174
- var quotes;
175
- (function (quotes) {
176
- function ensure(value, which) {
177
- return which + remove(value) + which;
178
- }
179
- quotes.ensure = ensure;
180
- function remove(value) {
181
- const match = value.match(/^['"](.*)["']$/);
182
- if (match) {
183
- return match[1];
184
- }
185
- return value;
186
- }
187
- quotes.remove = remove;
188
- })(quotes || (quotes = {}));
189
- class Specificity {
190
- constructor() {
191
- /** Count of identifiers (e.g., `#app`) */
192
- this.id = 0;
193
- /** Count of attributes (`[type="number"]`), classes (`.container-fluid`), and pseudo-classes (`:hover`) */
194
- this.attr = 0;
195
- /** Count of tag names (`div`), and pseudo-elements (`::before`) */
196
- this.tag = 0;
197
- }
198
- }
199
- export function toElement(node, parentElement) {
200
- let result = new Element();
201
- for (const child of node.getChildren()) {
202
- switch (child.type) {
203
- case nodes.NodeType.SelectorCombinator:
204
- if (parentElement) {
205
- const segments = child.getText().split('&');
206
- if (segments.length === 1) {
207
- // should not happen
208
- result.addAttr('name', segments[0]);
209
- break;
210
- }
211
- result = parentElement.cloneWithParent();
212
- if (segments[0]) {
213
- const root = result.findRoot();
214
- root.prepend(segments[0]);
215
- }
216
- for (let i = 1; i < segments.length; i++) {
217
- if (i > 1) {
218
- const clone = parentElement.cloneWithParent();
219
- result.addChild(clone.findRoot());
220
- result = clone;
221
- }
222
- result.append(segments[i]);
223
- }
224
- }
225
- break;
226
- case nodes.NodeType.SelectorPlaceholder:
227
- if (child.matches('@at-root')) {
228
- return result;
229
- }
230
- // fall through
231
- case nodes.NodeType.ElementNameSelector:
232
- const text = child.getText();
233
- result.addAttr('name', text === '*' ? 'element' : unescape(text));
234
- break;
235
- case nodes.NodeType.ClassSelector:
236
- result.addAttr('class', unescape(child.getText().substring(1)));
237
- break;
238
- case nodes.NodeType.IdentifierSelector:
239
- result.addAttr('id', unescape(child.getText().substring(1)));
240
- break;
241
- case nodes.NodeType.MixinDeclaration:
242
- result.addAttr('class', child.getName());
243
- break;
244
- case nodes.NodeType.PseudoSelector:
245
- result.addAttr(unescape(child.getText()), '');
246
- break;
247
- case nodes.NodeType.AttributeSelector:
248
- const selector = child;
249
- const identifier = selector.getIdentifier();
250
- if (identifier) {
251
- const expression = selector.getValue();
252
- const operator = selector.getOperator();
253
- let value;
254
- if (expression && operator) {
255
- switch (unescape(operator.getText())) {
256
- case '|=':
257
- // excatly or followed by -words
258
- value = `${quotes.remove(unescape(expression.getText()))}-\u2026`;
259
- break;
260
- case '^=':
261
- // prefix
262
- value = `${quotes.remove(unescape(expression.getText()))}\u2026`;
263
- break;
264
- case '$=':
265
- // suffix
266
- value = `\u2026${quotes.remove(unescape(expression.getText()))}`;
267
- break;
268
- case '~=':
269
- // one of a list of words
270
- value = ` \u2026 ${quotes.remove(unescape(expression.getText()))} \u2026 `;
271
- break;
272
- case '*=':
273
- // substring
274
- value = `\u2026${quotes.remove(unescape(expression.getText()))}\u2026`;
275
- break;
276
- default:
277
- value = quotes.remove(unescape(expression.getText()));
278
- break;
279
- }
280
- }
281
- result.addAttr(unescape(identifier.getText()), value);
282
- }
283
- break;
284
- }
285
- }
286
- return result;
287
- }
288
- function unescape(content) {
289
- const scanner = new Scanner();
290
- scanner.setSource(content);
291
- const token = scanner.scanUnquotedString();
292
- if (token) {
293
- return token.text;
294
- }
295
- return content;
296
- }
297
- export class SelectorPrinting {
298
- constructor(cssDataManager) {
299
- this.cssDataManager = cssDataManager;
300
- }
301
- selectorToMarkedString(node) {
302
- const root = selectorToElement(node);
303
- if (root) {
304
- const markedStrings = new MarkedStringPrinter('"').print(root);
305
- markedStrings.push(this.selectorToSpecificityMarkedString(node));
306
- return markedStrings;
307
- }
308
- else {
309
- return [];
310
- }
311
- }
312
- simpleSelectorToMarkedString(node) {
313
- const element = toElement(node);
314
- const markedStrings = new MarkedStringPrinter('"').print(element);
315
- markedStrings.push(this.selectorToSpecificityMarkedString(node));
316
- return markedStrings;
317
- }
318
- isPseudoElementIdentifier(text) {
319
- const match = text.match(/^::?([\w-]+)/);
320
- if (!match) {
321
- return false;
322
- }
323
- return !!this.cssDataManager.getPseudoElement("::" + match[1]);
324
- }
325
- selectorToSpecificityMarkedString(node) {
326
- //https://www.w3.org/TR/selectors-3/#specificity
327
- const calculateScore = (node) => {
328
- const specificity = new Specificity();
329
- elementLoop: for (const element of node.getChildren()) {
330
- switch (element.type) {
331
- case nodes.NodeType.IdentifierSelector:
332
- specificity.id++;
333
- break;
334
- case nodes.NodeType.ClassSelector:
335
- case nodes.NodeType.AttributeSelector:
336
- specificity.attr++;
337
- break;
338
- case nodes.NodeType.ElementNameSelector:
339
- //ignore universal selector
340
- if (element.matches("*")) {
341
- break;
342
- }
343
- specificity.tag++;
344
- break;
345
- case nodes.NodeType.PseudoSelector:
346
- const text = element.getText();
347
- if (this.isPseudoElementIdentifier(text)) {
348
- specificity.tag++; // pseudo element
349
- continue elementLoop;
350
- }
351
- // where and child selectors have zero specificity
352
- if (text.match(/^:where/i)) {
353
- continue elementLoop;
354
- }
355
- // the most specific child selector
356
- if (text.match(/^:(not|has|is)/i) && element.getChildren().length > 0) {
357
- let mostSpecificListItem = new Specificity();
358
- for (const containerElement of element.getChildren()) {
359
- let list;
360
- if (containerElement.type === nodes.NodeType.Undefined) { // containerElement is a list of selectors
361
- list = containerElement.getChildren();
362
- }
363
- else { // containerElement is a selector
364
- list = [containerElement];
365
- }
366
- for (const childElement of containerElement.getChildren()) {
367
- const itemSpecificity = calculateScore(childElement);
368
- if (itemSpecificity.id > mostSpecificListItem.id) {
369
- mostSpecificListItem = itemSpecificity;
370
- continue;
371
- }
372
- else if (itemSpecificity.id < mostSpecificListItem.id) {
373
- continue;
374
- }
375
- if (itemSpecificity.attr > mostSpecificListItem.attr) {
376
- mostSpecificListItem = itemSpecificity;
377
- continue;
378
- }
379
- else if (itemSpecificity.attr < mostSpecificListItem.attr) {
380
- continue;
381
- }
382
- if (itemSpecificity.tag > mostSpecificListItem.tag) {
383
- mostSpecificListItem = itemSpecificity;
384
- continue;
385
- }
386
- }
387
- }
388
- specificity.id += mostSpecificListItem.id;
389
- specificity.attr += mostSpecificListItem.attr;
390
- specificity.tag += mostSpecificListItem.tag;
391
- continue elementLoop;
392
- }
393
- specificity.attr++; //pseudo class
394
- continue elementLoop;
395
- }
396
- if (element.getChildren().length > 0) {
397
- const itemSpecificity = calculateScore(element);
398
- specificity.id += itemSpecificity.id;
399
- specificity.attr += itemSpecificity.attr;
400
- specificity.tag += itemSpecificity.tag;
401
- }
402
- }
403
- return specificity;
404
- };
405
- const specificity = calculateScore(node);
406
- ;
407
- return localize('specificity', "[Selector Specificity](https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity): ({0}, {1}, {2})", specificity.id, specificity.attr, specificity.tag);
408
- }
409
- }
410
- class SelectorElementBuilder {
411
- constructor(element) {
412
- this.prev = null;
413
- this.element = element;
414
- }
415
- processSelector(selector) {
416
- let parentElement = null;
417
- if (!(this.element instanceof RootElement)) {
418
- if (selector.getChildren().some((c) => c.hasChildren() && c.getChild(0).type === nodes.NodeType.SelectorCombinator)) {
419
- const curr = this.element.findRoot();
420
- if (curr.parent instanceof RootElement) {
421
- parentElement = this.element;
422
- this.element = curr.parent;
423
- this.element.removeChild(curr);
424
- this.prev = null;
425
- }
426
- }
427
- }
428
- for (const selectorChild of selector.getChildren()) {
429
- if (selectorChild instanceof nodes.SimpleSelector) {
430
- if (this.prev instanceof nodes.SimpleSelector) {
431
- const labelElement = new LabelElement('\u2026');
432
- this.element.addChild(labelElement);
433
- this.element = labelElement;
434
- }
435
- else if (this.prev && (this.prev.matches('+') || this.prev.matches('~')) && this.element.parent) {
436
- this.element = this.element.parent;
437
- }
438
- if (this.prev && this.prev.matches('~')) {
439
- this.element.addChild(new LabelElement('\u22EE'));
440
- }
441
- const thisElement = toElement(selectorChild, parentElement);
442
- const root = thisElement.findRoot();
443
- this.element.addChild(root);
444
- this.element = thisElement;
445
- }
446
- if (selectorChild instanceof nodes.SimpleSelector ||
447
- selectorChild.type === nodes.NodeType.SelectorCombinatorParent ||
448
- selectorChild.type === nodes.NodeType.SelectorCombinatorShadowPiercingDescendant ||
449
- selectorChild.type === nodes.NodeType.SelectorCombinatorSibling ||
450
- selectorChild.type === nodes.NodeType.SelectorCombinatorAllSiblings) {
451
- this.prev = selectorChild;
452
- }
453
- }
454
- }
455
- }
456
- function isNewSelectorContext(node) {
457
- switch (node.type) {
458
- case nodes.NodeType.MixinDeclaration:
459
- case nodes.NodeType.Stylesheet:
460
- return true;
461
- }
462
- return false;
463
- }
464
- export function selectorToElement(node) {
465
- if (node.matches('@at-root')) {
466
- return null;
467
- }
468
- const root = new RootElement();
469
- const parentRuleSets = [];
470
- const ruleSet = node.getParent();
471
- if (ruleSet instanceof nodes.RuleSet) {
472
- let parent = ruleSet.getParent(); // parent of the selector's ruleset
473
- while (parent && !isNewSelectorContext(parent)) {
474
- if (parent instanceof nodes.RuleSet) {
475
- if (parent.getSelectors().matches('@at-root')) {
476
- break;
477
- }
478
- parentRuleSets.push(parent);
479
- }
480
- parent = parent.getParent();
481
- }
482
- }
483
- const builder = new SelectorElementBuilder(root);
484
- for (let i = parentRuleSets.length - 1; i >= 0; i--) {
485
- const selector = parentRuleSets[i].getSelectors().getChild(0);
486
- if (selector) {
487
- builder.processSelector(selector);
488
- }
489
- }
490
- builder.processSelector(node);
491
- return root;
492
- }
1
+ /*---------------------------------------------------------------------------------------------
2
+ * Copyright (c) Microsoft Corporation. All rights reserved.
3
+ * Licensed under the MIT License. See License.txt in the project root for license information.
4
+ *--------------------------------------------------------------------------------------------*/
5
+ 'use strict';
6
+ import * as nodes from '../parser/cssNodes';
7
+ import { Scanner } from '../parser/cssScanner';
8
+ import * as nls from 'vscode-nls';
9
+ const localize = nls.loadMessageBundle();
10
+ export class Element {
11
+ constructor() {
12
+ this.parent = null;
13
+ this.children = null;
14
+ this.attributes = null;
15
+ }
16
+ findAttribute(name) {
17
+ if (this.attributes) {
18
+ for (const attribute of this.attributes) {
19
+ if (attribute.name === name) {
20
+ return attribute.value;
21
+ }
22
+ }
23
+ }
24
+ return null;
25
+ }
26
+ addChild(child) {
27
+ if (child instanceof Element) {
28
+ child.parent = this;
29
+ }
30
+ if (!this.children) {
31
+ this.children = [];
32
+ }
33
+ this.children.push(child);
34
+ }
35
+ append(text) {
36
+ if (this.attributes) {
37
+ const last = this.attributes[this.attributes.length - 1];
38
+ last.value = last.value + text;
39
+ }
40
+ }
41
+ prepend(text) {
42
+ if (this.attributes) {
43
+ const first = this.attributes[0];
44
+ first.value = text + first.value;
45
+ }
46
+ }
47
+ findRoot() {
48
+ let curr = this;
49
+ while (curr.parent && !(curr.parent instanceof RootElement)) {
50
+ curr = curr.parent;
51
+ }
52
+ return curr;
53
+ }
54
+ removeChild(child) {
55
+ if (this.children) {
56
+ const index = this.children.indexOf(child);
57
+ if (index !== -1) {
58
+ this.children.splice(index, 1);
59
+ return true;
60
+ }
61
+ }
62
+ return false;
63
+ }
64
+ addAttr(name, value) {
65
+ if (!this.attributes) {
66
+ this.attributes = [];
67
+ }
68
+ for (const attribute of this.attributes) {
69
+ if (attribute.name === name) {
70
+ attribute.value += ' ' + value;
71
+ return;
72
+ }
73
+ }
74
+ this.attributes.push({ name, value });
75
+ }
76
+ clone(cloneChildren = true) {
77
+ const elem = new Element();
78
+ if (this.attributes) {
79
+ elem.attributes = [];
80
+ for (const attribute of this.attributes) {
81
+ elem.addAttr(attribute.name, attribute.value);
82
+ }
83
+ }
84
+ if (cloneChildren && this.children) {
85
+ elem.children = [];
86
+ for (let index = 0; index < this.children.length; index++) {
87
+ elem.addChild(this.children[index].clone());
88
+ }
89
+ }
90
+ return elem;
91
+ }
92
+ cloneWithParent() {
93
+ const clone = this.clone(false);
94
+ if (this.parent && !(this.parent instanceof RootElement)) {
95
+ const parentClone = this.parent.cloneWithParent();
96
+ parentClone.addChild(clone);
97
+ }
98
+ return clone;
99
+ }
100
+ }
101
+ export class RootElement extends Element {
102
+ }
103
+ export class LabelElement extends Element {
104
+ constructor(label) {
105
+ super();
106
+ this.addAttr('name', label);
107
+ }
108
+ }
109
+ class MarkedStringPrinter {
110
+ constructor(quote) {
111
+ this.quote = quote;
112
+ this.result = [];
113
+ // empty
114
+ }
115
+ print(element) {
116
+ this.result = [];
117
+ if (element instanceof RootElement) {
118
+ if (element.children) {
119
+ this.doPrint(element.children, 0);
120
+ }
121
+ }
122
+ else {
123
+ this.doPrint([element], 0);
124
+ }
125
+ const value = this.result.join('\n');
126
+ return [{ language: 'html', value }];
127
+ }
128
+ doPrint(elements, indent) {
129
+ for (const element of elements) {
130
+ this.doPrintElement(element, indent);
131
+ if (element.children) {
132
+ this.doPrint(element.children, indent + 1);
133
+ }
134
+ }
135
+ }
136
+ writeLine(level, content) {
137
+ const indent = new Array(level + 1).join(' ');
138
+ this.result.push(indent + content);
139
+ }
140
+ doPrintElement(element, indent) {
141
+ const name = element.findAttribute('name');
142
+ // special case: a simple label
143
+ if (element instanceof LabelElement || name === '\u2026') {
144
+ this.writeLine(indent, name);
145
+ return;
146
+ }
147
+ // the real deal
148
+ const content = ['<'];
149
+ // element name
150
+ if (name) {
151
+ content.push(name);
152
+ }
153
+ else {
154
+ content.push('element');
155
+ }
156
+ // attributes
157
+ if (element.attributes) {
158
+ for (const attr of element.attributes) {
159
+ if (attr.name !== 'name') {
160
+ content.push(' ');
161
+ content.push(attr.name);
162
+ const value = attr.value;
163
+ if (value) {
164
+ content.push('=');
165
+ content.push(quotes.ensure(value, this.quote));
166
+ }
167
+ }
168
+ }
169
+ }
170
+ content.push('>');
171
+ this.writeLine(indent, content.join(''));
172
+ }
173
+ }
174
+ var quotes;
175
+ (function (quotes) {
176
+ function ensure(value, which) {
177
+ return which + remove(value) + which;
178
+ }
179
+ quotes.ensure = ensure;
180
+ function remove(value) {
181
+ const match = value.match(/^['"](.*)["']$/);
182
+ if (match) {
183
+ return match[1];
184
+ }
185
+ return value;
186
+ }
187
+ quotes.remove = remove;
188
+ })(quotes || (quotes = {}));
189
+ class Specificity {
190
+ constructor() {
191
+ /** Count of identifiers (e.g., `#app`) */
192
+ this.id = 0;
193
+ /** Count of attributes (`[type="number"]`), classes (`.container-fluid`), and pseudo-classes (`:hover`) */
194
+ this.attr = 0;
195
+ /** Count of tag names (`div`), and pseudo-elements (`::before`) */
196
+ this.tag = 0;
197
+ }
198
+ }
199
+ export function toElement(node, parentElement) {
200
+ let result = new Element();
201
+ for (const child of node.getChildren()) {
202
+ switch (child.type) {
203
+ case nodes.NodeType.SelectorCombinator:
204
+ if (parentElement) {
205
+ const segments = child.getText().split('&');
206
+ if (segments.length === 1) {
207
+ // should not happen
208
+ result.addAttr('name', segments[0]);
209
+ break;
210
+ }
211
+ result = parentElement.cloneWithParent();
212
+ if (segments[0]) {
213
+ const root = result.findRoot();
214
+ root.prepend(segments[0]);
215
+ }
216
+ for (let i = 1; i < segments.length; i++) {
217
+ if (i > 1) {
218
+ const clone = parentElement.cloneWithParent();
219
+ result.addChild(clone.findRoot());
220
+ result = clone;
221
+ }
222
+ result.append(segments[i]);
223
+ }
224
+ }
225
+ break;
226
+ case nodes.NodeType.SelectorPlaceholder:
227
+ if (child.matches('@at-root')) {
228
+ return result;
229
+ }
230
+ // fall through
231
+ case nodes.NodeType.ElementNameSelector:
232
+ const text = child.getText();
233
+ result.addAttr('name', text === '*' ? 'element' : unescape(text));
234
+ break;
235
+ case nodes.NodeType.ClassSelector:
236
+ result.addAttr('class', unescape(child.getText().substring(1)));
237
+ break;
238
+ case nodes.NodeType.IdentifierSelector:
239
+ result.addAttr('id', unescape(child.getText().substring(1)));
240
+ break;
241
+ case nodes.NodeType.MixinDeclaration:
242
+ result.addAttr('class', child.getName());
243
+ break;
244
+ case nodes.NodeType.PseudoSelector:
245
+ result.addAttr(unescape(child.getText()), '');
246
+ break;
247
+ case nodes.NodeType.AttributeSelector:
248
+ const selector = child;
249
+ const identifier = selector.getIdentifier();
250
+ if (identifier) {
251
+ const expression = selector.getValue();
252
+ const operator = selector.getOperator();
253
+ let value;
254
+ if (expression && operator) {
255
+ switch (unescape(operator.getText())) {
256
+ case '|=':
257
+ // excatly or followed by -words
258
+ value = `${quotes.remove(unescape(expression.getText()))}-\u2026`;
259
+ break;
260
+ case '^=':
261
+ // prefix
262
+ value = `${quotes.remove(unescape(expression.getText()))}\u2026`;
263
+ break;
264
+ case '$=':
265
+ // suffix
266
+ value = `\u2026${quotes.remove(unescape(expression.getText()))}`;
267
+ break;
268
+ case '~=':
269
+ // one of a list of words
270
+ value = ` \u2026 ${quotes.remove(unescape(expression.getText()))} \u2026 `;
271
+ break;
272
+ case '*=':
273
+ // substring
274
+ value = `\u2026${quotes.remove(unescape(expression.getText()))}\u2026`;
275
+ break;
276
+ default:
277
+ value = quotes.remove(unescape(expression.getText()));
278
+ break;
279
+ }
280
+ }
281
+ result.addAttr(unescape(identifier.getText()), value);
282
+ }
283
+ break;
284
+ }
285
+ }
286
+ return result;
287
+ }
288
+ function unescape(content) {
289
+ const scanner = new Scanner();
290
+ scanner.setSource(content);
291
+ const token = scanner.scanUnquotedString();
292
+ if (token) {
293
+ return token.text;
294
+ }
295
+ return content;
296
+ }
297
+ export class SelectorPrinting {
298
+ constructor(cssDataManager) {
299
+ this.cssDataManager = cssDataManager;
300
+ }
301
+ selectorToMarkedString(node) {
302
+ const root = selectorToElement(node);
303
+ if (root) {
304
+ const markedStrings = new MarkedStringPrinter('"').print(root);
305
+ markedStrings.push(this.selectorToSpecificityMarkedString(node));
306
+ return markedStrings;
307
+ }
308
+ else {
309
+ return [];
310
+ }
311
+ }
312
+ simpleSelectorToMarkedString(node) {
313
+ const element = toElement(node);
314
+ const markedStrings = new MarkedStringPrinter('"').print(element);
315
+ markedStrings.push(this.selectorToSpecificityMarkedString(node));
316
+ return markedStrings;
317
+ }
318
+ isPseudoElementIdentifier(text) {
319
+ const match = text.match(/^::?([\w-]+)/);
320
+ if (!match) {
321
+ return false;
322
+ }
323
+ return !!this.cssDataManager.getPseudoElement("::" + match[1]);
324
+ }
325
+ selectorToSpecificityMarkedString(node) {
326
+ //https://www.w3.org/TR/selectors-3/#specificity
327
+ const calculateScore = (node) => {
328
+ const specificity = new Specificity();
329
+ elementLoop: for (const element of node.getChildren()) {
330
+ switch (element.type) {
331
+ case nodes.NodeType.IdentifierSelector:
332
+ specificity.id++;
333
+ break;
334
+ case nodes.NodeType.ClassSelector:
335
+ case nodes.NodeType.AttributeSelector:
336
+ specificity.attr++;
337
+ break;
338
+ case nodes.NodeType.ElementNameSelector:
339
+ //ignore universal selector
340
+ if (element.matches("*")) {
341
+ break;
342
+ }
343
+ specificity.tag++;
344
+ break;
345
+ case nodes.NodeType.PseudoSelector:
346
+ const text = element.getText();
347
+ if (this.isPseudoElementIdentifier(text)) {
348
+ specificity.tag++; // pseudo element
349
+ continue elementLoop;
350
+ }
351
+ // where and child selectors have zero specificity
352
+ if (text.match(/^:where/i)) {
353
+ continue elementLoop;
354
+ }
355
+ // the most specific child selector
356
+ if (text.match(/^:(not|has|is)/i) && element.getChildren().length > 0) {
357
+ let mostSpecificListItem = new Specificity();
358
+ for (const containerElement of element.getChildren()) {
359
+ let list;
360
+ if (containerElement.type === nodes.NodeType.Undefined) { // containerElement is a list of selectors
361
+ list = containerElement.getChildren();
362
+ }
363
+ else { // containerElement is a selector
364
+ list = [containerElement];
365
+ }
366
+ for (const childElement of containerElement.getChildren()) {
367
+ const itemSpecificity = calculateScore(childElement);
368
+ if (itemSpecificity.id > mostSpecificListItem.id) {
369
+ mostSpecificListItem = itemSpecificity;
370
+ continue;
371
+ }
372
+ else if (itemSpecificity.id < mostSpecificListItem.id) {
373
+ continue;
374
+ }
375
+ if (itemSpecificity.attr > mostSpecificListItem.attr) {
376
+ mostSpecificListItem = itemSpecificity;
377
+ continue;
378
+ }
379
+ else if (itemSpecificity.attr < mostSpecificListItem.attr) {
380
+ continue;
381
+ }
382
+ if (itemSpecificity.tag > mostSpecificListItem.tag) {
383
+ mostSpecificListItem = itemSpecificity;
384
+ continue;
385
+ }
386
+ }
387
+ }
388
+ specificity.id += mostSpecificListItem.id;
389
+ specificity.attr += mostSpecificListItem.attr;
390
+ specificity.tag += mostSpecificListItem.tag;
391
+ continue elementLoop;
392
+ }
393
+ specificity.attr++; //pseudo class
394
+ continue elementLoop;
395
+ }
396
+ if (element.getChildren().length > 0) {
397
+ const itemSpecificity = calculateScore(element);
398
+ specificity.id += itemSpecificity.id;
399
+ specificity.attr += itemSpecificity.attr;
400
+ specificity.tag += itemSpecificity.tag;
401
+ }
402
+ }
403
+ return specificity;
404
+ };
405
+ const specificity = calculateScore(node);
406
+ ;
407
+ return localize('specificity', "[Selector Specificity](https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity): ({0}, {1}, {2})", specificity.id, specificity.attr, specificity.tag);
408
+ }
409
+ }
410
+ class SelectorElementBuilder {
411
+ constructor(element) {
412
+ this.prev = null;
413
+ this.element = element;
414
+ }
415
+ processSelector(selector) {
416
+ let parentElement = null;
417
+ if (!(this.element instanceof RootElement)) {
418
+ if (selector.getChildren().some((c) => c.hasChildren() && c.getChild(0).type === nodes.NodeType.SelectorCombinator)) {
419
+ const curr = this.element.findRoot();
420
+ if (curr.parent instanceof RootElement) {
421
+ parentElement = this.element;
422
+ this.element = curr.parent;
423
+ this.element.removeChild(curr);
424
+ this.prev = null;
425
+ }
426
+ }
427
+ }
428
+ for (const selectorChild of selector.getChildren()) {
429
+ if (selectorChild instanceof nodes.SimpleSelector) {
430
+ if (this.prev instanceof nodes.SimpleSelector) {
431
+ const labelElement = new LabelElement('\u2026');
432
+ this.element.addChild(labelElement);
433
+ this.element = labelElement;
434
+ }
435
+ else if (this.prev && (this.prev.matches('+') || this.prev.matches('~')) && this.element.parent) {
436
+ this.element = this.element.parent;
437
+ }
438
+ if (this.prev && this.prev.matches('~')) {
439
+ this.element.addChild(new LabelElement('\u22EE'));
440
+ }
441
+ const thisElement = toElement(selectorChild, parentElement);
442
+ const root = thisElement.findRoot();
443
+ this.element.addChild(root);
444
+ this.element = thisElement;
445
+ }
446
+ if (selectorChild instanceof nodes.SimpleSelector ||
447
+ selectorChild.type === nodes.NodeType.SelectorCombinatorParent ||
448
+ selectorChild.type === nodes.NodeType.SelectorCombinatorShadowPiercingDescendant ||
449
+ selectorChild.type === nodes.NodeType.SelectorCombinatorSibling ||
450
+ selectorChild.type === nodes.NodeType.SelectorCombinatorAllSiblings) {
451
+ this.prev = selectorChild;
452
+ }
453
+ }
454
+ }
455
+ }
456
+ function isNewSelectorContext(node) {
457
+ switch (node.type) {
458
+ case nodes.NodeType.MixinDeclaration:
459
+ case nodes.NodeType.Stylesheet:
460
+ return true;
461
+ }
462
+ return false;
463
+ }
464
+ export function selectorToElement(node) {
465
+ if (node.matches('@at-root')) {
466
+ return null;
467
+ }
468
+ const root = new RootElement();
469
+ const parentRuleSets = [];
470
+ const ruleSet = node.getParent();
471
+ if (ruleSet instanceof nodes.RuleSet) {
472
+ let parent = ruleSet.getParent(); // parent of the selector's ruleset
473
+ while (parent && !isNewSelectorContext(parent)) {
474
+ if (parent instanceof nodes.RuleSet) {
475
+ if (parent.getSelectors().matches('@at-root')) {
476
+ break;
477
+ }
478
+ parentRuleSets.push(parent);
479
+ }
480
+ parent = parent.getParent();
481
+ }
482
+ }
483
+ const builder = new SelectorElementBuilder(root);
484
+ for (let i = parentRuleSets.length - 1; i >= 0; i--) {
485
+ const selector = parentRuleSets[i].getSelectors().getChild(0);
486
+ if (selector) {
487
+ builder.processSelector(selector);
488
+ }
489
+ }
490
+ builder.processSelector(node);
491
+ return root;
492
+ }