@@ -87,6 +87,67 @@ local function get_chunk_range_by_treesitter(pos)
8787 return chunkHelper .CHUNK_RANGE_RET .NO_CHUNK , Scope (pos .bufnr , - 1 , - 1 )
8888end
8989
90+ --- @param char string
91+ --- @param shiftwidth integer
92+ --- @return integer
93+ local function virt_text_char_width (char , shiftwidth )
94+ local b1 = char :byte (1 )
95+ if b1 == 0x00 then
96+ -- NULL is a terminator when used in virtual texts
97+ return 0
98+ elseif b1 == 0x09 then
99+ return shiftwidth
100+ elseif b1 <= 0x1F or b1 == 0x7F then
101+ -- control chars other than NULL and TAB are two cells wide
102+ return 2
103+ elseif b1 <= 0x7F then
104+ -- other ASCII chars are single cell wide
105+ return 1
106+ else
107+ return vim .api .nvim_strwidth (char )
108+ end
109+ end
110+
111+ --- faster alternative to `vim.fn.reverse()`
112+ --- unlike the original, this only supports lists
113+ --- @generic T
114+ --- @param list T[]
115+ --- @return T[]
116+ function chunkHelper .listReverse (list )
117+ local dst = {}
118+ for i , v in ipairs (list ) do
119+ dst [# list + 1 - i ] = v
120+ end
121+ return dst
122+ end
123+
124+ --- faster alternative to `vim.fn.repeat()`
125+ --- unlike the original, the input will be repeated as-is and the output will always be a list
126+ --- @generic T
127+ --- @param input T
128+ --- @param count integer
129+ --- @return T[]
130+ function chunkHelper .repeated (input , count )
131+ local dst = {}
132+ for i = 1 , count do
133+ dst [i ] = input
134+ end
135+ return dst
136+ end
137+
138+ --- faster alternative to `vim.list_extend()` (mutates dst!)
139+ --- unlike the original, this function lacks validation and range support
140+ --- @generic T
141+ --- @param dst T[]
142+ --- @param src T[]
143+ --- @return T[] dst
144+ function chunkHelper .list_extend (dst , src )
145+ for i = 1 , # src do
146+ dst [# dst + 1 ] = src [i ]
147+ end
148+ return dst
149+ end
150+
90151--- @param opts ? { pos : Pos , use_treesitter : boolean }
91152--- @return CHUNK_RANGE_RETCODE enum
92153--- @return Scope
123184function chunkHelper .utf8Split (inputstr )
124185 local list = {}
125186 for uchar in string.gmatch (inputstr , " [^\128 -\191 ][\128 -\191 ]*" ) do
126- table.insert ( list , uchar )
187+ list [ # list + 1 ] = uchar
127188 end
128189 return list
129190end
@@ -135,7 +196,7 @@ function chunkHelper.rangeFromTo(i, j, step)
135196 local t = {}
136197 step = step or 1
137198 for x = i , j , step do
138- table.insert ( t , x )
199+ t [ # t + 1 ] = x
139200 end
140201 return t
141202end
@@ -148,8 +209,8 @@ function chunkHelper.getColList(char_list, leftcol, shiftwidth)
148209 local t = {}
149210 local next_col = leftcol
150211 for i = 1 , # char_list do
151- table.insert ( t , next_col )
152- next_col = next_col + chunkHelper . virtTextStrWidth (char_list [i ], shiftwidth )
212+ t [ # t + 1 ] = next_col
213+ next_col = next_col + virt_text_char_width (char_list [i ], shiftwidth )
153214 end
154215 return t
155216end
@@ -179,7 +240,7 @@ function chunkHelper.repeatToWidth(str, width, shiftwidth)
179240 local current_width = str_width * repeatable_len
180241 local i = 1
181242 while i <= # chars do
182- local char_width = chunkHelper . virtTextStrWidth (chars [i ], shiftwidth )
243+ local char_width = virt_text_char_width (chars [i ], shiftwidth )
183244 --- assumed to be an out-of-bounds char (like in nerd fonts) followed by a whitespace if true
184245 local likely_oob_char =
185246 -- single-cell
@@ -231,18 +292,13 @@ end
231292--- @return boolean
232293function chunkHelper .checkCellsBlank (line , start_col , end_col , shiftwidth )
233294 local current_col = 1
234- local current_byte = 1
235295 local current_char = 1
236- while current_byte <= # line and current_col <= end_col do
237- local final_byte = vim . str_byteindex ( line , current_char )
238- local char = line : sub ( current_byte , final_byte )
296+ local chars = chunkHelper . utf8Split ( line )
297+ while current_char <= # chars and current_col <= end_col do
298+ local char = chars [ current_char ]
239299 local b1 , b2 , b3 = char :byte (1 , 3 )
240- if char == " " then
241- break
242- end
243300 --- @type integer
244301 local next_col
245- local next_byte = final_byte + 1
246302 local next_char = current_char + 1
247303 if char == " " then
248304 next_col = current_col + 1
@@ -256,13 +312,11 @@ function chunkHelper.checkCellsBlank(line, start_col, end_col, shiftwidth)
256312 next_col = current_col + 1
257313 else
258314 local char_width = vim .api .nvim_strwidth (char )
259- local next_byte_peek = line :byte (final_byte + 1 )
260- if char_width == 1 and next_byte_peek == 0x20 then
315+ if char_width == 1 and chars [current_char + 1 ] == " " then
261316 -- the char is assumed to be an out-of-bounds char (like in nerd fonts)
262317 -- followed by a whitespace
263318 next_col = current_col + 2
264319 -- skip the whitespace part of out-of-bounds char + " "
265- next_byte = next_byte + 1
266320 next_char = next_char + 1
267321 else
268322 next_col = current_col + char_width
@@ -334,7 +388,6 @@ function chunkHelper.checkCellsBlank(line, start_col, end_col, shiftwidth)
334388 return false
335389 end
336390 current_col = next_col
337- current_byte = next_byte
338391 current_char = next_char
339392 end
340393 return true
@@ -347,25 +400,11 @@ end
347400function chunkHelper .virtTextStrWidth (str , shiftwidth , stop_on_null )
348401 local current_width = 0
349402 for _ , char in ipairs (chunkHelper .utf8Split (str )) do
350- if char == " \0 " then
351- if stop_on_null then
352- return current_width
353- end
354- -- just ignore otherwise
355- elseif char == " \t " then
356- current_width = current_width + shiftwidth
357- else
358- local b1 = char :byte (1 )
359- if b1 <= 0x1F or b1 == 0x7F then
360- -- control chars other than NULL and TAB are two cells wide
361- current_width = current_width + 2
362- elseif b1 <= 0x7F then
363- -- other ASCII chars are single cell wide
364- current_width = current_width + 1
365- else
366- current_width = current_width + vim .api .nvim_strwidth (char )
367- end
403+ if stop_on_null and char == " \0 " then
404+ -- NULL is a terminator when used in virtual texts
405+ return current_width
368406 end
407+ current_width = current_width + virt_text_char_width (char , shiftwidth )
369408 end
370409 return current_width
371410end
0 commit comments