changeset 30547:1e91e26ceebf

Update runtime files Commit: https://github.com/vim/vim/commit/9fbdbb814f4ad67a14979aba4a6a49800c2f1a99 Author: Bram Moolenaar <Bram@vim.org> Date: Tue Sep 27 17:30:34 2022 +0100 Update runtime files
author Bram Moolenaar <Bram@vim.org>
date Tue, 27 Sep 2022 18:45:05 +0200
parents 3107cc41afc2
children 84ea6d876d43
files .github/CODEOWNERS runtime/autoload/context.vim runtime/autoload/dist/vimindent.vim runtime/compiler/hare.vim runtime/doc/cmdline.txt runtime/doc/develop.txt runtime/doc/ft_context.txt runtime/doc/indent.txt runtime/doc/map.txt runtime/doc/motion.txt runtime/doc/options.txt runtime/doc/pattern.txt runtime/doc/quickfix.txt runtime/doc/repeat.txt runtime/doc/syntax.txt runtime/doc/tags runtime/doc/textprop.txt runtime/doc/todo.txt runtime/doc/various.txt runtime/doc/vim9.txt runtime/filetype.vim runtime/ftplugin/chatito.vim runtime/ftplugin/elixir.vim runtime/ftplugin/gyp.vim runtime/ftplugin/hare.vim runtime/ftplugin/heex.vim runtime/ftplugin/vim.vim runtime/indent/chatito.vim runtime/indent/gyp.vim runtime/indent/hare.vim runtime/indent/solidity.vim runtime/indent/testdir/vim.in runtime/indent/testdir/vim.ok runtime/indent/vim.vim runtime/indent/vue.vim runtime/syntax/chatito.vim runtime/syntax/desktop.vim runtime/syntax/gyp.vim runtime/syntax/hare.vim runtime/syntax/help.vim runtime/syntax/hlsplaylist.vim runtime/syntax/plsql.vim runtime/syntax/solidity.vim runtime/syntax/vim.vim src/INSTALLpc.txt
diffstat 45 files changed, 4223 insertions(+), 379 deletions(-) [+]
line wrap: on
line diff
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -57,6 +57,7 @@ runtime/compiler/gawk.vim		@dkearns
 runtime/compiler/gjs.vim		@dkearns
 runtime/compiler/go.vim			@dbarnett
 runtime/compiler/haml.vim		@tpope
+runtime/compiler/hare.vim		@rsaihe
 runtime/compiler/icon.vim		@dkearns
 runtime/compiler/javac.vim		@dkearns
 runtime/compiler/jest.vim		@dkearns
@@ -107,6 +108,7 @@ runtime/ftplugin/awk.vim		@dkearns
 runtime/ftplugin/basic.vim		@dkearns
 runtime/ftplugin/bst.vim		@tpope
 runtime/ftplugin/cfg.vim		@chrisbra
+runtime/ftplugin/chatito.vim		@ObserverOfTime
 runtime/ftplugin/clojure.vim		@axvr
 runtime/ftplugin/cs.vim			@nickspoons
 runtime/ftplugin/csh.vim		@dkearns
@@ -116,9 +118,9 @@ runtime/ftplugin/desktop.vim		@e-kwsm
 runtime/ftplugin/dosbatch.vim		@mrdubya
 runtime/ftplugin/eiffel.vim		@dkearns
 runtime/ftplugin/elixir.vim		@mhanberg
-runtime/ftplugin/expect.vim		@dkearns
 runtime/ftplugin/erlang.vim		@hcs42
 runtime/ftplugin/eruby.vim		@tpope @dkearns
+runtime/ftplugin/expect.vim		@dkearns
 runtime/ftplugin/fennel.vim		@gpanders
 runtime/ftplugin/fetchmail.vim		@dkearns
 runtime/ftplugin/fpcmake.vim		@dkearns
@@ -134,9 +136,11 @@ runtime/ftplugin/gitconfig.vim		@tpope
 runtime/ftplugin/gitignore.vim		@ObserverOfTime
 runtime/ftplugin/gitrebase.vim		@tpope
 runtime/ftplugin/gitsendemail.vim	@tpope
+runtime/ftplugin/gyp.vim		@ObserverOfTime
 runtime/ftplugin/go.vim			@dbarnett
 runtime/ftplugin/gprof.vim		@dpelle
 runtime/ftplugin/haml.vim		@tpope
+runtime/ftplugin/hare.vim		@rsaihe
 runtime/ftplugin/hgcommit.vim		@k-takata
 runtime/ftplugin/html.vim		@dkearns
 runtime/ftplugin/i3config.vim		@hiqua
@@ -208,6 +212,7 @@ runtime/ftplugin/zsh.vim		@chrisbra
 runtime/indent/basic.vim		@dkearns
 runtime/indent/bst.vim			@tpope
 runtime/indent/cdl.vim			@dkearns
+runtime/indent/chatito.vim		@ObserverOfTime
 runtime/indent/clojure.vim		@axvr
 runtime/indent/config.vim		@dkearns
 runtime/indent/cs.vim			@nickspoons
@@ -229,7 +234,9 @@ runtime/indent/gdscript.vim		@habamax
 runtime/indent/gitconfig.vim		@tpope
 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/idlang.vim		@dkearns
 runtime/indent/j.vim			@glts
 runtime/indent/java.vim			@xuhdev
@@ -303,6 +310,7 @@ runtime/syntax/cabal.vim		@coot
 runtime/syntax/cabalconfig.vim		@coot
 runtime/syntax/cabalproject.vim		@coot
 runtime/syntax/cf.vim			@ernstvanderlinden
+runtime/syntax/chatito.vim		@ObserverOfTime
 runtime/syntax/clojure.vim		@axvr
 runtime/syntax/cs.vim			@nickspoons
 runtime/syntax/csh.vim			@cecamp
@@ -345,7 +353,9 @@ runtime/syntax/go.vim			@bhcleek
 runtime/syntax/godoc.vim		@dbarnett
 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/haskell.vim		@coot
 runtime/syntax/hgcommit.vim		@k-takata
 runtime/syntax/html.vim			@dkearns
--- a/runtime/autoload/context.vim
+++ b/runtime/autoload/context.vim
@@ -3,13 +3,18 @@ vim9script
 # Language:           ConTeXt typesetting engine
 # Maintainer:         Nicola Vitacolonna <nvitacolonna@gmail.com>
 # Former Maintainers: Nikolai Weibull <now@bitwi.se>
-# Latest Revision:    2022 Aug 12
+# Latest Revision:    2022 Sep 19
 
 # Typesetting {{{
 import autoload './typeset.vim'
 
 export def ConTeXtCmd(path: string): list<string>
-  return ['mtxrun', '--script', 'context', '--nonstopmode', '--autogenerate', path]
+  var cmd = ['mtxrun', '--script', 'context', '--nonstopmode', '--autogenerate']
+  if !empty(get(g:, 'context_extra_options', ''))
+    cmd += g:context_extra_options
+  endif
+  cmd->add(path)
+  return cmd
 enddef
 
 export def Typeset(bufname: string, env = {}, Cmd = ConTeXtCmd): bool
