diff options
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ra_hir/src/source_binder.rs | 2 | ||||
-rw-r--r-- | crates/ra_hir_expand/src/quote.rs | 8 | ||||
-rw-r--r-- | crates/ra_ide_api/src/expand_macro.rs | 178 | ||||
-rw-r--r-- | crates/ra_ide_api/src/lib.rs | 6 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/main_loop.rs | 1 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/main_loop/handlers.rs | 18 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/req.rs | 22 | ||||
-rw-r--r-- | crates/ra_syntax/Cargo.toml | 2 | ||||
-rw-r--r-- | crates/ra_syntax/src/algo.rs | 33 | ||||
-rw-r--r-- | crates/ra_syntax/src/ast/extensions.rs | 2 |
10 files changed, 248 insertions, 24 deletions
diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs index f0ed8e2b2..5d3196c2a 100644 --- a/crates/ra_hir/src/source_binder.rs +++ b/crates/ra_hir/src/source_binder.rs | |||
@@ -140,7 +140,7 @@ impl Expansion { | |||
140 | exp_info.map_token_down(token) | 140 | exp_info.map_token_down(token) |
141 | } | 141 | } |
142 | 142 | ||
143 | fn file_id(&self) -> HirFileId { | 143 | pub fn file_id(&self) -> HirFileId { |
144 | self.macro_call_id.as_file(MacroFileKind::Items) | 144 | self.macro_call_id.as_file(MacroFileKind::Items) |
145 | } | 145 | } |
146 | } | 146 | } |
diff --git a/crates/ra_hir_expand/src/quote.rs b/crates/ra_hir_expand/src/quote.rs index 35133d216..65a35e52f 100644 --- a/crates/ra_hir_expand/src/quote.rs +++ b/crates/ra_hir_expand/src/quote.rs | |||
@@ -172,12 +172,12 @@ impl_to_to_tokentrees! { | |||
172 | u32 => self { tt::Literal{text: self.to_string().into()} }; | 172 | u32 => self { tt::Literal{text: self.to_string().into()} }; |
173 | usize => self { tt::Literal{text: self.to_string().into()}}; | 173 | usize => self { tt::Literal{text: self.to_string().into()}}; |
174 | i32 => self { tt::Literal{text: self.to_string().into()}}; | 174 | i32 => self { tt::Literal{text: self.to_string().into()}}; |
175 | &str => self { tt::Literal{text: self.to_string().into()}}; | ||
176 | String => self { tt::Literal{text: self.into()}}; | ||
177 | tt::Leaf => self { self }; | 175 | tt::Leaf => self { self }; |
178 | tt::Literal => self { self }; | 176 | tt::Literal => self { self }; |
179 | tt::Ident => self { self }; | 177 | tt::Ident => self { self }; |
180 | tt::Punct => self { self } | 178 | tt::Punct => self { self }; |
179 | &str => self { tt::Literal{text: format!("{:?}", self.escape_default().to_string()).into()}}; | ||
180 | String => self { tt::Literal{text: format!("{:?}", self.escape_default().to_string()).into()}} | ||
181 | } | 181 | } |
182 | 182 | ||
183 | #[cfg(test)] | 183 | #[cfg(test)] |
@@ -200,7 +200,7 @@ mod tests { | |||
200 | let a = 20; | 200 | let a = 20; |
201 | assert_eq!(quote!(#a).to_string(), "20"); | 201 | assert_eq!(quote!(#a).to_string(), "20"); |
202 | let s: String = "hello".into(); | 202 | let s: String = "hello".into(); |
203 | assert_eq!(quote!(#s).to_string(), "hello"); | 203 | assert_eq!(quote!(#s).to_string(), "\"hello\""); |
204 | } | 204 | } |
205 | 205 | ||
206 | fn mk_ident(name: &str) -> tt::Ident { | 206 | fn mk_ident(name: &str) -> tt::Ident { |
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..e9eb2a7fb --- /dev/null +++ b/crates/ra_ide_api/src/expand_macro.rs | |||
@@ -0,0 +1,178 @@ | |||
1 | //! This modules implements "expand macro" functionality in the IDE | ||
2 | |||
3 | use crate::{db::RootDatabase, FilePosition}; | ||
4 | use hir::db::AstDatabase; | ||
5 | use ra_db::SourceDatabase; | ||
6 | use rustc_hash::FxHashMap; | ||
7 | |||
8 | use ra_syntax::{ | ||
9 | algo::{find_node_at_offset, replace_descendants}, | ||
10 | ast::{self}, | ||
11 | AstNode, NodeOrToken, SyntaxKind, SyntaxNode, WalkEvent, T, | ||
12 | }; | ||
13 | |||
14 | pub struct ExpandedMacro { | ||
15 | pub name: String, | ||
16 | pub expansion: String, | ||
17 | } | ||
18 | |||
19 | pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<ExpandedMacro> { | ||
20 | let parse = db.parse(position.file_id); | ||
21 | let file = parse.tree(); | ||
22 | let name_ref = find_node_at_offset::<ast::NameRef>(file.syntax(), position.offset)?; | ||
23 | let mac = name_ref.syntax().ancestors().find_map(ast::MacroCall::cast)?; | ||
24 | |||
25 | let source = hir::Source::new(position.file_id.into(), mac.syntax()); | ||
26 | let expanded = expand_macro_recur(db, source, &mac)?; | ||
27 | |||
28 | // FIXME: | ||
29 | // macro expansion may lose all white space information | ||
30 | // But we hope someday we can use ra_fmt for that | ||
31 | let expansion = insert_whitespaces(expanded); | ||
32 | Some(ExpandedMacro { name: name_ref.text().to_string(), expansion }) | ||
33 | } | ||
34 | |||
35 | fn expand_macro_recur( | ||
36 | db: &RootDatabase, | ||
37 | source: hir::Source<&SyntaxNode>, | ||
38 | macro_call: &ast::MacroCall, | ||
39 | ) -> Option<SyntaxNode> { | ||
40 | let analyzer = hir::SourceAnalyzer::new(db, source, None); | ||
41 | let expansion = analyzer.expand(db, ¯o_call)?; | ||
42 | let macro_file_id = expansion.file_id(); | ||
43 | let expanded: SyntaxNode = db.parse_or_expand(macro_file_id)?; | ||
44 | |||
45 | let children = expanded.descendants().filter_map(ast::MacroCall::cast); | ||
46 | let mut replaces = FxHashMap::default(); | ||
47 | |||
48 | for child in children.into_iter() { | ||
49 | let source = hir::Source::new(macro_file_id, source.ast); | ||
50 | let new_node = expand_macro_recur(db, source, &child)?; | ||
51 | |||
52 | replaces.insert(child.syntax().clone().into(), new_node.into()); | ||
53 | } | ||
54 | |||
55 | Some(replace_descendants(&expanded, &replaces)) | ||
56 | } | ||
57 | |||
58 | // FIXME: It would also be cool to share logic here and in the mbe tests, | ||
59 | // which are pretty unreadable at the moment. | ||
60 | fn insert_whitespaces(syn: SyntaxNode) -> String { | ||
61 | use SyntaxKind::*; | ||
62 | |||
63 | let mut res = String::new(); | ||
64 | let mut token_iter = syn | ||
65 | .preorder_with_tokens() | ||
66 | .filter_map(|event| { | ||
67 | if let WalkEvent::Enter(NodeOrToken::Token(token)) = event { | ||
68 | Some(token) | ||
69 | } else { | ||
70 | None | ||
71 | } | ||
72 | }) | ||
73 | .peekable(); | ||
74 | |||
75 | let mut indent = 0; | ||
76 | let mut last: Option<SyntaxKind> = None; | ||
77 | |||
78 | while let Some(token) = token_iter.next() { | ||
79 | let mut is_next = |f: fn(SyntaxKind) -> bool, default| -> bool { | ||
80 | token_iter.peek().map(|it| f(it.kind())).unwrap_or(default) | ||
81 | }; | ||
82 | let is_last = |f: fn(SyntaxKind) -> bool, default| -> bool { | ||
83 | last.map(|it| f(it)).unwrap_or(default) | ||
84 | }; | ||
85 | |||
86 | res += &match token.kind() { | ||
87 | k @ _ | ||
88 | if (k.is_keyword() || k.is_literal() || k == IDENT) | ||
89 | && is_next(|it| !it.is_punct(), true) => | ||
90 | { | ||
91 | token.text().to_string() + " " | ||
92 | } | ||
93 | L_CURLY if is_next(|it| it != R_CURLY, true) => { | ||
94 | indent += 1; | ||
95 | format!(" {{\n{}", " ".repeat(indent)) | ||
96 | } | ||
97 | R_CURLY if is_last(|it| it != L_CURLY, true) => { | ||
98 | indent = indent.checked_sub(1).unwrap_or(0); | ||
99 | format!("\n}}{}", " ".repeat(indent)) | ||
100 | } | ||
101 | R_CURLY => { | ||
102 | indent = indent.checked_sub(1).unwrap_or(0); | ||
103 | format!("}}\n{}", " ".repeat(indent)) | ||
104 | } | ||
105 | T![;] => format!(";\n{}", " ".repeat(indent)), | ||
106 | T![->] => " -> ".to_string(), | ||
107 | T![=] => " = ".to_string(), | ||
108 | T![=>] => " => ".to_string(), | ||
109 | _ => token.text().to_string(), | ||
110 | }; | ||
111 | |||
112 | last = Some(token.kind()); | ||
113 | } | ||
114 | |||
115 | res | ||
116 | } | ||
117 | |||
118 | #[cfg(test)] | ||
119 | mod tests { | ||
120 | use super::*; | ||
121 | use crate::mock_analysis::analysis_and_position; | ||
122 | use insta::assert_snapshot; | ||
123 | |||
124 | fn check_expand_macro(fixture: &str) -> ExpandedMacro { | ||
125 | let (analysis, pos) = analysis_and_position(fixture); | ||
126 | analysis.expand_macro(pos).unwrap().unwrap() | ||
127 | } | ||
128 | |||
129 | #[test] | ||
130 | fn macro_expand_recursive_expansion() { | ||
131 | let res = check_expand_macro( | ||
132 | r#" | ||
133 | //- /lib.rs | ||
134 | macro_rules! bar { | ||
135 | () => { fn b() {} } | ||
136 | } | ||
137 | macro_rules! foo { | ||
138 | () => { bar!(); } | ||
139 | } | ||
140 | macro_rules! baz { | ||
141 | () => { foo!(); } | ||
142 | } | ||
143 | f<|>oo!(); | ||
144 | "#, | ||
145 | ); | ||
146 | |||
147 | assert_eq!(res.name, "foo"); | ||
148 | assert_snapshot!(res.expansion, @r###" | ||
149 | fn b(){} | ||
150 | "###); | ||
151 | } | ||
152 | |||
153 | #[test] | ||
154 | fn macro_expand_multiple_lines() { | ||
155 | let res = check_expand_macro( | ||
156 | r#" | ||
157 | //- /lib.rs | ||
158 | macro_rules! foo { | ||
159 | () => { | ||
160 | fn some_thing() -> u32 { | ||
161 | let a = 0; | ||
162 | a + 10 | ||
163 | } | ||
164 | } | ||
165 | } | ||
166 | f<|>oo!(); | ||
167 | "#, | ||
168 | ); | ||
169 | |||
170 | assert_eq!(res.name, "foo"); | ||
171 | assert_snapshot!(res.expansion, @r###" | ||
172 | fn some_thing() -> u32 { | ||
173 | let a = 0; | ||
174 | a+10 | ||
175 | } | ||
176 | "###); | ||
177 | } | ||
178 | } | ||
diff --git a/crates/ra_ide_api/src/lib.rs b/crates/ra_ide_api/src/lib.rs index fcb3da90e..62ad996bc 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; |
@@ -65,6 +66,7 @@ pub use crate::{ | |||
65 | completion::{CompletionItem, CompletionItemKind, InsertTextFormat}, | 66 | completion::{CompletionItem, CompletionItemKind, InsertTextFormat}, |
66 | diagnostics::Severity, | 67 | diagnostics::Severity, |
67 | display::{file_structure, FunctionSignature, NavigationTarget, StructureNode}, | 68 | display::{file_structure, FunctionSignature, NavigationTarget, StructureNode}, |
69 | expand_macro::ExpandedMacro, | ||
68 | feature_flags::FeatureFlags, | 70 | feature_flags::FeatureFlags, |
69 | folding_ranges::{Fold, FoldKind}, | 71 | folding_ranges::{Fold, FoldKind}, |
70 | hover::HoverResult, | 72 | hover::HoverResult, |
@@ -296,6 +298,10 @@ impl Analysis { | |||
296 | self.with_db(|db| syntax_tree::syntax_tree(&db, file_id, text_range)) | 298 | self.with_db(|db| syntax_tree::syntax_tree(&db, file_id, text_range)) |
297 | } | 299 | } |
298 | 300 | ||
301 | pub fn expand_macro(&self, position: FilePosition) -> Cancelable<Option<ExpandedMacro>> { | ||
302 | self.with_db(|db| expand_macro::expand_macro(db, position)) | ||
303 | } | ||
304 | |||
299 | /// Returns an edit to remove all newlines in the range, cleaning up minor | 305 | /// Returns an edit to remove all newlines in the range, cleaning up minor |
300 | /// stuff like trailing commas. | 306 | /// stuff like trailing commas. |
301 | pub fn join_lines(&self, frange: FileRange) -> Cancelable<SourceChange> { | 307 | 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 3e2ac3683..0dc0aeee8 100644 --- a/crates/ra_lsp_server/src/main_loop.rs +++ b/crates/ra_lsp_server/src/main_loop.rs | |||
@@ -437,6 +437,7 @@ fn on_request( | |||
437 | })? | 437 | })? |
438 | .on::<req::AnalyzerStatus>(handlers::handle_analyzer_status)? | 438 | .on::<req::AnalyzerStatus>(handlers::handle_analyzer_status)? |
439 | .on::<req::SyntaxTree>(handlers::handle_syntax_tree)? | 439 | .on::<req::SyntaxTree>(handlers::handle_syntax_tree)? |
440 | .on::<req::ExpandMacro>(handlers::handle_expand_macro)? | ||
440 | .on::<req::OnTypeFormatting>(handlers::handle_on_type_formatting)? | 441 | .on::<req::OnTypeFormatting>(handlers::handle_on_type_formatting)? |
441 | .on::<req::DocumentSymbolRequest>(handlers::handle_document_symbol)? | 442 | .on::<req::DocumentSymbolRequest>(handlers::handle_document_symbol)? |
442 | .on::<req::WorkspaceSymbol>(handlers::handle_workspace_symbol)? | 443 | .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 7347a78c7..e552f2106 100644 --- a/crates/ra_lsp_server/src/main_loop/handlers.rs +++ b/crates/ra_lsp_server/src/main_loop/handlers.rs | |||
@@ -47,6 +47,24 @@ 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<req::ExpandedMacro>> { | ||
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) => { | ||
62 | let res = world.analysis().expand_macro(FilePosition { file_id, offset })?; | ||
63 | Ok(res.map(|it| req::ExpandedMacro { name: it.name, expansion: it.expansion })) | ||
64 | } | ||
65 | } | ||
66 | } | ||
67 | |||
50 | pub fn handle_selection_range( | 68 | pub fn handle_selection_range( |
51 | world: WorldSnapshot, | 69 | world: WorldSnapshot, |
52 | params: req::SelectionRangeParams, | 70 | params: req::SelectionRangeParams, |
diff --git a/crates/ra_lsp_server/src/req.rs b/crates/ra_lsp_server/src/req.rs index d25fc5726..39361b7e8 100644 --- a/crates/ra_lsp_server/src/req.rs +++ b/crates/ra_lsp_server/src/req.rs | |||
@@ -45,6 +45,28 @@ pub struct SyntaxTreeParams { | |||
45 | pub range: Option<Range>, | 45 | pub range: Option<Range>, |
46 | } | 46 | } |
47 | 47 | ||
48 | #[derive(Serialize, Debug)] | ||
49 | #[serde(rename_all = "camelCase")] | ||
50 | pub struct ExpandedMacro { | ||
51 | pub name: String, | ||
52 | pub expansion: String, | ||
53 | } | ||
54 | |||
55 | pub enum ExpandMacro {} | ||
56 | |||
57 | impl Request for ExpandMacro { | ||
58 | type Params = ExpandMacroParams; | ||
59 | type Result = Option<ExpandedMacro>; | ||
60 | const METHOD: &'static str = "rust-analyzer/expandMacro"; | ||
61 | } | ||
62 | |||
63 | #[derive(Deserialize, Debug)] | ||
64 | #[serde(rename_all = "camelCase")] | ||
65 | pub struct ExpandMacroParams { | ||
66 | pub text_document: TextDocumentIdentifier, | ||
67 | pub position: Option<Position>, | ||
68 | } | ||
69 | |||
48 | pub enum SelectionRangeRequest {} | 70 | pub enum SelectionRangeRequest {} |
49 | 71 | ||
50 | impl Request for SelectionRangeRequest { | 72 | impl Request for SelectionRangeRequest { |
diff --git a/crates/ra_syntax/Cargo.toml b/crates/ra_syntax/Cargo.toml index 45a18a73f..5db2b58c0 100644 --- a/crates/ra_syntax/Cargo.toml +++ b/crates/ra_syntax/Cargo.toml | |||
@@ -12,7 +12,7 @@ doctest = false | |||
12 | 12 | ||
13 | [dependencies] | 13 | [dependencies] |
14 | itertools = "0.8.0" | 14 | itertools = "0.8.0" |
15 | rowan = "0.6.1" | 15 | rowan = "0.7.0" |
16 | rustc_lexer = "0.1.0" | 16 | rustc_lexer = "0.1.0" |
17 | rustc-hash = "1.0.1" | 17 | rustc-hash = "1.0.1" |
18 | arrayvec = "0.5.1" | 18 | arrayvec = "0.5.1" |
diff --git a/crates/ra_syntax/src/algo.rs b/crates/ra_syntax/src/algo.rs index 7cfea70f9..1c075082a 100644 --- a/crates/ra_syntax/src/algo.rs +++ b/crates/ra_syntax/src/algo.rs | |||
@@ -134,23 +134,19 @@ pub fn insert_children( | |||
134 | to_green_element(element) | 134 | to_green_element(element) |
135 | }); | 135 | }); |
136 | 136 | ||
137 | let old_children = parent.green().children(); | 137 | let mut old_children = parent.green().children().map(|it| match it { |
138 | NodeOrToken::Token(it) => NodeOrToken::Token(it.clone()), | ||
139 | NodeOrToken::Node(it) => NodeOrToken::Node(it.clone()), | ||
140 | }); | ||
138 | 141 | ||
139 | let new_children = match &position { | 142 | let new_children = match &position { |
140 | InsertPosition::First => { | 143 | InsertPosition::First => to_insert.chain(old_children).collect::<Box<[_]>>(), |
141 | to_insert.chain(old_children.iter().cloned()).collect::<Box<[_]>>() | 144 | InsertPosition::Last => old_children.chain(to_insert).collect::<Box<[_]>>(), |
142 | } | ||
143 | InsertPosition::Last => old_children.iter().cloned().chain(to_insert).collect::<Box<[_]>>(), | ||
144 | InsertPosition::Before(anchor) | InsertPosition::After(anchor) => { | 145 | InsertPosition::Before(anchor) | InsertPosition::After(anchor) => { |
145 | let take_anchor = if let InsertPosition::After(_) = position { 1 } else { 0 }; | 146 | let take_anchor = if let InsertPosition::After(_) = position { 1 } else { 0 }; |
146 | let split_at = position_of_child(parent, anchor.clone()) + take_anchor; | 147 | let split_at = position_of_child(parent, anchor.clone()) + take_anchor; |
147 | let (before, after) = old_children.split_at(split_at); | 148 | let before = old_children.by_ref().take(split_at).collect::<Vec<_>>(); |
148 | before | 149 | before.into_iter().chain(to_insert).chain(old_children).collect::<Box<[_]>>() |
149 | .iter() | ||
150 | .cloned() | ||
151 | .chain(to_insert) | ||
152 | .chain(after.iter().cloned()) | ||
153 | .collect::<Box<[_]>>() | ||
154 | } | 150 | } |
155 | }; | 151 | }; |
156 | 152 | ||
@@ -168,13 +164,16 @@ pub fn replace_children( | |||
168 | ) -> SyntaxNode { | 164 | ) -> SyntaxNode { |
169 | let start = position_of_child(parent, to_delete.start().clone()); | 165 | let start = position_of_child(parent, to_delete.start().clone()); |
170 | let end = position_of_child(parent, to_delete.end().clone()); | 166 | let end = position_of_child(parent, to_delete.end().clone()); |
171 | let old_children = parent.green().children(); | 167 | let mut old_children = parent.green().children().map(|it| match it { |
168 | NodeOrToken::Token(it) => NodeOrToken::Token(it.clone()), | ||
169 | NodeOrToken::Node(it) => NodeOrToken::Node(it.clone()), | ||
170 | }); | ||
172 | 171 | ||
173 | let new_children = old_children[..start] | 172 | let before = old_children.by_ref().take(start).collect::<Vec<_>>(); |
174 | .iter() | 173 | let new_children = before |
175 | .cloned() | 174 | .into_iter() |
176 | .chain(to_insert.map(to_green_element)) | 175 | .chain(to_insert.map(to_green_element)) |
177 | .chain(old_children[end + 1..].iter().cloned()) | 176 | .chain(old_children.skip(end + 1 - start)) |
178 | .collect::<Box<[_]>>(); | 177 | .collect::<Box<[_]>>(); |
179 | with_children(parent, new_children) | 178 | with_children(parent, new_children) |
180 | } | 179 | } |
diff --git a/crates/ra_syntax/src/ast/extensions.rs b/crates/ra_syntax/src/ast/extensions.rs index 761b2435c..4851dacb2 100644 --- a/crates/ra_syntax/src/ast/extensions.rs +++ b/crates/ra_syntax/src/ast/extensions.rs | |||
@@ -32,7 +32,7 @@ impl ast::NameRef { | |||
32 | } | 32 | } |
33 | 33 | ||
34 | fn text_of_first_token(node: &SyntaxNode) -> &SmolStr { | 34 | fn text_of_first_token(node: &SyntaxNode) -> &SmolStr { |
35 | node.green().children().first().and_then(|it| it.as_token()).unwrap().text() | 35 | node.green().children().next().and_then(|it| it.into_token()).unwrap().text() |
36 | } | 36 | } |
37 | 37 | ||
38 | impl ast::Attr { | 38 | impl ast::Attr { |