diff options
Diffstat (limited to 'crates/libeditor')
-rw-r--r-- | crates/libeditor/src/extend_selection.rs | 7 | ||||
-rw-r--r-- | crates/libeditor/src/lib.rs | 14 | ||||
-rw-r--r-- | crates/libeditor/src/symbols.rs | 67 | ||||
-rw-r--r-- | crates/libeditor/tests/test.rs | 25 |
4 files changed, 104 insertions, 9 deletions
diff --git a/crates/libeditor/src/extend_selection.rs b/crates/libeditor/src/extend_selection.rs index 16d4bc084..ed7d9b3f7 100644 --- a/crates/libeditor/src/extend_selection.rs +++ b/crates/libeditor/src/extend_selection.rs | |||
@@ -1,11 +1,16 @@ | |||
1 | use libsyntax2::{ | 1 | use libsyntax2::{ |
2 | ast, AstNode, | ||
2 | TextRange, SyntaxNodeRef, | 3 | TextRange, SyntaxNodeRef, |
3 | SyntaxKind::WHITESPACE, | 4 | SyntaxKind::WHITESPACE, |
4 | algo::{find_leaf_at_offset, find_covering_node, ancestors}, | 5 | algo::{find_leaf_at_offset, find_covering_node, ancestors}, |
5 | }; | 6 | }; |
6 | 7 | ||
8 | pub fn extend_selection(file: &ast::File, range: TextRange) -> Option<TextRange> { | ||
9 | let syntax = file.syntax(); | ||
10 | extend(syntax.as_ref(), range) | ||
11 | } | ||
7 | 12 | ||
8 | pub(crate) fn extend_selection(root: SyntaxNodeRef, range: TextRange) -> Option<TextRange> { | 13 | pub(crate) fn extend(root: SyntaxNodeRef, range: TextRange) -> Option<TextRange> { |
9 | if range.is_empty() { | 14 | if range.is_empty() { |
10 | let offset = range.start(); | 15 | let offset = range.start(); |
11 | let mut leaves = find_leaf_at_offset(root, offset); | 16 | let mut leaves = find_leaf_at_offset(root, offset); |
diff --git a/crates/libeditor/src/lib.rs b/crates/libeditor/src/lib.rs index 293fafae7..4ea344b17 100644 --- a/crates/libeditor/src/lib.rs +++ b/crates/libeditor/src/lib.rs | |||
@@ -2,16 +2,21 @@ extern crate libsyntax2; | |||
2 | extern crate superslice; | 2 | extern crate superslice; |
3 | 3 | ||
4 | mod extend_selection; | 4 | mod extend_selection; |
5 | mod symbols; | ||
5 | mod line_index; | 6 | mod line_index; |
6 | 7 | ||
7 | use libsyntax2::{ | 8 | use libsyntax2::{ |
8 | ast, | 9 | ast::{self, NameOwner}, |
9 | SyntaxNodeRef, AstNode, | 10 | SyntaxNodeRef, AstNode, |
10 | algo::walk, | 11 | algo::walk, |
11 | SyntaxKind::*, | 12 | SyntaxKind::*, |
12 | }; | 13 | }; |
13 | pub use libsyntax2::{File, TextRange, TextUnit}; | 14 | pub use libsyntax2::{File, TextRange, TextUnit}; |
14 | pub use self::line_index::{LineIndex, LineCol}; | 15 | pub use self::{ |
16 | line_index::{LineIndex, LineCol}, | ||
17 | extend_selection::extend_selection, | ||
18 | symbols::{FileSymbol, file_symbols} | ||
19 | }; | ||
15 | 20 | ||
16 | #[derive(Debug)] | 21 | #[derive(Debug)] |
17 | pub struct HighlightedRange { | 22 | pub struct HighlightedRange { |
@@ -108,11 +113,6 @@ pub fn symbols(file: &ast::File) -> Vec<Symbol> { | |||
108 | res // NLL :-( | 113 | res // NLL :-( |
109 | } | 114 | } |
110 | 115 | ||
111 | pub fn extend_selection(file: &ast::File, range: TextRange) -> Option<TextRange> { | ||
112 | let syntax = file.syntax(); | ||
113 | extend_selection::extend_selection(syntax.as_ref(), range) | ||
114 | } | ||
115 | |||
116 | pub fn runnables(file: &ast::File) -> Vec<Runnable> { | 116 | pub fn runnables(file: &ast::File) -> Vec<Runnable> { |
117 | file | 117 | file |
118 | .functions() | 118 | .functions() |
diff --git a/crates/libeditor/src/symbols.rs b/crates/libeditor/src/symbols.rs new file mode 100644 index 000000000..3faf96868 --- /dev/null +++ b/crates/libeditor/src/symbols.rs | |||
@@ -0,0 +1,67 @@ | |||
1 | use libsyntax2::{ | ||
2 | SyntaxKind, SyntaxNodeRef, SyntaxRoot, AstNode, | ||
3 | ast::{self, NameOwner}, | ||
4 | algo::{ | ||
5 | visit::{visitor, Visitor}, | ||
6 | walk::{walk, WalkEvent}, | ||
7 | }, | ||
8 | }; | ||
9 | use TextRange; | ||
10 | |||
11 | #[derive(Debug)] | ||
12 | pub struct FileSymbol { | ||
13 | pub parent: Option<usize>, | ||
14 | pub name: String, | ||
15 | pub name_range: TextRange, | ||
16 | pub node_range: TextRange, | ||
17 | pub kind: SyntaxKind, | ||
18 | } | ||
19 | |||
20 | |||
21 | pub fn file_symbols(file: &ast::File) -> Vec<FileSymbol> { | ||
22 | let mut res = Vec::new(); | ||
23 | let mut stack = Vec::new(); | ||
24 | let syntax = file.syntax(); | ||
25 | |||
26 | for event in walk(syntax.as_ref()) { | ||
27 | match event { | ||
28 | WalkEvent::Enter(node) => { | ||
29 | match to_symbol(node) { | ||
30 | Some(mut symbol) => { | ||
31 | symbol.parent = stack.last().map(|&n| n); | ||
32 | stack.push(res.len()); | ||
33 | res.push(symbol); | ||
34 | } | ||
35 | None => (), | ||
36 | } | ||
37 | } | ||
38 | WalkEvent::Exit(node) => { | ||
39 | if to_symbol(node).is_some() { | ||
40 | stack.pop().unwrap(); | ||
41 | } | ||
42 | } | ||
43 | } | ||
44 | } | ||
45 | res | ||
46 | } | ||
47 | |||
48 | fn to_symbol(node: SyntaxNodeRef) -> Option<FileSymbol> { | ||
49 | fn decl<'a, N: NameOwner<&'a SyntaxRoot>>(node: N) -> Option<FileSymbol> { | ||
50 | let name = node.name()?; | ||
51 | Some(FileSymbol { | ||
52 | parent: None, | ||
53 | name: name.text(), | ||
54 | name_range: name.syntax().range(), | ||
55 | node_range: node.syntax().range(), | ||
56 | kind: node.syntax().kind(), | ||
57 | }) | ||
58 | } | ||
59 | |||
60 | visitor() | ||
61 | .visit(decl::<ast::Function<_>>) | ||
62 | .visit(decl::<ast::Struct<_>>) | ||
63 | .visit(decl::<ast::Enum<_>>) | ||
64 | .visit(decl::<ast::Trait<_>>) | ||
65 | .visit(decl::<ast::Module<_>>) | ||
66 | .accept(node)? | ||
67 | } | ||
diff --git a/crates/libeditor/tests/test.rs b/crates/libeditor/tests/test.rs index d617f4b99..ba7181ab8 100644 --- a/crates/libeditor/tests/test.rs +++ b/crates/libeditor/tests/test.rs | |||
@@ -3,7 +3,7 @@ extern crate itertools; | |||
3 | 3 | ||
4 | use std::fmt; | 4 | use std::fmt; |
5 | use itertools::Itertools; | 5 | use itertools::Itertools; |
6 | use libeditor::{File, highlight, runnables, extend_selection, TextRange}; | 6 | use libeditor::{File, highlight, runnables, extend_selection, TextRange, file_symbols}; |
7 | 7 | ||
8 | #[test] | 8 | #[test] |
9 | fn test_extend_selection() { | 9 | fn test_extend_selection() { |
@@ -58,6 +58,29 @@ fn test_foo() {} | |||
58 | ) | 58 | ) |
59 | } | 59 | } |
60 | 60 | ||
61 | #[test] | ||
62 | fn symbols() { | ||
63 | let file = file(r#" | ||
64 | struct Foo { | ||
65 | x: i32 | ||
66 | } | ||
67 | |||
68 | mod m { | ||
69 | fn bar() {} | ||
70 | } | ||
71 | |||
72 | enum E { X, Y(i32) } | ||
73 | "#); | ||
74 | let symbols = file_symbols(&file); | ||
75 | dbg_eq( | ||
76 | &symbols, | ||
77 | r#"[FileSymbol { parent: None, name: "Foo", name_range: [8; 11), node_range: [1; 26), kind: STRUCT }, | ||
78 | FileSymbol { parent: None, name: "m", name_range: [32; 33), node_range: [28; 53), kind: MODULE }, | ||
79 | FileSymbol { parent: Some(1), name: "bar", name_range: [43; 46), node_range: [40; 51), kind: FUNCTION }, | ||
80 | FileSymbol { parent: None, name: "E", name_range: [60; 61), node_range: [55; 75), kind: ENUM }]"#, | ||
81 | ) | ||
82 | } | ||
83 | |||
61 | fn file(text: &str) -> File { | 84 | fn file(text: &str) -> File { |
62 | File::parse(text) | 85 | File::parse(text) |
63 | } | 86 | } |