use hjkl_buffer::Buffer;
use hjkl_engine::{Editor, Host, Options};
use std::path::PathBuf;
use super::{App, DiskState, STATUS_LINE_HEIGHT};
use crate::host::TuiHost;
impl App {
pub(crate) fn switch_to(&mut self, idx: usize) {
let current_slot = self.focused_slot_idx();
if idx != current_slot {
self.prev_active = Some(current_slot);
let regs = self.slots[current_slot].editor.registers().clone();
*self.slots[idx].editor.registers_mut() = regs;
}
let fw = self.focused_window();
self.windows[fw].as_mut().expect("focused_window open").slot = idx;
if let Ok(size) = crossterm::terminal::size() {
let vp = self.active_mut().editor.host_mut().viewport_mut();
vp.width = size.0;
vp.height = size.1.saturating_sub(STATUS_LINE_HEIGHT);
}
let buffer_id = self.active().buffer_id;
let (vp_top, vp_height) = {
let vp = self.active().editor.host().viewport();
(vp.top_row, vp.height as usize)
};
if let Some(out) =
self.syntax
.preview_render(buffer_id, self.active().editor.buffer(), vp_top, vp_height)
{
self.active_mut()
.editor
.install_ratatui_syntax_spans(out.spans);
}
self.active_mut().last_recompute_key = None;
self.recompute_and_install();
self.refresh_git_signs_force();
}
pub(crate) fn buffer_next(&mut self) {
if !self.require_multi_buffer() {
return;
}
let next = (self.focused_slot_idx() + 1) % self.slots.len();
self.switch_to(next);
}
pub(crate) fn buffer_prev(&mut self) {
if !self.require_multi_buffer() {
return;
}
let prev = (self.focused_slot_idx() + self.slots.len() - 1) % self.slots.len();
self.switch_to(prev);
}
pub(crate) fn buffer_alt(&mut self) {
if !self.require_multi_buffer() {
return;
}
match self.prev_active {
Some(i) if i < self.slots.len() => {
self.switch_to(i);
}
_ => {
self.status_message = Some("no alternate buffer".into());
}
}
}
pub(crate) fn buffer_delete(&mut self, force: bool) {
if !force && self.active().dirty {
self.status_message =
Some("E89: No write since last change (add ! to override)".into());
return;
}
let active_slot = self.focused_slot_idx();
if self.slots.len() == 1 {
self.lsp_detach_buffer(active_slot);
let old_id = self.active().buffer_id;
self.syntax.forget(old_id);
let new_id = self.next_buffer_id;
self.next_buffer_id += 1;
let host = TuiHost::new();
let mut editor = Editor::new(Buffer::new(), host, Options::default());
if let Ok(size) = crossterm::terminal::size() {
let vp = editor.host_mut().viewport_mut();
vp.width = size.0;
vp.height = size.1.saturating_sub(STATUS_LINE_HEIGHT);
}
let _ = editor.take_content_edits();
let _ = editor.take_content_reset();
let slot = &mut self.slots[0];
slot.buffer_id = new_id;
slot.editor = editor;
slot.filename = None;
slot.dirty = false;
slot.is_new_file = false;
slot.is_untracked = false;
slot.diag_signs.clear();
slot.git_signs.clear();
slot.last_git_dirty_gen = None;
slot.last_recompute_key = None;
slot.saved_hash = 0;
slot.saved_len = 0;
slot.disk_mtime = None;
slot.disk_len = None;
slot.disk_state = DiskState::Synced;
slot.snapshot_saved();
for win in self.windows.iter_mut().flatten() {
win.slot = 0;
}
self.status_message = Some("buffer closed (replaced with [No Name])".into());
return;
}
self.lsp_detach_buffer(active_slot);
let removed = self.slots.remove(active_slot);
self.syntax.forget(removed.buffer_id);
let slot_count = self.slots.len();
for win in self.windows.iter_mut().flatten() {
if win.slot == active_slot {
win.slot = if active_slot > 0 { active_slot - 1 } else { 0 };
} else if win.slot > active_slot {
win.slot -= 1;
}
win.slot = win.slot.min(slot_count.saturating_sub(1));
}
let target = self.focused_slot_idx();
self.switch_to(target);
self.prev_active = None;
let name = removed
.filename
.as_ref()
.map(|p| p.display().to_string())
.unwrap_or_else(|| "[No Name]".into());
self.status_message = Some(format!("buffer closed: \"{name}\""));
}
pub(crate) fn require_multi_buffer(&mut self) -> bool {
if self.slots.len() <= 1 {
self.status_message = Some("only one buffer open".into());
return false;
}
true
}
pub(crate) fn list_buffers(&self) -> String {
let active_slot = self.focused_slot_idx();
let mut parts = Vec::with_capacity(self.slots.len());
for (i, slot) in self.slots.iter().enumerate() {
let active = if i == active_slot { '%' } else { ' ' };
let modf = if slot.dirty { '+' } else { ' ' };
let name = slot
.filename
.as_ref()
.map(|p| p.display().to_string())
.unwrap_or_else(|| "[No Name]".into());
parts.push(format!("{}:{active}{modf} \"{name}\"", i + 1));
}
parts.join(" | ")
}
pub(crate) fn open_new_slot(&mut self, path: PathBuf) -> Result<usize, String> {
let buffer_id = self.next_buffer_id;
self.next_buffer_id += 1;
let slot = super::build_slot(&mut self.syntax, buffer_id, Some(path), &self.config)?;
self.slots.push(slot);
let idx = self.slots.len() - 1;
self.lsp_attach_buffer(idx);
Ok(idx)
}
}