use strop_core::selection::SelectionSet;
use super::Editor;
#[derive(Debug, Clone)]
pub struct Pane {
pub doc: strop_core::id::DocumentId,
pub sels: SelectionSet,
pub view_top: usize,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LayoutDir {
Row,
Column,
}
impl Editor {
#[inline]
pub fn view(&self) -> &Pane {
&self.panes[self.active_pane]
}
#[inline]
pub fn view_mut(&mut self) -> &mut Pane {
&mut self.panes[self.active_pane]
}
pub(crate) fn split(&mut self, vertical: bool, path: Option<&str>) {
let view = self.view().clone();
let doc = if let Some(p) = path {
match self.open_document(p) {
Ok(id) => id,
Err(e) => {
self.message = format!("open {p}: {e}");
return;
}
}
} else {
view.doc
};
self.panes.push(Pane {
doc,
sels: view.sels,
view_top: view.view_top,
});
self.layout = if vertical {
LayoutDir::Row
} else {
LayoutDir::Column
};
self.active_pane = self.panes.len() - 1;
self.discover_git();
self.lsp_maybe_attach();
}
pub(crate) fn close_pane_or_buffer(&mut self, force: bool) {
if self.panes.len() > 1 {
self.panes.remove(self.active_pane);
self.active_pane = self.active_pane.min(self.panes.len() - 1);
self.discover_git();
} else {
self.close_buffer(force);
}
}
pub(crate) fn pane_move(&mut self, key: char) {
let n = self.panes.len();
if n < 2 {
self.message = "no other pane".into();
return;
}
let next = match (self.layout, key) {
(LayoutDir::Row, 'h') => self.active_pane.checked_sub(1).unwrap_or(n - 1),
(LayoutDir::Row, 'l') => (self.active_pane + 1) % n,
(LayoutDir::Column, 'k') => self.active_pane.checked_sub(1).unwrap_or(n - 1),
(LayoutDir::Column, 'j') => (self.active_pane + 1) % n,
(_, 'w') => (self.active_pane + 1) % n,
_ => return,
};
self.active_pane = next; self.discover_git();
self.clamp_cursor();
}
}
impl Editor {
pub(crate) fn pane_move_pub(&mut self, key: char) {
self.pane_move(key);
}
pub(crate) fn split_pub(&mut self, key: char) {
self.split(key == 'v', None);
}
pub(crate) fn pane_close_pub(&mut self) {
self.close_pane_or_buffer(false);
}
}
#[cfg(test)]
mod tests {
use super::*;
use strop_core::Buffer;
#[test]
fn vsplit_shares_buffer_and_navigates() {
let dir = tempfile::tempdir().unwrap();
let a = dir.path().join("vsplit-a.rs");
std::fs::write(&a, "fn a() {}\nfn b() {}\n").unwrap();
let mut e = Editor::new(Buffer::open(a.to_str().unwrap()).unwrap());
e.feed_text("j"); e.feed_text(":vs<cr>");
assert_eq!(e.panes.len(), 2);
assert_eq!(e.active_pane, 1);
e.feed_text("gg");
e.feed(crate::editor::Key::CtrlW);
e.feed(crate::editor::Key::Char('h'));
assert_eq!(e.active_pane, 0);
assert_eq!(e.buf().line_of(e.head()), 1, "pane 1 kept its own cursor");
e.feed_text(":q<cr>");
assert_eq!(e.panes.len(), 1);
assert_eq!(e.docs.len(), 1);
}
#[test]
fn split_with_path_opens_other_file() {
let dir = tempfile::tempdir().unwrap();
let a = dir.path().join("split-a.rs");
let b = dir.path().join("split-b.rs");
std::fs::write(&a, "fn a() {}\n").unwrap();
std::fs::write(&b, "fn b() {}\n").unwrap();
let mut e = Editor::new(Buffer::open(a.to_str().unwrap()).unwrap());
e.feed_text(&format!(":vs {}<cr>", b.display()));
assert_eq!(e.panes.len(), 2);
assert_eq!(e.buf().path.as_deref(), b.to_str());
e.feed(crate::editor::Key::CtrlW);
e.feed(crate::editor::Key::Char('h'));
assert_eq!(e.buf().path.as_deref(), a.to_str());
}
}