mirror of https://github.com/helix-editor/helix
Allow closing individual views.
parent
1e1dae1c11
commit
7877647cf0
|
@ -82,7 +82,7 @@ impl Application {
|
||||||
self.render();
|
self.render();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
if self.editor.should_close {
|
if self.editor.should_close() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -116,9 +116,8 @@ impl Application {
|
||||||
None => panic!(),
|
None => panic!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
if should_redraw {
|
if should_redraw && !self.editor.should_close() {
|
||||||
self.render();
|
self.render();
|
||||||
// calling render twice here fixes it for some reason
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -558,7 +558,10 @@ pub fn command_mode(cx: &mut Context) {
|
||||||
let parts = input.split_ascii_whitespace().collect::<Vec<&str>>();
|
let parts = input.split_ascii_whitespace().collect::<Vec<&str>>();
|
||||||
|
|
||||||
match *parts.as_slice() {
|
match *parts.as_slice() {
|
||||||
["q"] => editor.should_close = true,
|
["q"] => {
|
||||||
|
editor.tree.remove(editor.view().id);
|
||||||
|
// editor.should_close = true,
|
||||||
|
}
|
||||||
["o", path] => {
|
["o", path] => {
|
||||||
editor.open(path.into(), executor);
|
editor.open(path.into(), executor);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,12 +4,13 @@ use crate::{Document, View};
|
||||||
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use slotmap::DefaultKey as Key;
|
||||||
|
|
||||||
use anyhow::Error;
|
use anyhow::Error;
|
||||||
|
|
||||||
pub struct Editor {
|
pub struct Editor {
|
||||||
pub tree: Tree,
|
pub tree: Tree,
|
||||||
// pub documents: Vec<Document>,
|
// pub documents: Vec<Document>,
|
||||||
pub should_close: bool,
|
|
||||||
pub count: Option<usize>,
|
pub count: Option<usize>,
|
||||||
pub theme: Theme, // TODO: share one instance
|
pub theme: Theme, // TODO: share one instance
|
||||||
pub language_servers: helix_lsp::Registry,
|
pub language_servers: helix_lsp::Registry,
|
||||||
|
@ -25,7 +26,6 @@ impl Editor {
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
tree: Tree::new(area),
|
tree: Tree::new(area),
|
||||||
should_close: false,
|
|
||||||
count: None,
|
count: None,
|
||||||
theme,
|
theme,
|
||||||
language_servers,
|
language_servers,
|
||||||
|
@ -54,10 +54,19 @@ impl Editor {
|
||||||
}
|
}
|
||||||
|
|
||||||
let view = View::new(doc)?;
|
let view = View::new(doc)?;
|
||||||
self.tree.insert(view);
|
let id = self.tree.insert(view);
|
||||||
|
self.tree.get_mut(id).id = id;
|
||||||
Ok(())
|
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 {
|
pub fn view(&self) -> &View {
|
||||||
self.tree.get(self.tree.focus)
|
self.tree.get(self.tree.focus)
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,6 @@ use tui::layout::Rect;
|
||||||
pub struct Tree {
|
pub struct Tree {
|
||||||
root: Key,
|
root: Key,
|
||||||
// (container, index inside the container)
|
// (container, index inside the container)
|
||||||
current: (Key, usize),
|
|
||||||
pub focus: Key,
|
pub focus: Key,
|
||||||
// fullscreen: bool,
|
// fullscreen: bool,
|
||||||
area: Rect,
|
area: Rect,
|
||||||
|
@ -18,18 +17,29 @@ pub struct Tree {
|
||||||
stack: Vec<(Key, Rect)>,
|
stack: Vec<(Key, Rect)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum Node {
|
pub struct Node {
|
||||||
|
parent: Key,
|
||||||
|
content: Content,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum Content {
|
||||||
View(Box<View>),
|
View(Box<View>),
|
||||||
Container(Box<Container>),
|
Container(Box<Container>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Node {
|
impl Node {
|
||||||
pub fn container() -> Self {
|
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 {
|
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 {
|
impl Tree {
|
||||||
pub fn new(area: Rect) -> Self {
|
pub fn new(area: Rect) -> Self {
|
||||||
let root = Node::container();
|
let root = Node::container();
|
||||||
|
|
||||||
let mut nodes = HopSlotMap::new();
|
let mut nodes = HopSlotMap::new();
|
||||||
let root = nodes.insert(root);
|
let root = nodes.insert(root);
|
||||||
|
|
||||||
|
// root is it's own parent
|
||||||
|
nodes[root].parent = root;
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
root,
|
root,
|
||||||
current: (root, 0),
|
focus: root,
|
||||||
focus: Key::default(),
|
|
||||||
// fullscreen: false,
|
// fullscreen: false,
|
||||||
area,
|
area,
|
||||||
nodes,
|
nodes,
|
||||||
|
@ -81,23 +94,34 @@ impl Tree {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert(&mut self, view: View) -> Key {
|
pub fn insert(&mut self, view: View) -> Key {
|
||||||
let node = self.nodes.insert(Node::view(view));
|
let focus = self.focus;
|
||||||
let (id, pos) = self.current;
|
let parent = self.nodes[focus].parent;
|
||||||
let container = match &mut self.nodes[id] {
|
let mut node = Node::view(view);
|
||||||
Node::Container(container) => container,
|
node.parent = parent;
|
||||||
|
let node = self.nodes.insert(node);
|
||||||
|
|
||||||
|
let container = match &mut self.nodes[parent] {
|
||||||
|
Node {
|
||||||
|
content: Content::Container(container),
|
||||||
|
..
|
||||||
|
} => container,
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
// insert node after the current item if there is children already
|
// insert node after the current item if there is children already
|
||||||
let pos = if container.children.is_empty() {
|
let pos = if container.children.is_empty() {
|
||||||
pos
|
0
|
||||||
} else {
|
} else {
|
||||||
|
let pos = container
|
||||||
|
.children
|
||||||
|
.iter()
|
||||||
|
.position(|&child| child == focus)
|
||||||
|
.unwrap();
|
||||||
pos + 1
|
pos + 1
|
||||||
};
|
};
|
||||||
|
|
||||||
container.children.insert(pos, node);
|
container.children.insert(pos, node);
|
||||||
// focus the new node
|
// focus the new node
|
||||||
self.current = (id, pos);
|
|
||||||
self.focus = node;
|
self.focus = node;
|
||||||
|
|
||||||
// recalculate all the sizes
|
// recalculate all the sizes
|
||||||
|
@ -106,26 +130,78 @@ impl Tree {
|
||||||
node
|
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)> {
|
pub fn views(&mut self) -> impl Iterator<Item = (&mut View, bool)> {
|
||||||
let focus = self.focus;
|
let focus = self.focus;
|
||||||
self.nodes
|
self.nodes
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
.filter_map(move |(key, node)| match node {
|
.filter_map(move |(key, node)| match node {
|
||||||
Node::View(view) => Some((view.as_mut(), focus == key)),
|
Node {
|
||||||
Node::Container(..) => None,
|
content: Content::View(view),
|
||||||
|
..
|
||||||
|
} => Some((view.as_mut(), focus == key)),
|
||||||
|
_ => None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get(&self, index: Key) -> &View {
|
pub fn get(&self, index: Key) -> &View {
|
||||||
match &self.nodes[index] {
|
match &self.nodes[index] {
|
||||||
Node::View(view) => view,
|
Node {
|
||||||
|
content: Content::View(view),
|
||||||
|
..
|
||||||
|
} => view,
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_mut(&mut self, index: Key) -> &mut View {
|
pub fn get_mut(&mut self, index: Key) -> &mut View {
|
||||||
match &mut self.nodes[index] {
|
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!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -136,6 +212,10 @@ impl Tree {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn recalculate(&mut self) {
|
pub fn recalculate(&mut self) {
|
||||||
|
if self.is_empty() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
self.stack.push((self.root, self.area));
|
self.stack.push((self.root, self.area));
|
||||||
|
|
||||||
// take the area
|
// take the area
|
||||||
|
@ -146,12 +226,12 @@ impl Tree {
|
||||||
while let Some((key, area)) = self.stack.pop() {
|
while let Some((key, area)) = self.stack.pop() {
|
||||||
let node = &mut self.nodes[key];
|
let node = &mut self.nodes[key];
|
||||||
|
|
||||||
match node {
|
match &mut node.content {
|
||||||
Node::View(view) => {
|
Content::View(view) => {
|
||||||
// debug!!("setting view area {:?}", area);
|
// debug!!("setting view area {:?}", area);
|
||||||
view.area = area;
|
view.area = area;
|
||||||
} // TODO: call f()
|
} // TODO: call f()
|
||||||
Node::Container(container) => {
|
Content::Container(container) => {
|
||||||
// debug!!("setting container area {:?}", area);
|
// debug!!("setting container area {:?}", area);
|
||||||
container.area = area;
|
container.area = area;
|
||||||
|
|
||||||
|
@ -263,9 +343,9 @@ impl<'a> Iterator for Traverse<'a> {
|
||||||
|
|
||||||
let node = &self.tree.nodes[key];
|
let node = &self.tree.nodes[key];
|
||||||
|
|
||||||
match node {
|
match &node.content {
|
||||||
Node::View(view) => return Some((key, view)),
|
Content::View(view) => return Some((key, view)),
|
||||||
Node::Container(container) => {
|
Content::Container(container) => {
|
||||||
self.stack.extend(container.children.iter().rev());
|
self.stack.extend(container.children.iter().rev());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ use helix_core::{
|
||||||
indent::TAB_WIDTH,
|
indent::TAB_WIDTH,
|
||||||
Position, RopeSlice,
|
Position, RopeSlice,
|
||||||
};
|
};
|
||||||
|
use slotmap::DefaultKey as Key;
|
||||||
use tui::layout::Rect;
|
use tui::layout::Rect;
|
||||||
|
|
||||||
pub const PADDING: usize = 5;
|
pub const PADDING: usize = 5;
|
||||||
|
@ -15,6 +16,7 @@ pub const PADDING: usize = 5;
|
||||||
// TODO: view should be View { doc: Document(state, history,..) }
|
// TODO: view should be View { doc: Document(state, history,..) }
|
||||||
// since we can have multiple views into the same file
|
// since we can have multiple views into the same file
|
||||||
pub struct View {
|
pub struct View {
|
||||||
|
pub id: Key,
|
||||||
pub doc: Document,
|
pub doc: Document,
|
||||||
pub first_line: usize,
|
pub first_line: usize,
|
||||||
pub area: Rect,
|
pub area: Rect,
|
||||||
|
@ -24,6 +26,7 @@ pub struct View {
|
||||||
impl View {
|
impl View {
|
||||||
pub fn new(doc: Document) -> Result<Self, Error> {
|
pub fn new(doc: Document) -> Result<Self, Error> {
|
||||||
let view = Self {
|
let view = Self {
|
||||||
|
id: Key::default(),
|
||||||
doc,
|
doc,
|
||||||
first_line: 0,
|
first_line: 0,
|
||||||
area: Rect::default(), // will get calculated upon inserting into tree
|
area: Rect::default(), // will get calculated upon inserting into tree
|
||||||
|
|
Loading…
Reference in New Issue