refactor(#2826): singleton View class, WIP

This commit is contained in:
Alexander Courtis 2025-04-20 14:28:44 +10:00
parent 0a04e435b1
commit f098195667
9 changed files with 70 additions and 71 deletions

View File

@ -158,7 +158,7 @@ local function setup_autocommands(opts)
return return
end end
if opts.actions.open_file.eject then if opts.actions.open_file.eject then
view._prevent_buffer_override() view.View:prevent_buffer_override()
else else
view.View:abandon_current_window() view.View:abandon_current_window()
end end

View File

@ -21,7 +21,7 @@ end
local function usable_win_ids() local function usable_win_ids()
local tabpage = vim.api.nvim_get_current_tabpage() local tabpage = vim.api.nvim_get_current_tabpage()
local win_ids = vim.api.nvim_tabpage_list_wins(tabpage) local win_ids = vim.api.nvim_tabpage_list_wins(tabpage)
local tree_winid = view.get_winnr(tabpage) local tree_winid = view.View:get_winnr(tabpage)
return vim.tbl_filter(function(id) return vim.tbl_filter(function(id)
local bufid = vim.api.nvim_win_get_buf(id) local bufid = vim.api.nvim_win_get_buf(id)

View File

@ -21,7 +21,7 @@ function M.fn(opts)
return return
end end
if not view.is_width_determined() then if not view.View:is_width_determined() then
-- {absolute} and {relative} do nothing when {width} is a function. -- {absolute} and {relative} do nothing when {width} is a function.
return return
end end

View File

@ -182,7 +182,7 @@ function M.update_coc()
end end
log.profile_end(profile) log.profile_end(profile)
local bufnr = view.get_bufnr() local bufnr = view.View:get_bufnr()
local should_draw = bufnr and vim.api.nvim_buf_is_valid(bufnr) and vim.api.nvim_buf_is_loaded(bufnr) local should_draw = bufnr and vim.api.nvim_buf_is_valid(bufnr) and vim.api.nvim_buf_is_loaded(bufnr)
if should_draw then if should_draw then
local explorer = core.get_explorer() local explorer = core.get_explorer()

View File

@ -84,7 +84,7 @@ function Explorer:create_autocmds()
group = self.augroup_id, group = self.augroup_id,
callback = function() callback = function()
appearance.setup() appearance.setup()
view.reset_winhl() view.View:reset_winhl()
self.renderer:draw() self.renderer:draw()
end, end,
}) })
@ -508,7 +508,7 @@ end
---nil on no explorer or invalid view win ---nil on no explorer or invalid view win
---@return integer[]|nil ---@return integer[]|nil
function Explorer:get_cursor_position() function Explorer:get_cursor_position()
local winnr = view.get_winnr() local winnr = view.View:get_winnr()
if not winnr or not vim.api.nvim_win_is_valid(winnr) then if not winnr or not vim.api.nvim_win_is_valid(winnr) then
return return
end end

View File

@ -156,7 +156,7 @@ end
---@return integer ---@return integer
local function calculate_overlay_win_width(self) local function calculate_overlay_win_width(self)
local wininfo = vim.fn.getwininfo(view.get_winnr())[1] local wininfo = vim.fn.getwininfo(view.View:get_winnr())[1]
if wininfo then if wininfo then
return wininfo.width - wininfo.textoff - #self.prefix return wininfo.width - wininfo.textoff - #self.prefix

View File

