vscode-css-languageservice 5.4.1 → 6.0.1

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