From 448c1abba04e11f77e53629dc06fe47619a741d4 Mon Sep 17 00:00:00 2001 From: Blaž Hrastnik Date: Wed, 3 Feb 2021 19:36:54 +0900 Subject: View tree implementation: render multiple split views. Cursors are still a bit buggy and we should render in focus statusbar differently than in the other pane. --- helix-term/src/application.rs | 19 +++++--- helix-term/src/commands.rs | 111 ++++++++++++++++++++---------------------- helix-term/src/ui/editor.rs | 50 +++++++++++++------ helix-term/src/ui/mod.rs | 56 ++++++++++----------- 4 files changed, 127 insertions(+), 109 deletions(-) (limited to 'helix-term/src') diff --git a/helix-term/src/application.rs b/helix-term/src/application.rs index f32db3b3..6e000534 100644 --- a/helix-term/src/application.rs +++ b/helix-term/src/application.rs @@ -39,11 +39,12 @@ impl Application { pub fn new(mut args: Args, executor: &'static smol::Executor<'static>) -> Result { let backend = CrosstermBackend::new(stdout()); let mut terminal = Terminal::new(backend)?; - let mut editor = Editor::new(); let size = terminal.size()?; + let mut editor = Editor::new(size); - if let Some(file) = args.values_of_t::("files").unwrap().pop() { - editor.open(file, (size.width, size.height), executor)?; + let files = args.values_of_t::("files").unwrap(); + for file in files { + editor.open(file, executor)?; } let mut compositor = Compositor::new(); @@ -132,11 +133,13 @@ impl Application { Notification::PublishDiagnostics(params) => { let path = Some(params.uri.to_file_path().unwrap()); - let view = self - .editor - .views - .iter_mut() - .find(|view| view.doc.path == path); + let view: Option<&mut View> = None; + // TODO + // let view = self + // .editor + // .views + // .iter_mut() + // .find(|view| view.doc.path == path); if let Some(view) = view { let doc = view.doc.text().slice(..); diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index ff8704d8..be43159d 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -183,7 +183,7 @@ pub fn check_cursor_in_view(view: &View) -> bool { let doc = &view.doc; let cursor = doc.selection().cursor(); let line = doc.text().char_to_line(cursor); - let document_end = view.first_line + view.size.1.saturating_sub(1) as usize; + let document_end = view.first_line + view.area.height.saturating_sub(1) as usize; if (line > document_end.saturating_sub(PADDING)) | (line < view.first_line + PADDING) { return false; @@ -197,7 +197,7 @@ pub fn page_up(cx: &mut Context) { return; } - view.first_line = view.first_line.saturating_sub(view.size.1 as usize); + view.first_line = view.first_line.saturating_sub(view.area.height as usize); if !check_cursor_in_view(view) { let text = view.doc.text(); @@ -208,7 +208,7 @@ pub fn page_up(cx: &mut Context) { pub fn page_down(cx: &mut Context) { let view = cx.view(); - view.first_line += view.size.1 as usize + PADDING; + view.first_line += view.area.height as usize + PADDING; if view.first_line < view.doc.text().len_lines() { let text = view.doc.text(); @@ -223,7 +223,9 @@ pub fn half_page_up(cx: &mut Context) { return; } - view.first_line = view.first_line.saturating_sub(view.size.1 as usize / 2); + view.first_line = view + .first_line + .saturating_sub(view.area.height as usize / 2); if !check_cursor_in_view(view) { let text = &view.doc.text(); @@ -235,8 +237,8 @@ pub fn half_page_up(cx: &mut Context) { pub fn half_page_down(cx: &mut Context) { let view = cx.view(); let lines = view.doc.text().len_lines(); - if view.first_line < lines.saturating_sub(view.size.1 as usize) { - view.first_line += view.size.1 as usize / 2; + if view.first_line < lines.saturating_sub(view.area.height as usize) { + view.first_line += view.area.height as usize / 2; } if !check_cursor_in_view(view) { let text = view.doc.text(); @@ -367,8 +369,7 @@ pub fn change_selection(cx: &mut Context) { pub fn collapse_selection(cx: &mut Context) { let selection = cx - .view() - .doc + .doc() .selection() .transform(|range| Range::new(range.head, range.head)); @@ -377,8 +378,7 @@ pub fn collapse_selection(cx: &mut Context) { pub fn flip_selections(cx: &mut Context) { let selection = cx - .view() - .doc + .doc() .selection() .transform(|range| Range::new(range.head, range.anchor)); @@ -396,8 +396,7 @@ pub fn insert_mode(cx: &mut Context) { enter_insert_mode(cx); let selection = cx - .view() - .doc + .doc() .selection() .transform(|range| Range::new(range.to(), range.from())); cx.doc().set_selection(selection); @@ -431,37 +430,40 @@ pub fn command_mode(cx: &mut Context) { |_input: &str| { // TODO: i need this duplicate list right now to avoid borrow checker issues let command_list = vec![ - String::from("q"), - String::from("aaa"), - String::from("bbb"), - String::from("ccc"), - String::from("ddd"), - String::from("eee"), - String::from("averylongcommandaverylongcommandaverylongcommandaverylongcommandaverylongcommand"), - String::from("q"), - String::from("aaa"), - String::from("bbb"), - String::from("ccc"), - String::from("ddd"), - String::from("eee"), - String::from("q"), - String::from("aaa"), - String::from("bbb"), - String::from("ccc"), - String::from("ddd"), - String::from("eee"), - String::from("q"), - String::from("aaa"), - String::from("bbb"), - String::from("ccc"), - String::from("ddd"), - String::from("eee"), - String::from("q"), - String::from("aaa"), - String::from("bbb"), - String::from("ccc"), - String::from("ddd"), - String::from("eee"), + "q".to_string(), + "o".to_string(), + "w".to_string(), + // String::from("q"), + // String::from("aaa"), + // String::from("bbb"), + // String::from("ccc"), + // String::from("ddd"), + // String::from("eee"), + // String::from("averylongcommandaverylongcommandaverylongcommandaverylongcommandaverylongcommand"), + // String::from("q"), + // String::from("aaa"), + // String::from("bbb"), + // String::from("ccc"), + // String::from("ddd"), + // String::from("eee"), + // String::from("q"), + // String::from("aaa"), + // String::from("bbb"), + // String::from("ccc"), + // String::from("ddd"), + // String::from("eee"), + // String::from("q"), + // String::from("aaa"), + // String::from("bbb"), + // String::from("ccc"), + // String::from("ddd"), + // String::from("eee"), + // String::from("q"), + // String::from("aaa"), + // String::from("bbb"), + // String::from("ccc"), + // String::from("ddd"), + // String::from("eee"), ]; command_list .into_iter() @@ -478,8 +480,7 @@ pub fn command_mode(cx: &mut Context) { match *parts.as_slice() { ["q"] => editor.should_close = true, ["o", path] => { - let size = editor.view().size; - editor.open(path.into(), size, executor); + editor.open(path.into(), executor); } _ => (), } @@ -499,12 +500,13 @@ pub fn file_picker(cx: &mut Context) { } pub fn buffer_picker(cx: &mut Context) { - cx.callback = Some(Box::new( - |compositor: &mut Compositor, editor: &mut Editor| { - let picker = ui::buffer_picker(&editor.views, editor.focus); - compositor.push(Box::new(picker)); - }, - )); + unimplemented!() + // cx.callback = Some(Box::new( + // |compositor: &mut Compositor, editor: &mut Editor| { + // let picker = ui::buffer_picker(&editor.views, editor.focus); + // compositor.push(Box::new(picker)); + // }, + // )); } // calculate line numbers for each selection range @@ -617,12 +619,7 @@ fn append_changes_to_history(cx: &mut Context) { // TODO: trigger lsp/documentDidChange with changes // HAXX: we need to reconstruct the state as it was before the changes.. - let old_state = cx - .view() - .doc - .old_state - .take() - .expect("no old_state available"); + let old_state = cx.doc().old_state.take().expect("no old_state available"); // TODO: take transaction by value? cx.doc().history.commit_revision(&transaction, &old_state); diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs index a97ee713..721dccc0 100644 --- a/helix-term/src/ui/editor.rs +++ b/helix-term/src/ui/editor.rs @@ -37,15 +37,20 @@ impl EditorView { surface: &mut Surface, theme: &Theme, ) { - let area = Rect::new(OFFSET, 0, viewport.width - OFFSET, viewport.height - 2); // - 2 for statusline and prompt + let area = Rect::new( + viewport.x + OFFSET, + viewport.y, + viewport.width - OFFSET, + viewport.height - 2, + ); // - 2 for statusline and prompt self.render_buffer(view, area, surface, theme); // clear with background color // TODO: this seems to prevent setting style later // surface.set_style(viewport, theme.get("ui.background")); - let area = Rect::new(0, viewport.height - 2, viewport.width, 1); - self.render_statusline(view, area, surface, theme); + let area = Rect::new(viewport.x, viewport.height - 2, viewport.width, 1); + self.render_statusline(&view.doc, area, surface, theme); } // TODO: ideally not &mut View but highlights require it because of cursor cache @@ -203,34 +208,46 @@ impl EditorView { let last_line = view.last_line(); for (i, line) in (view.first_line..last_line).enumerate() { if view.doc.diagnostics.iter().any(|d| d.line == line) { - surface.set_stringn(0, i as u16, "●", 1, warning); + surface.set_stringn( + viewport.x + 0 - OFFSET, + viewport.y + i as u16, + "●", + 1, + warning, + ); } - surface.set_stringn(1, i as u16, format!("{:>5}", line + 1), 5, style); + surface.set_stringn( + viewport.x + 1 - OFFSET, + viewport.y + i as u16, + format!("{:>5}", line + 1), + 5, + style, + ); } } pub fn render_statusline( &self, - view: &View, + doc: &Document, viewport: Rect, surface: &mut Surface, theme: &Theme, ) { let text_color = text_color(); - let mode = match view.doc.mode() { + let mode = match doc.mode() { Mode::Insert => "INS", Mode::Normal => "NOR", Mode::Goto => "GOTO", }; // statusline surface.set_style( - Rect::new(0, viewport.y, viewport.width, 1), + Rect::new(viewport.x, viewport.y, viewport.width, 1), theme.get("ui.statusline"), ); - surface.set_string(1, viewport.y, mode, text_color); + surface.set_string(viewport.x + 1, viewport.y, mode, text_color); - if let Some(path) = view.doc.relative_path() { + if let Some(path) = doc.relative_path() { let path = path.to_string_lossy(); surface.set_string(6, viewport.y, path, text_color); // TODO: append [+] if modified @@ -239,7 +256,7 @@ impl EditorView { surface.set_string( viewport.width - 10, viewport.y, - format!("{}", view.doc.diagnostics.len()), + format!("{}", doc.diagnostics.len()), text_color, ); } @@ -251,9 +268,8 @@ impl Component for EditorView { Event::Resize(width, height) => { // TODO: simplistic ensure cursor in view for now // TODO: loop over views - let view = cx.editor.view_mut(); - view.size = (width, height); - view.ensure_cursor_in_view(); + cx.editor.tree.resize(Rect::new(0, 0, width, height)); + // TODO: restore view.ensure_cursor_in_view(); EventResult::Consumed(None) } Event::Key(event) => { @@ -306,8 +322,10 @@ impl Component for EditorView { // SAFETY: we cheat around the view_mut() borrow because it doesn't allow us to also borrow // theme. Theme is immutable mutating view won't disrupt theme_ref. let theme_ref = unsafe { &*(&cx.editor.theme as *const Theme) }; - let view = cx.editor.view_mut(); - self.render_view(view, area, surface, theme_ref); + for view in cx.editor.tree.views() { + // TODO: use parent area + self.render_view(view, view.area, surface, theme_ref); + } // TODO: drop unwrap } diff --git a/helix-term/src/ui/mod.rs b/helix-term/src/ui/mod.rs index bd40b249..7c12b918 100644 --- a/helix-term/src/ui/mod.rs +++ b/helix-term/src/ui/mod.rs @@ -82,38 +82,38 @@ pub fn file_picker(root: &str, ex: &'static smol::Executor) -> Picker { path.strip_prefix("./").unwrap().to_str().unwrap().into() }, move |editor: &mut Editor, path: &PathBuf| { - let size = editor.view().size; - editor.open(path.into(), size, ex); + editor.open(path.into(), ex); }, ) } use helix_view::View; pub fn buffer_picker(views: &[View], current: usize) -> Picker<(Option, usize)> { - use helix_view::Editor; - Picker::new( - views - .iter() - .enumerate() - .map(|(i, view)| (view.doc.relative_path().map(Path::to_path_buf), i)) - .collect(), - move |(path, index): &(Option, usize)| { - // format_fn - match path { - Some(path) => { - if *index == current { - format!("{} (*)", path.to_str().unwrap()).into() - } else { - path.to_str().unwrap().into() - } - } - None => "[NEW]".into(), - } - }, - |editor: &mut Editor, &(_, index): &(Option, usize)| { - if index < editor.views.len() { - editor.focus = index; - } - }, - ) + unimplemented!(); + // use helix_view::Editor; + // Picker::new( + // views + // .iter() + // .enumerate() + // .map(|(i, view)| (view.doc.relative_path().map(Path::to_path_buf), i)) + // .collect(), + // move |(path, index): &(Option, usize)| { + // // format_fn + // match path { + // Some(path) => { + // if *index == current { + // format!("{} (*)", path.to_str().unwrap()).into() + // } else { + // path.to_str().unwrap().into() + // } + // } + // None => "[NEW]".into(), + // } + // }, + // |editor: &mut Editor, &(_, index): &(Option, usize)| { + // if index < editor.views.len() { + // editor.focus = index; + // } + // }, + // ) } -- cgit v1.2.3-70-g09d2