After dealing with Go templates (also HTML templates) for more than a year and keep loosing track of where each block {{end}}s I finally did something about it.

But that didn’t work that good. So I’ve switch to the match-up plugin, which is better than the built-in matchit that I was using previously. With Neovim I’m using this setup:

Plug 'andymass/vim-matchup'

And this extra bit of config:

require'nvim-treesitter.configs'.setup {
  matchup = {
    enable = true,
  },
}

This in itself does not make template matching work, for this I just copied the Hugo template matcher (which is just like Go templating) to the correct place. Doing this with some automation from inside Neovim:

let vim_after=expand('~/.config/nvim/after/ftplugin')
if !isdirectory(vim_after)
  silent execute "!mkdir -p ~/.config/nvim/after/ftplugin"
  silent execute "!cp ~/.dotfiles/after/* ~/.config/nvim/after/ftplugin/"
endif

Update 2025-05-11

I have since moved to a full Lua config in neovim (using a bit of NvChad. My config now looks like:

In lua/plugins/vim-matchup.lua:

return {
	{
		"andymass/vim-matchup",
		event = { "BufReadPost" },
		config = function()
			vim.cmd([[
                let s:block_start = '\(define\|block\|with\|range\|if\)'
                call matchup#util#append_match_words(
                       \ '{{-\?\s*' . s:block_start . '.\{-}\s*-\?}}'
                       \ . ':{{-\?\s*end\s*-\?}}')
                call matchup#util#append_match_words('{{:}}')
            ]])
		end,
	},
}

And lua/plugins/init.lua - a NvChad thing(?), in the treesitter config:

                ...
                ....
				"vim",
				"vimdoc",
				"yaml",
			},
			matchup = { -- see vim-matchup.lua
				enable = true,
			},
		},
	},