on master help sign in
editor.open can now either replace the current view or open in a split.
8a0ab44Blaž Hrastnik
helix-term/src/application.rs+2−1
@@ -39,7 +39,8 @@ impl Application {
39
39
40 let files = args.values_of_t::<PathBuf>("files").unwrap();
40 let files = args.values_of_t::<PathBuf>("files").unwrap();
41 for file in files {
41 for file in files {
42+ use helix_view::editor::Action;
42− editor.open(file)?;
43+ editor.open(file, Action::HorizontalSplit)?;
43 }
44 }
44
45
45 compositor.push(Box::new(ui::EditorView::new()));
46 compositor.push(Box::new(ui::EditorView::new()));
helix-term/src/commands.rs+6−3
@@ -798,7 +798,8 @@ pub fn command_mode(cx: &mut Context) {
798 // editor.should_close = true,
798 // editor.should_close = true,
799 }
799 }
800 ["o", path] | ["open", path] => {
800 ["o", path] | ["open", path] => {
801+ use helix_view::editor::Action;
801− editor.open(path.into());
802+ editor.open(path.into(), Action::Replace);
802 }
803 }
803 ["w"] | ["write"] => {
804 ["w"] | ["write"] => {
804 // TODO: non-blocking via save() command
805 // TODO: non-blocking via save() command
@@ -992,11 +993,13 @@ pub fn exit_select_mode(cx: &mut Context) {
992}
993}
993
994
994fn goto(cx: &mut Context, locations: Vec<lsp::Location>) {
995fn goto(cx: &mut Context, locations: Vec<lsp::Location>) {
996+ use helix_view::editor::Action;
995 cx.doc().mode = Mode::Normal;
997 cx.doc().mode = Mode::Normal;
996
998
997 match locations.as_slice() {
999 match locations.as_slice() {
998 [location] => {
1000 [location] => {
1001+ cx.editor
999− cx.editor.open(PathBuf::from(location.uri.path()));
1002+ .open(PathBuf::from(location.uri.path()), Action::Replace);
1000 let doc = cx.doc();
1003 let doc = cx.doc();
1001 let definition_pos = location.range.start;
1004 let definition_pos = location.range.start;
1002 let new_pos = helix_lsp::util::lsp_pos_to_pos(doc.text(), definition_pos);
1005 let new_pos = helix_lsp::util::lsp_pos_to_pos(doc.text(), definition_pos);
@@ -1012,7 +1015,7 @@ fn goto(cx: &mut Context, locations: Vec<lsp::Location>) {
1012 format!("{}:{}", file, line).into()
1015 format!("{}:{}", file, line).into()
1013 },
1016 },
1014 move |editor: &mut Editor, item| {
1017 move |editor: &mut Editor, item| {
1015− editor.open(PathBuf::from(item.uri.path()));
1018+ editor.open(PathBuf::from(item.uri.path()), Action::Replace);
1016 // TODO: issues with doc already being broo
1019 // TODO: issues with doc already being broo
1017 let id = editor.view().doc;
1020 let id = editor.view().doc;
1018 let doc = &mut editor.documents[id];
1021 let doc = &mut editor.documents[id];
helix-term/src/ui/mod.rs+4−1
@@ -101,7 +101,10 @@ pub fn file_picker(root: &str) -> Picker<PathBuf> {
101 path.strip_prefix("./").unwrap().to_str().unwrap().into()
101 path.strip_prefix("./").unwrap().to_str().unwrap().into()
102 },
102 },
103 move |editor: &mut Editor, path: &PathBuf| {
103 move |editor: &mut Editor, path: &PathBuf| {
104+ use helix_view::editor::Action;
105+ let document_id = editor
106+ .open(path.into(), Action::Replace)
104− let document_id = editor.open(path.into()).expect("editor.open failed");
107+ .expect("editor.open failed");
105 },
108 },
106 )
109 )
107}
110}
helix-view/src/editor.rs+61−42
@@ -16,6 +16,12 @@ pub struct Editor {
16 pub executor: &'static smol::Executor<'static>,
16 pub executor: &'static smol::Executor<'static>,
17}
17}
18
18
19+pub enum Action {
20+ Replace,
21+ HorizontalSplit,
22+ VerticalSplit,
23+}
24+
19impl Editor {
25impl Editor {
20 pub fn new(executor: &'static smol::Executor<'static>, mut area: tui::layout::Rect) -> Self {
26 pub fn new(executor: &'static smol::Executor<'static>, mut area: tui::layout::Rect) -> Self {
21 let theme = Theme::default();
27 let theme = Theme::default();
@@ -41,50 +47,63 @@ impl Editor {
41 }
47 }
42 }
48 }
43
49
44− pub fn open(&mut self, path: PathBuf) -> Result<DocumentId, Error> {
50+ pub fn open(&mut self, path: PathBuf, action: Action) -> Result<DocumentId, Error> {
51+ let id = self
52+ .documents()
45− let existing_view = self.documents().find(|doc| doc.path() == Some(&path));
53+ .find(|doc| doc.path() == Some(&path))
46−
54+ .map(|doc| doc.id);
47− // TODO:
55+
48− // if view with doc, focus it
56+ let id = if let Some(id) = id {
57+ id
49− // else open new split
58+ } else {
50−
51− // if let Some((view, _)) = existing_view {
52− // let id = view.doc.id;
53− // self.tree.focus = view.id;
54− // return Ok(id);
55− // }
56−
57− let mut doc = Document::load(path, self.theme.scopes())?;
59+ let mut doc = Document::load(path, self.theme.scopes())?;
58−
60+
59− // try to find a language server based on the language name
61+ // try to find a language server based on the language name
60− let language_server = doc
62+ let language_server = doc
61− .language
63+ .language
62− .as_ref()
64+ .as_ref()
63− .and_then(|language| self.language_servers.get(language, self.executor));
65+ .and_then(|language| self.language_servers.get(language, self.executor));
64−
66+
65− if let Some(language_server) = language_server {
67+ if let Some(language_server) = language_server {
66− doc.set_language_server(Some(language_server.clone()));
68+ doc.set_language_server(Some(language_server.clone()));
67−
69+
68− let language_id = doc
70+ let language_id = doc
69− .language()
71+ .language()
70− .and_then(|s| s.split('.').last()) // source.rust
72+ .and_then(|s| s.split('.').last()) // source.rust
71− .map(ToOwned::to_owned)
73+ .map(ToOwned::to_owned)
72− .unwrap_or_default();
74+ .unwrap_or_default();
73−
75+
74− smol::block_on(language_server.text_document_did_open(
76+ smol::block_on(language_server.text_document_did_open(
75− doc.url().unwrap(),
77+ doc.url().unwrap(),
76− doc.version(),
78+ doc.version(),
77− doc.text(),
79+ doc.text(),
78− language_id,
80+ language_id,
79− ))
81+ ))
80− .unwrap();
82+ .unwrap();
83+ }
84+
85+ let id = self.documents.insert(doc);
86+ self.documents[id].id = id;
87+ id
88+ };
89+
90+ use crate::tree::Layout;
91+ match action {
92+ Action::Replace => {
93+ self.view_mut().doc = id;
94+ // TODO: reset selection?
95+ return Ok(id);
96+ }
97+ Action::HorizontalSplit => {
98+ let view = View::new(id)?;
99+ self.tree.split(view, Layout::Horizontal);
100+ }
101+ Action::VerticalSplit => {
102+ let view = View::new(id)?;
103+ self.tree.split(view, Layout::Vertical);
104+ }
81 }
105 }
82
106
83− let id = self.documents.insert(doc);
84− self.documents[id].id = id;
85−
86− let view = View::new(id)?;
87− self.tree.insert(view);
88 self._refresh();
107 self._refresh();
89
108
90 Ok(id)
109 Ok(id)
helix-view/src/tree.rs+80−6
@@ -28,10 +28,10 @@ pub enum Content {
28}
28}
29
29
30impl Node {
30impl Node {
31− pub fn container() -> Self {
31+ pub fn container(layout: Layout) -> Self {
32 Node {
32 Node {
33 parent: ViewId::default(),
33 parent: ViewId::default(),
34− content: Content::Container(Box::new(Container::new())),
34+ content: Content::Container(Box::new(Container::new(layout))),
35 }
35 }
36 }
36 }
37
37
@@ -45,6 +45,7 @@ impl Node {
45
45
46// TODO: screen coord to container + container coordinate helpers
46// TODO: screen coord to container + container coordinate helpers
47
47
48+#[derive(PartialEq, Eq)]
48pub enum Layout {
49pub enum Layout {
49 Horizontal,
50 Horizontal,
50 Vertical,
51 Vertical,
@@ -58,9 +59,9 @@ pub struct Container {
58}
59}
59
60
60impl Container {
61impl Container {
61− pub fn new() -> Self {
62+ pub fn new(layout: Layout) -> Self {
62 Self {
63 Self {
63− layout: Layout::Horizontal,
64+ layout,
64 children: Vec::new(),
65 children: Vec::new(),
65 area: Rect::default(),
66 area: Rect::default(),
66 }
67 }
@@ -69,13 +70,13 @@ impl Container {
69
70
70impl Default for Container {
71impl Default for Container {
71 fn default() -> Self {
72 fn default() -> Self {
72− Self::new()
73+ Self::new(Layout::Horizontal)
73 }
74 }
74}
75}
75
76
76impl Tree {
77impl Tree {
77 pub fn new(area: Rect) -> Self {
78 pub fn new(area: Rect) -> Self {
78− let root = Node::container();
79+ let root = Node::container(Layout::Horizontal);
79
80
80 let mut nodes = HopSlotMap::with_key();
81 let mut nodes = HopSlotMap::with_key();
81 let root = nodes.insert(root);
82 let root = nodes.insert(root);
@@ -131,6 +132,79 @@ impl Tree {
131 node
132 node
132 }
133 }
133
134
135+ pub fn split(&mut self, view: View, layout: Layout) -> ViewId {
136+ let focus = self.focus;
137+ let parent = self.nodes[focus].parent;
138+
139+ let node = Node::view(view);
140+ let node = self.nodes.insert(node);
141+ self.get_mut(node).id = node;
142+
143+ let container = match &mut self.nodes[parent] {
144+ Node {
145+ content: Content::Container(container),
146+ ..
147+ } => container,
148+ _ => unreachable!(),
149+ };
150+
151+ if container.layout == layout {
152+ // insert node after the current item if there is children already
153+ let pos = if container.children.is_empty() {
154+ 0
155+ } else {
156+ let pos = container
157+ .children
158+ .iter()
159+ .position(|&child| child == focus)
160+ .unwrap();
161+ pos + 1
162+ };
163+ container.children.insert(pos, node);
164+ self.nodes[node].parent = parent;
165+ } else {
166+ let split = Node::container(layout);
167+ let split = self.nodes.insert(split);
168+
169+ let container = match &mut self.nodes[split] {
170+ Node {
171+ content: Content::Container(container),
172+ ..
173+ } => container,
174+ _ => unreachable!(),
175+ };
176+ container.children.push(focus);
177+ container.children.push(node);
178+ self.nodes[focus].parent = split;
179+ self.nodes[node].parent = split;
180+
181+ let container = match &mut self.nodes[parent] {
182+ Node {
183+ content: Content::Container(container),
184+ ..
185+ } => container,
186+ _ => unreachable!(),
187+ };
188+
189+ let pos = container
190+ .children
191+ .iter()
192+ .position(|&child| child == focus)
193+ .unwrap();
194+
195+ // replace focus on parent with split
196+ container.children[pos] = split;
197+ }
198+
199+ // focus the new node
200+ self.focus = node;
201+
202+ // recalculate all the sizes
203+ self.recalculate();
204+
205+ node
206+ }
207+
134 pub fn remove(&mut self, index: ViewId) {
208 pub fn remove(&mut self, index: ViewId) {
135 let mut stack = Vec::new();
209 let mut stack = Vec::new();
136
210