Skip to content

Commit

Permalink
draft(files): compute prefix only for visible part of preview
Browse files Browse the repository at this point in the history
Main motivation for doing this is to enable smoother vertical navigation
with enabled 'mini.icons'.

Without this, `MiniIcons.get()` is called *synchronously* for *all*
entries in previewed directory. When done first time (before caching)
this manifests into a visible delay in case of many (like 1000+) files.
Especially if they all don't have detectable filetypes and contain many
dots.

The only downside of this optimization is that with enabled preview, all
lines are computed twice: first in preview (if particular directory is
even previewed), second - when focused.
  • Loading branch information
echasnovski committed Aug 20, 2024
1 parent 08b4769 commit 22bc4d3
Showing 1 changed file with 46 additions and 28 deletions.
74 changes: 46 additions & 28 deletions lua/mini/files.lua
Original file line number Diff line number Diff line change
Expand Up @@ -989,10 +989,7 @@ MiniFiles.get_fs_entry = function(buf_id, line)
line = H.validate_line(buf_id, line)

local path_id = H.match_line_path_id(H.get_bufline(buf_id, line))
if path_id == nil then return nil end

local path = H.path_index[path_id]
return { fs_type = H.fs_get_type(path), name = H.fs_get_basename(path), path = path }
return H.get_fs_entry_from_path_index(path_id)
end

--- Get target window
Expand Down Expand Up @@ -1262,6 +1259,7 @@ end
---@field depth_focus number Depth to focus.
---@field views table Views for paths. Each view is a table with:
--- - <buf_id> where to show directory content.
--- - <was_focused> - whether buffer was focused.
--- - <cursor> to position cursor; can be:
--- - `{ line, col }` table to set cursor when buffer changes window.
--- - `entry_name` string entry name to find inside directory buffer.
Expand Down Expand Up @@ -1319,12 +1317,14 @@ H.explorer_refresh = function(explorer, opts)
if not opts.skip_update_cursor then explorer = H.explorer_update_cursors(explorer) end

-- Possibly force content updates on all explorer buffers. Doing it for *all*
-- of them and not only on modified once to allow synch outside changes.
-- of them and not only on modified ones to allow sync changes from outside.
if opts.force_update then
for path, view in pairs(explorer.views) do
-- Encode cursors to allow them to "stick" to current entry
view = H.view_encode_cursor(view)
H.buffer_update(view.buf_id, path, explorer.opts)
-- Force update of shown path ids
if H.opened_buffers[view.buf_id] then H.opened_buffers[view.buf_id].children_path_ids = nil end
H.buffer_update(view.buf_id, path, explorer.opts, not view.was_focused)
explorer.views[path] = view
end
end
Expand Down Expand Up @@ -1565,17 +1565,17 @@ H.explorer_refresh_depth_window = function(explorer, depth, win_count, win_col)
local path = explorer.branch[depth]
local views, windows, opts = explorer.views, explorer.windows, explorer.opts

-- Prepare target view
local view = views[path] or {}
view = H.view_ensure_proper(view, path, opts)
views[path] = view

-- Compute width based on window role
local win_is_focused = depth == explorer.depth_focus
local win_is_preview = opts.windows.preview and (depth == (explorer.depth_focus + 1))
local cur_width = win_is_focused and opts.windows.width_focus
or (win_is_preview and opts.windows.width_preview or opts.windows.width_nofocus)

-- Prepare target view
local view = views[path] or {}
view = H.view_ensure_proper(view, path, opts, win_is_focused, win_is_preview)
views[path] = view

