From 098adc4f3b71b4db985d8ad43abfe0633d168428 Mon Sep 17 00:00:00 2001 From: Chris Date: Thu, 17 Sep 2026 12:49:42 +0800 Subject: [PATCH] fix(css): add @property syntax highlighting --- grammars/css.cson | 82 +++++++++++++++++++++++++++++++++++++++++++++++ spec/css-spec.mjs | 27 ++++++++++++++++ 2 files changed, 109 insertions(+) diff --git a/grammars/css.cson b/grammars/css.cson index 039f54b..6d3ec43 100644 --- a/grammars/css.cson +++ b/grammars/css.cson @@ -41,6 +41,88 @@ 'name': 'keyword.operator.arithmetic.css' 'at-rules': 'patterns': [ + { + # @property + 'begin': '(?i)(?=@property(?:[\\s{;]|/\\*|$))' + 'end': '(?<=})(?!\\G)' + 'patterns': [ + { + 'begin': '(?i)\\G(@)property' + 'beginCaptures': + '0': + 'name': 'keyword.control.at-rule.property.css' + '1': + 'name': 'punctuation.definition.keyword.css' + 'end': '(?=\\s*\\{)' + 'name': 'meta.at-rule.property.header.css' + 'patterns': [ + { + 'include': '#comment-block' + } + { + 'include': '#escapes' + } + { + 'match': '''(?x) + -- + (?:[-a-zA-Z_] | [^\\x00-\\x7F]) + (?:[-a-zA-Z0-9_] | [^\\x00-\\x7F] | \\\\(?:[0-9a-fA-F]{1,6}|.))* + ''' + 'name': 'variable.parameter.property-name.css' + } + ] + } + { + 'begin': '{' + 'beginCaptures': + '0': + 'name': 'punctuation.section.property.begin.bracket.curly.css' + 'end': '}' + 'endCaptures': + '0': + 'name': 'punctuation.section.property.end.bracket.curly.css' + 'name': 'meta.at-rule.property.body.css' + 'patterns': [ + { + 'begin': '(?i)(?";inherits:false;initial-value:0px;}').tokens; + assert.deepStrictEqual(tokens[0], { scopes: ['source.css', 'meta.at-rule.property.header.css', 'keyword.control.at-rule.property.css', 'punctuation.definition.keyword.css'], value: '@' }); + assert.deepStrictEqual(tokens[3], { scopes: ['source.css', 'meta.at-rule.property.header.css', 'variable.parameter.property-name.css'], value: '--foo' }); + assert.deepStrictEqual(tokens[4], { scopes: ['source.css', 'meta.at-rule.property.body.css', 'punctuation.section.property.begin.bracket.curly.css'], value: '{' }); + assert.deepStrictEqual(tokens[5], { scopes: ['source.css', 'meta.at-rule.property.body.css', 'meta.property.css', 'meta.property-name.css', 'support.type.property-name.css'], value: 'syntax' }); + assert.deepStrictEqual(tokens[8], { scopes: ['source.css', 'meta.at-rule.property.body.css', 'meta.property.css', 'meta.property-value.css', 'string.quoted.double.css'], value: '' }); + assert.deepStrictEqual(tokens[13], { scopes: ['source.css', 'meta.at-rule.property.body.css', 'meta.property.css', 'meta.property-value.css', 'constant.language.boolean.css'], value: 'false' }); + assert.deepStrictEqual(tokens[15], { scopes: ['source.css', 'meta.at-rule.property.body.css', 'meta.property.css', 'meta.property-name.css', 'support.type.property-name.css'], value: 'initial-value' }); + assert.deepStrictEqual(tokens[18], { scopes: ['source.css', 'meta.at-rule.property.body.css', 'meta.property.css', 'meta.property-value.css', 'constant.numeric.css', 'keyword.other.unit.px.css'], value: 'px' }); + assert.deepStrictEqual(tokens[19], { scopes: ['source.css', 'meta.at-rule.property.body.css', 'punctuation.terminator.rule.css'], value: ';' }); + assert.deepStrictEqual(tokens[20], { scopes: ['source.css', 'meta.at-rule.property.body.css', 'punctuation.section.property.end.bracket.curly.css'], value: '}' }); + }); + + it('tokenises descriptors across lines', function () { + var lines; + lines = testGrammar.tokenizeLines('@property --bar {\n syntax: "*";\n inherits: true;\n initial-value: 0 0 #0000;\n}'); + assert.deepStrictEqual(lines[0][3], { scopes: ['source.css', 'meta.at-rule.property.header.css', 'variable.parameter.property-name.css'], value: '--bar' }); + assert.deepStrictEqual(lines[1][1], { scopes: ['source.css', 'meta.at-rule.property.body.css', 'meta.property.css', 'meta.property-name.css', 'support.type.property-name.css'], value: 'syntax' }); + assert.deepStrictEqual(lines[2][1], { scopes: ['source.css', 'meta.at-rule.property.body.css', 'meta.property.css', 'meta.property-name.css', 'support.type.property-name.css'], value: 'inherits' }); + assert.deepStrictEqual(lines[2][4], { scopes: ['source.css', 'meta.at-rule.property.body.css', 'meta.property.css', 'meta.property-value.css', 'constant.language.boolean.css'], value: 'true' }); + assert.deepStrictEqual(lines[3][1], { scopes: ['source.css', 'meta.at-rule.property.body.css', 'meta.property.css', 'meta.property-name.css', 'support.type.property-name.css'], value: 'initial-value' }); + assert.deepStrictEqual(lines[4][0], { scopes: ['source.css', 'meta.at-rule.property.body.css', 'punctuation.section.property.end.bracket.curly.css'], value: '}' }); + }); + }); describe('@import', function () { it('tokenises @import statements', function () { var tokens;