feat: add configurable diff modes and split orientation
This commit is contained in:
+185
-1
@@ -1,6 +1,6 @@
|
||||
use crate::chrome::{ChromeLine, PopupConfig, validate_name};
|
||||
use crate::clipboard::{ClipboardMode, SystemClipboard};
|
||||
use crate::diff::{DiffState, Focus, Operation, Outcome, Phase, RevisionTarget};
|
||||
use crate::diff::{DiffState, Focus, Operation, Outcome, Phase, RevisionTarget, ViewMode};
|
||||
use crate::files::{DockSide, FilePicker, FilePresentation, FileView};
|
||||
use crate::panes::{PaneDirection, PaneRect, PaneTree, SplitAxis, directional_neighbor};
|
||||
use crate::plugin::{
|
||||
@@ -703,6 +703,18 @@ core_actions![
|
||||
true
|
||||
),
|
||||
("core.diff", Action::Diff, &[Mode::Normal], true),
|
||||
(
|
||||
"core.diff.view.toggle",
|
||||
Action::DiffViewToggle,
|
||||
&[Mode::Normal],
|
||||
true,
|
||||
),
|
||||
(
|
||||
"core.diff.orientation.toggle",
|
||||
Action::DiffOrientationToggle,
|
||||
&[Mode::Normal],
|
||||
true,
|
||||
),
|
||||
(
|
||||
"core.word_wrap.toggle",
|
||||
Action::WordWrapToggle,
|
||||
@@ -1652,6 +1664,7 @@ pub struct Editor {
|
||||
diff_bound: Option<(u64, usize, Focus, usize)>,
|
||||
diff_return_buffer: Option<usize>,
|
||||
diff_context_options: (usize, usize),
|
||||
diff_display_options: (ViewMode, SplitAxis),
|
||||
next_diff_request: u64,
|
||||
outstanding_diff_request: Option<u64>,
|
||||
pending_lsp_navigation: Option<PendingLspNavigation>,
|
||||
@@ -1770,6 +1783,7 @@ impl Editor {
|
||||
diff_bound: None,
|
||||
diff_return_buffer: None,
|
||||
diff_context_options: (10, 15),
|
||||
diff_display_options: (ViewMode::Split, SplitAxis::Vertical),
|
||||
next_diff_request: 0,
|
||||
outstanding_diff_request: None,
|
||||
pending_lsp_navigation: None,
|
||||
@@ -1803,6 +1817,8 @@ impl Editor {
|
||||
("normal", "leader b p", "core.buffer_previous"),
|
||||
("normal", "leader b l", "core.buffers"),
|
||||
("normal", "leader b d", "core.buffer_delete"),
|
||||
("normal", "leader v", "core.diff.view.toggle"),
|
||||
("normal", "leader s", "core.diff.orientation.toggle"),
|
||||
] {
|
||||
editor
|
||||
.bind(mode, key, Some(target.into()))
|
||||
@@ -2445,6 +2461,10 @@ impl Editor {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_diff_display_options(&mut self, view_mode: ViewMode, split_axis: SplitAxis) {
|
||||
self.diff_display_options = (view_mode, split_axis);
|
||||
}
|
||||
|
||||
pub(crate) fn take_diff_request(&mut self) -> Option<(u64, Operation)> {
|
||||
let mut operation = self
|
||||
.diff
|
||||
@@ -2576,6 +2596,8 @@ impl Editor {
|
||||
self.outstanding_diff_request = None;
|
||||
let mut diff = DiffState::new();
|
||||
diff.word_wrap = self.active.view.word_wrap;
|
||||
diff.view_mode = self.diff_display_options.0;
|
||||
diff.split_axis = self.diff_display_options.1;
|
||||
diff.set_context_options(self.diff_context_options.0, self.diff_context_options.1);
|
||||
self.diff = Some(diff);
|
||||
}
|
||||
@@ -3122,6 +3144,8 @@ impl Editor {
|
||||
| Action::FilePicker
|
||||
| Action::FileExplorer
|
||||
| Action::DiffClose
|
||||
| Action::DiffViewToggle
|
||||
| Action::DiffOrientationToggle
|
||||
| Action::WordWrapToggle
|
||||
| Action::Quit
|
||||
| Action::ForceQuit
|
||||
@@ -5881,6 +5905,8 @@ impl Editor {
|
||||
| Action::BufferForceDelete
|
||||
| Action::Diff
|
||||
| Action::DiffClose
|
||||
| Action::DiffViewToggle
|
||||
| Action::DiffOrientationToggle
|
||||
| Action::WordWrapToggle
|
||||
| Action::LspDefinition
|
||||
| Action::LspHover
|
||||
@@ -6685,6 +6711,44 @@ impl Editor {
|
||||
}
|
||||
return None;
|
||||
}
|
||||
if matches!(
|
||||
entry.action,
|
||||
Action::DiffViewToggle | Action::DiffOrientationToggle
|
||||
) {
|
||||
let name = entry.ex.expect("diff display action has an Ex command");
|
||||
if !args.is_empty() {
|
||||
self.set_message(format!("The {name} command takes no arguments"));
|
||||
return None;
|
||||
}
|
||||
if let Some(owner) = self.diff_owner.filter(|owner| *owner != self.pane_id) {
|
||||
return self.with_pane(owner, |editor| editor.execute_action(entry, args));
|
||||
}
|
||||
self.count = None;
|
||||
self.clear_sequence();
|
||||
self.palette = None;
|
||||
if self.active.view.mode == Mode::Command {
|
||||
self.active.view.command_line.clear();
|
||||
self.active.view.mode = Mode::Normal;
|
||||
}
|
||||
let Some(diff) = self.diff.as_mut().filter(|diff| diff.phase == Phase::View) else {
|
||||
self.set_message("No comparison is open");
|
||||
return None;
|
||||
};
|
||||
if entry.action == Action::DiffViewToggle {
|
||||
diff.set_view_mode(match diff.view_mode {
|
||||
ViewMode::Split => ViewMode::Unified,
|
||||
ViewMode::Unified => ViewMode::Split,
|
||||
});
|
||||
} else if diff.view_mode == ViewMode::Split {
|
||||
diff.split_axis = match diff.split_axis {
|
||||
SplitAxis::Vertical => SplitAxis::Horizontal,
|
||||
SplitAxis::Horizontal => SplitAxis::Vertical,
|
||||
};
|
||||
} else {
|
||||
self.set_message("Diff orientation applies only to split view");
|
||||
}
|
||||
return None;
|
||||
}
|
||||
if entry.action == Action::WordWrapToggle {
|
||||
if !args.is_empty() {
|
||||
self.set_message("The word_wrap.toggle command takes no arguments");
|
||||
@@ -12844,6 +12908,126 @@ mod tests {
|
||||
assert_eq!(editor.lines(), ["beta"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn diff_display_defaults_tabs_and_remaps_preserve_native_snapshot_carets() {
|
||||
let directory = Directory::new();
|
||||
let mut editor = editor();
|
||||
editor.execute("diff.view.toggle");
|
||||
assert_eq!(editor.message(), "No comparison is open");
|
||||
editor.set_diff_display_options(ViewMode::Unified, SplitAxis::Horizontal);
|
||||
editor.execute("diff");
|
||||
type_text(&mut editor, " v");
|
||||
assert_eq!(editor.diff().unwrap().query, " v");
|
||||
assert_eq!(editor.diff().unwrap().view_mode, ViewMode::Unified);
|
||||
historical_diff(
|
||||
&mut editor,
|
||||
&directory.0,
|
||||
vec!["old one".into(), "old two".into()],
|
||||
vec!["new one".into(), "new two".into()],
|
||||
);
|
||||
let first_tab = editor.tab_id();
|
||||
let buffer = editor.buffer_id();
|
||||
editor.handle(Input::Char('j'));
|
||||
let caret = editor.cursor();
|
||||
let text = editor.lines().to_vec();
|
||||
assert!(!editor.diff_editing());
|
||||
assert_eq!(editor.diff().unwrap().view_mode, ViewMode::Unified);
|
||||
editor.execute("diff.orientation.toggle");
|
||||
assert_eq!(editor.diff().unwrap().split_axis, SplitAxis::Horizontal);
|
||||
editor
|
||||
.bind("normal", "Q", Some("core.diff.view.toggle".into()))
|
||||
.unwrap();
|
||||
assert!(
|
||||
editor
|
||||
.bind("insert", "Q", Some("core.diff.view.toggle".into()))
|
||||
.is_err()
|
||||
);
|
||||
editor.handle(Input::Char('Q'));
|
||||
assert_eq!(editor.diff().unwrap().view_mode, ViewMode::Split);
|
||||
editor
|
||||
.bind("normal", "W", Some("core.diff.orientation.toggle".into()))
|
||||
.unwrap();
|
||||
editor.handle(Input::Char('W'));
|
||||
assert_eq!(editor.diff().unwrap().split_axis, SplitAxis::Vertical);
|
||||
type_text(&mut editor, " v");
|
||||
assert_eq!(editor.diff().unwrap().view_mode, ViewMode::Unified);
|
||||
assert_eq!(editor.buffer_id(), buffer);
|
||||
assert_eq!(editor.cursor(), caret);
|
||||
assert_eq!(editor.lines(), text);
|
||||
editor.set_diff_display_options(ViewMode::Split, SplitAxis::Horizontal);
|
||||
assert_eq!(editor.diff().unwrap().view_mode, ViewMode::Unified);
|
||||
editor.handle(Input::Ctrl('p'));
|
||||
type_text(&mut editor, "diff.view.toggle");
|
||||
assert!(
|
||||
editor
|
||||
.palette()
|
||||
.unwrap()
|
||||
.entries
|
||||
.iter()
|
||||
.any(|entry| entry.name == "core.diff.view.toggle" && entry.enabled)
|
||||
);
|
||||
editor.handle(Input::Enter);
|
||||
assert_eq!(editor.diff().unwrap().view_mode, ViewMode::Split);
|
||||
editor.execute("tab.new");
|
||||
historical_diff(
|
||||
&mut editor,
|
||||
&directory.0,
|
||||
vec!["old".into()],
|
||||
vec!["new".into()],
|
||||
);
|
||||
assert_eq!(editor.diff().unwrap().view_mode, ViewMode::Split);
|
||||
assert_eq!(editor.diff().unwrap().split_axis, SplitAxis::Horizontal);
|
||||
editor.execute("tab.previous");
|
||||
assert_eq!(editor.tab_id(), first_tab);
|
||||
assert_eq!(editor.buffer_id(), buffer);
|
||||
assert_eq!(editor.cursor(), caret);
|
||||
assert_eq!(editor.diff().unwrap().split_axis, SplitAxis::Vertical);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn diff_display_actions_keep_working_edits_files_and_filter_input_native() {
|
||||
let directory = Directory::new();
|
||||
let first = directory.path("first.txt");
|
||||
fs::write(&first, "base").unwrap();
|
||||
fs::write(directory.path("second.txt"), "second").unwrap();
|
||||
let mut editor = Editor::open(Some(first), vec![]).unwrap();
|
||||
let working = editor.buffer_id();
|
||||
working_diff(&mut editor, &directory.0, &["first.txt", "second.txt"]);
|
||||
assert_eq!(editor.diff().unwrap().focus, Focus::Files);
|
||||
type_text(&mut editor, " s");
|
||||
assert_eq!(editor.diff().unwrap().split_axis, SplitAxis::Horizontal);
|
||||
type_text(&mut editor, " v");
|
||||
assert_eq!(editor.diff().unwrap().view_mode, ViewMode::Unified);
|
||||
editor.handle(Input::Char('i'));
|
||||
type_text(&mut editor, " v");
|
||||
assert_eq!(editor.diff().unwrap().view_mode, ViewMode::Unified);
|
||||
assert_eq!(editor.file_picker().unwrap().query, " v");
|
||||
editor.handle(Input::Escape);
|
||||
editor.handle(Input::BackTab);
|
||||
assert!(editor.diff_editing());
|
||||
assert_eq!(editor.buffer_id(), working);
|
||||
type_text(&mut editor, "i dv");
|
||||
assert_eq!(editor.lines(), [" dvbase"]);
|
||||
editor.handle(Input::Escape);
|
||||
assert_eq!(editor.diff().unwrap().view_mode, ViewMode::Unified);
|
||||
assert!(editor.diff().unwrap().after_display_row(0).is_some());
|
||||
let caret = editor.cursor();
|
||||
type_text(&mut editor, " v");
|
||||
assert_eq!(editor.diff().unwrap().view_mode, ViewMode::Split);
|
||||
assert_eq!(editor.cursor(), caret);
|
||||
assert!(editor.diff_editing());
|
||||
assert!(editor.diff.as_mut().unwrap().select_file_index(1));
|
||||
editor.bind_diff_buffer();
|
||||
assert_eq!(editor.diff().unwrap().file, 1);
|
||||
assert_eq!(editor.diff().unwrap().split_axis, SplitAxis::Horizontal);
|
||||
assert_eq!(editor.diff().unwrap().view_mode, ViewMode::Split);
|
||||
assert_eq!(editor.diff().unwrap().focus, Focus::After);
|
||||
type_text(&mut editor, " v");
|
||||
assert_eq!(editor.diff().unwrap().view_mode, ViewMode::Unified);
|
||||
assert!(editor.diff_editing());
|
||||
assert_eq!(editor.lines(), ["second"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn word_wrap_toggle_retains_buffers_and_readonly_diff_state() {
|
||||
let directory = Directory::new();
|
||||
|
||||
Reference in New Issue
Block a user