new file mode 100644
--- /dev/null
+++ b/runtime/autoload/dist/vimindent.vim
@@ -0,0 +1,1200 @@
+vim9script
+
+# Language:     Vim script
+# Maintainer:   github user lacygoill
+# Last Change:  2022 Sep 24
+
+# Config {{{1
+
+const TIMEOUT: number = get(g:, 'vim_indent', {})
+    ->get('searchpair_timeout', 100)
+
+def IndentMoreInBracketBlock(): number # {{{2
+    if get(g:, 'vim_indent', {})
+            ->get('more_in_bracket_block', false)
+        return shiftwidth()
+    else
+        return 0
+    endif
+enddef
+
+def IndentMoreLineContinuation(): number # {{{2
+    var n: any = get(g:, 'vim_indent', {})
+        # We inspect `g:vim_indent_cont` to stay backward compatible.
+        ->get('line_continuation', get(g:, 'vim_indent_cont', shiftwidth() * 3))
+
+    if n->typename() == 'string'
+        return n->eval()
+    else
+        return n
+    endif
+enddef
+# }}}2
+
+# Init {{{1
+var patterns: list<string>
+# Tokens {{{2
+# BAR_SEPARATION {{{3
+
+const BAR_SEPARATION: string = '[^|\\]\@1<=|'
+
+# OPENING_BRACKET {{{3
+
+const OPENING_BRACKET: string = '[[{(]'
+
+# CLOSING_BRACKET {{{3
+
+const CLOSING_BRACKET: string = '[]})]'
+
+# NON_BRACKET {{{3
+
+const NON_BRACKET: string = '[^[\]{}()]'
+
+# LIST_OR_DICT_CLOSING_BRACKET {{{3
+
+const LIST_OR_DICT_CLOSING_BRACKET: string = '[]}]'
+
+# LIST_OR_DICT_OPENING_BRACKET {{{3
+
+const LIST_OR_DICT_OPENING_BRACKET: string = '[[{]'
+
+# CHARACTER_UNDER_CURSOR {{{3
+
+const CHARACTER_UNDER_CURSOR: string = '\%.c.'
+
+# INLINE_COMMENT {{{3
+
+# TODO: It is not required for an inline comment to be surrounded by whitespace.
+# But it might help against false positives.
+# To be more reliable, we should inspect the syntax, and only require whitespace
+# before  the `#`  comment leader.   But that  might be  too costly  (because of
+# `synstack()`).
+const INLINE_COMMENT: string = '\s[#"]\%(\s\|[{}]\{3}\)'
+
+# INLINE_VIM9_COMMENT {{{3
+
+const INLINE_VIM9_COMMENT: string = '\s#'
+
+# COMMENT {{{3
+
+# TODO: Technically, `"\s` is wrong.
+#
+# First, whitespace is not required.
+# Second, in Vim9, a string might appear at the start of the line.
+# To be sure, we should also inspect the syntax.
+# We can't use `INLINE_COMMENT` here. {{{
+#
+#     const COMMENT: string = $'^\s*{INLINE_COMMENT}'
+#                                    ^------------^
+#                                          ✘
+#
+# Because  `INLINE_COMMENT` asserts  the  presence of  a  whitespace before  the
+# comment leader.  This assertion is not satisfied for a comment starting at the
+# start of the line.
+#}}}
+const COMMENT: string = '^\s*\%(#\|"\\\=\s\).*$'
+
+# DICT_KEY {{{3
+
+const DICT_KEY: string = '^\s*\%('
+    .. '\%(\w\|-\)\+'
+    .. '\|'
+    .. '"[^"]*"'
+    .. '\|'
+    .. "'[^']*'"
+    .. '\|'
+    .. '\[[^]]\+\]'
+    .. '\)'
+    .. ':\%(\s\|$\)'
+
+# END_OF_COMMAND {{{3
+
+const END_OF_COMMAND: string = $'\s*\%($\|||\@!\|{INLINE_COMMENT}\)'
+
+# END_OF_LINE {{{3
+
+const END_OF_LINE: string = $'\s*\%($\|{INLINE_COMMENT}\)'
+
+# END_OF_VIM9_LINE {{{3
+
+const END_OF_VIM9_LINE: string = $'\s*\%($\|{INLINE_VIM9_COMMENT}\)'
+
+# OPERATOR {{{3
+
+const OPERATOR: string = '\%(^\|\s\)\%([-+*/%]\|\.\.\|||\|&&\|??\|?\|<<\|>>\|\%([=!]=\|[<>]=\=\|[=!]\~\|is\|isnot\)[?#]\=\)\%(\s\|$\)\@=\%(\s*[|<]\)\@!'
+    # assignment operators
+    .. '\|' .. '\s\%([-+*/%]\|\.\.\)\==\%(\s\|$\)\@='
+    # support `:` when used inside conditional operator `?:`
+    .. '\|' .. '\%(\s\|^\):\%(\s\|$\)'
+
+# HEREDOC_OPERATOR {{{3
+
+const HEREDOC_OPERATOR: string = '\s=<<\s\@=\%(\s\+\%(trim\|eval\)\)\{,2}'
+
+# PATTERN_DELIMITER {{{3
+
+# A better regex would be:
+#
+#     [^-+*/%.:# \t[:alnum:]\"|]\@=.\|->\@!\%(=\s\)\@!\|[+*/%]\%(=\s\)\@!
+#
+# But sometimes, it can be too costly and cause `E363` to be given.
+const PATTERN_DELIMITER: string = '[-+*/%]\%(=\s\)\@!'
+
+# QUOTE {{{3
+
+const QUOTE: string = '["'']'
+# }}}2
+# Syntaxes {{{2
+# ASSIGNS_HEREDOC {{{3
+
+const ASSIGNS_HEREDOC: string = $'^\%({COMMENT}\)\@!.*\%({HEREDOC_OPERATOR}\)\s\+\zs[A-Z]\+{END_OF_LINE}'
+
+# CD_COMMAND {{{3
+
+const CD_COMMAND: string = $'[lt]\=cd!\=\s\+-{END_OF_COMMAND}'
+
+# HIGHER_ORDER_COMMAND {{{3
+
+patterns =<< trim eval END
+    argdo\>!\=
+    bufdo\>!\=
+    cdo\>!\=
+    folddoc\%[losed]\>
+    foldd\%[oopen]\>
+    ldo\=\>!\=
+    tabdo\=\>
+    windo\>
+    au\%[tocmd]\>.*
+    com\%[mand]\>.*
+    g\%[lobal]!\={PATTERN_DELIMITER}.*
+    v\%[global]!\={PATTERN_DELIMITER}.*
+END
+const HIGHER_ORDER_COMMAND: string = $'\%(^\|{BAR_SEPARATION}\)\s*\<\%(' .. patterns->join('\|') .. '\):\@!'
+
+# MAPPING_COMMAND {{{3
+
+const MAPPING_COMMAND: string = '\%(\<sil\%[ent]!\=\s\+\)\=[nvxsoilct]\=\%(nore\|un\)map!\=\s'
+
+# NORMAL_COMMAND {{{3
+
+const NORMAL_COMMAND: string = '\<norm\%[al]!\=\s*\S\+$'
+
+# PLUS_MINUS_COMMAND {{{3
+
+# In legacy, the `:+` and `:-` commands are not required to be preceded by a colon.
+# As a result, when `+` or `-` is alone on a line, there is ambiguity.
+# It might be an operator or a command.
+# To not break the indentation in legacy scripts, we might need to consider such
+# lines as commands.
+const PLUS_MINUS_COMMAND: string = '^\s*[+-]\s*$'
+
+# ENDS_BLOCK {{{3
+
+const ENDS_BLOCK: string = '^\s*\%('
+    .. 'en\%[dif]'
+    .. '\|' .. 'endfor\='
+    .. '\|' .. 'endw\%[hile]'
+    .. '\|' .. 'endt\%[ry]'
+    .. '\|' .. 'enddef'
+    .. '\|' .. 'endf\%[unction]'
+    .. '\|' .. 'aug\%[roup]\s\+[eE][nN][dD]'
+    .. '\|' .. CLOSING_BRACKET
+    .. $'\){END_OF_COMMAND}'
+
+# ENDS_BLOCK_OR_CLAUSE {{{3
+
+patterns =<< trim END
+    en\%[dif]
+    el\%[se]
+    endfor\=
+    endw\%[hile]
+    endt\%[ry]
+    fina\|finally\=
+    enddef
+    endf\%[unction]
+    aug\%[roup]\s\+[eE][nN][dD]
+END
+
+const ENDS_BLOCK_OR_CLAUSE: string = '^\s*\%(' .. patterns->join('\|') .. $'\){END_OF_COMMAND}'
+    .. $'\|^\s*cat\%[ch]\%(\s\+\({PATTERN_DELIMITER}\).*\1\)\={END_OF_COMMAND}'
+    .. $'\|^\s*elseif\=\s\+\%({OPERATOR}\)\@!'
+
+# STARTS_CURLY_BLOCK {{{3
+
+# TODO: `{` alone on a line is not necessarily the start of a block.
+# It  could be  a dictionary  if the  previous line  ends with  a binary/ternary
+# operator.  This  can cause  an issue whenever  we use  `STARTS_CURLY_BLOCK` or
+# `LINE_CONTINUATION_AT_EOL`.
+const STARTS_CURLY_BLOCK: string = '\%('
+    .. '^\s*{'
+    .. '\|' .. '^.*\zs\s=>\s\+{'
+    .. '\|' ..  $'^\%(\s*\|.*{BAR_SEPARATION}\s*\)\%(com\%[mand]\|au\%[tocmd]\).*\zs\s{{'
+    .. '\)' .. END_OF_COMMAND
+
+# STARTS_NAMED_BLOCK {{{3
+
+# All of these will be used at the start of a line (or after a bar).
+# NOTE: Don't replace `\%x28` with `(`.{{{
+#
+# Otherwise, the paren would be unbalanced which might cause syntax highlighting
+# issues much  later in the  code of the  current script (sometimes,  the syntax
+# highlighting plugin fails  to correctly recognize a heredoc which  is far away
+# and/or not displayed because inside a fold).
+# }}}
+patterns =<< trim END
+    if
+    el\%[se]
+    elseif\=
+    for
+    wh\%[ile]
+    try
+    cat\%[ch]
+    fina\|finally\=
+    fu\%[nction]\%x28\@!
+    \%(export\s\+\)\=def
+    aug\%[roup]\%(\s\+[eE][nN][dD]\)\@!\s\+\S\+
+END
+const STARTS_NAMED_BLOCK: string = '^\s*\%(sil\%[ent]\s\+\)\=\%(' .. patterns->join('\|') .. '\)\>:\@!'
+
+# STARTS_FUNCTION {{{3
+
+const STARTS_FUNCTION: string = '^\s*\%(export\s\+\)\=def\>:\@!'
+
+# ENDS_FUNCTION {{{3
+
+const ENDS_FUNCTION: string = $'^\s*enddef\>:\@!{END_OF_COMMAND}'
+
+# START_MIDDLE_END {{{3
+
+const START_MIDDLE_END: dict<list<string>> = {
+    if: ['if', 'el\%[se]\|elseif\=', 'en\%[dif]'],
+    else: ['if', 'el\%[se]\|elseif\=', 'en\%[dif]'],
+    elseif: ['if', 'el\%[se]\|elseif\=', 'en\%[dif]'],
+    endif: ['if', 'el\%[se]\|elseif\=', 'en\%[dif]'],
+    for: ['for', '', 'endfor\='],
+    endfor: ['for', '', 'endfor\='],
+    while: ['wh\%[ile]', '', 'endw\%[hile]'],
+    endwhile: ['wh\%[ile]', '', 'endw\%[hile]'],
+    try: ['try', 'cat\%[ch]\|fina\|finally\=', 'endt\%[ry]'],
+    catch: ['try', 'cat\%[ch]\|fina\|finally\=', 'endt\%[ry]'],
+    finally: ['try', 'cat\%[ch]\|fina\|finally\=', 'endt\%[ry]'],
+    endtry: ['try', 'cat\%[ch]\|fina\|finally\=', 'endt\%[ry]'],
+    def: ['\%(export\s\+\)\=def', '', 'enddef'],
+    enddef: ['\%(export\s\+\)\=def', '', 'enddef'],
+    function: ['fu\%[nction]', '', 'endf\%[unction]'],
+    endfunction: ['fu\%[nction]', '', 'endf\%[unction]'],
+    augroup: ['aug\%[roup]\%(\s\+[eE][nN][dD]\)\@!\s\+\S\+', '', 'aug\%[roup]\s\+[eE][nN][dD]'],
+}->map((_, kwds: list<string>) =>
+    kwds->map((_, kwd: string) => kwd == ''
+    ? ''
+    : $'\%(^\|{BAR_SEPARATION}\|\<sil\%[ent]\|{HIGHER_ORDER_COMMAND}\)\s*'
+    .. $'\%({printf('\C\<\%%(%s\)\>:\@!\%%(\s*%s\)\@!', kwd, OPERATOR)}\)'))
+# }}}2
+# EOL {{{2
+# OPENING_BRACKET_AT_EOL {{{3
+
+const OPENING_BRACKET_AT_EOL: string = $'{OPENING_BRACKET}{END_OF_VIM9_LINE}'
+
+# COMMA_AT_EOL {{{3
+
+const COMMA_AT_EOL: string = $',{END_OF_VIM9_LINE}'
+
+# COMMA_OR_DICT_KEY_AT_EOL {{{3
+
+const COMMA_OR_DICT_KEY_AT_EOL: string = $'\%(,\|{DICT_KEY}\){END_OF_VIM9_LINE}'
+
+# LAMBDA_ARROW_AT_EOL {{{3
+
+const LAMBDA_ARROW_AT_EOL: string = $'\s=>{END_OF_VIM9_LINE}'
+
+# LINE_CONTINUATION_AT_EOL {{{3
+
+const LINE_CONTINUATION_AT_EOL: string = '\%('
+    .. ','
+    .. '\|' .. OPERATOR
+    .. '\|' .. '\s=>'
+    .. '\|' .. '[^=]\zs[[(]'
+    .. '\|' .. DICT_KEY
+    # `{` is ambiguous.
+    # It can be the start of a dictionary or a block.
+    # We only want to match the former.
+    .. '\|' .. $'^\%({STARTS_CURLY_BLOCK}\)\@!.*\zs{{'
+    .. '\)\s*\%(\s#.*\)\=$'
+# }}}2
+# SOL {{{2
+# BACKSLASH_AT_SOL {{{3
+
+const BACKSLASH_AT_SOL: string = '^\s*\%(\\\|[#"]\\ \)'
+
+# CLOSING_BRACKET_AT_SOL {{{3
+
+const CLOSING_BRACKET_AT_SOL: string = $'^\s*{CLOSING_BRACKET}'
+
+# LINE_CONTINUATION_AT_SOL {{{3
+
+const LINE_CONTINUATION_AT_SOL: string = '^\s*\%('
+    .. '\\'
+    .. '\|' .. '[#"]\\ '
+    .. '\|' .. OPERATOR
+    .. '\|' .. '->\s*\h'
+    .. '\|' .. '\.\h'  # dict member
+    .. '\|' .. '|'
+    # TODO: `}` at the start of a line is not necessarily a line continuation.
+    # Could be the end of a block.
+    .. '\|' .. CLOSING_BRACKET
+    .. '\)'
+
+# RANGE_AT_SOL {{{3
+
+const RANGE_AT_SOL: string = '^\s*:\S'
+# }}}1
+# Interface {{{1
+export def Expr(lnum: number): number # {{{2
+    # line which is indented
+    var line_A: dict<any> = {text: getline(lnum), lnum: lnum}
+    # line above, on which we'll base the indent of line A
+    var line_B: dict<any>
+
+    if line_A->AtStartOf('HereDoc')
+        line_A->CacheHeredoc()
+    elseif line_A.lnum->IsInside('HereDoc')
+        return line_A.text->HereDocIndent()
+    elseif line_A.lnum->IsRightBelow('HereDoc')
+        var ind: number = b:vimindent.startindent
+        unlet! b:vimindent
+        return ind
+    endif
+
+    # Don't move this block after the function header one.
+    # Otherwise, we  might clear the cache  too early if the  line following the
+    # header is a comment.
+    if line_A.text =~ COMMENT
+        return CommentIndent()
+    endif
+
+    line_B = PrevCodeLine(line_A.lnum)
+    if line_A.text =~ BACKSLASH_AT_SOL
+        if line_B.text =~ BACKSLASH_AT_SOL
+            return Indent(line_B.lnum)
+        else
+            return Indent(line_B.lnum) + IndentMoreLineContinuation()
+        endif
+    endif
+
+    if line_A->AtStartOf('FuncHeader')
+        line_A.lnum->CacheFuncHeader()
+    elseif line_A.lnum->IsInside('FuncHeader')
+        return b:vimindent.startindent + 2 * shiftwidth()
+    elseif line_A.lnum->IsRightBelow('FuncHeader')
+        var startindent: number = b:vimindent.startindent
+        unlet! b:vimindent
+        if line_A.text =~ ENDS_FUNCTION
+            return startindent
+        else
+            return startindent + shiftwidth()
+        endif
+    endif
+
+    var past_bracket_block: dict<any>
+    if exists('b:vimindent')
+            && b:vimindent->has_key('is_BracketBlock')
+        past_bracket_block = RemovePastBracketBlock(line_A)
+    endif
+    if line_A->AtStartOf('BracketBlock')
+        line_A->CacheBracketBlock()
+    endif
+    if line_A.lnum->IsInside('BracketBlock')
+            && !b:vimindent.block_stack[0].is_curly_block
+        for block: dict<any> in b:vimindent.block_stack
+            # Can't call `BracketBlockIndent()` before we're indenting a line *after* the start of the block.{{{
+            #
+            # That's because it might need  the correct indentation of the start
+            # of the block.   But if we're still *on* the  start, we haven't yet
+            # computed that indentation.
+            #}}}
+            if line_A.lnum > block.startlnum
+                    && !block.is_curly_block
+                return BracketBlockIndent(line_A, block)
+            endif
+        endfor
+    endif
+    if line_A.text->ContinuesBelowBracketBlock(line_B, past_bracket_block)
+            && line_A.text !~ CLOSING_BRACKET_AT_SOL
+        return past_bracket_block.startindent
+    endif
+
+    # Problem: If we press `==` on the line right below the start of a multiline
+    # lambda (split after its arrow `=>`), the indent is not correct.
+    # Solution: Indent relative to the line above.
+    if line_B->EndsWithLambdaArrow()
+        return Indent(line_B.lnum) + shiftwidth() + IndentMoreInBracketBlock()
+    endif
+
+    # Don't move this block before the heredoc one.{{{
+    #
+    # A heredoc might be assigned on the very first line.
+    # And if it is, we need to cache some info.
+    #}}}
+    # Don't move it before the function header and bracket block ones either.{{{
+    #
+    # You could, because these blocks of code deal with construct which can only
+    # appear  in a  Vim9  script.  And  in  a  Vim9 script,  the  first line  is
+    # `vim9script`.  Or  maybe some legacy code/comment  (see `:help vim9-mix`).
+    # But you  can't find a  Vim9 function header or  Vim9 bracket block  on the
+    # first line.
+    #
+    # Anyway, even if you could, don't.  First, it would be inconsistent.
+    # Second, it  could give unexpected results  while we're trying to  fix some
+    # failing test.
+    #}}}
+    if line_A.lnum == 1
+        return 0
+    endif
+
+    # Don't do that:
+    #     if line_A.text !~ '\S'
+    #         return -1
+    #     endif
+    # It would prevent  a line from being automatically indented  when using the
+    # normal command `o`.
+    # TODO: Can we write a test for this?
+
+    if line_B.text =~ STARTS_CURLY_BLOCK
+        return Indent(line_B.lnum) + shiftwidth() + IndentMoreInBracketBlock()
+
+    elseif line_A.text =~ CLOSING_BRACKET_AT_SOL
+        var start: number = MatchingOpenBracket(line_A)
+        if start <= 0
+            return -1
+        endif
+        return Indent(start) + IndentMoreInBracketBlock()
+
+    elseif line_A.text =~ ENDS_BLOCK_OR_CLAUSE
+            && !line_B->EndsWithLineContinuation()
+        var kwd: string = BlockStartKeyword(line_A.text)
+        if !START_MIDDLE_END->has_key(kwd)
+            return -1
+        endif
+
+        # If the cursor  is after the match  for the end pattern,  we won't find
+        # the start of the block.  Let's make sure that doesn't happen.
+        cursor(line_A.lnum, 1)
+
+        var [start: string, middle: string, end: string] = START_MIDDLE_END[kwd]
+        var block_start = SearchPairStart(start, middle, end)
+        if block_start > 0
+            return Indent(block_start)
+        else
+            return -1
+        endif
+    endif
+
+    var base_ind: number
+    if line_A->IsFirstLineOfCommand(line_B)
+        line_A.isfirst = true
+        line_B = line_B->FirstLinePreviousCommand()
+        base_ind = Indent(line_B.lnum)
+
+        if line_B->EndsWithCurlyBlock()
+                && !line_A->IsInThisBlock(line_B.lnum)
+            return base_ind
+        endif
+
+    else
+        line_A.isfirst = false
+        base_ind = Indent(line_B.lnum)
+
+        var line_C: dict<any> = PrevCodeLine(line_B.lnum)
+        if !line_B->IsFirstLineOfCommand(line_C) || line_C.lnum <= 0
+            return base_ind
+        endif
+    endif
+
+    var ind: number = base_ind + Offset(line_A, line_B)
+    return [ind, 0]->max()
+enddef
+
+def g:GetVimIndent(): number # {{{2
+    # for backward compatibility
+    return Expr(v:lnum)
+enddef
+# }}}1
+# Core {{{1
+def Offset( # {{{2
+        # we indent this line ...
+        line_A: dict<any>,
+        # ... relatively to this line
+        line_B: dict<any>,
+        ): number
+
+    # increase indentation inside a block
+    if line_B.text =~ STARTS_NAMED_BLOCK || line_B->EndsWithCurlyBlock()
+        # But don't indent if the line starting the block also closes it.
+        if line_B->AlsoClosesBlock()
+            return 0
+        # Indent twice for  a line continuation in the block  header itself, so that
+        # we can easily  distinguish the end of  the block header from  the start of
+        # the block body.
+        elseif line_B->EndsWithLineContinuation()
+                && !line_A.isfirst
+                || line_A.text =~ LINE_CONTINUATION_AT_SOL
+                && line_A.text !~ PLUS_MINUS_COMMAND
+                || line_A.text->Is_IN_KeywordForLoop(line_B.text)
+            return 2 * shiftwidth()
+        else
+            return shiftwidth()
+        endif
+
+    # increase indentation of  a line if it's the continuation  of a command which
+    # started on a previous line
+    elseif !line_A.isfirst
+            && (line_B->EndsWithLineContinuation()
+            || line_A.text =~ LINE_CONTINUATION_AT_SOL)
+        return shiftwidth()
+    endif
+
+    return 0
+enddef
+
+def HereDocIndent(line_A: string): number # {{{2
+    # at the end of a heredoc
+    if line_A =~ $'^\s*{b:vimindent.endmarker}$'
+        # `END` must be at the very start of the line if the heredoc is not trimmed
+        if !b:vimindent.is_trimmed
+            # We can't invalidate the cache just yet.
+            # The indent of `END` is meaningless;  it's always 0.  The next line
+            # will need to be indented relative to the start of the heredoc.  It
+            # must know where it starts; it needs the cache.
+            return 0
+        else
+            var ind: number = b:vimindent.startindent
+            # invalidate the cache so that it's not used for the next heredoc
+            unlet! b:vimindent
+            return ind
+        endif
+    endif
+
+    # In a non-trimmed heredoc, all of leading whitespace is semantic.
+    # Leave it alone.
+    if !b:vimindent.is_trimmed
+        # But do save the indent of the assignment line.
+        if !b:vimindent->has_key('startindent')
+            b:vimindent.startindent = b:vimindent.startlnum->Indent()
+        endif
+        return -1
+    endif
+
+    # In a trimmed heredoc, *some* of the leading whitespace is semantic.
+    # We want to preserve  it, so we can't just indent  relative to the assignment
+    # line.  That's because we're dealing with data, not with code.
+    # Instead, we need  to compute by how  much the indent of  the assignment line
+    # was increased  or decreased.   Then, we  need to apply  that same  change to
+    # every line inside the body.
+    var offset: number
+    if !b:vimindent->has_key('offset')
+        var old_startindent: number = b:vimindent.startindent
+        var new_startindent: number = b:vimindent.startlnum->Indent()
+        offset = new_startindent - old_startindent
+
+        # If all the non-empty lines in  the body have a higher indentation relative
+        # to the assignment, there is no need to indent them more.
+        # But if  at least one of  them does have  the same indentation level  (or a
+        # lower one), then we want to indent it further (and the whole block with it).
+        # This way,  we can clearly distinguish  the heredoc block from  the rest of
+        # the code.
+        var end: number = search($'^\s*{b:vimindent.endmarker}$', 'nW')
+        var should_indent_more: bool = range(v:lnum, end - 1)
+            ->indexof((_, lnum: number): bool => Indent(lnum) <= old_startindent && getline(lnum) != '') >= 0
+        if should_indent_more
+            offset += shiftwidth()
+        endif
+
+        b:vimindent.offset = offset
+        b:vimindent.startindent = new_startindent
+    endif
+
+    return [0, Indent(v:lnum) + b:vimindent.offset]->max()
+enddef
+
+def CommentIndent(): number # {{{2
+    var line_B: dict<any>
+    line_B.lnum = prevnonblank(v:lnum - 1)
+    line_B.text = getline(line_B.lnum)
+    if line_B.text =~ COMMENT
+        return Indent(line_B.lnum)
+    endif
+
+    var next: number = NextCodeLine()
+    if next == 0
+        return 0
+    endif
+    var vimindent_save: dict<any> = get(b:, 'vimindent', {})->deepcopy()
+    var ind: number = next->Expr()
+    # The previous `Expr()` might have set or deleted `b:vimindent`.
+    # This could  cause issues (e.g.  when indenting  2 commented lines  above a
+    # heredoc).  Let's make sure the state of the variable is not altered.
+    if vimindent_save->empty()
+        unlet! b:vimindent
+    else
+        b:vimindent = vimindent_save
+    endif
+    if getline(next) =~ ENDS_BLOCK
+        return ind + shiftwidth()
+    else
+        return ind
+    endif
+enddef
+
+def BracketBlockIndent(line_A: dict<any>, block: dict<any>): number # {{{2
+    if !block->has_key('startindent')
+        block.startindent = block.startlnum->Indent()
+    endif
+
+    var ind: number = block.startindent
+
+    if line_A.text =~ CLOSING_BRACKET_AT_SOL
+        if b:vimindent.is_on_named_block_line
+            ind += 2 * shiftwidth()
+        endif
+        return ind + IndentMoreInBracketBlock()
+    endif
+
+    var startline: dict<any> = {
+        text: block.startline,
+        lnum: block.startlnum
+    }
+    if startline->EndsWithComma()
+            || startline->EndsWithLambdaArrow()
+            || (startline->EndsWithOpeningBracket()
+            # TODO: Is that reliable?
+            && block.startline !~
+            $'^\s*{NON_BRACKET}\+{LIST_OR_DICT_CLOSING_BRACKET},\s\+{LIST_OR_DICT_OPENING_BRACKET}')
+        ind += shiftwidth() + IndentMoreInBracketBlock()
+    endif
+
+    if b:vimindent.is_on_named_block_line
+        ind += shiftwidth()
+    endif
+
+    if block.is_dict
+            && line_A.text !~ DICT_KEY
+        ind += shiftwidth()
+    endif
+
+    return ind
+enddef
+
+def CacheHeredoc(line_A: dict<any>) # {{{2
+    var endmarker: string = line_A.text->matchstr(ASSIGNS_HEREDOC)
+    var endlnum: number = search($'^\s*{endmarker}$', 'nW')
+    var is_trimmed: bool = line_A.text =~ $'.*\s\%(trim\%(\s\+eval\)\=\)\s\+[A-Z]\+{END_OF_LINE}'
+    b:vimindent = {
+        is_HereDoc: true,
+        startlnum: line_A.lnum,
+        endlnum: endlnum,
+        endmarker: endmarker,
+        is_trimmed: is_trimmed,
+    }
+    if is_trimmed
+        b:vimindent.startindent = Indent(line_A.lnum)
+    endif
+    RegisterCacheInvalidation()
+enddef
+
+def CacheFuncHeader(startlnum: number) # {{{2
+    var pos: list<number> = getcurpos()
+    cursor(startlnum, 1)
+    if search('(', 'W', startlnum) <= 0
+        return
+    endif
+    var endlnum: number = SearchPair('(', '', ')', 'nW')
+    setpos('.', pos)
+    if endlnum == startlnum
+        return
+    endif
+
+    b:vimindent = {
+        is_FuncHeader: true,
+        startindent: startlnum->Indent(),
+        endlnum: endlnum,
+    }
+    RegisterCacheInvalidation()
+enddef
+
+def CacheBracketBlock(line_A: dict<any>) # {{{2
+    var pos: list<number> = getcurpos()
+    var opening: string = line_A.text->matchstr(CHARACTER_UNDER_CURSOR)
+    var closing: string = {'[': ']', '{': '}', '(': ')'}[opening]
+    var endlnum: number = SearchPair(opening, '', closing, 'nW')
+    setpos('.', pos)
+    if endlnum <= line_A.lnum
+        return
+    endif
+
+    if !exists('b:vimindent')
+        b:vimindent = {
+            is_BracketBlock: true,
+            is_on_named_block_line: line_A.text =~ STARTS_NAMED_BLOCK,
+            block_stack: [],
+        }
+    endif
+
+    var is_dict: bool
+    var is_curly_block: bool
+    if opening == '{'
+        if line_A.text =~ STARTS_CURLY_BLOCK
+            [is_dict, is_curly_block] = [false, true]
+        else
+            [is_dict, is_curly_block] = [true, false]
+        endif
+    endif
+    b:vimindent.block_stack->insert({
+        is_dict: is_dict,
+        is_curly_block: is_curly_block,
+        startline: line_A.text,
+        startlnum: line_A.lnum,
+        endlnum: endlnum,
+    })
+
+    RegisterCacheInvalidation()
+enddef
+
+def RegisterCacheInvalidation() # {{{2
+    # invalidate the cache so that it's not used for the next `=` normal command
+    autocmd_add([{
+        cmd: 'unlet! b:vimindent',
+        event: 'ModeChanged',
+        group: '__VimIndent__',
+        once: true,
+        pattern: '*:n',
+        replace: true,
+    }])
+enddef
+
+def RemovePastBracketBlock(line_A: dict<any>): dict<any> # {{{2
+    var stack: list<dict<any>> = b:vimindent.block_stack
+
+    var removed: dict<any>
+    if line_A.lnum > stack[0].endlnum
+        removed = stack[0]
+    endif
+
+    stack->filter((_, block: dict<any>): bool => line_A.lnum <= block.endlnum)
+    if stack->empty()
+        unlet! b:vimindent
+    endif
+    return removed
+enddef
+# }}}1
+# Util {{{1
+# Get {{{2
+def Indent(lnum: number): number # {{{3
+    if lnum <= 0
+        # Don't  return `-1`.  It could cause `Expr()` to return a non-multiple of `'shiftwidth'`.{{{
+        #
+        # It would be  OK if we were always returning  `Indent()` directly.  But
+        # we  don't.  Most  of  the  time, we  include  it  in some  computation
+        # like  `Indent(...) + shiftwidth()`.   If  `'shiftwidth'` is  `4`,  and
+        # `Indent()` returns `-1`, `Expr()` will end up returning `3`.
+        #}}}
+        return 0
+    endif
+    return indent(lnum)
+enddef
+
+def BlockStartKeyword(line: string): string # {{{3
+    var kwd: string = line->matchstr('\l\+')
+    return fullcommand(kwd, false)
+enddef
+
+def MatchingOpenBracket(line: dict<any>): number # {{{3
+    var end: string = line.text->matchstr(CLOSING_BRACKET)
+    var start: string = {']': '[', '}': '{', ')': '('}[end]
+    cursor(line.lnum, 1)
+    return SearchPairStart(start, '', end)
+enddef
+
+def FirstLinePreviousCommand(line: dict<any>): dict<any> # {{{3
+    var line_B: dict<any> = line
+
+    while line_B.lnum > 1
+        var code_line_above: dict<any> = PrevCodeLine(line_B.lnum)
+
+        if line_B.text =~ CLOSING_BRACKET_AT_SOL
+            var n: number = MatchingOpenBracket(line_B)
+
+            if n <= 0
+                break
+            endif
+
+            line_B.lnum = n
+            line_B.text = getline(line_B.lnum)
+            continue
+
+        elseif line_B->IsFirstLineOfCommand(code_line_above)
+            break
+        endif
+
+        line_B = code_line_above
+    endwhile
+
+    return line_B
+enddef
+
+def PrevCodeLine(lnum: number): dict<any> # {{{3
+    var line: string = getline(lnum)
+    if line =~ '^\s*[A-Z]\+$'
+        var endmarker: string = line->matchstr('[A-Z]\+')
+        var pos: list<number> = getcurpos()
+        cursor(lnum, 1)
+        var n: number = search(ASSIGNS_HEREDOC, 'bnW')
+        setpos('.', pos)
+        if n > 0
+            line = getline(n)
+            if line =~ $'{HEREDOC_OPERATOR}\s\+{endmarker}'
+                return {lnum: n, text: line}
+            endif
+        endif
+    endif
+
+    var n: number = prevnonblank(lnum - 1)
+    line = getline(n)
+    while line =~ COMMENT && n > 1
+        n = prevnonblank(n - 1)
+        line = getline(n)
+    endwhile
+    # If we get back to the first line, we return 1 no matter what; even if it's a
+    # commented line.   That should not  cause an issue  though.  We just  want to
+    # avoid a  commented line above which  there is a  line of code which  is more
+    # relevant.  There is nothing above the first line.
+    return {lnum: n, text: line}
+enddef
+
+def NextCodeLine(): number # {{{3
+    var last: number = line('$')
+    if v:lnum == last
+        return 0
+    endif
+
+    var lnum: number = v:lnum + 1
+    while lnum <= last
+        var line: string = getline(lnum)
+        if line != '' && line !~ COMMENT
+            return lnum
+        endif
+        ++lnum
+    endwhile
+    return 0
+enddef
+
+def SearchPair( # {{{3
+        start: string,
+        middle: string,
+        end: string,
+        flags: string,
+        stopline = 0,
+        ): number
+
+    var s: string = start
+    var e: string = end
+    if start == '[' || start == ']'
+        s = s->escape('[]')
+    endif
+    if end == '[' || end == ']'
+        e = e->escape('[]')
+    endif
+    return searchpair(s, middle, e, flags, (): bool => InCommentOrString(), stopline, TIMEOUT)
+enddef
+
+def SearchPairStart( # {{{3
+        start: string,
+        middle: string,
+        end: string,
+        ): number
+    return SearchPair(start, middle, end, 'bnW')
+enddef
+
+def SearchPairEnd( # {{{3
+        start: string,
+        middle: string,
+        end: string,
+        stopline = 0,
+        ): number
+    return SearchPair(start, middle, end, 'nW', stopline)
+enddef
+# }}}2
+# Test {{{2
+def AtStartOf(line_A: dict<any>, syntax: string): bool # {{{3
+    if syntax == 'BracketBlock'
+        return AtStartOfBracketBlock(line_A)
+    endif
+
+    var pat: string = {
+        HereDoc: ASSIGNS_HEREDOC,
+        FuncHeader: STARTS_FUNCTION
+    }[syntax]
+    return line_A.text =~ pat
+        && (!exists('b:vimindent') || !b:vimindent->has_key('is_HereDoc'))
+enddef
+
+def AtStartOfBracketBlock(line_A: dict<any>): bool # {{{3
+    # We  ignore bracket  blocks  while we're  indenting  a function  header
+    # because  it makes  the logic  simpler.  It  might mean  that we  don't
+    # indent correctly a  multiline bracket block inside  a function header,
+    # but that's  a corner case for  which it doesn't seem  worth making the
+    # code more complex.
+    if exists('b:vimindent')
+            && !b:vimindent->has_key('is_BracketBlock')
+        return false
+    endif
+
+    var pos: list<number> = getcurpos()
+    cursor(line_A.lnum, [line_A.lnum, '$']->col())
+
+    if SearchPair(OPENING_BRACKET, '', CLOSING_BRACKET, 'bcW', line_A.lnum) <= 0
+        setpos('.', pos)
+        return false
+    endif
+    # Don't restore the cursor position.
+    # It needs to be on a bracket for `CacheBracketBlock()` to work as intended.
+
+    return line_A->EndsWithOpeningBracket()
+        || line_A->EndsWithCommaOrDictKey()
+        || line_A->EndsWithLambdaArrow()
+enddef
+
+def ContinuesBelowBracketBlock( # {{{3
+        line_A: string,
+        line_B: dict<any>,
+        block: dict<any>
+        ): bool
+
+    return !block->empty()
+        && (line_A =~ LINE_CONTINUATION_AT_SOL
+        || line_B->EndsWithLineContinuation())
+enddef
+
+def IsInside(lnum: number, syntax: string): bool # {{{3
+    if !exists('b:vimindent')
+            || !b:vimindent->has_key($'is_{syntax}')
+        return false
+    endif
+
+    if syntax == 'BracketBlock'
+        if !b:vimindent->has_key('block_stack')
+                || b:vimindent.block_stack->empty()
+            return false
+        endif
+        return lnum <= b:vimindent.block_stack[0].endlnum
+    endif
+
+    return lnum <= b:vimindent.endlnum
+enddef
+
+def IsRightBelow(lnum: number, syntax: string): bool # {{{3
+    return exists('b:vimindent')
+        && b:vimindent->has_key($'is_{syntax}')
+        && lnum > b:vimindent.endlnum
+enddef
+
+def IsInThisBlock(line_A: dict<any>, lnum: number): bool # {{{3
+    var pos: list<number> = getcurpos()
+    cursor(lnum, [lnum, '$']->col())
+    var end: number = SearchPairEnd('{', '', '}')
+    setpos('.', pos)
+
+    return line_A.lnum <= end
+enddef
+
+def IsFirstLineOfCommand(line_1: dict<any>, line_2: dict<any>): bool # {{{3
+    if line_1.text->Is_IN_KeywordForLoop(line_2.text)
+        return false
+    endif
+
+    if line_1.text =~ RANGE_AT_SOL
+            || line_1.text =~ PLUS_MINUS_COMMAND
+        return true
+    endif
+
+    if line_2.text =~ DICT_KEY
+            && !line_1->IsInThisBlock(line_2.lnum)
+        return true
+    endif
+
+    var line_1_is_good: bool = line_1.text !~ COMMENT
+        && line_1.text !~ DICT_KEY
+        && line_1.text !~ LINE_CONTINUATION_AT_SOL
+
+    var line_2_is_good: bool = !line_2->EndsWithLineContinuation()
+
+    return line_1_is_good && line_2_is_good
+enddef
+
+def Is_IN_KeywordForLoop(line_1: string, line_2: string): bool # {{{3
+    return line_2 =~ '^\s*for\s'
+        && line_1 =~ '^\s*in\s'
+enddef
+
+def InCommentOrString(): bool # {{{3
+    for synID: number in synstack('.', col('.'))
+        if synIDattr(synID, 'name') =~ '\ccomment\|string\|heredoc'
+            return true
+        endif
+    endfor
+
+    return false
+enddef
+
+def AlsoClosesBlock(line_B: dict<any>): bool # {{{3
+    # We know that `line_B` opens a block.
+    # Let's see if it also closes that block.
+    var kwd: string = BlockStartKeyword(line_B.text)
+    if !START_MIDDLE_END->has_key(kwd)
+        return false
+    endif
+
+    var [start: string, middle: string, end: string] = START_MIDDLE_END[kwd]
+    var pos: list<number> = getcurpos()
+    cursor(line_B.lnum, 1)
+    var block_end: number = SearchPairEnd(start, middle, end, line_B.lnum)
+    setpos('.', pos)
+
+    return block_end > 0
+enddef
+
+def EndsWithComma(line: dict<any>): bool # {{{3
+    return NonCommentedMatch(line, COMMA_AT_EOL)
+enddef
+
+def EndsWithCommaOrDictKey(line_A: dict<any>): bool # {{{3
+    return NonCommentedMatch(line_A, COMMA_OR_DICT_KEY_AT_EOL)
+enddef
+
+def EndsWithCurlyBlock(line_B: dict<any>): bool # {{{3
+    return NonCommentedMatch(line_B, STARTS_CURLY_BLOCK)
+enddef
+
+def EndsWithLambdaArrow(line_A: dict<any>): bool # {{{3
+    return NonCommentedMatch(line_A, LAMBDA_ARROW_AT_EOL)
+enddef
+
+def EndsWithLineContinuation(line_B: dict<any>): bool # {{{3
+    return NonCommentedMatch(line_B, LINE_CONTINUATION_AT_EOL)
+enddef
+
+def EndsWithOpeningBracket(line: dict<any>): bool # {{{3
+    return NonCommentedMatch(line, OPENING_BRACKET_AT_EOL)
+enddef
+
+def NonCommentedMatch(line: dict<any>, pat: string): bool # {{{3
+    # Could happen if there is no code above us, and we're not on the 1st line.
+    # In that case, `PrevCodeLine()` returns `{lnum: 0, line: ''}`.
+    if line.lnum == 0
+        return false
+    endif
+
+    if line.text =~ PLUS_MINUS_COMMAND
+        return false
+    endif
+
+    # In `argdelete *`, `*` is not a multiplication operator.
+    # TODO: Other commands can accept `*` as an argument.  Handle them too.
+    if line.text =~ '\<argd\%[elete]\s\+\*\s*$'
+        return false
+    endif
+
+    # Technically, that's wrong.  A  line might start with a range  and end with a
+    # line continuation symbol.  But it's unlikely.  And it's useful to assume the
+    # opposite because it  prevents us from conflating a mark  with an operator or
+    # the start of a list:
+    #
+    #              not a comparison operator
+    #              v
+    #     :'< mark <
+    #     :'< mark [
+    #              ^
+    #              not the start of a list
+    if line.text =~ RANGE_AT_SOL
+        return false
+    endif
+
+    #                    that's not an arithmetic operator
+    #                    v
+    #     catch /pattern /
+    #
+    # When `/` is used as a pattern delimiter, it's always present twice.
+    # And  usually, the  first occurrence  is  in the  middle of  a sequence  of
+    # non-whitespace characters.  If we can find  such a `/`, we assume that the
+    # trailing `/` is not an operator.
+    # Warning: Here, don't use a too complex pattern.{{{
+    #
+    # In particular, avoid backreferences.
+    # For example, this would be too costly:
+    #
+    #     if line.text =~ $'\%(\S*\({PATTERN_DELIMITER}\)\S\+\|\S\+\({PATTERN_DELIMITER}\)\S*\)'
+    #             .. $'\s\+\1{END_OF_COMMAND}'
+    #
+    # Sometimes, it could even give `E363`.
+    #}}}
+    var delim: string = line.text
+        ->matchstr($'\s\+\zs{PATTERN_DELIMITER}\ze{END_OF_COMMAND}')
+    if !delim->empty()
+        delim = $'\V{delim}\m'
+        if line.text =~ $'\%(\S*{delim}\S\+\|\S\+{delim}\S*\)\s\+{delim}{END_OF_COMMAND}'
+            return false
+        endif
+    endif
+    # TODO: We might still miss some corner cases:{{{
+    #
+    #                          conflated with arithmetic division
+    #                          v
+    #     substitute/pat / rep /
+    #         echo
+    #     ^--^
+    #      ✘
+    #
+    # A better way to handle all these corner cases, would be to inspect the top
+    # of the syntax stack:
+    #
+    #     :echo synID('.', col('.'), v:false)->synIDattr('name')
+    #
+    # Unfortunately, the legacy syntax plugin is not accurate enough.
+    # For example, it doesn't highlight a slash as an operator.
+    # }}}
+
+    # `%` at the end of a line is tricky.
+    # It might be the modulo operator or the current file (e.g. `edit %`).
+    # Let's assume it's the latter.
+    if line.text =~ $'%{END_OF_COMMAND}'
+        return false
+    endif
+
+    # `:help cd-`
+    if line.text =~ CD_COMMAND
+        return false
+    endif
+
+    # At the end of a mapping, any character might appear; e.g. a paren:
+    #
+    #     nunmap <buffer> (
+    #
+    # Don't conflate this with a line continuation symbol.
+    if line.text =~ MAPPING_COMMAND
+        return false
+    endif
+
+    #             not a comparison operator
+    #             vv
+    #     normal! ==
+    if line.text =~ NORMAL_COMMAND
+        return false
+    endif
+
+    var pos: list<number> = getcurpos()
+    cursor(line.lnum, 1)
+    var match_lnum: number = search(pat, 'cnW', line.lnum, TIMEOUT, (): bool => InCommentOrString())
+    setpos('.', pos)
+    return match_lnum > 0
+enddef
+# }}}1
+# vim:sw=4
new file mode 100644
--- /dev/null
+++ b/runtime/compiler/hare.vim
@@ -0,0 +1,31 @@
+" Vim compiler file
+" Compiler: Hare Compiler
+" Maintainer: Amelia Clarke <me@rsaihe.dev>
+" Last Change: 2022-09-21
+
+if exists("g:current_compiler")
+  finish
+endif
+let g:current_compiler = "hare"
+
+let s:cpo_save = &cpo
+set cpo&vim
+
+if exists(':CompilerSet') != 2
+  command -nargs=* CompilerSet setlocal <args>
+endif
+
+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,
+  \%-G%.%#
+
+let &cpo = s:cpo_save
+unlet s:cpo_save
+" vim: tabstop=2 shiftwidth=2 expandtab
--- a/runtime/doc/cmdline.txt
+++ b/runtime/doc/cmdline.txt
@@ -1,4 +1,4 @@
-*cmdline.txt*   For Vim version 9.0.  Last change: 2022 Jun 16
+*cmdline.txt*   For Vim version 9.0.  Last change: 2022 Sep 26
 
 
 		  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -806,7 +806,7 @@ Count and Range						*N:*
 
 When giving a count before entering ":", this is translated into:
 		:.,.+(count - 1)
-In words: The 'count' lines at and after the cursor.  Example: To delete
+In words: The "count" lines at and after the cursor.  Example: To delete
 three lines: >
 		3:d<CR>		is translated into: .,.+2d<CR>
 <
--- a/runtime/doc/develop.txt
+++ b/runtime/doc/develop.txt
@@ -1,4 +1,4 @@
-*develop.txt*   For Vim version 9.0.  Last change: 2020 Aug 15
+*develop.txt*   For Vim version 9.0.  Last change: 2022 Sep 20
 
 
 		  VIM REFERENCE MANUAL    by Bram Moolenaar
--- a/runtime/doc/ft_context.txt
+++ b/runtime/doc/ft_context.txt
@@ -1,4 +1,4 @@
-*ft_context.txt*	For Vim version 9.0.  Last change: 2022 Aug 12
+*ft_context.txt*	For Vim version 9.0.  Last change: 2022 Sep 27
 
 This is the documentation for the ConTeXt filetype plugin.
 
@@ -23,13 +23,12 @@ ConTeXt, similarly to LaTeX, is a macro-
 <
 The ConTeXt plugin provides syntax highlighting, completion and support for
 typesetting ConTeXt documents. The recommended way to typeset a document is to
