aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.cargo/config18
-rw-r--r--crates/ra_analysis/src/extend_selection.rs29
-rw-r--r--crates/ra_analysis/src/imp.rs22
-rw-r--r--crates/ra_analysis/src/lib.rs14
-rw-r--r--crates/ra_analysis/src/runnables.rs72
-rw-r--r--crates/ra_analysis/src/syntax_highlighting.rs4
-rw-r--r--crates/ra_analysis/tests/runnables.rs118
-rw-r--r--crates/ra_analysis/tests/tests.rs50
-rw-r--r--crates/ra_cli/src/main.rs2
-rw-r--r--crates/ra_editor/src/extend_selection.rs12
-rw-r--r--crates/ra_editor/src/lib.rs66
-rw-r--r--crates/ra_hir/src/module.rs5
-rw-r--r--crates/ra_lsp_server/src/caps.rs2
-rw-r--r--crates/ra_lsp_server/src/conv.rs1
-rw-r--r--crates/ra_lsp_server/src/main_loop.rs1
-rw-r--r--crates/ra_lsp_server/src/main_loop/handlers.rs36
-rw-r--r--crates/tools/src/lib.rs17
-rw-r--r--crates/tools/src/main.rs7
18 files changed, 375 insertions, 101 deletions
diff --git a/.cargo/config b/.cargo/config
index c319d33f2..b9db30c96 100644
--- a/.cargo/config
+++ b/.cargo/config
@@ -1,10 +1,16 @@
1[alias] 1[alias]
2# Automatically generates the ast and syntax kinds files 2# Automatically generates the ast and syntax kinds files
3gen-syntax = "run --package tools --bin tools -- gen-syntax" 3gen-syntax = "run --package tools --bin tools -- gen-syntax"
4gen-tests = "run --package tools --bin tools -- gen-tests" 4# Extracts the tests from
5gen-tests = "run --package tools --bin tools -- gen-tests"
6# Installs the visual studio code extension
5install-code = "run --package tools --bin tools -- install-code" 7install-code = "run --package tools --bin tools -- install-code"
6format = "run --package tools --bin tools -- format" 8# Formats the full repository or installs the git hook to do it automatically.
7format-hook = "run --package tools --bin tools -- format-hook" 9format = "run --package tools --bin tools -- format"
10format-hook = "run --package tools --bin tools -- format-hook"
11# Runs the fuzzing test suite (currently only parser)
12fuzz-tests = "run --package tools --bin tools -- fuzz-tests"
8 13
9render-test = "run --package ra_cli -- render-test" 14render-test = "run --package ra_cli -- render-test"
10parse = "run --package ra_cli -- parse" 15# Parse a file. This should be piped the file contents
16parse = "run --package ra_cli -- parse"
diff --git a/crates/ra_analysis/src/extend_selection.rs b/crates/ra_analysis/src/extend_selection.rs
index cde6ee101..805e9059e 100644
--- a/crates/ra_analysis/src/extend_selection.rs
+++ b/crates/ra_analysis/src/extend_selection.rs
@@ -1,6 +1,6 @@
1use ra_db::SyntaxDatabase; 1use ra_db::SyntaxDatabase;
2use ra_syntax::{ 2use ra_syntax::{
3 SyntaxNodeRef, AstNode, 3 SyntaxNodeRef, AstNode, SourceFileNode,
4 ast, algo::find_covering_node, 4 ast, algo::find_covering_node,
5}; 5};
6 6
@@ -11,18 +11,23 @@ use crate::{
11 11
12pub(crate) fn extend_selection(db: &RootDatabase, frange: FileRange) -> TextRange { 12pub(crate) fn extend_selection(db: &RootDatabase, frange: FileRange) -> TextRange {
13 let source_file = db.source_file(frange.file_id); 13 let source_file = db.source_file(frange.file_id);
14 if let Some(macro_call) = find_macro_call(source_file.syntax(), frange.range) { 14 if let Some(range) = extend_selection_in_macro(db, &source_file, frange) {
15 if let Some(exp) = crate::macros::expand(db, frange.file_id, macro_call) { 15 return range;
16 if let Some(dst_range) = exp.map_range_forward(frange.range) {
17 if let Some(dst_range) = ra_editor::extend_selection(exp.source_file(), dst_range) {
18 if let Some(src_range) = exp.map_range_back(dst_range) {
19 return src_range;
20 }
21 }
22 }
23 }
24 } 16 }
25 ra_editor::extend_selection(&source_file, frange.range).unwrap_or(frange.range) 17 ra_editor::extend_selection(source_file.syntax(), frange.range).unwrap_or(frange.range)
18}
19
20fn extend_selection_in_macro(
21 db: &RootDatabase,
22 source_file: &SourceFileNode,
23 frange: FileRange,
24) -> Option<TextRange> {
25 let macro_call = find_macro_call(source_file.syntax(), frange.range)?;
26 let exp = crate::macros::expand(db, frange.file_id, macro_call)?;
27 let dst_range = exp.map_range_forward(frange.range)?;
28 let dst_range = ra_editor::extend_selection(exp.source_file().syntax(), dst_range)?;
29 let src_range = exp.map_range_back(dst_range)?;
30 Some(src_range)
26} 31}
27 32
28fn find_macro_call(node: SyntaxNodeRef, range: TextRange) -> Option<ast::MacroCall> { 33fn find_macro_call(node: SyntaxNodeRef, range: TextRange) -> Option<ast::MacroCall> {
diff --git a/crates/ra_analysis/src/imp.rs b/crates/ra_analysis/src/imp.rs
index 5ed374c79..5669aa94d 100644
--- a/crates/ra_analysis/src/imp.rs
+++ b/crates/ra_analysis/src/imp.rs
@@ -181,6 +181,28 @@ impl AnalysisImpl {
181 }; 181 };
182 Ok(query.search(&buf)) 182 Ok(query.search(&buf))
183 } 183 }
184
185 pub(crate) fn module_path(&self, position: FilePosition) -> Cancelable<Option<String>> {
186 let descr = match source_binder::module_from_position(&*self.db, position)? {
187 None => return Ok(None),
188 Some(it) => it,
189 };
190 let name = match descr.name() {
191 None => return Ok(None),
192 Some(it) => it.to_string(),
193 };
194
195 let modules = descr.path_to_root();
196
197 let path = modules
198 .iter()
199 .filter_map(|s| s.name())
200 .skip(1) // name is already part of the string.
201 .fold(name, |path, it| format!("{}::{}", it, path));
202
203 Ok(Some(path.to_string()))
204 }
205
184 /// This returns `Vec` because a module may be included from several places. We 206 /// This returns `Vec` because a module may be included from several places. We
185 /// don't handle this case yet though, so the Vec has length at most one. 207 /// don't handle this case yet though, so the Vec has length at most one.
186 pub fn parent_module(&self, position: FilePosition) -> Cancelable<Vec<(FileId, FileSymbol)>> { 208 pub fn parent_module(&self, position: FilePosition) -> Cancelable<Vec<(FileId, FileSymbol)>> {
diff --git a/crates/ra_analysis/src/lib.rs b/crates/ra_analysis/src/lib.rs
index e56168510..e6cfaecc3 100644
--- a/crates/ra_analysis/src/lib.rs
+++ b/crates/ra_analysis/src/lib.rs
@@ -15,6 +15,7 @@ mod imp;
15mod completion; 15mod completion;
16mod symbol_index; 16mod symbol_index;
17pub mod mock_analysis; 17pub mod mock_analysis;
18mod runnables;
18 19
19mod extend_selection; 20mod extend_selection;
20mod syntax_highlighting; 21mod syntax_highlighting;
@@ -33,10 +34,12 @@ use crate::{
33 symbol_index::SymbolIndex, 34 symbol_index::SymbolIndex,
34}; 35};
35 36
36pub use crate::completion::{CompletionItem, CompletionItemKind, InsertText}; 37pub use crate::{
38 completion::{CompletionItem, CompletionItemKind, InsertText},
39 runnables::{Runnable, RunnableKind}
40};
37pub use ra_editor::{ 41pub use ra_editor::{
38 FileSymbol, Fold, FoldKind, HighlightedRange, LineIndex, Runnable, RunnableKind, StructureNode, 42 FileSymbol, Fold, FoldKind, HighlightedRange, LineIndex, StructureNode, Severity
39 Severity
40}; 43};
41pub use hir::FnSignatureInfo; 44pub use hir::FnSignatureInfo;
42 45
@@ -336,6 +339,9 @@ impl Analysis {
336 pub fn parent_module(&self, position: FilePosition) -> Cancelable<Vec<(FileId, FileSymbol)>> { 339 pub fn parent_module(&self, position: FilePosition) -> Cancelable<Vec<(FileId, FileSymbol)>> {
337 self.imp.parent_module(position) 340 self.imp.parent_module(position)
338 } 341 }
342 pub fn module_path(&self, position: FilePosition) -> Cancelable<Option<String>> {
343 self.imp.module_path(position)
344 }
339 pub fn crate_for(&self, file_id: FileId) -> Cancelable<Vec<CrateId>> { 345 pub fn crate_for(&self, file_id: FileId) -> Cancelable<Vec<CrateId>> {
340 self.imp.crate_for(file_id) 346 self.imp.crate_for(file_id)
341 } 347 }
@@ -344,7 +350,7 @@ impl Analysis {
344 } 350 }
345 pub fn runnables(&self, file_id: FileId) -> Cancelable<Vec<Runnable>> { 351 pub fn runnables(&self, file_id: FileId) -> Cancelable<Vec<Runnable>> {
346 let file = self.imp.file_syntax(file_id); 352 let file = self.imp.file_syntax(file_id);
347 Ok(ra_editor::runnables(&file)) 353 Ok(runnables::runnables(self, &file, file_id))
348 } 354 }
349 pub fn highlight(&self, file_id: FileId) -> Cancelable<Vec<HighlightedRange>> { 355 pub fn highlight(&self, file_id: FileId) -> Cancelable<Vec<HighlightedRange>> {
350 syntax_highlighting::highlight(&*self.imp.db, file_id) 356 syntax_highlighting::highlight(&*self.imp.db, file_id)
diff --git a/crates/ra_analysis/src/runnables.rs b/crates/ra_analysis/src/runnables.rs
new file mode 100644
index 000000000..61ca0930a
--- /dev/null
+++ b/crates/ra_analysis/src/runnables.rs
@@ -0,0 +1,72 @@
1use ra_syntax::{
2 ast::{self, AstNode, NameOwner, ModuleItemOwner},
3 SourceFileNode, TextRange, SyntaxNodeRef,
4 TextUnit,
5};
6use crate::{
7 Analysis, FileId, FilePosition
8};
9
10#[derive(Debug)]
11pub struct Runnable {
12 pub range: TextRange,
13 pub kind: RunnableKind,
14}
15
16#[derive(Debug)]
17pub enum RunnableKind {
18 Test { name: String },
19 TestMod { path: String },
20 Bin,
21}
22
23pub fn runnables(
24 analysis: &Analysis,
25 file_node: &SourceFileNode,
26 file_id: FileId,
27) -> Vec<Runnable> {
28 file_node
29 .syntax()
30 .descendants()
31 .filter_map(|i| runnable(analysis, i, file_id))
32 .collect()
33}
34
35fn runnable<'a>(analysis: &Analysis, item: SyntaxNodeRef<'a>, file_id: FileId) -> Option<Runnable> {
36 if let Some(f) = ast::FnDef::cast(item) {
37 let name = f.name()?.text();
38 let kind = if name == "main" {
39 RunnableKind::Bin
40 } else if f.has_atom_attr("test") {
41 RunnableKind::Test {
42 name: name.to_string(),
43 }
44 } else {
45 return None;
46 };
47 Some(Runnable {
48 range: f.syntax().range(),
49 kind,
50 })
51 } else if let Some(m) = ast::Module::cast(item) {
52 if m.item_list()?
53 .items()
54 .map(ast::ModuleItem::syntax)
55 .filter_map(ast::FnDef::cast)
56 .any(|f| f.has_atom_attr("test"))
57 {
58 let postition = FilePosition {
59 file_id,
60 offset: m.syntax().range().start() + TextUnit::from_usize(1),
61 };
62 analysis.module_path(postition).ok()?.map(|path| Runnable {
63 range: m.syntax().range(),
64 kind: RunnableKind::TestMod { path },
65 })
66 } else {
67 None
68 }
69 } else {
70 None
71 }
72}
diff --git a/crates/ra_analysis/src/syntax_highlighting.rs b/crates/ra_analysis/src/syntax_highlighting.rs
index 38219da71..7e9139a74 100644
--- a/crates/ra_analysis/src/syntax_highlighting.rs
+++ b/crates/ra_analysis/src/syntax_highlighting.rs
@@ -9,14 +9,14 @@ use crate::{
9 9
10pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Cancelable<Vec<HighlightedRange>> { 10pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Cancelable<Vec<HighlightedRange>> {
11 let source_file = db.source_file(file_id); 11 let source_file = db.source_file(file_id);
12 let mut res = ra_editor::highlight(&source_file); 12 let mut res = ra_editor::highlight(source_file.syntax());
13 for macro_call in source_file 13 for macro_call in source_file
14 .syntax() 14 .syntax()
15 .descendants() 15 .descendants()
16 .filter_map(ast::MacroCall::cast) 16 .filter_map(ast::MacroCall::cast)
17 { 17 {
18 if let Some(exp) = crate::macros::expand(db, file_id, macro_call) { 18 if let Some(exp) = crate::macros::expand(db, file_id, macro_call) {
19 let mapped_ranges = ra_editor::highlight(exp.source_file()) 19 let mapped_ranges = ra_editor::highlight(exp.source_file().syntax())
20 .into_iter() 20 .into_iter()
21 .filter_map(|r| { 21 .filter_map(|r| {
22 let mapped_range = exp.map_range_back(r.range)?; 22 let mapped_range = exp.map_range_back(r.range)?;
diff --git a/crates/ra_analysis/tests/runnables.rs b/crates/ra_analysis/tests/runnables.rs
new file mode 100644
index 000000000..9e5342c46
--- /dev/null
+++ b/crates/ra_analysis/tests/runnables.rs
@@ -0,0 +1,118 @@
1extern crate ra_analysis;
2extern crate ra_editor;
3extern crate ra_syntax;
4extern crate relative_path;
5extern crate rustc_hash;
6extern crate test_utils;
7
8use test_utils::assert_eq_dbg;
9
10use ra_analysis::{
11 mock_analysis::{analysis_and_position},
12};
13
14#[test]
15fn test_runnables() {
16 let (analysis, pos) = analysis_and_position(
17 r#"
18 //- /lib.rs
19 <|> //empty
20 fn main() {}
21
22 #[test]
23 fn test_foo() {}
24
25 #[test]
26 #[ignore]
27 fn test_foo() {}
28 "#,
29 );
30 let runnables = analysis.runnables(pos.file_id).unwrap();
31 assert_eq_dbg(
32 r#"[Runnable { range: [1; 21), kind: Bin },
33 Runnable { range: [22; 46), kind: Test { name: "test_foo" } },
34 Runnable { range: [47; 81), kind: Test { name: "test_foo" } }]"#,
35 &runnables,
36 )
37}
38
39#[test]
40fn test_runnables_module() {
41 let (analysis, pos) = analysis_and_position(
42 r#"
43 //- /lib.rs
44 <|> //empty
45 mod test_mod {
46 #[test]
47 fn test_foo1() {}
48 }
49 "#,
50 );
51 let runnables = analysis.runnables(pos.file_id).unwrap();
52 assert_eq_dbg(
53 r#"[Runnable { range: [1; 59), kind: TestMod { path: "test_mod" } },
54 Runnable { range: [28; 57), kind: Test { name: "test_foo1" } }]"#,
55 &runnables,
56 )
57}
58
59#[test]
60fn test_runnables_one_depth_layer_module() {
61 let (analysis, pos) = analysis_and_position(
62 r#"
63 //- /lib.rs
64 <|> //empty
65 mod foo {
66 mod test_mod {
67 #[test]
68 fn test_foo1() {}
69 }
70 }
71 "#,
72 );
73 let runnables = analysis.runnables(pos.file_id).unwrap();
74 assert_eq_dbg(
75 r#"[Runnable { range: [23; 85), kind: TestMod { path: "foo::test_mod" } },
76 Runnable { range: [46; 79), kind: Test { name: "test_foo1" } }]"#,
77 &runnables,
78 )
79}
80
81#[test]
82fn test_runnables_multiple_depth_module() {
83 let (analysis, pos) = analysis_and_position(
84 r#"
85 //- /lib.rs
86 <|> //empty
87 mod foo {
88 mod bar {
89 mod test_mod {
90 #[test]
91 fn test_foo1() {}
92 }
93 }
94 }
95 "#,
96 );
97 let runnables = analysis.runnables(pos.file_id).unwrap();
98 assert_eq_dbg(
99 r#"[Runnable { range: [41; 115), kind: TestMod { path: "foo::bar::test_mod" } },
100 Runnable { range: [68; 105), kind: Test { name: "test_foo1" } }]"#,
101 &runnables,
102 )
103}
104
105#[test]
106fn test_runnables_no_test_function_in_module() {
107 let (analysis, pos) = analysis_and_position(
108 r#"
109 //- /lib.rs
110 <|> //empty
111 mod test_mod {
112 fn foo1() {}
113 }
114 "#,
115 );
116 let runnables = analysis.runnables(pos.file_id).unwrap();
117 assert_eq_dbg(r#"[]"#, &runnables)
118}
diff --git a/crates/ra_analysis/tests/tests.rs b/crates/ra_analysis/tests/tests.rs
index a314fbc40..b61ead752 100644
--- a/crates/ra_analysis/tests/tests.rs
+++ b/crates/ra_analysis/tests/tests.rs
@@ -132,6 +132,56 @@ fn test_resolve_parent_module_for_inline() {
132} 132}
133 133
134#[test] 134#[test]
135fn test_path_one_layer() {
136 let (analysis, pos) = analysis_and_position(
137 "
138 //- /lib.rs
139 mod foo;
140 //- /foo/mod.rs
141 mod bla;
142 //- /foo/bla.rs
143 <|> //empty
144 ",
145 );
146 let symbols = analysis.module_path(pos).unwrap().unwrap();
147 assert_eq!("foo::bla", &symbols);
148}
149
150#[test]
151fn test_path_two_layer() {
152 let (analysis, pos) = analysis_and_position(
153 "
154 //- /lib.rs
155 mod foo;
156 //- /foo/mod.rs
157 mod bla;
158 //- /foo/bla/mod.rs
159 mod more;
160 //- /foo/bla/more.rs
161 <|> //empty
162 ",
163 );
164 let symbols = analysis.module_path(pos).unwrap().unwrap();
165 assert_eq!("foo::bla::more", &symbols);
166}
167
168#[test]
169fn test_path_in_file_mod() {
170 let (analysis, pos) = analysis_and_position(
171 "
172 //- /lib.rs
173 mod foo;
174 //- /foo.rs
175 mod bar {
176 <|> //empty
177 }
178 ",
179 );
180 let symbols = analysis.module_path(pos).unwrap().unwrap();
181 assert_eq!("foo::bar", &symbols);
182}
183
184#[test]
135fn test_resolve_crate_root() { 185fn test_resolve_crate_root() {
136 let mock = MockAnalysis::with_files( 186 let mock = MockAnalysis::with_files(
137 " 187 "
diff --git a/crates/ra_cli/src/main.rs b/crates/ra_cli/src/main.rs
index 939f7fe77..a3b856aa9 100644
--- a/crates/ra_cli/src/main.rs
+++ b/crates/ra_cli/src/main.rs
@@ -102,7 +102,7 @@ fn selections(file: &SourceFileNode, start: u32, end: u32) -> String {
102 let mut cur = Some(TextRange::from_to((start - 1).into(), (end - 1).into())); 102 let mut cur = Some(TextRange::from_to((start - 1).into(), (end - 1).into()));
103 while let Some(r) = cur { 103 while let Some(r) = cur {
104 ranges.push(r); 104 ranges.push(r);
105 cur = extend_selection(&file, r); 105 cur = extend_selection(file.syntax(), r);
106 } 106 }
107 let ranges = ranges 107 let ranges = ranges
108 .iter() 108 .iter()
diff --git a/crates/ra_editor/src/extend_selection.rs b/crates/ra_editor/src/extend_selection.rs
index 4665a336a..bf0727dde 100644
--- a/crates/ra_editor/src/extend_selection.rs
+++ b/crates/ra_editor/src/extend_selection.rs
@@ -1,16 +1,11 @@
1use ra_syntax::{ 1use ra_syntax::{
2 algo::{find_covering_node, find_leaf_at_offset, LeafAtOffset}, 2 algo::{find_covering_node, find_leaf_at_offset, LeafAtOffset},
3 Direction, SourceFileNode, 3 Direction,
4 SyntaxKind::*, 4 SyntaxKind::*,
5 SyntaxNodeRef, TextRange, TextUnit, 5 SyntaxNodeRef, TextRange, TextUnit,
6}; 6};
7 7
8pub fn extend_selection(file: &SourceFileNode, range: TextRange) -> Option<TextRange> { 8pub fn extend_selection(root: SyntaxNodeRef, range: TextRange) -> Option<TextRange> {
9 let syntax = file.syntax();
10 extend(syntax.borrowed(), range)
11}
12
13pub(crate) fn extend(root: SyntaxNodeRef, range: TextRange) -> Option<TextRange> {
14 if range.is_empty() { 9 if range.is_empty() {
15 let offset = range.start(); 10 let offset = range.start();
16 let mut leaves = find_leaf_at_offset(root, offset); 11 let mut leaves = find_leaf_at_offset(root, offset);
@@ -126,6 +121,7 @@ fn adj_comments(node: SyntaxNodeRef, dir: Direction) -> SyntaxNodeRef {
126#[cfg(test)] 121#[cfg(test)]
127mod tests { 122mod tests {
128 use super::*; 123 use super::*;
124 use ra_syntax::SourceFileNode;
129 use test_utils::extract_offset; 125 use test_utils::extract_offset;
130 126
131 fn do_check(before: &str, afters: &[&str]) { 127 fn do_check(before: &str, afters: &[&str]) {
@@ -133,7 +129,7 @@ mod tests {
133 let file = SourceFileNode::parse(&before); 129 let file = SourceFileNode::parse(&before);
134 let mut range = TextRange::offset_len(cursor, 0.into()); 130 let mut range = TextRange::offset_len(cursor, 0.into());
135 for &after in afters { 131 for &after in afters {
136 range = extend_selection(&file, range).unwrap(); 132 range = extend_selection(file.syntax(), range).unwrap();
137 let actual = &before[range]; 133 let actual = &before[range];
138 assert_eq!(after, actual); 134 assert_eq!(after, actual);
139 } 135 }
diff --git a/crates/ra_editor/src/lib.rs b/crates/ra_editor/src/lib.rs
index a65637d52..b03f9ea54 100644
--- a/crates/ra_editor/src/lib.rs
+++ b/crates/ra_editor/src/lib.rs
@@ -22,7 +22,7 @@ pub use self::{
22use ra_text_edit::{TextEdit, TextEditBuilder}; 22use ra_text_edit::{TextEdit, TextEditBuilder};
23use ra_syntax::{ 23use ra_syntax::{
24 algo::find_leaf_at_offset, 24 algo::find_leaf_at_offset,
25 ast::{self, AstNode, NameOwner}, 25 ast::{self, AstNode},
26 SourceFileNode, 26 SourceFileNode,
27 SyntaxKind::{self, *}, 27 SyntaxKind::{self, *},
28 SyntaxNodeRef, TextRange, TextUnit, Direction, 28 SyntaxNodeRef, TextRange, TextUnit, Direction,
@@ -49,18 +49,6 @@ pub struct Diagnostic {
49 pub fix: Option<LocalEdit>, 49 pub fix: Option<LocalEdit>,
50} 50}
51 51
52#[derive(Debug)]
53pub struct Runnable {
54 pub range: TextRange,
55 pub kind: RunnableKind,
56}
57
58#[derive(Debug)]
59pub enum RunnableKind {
60 Test { name: String },
61 Bin,
62}
63
64pub fn matching_brace(file: &SourceFileNode, offset: TextUnit) -> Option<TextUnit> { 52pub fn matching_brace(file: &SourceFileNode, offset: TextUnit) -> Option<TextUnit> {
65 const BRACES: &[SyntaxKind] = &[ 53 const BRACES: &[SyntaxKind] = &[
66 L_CURLY, R_CURLY, L_BRACK, R_BRACK, L_PAREN, R_PAREN, L_ANGLE, R_ANGLE, 54 L_CURLY, R_CURLY, L_BRACK, R_BRACK, L_PAREN, R_PAREN, L_ANGLE, R_ANGLE,
@@ -79,11 +67,11 @@ pub fn matching_brace(file: &SourceFileNode, offset: TextUnit) -> Option<TextUni
79 Some(matching_node.range().start()) 67 Some(matching_node.range().start())
80} 68}
81 69
82pub fn highlight(file: &SourceFileNode) -> Vec<HighlightedRange> { 70pub fn highlight(root: SyntaxNodeRef) -> Vec<HighlightedRange> {
83 // Visited nodes to handle highlighting priorities 71 // Visited nodes to handle highlighting priorities
84 let mut highlighted = FxHashSet::default(); 72 let mut highlighted = FxHashSet::default();
85 let mut res = Vec::new(); 73 let mut res = Vec::new();
86 for node in file.syntax().descendants() { 74 for node in root.descendants() {
87 if highlighted.contains(&node) { 75 if highlighted.contains(&node) {
88 continue; 76 continue;
89 } 77 }
@@ -133,29 +121,6 @@ pub fn syntax_tree(file: &SourceFileNode) -> String {
133 ::ra_syntax::utils::dump_tree(file.syntax()) 121 ::ra_syntax::utils::dump_tree(file.syntax())
134} 122}
135 123
136pub fn runnables(file: &SourceFileNode) -> Vec<Runnable> {
137 file.syntax()
138 .descendants()
139 .filter_map(ast::FnDef::cast)
140 .filter_map(|f| {
141 let name = f.name()?.text();
142 let kind = if name == "main" {
143 RunnableKind::Bin
144 } else if f.has_atom_attr("test") {
145 RunnableKind::Test {
146 name: name.to_string(),
147 }
148 } else {
149 return None;
150 };
151 Some(Runnable {
152 range: f.syntax().range(),
153 kind,
154 })
155 })
156 .collect()
157}
158
159pub fn find_node_at_offset<'a, N: AstNode<'a>>( 124pub fn find_node_at_offset<'a, N: AstNode<'a>>(
160 syntax: SyntaxNodeRef<'a>, 125 syntax: SyntaxNodeRef<'a>,
161 offset: TextUnit, 126 offset: TextUnit,
@@ -178,7 +143,7 @@ fn main() {}
178 println!("Hello, {}!", 92); 143 println!("Hello, {}!", 92);
179"#, 144"#,
180 ); 145 );
181 let hls = highlight(&file); 146 let hls = highlight(file.syntax());
182 assert_eq_dbg( 147 assert_eq_dbg(
183 r#"[HighlightedRange { range: [1; 11), tag: "comment" }, 148 r#"[HighlightedRange { range: [1; 11), tag: "comment" },
184 HighlightedRange { range: [12; 14), tag: "keyword" }, 149 HighlightedRange { range: [12; 14), tag: "keyword" },
@@ -191,29 +156,6 @@ fn main() {}
191 } 156 }
192 157
193 #[test] 158 #[test]
194 fn test_runnables() {
195 let file = SourceFileNode::parse(
196 r#"
197fn main() {}
198
199#[test]
200fn test_foo() {}
201
202#[test]
203#[ignore]
204fn test_foo() {}
205"#,
206 );
207 let runnables = runnables(&file);
208 assert_eq_dbg(
209 r#"[Runnable { range: [1; 13), kind: Bin },
210 Runnable { range: [15; 39), kind: Test { name: "test_foo" } },
211 Runnable { range: [41; 75), kind: Test { name: "test_foo" } }]"#,
212 &runnables,
213 )
214 }
215
216 #[test]
217 fn test_matching_brace() { 159 fn test_matching_brace() {
218 fn do_check(before: &str, after: &str) { 160 fn do_check(before: &str, after: &str) {
219 let (pos, before) = extract_offset(before); 161 let (pos, before) = extract_offset(before);
diff --git a/crates/ra_hir/src/module.rs b/crates/ra_hir/src/module.rs
index 24c346984..87e30191f 100644
--- a/crates/ra_hir/src/module.rs
+++ b/crates/ra_hir/src/module.rs
@@ -75,6 +75,11 @@ impl Module {
75 Some(Crate::new(crate_id)) 75 Some(Crate::new(crate_id))
76 } 76 }
77 77
78 /// Returns the all modulkes on the way to the root.
79 pub fn path_to_root(&self) -> Vec<Module> {
80 generate(Some(self.clone()), move |it| it.parent()).collect::<Vec<Module>>()
81 }
82
78 /// The root of the tree this module is part of 83 /// The root of the tree this module is part of
79 pub fn crate_root(&self) -> Module { 84 pub fn crate_root(&self) -> Module {
80 let root_id = self.module_id.crate_root(&self.tree); 85 let root_id = self.module_id.crate_root(&self.tree);
diff --git a/crates/ra_lsp_server/src/caps.rs b/crates/ra_lsp_server/src/caps.rs
index 8d508a3ba..a74f9f27b 100644
--- a/crates/ra_lsp_server/src/caps.rs
+++ b/crates/ra_lsp_server/src/caps.rs
@@ -28,7 +28,7 @@ pub fn server_capabilities() -> ServerCapabilities {
28 type_definition_provider: None, 28 type_definition_provider: None,
29 implementation_provider: None, 29 implementation_provider: None,
30 references_provider: Some(true), 30 references_provider: Some(true),
31 document_highlight_provider: None, 31 document_highlight_provider: Some(true),
32 document_symbol_provider: Some(true), 32 document_symbol_provider: Some(true),
33 workspace_symbol_provider: Some(true), 33 workspace_symbol_provider: Some(true),
34 code_action_provider: Some(CodeActionProviderCapability::Simple(true)), 34 code_action_provider: Some(CodeActionProviderCapability::Simple(true)),
diff --git a/crates/ra_lsp_server/src/conv.rs b/crates/ra_lsp_server/src/conv.rs
index 0d6e62727..618486250 100644
--- a/crates/ra_lsp_server/src/conv.rs
+++ b/crates/ra_lsp_server/src/conv.rs
@@ -82,7 +82,6 @@ impl Conv for CompletionItem {
82 InsertText::Snippet { text } => { 82 InsertText::Snippet { text } => {
83 res.insert_text = Some(text); 83 res.insert_text = Some(text);
84 res.insert_text_format = Some(InsertTextFormat::Snippet); 84 res.insert_text_format = Some(InsertTextFormat::Snippet);
85 res.kind = Some(languageserver_types::CompletionItemKind::Keyword);
86 } 85 }
87 } 86 }
88 res 87 res
diff --git a/crates/ra_lsp_server/src/main_loop.rs b/crates/ra_lsp_server/src/main_loop.rs
index d425b6733..06dd373c0 100644
--- a/crates/ra_lsp_server/src/main_loop.rs
+++ b/crates/ra_lsp_server/src/main_loop.rs
@@ -300,6 +300,7 @@ fn on_request(
300 .on::<req::Rename>(handlers::handle_rename)? 300 .on::<req::Rename>(handlers::handle_rename)?
301 .on::<req::References>(handlers::handle_references)? 301 .on::<req::References>(handlers::handle_references)?
302 .on::<req::Formatting>(handlers::handle_formatting)? 302 .on::<req::Formatting>(handlers::handle_formatting)?
303 .on::<req::DocumentHighlightRequest>(handlers::handle_document_highlight)?
303 .finish(); 304 .finish();
304 match req { 305 match req {
305 Ok(id) => { 306 Ok(id) => {
diff --git a/crates/ra_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs
index 3b7a14a5c..11825d74e 100644
--- a/crates/ra_lsp_server/src/main_loop/handlers.rs
+++ b/crates/ra_lsp_server/src/main_loop/handlers.rs
@@ -6,9 +6,8 @@ use languageserver_types::{
6 DiagnosticSeverity, DocumentSymbol, Documentation, FoldingRange, FoldingRangeKind, 6 DiagnosticSeverity, DocumentSymbol, Documentation, FoldingRange, FoldingRangeKind,
7 FoldingRangeParams, Location, MarkupContent, MarkupKind, MarkedString, Position, 7 FoldingRangeParams, Location, MarkupContent, MarkupKind, MarkedString, Position,
8 PrepareRenameResponse, RenameParams, SymbolInformation, TextDocumentIdentifier, TextEdit, 8 PrepareRenameResponse, RenameParams, SymbolInformation, TextDocumentIdentifier, TextEdit,
9 Range, 9 Range, WorkspaceEdit, ParameterInformation, ParameterLabel, SignatureInformation, Hover,
10 WorkspaceEdit, ParameterInformation, ParameterLabel, SignatureInformation, Hover, HoverContents, 10 HoverContents, DocumentFormattingParams, DocumentHighlight,
11 DocumentFormattingParams,
12}; 11};
13use ra_analysis::{FileId, FoldKind, Query, RunnableKind, FileRange, FilePosition, Severity}; 12use ra_analysis::{FileId, FoldKind, Query, RunnableKind, FileRange, FilePosition, Severity};
14use ra_syntax::{TextUnit, text_utils::intersect}; 13use ra_syntax::{TextUnit, text_utils::intersect};
@@ -257,6 +256,7 @@ pub fn handle_runnables(
257 range: runnable.range.conv_with(&line_index), 256 range: runnable.range.conv_with(&line_index),
258 label: match &runnable.kind { 257 label: match &runnable.kind {
259 RunnableKind::Test { name } => format!("test {}", name), 258 RunnableKind::Test { name } => format!("test {}", name),
259 RunnableKind::TestMod { path } => format!("test-mod {}", path),
260 RunnableKind::Bin => "run binary".to_string(), 260 RunnableKind::Bin => "run binary".to_string(),
261 }, 261 },
262 bin: "cargo".to_string(), 262 bin: "cargo".to_string(),
@@ -308,6 +308,15 @@ pub fn handle_runnables(
308 res.push(name.to_string()); 308 res.push(name.to_string());
309 res.push("--nocapture".to_string()); 309 res.push("--nocapture".to_string());
310 } 310 }
311 RunnableKind::TestMod { path } => {
312 res.push("test".to_string());
313 if let Some(spec) = spec {
314 spec.push_to(&mut res);
315 }
316 res.push("--".to_string());
317 res.push(path.to_string());
318 res.push("--nocapture".to_string());
319 }
311 RunnableKind::Bin => { 320 RunnableKind::Bin => {
312 res.push("run".to_string()); 321 res.push("run".to_string());
313 if let Some(spec) = spec { 322 if let Some(spec) = spec {
@@ -668,6 +677,27 @@ pub fn handle_code_action(
668 Ok(Some(CodeActionResponse::Commands(res))) 677 Ok(Some(CodeActionResponse::Commands(res)))
669} 678}
670 679
680pub fn handle_document_highlight(
681 world: ServerWorld,
682 params: req::TextDocumentPositionParams,
683) -> Result<Option<Vec<DocumentHighlight>>> {
684 let file_id = params.text_document.try_conv_with(&world)?;
685 let line_index = world.analysis().file_line_index(file_id);
686
687 let refs = world
688 .analysis()
689 .find_all_refs(params.try_conv_with(&world)?)?;
690
691 Ok(Some(
692 refs.into_iter()
693 .map(|r| DocumentHighlight {
694 range: r.1.conv_with(&line_index),
695 kind: None,
696 })
697 .collect(),
698 ))
699}
700
671pub fn publish_diagnostics( 701pub fn publish_diagnostics(
672 world: &ServerWorld, 702 world: &ServerWorld,
673 file_id: FileId, 703 file_id: FileId,
diff --git a/crates/tools/src/lib.rs b/crates/tools/src/lib.rs
index e5b32c25c..fa619af33 100644
--- a/crates/tools/src/lib.rs
+++ b/crates/tools/src/lib.rs
@@ -139,3 +139,20 @@ pub fn install_format_hook() -> Result<()> {
139 } 139 }
140 Ok(()) 140 Ok(())
141} 141}
142
143pub fn run_fuzzer() -> Result<()> {
144 match Command::new("cargo")
145 .args(&["fuzz", "--help"])
146 .stderr(Stdio::null())
147 .stdout(Stdio::null())
148 .status()
149 {
150 Ok(status) if status.success() => (),
151 _ => run("cargo install cargo-fuzz", ".")?,
152 };
153
154 run(
155 "rustup run nightly -- cargo fuzz run parser",
156 "./crates/ra_syntax",
157 )
158}
diff --git a/crates/tools/src/main.rs b/crates/tools/src/main.rs
index 7edf8f52d..9d73d57c4 100644
--- a/crates/tools/src/main.rs
+++ b/crates/tools/src/main.rs
@@ -7,7 +7,10 @@ use std::{
7use clap::{App, Arg, SubCommand}; 7use clap::{App, Arg, SubCommand};
8use failure::bail; 8use failure::bail;
9 9
10use tools::{collect_tests, generate, install_format_hook, run, run_rustfmt, Mode, Overwrite, Result, Test, Verify, project_root}; 10use tools::{
11 collect_tests, generate,install_format_hook, run, run_rustfmt,
12 Mode, Overwrite, Result, Test, Verify, project_root, run_fuzzer
13};
11 14
12const GRAMMAR_DIR: &str = "crates/ra_syntax/src/grammar"; 15const GRAMMAR_DIR: &str = "crates/ra_syntax/src/grammar";
13const OK_INLINE_TESTS_DIR: &str = "crates/ra_syntax/tests/data/parser/inline/ok"; 16const OK_INLINE_TESTS_DIR: &str = "crates/ra_syntax/tests/data/parser/inline/ok";
@@ -27,6 +30,7 @@ fn main() -> Result<()> {
27 .subcommand(SubCommand::with_name("install-code")) 30 .subcommand(SubCommand::with_name("install-code"))
28 .subcommand(SubCommand::with_name("format")) 31 .subcommand(SubCommand::with_name("format"))
29 .subcommand(SubCommand::with_name("format-hook")) 32 .subcommand(SubCommand::with_name("format-hook"))
33 .subcommand(SubCommand::with_name("fuzz-tests"))
30 .get_matches(); 34 .get_matches();
31 let mode = if matches.is_present("verify") { 35 let mode = if matches.is_present("verify") {
32 Verify 36 Verify
@@ -42,6 +46,7 @@ fn main() -> Result<()> {
42 "gen-syntax" => generate(Overwrite)?, 46 "gen-syntax" => generate(Overwrite)?,
43 "format" => run_rustfmt(mode)?, 47 "format" => run_rustfmt(mode)?,
44 "format-hook" => install_format_hook()?, 48 "format-hook" => install_format_hook()?,
49 "fuzz-tests" => run_fuzzer()?,
45 _ => unreachable!(), 50 _ => unreachable!(),
46 } 51 }
47 Ok(()) 52 Ok(())