diff --git a/lua/oil/mutator/parser.lua b/lua/oil/mutator/parser.lua index bb222749..d9a51ae5 100644 --- a/lua/oil/mutator/parser.lua +++ b/lua/oil/mutator/parser.lua @@ -57,8 +57,14 @@ local function compare_link_target(meta, parsed_entry) return meta_name == parsed_name end +---@class oil.ParseResultData +---@field _type 'directory'|'file'|'link' +---@field id number +---@field name string +---@field link_target string + ---@class (exact) oil.ParseResult ----@field data table Parsed entry data +---@field data oil.ParseResultData | table Parsed entry data ---@field ranges table Locations of the various columns ---@field entry nil|oil.InternalEntry If the entry already exists @@ -220,8 +226,6 @@ M.parse = function(bufnr) err_message = "No filename found" elseif not entry then err_message = "Could not find existing entry (was the ID changed?)" - elseif parsed_entry.name:match("/") or parsed_entry.name:match(fs.sep) then - err_message = "Filename cannot contain path separator" end if err_message then table.insert(errors, { @@ -235,6 +239,16 @@ M.parse = function(bufnr) assert(entry) check_dupe(parsed_entry.name, i) + + local maybe_new_dir = parsed_entry.name:gsub("/[^/]+$", "") + if #maybe_new_dir ~= 0 and maybe_new_dir ~= parsed_entry.name then + table.insert(diffs, { + type = "new", + name = maybe_new_dir, + entry_type = "directory", + }) + end + local meta = entry[FIELD_META] if original_entries[parsed_entry.name] == parsed_entry.id then if entry[FIELD_TYPE] == "link" and not compare_link_target(meta, parsed_entry) then diff --git a/tests/parser_spec.lua b/tests/parser_spec.lua index 9884ca1b..2fd6edbd 100644 --- a/tests/parser_spec.lua +++ b/tests/parser_spec.lua @@ -194,6 +194,60 @@ describe("parser", function() }, diffs) end) + it("moves a file into a new directory by renaming", function() + local file = test_adapter.test_set("/foo/a.txt", "file") + vim.cmd.edit({ args = { "oil-test:///foo/" } }) + local bufnr = vim.api.nvim_get_current_buf() + + set_lines(bufnr, { + string.format("/%d new/a.txt", file[FIELD_ID]), + }) + + local diffs = parser.parse(bufnr) + + assert.are.same({ + { type = "new", name = "new", entry_type = "directory" }, + { type = "new", id = file[FIELD_ID], name = "new/a.txt", entry_type = "file" }, + { type = "delete", id = file[FIELD_ID], name = "a.txt" }, + }, diffs) + end) + + it("moves a directory into a new directory by renaming", function() + local dir = test_adapter.test_set("/foo/0.7", "directory") + vim.cmd.edit({ args = { "oil-test:///foo/" } }) + local bufnr = vim.api.nvim_get_current_buf() + + set_lines(bufnr, { + string.format("/%d exercises/0.7/", dir[FIELD_ID]), + }) + + local diffs = parser.parse(bufnr) + + assert.are.same({ + { type = "new", name = "exercises", entry_type = "directory" }, + { type = "new", id = dir[FIELD_ID], name = "exercises/0.7", entry_type = "directory" }, + { type = "delete", id = dir[FIELD_ID], name = "0.7" }, + }, diffs) + end) + + it("moves a file into nested new directories by renaming", function() + local file = test_adapter.test_set("/foo/a.txt", "file") + vim.cmd.edit({ args = { "oil-test:///foo/" } }) + local bufnr = vim.api.nvim_get_current_buf() + + set_lines(bufnr, { + string.format("/%d one/two/a.txt", file[FIELD_ID]), + }) + + local diffs = parser.parse(bufnr) + + assert.are.same({ + { type = "new", name = "one/two", entry_type = "directory" }, + { type = "new", id = file[FIELD_ID], name = "one/two/a.txt", entry_type = "file" }, + { type = "delete", id = file[FIELD_ID], name = "a.txt" }, + }, diffs) + end) + it("detects a new trailing slash as a delete + create", function() local file = test_adapter.test_set("/foo", "file") vim.cmd.edit({ args = { "oil-test:///" } })