aboutsummaryrefslogtreecommitdiff
path: root/crates/libeditor
diff options
context:
space:
mode:
Diffstat (limited to 'crates/libeditor')
-rw-r--r--crates/libeditor/src/extend_selection.rs83
1 files changed, 59 insertions, 24 deletions
diff --git a/crates/libeditor/src/extend_selection.rs b/crates/libeditor/src/extend_selection.rs
index 3adb1e45e..9dc59e254 100644
--- a/crates/libeditor/src/extend_selection.rs
+++ b/crates/libeditor/src/extend_selection.rs
@@ -1,7 +1,7 @@
1use libsyntax2::{ 1use libsyntax2::{
2 File, TextRange, SyntaxNodeRef, TextUnit, 2 File, TextRange, SyntaxNodeRef, TextUnit,
3 SyntaxKind::*, 3 SyntaxKind::*,
4 algo::{find_leaf_at_offset, find_covering_node, ancestors, Direction, siblings}, 4 algo::{find_leaf_at_offset, LeafAtOffset, find_covering_node, ancestors, Direction, siblings},
5}; 5};
6 6
7pub fn extend_selection(file: &File, range: TextRange) -> Option<TextRange> { 7pub fn extend_selection(file: &File, range: TextRange) -> Option<TextRange> {
@@ -13,30 +13,15 @@ pub(crate) fn extend(root: SyntaxNodeRef, range: TextRange) -> Option<TextRange>
13 if range.is_empty() { 13 if range.is_empty() {
14 let offset = range.start(); 14 let offset = range.start();
15 let mut leaves = find_leaf_at_offset(root, offset); 15 let mut leaves = find_leaf_at_offset(root, offset);
16 if let Some(leaf) = leaves.clone().find(|node| node.kind() != WHITESPACE) { 16 if leaves.clone().all(|it| it.kind() == WHITESPACE) {
17 return Some(leaf.range()); 17 return Some(extend_ws(root, leaves.next()?, offset));
18 } 18 }
19 let ws = leaves.next()?; 19 let leaf = match leaves {
20 let ws_text = ws.leaf_text().unwrap(); 20 LeafAtOffset::None => return None,
21 let suffix = TextRange::from_to(offset, ws.range().end()) - ws.range().start(); 21 LeafAtOffset::Single(l) => l,
22 let prefix = TextRange::from_to(ws.range().start(), offset) - ws.range().start(); 22 LeafAtOffset::Between(l, r) => pick_best(l, r),
23 let ws_suffix = &ws_text.as_str()[suffix]; 23 };
24 let ws_prefix = &ws_text.as_str()[prefix]; 24 return Some(leaf.range());
25 if ws_text.contains("\n") && !ws_suffix.contains("\n") {
26 if let Some(node) = ws.next_sibling() {
27 let start = match ws_prefix.rfind('\n') {
28 Some(idx) => ws.range().start() + TextUnit::from((idx + 1) as u32),
29 None => node.range().start()
30 };
31 let end = if root.text().char_at(node.range().end()) == Some('\n') {
32 node.range().end() + TextUnit::of_char('\n')
33 } else {
34 node.range().end()
35 };
36 return Some(TextRange::from_to(start, end));
37 }
38 }
39 return Some(ws.range());
40 }; 25 };
41 let node = find_covering_node(root, range); 26 let node = find_covering_node(root, range);
42 if node.kind() == COMMENT && range == node.range() { 27 if node.kind() == COMMENT && range == node.range() {
@@ -51,6 +36,40 @@ pub(crate) fn extend(root: SyntaxNodeRef, range: TextRange) -> Option<TextRange>
51 } 36 }
52} 37}
53 38
39fn extend_ws(root: SyntaxNodeRef, ws: SyntaxNodeRef, offset: TextUnit) -> TextRange {
40 let ws_text = ws.leaf_text().unwrap();
41 let suffix = TextRange::from_to(offset, ws.range().end()) - ws.range().start();
42 let prefix = TextRange::from_to(ws.range().start(), offset) - ws.range().start();
43 let ws_suffix = &ws_text.as_str()[suffix];
44 let ws_prefix = &ws_text.as_str()[prefix];
45 if ws_text.contains("\n") && !ws_suffix.contains("\n") {
46 if let Some(node) = ws.next_sibling() {
47 let start = match ws_prefix.rfind('\n') {
48 Some(idx) => ws.range().start() + TextUnit::from((idx + 1) as u32),
49 None => node.range().start()
50 };
51 let end = if root.text().char_at(node.range().end()) == Some('\n') {
52 node.range().end() + TextUnit::of_char('\n')
53 } else {
54 node.range().end()
55 };
56 return TextRange::from_to(start, end);
57 }
58 }
59 ws.range()
60}
61
62fn pick_best<'a>(l: SyntaxNodeRef<'a>, r: SyntaxNodeRef<'a>) -> SyntaxNodeRef<'a> {
63 return if priority(r) > priority(l) { r } else { l };
64 fn priority(n: SyntaxNodeRef) -> usize {
65 match n.kind() {
66 WHITESPACE => 0,
67 IDENT | SELF_KW | SUPER_KW | CRATE_KW => 2,
68 _ => 1,
69 }
70 }
71}
72
54fn extend_comments(node: SyntaxNodeRef) -> Option<TextRange> { 73fn extend_comments(node: SyntaxNodeRef) -> Option<TextRange> {
55 let left = adj_comments(node, Direction::Backward); 74 let left = adj_comments(node, Direction::Backward);
56 let right = adj_comments(node, Direction::Forward); 75 let right = adj_comments(node, Direction::Forward);
@@ -129,4 +148,20 @@ fn bar(){}
129 &["// 1 + 1", "// fn foo() {\n// 1 + 1\n// }"] 148 &["// 1 + 1", "// fn foo() {\n// 1 + 1\n// }"]
130 ); 149 );
131 } 150 }
151
152 #[test]
153 fn test_extend_selection_prefer_idents() {
154 do_check(
155 r#"
156fn main() { foo<|>+bar;}
157 "#,
158 &["foo", "foo+bar"]
159 );
160 do_check(
161 r#"
162fn main() { foo+<|>bar;}
163 "#,
164 &["bar", "foo+bar"]
165 );
166 }
132} 167}