@@ -91,7 +91,68 @@ local function get_chunk_range_by_treesitter(pos)
9191 return chunkHelper .CHUNK_RANGE_RET .NO_CHUNK , Scope (pos .bufnr , - 1 , - 1 )
9292end
9393
94- --- @param opts ? { pos : HlChunk.Pos , use_treesitter : boolean }
94+ --- @param char string
95+ --- @param shiftwidth integer
96+ --- @return integer
97+ local function virt_text_char_width (char , shiftwidth )
98+ local b1 = char :byte (1 )
99+ if b1 == 0x00 then
100+ -- NULL is a terminator when used in virtual texts
101+ return 0
102+ elseif b1 == 0x09 then
103+ return shiftwidth
104+ elseif b1 <= 0x1F or b1 == 0x7F then
105+ -- control chars other than NULL and TAB are two cells wide
106+ return 2
107+ elseif b1 <= 0x7F then
108+ -- other ASCII chars are single cell wide
109+ return 1
110+ else
111+ return vim .api .nvim_strwidth (char )
112+ end
113+ end
114+
115+ --- faster alternative to `vim.fn.reverse()`
116+ --- unlike the original, this only supports lists
117+ --- @generic T
118+ --- @param list T[]
119+ --- @return T[]
120+ function chunkHelper .listReverse (list )
121+ local dst = {}
122+ for i , v in ipairs (list ) do
123+ dst [# list + 1 - i ] = v
124+ end
125+ return dst
126+ end
127+
128+ --- faster alternative to `vim.fn.repeat()`
129+ --- unlike the original, the input will be repeated as-is and the output will always be a list
130+ --- @generic T
131+ --- @param input T
132+ --- @param count integer
133+ --- @return T[]
134+ function chunkHelper .repeated (input , count )
135+ local dst = {}
136+ for i = 1 , count do
137+ dst [i ] = input
138+ end
139+ return dst
140+ end
141+
142+ --- faster alternative to `vim.list_extend()` (mutates dst!)
143+ --- unlike the original, this function lacks validation and range support
144+ --- @generic T
145+ --- @param dst T[]
146+ --- @param src T[]
147+ --- @return T[] dst
148+ function chunkHelper .list_extend (dst , src )
149+ for i = 1 , # src do
150+ dst [# dst + 1 ] = src [i ]
151+ end
152+ return dst
153+ end
154+
155+ --- @param opts ? { pos : Pos , use_treesitter : boolean }
95156--- @return CHUNK_RANGE_RETCODE enum
96157--- @return HlChunk.Scope
97158function chunkHelper .get_chunk_range (opts )
127188function chunkHelper .utf8Split (inputstr )
128189 local list = {}
129190 for uchar in string.gmatch (inputstr , " [^\128 -\191 ][\128 -\191 ]*" ) do
130- table.insert ( list , uchar )
191+ list [ # list + 1 ] = uchar
131192 end
132193 return list
133194end
@@ -139,7 +200,7 @@ function chunkHelper.rangeFromTo(i, j, step)
139200 local t = {}
140201 step = step or 1
141202 for x = i , j , step do
142- table.insert ( t , x )
203+ t [ # t + 1 ] = x
143204 end
144205 return t
145206end
@@ -152,8 +213,8 @@ function chunkHelper.getColList(char_list, leftcol, shiftwidth)
152213 local t = {}
153214 local next_col = leftcol
154215 for i = 1 , # char_list do
155- table.insert ( t , next_col )
156- next_col = next_col + chunkHelper . virtTextStrWidth (char_list [i ], shiftwidth )
216+ t [ # t + 1 ] = next_col
217+ next_col = next_col + virt_text_char_width (char_list [i ], shiftwidth )
157218 end
158219 return t
159220end
@@ -183,7 +244,7 @@ function chunkHelper.repeatToWidth(str, width, shiftwidth)
183244 local current_width = str_width * repeatable_len
184245 local i = 1
185246 while i <= # chars do
186- local char_width = chunkHelper . virtTextStrWidth (chars [i ], shiftwidth )
247+ local char_width = virt_text_char_width (chars [i ], shiftwidth )
187248 --- assumed to be an out-of-bounds char (like in nerd fonts) followed by a whitespace if true
188249 local likely_oob_char =
189250 -- single-cell
@@ -235,18 +296,13 @@ end
235296--- @return boolean
236297function chunkHelper .checkCellsBlank (line , start_col , end_col , shiftwidth )
237298 local current_col = 1
238- local current_byte = 1
239299 local current_char = 1
240- while current_byte <= # line and current_col <= end_col do
241- local final_byte = vim . str_byteindex ( line , current_char )
242- local char = line : sub ( current_byte , final_byte )
300+ local chars = chunkHelper . utf8Split ( line )
301+ while current_char <= # chars and current_col <= end_col do
302+ local char = chars [ current_char ]
243303 local b1 , b2 , b3 = char :byte (1 , 3 )
244- if char == " " then
245- break
246- end
247304 --- @type integer
248305 local next_col
249- local next_byte = final_byte + 1
250306 local next_char = current_char + 1
251307 if char == " " then
252308 next_col = current_col + 1
@@ -260,13 +316,11 @@ function chunkHelper.checkCellsBlank(line, start_col, end_col, shiftwidth)
260316 next_col = current_col + 1
261317 else
262318 local char_width = vim .api .nvim_strwidth (char )
263- local next_byte_peek = line :byte (final_byte + 1 )
264- if char_width == 1 and next_byte_peek == 0x20 then
319+ if char_width == 1 and chars [current_char + 1 ] == " " then
265320 -- the char is assumed to be an out-of-bounds char (like in nerd fonts)
266321 -- followed by a whitespace
267322 next_col = current_col + 2
268323 -- skip the whitespace part of out-of-bounds char + " "
269- next_byte = next_byte + 1
270324 next_char = next_char + 1
271325 else
272326 next_col = current_col + char_width
@@ -338,7 +392,6 @@ function chunkHelper.checkCellsBlank(line, start_col, end_col, shiftwidth)
338392 return false
339393 end
340394 current_col = next_col
341- current_byte = next_byte
342395 current_char = next_char
343396 end
344397 return true
@@ -351,25 +404,11 @@ end
351404function chunkHelper .virtTextStrWidth (str , shiftwidth , stop_on_null )
352405 local current_width = 0
353406 for _ , char in ipairs (chunkHelper .utf8Split (str )) do
354- if char == " \0 " then
355- if stop_on_null then
356- return current_width
357- end
358- -- just ignore otherwise
359- elseif char == " \t " then
360- current_width = current_width + shiftwidth
361- else
362- local b1 = char :byte (1 )
363- if b1 <= 0x1F or b1 == 0x7F then
364- -- control chars other than NULL and TAB are two cells wide
365- current_width = current_width + 2
366- elseif b1 <= 0x7F then
367- -- other ASCII chars are single cell wide
368- current_width = current_width + 1
369- else
370- current_width = current_width + vim .api .nvim_strwidth (char )
371- end
407+ if stop_on_null and char == " \0 " then
408+ -- NULL is a terminator when used in virtual texts
409+ return current_width
372410 end
411+ current_width = current_width + virt_text_char_width (char , shiftwidth )
373412 end
374413 return current_width
375414end
0 commit comments