Skip to content

Commit

Permalink
fix: renaming the same file multiple times did not sync to nvim
Browse files Browse the repository at this point in the history
  • Loading branch information
mikavilpas committed Apr 6, 2024
1 parent 07826ef commit 3a59384
Show file tree
Hide file tree
Showing 7 changed files with 238 additions and 64 deletions.
2 changes: 1 addition & 1 deletion lua/yazi.lua
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ function M.yazi(path)
utils.get_buffers_that_need_renaming_after_yazi_exited(rename_events)

for _, event in ipairs(renames) do
vim.api.nvim_buf_set_name(event.buffer, event.to)
vim.api.nvim_buf_set_name(event.bufnr, event.path.filename)
end
end,
})
Expand Down
71 changes: 71 additions & 0 deletions lua/yazi/renameable_buffer.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
local plenary_path = require('plenary.path')
local plenary_iterators = require('plenary.iterators')

local function remove_trailing_slash(path)
if path:sub(-1) == '/' then
return path:sub(1, -2)
end

return path
end

---@class RenameableBuffer
---@field bufnr number
---@field original_path string
---@field path Path
local RenameableBuffer = {}
RenameableBuffer.__index = RenameableBuffer

---@param bufnr number
---@param path string the original path of the buffer
function RenameableBuffer.new(bufnr, path)
local self = setmetatable({}, RenameableBuffer)

path = remove_trailing_slash(path)

self.bufnr = bufnr
self.original_path = path
self.path = plenary_path:new(path)

return self
end

---@param path string can be a parent directory or an exact file path
---@return boolean
function RenameableBuffer:matches_exactly(path)
path = remove_trailing_slash(path)
return self.path.filename == path
end

---@param path string
function RenameableBuffer:matches_parent(path)
path = remove_trailing_slash(path)
local found = plenary_iterators
.iter(self.path:parents())
:find(function(parent_path)
return path == parent_path
end)

return found ~= nil
end

---@param path string
---@return nil
function RenameableBuffer:rename(path)
path = remove_trailing_slash(path)
self.path = plenary_path:new(path)
end

