aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide')
-rw-r--r--crates/ra_ide/src/assists.rs24
-rw-r--r--crates/ra_ide/src/completion/complete_scope.rs136
-rw-r--r--crates/ra_ide/src/inlay_hints.rs5
-rw-r--r--crates/ra_ide/src/join_lines.rs85
-rw-r--r--crates/ra_ide/src/references.rs74
-rw-r--r--crates/ra_ide/src/references/rename.rs19
6 files changed, 137 insertions, 206 deletions
diff --git a/crates/ra_ide/src/assists.rs b/crates/ra_ide/src/assists.rs
index b60b1a60d..40d56a4f7 100644
--- a/crates/ra_ide/src/assists.rs
+++ b/crates/ra_ide/src/assists.rs
@@ -1,6 +1,5 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use either::Either;
4use ra_assists::{resolved_assists, AssistAction, AssistLabel}; 3use ra_assists::{resolved_assists, AssistAction, AssistLabel};
5use ra_db::{FilePosition, FileRange}; 4use ra_db::{FilePosition, FileRange};
6use ra_ide_db::RootDatabase; 5use ra_ide_db::RootDatabase;
@@ -13,7 +12,8 @@ pub use ra_assists::AssistId;
13pub struct Assist { 12pub struct Assist {
14 pub id: AssistId, 13 pub id: AssistId,
15 pub label: String, 14 pub label: String,
16 pub change_data: Either<SourceChange, Vec<SourceChange>>, 15 pub group_label: Option<String>,
16 pub source_change: SourceChange,
17} 17}
18 18
19pub(crate) fn assists(db: &RootDatabase, frange: FileRange) -> Vec<Assist> { 19pub(crate) fn assists(db: &RootDatabase, frange: FileRange) -> Vec<Assist> {
@@ -25,17 +25,8 @@ pub(crate) fn assists(db: &RootDatabase, frange: FileRange) -> Vec<Assist> {
25 Assist { 25 Assist {
26 id: assist_label.id, 26 id: assist_label.id,
27 label: assist_label.label.clone(), 27 label: assist_label.label.clone(),
28 change_data: match assist.action_data { 28 group_label: assist.group_label.map(|it| it.0),
29 Either::Left(action) => { 29 source_change: action_to_edit(assist.action, file_id, assist_label),
30 Either::Left(action_to_edit(action, file_id, assist_label))
31 }
32 Either::Right(actions) => Either::Right(
33 actions
34 .into_iter()
35 .map(|action| action_to_edit(action, file_id, assist_label))
36 .collect(),
37 ),
38 },
39 } 30 }
40 }) 31 })
41 .collect() 32 .collect()
@@ -47,9 +38,6 @@ fn action_to_edit(
47 assist_label: &AssistLabel, 38 assist_label: &AssistLabel,
48) -> SourceChange { 39) -> SourceChange {
49 let file_edit = SourceFileEdit { file_id, edit: action.edit }; 40 let file_edit = SourceFileEdit { file_id, edit: action.edit };
50 SourceChange::source_file_edit( 41 SourceChange::source_file_edit(assist_label.label.clone(), file_edit)
51 action.label.unwrap_or_else(|| assist_label.label.clone()), 42 .with_cursor_opt(action.cursor_position.map(|offset| FilePosition { offset, file_id }))
52 file_edit,
53 )
54 .with_cursor_opt(action.cursor_position.map(|offset| FilePosition { offset, file_id }))
55} 43}
diff --git a/crates/ra_ide/src/completion/complete_scope.rs b/crates/ra_ide/src/completion/complete_scope.rs
index 64b04ec2b..e2ee86dd1 100644
--- a/crates/ra_ide/src/completion/complete_scope.rs
+++ b/crates/ra_ide/src/completion/complete_scope.rs
@@ -1,12 +1,6 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use ra_assists::auto_import_text_edit; 3use crate::completion::{CompletionContext, Completions};
4use ra_syntax::{ast, AstNode, SmolStr};
5use ra_text_edit::TextEditBuilder;
6use rustc_hash::FxHashMap;
7
8use crate::completion::{CompletionContext, CompletionItem, CompletionKind, Completions};
9use hir::{ModPath, PathKind};
10 4
11pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) { 5pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) {
12 if !ctx.is_trivial_path { 6 if !ctx.is_trivial_path {
@@ -16,138 +10,14 @@ pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) {
16 ctx.analyzer.process_all_names(ctx.db, &mut |name, res| { 10 ctx.analyzer.process_all_names(ctx.db, &mut |name, res| {
17 acc.add_resolution(ctx, name.to_string(), &res) 11 acc.add_resolution(ctx, name.to_string(), &res)
18 }); 12 });
19
20 // auto-import
21 // We fetch ident from the original file, because we need to pre-filter auto-imports
22 if ast::NameRef::cast(ctx.token.parent()).is_some() {
23 let import_resolver = ImportResolver::new();
24 let import_names = import_resolver.all_names(ctx.token.text());
25 import_names.into_iter().for_each(|(name, path)| {
26 let edit = {
27 let mut builder = TextEditBuilder::default();
28 builder.replace(ctx.source_range(), name.to_string());
29 auto_import_text_edit(
30 &ctx.token.parent(),
31 &ctx.token.parent(),
32 &path,
33 &mut builder,
34 );
35 builder.finish()
36 };
37
38 // Hack: copied this check form conv.rs beacause auto import can produce edits
39 // that invalidate assert in conv_with.
40 if edit
41 .as_atoms()
42 .iter()
43 .filter(|atom| !ctx.source_range().is_subrange(&atom.delete))
44 .all(|atom| ctx.source_range().intersection(&atom.delete).is_none())
45 {
46 CompletionItem::new(
47 CompletionKind::Reference,
48 ctx.source_range(),
49 build_import_label(&name, &path),
50 )
51 .text_edit(edit)
52 .add_to(acc);
53 }
54 });
55 }
56}
57
58fn build_import_label(name: &str, path: &ModPath) -> String {
59 let mut buf = String::with_capacity(64);
60 buf.push_str(name);
61 buf.push_str(" (");
62 buf.push_str(&path.to_string());
63 buf.push_str(")");
64 buf
65}
66
67#[derive(Debug, Clone, Default)]
68pub(crate) struct ImportResolver {
69 // todo: use fst crate or something like that
70 dummy_names: Vec<(SmolStr, ModPath)>,
71}
72
73impl ImportResolver {
74 pub(crate) fn new() -> Self {
75 use hir::name;
76
77 let dummy_names = vec![
78 (
79 SmolStr::new("fmt"),
80 ModPath { kind: PathKind::Plain, segments: vec![name![std], name![fmt]] },
81 ),
82 (
83 SmolStr::new("io"),
84 ModPath { kind: PathKind::Plain, segments: vec![name![std], name![io]] },
85 ),
86 (
87 SmolStr::new("iter"),
88 ModPath { kind: PathKind::Plain, segments: vec![name![std], name![iter]] },
89 ),
90 (
91 SmolStr::new("hash"),
92 ModPath { kind: PathKind::Plain, segments: vec![name![std], name![hash]] },
93 ),
94 (
95 SmolStr::new("Debug"),
96 ModPath {
97 kind: PathKind::Plain,
98 segments: vec![name![std], name![fmt], name![Debug]],
99 },
100 ),
101 (
102 SmolStr::new("Display"),
103 ModPath {
104 kind: PathKind::Plain,
105 segments: vec![name![std], name![fmt], name![Display]],
106 },
107 ),
108 (
109 SmolStr::new("Hash"),
110 ModPath {
111 kind: PathKind::Plain,
112 segments: vec![name![std], name![hash], name![Hash]],
113 },
114 ),
115 (
116 SmolStr::new("Hasher"),
117 ModPath {
118 kind: PathKind::Plain,
119 segments: vec![name![std], name![hash], name![Hasher]],
120 },
121 ),
122 (
123 SmolStr::new("Iterator"),
124 ModPath {
125 kind: PathKind::Plain,
126 segments: vec![name![std], name![iter], name![Iterator]],
127 },
128 ),
129 ];
130
131 ImportResolver { dummy_names }
132 }
133
134 // Returns a map of importable items filtered by name.
135 // The map associates item name with its full path.
136 // todo: should return Resolutions
137 pub(crate) fn all_names(&self, name: &str) -> FxHashMap<SmolStr, ModPath> {
138 if name.len() > 1 {
139 self.dummy_names.iter().filter(|(n, _)| n.contains(name)).cloned().collect()
140 } else {
141 FxHashMap::default()
142 }
143 }
144} 13}
145 14
146#[cfg(test)] 15#[cfg(test)]
147mod tests { 16mod tests {
148 use crate::completion::{do_completion, CompletionItem, CompletionKind};
149 use insta::assert_debug_snapshot; 17 use insta::assert_debug_snapshot;
150 18
19 use crate::completion::{do_completion, CompletionItem, CompletionKind};
20
151 fn do_reference_completion(code: &str) -> Vec<CompletionItem> { 21 fn do_reference_completion(code: &str) -> Vec<CompletionItem> {
152 do_completion(code, CompletionKind::Reference) 22 do_completion(code, CompletionKind::Reference)
153 } 23 }
diff --git a/crates/ra_ide/src/inlay_hints.rs b/crates/ra_ide/src/inlay_hints.rs
index 6b0d3d996..2ae97e65f 100644
--- a/crates/ra_ide/src/inlay_hints.rs
+++ b/crates/ra_ide/src/inlay_hints.rs
@@ -80,8 +80,7 @@ fn get_inlay_hints(
80 }, 80 },
81 ast::MatchArmList(it) => { 81 ast::MatchArmList(it) => {
82 it.arms() 82 it.arms()
83 .map(|match_arm| match_arm.pats()) 83 .filter_map(|match_arm| match_arm.pat())
84 .flatten()
85 .for_each(|root_pat| get_pat_type_hints(acc, db, &analyzer, root_pat, true, max_inlay_hint_length)); 84 .for_each(|root_pat| get_pat_type_hints(acc, db, &analyzer, root_pat, true, max_inlay_hint_length));
86 }, 85 },
87 ast::CallExpr(it) => { 86 ast::CallExpr(it) => {
@@ -202,6 +201,7 @@ fn get_leaf_pats(root_pat: ast::Pat) -> Vec<ast::Pat> {
202 Some(pat) => pats_to_process.push_back(pat), 201 Some(pat) => pats_to_process.push_back(pat),
203 _ => leaf_pats.push(maybe_leaf_pat), 202 _ => leaf_pats.push(maybe_leaf_pat),
204 }, 203 },
204 ast::Pat::OrPat(ref_pat) => pats_to_process.extend(ref_pat.pats()),
205 ast::Pat::TuplePat(tuple_pat) => pats_to_process.extend(tuple_pat.args()), 205 ast::Pat::TuplePat(tuple_pat) => pats_to_process.extend(tuple_pat.args()),
206 ast::Pat::RecordPat(record_pat) => { 206 ast::Pat::RecordPat(record_pat) => {
207 if let Some(pat_list) = record_pat.record_field_pat_list() { 207 if let Some(pat_list) = record_pat.record_field_pat_list() {
@@ -222,6 +222,7 @@ fn get_leaf_pats(root_pat: ast::Pat) -> Vec<ast::Pat> {
222 ast::Pat::TupleStructPat(tuple_struct_pat) => { 222 ast::Pat::TupleStructPat(tuple_struct_pat) => {
223 pats_to_process.extend(tuple_struct_pat.args()) 223 pats_to_process.extend(tuple_struct_pat.args())
224 } 224 }
225 ast::Pat::ParenPat(inner_pat) => pats_to_process.extend(inner_pat.pat()),
225 ast::Pat::RefPat(ref_pat) => pats_to_process.extend(ref_pat.pat()), 226 ast::Pat::RefPat(ref_pat) => pats_to_process.extend(ref_pat.pat()),
226 _ => (), 227 _ => (),
227 } 228 }
diff --git a/crates/ra_ide/src/join_lines.rs b/crates/ra_ide/src/join_lines.rs
index 7deeb3494..01fb32b3d 100644
--- a/crates/ra_ide/src/join_lines.rs
+++ b/crates/ra_ide/src/join_lines.rs
@@ -60,36 +60,15 @@ fn remove_newline(edit: &mut TextEditBuilder, token: &SyntaxToken, offset: TextU
60 return; 60 return;
61 } 61 }
62 62
63 // Special case that turns something like:
64 //
65 // ```
66 // my_function({<|>
67 // <some-expr>
68 // })
69 // ```
70 //
71 // into `my_function(<some-expr>)`
72 if join_single_expr_block(edit, token).is_some() {
73 return;
74 }
75 // ditto for
76 //
77 // ```
78 // use foo::{<|>
79 // bar
80 // };
81 // ```
82 if join_single_use_tree(edit, token).is_some() {
83 return;
84 }
85
86 // The node is between two other nodes 63 // The node is between two other nodes
87 let prev = token.prev_sibling_or_token().unwrap(); 64 let prev = token.prev_sibling_or_token().unwrap();
88 let next = token.next_sibling_or_token().unwrap(); 65 let next = token.next_sibling_or_token().unwrap();
89 if is_trailing_comma(prev.kind(), next.kind()) { 66 if is_trailing_comma(prev.kind(), next.kind()) {
90 // Removes: trailing comma, newline (incl. surrounding whitespace) 67 // Removes: trailing comma, newline (incl. surrounding whitespace)
91 edit.delete(TextRange::from_to(prev.text_range().start(), token.text_range().end())); 68 edit.delete(TextRange::from_to(prev.text_range().start(), token.text_range().end()));
92 } else if prev.kind() == T![,] && next.kind() == T!['}'] { 69 return;
70 }
71 if prev.kind() == T![,] && next.kind() == T!['}'] {
93 // Removes: comma, newline (incl. surrounding whitespace) 72 // Removes: comma, newline (incl. surrounding whitespace)
94 let space = if let Some(left) = prev.prev_sibling_or_token() { 73 let space = if let Some(left) = prev.prev_sibling_or_token() {
95 compute_ws(left.kind(), next.kind()) 74 compute_ws(left.kind(), next.kind())
@@ -100,7 +79,10 @@ fn remove_newline(edit: &mut TextEditBuilder, token: &SyntaxToken, offset: TextU
100 TextRange::from_to(prev.text_range().start(), token.text_range().end()), 79 TextRange::from_to(prev.text_range().start(), token.text_range().end()),
101 space.to_string(), 80 space.to_string(),
102 ); 81 );
103 } else if let (Some(_), Some(next)) = ( 82 return;
83 }
84
85 if let (Some(_), Some(next)) = (
104 prev.as_token().cloned().and_then(ast::Comment::cast), 86 prev.as_token().cloned().and_then(ast::Comment::cast),
105 next.as_token().cloned().and_then(ast::Comment::cast), 87 next.as_token().cloned().and_then(ast::Comment::cast),
106 ) { 88 ) {
@@ -109,10 +91,34 @@ fn remove_newline(edit: &mut TextEditBuilder, token: &SyntaxToken, offset: TextU
109 token.text_range().start(), 91 token.text_range().start(),
110 next.syntax().text_range().start() + TextUnit::of_str(next.prefix()), 92 next.syntax().text_range().start() + TextUnit::of_str(next.prefix()),
111 )); 93 ));
112 } else { 94 return;
113 // Remove newline but add a computed amount of whitespace characters 95 }
114 edit.replace(token.text_range(), compute_ws(prev.kind(), next.kind()).to_string()); 96
97 // Special case that turns something like:
98 //
99 // ```
100 // my_function({<|>
101 // <some-expr>
102 // })
103 // ```
104 //
105 // into `my_function(<some-expr>)`
106 if join_single_expr_block(edit, token).is_some() {
107 return;
115 } 108 }
109 // ditto for
110 //
111 // ```
112 // use foo::{<|>
113 // bar
114 // };
115 // ```
116 if join_single_use_tree(edit, token).is_some() {
117 return;
118 }
119
120 // Remove newline but add a computed amount of whitespace characters
121 edit.replace(token.text_range(), compute_ws(prev.kind(), next.kind()).to_string());
116} 122}
117 123
118fn has_comma_after(node: &SyntaxNode) -> bool { 124fn has_comma_after(node: &SyntaxNode) -> bool {
@@ -608,4 +614,27 @@ pub fn handle_find_matching_brace() {
608}", 614}",
609 ); 615 );
610 } 616 }
617
618 #[test]
619 fn test_join_lines_commented_block() {
620 check_join_lines(
621 r"
622fn main() {
623 let _ = {
624 // <|>foo
625 // bar
626 92
627 };
628}
629 ",
630 r"
631fn main() {
632 let _ = {
633 // <|>foo bar
634 92
635 };
636}
637 ",
638 )
639 }
611} 640}
diff --git a/crates/ra_ide/src/references.rs b/crates/ra_ide/src/references.rs
index a6320bd2f..de924fad2 100644
--- a/crates/ra_ide/src/references.rs
+++ b/crates/ra_ide/src/references.rs
@@ -13,6 +13,7 @@ mod classify;
13mod rename; 13mod rename;
14mod search_scope; 14mod search_scope;
15 15
16use crate::expand::descend_into_macros_with_analyzer;
16use hir::{InFile, SourceBinder}; 17use hir::{InFile, SourceBinder};
17use once_cell::unsync::Lazy; 18use once_cell::unsync::Lazy;
18use ra_db::{SourceDatabase, SourceDatabaseExt}; 19use ra_db::{SourceDatabase, SourceDatabaseExt};
@@ -192,39 +193,62 @@ fn process_definition(
192 193
193 let parse = Lazy::new(|| SourceFile::parse(&text)); 194 let parse = Lazy::new(|| SourceFile::parse(&text));
194 let mut sb = Lazy::new(|| SourceBinder::new(db)); 195 let mut sb = Lazy::new(|| SourceBinder::new(db));
196 let mut analyzer = None;
195 197
196 for (idx, _) in text.match_indices(pat) { 198 for (idx, _) in text.match_indices(pat) {
197 let offset = TextUnit::from_usize(idx); 199 let offset = TextUnit::from_usize(idx);
198 200
199 if let Some(name_ref) = 201 let (name_ref, range) = if let Some(name_ref) =
200 find_node_at_offset::<ast::NameRef>(parse.tree().syntax(), offset) 202 find_node_at_offset::<ast::NameRef>(parse.tree().syntax(), offset)
201 { 203 {
202 let range = name_ref.syntax().text_range(); 204 let range = name_ref.syntax().text_range();
203 if let Some(search_range) = search_range { 205 (InFile::new(file_id.into(), name_ref), range)
204 if !range.is_subrange(&search_range) { 206 } else {
205 continue; 207 // Handle macro token cases
206 } 208 let t = match parse.tree().syntax().token_at_offset(offset) {
209 TokenAtOffset::None => continue,
210 TokenAtOffset::Single(t) => t,
211 TokenAtOffset::Between(_, t) => t,
212 };
213 let range = t.text_range();
214 let analyzer = analyzer.get_or_insert_with(|| {
215 sb.analyze(InFile::new(file_id.into(), parse.tree().syntax()), None)
216 });
217 let expanded = descend_into_macros_with_analyzer(
218 db,
219 &analyzer,
220 InFile::new(file_id.into(), t),
221 );
222 if let Some(token) = ast::NameRef::cast(expanded.value.parent()) {
223 (expanded.with_value(token), range)
224 } else {
225 continue;
207 } 226 }
208 // FIXME: reuse sb 227 };
209 // See https://github.com/rust-lang/rust/pull/68198#issuecomment-574269098 228
210 229 if let Some(search_range) = search_range {
211 if let Some(d) = classify_name_ref(&mut sb, InFile::new(file_id.into(), &name_ref)) 230 if !range.is_subrange(&search_range) {
212 { 231 continue;
213 if d == def { 232 }
214 let kind = if is_record_lit_name_ref(&name_ref) 233 }
215 || is_call_expr_name_ref(&name_ref) 234 // FIXME: reuse sb
216 { 235 // See https://github.com/rust-lang/rust/pull/68198#issuecomment-574269098
217 ReferenceKind::StructLiteral 236
218 } else { 237 if let Some(d) = classify_name_ref(&mut sb, name_ref.as_ref()) {
219 ReferenceKind::Other 238 if d == def {
220 }; 239 let kind = if is_record_lit_name_ref(&name_ref.value)
221 240 || is_call_expr_name_ref(&name_ref.value)
222 refs.push(Reference { 241 {
223 file_range: FileRange { file_id, range }, 242 ReferenceKind::StructLiteral
224 kind, 243 } else {
225 access: reference_access(&d.kind, &name_ref), 244 ReferenceKind::Other
226 }); 245 };
227 } 246
247 refs.push(Reference {
248 file_range: FileRange { file_id, range },
249 kind,
250 access: reference_access(&d.kind, &name_ref.value),
251 });
228 } 252 }
229 } 253 }
230 } 254 }
diff --git a/crates/ra_ide/src/references/rename.rs b/crates/ra_ide/src/references/rename.rs
index 08e77c01f..c46b78cb6 100644
--- a/crates/ra_ide/src/references/rename.rs
+++ b/crates/ra_ide/src/references/rename.rs
@@ -211,6 +211,25 @@ mod tests {
211 } 211 }
212 212
213 #[test] 213 #[test]
214 fn test_rename_for_macro_args() {
215 test_rename(
216 r#"
217 macro_rules! foo {($i:ident) => {$i} }
218 fn main() {
219 let a<|> = "test";
220 foo!(a);
221 }"#,
222 "b",
223 r#"
224 macro_rules! foo {($i:ident) => {$i} }
225 fn main() {
226 let b = "test";
227 foo!(b);
228 }"#,
229 );
230 }
231
232 #[test]
214 fn test_rename_for_param_inside() { 233 fn test_rename_for_param_inside() {
215 test_rename( 234 test_rename(
216 r#" 235 r#"