-- Create relevant window config
local config = {
col = win_col,
Expand Down Expand Up @@ -1792,17 +1792,21 @@ H.compute_visible_depth_range = function(explorer, opts)
end

-- Views ----------------------------------------------------------------------
H.view_ensure_proper = function(view, path, opts)
H.view_ensure_proper = function(view, path, opts, is_focused, is_preview)
-- Ensure proper buffer
if not H.is_valid_buf(view.buf_id) then
local needs_recreate, needs_reprocess = not H.is_valid_buf(view.buf_id), not view.was_focused and is_focused
if needs_recreate then
H.buffer_delete(view.buf_id)
view.buf_id = H.buffer_create(path, opts.mappings)
end
if needs_recreate or needs_reprocess then
-- Make sure that pressing `u` in new buffer does nothing
local cache_undolevels = vim.bo[view.buf_id].undolevels
vim.bo[view.buf_id].undolevels = -1
H.buffer_update(view.buf_id, path, opts)
H.buffer_update(view.buf_id, path, opts, is_preview)
vim.bo[view.buf_id].undolevels = cache_undolevels
end
view.was_focused = view.was_focused or is_focused

-- Ensure proper cursor. If string, find it as line in current buffer.
view.cursor = view.cursor or { 1, 0 }
Expand Down Expand Up @@ -2001,12 +2005,12 @@ H.buffer_make_mappings = function(buf_id, mappings)
--stylua: ignore end
end

H.buffer_update = function(buf_id, path, opts)
H.buffer_update = function(buf_id, path, opts, is_preview)
if not (H.is_valid_buf(buf_id) and H.fs_is_present_path(path)) then return end

-- Perform entry type specific updates
local update_fun = H.fs_get_type(path) == 'directory' and H.buffer_update_directory or H.buffer_update_file
update_fun(buf_id, path, opts)
update_fun(buf_id, path, opts, is_preview)

-- Trigger dedicated event
H.trigger_event('MiniFilesBufferUpdate', { buf_id = buf_id, win_id = H.opened_buffers[buf_id].win_id })
Expand All @@ -2015,21 +2019,29 @@ H.buffer_update = function(buf_id, path, opts)
H.opened_buffers[buf_id].n_modified = -1
end

H.buffer_update_directory = function(buf_id, path, opts)
-- Compute and cache (to use during synchronization) shown file system entries
local fs_entries = H.fs_read_dir(path, opts.content)
H.opened_buffers[buf_id].children_path_ids = vim.tbl_map(function(x) return x.path_id end, fs_entries)
H.buffer_update_directory = function(buf_id, path, opts, is_preview)
-- Compute and cache (to use in synchronization) shown file system entries
local children_path_ids = H.opened_buffers[buf_id].children_path_ids
local fs_entries = children_path_ids == nil and H.fs_read_dir(path, opts.content)
or vim.tbl_map(H.get_fs_entry_from_path_index, children_path_ids)
H.opened_buffers[buf_id].children_path_ids = children_path_ids
or vim.tbl_map(function(x) return x.path_id end, fs_entries)

-- Compute lines
local lines, icon_hl, name_hl = {}, {}, {}

-- - Compute format expression resulting into same width path ids
-- Compute format expression resulting into same width path ids
local path_width = math.floor(math.log10(#H.path_index)) + 1
local line_format = '/%0' .. path_width .. 'd/%s/%s'

local prefix_fun = opts.content.prefix
for _, entry in ipairs(fs_entries) do
local prefix, hl = prefix_fun(entry)
-- Compute lines
local lines, icon_hl, name_hl = {}, {}, {}
local prefix_fun, n_computed_prefixes = opts.content.prefix, is_preview and vim.o.lines or math.huge
for i, entry in ipairs(fs_entries) do
local prefix, hl
-- Compute prefix only in visible preview (for performance).
-- NOTE: limiting entries in `fs_read_dir()` is not possible because all
-- entries are needed for a proper filter and sort.
if i <= n_computed_prefixes then
prefix, hl = prefix_fun(entry)
end
prefix, hl = prefix or '', hl or ''
table.insert(lines, string.format(line_format, H.path_index[entry.path], prefix, entry.name))
table.insert(icon_hl, hl)
Expand Down Expand Up @@ -2057,7 +2069,7 @@ H.buffer_update_directory = function(buf_id, path, opts)
end
end

H.buffer_update_file = function(buf_id, path, opts)
H.buffer_update_file = function(buf_id, path, opts, _)
-- Work only with readable text file. This is not 100% proof, but good enough.
-- Source: https://github.com/sharkdp/content_inspector
local fd, width_preview = vim.loop.fs_open(path, 'r', 1), opts.windows.width_preview
Expand Down Expand Up @@ -2362,6 +2374,12 @@ H.add_path_to_index = function(path)
return new_id
end

H.get_fs_entry_from_path_index = function(path_id)
local path = H.path_index[path_id]
if path == nil then return nil end
return { fs_type = H.fs_get_type(path), name = H.fs_get_basename(path), path = path }
end

H.replace_path_in_index = function(from, to)
local from_id, to_id = H.path_index[from], H.path_index[to]
H.path_index[from_id], H.path_index[to] = to, from_id
Expand Down

0 comments on commit 22bc4d3

Please sign in to comment.