on master help sign in
Add live preview to theme picker (#1798)
b26e7e2Joe
helix-term/src/application.rs+2−14
@@ -108,13 +108,7 @@ impl Application {
108 .ok()
108 .ok()
109 .filter(|theme| (true_color || theme.is_16_color()))
109 .filter(|theme| (true_color || theme.is_16_color()))
110 })
110 })
111− .unwrap_or_else(|| {
111+ .unwrap_or_else(|| theme_loader.default_theme(true_color));
112− if true_color {
113− theme_loader.default()
114− } else {
115− theme_loader.base16_default()
116− }
117− });
118
112
119 let syn_loader_conf = user_syntax_loader().unwrap_or_else(|err| {
113 let syn_loader_conf = user_syntax_loader().unwrap_or_else(|err| {
120 eprintln!("Bad language config: {}", err);
114 eprintln!("Bad language config: {}", err);
@@ -373,13 +367,7 @@ impl Application {
373 })
367 })
374 .ok()
368 .ok()
375 .filter(|theme| (true_color || theme.is_16_color()))
369 .filter(|theme| (true_color || theme.is_16_color()))
376− .unwrap_or_else(|| {
370+ .unwrap_or_else(|| self.theme_loader.default_theme(true_color)),
377− if true_color {
378− self.theme_loader.default()
379− } else {
380− self.theme_loader.base16_default()
381− }
382− }),
383 );
371 );
384 }
372 }
385
373
helix-term/src/commands/typed.rs+371−101
@@ -15,11 +15,11 @@ pub struct TypableCommand {
15 pub completer: Option<Completer>,
15 pub completer: Option<Completer>,
16}
16}
17
17
18−fn quit(
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+ }
22+
23 ensure!(args.is_empty(), ":quit takes no arguments");
23 ensure!(args.is_empty(), ":quit takes no arguments");
24
24
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 {
41+ return Ok(());
42+ }
43+
40 ensure!(args.is_empty(), ":quit! takes no arguments");
44 ensure!(args.is_empty(), ":quit! takes no arguments");
41
45
42 cx.editor.close(view!(cx.editor).id);
46 cx.editor.close(view!(cx.editor).id);
@@ -44,11 +48,11 @@ fn force_quit(
44 Ok(())
48 Ok(())
45}
49}
46
50
47−fn open(
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+ }
55+
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 {
124+ return Ok(());
125+ }
126+
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)
121}
129}
@@ -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 {
137+ return Ok(());
138+ }
139+
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)
130}
142}
@@ -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 {
159+ return Ok(());
160+ }
161+
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)
148}
164}
@@ -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 {
172+ return Ok(());
173+ }
174+
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)
157}
177}
@@ -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 {
189+ return Ok(());
190+ }
191+
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)
170}
194}
@@ -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 {
202+ return Ok(());
203+ }
204+
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)
179}
207}
@@ -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 {
215+ return Ok(());
216+ }
217+
186 goto_buffer(cx.editor, Direction::Forward);
218 goto_buffer(cx.editor, Direction::Forward);
187 Ok(())
219 Ok(())
188}
220}
@@ -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 {
228+ return Ok(());
229+ }
230+
195 goto_buffer(cx.editor, Direction::Backward);
231 goto_buffer(cx.editor, Direction::Backward);
196 Ok(())
232 Ok(())
197}
233}
@@ -242,24 +278,36 @@ fn write_impl(
242fn write(
278fn write(
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 {
284+ return Ok(());
285+ }
286+
247 write_impl(cx, args.first(), false)
287 write_impl(cx, args.first(), false)
248}
288}
249
289
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 {
296+ return Ok(());
297+ }
298+
255 write_impl(cx, args.first(), true)
299 write_impl(cx, args.first(), true)
256}
300}
257
301
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 {
308+ return Ok(());
309+ }
310+
263 cx.editor.new_file(Action::Replace);
311 cx.editor.new_file(Action::Replace);
264
312
265 Ok(())
313 Ok(())
@@ -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 {
322+ return Ok(());
323+ }
324+
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 {
340+ return Ok(());
341+ }
342+
287 use IndentStyle::*;
343 use IndentStyle::*;
288
344
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 {
383+ return Ok(());
384+ }
385+
326 use LineEnding::*;
386 use LineEnding::*;
327
387
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 {
457+ return Ok(());
458+ }
459+
396 let uk = args.join(" ").parse::<UndoKind>().map_err(|s| anyhow!(s))?;
460 let uk = args.join(" ").parse::<UndoKind>().map_err(|s| anyhow!(s))?;
397
461
398 let (view, doc) = current!(cx.editor);
462 let (view, doc) = current!(cx.editor);
@@ -407,8 +471,12 @@ fn earlier(
407fn later(
471fn later(
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 {
477+ return Ok(());
478+ }
479+
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 {
496+ return Ok(());
497+ }
498+
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 {
510+ return Ok(());
511+ }
512+
437 write_impl(cx, args.first(), true)?;
513 write_impl(cx, args.first(), true)?;
438 force_quit(cx, &[], event)
514 force_quit(cx, &[], event)
439}
515}
@@ -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 {
547+ return Ok(());
548+ }
549+
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 {
604+ return Ok(());
605+ }
606+
523 write_all_impl(cx, args, event, false, false)
607 write_all_impl(cx, args, event, false, false)
524}
608}
525
609
@@ -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 {
616+ return Ok(());
617+ }
618+
531 write_all_impl(cx, args, event, true, false)
619 write_all_impl(cx, args, event, true, false)
532}
620}
533
621
@@ -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 {
628+ return Ok(());
629+ }
630+
539 write_all_impl(cx, args, event, true, true)
631 write_all_impl(cx, args, event, true, true)
540}
632}
541
633
@@ -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 {
654+ return Ok(());
655+ }
656+
561 quit_all_impl(cx.editor, false)
657 quit_all_impl(cx.editor, false)
562}
658}
563
659
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 {
666+ return Ok(());
667+ }
668+
569 quit_all_impl(cx.editor, true)
669 quit_all_impl(cx.editor, true)
570}
670}
571
671
572fn cquit(
672fn cquit(
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 {
678+ return Ok(());
679+ }
680+
577 let exit_code = args
681 let exit_code = args
578 .first()
682 .first()
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 {
696+ return Ok(());
697+ }
698+
591 let exit_code = args
699 let exit_code = args
592 .first()
700 .first()
593 .and_then(|code| code.parse::<i32>().ok())
701 .and_then(|code| code.parse::<i32>().ok())
@@ -600,35 +708,61 @@ fn force_cquit(
600fn theme(
708fn theme(
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")?;
606− let theme = cx
607− .editor
608− .theme_loader
609− .load(theme)
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();
714+ match event {
715+ PromptEvent::Abort => {
716+ cx.editor.unset_theme_preview();
717+ }
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");
723+ }
724+ cx.editor.set_theme_preview(theme);
725+ };
726+ };
727+ }
728+ PromptEvent::Validate => {
729+ let theme_name = args.first().with_context(|| "Theme name not provided")?;
730+ let theme = cx
731+ .editor
732+ .theme_loader
733+ .load(theme_name)
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");
614− }
737+ }
615− cx.editor.set_theme(theme);
738+ cx.editor.set_theme(theme);
739+ }
740+ };
741+
616 Ok(())
742 Ok(())
617}
743}
618
744
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 {
751+ return Ok(());
752+ }
753+
624 yank_main_selection_to_clipboard_impl(cx.editor, ClipboardType::Clipboard)
754 yank_main_selection_to_clipboard_impl(cx.editor, ClipboardType::Clipboard)
625}
755}
626
756
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 {
763+ return Ok(());
764+ }
765+
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 {
778+ return Ok(());
779+ }
780+
643 yank_main_selection_to_clipboard_impl(cx.editor, ClipboardType::Selection)
781 yank_main_selection_to_clipboard_impl(cx.editor, ClipboardType::Selection)
644}
782}
645
783
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 {
790+ return Ok(());
791+ }
792+
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 {
805+ return Ok(());
806+ }
807+
662 paste_clipboard_impl(cx.editor, Paste::After, ClipboardType::Clipboard, 1)
808 paste_clipboard_impl(cx.editor, Paste::After, ClipboardType::Clipboard, 1)
663}
809}
664
810
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 {
817+ return Ok(());
818+ }
819+
670 paste_clipboard_impl(cx.editor, Paste::Before, ClipboardType::Clipboard, 1)
820 paste_clipboard_impl(cx.editor, Paste::Before, ClipboardType::Clipboard, 1)
671}
821}
672
822
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 {
829+ return Ok(());
830+ }
831+
678 paste_clipboard_impl(cx.editor, Paste::After, ClipboardType::Selection, 1)
832 paste_clipboard_impl(cx.editor, Paste::After, ClipboardType::Selection, 1)
679}
833}
680
834
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 {
841+ return Ok(());
842+ }
843+
686 paste_clipboard_impl(cx.editor, Paste::Before, ClipboardType::Selection, 1)
844 paste_clipboard_impl(cx.editor, Paste::Before, ClipboardType::Selection, 1)
687}
845}
688
846
@@ -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 {
874+ return Ok(());
875+ }
876+
715 replace_selections_with_clipboard_impl(cx, ClipboardType::Clipboard)
877 replace_selections_with_clipboard_impl(cx, ClipboardType::Clipboard)
716}
878}
717
879
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 {
886+ return Ok(());
887+ }
888+
723 replace_selections_with_clipboard_impl(cx, ClipboardType::Selection)
889 replace_selections_with_clipboard_impl(cx, ClipboardType::Selection)
724}
890}
725
891
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 {
898+ return Ok(());
899+ }
900+
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());
733 Ok(())
903 Ok(())
@@ -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 {
912+ return Ok(());
913+ }
914+
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 {
940+ return Ok(());
941+ }
942+
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 {
956+ return Ok(());
957+ }
958+
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 {
976+ return Ok(());
977+ }
978+
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 {
992+ return Ok(());
993+ }
994+
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(..);
807
997
@@ -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 {
1010+ return Ok(());
1011+ }
1012+
819 let id = view!(cx.editor).doc;
1013 let id = view!(cx.editor).doc;
820
1014
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 {
1033+ return Ok(());
1034+ }
1035+
838 let id = view!(cx.editor).doc;
1036 let id = view!(cx.editor).doc;
839
1037
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 {
1056+ return Ok(());
1057+ }
1058+
857 cx.editor.new_file(Action::VerticalSplit);
1059 cx.editor.new_file(Action::VerticalSplit);
858
1060
859 Ok(())
1061 Ok(())
@@ -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 {
1070+ return Ok(());
1071+ }
1072+
867 cx.editor.new_file(Action::HorizontalSplit);
1073 cx.editor.new_file(Action::HorizontalSplit);
868
1074
869 Ok(())
1075 Ok(())
@@ -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 {
1084+ return Ok(());
1085+ }
1086+
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 {
1110+ return Ok(());
1111+ }
1112+
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 {
1127+ return Ok(());
1128+ }
1129+
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 {
1148+ return Ok(());
1149+ }
1150+
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 {
1164+ return Ok(());
1165+ }
1166+
941 ensure!(!args.is_empty(), "Line number required");
1167 ensure!(!args.is_empty(), "Line number required");
942
1168
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 {
1186+ return Ok(());
1187+ }
1188+
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`");
961 }
1191 }
@@ -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 {
1212+ return Ok(());
1213+ }
1214+
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`");
983 }
1217 }
@@ -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 {
1249+ return Ok(());
1250+ }
1251+
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`");
1016 }
1254 }
@@ -1023,19 +1261,23 @@ fn language(
1023 Ok(())
1261 Ok(())
1024}
1262}
1025
1263
1026−fn sort(
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+ }
1268+
1031 sort_impl(cx, args, false)
1269 sort_impl(cx, args, false)
1032}
1270}
1033
1271
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 {
1278+ return Ok(());
1279+ }
1280+
1039 sort_impl(cx, args, true)
1281 sort_impl(cx, args, true)
1040}
1282}
1041
1283
@@ -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 {
1324+ return Ok(());
1325+ }
1326+
1081 let (view, doc) = current!(cx.editor);
1327 let (view, doc) = current!(cx.editor);
1082
1328
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 {
1367+ return Ok(());
1368+ }
1369+
1120 let (view, doc) = current!(cx.editor);
1370 let (view, doc) = current!(cx.editor);
1121
1371
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 {
1407+ return Ok(());
1408+ }
1409+
1156 cx.editor
1410 cx.editor
1157 .open(&helix_loader::config_file(), Action::Replace)?;
1411 .open(&helix_loader::config_file(), Action::Replace)?;
1158 Ok(())
1412 Ok(())
@@ -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 {
1421+ return Ok(());
1422+ }
1423+
1166 cx.editor.open(&helix_loader::log_file(), Action::Replace)?;
1424 cx.editor.open(&helix_loader::log_file(), Action::Replace)?;
1167 Ok(())
1425 Ok(())
1168}
1426}
@@ -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 {
1434+ return Ok(());
1435+ }
1436+
1175 cx.editor.config_events.0.send(ConfigEvent::Refresh)?;
1437 cx.editor.config_events.0.send(ConfigEvent::Refresh)?;
1176 Ok(())
1438 Ok(())
1177}
1439}
@@ -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 {
1447+ return Ok(());
1448+ }
1449+
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);
1186 Ok(())
1452 Ok(())
@@ -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 {
1461+ return Ok(());
1462+ }
1463+
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);
1196 Ok(())
1466 Ok(())
1197}
1467}
1198
1468
1199−fn pipe(
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+ }
1473+
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);
1206 Ok(())
1476 Ok(())
@@ -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 {
1485+ return Ok(());
1486+ }
1487+
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),
1274 },
1548 },
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),
1281 },
1555 },
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,
1564− },
1838+ },
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) {
1787 }
2061 }
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 {
1791− return;
1792− }
1793−
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() {
1796 return;
2066 return;
@@ -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));
1813 }
2083 }
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]));
1817− };
2087+ }
1818 },
2088 },
1819 );
2089 );
1820 prompt.doc_fn = Box::new(|input: &str| {
2090 prompt.doc_fn = Box::new(|input: &str| {
helix-term/src/ui/mod.rs+1
@@ -237,6 +237,7 @@ pub mod completers {
237 ));
237 ));
238 names.push("default".into());
238 names.push("default".into());
239 names.push("base16_default".into());
239 names.push("base16_default".into());
240+ names.sort();
240
241
241 let mut names: Vec<_> = names
242 let mut names: Vec<_> = names
242 .into_iter()
243 .into_iter()
helix-term/src/ui/picker.rs+1−1
@@ -490,7 +490,7 @@ impl<T: Item + 'static> Component for Picker<T> {
490 _ => return EventResult::Ignored(None),
490 _ => return EventResult::Ignored(None),
491 };
491 };
492
492
493− let close_fn = EventResult::Consumed(Some(Box::new(|compositor: &mut Compositor, _| {
493+ let close_fn = EventResult::Consumed(Some(Box::new(|compositor: &mut Compositor, _cx| {
494 // remove the layer
494 // remove the layer
495 compositor.last_picker = compositor.pop();
495 compositor.last_picker = compositor.pop();
496 })));
496 })));
helix-view/src/editor.rs+39−2
@@ -464,7 +464,6 @@ pub struct Editor {
464 pub registers: Registers,
464 pub registers: Registers,
465 pub macro_recording: Option<(char, Vec<KeyEvent>)>,
465 pub macro_recording: Option<(char, Vec<KeyEvent>)>,
466 pub macro_replaying: Vec<char>,
466 pub macro_replaying: Vec<char>,
467− pub theme: Theme,
468 pub language_servers: helix_lsp::Registry,
467 pub language_servers: helix_lsp::Registry,
469 pub diagnostics: BTreeMap<lsp::Url, Vec<lsp::Diagnostic>>,
468 pub diagnostics: BTreeMap<lsp::Url, Vec<lsp::Diagnostic>>,
470
469
@@ -476,6 +475,12 @@ pub struct Editor {
476
475
477 pub syn_loader: Arc<syntax::Loader>,
476 pub syn_loader: Arc<syntax::Loader>,
478 pub theme_loader: Arc<theme::Loader>,
477 pub theme_loader: Arc<theme::Loader>,
478+ /// last_theme is used for theme previews. We store the current theme here,
479+ /// and if previewing is cancelled, we can return to it.
480+ pub last_theme: Option<Theme>,
481+ /// The currently applied editor theme. While previewing a theme, the previewed theme
482+ /// is set here.
483+ pub theme: Theme,
479
484
480 pub status_msg: Option<(Cow<'static, str>, Severity)>,
485 pub status_msg: Option<(Cow<'static, str>, Severity)>,
481 pub autoinfo: Option<Info>,
486 pub autoinfo: Option<Info>,
@@ -500,6 +505,11 @@ pub enum ConfigEvent {
500 Update(Box<Config>),
505 Update(Box<Config>),
501}
506}
502
507
508+enum ThemeAction {
509+ Set,
510+ Preview,
511+}
512+
503#[derive(Debug, Clone)]
513#[derive(Debug, Clone)]
504pub struct CompleteAction {
514pub struct CompleteAction {
505 pub trigger_offset: usize,
515 pub trigger_offset: usize,
@@ -544,6 +554,7 @@ impl Editor {
544 breakpoints: HashMap::new(),
554 breakpoints: HashMap::new(),
545 syn_loader,
555 syn_loader,
546 theme_loader,
556 theme_loader,
557+ last_theme: None,
547 registers: Registers::default(),
558 registers: Registers::default(),
548 clipboard_provider: get_clipboard_provider(),
559 clipboard_provider: get_clipboard_provider(),
549 status_msg: None,
560 status_msg: None,
@@ -613,7 +624,22 @@ impl Editor {
613 .unwrap_or(false)
624 .unwrap_or(false)
614 }
625 }
615
626
627+ pub fn unset_theme_preview(&mut self) {
628+ if let Some(last_theme) = self.last_theme.take() {
629+ self.set_theme(last_theme);
630+ }
631+ // None likely occurs when the user types ":theme" and then exits before previewing
632+ }
633+
634+ pub fn set_theme_preview(&mut self, theme: Theme) {
635+ self.set_theme_impl(theme, ThemeAction::Preview);
636+ }
637+
616 pub fn set_theme(&mut self, theme: Theme) {
638 pub fn set_theme(&mut self, theme: Theme) {
639+ self.set_theme_impl(theme, ThemeAction::Set);
640+ }
641+
642+ fn set_theme_impl(&mut self, theme: Theme, preview: ThemeAction) {
617 // `ui.selection` is the only scope required to be able to render a theme.
643 // `ui.selection` is the only scope required to be able to render a theme.
618 if theme.find_scope_index("ui.selection").is_none() {
644 if theme.find_scope_index("ui.selection").is_none() {
619 self.set_error("Invalid theme: `ui.selection` required");
645 self.set_error("Invalid theme: `ui.selection` required");
@@ -623,7 +649,18 @@ impl Editor {
623 let scopes = theme.scopes();
649 let scopes = theme.scopes();
624 self.syn_loader.set_scopes(scopes.to_vec());
650 self.syn_loader.set_scopes(scopes.to_vec());
625
651
626− self.theme = theme;
652+ match preview {
653+ ThemeAction::Preview => {
654+ let last_theme = std::mem::replace(&mut self.theme, theme);
655+ // only insert on first preview: this will be the last theme the user has saved
656+ self.last_theme.get_or_insert(last_theme);
657+ }
658+ ThemeAction::Set => {
659+ self.last_theme = None;
660+ self.theme = theme;
661+ }
662+ }
663+
627 self._refresh();
664 self._refresh();
628 }
665 }
629
666
helix-view/src/theme.rs+8
@@ -77,6 +77,14 @@ impl Loader {
77 names
77 names
78 }
78 }
79
79
80+ pub fn default_theme(&self, true_color: bool) -> Theme {
81+ if true_color {
82+ self.default()
83+ } else {
84+ self.base16_default()
85+ }
86+ }
87+
80 /// Returns the default theme
88 /// Returns the default theme
81 pub fn default(&self) -> Theme {
89 pub fn default(&self) -> Theme {
82 DEFAULT_THEME.clone()
90 DEFAULT_THEME.clone()