mirror of
https://github.com/folke/snacks.nvim
synced 2025-08-04 02:38:46 +00:00
fix(picker): better main window management. Closes #842
This commit is contained in:
parent
597ba50ce9
commit
f0f053a1d9
4 changed files with 80 additions and 16 deletions
|
@ -123,10 +123,13 @@ M.edit_vsplit = { "vsplit", "confirm" }
|
|||
M.edit_tab = { "tab", "confirm" }
|
||||
|
||||
local function wincmd(picker, cmd)
|
||||
local win = vim.api.nvim_get_current_win()
|
||||
if vim.api.nvim_win_is_valid(picker.main) then
|
||||
vim.api.nvim_win_call(picker.main, function()
|
||||
vim.cmd(cmd)
|
||||
picker.main = vim.api.nvim_get_current_win()
|
||||
local ft = vim.bo[vim.api.nvim_get_current_buf()].filetype
|
||||
vim.api.nvim_set_current_win(win)
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
---@class snacks.picker.main
|
||||
---@class snacks.picker.Main
|
||||
---@field opts snacks.picker.main.Config
|
||||
---@field win number
|
||||
local M = {}
|
||||
M.__index = M
|
||||
|
||||
---@class snacks.picker.main.Config
|
||||
---@field float? boolean main window can be a floating window (defaults to false)
|
||||
|
@ -7,19 +10,40 @@ local M = {}
|
|||
---@field current? boolean main window should be the current window (defaults to false)
|
||||
|
||||
---@param opts? snacks.picker.main.Config
|
||||
function M.get(opts)
|
||||
function M.new(opts)
|
||||
opts = vim.tbl_extend("force", {
|
||||
float = false,
|
||||
file = true,
|
||||
current = false,
|
||||
}, opts or {})
|
||||
local self = setmetatable({}, M)
|
||||
self.opts = opts
|
||||
self.win = vim.api.nvim_get_current_win()
|
||||
self.win = self:find()
|
||||
return self
|
||||
end
|
||||
|
||||
function M:get()
|
||||
if not self.win or not vim.api.nvim_win_is_valid(self.win) then
|
||||
self.win = self:find()
|
||||
end
|
||||
return self.win
|
||||
end
|
||||
|
||||
---@param win number
|
||||
function M:set(win)
|
||||
self.win = win
|
||||
end
|
||||
|
||||
function M:find()
|
||||
local current = vim.api.nvim_get_current_win()
|
||||
if opts.current then
|
||||
if self.opts.current then
|
||||
return current
|
||||
end
|
||||
local prev = vim.fn.winnr("#")
|
||||
local wins = { current, prev }
|
||||
local all = vim.api.nvim_list_wins()
|
||||
local non_float = 0
|
||||
local wins = { self.win, current, prev }
|
||||
local all = vim.api.nvim_tabpage_list_wins(0)
|
||||
-- sort all by lastused of the win buffer
|
||||
table.sort(all, function(a, b)
|
||||
local ba = vim.api.nvim_win_get_buf(a)
|
||||
|
@ -27,27 +51,32 @@ function M.get(opts)
|
|||
return vim.fn.getbufinfo(ba)[1].lastused > vim.fn.getbufinfo(bb)[1].lastused
|
||||
end)
|
||||
vim.list_extend(wins, all)
|
||||
---@param win number
|
||||
wins = vim.tbl_filter(function(win)
|
||||
-- exclude invalid windows
|
||||
if win == 0 or not vim.api.nvim_win_is_valid(win) then
|
||||
return false
|
||||
end
|
||||
local win_config = vim.api.nvim_win_get_config(win)
|
||||
local is_float = win_config.relative ~= ""
|
||||
if not is_float then
|
||||
non_float = win
|
||||
end
|
||||
if vim.w[win].snacks_layout then
|
||||
return false
|
||||
end
|
||||
local buf = vim.api.nvim_win_get_buf(win)
|
||||
-- exclude non-file buffers
|
||||
if opts.file and vim.bo[vim.api.nvim_win_get_buf(win)].buftype ~= "" then
|
||||
if self.opts.file and vim.bo[buf].buftype ~= "" then
|
||||
return false
|
||||
end
|
||||
local win_config = vim.api.nvim_win_get_config(win)
|
||||
local is_float = win_config.relative ~= ""
|
||||
-- exclude floating windows and non-focusable windows
|
||||
if is_float and (not opts.float or not win_config.focusable) then
|
||||
if is_float and (not self.opts.float or not win_config.focusable) then
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end, wins)
|
||||
return wins[1] or current
|
||||
return wins[1] or non_float
|
||||
end
|
||||
|
||||
return M
|
||||
|
|
|
@ -18,6 +18,7 @@ local _id = 0
|
|||
---@field list snacks.picker.list
|
||||
---@field matcher snacks.picker.Matcher
|
||||
---@field main number
|
||||
---@field _main snacks.picker.Main
|
||||
---@field preview snacks.picker.Preview
|
||||
---@field shown? boolean
|
||||
---@field sort snacks.picker.sort
|
||||
|
@ -28,7 +29,6 @@ local _id = 0
|
|||
---@field history snacks.picker.History
|
||||
---@field visual? snacks.picker.Visual
|
||||
local M = {}
|
||||
M.__index = M
|
||||
|
||||
--- Keep track of garbage collection
|
||||
---@type table<snacks.Picker,boolean>
|
||||
|
@ -49,8 +49,26 @@ M.last = nil
|
|||
|
||||
---@alias snacks.picker.history.Record {pattern: string, search: string, live?: boolean}
|
||||
|
||||
function M:__index(key)
|
||||
if M[key] then
|
||||
return M[key]
|
||||
end
|
||||
if key == "main" then
|
||||
return self._main:get()
|
||||
end
|
||||
end
|
||||
|
||||
function M:__newindex(key, value)
|
||||
if key == "main" then
|
||||
self._main:set(value)
|
||||
else
|
||||
rawset(self, key, value)
|
||||
end
|
||||
end
|
||||
|
||||
---@hide
|
||||
---@param opts? snacks.picker.Config
|
||||
---@return snacks.Picker
|
||||
function M.new(opts)
|
||||
local self = setmetatable({}, M)
|
||||
_id = _id + 1
|
||||
|
@ -112,7 +130,7 @@ function M.new(opts)
|
|||
self.visual = Snacks.picker.util.visual()
|
||||
self.start_time = uv.hrtime()
|
||||
Snacks.picker.current = self
|
||||
self.main = require("snacks.picker.core.main").get(self.opts.main)
|
||||
self._main = require("snacks.picker.core.main").new(self.opts.main)
|
||||
local actions = require("snacks.picker.core.actions").get(self)
|
||||
self.opts.win.input.actions = actions
|
||||
self.opts.win.list.actions = actions
|
||||
|
@ -210,6 +228,11 @@ function M:is_focused()
|
|||
return vim.tbl_contains({ self.input.win.win, self.list.win.win, self.preview.win.win }, current)
|
||||
end
|
||||
|
||||
function M:on_current_tab()
|
||||
return self.layout:valid()
|
||||
and vim.api.nvim_get_current_tabpage() == vim.api.nvim_win_get_tabpage(self.layout.root.win)
|
||||
end
|
||||
|
||||
--- Execute the callback in normal mode.
|
||||
--- When still in insert mode, stop insert mode first,
|
||||
--- and then`vim.schedule` the callback.
|
||||
|
|
|
@ -152,8 +152,12 @@ function State.new(picker)
|
|||
if uv.fs_stat(buf_file) then
|
||||
self:open(buf_file)
|
||||
end
|
||||
picker.list.win:on({ "WinEnter", "BufEnter" }, function()
|
||||
self:follow()
|
||||
picker.list.win:on({ "WinEnter", "BufEnter" }, function(_, ev)
|
||||
vim.schedule(function()
|
||||
if ev.buf == vim.api.nvim_get_current_buf() then
|
||||
self:follow()
|
||||
end
|
||||
end)
|
||||
end)
|
||||
picker.list.win:on("TermClose", function()
|
||||
self:update()
|
||||
|
@ -185,7 +189,7 @@ function State:follow()
|
|||
return
|
||||
end
|
||||
local picker = self:picker()
|
||||
if not picker or picker:is_focused() then
|
||||
if not picker or picker:is_focused() or not picker:on_current_tab() then
|
||||
return
|
||||
end
|
||||
local win = vim.api.nvim_get_current_win()
|
||||
|
@ -194,6 +198,11 @@ function State:follow()
|
|||
end
|
||||
local buf = vim.api.nvim_get_current_buf()
|
||||
local file = vim.api.nvim_buf_get_name(buf)
|
||||
local item = picker:current()
|
||||
if item and item.file == norm(file) then
|
||||
return
|
||||
end
|
||||
dd("follow", file)
|
||||
self:show(file)
|
||||
end
|
||||
|
||||
|
@ -201,7 +210,7 @@ end
|
|||
---@param opts? {refresh?: boolean}
|
||||
function State:show(path, opts)
|
||||
opts = opts or {}
|
||||
path = vim.fs.normalize(path)
|
||||
path = norm(path)
|
||||
if not uv.fs_stat(path) then
|
||||
return
|
||||
end
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue