Skip to content

Commit aefa0f9

Browse files
committed
Merge pull request #10893 from peter1000/adds_function_relpath
adds_function_relpath, docs, tests
2 parents a49ae53 + 13302e6 commit aefa0f9

File tree

6 files changed

+132
-0
lines changed

6 files changed

+132
-0
lines changed

NEWS.md

+4
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,9 @@ Library improvements
274274
* New `withenv(var=>val, ...) do ... end` function to temporarily
275275
modify environment variables ([#10914]).
276276

277+
* New function `relpath` returns a relative filepath to path either from the current
278+
directory or from an optional start directory ([#10893]).
279+
277280
Deprecated or removed
278281
---------------------
279282

@@ -1375,4 +1378,5 @@ Too numerous to mention.
13751378
[#10844]: https://github.com/JuliaLang/julia/issues/10844
13761379
[#10870]: https://github.com/JuliaLang/julia/issues/10870
13771380
[#10885]: https://github.com/JuliaLang/julia/issues/10885
1381+
[#10893]: https://github.com/JuliaLang/julia/pull/10893
13781382
[#10914]: https://github.com/JuliaLang/julia/issues/10914

base/exports.jl

+1
Original file line numberDiff line numberDiff line change
@@ -1242,6 +1242,7 @@ export
12421242
joinpath,
12431243
normpath,
12441244
realpath,
1245+
relpath,
12451246
splitdir,
12461247
splitdrive,
12471248
splitext,

base/path.jl

+29
Original file line numberDiff line numberDiff line change
@@ -141,3 +141,32 @@ end
141141
if c == '/' return homedir()*path[i:end] end
142142
throw(ArgumentError("~user tilde expansion not yet implemented"))
143143
end
144+
145+
function relpath(path::AbstractString, startpath::AbstractString = ".")
146+
isempty(path) && throw(ArgumentError("`path` must be specified"))
147+
isempty(startpath) && throw(ArgumentError("`startpath` must be specified"))
148+
curdir = "."
149+
pardir = ".."
150+
path == startpath && return curdir
151+
path_arr = split(abspath(path), path_separator_re)
152+
start_arr = split(abspath(startpath), path_separator_re)
153+
i = 0
154+
while i < min(length(path_arr), length(start_arr))
155+
i += 1
156+
if path_arr[i] != start_arr[i]
157+
i -= 1
158+
break
159+
end
160+
end
161+
pathpart = join(path_arr[i+1:findlast(x -> !isempty(x), path_arr)], path_separator)
162+
prefix_num = findlast(x -> !isempty(x), start_arr) - i - 1
163+
if prefix_num >= 0
164+
prefix = pardir * path_separator
165+
relpath_ = isempty(pathpart) ?
166+
(prefix^prefix_num) * pardir :
167+
(prefix^prefix_num) * pardir * path_separator * pathpart
168+
else
169+
relpath_ = pathpart
170+
end
171+
return isempty(relpath_) ? curdir : relpath_
172+
end

doc/helpdb.jl

+9
Original file line numberDiff line numberDiff line change
@@ -5354,6 +5354,15 @@ Millisecond(v)
53545354
53555355
"),
53565356

5357+
("Base","relpath","relpath(path::AbstractString, startpath::AbstractString = ".") -> AbstractString
5358+
5359+
Return a relative filepath to path either from the current directory or from an optional
5360+
start directory.
5361+
This is a path computation: the filesystem is not accessed to confirm the existence or
5362+
nature of path or startpath.
5363+
5364+
"),
5365+
53575366
("Base","expanduser","expanduser(path::AbstractString) -> AbstractString
53585367
53595368
On Unix systems, replace a tilde character at the start of a path

doc/stdlib/file.rst

+7
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,13 @@
254254

255255
Canonicalize a path by expanding symbolic links and removing "." and ".." entries.
256256

257+
.. function:: relpath(path::AbstractString, startpath::AbstractString = ".") -> AbstractString
258+
259+
Return a relative filepath to path either from the current directory or from an optional
260+
start directory.
261+
This is a path computation: the filesystem is not accessed to confirm the existence or
262+
nature of path or startpath.
263+
257264
.. function:: expanduser(path::AbstractString) -> AbstractString
258265

259266
On Unix systems, replace a tilde character at the start of a path with the

test/path.jl

+82
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,85 @@
33
@unix_only @test isabspath("/") == true
44
@test isabspath("~") == false
55
@unix_only @test isabspath(expanduser("~")) == true
6+
7+
############################################
8+
# This section tests relpath computation. #
9+
###########################################
10+
function test_relpath()
11+
sep = Base.path_separator
12+
filepaths = [
13+
"$(sep)home$(sep)user$(sep).julia$(sep)Test1$(sep)docs$(sep)api$(sep)Test1.md",
14+
"$(sep)home$(sep)user$(sep).julia$(sep)Test1$(sep)docs$(sep)api$(sep)lib$(sep)file1.md",
15+
"$(sep)home$(sep)user$(sep).julia$(sep)测试2$(sep)docs$(sep)api$(sep)测试2.md",
16+
"$(sep)home$(sep)user$(sep)dir_withendsep$(sep)",
17+
"$(sep)home$(sep)dir2_withendsep$(sep)",
18+
"$(sep)home$(sep)test.md",
19+
"$(sep)home",
20+
# Special cases
21+
"$(sep)",
22+
"$(sep)home$(sep)$(sep)$(sep)"
23+
]
24+
startpaths = [
25+
"$(sep)home$(sep)user$(sep).julia$(sep)Test1$(sep)docs$(sep)api$(sep)genindex.md",
26+
"$(sep)multi_docs$(sep)genindex.md",
27+
"$(sep)home$(sep)user$(sep)dir_withendsep$(sep)",
28+
"$(sep)home$(sep)dir2_withendsep$(sep)",
29+
"$(sep)home$(sep)test.md",
30+
"$(sep)home",
31+
# Special cases
32+
"$(sep)",
33+
"$(sep)home$(sep)$(sep)$(sep)"
34+
]
35+
relpath_expected_results = [
36+
"..$(sep)Test1.md",
37+
"..$(sep)..$(sep)home$(sep)user$(sep).julia$(sep)Test1$(sep)docs$(sep)api$(sep)Test1.md",
38+
"..$(sep).julia$(sep)Test1$(sep)docs$(sep)api$(sep)Test1.md",
39+
"..$(sep)user$(sep).julia$(sep)Test1$(sep)docs$(sep)api$(sep)Test1.md",
40+
"..$(sep)user$(sep).julia$(sep)Test1$(sep)docs$(sep)api$(sep)Test1.md",
41+
"user$(sep).julia$(sep)Test1$(sep)docs$(sep)api$(sep)Test1.md",
42+
"home$(sep)user$(sep).julia$(sep)Test1$(sep)docs$(sep)api$(sep)Test1.md",
43+
"user$(sep).julia$(sep)Test1$(sep)docs$(sep)api$(sep)Test1.md",
44+
"..$(sep)lib$(sep)file1.md",
45+
"..$(sep)..$(sep)home$(sep)user$(sep).julia$(sep)Test1$(sep)docs$(sep)api$(sep)lib$(sep)file1.md",
46+
"..$(sep).julia$(sep)Test1$(sep)docs$(sep)api$(sep)lib$(sep)file1.md",
47+
"..$(sep)user$(sep).julia$(sep)Test1$(sep)docs$(sep)api$(sep)lib$(sep)file1.md",
48+
"..$(sep)user$(sep).julia$(sep)Test1$(sep)docs$(sep)api$(sep)lib$(sep)file1.md",
49+
"user$(sep).julia$(sep)Test1$(sep)docs$(sep)api$(sep)lib$(sep)file1.md",
50+
"home$(sep)user$(sep).julia$(sep)Test1$(sep)docs$(sep)api$(sep)lib$(sep)file1.md",
51+
"user$(sep).julia$(sep)Test1$(sep)docs$(sep)api$(sep)lib$(sep)file1.md",
52+
"..$(sep)..$(sep)..$(sep)..$(sep)测试2$(sep)docs$(sep)api$(sep)测试2.md",
53+
"..$(sep)..$(sep)home$(sep)user$(sep).julia$(sep)测试2$(sep)docs$(sep)api$(sep)测试2.md",
54+
"..$(sep).julia$(sep)测试2$(sep)docs$(sep)api$(sep)测试2.md",
55+
"..$(sep)user$(sep).julia$(sep)测试2$(sep)docs$(sep)api$(sep)测试2.md",
56+
"..$(sep)user$(sep).julia$(sep)测试2$(sep)docs$(sep)api$(sep)测试2.md",
57+
"user$(sep).julia$(sep)测试2$(sep)docs$(sep)api$(sep)测试2.md",
58+
"home$(sep)user$(sep).julia$(sep)测试2$(sep)docs$(sep)api$(sep)测试2.md",
59+
"user$(sep).julia$(sep)测试2$(sep)docs$(sep)api$(sep)测试2.md",
60+
"..$(sep)..$(sep)..$(sep)..$(sep)..$(sep)dir_withendsep",
61+
"..$(sep)..$(sep)home$(sep)user$(sep)dir_withendsep",".","..$(sep)user$(sep)dir_withendsep",
62+
"..$(sep)user$(sep)dir_withendsep","user$(sep)dir_withendsep",
63+
"home$(sep)user$(sep)dir_withendsep","user$(sep)dir_withendsep",
64+
"..$(sep)..$(sep)..$(sep)..$(sep)..$(sep)..$(sep)dir2_withendsep",
65+
"..$(sep)..$(sep)home$(sep)dir2_withendsep","..$(sep)..$(sep)dir2_withendsep",".",
66+
"..$(sep)dir2_withendsep","dir2_withendsep","home$(sep)dir2_withendsep","dir2_withendsep",
67+
"..$(sep)..$(sep)..$(sep)..$(sep)..$(sep)..$(sep)test.md","..$(sep)..$(sep)home$(sep)test.md",
68+
"..$(sep)..$(sep)test.md","..$(sep)test.md",".","test.md","home$(sep)test.md","test.md",
69+
"..$(sep)..$(sep)..$(sep)..$(sep)..$(sep)..","..$(sep)..$(sep)home","..$(sep)..",
70+
"..","..",".","home",".","..$(sep)..$(sep)..$(sep)..$(sep)..$(sep)..$(sep)..","..$(sep)..",
71+
"..$(sep)..$(sep)..","..$(sep)..","..$(sep)..","..",".","..",
72+
"..$(sep)..$(sep)..$(sep)..$(sep)..$(sep)..","..$(sep)..$(sep)home","..$(sep)..",
73+
"..","..",".","home","."
74+
]
75+
idx = 0
76+
for filep in filepaths
77+
for startp in startpaths
78+
res = relpath(filep, startp)
79+
idx += 1
80+
@test res == relpath_expected_results[idx]
81+
end
82+
end
83+
# Additional cases
84+
@test_throws ArgumentError relpath("$(sep)home$(sep)user$(sep)dir_withendsep$(sep)", "")
85+
@test_throws ArgumentError relpath("", "$(sep)home$(sep)user$(sep)dir_withendsep$(sep)")
86+
end
87+
test_relpath()

0 commit comments

Comments
 (0)