Skip to content

Commit 730b5c9

Browse files
committed
Allow any object into the macros, making any extension possible
1 parent f874b09 commit 730b5c9

File tree

3 files changed

+40
-26
lines changed

3 files changed

+40
-26
lines changed

docs/src/extensions.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,11 @@ involves two functions:
5757
- `CodeDiffs.extract_extra_options(f, kwargs)` returns some additional `kwargs` which are passed to `get_code`
5858
- `CodeDiffs.get_code(code_type, f, types; kwargs...)` allows to change `f` depending on its type.
5959
To avoid method ambiguities, do not put type constraints on `code_type`.
60+
61+
Defining a new object type which can be put as an argument to `@code_diff` or `@code_for`
62+
invoves at one function: `CodeDiffs.code_for_diff(obj::YourType; kwargs...)`.
63+
It must return two `String`s, one without and the other without highlighting.
64+
When calling `@code_for obj`, [`code_for_diff(obj)`](@ref) will be called only if `obj` is
65+
not a call expression or a quoted `Expr`.
66+
`kwargs` are the options passed to `@code_for` or the options passed to `@code_diff` for
67+
the side of `obj`.

src/compare.jl

Lines changed: 26 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -100,12 +100,13 @@ argconvert(@nospecialize(code_type), arg) = arg
100100
extract_extra_options(@nospecialize(f), _) = (;)
101101
separate_kwargs(code_type::Val, args...; kwargs...) = (argconvert.(Ref(code_type), args), values(kwargs))
102102
is_error_expr(expr) = Base.isexpr(expr, :call) && expr.args[1] in (:error, :throw)
103+
is_call_expr(expr) = Base.isexpr(expr, :call) || (Base.isexpr(expr, :(.), 2) && Base.isexpr(expr.args[2], :tuple))
103104

104105

105106
function gen_code_for_diff_call(mod, expr, diff_options)
106107
# `diff_options` must be already `esc`aped, but not `expr`
107108

108-
if !Base.isexpr(expr, [:call, :(.)])
109+
if !is_call_expr(expr)
109110
error_str = "Expected call (or dot call) to function, got: $expr"
110111
return :(throw(ArgumentError($error_str)))
111112
end
@@ -173,6 +174,23 @@ function gen_code_for_diff_call(mod, expr, diff_options)
173174
end
174175

175176

177+
function code_diff_for_expr(code_expr, options, mod)
178+
if is_call_expr(code_expr)
179+
# Generic call comparison
180+
return gen_code_for_diff_call(mod, code_expr, options)
181+
else
182+
if Base.isexpr(code_expr, :quote)
183+
# AST comparison
184+
type_guess = (Expr(:kw, :type, QuoteNode(:ast)),)
185+
else
186+
# Mystery comparison
187+
type_guess = ()
188+
end
189+
return :($code_for_diff($(esc(code_expr)); $(type_guess...), $(options...)))
190+
end
191+
end
192+
193+
176194
"""
177195
@code_diff [type=:native] [color=true] [cleanup=true] [option=value...] f₁(...) f₂(...)
178196
@code_diff [option=value...] :(expr₁) :(expr₂)
@@ -256,17 +274,10 @@ macro code_diff(args...)
256274
code₁ isa QuoteNode && (code₁ = Expr(:quote, Expr(:block, code₁.value)))
257275
code₂ isa QuoteNode && (code₂ = Expr(:quote, Expr(:block, code₂.value)))
258276

259-
if Base.isexpr(code₁, :quote) && Base.isexpr(code₂, :quote)
260-
code₁ = esc(code₁)
261-
code₂ = esc(code₂)
262-
code_for_diff₁ = :($code_for_diff($code₁; type=:ast, $(options₁...)))
263-
code_for_diff₂ = :($code_for_diff($code₂; type=:ast, $(options₂...)))
264-
else
265-
code_for_diff₁ = gen_code_for_diff_call(__module__, code₁, options₁)
266-
code_for_diff₂ = gen_code_for_diff_call(__module__, code₂, options₂)
267-
is_error_expr(code_for_diff₁) && return code_for_diff₁
268-
is_error_expr(code_for_diff₂) && return code_for_diff₂
269-
end
277+
code_for_diff₁ = code_diff_for_expr(code₁, options₁, __module__)
278+
code_for_diff₂ = code_diff_for_expr(code₂, options₂, __module__)
279+
is_error_expr(code_for_diff₁) && return code_for_diff₁
280+
is_error_expr(code_for_diff₂) && return code_for_diff₂
270281

271282
return quote
272283
let
@@ -342,15 +353,10 @@ macro code_for(args...)
342353
# Simple values such as `:(1)` are stored in a `QuoteNode`
343354
code isa QuoteNode && (code = Expr(:quote, Expr(:block, code.value)))
344355

356+
# Place the diff options in a temp variable in order to extract `io` at runtime
345357
diff_options = esc(gensym(:diff_options))
346-
347-
if Base.isexpr(code, :quote)
348-
code = esc(code)
349-
code_for = :($code_for_diff($code; type=:ast, $(options...)))
350-
else
351-
code_for = gen_code_for_diff_call(__module__, code, [:($diff_options...)])
352-
is_error_expr(code_for) && return code_for
353-
end
358+
code_for = code_diff_for_expr(code, [:($diff_options...)], __module__)
359+
is_error_expr(code_for) && return code_for
354360

355361
return quote
356362
let

test/runtests.jl

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -461,15 +461,15 @@ end
461461

462462
@testset "Macros" begin
463463
@testset "error" begin
464-
@test_throws "Expected call" @code_diff "f()" g()
465-
@test_throws "Expected call" @code_diff f() "g()"
466-
@test_throws "Expected call" @code_diff "f()" "g()"
467-
@test_throws "Expected call" @code_diff a b
464+
@test_throws MethodError @code_diff "f()" g()
465+
@test_throws MethodError @code_diff f() "g()"
466+
@test_throws MethodError @code_diff "f()" "g()"
467+
@test_throws UndefVarError @code_diff a b
468468
@test_throws "`key=value`, got: `a + 1`" @code_diff a+1 b c
469469
@test_throws "world age" @code_diff type=:ast world_1=1 f() f()
470470

471-
@test_throws "Expected call" @code_for "f()"
472-
@test_throws "Expected call" @code_for a
471+
@test_throws MethodError @code_for "f()"
472+
@test_throws UndefVarError @code_for a
473473
@test_throws "`key=value`, got: `a + 1`" @code_for a+1 b c
474474
@test_throws "world age" @code_for type=:ast world=1 f()
475475
end

0 commit comments

Comments
 (0)