diff --git a/docs/src/man/syntax.md b/docs/src/man/syntax.md index 98c82c62642..9ac5ece4f9a 100644 --- a/docs/src/man/syntax.md +++ b/docs/src/man/syntax.md @@ -148,6 +148,46 @@ Order = [:type] ``` ```` +You may have some names in your package that are unexported but are still part +of your package's public API. You can tell Documenter about these names by +defining the `NONEXPORTED_PUBLIC_NAMES` vector in your package. The names in +`NONEXPORTED_PUBLIC_NAMES` will be treated as public by Documenter. Example +usage: + +```julia +module MyPackage + +export a + +const NONEXPORTED_PUBLIC_NAMES = Symbol[:b, :c] + +""" +`a` is exported, and it is part of the public API. +""" +function a end + +""" +`b` is not exported, but it is part of the public API. +""" +function b end + +""" +`c` is not exported, but it is part of the public API. +""" +function c end + +""" +`d` is not exported, and it is private (internal). +""" +function d end + +end # module +``` + +In the above example, `a`, `b`, and `c` will be considered by Documenter to be +public (even though `b` and `c` are not exported). `d` will be considered to be +private. + !!! note When more complex sorting is needed then use `@docs` to define it diff --git a/src/Expanders.jl b/src/Expanders.jl index f798d1fba39..ebc149bf7a6 100644 --- a/src/Expanders.jl +++ b/src/Expanders.jl @@ -367,6 +367,15 @@ end const AUTODOCS_DEFAULT_ORDER = [:module, :constant, :type, :function, :macro] +@inline function _name_is_public_but_nonexported(m::Module, name::Symbol) + isdefined(m, :NONEXPORTED_PUBLIC_NAMES) || return false + return name in getfield(m, :NONEXPORTED_PUBLIC_NAMES) +end + +@inline function name_is_public(m::Module, name::Symbol) + return Base.isexported(m, name) || _name_is_public_but_nonexported(m, name) +end + function Selectors.runner(::Type{AutoDocsBlocks}, x, page, doc) curmod = get(page.globals.meta, :CurrentModule, Main) fields = Dict{Symbol, Any}() @@ -402,8 +411,8 @@ function Selectors.runner(::Type{AutoDocsBlocks}, x, page, doc) for mod in modules for (binding, multidoc) in Documenter.DocSystem.getmeta(mod) # Which bindings should be included? - isexported = Base.isexported(mod, binding.var) - included = (isexported && public) || (!isexported && private) + ispublic = name_is_public(mod, binding.var) + included = (ispublic && public) || (!ispublic && private) # What category does the binding belong to? category = Documenter.DocSystem.category(binding) if category in order && included @@ -416,11 +425,11 @@ function Selectors.runner(::Type{AutoDocsBlocks}, x, page, doc) path = normpath(docstr.data[:path]) object = Utilities.Object(binding, typesig) if isempty(pages) - push!(results, (mod, path, category, object, isexported, docstr)) + push!(results, (mod, path, category, object, ispublic, docstr)) else for p in pages if endswith(path, p) - push!(results, (mod, p, category, object, isexported, docstr)) + push!(results, (mod, p, category, object, ispublic, docstr)) break end end @@ -446,7 +455,7 @@ function Selectors.runner(::Type{AutoDocsBlocks}, x, page, doc) # Finalise docstrings. nodes = DocsNode[] - for (mod, path, category, object, isexported, docstr) in results + for (mod, path, category, object, ispublic, docstr) in results if haskey(doc.internal.objects, object) push!(doc.internal.errors, :autodocs_block) @warn(""" diff --git a/test/public_names.jl b/test/public_names.jl new file mode 100644 index 00000000000..51849ee2695 --- /dev/null +++ b/test/public_names.jl @@ -0,0 +1,37 @@ +using Documenter +using Test + +module TestingNonExportedPublicNames + +export f + +const NONEXPORTED_PUBLIC_NAMES = Symbol[:g] + +""" +f is exported, and it is part of the public API +""" +function f end + +""" +g is not exported, but it is part of the public API +""" +function g end + +""" +h is not exported, and it is private (internal) +""" +function h end + +end # end module TestingNonExportedPublicNames + +@test Documenter.Expanders.name_is_public(TestingNonExportedPublicNames, :f) +@test Documenter.Expanders.name_is_public(TestingNonExportedPublicNames, :g) +@test !Documenter.Expanders.name_is_public(TestingNonExportedPublicNames, :h) + +@test Base.isexported(TestingNonExportedPublicNames, :f) +@test !Base.isexported(TestingNonExportedPublicNames, :g) +@test !Base.isexported(TestingNonExportedPublicNames, :h) + +@test !Documenter.Expanders._name_is_public_but_nonexported(TestingNonExportedPublicNames, :f) +@test Documenter.Expanders._name_is_public_but_nonexported(TestingNonExportedPublicNames, :g) +@test !Documenter.Expanders._name_is_public_but_nonexported(TestingNonExportedPublicNames, :h) diff --git a/test/runtests.jl b/test/runtests.jl index 34fc98fc2e4..c46a7f9d5bc 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -76,3 +76,5 @@ include("TestUtilities.jl"); using .TestUtilities @info "doctest() Documenter's manual" @quietly include("manual.jl") end + +include("public_names.jl")