diff options
Diffstat (limited to 'crates')
55 files changed, 634 insertions, 425 deletions
diff --git a/crates/ra_assists/src/assist_ctx.rs b/crates/ra_assists/src/assist_ctx.rs index da2880037..83dd270c6 100644 --- a/crates/ra_assists/src/assist_ctx.rs +++ b/crates/ra_assists/src/assist_ctx.rs | |||
@@ -4,14 +4,13 @@ use ra_db::FileRange; | |||
4 | use ra_fmt::{leading_indent, reindent}; | 4 | use ra_fmt::{leading_indent, reindent}; |
5 | use ra_ide_db::RootDatabase; | 5 | use ra_ide_db::RootDatabase; |
6 | use ra_syntax::{ | 6 | use ra_syntax::{ |
7 | algo::{self, find_covering_element, find_node_at_offset}, | 7 | algo::{self, find_covering_element, find_node_at_offset, SyntaxRewriter}, |
8 | AstNode, SourceFile, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, TextRange, TextSize, | 8 | AstNode, SourceFile, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, TextRange, TextSize, |
9 | TokenAtOffset, | 9 | TokenAtOffset, |
10 | }; | 10 | }; |
11 | use ra_text_edit::TextEditBuilder; | 11 | use ra_text_edit::TextEditBuilder; |
12 | 12 | ||
13 | use crate::{AssistAction, AssistFile, AssistId, AssistLabel, GroupLabel, ResolvedAssist}; | 13 | use crate::{AssistAction, AssistFile, AssistId, AssistLabel, GroupLabel, ResolvedAssist}; |
14 | use algo::SyntaxRewriter; | ||
15 | 14 | ||
16 | #[derive(Clone, Debug)] | 15 | #[derive(Clone, Debug)] |
17 | pub(crate) struct Assist(pub(crate) Vec<AssistInfo>); | 16 | pub(crate) struct Assist(pub(crate) Vec<AssistInfo>); |
@@ -38,13 +37,10 @@ impl AssistInfo { | |||
38 | 37 | ||
39 | pub(crate) fn into_resolved(self) -> Option<ResolvedAssist> { | 38 | pub(crate) fn into_resolved(self) -> Option<ResolvedAssist> { |
40 | let label = self.label; | 39 | let label = self.label; |
41 | let group_label = self.group_label; | 40 | self.action.map(|action| ResolvedAssist { label, action }) |
42 | self.action.map(|action| ResolvedAssist { label, group_label, action }) | ||
43 | } | 41 | } |
44 | } | 42 | } |
45 | 43 | ||
46 | pub(crate) type AssistHandler = fn(AssistCtx) -> Option<Assist>; | ||
47 | |||
48 | /// `AssistCtx` allows to apply an assist or check if it could be applied. | 44 | /// `AssistCtx` allows to apply an assist or check if it could be applied. |
49 | /// | 45 | /// |
50 | /// Assists use a somewhat over-engineered approach, given the current needs. The | 46 | /// Assists use a somewhat over-engineered approach, given the current needs. The |
@@ -100,7 +96,7 @@ impl<'a> AssistCtx<'a> { | |||
100 | label: impl Into<String>, | 96 | label: impl Into<String>, |
101 | f: impl FnOnce(&mut ActionBuilder), | 97 | f: impl FnOnce(&mut ActionBuilder), |
102 | ) -> Option<Assist> { | 98 | ) -> Option<Assist> { |
103 | let label = AssistLabel::new(label.into(), id); | 99 | let label = AssistLabel::new(id, label.into(), None); |
104 | 100 | ||
105 | let mut info = AssistInfo::new(label); | 101 | let mut info = AssistInfo::new(label); |
106 | if self.should_compute_edit { | 102 | if self.should_compute_edit { |
@@ -116,7 +112,8 @@ impl<'a> AssistCtx<'a> { | |||
116 | } | 112 | } |
117 | 113 | ||
118 | pub(crate) fn add_assist_group(self, group_name: impl Into<String>) -> AssistGroup<'a> { | 114 | pub(crate) fn add_assist_group(self, group_name: impl Into<String>) -> AssistGroup<'a> { |
119 | AssistGroup { ctx: self, group_name: group_name.into(), assists: Vec::new() } | 115 | let group = GroupLabel(group_name.into()); |
116 | AssistGroup { ctx: self, group, assists: Vec::new() } | ||
120 | } | 117 | } |
121 | 118 | ||
122 | pub(crate) fn token_at_offset(&self) -> TokenAtOffset<SyntaxToken> { | 119 | pub(crate) fn token_at_offset(&self) -> TokenAtOffset<SyntaxToken> { |
@@ -146,7 +143,7 @@ impl<'a> AssistCtx<'a> { | |||
146 | 143 | ||
147 | pub(crate) struct AssistGroup<'a> { | 144 | pub(crate) struct AssistGroup<'a> { |
148 | ctx: AssistCtx<'a>, | 145 | ctx: AssistCtx<'a>, |
149 | group_name: String, | 146 | group: GroupLabel, |
150 | assists: Vec<AssistInfo>, | 147 | assists: Vec<AssistInfo>, |
151 | } | 148 | } |
152 | 149 | ||
@@ -157,9 +154,9 @@ impl<'a> AssistGroup<'a> { | |||
157 | label: impl Into<String>, | 154 | label: impl Into<String>, |
158 | f: impl FnOnce(&mut ActionBuilder), | 155 | f: impl FnOnce(&mut ActionBuilder), |
159 | ) { | 156 | ) { |
160 | let label = AssistLabel::new(label.into(), id); | 157 | let label = AssistLabel::new(id, label.into(), Some(self.group.clone())); |
161 | 158 | ||
162 | let mut info = AssistInfo::new(label).with_group(GroupLabel(self.group_name.clone())); | 159 | let mut info = AssistInfo::new(label).with_group(self.group.clone()); |
163 | if self.ctx.should_compute_edit { | 160 | if self.ctx.should_compute_edit { |
164 | let action = { | 161 | let action = { |
165 | let mut edit = ActionBuilder::new(&self.ctx); | 162 | let mut edit = ActionBuilder::new(&self.ctx); |
diff --git a/crates/ra_assists/src/doc_tests.rs b/crates/ra_assists/src/doc_tests.rs index c0f9bc1fb..f627f31dc 100644 --- a/crates/ra_assists/src/doc_tests.rs +++ b/crates/ra_assists/src/doc_tests.rs | |||
@@ -30,6 +30,10 @@ fn check(assist_id: &str, before: &str, after: &str) { | |||
30 | ) | 30 | ) |
31 | }); | 31 | }); |
32 | 32 | ||
33 | let actual = assist.action.edit.apply(&before); | 33 | let actual = { |
34 | let mut actual = before.clone(); | ||
35 | assist.action.edit.apply(&mut actual); | ||
36 | actual | ||
37 | }; | ||
34 | assert_eq_text!(after, &actual); | 38 | assert_eq_text!(after, &actual); |
35 | } | 39 | } |
diff --git a/crates/ra_assists/src/handlers/add_missing_impl_members.rs b/crates/ra_assists/src/handlers/add_missing_impl_members.rs index e466c9a86..e47feda71 100644 --- a/crates/ra_assists/src/handlers/add_missing_impl_members.rs +++ b/crates/ra_assists/src/handlers/add_missing_impl_members.rs | |||
@@ -10,7 +10,7 @@ use ra_syntax::{ | |||
10 | 10 | ||
11 | use crate::{ | 11 | use crate::{ |
12 | ast_transform::{self, AstTransform, QualifyPaths, SubstituteTypeParams}, | 12 | ast_transform::{self, AstTransform, QualifyPaths, SubstituteTypeParams}, |
13 | utils::{get_missing_impl_items, resolve_target_trait}, | 13 | utils::{get_missing_assoc_items, resolve_target_trait}, |
14 | Assist, AssistCtx, AssistId, | 14 | Assist, AssistCtx, AssistId, |
15 | }; | 15 | }; |
16 | 16 | ||
@@ -112,25 +112,25 @@ fn add_missing_impl_members_inner( | |||
112 | 112 | ||
113 | let trait_ = resolve_target_trait(&ctx.sema, &impl_node)?; | 113 | let trait_ = resolve_target_trait(&ctx.sema, &impl_node)?; |
114 | 114 | ||
115 | let def_name = |item: &ast::ImplItem| -> Option<SmolStr> { | 115 | let def_name = |item: &ast::AssocItem| -> Option<SmolStr> { |
116 | match item { | 116 | match item { |
117 | ast::ImplItem::FnDef(def) => def.name(), | 117 | ast::AssocItem::FnDef(def) => def.name(), |
118 | ast::ImplItem::TypeAliasDef(def) => def.name(), | 118 | ast::AssocItem::TypeAliasDef(def) => def.name(), |
119 | ast::ImplItem::ConstDef(def) => def.name(), | 119 | ast::AssocItem::ConstDef(def) => def.name(), |
120 | } | 120 | } |
121 | .map(|it| it.text().clone()) | 121 | .map(|it| it.text().clone()) |
122 | }; | 122 | }; |
123 | 123 | ||
124 | let missing_items = get_missing_impl_items(&ctx.sema, &impl_node) | 124 | let missing_items = get_missing_assoc_items(&ctx.sema, &impl_node) |
125 | .iter() | 125 | .iter() |
126 | .map(|i| match i { | 126 | .map(|i| match i { |
127 | hir::AssocItem::Function(i) => ast::ImplItem::FnDef(i.source(ctx.db).value), | 127 | hir::AssocItem::Function(i) => ast::AssocItem::FnDef(i.source(ctx.db).value), |
128 | hir::AssocItem::TypeAlias(i) => ast::ImplItem::TypeAliasDef(i.source(ctx.db).value), | 128 | hir::AssocItem::TypeAlias(i) => ast::AssocItem::TypeAliasDef(i.source(ctx.db).value), |
129 | hir::AssocItem::Const(i) => ast::ImplItem::ConstDef(i.source(ctx.db).value), | 129 | hir::AssocItem::Const(i) => ast::AssocItem::ConstDef(i.source(ctx.db).value), |
130 | }) | 130 | }) |
131 | .filter(|t| def_name(&t).is_some()) | 131 | .filter(|t| def_name(&t).is_some()) |
132 | .filter(|t| match t { | 132 | .filter(|t| match t { |
133 | ast::ImplItem::FnDef(def) => match mode { | 133 | ast::AssocItem::FnDef(def) => match mode { |
134 | AddMissingImplMembersMode::DefaultMethodsOnly => def.body().is_some(), | 134 | AddMissingImplMembersMode::DefaultMethodsOnly => def.body().is_some(), |
135 | AddMissingImplMembersMode::NoDefaultMethods => def.body().is_none(), | 135 | AddMissingImplMembersMode::NoDefaultMethods => def.body().is_none(), |
136 | }, | 136 | }, |
@@ -145,7 +145,7 @@ fn add_missing_impl_members_inner( | |||
145 | let sema = ctx.sema; | 145 | let sema = ctx.sema; |
146 | 146 | ||
147 | ctx.add_assist(AssistId(assist_id), label, |edit| { | 147 | ctx.add_assist(AssistId(assist_id), label, |edit| { |
148 | let n_existing_items = impl_item_list.impl_items().count(); | 148 | let n_existing_items = impl_item_list.assoc_items().count(); |
149 | let source_scope = sema.scope_for_def(trait_); | 149 | let source_scope = sema.scope_for_def(trait_); |
150 | let target_scope = sema.scope(impl_item_list.syntax()); | 150 | let target_scope = sema.scope(impl_item_list.syntax()); |
151 | let ast_transform = QualifyPaths::new(&target_scope, &source_scope) | 151 | let ast_transform = QualifyPaths::new(&target_scope, &source_scope) |
@@ -154,13 +154,13 @@ fn add_missing_impl_members_inner( | |||
154 | .into_iter() | 154 | .into_iter() |
155 | .map(|it| ast_transform::apply(&*ast_transform, it)) | 155 | .map(|it| ast_transform::apply(&*ast_transform, it)) |
156 | .map(|it| match it { | 156 | .map(|it| match it { |
157 | ast::ImplItem::FnDef(def) => ast::ImplItem::FnDef(add_body(def)), | 157 | ast::AssocItem::FnDef(def) => ast::AssocItem::FnDef(add_body(def)), |
158 | _ => it, | 158 | _ => it, |
159 | }) | 159 | }) |
160 | .map(|it| edit::remove_attrs_and_docs(&it)); | 160 | .map(|it| edit::remove_attrs_and_docs(&it)); |
161 | let new_impl_item_list = impl_item_list.append_items(items); | 161 | let new_impl_item_list = impl_item_list.append_items(items); |
162 | let cursor_position = { | 162 | let cursor_position = { |
163 | let first_new_item = new_impl_item_list.impl_items().nth(n_existing_items).unwrap(); | 163 | let first_new_item = new_impl_item_list.assoc_items().nth(n_existing_items).unwrap(); |
164 | first_new_item.syntax().text_range().start() | 164 | first_new_item.syntax().text_range().start() |
165 | }; | 165 | }; |
166 | 166 | ||
diff --git a/crates/ra_assists/src/handlers/add_new.rs b/crates/ra_assists/src/handlers/add_new.rs index 0f9174a29..e8a36c7de 100644 --- a/crates/ra_assists/src/handlers/add_new.rs +++ b/crates/ra_assists/src/handlers/add_new.rs | |||
@@ -162,8 +162,8 @@ fn find_struct_impl(ctx: &AssistCtx, strukt: &ast::StructDef) -> Option<Option<a | |||
162 | 162 | ||
163 | fn has_new_fn(imp: &ast::ImplDef) -> bool { | 163 | fn has_new_fn(imp: &ast::ImplDef) -> bool { |
164 | if let Some(il) = imp.item_list() { | 164 | if let Some(il) = imp.item_list() { |
165 | for item in il.impl_items() { | 165 | for item in il.assoc_items() { |
166 | if let ast::ImplItem::FnDef(f) = item { | 166 | if let ast::AssocItem::FnDef(f) = item { |
167 | if let Some(name) = f.name() { | 167 | if let Some(name) = f.name() { |
168 | if name.text().eq_ignore_ascii_case("new") { | 168 | if name.text().eq_ignore_ascii_case("new") { |
169 | return true; | 169 | return true; |
diff --git a/crates/ra_assists/src/handlers/change_visibility.rs b/crates/ra_assists/src/handlers/change_visibility.rs index 44f6a1dae..1cd532e80 100644 --- a/crates/ra_assists/src/handlers/change_visibility.rs +++ b/crates/ra_assists/src/handlers/change_visibility.rs | |||
@@ -47,8 +47,7 @@ fn add_vis(ctx: AssistCtx) -> Option<Assist> { | |||
47 | return None; | 47 | return None; |
48 | } | 48 | } |
49 | (vis_offset(&parent), keyword.text_range()) | 49 | (vis_offset(&parent), keyword.text_range()) |
50 | } else { | 50 | } else if let Some(field_name) = ctx.find_node_at_offset::<ast::Name>() { |
51 | let field_name: ast::Name = ctx.find_node_at_offset()?; | ||
52 | let field = field_name.syntax().ancestors().find_map(ast::RecordFieldDef::cast)?; | 51 | let field = field_name.syntax().ancestors().find_map(ast::RecordFieldDef::cast)?; |
53 | if field.name()? != field_name { | 52 | if field.name()? != field_name { |
54 | tested_by!(change_visibility_field_false_positive); | 53 | tested_by!(change_visibility_field_false_positive); |
@@ -58,6 +57,13 @@ fn add_vis(ctx: AssistCtx) -> Option<Assist> { | |||
58 | return None; | 57 | return None; |
59 | } | 58 | } |
60 | (vis_offset(field.syntax()), field_name.syntax().text_range()) | 59 | (vis_offset(field.syntax()), field_name.syntax().text_range()) |
60 | } else if let Some(field) = ctx.find_node_at_offset::<ast::TupleFieldDef>() { | ||
61 | if field.visibility().is_some() { | ||
62 | return None; | ||
63 | } | ||
64 | (vis_offset(field.syntax()), field.syntax().text_range()) | ||
65 | } else { | ||
66 | return None; | ||
61 | }; | 67 | }; |
62 | 68 | ||
63 | ctx.add_assist(AssistId("change_visibility"), "Change visibility to pub(crate)", |edit| { | 69 | ctx.add_assist(AssistId("change_visibility"), "Change visibility to pub(crate)", |edit| { |
@@ -129,7 +135,8 @@ mod tests { | |||
129 | change_visibility, | 135 | change_visibility, |
130 | r"struct S { <|>field: u32 }", | 136 | r"struct S { <|>field: u32 }", |
131 | r"struct S { <|>pub(crate) field: u32 }", | 137 | r"struct S { <|>pub(crate) field: u32 }", |
132 | ) | 138 | ); |
139 | check_assist(change_visibility, r"struct S ( <|>u32 )", r"struct S ( <|>pub(crate) u32 )"); | ||
133 | } | 140 | } |
134 | 141 | ||
135 | #[test] | 142 | #[test] |
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs index c5df86600..0f94f5ee8 100644 --- a/crates/ra_assists/src/lib.rs +++ b/crates/ra_assists/src/lib.rs | |||
@@ -17,13 +17,13 @@ mod doc_tests; | |||
17 | pub mod utils; | 17 | pub mod utils; |
18 | pub mod ast_transform; | 18 | pub mod ast_transform; |
19 | 19 | ||
20 | use hir::Semantics; | ||
20 | use ra_db::{FileId, FileRange}; | 21 | use ra_db::{FileId, FileRange}; |
21 | use ra_ide_db::RootDatabase; | 22 | use ra_ide_db::RootDatabase; |
22 | use ra_syntax::{TextRange, TextSize}; | 23 | use ra_syntax::{TextRange, TextSize}; |
23 | use ra_text_edit::TextEdit; | 24 | use ra_text_edit::TextEdit; |
24 | 25 | ||
25 | pub(crate) use crate::assist_ctx::{Assist, AssistCtx, AssistHandler}; | 26 | pub(crate) use crate::assist_ctx::{Assist, AssistCtx}; |
26 | use hir::Semantics; | ||
27 | 27 | ||
28 | /// Unique identifier of the assist, should not be shown to the user | 28 | /// Unique identifier of the assist, should not be shown to the user |
29 | /// directly. | 29 | /// directly. |
@@ -32,19 +32,20 @@ pub struct AssistId(pub &'static str); | |||
32 | 32 | ||
33 | #[derive(Debug, Clone)] | 33 | #[derive(Debug, Clone)] |
34 | pub struct AssistLabel { | 34 | pub struct AssistLabel { |
35 | pub id: AssistId, | ||
35 | /// Short description of the assist, as shown in the UI. | 36 | /// Short description of the assist, as shown in the UI. |
36 | pub label: String, | 37 | pub label: String, |
37 | pub id: AssistId, | 38 | pub group: Option<GroupLabel>, |
38 | } | 39 | } |
39 | 40 | ||
40 | #[derive(Clone, Debug)] | 41 | #[derive(Clone, Debug)] |
41 | pub struct GroupLabel(pub String); | 42 | pub struct GroupLabel(pub String); |
42 | 43 | ||
43 | impl AssistLabel { | 44 | impl AssistLabel { |
44 | pub(crate) fn new(label: String, id: AssistId) -> AssistLabel { | 45 | pub(crate) fn new(id: AssistId, label: String, group: Option<GroupLabel>) -> AssistLabel { |
45 | // FIXME: make fields private, so that this invariant can't be broken | 46 | // FIXME: make fields private, so that this invariant can't be broken |
46 | assert!(label.starts_with(|c: char| c.is_uppercase())); | 47 | assert!(label.starts_with(|c: char| c.is_uppercase())); |
47 | AssistLabel { label, id } | 48 | AssistLabel { id, label, group } |
48 | } | 49 | } |
49 | } | 50 | } |
50 | 51 | ||
@@ -60,7 +61,6 @@ pub struct AssistAction { | |||
60 | #[derive(Debug, Clone)] | 61 | #[derive(Debug, Clone)] |
61 | pub struct ResolvedAssist { | 62 | pub struct ResolvedAssist { |
62 | pub label: AssistLabel, | 63 | pub label: AssistLabel, |
63 | pub group_label: Option<GroupLabel>, | ||
64 | pub action: AssistAction, | 64 | pub action: AssistAction, |
65 | } | 65 | } |
66 | 66 | ||
@@ -109,7 +109,9 @@ pub fn resolved_assists(db: &RootDatabase, range: FileRange) -> Vec<ResolvedAssi | |||
109 | } | 109 | } |
110 | 110 | ||
111 | mod handlers { | 111 | mod handlers { |
112 | use crate::AssistHandler; | 112 | use crate::{Assist, AssistCtx}; |
113 | |||
114 | pub(crate) type Handler = fn(AssistCtx) -> Option<Assist>; | ||
113 | 115 | ||
114 | mod add_custom_impl; | 116 | mod add_custom_impl; |
115 | mod add_derive; | 117 | mod add_derive; |
@@ -145,12 +147,13 @@ mod handlers { | |||
145 | mod reorder_fields; | 147 | mod reorder_fields; |
146 | mod unwrap_block; | 148 | mod unwrap_block; |
147 | 149 | ||
148 | pub(crate) fn all() -> &'static [AssistHandler] { | 150 | pub(crate) fn all() -> &'static [Handler] { |
149 | &[ | 151 | &[ |
150 | // These are alphabetic for the foolish consistency | 152 | // These are alphabetic for the foolish consistency |
151 | add_custom_impl::add_custom_impl, | 153 | add_custom_impl::add_custom_impl, |
152 | add_derive::add_derive, | 154 | add_derive::add_derive, |
153 | add_explicit_type::add_explicit_type, | 155 | add_explicit_type::add_explicit_type, |
156 | add_from_impl_for_enum::add_from_impl_for_enum, | ||
154 | add_function::add_function, | 157 | add_function::add_function, |
155 | add_impl::add_impl, | 158 | add_impl::add_impl, |
156 | add_new::add_new, | 159 | add_new::add_new, |
@@ -176,17 +179,18 @@ mod handlers { | |||
176 | raw_string::remove_hash, | 179 | raw_string::remove_hash, |
177 | remove_dbg::remove_dbg, | 180 | remove_dbg::remove_dbg, |
178 | remove_mut::remove_mut, | 181 | remove_mut::remove_mut, |
182 | reorder_fields::reorder_fields, | ||
179 | replace_if_let_with_match::replace_if_let_with_match, | 183 | replace_if_let_with_match::replace_if_let_with_match, |
180 | replace_let_with_if_let::replace_let_with_if_let, | 184 | replace_let_with_if_let::replace_let_with_if_let, |
181 | replace_qualified_name_with_use::replace_qualified_name_with_use, | 185 | replace_qualified_name_with_use::replace_qualified_name_with_use, |
182 | replace_unwrap_with_match::replace_unwrap_with_match, | 186 | replace_unwrap_with_match::replace_unwrap_with_match, |
183 | split_import::split_import, | 187 | split_import::split_import, |
184 | add_from_impl_for_enum::add_from_impl_for_enum, | ||
185 | unwrap_block::unwrap_block, | 188 | unwrap_block::unwrap_block, |
186 | // These are manually sorted for better priorities | 189 | // These are manually sorted for better priorities |
187 | add_missing_impl_members::add_missing_impl_members, | 190 | add_missing_impl_members::add_missing_impl_members, |
188 | add_missing_impl_members::add_missing_default_members, | 191 | add_missing_impl_members::add_missing_default_members, |
189 | reorder_fields::reorder_fields, | 192 | // Are you sure you want to add new assist here, and not to the |
193 | // sorted list above? | ||
190 | ] | 194 | ] |
191 | } | 195 | } |
192 | } | 196 | } |
@@ -195,12 +199,12 @@ mod handlers { | |||
195 | mod helpers { | 199 | mod helpers { |
196 | use std::sync::Arc; | 200 | use std::sync::Arc; |
197 | 201 | ||
202 | use hir::Semantics; | ||
198 | use ra_db::{fixture::WithFixture, FileId, FileRange, SourceDatabaseExt}; | 203 | use ra_db::{fixture::WithFixture, FileId, FileRange, SourceDatabaseExt}; |
199 | use ra_ide_db::{symbol_index::SymbolsDatabase, RootDatabase}; | 204 | use ra_ide_db::{symbol_index::SymbolsDatabase, RootDatabase}; |
200 | use test_utils::{add_cursor, assert_eq_text, extract_range_or_offset, RangeOrOffset}; | 205 | use test_utils::{add_cursor, assert_eq_text, extract_range_or_offset, RangeOrOffset}; |
201 | 206 | ||
202 | use crate::{AssistCtx, AssistFile, AssistHandler}; | 207 | use crate::{handlers::Handler, AssistCtx, AssistFile}; |
203 | use hir::Semantics; | ||
204 | 208 | ||
205 | pub(crate) fn with_single_file(text: &str) -> (RootDatabase, FileId) { | 209 | pub(crate) fn with_single_file(text: &str) -> (RootDatabase, FileId) { |
206 | let (mut db, file_id) = RootDatabase::with_single_file(text); | 210 | let (mut db, file_id) = RootDatabase::with_single_file(text); |
@@ -210,22 +214,18 @@ mod helpers { | |||
210 | (db, file_id) | 214 | (db, file_id) |
211 | } | 215 | } |
212 | 216 | ||
213 | pub(crate) fn check_assist( | 217 | pub(crate) fn check_assist(assist: Handler, ra_fixture_before: &str, ra_fixture_after: &str) { |
214 | assist: AssistHandler, | ||
215 | ra_fixture_before: &str, | ||
216 | ra_fixture_after: &str, | ||
217 | ) { | ||
218 | check(assist, ra_fixture_before, ExpectedResult::After(ra_fixture_after)); | 218 | check(assist, ra_fixture_before, ExpectedResult::After(ra_fixture_after)); |
219 | } | 219 | } |
220 | 220 | ||
221 | // FIXME: instead of having a separate function here, maybe use | 221 | // FIXME: instead of having a separate function here, maybe use |
222 | // `extract_ranges` and mark the target as `<target> </target>` in the | 222 | // `extract_ranges` and mark the target as `<target> </target>` in the |
223 | // fixuture? | 223 | // fixuture? |
224 | pub(crate) fn check_assist_target(assist: AssistHandler, ra_fixture: &str, target: &str) { | 224 | pub(crate) fn check_assist_target(assist: Handler, ra_fixture: &str, target: &str) { |
225 | check(assist, ra_fixture, ExpectedResult::Target(target)); | 225 | check(assist, ra_fixture, ExpectedResult::Target(target)); |
226 | } | 226 | } |
227 | 227 | ||
228 | pub(crate) fn check_assist_not_applicable(assist: AssistHandler, ra_fixture: &str) { | 228 | pub(crate) fn check_assist_not_applicable(assist: Handler, ra_fixture: &str) { |
229 | check(assist, ra_fixture, ExpectedResult::NotApplicable); | 229 | check(assist, ra_fixture, ExpectedResult::NotApplicable); |
230 | } | 230 | } |
231 | 231 | ||
@@ -235,7 +235,7 @@ mod helpers { | |||
235 | Target(&'a str), | 235 | Target(&'a str), |
236 | } | 236 | } |
237 | 237 | ||
238 | fn check(assist: AssistHandler, before: &str, expected: ExpectedResult) { | 238 | fn check(assist: Handler, before: &str, expected: ExpectedResult) { |
239 | let (text_without_caret, file_with_caret_id, range_or_offset, db) = | 239 | let (text_without_caret, file_with_caret_id, range_or_offset, db) = |
240 | if before.contains("//-") { | 240 | if before.contains("//-") { |
241 | let (mut db, position) = RootDatabase::with_position(before); | 241 | let (mut db, position) = RootDatabase::with_position(before); |
@@ -261,13 +261,13 @@ mod helpers { | |||
261 | (Some(assist), ExpectedResult::After(after)) => { | 261 | (Some(assist), ExpectedResult::After(after)) => { |
262 | let action = assist.0[0].action.clone().unwrap(); | 262 | let action = assist.0[0].action.clone().unwrap(); |
263 | 263 | ||
264 | let assisted_file_text = if let AssistFile::TargetFile(file_id) = action.file { | 264 | let mut actual = if let AssistFile::TargetFile(file_id) = action.file { |
265 | db.file_text(file_id).as_ref().to_owned() | 265 | db.file_text(file_id).as_ref().to_owned() |
266 | } else { | 266 | } else { |
267 | text_without_caret | 267 | text_without_caret |
268 | }; | 268 | }; |
269 | action.edit.apply(&mut actual); | ||
269 | 270 | ||
270 | let mut actual = action.edit.apply(&assisted_file_text); | ||
271 | match action.cursor_position { | 271 | match action.cursor_position { |
272 | None => { | 272 | None => { |
273 | if let RangeOrOffset::Offset(before_cursor_pos) = range_or_offset { | 273 | if let RangeOrOffset::Offset(before_cursor_pos) = range_or_offset { |
diff --git a/crates/ra_assists/src/utils.rs b/crates/ra_assists/src/utils.rs index 6be704ce3..2f15a3f15 100644 --- a/crates/ra_assists/src/utils.rs +++ b/crates/ra_assists/src/utils.rs | |||
@@ -13,7 +13,7 @@ use rustc_hash::FxHashSet; | |||
13 | 13 | ||
14 | pub(crate) use insert_use::insert_use_statement; | 14 | pub(crate) use insert_use::insert_use_statement; |
15 | 15 | ||
16 | pub fn get_missing_impl_items( | 16 | pub fn get_missing_assoc_items( |
17 | sema: &Semantics<RootDatabase>, | 17 | sema: &Semantics<RootDatabase>, |
18 | impl_def: &ast::ImplDef, | 18 | impl_def: &ast::ImplDef, |
19 | ) -> Vec<hir::AssocItem> { | 19 | ) -> Vec<hir::AssocItem> { |
@@ -23,21 +23,21 @@ pub fn get_missing_impl_items( | |||
23 | let mut impl_type = FxHashSet::default(); | 23 | let mut impl_type = FxHashSet::default(); |
24 | 24 | ||
25 | if let Some(item_list) = impl_def.item_list() { | 25 | if let Some(item_list) = impl_def.item_list() { |
26 | for item in item_list.impl_items() { | 26 | for item in item_list.assoc_items() { |
27 | match item { | 27 | match item { |
28 | ast::ImplItem::FnDef(f) => { | 28 | ast::AssocItem::FnDef(f) => { |
29 | if let Some(n) = f.name() { | 29 | if let Some(n) = f.name() { |
30 | impl_fns_consts.insert(n.syntax().to_string()); | 30 | impl_fns_consts.insert(n.syntax().to_string()); |
31 | } | 31 | } |
32 | } | 32 | } |
33 | 33 | ||
34 | ast::ImplItem::TypeAliasDef(t) => { | 34 | ast::AssocItem::TypeAliasDef(t) => { |
35 | if let Some(n) = t.name() { | 35 | if let Some(n) = t.name() { |
36 | impl_type.insert(n.syntax().to_string()); | 36 | impl_type.insert(n.syntax().to_string()); |
37 | } | 37 | } |
38 | } | 38 | } |
39 | 39 | ||
40 | ast::ImplItem::ConstDef(c) => { | 40 | ast::AssocItem::ConstDef(c) => { |
41 | if let Some(n) = c.name() { | 41 | if let Some(n) = c.name() { |
42 | impl_fns_consts.insert(n.syntax().to_string()); | 42 | impl_fns_consts.insert(n.syntax().to_string()); |
43 | } | 43 | } |
diff --git a/crates/ra_flycheck/src/lib.rs b/crates/ra_flycheck/src/lib.rs index b54a30ab8..f27252949 100644 --- a/crates/ra_flycheck/src/lib.rs +++ b/crates/ra_flycheck/src/lib.rs | |||
@@ -24,7 +24,7 @@ pub use crate::conv::url_from_path_with_drive_lowercasing; | |||
24 | 24 | ||
25 | #[derive(Clone, Debug, PartialEq, Eq)] | 25 | #[derive(Clone, Debug, PartialEq, Eq)] |
26 | pub enum FlycheckConfig { | 26 | pub enum FlycheckConfig { |
27 | CargoCommand { command: String, all_targets: bool, extra_args: Vec<String> }, | 27 | CargoCommand { command: String, all_targets: bool, all_features: bool, extra_args: Vec<String> }, |
28 | CustomCommand { command: String, args: Vec<String> }, | 28 | CustomCommand { command: String, args: Vec<String> }, |
29 | } | 29 | } |
30 | 30 | ||
@@ -215,7 +215,7 @@ impl FlycheckThread { | |||
215 | self.check_process = None; | 215 | self.check_process = None; |
216 | 216 | ||
217 | let mut cmd = match &self.config { | 217 | let mut cmd = match &self.config { |
218 | FlycheckConfig::CargoCommand { command, all_targets, extra_args } => { | 218 | FlycheckConfig::CargoCommand { command, all_targets, all_features, extra_args } => { |
219 | let mut cmd = Command::new(cargo_binary()); | 219 | let mut cmd = Command::new(cargo_binary()); |
220 | cmd.arg(command); | 220 | cmd.arg(command); |
221 | cmd.args(&["--workspace", "--message-format=json", "--manifest-path"]); | 221 | cmd.args(&["--workspace", "--message-format=json", "--manifest-path"]); |
@@ -223,6 +223,9 @@ impl FlycheckThread { | |||
223 | if *all_targets { | 223 | if *all_targets { |
224 | cmd.arg("--all-targets"); | 224 | cmd.arg("--all-targets"); |
225 | } | 225 | } |
226 | if *all_features { | ||
227 | cmd.arg("--all-features"); | ||
228 | } | ||
226 | cmd.args(extra_args); | 229 | cmd.args(extra_args); |
227 | cmd | 230 | cmd |
228 | } | 231 | } |
diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs index a004363ee..5f480c304 100644 --- a/crates/ra_hir/src/code_model.rs +++ b/crates/ra_hir/src/code_model.rs | |||
@@ -1211,7 +1211,7 @@ impl Type { | |||
1211 | 1211 | ||
1212 | // This would be nicer if it just returned an iterator, but that runs into | 1212 | // This would be nicer if it just returned an iterator, but that runs into |
1213 | // lifetime problems, because we need to borrow temp `CrateImplDefs`. | 1213 | // lifetime problems, because we need to borrow temp `CrateImplDefs`. |
1214 | pub fn iterate_impl_items<T>( | 1214 | pub fn iterate_assoc_items<T>( |
1215 | self, | 1215 | self, |
1216 | db: &dyn HirDatabase, | 1216 | db: &dyn HirDatabase, |
1217 | krate: Crate, | 1217 | krate: Crate, |
diff --git a/crates/ra_hir_def/src/adt.rs b/crates/ra_hir_def/src/adt.rs index d0912ddaa..2bc34d449 100644 --- a/crates/ra_hir_def/src/adt.rs +++ b/crates/ra_hir_def/src/adt.rs | |||
@@ -118,11 +118,12 @@ fn lower_enum( | |||
118 | module_id: ModuleId, | 118 | module_id: ModuleId, |
119 | ) { | 119 | ) { |
120 | let expander = CfgExpander::new(db, ast.file_id, module_id.krate); | 120 | let expander = CfgExpander::new(db, ast.file_id, module_id.krate); |
121 | let variants = | 121 | let variants = ast |
122 | ast.value.variant_list().into_iter().flat_map(|it| it.variants()).filter(|var| { | 122 | .value |
123 | let attrs = expander.parse_attrs(var); | 123 | .variant_list() |
124 | expander.is_cfg_enabled(&attrs) | 124 | .into_iter() |
125 | }); | 125 | .flat_map(|it| it.variants()) |
126 | .filter(|var| expander.is_cfg_enabled(var)); | ||
126 | for var in variants { | 127 | for var in variants { |
127 | trace.alloc( | 128 | trace.alloc( |
128 | || var.clone(), | 129 | || var.clone(), |
@@ -215,8 +216,7 @@ fn lower_struct( | |||
215 | match &ast.value { | 216 | match &ast.value { |
216 | ast::StructKind::Tuple(fl) => { | 217 | ast::StructKind::Tuple(fl) => { |
217 | for (i, fd) in fl.fields().enumerate() { | 218 | for (i, fd) in fl.fields().enumerate() { |
218 | let attrs = expander.parse_attrs(&fd); | 219 | if !expander.is_cfg_enabled(&fd) { |
219 | if !expander.is_cfg_enabled(&attrs) { | ||
220 | continue; | 220 | continue; |
221 | } | 221 | } |
222 | 222 | ||
@@ -233,8 +233,7 @@ fn lower_struct( | |||
233 | } | 233 | } |
234 | ast::StructKind::Record(fl) => { | 234 | ast::StructKind::Record(fl) => { |
235 | for fd in fl.fields() { | 235 | for fd in fl.fields() { |
236 | let attrs = expander.parse_attrs(&fd); | 236 | if !expander.is_cfg_enabled(&fd) { |
237 | if !expander.is_cfg_enabled(&attrs) { | ||
238 | continue; | 237 | continue; |
239 | } | 238 | } |
240 | 239 | ||
diff --git a/crates/ra_hir_def/src/body.rs b/crates/ra_hir_def/src/body.rs index 4edaad960..f5a7305dc 100644 --- a/crates/ra_hir_def/src/body.rs +++ b/crates/ra_hir_def/src/body.rs | |||
@@ -60,7 +60,8 @@ impl CfgExpander { | |||
60 | Attrs::new(owner, &self.hygiene) | 60 | Attrs::new(owner, &self.hygiene) |
61 | } | 61 | } |
62 | 62 | ||
63 | pub(crate) fn is_cfg_enabled(&self, attrs: &Attrs) -> bool { | 63 | pub(crate) fn is_cfg_enabled(&self, owner: &dyn ast::AttrsOwner) -> bool { |
64 | let attrs = self.parse_attrs(owner); | ||
64 | attrs.is_cfg_enabled(&self.cfg_options) | 65 | attrs.is_cfg_enabled(&self.cfg_options) |
65 | } | 66 | } |
66 | } | 67 | } |
@@ -141,12 +142,8 @@ impl Expander { | |||
141 | InFile { file_id: self.current_file_id, value } | 142 | InFile { file_id: self.current_file_id, value } |
142 | } | 143 | } |
143 | 144 | ||
144 | pub(crate) fn parse_attrs(&self, owner: &dyn ast::AttrsOwner) -> Attrs { | 145 | pub(crate) fn is_cfg_enabled(&self, owner: &dyn ast::AttrsOwner) -> bool { |
145 | self.cfg_expander.parse_attrs(owner) | 146 | self.cfg_expander.is_cfg_enabled(owner) |
146 | } | ||
147 | |||
148 | pub(crate) fn is_cfg_enabled(&self, attrs: &Attrs) -> bool { | ||
149 | self.cfg_expander.is_cfg_enabled(attrs) | ||
150 | } | 147 | } |
151 | 148 | ||
152 | fn parse_path(&mut self, path: ast::Path) -> Option<Path> { | 149 | fn parse_path(&mut self, path: ast::Path) -> Option<Path> { |
diff --git a/crates/ra_hir_def/src/body/lower.rs b/crates/ra_hir_def/src/body/lower.rs index 687216dc3..443b057ab 100644 --- a/crates/ra_hir_def/src/body/lower.rs +++ b/crates/ra_hir_def/src/body/lower.rs | |||
@@ -162,8 +162,7 @@ impl ExprCollector<'_> { | |||
162 | 162 | ||
163 | fn collect_expr(&mut self, expr: ast::Expr) -> ExprId { | 163 | fn collect_expr(&mut self, expr: ast::Expr) -> ExprId { |
164 | let syntax_ptr = AstPtr::new(&expr); | 164 | let syntax_ptr = AstPtr::new(&expr); |
165 | let attrs = self.expander.parse_attrs(&expr); | 165 | if !self.expander.is_cfg_enabled(&expr) { |
166 | if !self.expander.is_cfg_enabled(&attrs) { | ||
167 | return self.missing_expr(); | 166 | return self.missing_expr(); |
168 | } | 167 | } |
169 | match expr { | 168 | match expr { |
@@ -329,8 +328,7 @@ impl ExprCollector<'_> { | |||
329 | .fields() | 328 | .fields() |
330 | .inspect(|field| field_ptrs.push(AstPtr::new(field))) | 329 | .inspect(|field| field_ptrs.push(AstPtr::new(field))) |
331 | .filter_map(|field| { | 330 | .filter_map(|field| { |
332 | let attrs = self.expander.parse_attrs(&field); | 331 | if !self.expander.is_cfg_enabled(&field) { |
333 | if !self.expander.is_cfg_enabled(&attrs) { | ||
334 | return None; | 332 | return None; |
335 | } | 333 | } |
336 | let name = field.field_name()?.as_name(); | 334 | let name = field.field_name()?.as_name(); |
@@ -575,9 +573,16 @@ impl ExprCollector<'_> { | |||
575 | self.body.item_scope.define_def(def); | 573 | self.body.item_scope.define_def(def); |
576 | if let Some(name) = name { | 574 | if let Some(name) = name { |
577 | let vis = crate::visibility::Visibility::Public; // FIXME determine correctly | 575 | let vis = crate::visibility::Visibility::Public; // FIXME determine correctly |
578 | self.body | 576 | let has_constructor = match def { |
579 | .item_scope | 577 | ModuleDefId::AdtId(AdtId::StructId(s)) => { |
580 | .push_res(name.as_name(), crate::per_ns::PerNs::from_def(def, vis)); | 578 | self.db.struct_data(s).variant_data.kind() != StructKind::Record |
579 | } | ||
580 | _ => true, | ||
581 | }; | ||
582 | self.body.item_scope.push_res( | ||
583 | name.as_name(), | ||
584 | crate::per_ns::PerNs::from_def(def, vis, has_constructor), | ||
585 | ); | ||
581 | } | 586 | } |
582 | } | 587 | } |
583 | } | 588 | } |
diff --git a/crates/ra_hir_def/src/data.rs b/crates/ra_hir_def/src/data.rs index d4cba4d05..e7eb2bb11 100644 --- a/crates/ra_hir_def/src/data.rs +++ b/crates/ra_hir_def/src/data.rs | |||
@@ -9,7 +9,7 @@ use hir_expand::{ | |||
9 | }; | 9 | }; |
10 | use ra_prof::profile; | 10 | use ra_prof::profile; |
11 | use ra_syntax::ast::{ | 11 | use ra_syntax::ast::{ |
12 | self, AstNode, ImplItem, ModuleItemOwner, NameOwner, TypeAscriptionOwner, TypeBoundsOwner, | 12 | self, AssocItem, AstNode, ModuleItemOwner, NameOwner, TypeAscriptionOwner, TypeBoundsOwner, |
13 | VisibilityOwner, | 13 | VisibilityOwner, |
14 | }; | 14 | }; |
15 | 15 | ||
@@ -164,7 +164,7 @@ impl TraitData { | |||
164 | items.extend(collect_items( | 164 | items.extend(collect_items( |
165 | db, | 165 | db, |
166 | &mut expander, | 166 | &mut expander, |
167 | item_list.impl_items(), | 167 | item_list.assoc_items(), |
168 | src.file_id, | 168 | src.file_id, |
169 | container, | 169 | container, |
170 | )); | 170 | )); |
@@ -219,7 +219,7 @@ impl ImplData { | |||
219 | if let Some(item_list) = src.value.item_list() { | 219 | if let Some(item_list) = src.value.item_list() { |
220 | let mut expander = Expander::new(db, impl_loc.ast_id.file_id, module_id); | 220 | let mut expander = Expander::new(db, impl_loc.ast_id.file_id, module_id); |
221 | items.extend( | 221 | items.extend( |
222 | collect_items(db, &mut expander, item_list.impl_items(), src.file_id, container) | 222 | collect_items(db, &mut expander, item_list.assoc_items(), src.file_id, container) |
223 | .into_iter() | 223 | .into_iter() |
224 | .map(|(_, item)| item), | 224 | .map(|(_, item)| item), |
225 | ); | 225 | ); |
@@ -304,7 +304,7 @@ fn collect_items_in_macro( | |||
304 | let mut res = collect_items( | 304 | let mut res = collect_items( |
305 | db, | 305 | db, |
306 | expander, | 306 | expander, |
307 | items.value.items().filter_map(|it| ImplItem::cast(it.syntax().clone())), | 307 | items.value.items().filter_map(|it| AssocItem::cast(it.syntax().clone())), |
308 | items.file_id, | 308 | items.file_id, |
309 | container, | 309 | container, |
310 | ); | 310 | ); |
@@ -325,31 +325,30 @@ fn collect_items_in_macro( | |||
325 | fn collect_items( | 325 | fn collect_items( |
326 | db: &dyn DefDatabase, | 326 | db: &dyn DefDatabase, |
327 | expander: &mut Expander, | 327 | expander: &mut Expander, |
328 | impl_items: impl Iterator<Item = ImplItem>, | 328 | assoc_items: impl Iterator<Item = AssocItem>, |
329 | file_id: crate::HirFileId, | 329 | file_id: crate::HirFileId, |
330 | container: AssocContainerId, | 330 | container: AssocContainerId, |
331 | ) -> Vec<(Name, AssocItemId)> { | 331 | ) -> Vec<(Name, AssocItemId)> { |
332 | let items = db.ast_id_map(file_id); | 332 | let items = db.ast_id_map(file_id); |
333 | 333 | ||
334 | impl_items | 334 | assoc_items |
335 | .filter_map(|item_node| match item_node { | 335 | .filter_map(|item_node| match item_node { |
336 | ast::ImplItem::FnDef(it) => { | 336 | ast::AssocItem::FnDef(it) => { |
337 | let name = it.name().map_or_else(Name::missing, |it| it.as_name()); | 337 | let name = it.name().map_or_else(Name::missing, |it| it.as_name()); |
338 | let attrs = expander.parse_attrs(&it); | 338 | if !expander.is_cfg_enabled(&it) { |
339 | if !expander.is_cfg_enabled(&attrs) { | ||
340 | return None; | 339 | return None; |
341 | } | 340 | } |
342 | let def = FunctionLoc { container, ast_id: AstId::new(file_id, items.ast_id(&it)) } | 341 | let def = FunctionLoc { container, ast_id: AstId::new(file_id, items.ast_id(&it)) } |
343 | .intern(db); | 342 | .intern(db); |
344 | Some((name, def.into())) | 343 | Some((name, def.into())) |
345 | } | 344 | } |
346 | ast::ImplItem::ConstDef(it) => { | 345 | ast::AssocItem::ConstDef(it) => { |
347 | let name = it.name().map_or_else(Name::missing, |it| it.as_name()); | 346 | let name = it.name().map_or_else(Name::missing, |it| it.as_name()); |
348 | let def = ConstLoc { container, ast_id: AstId::new(file_id, items.ast_id(&it)) } | 347 | let def = ConstLoc { container, ast_id: AstId::new(file_id, items.ast_id(&it)) } |
349 | .intern(db); | 348 | .intern(db); |
350 | Some((name, def.into())) | 349 | Some((name, def.into())) |
351 | } | 350 | } |
352 | ast::ImplItem::TypeAliasDef(it) => { | 351 | ast::AssocItem::TypeAliasDef(it) => { |
353 | let name = it.name().map_or_else(Name::missing, |it| it.as_name()); | 352 | let name = it.name().map_or_else(Name::missing, |it| it.as_name()); |
354 | let def = | 353 | let def = |
355 | TypeAliasLoc { container, ast_id: AstId::new(file_id, items.ast_id(&it)) } | 354 | TypeAliasLoc { container, ast_id: AstId::new(file_id, items.ast_id(&it)) } |
diff --git a/crates/ra_hir_def/src/item_scope.rs b/crates/ra_hir_def/src/item_scope.rs index 259b9ff03..fc15948ad 100644 --- a/crates/ra_hir_def/src/item_scope.rs +++ b/crates/ra_hir_def/src/item_scope.rs | |||
@@ -151,13 +151,20 @@ impl ItemScope { | |||
151 | } | 151 | } |
152 | 152 | ||
153 | impl PerNs { | 153 | impl PerNs { |
154 | pub(crate) fn from_def(def: ModuleDefId, v: Visibility) -> PerNs { | 154 | pub(crate) fn from_def(def: ModuleDefId, v: Visibility, has_constructor: bool) -> PerNs { |
155 | match def { | 155 | match def { |
156 | ModuleDefId::ModuleId(_) => PerNs::types(def, v), | 156 | ModuleDefId::ModuleId(_) => PerNs::types(def, v), |
157 | ModuleDefId::FunctionId(_) => PerNs::values(def, v), | 157 | ModuleDefId::FunctionId(_) => PerNs::values(def, v), |
158 | ModuleDefId::AdtId(adt) => match adt { | 158 | ModuleDefId::AdtId(adt) => match adt { |
159 | AdtId::StructId(_) | AdtId::UnionId(_) => PerNs::both(def, def, v), | 159 | AdtId::UnionId(_) => PerNs::types(def, v), |
160 | AdtId::EnumId(_) => PerNs::types(def, v), | 160 | AdtId::EnumId(_) => PerNs::types(def, v), |
161 | AdtId::StructId(_) => { | ||
162 | if has_constructor { | ||
163 | PerNs::both(def, def, v) | ||
164 | } else { | ||
165 | PerNs::types(def, v) | ||
166 | } | ||
167 | } | ||
161 | }, | 168 | }, |
162 | ModuleDefId::EnumVariantId(_) => PerNs::both(def, def, v), | 169 | ModuleDefId::EnumVariantId(_) => PerNs::both(def, def, v), |
163 | ModuleDefId::ConstId(_) | ModuleDefId::StaticId(_) => PerNs::values(def, v), | 170 | ModuleDefId::ConstId(_) | ModuleDefId::StaticId(_) => PerNs::values(def, v), |
diff --git a/crates/ra_hir_def/src/nameres/collector.rs b/crates/ra_hir_def/src/nameres/collector.rs index bf3968bd6..db994122a 100644 --- a/crates/ra_hir_def/src/nameres/collector.rs +++ b/crates/ra_hir_def/src/nameres/collector.rs | |||
@@ -830,7 +830,7 @@ impl ModCollector<'_, '_> { | |||
830 | let module = ModuleId { krate: self.def_collector.def_map.krate, local_id: res }; | 830 | let module = ModuleId { krate: self.def_collector.def_map.krate, local_id: res }; |
831 | let def: ModuleDefId = module.into(); | 831 | let def: ModuleDefId = module.into(); |
832 | self.def_collector.def_map.modules[self.module_id].scope.define_def(def); | 832 | self.def_collector.def_map.modules[self.module_id].scope.define_def(def); |
833 | self.def_collector.update(self.module_id, &[(name, PerNs::from_def(def, vis))], vis); | 833 | self.def_collector.update(self.module_id, &[(name, PerNs::from_def(def, vis, false))], vis); |
834 | res | 834 | res |
835 | } | 835 | } |
836 | 836 | ||
@@ -844,6 +844,8 @@ impl ModCollector<'_, '_> { | |||
844 | let name = def.name.clone(); | 844 | let name = def.name.clone(); |
845 | let container = ContainerId::ModuleId(module); | 845 | let container = ContainerId::ModuleId(module); |
846 | let vis = &def.visibility; | 846 | let vis = &def.visibility; |
847 | let mut has_constructor = false; | ||
848 | |||
847 | let def: ModuleDefId = match def.kind { | 849 | let def: ModuleDefId = match def.kind { |
848 | raw::DefKind::Function(ast_id) => FunctionLoc { | 850 | raw::DefKind::Function(ast_id) => FunctionLoc { |
849 | container: container.into(), | 851 | container: container.into(), |
@@ -851,7 +853,8 @@ impl ModCollector<'_, '_> { | |||
851 | } | 853 | } |
852 | .intern(self.def_collector.db) | 854 | .intern(self.def_collector.db) |
853 | .into(), | 855 | .into(), |
854 | raw::DefKind::Struct(ast_id) => { | 856 | raw::DefKind::Struct(ast_id, mode) => { |
857 | has_constructor = mode != raw::StructDefKind::Record; | ||
855 | StructLoc { container, ast_id: AstId::new(self.file_id, ast_id) } | 858 | StructLoc { container, ast_id: AstId::new(self.file_id, ast_id) } |
856 | .intern(self.def_collector.db) | 859 | .intern(self.def_collector.db) |
857 | .into() | 860 | .into() |
@@ -894,7 +897,11 @@ impl ModCollector<'_, '_> { | |||
894 | .def_map | 897 | .def_map |
895 | .resolve_visibility(self.def_collector.db, self.module_id, vis) | 898 | .resolve_visibility(self.def_collector.db, self.module_id, vis) |
896 | .unwrap_or(Visibility::Public); | 899 | .unwrap_or(Visibility::Public); |
897 | self.def_collector.update(self.module_id, &[(name, PerNs::from_def(def, vis))], vis) | 900 | self.def_collector.update( |
901 | self.module_id, | ||
902 | &[(name, PerNs::from_def(def, vis, has_constructor))], | ||
903 | vis, | ||
904 | ) | ||
898 | } | 905 | } |
899 | 906 | ||
900 | fn collect_derives(&mut self, attrs: &Attrs, def: &raw::DefData) { | 907 | fn collect_derives(&mut self, attrs: &Attrs, def: &raw::DefData) { |
diff --git a/crates/ra_hir_def/src/nameres/raw.rs b/crates/ra_hir_def/src/nameres/raw.rs index a71503c76..f2716a295 100644 --- a/crates/ra_hir_def/src/nameres/raw.rs +++ b/crates/ra_hir_def/src/nameres/raw.rs | |||
@@ -156,9 +156,16 @@ pub(super) struct DefData { | |||
156 | } | 156 | } |
157 | 157 | ||
158 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] | 158 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] |
159 | pub(super) enum StructDefKind { | ||
160 | Record, | ||
161 | Tuple, | ||
162 | Unit, | ||
163 | } | ||
164 | |||
165 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] | ||
159 | pub(super) enum DefKind { | 166 | pub(super) enum DefKind { |
160 | Function(FileAstId<ast::FnDef>), | 167 | Function(FileAstId<ast::FnDef>), |
161 | Struct(FileAstId<ast::StructDef>), | 168 | Struct(FileAstId<ast::StructDef>, StructDefKind), |
162 | Union(FileAstId<ast::UnionDef>), | 169 | Union(FileAstId<ast::UnionDef>), |
163 | Enum(FileAstId<ast::EnumDef>), | 170 | Enum(FileAstId<ast::EnumDef>), |
164 | Const(FileAstId<ast::ConstDef>), | 171 | Const(FileAstId<ast::ConstDef>), |
@@ -171,7 +178,7 @@ impl DefKind { | |||
171 | pub fn ast_id(&self) -> FileAstId<ast::ModuleItem> { | 178 | pub fn ast_id(&self) -> FileAstId<ast::ModuleItem> { |
172 | match self { | 179 | match self { |
173 | DefKind::Function(it) => it.upcast(), | 180 | DefKind::Function(it) => it.upcast(), |
174 | DefKind::Struct(it) => it.upcast(), | 181 | DefKind::Struct(it, _) => it.upcast(), |
175 | DefKind::Union(it) => it.upcast(), | 182 | DefKind::Union(it) => it.upcast(), |
176 | DefKind::Enum(it) => it.upcast(), | 183 | DefKind::Enum(it) => it.upcast(), |
177 | DefKind::Const(it) => it.upcast(), | 184 | DefKind::Const(it) => it.upcast(), |
@@ -236,9 +243,14 @@ impl RawItemsCollector { | |||
236 | return; | 243 | return; |
237 | } | 244 | } |
238 | ast::ModuleItem::StructDef(it) => { | 245 | ast::ModuleItem::StructDef(it) => { |
246 | let kind = match it.kind() { | ||
247 | ast::StructKind::Record(_) => StructDefKind::Record, | ||
248 | ast::StructKind::Tuple(_) => StructDefKind::Tuple, | ||
249 | ast::StructKind::Unit => StructDefKind::Unit, | ||
250 | }; | ||
239 | let id = self.source_ast_id_map.ast_id(&it); | 251 | let id = self.source_ast_id_map.ast_id(&it); |
240 | let name = it.name(); | 252 | let name = it.name(); |
241 | (DefKind::Struct(id), name) | 253 | (DefKind::Struct(id, kind), name) |
242 | } | 254 | } |
243 | ast::ModuleItem::UnionDef(it) => { | 255 | ast::ModuleItem::UnionDef(it) => { |
244 | let id = self.source_ast_id_map.ast_id(&it); | 256 | let id = self.source_ast_id_map.ast_id(&it); |
diff --git a/crates/ra_hir_def/src/nameres/tests.rs b/crates/ra_hir_def/src/nameres/tests.rs index 83120fa36..1b66c1aac 100644 --- a/crates/ra_hir_def/src/nameres/tests.rs +++ b/crates/ra_hir_def/src/nameres/tests.rs | |||
@@ -67,7 +67,7 @@ fn crate_def_map_smoke_test() { | |||
67 | ⋮Baz: t v | 67 | ⋮Baz: t v |
68 | ⋮E: t | 68 | ⋮E: t |
69 | ⋮EXT: v | 69 | ⋮EXT: v |
70 | ⋮U: t v | 70 | ⋮U: t |
71 | ⋮ext: v | 71 | ⋮ext: v |
72 | "###) | 72 | "###) |
73 | } | 73 | } |
diff --git a/crates/ra_hir_def/src/nameres/tests/macros.rs b/crates/ra_hir_def/src/nameres/tests/macros.rs index 9bc0e6287..40289e3ca 100644 --- a/crates/ra_hir_def/src/nameres/tests/macros.rs +++ b/crates/ra_hir_def/src/nameres/tests/macros.rs | |||
@@ -19,12 +19,12 @@ fn macro_rules_are_globally_visible() { | |||
19 | ); | 19 | ); |
20 | assert_snapshot!(map, @r###" | 20 | assert_snapshot!(map, @r###" |
21 | ⋮crate | 21 | ⋮crate |
22 | ⋮Foo: t v | 22 | ⋮Foo: t |
23 | ⋮nested: t | 23 | ⋮nested: t |
24 | ⋮ | 24 | ⋮ |
25 | ⋮crate::nested | 25 | ⋮crate::nested |
26 | ⋮Bar: t v | 26 | ⋮Bar: t |
27 | ⋮Baz: t v | 27 | ⋮Baz: t |
28 | "###); | 28 | "###); |
29 | } | 29 | } |
30 | 30 | ||
@@ -91,13 +91,13 @@ fn macro_rules_from_other_crates_are_visible() { | |||
91 | ); | 91 | ); |
92 | assert_snapshot!(map, @r###" | 92 | assert_snapshot!(map, @r###" |
93 | ⋮crate | 93 | ⋮crate |
94 | ⋮Bar: t v | 94 | ⋮Bar: t |
95 | ⋮Foo: t v | 95 | ⋮Foo: t |
96 | ⋮bar: t | 96 | ⋮bar: t |
97 | ⋮ | 97 | ⋮ |
98 | ⋮crate::bar | 98 | ⋮crate::bar |
99 | ⋮Bar: t v | 99 | ⋮Bar: t |
100 | ⋮Foo: t v | 100 | ⋮Foo: t |
101 | ⋮bar: t | 101 | ⋮bar: t |
102 | "###); | 102 | "###); |
103 | } | 103 | } |
@@ -124,13 +124,13 @@ fn macro_rules_export_with_local_inner_macros_are_visible() { | |||
124 | ); | 124 | ); |
125 | assert_snapshot!(map, @r###" | 125 | assert_snapshot!(map, @r###" |
126 | ⋮crate | 126 | ⋮crate |
127 | ⋮Bar: t v | 127 | ⋮Bar: t |
128 | ⋮Foo: t v | 128 | ⋮Foo: t |
129 | ⋮bar: t | 129 | ⋮bar: t |
130 | ⋮ | 130 | ⋮ |
131 | ⋮crate::bar | 131 | ⋮crate::bar |
132 | ⋮Bar: t v | 132 | ⋮Bar: t |
133 | ⋮Foo: t v | 133 | ⋮Foo: t |
134 | ⋮bar: t | 134 | ⋮bar: t |
135 | "###); | 135 | "###); |
136 | } | 136 | } |
@@ -161,13 +161,13 @@ fn local_inner_macros_makes_local_macros_usable() { | |||
161 | ); | 161 | ); |
162 | assert_snapshot!(map, @r###" | 162 | assert_snapshot!(map, @r###" |
163 | ⋮crate | 163 | ⋮crate |
164 | ⋮Bar: t v | 164 | ⋮Bar: t |
165 | ⋮Foo: t v | 165 | ⋮Foo: t |
166 | ⋮bar: t | 166 | ⋮bar: t |
167 | ⋮ | 167 | ⋮ |
168 | ⋮crate::bar | 168 | ⋮crate::bar |
169 | ⋮Bar: t v | 169 | ⋮Bar: t |
170 | ⋮Foo: t v | 170 | ⋮Foo: t |
171 | ⋮bar: t | 171 | ⋮bar: t |
172 | "###); | 172 | "###); |
173 | } | 173 | } |
@@ -204,7 +204,7 @@ fn unexpanded_macro_should_expand_by_fixedpoint_loop() { | |||
204 | ); | 204 | ); |
205 | assert_snapshot!(map, @r###" | 205 | assert_snapshot!(map, @r###" |
206 | ⋮crate | 206 | ⋮crate |
207 | ⋮Foo: t v | 207 | ⋮Foo: t |
208 | ⋮bar: m | 208 | ⋮bar: m |
209 | ⋮foo: m | 209 | ⋮foo: m |
210 | "###); | 210 | "###); |
diff --git a/crates/ra_hir_ty/src/tests/macros.rs b/crates/ra_hir_ty/src/tests/macros.rs index 29e38a06c..07398ddcc 100644 --- a/crates/ra_hir_ty/src/tests/macros.rs +++ b/crates/ra_hir_ty/src/tests/macros.rs | |||
@@ -269,7 +269,7 @@ fn test() { S.foo()<|>; } | |||
269 | } | 269 | } |
270 | 270 | ||
271 | #[test] | 271 | #[test] |
272 | fn infer_impl_items_generated_by_macros() { | 272 | fn infer_assoc_items_generated_by_macros() { |
273 | let t = type_at( | 273 | let t = type_at( |
274 | r#" | 274 | r#" |
275 | //- /main.rs | 275 | //- /main.rs |
@@ -288,7 +288,7 @@ fn test() { S.foo()<|>; } | |||
288 | } | 288 | } |
289 | 289 | ||
290 | #[test] | 290 | #[test] |
291 | fn infer_impl_items_generated_by_macros_chain() { | 291 | fn infer_assoc_items_generated_by_macros_chain() { |
292 | let t = type_at( | 292 | let t = type_at( |
293 | r#" | 293 | r#" |
294 | //- /main.rs | 294 | //- /main.rs |
diff --git a/crates/ra_hir_ty/src/tests/regression.rs b/crates/ra_hir_ty/src/tests/regression.rs index 8a1292c7a..115ad8328 100644 --- a/crates/ra_hir_ty/src/tests/regression.rs +++ b/crates/ra_hir_ty/src/tests/regression.rs | |||
@@ -535,6 +535,35 @@ fn foo(b: Bar) { | |||
535 | } | 535 | } |
536 | 536 | ||
537 | #[test] | 537 | #[test] |
538 | fn issue_4235_name_conflicts() { | ||
539 | assert_snapshot!( | ||
540 | infer(r#" | ||
541 | struct FOO {} | ||
542 | static FOO:FOO = FOO {}; | ||
543 | |||
544 | impl FOO { | ||
545 | fn foo(&self) {} | ||
546 | } | ||
547 | |||
548 | fn main() { | ||
549 | let a = &FOO; | ||
550 | a.foo(); | ||
551 | } | ||
552 | "#), @r###" | ||
553 | 32..38 'FOO {}': FOO | ||
554 | 64..68 'self': &FOO | ||
555 | 70..72 '{}': () | ||
556 | 86..120 '{ ...o(); }': () | ||
557 | 96..97 'a': &FOO | ||
558 | 100..104 '&FOO': &FOO | ||
559 | 101..104 'FOO': FOO | ||
560 | 110..111 'a': &FOO | ||
561 | 110..117 'a.foo()': () | ||
562 | "### | ||
563 | ); | ||
564 | } | ||
565 | |||
566 | #[test] | ||
538 | fn issue_4053_diesel_where_clauses() { | 567 | fn issue_4053_diesel_where_clauses() { |
539 | assert_snapshot!( | 568 | assert_snapshot!( |
540 | infer(r#" | 569 | infer(r#" |
diff --git a/crates/ra_ide/src/assists.rs b/crates/ra_ide/src/assists.rs index 2b5d11681..389339a03 100644 --- a/crates/ra_ide/src/assists.rs +++ b/crates/ra_ide/src/assists.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use ra_assists::{resolved_assists, AssistAction, AssistLabel}; | 3 | use ra_assists::{resolved_assists, AssistAction}; |
4 | use ra_db::{FilePosition, FileRange}; | 4 | use ra_db::{FilePosition, FileRange}; |
5 | use ra_ide_db::RootDatabase; | 5 | use ra_ide_db::RootDatabase; |
6 | 6 | ||
@@ -21,27 +21,22 @@ pub(crate) fn assists(db: &RootDatabase, frange: FileRange) -> Vec<Assist> { | |||
21 | .into_iter() | 21 | .into_iter() |
22 | .map(|assist| { | 22 | .map(|assist| { |
23 | let file_id = frange.file_id; | 23 | let file_id = frange.file_id; |
24 | let assist_label = &assist.label; | ||
25 | Assist { | 24 | Assist { |
26 | id: assist_label.id, | 25 | id: assist.label.id, |
27 | label: assist_label.label.clone(), | 26 | label: assist.label.label.clone(), |
28 | group_label: assist.group_label.map(|it| it.0), | 27 | group_label: assist.label.group.map(|it| it.0), |
29 | source_change: action_to_edit(assist.action, file_id, assist_label), | 28 | source_change: action_to_edit(assist.action, file_id, assist.label.label.clone()), |
30 | } | 29 | } |
31 | }) | 30 | }) |
32 | .collect() | 31 | .collect() |
33 | } | 32 | } |
34 | 33 | ||
35 | fn action_to_edit( | 34 | fn action_to_edit(action: AssistAction, file_id: FileId, label: String) -> SourceChange { |
36 | action: AssistAction, | ||
37 | file_id: FileId, | ||
38 | assist_label: &AssistLabel, | ||
39 | ) -> SourceChange { | ||
40 | let file_id = match action.file { | 35 | let file_id = match action.file { |
41 | ra_assists::AssistFile::TargetFile(it) => it, | 36 | ra_assists::AssistFile::TargetFile(it) => it, |
42 | _ => file_id, | 37 | _ => file_id, |
43 | }; | 38 | }; |
44 | let file_edit = SourceFileEdit { file_id, edit: action.edit }; | 39 | let file_edit = SourceFileEdit { file_id, edit: action.edit }; |
45 | SourceChange::source_file_edit(assist_label.label.clone(), file_edit) | 40 | SourceChange::source_file_edit(label, file_edit) |
46 | .with_cursor_opt(action.cursor_position.map(|offset| FilePosition { offset, file_id })) | 41 | .with_cursor_opt(action.cursor_position.map(|offset| FilePosition { offset, file_id })) |
47 | } | 42 | } |
diff --git a/crates/ra_ide/src/completion/complete_qualified_path.rs b/crates/ra_ide/src/completion/complete_qualified_path.rs index d9ea92ef8..7fcd22525 100644 --- a/crates/ra_ide/src/completion/complete_qualified_path.rs +++ b/crates/ra_ide/src/completion/complete_qualified_path.rs | |||
@@ -84,7 +84,7 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon | |||
84 | }); | 84 | }); |
85 | 85 | ||
86 | // Iterate assoc types separately | 86 | // Iterate assoc types separately |
87 | ty.iterate_impl_items(ctx.db, krate, |item| { | 87 | ty.iterate_assoc_items(ctx.db, krate, |item| { |
88 | if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) { | 88 | if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) { |
89 | return None; | 89 | return None; |
90 | } | 90 | } |
diff --git a/crates/ra_ide/src/completion/complete_trait_impl.rs b/crates/ra_ide/src/completion/complete_trait_impl.rs index ee32d1ff6..039df03e0 100644 --- a/crates/ra_ide/src/completion/complete_trait_impl.rs +++ b/crates/ra_ide/src/completion/complete_trait_impl.rs | |||
@@ -32,7 +32,7 @@ | |||
32 | //! ``` | 32 | //! ``` |
33 | 33 | ||
34 | use hir::{self, Docs, HasSource}; | 34 | use hir::{self, Docs, HasSource}; |
35 | use ra_assists::utils::get_missing_impl_items; | 35 | use ra_assists::utils::get_missing_assoc_items; |
36 | use ra_syntax::{ | 36 | use ra_syntax::{ |
37 | ast::{self, edit, ImplDef}, | 37 | ast::{self, edit, ImplDef}, |
38 | AstNode, SyntaxKind, SyntaxNode, TextRange, T, | 38 | AstNode, SyntaxKind, SyntaxNode, TextRange, T, |
@@ -50,7 +50,7 @@ pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext | |||
50 | if let Some((trigger, impl_def)) = completion_match(ctx) { | 50 | if let Some((trigger, impl_def)) = completion_match(ctx) { |
51 | match trigger.kind() { | 51 | match trigger.kind() { |
52 | SyntaxKind::NAME_REF => { | 52 | SyntaxKind::NAME_REF => { |
53 | get_missing_impl_items(&ctx.sema, &impl_def).iter().for_each(|item| match item { | 53 | get_missing_assoc_items(&ctx.sema, &impl_def).iter().for_each(|item| match item { |
54 | hir::AssocItem::Function(fn_item) => { | 54 | hir::AssocItem::Function(fn_item) => { |
55 | add_function_impl(&trigger, acc, ctx, &fn_item) | 55 | add_function_impl(&trigger, acc, ctx, &fn_item) |
56 | } | 56 | } |
@@ -64,34 +64,40 @@ pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext | |||
64 | } | 64 | } |
65 | 65 | ||
66 | SyntaxKind::FN_DEF => { | 66 | SyntaxKind::FN_DEF => { |
67 | for missing_fn in get_missing_impl_items(&ctx.sema, &impl_def).iter().filter_map( | 67 | for missing_fn in |
68 | |item| match item { | 68 | get_missing_assoc_items(&ctx.sema, &impl_def).iter().filter_map(|item| { |
69 | hir::AssocItem::Function(fn_item) => Some(fn_item), | 69 | match item { |
70 | _ => None, | 70 | hir::AssocItem::Function(fn_item) => Some(fn_item), |
71 | }, | 71 | _ => None, |
72 | ) { | 72 | } |
73 | }) | ||
74 | { | ||
73 | add_function_impl(&trigger, acc, ctx, &missing_fn); | 75 | add_function_impl(&trigger, acc, ctx, &missing_fn); |
74 | } | 76 | } |
75 | } | 77 | } |
76 | 78 | ||
77 | SyntaxKind::TYPE_ALIAS_DEF => { | 79 | SyntaxKind::TYPE_ALIAS_DEF => { |
78 | for missing_fn in get_missing_impl_items(&ctx.sema, &impl_def).iter().filter_map( | 80 | for missing_fn in |
79 | |item| match item { | 81 | get_missing_assoc_items(&ctx.sema, &impl_def).iter().filter_map(|item| { |
80 | hir::AssocItem::TypeAlias(type_item) => Some(type_item), | 82 | match item { |
81 | _ => None, | 83 | hir::AssocItem::TypeAlias(type_item) => Some(type_item), |
82 | }, | 84 | _ => None, |
83 | ) { | 85 | } |
86 | }) | ||
87 | { | ||
84 | add_type_alias_impl(&trigger, acc, ctx, &missing_fn); | 88 | add_type_alias_impl(&trigger, acc, ctx, &missing_fn); |
85 | } | 89 | } |
86 | } | 90 | } |
87 | 91 | ||
88 | SyntaxKind::CONST_DEF => { | 92 | SyntaxKind::CONST_DEF => { |
89 | for missing_fn in get_missing_impl_items(&ctx.sema, &impl_def).iter().filter_map( | 93 | for missing_fn in |
90 | |item| match item { | 94 | get_missing_assoc_items(&ctx.sema, &impl_def).iter().filter_map(|item| { |
91 | hir::AssocItem::Const(const_item) => Some(const_item), | 95 | match item { |
92 | _ => None, | 96 | hir::AssocItem::Const(const_item) => Some(const_item), |
93 | }, | 97 | _ => None, |
94 | ) { | 98 | } |
99 | }) | ||
100 | { | ||
95 | add_const_impl(&trigger, acc, ctx, &missing_fn); | 101 | add_const_impl(&trigger, acc, ctx, &missing_fn); |
96 | } | 102 | } |
97 | } | 103 | } |
diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs index dd87bd119..b6b9627de 100644 --- a/crates/ra_ide/src/completion/completion_context.rs +++ b/crates/ra_ide/src/completion/completion_context.rs | |||
@@ -9,7 +9,7 @@ use ra_syntax::{ | |||
9 | SyntaxKind::*, | 9 | SyntaxKind::*, |
10 | SyntaxNode, SyntaxToken, TextRange, TextSize, | 10 | SyntaxNode, SyntaxToken, TextRange, TextSize, |
11 | }; | 11 | }; |
12 | use ra_text_edit::AtomTextEdit; | 12 | use ra_text_edit::Indel; |
13 | 13 | ||
14 | use crate::{call_info::ActiveParameter, completion::CompletionConfig, FilePosition}; | 14 | use crate::{call_info::ActiveParameter, completion::CompletionConfig, FilePosition}; |
15 | 15 | ||
@@ -76,7 +76,7 @@ impl<'a> CompletionContext<'a> { | |||
76 | // actual completion. | 76 | // actual completion. |
77 | let file_with_fake_ident = { | 77 | let file_with_fake_ident = { |
78 | let parse = db.parse(position.file_id); | 78 | let parse = db.parse(position.file_id); |
79 | let edit = AtomTextEdit::insert(position.offset, "intellijRulezz".to_string()); | 79 | let edit = Indel::insert(position.offset, "intellijRulezz".to_string()); |
80 | parse.reparse(&edit).tree() | 80 | parse.reparse(&edit).tree() |
81 | }; | 81 | }; |
82 | let fake_ident_token = | 82 | let fake_ident_token = |
diff --git a/crates/ra_ide/src/completion/completion_item.rs b/crates/ra_ide/src/completion/completion_item.rs index 5936fb8f7..383b23ac4 100644 --- a/crates/ra_ide/src/completion/completion_item.rs +++ b/crates/ra_ide/src/completion/completion_item.rs | |||
@@ -62,8 +62,8 @@ impl fmt::Debug for CompletionItem { | |||
62 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | 62 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
63 | let mut s = f.debug_struct("CompletionItem"); | 63 | let mut s = f.debug_struct("CompletionItem"); |
64 | s.field("label", &self.label()).field("source_range", &self.source_range()); | 64 | s.field("label", &self.label()).field("source_range", &self.source_range()); |
65 | if self.text_edit().as_atoms().len() == 1 { | 65 | if self.text_edit().as_indels().len() == 1 { |
66 | let atom = &self.text_edit().as_atoms()[0]; | 66 | let atom = &self.text_edit().as_indels()[0]; |
67 | s.field("delete", &atom.delete); | 67 | s.field("delete", &atom.delete); |
68 | s.field("insert", &atom.insert); | 68 | s.field("insert", &atom.insert); |
69 | } else { | 69 | } else { |
diff --git a/crates/ra_ide/src/diagnostics.rs b/crates/ra_ide/src/diagnostics.rs index a6b4c2c28..87a0b80f1 100644 --- a/crates/ra_ide/src/diagnostics.rs +++ b/crates/ra_ide/src/diagnostics.rs | |||
@@ -64,7 +64,7 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic> | |||
64 | .unwrap_or_else(|| RelativePath::new("")) | 64 | .unwrap_or_else(|| RelativePath::new("")) |
65 | .join(&d.candidate); | 65 | .join(&d.candidate); |
66 | let create_file = FileSystemEdit::CreateFile { source_root, path }; | 66 | let create_file = FileSystemEdit::CreateFile { source_root, path }; |
67 | let fix = SourceChange::file_system_edit("create module", create_file); | 67 | let fix = SourceChange::file_system_edit("Create module", create_file); |
68 | res.borrow_mut().push(Diagnostic { | 68 | res.borrow_mut().push(Diagnostic { |
69 | range: sema.diagnostics_range(d).range, | 69 | range: sema.diagnostics_range(d).range, |
70 | message: d.message(), | 70 | message: d.message(), |
@@ -92,7 +92,7 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic> | |||
92 | algo::diff(&d.ast(db).syntax(), &field_list.syntax()).into_text_edit(&mut builder); | 92 | algo::diff(&d.ast(db).syntax(), &field_list.syntax()).into_text_edit(&mut builder); |
93 | 93 | ||
94 | Some(SourceChange::source_file_edit_from( | 94 | Some(SourceChange::source_file_edit_from( |
95 | "fill struct fields", | 95 | "Fill struct fields", |
96 | file_id, | 96 | file_id, |
97 | builder.finish(), | 97 | builder.finish(), |
98 | )) | 98 | )) |
@@ -117,7 +117,7 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic> | |||
117 | let node = d.ast(db); | 117 | let node = d.ast(db); |
118 | let replacement = format!("Ok({})", node.syntax()); | 118 | let replacement = format!("Ok({})", node.syntax()); |
119 | let edit = TextEdit::replace(node.syntax().text_range(), replacement); | 119 | let edit = TextEdit::replace(node.syntax().text_range(), replacement); |
120 | let fix = SourceChange::source_file_edit_from("wrap with ok", file_id, edit); | 120 | let fix = SourceChange::source_file_edit_from("Wrap with ok", file_id, edit); |
121 | res.borrow_mut().push(Diagnostic { | 121 | res.borrow_mut().push(Diagnostic { |
122 | range: sema.diagnostics_range(d).range, | 122 | range: sema.diagnostics_range(d).range, |
123 | message: d.message(), | 123 | message: d.message(), |
@@ -199,7 +199,7 @@ fn check_struct_shorthand_initialization( | |||
199 | message: "Shorthand struct initialization".to_string(), | 199 | message: "Shorthand struct initialization".to_string(), |
200 | severity: Severity::WeakWarning, | 200 | severity: Severity::WeakWarning, |
201 | fix: Some(SourceChange::source_file_edit( | 201 | fix: Some(SourceChange::source_file_edit( |
202 | "use struct shorthand initialization", | 202 | "Use struct shorthand initialization", |
203 | SourceFileEdit { file_id, edit }, | 203 | SourceFileEdit { file_id, edit }, |
204 | )), | 204 | )), |
205 | }); | 205 | }); |
@@ -241,7 +241,11 @@ mod tests { | |||
241 | diagnostics.pop().unwrap_or_else(|| panic!("no diagnostics for:\n{}\n", before)); | 241 | diagnostics.pop().unwrap_or_else(|| panic!("no diagnostics for:\n{}\n", before)); |
242 | let mut fix = diagnostic.fix.unwrap(); | 242 | let mut fix = diagnostic.fix.unwrap(); |
243 | let edit = fix.source_file_edits.pop().unwrap().edit; | 243 | let edit = fix.source_file_edits.pop().unwrap().edit; |
244 | let actual = edit.apply(&before); | 244 | let actual = { |
245 | let mut actual = before.to_string(); | ||
246 | edit.apply(&mut actual); | ||
247 | actual | ||
248 | }; | ||
245 | assert_eq_text!(after, &actual); | 249 | assert_eq_text!(after, &actual); |
246 | } | 250 | } |
247 | 251 | ||
@@ -256,7 +260,11 @@ mod tests { | |||
256 | let mut fix = diagnostic.fix.unwrap(); | 260 | let mut fix = diagnostic.fix.unwrap(); |
257 | let edit = fix.source_file_edits.pop().unwrap().edit; | 261 | let edit = fix.source_file_edits.pop().unwrap().edit; |
258 | let target_file_contents = analysis.file_text(file_position.file_id).unwrap(); | 262 | let target_file_contents = analysis.file_text(file_position.file_id).unwrap(); |
259 | let actual = edit.apply(&target_file_contents); | 263 | let actual = { |
264 | let mut actual = target_file_contents.to_string(); | ||
265 | edit.apply(&mut actual); | ||
266 | actual | ||
267 | }; | ||
260 | 268 | ||
261 | // Strip indent and empty lines from `after`, to match the behaviour of | 269 | // Strip indent and empty lines from `after`, to match the behaviour of |
262 | // `parse_fixture` called from `analysis_and_position`. | 270 | // `parse_fixture` called from `analysis_and_position`. |
@@ -288,7 +296,11 @@ mod tests { | |||
288 | let diagnostic = analysis.diagnostics(file_id).unwrap().pop().unwrap(); | 296 | let diagnostic = analysis.diagnostics(file_id).unwrap().pop().unwrap(); |
289 | let mut fix = diagnostic.fix.unwrap(); | 297 | let mut fix = diagnostic.fix.unwrap(); |
290 | let edit = fix.source_file_edits.pop().unwrap().edit; | 298 | let edit = fix.source_file_edits.pop().unwrap().edit; |
291 | let actual = edit.apply(&before); | 299 | let actual = { |
300 | let mut actual = before.to_string(); | ||
301 | edit.apply(&mut actual); | ||
302 | actual | ||
303 | }; | ||
292 | assert_eq_text!(after, &actual); | 304 | assert_eq_text!(after, &actual); |
293 | } | 305 | } |
294 | 306 | ||
@@ -606,7 +618,7 @@ mod tests { | |||
606 | range: 0..8, | 618 | range: 0..8, |
607 | fix: Some( | 619 | fix: Some( |
608 | SourceChange { | 620 | SourceChange { |
609 | label: "create module", | 621 | label: "Create module", |
610 | source_file_edits: [], | 622 | source_file_edits: [], |
611 | file_system_edits: [ | 623 | file_system_edits: [ |
612 | CreateFile { | 624 | CreateFile { |
@@ -655,17 +667,17 @@ mod tests { | |||
655 | range: 224..233, | 667 | range: 224..233, |
656 | fix: Some( | 668 | fix: Some( |
657 | SourceChange { | 669 | SourceChange { |
658 | label: "fill struct fields", | 670 | label: "Fill struct fields", |
659 | source_file_edits: [ | 671 | source_file_edits: [ |
660 | SourceFileEdit { | 672 | SourceFileEdit { |
661 | file_id: FileId( | 673 | file_id: FileId( |
662 | 1, | 674 | 1, |
663 | ), | 675 | ), |
664 | edit: TextEdit { | 676 | edit: TextEdit { |
665 | atoms: [ | 677 | indels: [ |
666 | AtomTextEdit { | 678 | Indel { |
667 | delete: 3..9, | ||
668 | insert: "{a:42, b: ()}", | 679 | insert: "{a:42, b: ()}", |
680 | delete: 3..9, | ||
669 | }, | 681 | }, |
670 | ], | 682 | ], |
671 | }, | 683 | }, |
diff --git a/crates/ra_ide/src/join_lines.rs b/crates/ra_ide/src/join_lines.rs index 63fd6b3e4..d3af780c4 100644 --- a/crates/ra_ide/src/join_lines.rs +++ b/crates/ra_ide/src/join_lines.rs | |||
@@ -569,7 +569,11 @@ fn foo() { | |||
569 | let (sel, before) = extract_range(before); | 569 | let (sel, before) = extract_range(before); |
570 | let parse = SourceFile::parse(&before); | 570 | let parse = SourceFile::parse(&before); |
571 | let result = join_lines(&parse.tree(), sel); | 571 | let result = join_lines(&parse.tree(), sel); |
572 | let actual = result.apply(&before); | 572 | let actual = { |
573 | let mut actual = before.to_string(); | ||
574 | result.apply(&mut actual); | ||
575 | actual | ||
576 | }; | ||
573 | assert_eq_text!(after, &actual); | 577 | assert_eq_text!(after, &actual); |
574 | } | 578 | } |
575 | 579 | ||
diff --git a/crates/ra_ide/src/references/rename.rs b/crates/ra_ide/src/references/rename.rs index 916edaef2..0398d53bc 100644 --- a/crates/ra_ide/src/references/rename.rs +++ b/crates/ra_ide/src/references/rename.rs | |||
@@ -122,7 +122,7 @@ fn rename_mod( | |||
122 | source_file_edits.extend(ref_edits); | 122 | source_file_edits.extend(ref_edits); |
123 | } | 123 | } |
124 | 124 | ||
125 | Some(SourceChange::from_edits("rename", source_file_edits, file_system_edits)) | 125 | Some(SourceChange::from_edits("Rename", source_file_edits, file_system_edits)) |
126 | } | 126 | } |
127 | 127 | ||
128 | fn rename_reference( | 128 | fn rename_reference( |
@@ -141,7 +141,7 @@ fn rename_reference( | |||
141 | return None; | 141 | return None; |
142 | } | 142 | } |
143 | 143 | ||
144 | Some(RangeInfo::new(range, SourceChange::source_file_edits("rename", edit))) | 144 | Some(RangeInfo::new(range, SourceChange::source_file_edits("Rename", edit))) |
145 | } | 145 | } |
146 | 146 | ||
147 | #[cfg(test)] | 147 | #[cfg(test)] |
@@ -530,17 +530,17 @@ mod tests { | |||
530 | RangeInfo { | 530 | RangeInfo { |
531 | range: 4..7, | 531 | range: 4..7, |
532 | info: SourceChange { | 532 | info: SourceChange { |
533 | label: "rename", | 533 | label: "Rename", |
534 | source_file_edits: [ | 534 | source_file_edits: [ |
535 | SourceFileEdit { | 535 | SourceFileEdit { |
536 | file_id: FileId( | 536 | file_id: FileId( |
537 | 2, | 537 | 2, |
538 | ), | 538 | ), |
539 | edit: TextEdit { | 539 | edit: TextEdit { |
540 | atoms: [ | 540 | indels: [ |
541 | AtomTextEdit { | 541 | Indel { |
542 | delete: 4..7, | ||
543 | insert: "foo2", | 542 | insert: "foo2", |
543 | delete: 4..7, | ||
544 | }, | 544 | }, |
545 | ], | 545 | ], |
546 | }, | 546 | }, |
@@ -582,17 +582,17 @@ mod tests { | |||
582 | RangeInfo { | 582 | RangeInfo { |
583 | range: 4..7, | 583 | range: 4..7, |
584 | info: SourceChange { | 584 | info: SourceChange { |
585 | label: "rename", | 585 | label: "Rename", |
586 | source_file_edits: [ | 586 | source_file_edits: [ |
587 | SourceFileEdit { | 587 | SourceFileEdit { |
588 | file_id: FileId( | 588 | file_id: FileId( |
589 | 1, | 589 | 1, |
590 | ), | 590 | ), |
591 | edit: TextEdit { | 591 | edit: TextEdit { |
592 | atoms: [ | 592 | indels: [ |
593 | AtomTextEdit { | 593 | Indel { |
594 | delete: 4..7, | ||
595 | insert: "foo2", | 594 | insert: "foo2", |
595 | delete: 4..7, | ||
596 | }, | 596 | }, |
597 | ], | 597 | ], |
598 | }, | 598 | }, |
@@ -665,17 +665,17 @@ mod tests { | |||
665 | RangeInfo { | 665 | RangeInfo { |
666 | range: 8..11, | 666 | range: 8..11, |
667 | info: SourceChange { | 667 | info: SourceChange { |
668 | label: "rename", | 668 | label: "Rename", |
669 | source_file_edits: [ | 669 | source_file_edits: [ |
670 | SourceFileEdit { | 670 | SourceFileEdit { |
671 | file_id: FileId( | 671 | file_id: FileId( |
672 | 2, | 672 | 2, |
673 | ), | 673 | ), |
674 | edit: TextEdit { | 674 | edit: TextEdit { |
675 | atoms: [ | 675 | indels: [ |
676 | AtomTextEdit { | 676 | Indel { |
677 | delete: 8..11, | ||
678 | insert: "foo2", | 677 | insert: "foo2", |
678 | delete: 8..11, | ||
679 | }, | 679 | }, |
680 | ], | 680 | ], |
681 | }, | 681 | }, |
@@ -685,10 +685,10 @@ mod tests { | |||
685 | 1, | 685 | 1, |
686 | ), | 686 | ), |
687 | edit: TextEdit { | 687 | edit: TextEdit { |
688 | atoms: [ | 688 | indels: [ |
689 | AtomTextEdit { | 689 | Indel { |
690 | delete: 27..30, | ||
691 | insert: "foo2", | 690 | insert: "foo2", |
691 | delete: 27..30, | ||
692 | }, | 692 | }, |
693 | ], | 693 | ], |
694 | }, | 694 | }, |
@@ -720,13 +720,13 @@ mod tests { | |||
720 | if let Some(change) = source_change { | 720 | if let Some(change) = source_change { |
721 | for edit in change.info.source_file_edits { | 721 | for edit in change.info.source_file_edits { |
722 | file_id = Some(edit.file_id); | 722 | file_id = Some(edit.file_id); |
723 | for atom in edit.edit.as_atoms() { | 723 | for indel in edit.edit.as_indels() { |
724 | text_edit_builder.replace(atom.delete, atom.insert.clone()); | 724 | text_edit_builder.replace(indel.delete, indel.insert.clone()); |
725 | } | 725 | } |
726 | } | 726 | } |
727 | } | 727 | } |
728 | let result = | 728 | let mut result = analysis.file_text(file_id.unwrap()).unwrap().to_string(); |
729 | text_edit_builder.finish().apply(&*analysis.file_text(file_id.unwrap()).unwrap()); | 729 | text_edit_builder.finish().apply(&mut result); |
730 | assert_eq_text!(expected, &*result); | 730 | assert_eq_text!(expected, &*result); |
731 | } | 731 | } |
732 | } | 732 | } |
diff --git a/crates/ra_ide/src/runnables.rs b/crates/ra_ide/src/runnables.rs index 38637c19c..fa8a9d92c 100644 --- a/crates/ra_ide/src/runnables.rs +++ b/crates/ra_ide/src/runnables.rs | |||
@@ -9,6 +9,7 @@ use ra_syntax::{ | |||
9 | }; | 9 | }; |
10 | 10 | ||
11 | use crate::FileId; | 11 | use crate::FileId; |
12 | use ast::DocCommentsOwner; | ||
12 | use std::fmt::Display; | 13 | use std::fmt::Display; |
13 | 14 | ||
14 | #[derive(Debug)] | 15 | #[derive(Debug)] |
@@ -37,6 +38,7 @@ pub enum RunnableKind { | |||
37 | Test { test_id: TestId, attr: TestAttr }, | 38 | Test { test_id: TestId, attr: TestAttr }, |
38 | TestMod { path: String }, | 39 | TestMod { path: String }, |
39 | Bench { test_id: TestId }, | 40 | Bench { test_id: TestId }, |
41 | DocTest { test_id: TestId }, | ||
40 | Bin, | 42 | Bin, |
41 | } | 43 | } |
42 | 44 | ||
@@ -81,6 +83,8 @@ fn runnable_fn(sema: &Semantics<RootDatabase>, fn_def: ast::FnDef) -> Option<Run | |||
81 | RunnableKind::Test { test_id, attr } | 83 | RunnableKind::Test { test_id, attr } |
82 | } else if fn_def.has_atom_attr("bench") { | 84 | } else if fn_def.has_atom_attr("bench") { |
83 | RunnableKind::Bench { test_id } | 85 | RunnableKind::Bench { test_id } |
86 | } else if has_doc_test(&fn_def) { | ||
87 | RunnableKind::DocTest { test_id } | ||
84 | } else { | 88 | } else { |
85 | return None; | 89 | return None; |
86 | } | 90 | } |
@@ -117,6 +121,10 @@ fn has_test_related_attribute(fn_def: &ast::FnDef) -> bool { | |||
117 | .any(|attribute_text| attribute_text.contains("test")) | 121 | .any(|attribute_text| attribute_text.contains("test")) |
118 | } | 122 | } |
119 | 123 | ||
124 | fn has_doc_test(fn_def: &ast::FnDef) -> bool { | ||
125 | fn_def.doc_comment_text().map_or(false, |comment| comment.contains("```")) | ||
126 | } | ||
127 | |||
120 | fn runnable_mod(sema: &Semantics<RootDatabase>, module: ast::Module) -> Option<Runnable> { | 128 | fn runnable_mod(sema: &Semantics<RootDatabase>, module: ast::Module) -> Option<Runnable> { |
121 | let has_test_function = module | 129 | let has_test_function = module |
122 | .item_list()? | 130 | .item_list()? |
@@ -195,6 +203,41 @@ mod tests { | |||
195 | } | 203 | } |
196 | 204 | ||
197 | #[test] | 205 | #[test] |
206 | fn test_runnables_doc_test() { | ||
207 | let (analysis, pos) = analysis_and_position( | ||
208 | r#" | ||
209 | //- /lib.rs | ||
210 | <|> //empty | ||
211 | fn main() {} | ||
212 | |||
213 | /// ``` | ||
214 | /// let x = 5; | ||
215 | /// ``` | ||
216 | fn foo() {} | ||
217 | "#, | ||
218 | ); | ||
219 | let runnables = analysis.runnables(pos.file_id).unwrap(); | ||
220 | assert_debug_snapshot!(&runnables, | ||
221 | @r###" | ||
222 | [ | ||
223 | Runnable { | ||
224 | range: 1..21, | ||
225 | kind: Bin, | ||
226 | }, | ||
227 | Runnable { | ||
228 | range: 22..64, | ||
229 | kind: DocTest { | ||
230 | test_id: Path( | ||
231 | "foo", | ||
232 | ), | ||
233 | }, | ||
234 | }, | ||
235 | ] | ||
236 | "### | ||
237 | ); | ||
238 | } | ||
239 | |||
240 | #[test] | ||
198 | fn test_runnables_module() { | 241 | fn test_runnables_module() { |
199 | let (analysis, pos) = analysis_and_position( | 242 | let (analysis, pos) = analysis_and_position( |
200 | r#" | 243 | r#" |
diff --git a/crates/ra_ide/src/source_change.rs b/crates/ra_ide/src/source_change.rs index 71b0e8f75..10afd7825 100644 --- a/crates/ra_ide/src/source_change.rs +++ b/crates/ra_ide/src/source_change.rs | |||
@@ -35,8 +35,10 @@ impl SourceChange { | |||
35 | /// Creates a new SourceChange with the given label, | 35 | /// Creates a new SourceChange with the given label, |
36 | /// containing only the given `SourceFileEdits`. | 36 | /// containing only the given `SourceFileEdits`. |
37 | pub(crate) fn source_file_edits<L: Into<String>>(label: L, edits: Vec<SourceFileEdit>) -> Self { | 37 | pub(crate) fn source_file_edits<L: Into<String>>(label: L, edits: Vec<SourceFileEdit>) -> Self { |
38 | let label = label.into(); | ||
39 | assert!(label.starts_with(char::is_uppercase)); | ||
38 | SourceChange { | 40 | SourceChange { |
39 | label: label.into(), | 41 | label: label, |
40 | source_file_edits: edits, | 42 | source_file_edits: edits, |
41 | file_system_edits: vec![], | 43 | file_system_edits: vec![], |
42 | cursor_position: None, | 44 | cursor_position: None, |
diff --git a/crates/ra_ide/src/ssr.rs b/crates/ra_ide/src/ssr.rs index 7b93ff2d2..8bf52d0fa 100644 --- a/crates/ra_ide/src/ssr.rs +++ b/crates/ra_ide/src/ssr.rs | |||
@@ -401,16 +401,22 @@ fn render_replace( | |||
401 | ignored_comments: &Vec<Comment>, | 401 | ignored_comments: &Vec<Comment>, |
402 | template: &SsrTemplate, | 402 | template: &SsrTemplate, |
403 | ) -> String { | 403 | ) -> String { |
404 | let mut builder = TextEditBuilder::default(); | 404 | let edit = { |
405 | for element in template.template.descendants() { | 405 | let mut builder = TextEditBuilder::default(); |
406 | if let Some(var) = template.placeholders.get(&element) { | 406 | for element in template.template.descendants() { |
407 | builder.replace(element.text_range(), binding[var].to_string()) | 407 | if let Some(var) = template.placeholders.get(&element) { |
408 | builder.replace(element.text_range(), binding[var].to_string()) | ||
409 | } | ||
408 | } | 410 | } |
409 | } | 411 | for comment in ignored_comments { |
410 | for comment in ignored_comments { | 412 | builder.insert(template.template.text_range().end(), comment.syntax().to_string()) |
411 | builder.insert(template.template.text_range().end(), comment.syntax().to_string()) | 413 | } |
412 | } | 414 | builder.finish() |
413 | builder.finish().apply(&template.template.text().to_string()) | 415 | }; |
416 | |||
417 | let mut text = template.template.text().to_string(); | ||
418 | edit.apply(&mut text); | ||
419 | text | ||
414 | } | 420 | } |
415 | 421 | ||
416 | #[cfg(test)] | 422 | #[cfg(test)] |
@@ -505,7 +511,9 @@ mod tests { | |||
505 | ); | 511 | ); |
506 | 512 | ||
507 | let edit = replace(&matches, &query.template); | 513 | let edit = replace(&matches, &query.template); |
508 | assert_eq!(edit.apply(input), "fn main() { bar(1+2); }"); | 514 | let mut after = input.to_string(); |
515 | edit.apply(&mut after); | ||
516 | assert_eq!(after, "fn main() { bar(1+2); }"); | ||
509 | } | 517 | } |
510 | 518 | ||
511 | fn assert_ssr_transform(query: &str, input: &str, result: &str) { | 519 | fn assert_ssr_transform(query: &str, input: &str, result: &str) { |
@@ -513,7 +521,9 @@ mod tests { | |||
513 | let code = SourceFile::parse(input).tree(); | 521 | let code = SourceFile::parse(input).tree(); |
514 | let matches = find(&query.pattern, code.syntax()); | 522 | let matches = find(&query.pattern, code.syntax()); |
515 | let edit = replace(&matches, &query.template); | 523 | let edit = replace(&matches, &query.template); |
516 | assert_eq!(edit.apply(input), result); | 524 | let mut after = input.to_string(); |
525 | edit.apply(&mut after); | ||
526 | assert_eq!(after, result); | ||
517 | } | 527 | } |
518 | 528 | ||
519 | #[test] | 529 | #[test] |
diff --git a/crates/ra_ide/src/test_utils.rs b/crates/ra_ide/src/test_utils.rs index f14533e14..48c8fd1f4 100644 --- a/crates/ra_ide/src/test_utils.rs +++ b/crates/ra_ide/src/test_utils.rs | |||
@@ -13,7 +13,11 @@ pub fn check_action<F: Fn(&SourceFile, TextSize) -> Option<TextEdit>>( | |||
13 | let (before_cursor_pos, before) = extract_offset(before); | 13 | let (before_cursor_pos, before) = extract_offset(before); |
14 | let file = SourceFile::parse(&before).ok().unwrap(); | 14 | let file = SourceFile::parse(&before).ok().unwrap(); |
15 | let result = f(&file, before_cursor_pos).expect("code action is not applicable"); | 15 | let result = f(&file, before_cursor_pos).expect("code action is not applicable"); |
16 | let actual = result.apply(&before); | 16 | let actual = { |
17 | let mut actual = before.to_string(); | ||
18 | result.apply(&mut actual); | ||
19 | actual | ||
20 | }; | ||
17 | let actual_cursor_pos = | 21 | let actual_cursor_pos = |
18 | result.apply_to_offset(before_cursor_pos).expect("cursor position is affected by the edit"); | 22 | result.apply_to_offset(before_cursor_pos).expect("cursor position is affected by the edit"); |
19 | let actual = add_cursor(&actual, actual_cursor_pos); | 23 | let actual = add_cursor(&actual, actual_cursor_pos); |
diff --git a/crates/ra_ide/src/typing.rs b/crates/ra_ide/src/typing.rs index 2a8b4327f..a03da4693 100644 --- a/crates/ra_ide/src/typing.rs +++ b/crates/ra_ide/src/typing.rs | |||
@@ -142,10 +142,13 @@ mod tests { | |||
142 | fn do_type_char(char_typed: char, before: &str) -> Option<(String, SingleFileChange)> { | 142 | fn do_type_char(char_typed: char, before: &str) -> Option<(String, SingleFileChange)> { |
143 | let (offset, before) = extract_offset(before); | 143 | let (offset, before) = extract_offset(before); |
144 | let edit = TextEdit::insert(offset, char_typed.to_string()); | 144 | let edit = TextEdit::insert(offset, char_typed.to_string()); |
145 | let before = edit.apply(&before); | 145 | let mut before = before.to_string(); |
146 | edit.apply(&mut before); | ||
146 | let parse = SourceFile::parse(&before); | 147 | let parse = SourceFile::parse(&before); |
147 | on_char_typed_inner(&parse.tree(), offset, char_typed) | 148 | on_char_typed_inner(&parse.tree(), offset, char_typed).map(|it| { |
148 | .map(|it| (it.edit.apply(&before), it)) | 149 | it.edit.apply(&mut before); |
150 | (before.to_string(), it) | ||
151 | }) | ||
149 | } | 152 | } |
150 | 153 | ||
151 | fn type_char(char_typed: char, before: &str, after: &str) { | 154 | fn type_char(char_typed: char, before: &str, after: &str) { |
diff --git a/crates/ra_ide/src/typing/on_enter.rs b/crates/ra_ide/src/typing/on_enter.rs index 30c8c5572..78a40cc94 100644 --- a/crates/ra_ide/src/typing/on_enter.rs +++ b/crates/ra_ide/src/typing/on_enter.rs | |||
@@ -44,7 +44,7 @@ pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<Sour | |||
44 | 44 | ||
45 | Some( | 45 | Some( |
46 | SourceChange::source_file_edit( | 46 | SourceChange::source_file_edit( |
47 | "on enter", | 47 | "On enter", |
48 | SourceFileEdit { edit, file_id: position.file_id }, | 48 | SourceFileEdit { edit, file_id: position.file_id }, |
49 | ) | 49 | ) |
50 | .with_cursor(FilePosition { offset: cursor_position, file_id: position.file_id }), | 50 | .with_cursor(FilePosition { offset: cursor_position, file_id: position.file_id }), |
@@ -96,7 +96,8 @@ mod tests { | |||
96 | let result = analysis.on_enter(FilePosition { offset, file_id }).unwrap()?; | 96 | let result = analysis.on_enter(FilePosition { offset, file_id }).unwrap()?; |
97 | 97 | ||
98 | assert_eq!(result.source_file_edits.len(), 1); | 98 | assert_eq!(result.source_file_edits.len(), 1); |
99 | let actual = result.source_file_edits[0].edit.apply(&before); | 99 | let mut actual = before.to_string(); |
100 | result.source_file_edits[0].edit.apply(&mut actual); | ||
100 | let actual = add_cursor(&actual, result.cursor_position.unwrap().offset); | 101 | let actual = add_cursor(&actual, result.cursor_position.unwrap().offset); |
101 | Some(actual) | 102 | Some(actual) |
102 | } | 103 | } |
diff --git a/crates/ra_ide_db/src/line_index.rs b/crates/ra_ide_db/src/line_index.rs index 212cb7b5b..c7c744fce 100644 --- a/crates/ra_ide_db/src/line_index.rs +++ b/crates/ra_ide_db/src/line_index.rs | |||
@@ -31,9 +31,19 @@ pub(crate) struct Utf16Char { | |||
31 | } | 31 | } |
32 | 32 | ||
33 | impl Utf16Char { | 33 | impl Utf16Char { |
34 | /// Returns the length in 8-bit UTF-8 code units. | ||
34 | fn len(&self) -> TextSize { | 35 | fn len(&self) -> TextSize { |
35 | self.end - self.start | 36 | self.end - self.start |
36 | } | 37 | } |
38 | |||
39 | /// Returns the length in 16-bit UTF-16 code units. | ||
40 | fn len_utf16(&self) -> usize { | ||
41 | if self.len() == TextSize::from(4) { | ||
42 | 2 | ||
43 | } else { | ||
44 | 1 | ||
45 | } | ||
46 | } | ||
37 | } | 47 | } |
38 | 48 | ||
39 | impl LineIndex { | 49 | impl LineIndex { |
@@ -110,7 +120,7 @@ impl LineIndex { | |||
110 | if let Some(utf16_chars) = self.utf16_lines.get(&line) { | 120 | if let Some(utf16_chars) = self.utf16_lines.get(&line) { |
111 | for c in utf16_chars { | 121 | for c in utf16_chars { |
112 | if c.end <= col { | 122 | if c.end <= col { |
113 | res -= usize::from(c.len()) - 1; | 123 | res -= usize::from(c.len()) - c.len_utf16(); |
114 | } else { | 124 | } else { |
115 | // From here on, all utf16 characters come *after* the character we are mapping, | 125 | // From here on, all utf16 characters come *after* the character we are mapping, |
116 | // so we don't need to take them into account | 126 | // so we don't need to take them into account |
@@ -125,7 +135,7 @@ impl LineIndex { | |||
125 | if let Some(utf16_chars) = self.utf16_lines.get(&line) { | 135 | if let Some(utf16_chars) = self.utf16_lines.get(&line) { |
126 | for c in utf16_chars { | 136 | for c in utf16_chars { |
127 | if col > u32::from(c.start) { | 137 | if col > u32::from(c.start) { |
128 | col += u32::from(c.len()) - 1; | 138 | col += u32::from(c.len()) - c.len_utf16() as u32; |
129 | } else { | 139 | } else { |
130 | // From here on, all utf16 characters come *after* the character we are mapping, | 140 | // From here on, all utf16 characters come *after* the character we are mapping, |
131 | // so we don't need to take them into account | 141 | // so we don't need to take them into account |
@@ -204,6 +214,9 @@ const C: char = 'メ'; | |||
204 | 214 | ||
205 | // UTF-16 to UTF-8 | 215 | // UTF-16 to UTF-8 |
206 | assert_eq!(col_index.utf16_to_utf8_col(1, 19), TextSize::from(21)); | 216 | assert_eq!(col_index.utf16_to_utf8_col(1, 19), TextSize::from(21)); |
217 | |||
218 | let col_index = LineIndex::new("a𐐏b"); | ||
219 | assert_eq!(col_index.utf16_to_utf8_col(0, 3), TextSize::from(5)); | ||
207 | } | 220 | } |
208 | 221 | ||
209 | #[test] | 222 | #[test] |
diff --git a/crates/ra_ide_db/src/line_index_utils.rs b/crates/ra_ide_db/src/line_index_utils.rs index 039a12c0d..7fa6fc448 100644 --- a/crates/ra_ide_db/src/line_index_utils.rs +++ b/crates/ra_ide_db/src/line_index_utils.rs | |||
@@ -10,7 +10,7 @@ | |||
10 | use std::convert::TryInto; | 10 | use std::convert::TryInto; |
11 | 11 | ||
12 | use ra_syntax::{TextRange, TextSize}; | 12 | use ra_syntax::{TextRange, TextSize}; |
13 | use ra_text_edit::{AtomTextEdit, TextEdit}; | 13 | use ra_text_edit::{Indel, TextEdit}; |
14 | 14 | ||
15 | use crate::line_index::{LineCol, LineIndex, Utf16Char}; | 15 | use crate::line_index::{LineCol, LineIndex, Utf16Char}; |
16 | 16 | ||
@@ -182,14 +182,14 @@ struct TranslatedEdit<'a> { | |||
182 | } | 182 | } |
183 | 183 | ||
184 | struct Edits<'a> { | 184 | struct Edits<'a> { |
185 | edits: &'a [AtomTextEdit], | 185 | edits: &'a [Indel], |
186 | current: Option<TranslatedEdit<'a>>, | 186 | current: Option<TranslatedEdit<'a>>, |
187 | acc_diff: i64, | 187 | acc_diff: i64, |
188 | } | 188 | } |
189 | 189 | ||
190 | impl<'a> Edits<'a> { | 190 | impl<'a> Edits<'a> { |
191 | fn from_text_edit(text_edit: &'a TextEdit) -> Edits<'a> { | 191 | fn from_text_edit(text_edit: &'a TextEdit) -> Edits<'a> { |
192 | let mut x = Edits { edits: text_edit.as_atoms(), current: None, acc_diff: 0 }; | 192 | let mut x = Edits { edits: text_edit.as_indels(), current: None, acc_diff: 0 }; |
193 | x.advance_edit(); | 193 | x.advance_edit(); |
194 | x | 194 | x |
195 | } | 195 | } |
diff --git a/crates/ra_mbe/src/syntax_bridge.rs b/crates/ra_mbe/src/syntax_bridge.rs index bb28acfd9..fc4133a67 100644 --- a/crates/ra_mbe/src/syntax_bridge.rs +++ b/crates/ra_mbe/src/syntax_bridge.rs | |||
@@ -63,7 +63,7 @@ pub fn syntax_node_to_token_tree(node: &SyntaxNode) -> Option<(tt::Subtree, Toke | |||
63 | // * Items(SmallVec<[P<ast::Item>; 1]>) -> token_tree_to_items | 63 | // * Items(SmallVec<[P<ast::Item>; 1]>) -> token_tree_to_items |
64 | // | 64 | // |
65 | // * TraitItems(SmallVec<[ast::TraitItem; 1]>) | 65 | // * TraitItems(SmallVec<[ast::TraitItem; 1]>) |
66 | // * ImplItems(SmallVec<[ast::ImplItem; 1]>) | 66 | // * AssocItems(SmallVec<[ast::AssocItem; 1]>) |
67 | // * ForeignItems(SmallVec<[ast::ForeignItem; 1]> | 67 | // * ForeignItems(SmallVec<[ast::ForeignItem; 1]> |
68 | 68 | ||
69 | pub fn token_tree_to_syntax_node( | 69 | pub fn token_tree_to_syntax_node( |
diff --git a/crates/ra_project_model/src/cargo_workspace.rs b/crates/ra_project_model/src/cargo_workspace.rs index 362ee30fe..59f46a2a0 100644 --- a/crates/ra_project_model/src/cargo_workspace.rs +++ b/crates/ra_project_model/src/cargo_workspace.rs | |||
@@ -56,6 +56,9 @@ pub struct CargoConfig { | |||
56 | 56 | ||
57 | /// Runs cargo check on launch to figure out the correct values of OUT_DIR | 57 | /// Runs cargo check on launch to figure out the correct values of OUT_DIR |
58 | pub load_out_dirs_from_check: bool, | 58 | pub load_out_dirs_from_check: bool, |
59 | |||
60 | /// rustc target | ||
61 | pub target: Option<String>, | ||
59 | } | 62 | } |
60 | 63 | ||
61 | impl Default for CargoConfig { | 64 | impl Default for CargoConfig { |
@@ -65,6 +68,7 @@ impl Default for CargoConfig { | |||
65 | all_features: true, | 68 | all_features: true, |
66 | features: Vec::new(), | 69 | features: Vec::new(), |
67 | load_out_dirs_from_check: false, | 70 | load_out_dirs_from_check: false, |
71 | target: None, | ||
68 | } | 72 | } |
69 | } | 73 | } |
70 | } | 74 | } |
@@ -160,6 +164,9 @@ impl CargoWorkspace { | |||
160 | if let Some(parent) = cargo_toml.parent() { | 164 | if let Some(parent) = cargo_toml.parent() { |
161 | meta.current_dir(parent); | 165 | meta.current_dir(parent); |
162 | } | 166 | } |
167 | if let Some(target) = cargo_features.target.as_ref() { | ||
168 | meta.other_options(&[String::from("--filter-platform"), target.clone()]); | ||
169 | } | ||
163 | let meta = meta.exec().with_context(|| { | 170 | let meta = meta.exec().with_context(|| { |
164 | format!("Failed to run `cargo metadata --manifest-path {}`", cargo_toml.display()) | 171 | format!("Failed to run `cargo metadata --manifest-path {}`", cargo_toml.display()) |
165 | })?; | 172 | })?; |
diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs index 731cbd291..c2b33c1dc 100644 --- a/crates/ra_project_model/src/lib.rs +++ b/crates/ra_project_model/src/lib.rs | |||
@@ -543,7 +543,7 @@ impl ProjectWorkspace { | |||
543 | } | 543 | } |
544 | } | 544 | } |
545 | 545 | ||
546 | pub fn get_rustc_cfg_options() -> CfgOptions { | 546 | pub fn get_rustc_cfg_options(target: Option<&String>) -> CfgOptions { |
547 | let mut cfg_options = CfgOptions::default(); | 547 | let mut cfg_options = CfgOptions::default(); |
548 | 548 | ||
549 | // Some nightly-only cfgs, which are required for stdlib | 549 | // Some nightly-only cfgs, which are required for stdlib |
@@ -558,10 +558,12 @@ pub fn get_rustc_cfg_options() -> CfgOptions { | |||
558 | 558 | ||
559 | match (|| -> Result<String> { | 559 | match (|| -> Result<String> { |
560 | // `cfg(test)` and `cfg(debug_assertion)` are handled outside, so we suppress them here. | 560 | // `cfg(test)` and `cfg(debug_assertion)` are handled outside, so we suppress them here. |
561 | let output = Command::new("rustc") | 561 | let mut cmd = Command::new("rustc"); |
562 | .args(&["--print", "cfg", "-O"]) | 562 | cmd.args(&["--print", "cfg", "-O"]); |
563 | .output() | 563 | if let Some(target) = target { |
564 | .context("Failed to get output from rustc --print cfg -O")?; | 564 | cmd.args(&["--target", target.as_str()]); |
565 | } | ||
566 | let output = cmd.output().context("Failed to get output from rustc --print cfg -O")?; | ||
565 | if !output.status.success() { | 567 | if !output.status.success() { |
566 | bail!( | 568 | bail!( |
567 | "rustc --print cfg -O exited with exit code ({})", | 569 | "rustc --print cfg -O exited with exit code ({})", |
diff --git a/crates/ra_syntax/src/ast/edit.rs b/crates/ra_syntax/src/ast/edit.rs index c507dc683..3e6dd6061 100644 --- a/crates/ra_syntax/src/ast/edit.rs +++ b/crates/ra_syntax/src/ast/edit.rs | |||
@@ -79,7 +79,7 @@ where | |||
79 | 79 | ||
80 | impl ast::ItemList { | 80 | impl ast::ItemList { |
81 | #[must_use] | 81 | #[must_use] |
82 | pub fn append_items(&self, items: impl IntoIterator<Item = ast::ImplItem>) -> ast::ItemList { | 82 | pub fn append_items(&self, items: impl IntoIterator<Item = ast::AssocItem>) -> ast::ItemList { |
83 | let mut res = self.clone(); | 83 | let mut res = self.clone(); |
84 | if !self.syntax().text().contains_char('\n') { | 84 | if !self.syntax().text().contains_char('\n') { |
85 | res = make_multiline(res); | 85 | res = make_multiline(res); |
@@ -89,8 +89,8 @@ impl ast::ItemList { | |||
89 | } | 89 | } |
90 | 90 | ||
91 | #[must_use] | 91 | #[must_use] |
92 | pub fn append_item(&self, item: ast::ImplItem) -> ast::ItemList { | 92 | pub fn append_item(&self, item: ast::AssocItem) -> ast::ItemList { |
93 | let (indent, position) = match self.impl_items().last() { | 93 | let (indent, position) = match self.assoc_items().last() { |
94 | Some(it) => ( | 94 | Some(it) => ( |
95 | leading_indent(it.syntax()).unwrap_or_default().to_string(), | 95 | leading_indent(it.syntax()).unwrap_or_default().to_string(), |
96 | InsertPosition::After(it.syntax().clone().into()), | 96 | InsertPosition::After(it.syntax().clone().into()), |
diff --git a/crates/ra_syntax/src/ast/generated/nodes.rs b/crates/ra_syntax/src/ast/generated/nodes.rs index c2cc25958..b00c15608 100644 --- a/crates/ra_syntax/src/ast/generated/nodes.rs +++ b/crates/ra_syntax/src/ast/generated/nodes.rs | |||
@@ -196,7 +196,7 @@ pub struct ItemList { | |||
196 | impl ast::ModuleItemOwner for ItemList {} | 196 | impl ast::ModuleItemOwner for ItemList {} |
197 | impl ItemList { | 197 | impl ItemList { |
198 | pub fn l_curly_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['{']) } | 198 | pub fn l_curly_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['{']) } |
199 | pub fn impl_items(&self) -> AstChildren<ImplItem> { support::children(&self.syntax) } | 199 | pub fn assoc_items(&self) -> AstChildren<AssocItem> { support::children(&self.syntax) } |
200 | pub fn r_curly_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['}']) } | 200 | pub fn r_curly_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['}']) } |
201 | } | 201 | } |
202 | 202 | ||
@@ -1429,13 +1429,13 @@ impl ast::AttrsOwner for ModuleItem {} | |||
1429 | impl ast::VisibilityOwner for ModuleItem {} | 1429 | impl ast::VisibilityOwner for ModuleItem {} |
1430 | 1430 | ||
1431 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 1431 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
1432 | pub enum ImplItem { | 1432 | pub enum AssocItem { |
1433 | FnDef(FnDef), | 1433 | FnDef(FnDef), |
1434 | TypeAliasDef(TypeAliasDef), | 1434 | TypeAliasDef(TypeAliasDef), |
1435 | ConstDef(ConstDef), | 1435 | ConstDef(ConstDef), |
1436 | } | 1436 | } |
1437 | impl ast::NameOwner for ImplItem {} | 1437 | impl ast::NameOwner for AssocItem {} |
1438 | impl ast::AttrsOwner for ImplItem {} | 1438 | impl ast::AttrsOwner for AssocItem {} |
1439 | 1439 | ||
1440 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 1440 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
1441 | pub enum ExternItem { | 1441 | pub enum ExternItem { |
@@ -3167,16 +3167,16 @@ impl AstNode for ModuleItem { | |||
3167 | } | 3167 | } |
3168 | } | 3168 | } |
3169 | } | 3169 | } |
3170 | impl From<FnDef> for ImplItem { | 3170 | impl From<FnDef> for AssocItem { |
3171 | fn from(node: FnDef) -> ImplItem { ImplItem::FnDef(node) } | 3171 | fn from(node: FnDef) -> AssocItem { AssocItem::FnDef(node) } |
3172 | } | 3172 | } |
3173 | impl From<TypeAliasDef> for ImplItem { | 3173 | impl From<TypeAliasDef> for AssocItem { |
3174 | fn from(node: TypeAliasDef) -> ImplItem { ImplItem::TypeAliasDef(node) } | 3174 | fn from(node: TypeAliasDef) -> AssocItem { AssocItem::TypeAliasDef(node) } |
3175 | } | 3175 | } |
3176 | impl From<ConstDef> for ImplItem { | 3176 | impl From<ConstDef> for AssocItem { |
3177 | fn from(node: ConstDef) -> ImplItem { ImplItem::ConstDef(node) } | 3177 | fn from(node: ConstDef) -> AssocItem { AssocItem::ConstDef(node) } |
3178 | } | 3178 | } |
3179 | impl AstNode for ImplItem { | 3179 | impl AstNode for AssocItem { |
3180 | fn can_cast(kind: SyntaxKind) -> bool { | 3180 | fn can_cast(kind: SyntaxKind) -> bool { |
3181 | match kind { | 3181 | match kind { |
3182 | FN_DEF | TYPE_ALIAS_DEF | CONST_DEF => true, | 3182 | FN_DEF | TYPE_ALIAS_DEF | CONST_DEF => true, |
@@ -3185,18 +3185,18 @@ impl AstNode for ImplItem { | |||
3185 | } | 3185 | } |
3186 | fn cast(syntax: SyntaxNode) -> Option<Self> { | 3186 | fn cast(syntax: SyntaxNode) -> Option<Self> { |
3187 | let res = match syntax.kind() { | 3187 | let res = match syntax.kind() { |
3188 | FN_DEF => ImplItem::FnDef(FnDef { syntax }), | 3188 | FN_DEF => AssocItem::FnDef(FnDef { syntax }), |
3189 | TYPE_ALIAS_DEF => ImplItem::TypeAliasDef(TypeAliasDef { syntax }), | 3189 | TYPE_ALIAS_DEF => AssocItem::TypeAliasDef(TypeAliasDef { syntax }), |
3190 | CONST_DEF => ImplItem::ConstDef(ConstDef { syntax }), | 3190 | CONST_DEF => AssocItem::ConstDef(ConstDef { syntax }), |
3191 | _ => return None, | 3191 | _ => return None, |
3192 | }; | 3192 | }; |
3193 | Some(res) | 3193 | Some(res) |
3194 | } | 3194 | } |
3195 | fn syntax(&self) -> &SyntaxNode { | 3195 | fn syntax(&self) -> &SyntaxNode { |
3196 | match self { | 3196 | match self { |
3197 | ImplItem::FnDef(it) => &it.syntax, | 3197 | AssocItem::FnDef(it) => &it.syntax, |
3198 | ImplItem::TypeAliasDef(it) => &it.syntax, | 3198 | AssocItem::TypeAliasDef(it) => &it.syntax, |
3199 | ImplItem::ConstDef(it) => &it.syntax, | 3199 | AssocItem::ConstDef(it) => &it.syntax, |
3200 | } | 3200 | } |
3201 | } | 3201 | } |
3202 | } | 3202 | } |
@@ -3641,7 +3641,7 @@ impl std::fmt::Display for ModuleItem { | |||
3641 | std::fmt::Display::fmt(self.syntax(), f) | 3641 | std::fmt::Display::fmt(self.syntax(), f) |
3642 | } | 3642 | } |
3643 | } | 3643 | } |
3644 | impl std::fmt::Display for ImplItem { | 3644 | impl std::fmt::Display for AssocItem { |
3645 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { | 3645 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { |
3646 | std::fmt::Display::fmt(self.syntax(), f) | 3646 | std::fmt::Display::fmt(self.syntax(), f) |
3647 | } | 3647 | } |
diff --git a/crates/ra_syntax/src/fuzz.rs b/crates/ra_syntax/src/fuzz.rs index 10fbe3176..39f9b12ab 100644 --- a/crates/ra_syntax/src/fuzz.rs +++ b/crates/ra_syntax/src/fuzz.rs | |||
@@ -5,7 +5,7 @@ use std::{ | |||
5 | str::{self, FromStr}, | 5 | str::{self, FromStr}, |
6 | }; | 6 | }; |
7 | 7 | ||
8 | use ra_text_edit::AtomTextEdit; | 8 | use ra_text_edit::Indel; |
9 | 9 | ||
10 | use crate::{validation, AstNode, SourceFile, TextRange}; | 10 | use crate::{validation, AstNode, SourceFile, TextRange}; |
11 | 11 | ||
@@ -22,7 +22,7 @@ pub fn check_parser(text: &str) { | |||
22 | #[derive(Debug, Clone)] | 22 | #[derive(Debug, Clone)] |
23 | pub struct CheckReparse { | 23 | pub struct CheckReparse { |
24 | text: String, | 24 | text: String, |
25 | edit: AtomTextEdit, | 25 | edit: Indel, |
26 | edited_text: String, | 26 | edited_text: String, |
27 | } | 27 | } |
28 | 28 | ||
@@ -43,7 +43,7 @@ impl CheckReparse { | |||
43 | TextRange::at(delete_start.try_into().unwrap(), delete_len.try_into().unwrap()); | 43 | TextRange::at(delete_start.try_into().unwrap(), delete_len.try_into().unwrap()); |
44 | let edited_text = | 44 | let edited_text = |
45 | format!("{}{}{}", &text[..delete_start], &insert, &text[delete_start + delete_len..]); | 45 | format!("{}{}{}", &text[..delete_start], &insert, &text[delete_start + delete_len..]); |
46 | let edit = AtomTextEdit { delete, insert }; | 46 | let edit = Indel { delete, insert }; |
47 | Some(CheckReparse { text, edit, edited_text }) | 47 | Some(CheckReparse { text, edit, edited_text }) |
48 | } | 48 | } |
49 | 49 | ||
diff --git a/crates/ra_syntax/src/lib.rs b/crates/ra_syntax/src/lib.rs index d0234cada..61e686da5 100644 --- a/crates/ra_syntax/src/lib.rs +++ b/crates/ra_syntax/src/lib.rs | |||
@@ -39,7 +39,7 @@ pub mod fuzz; | |||
39 | 39 | ||
40 | use std::{marker::PhantomData, sync::Arc}; | 40 | use std::{marker::PhantomData, sync::Arc}; |
41 | 41 | ||
42 | use ra_text_edit::AtomTextEdit; | 42 | use ra_text_edit::Indel; |
43 | use stdx::format_to; | 43 | use stdx::format_to; |
44 | 44 | ||
45 | use crate::syntax_node::GreenNode; | 45 | use crate::syntax_node::GreenNode; |
@@ -126,13 +126,13 @@ impl Parse<SourceFile> { | |||
126 | buf | 126 | buf |
127 | } | 127 | } |
128 | 128 | ||
129 | pub fn reparse(&self, edit: &AtomTextEdit) -> Parse<SourceFile> { | 129 | pub fn reparse(&self, indel: &Indel) -> Parse<SourceFile> { |
130 | self.incremental_reparse(edit).unwrap_or_else(|| self.full_reparse(edit)) | 130 | self.incremental_reparse(indel).unwrap_or_else(|| self.full_reparse(indel)) |
131 | } | 131 | } |
132 | 132 | ||
133 | fn incremental_reparse(&self, edit: &AtomTextEdit) -> Option<Parse<SourceFile>> { | 133 | fn incremental_reparse(&self, indel: &Indel) -> Option<Parse<SourceFile>> { |
134 | // FIXME: validation errors are not handled here | 134 | // FIXME: validation errors are not handled here |
135 | parsing::incremental_reparse(self.tree().syntax(), edit, self.errors.to_vec()).map( | 135 | parsing::incremental_reparse(self.tree().syntax(), indel, self.errors.to_vec()).map( |
136 | |(green_node, errors, _reparsed_range)| Parse { | 136 | |(green_node, errors, _reparsed_range)| Parse { |
137 | green: green_node, | 137 | green: green_node, |
138 | errors: Arc::new(errors), | 138 | errors: Arc::new(errors), |
@@ -141,8 +141,9 @@ impl Parse<SourceFile> { | |||
141 | ) | 141 | ) |
142 | } | 142 | } |
143 | 143 | ||
144 | fn full_reparse(&self, edit: &AtomTextEdit) -> Parse<SourceFile> { | 144 | fn full_reparse(&self, indel: &Indel) -> Parse<SourceFile> { |
145 | let text = edit.apply(self.tree().syntax().text().to_string()); | 145 | let mut text = self.tree().syntax().text().to_string(); |
146 | indel.apply(&mut text); | ||
146 | SourceFile::parse(&text) | 147 | SourceFile::parse(&text) |
147 | } | 148 | } |
148 | } | 149 | } |
diff --git a/crates/ra_syntax/src/parsing/reparsing.rs b/crates/ra_syntax/src/parsing/reparsing.rs index ffff0a7b2..edbc190f8 100644 --- a/crates/ra_syntax/src/parsing/reparsing.rs +++ b/crates/ra_syntax/src/parsing/reparsing.rs | |||
@@ -7,7 +7,7 @@ | |||
7 | //! and try to parse only this block. | 7 | //! and try to parse only this block. |
8 | 8 | ||
9 | use ra_parser::Reparser; | 9 | use ra_parser::Reparser; |
10 | use ra_text_edit::AtomTextEdit; | 10 | use ra_text_edit::Indel; |
11 | 11 | ||
12 | use crate::{ | 12 | use crate::{ |
13 | algo, | 13 | algo, |
@@ -24,7 +24,7 @@ use crate::{ | |||
24 | 24 | ||
25 | pub(crate) fn incremental_reparse( | 25 | pub(crate) fn incremental_reparse( |
26 | node: &SyntaxNode, | 26 | node: &SyntaxNode, |
27 | edit: &AtomTextEdit, | 27 | edit: &Indel, |
28 | errors: Vec<SyntaxError>, | 28 | errors: Vec<SyntaxError>, |
29 | ) -> Option<(GreenNode, Vec<SyntaxError>, TextRange)> { | 29 | ) -> Option<(GreenNode, Vec<SyntaxError>, TextRange)> { |
30 | if let Some((green, new_errors, old_range)) = reparse_token(node, &edit) { | 30 | if let Some((green, new_errors, old_range)) = reparse_token(node, &edit) { |
@@ -39,7 +39,7 @@ pub(crate) fn incremental_reparse( | |||
39 | 39 | ||
40 | fn reparse_token<'node>( | 40 | fn reparse_token<'node>( |
41 | root: &'node SyntaxNode, | 41 | root: &'node SyntaxNode, |
42 | edit: &AtomTextEdit, | 42 | edit: &Indel, |
43 | ) -> Option<(GreenNode, Vec<SyntaxError>, TextRange)> { | 43 | ) -> Option<(GreenNode, Vec<SyntaxError>, TextRange)> { |
44 | let prev_token = algo::find_covering_element(root, edit.delete).as_token()?.clone(); | 44 | let prev_token = algo::find_covering_element(root, edit.delete).as_token()?.clone(); |
45 | let prev_token_kind = prev_token.kind(); | 45 | let prev_token_kind = prev_token.kind(); |
@@ -88,7 +88,7 @@ fn reparse_token<'node>( | |||
88 | 88 | ||
89 | fn reparse_block<'node>( | 89 | fn reparse_block<'node>( |
90 | root: &'node SyntaxNode, | 90 | root: &'node SyntaxNode, |
91 | edit: &AtomTextEdit, | 91 | edit: &Indel, |
92 | ) -> Option<(GreenNode, Vec<SyntaxError>, TextRange)> { | 92 | ) -> Option<(GreenNode, Vec<SyntaxError>, TextRange)> { |
93 | let (node, reparser) = find_reparsable_node(root, edit.delete)?; | 93 | let (node, reparser) = find_reparsable_node(root, edit.delete)?; |
94 | let text = get_text_after_edit(node.clone().into(), edit); | 94 | let text = get_text_after_edit(node.clone().into(), edit); |
@@ -108,15 +108,15 @@ fn reparse_block<'node>( | |||
108 | Some((node.replace_with(green), new_parser_errors, node.text_range())) | 108 | Some((node.replace_with(green), new_parser_errors, node.text_range())) |
109 | } | 109 | } |
110 | 110 | ||
111 | fn get_text_after_edit(element: SyntaxElement, edit: &AtomTextEdit) -> String { | 111 | fn get_text_after_edit(element: SyntaxElement, edit: &Indel) -> String { |
112 | let edit = | 112 | let edit = Indel::replace(edit.delete - element.text_range().start(), edit.insert.clone()); |
113 | AtomTextEdit::replace(edit.delete - element.text_range().start(), edit.insert.clone()); | ||
114 | 113 | ||
115 | let text = match element { | 114 | let mut text = match element { |
116 | NodeOrToken::Token(token) => token.text().to_string(), | 115 | NodeOrToken::Token(token) => token.text().to_string(), |
117 | NodeOrToken::Node(node) => node.text().to_string(), | 116 | NodeOrToken::Node(node) => node.text().to_string(), |
118 | }; | 117 | }; |
119 | edit.apply(text) | 118 | edit.apply(&mut text); |
119 | text | ||
120 | } | 120 | } |
121 | 121 | ||
122 | fn is_contextual_kw(text: &str) -> bool { | 122 | fn is_contextual_kw(text: &str) -> bool { |
@@ -167,7 +167,7 @@ fn merge_errors( | |||
167 | old_errors: Vec<SyntaxError>, | 167 | old_errors: Vec<SyntaxError>, |
168 | new_errors: Vec<SyntaxError>, | 168 | new_errors: Vec<SyntaxError>, |
169 | range_before_reparse: TextRange, | 169 | range_before_reparse: TextRange, |
170 | edit: &AtomTextEdit, | 170 | edit: &Indel, |
171 | ) -> Vec<SyntaxError> { | 171 | ) -> Vec<SyntaxError> { |
172 | let mut res = Vec::new(); | 172 | let mut res = Vec::new(); |
173 | 173 | ||
@@ -198,8 +198,12 @@ mod tests { | |||
198 | 198 | ||
199 | fn do_check(before: &str, replace_with: &str, reparsed_len: u32) { | 199 | fn do_check(before: &str, replace_with: &str, reparsed_len: u32) { |
200 | let (range, before) = extract_range(before); | 200 | let (range, before) = extract_range(before); |
201 | let edit = AtomTextEdit::replace(range, replace_with.to_owned()); | 201 | let edit = Indel::replace(range, replace_with.to_owned()); |
202 | let after = edit.apply(before.clone()); | 202 | let after = { |
203 | let mut after = before.clone(); | ||
204 | edit.apply(&mut after); | ||
205 | after | ||
206 | }; | ||
203 | 207 | ||
204 | let fully_reparsed = SourceFile::parse(&after); | 208 | let fully_reparsed = SourceFile::parse(&after); |
205 | let incrementally_reparsed: Parse<SourceFile> = { | 209 | let incrementally_reparsed: Parse<SourceFile> = { |
diff --git a/crates/ra_text_edit/src/lib.rs b/crates/ra_text_edit/src/lib.rs index e656260c7..7138bbc65 100644 --- a/crates/ra_text_edit/src/lib.rs +++ b/crates/ra_text_edit/src/lib.rs | |||
@@ -1,36 +1,144 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! Representation of a `TextEdit`. |
2 | 2 | //! | |
3 | mod text_edit; | 3 | //! `rust-analyzer` never mutates text itself and only sends diffs to clients, |
4 | //! so `TextEdit` is the ultimate representation of the work done by | ||
5 | //! rust-analyzer. | ||
4 | 6 | ||
5 | use text_size::{TextRange, TextSize}; | 7 | use text_size::{TextRange, TextSize}; |
6 | 8 | ||
7 | pub use crate::text_edit::{TextEdit, TextEditBuilder}; | 9 | /// `InsertDelete` -- a single "atomic" change to text |
8 | 10 | /// | |
9 | /// Must not overlap with other `AtomTextEdit`s | 11 | /// Must not overlap with other `InDel`s |
10 | #[derive(Debug, Clone)] | 12 | #[derive(Debug, Clone)] |
11 | pub struct AtomTextEdit { | 13 | pub struct Indel { |
14 | pub insert: String, | ||
12 | /// Refers to offsets in the original text | 15 | /// Refers to offsets in the original text |
13 | pub delete: TextRange, | 16 | pub delete: TextRange, |
14 | pub insert: String, | ||
15 | } | 17 | } |
16 | 18 | ||
17 | impl AtomTextEdit { | 19 | #[derive(Debug, Clone)] |
18 | pub fn replace(range: TextRange, replace_with: String) -> AtomTextEdit { | 20 | pub struct TextEdit { |
19 | AtomTextEdit { delete: range, insert: replace_with } | 21 | indels: Vec<Indel>, |
20 | } | 22 | } |
21 | 23 | ||
22 | pub fn delete(range: TextRange) -> AtomTextEdit { | 24 | #[derive(Debug, Default)] |
23 | AtomTextEdit::replace(range, String::new()) | 25 | pub struct TextEditBuilder { |
24 | } | 26 | indels: Vec<Indel>, |
27 | } | ||
25 | 28 | ||
26 | pub fn insert(offset: TextSize, text: String) -> AtomTextEdit { | 29 | impl Indel { |
27 | AtomTextEdit::replace(TextRange::empty(offset), text) | 30 | pub fn insert(offset: TextSize, text: String) -> Indel { |
31 | Indel::replace(TextRange::empty(offset), text) | ||
32 | } | ||
33 | pub fn delete(range: TextRange) -> Indel { | ||
34 | Indel::replace(range, String::new()) | ||
35 | } | ||
36 | pub fn replace(range: TextRange, replace_with: String) -> Indel { | ||
37 | Indel { delete: range, insert: replace_with } | ||
28 | } | 38 | } |
29 | 39 | ||
30 | pub fn apply(&self, mut text: String) -> String { | 40 | pub fn apply(&self, text: &mut String) { |
31 | let start: usize = self.delete.start().into(); | 41 | let start: usize = self.delete.start().into(); |
32 | let end: usize = self.delete.end().into(); | 42 | let end: usize = self.delete.end().into(); |
33 | text.replace_range(start..end, &self.insert); | 43 | text.replace_range(start..end, &self.insert); |
34 | text | 44 | } |
45 | } | ||
46 | |||
47 | impl TextEdit { | ||
48 | pub fn insert(offset: TextSize, text: String) -> TextEdit { | ||
49 | let mut builder = TextEditBuilder::default(); | ||
50 | builder.insert(offset, text); | ||
51 | builder.finish() | ||
52 | } | ||
53 | |||
54 | pub fn delete(range: TextRange) -> TextEdit { | ||
55 | let mut builder = TextEditBuilder::default(); | ||
56 | builder.delete(range); | ||
57 | builder.finish() | ||
58 | } | ||
59 | |||
60 | pub fn replace(range: TextRange, replace_with: String) -> TextEdit { | ||
61 | let mut builder = TextEditBuilder::default(); | ||
62 | builder.replace(range, replace_with); | ||
63 | builder.finish() | ||
64 | } | ||
65 | |||
66 | pub(crate) fn from_indels(mut indels: Vec<Indel>) -> TextEdit { | ||
67 | indels.sort_by_key(|a| (a.delete.start(), a.delete.end())); | ||
68 | for (a1, a2) in indels.iter().zip(indels.iter().skip(1)) { | ||
69 | assert!(a1.delete.end() <= a2.delete.start()) | ||
70 | } | ||
71 | TextEdit { indels } | ||
72 | } | ||
73 | |||
74 | pub fn as_indels(&self) -> &[Indel] { | ||
75 | &self.indels | ||
76 | } | ||
77 | |||
78 | pub fn apply(&self, text: &mut String) { | ||
79 | match self.indels.len() { | ||
80 | 0 => return, | ||
81 | 1 => { | ||
82 | self.indels[0].apply(text); | ||
83 | return; | ||
84 | } | ||
85 | _ => (), | ||
86 | } | ||
87 | |||
88 | let mut total_len = TextSize::of(&*text); | ||
89 | for indel in self.indels.iter() { | ||
90 | total_len += TextSize::of(&indel.insert); | ||
91 | total_len -= indel.delete.end() - indel.delete.start(); | ||
92 | } | ||
93 | let mut buf = String::with_capacity(total_len.into()); | ||
94 | let mut prev = 0; | ||
95 | for indel in self.indels.iter() { | ||
96 | let start: usize = indel.delete.start().into(); | ||
97 | let end: usize = indel.delete.end().into(); | ||
98 | if start > prev { | ||
99 | buf.push_str(&text[prev..start]); | ||
100 | } | ||
101 | buf.push_str(&indel.insert); | ||
102 | prev = end; | ||
103 | } | ||
104 | buf.push_str(&text[prev..text.len()]); | ||
105 | assert_eq!(TextSize::of(&buf), total_len); | ||
106 | |||
107 | // FIXME: figure out a way to mutate the text in-place or reuse the | ||
108 | // memory in some other way | ||
109 | *text = buf | ||
110 | } | ||
111 | |||
112 | pub fn apply_to_offset(&self, offset: TextSize) -> Option<TextSize> { | ||
113 | let mut res = offset; | ||
114 | for indel in self.indels.iter() { | ||
115 | if indel.delete.start() >= offset { | ||
116 | break; | ||
117 | } | ||
118 | if offset < indel.delete.end() { | ||
119 | return None; | ||
120 | } | ||
121 | res += TextSize::of(&indel.insert); | ||
122 | res -= indel.delete.len(); | ||
123 | } | ||
124 | Some(res) | ||
125 | } | ||
126 | } | ||
127 | |||
128 | impl TextEditBuilder { | ||
129 | pub fn replace(&mut self, range: TextRange, replace_with: String) { | ||
130 | self.indels.push(Indel::replace(range, replace_with)) | ||
131 | } | ||
132 | pub fn delete(&mut self, range: TextRange) { | ||
133 | self.indels.push(Indel::delete(range)) | ||
134 | } | ||
135 | pub fn insert(&mut self, offset: TextSize, text: String) { | ||
136 | self.indels.push(Indel::insert(offset, text)) | ||
137 | } | ||
138 | pub fn finish(self) -> TextEdit { | ||
139 | TextEdit::from_indels(self.indels) | ||
140 | } | ||
141 | pub fn invalidates_offset(&self, offset: TextSize) -> bool { | ||
142 | self.indels.iter().any(|indel| indel.delete.contains_inclusive(offset)) | ||
35 | } | 143 | } |
36 | } | 144 | } |
diff --git a/crates/ra_text_edit/src/text_edit.rs b/crates/ra_text_edit/src/text_edit.rs deleted file mode 100644 index eabab4b4d..000000000 --- a/crates/ra_text_edit/src/text_edit.rs +++ /dev/null | |||
@@ -1,102 +0,0 @@ | |||
1 | //! FIXME: write short doc here | ||
2 | |||
3 | use crate::AtomTextEdit; | ||
4 | |||
5 | use text_size::{TextRange, TextSize}; | ||
6 | |||
7 | #[derive(Debug, Clone)] | ||
8 | pub struct TextEdit { | ||
9 | atoms: Vec<AtomTextEdit>, | ||
10 | } | ||
11 | |||
12 | #[derive(Debug, Default)] | ||
13 | pub struct TextEditBuilder { | ||
14 | atoms: Vec<AtomTextEdit>, | ||
15 | } | ||
16 | |||
17 | impl TextEditBuilder { | ||
18 | pub fn replace(&mut self, range: TextRange, replace_with: String) { | ||
19 | self.atoms.push(AtomTextEdit::replace(range, replace_with)) | ||
20 | } | ||
21 | pub fn delete(&mut self, range: TextRange) { | ||
22 | self.atoms.push(AtomTextEdit::delete(range)) | ||
23 | } | ||
24 | pub fn insert(&mut self, offset: TextSize, text: String) { | ||
25 | self.atoms.push(AtomTextEdit::insert(offset, text)) | ||
26 | } | ||
27 | pub fn finish(self) -> TextEdit { | ||
28 | TextEdit::from_atoms(self.atoms) | ||
29 | } | ||
30 | pub fn invalidates_offset(&self, offset: TextSize) -> bool { | ||
31 | self.atoms.iter().any(|atom| atom.delete.contains_inclusive(offset)) | ||
32 | } | ||
33 | } | ||
34 | |||
35 | impl TextEdit { | ||
36 | pub fn insert(offset: TextSize, text: String) -> TextEdit { | ||
37 | let mut builder = TextEditBuilder::default(); | ||
38 | builder.insert(offset, text); | ||
39 | builder.finish() | ||
40 | } | ||
41 | |||
42 | pub fn delete(range: TextRange) -> TextEdit { | ||
43 | let mut builder = TextEditBuilder::default(); | ||
44 | builder.delete(range); | ||
45 | builder.finish() | ||
46 | } | ||
47 | |||
48 | pub fn replace(range: TextRange, replace_with: String) -> TextEdit { | ||
49 | let mut builder = TextEditBuilder::default(); | ||
50 | builder.replace(range, replace_with); | ||
51 | builder.finish() | ||
52 | } | ||
53 | |||
54 | pub(crate) fn from_atoms(mut atoms: Vec<AtomTextEdit>) -> TextEdit { | ||
55 | atoms.sort_by_key(|a| (a.delete.start(), a.delete.end())); | ||
56 | for (a1, a2) in atoms.iter().zip(atoms.iter().skip(1)) { | ||
57 | assert!(a1.delete.end() <= a2.delete.start()) | ||
58 | } | ||
59 | TextEdit { atoms } | ||
60 | } | ||
61 | |||
62 | pub fn as_atoms(&self) -> &[AtomTextEdit] { | ||
63 | &self.atoms | ||
64 | } | ||
65 | |||
66 | pub fn apply(&self, text: &str) -> String { | ||
67 | let mut total_len = TextSize::of(text); | ||
68 | for atom in self.atoms.iter() { | ||
69 | total_len += TextSize::of(&atom.insert); | ||
70 | total_len -= atom.delete.end() - atom.delete.start(); | ||
71 | } | ||
72 | let mut buf = String::with_capacity(total_len.into()); | ||
73 | let mut prev = 0; | ||
74 | for atom in self.atoms.iter() { | ||
75 | let start: usize = atom.delete.start().into(); | ||
76 | let end: usize = atom.delete.end().into(); | ||
77 | if start > prev { | ||
78 | buf.push_str(&text[prev..start]); | ||
79 | } | ||
80 | buf.push_str(&atom.insert); | ||
81 | prev = end; | ||
82 | } | ||
83 | buf.push_str(&text[prev..text.len()]); | ||
84 | assert_eq!(TextSize::of(&buf), total_len); | ||
85 | buf | ||
86 | } | ||
87 | |||
88 | pub fn apply_to_offset(&self, offset: TextSize) -> Option<TextSize> { | ||
89 | let mut res = offset; | ||
90 | for atom in self.atoms.iter() { | ||
91 | if atom.delete.start() >= offset { | ||
92 | break; | ||
93 | } | ||
94 | if offset < atom.delete.end() { | ||
95 | return None; | ||
96 | } | ||
97 | res += TextSize::of(&atom.insert); | ||
98 | res -= atom.delete.len(); | ||
99 | } | ||
100 | Some(res) | ||
101 | } | ||
102 | } | ||
diff --git a/crates/rust-analyzer/src/caps.rs b/crates/rust-analyzer/src/caps.rs index 680415cac..110c9a442 100644 --- a/crates/rust-analyzer/src/caps.rs +++ b/crates/rust-analyzer/src/caps.rs | |||
@@ -17,10 +17,10 @@ pub fn server_capabilities() -> ServerCapabilities { | |||
17 | ServerCapabilities { | 17 | ServerCapabilities { |
18 | text_document_sync: Some(TextDocumentSyncCapability::Options(TextDocumentSyncOptions { | 18 | text_document_sync: Some(TextDocumentSyncCapability::Options(TextDocumentSyncOptions { |
19 | open_close: Some(true), | 19 | open_close: Some(true), |
20 | change: Some(if env::var("RA_PROFILE").is_ok() { | 20 | change: Some(if env::var("RA_NO_INCREMENTAL_SYNC").is_ok() { |
21 | TextDocumentSyncKind::Incremental | ||
22 | } else { | ||
23 | TextDocumentSyncKind::Full | 21 | TextDocumentSyncKind::Full |
22 | } else { | ||
23 | TextDocumentSyncKind::Incremental | ||
24 | }), | 24 | }), |
25 | will_save: None, | 25 | will_save: None, |
26 | will_save_wait_until: None, | 26 | will_save_wait_until: None, |
diff --git a/crates/rust-analyzer/src/cargo_target_spec.rs b/crates/rust-analyzer/src/cargo_target_spec.rs index 10c25666a..5e5a17943 100644 --- a/crates/rust-analyzer/src/cargo_target_spec.rs +++ b/crates/rust-analyzer/src/cargo_target_spec.rs | |||
@@ -27,7 +27,7 @@ impl CargoTargetSpec { | |||
27 | RunnableKind::Test { test_id, attr } => { | 27 | RunnableKind::Test { test_id, attr } => { |
28 | args.push("test".to_string()); | 28 | args.push("test".to_string()); |
29 | if let Some(spec) = spec { | 29 | if let Some(spec) = spec { |
30 | spec.push_to(&mut args); | 30 | spec.push_to(&mut args, kind); |
31 | } | 31 | } |
32 | extra_args.push(test_id.to_string()); | 32 | extra_args.push(test_id.to_string()); |
33 | if let TestId::Path(_) = test_id { | 33 | if let TestId::Path(_) = test_id { |
@@ -35,13 +35,13 @@ impl CargoTargetSpec { | |||
35 | } | 35 | } |
36 | extra_args.push("--nocapture".to_string()); | 36 | extra_args.push("--nocapture".to_string()); |
37 | if attr.ignore { | 37 | if attr.ignore { |
38 | extra_args.push("--ignored".to_string()) | 38 | extra_args.push("--ignored".to_string()); |
39 | } | 39 | } |
40 | } | 40 | } |
41 | RunnableKind::TestMod { path } => { | 41 | RunnableKind::TestMod { path } => { |
42 | args.push("test".to_string()); | 42 | args.push("test".to_string()); |
43 | if let Some(spec) = spec { | 43 | if let Some(spec) = spec { |
44 | spec.push_to(&mut args); | 44 | spec.push_to(&mut args, kind); |
45 | } | 45 | } |
46 | extra_args.push(path.to_string()); | 46 | extra_args.push(path.to_string()); |
47 | extra_args.push("--nocapture".to_string()); | 47 | extra_args.push("--nocapture".to_string()); |
@@ -49,7 +49,7 @@ impl CargoTargetSpec { | |||
49 | RunnableKind::Bench { test_id } => { | 49 | RunnableKind::Bench { test_id } => { |
50 | args.push("bench".to_string()); | 50 | args.push("bench".to_string()); |
51 | if let Some(spec) = spec { | 51 | if let Some(spec) = spec { |
52 | spec.push_to(&mut args); | 52 | spec.push_to(&mut args, kind); |
53 | } | 53 | } |
54 | extra_args.push(test_id.to_string()); | 54 | extra_args.push(test_id.to_string()); |
55 | if let TestId::Path(_) = test_id { | 55 | if let TestId::Path(_) = test_id { |
@@ -57,10 +57,19 @@ impl CargoTargetSpec { | |||
57 | } | 57 | } |
58 | extra_args.push("--nocapture".to_string()); | 58 | extra_args.push("--nocapture".to_string()); |
59 | } | 59 | } |
60 | RunnableKind::DocTest { test_id } => { | ||
61 | args.push("test".to_string()); | ||
62 | args.push("--doc".to_string()); | ||
63 | if let Some(spec) = spec { | ||
64 | spec.push_to(&mut args, kind); | ||
65 | } | ||
66 | extra_args.push(test_id.to_string()); | ||
67 | extra_args.push("--nocapture".to_string()); | ||
68 | } | ||
60 | RunnableKind::Bin => { | 69 | RunnableKind::Bin => { |
61 | args.push("run".to_string()); | 70 | args.push("run".to_string()); |
62 | if let Some(spec) = spec { | 71 | if let Some(spec) = spec { |
63 | spec.push_to(&mut args); | 72 | spec.push_to(&mut args, kind); |
64 | } | 73 | } |
65 | } | 74 | } |
66 | } | 75 | } |
@@ -91,9 +100,14 @@ impl CargoTargetSpec { | |||
91 | Ok(res) | 100 | Ok(res) |
92 | } | 101 | } |
93 | 102 | ||
94 | pub(crate) fn push_to(self, buf: &mut Vec<String>) { | 103 | pub(crate) fn push_to(self, buf: &mut Vec<String>, kind: &RunnableKind) { |
95 | buf.push("--package".to_string()); | 104 | buf.push("--package".to_string()); |
96 | buf.push(self.package); | 105 | buf.push(self.package); |
106 | |||
107 | // Can't mix --doc with other target flags | ||
108 | if let RunnableKind::DocTest { .. } = kind { | ||
109 | return; | ||
110 | } | ||
97 | match self.target_kind { | 111 | match self.target_kind { |
98 | TargetKind::Bin => { | 112 | TargetKind::Bin => { |
99 | buf.push("--bin".to_string()); | 113 | buf.push("--bin".to_string()); |
diff --git a/crates/rust-analyzer/src/cli/load_cargo.rs b/crates/rust-analyzer/src/cli/load_cargo.rs index d0a71120a..023ced6cf 100644 --- a/crates/rust-analyzer/src/cli/load_cargo.rs +++ b/crates/rust-analyzer/src/cli/load_cargo.rs | |||
@@ -149,7 +149,7 @@ pub(crate) fn load( | |||
149 | 149 | ||
150 | // FIXME: cfg options? | 150 | // FIXME: cfg options? |
151 | let default_cfg_options = { | 151 | let default_cfg_options = { |
152 | let mut opts = get_rustc_cfg_options(); | 152 | let mut opts = get_rustc_cfg_options(None); |
153 | opts.insert_atom("test".into()); | 153 | opts.insert_atom("test".into()); |
154 | opts.insert_atom("debug_assertion".into()); | 154 | opts.insert_atom("debug_assertion".into()); |
155 | opts | 155 | opts |
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 15b7c6912..ccc38e3bb 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs | |||
@@ -89,6 +89,7 @@ impl Default for Config { | |||
89 | check: Some(FlycheckConfig::CargoCommand { | 89 | check: Some(FlycheckConfig::CargoCommand { |
90 | command: "check".to_string(), | 90 | command: "check".to_string(), |
91 | all_targets: true, | 91 | all_targets: true, |
92 | all_features: true, | ||
92 | extra_args: Vec::new(), | 93 | extra_args: Vec::new(), |
93 | }), | 94 | }), |
94 | 95 | ||
@@ -131,6 +132,7 @@ impl Config { | |||
131 | set(value, "/cargo/allFeatures", &mut self.cargo.all_features); | 132 | set(value, "/cargo/allFeatures", &mut self.cargo.all_features); |
132 | set(value, "/cargo/features", &mut self.cargo.features); | 133 | set(value, "/cargo/features", &mut self.cargo.features); |
133 | set(value, "/cargo/loadOutDirsFromCheck", &mut self.cargo.load_out_dirs_from_check); | 134 | set(value, "/cargo/loadOutDirsFromCheck", &mut self.cargo.load_out_dirs_from_check); |
135 | set(value, "/cargo/target", &mut self.cargo.target); | ||
134 | 136 | ||
135 | match get(value, "/procMacro/enable") { | 137 | match get(value, "/procMacro/enable") { |
136 | Some(true) => { | 138 | Some(true) => { |
@@ -172,12 +174,13 @@ impl Config { | |||
172 | } | 174 | } |
173 | // otherwise configure command customizations | 175 | // otherwise configure command customizations |
174 | _ => { | 176 | _ => { |
175 | if let Some(FlycheckConfig::CargoCommand { command, extra_args, all_targets }) | 177 | if let Some(FlycheckConfig::CargoCommand { command, extra_args, all_targets, all_features }) |
176 | = &mut self.check | 178 | = &mut self.check |
177 | { | 179 | { |
178 | set(value, "/checkOnSave/extraArgs", extra_args); | 180 | set(value, "/checkOnSave/extraArgs", extra_args); |
179 | set(value, "/checkOnSave/command", command); | 181 | set(value, "/checkOnSave/command", command); |
180 | set(value, "/checkOnSave/allTargets", all_targets); | 182 | set(value, "/checkOnSave/allTargets", all_targets); |
183 | set(value, "/checkOnSave/allFeatures", all_features); | ||
181 | } | 184 | } |
182 | } | 185 | } |
183 | }; | 186 | }; |
diff --git a/crates/rust-analyzer/src/conv.rs b/crates/rust-analyzer/src/conv.rs index 7be5ebcdb..f64c90b5b 100644 --- a/crates/rust-analyzer/src/conv.rs +++ b/crates/rust-analyzer/src/conv.rs | |||
@@ -15,7 +15,7 @@ use ra_ide::{ | |||
15 | ReferenceAccess, Severity, SourceChange, SourceFileEdit, | 15 | ReferenceAccess, Severity, SourceChange, SourceFileEdit, |
16 | }; | 16 | }; |
17 | use ra_syntax::{SyntaxKind, TextRange, TextSize}; | 17 | use ra_syntax::{SyntaxKind, TextRange, TextSize}; |
18 | use ra_text_edit::{AtomTextEdit, TextEdit}; | 18 | use ra_text_edit::{Indel, TextEdit}; |
19 | use ra_vfs::LineEndings; | 19 | use ra_vfs::LineEndings; |
20 | 20 | ||
21 | use crate::{ | 21 | use crate::{ |
@@ -124,23 +124,22 @@ impl ConvWith<(&LineIndex, LineEndings)> for CompletionItem { | |||
124 | let mut text_edit = None; | 124 | let mut text_edit = None; |
125 | // LSP does not allow arbitrary edits in completion, so we have to do a | 125 | // LSP does not allow arbitrary edits in completion, so we have to do a |
126 | // non-trivial mapping here. | 126 | // non-trivial mapping here. |
127 | for atom_edit in self.text_edit().as_atoms() { | 127 | for indel in self.text_edit().as_indels() { |
128 | if atom_edit.delete.contains_range(self.source_range()) { | 128 | if indel.delete.contains_range(self.source_range()) { |
129 | text_edit = Some(if atom_edit.delete == self.source_range() { | 129 | text_edit = Some(if indel.delete == self.source_range() { |
130 | atom_edit.conv_with((ctx.0, ctx.1)) | 130 | indel.conv_with((ctx.0, ctx.1)) |
131 | } else { | 131 | } else { |
132 | assert!(self.source_range().end() == atom_edit.delete.end()); | 132 | assert!(self.source_range().end() == indel.delete.end()); |
133 | let range1 = | 133 | let range1 = TextRange::new(indel.delete.start(), self.source_range().start()); |
134 | TextRange::new(atom_edit.delete.start(), self.source_range().start()); | ||
135 | let range2 = self.source_range(); | 134 | let range2 = self.source_range(); |
136 | let edit1 = AtomTextEdit::replace(range1, String::new()); | 135 | let edit1 = Indel::replace(range1, String::new()); |
137 | let edit2 = AtomTextEdit::replace(range2, atom_edit.insert.clone()); | 136 | let edit2 = Indel::replace(range2, indel.insert.clone()); |
138 | additional_text_edits.push(edit1.conv_with((ctx.0, ctx.1))); | 137 | additional_text_edits.push(edit1.conv_with((ctx.0, ctx.1))); |
139 | edit2.conv_with((ctx.0, ctx.1)) | 138 | edit2.conv_with((ctx.0, ctx.1)) |
140 | }) | 139 | }) |
141 | } else { | 140 | } else { |
142 | assert!(self.source_range().intersect(atom_edit.delete).is_none()); | 141 | assert!(self.source_range().intersect(indel.delete).is_none()); |
143 | additional_text_edits.push(atom_edit.conv_with((ctx.0, ctx.1))); | 142 | additional_text_edits.push(indel.conv_with((ctx.0, ctx.1))); |
144 | } | 143 | } |
145 | } | 144 | } |
146 | let text_edit = text_edit.unwrap(); | 145 | let text_edit = text_edit.unwrap(); |
@@ -257,11 +256,11 @@ impl ConvWith<(&LineIndex, LineEndings)> for TextEdit { | |||
257 | type Output = Vec<lsp_types::TextEdit>; | 256 | type Output = Vec<lsp_types::TextEdit>; |
258 | 257 | ||
259 | fn conv_with(self, ctx: (&LineIndex, LineEndings)) -> Vec<lsp_types::TextEdit> { | 258 | fn conv_with(self, ctx: (&LineIndex, LineEndings)) -> Vec<lsp_types::TextEdit> { |
260 | self.as_atoms().iter().map_conv_with(ctx).collect() | 259 | self.as_indels().iter().map_conv_with(ctx).collect() |
261 | } | 260 | } |
262 | } | 261 | } |
263 | 262 | ||
264 | impl ConvWith<(&LineIndex, LineEndings)> for &AtomTextEdit { | 263 | impl ConvWith<(&LineIndex, LineEndings)> for &Indel { |
265 | type Output = lsp_types::TextEdit; | 264 | type Output = lsp_types::TextEdit; |
266 | 265 | ||
267 | fn conv_with( | 266 | fn conv_with( |
@@ -522,7 +521,7 @@ impl TryConvWith<&WorldSnapshot> for SourceFileEdit { | |||
522 | let line_index = world.analysis().file_line_index(self.file_id)?; | 521 | let line_index = world.analysis().file_line_index(self.file_id)?; |
523 | let line_endings = world.file_line_endings(self.file_id); | 522 | let line_endings = world.file_line_endings(self.file_id); |
524 | let edits = | 523 | let edits = |
525 | self.edit.as_atoms().iter().map_conv_with((&line_index, line_endings)).collect(); | 524 | self.edit.as_indels().iter().map_conv_with((&line_index, line_endings)).collect(); |
526 | Ok(TextDocumentEdit { text_document, edits }) | 525 | Ok(TextDocumentEdit { text_document, edits }) |
527 | } | 526 | } |
528 | } | 527 | } |
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index 401fae755..b77f0c5a9 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs | |||
@@ -676,13 +676,13 @@ fn apply_document_changes( | |||
676 | // remember the last valid line in the index and only rebuild it if needed. | 676 | // remember the last valid line in the index and only rebuild it if needed. |
677 | enum IndexValid { | 677 | enum IndexValid { |
678 | All, | 678 | All, |
679 | UpToLine(u64), | 679 | UpToLineExclusive(u64), |
680 | } | 680 | } |
681 | 681 | ||
682 | impl IndexValid { | 682 | impl IndexValid { |
683 | fn covers(&self, line: u64) -> bool { | 683 | fn covers(&self, line: u64) -> bool { |
684 | match *self { | 684 | match *self { |
685 | IndexValid::UpToLine(to) => to >= line, | 685 | IndexValid::UpToLineExclusive(to) => to > line, |
686 | _ => true, | 686 | _ => true, |
687 | } | 687 | } |
688 | } | 688 | } |
@@ -692,10 +692,10 @@ fn apply_document_changes( | |||
692 | for change in content_changes { | 692 | for change in content_changes { |
693 | match change.range { | 693 | match change.range { |
694 | Some(range) => { | 694 | Some(range) => { |
695 | if !index_valid.covers(range.start.line) { | 695 | if !index_valid.covers(range.end.line) { |
696 | line_index = Cow::Owned(LineIndex::new(&old_text)); | 696 | line_index = Cow::Owned(LineIndex::new(&old_text)); |
697 | } | 697 | } |
698 | index_valid = IndexValid::UpToLine(range.start.line); | 698 | index_valid = IndexValid::UpToLineExclusive(range.start.line); |
699 | let range = range.conv_with(&line_index); | 699 | let range = range.conv_with(&line_index); |
700 | let mut text = old_text.to_owned(); | 700 | let mut text = old_text.to_owned(); |
701 | match std::panic::catch_unwind(move || { | 701 | match std::panic::catch_unwind(move || { |
@@ -713,7 +713,7 @@ fn apply_document_changes( | |||
713 | } | 713 | } |
714 | None => { | 714 | None => { |
715 | *old_text = change.text; | 715 | *old_text = change.text; |
716 | index_valid = IndexValid::UpToLine(0); | 716 | index_valid = IndexValid::UpToLineExclusive(0); |
717 | } | 717 | } |
718 | } | 718 | } |
719 | } | 719 | } |
@@ -1083,5 +1083,17 @@ mod tests { | |||
1083 | assert_eq!(text, "the quick foxes\nDREAM\nthey have quiet dreams\nDON'T THEY?\n"); | 1083 | assert_eq!(text, "the quick foxes\nDREAM\nthey have quiet dreams\nDON'T THEY?\n"); |
1084 | run(&mut text, c![0, 10; 1, 5 => "", 2, 0; 2, 12 => ""]); | 1084 | run(&mut text, c![0, 10; 1, 5 => "", 2, 0; 2, 12 => ""]); |
1085 | assert_eq!(text, "the quick \nthey have quiet dreams\n"); | 1085 | assert_eq!(text, "the quick \nthey have quiet dreams\n"); |
1086 | |||
1087 | text = String::from("❤️"); | ||
1088 | run(&mut text, c![0, 0; 0, 0 => "a"]); | ||
1089 | assert_eq!(text, "a❤️"); | ||
1090 | |||
1091 | text = String::from("a\nb"); | ||
1092 | run(&mut text, c![0, 1; 1, 0 => "\nțc", 0, 1; 1, 1 => "d"]); | ||
1093 | assert_eq!(text, "adcb"); | ||
1094 | |||
1095 | text = String::from("a\nb"); | ||
1096 | run(&mut text, c![0, 1; 1, 0 => "ț\nc", 0, 2; 0, 2 => "c"]); | ||
1097 | assert_eq!(text, "ațc\ncb"); | ||
1086 | } | 1098 | } |
1087 | } | 1099 | } |
diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs index c7a96ba93..15e8305f8 100644 --- a/crates/rust-analyzer/src/main_loop/handlers.rs +++ b/crates/rust-analyzer/src/main_loop/handlers.rs | |||
@@ -835,6 +835,7 @@ pub fn handle_code_lens( | |||
835 | for runnable in world.analysis().runnables(file_id)? { | 835 | for runnable in world.analysis().runnables(file_id)? { |
836 | let title = match &runnable.kind { | 836 | let title = match &runnable.kind { |
837 | RunnableKind::Test { .. } | RunnableKind::TestMod { .. } => "▶️\u{fe0e}Run Test", | 837 | RunnableKind::Test { .. } | RunnableKind::TestMod { .. } => "▶️\u{fe0e}Run Test", |
838 | RunnableKind::DocTest { .. } => "▶️\u{fe0e}Run Doctest", | ||
838 | RunnableKind::Bench { .. } => "Run Bench", | 839 | RunnableKind::Bench { .. } => "Run Bench", |
839 | RunnableKind::Bin => "Run", | 840 | RunnableKind::Bin => "Run", |
840 | } | 841 | } |
@@ -1018,6 +1019,7 @@ fn to_lsp_runnable( | |||
1018 | RunnableKind::Test { test_id, .. } => format!("test {}", test_id), | 1019 | RunnableKind::Test { test_id, .. } => format!("test {}", test_id), |
1019 | RunnableKind::TestMod { path } => format!("test-mod {}", path), | 1020 | RunnableKind::TestMod { path } => format!("test-mod {}", path), |
1020 | RunnableKind::Bench { test_id } => format!("bench {}", test_id), | 1021 | RunnableKind::Bench { test_id } => format!("bench {}", test_id), |
1022 | RunnableKind::DocTest { test_id, .. } => format!("doctest {}", test_id), | ||
1021 | RunnableKind::Bin => "run binary".to_string(), | 1023 | RunnableKind::Bin => "run binary".to_string(), |
1022 | }; | 1024 | }; |
1023 | Ok(req::Runnable { | 1025 | Ok(req::Runnable { |
diff --git a/crates/rust-analyzer/src/world.rs b/crates/rust-analyzer/src/world.rs index 34941931b..16020648d 100644 --- a/crates/rust-analyzer/src/world.rs +++ b/crates/rust-analyzer/src/world.rs | |||
@@ -131,7 +131,7 @@ impl WorldState { | |||
131 | 131 | ||
132 | // FIXME: Read default cfgs from config | 132 | // FIXME: Read default cfgs from config |
133 | let default_cfg_options = { | 133 | let default_cfg_options = { |
134 | let mut opts = get_rustc_cfg_options(); | 134 | let mut opts = get_rustc_cfg_options(config.cargo.target.as_ref()); |
135 | opts.insert_atom("test".into()); | 135 | opts.insert_atom("test".into()); |
136 | opts.insert_atom("debug_assertion".into()); | 136 | opts.insert_atom("debug_assertion".into()); |
137 | opts | 137 | opts |
diff --git a/crates/rust-analyzer/tests/heavy_tests/main.rs b/crates/rust-analyzer/tests/heavy_tests/main.rs index a218da76d..1efa5dd63 100644 --- a/crates/rust-analyzer/tests/heavy_tests/main.rs +++ b/crates/rust-analyzer/tests/heavy_tests/main.rs | |||
@@ -337,7 +337,7 @@ fn main() {} | |||
337 | "arguments": [ | 337 | "arguments": [ |
338 | { | 338 | { |
339 | "cursorPosition": null, | 339 | "cursorPosition": null, |
340 | "label": "create module", | 340 | "label": "Create module", |
341 | "workspaceEdit": { | 341 | "workspaceEdit": { |
342 | "documentChanges": [ | 342 | "documentChanges": [ |
343 | { | 343 | { |
@@ -349,9 +349,9 @@ fn main() {} | |||
349 | } | 349 | } |
350 | ], | 350 | ], |
351 | "command": "rust-analyzer.applySourceChange", | 351 | "command": "rust-analyzer.applySourceChange", |
352 | "title": "create module" | 352 | "title": "Create module" |
353 | }, | 353 | }, |
354 | "title": "create module" | 354 | "title": "Create module" |
355 | } | 355 | } |
356 | ]), | 356 | ]), |
357 | ); | 357 | ); |
@@ -420,7 +420,7 @@ fn main() {{}} | |||
420 | "arguments": [ | 420 | "arguments": [ |
421 | { | 421 | { |
422 | "cursorPosition": null, | 422 | "cursorPosition": null, |
423 | "label": "create module", | 423 | "label": "Create module", |
424 | "workspaceEdit": { | 424 | "workspaceEdit": { |
425 | "documentChanges": [ | 425 | "documentChanges": [ |
426 | { | 426 | { |
@@ -432,9 +432,9 @@ fn main() {{}} | |||
432 | } | 432 | } |
433 | ], | 433 | ], |
434 | "command": "rust-analyzer.applySourceChange", | 434 | "command": "rust-analyzer.applySourceChange", |
435 | "title": "create module" | 435 | "title": "Create module" |
436 | }, | 436 | }, |
437 | "title": "create module" | 437 | "title": "Create module" |
438 | } | 438 | } |
439 | ]), | 439 | ]), |
440 | ); | 440 | ); |
@@ -500,7 +500,7 @@ fn main() {{}} | |||
500 | "position": { "character": 4, "line": 1 }, | 500 | "position": { "character": 4, "line": 1 }, |
501 | "textDocument": { "uri": "file:///[..]src/m0.rs" } | 501 | "textDocument": { "uri": "file:///[..]src/m0.rs" } |
502 | }, | 502 | }, |
503 | "label": "on enter", | 503 | "label": "On enter", |
504 | "workspaceEdit": { | 504 | "workspaceEdit": { |
505 | "documentChanges": [ | 505 | "documentChanges": [ |
506 | { | 506 | { |
@@ -552,7 +552,7 @@ version = \"0.0.0\" | |||
552 | "position": { "line": 1, "character": 4 }, | 552 | "position": { "line": 1, "character": 4 }, |
553 | "textDocument": { "uri": "file:///[..]src/main.rs" } | 553 | "textDocument": { "uri": "file:///[..]src/main.rs" } |
554 | }, | 554 | }, |
555 | "label": "on enter", | 555 | "label": "On enter", |
556 | "workspaceEdit": { | 556 | "workspaceEdit": { |
557 | "documentChanges": [ | 557 | "documentChanges": [ |
558 | { | 558 | { |