Design alphaTex Syntax Highlighting in Editors #2303
Replies: 2 comments 2 replies
-
There is no official grammar definition we release. The parser in alphaTab itself is a "recursive descent parser" directly handling the mapping into the data model as it is parsed. We are not using any formal grammar to generate a lexer/parser/AST. For simple syntax highlighting you might not need a full exact grammar but just define a set of rules (e.g. for metadata commands You'll need a full grammar if you want things like syntax checking, code completion etc. Another path could be to create a LSP implementation for alphatex providing semantic tokens information. The alphaTex importer would have to be updated to emit this information but it seems more feasible to extend the importer and use it in a LSP server than maintaining a separate implementation. 😉 |
Beta Was this translation helpful? Give feedback.
-
I looked a bit into adding a language support in VS Code and stitched together a very basic TextMate grammar. My (wrong) assumption was that we would need a full proper grammar of alphaTex reflecting the AST. But it seems TextMate grammars are quite a bit simpler. Having some basic regex patterns for the most relevant constructs is quite easy. It doesn't yet cover all constructs and isn't very specific on bits like track or staff properties but its a start: {
"name": "alphaTex",
"scopeName": "score.atex",
"fileTypes": [ "atex" ],
"uuid": "1ebe68ca-6558-428e-a6af-90831f41e88e",
"patterns": [
{ "include": "#statements" }
],
"repository": {
"statements": {
"patterns": [
{ "include": "#beat-duration" },
{ "include": "#expression" },
{ "include": "#punctuation-pipe" },
{ "include": "#string" },
{ "include": "#comment" }
]
},
"beat-duration": {
"name": "meta.beat-duration.atex",
"match": ":[0-9]+",
"captures": { "0": { "name": "constant.numeric.beat-duration.atex" } }
},
"string": {
"patterns": [
{ "include": "#qstring-single" },
{ "include": "#qstring-double" }
]
},
"qstring-single": {
"name": "string.quoted.single.atex",
"begin": "'",
"beginCaptures": { "0": { "name": "punctuation.definition.string.begin.atex" } },
"end": "(\\')|((?:[^\\\\\\n])$)",
"endCaptures": {
"1": { "name": "punctuation.definition.string.end.atex" },
"2": { "name": "invalid.illegal.newline.atex" }
},
"patterns": [ { "include": "#string-character-escape" } ]
},
"qstring-double": {
"name": "string.quoted.double.tsatex",
"begin": "\"",
"beginCaptures": { "0": { "name": "punctuation.definition.string.begin.atex" } },
"end": "(\")|((?:[^\\\\\\n])$)",
"endCaptures": {
"1": { "name": "punctuation.definition.string.end.atex" },
"2": { "name": "invalid.illegal.newline.atex" }
},
"patterns": [ { "include": "#string-character-escape" } ]
},
"string-character-escape": {
"name": "constant.character.escape.atex",
"match": "\\\\(x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4}|[0-2][0-7]{0,2}|3[0-6][0-7]?|37[0-7]?|[4-7][0-7]?|.|$)"
},
"comment": {
"patterns": [
{
"name": "comment.block.atex",
"begin": "/\\*",
"beginCaptures": { "0": { "name": "punctuation.definition.comment.atex" } },
"end": "\\*/",
"endCaptures": { "0": { "name": "punctuation.definition.comment.atex" } }
},
{
"begin": "(^[ \\t]+)?(//)",
"beginCaptures": {
"1": { "name": "punctuation.whitespace.comment.leading.atex" },
"2": { "name": "comment.line.double-slash.atex" }
},
"end": "(?=$)",
"contentName": "comment.line.double-slash.atex"
}
]
},
"expression": {
"patterns": [
{ "include": "#expressionWithoutIdentifiers" },
{ "include": "#expressionPunctuations" },
{ "include": "#metadata" }
]
},
"expressionPunctuations": {
"patterns": [ { "include": "#punctuation-dot" } ]
},
"expressionWithoutIdentifiers": {
"patterns": [
{ "include": "#string" },
{ "include": "#comment" },
{ "include": "#literal" },
{ "include": "#effects" }
]
},
"effects": {
"patterns": [
{
"name": "keyword.operator.effect.beat.atex",
"match": "(?:f|fo|vs|v|vw|s|p|tt|txt|lyrics|dd|d|su|sd|tuplet|tb|tbe|bu|bd|ai|ad|ch|gr|ob|b|dy|cre|dec|tempo|volume|balance|tp|spd|sph|spu|spe|slashed|glpf|glpt|waho|wahc|barre|rasg|ot|legaoorigin|instrument|bank|fermata|beam|timer)"
},
{
"name": "keyword.operator.effect.note.atex",
"match": "(?:b|be|nh|ah|th|ph|sh|fh|tr|v|vw|sl|ss|sib|sia|sou|sod|psd|psu|h|lht|g|ac|hac|ten|pm|st|lr|x|t|lf|rf|acc|turn|iturn|umordent|lmordent|string|hide|slur)"
}
]
},
"metadata": {
"name": "meta.metadata.atex",
"match": "\\\\{1,2}[a-zA-Z0-9_-]+",
"captures": { "0": { "name": "keyword.metadata.atex" } }
},
"literal": {
"patterns": [
{ "include": "#numeric-literal" },
{ "include": "#boolean-literal" },
{ "include": "#tuning-literal" }
]
},
"numeric-literal": {
"name": "constant.numeric.decimal.atex",
"match": "[0-9]+",
"captures": { "0": { "name": "constant.numeric.decimal.atex" } }
},
"boolean-literal": {
"patterns": [
{
"name": "constant.language.boolean.true.atex",
"match": "true"
},
{
"name": "constant.language.boolean.false.atex",
"match": "false"
}
]
},
"tuning-literal": {
"name": "constant.language.tuning.atex",
"match": "([A-Ga-g][0-9]+)",
"captures": { "1": { "name": "constant.numeric.tuning.atex" } }
},
"punctuation-pipe": {
"name": "punctuation.terminator.bar.atex",
"match": "\\|"
},
"punctuation-dot": {
"name": "punctuation.separator.note.atex",
"match": "\\."
}
}
} |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
Hi there, I'm currently developing an Obsidian plugin to make it easier to write and edit tabs directly in alphaTex. The editing experience is key, so I've been looking for an official or community-supported syntax highlighting grammar for alphaTex, but haven't found one yet. Is there any highlight syntax for alphaTex?
I've created a proof-of-concept using a regex-based approach to make it clearer to locate metadata tags (like
\chord
), note effects()/{}
and Bars|
.Here is what it looks like in action:
Highlight.ts
Implementation from a previous project

