diff --git a/README.md b/README.md index c41c7c4..f95dacd 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ Syntax highlighting and language support for **EFTL (ElixForms Template Language - `[EFTL]...[/EFTL]` - Main EFTL block - `[VAR name="..." type="..."]...[/VAR]` - Variable declarations - `[IF]...[/IF]` - Conditionals -- `[WHILE]...[/WHILE]` - Loops +- `[WHILE threshold="..."]...[/WHILE]` - Loops with an optional string threshold ### Expressions @@ -40,7 +40,7 @@ Syntax highlighting and language support for **EFTL (ElixForms Template Language ### Control Structures - `[IF]`, `[CONDITION]`, `[THEN]`, `[ELSE]`, `[ELSE IF]` -- `[WHILE]`, `[DO]` +- `[WHILE]`, `[WHILE threshold="..."]`, `[DO]` ### Comments @@ -105,25 +105,26 @@ If you want to debug the extension or the language server: ## Snippets -| Prefix | Description | -| ----------- | ---------------------- | -| `eftl` | EFTL block with header | -| `var` | Variable declaration | -| `vartag` | Variable from TAG | -| `varschema` | Variable from SCHEMAID | -| `if` | IF statement | -| `ifelse` | IF-ELSE statement | -| `while` | WHILE loop | -| `split` | Split function | -| `trim` | Trim function | -| `valueof` | VALUE_OF function | -| `sizeof` | SIZE_OF function | -| `contains` | CONTAINS function | -| `formatnum` | Format number | -| `tag` | GETVALUEBYTAG tag | -| `schema` | SCHEMAID tag | -| `out` | Output expression | -| `comment` | Comment block | +| Prefix | Description | +| ---------------- | ------------------------- | +| `eftl` | EFTL block with header | +| `var` | Variable declaration | +| `vartag` | Variable from TAG | +| `varschema` | Variable from SCHEMAID | +| `if` | IF statement | +| `ifelse` | IF-ELSE statement | +| `while` | WHILE loop | +| `whilethreshold` | WHILE loop with threshold | +| `split` | Split function | +| `trim` | Trim function | +| `valueof` | VALUE_OF function | +| `sizeof` | SIZE_OF function | +| `contains` | CONTAINS function | +| `formatnum` | Format number | +| `tag` | GETVALUEBYTAG tag | +| `schema` | SCHEMAID tag | +| `out` | Output expression | +| `comment` | Comment block | ## Example diff --git a/examples/sample2.eftl b/examples/sample2.eftl index 9356349..1d4ce8a 100644 --- a/examples/sample2.eftl +++ b/examples/sample2.eftl @@ -8,7 +8,7 @@ [VAR name="CONTRAENTI" type="iterable"][SPLIT regex="\n"][TRIM][TAG]SCHEMAID,296,COL0004,IUQOID, , [/TAG][/TRIM][/SPLIT][/VAR] [VAR name="CONTRAENTI_NUM" type="number"][SIZE_OF varname="CONTRAENTI" /][/VAR] -[WHILE] +[WHILE threshold="99"] [CONDITION][% IDX < CONTRAENTI_NUM %][/CONDITION] [DO] [VAR name="CONTRAENTE" type="string"][VALUE_OF varname="CONTRAENTI" index="IDX" /][/VAR] diff --git a/language-configuration.json b/language-configuration.json index ccaee28..e99b4ab 100644 --- a/language-configuration.json +++ b/language-configuration.json @@ -6,7 +6,7 @@ ["[EFTL]", "[/EFTL]"], ["[VAR", "[/VAR]"], ["[IF]", "[/IF]"], - ["[WHILE]", "[/WHILE]"], + ["[WHILE", "[/WHILE]"], ["[FORMAT", "[/FORMAT]"], ["[CONDITION]", "[/CONDITION]"], ["[THEN]", "[/THEN]"], diff --git a/snippets/eftl.json b/snippets/eftl.json index 6cc3d1d..05da051 100644 --- a/snippets/eftl.json +++ b/snippets/eftl.json @@ -79,6 +79,18 @@ ], "description": "Create a WHILE loop" }, + "While Loop with Threshold": { + "prefix": "whilethreshold", + "body": [ + "[WHILE threshold=\"${1:99}\"]", + " [CONDITION][% ${2:condition} %][/CONDITION]", + " [DO]", + " $0", + " [/DO]", + "[/WHILE]" + ], + "description": "Create a WHILE loop with a threshold" + }, "Split": { "prefix": "split", "body": [ diff --git a/src/parser.ts b/src/parser.ts index 50ffb23..8da550a 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -128,6 +128,9 @@ export class EftlParser { break; case TokenType.WHILE_OPEN: + if (!/^\[WHILE(?:\s+threshold\s*=\s*"[^"]*")?\s*\]$/.test(token.value)) { + this.addError(token, 'WHILE accepts only the optional string attribute threshold="..."'); + } this.pushBlock(token); this.advance(); break; diff --git a/src/tokenizer.ts b/src/tokenizer.ts index 04dcbcb..cf7f666 100644 --- a/src/tokenizer.ts +++ b/src/tokenizer.ts @@ -21,7 +21,7 @@ export enum TokenType { ELSE_CLOSE = 'ELSE_CLOSE', // [/ELSE] ELSE_IF_OPEN = 'ELSE_IF_OPEN', // [ELSE IF] ELSE_IF_CLOSE = 'ELSE_IF_CLOSE', // [/ELSE IF] - WHILE_OPEN = 'WHILE_OPEN', // [WHILE] + WHILE_OPEN = 'WHILE_OPEN', // [WHILE ...] WHILE_CLOSE = 'WHILE_CLOSE', // [/WHILE] DO_OPEN = 'DO_OPEN', // [DO] DO_CLOSE = 'DO_CLOSE', // [/DO] @@ -192,6 +192,11 @@ export class EftlTokenizer { this.addToken(TokenType.WHILE_OPEN, '[WHILE]', startLine, startColumn); return; } + if (this.source.startsWith('[WHILE', this.pos) && /\s/.test(this.source[this.pos + '[WHILE'.length] || '')) { + this.match('[WHILE'); + this.scanTagWithAttributes(TokenType.WHILE_OPEN, 'WHILE', startLine, startColumn); + return; + } if (this.match('[/WHILE]')) { this.addToken(TokenType.WHILE_CLOSE, '[/WHILE]', startLine, startColumn); return; diff --git a/syntaxes/eftl.tmLanguage.json b/syntaxes/eftl.tmLanguage.json index 52e4ad2..a80c2fb 100644 --- a/syntaxes/eftl.tmLanguage.json +++ b/syntaxes/eftl.tmLanguage.json @@ -236,9 +236,24 @@ "name": "keyword.control.conditional.eftl", "match": "\\[(IF|ELSE IF|ELSE|/IF|CONDITION|/CONDITION|THEN|/THEN|/ELSE IF|/ELSE)\\]" }, + { + "name": "meta.control.loop.eftl", + "begin": "(\\[)(WHILE)\\b", + "end": "(\\])", + "beginCaptures": { + "1": { "name": "keyword.control.loop.eftl" }, + "2": { "name": "keyword.control.loop.eftl" } + }, + "endCaptures": { + "1": { "name": "keyword.control.loop.eftl" } + }, + "patterns": [ + { "include": "#attributes" } + ] + }, { "name": "keyword.control.loop.eftl", - "match": "\\[(WHILE|/WHILE|DO|/DO)\\]" + "match": "\\[(/WHILE|DO|/DO)\\]" } ] },