aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBlaž Hrastnik2021-02-19 08:46:43 +0000
committerBlaž Hrastnik2021-02-19 08:46:43 +0000
commit7877647cf0812380623b0087fbd7bea0ee9fae20 (patch)
treea386d8b5534310763dbab12b9e21ac893b431f94
parent1e1dae1c11bf00b56213995679647ff30b664a17 (diff)
Allow closing individual views.
-rw-r--r--helix-term/src/application.rs5
-rw-r--r--helix-term/src/commands.rs5
-rw-r--r--helix-view/src/editor.rs15
-rw-r--r--helix-view/src/tree.rs124
-rw-r--r--helix-view/src/view.rs3
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::<Vec<&str>>();
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<Document>,
- pub should_close: bool,
pub count: Option<usize>,
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<View>),
Container(Box<Container>),
}
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<Item = (&mut View, bool)> {
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<Self, Error> {
let view = Self {
+ id: Key::default(),
doc,
first_line: 0,
area: Rect::default(), // will get calculated upon inserting into tree