///|
/// The builtin table from which a completion command originates.
pub(all) enum CommandKind {
SymbolCommand
FunctionCommand
MacroCommand
ImplicitCommand
} derive(ToJson)
///|
/// How a command consumes input. Only RegularCommand can be completed by
/// appending bracketed args alone; use templates for the other forms.
pub(all) enum CommandSyntax {
RegularCommand
InfixCommand
DeclarationCommand
DelimiterCommand
EnvironmentCommand
VerbatimCommand
DefinitionCommand
TokenCommand
GroupCommand
} derive(ToJson)
///|
/// An explicit argument, in input order. A missing arg_type means the builtin
/// macro's argument type is not declared. Optional arguments use square brackets.
pub struct CommandArgument {
arg_type : ArgType?
optional : Bool
} derive(ToJson)
///|
/// A snapshot of one public builtin command, including its leading backslash.
/// args describes explicit arguments, not an infix operand or an implicit body.
/// modes is absent when a macro's mode restrictions are not statically known.
/// unicode/group describe its registered symbol, when available.
/// templates use named ${field} placeholders, as accepted by CodeMirror's
/// snippetCompletion. Regular commands list the form without optional args first.
/// Templates are editing skeletons, not necessarily valid until fields are filled.
pub struct CommandInfo {
name : String
kind : CommandKind
syntax : CommandSyntax
args : Array[CommandArgument]
modes : Array[Mode]?
unicode : String?
group : String?
templates : Array[String]
} derive(ToJson)
///|
/// Returns fresh, name-sorted completion metadata for the parser's builtins.
/// Includes symbols, functions, static/dynamic macros, implicit commands and
/// supported starred command forms. Internal names and raw Unicode aliases
/// are excluded. No handlers are run and no parser state is changed.
/// This describes default builtins, not settings-dependent or user macros.
pub fn builtin_commands() -> Array[CommandInfo] {
let commands : Map[String, CommandInfo] = Map([])
for
(mode, symbols) in [
(Math, builtin_symbols.math),
(Text, builtin_symbols.text),
] {
for name, symbol in symbols {
guard is_public_builtin_command(name) else { continue }
let modes = commands
.get(name)
.map_or([], info => info.modes.unwrap_or([]))
modes.push(mode)
let info = commands
.get(name)
.unwrap_or({
name,
kind: SymbolCommand,
syntax: RegularCommand,
args: [],
modes: None,
unicode: if symbol.replacement == "" {
None
} else {
Some(symbol.replacement)
},
group: Some(command_symbol_group_name(symbol.group)),
templates: [name],
})
commands[name] = { ..info, modes: Some(modes), }
}
}
// Match parser resolution: macro expansion, then functions, then symbols.
for name, spec in builtin_function_registry.entries {
guard is_public_builtin_command(name) else { continue }
commands[name] = function_command_info(name, spec, commands.get(name))
}
for name, _ in builtin_dynamic_macro_definitions {
guard is_public_builtin_command(name) else { continue }
commands[name] = macro_command_info(name, [], commands.get(name))
}
for name, definition in builtin_static_macro_definitions {
guard is_public_builtin_command(name) else { continue }
let count = match definition {
Text(expansion) => inferred_argument_count(expansion)
Expansion(expansion) => expansion.num_args
}
commands[name] = macro_command_info(
name,
Array::makei(count, _ => { arg_type: None, optional: false, }),
commands.get(name),
)
}
for name in ["\\limits", "\\nolimits", "\\begingroup", "\\endgroup"] {
let info : CommandInfo = {
name,
kind: ImplicitCommand,
syntax: if name == "\\limits" || name == "\\nolimits" {
TokenCommand
} else {
GroupCommand
},
args: [],
modes: Some(
if name == "\\limits" || name == "\\nolimits" {
[Math]
} else {
[Math, Text]
},
),
unicode: None,
group: None,
templates: [name],
}
commands[name] = complete_command_signature(info)
}
let result : Array[CommandInfo] = []
for _, info in commands {
result.push(info)
if info.name == "\\operatorname" ||
info.name == "\\tag" ||
info.name == "\\hspace" ||
info.name == "\\verb" {
result.push(
complete_command_signature({ ..info, name: info.name + "*", }),
)
}
}
result.sort_by((left, right) => left.name.compare(right.name))
result.map(info => {
..info,
templates: info.templates.map(template => {
template
.replace_all(old="#{", new="${")
.replace_all(old="\\{", new="\\\\{")
.replace_all(old="\\}", new="\\\\}")
}),
})
}
///|
/// Exports the builtin completion catalogue as a JSON array.
pub fn builtin_commands_json() -> Json {
builtin_commands().to_json()
}
///|
pub extend CommandKind with ToJson::{to_json}
///|
pub extend CommandSyntax with ToJson::{to_json}
///|
pub extend CommandArgument with ToJson::{to_json}
///|
pub extend CommandInfo with ToJson::{to_json}