Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
139 changes: 114 additions & 25 deletions crates/vim/src/normal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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 {
Expand All @@ -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);
}
});
});
}
Expand Down Expand Up @@ -1140,6 +1172,7 @@ mod test {
state::Mode::{self},
test::{NeovimBackedTestContext, VimTestContext},
};
use language;

#[gpui::test]
async fn test_h(cx: &mut gpui::TestAppContext) {
Expand Down Expand Up @@ -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;
Expand Down
Loading