patch 9.2.1027: runtime(helptoc): FuzzySearch() highlights matched characters at the wrong column
Commit:
https://github.com/vim/vim/commit/762260130724cc51d2ef5762edbf847b15a56434
Author: VimWei <
vime...@gmail.com>
Date: Tue Sep 1 19:10:27 2026 +0000
patch 9.2.1027: runtime(helptoc): FuzzySearch() highlights matched characters at the wrong column
Problem: matchfuzzypos() returns match positions as character indices, but
text property col/length are counted in bytes. FuzzySearch()
used "col + 1" and "length: 1", so matched multibyte characters
were split and only their first byte was highlighted.
Solution: Convert character indices to byte offsets with byteidx() and use
the byte length of each matched character (VimWei).
fixes: #21056
closes: #21198
Signed-off-by: VimWei <
vime...@gmail.com>
Signed-off-by: Christian Brabandt <
c...@256bit.org>
diff --git a/runtime/pack/dist/opt/helptoc/autoload/helptoc.vim b/runtime/pack/dist/opt/helptoc/autoload/helptoc.vim
index f3ce9febf..6edbbe459 100644
--- a/runtime/pack/dist/opt/helptoc/autoload/helptoc.vim
+++ b/runtime/pack/dist/opt/helptoc/autoload/helptoc.vim
@@ -3,7 +3,7 @@ vim9script noclear
# the Vim HelpTOC plugin, creates a table of contents in a popup
# Maintainer: Vim project
# Original Author: @lacygoill
-# Latest Change: 2025 Oct 17
+# Latest Change: 2026 Aug 31
#
# Config {{{1
# g:helptoc {{{2
@@ -1124,9 +1124,9 @@ def FuzzySearch(winid: number) #{{{2
->get(0, [])
->map((i: number, match: dict<any>) => ({
text: match.text,
- props: pos[i]->copy()->map((_, col: number) => ({
- col: col + 1,
- length: 1,
+ props: pos[i]->copy()->map((_, cp: number) => ({
+ col: byteidx(match.text, cp) + 1,
+ length: byteidx(match.text, cp + 1) - byteidx(match.text, cp),
type: 'help-fuzzy-toc',
}))}))
endif
diff --git a/src/testdir/test_plugin_helptoc.vim b/src/testdir/test_plugin_helptoc.vim
index 6d311aecd..0e20e99c6 100644
--- a/src/testdir/test_plugin_helptoc.vim
+++ b/src/testdir/test_plugin_helptoc.vim
@@ -38,3 +38,34 @@ func Test_helptoc_markdown_with_comments()
call term_sendkeys(buf, ":qa!\<cr>")
call StopVimInTerminal(buf)
endfunc
+
+func Test_helptoc_fuzzy_multibyte()
+ packadd helptoc
+ new Xhelptoc_md.md
+ call setline(1, ['# 中文标题'])
+ setfiletype markdown
+
+ HelpToc
+ redraw
+ " Drive the fuzzy search through the popup filter: "/" opens the input()
+ " prompt, the query chars are typed into it, <Esc> leaves it.
+ call feedkeys("/标题\<Esc>", 'xt')
+ redraw
+
+ let winid = popup_list()[0]
+ let buf = winbufnr(winid)
+ let props = []
+ for lnum in range(1, line('$', winid))
+ for p in prop_list(lnum, {'bufnr': buf})
+ if p.type == 'help-fuzzy-toc'
+ call add(props, [p.col, p.length])
+ endif
+ endfor
+ endfor
+ " 中文标题: 中/文 are 3 bytes each, so 标 starts at byte 6 (col 7) and 题 at
+ " byte 9 (col 10), each 3 bytes long.
+ call assert_equal([[7, 3], [10, 3]], props)
+
+ call popup_close(winid)
+ bwipe!
+endfunc
diff --git a/src/version.c b/src/version.c
index 72f896875..5b0cc9e0c 100644
--- a/src/version.c
+++ b/src/version.c
@@ -763,6 +763,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
+/**/
+ 1027,
/**/
1026,
/**/