diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cafa2de..cb64cdc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -47,9 +47,13 @@ jobs: run: julia -e 'using Pkg; Pkg.develop(PackageSpec(path=".")); Pkg.add(PackageSpec(url="https://github.com/timholy/Revise.jl")); Pkg.test("Revise")' - name: Test while running Revise if: ${{ matrix.os == 'ubuntu-latest' && matrix.version != '1.0' }} - run: julia --project -e 'using Pkg; - Pkg.add("Revise"); - Pkg.test("CodeTracking"; coverage=true, test_args=["revise"])' + run: TERM="xterm" julia --project -i --code-coverage -e 'using InteractiveUtils, REPL, Revise, Pkg; + Pkg.add("ColorTypes"); + @async(Base.run_main_repl(true, true, false, true, false)); + sleep(2); + cd("test"); + include("runtests.jl"); + REPL.eval_user_input(:(exit()), Base.active_repl_backend)' - uses: julia-actions/julia-processcoverage@v1 - uses: codecov/codecov-action@v1 with: diff --git a/Project.toml b/Project.toml index db182a8..3517470 100644 --- a/Project.toml +++ b/Project.toml @@ -13,8 +13,9 @@ julia = "1" [extras] ColorTypes = "3da002f7-5984-5a60-b8a6-cbb66c0b333f" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" +REPL = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [targets] -test = ["ColorTypes", "LinearAlgebra", "SparseArrays", "Test"] +test = ["ColorTypes", "LinearAlgebra", "REPL", "SparseArrays", "Test"] diff --git a/README.md b/README.md index 16f56c1..8711314 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,8 @@ # CodeTracking +[![Build status](https://github.com/timholy/CodeTracking.jl/actions/workflows/ci.yml/badge.svg)](https://github.com/timholy/CodeTracking.jl/actions/workflows/ci.yml) +[![Coverage](https://codecov.io/gh/timholy/CodeTracking.jl/branch/master/graph/badge.svg?token=bBzCYyj19O)](https://codecov.io/gh/timholy/CodeTracking.jl) + CodeTracking can be thought of as an extension of Julia's [InteractiveUtils library](https://docs.julialang.org/en/latest/stdlib/InteractiveUtils/). It provides an interface for obtaining: diff --git a/src/CodeTracking.jl b/src/CodeTracking.jl index c561667..d1a6f42 100644 --- a/src/CodeTracking.jl +++ b/src/CodeTracking.jl @@ -1,3 +1,13 @@ +""" +CodeTracking can be thought of as an extension of InteractiveUtils, and pairs well with Revise.jl. + +- `code_string`, `@code_string`: fetch the source code (as a string) for a method definition +- `code_expr`, `@code_expr`: fetch the expression for a method definition +- `definition`: a lower-level variant of the above +- `pkgfiles`: return information about the source files that define a package +- `whereis`: Return location information about methods (with Revise, it updates as you edit files) +- `signatures_at`: return the signatures of all methods whose definition spans the specified location +""" module CodeTracking using Base: PkgId @@ -159,7 +169,7 @@ function signatures_at(filename::AbstractString, line::Integer) end end end - error("$filename not found in internal data, perhaps the package is not loaded (or not loaded with `includet`)") + throw(ArgumentError("$filename not found in internal data, perhaps the package is not loaded (or not loaded with `includet`)")) end """ diff --git a/src/utils.jl b/src/utils.jl index 8a8a367..9dad520 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -1,3 +1,5 @@ +# This should stay as the first method because it's used in a test +# (or change the test) function checkname(fdef::Expr, name) fproto = fdef.args[1] fdef.head === :where && return checkname(fproto, name) diff --git a/test/runtests.jl b/test/runtests.jl index fbe1fe1..ee5756d 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,7 +1,7 @@ # Note: some of CodeTracking's functionality can only be tested by Revise using CodeTracking -using Test, InteractiveUtils, LinearAlgebra, SparseArrays +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 @@ -67,6 +67,10 @@ isdefined(Main, :Revise) ? Main.Revise.includet("script.jl") : include("script.j show(io, info) str = String(take!(io)) @test startswith(str, "PkgFiles(CodeTracking [da1fd8a2-8d9e-5ec2-8556-3022fb5608a2]):\n basedir:") + ioctx = IOContext(io, :compact=>true) + show(ioctx, info) + str = String(take!(io)) + @test match(r"PkgFiles\(CodeTracking, .*CodeTracking(\.jl)?, String\[\]\)", str) !== nothing @test pkgfiles("ColorTypes") === nothing @test_throws ErrorException pkgfiles("NotAPkg") @@ -86,17 +90,6 @@ isdefined(Main, :Revise) ? Main.Revise.includet("script.jl") : include("script.j oldlookup = CodeTracking.method_lookup_callback[] CodeTracking.method_lookup_callback[] = m -> error("oops") @test whereis(m) == ("REPL[1]", 1) - # Test with definition(String, m) - if isdefined(Base, :active_repl) - hp = Base.active_repl.interface.modes[1].hist - fstr = "__fREPL__(x::Int16) = 0" - histidx = length(hp.history) + 1 - hp.start_idx - ex = Base.parse_input_line(fstr; filename="REPL[$histidx]") - f = Core.eval(Main, ex) - push!(hp.history, fstr) - @test definition(String, first(methods(f))) == (fstr, 1) - pop!(hp.history) - end CodeTracking.method_lookup_callback[] = oldlookup m = first(methods(Test.eval)) @@ -109,6 +102,10 @@ isdefined(Main, :Revise) ? Main.Revise.includet("script.jl") : include("script.j end m = first(methods(f150)) src = Base.uncompressed_ast(m) + idx = findfirst(lin -> String(lin.file) == @__FILE__, src.linetable) + lin = src.linetable[idx] + file, line = whereis(lin, m) + @test endswith(file, String(lin.file)) idx = findfirst(lin -> String(lin.file) != @__FILE__, src.linetable) lin = src.linetable[idx] file, line = whereis(lin, m) @@ -167,6 +164,35 @@ end # issue #23 @test !isempty(signatures_at("script.jl", 9)) + + @test_throws ArgumentError signatures_at("nofile.jl", 1) + + if isdefined(Revise, :add_revise_deps) + Revise.add_revise_deps() + sigs = signatures_at(CodeTracking, "src/utils.jl", 5) + @test length(sigs) == 1 # only isn't available on julia 1.0 + @test first(sigs) == Tuple{typeof(CodeTracking.checkname), Expr, Any} + @test pkgfiles(CodeTracking).id == Base.PkgId(CodeTracking) + end + + # REPL (test copied from Revise) + if isdefined(Base, :active_repl) + hp = Base.active_repl.interface.modes[1].hist + fstr = "__fREPL__(x::Int16) = 0" + histidx = length(hp.history) + 1 - hp.start_idx + ex = Base.parse_input_line(fstr; filename="REPL[$histidx]") + f = Core.eval(Main, ex) + if ex.head === :toplevel + ex = ex.args[end] + end + push!(hp.history, fstr) + m = first(methods(f)) + @test definition(String, first(methods(f))) == (fstr, 1) + @test !isempty(signatures_at(String(m.file), m.line)) + pop!(hp.history) + elseif haskey(ENV, "CI") + error("CI Revise tests must be run with -i") + end end end