nvrim/plugins/
gdiff.rs

1//! Exposes a dictionary with a `get_hunks` function that fetches git diff output, extracts paths and line numbers
2//! of changed hunks, and presents a selection UI to jump to specific diff locations in buffers.
3
4use nvim_oxi::Dictionary;
5use nvim_oxi::api::Buffer;
6use ytil_noxi::vim_ui_select::QuickfixConfig;
7
8/// [`Dictionary`] of git diff helpers.
9pub fn dict() -> Dictionary {
10    dict! {
11        "get_hunks": fn_from!(get_hunks),
12    }
13}
14
15/// Opens the selected git diff file and line number.
16///
17/// Fetches git diff output, parses paths and line numbers of changed hunks, displays them in a Neovim selection UI, and
18/// on selection opens the buffer at the specified line.
19fn get_hunks(only_current_buffer: Option<bool>) {
20    let current_buffer_path = ytil_noxi::buffer::get_absolute_path(
21        only_current_buffer
22            .is_some_and(std::convert::identity)
23            .then(Buffer::current)
24            .as_ref(),
25    );
26
27    let Ok(raw_output) = ytil_git::diff::get_raw(current_buffer_path.as_deref()).inspect_err(|err| {
28        ytil_noxi::notify::error(format!("error getting git diff raw output | error={err:#?}"));
29    }) else {
30        return;
31    };
32
33    let Ok(hunks) = ytil_git::diff::get_hunks(&raw_output)
34        .map(|x| {
35            x.into_iter()
36                .map(|(path, lnum)| (path.to_owned(), lnum))
37                .collect::<Vec<_>>()
38        })
39        .inspect_err(|err| {
40            ytil_noxi::notify::error(format!("error getting git diff hunks | error={err:#?}"));
41        })
42    else {
43        return;
44    };
45
46    let mut all_items = vec![];
47    for (path, lnum) in &hunks {
48        let Ok(lnum) = i64::try_from(*lnum) else {
49            ytil_noxi::notify::error(format!("error converting hunk lnum to i64 | lnum={lnum}"));
50            return;
51        };
52        all_items.push((path.clone(), lnum));
53    }
54
55    let quickfix = QuickfixConfig {
56        trigger_value: "All to quickfix".into(),
57        all_items,
58    };
59
60    let mut displayable_hunks: Vec<_> = hunks.iter().map(|(path, lnum)| format!("{path}:{lnum}")).collect();
61    displayable_hunks.push(quickfix.trigger_value.clone());
62
63    let callback = {
64        move |choice_idx: usize| {
65            let Some((path, lnum)) = hunks.get(choice_idx) else {
66                return;
67            };
68            let _ = ytil_noxi::buffer::open(path, Some(*lnum), None).inspect_err(|err| {
69                ytil_noxi::notify::error(format!(
70                    "error opening buffer | path={path:?} lnum={lnum} error={err:#?}"
71                ));
72            });
73        }
74    };
75
76    if let Err(err) = ytil_noxi::vim_ui_select::open(
77        displayable_hunks,
78        &[("prompt", "Git diff hunks ")],
79        callback,
80        Some(quickfix),
81    ) {
82        ytil_noxi::notify::error(format!("error opening selected path | error={err:#?}"));
83    }
84}