Skip to content

Commit 6afd36c

Browse files
committed
Add MapExprFile
This is aimed at supporting `include(mapexpr, "filename.jl")` in Revise.
1 parent 8e1ca1e commit 6afd36c

File tree

2 files changed

+77
-2
lines changed

2 files changed

+77
-2
lines changed

src/pkgfiles.jl

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,57 @@
1+
"""
2+
`MapExprFile(mapexpr, filename::String)` stores the arguments needed for
3+
`include(mapexpr, filename)`. `mapexpr` is a function mapping `Expr` to an
4+
`Expr`, which is applied to the parsed expressions from `filename` before
5+
evaluation. This is sometimes used for preprocessing files before they are
6+
loaded.
7+
8+
Otherwise, `MapExprFile` behaves like a string, allowing it to be used
9+
wherever a file path is expected.
10+
"""
11+
struct MapExprFile <: AbstractString
12+
mapexpr
13+
filename::String
14+
end
15+
MapExprFile(filename::String) = MapExprFile(identity, filename)
16+
17+
Base.show(io::IO, mapfile::MapExprFile) =
18+
print(io, "MapExprFile(", mapfile.mapexpr, ", \"", mapfile.filename, "\")")
19+
20+
# AbstractString interface
21+
Base.iterate(mapfile::MapExprFile) = iterate(mapfile.filename)
22+
Base.iterate(mapfile::MapExprFile, state::Integer) = iterate(mapfile.filename, state)
23+
24+
Base.getindex(mapfile::MapExprFile, i::Integer) = getindex(mapfile.filename, i)
25+
26+
Base.ncodeunits(mapfile::MapExprFile) = ncodeunits(mapfile.filename)
27+
Base.codeunit(mapfile::MapExprFile, i::Integer) = codeunit(mapfile.filename, i)
28+
29+
Base.:(==)(mapfile1::MapExprFile, mapfile2::MapExprFile) =
30+
(mapfile1.mapexpr == mapfile2.mapexpr) & (mapfile1.filename == mapfile2.filename)
31+
Base.:(==)(mapfile1::MapExprFile, file2::AbstractString) = false
32+
Base.:(==)(file1::AbstractString, mapfile2::MapExprFile) = false
33+
34+
# Don't lose the `mapexpr` from common path operations
35+
Base.:(*)(mapfile::MapExprFile, path::AbstractString) =
36+
MapExprFile(mapfile.mapexpr, mapfile.filename * path)
37+
Base.:(*)(path::AbstractString, mapfile::MapExprFile) =
38+
MapExprFile(mapfile.mapexpr, path * mapfile.filename)
39+
# The above would be enough for `joinpath` except for its return-type assertion ::String
40+
function Base.joinpath(mapfile::MapExprFile, path::AbstractString)
41+
@assert !isa(path, MapExprFile) "Cannot join MapExprFile with another MapExprFile"
42+
return MapExprFile(mapfile.mapexpr, joinpath(mapfile.filename, path))
43+
end
44+
function Base.joinpath(path::AbstractString, mapfile::MapExprFile)
45+
@assert !isa(path, MapExprFile) "Cannot join MapExprFile with another MapExprFile"
46+
return MapExprFile(mapfile.mapexpr, joinpath(path, mapfile.filename))
47+
end
48+
Base.normpath(mapfile::MapExprFile) = MapExprFile(mapfile.mapexpr, normpath(mapfile.filename))
49+
Base.abspath(mapfile::MapExprFile) = MapExprFile(mapfile.mapexpr, abspath(mapfile.filename))
50+
function Base.relpath(mapfile::MapExprFile, path::AbstractString)
51+
@assert !isa(path, MapExprFile) "Cannot get relative path from MapExprFile to another MapExprFile"
52+
return MapExprFile(mapfile.mapexpr, relpath(mapfile.filename, path))
53+
end
54+
155
"""
256
PkgFiles encodes information about the current location of a package.
357
Fields:
@@ -10,7 +64,7 @@ Note that `basedir` may be subsequently updated by Pkg operations such as `add`
1064
mutable struct PkgFiles
1165
id::PkgId
1266
basedir::String
13-
files::Vector{Any}
67+
files::Vector{Any} # might contain `filename::String`, `::MapExprFile`, or custom file types (https://github.com/timholy/Revise.jl/pull/680)
1468
end
1569

1670
PkgFiles(id::PkgId, path::AbstractString) = PkgFiles(id, path, Any[])

test/runtests.jl

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ using CodeTracking
44
using Test, InteractiveUtils, REPL, LinearAlgebra, SparseArrays
55
# Note: ColorTypes needs to be installed, but note the intentional absence of `using ColorTypes`
66

7-
using CodeTracking: line_is_decl, MethodInfoKey
7+
using CodeTracking: line_is_decl, MethodInfoKey, MapExprFile
88

99
if !isempty(ARGS) && "revise" ARGS
1010
# For running tests with and without Revise
@@ -294,6 +294,27 @@ isdefined(Main, :Revise) ? Main.Revise.includet("script.jl") : include("script.j
294294
@test line == 148
295295
end
296296

297+
@testset "MapExprFile" begin
298+
mapfile = MapExprFile(identity, "testfile.jl")
299+
@test String(mapfile) == "testfile.jl"
300+
@test sprint(show, mapfile) == "MapExprFile(identity, \"testfile.jl\")"
301+
mappath = joinpath("base", mapfile)
302+
@test mappath == MapExprFile(identity, joinpath("base", "testfile.jl"))
303+
@test abspath(mapfile) == MapExprFile(identity, abspath("testfile.jl"))
304+
@test normpath(mapfile) == MapExprFile(identity, normpath("testfile.jl"))
305+
@test relpath(mappath, "base") == mapfile
306+
@test isabspath(mapfile) == false
307+
@test ispath(mapfile) == false
308+
@test isfile(mapfile) == false
309+
open(mapfile, "w") do io
310+
write(io, "test content")
311+
end
312+
@test isabspath(mapfile) == false
313+
@test ispath(mapfile) == true
314+
@test isfile(mapfile) == true
315+
rm(mapfile)
316+
end
317+
297318
@testset "With Revise" begin
298319
if isdefined(Main, :Revise)
299320
m = @which gcd(10, 20)

0 commit comments

Comments
 (0)