Compare commits
2 Commits
static-rel
...
indenting-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4c7d1b36df | ||
|
|
c54f3f6443 |
@@ -9743,6 +9743,14 @@ impl Editor {
|
|||||||
let mut edits = Vec::new();
|
let mut edits = Vec::new();
|
||||||
let mut prev_edited_row = 0;
|
let mut prev_edited_row = 0;
|
||||||
let mut row_delta = 0;
|
let mut row_delta = 0;
|
||||||
|
|
||||||
|
// Check if we have multiple cursors all at the beginning of their lines (column 0)
|
||||||
|
// This is specifically for fixing the issue where indenting multiple comment lines
|
||||||
|
// results in inconsistent indentation
|
||||||
|
let empty_selections: Vec<_> = selections.iter().filter(|s| s.is_empty()).collect();
|
||||||
|
let multiple_cursors_at_line_start =
|
||||||
|
empty_selections.len() > 1 && empty_selections.iter().all(|s| s.head().column == 0);
|
||||||
|
|
||||||
for selection in &mut selections {
|
for selection in &mut selections {
|
||||||
if selection.start.row != prev_edited_row {
|
if selection.start.row != prev_edited_row {
|
||||||
row_delta = 0;
|
row_delta = 0;
|
||||||
@@ -9761,67 +9769,87 @@ impl Editor {
|
|||||||
if let Some(suggested_indent) =
|
if let Some(suggested_indent) =
|
||||||
suggested_indents.get(&MultiBufferRow(cursor.row)).copied()
|
suggested_indents.get(&MultiBufferRow(cursor.row)).copied()
|
||||||
{
|
{
|
||||||
// Don't do anything if already at suggested indent
|
// Skip suggested indent logic when we have multiple cursors at the beginning
|
||||||
// and there is any other cursor which is not
|
// of lines to ensure consistent indentation across multiple lines
|
||||||
if has_some_cursor_in_whitespace
|
if !(multiple_cursors_at_line_start && cursor.column == 0) {
|
||||||
&& cursor.column == current_indent.len
|
// Don't do anything if already at suggested indent
|
||||||
&& current_indent.len == suggested_indent.len
|
// and there is any other cursor which is not
|
||||||
{
|
if has_some_cursor_in_whitespace
|
||||||
continue;
|
&& cursor.column == current_indent.len
|
||||||
}
|
&& current_indent.len == suggested_indent.len
|
||||||
|
{
|
||||||
// Adjust line and move cursor to suggested indent
|
continue;
|
||||||
// if cursor is not at suggested indent
|
|
||||||
if cursor.column < suggested_indent.len
|
|
||||||
&& cursor.column <= current_indent.len
|
|
||||||
&& current_indent.len <= suggested_indent.len
|
|
||||||
{
|
|
||||||
selection.start = Point::new(cursor.row, suggested_indent.len);
|
|
||||||
selection.end = selection.start;
|
|
||||||
if row_delta == 0 {
|
|
||||||
edits.extend(Buffer::edit_for_indent_size_adjustment(
|
|
||||||
cursor.row,
|
|
||||||
current_indent,
|
|
||||||
suggested_indent,
|
|
||||||
));
|
|
||||||
row_delta = suggested_indent.len - current_indent.len;
|
|
||||||
}
|
}
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If current indent is more than suggested indent
|
// Adjust line and move cursor to suggested indent
|
||||||
// only move cursor to current indent and skip indent
|
// if cursor is not at suggested indent
|
||||||
if cursor.column < current_indent.len && current_indent.len > suggested_indent.len {
|
if cursor.column < suggested_indent.len
|
||||||
selection.start = Point::new(cursor.row, current_indent.len);
|
&& cursor.column <= current_indent.len
|
||||||
selection.end = selection.start;
|
&& current_indent.len <= suggested_indent.len
|
||||||
continue;
|
{
|
||||||
|
selection.start = Point::new(cursor.row, suggested_indent.len);
|
||||||
|
selection.end = selection.start;
|
||||||
|
if row_delta == 0 {
|
||||||
|
edits.extend(Buffer::edit_for_indent_size_adjustment(
|
||||||
|
cursor.row,
|
||||||
|
current_indent,
|
||||||
|
suggested_indent,
|
||||||
|
));
|
||||||
|
row_delta = suggested_indent.len - current_indent.len;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If current indent is more than suggested indent
|
||||||
|
// only move cursor to current indent and skip indent
|
||||||
|
if cursor.column < current_indent.len
|
||||||
|
&& current_indent.len > suggested_indent.len
|
||||||
|
{
|
||||||
|
selection.start = Point::new(cursor.row, current_indent.len);
|
||||||
|
selection.end = selection.start;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise, insert a hard or soft tab.
|
// Otherwise, insert a hard or soft tab.
|
||||||
let settings = buffer.language_settings_at(cursor, cx);
|
let settings = buffer.language_settings_at(cursor, cx);
|
||||||
let tab_size = if settings.hard_tabs {
|
let indent_size = if settings.hard_tabs {
|
||||||
IndentSize::tab()
|
IndentSize::tab()
|
||||||
} else {
|
} else {
|
||||||
let tab_size = settings.tab_size.get();
|
let tab_size = settings.tab_size.get();
|
||||||
let indent_remainder = snapshot
|
// When we have multiple cursors at the beginning of their lines,
|
||||||
.text_for_range(Point::new(cursor.row, 0)..cursor)
|
// don't use row_delta in the calculation to ensure consistent indentation
|
||||||
.flat_map(str::chars)
|
let indent_remainder = if multiple_cursors_at_line_start && cursor.column == 0 {
|
||||||
.fold(row_delta % tab_size, |counter: u32, c| {
|
0
|
||||||
if c == '\t' {
|
} else {
|
||||||
0
|
snapshot
|
||||||
} else {
|
.text_for_range(Point::new(cursor.row, 0)..cursor)
|
||||||
(counter + 1) % tab_size
|
.flat_map(str::chars)
|
||||||
}
|
.fold(row_delta % tab_size, |counter: u32, c| {
|
||||||
});
|
if c == '\t' {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
(counter + 1) % tab_size
|
||||||
|
}
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
let chars_to_next_tab_stop = tab_size - indent_remainder;
|
let chars_to_next_tab_stop = tab_size - indent_remainder;
|
||||||
IndentSize::spaces(chars_to_next_tab_stop)
|
IndentSize::spaces(chars_to_next_tab_stop)
|
||||||
};
|
};
|
||||||
selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
|
|
||||||
|
// When we have multiple cursors at line start, don't apply row_delta
|
||||||
|
let new_column = if multiple_cursors_at_line_start && cursor.column == 0 {
|
||||||
|
cursor.column + indent_size.len
|
||||||
|
} else {
|
||||||
|
cursor.column + row_delta + indent_size.len
|
||||||
|
};
|
||||||
|
|
||||||
|
selection.start = Point::new(cursor.row, new_column);
|
||||||
selection.end = selection.start;
|
selection.end = selection.start;
|
||||||
edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
|
edits.push((cursor..cursor, indent_size.chars().collect::<String>()));
|
||||||
row_delta += tab_size.len;
|
row_delta += indent_size.len;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.transact(window, cx, |this, window, cx| {
|
self.transact(window, cx, |this, window, cx| {
|
||||||
|
|||||||
@@ -3468,6 +3468,123 @@ async fn test_indent_outdent(cx: &mut TestAppContext) {
|
|||||||
"});
|
"});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[gpui::test]
|
||||||
|
async fn test_indent_yaml_comments_with_multiple_cursors(cx: &mut TestAppContext) {
|
||||||
|
init_test(cx, |_| {});
|
||||||
|
|
||||||
|
let mut cx = EditorTestContext::new(cx).await;
|
||||||
|
let yaml_language = Arc::new(Language::new(
|
||||||
|
LanguageConfig {
|
||||||
|
name: "YAML".into(),
|
||||||
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["yml".to_string(), "yaml".to_string()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
line_comments: vec!["# ".into()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
));
|
||||||
|
cx.update_buffer(|buffer, cx| buffer.set_language(Some(yaml_language), cx));
|
||||||
|
|
||||||
|
cx.set_state(indoc! {"
|
||||||
|
ˇ# ingress:
|
||||||
|
ˇ# api:
|
||||||
|
ˇ# enabled: false
|
||||||
|
ˇ# pathType: Prefix
|
||||||
|
ˇ# console:
|
||||||
|
ˇ# enabled: false
|
||||||
|
ˇ# pathType: Prefix
|
||||||
|
"});
|
||||||
|
|
||||||
|
// Press tab to indent all lines
|
||||||
|
cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
|
||||||
|
|
||||||
|
cx.assert_editor_state(indoc! {"
|
||||||
|
ˇ# ingress:
|
||||||
|
ˇ# api:
|
||||||
|
ˇ# enabled: false
|
||||||
|
ˇ# pathType: Prefix
|
||||||
|
ˇ# console:
|
||||||
|
ˇ# enabled: false
|
||||||
|
ˇ# pathType: Prefix
|
||||||
|
"});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[gpui::test]
|
||||||
|
async fn test_indent_yaml_comments_simple(cx: &mut TestAppContext) {
|
||||||
|
init_test(cx, |_| {});
|
||||||
|
|
||||||
|
let mut cx = EditorTestContext::new(cx).await;
|
||||||
|
let yaml_language = Arc::new(Language::new(
|
||||||
|
LanguageConfig {
|
||||||
|
name: "YAML".into(),
|
||||||
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["yml".to_string(), "yaml".to_string()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
line_comments: vec!["# ".into()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
));
|
||||||
|
cx.update_buffer(|buffer, cx| buffer.set_language(Some(yaml_language), cx));
|
||||||
|
|
||||||
|
cx.set_state(indoc! {"
|
||||||
|
ˇ# foo
|
||||||
|
ˇ# bar
|
||||||
|
"});
|
||||||
|
|
||||||
|
cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
|
||||||
|
|
||||||
|
cx.assert_editor_state(indoc! {"
|
||||||
|
ˇ# foo
|
||||||
|
ˇ# bar
|
||||||
|
"});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[gpui::test]
|
||||||
|
async fn test_indent_yaml_comments_full(cx: &mut TestAppContext) {
|
||||||
|
init_test(cx, |_| {});
|
||||||
|
|
||||||
|
let mut cx = EditorTestContext::new(cx).await;
|
||||||
|
let yaml_language = Arc::new(Language::new(
|
||||||
|
LanguageConfig {
|
||||||
|
name: "YAML".into(),
|
||||||
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["yml".to_string(), "yaml".to_string()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
line_comments: vec!["# ".into()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
));
|
||||||
|
cx.update_buffer(|buffer, cx| buffer.set_language(Some(yaml_language), cx));
|
||||||
|
|
||||||
|
cx.set_state(indoc! {"
|
||||||
|
ˇ# ingress:
|
||||||
|
ˇ# api:
|
||||||
|
ˇ# enabled: false
|
||||||
|
ˇ# pathType: Prefix
|
||||||
|
ˇ# console:
|
||||||
|
ˇ# enabled: false
|
||||||
|
ˇ# pathType: Prefix
|
||||||
|
"});
|
||||||
|
|
||||||
|
cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
|
||||||
|
|
||||||
|
cx.assert_editor_state(indoc! {"
|
||||||
|
ˇ# ingress:
|
||||||
|
ˇ# api:
|
||||||
|
ˇ# enabled: false
|
||||||
|
ˇ# pathType: Prefix
|
||||||
|
ˇ# console:
|
||||||
|
ˇ# enabled: false
|
||||||
|
ˇ# pathType: Prefix
|
||||||
|
"});
|
||||||
|
}
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
async fn test_indent_outdent_with_hard_tabs(cx: &mut TestAppContext) {
|
async fn test_indent_outdent_with_hard_tabs(cx: &mut TestAppContext) {
|
||||||
init_test(cx, |settings| {
|
init_test(cx, |settings| {
|
||||||
|
|||||||
Reference in New Issue
Block a user