refine blocks detection

This commit is contained in:
2026-08-17 18:22:58 +02:00
parent d963472729
commit c3d5d3541b
3 changed files with 133 additions and 153 deletions
+9
View File
@@ -233,6 +233,9 @@ export class EftlParser {
case TokenType.VALUE_OF: case TokenType.VALUE_OF:
case TokenType.SIZE_OF: case TokenType.SIZE_OF:
case TokenType.HEADER: case TokenType.HEADER:
if (!token.value.endsWith('/]')) {
this.pushBlock(token);
}
this.advance(); this.advance();
break; break;
@@ -249,6 +252,9 @@ export class EftlParser {
} }
private pushBlock(token: Token): void { private pushBlock(token: Token): void {
if (token.value.endsWith('/]')) {
return; // It's self-closing, don't push to stack
}
this.blockStack.push({ type: token.type, token }); this.blockStack.push({ type: token.type, token });
} }
@@ -295,6 +301,9 @@ export class EftlParser {
[TokenType.TRIM_OPEN]: '[/TRIM]', [TokenType.TRIM_OPEN]: '[/TRIM]',
[TokenType.CONTAINS_OPEN]: '[/CONTAINS]', [TokenType.CONTAINS_OPEN]: '[/CONTAINS]',
[TokenType.FORMAT_OPEN]: '[/FORMAT]', [TokenType.FORMAT_OPEN]: '[/FORMAT]',
[TokenType.VALUE_OF]: '[/VALUE_OF]',
[TokenType.SIZE_OF]: '[/SIZE_OF]',
[TokenType.HEADER]: '[/HEADER]',
[TokenType.EXPR_OPEN]: '%]', [TokenType.EXPR_OPEN]: '%]',
[TokenType.EXPR_OUTPUT_OPEN]: '%]', [TokenType.EXPR_OUTPUT_OPEN]: '%]',
[TokenType.COMMENT_OPEN]: '--]' [TokenType.COMMENT_OPEN]: '--]'
+2 -8
View File
@@ -298,7 +298,7 @@ export class EftlTokenizer {
private scanVarOpen(startLine: number, startColumn: number): void { private scanVarOpen(startLine: number, startColumn: number): void {
let value = '[VAR'; let value = '[VAR';
// Scan until we find ] or ] // Scan until we find ]
while (this.pos < this.source.length && this.peek() !== ']') { while (this.pos < this.source.length && this.peek() !== ']') {
value += this.advance(); value += this.advance();
} }
@@ -328,13 +328,7 @@ export class EftlTokenizer {
break; break;
} }
if (ch === ']') { if (ch === ']') {
// Not self-closing, error // Could be block start, let parser handle it if it's supposed to be self-closing
this.errors.push({
message: `Expected self-closing tag: [${tagName} ... /]`,
line: startLine,
column: startColumn,
length: value.length
});
break; break;
} }
} }
+122 -145
View File
@@ -94,7 +94,7 @@
}, },
{ {
"name": "keyword.operator.arithmetic.eftl", "name": "keyword.operator.arithmetic.eftl",
"match": "[+\\-*/%]" "match": "([+\\-*%]|/(?!\\]))"
}, },
{ {
"name": "keyword.operator.comparison.eftl", "name": "keyword.operator.comparison.eftl",
@@ -129,45 +129,85 @@
} }
] ]
}, },
"tags": { "attributes": {
"patterns": [ "patterns": [
{ {
"name": "meta.tag.data.eftl", "comment": "name attribute",
"begin": "(\\[TAG\\])", "match": "\\b(name)\\b\\s*(=)\\s*(\"(.*?)\")",
"end": "(\\[/TAG\\])", "captures": {
"beginCaptures": { "1": { "name": "entity.other.attribute-name.eftl" },
"1": { "name": "entity.name.tag.eftl" } "2": { "name": "punctuation.separator.key-value.eftl" },
}, "3": { "name": "string.quoted.double.eftl" },
"endCaptures": { "4": { "name": "variable.other.eftl" }
"1": { "name": "entity.name.tag.eftl" } }
}, },
"patterns": [ {
{ "include": "#tag-content" } "comment": "type attribute",
] "match": "\\b(type)\\b\\s*(=)\\s*(\"(string|boolean|number|date|object|iterable)\")",
"captures": {
"1": { "name": "entity.other.attribute-name.eftl" },
"2": { "name": "punctuation.separator.key-value.eftl" },
"3": { "name": "string.quoted.double.eftl" },
"4": { "name": "support.type.primitive.eftl" }
}
},
{
"comment": "generic attribute",
"match": "\\b([a-zA-Z_][a-zA-Z0-9_-]*)\\b\\s*(=)\\s*(\"(.*?)\")",
"captures": {
"1": { "name": "entity.other.attribute-name.eftl" },
"2": { "name": "punctuation.separator.key-value.eftl" },
"3": { "name": "string.quoted.double.eftl" }
}
} }
] ]
}, },
"tag-content": { "tag-open-context": {
"patterns": [
{ "include": "#attributes" },
{ "include": "#expressions" },
{ "include": "#functions" },
{ "include": "#tags" }
]
},
"tags": {
"patterns": [ "patterns": [
{ {
"name": "support.function.builtin.eftl", "name": "meta.tag.block.eftl",
"match": "\\b(GETVALUEBYTAG|SCHEMAID|PLUGIN_DOM_PARAM)\\b" "begin": "(\\[)(TAG)(?:\\s+[^\\x00]*?)?(?<!/)(\\])",
"end": "(\\[/)(TAG)(\\])",
"beginCaptures": {
"1": { "name": "entity.name.tag.eftl" },
"2": { "name": "entity.name.tag.eftl" },
"3": { "name": "entity.name.tag.eftl" }
},
"endCaptures": {
"1": { "name": "entity.name.tag.eftl" },
"2": { "name": "entity.name.tag.eftl" },
"3": { "name": "entity.name.tag.eftl" }
},
"patterns": [
{ "include": "#expressions" },
{ "include": "#tags" },
{ "include": "#functions" },
{ "include": "#control-structures" },
{ "include": "#variables" }
]
}, },
{ {
"name": "variable.parameter.eftl", "name": "meta.tag.eftl",
"match": "\\b(REQUEST|MODULE|IUQOID)\\b" "begin": "(\\[)(TAG)\\b",
}, "end": "(/\\s*\\]|(?<!/)\\])",
{ "beginCaptures": {
"name": "constant.numeric.eftl", "1": { "name": "entity.name.tag.eftl" },
"match": "\\b[0-9]+\\b" "2": { "name": "entity.name.tag.eftl" }
}, },
{ "endCaptures": {
"name": "variable.other.eftl", "0": { "name": "entity.name.tag.eftl" }
"match": "\\b(COL[0-9]+)\\b" },
}, "patterns": [
{ { "include": "#tag-open-context" }
"name": "punctuation.separator.eftl", ]
"match": ","
} }
] ]
}, },
@@ -175,14 +215,18 @@
"patterns": [ "patterns": [
{ {
"name": "meta.header.eftl", "name": "meta.header.eftl",
"match": "(\\[HEADER)\\s+(name=\"[^\"]*\")\\s+(value=\"[^\"]*\")\\s+(type=\"[^\"]*\")\\s*(/\\])", "begin": "(\\[)(HEADER)\\b",
"captures": { "end": "(/\\s*\\]|(?<!/)\\])",
"beginCaptures": {
"1": { "name": "keyword.other.header.eftl" }, "1": { "name": "keyword.other.header.eftl" },
"2": { "name": "entity.other.attribute-name.eftl" }, "2": { "name": "keyword.other.header.eftl" }
"3": { "name": "string.quoted.double.eftl" }, },
"4": { "name": "entity.other.attribute-name.eftl" }, "endCaptures": {
"5": { "name": "punctuation.definition.tag.end.eftl" } "0": { "name": "keyword.other.header.eftl" }
} },
"patterns": [
{ "include": "#tag-open-context" }
]
} }
] ]
}, },
@@ -201,146 +245,79 @@
"variables": { "variables": {
"patterns": [ "patterns": [
{ {
"name": "meta.variable.declaration.eftl", "name": "meta.variable.block.eftl",
"begin": "(\\[VAR)", "begin": "(\\[)(VAR)(?:\\s+[^\\x00]*?)?(?<!/)(\\])",
"end": "(\\[/VAR\\])", "end": "(\\[/)(VAR)(\\])",
"beginCaptures": { "beginCaptures": {
"1": { "name": "storage.type.variable.eftl" } "1": { "name": "storage.type.variable.eftl" },
"2": { "name": "storage.type.variable.eftl" },
"3": { "name": "storage.type.variable.eftl" }
}, },
"endCaptures": { "endCaptures": {
"1": { "name": "storage.type.variable.eftl" } "1": { "name": "storage.type.variable.eftl" },
"2": { "name": "storage.type.variable.eftl" },
"3": { "name": "storage.type.variable.eftl" }
}, },
"patterns": [ "patterns": [
{ "include": "#var-attributes" },
{ "include": "#expressions" }, { "include": "#expressions" },
{ "include": "#functions" }, { "include": "#functions" },
{ "include": "#tags" } { "include": "#tags" },
{ "include": "#control-structures" }
] ]
}
]
},
"var-attributes": {
"patterns": [
{
"comment": "name attribute - variable name gets special color",
"match": "(name)(=)(\"([^\"]*)\")",
"captures": {
"1": { "name": "entity.other.attribute-name.name.eftl" },
"2": { "name": "punctuation.separator.key-value.eftl" },
"3": { "name": "string.quoted.double.eftl" },
"4": { "name": "variable.other.declaration.eftl" }
}
}, },
{ {
"comment": "type attribute with valid type values", "name": "meta.variable.declaration.eftl",
"match": "(type)(=)(\"(string|boolean|number|date|object|iterable)\")", "begin": "(\\[)(VAR)\\b",
"captures": { "end": "(/\\s*\\]|(?<!/)\\])",
"1": { "name": "entity.other.attribute-name.type.eftl" }, "beginCaptures": {
"2": { "name": "punctuation.separator.key-value.eftl" }, "1": { "name": "storage.type.variable.eftl" },
"3": { "name": "string.quoted.double.eftl" }, "2": { "name": "storage.type.variable.eftl" }
"4": { "name": "support.type.primitive.eftl" } },
} "endCaptures": {
}, "0": { "name": "storage.type.variable.eftl" }
{ },
"comment": "unique attribute - boolean value", "patterns": [
"match": "(unique)(=)(\"(true|false)\")", { "include": "#tag-open-context" }
"captures": { ]
"1": { "name": "entity.other.attribute-name.unique.eftl" },
"2": { "name": "punctuation.separator.key-value.eftl" },
"3": { "name": "string.quoted.double.eftl" },
"4": { "name": "constant.language.boolean.eftl" }
}
},
{
"comment": "closing bracket of VAR open tag",
"match": "\\]",
"name": "punctuation.definition.tag.end.eftl"
} }
] ]
}, },
"functions": { "functions": {
"patterns": [ "patterns": [
{ {
"name": "meta.function.split.eftl", "name": "meta.function.block.eftl",
"begin": "(\\[SPLIT)\\s+(regex=\"[^\"]*\")(\\])?", "begin": "(\\[)(VALUE_OF|SIZE_OF|SPLIT|FORMAT|CONTAINS|TRIM)(?:\\s+[^\\x00]*?)?(?<!/)(\\])",
"end": "(\\[/SPLIT\\])", "end": "(\\[/)(VALUE_OF|SIZE_OF|SPLIT|FORMAT|CONTAINS|TRIM)(\\])",
"beginCaptures": { "beginCaptures": {
"1": { "name": "support.function.eftl" }, "1": { "name": "support.function.eftl" },
"2": { "name": "string.regexp.eftl" } "2": { "name": "support.function.eftl" },
"3": { "name": "support.function.eftl" }
}, },
"endCaptures": { "endCaptures": {
"1": { "name": "support.function.eftl" } "1": { "name": "support.function.eftl" },
"2": { "name": "support.function.eftl" },
"3": { "name": "support.function.eftl" }
}, },
"patterns": [ "patterns": [
{ "include": "#expressions" }, { "include": "#expressions" },
{ "include": "#functions" },
{ "include": "#tags" }, { "include": "#tags" },
{ "include": "#functions" } { "include": "#control-structures" }
] ]
}, },
{ {
"name": "meta.function.trim.eftl", "name": "meta.function.eftl",
"begin": "(\\[TRIM\\])", "begin": "(\\[)(VALUE_OF|SIZE_OF|SPLIT|FORMAT|CONTAINS|TRIM)\\b",
"end": "(\\[/TRIM\\])", "end": "(/\\s*\\]|(?<!/)\\])",
"beginCaptures": {
"1": { "name": "support.function.eftl" }
},
"endCaptures": {
"1": { "name": "support.function.eftl" }
},
"patterns": [
{ "include": "#expressions" },
{ "include": "#tags" },
{ "include": "#functions" }
]
},
{
"name": "meta.function.contains.eftl",
"begin": "(\\[CONTAINS)\\s+(varname=\"[^\"]*\")(\\])?",
"end": "(\\[/CONTAINS\\])",
"beginCaptures": { "beginCaptures": {
"1": { "name": "support.function.eftl" }, "1": { "name": "support.function.eftl" },
"2": { "name": "entity.other.attribute-name.eftl" } "2": { "name": "support.function.eftl" }
}, },
"endCaptures": { "endCaptures": {
"1": { "name": "support.function.eftl" } "0": { "name": "support.function.eftl" }
}, },
"patterns": [ "patterns": [
{ "include": "#functions" } { "include": "#tag-open-context" }
]
},
{
"name": "meta.function.value-of.eftl",
"match": "(\\[VALUE_OF)\\s+(varname=\"[^\"]*\")\\s*(index=\"?[^\"]*\"?)?\\s*(/\\])",
"captures": {
"1": { "name": "support.function.eftl" },
"2": { "name": "entity.other.attribute-name.eftl" },
"3": { "name": "entity.other.attribute-name.eftl" },
"4": { "name": "punctuation.definition.tag.end.eftl" }
}
},
{
"name": "meta.function.size-of.eftl",
"match": "(\\[SIZE_OF)\\s+(varname=\"[^\"]*\")\\s*(/\\])",
"captures": {
"1": { "name": "support.function.eftl" },
"2": { "name": "entity.other.attribute-name.eftl" },
"3": { "name": "punctuation.definition.tag.end.eftl" }
}
},
{
"name": "meta.function.format.eftl",
"begin": "(\\[FORMAT)\\s+(type=\"[^\"]*\")\\s*(pattern=\"[^\"]*\")?(\\])?",
"end": "(\\[/FORMAT\\])",
"beginCaptures": {
"1": { "name": "support.function.eftl" },
"2": { "name": "entity.other.attribute-name.eftl" },
"3": { "name": "string.other.pattern.eftl" }
},
"endCaptures": {
"1": { "name": "support.function.eftl" }
},
"patterns": [
{ "include": "#expressions" }
] ]
} }
] ]