Skip to content

Commit 2889cbd

Browse files
committed
Add workaround for inconsistent statusline eval padding
It seems that neovim applies inconsistent padding when evaluating the statusline: - When padding a normal component, e.g. "%5l", then the global fillchars option is used - When padding a grouped component with minwid, e.g. "%2(a%)", then the fillchars parameter for nvim_eval_statusline() is used It is this inconsistent padding that causes our statusline split heuristic to mistakenly think that a grouped component is the actual split of the statusline (like "%="), while it is actually not. This caused some statuslines (in particular heirline, luckily not many other popular statuslines use grouped components like this) to have very weird and early right alignment. This is obviously not the fault of those statuslines, but it turns out that this bug is not very easy to fix in upstream neovim. Ideally we would have API in nvim_eval_statusline() that would tell us the split point directly, then we could avoid these nasty heuristics altogether. So instead I came up with a workaround in the meantime. Keep in mind, this workaround is absolutely horrid, but at least it works. Hopefully we can resolve this problem properly, but for now I have added a unit test to make sure that we don't regress this further. Also fuck whoever masochist came up with 1-based indexes in Lua. It's causing off-by-one errors literally everywhere, and unfortunately that's not even the worst part about this horrible language. Fixes #58
1 parent eb38875 commit 2889cbd

File tree

2 files changed

+43
-12
lines changed

2 files changed

+43
-12
lines changed

lua/tpipeline/main.lua

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,19 @@ function M.color(grp)
3535
return string.format('#[fg=%s,bg=%s%s]', fg, bg, st)
3636
end
3737

38+
function M.eval_stl(stl, width)
39+
local evl = vim.api.nvim_eval_statusline(stl, {fillchar = vim.g.tpipeline_fillchar, highlights = 1, use_tabline = vim.g.tpipeline_tabline, maxwidth = width})
40+
local res = evl.str
41+
local i = 0
42+
for k, hl in pairs(evl.highlights) do
43+
local grp = M.color(hl.group)
44+
-- unfortunately we need to use vimscript strpart() as Lua's UTF 8 support is absolutely horrendous
45+
res = vim.fn.strpart(res, 0, hl.start + i) .. grp .. vim.fn.strpart(res, hl.start + i)
46+
i = i + string.len(grp)
47+
end
48+
return res
49+
end
50+
3851
function M.update()
3952
was_bold = false
4053
was_italic = false
@@ -43,16 +56,26 @@ function M.update()
4356
if stl == '' then
4457
stl = vim.o.stl
4558
end
46-
local evl = vim.api.nvim_eval_statusline(stl, {fillchar = vim.g.tpipeline_fillchar, highlights = 1, use_tabline = vim.g.tpipeline_tabline, maxwidth = vim.g.tpipeline_size})
47-
local res = evl.str
48-
local i = 0
49-
for k, hl in pairs(evl.highlights) do
50-
local grp = M.color(hl.group)
51-
-- unfortunately we need to use vimscript strpart() as Lua's UTF 8 support is absolutely horrendous
52-
res = vim.fn.strpart(res, 0, hl.start + i) .. grp .. vim.fn.strpart(res, hl.start + i)
53-
i = i + string.len(grp)
59+
60+
local res = M.eval_stl(stl, vim.g.tpipeline_size)
61+
62+
if #vim.split(res, split_pattern) <= 2 then
63+
res = res:gsub(split_pattern, '%%=')
64+
else
65+
-- sometimes the split point is not unique, in which case we have to find it out manually
66+
local retry = M.eval_stl(stl, vim.g.tpipeline_size + 2)
67+
local i = 1
68+
local start = 1
69+
while i <= res:len() and res:sub(i, i) == retry:sub(i, i) do
70+
if res:sub(i, i) ~= vim.g.tpipeline_fillchar then
71+
start = i
72+
end
73+
i = i + 1
74+
end
75+
res = string.gsub(res:sub(1, start), vim.g.tpipeline_fillchar, ' ') .. string.gsub(res:sub(start + 1, i - 1), split_pattern, '%%=') .. string.gsub(res:sub(i), vim.g.tpipeline_fillchar, ' ')
5476
end
55-
return res:gsub(split_pattern, '%%=')
77+
78+
return res
5679
end
5780

5881
return M

tests/test_general.vim

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@ func Test_loaded()
3030
call assert_equal(1, g:loaded_tpipeline)
3131
endfunc
3232

33+
func Test_can_debug()
34+
let info = tpipeline#debug#info()
35+
call assert_true(len(info))
36+
endfunc
37+
3338
func Test_job_runs()
3439
let job = tpipeline#debug#info()
3540
" background job is still running
@@ -208,7 +213,10 @@ func Test_quoted_strings()
208213
call assert_equal(string(g:ReturnNumber()), Strip_hl(s:left))
209214
endfunc
210215

211-
func Test_can_debug()
212-
let info = tpipeline#debug#info()
213-
call assert_true(len(info))
216+
func Test_minwid_padded()
217+
" padding stl groups with minwid should not confuse the statusline splitter to cause right alignment where there is none
218+
let g:tpipeline_statusline = '%2(a%)%='
219+
call Read_socket()
220+
call assert_match("a$", s:left)
221+
call assert_true(empty(s:right))
214222
endfunc

0 commit comments

Comments
 (0)