Skip to content

Commit

Permalink
feat(path): support floating and embedded preview for path source
Browse files Browse the repository at this point in the history
  • Loading branch information
willothy committed Jan 24, 2024
1 parent 0c3b4f6 commit 5c8cbe9
Show file tree
Hide file tree
Showing 4 changed files with 199 additions and 8 deletions.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1409,6 +1409,10 @@ each sources.
})
end
```
- `opts.sources.path.preview`: `"floating"` | `"previous"`
- Default: `"previous"`
- `"previous"` will preview the buffer in the menu's previous split window, like TS/LSP symbols.
- `"floating"` will preview the buffer in a popup winow adjacent to the menu.

##### Treesitter

Expand Down Expand Up @@ -1942,12 +1946,14 @@ Declared and defined in [`lua/dropbar/menu.lua`](https://github.com/Bekaboo/drop
| ------ | ------ | ------ |
| `buf` | `integer` | buffer number of the menu |
| `win` | `integer` | window id of the menu |
| `preview_win` | `integer` | window id being used for preview, if any |
| `is_opened` | `boolean?` | whether the menu is currently opened |
| `entries` | [`dropbar_menu_entry_t[]`](#dropbar_menu_entry_t) | entries in the menu |
| `win_configs` | `table` | window configuration, value can be a function, see [menu configuration options](#menu) |
| `_win_configs` | `table?` | evaluated window configuration |
| `cursor` | `integer[]?` | initial cursor position |
| `prev_win` | `integer?` | previous window, assigned when calling new() or automatically determined in open() |
| `prev_buf` | `integer?` | previous buffer, assigned when calling new() or automatically determined in open() |
| `sub_menu` | `dropbar_menu_t?` | submenu, assigned when calling new() or automatically determined when a new menu opens |
| `prev_menu` | `dropbar_menu_t?` | previous menu, assigned when calling new() or automatically determined in open() |
| `clicked_at` | `integer[]?` | last position where the menu was clicked, 1,0-indexed |
Expand Down
3 changes: 3 additions & 0 deletions lua/dropbar/configs.lua
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,9 @@ M.opts = {
modified = function(sym)
return sym
end,
---Preview files in the prevous window or a floating window.
---@type "previous"|"floating"
preview = 'previous',
},
treesitter = {
-- Lua pattern used to extract a short name from the node text
Expand Down
16 changes: 16 additions & 0 deletions lua/dropbar/menu.lua
Original file line number Diff line number Diff line change
Expand Up @@ -177,12 +177,14 @@ end
---@class dropbar_menu_t
---@field buf integer?
---@field win integer?
---@field preview_win integer? The open preview window, if any
---@field is_opened boolean?
---@field entries dropbar_menu_entry_t[]
---@field win_configs table window configuration, value can be a function
---@field _win_configs table evaluated window configuration
---@field cursor integer[]? initial cursor position
---@field prev_win integer? previous window, assigned when calling new() or automatically determined in open()
---@field prev_buf integer? previous buffer, assigned when calling new() or automatically determined in open()
---@field sub_menu dropbar_menu_t? submenu, assigned when calling new() or automatically determined when a new menu opens
---@field prev_menu dropbar_menu_t? previous menu, assigned when calling new() or automatically determined in open()
---@field clicked_at integer[]? last position where the menu was clicked, byte-indexed, 1,0-indexed
Expand Down Expand Up @@ -311,6 +313,20 @@ function dropbar_menu_t:click_at(pos, min_width, n_clicks, button, modifiers)
end
end

---Retrieves the root window of the menu.
---If `self.prev_menu` is nil then this `self.prev_win`.
---Otherwise, it is the root window of `self.prev_menu`.
---@return integer?
function dropbar_menu_t:root_win()
local current = self
local win = self.prev_win
while current and current.prev_menu do
win = current.prev_menu.prev_win
current = current.prev_menu
end
return win
end

---"Click" the component in the dropbar menu
---Side effects: update self.clicked_at
---@param symbol dropbar_symbol_t
Expand Down
182 changes: 174 additions & 8 deletions lua/dropbar/sources/path.lua
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,155 @@ local function get_icon_and_hl(path)
return icon, icon_hl, name_hl
end

---@param self dropbar_symbol_t
local function preview_prepare_buf(self, path)
local buf
if vim.uv.fs_stat(path).type == 'directory' then
-- TODO: preview directory entries
self:preview_restore_view()
return
end
buf = vim.fn.bufnr(path, false)
if buf == nil or buf == -1 then
buf = vim.fn.bufadd(path)
if not buf then
self:preview_restore_view()
return
end
if not vim.api.nvim_buf_is_loaded(buf) then
vim.fn.bufload(buf)
end
end
if buf == nil or self.entry.menu == nil or self.entry.menu.win == nil then
self:preview_restore_view()
return
end
return buf
end

---@param self dropbar_symbol_t
local function preview_open_float(self, path, icon, icon_hl)
local preview_buf = preview_prepare_buf(self, path)
if not preview_buf then
return
end

local function make_title()
local pat = vim.fs.normalize(
configs.eval(configs.opts.sources.path.relative_to, preview_buf)
)
return {
{ icon, icon_hl },
{
vim.api
.nvim_buf_get_name(preview_buf)
:gsub('' .. pat .. '/', '')
:gsub('^' .. pat, ''), -- ':~'
'NormalFloat',
},
}
end
if
self.entry.menu.preview_win == nil
or vim.api.nvim_win_is_valid(self.entry.menu.preview_win) == false
then
self.entry.menu.preview_win = vim.api.nvim_open_win(preview_buf, false, {
-- relative = 'editor',
relative = 'win',
style = 'minimal',
-- focusable = false,
width = math.min(80, math.floor(vim.o.columns / 2)),
height = math.min(25, math.floor(vim.o.lines / 2)),
row = 0,
col = vim.api.nvim_win_get_width(self.entry.menu.win) + 1,
-- row = vim.api.nvim_win_get_position(self.entry.menu.win)[1],
-- col = vim.api.nvim_win_get_position(self.entry.menu.win)[2]
-- + vim.api.nvim_win_get_width(self.entry.menu.win)
-- + 1,
border = 'solid',
title = make_title(),
})
vim.api.nvim_create_autocmd('BufLeave', {
buffer = self.entry.menu.buf,
callback = function()
self:preview_restore_view()
end,
})
vim.schedule(function()
vim.api.nvim_exec_autocmds(
'CursorMoved',
{ buffer = self.entry.menu.buf }
)
end)
else
vim.api.nvim_win_set_buf(self.entry.menu.preview_win, preview_buf)
local config = vim.api.nvim_win_get_config(self.entry.menu.preview_win)
config.title = make_title()
vim.api.nvim_win_set_config(self.entry.menu.preview_win, config)
end
vim.api.nvim_win_set_cursor(self.entry.menu.preview_win, { 1, 0 })
vim.wo[self.entry.menu.preview_win].winbar = ''
vim.wo[self.entry.menu.preview_win].stc = ''
vim.wo[self.entry.menu.preview_win].signcolumn = 'no'
vim.wo[self.entry.menu.preview_win].number = false
vim.wo[self.entry.menu.preview_win].relativenumber = false
end

---@param self dropbar_symbol_t
local function preview_close_float(self)
if
self.entry.menu.preview_win
and vim.api.nvim_win_is_valid(self.entry.menu.preview_win)
then
vim.api.nvim_win_close(self.entry.menu.preview_win, true)
end
self.entry.menu.preview_win = nil
end

---@param self dropbar_symbol_t
local function preview_open_previous(self, path)
local preview_buf = preview_prepare_buf(self, path)
if not preview_buf then
return
end
local buflisted = vim.bo[preview_buf].buflisted

self.entry.menu.preview_win = self.entry.menu:root_win()
self.entry.menu.prev_buf = self.entry.menu.prev_buf
or vim.api.nvim_win_get_buf(self.entry.menu.preview_win)

vim.api.nvim_create_autocmd('BufLeave', {
buffer = self.entry.menu.buf,
callback = function()
self:preview_restore_view()
end,
})
vim.api.nvim_win_set_buf(self.entry.menu.preview_win, preview_buf)
local last_exit = vim.api.nvim_buf_get_mark(preview_buf, '"')
if last_exit[1] ~= 0 then
vim.api.nvim_win_set_cursor(self.entry.menu.preview_win, last_exit)
end

vim.bo[preview_buf].buflisted = buflisted
-- ensure dropbar still shows then the preview buffer is opened
vim.wo[self.entry.menu.preview_win].winbar =
'%{%v:lua.dropbar.get_dropbar_str()%}'
end

---@param self dropbar_symbol_t
local function preview_close_previous(self)
if self.win then
if self.entry.menu.prev_buf then
vim.api.nvim_win_set_buf(self.win, self.entry.menu.prev_buf)
end
if self.view then
vim.api.nvim_win_call(self.win, function()
vim.fn.winrestview(self.view)
end)
end
end
end

---Convert a path to a dropbar symbol
---@param path string full path
---@param buf integer buffer handler
Expand All @@ -42,6 +191,7 @@ end
local function convert(path, buf, win)
local path_opts = configs.opts.sources.path
local icon, icon_hl, name_hl = get_icon_and_hl(path)

return bar.dropbar_symbol_t:new(setmetatable({
buf = buf,
win = win,
Expand All @@ -53,6 +203,20 @@ local function convert(path, buf, win)
jump = function(_)
vim.cmd.edit(path)
end,
preview = vim.schedule_wrap(function(self)
if path_opts.preview == 'previous' then
preview_open_previous(self, path)
else
preview_open_float(self, path, icon, icon_hl)
end
end),
preview_restore_view = function(self)
if path_opts.preview == 'previous' then
preview_close_previous(self)
else
preview_close_float(self)
end
end,
}, {
---@param self dropbar_symbol_t
__index = function(self, k)
Expand All @@ -69,14 +233,16 @@ local function convert(path, buf, win)
local parent_dir = vim.fs.dirname(path)
self.siblings = {}
self.sibling_idx = 1
for idx, name in vim.iter(vim.fs.dir(parent_dir)):enumerate() do
if path_opts.filter(name) then
table.insert(
self.siblings,
convert(parent_dir .. '/' .. name, buf, win)
)
if name == self.name then
self.sibling_idx = idx
if parent_dir then
for idx, name in vim.iter(vim.fs.dir(parent_dir)):enumerate() do
if path_opts.filter(name) then
table.insert(
self.siblings,
convert(parent_dir .. '/' .. name, buf, win)
)
if name == self.name then
self.sibling_idx = idx
end
end
end
end
Expand Down

0 comments on commit 5c8cbe9

Please sign in to comment.