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