feat(preview): add max_file_size config to skip large file previews

Problem: previewing large files (e.g. 500 MB logs, binaries) loads them
into a buffer and can freeze or OOM Neovim. `disable_preview` only
receives the filename, so users cannot gate on file size.

Solution: add `preview_win.max_file_size` (number, MB, default 10). In
`open_preview`, check `entry.meta.stat.size` and fall back to
`vim.uv.fs_stat` when the cached stat is absent. If the file exceeds
the limit and a preview window is already open, render "File too large
to preview" in it; if not, emit a WARN notify and return early. The
cursor-moved auto-update path only fires when a window already exists,
so no flag threading is needed to distinguish explicit from implicit.

Based on: stevearc/oil.nvim#213
This commit is contained in:
Barrett Ruth 2026-03-07 16:49:08 -05:00
parent a9a06b8f3b
commit 4b32ada2d9
Signed by: barrett
GPG key ID: A6C96C9349D2FC81
4 changed files with 47 additions and 3 deletions

View file

@ -84,6 +84,7 @@ local default_config = {
view_options = {
-- Show files and directories that start with "."
show_hidden = false,
show_hidden_when_empty = false,
-- This function defines what is considered a "hidden" file
is_hidden_file = function(name, bufnr)
local m = name:match('^%.')
@ -159,6 +160,7 @@ local default_config = {
disable_preview = function(filename)
return false
end,
max_file_size = 10,
-- Window-local options to use for preview window buffers
win_options = {},
},
@ -306,6 +308,7 @@ local M = {}
---@class (exact) canola.ViewOptions
---@field show_hidden boolean
---@field show_hidden_when_empty boolean
---@field is_hidden_file fun(name: string, bufnr: integer, entry: canola.Entry): boolean
---@field is_always_hidden fun(name: string, bufnr: integer, entry: canola.Entry): boolean
---@field natural_order boolean|"fast"
@ -315,6 +318,7 @@ local M = {}
---@class (exact) canola.SetupViewOptions
---@field show_hidden? boolean Show files and directories that start with "."
---@field show_hidden_when_empty? boolean When true and the directory has no visible entries, show hidden entries instead of an empty listing (:help canola.show_hidden_when_empty).
---@field is_hidden_file? fun(name: string, bufnr: integer): boolean This function defines what is considered a "hidden" file
---@field is_always_hidden? fun(name: string, bufnr: integer): boolean This function defines what will never be shown, even when `show_hidden` is set
---@field natural_order? boolean|"fast" Sort file names with numbers in a more intuitive order for humans. Can be slow for large directories.
@ -371,6 +375,7 @@ local M = {}
---@field update_on_cursor_moved boolean
---@field preview_method canola.PreviewMethod
---@field disable_preview fun(filename: string): boolean
---@field max_file_size number Maximum file size (in MB) to preview. Files larger than this will show a placeholder.
---@field win_options table<string, any>
---@class (exact) canola.ConfirmationWindowConfig : canola.WindowConfig
@ -378,6 +383,7 @@ local M = {}
---@class (exact) canola.SetupPreviewWindowConfig
---@field update_on_cursor_moved? boolean Whether the preview window is automatically updated when the cursor is moved
---@field disable_preview? fun(filename: string): boolean A function that returns true to disable preview on a file e.g. to avoid lag
---@field max_file_size? number Maximum file size in MB to show in preview. Files exceeding this will not be loaded (:help canola.preview_win). Set to nil to disable the limit.
---@field preview_method? canola.PreviewMethod How to open the preview window
---@field win_options? table<string, any> Window-local options to use for preview window buffers

View file

@ -642,7 +642,23 @@ M.open_preview = function(opts, callback)
local entry_is_file = not vim.endswith(normalized_url, '/')
local filebufnr
if entry_is_file then
if config.preview_win.disable_preview(normalized_url) then
local max_mb = config.preview_win.max_file_size
local _size = entry.meta and entry.meta.stat and entry.meta.stat.size
if not _size then
local _st = vim.uv.fs_stat(normalized_url)
_size = _st and _st.size
end
if max_mb and _size and _size > max_mb * 1024 * 1024 then
if preview_win then
filebufnr = vim.api.nvim_create_buf(false, true)
vim.bo[filebufnr].bufhidden = 'wipe'
vim.bo[filebufnr].buftype = 'nofile'
util.render_text(filebufnr, 'File too large to preview', { winid = preview_win })
else
vim.notify('File too large to preview', vim.log.levels.WARN)
return finish()
end
elseif config.preview_win.disable_preview(normalized_url) then
filebufnr = vim.api.nvim_create_buf(false, true)
vim.bo[filebufnr].bufhidden = 'wipe'
vim.bo[filebufnr].buftype = 'nofile'