---@param parent_from string the parent directory that was renamed
---@param parent_to string the new parent directory
---@return nil
function RenameableBuffer:rename_parent(parent_from, parent_to)
local common = self.path.filename:sub(1, #parent_from)
local rest = self.path.filename:sub(#common + 1, -1)

local new_path = parent_to .. rest

self.path = plenary_path:new(new_path)
end

return RenameableBuffer
4 changes: 0 additions & 4 deletions lua/yazi/types.lua
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,3 @@
---@class YaziEventDataRename
---@field public from string
---@field public to string

---@class YaziBufferRenameInstruction
---@field buffer integer the existing buffer number that needs renaming
---@field to string the new file name that the buffer should point to
62 changes: 25 additions & 37 deletions lua/yazi/utils.lua
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
local fn = vim.fn
local iterators = require('plenary.iterators')
local RenameableBuffer = require('yazi.renameable_buffer')

local M = {}

Expand Down Expand Up @@ -87,46 +87,34 @@ function M.read_events_file(path)
end

---@param rename_events YaziRenameEvent[]
---@return YaziBufferRenameInstruction[]
---@return RenameableBuffer[] "instructions for renaming the buffers (command pattern)"
function M.get_buffers_that_need_renaming_after_yazi_exited(rename_events)
local buffers = iterators
.iter(vim.api.nvim_list_bufs())
:filter(function(buffer)
if vim.api.nvim_buf_get_name(buffer) == '' then
return false
end
---@type RenameableBuffer[]
local open_buffers = {}
for _, bufnr in ipairs(vim.api.nvim_list_bufs()) do
local path = vim.api.nvim_buf_get_name(bufnr)
if path ~= '' and path ~= nil then
local renameable_buffer = RenameableBuffer.new(bufnr, path)
open_buffers[#open_buffers + 1] = renameable_buffer
end
end

return true
end)
:map(function(buffer)
-- the buffer is found if
-- * the buffer name matches the original name
-- * or the buffer's file is under a directory that was renamed (also nested directories)
for _, event in ipairs(rename_events) do
local buffer_name = vim.api.nvim_buf_get_name(buffer)

if event.data.from == buffer_name then
---@type YaziBufferRenameInstruction
return {
buffer = buffer,
to = event.data.to,
}
end

local starts_with = buffer_name:sub(1, #event.data.from)
== event.data.from
if starts_with then
---@type YaziBufferRenameInstruction
return {
buffer = buffer,
to = event.data.to .. buffer_name:sub(#event.data.from + 1),
}
end
---@type table<integer, RenameableBuffer>
local renamed_buffers = {}

for _, event in ipairs(rename_events) do
for _, buffer in ipairs(open_buffers) do
if buffer:matches_exactly(event.data.from) then
buffer:rename(event.data.to)
renamed_buffers[buffer.bufnr] = buffer
elseif buffer:matches_parent(event.data.from) then
buffer:rename_parent(event.data.from, event.data.to)
renamed_buffers[buffer.bufnr] = buffer
end
end)
:tolist()
end
end

return buffers
return vim.tbl_values(renamed_buffers)
end

return M
8 changes: 4 additions & 4 deletions tests/yazi/example_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ describe('opening a file', function()
end)

it('opens yazi with the current file selected', function()
vim.api.nvim_command('edit /tmp/test-file.txt')
vim.api.nvim_command('edit /abc/test-file.txt')
plugin.yazi()

assert.stub(api_mock.termopen).was_called_with(
'yazi "/tmp/test-file.txt" --local-events "rename" --chooser-file "/tmp/yazi_filechosen" > /tmp/yazi.nvim.events.txt',
'yazi "/abc/test-file.txt" --local-events "rename" --chooser-file "/tmp/yazi_filechosen" > /tmp/yazi.nvim.events.txt',
match.is_table()
)
end)
Expand All @@ -41,11 +41,11 @@ describe('opening a file', function()

describe("when a file is selected in yazi's chooser", function()
-- yazi writes the selected file to this file for us to read
local target_file = '/tmp/test-file.txt'
local target_file = '/abc/test-file.txt'

before_each(function()
-- have to start editing a valid file, otherwise the plugin will ignore the callback
vim.cmd('edit /tmp/a.txt')
vim.cmd('edit /abc/a.txt')

local termopen = spy.on(api_mock, 'termopen')
termopen.callback = function(_, callback)
Expand Down
105 changes: 87 additions & 18 deletions tests/yazi/rename_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -26,28 +26,28 @@ describe('get_buffers_that_need_renaming_after_yazi_exited', function()
timestamp = '1712242143209837',
id = '1712242143209837',
data = {
from = '/my-tmp/file3',
to = '/my-tmp/file4',
from = '/my-tmp/file_A',
to = '/my-tmp/file_B',
},
},
}

-- simulate the buffers being opened
vim.fn.bufadd('/my-tmp/file1')
vim.fn.bufadd('/my-tmp/file3')
vim.fn.bufadd('/my-tmp/file_A')

local renames =
local rename_instructions =
utils.get_buffers_that_need_renaming_after_yazi_exited(rename_events)

assert.is_equal(#renames, 2)
assert.is_equal(vim.tbl_count(rename_instructions), 2)

local result1 = renames[1]
assert.is_equal('/my-tmp/file2', result1.to)
assert.is_number(result1.buffer)
local result1 = rename_instructions[1]
assert.is_equal('/my-tmp/file2', result1.path.filename)
assert.is_number(result1.bufnr)

local result2 = renames[2]
assert.is_equal('/my-tmp/file4', result2.to)
assert.is_number(result2.buffer)
local result2 = rename_instructions[2]
assert.is_equal('/my-tmp/file_B', result2.path.filename)
assert.is_number(result2.bufnr)
end)

it(
Expand All @@ -69,13 +69,12 @@ describe('get_buffers_that_need_renaming_after_yazi_exited', function()
-- simulate the buffer being opened
vim.fn.bufadd('/my-tmp/dir1/file')

local renames =
local rename_instructions =
utils.get_buffers_that_need_renaming_after_yazi_exited(rename_events)

assert.is_equal(#renames, 1)

local result1 = renames[1]
assert.is_equal('/my-tmp/dir2/file', result1.to)
assert.is_equal(vim.tbl_count(rename_instructions), 1)
local result = rename_instructions[1]
assert.is_equal('/my-tmp/dir2/file', result.path.filename)
end
)

Expand All @@ -96,9 +95,79 @@ describe('get_buffers_that_need_renaming_after_yazi_exited', function()
-- simulate the buffer being opened
vim.fn.bufadd('/my-tmp/dir1/file')

local renames =
local rename_instructions =
utils.get_buffers_that_need_renaming_after_yazi_exited(rename_events)

assert.is_equal(vim.tbl_count(rename_instructions), 0)
end)

it('can rename the same file multiple times', function()
---@type YaziRenameEvent[]
local rename_events = {
{
type = 'rename',
timestamp = '1712242143209837',
id = '1712242143209837',
data = {
from = '/my-tmp/file1',
to = '/my-tmp/file2',
},
},
{
type = 'rename',
timestamp = '1712242143209837',
id = '1712242143209837',
data = {
from = '/my-tmp/file2',
to = '/my-tmp/file3',
},
},
}

-- simulate the buffers being opened
vim.fn.bufadd('/my-tmp/file1')

local rename_instructions =
utils.get_buffers_that_need_renaming_after_yazi_exited(rename_events)

assert.is_equal(vim.tbl_count(rename_instructions), 1)

local result = rename_instructions[1]
assert.is_equal('/my-tmp/file3', result.path.filename)
assert.is_number(result.bufnr)
end)

it('can rename the same directory multiple times', function()
---@type YaziRenameEvent[]
local rename_events = {
{
type = 'rename',
timestamp = '1712242143209837',
id = '1712242143209837',
data = {
from = '/my-tmp/dir1',
to = '/my-tmp/dir2',
},
},
{
type = 'rename',
timestamp = '1712242143209837',
id = '1712242143209837',
data = {
from = '/my-tmp/dir2',
to = '/my-tmp/dir3',
},
},
}

-- simulate the buffer being opened
vim.fn.bufadd('/my-tmp/dir1/file')

local rename_instructions =
utils.get_buffers_that_need_renaming_after_yazi_exited(rename_events)

assert.is_equal(#renames, 0)
assert.is_equal(vim.tbl_count(rename_instructions), 1)
local result = rename_instructions[1]
assert.is_equal('/my-tmp/dir3/file', result.path.filename)
end)
end)
50 changes: 50 additions & 0 deletions tests/yazi/renameable_buffer_spec.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
local RenameableBuffer = require('yazi.renameable_buffer')
local assert = require('luassert')

describe('the RenameableBuffer class', function()
it('matches a parent directory', function()
local rename = RenameableBuffer.new(1, '/my-tmp/file1')
local result = rename:matches_parent('/my-tmp/')
assert.is_true(result)
end)

it('matches a parent directory with a trailing slash', function()
local rename = RenameableBuffer.new(1, '/my-tmp/file1')
local result = rename:matches_parent('/my-tmp')
assert.is_true(result)
end)

it('matches an exact file path', function()
local rename = RenameableBuffer.new(1, '/my-tmp/file1')
local result = rename:matches_exactly('/my-tmp/file1')
assert.is_true(result)
end)

it('does not match a different parent directory', function()
local rename = RenameableBuffer.new(1, '/my-tmp/file1')
assert.is_false(rename:matches_exactly('/my-tmp2'))
end)

it('does not match a different file path', function()
local rename = RenameableBuffer.new(1, '/my-tmp/file1')
assert.is_false(rename:matches_exactly('/my-tmp/file2'))
end)

it('renames a file', function()
local rename = RenameableBuffer.new(1, '/my-tmp/file1')
rename:rename('/my-tmp/file2')
assert.are.equal('/my-tmp/file2', rename.path.filename)

-- does not change the original path
assert.are.equal('/my-tmp/file1', rename.original_path)
end)

it("renames the buffer's parent directory", function()
local buffer = RenameableBuffer.new(1, '/my-tmp/file1')
buffer:rename_parent('/my-tmp', '/my-tmp2')
assert.are.equal('/my-tmp2/file1', buffer.path.filename)

-- does not change the original path
assert.are.equal('/my-tmp/file1', buffer.original_path)
end)
end)

0 comments on commit 3a59384

Please sign in to comment.