changeset 35266:ea0402ba92f6 v9.1.0442

patch 9.1.0442: hare runtime files outdated Commit: https://github.com/vim/vim/commit/35dfe58a540e2fb0eff953630f8e4fcbf4bc26ca Author: Amelia Clarke <selene@perilune.dev> Date: Fri May 24 08:05:00 2024 +0200 patch 9.1.0442: hare runtime files outdated Problem: hare runtime files outdated Solution: runtime(hare): update hare.vim to match upstream (Amelia Clarke) closes: #14836 Signed-off-by: Amelia Clarke <selene@perilune.dev> Signed-off-by: Christian Brabandt <cb@256bit.org>
author Christian Brabandt <cb@256bit.org>
date Fri, 24 May 2024 08:15:04 +0200
parents 43336b53d864
children 801a4e9aafab
files .github/MAINTAINERS runtime/autoload/dist/ft.vim runtime/autoload/hare.vim runtime/compiler/hare.vim runtime/doc/Make_all.mak runtime/doc/filetype.txt runtime/doc/ft_hare.txt runtime/doc/tags runtime/filetype.vim runtime/ftplugin/hare.vim runtime/ftplugin/haredoc.vim runtime/indent/hare.vim runtime/syntax/hare.vim runtime/syntax/haredoc.vim src/testdir/test_filetype.vim src/version.c
diffstat 16 files changed, 445 insertions(+), 126 deletions(-) [+]
line wrap: on
line diff
--- a/.github/MAINTAINERS
+++ b/.github/MAINTAINERS
@@ -11,6 +11,7 @@
 
 nsis/lang/russian.nsi			@RestorerZ
 runtime/autoload/freebasic.vim		@dkearns
+runtime/autoload/hare.vim		@selenebun
 runtime/autoload/haskell.vim		@alx741
 runtime/autoload/javascript.vim		@jsit
 runtime/autoload/modula2.vim		@dkearns
@@ -63,7 +64,7 @@ runtime/compiler/gjs.vim		@dkearns
 runtime/compiler/gm2.vim		@dkearns
 runtime/compiler/go.vim			@dbarnett
 runtime/compiler/haml.vim		@tpope
-runtime/compiler/hare.vim		@rsaihe
+runtime/compiler/hare.vim		@selenebun
 runtime/compiler/icon.vim		@dkearns
 runtime/compiler/javac.vim		@dkearns
 runtime/compiler/jest.vim		@dkearns
@@ -103,6 +104,7 @@ runtime/compiler/xmllint.vim		@dkearns
 runtime/compiler/xo.vim			@dkearns
 runtime/compiler/yamllint.vim		@romainl
 runtime/compiler/zsh.vim		@dkearns
+runtime/doc/ft_hare.txt			@selenebun
 runtime/doc/ps1.txt			@heaths
 runtime/ftplugin/abaqus.vim		@costerwi
 runtime/ftplugin/apache.vim		@dubgeiser 
@@ -151,7 +153,8 @@ runtime/ftplugin/gyp.vim		@ObserverOfTim
 runtime/ftplugin/go.vim			@dbarnett
 runtime/ftplugin/gprof.vim		@dpelle
 runtime/ftplugin/haml.vim		@tpope
-runtime/ftplugin/hare.vim		@rsaihe
+runtime/ftplugin/hare.vim		@selenebun
+runtime/ftplugin/haredoc.vim		@selenebun
 runtime/ftplugin/heex.vim		@cvincent
 runtime/ftplugin/hgcommit.vim		@k-takata
 runtime/ftplugin/hog.vim		@wtfbbqhax
@@ -277,7 +280,7 @@ runtime/indent/gitolite.vim		@sitaramc
 runtime/indent/go.vim			@dbarnett
 runtime/indent/gyp.vim			@ObserverOfTime
 runtime/indent/haml.vim			@tpope
-runtime/indent/hare.vim			@rsaihe
+runtime/indent/hare.vim			@selenebun
 runtime/indent/hog.vim			@wtfbbqhax
 runtime/indent/idlang.vim		@dkearns
 runtime/indent/j.vim			@glts
@@ -405,7 +408,8 @@ runtime/syntax/gprof.vim		@dpelle
 runtime/syntax/groff.vim		@jmarshall
 runtime/syntax/gyp.vim			@ObserverOfTime
 runtime/syntax/haml.vim			@tpope
-runtime/syntax/hare.vim			@rsaihe
+runtime/syntax/hare.vim			@selenebun
+runtime/syntax/haredoc.vim		@selenebun
 runtime/syntax/haskell.vim		@coot
 runtime/syntax/help_ru.vim		@RestorerZ
 runtime/syntax/hgcommit.vim		@k-takata
