From 4a8d57a269db08074050711dbb39e3921dfea153 Mon Sep 17 00:00:00 2001 From: Barrett Ruth <62671086+barrettruth@users.noreply.github.com> Date: Sat, 7 Mar 2026 16:52:57 -0500 Subject: [PATCH] feat: add max_file_size preview limit and show_hidden_when_empty (#85) * 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 * feat(view): add `show_hidden_when_empty` for hidden-only directories Problem: with `show_hidden = false`, a directory containing only dotfiles renders as just `..`, giving no indication that entries exist. Solution: add `view_options.show_hidden_when_empty` (boolean, default false). After the main filter loop in `render_buffer`, if the option is set and `#line_table <= 1`, iterate `entry_list` again and render any entry not matched by `is_always_hidden`, using `is_hidden = true` so they render with the dimmed hidden style. Based on: stevearc/oil.nvim#473 * docs(upstream): fix formatting * docs(upstream): update #213 and #473 with PR and commit links --- doc/canola.txt | 20 ++++++++++++++++++++ doc/upstream.md | 6 ++++-- lua/canola/config.lua | 6 ++++++ lua/canola/init.lua | 18 +++++++++++++++++- lua/canola/view.lua | 15 +++++++++++++++ 5 files changed, 62 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..1e4d541 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 | [#85](https://github.com/barrettruth/canola.nvim/pull/85) ([`4b32ada`](https://github.com/barrettruth/canola.nvim/commit/4b32ada)) | +| [#473](https://github.com/stevearc/oil.nvim/issues/473) | Show hidden when dir is all-hidden | [#85](https://github.com/barrettruth/canola.nvim/pull/85) ([`2fe4e78`](https://github.com/barrettruth/canola.nvim/commit/2fe4e78)) | ## 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' diff --git a/lua/canola/view.lua b/lua/canola/view.lua index 0f75faf..7a7ac89 100644 --- a/lua/canola/view.lua +++ b/lua/canola/view.lua @@ -807,6 +807,21 @@ local function render_buffer(bufnr, opts) end end + if config.view_options.show_hidden_when_empty and #line_table <= 1 then + for _, entry in ipairs(entry_list) do + local name = entry[FIELD_NAME] + local public_entry = util.export_entry(entry) + if not config.view_options.is_always_hidden(name, bufnr, public_entry) then + local cols = M.format_entry_cols(entry, column_defs, col_width, adapter, true, bufnr) + table.insert(line_table, cols) + if seek_after_render == name then + seek_after_render_found = true + jump_idx = #line_table + end + end + end + end + local lines, highlights = util.render_table(line_table, col_width, col_align) vim.bo[bufnr].modifiable = true