diff options
author | Aleksey Kladov <[email protected]> | 2018-09-16 10:54:24 +0100 |
---|---|---|
committer | Aleksey Kladov <[email protected]> | 2018-09-16 11:07:39 +0100 |
commit | b5021411a84822cb3f1e3aeffad9550dd15bdeb6 (patch) | |
tree | 9dca564f8e51b298dced01c4ce669c756dce3142 /crates/ra_editor/src/extend_selection.rs | |
parent | ba0bfeee12e19da40b5eabc8d0408639af10e96f (diff) |
rename all things
Diffstat (limited to 'crates/ra_editor/src/extend_selection.rs')
-rw-r--r-- | crates/ra_editor/src/extend_selection.rs | 167 |
1 files changed, 167 insertions, 0 deletions
diff --git a/crates/ra_editor/src/extend_selection.rs b/crates/ra_editor/src/extend_selection.rs new file mode 100644 index 000000000..5fd1ca4fc --- /dev/null +++ b/crates/ra_editor/src/extend_selection.rs | |||
@@ -0,0 +1,167 @@ | |||
1 | use ra_syntax::{ | ||
2 | File, TextRange, SyntaxNodeRef, TextUnit, | ||
3 | SyntaxKind::*, | ||
4 | algo::{find_leaf_at_offset, LeafAtOffset, find_covering_node, ancestors, Direction, siblings}, | ||
5 | }; | ||
6 | |||
7 | pub fn extend_selection(file: &File, range: TextRange) -> Option<TextRange> { | ||
8 | let syntax = file.syntax(); | ||
9 | extend(syntax.borrowed(), range) | ||
10 | } | ||
11 | |||
12 | pub(crate) fn extend(root: SyntaxNodeRef, range: TextRange) -> Option<TextRange> { | ||
13 | if range.is_empty() { | ||
14 | let offset = range.start(); | ||
15 | let mut leaves = find_leaf_at_offset(root, offset); | ||
16 | if leaves.clone().all(|it| it.kind() == WHITESPACE) { | ||
17 | return Some(extend_ws(root, leaves.next()?, offset)); | ||
18 | } | ||
19 | let leaf = match leaves { | ||
20 | LeafAtOffset::None => return None, | ||
21 | LeafAtOffset::Single(l) => l, | ||
22 | LeafAtOffset::Between(l, r) => pick_best(l, r), | ||
23 | }; | ||
24 | return Some(leaf.range()); | ||
25 | }; | ||
26 | let node = find_covering_node(root, range); | ||
27 | if node.kind() == COMMENT && range == node.range() { | ||
28 | if let Some(range) = extend_comments(node) { | ||
29 | return Some(range); | ||
30 | } | ||
31 | } | ||
32 | |||
33 | match ancestors(node).skip_while(|n| n.range() == range).next() { | ||
34 | None => None, | ||
35 | Some(parent) => Some(parent.range()), | ||
36 | } | ||
37 | } | ||
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 | |||
73 | fn extend_comments(node: SyntaxNodeRef) -> Option<TextRange> { | ||
74 | let left = adj_comments(node, Direction::Backward); | ||
75 | let right = adj_comments(node, Direction::Forward); | ||
76 | if left != right { | ||
77 | Some(TextRange::from_to( | ||
78 | left.range().start(), | ||
79 | right.range().end(), | ||
80 | )) | ||
81 | } else { | ||
82 | None | ||
83 | } | ||
84 | } | ||
85 | |||
86 | fn adj_comments(node: SyntaxNodeRef, dir: Direction) -> SyntaxNodeRef { | ||
87 | let mut res = node; | ||
88 | for node in siblings(node, dir) { | ||
89 | match node.kind() { | ||
90 | COMMENT => res = node, | ||
91 | WHITESPACE if !node.leaf_text().unwrap().as_str().contains("\n\n") => (), | ||
92 | _ => break | ||
93 | } | ||
94 | } | ||
95 | res | ||
96 | } | ||
97 | |||
98 | #[cfg(test)] | ||
99 | mod tests { | ||
100 | use super::*; | ||
101 | use test_utils::extract_offset; | ||
102 | |||
103 | fn do_check(before: &str, afters: &[&str]) { | ||
104 | let (cursor, before) = extract_offset(before); | ||
105 | let file = File::parse(&before); | ||
106 | let mut range = TextRange::offset_len(cursor, 0.into()); | ||
107 | for &after in afters { | ||
108 | range = extend_selection(&file, range) | ||
109 | .unwrap(); | ||
110 | let actual = &before[range]; | ||
111 | assert_eq!(after, actual); | ||
112 | } | ||
113 | } | ||
114 | |||
115 | #[test] | ||
116 | fn test_extend_selection_arith() { | ||
117 | do_check( | ||
118 | r#"fn foo() { <|>1 + 1 }"#, | ||
119 | &["1", "1 + 1", "{ 1 + 1 }"], | ||
120 | ); | ||
121 | } | ||
122 | |||
123 | #[test] | ||
124 | fn test_extend_selection_start_of_the_lind() { | ||
125 | do_check( | ||
126 | r#" | ||
127 | impl S { | ||
128 | <|> fn foo() { | ||
129 | |||
130 | } | ||
131 | }"#, | ||
132 | &[" fn foo() {\n\n }\n"] | ||
133 | ); | ||
134 | } | ||
135 | |||
136 | #[test] | ||
137 | fn test_extend_selection_comments() { | ||
138 | do_check( | ||
139 | r#" | ||
140 | fn bar(){} | ||
141 | |||
142 | // fn foo() { | ||
143 | // 1 + <|>1 | ||
144 | // } | ||
145 | |||
146 | // fn foo(){} | ||
147 | "#, | ||
148 | &["// 1 + 1", "// fn foo() {\n// 1 + 1\n// }"] | ||
149 | ); | ||
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 | } | ||
167 | } | ||