patch 9.1.1623: Buffer menu does not handle unicode names correctly
Problem: Buffer menu does not handle unicode names correctly (after v9.1.1622) Solution: Fix the BMHash() function (Yee Cheng Chin) The Buffers menu uses a BMHash() function to generate a sortable number to be used for the menu index. It used a naive (and incorrect) way of encoding multiple ASCII values into a single integer, but assumes each character to be only in the ASCII 32-96 range. This means if we use non-ASCII file names (e.g. Unicode values like CJK or emojis) we get integer underflow and overflow, causing the menu index to wrap around. Vim's GUI implementations internally use a signed 32-bit integer for the `gui_mch_add_menu_item()` function and so we need to make sure the menu index is in the (0, 2^31-1) range. To do this, if the file name starts with a non-ASCII value, we just use the first character's value and set the high bit so it sorts after the other ASCII ones. Otherwise, we just take the first 5 characters, and use 5 bit for each character to encode a 30-bit number that can be sorted. This means Unicode file names won't be sorted beyond the first character. This is likely going to be fine as there are lots of ways to query buffers. related: #17403 closes: #17928 Signed-off-by: Yee Cheng Chin <ychin.git@gmail.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
committed by
Christian Brabandt
parent
cda0d17f59
commit
8f9de4991e
@ -2,7 +2,7 @@
|
||||
" You can also use this as a start for your own set of menus.
|
||||
"
|
||||
" Maintainer: The Vim Project <https://github.com/vim/vim>
|
||||
" Last Change: 2023 Aug 10
|
||||
" Last Change: 2025 Aug 10
|
||||
" Former Maintainer: Bram Moolenaar <Bram@vim.org>
|
||||
|
||||
" Note that ":an" (short for ":anoremenu") is often used to make a menu work
|
||||
@ -797,8 +797,21 @@ def s:BMShow()
|
||||
enddef
|
||||
|
||||
def s:BMHash(name: string): number
|
||||
# Make name all upper case, so that chars are between 32 and 96
|
||||
var nm = substitute(name, ".*", '\U\0', "")
|
||||
# Create a sortable numeric hash of the name. This number has to be within
|
||||
# the bounds of a signed 32-bit integer as this is what Vim GUI uses
|
||||
# internally for the index.
|
||||
|
||||
# Make name all upper case, so that alphanumeric chars are between 32 and 96
|
||||
var nm = toupper(name)
|
||||
|
||||
if char2nr(nm[0]) < 32 || char2nr(nm[0]) > 96
|
||||
# We don't have an ASCII character, so just return the raw character value
|
||||
# for first character (clamped to 2^31) and set the high bit to make it
|
||||
# sort after other items. This means only the first character will be
|
||||
# sorted, unfortunately.
|
||||
return or(and(char2nr(nm), 0x7fffffff), 0x40000000)
|
||||
endif
|
||||
|
||||
var sp: number
|
||||
if has("ebcdic")
|
||||
# HACK: Replace all non alphabetics with 'Z'
|
||||
@ -808,12 +821,18 @@ def s:BMHash(name: string): number
|
||||
else
|
||||
sp = char2nr(' ')
|
||||
endif
|
||||
# convert first six chars into a number for sorting:
|
||||
return (char2nr(nm[0]) - sp) * 0x800000 + (char2nr(nm[1]) - sp) * 0x20000 + (char2nr(nm[2]) - sp) * 0x1000 + (char2nr(nm[3]) - sp) * 0x80 + (char2nr(nm[4]) - sp) * 0x20 + (char2nr(nm[5]) - sp)
|
||||
# convert first five chars into a number for sorting by compressing each
|
||||
# char into 5 bits (0-63), to a total of 30 bits. If any character is not
|
||||
# ASCII, it will simply be clamped to prevent overflow.
|
||||
return (max([0, min([63, char2nr(nm[0]) - sp])]) << 24) +
|
||||
(max([0, min([63, char2nr(nm[1]) - sp])]) << 18) +
|
||||
(max([0, min([63, char2nr(nm[2]) - sp])]) << 12) +
|
||||
(max([0, min([63, char2nr(nm[3]) - sp])]) << 6) +
|
||||
max([0, min([63, char2nr(nm[4]) - sp])])
|
||||
enddef
|
||||
|
||||
def s:BMHash2(name: string): string
|
||||
var nm = substitute(name, ".", '\L\0', "")
|
||||
var nm = tolower(name[0])
|
||||
if nm[0] < 'a' || nm[0] > 'z'
|
||||
return '&others.'
|
||||
elseif nm[0] <= 'd'
|
||||
|
Reference in New Issue
Block a user