Commit: runtime(hare): update to match upstream

1 view
Skip to first unread message

Christian Brabandt

unread,
Feb 6, 2026, 4:46:42 AM (12 days ago) Feb 6
to vim...@googlegroups.com
runtime(hare): update to match upstream

Commit: https://github.com/vim/vim/commit/de5d100c2f7cce390e6a01540c368e438ed41c61
Author: Amelia Clarke <sel...@perilune.dev>
Date: Fri Feb 6 09:44:16 2026 +0000

runtime(hare): update to match upstream

closes: https://github.com/vim/vim/issues/18640

Signed-off-by: Amelia Clarke <sel...@perilune.dev>
Signed-off-by: Christian Brabandt <c...@256bit.org>

diff --git a/runtime/autoload/dist/ft.vim b/runtime/autoload/dist/ft.vim
index 75a4bb5e6..188573f69 100644
--- a/runtime/autoload/dist/ft.vim
+++ b/runtime/autoload/dist/ft.vim
@@ -3,7 +3,7 @@ vim9script
# Vim functions for file type detection
#
# Maintainer: The Vim Project <https://github.com/vim/vim>
-# Last Change: 2026 Jan 25
+# Last Change: 2026 Feb 06
# Former Maintainer: Bram Moolenaar <Br...@vim.org>

# These functions are moved here from runtime/filetype.vim to make startup
@@ -473,12 +473,19 @@ def IsHareModule(dir: string, depth: number): bool
endif

# Check all files in the directory before recursing into subdirectories.
- return glob(dir .. '/*', true, true)
+ const items = glob(dir .. '/*', true, true)
->sort((a, b) => isdirectory(a) - isdirectory(b))
- ->reduce((acc, n) => acc
- || n =~ '\.ha$'
- || isdirectory(n) && IsHareModule(n, depth - 1),
- false)
+ for n in items
+ if isdirectory(n)
+ if IsHareModule(n, depth - 1)
+ return true
+ endif
+ elseif n =~ '\.ha$'
+ return true
+ endif
+ endfor
+
+ return false
enddef

# Determines whether a README file is inside a Hare module and should receive
diff --git a/runtime/autoload/hare.vim b/runtime/autoload/hare.vim
index 479b0f681..d85a05b38 100644
--- a/runtime/autoload/hare.vim
+++ b/runtime/autoload/hare.vim
@@ -1,13 +1,13 @@
vim9script

# Helper functions for Hare.
-# Language: Hare
-# Maintainer: Amelia Clarke <sel...@perilune.dev>
-# Last Updated: 2025 Sep 06
-# Upstream: https://git.sr.ht/~sircmpwn/hare.vim
+# Language: Hare
+# Maintainer: Amelia Clarke <sel...@perilune.dev>
+# Last Change: 2026 Jan 24
+# Upstream: https://git.sr.ht/~sircmpwn/hare.vim

-# Returns the value of HAREPATH, if it exists. Otherwise, returns a safe
-# default.
+# Returns the value of $HAREPATH, if it exists. Otherwise, returns a safe
+# default value.
export def GetPath(): string
var path: list<string>
if !empty($HAREPATH)
@@ -18,24 +18,7 @@ export def GetPath(): string
return '/usr/src/hare/stdlib,/usr/src/hare/third-party'
endif
endif
- return mapnew(path, (_, n) => escape(n, ' ,;'))->join(',')
-enddef
-
-# Converts a module identifier into a path.
-export def IncludeExpr(): string
- var path = trim(v:fname, ':', 2)->substitute('::', '/', 'g')
-
- # If the module cannot be found, it might be a member instead. Try removing
- # the final component until a directory is found.
- while !finddir(path)
- const head = fnamemodify(path, ':h')
- if head == '.'
- break
- endif
- path = head
- endwhile
-
- return path
+ return map(path, (_, n) => escape(n, ' ,;'))->join(',')
enddef

# Modifies quickfix or location list entries to refer to the correct paths after
@@ -61,14 +44,14 @@ export def QuickFixPaths()
SetList([], 'r', list)
enddef

-# Attempts to parse the directories in $HAREPATH from the output of `hare
-# version -v`. Otherwise, returns an empty list.
+# Attempts to parse a list of directories from the output of `hare version -v`.
+# Otherwise, returns an empty list.
def ParsePath(): list<string>
if !executable('hare')
return []
endif

- silent const lines = systemlist('hare version -v')
+ silent final lines = systemlist('hare version -v')
const min = match(lines, '^HAREPATH') + 1
if min == 0
return []
@@ -76,7 +59,7 @@ def ParsePath(): list<string>

const max = match(lines, '^\S', min)
return (max < 0 ? slice(lines, min) : slice(lines, min, max))
- ->mapnew((_, n) => matchstr(n, '^\s*\zs.*'))
+ ->map((_, n) => matchstr(n, '^\s*\zs.*'))
enddef

# vim: et sts=2 sw=2 ts=8 tw=80
diff --git a/runtime/compiler/hare.vim b/runtime/compiler/hare.vim
index 88f36a9e2..2b7d9345b 100644
--- a/runtime/compiler/hare.vim
+++ b/runtime/compiler/hare.vim
@@ -3,25 +3,30 @@ vim9script
# Vim compiler file.
# Compiler: Hare
# Maintainer: Amelia Clarke <sel...@perilune.dev>
-# Last Change: 2025 Sep 06
+# Last Change: 2026 Jan 24
# Upstream: https://git.sr.ht/~sircmpwn/hare.vim

if exists('g:current_compiler')
finish
endif
+g:current_compiler = 'hare'

