diff options
Diffstat (limited to 'crates/libeditor')
-rw-r--r-- | crates/libeditor/src/extend_selection.rs | 83 |
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 @@ | |||
1 | use libsyntax2::{ | 1 | use 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 | ||
7 | pub fn extend_selection(file: &File, range: TextRange) -> Option<TextRange> { | 7 | pub 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 | ||
39 | fn 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 | |||
62 | fn 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 | |||
54 | fn extend_comments(node: SyntaxNodeRef) -> Option<TextRange> { | 73 | fn 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#" | ||
156 | fn main() { foo<|>+bar;} | ||
157 | "#, | ||
158 | &["foo", "foo+bar"] | ||
159 | ); | ||
160 | do_check( | ||
161 | r#" | ||
162 | fn main() { foo+<|>bar;} | ||
163 | "#, | ||
164 | &["bar", "foo+bar"] | ||
165 | ); | ||
166 | } | ||
132 | } | 167 | } |