Skip to content

Commit 2de4173

Browse files
oscardssmithtimholypfitzsebKeno
authored
Tracking non-.jl source files (#680)
Co-authored-by: Tim Holy <[email protected]> Co-authored-by: Sebastian Pfitzner <[email protected]> Co-authored-by: Keno Fischer <[email protected]>
1 parent fe8d3b4 commit 2de4173

File tree

9 files changed

+90
-14
lines changed

9 files changed

+90
-14
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ UUIDs = "cf7118a7-6976-5b1a-9a39-7adc72f591a4"
1717
Unicode = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5"
1818

1919
[compat]
20-
CodeTracking = "1"
20+
CodeTracking = "1.1"
2121
JuliaInterpreter = "0.9"
2222
LoweredCodeUtils = "2.2"
2323
OrderedCollections = "1"

docs/src/dev_reference.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,3 +151,16 @@ Revise.git_repo
151151
```@docs
152152
Revise.init_worker
153153
```
154+
155+
## Teaching Revise about non-julia source codes
156+
Revise can be made to work for transpilers from non-Julia languages to Julia with a little effort.
157+
For example, if you wrote a transpiler from C to Julia, you can define a `struct CFile`
158+
which overrides enough of the common `String` methods (`abspath`,`isabspath`, `joinpath`, `normpath`,`isfile`,`findfirst`, and `String`),
159+
it will be supported by Revise if you define a method like
160+
```
161+
function Revise.parse_source!(mod_exprs_sigs::Revise.ModuleExprsSigs, file::CFile, mod::Module; kwargs...)
162+
ex = # julia Expr returned from running transpiler
163+
Revise.process_source!(mod_exprs_sigs, ex, file, mod; kwargs...)
164+
end
165+
166+
```

src/packagedef.jl

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -520,6 +520,7 @@ Use the `pkgdata` version if the files are supplied using relative paths.
520520
function init_watching(pkgdata::PkgData, files=srcfiles(pkgdata))
521521
udirs = Set{String}()
522522
for file in files
523+
file = String(file)::String
523524
dir, basename = splitdir(file)
524525
dirfull = joinpath(basedir(pkgdata), dir)
525526
already_watching_dir = haskey(watched_files, dirfull)
@@ -643,16 +644,19 @@ function handle_deletions(pkgdata, file)
643644
fi = maybe_parse_from_cache!(pkgdata, file)
644645
maybe_extract_sigs!(fi)
645646
mexsold = fi.modexsigs
646-
filep = normpath(joinpath(basedir(pkgdata), file))
647+
idx = fileindex(pkgdata, file)
648+
filep = pkgdata.info.files[idx]
649+
if isa(filep, AbstractString)
650+
filep = normpath(joinpath(basedir(pkgdata), file))
651+
end
647652
topmod = first(keys(mexsold))
648-
fileok = file_exists(filep)
653+
fileok = file_exists(String(filep)::String)
649654
mexsnew = fileok ? parse_source(filep, topmod) : ModuleExprsSigs(topmod)
650655
if mexsnew !== nothing
651656
delete_missing!(mexsold, mexsnew)
652657
end
653658
if !fileok
654659
@warn("$filep no longer exists, deleted all methods")
655-
idx = fileindex(pkgdata, file)
656660
deleteat!(pkgdata.fileinfos, idx)
657661
deleteat!(pkgdata.info.files, idx)
658662
wl = get(watched_files, basedir(pkgdata), nothing)
@@ -875,7 +879,7 @@ it defaults to `Main`.
875879
876880
If this produces many errors, check that you specified `mod` correctly.
877881
"""
878-
function track(mod::Module, file::AbstractString; mode=:sigs, kwargs...)
882+
function track(mod::Module, file; mode=:sigs, kwargs...)
879883
isfile(file) || error(file, " is not a file")
880884
# Determine whether we're already tracking this file
881885
id = Base.moduleroot(mod) == Main ? PkgId(mod, string(mod)) : PkgId(mod) # see #689 for `Main`
@@ -913,13 +917,13 @@ function track(mod::Module, file::AbstractString; mode=:sigs, kwargs...)
913917
CodeTracking._pkgfiles[id] = pkgdata.info
914918
end
915919
push!(pkgdata, relpath(file, pkgdata)=>FileInfo(fm))
916-
init_watching(pkgdata, (file,))
920+
init_watching(pkgdata, (String(file)::String,))
917921
pkgdatas[id] = pkgdata
918922
end
919923
return nothing
920924
end
921925

922-
function track(file::AbstractString; kwargs...)
926+
function track(file; kwargs...)
923927
startswith(file, juliadir) && error("use Revise.track(Base) or Revise.track(<stdlib module>)")
924928
track(Main, file; kwargs...)
925929
end
@@ -985,7 +989,7 @@ try fixing it with something like `push!(LOAD_PATH, "/path/to/my/private/repos")
985989
they will not be automatically tracked.
986990
(See [`Revise.track`](@ref) to set it up manually.)
987991
"""
988-
function includet(mod::Module, file::AbstractString)
992+
function includet(mod::Module, file)
989993
prev = Base.source_path(nothing)
990994
file = if prev === nothing
991995
abspath(file)
@@ -1017,7 +1021,7 @@ function includet(mod::Module, file::AbstractString)
10171021
end
10181022
return nothing
10191023
end
1020-
includet(file::AbstractString) = includet(Main, file)
1024+
includet(file) = includet(Main, file)
10211025

10221026
"""
10231027
Revise.silence(pkg)

src/parsing.jl

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ if `filename` defines more module(s) then these will all have separate entries i
77
88
If parsing `filename` fails, `nothing` is returned.
99
"""
10-
parse_source(filename::AbstractString, mod::Module; kwargs...) =
10+
parse_source(filename, mod::Module; kwargs...) =
1111
parse_source!(ModuleExprsSigs(mod), filename, mod; kwargs...)
1212

1313
"""
@@ -35,7 +35,7 @@ string. `pos` is the 1-based byte offset from which to begin parsing `src`.
3535
3636
See also [`Revise.parse_source`](@ref).
3737
"""
38-
function parse_source!(mod_exprs_sigs::ModuleExprsSigs, src::AbstractString, filename::AbstractString, mod::Module; mode::Symbol=:sigs)
38+
function parse_source!(mod_exprs_sigs::ModuleExprsSigs, src::AbstractString, filename::AbstractString, mod::Module; kwargs...)
3939
startswith(src, "# REVISE: DO NOT PARSE") && return nothing
4040
ex = Base.parse_input_line(src; filename=filename)
4141
ex === nothing && return mod_exprs_sigs
@@ -44,6 +44,10 @@ function parse_source!(mod_exprs_sigs::ModuleExprsSigs, src::AbstractString, fil
4444
ln = count(isequal('\n'), SubString(src, 1, min(pos, length(src)))) + 1
4545
throw(LoadError(filename, ln, ex.args[1]))
4646
end
47+
return process_source!(mod_exprs_sigs, ex, filename, mod; kwargs...)
48+
end
49+
50+
function process_source!(mod_exprs_sigs::ModuleExprsSigs, ex, filename, mod::Module; mode::Symbol=:sigs)
4751
for (mod, ex) in ExprSplitter(mod, ex)
4852
if mode === :includet
4953
try

src/types.jl

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -147,9 +147,11 @@ Base.PkgId(pkgdata::PkgData) = PkgId(pkgdata.info)
147147
CodeTracking.basedir(pkgdata::PkgData) = basedir(pkgdata.info)
148148
CodeTracking.srcfiles(pkgdata::PkgData) = srcfiles(pkgdata.info)
149149

150-
function fileindex(info, file::AbstractString)
150+
is_same_file(a, b) = String(a) == String(b)
151+
152+
function fileindex(info, file)
151153
for (i, f) in enumerate(srcfiles(info))
152-
f == file && return i
154+
is_same_file(f, file) && return i
153155
end
154156
return nothing
155157
end
@@ -168,7 +170,7 @@ function fileinfo(pkgdata::PkgData, file::AbstractString)
168170
end
169171
fileinfo(pkgdata::PkgData, i::Integer) = pkgdata.fileinfos[i]
170172

171-
function Base.push!(pkgdata::PkgData, pr::Pair{<:AbstractString,FileInfo})
173+
function Base.push!(pkgdata::PkgData, pr::Pair{<:Any,FileInfo})
172174
push!(srcfiles(pkgdata), pr.first)
173175
push!(pkgdata.fileinfos, pr.second)
174176
return pkgdata

test/fake_lang/new_test.program

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
2=x

test/fake_lang/test.program

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
1=x
2+
2=y

test/non_jl_test.jl

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
struct MyFile
2+
file::String
3+
end
4+
Base.abspath(file::MyFile) = MyFile(Base.abspath(file.file))
5+
Base.isabspath(file::MyFile) = Base.isabspath(file.file)
6+
Base.joinpath(str::String, file::MyFile) = MyFile(Base.joinpath(str, file.file))
7+
Base.normpath(file::MyFile) = MyFile(Base.normpath(file.file))
8+
Base.isfile(file::MyFile) = Base.isfile(file.file)
9+
Base.findfirst(str::String, file::MyFile) = Base.findfirst(str, file.file)
10+
Base.String(file::MyFile) = file.file
11+
12+
function make_module(file::MyFile)
13+
exprs = []
14+
for line in eachline(file.file)
15+
val, name = split(line, '=')
16+
push!(exprs, :(function $(Symbol(name))() $val end))
17+
end
18+
Expr(:toplevel, :(baremodule fake_lang
19+
$(exprs...)
20+
end), :(using .fake_lang))
21+
end
22+
23+
function Base.include(mod::Module, file::MyFile)
24+
Core.eval(mod, make_module(file))
25+
end
26+
Base.include(file::MyFile) = Base.include(Core.Main, file)
27+
28+
using Revise
29+
function Revise.parse_source!(mod_exprs_sigs::Revise.ModuleExprsSigs, file::MyFile, mod::Module; kwargs...)
30+
ex = make_module(file)
31+
Revise.process_source!(mod_exprs_sigs, ex, file, mod; kwargs...)
32+
end
33+
34+
path = joinpath(@__DIR__, "test.program")
35+
try
36+
cp(joinpath(@__DIR__, "fake_lang", "test.program"), path, force=true)
37+
m=MyFile(path)
38+
includet(m)
39+
Revise.revise()
40+
@test fake_lang.y() == "2"
41+
@test fake_lang.x() == "1"
42+
cp(joinpath(@__DIR__, "fake_lang", "new_test.program"), path, force=true)
43+
Revise.revise()
44+
@test fake_lang.x() == "2"
45+
@test_throws MethodError fake_lang.y()
46+
finally
47+
rm(path, force=true)
48+
end

test/runtests.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3670,3 +3670,5 @@ do_test("Base signatures") && @testset "Base signatures" begin
36703670
# Using the extensive repository of code in Base as a testbed
36713671
include("sigtest.jl")
36723672
end
3673+
3674+
include("non_jl_test.jl")

0 commit comments

Comments
 (0)