1use ytil_noxi::Dictionary;
8use ytil_noxi::api::opts::SetKeymapOpts;
9use ytil_noxi::api::opts::SetKeymapOptsBuilder;
10use ytil_noxi::api::types::Mode;
11
12const NV_MODE: [Mode; 2] = [Mode::Normal, Mode::Visual];
13const NVOP_MODE: [Mode; 1] = [Mode::NormalVisualOperator];
14
15pub const NORMAL_ESC: &str = r#":noh<cr>:echo""<cr>"#;
17
18pub fn dict() -> Dictionary {
20 dict! {
21 "set_all": fn_from!(set_all),
22 "smart_ident_on_blank_line": fn_from!(smart_ident_on_blank_line),
23 "smart_dd_no_yank_empty_line": fn_from!(smart_dd_no_yank_empty_line),
24 "normal_esc": NORMAL_ESC,
25 "visual_esc": fn_from!(visual_esc),
26 }
27}
28
29pub fn set(modes: &[Mode], lhs: &str, rhs: &str, opts: &SetKeymapOpts) {
33 for mode in modes {
34 if let Err(err) = nvim_oxi::api::set_keymap(*mode, lhs, rhs, opts) {
35 ytil_noxi::notify::error(format!(
36 "cannot set keymap | mode={mode:#?} lhs={lhs} rhs={rhs} opts={opts:#?} error={err:#?}"
37 ));
38 }
39 }
40}
41
42pub fn default_opts() -> SetKeymapOpts {
47 SetKeymapOptsBuilder::default().silent(true).noremap(true).build()
48}
49
50fn set_all(_: ()) {
57 let default_opts = default_opts();
58
59 ytil_noxi::common::set_g_var("mapleader", " ");
60 ytil_noxi::common::set_g_var("maplocalleader", " ");
61
62 set(&[Mode::Terminal], "<Esc>", "<c-\\><c-n>", &default_opts);
63 set(&[Mode::Insert], "<c-a>", "<esc>^i", &default_opts);
64 set(&[Mode::Normal], "<c-a>", "^i", &default_opts);
65 set(&[Mode::Insert], "<c-e>", "<end>", &default_opts);
66 set(&[Mode::Normal], "<c-e>", "$a", &default_opts);
67
68 set(&NVOP_MODE, "gn", ":bn<cr>", &default_opts);
69 set(&NVOP_MODE, "gp", ":bp<cr>", &default_opts);
70 set(&NV_MODE, "gh", "0", &default_opts);
71 set(&NV_MODE, "gl", "$", &default_opts);
72 set(&NV_MODE, "gs", "_", &default_opts);
73
74 set(&NV_MODE, "x", r#""_x"#, &default_opts);
75 set(&NV_MODE, "X", r#""_X"#, &default_opts);
76
77 set(
78 &NV_MODE,
79 "<leader>yf",
80 r#":let @+ = expand("%") . ":" . line(".")<cr>"#,
81 &default_opts,
82 );
83 set(&[Mode::Visual], "y", "ygv<esc>", &default_opts);
84 set(&[Mode::Visual], "p", r#""_dP"#, &default_opts);
85
86 set(&[Mode::Visual], ">", ">gv", &default_opts);
87 set(&[Mode::Visual], "<", "<gv", &default_opts);
88 set(&[Mode::Normal], ">", ">>", &default_opts);
89 set(&[Mode::Normal], "<", "<<", &default_opts);
90 set(&NV_MODE, "U", "<c-r>", &default_opts);
91
92 set(&NV_MODE, "<leader><leader>", ":silent :w!<cr>", &default_opts);
93 set(&NV_MODE, "<leader>x", ":bd<cr>", &default_opts);
94 set(&NV_MODE, "<leader>X", ":bd!<cr>", &default_opts);
95 set(&NV_MODE, "<leader>q", ":q<cr>", &default_opts);
96 set(&NV_MODE, "<leader>Q", ":q!<cr>", &default_opts);
97
98 set(&NV_MODE, "<c-;>", ":set wrap!<cr>", &default_opts);
99 set(&[Mode::Normal], "<esc>", r#":noh<cr>:echo""<cr>"#, &default_opts);
100}
101
102fn smart_ident_on_blank_line(_: ()) -> String {
109 apply_on_current_line_or_unwrap(|line| if line.is_empty() { r#""_cc"# } else { "i" }, "i")
110}
111
112fn smart_dd_no_yank_empty_line(_: ()) -> String {
118 apply_on_current_line_or_unwrap(
119 |line| {
120 if line.chars().all(char::is_whitespace) {
121 r#""_dd"#
122 } else {
123 "dd"
124 }
125 },
126 "dd",
127 )
128}
129
130fn visual_esc(_: ()) -> String {
133 let current_line: i64 = nvim_oxi::api::call_function("line", (".",))
134 .inspect_err(|err| {
135 ytil_noxi::notify::error(format!("error getting current line | error={err:#?}"));
136 })
137 .unwrap_or(0);
138 let visual_line: i64 = nvim_oxi::api::call_function("line", ("v",))
139 .inspect_err(|err| {
140 ytil_noxi::notify::error(format!("error getting visual line | error={err:#?}"));
141 })
142 .unwrap_or(0);
143 format!(
144 ":<c-u>'{}<cr>{}",
145 if current_line < visual_line { "<" } else { ">" },
146 NORMAL_ESC
147 )
148}
149
150fn apply_on_current_line_or_unwrap<'a, F: FnOnce(String) -> &'a str>(fun: F, default: &'a str) -> String {
156 ytil_noxi::buffer::get_current_line().map_or(default, fun).to_string()
157}