--- 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:		2024 Feb 18
+# Last Change:		2024 May 23
 # Former Maintainer:	Bram Moolenaar <Bram@vim.org>
 
 # These functions are moved here from runtime/filetype.vim to make startup
@@ -376,6 +376,32 @@ export def FTfs()
   endif
 enddef
 
+# Recursively search for Hare source files in a directory and any
+# subdirectories, up to a given depth.
+def IsHareModule(dir: string, depth: number): bool
+  if depth <= 0
+    return !empty(glob(dir .. '/*.ha'))
+  endif
+
+  return reduce(sort(glob(dir .. '/*', true, true),
+    (a, b) => isdirectory(a) - isdirectory(b)),
+    (acc, n) => acc
+      || n =~ '\.ha$'
+      || isdirectory(n)
+      && IsHareModule(n, depth - 1),
+    false)
+enddef
+
+# Determine if a README file exists within a Hare module and should be given the
+# Haredoc filetype.
+export def FTharedoc()
+  if exists('g:filetype_haredoc')
+    if IsHareModule('<afile>:h', get(g:, 'haredoc_search_depth', 1))
+      setf haredoc
+    endif
+  endif
+enddef
+
 # Distinguish between HTML, XHTML and Django
 export def FThtml()
   var n = 1
new file mode 100644
--- /dev/null
+++ b/runtime/autoload/hare.vim
@@ -0,0 +1,26 @@
+" Vim autoload file.
+" Language:     Hare
+" Maintainer:   Amelia Clarke <selene@perilune.dev>
+" Last Updated: 2024-05-10
+" Upstream:     https://git.sr.ht/~sircmpwn/hare.vim
+
+" Attempt to find the directory for a given Hare module.
+function hare#FindModule(str)
+  let path = substitute(trim(a:str, ':', 2), '::', '/', 'g')
+  let dir = finddir(path)
+  while !empty(path) && empty(dir)
+    let path = substitute(path, '/\?\h\w*$', '', '')
+    let dir = finddir(path)
+  endwhile
+  return dir
+endfunction
+
+" Return the value of HAREPATH if it exists. Otherwise use a reasonable default.
+function hare#GetPath()
+  if empty($HAREPATH)
+    return '/usr/src/hare/stdlib,/usr/src/hare/third-party'
+  endif
+  return substitute($HAREPATH, ':', ',', 'g')
+endfunction
+
+" vim: et sts=2 sw=2 ts=8
--- a/runtime/compiler/hare.vim
+++ b/runtime/compiler/hare.vim
@@ -1,28 +1,29 @@
-" Vim compiler file
-" Compiler: Hare Compiler
-" Maintainer: Amelia Clarke <me@rsaihe.dev>
-" Last Change: 2022-09-21
-"              2024 Apr 05 by The Vim Project (removed :CompilerSet definition)
+" Vim compiler file.
+" Compiler:    Hare
+" Maintainer:  Amelia Clarke <selene@perilune.dev>
+" Last Change: 2024-05-23
+" Upstream:    https://git.sr.ht/~sircmpwn/hare.vim
 
-if exists("g:current_compiler")
+if exists('current_compiler')
   finish
 endif
-let g:current_compiler = "hare"
+let current_compiler = 'hare'
 
 let s:cpo_save = &cpo
 set cpo&vim
 
-if filereadable("Makefile") || filereadable("makefile")
+if filereadable('Makefile') || filereadable('makefile')
   CompilerSet makeprg=make
 else
   CompilerSet makeprg=hare\ build
 endif
 
 CompilerSet errorformat=
-  \Error\ %f:%l:%c:\ %m,
-  \Syntax\ error:\ %.%#\ at\ %f:%l:%c\\,\ %m,
+  \%f:%l:%c:\ syntax\ error:\ %m,
+  \%f:%l:%c:\ error:\ %m,
   \%-G%.%#
 
 let &cpo = s:cpo_save
 unlet s:cpo_save
-" vim: tabstop=2 shiftwidth=2 expandtab
+
+" vim: et sts=2 sw=2 ts=8
--- a/runtime/doc/Make_all.mak
+++ b/runtime/doc/Make_all.mak
@@ -19,6 +19,7 @@ DOCS = \
 	fold.txt \
 	ft_ada.txt \
 	ft_context.txt \
+	ft_hare.txt \
 	ft_mp.txt \
 	ft_ps1.txt \
 	ft_raku.txt \
@@ -172,6 +173,7 @@ HTMLS = \
 	fold.html \
 	ft_ada.html \
 	ft_context.html \
+	ft_hare.html \
 	ft_mp.html \
 	ft_ps1.html \
 	ft_raku.html \
