Problem: the float title was only shown via the native `nvim_win_set_config`
path, which requires a border to render. The guard `config.float.border ~=
'none'` did not account for `nil`, which is the default — so users with no
explicit `border` config never saw the path title in the floating window.
Solution: require both `~= nil` and `~= 'none'` before using the native
title. In all other cases (border nil, 'none', or nvim < 0.9), fall back to
`util.add_title_to_win`, which renders a child floating window for the title.
Based on: stevearc/oil.nvim#683
* feat: emit \`CanolaFileCreated\` autocmd on file creation
Problem: no way to hook into individual file creation to populate
initial contents, without a plugin-specific config callback.
Solution: fire \`User CanolaFileCreated\` with \`data.path\` after each
successful \`fs.touch\` in the files adapter. Users listen with
\`nvim_create_autocmd\` and write to the path however they like.
* build: gitignore `doc/upstream.html`
* docs(upstream): mark #721 fixed, triage #735
* docs(upstream): simplify #735 note
Problem: When files are deleted via canola, any open Neovim buffers
for those files remain alive, polluting the jumplist with stale
entries.
Solution: Add an opt-in `cleanup_buffers_on_delete` config option
(default `false`). When enabled, `finish()` in `mutator/init.lua`
iterates completed delete actions and wipes matching buffers via
`nvim_buf_delete` before `CanolaActionsPost` fires. Only local
filesystem deletes are handled (guarded by the `files` adapter
check).
Problem: pressing `o`/`O` in a canola buffer placed the cursor at
column 0, requiring manual navigation past concealed ID prefixes and
column text (icons, permissions) to reach the name column.
Solution: add `show_insert_guide()` which temporarily sets
`virtualedit=all` on empty lines and positions the cursor at the
name column. Computes the correct virtual column by measuring the
visible column prefix width via `nvim_strwidth`, adjusting for
`conceallevel` (0=full ID width, 1=replacement char, 2/3=hidden).
Restores `virtualedit` on `TextChangedI` or `InsertLeave`.
* fix: restore `buflisted` on jumplist buffer re-entry
Problem: Neovim's jumplist machinery re-enters canola buffers via an
internal `:edit`-equivalent path, which unconditionally sets
`buflisted = true`. The existing workaround in `open()` and
`open_float()` only covers canola-initiated navigation, leaving
`<C-o>` and `<C-i>` unhandled.
Solution: Apply the same `buf_options.buflisted` guard in the
`BufEnter` autocmd, directly after `set_win_options()`. This fires
on every buffer entry — including all jumplist paths — and mirrors
the pattern already used at the two `:edit` callsites.
* docs: mark upstream #302 as fixed in tracker
Problem: the codebase still used the upstream \`oil\` naming everywhere —
URL schemes, the \`:Oil\` command, highlight groups, user events, module
paths, filetypes, buffer/window variables, LuaCATS type annotations,
vimdoc help tags, syntax groups, and internal identifiers.
Solution: mechanical rename of every reference. URL schemes now use
\`canola://\` (plus \`canola-ssh://\`, \`canola-s3://\`, \`canola-sss://\`,
\`canola-trash://\`, \`canola-test://\`). The \`:Canola\` command replaces
\`:Oil\`. All highlight groups, user events, augroups, namespaces,
filetypes, require paths, type annotations, help tags, and identifiers
follow suit. The \`upstream\` remote to \`stevearc/oil.nvim\` has been
removed and the \`vim.g.oil\` deprecation shim dropped.
Problem: when the nonicons direct API was detected, all icons were
returned with the generic 'OilFileIcon' highlight group, losing
per-filetype colors from nvim-web-devicons.
Solution: resolve highlight groups from devicons when available so
nonicons glyphs retain their per-filetype colors.
Problem: nonicons.nvim exposes a public get_icon/get_icon_by_filetype
API, but oil.nvim can only use nonicons glyphs indirectly through the
devicons monkey-patch. This couples oil to devicons even when nonicons
is available standalone.
Solution: add a nonicons provider in get_icon_provider() between
mini.icons and devicons. Feature-gated on nonicons.get_icon existing
so old nonicons versions fall through to devicons. Uses OilDirIcon
and OilFileIcon highlight groups.
* feat(icons): add nonicons.nvim icon provider support
Problem: oil.nvim only recognizes mini.icons and nvim-web-devicons as
icon providers. nonicons.nvim works when paired with devicons (via its
apply() monkey-patch), but has no standalone support.
Solution: add a nonicons.nvim fallback in get_icon_provider(), placed
after devicons so the patched devicons path is preferred when both are
installed. The standalone path handles directories via
nonicons.get('file-directory'), files via filetype/extension lookup with
a generic file icon fallback.
* fix(doc): improve readme phrasing
* build: replace luacheck with selene
Problem: luacheck is unmaintained (last release 2018) and required
suppressing four warning classes to avoid false positives. It also
lacks first-class vim/neovim awareness.
Solution: switch to selene with std='vim' for vim-aware linting.
Replace the luacheck CI job with selene, update the Makefile lint
target, and delete .luacheckrc.
* build: add nix devshell and pre-commit hooks
Problem: oil.nvim had no reproducible dev environment. The .envrc
set up a Python venv for the now-removed docgen pipeline, and there
were no pre-commit hooks for local formatting checks.
Solution: add flake.nix with stylua, selene, and prettier in the
devshell. Replace the stale Python .envrc with 'use flake'. Add
.pre-commit-config.yaml with stylua and prettier hooks matching
other plugins in the repo collection.
* fix: format with stylua
* build(selene): configure lints and add inline suppressions
Problem: selene fails on 5 errors and 3 warnings from upstream code
patterns that are intentional (mixed tables in config API, unused
callback parameters, identical if branches for readability).
Solution: globally allow mixed_table and unused_variable (high volume,
inherent to the codebase design). Add inline selene:allow directives
for the 8 remaining issues: if_same_then_else (4), mismatched_arg_count
(1), empty_if (2), global_usage (1). Remove .envrc from tracking.
* build: switch typecheck action to mrcjkb/lua-typecheck-action
Problem: oil.nvim used stevearc/nvim-typecheck-action, which required
cloning the action repo locally for the Makefile lint target. All
other plugins in the collection use mrcjkb/lua-typecheck-action.
Solution: swap to mrcjkb/lua-typecheck-action@v0 for consistency.
Remove the nvim-typecheck-action git clone from the Makefile and
.gitignore. Drop LuaLS from the local lint target since it requires
a full language server install — CI handles it.
* feat: support vim.g.oil declarative configuration
Problem: oil.nvim requires an imperative require("oil").setup(opts)
call to initialize. The Neovim ecosystem is moving toward vim.g.plugin
as a declarative config source that works without explicit setup calls.
Solution: fall back to vim.g.oil in config.setup() when no opts are
passed, and add plugin/oil.lua to auto-initialize when vim.g.oil is
set. Explicit setup(opts) calls still take precedence. Update docs
and add tests for the new resolution order.
Closes: barrettruth/oil.nvim#1
* build: remove release-please pipeline
Problem: the release-please action creates automated releases that
are not needed for this fork's workflow.
Solution: remove the release job from tests.yml and the
release-please branch exclusion from the review request workflow.
* fix(doc): improve readme phrasing
* doc: minor phrasing "improvements"
* ci: add luarocks release on tag push
Problem: there is no automated way to publish oil.nvim to luarocks
when a new version is tagged.
Solution: add a luarocks workflow that triggers on v* tag pushes,
runs the test suite via workflow_call, then publishes via
luarocks-tag-release. Add workflow_call trigger to tests.yml so it
can be reused.
Problem: when the preview window opens a directory that already has a
loaded oil buffer with unsaved edits, open_preview() unconditionally
calls load_oil_buffer() on it. This re-initializes the buffer via
view.initialize() -> render_buffer_async(), which re-fetches the
directory listing from disk and replaces all buffer lines, destroying
the user's pending edits. The mutation parser then can't see the
deleted entry in the source buffer, so it produces a COPY action
instead of a MOVE.
Solution: guard the load_oil_buffer() call in open_preview() with a
check for vim.b[filebufnr].oil_ready. Buffers that are already
initialized and rendered are not re-loaded, preserving any unsaved
modifications the user has made.
Closes: stevearc/oil.nvim#632
Problem: when neovim is opened with multiple directory arguments
(e.g. nvim dir1/ dir2/), only the first directory gets handled by oil.
The BufAdd autocmd that renames directory buffers to oil:// URLs is
registered inside setup(), but neovim creates all argument buffers
before setup() runs. The initial hijack block only processes
nvim_get_current_buf(), so additional directory buffers are never
renamed and remain as plain empty buffers.
Solution: iterate all existing buffers at setup time instead of only
the current one. Each directory buffer gets renamed to an oil:// URL
so that BufReadCmd fires when the user switches to it.
Closes: stevearc/oil.nvim#670
Problem: oil sets buftype='acwrite' inside view.initialize(), which runs
in an async finish() callback after adapter.normalize_url(). BufEnter
fires before finish() completes, so user autocmds that check buftype on
oil buffers see an empty string instead of 'acwrite'.
Solution: set buftype='acwrite' early in load_oil_buffer() alongside the
existing early filetype='oil' assignment, before the async gap. The
redundant set in view.initialize() is harmless (idempotent).
Closes: stevearc/oil.nvim#710
Problem: the ---@cast opts -nil was placed after the first opts guard
but LuaLS loses narrowing at the second tbl_deep_extend on line 928,
causing a persistent need-check-nil warning at opts.refetch.
Solution: remove the redundant first opts = opts or {} guard (the
tbl_deep_extend already handles nil) and place the cast after the
second tbl_deep_extend where opts is actually used.
Problem: LuaLS still warns at opts.refetch access because it does not
track narrowing through opts = opts or {}.
Solution: add ---@cast opts -nil after the guard, matching the same
pattern used in util.render_text.
Problem: CI typecheck fails with 13 warnings — 11 need-check-nil in
util.render_text (opts param annotated nil|table but guaranteed non-nil
after tbl_deep_extend), 1 undefined-doc-param in view.render_buffer_async
(annotation says "callback" but param is "caller_callback"), and 1
need-check-nil on opts in the same function.
Solution: add ---@cast opts -nil after the tbl_deep_extend call, fix
the param name in the doc annotation, and add opts = opts or {} guard.
Problem: users who bind <Esc> to close oil in floating windows also
accidentally close oil in split or fullscreen windows. There is no
action that closes only a floating oil window and is a no-op otherwise.
Solution: add a close_float action that checks vim.w.is_oil_win (the
same window-local variable oil.close already uses to identify its own
floating windows) before delegating to oil.close.
Resolves: stevearc/oil.nvim#645
Problem: when nvim-web-devicons returns no highlight for an unrecognized
file extension, the icon gets nil for its highlight group. Directories
have OilDirIcon as a targetable fallback, but files have nothing.
Solution: define OilFileIcon (linked to nil, matching OilFile) and use
it as the fallback in the devicons provider when get_icon returns a nil
highlight. The mini.icons provider already returns highlights for all
files so only the devicons path needs this.
Resolves: stevearc/oil.nvim#690
Problem: the rendering code constructs "OilExecutable" .. "Hidden" for
hidden executables, but that group was never defined. Hidden executables
silently lose all highlighting instead of being dimmed like every other
hidden entry type.
Solution: add OilExecutableHidden linked to OilHidden, consistent with
OilFileHidden, OilDirHidden, and all other hidden variants.
Based on: stevearc/oil.nvim#698
Problem: when a symlink target path contains a newline character, the
rendered line passed to nvim_buf_set_lines includes that newline,
causing the error "'replacement string' item contains newlines".
Solution: apply the same gsub("\n", "") sanitization to meta.link in
get_link_text that is already used for entry names at line 791.
Resolves: stevearc/oil.nvim#673
Problem: when Neovim is started with -R (read-only), all buffers get
readonly=true. Oil's render_buffer toggles modifiable to write directory
listings, which triggers "W10: Warning: Changing a readonly file" on
every directory navigation.
Solution: clear readonly on oil buffers during initialization. Oil
buffers are buftype=acwrite — readonly has no meaning for them since
writes go through BufWriteCmd, not the filesystem.
Resolves: stevearc/oil.nvim#642
Problem: files were always created with mode 0644 and directories
with 0755, hardcoded in fs.touch and uv.fs_mkdir. Users who need
different defaults (e.g. 0600 for security) had no config option.
Solution: add new_file_mode (default 420 = 0644) and new_dir_mode
(default 493 = 0755) config options, passed through to fs.touch and
uv.fs_mkdir in the files and mac trash adapters. The fs.touch
signature accepts an optional mode parameter with backwards
compatibility (detects function argument to support old callers).
Local cache directories (SSH, S3) continue using standard system
permissions rather than the user-configured mode.
Based on: stevearc/oil.nvim#537
Problem: files without standard extensions (e.g. scripts with
shebangs, Makefile, Dockerfile) got incorrect or default icons since
icon providers match by filename or extension only.
Solution: add use_slow_filetype_detection option to the icon column
config. When enabled, reads the first 16 lines of each file and
passes them to vim.filetype.match for content-based detection. The
detected filetype is forwarded to mini.icons or nvim-web-devicons as
a trailing parameter, preserving backwards compatibility with
existing icon provider implementations.
Based on: stevearc/oil.nvim#618
Problem: executable files were visually indistinguishable from regular
files in oil buffers.
Solution: add OilExecutable highlight group (linked to DiagnosticOk)
that detects executables via Unix permission bits (S_IXUSR|S_IXGRP|
S_IXOTH) and Windows executable extensions (.exe, .bat, .cmd, .com,
.ps1). Applied to both regular files and symlink targets.
Based on: stevearc/oil.nvim#698Closesstevearc/oil.nvim#679
Problem: the is_hidden_file and is_always_hidden config callbacks
only received (name, bufnr), making it impossible to filter by entry
type, permissions, or other metadata without reimplementing entry
lookup.
Solution: pass the full oil.Entry as a third argument to both
callbacks. Existing configs that only accept (name, bufnr) are
unaffected since Lua silently ignores extra arguments. The internal
should_display function signature changes from (name, bufnr) to
(bufnr, entry) to reflect its new contract.
Cherry-picked from: stevearc/oil.nvim#644
Problem: when close() is triggered from visual or operator-pending
mode (e.g. pressing q after d in an oil buffer), the buffer closes
instead of canceling the pending operation. This happens because
keymaps without an explicit mode default to "" which covers normal,
visual, select, and operator-pending modes.
Solution: check the current mode at the top of close() and send
<Esc> to cancel the mode instead of proceeding with the close. The
check covers all visual modes (v, V, CTRL-V), select modes (s, S,
CTRL-S), and operator-pending submodes (no, nov, noV, noCTRL-V).
Based on: stevearc/oil.nvim#495
Problem: third-party plugins like oil-git-status.nvim had no way to
know when an oil buffer was re-rendered after a filesystem change,
causing their decorations to be cleared with no signal to refresh.
Solution: emit a User OilReadPost autocmd after every successful
render_buffer_async call. Also document all oil user events
(OilEnter, OilReadPost, OilMutationComplete) in oil.txt since none
were previously documented.
Cherry-picked from: stevearc/oil.nvim#723
Problem: the BufAdd autocmd that hijacks directory buffers was always
created, even when default_file_explorer was false. The guard inside
maybe_hijack_directory_buffer made it a no-op, but the autocmd still
fired on every BufAdd event.
Solution: wrap the BufAdd autocmd creation and initial buffer hijack
in a config.default_file_explorer conditional. The guard inside
maybe_hijack_directory_buffer is kept as defense-in-depth.
Based on: stevearc/oil.nvim#720
Problem: user keymap overrides like <c-t> failed to replace the
default <C-t> because the keys were compared as raw strings during
merge, leaving both entries in the table.
Solution: use nvim_replace_termcodes to compare keymap keys by their
internal representation, removing any default entry that normalizes
to the same key as the user-provided one before inserting it.
Closes#692
Cherry-picked from: stevearc/oil.nvim#725
Problem: get_current_dir returns nil in several cases that were not
documented, causing confusion when used in keymaps that open pickers
like Telescope (#682). Also, posix_to_os_path could crash on Windows
when no drive letter is found.
Solution: expand get_current_dir docs to explain nil return cases, add
a Telescope recipe with nil guards, and add a defensive nil check in
posix_to_os_path.
Cherry-picked from: stevearc/oil.nvim#727
Problem: the old URL specifications.freedesktop.org/trash-spec/1.0/ is
dead and redirects or 404s.
Solution: update to the current URL trash/1.0/ in docs, source, and
the generation script.
Cherry-picked from: stevearc/oil.nvim#722
* feat: `toggle_float` now takes the same params as `open_float`
* docs: update `toggle_float` docs for `opts` and `cb` params
* fix: ensure cb is always called
---------
Co-authored-by: Steven Arcangeli <506791+stevearc@users.noreply.github.com>
* feat: add support for column text alignment
* refactor(util): replace rpad with pad_align
* refactor(columns): whitespace handling in parse_col
* refactor: small changes
* doc: add align option to doc generation
* refactor: replace lpad with pad_align
---------
Co-authored-by: Steven Arcangeli <stevearc@stevearc.com>
The `complete` callback checks `err` instead of `err2`, but `err` is
always nil inside the `elseif entries` branch. This silently ignores
child operation errors, causing misleading "directory not empty" failures.