Skip to content

Commit edf3524

Browse files
committed
feat(ivy): reuse focused win for blines/git_blame preview
Can be used for a single picker with: ```lua :FzfLua blines profile=ivy -- combined with "hide" profile :FzfLua blines profile={"ivy","hide"} -- lua version: :lua FzfLua.git_blame({ profile={"ivy","hide"} }) ``` Closes #1754
1 parent c7f9ec3 commit edf3524

2 files changed

Lines changed: 112 additions & 8 deletions

File tree

lua/fzf-lua/profiles/ivy.lua

Lines changed: 111 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
local M = {
1+
local M = {
22
{ "default-title" }, -- base profile
33
desc = "UI at the bottom of the screen",
44
winopts = {
@@ -40,7 +40,7 @@ local M = {
4040
},
4141
}
4242

43-
local up = {
43+
local up = {
4444
row = 1,
4545
col = 0,
4646
width = 1,
@@ -52,10 +52,114 @@ local up = {
5252
},
5353
}
5454

55-
M.blines = { winopts = up, previewer = { toggle_behavior = "extend" } }
56-
M.lines = M.blines
57-
M.grep = M.blines
58-
M.grep_curbuf = M.blines
59-
M.git = { blame = { winopts = up } }
55+
---Extract lnum, col from blines/git_blame entries
56+
---@param sel string[]
57+
---@param opts table
58+
---@return integer?, integer?
59+
local parse_lnum_col = function(sel, opts)
60+
if not sel[1] then return end
61+
local lnum = sel[1]:match("^%w+ %(.-(%d+)%)") -- git_blame
62+
if tonumber(lnum) then return tonumber(lnum), 1 end
63+
local entry = FzfLua.path.entry_to_file(sel[1], opts)
64+
return entry.line, entry.col
65+
end
66+
67+
-- Credit to phanen@GitHub:
68+
-- https://github.com/ibhagwan/fzf-lua/issues/1754#issuecomment-2944053022
69+
local focused_win = {
70+
-- _treesitter = function(line) return "foo.lua", nil, line:sub(2) end,
71+
-- fzf_opts = { ["--nth"] = "1.." },
72+
fzf_args = "--pointer=",
73+
winopts = function()
74+
local off = vim.o.cmdheight + (vim.o.laststatus and 1 or 0)
75+
local height = math.ceil(vim.o.lines / 4)
76+
local ns = vim.api.nvim_create_namespace("fzf-lua.preview.swiper")
77+
local buf = vim.api.nvim_get_current_buf()
78+
local hl = function(start_row, start_col, end_row, end_col)
79+
assert(start_col >= 0 and end_col >= 0, "start_col and end_col must be non-negative")
80+
vim.hl.range(buf, ns, "IncSearch", { start_row, start_col }, { end_row, end_col }, {})
81+
end
82+
local on_buf_change = function()
83+
vim.api.nvim_buf_clear_namespace(buf, ns, 0, -1)
84+
local lines = vim.o.lines
85+
local l_s = lines - height - off + 1
86+
local l_e = lines - off - 1
87+
local max_columns = vim.o.columns
88+
for r = l_s, l_e do
89+
local state = {}
90+
for c = 1, max_columns do
91+
local ok, ret = pcall(vim.api.nvim__inspect_cell, 1, r, c)
92+
if not ok or not ret[1] then break end
93+
(function()
94+
if not state.lnum then -- parsing lnum
95+
local d = tonumber(ret[1])
96+
if not state.parsing_lnum and not d then return end
97+
if not state.parsing_lnum then
98+
state.parsing_lnum = d
99+
return
100+
end
101+
if d then
102+
state.parsing_lnum = state.parsing_lnum * 10 + d
103+
return
104+
end
105+
state.lnum, state.parsing_lnum = assert(state.parsing_lnum), nil
106+
return
107+
end
108+
local in_matched = ret[2] and ret[2].reverse
109+
if in_matched and not state.in_matched then
110+
state.start_col = math.max(c - 8, 0)
111+
state.text = { ret[1] }
112+
state.in_matched = in_matched
113+
return
114+
end
115+
if in_matched then
116+
state.text[#state.text + 1] = ret[1]
117+
return
118+
end
119+
if state.in_matched then
120+
hl(state.lnum - 1, state.start_col, state.lnum - 1, c - 8)
121+
state.in_matched = nil
122+
end
123+
end)()
124+
end
125+
end
126+
end
127+
return {
128+
preview = { hidden = true },
129+
split = ("botright %snew +set\\ nobl"):format(height),
130+
on_create = function(e)
131+
vim.api.nvim_create_autocmd("TextChangedT", { buffer = e.bufnr, callback = on_buf_change })
132+
end,
133+
on_close = function()
134+
vim.api.nvim_buf_clear_namespace(buf, ns, 0, -1)
135+
vim.api.nvim_win_set_cursor(0, FzfLua.utils.__CTX().cursor)
136+
FzfLua.utils.zz()
137+
end,
138+
}
139+
end,
140+
actions = {
141+
enter = function(sel, opts)
142+
local lnum, col = parse_lnum_col(sel, opts)
143+
pcall(vim.api.nvim_win_set_cursor, 0, { lnum, col })
144+
end,
145+
focus = {
146+
fn = function(sel, opts)
147+
local lnum, col = parse_lnum_col(sel, opts)
148+
if not lnum then return end
149+
local ctx = FzfLua.utils.CTX()
150+
vim.wo[ctx.winid].cursorline = true
151+
pcall(vim.api.nvim_win_set_cursor, ctx.winid, { lnum, col })
152+
end,
153+
field_index = "{}",
154+
exec_silent = true,
155+
},
156+
},
157+
}
158+
159+
M.blines = focused_win
160+
M.git = { blame = focused_win }
161+
M.lines = { winopts = up, previewer = { toggle_behavior = "extend" } }
162+
M.grep = { winopts = up, previewer = { toggle_behavior = "extend" } }
163+
M.grep_curbuf = { winopts = up, previewer = { toggle_behavior = "extend" } }
60164

61165
return M

lua/fzf-lua/providers/git.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ local function git_cmd(opts)
6262
end
6363

6464
local function git_preview(opts, file)
65-
if not type(opts.preview) == "string" then return end
65+
if type(opts.preview) ~= "string" then return end
6666
if file then
6767
opts.preview = opts.preview:gsub("[<{]file[}>]", file)
6868
end

0 commit comments

Comments
 (0)