From 7877647cf0812380623b0087fbd7bea0ee9fae20 Mon Sep 17 00:00:00 2001 From: Blaž Hrastnik Date: Fri, 19 Feb 2021 17:46:43 +0900 Subject: Allow closing individual views. --- helix-term/src/application.rs | 5 +- helix-term/src/commands.rs | 5 +- helix-view/src/editor.rs | 15 ++++- helix-view/src/tree.rs | 124 ++++++++++++++++++++++++++++++++++-------- helix-view/src/view.rs | 3 + 5 files changed, 123 insertions(+), 29 deletions(-) diff --git a/helix-term/src/application.rs b/helix-term/src/application.rs index 90391024..c82724a5 100644 --- a/helix-term/src/application.rs +++ b/helix-term/src/application.rs @@ -82,7 +82,7 @@ impl Application { self.render(); loop { - if self.editor.should_close { + if self.editor.should_close() { break; } @@ -116,9 +116,8 @@ impl Application { None => panic!(), }; - if should_redraw { + if should_redraw && !self.editor.should_close() { self.render(); - // calling render twice here fixes it for some reason } } diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index 8aff8035..e65144f4 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -558,7 +558,10 @@ pub fn command_mode(cx: &mut Context) { let parts = input.split_ascii_whitespace().collect::>(); match *parts.as_slice() { - ["q"] => editor.should_close = true, + ["q"] => { + editor.tree.remove(editor.view().id); + // editor.should_close = true, + } ["o", path] => { editor.open(path.into(), executor); } diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index 762b9e96..52285bf8 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -4,12 +4,13 @@ use crate::{Document, View}; use std::path::PathBuf; +use slotmap::DefaultKey as Key; + use anyhow::Error; pub struct Editor { pub tree: Tree, // pub documents: Vec, - pub should_close: bool, pub count: Option, pub theme: Theme, // TODO: share one instance pub language_servers: helix_lsp::Registry, @@ -25,7 +26,6 @@ impl Editor { Self { tree: Tree::new(area), - should_close: false, count: None, theme, language_servers, @@ -54,10 +54,19 @@ impl Editor { } let view = View::new(doc)?; - self.tree.insert(view); + let id = self.tree.insert(view); + self.tree.get_mut(id).id = id; Ok(()) } + pub fn close(&mut self, id: Key) { + self.tree.remove(id) + } + + pub fn should_close(&mut self) -> bool { + self.tree.is_empty() + } + pub fn view(&self) -> &View { self.tree.get(self.tree.focus) } diff --git a/helix-view/src/tree.rs b/helix-view/src/tree.rs index 9d31a33e..4b6fd77c 100644 --- a/helix-view/src/tree.rs +++ b/helix-view/src/tree.rs @@ -7,7 +7,6 @@ use tui::layout::Rect; pub struct Tree { root: Key, // (container, index inside the container) - current: (Key, usize), pub focus: Key, // fullscreen: bool, area: Rect, @@ -18,18 +17,29 @@ pub struct Tree { stack: Vec<(Key, Rect)>, } -pub enum Node { +pub struct Node { + parent: Key, + content: Content, +} + +pub enum Content { View(Box), Container(Box), } impl Node { pub fn container() -> Self { - Self::Container(Box::new(Container::new())) + Node { + parent: Key::default(), + content: Content::Container(Box::new(Container::new())), + } } pub fn view(view: View) -> Self { - Self::View(Box::new(view)) + Node { + parent: Key::default(), + content: Content::View(Box::new(view)), + } } } @@ -66,13 +76,16 @@ impl Default for Container { impl Tree { pub fn new(area: Rect) -> Self { let root = Node::container(); + let mut nodes = HopSlotMap::new(); let root = nodes.insert(root); + // root is it's own parent + nodes[root].parent = root; + Self { root, - current: (root, 0), - focus: Key::default(), + focus: root, // fullscreen: false, area, nodes, @@ -81,23 +94,34 @@ impl Tree { } pub fn insert(&mut self, view: View) -> Key { - let node = self.nodes.insert(Node::view(view)); - let (id, pos) = self.current; - let container = match &mut self.nodes[id] { - Node::Container(container) => container, + let focus = self.focus; + let parent = self.nodes[focus].parent; + let mut node = Node::view(view); + node.parent = parent; + let node = self.nodes.insert(node); + + let container = match &mut self.nodes[parent] { + Node { + content: Content::Container(container), + .. + } => container, _ => unreachable!(), }; // insert node after the current item if there is children already let pos = if container.children.is_empty() { - pos + 0 } else { + let pos = container + .children + .iter() + .position(|&child| child == focus) + .unwrap(); pos + 1 }; container.children.insert(pos, node); // focus the new node - self.current = (id, pos); self.focus = node; // recalculate all the sizes @@ -106,26 +130,78 @@ impl Tree { node } + pub fn remove(&mut self, index: Key) { + let mut stack = Vec::new(); + + if self.focus == index { + // focus on something else + self.focus_next(); + } + + stack.push(index); + + while let Some(index) = stack.pop() { + let parent_id = self.nodes[index].parent; + if let Node { + content: Content::Container(container), + .. + } = &mut self.nodes[parent_id] + { + if let Some(pos) = container.children.iter().position(|&child| child == index) { + container.children.remove(pos); + + // TODO: if container now only has one child, remove it and place child in parent + if container.children.is_empty() && parent_id != self.root { + // if container now empty, remove it + stack.push(parent_id); + } + } + } + self.nodes.remove(index); + } + + self.recalculate() + } + pub fn views(&mut self) -> impl Iterator { let focus = self.focus; self.nodes .iter_mut() .filter_map(move |(key, node)| match node { - Node::View(view) => Some((view.as_mut(), focus == key)), - Node::Container(..) => None, + Node { + content: Content::View(view), + .. + } => Some((view.as_mut(), focus == key)), + _ => None, }) } pub fn get(&self, index: Key) -> &View { match &self.nodes[index] { - Node::View(view) => view, + Node { + content: Content::View(view), + .. + } => view, _ => unreachable!(), } } pub fn get_mut(&mut self, index: Key) -> &mut View { match &mut self.nodes[index] { - Node::View(view) => view, + Node { + content: Content::View(view), + .. + } => view, + _ => unreachable!(), + } + } + + pub fn is_empty(&self) -> bool { + match &self.nodes[self.root] { + Node { + content: Content::Container(container), + .. + } => container.children.is_empty(), _ => unreachable!(), } } @@ -136,6 +212,10 @@ impl Tree { } pub fn recalculate(&mut self) { + if self.is_empty() { + return; + } + self.stack.push((self.root, self.area)); // take the area @@ -146,12 +226,12 @@ impl Tree { while let Some((key, area)) = self.stack.pop() { let node = &mut self.nodes[key]; - match node { - Node::View(view) => { + match &mut node.content { + Content::View(view) => { // debug!!("setting view area {:?}", area); view.area = area; } // TODO: call f() - Node::Container(container) => { + Content::Container(container) => { // debug!!("setting container area {:?}", area); container.area = area; @@ -263,9 +343,9 @@ impl<'a> Iterator for Traverse<'a> { let node = &self.tree.nodes[key]; - match node { - Node::View(view) => return Some((key, view)), - Node::Container(container) => { + match &node.content { + Content::View(view) => return Some((key, view)), + Content::Container(container) => { self.stack.extend(container.children.iter().rev()); } } diff --git a/helix-view/src/view.rs b/helix-view/src/view.rs index 1623dfc4..e02436b3 100644 --- a/helix-view/src/view.rs +++ b/helix-view/src/view.rs @@ -8,6 +8,7 @@ use helix_core::{ indent::TAB_WIDTH, Position, RopeSlice, }; +use slotmap::DefaultKey as Key; use tui::layout::Rect; pub const PADDING: usize = 5; @@ -15,6 +16,7 @@ pub const PADDING: usize = 5; // TODO: view should be View { doc: Document(state, history,..) } // since we can have multiple views into the same file pub struct View { + pub id: Key, pub doc: Document, pub first_line: usize, pub area: Rect, @@ -24,6 +26,7 @@ pub struct View { impl View { pub fn new(doc: Document) -> Result { let view = Self { + id: Key::default(), doc, first_line: 0, area: Rect::default(), // will get calculated upon inserting into tree -- cgit v1.2.3-70-g09d2