# 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 <Bram@vim.org>
# These functions are moved here from runtime/filetype.vim to make startup
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
vim9script
# Helper functions for Hare.
-# Language: Hare
-# Maintainer: Amelia Clarke <selene@perilune.dev>
-# Last Updated: 2025 Sep 06
-# Upstream: https://git.sr.ht/~sircmpwn/hare.vim
+# Language: Hare
+# Maintainer: Amelia Clarke <selene@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)
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
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 []
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
# Vim compiler file.
# Compiler: Hare
# Maintainer: Amelia Clarke <selene@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
autocmd QuickFixCmdPost lmake hare#QuickFixPaths()
augroup END
-g:current_compiler = 'hare'
-
# vim: et sts=2 sw=2 ts=8 tw=80
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
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
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)) {
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:
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*
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*
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*
vim9script
# Vim filetype plugin.
-# Language: Hare
-# Maintainer: Amelia Clarke <selene@perilune.dev>
-# Last Updated: 2025 Sep 06
-# Upstream: https://git.sr.ht/~sircmpwn/hare.vim
+# Language: Hare
+# Maintainer: Amelia Clarke <selene@perilune.dev>
+# Last Change: 2026 Jan 24
+# Upstream: https://git.sr.ht/~sircmpwn/hare.vim
if exists('b:did_ftplugin')
finish
# 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 = '\v^\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
vim9script
# Vim filetype plugin.
-# Language: Haredoc (Hare documentation format)
-# Maintainer: Amelia Clarke <selene@perilune.dev>
-# Last Updated: 2025 Sep 06
-# Upstream: https://git.sr.ht/~sircmpwn/hare.vim
+# Language: Haredoc (Hare documentation format)
+# Maintainer: Amelia Clarke <selene@perilune.dev>
+# Last Change: 2026 Jan 24
+# Upstream: https://git.sr.ht/~sircmpwn/hare.vim
if exists('b:did_ftplugin')
finish
# 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
# Vim indent file.
# Language: Hare
# Maintainer: Amelia Clarke <selene@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')
# +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.
# If the previous line started the block, use the same indent.
if pline =~ '{$'
- return pindent
+ if pline =~ '\v<%(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
# Vim syntax file.
# Language: Hare
# Maintainer: Amelia Clarke <selene@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')
syn case match
syn iskeyword @,48-57,@-@,_
+# Identifiers {{{2
+syn cluster hareIdentifier contains=hareName,hareScopeDelimiter,@hareReserved
+syn match hareIdentifier '\v<\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
# 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 '\v<\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 '\v<\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 '\v<\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 '\v<\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 '\v<\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 '\v<%(0|[1-9]%(_?\d)*)%([Ee]\+?\d+)?%([iu]%(8|16|32|64)?|z)?>'
syn match hareNumber '\v<0b[01]%(_?[01])*%([iu]%(8|16|32|64)?|z)?>'
syn match hareNumber '\v<0o\o%(_?\o)*%([iu]%(8|16|32|64)?|z)?>'
syn match hareNumber '\v<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 '\v<%(0|[1-9]%(_?\d)*)\.\d%(_?\d)*%([Ee][+-]?\d+)?%(f32|f64)?>'
syn match hareFloat '\v<%(0|[1-9]%(_?\d)*)%([Ee][+-]?\d+)?%(f32|f64)>'
syn match hareFloat '\v<%(0|[1-9]%(_?\d)*)[Ee]-\d+>'
syn match hareFloat '\v<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 '\v\\%(x\x{2}|u\x{4}|U\x{8})' contained display
# Format sequences.
-syn match hareFormat '\v\{\d*%(:%(\.?\d+|[- +=Xbefgox]|F[.2ESUs]|_%(\_.|\\%([0abfnrtv\'"]|x\x{2}|u\x{4}|U\x{8})))*)?}' contained contains=hareEscape
+syn match hareFormat '\v\{\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 '\v<\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 '\v#\[\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='\t\zs' end='$' contained contains=@NoSpell display
-syn match hareCommentRef '\v\[\[\h\w*%(::\h\w*)*%(::)?]]' contained contains=@NoSpell display
+syn region hareComment excludenl start='//' end='$' contains=hareSpecialComment,hareTodo,@Spell
+syn match hareSpecialComment '\v\[\[\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
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\t' display
if get(g:, 'hare_space_error', 1)
hi! def link hareSpaceError Error
# Vim syntax file.
# Language: Haredoc (Hare documentation format)
# Maintainer: Amelia Clarke <selene@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')
syn iskeyword @,48-57,_
# Embedded code samples.
-syn region haredocCode start='\t\zs' end='$' contains=@NoSpell display
+syn region haredocCode excludenl start='\v%(^\s*\t)@<=' end='$' contains=@NoSpell display
# References to other declarations and modules.
-syn match haredocRef '\v\[\[\h\w*%(::\h\w*)*%(::)?]]' contains=@NoSpell display
+syn match haredocSpecial '\v\[\[\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\t' containedin=ALL display
-syn match haredocSpaceError '[^ ]\zs \+\ze\t' containedin=ALL display
+syn match haredocSpaceError excludenl '\s\+$' containedin=ALL display
+syn match haredocSpaceError '.\zs \+\ze\t' containedin=ALL display
if get(g:, 'hare_space_error', 1)
hi! def link haredocSpaceError Error
else