Skip to content

Commit

Permalink
feat: preview can now be shown in a split/float
Browse files Browse the repository at this point in the history
  • Loading branch information
folke committed May 30, 2024
1 parent 6077342 commit e2919eb
Show file tree
Hide file tree
Showing 5 changed files with 182 additions and 108 deletions.
2 changes: 1 addition & 1 deletion lua/trouble/config/actions.lua
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ local M = {
end,
preview = function(self, ctx)
local Preview = require("trouble.view.preview")
if Preview.preview then
if Preview.is_open() then
Preview.close()
else
self:preview(ctx.item)
Expand Down
2 changes: 1 addition & 1 deletion lua/trouble/filter.lua
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ M.filters = {
buf = function(item, buf, view)
local main = view:main()
if buf == 0 then
return main and main.path == item.filename or false
return main and main.filename == item.filename or false
end
return item.buf == buf
end,
Expand Down
65 changes: 40 additions & 25 deletions lua/trouble/view/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -11,28 +11,20 @@ local Window = require("trouble.view.window")

---@class trouble.View
---@field win trouble.Window
---@field preview_win trouble.Window
---@field opts trouble.Mode
---@field sections trouble.Section[]
---@field items trouble.Item[][]
---@field nodes trouble.Node[]
---@field renderer trouble.Render
---@field private _main? {buf:number, win:number, path:string}
---@field private _main? trouble.Window.info
---@field fetching number
local M = {}
M.__index = M
local _idx = 0
---@type table<trouble.View, number>
M._views = setmetatable({}, { __mode = "k" })

-- local timer = assert(vim.loop.new_timer())
-- timer:start(
-- 3000,
-- 3000,
-- vim.schedule_wrap(function()
-- dd("views", vim.tbl_count(M._views))
-- end)
-- )

---@param opts trouble.Mode
function M.new(opts)
local self = setmetatable({}, M)
Expand Down Expand Up @@ -61,6 +53,17 @@ function M.new(opts)
end
self.win = Window.new(opts.win)
self.opts.win = self.win.opts
self.preview_win = Window.new({
type = "float",
-- position = "right",
relative = "editor",
border = "rounded",
title = "Preview",
title_pos = "center",
position = { 0, -2 },
size = { width = 0.3, height = 0.3 },
zindex = 200,
})

self.renderer = Render.new(self, {
padding = vim.tbl_get(self.opts.win, "padding", "left") or 0,
Expand Down Expand Up @@ -206,6 +209,7 @@ function M:jump(item, opts)
vim.api.nvim_win_set_buf(win, item.buf)
-- order of the below seems important with splitkeep=screen
vim.api.nvim_set_current_win(win)
-- FIXME:
vim.api.nvim_win_set_cursor(win, item.pos)
if vim.b[item.buf].trouble_preview then
vim.cmd.edit()
Expand All @@ -223,7 +227,6 @@ function M:preview(item)
return Preview.open(self, item)
end

---@return {buf:number, win:number, path:string, cursor:number[]}?
function M:main()
local valid = self._main
and self._main.win
Expand All @@ -233,14 +236,7 @@ function M:main()
if not valid then
self._main = self.win:find_main()
end
if self._main then
local cursor = vim.api.nvim_win_get_cursor(self._main.win)
-- When the preview is open, use the stored main window cursor
if Preview.preview and Preview.preview.win == self._main.win then
cursor = Preview.preview.cursor
end
return { buf = self._main.buf, path = self._main.path, win = self._main.win, cursor = cursor }
end
return self._main
end

function M:goto_main()
Expand All @@ -253,24 +249,40 @@ end
function M:listen()
local _self = Util.weak(self)
self:main()

-- track main window
self.win:on("BufEnter", function()
local this = _self()
if not this then
return true
end
-- FIXME:
assert(not Preview.is_open())
-- don't update the main window when
-- preview is open or when the window is pinned
if Preview.preview or this.opts.pinned then
if this.opts.pinned then
return
end
local buf = vim.api.nvim_get_current_buf()
local win = vim.api.nvim_get_current_win()
if vim.bo[buf].buftype == "" and vim.bo[buf].filetype ~= "" then
if vim.bo[buf].buftype == "" then
---@diagnostic disable-next-line: invisible
this._main = Window.win_info(win)
end
end, { buffer = false })

self.win:on("WinEnter", function()
local this = _self()
if not this then
return true
end
---@diagnostic disable-next-line: invisible
if this._main and vim.api.nvim_win_is_valid(this._main.win) then
---@diagnostic disable-next-line: invisible
this._main = Window.win_info(this._main.win)
end
end, { win = true })

for _, section in ipairs(self.sections) do
for _, event in ipairs(section.events or {}) do
vim.api.nvim_create_autocmd(event.event, {
Expand Down Expand Up @@ -513,19 +525,22 @@ function M:render()

-- Move cursor to the same item
local cursor = vim.api.nvim_win_get_cursor(self.win.win)
local item_row ---@type number?
for row, l in pairs(self.renderer._locations) do
if loc.node and loc.item then
if l.node and l.item and loc.node.id == l.node.id and l.item == loc.item then
cursor[1] = row
vim.api.nvim_win_set_cursor(self.win.win, cursor)
item_row = row
break
end
elseif loc.node and l.node and loc.node.id == l.node.id then
cursor[1] = row
vim.api.nvim_win_set_cursor(self.win.win, cursor)
item_row = row
break
end
end
if item_row and item_row ~= cursor[1] then
vim.api.nvim_win_set_cursor(self.win.win, { item_row, cursor[2] })
return
end
end

-- Tree.build = Util.track(Tree.build, "Tree.build")
Expand Down
161 changes: 99 additions & 62 deletions lua/trouble/view/preview.lua
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,24 @@ local Render = require("trouble.view.render")
local Util = require("trouble.util")

local M = {}
M.preview = nil ---@type {win:number,buf:number,view:table,cursor:number[], preview_buf:number}?
M.preview = nil ---@type {item:trouble.Item, win:number, buf: number, close:fun()}?

function M.is_open()
return M.preview ~= nil
end

function M.item()
return M.preview and M.preview.item
end

function M.close()
local preview = M.preview
M.preview = nil
if not (preview and vim.api.nvim_buf_is_valid(preview.buf)) then
if not preview then
return
end
Render.reset(preview.preview_buf)
if vim.api.nvim_win_is_valid(preview.win) then
Util.noautocmd(function()
vim.api.nvim_win_set_buf(preview.win, preview.buf)
vim.api.nvim_win_call(preview.win, function()
vim.fn.winrestview(preview.view)
end)
end)
end
Render.reset(preview.buf)
preview.close()
end

--- Create a preview buffer for an item.
Expand All @@ -28,80 +29,116 @@ end
function M.create(item)
local buf = item.buf or vim.fn.bufnr(item.filename)

if buf and vim.api.nvim_buf_is_loaded(buf) then
return buf
-- create a scratch preview buffer when needed
if not (buf and vim.api.nvim_buf_is_loaded(buf)) then
buf = vim.api.nvim_create_buf(false, true)
vim.bo[buf].bufhidden = "wipe"
vim.bo[buf].buftype = "nofile"
local lines = Util.get_lines({ path = item.filename, buf = item.buf })
vim.api.nvim_buf_set_lines(buf, 0, -1, false, lines)
local ft = item:get_ft()
if ft then
local lang = vim.treesitter.language.get_lang(ft)
if not pcall(vim.treesitter.start, buf, lang) then
vim.bo[buf].syntax = ft
end
end
end

buf = vim.api.nvim_create_buf(false, true)
vim.bo[buf].bufhidden = "wipe"
local lines = Util.get_lines({ path = item.filename, buf = item.buf })
vim.api.nvim_buf_set_lines(buf, 0, -1, false, lines)
local ft = item:get_ft()
if ft then
local lang = vim.treesitter.language.get_lang(ft)
if not pcall(vim.treesitter.start, buf, lang) then
vim.bo[buf].syntax = ft
end
-- make sure we highlight at least one character
local end_pos = { item.end_pos[1], item.end_pos[2] }
if end_pos[1] == item.pos[1] and end_pos[2] == item.pos[2] then
end_pos[2] = end_pos[2] + 1
end

-- highlight the line
vim.api.nvim_buf_set_extmark(buf, Render.ns, item.pos[1] - 1, 0, {
end_row = end_pos[1],
hl_group = "CursorLine",
hl_eol = true,
strict = false,
})

-- highlight the range
vim.api.nvim_buf_set_extmark(buf, Render.ns, item.pos[1] - 1, item.pos[2], {
end_row = end_pos[1] - 1,
end_col = end_pos[2],
hl_group = "TroublePreview",
strict = false,
})

return buf
end

---@param view trouble.View
---@param item trouble.Item
function M.open(view, item)
if M.item() == item then
return
end
if M.preview then
M.close()
end

local buf = M.create(item)

M.preview = M.preview_win(buf, view)
M.preview.buf = buf
M.preview.item = item

-- no autocmds should be triggered. So LSP's etc won't try to attach in the preview
Util.noautocmd(function()
vim.api.nvim_win_set_cursor(M.preview.win, item.pos)
end)

return item
end

---@param buf number
---@param view trouble.View
function M.preview_win(buf, view)
view.preview_win.opts.win = view.win.win
view.preview_win:open()
Util.noautocmd(function()
view.preview_win:set_buf(buf)
view.preview_win:set_options("win")
end)

return {
win = view.preview_win.win,
close = function()
view.preview_win:close()
end,
}
end

---@param buf number
---@param view trouble.View
function M.preview_main(buf, view)
local main = view:main()
if not main then
Util.debug("No main window")
return
end

if not vim.api.nvim_buf_is_valid(item.buf) then
Util.debug("Item has invalid buffer", item)
return
end

local buf = M.create(item)

M.preview = {
win = main.win,
buf = main.buf,
view = vim.api.nvim_win_call(main.win, vim.fn.winsaveview),
cursor = vim.api.nvim_win_get_cursor(main.win),
preview_buf = buf,
}

-- no autocmds should be triggered. So LSP's etc won't try to attach in the preview
local main_view = vim.api.nvim_win_call(main.win, vim.fn.winsaveview)
Util.noautocmd(function()
vim.api.nvim_win_set_buf(main.win, buf)
vim.api.nvim_win_set_cursor(main.win, item.pos)
end)

local end_pos = item.end_pos or item.pos
end_pos[1] = end_pos[1] or item.pos[1]
end_pos[2] = end_pos[2] or item.pos[2]
if end_pos[1] == item.pos[1] and end_pos[2] == item.pos[2] then
end_pos[2] = end_pos[2] + 1
end

vim.api.nvim_buf_set_extmark(buf, Render.ns, item.pos[1] - 1, 0, {
end_row = end_pos[1],
-- end_col = end_pos[2],
hl_group = "CursorLine",
hl_eol = true,
strict = false,
})
-- only highlight the range if it's on the same line
vim.api.nvim_buf_set_extmark(buf, Render.ns, item.pos[1] - 1, item.pos[2], {
end_row = end_pos[1] - 1,
end_col = end_pos[2],
hl_group = "TroublePreview",
strict = false,
})
return item
return {
win = main.win,
close = function()
if vim.api.nvim_win_is_valid(main.win) then
Util.noautocmd(function()
vim.api.nvim_win_set_buf(main.win, main.buf)
vim.api.nvim_win_call(main.win, function()
vim.fn.winrestview(main_view)
end)
end)
end
end,
}
end

return M
Loading

0 comments on commit e2919eb

Please sign in to comment.