runtime(handlebars): adds handlebars template syntax & indent support

Commit: 
https://github.com/vim/vim/commit/99ea2b5b062edcb22ac0cd92c5ae317cc663a7ca
Author: Devin Weaver <[email protected]>
Date:   Thu Mar 5 20:06:02 2026 +0000

    runtime(handlebars): adds handlebars template syntax & indent support
    
    The runtime had support to detect handlebars (*.hbs) files as filetype
    handlebars but was lacking any indent or syntax highlighting for that
    filetype.
    
    The handlebars syntax file is also a prerequisite for the glimmer
    syntax.
    
    Permission was granted by the original author to retrofit these into the
    Vim runtime. Original License (MIT) maintained in code comments.
    
    related: #19569
    
    Signed-off-by: Devin Weaver <[email protected]>
    Signed-off-by: Christian Brabandt <[email protected]>

diff --git a/.github/MAINTAINERS b/.github/MAINTAINERS
index daf31d7cf..92e9e6025 100644
--- a/.github/MAINTAINERS
+++ b/.github/MAINTAINERS
@@ -388,6 +388,7 @@ runtime/indent/go.vim                                       
@dbarnett
 runtime/indent/graphql.vim                             @jparise
 runtime/indent/gyp.vim                                 @ObserverOfTime
 runtime/indent/haml.vim                                        @tpope
+runtime/indent/handlebars.vim                          @sukima
 runtime/indent/hare.vim                                        @selenebun
 runtime/indent/hcl.vim                                 @gpanders
 runtime/indent/hog.vim                                 @wtfbbqhax
@@ -551,6 +552,7 @@ runtime/syntax/graphql.vim                          @jparise
 runtime/syntax/groff.vim                               @jmarshall
 runtime/syntax/gyp.vim                                 @ObserverOfTime
 runtime/syntax/haml.vim                                        @tpope
+runtime/syntax/handlebars.vim                          @sukima
 runtime/syntax/hare.vim                                        @selenebun
 runtime/syntax/haredoc.vim                             @selenebun
 runtime/syntax/haskell.vim                             @coot
