From aa9ab2e728acac6dd4a88402ab273c042e154a73 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 6 Mar 2026 07:59:09 +0800 Subject: [PATCH 1/2] vim-patch:99ea2b5: 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: vim/vim#19569 https://github.com/vim/vim/commit/99ea2b5b062edcb22ac0cd92c5ae317cc663a7ca Co-authored-by: Devin Weaver --- runtime/indent/handlebars.vim | 128 ++++++++++++++++++++++++++++++ runtime/syntax/handlebars.vim | 144 ++++++++++++++++++++++++++++++++++ 2 files changed, 272 insertions(+) create mode 100644 runtime/indent/handlebars.vim create mode 100644 runtime/syntax/handlebars.vim diff --git a/runtime/indent/handlebars.vim b/runtime/indent/handlebars.vim new file mode 100644 index 0000000000..6b76b5a39c --- /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,*,<>>,{,},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 =~# '\v\s*\{\{\#' + let ind = ind + sw + endif + " but not if the block ends on the same line + if prevLine =~# '\v\s*\{\{\#(.+)(\s+|\}\}).+\{\{\/\1' + let ind = ind - sw + endif + " unindent after block close {{/block}} + if currentLine =~# '\v^\s*\{\{\/' + let ind = ind - sw + endif + " indent after component block {{a-component + if prevLine =~# '\v\s*\{\{\w' + let ind = ind + sw + endif + " but not if the component block ends on the same line + if prevLine =~# '\v\s*\{\{\w(.+)\}\}' + let ind = ind - sw + endif + " unindent }} lines + if currentLine =~# '\v^\s*\}\}\s*$' || (currentLine !~# '\v^\s*\{\{\/' && prevLine =~# '\v^\s*[^\{\}]+\}\}\s*$') + let ind = ind - sw + endif + " unindent {{else}} + if currentLine =~# '\v^\s*\{\{else' + let ind = ind - sw + endif + " indent again after {{else}} + if prevLine =~# '\v^\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 0000000000..439a2284f6 --- /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 "\v\\\{\{" + +syntax region hbsComponent matchgroup=hbsComponentStatement start="\v\<\/?:?\a+(\.\a+|::-?\a+)*" end="\v\/?\>" keepend +syntax region hbsMustache matchgroup=hbsHandles start="\v\{\{" skip="\v\\\}\}" end="\v\}\}" containedin=hbsComponent,hbsString keepend +syntax region hbsMustacheBlock matchgroup=hbsHandles start="\v\{\{[#/]" skip="\v\\\}\}" end="\v\}\}" keepend +" modern hbs supports {{else }} where starts a new block +syntax region hbsElseBlock matchgroup=hbsHandles start="\v\{\{else\ "rs=e-5 skip="\v\\\}\}" end="\v\}\}" keepend + +syntax region hbsPencil matchgroup=hbsOperator start="\v\(" end="\v\)" 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 "\v(\(|\{\{[#/]?)@" 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="\v\{\{\{" skip="\v\\\}\}\}" end="\v\}\}\}" keepend +syntax match hbsUnescapedIdentifier "\v(\{\{\{)@<=<\S+>(\}\}\})" contained containedin=hbsUnescaped + +syntax match hbsMustacheName "\v(\{\{[#/]?)@<=<\S+>" contained containedin=hbsMustache,hbsMustacheBlock,hbsPencil +syntax match hbsPencilName "\v(\()@<=<\S+>" contained containedin=hbsMustache,hbsMustacheBlock,hbsPencil +syntax match hbsBuiltInHelper "\v\(@<=<(query-params|mut|fn|array|hash|get|action|unbound|concat)>" contained containedin=hbsPencil +syntax match hbsBuiltInHelper "\v(\{\{)@<=<(textarea|mut|fn|array|hash|input|get|action|on|input|unbound)>" contained containedin=hbsMustache +syntax match hbsBuiltInHelper "\v(\{\{[#/]?)@<=<(component|with|link\-to)>" contained containedin=hbsMustacheBlock,hbsElseBlock +syntax match hbsBuiltInHelperInElse "\v(\{\{else\ )@<=<(component|link\-to)>" contained containedin=hbsMustacheBlock,hbsElseBlock +syntax match hbsControlFlow "\v(\{\{)@<=( ?)@=" contained containedin=hbsElseBlock +syntax match hbsControlFlow "\v\(@<=<(if|unless)>" contained containedin=hbsPencil +syntax match hbsControlFlow "\v(\{\{)@<=<(debugger|unless|yield|outlet|else)>" contained containedin=hbsMustache +syntax match hbsControlFlow "\v(\{\{[#/]?)@<=<(with|let|if|each(\-in)?|unless)>" contained containedin=hbsMustacheBlock,hbsElseBlock +syntax match hbsKeyword "\v\s+as\s+" contained containedin=hbsComponent,hbsMustacheBlock,hbsElseBlock +syntax region hbsStatement matchgroup=hbsDelimiter start="\v\|" end="\v\|" contained containedin=hbsComponent,hbsMustacheBlock,hbsElseBlock + +syntax region hbsString matchgroup=hbsString start=/\v\"/ skip=/\v\\\"/ end=/\v\"/ extend contained containedin=hbsComponent,hbsMustache,hbsMustacheBlock,hbsPencil,hbsElseBlock +syntax region hbsString matchgroup=hbsString start=/\v\'/ skip=/\v\\\'/ end=/\v\'/ extend contained containedin=hbsComponent,hbsMustache,hbsMustacheBlock,hbsPencil,hbsElseBlock +syntax match hbsNumber "\v<\d+>" contained containedin=hbsComponent,hbsMustache,hbsMustacheBlock,hbsPencil,hbsElseBlock +syntax match hbsBool "\v<(true|false)>" contained containedin=hbsComponent,hbsMustache,hbsMustacheBlock,hbsPencil,hbsElseBlock +syntax match hbsArg "\v(\@\S+|\S+)\=@=" contained containedin=hbsComponent,hbsMustache,hbsMustacheBlock,hbsPencil,hbsElseBlock +syntax match hbsOperator "\v(\S+)@<=\=" contained containedin=hbsComponent,hbsMustache,hbsMustacheBlock,hbsPencil,hbsElseBlock + +syntax region hbsComment start="\v\{\{\!" end="\v\}\}" keepend +syntax region hbsComment start="\v\{\{\!\-\-" end="\v\-\-\}\}" keepend + +" *Comment any comment + +" *Constant any constant +" String a string constant: "this is a string" +" Character a character constant: 'c', '\n' +" 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" From 2926668f29f5c3e3f83ead9e0d127dbc6f32c0f2 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 6 Mar 2026 07:59:38 +0800 Subject: [PATCH 2/2] vim-patch:75decb4: runtime(glimmer): add syntax support for glimmer files In commit cdf717283 ("patch 8.2.4424: ".gts" and ".gjs" files are not recognized", 2022-02-19) support for the glimmer file types were added. Problem: Syntax hilighting suppoprt was missing. Solution: Added a glimmer syntax file that will leverage the base syntaxs (javascript/typescript) and include handlebars syntax for .gjs/.gts files. closes: vim/vim#19569 https://github.com/vim/vim/commit/75decb4a8d10c2e529add1f0e7e0a545576de2ea Co-authored-by: Devin Weaver --- runtime/syntax/glimmer.vim | 51 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 runtime/syntax/glimmer.vim diff --git a/runtime/syntax/glimmer.vim b/runtime/syntax/glimmer.vim new file mode 100644 index 0000000000..67555db857 --- /dev/null +++ b/runtime/syntax/glimmer.vim @@ -0,0 +1,51 @@ +" Vim syntax file +" Language: Glimmer +" Maintainer: Devin Weaver +" Last Change: 2026 Feb 20 +" Origin: https://github.com/joukevandermaas/vim-ember-hbs +" Credits: Jouke van der Maas +" License: Same as Vim + +" Vim detects GJS/GTS files as {java,type}script.glimmer +" Vim will read the javascript/typescript syntax files first and set +" b:current_syntax accordingly then it will read the glimmer syntax file. +" This is why we use b:current_syntax to make sure we are in the correct state +" to continue. + +if exists('b:current_syntax') && b:current_syntax !~# '\v%(type|java)script' + finish +endif + +let base_syntax = b:current_syntax +unlet! b:current_syntax + +let s:cpo_save = &cpo +set cpo&vim + +syntax include @hbs syntax/handlebars.vim + +if base_syntax == "javascript" + syntax region glimmerTemplateBlock + \ start="" + \ contains=@hbs + \ keepend fold + + let b:current_syntax = "javascript.glimmer" +else + " syntax/typescript.vim adds typescriptTypeCast which is in conflict with + "