From 6295532b71b39d88d3fd8d820e9c77ec04eab452 Mon Sep 17 00:00:00 2001 From: Mika Vilpas Date: Wed, 16 Oct 2024 18:58:52 +0300 Subject: [PATCH] fix: report parent directory of input_path as last_directory Background ========== When yazi is started too late, it can happen that we don't know what the last_directory was when yazi has exited. When this happens, yazi has already reported its `cd` event before `ya` starts and yazi.nvim cannot capture it. The issue can be tracked in https://github.com/sxyazi/yazi/issues/1314 Issue ===== Currently yazi.nvim works around this limitation by using the parent directory of the input_path as the last_directory since it's a good guess. However, it looks like this has never worked reliably due to a bug in the implementation. The correct directory was calculated, but it was not assigned to the last_directory field in the state - it was ignored and had no effect. Solution ======== Assign the correct directory to the last_directory field in the state. --- lua/yazi.lua | 4 +- spec/yazi/helpers/test_files.lua | 17 +++++ spec/yazi/yazi_spec.lua | 124 +++++++++++++++++++++++-------- 3 files changed, 112 insertions(+), 33 deletions(-) create mode 100644 spec/yazi/helpers/test_files.lua diff --git a/lua/yazi.lua b/lua/yazi.lua index 5f03aed..b4db995 100644 --- a/lua/yazi.lua +++ b/lua/yazi.lua @@ -73,6 +73,7 @@ function M.yazi(config, input_path) yazi_event_handling.process_events_emitted_from_yazi(events) local last_directory = event_info.last_directory + if last_directory == nil then if path:is_file() then last_directory = path:parent() @@ -80,8 +81,9 @@ function M.yazi(config, input_path) last_directory = path end end + utils.on_yazi_exited(prev_win, prev_buf, win, config, selected_files, { - last_directory = event_info.last_directory or path:parent(), + last_directory = last_directory, }) if hovered_url then diff --git a/spec/yazi/helpers/test_files.lua b/spec/yazi/helpers/test_files.lua new file mode 100644 index 0000000..d86b0cf --- /dev/null +++ b/spec/yazi/helpers/test_files.lua @@ -0,0 +1,17 @@ +local M = {} + +---@param target_file string +function M.create_test_file(target_file) + local plenary_path = require("plenary.path") + local file = io.open(target_file, "w") -- Open or create the file in write mode + assert(file, "Failed to create file " .. target_file) + if file then + file:write("") + file:close() + end + assert(plenary_path:new(target_file):exists()) + assert(plenary_path:new(target_file):is_file()) + assert(plenary_path:new(target_file):parent():is_dir()) +end + +return M diff --git a/spec/yazi/yazi_spec.lua b/spec/yazi/yazi_spec.lua index 76e364b..04026de 100644 --- a/spec/yazi/yazi_spec.lua +++ b/spec/yazi/yazi_spec.lua @@ -4,6 +4,7 @@ local assert = require("luassert") local mock = require("luassert.mock") local match = require("luassert.match") local spy = require("luassert.spy") +local test_files = require("spec.yazi.helpers.test_files") package.loaded["yazi.process.yazi_process"] = require("spec.yazi.helpers.fake_yazi_process") local fake_yazi_process = require("spec.yazi.helpers.fake_yazi_process") @@ -68,38 +69,97 @@ describe("opening a file", function() assert_opened_yazi_with_files({ "/tmp/" }) end) - it( - "calls the yazi_closed_successfully hook when a file is selected in yazi's chooser", - function() - local target_file = "/abc/test-file-potato.txt" - - fake_yazi_process.setup_created_instances_to_instantly_exit({ - selected_files = { target_file }, - }) - - ---@param state YaziClosedState - ---@diagnostic disable-next-line: unused-local - local spy_hook = spy.new(function(chosen_file, _config, state) - assert.equals(target_file, chosen_file) - assert.equals("/abc", state.last_directory.filename) - end) - - vim.api.nvim_command("edit /abc/test-file.txt") - - plugin.yazi({ - chosen_file_path = "/tmp/yazi_filechosen", - ---@diagnostic disable-next-line: missing-fields - hooks = { - ---@diagnostic disable-next-line: assign-type-mismatch - yazi_closed_successfully = spy_hook, - }, - }) - - assert - .spy(spy_hook) - .was_called_with(target_file, match.is_table(), match.is_table()) - end - ) + describe("when a file is selected in yazi's chooser", function() + it( + "calls the yazi_closed_successfully hook with the target_file and last_directory", + function() + fake_yazi_process.setup_created_instances_to_instantly_exit({ + selected_files = { + -- in this test, the cd event defines the last_directory so this is ignored + }, + ---@type YaziChangeDirectoryEvent[] + events = { + { + type = "cd", + timestamp = "123", + id = "123", + url = "/abc", + }, + }, + }) + + ---@param state YaziClosedState + ---@diagnostic disable-next-line: unused-local + local spy_hook = spy.new(function(chosen_file, _config, state) + assert.equals(nil, chosen_file) + assert.equals("/abc", state.last_directory.filename) + end) + + plugin.yazi({ + chosen_file_path = "/tmp/yazi_filechosen", + ---@diagnostic disable-next-line: missing-fields + hooks = { + ---@diagnostic disable-next-line: assign-type-mismatch + yazi_closed_successfully = spy_hook, + }, + }) + + assert + .spy(spy_hook) + .was_called_with(nil, match.is_table(), match.is_table()) + end + ) + + it( + "uses the parent directory of the input_path as the last_directory when no events are emitted", + function() + local plenary_path = require("plenary.path") + -- it can happen that we don't know what the last_directory was when + -- yazi has exited. This currently happens when `ya` is started too + -- late - yazi has already reported its `cd` event before `ya` starts + -- due to https://github.com/sxyazi/yazi/issues/1314 + -- + -- we work around this by using the parent directory of the input_path + -- since it's a good guess + local target_file = + "/tmp/test-file-potato-ea7142f8-ac6d-4037-882c-7dbc4f7b6c65.txt" + test_files.create_test_file(target_file) + + fake_yazi_process.setup_created_instances_to_instantly_exit({ + selected_files = { target_file }, + events = { + -- no events are emitted from yazi + }, + }) + + local spy_yazi_closed_successfully = spy.new( + ---@param state YaziClosedState + ---@diagnostic disable-next-line: unused-local + function(chosen_file, _config, state) + assert.equals(target_file, chosen_file) + assert.equals("/tmp", state.last_directory.filename) + assert.equals( + "/tmp", + plenary_path:new(target_file):parent().filename + ) + end + ) + + plugin.yazi({ + chosen_file_path = "/tmp/yazi_filechosen", + ---@diagnostic disable-next-line: missing-fields + hooks = { + ---@diagnostic disable-next-line: assign-type-mismatch + yazi_closed_successfully = spy_yazi_closed_successfully, + }, + }, target_file) + + assert + .spy(spy_yazi_closed_successfully) + .was_called_with(target_file, match.is_table(), match.is_table()) + end + ) + end) it("calls the yazi_opened hook when yazi is opened", function() local spy_yazi_opened_hook = spy.new()