diff --git a/README.md b/README.md index c9d16afd..f5dd98f6 100644 --- a/README.md +++ b/README.md @@ -38,10 +38,12 @@ Using lazy.nvim: ---@type YaziConfig opts = { -- Below is the default configuration. It is optional to set these values. + -- You can customize the configuration for each yazi call by passing it to + -- yazi() explicitly -- enable this if you want to open yazi instead of netrw. -- Note that if you enable this, you need to call yazi.setup() to - -- initialize the plugin. + -- initialize the plugin. lazy.nvim does this for you in certain cases. open_for_directories = false, -- the path to a temporary file that will be created by yazi to store the @@ -54,20 +56,26 @@ Using lazy.nvim: -- what neovim should do a when a file was opened (selected) in yazi. -- Defaults to simply opening the file. - -- If you want to open it in a split / new tab, you can define it here. - open_file_function = function(chosen_file) end, + open_file_function = function(chosen_file, config) end, + + -- completely override the keymappings for yazi. This function will be + -- called in the context of the yazi terminal buffer. + set_keymappings_function = function(yazi_buffer_id, config) end, hooks = { -- if you want to execute a custom action when yazi has been opened, - -- you can define it here - yazi_opened = function(preselected_path) end, + -- you can define it here. + yazi_opened = function(preselected_path, yazi_buffer_id, config) + -- you can optionally modify the config for this specific yazi + -- invocation if you want to customize the behaviour + end, -- when yazi was successfully closed - yazi_closed_successfully = function(chosen_file) end, + yazi_closed_successfully = function(chosen_file, config) end, -- when yazi opened multiple files. The default is to send them to the -- quickfix list, but if you want to change that, you can define it here - yazi_opened_multiple_files = function(chosen_files) end, + yazi_opened_multiple_files = function(chosen_files, config) end, }, -- the floating window scaling factor. 1 means 100%, 0.9 means 90%, etc. @@ -79,6 +87,12 @@ Using lazy.nvim: } ``` +## Keybindings + +These are the default keybindings that are available when yazi is open: + +- ``: open the selected file in a vertical split + ## About my fork I forked this from for my own use, and also because I wanted to learn neovim plugin development. @@ -93,5 +107,6 @@ So far I have done some maintenance work and added a bunch of features: - feat: files that are renamed, moved, deleted, or trashed in yazi are kept in sync with open buffers (this requires a version of yazi that includes [this](https://github.com/sxyazi/yazi/pull/880) change from 2024-04-06) - feat: allow customizing the method of opening the selected file in neovim - feat: can send multiple opened files to the quickfix list +- feat: can open a file in a vertical split If you'd like to collaborate, contact me via GitHub issues. diff --git a/lua/yazi.lua b/lua/yazi.lua index fabb073a..5468e081 100644 --- a/lua/yazi.lua +++ b/lua/yazi.lua @@ -24,7 +24,7 @@ function M.yazi(config, path) local prev_win = vim.api.nvim_get_current_win() - local win, buffer = window.open_floating_window(config) + local win, _, yazi_buffer = window.open_floating_window(config) os.remove(config.chosen_file_path) local cmd = string.format( @@ -41,16 +41,18 @@ function M.yazi(config, path) on_exit = function(_job_id, code, _event) M.yazi_loaded = false if code ~= 0 then + print('yazi exited with code', code) return end - utils.on_yazi_exited(prev_win, win, buffer, config) + utils.on_yazi_exited(prev_win, win, yazi_buffer, config) event_handling.process_events_emitted_from_yazi(config) end, }) - config.hooks.yazi_opened(path) + config.hooks.yazi_opened(path, yazi_buffer, config) + config.set_keymappings_function(yazi_buffer, config) end vim.schedule(function() vim.cmd('startinsert') diff --git a/lua/yazi/config.lua b/lua/yazi/config.lua index 52867cd7..e215e5c9 100644 --- a/lua/yazi/config.lua +++ b/lua/yazi/config.lua @@ -1,36 +1,19 @@ +local openers = require('yazi.openers') + local M = {} ----@return YaziConfig function M.default() ---@type YaziConfig return { open_for_directories = false, chosen_file_path = '/tmp/yazi_filechosen', events_file_path = '/tmp/yazi.nvim.events.txt', - open_file_function = function(chosen_file) - vim.cmd(string.format('edit %s', chosen_file)) - end, + open_file_function = openers.open_file, + set_keymappings_function = M.default_set_keymappings_function, hooks = { - ---@diagnostic disable-next-line: unused-local - yazi_opened = function(_preselected_path) end, - ---@diagnostic disable-next-line: unused-local - yazi_closed_successfully = function(_chosen_file) end, - yazi_opened_multiple_files = function(chosen_files) - -- show the items in the quickfix list - vim.fn.setqflist({}, 'r', { - title = 'Yazi', - items = vim.tbl_map(function(file) - return { - filename = file, - lnum = 1, - text = file, - } - end, chosen_files), - }) - - -- open the quickfix window - vim.cmd('copen') - end, + yazi_opened = function() end, + yazi_closed_successfully = function() end, + yazi_opened_multiple_files = openers.send_files_to_quickfix_list, }, floating_window_scaling_factor = 0.9, @@ -38,4 +21,20 @@ function M.default() } end +---@param yazi_buffer integer +---@param config YaziConfig +function M.default_set_keymappings_function(yazi_buffer, config) + vim.keymap.set({ 't' }, '', function() + config.open_file_function = openers.open_file_in_vertical_split + + -- select the current file in yazi and close it (enter is the default + -- keybinding for selecting a file) + vim.api.nvim_feedkeys( + vim.api.nvim_replace_termcodes('', true, false, true), + 'n', + true + ) + end, { buffer = yazi_buffer }) +end + return M diff --git a/lua/yazi/openers.lua b/lua/yazi/openers.lua new file mode 100644 index 00000000..ce20d8ae --- /dev/null +++ b/lua/yazi/openers.lua @@ -0,0 +1,29 @@ +local M = {} + +---@param chosen_file string +function M.open_file(chosen_file) + vim.cmd(string.format('edit %s', chosen_file)) +end + +function M.open_file_in_vertical_split(chosen_file) + vim.cmd(string.format('vsplit %s', chosen_file)) +end + +---@param chosen_files string[] +function M.send_files_to_quickfix_list(chosen_files) + vim.fn.setqflist({}, 'r', { + title = 'Yazi', + items = vim.tbl_map(function(file) + return { + filename = file, + lnum = 1, + text = file, + } + end, chosen_files), + }) + + -- open the quickfix window + vim.cmd('copen') +end + +return M diff --git a/lua/yazi/types.lua b/lua/yazi/types.lua index 1e17b361..4d811f9b 100644 --- a/lua/yazi/types.lua +++ b/lua/yazi/types.lua @@ -2,15 +2,16 @@ ---@field public open_for_directories? boolean ---@field public chosen_file_path? string "the path to a temporary file that will be created by yazi to store the chosen file path" ---@field public events_file_path? string "the path to a temporary file that will be created by yazi to store events" ----@field public open_file_function? fun(chosen_file: string): nil "a function that will be called when a file is chosen in yazi" +---@field public open_file_function? fun(chosen_file: string, config: YaziConfig): nil "a function that will be called when a file is chosen in yazi" +---@field public set_keymappings_function? fun(buffer: integer, config: YaziConfig): nil "the function that will set the keymappings for the yazi floating window. It will be called after the floating window is created." ---@field public hooks? YaziConfigHooks ---@field public floating_window_scaling_factor? float "the scaling factor for the floating window. 1 means 100%, 0.9 means 90%, etc." ---@field public yazi_floating_window_winblend? float "the transparency of the yazi floating window (0-100). See :h winblend" ---@class YaziConfigHooks ----@field public yazi_opened fun(preselected_path: string | nil): nil ----@field public yazi_closed_successfully fun(chosen_file: string | nil): nil ----@field public yazi_opened_multiple_files fun(chosen_files: string[]): nil +---@field public yazi_opened fun(preselected_path: string | nil, buffer: integer, config: YaziConfig):nil +---@field public yazi_closed_successfully fun(chosen_file: string | nil, config: YaziConfig): nil +---@field public yazi_opened_multiple_files fun(chosen_files: string[], config: YaziConfig): nil ---@alias YaziEvent YaziRenameEvent | YaziMoveEvent | YaziDeleteEvent | YaziTrashEvent diff --git a/lua/yazi/utils.lua b/lua/yazi/utils.lua index e976e395..082acb99 100644 --- a/lua/yazi/utils.lua +++ b/lua/yazi/utils.lua @@ -172,12 +172,12 @@ function M.on_yazi_exited( local chosen_files = vim.fn.readfile(config.chosen_file_path) if #chosen_files > 1 then - config.hooks.yazi_opened_multiple_files(chosen_files) + config.hooks.yazi_opened_multiple_files(chosen_files, config) else local chosen_file = chosen_files[1] - config.hooks.yazi_closed_successfully(chosen_file) + config.hooks.yazi_closed_successfully(chosen_file, config) if chosen_file then - config.open_file_function(chosen_file) + config.open_file_function(chosen_file, config) end end end diff --git a/lua/yazi/window.lua b/lua/yazi/window.lua index 2ba4a204..b974da09 100644 --- a/lua/yazi/window.lua +++ b/lua/yazi/window.lua @@ -2,7 +2,7 @@ local M = {} --- open a floating window with nice borders ---@param config YaziConfig ----@return integer, integer +---@return integer, integer, integer function M.open_floating_window(config) local height = math.ceil(vim.o.lines * config.floating_window_scaling_factor) - 1 @@ -66,7 +66,7 @@ function M.open_floating_window(config) local cmd = [[autocmd WinLeave silent! execute 'silent bdelete! %s']] vim.cmd(cmd:format(border_buffer)) - return win, border_window + return win, border_window, yazi_buffer end return M diff --git a/tests/yazi/yazi_spec.lua b/tests/yazi/yazi_spec.lua index d247b40d..0d902950 100644 --- a/tests/yazi/yazi_spec.lua +++ b/tests/yazi/yazi_spec.lua @@ -48,11 +48,12 @@ describe('opening a file', function() local exit_code = 0 vim.fn.writefile({ target_file }, '/tmp/yazi_filechosen') callback.on_exit('job-id-ignored', exit_code, 'event-ignored') + return 0 end end) it('opens the file that the user selected in yazi', function() - plugin.yazi() + plugin.yazi({ set_keymappings_function = function() end }) assert.equals(target_file, vim.fn.expand('%')) end) @@ -68,6 +69,7 @@ describe('opening a file', function() vim.api.nvim_command('edit /abc/test-file.txt') plugin.yazi({ + set_keymappings_function = function() end, ---@diagnostic disable-next-line: missing-fields hooks = { ---@diagnostic disable-next-line: assign-type-mismatch @@ -75,37 +77,45 @@ describe('opening a file', function() }, }) - assert.spy(spy_hook).was_called_with('/abc/test-file.txt') + assert + .spy(spy_hook) + .was_called_with('/abc/test-file.txt', match.is_table()) end ) it('calls the yazi_opened hook when yazi is opened', function() - local spy_hook = spy.new() + local spy_yazi_opened_hook = spy.new() vim.api.nvim_command('edit /abc/yazi_opened_hook_file.txt') plugin.yazi({ + set_keymappings_function = function() end, ---@diagnostic disable-next-line: missing-fields hooks = { ---@diagnostic disable-next-line: assign-type-mismatch - yazi_opened = spy_hook, + yazi_opened = spy_yazi_opened_hook, }, }) - assert.spy(spy_hook).was_called_with('/abc/yazi_opened_hook_file.txt') + assert + .spy(spy_yazi_opened_hook) + .was_called_with('/abc/yazi_opened_hook_file.txt', match.is_number(), match.is_table()) end) it('calls the open_file_function to open the selected file', function() - local spy_hook = spy.new() + local spy_open_file_function = spy.new() vim.api.nvim_command('edit /abc/test-file.txt') plugin.yazi({ + set_keymappings_function = function() end, ---@diagnostic disable-next-line: assign-type-mismatch - open_file_function = spy_hook, + open_file_function = spy_open_file_function, }) - assert.spy(spy_hook).was_called_with('/abc/test-file.txt') + assert + .spy(spy_open_file_function) + .was_called_with('/abc/test-file.txt', match.is_table()) end) end) @@ -130,6 +140,7 @@ describe('opening multiple files', function() it('can open multiple files', function() local spy_open_multiple_files = spy.new() plugin.yazi({ + set_keymappings_function = function() end, ---@diagnostic disable-next-line: missing-fields hooks = { ---@diagnostic disable-next-line: assign-type-mismatch @@ -141,6 +152,6 @@ describe('opening multiple files', function() assert.spy(spy_open_multiple_files).was_called_with({ target_file_1, target_file_2, - }) + }, match.is_table()) end) end)