]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
runtime(hare): update to match upstream
authorAmelia Clarke <selene@perilune.dev>
Fri, 6 Feb 2026 09:44:16 +0000 (09:44 +0000)
committerChristian Brabandt <cb@256bit.org>
Fri, 6 Feb 2026 09:44:56 +0000 (09:44 +0000)
closes: #18640

Signed-off-by: Amelia Clarke <selene@perilune.dev>
Signed-off-by: Christian Brabandt <cb@256bit.org>
runtime/autoload/dist/ft.vim
runtime/autoload/hare.vim
runtime/compiler/hare.vim
runtime/doc/ft_hare.txt
runtime/doc/tags
runtime/ftplugin/hare.vim
runtime/ftplugin/haredoc.vim
runtime/indent/hare.vim
runtime/syntax/hare.vim
runtime/syntax/haredoc.vim

index 75a4bb5e60bc5be248618c9bee919c947a61c0be..188573f6908b5d91fcca2e77692c87244a0a51f4 100644 (file)
@@ -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 <Bram@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
index 479b0f6812d86f3407c66b6b445ac2dc7418fd71..d85a05b38c68b2cd813ec459edcebf8cf27890c0 100644 (file)
@@ -1,13 +1,13 @@
 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)
@@ -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
index 88f36a9e2093bf78b2256b4f1db36cd581510fba..2b7d9345b4173b463569af0e4e4016ad67fec9ea 100644 (file)
@@ -3,25 +3,30 @@ vim9script
 # 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
@@ -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
index ce344b73d91dad73bf20d7f6d4f10a34a8de5bd7..918ff2a5bcd49d3526e5c86ffeac9328199ab8bd 100644 (file)
@@ -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:
index cafe489a77b1fd28e2f25cb2bd818cb746fef2ec..23aa27960707bd36c79c7041db8e0ecac642c7b3 100644 (file)
@@ -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*
index eca1a78817fa991b2cd358559a7eae18cd92bef2..ebd7fbdcc302543b9b6e44ad4c2f7f0767f9a948 100644 (file)
@@ -1,10 +1,10 @@
 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
@@ -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 = '\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
index ca66b066388c6c5dbd0f645d4d10d842975d4cf1..04cabfb4b5eb249224aaac9ec89a506fbd088ad3 100644 (file)
@@ -1,10 +1,10 @@
 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
@@ -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
index 84496348a1cdc11a767264cd42c827c977ebf2f3..ff38f600e8f82b82077d34f5914d6f1be60d773a 100644 (file)
@@ -3,7 +3,7 @@ vim9script
 # 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')
@@ -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 =~ '\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
index 992b7b90590a4a47ca18072d0833d8de63e8c602..44f72b6d78f03056fe0bb76e8756d1257875a958 100644 (file)
@@ -3,7 +3,7 @@ vim9script
 # 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')
@@ -14,11 +14,39 @@ endif
 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
@@ -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 '\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
@@ -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\t' display
 if get(g:, 'hare_space_error', 1)
   hi! def link hareSpaceError Error
index adf15bc3df2eada23fe6bac2e1037c2ddc4e6041..953962c2ef9b9e799e0e2ef8bc966101b006bf87 100644 (file)
@@ -3,7 +3,7 @@ vim9script
 # 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')
@@ -15,23 +15,22 @@ syn case match
 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