diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index f379d051e150f114227484dba380938f493ae550..d41db01368430b9273686dc9eff2d6d925410451 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -5303,8 +5303,9 @@ impl Editor { let start_point = selection.start.to_point(&buffer); let mut existing_indent = buffer.indent_size_for_line(MultiBufferRow(start_point.row)); + let full_indent_len = existing_indent.len; existing_indent.len = cmp::min(existing_indent.len, start_point.column); - let start = selection.start; + let mut start = selection.start; let end = selection.end; let selection_is_empty = start == end; let language_scope = buffer.language_scope_at(start); @@ -5454,6 +5455,19 @@ impl Editor { } new_text.extend(extra_indent.chars()); } + // Extend the edit to the beginning of the line + // to clear auto-indent whitespace that would + // otherwise remain as trailing whitespace. This + // applies to blank lines and lines where only + // indentation remains before the cursor. + if selection_is_empty + && preserve_indent + && full_indent_len > 0 + && start_point.column == full_indent_len + { + start = buffer.point_to_offset(Point::new(start_point.row, 0)); + } + ( start, new_text, diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index 297a943455f0b3b6acb26af7879c949d6b308afe..66c97276c34221f11cc2ddbab338f49626714f89 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -3553,6 +3553,81 @@ fn test_newline(cx: &mut TestAppContext) { }); } +#[gpui::test] +fn test_newline_trailing_whitespace(cx: &mut TestAppContext) { + init_test(cx, |settings| { + settings.defaults.auto_indent = Some(settings::AutoIndentMode::PreserveIndent); + }); + + let buffer = cx.update(|cx| MultiBuffer::build_simple(" hello\n world\n", cx)); + let editor = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx)); + + editor + .update(cx, |editor, window, cx| { + editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| { + s.select_display_ranges([ + DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 9) + ]) + }); + + editor.newline(&Newline, window, cx); + assert_eq!(editor.text(cx), " hello\n \n world\n"); + + editor.newline(&Newline, window, cx); + assert_eq!(editor.text(cx), " hello\n\n \n world\n"); + }) + .unwrap(); + + buffer.update(cx, |buffer, cx| { + let start = MultiBufferOffset(0); + let end = buffer.len(cx); + buffer.edit([(start..end, " hello\n world\n")], None, cx); + }); + + editor + .update(cx, |editor, window, cx| { + editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| { + s.select_display_ranges([ + DisplayPoint::new(DisplayRow(0), 7)..DisplayPoint::new(DisplayRow(0), 7) + ]) + }); + + editor.newline(&Newline, window, cx); + assert_eq!(editor.text(cx), " hel\n lo\n world\n"); + + editor.newline(&Newline, window, cx); + assert_eq!(editor.text(cx), " hel\n\n lo\n world\n"); + }) + .unwrap(); + + update_test_language_settings(cx, &|settings| { + settings.defaults.tab_size = NonZeroU32::new(4); + settings.defaults.hard_tabs = Some(true); + }); + + buffer.update(cx, |buffer, cx| { + let start = MultiBufferOffset(0); + let end = buffer.len(cx); + buffer.edit([(start..end, "\thello\n\tworld\n")], None, cx); + }); + + editor + .update(cx, |editor, window, cx| { + editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| { + s.select_display_ranges([ + DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 9) + ]) + }); + + editor.newline(&Newline, window, cx); + assert_eq!(editor.text(cx), "\thello\n\t\n\tworld\n"); + + editor.newline(&Newline, window, cx); + assert_eq!(editor.text(cx), "\thello\n\n\t\n\tworld\n"); + }) + .unwrap(); +} + #[gpui::test] async fn test_newline_yaml(cx: &mut TestAppContext) { init_test(cx, |_| {});