feat(dir): open entries in splits with a, o mappings #41834

This commit is contained in:
Barrett Ruth authored and GitHub committed 2026-09-11 19:01:41 -04:00
1 parent 361191790a
commit 4ceb92cd76
4 files changed
+76 -27

No files matched your search

+10 -4
View File
@@ -70,13 +70,19 @@ GLOBAL MAPPINGS *dir-mappings*
BUFFER-LOCAL MAPPINGS *dir-buffer-mappings*
• <CR> opens the file or directory under the cursor.
• a opens the file or directory under the cursor in a vertical split.
• o opens the file or directory under the cursor in a horizontal split.
• - opens the parent directory.
• R reloads the directory listing.
These keys map to <Plug>(nvim-dir-open), <Plug>(nvim-dir-up), and
<Plug>(nvim-dir-reload); map those to use different keys. Default mappings are
skipped when the target <Plug> mapping is already mapped. The default global and
directory-buffer "-" mappings are also skipped when "-" is already mapped.
a and o are available for local directories and keep focus in the listing.
'splitright' and 'splitbelow' control where the new window appears.
These keys map to <Plug>(nvim-dir-open), <Plug>(nvim-dir-vsplit),
<Plug>(nvim-dir-split), <Plug>(nvim-dir-up), and <Plug>(nvim-dir-reload); map those
to use different keys. Default mappings are skipped when the target <Plug>
mapping is already mapped. The default "-", "a", and "o" mappings are also
skipped when their keys are already mapped.
*dir-config*
Directory buffers follow the global 'hidden' option by default. To delete them
+28 -20
View File
@@ -167,24 +167,15 @@ local function reload(buf, provider)
end
---@param buf integer
local function set_maps(buf)
---@param lhs string
---@param plug string
local function map(lhs, plug)
if vim.fn.hasmapto(plug, 'n') == 0 then
---@param lhs string
---@param plug string
---@param force? boolean
function M._map(buf, lhs, plug, force)
api.nvim_buf_call(buf, function()
if (force or vim.fn.mapcheck(lhs, 'n') == '') and vim.fn.hasmapto(plug, 'n') == 0 then
vim.keymap.set('n', lhs, plug, { buffer = buf, silent = true })
end
end
---@param lhs string
---@param plug string
local function default_map(lhs, plug)
if vim.fn.mapcheck(lhs, 'n') == '' and vim.fn.hasmapto(plug, 'n') == 0 then
vim.keymap.set('n', lhs, plug, { buffer = buf, silent = true })
end
end
map('<CR>', '<Plug>(nvim-dir-open)')
default_map('-', '<Plug>(nvim-dir-up)')
map('R', '<Plug>(nvim-dir-reload)')
end)
end
--- Let handlers reshape the rendered listing. Unlocks the buffer for the duration and
@@ -286,7 +277,9 @@ function load(buf, name, provider, restore_view, setup, select)
end
setup_render_autocmds(buf)
set_maps(buf)
M._map(buf, '<CR>', '<Plug>(nvim-dir-open)', true)
M._map(buf, '-', '<Plug>(nvim-dir-up)')
M._map(buf, 'R', '<Plug>(nvim-dir-reload)', true)
if provider.init then
provider.init(buf, name)
end
@@ -326,12 +319,27 @@ local function get_provider(buf)
return state and state.provider or nil
end
function M._open_entry()
---@param cmd? 'split'|'vsplit'
function M._open_entry(cmd)
local buf = api.nvim_get_current_buf()
local provider = get_provider(buf)
local entry = current_entry(buf)
if provider and entry then
provider.open(buf, api.nvim_buf_get_name(buf), entry)
if not provider or not entry then
return
end
local name = api.nvim_buf_get_name(buf)
local win = cmd and api.nvim_get_current_win()
local ok, err = pcall(function()
if cmd then
api.nvim_cmd({ cmd = cmd }, {})
end
provider.open(buf, name, entry)
end)
if win and api.nvim_win_is_valid(win) then
api.nvim_set_current_win(win)
end
if not ok then
error(err, 0)
end
end
+9
View File
@@ -102,6 +102,15 @@ function M.init(buf, path)
if api.nvim_get_option_value('filetype', { buf = buf }) ~= 'directory' then
api.nvim_set_option_value('filetype', 'directory', { buf = buf })
end
local dir = require('nvim.dir')
vim.keymap.set('n', '<Plug>(nvim-dir-vsplit)', function()
dir._open_entry('vsplit')
end, { buffer = buf, silent = true, desc = 'Open directory entry in a vertical split' })
vim.keymap.set('n', '<Plug>(nvim-dir-split)', function()
dir._open_entry('split')
end, { buffer = buf, silent = true, desc = 'Open directory entry in a horizontal split' })
dir._map(buf, 'a', '<Plug>(nvim-dir-vsplit)')
dir._map(buf, 'o', '<Plug>(nvim-dir-split)')
api.nvim_buf_call(buf, function()
pcall(api.nvim_cmd, {
cmd = 'bcd',
+29 -3
View File
@@ -572,7 +572,7 @@ describe('nvim.dir', function()
eq({ 'unsaved' }, api.nvim_buf_get_lines(old_buf, 0, -1, false))
end)
it('does not shadow startup plugin `-` mappings in directory buffers', function()
it('does not shadow startup plugin mappings in directory buffers', function()
make_fixture()
write_config_plugin(
'plugin/dirvish.lua',
@@ -581,6 +581,8 @@ describe('nvim.dir', function()
vim.keymap.set('n', '-', function()
vim.g.dirvish_up = vim.g.dirvish_up + 1
end)
vim.keymap.set('n', 'a', '<Nop>')
vim.keymap.set('n', 's', '<Plug>(nvim-dir-split)')
]]
)
@@ -591,12 +593,17 @@ describe('nvim.dir', function()
edit(root)
assert_directory(root)
eq(0, fn.maparg('-', 'n', false, true).buffer)
eq('<Nop>', fn.maparg('a', 'n'))
eq('', fn.maparg('o', 'n'))
feed('-')
feed('-as')
poke_eventloop()
eq(1, exec_lua('return vim.g.dirvish_up'))
assert_directory(root)
eq(2, #api.nvim_list_wins())
feed('<C-W>p')
assert_directory(subdir)
end)
it('preserves alternate buffer when opening a parent directory', function()
@@ -669,6 +676,22 @@ describe('nvim.dir', function()
eq(vim.uv.fs_realpath(root), vim.uv.fs_realpath(fn.getcwd()))
eq('alpha.txt', fn.findfile('alpha.txt'))
local win = api.nvim_get_current_win()
for key, entry in pairs({ a = 'alpha.txt', o = 'subdir/' }) do
local cursor = { line_of(entry), 0 }
api.nvim_win_set_cursor(0, cursor)
feed(key)
poke_eventloop()
eq(win, api.nvim_get_current_win())
assert_directory(root)
eq(cursor, api.nvim_win_get_cursor(0))
eq(2, #api.nvim_list_wins())
eq(key == 'a' and 'row' or 'col', fn.winlayout()[1])
feed('<C-W>p')
eq(root .. '/' .. entry, api.nvim_buf_get_name(0))
command('close')
end
api.nvim_win_set_cursor(0, { line_of('alpha.txt'), 0 })
feed('<CR>')
poke_eventloop()
@@ -680,6 +703,9 @@ describe('nvim.dir', function()
assert_directory(subdir)
eq({ '' }, lines())
eq(vim.uv.fs_realpath(subdir), vim.uv.fs_realpath(fn.getcwd()))
feed('ao')
poke_eventloop()
eq(1, #api.nvim_list_wins())
feed('-')
poke_eventloop()
@@ -824,7 +850,7 @@ describe('nvim.dir', function()
line_of(raw_name)
end
api.nvim_win_set_cursor(0, { line_of('line\0break.txt'), 0 })
feed('<CR>')
feed('a<C-W>p')
poke_eventloop()
eq(root .. '/' .. name, api.nvim_buf_get_name(0))