on master help sign in
Add the :new command, don't crash if saving without filename.
7c915dcBlaž Hrastnik
helix-core/src/diagnostic.rs+1
@@ -1,3 +1,4 @@
1+#[derive(Eq, PartialEq)]
1pub enum Severity {
2pub enum Severity {
2 Error,
3 Error,
3 Warning,
4 Warning,
helix-term/src/commands.rs+10−10
@@ -42,7 +42,6 @@ pub struct Context<'a> {
42 pub callback: Option<crate::compositor::Callback>,
42 pub callback: Option<crate::compositor::Callback>,
43 pub on_next_key_callback: Option<Box<dyn FnOnce(&mut Context, KeyEvent)>>,
43 pub on_next_key_callback: Option<Box<dyn FnOnce(&mut Context, KeyEvent)>>,
44 pub callbacks: &'a mut LspCallbacks,
44 pub callbacks: &'a mut LspCallbacks,
45− pub status_msg: Option<String>,
46}
45}
47
46
48impl<'a> Context<'a> {
47impl<'a> Context<'a> {
@@ -97,11 +96,6 @@ impl<'a> Context<'a> {
97 });
96 });
98 self.callbacks.push(callback);
97 self.callbacks.push(callback);
99 }
98 }
100−
101− // TODO: allow &'static str?
102− pub fn set_status(&mut self, msg: String) {
103− self.status_msg = Some(msg);
104− }
105}
99}
106
100
107/// A command is a function that takes the current state and a count, and does a side-effect on the
101/// A command is a function that takes the current state and a count, and does a side-effect on the
@@ -852,6 +846,8 @@ pub fn command_mode(cx: &mut Context) {
852 }
846 }
853 }, // completion
847 }, // completion
854 move |editor: &mut Editor, input: &str, event: PromptEvent| {
848 move |editor: &mut Editor, input: &str, event: PromptEvent| {
849+ use helix_view::editor::Action;
850+
855 if event != PromptEvent::Validate {
851 if event != PromptEvent::Validate {
856 return;
852 return;
857 }
853 }
@@ -863,16 +859,20 @@ pub fn command_mode(cx: &mut Context) {
863 editor.close(editor.view().id);
859 editor.close(editor.view().id);
864 }
860 }
865 ["o", path] | ["open", path] => {
861 ["o", path] | ["open", path] => {
866− use helix_view::editor::Action;
867 editor.open(path.into(), Action::Replace);
862 editor.open(path.into(), Action::Replace);
868 }
863 }
869 ["w"] | ["write"] => {
864 ["w"] | ["write"] => {
870− // TODO: non-blocking via save() command
871 let id = editor.view().doc;
865 let id = editor.view().doc;
872 let doc = &mut editor.documents[id];
866 let doc = &mut editor.documents[id];
867+ if doc.path().is_none() {
868+ editor.set_error("cannot write a buffer without a filename".to_string());
869+ return;
870+ }
873 tokio::spawn(doc.save());
871 tokio::spawn(doc.save());
874 }
872 }
875−
873+ ["new"] => {
874+ editor.new_file(Action::Replace);
875+ }
876 _ => (),
876 _ => (),
877 }
877 }
878 },
878 },
@@ -1585,7 +1585,7 @@ pub fn yank(cx: &mut Context) {
1585
1585
1586 register::set(reg, values);
1586 register::set(reg, values);
1587
1587
1588− cx.set_status(msg)
1588+ cx.editor.set_status(msg)
1589}
1589}
1590
1590
1591#[derive(Copy, Clone)]
1591#[derive(Copy, Clone)]
helix-term/src/ui/editor.rs+20−18
@@ -28,7 +28,6 @@ use tui::{
28pub struct EditorView {
28pub struct EditorView {
29 keymap: Keymaps,
29 keymap: Keymaps,
30 on_next_key: Option<Box<dyn FnOnce(&mut commands::Context, KeyEvent)>>,
30 on_next_key: Option<Box<dyn FnOnce(&mut commands::Context, KeyEvent)>>,
31− status_msg: Option<String>,
32 last_insert: (commands::Command, Vec<KeyEvent>),
31 last_insert: (commands::Command, Vec<KeyEvent>),
33 completion: Option<Completion>,
32 completion: Option<Completion>,
34}
33}
@@ -40,7 +39,6 @@ impl EditorView {
40 Self {
39 Self {
41 keymap: keymap::default(),
40 keymap: keymap::default(),
42 on_next_key: None,
41 on_next_key: None,
43− status_msg: None,
44 last_insert: (commands::normal_mode, Vec::new()),
42 last_insert: (commands::normal_mode, Vec::new()),
45 completion: None,
43 completion: None,
46 }
44 }
@@ -86,17 +84,6 @@ impl EditorView {
86 1,
84 1,
87 );
85 );
88 self.render_statusline(doc, view, area, surface, theme, is_focused);
86 self.render_statusline(doc, view, area, surface, theme, is_focused);
89−
90− // render status
91− if let Some(status_msg) = &self.status_msg {
92− let style = Style::default().fg(Color::Rgb(164, 160, 232)); // lavender
93− surface.set_string(
94− view.area.x,
95− view.area.y + view.area.height,
96− status_msg,
97− style,
98− );
99− }
100 }
87 }
101
88
102 pub fn render_buffer(
89 pub fn render_buffer(
@@ -537,6 +524,9 @@ impl Component for EditorView {
537 EventResult::Consumed(None)
524 EventResult::Consumed(None)
538 }
525 }
539 Event::Key(key) => {
526 Event::Key(key) => {
527+ // clear status
528+ cx.editor.status_msg = None;
529+
540 let (view, doc) = cx.editor.current();
530 let (view, doc) = cx.editor.current();
541 let mode = doc.mode();
531 let mode = doc.mode();
542
532
@@ -547,12 +537,8 @@ impl Component for EditorView {
547 callback: None,
537 callback: None,
548 callbacks: cx.callbacks,
538 callbacks: cx.callbacks,
549 on_next_key_callback: None,
539 on_next_key_callback: None,
550− status_msg: None,
551 };
540 };
552
541
553− // clear status
554− self.status_msg = None;
555−
556 if let Some(on_next_key) = self.on_next_key.take() {
542 if let Some(on_next_key) = self.on_next_key.take() {
557 // if there's a command waiting input, do that first
543 // if there's a command waiting input, do that first
558 on_next_key(&mut cxt, key);
544 on_next_key(&mut cxt, key);
@@ -602,7 +588,6 @@ impl Component for EditorView {
602 }
588 }
603
589
604 self.on_next_key = cxt.on_next_key_callback.take();
590 self.on_next_key = cxt.on_next_key_callback.take();
605− self.status_msg = cxt.status_msg.take();
606 // appease borrowck
591 // appease borrowck
607 let callback = cxt.callback.take();
592 let callback = cxt.callback.take();
608
593
@@ -641,6 +626,23 @@ impl Component for EditorView {
641 self.render_view(doc, view, area, surface, &cx.editor.theme, is_focused);
626 self.render_view(doc, view, area, surface, &cx.editor.theme, is_focused);
642 }
627 }
643
628
629+ // render status msg
630+ if let Some((status_msg, severity)) = &cx.editor.status_msg {
631+ use helix_view::editor::Severity;
632+ let style = if *severity == Severity::Error {
633+ cx.editor.theme.get("error")
634+ } else {
635+ Style::default().fg(Color::Rgb(164, 160, 232)) // lavender
636+ };
637+
638+ surface.set_string(
639+ area.x,
640+ area.y + area.height.saturating_sub(1),
641+ status_msg,
642+ style,
643+ );
644+ }
645+
644 if let Some(completion) = &self.completion {
646 if let Some(completion) = &self.completion {
645 completion.render(area, surface, cx)
647 completion.render(area, surface, cx)
646 // render completion here
648 // render completion here
helix-view/src/editor.rs+13
@@ -7,12 +7,16 @@ use slotmap::SlotMap;
7
7
8use anyhow::Error;
8use anyhow::Error;
9
9
10+pub use helix_core::diagnostic::Severity;
11+
10pub struct Editor {
12pub struct Editor {
11 pub tree: Tree,
13 pub tree: Tree,
12 pub documents: SlotMap<DocumentId, Document>,
14 pub documents: SlotMap<DocumentId, Document>,
13 pub count: Option<usize>,
15 pub count: Option<usize>,
14 pub theme: Theme,
16 pub theme: Theme,
15 pub language_servers: helix_lsp::Registry,
17 pub language_servers: helix_lsp::Registry,
18+
19+ pub status_msg: Option<(String, Severity)>,
16}
20}
17
21
18#[derive(Copy, Clone)]
22#[derive(Copy, Clone)]
@@ -43,9 +47,18 @@ impl Editor {
43 count: None,
47 count: None,
44 theme,
48 theme,
45 language_servers,
49 language_servers,
50+ status_msg: None,
46 }
51 }
47 }
52 }
48
53
54+ pub fn set_status(&mut self, status: String) {
55+ self.status_msg = Some((status, Severity::Info));
56+ }
57+
58+ pub fn set_error(&mut self, error: String) {
59+ self.status_msg = Some((error, Severity::Error));
60+ }
61+
49 fn _refresh(&mut self) {
62 fn _refresh(&mut self) {
50 for (view, _) in self.tree.views_mut() {
63 for (view, _) in self.tree.views_mut() {
51 let doc = &self.documents[view.doc];
64 let doc = &self.documents[view.doc];