mirror of
https://github.com/zed-industries/zed.git
synced 2025-01-11 13:10:54 +00:00
Merge pull request #690 from zed-industries/indent-keybindings
Bind `Outdent` and `Indent` respectively to `cmd-[` and `cmd-]`
This commit is contained in:
commit
686085dd60
1 changed files with 95 additions and 70 deletions
|
@ -68,7 +68,8 @@ action!(Backspace);
|
||||||
action!(Delete);
|
action!(Delete);
|
||||||
action!(Input, String);
|
action!(Input, String);
|
||||||
action!(Newline);
|
action!(Newline);
|
||||||
action!(Tab);
|
action!(Tab, Direction);
|
||||||
|
action!(Indent);
|
||||||
action!(Outdent);
|
action!(Outdent);
|
||||||
action!(DeleteLine);
|
action!(DeleteLine);
|
||||||
action!(DeleteToPreviousWordStart);
|
action!(DeleteToPreviousWordStart);
|
||||||
|
@ -171,13 +172,15 @@ pub fn init(cx: &mut MutableAppContext) {
|
||||||
Some("Editor && showing_code_actions"),
|
Some("Editor && showing_code_actions"),
|
||||||
),
|
),
|
||||||
Binding::new("enter", ConfirmRename, Some("Editor && renaming")),
|
Binding::new("enter", ConfirmRename, Some("Editor && renaming")),
|
||||||
Binding::new("tab", Tab, Some("Editor")),
|
Binding::new("tab", Tab(Direction::Next), Some("Editor")),
|
||||||
|
Binding::new("shift-tab", Tab(Direction::Prev), Some("Editor")),
|
||||||
Binding::new(
|
Binding::new(
|
||||||
"tab",
|
"tab",
|
||||||
ConfirmCompletion(None),
|
ConfirmCompletion(None),
|
||||||
Some("Editor && showing_completions"),
|
Some("Editor && showing_completions"),
|
||||||
),
|
),
|
||||||
Binding::new("shift-tab", Outdent, Some("Editor")),
|
Binding::new("cmd-[", Outdent, Some("Editor")),
|
||||||
|
Binding::new("cmd-]", Indent, Some("Editor")),
|
||||||
Binding::new("ctrl-shift-K", DeleteLine, Some("Editor")),
|
Binding::new("ctrl-shift-K", DeleteLine, Some("Editor")),
|
||||||
Binding::new("alt-backspace", DeleteToPreviousWordStart, Some("Editor")),
|
Binding::new("alt-backspace", DeleteToPreviousWordStart, Some("Editor")),
|
||||||
Binding::new("alt-h", DeleteToPreviousWordStart, Some("Editor")),
|
Binding::new("alt-h", DeleteToPreviousWordStart, Some("Editor")),
|
||||||
|
@ -305,6 +308,7 @@ pub fn init(cx: &mut MutableAppContext) {
|
||||||
cx.add_action(Editor::backspace);
|
cx.add_action(Editor::backspace);
|
||||||
cx.add_action(Editor::delete);
|
cx.add_action(Editor::delete);
|
||||||
cx.add_action(Editor::tab);
|
cx.add_action(Editor::tab);
|
||||||
|
cx.add_action(Editor::indent);
|
||||||
cx.add_action(Editor::outdent);
|
cx.add_action(Editor::outdent);
|
||||||
cx.add_action(Editor::delete_line);
|
cx.add_action(Editor::delete_line);
|
||||||
cx.add_action(Editor::delete_to_previous_word_start);
|
cx.add_action(Editor::delete_to_previous_word_start);
|
||||||
|
@ -2861,75 +2865,101 @@ impl Editor {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn tab(&mut self, _: &Tab, cx: &mut ViewContext<Self>) {
|
pub fn tab(&mut self, &Tab(direction): &Tab, cx: &mut ViewContext<Self>) {
|
||||||
if self.move_to_next_snippet_tabstop(cx) {
|
match direction {
|
||||||
return;
|
Direction::Prev => {
|
||||||
}
|
if !self.snippet_stack.is_empty() {
|
||||||
|
self.move_to_prev_snippet_tabstop(cx);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.outdent(&Outdent, cx);
|
||||||
|
}
|
||||||
|
Direction::Next => {
|
||||||
|
if self.move_to_next_snippet_tabstop(cx) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let tab_size = cx.global::<Settings>().tab_size;
|
||||||
|
let mut selections = self.local_selections::<Point>(cx);
|
||||||
|
if selections.iter().all(|s| s.is_empty()) {
|
||||||
|
self.transact(cx, |this, cx| {
|
||||||
|
this.buffer.update(cx, |buffer, cx| {
|
||||||
|
for selection in &mut selections {
|
||||||
|
let char_column = buffer
|
||||||
|
.read(cx)
|
||||||
|
.text_for_range(
|
||||||
|
Point::new(selection.start.row, 0)..selection.start,
|
||||||
|
)
|
||||||
|
.flat_map(str::chars)
|
||||||
|
.count();
|
||||||
|
let chars_to_next_tab_stop = tab_size - (char_column % tab_size);
|
||||||
|
buffer.edit(
|
||||||
|
[selection.start..selection.start],
|
||||||
|
" ".repeat(chars_to_next_tab_stop),
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
selection.start.column += chars_to_next_tab_stop as u32;
|
||||||
|
selection.end = selection.start;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.update_selections(selections, Some(Autoscroll::Fit), cx);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
self.indent(&Indent, cx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn indent(&mut self, _: &Indent, cx: &mut ViewContext<Self>) {
|
||||||
let tab_size = cx.global::<Settings>().tab_size;
|
let tab_size = cx.global::<Settings>().tab_size;
|
||||||
let mut selections = self.local_selections::<Point>(cx);
|
let mut selections = self.local_selections::<Point>(cx);
|
||||||
self.transact(cx, |this, cx| {
|
self.transact(cx, |this, cx| {
|
||||||
let mut last_indent = None;
|
let mut last_indent = None;
|
||||||
this.buffer.update(cx, |buffer, cx| {
|
this.buffer.update(cx, |buffer, cx| {
|
||||||
for selection in &mut selections {
|
for selection in &mut selections {
|
||||||
if selection.is_empty() {
|
let mut start_row = selection.start.row;
|
||||||
let char_column = buffer
|
let mut end_row = selection.end.row + 1;
|
||||||
.read(cx)
|
|
||||||
.text_for_range(Point::new(selection.start.row, 0)..selection.start)
|
// If a selection ends at the beginning of a line, don't indent
|
||||||
.flat_map(str::chars)
|
// that last line.
|
||||||
.count();
|
if selection.end.column == 0 {
|
||||||
let chars_to_next_tab_stop = tab_size - (char_column % tab_size);
|
end_row -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Avoid re-indenting a row that has already been indented by a
|
||||||
|
// previous selection, but still update this selection's column
|
||||||
|
// to reflect that indentation.
|
||||||
|
if let Some((last_indent_row, last_indent_len)) = last_indent {
|
||||||
|
if last_indent_row == selection.start.row {
|
||||||
|
selection.start.column += last_indent_len;
|
||||||
|
start_row += 1;
|
||||||
|
}
|
||||||
|
if last_indent_row == selection.end.row {
|
||||||
|
selection.end.column += last_indent_len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for row in start_row..end_row {
|
||||||
|
let indent_column = buffer.read(cx).indent_column_for_line(row) as usize;
|
||||||
|
let columns_to_next_tab_stop = tab_size - (indent_column % tab_size);
|
||||||
|
let row_start = Point::new(row, 0);
|
||||||
buffer.edit(
|
buffer.edit(
|
||||||
[selection.start..selection.start],
|
[row_start..row_start],
|
||||||
" ".repeat(chars_to_next_tab_stop),
|
" ".repeat(columns_to_next_tab_stop),
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
selection.start.column += chars_to_next_tab_stop as u32;
|
|
||||||
selection.end = selection.start;
|
|
||||||
} else {
|
|
||||||
let mut start_row = selection.start.row;
|
|
||||||
let mut end_row = selection.end.row + 1;
|
|
||||||
|
|
||||||
// If a selection ends at the beginning of a line, don't indent
|
// Update this selection's endpoints to reflect the indentation.
|
||||||
// that last line.
|
if row == selection.start.row {
|
||||||
if selection.end.column == 0 {
|
selection.start.column += columns_to_next_tab_stop as u32;
|
||||||
end_row -= 1;
|
}
|
||||||
|
if row == selection.end.row {
|
||||||
|
selection.end.column += columns_to_next_tab_stop as u32;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Avoid re-indenting a row that has already been indented by a
|
last_indent = Some((row, columns_to_next_tab_stop as u32));
|
||||||
// previous selection, but still update this selection's column
|
|
||||||
// to reflect that indentation.
|
|
||||||
if let Some((last_indent_row, last_indent_len)) = last_indent {
|
|
||||||
if last_indent_row == selection.start.row {
|
|
||||||
selection.start.column += last_indent_len;
|
|
||||||
start_row += 1;
|
|
||||||
}
|
|
||||||
if last_indent_row == selection.end.row {
|
|
||||||
selection.end.column += last_indent_len;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for row in start_row..end_row {
|
|
||||||
let indent_column =
|
|
||||||
buffer.read(cx).indent_column_for_line(row) as usize;
|
|
||||||
let columns_to_next_tab_stop = tab_size - (indent_column % tab_size);
|
|
||||||
let row_start = Point::new(row, 0);
|
|
||||||
buffer.edit(
|
|
||||||
[row_start..row_start],
|
|
||||||
" ".repeat(columns_to_next_tab_stop),
|
|
||||||
cx,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Update this selection's endpoints to reflect the indentation.
|
|
||||||
if row == selection.start.row {
|
|
||||||
selection.start.column += columns_to_next_tab_stop as u32;
|
|
||||||
}
|
|
||||||
if row == selection.end.row {
|
|
||||||
selection.end.column += columns_to_next_tab_stop as u32;
|
|
||||||
}
|
|
||||||
|
|
||||||
last_indent = Some((row, columns_to_next_tab_stop as u32));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -2939,11 +2969,6 @@ impl Editor {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn outdent(&mut self, _: &Outdent, cx: &mut ViewContext<Self>) {
|
pub fn outdent(&mut self, _: &Outdent, cx: &mut ViewContext<Self>) {
|
||||||
if !self.snippet_stack.is_empty() {
|
|
||||||
self.move_to_prev_snippet_tabstop(cx);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let tab_size = cx.global::<Settings>().tab_size;
|
let tab_size = cx.global::<Settings>().tab_size;
|
||||||
let selections = self.local_selections::<Point>(cx);
|
let selections = self.local_selections::<Point>(cx);
|
||||||
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));
|
||||||
|
@ -7458,7 +7483,7 @@ mod tests {
|
||||||
);
|
);
|
||||||
|
|
||||||
// indent from mid-tabstop to full tabstop
|
// indent from mid-tabstop to full tabstop
|
||||||
view.tab(&Tab, cx);
|
view.tab(&Tab(Direction::Next), cx);
|
||||||
assert_eq!(view.text(cx), " one two\nthree\n four");
|
assert_eq!(view.text(cx), " one two\nthree\n four");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
view.selected_display_ranges(cx),
|
view.selected_display_ranges(cx),
|
||||||
|
@ -7469,7 +7494,7 @@ mod tests {
|
||||||
);
|
);
|
||||||
|
|
||||||
// outdent from 1 tabstop to 0 tabstops
|
// outdent from 1 tabstop to 0 tabstops
|
||||||
view.outdent(&Outdent, cx);
|
view.tab(&Tab(Direction::Prev), cx);
|
||||||
assert_eq!(view.text(cx), "one two\nthree\n four");
|
assert_eq!(view.text(cx), "one two\nthree\n four");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
view.selected_display_ranges(cx),
|
view.selected_display_ranges(cx),
|
||||||
|
@ -7483,13 +7508,13 @@ mod tests {
|
||||||
view.select_display_ranges(&[DisplayPoint::new(1, 1)..DisplayPoint::new(2, 0)], cx);
|
view.select_display_ranges(&[DisplayPoint::new(1, 1)..DisplayPoint::new(2, 0)], cx);
|
||||||
|
|
||||||
// indent and outdent affect only the preceding line
|
// indent and outdent affect only the preceding line
|
||||||
view.tab(&Tab, cx);
|
view.tab(&Tab(Direction::Next), cx);
|
||||||
assert_eq!(view.text(cx), "one two\n three\n four");
|
assert_eq!(view.text(cx), "one two\n three\n four");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
view.selected_display_ranges(cx),
|
view.selected_display_ranges(cx),
|
||||||
&[DisplayPoint::new(1, 5)..DisplayPoint::new(2, 0)]
|
&[DisplayPoint::new(1, 5)..DisplayPoint::new(2, 0)]
|
||||||
);
|
);
|
||||||
view.outdent(&Outdent, cx);
|
view.tab(&Tab(Direction::Prev), cx);
|
||||||
assert_eq!(view.text(cx), "one two\nthree\n four");
|
assert_eq!(view.text(cx), "one two\nthree\n four");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
view.selected_display_ranges(cx),
|
view.selected_display_ranges(cx),
|
||||||
|
@ -7498,7 +7523,7 @@ mod tests {
|
||||||
|
|
||||||
// Ensure that indenting/outdenting works when the cursor is at column 0.
|
// Ensure that indenting/outdenting works when the cursor is at column 0.
|
||||||
view.select_display_ranges(&[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)], cx);
|
view.select_display_ranges(&[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)], cx);
|
||||||
view.tab(&Tab, cx);
|
view.tab(&Tab(Direction::Next), cx);
|
||||||
assert_eq!(view.text(cx), "one two\n three\n four");
|
assert_eq!(view.text(cx), "one two\n three\n four");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
view.selected_display_ranges(cx),
|
view.selected_display_ranges(cx),
|
||||||
|
@ -7506,7 +7531,7 @@ mod tests {
|
||||||
);
|
);
|
||||||
|
|
||||||
view.select_display_ranges(&[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)], cx);
|
view.select_display_ranges(&[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)], cx);
|
||||||
view.outdent(&Outdent, cx);
|
view.tab(&Tab(Direction::Prev), cx);
|
||||||
assert_eq!(view.text(cx), "one two\nthree\n four");
|
assert_eq!(view.text(cx), "one two\nthree\n four");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
view.selected_display_ranges(cx),
|
view.selected_display_ranges(cx),
|
||||||
|
|
Loading…
Reference in a new issue