Skip to content

Commit 9f2af95

Browse files
committed
Add alias_methods_as_function=true
This adds the keyword argument `alias_methods_as_function` to `InterLinks` (`true` by default) which adds `function` aliases for `method` items in any inventory that is loaded. An alias is only created if it is unambiguous, i.e., if there is only a single documented method for a function and no existing docstring for the function. The feature is to work around Documenter's `@autodocs` inserting method docstring instead of function docstrings and the fact that method docstrings can be exceedingly verbose to reference in an `@extref`. With the alias, an `@extref` can be, e.g., [`Documenter.Selectors.dispatch`](@extref) instead of the verbose [`Documenter.Selectors.dispatch`](@extref `Documenter.Selectors.dispatch-Union{Tuple{T}, Tuple{Type{T}, Vararg{Any}}} where T<:Documenter.Selectors.AbstractSelector`) that would be necessary without `alias_method_as_function=false`.
1 parent 524b3b6 commit 9f2af95

File tree

6 files changed

+110
-16
lines changed

6 files changed

+110
-16
lines changed

docs/src/howtos.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ Essentially, what we've done here is to open a Julia REPL like we normally would
135135
The above routine is how the local [inventory files](https://github.com/JuliaDocs/DocumenterInterLinks.jl/tree/master/docs/src/inventories) used in this documentation were generated. Using the TOML format is recommended for any inventory that will be committed to Git, as it is both human-readable and easier for `git` to track.
136136

137137
!!! warning
138-
Make sure that `prettyurls=true` in [`Documenter.makedocs`](@ref), or, more specifically, that the `prettyurls` option is not set conditionally with something like `prettyurls = get(ENV, "CI", nothing) == "true"`. This would cause a mismatch between the locally generated inventory and the deployed documentation.
138+
Make sure that `prettyurls=true` in [`Documenter.makedocs`](@extref), or, more specifically, that the `prettyurls` option is not set conditionally with something like `prettyurls = get(ENV, "CI", nothing) == "true"`. This would cause a mismatch between the locally generated inventory and the deployed documentation.
139139

140140

141141
Some local inventory files are also available in the [project wiki](https://github.com/JuliaDocs/DocumenterInterLinks.jl/wiki/Inventory-File-Repository). You may contribute your own generated inventories there.

docs/src/syntax.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ where the latter two are unnecessarily verbose, as `Documenter.makedocs` is alre
5555

5656
The short-circuit mechanism only works if the project name used in the instantiation of [`InterLinks`](@ref) matches the package name as it occurs in the fully specified name of any code object. That is, name the project `"Documenter"`, not, e.g., `"Documenter121"` for version `1.2.1` of `Documenter`.
5757

58-
When this is not possible, e.g. for the `Julia` project which contains many different modules without a common prefix (`Base`, `Core`, `LinearAlgebra`, …), it is best to declare that project as the *first* element in [`InterLinks`](@ref). That way,
58+
When this is not possible, e.g., for the `Julia` project which contains many different modules without a common prefix (`Base`, `Core`, `LinearAlgebra`, …), it is best to declare that project as the *first* element in [`InterLinks`](@ref). That way,
5959

6060
```
6161
[`Base.sort!`](@extref)
@@ -78,11 +78,11 @@ looks in the `Julia` project first, avoiding the need for
7878

7979
With the [Performance Tips](@ref) in mind, not all of the [10 possible Syntax forms](@ref Syntax) are recommended in practice. For maximum clarity and performance, use the following guidelines:
8080

81-
1. When referencing section headers in another project, e.g. the [Basic Markdown](@extref Documenter Basic-Markdown) section in Documenter's documentation, look up the appropriate sluggified name:
81+
1. When referencing section headers in another project, e.g., the [Basic Markdown](@extref Documenter Basic-Markdown) section in Documenter's documentation, look up the appropriate sluggified name:
8282

8383
```@example syntax
8484
using DocumenterInterLinks # hide
85-
links = InterLinks("Documenter" => ("https://documenter.juliadocs.org/stable/", joinpath(@__DIR__, "inventories", "Documenter.toml")),) # hide
85+
links = InterLinks("Documenter" => ("https://documenter.juliadocs.org/stable/", joinpath(@__DIR__, "inventories", "Documenter.toml")),"Julia" => ("https://docs.julialang.org/en/v1/", joinpath(@__DIR__, "inventories", "Julia.toml")),) # hide
8686
links["Documenter"]("Basic Markdown")
8787
```
8888

@@ -100,27 +100,27 @@ With the [Performance Tips](@ref) in mind, not all of the [10 possible Syntax fo
100100

101101
Make sure that `Documenter` is a project name in `links` (see [Performance Tips](@ref)).
102102

103-
This gets slightly more complicated when the code object is a "method" (where the docstring is for specific types of arguments), e.g., [`Documenter.parseblock`](@extref `Documenter.parseblock-Tuple{AbstractString, Any, Any}`). You will generally have to look up the full name
103+
This gets slightly more complicated when the code object is a "method" (where the docstring is for specific types of arguments), e.g., [`Base.Filesystem.cd`](@extref `Base.Filesystem.cd-Tuple{AbstractString}`). You will generally have to look up the full name
104104

105105
```@example syntax
106-
links["Documenter"]("parseblock")
106+
links["Julia"]("Base.Filesystem.cd")
107107
```
108108

109109
and then use form (3),
110110

111111
```
112-
[`Documenter.parseblock`](@extref `Documenter.parseblock-Tuple{AbstractString, Any, Any}`)
112+
[`Base.Filesystem.cd`](@extref `Base.Filesystem.cd-Tuple{AbstractString}`)
113113
```
114114

115-
The use of backticks around the full method name is optional, but recommended when there are spaces in the name.
115+
to link to a specific method. The use of backticks around the full method name is optional, but recommended especially when there are spaces in the type description. Note that if there is only a single method for a function, [`InterLinks`](@ref) by default (due to `alias_methods_as_function`) will add an alias that links the function name to that method docstring, allowing to use the shorter function name as a convenient target for the reference.
116116

117117
If the module name of the object cannot match the project name (e.g., for the `Julia` documentation, which contains docstrings for `Base`, `Core`, `LinearAlgebra`, etc.), use form (5),
118118

119119
```
120120
[`Base.sort!`](@extref Julia)
121121
```
122122

123-
3. When referencing a page, e.g. the [Home page of the Documenter documentation](@extref Documenter :doc:`index`), use form (9):
123+
3. When referencing a page, e.g., the [Home page of the Documenter documentation](@extref Documenter :doc:`index`), use form (9):
124124

125125
```
126126
[Home page of the Documenter documentation](@extref Documenter :doc:`index`)

docs/src/write_inventory.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ For a Documenter-based project, the automatic inventory contains:
2020
!!! info
2121
You probably will not need to worry about the information in this section.
2222

23-
The inventory for a `Documenter`-based documentation includes [entries](@extref `DocInventories.InventoryItem`) for docstrings using an [ad-hoc](@ref Compatibility-with-Sphinx) `jl` domain. The `role` for each entry matches how `Documenter` identifies the underlying object with [`Documenter.doccat`](@extref `Documenter.doccat-Tuple{Documenter.Object}`). You will find this identification as part of how the docstring shows in the documentation; for example, note the "— Type" in the header of [` DocumenterInterLinks.InterLinks`](@ref). The `role` that will be written to the inventory is simply the lowercase string of this identification. Currently, `Documenter` uses the following:
23+
The inventory for a `Documenter`-based documentation includes [entries](@extref `DocInventories.InventoryItem`) for docstrings using an [ad-hoc](@ref Compatibility-with-Sphinx) `jl` domain. The `role` for each entry matches how `Documenter` identifies the underlying object with [`Documenter.doccat`](@extref). You will find this identification as part of how the docstring shows in the documentation; for example, note the "— Type" in the header of [` DocumenterInterLinks.InterLinks`](@ref). The `role` that will be written to the inventory is simply the lowercase string of this identification. Currently, `Documenter` uses the following:
2424

2525
* "Macro" (role `macro`): for macros. For example, ```":jl:macro:`Base.@inbounds`"``` for [`Base.@inbounds`](@extref Julia :jl:macro:`Base.@inbounds`).
2626
* "Keyword" (role `keyword`): for Julia keywords (used in the documentation of the Julia language itself, only). For example, ```":jl:keyword:`if`"``` for [the `if` keyword](@extref Julia :jl:keyword:`if`).

src/interlinks.jl

Lines changed: 63 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
using Documenter: Plugin
2-
using DocInventories: Inventory, uri, spec, split_url
2+
using DocInventories: Inventory, InventoryItem, uri, spec, split_url, search_in_inventory
33

44

55
"""
@@ -13,7 +13,8 @@ links = InterLinks(
1313
"https://project3.url/",
1414
joinpath(@__DIR__, "src", "interlinks", "inventory.file")
1515
);
16-
default_inventory_file="objects.inv"
16+
default_inventory_file="objects.inv",
17+
alias_methods_as_function=true,
1718
)
1819
```
1920
@@ -76,6 +77,29 @@ any of the following forms:
7677
7778
* A [`DocInventories.Inventory`](@extref) instance.
7879
80+
# Keyword arguments
81+
82+
* `default_inventory_file`: The file name for the inventory file to use if the
83+
"inventory location" is given as the root URL. Since both Sphinx and
84+
Documenter automatically write `objects.inv`, there is little conceivable
85+
reason to set this to something other than the default `objects.inv`.
86+
* `alias_methods_as_function`: If `true` (default), for any inventory loaded
87+
from a file or URL, automatically add a `:jl:function:` alias for any
88+
`:jl:method` if that alias is unambiguous. This accounts for Documenter
89+
preferentially generating method-docstrings from `@autodoc` blocks, even if
90+
that method is the only method for the underlying function. For example, the
91+
[`Documenter.Selectors.dispatch`](@extref) function only has a
92+
method-docstring that would have to be referenced as the excessively long
93+
94+
```
95+
[`Documenter.Selectors.dispatch`](@extref `Documenter.Selectors.dispatch-Union{Tuple{T}, Tuple{Type{T}, Vararg{Any}}} where T<:Documenter.Selectors.AbstractSelector`)
96+
```
97+
98+
With the alias, this can be shortened to
99+
```[`Documenter.Selectors.dispatch`](@extref)```. No alias will be created
100+
if there are docstrings for multiple methods of the same function, or if
101+
there is an existing function docstring.
102+
79103
# Properties
80104
81105
* `names`: A list of project names
@@ -175,7 +199,11 @@ end
175199

176200

177201

178-
function InterLinks(mapping...; default_inventory_file="objects.inv")
202+
function InterLinks(
203+
mapping...;
204+
default_inventory_file="objects.inv",
205+
alias_methods_as_function=true
206+
)
179207
names = String[]
180208
inventories_list = Inventory[]
181209
try
@@ -204,6 +232,9 @@ function InterLinks(mapping...; default_inventory_file="objects.inv")
204232
try
205233
inventory = Inventory(source; root_url=root_url)
206234
@debug "Successfully loaded inventory $(repr(project)) from source $(repr(source))."
235+
if alias_methods_as_function
236+
_set_method_aliases!(inventory)
237+
end
207238
break # stop after first successful source
208239
catch exception
209240
msg = "Failed to load inventory $(repr(project)) from possible source $(repr(source))."
@@ -263,6 +294,34 @@ function (links::InterLinks)(search)
263294
end
264295

265296

297+
function _set_method_aliases!(inventory)
298+
aliases = Dict{String,Vector{String}}()
299+
for method_item in search_in_inventory(inventory, ":jl:method:`")
300+
func_name = replace(method_item.name, r"-.*$" => "")
301+
if haskey(aliases, func_name)
302+
push!(aliases[func_name], spec(method_item))
303+
else
304+
aliases[func_name] = [spec(method_item)]
305+
end
306+
end
307+
for (func_name, method_specs) in aliases
308+
func_spec = ":jl:function:`$func_name`"
309+
if length(method_specs) == 1
310+
method_spec = method_specs[1]
311+
if isnothing(inventory[func_spec])
312+
@debug "Setting alias $method_spec -> $func_spec"
313+
push!(inventory, InventoryItem(func_spec => uri(inventory[method_spec])))
314+
else
315+
@debug "Not setting alias $method_spec -> $func_spec: function entry already exists"
316+
end
317+
else
318+
@debug "Not setting alias to $func_spec: ambiguous" method_specs
319+
end
320+
end
321+
return inventory
322+
end
323+
324+
266325
"""Find an `@extref` link in any of the [`InterLinks`](@ref) inventories.
267326
268327
```julia
@@ -273,7 +332,7 @@ finds `extref` in `links` and returns the full URL that resolves the link.
273332
274333
# Arguments
275334
276-
* `links`: the [`InterLinks`] instance to resolve the reference in
335+
* `links`: the [`InterLinks`](@ref) instance to resolve the reference in
277336
* `extref`: a string of the form
278337
`"@extref [project] [[:domain][:role]:]name"`
279338
"""

test/test_fallback.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,8 @@ end
193193
"DocumenterInterLinks" => (
194194
"http://juliadocs.org/DocumenterInterLinks.jl/dev/",
195195
joinpath(splitext(@__FILE__)[1], "DocumenterInterLinks.toml")
196-
),
196+
);
197+
alias_methods_as_function=false,
197198
)
198199

199200
push!(
@@ -225,7 +226,6 @@ end
225226
) do dir, result, success, backtrace, output
226227

227228
if DOCUMENTER_VERSION >= v"1.3.0-dev"
228-
@test success
229229
@test contains(
230230
output,
231231
"Warning: ExternalFallbacks resolution of \"makedocs\" is ambiguous"

test/test_interlinks.jl

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using Logging
12
using Test
23
using TestingUtilities: @Test
34
using DocInventories
@@ -207,3 +208,37 @@ end
207208
["@extref Documenter :std:doc:`index`", "@extref Julia :std:doc:`index`",]
208209

209210
end
211+
212+
213+
@testset "method aliases" begin
214+
captured = IOCapture.capture(; passthrough=false) do
215+
with_logger(ConsoleLogger(stdout, Logging.Debug)) do
216+
InterLinks(
217+
"Documenter" => (
218+
"https://documenter.juliadocs.org/stable/",
219+
joinpath(@__DIR__, "inventories", "Documenter.toml")
220+
),
221+
"Julia" => (
222+
"https://docs.julialang.org/en/v1/",
223+
joinpath(@__DIR__, "inventories", "Julia.toml")
224+
);
225+
alias_methods_as_function=true,
226+
)
227+
end
228+
end
229+
@test contains(
230+
captured.output,
231+
"Setting alias :jl:method:`Documenter.nodocs-Tuple{Any}` -> :jl:function:`Documenter.nodocs`"
232+
)
233+
nodocs_items = captured.value["Documenter"]("Documenter.nodocs")
234+
@test length(nodocs_items) == 2
235+
@test Set([item.role for item in nodocs_items]) == Set(["function", "method"])
236+
@test contains(
237+
captured.output,
238+
"Not setting alias to :jl:function:`Documenter.HTMLWriter.get_url`: ambiguous"
239+
)
240+
@test contains(
241+
captured.output,
242+
"Not setting alias :jl:method:`Base.first-Tuple{AbstractString, Integer}` -> :jl:function:`Base.first`: function entry already exists"
243+
)
244+
end

0 commit comments

Comments
 (0)