I am a litte bit stuck because the rules are getting more complex and I have write two version for obsidian and web(vue). I think if I need to design a syntax for it, the most important part is to figure out
readScore()
method and the AlphaTexImporter (I haven't yet explored the importer in depth yet). As metioned in #695 (comment) , developers like me often design tokenizers without fully understanding the importer internals.alphaTab/src/importer/AlphaTexImporter.ts
Lines 613 to 669 in 9455cf9
Related content I have explored: alphaTex Data model
To be clear, I understand that alphaTab’s core focus is on rendering and playback, not editor integration and should not be responsible for implementing the highlighting for various editors (codemirror, regex, textmate....) as there is already
AlphaTexImporter
for lexer and parser. Definition of token scopes/classes is rightly the job of the application layer. However, I think it's the perfect place for a discussion about visual effect and design. It would empower developers (like myself) to create consistent, high-quality highlighters for their chosen platforms and save a lot of time.One more thing is I think once alphaTexExporter (#1123 & #1485) is stable and ready to release, the editing experence will be more important as I edit alphaTex more frequently — making a reliable syntax highlighter even more valuable. So maybe it's the time to come up with a discussion (not feature request) for anyone interested in it.
I'd love to discuss:
Thanks for the great work and help! I am very satisfied and happy with alphaTab.js to make my tabs interactive. For AlphaTexExporter, maybe I will come up with a new discussion after I have tested it in alpha versions.
Beta Was this translation helpful? Give feedback.
All reactions