Merge branch 'main' into collab-titlebar-2

This commit is contained in:
Piotr Osiewicz 2023-06-22 17:41:00 +02:00
commit 1a55b687b0
6 changed files with 214 additions and 5 deletions

View File

@ -51,6 +51,7 @@ jobs:
rustup set profile minimal
rustup update stable
rustup target add wasm32-wasi
cargo install cargo-nextest
- name: Install Node
uses: actions/setup-node@v2
@ -70,7 +71,7 @@ jobs:
run: cargo check --workspace
- name: Run tests
run: cargo test --workspace --no-fail-fast
run: cargo nextest run --workspace --no-fail-fast
- name: Build collab
run: cargo build -p collab

View File

@ -411,6 +411,7 @@
"ctrl-shift-k": "editor::DeleteLine",
"cmd-shift-d": "editor::DuplicateLine",
"cmd-shift-l": "editor::SplitSelectionIntoLines",
"ctrl-j": "editor::JoinLines",
"ctrl-cmd-up": "editor::MoveLineUp",
"ctrl-cmd-down": "editor::MoveLineDown",
"ctrl-alt-backspace": "editor::DeleteToPreviousSubwordStart",

View File

@ -25,11 +25,15 @@
}
],
"h": "vim::Left",
"left": "vim::Left",
"backspace": "vim::Backspace",
"j": "vim::Down",
"down": "vim::Down",
"enter": "vim::NextLineStart",
"k": "vim::Up",
"up": "vim::Up",
"l": "vim::Right",
"right": "vim::Right",
"$": "vim::EndOfLine",
"shift-g": "vim::EndOfDocument",
"w": "vim::NextWordStart",
@ -90,6 +94,8 @@
}
}
],
"ctrl-o": "pane::GoBack",
"ctrl-]": "editor::GoToDefinition",
"escape": "editor::Cancel",
"0": "vim::StartOfLine", // When no number operator present, use start of line motion
"1": [
@ -143,6 +149,7 @@
"Delete"
],
"shift-d": "vim::DeleteToEndOfLine",
"shift-j": "editor::JoinLines",
"y": [
"vim::PushOperator",
"Yank"
@ -184,7 +191,6 @@
"p": "vim::Paste",
"u": "editor::Undo",
"ctrl-r": "editor::Redo",
"ctrl-o": "pane::GoBack",
"/": [
"buffer_search::Deploy",
{

View File

@ -206,6 +206,7 @@ actions!(
DuplicateLine,
MoveLineUp,
MoveLineDown,
JoinLines,
Transpose,
Cut,
Copy,
@ -321,6 +322,7 @@ pub fn init(cx: &mut AppContext) {
cx.add_action(Editor::indent);
cx.add_action(Editor::outdent);
cx.add_action(Editor::delete_line);
cx.add_action(Editor::join_lines);
cx.add_action(Editor::delete_to_previous_word_start);
cx.add_action(Editor::delete_to_previous_subword_start);
cx.add_action(Editor::delete_to_next_word_end);
@ -3956,6 +3958,60 @@ impl Editor {
});
}
pub fn join_lines(&mut self, _: &JoinLines, cx: &mut ViewContext<Self>) {
let mut row_ranges = Vec::<Range<u32>>::new();
for selection in self.selections.all::<Point>(cx) {
let start = selection.start.row;
let end = if selection.start.row == selection.end.row {
selection.start.row + 1
} else {
selection.end.row
};
if let Some(last_row_range) = row_ranges.last_mut() {
if start <= last_row_range.end {
last_row_range.end = end;
continue;
}
}
row_ranges.push(start..end);
}
let snapshot = self.buffer.read(cx).snapshot(cx);
let mut cursor_positions = Vec::new();
for row_range in &row_ranges {
let anchor = snapshot.anchor_before(Point::new(
row_range.end - 1,
snapshot.line_len(row_range.end - 1),
));
cursor_positions.push(anchor.clone()..anchor);
}
self.transact(cx, |this, cx| {
for row_range in row_ranges.into_iter().rev() {
for row in row_range.rev() {
let end_of_line = Point::new(row, snapshot.line_len(row));
let indent = snapshot.indent_size_for_line(row + 1);
let start_of_next_line = Point::new(row + 1, indent.len);
let replace = if snapshot.line_len(row + 1) > indent.len {
" "
} else {
""
};
this.buffer.update(cx, |buffer, cx| {
buffer.edit([(end_of_line..start_of_next_line, replace)], None, cx)
});
}
}
this.change_selections(Some(Autoscroll::fit()), cx, |s| {
s.select_anchor_ranges(cursor_positions)
});
});
}
pub fn duplicate_line(&mut self, _: &DuplicateLine, cx: &mut ViewContext<Self>) {
let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
let buffer = &display_map.buffer_snapshot;

View File

@ -1,7 +1,10 @@
use super::*;
use crate::test::{
assert_text_with_selections, build_editor, editor_lsp_test_context::EditorLspTestContext,
editor_test_context::EditorTestContext, select_ranges,
use crate::{
test::{
assert_text_with_selections, build_editor, editor_lsp_test_context::EditorLspTestContext,
editor_test_context::EditorTestContext, select_ranges,
},
JoinLines,
};
use drag_and_drop::DragAndDrop;
use futures::StreamExt;
@ -2325,6 +2328,137 @@ fn test_delete_line(cx: &mut TestAppContext) {
});
}
#[gpui::test]
fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
init_test(cx, |_| {});
cx.add_window(|cx| {
let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
let mut editor = build_editor(buffer.clone(), cx);
let buffer = buffer.read(cx).as_singleton().unwrap();
assert_eq!(
editor.selections.ranges::<Point>(cx),
&[Point::new(0, 0)..Point::new(0, 0)]
);
// When on single line, replace newline at end by space
editor.join_lines(&JoinLines, cx);
assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
assert_eq!(
editor.selections.ranges::<Point>(cx),
&[Point::new(0, 3)..Point::new(0, 3)]
);
// When multiple lines are selected, remove newlines that are spanned by the selection
editor.change_selections(None, cx, |s| {
s.select_ranges([Point::new(0, 5)..Point::new(2, 2)])
});
editor.join_lines(&JoinLines, cx);
assert_eq!(buffer.read(cx).text(), "aaa bbb ccc ddd\n\n");
assert_eq!(
editor.selections.ranges::<Point>(cx),
&[Point::new(0, 11)..Point::new(0, 11)]
);
// Undo should be transactional
editor.undo(&Undo, cx);
assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
assert_eq!(
editor.selections.ranges::<Point>(cx),
&[Point::new(0, 5)..Point::new(2, 2)]
);
// When joining an empty line don't insert a space
editor.change_selections(None, cx, |s| {
s.select_ranges([Point::new(2, 1)..Point::new(2, 2)])
});
editor.join_lines(&JoinLines, cx);
assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n");
assert_eq!(
editor.selections.ranges::<Point>(cx),
[Point::new(2, 3)..Point::new(2, 3)]
);
// We can remove trailing newlines
editor.join_lines(&JoinLines, cx);
assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
assert_eq!(
editor.selections.ranges::<Point>(cx),
[Point::new(2, 3)..Point::new(2, 3)]
);
// We don't blow up on the last line
editor.join_lines(&JoinLines, cx);
assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
assert_eq!(
editor.selections.ranges::<Point>(cx),
[Point::new(2, 3)..Point::new(2, 3)]
);
// reset to test indentation
editor.buffer.update(cx, |buffer, cx| {
buffer.edit(
[
(Point::new(1, 0)..Point::new(1, 2), " "),
(Point::new(2, 0)..Point::new(2, 3), " \n\td"),
],
None,
cx,
)
});
// We remove any leading spaces
assert_eq!(buffer.read(cx).text(), "aaa bbb\n c\n \n\td");
editor.change_selections(None, cx, |s| {
s.select_ranges([Point::new(0, 1)..Point::new(0, 1)])
});
editor.join_lines(&JoinLines, cx);
assert_eq!(buffer.read(cx).text(), "aaa bbb c\n \n\td");
// We don't insert a space for a line containing only spaces
editor.join_lines(&JoinLines, cx);
assert_eq!(buffer.read(cx).text(), "aaa bbb c\n\td");
// We ignore any leading tabs
editor.join_lines(&JoinLines, cx);
assert_eq!(buffer.read(cx).text(), "aaa bbb c d");
editor
});
}
#[gpui::test]
fn test_join_lines_with_multi_selection(cx: &mut TestAppContext) {
init_test(cx, |_| {});
cx.add_window(|cx| {
let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
let mut editor = build_editor(buffer.clone(), cx);
let buffer = buffer.read(cx).as_singleton().unwrap();
editor.change_selections(None, cx, |s| {
s.select_ranges([
Point::new(0, 2)..Point::new(1, 1),
Point::new(1, 2)..Point::new(1, 2),
Point::new(3, 1)..Point::new(3, 2),
])
});
editor.join_lines(&JoinLines, cx);
assert_eq!(buffer.read(cx).text(), "aaa bbb ccc\nddd\n");
assert_eq!(
editor.selections.ranges::<Point>(cx),
[
Point::new(0, 7)..Point::new(0, 7),
Point::new(1, 3)..Point::new(1, 3)
]
);
editor
});
}
#[gpui::test]
fn test_duplicate_line(cx: &mut TestAppContext) {
init_test(cx, |_| {});

View File

@ -98,3 +98,14 @@ async fn test_buffer_search(cx: &mut gpui::TestAppContext) {
assert_eq!(bar.query_editor.read(cx).text(cx), "jumps");
})
}
#[gpui::test]
async fn test_count_down(cx: &mut gpui::TestAppContext) {
let mut cx = VimTestContext::new(cx, true).await;
cx.set_state(indoc! {"aˇa\nbb\ncc\ndd\nee"}, Mode::Normal);
cx.simulate_keystrokes(["2", "down"]);
cx.assert_editor_state("aa\nbb\ncˇc\ndd\nee");
cx.simulate_keystrokes(["9", "down"]);
cx.assert_editor_state("aa\nbb\ncc\ndd\neˇe");
}