Skip to content

Commit 4399cd7

Browse files
joskeclaude
andauthored
Add wrap-around navigation setting (#26)
* Add wrap-around navigation setting (default off) Chunk/conflict navigation no longer wraps around by default. A new "Wrap around navigation" toggle in Preferences lets users opt-in to the previous wrap behavior. Closes #10 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Fix UI test: don't hardcode chunk count The fixture files produce 3 chunks, not 2. Parse the label dynamically instead. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Fix Makefile: build release binary before UI tests The test-release target was running pytest against target/release/mergers without rebuilding it first, so UI tests could run against a stale binary. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Fix merge view cursor starting at end of buffer GTK's set_text leaves the cursor at the end, so conflict/chunk navigation couldn't find anything forward on first use. Place cursors at start after setting text, matching diff_view behavior. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Add nav button sensitivity at chunk boundaries When wrap-around navigation is off, prev/next chunk buttons now become insensitive when there is no chunk to navigate to in that direction. Buttons also start insensitive until the initial async diff completes. Applies to diff view (chunk nav), merge view (chunk nav + conflict nav). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Update chunk/conflict label on cursor movement When the user clicks or moves the cursor into a change region, the chunk label now shows "Change X of Y" and nav button sensitivity updates accordingly. In merge view, the conflict label also tracks cursor position on conflict marker lines. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Track conflict label by cursor position within block The conflict label now shows "Conflict X of Y" when the cursor is anywhere inside a conflict block (between <<<<<<< and >>>>>>>), not just on the exact marker line. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * bump to 0.5.1 --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 5ab6fd6 commit 4399cd7

11 files changed

Lines changed: 902 additions & 53 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "mergers"
3-
version = "0.5.0"
3+
version = "0.5.1"
44
edition = "2024"
55
description = "A visual diff and merge tool for files and directories"
66
license = "GPL-2.0"

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ $(VENV): tests/ui_integration/requirements.txt
1515
@touch $(VENV)
1616

1717
test-release: $(VENV)
18+
cargo build --release
1819
xvfb-run -a cargo test
1920
xvfb-run -a $(PYTEST) tests/ui_integration/ -v
2021

src/settings.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use std::path::PathBuf;
33

44
#[derive(Debug, Clone, Serialize, Deserialize)]
55
#[serde(default)]
6+
#[allow(clippy::struct_excessive_bools)]
67
pub struct Settings {
78
pub font: String,
89
pub style_scheme: String,
@@ -11,6 +12,7 @@ pub struct Settings {
1112
pub tab_width: u32,
1213
pub highlight_current_line: bool,
1314
pub hide_hidden_files: bool,
15+
pub wrap_around_navigation: bool,
1416
pub dir_filters: Vec<String>,
1517
}
1618

@@ -24,6 +26,7 @@ impl Default for Settings {
2426
tab_width: 4,
2527
highlight_current_line: true,
2628
hide_hidden_files: true,
29+
wrap_around_navigation: false,
2730
dir_filters: vec![
2831
".git".into(),
2932
".svn".into(),
@@ -96,6 +99,7 @@ mod tests {
9699
assert!(s.show_line_numbers);
97100
assert_eq!(s.tab_width, 4);
98101
assert!(s.dir_filters.contains(&".git".to_string()));
102+
assert!(!s.wrap_around_navigation);
99103
}
100104

101105
#[test]
@@ -108,6 +112,10 @@ mod tests {
108112
assert_eq!(original.tab_width, parsed.tab_width);
109113
assert_eq!(original.show_line_numbers, parsed.show_line_numbers);
110114
assert_eq!(original.wrap_mode, parsed.wrap_mode);
115+
assert_eq!(
116+
original.wrap_around_navigation,
117+
parsed.wrap_around_navigation
118+
);
111119
assert_eq!(original.dir_filters, parsed.dir_filters);
112120
}
113121

src/ui/common.rs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1527,6 +1527,7 @@ pub(super) fn navigate_chunk(
15271527
right_buf: &TextBuffer,
15281528
right_scroll: &ScrolledWindow,
15291529
active_tv: &TextView,
1530+
wrap: bool,
15301531
) {
15311532
let cursor_line = cursor_line_from_view(active_tv);
15321533
let side = if active_tv == right_tv {
@@ -1535,7 +1536,7 @@ pub(super) fn navigate_chunk(
15351536
Side::A
15361537
};
15371538

1538-
if let Some(idx) = diff_state::find_next_chunk(chunks, cursor_line, direction, side) {
1539+
if let Some(idx) = diff_state::find_next_chunk(chunks, cursor_line, direction, side, wrap) {
15391540
current_chunk.set(Some(idx));
15401541
let chunk = &chunks[idx];
15411542
scroll_to_line(left_tv, left_buf, chunk.start_a, left_scroll);
@@ -1586,6 +1587,26 @@ pub(super) fn update_chunk_label(label: &Label, chunks: &[DiffChunk], current: O
15861587
label.set_label(&diff_state::format_chunk_label(chunks, current));
15871588
}
15881589

1590+
/// Update sensitivity of prev/next chunk nav buttons based on cursor position.
1591+
pub(super) fn update_chunk_nav_sensitivity(
1592+
prev_btn: &Button,
1593+
next_btn: &Button,
1594+
chunks: &[DiffChunk],
1595+
active_tv: &TextView,
1596+
right_tv: &TextView,
1597+
wrap: bool,
1598+
) {
1599+
let cursor_line = cursor_line_from_view(active_tv);
1600+
let side = if active_tv == right_tv {
1601+
Side::B
1602+
} else {
1603+
Side::A
1604+
};
1605+
let (prev, next) = diff_state::chunk_nav_sensitivity(chunks, cursor_line, side, wrap);
1606+
prev_btn.set_sensitive(prev);
1607+
next_btn.set_sensitive(next);
1608+
}
1609+
15891610
// ─── Scroll synchronization ───────────────────────────────────────────────
15901611

15911612
pub(super) fn setup_scroll_sync(

0 commit comments

Comments
 (0)