-use |:ConTeXt|. This will invoke the `mtxrun` script that is found in $PATH.
+use |:ConTeXt|. This will invoke the `mtxrun` script that is found in `$PATH`.
 
-For more fine grained control over the command and its environment, you may
-invoke `context.Typeset()` directly (or `context#Typeset()` from legacy Vim
-script). For instance, if you have installed a version of ConTeXt in
-`~/context`, you may define a function to use it (you may put the following
-code in `~/.vim/after/ftplugin/context.vim`) similar to the following:
+For more fine grained control over the command and its environment,
+`context.Typeset()` can be used directly (or `context#Typeset()` from legacy
+Vim script). For instance, if a version of ConTeXt is installed in
+`~/context`, you may define a function to use it similar to the following:
 >
 	import autoload 'context.vim'
 
@@ -38,14 +37,15 @@ code in `~/.vim/after/ftplugin/context.v
 	      printf("%s/context/tex/texmf-<os>-<arch>/bin:%s", $HOME, $PATH)}
 	    context.Typeset("%", env)
 	enddef
-<
-and perhaps use it with a mapping:
+
+This code may go in `~/.vim/after/ftplugin/context.vim`. A mapping can then be
+defined to invoke the custom command:
 >
 	nnoremap <silent><buffer><leader>t <scriptcmd>MyConTeXt()<cr>
 <
 `context.Typeset()` accepts a third optional argument to specify a custom
-typesetting command. Such argument must be a function that takes a path and
-returns the command as a List. For example:
+typesetting command. That must be a function that takes a path and returns the
+command as a List. For example:
 >
 	def ConTeXtCustomCommand(path: string): list<string>
 	  return ['mtxrun', '--script', 'context', '--nonstopmode, path]
@@ -103,14 +103,20 @@ Stop all the ConTeXt jobs currently runn
 Settings ~
 					*'b:context_ignore_makefile'*
 					*'g:context_ignore_makefile'*
-`make` can be used to (synchronously) typeset a document. If a Makefile exists
+`:make` can be used to (synchronously) typeset a document. If a Makefile exists
 and this option is not set, standard `make` is used. If this option is set,
 `mtxrun` is invoked instead, even if a Makefile exists.
 >
 	g:context_ignore_makefile = 0
 <
-NOTE: before using `make`, set the working directory of the buffer to the
+NOTE: before using `:make`, set the working directory of the buffer to the
 directory of the file to be typeset.
+
+					*'g:context_extra_options'*
+A list of additional options to pass to `mtxrun`.
+>
+	g:context_extra_options = []
+<
 					*'b:context_include'*
 					*'g:context_include'*
 Dictionary of filetype/GROUP pairs for which syntax highlighting should be
--- a/runtime/doc/indent.txt
+++ b/runtime/doc/indent.txt
@@ -1227,13 +1227,31 @@ comments will be indented according to t
 
 
 VIM							*ft-vim-indent*
-							*g:vim_indent_cont*
-For indenting Vim scripts there is one variable that specifies the amount of
-indent for a continuation line, a line that starts with a backslash: >
+							*g:vim_indent*
+Vim scripts indentation can be configured with the `g:vim_indent` dictionary
+variable.  It supports 3 keys, `line_continuation`, `more_in_bracket_block`,
+and `searchpair_timeout`.
+`line_continuation` expects a number which will be added to the indent level of
+a continuation line starting with a backslash, and defaults to
+`shiftwidth() * 3`.  It also accepts a string, which is evaluated at runtime.
+`more_in_bracket_block` expects a boolean value; when on, an extra
+`shiftwidth()` is added inside blocks surrounded with brackets.  It defaults to
+`v:false`.
+`searchpair_timeout` expects a number which will be passed to `searchpair()` as
+a timeout.  Increasing the value might give more accurate results, but also
+causes the indentation to take more time.  It defaults to 100 (milliseconds).
 
-	:let g:vim_indent_cont = shiftwidth() * 3
+Example of configuration:
 
-Three times shiftwidth is the default value.
+	let g:vim_indent = #{
+	    \ line_continuation: shiftwidth() * 3,
+	    \ more_in_bracket_block: v:false,
+	    \ searchpair_timeout: 100,
+	    \ }
+
+							*g:vim_indent_cont*
+This variable is equivalent to `g:vim_indent.line_continuation`.
+It's supported for backward compatibility.
 
 
  vim:tw=78:ts=8:noet:ft=help:norl:
--- a/runtime/doc/map.txt
+++ b/runtime/doc/map.txt
@@ -1,4 +1,4 @@
-*map.txt*       For Vim version 9.0.  Last change: 2022 Sep 12
+*map.txt*       For Vim version 9.0.  Last change: 2022 Sep 26
 
 
 		  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -1761,7 +1761,8 @@ The valid escape sequences are
 If the first two characters of an escape sequence are "q-" (for example,
 <q-args>) then the value is quoted in such a way as to make it a valid value
 for use in an expression.  This uses the argument as one single value.
-When there is no argument <q-args> is an empty string.
+When there is no argument <q-args> is an empty string.  See the
+|q-args-example| below.
 							*<f-args>*
 To allow commands to pass their arguments on to a user-defined function, there
 is a special form <f-args> ("function args").  This splits the command
@@ -1771,7 +1772,7 @@ See the Mycmd example below.  If no argu
    To embed whitespace into an argument of <f-args>, prepend a backslash.
 <f-args> replaces every pair of backslashes (\\) with one backslash.  A
 backslash followed by a character other than white space or a backslash
-remains unmodified.  Overview:
+remains unmodified.  Also see |f-args-example| below.  Overview:
 
 	command		   <f-args> ~
 	XX ab		   'ab'
@@ -1785,7 +1786,8 @@ remains unmodified.  Overview:
 	XX a\\\\b	   'a\\b'
 	XX a\\\\ b	   'a\\', 'b'
 
-Examples >
+
+Examples for user commands: >
 
    " Delete everything after here to the end
    :com Ddel +,$d
@@ -1801,7 +1803,8 @@ Examples >
    " Count the number of lines in the range
    :com! -range -nargs=0 Lines  echo <line2> - <line1> + 1 "lines"
 
-   " Call a user function (example of <f-args>)
+<						*f-args-example*
+Call a user function (example of <f-args>) >
    :com -nargs=* Mycmd call Myfunc(<f-args>)
 
 When executed as: >
@@ -1809,7 +1812,8 @@ When executed as: >
 This will invoke: >
 	:call Myfunc("arg1","arg2")
 
-   :" A more substantial example
+<						*q-args-example* 
+A more substantial example: >
    :function Allargs(command)
    :   let i = 0
    :   while i < argc()
--- a/runtime/doc/motion.txt
+++ b/runtime/doc/motion.txt
@@ -1,4 +1,4 @@
-*motion.txt*    For Vim version 9.0.  Last change: 2022 Apr 18
+*motion.txt*    For Vim version 9.0.  Last change: 2022 Sep 26
 
 
 		  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -1145,7 +1145,7 @@ g;			Go to [count] older position in cha
 			(not a motion command)
 
 							*g,* *E663*
-g,			Go to [count] newer cursor position in change list.
+g,			Go to [count] newer position in change list.
 			Just like |g;| but in the opposite direction.
 			(not a motion command)
 
--- a/runtime/doc/options.txt
+++ b/runtime/doc/options.txt
@@ -1,4 +1,4 @@
-*options.txt*	For Vim version 9.0.  Last change: 2022 Sep 09
+*options.txt*	For Vim version 9.0.  Last change: 2022 Sep 27
 
 
 		  VIM REFERENCE MANUAL	  by Bram Moolenaar
@@ -1702,7 +1702,8 @@ A jump table for the options with a shor
 	after that.  Therefore do not append an item with += but use ^= to
 	prepend, e.g.: >
 		set clipboard^=unnamed
-<	These names are recognized:
+<	When using the GUI see |'go-A'|.
+	These names are recognized:
 
 						*clipboard-unnamed*
 	unnamed		When included, Vim will use the clipboard register '*'
@@ -3978,6 +3979,8 @@ A jump table for the options with a shor
 			 "A"		 -			yes
 			 "aA"		yes			yes
 
+		When using a terminal see the 'clipboard' option.
+
 								*'go-c'*
 	  'c'	Use console dialogs instead of popup dialogs for simple
 		choices.
--- a/runtime/doc/pattern.txt
+++ b/runtime/doc/pattern.txt
@@ -1,4 +1,4 @@
-*pattern.txt*   For Vim version 9.0.  Last change: 2022 Mar 04
+*pattern.txt*   For Vim version 9.0.  Last change: 2022 Sep 24
 
 
 		  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -134,6 +134,11 @@ 1gD			Like "gD", but ignore matches insi
 CTRL-C			Interrupt current (search) command.  Use CTRL-Break on
 			MS-Windows |dos-CTRL-Break|.
 			In Normal mode, any pending command is aborted.
+			When Vim was started with output redirected and there
+			are no changed buffers CTRL-C exits Vim.  That is to
+			help users who use "vim file | grep word" and don't
+			know how to get out (blindly typing :qa<CR> would
+			work).
 
 							*:noh* *:nohlsearch*
 :noh[lsearch]		Stop the highlighting for the 'hlsearch' option.  It
--- a/runtime/doc/quickfix.txt
+++ b/runtime/doc/quickfix.txt
@@ -1,4 +1,4 @@
-*quickfix.txt*  For Vim version 9.0.  Last change: 2022 Feb 22
+*quickfix.txt*  For Vim version 9.0.  Last change: 2022 Sep 26
 
 
 		  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -478,7 +478,7 @@ You can parse a list of lines using 'err
 modifying a quickfix list using the |getqflist()| function. Examples: >
 	echo getqflist({'lines' : ["F1:10:Line10", "F2:20:Line20"]})
 	echo getqflist({'lines' : systemlist('grep -Hn quickfix *')})
-This returns a dictionary where the 'items' key contains the list of quickfix
+This returns a dictionary where the "items" key contains the list of quickfix
 entries parsed from lines. The following shows how to use a custom
 'errorformat' to parse the lines without modifying the 'errorformat' option: >
 	echo getqflist({'efm' : '%f#%l#%m', 'lines' : ['F1#10#Line']})
@@ -597,7 +597,7 @@ 2. The error window					*quickfix-window
 			quickfix command or function, the |b:changedtick|
 			variable is incremented.  You can get the number of
 			this buffer using the getqflist() and getloclist()
-			functions by passing the 'qfbufnr' item. For a
+			functions by passing the "qfbufnr" item. For a
 			location list, this buffer is wiped out when the
 			location list is removed.
 
@@ -2011,7 +2011,7 @@ The function should return a single line
 window for each entry from start_idx to end_idx. The function can obtain
 information about the entries using the |getqflist()| function and specifying
 the quickfix list identifier "id". For a location list, getloclist() function
-can be used with the 'winid' argument. If an empty list is returned, then the
+can be used with the "winid" argument. If an empty list is returned, then the
 default format is used to display all the entries. If an item in the returned
 list is an empty string, then the default format is used to display the
 corresponding entry.
--- a/runtime/doc/repeat.txt
+++ b/runtime/doc/repeat.txt
@@ -1,4 +1,4 @@
-*repeat.txt*    For Vim version 9.0.  Last change: 2022 Jun 18
+*repeat.txt*    For Vim version 9.0.  Last change: 2022 Sep 22
 
 
 		  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -92,7 +92,8 @@ pattern and do not match another pattern
 This first finds all lines containing "found", but only executes {cmd} when
 there is no match for "notfound".
 
-To execute a non-Ex command, you can use the `:normal` command: >
+Any Ex command can be used, see |ex-cmd-index|.  To execute a Normal mode
+command, you can use the `:normal` command: >
 	:g/pat/normal {commands}
 Make sure that {commands} ends with a whole command, otherwise Vim will wait
 for you to type the rest of the command for each match.  The screen will not
@@ -200,7 +201,8 @@ For writing a Vim script, see chapter 41
 							*:source-range*
 :[range]so[urce] [++clear]
 			Read Ex commands from the [range] of lines in the
-			current buffer.
+			current buffer.  When [range] is omitted read all
+			lines.
 
 			When sourcing commands from the current buffer, the
 			same script-ID |<SID>| is used even if the buffer is
@@ -904,6 +906,11 @@ executed like a normal Ex command, "step
 context, where local variables can be inspected, and once just before
 executing the command.
 
+In a :def function variables that haven't been declared yet cannot be
+inspected.  Variables that have been declared can be inspected, also when the
+block they were declared in has finished.  In commands this would not be
+possible, thus is slightly misleading (but can be useful).
+
 The backtrace shows the hierarchy of function calls, e.g.:
 	>bt ~
 	  3 function One[3] ~
--- a/runtime/doc/syntax.txt
+++ b/runtime/doc/syntax.txt
@@ -1,4 +1,4 @@
-*syntax.txt*	For Vim version 9.0.  Last change: 2022 Jun 10
+*syntax.txt*	For Vim version 9.0.  Last change: 2022 Sep 26
 
 
 		  VIM REFERENCE MANUAL	  by Bram Moolenaar
@@ -3153,7 +3153,7 @@ The default is to use the twice sh_minli
 speed up displaying.  The disadvantage is that highlight errors may appear.
 
 syntax/sh.vim tries to flag certain problems as errors; usually things like
-extra ']'s, 'done's, 'fi's, etc.  If you find the error handling problematic
+unmatched "]", "done", "fi", etc.  If you find the error handling problematic
 for your purposes, you may suppress such error highlighting by putting
 the following line in your .vimrc: >
 
--- a/runtime/doc/tags
+++ b/runtime/doc/tags
@@ -302,6 +302,7 @@
 'fs'	options.txt	/*'fs'*
 'fsync'	options.txt	/*'fsync'*
 'ft'	options.txt	/*'ft'*
+'g:context_extra_options'	ft_context.txt	/*'g:context_extra_options'*
 'g:context_ignore_makefile'	ft_context.txt	/*'g:context_ignore_makefile'*
 'g:context_include'	ft_context.txt	/*'g:context_include'*
 'g:mf_other_macros'	ft_mp.txt	/*'g:mf_other_macros'*
@@ -4343,6 +4344,7 @@ E1302	eval.txt	/*E1302*
 E1303	map.txt	/*E1303*
 E1304	vim9.txt	/*E1304*
 E1305	textprop.txt	/*E1305*
+E1306	vim9.txt	/*E1306*
 E131	userfunc.txt	/*E131*
 E132	userfunc.txt	/*E132*
 E133	userfunc.txt	/*E133*
@@ -6802,6 +6804,7 @@ extendnew()	builtin.txt	/*extendnew()*
 extension-removal	cmdline.txt	/*extension-removal*
 extensions-improvements	todo.txt	/*extensions-improvements*
 f	motion.txt	/*f*
+f-args-example	map.txt	/*f-args-example*
 false	vim9.txt	/*false*
 false-variable	eval.txt	/*false-variable*
 falsy	eval.txt	/*falsy*
@@ -7399,6 +7402,7 @@ g:tex_subscripts	syntax.txt	/*g:tex_subs
 g:tex_superscripts	syntax.txt	/*g:tex_superscripts*
 g:tex_verbspell	syntax.txt	/*g:tex_verbspell*
 g:var	eval.txt	/*g:var*
+g:vim_indent	indent.txt	/*g:vim_indent*
 g:vim_indent_cont	indent.txt	/*g:vim_indent_cont*
 g:vimball_home	pi_vimball.txt	/*g:vimball_home*
 g:vimball_mkdir	pi_vimball.txt	/*g:vimball_mkdir*
@@ -9173,6 +9177,7 @@ pythonx	if_pyth.txt	/*pythonx*
 pythonx-directory	if_pyth.txt	/*pythonx-directory*
 pyxeval()	builtin.txt	/*pyxeval()*
 q	repeat.txt	/*q*
+q-args-example	map.txt	/*q-args-example*
 q/	cmdline.txt	/*q\/*
 q:	cmdline.txt	/*q:*
 q?	cmdline.txt	/*q?*
--- a/runtime/doc/textprop.txt
+++ b/runtime/doc/textprop.txt
@@ -1,4 +1,4 @@
-*textprop.txt*  For Vim version 9.0.  Last change: 2022 Sep 17
+*textprop.txt*  For Vim version 9.0.  Last change: 2022 Sep 21
 
 
 		  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -143,10 +143,11 @@ prop_add({lnum}, {col}, {props})
 				automatically to a negative number; otherwise
 				zero is used
 							*E1305*
-		   text		text to be displayed before {col}, or after the
-				line if {col} is zero; prepend and/or append
-				spaces for padding with highlighting; cannot
-				be used with "length", "end_lnum" and "end_col"
+		   text		text to be displayed before {col}, or
+				above/below the line if {col} is zero; prepend
+				and/or append spaces for padding with
+				highlighting; cannot be used with "length",
+				"end_lnum" and "end_col" |virtual-text|
 		   					*E1294*
 		   text_align	when "text" is present and {col} is zero;
 				specifies where to display the text:
@@ -191,12 +192,23 @@ prop_add({lnum}, {col}, {props})
 		If not found an error is given.
 							*virtual-text*
 		When "text" is used and the column is non-zero then this text
-		will be displayed at the start location of the text property
-		after the text.  The text of the buffer line will be shifted
-		to make room.  This is called "virtual text".
-		When the column is zero the virtual text will appear after the
-		buffer text.  The "text_align" and "text_wrap" arguments
-		determine how it is displayed.
+		will be displayed at the specified start location of the text
+		property.  The text of the buffer line will be shifted to make
+		room.  This is called "virtual text".
+		When the column is zero the virtual text will appear above,
+		after or below the buffer text.  The "text_align" and
+		"text_wrap" arguments determine how it is displayed.
+		To separate the virtual text from the buffer text prepend
+		and/or append spaces to the "text" field or use the
+		"text_padding_left" value.
+
+		Make sure to use a highlight that makes clear to the user that
+		this is virtual text, otherwise it will be very confusing that
+		the text cannot be edited.  When using "above" you need to
+		make clear this text belongs to the text line below it, when
+		using "below" you need to make sure it belongs to the text
+		line above it.
+
 		The text will be displayed but it is not part of the actual
 		buffer line, the cursor cannot be placed on it.  A mouse click
 		in the text will move the cursor to the first character after
@@ -208,11 +220,6 @@ prop_add({lnum}, {col}, {props})
 		property with "text" has been added for a buffer then using a
 		negative "id" for any other property will give an error:
 		*E1293*
-		Make sure to use a highlight that makes clear to the user that
-		this is virtual text, otherwise it will be very confusing that
-		the text cannot be edited.
-		To separate the virtual text from the buffer text prepend
-		and/or append spaces to the "text" field.
 
 		Can also be used as a |method|: >
 			GetLnum()->prop_add(col, props)
--- a/runtime/doc/todo.txt
+++ b/runtime/doc/todo.txt
@@ -1,4 +1,4 @@
-*todo.txt*      For Vim version 9.0.  Last change: 2022 Sep 18
+*todo.txt*      For Vim version 9.0.  Last change: 2022 Sep 27
 
 
 		  VIM REFERENCE MANUAL	  by Bram Moolenaar
@@ -38,49 +38,21 @@ browser use: https://github.com/vim/vim/
 							*known-bugs*
 -------------------- Known bugs and current work -----------------------
 
-Closure created in for loop can use loop variable?  #11094
-    Nested loops do not work correctly yet.
-    Would need to save vars for each block separately.
-
-Virtual text:
-- Virtual text below: padding is highlighted when 'number' is set  #11138
-- Virtual text above: do not highlight until end of line?  #11138
-- 'number' should be below "above" virtual text?  Might be difficult to
-  implement.
-- Add highlight for the gap before/after virtual text above/below?
-- option to hide virtual text?
-
-Fail with valgrind: test_edit
-Found errors in Test_edit_insertmode_ex_edit():
-        Run 1, 01:19:51 - 01:20:01:
-        command line..script /home/mool/vim/vim90/src/testdir/runtest.vim[469]..function RunTheTest[44]..Test_edit_insertmode_ex_edit[13]..WaitForAssert[2]..<SNR>6_WaitForCommon[11]..<lambda>4 line 1: Pattern '^-- INSERT --\\s*$' does not match ''
-
 From test_global
         Found errors in Test_interrupt_global():
         Run 1, 02:16:22 - 02:16:27:
         command line..script /home/mool/vim/vim90/src/testdir/runtest.vim[469]..function RunTheTest[44]..Test_interrupt_global[13]..WaitForAssert[2]..<SNR>7_WaitForCommon[11]..<lambda>20 line 1: Pattern 'Interrupted' does not match 'Type  :qa!  and press...l changes and exit Vim           1,1           All'
         command line..script /home/mool/vim/vim90/src/testdir/runtest.vim[469]..function RunTheTest[44]..Test_interrupt_global[20]..WaitForAssert[2]..<SNR>7_WaitForCommon[11]..<lambda>21 line 1: Pattern 'Interrupted' does not match 'Entering Ex mode.  Type "visual" to go to Normal mode.'
 
-test_terminal3:
-Conditional jump or move depends on uninitialised value(s)
-==2819005==    at 0x2E9134: jump_to_mouse (mouse.c:2015)
-==2819005==    by 0x2E69E6: do_mouse (mouse.c:702)
-==2819005==    by 0x2E95C2: nv_mouse (mouse.c:2166)
-
-option_set():  "get a bit too much"
-    - refactor to separate function
-    - check for empty result
-
 Use :defer command:
     - Use "D" flag of writefile() and mkdir() in tests.
-	(testdir/test_c*.vim done)
-
-When using :echomessage do use msg_row and msg_col, but save and restore.
-How to test any failure?  If nothing fails perhaps it's OK alrady.
-
-New Vim indent script: #11079  Not done yet.
+	(testdir/test_e*.vim done)
+
+New Vim indent script: #11079  OK? 
 
 Further Vim9 improvements, possibly after launch:
+- For map(), reduce() and filter() use a specific implementation if the second
+  argument is a compiled function.  #11163
 - Use Vim9 for more runtime files.
 - Check performance with callgrind and kcachegrind.
     getline()/substitute()/setline() in #5632
@@ -228,6 +200,9 @@ reduced?
 
 Add BufDeletePost.  #11041
 
+Test property disappears when using CR twice in a row.  OK when some text was
+entered. (#11151)
+
 Add a string to the 'display' option ("smoothscroll" ?) to make CTRL-E and
 CTRL-Y scroll one screen line, also if this means the first line doesn't start
 with the first character (like what happens with a last line that doesn't
@@ -312,7 +287,9 @@ Adding "10" to 'spellsuggest' causes spe
 Also, z= in German on a long word can take a very long time, but CTRL-C to
 interrupt does not work. Where to add ui_breakcheck()?
 New English spell files also have very slow suggestions.
+
 French spell files don't work correctly.  #4916
+    Make Vim understand the format somehow?
 
 Make "g>" and "g<" in Visual mode move the text right or left.
 Also for a block selection.  #8558
--- a/runtime/doc/various.txt
+++ b/runtime/doc/various.txt
@@ -1,4 +1,4 @@
-*various.txt*   For Vim version 9.0.  Last change: 2022 Sep 17
+*various.txt*   For Vim version 9.0.  Last change: 2022 Sep 19
 
 
 		  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -29,6 +29,8 @@ CTRL-L			Clear and redraw the screen.  T
 			Useful to update the status line(s) when 'statusline'
 			includes an item that doesn't cause automatic
 			updating.
+			If the command line is being edited the redraw is
+			postponed until later.
 
 						*:redrawt* *:redrawtabline*
 :redrawt[abline]	Redraw the tabline.  Useful to update the tabline when
--- a/runtime/doc/vim9.txt
+++ b/runtime/doc/vim9.txt
@@ -1,4 +1,4 @@
-*vim9.txt*	For Vim version 9.0.  Last change: 2022 Sep 15
+*vim9.txt*	For Vim version 9.0.  Last change: 2022 Sep 19
 
 
 		  VIM REFERENCE MANUAL	  by Bram Moolenaar
@@ -962,6 +962,8 @@ In compiled Vim9 script you get:
 	3
 Generally, you should not change the list that is iterated over.  Make a copy
 first if needed.
+							*E1306*
+The depth of loops, :for and :while loops added together, cannot exceed 10.
 
 
 Conditions and expressions ~
--- a/runtime/filetype.vim
+++ b/runtime/filetype.vim
@@ -1,7 +1,7 @@
 " Vim support file to detect file types
 "
 " Maintainer:	Bram Moolenaar <Bram@vim.org>
-" Last Change:	2022 Sep 11
+" Last Change:	2022 Sep 27
 
 " Listen very carefully, I will say this only once
 if exists("did_load_filetypes")
new file mode 100644
--- /dev/null
+++ b/runtime/ftplugin/chatito.vim
@@ -0,0 +1,15 @@
+" Vim filetype plugin
+" Language:	Chatito
+" Maintainer:	ObserverOfTime <chronobserver@disroot.org>
+" Last Change:	2022 Sep 19
+
+if exists('b:did_ftplugin')
+  finish
+endif
+let b:did_ftplugin = 1
+
+setlocal comments=:#,:// commentstring=#\ %s
+" indent of 4 spaces is mandated by the spec
+setlocal expandtab softtabstop=4 shiftwidth=4
+
+let b:undo_ftplugin = 'setl com< cms< et< sts< sw<'
--- a/runtime/ftplugin/elixir.vim
+++ b/runtime/ftplugin/elixir.vim
@@ -1,7 +1,7 @@
 " Elixir filetype plugin
 " Language: Elixir
 " Maintainer:	Mitchell Hanberg <vimNOSPAM@mitchellhanberg.com>
-" Last Change: 2022 August 10
+" Last Change: 2022 Sep 20
 
 if exists("b:did_ftplugin")
   finish
@@ -23,7 +23,11 @@ if exists('loaded_matchit') && !exists('
         \ ',{:},\[:\],(:)'
 endif
 
+setlocal shiftwidth=2 softtabstop=2 expandtab iskeyword+=!,?
+setlocal comments=:#
 setlocal commentstring=#\ %s
 
+let b:undo_ftplugin = 'setlocal sw< sts< et< isk< com< cms<'
+
 let &cpo = s:save_cpo
 unlet s:save_cpo
new file mode 100644
--- /dev/null
+++ b/runtime/ftplugin/gyp.vim
@@ -0,0 +1,14 @@
+" Vim filetype plugin
+" Language:	GYP
+" Maintainer:	ObserverOfTime <chronobserver@disroot.org>
+" Last Change:	2022 Sep 27
+
+if exists('b:did_ftplugin')
+  finish
+endif
+let b:did_ftplugin = 1
+
+setlocal formatoptions-=t
+setlocal commentstring=#\ %s comments=b:#,fb:-
+
+let b:undo_ftplugin = 'setlocal fo< cms< com<'
new file mode 100644
--- /dev/null
+++ b/runtime/ftplugin/hare.vim
@@ -0,0 +1,27 @@
+" Vim filetype plugin
+" Language: Hare
+" Maintainer: Amelia Clarke <me@rsaihe.dev>
+" Previous Maintainer: Drew DeVault <sir@cmpwn.com>
+" Last Updated: 2022-09-21
+
+" Only do this when not done yet for this buffer
+if exists('b:did_ftplugin')
+  finish
+endif
+
+" Don't load another plugin for this buffer
+let b:did_ftplugin = 1
+
+setlocal noexpandtab
+setlocal tabstop=8
+setlocal shiftwidth=0
+setlocal softtabstop=0
+setlocal textwidth=80
+setlocal commentstring=//\ %s
+
+" Set 'formatoptions' to break comment lines but not other lines,
+" and insert the comment leader when hitting <CR> or using "o".
+setlocal fo-=t fo+=croql
+
+compiler hare
+" vim: tabstop=2 shiftwidth=2 expandtab
new file mode 100644
--- /dev/null
+++ b/runtime/ftplugin/heex.vim
@@ -0,0 +1,16 @@
+" Elixir filetype plugin
+" Language: HEEx
+" Maintainer:	Mitchell Hanberg <vimNOSPAM@mitchellhanberg.com>
+" Last Change: 2022 Sep 21
+
+if exists("b:did_ftplugin")
+  finish
+endif
+let b:did_ftplugin = 1
+
+setlocal shiftwidth=2 softtabstop=2 expandtab
+
+setlocal comments=:<%!--
+setlocal commentstring=<%!--\ %s\ --%>
+
+let b:undo_ftplugin = 'set sw< sts< et< com< cms<'
--- a/runtime/ftplugin/vim.vim
+++ b/runtime/ftplugin/vim.vim
@@ -1,7 +1,7 @@
 " Vim filetype plugin
 " Language:	Vim
 " Maintainer:	Bram Moolenaar <Bram@vim.org>
-" Last Change:	2022 Sep 09
+" Last Change:	2022 Sep 20
 
 " Only do this when not done yet for this buffer
 if exists("b:did_ftplugin")
@@ -74,14 +74,14 @@ if !exists("no_plugin_maps") && !exists(
   let b:did_add_maps = 1
 
   " Move around functions.
-  nnoremap <silent><buffer> [[ m':call search('^\s*\(fu\%[nction]\\|def\)\>', "bW")<CR>
-  vnoremap <silent><buffer> [[ m':<C-U>exe "normal! gv"<Bar>call search('^\s*\(fu\%[nction]\\|def\)\>', "bW")<CR>
-  nnoremap <silent><buffer> ]] m':call search('^\s*\(fu\%[nction]\\|def\)\>', "W")<CR>
-  vnoremap <silent><buffer> ]] m':<C-U>exe "normal! gv"<Bar>call search('^\s*\(fu\%[nction]\\|def\)\>', "W")<CR>
-  nnoremap <silent><buffer> [] m':call search('^\s*end\(f\%[unction]\\|def\)\>', "bW")<CR>
-  vnoremap <silent><buffer> [] m':<C-U>exe "normal! gv"<Bar>call search('^\s*end\(f\%[unction]\\|def\)\>', "bW")<CR>
-  nnoremap <silent><buffer> ][ m':call search('^\s*end\(f\%[unction]\\|def\)\>', "W")<CR>
-  vnoremap <silent><buffer> ][ m':<C-U>exe "normal! gv"<Bar>call search('^\s*end\(f\%[unction]\\|def\)\>', "W")<CR>
+  nnoremap <silent><buffer> [[ m':call search('^\s*\(fu\%[nction]\\|\(export\s\+\)\?def\)\>', "bW")<CR>
+  vnoremap <silent><buffer> [[ m':<C-U>exe "normal! gv"<Bar>call search('^\s*\(fu\%[nction]\\|\(export\s\+\)\?def\)\>', "bW")<CR>
+  nnoremap <silent><buffer> ]] m':call search('^\s*\(fu\%[nction]\\|\(export\s\+\)\?def\)\>', "W")<CR>
+  vnoremap <silent><buffer> ]] m':<C-U>exe "normal! gv"<Bar>call search('^\s*\(fu\%[nction]\\|\(export\s\+\)\?def\)\>', "W")<CR>
+  nnoremap <silent><buffer> [] m':call search('^\s*end\(f\%[unction]\\|\(export\s\+\)\?def\)\>', "bW")<CR>
+  vnoremap <silent><buffer> [] m':<C-U>exe "normal! gv"<Bar>call search('^\s*end\(f\%[unction]\\|\(export\s\+\)\?def\)\>', "bW")<CR>
+  nnoremap <silent><buffer> ][ m':call search('^\s*end\(f\%[unction]\\|\(export\s\+\)\?def\)\>', "W")<CR>
+  vnoremap <silent><buffer> ][ m':<C-U>exe "normal! gv"<Bar>call search('^\s*end\(f\%[unction]\\|\(export\s\+\)\?def\)\>', "W")<CR>
 
   " Move around comments
   nnoremap <silent><buffer> ]" :call search('^\(\s*".*\n\)\@<!\(\s*"\)', "W")<CR>
new file mode 100644
--- /dev/null
+++ b/runtime/indent/chatito.vim
@@ -0,0 +1,32 @@
+" Vim indent file
+" Language:	Chatito
+" Maintainer:	ObserverOfTime <chronobserver@disroot.org>
+" Last Change:	2022 Sep 20
+
+if exists('b:did_indent')
+    finish
+endif
+let b:did_indent = 1
+
+setlocal indentexpr=GetChatitoIndent()
+setlocal indentkeys=o,O,*<Return>,0#,!^F
+
+let b:undo_indent = 'setl inde< indk<'
+
+if exists('*GetChatitoIndent')
+    finish
+endif
+
+function GetChatitoIndent()
+    let l:prev = v:lnum - 1
+    if getline(prevnonblank(l:prev)) =~# '^[~%@]\['
+        " shift indent after definitions
+        return shiftwidth()
+    elseif getline(l:prev) !~# '^\s*$'
+        " maintain indent in sentences
+        return indent(l:prev)
+    else
+        " reset indent after a blank line
+        return 0
+    end
+endfunction
new file mode 100644
--- /dev/null
+++ b/runtime/indent/gyp.vim
@@ -0,0 +1,7 @@
+" Vim indent file
+" Language:	GYP
+" Maintainer:	ObserverOfTime <chronobserver@disroot.org>
+" Last Change:	2022 Sep 27
+
+" JSON indent works well
+runtime! indent/json.vim
new file mode 100644
--- /dev/null
+++ b/runtime/indent/hare.vim
@@ -0,0 +1,138 @@
+" Vim indent file
+" Language: Hare
+" Maintainer: Amelia Clarke <me@rsaihe.dev>
+" Last Change: 2022 Sep 22
+
+if exists("b:did_indent")
+  finish
+endif
+let b:did_indent = 1
+
+if !has("cindent") || !has("eval")
+  finish
+endif
+
+setlocal cindent
+
+" L0 -> don't deindent labels
+" (s -> use one indent after a trailing (
+" m1 -> if ) starts a line, indent it the same as its matching (
+" ks -> add an extra indent to extra lines in an if expression or for expression
+" j1 -> indent code inside {} one level when in parentheses
+" J1 -> see j1
+" *0 -> don't search for unclosed block comments
+" #1 -> don't deindent lines that begin with #
+setlocal cinoptions=L0,(s,m1,ks,j1,J1,*0,#1
+
+" Controls which keys reindent the current line.
+" 0{     -> { at beginning of line
+" 0}     -> } at beginning of line
+" 0)     -> ) at beginning of line
+" 0]     -> ] at beginning of line
+" !^F    -> <C-f> (not inserted)
+" o      -> <CR> or `o` command
+" O      -> `O` command
+" e      -> else
+" 0=case -> case
+setlocal indentkeys=0{,0},0),0],!^F,o,O,e,0=case
+
+setlocal cinwords=if,else,for,switch,match
+
+setlocal indentexpr=GetHareIndent()
+
+function! FloorCindent(lnum)
+  return cindent(a:lnum) / shiftwidth() * shiftwidth()
+endfunction
+
+function! GetHareIndent()
+  let line = getline(v:lnum)
+  let prevlnum = prevnonblank(v:lnum - 1)
+  let prevline = getline(prevlnum)
+  let prevprevline = getline(prevnonblank(prevlnum - 1))
+
+  " This is all very hacky and imperfect, but it's tough to do much better when
+  " working with regex-based indenting rules.
+
+  " If the previous line ended with =, indent by one shiftwidth.
+  if prevline =~# '\v\=\s*(//.*)?$'
+    return indent(prevlnum) + shiftwidth()
+  endif
+
+  " If the previous line ended in a semicolon and the line before that ended
+  " with =, deindent by one shiftwidth.
+  if prevline =~# '\v;\s*(//.*)?$' && prevprevline =~# '\v\=\s*(//.*)?$'
+    return indent(prevlnum) - shiftwidth()
+  endif
+
+  " TODO: The following edge-case is still indented incorrectly:
+  " case =>
+  "         if (foo) {
+  "                 bar;
+  "         };
+  " | // cursor is incorrectly deindented by one shiftwidth.
+  "
+  " This only happens if the {} block is the first statement in the case body.
+  " If `case` is typed, the case will also be incorrectly deindented by one
+  " shiftwidth. Are you having fun yet?
+
+  " Deindent cases.
+  if line =~# '\v^\s*case'
+    " If the previous line was also a case, don't do any special indenting.
+    if prevline =~# '\v^\s*case'
+      return indent(prevlnum)
+    end
+
+    " If the previous line was a multiline case, deindent by one shiftwidth.
+    if prevline =~# '\v\=\>\s*(//.*)?$'
+      return indent(prevlnum) - shiftwidth()
+    endif
+
+    " If the previous line started a block, deindent by one shiftwidth.
+    " This handles the first case in a switch/match block.
+    if prevline =~# '\v\{\s*(//.*)?$'
+      return FloorCindent(v:lnum) - shiftwidth()
+    end
+
+    " If the previous line ended in a semicolon and the line before that wasn't
+    " a case, deindent by one shiftwidth.
+    if prevline =~# '\v;\s*(//.*)?$' && prevprevline !~# '\v\=\>\s*(//.*)?$'
+      return FloorCindent(v:lnum) - shiftwidth()
+    end
+
+    let l:indent = FloorCindent(v:lnum)
+
+    " If a normal cindent would indent the same amount as the previous line,
+    " deindent by one shiftwidth. This fixes some issues with `case let` blocks.
+    if l:indent == indent(prevlnum)
+      return l:indent - shiftwidth()
+    endif
+
+    " Otherwise, do a normal cindent.
+    return l:indent
+  endif
+
+  " Don't indent an extra shiftwidth for cases which span multiple lines.
+  if prevline =~# '\v\=\>\s*(//.*)?$' && prevline !~# '\v^\s*case\W'
+    return indent(prevlnum)
+  endif
+
+  " 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*}'
+    return indent(prevlnum)
+  endif
+
+  let l:indent = FloorCindent(v:lnum)
+
+  " If the previous line was a case and a normal cindent wouldn't indent, indent
+  " an extra shiftwidth.
+  if prevline =~# '\v\=\>\s*(//.*)?$' && l:indent == indent(prevlnum)
+    return l:indent + shiftwidth()
+  endif
+
+  " If everything above is false, do a normal cindent.
+  return l:indent
+endfunction
+
+" vim: tabstop=2 shiftwidth=2 expandtab
new file mode 100644
--- /dev/null
+++ b/runtime/indent/solidity.vim
@@ -0,0 +1,442 @@
+" Vim indent file
+" Language: 		Solidity
+" Acknowledgement: 	Based off of vim-javascript
+" Maintainer: 		Cothi (jiungdev@gmail.com)
+" Original Author: 	tomlion (https://github.com/tomlion/vim-solidity)
+" Last Changed: 	2022 Sep 27
+"
+" 0. Initialization {{{1
+" =================
+
+" Only load this indent file when no other was loaded.
+if exists("b:did_indent")
+  finish
+endif
+let b:did_indent = 1
+
+setlocal nosmartindent
+
+" Now, set up our indentation expression and keys that trigger it.
+setlocal indentexpr=GetSolidityIndent()
+setlocal indentkeys=0{,0},0),0],0\,,!^F,o,O,e
+
+" Only define the function once.
+if exists("*GetSolidityIndent")
+  finish
+endif
+
+let s:cpo_save = &cpo
+set cpo&vim
+
+" 1. Variables {{{1
+" ============
+
+let s:js_keywords = '^\s*\(break\|case\|catch\|continue\|debugger\|default\|delete\|do\|else\|finally\|for\|function\|if\|in\|instanceof\|new\|return\|switch\|this\|throw\|try\|typeof\|var\|void\|while\|with\)'
+
+" Regex of syntax group names that are or delimit string or are comments.
+let s:syng_strcom = 'string\|regex\|comment\c'
+
+" Regex of syntax group names that are strings.
+let s:syng_string = 'regex\c'
+
+" Regex of syntax group names that are strings or documentation.
+let s:syng_multiline = 'comment\c'
+
+" Regex of syntax group names that are line comment.
+let s:syng_linecom = 'linecomment\c'
+
+" Expression used to check whether we should skip a match with searchpair().
+let s:skip_expr = "synIDattr(synID(line('.'),col('.'),1),'name') =~ '".s:syng_strcom."'"
+
+let s:line_term = '\s*\%(\%(\/\/\).*\)\=$'
+
+" Regex that defines continuation lines, not including (, {, or [.
+let s:continuation_regex = '\%([\\*+/.:]\|\%(<%\)\@<![=-]\|\W[|&?]\|||\|&&\)' . s:line_term
+
+" Regex that defines continuation lines.
+" TODO: this needs to deal with if ...: and so on
+let s:msl_regex = '\%([\\*+/.:([]\|\%(<%\)\@<![=-]\|\W[|&?]\|||\|&&\)' . s:line_term
+
+let s:one_line_scope_regex = '\<\%(if\|else\|for\|while\)\>[^{;]*' . s:line_term
+
+" Regex that defines blocks.
+let s:block_regex = '\%([{[]\)\s*\%(|\%([*@]\=\h\w*,\=\s*\)\%(,\s*[*@]\=\h\w*\)*|\)\=' . s:line_term
+
+let s:var_stmt = '^\s*var'
+
+let s:comma_first = '^\s*,'
+let s:comma_last = ',\s*$'
+
+let s:ternary = '^\s\+[?|:]'
+let s:ternary_q = '^\s\+?'
+
+" 2. Auxiliary Functions {{{1
+" ======================
+
+" Check if the character at lnum:col is inside a string, comment, or is ascii.
+function s:IsInStringOrComment(lnum, col)
+  return synIDattr(synID(a:lnum, a:col, 1), 'name') =~ s:syng_strcom
+endfunction
+
+" Check if the character at lnum:col is inside a string.
+function s:IsInString(lnum, col)
+  return synIDattr(synID(a:lnum, a:col, 1), 'name') =~ s:syng_string
+endfunction
+
+" Check if the character at lnum:col is inside a multi-line comment.
+function s:IsInMultilineComment(lnum, col)
+  return !s:IsLineComment(a:lnum, a:col) && synIDattr(synID(a:lnum, a:col, 1), 'name') =~ s:syng_multiline
+endfunction
+
+" Check if the character at lnum:col is a line comment.
+function s:IsLineComment(lnum, col)
+  return synIDattr(synID(a:lnum, a:col, 1), 'name') =~ s:syng_linecom
+endfunction
+
+" Find line above 'lnum' that isn't empty, in a comment, or in a string.
+function s:PrevNonBlankNonString(lnum)
+  let in_block = 0
+  let lnum = prevnonblank(a:lnum)
+  while lnum > 0
+    " Go in and out of blocks comments as necessary.
+    " If the line isn't empty (with opt. comment) or in a string, end search.
+    let line = getline(lnum)
+    if line =~ '/\*'
+      if in_block
+        let in_block = 0
+      else
+        break
+      endif
+    elseif !in_block && line =~ '\*/'
+      let in_block = 1
+    elseif !in_block && line !~ '^\s*\%(//\).*$' && !(s:IsInStringOrComment(lnum, 1) && s:IsInStringOrComment(lnum, strlen(line)))
+      break
+    endif
+    let lnum = prevnonblank(lnum - 1)
+  endwhile
+  return lnum
+endfunction
+
+" Find line above 'lnum' that started the continuation 'lnum' may be part of.
+function s:GetMSL(lnum, in_one_line_scope)
+  " Start on the line we're at and use its indent.
+  let msl = a:lnum
+  let lnum = s:PrevNonBlankNonString(a:lnum - 1)
+  while lnum > 0
+    " If we have a continuation line, or we're in a string, use line as MSL.
+    " Otherwise, terminate search as we have found our MSL already.
+    let line = getline(lnum)
+    let col = match(line, s:msl_regex) + 1
+    if (col > 0 && !s:IsInStringOrComment(lnum, col)) || s:IsInString(lnum, strlen(line))
+      let msl = lnum
+    else
+      " Don't use lines that are part of a one line scope as msl unless the
+      " flag in_one_line_scope is set to 1
+      "
+      if a:in_one_line_scope
+        break
+      end
+      let msl_one_line = s:Match(lnum, s:one_line_scope_regex)
+      if msl_one_line == 0
+        break
+      endif
+    endif
+    let lnum = s:PrevNonBlankNonString(lnum - 1)
+  endwhile
+  return msl
+endfunction
+
+function s:RemoveTrailingComments(content)
+  let single = '\/\/\(.*\)\s*$'
+  let multi = '\/\*\(.*\)\*\/\s*$'
+  return substitute(substitute(a:content, single, '', ''), multi, '', '')
+endfunction
+
+" Find if the string is inside var statement (but not the first string)
+function s:InMultiVarStatement(lnum)
+  let lnum = s:PrevNonBlankNonString(a:lnum - 1)
+
+"  let type = synIDattr(synID(lnum, indent(lnum) + 1, 0), 'name')
+
+  " loop through previous expressions to find a var statement
+  while lnum > 0
+    let line = getline(lnum)
+
+    " if the line is a js keyword
+    if (line =~ s:js_keywords)
+      " check if the line is a var stmt
+      " if the line has a comma first or comma last then we can assume that we
+      " are in a multiple var statement
+      if (line =~ s:var_stmt)
+        return lnum
+      endif
+
+      " other js keywords, not a var
+      return 0
+    endif
+
+    let lnum = s:PrevNonBlankNonString(lnum - 1)
+  endwhile
+
+  " beginning of program, not a var
+  return 0
+endfunction
+
+" Find line above with beginning of the var statement or returns 0 if it's not
+" this statement
+function s:GetVarIndent(lnum)
+  let lvar = s:InMultiVarStatement(a:lnum)
+  let prev_lnum = s:PrevNonBlankNonString(a:lnum - 1)
+
+  if lvar
+    let line = s:RemoveTrailingComments(getline(prev_lnum))
+
+    " if the previous line doesn't end in a comma, return to regular indent
+    if (line !~ s:comma_last)
+      return indent(prev_lnum) - &sw
+    else
+      return indent(lvar) + &sw
+    endif
+  endif
+
+  return -1
+endfunction
+
+
+" Check if line 'lnum' has more opening brackets than closing ones.
+function s:LineHasOpeningBrackets(lnum)
+  let open_0 = 0
+  let open_2 = 0
+  let open_4 = 0
+  let line = getline(a:lnum)
+  let pos = match(line, '[][(){}]', 0)
+  while pos != -1
+    if !s:IsInStringOrComment(a:lnum, pos + 1)
+      let idx = stridx('(){}[]', line[pos])
+      if idx % 2 == 0
+        let open_{idx} = open_{idx} + 1
+      else
+        let open_{idx - 1} = open_{idx - 1} - 1
+      endif
+    endif
+    let pos = match(line, '[][(){}]', pos + 1)
+  endwhile
+  return (open_0 > 0) . (open_2 > 0) . (open_4 > 0)
+endfunction
+
+function s:Match(lnum, regex)
+  let col = match(getline(a:lnum), a:regex) + 1
+  return col > 0 && !s:IsInStringOrComment(a:lnum, col) ? col : 0
+endfunction
+
+function s:IndentWithContinuation(lnum, ind, width)
+  " Set up variables to use and search for MSL to the previous line.
+  let p_lnum = a:lnum
+  let lnum = s:GetMSL(a:lnum, 1)
+  let line = getline(lnum)
+
+  " If the previous line wasn't a MSL and is continuation return its indent.
+  " TODO: the || s:IsInString() thing worries me a bit.
+  if p_lnum != lnum
+    if s:Match(p_lnum,s:continuation_regex)||s:IsInString(p_lnum,strlen(line))
+      return a:ind
+    endif
+  endif
+
+  " Set up more variables now that we know we aren't continuation bound.
+  let msl_ind = indent(lnum)
+
+  " If the previous line ended with [*+/.-=], start a continuation that
+  " indents an extra level.
+  if s:Match(lnum, s:continuation_regex)
+    if lnum == p_lnum
+      return msl_ind + a:width
+    else
+      return msl_ind
+    endif
+  endif
+
+  return a:ind
+endfunction
+
+function s:InOneLineScope(lnum)
+  let msl = s:GetMSL(a:lnum, 1)
+  if msl > 0 && s:Match(msl, s:one_line_scope_regex)
+    return msl
+  endif
+  return 0
+endfunction
+
+function s:ExitingOneLineScope(lnum)
+  let msl = s:GetMSL(a:lnum, 1)
+  if msl > 0
+    " if the current line is in a one line scope ..
+    if s:Match(msl, s:one_line_scope_regex)
+      return 0
+    else
+      let prev_msl = s:GetMSL(msl - 1, 1)
+      if s:Match(prev_msl, s:one_line_scope_regex)
+        return prev_msl
+      endif
+    endif
+  endif
+  return 0
+endfunction
+
+" 3. GetSolidityIndent Function {{{1
+" =========================
+
+function GetSolidityIndent()
+  " 3.1. Setup {{{2
+  " ----------
+
+  " Set up variables for restoring position in file.  Could use v:lnum here.
+  let vcol = col('.')
+
+  " 3.2. Work on the current line {{{2
+  " -----------------------------
+
+  let ind = -1
+  " Get the current line.
+  let line = getline(v:lnum)
+  " previous nonblank line number
+  let prevline = prevnonblank(v:lnum - 1)
+
+  " If we got a closing bracket on an empty line, find its match and indent
+  " according to it.  For parentheses we indent to its column - 1, for the
+  " others we indent to the containing line's MSL's level.  Return -1 if fail.
+  let col = matchend(line, '^\s*[],})]')
+  if col > 0 && !s:IsInStringOrComment(v:lnum, col)
+    call cursor(v:lnum, col)
+
+    let lvar = s:InMultiVarStatement(v:lnum)
+    if lvar
+      let prevline_contents = s:RemoveTrailingComments(getline(prevline))
+
+      " check for comma first
+      if (line[col - 1] =~ ',')
+        " if the previous line ends in comma or semicolon don't indent
+        if (prevline_contents =~ '[;,]\s*$')
+          return indent(s:GetMSL(line('.'), 0))
+        " get previous line indent, if it's comma first return prevline indent
+        elseif (prevline_contents =~ s:comma_first)
+          return indent(prevline)
+        " otherwise we indent 1 level
+        else
+          return indent(lvar) + &sw
+        endif
+      endif
+    endif
+
+
+    let bs = strpart('(){}[]', stridx(')}]', line[col - 1]) * 2, 2)
+    if searchpair(escape(bs[0], '\['), '', bs[1], 'bW', s:skip_expr) > 0
+      if line[col-1]==')' && col('.') != col('$') - 1
+        let ind = virtcol('.')-1
+      else
+        let ind = indent(s:GetMSL(line('.'), 0))
+      endif
+    endif
+    return ind
+  endif
+
+  " If the line is comma first, dedent 1 level
+  if (getline(prevline) =~ s:comma_first)
+    return indent(prevline) - &sw
+  endif
+
+  if (line =~ s:ternary)
+    if (getline(prevline) =~ s:ternary_q)
+      return indent(prevline)
+    else
+      return indent(prevline) + &sw
+    endif
+  endif
+
+  " If we are in a multi-line comment, cindent does the right thing.
+  if s:IsInMultilineComment(v:lnum, 1) && !s:IsLineComment(v:lnum, 1)
+    return cindent(v:lnum)
+  endif
+
+  " Check for multiple var assignments
+"  let var_indent = s:GetVarIndent(v:lnum)
+"  if var_indent >= 0
+"    return var_indent
+"  endif
+
+  " 3.3. Work on the previous line. {{{2
+  " -------------------------------
+
+  " If the line is empty and the previous nonblank line was a multi-line
+  " comment, use that comment's indent. Deduct one char to account for the
+  " space in ' */'.
+  if line =~ '^\s*$' && s:IsInMultilineComment(prevline, 1)
+    return indent(prevline) - 1
+  endif
+
+  " Find a non-blank, non-multi-line string line above the current line.
+  let lnum = s:PrevNonBlankNonString(v:lnum - 1)
+
+  " If the line is empty and inside a string, use the previous line.
+  if line =~ '^\s*$' && lnum != prevline
+    return indent(prevnonblank(v:lnum))
+  endif
+
+  " At the start of the file use zero indent.
+  if lnum == 0
+    return 0
+  endif
+
+  " Set up variables for current line.
+  let line = getline(lnum)
+  let ind = indent(lnum)
+
+  " If the previous line ended with a block opening, add a level of indent.
+  if s:Match(lnum, s:block_regex)
+    return indent(s:GetMSL(lnum, 0)) + &sw
+  endif
+
+  " If the previous line contained an opening bracket, and we are still in it,
+  " add indent depending on the bracket type.
+  if line =~ '[[({]'
+    let counts = s:LineHasOpeningBrackets(lnum)
+    if counts[0] == '1' && searchpair('(', '', ')', 'bW', s:skip_expr) > 0
+      if col('.') + 1 == col('$')
+        return ind + &sw
+      else
+        return virtcol('.')
+      endif
+    elseif counts[1] == '1' || counts[2] == '1'
+      return ind + &sw
+    else
+      call cursor(v:lnum, vcol)
+    end
+  endif
+
+  " 3.4. Work on the MSL line. {{{2
+  " --------------------------
+
+  let ind_con = ind
+  let ind = s:IndentWithContinuation(lnum, ind_con, &sw)
+
+  " }}}2
+  "
+  "
+  let ols = s:InOneLineScope(lnum)
+  if ols > 0
+    let ind = ind + &sw
+  else
+    let ols = s:ExitingOneLineScope(lnum)
+    while ols > 0 && ind > 0
+      let ind = ind - &sw
+      let ols = s:InOneLineScope(ols - 1)
+    endwhile
+  endif
+
+  return ind
+endfunction
+
+" }}}1
+
+let &cpo = s:cpo_save
+unlet s:cpo_save
--- a/runtime/indent/testdir/vim.in
+++ b/runtime/indent/testdir/vim.in
@@ -1,7 +1,6 @@
 " vim: set ft=vim sw=4 :
 
 " START_INDENT
-
 func Some()
 let x = 1
 endfunc
@@ -15,15 +14,6 @@ let x = [
 \ ]
 endif
 
-" TODO: add searchpair() to find matching {
-"for x in [
-"{
-"key: 'value'
-"},
-"]
-"eval 0
-"endfor
-
 for x in [
 {key: 'value'},
 ]
@@ -37,12 +27,11 @@ let t = [
 \  ]
 
 def Func()
-  var d = dd
-	->extend({
-	})
-  eval 0
+var d = dd
+->extend({
+})
+eval 0
 enddef
-
 " END_INDENT
 
 " START_INDENT
@@ -84,7 +73,7 @@ endfunc
 " START_INDENT
 " INDENT_NEXT  next-line
 func Some()
-     " next-line
+    " next-line
 let f = x
 endfunc
 " END_INDENT
@@ -104,6 +93,12 @@ END
 " END_INDENT
 
 " START_INDENT
+let a =<< trim END
+nothing
+END
+" END_INDENT
+
+" START_INDENT
 " INDENT_AT  this-line
 let a=<< trim END
    blah
@@ -111,3 +106,754 @@ let a=<< trim END
       blah this-line
 END
 " END_INDENT
+
+" START_INDENT
+if v:true
+echo 0
+end
+" END_INDENT
+
+" START_INDENT
+var result = Func(
+arg1,
+arg2
+)
+" END_INDENT
+
+" START_INDENT
+var result = Func(arg1,
+arg2)
+" END_INDENT
+
+" START_INDENT
+filter(list, (k, v) =>
+v > 0)
+" END_INDENT
+
+" START_INDENT
+filter(list, (k, v) => {
+const x = get(list, k, 0)
+return x > 0
+})
+" END_INDENT
+
+" START_INDENT
+if x > 0
+filter(list, (k, v) => {
+const x = get(list, k, 1)
+return x > 0
+})
+endif
+" END_INDENT
+
+" START_INDENT
+{
+var temp = 'temp'
+}
+" END_INDENT
+
+" START_INDENT
+var text = lead
+.. middle
+.. end
+" END_INDENT
+
+" START_INDENT
+var text = lead ..
+middle ..
+end
+" END_INDENT
+
+" START_INDENT
+var total = start +
+end -
+correction
+" END_INDENT
+
+" START_INDENT
+var result = start
+:+ print
+" END_INDENT
+
+" START_INDENT
+var result = positive
+? PosFunc(arg)
+: NegFunc(arg)
+" END_INDENT
+
+" START_INDENT
+var result = GetBuilder()
+->BuilderSetWidth(333)
+->BuilderSetHeight(777)
+->BuilderBuild()
+" END_INDENT
+
+" START_INDENT
+var result = MyDict
+.member
+" END_INDENT
+
+" START_INDENT
+autocmd BufNewFile *.match if condition
+|   echo 'match'
+| endif
+" END_INDENT
+
+" START_INDENT
+set cpo+=C
+var lines =<< trim END
+| this works
+END
+set cpo-=C
+" END_INDENT
+
+" START_INDENT
+syn region Text
+\ start='foo'
+#\ comment
+\ end='bar'
+" END_INDENT
+
+" START_INDENT
+au CursorHold * echom 'BEFORE bar'
+#\ some comment
+| echom 'AFTER bar'
+" END_INDENT
+
+" START_INDENT
+def MyFunc(text: string,
+separator = '-'
+): string
+enddef
+" END_INDENT
+
+" START_INDENT
+def MyFunc(
+text: string,
+separator = '-'
+): string
+enddef
+" END_INDENT
+
+" START_INDENT
+[var1, var2] =
+Func()
+" END_INDENT
+
+" START_INDENT
+const list = ['one',
+'two']
+" END_INDENT
+
+" START_INDENT
+const list = [
+'one',
+'two',
+]
+" END_INDENT
+
+" START_INDENT
+const dict = {one: 1,
+two: 2
+}
+" END_INDENT
+
+" START_INDENT
+const dict = {
+one: 1,
+two: 2
+}
+" END_INDENT
+
+" START_INDENT
+if true
+const dict =
+{
+one: 1,
+two: 2
+}
+endif
+" END_INDENT
+
+" START_INDENT
+def Func()
+return {
+one: 1
+}
+enddef
+" END_INDENT
+
+" START_INDENT
+echo {
+a: 0,
+# b
+# c
+}
+" END_INDENT
+
+" START_INDENT
+echo search(
+# comment
+'1'
+.. '2'
+)
+" END_INDENT
+
+" START_INDENT
+if true
+var v = (      # trailing "(" starts line continuation
+3 + 4      # nothing special
+)              # end of expression indicates continued line
+var x: number  # needs to align with previous "var"
+endif
+" END_INDENT
+
+" START_INDENT
+def Func() # {{{
+# comment
+if true
+return
+endif
+enddef
+" END_INDENT
+
+" START_INDENT
+echo {
+key:
+'value',
+}
+" END_INDENT
+
+" START_INDENT
+var id = time
+->timer_start((_) => {
+n = 0
+})
+" END_INDENT
+
+" START_INDENT
+augroup Name
+autocmd!
+augroup END
+" END_INDENT
+
+" START_INDENT
+var n =
+# comment
+1
++ 2
+
+var s = ''
+" END_INDENT
+
+" START_INDENT
+var keys = {
+J: 'j',
+"\<Home>": '1G',
+"\<End>": 'G',
+z: 'zz'
+}
+" END_INDENT
+
+" START_INDENT
+export def Func(
+n: number,
+s: string,
+...l: list<bool>
+)
+enddef
+" END_INDENT
+
+" START_INDENT
+var heredoc =<< trim ENDD
+var nested_heredoc =<< trim END
+END
+ENDD
+" END_INDENT
+
+" START_INDENT
+if true
+else  " comment
+endif
+" END_INDENT
+
+" START_INDENT
+if true | echo 'one' | endif
+if true | echo 'two' | endif
+if true | echo 'three' | endif
+" END_INDENT
+
+" START_INDENT
+if true
+:'<-1 mark <
+else
+echo ''
+endif
+" END_INDENT
+
+" START_INDENT
+substitute/pat /rep /
+echo
+" END_INDENT
+
+" START_INDENT
+try
+echo 1
+catch /pat /  # comment
+echo 2
+endtry
+" END_INDENT
+
+" START_INDENT
+def Func()
+Cmd %
+enddef
+" END_INDENT
+
+" START_INDENT
+if end == 'xxx' || end == 'yyy'
+echo
+endif
+" END_INDENT
+
+" START_INDENT
+if true
+popup_move(id, {col: 1,
+line: 2})
+endif
+setwinvar(id, 'name', 3)
+" END_INDENT
+
+" START_INDENT
+var d = [
+{a: 'x',
+b: 'y'},
+FuncA(),
+FuncB(),
+]
+" END_INDENT
+
+" START_INDENT
+var ll = [[
+1,
+2,
+3], [
+4,
+5,
+6], [
+7,
+8,
+9]]
+" END_INDENT
+
+" START_INDENT
+var ld = [{
+a: 'xxx',
+b: 'yyy'}, {
+c: 'xxx',
+d: 'yyy'}, {
+e: 'xxx',
+f: 'yyy'}, {
+}]
+" END_INDENT
+
+" START_INDENT
+var d = {
+a: {
+b: {
+c: [{
+d: 'e',
+f: 'g',
+h: 'i'
+}],
+j: 'k',
+},
+},
+}
+" END_INDENT
+
+" START_INDENT
+if true
+var end: any
+if true
+end = 0
+elseif true
+echo
+endif
+endif
+" END_INDENT
+
+" START_INDENT
+nunmap <buffer> (
+nunmap <buffer> )
+inoremap [ {
+inoremap ] }
+silent! xunmap i{
+silent! xunmap a{
+" END_INDENT
+
+" START_INDENT
+def Func(
+s: string,
+n = 1,
+m = 2
+)
+enddef
+" END_INDENT
+
+" START_INDENT
+var h =<< END
+text
+END
+
+def Func()
+echo
+enddef
+" END_INDENT
+
+" START_INDENT
+def Func()
+var h =<< END
+text
+END
+echo 'test'
+enddef
+" END_INDENT
+
+" START_INDENT
+def Foo()
+lcd -
+enddef
+def Bar()
+echo
+enddef
+" END_INDENT
+
+" START_INDENT
+if true
+n = Func(1, 2,
+3)
+endif
+" END_INDENT
+
+" START_INDENT
+def Func(s: string,
+n: number): bool
+if true
+return false
+endif
+enddef
+" END_INDENT
+
+" START_INDENT
+def Func(
+n: number)
+#
+echo
+enddef
+" END_INDENT
+
+" START_INDENT
+" INDENT_AT  this-line
+def Func(
+	n: number)
+    #
+echo  # this-line
+enddef
+" END_INDENT
+
+" START_INDENT
+if true
+if true
+normal! ==
+endif
+endif
+" END_INDENT
+
+" START_INDENT
+var d = {
+a: () => true,
+b: () => true
+&& true
+&& Foo(),
+c: () => Bar(),
+e: () => Baz(),
+}
+" END_INDENT
+
+" START_INDENT
+def Select(Cont: func(func(any)), Pred: func(any): bool): func(func(any))
+return (Emit: func(any)) => {
+Cont((t: any) => {
+if Pred(t)
+Emit(t)
+endif
+})
+}
+enddef
+" END_INDENT
+
+" START_INDENT
+" INDENT_EXE let g:vim_indent = {'more_in_bracket_block': v:true}
+def Select(Cont: func(func(any)), Pred: func(any): bool): func(func(any))
+return (Emit: func(any)) => {
+Cont((t: any) => {
+if Pred(t)
+Emit(t)
+endif
+})
+}
+enddef
+" END_INDENT
+
+" START_INDENT
+" INDENT_EXE unlet! g:vim_indent
+" END_INDENT
+
+" START_INDENT
+g:lightline = {
+'active': {
+'left': [ [ 'mode', 'paste' ], [ 'readonly', 'relativepath', 'modified' ] ],
+},
+'inactive': {
+'left': [ [ 'readonly', 'relativepath', 'modified' ] ],
+}
+}
+" END_INDENT
+
+" START_INDENT
+if getline(1, 10)
+->map((_, v: string): number => strcharlen(v))
+->max() > 1'000
+&l:breakindent = false
+&l:linebreak = false
+else
+&l:breakindent = true
+&l:linebreak = true
+endif
+" END_INDENT
+
+" START_INDENT
+var ext2cmd: dict<string> = {
+doc: $'antiword {fname}',
+docx: $'pandoc --from=docx --to=markdown {fname}',
+epub: $'pandoc --from=epub --to=markdown {fname}',
+odp: $'odt2txt {fname}',
+odt: $'odt2txt {fname}',
+pdf: $'pdftotext -nopgbrk -layout -q -eol unix {fname} -',
+rtf: 'unrtf --text',
+}
+" END_INDENT
+
+" START_INDENT
+const ptybuf: number = term_start(&shell, {
+hidden: true,
+exit_cb: (_, _) => {
+if true
+close
+else
+help
+endif
+}
+})
+" END_INDENT
+
+" START_INDENT
+var d = {
+a: 0,
+# a ' quote {{{
+#}}}
+b: 0,
+}
+" END_INDENT
+
+" START_INDENT
+echo printf('%s () %s',
+1,
+2
+)
+" END_INDENT
+
+" START_INDENT
+prop_add(1, col('.'), {
+length: 2,
+type: 'test'
+})
+" END_INDENT
+
+" START_INDENT
+echo (() => " string starting with space")()
+echo
+" END_INDENT
+
+" START_INDENT
+var variables = deepcopy(g:)
+->filter((k: string, _): bool =>
+k =~ '\c\V' .. keyword->escape('\')
+&& k !~ '\%(loaded\|did_plugin_\)')
+->items()
+->map((_, v): string => v[0] .. ' = ' .. string(v[1]))
+new
+" END_INDENT
+
+" START_INDENT
+var d = freq
+->map((_, v) =>
+v * (
+1
++ 2
+))
+for item in d
+->items()
+->sort((a, b) => b[1] - a[1])
+echo
+endfor
+" END_INDENT
+
+" START_INDENT
+make_job = job_start([&shell, &shellcmdflag, make_cmd], {
+callback: function(MakeProcessOutput, [qfid]),
+close_cb: function(MakeCloseCb, [qfid]),
+exit_cb: MakeCompleted,
+in_io: 'null'
+})
+" END_INDENT
+
+" START_INDENT
+var matching_abbrev: list<dict<string>> = copy(ABBREV)
+->filter((_, v: dict<string>): bool =>
+stridx(v.lhs, word_to_complete) == 0)
+->map((_, v: dict<string>) => ({
+word: v.lhs,
+menu: AbbrevRhs(v.rhs)->stridx('expand_') >= 0
+?    AbbrevRhs(v.rhs)->matchstr('.*,\s*''\zs.*\ze'')')
+:    AbbrevRhs(v.rhs)
+}))
+" END_INDENT
+
+" START_INDENT
+def Func()
+if true
+vimgrep /^\C\s*\%(fu\%[nction]\|def\)\s\+/ file
+endif
+enddef
+" END_INDENT
+
+" START_INDENT
+setlocal iskeyword+=[
+cword = expand('<cword>')
+" END_INDENT
+
+" START_INDENT
+silent if true
+echo
+endif
+" END_INDENT
+
+" START_INDENT
+def Func()
+sort :^.*[\/]:
+enddef
+" END_INDENT
+
+" START_INDENT
+def Func()
+d = {
+}
+hd =<< trim END
+['
+]'
+END
+enddef
+" END_INDENT
+
+" START_INDENT
+def Func()
+if true
+var hd =<< trim END
+if get(b:, 'current_syntax', '')
+endif
+END
+elseif true
+echo
+endif
+enddef
+" END_INDENT
+
+" START_INDENT
+# test for control-flow keyword followed by commented fold marker {{{
+if true
+echo
+endif #}}}
+" END_INDENT
+
+" START_INDENT
+if winsz == 0|let winsz= ""|endif
+exe "noswapfile ".winsz."wincmd s"
+" END_INDENT
+
+" START_INDENT
+if true
+if true
+windo if true | echo | endif
+augroup Name
+autocmd WinLeave * if true | eval 1 + 2 | endif
+augroup END
+endif
+endif
+" END_INDENT
+
+" START_INDENT
+if true
+echo ' =<< trim END'
+->len()
+endif
+" END_INDENT
+
+" START_INDENT
+function Func()
+if true
+if true
+if true | echo com | endif
+if true | echo com | endif
+endif
+else
+endif
+endfunction
+" END_INDENT
+
+" START_INDENT
+function Func()
+if v:true
++
+echo
+-
+endif
+endfunction
+" END_INDENT
+
+" START_INDENT
+var matchpairs: string = &matchpairs
+var pairs: dict<list<string>>
+for [opening: string, closing: string]
+in matchpairs
+->split(',')
+->map((_, v: string): list<string> => split(v, ':'))
+pairs[opening] = [escape(opening, '[]'), escape(closing, '[]'),  'nW', 'w$']
+pairs[closing] = [escape(opening, '[]'), escape(closing, '[]'), 'bnW', 'w0']
+endfor
+" END_INDENT
+
+" START_INDENT
+{
+echo []
++ []
++ [{a: 1,
+b: 2}]
+}
+" END_INDENT
+
+" START_INDENT
+silent! argdel *
+edit file
+" END_INDENT
--- a/runtime/indent/testdir/vim.ok
+++ b/runtime/indent/testdir/vim.ok
@@ -1,7 +1,6 @@
 " vim: set ft=vim sw=4 :
 
 " START_INDENT
-
 func Some()
     let x = 1
 endfunc
@@ -15,15 +14,6 @@ if 1
 		\ ]
 endif
 
-" TODO: add searchpair() to find matching {
-"for x in [
-"{
-"key: 'value'
-"},
-"]
-"eval 0
-"endfor
-
 for x in [
 	{key: 'value'},
 	]
@@ -38,11 +28,10 @@ let t = [
 
 def Func()
     var d = dd
-		->extend({
-    })
+	->extend({
+	})
     eval 0
 enddef
-
 " END_INDENT
 
 " START_INDENT
@@ -69,7 +58,7 @@ let list = [
 let list = [
     'one',
     'two',
-    ]
+]
 echo
 
 " END_INDENT
@@ -84,8 +73,8 @@ endfunc
 " START_INDENT
 " INDENT_NEXT  next-line
 func Some()
-     " next-line
-     let f = x
+    " next-line
+    let f = x
 endfunc
 " END_INDENT
 
@@ -99,6 +88,12 @@ endfunc
 
 " START_INDENT
 let a =<< END
+nothing
+END
+" END_INDENT
+
+" START_INDENT
+let a =<< trim END
     nothing
 END
 " END_INDENT
@@ -111,3 +106,754 @@ let a=<< trim END
       blah this-line
 END
 " END_INDENT
+
+" START_INDENT
+if v:true
+    echo 0
+end
+" END_INDENT
+
+" START_INDENT
+var result = Func(
+    arg1,
+    arg2
+)
+" END_INDENT
+
+" START_INDENT
+var result = Func(arg1,
+    arg2)
+" END_INDENT
+
+" START_INDENT
+filter(list, (k, v) =>
+    v > 0)
+" END_INDENT
+
+" START_INDENT
+filter(list, (k, v) => {
+    const x = get(list, k, 0)
+    return x > 0
+})
+" END_INDENT
+
+" START_INDENT
+if x > 0
+    filter(list, (k, v) => {
+	const x = get(list, k, 1)
+	return x > 0
+    })
+endif
+" END_INDENT
+
+" START_INDENT
+{
+    var temp = 'temp'
+}
+" END_INDENT
+
+" START_INDENT
+var text = lead
+    .. middle
+    .. end
+" END_INDENT
+
+" START_INDENT
+var text = lead ..
+    middle ..
+    end
+" END_INDENT
+
+" START_INDENT
+var total = start +
+    end -
+    correction
+" END_INDENT
+
+" START_INDENT
+var result = start
+:+ print
+" END_INDENT
+
+" START_INDENT
+var result = positive
+    ? PosFunc(arg)
+    : NegFunc(arg)
+" END_INDENT
+
+" START_INDENT
+var result = GetBuilder()
+    ->BuilderSetWidth(333)
+    ->BuilderSetHeight(777)
+    ->BuilderBuild()
+" END_INDENT
+
+" START_INDENT
+var result = MyDict
+    .member
+" END_INDENT
+
+" START_INDENT
+autocmd BufNewFile *.match if condition
+    |   echo 'match'
+    | endif
+" END_INDENT
+
+" START_INDENT
+set cpo+=C
+var lines =<< trim END
+    | this works
+END
+set cpo-=C
+" END_INDENT
+
+" START_INDENT
+syn region Text
+	    \ start='foo'
+	    #\ comment
+	    \ end='bar'
+" END_INDENT
+
+" START_INDENT
+au CursorHold * echom 'BEFORE bar'
+    #\ some comment
+    | echom 'AFTER bar'
+" END_INDENT
+
+" START_INDENT
+def MyFunc(text: string,
+	separator = '-'
+	): string
+enddef
+" END_INDENT
+
+" START_INDENT
+def MyFunc(
+	text: string,
+	separator = '-'
+	): string
+enddef
+" END_INDENT
+
+" START_INDENT
+[var1, var2] =
+    Func()
+" END_INDENT
+
+" START_INDENT
+const list = ['one',
+    'two']
+" END_INDENT
+
+" START_INDENT
+const list = [
+    'one',
+    'two',
+]
+" END_INDENT
+
+" START_INDENT
+const dict = {one: 1,
+    two: 2
+}
+" END_INDENT
+
+" START_INDENT
+const dict = {
+    one: 1,
+    two: 2
+}
+" END_INDENT
+
+" START_INDENT
+if true
+    const dict =
+	{
+	    one: 1,
+	    two: 2
+	}
+endif
+" END_INDENT
+
+" START_INDENT
+def Func()
+    return {
+	one: 1
+    }
+enddef
+" END_INDENT
+
+" START_INDENT
+echo {
+    a: 0,
+    # b
+    # c
+}
+" END_INDENT
+
+" START_INDENT
+echo search(
+    # comment
+    '1'
+    .. '2'
+)
+" END_INDENT
+
+" START_INDENT
+if true
+    var v = (      # trailing "(" starts line continuation
+	3 + 4      # nothing special
+    )              # end of expression indicates continued line
+    var x: number  # needs to align with previous "var"
+endif
+" END_INDENT
+
+" START_INDENT
+def Func() # {{{
+    # comment
+    if true
+	return
+    endif
+enddef
+" END_INDENT
+
+" START_INDENT
+echo {
+    key:
+	'value',
+}
+" END_INDENT
+
+" START_INDENT
+var id = time
+    ->timer_start((_) => {
+	n = 0
+    })
+" END_INDENT
+
+" START_INDENT
+augroup Name
+    autocmd!
+augroup END
+" END_INDENT
+
+" START_INDENT
+var n =
+    # comment
+    1
+    + 2
+
+var s = ''
+" END_INDENT
+
+" START_INDENT
+var keys = {
+    J: 'j',
+    "\<Home>": '1G',
+    "\<End>": 'G',
+    z: 'zz'
+}
+" END_INDENT
+
+" START_INDENT
+export def Func(
+	n: number,
+	s: string,
+	...l: list<bool>
+	)
+enddef
+" END_INDENT
+
+" START_INDENT
+var heredoc =<< trim ENDD
+    var nested_heredoc =<< trim END
+    END
+ENDD
+" END_INDENT
+
+" START_INDENT
+if true
+else  " comment
+endif
+" END_INDENT
+
+" START_INDENT
+if true | echo 'one' | endif
+if true | echo 'two' | endif
+if true | echo 'three' | endif
+" END_INDENT
+
+" START_INDENT
+if true
+    :'<-1 mark <
+else
+    echo ''
+endif
+" END_INDENT
+
+" START_INDENT
+substitute/pat /rep /
+echo
+" END_INDENT
+
+" START_INDENT
+try
+    echo 1
+catch /pat /  # comment
+    echo 2
+endtry
+" END_INDENT
+
+" START_INDENT
+def Func()
+    Cmd %
+enddef
+" END_INDENT
+
+" START_INDENT
+if end == 'xxx' || end == 'yyy'
+    echo
+endif
+" END_INDENT
+
+" START_INDENT
+if true
+    popup_move(id, {col: 1,
+	line: 2})
+endif
+setwinvar(id, 'name', 3)
+" END_INDENT
+
+" START_INDENT
+var d = [
+    {a: 'x',
+	b: 'y'},
+    FuncA(),
+    FuncB(),
+]
+" END_INDENT
+
+" START_INDENT
+var ll = [[
+    1,
+    2,
+    3], [
+    4,
+    5,
+    6], [
+    7,
+    8,
+    9]]
+" END_INDENT
+
+" START_INDENT
+var ld = [{
+    a: 'xxx',
+    b: 'yyy'}, {
+    c: 'xxx',
+    d: 'yyy'}, {
+    e: 'xxx',
+    f: 'yyy'}, {
+    }]
+" END_INDENT
+
+" START_INDENT
+var d = {
+    a: {
+	b: {
+	    c: [{
+		d: 'e',
+		f: 'g',
+		h: 'i'
+	    }],
+	    j: 'k',
+	},
+    },
+}
+" END_INDENT
+
+" START_INDENT
+if true
+    var end: any
+    if true
+	end = 0
+    elseif true
+	echo
+    endif
+endif
+" END_INDENT
+
+" START_INDENT
+nunmap <buffer> (
+nunmap <buffer> )
+inoremap [ {
+inoremap ] }
+silent! xunmap i{
+silent! xunmap a{
+" END_INDENT
+
+" START_INDENT
+def Func(
+	s: string,
+	n = 1,
+	m = 2
+	)
+enddef
+" END_INDENT
+
+" START_INDENT
+var h =<< END
+text
+END
+
+def Func()
+    echo
+enddef
+" END_INDENT
+
+" START_INDENT
+def Func()
+    var h =<< END
+text
+END
+    echo 'test'
+enddef
+" END_INDENT
+
+" START_INDENT
+def Foo()
+    lcd -
+enddef
+def Bar()
+    echo
+enddef
+" END_INDENT
+
+" START_INDENT
+if true
+    n = Func(1, 2,
+	3)
+endif
+" END_INDENT
+
+" START_INDENT
+def Func(s: string,
+	n: number): bool
+    if true
+	return false
+    endif
+enddef
+" END_INDENT
+
+" START_INDENT
+def Func(
+	n: number)
+    #
+    echo
+enddef
+" END_INDENT
+
+" START_INDENT
+" INDENT_AT  this-line
+def Func(
+	n: number)
+    #
+    echo  # this-line
+enddef
+" END_INDENT
+
+" START_INDENT
+if true
+    if true
+	normal! ==
+    endif
+endif
+" END_INDENT
+
+" START_INDENT
+var d = {
+    a: () => true,
+    b: () => true
+	&& true
+	&& Foo(),
+    c: () => Bar(),
+    e: () => Baz(),
+}
+" END_INDENT
+
+" START_INDENT
+def Select(Cont: func(func(any)), Pred: func(any): bool): func(func(any))
+    return (Emit: func(any)) => {
+	Cont((t: any) => {
+	    if Pred(t)
+		Emit(t)
+	    endif
+	})
+    }
+enddef
+" END_INDENT
+
+" START_INDENT
+" INDENT_EXE let g:vim_indent = {'more_in_bracket_block': v:true}
+def Select(Cont: func(func(any)), Pred: func(any): bool): func(func(any))
+    return (Emit: func(any)) => {
+	    Cont((t: any) => {
+		    if Pred(t)
+			Emit(t)
+		    endif
+		})
+	}
+enddef
+" END_INDENT
+
+" START_INDENT
+" INDENT_EXE unlet! g:vim_indent
+" END_INDENT
+
+" START_INDENT
+g:lightline = {
+    'active': {
+	'left': [ [ 'mode', 'paste' ], [ 'readonly', 'relativepath', 'modified' ] ],
+    },
+    'inactive': {
+	'left': [ [ 'readonly', 'relativepath', 'modified' ] ],
+    }
+}
+" END_INDENT
+
+" START_INDENT
+if getline(1, 10)
+	->map((_, v: string): number => strcharlen(v))
+	->max() > 1'000
+    &l:breakindent = false
+    &l:linebreak = false
+else
+    &l:breakindent = true
+    &l:linebreak = true
+endif
+" END_INDENT
+
+" START_INDENT
+var ext2cmd: dict<string> = {
+    doc: $'antiword {fname}',
+    docx: $'pandoc --from=docx --to=markdown {fname}',
+    epub: $'pandoc --from=epub --to=markdown {fname}',
+    odp: $'odt2txt {fname}',
+    odt: $'odt2txt {fname}',
+    pdf: $'pdftotext -nopgbrk -layout -q -eol unix {fname} -',
+    rtf: 'unrtf --text',
+}
+" END_INDENT
+
+" START_INDENT
+const ptybuf: number = term_start(&shell, {
+    hidden: true,
+    exit_cb: (_, _) => {
+	if true
+	    close
+	else
+	    help
+	endif
+    }
+})
+" END_INDENT
+
+" START_INDENT
+var d = {
+    a: 0,
+    # a ' quote {{{
+    #}}}
+    b: 0,
+}
+" END_INDENT
+
+" START_INDENT
+echo printf('%s () %s',
+    1,
+    2
+)
+" END_INDENT
+
+" START_INDENT
+prop_add(1, col('.'), {
+    length: 2,
+    type: 'test'
+})
+" END_INDENT
+
+" START_INDENT
+echo (() => " string starting with space")()
+echo
+" END_INDENT
+
+" START_INDENT
+var variables = deepcopy(g:)
+    ->filter((k: string, _): bool =>
+	k =~ '\c\V' .. keyword->escape('\')
+	&& k !~ '\%(loaded\|did_plugin_\)')
+    ->items()
+    ->map((_, v): string => v[0] .. ' = ' .. string(v[1]))
+new
+" END_INDENT
+
+" START_INDENT
+var d = freq
+    ->map((_, v) =>
+	v * (
+	    1
+	    + 2
+	))
+for item in d
+	->items()
+	->sort((a, b) => b[1] - a[1])
+    echo
+endfor
+" END_INDENT
+
+" START_INDENT
+make_job = job_start([&shell, &shellcmdflag, make_cmd], {
+    callback: function(MakeProcessOutput, [qfid]),
+    close_cb: function(MakeCloseCb, [qfid]),
+    exit_cb: MakeCompleted,
+    in_io: 'null'
+})
+" END_INDENT
+
+" START_INDENT
+var matching_abbrev: list<dict<string>> = copy(ABBREV)
+    ->filter((_, v: dict<string>): bool =>
+	stridx(v.lhs, word_to_complete) == 0)
+    ->map((_, v: dict<string>) => ({
+	word: v.lhs,
+	menu: AbbrevRhs(v.rhs)->stridx('expand_') >= 0
+	    ?    AbbrevRhs(v.rhs)->matchstr('.*,\s*''\zs.*\ze'')')
+	    :    AbbrevRhs(v.rhs)
+    }))
+" END_INDENT
+
+" START_INDENT
+def Func()
+    if true
+	vimgrep /^\C\s*\%(fu\%[nction]\|def\)\s\+/ file
+    endif
+enddef
+" END_INDENT
+
+" START_INDENT
+setlocal iskeyword+=[
+cword = expand('<cword>')
+" END_INDENT
+
+" START_INDENT
+silent if true
+    echo
+endif
+" END_INDENT
+
+" START_INDENT
+def Func()
+    sort :^.*[\/]:
+enddef
+" END_INDENT
+
+" START_INDENT
+def Func()
+    d = {
+    }
+    hd =<< trim END
+	['
+	]'
+    END
+enddef
+" END_INDENT
+
+" START_INDENT
+def Func()
+    if true
+	var hd =<< trim END
+	    if get(b:, 'current_syntax', '')
+	    endif
+	END
+    elseif true
+	echo
+    endif
+enddef
+" END_INDENT
+
+" START_INDENT
+# test for control-flow keyword followed by commented fold marker {{{
+if true
+    echo
+endif #}}}
+" END_INDENT
+
+" START_INDENT
+if winsz == 0|let winsz= ""|endif
+exe "noswapfile ".winsz."wincmd s"
+" END_INDENT
+
+" START_INDENT
+if true
+    if true
+	windo if true | echo | endif
+	augroup Name
+	    autocmd WinLeave * if true | eval 1 + 2 | endif
+	augroup END
+    endif
+endif
+" END_INDENT
+
+" START_INDENT
+if true
+    echo ' =<< trim END'
+	->len()
+endif
+" END_INDENT
+
+" START_INDENT
+function Func()
+    if true
+	if true
+	    if true | echo com | endif
+	    if true | echo com | endif
+	endif
+    else
+    endif
+endfunction
+" END_INDENT
+
+" START_INDENT
+function Func()
+    if v:true
+	+
+	echo
+	-
+    endif
+endfunction
+" END_INDENT
+
+" START_INDENT
+var matchpairs: string = &matchpairs
+var pairs: dict<list<string>>
+for [opening: string, closing: string]
+	in matchpairs
+	->split(',')
+	->map((_, v: string): list<string> => split(v, ':'))
+    pairs[opening] = [escape(opening, '[]'), escape(closing, '[]'),  'nW', 'w$']
+    pairs[closing] = [escape(opening, '[]'), escape(closing, '[]'), 'bnW', 'w0']
+endfor
+" END_INDENT
+
+" START_INDENT
+{
+    echo []
+	+ []
+	+ [{a: 1,
+	    b: 2}]
+}
+" END_INDENT
+
+" START_INDENT
+silent! argdel *
+edit file
+" END_INDENT
--- a/runtime/indent/vim.vim
+++ b/runtime/indent/vim.vim
@@ -1,214 +1,22 @@
-" Vim indent file
-" Language:	Vim script
-" Maintainer:	Bram Moolenaar <Bram@vim.org>
-" Last Change:	2022 Jun 24
+vim9script
+
+# Vim indent file
+# Language:	Vim script
+# Maintainer:	Bram Moolenaar <Bram@vim.org>
+# Last Change:	2022 Sep 27
 
-" Only load this indent file when no other was loaded.
-if exists("b:did_indent")
-  finish
+# Only load this indent file when no other was loaded.
+if exists('b:did_indent')
+    finish
 endif
-let b:did_indent = 1
+
+b:did_indent = true
+b:undo_indent = 'setlocal indentkeys< indentexpr<'
 
-setlocal indentexpr=GetVimIndent()
-setlocal indentkeys+==endif,=enddef,=endfu,=endfor,=endwh,=endtry,=},=else,=cat,=finall,=END,0\\,0=\"\\\ 
+import autoload '../autoload/dist/vimindent.vim'
+
+setlocal indentexpr=vimindent.Expr(v:lnum)
+setlocal indentkeys+==endif,=enddef,=endfu,=endfor,=endwh,=endtry,=},=else,=cat,=finall,=END,0\\
+execute('setlocal indentkeys+=0=\"\\\ ,0=#\\\ ')
 setlocal indentkeys-=0#
 setlocal indentkeys-=:
-
-let b:undo_indent = "setl indentkeys< indentexpr<"
-
-" Only define the function once.
-if exists("*GetVimIndent")
-  finish
-endif
-let s:keepcpo= &cpo
-set cpo&vim
-
-function GetVimIndent()
-  let ignorecase_save = &ignorecase
-  try
-    let &ignorecase = 0
-    return GetVimIndentIntern()
-  finally
-    let &ignorecase = ignorecase_save
-  endtry
-endfunc
-
-" Legacy script line continuation and Vim9 script operators that must mean an
-" expression that continues from the previous line.
-let s:lineContPat = '^\s*\(\\\|"\\ \|->\)'
-
-function GetVimIndentIntern()
-  " If the current line has line continuation and the previous one too, use
-  " the same indent.  This does not skip empty lines.
-  let cur_text = getline(v:lnum)
-  let cur_has_linecont = cur_text =~ s:lineContPat
-  if cur_has_linecont && v:lnum > 1 && getline(v:lnum - 1) =~ s:lineContPat
-    return indent(v:lnum - 1)
-  endif
-
-  " Find a non-blank line above the current line.
-  let lnum = prevnonblank(v:lnum - 1)
-
-  " The previous line, ignoring line continuation
-  let prev_text_end = lnum > 0 ? getline(lnum) : ''
-
-  " If the current line doesn't start with '\' or '"\ ' and below a line that
-  " starts with '\' or '"\ ', use the indent of the line above it.
-  if !cur_has_linecont
-    while lnum > 0 && getline(lnum) =~ s:lineContPat
-      let lnum = lnum - 1
-    endwhile
-  endif
-
-  " At the start of the file use zero indent.
-  if lnum == 0
-    return 0
-  endif
-
-  " the start of the previous line, skipping over line continuation
-  let prev_text = getline(lnum)
-  let found_cont = 0
-
-  " Add a 'shiftwidth' after :if, :while, :try, :catch, :finally, :function
-  " and :else.  Add it three times for a line that starts with '\' or '"\ '
-  " after a line that doesn't (or g:vim_indent_cont if it exists).
-  let ind = indent(lnum)
-
-  " In heredoc indenting works completely differently.
-  if has('syntax_items') 
-    let syn_here = synIDattr(synID(v:lnum, 1, 1), "name")
-    if syn_here =~ 'vimLetHereDocStop'
-      " End of heredoc: use indent of matching start line
-      let lnum = v:lnum - 1
-      while lnum > 0
-	let attr = synIDattr(synID(lnum, 1, 1), "name")
-	if attr != '' && attr !~ 'vimLetHereDoc'
-	  return indent(lnum)
-	endif
-	let lnum -= 1
-      endwhile
-      return 0
-    endif
-    if syn_here =~ 'vimLetHereDoc'
-      if synIDattr(synID(lnum, 1, 1), "name") !~ 'vimLetHereDoc'
-	" First line in heredoc: increase indent
-	return ind + shiftwidth()
-      endif
-      " Heredoc continues: no change in indent
-      return ind
-    endif
-  endif
-
-  if cur_text =~ s:lineContPat && v:lnum > 1 && prev_text !~ s:lineContPat
-    let found_cont = 1
-    if exists("g:vim_indent_cont")
-      let ind = ind + g:vim_indent_cont
-    else
-      let ind = ind + shiftwidth() * 3
-    endif
-  elseif prev_text =~ '^\s*aug\%[roup]\s\+' && prev_text !~ '^\s*aug\%[roup]\s\+[eE][nN][dD]\>'
-    let ind = ind + shiftwidth()
-  else
-    " A line starting with :au does not increment/decrement indent.
-    " A { may start a block or a dict.  Assume that when a } follows it's a
-    " terminated dict.
-    " ":function" starts a block but "function(" doesn't.
-    if prev_text !~ '^\s*au\%[tocmd]' && prev_text !~ '^\s*{.*}'
-      let i = match(prev_text, '\(^\||\)\s*\(export\s\+\)\?\({\|\(if\|wh\%[ile]\|for\|try\|cat\%[ch]\|fina\|finall\%[y]\|def\|el\%[seif]\)\>\|fu\%[nction][! ]\)')
-      if i >= 0
-	let ind += shiftwidth()
-	if strpart(prev_text, i, 1) == '|' && has('syntax_items')
-	      \ && synIDattr(synID(lnum, i, 1), "name") =~ '\(Comment\|String\|PatSep\)$'
-	  let ind -= shiftwidth()
-	endif
-      endif
-    endif
-  endif
-
-  " If the previous line contains an "end" after a pipe, but not in an ":au"
-  " command.  And not when there is a backslash before the pipe.
-  " And when syntax HL is enabled avoid a match inside a string.
-  let i = match(prev_text, '[^\\]|\s*\(ene\@!\)')
-  if i > 0 && prev_text !~ '^\s*au\%[tocmd]'
-    if !has('syntax_items') || synIDattr(synID(lnum, i + 2, 1), "name") !~ '\(Comment\|String\)$'
-      let ind = ind - shiftwidth()
-    endif
-  endif
-
-  " For a line starting with "}" find the matching "{".  Align with that line,
-  " it is either the matching block start or dictionary start.
-  " Use the mapped "%" from matchit to find the match, otherwise we may match
-  " a { inside a comment or string.
-  if cur_text =~ '^\s*}'
-    if maparg('%') != ''
-      exe v:lnum
-      silent! normal %
-      if line('.') < v:lnum
-	let ind = indent('.')
-      endif
-    else
-      " todo: use searchpair() to find a match
-    endif
-  endif
-
-  " Look back for a line to align with
-  while lnum > 1
-    " Below a line starting with "}" find the matching "{".
-    if prev_text =~ '^\s*}'
-      if maparg('%') != ''
-	exe lnum
-	silent! normal %
-	if line('.') < lnum
-	  let lnum = line('.')
-	  let ind = indent(lnum)
-	  let prev_text = getline(lnum)
-	else
-	  break
-	endif
-      else
-	" todo: use searchpair() to find a match
-	break
-      endif
-    elseif prev_text =~ s:lineContPat
-      " looks like a continuation like, go back one line
-      let lnum = lnum - 1
-      let ind = indent(lnum)
-      let prev_text = getline(lnum)
-    else
-      break
-    endif
-  endwhile
-
-  " Below a line starting with "]" we must be below the end of a list.
-  " Include a "}" and "},} in case a dictionary ends too.
-  if prev_text_end =~ '^\s*\(},\=\s*\)\=]'
-    let ind = ind - shiftwidth()
-  endif
-
-  let ends_in_comment = has('syntax_items')
-	\ && synIDattr(synID(lnum, len(getline(lnum)), 1), "name") =~ '\(Comment\|String\)$'
-
-  " A line ending in "{" or "[" is most likely the start of a dict/list literal,
-  " indent the next line more.  Not for a continuation line or {{{.
-  if !ends_in_comment && prev_text_end =~ '\s[{[]\s*$' && !found_cont
-    let ind = ind + shiftwidth()
-  endif
-
-  " Subtract a 'shiftwidth' on a :endif, :endwhile, :endfor, :catch, :finally,
-  " :endtry, :endfun, :enddef, :else and :augroup END.
-  " Although ":en" would be enough only match short command names as in
-  " 'indentkeys'.
-  if cur_text =~ '^\s*\(endif\|endwh\|endfor\|endtry\|endfu\|enddef\|cat\|finall\|else\|aug\%[roup]\s\+[eE][nN][dD]\)'
-    let ind = ind - shiftwidth()
-    if ind < 0
-      let ind = 0
-    endif
-  endif
-
-  return ind
-endfunction
-
-let &cpo = s:keepcpo
-unlet s:keepcpo
-
-" vim:sw=2
new file mode 100644
--- /dev/null
+++ b/runtime/indent/vue.vim
@@ -0,0 +1,12 @@
+" Vim indent file placeholder
+" Language:	Vue
+" Maintainer:	None, please volunteer if you have a real Vue indent script
+
+" Only load this indent file when no other was loaded.
+if exists("b:did_indent")
+   finish
+endif
+let b:did_indent = 1
+
+" Html comes closest
+runtime! indent/html.vim
new file mode 100644
--- /dev/null
+++ b/runtime/syntax/chatito.vim
@@ -0,0 +1,62 @@
+" Vim syntax file
+" Language:	Chatito
+" Maintainer:	ObserverOfTime <chronobserver@disroot.org>
+" Filenames:	*.chatito
+" Last Change:	2022 Sep 19
+
+if exists('b:current_syntax')
+    finish
+endif
+
+" Comment
+syn keyword chatitoTodo contained TODO FIXME XXX
+syn match chatitoComment /^#.*/ contains=chatitoTodo,@Spell
+syn match chatitoComment +^//.*+ contains=chatitoTodo,@Spell
+
+" Import
+syn match chatitoImport /^import \+.*$/ transparent contains=chatitoImportKeyword,chatitoImportFile
+syn keyword chatitoImportKeyword import contained nextgroup=chatitoImportFile
+syn match chatitoImportFile /.*$/ contained skipwhite
+
+" Intent
+syn match chatitoIntent /^%\[[^\]?]\+\]\((.\+)\)\=$/ contains=chatitoArgs
+
+" Slot
+syn match chatitoSlot /^@\[[^\]?#]\+\(#[^\]?#]\+\)\=\]\((.\+)\)\=$/ contains=chatitoArgs,chatitoVariation
+syn match chatitoSlot /@\[[^\]?#]\+\(#[^\]?#]\+\)\=?\=\]/ contained contains=chatitoOpt,chatitoVariation
+
+" Alias
+syn match chatitoAlias /^\~\[[^\]?]\+\]\=$/
+syn match chatitoAlias /\~\[[^\]?]\+?\=\]/ contained contains=chatitoOpt
+
+" Probability
+syn match chatitoProbability /\*\[\d\+\(\.\d\+\)\=%\=\]/ contained
+
+" Optional
+syn match chatitoOpt '?' contained
+
+" Arguments
+syn match chatitoArgs /(.\+)/ contained
+
+" Variation
+syn match chatitoVariation /#[^\]?#]\+/ contained
+
+" Value
+syn match chatitoValue /^ \{4\}\zs.\+$/ contains=chatitoProbability,chatitoSlot,chatitoAlias,@Spell
+
+" Errors
+syn match chatitoError /^\t/
+
+hi def link chatitoAlias String
+hi def link chatitoArgs Special
+hi def link chatitoComment Comment
+hi def link chatitoError Error
+hi def link chatitoImportKeyword Include
+hi def link chatitoIntent Statement
+hi def link chatitoOpt SpecialChar
+hi def link chatitoProbability Number
+hi def link chatitoSlot Identifier
+hi def link chatitoTodo Todo
+hi def link chatitoVariation Special
+
+let b:current_syntax = 'chatito'
--- a/runtime/syntax/desktop.vim
+++ b/runtime/syntax/desktop.vim
@@ -3,7 +3,7 @@
 " Filenames: *.desktop, *.directory
 " Maintainer: Eisuke Kawashima ( e.kawaschima+vim AT gmail.com )
 " Previous Maintainer: Mikolaj Machowski ( mikmach AT wp DOT pl )
-" Last Change: 2020-06-11
+" Last Change: 2022 Sep 22
 " Version Info: desktop.vim 1.5
 " References:
 " - https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-1.5.html (2020-04-27)
@@ -60,10 +60,10 @@ syn match dtLocaleSuffix
 
 " Boolean Value {{{2
 syn match   dtBoolean
-      \ /^\%(DBusActivatable\|Hidden\|NoDisplay\|PrefersNonDefaultGPU\|StartupNotify\|Terminal\)\s*=\s*\%(true\|false\)/
+      \ /^\%(DBusActivatable\|Hidden\|NoDisplay\|PrefersNonDefaultGPU\|SingleMainWindow\|StartupNotify\|Terminal\)\s*=\s*\%(true\|false\)/
       \ contains=dtBooleanKey,dtDelim,dtBooleanValue transparent
 syn keyword dtBooleanKey
-      \ DBusActivatable Hidden NoDisplay PrefersNonDefaultGPU StartupNotify Terminal
+      \ DBusActivatable Hidden NoDisplay PrefersNonDefaultGPU SingleMainWindow StartupNotify Terminal
       \ contained nextgroup=dtDelim
 
 if s:desktop_enable_kde
new file mode 100644
--- /dev/null
+++ b/runtime/syntax/gyp.vim
@@ -0,0 +1,49 @@
+" Vim syntax file
+" Language:	GYP
+" Maintainer:	ObserverOfTime <chronobserver@disroot.org>
+" Filenames:	*.gyp,*.gypi
+" Last Change:	2022 Sep 27
+
+if !exists('g:main_syntax')
+  if exists('b:current_syntax') && b:current_syntax ==# 'gyp'
+    finish
+  endif
+  let g:main_syntax = 'gyp'
+endif
+
+" Based on JSON syntax
+runtime! syntax/json.vim
+
+" Single quotes are allowed
+syn clear jsonStringSQError
+
+syn match jsonKeywordMatch /'\([^']\|\\\'\)\+'[[:blank:]\r\n]*\:/ contains=jsonKeyword
+if has('conceal') && (!exists('g:vim_json_conceal') || g:vim_json_conceal==1)
+   syn region  jsonKeyword matchgroup=jsonQuote start=/'/  end=/'\ze[[:blank:]\r\n]*\:/ concealends contained
+else
+   syn region  jsonKeyword matchgroup=jsonQuote start=/'/  end=/'\ze[[:blank:]\r\n]*\:/ contained
+endif
+
+syn match  jsonStringMatch /'\([^']\|\\\'\)\+'\ze[[:blank:]\r\n]*[,}\]]/ contains=jsonString
+if has('conceal') && (!exists('g:vim_json_conceal') || g:vim_json_conceal==1)
+    syn region  jsonString oneline matchgroup=jsonQuote start=/'/  skip=/\\\\\|\\'/  end=/'/ concealends contains=jsonEscape contained
+else
+    syn region  jsonString oneline matchgroup=jsonQuote start=/'/  skip=/\\\\\|\\'/  end=/'/ contains=jsonEscape contained
+endif
+
+" Trailing commas are allowed
+if !exists('g:vim_json_warnings') || g:vim_json_warnings==1
+    syn clear jsonTrailingCommaError
+endif
+
+" Python-style comments are allowed
+syn match   jsonComment  /#.*$/ contains=jsonTodo,@Spell
+syn keyword jsonTodo     FIXME NOTE TODO XXX TBD contained
+
+hi def link jsonComment Comment
+hi def link jsonTodo    Todo
+
+let b:current_syntax = 'gyp'
+if g:main_syntax ==# 'gyp'
+  unlet g:main_syntax
+endif
new file mode 100644
--- /dev/null
+++ b/runtime/syntax/hare.vim
@@ -0,0 +1,133 @@
+" PRELUDE {{{1
+" Vim syntax file
+" Language: Hare
+" Maintainer: Amelia Clarke <me@rsaihe.dev>
+" Last Change: 2022-09-21
+
+if exists("b:current_syntax")
+  finish
+endif
+let b:current_syntax = "hare"
+
+" SYNTAX {{{1
+syn case match
+
+" KEYWORDS {{{2
+syn keyword hareConditional if else match switch
+syn keyword hareKeyword break continue return yield
+syn keyword hareKeyword defer
+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
+
+" BUILTINS {{{2
+syn keyword hareBuiltin abort
+syn keyword hareBuiltin alloc free
+syn keyword hareBuiltin append delete insert
+syn keyword hareBuiltin assert
+syn keyword hareBuiltin len offset
+
+" TYPES {{{2
+syn keyword hareType bool
+syn keyword hareType char str
+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 uintptr
+syn keyword hareType void
+
+" C ABI.
+syn keyword hareType valist
+
+" LITERALS {{{2
+syn keyword hareBoolean true false
+syn keyword hareNull null
+
+" 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 number literals.
+syn match hareFloat "\v<\d+\.\d+([Ee][+-]?\d+)?(f32|f64)?>" display
+syn match hareFloat "\v<\d+([Ee][+-]?\d+)?(f32|f64)>" 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
+
+" MISCELLANEOUS {{{2
+syn keyword hareTodo FIXME TODO XXX contained
+
+" Attributes.
+syn match hareAttribute "@[a-z]*"
+
+" Blocks.
+syn region hareBlock start="{" end="}" fold transparent
+
+" Comments.
+syn region hareComment start="//" end="$" contains=hareCommentDoc,hareTodo,@Spell display keepend
+syn region hareCommentDoc start="\[\[" end="]]\|\ze\_s" contained 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
+
+" Trailing whitespace.
+syn match hareSpaceError "\v\s+$" display excludenl
+syn match hareSpaceError "\v\zs +\ze\t" display
+
+" Use statement.
+syn region hareUse start="\v^\s*\zsuse>" end=";" contains=hareComment display
+
+syn match hareErrorAssertion "\v(^([^/]|//@!)*\)\_s*)@<=!\=@!"
+syn match hareQuestionMark "?"
+
+" DEFAULT HIGHLIGHTING {{{1
+hi def link hareAttribute Keyword
+hi def link hareBoolean Boolean
+hi def link hareBuiltin Function
+hi def link hareComment Comment
+hi def link hareCommentDoc SpecialComment
+hi def link hareConditional Conditional
+hi def link hareEscape SpecialChar
+hi def link hareFloat Float
+hi def link hareFormat SpecialChar
+hi def link hareKeyword Keyword
+hi def link hareLabel Label
+hi def link hareNull Constant
+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
+hi def link hareString String
+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
+
+hi def link hareSpaceError Error
+autocmd InsertEnter * hi link hareSpaceError NONE
+autocmd InsertLeave * hi link hareSpaceError Error
+
+hi def hareErrorAssertion ctermfg=red cterm=bold guifg=red gui=bold
+
+" vim: tabstop=8 shiftwidth=2 expandtab
--- a/runtime/syntax/help.vim
+++ b/runtime/syntax/help.vim
@@ -1,7 +1,7 @@
 " Vim syntax file
 " Language:	Vim help file
 " Maintainer:	Bram Moolenaar (Bram@vim.org)
-" Last Change:	2022 May 15
+" Last Change:	2022 Sep 26
 
 " Quit when a (custom) syntax file was already loaded
 if exists("b:current_syntax")
@@ -44,6 +44,7 @@ syn match helpVim		"\<Vim version [0-9][
 syn match helpVim		"VIM REFERENCE.*"
 syn match helpOption		"'[a-z]\{2,\}'"
 syn match helpOption		"'t_..'"
+syn match helpNormal		"'ab'"
 syn match helpCommand		"`[^` \t]\+`"hs=s+1,he=e-1 contains=helpBacktick
 syn match helpCommand		"\(^\|[^a-z"[]\)\zs`[^`]\+`\ze\([^a-z\t."']\|$\)"hs=s+1,he=e-1 contains=helpBacktick
 syn match helpHeader		"\s*\zs.\{-}\ze\s\=\~$" nextgroup=helpIgnore
new file mode 100644
--- /dev/null
+++ b/runtime/syntax/hlsplaylist.vim
@@ -0,0 +1,120 @@
+" Vim syntax file
+" Language: HLS Playlist
+" Maintainer: Benoît Ryder <benoit@ryder.fr>
+" Latest Revision: 2022-09-23
+
+if exists("b:current_syntax")
+  finish
+endif
+
+let s:cpo_save = &cpo
+set cpo&vim
+
+" Comment line
+syn match  hlsplaylistComment  "^#\(EXT\)\@!.*$"
+" Segment URL
+syn match  hlsplaylistUrl      "^[^#].*$"
+
+" Unknown tags, assume an attribute list or nothing
+syn match  hlsplaylistTagUnknown    "^#EXT[^:]*$"
+syn region hlsplaylistTagLine matchgroup=hlsplaylistTagUnknown    start="^#EXT[^:]*\ze:"  end="$" keepend contains=hlsplaylistAttributeList
+
+" Basic Tags
+syn match  hlsplaylistTagHeader     "^#EXTM3U$"
+syn region hlsplaylistTagLine matchgroup=hlsplaylistTagHeader     start="^#EXT-X-VERSION\ze:"  end="$" keepend contains=hlsplaylistValueInt
+
+" Media or Multivariant Playlist Tags
+syn match  hlsplaylistTagHeader     "^#EXT-X-INDEPENDENT-SEGMENTS$"
+syn region hlsplaylistTagLine matchgroup=hlsplaylistTagDelimiter  start="^#EXT-X-START\ze:"  end="$" keepend contains=hlsplaylistAttributeList
+syn region hlsplaylistTagLine matchgroup=hlsplaylistTagStandard   start="^#EXT-X-DEFINE\ze:"  end="$" keepend contains=hlsplaylistAttributeList
+
+" Media Playlist Tags
+syn region hlsplaylistTagLine matchgroup=hlsplaylistTagHeader     start="^#EXT-X-TARGETDURATION\ze:"  end="$" keepend contains=hlsplaylistValueFloat
+syn region hlsplaylistTagLine matchgroup=hlsplaylistTagHeader     start="^#EXT-X-MEDIA-SEQUENCE\ze:"  end="$" keepend contains=hlsplaylistValueInt
+syn region hlsplaylistTagLine matchgroup=hlsplaylistTagHeader     start="^#EXT-X-DISCONTINUITY-SEQUENCE\ze:"  end="$" keepend contains=hlsplaylistValueInt
+syn match  hlsplaylistTagDelimiter  "^#EXT-X-ENDLIST$"
+syn region hlsplaylistTagLine matchgroup=hlsplaylistTagHeader     start="^#EXT-X-PLAYLIST-TYPE\ze:"  end="$" keepend contains=hlsplaylistAttributeEnum
+syn match  hlsplaylistTagStandard   "^#EXT-X-I-FRAME-ONLY$"
+syn region hlsplaylistTagLine matchgroup=hlsplaylistTagHeader     start="^#EXT-X-PART-INF\ze:"  end="$" keepend contains=hlsplaylistAttributeList
+syn region hlsplaylistTagLine matchgroup=hlsplaylistTagHeader     start="^#EXT-X-SERVER-CONTROL\ze:"  end="$" keepend contains=hlsplaylistAttributeList
+
+" Media Segment Tags
+syn region hlsplaylistTagLine matchgroup=hlsplaylistTagStatement  start="^#EXTINF\ze:"  end="$" keepend contains=hlsplaylistValueFloat,hlsplaylistExtInfDesc
+syn region hlsplaylistTagLine matchgroup=hlsplaylistTagStandard   start="^#EXT-X-BYTERANGE\ze:"  end="$" keepend contains=hlsplaylistValueInt
+syn match  hlsplaylistTagDelimiter  "^#EXT-X-DISCONTINUITY$"
+syn region hlsplaylistTagLine matchgroup=hlsplaylistTagStandard   start="^#EXT-X-KEY\ze:"  end="$" keepend contains=hlsplaylistAttributeList
+syn region hlsplaylistTagLine matchgroup=hlsplaylistTagStandard   start="^#EXT-X-MAP\ze:"  end="$" keepend contains=hlsplaylistAttributeList
+syn region hlsplaylistTagLine matchgroup=hlsplaylistTagStandard   start="^#EXT-X-PROGRAM-DATE-TIME\ze:"  end="$" keepend contains=hlsplaylistValueDateTime
+syn match  hlsplaylistTagDelimiter  "^#EXT-X-GAP$"
+syn region hlsplaylistTagLine matchgroup=hlsplaylistTagStandard   start="^#EXT-X-BITRATE\ze:"  end="$" keepend contains=hlsplaylistValueFloat
+syn region hlsplaylistTagLine matchgroup=hlsplaylistTagStatement  start="^#EXT-X-PART\ze:"  end="$" keepend contains=hlsplaylistAttributeList
+
+" Media Metadata Tags
+syn region hlsplaylistTagLine matchgroup=hlsplaylistTagStandard   start="^#EXT-X-DATERANGE\ze:"  end="$" keepend contains=hlsplaylistAttributeList
+syn region hlsplaylistTagLine matchgroup=hlsplaylistTagStandard   start="^#EXT-X-SKIP\ze:"  end="$" keepend contains=hlsplaylistAttributeList
+syn region hlsplaylistTagLine matchgroup=hlsplaylistTagStatement  start="^#EXT-X-PRELOAD-HINT\ze:"  end="$" keepend contains=hlsplaylistAttributeList
+syn region hlsplaylistTagLine matchgroup=hlsplaylistTagStatement  start="^#EXT-X-RENDITION-REPORT\ze:"  end="$" keepend contains=hlsplaylistAttributeList
+
+" Multivariant Playlist Tags
+syn region hlsplaylistTagLine matchgroup=hlsplaylistTagStandard   start="^#EXT-X-MEDIA\ze:"  end="$" keepend contains=hlsplaylistAttributeList
+syn region hlsplaylistTagLine matchgroup=hlsplaylistTagStatement  start="^#EXT-X-STREAM-INF\ze:"  end="$" keepend contains=hlsplaylistAttributeList
+syn region hlsplaylistTagLine matchgroup=hlsplaylistTagStatement  start="^#EXT-X-I-FRAME-STREAM-INF\ze:"  end="$" keepend contains=hlsplaylistAttributeList
+syn region hlsplaylistTagLine matchgroup=hlsplaylistTagStandard   start="^#EXT-X-SESSION-DATA\ze:"  end="$" keepend contains=hlsplaylistAttributeList
+syn region hlsplaylistTagLine matchgroup=hlsplaylistTagStandard   start="^#EXT-X-SESSION-KEY\ze:"  end="$" keepend contains=hlsplaylistAttributeList
+syn region hlsplaylistTagLine matchgroup=hlsplaylistTagStandard   start="^#EXT-X-CONTENT-STEERING\ze:"  end="$" keepend contains=hlsplaylistAttributeList
+
+" Attributes
+syn region hlsplaylistAttributeList  start=":" end="$" keepend contained
+  \ contains=hlsplaylistAttributeName,hlsplaylistAttributeInt,hlsplaylistAttributeHex,hlsplaylistAttributeFloat,hlsplaylistAttributeString,hlsplaylistAttributeEnum,hlsplaylistAttributeResolution,hlsplaylistAttributeUri
+" Common attributes
+syn match  hlsplaylistAttributeName        "[A-Za-z-]\+\ze=" contained
+syn match  hlsplaylistAttributeEnum        "=\zs[A-Za-z][A-Za-z0-9-_]*" contained
+syn match  hlsplaylistAttributeString      +=\zs"[^"]*"+ contained
+syn match  hlsplaylistAttributeInt         "=\zs\d\+" contained
+syn match  hlsplaylistAttributeFloat       "=\zs-\?\d*\.\d*" contained
+syn match  hlsplaylistAttributeHex         "=\zs0[xX]\d*" contained
+syn match  hlsplaylistAttributeResolution  "=\zs\d\+x\d\+" contained
+" Allow different highligting for URI attributes
+syn region hlsplaylistAttributeUri matchgroup=hlsplaylistAttributeName    start="\zsURI\ze" end="\(,\|$\)" contained contains=hlsplaylistUriQuotes
+syn region hlsplaylistUriQuotes    matchgroup=hlsplaylistAttributeString  start=+"+ end=+"+ keepend contained contains=hlsplaylistUriValue
+syn match  hlsplaylistUriValue             /[^" ]\+/ contained
+" Individual values
+syn match  hlsplaylistValueInt             "[0-9]\+" contained
+syn match  hlsplaylistValueFloat           "\(\d\+\|\d*\.\d*\)" contained
+syn match  hlsplaylistExtInfDesc           ",\zs.*$" contained
+syn match  hlsplaylistValueDateTime        "\d\d\d\d-\d\d-\d\dT\d\d:\d\d:\d\d\(\.\d*\)\?\(Z\|\d\d:\?\d\d\)$" contained
+
+
+" Define default highlighting
+
+hi def link hlsplaylistComment  Comment
+hi def link hlsplaylistUrl      NONE
+
+hi def link hlsplaylistTagHeader     Special
+hi def link hlsplaylistTagStandard   Define
+hi def link hlsplaylistTagDelimiter  Delimiter
+hi def link hlsplaylistTagStatement  Statement
+hi def link hlsplaylistTagUnknown    Special
+
+hi def link hlsplaylistUriQuotes            String
+hi def link hlsplaylistUriValue             Underlined
+hi def link hlsplaylistAttributeQuotes      String
+hi def link hlsplaylistAttributeName        Identifier
+hi def link hlsplaylistAttributeInt         Number
+hi def link hlsplaylistAttributeHex         Number
+hi def link hlsplaylistAttributeFloat       Float
+hi def link hlsplaylistAttributeString      String
+hi def link hlsplaylistAttributeEnum        Constant
+hi def link hlsplaylistAttributeResolution  Constant
+hi def link hlsplaylistValueInt             Number
+hi def link hlsplaylistValueFloat           Float
+hi def link hlsplaylistExtInfDesc           String
+hi def link hlsplaylistValueDateTime        Constant
+
+
+let b:current_syntax = "hlsplaylist"
+
+let &cpo = s:cpo_save
+unlet s:cpo_save
+
+" vim: sts=2 sw=2 et
--- a/runtime/syntax/plsql.vim
+++ b/runtime/syntax/plsql.vim
@@ -4,8 +4,11 @@
 " Previous Maintainer: Jeff Lanzarotta (jefflanzarotta at yahoo dot com)
 " Previous Maintainer: C. Laurence Gonsalves (clgonsal@kami.com)
 " URL: https://github.com/lee-lindley/vim_plsql_syntax
-" Last Change: Aug 21, 2022   
-" History  Lee Lindley (lee dot lindley at gmail dot com)
+" Last Change: Sep 19, 2022   
+" History  Carsten Czarski (carsten dot czarski at oracle com)
+"               add handling for typical SQL*Plus commands (rem, start, host, set, etc)
+"               add error highlight for non-breaking space
+"          Lee Lindley (lee dot lindley at gmail dot com)
 "               use get with default 0 instead of exists per Bram suggestion
 "               make procedure folding optional
 "               updated to 19c keywords. refined quoting. 
@@ -54,8 +57,13 @@ syn case ignore
 
 syn match   plsqlGarbage "[^ \t()]"
 syn match   plsqlIdentifier "[a-z][a-z0-9$_#]*"
+syn match   plsqlSqlPlusDefine "&&\?[a-z][a-z0-9$_#]*\.\?"
 syn match   plsqlHostIdentifier ":[a-z][a-z0-9$_#]*"
 
+" The Non-Breaking is often accidentally typed (Mac User: Opt+Space, after typing the "|",  Opt+7);
+" error highlight for these avoids running into annoying compiler errors.
+syn match   plsqlIllegalSpace "[\xa0]"
+
 " When wanted, highlight the trailing whitespace.
 if get(g:,"plsql_space_errors",0) == 1
   if get(g:,"plsql_no_trail_space_error",0) == 0
@@ -79,7 +87,6 @@ syn match   plsqlOperator "\<IS\\_s\+\(N
 "
 " conditional compilation Preprocessor directives and sqlplus define sigil
 syn match plsqlPseudo "$[$a-z][a-z0-9$_#]*"
-syn match plsqlPseudo "&"
 
 syn match plsqlReserved "\<\(CREATE\|THEN\|UPDATE\|INSERT\|SET\)\>"
 syn match plsqlKeyword "\<\(REPLACE\|PACKAGE\|FUNCTION\|PROCEDURE\|TYPE|BODY\|WHEN\|MATCHED\)\>"
@@ -150,7 +157,7 @@ syn keyword plsqlKeyword DATA_SECURITY_R
 syn keyword plsqlKeyword DBMS_STATS DBSTR2UTF8 DBTIMEZONE DB_ROLE_CHANGE DB_UNIQUE_NAME DB_VERSION
 syn keyword plsqlKeyword DDL DEALLOCATE DEBUG DEBUGGER DECLARE DECODE DECOMPOSE DECOMPRESS DECORRELATE
 syn keyword plsqlKeyword DECR DECREMENT DECRYPT DEDUPLICATE DEFAULTS DEFAULT_COLLATION DEFAULT_PDB_HINT
-syn keyword plsqlKeyword DEFERRABLE DEFERRED DEFINE DEFINED DEFINER DEFINITION DEGREE DELAY DELEGATE
+syn keyword plsqlKeyword DEFERRABLE DEFERRED DEFINED DEFINER DEFINITION DEGREE DELAY DELEGATE
 syn keyword plsqlKeyword DELETEXML DELETE_ALL DEMAND DENORM_AV DENSE_RANK DENSE_RANKM DEPENDENT DEPTH
 syn keyword plsqlKeyword DEQUEUE DEREF DEREF_NO_REWRITE DESCENDANT DESCRIPTION DESTROY DETACHED DETERMINED
 syn keyword plsqlKeyword DETERMINES DETERMINISTIC DG_GATHER_STATS DIAGNOSTICS DICTIONARY DIGEST DIMENSION
@@ -189,7 +196,7 @@ syn keyword plsqlKeyword HELP HEXTORAW H
 syn keyword plsqlKeyword HIER_CAPTION HIER_CHILDREN HIER_CHILD_COUNT HIER_COLUMN HIER_CONDITION HIER_DEPTH
 syn keyword plsqlKeyword HIER_DESCRIPTION HIER_HAS_CHILDREN HIER_LAG HIER_LEAD HIER_LEVEL HIER_MEMBER_NAME
 syn keyword plsqlKeyword HIER_MEMBER_UNIQUE_NAME HIER_ORDER HIER_PARENT HIER_WINDOW HIGH HINTSET_BEGIN
-syn keyword plsqlKeyword HINTSET_END HOST HOT HOUR HOURS HTTP HWM_BROKERED HYBRID ID IDENTIFIER IDENTITY
+syn keyword plsqlKeyword HINTSET_END HOT HOUR HOURS HTTP HWM_BROKERED HYBRID ID IDENTIFIER IDENTITY
 syn keyword plsqlKeyword IDGENERATORS IDLE IDLE_TIME IGNORE IGNORE_OPTIM_EMBEDDED_HINTS IGNORE_ROW_ON_DUPKEY_INDEX
 syn keyword plsqlKeyword IGNORE_WHERE_CLAUSE ILM IMMEDIATE IMMUTABLE IMPACT IMPORT INACTIVE INACTIVE_ACCOUNT_TIME
 syn keyword plsqlKeyword INCLUDE INCLUDES INCLUDE_VERSION INCLUDING INCOMING INCR INCREMENT INCREMENTAL
@@ -342,7 +349,7 @@ syn keyword plsqlKeyword SELF SEMIJOIN S
 syn keyword plsqlKeyword SERIAL SERIALIZABLE SERVERERROR SERVICE SERVICES SERVICE_NAME_CONVERT SESSION
 syn keyword plsqlKeyword SESSIONS_PER_USER SESSIONTIMEZONE SESSIONTZNAME SESSION_CACHED_CURSORS SETS
 syn keyword plsqlKeyword SETTINGS SET_GBY_PUSHDOWN SET_TO_JOIN SEVERE SHARD SHARDED SHARDS SHARDSPACE
-syn keyword plsqlKeyword SHARD_CHUNK_ID SHARED SHARED_POOL SHARE_OF SHARING SHD$COL$MAP SHELFLIFE SHOW
+syn keyword plsqlKeyword SHARD_CHUNK_ID SHARED SHARED_POOL SHARE_OF SHARING SHD$COL$MAP SHELFLIFE 
 syn keyword plsqlKeyword SHRINK SHUTDOWN SIBLING SIBLINGS SID SIGN SIGNAL_COMPONENT SIGNAL_FUNCTION
 syn keyword plsqlKeyword SIGNATURE SIMPLE SIN SINGLE SINGLETASK SINH SITE SKEWNESS_POP SKEWNESS_SAMP
 syn keyword plsqlKeyword SKIP SKIP_EXT_OPTIMIZER SKIP_PROXY SKIP_UNQ_UNUSABLE_IDX SKIP_UNUSABLE_INDEXES
@@ -445,7 +452,7 @@ syn keyword plsqlKeyword VECTOR_READ_TRA
 syn keyword plsqlKeyword VERIFIER VERIFY VERSION VERSIONING VERSIONS VERSIONS_ENDSCN VERSIONS_ENDTIME
 syn keyword plsqlKeyword VERSIONS_OPERATION VERSIONS_STARTSCN VERSIONS_STARTTIME VERSIONS_XID VIEWS
 syn keyword plsqlKeyword VIOLATION VIRTUAL VISIBILITY VISIBLE VOLUME VSIZE WAIT WALLET WEEK WEEKS WELLFORMED
-syn keyword plsqlKeyword WHENEVER WHITESPACE WIDTH_BUCKET WINDOW WITHIN WITHOUT WITH_EXPRESSION
+syn keyword plsqlKeyword WHITESPACE WIDTH_BUCKET WINDOW WITHIN WITHOUT WITH_EXPRESSION
 syn keyword plsqlKeyword WITH_PLSQL WORK WRAPPED WRAPPER WRITE XDB_FASTPATH_INSERT XID XML XML2OBJECT
 syn keyword plsqlKeyword XMLATTRIBUTES XMLCAST XMLCDATA XMLCOLATTVAL XMLCOMMENT XMLCONCAT XMLDIFF XMLELEMENT
 syn keyword plsqlKeyword XMLEXISTS XMLEXISTS2 XMLFOREST XMLINDEX_REWRITE XMLINDEX_REWRITE_IN_SELECT
@@ -468,13 +475,13 @@ syn keyword plsqlReserved MINUS MODE NOC
 syn keyword plsqlReserved OCIDURATION OCIINTERVAL OCILOBLOCATOR OCINUMBER OCIRAW OCIREF OCIREFCURSOR
 syn keyword plsqlReserved OCIROWID OCISTRING OCITYPE OF ON OPTION ORACLE ORADATA ORDER ORLANY ORLVARY
 syn keyword plsqlReserved OUT OVERRIDING PARALLEL_ENABLE PARAMETER PASCAL PCTFREE PIPE PIPELINED POLYMORPHIC
-syn keyword plsqlReserved PRAGMA PRIOR PUBLIC RAISE RECORD RELIES_ON REM RENAME RESOURCE RESULT REVOKE ROWID 
+syn keyword plsqlReserved PRAGMA PRIOR PUBLIC RAISE RECORD RELIES_ON RENAME RESOURCE RESULT REVOKE ROWID 
 syn keyword plsqlReserved SB1 SB2 
 syn match plsqlReserved "\<SELECT\>"
 syn keyword plsqlReserved SEPARATE SHARE SHORT SIZE SIZE_T SPARSE SQLCODE SQLDATA
 syn keyword plsqlReserved SQLNAME SQLSTATE STANDARD START STORED STRUCT STYLE SYNONYM TABLE TDO
 syn keyword plsqlReserved TRANSACTIONAL TRIGGER UB1 UB4 UNION UNIQUE UNSIGNED UNTRUSTED VALIST
-syn keyword plsqlReserved VALUES VARIABLE VIEW VOID WHERE WITH
+syn keyword plsqlReserved VALUES VIEW VOID WHERE WITH
 
 " PL/SQL and SQL functions.
 syn keyword plsqlFunction ABS ACOS ADD_MONTHS APPROX_COUNT APPROX_COUNT_DISTINCT APPROX_COUNT_DISTINCT_AGG
@@ -584,18 +591,18 @@ syn match plsqlEND "\<END\>"
 syn match plsqlISAS "\<\(IS\|AS\)\>"
 
 " Various types of comments.
-syntax region plsqlCommentL start="--" skip="\\$" end="$" keepend extend contains=@plsqlCommentGroup,plsqlSpaceError
+syntax region plsqlCommentL start="--" skip="\\$" end="$" keepend extend contains=@plsqlCommentGroup,plsqlSpaceError,plsqlIllegalSpace,plsqlSqlplusDefine
 if get(g:,"plsql_fold",0) == 1
     syntax region plsqlComment
         \ start="/\*" end="\*/"
         \ extend
-        \ contains=@plsqlCommentGroup,plsqlSpaceError
+        \ contains=@plsqlCommentGroup,plsqlSpaceError,plsqlIllegalSpace,plsqlSqlplusDefine
         \ fold
 else
     syntax region plsqlComment
         \ start="/\*" end="\*/"
         \ extend
-        \ contains=@plsqlCommentGroup,plsqlSpaceError
+        \ contains=@plsqlCommentGroup,plsqlSpaceError,plsqlIllegalSpace,plsqlSqlplusDefine
 endif
 syn cluster plsqlCommentAll contains=plsqlCommentL,plsqlComment
 
@@ -618,23 +625,23 @@ syn match   plsqlFloatLiteral contained 
 " double quoted strings in SQL are database object names. Should be a subgroup of Normal.
 " We will use Character group as a proxy for that so color can be chosen close to Normal
 syn region plsqlQuotedIdentifier	matchgroup=plsqlOperator start=+n\?"+     end=+"+ keepend extend
-syn cluster plsqlIdentifiers contains=plsqlIdentifier,plsqlQuotedIdentifier
+syn cluster plsqlIdentifiers contains=plsqlIdentifier,plsqlQuotedIdentifier,plsqlSqlPlusDefine
 
 " quoted string literals
 if get(g:,"plsql_fold",0) == 1
-    syn region plsqlStringLiteral	matchgroup=plsqlOperator start=+n\?'+  skip=+''+    end=+'+ fold keepend extend
-    syn region plsqlStringLiteral	matchgroup=plsqlOperator start=+n\?q'\z([^[(<{]\)+    end=+\z1'+ fold keepend extend
-    syn region plsqlStringLiteral	matchgroup=plsqlOperator start=+n\?q'<+   end=+>'+ fold keepend extend
-    syn region plsqlStringLiteral	matchgroup=plsqlOperator start=+n\?q'{+   end=+}'+ fold keepend extend
-    syn region plsqlStringLiteral	matchgroup=plsqlOperator start=+n\?q'(+   end=+)'+ fold keepend extend
-    syn region plsqlStringLiteral	matchgroup=plsqlOperator start=+n\?q'\[+  end=+]'+ fold keepend extend
+    syn region plsqlStringLiteral	matchgroup=plsqlOperator start=+n\?'+  skip=+''+    end=+'+ contains=plsqlSqlplusDefine fold keepend extend
+    syn region plsqlStringLiteral	matchgroup=plsqlOperator start=+n\?q'\z([^[(<{]\)+    end=+\z1'+ contains=plsqlSqlplusDefine fold keepend extend
+    syn region plsqlStringLiteral	matchgroup=plsqlOperator start=+n\?q'<+   end=+>'+ contains=plsqlSqlplusDefine fold keepend extend
+    syn region plsqlStringLiteral	matchgroup=plsqlOperator start=+n\?q'{+   end=+}'+ contains=plsqlSqlplusDefine fold keepend extend
+    syn region plsqlStringLiteral	matchgroup=plsqlOperator start=+n\?q'(+   end=+)'+ contains=plsqlSqlplusDefine fold keepend extend
+    syn region plsqlStringLiteral	matchgroup=plsqlOperator start=+n\?q'\[+  end=+]'+ contains=plsqlSqlplusDefine fold keepend extend
 else
-    syn region plsqlStringLiteral	matchgroup=plsqlOperator start=+n\?'+  skip=+''+    end=+'+ 
-    syn region plsqlStringLiteral	matchgroup=plsqlOperator start=+n\?q'\z([^[(<{]\)+    end=+\z1'+ 
-    syn region plsqlStringLiteral	matchgroup=plsqlOperator start=+n\?q'<+   end=+>'+ 
-    syn region plsqlStringLiteral	matchgroup=plsqlOperator start=+n\?q'{+   end=+}'+ 
-    syn region plsqlStringLiteral	matchgroup=plsqlOperator start=+n\?q'(+   end=+)'+ 
-    syn region plsqlStringLiteral	matchgroup=plsqlOperator start=+n\?q'\[+  end=+]'+ 
+    syn region plsqlStringLiteral	matchgroup=plsqlOperator start=+n\?'+  skip=+''+    end=+'+ contains=plsqlSqlplusDefine
+    syn region plsqlStringLiteral	matchgroup=plsqlOperator start=+n\?q'\z([^[(<{]\)+    end=+\z1'+ contains=plsqlSqlplusDefine
+    syn region plsqlStringLiteral	matchgroup=plsqlOperator start=+n\?q'<+   end=+>'+ contains=plsqlSqlplusDefine
+    syn region plsqlStringLiteral	matchgroup=plsqlOperator start=+n\?q'{+   end=+}'+ contains=plsqlSqlplusDefine
+    syn region plsqlStringLiteral	matchgroup=plsqlOperator start=+n\?q'(+   end=+)'+ contains=plsqlSqlplusDefine
+    syn region plsqlStringLiteral	matchgroup=plsqlOperator start=+n\?q'\[+  end=+]'+ contains=plsqlSqlplusDefine
 endif
 
 syn keyword plsqlBooleanLiteral TRUE FALSE 
@@ -682,6 +689,10 @@ syn match plsqlConditional "\<END\>\_s\+
 syn match plsqlCase "\<END\>\_s\+\<CASE\>"
 syn match plsqlCase "\<CASE\>"
 
+syn region plsqlSqlPlusCommentL start="^\(REM\)\( \|$\)" skip="\\$" end="$" keepend extend contains=@plsqlCommentGroup,plsqlSpaceError,plsqlIllegalSpace
+syn region plsqlSqlPlusCommand  start="^\(SET\|DEFINE\|PROMPT\|ACCEPT\|EXEC\|HOST\|SHOW\|VAR\|VARIABLE\|COL\|WHENEVER\|TIMING\)\( \|$\)" skip="\\$" end="$" keepend extend
+syn region plsqlSqlPlusRunFile  start="^\(@\|@@\)" skip="\\$" end="$" keepend extend
+
 if get(g:,"plsql_fold",0) == 1
     setlocal foldmethod=syntax
     syn sync fromstart
@@ -826,6 +837,13 @@ hi def link plsqlComment2String     Stri
 hi def link plsqlTrigger	        Function
 hi def link plsqlTypeAttribute      StorageClass
 hi def link plsqlTodo		        Todo
+
+hi def link plsqlIllegalSpace       Error
+hi def link plsqlSqlPlusDefine      PreProc
+hi def link plsqlSqlPlusCommand     PreProc
+hi def link plsqlSqlPlusRunFile     Include
+hi def link plsqlSqlPlusCommentL    Comment
+
 " to be able to change them after loading, need override whether defined or not
 if get(g:,"plsql_legacy_sql_keywords",0) == 1
     hi link plsqlSQLKeyword         Function
new file mode 100644
--- /dev/null
+++ b/runtime/syntax/solidity.vim
@@ -0,0 +1,173 @@
+" Vim syntax file
+" Language: 		Solidity
+" Maintainer: 		Cothi (jiungdev@gmail.com)
+" Original Author: 	tomlion (https://github.com/tomlion/vim-solidity/blob/master/syntax/solidity.vim)
+" Last Changed: 	2022 Sep 27
+"
+" Additional contributors:
+"       Modified by thesis (https://github.com/thesis/vim-solidity/blob/main/indent/solidity.vim)
+
+if exists("b:current_syntax")
+  finish
+endif
+
+" keyword
+syn keyword solKeyword           abstract anonymous as break calldata case catch constant constructor continue default switch revert require
+syn keyword solKeyword           ecrecover addmod mulmod keccak256
+syn keyword solKeyword           delete do else emit enum external final for function if immutable import in indexed inline
+syn keyword solKeyword           interface internal is let match memory modifier new of payable pragma private public pure override virtual
+syn keyword solKeyword           relocatable return returns static storage struct throw try type typeof using
+syn keyword solKeyword           var view while
+
+syn keyword solConstant          true false wei szabo finney ether seconds minutes hours days weeks years now
+syn keyword solConstant          abi block blockhash msg tx this super selfdestruct
+
+syn keyword solBuiltinType       mapping address bool
+syn keyword solBuiltinType       int int8 int16 int24 int32 int40 int48 int56 int64 int72 int80 int88 int96 int104 int112 int120 int128 int136 int144 int152 int160 int168 int178 int184 int192 int200 int208 int216 int224 int232 int240 int248 int256
+syn keyword solBuiltinType       uint uint8 uint16 uint24 uint32 uint40 uint48 uint56 uint64 uint72 uint80 uint88 uint96 uint104 uint112 uint120 uint128 uint136 uint144 uint152 uint160 uint168 uint178 uint184 uint192 uint200 uint208 uint216 uint224 uint232 uint240 uint248 uint256
+syn keyword solBuiltinType       fixed
+syn keyword solBuiltinType       fixed0x8 fixed0x16 fixed0x24 fixed0x32 fixed0x40 fixed0x48 fixed0x56 fixed0x64 fixed0x72 fixed0x80 fixed0x88 fixed0x96 fixed0x104 fixed0x112 fixed0x120 fixed0x128 fixed0x136 fixed0x144 fixed0x152 fixed0x160 fixed0x168 fixed0x178 fixed0x184 fixed0x192 fixed0x200 fixed0x208 fixed0x216 fixed0x224 fixed0x232 fixed0x240 fixed0x248 fixed0x256
+syn keyword solBuiltinType       fixed8x8 fixed8x16 fixed8x24 fixed8x32 fixed8x40 fixed8x48 fixed8x56 fixed8x64 fixed8x72 fixed8x80 fixed8x88 fixed8x96 fixed8x104 fixed8x112 fixed8x120 fixed8x128 fixed8x136 fixed8x144 fixed8x152 fixed8x160 fixed8x168 fixed8x178 fixed8x184 fixed8x192 fixed8x200 fixed8x208 fixed8x216 fixed8x224 fixed8x232 fixed8x240 fixed8x248
+syn keyword solBuiltinType       fixed16x8 fixed16x16 fixed16x24 fixed16x32 fixed16x40 fixed16x48 fixed16x56 fixed16x64 fixed16x72 fixed16x80 fixed16x88 fixed16x96 fixed16x104 fixed16x112 fixed16x120 fixed16x128 fixed16x136 fixed16x144 fixed16x152 fixed16x160 fixed16x168 fixed16x178 fixed16x184 fixed16x192 fixed16x200 fixed16x208 fixed16x216 fixed16x224 fixed16x232 fixed16x240
+syn keyword solBuiltinType       fixed24x8 fixed24x16 fixed24x24 fixed24x32 fixed24x40 fixed24x48 fixed24x56 fixed24x64 fixed24x72 fixed24x80 fixed24x88 fixed24x96 fixed24x104 fixed24x112 fixed24x120 fixed24x128 fixed24x136 fixed24x144 fixed24x152 fixed24x160 fixed24x168 fixed24x178 fixed24x184 fixed24x192 fixed24x200 fixed24x208 fixed24x216 fixed24x224 fixed24x232
+syn keyword solBuiltinType       fixed32x8 fixed32x16 fixed32x24 fixed32x32 fixed32x40 fixed32x48 fixed32x56 fixed32x64 fixed32x72 fixed32x80 fixed32x88 fixed32x96 fixed32x104 fixed32x112 fixed32x120 fixed32x128 fixed32x136 fixed32x144 fixed32x152 fixed32x160 fixed32x168 fixed32x178 fixed32x184 fixed32x192 fixed32x200 fixed32x208 fixed32x216 fixed32x224
+syn keyword solBuiltinType       fixed40x8 fixed40x16 fixed40x24 fixed40x32 fixed40x40 fixed40x48 fixed40x56 fixed40x64 fixed40x72 fixed40x80 fixed40x88 fixed40x96 fixed40x104 fixed40x112 fixed40x120 fixed40x128 fixed40x136 fixed40x144 fixed40x152 fixed40x160 fixed40x168 fixed40x178 fixed40x184 fixed40x192 fixed40x200 fixed40x208 fixed40x216
+syn keyword solBuiltinType       fixed48x8 fixed48x16 fixed48x24 fixed48x32 fixed48x40 fixed48x48 fixed48x56 fixed48x64 fixed48x72 fixed48x80 fixed48x88 fixed48x96 fixed48x104 fixed48x112 fixed48x120 fixed48x128 fixed48x136 fixed48x144 fixed48x152 fixed48x160 fixed48x168 fixed48x178 fixed48x184 fixed48x192 fixed48x200 fixed48x208
+syn keyword solBuiltinType       fixed56x8 fixed56x16 fixed56x24 fixed56x32 fixed56x40 fixed56x48 fixed56x56 fixed56x64 fixed56x72 fixed56x80 fixed56x88 fixed56x96 fixed56x104 fixed56x112 fixed56x120 fixed56x128 fixed56x136 fixed56x144 fixed56x152 fixed56x160 fixed56x168 fixed56x178 fixed56x184 fixed56x192 fixed56x200
+syn keyword solBuiltinType       fixed64x8 fixed64x16 fixed64x24 fixed64x32 fixed64x40 fixed64x48 fixed64x56 fixed64x64 fixed64x72 fixed64x80 fixed64x88 fixed64x96 fixed64x104 fixed64x112 fixed64x120 fixed64x128 fixed64x136 fixed64x144 fixed64x152 fixed64x160 fixed64x168 fixed64x178 fixed64x184 fixed64x192
+syn keyword solBuiltinType       fixed72x8 fixed72x16 fixed72x24 fixed72x32 fixed72x40 fixed72x48 fixed72x56 fixed72x64 fixed72x72 fixed72x80 fixed72x88 fixed72x96 fixed72x104 fixed72x112 fixed72x120 fixed72x128 fixed72x136 fixed72x144 fixed72x152 fixed72x160 fixed72x168 fixed72x178 fixed72x184
+syn keyword solBuiltinType       fixed80x8 fixed80x16 fixed80x24 fixed80x32 fixed80x40 fixed80x48 fixed80x56 fixed80x64 fixed80x72 fixed80x80 fixed80x88 fixed80x96 fixed80x104 fixed80x112 fixed80x120 fixed80x128 fixed80x136 fixed80x144 fixed80x152 fixed80x160 fixed80x168 fixed80x178
+syn keyword solBuiltinType       fixed88x8 fixed88x16 fixed88x24 fixed88x32 fixed88x40 fixed88x48 fixed88x56 fixed88x64 fixed88x72 fixed88x80 fixed88x88 fixed88x96 fixed88x104 fixed88x112 fixed88x120 fixed88x128 fixed88x136 fixed88x144 fixed88x152 fixed88x160 fixed88x168
+syn keyword solBuiltinType       fixed96x8 fixed96x16 fixed96x24 fixed96x32 fixed96x40 fixed96x48 fixed96x56 fixed96x64 fixed96x72 fixed96x80 fixed96x88 fixed96x96 fixed96x104 fixed96x112 fixed96x120 fixed96x128 fixed96x136 fixed96x144 fixed96x152 fixed96x160
+syn keyword solBuiltinType       fixed104x8 fixed104x16 fixed104x24 fixed104x32 fixed104x40 fixed104x48 fixed104x56 fixed104x64 fixed104x72 fixed104x80 fixed104x88 fixed104x96 fixed104x104 fixed104x112 fixed104x120 fixed104x128 fixed104x136 fixed104x144 fixed104x152
+syn keyword solBuiltinType       fixed112x8 fixed112x16 fixed112x24 fixed112x32 fixed112x40 fixed112x48 fixed112x56 fixed112x64 fixed112x72 fixed112x80 fixed112x88 fixed112x96 fixed112x104 fixed112x112 fixed112x120 fixed112x128 fixed112x136 fixed112x144
+syn keyword solBuiltinType       fixed120x8 fixed120x16 fixed120x24 fixed120x32 fixed120x40 fixed120x48 fixed120x56 fixed120x64 fixed120x72 fixed120x80 fixed120x88 fixed120x96 fixed120x104 fixed120x112 fixed120x120 fixed120x128 fixed120x136
+syn keyword solBuiltinType       fixed128x8 fixed128x16 fixed128x24 fixed128x32 fixed128x40 fixed128x48 fixed128x56 fixed128x64 fixed128x72 fixed128x80 fixed128x88 fixed128x96 fixed128x104 fixed128x112 fixed128x120 fixed128x128
+syn keyword solBuiltinType       fixed136x8 fixed136x16 fixed136x24 fixed136x32 fixed136x40 fixed136x48 fixed136x56 fixed136x64 fixed136x72 fixed136x80 fixed136x88 fixed136x96 fixed136x104 fixed136x112 fixed136x120
+syn keyword solBuiltinType       fixed144x8 fixed144x16 fixed144x24 fixed144x32 fixed144x40 fixed144x48 fixed144x56 fixed144x64 fixed144x72 fixed144x80 fixed144x88 fixed144x96 fixed144x104 fixed144x112
+syn keyword solBuiltinType       fixed152x8 fixed152x16 fixed152x24 fixed152x32 fixed152x40 fixed152x48 fixed152x56 fixed152x64 fixed152x72 fixed152x80 fixed152x88 fixed152x96 fixed152x104
+syn keyword solBuiltinType       fixed160x8 fixed160x16 fixed160x24 fixed160x32 fixed160x40 fixed160x48 fixed160x56 fixed160x64 fixed160x72 fixed160x80 fixed160x88 fixed160x96
+syn keyword solBuiltinType       fixed168x8 fixed168x16 fixed168x24 fixed168x32 fixed168x40 fixed168x48 fixed168x56 fixed168x64 fixed168x72 fixed168x80 fixed168x88
+syn keyword solBuiltinType       fixed176x8 fixed176x16 fixed176x24 fixed176x32 fixed176x40 fixed176x48 fixed176x56 fixed176x64 fixed176x72 fixed176x80
+syn keyword solBuiltinType       fixed184x8 fixed184x16 fixed184x24 fixed184x32 fixed184x40 fixed184x48 fixed184x56 fixed184x64 fixed184x72
+syn keyword solBuiltinType       fixed192x8 fixed192x16 fixed192x24 fixed192x32 fixed192x40 fixed192x48 fixed192x56 fixed192x64
+syn keyword solBuiltinType       fixed200x8 fixed200x16 fixed200x24 fixed200x32 fixed200x40 fixed200x48 fixed200x56
+syn keyword solBuiltinType       fixed208x8 fixed208x16 fixed208x24 fixed208x32 fixed208x40 fixed208x48
+syn keyword solBuiltinType       fixed216x8 fixed216x16 fixed216x24 fixed216x32 fixed216x40
+syn keyword solBuiltinType       fixed224x8 fixed224x16 fixed224x24 fixed224x32
+syn keyword solBuiltinType       fixed232x8 fixed232x16 fixed232x24
+syn keyword solBuiltinType       fixed240x8 fixed240x16
+syn keyword solBuiltinType       fixed248x8
+syn keyword solBuiltinType       ufixed
+syn keyword solBuiltinType       ufixed0x8 ufixed0x16 ufixed0x24 ufixed0x32 ufixed0x40 ufixed0x48 ufixed0x56 ufixed0x64 ufixed0x72 ufixed0x80 ufixed0x88 ufixed0x96 ufixed0x104 ufixed0x112 ufixed0x120 ufixed0x128 ufixed0x136 ufixed0x144 ufixed0x152 ufixed0x160 ufixed0x168 ufixed0x178 ufixed0x184 ufixed0x192 ufixed0x200 ufixed0x208 ufixed0x216 ufixed0x224 ufixed0x232 ufixed0x240 ufixed0x248 ufixed0x256
+syn keyword solBuiltinType       ufixed8x8 ufixed8x16 ufixed8x24 ufixed8x32 ufixed8x40 ufixed8x48 ufixed8x56 ufixed8x64 ufixed8x72 ufixed8x80 ufixed8x88 ufixed8x96 ufixed8x104 ufixed8x112 ufixed8x120 ufixed8x128 ufixed8x136 ufixed8x144 ufixed8x152 ufixed8x160 ufixed8x168 ufixed8x178 ufixed8x184 ufixed8x192 ufixed8x200 ufixed8x208 ufixed8x216 ufixed8x224 ufixed8x232 ufixed8x240 ufixed8x248
+syn keyword solBuiltinType       ufixed16x8 ufixed16x16 ufixed16x24 ufixed16x32 ufixed16x40 ufixed16x48 ufixed16x56 ufixed16x64 ufixed16x72 ufixed16x80 ufixed16x88 ufixed16x96 ufixed16x104 ufixed16x112 ufixed16x120 ufixed16x128 ufixed16x136 ufixed16x144 ufixed16x152 ufixed16x160 ufixed16x168 ufixed16x178 ufixed16x184 ufixed16x192 ufixed16x200 ufixed16x208 ufixed16x216 ufixed16x224 ufixed16x232 ufixed16x240
+syn keyword solBuiltinType       ufixed24x8 ufixed24x16 ufixed24x24 ufixed24x32 ufixed24x40 ufixed24x48 ufixed24x56 ufixed24x64 ufixed24x72 ufixed24x80 ufixed24x88 ufixed24x96 ufixed24x104 ufixed24x112 ufixed24x120 ufixed24x128 ufixed24x136 ufixed24x144 ufixed24x152 ufixed24x160 ufixed24x168 ufixed24x178 ufixed24x184 ufixed24x192 ufixed24x200 ufixed24x208 ufixed24x216 ufixed24x224 ufixed24x232
+syn keyword solBuiltinType       ufixed32x8 ufixed32x16 ufixed32x24 ufixed32x32 ufixed32x40 ufixed32x48 ufixed32x56 ufixed32x64 ufixed32x72 ufixed32x80 ufixed32x88 ufixed32x96 ufixed32x104 ufixed32x112 ufixed32x120 ufixed32x128 ufixed32x136 ufixed32x144 ufixed32x152 ufixed32x160 ufixed32x168 ufixed32x178 ufixed32x184 ufixed32x192 ufixed32x200 ufixed32x208 ufixed32x216 ufixed32x224
+syn keyword solBuiltinType       ufixed40x8 ufixed40x16 ufixed40x24 ufixed40x32 ufixed40x40 ufixed40x48 ufixed40x56 ufixed40x64 ufixed40x72 ufixed40x80 ufixed40x88 ufixed40x96 ufixed40x104 ufixed40x112 ufixed40x120 ufixed40x128 ufixed40x136 ufixed40x144 ufixed40x152 ufixed40x160 ufixed40x168 ufixed40x178 ufixed40x184 ufixed40x192 ufixed40x200 ufixed40x208 ufixed40x216
+syn keyword solBuiltinType       ufixed48x8 ufixed48x16 ufixed48x24 ufixed48x32 ufixed48x40 ufixed48x48 ufixed48x56 ufixed48x64 ufixed48x72 ufixed48x80 ufixed48x88 ufixed48x96 ufixed48x104 ufixed48x112 ufixed48x120 ufixed48x128 ufixed48x136 ufixed48x144 ufixed48x152 ufixed48x160 ufixed48x168 ufixed48x178 ufixed48x184 ufixed48x192 ufixed48x200 ufixed48x208
+syn keyword solBuiltinType       ufixed56x8 ufixed56x16 ufixed56x24 ufixed56x32 ufixed56x40 ufixed56x48 ufixed56x56 ufixed56x64 ufixed56x72 ufixed56x80 ufixed56x88 ufixed56x96 ufixed56x104 ufixed56x112 ufixed56x120 ufixed56x128 ufixed56x136 ufixed56x144 ufixed56x152 ufixed56x160 ufixed56x168 ufixed56x178 ufixed56x184 ufixed56x192 ufixed56x200
+syn keyword solBuiltinType       ufixed64x8 ufixed64x16 ufixed64x24 ufixed64x32 ufixed64x40 ufixed64x48 ufixed64x56 ufixed64x64 ufixed64x72 ufixed64x80 ufixed64x88 ufixed64x96 ufixed64x104 ufixed64x112 ufixed64x120 ufixed64x128 ufixed64x136 ufixed64x144 ufixed64x152 ufixed64x160 ufixed64x168 ufixed64x178 ufixed64x184 ufixed64x192
+syn keyword solBuiltinType       ufixed72x8 ufixed72x16 ufixed72x24 ufixed72x32 ufixed72x40 ufixed72x48 ufixed72x56 ufixed72x64 ufixed72x72 ufixed72x80 ufixed72x88 ufixed72x96 ufixed72x104 ufixed72x112 ufixed72x120 ufixed72x128 ufixed72x136 ufixed72x144 ufixed72x152 ufixed72x160 ufixed72x168 ufixed72x178 ufixed72x184
+syn keyword solBuiltinType       ufixed80x8 ufixed80x16 ufixed80x24 ufixed80x32 ufixed80x40 ufixed80x48 ufixed80x56 ufixed80x64 ufixed80x72 ufixed80x80 ufixed80x88 ufixed80x96 ufixed80x104 ufixed80x112 ufixed80x120 ufixed80x128 ufixed80x136 ufixed80x144 ufixed80x152 ufixed80x160 ufixed80x168 ufixed80x178
+syn keyword solBuiltinType       ufixed88x8 ufixed88x16 ufixed88x24 ufixed88x32 ufixed88x40 ufixed88x48 ufixed88x56 ufixed88x64 ufixed88x72 ufixed88x80 ufixed88x88 ufixed88x96 ufixed88x104 ufixed88x112 ufixed88x120 ufixed88x128 ufixed88x136 ufixed88x144 ufixed88x152 ufixed88x160 ufixed88x168
+syn keyword solBuiltinType       ufixed96x8 ufixed96x16 ufixed96x24 ufixed96x32 ufixed96x40 ufixed96x48 ufixed96x56 ufixed96x64 ufixed96x72 ufixed96x80 ufixed96x88 ufixed96x96 ufixed96x104 ufixed96x112 ufixed96x120 ufixed96x128 ufixed96x136 ufixed96x144 ufixed96x152 ufixed96x160
+syn keyword solBuiltinType       ufixed104x8 ufixed104x16 ufixed104x24 ufixed104x32 ufixed104x40 ufixed104x48 ufixed104x56 ufixed104x64 ufixed104x72 ufixed104x80 ufixed104x88 ufixed104x96 ufixed104x104 ufixed104x112 ufixed104x120 ufixed104x128 ufixed104x136 ufixed104x144 ufixed104x152
+syn keyword solBuiltinType       ufixed112x8 ufixed112x16 ufixed112x24 ufixed112x32 ufixed112x40 ufixed112x48 ufixed112x56 ufixed112x64 ufixed112x72 ufixed112x80 ufixed112x88 ufixed112x96 ufixed112x104 ufixed112x112 ufixed112x120 ufixed112x128 ufixed112x136 ufixed112x144
+syn keyword solBuiltinType       ufixed120x8 ufixed120x16 ufixed120x24 ufixed120x32 ufixed120x40 ufixed120x48 ufixed120x56 ufixed120x64 ufixed120x72 ufixed120x80 ufixed120x88 ufixed120x96 ufixed120x104 ufixed120x112 ufixed120x120 ufixed120x128 ufixed120x136
+syn keyword solBuiltinType       ufixed128x8 ufixed128x16 ufixed128x24 ufixed128x32 ufixed128x40 ufixed128x48 ufixed128x56 ufixed128x64 ufixed128x72 ufixed128x80 ufixed128x88 ufixed128x96 ufixed128x104 ufixed128x112 ufixed128x120 ufixed128x128
+syn keyword solBuiltinType       ufixed136x8 ufixed136x16 ufixed136x24 ufixed136x32 ufixed136x40 ufixed136x48 ufixed136x56 ufixed136x64 ufixed136x72 ufixed136x80 ufixed136x88 ufixed136x96 ufixed136x104 ufixed136x112 ufixed136x120
+syn keyword solBuiltinType       ufixed144x8 ufixed144x16 ufixed144x24 ufixed144x32 ufixed144x40 ufixed144x48 ufixed144x56 ufixed144x64 ufixed144x72 ufixed144x80 ufixed144x88 ufixed144x96 ufixed144x104 ufixed144x112
+syn keyword solBuiltinType       ufixed152x8 ufixed152x16 ufixed152x24 ufixed152x32 ufixed152x40 ufixed152x48 ufixed152x56 ufixed152x64 ufixed152x72 ufixed152x80 ufixed152x88 ufixed152x96 ufixed152x104
+syn keyword solBuiltinType       ufixed160x8 ufixed160x16 ufixed160x24 ufixed160x32 ufixed160x40 ufixed160x48 ufixed160x56 ufixed160x64 ufixed160x72 ufixed160x80 ufixed160x88 ufixed160x96
+syn keyword solBuiltinType       ufixed168x8 ufixed168x16 ufixed168x24 ufixed168x32 ufixed168x40 ufixed168x48 ufixed168x56 ufixed168x64 ufixed168x72 ufixed168x80 ufixed168x88
+syn keyword solBuiltinType       ufixed176x8 ufixed176x16 ufixed176x24 ufixed176x32 ufixed176x40 ufixed176x48 ufixed176x56 ufixed176x64 ufixed176x72 ufixed176x80
+syn keyword solBuiltinType       ufixed184x8 ufixed184x16 ufixed184x24 ufixed184x32 ufixed184x40 ufixed184x48 ufixed184x56 ufixed184x64 ufixed184x72
+syn keyword solBuiltinType       ufixed192x8 ufixed192x16 ufixed192x24 ufixed192x32 ufixed192x40 ufixed192x48 ufixed192x56 ufixed192x64
+syn keyword solBuiltinType       ufixed200x8 ufixed200x16 ufixed200x24 ufixed200x32 ufixed200x40 ufixed200x48 ufixed200x56
+syn keyword solBuiltinType       ufixed208x8 ufixed208x16 ufixed208x24 ufixed208x32 ufixed208x40 ufixed208x48
+syn keyword solBuiltinType       ufixed216x8 ufixed216x16 ufixed216x24 ufixed216x32 ufixed216x40
+syn keyword solBuiltinType       ufixed224x8 ufixed224x16 ufixed224x24 ufixed224x32
+syn keyword solBuiltinType       ufixed232x8 ufixed232x16 ufixed232x24
+syn keyword solBuiltinType       ufixed240x8 ufixed240x16
+syn keyword solBuiltinType       ufixed248x8
+syn keyword solBuiltinType       string string1 string2 string3 string4 string5 string6 string7 string8 string9 string10 string11 string12 string13 string14 string15 string16 string17 string18 string19 string20 string21 string22 string23 string24 string25 string26 string27 string28 string29 string30 string31 string32
+syn keyword solBuiltinType       byte bytes bytes1 bytes2 bytes3 bytes4 bytes5 bytes6 bytes7 bytes8 bytes9 bytes10 bytes11 bytes12 bytes13 bytes14 bytes15 bytes16 bytes17 bytes18 bytes19 bytes20 bytes21 bytes22 bytes23 bytes24 bytes25 bytes26 bytes27 bytes28 bytes29 bytes30 bytes31 bytes32
+
+hi def link solKeyword           Keyword
+hi def link solConstant          Constant
+hi def link solBuiltinType       Type
+hi def link solBuiltinFunction   Keyword
+
+syn match   solOperator          /\(!\||\|&\|+\|-\|<\|>\|=\|%\|\/\|*\|\~\|\^\)/
+syn match   solNumber            /\<-\=\d\+L\=\>\|\<0[xX]\x\+\>/
+syn match   solFloat             /\<-\=\%(\d\+\.\d\+\|\d\+\.\|\.\d\+\)\%([eE][+-]\=\d\+\)\=\>/
+
+syn region  solString            start=+"+  skip=+\\\\\|\\$"\|\\"+  end=+"+
+syn region  solString            start=+'+  skip=+\\\\\|\\$'\|\\'+  end=+'+
+
+hi def link solOperator          Operator
+hi def link solNumber            Number
+hi def link solFloat             Float
+hi def link solString            String
+
+" Function
+syn match   solFunction          /\<function\>/ nextgroup=solFuncName,solFuncArgs skipwhite
+syn match   solFuncName          contained /\<[a-zA-Z_$][0-9a-zA-Z_$]*/ nextgroup=solFuncArgs skipwhite
+
+syn region  solFuncArgs          contained matchgroup=solFuncParens start='(' end=')' contains=solFuncArgCommas,solBuiltinType nextgroup=solModifierName,solFuncReturns,solFuncBody keepend skipwhite skipempty
+syn match   solModifierName      contained /\<[a-zA-Z_$][0-9a-zA-Z_$]*/ nextgroup=solModifierArgs,solModifierName skipwhite
+syn region  solModifierArgs      contained matchgroup=solFuncParens start='(' end=')' contains=solFuncArgCommas nextgroup=solModifierName,solFuncReturns,solFuncBody skipwhite
+syn region  solFuncReturns       contained matchgroup=solFuncParens nextgroup=solFuncBody start='(' end=')' contains=solFuncArgCommas,solBuiltinType skipwhite
+
+syn match   solFuncArgCommas     contained ','
+syn region  solFuncBody          start="{" end="}" fold transparent
+
+hi def link solFunction          Type
+hi def link solFuncName          Function
+hi def link solModifierName      Function
+
+" Yul blocks
+syn match   yul                  /\<assembly\>/ skipwhite skipempty nextgroup=yulBody
+syn region  yulBody              contained start='{' end='}' fold contains=yulAssemblyOp,solNumber,yulVarDeclaration,solLineComment,solComment skipwhite skipempty
+syn keyword yulAssemblyOp        contained stop add sub mul div sdiv mod smod exp not lt gt slt sgt eq iszero and or xor byte shl shr sar addmod mulmod signextend keccak256 pc pop mload mstore mstore8 sload sstore msize gas address balance selfbalance caller callvalue calldataload calldatasize calldatacopy codesize codecopy extcodesize extcodecopy returndatasize returndatacopy extcodehash create create2 call callcode delegatecall staticcall return revert selfdestruct invalid log0 log1 log2 log3 log4 chainid basefee origin gasprice blockhash coinbase timestamp number difficulty gaslimit
+syn keyword yulVarDeclaration    contained let
+
+hi def link yul                 Keyword
+hi def link yulVarDeclaration   Keyword
+hi def link yulAssemblyOp       Keyword
+
+" Contract
+syn match   solContract          /\<\%(contract\|library\|interface\)\>/ nextgroup=solContractName skipwhite
+syn match   solContractName      contained /\<[a-zA-Z_$][0-9a-zA-Z_$]*/ nextgroup=solContractParent skipwhite
+syn region  solContractParent    contained start='is' end='{' contains=solContractName,solContractNoise,solContractCommas skipwhite skipempty
+syn match   solContractNoise     contained 'is' containedin=solContractParent
+syn match   solContractCommas    contained ','
+
+hi def link solContract          Type
+hi def link solContractName      Function
+
+" Event
+syn match   solEvent             /\<event\>/ nextgroup=solEventName,solEventArgs skipwhite
+syn match   solEventName         contained /\<[a-zA-Z_$][0-9a-zA-Z_$]*/ nextgroup=solEventArgs skipwhite
+syn region  solEventArgs         contained matchgroup=solFuncParens start='(' end=')' contains=solEventArgCommas,solBuiltinType,solEventArgSpecial skipwhite skipempty
+syn match   solEventArgCommas    contained ','
+syn match   solEventArgSpecial   contained 'indexed'
+
+hi def link solEvent             Type
+hi def link solEventName         Function
+hi def link solEventArgSpecial   Label
+
+" Comment
+syn keyword solCommentTodo       TODO FIXME XXX TBD contained
+syn match solNatSpec             contained /@title\|@author\|@notice\|@dev\|@param\|@inheritdoc\|@return/
+syn region  solLineComment       start=+\/\/+ end=+$+ contains=solCommentTodo,solNatSpec,@Spell
+syn region  solLineComment       start=+^\s*\/\/+ skip=+\n\s*\/\/+ end=+$+ contains=solCommentTodo,solNatSpec,@Spell fold
+syn region  solComment           start="/\*"  end="\*/" contains=solCommentTodo,solNatSpec,@Spell fold
+
+hi def link solCommentTodo       Todo
+hi def link solNatSpec           Label
+hi def link solLineComment       Comment
+hi def link solComment           Comment
+
+let b:current_syntax = "solidity"
--- a/runtime/syntax/vim.vim
+++ b/runtime/syntax/vim.vim
@@ -401,7 +401,7 @@ syn match	vimSetMod	contained	"&vim\=\|[
 " Let: {{{2
 " ===
 syn keyword	vimLet	let	unl[et]	skipwhite nextgroup=vimVar,vimFuncVar,vimLetHereDoc
-VimFoldh syn region vimLetHereDoc	matchgroup=vimLetHereDocStart start='=<<\s\+\%(trim\|eval\>\)\=\s*\z(\L\S*\)'	matchgroup=vimLetHereDocStop end='^\s*\z1\s*$'
+VimFoldh syn region vimLetHereDoc	matchgroup=vimLetHereDocStart start='=<<\s\+\%(trim\s\+\)\=\%(eval\s\+\)\=\z(\L\S*\)'	matchgroup=vimLetHereDocStop end='^\s*\z1\s*$'
 
 " Abbreviations: {{{2
 " =============
--- a/src/INSTALLpc.txt
+++ b/src/INSTALLpc.txt
@@ -686,7 +686,6 @@ Ruby version vs. Ruby API version:
 
     Ruby ver. | Ruby API ver.
     =========================
-    1.8.X     | 1.8
     1.9.[1-3] | 1.9.1
     2.0.0     | 2.0.0
     2.X.Y     | 2.X.0