Skip to content

Commit

Permalink
fix: Generated methods now use the line number information of the inv…
Browse files Browse the repository at this point in the history
…oking macro, rather than internal package line numbers (#8)

docs: Added `per_property_pprint_exprs`, `GenericMimeType`

deps: Set `Dictionaries` `0.3` -> `0.3, 0.4`
  • Loading branch information
curtd authored Jan 31, 2024
1 parent 15cc58f commit d281a08
Show file tree
Hide file tree
Showing 9 changed files with 93 additions and 50 deletions.
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ AutoPrettyPrintingTimeZonesExt = "TimeZones"

[compat]
Dates = "1"
Dictionaries = "0.3"
Dictionaries = "0.3, 0.4"
MacroUtilities = "1.15"
PrecompileTools = "1"
PrettyPrinting = "0.4.1"
Expand Down
20 changes: 17 additions & 3 deletions docs/src/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
@decorators
```

## Functions
## Layout/Printing

### Functions
```@docs
AutoPrettyPrinting.custom_tile(x, mime::MIME; kwargs...)
AutoPrettyPrinting.custom_tile_horiz(x, mime::MIME; kwargs...)
Expand All @@ -19,8 +21,20 @@ AutoPrettyPrinting.pprint(io::IO, mime::MIME, obj)
repr_pretty
```

## Types
### Types
```@docs
KeyValue
PPrintContext
```
```

## Code Generation
### Functions
```@docs
AutoPrettyPrinting.per_property_pprint_exprs
```

### Types
```@docs
AutoPrettyPrinting.GenericMimeType
```

1 change: 1 addition & 0 deletions docs/src/examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ DocTestSetup = nothing
```

# Example usage
## Basic package macros
If we're not particular about the particular layout or format of the pretty printing layouts of our custom types, we can use the `@def_pprint` macro to define our `Base.show` method for the default `"text/plain"` MIME type.

```jldoctest examples
Expand Down
2 changes: 1 addition & 1 deletion src/AutoPrettyPrinting.jl
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ module AutoPrettyPrinting
export KeyValue

export @mime_type, @custom_tile, @def_pprint, @hide_typename, @def_pprint_atomic, @decorators

export PPrintContext
export repr_pretty

Expand Down
4 changes: 2 additions & 2 deletions src/auto_layout.jl
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ function pprint_values_expr(expr; level=nothing, next_level::Bool=false, parent_
push!(m.args, :($AutoPrettyPrinting.PPRINT_NESTED_LEVEL => level+1))
end
if !isnothing(parent_is_container)
push!(m.args, :($AutoPrettyPrinting.PPRINT_PARENT_IS_CONTAINER => $(esc(parent_is_container))))
push!(m.args, :($AutoPrettyPrinting.PPRINT_PARENT_IS_CONTAINER => $(parent_is_container isa Bool ? parent_is_container : esc(parent_is_container))))
end
if !isnothing(show_typename)
push!(m.args, :($AutoPrettyPrinting.PPRINT_SHOW_TYPENAME => $(esc(show_typename))))
push!(m.args, :($AutoPrettyPrinting.PPRINT_SHOW_TYPENAME => $(show_typename isa Bool ? show_typename : esc(show_typename))))
end
push!(output_expr.args, to_expr(m(expr)))
return output_expr
Expand Down
38 changes: 16 additions & 22 deletions src/auto_prettyprinting.jl
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,6 @@ function custom_tile_func_def_expr(tile_expr, mime_types, generic_mime)
object_arg = :_obj_
mime_arg = :_mime_
g = from_expr(FuncDef, tile_expr)
object_arg = :_obj_
mime_arg = :_mime_
if !isnothing(g)
length(g.args) == 2 || throw(@arg_error tile_expr "Must have exactly two arguments")
arg1 = g.args[1]
Expand All @@ -78,12 +76,12 @@ function custom_tile_func_def_expr(tile_expr, mime_types, generic_mime)
end
return object_arg, mime_arg, tile_expr, mime_types, generic_mime
end
function custom_tile_expr(typename, mime_type, tile_expr; _sourceinfo=nothing, generate_base_show::Bool, object_arg::Symbol, mime_arg::Symbol)
function custom_tile_expr(typename, mime_type, tile_expr; _sourceinfo::Union{LineNumberNode, Nothing}=nothing, generate_base_show::Bool, object_arg::Symbol, mime_arg::Symbol)
output = Any[]
body = Expr(:block, _sourceinfo, tile_expr)
push!(output, :($AutoPrettyPrinting.custom_tile($(object_arg)::$(typename), $(mime_arg)::$(mime_type); kwargs...) = $(body)))
push!(output, :($AutoPrettyPrinting.custom_tile_horiz($(object_arg)::$(typename), $(mime_arg)::$(mime_type); kwargs...) = $(body)))
push!(output, :($AutoPrettyPrinting.custom_tile_vert($(object_arg)::$(typename), $(mime_arg)::$(mime_type); kwargs...) = $(body)))
push!(output, Expr(:(=), :($AutoPrettyPrinting.custom_tile($(object_arg)::$(typename), $(mime_arg)::$(mime_type); kwargs...)),body))
push!(output, Expr(:(=), :($AutoPrettyPrinting.custom_tile_horiz($(object_arg)::$(typename), $(mime_arg)::$(mime_type); kwargs...)),body))
push!(output, Expr(:(=), :($AutoPrettyPrinting.custom_tile_vert($(object_arg)::$(typename), $(mime_arg)::$(mime_type); kwargs...)),body))
if generate_base_show
push!(output, generate_base_show_expr(typename, mime_type; _sourceinfo))
end
Expand All @@ -93,7 +91,7 @@ end
"""
@custom_tile [mime_types=nothing] [generic_mime=false] [base_show=false] T => custom_tile_expr
Defines a `AutoPrettyPrinting.custom_tile` method for type `T` and optionally provided `mime_types` using `custom_tile_expr` as the function's body.
Creates custom tile method definitions for type `T` and optionally provided `mime_types` using `custom_tile_expr` as the function's body.
# Arguments
- $mime_types_docstr
Expand All @@ -114,25 +112,24 @@ macro custom_tile(args...)
object_arg, mime_arg, tile_expr, mime_types, generic_mime = custom_tile_func_def_expr(f.rhs, mime_types, generic_mime)
mime_types = normalize_mime_type_args(mime_types; generic_mime)

_sourceinfo = __source__
output = Expr(:block, _sourceinfo)
output = Expr(:block, __source__)

for mime_type in mime_type_exprs(mime_types)
append!(output.args, custom_tile_expr(typename, mime_type, tile_expr; _sourceinfo, generate_base_show=base_show, object_arg, mime_arg))
append!(output.args, custom_tile_expr(typename, mime_type, tile_expr; _sourceinfo=__source__, generate_base_show=base_show, object_arg, mime_arg))
end

return output |> esc
end

function def_pprint_expr(type, _mod; properties::Union{Vector{Symbol}, Nothing}, mime_types::Union{Vector{Symbol}, GenericMimeType}, generate_base_show::Bool)
function def_pprint_expr(type, _mod; properties::Union{Vector{Symbol}, Nothing}, mime_types::Union{Vector{Symbol}, GenericMimeType}, generate_base_show::Bool, _sourceinfo::Union{LineNumberNode, Nothing}=nothing)
mime_types_expr = mime_types isa GenericMimeType ? :($GenericMimeType()) : Expr(:vect, QuoteNode.(mime_types)...)
return Base.remove_linenums!(quote
source_expr = _sourceinfo isa LineNumberNode ? :(LineNumberNode($(_sourceinfo.line), $(QuoteNode(_sourceinfo.file)))) : :nothing
return quote
let
local _properties = $(isnothing(properties) ? :($Base.fieldnames($type)) : Expr(:tuple, QuoteNode.(properties)...))
local expr = $per_property_pprint_exprs(_properties, $type; generate_base_show=$generate_base_show, mime_types=$(mime_types_expr))
local expr = $per_property_pprint_exprs($(isnothing(properties) ? :($Base.fieldnames($type)) : Expr(:tuple, QuoteNode.(properties)...)), $type; generate_base_show=$generate_base_show, mime_types=$(mime_types_expr), _sourceinfo=$(source_expr))
$Core.eval($_mod, expr)
end
end)
end
end

"""
Expand All @@ -156,8 +153,7 @@ macro def_pprint(args...)
end
mime_types = normalize_mime_type_args(mime_types; generic_mime)
type = args[end]
_sourceinfo = __source__
return Expr(:block, _sourceinfo, def_pprint_expr(type, __module__; mime_types, generate_base_show=base_show, properties)) |> esc
return Expr(:block, __source__, def_pprint_expr(type, __module__; mime_types, generate_base_show=base_show, properties, _sourceinfo=__source__)) |> esc
end

"""
Expand Down Expand Up @@ -192,9 +188,9 @@ macro def_pprint_atomic(args...)
T = esc(args[end])
_mime_types = normalize_mime_type_args(mime_types; generic_mime)

custom_tile_exprs = [:($AutoPrettyPrinting.custom_tile($x::$T, ::$mime_type; kwargs...) = $literal($to_string_expr)) for mime_type in mime_type_exprs(_mime_types)]
custom_tile_exprs = [Expr(:(=), :($AutoPrettyPrinting.custom_tile($x::$T, ::$mime_type; kwargs...)), Expr(:block, __source__, :($literal($to_string_expr)))) for mime_type in mime_type_exprs(_mime_types)]

return Expr(:block, __source__, :($AutoPrettyPrinting._is_atomic_type(::Type{<:$T}) = true), custom_tile_exprs...)
return Expr(:block, __source__, Expr(:(=), :($AutoPrettyPrinting._is_atomic_type(::Type{<:$T})), Expr(:block, __source__, :true)), custom_tile_exprs...)
end


Expand All @@ -210,9 +206,7 @@ for T in (:(Period), :(Instant), :(TimeType))
end
end

for T in (:IPv4, :IPv6)
@eval @def_pprint_atomic all_mime_types=true Sockets.$T
end
@def_pprint_atomic all_mime_types=true Sockets.IPAddr

"""
PPrintContext(io::IO, [mime::MIME])
Expand Down
63 changes: 42 additions & 21 deletions src/code_generation.jl
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
"""
GenericMimeType
Indicates that code generation should use the unparametrized type `MIME` instead of a particular parametric instance `MIME{S}`
"""
struct GenericMimeType
end

Expand All @@ -9,12 +14,12 @@ const generic_mime_type_expr = :($Base.MIME)
mime_type_exprs(::GenericMimeType; add_text_plain::Bool=false) = add_text_plain ? Any[generic_mime_type_expr, mime_text_plain_type_expr] : Any[generic_mime_type_expr]
mime_type_exprs(mime_types::Vector{Symbol}; kwargs...) = Any[:($Base.MIME{$(QuoteNode(mime_t))}) for mime_t in mime_types]

function generate_base_show_expr(typename, mime_type; _sourceinfo=nothing)
function generate_base_show_expr(typename, mime_type; _sourceinfo::Union{LineNumberNode, Nothing}=nothing)
body = Expr(:block)
!isnothing(_sourceinfo) && push!(body.args, _sourceinfo)
push!(body.args, :($AutoPrettyPrinting.pprint(io, mime, o)))

return :($Base.show(io::IO, mime::$(mime_type), o::$typename) = $body)
return Expr(:block, _sourceinfo, Expr(:(=), :($Base.show(io::IO, mime::$(mime_type), o::$typename)), body))
end

function _per_property_pprint_exprs(f_exprs, properties; vert_layout_rpad::Int=0, input_obj::Symbol)
Expand Down Expand Up @@ -51,41 +56,57 @@ function _per_property_pprint_exprs(f_exprs, properties; vert_layout_rpad::Int=0
return (; horizontal=(layout_horizontal, object_list_compact_expr), vertical=(layout_aligned_vertical, object_list_aligned_expr))
end

function custom_tile_horiz_or_vert_body_expr(layout_expr, object_list; prefix="", parentheses, is_horiz::Bool, _sourceinfo=nothing)
return Expr(:block, :(prefix = $PPRINT_SHOW_TYPENAME[] ? $(prefix) : ""), :( $AutoPrettyPrinting.@pprint_values parent_is_container=false next_level=true show_typename=true begin
$(something(_sourceinfo, :()))
$layout_expr
$list_layout_prefer_horizontal($object_list; prefix, parentheses=$parentheses, allow_horiz=$(is_horiz), allow_vert=$(!is_horiz), kwargs...)
end ))
function custom_tile_horiz_or_vert_body_expr(layout_expr, object_list; prefix="", parentheses, is_horiz::Bool, _sourceinfo::Union{LineNumberNode, Nothing}=nothing)
return Expr(:block, something(_sourceinfo, :()), :(prefix = $PPRINT_SHOW_TYPENAME[] ? $(prefix) : ""), pprint_values_expr(Expr(:block, layout_expr, :($list_layout_prefer_horizontal($object_list; prefix, parentheses=$parentheses, allow_horiz=$is_horiz, allow_vert=$(!is_horiz), kwargs...))); parent_is_container=true, next_level=true, show_typename=true, _sourceinfo=something(_sourceinfo, not_provided)))
end

function default_custom_tile_def_expr(typename, mime_type, input_obj=:_obj_, input_mime=:_mime_; _sourceinfo::Union{LineNumberNode, Nothing}=nothing)
body = Expr(:block)
if !isnothing(_sourceinfo)
push!(body.args, _sourceinfo)
end
push!(body.args, :($custom_tile_horiz_or_vert($input_obj, $input_mime; kwargs...)))
return Expr(:(=), :($AutoPrettyPrinting.custom_tile($input_obj::$typename, $input_mime::$mime_type; kwargs...)), body)
end

function per_property_pprint_exprs(f_exprs, properties, typename; mime_types::Union{Vector{Symbol}, GenericMimeType}, generate_base_show::Bool, typename_str=typename isa Type ? string(nameof(typename)) : string(typename), vert_layout_rpad::Int=0, _sourceinfo=nothing)
"""
per_property_pprint_exprs([f_exprs], properties, typename; mime_types, generate_base_show, [typename_str], [vert_layout_rpad], [_sourceinfo], [add_text_plain::Bool])
Returns an expression providing default `custom_tile_horiz`, `custom_tile_vert`, and `custom_tile` definitions for type `typename` involving a list of `properties`.
If provided, `f_exprs` must be a single-argument function which takes an argument `property_name::Symbol` and returns either a 2-tuple `(horizontal_expr, vertical_expr)` of the horizontal + vertical expressions, respectively, to use for that particular property, or `nothing` if the default expressions are to be used.
# Arguments
- `mime_types::Union{Vector{Symbol}, GenericMimeType}`: List of `MIME` parameter `Symbol`s to generate. If a `GenericMimeType` is provided, will use an unparametrized `MIME` type.
- `generate_base_show::Bool`: If `true`, will define the corresponding `Base.show` method for `typename` using this package's `pprint`
- `typename_str::String = string(typename)`: The generated code uses this string for the typename, if provided
- `vert_layout_rpad::Int = 0`: Right-pads the aligned vertical layout keys by this amount
- `_sourceinfo::Union{LineNumberNode, Nothing} = nothing`: If provided, uses this line information when generating methods
- `add_text_plain::Bool = true`: If `mime_types` is a `GenericMimeType`, adds the `MIME"text/plain"` type to the list of generated mime types. Otherwise, this argument is ignored.
"""
function per_property_pprint_exprs(f_exprs, properties, typename; mime_types::Union{Vector{Symbol}, GenericMimeType}, generate_base_show::Bool, typename_str=typename isa Type ? string(nameof(typename)) : string(typename), vert_layout_rpad::Int=0, _sourceinfo::Union{LineNumberNode, Nothing}=nothing, add_text_plain::Bool=true)
@nospecialize

input_obj = :obj
(; horizontal, vertical) = _per_property_pprint_exprs(f_exprs, properties; vert_layout_rpad, input_obj)
layout_horizontal, object_list_compact_expr = horizontal
layout_vertical, object_list_vert_expr = vertical
parentheses = isempty(typename_str) ? empty_parentheses : dict_parentheses

output = Expr(:block)
_mime_types = mime_type_exprs(mime_types; add_text_plain=true)
_mime_types = mime_type_exprs(mime_types; add_text_plain)
for mime_type in _mime_types
push!(output.args, quote
function $AutoPrettyPrinting.custom_tile_horiz($input_obj::$typename, mime::$mime_type; kwargs...)
return $(custom_tile_horiz_or_vert_body_expr(layout_horizontal, object_list_compact_expr; prefix=typename_str, parentheses, _sourceinfo, is_horiz=true))
end
function $AutoPrettyPrinting.custom_tile_vert($input_obj::$typename, mime::$mime_type; kwargs...)
return $(custom_tile_horiz_or_vert_body_expr(layout_vertical, object_list_vert_expr; prefix=typename_str, parentheses, _sourceinfo,is_horiz=false))
end
$AutoPrettyPrinting.custom_tile($input_obj::$typename, mime::$mime_type; kwargs...) = $custom_tile_horiz_or_vert($input_obj, mime; kwargs...)
end)
push!(output.args, Expr(:block,
Expr(:(=), :($AutoPrettyPrinting.custom_tile_horiz($input_obj::$typename, mime::$mime_type; kwargs...)), custom_tile_horiz_or_vert_body_expr(layout_horizontal, object_list_compact_expr; prefix=typename_str, parentheses, _sourceinfo, is_horiz=true)),
Expr(:(=), :($AutoPrettyPrinting.custom_tile_vert($input_obj::$typename, mime::$mime_type; kwargs...)), custom_tile_horiz_or_vert_body_expr(layout_vertical, object_list_vert_expr; prefix=typename_str, parentheses, _sourceinfo, is_horiz=false)),
default_custom_tile_def_expr(typename, mime_type, input_obj; _sourceinfo)))
end
if generate_base_show
show_expr = Expr(:block)
for mime_type in _mime_types
push!(show_expr.args, generate_base_show_expr(typename, mime_type))
push!(show_expr.args, generate_base_show_expr(typename, mime_type; _sourceinfo))
end
push!(output.args, show_expr)

end
return output
end
Expand Down
1 change: 1 addition & 0 deletions test/TestAutoPrettyPrinting.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
module TestAutoPrettyPrinting
using AutoPrettyPrinting
using Dictionaries, Dates, Sockets
using JET, Test, TestingUtilities

import AutoPrettyPrinting: literal
Expand Down
12 changes: 12 additions & 0 deletions test/tests/test_printing.jl
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
@testset "Printing" begin
@testset "Atomic types" begin
@Test repr_pretty(mime_plain, Date(2024, 1, 1)) == "2024-01-01"
@Test repr_pretty(mime_plain, DateTime(2024, 1, 1, 12, 30)) == "2024-01-01T12:30:00"
@Test repr_pretty(mime_plain, Time(12, 31)) == "12:31:00"
@Test repr_pretty(mime_plain, ip"127.0.0.1") == "127.0.0.1"
@Test repr_pretty(mime_plain, ip"::") == "::"
end
@testset "Pairs + KeyValues" begin
x = CustomTileFunc(2)
@Test repr_pretty(:x => x) == ":x => CustomTileFunc(4)"
Expand Down Expand Up @@ -29,6 +36,11 @@
@Test repr("text/plain", t) == "(x = $TooManyFields(1, 2, 3, 4, 5),)"
@Test repr_pretty(t) == ref_repr
end
@testset "Dictionaries" begin
d = dictionary((i => CustomTileFunc(i) for i in 1:3))
@Test repr_pretty(mime_plain, d) == "Dictionary( 1 => CustomTileFunc(1), 2 => CustomTileFunc(4), 3 => CustomTileFunc(9) )"
@Test repr_pretty(mime_testing, d) == "Dictionary( 1 => CustomTileFunc(0), 2 => CustomTileFunc(1), 3 => CustomTileFunc(2) )"
end
@testset "PrintContext" begin
x = TooManyFields(1,2,3,4,5)
io = IOBuffer()
Expand Down

0 comments on commit d281a08

Please sign in to comment.