-
Notifications
You must be signed in to change notification settings - Fork 34
Expand file tree
/
Copy pathtemplates.jl
More file actions
132 lines (106 loc) · 4.6 KB
/
templates.jl
File metadata and controls
132 lines (106 loc) · 4.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
const expander = Core.atdoc
const setter! = Core.atdoc!
"""
$(:SIGNATURES)
Set the docstring expander function to first call `func` before calling the default expander.
To remove a hook that has been applied using this method call [`hook!()`](@ref).
"""
hook!(func) = setter!((args...) -> expander(func(args...)...))
"""
$(:SIGNATURES)
Reset the docstring expander to only call the default expander function. This clears any
'hook' that has been set using [`hook!(func)`](@ref).
"""
hook!() = setter!(expander)
"""
$(:SIGNATURES)
Defines a docstring template that will be applied to all docstrings in a module that match
the specified category or tuple of categories of documented bindings.
Effectively, it replaces all the matching docstrings in the module with the template.
The `DOCSTRING` abbreviation can be used to splice the original docstring into the
replacement docstring generated from the template.
# Examples
```julia
@template DEFAULT =
\"""
\$(SIGNATURES)
\$(DOCSTRING)
\"""
```
Note that a significant limitation of docstring templates is that the
abbreviations used will be declared separately from the bindings that they
operate on, which means that they will not have access to the bindings
`Expr`'s. That will disable `TYPEDSIGNATURES` and `SIGNATURES` from showing
default [keyword ]argument values in docstrings.
`DEFAULT` is the default template that is applied to a docstring if no other template
definitions match the documented expression. The `DOCSTRING` abbreviation is used to mark
the location in the template where the actual docstring body will be spliced into each
docstring.
```julia
@template (FUNCTIONS, METHODS, MACROS) =
\"""
\$(SIGNATURES)
\$(DOCSTRING)
\$(METHODLIST)
\"""
```
A tuple of categories can be specified when a docstring template should be used for several
different categories.
```julia
@template MODULES = ModName
```
The template definition above will define a template for module docstrings based on the
template for modules found in module `ModName`.
!!! note
Supported categories are `DEFAULT`, `FUNCTIONS`, `METHODS`, `MACROS`, `TYPES`,
`MODULES`, and `CONSTANTS`.
"""
macro template(ex)
template(__source__, __module__, ex)
end
const TEMP_SYM = gensym("templates")
function template(src::LineNumberNode, mod::Module, ex::Expr)
Meta.isexpr(ex, :(=), 2) || error("invalid `@template` syntax.")
template(src, mod, ex.args[1], ex.args[2])
end
function template(source::LineNumberNode, mod::Module, tuple::Expr, docstr::Union{Symbol, Expr})
Meta.isexpr(tuple, :tuple) || error("invalid `@template` syntax on LHS.")
isdefined(mod, TEMP_SYM) || Core.eval(mod, :(const $(TEMP_SYM) = $(Dict{Symbol, Vector}())))
local block = Expr(:block)
for category in tuple.args
local key = Meta.quot(category)
local vec = Meta.isexpr(docstr, :string) ?
Expr(:vect, docstr.args...) : :($(docstr).$(TEMP_SYM)[$(key)])
push!(block.args, :($(TEMP_SYM)[$(key)] = $(vec)))
end
push!(block.args, nothing)
return esc(block)
end
function template(src::LineNumberNode, mod::Module, sym::Symbol, docstr::Union{Symbol, Expr})
template(src, mod, Expr(:tuple, sym), docstr)
end
# The signature for the atdocs() calls changed in v0.7
# On v0.6 and below it seems it was assumed to be (docstr::String, expr::Expr), but on v0.7
# it is (source::LineNumberNode, mod::Module, docstr::String, expr::Expr)
function template_hook(source::LineNumberNode, mod::Module, docstr, expr::Expr)
docstr = _capture_expression(docstr, expr)
# During macro expansion we only need to wrap docstrings in special
# abbreviations that later print out what was before and after the
# docstring in it's specific template. This is only done when the module
# actually defines templates.
if isdefined(mod, TEMP_SYM)
dict = getfield(mod, TEMP_SYM)
# We unwrap interpolated strings so that we can add the `:before` and
# `:after` abbreviations. Otherwise they're just left as is.
unwrapped = Meta.isexpr(docstr, :string) ? docstr.args : [docstr]
before, after = Template{:before}(dict), Template{:after}(dict)
# Rebuild the original docstring, but with the template abbreviations
# surrounding it.
docstr = Expr(:string, before, unwrapped..., after)
end
return (source, mod, docstr, expr)
end
# This definition looks a bit weird, but in combination with hook!() the effect
# is that template_hook() will fall back to calling the default expander().
template_hook(args...) = args
get_template(t::Dict, k::Symbol) = haskey(t, k) ? t[k] : get(t, :DEFAULT, Any[DOCSTRING])