Skip to content

Commit

Permalink
support @container, prepare 6.2.10 (#365)
Browse files Browse the repository at this point in the history
* update dependencies

* support for @container

* prepare 6.2.10
  • Loading branch information
aeschli authored Oct 5, 2023
1 parent 9d26829 commit 05e3631
Show file tree
Hide file tree
Showing 5 changed files with 303 additions and 118 deletions.
16 changes: 8 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "vscode-css-languageservice",
"version": "6.2.9",
"version": "6.2.10",
"description": "Language service for CSS, LESS and SCSS",
"main": "./lib/umd/cssLanguageService.js",
"typings": "./lib/umd/cssLanguageService",
Expand All @@ -15,23 +15,23 @@
"url": "https://github.com/Microsoft/vscode-css-languageservice"
},
"devDependencies": {
"@types/mocha": "^10.0.1",
"@types/mocha": "^10.0.2",
"@types/node": "16.x",
"@typescript-eslint/eslint-plugin": "^6.7.3",
"@typescript-eslint/parser": "^6.7.3",
"@typescript-eslint/eslint-plugin": "^6.7.4",
"@typescript-eslint/parser": "^6.7.4",
"@vscode/web-custom-data": "^0.4.8",
"eslint": "^8.50.0",
"js-beautify": "^1.14.9",
"mocha": "^10.2.0",
"rimraf": "^5.0.1",
"rimraf": "^5.0.5",
"source-map-support": "^0.5.21",
"typescript": "^5.2.2"
},
"dependencies": {
"@vscode/l10n": "^0.0.16",
"vscode-languageserver-textdocument": "^1.0.8",
"vscode-languageserver-types": "3.17.3",
"vscode-uri": "^3.0.7"
"vscode-languageserver-textdocument": "^1.0.11",
"vscode-languageserver-types": "3.17.5",
"vscode-uri": "^3.0.8"
},
"scripts": {
"prepack": "npm run clean && npm run compile-esm && npm run test && npm run remove-sourcemap-refs",
Expand Down
14 changes: 13 additions & 1 deletion src/parser/cssNodes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,8 @@ export enum NodeType {
Layer,
LayerNameList,
LayerName,
PropertyAtRule
PropertyAtRule,
Container
}

export enum ReferenceType {
Expand Down Expand Up @@ -1239,6 +1240,17 @@ export class Document extends BodyDeclaration {
}
}

export class Container extends BodyDeclaration {

constructor(offset: number, length: number) {
super(offset, length);
}

public get type(): NodeType {
return NodeType.Container;
}
}

export class Medialist extends Node {
constructor(offset: number, length: number) {
super(offset, length);
Expand Down
105 changes: 105 additions & 0 deletions src/parser/cssParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,7 @@ export class Parser {
|| this._parseViewPort()
|| this._parseNamespace()
|| this._parseDocument()
|| this._parseContainer()
|| this._parseUnknownAtRule();
}

Expand Down Expand Up @@ -1292,6 +1293,110 @@ export class Parser {
return this._parseBody(node, this._parseStylesheetStatement.bind(this));
}

public _parseContainer(): nodes.Node | null {
if (!this.peekKeyword('@container')) {
return null;
}
const node = this.create(nodes.Container);
this.consumeToken(); // @container

node.addChild(this._parseIdent()); // optional container name
node.addChild(this._parseContainerQuery());

return this._parseBody(node, this._parseStylesheetStatement.bind(this));
}

public _parseContainerQuery(): nodes.Node | null {
// <container-query> = not <query-in-parens>
// | <query-in-parens> [ [ and <query-in-parens> ]* | [ or <query-in-parens> ]* ]
const node = this.create(nodes.Node);
if (this.acceptIdent('not')) {
node.addChild(this._parseContainerQueryInParens());
} else {
node.addChild(this._parseContainerQueryInParens());
if (this.peekIdent('and')) {
while (this.acceptIdent('and')) {
node.addChild(this._parseContainerQueryInParens());
}
} else if (this.peekIdent('or')) {
while (this.acceptIdent('or')) {
node.addChild(this._parseContainerQueryInParens());
}
}
}
return this.finish(node);
}

public _parseContainerQueryInParens(): nodes.Node {
// <query-in-parens> = ( <container-query> )
// | ( <size-feature> )
// | style( <style-query> )
// | <general-enclosed>
const node = this.create(nodes.Node);
if (this.accept(TokenType.ParenthesisL)) {
if (this.peekIdent('not') || this.peek(TokenType.ParenthesisL)) {
node.addChild(this._parseContainerQuery());
} else {
node.addChild(this._parseMediaFeature());
}
if (!this.accept(TokenType.ParenthesisR)) {
return this.finish(node, ParseError.RightParenthesisExpected, [], [TokenType.CurlyL]);
}
} else if (this.acceptIdent('style')) {
if (this.hasWhitespace() || !this.accept(TokenType.ParenthesisL)) {
return this.finish(node, ParseError.LeftParenthesisExpected, [], [TokenType.CurlyL]);
}
node.addChild(this._parseStyleQuery());
if (!this.accept(TokenType.ParenthesisR)) {
return this.finish(node, ParseError.RightParenthesisExpected, [], [TokenType.CurlyL]);
}
} else {
return this.finish(node, ParseError.LeftParenthesisExpected, [], [TokenType.CurlyL]);
}
return this.finish(node);
}

public _parseStyleQuery(): nodes.Node {
// <style-query> = not <style-in-parens>
// | <style-in-parens> [ [ and <style-in-parens> ]* | [ or <style-in-parens> ]* ]
// | <style-feature>
// <style-in-parens> = ( <style-query> )
// | ( <style-feature> )
// | <general-enclosed>
const node = this.create(nodes.Node);

if (this.acceptIdent('not')) {
node.addChild(this._parseStyleInParens());
} else if (this.peek(TokenType.ParenthesisL)) {
node.addChild(this._parseStyleInParens());
if (this.peekIdent('and')) {
while (this.acceptIdent('and')) {
node.addChild(this._parseStyleInParens());
}
} else if (this.peekIdent('or')) {
while (this.acceptIdent('or')) {
node.addChild(this._parseStyleInParens());
}
}
} else {
node.addChild(this._parseDeclaration([TokenType.ParenthesisR]));
}
return this.finish(node);
}

public _parseStyleInParens(): nodes.Node {
const node = this.create(nodes.Node);
if (this.accept(TokenType.ParenthesisL)) {
node.addChild(this._parseStyleQuery());
if (!this.accept(TokenType.ParenthesisR)) {
return this.finish(node, ParseError.RightParenthesisExpected, [], [TokenType.CurlyL]);
}
} else {
return this.finish(node, ParseError.LeftParenthesisExpected, [], [TokenType.CurlyL]);
}
return this.finish(node);
}

// https://www.w3.org/TR/css-syntax-3/#consume-an-at-rule
public _parseUnknownAtRule(): nodes.Node | null {
if (!this.peek(TokenType.AtKeyword)) {
Expand Down
7 changes: 7 additions & 0 deletions src/test/css/parser.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,13 @@ suite('CSS - Parser', () => {
assertError(`@property { }`, parser, parser._parseStylesheet.bind(parser), ParseError.IdentifierExpected);
});

test('@container', function () {
const parser = new Parser();
assertNode(`@container (width <= 150px) { #inner { background-color: skyblue; }}`, parser, parser._parseStylesheet.bind(parser));
assertNode(`@container card (inline-size > 30em) and style(--responsive: true) { }`, parser, parser._parseStylesheet.bind(parser));
assertNode(`@container card (inline-size > 30em) { @container style(--responsive: true) {} }`, parser, parser._parseStylesheet.bind(parser));
});

test('@import', function () {
const parser = new Parser();
assertNode('@import "asdasdsa"', parser, parser._parseImport.bind(parser));
Expand Down
Loading

0 comments on commit 05e3631

Please sign in to comment.