Skip to content
Merged
Show file tree
Hide file tree
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
44 changes: 44 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Project: Ngx HTML Syntax for Sublime Text

## Overview
A Sublime Text syntax highlighting package for Angular 2+ HTML templates. Extends the default HTML syntax to support Angular-specific features.

## Key Files
- `NgxHTML.sublime-syntax` - Main syntax definition (YAML 1.2 format)
- `tests/syntax_test_scopes.component.html` - Syntax test file
- `Embeddings/*.sublime-syntax` - Embedded syntax definitions for CSS, HTML, RegExp in template strings

## Syntax File Structure
The syntax file (`NgxHTML.sublime-syntax`) is organized into sections:
- HTML Customizations (extends base HTML)
- Angular Directives (`*ngIf`, `[bind]`, `(event)`, `[(twoWay)]`, `#ref`)
- Angular Declarations (`@let`)
- Angular Statements (`@if`, `@for`, `@switch`, `@case`, `@defer`, etc.)
- Angular Expressions (arrays, objects, functions, operators, literals)
- Angular Variables and property access

## Testing
- Tests run via GitHub Actions using `SublimeText/syntax-test-action@v2`
- Test file uses Sublime Text syntax test format with `<!-- ^ scope.name -->` assertions
- Column positions in test assertions must align exactly with the code being tested
- The `^` marker points to the same column in the line above (1-indexed)

## Scope Naming Conventions
- `keyword.operator.spread.ngx` - Spread operator `...`
- `keyword.control.conditional.*.ngx` - Control flow keywords
- `punctuation.section.*.ngx` - Brackets and delimiters
- `punctuation.separator.*.ngx` - Commas and separators
- `variable.other.*.ngx` - Variables
- `meta.*.ngx` - Meta scopes for regions

## Build/CI
- GitHub Actions workflow in `.github/`
- Uses Sublime Text build 4180 for syntax tests
- No local test runner required; tests run in CI

## Common Patterns
When adding new syntax support:
1. Add pattern to appropriate section in `NgxHTML.sublime-syntax`
2. Use `include: ng-<context>` to compose contexts
3. Add tests in `tests/syntax_test_scopes.component.html`
4. Ensure test `^` markers align with exact column positions
9 changes: 9 additions & 0 deletions NgxHTML.sublime-syntax
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,7 @@ contexts:
- match: \]
scope: punctuation.section.sequence.end.ngx
pop: 1
- include: ng-spread
- include: ng-expressions

###[ ANGULAR GROUPS ]##########################################################
Expand Down Expand Up @@ -474,6 +475,7 @@ contexts:
- match: ':'
scope: meta.mapping.ngx punctuation.separator.mapping.key-value.ngx
set: ng-object-value
- include: ng-spread
- match: (?=\S)
set: ng-object-key

Expand Down Expand Up @@ -542,8 +544,15 @@ contexts:
pop: 1
- match: ','
scope: punctuation.separator.arguments.ngx
- include: ng-spread
- include: ng-expressions

###[ ANGULAR SPREAD ]##########################################################

ng-spread:
- match: \.\.\.
scope: keyword.operator.spread.ngx

###[ ANGULAR OPERATORS ]#######################################################

ng-operators:
Expand Down
132 changes: 132 additions & 0 deletions tests/syntax_test_scopes.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -1365,3 +1365,135 @@
<!-- ^^^^^^^^^^^ meta.embedded.expression.ngx source.ngx.embedded.html variable.other.readwrite.ngx -->
<!-- ^ string.quoted.double.ngx punctuation.definition.string.end.ngx -->
<!-- ^^ punctuation.definition.tag.end.html -->


<!--
Spread operator in arrays
-->

{{ [first, ...rest, last] }}
<!-- ^^^^^^^^^^^^^^^^^^^^^^ meta.sequence.array.ngx -->
<!-- ^ punctuation.section.sequence.begin.ngx -->
<!-- ^^^^^ variable.other.readwrite.ngx -->
<!-- ^ punctuation.separator.sequence.ngx -->
<!-- ^^^ keyword.operator.spread.ngx -->
<!-- ^^^^ variable.other.readwrite.ngx -->
<!-- ^ punctuation.separator.sequence.ngx -->
<!-- ^^^^ variable.other.readwrite.ngx -->
<!-- ^ punctuation.section.sequence.end.ngx -->

{{ [...items] }}
<!-- ^^^^^^^^^^ meta.sequence.array.ngx -->
<!-- ^^^ keyword.operator.spread.ngx -->
<!-- ^^^^^ variable.other.readwrite.ngx -->


<!--
Spread operator in objects
-->

{{ { a: 1, ...obj, b: 2 } }}
<!-- ^ punctuation.section.mapping.begin.ngx -->
<!-- ^^ meta.mapping.ngx -->
<!-- ^ meta.mapping.key.ngx meta.string.ngx string.unquoted.ngx -->
<!-- ^ punctuation.separator.mapping.key-value.ngx -->
<!-- ^ meta.number.integer.decimal.ngx constant.numeric.value.ngx -->
<!-- ^ punctuation.separator.mapping.pair.ngx -->
<!-- ^^^ keyword.operator.spread.ngx -->
<!-- ^^^ variable.other.readwrite.ngx -->
<!-- ^ punctuation.separator.mapping.pair.ngx -->
<!-- ^ meta.mapping.key.ngx meta.string.ngx string.unquoted.ngx -->
<!-- ^ punctuation.separator.mapping.key-value.ngx -->
<!-- ^ meta.number.integer.decimal.ngx constant.numeric.value.ngx -->
<!-- ^ punctuation.section.mapping.end.ngx -->

