From 4ab9cc473ea851d554a7f0b26c11d755679f402b Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 19 Apr 2026 09:54:08 +0000 Subject: [PATCH 1/4] fix: end type cast at arithmetic/comparison operators (issue #24) Highlighting was lost after a type cast when followed by arithmetic operators like `*`, because `lookAheadEndOfType` didn't recognize those operators as terminators for the type expression. Add binary arithmetic operators (`*`, `+`, `%`, `^`, `/`, `//`, `..`, `-`, `~=`, `==`) and logical keywords (`and`, `or`) to `lookAheadEndOfType` so that `expr::Type * value` correctly ends the type cast at `*`. Care is taken to avoid false positives: - `-(?![->\`])` prevents matching `--` (comments) or `->` (function types) - `(?`])|~=|==))|(?=\s*\b(?:and|or|break|const|continue|do|else|elseif|end|for|function|if|in|local|repeat|return|then|until|while)\b)|(?=^\s*$)|(?=^\s*(?:@|\b(?:export\s+)?type\b))|(?=^\s*{{identifier}}(?:\.{{identifier}})*\s*(?:(?:[+\-*/%^]|\.\.)?=(?!=)|\(|\[|:))' identifier: "[a-zA-Z_][a-zA-Z0-9_]*" patterns: diff --git a/Luau.tmLanguage b/Luau.tmLanguage index 8f71ba7..6d799da 100644 --- a/Luau.tmLanguage +++ b/Luau.tmLanguage @@ -1114,7 +1114,7 @@ begin (?<![^.]\.|:)\b(?:(export)\s+)?(type)\b(?=\s+(?!function\b)[a-zA-Z_][a-zA-Z0-9_]*) end - (?=[),}\]](?!\s*[&|]))|(?=\s*;)|(?=\s*\b(?:break|const|continue|do|else|elseif|end|for|function|if|in|local|repeat|return|then|until|while)\b)|(?=^\s*$)|(?=^\s*(?:@|\b(?:export\s+)?type\b))|(?=^\s*[a-zA-Z_][a-zA-Z0-9_]*(?:\.[a-zA-Z_][a-zA-Z0-9_]*)*\s*(?:(?:[+\-*/%^]|\.\.)?=(?!=)|\(|\[|:)) + (?=[),}\]](?!\s*[&|]))|(?=\s*;)|(?=\s*(?:[*+%^]|\/\/?|(?<!\.)\.\.(?!\.)|-(?![->`])|~=|==))|(?=\s*\b(?:and|or|break|const|continue|do|else|elseif|end|for|function|if|in|local|repeat|return|then|until|while)\b)|(?=^\s*$)|(?=^\s*(?:@|\b(?:export\s+)?type\b))|(?=^\s*[a-zA-Z_][a-zA-Z0-9_]*(?:\.[a-zA-Z_][a-zA-Z0-9_]*)*\s*(?:(?:[+\-*/%^]|\.\.)?=(?!=)|\(|\[|:)) beginCaptures 1 @@ -1173,7 +1173,7 @@ end - (?=[),}\]](?!\s*[&|]))|(?=\s*;)|(?=\s*\b(?:break|const|continue|do|else|elseif|end|for|function|if|in|local|repeat|return|then|until|while)\b)|(?=^\s*$)|(?=^\s*(?:@|\b(?:export\s+)?type\b))|(?=^\s*[a-zA-Z_][a-zA-Z0-9_]*(?:\.[a-zA-Z_][a-zA-Z0-9_]*)*\s*(?:(?:[+\-*/%^]|\.\.)?=(?!=)|\(|\[|:)) + (?=[),}\]](?!\s*[&|]))|(?=\s*;)|(?=\s*(?:[*+%^]|\/\/?|(?<!\.)\.\.(?!\.)|-(?![->`])|~=|==))|(?=\s*\b(?:and|or|break|const|continue|do|else|elseif|end|for|function|if|in|local|repeat|return|then|until|while)\b)|(?=^\s*$)|(?=^\s*(?:@|\b(?:export\s+)?type\b))|(?=^\s*[a-zA-Z_][a-zA-Z0-9_]*(?:\.[a-zA-Z_][a-zA-Z0-9_]*)*\s*(?:(?:[+\-*/%^]|\.\.)?=(?!=)|\(|\[|:)) patterns diff --git a/Luau.tmLanguage.json b/Luau.tmLanguage.json index 50db9fb..5c00a84 100644 --- a/Luau.tmLanguage.json +++ b/Luau.tmLanguage.json @@ -733,7 +733,7 @@ }, "type-alias-declaration": { "begin": "(?`])|~=|==))|(?=\\s*\\b(?:and|or|break|const|continue|do|else|elseif|end|for|function|if|in|local|repeat|return|then|until|while)\\b)|(?=^\\s*$)|(?=^\\s*(?:@|\\b(?:export\\s+)?type\\b))|(?=^\\s*[a-zA-Z_][a-zA-Z0-9_]*(?:\\.[a-zA-Z_][a-zA-Z0-9_]*)*\\s*(?:(?:[+\\-*/%^]|\\.\\.)?=(?!=)|\\(|\\[|:))", "beginCaptures": { "1": { "name": "storage.modifier.visibility.luau" @@ -771,7 +771,7 @@ "name": "keyword.operator.typecast.luau" } }, - "end": "(?=[),}\\]](?!\\s*[&|]))|(?=\\s*;)|(?=\\s*\\b(?:break|const|continue|do|else|elseif|end|for|function|if|in|local|repeat|return|then|until|while)\\b)|(?=^\\s*$)|(?=^\\s*(?:@|\\b(?:export\\s+)?type\\b))|(?=^\\s*[a-zA-Z_][a-zA-Z0-9_]*(?:\\.[a-zA-Z_][a-zA-Z0-9_]*)*\\s*(?:(?:[+\\-*/%^]|\\.\\.)?=(?!=)|\\(|\\[|:))", + "end": "(?=[),}\\]](?!\\s*[&|]))|(?=\\s*;)|(?=\\s*(?:[*+%^]|\\/\\/?|(?`])|~=|==))|(?=\\s*\\b(?:and|or|break|const|continue|do|else|elseif|end|for|function|if|in|local|repeat|return|then|until|while)\\b)|(?=^\\s*$)|(?=^\\s*(?:@|\\b(?:export\\s+)?type\\b))|(?=^\\s*[a-zA-Z_][a-zA-Z0-9_]*(?:\\.[a-zA-Z_][a-zA-Z0-9_]*)*\\s*(?:(?:[+\\-*/%^]|\\.\\.)?=(?!=)|\\(|\\[|:))", "patterns": [ { "include": "#type_literal" diff --git a/build/tsconfig.json b/build/tsconfig.json index 710a394..fc7baf2 100644 --- a/build/tsconfig.json +++ b/build/tsconfig.json @@ -1,6 +1,7 @@ { "compilerOptions": { "target": "ES5", + "ignoreDeprecations": "6.0", "module": "commonjs", "strict": true, "skipLibCheck": true diff --git a/tests/baselines/issue-24.baseline.txt b/tests/baselines/issue-24.baseline.txt new file mode 100644 index 0000000..b573ee2 --- /dev/null +++ b/tests/baselines/issue-24.baseline.txt @@ -0,0 +1,56 @@ +original file +----------------------------------- +local a = tonumber("1")::number * 1000 +print(a) + +----------------------------------- + +>local a = tonumber("1")::number * 1000 + ^^^^^ + source.luau storage.modifier.local.luau + ^ + source.luau + ^ + source.luau variable.other.readwrite.luau + ^ + source.luau + ^ + source.luau keyword.operator.assignment.luau + ^ + source.luau + ^^^^^^^^ + source.luau support.function.luau + ^ + source.luau punctuation.arguments.begin.luau + ^ + source.luau string.quoted.double.luau + ^ + source.luau string.quoted.double.luau + ^ + source.luau string.quoted.double.luau + ^ + source.luau punctuation.arguments.end.luau + ^^ + source.luau keyword.operator.typecast.luau + ^^^^^^ + source.luau support.type.primitive.luau + ^ + source.luau + ^ + source.luau keyword.operator.arithmetic.luau + ^ + source.luau + ^^^^ + source.luau constant.numeric.decimal.luau +>print(a) + ^^^^^ + source.luau support.function.luau + ^ + source.luau punctuation.arguments.begin.luau + ^ + source.luau variable.other.readwrite.luau + ^ + source.luau punctuation.arguments.end.luau +> + ^ + source.luau \ No newline at end of file diff --git a/tests/baselines/stylua-luau-assignment-hang-1.lua.baseline.txt b/tests/baselines/stylua-luau-assignment-hang-1.lua.baseline.txt index d04a4cf..d0fc0e6 100644 --- a/tests/baselines/stylua-luau-assignment-hang-1.lua.baseline.txt +++ b/tests/baselines/stylua-luau-assignment-hang-1.lua.baseline.txt @@ -450,10 +450,14 @@ exports.separateDisplayNameAndHOCs = source.luau ^^^ source.luau support.type.primitive.luau - ^^^^ + ^ source.luau + ^^ + source.luau keyword.operator.comparison.luau + ^ + source.luau ^^^ - source.luau support.type.primitive.luau + source.luau constant.language.nil.luau ^ source.luau ^^ diff --git a/tests/cases/issue-24.luau b/tests/cases/issue-24.luau new file mode 100644 index 0000000..90be498 --- /dev/null +++ b/tests/cases/issue-24.luau @@ -0,0 +1,2 @@ +local a = tonumber("1")::number * 1000 +print(a) From 2f71afd5edfda9736d1d64f6b51adff043f64187 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 19 Apr 2026 09:59:08 +0000 Subject: [PATCH 2/4] refactor: extract nonTypeOperators and nonTypeKeywords variables Break up the monolithic lookAheadEndOfType regex into named sub-variables with comments explaining each group, making the grammar easier to read and maintain. https://claude.ai/code/session_01M3sgc87GoVj2GrtNsV9vdi --- Luau.YAML-tmLanguage | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Luau.YAML-tmLanguage b/Luau.YAML-tmLanguage index 559428d..4006755 100644 --- a/Luau.YAML-tmLanguage +++ b/Luau.YAML-tmLanguage @@ -12,8 +12,15 @@ fileTypes: [luau] variables: matchingParentheses: (\(([^\(\)]|(\(([^\(\)]|\([^\(\)]*\))*\)))*\)) - lookAheadEndOfType: '(?=[),}\]](?!\s*[&|]))|(?=\s*;)|(?=\s*(?:[*+%^]|\/\/?|(?`])|~=|==))|(?=\s*\b(?:and|or|break|const|continue|do|else|elseif|end|for|function|if|in|local|repeat|return|then|until|while)\b)|(?=^\s*$)|(?=^\s*(?:@|\b(?:export\s+)?type\b))|(?=^\s*{{identifier}}(?:\.{{identifier}})*\s*(?:(?:[+\-*/%^]|\.\.)?=(?!=)|\(|\[|:))' identifier: "[a-zA-Z_][a-zA-Z0-9_]*" + # Arithmetic/comparison operators that are valid in expressions but cannot appear at the + # top level of a Luau type expression, so their presence signals the end of a type. + # Exclusions: -> (function return type arrow), -- (comment), ... (variadic pack) + nonTypeOperators: '[*+%^]|\/\/?|(?`])|~=|==' + # Keywords that are valid outside a type expression (as binary operators or statement + # starters) but cannot appear within one, so their presence signals the end of a type. + nonTypeKeywords: 'and|or|break|const|continue|do|else|elseif|end|for|function|if|in|local|repeat|return|then|until|while' + lookAheadEndOfType: '(?=[),}\]](?!\s*[&|]))|(?=\s*;)|(?=\s*(?:{{nonTypeOperators}}))|(?=\s*\b(?:{{nonTypeKeywords}})\b)|(?=^\s*$)|(?=^\s*(?:@|\b(?:export\s+)?type\b))|(?=^\s*{{identifier}}(?:\.{{identifier}})*\s*(?:(?:[+\-*/%^]|\.\.)?=(?!=)|\(|\[|:))' patterns: - include: "#function-definition" From 0a2644833dd89db114cbea4e9b773b99f00f8584 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 19 Apr 2026 10:02:07 +0000 Subject: [PATCH 3/4] simplify: remove unrelated print() from issue-24 test case https://claude.ai/code/session_01M3sgc87GoVj2GrtNsV9vdi --- tests/baselines/issue-24.baseline.txt | 10 ---------- tests/cases/issue-24.luau | 1 - 2 files changed, 11 deletions(-) diff --git a/tests/baselines/issue-24.baseline.txt b/tests/baselines/issue-24.baseline.txt index b573ee2..3346961 100644 --- a/tests/baselines/issue-24.baseline.txt +++ b/tests/baselines/issue-24.baseline.txt @@ -1,7 +1,6 @@ original file ----------------------------------- local a = tonumber("1")::number * 1000 -print(a) ----------------------------------- @@ -42,15 +41,6 @@ print(a) source.luau ^^^^ source.luau constant.numeric.decimal.luau ->print(a) - ^^^^^ - source.luau support.function.luau - ^ - source.luau punctuation.arguments.begin.luau - ^ - source.luau variable.other.readwrite.luau - ^ - source.luau punctuation.arguments.end.luau > ^ source.luau \ No newline at end of file diff --git a/tests/cases/issue-24.luau b/tests/cases/issue-24.luau index 90be498..62bb75d 100644 --- a/tests/cases/issue-24.luau +++ b/tests/cases/issue-24.luau @@ -1,2 +1 @@ local a = tonumber("1")::number * 1000 -print(a) From 53bd3a418b876d299dabc8b39d08487f502a83b7 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 20 Apr 2026 20:46:26 +0000 Subject: [PATCH 4/4] revert: remove ignoreDeprecations from build/tsconfig.json "6.0" is not a valid value in TypeScript 5.1 (TS5103), causing CI failure. https://claude.ai/code/session_01M3sgc87GoVj2GrtNsV9vdi --- build/tsconfig.json | 1 - 1 file changed, 1 deletion(-) diff --git a/build/tsconfig.json b/build/tsconfig.json index fc7baf2..710a394 100644 --- a/build/tsconfig.json +++ b/build/tsconfig.json @@ -1,7 +1,6 @@ { "compilerOptions": { "target": "ES5", - "ignoreDeprecations": "6.0", "module": "commonjs", "strict": true, "skipLibCheck": true