diff --git a/runtime/indent/handlebars.vim b/runtime/indent/handlebars.vim
new file mode 100644
index 000000000..6b76b5a39
--- /dev/null
+++ b/runtime/indent/handlebars.vim
@@ -0,0 +1,128 @@
+" Vim indent file
+" Language:     Handlebars
+" Maintainer:   Devin Weaver
+" Last Change:  2026 Feb 20
+" Origin:       https://github.com/joukevandermaas/vim-ember-hbs
+" Credits:      Jouke van der Maas
+" Acknowledgement: Based on eruby.vim indentation by TPope
+" License:      MIT
+" The MIT License (MIT)
+"
+" Copyright (c) 2026 Devin Weaver
+" Copyright (c) 2015 Jouke van der Maas
+"
+" Permission is hereby granted, free of charge, to any person obtaining a copy
+" of this software and associated documentation files (the "Software"), to deal
+" in the Software without restriction, including without limitation the rights
+" to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+" copies of the Software, and to permit persons to whom the Software is
+" furnished to do so, subject to the following conditions:
+"
+" The above copyright notice and this permission notice shall be included in 
all
+" copies or substantial portions of the Software.
+"
+" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+" IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+" FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+" AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+" LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+" OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+" SOFTWARE.
+
+if exists("b:did_indent")
+  finish
+endif
+
+runtime! indent/html.vim
+unlet! b:did_indent
+
+" Force HTML indent to not keep state.
+let b:html_indent_usestate = 0
+let b:handlebars_current_indent = 0
+
+if &l:indentexpr == ''
+  if &l:cindent
+    let &l:indentexpr = 'cindent(v:lnum)'
+  else
+    let &l:indentexpr = 'indent(prevnonblank(v:lnum-1))'
+  endif
+endif
+let b:handlebars_subtype_indentexpr = &l:indentexpr
+
+let b:did_indent = 1
+
+setlocal indentexpr=GetHandlebarsIndent()
+setlocal indentkeys=o,O,*<Return>,<>>,{,},0),0],o,O,!^F,=else,={{#,={{/
+
+" Only define the function once.
+if exists("*GetHandlebarsIndent")
+  finish
+endif
+
+function! GetHandlebarsIndent(...)
+  " The value of a single shift-width
+  let sw = shiftwidth()
+
+  if a:0 && a:1 == '.'
+    let v:lnum = line('.')
+  elseif a:0 && a:1 =~ '^\d'
+    let v:lnum = a:1
+  endif
+  let vcol = col('.')
+  call cursor(v:lnum,1)
+  call cursor(v:lnum,vcol)
+  exe "let ind = ".b:handlebars_subtype_indentexpr
+
+  " Workaround for Andy Wokula's HTML indent. This should be removed after
+  " some time, since the newest version is fixed in a different way. Credit
+  " to eruby.vim indent by tpope
+  if b:handlebars_subtype_indentexpr =~# '^HtmlIndent('
+  \ && exists('b:indent')
+  \ && type(b:indent) == type({})
+  \ && has_key(b:indent, 'lnum')
+    " Force HTML indent to not keep state
+    let b:indent.lnum = -1
+  endif
+
+  let lnum = prevnonblank(v:lnum-1)
+  let prevLine = getline(lnum)
+  let currentLine = getline(v:lnum)
+
+  " all indent rules only apply if the block opening/closing
+  " tag is on a separate line
+
+  " indent after block {{#block
+  if prevLine =~# ' \s*\{\{\#'
+    let ind = ind + sw
+  endif
+  " but not if the block ends on the same line
+  if prevLine =~# ' \s*\{\{\#(.+)(\s+|\}\}).+\{\{\/ '
+    let ind = ind - sw
+  endif
+  " unindent after block close {{/block}}
+  if currentLine =~# ' ^\s*\{\{\/'
+    let ind = ind - sw
+  endif
+  " indent after component block {{a-component
+  if prevLine =~# ' \s*\{\{\w'
+     let ind = ind + sw
+  endif
+  " but not if the component block ends on the same line
+  if prevLine =~# ' \s*\{\{\w(.+)\}\}'
+    let ind = ind - sw
+  endif
+  " unindent }} lines
+  if currentLine =~# ' ^\s*\}\}\s*$' || (currentLine !~# ' ^\s*\{\{\/' && 
prevLine =~# ' ^\s*[^\{\}]+\}\}\s*$')
+    let ind = ind - sw
+  endif
+  " unindent {{else}}
+  if currentLine =~# ' ^\s*\{\{else'
+    let ind = ind - sw
+  endif
+  " indent again after {{else}}
+  if prevLine =~# ' ^\s*\{\{else'
+    let ind = ind + sw
+  endif
+
+  return ind
+endfunction
diff --git a/runtime/syntax/handlebars.vim b/runtime/syntax/handlebars.vim
new file mode 100644
index 000000000..439a2284f
--- /dev/null
+++ b/runtime/syntax/handlebars.vim
@@ -0,0 +1,144 @@
+" Vim syntax file
+" Language:     Handlebars
+" Maintainer:   Devin Weaver
+" Last Change:  2026 Feb 20
+" Origin:       https://github.com/joukevandermaas/vim-ember-hbs
+" Credits:      Jouke van der Maas
+" License:      MIT
+" The MIT License (MIT)
+"
+" Copyright (c) 2026 Devin Weaver
+" Copyright (c) 2015 Jouke van der Maas
+"
+" Permission is hereby granted, free of charge, to any person obtaining a copy
+" of this software and associated documentation files (the "Software"), to deal
+" in the Software without restriction, including without limitation the rights
+" to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+" copies of the Software, and to permit persons to whom the Software is
+" furnished to do so, subject to the following conditions:
+"
+" The above copyright notice and this permission notice shall be included in 
all
+" copies or substantial portions of the Software.
+"
+" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+" IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+" FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+" AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+" LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+" OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+" SOFTWARE.
+
+if exists("b:current_syntax")
+  finish
+endif
+
+runtime! syntax/html.vim
+syntax cluster htmlPreproc 
add=hbsComponent,hbsMustache,hbsUnescaped,hbsMustacheBlock,hbsComment,hbsElseBlock,hbsEscapedMustache
+
+syntax match hbsEscapedMustache " \\{\{"
+
+syntax region hbsComponent matchgroup=hbsComponentStatement start=" \<\/?:? 
+(\. +|::-? +)*" end=" \/?\>" keepend
+syntax region hbsMustache matchgroup=hbsHandles start=" \{\{" skip=" \\}\}" 
end=" \}\}" containedin=hbsComponent,hbsString keepend
+syntax region hbsMustacheBlock matchgroup=hbsHandles start=" \{\{[#/]" skip=" 
\\}\}" end=" \}\}" keepend
+" modern hbs supports {{else <block>}} where <block> starts a new block
+syntax region hbsElseBlock matchgroup=hbsHandles start=" \{\{else\ "rs=e-5 
skip=" \\}\}" end=" \}\}" keepend
+
+syntax region hbsPencil matchgroup=hbsOperator start=" \(" end=" \)" contained 
containedin=hbsMustache,hbsMustacheBlock,hbsElseBlock,hbsPencil
+
+" identifier is any word inside a mustache or a pencil that is not followed by 
a = sign (see hbsArg below)
+syntax match hbsIdentifier " (\(|\{\{[#/]?)@<!<(\w+)|(\@\w+)>" contained 
containedin=hbsMustache,hbsMustacheBlock,hbsPencil,hbsElseBlock,hbsStatement
+
+" unescaped are special forms of mustaches that don't have other stuff except 
for an identifier in it
+syntax region hbsUnescaped matchgroup=hbsUnescapedHandles start=" \{\{\{" 
skip=" \\}\}\}" end=" \}\}\}" keepend
+syntax match hbsUnescapedIdentifier " (\{\{\{)@<=<\S+>(\}\}\})" contained 
containedin=hbsUnescaped
+
+syntax match hbsMustacheName " (\{\{[#/]?)@<=<\S+>" contained 
containedin=hbsMustache,hbsMustacheBlock,hbsPencil
+syntax match hbsPencilName " (\()@<=<\S+>" contained 
containedin=hbsMustache,hbsMustacheBlock,hbsPencil
+syntax match hbsBuiltInHelper " 
\(@<=<(query-params|mut|fn|array|hash|get|action|unbound|concat)>" contained 
containedin=hbsPencil
+syntax match hbsBuiltInHelper " 
(\{\{)@<=<(textarea|mut|fn|array|hash|input|get|action|on|input|unbound)>" 
contained containedin=hbsMustache
+syntax match hbsBuiltInHelper " (\{\{[#/]?)@<=<(component|with|link\-to)>" 
contained containedin=hbsMustacheBlock,hbsElseBlock
+syntax match hbsBuiltInHelperInElse " (\{\{else\ )@<=<(component|link\-to)>" 
contained containedin=hbsMustacheBlock,hbsElseBlock
+syntax match hbsControlFlow " (\{\{)@<=<else>( ?)@=" contained 
containedin=hbsElseBlock
+syntax match hbsControlFlow " \(@<=<(if|unless)>" contained 
containedin=hbsPencil
+syntax match hbsControlFlow " (\{\{)@<=<(debugger|unless|yield|outlet|else)>" 
contained containedin=hbsMustache
+syntax match hbsControlFlow " 
(\{\{[#/]?)@<=<(with|let|if|each(\-in)?|unless)>" contained 
containedin=hbsMustacheBlock,hbsElseBlock
+syntax match hbsKeyword " \s+as\s+" contained 
containedin=hbsComponent,hbsMustacheBlock,hbsElseBlock
+syntax region hbsStatement matchgroup=hbsDelimiter start=" \|" end=" \|" 
contained containedin=hbsComponent,hbsMustacheBlock,hbsElseBlock
+
+syntax region hbsString matchgroup=hbsString start=/ \"/ skip=/ \\"/ end=/ \"/ 
extend contained 
containedin=hbsComponent,hbsMustache,hbsMustacheBlock,hbsPencil,hbsElseBlock
+syntax region hbsString matchgroup=hbsString start=/ \'/ skip=/ \\'/ end=/ \'/ 
extend contained 
containedin=hbsComponent,hbsMustache,hbsMustacheBlock,hbsPencil,hbsElseBlock
+syntax match hbsNumber " <\d+>" contained 
containedin=hbsComponent,hbsMustache,hbsMustacheBlock,hbsPencil,hbsElseBlock
+syntax match hbsBool " <(true|false)>" contained 
containedin=hbsComponent,hbsMustache,hbsMustacheBlock,hbsPencil,hbsElseBlock
+syntax match hbsArg " (\@\S+|\S+)\=@=" contained 
containedin=hbsComponent,hbsMustache,hbsMustacheBlock,hbsPencil,hbsElseBlock
+syntax match hbsOperator " (\S+)@<=\=" contained 
containedin=hbsComponent,hbsMustache,hbsMustacheBlock,hbsPencil,hbsElseBlock
+
+syntax region hbsComment start=" \{\{\!" end=" \}\}" keepend
+syntax region hbsComment start=" \{\{\!\-\-" end=" \-\-\}\}" keepend
+
+" *Comment     any comment
+
+" *Constant    any constant
+"  String              a string constant: "this is a string"
+"  Character   a character constant: 'c', '
'
+"  Number              a number constant: 234, 0xff
+"  Boolean     a boolean constant: TRUE, false
+"  Float               a floating point constant: 2.3e10
+
+" *Identifier  any variable name
+"  Function    function name (also: methods for classes)
+
+" *Statement   any statement
+"  Conditional if, then, else, endif, switch, etc.
+"  Repeat              for, do, while, etc.
+"  Label               case, default, etc.
+"  Operator    "sizeof", "+", "*", etc.
+"  Keyword     any other keyword
+"  Exception   try, catch, throw
+
+" *PreProc     generic Preprocessor
+"  Include     preprocessor #include
+"  Define              preprocessor #define
+"  Macro               same as Define
+"  PreCondit   preprocessor #if, #else, #endif, etc.
+
+" *Type                int, long, char, etc.
+"  StorageClass        static, register, volatile, etc.
+"  Structure   struct, union, enum, etc.
+"  Typedef     A typedef
+
+" *Special     any special symbol
+"  SpecialChar special character in a constant
+"  Tag         you can use CTRL-] on this
+"  Delimiter   character that needs attention
+"  SpecialComment      special things inside a comment
+"  Debug               debugging statements
+
+" *Underlined  text that stands out, HTML links
+
+" *Ignore              left blank, hidden  |hl-Ignore|
+
+" *Error               any erroneous construct
+
+" *Todo                anything that needs extra attention; mostly the
+"              keywords TODO FIXME and XXX
+
+highlight link hbsBuiltInHelper Function
+highlight link hbsBuiltInHelperInElse Function
+highlight link hbsControlFlow Function
+highlight link hbsKeyword Keyword
+highlight link hbsOperator Operator
+highlight link hbsDelimiter Delimiter
+highlight link hbsMustacheName Statement
+highlight link hbsPencilName Statement
+highlight link hbsIdentifier Identifier
+highlight link hbsString String
+highlight link hbsNumber Special
+highlight link hbsBool Boolean
+highlight link hbsHandles Define
+highlight link hbsComponentStatement Define
+highlight link hbsUnescapedHandles Identifier
+highlight link hbsUnescapedIdentifier Identifier
+highlight link hbsComment Comment
+highlight link hbsArg Type
+
+let b:current_syntax = "handlebars"

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion visit 
https://groups.google.com/d/msgid/vim_dev/E1vyF69-008Ucx-Sb%40256bit.org.

Raspunde prin e-mail lui