@@ -29,7 +29,7 @@ use editor::{Anchor, SelectionEffects};
use editor::{Bias, ToPoint};
use editor::{display_map::ToDisplayPoint, movement};
use gpui::{Context, Window, actions};
-use language::{Point, SelectionGoal};
+use language::{AutoIndentMode, Point, SelectionGoal};
use log::error;
use multi_buffer::MultiBufferRow;
@@ -729,19 +729,35 @@ impl Vim {
.into_iter()
.map(|selection| selection.start.row)
.collect();
- let edits = selection_start_rows
- .into_iter()
- .map(|row| {
- let indent = snapshot
- .indent_and_comment_for_line(MultiBufferRow(row), cx)
- .chars()
- .collect::<String>();
- let start_of_line = Point::new(row, 0);
- (start_of_line..start_of_line, indent + "\n")
- })
- .collect::<Vec<_>>();
- editor.edit_with_autoindent(edits, cx);
+ let mut auto_indent_edits = Vec::new();
+ let mut plain_edits = Vec::new();
+
+ for row in selection_start_rows {
+ let auto_indent_mode = snapshot
+ .language_settings_at(Point::new(row, 0), cx)
+ .auto_indent;
+ let indent = if auto_indent_mode == AutoIndentMode::None {
+ String::new()
+ } else {
+ snapshot.indent_and_comment_for_line(MultiBufferRow(row), cx)
+ };
+ let start_of_line = Point::new(row, 0);
+ let edit = (start_of_line..start_of_line, indent + "\n");
+ if auto_indent_mode == AutoIndentMode::None {
+ plain_edits.push(edit);
+ } else {
+ auto_indent_edits.push(edit);
+ }
+ }
+
+ if !plain_edits.is_empty() {
+ editor.edit(plain_edits, cx);
+ }
+ if !auto_indent_edits.is_empty() {
+ editor.edit_with_autoindent(auto_indent_edits, cx);
+ }
+
editor.change_selections(Default::default(), window, cx, |s| {
s.move_with(&mut |map, selection| {
let previous_line = map.start_of_relative_buffer_row(selection.start, -1);
@@ -776,18 +792,28 @@ impl Vim {
}
})
.collect();
- let edits = selection_end_rows
- .into_iter()
- .map(|row| {
- let indent = snapshot
- .indent_and_comment_for_line(MultiBufferRow(row), cx)
- .chars()
- .collect::<String>();
- let end_of_line = Point::new(row, snapshot.line_len(MultiBufferRow(row)));
- (end_of_line..end_of_line, "\n".to_string() + &indent)
- })
- .collect::<Vec<_>>();
+ let mut auto_indent_edits = Vec::new();
+ let mut plain_edits = Vec::new();
+
+ for row in selection_end_rows {
+ let auto_indent_mode = snapshot
+ .language_settings_at(Point::new(row, 0), cx)
+ .auto_indent;
+ let indent = if auto_indent_mode == AutoIndentMode::None {
+ String::new()
+ } else {
+ snapshot.indent_and_comment_for_line(MultiBufferRow(row), cx)
+ };
+ let end_of_line = Point::new(row, snapshot.line_len(MultiBufferRow(row)));
+ let edit = (end_of_line..end_of_line, "\n".to_string() + &indent);
+ if auto_indent_mode == AutoIndentMode::None {
+ plain_edits.push(edit);
+ } else {
+ auto_indent_edits.push(edit);
+ }
+ }
+
editor.change_selections(Default::default(), window, cx, |s| {
s.move_with(&mut |map, selection| {
let current_line = if !selection.is_empty() && selection.end.column() == 0 {
@@ -802,7 +828,13 @@ impl Vim {
selection.collapse_to(insert_point, SelectionGoal::None)
});
});
- editor.edit_with_autoindent(edits, cx);
+
+ if !plain_edits.is_empty() {
+ editor.edit(plain_edits, cx);
+ }
+ if !auto_indent_edits.is_empty() {
+ editor.edit_with_autoindent(auto_indent_edits, cx);
+ }
});
});
}
@@ -1140,6 +1172,7 @@ mod test {
state::Mode::{self},
test::{NeovimBackedTestContext, VimTestContext},
};
+ use language;
#[gpui::test]
async fn test_h(cx: &mut gpui::TestAppContext) {
@@ -2099,6 +2132,62 @@ mod test {
cx.shared_state().await.assert_eq("// hello\n// ˇ\n// x\n");
}
+ #[gpui::test]
+ async fn test_o_auto_indent_none(cx: &mut gpui::TestAppContext) {
+ let mut cx = VimTestContext::new(cx, true).await;
+ cx.update_global(|store: &mut SettingsStore, cx| {
+ store.update_user_settings(cx, |s| {
+ s.project.all_languages.defaults.auto_indent = Some(language::AutoIndentMode::None);
+ });
+ });
+
+ // o: new line below starts at column 0 regardless of current indentation
+ cx.set_state(" let xˇ = 1;", Mode::Normal);
+ cx.simulate_keystrokes("o");
+ cx.assert_state(" let x = 1;\nˇ", Mode::Insert);
+
+ // O: new line above starts at column 0 regardless of current indentation
+ cx.set_state(" let xˇ = 1;", Mode::Normal);
+ cx.simulate_keystrokes("shift-o");
+ cx.assert_state("ˇ\n let x = 1;", Mode::Insert);
+
+ // o on the first line: no crash and column 0
+ cx.set_state("ˇfoo", Mode::Normal);
+ cx.simulate_keystrokes("o");
+ cx.assert_state("foo\nˇ", Mode::Insert);
+
+ // O on the first line: no crash and column 0
+ cx.set_state("ˇfoo", Mode::Normal);
+ cx.simulate_keystrokes("shift-o");
+ cx.assert_state("ˇ\nfoo", Mode::Insert);
+
+ // o on an already-empty line: stays at column 0
+ cx.set_state("fooˇ\n\nbar", Mode::Normal);
+ cx.simulate_keystrokes("j o");
+ cx.assert_state("foo\n\nˇ\nbar", Mode::Insert);
+ }
+
+ #[gpui::test]
+ async fn test_o_preserve_indent(cx: &mut gpui::TestAppContext) {
+ let mut cx = VimTestContext::new(cx, true).await;
+ cx.update_global(|store: &mut SettingsStore, cx| {
+ store.update_user_settings(cx, |s| {
+ s.project.all_languages.defaults.auto_indent =
+ Some(language::AutoIndentMode::PreserveIndent);
+ });
+ });
+
+ // o: new line below copies current line's indentation
+ cx.set_state(" let xˇ = 1;", Mode::Normal);
+ cx.simulate_keystrokes("o");
+ cx.assert_state(" let x = 1;\n ˇ", Mode::Insert);
+
+ // O: new line above copies current line's indentation
+ cx.set_state(" let xˇ = 1;", Mode::Normal);
+ cx.simulate_keystrokes("shift-o");
+ cx.assert_state(" ˇ\n let x = 1;", Mode::Insert);
+ }
+
#[gpui::test]
async fn test_yank_line_with_trailing_newline(cx: &mut gpui::TestAppContext) {
let mut cx = NeovimBackedTestContext::new(cx).await;