Skip to main content

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}