diff options
| author | Florian Fischer <florian.fischer@muhq.space> | 2025-06-08 11:09:30 -0500 |
|---|---|---|
| committer | Florian Fischer <florian.fischer@muhq.space> | 2025-06-08 15:35:07 -0500 |
| commit | 59ce465f6d51f1d3b8ae18a3346264006e99aab5 (patch) | |
| tree | ebabd52d1adece59d9521b040ca844f13b0190d9 | |
| parent | cef8b428cff4cfb3fa07f30eab644cba1540b4b2 (diff) | |
| download | vis-lspc-59ce465f6d51f1d3b8ae18a3346264006e99aab5.tar.gz vis-lspc-59ce465f6d51f1d3b8ae18a3346264006e99aab5.zip | |
fix the line number detection with tabs
If a line includes tabs add the visually added space for each tab stop
to the line length in order to detect a wrapped line.
| -rw-r--r-- | init.lua | 11 | ||||
| -rw-r--r-- | util.lua | 25 | ||||
| -rwxr-xr-x | util_test.lua | 15 |
3 files changed, 48 insertions, 3 deletions
@@ -566,9 +566,14 @@ local function file_lineno_to_viewport_lineno(win, file_lineno) local view_lineno = 0 for n = win.viewport.lines.start, file_lineno do view_lineno = view_lineno + 1 - -- Wrapped line this shifts our displayed line down - if #win.file.lines[n] > win.viewport.width then - view_lineno = view_lineno + math.floor(#win.file.lines[n] / win.viewport.width) + -- Wrapped lines shift our displayed line down + local line_len = #win.file.lines[n] + if not win.options.expandtab then + line_len = util.visual_chars_in_line(win, win.file.lines[n], line_len) + end + + if line_len >= win.viewport.width then + view_lineno = view_lineno + math.floor(line_len / win.viewport.width) end end return view_lineno @@ -279,4 +279,29 @@ function util.vis_pos_to_sel(win, pos) return sel end +--- Count the visual characters in a line +-- This is useful to detect wrapped lines including tabs which may add a +-- unspecified amount of white space to a line depending on their position and +-- the used tabwidth. +-- @param win the window containing the line +-- @param line the line to count +-- @param nchars the number of characters in the line +-- @return the number of visual characters +util.visual_chars_in_line = function(win, line, nchars) + -- fast string iteration inspired by: + -- https://stackoverflow.com/a/49222705 + local l = {string.byte(line, 1, nchars)} + local line_len = 0 + for i = 1, nchars do + local c = l[i] -- Note: produces char codes instead of chars. + if c == 9 then -- '\t' + local chars_to_tab_stop = win.options.tabwidth - (line_len % win.options.tabwidth) + line_len = line_len + chars_to_tab_stop + else + line_len = line_len + 1 + end + end + return line_len +end + return util diff --git a/util_test.lua b/util_test.lua index fd6570d..a0f4218 100755 --- a/util_test.lua +++ b/util_test.lua @@ -15,4 +15,19 @@ function test_dirname() -- luacheck: ignore 111 lunatest.assert_equal('/', util.dirname('/')) end +function test_visual_chars_in_line() -- luacheck: ignore 111 + local win = {options = {tabwidth = 4}} -- win mock + local s = '\tfo' -- visual chars == 6 + lunatest.assert_equal(util.visual_chars_in_line(win, s, #s), 6) + + s = 'f\tfo' -- visual chars == 6 + lunatest.assert_equal(util.visual_chars_in_line(win, s, #s), 6) + + s = 'fo\tfo' -- visual chars == 6 + lunatest.assert_equal(util.visual_chars_in_line(win, s, #s), 6) + + s = 'foo\tfo' -- visual chars == 6 + lunatest.assert_equal(util.visual_chars_in_line(win, s, #s), 6) +end + lunatest.run() |
