diff --git a/src/pkgfiles.jl b/src/pkgfiles.jl index 4854066..690f5e8 100644 --- a/src/pkgfiles.jl +++ b/src/pkgfiles.jl @@ -1,3 +1,57 @@ +""" +`MapExprFile(mapexpr, filename::String)` stores the arguments needed for +`include(mapexpr, filename)`. `mapexpr` is a function mapping `Expr` to an +`Expr`, which is applied to the parsed expressions from `filename` before +evaluation. This is sometimes used for preprocessing files before they are +loaded. + +Otherwise, `MapExprFile` behaves like a string, allowing it to be used +wherever a file path is expected. +""" +struct MapExprFile <: AbstractString + mapexpr + filename::String +end +MapExprFile(filename::String) = MapExprFile(identity, filename) + +Base.show(io::IO, mapfile::MapExprFile) = + print(io, "MapExprFile(", mapfile.mapexpr, ", \"", mapfile.filename, "\")") + +# AbstractString interface +Base.iterate(mapfile::MapExprFile) = iterate(mapfile.filename) +Base.iterate(mapfile::MapExprFile, state::Integer) = iterate(mapfile.filename, state) + +Base.getindex(mapfile::MapExprFile, i::Integer) = getindex(mapfile.filename, i) + +Base.ncodeunits(mapfile::MapExprFile) = ncodeunits(mapfile.filename) +Base.codeunit(mapfile::MapExprFile, i::Integer) = codeunit(mapfile.filename, i) + +Base.:(==)(mapfile1::MapExprFile, mapfile2::MapExprFile) = + (mapfile1.mapexpr == mapfile2.mapexpr) & (mapfile1.filename == mapfile2.filename) +Base.:(==)(mapfile1::MapExprFile, file2::AbstractString) = false +Base.:(==)(file1::AbstractString, mapfile2::MapExprFile) = false + +# Don't lose the `mapexpr` from common path operations +Base.:(*)(mapfile::MapExprFile, path::AbstractString) = + MapExprFile(mapfile.mapexpr, mapfile.filename * path) +Base.:(*)(path::AbstractString, mapfile::MapExprFile) = + MapExprFile(mapfile.mapexpr, path * mapfile.filename) +# The above would be enough for `joinpath` except for its return-type assertion ::String +function Base.joinpath(mapfile::MapExprFile, path::AbstractString) + @assert !isa(path, MapExprFile) "Cannot join MapExprFile with another MapExprFile" + return MapExprFile(mapfile.mapexpr, joinpath(mapfile.filename, path)) +end +function Base.joinpath(path::AbstractString, mapfile::MapExprFile) + @assert !isa(path, MapExprFile) "Cannot join MapExprFile with another MapExprFile" + return MapExprFile(mapfile.mapexpr, joinpath(path, mapfile.filename)) +end +Base.normpath(mapfile::MapExprFile) = MapExprFile(mapfile.mapexpr, normpath(mapfile.filename)) +Base.abspath(mapfile::MapExprFile) = MapExprFile(mapfile.mapexpr, abspath(mapfile.filename)) +function Base.relpath(mapfile::MapExprFile, path::AbstractString) + @assert !isa(path, MapExprFile) "Cannot get relative path from MapExprFile to another MapExprFile" + return MapExprFile(mapfile.mapexpr, relpath(mapfile.filename, path)) +end + """ PkgFiles encodes information about the current location of a package. Fields: @@ -10,7 +64,7 @@ Note that `basedir` may be subsequently updated by Pkg operations such as `add` mutable struct PkgFiles id::PkgId basedir::String - files::Vector{Any} + files::Vector{Any} # might contain `filename::String`, `::MapExprFile`, or custom file types (https://github.com/timholy/Revise.jl/pull/680) end PkgFiles(id::PkgId, path::AbstractString) = PkgFiles(id, path, Any[]) diff --git a/test/runtests.jl b/test/runtests.jl index d3d3d04..d3adf5e 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -4,7 +4,7 @@ using CodeTracking using Test, InteractiveUtils, REPL, LinearAlgebra, SparseArrays # Note: ColorTypes needs to be installed, but note the intentional absence of `using ColorTypes` -using CodeTracking: line_is_decl, MethodInfoKey +using CodeTracking: line_is_decl, MethodInfoKey, MapExprFile if !isempty(ARGS) && "revise" ∈ ARGS # For running tests with and without Revise @@ -294,6 +294,27 @@ isdefined(Main, :Revise) ? Main.Revise.includet("script.jl") : include("script.j @test line == 148 end +@testset "MapExprFile" begin + mapfile = MapExprFile(identity, "testfile.jl") + @test String(mapfile) == "testfile.jl" + @test sprint(show, mapfile) == "MapExprFile(identity, \"testfile.jl\")" + mappath = joinpath("base", mapfile) + @test mappath == MapExprFile(identity, joinpath("base", "testfile.jl")) + @test abspath(mapfile) == MapExprFile(identity, abspath("testfile.jl")) + @test normpath(mapfile) == MapExprFile(identity, normpath("testfile.jl")) + @test relpath(mappath, "base") == mapfile + @test isabspath(mapfile) == false + @test ispath(mapfile) == false + @test isfile(mapfile) == false + open(mapfile, "w") do io + write(io, "test content") + end + @test isabspath(mapfile) == false + @test ispath(mapfile) == true + @test isfile(mapfile) == true + rm(mapfile) +end + @testset "With Revise" begin if isdefined(Main, :Revise) m = @which gcd(10, 20)