From: Lopy <70210066+lopi-py@users.noreply.github.com> Date: Thu, 18 Jun 2026 19:36:32 +0000 (+0000) Subject: runtime(luau): runtime support is incomplete X-Git-Tag: v9.2.0677~1 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=8181c6e31387c0467fcd6394d1b7da07f38e737c;p=thirdparty%2Fvim.git runtime(luau): runtime support is incomplete Problem: runtime(luau): runtime support is incomplete. Solution: Add Luau syntax, indent, filetype plugin and indent tests. closes: #20544 Signed-off-by: Lopy <70210066+lopi-py@users.noreply.github.com> Signed-off-by: Christian Brabandt --- diff --git a/.github/MAINTAINERS b/.github/MAINTAINERS index 6a786bfb12..f0266e7726 100644 --- a/.github/MAINTAINERS +++ b/.github/MAINTAINERS @@ -255,6 +255,7 @@ runtime/ftplugin/liquid.vim @tpope runtime/ftplugin/logtalk.dict @pmoura runtime/ftplugin/logtalk.vim @pmoura runtime/ftplugin/lua.vim @dkearns +runtime/ftplugin/luau.vim @lopi-py runtime/ftplugin/lynx.vim @dkearns runtime/ftplugin/m17ndb.vim @dseomn runtime/ftplugin/m3build.vim @dkearns @@ -420,6 +421,7 @@ runtime/indent/less.vim @genoma runtime/indent/liquid.vim @tpope runtime/indent/logtalk.vim @pmoura runtime/indent/lua.vim @marcuscf +runtime/indent/luau.vim @lopi-py runtime/indent/m17ndb.vim @dseomn runtime/indent/make.vim @dkearns runtime/indent/meson.vim @Liambeguin @@ -609,6 +611,7 @@ runtime/syntax/liquid.vim @tpope runtime/syntax/log.vim @mao-yining runtime/syntax/logtalk.vim @pmoura runtime/syntax/lua.vim @marcuscf +runtime/syntax/luau.vim @lopi-py runtime/syntax/lynx.vim @dkearns runtime/syntax/lyrics.vim @ObserverOfTime runtime/syntax/m17ndb.vim @dseomn diff --git a/runtime/doc/filetype.txt b/runtime/doc/filetype.txt index 61c72fffac..73ce826c19 100644 --- a/runtime/doc/filetype.txt +++ b/runtime/doc/filetype.txt @@ -1,4 +1,4 @@ -*filetype.txt* For Vim version 9.2. Last change: 2026 Jun 17 +*filetype.txt* For Vim version 9.2. Last change: 2026 Jun 18 VIM REFERENCE MANUAL by Bram Moolenaar @@ -890,6 +890,12 @@ For example, to use Lua 5.1, set the variables like this: > let g:lua_version = 5 let g:lua_subversion = 1 < +LUAU *ft-luau-plugin* *g:luau_folding* + +You can enable folding of Luau block constructs using |fold-expr| by: > + + let g:luau_folding = 1 +< MAIL *ft-mail-plugin* Options: diff --git a/runtime/doc/tags b/runtime/doc/tags index 40161f75c3..1b9575fd9e 100644 --- a/runtime/doc/tags +++ b/runtime/doc/tags @@ -7634,6 +7634,7 @@ ft-log-syntax syntax.txt /*ft-log-syntax* ft-lpc-syntax syntax.txt /*ft-lpc-syntax* ft-lua-plugin filetype.txt /*ft-lua-plugin* ft-lua-syntax syntax.txt /*ft-lua-syntax* +ft-luau-plugin filetype.txt /*ft-luau-plugin* ft-mail-plugin filetype.txt /*ft-mail-plugin* ft-mail.vim syntax.txt /*ft-mail.vim* ft-make-syntax syntax.txt /*ft-make-syntax* @@ -7898,6 +7899,7 @@ g:lf_shell_syntax syntax.txt /*g:lf_shell_syntax* g:lua_folding filetype.txt /*g:lua_folding* g:lua_subversion filetype.txt /*g:lua_subversion* g:lua_version filetype.txt /*g:lua_version* +g:luau_folding filetype.txt /*g:luau_folding* g:markdown_fenced_languages syntax.txt /*g:markdown_fenced_languages* g:markdown_minlines syntax.txt /*g:markdown_minlines* g:markdown_syntax_conceal syntax.txt /*g:markdown_syntax_conceal* diff --git a/runtime/ftplugin/luau.vim b/runtime/ftplugin/luau.vim index 458d0b05a9..9b413e6778 100644 --- a/runtime/ftplugin/luau.vim +++ b/runtime/ftplugin/luau.vim @@ -1,14 +1,193 @@ " Vim filetype plugin file -" Language: Luau -" Maintainer: None yet -" Last Change: 2023 Apr 30 +" Language: Luau +" Maintainer: Lopy (@lopi-py) +" Last Change: 2026 Jun 16 if exists("b:did_ftplugin") finish endif +let b:did_ftplugin = 1 -" Luau is a superset of Lua -runtime! ftplugin/lua.vim +let s:cpo_save = &cpo +set cpo&vim +setlocal comments=:---,:-- +setlocal commentstring=--\ %s +setlocal formatoptions-=t formatoptions+=croql -" vim: nowrap sw=2 sts=2 ts=8 +let &l:define = '\<\%(function\|local\%(\s\+function\)\=\|const\%(\s\+function\)\=\|type\%(\s\+function\)\=\|export\s\+type\%(\s\+function\)\=\|class\|declare\s\+\%(function\|class\|extern\s\+type\)\)\>' +let &l:include = '\.\{-}\' .. + \ '\|^\s*\%(' .. s:attr .. '\)*class\>\s\+\h' .. + \ '\|^\s*\%(' .. s:attr .. '\)*declare\s\+class\>\s\+\h' .. + \ '\|^\s*\%(' .. s:attr .. '\)*declare\s\+extern\s\+type\>.\{-}\' .. s:line_end + +if exists("loaded_matchit") && !exists("b:match_words") + let b:match_ignorecase = 0 + let s:match_tail = ':\\|^\s*\%(else\|elseif\)\>:' .. s:end_pat .. + \ ',^\s*repeat\>:\,' .. + \ '\%(--\)\=\[\(=*\)\[:\]\1\]' + let s:match_words = '\<\%(do\|function\)\>\|' .. s:end_block .. s:match_tail + let s:match_words_no_function = '\\|' .. s:end_block .. s:match_tail + let b:match_words = "LuauMatchWords()" + let b:match_skip = 'LuauMatchSkip()' + let b:undo_ftplugin ..= " | unlet! b:match_words b:match_ignorecase b:match_skip" +endif + +if (has("gui_win32") || has("gui_gtk")) && !exists("b:browsefilter") + let b:browsefilter = "Luau Source Files (*.luau)\t*.luau\n" + if has("win32") + let b:browsefilter ..= "All Files (*.*)\t*\n" + else + let b:browsefilter ..= "All Files (*)\t*\n" + endif + let b:undo_ftplugin ..= " | unlet! b:browsefilter" +endif + +if has("folding") && get(g:, "luau_folding", 0) + setlocal foldmethod=expr + setlocal foldexpr=s:LuauFold() + let b:luau_lasttick = -1 + let b:undo_ftplugin ..= " | setl foldexpr< foldmethod< | unlet! b:luau_lasttick b:luau_foldlists" +endif + +" the rest of the file needs to be sourced only once per Vim session +if exists("s:loaded_luau") || &cp + let &cpo = s:cpo_save + unlet s:cpo_save + finish +endif +let s:loaded_luau = 1 + +function s:LuauInclude(fname) abort + let fname = a:fname + if fname =~# '^@' + return fname + endif + for path in [fname, fname .. ".luau", fname .. ".lua", fname .. "/init.luau", fname .. "/init.lua"] + if filereadable(path) + return path + endif + endfor + return fname +endfunction + +function s:IsStringOrComment(lnum, col) abort + let name = synIDattr(synID(a:lnum, a:col, 1), "name") + return name =~# '^luau\%(Comment\|.*String\)' +endfunction + +function s:LineCommentStart(lnum) abort + let line = getline(a:lnum) + let midx = stridx(line, '--') + while midx != -1 + if !s:IsStringOrComment(a:lnum, midx + 1) + return midx + endif + let midx = stridx(line, '--', midx + 2) + endwhile + return -1 +endfunction + +function s:InDeclareClass(lnum) abort + let save_cursor = getcurpos() + call cursor(a:lnum - 1, 1) + let lnum = search('^\s*\%(end\>\|declare\s\+class\>\)', 'bcnW') + call setpos('.', save_cursor) + return lnum > 0 && getline(lnum) =~# '^\s*declare\s\+class\>' +endfunction + +function LuauMatchSkip() abort + let lnum = line(".") + let col = col(".") + let comment = s:LineCommentStart(lnum) + return s:IsStringOrComment(lnum, col) + \ || (comment != -1 && col > comment) + \ || (expand("") ==# "function" && s:InDeclareClass(lnum)) +endfunction + +function LuauMatchWords() abort + if exists("s:match_words_no_function") + \ && expand("") ==# "function" && s:InDeclareClass(line(".")) + return s:match_words_no_function + endif + return get(s:, "match_words", "") +endfunction + +let s:fold_guard = '^\s*\%(@\|\%(do\|if\|repeat\|for\|while\|public' .. + \ '\|function\|return\|local\|const\|type\|export\|class' .. + \ '\|declare\|end\|until\)\>\)' + +let s:patterns = [ + \ ['^\s*do\>' .. s:line_end, s:end_pat, 'block'], + \ ['^\s*if\>.\{-}\' .. s:line_end, s:end_pat, 'block'], + \ ['^\s*repeat\>' .. s:line_end, '^\s*until\>.*', 'block'], + \ ['^\s*for\>.\{-}\' .. s:line_end, s:end_pat, 'block'], + \ ['^\s*while\>.\{-}\' .. s:line_end, s:end_pat, 'block'], + \ ['^\s*\%(' .. s:attr .. '\)*\%(public\s\+\)\=function\>.*', s:end_pat, 'function'], + \ ['^\s*return\s\+function\>.*', s:end_pat, 'function'], + \ ['^\s*\%(' .. s:attr .. '\)*local\s\+function\>.*', s:end_pat, 'function'], + \ ['^\s*\%(' .. s:attr .. '\)*const\s\+function\>.*', s:end_pat, 'function'], + \ ['^\s*\%(' .. s:attr .. '\)*\%(export\s\+\)\=type\s\+function\>.*', s:end_pat, 'function'], + \ ['^\s*class\>\s\+\h.*', s:end_pat, 'class'], + \ ['^\s*declare\s\+class\>\s\+\h.*', s:end_pat, 'declare_class'], + \ ['^\s*declare\s\+extern\s\+type\>.\{-}\' .. s:line_end, s:end_pat, 'block'], + \ ] + +function s:LuauFold() abort + if b:luau_lasttick == b:changedtick + return b:luau_foldlists[v:lnum - 1] + endif + let b:luau_lasttick = b:changedtick + + let b:luau_foldlists = [] + let foldlist = [] + let buf = getline(1, "$") + for line in buf + let open = 0 + let end = 0 + if line !~# s:fold_guard + call add(b:luau_foldlists, "" .. len(foldlist)) + continue + endif + for t in s:patterns + let tagopen = t[0] + let tagend = t[1] + if line =~# tagopen + if t[2] ==# 'function' && len(foldlist) > 0 && foldlist[-1][2] ==# 'declare_class' + continue + endif + call add(foldlist, t) + let open = 1 + break + elseif line =~# tagend + if len(foldlist) > 0 && line =~# foldlist[-1][1] + call remove(foldlist, -1) + let end = 1 + else + let foldlist = [] + endif + break + endif + endfor + let prefix = "" + if open == 1 | let prefix = ">" | endif + if end == 1 | let prefix = "<" | endif + call add(b:luau_foldlists, prefix .. (len(foldlist) + end)) + endfor + + return b:luau_foldlists[v:lnum - 1] +endfunction + +let &cpo = s:cpo_save +unlet s:cpo_save + +" vim: nowrap sw=2 sts=2 ts=8 noet: diff --git a/runtime/indent/luau.vim b/runtime/indent/luau.vim index 69893f7399..a7422acf36 100644 --- a/runtime/indent/luau.vim +++ b/runtime/indent/luau.vim @@ -1,14 +1,153 @@ -" Vim filetype indent file -" Language: Luau -" Maintainer: None yet -" Last Change: 2023 Apr 30 +" Vim indent file +" Language: Luau +" Maintainer: Lopy (@lopi-py) +" Last Change: 2026 Jun 17 -" Only load this indent file when no other was loaded. +" only load this indent file when no other was loaded if exists("b:did_indent") - finish + finish endif +let b:did_indent = 1 -" Luau is a superset of Lua -runtime! indent/lua.vim +let s:cpo_save = &cpo +set cpo&vim +setlocal indentexpr=GetLuauIndent() +" make Vim call GetLuauIndent() when it finds a closing keyword or delimiter +setlocal indentkeys+=0=end,0=until,0=else,0=elseif,0=},0=),0=] + +setlocal autoindent + +let b:undo_indent = "setlocal autoindent< indentexpr< indentkeys<" + +" only define the function once +if exists("*GetLuauIndent") + let &cpo = s:cpo_save + unlet s:cpo_save + finish +endif + +function GetLuauIndent() abort + let ignorecase_save = &ignorecase + try + let &ignorecase = 0 + return s:GetLuauIndentIntern() + finally + let &ignorecase = ignorecase_save + endtry +endfunction + +function s:InDeclareClass(lnum) abort + let save_cursor = getcurpos() + call cursor(a:lnum - 1, 1) + let lnum = search('^\s*\%(end\>\|declare\s\+class\>\)', 'bcnW') + call setpos('.', save_cursor) + return lnum > 0 && getline(lnum) =~# '^\s*declare\s\+class\>' +endfunction + +function s:IsStringOrComment(lnum, col) abort + let name = synIDattr(synID(a:lnum, a:col, 1), "name") + return name =~# '^luau\%(Comment\|.*String\)' +endfunction + +function s:LineCommentStart(lnum) abort + let line = getline(a:lnum) + let midx = stridx(line, '--') + while midx != -1 + if !s:IsStringOrComment(a:lnum, midx + 1) + return midx + endif + let midx = stridx(line, '--', midx + 2) + endwhile + return -1 +endfunction + +function s:IsCode(lnum, col) abort + let comment = s:LineCommentStart(a:lnum) + return (comment == -1 || a:col <= comment) && !s:IsStringOrComment(a:lnum, a:col) +endfunction + +function s:HasBlockCloser(lnum) abort + let line = getline(a:lnum) + let midx = match(line, '\<\%(end\|until\)\>') + while midx != -1 + if s:IsCode(a:lnum, midx + 1) + return 1 + endif + let midx = match(line, '\<\%(end\|until\)\>', midx + 1) + endwhile + return 0 +endfunction + +function s:GetLuauIndentIntern() abort + " find a non-blank line above the current line + let prevlnum = prevnonblank(v:lnum - 1) + + " hit the start of the file, use zero indent + if prevlnum == 0 + return 0 + endif + + " add a 'shiftwidth' after lines that start a block + let ind = indent(prevlnum) + let prevline = getline(prevlnum) + let attr = '@\%(\h\w*\|\[[^]]*\]\)\s*' + let stmt = 'if\>\|for\>\|while\>\|repeat\>\|else\>\|elseif\>\|do\>\|then\>' + let class = 'class\>\s\+\h\|declare\s\+class\>\s\+\h' + let func = '\%(\%(public\s\+\)\=function\|local\s\+function\|const\s\+function\|type\s\+function\|return\s\+function\)' + let declare_func = '^\s*\%(' .. attr .. '\)*declare\s\+function\>' + let midx = -1 + if prevline =~# '^\s*\%(@\|\%(if\|for\|while\|repeat\|else\|elseif\|do\|then\|class\)\>\|declare\s\+class\>\)' + let midx = match(prevline, '^\s*\%(' .. attr .. '\)*\%(' .. stmt .. '\|' .. class .. '\)') + endif + if midx == -1 + if prevline =~# '^\s*\%(' .. attr .. '\)*declare\s\+extern\s\+type\>' + let midx = match(prevline, '\\s*\%(--.*\)\=$') + endif + if midx == -1 + let midx = match(prevline, '\%({\|(\|\[\)\s*\%(--\%([^[].*\)\?\)\?$') + if midx == -1 && stridx(prevline, 'function') != -1 && prevline !~# declare_func + let midx = match(prevline, '\<' .. func .. '\>\s*\%(\k\|[.:]\)\{-}\s*\%(<[^>]*>\s*\)\=(') + endif + endif + endif + + if midx == -1 && prevline =~ '^\s*)\s*\%(:.\+\)\=\s*\%(--.*\)\=$' + let save_cursor = getcurpos() + call cursor(prevlnum, match(prevline, ')') + 1) + let [par_lnum, par_col] = searchpairpos('(', '', ')', 'bnW') + call setpos('.', save_cursor) + if par_lnum > 0 + let funline = getline(par_lnum) + let funidx = match(funline, '\<' .. func .. '\>') + if funidx != -1 && funidx < par_col && funline !~# declare_func + let midx = match(prevline, ')') + endif + endif + endif + + if midx != -1 + " add 'shiftwidth' if this is not in a comment or string and the block + " does not close on the same line + if s:IsCode(prevlnum, midx + 1) && !s:HasBlockCloser(prevlnum) + \ && !(prevline =~# '^\s*\%(public\s\+\)\=function\>' && s:InDeclareClass(prevlnum)) + let ind = ind + shiftwidth() + endif + endif + + " subtract a 'shiftwidth' on end, else, elseif, until, '}', ')' and ']' + let midx = match(getline(v:lnum), '^\s*\%(end\>\|else\>\|elseif\>\|until\>\|}\|)\|\]\)') + if midx != -1 + if s:IsCode(v:lnum, midx + 1) + let ind = ind - shiftwidth() + endif + endif + + return ind +endfunction + +let &cpo = s:cpo_save +unlet s:cpo_save + +" vim: nowrap sw=2 sts=2 ts=8 noet: diff --git a/runtime/indent/testdir/luau.in b/runtime/indent/testdir/luau.in new file mode 100644 index 0000000000..0d3a4afaaa --- /dev/null +++ b/runtime/indent/testdir/luau.in @@ -0,0 +1,101 @@ +-- vim: set ft=luau sw=2 noet: + +-- START_INDENT +@native +function compute(values: {T}): number +local total: number = 0 +for _, value: T in values do +if value then +continue +else +total += 1 +end +end +return total +end + +local function typed( +name: string, +count: number +): boolean +return count > 0 +end + +function singletonReturn(): "end" +print("string singleton return") +end + +if ready then -- end in a comment +print("comment closer ignored") +end + +local oneline = function() end +local afterOneline = oneline + +declare function PluginManager(): PluginManager +local afterDeclareFunction = true + +declare function WithArgs( +arg: string +): boolean +local afterDeclareFunctionArgs = true + +local text = `function(` +local afterText = text + +-- function( +local afterLineComment = text + +class = makeClass() +local afterClassIdentifier = class + +export type Box = { +read value: T, +write name: string, +callback: (T) -> (), +} + +const function makeBox(value: T): Box +return { +value = value, +name = `box {tostring(value)}`, +callback = function(item: T) +print(item) +end, +} +end + +declare extern type Vector with +x: number +y: number +z: number +end + +declare class EnumItem extends Enum +@deprecated +function IsA(self, enumName: string): boolean +Name: string +end + +type function Optionalize(t) +return types.optional(t) +end + +local choice = if 2 == 3 then 4 else 5 +local raw = [=[ +if true then +]=] +local afterRaw = choice + +--[=[ +function ignored() +]=] +local afterComment = raw + +class Widget +public Name: string +public function Describe(self): string +return `Widget {self.Name}` +end +end +-- END_INDENT diff --git a/runtime/indent/testdir/luau.ok b/runtime/indent/testdir/luau.ok new file mode 100644 index 0000000000..d38b79525b --- /dev/null +++ b/runtime/indent/testdir/luau.ok @@ -0,0 +1,101 @@ +-- vim: set ft=luau sw=2 noet: + +-- START_INDENT +@native +function compute(values: {T}): number + local total: number = 0 + for _, value: T in values do + if value then + continue + else + total += 1 + end + end + return total +end + +local function typed( + name: string, + count: number +): boolean + return count > 0 +end + +function singletonReturn(): "end" + print("string singleton return") +end + +if ready then -- end in a comment + print("comment closer ignored") +end + +local oneline = function() end +local afterOneline = oneline + +declare function PluginManager(): PluginManager +local afterDeclareFunction = true + +declare function WithArgs( + arg: string +): boolean +local afterDeclareFunctionArgs = true + +local text = `function(` +local afterText = text + +-- function( +local afterLineComment = text + +class = makeClass() +local afterClassIdentifier = class + +export type Box = { + read value: T, + write name: string, + callback: (T) -> (), +} + +const function makeBox(value: T): Box + return { + value = value, + name = `box {tostring(value)}`, + callback = function(item: T) + print(item) + end, + } +end + +declare extern type Vector with + x: number + y: number + z: number +end + +declare class EnumItem extends Enum + @deprecated + function IsA(self, enumName: string): boolean + Name: string +end + +type function Optionalize(t) + return types.optional(t) +end + +local choice = if 2 == 3 then 4 else 5 +local raw = [=[ +if true then +]=] +local afterRaw = choice + +--[=[ +function ignored() +]=] +local afterComment = raw + +class Widget + public Name: string + public function Describe(self): string + return `Widget {self.Name}` + end +end +-- END_INDENT diff --git a/runtime/syntax/luau.vim b/runtime/syntax/luau.vim index 59eccac100..e5b3490ee4 100644 --- a/runtime/syntax/luau.vim +++ b/runtime/syntax/luau.vim @@ -1,15 +1,193 @@ " Vim syntax file -" Language: Luau -" Maintainer: None yet -" Last Change: 2023 Apr 30 +" Language: Luau +" Maintainer: Lopy (@lopi-py) +" Last Change: 2026 Jun 17 +" quit when a syntax file was already loaded if exists("b:current_syntax") finish endif -" Luau is a superset of lua -runtime! syntax/lua.vim +let s:cpo_save = &cpo +set cpo&vim + +syn case match +syn sync minlines=300 + +" comments +syn keyword luauTodo contained TODO FIXME +syn match luauDirective contained "--!\%(strict\|nonstrict\|nocheck\|nolint\%(Global\)\=\|native\|optimize\)\>.*" + +" strings +syn match luauSpecial contained #\\[[:digit:]]\{1,3}\|\\x[[:xdigit:]]\{2}\|\\u{[[:xdigit:]]\+}\|\\z\s*\|\\\n\|\\[^xu[:digit:]\r\n]# +syn region luauString2 matchgroup=luauStringDelimiter start="\[\z(=*\)\[" end="\]\z1\]" contains=@Spell +syn region luauString matchgroup=luauStringDelimiter start=+'+ end=+'+ skip=+\\\\\|\\'+ contains=luauSpecial,@Spell +syn region luauString matchgroup=luauStringDelimiter start=+"+ end=+"+ skip=+\\\\\|\\"+ contains=luauSpecial,@Spell +syn region luauInterpString matchgroup=luauStringDelimiter start=+`+ end=+`+ skip=+\\`+ contains=luauSpecial,luauInterp,@Spell +syn region luauInterp contained transparent matchgroup=luauInterpDelimiter start=+\\\@" +syn match luauNumber "\<0_*[bB][01_]*[01][01_]*\>" +syn match luauNumber "\<\d[[:digit:]_]*\%(\.[[:digit:]_]*\)\=\%([eE][-+]\=[[:digit:]_]*\d[[:digit:]_]*\)\=\>" +syn match luauNumber "\.\d[[:digit:]_]*\%([eE][-+]\=[[:digit:]_]*\d[[:digit:]_]*\)\=\>" + +" keywords +syn keyword luauStatement return local break end +syn keyword luauStatement do nextgroup=luauStatement skipwhite +syn keyword luauStatement contained continue +syn match luauStatement "^\s*\zscontinue\>\ze\s*\%(;\|end\>\|else\>\|elseif\>\|until\>\|--.*\|$\)" +syn match luauStatement ";\s*\zscontinue\>\ze\s*\%(;\|end\>\|else\>\|elseif\>\|until\>\|--.*\|$\)" +syn keyword luauFunction function nextgroup=luauFunctionName,luauGenericParams skipwhite +syn match luauStatement "^\s*\%(@\%(\h\w*\|\[[^]]*\]\)\s*\)*\zsconst\>\ze\s\+\%(function\>\|\h\)" +syn match luauStatement "^\s*\%(@\%(\h\w*\|\[[^]]*\]\)\s*\)*\zsdeclare\>\ze\s\+\%(function\>\|class\>\s\+\h\|extern\s\+type\>\|\h\w*\s*:\)" nextgroup=luauStatement,luauDeclareName,luauFunction skipwhite +syn match luauStatement contained "\\ze\s\+\h" nextgroup=luauTypedef skipwhite +syn match luauDeclareName contained "\h\w*\ze\s*:" nextgroup=luauTypeColon skipwhite +syn match luauStatement "\\ze\s\+type\>" +syn match luauStatement contained "\\ze\s\+type\>" +syn match luauStatement "\\ze\s\+\h" nextgroup=luauTypeName skipwhite +syn match luauStatement "\\ze\s*\%(--.*\)\=$" +syn match luauModifier "\\ze\s\+\%(function\|\h\)" +syn match luauTypeKeyword "\\ze\s\+\h" nextgroup=luauTypeFunction,luauTypedef skipwhite +syn keyword luauTypeFunction contained function nextgroup=luauFunctionName skipwhite +syn match luauFunctionName contained transparent "\h\w*\%([.:]\h\w*\)*" nextgroup=luauGenericParams skipwhite +syn keyword luauCond if elseif +syn keyword luauCond then else nextgroup=luauStatement skipwhite +syn keyword luauRepeat while for until in +syn keyword luauRepeat repeat nextgroup=luauStatement skipwhite +syn keyword luauOperator and or not + +" operators and punctuation +syn match luauSymbolOperator "[#+*/%^=<>~?-]\|\.\{3}\|\.\.=\=" +syn match luauSymbolOperator "->" nextgroup=luauSymbolOperator,luauConstant,luauTableType,luauTypePack,luauTypeName skipwhite +syn match luauSymbolOperator "[|&]" nextgroup=luauConstant,luauTableType,luauTypePack,luauTypeName skipwhite +syn match luauSymbolOperator "::" + +" tables +syn region luauTableBlock transparent matchgroup=luauTable start="{" end="}" contains=TOP,luauStatement + +syn match luauComment "--.*$" contains=luauTodo,luauDirective,@Spell +syn region luauComment matchgroup=luauCommentDelimiter start="--\[\z(=*\)\[" end="\]\z1\]" contains=luauTodo,@Spell + +" the first line may start with #! +syn match luauComment "\%^#!.*" + +" attributes +syn match luauAttribute "@\h\w*" +syn region luauAttributeBlock matchgroup=luauAttributeDelimiter start="@\[" end="\]" contains=luauAttributeName,luauAttributeTable,luauString,luauString2,luauInterpString,luauNumber,luauConstant +syn region luauAttributeTable contained transparent matchgroup=luauTable start="{" end="}" contains=luauAttributeTable,luauString,luauString2,luauInterpString,luauNumber,luauConstant,luauSymbolOperator +syn keyword luauAttributeName contained checked native deprecated + +" constants and types +syn keyword luauConstant nil true false +syn match luauConstant "\.\.\." +syn keyword luauSelf self +syn match luauSelf contained "(\s*\zsself\>\ze\s*:" +syn cluster luauTypeCommon contains=luauGenericParams,luauString,luauString2,luauInterpString,luauNumber,luauConstant,luauSymbolOperator +syn cluster luauTypeExpr contains=luauFunctionTypeParams,luauTableType,luauTypeParam,luauType,luauTypeKeyword,@luauTypeCommon +syn region luauGenericParams contained transparent matchgroup=luauSymbolOperator start="<" end=">" contains=@luauTypeExpr +syn region luauTypedefGenericParams contained transparent matchgroup=luauSymbolOperator start="<" end=">" contains=@luauTypeExpr nextgroup=luauTypeAliasAssign skipwhite +syn region luauExplicitTypeArgs transparent matchgroup=luauSymbolOperator start="<<" end=">>" contains=@luauTypeExpr +syn match luauTypeParam contained "\h\w*\%(\.\.\.\)\=" +syn match luauTypedef "\h\w*" contained nextgroup=luauTypedefGenericParams,luauTypeAliasAssign skipwhite +syn match luauTypeAliasAssign contained "=" nextgroup=luauSymbolOperator,luauConstant,luauTableType,luauTypeName skipwhite +syn region luauTableType contained transparent matchgroup=luauTable start="{" end="}" contains=luauFunctionTypeParams,luauTableType,luauSelf,luauTypeIndexer,luauTableElementType,luauTypeColon,luauTypeKeyword,@luauTypeCommon +syn match luauTypeIndexer "^\s*\[\s*" nextgroup=luauType skipwhite +syn match luauTypeIndexer contained transparent "\[\s*" nextgroup=luauType skipwhite +syn match luauType contained "\h\w*\%(\.\h\w*\)*\ze\s*\]\s*:" nextgroup=luauGenericParams skipwhite +syn match luauTableElementType contained "\h\w*\%(\.\h\w*\)*\ze\s*[\|$\)" nextgroup=luauGenericParams skipwhite +syn region luauFunctionTypeParams transparent start="(\ze\%([^()]*\))\s*->" end=")\ze\s*->" contains=luauFunctionTypeParam,luauTypeColon,luauSelf,@luauTypeCommon +syn match luauFunctionTypeParam contained "\h\w*\%(\.\h\w*\)*\%(\.\.\.\)\=\ze\s*?\=\s*[,)|&]" nextgroup=luauGenericParams skipwhite +syn match luauFunctionReturnStart transparent ")\s*:\s*" nextgroup=luauSymbolOperator,luauFunctionTypeParams,luauConstant,luauTableType,luauTypePack,luauTypeName skipwhite +syn region luauTypePack contained transparent start="(\%([^()]*)\s*->\)\@!" end=")" contains=luauFunctionTypeParams,luauTableType,luauTypeName,@luauTypeCommon +syn match luauTypeName contained "\h\w*\%(\.\h\w*\)*\%(\.\.\.\)\=" nextgroup=luauGenericParams skipwhite +syn match luauTypeKeyword "\<\%(read\|write\)\>\ze\s*\%(\[\|\h\+\s*:\)" nextgroup=luauTypeIndexer skipwhite + +" metamethods +syn keyword luauMetaMethod __index __newindex __mode __namecall __call __iter __len +syn keyword luauMetaMethod __eq __add __sub __mul __div __idiv __mod __pow __unm +syn keyword luauMetaMethod __lt __le __concat __type __metatable __tostring + +" methods +syn match luauMethodColon transparent ":\ze\s*\h\w*\s*\%(<<.\{-}>>\s*\|<[^>]*>\s*\)\=\%((\|{\|'\|\"\|\[\)" nextgroup=luauFunc skipwhite +syn match luauFunc contained "\h\w*\ze\s*\%(<<.\{-}>>\s*\|<[^>]*>\s*\)\=\%((\|{\|'\|\"\|\[\)" + +" global functions and values +syn keyword luauFunc assert error gcinfo getfenv getmetatable +syn keyword luauFunc ipairs loadstring newproxy next pairs pcall print +syn keyword luauFunc rawequal rawget rawlen rawset require select setfenv +syn keyword luauFunc setmetatable tonumber tostring unpack xpcall +syn keyword luauGlobal _G +syn keyword luauBuiltinConstant _VERSION +syn match luauFunc "\\ze\s*\%(<<.\{-}>>\s*\)\=(" +syn match luauFunc "\\ze\s*\%(<<.\{-}>>\s*\)\=(" + +" standard library members +syn match luauFunc /\/ +syn match luauFunc /\/ +syn match luauFunc /\/ +syn match luauFunc /\/ +syn match luauFunc /\/ +syn match luauFunc /\/ +syn match luauFunc /\/ +syn match luauFunc /\/ +syn match luauBuiltinConstant /\/ +syn match luauFunc /\/ +syn match luauFunc /\/ +syn match luauFunc /\/ +syn match luauFunc /\/ +syn match luauBuiltinConstant /\/ +syn match luauFunc /\/ +syn match luauBuiltinConstant /\/ +syn match luauFunc /\/ + +" define the default highlighting +hi def link luauStatement Statement +hi def link luauRepeat Repeat +hi def link luauString String +hi def link luauString2 String +hi def link luauInterpString String +hi def link luauStringDelimiter luauString +hi def link luauInterpDelimiter Special +hi def link luauNumber Number +hi def link luauOperator Operator +hi def link luauSymbolOperator luauOperator +hi def link luauConstant Constant +hi def link luauCond Conditional +hi def link luauFunction Function +hi def link luauMetaMethod Function +hi def link luauTable Structure +hi def link luauComment Comment +hi def link luauCommentDelimiter luauComment +hi def link luauDirective PreProc +hi def link luauTodo Todo +hi def link luauSpecial SpecialChar +hi def link luauFunc Identifier +hi def link luauGlobal Identifier +hi def link luauBuiltinConstant Constant +hi def link luauSelf Identifier +hi def link luauModifier StorageClass +hi def link luauType Type +hi def link luauTypeAfterColon luauType +hi def link luauTableElementType luauType +hi def link luauTypeAliasAssign luauSymbolOperator +hi def link luauFunctionTypeParam luauType +hi def link luauTypeName luauType +hi def link luauTypedef Typedef +hi def link luauTypeParam Type +hi def link luauTypeKeyword Keyword +hi def link luauTypeFunction luauFunction +hi def link luauDeclareName Identifier +hi def link luauAttribute PreProc +hi def link luauAttributeName PreProc +hi def link luauAttributeDelimiter PreProc let b:current_syntax = "luau" +let &cpo = s:cpo_save +unlet s:cpo_save + " vim: nowrap sw=2 sts=2 ts=8 noet: