1use nvim_oxi::Dictionary;
2use nvim_oxi::api::Buffer;
3use serde::Deserialize;
4use ytil_noxi::mru_buffers::BufferKind;
5
6pub fn dict() -> Dictionary {
8 dict! {
9 "focus_vsplit": fn_from!(focus_vsplit),
10 "smart_close_buffer": fn_from!(smart_close_buffer),
11 "toggle_alternate_buffer": fn_from!(toggle_alternate_buffer),
12 }
13}
14
15pub fn create_autocmd() {
16 crate::cmds::create_lua_autocmd(
17 &["BufEnter", "WinEnter", "TermOpen"],
18 "TerminalAutoInsertMode",
19 Some(&["term://*"]),
20 "vim.cmd('startinsert')",
21 );
22}
23
24#[derive(Clone, Copy, Deserialize)]
25#[serde(rename_all(deserialize = "snake_case"))]
26pub enum SplitKind {
27 Term,
28 Buffer,
29}
30
31impl SplitKind {
32 pub const fn is_term(self) -> bool {
33 match self {
34 Self::Term => true,
35 Self::Buffer => false,
36 }
37 }
38}
39
40ytil_noxi::impl_nvim_deserializable!(SplitKind);
41
42fn focus_vsplit((split_kind, width_perc): (SplitKind, i32)) -> Option<()> {
43 let mru_bufs = ytil_noxi::mru_buffers::get().unwrap_or_default();
45 let term_bufs: Vec<Buffer> = mru_bufs.iter().filter(|b| b.is_term()).map(Buffer::from).collect();
46
47 let current_buf = nvim_oxi::api::get_current_buf();
48
49 let is_term = split_kind.is_term();
50
51 if (is_term && term_bufs.contains(¤t_buf)) || (!is_term && !term_bufs.contains(¤t_buf)) {
53 ytil_noxi::common::exec_vim_script("only", None)?;
54 return Some(());
55 }
56
57 if !is_term && let Some(float_win) = ytil_noxi::window::find_focusable_float(&["fzf"]) {
59 ytil_noxi::window::set_current(&float_win)?;
60 return Some(());
61 }
62
63 if let Some(win) = nvim_oxi::api::list_wins().find(|win| {
66 !ytil_noxi::window::is_floating(win)
67 && ytil_noxi::window::get_buffer(win)
68 .is_some_and(|buf| (is_term && term_bufs.contains(&buf)) || (!is_term && !term_bufs.contains(&buf)))
69 }) {
70 ytil_noxi::window::set_current(&win)?;
71 return Some(());
72 }
73
74 let width = compute_width(width_perc)?;
75 let (leftabove, split_new_cmd) = if is_term { ("leftabove ", "term") } else { ("", "enew") };
76
77 if let Some(mru_buf) = mru_bufs
79 .iter()
80 .find(|b| (is_term && b.is_term()) || (!is_term && matches!(b.kind, BufferKind::Path | BufferKind::NoName)))
81 {
82 ytil_noxi::common::exec_vim_script(
83 &format!("{leftabove}vsplit | vertical resize {width} | buffer {}", mru_buf.id),
84 None,
85 );
86 return Some(());
87 }
88
89 ytil_noxi::common::exec_vim_script(
91 &format!("{leftabove}vsplit | vertical resize {width} | {split_new_cmd}"),
92 None,
93 );
94
95 Some(())
96}
97
98fn toggle_alternate_buffer(_: ()) -> Option<()> {
99 let mru_bufs = ytil_noxi::mru_buffers::get().unwrap_or_default();
102
103 if let Some(target) = mru_bufs.iter().skip(1).find(|b| matches!(b.kind, BufferKind::Path)) {
104 ytil_noxi::buffer::set_current(&Buffer::from(target))?;
105 }
106
107 Some(())
108}
109
110fn smart_close_buffer(force_close: Option<bool>) -> Option<()> {
111 let mru_buffers = ytil_noxi::mru_buffers::get()?;
112
113 let Some(current_buffer) = mru_buffers.first() else {
114 return Some(());
115 };
116
117 let force = if force_close.is_some_and(std::convert::identity) {
118 "!"
119 } else {
120 ""
121 };
122
123 match current_buffer.kind {
124 BufferKind::Term | BufferKind::NoName => return Some(()),
125 BufferKind::GrugFar => {}
126 BufferKind::Path => {
127 let new_current_buffer = if let Some(mru_buffer) = mru_buffers.get(1)
128 && matches!(mru_buffer.kind, BufferKind::Path | BufferKind::NoName)
129 {
130 Buffer::from(mru_buffer.id)
131 } else {
132 ytil_noxi::buffer::create()?
133 };
134
135 ytil_noxi::buffer::set_current(&new_current_buffer)?;
136 }
137 }
138
139 ytil_noxi::common::exec_vim_script(&format!("bd{force} {}", current_buffer.id), Option::default())?;
140
141 Some(())
142}
143
144pub fn compute_width(perc: i32) -> Option<i32> {
145 let total_width: i32 = crate::vim_opts::get("columns", &crate::vim_opts::global_scope())?;
146 Some((total_width.saturating_mul(perc)) / 100)
147}