@@ -15,11 +15,11 @@ pub struct TypableCommand {
15 pub completer: Option<Completer>,
15 pub completer: Option<Completer>,
19− cx: &mut compositor::Context,
18+fn quit(cx: &mut compositor::Context, args: &[Cow<str>], event: PromptEvent) -> anyhow::Result<()> {
20− args: &[Cow<str>],
19+ if event != PromptEvent::Validate {
21− _event: PromptEvent,
20+ return Ok(());
22−) -> anyhow::Result<()> {
21+ }
23 ensure!(args.is_empty(), ":quit takes no arguments");
23 ensure!(args.is_empty(), ":quit takes no arguments");
25 // last view and we have unsaved changes
25 // last view and we have unsaved changes
@@ -35,8 +35,12 @@ fn quit(
35fn force_quit(
35fn force_quit(
36 cx: &mut compositor::Context,
36 cx: &mut compositor::Context,
37 args: &[Cow<str>],
37 args: &[Cow<str>],
38− _event: PromptEvent,
38+ event: PromptEvent,
39) -> anyhow::Result<()> {
39) -> anyhow::Result<()> {
40+ if event != PromptEvent::Validate {
40 ensure!(args.is_empty(), ":quit! takes no arguments");
44 ensure!(args.is_empty(), ":quit! takes no arguments");
42 cx.editor.close(view!(cx.editor).id);
46 cx.editor.close(view!(cx.editor).id);
@@ -44,11 +48,11 @@ fn force_quit(
48− cx: &mut compositor::Context,
51+fn open(cx: &mut compositor::Context, args: &[Cow<str>], event: PromptEvent) -> anyhow::Result<()> {
49− args: &[Cow<str>],
52+ if event != PromptEvent::Validate {
50− _event: PromptEvent,
53+ return Ok(());
51−) -> anyhow::Result<()> {
54+ }
52 ensure!(!args.is_empty(), "wrong argument count");
56 ensure!(!args.is_empty(), "wrong argument count");
53 for arg in args {
57 for arg in args {
54 let (path, pos) = args::parse_file(arg);
58 let (path, pos) = args::parse_file(arg);
@@ -114,8 +118,12 @@ fn buffer_gather_paths_impl(editor: &mut Editor, args: &[Cow<str>]) -> Vec<Docum
114fn buffer_close(
118fn buffer_close(
115 cx: &mut compositor::Context,
119 cx: &mut compositor::Context,
116 args: &[Cow<str>],
120 args: &[Cow<str>],
117− _event: PromptEvent,
121+ event: PromptEvent,
118) -> anyhow::Result<()> {
122) -> anyhow::Result<()> {
123+ if event != PromptEvent::Validate {
119 let document_ids = buffer_gather_paths_impl(cx.editor, args);
127 let document_ids = buffer_gather_paths_impl(cx.editor, args);
120 buffer_close_by_ids_impl(cx.editor, &document_ids, false)
128 buffer_close_by_ids_impl(cx.editor, &document_ids, false)
@@ -123,8 +131,12 @@ fn buffer_close(
123fn force_buffer_close(
131fn force_buffer_close(
124 cx: &mut compositor::Context,
132 cx: &mut compositor::Context,
125 args: &[Cow<str>],
133 args: &[Cow<str>],
126− _event: PromptEvent,
134+ event: PromptEvent,
127) -> anyhow::Result<()> {
135) -> anyhow::Result<()> {
136+ if event != PromptEvent::Validate {
128 let document_ids = buffer_gather_paths_impl(cx.editor, args);
140 let document_ids = buffer_gather_paths_impl(cx.editor, args);
129 buffer_close_by_ids_impl(cx.editor, &document_ids, true)
141 buffer_close_by_ids_impl(cx.editor, &document_ids, true)
@@ -141,8 +153,12 @@ fn buffer_gather_others_impl(editor: &mut Editor) -> Vec<DocumentId> {
141fn buffer_close_others(
153fn buffer_close_others(
142 cx: &mut compositor::Context,
154 cx: &mut compositor::Context,
143 _args: &[Cow<str>],
155 _args: &[Cow<str>],
144− _event: PromptEvent,
156+ event: PromptEvent,
145) -> anyhow::Result<()> {
157) -> anyhow::Result<()> {
158+ if event != PromptEvent::Validate {
146 let document_ids = buffer_gather_others_impl(cx.editor);
162 let document_ids = buffer_gather_others_impl(cx.editor);
147 buffer_close_by_ids_impl(cx.editor, &document_ids, false)
163 buffer_close_by_ids_impl(cx.editor, &document_ids, false)
@@ -150,8 +166,12 @@ fn buffer_close_others(
150fn force_buffer_close_others(
166fn force_buffer_close_others(
151 cx: &mut compositor::Context,
167 cx: &mut compositor::Context,
152 _args: &[Cow<str>],
168 _args: &[Cow<str>],
153− _event: PromptEvent,
169+ event: PromptEvent,
154) -> anyhow::Result<()> {
170) -> anyhow::Result<()> {
171+ if event != PromptEvent::Validate {
155 let document_ids = buffer_gather_others_impl(cx.editor);
175 let document_ids = buffer_gather_others_impl(cx.editor);
156 buffer_close_by_ids_impl(cx.editor, &document_ids, true)
176 buffer_close_by_ids_impl(cx.editor, &document_ids, true)
@@ -163,8 +183,12 @@ fn buffer_gather_all_impl(editor: &mut Editor) -> Vec<DocumentId> {
163fn buffer_close_all(
183fn buffer_close_all(
164 cx: &mut compositor::Context,
184 cx: &mut compositor::Context,
165 _args: &[Cow<str>],
185 _args: &[Cow<str>],
166− _event: PromptEvent,
186+ event: PromptEvent,
167) -> anyhow::Result<()> {
187) -> anyhow::Result<()> {
188+ if event != PromptEvent::Validate {
168 let document_ids = buffer_gather_all_impl(cx.editor);
192 let document_ids = buffer_gather_all_impl(cx.editor);
169 buffer_close_by_ids_impl(cx.editor, &document_ids, false)
193 buffer_close_by_ids_impl(cx.editor, &document_ids, false)
@@ -172,8 +196,12 @@ fn buffer_close_all(
172fn force_buffer_close_all(
196fn force_buffer_close_all(
173 cx: &mut compositor::Context,
197 cx: &mut compositor::Context,
174 _args: &[Cow<str>],
198 _args: &[Cow<str>],
175− _event: PromptEvent,
199+ event: PromptEvent,
176) -> anyhow::Result<()> {
200) -> anyhow::Result<()> {
201+ if event != PromptEvent::Validate {
177 let document_ids = buffer_gather_all_impl(cx.editor);
205 let document_ids = buffer_gather_all_impl(cx.editor);
178 buffer_close_by_ids_impl(cx.editor, &document_ids, true)
206 buffer_close_by_ids_impl(cx.editor, &document_ids, true)
@@ -181,8 +209,12 @@ fn force_buffer_close_all(
181fn buffer_next(
209fn buffer_next(
182 cx: &mut compositor::Context,
210 cx: &mut compositor::Context,
183 _args: &[Cow<str>],
211 _args: &[Cow<str>],
184− _event: PromptEvent,
212+ event: PromptEvent,
185) -> anyhow::Result<()> {
213) -> anyhow::Result<()> {
214+ if event != PromptEvent::Validate {
186 goto_buffer(cx.editor, Direction::Forward);
218 goto_buffer(cx.editor, Direction::Forward);
@@ -190,8 +222,12 @@ fn buffer_next(
190fn buffer_previous(
222fn buffer_previous(
191 cx: &mut compositor::Context,
223 cx: &mut compositor::Context,
192 _args: &[Cow<str>],
224 _args: &[Cow<str>],
193− _event: PromptEvent,
225+ event: PromptEvent,
194) -> anyhow::Result<()> {
226) -> anyhow::Result<()> {
227+ if event != PromptEvent::Validate {
195 goto_buffer(cx.editor, Direction::Backward);
231 goto_buffer(cx.editor, Direction::Backward);
@@ -242,24 +278,36 @@ fn write_impl(
243 cx: &mut compositor::Context,
279 cx: &mut compositor::Context,
244 args: &[Cow<str>],
280 args: &[Cow<str>],
245− _event: PromptEvent,
281+ event: PromptEvent,
246) -> anyhow::Result<()> {
282) -> anyhow::Result<()> {
283+ if event != PromptEvent::Validate {
247 write_impl(cx, args.first(), false)
287 write_impl(cx, args.first(), false)
250fn force_write(
290fn force_write(
251 cx: &mut compositor::Context,
291 cx: &mut compositor::Context,
252 args: &[Cow<str>],
292 args: &[Cow<str>],
253− _event: PromptEvent,
293+ event: PromptEvent,
254) -> anyhow::Result<()> {
294) -> anyhow::Result<()> {
295+ if event != PromptEvent::Validate {
255 write_impl(cx, args.first(), true)
299 write_impl(cx, args.first(), true)
258fn new_file(
302fn new_file(
259 cx: &mut compositor::Context,
303 cx: &mut compositor::Context,
260 _args: &[Cow<str>],
304 _args: &[Cow<str>],
261− _event: PromptEvent,
305+ event: PromptEvent,
262) -> anyhow::Result<()> {
306) -> anyhow::Result<()> {
307+ if event != PromptEvent::Validate {
263 cx.editor.new_file(Action::Replace);
311 cx.editor.new_file(Action::Replace);
@@ -268,8 +316,12 @@ fn new_file(
268fn format(
316fn format(
269 cx: &mut compositor::Context,
317 cx: &mut compositor::Context,
270 _args: &[Cow<str>],
318 _args: &[Cow<str>],
271− _event: PromptEvent,
319+ event: PromptEvent,
272) -> anyhow::Result<()> {
320) -> anyhow::Result<()> {
321+ if event != PromptEvent::Validate {
273 let doc = doc!(cx.editor);
325 let doc = doc!(cx.editor);
274 if let Some(format) = doc.format() {
326 if let Some(format) = doc.format() {
275 let callback =
327 let callback =
@@ -282,8 +334,12 @@ fn format(
282fn set_indent_style(
334fn set_indent_style(
283 cx: &mut compositor::Context,
335 cx: &mut compositor::Context,
284 args: &[Cow<str>],
336 args: &[Cow<str>],
285− _event: PromptEvent,
337+ event: PromptEvent,
286) -> anyhow::Result<()> {
338) -> anyhow::Result<()> {
339+ if event != PromptEvent::Validate {
287 use IndentStyle::*;
343 use IndentStyle::*;
289 // If no argument, report current indent style.
345 // If no argument, report current indent style.
@@ -321,8 +377,12 @@ fn set_indent_style(
321fn set_line_ending(
377fn set_line_ending(
322 cx: &mut compositor::Context,
378 cx: &mut compositor::Context,
323 args: &[Cow<str>],
379 args: &[Cow<str>],
324− _event: PromptEvent,
380+ event: PromptEvent,
325) -> anyhow::Result<()> {
381) -> anyhow::Result<()> {
382+ if event != PromptEvent::Validate {
326 use LineEnding::*;
386 use LineEnding::*;
328 // If no argument, report current line ending setting.
388 // If no argument, report current line ending setting.
@@ -391,8 +451,12 @@ fn set_line_ending(
391fn earlier(
451fn earlier(
392 cx: &mut compositor::Context,
452 cx: &mut compositor::Context,
393 args: &[Cow<str>],
453 args: &[Cow<str>],
394− _event: PromptEvent,
454+ event: PromptEvent,
395) -> anyhow::Result<()> {
455) -> anyhow::Result<()> {
456+ if event != PromptEvent::Validate {
396 let uk = args.join(" ").parse::<UndoKind>().map_err(|s| anyhow!(s))?;
460 let uk = args.join(" ").parse::<UndoKind>().map_err(|s| anyhow!(s))?;
398 let (view, doc) = current!(cx.editor);
462 let (view, doc) = current!(cx.editor);
@@ -407,8 +471,12 @@ fn earlier(
408 cx: &mut compositor::Context,
472 cx: &mut compositor::Context,
409 args: &[Cow<str>],
473 args: &[Cow<str>],
410− _event: PromptEvent,
474+ event: PromptEvent,
411) -> anyhow::Result<()> {
475) -> anyhow::Result<()> {
476+ if event != PromptEvent::Validate {
412 let uk = args.join(" ").parse::<UndoKind>().map_err(|s| anyhow!(s))?;
480 let uk = args.join(" ").parse::<UndoKind>().map_err(|s| anyhow!(s))?;
413 let (view, doc) = current!(cx.editor);
481 let (view, doc) = current!(cx.editor);
414 let success = doc.later(view.id, uk);
482 let success = doc.later(view.id, uk);
@@ -424,6 +492,10 @@ fn write_quit(
424 args: &[Cow<str>],
492 args: &[Cow<str>],
425 event: PromptEvent,
493 event: PromptEvent,
426) -> anyhow::Result<()> {
494) -> anyhow::Result<()> {
495+ if event != PromptEvent::Validate {
427 write_impl(cx, args.first(), false)?;
499 write_impl(cx, args.first(), false)?;
428 helix_lsp::block_on(cx.jobs.finish())?;
500 helix_lsp::block_on(cx.jobs.finish())?;
429 quit(cx, &[], event)
501 quit(cx, &[], event)
@@ -434,6 +506,10 @@ fn force_write_quit(
434 args: &[Cow<str>],
506 args: &[Cow<str>],
435 event: PromptEvent,
507 event: PromptEvent,
436) -> anyhow::Result<()> {
508) -> anyhow::Result<()> {
509+ if event != PromptEvent::Validate {
437 write_impl(cx, args.first(), true)?;
513 write_impl(cx, args.first(), true)?;
438 force_quit(cx, &[], event)
514 force_quit(cx, &[], event)
@@ -463,10 +539,14 @@ pub(super) fn buffers_remaining_impl(editor: &mut Editor) -> anyhow::Result<()>
463fn write_all_impl(
539fn write_all_impl(
464 cx: &mut compositor::Context,
540 cx: &mut compositor::Context,
465 _args: &[Cow<str>],
541 _args: &[Cow<str>],
466− _event: PromptEvent,
542+ event: PromptEvent,
467 quit: bool,
543 quit: bool,
468 force: bool,
544 force: bool,
469) -> anyhow::Result<()> {
545) -> anyhow::Result<()> {
546+ if event != PromptEvent::Validate {
470 let mut errors = String::new();
550 let mut errors = String::new();
471 let auto_format = cx.editor.config().auto_format;
551 let auto_format = cx.editor.config().auto_format;
472 let jobs = &mut cx.jobs;
552 let jobs = &mut cx.jobs;
@@ -520,6 +600,10 @@ fn write_all(
520 args: &[Cow<str>],
600 args: &[Cow<str>],
521 event: PromptEvent,
601 event: PromptEvent,
522) -> anyhow::Result<()> {
602) -> anyhow::Result<()> {
603+ if event != PromptEvent::Validate {
523 write_all_impl(cx, args, event, false, false)
607 write_all_impl(cx, args, event, false, false)
@@ -528,6 +612,10 @@ fn write_all_quit(
528 args: &[Cow<str>],
612 args: &[Cow<str>],
529 event: PromptEvent,
613 event: PromptEvent,
530) -> anyhow::Result<()> {
614) -> anyhow::Result<()> {
615+ if event != PromptEvent::Validate {
531 write_all_impl(cx, args, event, true, false)
619 write_all_impl(cx, args, event, true, false)
@@ -536,6 +624,10 @@ fn force_write_all_quit(
536 args: &[Cow<str>],
624 args: &[Cow<str>],
537 event: PromptEvent,
625 event: PromptEvent,
538) -> anyhow::Result<()> {
626) -> anyhow::Result<()> {
627+ if event != PromptEvent::Validate {
539 write_all_impl(cx, args, event, true, true)
631 write_all_impl(cx, args, event, true, true)
@@ -556,24 +648,36 @@ fn quit_all_impl(editor: &mut Editor, force: bool) -> anyhow::Result<()> {
556fn quit_all(
648fn quit_all(
557 cx: &mut compositor::Context,
649 cx: &mut compositor::Context,
558 _args: &[Cow<str>],
650 _args: &[Cow<str>],
559− _event: PromptEvent,
651+ event: PromptEvent,
560) -> anyhow::Result<()> {
652) -> anyhow::Result<()> {
653+ if event != PromptEvent::Validate {
561 quit_all_impl(cx.editor, false)
657 quit_all_impl(cx.editor, false)
564fn force_quit_all(
660fn force_quit_all(
565 cx: &mut compositor::Context,
661 cx: &mut compositor::Context,
566 _args: &[Cow<str>],
662 _args: &[Cow<str>],
567− _event: PromptEvent,
663+ event: PromptEvent,
568) -> anyhow::Result<()> {
664) -> anyhow::Result<()> {
665+ if event != PromptEvent::Validate {
569 quit_all_impl(cx.editor, true)
669 quit_all_impl(cx.editor, true)
573 cx: &mut compositor::Context,
673 cx: &mut compositor::Context,
574 args: &[Cow<str>],
674 args: &[Cow<str>],
575− _event: PromptEvent,
675+ event: PromptEvent,
576) -> anyhow::Result<()> {
676) -> anyhow::Result<()> {
677+ if event != PromptEvent::Validate {
577 let exit_code = args
681 let exit_code = args
579 .and_then(|code| code.parse::<i32>().ok())
683 .and_then(|code| code.parse::<i32>().ok())
@@ -586,8 +690,12 @@ fn cquit(
586fn force_cquit(
690fn force_cquit(
587 cx: &mut compositor::Context,
691 cx: &mut compositor::Context,
588 args: &[Cow<str>],
692 args: &[Cow<str>],
589− _event: PromptEvent,
693+ event: PromptEvent,
590) -> anyhow::Result<()> {
694) -> anyhow::Result<()> {
695+ if event != PromptEvent::Validate {
591 let exit_code = args
699 let exit_code = args
593 .and_then(|code| code.parse::<i32>().ok())
701 .and_then(|code| code.parse::<i32>().ok())
@@ -600,35 +708,61 @@ fn force_cquit(
601 cx: &mut compositor::Context,
709 cx: &mut compositor::Context,
602 args: &[Cow<str>],
710 args: &[Cow<str>],
603− _event: PromptEvent,
711+ event: PromptEvent,
604−) -> anyhow::Result<()> {
712+) -> anyhow::Result<()> {
605− let theme = args.first().context("Theme not provided")?;
610− .with_context(|| format!("Failed setting theme {}", theme))?;
611− let true_color = cx.editor.config().true_color || crate::true_color();
713+ let true_color = cx.editor.config.load().true_color || crate::true_color();
715+ PromptEvent::Abort => {
716+ cx.editor.unset_theme_preview();
718+ PromptEvent::Update => {
719+ if let Some(theme_name) = args.first() {
720+ if let Ok(theme) = cx.editor.theme_loader.load(theme_name) {
612− if !(true_color || theme.is_16_color()) {
721+ if !(true_color || theme.is_16_color()) {
722+ bail!("Unsupported theme: theme requires true color support");
724+ cx.editor.set_theme_preview(theme);
728+ PromptEvent::Validate => {
729+ let theme_name = args.first().with_context(|| "Theme name not provided")?;
734+ .with_context(|| "Theme does not exist")?;
735+ if !(true_color || theme.is_16_color()) {
613− bail!("Unsupported theme: theme requires true color support");
736+ bail!("Unsupported theme: theme requires true color support");
615− cx.editor.set_theme(theme);
738+ cx.editor.set_theme(theme);
619fn yank_main_selection_to_clipboard(
745fn yank_main_selection_to_clipboard(
620 cx: &mut compositor::Context,
746 cx: &mut compositor::Context,
621 _args: &[Cow<str>],
747 _args: &[Cow<str>],
622− _event: PromptEvent,
748+ event: PromptEvent,
623) -> anyhow::Result<()> {
749) -> anyhow::Result<()> {
750+ if event != PromptEvent::Validate {
624 yank_main_selection_to_clipboard_impl(cx.editor, ClipboardType::Clipboard)
754 yank_main_selection_to_clipboard_impl(cx.editor, ClipboardType::Clipboard)
627fn yank_joined_to_clipboard(
757fn yank_joined_to_clipboard(
628 cx: &mut compositor::Context,
758 cx: &mut compositor::Context,
629 args: &[Cow<str>],
759 args: &[Cow<str>],
630− _event: PromptEvent,
760+ event: PromptEvent,
631) -> anyhow::Result<()> {
761) -> anyhow::Result<()> {
762+ if event != PromptEvent::Validate {
632 let doc = doc!(cx.editor);
766 let doc = doc!(cx.editor);
633 let default_sep = Cow::Borrowed(doc.line_ending.as_str());
767 let default_sep = Cow::Borrowed(doc.line_ending.as_str());
634 let separator = args.first().unwrap_or(&default_sep);
768 let separator = args.first().unwrap_or(&default_sep);
@@ -638,16 +772,24 @@ fn yank_joined_to_clipboard(
638fn yank_main_selection_to_primary_clipboard(
772fn yank_main_selection_to_primary_clipboard(
639 cx: &mut compositor::Context,
773 cx: &mut compositor::Context,
640 _args: &[Cow<str>],
774 _args: &[Cow<str>],
641− _event: PromptEvent,
775+ event: PromptEvent,
642) -> anyhow::Result<()> {
776) -> anyhow::Result<()> {
777+ if event != PromptEvent::Validate {
643 yank_main_selection_to_clipboard_impl(cx.editor, ClipboardType::Selection)
781 yank_main_selection_to_clipboard_impl(cx.editor, ClipboardType::Selection)
646fn yank_joined_to_primary_clipboard(
784fn yank_joined_to_primary_clipboard(
647 cx: &mut compositor::Context,
785 cx: &mut compositor::Context,
648 args: &[Cow<str>],
786 args: &[Cow<str>],
649− _event: PromptEvent,
787+ event: PromptEvent,
650) -> anyhow::Result<()> {
788) -> anyhow::Result<()> {
789+ if event != PromptEvent::Validate {
651 let doc = doc!(cx.editor);
793 let doc = doc!(cx.editor);
652 let default_sep = Cow::Borrowed(doc.line_ending.as_str());
794 let default_sep = Cow::Borrowed(doc.line_ending.as_str());
653 let separator = args.first().unwrap_or(&default_sep);
795 let separator = args.first().unwrap_or(&default_sep);
@@ -657,32 +799,48 @@ fn yank_joined_to_primary_clipboard(
657fn paste_clipboard_after(
799fn paste_clipboard_after(
658 cx: &mut compositor::Context,
800 cx: &mut compositor::Context,
659 _args: &[Cow<str>],
801 _args: &[Cow<str>],
660− _event: PromptEvent,
802+ event: PromptEvent,
661) -> anyhow::Result<()> {
803) -> anyhow::Result<()> {
804+ if event != PromptEvent::Validate {
662 paste_clipboard_impl(cx.editor, Paste::After, ClipboardType::Clipboard, 1)
808 paste_clipboard_impl(cx.editor, Paste::After, ClipboardType::Clipboard, 1)
665fn paste_clipboard_before(
811fn paste_clipboard_before(
666 cx: &mut compositor::Context,
812 cx: &mut compositor::Context,
667 _args: &[Cow<str>],
813 _args: &[Cow<str>],
668− _event: PromptEvent,
814+ event: PromptEvent,
669) -> anyhow::Result<()> {
815) -> anyhow::Result<()> {
816+ if event != PromptEvent::Validate {
670 paste_clipboard_impl(cx.editor, Paste::Before, ClipboardType::Clipboard, 1)
820 paste_clipboard_impl(cx.editor, Paste::Before, ClipboardType::Clipboard, 1)
673fn paste_primary_clipboard_after(
823fn paste_primary_clipboard_after(
674 cx: &mut compositor::Context,
824 cx: &mut compositor::Context,
675 _args: &[Cow<str>],
825 _args: &[Cow<str>],
676− _event: PromptEvent,
826+ event: PromptEvent,
677) -> anyhow::Result<()> {
827) -> anyhow::Result<()> {
828+ if event != PromptEvent::Validate {
678 paste_clipboard_impl(cx.editor, Paste::After, ClipboardType::Selection, 1)
832 paste_clipboard_impl(cx.editor, Paste::After, ClipboardType::Selection, 1)
681fn paste_primary_clipboard_before(
835fn paste_primary_clipboard_before(
682 cx: &mut compositor::Context,
836 cx: &mut compositor::Context,
683 _args: &[Cow<str>],
837 _args: &[Cow<str>],
684− _event: PromptEvent,
838+ event: PromptEvent,
685) -> anyhow::Result<()> {
839) -> anyhow::Result<()> {
840+ if event != PromptEvent::Validate {
686 paste_clipboard_impl(cx.editor, Paste::Before, ClipboardType::Selection, 1)
844 paste_clipboard_impl(cx.editor, Paste::Before, ClipboardType::Selection, 1)
@@ -710,24 +868,36 @@ fn replace_selections_with_clipboard_impl(
710fn replace_selections_with_clipboard(
868fn replace_selections_with_clipboard(
711 cx: &mut compositor::Context,
869 cx: &mut compositor::Context,
712 _args: &[Cow<str>],
870 _args: &[Cow<str>],
713− _event: PromptEvent,
871+ event: PromptEvent,
714) -> anyhow::Result<()> {
872) -> anyhow::Result<()> {
873+ if event != PromptEvent::Validate {
715 replace_selections_with_clipboard_impl(cx, ClipboardType::Clipboard)
877 replace_selections_with_clipboard_impl(cx, ClipboardType::Clipboard)
718fn replace_selections_with_primary_clipboard(
880fn replace_selections_with_primary_clipboard(
719 cx: &mut compositor::Context,
881 cx: &mut compositor::Context,
720 _args: &[Cow<str>],
882 _args: &[Cow<str>],
721− _event: PromptEvent,
883+ event: PromptEvent,
722) -> anyhow::Result<()> {
884) -> anyhow::Result<()> {
885+ if event != PromptEvent::Validate {
723 replace_selections_with_clipboard_impl(cx, ClipboardType::Selection)
889 replace_selections_with_clipboard_impl(cx, ClipboardType::Selection)
726fn show_clipboard_provider(
892fn show_clipboard_provider(
727 cx: &mut compositor::Context,
893 cx: &mut compositor::Context,
728 _args: &[Cow<str>],
894 _args: &[Cow<str>],
729− _event: PromptEvent,
895+ event: PromptEvent,
730) -> anyhow::Result<()> {
896) -> anyhow::Result<()> {
897+ if event != PromptEvent::Validate {
731 cx.editor
901 cx.editor
732 .set_status(cx.editor.clipboard_provider.name().to_string());
902 .set_status(cx.editor.clipboard_provider.name().to_string());
@@ -736,8 +906,12 @@ fn show_clipboard_provider(
736fn change_current_directory(
906fn change_current_directory(
737 cx: &mut compositor::Context,
907 cx: &mut compositor::Context,
738 args: &[Cow<str>],
908 args: &[Cow<str>],
739− _event: PromptEvent,
909+ event: PromptEvent,
740) -> anyhow::Result<()> {
910) -> anyhow::Result<()> {
911+ if event != PromptEvent::Validate {
741 let dir = helix_core::path::expand_tilde(
915 let dir = helix_core::path::expand_tilde(
742 args.first()
916 args.first()
743 .context("target directory not provided")?
917 .context("target directory not provided")?
@@ -760,8 +934,12 @@ fn change_current_directory(
760fn show_current_directory(
934fn show_current_directory(
761 cx: &mut compositor::Context,
935 cx: &mut compositor::Context,
762 _args: &[Cow<str>],
936 _args: &[Cow<str>],
763− _event: PromptEvent,
937+ event: PromptEvent,
764) -> anyhow::Result<()> {
938) -> anyhow::Result<()> {
939+ if event != PromptEvent::Validate {
765 let cwd = std::env::current_dir().context("Couldn't get the new working directory")?;
943 let cwd = std::env::current_dir().context("Couldn't get the new working directory")?;
766 cx.editor
944 cx.editor
767 .set_status(format!("Current working directory is {}", cwd.display()));
945 .set_status(format!("Current working directory is {}", cwd.display()));
@@ -772,8 +950,12 @@ fn show_current_directory(
772fn set_encoding(
950fn set_encoding(
773 cx: &mut compositor::Context,
951 cx: &mut compositor::Context,
774 args: &[Cow<str>],
952 args: &[Cow<str>],
775− _event: PromptEvent,
953+ event: PromptEvent,
776) -> anyhow::Result<()> {
954) -> anyhow::Result<()> {
955+ if event != PromptEvent::Validate {
777 let doc = doc_mut!(cx.editor);
959 let doc = doc_mut!(cx.editor);
778 if let Some(label) = args.first() {
960 if let Some(label) = args.first() {
779 doc.set_encoding(label)
961 doc.set_encoding(label)
@@ -788,8 +970,12 @@ fn set_encoding(
788fn reload(
970fn reload(
789 cx: &mut compositor::Context,
971 cx: &mut compositor::Context,
790 _args: &[Cow<str>],
972 _args: &[Cow<str>],
791− _event: PromptEvent,
973+ event: PromptEvent,
792) -> anyhow::Result<()> {
974) -> anyhow::Result<()> {
975+ if event != PromptEvent::Validate {
793 let scrolloff = cx.editor.config().scrolloff;
979 let scrolloff = cx.editor.config().scrolloff;
794 let (view, doc) = current!(cx.editor);
980 let (view, doc) = current!(cx.editor);
795 doc.reload(view.id).map(|_| {
981 doc.reload(view.id).map(|_| {
@@ -800,8 +986,12 @@ fn reload(
800fn tree_sitter_scopes(
986fn tree_sitter_scopes(
801 cx: &mut compositor::Context,
987 cx: &mut compositor::Context,
802 _args: &[Cow<str>],
988 _args: &[Cow<str>],
803− _event: PromptEvent,
989+ event: PromptEvent,
804) -> anyhow::Result<()> {
990) -> anyhow::Result<()> {
991+ if event != PromptEvent::Validate {
805 let (view, doc) = current!(cx.editor);
995 let (view, doc) = current!(cx.editor);
806 let text = doc.text().slice(..);
996 let text = doc.text().slice(..);
@@ -814,8 +1004,12 @@ fn tree_sitter_scopes(
814fn vsplit(
1004fn vsplit(
815 cx: &mut compositor::Context,
1005 cx: &mut compositor::Context,
816 args: &[Cow<str>],
1006 args: &[Cow<str>],
817− _event: PromptEvent,
1007+ event: PromptEvent,
818) -> anyhow::Result<()> {
1008) -> anyhow::Result<()> {
1009+ if event != PromptEvent::Validate {
819 let id = view!(cx.editor).doc;
1013 let id = view!(cx.editor).doc;
821 if args.is_empty() {
1015 if args.is_empty() {
@@ -833,8 +1027,12 @@ fn vsplit(
833fn hsplit(
1027fn hsplit(
834 cx: &mut compositor::Context,
1028 cx: &mut compositor::Context,
835 args: &[Cow<str>],
1029 args: &[Cow<str>],
836− _event: PromptEvent,
1030+ event: PromptEvent,
837) -> anyhow::Result<()> {
1031) -> anyhow::Result<()> {
1032+ if event != PromptEvent::Validate {
838 let id = view!(cx.editor).doc;
1036 let id = view!(cx.editor).doc;
840 if args.is_empty() {
1038 if args.is_empty() {
@@ -852,8 +1050,12 @@ fn hsplit(
852fn vsplit_new(
1050fn vsplit_new(
853 cx: &mut compositor::Context,
1051 cx: &mut compositor::Context,
854 _args: &[Cow<str>],
1052 _args: &[Cow<str>],
855− _event: PromptEvent,
1053+ event: PromptEvent,
856) -> anyhow::Result<()> {
1054) -> anyhow::Result<()> {
1055+ if event != PromptEvent::Validate {
857 cx.editor.new_file(Action::VerticalSplit);
1059 cx.editor.new_file(Action::VerticalSplit);
@@ -862,8 +1064,12 @@ fn vsplit_new(
862fn hsplit_new(
1064fn hsplit_new(
863 cx: &mut compositor::Context,
1065 cx: &mut compositor::Context,
864 _args: &[Cow<str>],
1066 _args: &[Cow<str>],
865− _event: PromptEvent,
1067+ event: PromptEvent,
866) -> anyhow::Result<()> {
1068) -> anyhow::Result<()> {
1069+ if event != PromptEvent::Validate {
867 cx.editor.new_file(Action::HorizontalSplit);
1073 cx.editor.new_file(Action::HorizontalSplit);
@@ -872,8 +1078,12 @@ fn hsplit_new(
872fn debug_eval(
1078fn debug_eval(
873 cx: &mut compositor::Context,
1079 cx: &mut compositor::Context,
874 args: &[Cow<str>],
1080 args: &[Cow<str>],
875− _event: PromptEvent,
1081+ event: PromptEvent,
876) -> anyhow::Result<()> {
1082) -> anyhow::Result<()> {
1083+ if event != PromptEvent::Validate {
877 if let Some(debugger) = cx.editor.debugger.as_mut() {
1087 if let Some(debugger) = cx.editor.debugger.as_mut() {
878 let (frame, thread_id) = match (debugger.active_frame, debugger.thread_id) {
1088 let (frame, thread_id) = match (debugger.active_frame, debugger.thread_id) {
879 (Some(frame), Some(thread_id)) => (frame, thread_id),
1089 (Some(frame), Some(thread_id)) => (frame, thread_id),
@@ -894,8 +1104,12 @@ fn debug_eval(
894fn debug_start(
1104fn debug_start(
895 cx: &mut compositor::Context,
1105 cx: &mut compositor::Context,
896 args: &[Cow<str>],
1106 args: &[Cow<str>],
897− _event: PromptEvent,
1107+ event: PromptEvent,
898) -> anyhow::Result<()> {
1108) -> anyhow::Result<()> {
1109+ if event != PromptEvent::Validate {
899 let mut args = args.to_owned();
1113 let mut args = args.to_owned();
900 let name = match args.len() {
1114 let name = match args.len() {
901 0 => None,
1115 0 => None,
@@ -907,8 +1121,12 @@ fn debug_start(
907fn debug_remote(
1121fn debug_remote(
908 cx: &mut compositor::Context,
1122 cx: &mut compositor::Context,
909 args: &[Cow<str>],
1123 args: &[Cow<str>],
910− _event: PromptEvent,
1124+ event: PromptEvent,
911) -> anyhow::Result<()> {
1125) -> anyhow::Result<()> {
1126+ if event != PromptEvent::Validate {
912 let mut args = args.to_owned();
1130 let mut args = args.to_owned();
913 let address = match args.len() {
1131 let address = match args.len() {
914 0 => None,
1132 0 => None,
@@ -924,8 +1142,12 @@ fn debug_remote(
924fn tutor(
1142fn tutor(
925 cx: &mut compositor::Context,
1143 cx: &mut compositor::Context,
926 _args: &[Cow<str>],
1144 _args: &[Cow<str>],
927− _event: PromptEvent,
1145+ event: PromptEvent,
928) -> anyhow::Result<()> {
1146) -> anyhow::Result<()> {
1147+ if event != PromptEvent::Validate {
929 let path = helix_loader::runtime_dir().join("tutor.txt");
1151 let path = helix_loader::runtime_dir().join("tutor.txt");
930 cx.editor.open(&path, Action::Replace)?;
1152 cx.editor.open(&path, Action::Replace)?;
931 // Unset path to prevent accidentally saving to the original tutor file.
1153 // Unset path to prevent accidentally saving to the original tutor file.
@@ -936,8 +1158,12 @@ fn tutor(
936pub(super) fn goto_line_number(
1158pub(super) fn goto_line_number(
937 cx: &mut compositor::Context,
1159 cx: &mut compositor::Context,
938 args: &[Cow<str>],
1160 args: &[Cow<str>],
939− _event: PromptEvent,
1161+ event: PromptEvent,
940) -> anyhow::Result<()> {
1162) -> anyhow::Result<()> {
1163+ if event != PromptEvent::Validate {
941 ensure!(!args.is_empty(), "Line number required");
1167 ensure!(!args.is_empty(), "Line number required");
943 let line = args[0].parse::<usize>()?;
1169 let line = args[0].parse::<usize>()?;
@@ -954,8 +1180,12 @@ pub(super) fn goto_line_number(
954fn get_option(
1180fn get_option(
955 cx: &mut compositor::Context,
1181 cx: &mut compositor::Context,
956 args: &[Cow<str>],
1182 args: &[Cow<str>],
957− _event: PromptEvent,
1183+ event: PromptEvent,
958) -> anyhow::Result<()> {
1184) -> anyhow::Result<()> {
1185+ if event != PromptEvent::Validate {
959 if args.len() != 1 {
1189 if args.len() != 1 {
960 anyhow::bail!("Bad arguments. Usage: `:get key`");
1190 anyhow::bail!("Bad arguments. Usage: `:get key`");
@@ -976,8 +1206,12 @@ fn get_option(
976fn set_option(
1206fn set_option(
977 cx: &mut compositor::Context,
1207 cx: &mut compositor::Context,
978 args: &[Cow<str>],
1208 args: &[Cow<str>],
979− _event: PromptEvent,
1209+ event: PromptEvent,
980) -> anyhow::Result<()> {
1210) -> anyhow::Result<()> {
1211+ if event != PromptEvent::Validate {
981 if args.len() != 2 {
1215 if args.len() != 2 {
982 anyhow::bail!("Bad arguments. Usage: `:set key field`");
1216 anyhow::bail!("Bad arguments. Usage: `:set key field`");
@@ -1009,8 +1243,12 @@ fn set_option(
1009fn language(
1243fn language(
1010 cx: &mut compositor::Context,
1244 cx: &mut compositor::Context,
1011 args: &[Cow<str>],
1245 args: &[Cow<str>],
1012− _event: PromptEvent,
1246+ event: PromptEvent,
1013) -> anyhow::Result<()> {
1247) -> anyhow::Result<()> {
1248+ if event != PromptEvent::Validate {
1014 if args.len() != 1 {
1252 if args.len() != 1 {
1015 anyhow::bail!("Bad arguments. Usage: `:set-language language`");
1253 anyhow::bail!("Bad arguments. Usage: `:set-language language`");
@@ -1023,19 +1261,23 @@ fn language(
1027− cx: &mut compositor::Context,
1264+fn sort(cx: &mut compositor::Context, args: &[Cow<str>], event: PromptEvent) -> anyhow::Result<()> {
1028− args: &[Cow<str>],
1265+ if event != PromptEvent::Validate {
1029− _event: PromptEvent,
1266+ return Ok(());
1030−) -> anyhow::Result<()> {
1267+ }
1031 sort_impl(cx, args, false)
1269 sort_impl(cx, args, false)
1034fn sort_reverse(
1272fn sort_reverse(
1035 cx: &mut compositor::Context,
1273 cx: &mut compositor::Context,
1036 args: &[Cow<str>],
1274 args: &[Cow<str>],
1037− _event: PromptEvent,
1275+ event: PromptEvent,
1038) -> anyhow::Result<()> {
1276) -> anyhow::Result<()> {
1277+ if event != PromptEvent::Validate {
1039 sort_impl(cx, args, true)
1281 sort_impl(cx, args, true)
@@ -1076,8 +1318,12 @@ fn sort_impl(
1076fn reflow(
1318fn reflow(
1077 cx: &mut compositor::Context,
1319 cx: &mut compositor::Context,
1078 args: &[Cow<str>],
1320 args: &[Cow<str>],
1079− _event: PromptEvent,
1321+ event: PromptEvent,
1080) -> anyhow::Result<()> {
1322) -> anyhow::Result<()> {
1323+ if event != PromptEvent::Validate {
1081 let (view, doc) = current!(cx.editor);
1327 let (view, doc) = current!(cx.editor);
1083 const DEFAULT_MAX_LEN: usize = 79;
1329 const DEFAULT_MAX_LEN: usize = 79;
@@ -1115,8 +1361,12 @@ fn reflow(
1115fn tree_sitter_subtree(
1361fn tree_sitter_subtree(
1116 cx: &mut compositor::Context,
1362 cx: &mut compositor::Context,
1117 _args: &[Cow<str>],
1363 _args: &[Cow<str>],
1118− _event: PromptEvent,
1364+ event: PromptEvent,
1119) -> anyhow::Result<()> {
1365) -> anyhow::Result<()> {
1366+ if event != PromptEvent::Validate {
1120 let (view, doc) = current!(cx.editor);
1370 let (view, doc) = current!(cx.editor);
1122 if let Some(syntax) = doc.syntax() {
1372 if let Some(syntax) = doc.syntax() {
@@ -1151,8 +1401,12 @@ fn tree_sitter_subtree(
1151fn open_config(
1401fn open_config(
1152 cx: &mut compositor::Context,
1402 cx: &mut compositor::Context,
1153 _args: &[Cow<str>],
1403 _args: &[Cow<str>],
1154− _event: PromptEvent,
1404+ event: PromptEvent,
1155) -> anyhow::Result<()> {
1405) -> anyhow::Result<()> {
1406+ if event != PromptEvent::Validate {
1156 cx.editor
1410 cx.editor
1157 .open(&helix_loader::config_file(), Action::Replace)?;
1411 .open(&helix_loader::config_file(), Action::Replace)?;
@@ -1161,8 +1415,12 @@ fn open_config(
1161fn open_log(
1415fn open_log(
1162 cx: &mut compositor::Context,
1416 cx: &mut compositor::Context,
1163 _args: &[Cow<str>],
1417 _args: &[Cow<str>],
1164− _event: PromptEvent,
1418+ event: PromptEvent,
1165) -> anyhow::Result<()> {
1419) -> anyhow::Result<()> {
1420+ if event != PromptEvent::Validate {
1166 cx.editor.open(&helix_loader::log_file(), Action::Replace)?;
1424 cx.editor.open(&helix_loader::log_file(), Action::Replace)?;
@@ -1170,8 +1428,12 @@ fn open_log(
1170fn refresh_config(
1428fn refresh_config(
1171 cx: &mut compositor::Context,
1429 cx: &mut compositor::Context,
1172 _args: &[Cow<str>],
1430 _args: &[Cow<str>],
1173− _event: PromptEvent,
1431+ event: PromptEvent,
1174) -> anyhow::Result<()> {
1432) -> anyhow::Result<()> {
1433+ if event != PromptEvent::Validate {
1175 cx.editor.config_events.0.send(ConfigEvent::Refresh)?;
1437 cx.editor.config_events.0.send(ConfigEvent::Refresh)?;
@@ -1179,8 +1441,12 @@ fn refresh_config(
1179fn append_output(
1441fn append_output(
1180 cx: &mut compositor::Context,
1442 cx: &mut compositor::Context,
1181 args: &[Cow<str>],
1443 args: &[Cow<str>],
1182− _event: PromptEvent,
1444+ event: PromptEvent,
1183) -> anyhow::Result<()> {
1445) -> anyhow::Result<()> {
1446+ if event != PromptEvent::Validate {
1184 ensure!(!args.is_empty(), "Shell command required");
1450 ensure!(!args.is_empty(), "Shell command required");
1185 shell(cx, &args.join(" "), &ShellBehavior::Append);
1451 shell(cx, &args.join(" "), &ShellBehavior::Append);
@@ -1189,18 +1455,22 @@ fn append_output(
1189fn insert_output(
1455fn insert_output(
1190 cx: &mut compositor::Context,
1456 cx: &mut compositor::Context,
1191 args: &[Cow<str>],
1457 args: &[Cow<str>],
1192− _event: PromptEvent,
1458+ event: PromptEvent,
1193) -> anyhow::Result<()> {
1459) -> anyhow::Result<()> {
1460+ if event != PromptEvent::Validate {
1194 ensure!(!args.is_empty(), "Shell command required");
1464 ensure!(!args.is_empty(), "Shell command required");
1195 shell(cx, &args.join(" "), &ShellBehavior::Insert);
1465 shell(cx, &args.join(" "), &ShellBehavior::Insert);
1200− cx: &mut compositor::Context,
1469+fn pipe(cx: &mut compositor::Context, args: &[Cow<str>], event: PromptEvent) -> anyhow::Result<()> {
1201− args: &[Cow<str>],
1470+ if event != PromptEvent::Validate {
1202− _event: PromptEvent,
1471+ return Ok(());
1203−) -> anyhow::Result<()> {
1472+ }
1204 ensure!(!args.is_empty(), "Shell command required");
1474 ensure!(!args.is_empty(), "Shell command required");
1205 shell(cx, &args.join(" "), &ShellBehavior::Replace);
1475 shell(cx, &args.join(" "), &ShellBehavior::Replace);
@@ -1209,8 +1479,12 @@ fn pipe(
1209fn run_shell_command(
1479fn run_shell_command(
1210 cx: &mut compositor::Context,
1480 cx: &mut compositor::Context,
1211 args: &[Cow<str>],
1481 args: &[Cow<str>],
1212− _event: PromptEvent,
1482+ event: PromptEvent,
1213) -> anyhow::Result<()> {
1483) -> anyhow::Result<()> {
1484+ if event != PromptEvent::Validate {
1214 let shell = &cx.editor.config().shell;
1488 let shell = &cx.editor.config().shell;
1215 let (output, success) = shell_impl(shell, &args.join(" "), None)?;
1489 let (output, success) = shell_impl(shell, &args.join(" "), None)?;
1216 if success {
1490 if success {
@@ -1270,14 +1544,14 @@ pub const TYPABLE_COMMAND_LIST: &[TypableCommand] = &[
1270 aliases: &["bc", "bclose"],
1544 aliases: &["bc", "bclose"],
1271 doc: "Close the current buffer.",
1545 doc: "Close the current buffer.",
1272 fun: buffer_close,
1546 fun: buffer_close,
1273− completer: Some(completers::buffer),
1547+ completer: Some(completers::buffer),
1275 TypableCommand {
1549 TypableCommand {
1276 name: "buffer-close!",
1550 name: "buffer-close!",
1277 aliases: &["bc!", "bclose!"],
1551 aliases: &["bc!", "bclose!"],
1278 doc: "Close the current buffer forcefully (ignoring unsaved changes).",
1552 doc: "Close the current buffer forcefully (ignoring unsaved changes).",
1279 fun: force_buffer_close,
1553 fun: force_buffer_close,
1280− completer: Some(completers::buffer),
1554+ completer: Some(completers::buffer),
1282 TypableCommand {
1556 TypableCommand {
1283 name: "buffer-close-others",
1557 name: "buffer-close-others",
@@ -1561,7 +1835,7 @@ pub const TYPABLE_COMMAND_LIST: &[TypableCommand] = &[
1561 doc: "Display tree sitter scopes, primarily for theming and development.",
1835 doc: "Display tree sitter scopes, primarily for theming and development.",
1562 fun: tree_sitter_scopes,
1836 fun: tree_sitter_scopes,
1563 completer: None,
1837 completer: None,
1565 TypableCommand {
1839 TypableCommand {
1566 name: "debug-start",
1840 name: "debug-start",
1567 aliases: &["dbg"],
1841 aliases: &["dbg"],
@@ -1787,10 +2061,6 @@ pub fn command_mode(cx: &mut Context) {
1788 }, // completion
2062 }, // completion
1789 move |cx: &mut compositor::Context, input: &str, event: PromptEvent| {
2063 move |cx: &mut compositor::Context, input: &str, event: PromptEvent| {
1790− if event != PromptEvent::Validate {
1794 let parts = input.split_whitespace().collect::<Vec<&str>>();
2064 let parts = input.split_whitespace().collect::<Vec<&str>>();
1795 if parts.is_empty() {
2065 if parts.is_empty() {
@@ -1811,10 +2081,10 @@ pub fn command_mode(cx: &mut Context) {
1811 if let Err(e) = (cmd.fun)(cx, &args[1..], event) {
2081 if let Err(e) = (cmd.fun)(cx, &args[1..], event) {
1812 cx.editor.set_error(format!("{}", e));
2082 cx.editor.set_error(format!("{}", e));
1814− } else {
2084+ } else if event == PromptEvent::Validate {
1815 cx.editor
2085 cx.editor
1816 .set_error(format!("no such command: '{}'", parts[0]));
2086 .set_error(format!("no such command: '{}'", parts[0]));
1820 prompt.doc_fn = Box::new(|input: &str| {
2090 prompt.doc_fn = Box::new(|input: &str| {