feat: add open_split/toggle_split API and upstream triage batch (#83)

* docs(upstream): triage batch — #739 cherry-pick, 10 issue updates

* feat: add `open_split` and `toggle_split` API

Problem: canola had no way to open a browser in a normal split window;
only floating windows were supported via `open_float`/`toggle_float`.
`M.close` also crashed with E444 when called from the last window.

Solution: port stevearc/oil.nvim#728 — add `open_split(dir, opts, cb)`
and `toggle_split(dir, opts, cb)` mirroring the float API. Use
`is_canola_win`/`canola_original_win` window vars (not the upstream
`is_oil_win` names). Wrap `nvim_win_close` in `pcall` with `enew()`
fallback to handle the last-window E444 case.

Based on: stevearc/oil.nvim#728

* docs: add vimdoc for `open_split`/`toggle_split` and macOS trash recipe

Cherry-picked from: stevearc/oil.nvim#739

* docs(upstream): fix prettier formatting
This commit is contained in:
Barrett Ruth 2026-03-07 15:45:23 -05:00 committed by GitHub
parent 6d19b5c8f5
commit 082573d779
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 121 additions and 8 deletions

View file

@ -404,6 +404,32 @@ toggle_float({dir}, {opts}, {cb}) *canola.toggle_f
plit modifier
{cb} `nil|fun()` Called after the canola buffer is ready
open_split({dir}, {opts}, {cb}) *canola.open_split*
Open canola browser in a split window
Parameters:
{dir} `nil|string` When nil, open the parent of the current buffer, or
the cwd if current buffer is not a file
{opts} `nil|canola.OpenSplitOpts`
{vertical} `nil|boolean` Open the buffer in a vertical split
{horizontal} `nil|boolean` Open the buffer in a horizontal split
{split} `nil|"aboveleft"|"belowright"|"topleft"|"botright"` Split
modifier
{cb} `nil|fun()` Called after the canola buffer is ready
toggle_split({dir}, {opts}, {cb}) *canola.toggle_split*
Open canola browser in a split window, or close it if open
Parameters:
{dir} `nil|string` When nil, open the parent of the current buffer, or
the cwd if current buffer is not a file
{opts} `nil|canola.OpenSplitOpts`
{vertical} `nil|boolean` Open the buffer in a vertical split
{horizontal} `nil|boolean` Open the buffer in a horizontal split
{split} `nil|"aboveleft"|"belowright"|"topleft"|"botright"` Split
modifier
{cb} `nil|fun()` Called after the canola buffer is ready
open({dir}, {opts}, {cb}) *canola.open*
Open canola browser for a directory
@ -1109,6 +1135,15 @@ variant after calling `setup()`:
end
<
Use FreeDesktop trash on macOS ~
*canola-recipe-macos-freedesktop-trash*
For full FreeDesktop spec compliance on macOS (list, restore operations), or
for compatibility with FreeDesktop-compliant trash programs like gtrash:
>lua
package.loaded["canola.adapters.trash.mac"] = require("canola.adapters.trash.freedesktop")
<
--------------------------------------------------------------------------------
EVENTS *canola-events*

View file

@ -21,6 +21,7 @@ Upstream PRs cherry-picked or adapted into this fork.
| [#723](https://github.com/stevearc/oil.nvim/pull/723) | Emit `OilReadPost` event after render | [`29239d5`](https://github.com/barrettruth/canola.nvim/commit/29239d5) |
| [#725](https://github.com/stevearc/oil.nvim/pull/725) | Normalize keymap keys before config merge | [`723145c`](https://github.com/barrettruth/canola.nvim/commit/723145c) |
| [#727](https://github.com/stevearc/oil.nvim/pull/727) | Clarify `get_current_dir` nil + Telescope recipe | [`eed6697`](https://github.com/barrettruth/canola.nvim/commit/eed6697) |
| [#739](https://github.com/stevearc/oil.nvim/pull/739) | macOS FreeDesktop trash recipe | `doc/canola.txt` (`canola-recipe-macos-freedesktop-trash`) |
## Original fixes
@ -67,29 +68,29 @@ Bugs fixed in this fork that remain open upstream.
| [#263](https://github.com/stevearc/oil.nvim/issues/263) | open | Diff mode (P2) |
| [#276](https://github.com/stevearc/oil.nvim/issues/276) | open | Archives manipulation (P2) |
| [#280](https://github.com/stevearc/oil.nvim/issues/280) | open | vim-projectionist support (P2) |
| [#288](https://github.com/stevearc/oil.nvim/issues/288) | open | Oil failing to load (P2) |
| [#288](https://github.com/stevearc/oil.nvim/issues/288) | not actionable | No reliable repro; likely lazy.nvim timing issue — setting `lazy = false` resolves it for affected users |
| [#289](https://github.com/stevearc/oil.nvim/issues/289) | open | Show absolute path toggle (P2) |
| [#294](https://github.com/stevearc/oil.nvim/issues/294) | open | Can't handle emojis in filenames (P2) |
| [#298](https://github.com/stevearc/oil.nvim/issues/298) | open | Open float on neovim directory startup (P2) |
| [#302](https://github.com/stevearc/oil.nvim/issues/302) | fixed | `buflisted=true` after jumplist nav — [#71](https://github.com/barrettruth/canola.nvim/pull/71) ([`a078bcf`](https://github.com/barrettruth/canola.nvim/commit/a078bcf)) |
| [#303](https://github.com/stevearc/oil.nvim/issues/303) | open | Preview in float window mode (P2) |
| [#325](https://github.com/stevearc/oil.nvim/issues/325) | open | oil-ssh error from command line (P0) |
| [#330](https://github.com/stevearc/oil.nvim/issues/330) | open | File opens in floating modal |
| [#330](https://github.com/stevearc/oil.nvim/issues/330) | not actionable | Telescope opens file in oil float — cross-plugin interaction, no repro provided |
| [#332](https://github.com/stevearc/oil.nvim/issues/332) | open | Buffer not fixed to floating window (P2) |
| [#335](https://github.com/stevearc/oil.nvim/issues/335) | open | Disable editing outside root dir |
| [#349](https://github.com/stevearc/oil.nvim/issues/349) | open | Parent directory as column/vsplit (P2) |
| [#351](https://github.com/stevearc/oil.nvim/issues/351) | open | Paste deleted file from register |
| [#359](https://github.com/stevearc/oil.nvim/issues/359) | open | Parse error on filenames differing by space (P1) |
| [#360](https://github.com/stevearc/oil.nvim/issues/360) | open | Pick window to open file into |
| [#362](https://github.com/stevearc/oil.nvim/issues/362) | open | "Could not find oil adapter for scheme" error |
| [#362](https://github.com/stevearc/oil.nvim/issues/362) | not actionable | "Could not find oil adapter for scheme" — no minimal repro provided, old nvim version (0.9.5) |
| [#363](https://github.com/stevearc/oil.nvim/issues/363) | fixed | `prompt_save_on_select_new_entry` uses wrong prompt — escape now cancels select |
| [#371](https://github.com/stevearc/oil.nvim/issues/371) | open | Constrain cursor in insert mode |
| [#373](https://github.com/stevearc/oil.nvim/issues/373) | open | Dir from quickfix with bqf/trouble broken (P1) |
| [#375](https://github.com/stevearc/oil.nvim/issues/375) | open | Highlights for file types and permissions (P2) |
| [#380](https://github.com/stevearc/oil.nvim/issues/380) | open | Show file in oil when editing hidden file |
| [#380](https://github.com/stevearc/oil.nvim/issues/380) | open | Show currently-edited hidden files in listing even when `show_hidden = false`; no upstream response, nice to have |
| [#382](https://github.com/stevearc/oil.nvim/issues/382) | open | Relative path in window title (P2) |
| [#392](https://github.com/stevearc/oil.nvim/issues/392) | fixed | Option to skip delete prompt — fixed — `skip_confirm_for_delete` option |
| [#393](https://github.com/stevearc/oil.nvim/issues/393) | open | Auto-save new buffer on entry |
| [#393](https://github.com/stevearc/oil.nvim/issues/393) | open | Auto-save new buffer on entry; no upstream design yet, possible interaction gap with `prompt_save_on_select_new_entry` + `skip_confirm_for_simple_edits` |
| [#396](https://github.com/stevearc/oil.nvim/issues/396) | open | Customize preview content (P2) |
| [#399](https://github.com/stevearc/oil.nvim/issues/399) | open | Open file without closing Oil (P1) |
| [#404](https://github.com/stevearc/oil.nvim/issues/404) | not actionable | Restricted UNC paths — Windows-only (P2) |
@ -108,7 +109,7 @@ Bugs fixed in this fork that remain open upstream.
| [#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 |
| [#492](https://github.com/stevearc/oil.nvim/issues/492) | not actionable | j/k remapping question — answered in comments |
| [#507](https://github.com/stevearc/oil.nvim/issues/507) | open | lacasitos.nvim conflict (P1) |
| [#507](https://github.com/stevearc/oil.nvim/issues/507) | not actionable | lacasitos.nvim conflict on Windows — cross-plugin + Windows-only, no actionable fix |
| [#521](https://github.com/stevearc/oil.nvim/issues/521) | open | oil-ssh connection issues |
| [#525](https://github.com/stevearc/oil.nvim/issues/525) | open | SSH adapter documentation (P2) |
| [#531](https://github.com/stevearc/oil.nvim/issues/531) | not actionable | Windows — incomplete drive letters (P1) |
@ -159,4 +160,5 @@ Bugs fixed in this fork that remain open upstream.
| [#714](https://github.com/stevearc/oil.nvim/issues/714) | not actionable | Support question — answered |
| [#719](https://github.com/stevearc/oil.nvim/issues/719) | not actionable | Neovim crash on node_modules — libuv/neovim bug |
| [#726](https://github.com/stevearc/oil.nvim/issues/726) | not actionable | Meta discussion/roadmap |
| [#736](https://github.com/stevearc/oil.nvim/issues/736) | open | feature request: make icons virtual text |
| [#736](https://github.com/stevearc/oil.nvim/issues/736) | open | Make icons virtual text — related to [#667](https://github.com/stevearc/oil.nvim/pull/667) (virtual text columns, deferred) |
| [#738](https://github.com/stevearc/oil.nvim/issues/738) | open | Allow changing mtime/atime via time column (P2) |

View file

@ -356,6 +356,79 @@ M.toggle_float = function(dir, opts, cb)
end
end
---@class (exact) canola.OpenSplitOpts : canola.OpenOpts
---@field vertical? boolean Open the buffer in a vertical split
---@field horizontal? boolean Open the buffer in a horizontal split
---@field split? "aboveleft"|"belowright"|"topleft"|"botright" Split modifier
---Open canola browser in a split window
---@param dir? string When nil, open the parent of the current buffer, or the cwd if current buffer is not a file
---@param opts? canola.OpenSplitOpts
---@param cb? fun() Called after the canola buffer is ready
M.open_split = function(dir, opts, cb)
opts = opts or {}
local config = require('canola.config')
local util = require('canola.util')
local view = require('canola.view')
local parent_url, basename = M.get_url_for_path(dir)
if basename then
view.set_last_cursor(parent_url, basename)
end
if not opts.vertical and opts.horizontal == nil then
opts.horizontal = true
end
if not opts.split then
if opts.horizontal then
opts.split = vim.o.splitbelow and 'belowright' or 'aboveleft'
else
opts.split = vim.o.splitright and 'belowright' or 'aboveleft'
end
end
local mods = {
vertical = opts.vertical,
horizontal = opts.horizontal,
split = opts.split,
}
local original_winid = vim.api.nvim_get_current_win()
vim.cmd.split({ mods = mods })
local winid = vim.api.nvim_get_current_win()
vim.w[winid].is_canola_win = true
vim.w[winid].canola_original_win = original_winid
vim.cmd.edit({ args = { util.escape_filename(parent_url) }, mods = { keepalt = true } })
if config.buf_options.buflisted ~= nil then
vim.api.nvim_set_option_value('buflisted', config.buf_options.buflisted, { buf = 0 })
end
util.run_after_load(0, function()
if opts.preview then
M.open_preview(opts.preview, cb)
elseif cb then
cb()
end
end)
end
---Open canola browser in a split window, or close it if open
---@param dir nil|string When nil, open the parent of the current buffer, or the cwd if current buffer is not a file
---@param opts? canola.OpenSplitOpts
---@param cb? fun() Called after the canola buffer is ready
M.toggle_split = function(dir, opts, cb)
if vim.w.is_canola_win then
M.close()
if cb then
cb()
end
else
M.open_split(dir, opts, cb)
end
end
---@param canola_bufnr? integer
local function update_preview_window(canola_bufnr)
canola_bufnr = canola_bufnr or 0
@ -419,7 +492,10 @@ M.close = function(opts)
-- If we're in a floating canola window, close it and try to restore focus to the original window
if vim.w.is_canola_win then
local original_winid = vim.w.canola_original_win
vim.api.nvim_win_close(0, true)
local ok, _ = pcall(vim.api.nvim_win_close, 0, true)
if not ok then
vim.cmd.enew()
end
if original_winid and vim.api.nvim_win_is_valid(original_winid) then
vim.api.nvim_set_current_win(original_winid)
end