if filereadable('Makefile') || filereadable('makefile')
CompilerSet makeprg=make
else
- const makeprg = 'hare build '
- .. get(b:, 'hare_makeprg_params', get(g:, 'hare_makeprg_params', '-q'))
+ const makeprg = 'hare build ' .. get(g:, 'hare_makeprg_params', '-q')
execute 'CompilerSet makeprg=' .. escape(makeprg, ' "\|')
endif

CompilerSet errorformat=
- \%o:%l:%v:\ syntax\ error:\ %m,
- \%o:%l:%v:\ error:\ %m,
- \Error:\ %m,
+ \%E%o:%l:%v:\ error:\ %m,
+ \%E%o:%l:%v:\ syntax\ error:\ %m,
+ \%E%o:%l:%v:\ %\%%(unexpected\ name\ %\)%\@=%m,
+ \%C,%C\ %.%#,%C%l\ %.%#,
+ \%trror:\ %o:\ %\%%(%\h%\w%\+%\%%(::%\h%\w%\+%\)%#:\ %\)%\@=%m,
+ \%trror:\ %m,
+ \%+EAbort:\ %m%>,
+ \%C%.%#,
\%-G%.%#

augroup HareQuickFix
@@ -30,6 +35,4 @@ augroup HareQuickFix
autocmd QuickFixCmdPost lmake hare#QuickFixPaths()
augroup END

-g:current_compiler = 'hare'
-
# vim: et sts=2 sw=2 ts=8 tw=80
diff --git a/runtime/doc/ft_hare.txt b/runtime/doc/ft_hare.txt
index ce344b73d..918ff2a5b 100644
--- a/runtime/doc/ft_hare.txt
+++ b/runtime/doc/ft_hare.txt
@@ -19,23 +19,33 @@ functionality for the Hare programming language.
FILETYPE PLUGIN *ft-hare-plugin*

This plugin has a few different variables that can be defined inside your
-|vimrc| to tweak its behavior.
+|vimrc| to adjust its behavior.

-Additionally, support is provided for folding `{ }` blocks. To enable folding,
-add the following to a file inside your |after-directory| (e.g.
+ *hare-folding*
+This plugin supports folding `{ }` blocks. To enable folding, add the
+following to a file inside your |after-directory| (e.g.
~/.vim/after/ftplugin/hare.vim): >

setlocal foldmethod=syntax

-Because block-based folding tends to create many small folds, consider setting
-a few related options, such as 'foldminlines' and 'foldnestmax'.
+Because syntax-based folding tends to create many small folds, consider
+setting a few related options, such as 'foldminlines' or 'foldnestmax'.

+ *hare-symbol-operators*
+Most symbolic operators do not receive any highlighting by default (with the
+exception of "?", "!", and "::"). If you prefer highlighting all operators,
+you can link them to your preferred highlight group inside your |vimrc|. For
+example: >
+
+ hi def link hareCast hareSymbolOperator
+ hi def link hareSymbolOperator hareOperator
+<
*g:hare_recommended_style*
The following options are set by default, in accordance with Hare's official
style guide: >

setlocal noexpandtab
- setlocal shiftwidth=0
+ setlocal shiftwidth=8
setlocal softtabstop=0
setlocal tabstop=8
setlocal textwidth=80
@@ -43,18 +53,11 @@ style guide: >
To disable this behavior, add the following to your |vimrc|: >

let g:hare_recommended_style = 0
-<
- *g:hare_symbol_operators*
-By default, symbolic operators do not receive any special highlighting (with
-`!`, `?`, and `::` being the only exceptions). To enable syntax highlighting
-for most other operators, add the following to your |vimrc|: >
-
- let g:hare_symbol_operators = 1
<
*g:hare_space_error*
-By default, trailing whitespace and spaces followed by <Tab> characters will
-be highlighted as errors. This is automatically disabled in Insert mode. To
-turn off this highlighting completely, add the following to your |vimrc|: >
+By default, trailing whitespace and spaces followed by <Tab> characters are
+highlighted as errors. This is automatically disabled while in insert mode.
+To turn off this highlighting completely, add the following to your |vimrc|: >

let g:hare_space_error = 0

@@ -67,28 +70,27 @@ this is such a common filename, this plugin only searches for Hare source
files within the same directory by default.

*g:filetype_haredoc*
-The |g:filetype_haredoc| variable can be used to tweak the depth of this
+The `g:filetype_haredoc` variable can be used to tweak the depth of this
search, or bypass the detection of Hare documentation files altogether:

Value Effect~
- 0 No automatic detection
+ 0 Search disabled
1 Search current directory only (this is the default)
2 Search one level of subdirectories
- 3 Search two levels of subdirectories

-The search depth may be any positive integer, but values higher than `2` are
-unlikely to provide a tangible benefit in most situations.
+The search depth may be any positive integer, but values greater than 2 are
+very unlikely to provide any tangible benefit and can impact performance.


INDENTATION SETTINGS *ft-hare-indent*

-Unlike most other settings for this plugin, the indentation settings may also
-be set per-buffer, overriding any global configuration that exists. To do
-this, simply prefix the variable with |b:| instead of |g:|.
+Unlike other settings, indentation settings may be configured on a per-buffer
+basis, overriding any existing global configuration. To do so, simply prefix
+the variable with |b:| instead of |g:|.

- *g:hare_indent_match_switch*
-By default, continuation lines for "match" and "switch" conditions are
-indented only one level: >hare
+ *g:hare_indent_match_switch* *b:hare_indent_match_switch*
+By default, the continuation lines for "match" and "switch" conditions are
+only indented one level: >hare

const file = match (os::create(path, 0o644,
flag::WRONLY | flag::TRUNC)) {
@@ -96,39 +98,33 @@ indented only one level: >hare
yield file;
// ...

-If you instead prefer indenting them two levels, to more closely resemble "if"
-and "for" conditions, add the following line to your |vimrc|: >
+If you prefer indenting them two levels, more closely resembling "if" and
+"for" conditions, add the following line to your |vimrc|: >

let g:hare_indent_match_switch = 2
<
- *g:hare_indent_case*
-By default, continuation lines for cases in "match" and "switch" expressions
-are indented two levels, to visually distinguish them from the body of the
-case: >hare
+ *g:hare_indent_case* *b:hare_indent_case*
+By default, the continuation lines for "match" and "switch" cases are indented
+two levels, to visually distinguish them from the case body: >hare

case ltok::I8, ltok::I16, ltok::I32,
ltok::I64, ltok::INT =>
// ...

-If you prefer a different amount of indentation, you can adjust it using
-|g:hare_indent_case|. Valid values include `0`, `1`, and `2`.
+If you prefer a different level of indentation, you can adjust it using
+`g:hare_indent_case`. The possible values are 0, 1, and 2.


COMPILER SUPPORT *compiler-hare*

-If this plugin detects a Makefile in the current directory, it will assume you
-wish to use `make` for your build system, and will leave 'makeprg' untouched.
-Otherwise, `hare build` will be used.
-
- *g:hare_makeprg_params*
-When `hare build` is used, additional compiler options may be appended to
-'makeprg' with the |g:hare_makeprg_params| variable. It may also be set on a
-per-buffer basis (using |b:| instead of |g:|), overriding any global
-configuration that exists. For example: >
-
- let b:hare_makeprg_params = '-lc -t o'
+If a Makefile is detected in the current directory, this plugin will assume
+you are using "make" for your build system, and will leave 'makeprg' as-is.
+Otherwise, "hare build" will be used.

-The global default is "-q", to suppress writing to stdout while building.
+ *g:hare_makeprg_params*
+When using "hare build", additional compiler options may be appended to
+'makeprg' using `g:hare_makeprg_params`. The default is "-q", to suppress
+printing to stdout when building.

==============================================================================
vim:tw=78:ts=8:noet:ft=help:norl:
diff --git a/runtime/doc/tags b/runtime/doc/tags
index cafe489a7..23aa27960 100644
--- a/runtime/doc/tags
+++ b/runtime/doc/tags
@@ -6315,6 +6315,8 @@ b:changelog_name filetype.txt /*b:changelog_name*
b:clojure_syntax_keywords syntax.txt /*b:clojure_syntax_keywords*
b:clojure_syntax_without_core_keywords syntax.txt /*b:clojure_syntax_without_core_keywords*
b:current_syntax-variable syntax.txt /*b:current_syntax-variable*
+b:hare_indent_case ft_hare.txt /*b:hare_indent_case*
+b:hare_indent_match_switch ft_hare.txt /*b:hare_indent_match_switch*
b:lf_shell_syntax syntax.txt /*b:lf_shell_syntax*
b:netrw_lastfile pi_netrw.txt /*b:netrw_lastfile*
b:rust_cargo_avoid_whole_workspace ft_rust.txt /*b:rust_cargo_avoid_whole_workspace*
@@ -7819,7 +7821,6 @@ g:hare_indent_match_switch ft_hare.txt /*g:hare_indent_match_switch*
g:hare_makeprg_params ft_hare.txt /*g:hare_makeprg_params*
g:hare_recommended_style ft_hare.txt /*g:hare_recommended_style*
g:hare_space_error ft_hare.txt /*g:hare_space_error*
-g:hare_symbol_operators ft_hare.txt /*g:hare_symbol_operators*
g:help_example_languages helphelp.txt /*g:help_example_languages*
g:html_charset_override syntax.txt /*g:html_charset_override*
g:html_diff_one_file syntax.txt /*g:html_diff_one_file*
@@ -8313,6 +8314,8 @@ haiku-vimdir os_haiku.txt /*haiku-vimdir*
hangul hangulin.txt /*hangul*
hangulin.txt hangulin.txt /*hangulin.txt*
hare ft_hare.txt /*hare*
+hare-folding ft_hare.txt /*hare-folding*
+hare-symbol-operators ft_hare.txt /*hare-symbol-operators*
hare.vim ft_hare.txt /*hare.vim*
has() builtin.txt /*has()*
has-patch builtin.txt /*has-patch*
diff --git a/runtime/ftplugin/hare.vim b/runtime/ftplugin/hare.vim
index eca1a7881..ebd7fbdcc 100644
--- a/runtime/ftplugin/hare.vim
+++ b/runtime/ftplugin/hare.vim
@@ -1,10 +1,10 @@
vim9script

# Vim filetype plugin.
-# Language: Hare
-# Maintainer: Amelia Clarke <sel...@perilune.dev>
-# Last Updated: 2025 Sep 06
-# Upstream: https://git.sr.ht/~sircmpwn/hare.vim
+# Language: Hare
+# Maintainer: Amelia Clarke <sel...@perilune.dev>
+# Last Change: 2026 Jan 24
+# Upstream: https://git.sr.ht/~sircmpwn/hare.vim

if exists('b:did_ftplugin')
finish
@@ -18,22 +18,21 @@ b:undo_ftplugin = 'compiler make'
# Formatting settings.
setlocal comments=://
setlocal commentstring=//\ %s
-setlocal formatlistpat=^\s*-\
+setlocal formatlistpat=^\s*-\s\+
setlocal formatoptions+=croqnlj/ formatoptions-=t
b:undo_ftplugin ..= ' | setl cms< com< flp< fo<'

# Locate Hare modules.
&l:include = ' ^\s*use\s+%(\h\w*\s*\=)?'
-setlocal includeexpr=hare#IncludeExpr()
+&l:includeexpr = 'trim(v:fname, ":", 2)->substitute("::", "/", "g")'
setlocal isfname+=:
&l:path = ',,' .. hare#GetPath()
-setlocal suffixesadd=.ha
-b:undo_ftplugin ..= ' | setl inc< inex< isf< pa< sua<'
+b:undo_ftplugin ..= ' | setl inc< inex< isf< pa<'

# Follow the official style guide by default.
if get(g:, 'hare_recommended_style', 1)
setlocal noexpandtab
- setlocal shiftwidth=0
+ setlocal shiftwidth=8
setlocal softtabstop=0
setlocal tabstop=8
setlocal textwidth=80
diff --git a/runtime/ftplugin/haredoc.vim b/runtime/ftplugin/haredoc.vim
index ca66b0663..04cabfb4b 100644
--- a/runtime/ftplugin/haredoc.vim
+++ b/runtime/ftplugin/haredoc.vim
@@ -1,10 +1,10 @@
vim9script

# Vim filetype plugin.
-# Language: Haredoc (Hare documentation format)
-# Maintainer: Amelia Clarke <sel...@perilune.dev>
-# Last Updated: 2025 Sep 06
-# Upstream: https://git.sr.ht/~sircmpwn/hare.vim
+# Language: Haredoc (Hare documentation format)
+# Maintainer: Amelia Clarke <sel...@perilune.dev>
+# Last Change: 2026 Jan 24
+# Upstream: https://git.sr.ht/~sircmpwn/hare.vim

if exists('b:did_ftplugin')
finish
@@ -18,21 +18,20 @@ b:undo_ftplugin = 'compiler make'
# Formatting settings.
setlocal comments=:\
setlocal commentstring=\ %s
-setlocal formatlistpat=^-\
+setlocal formatlistpat=^\s*-\s\+
setlocal formatoptions+=tnlj formatoptions-=c formatoptions-=q
b:undo_ftplugin ..= ' | setl cms< com< flp< fo<'

# Locate Hare modules.
-setlocal includeexpr=hare#IncludeExpr()
+&l:includeexpr = 'trim(v:fname, ":", 2)->substitute("::", "/", "g")'
setlocal isfname+=:
&l:path = ',,' .. hare#GetPath()
-setlocal suffixesadd=.ha
-b:undo_ftplugin ..= ' | setl inex< isf< pa< sua<'
+b:undo_ftplugin ..= ' | setl inex< isf< pa<'

# Follow the official style guide by default.
if get(g:, 'hare_recommended_style', 1)
setlocal noexpandtab
- setlocal shiftwidth=0
+ setlocal shiftwidth=8
setlocal softtabstop=0
setlocal tabstop=8
setlocal textwidth=80
diff --git a/runtime/indent/hare.vim b/runtime/indent/hare.vim
index 84496348a..ff38f600e 100644
--- a/runtime/indent/hare.vim
+++ b/runtime/indent/hare.vim
@@ -3,7 +3,7 @@ vim9script
# Vim indent file.
# Language: Hare
# Maintainer: Amelia Clarke <sel...@perilune.dev>
-# Last Change: 2025 Sep 06
+# Last Change: 2026 Jan 24
# Upstream: https://git.sr.ht/~sircmpwn/hare.vim

if exists('b:did_indent')
@@ -17,7 +17,7 @@ b:did_indent = 1
# +0 -> Don't indent continuation lines.
# (s -> Indent one level inside parens.
# u0 -> Don't indent additional levels inside nested parens.
-# U1 -> Don't treat `(` any differently if it is at the start of a line.
+# U1 -> Don't treat `(` any differently if it started a line.
# m1 -> Indent lines starting with `)` the same as the matching `(`.
# j1 -> Indent blocks one level inside parens.
# J1 -> Indent structs and unions correctly.
@@ -56,7 +56,11 @@ def GetHareIndent(): number

# If the previous line started the block, use the same indent.
if pline =~ '{$'
- return pindent
+ if pline =~ ' <%(match|switch)>[^(]*\('
+ return pindent
+ endif
+ return pindent - GetValue('hare_indent_match_switch', 1, 1, 2)
+ * shiftwidth()
endif

# If the current line contains a `:` that is not part of `::`, use the
diff --git a/runtime/syntax/hare.vim b/runtime/syntax/hare.vim
index 992b7b905..44f72b6d7 100644
--- a/runtime/syntax/hare.vim
+++ b/runtime/syntax/hare.vim
@@ -3,7 +3,7 @@ vim9script
# Vim syntax file.
# Language: Hare
# Maintainer: Amelia Clarke <sel...@perilune.dev>
-# Last Change: 2025 Sep 06
+# Last Change: 2026 Feb 01
# Upstream: https://git.sr.ht/~sircmpwn/hare.vim

if exists('b:current_syntax')
@@ -14,11 +14,39 @@ endif
syn case match
syn iskeyword @,48-57,@-@,_

+# Identifiers {{{2
+syn cluster hareIdentifier contains=hareName,hareScopeDelimiter,@hareReserved
+syn match hareIdentifier ' <\h\w*%(::\h\w*)*>' contains=@hareIdentifier nextgroup=hareScopeDelimiter,@harePostfix skipempty skipwhite
+syn match hareName '\<\h\w*\>' contained transparent
+
# Reserved keywords.
-syn cluster hareReserved contains=hareBoolean,hareBuiltin,hareConditional,hareConstant,hareDefine,hareInclude,hareKeyword,hareLabel,hareOperator,hareRepeat,hareStorageClass,hareStructure,hareType,hareTypedef
+syn cluster hareReserved contains=hareBoolean,hareBuiltin,hareConditional,hareConstant,hareDefine,hareInclude,hareKeyword,hareLabel,hareOperator,hareRepeat,hareStatement,hareStorageClass,hareStructure,hareType,hareTypedef
+
+# Punctuators {{{2
+
+# Balanced tokens.
+syn region hareBraces matchgroup=hareBrace start='{' end='}' contains=TOP fold transparent
+syn region hareBrackets matchgroup=hareBracket start='\[' end=']' contains=TOP transparent
+syn region hareParens matchgroup=hareParen start='(' end=')' contains=TOP nextgroup=@harePostfix skipempty skipwhite transparent
+
+# Symbolic operators.
+syn match hareSymbolOperator '\.\{2,3}'
+syn match hareSymbolOperator '[!<=>]=\?'
+syn match hareSymbolOperator '=>'
+
+# Additive and multiplicative arithmetic.
+syn match hareSymbolOperator '[-+*/%]=\?'
+
+# Bitwise arithmetic.
+syn match hareSymbolOperator '\%(<<\|>>\)=\?'
+syn match hareSymbolOperator '[&^|]=\?'
+syn match hareSymbolOperator '\~'
+
+# Logical arithmetic.
+syn match hareSymbolOperator '\%(&&\|^^\|||\)=\?'

# Types {{{2
-syn cluster hareType contains=hareErrorFlag,harePointer,hareSlice,hareStorageClass,hareStructure,hareTaggedUnion,hareType
+syn cluster hareType contains=hareArray,hareError,harePointer,hareStorageClass,hareStructure,hareTaggedUnion,hareType
syn keyword hareType bool
syn keyword hareType done
syn keyword hareType f32 f64
@@ -33,199 +61,167 @@ syn keyword hareType void
# C ABI.
syn keyword hareType valist

+# Pointer types.
+syn match harePointer '*' contained containedin=hareTaggedUnion,hareTypeParens nextgroup=@hareType skipempty skipwhite
+syn keyword hareStorageClass nullable nextgroup=harePointer skipempty skipwhite
+
# Slice and array types.
-syn region hareSlice matchgroup=hareSlice start='\[' end=']' contained containedin=hareBuiltinTypeCall,hareTaggedUnion contains=TOP nextgroup=@hareType skipempty skipwhite
-syn match hareSlice '\[[*_]]' contains=hareSliceBounds nextgroup=@hareType skipempty skipwhite
-syn match hareSliceBounds '[*_]' contained display
+syn region hareArray matchgroup=hareBracket start='\[' end=']' contained containedin=hareTaggedUnion,hareTypeParens contains=TOP nextgroup=@hareType skipempty skipwhite transparent
+syn match hareArray '\[[*_]]' contains=hareArrayBounds nextgroup=@hareType skipempty skipwhite transparent
+syn match hareArrayBounds '*' contained display
+
+# Tagged union and tuple types.
+syn region hareTaggedUnion matchgroup=hareParen start='(' end=')' contained containedin=hareTaggedUnion,hareTypeParens contains=TOP transparent
+syn match hareTaggedUnionBar '|' contained containedin=hareTaggedUnion

# Other types.
-syn keyword hareStorageClass nullable nextgroup=harePointer skipempty skipwhite
+syn match hareError '!' contained containedin=hareTaggedUnion,hareTypeParens nextgroup=@hareType skipempty skipwhite
syn keyword hareStructure enum struct union

# Declarations {{{2
syn keyword hareDefine def
syn keyword hareInclude use
-syn keyword hareKeyword const nextgroup=@hareType skipempty skipwhite
syn keyword hareKeyword export static
-syn keyword hareKeyword fn nextgroup=@hareFunction skipempty skipwhite
-syn keyword hareKeyword let
-syn keyword hareTypedef type nextgroup=hareTypeIdentifier skipempty skipwhite
+syn keyword hareKeyword fn nextgroup=@hareFunction,@hareReserved skipempty skipwhite
+syn keyword hareStatement const let
+syn keyword hareTypedef type nextgroup=hareTypedefBinding,@hareReserved skipempty skipwhite
+
+# Highlight `const` as a storage-class in places types are expected.
+syn keyword hareStorageClass const contained containedin=hareTaggedUnion,hareTypeParens nextgroup=@hareType skipempty skipwhite

# Function declarations.
-syn cluster hareFunction contains=hareFunction,hareFuncParams
-syn match hareFunction ' <\h\w*%(::\h\w*)*>' contained contains=@hareIdentifier nextgroup=hareFuncParams skipempty skipwhite
-syn region hareFuncParams matchgroup=hareFuncParams start='(' end=')' contained contains=TOP nextgroup=@hareType skipempty skipwhite
+syn cluster hareFunction contains=hareFunction,hareFunctionParams
+syn match hareFunction ' <\h\w*%(::\h\w*)*>' contained contains=@hareIdentifier nextgroup=hareFunctionParams skipempty skipwhite
+syn region hareFunctionParams matchgroup=hareParen start='(' end=')' contained contains=TOP nextgroup=@hareType skipempty skipwhite transparent

# Type declarations.
-# FIXME: Does not yet account for type declarations with multiple bindings.
-syn match hareTypeIdentifier ' <\h\w*%(::\h\w*)*>' contained contains=hareIdentifier nextgroup=hareTypeEquals skipempty skipwhite transparent
-syn match hareTypeEquals '=' contained nextgroup=@hareType skipempty skipwhite transparent
-
-# Identifiers.
-syn match hareIdentifier ' <\h\w*%(::\h\w*)*>' contains=@hareIdentifier nextgroup=@harePostfix skipempty skipwhite
-syn cluster hareIdentifier contains=hareDelimiter,hareName
-syn match hareName '\<\h\w*\>' contained contains=@hareReserved transparent
+# XXX: Does not yet account for type declarations with multiple bindings.
+syn match hareTypedefBinding ' <\h\w*%(::\h\w*)*>' contained nextgroup=hareTypedefEquals skipempty skipwhite transparent
+syn match hareTypedefEquals '=' contained nextgroup=@hareType skipempty skipwhite transparent

# Attributes {{{3
syn keyword hareAttribute @init @fini @test
-syn keyword hareAttribute @offset nextgroup=hareAttrParens skipempty skipwhite
+syn keyword hareAttribute @offset nextgroup=hareAttributeParens skipempty skipwhite
syn keyword hareAttribute @packed
-syn keyword hareAttribute @symbol nextgroup=hareAttrParens skipempty skipwhite
+syn keyword hareAttribute @symbol nextgroup=hareAttributeParens skipempty skipwhite
syn keyword hareAttribute @threadlocal

-# Match the parens after attributes.
-syn region hareAttrParens matchgroup=hareAttrParens start='(' end=')' contained contains=TOP
+# Match the parens following attributes.
+syn region hareAttributeParens matchgroup=hareParen start='(' end=')' contained contains=TOP transparent

# Expressions {{{2
syn keyword hareConditional else
-syn keyword hareConditional if nextgroup=hareCondParens skipempty skipwhite
+syn keyword hareConditional if nextgroup=hareConditionParens skipempty skipwhite
syn keyword hareConditional match switch nextgroup=@hareCondition skipempty skipwhite
-syn keyword hareKeyword break continue return yield
-syn keyword hareKeyword defer
syn keyword hareLabel case nextgroup=@hareType skipempty skipwhite
syn keyword hareOperator as is nextgroup=@hareType skipempty skipwhite
syn keyword hareRepeat for nextgroup=@hareCondition skipempty skipwhite
+syn keyword hareStatement break continue return yield
+syn keyword hareStatement defer

-# Match the parens in conditionals and for-loops.
-syn cluster hareCondition contains=hareCondLabel,hareCondParens
-syn match hareCondLabel ':\h\w*\>' contained contains=hareUserLabel nextgroup=hareCondParens skipempty skipwhite transparent
-syn region hareCondParens matchgroup=hareCondParens start='(' end=')' contained contains=TOP
+# Match the parens in conditionals and loops.
+syn cluster hareCondition contains=hareConditionLabel,hareConditionParens
+syn match hareConditionLabel ':\h\w*\>' contained nextgroup=hareConditionParens skipempty skipwhite transparent
+syn region hareConditionParens matchgroup=hareParen start='(' end=')' contained contains=TOP transparent

# Builtins {{{3
-syn keyword hareBuiltin abort assert nextgroup=hareBuiltinCall skipempty skipwhite
-syn keyword hareBuiltin align nextgroup=hareBuiltinTypeCall skipempty skipwhite
-syn keyword hareBuiltin alloc free nextgroup=hareBuiltinCall skipempty skipwhite
-syn keyword hareBuiltin append insert delete nextgroup=hareBuiltinCall skipempty skipwhite
-syn keyword hareBuiltin len offset nextgroup=hareBuiltinCall skipempty skipwhite
+syn keyword hareBuiltin abort assert
+syn keyword hareBuiltin align nextgroup=hareTypeParens skipempty skipwhite
+syn keyword hareBuiltin alloc free
+syn keyword hareBuiltin append insert delete
+syn keyword hareBuiltin len offset

# C ABI.
-syn keyword hareBuiltin vastart vaarg vaend nextgroup=hareBuiltinCall skipempty skipwhite
+syn keyword hareBuiltin vastart vaarg vaend

-# Highlight `size` as a builtin only if it is followed by an open paren.
+# Highlight `size` as a type unless it is followed by an open paren.
syn match hareType '\<size\>'
-syn match hareBuiltin '\<size\ze(' nextgroup=hareBuiltinTypeCall
+syn match hareBuiltin '\<size\ze(' nextgroup=hareTypeParens

-# Match the parens in builtin expressions.
-syn region hareBuiltinCall matchgroup=hareBuiltinCall start='(' end=')' contained contains=TOP nextgroup=@harePostfix skipempty skipwhite
-syn region hareBuiltinTypeCall matchgroup=hareBuiltinTypeCall start='(' end=')' contained contains=TOP nextgroup=@harePostfix skipempty skipwhite
-
-# Operators {{{3
-syn match hareSymbolOperator '\.\{2,3}'
-syn match hareSymbolOperator '[!<=>]=\?'
-syn match hareSymbolOperator '=>'
-
-# Additive and multiplicative arithmetic.
-syn match hareSymbolOperator '[-+*/%]=\?'
-
-# Bit-shifting arithmetic.
-syn match hareSymbolOperator '\%(<<\|>>\)=\?'
-
-# Bitwise arithmetic.
-syn match hareSymbolOperator '[&^|]=\?'
-syn match hareSymbolOperator '\~'
-
-# Logical arithmetic.
-syn match hareSymbolOperator '\%(&&\|^^\|||\)=\?'
-
-# Highlight `!`, `*`, and `|` correctly in types.
-syn match hareErrorFlag '!' contained containedin=hareBuiltinTypeCall,hareTaggedUnion nextgroup=@hareType skipempty skipwhite
-syn match harePointer '*' contained containedin=hareBuiltinTypeCall,hareTaggedUnion nextgroup=@hareType skipempty skipwhite
-syn match hareTaggedUnionBar '|' contained containedin=hareTaggedUnion
+# Match the parens in builtin expressions expecting a type.
+syn region hareTypeParens matchgroup=hareParen start='(' end=')' contained contains=TOP nextgroup=@harePostfix skipempty skipwhite transparent

# Postfix expressions {{{3
# TODO: Match postfix expressions after literals.
-syn cluster harePostfix contains=hareCast,hareErrorCheck,hareFieldAccess,hareFuncCall,hareIndex
+syn cluster harePostfix contains=hareCast,hareField,hareSlice,hareSpecial

# Casts and type hints.
syn match hareCast ':' nextgroup=@hareType skipempty skipwhite

-# Error handling.
-syn match hareErrorCheck '!=\@!' contained nextgroup=@harePostfix skipempty skipwhite
-syn match hareErrorCheck '?' nextgroup=@harePostfix skipempty skipwhite
+# Error checking.
+syn match hareSpecial '!=\@!' contained nextgroup=@harePostfix skipempty skipwhite
+syn match hareSpecial '?' nextgroup=@harePostfix skipempty skipwhite

# Field access.
-syn match hareFieldAccess '\.\w\+\>' contained contains=hareName,hareNumber nextgroup=@harePostfix skipempty skipwhite
-
-# Function calls.
-syn region hareFuncCall matchgroup=hareFuncCall start='(' end=')' contained contains=TOP nextgroup=@harePostfix skipempty skipwhite
+syn match hareField '\.\w\+\>' contained contains=hareName,hareNumber,@hareReserved nextgroup=@harePostfix skipempty skipwhite transparent

# Indexing and slicing.
-syn region hareIndex matchgroup=hareIndex start='\[' end=']' contained contains=TOP nextgroup=@harePostfix skipempty skipwhite
-
-# Nested expressions.
-syn region hareParens matchgroup=hareParens start='(' end=')' contains=TOP nextgroup=@harePostfix skipempty skipwhite
+syn region hareSlice matchgroup=hareBracket start='\[' end=']' contained contains=TOP nextgroup=@harePostfix skipempty skipwhite transparent

-# Tagged union and tuple types.
-syn region hareTaggedUnion matchgroup=hareTaggedUnion start='(' end=')' contained containedin=hareBuiltinTypeCall,hareTaggedUnion contains=TOP
-
-# Literals {{{3
+# Literals {{{2
syn keyword hareBoolean true false
syn keyword hareConstant null

-# Integers.
+# Integers {{{3
syn match hareNumber ' <%(0|[1-9]%(_?\d)*)%([Ee]\+?\d+)?%([iu]%(8|16|32|64)?|z)?>'
syn match hareNumber ' <0b[01]%(_?[01])*%([iu]%(8|16|32|64)?|z)?>'
syn match hareNumber ' <0o\o%(_?\o)*%([iu]%(8|16|32|64)?|z)?>'
syn match hareNumber ' <0x\x%(_?\x)*%([iu]%(8|16|32|64)?|z)?>'

-# Floats.
+# Floats {{{3
+# XXX: Technically, the third form is not a valid floating literal according to
+# the specification, but is currently accepted by the Hare compiler and
+# used occasionally within the standard library.
syn match hareFloat ' <%(0|[1-9]%(_?\d)*)\.\d%(_?\d)*%([Ee][+-]?\d+)?%(f32|f64)?>'
syn match hareFloat ' <%(0|[1-9]%(_?\d)*)%([Ee][+-]?\d+)?%(f32|f64)>'
syn match hareFloat ' <%(0|[1-9]%(_?\d)*)[Ee]-\d+>'
syn match hareFloat ' <0x\x%(_?\x)*%(\.\x%(_?\x)*)?[Pp][+-]?\d+%(f32|f64)?>'

-# Rune and string literals.
-syn region hareRune start="'" skip="\'" end="'" contains=hareEscape
-syn region hareString start='"' skip='\"' end='"' contains=hareEscape,hareFormat
-syn region hareString start='`' end='`' contains=hareFormat
+# Rune and string literals {{{3
+syn region hareRune matchgroup=hareRuneDelimiter start="'" skip="\'" end="'" contains=hareEscape
+syn region hareString matchgroup=hareStringDelimiter start='"' skip='\"' end='"' contains=hareEscape,hareFormat
+syn region hareString matchgroup=hareStringDelimiter start='`' end='`' contains=hareFormat

# Escape sequences.
syn match hareEscape '\[0abfnrtv\'"]' contained
syn match hareEscape ' \%(x\x{2}|u\x{4}|U\x{8})' contained display

# Format sequences.
-syn match hareFormat ' \{\d*%(:%(\.?\d+|[- +=Xbefgox]|F[.2ESUs]|_%(\_.|\%([0abfnrtv\'"]|x\x{2}|u\x{4}|U\x{8})))*)?}' contained contains=hareEscape
+syn match hareFormat ' \{\d*%(:%(\.?\d+|[- +=befgoxX]|F[.2EsSU]|_%(\_[^\]|\%([0abfnrtv\'"]|x\x{2}|u\x{4}|U\x{8})))*)?}' contained contains=hareEscape
syn match hareFormat '{\d*%\d*}' contained display
syn match hareFormat '{{\|}}' contained

# Miscellaneous {{{2

# Annotations.
-syn region hareAnnotation start='#\[' end=']' contains=hareAnnotationIdentifier
-syn match hareAnnotationIdentifier ' <\h\w*%(::\h\w*)*>' contained contains=@hareIdentifier nextgroup=hareAnnotationParens skipempty skipwhite transparent
-syn region hareAnnotationParens matchgroup=hareAnnotationParens start='(' end=')' contained contains=TOP
-
-# Blocks.
-syn region hareBlock matchgroup=hareBlock start='{' end='}' contains=TOP fold nextgroup=@harePostfix skipempty skipwhite
+syn region hareAnnotation start='#\[' end=']' contains=hareAnnotationIdentifier,hareComment,hareRune,hareString
+syn match hareAnnotationIdentifier ' #\[\s*\zs\h\w*%(::\h\w*)*>' contained contains=hareName,@hareReserved nextgroup=hareAnnotationParens skipempty skipwhite
+syn region hareAnnotationParens matchgroup=hareAnnotationParen start='(' end=')' contained contains=TOP

# Comments.
-syn region hareComment start='//' end='$' contains=@hareComment keepend
-syn cluster hareComment contains=hareCommentCode,hareCommentRef,hareTodo,@Spell
-syn region hareCommentCode start=' \zs' end='$' contained contains=@NoSpell display
-syn match hareCommentRef ' \[\[\h\w*%(::\h\w*)*%(::)?]]' contained contains=@NoSpell display
+syn region hareComment excludenl start='//' end='$' contains=hareSpecialComment,hareTodo,@Spell
+syn match hareSpecialComment ' \[\[\h\w*%(::\h\w*)*%(::)?]]' contained contains=@NoSpell display
syn keyword hareTodo FIXME TODO XXX contained

-# Delimiters.
-syn match hareDelimiter '::'
+# Scope delimiters.
+syn match hareScopeDelimiter '::'

-# Labels.
-syn match hareUserLabel ':\h\w*\>' contains=hareName
+# User labels.
+syn match hareUserLabel ':\h\w*\>' contains=hareName,@hareReserved

# Default highlighting {{{1
-hi def link hareAnnotation PreProc
-hi def link hareAnnotationParens hareAnnotation
+hi def link hareAnnotation Special
+hi def link hareAnnotationIdentifier hareAnnotation
+hi def link hareAnnotationParen hareAnnotation
+hi def link hareArrayBounds harePointer
hi def link hareAttribute PreProc
hi def link hareBoolean Boolean
-hi def link hareBuiltin Operator
+hi def link hareBuiltin hareOperator
hi def link hareComment Comment
-hi def link hareCommentCode hareComment
-hi def link hareCommentRef SpecialComment
hi def link hareConditional Conditional
hi def link hareConstant Constant
hi def link hareDefine Define
-hi def link hareDelimiter Delimiter
-hi def link hareErrorFlag hareStorageClass
-hi def link hareErrorCheck Special
+hi def link hareError hareSpecial
hi def link hareEscape SpecialChar
hi def link hareFloat Float
hi def link hareFormat SpecialChar
@@ -238,24 +234,22 @@ hi def link hareOperator Operator
hi def link harePointer hareStorageClass
hi def link hareRepeat Repeat
hi def link hareRune Character
-hi def link hareSliceBounds harePointer
+hi def link hareRuneDelimiter hareRune
+hi def link hareScopeDelimiter Delimiter
+hi def link hareSpecial Special
+hi def link hareSpecialComment SpecialComment
+hi def link hareStatement Statement
hi def link hareStorageClass StorageClass
hi def link hareString String
+hi def link hareStringDelimiter hareString
hi def link hareStructure Structure
hi def link hareTodo Todo
hi def link hareType Type
hi def link hareTypedef Typedef
hi def link hareUserLabel Identifier

-# Optionally highlight symbolic operators.
-if get(g:, 'hare_symbol_operators')
- hi! def link hareSymbolOperator hareOperator
-else
- hi! def link hareSymbolOperator NONE
-endif
-
# Highlight incorrect whitespace by default.
-syn match hareSpaceError '\s\+$' containedin=ALL display
+syn match hareSpaceError excludenl '\s\+$' containedin=ALL display
syn match hareSpaceError ' \+\ze ' display
if get(g:, 'hare_space_error', 1)
hi! def link hareSpaceError Error
diff --git a/runtime/syntax/haredoc.vim b/runtime/syntax/haredoc.vim
index adf15bc3d..953962c2e 100644
--- a/runtime/syntax/haredoc.vim
+++ b/runtime/syntax/haredoc.vim
@@ -3,7 +3,7 @@ vim9script
# Vim syntax file.
# Language: Haredoc (Hare documentation format)
# Maintainer: Amelia Clarke <sel...@perilune.dev>
-# Last Change: 2025 Aug 14
+# Last Change: 2026 Jan 24
# Upstream: https://git.sr.ht/~sircmpwn/hare.vim

if exists('b:current_syntax')
@@ -15,23 +15,22 @@ syn case match
syn iskeyword @,48-57,_

# Embedded code samples.
-syn region haredocCode start=' \zs' end='$' contains=@NoSpell display
+syn region haredocCode excludenl start=' %(^\s* )@<=' end='$' contains=@NoSpell display

# References to other declarations and modules.
-syn match haredocRef ' \[\[\h\w*%(::\h\w*)*%(::)?]]' contains=@NoSpell display
+syn match haredocSpecial ' \[\[\h\w*%(::\h\w*)*%(::)?]]' contains=@NoSpell display

# Miscellaneous.
syn keyword haredocTodo FIXME TODO XXX

# Default highlighting {{{1
hi def link haredocCode Comment
-hi def link haredocRef Special
+hi def link haredocSpecial Special
hi def link haredocTodo Todo

# Highlight incorrect whitespace by default.
-syn match haredocSpaceError '\s\+$' containedin=ALL display
-syn match haredocSpaceError '^ \zs \+\ze ' containedin=ALL display
-syn match haredocSpaceError '[^ ]\zs \+\ze ' containedin=ALL display
+syn match haredocSpaceError excludenl '\s\+$' containedin=ALL display
+syn match haredocSpaceError '.\zs \+\ze ' containedin=ALL display
if get(g:, 'hare_space_error', 1)
hi! def link haredocSpaceError Error
else
Reply all
Reply to author
Forward
0 new messages