Expand description
Built-in ex-commands registered as peers of motions / operators / text
objects in the unified CommandRegistry (DESIGN.md §5.2.1).
Each spec’s apply callback is intentionally thin: it only packages
the parsed args into the matching Effect variant. The host (App)
owns the side-effect implementation (file I/O, view options, echo
area, document swap, …) – this keeps the closures static-state-free
so they can later be loaded from a WASM plugin without redesign.
Coverage:
- Keyword form:
:w[rite],:q[uit],:wq/:x,:noh[lsearch],:reg[isters],:marks,:d[elete],:set,:e[dit]. - Delimiter-syntax form (Appendix B.2):
:s/.../.../[g],:%s/.../.../[g],:g/.../.../,:v/.../.../. These useArgs::Listto carry pattern / replacement / flags / body / inverted as positionalArgValues; the parser front-end strips the delimiter prefix and dispatches through the samegrammar::execute()as everything else.
(The list above is the original core; populate now also registers the
buffer / pane / tab commands, the :describe-* and :list-* help
family, the :lsp-* commands, :format, :tutor, :cd and more —
see ExBuiltins and :list-commands.)
Every builtin is registered as ex:<name>. Aliases (:w for
ex:write, :q for ex:quit, :e for ex:edit, …) are NOT
separate registry entries – they would inflate the CommandId
namespace and complicate :describe-command. Alias resolution is the
parser front-end’s job (expand_alias / ALIAS_TABLE in
lattice-host’s excommand module). New subsystem-coupled commands
follow the project naming rule: one dashed, namespaced alias
(lsp-format), no collapsed or generic forms.
§Examples
How the : front-end turns :w out.txt into an Effect: resolve
the name, let the spec parse the rest of the line, dispatch.
use lattice_core::{BufferId, Document};
use lattice_grammar::{
CancellationToken, CommandInvocation, CommandRegistry, Effect, ex_commands, execute,
};
use lattice_protocol::position::Position;
let mut registry = CommandRegistry::new();
ex_commands::populate(&mut registry);
// `w` -> `ex:write` is the host alias table's job; the registry holds
// the canonical name.
let id = registry.id_by_name("ex:write").unwrap();
let spec = registry.ex_command_spec(id).unwrap();
let args = (spec.parse_args)("out.txt", false).unwrap();
let mut doc = Document::from_text("hello\n");
let effect = execute(
®istry,
&mut doc,
BufferId(0),
Position::ZERO,
CommandInvocation::of(id).with_args(args),
&CancellationToken::never(),
)
.unwrap();
// The command only describes the work; the host does the I/O.
match effect {
Effect::SaveBuffer { path } => assert_eq!(path, Some("out.txt".into())),
other => panic!("unexpected {other:?}"),
}Structs§
- ExBuiltins
- The ids of the built-in ex-commands, as returned by
populate; mirrorsBuiltinsfor motions / operators / text objects.
Functions§
- populate
- Register every built-in ex-command into
registryand return the ids callers need by field (seeExBuiltins).