Skip to content

Commit 52e65c8

Browse files
committed
Add filesystem func to transform a path to a URI
In a few places across Base and the stdlib, we emit paths that we like people to be able to click on in their terminal and editor. Up to this point, we have relied on auto-filepath detection, but this does not allow for alternative link text, such as contracted paths. Doing so (via OSC 8 terminal links for example) requires filepath URI encoding. This functionality was previously part of a PR modifying stacktrace printing, but after that became held up for unrelated reasons and another PR appeared that would benefit from this utility, I've split out this functionality so it can be used before the stacktrace printing PR is resolved.
1 parent 7e809b0 commit 52e65c8

File tree

2 files changed

+39
-0
lines changed

2 files changed

+39
-0
lines changed

base/path.jl

+27
Original file line numberDiff line numberDiff line change
@@ -613,3 +613,30 @@ relpath(path::AbstractString, startpath::AbstractString) =
613613
for f in (:isdirpath, :splitdir, :splitdrive, :splitext, :normpath, :abspath)
614614
@eval $f(path::AbstractString) = $f(String(path))
615615
end
616+
617+
"""
618+
uripath(path::AbstractString)
619+
620+
Encode `path` as a URI as per [RFC1738](https://www.rfc-editor.org/rfc/rfc1738),
621+
[RFC3986](https://www.rfc-editor.org/rfc/rfc3986), and the [Freedesktop File URI
622+
spec](https://www.freedesktop.org/wiki/Specifications/file-uri-spec/).
623+
624+
## Examples
625+
626+
```julia-repl
627+
julia> uripath("/home/user/example file.jl")
628+
"file://<hostname>/home/user/example%20file.jl"
629+
```
630+
"""
631+
function uripath(path::String)
632+
percent_escape(s) =
633+
'%' * join(map(b -> string(b, base=16), codeunits(s)), '%')
634+
encode_uri_component(s) =
635+
replace(s, r"[^A-Za-z0-9\-_.~]+" => percent_escape)
636+
string("file://", gethostname(), '/',
637+
join(map(encode_uri_component,
638+
split(abspath(path), path_separator_re, keepempty=false)),
639+
'/'))
640+
end
641+
642+
uripath(path::AbstractString) = uripath(String(path))

test/path.jl

+12
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,18 @@
311311
test_relpath()
312312
end
313313

314+
@testset "uripath" begin
315+
host = gethostname()
316+
drive, absdrive = if Sys.iswindows() "C:$sep", "C:$sep" else "", "$sep" end
317+
@test Base.Filesystem.uripath("$(absdrive)some$(sep)file.txt") == "file://$host/$(drive)some/file.txt"
318+
@test Base.Filesystem.uripath("$(absdrive)another$(sep)$(sep)folder$(sep)file.md") == "file://$host/$(drive)another/folder/file.md"
319+
@test Base.Filesystem.uripath("$(absdrive)some file with ^odd% chars") == "file://$host/$(drive)some%20file%20with%20%5eodd%25%20chars"
320+
@test Base.Filesystem.uripath("$(absdrive)weird chars like @#&()[]{}") == "file://$host/$(drive)weird%20chars%20like%20%40%23%26%28%29%5b%5d%7b%7d"
321+
@test Base.Filesystem.uripath("$absdrive") == "file://$host/$drive"
322+
@test Base.Filesystem.uripath(".") == Base.Filesystem.uripath(pwd())
323+
@test Base.Filesystem.uripath("$(absdrive)unicode$(sep)Δεδομένα") == "file://$host/$(drive)unicode/%ce%94%ce%b5%ce%b4%ce%bf%ce%bc%ce%ad%ce%bd%ce%b1"
324+
end
325+
314326
if Sys.iswindows()
315327
@testset "issue #23646" begin
316328
@test lowercase(relpath("E:\\a\\b", "C:\\c")) == "e:\\a\\b"

0 commit comments

Comments
 (0)