@ -13,7 +13,7 @@ local M = {
function M.set_target_win() function M.set_target_win()
local id = vim.api.nvim_get_current_win() local id = vim.api.nvim_get_current_win()
local tree_id = view.get_winnr() local tree_id = view.View:get_winnr()
if tree_id and id == tree_id then if tree_id and id == tree_id then
M.target_winid = 0 M.target_winid = 0
return return

View File

@ -96,28 +96,28 @@ function Renderer:render_hl(bufnr, hl_range_args)
end end
function Renderer:draw() function Renderer:draw()
local bufnr = view.get_bufnr() local bufnr = view.View:get_bufnr()
if not bufnr or not vim.api.nvim_buf_is_loaded(bufnr) then if not bufnr or not vim.api.nvim_buf_is_loaded(bufnr) then
return return
end end
local profile = log.profile_start("draw") local profile = log.profile_start("draw")
local cursor = vim.api.nvim_win_get_cursor(view.get_winnr() or 0) local cursor = vim.api.nvim_win_get_cursor(view.View:get_winnr() or 0)
local builder = Builder(self.explorer):build() local builder = Builder(self.explorer):build()
self:_draw(bufnr, builder.lines, builder.hl_range_args, builder.signs, builder.extmarks, builder.virtual_lines) self:_draw(bufnr, builder.lines, builder.hl_range_args, builder.signs, builder.extmarks, builder.virtual_lines)
if cursor and #builder.lines >= cursor[1] then if cursor and #builder.lines >= cursor[1] then
vim.api.nvim_win_set_cursor(view.get_winnr() or 0, cursor) vim.api.nvim_win_set_cursor(view.View:get_winnr() or 0, cursor)
end end
view.View:grow_from_content() view.View:grow_from_content()
log.profile_end(profile) log.profile_end(profile)
events._dispatch_on_tree_rendered(bufnr, view.get_winnr()) events._dispatch_on_tree_rendered(bufnr, view.View:get_winnr())
end end
return Renderer return Renderer

View File

@ -22,27 +22,26 @@ local DEFAULT_PADDING = 1
---@field live_filter table ---@field live_filter table
---@field side string ---@field side string
---@field float table ---@field float table
---TODO #2826 private below here ---@field private explorer Explorer
---@field explorer Explorer ---@field private adaptive_size boolean
---@field adaptive_size boolean ---@field private centralize_selection boolean
---@field centralize_selection boolean ---@field private tabpages table
---@field tabpages table ---@field private cursors table
---@field cursors table ---@field private hide_root_folder boolean
---@field hide_root_folder boolean ---@field private winopts table
---@field winopts table ---@field private height integer
---@field height integer ---@field private tab table
---@field tab table ---@field private preserve_window_proportions boolean
---@field preserve_window_proportions boolean ---@field private initial_width integer
---@field initial_width integer ---@field private width (fun():integer)|integer|string
---@field width (fun():integer)|integer|string ---@field private max_width integer
---@field max_width integer ---@field private padding integer
---@field padding integer ---@field private bufnr_per_tab table<integer, integer>
local View = Class:extend() local View = Class:extend()
---@class View ---@class View
---@overload fun(args: ViewArgs): View ---@overload fun(args: ViewArgs): View
--TODO #2826 exact
---@class (exact) ViewArgs ---@class (exact) ViewArgs
---@field explorer Explorer ---@field explorer Explorer
@ -55,6 +54,7 @@ function View:new(args)
self.tabpages = {} self.tabpages = {}
self.cursors = {} self.cursors = {}
self.hide_root_folder = false self.hide_root_folder = false
self.bufnr_per_tab = {}
self.live_filter = { self.live_filter = {
prev_focused_node = nil, prev_focused_node = nil,
} }
@ -115,9 +115,6 @@ local tabinitial = {
winnr = nil, winnr = nil,
} }
-- TODO #2826 member
local BUFNR_PER_TAB = {}
---@type { name: string, value: any }[] ---@type { name: string, value: any }[]
local BUFFER_OPTIONS = { local BUFFER_OPTIONS = {
{ name = "bufhidden", value = "wipe" }, { name = "bufhidden", value = "wipe" },
@ -128,10 +125,11 @@ local BUFFER_OPTIONS = {
{ name = "swapfile", value = false }, { name = "swapfile", value = false },
} }
---@private
---@param bufnr integer ---@param bufnr integer
---@return boolean ---@return boolean
local function matches_bufnr(bufnr) function View:matches_bufnr(bufnr)
for _, b in pairs(BUFNR_PER_TAB) do for _, b in pairs(self.bufnr_per_tab) do
if b == bufnr then if b == bufnr then
return true return true
end end
@ -139,31 +137,32 @@ local function matches_bufnr(bufnr)
return false return false
end end
local function wipe_rogue_buffer() ---@private
function View:wipe_rogue_buffer()
for _, bufnr in ipairs(vim.api.nvim_list_bufs()) do for _, bufnr in ipairs(vim.api.nvim_list_bufs()) do
if not matches_bufnr(bufnr) and utils.is_nvim_tree_buf(bufnr) then if not self:matches_bufnr(bufnr) and utils.is_nvim_tree_buf(bufnr) then
pcall(vim.api.nvim_buf_delete, bufnr, { force = true }) pcall(vim.api.nvim_buf_delete, bufnr, { force = true })
end end
end end
end end
---@private ---@private
---@param bufnr integer|boolean|nil ---@param bufnr integer|false|nil
function View:create_buffer(bufnr) function View:create_buffer(bufnr)
wipe_rogue_buffer() self:wipe_rogue_buffer()
local tab = vim.api.nvim_get_current_tabpage() local tab = vim.api.nvim_get_current_tabpage()
BUFNR_PER_TAB[tab] = bufnr or vim.api.nvim_create_buf(false, false) self.bufnr_per_tab[tab] = bufnr or vim.api.nvim_create_buf(false, false)
vim.api.nvim_buf_set_name(M.get_bufnr(), "NvimTree_" .. tab) vim.api.nvim_buf_set_name(self:get_bufnr(), "NvimTree_" .. tab)
bufnr = M.get_bufnr() bufnr = self:get_bufnr()
for _, option in ipairs(BUFFER_OPTIONS) do for _, option in ipairs(BUFFER_OPTIONS) do
vim.api.nvim_set_option_value(option.name, option.value, { buf = bufnr }) vim.api.nvim_set_option_value(option.name, option.value, { buf = bufnr })
end end
require("nvim-tree.keymap").on_attach(M.get_bufnr()) require("nvim-tree.keymap").on_attach(self:get_bufnr())
events._dispatch_tree_attached_post(M.get_bufnr()) events._dispatch_tree_attached_post(self:get_bufnr())
end end
---@private ---@private
@ -205,7 +204,7 @@ end
---@private ---@private
function View:set_window_options_and_buffer() function View:set_window_options_and_buffer()
pcall(vim.api.nvim_command, "buffer " .. M.get_bufnr()) pcall(vim.api.nvim_command, "buffer " .. self:get_bufnr())
if vim.fn.has("nvim-0.10") == 1 then if vim.fn.has("nvim-0.10") == 1 then
local eventignore = vim.api.nvim_get_option_value("eventignore", {}) local eventignore = vim.api.nvim_get_option_value("eventignore", {})
@ -288,7 +287,7 @@ end
---@param tabnr integer ---@param tabnr integer
function View:save_tab_state(tabnr) function View:save_tab_state(tabnr)
local tabpage = tabnr or vim.api.nvim_get_current_tabpage() local tabpage = tabnr or vim.api.nvim_get_current_tabpage()
self.cursors[tabpage] = vim.api.nvim_win_get_cursor(M.get_winnr(tabpage) or 0) self.cursors[tabpage] = vim.api.nvim_win_get_cursor(self:get_winnr(tabpage) or 0)
end end
---@private ---@private
@ -299,7 +298,7 @@ function View:close_internal(tabpage)
end end
self:save_tab_state(tabpage) self:save_tab_state(tabpage)
switch_buf_if_last_buf() switch_buf_if_last_buf()
local tree_win = M.get_winnr(tabpage) local tree_win = self:get_winnr(tabpage)
local current_win = vim.api.nvim_get_current_win() local current_win = vim.api.nvim_get_current_win()
for _, win in pairs(vim.api.nvim_tabpage_list_wins(tabpage)) do for _, win in pairs(vim.api.nvim_tabpage_list_wins(tabpage)) do
if vim.api.nvim_win_get_config(win).relative == "" then if vim.api.nvim_win_get_config(win).relative == "" then
@ -364,12 +363,12 @@ end
---@private ---@private
function View:grow() function View:grow()
local starts_at = self:is_root_folder_visible(require("nvim-tree.core").get_cwd()) and 1 or 0 local starts_at = self:is_root_folder_visible(require("nvim-tree.core").get_cwd()) and 1 or 0
local lines = vim.api.nvim_buf_get_lines(M.get_bufnr(), starts_at, -1, false) local lines = vim.api.nvim_buf_get_lines(self:get_bufnr(), starts_at, -1, false)
-- number of columns of right-padding to indicate end of path -- number of columns of right-padding to indicate end of path
local padding = self:get_size(self.padding) local padding = self:get_size(self.padding)
-- account for sign/number columns etc. -- account for sign/number columns etc.
local wininfo = vim.fn.getwininfo(M.get_winnr()) local wininfo = vim.fn.getwininfo(self:get_winnr())
if type(wininfo) == "table" and type(wininfo[1]) == "table" then if type(wininfo) == "table" and type(wininfo[1]) == "table" then
padding = padding + wininfo[1].textoff padding = padding + wininfo[1].textoff
end end
@ -388,7 +387,7 @@ function View:grow()
for line_nr, l in pairs(lines) do for line_nr, l in pairs(lines) do
local count = vim.fn.strchars(l) local count = vim.fn.strchars(l)
-- also add space for right-aligned icons -- also add space for right-aligned icons
local extmarks = vim.api.nvim_buf_get_extmarks(M.get_bufnr(), ns_id, { line_nr, 0 }, { line_nr, -1 }, { details = true }) local extmarks = vim.api.nvim_buf_get_extmarks(self:get_bufnr(), ns_id, { line_nr, 0 }, { line_nr, -1 }, { details = true })
for _, extmark in ipairs(extmarks) do for _, extmark in ipairs(extmarks) do
local virt_texts = extmark[4].virt_text local virt_texts = extmark[4].virt_text
if virt_texts then if virt_texts then
@ -445,7 +444,7 @@ function View:resize(size)
return return
end end
local winnr = M.get_winnr() or 0 local winnr = self:get_winnr() or 0
local new_size = self:get_width() local new_size = self:get_width()
@ -492,7 +491,7 @@ end
function View:abandon_current_window() function View:abandon_current_window()
local tab = vim.api.nvim_get_current_tabpage() local tab = vim.api.nvim_get_current_tabpage()
BUFNR_PER_TAB[tab] = nil self.bufnr_per_tab[tab] = nil
if self.tabpages[tab] then if self.tabpages[tab] then
self.tabpages[tab].winnr = nil self.tabpages[tab].winnr = nil
end end
@ -500,7 +499,7 @@ end
function View:abandon_all_windows() function View:abandon_all_windows()
for tab, _ in pairs(vim.api.nvim_list_tabpages()) do for tab, _ in pairs(vim.api.nvim_list_tabpages()) do
BUFNR_PER_TAB[tab] = nil self.bufnr_per_tab[tab] = nil
if self.tabpages[tab] then if self.tabpages[tab] then
self.tabpages[tab].winnr = nil self.tabpages[tab].winnr = nil
end end
@ -527,25 +526,25 @@ function View:is_visible(opts)
return false return false
end end
return M.get_winnr() ~= nil and vim.api.nvim_win_is_valid(M.get_winnr() or 0) return self:get_winnr() ~= nil and vim.api.nvim_win_is_valid(self:get_winnr() or 0)
end end
---@param opts table|nil ---@param opts table|nil
function View:set_cursor(opts) function View:set_cursor(opts)
if self:is_visible() then if self:is_visible() then
pcall(vim.api.nvim_win_set_cursor, M.get_winnr(), opts) pcall(vim.api.nvim_win_set_cursor, self:get_winnr(), opts)
end end
end end
---@param winnr number|nil ---@param winnr number|nil
---@param open_if_closed boolean|nil ---@param open_if_closed boolean|nil
function View:focus(winnr, open_if_closed) function View:focus(winnr, open_if_closed)
local wnr = winnr or M.get_winnr() local wnr = winnr or self:get_winnr()
if vim.api.nvim_win_get_tabpage(wnr or 0) ~= vim.api.nvim_win_get_tabpage(0) then if vim.api.nvim_win_get_tabpage(wnr or 0) ~= vim.api.nvim_win_get_tabpage(0) then
self:close() self:close()
self:open() self:open()
wnr = M.get_winnr() wnr = self:get_winnr()
elseif open_if_closed and not self:is_visible() then elseif open_if_closed and not self:is_visible() then
self:open() self:open()
end end
@ -564,7 +563,7 @@ function View:winid(opts)
tabpage = vim.api.nvim_get_current_tabpage() tabpage = vim.api.nvim_get_current_tabpage()
end end
if self:is_visible({ tabpage = tabpage }) then if self:is_visible({ tabpage = tabpage }) then
return M.get_winnr(tabpage) return self:get_winnr(tabpage)
else else
return nil return nil
end end
@ -579,9 +578,9 @@ end
--- Returns the window number for nvim-tree within the tabpage specified --- Returns the window number for nvim-tree within the tabpage specified
---@param tabpage number|nil (optional) the number of the chosen tabpage. Defaults to current tabpage. ---@param tabpage number|nil (optional) the number of the chosen tabpage. Defaults to current tabpage.
---@return number|nil ---@return number|nil
function M.get_winnr(tabpage) function View:get_winnr(tabpage)
tabpage = tabpage or vim.api.nvim_get_current_tabpage() tabpage = tabpage or vim.api.nvim_get_current_tabpage()
local tabinfo = M.View.tabpages[tabpage] local tabinfo = self.tabpages[tabpage]
if tabinfo and tabinfo.winnr and vim.api.nvim_win_is_valid(tabinfo.winnr) then if tabinfo and tabinfo.winnr and vim.api.nvim_win_is_valid(tabinfo.winnr) then
return tabinfo.winnr return tabinfo.winnr
end end
@ -589,13 +588,13 @@ end
--- Returns the current nvim tree bufnr --- Returns the current nvim tree bufnr
---@return number ---@return number
function M.get_bufnr() function View:get_bufnr()
return BUFNR_PER_TAB[vim.api.nvim_get_current_tabpage()] return self.bufnr_per_tab[vim.api.nvim_get_current_tabpage()]
end end
function M._prevent_buffer_override() function View:prevent_buffer_override()
local view_winnr = M.get_winnr() local view_winnr = self:get_winnr()
local view_bufnr = M.get_bufnr() local view_bufnr = self:get_bufnr()
-- need to schedule to let the new buffer populate the window -- need to schedule to let the new buffer populate the window
-- because this event needs to be run on bufWipeout. -- because this event needs to be run on bufWipeout.
@ -607,9 +606,9 @@ function M._prevent_buffer_override()
local bufname = vim.api.nvim_buf_get_name(curbuf) local bufname = vim.api.nvim_buf_get_name(curbuf)
if not bufname:match("NvimTree") then if not bufname:match("NvimTree") then
for i, tabpage in ipairs(M.View.tabpages) do for i, tabpage in ipairs(self.tabpages) do
if tabpage.winnr == view_winnr then if tabpage.winnr == view_winnr then
M.View.tabpages[i] = nil self.tabpages[i] = nil
break break
end end
end end
@ -622,7 +621,7 @@ function M._prevent_buffer_override()
-- might need a better patch -- might need a better patch
vim.cmd("setlocal nowinfixwidth") vim.cmd("setlocal nowinfixwidth")
vim.cmd("setlocal nowinfixheight") vim.cmd("setlocal nowinfixheight")
M.View:open({ focus_tree = false }) self:open({ focus_tree = false })
local explorer = require("nvim-tree.core").get_explorer() local explorer = require("nvim-tree.core").get_explorer()
if explorer then if explorer then
@ -648,17 +647,17 @@ function View:is_root_folder_visible(cwd)
end end
-- used on ColorScheme event -- used on ColorScheme event
function M.reset_winhl() function View:reset_winhl()
local winnr = M.get_winnr() local winnr = self:get_winnr()
if winnr and vim.api.nvim_win_is_valid(winnr) then if winnr and vim.api.nvim_win_is_valid(winnr) then
vim.wo[M.get_winnr()].winhl = M.View.winopts.winhl vim.wo[self:get_winnr()].winhl = self.winopts.winhl
end end
end end
---Check if width determined or calculated on-fly ---Check if width determined or calculated on-fly
---@return boolean ---@return boolean
function M.is_width_determined() function View:is_width_determined()
return type(M.View.width) ~= "function" return type(self.width) ~= "function"
end end
---Configure width-related config ---Configure width-related config