{{ { ...defaults } }}
<!-- ^^ meta.mapping.ngx -->
<!-- ^^^ keyword.operator.spread.ngx -->
<!-- ^^^^^^^^ variable.other.readwrite.ngx -->


<!--
Rest arguments in function calls
-->

{{ func(a, b, ...args) }}
<!-- ^^^^ meta.function-call.identifier.ngx variable.function.ngx -->
<!-- ^^^^^^^^^^^^^^^ meta.function-call.arguments.ngx -->
<!-- ^ punctuation.section.arguments.begin.ngx -->
<!-- ^ variable.other.readwrite.ngx -->
<!-- ^ punctuation.separator.arguments.ngx -->
<!-- ^ variable.other.readwrite.ngx -->
<!-- ^ punctuation.separator.arguments.ngx -->
<!-- ^^^ keyword.operator.spread.ngx -->
<!-- ^^^^ variable.other.readwrite.ngx -->
<!-- ^ punctuation.section.arguments.end.ngx -->

{{ call(...items) }}
<!-- ^^^^ meta.function-call.identifier.ngx variable.function.ngx -->
<!-- ^^^^^^^^^ meta.function-call.arguments.ngx -->
<!-- ^ punctuation.section.arguments.begin.ngx -->
<!-- ^^^ keyword.operator.spread.ngx -->
<!-- ^^^^^ variable.other.readwrite.ngx -->
<!-- ^ punctuation.section.arguments.end.ngx -->

{{ obj.method(first, ...rest) }}
<!-- ^^^ variable.other.object.ngx -->
<!-- ^ punctuation.accessor.ngx -->
<!-- ^^^^^^ meta.function-call.identifier.ngx variable.function.method.ngx -->
<!-- ^^^^^^^^^^^^^^^^ meta.function-call.arguments.ngx -->
<!-- ^ punctuation.section.arguments.begin.ngx -->
<!-- ^^^^^ variable.other.readwrite.ngx -->
<!-- ^ punctuation.separator.arguments.ngx -->
<!-- ^^^ keyword.operator.spread.ngx -->
<!-- ^^^^ variable.other.readwrite.ngx -->
<!-- ^ punctuation.section.arguments.end.ngx -->


<!--
Multiple switch cases matching (fallthrough pattern)
https://angular.dev/guide/templates/control-flow#conditionally-display-content-with-the-switch-block
-->

@switch (cond) {
<!-- <- keyword.control.conditional.switch.ngx punctuation.definition.keyword.ngx -->
<!--^^^ keyword.control.conditional.switch.ngx -->
<!-- ^^^^^^ meta.group.ngx -->
<!-- ^ meta.block.ngx punctuation.section.block.begin.ngx -->
@case (1)
<!--^^^^^ meta.block.ngx meta.embedded.statement.ngx.html source.ngx.embedded.html keyword.control.conditional.case.ngx -->
<!--^ punctuation.definition.keyword.ngx -->
<!-- ^^^ meta.group.ngx -->
<!-- ^ punctuation.section.group.begin.ngx -->
<!-- ^ meta.number.integer.decimal.ngx constant.numeric.value.ngx -->
<!-- ^ punctuation.section.group.end.ngx -->
@case (2)
<!--^^^^^ meta.block.ngx meta.embedded.statement.ngx.html source.ngx.embedded.html keyword.control.conditional.case.ngx -->
<!--^ punctuation.definition.keyword.ngx -->
<!-- ^^^ meta.group.ngx -->
<!-- ^ meta.number.integer.decimal.ngx constant.numeric.value.ngx -->
@case (3) {
<!--^^^^^ meta.block.ngx meta.embedded.statement.ngx.html source.ngx.embedded.html keyword.control.conditional.case.ngx -->
<!--^ punctuation.definition.keyword.ngx -->
<!-- ^^^ meta.group.ngx -->
<!-- ^ meta.number.integer.decimal.ngx constant.numeric.value.ngx -->
<!-- ^ meta.block.ngx meta.block.ngx punctuation.section.block.begin.ngx -->
<span>1, 2 or 3</span>
<!--^^^^^^^^^^^^^^^^^^^^^^^^^ meta.block.ngx meta.block.ngx -->
}
<!--^ meta.block.ngx meta.block.ngx punctuation.section.block.end.ngx -->

@case (4) {
<!--^^^^^ meta.block.ngx meta.embedded.statement.ngx.html source.ngx.embedded.html keyword.control.conditional.case.ngx -->
<!-- ^ meta.block.ngx meta.block.ngx punctuation.section.block.begin.ngx -->
4
}
<!--^ meta.block.ngx meta.block.ngx punctuation.section.block.end.ngx -->

@default {
<!--^^^^^^^^ meta.block.ngx meta.embedded.statement.ngx.html source.ngx.embedded.html keyword.control.conditional.case.ngx -->
<!-- ^ meta.block.ngx meta.block.ngx punctuation.section.block.begin.ngx -->
...?
}
<!--^ meta.block.ngx meta.block.ngx punctuation.section.block.end.ngx -->
}