Skip to content

Commit b2746c8

Browse files
Support capturing Expr for use in abbreviations.
Add a generic interface function `interpolation` that hooks into the stage after macro expansion so that interpolated values in docstrings can implement their own behaviour rather than being directly interpolated into the docstring. Pass the documented expression through to `interpolation` such that abbreviation implementations can make use of this expression for their own needs.
1 parent 20c850b commit b2746c8

File tree

5 files changed

+66
-0
lines changed

5 files changed

+66
-0
lines changed

src/DocStringExtensions.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ import LibGit2
8282
export @template, FIELDS, TYPEDFIELDS, EXPORTS, METHODLIST, IMPORTS
8383
export SIGNATURES, TYPEDSIGNATURES, TYPEDEF, DOCSTRING, FUNCTIONNAME
8484
export README, LICENSE
85+
export interpolation
8586

8687
# Includes.
8788

src/templates.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ end
9797
# On v0.6 and below it seems it was assumed to be (docstr::String, expr::Expr), but on v0.7
9898
# it is (source::LineNumberNode, mod::Module, docstr::String, expr::Expr)
9999
function template_hook(source::LineNumberNode, mod::Module, docstr, expr::Expr)
100+
docstr = _capture_expression(docstr, expr)
100101
# During macro expansion we only need to wrap docstrings in special
101102
# abbreviations that later print out what was before and after the
102103
# docstring in it's specific template. This is only done when the module

src/utilities.jl

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,42 @@
33
# Utilities.
44
#
55

6+
#
7+
# Expression Capture.
8+
#
9+
10+
"""
11+
interpolation(object::T, captured::Expr) -> new_object
12+
13+
Interface method for hooking into interpolation within docstrings to change
14+
the behaviour of the interpolation. `object` is the interpolated object within a
15+
docstring and `captured` is the raw expression that is documented by the docstring
16+
in which the interpolated `object` has been included.
17+
18+
To define custom behaviour for your own `object` types implement a method of
19+
`interpolation(::T, captured)` for type `T` and return a `new_object` to
20+
be interpolated into the final docstring. Note that you must own the definition
21+
of type `T`. `new_object` does not need to be of type `T`.
22+
"""
23+
interpolation(@nospecialize(object), @nospecialize(_)) = object
24+
25+
# During macro expansion process the interpolated string and replace all interpolation
26+
# syntax with calls to `interpolation` that pass through the documented expression along
27+
# with the resolved object that was interpolated.
28+
function _capture_expression(docstr::Expr, expr::Expr)
29+
if Meta.isexpr(docstr, :string)
30+
quoted = QuoteNode(expr)
31+
new_docstring = Expr(:string)
32+
append!(new_docstring.args, [_process_interpolation(each, quoted) for each in docstr.args])
33+
return new_docstring
34+
end
35+
return docstr
36+
end
37+
_capture_expression(@nospecialize(other), ::Expr) = other
38+
39+
_process_interpolation(str::AbstractString, ::QuoteNode) = str
40+
_process_interpolation(@nospecialize(expr), quoted::QuoteNode) = Expr(:call, interpolation, expr, quoted)
41+
642
#
743
# Method grouping.
844
#

test/interpolation.jl

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
module InterpolationTestModule
2+
3+
struct TestType
4+
value::Int
5+
end
6+
7+
import DocStringExtensions
8+
9+
DocStringExtensions.interpolation(obj::TestType, ex::Expr) = ex.args[obj.value]
10+
11+
"""
12+
$(TestType(1))
13+
"""
14+
f(x) = x + 1
15+
16+
"""
17+
$(TestType(2))
18+
"""
19+
g(x) = x + 2
20+
21+
end

test/tests.jl

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
const DSE = DocStringExtensions
22

33
include("templates.jl")
4+
include("interpolation.jl")
45
include("TestModule/M.jl")
56

67
# initialize a test repo in test/TestModule which is needed for some tests
@@ -516,6 +517,12 @@ end
516517
@test fmt(:(TemplateTests.OtherModule.f)) == "method `f`\n"
517518
end
518519
end
520+
@testset "Interpolation" begin
521+
let fmt = expr -> Markdown.plain(eval(:(@doc $expr)))
522+
@test occursin("f(x)", fmt(:(InterpolationTestModule.f)))
523+
@test occursin("x + 2", fmt(:(InterpolationTestModule.g)))
524+
end
525+
end
519526
@testset "utilities" begin
520527
@testset "keywords" begin
521528
@test DSE.keywords(M.T, first(methods(M.T))) == Symbol[]

0 commit comments

Comments
 (0)