snippets: Preserve leading whitespace (#31933)
Closes #18481 Release Notes: - Snippet insertions now preserve leading whitespace instead of using language-specific auto-indentation.
This commit is contained in:
parent
feeda7fa37
commit
56d4c0af9f
3 changed files with 119 additions and 98 deletions
|
@ -8929,7 +8929,10 @@ impl Editor {
|
||||||
.iter()
|
.iter()
|
||||||
.cloned()
|
.cloned()
|
||||||
.map(|range| (range, snippet_text.clone()));
|
.map(|range| (range, snippet_text.clone()));
|
||||||
buffer.edit(edits, Some(AutoindentMode::EachLine), cx);
|
let autoindent_mode = AutoindentMode::Block {
|
||||||
|
original_indent_columns: Vec::new(),
|
||||||
|
};
|
||||||
|
buffer.edit(edits, Some(autoindent_mode), cx);
|
||||||
|
|
||||||
let snapshot = &*buffer.read(cx);
|
let snapshot = &*buffer.read(cx);
|
||||||
let snippet = &snippet;
|
let snippet = &snippet;
|
||||||
|
|
|
@ -8513,108 +8513,123 @@ async fn test_snippet_placeholder_choices(cx: &mut TestAppContext) {
|
||||||
async fn test_snippets(cx: &mut TestAppContext) {
|
async fn test_snippets(cx: &mut TestAppContext) {
|
||||||
init_test(cx, |_| {});
|
init_test(cx, |_| {});
|
||||||
|
|
||||||
let (text, insertion_ranges) = marked_text_ranges(
|
let mut cx = EditorTestContext::new(cx).await;
|
||||||
indoc! {"
|
|
||||||
a.ˇ b
|
|
||||||
a.ˇ b
|
|
||||||
a.ˇ b
|
|
||||||
"},
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
|
|
||||||
let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
|
cx.set_state(indoc! {"
|
||||||
let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
|
a.ˇ b
|
||||||
|
a.ˇ b
|
||||||
|
a.ˇ b
|
||||||
|
"});
|
||||||
|
|
||||||
editor.update_in(cx, |editor, window, cx| {
|
cx.update_editor(|editor, window, cx| {
|
||||||
let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
|
let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
|
||||||
|
let insertion_ranges = editor
|
||||||
|
.selections
|
||||||
|
.all(cx)
|
||||||
|
.iter()
|
||||||
|
.map(|s| s.range().clone())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
editor
|
editor
|
||||||
.insert_snippet(&insertion_ranges, snippet, window, cx)
|
.insert_snippet(&insertion_ranges, snippet, window, cx)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
});
|
||||||
|
|
||||||
fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
|
cx.assert_editor_state(indoc! {"
|
||||||
let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
|
a.f(«oneˇ», two, «threeˇ») b
|
||||||
assert_eq!(editor.text(cx), expected_text);
|
a.f(«oneˇ», two, «threeˇ») b
|
||||||
assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
|
a.f(«oneˇ», two, «threeˇ») b
|
||||||
}
|
"});
|
||||||
|
|
||||||
assert(
|
|
||||||
editor,
|
|
||||||
cx,
|
|
||||||
indoc! {"
|
|
||||||
a.f(«one», two, «three») b
|
|
||||||
a.f(«one», two, «three») b
|
|
||||||
a.f(«one», two, «three») b
|
|
||||||
"},
|
|
||||||
);
|
|
||||||
|
|
||||||
// Can't move earlier than the first tab stop
|
// Can't move earlier than the first tab stop
|
||||||
assert!(!editor.move_to_prev_snippet_tabstop(window, cx));
|
cx.update_editor(|editor, window, cx| {
|
||||||
assert(
|
assert!(!editor.move_to_prev_snippet_tabstop(window, cx))
|
||||||
editor,
|
});
|
||||||
cx,
|
cx.assert_editor_state(indoc! {"
|
||||||
indoc! {"
|
a.f(«oneˇ», two, «threeˇ») b
|
||||||
a.f(«one», two, «three») b
|
a.f(«oneˇ», two, «threeˇ») b
|
||||||
a.f(«one», two, «three») b
|
a.f(«oneˇ», two, «threeˇ») b
|
||||||
a.f(«one», two, «three») b
|
"});
|
||||||
"},
|
|
||||||
);
|
|
||||||
|
|
||||||
assert!(editor.move_to_next_snippet_tabstop(window, cx));
|
cx.update_editor(|editor, window, cx| assert!(editor.move_to_next_snippet_tabstop(window, cx)));
|
||||||
assert(
|
cx.assert_editor_state(indoc! {"
|
||||||
editor,
|
a.f(one, «twoˇ», three) b
|
||||||
cx,
|
a.f(one, «twoˇ», three) b
|
||||||
indoc! {"
|
a.f(one, «twoˇ», three) b
|
||||||
a.f(one, «two», three) b
|
"});
|
||||||
a.f(one, «two», three) b
|
|
||||||
a.f(one, «two», three) b
|
|
||||||
"},
|
|
||||||
);
|
|
||||||
|
|
||||||
editor.move_to_prev_snippet_tabstop(window, cx);
|
cx.update_editor(|editor, window, cx| assert!(editor.move_to_prev_snippet_tabstop(window, cx)));
|
||||||
assert(
|
cx.assert_editor_state(indoc! {"
|
||||||
editor,
|
a.f(«oneˇ», two, «threeˇ») b
|
||||||
cx,
|
a.f(«oneˇ», two, «threeˇ») b
|
||||||
indoc! {"
|
a.f(«oneˇ», two, «threeˇ») b
|
||||||
a.f(«one», two, «three») b
|
"});
|
||||||
a.f(«one», two, «three») b
|
|
||||||
a.f(«one», two, «three») b
|
|
||||||
"},
|
|
||||||
);
|
|
||||||
|
|
||||||
assert!(editor.move_to_next_snippet_tabstop(window, cx));
|
cx.update_editor(|editor, window, cx| assert!(editor.move_to_next_snippet_tabstop(window, cx)));
|
||||||
assert(
|
cx.assert_editor_state(indoc! {"
|
||||||
editor,
|
a.f(one, «twoˇ», three) b
|
||||||
cx,
|
a.f(one, «twoˇ», three) b
|
||||||
indoc! {"
|
a.f(one, «twoˇ», three) b
|
||||||
a.f(one, «two», three) b
|
"});
|
||||||
a.f(one, «two», three) b
|
cx.update_editor(|editor, window, cx| assert!(editor.move_to_next_snippet_tabstop(window, cx)));
|
||||||
a.f(one, «two», three) b
|
cx.assert_editor_state(indoc! {"
|
||||||
"},
|
|
||||||
);
|
|
||||||
assert!(editor.move_to_next_snippet_tabstop(window, cx));
|
|
||||||
assert(
|
|
||||||
editor,
|
|
||||||
cx,
|
|
||||||
indoc! {"
|
|
||||||
a.f(one, two, three)ˇ b
|
a.f(one, two, three)ˇ b
|
||||||
a.f(one, two, three)ˇ b
|
a.f(one, two, three)ˇ b
|
||||||
a.f(one, two, three)ˇ b
|
a.f(one, two, three)ˇ b
|
||||||
"},
|
"});
|
||||||
);
|
|
||||||
|
|
||||||
// As soon as the last tab stop is reached, snippet state is gone
|
// As soon as the last tab stop is reached, snippet state is gone
|
||||||
editor.move_to_prev_snippet_tabstop(window, cx);
|
cx.update_editor(|editor, window, cx| {
|
||||||
assert(
|
assert!(!editor.move_to_prev_snippet_tabstop(window, cx))
|
||||||
editor,
|
|
||||||
cx,
|
|
||||||
indoc! {"
|
|
||||||
a.f(one, two, three)ˇ b
|
|
||||||
a.f(one, two, three)ˇ b
|
|
||||||
a.f(one, two, three)ˇ b
|
|
||||||
"},
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
cx.assert_editor_state(indoc! {"
|
||||||
|
a.f(one, two, three)ˇ b
|
||||||
|
a.f(one, two, three)ˇ b
|
||||||
|
a.f(one, two, three)ˇ b
|
||||||
|
"});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[gpui::test]
|
||||||
|
async fn test_snippet_indentation(cx: &mut TestAppContext) {
|
||||||
|
init_test(cx, |_| {});
|
||||||
|
|
||||||
|
let mut cx = EditorTestContext::new(cx).await;
|
||||||
|
|
||||||
|
cx.update_editor(|editor, window, cx| {
|
||||||
|
let snippet = Snippet::parse(indoc! {"
|
||||||
|
/*
|
||||||
|
* Multiline comment with leading indentation
|
||||||
|
*
|
||||||
|
* $1
|
||||||
|
*/
|
||||||
|
$0"})
|
||||||
|
.unwrap();
|
||||||
|
let insertion_ranges = editor
|
||||||
|
.selections
|
||||||
|
.all(cx)
|
||||||
|
.iter()
|
||||||
|
.map(|s| s.range().clone())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
editor
|
||||||
|
.insert_snippet(&insertion_ranges, snippet, window, cx)
|
||||||
|
.unwrap();
|
||||||
|
});
|
||||||
|
|
||||||
|
cx.assert_editor_state(indoc! {"
|
||||||
|
/*
|
||||||
|
* Multiline comment with leading indentation
|
||||||
|
*
|
||||||
|
* ˇ
|
||||||
|
*/
|
||||||
|
"});
|
||||||
|
|
||||||
|
cx.update_editor(|editor, window, cx| assert!(editor.move_to_next_snippet_tabstop(window, cx)));
|
||||||
|
cx.assert_editor_state(indoc! {"
|
||||||
|
/*
|
||||||
|
* Multiline comment with leading indentation
|
||||||
|
*
|
||||||
|
*•
|
||||||
|
*/
|
||||||
|
ˇ"});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
|
|
|
@ -532,7 +532,9 @@ impl EditorTestContext {
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn assert_editor_selections(&mut self, expected_selections: Vec<Range<usize>>) {
|
pub fn assert_editor_selections(&mut self, expected_selections: Vec<Range<usize>>) {
|
||||||
let expected_marked_text =
|
let expected_marked_text =
|
||||||
generate_marked_text(&self.buffer_text(), &expected_selections, true);
|
generate_marked_text(&self.buffer_text(), &expected_selections, true)
|
||||||
|
.replace(" \n", "•\n");
|
||||||
|
|
||||||
self.assert_selections(expected_selections, expected_marked_text)
|
self.assert_selections(expected_selections, expected_marked_text)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -561,7 +563,8 @@ impl EditorTestContext {
|
||||||
) {
|
) {
|
||||||
let actual_selections = self.editor_selections();
|
let actual_selections = self.editor_selections();
|
||||||
let actual_marked_text =
|
let actual_marked_text =
|
||||||
generate_marked_text(&self.buffer_text(), &actual_selections, true);
|
generate_marked_text(&self.buffer_text(), &actual_selections, true)
|
||||||
|
.replace(" \n", "•\n");
|
||||||
if expected_selections != actual_selections {
|
if expected_selections != actual_selections {
|
||||||
pretty_assertions::assert_eq!(
|
pretty_assertions::assert_eq!(
|
||||||
actual_marked_text,
|
actual_marked_text,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue