aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_analysis
diff options
context:
space:
mode:
authorbors[bot] <bors[bot]@users.noreply.github.com>2018-12-28 16:17:19 +0000
committerbors[bot] <bors[bot]@users.noreply.github.com>2018-12-28 16:17:19 +0000
commit7a268b9b9635425176f93d3c893fb5345e84e9ce (patch)
tree6ea69024cb22d3fc48a3b392a0185163fa452014 /crates/ra_analysis
parent9d6740a9c9ad2ca47c4885bd994f849e90bbef86 (diff)
parentb911ee542b2f4d1cd62a655f24197856cd9b9097 (diff)
Merge #350
350: Super simple macro support r=matklad a=matklad Super simple support for macros, mostly for figuring out how to fit them into the current architecture. Expansion is hard-coded and string based (mid-term, we should try to copy-paste macro-by-example expander from rustc). Ideally, we should handle * highlighting inside the macro (done) * extend selection inside the macro * completion inside the macro * indexing structs, produced by the macro Co-authored-by: Aleksey Kladov <[email protected]>
Diffstat (limited to 'crates/ra_analysis')
-rw-r--r--crates/ra_analysis/src/extend_selection.rs11
-rw-r--r--crates/ra_analysis/src/imp.rs24
-rw-r--r--crates/ra_analysis/src/lib.rs27
-rw-r--r--crates/ra_analysis/src/macros.rs64
-rw-r--r--crates/ra_analysis/src/syntax_highlighting.rs63
5 files changed, 167 insertions, 22 deletions
diff --git a/crates/ra_analysis/src/extend_selection.rs b/crates/ra_analysis/src/extend_selection.rs
new file mode 100644
index 000000000..5e1fbee18
--- /dev/null
+++ b/crates/ra_analysis/src/extend_selection.rs
@@ -0,0 +1,11 @@
1use ra_db::SyntaxDatabase;
2
3use crate::{
4 TextRange, FileRange,
5 db::RootDatabase,
6};
7
8pub(crate) fn extend_selection(db: &RootDatabase, frange: FileRange) -> TextRange {
9 let file = db.source_file(frange.file_id);
10 ra_editor::extend_selection(&file, frange.range).unwrap_or(frange.range)
11}
diff --git a/crates/ra_analysis/src/imp.rs b/crates/ra_analysis/src/imp.rs
index e6663810d..fcb4cd957 100644
--- a/crates/ra_analysis/src/imp.rs
+++ b/crates/ra_analysis/src/imp.rs
@@ -23,7 +23,7 @@ use crate::{
23 AnalysisChange, 23 AnalysisChange,
24 Cancelable, 24 Cancelable,
25 completion::{CompletionItem, completions}, 25 completion::{CompletionItem, completions},
26 CrateId, db, Diagnostic, FileId, FilePosition, FileSystemEdit, 26 CrateId, db, Diagnostic, FileId, FilePosition, FileRange, FileSystemEdit,
27 Query, ReferenceResolution, RootChange, SourceChange, SourceFileEdit, 27 Query, ReferenceResolution, RootChange, SourceChange, SourceFileEdit,
28 symbol_index::{LibrarySymbolsQuery, SymbolIndex, SymbolsDatabase}, 28 symbol_index::{LibrarySymbolsQuery, SymbolIndex, SymbolsDatabase},
29}; 29};
@@ -404,19 +404,21 @@ impl AnalysisImpl {
404 Ok(res) 404 Ok(res)
405 } 405 }
406 406
407 pub fn assists(&self, file_id: FileId, range: TextRange) -> Vec<SourceChange> { 407 pub fn assists(&self, frange: FileRange) -> Vec<SourceChange> {
408 let file = self.file_syntax(file_id); 408 let file = self.file_syntax(frange.file_id);
409 let offset = range.start(); 409 let offset = frange.range.start();
410 let actions = vec![ 410 let actions = vec![
411 ra_editor::flip_comma(&file, offset).map(|f| f()), 411 ra_editor::flip_comma(&file, offset).map(|f| f()),
412 ra_editor::add_derive(&file, offset).map(|f| f()), 412 ra_editor::add_derive(&file, offset).map(|f| f()),
413 ra_editor::add_impl(&file, offset).map(|f| f()), 413 ra_editor::add_impl(&file, offset).map(|f| f()),
414 ra_editor::make_pub_crate(&file, offset).map(|f| f()), 414 ra_editor::make_pub_crate(&file, offset).map(|f| f()),
415 ra_editor::introduce_variable(&file, range).map(|f| f()), 415 ra_editor::introduce_variable(&file, frange.range).map(|f| f()),
416 ]; 416 ];
417 actions 417 actions
418 .into_iter() 418 .into_iter()
419 .filter_map(|local_edit| Some(SourceChange::from_local_edit(file_id, local_edit?))) 419 .filter_map(|local_edit| {
420 Some(SourceChange::from_local_edit(frange.file_id, local_edit?))
421 })
420 .collect() 422 .collect()
421 } 423 }
422 424
@@ -487,13 +489,15 @@ impl AnalysisImpl {
487 Ok(None) 489 Ok(None)
488 } 490 }
489 491
490 pub fn type_of(&self, file_id: FileId, range: TextRange) -> Cancelable<Option<String>> { 492 pub fn type_of(&self, frange: FileRange) -> Cancelable<Option<String>> {
491 let file = self.db.source_file(file_id); 493 let file = self.db.source_file(frange.file_id);
492 let syntax = file.syntax(); 494 let syntax = file.syntax();
493 let node = find_covering_node(syntax, range); 495 let node = find_covering_node(syntax, frange.range);
494 let parent_fn = ctry!(node.ancestors().find_map(FnDef::cast)); 496 let parent_fn = ctry!(node.ancestors().find_map(FnDef::cast));
495 let function = ctry!(source_binder::function_from_source( 497 let function = ctry!(source_binder::function_from_source(
496 &*self.db, file_id, parent_fn 498 &*self.db,
499 frange.file_id,
500 parent_fn
497 )?); 501 )?);
498 let infer = function.infer(&*self.db)?; 502 let infer = function.infer(&*self.db)?;
499 Ok(infer.type_of_node(node).map(|t| t.to_string())) 503 Ok(infer.type_of_node(node).map(|t| t.to_string()))
diff --git a/crates/ra_analysis/src/lib.rs b/crates/ra_analysis/src/lib.rs
index 65c3eb3ec..67b1c1482 100644
--- a/crates/ra_analysis/src/lib.rs
+++ b/crates/ra_analysis/src/lib.rs
@@ -16,6 +16,10 @@ mod completion;
16mod symbol_index; 16mod symbol_index;
17pub mod mock_analysis; 17pub mod mock_analysis;
18 18
19mod extend_selection;
20mod syntax_highlighting;
21mod macros;
22
19use std::{fmt, sync::Arc}; 23use std::{fmt, sync::Arc};
20 24
21use rustc_hash::FxHashMap; 25use rustc_hash::FxHashMap;
@@ -37,7 +41,7 @@ pub use ra_editor::{
37pub use hir::FnSignatureInfo; 41pub use hir::FnSignatureInfo;
38 42
39pub use ra_db::{ 43pub use ra_db::{
40 Canceled, Cancelable, FilePosition, 44 Canceled, Cancelable, FilePosition, FileRange,
41 CrateGraph, CrateId, SourceRootId, FileId 45 CrateGraph, CrateId, SourceRootId, FileId
42}; 46};
43 47
@@ -276,8 +280,8 @@ impl Analysis {
276 pub fn file_line_index(&self, file_id: FileId) -> Arc<LineIndex> { 280 pub fn file_line_index(&self, file_id: FileId) -> Arc<LineIndex> {
277 self.imp.file_line_index(file_id) 281 self.imp.file_line_index(file_id)
278 } 282 }
279 pub fn extend_selection(&self, file: &SourceFileNode, range: TextRange) -> TextRange { 283 pub fn extend_selection(&self, frange: FileRange) -> TextRange {
280 ra_editor::extend_selection(file, range).unwrap_or(range) 284 extend_selection::extend_selection(&self.imp.db, frange)
281 } 285 }
282 pub fn matching_brace(&self, file: &SourceFileNode, offset: TextUnit) -> Option<TextUnit> { 286 pub fn matching_brace(&self, file: &SourceFileNode, offset: TextUnit) -> Option<TextUnit> {
283 ra_editor::matching_brace(file, offset) 287 ra_editor::matching_brace(file, offset)
@@ -286,9 +290,9 @@ impl Analysis {
286 let file = self.imp.file_syntax(file_id); 290 let file = self.imp.file_syntax(file_id);
287 ra_editor::syntax_tree(&file) 291 ra_editor::syntax_tree(&file)
288 } 292 }
289 pub fn join_lines(&self, file_id: FileId, range: TextRange) -> SourceChange { 293 pub fn join_lines(&self, frange: FileRange) -> SourceChange {
290 let file = self.imp.file_syntax(file_id); 294 let file = self.imp.file_syntax(frange.file_id);
291 SourceChange::from_local_edit(file_id, ra_editor::join_lines(&file, range)) 295 SourceChange::from_local_edit(frange.file_id, ra_editor::join_lines(&file, frange.range))
292 } 296 }
293 pub fn on_enter(&self, position: FilePosition) -> Option<SourceChange> { 297 pub fn on_enter(&self, position: FilePosition) -> Option<SourceChange> {
294 let file = self.imp.file_syntax(position.file_id); 298 let file = self.imp.file_syntax(position.file_id);
@@ -340,14 +344,13 @@ impl Analysis {
340 Ok(ra_editor::runnables(&file)) 344 Ok(ra_editor::runnables(&file))
341 } 345 }
342 pub fn highlight(&self, file_id: FileId) -> Cancelable<Vec<HighlightedRange>> { 346 pub fn highlight(&self, file_id: FileId) -> Cancelable<Vec<HighlightedRange>> {
343 let file = self.imp.file_syntax(file_id); 347 syntax_highlighting::highlight(&*self.imp.db, file_id)
344 Ok(ra_editor::highlight(&file))
345 } 348 }
346 pub fn completions(&self, position: FilePosition) -> Cancelable<Option<Vec<CompletionItem>>> { 349 pub fn completions(&self, position: FilePosition) -> Cancelable<Option<Vec<CompletionItem>>> {
347 self.imp.completions(position) 350 self.imp.completions(position)
348 } 351 }
349 pub fn assists(&self, file_id: FileId, range: TextRange) -> Cancelable<Vec<SourceChange>> { 352 pub fn assists(&self, frange: FileRange) -> Cancelable<Vec<SourceChange>> {
350 Ok(self.imp.assists(file_id, range)) 353 Ok(self.imp.assists(frange))
351 } 354 }
352 pub fn diagnostics(&self, file_id: FileId) -> Cancelable<Vec<Diagnostic>> { 355 pub fn diagnostics(&self, file_id: FileId) -> Cancelable<Vec<Diagnostic>> {
353 self.imp.diagnostics(file_id) 356 self.imp.diagnostics(file_id)
@@ -358,8 +361,8 @@ impl Analysis {
358 ) -> Cancelable<Option<(FnSignatureInfo, Option<usize>)>> { 361 ) -> Cancelable<Option<(FnSignatureInfo, Option<usize>)>> {
359 self.imp.resolve_callable(position) 362 self.imp.resolve_callable(position)
360 } 363 }
361 pub fn type_of(&self, file_id: FileId, range: TextRange) -> Cancelable<Option<String>> { 364 pub fn type_of(&self, frange: FileRange) -> Cancelable<Option<String>> {
362 self.imp.type_of(file_id, range) 365 self.imp.type_of(frange)
363 } 366 }
364} 367}
365 368
diff --git a/crates/ra_analysis/src/macros.rs b/crates/ra_analysis/src/macros.rs
new file mode 100644
index 000000000..c0dd49dc8
--- /dev/null
+++ b/crates/ra_analysis/src/macros.rs
@@ -0,0 +1,64 @@
1/// Begining of macro expansion.
2///
3/// This code should be moved out of ra_analysis into hir (?) ideally.
4use ra_syntax::{ast, AstNode, SourceFileNode, TextRange};
5
6use crate::{db::RootDatabase, FileId};
7
8pub(crate) fn expand(
9 _db: &RootDatabase,
10 _file_id: FileId,
11 macro_call: ast::MacroCall,
12) -> Option<MacroExpansion> {
13 let path = macro_call.path()?;
14 if path.qualifier().is_some() {
15 return None;
16 }
17 let name_ref = path.segment()?.name_ref()?;
18 if name_ref.text() != "ctry" {
19 return None;
20 }
21
22 let arg = macro_call.token_tree()?;
23 let text = format!(
24 r"
25 fn dummy() {{
26 match {} {{
27 None => return Ok(None),
28 Some(it) => it,
29 }}
30 }}",
31 arg.syntax().text()
32 );
33 let file = SourceFileNode::parse(&text);
34 let match_expr = file.syntax().descendants().find_map(ast::MatchExpr::cast)?;
35 let match_arg = match_expr.expr()?;
36 let ranges_map = vec![(arg.syntax().range(), match_arg.syntax().range())];
37 let res = MacroExpansion {
38 source_file: file,
39 ranges_map,
40 };
41 Some(res)
42}
43
44pub(crate) struct MacroExpansion {
45 pub(crate) source_file: SourceFileNode,
46 pub(crate) ranges_map: Vec<(TextRange, TextRange)>,
47}
48
49impl MacroExpansion {
50 pub(crate) fn source_file(&self) -> &SourceFileNode {
51 &self.source_file
52 }
53 pub(crate) fn map_range_back(&self, tgt_range: TextRange) -> Option<TextRange> {
54 for (s_range, t_range) in self.ranges_map.iter() {
55 if tgt_range.is_subrange(&t_range) {
56 let tgt_at_zero_range = tgt_range - tgt_range.start();
57 let tgt_range_offset = tgt_range.start() - t_range.start();
58 let src_range = tgt_at_zero_range + tgt_range_offset + s_range.start();
59 return Some(src_range);
60 }
61 }
62 None
63 }
64}
diff --git a/crates/ra_analysis/src/syntax_highlighting.rs b/crates/ra_analysis/src/syntax_highlighting.rs
new file mode 100644
index 000000000..38219da71
--- /dev/null
+++ b/crates/ra_analysis/src/syntax_highlighting.rs
@@ -0,0 +1,63 @@
1use ra_syntax::{ast, AstNode,};
2use ra_editor::HighlightedRange;
3use ra_db::SyntaxDatabase;
4
5use crate::{
6 db::RootDatabase,
7 FileId, Cancelable,
8};
9
10pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Cancelable<Vec<HighlightedRange>> {
11 let source_file = db.source_file(file_id);
12 let mut res = ra_editor::highlight(&source_file);
13 for macro_call in source_file
14 .syntax()
15 .descendants()
16 .filter_map(ast::MacroCall::cast)
17 {
18 if let Some(exp) = crate::macros::expand(db, file_id, macro_call) {
19 let mapped_ranges = ra_editor::highlight(exp.source_file())
20 .into_iter()
21 .filter_map(|r| {
22 let mapped_range = exp.map_range_back(r.range)?;
23 let res = HighlightedRange {
24 range: mapped_range,
25 tag: r.tag,
26 };
27 Some(res)
28 });
29 res.extend(mapped_ranges);
30 }
31 }
32 Ok(res)
33}
34
35#[cfg(test)]
36mod tests {
37 use crate::mock_analysis::single_file;
38 use test_utils::assert_eq_dbg;
39
40 #[test]
41 fn highlights_code_inside_macros() {
42 let (analysis, file_id) = single_file(
43 "
44 fn main() {
45 ctry!({ let x = 92; x});
46 }
47 ",
48 );
49 let highlights = analysis.highlight(file_id).unwrap();
50 assert_eq_dbg(
51 r#"[HighlightedRange { range: [13; 15), tag: "keyword" },
52 HighlightedRange { range: [16; 20), tag: "function" },
53 HighlightedRange { range: [41; 46), tag: "macro" },
54 HighlightedRange { range: [49; 52), tag: "keyword" },
55 HighlightedRange { range: [57; 59), tag: "literal" },
56 HighlightedRange { range: [49; 52), tag: "keyword" },
57 HighlightedRange { range: [53; 54), tag: "function" },
58 HighlightedRange { range: [57; 59), tag: "literal" },
59 HighlightedRange { range: [61; 62), tag: "text" }]"#,
60 &highlights,
61 )
62 }
63}