diff options
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ra_ide_api/src/expand_macro.rs | 112 | ||||
-rw-r--r-- | crates/ra_ide_api/src/lib.rs | 5 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/main_loop.rs | 1 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/main_loop/handlers.rs | 15 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/req.rs | 15 |
5 files changed, 148 insertions, 0 deletions
diff --git a/crates/ra_ide_api/src/expand_macro.rs b/crates/ra_ide_api/src/expand_macro.rs new file mode 100644 index 000000000..48dc90932 --- /dev/null +++ b/crates/ra_ide_api/src/expand_macro.rs | |||
@@ -0,0 +1,112 @@ | |||
1 | //! FIXME: write short doc here | ||
2 | |||
3 | use crate::{db::RootDatabase, FilePosition}; | ||
4 | use ra_db::SourceDatabase; | ||
5 | use rustc_hash::FxHashMap; | ||
6 | |||
7 | use hir::db::AstDatabase; | ||
8 | use ra_syntax::{ | ||
9 | algo::{find_node_at_offset, replace_descendants}, | ||
10 | ast::{self}, | ||
11 | AstNode, NodeOrToken, SyntaxKind, SyntaxNode, WalkEvent, | ||
12 | }; | ||
13 | |||
14 | fn insert_whitespaces(syn: SyntaxNode) -> String { | ||
15 | let mut res = String::new(); | ||
16 | |||
17 | let mut token_iter = syn | ||
18 | .preorder_with_tokens() | ||
19 | .filter_map(|event| { | ||
20 | if let WalkEvent::Enter(NodeOrToken::Token(token)) = event { | ||
21 | Some(token) | ||
22 | } else { | ||
23 | None | ||
24 | } | ||
25 | }) | ||
26 | .peekable(); | ||
27 | |||
28 | while let Some(token) = token_iter.next() { | ||
29 | res += &token.text().to_string(); | ||
30 | if token.kind().is_keyword() | ||
31 | || token.kind().is_literal() | ||
32 | || token.kind() == SyntaxKind::IDENT | ||
33 | { | ||
34 | if !token_iter.peek().map(|it| it.kind().is_punct()).unwrap_or(false) { | ||
35 | res += " "; | ||
36 | } | ||
37 | } | ||
38 | } | ||
39 | |||
40 | res | ||
41 | } | ||
42 | |||
43 | fn expand_macro_recur( | ||
44 | db: &RootDatabase, | ||
45 | source: hir::Source<&SyntaxNode>, | ||
46 | macro_call: &ast::MacroCall, | ||
47 | ) -> Option<SyntaxNode> { | ||
48 | let analyzer = hir::SourceAnalyzer::new(db, source, None); | ||
49 | let expansion = analyzer.expand(db, ¯o_call)?; | ||
50 | let expanded: SyntaxNode = db.parse_or_expand(expansion.file_id())?; | ||
51 | |||
52 | let children = expanded.descendants().filter_map(ast::MacroCall::cast); | ||
53 | let mut replaces = FxHashMap::default(); | ||
54 | |||
55 | for child in children.into_iter() { | ||
56 | let source = hir::Source::new(expansion.file_id(), source.ast); | ||
57 | let new_node = expand_macro_recur(db, source, &child)?; | ||
58 | |||
59 | replaces.insert(child.syntax().clone().into(), new_node.into()); | ||
60 | } | ||
61 | |||
62 | Some(replace_descendants(&expanded, &replaces)) | ||
63 | } | ||
64 | |||
65 | pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<(String, String)> { | ||
66 | let parse = db.parse(position.file_id); | ||
67 | let file = parse.tree(); | ||
68 | let name_ref = find_node_at_offset::<ast::NameRef>(file.syntax(), position.offset)?; | ||
69 | let mac = name_ref.syntax().ancestors().find_map(ast::MacroCall::cast)?; | ||
70 | |||
71 | let source = hir::Source::new(position.file_id.into(), mac.syntax()); | ||
72 | |||
73 | let expanded = expand_macro_recur(db, source, &mac)?; | ||
74 | |||
75 | // FIXME: | ||
76 | // macro expansion may lose all white space information | ||
77 | // But we hope someday we can use ra_fmt for that | ||
78 | let res = insert_whitespaces(expanded); | ||
79 | Some((name_ref.text().to_string(), res)) | ||
80 | } | ||
81 | |||
82 | #[cfg(test)] | ||
83 | mod tests { | ||
84 | use crate::mock_analysis::analysis_and_position; | ||
85 | |||
86 | fn check_expand_macro(fixture: &str, expected: (&str, &str)) { | ||
87 | let (analysis, pos) = analysis_and_position(fixture); | ||
88 | |||
89 | let result = analysis.expand_macro(pos).unwrap().unwrap(); | ||
90 | assert_eq!(result, (expected.0.to_string(), expected.1.to_string())); | ||
91 | } | ||
92 | |||
93 | #[test] | ||
94 | fn macro_expand_recursive_expansion() { | ||
95 | check_expand_macro( | ||
96 | r#" | ||
97 | //- /lib.rs | ||
98 | macro_rules! bar { | ||
99 | () => { fn b() {} } | ||
100 | } | ||
101 | macro_rules! foo { | ||
102 | () => { bar!(); } | ||
103 | } | ||
104 | macro_rules! baz { | ||
105 | () => { foo!(); } | ||
106 | } | ||
107 | f<|>oo!(); | ||
108 | "#, | ||
109 | ("foo", "fn b(){}"), | ||
110 | ); | ||
111 | } | ||
112 | } | ||
diff --git a/crates/ra_ide_api/src/lib.rs b/crates/ra_ide_api/src/lib.rs index 110ddcd62..d1b73ef6f 100644 --- a/crates/ra_ide_api/src/lib.rs +++ b/crates/ra_ide_api/src/lib.rs | |||
@@ -42,6 +42,7 @@ mod display; | |||
42 | mod inlay_hints; | 42 | mod inlay_hints; |
43 | mod wasm_shims; | 43 | mod wasm_shims; |
44 | mod expand; | 44 | mod expand; |
45 | mod expand_macro; | ||
45 | 46 | ||
46 | #[cfg(test)] | 47 | #[cfg(test)] |
47 | mod marks; | 48 | mod marks; |
@@ -296,6 +297,10 @@ impl Analysis { | |||
296 | self.with_db(|db| syntax_tree::syntax_tree(&db, file_id, text_range)) | 297 | self.with_db(|db| syntax_tree::syntax_tree(&db, file_id, text_range)) |
297 | } | 298 | } |
298 | 299 | ||
300 | pub fn expand_macro(&self, position: FilePosition) -> Cancelable<Option<(String, String)>> { | ||
301 | self.with_db(|db| expand_macro::expand_macro(db, position)) | ||
302 | } | ||
303 | |||
299 | /// Returns an edit to remove all newlines in the range, cleaning up minor | 304 | /// Returns an edit to remove all newlines in the range, cleaning up minor |
300 | /// stuff like trailing commas. | 305 | /// stuff like trailing commas. |
301 | pub fn join_lines(&self, frange: FileRange) -> Cancelable<SourceChange> { | 306 | pub fn join_lines(&self, frange: FileRange) -> Cancelable<SourceChange> { |
diff --git a/crates/ra_lsp_server/src/main_loop.rs b/crates/ra_lsp_server/src/main_loop.rs index 379dab438..f828efdee 100644 --- a/crates/ra_lsp_server/src/main_loop.rs +++ b/crates/ra_lsp_server/src/main_loop.rs | |||
@@ -436,6 +436,7 @@ fn on_request( | |||
436 | })? | 436 | })? |
437 | .on::<req::AnalyzerStatus>(handlers::handle_analyzer_status)? | 437 | .on::<req::AnalyzerStatus>(handlers::handle_analyzer_status)? |
438 | .on::<req::SyntaxTree>(handlers::handle_syntax_tree)? | 438 | .on::<req::SyntaxTree>(handlers::handle_syntax_tree)? |
439 | .on::<req::ExpandMacro>(handlers::handle_expand_macro)? | ||
439 | .on::<req::OnTypeFormatting>(handlers::handle_on_type_formatting)? | 440 | .on::<req::OnTypeFormatting>(handlers::handle_on_type_formatting)? |
440 | .on::<req::DocumentSymbolRequest>(handlers::handle_document_symbol)? | 441 | .on::<req::DocumentSymbolRequest>(handlers::handle_document_symbol)? |
441 | .on::<req::WorkspaceSymbol>(handlers::handle_workspace_symbol)? | 442 | .on::<req::WorkspaceSymbol>(handlers::handle_workspace_symbol)? |
diff --git a/crates/ra_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs index 20f9aee13..783b0a827 100644 --- a/crates/ra_lsp_server/src/main_loop/handlers.rs +++ b/crates/ra_lsp_server/src/main_loop/handlers.rs | |||
@@ -47,6 +47,21 @@ pub fn handle_syntax_tree(world: WorldSnapshot, params: req::SyntaxTreeParams) - | |||
47 | Ok(res) | 47 | Ok(res) |
48 | } | 48 | } |
49 | 49 | ||
50 | pub fn handle_expand_macro( | ||
51 | world: WorldSnapshot, | ||
52 | params: req::ExpandMacroParams, | ||
53 | ) -> Result<Option<(String, String)>> { | ||
54 | let _p = profile("handle_expand_macro"); | ||
55 | let file_id = params.text_document.try_conv_with(&world)?; | ||
56 | let line_index = world.analysis().file_line_index(file_id)?; | ||
57 | let offset = params.position.map(|p| p.conv_with(&line_index)); | ||
58 | |||
59 | match offset { | ||
60 | None => Ok(None), | ||
61 | Some(offset) => Ok(world.analysis().expand_macro(FilePosition { file_id, offset })?), | ||
62 | } | ||
63 | } | ||
64 | |||
50 | pub fn handle_selection_range( | 65 | pub fn handle_selection_range( |
51 | world: WorldSnapshot, | 66 | world: WorldSnapshot, |
52 | params: req::SelectionRangeParams, | 67 | params: req::SelectionRangeParams, |
diff --git a/crates/ra_lsp_server/src/req.rs b/crates/ra_lsp_server/src/req.rs index d25fc5726..dbc0e9624 100644 --- a/crates/ra_lsp_server/src/req.rs +++ b/crates/ra_lsp_server/src/req.rs | |||
@@ -45,6 +45,21 @@ pub struct SyntaxTreeParams { | |||
45 | pub range: Option<Range>, | 45 | pub range: Option<Range>, |
46 | } | 46 | } |
47 | 47 | ||
48 | pub enum ExpandMacro {} | ||
49 | |||
50 | impl Request for ExpandMacro { | ||
51 | type Params = ExpandMacroParams; | ||
52 | type Result = Option<(String, String)>; | ||
53 | const METHOD: &'static str = "rust-analyzer/expandMacro"; | ||
54 | } | ||
55 | |||
56 | #[derive(Deserialize, Debug)] | ||
57 | #[serde(rename_all = "camelCase")] | ||
58 | pub struct ExpandMacroParams { | ||
59 | pub text_document: TextDocumentIdentifier, | ||
60 | pub position: Option<Position>, | ||
61 | } | ||
62 | |||
48 | pub enum SelectionRangeRequest {} | 63 | pub enum SelectionRangeRequest {} |
49 | 64 | ||
50 | impl Request for SelectionRangeRequest { | 65 | impl Request for SelectionRangeRequest { |