@@ -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,
@@ -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, |_| {});