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 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206
|
defmodule ExDoc.Language do
@moduledoc false
@type spec_ast() :: term()
@typedoc """
The map has the following keys:
* `:module` - the module
* `:docs` - the docs chunk
* `:language` - the language callback
* `:id` - module page name
* `:title` - module display title
* `:type` - module type
* `:source_line` - the line where the module code is located, defmodule in Elixir, or -module in Erlang
* `:source_file` - the source file the module code is located, defmodule in Elixir, or -module in Erlang
* `:source_basedir` - the absolute directory where the Elixir/Erlang compiler was run.
* `:callback_types` - a list of types that are considered callbacks
* `:nesting_info` - a `{nested_title, nested_context}` tuple or `nil`.
For example, `"A.B.C"` becomes `{"C", "A.B."}`.
* `:private` - a map with language-specific data
"""
@type module_data() :: %{
module: module(),
docs: tuple(),
language: module(),
id: String.t(),
title: String.t(),
type: atom() | nil,
source_basedir: String.t(),
source_file: String.t() | nil,
source_line: non_neg_integer(),
callback_types: [atom()],
nesting_info: {String.t(), String.t()} | nil,
private: map()
}
@doc """
Returns a map with module information.
"""
@callback module_data(module(), tuple(), ExDoc.Config.t()) :: module_data() | :skip
@doc """
Returns a map with function information or an atom `:skip`.
The map has the following keys:
* `:source_line` - the line where the code is located, def/defp in Elixir, foo(...) in Erlang
* `:source_file` - the source file where the code in located
* `:specs` - a list of specs that will be later formatted by `c:typespec/2`
* `:doc_fallback` - if set, a 0-arity function that returns DocAST which
will be used as fallback to empty docs on the function node
* `:extra_annotations` - additional annotations
"""
@callback function_data(entry :: tuple(), module_data()) ::
%{
source_line: non_neg_integer() | nil,
source_file: String.t() | nil,
specs: [spec_ast()],
# TODO: change to following on Elixir 1.15. It trips mix formatter between 1.14 and 1.15
# doc_fallback: (-> ExDoc.DocAST.t()) | nil,
doc_fallback: (... -> ExDoc.DocAST.t()) | nil,
extra_annotations: [String.t()]
}
| :skip
@doc """
Returns a map with callback information.
The map has the following keys:
* `:source_line` - the line where the code is located
* `:source_file` - the source file where the code in located
* `:signature` - the signature
* `:specs` - a list of specs that will be later formatted by `c:typespec/2`
* `:extra_annotations` - additional annotations
"""
@callback callback_data(entry :: tuple(), module_data()) ::
%{
source_line: non_neg_integer() | nil,
source_file: String.t() | nil,
signature: [binary()],
specs: [spec_ast()],
extra_annotations: [String.t()]
}
@doc """
Returns a map with type information.
The map has the following keys:
* `:type` - `:type` or `:opaque` or `:nominal`
* `:source_line` - the line where the code is located
* `:source_file` - the source file where the code in located
* `:signature` - the signature
* `:spec` - a spec that will be later formatted by `c:typespec/2`
"""
@callback type_data(entry :: tuple(), spec :: term()) ::
%{
type: :type | :opaque | :nominal,
source_line: non_neg_integer(),
source_file: String.t() | nil,
signature: [binary()],
spec: spec_ast(),
extra_annotations: [String.t()]
}
@doc """
Autolinks docs.
"""
@callback autolink_doc(doc :: ExDoc.DocAST.t(), opts :: keyword()) :: ExDoc.DocAST.t()
@doc """
Autolinks typespecs.
"""
@callback autolink_spec(spec :: term(), opts :: keyword()) :: iodata()
@doc """
Returns information for syntax highlighting.
"""
@callback highlight_info() :: %{
language_name: String.t(),
lexer: module(),
opts: keyword()
}
@doc """
Return an attribute in the canonical representation.
"""
@callback format_spec_attribute(%ExDoc.FunctionNode{} | %ExDoc.TypeNode{}) :: String.t()
@doc """
Parse a module.function string and return it.
"""
@callback parse_module_function(String.t()) ::
{:local, function :: atom()}
| {:remote, module :: module(), function :: atom()}
| :error
@doc """
Parse a module string and return it.
"""
@callback parse_module(String.t(), mode :: :regular_link | :custom_link) ::
{:module, atom()} | :error
@doc """
Return a URL to autoimported function if atom+arity are autoimported
"""
@callback try_autoimported_function(
name :: atom(),
arity :: non_neg_integer(),
mode :: :regular_link | :custom_link,
opts :: keyword(),
original_text :: String.t()
) ::
nil | String.t()
@doc """
Return a URL to built-in type if atom+arity are built-in
"""
@callback try_builtin_type(
name :: atom(),
arity :: non_neg_integer(),
mode :: :regular_link | :custom_link,
opts :: keyword(),
original_text :: String.t()
) ::
nil | String.t()
def get(:elixir, _module), do: {:ok, ExDoc.Language.Elixir}
def get(:erlang, _module), do: {:ok, ExDoc.Language.Erlang}
def get(language, module) when is_atom(language) and is_atom(module) do
ExDoc.Utils.warn(
"skipping module #{module}, reason: unsupported language (#{language})",
[]
)
:error
end
end
|