nvrim/plugins/caseconv.rs
1//! Text conversions helpers for the current Visual selection.
2//!
3//! Provides a namespaced [`Dictionary`] exposing selection conversion
4//! functionality (currently only case conversion via [`convert_case`]).
5
6use std::fmt::Display;
7use std::fmt::Formatter;
8
9use convert_case::Case;
10use convert_case::Casing;
11use nvim_oxi::Dictionary;
12use rootcause::Report;
13
14/// Namespaced dictionary of case conversion helpers.
15///
16/// Entries:
17/// - `"convert_selection"`: wraps [`convert_selection`] and converts the active Visual selection to a user‑selected
18/// [`Case`].
19pub fn dict() -> Dictionary {
20 dict! {
21 "convert_selection": fn_from!(convert_selection),
22 }
23}
24
25/// Converts the current visual selection to a user-chosen case variant.
26///
27/// Prompts the user (via [`ytil_noxi::vim_ui_select::open`]) to select a case conversion
28/// option, then applies the conversion to all selected lines in place.
29///
30/// Returns early if:
31/// - No active Visual selection is detected.
32/// - The user cancels the prompt.
33/// - Writing the converted text back to the buffer fails (an error is reported via [`ytil_noxi::notify::error`]).
34///
35/// # Errors
36/// Errors from [`ytil_noxi::vim_ui_select::open`] are reported via [`ytil_noxi::notify::error`]
37/// using the direct display representation of [`rootcause::Report`].
38///
39/// # Notes
40/// Blockwise selections are treated as a contiguous span (not a rectangle).
41fn convert_selection(_: ()) {
42 let Some(selection) = ytil_noxi::visual_selection::get(()) else {
43 return;
44 };
45
46 let cases = Case::all_cases();
47
48 let callback = move |choice_idx| {
49 cases.get(choice_idx).map(|case| {
50 let converted_lines = selection
51 .lines()
52 .iter()
53 .map(|line| line.as_str().to_case(*case))
54 .collect::<Vec<_>>();
55 ytil_noxi::buffer::replace_text_and_notify_if_error(&selection, converted_lines);
56 Ok::<(), Report>(())
57 });
58 };
59
60 if let Err(err) = ytil_noxi::vim_ui_select::open(
61 cases.iter().map(DisplayableCase),
62 &[("prompt", "Convert selection to case ")],
63 callback,
64 None,
65 ) {
66 ytil_noxi::notify::error(format!("error converting selection to case | error={err:#?}"));
67 }
68}
69
70/// Newtype wrapper to make [`Case`] displayable using its [`Debug`] representation.
71struct DisplayableCase<'a>(&'a Case<'a>);
72
73impl Display for DisplayableCase<'_> {
74 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
75 write!(f, "{:?}", self.0)
76 }
77}