diff --git a/package.json b/package.json index 40f8ba7..126c0c3 100644 --- a/package.json +++ b/package.json @@ -50,7 +50,7 @@ { "language": "raku", "scopeName": "source.raku", - "path": "./server/raku.tmLanguage.json" + "path": "./server/raku.merged.tmLanguage.json" } ], "snippets": [ diff --git a/scripts/merge-grammars.js b/scripts/merge-grammars.js new file mode 100644 index 0000000..77a5f0d --- /dev/null +++ b/scripts/merge-grammars.js @@ -0,0 +1,64 @@ +// Merges raku.regex.tmLanguage.json and raku.quoting.tmLanguage.json +// into raku.tmLanguage.json so vscode-textmate can resolve all includes +// without needing cross-grammar support. +// +// To generate raku.merged.tmLanguage.json: node ./scripts/merge-grammars.js + +const fs = require('fs'); +const path = require('path'); + +const serverDir = path.join(__dirname, '../server'); +const mainPath = path.join(serverDir, 'raku.tmLanguage.json'); +const regexPath = path.join(serverDir, 'raku.regex.tmLanguage.json'); +const quotingPath = path.join(serverDir, 'raku.quoting.tmLanguage.json'); +const outputPath = path.join(serverDir, 'raku.merged.tmLanguage.json'); + +const main = JSON.parse(fs.readFileSync(mainPath, 'utf8')); +const regex = JSON.parse(fs.readFileSync(regexPath, 'utf8')); +const quoting = JSON.parse(fs.readFileSync(quotingPath, 'utf8')); + +// 1. Merge repository entries from both external grammars into main +// Skip keys that already exist in main to avoid overwriting. +function mergeRepository(source) { + for (const [key, value] of Object.entries(source.repository || {})) { + if (main.repository[key] !== undefined) { + console.log(` Skipping duplicate key: ${key}`); + } else { + main.repository[key] = value; + } + } +} + +console.log('Merging source.regexp.raku ...'); +mergeRepository(regex); + +console.log('Merging source.quoting.raku ...'); +mergeRepository(quoting); + +// 2a. Add a wrapper rule for bare "source.quoting.raku" (no fragment), +// which means "include all top-level patterns of the quoting grammar". +if (!main.repository['__quoting__']) { + main.repository['__quoting__'] = { patterns: quoting.patterns }; +} + +// 2. Rewrite cross-grammar include references in the whole JSON. +// "source.regexp.raku" -> "#regexp" (top-level of regex grammar is #regexp) +// "source.quoting.raku#foo" -> "#foo" +// "source.quoting.raku" -> "#__quoting__" +let text = JSON.stringify(main, null, '\t'); + +const before = (text.match(/"source\.(regexp|quoting)\.raku/g) || []).length; + +text = text.replace(/"source\.regexp\.raku"/g, '"#regexp"'); +text = text.replace(/"source\.quoting\.raku#([\w-]+)"/g, '"#$1"'); +text = text.replace(/"source\.quoting\.raku"/g, '"#__quoting__"'); + +const after = (text.match(/"source\.(regexp|quoting)\.raku/g) || []).length; +console.log(`Replaced cross-grammar includes: ${before} found, ${after} remaining`); + +if (after > 0) { + console.warn('WARNING: Some cross-grammar includes were not rewritten. Check the output file.'); +} + +fs.writeFileSync(outputPath, text, 'utf8'); +console.log(`Done. Written to ${outputPath}`); diff --git a/server/raku.merged.tmLanguage.json b/server/raku.merged.tmLanguage.json new file mode 100644 index 0000000..2e314f5 --- /dev/null +++ b/server/raku.merged.tmLanguage.json @@ -0,0 +1,5376 @@ +{ + "fileTypes": [ + "nqp", + "raku", + "rakumod", + "rakudoc", + "rakutest", + "t6", + "p6", + "pl6", + "pm6", + "pod6" + ], + "firstLineMatch": "(?x) ^ \\s* (?: use \\s v6 | =begin \\s pod | =comment | \\#!(?: (?:perl6|raku)|/.*(?:perl6|raku) (?! \\S)) | my\\s*class )", + "name": "Raku", + "patterns": [ + { + "begin": "^\\s*(=)(finish)", + "beginCaptures": { + "1": { + "name": "storage.modifier.block.finish.raku" + }, + "2": { + "name": "keyword.operator.block.finish.raku" + } + }, + "contentName": "comment.block.finish.raku", + "patterns": [ + { + "include": "#comment-block-syntax" + } + ] + }, + { + "include": "#comment-block-delimited" + }, + { + "begin": "^\\s*(=)(?:(para)|(for)\\s+(\\w+))", + "beginCaptures": { + "1": { + "name": "storage.modifier.block.paragraph.raku" + }, + "2": { + "name": "keyword.operator.block.paragraph.raku" + }, + "3": { + "name": "entity.other.attribute-name.paragraph.raku" + } + }, + "contentName": "comment.block.paragraph.raku", + "end": "(?=^\\s*$|^\\s*=\\w+.*$)", + "patterns": [ + { + "include": "#comment-block-syntax" + } + ] + }, + { + "include": "#comment-block-abbreviated" + }, + { + "captures": { + "1": { + "name": "comment.punctuation.pound.raku" + }, + "2": { + "name": "meta.declarator.raku" + }, + "3": { + "name": "comment.inline.declarator.raku" + } + }, + "match": "^\\s*(#)([\\|\\=])(.*)\\n", + "name": "meta.documentation.block.declarator.raku" + }, + { + "begin": "\\s*#`\\(\\(\\(", + "end": "\\)\\)\\)", + "name": "comment.multiline.hash-tick.triple_paren.raku", + "patterns": [ + { + "begin": "\\(\\(\\(", + "end": "\\)\\)\\)", + "name": "comment.internal.triple_paren.raku" + } + ] + }, + { + "begin": "\\s*#`\\[\\[\\[", + "end": "\\]\\]\\]", + "name": "comment.multiline.hash-tick.triple_bracket.raku", + "patterns": [ + { + "begin": "\\[\\[\\[", + "end": "\\]\\]\\]", + "name": "comment.internal.triple_bracket.raku" + } + ] + }, + { + "begin": "\\s*#`\\{\\{\\{", + "end": "\\}\\}\\}", + "name": "comment.multiline.hash-tick.triple_brace.raku", + "patterns": [ + { + "begin": "\\{\\{\\{", + "end": "\\}\\}\\}", + "name": "comment.internal.triple_brace.raku" + } + ] + }, + { + "begin": "\\s*#`<<<", + "end": ">>>", + "name": "comment.multiline.hash-tick.triple_angle.raku", + "patterns": [ + { + "begin": "<<<", + "end": ">>>", + "name": "comment.internal.triple_angle.raku" + } + ] + }, + { + "begin": "\\s*#`<<", + "end": ">>", + "name": "comment.multiline.hash-tick.double_angle.raku", + "patterns": [ + { + "begin": "<<", + "end": ">>", + "name": "comment.internal.double_angle.raku" + } + ] + }, + { + "begin": "\\s*#`\\(\\(", + "end": "\\)\\)", + "name": "comment.multiline.hash-tick.double_paren.raku", + "patterns": [ + { + "begin": "\\(\\(", + "end": "\\)\\)", + "name": "comment.internal.double_paren.raku" + } + ] + }, + { + "begin": "\\s*#`\\[\\[", + "end": "\\]\\]", + "name": "comment.multiline.hash-tick.double_bracket.raku", + "patterns": [ + { + "begin": "\\[\\[", + "end": "\\]\\]", + "name": "comment.internal.double_bracket.raku" + } + ] + }, + { + "begin": "\\s*#`{{", + "end": "}}", + "name": "comment.multiline.hash-tick.double_brace.raku", + "patterns": [ + { + "begin": "{{", + "end": "}}", + "name": "comment.internal.double_brace.raku" + } + ] + }, + { + "begin": "\\s*#`{", + "end": "}", + "name": "comment.multiline.hash-tick.brace.raku", + "patterns": [ + { + "begin": "{", + "end": "}", + "name": "comment.internal.brace.raku" + } + ] + }, + { + "begin": "\\s*#`<", + "end": ">", + "name": "comment.multiline.hash-tick.angle.raku", + "patterns": [ + { + "begin": "<", + "end": ">", + "name": "comment.internal.angle.raku" + } + ] + }, + { + "begin": "\\s*#`\\(", + "end": "\\)", + "name": "comment.multiline.hash-tick.paren.raku", + "patterns": [ + { + "begin": "\\(", + "end": "\\)", + "name": "comment.internal.paren.raku" + } + ] + }, + { + "begin": "\\s*#`\\[", + "end": "\\]", + "name": "comment.multiline.hash-tick.bracket.raku", + "patterns": [ + { + "begin": "\\[", + "end": "\\]", + "name": "comment.internal.bracket.raku" + } + ] + }, + { + "begin": "\\s*#`“", + "end": "”", + "name": "comment.multiline.hash-tick.left_double_right_double.raku", + "patterns": [ + { + "begin": "“", + "end": "”", + "name": "comment.internal.left_double_right_double.raku" + } + ] + }, + { + "begin": "\\s*#`„", + "end": "”|“", + "name": "comment.multiline.hash-tick.left_double-low-q_right_double.raku", + "patterns": [ + { + "begin": "„", + "end": "”|“", + "name": "comment.internal.left_double-low-q_right_double.raku" + } + ] + }, + { + "begin": "\\s*#`‘", + "end": "’", + "name": "comment.multiline.hash-tick.left_single_right_single.raku", + "patterns": [ + { + "begin": "‘", + "end": "’", + "name": "comment.internal.left_single_right_single.raku" + } + ] + }, + { + "begin": "\\s*#`‚", + "end": "‘", + "name": "comment.multiline.hash-tick.low-q_left_single.raku", + "patterns": [ + { + "begin": "‚", + "end": "‘", + "name": "comment.internal.low-q_left_single.raku" + } + ] + }, + { + "begin": "\\s*#`「", + "end": "」", + "name": "comment.multiline.hash-tick.fw_cornerbracket.raku", + "patterns": [ + { + "begin": "「", + "end": "」", + "name": "comment.internal.fw_cornerbracket.raku" + } + ] + }, + { + "begin": "\\s*#`「", + "end": "」", + "name": "comment.multiline.hash-tick.hw_cornerbracket.raku", + "patterns": [ + { + "begin": "「", + "end": "」", + "name": "comment.internal.hw_cornerbracket.raku" + } + ] + }, + { + "begin": "\\s*#`«", + "end": "»", + "name": "comment.multiline.hash-tick.chevron.raku", + "patterns": [ + { + "begin": "«", + "end": "»", + "name": "comment.internal.chevron.raku" + } + ] + }, + { + "begin": "\\s*#`⟅", + "end": "⟆", + "name": "comment.multiline.hash-tick.s-shaped-bag-delimiter.raku", + "patterns": [ + { + "begin": "⟅", + "end": "⟆", + "name": "comment.internal.s-shaped-bag-delimiter.raku" + } + ] + }, + { + "begin": "“", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.raku" + } + }, + "end": "”", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.raku" + } + }, + "name": "string.quoted.left_double_right_double.raku", + "patterns": [ + { + "match": "\\\\[“”abtnfre\\\\\\{\\}]", + "name": "constant.character.escape.raku" + }, + { + "include": "#interpolation" + }, + { + "include": "#q_left_double_right_double_string_content" + } + ] + }, + { + "begin": "„", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.raku" + } + }, + "end": "”|“", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.raku" + } + }, + "name": "string.quoted.left_double-low-q_right_double.raku", + "patterns": [ + { + "match": "\\\\[„”|“abtnfre\\\\\\{\\}]", + "name": "constant.character.escape.raku" + }, + { + "include": "#interpolation" + }, + { + "include": "#q_left_double-low-q_right_double_string_content" + } + ] + }, + { + "begin": "(?<=\\W|^)‘", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.raku" + } + }, + "end": "’", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.raku" + } + }, + "name": "string.quoted.single.left_single_right_single.raku", + "patterns": [ + { + "match": "\\\\[‘’\\\\]", + "name": "constant.character.escape.raku" + }, + { + "include": "#q_left_single_right_single_string_content" + } + ] + }, + { + "begin": "(?<=\\W|^)‚", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.raku" + } + }, + "end": "‘", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.raku" + } + }, + "name": "string.quoted.single.low-q_left_single.raku", + "patterns": [ + { + "match": "\\\\[‚‘\\\\]", + "name": "constant.character.escape.raku" + }, + { + "include": "#q_low-q_left_single_string_content" + } + ] + }, + { + "begin": "(?<=\\W|^)'", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.raku" + } + }, + "end": "'", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.raku" + } + }, + "name": "string.quoted.single.single.raku", + "patterns": [ + { + "match": "\\\\['\\\\]", + "name": "constant.character.escape.raku" + }, + { + "include": "#q_single_string_content" + } + ] + }, + { + "begin": "\"", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.raku" + } + }, + "end": "\"", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.raku" + } + }, + "name": "string.quoted.double.raku", + "patterns": [ + { + "match": "\\\\[\"abtnfre\\\\\\{\\}]", + "name": "constant.character.escape.raku" + }, + { + "include": "#interpolation" + }, + { + "include": "#q_double_string_content" + } + ] + }, + { + "begin": "”", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.raku" + } + }, + "end": "”", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.raku" + } + }, + "name": "string.quoted.right_double_right_double.raku", + "patterns": [ + { + "match": "\\\\[”abtnfre\\\\\\{\\}]", + "name": "constant.character.escape.raku" + }, + { + "include": "#interpolation" + }, + { + "include": "#q_right_double_right_double_string_content" + } + ] + }, + { + "begin": "(^[ \\t]+)?(?=#)", + "beginCaptures": { + "1": { + "name": "punctuation.whitespace.comment.leading.raku" + } + }, + "end": "(?!\\G)", + "patterns": [ + { + "begin": "#", + "beginCaptures": { + "0": { + "name": "punctuation.definition.comment.raku" + } + }, + "end": "\\n", + "name": "comment.line.number-sign.raku" + } + ] + }, + { + "match": "(?x) \\x{2208}|\\(elem\\)|\\x{2209}|\\!\\(elem\\)| \\x{220B}|\\(cont\\)|\\x{220C}|\\!\\(cont\\)| \\x{2286}|\\(<=\\) |\\x{2288}|\\!\\(<=\\) | \\x{2282}|\\(<\\) |\\x{2284}|\\!\\(<\\) | \\x{2287}|\\(>=\\) |\\x{2289}|\\!\\(>=\\) | \\x{2283}|\\(>\\) |\\x{2285}|\\!\\(>\\) | \\x{227C}|\\(<\\+\\)|\\x{227D}|\\(>\\+\\) | \\x{222A}|\\(\\|\\) |\\x{2229}|\\(&\\) | \\x{2216}|\\(\\-\\) |\\x{2296}|\\(\\^\\) | \\x{228D}|\\(\\.\\) |\\x{228E}|\\(\\+\\)", + "name": "keyword.operator.setbagmix.raku" + }, + { + "captures": { + "1": { + "name": "storage.type.class.raku" + }, + "3": { + "name": "entity.name.type.class.raku" + } + }, + "match": "(?x) ( class|enum|grammar|knowhow|module| package|role|slang|subset|monitor|actor ) (\\s+) ( ( (?:::|')? (?: ([a-zA-Z_À-ÿ\\$]) ([a-zA-Z0-9_À-ÿ\\$]|[\\-'][a-zA-Z0-9_À-ÿ\\$])* ) )+ )", + "name": "meta.class.raku" + }, + { + "include": "#p5_regex" + }, + { + "captures": { + "1": { + "name": "punctuation.definition.regexp.raku" + }, + "2": { + "name": "string.regexp.raku", + "patterns": [ + { + "include": "#interpolation" + }, + { + "include": "#regexp" + } + ] + }, + "3": { + "name": "punctuation.definition.regexp.raku" + } + }, + "match": "(?x)\n(?<=\n ^\n | ^\\s\n | [\\s\\(] [^\\p{Nd}\\p{L}]\n | ~~\\s|~~\\s\\s|match\\(\n | match:\\s\n)\n([/]) # Solidus\n(.*?) # Regex contents\n(?: (?|~~) \\s*\n(?:\n (m|rx|s)?\n (\n (?:\n (? ) \\s*\n(?:\n (m|rx|s)\n (\n (?:\n (?)", + "endCaptures": { + "1": { + "name": "punctuation.definition.regexp.angle.raku" + } + }, + "patterns": [ + { + "include": "#interpolation" + }, + { + "include": "#regexp" + } + ] + }, + { + "begin": "(?x)\n(?<= ^|\\s )\n(?:\n (m|rx|s|S)\n (\n (?:\n (?\\-\\]\\)\\}\\{])", + "beginCaptures": { + "1": { + "name": "string.regexp.construct.any.raku" + }, + "2": { + "name": "entity.name.section.adverb.regexp.any.raku" + }, + "3": { + "name": "punctuation.definition.regexp.any.raku" + } + }, + "contentName": "string.regexp.any.raku", + "end": "(?x) (?: (?|\\S)", + "beginCaptures": { + "1": { + "name": "storage.type.declare.regexp.named.raku" + } + }, + "end": "(? ) (?=[\\s\\{])" + }, + { + "begin": "«", + "contentName": "string.array.words.chevron.raku", + "end": "(?x) \\\\\\\\|(? )", + "endCaptures": { + "1": { + "name": "span.keyword.operator.array.words.raku" + } + }, + "patterns": [ + { + "include": "#q_bracket_string_content" + } + ] + }, + { + "match": "(?x) (?: [+:\\-.*/] | \\|\\| )? (?=~] )", + "name": "storage.modifier.assignment.raku" + }, + { + "begin": "(?x) (? ) (?= [^<]* (?: [^<] ) > )", + "beginCaptures": { + "1": { + "name": "span.keyword.operator.array.words.raku" + } + }, + "contentName": "string.array.words.raku", + "end": "(?x) \\\\\\\\|(? )", + "endCaptures": { + "1": { + "name": "span.keyword.operator.array.words.raku" + } + } + }, + { + "match": "\\b(for|loop|repeat|while|until|gather|given)(?!\\-)\\b", + "name": "keyword.control.repeat.raku" + }, + { + "match": "(?x)\n\\b (?)", + "name": "storage.modifier.type.constraints.raku" + }, + { + "match": "(?x)\\b(?)", + "name": "keyword.control.control-handlers.raku" + }, + { + "match": "(?x)\\b(?)", + "name": "entity.name.type.trait.raku" + }, + { + "match": "\\b(NaN|Inf)(?!\\-)\\b", + "name": "constant.numeric.raku" + }, + { + "match": "\\b(True|False)\\b", + "name": "constant.language.boolean.raku" + }, + { + "match": "(?x)\\b(?)", + "name": "constant.language.pragma.raku" + }, + { + "captures": { + "0": { + "name": "support.type.exception.raku" + } + }, + "match": "(?x)(?)" + }, + { + "match": "(?x) ( \\[ / \\] )", + "name": "keyword.operator.reduction.raku" + }, + { + "captures": { + "1": { + "name": "keyword.operator.adverb.raku" + }, + "2": { + "name": "keyword.other.special-method.definedness.raku" + } + }, + "match": "(?<=\\w)(\\:)([DU_])\\b", + "name": "meta.adverb.definedness.raku" + }, + { + "match": "(?x)\\b( div|mod|gcd|lcm|x|xx|temp|let|but|cmp|leg| eq|ne|gt|ge|lt|le|before|after|eqv|min|max|ff|fff|not|so|Z| and|andthen|or|orelse )\\b(?!\\-)| \\b(X)(?!:)\\b", + "name": "keyword.operator.word.raku" + }, + { + "captures": { + "1": { + "name": "keyword.operator.approx-equal.raku" + } + }, + "match": "(=~=|≅)", + "name": "meta.operator.non.ligature.raku" + }, + { + "match": "(?x) <== | ==> | <=> | => | --> | -> | \\+\\| | \\+\\+ | -- | \\*\\* | \\?\\?\\? | \\?\\? | \\!\\!\\! | \\!\\! | && | \\+\\^ | \\?\\^ | %% | \\+& | \\+< | \\+> | \\+\\^ | \\.\\.(?!\\.) | \\.\\.\\^ | \\^\\.\\. | \\^\\.\\.\\^ | \\?\\| | !=(?!\\=) | !==(?!\\=) | <=(?!>) | >= | === | == | =:= | ~~ | \\x{2245} | \\|\\| | \\^\\^ | \\/\\/ | := | ::= | \\.\\.\\.", + "name": "keyword.operator.multi-symbol.raku" + }, + { + "include": "#special_variables" + }, + { + "captures": { + "1": { + "name": "constant.language.whatever.raku" + }, + "2": { + "name": "keyword.operator.minus.back-from.raku" + }, + "3": { + "name": "constant.numeric.back-from.raku" + } + }, + "match": "(?x)(?<=\\[) \\s* (\\*) \\s* ([\\-\\*%\\^\\+\\/]|div|mod|gcd|lcm) \\s* (\\d+) \\s* (?=\\])", + "name": "meta.subscript.whatever.raku" + }, + { + "match": "\\*\\s*(?=\\])", + "name": "constant.language.whatever.hack.raku" + }, + { + "captures": { + "1": { + "name": "keyword.operator.colon.raku" + } + }, + "match": "(?x)\\b(?)", + "name": "support.function.raku" + }, + { + "captures": { + "1": { + "name": "keyword.operator.colon.raku" + } + }, + "match": "(?x)\\b(?)", + "name": "support.function.raku" + }, + { + "match": "(?x)\\b(?)", + "name": "support.function.raku" + }, + { + "include": "#numbers" + }, + { + "match": "(?x) (?<\\*\\!\\?~\\/\\|]| (?)", + "name": "string.pair.key.raku" + }, + { + "match": "(?x) \\b (?", + "endCaptures": { + "0": { + "name": "punctuation.definition.radix.raku" + } + } + }, + { + "begin": "\\{", + "beginCaptures": { + "0": { + "name": "punctuation.definition.block.raku" + } + }, + "end": "\\}", + "endCaptures": { + "0": { + "name": "punctuation.definition.block.raku" + } + }, + "name": "meta.block.raku", + "patterns": [ + { + "include": "$self" + } + ] + } + ], + "repository": { + "comment-block-abbreviated": { + "patterns": [ + { + "begin": "^\\s*(=)(head\\w*)\\s+(.+?)\\s*$", + "captures": { + "1": { + "name": "storage.modifier.block.abbreviated.raku" + }, + "2": { + "name": "entity.other.attribute-name.block.abbreviated.raku" + }, + "3": { + "name": "entity.name.section.abbreviated.raku", + "patterns": [ + { + "include": "#comment-block-syntax" + } + ] + } + }, + "contentName": "entity.name.section.head.abbreviated.raku", + "end": "(?=^\\s*$|^\\s*=\\w+.*$)", + "patterns": [ + { + "include": "#comment-block-syntax" + } + ] + }, + { + "begin": "^\\s*(=)(\\w+)\\s+(.+?)\\s*$", + "captures": { + "1": { + "name": "storage.modifier.block.abbreviated.raku" + }, + "2": { + "name": "entity.other.attribute-name.block.abbreviated.raku" + }, + "3": { + "name": "entity.name.section.abbreviated.raku", + "patterns": [ + { + "include": "#comment-block-syntax" + } + ] + } + }, + "contentName": "comment.block.abbreviated.raku", + "end": "(?=^\\s*$|^\\s*=\\w+.*$)", + "patterns": [ + { + "include": "#comment-block-syntax" + } + ] + } + ] + }, + "comment-block-delimited": { + "patterns": [ + { + "begin": "^\\s*(=)(begin)\\s+(\\w+)", + "captures": { + "1": { + "name": "storage.modifier.block.delimited.raku" + }, + "2": { + "name": "keyword.operator.block.delimited.raku" + }, + "3": { + "name": "entity.other.attribute-name.block.delimited.raku" + } + }, + "contentName": "comment.block.delimited.raku", + "end": "^\\s*(=)(end)\\s+(\\w+)", + "patterns": [ + { + "include": "#comment-block-syntax" + } + ] + } + ] + }, + "comment-block-syntax": { + "patterns": [ + { + "include": "#comment-block-delimited" + }, + { + "include": "#comment-block-abbreviated" + }, + { + "begin": "(?x) (U) (<<<)", + "beginCaptures": { + "1": { + "name": "support.function.pod.code.raku" + }, + "2": { + "name": "punctuation.section.embedded.pod.code.raku" + } + }, + "contentName": "markup.underline.raku", + "end": "(?x) (>>>)", + "endCaptures": { + "1": { + "name": "punctuation.section.embedded.pod.code.raku" + } + }, + "name": "meta.pod.c.raku", + "patterns": [ + { + "include": "#comment-block-syntax" + }, + { + "include": "#q_triple_angle_string_content" + } + ] + }, + { + "begin": "(?x) (I) (<<<)", + "beginCaptures": { + "1": { + "name": "support.function.pod.code.raku" + }, + "2": { + "name": "punctuation.section.embedded.pod.code.raku" + } + }, + "contentName": "markup.italic.raku", + "end": "(?x) (>>>)", + "endCaptures": { + "1": { + "name": "punctuation.section.embedded.pod.code.raku" + } + }, + "name": "meta.pod.c.raku", + "patterns": [ + { + "include": "#comment-block-syntax" + }, + { + "include": "#q_triple_angle_string_content" + } + ] + }, + { + "begin": "(?x) (B) (<<<)", + "beginCaptures": { + "1": { + "name": "support.function.pod.code.raku" + }, + "2": { + "name": "punctuation.section.embedded.pod.code.raku" + } + }, + "contentName": "markup.bold.raku", + "end": "(?x) (>>>)", + "endCaptures": { + "1": { + "name": "punctuation.section.embedded.pod.code.raku" + } + }, + "name": "meta.pod.c.raku", + "patterns": [ + { + "include": "#comment-block-syntax" + }, + { + "include": "#q_triple_angle_string_content" + } + ] + }, + { + "begin": "(?x) ([A-Z]) (<<<)", + "beginCaptures": { + "1": { + "name": "support.function.pod.code.raku" + }, + "2": { + "name": "punctuation.section.embedded.pod.code.raku" + } + }, + "contentName": "markup.raw.code.raku", + "end": "(?x) (>>>)", + "endCaptures": { + "1": { + "name": "punctuation.section.embedded.pod.code.raku" + } + }, + "name": "meta.pod.c.raku", + "patterns": [ + { + "include": "#comment-block-syntax" + }, + { + "include": "#q_triple_angle_string_content" + } + ] + }, + { + "begin": "(?x) (U) (<<)", + "beginCaptures": { + "1": { + "name": "support.function.pod.code.raku" + }, + "2": { + "name": "punctuation.section.embedded.pod.code.raku" + } + }, + "contentName": "markup.underline.raku", + "end": "(?x) (>>)", + "endCaptures": { + "1": { + "name": "punctuation.section.embedded.pod.code.raku" + } + }, + "name": "meta.pod.c.raku", + "patterns": [ + { + "include": "#comment-block-syntax" + }, + { + "include": "#q_double_angle_string_content" + } + ] + }, + { + "begin": "(?x) (I) (<<)", + "beginCaptures": { + "1": { + "name": "support.function.pod.code.raku" + }, + "2": { + "name": "punctuation.section.embedded.pod.code.raku" + } + }, + "contentName": "markup.italic.raku", + "end": "(?x) (>>)", + "endCaptures": { + "1": { + "name": "punctuation.section.embedded.pod.code.raku" + } + }, + "name": "meta.pod.c.raku", + "patterns": [ + { + "include": "#comment-block-syntax" + }, + { + "include": "#q_double_angle_string_content" + } + ] + }, + { + "begin": "(?x) (B) (<<)", + "beginCaptures": { + "1": { + "name": "support.function.pod.code.raku" + }, + "2": { + "name": "punctuation.section.embedded.pod.code.raku" + } + }, + "contentName": "markup.bold.raku", + "end": "(?x) (>>)", + "endCaptures": { + "1": { + "name": "punctuation.section.embedded.pod.code.raku" + } + }, + "name": "meta.pod.c.raku", + "patterns": [ + { + "include": "#comment-block-syntax" + }, + { + "include": "#q_double_angle_string_content" + } + ] + }, + { + "begin": "(?x) ([A-Z]) (<<)", + "beginCaptures": { + "1": { + "name": "support.function.pod.code.raku" + }, + "2": { + "name": "punctuation.section.embedded.pod.code.raku" + } + }, + "contentName": "markup.raw.code.raku", + "end": "(?x) (>>)", + "endCaptures": { + "1": { + "name": "punctuation.section.embedded.pod.code.raku" + } + }, + "name": "meta.pod.c.raku", + "patterns": [ + { + "include": "#comment-block-syntax" + }, + { + "include": "#q_double_angle_string_content" + } + ] + }, + { + "begin": "(?x) (U) (<)", + "beginCaptures": { + "1": { + "name": "support.function.pod.code.raku" + }, + "2": { + "name": "punctuation.section.embedded.pod.code.raku" + } + }, + "contentName": "markup.underline.raku", + "end": "(?x) (>)", + "endCaptures": { + "1": { + "name": "punctuation.section.embedded.pod.code.raku" + } + }, + "name": "meta.pod.c.raku", + "patterns": [ + { + "include": "#comment-block-syntax" + }, + { + "include": "#q_angle_string_content" + } + ] + }, + { + "begin": "(?x) (I) (<)", + "beginCaptures": { + "1": { + "name": "support.function.pod.code.raku" + }, + "2": { + "name": "punctuation.section.embedded.pod.code.raku" + } + }, + "contentName": "markup.italic.raku", + "end": "(?x) (>)", + "endCaptures": { + "1": { + "name": "punctuation.section.embedded.pod.code.raku" + } + }, + "name": "meta.pod.c.raku", + "patterns": [ + { + "include": "#comment-block-syntax" + }, + { + "include": "#q_angle_string_content" + } + ] + }, + { + "begin": "(?x) (B) (<)", + "beginCaptures": { + "1": { + "name": "support.function.pod.code.raku" + }, + "2": { + "name": "punctuation.section.embedded.pod.code.raku" + } + }, + "contentName": "markup.bold.raku", + "end": "(?x) (>)", + "endCaptures": { + "1": { + "name": "punctuation.section.embedded.pod.code.raku" + } + }, + "name": "meta.pod.c.raku", + "patterns": [ + { + "include": "#comment-block-syntax" + }, + { + "include": "#q_angle_string_content" + } + ] + }, + { + "begin": "(?x) ([A-Z]) (<)", + "beginCaptures": { + "1": { + "name": "support.function.pod.code.raku" + }, + "2": { + "name": "punctuation.section.embedded.pod.code.raku" + } + }, + "contentName": "markup.raw.code.raku", + "end": "(?x) (>)", + "endCaptures": { + "1": { + "name": "punctuation.section.embedded.pod.code.raku" + } + }, + "name": "meta.pod.c.raku", + "patterns": [ + { + "include": "#comment-block-syntax" + }, + { + "include": "#q_angle_string_content" + } + ] + }, + { + "begin": "(?x) (U) («)", + "beginCaptures": { + "1": { + "name": "support.function.pod.code.raku" + }, + "2": { + "name": "punctuation.section.embedded.pod.code.raku" + } + }, + "contentName": "markup.underline.raku", + "end": "(?x) (»)", + "endCaptures": { + "1": { + "name": "punctuation.section.embedded.pod.code.raku" + } + }, + "name": "meta.pod.c.raku", + "patterns": [ + { + "include": "#comment-block-syntax" + }, + { + "include": "#q_chevron_string_content" + } + ] + }, + { + "begin": "(?x) (I) («)", + "beginCaptures": { + "1": { + "name": "support.function.pod.code.raku" + }, + "2": { + "name": "punctuation.section.embedded.pod.code.raku" + } + }, + "contentName": "markup.italic.raku", + "end": "(?x) (»)", + "endCaptures": { + "1": { + "name": "punctuation.section.embedded.pod.code.raku" + } + }, + "name": "meta.pod.c.raku", + "patterns": [ + { + "include": "#comment-block-syntax" + }, + { + "include": "#q_chevron_string_content" + } + ] + }, + { + "begin": "(?x) (B) («)", + "beginCaptures": { + "1": { + "name": "support.function.pod.code.raku" + }, + "2": { + "name": "punctuation.section.embedded.pod.code.raku" + } + }, + "contentName": "markup.bold.raku", + "end": "(?x) (»)", + "endCaptures": { + "1": { + "name": "punctuation.section.embedded.pod.code.raku" + } + }, + "name": "meta.pod.c.raku", + "patterns": [ + { + "include": "#comment-block-syntax" + }, + { + "include": "#q_chevron_string_content" + } + ] + }, + { + "begin": "(?x) ([A-Z]) («)", + "beginCaptures": { + "1": { + "name": "support.function.pod.code.raku" + }, + "2": { + "name": "punctuation.section.embedded.pod.code.raku" + } + }, + "contentName": "markup.raw.code.raku", + "end": "(?x) (»)", + "endCaptures": { + "1": { + "name": "punctuation.section.embedded.pod.code.raku" + } + }, + "name": "meta.pod.c.raku", + "patterns": [ + { + "include": "#comment-block-syntax" + }, + { + "include": "#q_chevron_string_content" + } + ] + } + ] + }, + "hex_escapes": { + "patterns": [ + { + "captures": { + "1": { + "name": "keyword.punctuation.hex.raku" + }, + "2": { + "name": "keyword.operator.bracket.open.raku" + }, + "3": { + "name": "routine.name.hex.raku" + }, + "4": { + "name": "keyword.operator.bracket.close.raku" + } + }, + "match": "(?x) (\\\\x) ( \\[ ) ( [\\dA-Fa-f]+ ) ( \\] )", + "name": "punctuation.hex.raku" + } + ] + }, + "interpolation": { + "patterns": [ + { + "captures": { + "1": { + "name": "variable.other.identifier.sigil.raku" + }, + "2": { + "name": "support.class.twigil.interpolated.raku" + }, + "5": { + "patterns": [ + { + "begin": "<", + "beginCaptures": { + "0": { + "name": "keyword.operator.chevron.open.raku" + } + }, + "end": ">", + "endCaptures": { + "0": { + "name": "keyword.operator.chevron.close.raku" + } + } + }, + { + "begin": "\\[", + "beginCaptures": { + "0": { + "name": "keyword.operator.bracket.open.raku" + } + }, + "end": "\\]", + "endCaptures": { + "0": { + "name": "keyword.operator.bracket.close.raku" + } + }, + "patterns": [ + { + "include": "$self" + } + ] + } + ] + }, + "6": { + "name": "keyword.operator.dot.raku" + }, + "7": { + "name": "support.function.raku" + }, + "8": { + "begin": "\\(", + "beginCaptures": { + "0": { + "name": "keyword.operator.paren.open.raku" + } + }, + "end": "\\)", + "endCaptures": { + "0": { + "name": "keyword.operator.paren.close.raku" + } + }, + "patterns": [ + { + "include": "$self" + } + ] + } + }, + "match": "(?x)\n(?|'\\w*')", + "name": "constant.character.escape.perl" + }, + { + "match": "\\\\N\\{[^\\}]*\\}", + "name": "constant.character.escape.perl" + }, + { + "match": "\\\\o\\{\\d*\\}", + "name": "constant.character.escape.perl" + }, + { + "match": "\\\\(?:p|P)(?:\\{\\w*\\}|P)", + "name": "constant.character.escape.perl" + }, + { + "match": "\\\\x(?:[0-9a-zA-Z]{2}|\\{\\w*\\})?", + "name": "constant.character.escape.perl" + }, + { + "match": "\\\\.", + "name": "constant.character.escape.perl" + } + ] + }, + "p5_regex": { + "patterns": [ + { + "begin": "(?x)(?])", + "name": "meta.match.variable.raku", + "patterns": [ + { + "captures": { + "1": { + "name": "support.class.match.name.delimiter.regexp.raku" + }, + "2": { + "name": "variable.other.identifier.regexp.perl6" + }, + "3": { + "name": "support.class.match.name.delimiter.regexp.raku" + } + }, + "match": "(\\<)([\\w\\-]+)(\\>)" + } + ] + } + ] + }, + "shellquotes": { + "patterns": [ + { + "begin": "([qQ]x)\\s*({{)", + "beginCaptures": { + "1": { + "name": "string.quoted.q.shell.operator.raku" + }, + "2": { + "name": "punctuation.section.embedded.shell.begin.raku" + } + }, + "end": "}}", + "endCaptures": { + "0": { + "name": "punctuation.section.embedded.shell.begin.raku" + } + }, + "name": "meta.shell.quote.single.raku", + "patterns": [ + { + "include": "#q_single_string_content" + } + ] + }, + { + "begin": "([qQ]x)\\s*({)", + "beginCaptures": { + "1": { + "name": "string.quoted.q.shell.operator.raku" + }, + "2": { + "name": "punctuation.section.embedded.shell.begin.raku" + } + }, + "end": "}", + "endCaptures": { + "0": { + "name": "punctuation.section.embedded.shell.begin.raku" + } + }, + "name": "meta.shell.quote.single.raku", + "patterns": [ + { + "include": "#q_single_string_content" + } + ] + }, + { + "begin": "([qQ]x)\\s*(\\[\\[)", + "beginCaptures": { + "1": { + "name": "string.quoted.q.shell.operator.raku" + }, + "2": { + "name": "punctuation.section.embedded.shell.begin.raku" + } + }, + "end": "\\]\\]", + "endCaptures": { + "0": { + "name": "punctuation.section.embedded.shell.begin.raku" + } + }, + "name": "meta.shell.quote.single.raku", + "patterns": [ + { + "include": "#q_single_string_content" + } + ] + }, + { + "begin": "([Qq]x)\\s*(\\[)", + "beginCaptures": { + "1": { + "name": "string.quoted.q.shell.operator.raku" + }, + "2": { + "name": "punctuation.section.embedded.shell.begin.raku" + } + }, + "end": "\\]", + "endCaptures": { + "0": { + "name": "punctuation.section.embedded.shell.begin.raku" + } + }, + "name": "meta.shell.quote.single.raku", + "patterns": [ + { + "include": "#q_single_string_content" + } + ] + }, + { + "begin": "([Qq]x)\\s*(\\|)", + "beginCaptures": { + "1": { + "name": "string.quoted.q.shell.operator.raku" + }, + "2": { + "name": "punctuation.section.embedded.shell.begin.raku" + } + }, + "end": "\\|", + "endCaptures": { + "0": { + "name": "punctuation.section.embedded.shell.begin.raku" + } + }, + "name": "meta.shell.quote.single.raku", + "patterns": [ + { + "include": "#q_single_string_content" + } + ] + }, + { + "begin": "([Qq]x)\\s*(\\/)", + "beginCaptures": { + "1": { + "name": "string.quoted.q.shell.operator.raku" + }, + "2": { + "name": "punctuation.section.embedded.shell.begin.raku" + } + }, + "end": "(?>", + "name": "entity.name.section.boundary.regexp.raku" + }, + { + "match": "(?)\\s*(=)", + "name": "meta.match.variable.raku" + }, + { + "begin": "(\\<(?:\\?|\\!)\\{)", + "beginCaptures": { + "1": { + "name": "punctuation.section.embedded.begin.raku" + } + }, + "end": "(\\}\\>)", + "endCaptures": { + "1": { + "name": "punctuation.section.embedded.end.raku" + } + }, + "name": "meta.interpolation.raku", + "patterns": [ + { + "include": "source.raku" + } + ] + }, + { + "match": "<\\(|\\)>", + "name": "keyword.operator.capture.marker.regexp.raku" + }, + { + "begin": "(?!\\\\)<", + "beginCaptures": { + "0": { + "name": "punctuation.delimiter.property.regexp.raku" + } + }, + "end": ">", + "endCaptures": { + "0": { + "name": "punctuation.delimiter.property.regexp.raku" + } + }, + "name": "meta.property.regexp.raku", + "patterns": [ + { + "include": "#re_strings" + }, + { + "begin": "(\\?|\\!)(before|after)\\s+", + "beginCaptures": { + "1": { + "name": "keyword.operator.negativity.raku" + }, + "2": { + "name": "entity.name.section.assertion.raku" + } + }, + "end": "(?=>)", + "name": "meta.assertion.lookaround.raku", + "patterns": [ + { + "include": "#regexp" + } + ] + }, + { + "captures": { + "1": { + "name": "entity.name.function.capturename.raku" + }, + "2": { + "name": "storage.modifier.capture.assignment.raku" + } + }, + "match": "(\\w+)(=)", + "name": "meta.capture.assignment.raku" + }, + { + "captures": { + "1": { + "name": "punctuation.definition.property.regexp.raku" + }, + "2": { + "name": "variable.other.identifier.property.regexp.raku" + } + }, + "match": "(:)(\\w+)", + "name": "meta.property.name.regexp.raku" + }, + { + "match": "[+|&\\-^]", + "name": "keyword.operator.property.regexp.raku" + }, + { + "begin": "\\[", + "beginCaptures": { + "0": { + "name": "keyword.operator.charclass.open.regexp.raku" + } + }, + "contentName": "constant.character.custom.property.regexp.raku", + "end": "\\]", + "endCaptures": { + "0": { + "name": "keyword.operator.charclass.close.regexp.raku" + } + }, + "patterns": [ + { + "include": "source.raku#hex_escapes" + }, + { + "match": "(?|(?", + "patterns": [ + { + "include": "#q_angle_string_content" + } + ] + }, + "q_brace_string_content": { + "begin": "{", + "end": "\\\\\\\\}|(?>|(?>", + "patterns": [ + { + "include": "#q_double_angle_string_content" + } + ] + }, + "q_double_brace_string_content": { + "begin": "{{", + "end": "\\\\\\\\}}|(?>>|(?>>", + "patterns": [ + { + "include": "#q_triple_angle_string_content" + } + ] + }, + "q_triple_brace_string_content": { + "begin": "\\{\\{\\{", + "end": "\\\\\\\\\\}\\}\\}|(?>>", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.raku" + } + }, + "patterns": [ + { + "include": "#q_triple_angle_string_content" + } + ] + }, + { + "begin": "(?x) (?<=^|[\\[\\]\\s\\(\\){},;]) (q(?:x|w|ww|v|s|a|h|f|c|b|p)?) ((?: \\s*:(?: x|exec|w|words|ww|quotewords|v|val|q|single|double| s|scalar|a|array|h|hash|f|function|c|closure|b|blackslash| regexp|substr|trans|codes|p|path|nfkc|nfkd ) )*) \\s*(<<<)", + "beginCaptures": { + "1": { + "name": "string.quoted.q.operator.raku" + }, + "2": { + "name": "support.function.quote.adverb.raku" + }, + "3": { + "name": "punctuation.definition.string.raku" + } + }, + "contentName": "string.quoted.q.triple_angle.quote.raku", + "end": "\\\\\\\\>>>|(?>>", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.raku" + } + }, + "patterns": [ + { + "match": "\\\\<<<|\\\\>>>", + "name": "constant.character.escape.raku" + }, + { + "include": "#q_triple_angle_string_content" + } + ] + }, + { + "begin": "(?x) (?<=^|[\\[\\]\\s\\(\\){},;]) (qq(?:x|w|ww|v|s|a|h|f|c|b|p)?) ((?: \\s*:(?: x|exec|w|words|ww|quotewords|v|val|q|single|double| s|scalar|a|array|h|hash|f|function|c|closure|b|blackslash| regexp|substr|trans|codes|p|path|nfkc|nfkd ) )*) \\s*(<<<)", + "beginCaptures": { + "1": { + "name": "string.quoted.qq.operator.raku" + }, + "2": { + "name": "support.function.quote.adverb.raku" + }, + "3": { + "name": "punctuation.definition.string.raku" + } + }, + "contentName": "string.quoted.qq.triple_angle.quote.raku", + "end": "\\\\\\\\>>>|(?>>", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.raku" + } + }, + "patterns": [ + { + "match": "\\\\<<<|\\\\>>>", + "name": "constant.character.escape.raku" + }, + { + "include": "#qq_character_escape" + }, + { + "include": "source.raku#interpolation" + }, + { + "include": "#q_triple_angle_string_content" + } + ] + }, + { + "begin": "(?x) (?<=^|[\\[\\]\\s\\(\\){},;]) (Q(?:x|w|ww|v|s|a|h|f|c|b|p)?) ((?: \\s*:(?: x|exec|w|words|ww|quotewords|v|val|q|single|double| s|scalar|a|array|h|hash|f|function|c|closure|b|blackslash| regexp|substr|trans|codes|p|path|nfkc|nfkd ) )*) \\s*(<<)", + "beginCaptures": { + "1": { + "name": "string.quoted.q.operator.raku" + }, + "2": { + "name": "support.function.quote.adverb.raku" + }, + "3": { + "name": "punctuation.definition.string.raku" + } + }, + "contentName": "string.quoted.q.double_angle.quote.raku", + "end": ">>", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.raku" + } + }, + "patterns": [ + { + "include": "#q_double_angle_string_content" + } + ] + }, + { + "begin": "(?x) (?<=^|[\\[\\]\\s\\(\\){},;]) (q(?:x|w|ww|v|s|a|h|f|c|b|p)?) ((?: \\s*:(?: x|exec|w|words|ww|quotewords|v|val|q|single|double| s|scalar|a|array|h|hash|f|function|c|closure|b|blackslash| regexp|substr|trans|codes|p|path|nfkc|nfkd ) )*) \\s*(<<)", + "beginCaptures": { + "1": { + "name": "string.quoted.q.operator.raku" + }, + "2": { + "name": "support.function.quote.adverb.raku" + }, + "3": { + "name": "punctuation.definition.string.raku" + } + }, + "contentName": "string.quoted.q.double_angle.quote.raku", + "end": "\\\\\\\\>>|(?>", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.raku" + } + }, + "patterns": [ + { + "match": "\\\\<<|\\\\>>", + "name": "constant.character.escape.raku" + }, + { + "include": "#q_double_angle_string_content" + } + ] + }, + { + "begin": "(?x) (?<=^|[\\[\\]\\s\\(\\){},;]) (qq(?:x|w|ww|v|s|a|h|f|c|b|p)?) ((?: \\s*:(?: x|exec|w|words|ww|quotewords|v|val|q|single|double| s|scalar|a|array|h|hash|f|function|c|closure|b|blackslash| regexp|substr|trans|codes|p|path|nfkc|nfkd ) )*) \\s*(<<)", + "beginCaptures": { + "1": { + "name": "string.quoted.qq.operator.raku" + }, + "2": { + "name": "support.function.quote.adverb.raku" + }, + "3": { + "name": "punctuation.definition.string.raku" + } + }, + "contentName": "string.quoted.qq.double_angle.quote.raku", + "end": "\\\\\\\\>>|(?>", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.raku" + } + }, + "patterns": [ + { + "match": "\\\\<<|\\\\>>", + "name": "constant.character.escape.raku" + }, + { + "include": "#qq_character_escape" + }, + { + "include": "source.raku#interpolation" + }, + { + "include": "#q_double_angle_string_content" + } + ] + }, + { + "begin": "(?x) (?<=^|[\\[\\]\\s\\(\\){},;]) (Q(?:x|w|ww|v|s|a|h|f|c|b|p)?) ((?: \\s*:(?: x|exec|w|words|ww|quotewords|v|val|q|single|double| s|scalar|a|array|h|hash|f|function|c|closure|b|blackslash| regexp|substr|trans|codes|p|path|nfkc|nfkd ) )*) \\s*(\\(\\()", + "beginCaptures": { + "1": { + "name": "string.quoted.q.operator.raku" + }, + "2": { + "name": "support.function.quote.adverb.raku" + }, + "3": { + "name": "punctuation.definition.string.raku" + } + }, + "contentName": "string.quoted.q.double_paren.quote.raku", + "end": "\\)\\)", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.raku" + } + }, + "patterns": [ + { + "include": "#q_double_paren_string_content" + } + ] + }, + { + "begin": "(?x) (?<=^|[\\[\\]\\s\\(\\){},;]) (q(?:x|w|ww|v|s|a|h|f|c|b|p)?) ((?: \\s*:(?: x|exec|w|words|ww|quotewords|v|val|q|single|double| s|scalar|a|array|h|hash|f|function|c|closure|b|blackslash| regexp|substr|trans|codes|p|path|nfkc|nfkd ) )*) \\s*(\\(\\()", + "beginCaptures": { + "1": { + "name": "string.quoted.q.operator.raku" + }, + "2": { + "name": "support.function.quote.adverb.raku" + }, + "3": { + "name": "punctuation.definition.string.raku" + } + }, + "contentName": "string.quoted.q.double_paren.quote.raku", + "end": "\\\\\\\\\\)\\)|(?", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.raku" + } + }, + "patterns": [ + { + "include": "#q_angle_string_content" + } + ] + }, + { + "begin": "(?x) (?<=^|[\\[\\]\\s\\(\\){},;]) (q(?:x|w|ww|v|s|a|h|f|c|b|p)?) ((?: \\s*:(?: x|exec|w|words|ww|quotewords|v|val|q|single|double| s|scalar|a|array|h|hash|f|function|c|closure|b|blackslash| regexp|substr|trans|codes|p|path|nfkc|nfkd ) )*) \\s*(<)", + "beginCaptures": { + "1": { + "name": "string.quoted.q.operator.raku" + }, + "2": { + "name": "support.function.quote.adverb.raku" + }, + "3": { + "name": "punctuation.definition.string.raku" + } + }, + "contentName": "string.quoted.q.angle.quote.raku", + "end": "\\\\\\\\>|(?", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.raku" + } + }, + "patterns": [ + { + "match": "\\\\<|\\\\>", + "name": "constant.character.escape.raku" + }, + { + "include": "#q_angle_string_content" + } + ] + }, + { + "begin": "(?x) (?<=^|[\\[\\]\\s\\(\\){},;]) (qq(?:x|w|ww|v|s|a|h|f|c|b|p)?) ((?: \\s*:(?: x|exec|w|words|ww|quotewords|v|val|q|single|double| s|scalar|a|array|h|hash|f|function|c|closure|b|blackslash| regexp|substr|trans|codes|p|path|nfkc|nfkd ) )*) \\s*(<)", + "beginCaptures": { + "1": { + "name": "string.quoted.qq.operator.raku" + }, + "2": { + "name": "support.function.quote.adverb.raku" + }, + "3": { + "name": "punctuation.definition.string.raku" + } + }, + "contentName": "string.quoted.qq.angle.quote.raku", + "end": "\\\\\\\\>|(?", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.raku" + } + }, + "patterns": [ + { + "match": "\\\\<|\\\\>", + "name": "constant.character.escape.raku" + }, + { + "include": "#qq_character_escape" + }, + { + "include": "source.raku#interpolation" + }, + { + "include": "#q_angle_string_content" + } + ] + }, + { + "begin": "(?x) (?<=^|[\\[\\]\\s\\(\\){},;]) (Q(?:x|w|ww|v|s|a|h|f|c|b|p)?) ((?: \\s*:(?: x|exec|w|words|ww|quotewords|v|val|q|single|double| s|scalar|a|array|h|hash|f|function|c|closure|b|blackslash| regexp|substr|trans|codes|p|path|nfkc|nfkd ) )*) \\s+(\\()", + "beginCaptures": { + "1": { + "name": "string.quoted.q.operator.raku" + }, + "2": { + "name": "support.function.quote.adverb.raku" + }, + "3": { + "name": "punctuation.definition.string.raku" + } + }, + "contentName": "string.quoted.q.paren.quote.raku", + "end": "\\)", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.raku" + } + }, + "patterns": [ + { + "include": "#q_paren_string_content" + } + ] + }, + { + "begin": "(?x) (?<=^|[\\[\\]\\s\\(\\){},;]) (q(?:x|w|ww|v|s|a|h|f|c|b|p)?) ((?: \\s*:(?: x|exec|w|words|ww|quotewords|v|val|q|single|double| s|scalar|a|array|h|hash|f|function|c|closure|b|blackslash| regexp|substr|trans|codes|p|path|nfkc|nfkd ) )*) \\s+(\\()", + "beginCaptures": { + "1": { + "name": "string.quoted.q.operator.raku" + }, + "2": { + "name": "support.function.quote.adverb.raku" + }, + "3": { + "name": "punctuation.definition.string.raku" + } + }, + "contentName": "string.quoted.q.paren.quote.raku", + "end": "\\\\\\\\\\)|(?>>", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.raku" + } + }, + "patterns": [ + { + "include": "#q_triple_angle_string_content" + } + ] + }, + { + "begin": "(?x) (?<=^|[\\[\\]\\s\\(\\){},;]) (q(?:x|w|ww|v|s|a|h|f|c|b|p)?) ((?: \\s*:(?: x|exec|w|words|ww|quotewords|v|val|q|single|double| s|scalar|a|array|h|hash|f|function|c|closure|b|blackslash| regexp|substr|trans|codes|p|path|nfkc|nfkd ) )*) \\s*(<<<)", + "beginCaptures": { + "1": { + "name": "string.quoted.q.operator.raku" + }, + "2": { + "name": "support.function.quote.adverb.raku" + }, + "3": { + "name": "punctuation.definition.string.raku" + } + }, + "contentName": "string.quoted.q.triple_angle.quote.raku", + "end": "\\\\\\\\>>>|(?>>", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.raku" + } + }, + "patterns": [ + { + "match": "\\\\<<<|\\\\>>>", + "name": "constant.character.escape.raku" + }, + { + "include": "#q_triple_angle_string_content" + } + ] + }, + { + "begin": "(?x) (?<=^|[\\[\\]\\s\\(\\){},;]) (qq(?:x|w|ww|v|s|a|h|f|c|b|p)?) ((?: \\s*:(?: x|exec|w|words|ww|quotewords|v|val|q|single|double| s|scalar|a|array|h|hash|f|function|c|closure|b|blackslash| regexp|substr|trans|codes|p|path|nfkc|nfkd ) )*) \\s*(<<<)", + "beginCaptures": { + "1": { + "name": "string.quoted.qq.operator.raku" + }, + "2": { + "name": "support.function.quote.adverb.raku" + }, + "3": { + "name": "punctuation.definition.string.raku" + } + }, + "contentName": "string.quoted.qq.triple_angle.quote.raku", + "end": "\\\\\\\\>>>|(?>>", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.raku" + } + }, + "patterns": [ + { + "match": "\\\\<<<|\\\\>>>", + "name": "constant.character.escape.raku" + }, + { + "include": "#qq_character_escape" + }, + { + "include": "source.raku#interpolation" + }, + { + "include": "#q_triple_angle_string_content" + } + ] + }, + { + "begin": "(?x) (?<=^|[\\[\\]\\s\\(\\){},;]) (Q(?:x|w|ww|v|s|a|h|f|c|b|p)?) ((?: \\s*:(?: x|exec|w|words|ww|quotewords|v|val|q|single|double| s|scalar|a|array|h|hash|f|function|c|closure|b|blackslash| regexp|substr|trans|codes|p|path|nfkc|nfkd ) )*) \\s*(<<)", + "beginCaptures": { + "1": { + "name": "string.quoted.q.operator.raku" + }, + "2": { + "name": "support.function.quote.adverb.raku" + }, + "3": { + "name": "punctuation.definition.string.raku" + } + }, + "contentName": "string.quoted.q.double_angle.quote.raku", + "end": ">>", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.raku" + } + }, + "patterns": [ + { + "include": "#q_double_angle_string_content" + } + ] + }, + { + "begin": "(?x) (?<=^|[\\[\\]\\s\\(\\){},;]) (q(?:x|w|ww|v|s|a|h|f|c|b|p)?) ((?: \\s*:(?: x|exec|w|words|ww|quotewords|v|val|q|single|double| s|scalar|a|array|h|hash|f|function|c|closure|b|blackslash| regexp|substr|trans|codes|p|path|nfkc|nfkd ) )*) \\s*(<<)", + "beginCaptures": { + "1": { + "name": "string.quoted.q.operator.raku" + }, + "2": { + "name": "support.function.quote.adverb.raku" + }, + "3": { + "name": "punctuation.definition.string.raku" + } + }, + "contentName": "string.quoted.q.double_angle.quote.raku", + "end": "\\\\\\\\>>|(?>", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.raku" + } + }, + "patterns": [ + { + "match": "\\\\<<|\\\\>>", + "name": "constant.character.escape.raku" + }, + { + "include": "#q_double_angle_string_content" + } + ] + }, + { + "begin": "(?x) (?<=^|[\\[\\]\\s\\(\\){},;]) (qq(?:x|w|ww|v|s|a|h|f|c|b|p)?) ((?: \\s*:(?: x|exec|w|words|ww|quotewords|v|val|q|single|double| s|scalar|a|array|h|hash|f|function|c|closure|b|blackslash| regexp|substr|trans|codes|p|path|nfkc|nfkd ) )*) \\s*(<<)", + "beginCaptures": { + "1": { + "name": "string.quoted.qq.operator.raku" + }, + "2": { + "name": "support.function.quote.adverb.raku" + }, + "3": { + "name": "punctuation.definition.string.raku" + } + }, + "contentName": "string.quoted.qq.double_angle.quote.raku", + "end": "\\\\\\\\>>|(?>", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.raku" + } + }, + "patterns": [ + { + "match": "\\\\<<|\\\\>>", + "name": "constant.character.escape.raku" + }, + { + "include": "#qq_character_escape" + }, + { + "include": "source.raku#interpolation" + }, + { + "include": "#q_double_angle_string_content" + } + ] + }, + { + "begin": "(?x) (?<=^|[\\[\\]\\s\\(\\){},;]) (Q(?:x|w|ww|v|s|a|h|f|c|b|p)?) ((?: \\s*:(?: x|exec|w|words|ww|quotewords|v|val|q|single|double| s|scalar|a|array|h|hash|f|function|c|closure|b|blackslash| regexp|substr|trans|codes|p|path|nfkc|nfkd ) )*) \\s*(\\(\\()", + "beginCaptures": { + "1": { + "name": "string.quoted.q.operator.raku" + }, + "2": { + "name": "support.function.quote.adverb.raku" + }, + "3": { + "name": "punctuation.definition.string.raku" + } + }, + "contentName": "string.quoted.q.double_paren.quote.raku", + "end": "\\)\\)", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.raku" + } + }, + "patterns": [ + { + "include": "#q_double_paren_string_content" + } + ] + }, + { + "begin": "(?x) (?<=^|[\\[\\]\\s\\(\\){},;]) (q(?:x|w|ww|v|s|a|h|f|c|b|p)?) ((?: \\s*:(?: x|exec|w|words|ww|quotewords|v|val|q|single|double| s|scalar|a|array|h|hash|f|function|c|closure|b|blackslash| regexp|substr|trans|codes|p|path|nfkc|nfkd ) )*) \\s*(\\(\\()", + "beginCaptures": { + "1": { + "name": "string.quoted.q.operator.raku" + }, + "2": { + "name": "support.function.quote.adverb.raku" + }, + "3": { + "name": "punctuation.definition.string.raku" + } + }, + "contentName": "string.quoted.q.double_paren.quote.raku", + "end": "\\\\\\\\\\)\\)|(?", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.raku" + } + }, + "patterns": [ + { + "include": "#q_angle_string_content" + } + ] + }, + { + "begin": "(?x) (?<=^|[\\[\\]\\s\\(\\){},;]) (q(?:x|w|ww|v|s|a|h|f|c|b|p)?) ((?: \\s*:(?: x|exec|w|words|ww|quotewords|v|val|q|single|double| s|scalar|a|array|h|hash|f|function|c|closure|b|blackslash| regexp|substr|trans|codes|p|path|nfkc|nfkd ) )*) \\s*(<)", + "beginCaptures": { + "1": { + "name": "string.quoted.q.operator.raku" + }, + "2": { + "name": "support.function.quote.adverb.raku" + }, + "3": { + "name": "punctuation.definition.string.raku" + } + }, + "contentName": "string.quoted.q.angle.quote.raku", + "end": "\\\\\\\\>|(?", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.raku" + } + }, + "patterns": [ + { + "match": "\\\\<|\\\\>", + "name": "constant.character.escape.raku" + }, + { + "include": "#q_angle_string_content" + } + ] + }, + { + "begin": "(?x) (?<=^|[\\[\\]\\s\\(\\){},;]) (qq(?:x|w|ww|v|s|a|h|f|c|b|p)?) ((?: \\s*:(?: x|exec|w|words|ww|quotewords|v|val|q|single|double| s|scalar|a|array|h|hash|f|function|c|closure|b|blackslash| regexp|substr|trans|codes|p|path|nfkc|nfkd ) )*) \\s*(<)", + "beginCaptures": { + "1": { + "name": "string.quoted.qq.operator.raku" + }, + "2": { + "name": "support.function.quote.adverb.raku" + }, + "3": { + "name": "punctuation.definition.string.raku" + } + }, + "contentName": "string.quoted.qq.angle.quote.raku", + "end": "\\\\\\\\>|(?", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.raku" + } + }, + "patterns": [ + { + "match": "\\\\<|\\\\>", + "name": "constant.character.escape.raku" + }, + { + "include": "#qq_character_escape" + }, + { + "include": "source.raku#interpolation" + }, + { + "include": "#q_angle_string_content" + } + ] + }, + { + "begin": "(?x) (?<=^|[\\[\\]\\s\\(\\){},;]) (Q(?:x|w|ww|v|s|a|h|f|c|b|p)?) ((?: \\s*:(?: x|exec|w|words|ww|quotewords|v|val|q|single|double| s|scalar|a|array|h|hash|f|function|c|closure|b|blackslash| regexp|substr|trans|codes|p|path|nfkc|nfkd ) )*) \\s+(\\()", + "beginCaptures": { + "1": { + "name": "string.quoted.q.operator.raku" + }, + "2": { + "name": "support.function.quote.adverb.raku" + }, + "3": { + "name": "punctuation.definition.string.raku" + } + }, + "contentName": "string.quoted.q.paren.quote.raku", + "end": "\\)", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.raku" + } + }, + "patterns": [ + { + "include": "#q_paren_string_content" + } + ] + }, + { + "begin": "(?x) (?<=^|[\\[\\]\\s\\(\\){},;]) (q(?:x|w|ww|v|s|a|h|f|c|b|p)?) ((?: \\s*:(?: x|exec|w|words|ww|quotewords|v|val|q|single|double| s|scalar|a|array|h|hash|f|function|c|closure|b|blackslash| regexp|substr|trans|codes|p|path|nfkc|nfkd ) )*) \\s+(\\()", + "beginCaptures": { + "1": { + "name": "string.quoted.q.operator.raku" + }, + "2": { + "name": "support.function.quote.adverb.raku" + }, + "3": { + "name": "punctuation.definition.string.raku" + } + }, + "contentName": "string.quoted.q.paren.quote.raku", + "end": "\\\\\\\\\\)|(?|(?", + "patterns": [ + { + "include": "#q_angle_string_content" + } + ] + }, + "q_brace_string_content": { + "begin": "{", + "end": "\\\\\\\\}|(?>|(?>", + "patterns": [ + { + "include": "#q_double_angle_string_content" + } + ] + }, + "q_double_brace_string_content": { + "begin": "{{", + "end": "\\\\\\\\}}|(?>>|(?>>", + "patterns": [ + { + "include": "#q_triple_angle_string_content" + } + ] + }, + "q_triple_brace_string_content": { + "begin": "\\{\\{\\{", + "end": "\\\\\\\\\\}\\}\\}|(?>", + "name": "entity.name.section.boundary.regexp.raku" + }, + { + "match": "(?)\\s*(=)", + "name": "meta.match.variable.raku" + }, + { + "begin": "(\\<(?:\\?|\\!)\\{)", + "beginCaptures": { + "1": { + "name": "punctuation.section.embedded.begin.raku" + } + }, + "end": "(\\}\\>)", + "endCaptures": { + "1": { + "name": "punctuation.section.embedded.end.raku" + } + }, + "name": "meta.interpolation.raku", + "patterns": [ + { + "include": "source.raku" + } + ] + }, + { + "match": "<\\(|\\)>", + "name": "keyword.operator.capture.marker.regexp.raku" + }, + { + "begin": "(?!\\\\)<", + "beginCaptures": { + "0": { + "name": "punctuation.delimiter.property.regexp.raku" + } + }, + "end": ">", + "endCaptures": { + "0": { + "name": "punctuation.delimiter.property.regexp.raku" + } + }, + "name": "meta.property.regexp.raku", + "patterns": [ + { + "include": "#re_strings" + }, + { + "begin": "(\\?|\\!)(before|after)\\s+", + "beginCaptures": { + "1": { + "name": "keyword.operator.negativity.raku" + }, + "2": { + "name": "entity.name.section.assertion.raku" + } + }, + "end": "(?=>)", + "name": "meta.assertion.lookaround.raku", + "patterns": [ + { + "include": "#regexp" + } + ] + }, + { + "captures": { + "1": { + "name": "entity.name.function.capturename.raku" + }, + "2": { + "name": "storage.modifier.capture.assignment.raku" + } + }, + "match": "(\\w+)(=)", + "name": "meta.capture.assignment.raku" + }, + { + "captures": { + "1": { + "name": "punctuation.definition.property.regexp.raku" + }, + "2": { + "name": "variable.other.identifier.property.regexp.raku" + } + }, + "match": "(:)(\\w+)", + "name": "meta.property.name.regexp.raku" + }, + { + "match": "[+|&\\-^]", + "name": "keyword.operator.property.regexp.raku" + }, + { + "begin": "\\[", + "beginCaptures": { + "0": { + "name": "keyword.operator.charclass.open.regexp.raku" + } + }, + "contentName": "constant.character.custom.property.regexp.raku", + "end": "\\]", + "endCaptures": { + "0": { + "name": "keyword.operator.charclass.close.regexp.raku" + } + }, + "patterns": [ + { + "include": "source.raku#hex_escapes" + }, + { + "match": "(? { const registry = new vsctm.Registry({ onigLib: vscodeOnigurumaLib, loadGrammar: async (scopeName) => { - const grammarpath = path.join(__dirname, "./../raku.tmLanguage.json"); + const grammarpath = path.join(__dirname, "./../raku.merged.tmLanguage.json"); const grammar = await fs.promises.readFile(grammarpath, "utf8"); return vsctm.parseRawGrammar(grammar, grammarpath); }, @@ -68,11 +68,11 @@ function analyzeIndentChange(tokens: vsctm.IToken[], lineText: string): { before const token = tokens[i]; const startIndex = i === 0 ? 0 : tokens[i - 1].endIndex; const content = lineText.substring(startIndex, token.endIndex); - + // Check if this is a string or comment - ignore brackets inside them const isString = token.scopes.some(scope => scope.startsWith("string")); const isComment = token.scopes.some(scope => scope.startsWith("comment")); - + if (isString || isComment) { continue; } @@ -115,21 +115,21 @@ async function analyzeLine( grammar: vsctm.IGrammar, ruleStack: vsctm.StateStack | null ): Promise<{ lineInfo: LineInfo; ruleStack: vsctm.StateStack }> { - + const result = grammar.tokenizeLine(lineText, ruleStack); - + // Detect leading whitespace const leadingMatch = lineText.match(/^(\s*)/); const leadingWhitespace = leadingMatch ? leadingMatch[1] : ""; const contentStart = leadingWhitespace.length; const isEmpty = lineText.trim().length === 0; - + // Check if this entire line is a comment or string let isComment = false; let isString = false; let preserveLeadingWhitespace = false; let isPod = false; - + if (result.tokens.length > 0) { // Check if the first non-whitespace token is a comment const firstContentToken = result.tokens.find((token, idx) => { @@ -137,7 +137,7 @@ async function analyzeLine( const content = lineText.substring(startIdx, token.endIndex); return content.trim().length > 0; }); - + if (firstContentToken) { isComment = firstContentToken.scopes.some(scope => scope.startsWith("comment")); isPod = firstContentToken.scopes.some(scope => scope.includes("pod") || scope.includes("comment.block.documentation")); @@ -154,12 +154,12 @@ async function analyzeLine( const startsInsideHeredoc = firstToken.scopes.some(scope => scope.includes("meta.heredoc")); preserveLeadingWhitespace = startsInsideString || startsInsideHeredoc; } - + // Analyze indent changes based on brackets. // String continuation lines still need bracket tracking so indent state // stays correct for the code that follows them. const indentDelta = isEmpty || isComment ? { before: 0, after: 0 } : analyzeIndentChange(result.tokens, lineText); - + const lineInfo: LineInfo = { lineNumber, text: lineText, @@ -173,7 +173,7 @@ async function analyzeLine( indentDelta, tokens: result.tokens, }; - + return { lineInfo, ruleStack: result.ruleStack }; } @@ -183,20 +183,20 @@ async function analyzeLine( function calculateIndents(lines: LineInfo[], baseIndent: number = 0): number[] { const indents: number[] = []; let currentIndent = baseIndent; - + for (const line of lines) { // For empty lines or comments, keep current indent level if (line.isEmpty) { indents.push(0); // Empty lines have no indentation continue; } - + // For POD blocks, preserve their indentation if (line.isPod) { indents.push(currentIndent); continue; } - + // For string continuation lines, preserve whitespace but still track // bracket state so subsequent code gets the right indent level. if (line.preserveLeadingWhitespace) { @@ -205,17 +205,17 @@ function calculateIndents(lines: LineInfo[], baseIndent: number = 0): number[] { currentIndent = Math.max(0, currentIndent + line.indentDelta.after); continue; } - + // Apply "before" adjustment (for closing brackets at start of line) currentIndent = Math.max(0, currentIndent + line.indentDelta.before); - + // This line should be at the current indent level indents.push(currentIndent); - + // Apply "after" adjustment (for opening brackets at end of line) currentIndent = Math.max(0, currentIndent + line.indentDelta.after); } - + return indents; } @@ -225,7 +225,7 @@ function calculateIndents(lines: LineInfo[], baseIndent: number = 0): number[] { function generateIndent(level: number, options: FormattingOptions, config: FormattingConfig): string { const indentSize = options.tabSize || config.indentSize || 4; const totalSpaces = level * indentSize; - + if (options.insertSpaces !== false) { // Use spaces return ' '.repeat(totalSpaces); @@ -244,10 +244,10 @@ function isInsideSpecialContext(tokens: vsctm.IToken[], lineText: string, positi for (let i = 0; i < tokens.length; i++) { const token = tokens[i]; const startIdx = i === 0 ? 0 : tokens[i - 1].endIndex; - + if (position >= startIdx && position < token.endIndex) { - return token.scopes.some(scope => - scope.startsWith("string") || + return token.scopes.some(scope => + scope.startsWith("string") || scope.startsWith("comment") || scope.includes("regexp") ); @@ -274,8 +274,8 @@ function isBlockBraceAtPosition(tokens: vsctm.IToken[], position: number): boole * Check if line contains a declaration keyword (class, role, method, sub, etc.) */ function hasDeclarationKeyword(line: LineInfo): boolean { - return line.tokens.some(token => - token.scopes.some(scope => + return line.tokens.some(token => + token.scopes.some(scope => scope.includes('storage.type.class.raku') || scope.includes('storage.type.declarator.type.raku') || scope.includes('storage.type.declare.regexp.named.raku') @@ -288,7 +288,7 @@ function hasDeclarationKeyword(line: LineInfo): boolean { */ function isElseKeyword(line: LineInfo): boolean { if (line.isEmpty || line.isComment || line.isPod) return false; - + const trimmed = line.text.trim(); // Check if line starts with else/elsif/orwith keyword if (/^(else|elsif|orwith)\b/.test(trimmed)) { @@ -315,12 +315,12 @@ function applyLineFormatting(line: LineInfo, config: FormattingConfig): string { const originalText = line.text; let newText = originalText; const edits: { start: number; end: number; replacement: string }[] = []; - + // Skip entirely if line is comment, string, or POD if (line.isComment || line.isPod || line.isEmpty || line.isString) { return newText; } - + // Rule 1: Remove trailing whitespace if (config.trimTrailingWhitespace) { const trailingMatch = originalText.match(/\s+$/); @@ -332,7 +332,7 @@ function applyLineFormatting(line: LineInfo, config: FormattingConfig): string { }); } } - + // Rule 2: Remove space before semicolon if (config.noSpaceBeforeSemicolon) { for (let i = 0; i < originalText.length; i++) { @@ -347,7 +347,7 @@ function applyLineFormatting(line: LineInfo, config: FormattingConfig): string { } } } - + // Rule 3: Add space after comma (if not followed by space/newline/closing bracket) if (config.spaceAfterComma) { for (let i = 0; i < originalText.length; i++) { @@ -362,7 +362,7 @@ function applyLineFormatting(line: LineInfo, config: FormattingConfig): string { } } } - + // Rule 4: Add space after control keywords (if, for, while, etc.) if (config.spaceAfterKeywords) { const controlKeywords = ['if', 'unless', 'for', 'while', 'until', 'loop', 'given', 'when', 'with', 'without', 'orwith']; @@ -373,10 +373,10 @@ function applyLineFormatting(line: LineInfo, config: FormattingConfig): string { scope.includes('keyword.control.repeat') ); if (!isControlKeyword) continue; - + const startIdx = i === 0 ? 0 : line.tokens[i - 1].endIndex; const tokenText = originalText.substring(startIdx, token.endIndex); - + for (const keyword of controlKeywords) { const match = tokenText.match(new RegExp(`\\b${keyword}\\b`)); if (!match || match.index === undefined) continue; @@ -388,7 +388,7 @@ function applyLineFormatting(line: LineInfo, config: FormattingConfig): string { } } } - + // Rule 5: Add space before opening brace in declarations if (config.spaceBeforeBrace && hasDeclarationKeyword(line)) { for (let i = 0; i < originalText.length; i++) { @@ -400,13 +400,13 @@ function applyLineFormatting(line: LineInfo, config: FormattingConfig): string { } } } - + // Apply edits from end to start to keep offsets stable edits.sort((a, b) => b.start - a.start); for (const edit of edits) { newText = newText.substring(0, edit.start) + edit.replacement + newText.substring(edit.end); } - + return newText; } @@ -416,16 +416,16 @@ function applyLineFormatting(line: LineInfo, config: FormattingConfig): string { */ function applyMultiLineFormatting(lineInfos: LineInfo[], allLines: string[], config: FormattingConfig, rangeStart: number = 0, rangeEnd: number = lineInfos.length - 1): Map { const modifications = new Map(); - + for (let i = 0; i < lineInfos.length - 1; i++) { const currentLine = lineInfos[i]; const nextLine = lineInfos[i + 1]; - + // Skip if current line is comment, string, or POD if (currentLine.isComment || currentLine.isPod || currentLine.isEmpty || currentLine.isString) { continue; } - + // Rule 1: Uncuddled elses - move else/elsif/orwith to same line as closing brace if (config.cuddledElse && (currentLine.text.trim() === '}' || currentLine.text.trim() === '};')) { // Check if next non-blank line is an else keyword @@ -433,21 +433,21 @@ function applyMultiLineFormatting(lineInfos: LineInfo[], allLines: string[], con while (nextContentLine < lineInfos.length && lineInfos[nextContentLine].isEmpty) { nextContentLine++; } - + if (nextContentLine < lineInfos.length && isElseKeyword(lineInfos[nextContentLine])) { // Skip if any involved line is outside the formatting range if (i < rangeStart || nextContentLine > rangeEnd) continue; const elseLineInfo = lineInfos[nextContentLine]; const elseLinesqueezed = elseLineInfo.text.trim(); - + // Cuddle the else: add it to the current line const newCurrentLine = currentLine.text.trimEnd() + ' ' + elseLinesqueezed; modifications.set(i, newCurrentLine); - + // Mark the else line for removal (empty it) modifications.set(nextContentLine, ''); - + // Remove intermediate blank lines for (let j = i + 1; j < nextContentLine; j++) { if (lineInfos[j].isEmpty) { @@ -456,7 +456,7 @@ function applyMultiLineFormatting(lineInfos: LineInfo[], allLines: string[], con } } } - + // Rule 2: Opening brace on same line for declarations if (config.braceOnSameLine && hasDeclarationKeyword(currentLine) && !endsWithOpenParen(currentLine) && !currentLine.text.trim().includes('{')) { // Check if next non-blank line is just an opening brace @@ -464,11 +464,11 @@ function applyMultiLineFormatting(lineInfos: LineInfo[], allLines: string[], con while (nextContentLine < lineInfos.length && lineInfos[nextContentLine].isEmpty) { nextContentLine++; } - + if (nextContentLine < lineInfos.length) { const nextLineText = lineInfos[nextContentLine].text.trim(); const nextLineRaw = lineInfos[nextContentLine].text; - + // Check if next line starts with { (and might have other content like comments) if (nextLineText.startsWith('{')) { // Skip if any involved line is outside the formatting range @@ -481,10 +481,10 @@ function applyMultiLineFormatting(lineInfos: LineInfo[], allLines: string[], con // Move the brace to the current line const newCurrentLine = currentLine.text.trimEnd() + ' ' + nextLineText; modifications.set(i, newCurrentLine); - + // Remove the brace line modifications.set(nextContentLine, ''); - + // Remove intermediate blank lines for (let j = i + 1; j < nextContentLine; j++) { if (lineInfos[j].isEmpty) { @@ -495,7 +495,7 @@ function applyMultiLineFormatting(lineInfos: LineInfo[], allLines: string[], con } } } - + return modifications; } @@ -509,7 +509,7 @@ export async function formatDocument( ): Promise { const text = document.getText(); const lines = text.split(/\r?\n/); - + return formatLines(document, lines, 0, lines.length - 1, options, settings); } @@ -524,7 +524,7 @@ export async function formatRange( ): Promise { const text = document.getText(); const lines = text.split(/\r?\n/); - + // We need to analyze from the beginning to track bracket state correctly, // but only generate edits for the requested range return formatLines(document, lines, range.start.line, range.end.line, options, settings); @@ -541,22 +541,22 @@ async function formatLines( options: FormattingOptions, settings: NavigatorSettings ): Promise { - + const grammar = await registry.loadGrammar("source.raku"); if (!grammar) { throw new Error("Couldn't load Textmate grammar"); } - + // Tokenize all lines to track bracket state correctly let ruleStack: vsctm.StateStack | null = vsctm.INITIAL; const lineInfos: LineInfo[] = []; - + for (let i = 0; i < allLines.length; i++) { const analysis: { lineInfo: LineInfo; ruleStack: vsctm.StateStack } = await analyzeLine(i, allLines[i], grammar, ruleStack); lineInfos.push(analysis.lineInfo); ruleStack = analysis.ruleStack; } - + // Calculate expected indentation for all lines const expectedIndents = calculateIndents(lineInfos); const formatting: NonNullable = settings.formatting ?? { enable: true, indentSize: 4 }; @@ -571,11 +571,11 @@ async function formatLines( cuddledElse: formatting.cuddledElse !== false, indentSize: formatting.indentSize || 4, }; - + // Apply multi-line formatting rules (uncuddled elses, brace on same line) // Pass the range so merges straddling the boundary are skipped const multiLineChanges = applyMultiLineFormatting(lineInfos, allLines, config, startLine, endLine); - + // Track lines to delete (they'll be merged into other lines) const linesToDelete = new Set(); for (const [lineNum, newText] of multiLineChanges.entries()) { @@ -583,14 +583,14 @@ async function formatLines( linesToDelete.add(lineNum); } } - + // Generate TextEdit array only for the requested range const edits: TextEdit[] = []; - + for (let i = startLine; i <= Math.min(endLine, lineInfos.length - 1); i++) { const line = lineInfos[i]; const originalText = allLines[i]; - + // Check if this line should be deleted (merged into previous line) if (linesToDelete.has(i)) { if (i + 1 <= allLines.length - 1) { @@ -610,22 +610,22 @@ async function formatLines( } continue; } - + // Check if this line was modified by multi-line rules let newText = multiLineChanges.has(i) ? multiLineChanges.get(i)! : originalText; const isNowEmpty = newText.trim().length === 0; - + // Apply additional formatting rules (skip if multi-line modified to avoid token drift) const formattedText = multiLineChanges.has(i) ? newText : applyLineFormatting({ ...line, text: newText }, config); - + // Apply indentation fix after other line edits let finalText = formattedText; const trimmedText = finalText.trim(); const isMethodContinuation = trimmedText.startsWith('.'); - + // Skip indentation for method continuation lines (preserve user formatting) if (!isNowEmpty && !line.isPod && !line.preserveLeadingWhitespace && !isMethodContinuation) { const expectedIndent = expectedIndents[i]; @@ -636,7 +636,7 @@ async function formatLines( finalText = expectedWhitespace + finalText.substring(currentLeading.length); } } - + // Only create an edit if the line changed if (originalText !== finalText) { const range: Range = { @@ -646,12 +646,12 @@ async function formatLines( edits.push(TextEdit.replace(range, finalText)); } } - + // Handle final newline (only for full document formatting) if (config.insertFinalNewline && startLine === 0 && endLine === allLines.length - 1) { const documentText = document.getText(); const endsWithNewline = documentText.endsWith('\n') || documentText.endsWith('\r\n'); - + if (!endsWithNewline && documentText.length > 0) { // Add final newline const lastLine = allLines.length - 1; @@ -662,6 +662,6 @@ async function formatLines( )); } } - + return edits; } diff --git a/server/src/parser.ts b/server/src/parser.ts index 3667511..c451188 100644 --- a/server/src/parser.ts +++ b/server/src/parser.ts @@ -120,10 +120,10 @@ function localVars(state: ParserState): boolean { // line, or if this line starts with my or local let match; if (state.var_continues || (match = state.stmt.match(/^(?:my|our|let|state)\b/))) { - // The declaration continues unless there's a semicolon, signature end, or sub start. + // The declaration continues unless there's a semicolon, signature end, or sub start. // This can get tripped up with comments, but it's not a huge deal. subroutines are more important state.var_continues = !state.stmt.match(/[\)\=\}\{;]/); - + let mod_stmt = state.stmt; // Remove my or local from statement, if present mod_stmt = mod_stmt.replace(/^(my|our|let|state)\s+/, ""); @@ -550,7 +550,7 @@ const vscodeOnigurumaLib = oniguruma.loadWASM(wasmBin).then(() => { const registry = new vsctm.Registry({ onigLib: vscodeOnigurumaLib, loadGrammar: async (scopeName) => { - const grammarpath = path.join(__dirname, "./../raku.tmLanguage.json"); + const grammarpath = path.join(__dirname, "./../raku.merged.tmLanguage.json"); const grammar = await fs.promises.readFile(grammarpath, "utf8"); return vsctm.parseRawGrammar(grammar, grammarpath); }, diff --git a/server/src/references.ts b/server/src/references.ts index 6f611bc..b50c9f8 100644 --- a/server/src/references.ts +++ b/server/src/references.ts @@ -31,7 +31,7 @@ const vscodeOnigurumaLib = oniguruma.loadWASM(wasmBin).then(() => { const registry = new vsctm.Registry({ onigLib: vscodeOnigurumaLib, loadGrammar: async (scopeName) => { - const grammarpath = path.join(__dirname, './../raku.tmLanguage.json'); + const grammarpath = path.join(__dirname, './../raku.merged.tmLanguage.json'); const grammar = await readFileSync(grammarpath, 'utf8'); return vsctm.parseRawGrammar(grammar, grammarpath); }, diff --git a/testWorkspace/quoting.raku b/testWorkspace/quoting.raku new file mode 100644 index 0000000..bbc39e6 --- /dev/null +++ b/testWorkspace/quoting.raku @@ -0,0 +1,9 @@ +# This file is extracted from https://github.com/finanalyst/rakuast-rakudoc-render/blob/main/lib/RakuDoc/To/HTML.rakumod +# Aimed to verify the parsing for quoting + +class Test +{ + my $a = qw「 < > & & " > < 」 => qw「 < > & & " > <」; + + method test() {} +}