diff options
author | Dmitry <[email protected]> | 2020-08-14 19:32:05 +0100 |
---|---|---|
committer | Dmitry <[email protected]> | 2020-08-14 19:32:05 +0100 |
commit | 178c3e135a2a249692f7784712492e7884ae0c00 (patch) | |
tree | ac6b769dbf7162150caa0c1624786a4dd79ff3be /crates/ide/src/matching_brace.rs | |
parent | 06ff8e6c760ff05f10e868b5d1f9d79e42fbb49c (diff) | |
parent | c2594daf2974dbd4ce3d9b7ec72481764abaceb5 (diff) |
Merge remote-tracking branch 'origin/master'
Diffstat (limited to 'crates/ide/src/matching_brace.rs')
-rw-r--r-- | crates/ide/src/matching_brace.rs | 73 |
1 files changed, 73 insertions, 0 deletions
diff --git a/crates/ide/src/matching_brace.rs b/crates/ide/src/matching_brace.rs new file mode 100644 index 000000000..cb6abb0db --- /dev/null +++ b/crates/ide/src/matching_brace.rs | |||
@@ -0,0 +1,73 @@ | |||
1 | use syntax::{ | ||
2 | ast::{self, AstNode}, | ||
3 | SourceFile, SyntaxKind, TextSize, T, | ||
4 | }; | ||
5 | use test_utils::mark; | ||
6 | |||
7 | // Feature: Matching Brace | ||
8 | // | ||
9 | // If the cursor is on any brace (`<>(){}[]||`) which is a part of a brace-pair, | ||
10 | // moves cursor to the matching brace. It uses the actual parser to determine | ||
11 | // braces, so it won't confuse generics with comparisons. | ||
12 | // | ||
13 | // |=== | ||
14 | // | Editor | Action Name | ||
15 | // | ||
16 | // | VS Code | **Rust Analyzer: Find matching brace** | ||
17 | // |=== | ||
18 | pub fn matching_brace(file: &SourceFile, offset: TextSize) -> Option<TextSize> { | ||
19 | const BRACES: &[SyntaxKind] = | ||
20 | &[T!['{'], T!['}'], T!['['], T![']'], T!['('], T![')'], T![<], T![>], T![|], T![|]]; | ||
21 | let (brace_token, brace_idx) = file | ||
22 | .syntax() | ||
23 | .token_at_offset(offset) | ||
24 | .filter_map(|node| { | ||
25 | let idx = BRACES.iter().position(|&brace| brace == node.kind())?; | ||
26 | Some((node, idx)) | ||
27 | }) | ||
28 | .next()?; | ||
29 | let parent = brace_token.parent(); | ||
30 | if brace_token.kind() == T![|] && !ast::ParamList::can_cast(parent.kind()) { | ||
31 | mark::hit!(pipes_not_braces); | ||
32 | return None; | ||
33 | } | ||
34 | let matching_kind = BRACES[brace_idx ^ 1]; | ||
35 | let matching_node = parent | ||
36 | .children_with_tokens() | ||
37 | .filter_map(|it| it.into_token()) | ||
38 | .find(|node| node.kind() == matching_kind && node != &brace_token)?; | ||
39 | Some(matching_node.text_range().start()) | ||
40 | } | ||
41 | |||
42 | #[cfg(test)] | ||
43 | mod tests { | ||
44 | use test_utils::{add_cursor, assert_eq_text, extract_offset}; | ||
45 | |||
46 | use super::*; | ||
47 | |||
48 | #[test] | ||
49 | fn test_matching_brace() { | ||
50 | fn do_check(before: &str, after: &str) { | ||
51 | let (pos, before) = extract_offset(before); | ||
52 | let parse = SourceFile::parse(&before); | ||
53 | let new_pos = match matching_brace(&parse.tree(), pos) { | ||
54 | None => pos, | ||
55 | Some(pos) => pos, | ||
56 | }; | ||
57 | let actual = add_cursor(&before, new_pos); | ||
58 | assert_eq_text!(after, &actual); | ||
59 | } | ||
60 | |||
61 | do_check("struct Foo { a: i32, }<|>", "struct Foo <|>{ a: i32, }"); | ||
62 | do_check("fn main() { |x: i32|<|> x * 2;}", "fn main() { <|>|x: i32| x * 2;}"); | ||
63 | do_check("fn main() { <|>|x: i32| x * 2;}", "fn main() { |x: i32<|>| x * 2;}"); | ||
64 | |||
65 | { | ||
66 | mark::check!(pipes_not_braces); | ||
67 | do_check( | ||
68 | "fn main() { match 92 { 1 | 2 |<|> 3 => 92 } }", | ||
69 | "fn main() { match 92 { 1 | 2 |<|> 3 => 92 } }", | ||
70 | ); | ||
71 | } | ||
72 | } | ||
73 | } | ||