Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/parser/cssParser.ts
Original file line number Diff line number Diff line change
@@ -452,7 +452,7 @@ export class Parser {
return this.finish(node);
}

public _parseSelector(isNested: boolean): nodes.Selector | null {
public _parseSelector(isNested?: boolean): nodes.Selector | null {
const node = this.create(nodes.Selector);

let hasContent = false;
50 changes: 48 additions & 2 deletions src/services/selectorPrinting.ts
Original file line number Diff line number Diff line change
@@ -9,6 +9,7 @@ import { MarkedString } from '../cssLanguageTypes';
import { Scanner } from '../parser/cssScanner';
import * as l10n from '@vscode/l10n';
import { CSSDataManager } from '../languageFacts/dataManager';
import { Parser } from '../parser/cssParser';

export class Element {

@@ -456,10 +457,9 @@ export class SelectorPrinting {
continue elementLoop;
}

if (text.match(/^:(?:nth-child|nth-last-child|host|host-context)/i) && childElements.length > 0) {
if (text.match(/^:(?:host|host-context)/i) && childElements.length > 0) {
// The specificity of :host() is that of a pseudo-class, plus the specificity of its argument.
// The specificity of :host-context() is that of a pseudo-class, plus the specificity of its argument.
// The specificity of an :nth-child() or :nth-last-child() selector is the specificity of the pseudo class itself (counting as one pseudo-class selector) plus the specificity of the most specific complex selector in its selector list argument.
specificity.attr++;

let mostSpecificListItem = calculateMostSpecificListItem(childElements);
@@ -470,6 +470,52 @@ export class SelectorPrinting {
continue elementLoop;
}

if (text.match(/^:(?:nth-child|nth-last-child)/i) && childElements.length > 0) {
/* The specificity of the :nth-child(An+B [of S]?) pseudo-class is the specificity of a single pseudo-class plus, if S is specified, the specificity of the most specific complex selector in S */
// https://www.w3.org/TR/selectors-4/#the-nth-child-pseudo
specificity.attr++;

// 23 = Binary Expression.
if (childElements.length === 3 && childElements[1].type === 23) {
let mostSpecificListItem = calculateMostSpecificListItem(childElements[2].getChildren());

specificity.id += mostSpecificListItem.id;
specificity.attr += mostSpecificListItem.attr;
specificity.tag += mostSpecificListItem.tag;

continue elementLoop;
}

// Edge case: 'n' without integer prefix A, with B integer non-existent, is not regarded as a binary expression token.
const parser = new Parser();
const pseudoSelectorText = childElements[1].getText();
parser.scanner.setSource(pseudoSelectorText);
const firstToken = parser.scanner.scan();
const secondToken = parser.scanner.scan();

if (firstToken.text === 'n' || firstToken.text === '-n' && secondToken.text === 'of') {
const complexSelectorListNodes: nodes.Node[] = [];
const complexSelectorText = pseudoSelectorText.slice(secondToken.offset + 2);
const complexSelectorArray = complexSelectorText.split(',');

for (const selector of complexSelectorArray) {
const node = parser.internalParse(selector, parser._parseSelector);
if (node) {
complexSelectorListNodes.push(node);
}
}

let mostSpecificListItem = calculateMostSpecificListItem(complexSelectorListNodes);

specificity.id += mostSpecificListItem.id;
specificity.attr += mostSpecificListItem.attr;
specificity.tag += mostSpecificListItem.tag;
continue elementLoop;
}

continue elementLoop;
}

specificity.attr++; //pseudo class
continue elementLoop;
}
40 changes: 40 additions & 0 deletions src/test/css/selectorPrinting.test.ts
Original file line number Diff line number Diff line change
@@ -309,11 +309,51 @@ suite('CSS - MarkedStringPrinter selectors specificities', () => {
});

test('nth-child, nth-last-child specificity', function () {
assertSelectorMarkdown(p, '#foo:nth-child(2)', '#foo', [
{ language: 'html', value: '<element id="foo" :nth-child>' },
'[Selector Specificity](https://developer.mozilla.org/docs/Web/CSS/Specificity): (1, 1, 0)'
]);

assertSelectorMarkdown(p, '#foo:nth-child(even)', '#foo', [
{ language: 'html', value: '<element id="foo" :nth-child>' },
'[Selector Specificity](https://developer.mozilla.org/docs/Web/CSS/Specificity): (1, 1, 0)'
]);

assertSelectorMarkdown(p, '#foo:nth-child(-n + 2)', '#foo', [
{ language: 'html', value: '<element id="foo" :nth-child>' },
'[Selector Specificity](https://developer.mozilla.org/docs/Web/CSS/Specificity): (1, 1, 0)'
]);

assertSelectorMarkdown(p, '#foo:nth-child(n of.li)', '#foo', [
{ language: 'html', value: '<element id="foo" :nth-child>' },
'[Selector Specificity](https://developer.mozilla.org/docs/Web/CSS/Specificity): (1, 2, 0)'
]);

assertSelectorMarkdown(p, '#foo:nth-child(n of.li,.li.li)', '#foo', [
{ language: 'html', value: '<element id="foo" :nth-child>' },
'[Selector Specificity](https://developer.mozilla.org/docs/Web/CSS/Specificity): (1, 3, 0)'
]);

assertSelectorMarkdown(p, '#foo:nth-child(n of.li, .li.li)', '#foo', [
{ language: 'html', value: '<element id="foo" :nth-child>' },
'[Selector Specificity](https://developer.mozilla.org/docs/Web/CSS/Specificity): (1, 3, 0)'
]);

assertSelectorMarkdown(p, '#foo:nth-child(n of li)', '#foo', [
{ language: 'html', value: '<element id="foo" :nth-child>' },
'[Selector Specificity](https://developer.mozilla.org/docs/Web/CSS/Specificity): (1, 1, 1)'
]);

assertSelectorMarkdown(p, '#foo:nth-child(-n+3 of li.important)', '#foo', [
{ language: 'html', value: '<element id="foo" :nth-child>' },
'[Selector Specificity](https://developer.mozilla.org/docs/Web/CSS/Specificity): (1, 2, 1)'
]);

assertSelectorMarkdown(p, '#foo:nth-child(-n+3 of li.important, .class1.class2.class3)', '#foo', [
{ language: 'html', value: '<element id="foo" :nth-child>' },
'[Selector Specificity](https://developer.mozilla.org/docs/Web/CSS/Specificity): (1, 4, 0)'
]);

assertSelectorMarkdown(p, '#foo:nth-last-child(-n+3 of li, .important)', '#foo', [
{ language: 'html', value: '<element id="foo" :nth-last-child>' },
'[Selector Specificity](https://developer.mozilla.org/docs/Web/CSS/Specificity): (1, 2, 0)'