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 &[Mode::Normal],
79 "<leader>yf",
80 r#"yy:let @+ = expand("%") . ":" . line(".")<cr>"#,
81 &default_opts,
82 );
83 set(
84 &[Mode::Visual],
85 "<leader>yf",
86 concat!(
87 r#"y:let @+ = expand("%") . ":" . "#,
88 r#"min([line("'<"), line("'>")])"#,
89 r#" . (line("'<") == line("'>") ? "" : ":" . max([line("'<"), line("'>")]))<cr>"#,
90 ),
91 &default_opts,
92 );
93 set(&[Mode::Visual], "y", "ygv<esc>", &default_opts);
94 set(&[Mode::Visual], "p", r#""_dP"#, &default_opts);
95
96 set(&[Mode::Visual], ">", ">gv", &default_opts);
97 set(&[Mode::Visual], "<", "<gv", &default_opts);
98 set(&[Mode::Normal], ">", ">>", &default_opts);
99 set(&[Mode::Normal], "<", "<<", &default_opts);
100 set(&NV_MODE, "U", "<c-r>", &default_opts);
101
102 set(&NV_MODE, "<leader><leader>", ":silent :w!<cr>", &default_opts);
103 set(&NV_MODE, "<leader>x", ":bd<cr>", &default_opts);
104 set(&NV_MODE, "<leader>X", ":bd!<cr>", &default_opts);
105 set(&NV_MODE, "<leader>q", ":q<cr>", &default_opts);
106 set(&NV_MODE, "<leader>Q", ":q!<cr>", &default_opts);
107
108 set(&NV_MODE, "<c-;>", ":set wrap!<cr>", &default_opts);
109 set(&[Mode::Normal], "<esc>", r#":noh<cr>:echo""<cr>"#, &default_opts);
110}
111
112fn smart_ident_on_blank_line(_: ()) -> String {
119 apply_on_current_line_or_unwrap(|line| if line.is_empty() { r#""_cc"# } else { "i" }, "i")
120}
121
122fn smart_dd_no_yank_empty_line(_: ()) -> String {
128 apply_on_current_line_or_unwrap(
129 |line| {
130 if line.chars().all(char::is_whitespace) {
131 r#""_dd"#
132 } else {
133 "dd"
134 }
135 },
136 "dd",
137 )
138}
139
140fn visual_esc(_: ()) -> String {
143 let current_line: i64 = nvim_oxi::api::call_function("line", (".",))
144 .inspect_err(|err| {
145 ytil_noxi::notify::error(format!("error getting current line | error={err:#?}"));
146 })
147 .unwrap_or(0);
148 let visual_line: i64 = nvim_oxi::api::call_function("line", ("v",))
149 .inspect_err(|err| {
150 ytil_noxi::notify::error(format!("error getting visual line | error={err:#?}"));
151 })
152 .unwrap_or(0);
153 format!(
154 ":<c-u>'{}<cr>{}",
155 if current_line < visual_line { "<" } else { ">" },
156 NORMAL_ESC
157 )
158}
159
160fn apply_on_current_line_or_unwrap<'a, F: FnOnce(String) -> &'a str>(fun: F, default: &'a str) -> String {
166 ytil_noxi::buffer::get_current_line().map_or(default, fun).to_string()
167}