Developer reference
Internal global variables
Configuration-related variables
These are set during execution of Revise's __init__
function.
Revise.watching_files
— Constant.Revise.watching_files[]
Returns true
if we watch files rather than their containing directory. FreeBSD and NFS-mounted systems should watch files, otherwise we prefer to watch directories.
Revise.polling_files
— Constant.Revise.polling_files[]
Returns true
if we should poll the filesystem for changes to the files that define loaded code. It is preferable to avoid polling, instead relying on operating system notifications via FileWatching.watch_file
. However, NFS-mounted filesystems (and perhaps others) do not support file-watching, so for code stored on such filesystems you should turn polling on.
See the documentation for the JULIA_REVISE_POLL
environment variable.
Revise.tracking_Main_includes
— Constant.Revise.tracking_Main_includes[]
Returns true
if files directly included from the REPL should be tracked. The default is false
. See the documentation regarding the JULIA_REVISE_INCLUDE
environment variable to customize it.
Path-related variables
Revise.juliadir
— Constant.Revise.juliadir
Constant specifying full path to julia top-level source directory. This should be reliable even for local builds, cross-builds, and binary installs.
Revise.basesrccache
— Constant.Revise.basesrccache
Full path to the running Julia's cache of source code defining Base
.
Revise.basebuilddir
— Constant.Revise.basebuilddir
Julia's top-level directory when Julia was built, as recorded by the entries in Base._included_files
.
Internal state management
Revise.watched_files
— Constant.Revise.watched_files
Global variable, watched_files[dirname]
returns the collection of files in dirname
that we're monitoring for changes. The returned value has type WatchList
.
This variable allows us to watch directories rather than files, reducing the burden on the OS.
Revise.revision_queue
— Constant.Revise.revision_queue
Global variable, revision_queue
holds the names of files that we need to revise, meaning that these files have changed since we last processed a revision. This list gets populated by callbacks that watch directories for updates.
Revise.included_files
— Constant.Revise.included_files
Global variable, included_files
gets populated by callbacks we register with include
. It's used to track non-precompiled packages and, optionally, user scripts (see docs on JULIA_REVISE_INCLUDE
).
Revise.fileinfos
— Constant.Revise.fileinfos
fileinfos
is the core information that tracks the relationship between source code and julia objects, and allows re-evaluation of code in the proper module scope. It is a dictionary indexed by absolute paths of files; fileinfos[filename]
returns a value of type Revise.FileInfo
.
Revise.module2files
— Constant.Revise.module2files
module2files
holds the list of filenames used to define a particular module. This is only used by revise(MyModule)
to "refresh" all the definitions in a module.
Types
Revise.RelocatableExpr
— Type.A RelocatableExpr
is exactly like an Expr
except that comparisons between RelocatableExpr
s ignore line numbering information. This allows one to detect that two expressions are the same no matter where they appear in a file.
You can use convert(Expr, rex::RelocatableExpr)
to convert to an Expr
and convert(RelocatableExpr, ex::Expr)
for the converse. Beware that the latter operates in-place and is intended only for internal use.
Revise.DefMap
— Type.DefMap
Maps def
=>nothing or def=>([sigt1,...], lineoffset)
, where:
def
is an expression- the value is
nothing
ifdef
does not define a method - if it does define a method,
sigt1...
are the signature-types andlineoffset
is the difference between the line number when the method was compiled and the current state of the source file.
See the documentation page How Revise works for more information.
Revise.SigtMap
— Type.SigtMap
Maps sigt=>def
, where sigt
is the signature-type of a method and def
the expression defining the method.
See the documentation page How Revise works for more information.
Revise.FMMaps
— Type.FMMaps
source=>sigtypes
and sigtypes=>source
mappings for a particular file/module combination. See the documentation page How Revise works for more information.
Revise.FileModules
— Type.FileModules
For a particular source file, the corresponding FileModules
is an OrderedDict(mod1=>fmm1, mod2=>fmm2)
, mapping the collection of modules "active" in the file (the parent module and any submodules it defines) to their corresponding FMMaps
.
The first key is guaranteed to be the module into which this file was include
d.
To create a FileModules
from a source file, see parse_source
.
Revise.FileInfo
— Type.FileInfo(fm::FileModules, cachefile="")
Structure to hold the per-module expressions found when parsing a single file. fm
holds the FileModules
for the file.
Optionally, a FileInfo
can also record the path to a cache file holding the original source code. This is applicable only for precompiled modules and Base
. (This cache file is distinct from the original source file that might be edited by the developer, and it will always hold the state of the code when the package was precompiled or Julia's Base
was built.) When a cache is available, fm
will be empty until the file gets edited: the original source code gets parsed only when a revision needs to be made.
Source cache files greatly reduce the overhead of using Revise.
Revise.WatchList
— Type.Revise.WatchList
A struct for holding files that live inside a directory. Some platforms (OSX) have trouble watching too many files. So we watch parent directories, and keep track of which files in them should be tracked.
Fields:
timestamp
: mtime of last updatetrackedfiles
: Set of filenames
Function reference
Functions called during initialization of Revise
Revise.async_steal_repl_backend
— Function.Revise.async_steal_repl_backend()
Wait for the REPL to complete its initialization, and then call steal_repl_backend
. This is necessary because code registered with atreplinit
runs before the REPL is initialized, and there is no corresponding way to register code to run after it is complete.
Revise.steal_repl_backend
— Function.steal_repl_backend(backend = Base.active_repl_backend)
Replace the REPL's normal backend with one that calls revise
before executing any REPL input.
Functions called when you load a new package
Revise.watch_package
— Function.watch_package(id::Base.PkgId)
Start watching a package for changes to the files that define it. This function gets called via a callback registered with Base.require
, at the completion of module-loading by using
or import
.
Revise.parse_pkg_files
— Function.parse_pkg_files(modsym)
This function gets called by watch_package
and runs when a package is first loaded. Its job is to organize the files and expressions defining the module so that later we can detect and process revisions.
Revise.init_watching
— Function.Revise.init_watching(files)
For every filename in files
, monitor the filesystem for updates. When the file is updated, either revise_dir_queued
or revise_file_queued
will be called.
Monitoring for changes
These functions get called on each directory or file that you monitor for revisions. These block execution until the file(s) are updated, so you should only call them from within an @async
block. They work recursively: once an update has been detected and execution resumes, they schedule a revision (see Revise.revision_queue
) and then call themselves on the same directory or file to wait for the next set of changes.
Revise.revise_dir_queued
— Function.revise_dir_queued(dirname)
Wait for one or more of the files registered in Revise.watched_files[dirname]
to be modified, and then queue the corresponding files on Revise.revision_queue
. This is generally called within an @async
.
Revise.revise_file_queued
— Function.revise_file_queued(filename)
Wait for modifications to filename
, and then queue the corresponding files on Revise.revision_queue
. This is generally called within an @async
.
This is used only on platforms (like BSD) which cannot use revise_dir_queued
.
Evaluating changes (revising) and computing diffs
Revise.revise_file_now
— Function.Revise.revise_file_now(file)
Process revisions to file
. This parses file
and computes an expression-level diff between the current state of the file and its most recently evaluated state. It then deletes any removed methods and re-evaluates any changed expressions.
file
must be a key in Revise.fileinfos
Revise.eval_revised
— Function.fmrep = eval_revised(fmnew::FileModules, fmref::FileModules)
Implement the changes from fmref
to fmnew
, returning a replacement FileModules
fmrep
.
Interchange between methods and signatures
Revise.get_method
— Function.method = get_method(sigt)
Get the method method
with signature-type sigt
. This is used to provide the method to Base.delete_method
. See also get_signature
.
If sigt
does not correspond to a method, returns nothing
.
Examples
julia> mymethod(::Int) = 1
mymethod (generic function with 1 method)
julia> mymethod(::AbstractFloat) = 2
mymethod (generic function with 2 methods)
julia> Revise.get_method(Tuple{typeof(mymethod), Int})
mymethod(::Int64) in Main at REPL[0]:1
julia> Revise.get_method(Tuple{typeof(mymethod), Float64})
mymethod(::AbstractFloat) in Main at REPL[1]:1
julia> Revise.get_method(Tuple{typeof(mymethod), Number})
Revise.get_def
— Function.rex = get_def(method::Method)
Return the RelocatableExpr defining method
. The source-file defining method
must be tracked. If it is in Base, this will execute track(Base)
if necessary.
Parsing source code
Revise.parse_source
— Function.fm = parse_source(filename::AbstractString, mod::Module)
Parse the source filename
, returning a FileModules
fm
. mod
is the "parent" module for the file (i.e., the one that include
d the file); if filename
defines more module(s) then these will all have separate entries in fm
.
If parsing filename
fails, nothing
is returned.
Revise.parse_source!
— Function.parse_source!(fm::FileModules, filename, mod::Module)
Top-level parsing of filename
as included into module mod
. Successfully-parsed expressions will be added to fm
. Returns fm
if parsing finished successfully, otherwise nothing
is returned.
See also parse_source
.
success = parse_source!(fm::FileModules, src::AbstractString, file::Symbol, pos::Integer, mod::Module)
Parse a string src
obtained by reading file
as a single string. pos
is the 1-based byte offset from which to begin parsing src
.
See also parse_source
.
success = parse_source!(fm::FileModules, ex::Expr, file, mod::Module)
For a file
that defines a sub-module, parse the body ex
of the sub-module. mod
will be the module into which this sub-module is evaluated (i.e., included). Successfully-parsed expressions will be added to fm
. Returns true
if parsing finished successfully.
See also parse_source
.
Revise.parse_expr!
— Function.parse_expr!(fm::FileModules, ex::Expr, file::Symbol, mod::Module)
Recursively parse the expressions in ex
, iterating over blocks and sub-module definitions. Successfully parsed expressions are added to fm
with key mod
, and any sub-modules will be stored in fm
using appropriate new keys. This accomplishes two main tasks:
- add parsed expressions to the source-code cache (so that later we can detect changes)
- determine the module into which each parsed expression is
eval
uated into
Revise.parse_module!
— Function.newmod = parse_module!(fm::FileModules, ex::Expr, file, mod::Module)
Parse an expression ex
that defines a new module newmod
. This module is "parented" by mod
. Source-code expressions are added to fm
under the appropriate module name.
Revise.funcdef_expr
— Function.exf = funcdef_expr(ex)
Recurse, if necessary, into ex
until the first function definition expression is found.
Example
julia> Revise.funcdef_expr(quote
"""
A docstring
"""
@inline foo(x) = 5
end)
:(foo(x) = begin
#= REPL[31]:5 =#
5
end)
Revise.get_signature
— Function.sigex = get_signature(expr)
Extract the signature from an expression expr
that defines a function.
If expr
does not define a function, returns nothing
.
Examples
julia> Revise.get_signature(quote
function count_different(x::AbstractVector{T}, y::AbstractVector{S}) where {S,T}
sum(x .!= y)
end
end)
:(count_different(x::AbstractVector{T}, y::AbstractVector{S}) where {S, T})
Revise.get_callexpr
— Function.callex = get_callexpr(sigex::ExLike)
Return the "call" expression for a signature-expression sigex
. (This strips out :where
statements.)
Example
julia> Revise.get_callexpr(:(nested(x::A) where A<:AbstractVector{T} where T))
:(nested(x::A))
Revise.sig_type_exprs
— Function.typexs = sig_type_exprs(sigex::Expr)
From a function signature-expression sigex
(see get_signature
), generate a list typexs
of concrete signature type expressions. This list will have length 1 unless sigex
has default arguments, in which case it will produce one type signature per valid number of supplied arguments.
These type-expressions can be evaluated in the appropriate module to obtain a Tuple-type.
Examples
julia> Revise.sig_type_exprs(:(foo(x::Int, y::String)))
1-element Array{Expr,1}:
:(Tuple{Core.Typeof(foo), Int, String})
julia> Revise.sig_type_exprs(:(foo(x::Int, y::String="hello")))
2-element Array{Expr,1}:
:(Tuple{Core.Typeof(foo), Int})
:(Tuple{Core.Typeof(foo), Int, String})
julia> Revise.sig_type_exprs(:(foo(x::AbstractVector{T}, y) where T))
1-element Array{Expr,1}:
:(Tuple{Core.Typeof(foo), AbstractVector{T}, Any} where T)
Revise.argtypeexpr
— Function.typeex1, typeex2, ... = argtypeexpr(ex...)
Return expressions that specify the types assigned to each argument in a method signature. Returns :Any
if no type is assigned to a specific argument. It also skips keyword arguments.
ex...
should be arguments 2:end
of a :call
expression (i.e., skipping over the function name).
Examples
julia> sigex = :(varargs(x, rest::Int...))
:(varargs(x, rest::Int...))
julia> Revise.argtypeexpr(Revise.get_callexpr(sigex).args[2:end]...)
(:Any, :(Vararg{Int}))
julia> sigex = :(complexargs(w::Vector{T}, @nospecialize(x::Integer), y, z::String=""; kwarg::Bool=false) where T)
:(complexargs(w::Vector{T}, #= REPL[39]:1 =# @nospecialize(x::Integer), y, z::String=""; kwarg::Bool=false) where T)
julia> Revise.argtypeexpr(Revise.get_callexpr(sigex).args[2:end]...)
(:(Vector{T}), :Integer, :Any, :String)
Git integration
Revise.git_source
— Function.Revise.git_source(file::AbstractString, reference)
Read the source-text for file
from a git commit reference
. The reference may be a string, Symbol, or LibGit2.Tree
.
Example:
Revise.git_source("/path/to/myfile.jl", "HEAD")
Revise.git_source("/path/to/myfile.jl", :abcd1234) # by commit SHA
Revise.git_files
— Function.files = git_files(repo)
Return the list of files checked into repo
.
Revise.git_repo
— Function.repo, repo_path = git_repo(path::AbstractString)
Return the repo::LibGit2.GitRepo
containing the file or directory path
. path
does not necessarily need to be the top-level directory of the repository. Also returns the repo_path
of the top-level directory for the repository.