From 0904cdd30aed8cfbf3f1cc03130b256e23cdd455 Mon Sep 17 00:00:00 2001 From: Mika Vilpas Date: Thu, 11 Apr 2024 19:51:28 +0300 Subject: [PATCH] feat: when files are moved in yazi, they stay in sync in nvim https://github.com/sxyazi/yazi/pull/880 --- lua/yazi.lua | 16 +++++++- lua/yazi/event_handling.lua | 2 +- lua/yazi/types.lua | 12 ++++-- lua/yazi/utils.lua | 15 ++++++++ tests/yazi/move_spec.lua | 75 ++++++++++++++++++++++++++++++++++++ tests/yazi/open_dir_spec.lua | 2 +- tests/yazi/rename_spec.lua | 6 +-- tests/yazi/yazi_spec.lua | 4 +- 8 files changed, 121 insertions(+), 11 deletions(-) create mode 100644 tests/yazi/move_spec.lua diff --git a/lua/yazi.lua b/lua/yazi.lua index 3adf0fcd..a17a061f 100644 --- a/lua/yazi.lua +++ b/lua/yazi.lua @@ -28,7 +28,7 @@ function M.yazi(config, path) os.remove(config.chosen_file_path) local cmd = string.format( - 'yazi "%s" --local-events "rename,delete,trash" --chooser-file "%s" > %s', + 'yazi "%s" --local-events "rename,delete,trash,move" --chooser-file "%s" > %s', path, config.chosen_file_path, config.events_file_path @@ -84,6 +84,20 @@ function M.yazi(config, path) instruction.path.filename ) end + elseif event.type == 'move' then + ---@cast event YaziMoveEvent + for _, item in ipairs(event.data.items) do + local rename_instructions = + event_handling.get_buffers_that_need_renaming_after_yazi_exited( + item + ) + for _, instruction in ipairs(rename_instructions) do + vim.api.nvim_buf_set_name( + instruction.bufnr, + instruction.path.filename + ) + end + end elseif event.type == 'delete' then ---@cast event YaziDeleteEvent event_handling.process_delete_event(event) diff --git a/lua/yazi/event_handling.lua b/lua/yazi/event_handling.lua index d6d08f4d..7ff6f5a3 100644 --- a/lua/yazi/event_handling.lua +++ b/lua/yazi/event_handling.lua @@ -16,7 +16,7 @@ function M.process_delete_event(event) end end ----@param rename_event YaziEventDataRename +---@param rename_event YaziEventDataRenameOrMove ---@return RenameableBuffer[] "instructions for renaming the buffers (command pattern)" function M.get_buffers_that_need_renaming_after_yazi_exited(rename_event) local open_buffers = utils.get_open_buffers() diff --git a/lua/yazi/types.lua b/lua/yazi/types.lua index 0a59b6ac..b96fdcfa 100644 --- a/lua/yazi/types.lua +++ b/lua/yazi/types.lua @@ -11,15 +11,21 @@ ---@field public yazi_opened? fun(preselected_path: string | nil): nil ---@field public yazi_closed_successfully? fun(chosen_file: string | nil): nil ----@alias YaziEvent YaziRenameEvent | YaziDeleteEvent | YaziTrashEvent +---@alias YaziEvent YaziRenameEvent | YaziMoveEvent | YaziDeleteEvent | YaziTrashEvent ---@class YaziRenameEvent ---@field public type "rename" ---@field public timestamp string ---@field public id string ----@field public data YaziEventDataRename +---@field public data YaziEventDataRenameOrMove ----@class YaziEventDataRename +---@class YaziMoveEvent +---@field public type "move" +---@field public timestamp string +---@field public id string +---@field public data {items: YaziEventDataRenameOrMove[]} + +---@class YaziEventDataRenameOrMove ---@field public from string ---@field public to string diff --git a/lua/yazi/utils.lua b/lua/yazi/utils.lua index cc476446..eae626b9 100644 --- a/lua/yazi/utils.lua +++ b/lua/yazi/utils.lua @@ -62,6 +62,21 @@ function M.parse_events(events_file_lines) data = vim.fn.json_decode(data_string), } table.insert(events, event) + elseif type == 'move' then + -- example of a move event: + -- move,1712854829131439,1712854829131439,{"items":[{"from":"/tmp/test/test","to":"/tmp/test"}]} + local timestamp = parts[2] + local id = parts[3] + local data_string = table.concat(parts, ',', 4, #parts) + + ---@type YaziMoveEvent + local event = { + type = type, + timestamp = timestamp, + id = id, + data = vim.fn.json_decode(data_string), + } + table.insert(events, event) elseif type == 'delete' then -- example of a delete event: -- delete,1712766606832135,1712766606832135,{"urls":["/tmp/test-directory/test_2"]} diff --git a/tests/yazi/move_spec.lua b/tests/yazi/move_spec.lua new file mode 100644 index 00000000..021804fa --- /dev/null +++ b/tests/yazi/move_spec.lua @@ -0,0 +1,75 @@ +local assert = require('luassert') +local event_handling = require('yazi.event_handling') + +describe('get_buffers_that_need_renaming_after_yazi_exited', function() + before_each(function() + -- clear all buffers + for _, buf in ipairs(vim.api.nvim_list_bufs()) do + vim.api.nvim_buf_delete(buf, { force = true }) + end + end) + + it('can detect moves to files whose names match exactly', function() + ---@type YaziEventDataRenameOrMove + local move_event = { + from = '/my-tmp/file1', + to = '/my-tmp/file2', + } + + -- simulate buffers being opened + vim.fn.bufadd('/my-tmp/file1') + vim.fn.bufadd('/my-tmp/file_A') + + local instructions = + event_handling.get_buffers_that_need_renaming_after_yazi_exited( + move_event + ) + + assert.is_equal(vim.tbl_count(instructions), 1) + + local result1 = instructions[1] + assert.is_equal('/my-tmp/file2', result1.path.filename) + assert.is_number(result1.bufnr) + end) + + it( + 'can detect moves to buffers open in a directory that was moved', + function() + ---@type YaziEventDataRenameOrMove + local move_event = { + from = '/my-tmp/dir1', + to = '/my-tmp/dir2', + } + + -- simulate the buffer being opened + vim.fn.bufadd('/my-tmp/dir1/file') + + local instructions = + event_handling.get_buffers_that_need_renaming_after_yazi_exited( + move_event + ) + + assert.is_equal(vim.tbl_count(instructions), 1) + local result = instructions[1] + assert.is_equal('/my-tmp/dir2/file', result.path.filename) + end + ) + + it("doesn't move a buffer that was not moved in yazi", function() + ---@type YaziEventDataRenameOrMove + local move_event = { + from = '/my-tmp/not-opened-file', + to = '/my-tmp/not-opened-file-moved', + } + + -- simulate the buffer being opened + vim.fn.bufadd('/my-tmp/dir1/file') + + local instructions = + event_handling.get_buffers_that_need_renaming_after_yazi_exited( + move_event + ) + + assert.is_equal(vim.tbl_count(instructions), 0) + end) +end) diff --git a/tests/yazi/open_dir_spec.lua b/tests/yazi/open_dir_spec.lua index fe26767a..6328b66e 100644 --- a/tests/yazi/open_dir_spec.lua +++ b/tests/yazi/open_dir_spec.lua @@ -21,7 +21,7 @@ describe('when the user set open_for_directories = true', function() vim.api.nvim_command('edit /') assert.stub(api_mock.termopen).was_called_with( - 'yazi "/" --local-events "rename,delete,trash" --chooser-file "/tmp/yazi_filechosen" > /tmp/yazi.nvim.events.txt', + 'yazi "/" --local-events "rename,delete,trash,move" --chooser-file "/tmp/yazi_filechosen" > /tmp/yazi.nvim.events.txt', match.is_table() ) end) diff --git a/tests/yazi/rename_spec.lua b/tests/yazi/rename_spec.lua index fa2340dd..2c497952 100644 --- a/tests/yazi/rename_spec.lua +++ b/tests/yazi/rename_spec.lua @@ -10,7 +10,7 @@ describe('get_buffers_that_need_renaming_after_yazi_exited', function() end) it('can detect renames to files whose names match exactly', function() - ---@type YaziEventDataRename + ---@type YaziEventDataRenameOrMove local rename_event = { from = '/my-tmp/file1', to = '/my-tmp/file2', @@ -35,7 +35,7 @@ describe('get_buffers_that_need_renaming_after_yazi_exited', function() it( 'can detect renames to buffers open in a directory that was renamed', function() - ---@type YaziEventDataRename + ---@type YaziEventDataRenameOrMove local rename_event = { from = '/my-tmp/dir1', to = '/my-tmp/dir2', @@ -56,7 +56,7 @@ describe('get_buffers_that_need_renaming_after_yazi_exited', function() ) it("doesn't rename a buffer that was not renamed in yazi", function() - ---@type YaziEventDataRename + ---@type YaziEventDataRenameOrMove local rename_event = { from = '/my-tmp/not-opened-file', to = '/my-tmp/not-opened-file-renamed', diff --git a/tests/yazi/yazi_spec.lua b/tests/yazi/yazi_spec.lua index c754f84f..880d63db 100644 --- a/tests/yazi/yazi_spec.lua +++ b/tests/yazi/yazi_spec.lua @@ -17,7 +17,7 @@ describe('opening a file', function() plugin.yazi() assert.stub(api_mock.termopen).was_called_with( - 'yazi "/abc/test-file.txt" --local-events "rename,delete,trash" --chooser-file "/tmp/yazi_filechosen" > /tmp/yazi.nvim.events.txt', + 'yazi "/abc/test-file.txt" --local-events "rename,delete,trash,move" --chooser-file "/tmp/yazi_filechosen" > /tmp/yazi.nvim.events.txt', match.is_table() ) end) @@ -28,7 +28,7 @@ describe('opening a file', function() plugin.yazi() assert.stub(api_mock.termopen).was_called_with( - 'yazi "/tmp/" --local-events "rename,delete,trash" --chooser-file "/tmp/yazi_filechosen" > /tmp/yazi.nvim.events.txt', + 'yazi "/tmp/" --local-events "rename,delete,trash,move" --chooser-file "/tmp/yazi_filechosen" > /tmp/yazi.nvim.events.txt', match.is_table() ) end)