language: Fix larger syntax node when cursor is at end of word or line (#29978)
Closes #28699 Fixes two cases in the `editor::SelectLargerSyntaxNode` action: 1. When cursor is at the end of a word, it now selects that word first instead of selecting the whole line. 2. When cursor is at the end of a line, it now selects that line first instead of selecting the whole code block. Before and After: https://github.com/user-attachments/assets/233b891e-15f1-4f10-a51f-75693323c2bd Release Notes: - Fixed `editor::SelectLargerSyntaxNode` to properly select nodes when the cursor is positioned at the end of words or lines.
This commit is contained in:
parent
9d97e08e4f
commit
5640265160
2 changed files with 72 additions and 2 deletions
|
@ -6402,6 +6402,68 @@ async fn test_select_larger_smaller_syntax_node(cx: &mut TestAppContext) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[gpui::test]
|
||||||
|
async fn test_select_larger_syntax_node_for_cursor_at_end(cx: &mut TestAppContext) {
|
||||||
|
init_test(cx, |_| {});
|
||||||
|
|
||||||
|
let language = Arc::new(Language::new(
|
||||||
|
LanguageConfig::default(),
|
||||||
|
Some(tree_sitter_rust::LANGUAGE.into()),
|
||||||
|
));
|
||||||
|
|
||||||
|
let text = "let a = 2;";
|
||||||
|
|
||||||
|
let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
|
||||||
|
let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
|
||||||
|
let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
|
||||||
|
|
||||||
|
editor
|
||||||
|
.condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
|
||||||
|
.await;
|
||||||
|
|
||||||
|
// Test case 1: Cursor at end of word
|
||||||
|
editor.update_in(cx, |editor, window, cx| {
|
||||||
|
editor.change_selections(None, window, cx, |s| {
|
||||||
|
s.select_display_ranges([
|
||||||
|
DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5)
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
editor.update(cx, |editor, cx| {
|
||||||
|
assert_text_with_selections(editor, "let aˇ = 2;", cx);
|
||||||
|
});
|
||||||
|
editor.update_in(cx, |editor, window, cx| {
|
||||||
|
editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
|
||||||
|
});
|
||||||
|
editor.update(cx, |editor, cx| {
|
||||||
|
assert_text_with_selections(editor, "let «ˇa» = 2;", cx);
|
||||||
|
});
|
||||||
|
editor.update_in(cx, |editor, window, cx| {
|
||||||
|
editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
|
||||||
|
});
|
||||||
|
editor.update(cx, |editor, cx| {
|
||||||
|
assert_text_with_selections(editor, "«ˇlet a = 2;»", cx);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Test case 2: Cursor at end of statement
|
||||||
|
editor.update_in(cx, |editor, window, cx| {
|
||||||
|
editor.change_selections(None, window, cx, |s| {
|
||||||
|
s.select_display_ranges([
|
||||||
|
DisplayPoint::new(DisplayRow(0), 11)..DisplayPoint::new(DisplayRow(0), 11)
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
editor.update(cx, |editor, cx| {
|
||||||
|
assert_text_with_selections(editor, "let a = 2;ˇ", cx);
|
||||||
|
});
|
||||||
|
editor.update_in(cx, |editor, window, cx| {
|
||||||
|
editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
|
||||||
|
});
|
||||||
|
editor.update(cx, |editor, cx| {
|
||||||
|
assert_text_with_selections(editor, "«ˇlet a = 2;»", cx);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
async fn test_select_larger_smaller_syntax_node_for_string(cx: &mut TestAppContext) {
|
async fn test_select_larger_smaller_syntax_node_for_string(cx: &mut TestAppContext) {
|
||||||
init_test(cx, |_| {});
|
init_test(cx, |_| {});
|
||||||
|
|
|
@ -3307,12 +3307,20 @@ impl BufferSnapshot {
|
||||||
{
|
{
|
||||||
let mut cursor = layer.node().walk();
|
let mut cursor = layer.node().walk();
|
||||||
|
|
||||||
// Descend to the first leaf that touches the start of the range,
|
// Descend to the first leaf that touches the start of the range.
|
||||||
// and if the range is non-empty, extends beyond the start.
|
//
|
||||||
|
// If the range is non-empty and the current node ends exactly at the start,
|
||||||
|
// move to the next sibling to find a node that extends beyond the start.
|
||||||
|
//
|
||||||
|
// If the range is empty and the current node starts after the range position,
|
||||||
|
// move to the previous sibling to find the node that contains the position.
|
||||||
while cursor.goto_first_child_for_byte(range.start).is_some() {
|
while cursor.goto_first_child_for_byte(range.start).is_some() {
|
||||||
if !range.is_empty() && cursor.node().end_byte() == range.start {
|
if !range.is_empty() && cursor.node().end_byte() == range.start {
|
||||||
cursor.goto_next_sibling();
|
cursor.goto_next_sibling();
|
||||||
}
|
}
|
||||||
|
if range.is_empty() && cursor.node().start_byte() > range.start {
|
||||||
|
cursor.goto_previous_sibling();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ascend to the smallest ancestor that strictly contains the range.
|
// Ascend to the smallest ancestor that strictly contains the range.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue