From 8c436cffb24d029cd9371eef49738aa47f7aa55f Mon Sep 17 00:00:00 2001 From: deathaxe Date: Tue, 17 Dec 2024 12:24:35 +0100 Subject: [PATCH 1/5] Extend meta.expression scope This commit adds `.expression` sub-scope name to distinguish embedded interpolation expressions from control flow statements. --- NgxHTML.sublime-syntax | 10 +-- tests/syntax_test_scopes.component.html | 100 ++++++++++++------------ 2 files changed, 55 insertions(+), 55 deletions(-) diff --git a/NgxHTML.sublime-syntax b/NgxHTML.sublime-syntax index 2fa7686..f1fea55 100644 --- a/NgxHTML.sublime-syntax +++ b/NgxHTML.sublime-syntax @@ -85,7 +85,7 @@ contexts: - match: \" scope: meta.string.ngx string.quoted.double.ngx punctuation.definition.string.begin.ngx embed: ng-directive-expressions - embed_scope: meta.directive.value.ngx meta.string.ngx meta.interpolation.ngx source.ngx.embedded.html + embed_scope: meta.directive.value.ngx meta.string.ngx meta.embedded.expression.ngx source.ngx.embedded.html escape: \" escape_captures: 0: meta.string.ngx string.quoted.double.ngx punctuation.definition.string.end.ngx @@ -93,7 +93,7 @@ contexts: - match: \' scope: meta.string.ngx string.quoted.single.ngx punctuation.definition.string.begin.ngx embed: ng-directive-expressions - embed_scope: meta.directive.value.ngx meta.string.ngx meta.interpolation.ngx source.ngx.embedded.html + embed_scope: meta.directive.value.ngx meta.string.ngx meta.embedded.expression.ngx source.ngx.embedded.html escape: \' escape_captures: 0: meta.string.ngx string.quoted.single.ngx punctuation.definition.string.end.ngx @@ -297,14 +297,14 @@ contexts: ng-interpolations: - match: '{{' - scope: meta.embedded.ngx.html punctuation.section.embedded.begin.ngx.html + scope: meta.embedded.expression.ngx.html punctuation.section.embedded.begin.ngx.html push: ng-interpolation-body ng-interpolation-body: - meta_include_prototype: false - - meta_content_scope: meta.embedded.ngx.html source.ngx.embedded.html + - meta_content_scope: meta.embedded.expression.ngx.html source.ngx.embedded.html - match: '}}' - scope: meta.embedded.ngx.html punctuation.section.embedded.end.ngx.html + scope: meta.embedded.expression.ngx.html punctuation.section.embedded.end.ngx.html pop: 1 - include: ng-expressions diff --git a/tests/syntax_test_scopes.component.html b/tests/syntax_test_scopes.component.html index 0449ccb..ef3ebe1 100644 --- a/tests/syntax_test_scopes.component.html +++ b/tests/syntax_test_scopes.component.html @@ -160,9 +160,9 @@ {{ a }} is greater than {{ b }} - - - + + + @@ -184,9 +184,9 @@ {{ a() }} is less than {{ b.c() }} - - - + + + @@ -206,9 +206,9 @@ {{ a }} is equal to {{ b }} - - - + + + @@ -235,7 +235,7 @@ {{ startDate }} - + @@ -357,12 +357,12 @@ Item #{{ idx }}: {{ item.name }} - + - + @@ -390,7 +390,7 @@ - + @@ -489,7 +489,7 @@ {{ "\xAF \u2AFF \n \"" }} - + @@ -534,7 +534,7 @@ {{ { name: 'Alice', 'object': { array + "name": [0, 2, 3] } } }} - + @@ -607,7 +607,7 @@ {{ foo ? bar : baz }} - + @@ -618,7 +618,7 @@ {{ foo = null ?? 'default' }} - + @@ -631,7 +631,7 @@ {{ person['name'][0] = "Mirabel" }} - + @@ -650,20 +650,20 @@ {{ obj?.member }} - - - - + + + + {{ obj.member [5] }} - - - - + + + + @@ -672,10 +672,10 @@ {{ obj.method() }} - - - - + + + + @@ -685,10 +685,10 @@ {{ orders.value()?.[0]?.$extra?.#currency }} - - - - + + + + @@ -709,7 +709,7 @@ {{ func(arg, "value") }} - + @@ -723,7 +723,7 @@ {{ var | filter | annimation ("forward") }} - + @@ -757,7 +757,7 @@ - + @@ -776,7 +776,7 @@ - + @@ -801,7 +801,7 @@ - + @@ -820,7 +820,7 @@ - + @@ -845,7 +845,7 @@ - + @@ -868,7 +868,7 @@ - + @@ -892,7 +892,7 @@ - + @@ -916,7 +916,7 @@ - + @@ -932,7 +932,7 @@ - + @@ -949,7 +949,7 @@ - + @@ -971,7 +971,7 @@ - + @@ -987,7 +987,7 @@ - + @@ -999,7 +999,7 @@ - + @@ -1016,6 +1016,6 @@ - + From 93be7fda3b4acd60fdc545c0a51247bd61bd451b Mon Sep 17 00:00:00 2001 From: deathaxe Date: Mon, 16 Dec 2024 17:30:59 +0100 Subject: [PATCH 2/5] Fix interpolation in attribute values This commit adds new `ng-string-interpolations` context, which in addition to `ng-string-interpolations` to clear `string` scope in quoted strings. This ensures interpolated expressions are 1. treated as normal source instead of applying special string rules 2. correctly highlighted avoiding unmatched tokens looking like strings. An inherited special purpose CSS syntax is added to support Mustache tags in inline styles. --- Embeddings/CSS (for Ngx).sublime-syntax | 42 ++++++++++++++ NgxHTML.sublime-syntax | 57 +++++++++++++++++++ tests/syntax_test_scopes.component.html | 73 +++++++++++++++++++++++++ 3 files changed, 172 insertions(+) create mode 100644 Embeddings/CSS (for Ngx).sublime-syntax diff --git a/Embeddings/CSS (for Ngx).sublime-syntax b/Embeddings/CSS (for Ngx).sublime-syntax new file mode 100644 index 0000000..270c627 --- /dev/null +++ b/Embeddings/CSS (for Ngx).sublime-syntax @@ -0,0 +1,42 @@ +%YAML 1.2 +--- +# A special syntax definition to drive double quoted inline style attributes +# with {...} interpolation support +scope: source.css.embedded.ngx +version: 2 +hidden: true + +extends: Packages/CSS/CSS.sublime-syntax + +variables: + ident_start: (?:{{nmstart}}|{{) + +contexts: + + prototype: + - meta_prepend: true + - include: NgxHTML.sublime-syntax#ng-interpolations + + string-content: + - meta_prepend: true + - include: NgxHTML.sublime-syntax#ng-string-interpolations + + at-keyframe-block-body: + # required until ST4174 (PR #3820) + - meta_prepend: true + - meta_include_prototype: false + + at-layer-name-list: + # required until ST4174 (PR #3820) + - meta_prepend: true + - meta_include_prototype: false + + property-list-body: + # required until ST4174 (PR #3820) + - meta_prepend: true + - meta_include_prototype: false + + stylesheet-block-body: + # required until ST4174 (PR #3831) + - meta_prepend: true + - meta_include_prototype: false diff --git a/NgxHTML.sublime-syntax b/NgxHTML.sublime-syntax index f1fea55..3c4dc82 100644 --- a/NgxHTML.sublime-syntax +++ b/NgxHTML.sublime-syntax @@ -14,6 +14,14 @@ contexts: ###[ HTML CUSTOMIZATIONS ]##################################################### + cdata-content: + - meta_prepend: true + - include: ng-string-interpolations + + strings-common-content: + - meta_prepend: true + - include: ng-string-interpolations + tag: - meta_prepend: true - include: ng-declarations @@ -24,6 +32,44 @@ contexts: - meta_prepend: true - include: ng-directives + tag-generic-attribute-name: + - meta_prepend: true + - include: ng-interpolations + + tag-attribute-value-content: + - meta_prepend: true + - include: ng-string-interpolations + +###[ HTML STYLE ATTRIBUTES ]################################################### + + tag-style-attribute-assignment: + - meta_include_prototype: false + - match: = + scope: punctuation.separator.key-value.html + set: + - immediately-pop # workaround https://github.com/sublimehq/sublime_text/issues/4069 + - tag-style-attribute-value + - include: else-pop + + tag-style-attribute-value: + - match: \" + scope: meta.string.html string.quoted.double.html punctuation.definition.string.begin.html + embed: scope:source.css.embedded.ngx#rule-list-body + embed_scope: meta.string.html source.css.embedded.html + escape: \" + escape_captures: + 0: meta.string.html string.quoted.double.html punctuation.definition.string.end.html + pop: 1 + - match: \' + scope: meta.string.html string.quoted.single.html punctuation.definition.string.begin.html + embed: scope:source.css.embedded.ngx#rule-list-body + embed_scope: meta.string.html source.css.embedded.html + escape: \' + escape_captures: + 0: meta.string.html string.quoted.single.html punctuation.definition.string.end.html + pop: 1 + - include: else-pop + ###[ ANGULAR DIRECTIVES ]###################################################### ng-directives: @@ -295,6 +341,17 @@ contexts: ###[ ANGULAR INTERPOLATIONS ]################################################## + ng-string-interpolations: + - match: '{{' + scope: meta.embedded.expression.ngx.html punctuation.section.embedded.begin.ngx.html + push: ng-string-interpolation-body + + ng-string-interpolation-body: + - clear_scopes: 1 + - meta_include_prototype: false + - meta_content_scope: meta.embedded.expression.ngx.html source.ngx.embedded.html + - include: ng-interpolation-body + ng-interpolations: - match: '{{' scope: meta.embedded.expression.ngx.html punctuation.section.embedded.begin.ngx.html diff --git a/tests/syntax_test_scopes.component.html b/tests/syntax_test_scopes.component.html index ef3ebe1..c525647 100644 --- a/tests/syntax_test_scopes.component.html +++ b/tests/syntax_test_scopes.component.html @@ -742,6 +742,79 @@ + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + From 2f49366d2ef3fed358e62ed87836e06750e1b5ff Mon Sep 17 00:00:00 2001 From: deathaxe Date: Tue, 17 Dec 2024 09:09:29 +0100 Subject: [PATCH 4/5] Add meta scope to control flow statements This commit scopes all control flow statements `meta.embedded.statement source.ngx.embedded.html`. to also apply proper `source.ngx` to angular expressions. --- NgxHTML.sublime-syntax | 53 +++++++++++++++++++------ tests/syntax_test_scopes.component.html | 52 ++++++++++++++++++------ 2 files changed, 79 insertions(+), 26 deletions(-) diff --git a/NgxHTML.sublime-syntax b/NgxHTML.sublime-syntax index 8d14473..4dc6f16 100644 --- a/NgxHTML.sublime-syntax +++ b/NgxHTML.sublime-syntax @@ -177,7 +177,9 @@ contexts: ng-declarations: - match: (@)let{{ident_break}} - scope: meta.let.ngx keyword.declaration.variable.ngx + scope: + meta.embedded.statement.ngx.html source.ngx.embedded.html + meta.let.ngx keyword.declaration.variable.ngx captures: 1: punctuation.definition.keyword.ngx push: @@ -191,15 +193,21 @@ contexts: - include: else-pop ng-let-assignment: - - meta_content_scope: meta.let.identifier.ngx + - meta_content_scope: + meta.embedded.statement.ngx.html source.ngx.embedded.html + meta.let.identifier.ngx - match: = - scope: meta.let.ngx keyword.operator.assignment.ngx + scope: + meta.embedded.statement.ngx.html source.ngx.embedded.html + meta.let.ngx keyword.operator.assignment.ngx set: ng-let-value - include: else-pop ng-let-value: - meta_include_prototype: false - - meta_content_scope: meta.let.value.ngx + - meta_content_scope: + meta.embedded.statement.ngx.html source.ngx.embedded.html + meta.let.value.ngx - match: ';' scope: punctuation.terminator.expression.ngx pop: 1 @@ -211,39 +219,51 @@ contexts: # conditionals # https://angular.dev/guide/templates/control-flow#conditionally-display-content-with-if-else-if-and-else - match: (@)if{{ident_break}} - scope: keyword.control.conditional.if.ngx + scope: + meta.embedded.statement.ngx.html source.ngx.embedded.html + keyword.control.conditional.if.ngx captures: 1: punctuation.definition.keyword.ngx push: - ng-block - ng-conditional-group - match: (@)else\s+if{{ident_break}} - scope: keyword.control.conditional.elseif.ngx + scope: + meta.embedded.statement.ngx.html source.ngx.embedded.html + keyword.control.conditional.elseif.ngx captures: 1: punctuation.definition.keyword.ngx push: - ng-block - ng-conditional-group - match: (@)else{{ident_break}} - scope: keyword.control.conditional.else.ngx + scope: + meta.embedded.statement.ngx.html source.ngx.embedded.html + keyword.control.conditional.else.ngx captures: 1: punctuation.definition.keyword.ngx push: ng-block # https://angular.dev/guide/templates/control-flow#conditionally-display-content-with-the-switch-block - match: (@)case{{ident_break}} - scope: keyword.control.conditional.case.ngx + scope: + meta.embedded.statement.ngx.html source.ngx.embedded.html + keyword.control.conditional.case.ngx captures: 1: punctuation.definition.keyword.ngx push: - ng-block - ng-conditional-group - match: (@)default{{ident_break}} - scope: keyword.control.conditional.case.ngx + scope: + meta.embedded.statement.ngx.html source.ngx.embedded.html + keyword.control.conditional.case.ngx captures: 1: punctuation.definition.keyword.ngx push: ng-block - match: (@)switch{{ident_break}} - scope: keyword.control.conditional.switch.ngx + scope: + meta.embedded.statement.ngx.html source.ngx.embedded.html + keyword.control.conditional.switch.ngx captures: 1: punctuation.definition.keyword.ngx push: @@ -251,20 +271,26 @@ contexts: - ng-conditional-group # https://angular.dev/guide/templates/control-flow#providing-a-fallback-for-for-blocks-with-the-empty-block - match: (@)for{{ident_break}} - scope: keyword.control.loop.for.ngx + scope: + meta.embedded.statement.ngx.html source.ngx.embedded.html + keyword.control.loop.for.ngx captures: 1: punctuation.definition.keyword.ngx push: - ng-block - ng-for-group - match: (@)empty{{ident_break}} - scope: keyword.control.loop.else.ngx + scope: + meta.embedded.statement.ngx.html source.ngx.embedded.html + keyword.control.loop.else.ngx captures: 1: punctuation.definition.keyword.ngx push: ng-block # https://angular.dev/guide/templates/defer - match: (@)(?:defer|error|loading|placeholder){{ident_break}} - scope: keyword.control.flow.ngx + scope: + meta.embedded.statement.ngx.html source.ngx.embedded.html + keyword.control.flow.ngx captures: 1: punctuation.definition.keyword.ngx push: @@ -273,6 +299,7 @@ contexts: ng-block: - meta_include_prototype: false + - meta_content_scope: meta.embedded.statement.ngx.html source.ngx.embedded.html - match: \{ scope: punctuation.section.block.begin.ngx set: ng-block-body diff --git a/tests/syntax_test_scopes.component.html b/tests/syntax_test_scopes.component.html index 313fff1..4cbefac 100644 --- a/tests/syntax_test_scopes.component.html +++ b/tests/syntax_test_scopes.component.html @@ -6,6 +6,8 @@ --> @let name = user.name; + + @@ -16,6 +18,7 @@ @let func = user.func(); + @@ -29,6 +32,7 @@ @let greeting = 'Hello, ' + name; + @@ -41,6 +45,7 @@ @let data = data$ | async; + @@ -53,6 +58,7 @@ @let item = var[10]['bar']; + @@ -71,6 +77,7 @@ @let path = .foo?.bar? ; + @@ -87,6 +94,7 @@ @let path = .orders.value()?.[0]?.$extra?.#currency.unit; + @@ -116,6 +124,7 @@ @let path = orders.value()?.[0]?.$extra?.#currency.unit; + @@ -149,9 +158,10 @@ --> @if (a > b) { - - - + + + + @@ -173,6 +183,7 @@ } @else if (b > a()) { + @@ -202,7 +213,8 @@ } @else { - + + {{ a }} is equal to {{ b }} @@ -250,6 +262,8 @@ --> @switch (accessLevel) { + + @@ -257,7 +271,7 @@ @case ('admin') { - + @@ -277,7 +291,7 @@ @case { - + @@ -292,7 +306,7 @@ @default { - + @@ -311,6 +325,8 @@ --> @for (user of users; track user.id) { + + @@ -326,14 +342,16 @@ {{ user.name }} } @empty { - - + + + Empty list of users -} - +.html} + @for (item of items; track item.id; let idx = $index, e = $even) { + @@ -370,9 +388,11 @@ } - + @if (users$ | async; as users) { + + @@ -402,7 +422,7 @@ } - + @defer { } + @@ -422,6 +443,7 @@ @defer (on viewport) { + @@ -433,18 +455,21 @@ } @loading { + Loading… } @error { + Loading failed :( } @placeholder { + @@ -454,6 +479,7 @@ @defer (on viewport; when $var prefetch on idle; prefetch when true) { + From 3dcec662091d1bff02e14869b4010b39518d1afa Mon Sep 17 00:00:00 2001 From: deathaxe Date: Tue, 17 Dec 2024 12:04:13 +0100 Subject: [PATCH 5/5] Add a global `meta.template.ngx` scope This commit scopes whole document `meta.template`. Various template syntaxes make use of it to be able to use simpler snippet or completion selectors across all supported languages (e.g.: CSS/JS/HTML). --- NgxHTML.sublime-syntax | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/NgxHTML.sublime-syntax b/NgxHTML.sublime-syntax index 4dc6f16..dd04854 100644 --- a/NgxHTML.sublime-syntax +++ b/NgxHTML.sublime-syntax @@ -14,6 +14,10 @@ contexts: ###[ HTML CUSTOMIZATIONS ]##################################################### + main: + - meta_prepend: true + - meta_scope: meta.template.ngx + cdata-content: - meta_prepend: true - include: ng-string-interpolations