Skip to content

Commit

Permalink
Merge branch 'main' into #163967-css-alias
Browse files Browse the repository at this point in the history
  • Loading branch information
aeschli authored Nov 27, 2023
2 parents 896f755 + 9734506 commit 3f8932f
Show file tree
Hide file tree
Showing 8 changed files with 242 additions and 135 deletions.
12 changes: 8 additions & 4 deletions src/parser/cssParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -704,6 +704,11 @@ export class Parser {
return this.finish(node, ParseError.URIOrStringExpected);
}

return this._completeParseImport(node);
}


public _completeParseImport(node: nodes.Import): nodes.Node | null {
if (this.acceptIdent('layer')) {
if (this.accept(TokenType.ParenthesisL)) {
if (!node.addChild(this._parseLayerName())) {
Expand Down Expand Up @@ -945,11 +950,10 @@ export class Parser {

public _parseLayerName(): nodes.Node | null {
// <layer-name> = <ident> [ '.' <ident> ]*
if (!this.peek(TokenType.Ident)) {
const node = this.createNode(nodes.NodeType.LayerName);
if (!node.addChild(this._parseIdent()) ) {
return null;
}
const node = this.createNode(nodes.NodeType.LayerName);
node.addChild(this._parseIdent());
while (!this.hasWhitespace() && this.acceptDelim('.')) {
if (this.hasWhitespace() || !node.addChild(this._parseIdent())) {
return this.finish(node, ParseError.IdentifierExpected);
Expand Down
4 changes: 2 additions & 2 deletions src/parser/cssScanner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ export enum TokenType {
Comment,
SingleLineComment,
EOF,
CustomToken,
ContainerQueryLength
ContainerQueryLength,
CustomToken // must be last token type
}

export interface IToken {
Expand Down
2 changes: 1 addition & 1 deletion src/parser/lessParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export class LESSParser extends cssParser.Parser {
node.setMedialist(this._parseMediaQueryList());
}

return this.finish(node);
return this._completeParseImport(node);
}

public _parsePlugin(): nodes.Node | null {
Expand Down
9 changes: 4 additions & 5 deletions src/parser/scssParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,7 @@ export class SCSSParser extends cssParser.Parser {
}
}

if (!this.peek(TokenType.SemiColon) && !this.peek(TokenType.EOF)) {
node.setMedialist(this._parseMediaQueryList());
}

return this.finish(node);
return this._completeParseImport(node);
}

// scss variables: $font-size: 12px;
Expand Down Expand Up @@ -122,6 +118,9 @@ export class SCSSParser extends cssParser.Parser {
public _parseKeyframeSelector(): nodes.Node | null {
return this._tryParseKeyframeSelector()
|| this._parseControlStatement(this._parseKeyframeSelector.bind(this))
|| this._parseWarnAndDebug() // @warn, @debug and @error statements
|| this._parseMixinReference() // @include
|| this._parseFunctionDeclaration() // @function
|| this._parseVariableDeclaration()
|| this._parseMixinContent();
}
Expand Down
50 changes: 48 additions & 2 deletions src/services/selectorPrinting.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand Down Expand Up @@ -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);
Expand All @@ -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;
}
Expand Down
40 changes: 40 additions & 0 deletions src/test/css/selectorPrinting.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)'
Expand Down
Loading

0 comments on commit 3f8932f

Please sign in to comment.