Skip to content

Commit a21b58b

Browse files
committed
finish up make_relative
1 parent 9992fd8 commit a21b58b

File tree

2 files changed

+52
-35
lines changed

2 files changed

+52
-35
lines changed

lua/plenary/path2.lua

Lines changed: 34 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,18 @@
1717
--- things like `path.filename = 'foo'` but now explicitly adding some barrier
1818
--- to this. Allows us to compute `filename` from "metadata" parsed once on
1919
--- instantiation.
20-
21-
--- TODO: rework `_filename` according to `_format_parsed_parts`
20+
---
21+
--- - FIX: `Path:make_relative` throws error if you try to make a path relative
22+
--- to another path that is not in the same subpath.
23+
---
24+
--- eg. `Path:new("foo/bar_baz"):make_relative("foo/bar")` => errors as you
25+
--- can't get to "foo/bar_baz" from "foo/bar" without going up in directory.
26+
--- This would previously return "foo/bar_baz" which is wrong.
27+
---
28+
--- Adds an option to walk up path to compensate.
29+
---
30+
--- eg. `Path:new("foo/bar_baz"):make_relative("foo/bar", true)` => returns
31+
--- "../bar_baz"
2232

2333
local uv = vim.loop
2434
local iswin = uv.os_uname().sysname == "Windows_NT"
@@ -609,8 +619,21 @@ function Path:is_relative(to)
609619
end
610620
---@cast to plenary.Path2
611621

622+
if to == self then
623+
return true
624+
end
625+
626+
-- NOTE: could probably be optimized by letting _WindowsPath/_WindowsPath
627+
-- handle this.
628+
612629
local to_abs = to:absolute()
613-
return self:absolute():sub(1, #to_abs) == to_abs
630+
for parent in self:iter_parents() do
631+
if to_abs == parent then
632+
return true
633+
end
634+
end
635+
636+
return false
614637
end
615638

616639
--- makes a path relative to another (by default the cwd).
@@ -620,6 +643,11 @@ end
620643
---@param walk_up boolean? walk up to the provided path using '..' (default: `false`)
621644
---@return string
622645
function Path:make_relative(to, walk_up)
646+
-- NOTE: could probably take some shortcuts and avoid some `Path:new` calls
647+
-- by allowing _WindowsPath/_PosixPath handle this individually.
648+
-- As always, Windows root complicates things, so generating a new Path often
649+
-- easier/less error prone than manual string manipulate but at the cost of
650+
-- perf.
623651
walk_up = vim.F.if_nil(walk_up, false)
624652

625653
if to == nil then
@@ -649,7 +677,6 @@ function Path:make_relative(to, walk_up)
649677
local common_path
650678
for parent in to:iter_parents() do
651679
table.insert(steps, "..")
652-
print(parent, abs)
653680
if abs:sub(1, #parent) == parent then
654681
common_path = parent
655682
break
@@ -660,18 +687,9 @@ function Path:make_relative(to, walk_up)
660687
error(string.format("'%s' and '%s' have different anchors", self, to))
661688
end
662689

663-
local res_path = abs:sub(#common_path + 1)
664-
return table.concat(steps, self.sep) .. res_path
690+
local res_path = abs:sub(#common_path + 1):gsub("^" .. self.sep, "")
691+
table.insert(steps, res_path)
692+
return Path:new(steps).filename
665693
end
666694

667-
-- vim.o.shellslash = false
668-
669-
local root = "C:/"
670-
local p = Path:new(Path.path.root(vim.fn.getcwd()))
671-
vim.print("p parent", p.filename, p:parents(), p:parent().filename)
672-
-- local absolute = p:absolute()
673-
-- local relative = Path:new(absolute):make_relative(Path:new "C:/Windows", true)
674-
-- print(p.filename, absolute, relative)
675-
vim.o.shellslash = true
676-
677695
return Path

tests/plenary/path2_spec.lua

Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -307,32 +307,31 @@ describe("Path2", function()
307307
assert.are.same(p.filename, relative)
308308
end)
309309

310-
-- it_cross_plat("can take absolute paths and make them relative to the root directory", function()
311-
-- local p = Path:new { root, "prime", "aoeu", "agen.lua" }
312-
-- local absolute = root .. p.filename
313-
-- local relative = Path:new(absolute):make_relative(root)
314-
-- assert.are.same(p.filename, relative)
315-
-- end)
310+
it_cross_plat("can take absolute paths and make them relative to the root directory", function()
311+
local p = Path:new { root, "prime", "aoeu", "agen.lua" }
312+
local absolute = root .. p.filename
313+
local relative = Path:new(absolute):make_relative(root)
314+
assert.are.same(p.filename, relative)
315+
end)
316316

317317
it_cross_plat("can take absolute paths and make them relative to themselves", function()
318318
local p = Path:new { root, "home", "prime", "aoeu", "agen.lua" }
319319
local relative = Path:new(p.filename):make_relative(p.filename)
320320
assert.are.same(".", relative)
321321
end)
322322

323-
-- it_cross_plat("should not truncate if path separator is not present after cwd", function()
324-
-- local cwd = "tmp" .. path.sep .. "foo"
325-
-- local p = Path:new { "tmp", "foo_bar", "fileb.lua" }
326-
-- local relative = Path:new(p.filename):make_relative(cwd)
327-
-- assert.are.same(p.filename, relative)
328-
-- end)
329-
330-
-- it_cross_plat("should not truncate if path separator is not present after cwd and cwd ends in path sep", function()
331-
-- local cwd = "tmp" .. path.sep .. "foo" .. path.sep
332-
-- local p = Path:new { "tmp", "foo_bar", "fileb.lua" }
333-
-- local relative = Path:new(p.filename):make_relative(cwd)
334-
-- assert.are.same(p.filename, relative)
335-
-- end)
323+
it_cross_plat("should fail to make relative a path to somewhere not in the subpath", function()
324+
assert.has_error(function()
325+
_ = Path:new({ "tmp", "foo_bar", "fileb.lua" }):make_relative(Path:new { "tmp", "foo" })
326+
end)
327+
end)
328+
329+
it_cross_plat("can walk upwards out of current subpath", function()
330+
local p = Path:new { "foo", "bar", "baz" }
331+
local cwd = Path:new { "foo", "foo_inner" }
332+
local expect = Path:new { "..", "bar", "baz" }
333+
assert.are.same(expect.filename, p:make_relative(cwd, true))
334+
end)
336335
end)
337336

338337
describe("parents", function()

0 commit comments

Comments
 (0)