1use crate::WitBoundary;
19use crate::lattice::plugin_host::types::{
20 AppEffect as WitAppEffect, FormatIntent as WitFormatIntent,
21 FormatRangePayload as WitFormatRangePayload, Hscroll as WitHscroll,
22 InsertLineEdit as WitInsertLineEdit, NarrowLinesPayload as WitNarrowLinesPayload,
23 OpenProviderViewPayload as WitOpenProviderViewPayload, PaneDirection as WitPaneDirection,
24 ScrollPos as WitScrollPos, ViewportPos as WitViewportPos,
25};
26use lattice_grammar::app_effect::{
27 AppEffect as NativeAppEffect, HScroll as NativeHScroll, InsertLineEdit as NativeInsertLineEdit,
28 PaneDirection as NativePaneDirection, ScrollPos as NativeScrollPos,
29 ViewportPos as NativeViewportPos,
30};
31use lattice_grammar::modal::{
32 ModalState as NativeModalState, SearchDirection as NativeSearchDirection,
33 VisualKind as NativeVisualKind,
34};
35use lattice_grammar::register::Register as NativeRegister;
36use lattice_grammar::registry::OperatorId;
37use lattice_protocol::ids::CommandId;
38
39fn wit_format_intent(intent: lattice_core::FormatIntent) -> WitFormatIntent {
45 match intent {
46 lattice_core::FormatIntent::Indent => WitFormatIntent::Indent,
47 lattice_core::FormatIntent::Reflow => WitFormatIntent::Reflow,
48 lattice_core::FormatIntent::Reformat => WitFormatIntent::Reformat,
49 }
50}
51
52fn native_format_intent(intent: WitFormatIntent) -> lattice_core::FormatIntent {
53 match intent {
54 WitFormatIntent::Indent => lattice_core::FormatIntent::Indent,
55 WitFormatIntent::Reflow => lattice_core::FormatIntent::Reflow,
56 WitFormatIntent::Reformat => lattice_core::FormatIntent::Reformat,
57 }
58}
59
60impl WitBoundary for NativeViewportPos {
61 type Wit = WitViewportPos;
62 fn to_wit(&self) -> Result<WitViewportPos, String> {
63 Ok(match self {
64 NativeViewportPos::Top => WitViewportPos::Top,
65 NativeViewportPos::Middle => WitViewportPos::Middle,
66 NativeViewportPos::Bottom => WitViewportPos::Bottom,
67 })
68 }
69 fn from_wit(w: WitViewportPos) -> Result<Self, String> {
70 Ok(match w {
71 WitViewportPos::Top => NativeViewportPos::Top,
72 WitViewportPos::Middle => NativeViewportPos::Middle,
73 WitViewportPos::Bottom => NativeViewportPos::Bottom,
74 })
75 }
76}
77
78impl WitBoundary for NativeScrollPos {
79 type Wit = WitScrollPos;
80 fn to_wit(&self) -> Result<WitScrollPos, String> {
81 Ok(match self {
82 NativeScrollPos::Top => WitScrollPos::Top,
83 NativeScrollPos::Center => WitScrollPos::Center,
84 NativeScrollPos::Bottom => WitScrollPos::Bottom,
85 })
86 }
87 fn from_wit(w: WitScrollPos) -> Result<Self, String> {
88 Ok(match w {
89 WitScrollPos::Top => NativeScrollPos::Top,
90 WitScrollPos::Center => NativeScrollPos::Center,
91 WitScrollPos::Bottom => NativeScrollPos::Bottom,
92 })
93 }
94}
95
96impl WitBoundary for NativePaneDirection {
97 type Wit = WitPaneDirection;
98 fn to_wit(&self) -> Result<WitPaneDirection, String> {
99 Ok(match self {
100 NativePaneDirection::Left => WitPaneDirection::Left,
101 NativePaneDirection::Down => WitPaneDirection::Down,
102 NativePaneDirection::Up => WitPaneDirection::Up,
103 NativePaneDirection::Right => WitPaneDirection::Right,
104 })
105 }
106 fn from_wit(w: WitPaneDirection) -> Result<Self, String> {
107 Ok(match w {
108 WitPaneDirection::Left => NativePaneDirection::Left,
109 WitPaneDirection::Down => NativePaneDirection::Down,
110 WitPaneDirection::Up => NativePaneDirection::Up,
111 WitPaneDirection::Right => NativePaneDirection::Right,
112 })
113 }
114}
115
116impl WitBoundary for NativeInsertLineEdit {
117 type Wit = WitInsertLineEdit;
118 fn to_wit(&self) -> Result<WitInsertLineEdit, String> {
119 Ok(match self {
120 NativeInsertLineEdit::CursorLineStart => WitInsertLineEdit::CursorLineStart,
121 NativeInsertLineEdit::CursorLineEnd => WitInsertLineEdit::CursorLineEnd,
122 NativeInsertLineEdit::CursorCharLeft => WitInsertLineEdit::CursorCharLeft,
123 NativeInsertLineEdit::CursorCharRight => WitInsertLineEdit::CursorCharRight,
124 NativeInsertLineEdit::DeleteWordBackward => WitInsertLineEdit::DeleteWordBackward,
125 NativeInsertLineEdit::DeleteToLineStart => WitInsertLineEdit::DeleteToLineStart,
126 NativeInsertLineEdit::KillToLineEnd => WitInsertLineEdit::KillToLineEnd,
127 NativeInsertLineEdit::IndentLine => WitInsertLineEdit::IndentLine,
128 NativeInsertLineEdit::DedentLine => WitInsertLineEdit::DedentLine,
129 })
130 }
131 fn from_wit(w: WitInsertLineEdit) -> Result<Self, String> {
132 Ok(match w {
133 WitInsertLineEdit::CursorLineStart => NativeInsertLineEdit::CursorLineStart,
134 WitInsertLineEdit::CursorLineEnd => NativeInsertLineEdit::CursorLineEnd,
135 WitInsertLineEdit::CursorCharLeft => NativeInsertLineEdit::CursorCharLeft,
136 WitInsertLineEdit::CursorCharRight => NativeInsertLineEdit::CursorCharRight,
137 WitInsertLineEdit::DeleteWordBackward => NativeInsertLineEdit::DeleteWordBackward,
138 WitInsertLineEdit::DeleteToLineStart => NativeInsertLineEdit::DeleteToLineStart,
139 WitInsertLineEdit::KillToLineEnd => NativeInsertLineEdit::KillToLineEnd,
140 WitInsertLineEdit::IndentLine => NativeInsertLineEdit::IndentLine,
141 WitInsertLineEdit::DedentLine => NativeInsertLineEdit::DedentLine,
142 })
143 }
144}
145
146impl WitBoundary for NativeHScroll {
147 type Wit = WitHscroll;
148 fn to_wit(&self) -> Result<WitHscroll, String> {
149 Ok(match self {
150 NativeHScroll::Columns { right } => WitHscroll::Columns(*right),
151 NativeHScroll::HalfScreen { right } => WitHscroll::HalfScreen(*right),
152 NativeHScroll::CursorToEdge { end } => WitHscroll::CursorToEdge(*end),
153 })
154 }
155 fn from_wit(w: WitHscroll) -> Result<Self, String> {
156 Ok(match w {
157 WitHscroll::Columns(right) => NativeHScroll::Columns { right },
158 WitHscroll::HalfScreen(right) => NativeHScroll::HalfScreen { right },
159 WitHscroll::CursorToEdge(end) => NativeHScroll::CursorToEdge { end },
160 })
161 }
162}
163
164impl WitBoundary for NativeAppEffect {
165 type Wit = WitAppEffect;
166
167 fn to_wit(&self) -> Result<WitAppEffect, String> {
168 Ok(match self {
169 NativeAppEffect::Quit => WitAppEffect::Quit,
170 NativeAppEffect::Cancel => {
179 return Err(
180 "AppEffect::Cancel is the user's foreground-cancel hatch (`<C-c>`, \
181 cancellation.md CG.1); no plugin (WIT) surface — a plugin must not \
182 reset the user's mode"
183 .to_string(),
184 );
185 }
186 NativeAppEffect::MatchBracket => WitAppEffect::MatchBracket,
187 NativeAppEffect::ToggleCaseAtCursor => WitAppEffect::ToggleCaseAtCursor,
188 NativeAppEffect::OpenLineBelow => WitAppEffect::OpenLineBelow,
189 NativeAppEffect::OpenLineAbove => WitAppEffect::OpenLineAbove,
190 NativeAppEffect::SearchNext => WitAppEffect::SearchNext,
191 NativeAppEffect::SearchPrevious => WitAppEffect::SearchPrevious,
192 NativeAppEffect::JumpHistoryBack => WitAppEffect::JumpHistoryBack,
193 NativeAppEffect::JumpHistoryForward => WitAppEffect::JumpHistoryForward,
194 NativeAppEffect::PaneHistoryBack => WitAppEffect::PaneHistoryBack,
195 NativeAppEffect::PaneHistoryForward => WitAppEffect::PaneHistoryForward,
196 NativeAppEffect::WalkMarkHistoryBack => WitAppEffect::WalkMarkHistoryBack,
197 NativeAppEffect::WalkMarkHistoryForward => WitAppEffect::WalkMarkHistoryForward,
198 NativeAppEffect::TagStackPop => WitAppEffect::TagStackPop,
199 NativeAppEffect::OpenFoldAtCursor => WitAppEffect::OpenFoldAtCursor,
200 NativeAppEffect::CloseFoldAtCursor => WitAppEffect::CloseFoldAtCursor,
201 NativeAppEffect::ToggleFoldAtCursor => WitAppEffect::ToggleFoldAtCursor,
202 NativeAppEffect::OpenAllFolds => WitAppEffect::OpenAllFolds,
203 NativeAppEffect::CloseAllFolds => WitAppEffect::CloseAllFolds,
204 NativeAppEffect::CycleFoldAtCursor => WitAppEffect::CycleFoldAtCursor,
205 NativeAppEffect::CycleFoldsGlobal => WitAppEffect::CycleFoldsGlobal,
206 NativeAppEffect::GotoParentFold => WitAppEffect::GotoParentFold,
207 NativeAppEffect::DeleteFoldAtCursor => WitAppEffect::DeleteFoldAtCursor,
208 NativeAppEffect::OpenFoldsRecursively => WitAppEffect::OpenFoldsRecursively,
209 NativeAppEffect::CloseFoldsRecursively => WitAppEffect::CloseFoldsRecursively,
210 NativeAppEffect::DeleteFoldsRecursively => WitAppEffect::DeleteFoldsRecursively,
211 NativeAppEffect::GotoNextFold => WitAppEffect::GotoNextFold,
212 NativeAppEffect::GotoPrevFold => WitAppEffect::GotoPrevFold,
213 NativeAppEffect::ToggleFoldEnable => WitAppEffect::ToggleFoldEnable,
214 NativeAppEffect::Undo => WitAppEffect::Undo,
215 NativeAppEffect::Redo => WitAppEffect::Redo,
216 NativeAppEffect::RepeatLastChange => WitAppEffect::RepeatLastChange,
217 NativeAppEffect::PageDown => WitAppEffect::PageDown,
218 NativeAppEffect::HalfPageDown => WitAppEffect::HalfPageDown,
219 NativeAppEffect::HalfPageUp => WitAppEffect::HalfPageUp,
220 NativeAppEffect::PageUp => WitAppEffect::PageUp,
221 NativeAppEffect::ScrollLineUp => WitAppEffect::ScrollLineUp,
222 NativeAppEffect::ScrollLineDown => WitAppEffect::ScrollLineDown,
223 NativeAppEffect::RedrawScreen => WitAppEffect::RedrawScreen,
224 NativeAppEffect::OpenCommandPicker => WitAppEffect::OpenCommandPicker,
225 NativeAppEffect::OpenHistoryPicker => {
231 return Err(
232 "AppEffect::OpenHistoryPicker is a host-internal command-line-history \
233 affordance (rich-minibuffer MB.3); no plugin (WIT) surface — use the \
234 `:history` ex-command"
235 .to_string(),
236 );
237 }
238 NativeAppEffect::OpenSearchHistoryPicker => {
239 return Err(
240 "AppEffect::OpenSearchHistoryPicker is a host-internal search-line-history \
241 affordance (rich-minibuffer MB.5); no plugin (WIT) surface — use the \
242 `:history search` ex-command"
243 .to_string(),
244 );
245 }
246 NativeAppEffect::EnterCommandLine => WitAppEffect::EnterCommandLine,
247 NativeAppEffect::CommandLineSubmit
253 | NativeAppEffect::CommandLineCancel
254 | NativeAppEffect::CommandLineHistoryPrev
255 | NativeAppEffect::CommandLineHistoryNext
256 | NativeAppEffect::CommandLineComplete
257 | NativeAppEffect::CommandLineCompletePrev
258 | NativeAppEffect::CommandLineDescribeUnderCursor
259 | NativeAppEffect::CommandLineToggleExpand
260 | NativeAppEffect::SearchLineSubmit
261 | NativeAppEffect::SearchLineCancel
262 | NativeAppEffect::SearchLineBackspace
263 | NativeAppEffect::SearchLineHistoryPrev
264 | NativeAppEffect::SearchLineHistoryNext
265 | NativeAppEffect::SearchLineToggleExpand
266 | NativeAppEffect::PromptLineSubmit
267 | NativeAppEffect::PromptLineCancel => {
268 return Err(
269 "AppEffect::*Line* are host-internal minibuffer-prompt effects \
270 (rich-minibuffer MB.1–MB.5); no plugin (WIT) surface"
271 .to_string(),
272 );
273 }
274 NativeAppEffect::OilNavigateUp => WitAppEffect::OilNavigateUp,
275 NativeAppEffect::ReselectLastVisual => WitAppEffect::ReselectLastVisual,
276 NativeAppEffect::SwapVisualEnds => WitAppEffect::SwapVisualEnds,
277 NativeAppEffect::PasteAfter => WitAppEffect::PasteAfter,
278 NativeAppEffect::PasteBefore => WitAppEffect::PasteBefore,
279 NativeAppEffect::EnterAppend => WitAppEffect::EnterAppend,
280 NativeAppEffect::EnterInsertFirstNonBlank => WitAppEffect::EnterInsertFirstNonBlank,
281 NativeAppEffect::EnterAppendEndOfLine => WitAppEffect::EnterAppendEndOfLine,
282 NativeAppEffect::DisplayLineDown => WitAppEffect::DisplayLineDown,
283 NativeAppEffect::DisplayLineUp => WitAppEffect::DisplayLineUp,
284 NativeAppEffect::DisplayLineStart => WitAppEffect::DisplayLineStart,
285 NativeAppEffect::DisplayLineEnd => WitAppEffect::DisplayLineEnd,
286 NativeAppEffect::CreateFoldFromVisual => WitAppEffect::CreateFoldFromVisual,
287 NativeAppEffect::DeleteCharBackward => WitAppEffect::DeleteCharBackward,
288 NativeAppEffect::CompletionTrigger => WitAppEffect::CompletionTrigger,
289 NativeAppEffect::ExitVisual => WitAppEffect::ExitVisual,
290 NativeAppEffect::ReplaceUndoLast => WitAppEffect::ReplaceUndoLast,
291 NativeAppEffect::EnterMode(state) => WitAppEffect::EnterMode(state.to_wit()?),
292 NativeAppEffect::EnterVisual(k) => WitAppEffect::EnterVisual(k.to_wit()?),
293 NativeAppEffect::EnterSelect(k) => WitAppEffect::EnterSelect(k.to_wit()?),
294 NativeAppEffect::EnterSearch(d) => WitAppEffect::EnterSearch(d.to_wit()?),
295 NativeAppEffect::SearchWordUnderCursor(d) => {
296 WitAppEffect::SearchWordUnderCursor(d.to_wit()?)
297 }
298 NativeAppEffect::JumpViewport(p) => WitAppEffect::JumpViewport(p.to_wit()?),
299 NativeAppEffect::ScrollCursorTo(p) => WitAppEffect::ScrollCursorTo(p.to_wit()?),
300 NativeAppEffect::HorizontalScroll(h) => WitAppEffect::HorizontalScroll(h.to_wit()?),
301 NativeAppEffect::JoinLines { with_space } => WitAppEffect::JoinLines(*with_space),
302 NativeAppEffect::FindRepeat { reverse } => WitAppEffect::FindRepeat(*reverse),
303 NativeAppEffect::InsertNewline => WitAppEffect::InsertNewline,
304 NativeAppEffect::InsertTab => WitAppEffect::InsertTab,
305 NativeAppEffect::OverwriteChar(c) => WitAppEffect::OverwriteChar(*c),
306 NativeAppEffect::SetMark(c) => WitAppEffect::SetMark(*c),
307 NativeAppEffect::JumpToMarkLine(c) => WitAppEffect::JumpToMarkLine(*c),
308 NativeAppEffect::JumpToMarkExact(c) => WitAppEffect::JumpToMarkExact(*c),
309 NativeAppEffect::SelectRegister(r) => WitAppEffect::SelectRegister(r.to_wit()?),
310 NativeAppEffect::StartMacroRecord(c) => WitAppEffect::StartMacroRecord(*c),
311 NativeAppEffect::PlayMacro(c) => WitAppEffect::PlayMacro(*c),
312 NativeAppEffect::PlayLastMacro => WitAppEffect::PlayLastMacro,
313 NativeAppEffect::AbsorbOperatorPrefix(op) => {
314 WitAppEffect::AbsorbOperatorPrefix(op.0.raw())
315 }
316 NativeAppEffect::SplitPaneHorizontal => WitAppEffect::SplitPaneHorizontal,
317 NativeAppEffect::SplitPaneVertical => WitAppEffect::SplitPaneVertical,
318 NativeAppEffect::ClosePane => WitAppEffect::ClosePane,
319 NativeAppEffect::OnlyPane => WitAppEffect::OnlyPane,
320 NativeAppEffect::ToggleZoomPane => WitAppEffect::ToggleZoomPane,
321 NativeAppEffect::NavigatePane(d) => WitAppEffect::NavigatePane(d.to_wit()?),
322 NativeAppEffect::NextPane => WitAppEffect::NextPane,
323 NativeAppEffect::PrevPane => WitAppEffect::PrevPane,
324 NativeAppEffect::NextTab => WitAppEffect::NextTab,
325 NativeAppEffect::PrevTab => WitAppEffect::PrevTab,
326 NativeAppEffect::GoToTab(n) => WitAppEffect::GoToTab(*n),
327 NativeAppEffect::NewTab => WitAppEffect::NewTab,
328 NativeAppEffect::NewTabAt(path) => WitAppEffect::NewTabAt(path.clone()),
329 NativeAppEffect::TerminalSpawn(cmd) => WitAppEffect::TerminalSpawn(cmd.clone()),
330 NativeAppEffect::TerminalSpawnInNewTab(cmd) => {
331 WitAppEffect::TerminalSpawnInNewTab(cmd.clone())
332 }
333 NativeAppEffect::MovePaneToNewTab => WitAppEffect::MovePaneToNewTab,
334 NativeAppEffect::CloseTab => WitAppEffect::CloseTab,
335 NativeAppEffect::OnlyTab => WitAppEffect::OnlyTab,
336 NativeAppEffect::MoveTab(n) => WitAppEffect::MoveTab(*n),
337 NativeAppEffect::PickerAcceptInSplit => WitAppEffect::PickerAcceptInSplit,
338 NativeAppEffect::PickerAcceptInVSplit => WitAppEffect::PickerAcceptInVsplit,
339 NativeAppEffect::PickerAcceptInTab => WitAppEffect::PickerAcceptInTab,
340 NativeAppEffect::EqualizePanes => WitAppEffect::EqualizePanes,
341 NativeAppEffect::GrowPaneHeight => WitAppEffect::GrowPaneHeight,
342 NativeAppEffect::ShrinkPaneHeight => WitAppEffect::ShrinkPaneHeight,
343 NativeAppEffect::GrowPaneWidth => WitAppEffect::GrowPaneWidth,
344 NativeAppEffect::ShrinkPaneWidth => WitAppEffect::ShrinkPaneWidth,
345 NativeAppEffect::CompletionNext => WitAppEffect::CompletionNext,
346 NativeAppEffect::CompletionPrev => WitAppEffect::CompletionPrev,
347 NativeAppEffect::CompletionAccept => WitAppEffect::CompletionAccept,
348 NativeAppEffect::CompletionCancel => WitAppEffect::CompletionCancel,
349 NativeAppEffect::CompletionCancelAndExitInsert => {
350 WitAppEffect::CompletionCancelAndExitInsert
351 }
352 NativeAppEffect::CompletionToggleDocs => WitAppEffect::CompletionToggleDocs,
353 NativeAppEffect::CompletionDocsScrollDown => WitAppEffect::CompletionDocsScrollDown,
354 NativeAppEffect::CompletionDocsScrollUp => WitAppEffect::CompletionDocsScrollUp,
355 NativeAppEffect::CompletionAcceptThenInsert(c) => {
356 WitAppEffect::CompletionAcceptThenInsert(*c)
357 }
358 NativeAppEffect::InsertRegister(_)
367 | NativeAppEffect::OpenYankPicker
368 | NativeAppEffect::OpenArgPicker => {
369 return Err(
370 "insert-register / open-yank-picker / open-arg-picker are host-internal, \
371 not representable over WIT"
372 .into(),
373 );
374 }
375 NativeAppEffect::SnippetNextPlaceholder => WitAppEffect::SnippetNextPlaceholder,
376 NativeAppEffect::SnippetPrevPlaceholder => WitAppEffect::SnippetPrevPlaceholder,
377 NativeAppEffect::CompletionFilterToSource(s) => {
378 WitAppEffect::CompletionFilterToSource(s.clone())
379 }
380 NativeAppEffect::CompletionFilterClear => WitAppEffect::CompletionFilterClear,
381 NativeAppEffect::DiffGet => WitAppEffect::DiffGet,
382 NativeAppEffect::DiffPut => WitAppEffect::DiffPut,
383 NativeAppEffect::TutorAdvance => WitAppEffect::TutorAdvance,
384 NativeAppEffect::TutorRetreat => WitAppEffect::TutorRetreat,
385 NativeAppEffect::MultibufferExpand { delta } => WitAppEffect::MultibufferExpand(*delta),
386 NativeAppEffect::NarrowWiden => WitAppEffect::NarrowWiden,
387 NativeAppEffect::NarrowLines {
388 start_line,
389 end_line,
390 } => WitAppEffect::NarrowLines(WitNarrowLinesPayload {
391 start_line: *start_line,
392 end_line: *end_line,
393 }),
394 NativeAppEffect::CreateFold {
396 start_line,
397 end_line,
398 } => WitAppEffect::CreateFold(WitNarrowLinesPayload {
399 start_line: *start_line,
400 end_line: *end_line,
401 }),
402 NativeAppEffect::FormatRange {
407 intent,
408 start_line,
409 end_line,
410 } => WitAppEffect::FormatRange(WitFormatRangePayload {
411 intent: wit_format_intent(*intent),
412 start_line: *start_line,
413 end_line: *end_line,
414 }),
415 NativeAppEffect::SearchTrigger { query } => WitAppEffect::SearchTrigger(query.clone()),
416 NativeAppEffect::SearchRefresh => WitAppEffect::SearchRefresh,
417 NativeAppEffect::NarrowTrigger { .. } => {
420 return Err(
421 "AppEffect::NarrowTrigger carries a recursive ex-command Range; it crosses \
422 with the range mirror (fragment §4.4)"
423 .to_string(),
424 );
425 }
426 NativeAppEffect::InsertLineEdit(edit) => WitAppEffect::InsertLineEdit(edit.to_wit()?),
427 NativeAppEffect::CompileRun { .. } => {
433 return Err(
434 "AppEffect::CompileRun is a native built-in; its plugin (WIT) surface is \
435 deferred with the plugin host (compilation-mode.md §8)"
436 .to_string(),
437 );
438 }
439 NativeAppEffect::CompileJumpToLocation { .. } => {
444 return Err(
445 "AppEffect::CompileJumpToLocation jumps to a source location + syncs core \
446 error state; its plugin (WIT) surface is deferred with the plugin host \
447 (compilation-mode.md §5)"
448 .to_string(),
449 );
450 }
451 NativeAppEffect::ErrorNav { .. } => {
455 return Err(
456 "AppEffect::ErrorNav walks core error state; its plugin (WIT) surface \
457 is deferred with the plugin host (compilation-mode.md §3)"
458 .to_string(),
459 );
460 }
461 NativeAppEffect::SetErrorList { .. } => {
466 return Err(
467 "AppEffect::SetErrorList feeds core error state from the native compilation \
468 parser; its plugin (WIT) surface is deferred with the plugin host \
469 (compilation-mode.md §5)"
470 .to_string(),
471 );
472 }
473 NativeAppEffect::CompilationGutterSet { .. } => {
479 return Err(
480 "AppEffect::CompilationGutterSet feeds the *compilation* buffer's severity \
481 gutter marks from the native compilation parser; its plugin (WIT) surface is \
482 deferred with the plugin host (compilation-mode.md §5)"
483 .to_string(),
484 );
485 }
486 NativeAppEffect::CompilationLocationLines { .. } => {
491 return Err(
492 "AppEffect::CompilationLocationLines marks navigable file-location lines in the \
493 *compilation* buffer for theme-based highlighting; its plugin (WIT) surface is \
494 deferred with the plugin host"
495 .to_string(),
496 );
497 }
498 NativeAppEffect::CompilationThemeColors { .. } => {
501 return Err(
502 "AppEffect::CompilationThemeColors feeds theme-resolved compilation location \
503 colours to the renderer; its plugin (WIT) surface is deferred with the \
504 plugin host"
505 .to_string(),
506 );
507 }
508 NativeAppEffect::CompilationKill => {
509 return Err(
510 "AppEffect::CompilationKill kills the running compilation child process; \
511 its plugin (WIT) surface is deferred with the plugin host"
512 .to_string(),
513 );
514 }
515 NativeAppEffect::ProblemsOpen
521 | NativeAppEffect::ProblemsClose
522 | NativeAppEffect::ProblemsRefresh => {
523 return Err(
524 "AppEffect::Problems{Open,Close,Refresh} open, close and rebuild the \
525 *problems* view over core error state; their plugin (WIT) surface is \
526 deferred with the plugin host (compilation-mode.md §4)"
527 .to_string(),
528 );
529 }
530 NativeAppEffect::OpenProviderView { provider, args } => {
561 let (argument, scan_args) = match args {
562 lattice_grammar::args::Args::None => (None, Vec::new()),
563 lattice_grammar::args::Args::String(s) => (Some(s.clone()), Vec::new()),
564 lattice_grammar::args::Args::List(values) => {
565 let mut strings = Vec::with_capacity(values.len());
566 for v in values {
567 match v {
568 lattice_grammar::args::ArgValue::String(s) => {
569 strings.push(s.clone())
570 }
571 other => {
577 return Err(format!(
578 "AppEffect::OpenProviderView carries a non-string list \
579 argument the provider-view boundary does not mirror \
580 ({other:?}); provider-view arguments are free text"
581 ));
582 }
583 }
584 }
585 let mut it = strings.into_iter();
586 let first = it.next().unwrap_or_default();
587 let argument = (!first.is_empty()).then_some(first);
590 (argument, it.collect())
591 }
592 other => {
593 return Err(format!(
594 "AppEffect::OpenProviderView carries args the provider-view boundary \
595 does not mirror ({other:?}); a provider view takes at most one \
596 free-text argument"
597 ));
598 }
599 };
600 WitAppEffect::OpenProviderView(WitOpenProviderViewPayload {
601 provider: provider.clone(),
602 argument,
603 scan_args,
604 })
605 }
606 })
607 }
608
609 fn from_wit(w: WitAppEffect) -> Result<Self, String> {
610 Ok(match w {
611 WitAppEffect::Quit => NativeAppEffect::Quit,
612 WitAppEffect::MatchBracket => NativeAppEffect::MatchBracket,
613 WitAppEffect::ToggleCaseAtCursor => NativeAppEffect::ToggleCaseAtCursor,
614 WitAppEffect::OpenLineBelow => NativeAppEffect::OpenLineBelow,
615 WitAppEffect::OpenLineAbove => NativeAppEffect::OpenLineAbove,
616 WitAppEffect::SearchNext => NativeAppEffect::SearchNext,
617 WitAppEffect::SearchPrevious => NativeAppEffect::SearchPrevious,
618 WitAppEffect::JumpHistoryBack => NativeAppEffect::JumpHistoryBack,
619 WitAppEffect::JumpHistoryForward => NativeAppEffect::JumpHistoryForward,
620 WitAppEffect::PaneHistoryBack => NativeAppEffect::PaneHistoryBack,
621 WitAppEffect::PaneHistoryForward => NativeAppEffect::PaneHistoryForward,
622 WitAppEffect::WalkMarkHistoryBack => NativeAppEffect::WalkMarkHistoryBack,
623 WitAppEffect::WalkMarkHistoryForward => NativeAppEffect::WalkMarkHistoryForward,
624 WitAppEffect::TagStackPop => NativeAppEffect::TagStackPop,
625 WitAppEffect::OpenFoldAtCursor => NativeAppEffect::OpenFoldAtCursor,
626 WitAppEffect::CloseFoldAtCursor => NativeAppEffect::CloseFoldAtCursor,
627 WitAppEffect::ToggleFoldAtCursor => NativeAppEffect::ToggleFoldAtCursor,
628 WitAppEffect::OpenAllFolds => NativeAppEffect::OpenAllFolds,
629 WitAppEffect::CloseAllFolds => NativeAppEffect::CloseAllFolds,
630 WitAppEffect::CycleFoldAtCursor => NativeAppEffect::CycleFoldAtCursor,
631 WitAppEffect::CycleFoldsGlobal => NativeAppEffect::CycleFoldsGlobal,
632 WitAppEffect::GotoParentFold => NativeAppEffect::GotoParentFold,
633 WitAppEffect::DeleteFoldAtCursor => NativeAppEffect::DeleteFoldAtCursor,
634 WitAppEffect::OpenFoldsRecursively => NativeAppEffect::OpenFoldsRecursively,
635 WitAppEffect::CloseFoldsRecursively => NativeAppEffect::CloseFoldsRecursively,
636 WitAppEffect::DeleteFoldsRecursively => NativeAppEffect::DeleteFoldsRecursively,
637 WitAppEffect::GotoNextFold => NativeAppEffect::GotoNextFold,
638 WitAppEffect::GotoPrevFold => NativeAppEffect::GotoPrevFold,
639 WitAppEffect::ToggleFoldEnable => NativeAppEffect::ToggleFoldEnable,
640 WitAppEffect::Undo => NativeAppEffect::Undo,
641 WitAppEffect::Redo => NativeAppEffect::Redo,
642 WitAppEffect::RepeatLastChange => NativeAppEffect::RepeatLastChange,
643 WitAppEffect::PageDown => NativeAppEffect::PageDown,
644 WitAppEffect::HalfPageDown => NativeAppEffect::HalfPageDown,
645 WitAppEffect::HalfPageUp => NativeAppEffect::HalfPageUp,
646 WitAppEffect::PageUp => NativeAppEffect::PageUp,
647 WitAppEffect::ScrollLineUp => NativeAppEffect::ScrollLineUp,
648 WitAppEffect::ScrollLineDown => NativeAppEffect::ScrollLineDown,
649 WitAppEffect::RedrawScreen => NativeAppEffect::RedrawScreen,
650 WitAppEffect::OpenCommandPicker => NativeAppEffect::OpenCommandPicker,
651 WitAppEffect::EnterCommandLine => NativeAppEffect::EnterCommandLine,
652 WitAppEffect::OilNavigateUp => NativeAppEffect::OilNavigateUp,
653 WitAppEffect::ReselectLastVisual => NativeAppEffect::ReselectLastVisual,
654 WitAppEffect::SwapVisualEnds => NativeAppEffect::SwapVisualEnds,
655 WitAppEffect::PasteAfter => NativeAppEffect::PasteAfter,
656 WitAppEffect::PasteBefore => NativeAppEffect::PasteBefore,
657 WitAppEffect::EnterAppend => NativeAppEffect::EnterAppend,
658 WitAppEffect::EnterInsertFirstNonBlank => NativeAppEffect::EnterInsertFirstNonBlank,
659 WitAppEffect::EnterAppendEndOfLine => NativeAppEffect::EnterAppendEndOfLine,
660 WitAppEffect::DisplayLineDown => NativeAppEffect::DisplayLineDown,
661 WitAppEffect::DisplayLineUp => NativeAppEffect::DisplayLineUp,
662 WitAppEffect::DisplayLineStart => NativeAppEffect::DisplayLineStart,
663 WitAppEffect::DisplayLineEnd => NativeAppEffect::DisplayLineEnd,
664 WitAppEffect::CreateFoldFromVisual => NativeAppEffect::CreateFoldFromVisual,
665 WitAppEffect::DeleteCharBackward => NativeAppEffect::DeleteCharBackward,
666 WitAppEffect::CompletionTrigger => NativeAppEffect::CompletionTrigger,
667 WitAppEffect::ExitVisual => NativeAppEffect::ExitVisual,
668 WitAppEffect::ReplaceUndoLast => NativeAppEffect::ReplaceUndoLast,
669 WitAppEffect::EnterMode(state) => {
670 NativeAppEffect::EnterMode(NativeModalState::from_wit(state)?)
671 }
672 WitAppEffect::EnterVisual(k) => {
673 NativeAppEffect::EnterVisual(NativeVisualKind::from_wit(k)?)
674 }
675 WitAppEffect::EnterSelect(k) => {
676 NativeAppEffect::EnterSelect(NativeVisualKind::from_wit(k)?)
677 }
678 WitAppEffect::EnterSearch(d) => {
679 NativeAppEffect::EnterSearch(NativeSearchDirection::from_wit(d)?)
680 }
681 WitAppEffect::SearchWordUnderCursor(d) => {
682 NativeAppEffect::SearchWordUnderCursor(NativeSearchDirection::from_wit(d)?)
683 }
684 WitAppEffect::JumpViewport(p) => {
685 NativeAppEffect::JumpViewport(NativeViewportPos::from_wit(p)?)
686 }
687 WitAppEffect::ScrollCursorTo(p) => {
688 NativeAppEffect::ScrollCursorTo(NativeScrollPos::from_wit(p)?)
689 }
690 WitAppEffect::HorizontalScroll(h) => {
691 NativeAppEffect::HorizontalScroll(NativeHScroll::from_wit(h)?)
692 }
693 WitAppEffect::JoinLines(with_space) => NativeAppEffect::JoinLines { with_space },
694 WitAppEffect::FindRepeat(reverse) => NativeAppEffect::FindRepeat { reverse },
695 WitAppEffect::InsertNewline => NativeAppEffect::InsertNewline,
696 WitAppEffect::InsertTab => NativeAppEffect::InsertTab,
697 WitAppEffect::OverwriteChar(c) => NativeAppEffect::OverwriteChar(c),
698 WitAppEffect::SetMark(c) => NativeAppEffect::SetMark(c),
699 WitAppEffect::JumpToMarkLine(c) => NativeAppEffect::JumpToMarkLine(c),
700 WitAppEffect::JumpToMarkExact(c) => NativeAppEffect::JumpToMarkExact(c),
701 WitAppEffect::SelectRegister(r) => {
702 NativeAppEffect::SelectRegister(NativeRegister::from_wit(r)?)
703 }
704 WitAppEffect::StartMacroRecord(c) => NativeAppEffect::StartMacroRecord(c),
705 WitAppEffect::PlayMacro(c) => NativeAppEffect::PlayMacro(c),
706 WitAppEffect::PlayLastMacro => NativeAppEffect::PlayLastMacro,
707 WitAppEffect::AbsorbOperatorPrefix(raw) => {
708 NativeAppEffect::AbsorbOperatorPrefix(OperatorId(CommandId::new(raw)))
709 }
710 WitAppEffect::SplitPaneHorizontal => NativeAppEffect::SplitPaneHorizontal,
711 WitAppEffect::SplitPaneVertical => NativeAppEffect::SplitPaneVertical,
712 WitAppEffect::ClosePane => NativeAppEffect::ClosePane,
713 WitAppEffect::OnlyPane => NativeAppEffect::OnlyPane,
714 WitAppEffect::ToggleZoomPane => NativeAppEffect::ToggleZoomPane,
715 WitAppEffect::NavigatePane(d) => {
716 NativeAppEffect::NavigatePane(NativePaneDirection::from_wit(d)?)
717 }
718 WitAppEffect::NextPane => NativeAppEffect::NextPane,
719 WitAppEffect::PrevPane => NativeAppEffect::PrevPane,
720 WitAppEffect::NextTab => NativeAppEffect::NextTab,
721 WitAppEffect::PrevTab => NativeAppEffect::PrevTab,
722 WitAppEffect::GoToTab(n) => NativeAppEffect::GoToTab(n),
723 WitAppEffect::NewTab => NativeAppEffect::NewTab,
724 WitAppEffect::NewTabAt(path) => NativeAppEffect::NewTabAt(path),
725 WitAppEffect::TerminalSpawn(cmd) => NativeAppEffect::TerminalSpawn(cmd),
726 WitAppEffect::TerminalSpawnInNewTab(cmd) => NativeAppEffect::TerminalSpawnInNewTab(cmd),
727 WitAppEffect::MovePaneToNewTab => NativeAppEffect::MovePaneToNewTab,
728 WitAppEffect::CloseTab => NativeAppEffect::CloseTab,
729 WitAppEffect::OnlyTab => NativeAppEffect::OnlyTab,
730 WitAppEffect::MoveTab(n) => NativeAppEffect::MoveTab(n),
731 WitAppEffect::PickerAcceptInSplit => NativeAppEffect::PickerAcceptInSplit,
732 WitAppEffect::PickerAcceptInVsplit => NativeAppEffect::PickerAcceptInVSplit,
733 WitAppEffect::PickerAcceptInTab => NativeAppEffect::PickerAcceptInTab,
734 WitAppEffect::EqualizePanes => NativeAppEffect::EqualizePanes,
735 WitAppEffect::GrowPaneHeight => NativeAppEffect::GrowPaneHeight,
736 WitAppEffect::ShrinkPaneHeight => NativeAppEffect::ShrinkPaneHeight,
737 WitAppEffect::GrowPaneWidth => NativeAppEffect::GrowPaneWidth,
738 WitAppEffect::ShrinkPaneWidth => NativeAppEffect::ShrinkPaneWidth,
739 WitAppEffect::CompletionNext => NativeAppEffect::CompletionNext,
740 WitAppEffect::CompletionPrev => NativeAppEffect::CompletionPrev,
741 WitAppEffect::CompletionAccept => NativeAppEffect::CompletionAccept,
742 WitAppEffect::CompletionCancel => NativeAppEffect::CompletionCancel,
743 WitAppEffect::CompletionCancelAndExitInsert => {
744 NativeAppEffect::CompletionCancelAndExitInsert
745 }
746 WitAppEffect::CompletionToggleDocs => NativeAppEffect::CompletionToggleDocs,
747 WitAppEffect::CompletionDocsScrollDown => NativeAppEffect::CompletionDocsScrollDown,
748 WitAppEffect::CompletionDocsScrollUp => NativeAppEffect::CompletionDocsScrollUp,
749 WitAppEffect::CompletionAcceptThenInsert(c) => {
750 NativeAppEffect::CompletionAcceptThenInsert(c)
751 }
752 WitAppEffect::SnippetNextPlaceholder => NativeAppEffect::SnippetNextPlaceholder,
753 WitAppEffect::SnippetPrevPlaceholder => NativeAppEffect::SnippetPrevPlaceholder,
754 WitAppEffect::CompletionFilterToSource(s) => {
755 NativeAppEffect::CompletionFilterToSource(s)
756 }
757 WitAppEffect::CompletionFilterClear => NativeAppEffect::CompletionFilterClear,
758 WitAppEffect::DiffGet => NativeAppEffect::DiffGet,
759 WitAppEffect::DiffPut => NativeAppEffect::DiffPut,
760 WitAppEffect::TutorAdvance => NativeAppEffect::TutorAdvance,
761 WitAppEffect::TutorRetreat => NativeAppEffect::TutorRetreat,
762 WitAppEffect::MultibufferExpand(delta) => NativeAppEffect::MultibufferExpand { delta },
763 WitAppEffect::NarrowWiden => NativeAppEffect::NarrowWiden,
764 WitAppEffect::NarrowLines(p) => NativeAppEffect::NarrowLines {
765 start_line: p.start_line,
766 end_line: p.end_line,
767 },
768 WitAppEffect::CreateFold(p) => NativeAppEffect::CreateFold {
769 start_line: p.start_line,
770 end_line: p.end_line,
771 },
772 WitAppEffect::FormatRange(p) => NativeAppEffect::FormatRange {
773 intent: native_format_intent(p.intent),
774 start_line: p.start_line,
775 end_line: p.end_line,
776 },
777 WitAppEffect::SearchTrigger(query) => NativeAppEffect::SearchTrigger { query },
778 WitAppEffect::SearchRefresh => NativeAppEffect::SearchRefresh,
779 WitAppEffect::OpenProviderView(p) => NativeAppEffect::OpenProviderView {
788 provider: p.provider,
789 args: match (p.argument, p.scan_args.is_empty()) {
790 (Some(s), true) => lattice_grammar::args::Args::String(s),
791 (None, true) => lattice_grammar::args::Args::None,
792 (argument, false) => lattice_grammar::args::Args::List(
793 std::iter::once(argument.unwrap_or_default())
794 .chain(p.scan_args)
795 .map(lattice_grammar::args::ArgValue::String)
796 .collect(),
797 ),
798 },
799 },
800 WitAppEffect::InsertLineEdit(edit) => {
801 NativeAppEffect::InsertLineEdit(NativeInsertLineEdit::from_wit(edit)?)
802 }
803 })
804 }
805}
806
807#[cfg(test)]
808mod tests {
809 #![allow(clippy::unwrap_used, clippy::panic)]
810
811 use super::*;
812
813 fn assert_round_trips(native: NativeAppEffect) {
815 let wit = native.to_wit().expect("to_wit");
816 let back = NativeAppEffect::from_wit(wit).expect("from_wit");
817 assert_eq!(native, back);
818 }
819
820 #[test]
825 fn app_effect_payload_arms_round_trip() {
826 for e in [
827 NativeAppEffect::EnterMode(NativeModalState::Insert),
828 NativeAppEffect::EnterVisual(NativeVisualKind::Linewise),
829 NativeAppEffect::EnterSelect(NativeVisualKind::Blockwise),
830 NativeAppEffect::EnterSearch(NativeSearchDirection::Backward),
831 NativeAppEffect::SearchWordUnderCursor(NativeSearchDirection::Forward),
832 NativeAppEffect::JumpViewport(NativeViewportPos::Middle),
833 NativeAppEffect::ScrollCursorTo(NativeScrollPos::Center),
834 NativeAppEffect::HorizontalScroll(NativeHScroll::Columns { right: true }),
835 NativeAppEffect::HorizontalScroll(NativeHScroll::HalfScreen { right: false }),
836 NativeAppEffect::HorizontalScroll(NativeHScroll::CursorToEdge { end: true }),
837 NativeAppEffect::JoinLines { with_space: true },
838 NativeAppEffect::FindRepeat { reverse: false },
839 NativeAppEffect::OverwriteChar('z'),
840 NativeAppEffect::SetMark('a'),
841 NativeAppEffect::JumpToMarkLine('b'),
842 NativeAppEffect::JumpToMarkExact('c'),
843 NativeAppEffect::SelectRegister(NativeRegister::Named('q')),
844 NativeAppEffect::StartMacroRecord('m'),
845 NativeAppEffect::PlayMacro('m'),
846 NativeAppEffect::AbsorbOperatorPrefix(OperatorId(CommandId::new(17))),
847 NativeAppEffect::NavigatePane(NativePaneDirection::Right),
848 NativeAppEffect::GoToTab(3),
849 NativeAppEffect::MoveTab(2),
850 NativeAppEffect::NewTabAt("/a/b.rs".into()),
851 NativeAppEffect::TerminalSpawn(Some("bash".into())),
852 NativeAppEffect::TerminalSpawnInNewTab(None),
853 NativeAppEffect::CompletionAcceptThenInsert('x'),
854 NativeAppEffect::CompletionFilterToSource("gen:lsp-completion".into()),
855 NativeAppEffect::MultibufferExpand { delta: -2 },
856 NativeAppEffect::NarrowLines {
857 start_line: 3,
858 end_line: 9,
859 },
860 NativeAppEffect::CreateFold {
863 start_line: 4,
864 end_line: 11,
865 },
866 NativeAppEffect::SearchTrigger {
867 query: "TODO".into(),
868 },
869 ] {
870 assert_round_trips(e);
871 }
872 }
873
874 #[test]
876 fn app_effect_unit_arms_round_trip() {
877 for e in [
878 NativeAppEffect::Quit,
879 NativeAppEffect::MatchBracket,
880 NativeAppEffect::OpenLineBelow,
881 NativeAppEffect::Undo,
882 NativeAppEffect::Redo,
883 NativeAppEffect::PageDown,
884 NativeAppEffect::HalfPageDown,
885 NativeAppEffect::HalfPageUp,
886 NativeAppEffect::ExitVisual,
887 NativeAppEffect::SplitPaneVertical,
888 NativeAppEffect::OnlyPane,
889 NativeAppEffect::NextTab,
890 NativeAppEffect::CompletionAccept,
891 NativeAppEffect::DiffGet,
892 NativeAppEffect::DiffPut,
893 NativeAppEffect::TutorAdvance,
894 NativeAppEffect::NarrowWiden,
895 NativeAppEffect::SearchRefresh,
896 NativeAppEffect::PlayLastMacro,
897 NativeAppEffect::OpenFoldsRecursively,
898 NativeAppEffect::CloseFoldsRecursively,
899 NativeAppEffect::DeleteFoldsRecursively,
900 ] {
901 assert_round_trips(e);
902 }
903 }
904
905 #[test]
907 fn app_effect_helper_enums_round_trip() {
908 for p in [
909 NativeViewportPos::Top,
910 NativeViewportPos::Middle,
911 NativeViewportPos::Bottom,
912 ] {
913 assert_eq!(p, NativeViewportPos::from_wit(p.to_wit().unwrap()).unwrap());
914 }
915 for p in [
916 NativeScrollPos::Top,
917 NativeScrollPos::Center,
918 NativeScrollPos::Bottom,
919 ] {
920 assert_eq!(p, NativeScrollPos::from_wit(p.to_wit().unwrap()).unwrap());
921 }
922 for d in [
923 NativePaneDirection::Left,
924 NativePaneDirection::Down,
925 NativePaneDirection::Up,
926 NativePaneDirection::Right,
927 ] {
928 assert_eq!(
929 d,
930 NativePaneDirection::from_wit(d.to_wit().unwrap()).unwrap()
931 );
932 }
933 for h in [
934 NativeHScroll::Columns { right: true },
935 NativeHScroll::HalfScreen { right: false },
936 NativeHScroll::CursorToEdge { end: true },
937 ] {
938 assert_eq!(h, NativeHScroll::from_wit(h.to_wit().unwrap()).unwrap());
939 }
940 }
941
942 #[test]
952 fn a_provider_views_root_and_scan_args_do_not_consume_each_other() {
953 use lattice_grammar::args::{ArgValue, Args};
954
955 let native = NativeAppEffect::OpenProviderView {
956 provider: "agenda".to_string(),
957 args: Args::List(vec![
958 ArgValue::String("~/notes".to_string()),
959 ArgValue::String("waiting".to_string()),
960 ]),
961 };
962 let wit = native.to_wit().expect("to_wit");
963 let WitAppEffect::OpenProviderView(p) = &wit else {
964 panic!("wrong arm");
965 };
966 assert_eq!(p.argument.as_deref(), Some("~/notes"), "the host's slot");
967 assert_eq!(p.scan_args, vec!["waiting".to_string()], "the guest's");
968 assert_round_trips(native);
969
970 let native = NativeAppEffect::OpenProviderView {
974 provider: "agenda".to_string(),
975 args: Args::List(vec![
976 ArgValue::String(String::new()),
977 ArgValue::String("waiting".to_string()),
978 ]),
979 };
980 let wit = native.to_wit().expect("to_wit");
981 let WitAppEffect::OpenProviderView(p) = &wit else {
982 panic!("wrong arm");
983 };
984 assert_eq!(
985 p.argument, None,
986 "an empty root is no root, not a root of \"\""
987 );
988 assert_eq!(p.scan_args, vec!["waiting".to_string()]);
989 }
990
991 #[test]
996 fn a_provider_view_with_no_scan_args_maps_exactly_as_before() {
997 use lattice_grammar::args::Args;
998
999 for (args, expect) in [
1000 (Args::None, None),
1001 (Args::String("~/notes".to_string()), Some("~/notes")),
1002 ] {
1003 let native = NativeAppEffect::OpenProviderView {
1004 provider: "agenda".to_string(),
1005 args,
1006 };
1007 let wit = native.to_wit().expect("to_wit");
1008 let WitAppEffect::OpenProviderView(p) = &wit else {
1009 panic!("wrong arm");
1010 };
1011 assert_eq!(p.argument.as_deref(), expect);
1012 assert!(
1013 p.scan_args.is_empty(),
1014 "no scan args means no scan args — not an empty string in a list"
1015 );
1016 assert_round_trips(native);
1017 }
1018 }
1019
1020 #[test]
1024 fn a_non_string_provider_view_argument_is_a_typed_error() {
1025 use lattice_grammar::args::{ArgValue, Args};
1026
1027 let e = NativeAppEffect::OpenProviderView {
1028 provider: "agenda".to_string(),
1029 args: Args::List(vec![ArgValue::Int(7)]),
1030 };
1031 let err = e
1032 .to_wit()
1033 .expect_err("a non-string list argument must not cross");
1034 assert!(err.contains("free text"), "error says why: {err}");
1035 }
1036
1037 #[test]
1040 fn narrow_trigger_is_a_typed_error() {
1041 let e = NativeAppEffect::NarrowTrigger { range: None };
1042 let err = e.to_wit().expect_err("NarrowTrigger must not cross yet");
1043 assert!(
1044 err.contains("NarrowTrigger"),
1045 "error names the culprit: {err}"
1046 );
1047 }
1048}