From 4b32ada2d9c30dfa9915d1238c9b541ae7e28992 Mon Sep 17 00:00:00 2001 From: Barrett Ruth Date: Sat, 7 Mar 2026 16:49:08 -0500 Subject: [PATCH] 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 --- doc/canola.txt | 20 ++++++++++++++++++++ doc/upstream.md | 6 ++++-- lua/canola/config.lua | 6 ++++++ lua/canola/init.lua | 18 +++++++++++++++++- 4 files changed, 47 insertions(+), 3 deletions(-) diff --git a/doc/canola.txt b/doc/canola.txt index 0c3f7a2..bd302f0 100644 --- a/doc/canola.txt +++ b/doc/canola.txt @@ -152,6 +152,9 @@ The full list of options with their defaults: view_options = { -- Show files and directories that start with "." show_hidden = false, + -- When true and a directory has no visible entries, show hidden entries + -- instead of an empty listing. is_always_hidden entries are never shown. + show_hidden_when_empty = false, -- This function defines what is considered a "hidden" file is_hidden_file = function(name, bufnr) local m = name:match("^%.") @@ -227,6 +230,10 @@ The full list of options with their defaults: disable_preview = function(filename) return false end, + -- Maximum file size in MB to load into the preview window. Files larger + -- than this limit will show a placeholder message instead of being loaded. + -- Set to nil to disable the limit. + max_file_size = 10, -- Window-local options to use for preview window buffers win_options = {}, }, @@ -328,6 +335,19 @@ cleanup_buffers_on_delete *canola.cleanup_buffers_on_de was successfully deleted via canola. This prevents stale buffers from appearing in the jumplist after a deletion. +show_hidden_when_empty *canola.show_hidden_when_empty* + type: `boolean` default: `false` + When `true` and a directory contains no visible entries (because all + entries are hidden), canola will display the hidden entries anyway. + Entries matching `is_always_hidden` are never shown. Hidden entries + are still rendered with the dimmed hidden style. + +preview_win.max_file_size *canola.preview_win* + type: `number` default: `10` + Maximum file size in MB to load into the preview window. Files larger + than this limit will show a placeholder message instead of being loaded. + Set to `nil` to disable the limit. + -------------------------------------------------------------------------------- API *canola-api* diff --git a/doc/upstream.md b/doc/upstream.md index ac13a9a..72bc505 100644 --- a/doc/upstream.md +++ b/doc/upstream.md @@ -35,6 +35,8 @@ Bugs fixed in this fork that remain open upstream. | [#670](https://github.com/stevearc/oil.nvim/issues/670) | Multi-directory cmdline args ignored | [#11](https://github.com/barrettruth/canola.nvim/pull/11) ([`70861e5`](https://github.com/barrettruth/canola.nvim/commit/70861e5)) | | [#673](https://github.com/stevearc/oil.nvim/issues/673) | Symlink newlines crash | [`9110a1a`](https://github.com/barrettruth/canola.nvim/commit/9110a1a) | | [#710](https://github.com/stevearc/oil.nvim/issues/710) | buftype empty on BufEnter | [#10](https://github.com/barrettruth/canola.nvim/pull/10) ([`01b860e`](https://github.com/barrettruth/canola.nvim/commit/01b860e)) | +| [#213](https://github.com/stevearc/oil.nvim/issues/213) | Max file size for preview | pending PR | +| [#473](https://github.com/stevearc/oil.nvim/issues/473) | Show hidden when dir is all-hidden | pending PR | ## Open upstream PRs @@ -61,7 +63,7 @@ Bugs fixed in this fork that remain open upstream. | [#200](https://github.com/stevearc/oil.nvim/issues/200) | open | Highlights not working when opening a file (P2) | | [#207](https://github.com/stevearc/oil.nvim/issues/207) | open | Suppress "no longer available" message (P1) | | [#210](https://github.com/stevearc/oil.nvim/issues/210) | open | FTP support (P2) | -| [#213](https://github.com/stevearc/oil.nvim/issues/213) | open | Disable preview for large files (P1) | +| [#213](https://github.com/stevearc/oil.nvim/issues/213) | fixed | Disable preview for large files — `preview_win.max_file_size` (P1) | | [#226](https://github.com/stevearc/oil.nvim/issues/226) | open | K8s/Docker adapter (P2) | | [#232](https://github.com/stevearc/oil.nvim/issues/232) | open | Cannot close last window (P2) | | [#254](https://github.com/stevearc/oil.nvim/issues/254) | open | Buffer modified highlight group (P2) | @@ -104,7 +106,7 @@ Bugs fixed in this fork that remain open upstream. | [#450](https://github.com/stevearc/oil.nvim/issues/450) | open | Highlight opened file in directory listing | | [#457](https://github.com/stevearc/oil.nvim/issues/457) | open | Custom column API | | [#466](https://github.com/stevearc/oil.nvim/issues/466) | open | Select into window on right | -| [#473](https://github.com/stevearc/oil.nvim/issues/473) | open | Show all hidden files if dir only has hidden | +| [#473](https://github.com/stevearc/oil.nvim/issues/473) | fixed | Show all hidden files if dir only has hidden — `view_options.show_hidden_when_empty` | | [#479](https://github.com/stevearc/oil.nvim/issues/479) | open | Harpoon integration recipe | | [#483](https://github.com/stevearc/oil.nvim/issues/483) | not actionable | Spell downloads depend on netrw — fixed in [neovim#34940](https://github.com/neovim/neovim/pull/34940) | | [#486](https://github.com/stevearc/oil.nvim/issues/486) | open | All directory sizes show 4.1k | diff --git a/lua/canola/config.lua b/lua/canola/config.lua index 3b8e0c1..25e8876 100644 --- a/lua/canola/config.lua +++ b/lua/canola/config.lua @@ -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 ---@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 Window-local options to use for preview window buffers diff --git a/lua/canola/init.lua b/lua/canola/init.lua index e5e510b..18d9896 100644 --- a/lua/canola/init.lua +++ b/lua/canola/init.lua @@ -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'