Comparison entry points
CodeDiffs.@code_diff
— Macro@code_diff [type=:native] [color=true] [cleanup=true] [dbinfo=true] [option=value...] f₁(...) f₂(...)
@code_diff [option=value...] :(expr₁) :(expr₂)
Compare the methods called by the f₁(...)
and f₂(...)
or the expressions expr₁
and expr₂
, then return a CodeDiff
.
option
s are passed to get_code
. Option names ending with _1
or _2
are passed to the call of get_code
for f₁
and f₂
respectively. They can also be packed into extra_1
and extra_2
.
To compare Expr
in variables, use @code_diff :($a) :($b)
.
cleanup == true
will use cleanup_code
to make the codes more prone to comparisons (e.g. by renaming variables with names which change every time).
dbinfo
is a broader version of the debuginfo
option of @code_typed
and @code_llvm
. dbinfo
works for all code types. dbinfo=true
is equivalent to debuginfo=:source
(or :default
) and dbinfo=false
is debuginfo=:none
. For code types with no option to control the verbosity of the output, dbinfo
is silently ignored.
# Default comparison
@code_diff type=:native f() g()
# No debuginfo for `f()` and `g()`
@code_diff type=:native debuginfo=:none f() g()
# No debuginfo for `f()`
@code_diff type=:native debuginfo_1=:none f() g()
# No debuginfo for `g()`
@code_diff type=:native debuginfo_2=:none f() g()
# Options can be passed from variables with `extra_1` and `extra_2`
opts = (; debuginfo=:none, world=Base.get_world_counter())
@code_diff type=:native extra_1=opts extra_2=opts f() g()
# `type` and `color` can also be made different in each side
@code_diff type_1=:native type_2=:llvm f() f()
CodeDiffs.code_diff
— Functioncode_diff(args₁::Tuple, args₂::Tuple; extra_1=(;), extra_2=(;), kwargs...)
Function equivalent to @code_diff
(extra_1, extra_2, kwargs..., args₁, args₂)
. kwargs
are common to both sides, while extra_1
and extra_2
are passed to code_for_diff
only with args₁
and args₂
respectively.
julia> diff_1 = @code_diff debuginfo_1=:none f() g();
julia> diff_2 = code_diff((f, Tuple{}), (g, Tuple{}); extra_1=(; debuginfo=:none));
julia> diff_1 == diff_2
true
CodeDiffs.code_for_diff
— Functioncode_for_diff(f, types::Type{<:Tuple};
type=:native, color=true, dbinfo=true, cleanup=true, cleanup_opts=(;), kwargs...
)
code_for_diff(expr::Expr; type=:ast, color=true, kwargs...)
Fetches the code of f
with get_code(Val(type), f, types; dbinfo, kwargs...)
, cleans it up with cleanup_code(Val(type), code, dbinfo, cleanup_opts)
and highlights it using the appropriate code_highlighter(Val(type))
. The result is two String
s: one without and the other with highlighting.
CodeDiffs.CodeDiff
— TypeCodeDiff(code₁, code₂)
CodeDiff(code₁, code₂, highlighted₁, highlighted₂)
A difference between code₁
and code₂
.
code₁
and code₂
should have no highlighting. Only highlighted₁
and highlighted₂
should have syntax highlighting. When showing the differences, their formatting will be re-applied.
For cleaner differences, use cleanup_code
on all codes.
Use optimize_line_changes!
to improve the difference.
Fancy REPL output is done with side_by_side_diff
.
Code fetching
CodeDiffs.@code_for
— Macro@code_for [type=:native] [color=true] [io=stdout] [cleanup=true] [dbinfo=true] [option=value...] f(...)
@code_for [option=value...] :(expr)
Display the code of f(...)
to io
for the given code type
, or the expression expr
(AST only).
option
s are passed to get_code
. To display an Expr
in a variable, use @code_for :($expr)
.
io
defaults to stdout
. If io == String
, then the code is not printed and is simply returned.
If the type
option is the first option, "type"
can be omitted: i.e. @code_for :llvm f()
is valid.
cleanup == true
will use cleanup_code
on the code.
dbinfo
is a broader version of the debuginfo
option of @code_typed
and @code_llvm
. dbinfo
works for all code types. dbinfo=true
is equivalent to debuginfo=:source
(or :default
) and dbinfo=false
is debuginfo=:none
. For code types with no option to control the verbosity of the output, dbinfo
is silently ignored.
# Default display
@code_for type=:native f()
# Without debuginfo
@code_for type=:llvm debuginfo=:none f()
# Options sets can be passed from variables with the `extra` options
opts = (; debuginfo=:none, world=Base.get_world_counter())
@code_for type=:typed extra=opts f()
# Same as above, but shorter since we can omit "type"
@code_for :typed extra=opts f()
CodeDiffs.code_native
— Functioncode_native(f, types; world=nothing, kwargs...)
The native code of the method of f
called with types
(a Tuple
type), as a string. world
defaults to the current world age. kwargs
are forwarded to InteractiveUtils.code_native
.
CodeDiffs.code_llvm
— Functioncode_llvm(f, types; world=nothing, kwargs...)
The LLVM-IR code of the method of f
called with types
(a Tuple
type), as a string. world
defaults to the current world age. kwargs
are forwarded to InteractiveUtils.code_native
.
CodeDiffs.code_typed
— Functioncode_typed(f, types; world=nothing, kwargs...)
The Julia-IR code (aka 'typed code') of the method of f
called with types
(a Tuple
type), as a Core.CodeInfo
. world
defaults to the current world age. kwargs
are forwarded to Base.code_typed
.
The function call should only match a single method.
CodeDiffs.code_ast
— Functioncode_ast(f, types; world=nothing, prettify=true, lines=false, alias=false)
The Julia AST of the method of f
called with types
(a Tuple
type), as a Expr
. Revise.jl
is used to get those definitions, and it must be loaded before the definition of f
's method to get the AST for.
world
defaults to the current world age. Since Revise.jl
does not keep track of all definitions in all world ages, it is very likely that the only retrievable definition is the most recent one.
If prettify == true
, then MacroTools.prettify(code; lines, alias)
is used to cleanup the AST. lines == true
will keep the LineNumberNode
s and alias == true
will replace mangled names (or gensym
s) by more readable names.
CodeDiffs.get_code
— Functionget_code(::Val{code_type}, f, types; world=nothing, dbinfo=true, kwargs...)
The code object of code_type
for f
. Dispatch depends on code_type
:
:native
:code_native
:llvm
:code_llvm
:typed
:code_typed
:ast
:code_ast
world
is the world age of the code to get. If unsupported by code_type
and !isnothing(world)
, an error is raised.
dbinfo
is a superset of the debuginfo=:source
or debuginfo=:none
options for code_llvm
and code_typed
. If unsupported by code_type
, it should be ignored. The debuginfo
option takes precedence over dbinfo
if code_type
supports it.
Highlighting
CodeDiffs.code_highlighter
— Functioncode_highlighter(::Val{code_type}) where {code_type}
Return a function of signature (io::IO, code_obj)
which prints code_obj
to io
with highlighting/decorations. By default print(io, code_obj)
is used for AbstractString
s and Base.show(io, MIME"text/plain"(), code_obj)
otherwise.
The highlighting function is called twice: once for color-less text and again with color.
Diff display
CodeDiffs.optimize_line_changes!
— Functionoptimize_line_changes!(diff::CodeDiff; dist=Levenshtein(), tol=0.7)
Merges consecutive line removals+additions into single line changes in diff
, when they are within the tol
erance of the normalized string dist
ance.
This does not aim to produce an optimal CodeDiff
, but simply improve its display.
CodeDiffs.side_by_side_diff
— Functionside_by_side_diff([io::IO,] diff::CodeDiff; tab_width=4, width=nothing, line_numbers=nothing)
Side by side display of a CodeDiff
to io
(defaults to stdout
).
width
defaults to the width of the terminal. It is 80
by default for non-terminal io
.
tab_width
is the number of spaces tabs are replaced with.
line_numbers=true
will add line numbers on each side of the columns. It defaults to the environment variable "CODE_DIFFS_LINE_NUMBERS"
, which itself defaults to false
.