Skip to content

Commit de5acdc

Browse files
authored
fix: normalizing a relative path returns an absolute path (#251)
* path: fix for normalizing files including ... This still doesn't work for paths containing both .. parts as well as ... * Use path.sep instead of hard coded / * Change tests to check relative paths * Add check for absolute path * Linting fixes * Update _normalize_path to check initial .. parts This should return an absolute path if the initial .. count matches the number of parts in the cwd (Path:_cwd)
1 parent 901b96d commit de5acdc

File tree

2 files changed

+49
-11
lines changed

2 files changed

+49
-11
lines changed

lua/plenary/path.lua

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -76,29 +76,50 @@ local is_uri = function(filename)
7676
return string.match(filename, "^%w+://") ~= nil
7777
end
7878

79-
local function _normalize_path(filename)
79+
local is_absolute = function(filename, sep)
80+
if sep == "\\" then
81+
return string.match(filename, "^[A-Z]:\\.*$")
82+
end
83+
return string.sub(filename, 1, 1) == sep
84+
end
85+
86+
local function _normalize_path(filename, cwd)
8087
if is_uri(filename) then
8188
return filename
8289
end
8390

8491
local out_file = filename
8592

86-
local has = string.find(filename, "..", 1, true)
93+
local has = string.find(filename, path.sep .. "..", 1, true) or string.find(filename, ".." .. path.sep, 1, true)
8794

8895
if has then
8996
local parts = _split_by_separator(filename)
9097

9198
local idx = 1
99+
local initial_up_count = 0
100+
92101
repeat
93102
if parts[idx] == ".." then
103+
if idx == 1 then
104+
initial_up_count = initial_up_count + 1
105+
end
94106
table.remove(parts, idx)
95107
table.remove(parts, idx - 1)
96-
idx = idx - 2
108+
if idx > 1 then
109+
idx = idx - 2
110+
else
111+
idx = idx - 1
112+
end
97113
end
98114
idx = idx + 1
99115
until idx > #parts
100116

101-
out_file = path.root(filename) .. table.concat(parts, path.sep)
117+
local prefix = ""
118+
if is_absolute(filename, path.sep) or #_split_by_separator(cwd) == initial_up_count then
119+
prefix = path.root(filename)
120+
end
121+
122+
out_file = prefix .. table.concat(parts, path.sep)
102123
end
103124

104125
return out_file
@@ -251,9 +272,9 @@ end
251272

252273
function Path:absolute()
253274
if self:is_absolute() then
254-
return _normalize_path(self.filename)
275+
return _normalize_path(self.filename, self._cwd)
255276
else
256-
return _normalize_path(self._absolute or table.concat({ self._cwd, self.filename }, self._sep))
277+
return _normalize_path(self._absolute or table.concat({ self._cwd, self.filename }, self._sep), self._cwd)
257278
end
258279
end
259280

@@ -321,7 +342,7 @@ function Path:normalize(cwd)
321342
-- Substitute home directory w/ "~"
322343
self.filename = self.filename:gsub("^" .. path.home, "~", 1)
323344

324-
return _normalize_path(self.filename)
345+
return _normalize_path(self.filename, self._cwd)
325346
end
326347

327348
local function shorten_len(filename, len)
@@ -532,10 +553,7 @@ function Path:is_dir()
532553
end
533554

534555
function Path:is_absolute()
535-
if self._sep == "\\" then
536-
return string.match(self.filename, "^[A-Z]:\\.*$")
537-
end
538-
return string.sub(self.filename, 1, 1) == self._sep
556+
return is_absolute(self.filename, self._sep)
539557
end
540558
-- }}}
541559

tests/plenary/path_spec.lua

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,26 @@ describe("Path", function()
185185
local final = Path:new(orig):normalize()
186186
assert.are.same(final, "/lua/plenary/path.lua")
187187
end)
188+
189+
it("can normalize relative paths", function()
190+
assert.are.same(Path:new("lua/plenary/path.lua"):normalize(), "lua/plenary/path.lua")
191+
end)
192+
193+
it("can normalize relative paths containing ..", function()
194+
assert.are.same(Path:new("lua/plenary/path.lua/../path.lua"):normalize(), "lua/plenary/path.lua")
195+
end)
196+
197+
it("can normalize relative paths with initial ..", function()
198+
local p = Path:new "../lua/plenary/path.lua"
199+
p._cwd = "/tmp/lua"
200+
assert.are.same("lua/plenary/path.lua", p:normalize())
201+
end)
202+
203+
it("can normalize relative paths to absolute when initial .. count matches cwd parts", function()
204+
local p = Path:new "../../tmp/lua/plenary/path.lua"
205+
p._cwd = "/tmp/lua"
206+
assert.are.same("/tmp/lua/plenary/path.lua", p:normalize())
207+
end)
188208
end)
189209

190210
describe(":shorten", function()

0 commit comments

Comments
 (0)