feat: add toggleable buffer word wrap

This commit is contained in:
2026-10-11 15:55:52 +02:00
parent 2e05725a38
commit 1f625a69c4
9 changed files with 1199 additions and 97 deletions
+105
View File
@@ -574,6 +574,18 @@ core_actions![
),
("core.save", Action::Save, &[Mode::Normal], true),
("core.diff", Action::Diff, &[Mode::Normal], true),
(
"core.word_wrap.toggle",
Action::WordWrapToggle,
&[
Mode::Normal,
Mode::Insert,
Mode::Visual,
Mode::Command,
Mode::Search
],
true,
),
(
"core.diff.close",
Action::DiffClose,
@@ -1023,6 +1035,7 @@ struct Buffer {
targets: Vec<Target>,
primary: usize,
mode: Mode,
word_wrap: bool,
linewise: bool,
search_query: String,
search_match: Option<SearchMatch>,
@@ -1068,6 +1081,7 @@ impl Buffer {
targets: vec![Target::caret(Cursor::default())],
primary: 0,
mode: Mode::Normal,
word_wrap: false,
linewise: false,
search_query: String::new(),
search_match: None,
@@ -1363,6 +1377,13 @@ impl Editor {
self.diff.as_ref()
}
pub fn word_wrap(&self) -> bool {
self.diff
.as_ref()
.filter(|diff| diff.phase == Phase::View)
.map_or(self.active.word_wrap, |diff| diff.word_wrap)
}
pub(crate) fn set_diff_context_options(&mut self, context_lines: usize, expand_lines: usize) {
self.diff_context_options = (context_lines, expand_lines);
if let Some(diff) = &mut self.diff {
@@ -1427,6 +1448,7 @@ impl Editor {
}
self.outstanding_diff_request = None;
let mut diff = DiffState::new();
diff.word_wrap = self.active.word_wrap;
diff.set_context_options(self.diff_context_options.0, self.diff_context_options.1);
self.diff = Some(diff);
}
@@ -1868,6 +1890,7 @@ impl Editor {
| Action::FilePicker
| Action::FileExplorer
| Action::DiffClose
| Action::WordWrapToggle
| Action::Quit
| Action::ForceQuit
| Action::Plugins
@@ -3956,6 +3979,7 @@ impl Editor {
| Action::BufferForceDelete
| Action::Diff
| Action::DiffClose
| Action::WordWrapToggle
| Action::LspDefinition
| Action::LspHover
| Action::LspDiagnostics => unreachable!("catalog Ex actions dispatch above"),
@@ -4535,6 +4559,28 @@ impl Editor {
fn execute_action(&mut self, entry: &CoreAction, args: &str) -> Option<Invocation> {
self.finish_diff_prompt();
if entry.action == Action::WordWrapToggle {
if !args.is_empty() {
self.set_message("The word_wrap.toggle command takes no arguments");
return None;
}
if let Some(diff) = &mut self.diff
&& diff.phase == Phase::View
{
diff.word_wrap = !diff.word_wrap;
diff.follow_caret = true;
} else {
self.active.word_wrap = !self.active.word_wrap;
}
self.count = None;
self.clear_leader();
self.palette = None;
if self.active.mode == Mode::Command {
self.command_line.clear();
self.active.mode = Mode::Normal;
}
return None;
}
if entry.action == Action::DiffClose {
if args.is_empty() {
self.release_diff_buffers();
@@ -7989,6 +8035,65 @@ mod tests {
assert_eq!(editor.lines(), ["beta"]);
}
#[test]
fn word_wrap_toggle_retains_buffers_and_readonly_diff_state() {
let directory = Directory::new();
let mut editor = editor();
assert!(!editor.word_wrap());
editor
.bind("normal", "W", Some("core.word_wrap.toggle".into()))
.unwrap();
editor.handle(Input::Char('W'));
assert!(editor.word_wrap());
let original = editor.buffer_id();
editor.execute(&format!("edit {}", directory.path("other").display()));
assert!(!editor.word_wrap());
editor.execute(&format!("buffer {original}"));
assert!(editor.word_wrap());
editor.execute("word_wrap.toggle unexpected");
assert!(editor.word_wrap());
editor.execute("word_wrap.toggle");
assert!(!editor.word_wrap());
editor.execute("word_wrap.toggle");
historical_diff(
&mut editor,
&directory.0,
vec!["alpha beta".into()],
vec!["alpha changed".into()],
);
assert!(editor.word_wrap());
editor
.bind("visual", "W", Some("core.word_wrap.toggle".into()))
.unwrap();
type_text(&mut editor, "v2l");
let text = editor.lines().to_vec();
let selections = editor.selections();
let cursor = editor.cursor();
let revision = editor.active.text_revision;
let history = (editor.active.undo.len(), editor.active.redo.len());
let comparison = editor.diff().unwrap().comparison_id;
editor.execute("word_wrap.toggle unexpected");
assert!(editor.word_wrap());
editor.handle(Input::Char('W'));
assert!(!editor.word_wrap());
assert_eq!(editor.lines(), text);
assert_eq!(editor.selections(), selections);
assert_eq!(editor.cursor(), cursor);
assert_eq!(editor.active.text_revision, revision);
assert_eq!(
(editor.active.undo.len(), editor.active.redo.len()),
history
);
assert_eq!(editor.diff().unwrap().comparison_id, comparison);
assert_eq!(editor.diff().unwrap().phase, Phase::View);
editor.handle(Input::Ctrl('p'));
type_text(&mut editor, "word_wrap.toggle");
editor.handle(Input::Enter);
assert!(editor.word_wrap());
editor.execute("diff.close");
assert!(editor.word_wrap());
}
#[test]
fn diff_readonly_panes_dispatch_prompts_palette_and_picker_remaps_without_edits() {
let directory = Directory::new();