Skip to main content

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