pub struct GrammarEnv<'a> {Show 18 fields
pub scope_resolver: Option<&'a dyn ScopeResolver>,
pub comment_syntax: Option<&'a CommentSyntax>,
pub syntax: Option<&'a Arc<dyn Any + Send + Sync>>,
pub indent: IndentUnit,
pub indent_resolver: Option<&'a dyn IndentResolver>,
pub textwidth: WrapWidth,
pub native_format: NativeFormatIntents,
pub selection: Option<Range>,
pub last_find: Option<LastFind>,
pub fold_resolver: Option<&'a dyn FoldResolver>,
pub last_search: Option<&'a LastSearch>,
pub marks: Option<&'a dyn MarkResolver>,
pub viewport: Option<&'a dyn ViewportResolver>,
pub nostartofline: bool,
pub curswant: Option<Curswant>,
pub display: Option<&'a dyn DisplayResolver>,
pub curswant_out: Option<&'a Mutex<Option<Curswant>>>,
pub scrolloff: u32,
}Expand description
The per-dispatch environment: everything the host knows that a
command’s apply may need but the grammar layer cannot derive for
itself. Bundled into ONE value so the dispatch seam carries a single
env rather than a widening parameter list (the long-term-fit choice
over parallel params). Copy; default() is the no-input case, and
commands that read nothing from the env (iw, ap, i{) are
unaffected by what it carries.
It has widened twice, and the name followed on the second:
- N.1.6 (2026-06-10) introduced it as a text-object-only env —
the tree-sitter
scope_resolver(af/ac, N.1.4) and thecomment_syntax(aC/iC). - TS.1 added the tree-sitter
syntaxsnapshot for actions (borrowedArc<dyn Any>soexecute_actioncanArc::cloneit into the ownedActionContext::syntax— a&dyn Anyalone can’t recover theArc), which already madeTextObjectEnva misnomer. - IN.0 renamed it to
GrammarEnvon adding a field read by operators (>/<), which would have made the old name actively misleading rather than merely stale.
Fields§
§scope_resolver: Option<&'a dyn ScopeResolver>Tree-sitter scope lookup for structural text objects and motions.
None on buffers with no parse.
comment_syntax: Option<&'a CommentSyntax>The buffer language’s comment leader, for aC / iC and reflow.
None when the language declares none.
syntax: Option<&'a Arc<dyn Any + Send + Sync>>TS.1: the per-dispatch tree-sitter snapshot, type-erased and borrowed so
it stays Copy. execute_action clones it into ActionContext::syntax;
motions / text-objects / operators ignore it (they read
scope_resolver). None = no parse.
indent: IndentUnitIN.0: one level of indentation, resolved by the host from
shiftwidth / expandtab / tabstop (including any
:setlocal override). Read by the > / < operators.
Not an Option: there is always a defensible answer, and
IndentUnit::default() is the registered option defaults, so a
caller that never resolved config indents like an unconfigured
buffer instead of like nothing. That keeps the ~40 test and
plugin call sites that build a default() env working without
each having to care about indentation.
indent_resolver: Option<&'a dyn IndentResolver>IN.7: per-line indent depth for the = operator, injected by
the host. None (the default) means = has no structural
source and reindents nothing – the same graceful-degradation
contract every other env field carries.
textwidth: WrapWidthRF.2: the buffer’s textwidth, resolved by the host (including
any :setlocal). Read by the reflow operator.
WrapWidth rather than a bare usize precisely so this struct
can keep its DERIVED Default: a usize would default to 0,
which is not a column anything wraps at, and all ~40 hand-built
default() envs would silently get a reflow that does nothing.
native_format: NativeFormatIntentsRF.5b: which intents this buffer handles natively. See
NativeFormatIntents.
selection: Option<Range>OS.2: the active region — the Visual/Select selection extent,
normalised so start <= end. None in Normal mode and on every
non-chord firing path.
Injected by the host from the SAME resolver that fills
lattice_mode::ActionContext::selection (MG.18e), rather than
re-derived here from the document’s selections. That is deliberate: two
answers to “what is the region” is precisely the drift OS.3 spent a fix
round removing from Checkboxes, and a plugin action seeing a different
region than a native mode handler reached the same way is the same bug
wearing a boundary.
None (the default) means no region, so the ~40 call sites that build a
default() env keep behaving exactly as they did.
last_find: Option<LastFind>VM.3c: the last f / F / t / T, so ; and , can be motions
rather than host actions.
Carried here for the reason syntax and selection had to be: ;
reaches the grammar through the ACTOR path on every real keystroke, so
a field missing here is a field the motion never sees no matter how
well the direct path is tested. None — the default, and the state
before the user has pressed any f — makes ; a no-op, which is what
vim does.
fold_resolver: Option<&'a dyn FoldResolver>VM.3i: the buffer’s fold edges, so zj / zk can be motions.
Carried here for the reason last_find is: zj reaches the grammar
through the ACTOR path on every real keystroke, so a field missing
here is one the motion never sees. None — no folds, or a caller
with no fold table — leaves zj / zk where they are.
last_search: Option<&'a LastSearch>VM.3d-2: the last search, so n / N / * / # can be motions.
Carried for the reason last_find is: they reach the grammar through
the ACTOR path on every real keystroke. None makes n fail with
E35, as vim does before any search.
marks: Option<&'a dyn MarkResolver>VM.3e: the mark table, so 'x / `x can be motions. Carried for
the reason last_search is. None makes every mark unset (E20).
viewport: Option<&'a dyn ViewportResolver>VM.3f: the lines the window shows, so H / M / L can be motions.
None — no window, or an invocation that isn’t one of them — makes
them fail without moving.
nostartofline: boolVM.3f: !startofline. Named for the vim option that turns the rule
OFF so that Default (false) is vim’s default.
curswant: Option<Curswant>VM.3g-1: the goal column j / k aim for. None — nothing has set one
yet — means “use the cursor’s own column”, which is what the first j
after any edit does in vim.
display: Option<&'a dyn DisplayResolver>VM.3g-2: display geometry for gj / gk / g0 / g$. None — no
renderer has reported one — leaves them behaving as j / k / 0 /
$, which is also what they do with wrap off.
curswant_out: Option<&'a Mutex<Option<Curswant>>>VM.3g-3: where a motion’s MotionResult::curswant override is
reported back to the caller. None — the default — discards it, which
is right for every caller that does not maintain a goal column.
The symmetric partner of Self::curswant above: that field carries
the goal INTO the motion, this one carries the motion’s answer back
out. It is a slot rather than a return value because a motion’s effect
reaches the host as an Effect::CursorMove, a WIT type and the wrong
place for per-dispatch host state — exclusive and notice are kept
off that boundary for the same reason.
A shared slot rather than &mut so GrammarEnv stays Copy, which
the tree-sitter snapshot field above already depends on; a Mutex
rather than a Cell because the owning DispatchEnv crosses the
Document trait into an async Pending<Effect> and must stay
Send + Sync. The lock is uncontended — one motion writes it once per
dispatch, on the same thread that reads it.
scrolloff: u32VM.3f: scrolloff, the margin H / L keep from the window’s edges.
0 — the default — leaves them on the first and last lines shown,
which is vim with scrolloff=0.
Trait Implementations§
Source§impl<'a> Clone for GrammarEnv<'a>
impl<'a> Clone for GrammarEnv<'a>
Source§fn clone(&self) -> GrammarEnv<'a>
fn clone(&self) -> GrammarEnv<'a>
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read more