on master help sign in
wip: Getting the new prompt to render in a new layer.
commitada3f92c5b96e4c66f5647c4ac2487f3903692b3 authorBlaž Hrastnik <blaz@mxxn.io> dateFri 11 Dec 2020, 18:25 (6 years ago) parent5103dc9
4 files changed+159−145
helix-term/src/application.rs+56−126 helix-term/src/commands.rs+55−2 helix-term/src/compositor.rs+25−11 helix-term/src/prompt.rs+23−6
helix-term/src/application.rs+56−126
@@ -42,16 +42,15 @@ type Terminal = tui::Terminal<CrosstermBackend<std::io::Stdout>>;
4242const BASE_WIDTH: u16 = 30;
4343
4444pub struct Application<'a> {
45− prompt: Option<Prompt>,
46−
4745 compositor: Compositor,
46+ editor: Editor,
4847 renderer: Renderer,
4948
5049 executor: &'a smol::Executor<'a>,
5150 language_server: helix_lsp::Client,
5251}
5352
54−pub(crate) struct Renderer {
53+pub struct Renderer {
5554 size: (u16, u16),
5655 terminal: Terminal,
5756 surface: Surface,
@@ -263,6 +262,11 @@ impl Renderer {
263262 self.surface
264263 .set_string(1, self.size.1 - 2, mode, self.text_color);
265264
265+ if let Some(path) = view.doc.path() {
266+ self.surface
267+ .set_string(6, self.size.1 - 2, path.to_string_lossy(), self.text_color);
268+ }
269+
266270 self.surface.set_string(
267271 self.size.0 - 10,
268272 self.size.1 - 2,
@@ -271,7 +275,7 @@ impl Renderer {
271275 );
272276 }
273277
274− pub fn render_prompt(&mut self, view: &View, prompt: &Prompt, theme: &Theme) {
278+ pub fn render_prompt(&mut self, prompt: &Prompt, theme: &Theme) {
275279 // completion
276280 if !prompt.completion.is_empty() {
277281 // TODO: find out better way of clearing individual lines of the screen
@@ -343,15 +347,6 @@ impl Renderer {
343347 let pos = if let Some(prompt) = prompt {
344348 Position::new(self.size.0 as usize, 2 + prompt.cursor)
345349 } else {
346− if let Some(path) = view.doc.path() {
347− self.surface.set_string(
348− 6,
349− self.size.1 - 1,
350− path.to_string_lossy(),
351− self.text_color,
352− );
353− }
354−
355350 let cursor = view.doc.state.selection().cursor();
356351
357352 let mut pos = view
@@ -367,146 +362,77 @@ impl Renderer {
367362}
368363
369364struct EditorView {
370− editor: Editor,
371− prompt: Option<Prompt>, // TODO: this is None for now, make a layer
372365 keymap: Keymaps,
373366}
374367
375368impl EditorView {
376− fn new(editor: Editor) -> Self {
369+ fn new() -> Self {
377370 Self {
378− editor,
379− prompt: None,
380371 keymap: keymap::default(),
381372 }
382373 }
383374}
384375
376+use crate::compositor::Context;
377+
385378impl Component for EditorView {
386− fn handle_event(&mut self, event: Event, executor: &smol::Executor) -> EventResult {
379+ fn handle_event(&mut self, event: Event, cx: &mut Context) -> EventResult {
387380 match event {
388381 Event::Resize(width, height) => {
389382 // TODO: simplistic ensure cursor in view for now
390383 // TODO: loop over views
391− if let Some(view) = self.editor.view_mut() {
384+ if let Some(view) = cx.editor.view_mut() {
392385 view.size = (width, height);
393386 view.ensure_cursor_in_view()
394387 };
395388 EventResult::Consumed(None)
396389 }
397390 Event::Key(event) => {
398− if let Some(view) = self.editor.view_mut() {
391+ if let Some(view) = cx.editor.view_mut() {
399392 let keys = vec![event];
400393 // TODO: sequences (`gg`)
394+ let mode = view.doc.mode();
401395 // TODO: handle count other than 1
402− match view.doc.mode() {
396+ let mut cx = commands::Context {
397+ view,
398+ executor: cx.executor,
399+ count: 1,
400+ callback: None,
401+ };
402+
403+ match mode {
403404 Mode::Insert => {
404405 if let Some(command) = self.keymap[&Mode::Insert].get(&keys) {
405− let mut cx = commands::Context {
406− view,
407− executor,
408− count: 1,
409− };
410−
411406 command(&mut cx);
412407 } else if let KeyEvent {
413408 code: KeyCode::Char(c),
414409 ..
415410 } = event
416411 {
417− let mut cx = commands::Context {
418− view,
419− executor,
420− count: 1,
421− };
422412 commands::insert::insert_char(&mut cx, c);
423413 }
424− view.ensure_cursor_in_view();
425414 }
426415 Mode::Normal => {
427− if let &[KeyEvent {
428− code: KeyCode::Char(':'),
429− ..
430− }] = keys.as_slice()
431− {
432− let prompt = Prompt::new(
433− ":".to_owned(),
434− |_input: &str| {
435− // TODO: i need this duplicate list right now to avoid borrow checker issues
436− let command_list = vec![
437− String::from("q"),
438− String::from("aaa"),
439− String::from("bbb"),
440− String::from("ccc"),
441− String::from("ddd"),
442− String::from("eee"),
443− String::from("averylongcommandaverylongcommandaverylongcommandaverylongcommandaverylongcommand"),
444− String::from("q"),
445− String::from("aaa"),
446− String::from("bbb"),
447− String::from("ccc"),
448− String::from("ddd"),
449− String::from("eee"),
450− String::from("q"),
451− String::from("aaa"),
452− String::from("bbb"),
453− String::from("ccc"),
454− String::from("ddd"),
455− String::from("eee"),
456− String::from("q"),
457− String::from("aaa"),
458− String::from("bbb"),
459− String::from("ccc"),
460− String::from("ddd"),
461− String::from("eee"),
462− String::from("q"),
463− String::from("aaa"),
464− String::from("bbb"),
465− String::from("ccc"),
466− String::from("ddd"),
467− String::from("eee"),
468− ];
469− command_list
470− .into_iter()
471− .filter(|command| command.contains(_input))
472− .collect()
473− }, // completion
474− |editor: &mut Editor, input: &str| match input {
475− "q" => editor.should_close = true,
476− _ => (),
477− },
478− );
479−
480− self.prompt = Some(prompt);
481−
482− // HAXX: special casing for command mode
483− } else if let Some(command) = self.keymap[&Mode::Normal].get(&keys) {
484− let mut cx = commands::Context {
485− view,
486− executor,
487− count: 1,
488− };
416+ if let Some(command) = self.keymap[&Mode::Normal].get(&keys) {
489417 command(&mut cx);
490418
491419 // TODO: simplistic ensure cursor in view for now
492− view.ensure_cursor_in_view();
493420 }
494421 }
495422 mode => {
496423 if let Some(command) = self.keymap[&mode].get(&keys) {
497− let mut cx = commands::Context {
498− view,
499− executor,
500− count: 1,
501− };
502424 command(&mut cx);
503425
504426 // TODO: simplistic ensure cursor in view for now
505− view.ensure_cursor_in_view();
506427 }
507428 }
508429 }
509− EventResult::Consumed(None)
430+ // appease borrowck
431+ let callback = cx.callback.take();
432+
433+ view.ensure_cursor_in_view();
434+
435+ EventResult::Consumed(callback)
510436 } else {
511437 EventResult::Ignored
512438 }
@@ -514,19 +440,19 @@ impl Component for EditorView {
514440 Event::Mouse(_) => EventResult::Ignored,
515441 }
516442 }
517− fn render(&mut self, renderer: &mut Renderer) {
443+ fn render(&mut self, renderer: &mut Renderer, cx: &mut Context) {
518444 const OFFSET: u16 = 7; // 1 diagnostic + 5 linenr + 1 gutter
519445 let viewport = Rect::new(OFFSET, 0, renderer.size.0, renderer.size.1 - 2); // - 2 for statusline and prompt
520446
521447 // SAFETY: we cheat around the view_mut() borrow because it doesn't allow us to also borrow
522448 // theme. Theme is immutable mutating view won't disrupt theme_ref.
523− let theme_ref = unsafe { &*(&self.editor.theme as *const Theme) };
524− if let Some(view) = self.editor.view_mut() {
449+ let theme_ref = unsafe { &*(&cx.editor.theme as *const Theme) };
450+ if let Some(view) = cx.editor.view_mut() {
525451 renderer.render_view(view, viewport, theme_ref);
526452 }
527453
528454 // TODO: drop unwrap
529− renderer.render_cursor(self.editor.view().unwrap(), self.prompt.as_ref(), viewport);
455+ renderer.render_cursor(cx.editor.view().unwrap(), None, viewport);
530456 }
531457}
532458
@@ -540,11 +466,12 @@ impl<'a> Application<'a> {
540466 }
541467
542468 let mut compositor = Compositor::new();
543− compositor.push(Box::new(EditorView::new(editor)));
469+ compositor.push(Box::new(EditorView::new()));
544470
545471 let language_server = helix_lsp::Client::start(&executor, "rust-analyzer", &[]);
546472
547473 let mut app = Self {
474+ editor,
548475 renderer,
549476 // TODO; move to state
550477 compositor,
@@ -558,7 +485,11 @@ impl<'a> Application<'a> {
558485
559486 fn render(&mut self) {
560487 self.renderer.surface.reset(); // reset is faster than allocating new empty surface
561− self.compositor.render(&mut self.renderer); // viewport,
488+ let mut cx = crate::compositor::Context {
489+ editor: &mut self.editor,
490+ executor: &self.executor,
491+ };
492+ self.compositor.render(&mut self.renderer, &mut cx); // viewport,
562493 self.renderer.draw_and_swap();
563494 }
564495
@@ -569,17 +500,16 @@ impl<'a> Application<'a> {
569500 self.language_server.initialize().await.unwrap();
570501 // TODO: temp
571502 // self.language_server
572− // .text_document_did_open(&self.editor.view().unwrap().doc)
503+ // .text_document_did_open(&cx.editor.view().unwrap().doc)
573504 // .await
574505 // .unwrap();
575506
576507 self.render();
577508
578509 loop {
579− // TODO:
580− // if self.editor.should_close {
581− // break;
582− // }
510+ if self.editor.should_close {
511+ break;
512+ }
583513
584514 use futures_util::{select, FutureExt};
585515 select! {
@@ -594,26 +524,26 @@ impl<'a> Application<'a> {
594524 }
595525
596526 pub fn handle_terminal_events(&mut self, event: Option<Result<Event, crossterm::ErrorKind>>) {
527+ let mut cx = crate::compositor::Context {
528+ editor: &mut self.editor,
529+ executor: &self.executor,
530+ };
597531 // Handle key events
598− match event {
532+ let should_redraw = match event {
599533 Some(Ok(Event::Resize(width, height))) => {
600534 self.renderer.resize(width, height);
601535
602− // TODO: use the response
603536 self.compositor
604− .handle_event(Event::Resize(width, height), self.executor);
605−
606− self.render();
607− }
608− Some(Ok(event)) => {
609− // TODO: use the response
610− self.compositor.handle_event(event, self.executor);
611−
612− self.render();
537+ .handle_event(Event::Resize(width, height), &mut cx)
613538 }
539+ Some(Ok(event)) => self.compositor.handle_event(event, &mut cx),
614540 Some(Err(x)) => panic!(x),
615541 None => panic!(),
616542 };
543+
544+ if should_redraw {
545+ self.render();
546+ }
617547 }
618548
619549 pub async fn handle_language_server_message(&mut self, call: Option<helix_lsp::Call>) {
helix-term/src/commands.rs+55−2
@@ -9,17 +9,21 @@ use helix_core::{
99
1010use once_cell::sync::Lazy;
1111
12+use crate::compositor::Compositor;
1213use crate::prompt::Prompt;
1314
1415use helix_view::{
1516 document::Mode,
1617 view::{View, PADDING},
18+ Editor,
1719};
1820
1921pub struct Context<'a, 'b> {
2022 pub count: usize,
2123 pub view: &'a mut View,
2224 pub executor: &'a smol::Executor<'b>,
25+
26+ pub callback: Option<crate::compositor::Callback>,
2327}
2428
2529/// A command is a function that takes the current state and a count, and does a side-effect on the
@@ -333,8 +337,57 @@ pub fn append_mode(cx: &mut Context) {
333337}
334338
335339// TODO: I, A, o and O can share a lot of the primitives.
336−pub fn command_mode(_cx: &mut Context) {
337− unimplemented!()
340+pub fn command_mode(cx: &mut Context) {
341+ cx.callback = Some(Box::new(|compositor: &mut Compositor| {
342+ let prompt = Prompt::new(
343+ ":".to_owned(),
344+ |_input: &str| {
345+ // TODO: i need this duplicate list right now to avoid borrow checker issues
346+ let command_list = vec![
347+ String::from("q"),
348+ String::from("aaa"),
349+ String::from("bbb"),
350+ String::from("ccc"),
351+ String::from("ddd"),
352+ String::from("eee"),
353+ String::from("averylongcommandaverylongcommandaverylongcommandaverylongcommandaverylongcommand"),
354+ String::from("q"),
355+ String::from("aaa"),
356+ String::from("bbb"),
357+ String::from("ccc"),
358+ String::from("ddd"),
359+ String::from("eee"),
360+ String::from("q"),
361+ String::from("aaa"),
362+ String::from("bbb"),
363+ String::from("ccc"),
364+ String::from("ddd"),
365+ String::from("eee"),
366+ String::from("q"),
367+ String::from("aaa"),
368+ String::from("bbb"),
369+ String::from("ccc"),
370+ String::from("ddd"),
371+ String::from("eee"),
372+ String::from("q"),
373+ String::from("aaa"),
374+ String::from("bbb"),
375+ String::from("ccc"),
376+ String::from("ddd"),
377+ String::from("eee"),
378+ ];
379+ command_list
380+ .into_iter()
381+ .filter(|command| command.contains(_input))
382+ .collect()
383+ }, // completion
384+ |editor: &mut Editor, input: &str| match input {
385+ "q" => editor.should_close = true,
386+ _ => (),
387+ },
388+ );
389+ compositor.push(Box::new(prompt));
390+ }));
338391}
339392
340393// calculate line numbers for each selection range
helix-term/src/compositor.rs+25−11
@@ -18,7 +18,7 @@ use crossterm::event::Event;
1818use smol::Executor;
1919use tui::buffer::Buffer as Surface;
2020
21−pub(crate) type Callback = Box<dyn Fn(&mut Compositor)>;
21+pub type Callback = Box<dyn Fn(&mut Compositor)>;
2222
2323// --> EventResult should have a callback that takes a context with methods like .popup(),
2424// .prompt() etc. That way we can abstract it from the renderer.
@@ -29,14 +29,21 @@ pub(crate) type Callback = Box<dyn Fn(&mut Compositor)>;
2929// If Compositor was specified in the callback that's then problematic because of
3030
3131// Cursive-inspired
32−pub(crate) enum EventResult {
32+pub enum EventResult {
3333 Ignored,
3434 Consumed(Option<Callback>),
3535}
3636
37−pub(crate) trait Component {
37+use helix_view::{Editor, View};
38+// shared with commands.rs
39+pub struct Context<'a, 'b> {
40+ pub editor: &'a mut Editor,
41+ pub executor: &'a smol::Executor<'b>,
42+}
43+
44+pub trait Component {
3845 /// Process input events, return true if handled.
39− fn handle_event(&mut self, event: Event, executor: &Executor) -> EventResult;
46+ fn handle_event(&mut self, event: Event, ctx: &mut Context) -> EventResult;
4047 // , args: ()
4148
4249 /// Should redraw? Useful for saving redraw cycles if we know component didn't change.
@@ -44,7 +51,7 @@ pub(crate) trait Component {
4451 true
4552 }
4653
47− fn render(&mut self, renderer: &mut Renderer);
54+ fn render(&mut self, renderer: &mut Renderer, ctx: &mut Context);
4855}
4956
5057// struct Editor { };
@@ -94,7 +101,7 @@ pub(crate) trait Component {
94101// // 2) Alternatively,
95102//}
96103
97−pub(crate) struct Compositor {
104+pub struct Compositor {
98105 layers: Vec<Box<dyn Component>>,
99106}
100107
@@ -111,17 +118,24 @@ impl Compositor {
111118 self.layers.pop();
112119 }
113120
114− pub fn handle_event(&mut self, event: Event, executor: &Executor) -> () {
121+ pub fn handle_event(&mut self, event: Event, cx: &mut Context) -> bool {
115122 // TODO: custom focus
116123 if let Some(layer) = self.layers.last_mut() {
117− layer.handle_event(event, executor);
118− // return should_update
124+ return match layer.handle_event(event, cx) {
125+ EventResult::Consumed(Some(callback)) => {
126+ callback(self);
127+ true
128+ }
129+ EventResult::Consumed(None) => true,
130+ EventResult::Ignored => false,
131+ };
119132 }
133+ false
120134 }
121135
122− pub fn render(&mut self, renderer: &mut Renderer) {
136+ pub fn render(&mut self, renderer: &mut Renderer, cx: &mut Context) {
123137 for layer in &mut self.layers {
124− layer.render(renderer)
138+ layer.render(renderer, cx)
125139 }
126140 }
127141}
helix-term/src/prompt.rs+23−6
@@ -1,5 +1,9 @@
1−use crate::Editor;
2−use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
1+use crate::{
2+ application::Renderer,
3+ compositor::{Component, Context, EventResult},
4+};
5+use crossterm::event::{Event, KeyCode, KeyEvent, KeyModifiers};
6+use helix_view::Editor;
37use std::string::String;
48
59pub struct Prompt {
@@ -79,9 +83,16 @@ impl Prompt {
7983 pub fn exit_selection(&mut self) {
8084 self.completion_selection_index = None;
8185 }
86+}
87+
88+impl Component for Prompt {
89+ fn handle_event(&mut self, event: Event, cx: &mut Context) -> EventResult {
90+ let event = match event {
91+ Event::Key(event) => event,
92+ _ => return EventResult::Ignored,
93+ };
8294
83− pub fn handle_event(&mut self, key_event: KeyEvent, editor: &mut Editor) {
84− match key_event {
95+ match event {
8596 KeyEvent {
8697 code: KeyCode::Char(c),
8798 modifiers: KeyModifiers::NONE,
@@ -112,7 +123,7 @@ impl Prompt {
112123 KeyEvent {
113124 code: KeyCode::Enter,
114125 ..
115− } => (self.callback_fn)(editor, &self.line),
126+ } => (self.callback_fn)(cx.editor, &self.line),
116127 KeyEvent {
117128 code: KeyCode::Tab, ..
118129 } => self.change_completion_selection(),
@@ -121,6 +132,12 @@ impl Prompt {
121132 modifiers: KeyModifiers::CONTROL,
122133 } => self.exit_selection(),
123134 _ => (),
124− }
135+ };
136+
137+ EventResult::Consumed(None)
138+ }
139+
140+ fn render(&mut self, renderer: &mut Renderer, cx: &mut Context) {
141+ renderer.render_prompt(self, &cx.editor.theme)
125142 }
126143}