--- a/runtime/doc/filetype.txt
+++ b/runtime/doc/filetype.txt
@@ -1,4 +1,4 @@
-*filetype.txt*	For Vim version 9.1.  Last change: 2024 May 21
+*filetype.txt*	For Vim version 9.1.  Last change: 2024 May 23
 
 
 		  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -639,6 +639,12 @@ The mapping can be disabled with: >
 	let g:no_gprof_maps = 1
 
 
+HARE							*ft-hare*
+
+Since the text for this plugin is rather long it has been put in a separate
+file: |ft_hare.txt|.
+
+
 JAVA							*ft-java-plugin*
 
 Whenever the variable "g:ftplugin_java_source_path" is defined and its value
new file mode 100644
--- /dev/null
+++ b/runtime/doc/ft_hare.txt
@@ -0,0 +1,77 @@
+*ft_hare.txt*	Support for the Hare programming language
+
+==============================================================================
+CONTENTS								*hare*
+
+1. Introduction							  |hare-intro|
+2. Filetype plugin						 |hare-plugin|
+3. Settings						       |hare-settings|
+
+==============================================================================
+INTRODUCTION							  *hare-intro*
+
+This plugin provides syntax highlighting, indentation, and other functionality
+for the Hare programming language. Support is also provided for README files
+inside Hare modules, but this must be enabled by setting |g:filetype_haredoc|.
+
+==============================================================================
+FILETYPE PLUGIN							 *hare-plugin*
+
+This plugin automatically sets the value of 'path' to include the contents of
+the HAREPATH environment variable, allowing commands such as |gf| to directly
+open standard library or third-party modules. If HAREPATH is not set, it
+defaults to the recommended paths for most Unix-like filesystems, namely
+/usr/src/hare/stdlib and /usr/src/hare/third-party.
+
+==============================================================================
+SETTINGS						       *hare-settings*
+
+This plugin provides a small number of variables that you can define in your
+vimrc to configure its behavior.
+
+							  *g:filetype_haredoc*
+This plugin is able to automatically detect Hare modules and set the "haredoc"
+filetype for any README files. As the recursive directory search used as a
+heuristic has a minor performance impact, this feature is disabled by default
+and must be specifically opted into: >
+	let g:filetype_haredoc = 1
+<
+See |g:haredoc_search_depth| for ways to tweak the searching behavior.
+
+						    *g:hare_recommended_style*
+The following options are set by default, in accordance with the official Hare
+style guide: >
+	setlocal noexpandtab
+	setlocal shiftwidth=0
+	setlocal softtabstop=0
+	setlocal tabstop=8
+	setlocal textwidth=80
+<
+To disable this behavior: >
+	let g:hare_recommended_style = 0
+<
+							  *g:hare_space_error*
+By default, trailing whitespace and tabs preceded by space characters are
+highlighted as errors. This is automatically turned off when in insert mode.
+To disable this highlighting completely: >
+	let g:hare_space_error = 0
+<
+						      *g:haredoc_search_depth*
+By default, when |g:filetype_haredoc| is enabled, only the current directory
+and its immediate subdirectories are searched for Hare files. The maximum
+search depth may be adjusted with: >
+	let g:haredoc_search_depth = 2
+<
+	Value		Effect~
+	0		Only search the current directory.
+	1		Search the current directory and immediate
+			subdirectories.
+	2		Search the current directory and two levels of
+			subdirectories.
+
+The maximum search depth can be set to any integer, but using values higher
+than 2 is not recommended, and will likely provide no tangible benefit in most
+situations.
+
+==============================================================================
+ vim:tw=78:ts=8:noet:ft=help:norl:
--- a/runtime/doc/tags
+++ b/runtime/doc/tags
@@ -7285,6 +7285,7 @@ ft-gitcommit-plugin	filetype.txt	/*ft-gi
 ft-gprof-plugin	filetype.txt	/*ft-gprof-plugin*
 ft-groff-syntax	syntax.txt	/*ft-groff-syntax*
 ft-gsp-syntax	syntax.txt	/*ft-gsp-syntax*
+ft-hare	filetype.txt	/*ft-hare*
 ft-haskell-syntax	syntax.txt	/*ft-haskell-syntax*
 ft-html-indent	indent.txt	/*ft-html-indent*
 ft-html-omni	insert.txt	/*ft-html-omni*
@@ -7409,6 +7410,7 @@ ft-zimbu-plugin	filetype.txt	/*ft-zimbu-
 ft-zsh-syntax	syntax.txt	/*ft-zsh-syntax*
 ft_ada.txt	ft_ada.txt	/*ft_ada.txt*
 ft_context.txt	ft_context.txt	/*ft_context.txt*
+ft_hare.txt	ft_hare.txt	/*ft_hare.txt*
 ft_mp.txt	ft_mp.txt	/*ft_mp.txt*
 ft_ps1.txt	ft_ps1.txt	/*ft_ps1.txt*
 ft_raku.txt	ft_raku.txt	/*ft_raku.txt*
@@ -7496,6 +7498,7 @@ g:decada.Make_Command	ft_ada.txt	/*g:dec
 g:decada.Unit_Name()	ft_ada.txt	/*g:decada.Unit_Name()*
 g:do_no_lazyload_menus	gui.txt	/*g:do_no_lazyload_menus*
 g:filetype_csh	syntax.txt	/*g:filetype_csh*
+g:filetype_haredoc	ft_hare.txt	/*g:filetype_haredoc*
 g:filetype_r	syntax.txt	/*g:filetype_r*
 g:ftplugin_rust_source_path	ft_rust.txt	/*g:ftplugin_rust_source_path*
 g:gnat	ft_ada.txt	/*g:gnat*
@@ -7511,6 +7514,9 @@ g:gnat.Set_Project_File()	ft_ada.txt	/*g
 g:gnat.Tags()	ft_ada.txt	/*g:gnat.Tags()*
 g:gnat.Tags_Command	ft_ada.txt	/*g:gnat.Tags_Command*
 g:gzip_exec	pi_gzip.txt	/*g:gzip_exec*
+g:hare_recommended_style	ft_hare.txt	/*g:hare_recommended_style*
+g:hare_space_error	ft_hare.txt	/*g:hare_space_error*
+g:haredoc_search_depth	ft_hare.txt	/*g:haredoc_search_depth*
 g:html_charset_override	syntax.txt	/*g:html_charset_override*
 g:html_diff_one_file	syntax.txt	/*g:html_diff_one_file*
 g:html_dynamic_folds	syntax.txt	/*g:html_dynamic_folds*
@@ -7975,6 +7981,10 @@ haiku-user-settings-dir	os_haiku.txt	/*h
 haiku-vimdir	os_haiku.txt	/*haiku-vimdir*
 hangul	hangulin.txt	/*hangul*
 hangulin.txt	hangulin.txt	/*hangulin.txt*
+hare	ft_hare.txt	/*hare*
+hare-intro	ft_hare.txt	/*hare-intro*
+hare-plugin	ft_hare.txt	/*hare-plugin*
+hare-settings	ft_hare.txt	/*hare-settings*
 has()	builtin.txt	/*has()*
 has-patch	builtin.txt	/*has-patch*
 has-python	if_pyth.txt	/*has-python*
--- a/runtime/filetype.vim
+++ b/runtime/filetype.vim
@@ -1,7 +1,7 @@
 " Vim support file to detect file types
 "
 " Maintainer:	The Vim Project <https://github.com/vim/vim>
-" Last Change:	2023 Dec 06
+" Last Change:	2024 May 23
 " Former Maintainer:	Bram Moolenaar <Bram@vim.org>
 
 " Listen very carefully, I will say this only once
@@ -972,6 +972,7 @@ au BufNewFile,BufRead *.hbs			setf handl
 
 " Hare
 au BufNewFile,BufRead *.ha			setf hare
+au BufNewFile,BufRead README			call dist#ft#FTharedoc()
 
 " Haskell
 au BufNewFile,BufRead *.hs,*.hsc,*.hs-boot,*.hsig setf haskell
--- a/runtime/ftplugin/hare.vim
+++ b/runtime/ftplugin/hare.vim
@@ -1,35 +1,59 @@
-" Vim filetype plugin
-" Language: Hare
-" Maintainer: Amelia Clarke <me@rsaihe.dev>
-" Previous Maintainer: Drew DeVault <sir@cmpwn.com>
-" Last Updated: 2022-09-28
-"               2023 Aug 28 by Vim Project (undo_ftplugin)
+" Vim filetype plugin.
+" Language:     Hare
+" Maintainer:   Amelia Clarke <selene@perilune.dev>
+" Last Updated: 2024-05-10
+" Upstream:     https://git.sr.ht/~sircmpwn/hare.vim
 
 if exists('b:did_ftplugin')
   finish
 endif
 let b:did_ftplugin = 1
 
+let s:cpo_save = &cpo
+set cpo&vim
+
+" Set the default compiler.
+compiler hare
+
 " Formatting settings.
-setlocal formatoptions-=t formatoptions+=croql/
-
-" Miscellaneous.
 setlocal comments=://
 setlocal commentstring=//\ %s
+setlocal formatlistpat=^\ \\?-\ 
+setlocal formatoptions+=croqnlj/ formatoptions-=t
+
+" Search for Hare modules.
+setlocal include=^\\s*use\\>
+setlocal includeexpr=hare#FindModule(v:fname)
+setlocal isfname+=:
 setlocal suffixesadd=.ha
 
-let b:undo_ftplugin = "setl cms< com< fo< sua<"
+" Add HAREPATH to the default search paths.
+setlocal path-=/usr/include,,
+let &l:path .= ',' .. hare#GetPath() .. ',,'
 
-" Hare recommended style.
-if get(g:, "hare_recommended_style", 1)
+let b:undo_ftplugin = 'setl cms< com< flp< fo< inc< inex< isf< pa< sua< mp<'
+
+" Follow the Hare style guide by default.
+if get(g:, 'hare_recommended_style', 1)
   setlocal noexpandtab
-  setlocal shiftwidth=8
+  setlocal shiftwidth=0
   setlocal softtabstop=0
   setlocal tabstop=8
   setlocal textwidth=80
-  let b:undo_ftplugin ..= " | setl et< sts< sw< ts< tw<"
+  let b:undo_ftplugin .= ' et< sts< sw< ts< tw<'
 endif
 
-compiler hare
+augroup hare.vim
+  autocmd!
 
-" vim: et sw=2 sts=2 ts=8
+  " Highlight whitespace errors by default.
+  if get(g:, 'hare_space_error', 1)
+    autocmd InsertEnter * hi link hareSpaceError NONE
+    autocmd InsertLeave * hi link hareSpaceError Error
+  endif
+augroup END
+
+let &cpo = s:cpo_save
+unlet s:cpo_save
+
+" vim: et sts=2 sw=2 ts=8
new file mode 100644
--- /dev/null
+++ b/runtime/ftplugin/haredoc.vim
@@ -0,0 +1,44 @@
+" Vim filetype plugin.
+" Language:     Haredoc (Hare documentation format)
+" Maintainer:   Amelia Clarke <selene@perilune.dev>
+" Last Updated: 2024-05-02
+" Upstream:     https://git.sr.ht/~selene/hare.vim
+
+if exists('b:did_ftplugin')
+  finish
+endif
+let b:did_ftplugin = 1
+
+let s:cpo_save = &cpo
+set cpo&vim
+
+" Formatting settings.
+setlocal comments=:\	
+setlocal formatlistpat=^\ \\?-\ 
+setlocal formatoptions+=tnlj formatoptions-=c formatoptions-=q
+
+" Search for Hare modules.
+setlocal includeexpr=hare#FindModule(v:fname)
+setlocal isfname+=:
+setlocal suffixesadd=.ha
+
+" Add HAREPATH to the default search paths.
+setlocal path-=/usr/include,,
+let &l:path .= ',' .. hare#GetPath() .. ',,'
+
+let b:undo_ftplugin = 'setl com< flp< fo< inex< isf< pa< sua<'
+
+" Follow the Hare style guide by default.
+if get(g:, 'hare_recommended_style', 1)
+  setlocal noexpandtab
+  setlocal shiftwidth=0
+  setlocal softtabstop=0
+  setlocal tabstop=8
+  setlocal textwidth=80
+  let b:undo_ftplugin .= ' et< sts< sw< ts< tw<'
+endif
+
+let &cpo = s:cpo_save
+unlet s:cpo_save
+
+" vim: et sts=2 sw=2 ts=8
--- a/runtime/indent/hare.vim
+++ b/runtime/indent/hare.vim
@@ -1,19 +1,16 @@
 " Vim indent file
-" Language: Hare
-" Maintainer: Amelia Clarke <me@rsaihe.dev>
-" Last Change: 2022 Sep 22
-"              2023 Aug 28 by Vim Project (undo_indent)
+" Language:    Hare
+" Maintainer:  Amelia Clarke <selene@perilune.dev>
+" Last Change: 2024-04-14
+" Upstream:    https://git.sr.ht/~sircmpwn/hare.vim
 
-if exists("b:did_indent")
+if exists('b:did_indent')
   finish
 endif
 let b:did_indent = 1
 
-if !has("cindent") || !has("eval")
-  finish
-endif
-
-setlocal cindent
+let s:cpo_save = &cpo
+set cpo&vim
 
 " L0 -> don't deindent labels
 " (s -> use one indent after a trailing (
@@ -41,7 +38,11 @@ setlocal cinwords=if,else,for,switch,mat
 
 setlocal indentexpr=GetHareIndent()
 
-let b:undo_indent = "setl cin< cino< cinw< inde< indk<"
+let b:undo_indent = 'setl cino< cinw< inde< indk<'
+
+if exists('*GetHareIndent()')
+  finish
+endif
 
 function! FloorCindent(lnum)
   return cindent(a:lnum) / shiftwidth() * shiftwidth()
@@ -122,7 +123,8 @@ function! GetHareIndent()
   " Indent the body of a case.
   " If the previous line ended in a semicolon and the line before that was a
   " case, don't do any special indenting.
-  if prevline =~# '\v;\s*(//.*)?$' && prevprevline =~# '\v\=\>\s*(//.*)?$' && line !~# '\v^\s*}'
+  if prevline =~# '\v;\s*(//.*)?$' && prevprevline =~# '\v\=\>\s*(//.*)?$'
+        \ && line !~# '\v^\s*}'
     return indent(prevlnum)
   endif
 
@@ -138,4 +140,7 @@ function! GetHareIndent()
   return l:indent
 endfunction
 
-" vim: tabstop=2 shiftwidth=2 expandtab
+let &cpo = s:cpo_save
+unlet s:cpo_save
+
+" vim: et sw=2 sts=2 ts=8
--- a/runtime/syntax/hare.vim
+++ b/runtime/syntax/hare.vim
@@ -1,119 +1,142 @@
-" PRELUDE {{{1
-" Vim syntax file
-" Language: Hare
-" Maintainer: Amelia Clarke <me@rsaihe.dev>
-" Last Change: 2022-09-21
+" Vim syntax file.
+" Language:    Hare
+" Maintainer:  Amelia Clarke <selene@perilune.dev>
+" Last Change: 2024-05-10
+" Upstream:    https://git.sr.ht/~sircmpwn/hare.vim
 
-if exists("b:current_syntax")
+if exists('b:current_syntax')
   finish
 endif
-let b:current_syntax = "hare"
+syn include @haredoc syntax/haredoc.vim
+let b:current_syntax = 'hare'
 
-" SYNTAX {{{1
+" Syntax {{{1
 syn case match
+syn iskeyword @,48-57,@-@,_
 
-" KEYWORDS {{{2
-syn keyword hareConditional if else match switch
+" Keywords {{{2
+syn keyword hareConditional else if match switch
+syn keyword hareDefine def
+syn keyword hareInclude use
 syn keyword hareKeyword break continue return yield
+syn keyword hareKeyword case
+syn keyword hareKeyword const let
 syn keyword hareKeyword defer
+syn keyword hareKeyword export static
 syn keyword hareKeyword fn
-syn keyword hareKeyword let
-syn keyword hareLabel case
 syn keyword hareOperator as is
 syn keyword hareRepeat for
-syn keyword hareStorageClass const def export nullable static
-syn keyword hareStructure enum struct union
 syn keyword hareTypedef type
 
-" C ABI.
-syn keyword hareKeyword vastart vaarg vaend
+" Attributes.
+syn keyword hareAttribute @fini @init @test
+syn keyword hareAttribute @offset @packed
+syn keyword hareAttribute @symbol
+syn keyword hareAttribute @threadlocal
 
-" BUILTINS {{{2
-syn keyword hareBuiltin abort
+" Builtins.
+syn keyword hareBuiltin abort assert
+syn keyword hareBuiltin align len offset
 syn keyword hareBuiltin alloc free
 syn keyword hareBuiltin append delete insert
-syn keyword hareBuiltin assert
-syn keyword hareBuiltin len offset
+syn keyword hareBuiltin vaarg vaend vastart
 
-" TYPES {{{2
+" Types {{{2
 syn keyword hareType bool
-syn keyword hareType char str
+syn keyword hareType done
 syn keyword hareType f32 f64
-syn keyword hareType u8 u16 u32 u64 i8 i16 i32 i64
-syn keyword hareType uint int
-syn keyword hareType rune
+syn keyword hareType i8 i16 i32 i64 int
+syn keyword hareType never
+syn keyword hareType opaque
+syn keyword hareType rune str
+syn keyword hareType u8 u16 u32 u64 uint
 syn keyword hareType uintptr
+syn keyword hareType valist
 syn keyword hareType void
 
-" C ABI.
-syn keyword hareType valist
+" Other types.
+syn keyword hareStorageClass nullable
+syn keyword hareStructure enum struct union
+
+" Literals {{{2
+syn keyword hareBoolean false true
+syn keyword hareConstant null
 
-" LITERALS {{{2
-syn keyword hareBoolean true false
-syn keyword hareNull null
+" Integer literals.
+syn match hareNumber '\v<%(0|[1-9]%(_?\d)*)%([Ee]\+?\d+)?%([iu]%(8|16|32|64)?|z)?>' display
+syn match hareNumber '\v<0b[01]%(_?[01])*%([iu]%(8|16|32|64)?|z)?>' display
+syn match hareNumber '\v<0o\o%(_?\o)*%([iu]%(8|16|32|64)?|z)?>' display
+syn match hareNumber '\v<0x\x%(_?\x)*%([iu]%(8|16|32|64)?|z)?>' display
 
-" Number literals.
-syn match hareNumber "\v(\.@1<!|\.\.)\zs<\d+([Ee][+-]?\d+)?(z|[iu](8|16|32|64)?)?>" display
-syn match hareNumber "\v(\.@1<!|\.\.)\zs<0b[01]+(z|[iu](8|16|32|64)?)?>" display
-syn match hareNumber "\v(\.@1<!|\.\.)\zs<0o\o+(z|[iu](8|16|32|64)?)?>" display
-syn match hareNumber "\v(\.@1<!|\.\.)\zs<0x\x+(z|[iu](8|16|32|64)?)?>" display
+" Floating-point literals.
+syn match hareFloat '\v<%(0|[1-9]%(_?\d)*)\.\d%(_?\d)*%([Ee][+-]?\d+)?%(f32|f64)?>' display
+syn match hareFloat '\v<%(0|[1-9]%(_?\d)*)%([Ee][+-]?\d+)?%(f32|f64)>' display
+syn match hareFloat '\v<0x\x%(_?\x)*%(\.\x%(_?\x)*)?[Pp][+-]?\d+%(f32|f64)?>' display
 
-" Floating-point number literals.
-syn match hareFloat "\v<\d+\.\d+([Ee][+-]?\d+)?(f32|f64)?>" display
-syn match hareFloat "\v<\d+([Ee][+-]?\d+)?(f32|f64)>" display
+" 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
+
+" Escape sequences.
+syn match hareEscape '\\[0abfnrtv\\'"]' contained
+syn match hareEscape '\v\\%(x\x{2}|u\x{4}|U\x{8})' contained display
 
-" String and rune literals.
-syn match hareEscape "\\[\\'"0abfnrtv]" contained display
-syn match hareEscape "\v\\(x\x{2}|u\x{4}|U\x{8})" contained display
-syn match hareFormat "\v\{\d*(\%\d*|(:[ 0+-]?\d*(\.\d+)?[Xbox]?))?}" contained display
-syn match hareFormat "\({{\|}}\)" contained display
-syn region hareRune start="'" end="'\|$" skip="\\'" contains=hareEscape display extend
-syn region hareString start=+"+ end=+"\|$+ skip=+\\"+ contains=hareEscape,hareFormat display extend
-syn region hareString start="`" end="`\|$" contains=hareFormat 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 display
+syn match hareFormat '{\d*%\d*}' contained display
+syn match hareFormat '{{\|}}' contained display
 
-" MISCELLANEOUS {{{2
+" Miscellaneous {{{2
+
+" Comments.
+syn region hareComment start='//' end='$' contains=hareTodo,@haredoc,@Spell display
 syn keyword hareTodo FIXME TODO XXX contained
 
-" Attributes.
-syn match hareAttribute "@[a-z]*"
-
-" Blocks.
-syn region hareBlock start="{" end="}" fold transparent
+" Identifiers.
+syn match hareDelimiter '::' display
+syn match hareName '\<\h\w*\>' nextgroup=@harePostfix skipempty skipwhite transparent
 
-" Comments.
-syn region hareComment start="//" end="$" contains=hareCommentDoc,hareTodo,@Spell display keepend
-syn region hareCommentDoc start="\[\[" end="]]\|\ze\_s" contained display
+" Labels.
+syn match hareLabel ':\h\w*\>' display
 
-" The size keyword can be either a builtin or a type.
-syn match hareBuiltin "\v<size>\ze(\_s*//.*\_$)*\_s*\(" contains=hareComment
-syn match hareType "\v<size>((\_s*//.*\_$)*\_s*\()@!" contains=hareComment
+" Match `size` as a type unless it is followed by an open paren.
+syn match hareType '\<size\>' display
+syn match hareBuiltin '\<size\ze(' display
 
-" Trailing whitespace.
-syn match hareSpaceError "\v\s+$" display excludenl
-syn match hareSpaceError "\v\zs +\ze\t" display
+" Postfix expressions.
+syn cluster harePostfix contains=hareErrorTest,hareField,hareIndex,hareParens
+syn match hareErrorTest '!=\@!' contained nextgroup=@harePostfix skipempty skipwhite
+syn match hareErrorTest '?' nextgroup=@harePostfix skipempty skipwhite
+syn match hareField '\.\w*\>'hs=s+1 contained contains=hareNumber nextgroup=@harePostfix skipempty skipwhite
+syn region hareIndex start='\[' end=']' contained nextgroup=@harePostfix skipempty skipwhite transparent
+syn region hareParens start='(' end=')' nextgroup=@harePostfix skipempty skipwhite transparent
 
-" Use statement.
-syn region hareUse start="\v^\s*\zsuse>" end=";" contains=hareComment display
+" Whitespace errors.
+syn match hareSpaceError '^ \+\ze\t' display
+syn match hareSpaceError excludenl '\s\+$' containedin=ALL display
 
-syn match hareErrorAssertion "\v(^([^/]|//@!)*\)\_s*)@<=!\=@!"
-syn match hareQuestionMark "?"
+" Folding {{{3
+syn region hareBlock start='{' end='}' fold transparent
 
-" DEFAULT HIGHLIGHTING {{{1
-hi def link hareAttribute Keyword
+" Default highlighting {{{1
+hi def link hareAttribute PreProc
 hi def link hareBoolean Boolean
-hi def link hareBuiltin Function
+hi def link hareBuiltin Operator
 hi def link hareComment Comment
-hi def link hareCommentDoc SpecialComment
 hi def link hareConditional Conditional
+hi def link hareConstant Constant
+hi def link hareDefine Define
+hi def link hareDelimiter Delimiter
+hi def link hareErrorTest Special
 hi def link hareEscape SpecialChar
 hi def link hareFloat Float
 hi def link hareFormat SpecialChar
+hi def link hareInclude Include
 hi def link hareKeyword Keyword
-hi def link hareLabel Label
-hi def link hareNull Constant
+hi def link hareLabel Special
 hi def link hareNumber Number
 hi def link hareOperator Operator
-hi def link hareQuestionMark Special
 hi def link hareRepeat Repeat
 hi def link hareRune Character
 hi def link hareStorageClass StorageClass
@@ -122,12 +145,13 @@ hi def link hareStructure Structure
 hi def link hareTodo Todo
 hi def link hareType Type
 hi def link hareTypedef Typedef
-hi def link hareUse PreProc
+
+" Highlight embedded haredoc references.
+hi! def link haredocRefValid SpecialComment
 
-hi def link hareSpaceError Error
-autocmd InsertEnter * hi link hareSpaceError NONE
-autocmd InsertLeave * hi link hareSpaceError Error
+" Highlight whitespace errors by default.
+if get(g:, 'hare_space_error', 1)
+  hi def link hareSpaceError Error
+endif
 
-hi def hareErrorAssertion ctermfg=red cterm=bold guifg=red gui=bold
-
-" vim: tabstop=8 shiftwidth=2 expandtab
+" vim: et sts=2 sw=2 ts=8
new file mode 100644
--- /dev/null
+++ b/runtime/syntax/haredoc.vim
@@ -0,0 +1,32 @@
+" Vim syntax file.
+" Language:    Haredoc (Hare documentation format)
+" Maintainer:  Amelia Clarke <selene@perilune.dev>
+" Last Change: 2024-05-10
+" Upstream:    https://git.sr.ht/~selene/hare.vim
+
+if exists('b:current_syntax')
+  finish
+endif
+let b:current_syntax = 'haredoc'
+
+" Syntax {{{1
+syn case match
+syn iskeyword @,48-57,_
+
+" Code samples.
+syn region haredocCodeSample excludenl start='\t\zs' end='$' contains=@NoSpell display
+
+" References to other declarations and modules.
+syn region haredocRef start='\[\[' end=']]' contains=haredocRefValid,@NoSpell display keepend oneline
+syn match haredocRefValid '\v\[\[\h\w*%(::\h\w*)*%(::)?]]' contained contains=@NoSpell display
+
+" Miscellaneous.
+syn keyword haredocTodo FIXME TODO XXX
+
+" Default highlighting {{{1
+hi def link haredocCodeSample Comment
+hi def link haredocRef Error
+hi def link haredocRefValid Special
+hi def link haredocTodo Todo
+
+" vim: et sts=2 sw=2 ts=8
--- a/src/testdir/test_filetype.vim
+++ b/src/testdir/test_filetype.vim
@@ -1513,6 +1513,41 @@ func Test_git_file()
   filetype off
 endfunc
 
+func Test_haredoc_file()
+  filetype on
+  call assert_true(mkdir('foo/bar', 'pR'))
+
+  call writefile([], 'README', 'D')
+  split README
+  call assert_notequal('haredoc', &filetype)
+  bwipe!
+
+  let g:filetype_haredoc = 1
+  split README
+  call assert_notequal('haredoc', &filetype)
+  bwipe!
+
+  call writefile([], 'foo/quux.ha')
+  split README
+  call assert_equal('haredoc', &filetype)
+  bwipe!
+  call delete('foo/quux.ha')
+
+  call writefile([], 'foo/bar/baz.ha', 'D')
+  split README
+  call assert_notequal('haredoc', &filetype)
+  bwipe!
+
+  let g:haredoc_search_depth = 2
+  split README
+  call assert_equal('haredoc', &filetype)
+  bwipe!
+  unlet g:filetype_haredoc
+  unlet g:haredoc_search_depth
+
+  filetype off
+endfunc
+
 func Test_hook_file()
   filetype on
 
--- a/src/version.c
+++ b/src/version.c
@@ -705,6 +705,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    442,
+/**/
     441,
 /**/
     440,