Improve Editor::DuplicateSelection (#21976)
Improves the new `Editor::DuplicateSelection` @CharlesChen0823 added in https://github.com/zed-industries/zed/pull/21154. - Merge `duplicate_selection` and `duplicate_line` into single function. - Add keyboard shortcuts to JetBrains and SublimeText keymaps. - If the selection is empty (e.g. just a cursor) make `Editor::DuplicateSelection` fallback to being the same as `Editor::DuplicateLineDown`. - Tested with multiple cursors and for multiple selections. | Editor | Action | macOS | Linux | | ----------- | ------------------- | ----------- | ------------ | | VSCode | Duplicate Selection | | | | JetBrains | Duplicate Selection | cmd-d | ctrl-d | | XCode | Duplicate | cmd-d | N/A | | SublimeText | duplicate_line | cmd-shift-d | ctrl-shift-d | This matches behavior of the `duplicate` functionality in all other major editors, with one exception: other editors change the selection so that the newly duplicated object, current Zed behavior leaves the original selection unchanged (TODO?)
This commit is contained in:
parent
06edcd18be
commit
735849e201
5 changed files with 45 additions and 55 deletions
|
@ -12,7 +12,7 @@
|
||||||
"ctrl->": "zed::IncreaseBufferFontSize",
|
"ctrl->": "zed::IncreaseBufferFontSize",
|
||||||
"ctrl-<": "zed::DecreaseBufferFontSize",
|
"ctrl-<": "zed::DecreaseBufferFontSize",
|
||||||
"ctrl-shift-j": "editor::JoinLines",
|
"ctrl-shift-j": "editor::JoinLines",
|
||||||
"ctrl-d": "editor::DuplicateLineDown",
|
"ctrl-d": "editor::DuplicateSelection",
|
||||||
"ctrl-y": "editor::DeleteLine",
|
"ctrl-y": "editor::DeleteLine",
|
||||||
"ctrl-m": "editor::ScrollCursorCenter",
|
"ctrl-m": "editor::ScrollCursorCenter",
|
||||||
"ctrl-pagedown": "editor::MovePageDown",
|
"ctrl-pagedown": "editor::MovePageDown",
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
"ctrl-shift-m": "editor::SelectLargerSyntaxNode",
|
"ctrl-shift-m": "editor::SelectLargerSyntaxNode",
|
||||||
"ctrl-shift-l": "editor::SplitSelectionIntoLines",
|
"ctrl-shift-l": "editor::SplitSelectionIntoLines",
|
||||||
"ctrl-shift-a": "editor::SelectLargerSyntaxNode",
|
"ctrl-shift-a": "editor::SelectLargerSyntaxNode",
|
||||||
"ctrl-shift-d": "editor::DuplicateLineDown",
|
"ctrl-shift-d": "editor::DuplicateSelection",
|
||||||
"alt-f3": "editor::SelectAllMatches", // find_all_under
|
"alt-f3": "editor::SelectAllMatches", // find_all_under
|
||||||
"f12": "editor::GoToDefinition",
|
"f12": "editor::GoToDefinition",
|
||||||
"ctrl-f12": "editor::GoToDefinitionSplit",
|
"ctrl-f12": "editor::GoToDefinitionSplit",
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
"ctrl->": "zed::IncreaseBufferFontSize",
|
"ctrl->": "zed::IncreaseBufferFontSize",
|
||||||
"ctrl-<": "zed::DecreaseBufferFontSize",
|
"ctrl-<": "zed::DecreaseBufferFontSize",
|
||||||
"ctrl-shift-j": "editor::JoinLines",
|
"ctrl-shift-j": "editor::JoinLines",
|
||||||
"cmd-d": "editor::DuplicateLineDown",
|
"cmd-d": "editor::DuplicateSelection",
|
||||||
"cmd-backspace": "editor::DeleteLine",
|
"cmd-backspace": "editor::DeleteLine",
|
||||||
"cmd-pagedown": "editor::MovePageDown",
|
"cmd-pagedown": "editor::MovePageDown",
|
||||||
"cmd-pageup": "editor::MovePageUp",
|
"cmd-pageup": "editor::MovePageUp",
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
"ctrl-shift-m": "editor::SelectLargerSyntaxNode",
|
"ctrl-shift-m": "editor::SelectLargerSyntaxNode",
|
||||||
"cmd-shift-l": "editor::SplitSelectionIntoLines",
|
"cmd-shift-l": "editor::SplitSelectionIntoLines",
|
||||||
"cmd-shift-a": "editor::SelectLargerSyntaxNode",
|
"cmd-shift-a": "editor::SelectLargerSyntaxNode",
|
||||||
"cmd-shift-d": "editor::DuplicateLineDown",
|
"cmd-shift-d": "editor::DuplicateSelection",
|
||||||
"ctrl-cmd-g": "editor::SelectAllMatches", // find_all_under
|
"ctrl-cmd-g": "editor::SelectAllMatches", // find_all_under
|
||||||
"shift-f12": "editor::FindAllReferences",
|
"shift-f12": "editor::FindAllReferences",
|
||||||
"alt-cmd-down": "editor::GoToDefinition",
|
"alt-cmd-down": "editor::GoToDefinition",
|
||||||
|
|
|
@ -6135,29 +6135,7 @@ impl Editor {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn duplicate_selection(&mut self, _: &DuplicateSelection, cx: &mut ViewContext<Self>) {
|
pub fn duplicate(&mut self, upwards: bool, whole_lines: bool, cx: &mut ViewContext<Self>) {
|
||||||
let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
|
|
||||||
let buffer = &display_map.buffer_snapshot;
|
|
||||||
let selections = self.selections.all::<Point>(cx);
|
|
||||||
|
|
||||||
let mut edits = Vec::new();
|
|
||||||
for selection in selections.iter() {
|
|
||||||
let start = selection.start;
|
|
||||||
let end = selection.end;
|
|
||||||
let text = buffer.text_for_range(start..end).collect::<String>();
|
|
||||||
edits.push((selection.end..selection.end, text));
|
|
||||||
}
|
|
||||||
|
|
||||||
self.transact(cx, |this, cx| {
|
|
||||||
this.buffer.update(cx, |buffer, cx| {
|
|
||||||
buffer.edit(edits, None, cx);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.request_autoscroll(Autoscroll::fit(), cx);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn duplicate_line(&mut self, upwards: bool, cx: &mut ViewContext<Self>) {
|
|
||||||
let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
|
let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
|
||||||
let buffer = &display_map.buffer_snapshot;
|
let buffer = &display_map.buffer_snapshot;
|
||||||
let selections = self.selections.all::<Point>(cx);
|
let selections = self.selections.all::<Point>(cx);
|
||||||
|
@ -6165,36 +6143,44 @@ impl Editor {
|
||||||
let mut edits = Vec::new();
|
let mut edits = Vec::new();
|
||||||
let mut selections_iter = selections.iter().peekable();
|
let mut selections_iter = selections.iter().peekable();
|
||||||
while let Some(selection) = selections_iter.next() {
|
while let Some(selection) = selections_iter.next() {
|
||||||
// Avoid duplicating the same lines twice.
|
|
||||||
let mut rows = selection.spanned_rows(false, &display_map);
|
let mut rows = selection.spanned_rows(false, &display_map);
|
||||||
|
// duplicate line-wise
|
||||||
while let Some(next_selection) = selections_iter.peek() {
|
if whole_lines || selection.start == selection.end {
|
||||||
let next_rows = next_selection.spanned_rows(false, &display_map);
|
// Avoid duplicating the same lines twice.
|
||||||
if next_rows.start < rows.end {
|
while let Some(next_selection) = selections_iter.peek() {
|
||||||
rows.end = next_rows.end;
|
let next_rows = next_selection.spanned_rows(false, &display_map);
|
||||||
selections_iter.next().unwrap();
|
if next_rows.start < rows.end {
|
||||||
} else {
|
rows.end = next_rows.end;
|
||||||
break;
|
selections_iter.next().unwrap();
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Copy the text from the selected row region and splice it either at the start
|
// Copy the text from the selected row region and splice it either at the start
|
||||||
// or end of the region.
|
// or end of the region.
|
||||||
let start = Point::new(rows.start.0, 0);
|
let start = Point::new(rows.start.0, 0);
|
||||||
let end = Point::new(
|
let end = Point::new(
|
||||||
rows.end.previous_row().0,
|
rows.end.previous_row().0,
|
||||||
buffer.line_len(rows.end.previous_row()),
|
buffer.line_len(rows.end.previous_row()),
|
||||||
);
|
);
|
||||||
let text = buffer
|
let text = buffer
|
||||||
.text_for_range(start..end)
|
.text_for_range(start..end)
|
||||||
.chain(Some("\n"))
|
.chain(Some("\n"))
|
||||||
.collect::<String>();
|
.collect::<String>();
|
||||||
let insert_location = if upwards {
|
let insert_location = if upwards {
|
||||||
Point::new(rows.end.0, 0)
|
Point::new(rows.end.0, 0)
|
||||||
|
} else {
|
||||||
|
start
|
||||||
|
};
|
||||||
|
edits.push((insert_location..insert_location, text));
|
||||||
} else {
|
} else {
|
||||||
start
|
// duplicate character-wise
|
||||||
};
|
let start = selection.start;
|
||||||
edits.push((insert_location..insert_location, text));
|
let end = selection.end;
|
||||||
|
let text = buffer.text_for_range(start..end).collect::<String>();
|
||||||
|
edits.push((selection.end..selection.end, text));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.transact(cx, |this, cx| {
|
self.transact(cx, |this, cx| {
|
||||||
|
@ -6207,11 +6193,15 @@ impl Editor {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn duplicate_line_up(&mut self, _: &DuplicateLineUp, cx: &mut ViewContext<Self>) {
|
pub fn duplicate_line_up(&mut self, _: &DuplicateLineUp, cx: &mut ViewContext<Self>) {
|
||||||
self.duplicate_line(true, cx);
|
self.duplicate(true, true, cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn duplicate_line_down(&mut self, _: &DuplicateLineDown, cx: &mut ViewContext<Self>) {
|
pub fn duplicate_line_down(&mut self, _: &DuplicateLineDown, cx: &mut ViewContext<Self>) {
|
||||||
self.duplicate_line(false, cx);
|
self.duplicate(false, true, cx);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn duplicate_selection(&mut self, _: &DuplicateSelection, cx: &mut ViewContext<Self>) {
|
||||||
|
self.duplicate(false, false, cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn move_line_up(&mut self, _: &MoveLineUp, cx: &mut ViewContext<Self>) {
|
pub fn move_line_up(&mut self, _: &MoveLineUp, cx: &mut ViewContext<Self>) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue