aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ra_assists/src/assist_ctx.rs19
-rw-r--r--crates/ra_assists/src/doc_tests.rs6
-rw-r--r--crates/ra_assists/src/handlers/add_missing_impl_members.rs26
-rw-r--r--crates/ra_assists/src/handlers/add_new.rs4
-rw-r--r--crates/ra_assists/src/handlers/change_visibility.rs13
-rw-r--r--crates/ra_assists/src/lib.rs44
-rw-r--r--crates/ra_assists/src/utils.rs10
-rw-r--r--crates/ra_flycheck/src/lib.rs7
-rw-r--r--crates/ra_hir/src/code_model.rs2
-rw-r--r--crates/ra_hir_def/src/adt.rs17
-rw-r--r--crates/ra_hir_def/src/body.rs11
-rw-r--r--crates/ra_hir_def/src/body/lower.rs19
-rw-r--r--crates/ra_hir_def/src/data.rs21
-rw-r--r--crates/ra_hir_def/src/item_scope.rs11
-rw-r--r--crates/ra_hir_def/src/nameres/collector.rs13
-rw-r--r--crates/ra_hir_def/src/nameres/raw.rs18
-rw-r--r--crates/ra_hir_def/src/nameres/tests.rs2
-rw-r--r--crates/ra_hir_def/src/nameres/tests/macros.rs32
-rw-r--r--crates/ra_hir_ty/src/tests/macros.rs4
-rw-r--r--crates/ra_hir_ty/src/tests/regression.rs29
-rw-r--r--crates/ra_ide/src/assists.rs19
-rw-r--r--crates/ra_ide/src/completion/complete_qualified_path.rs2
-rw-r--r--crates/ra_ide/src/completion/complete_trait_impl.rs46
-rw-r--r--crates/ra_ide/src/completion/completion_context.rs4
-rw-r--r--crates/ra_ide/src/completion/completion_item.rs4
-rw-r--r--crates/ra_ide/src/diagnostics.rs36
-rw-r--r--crates/ra_ide/src/join_lines.rs6
-rw-r--r--crates/ra_ide/src/references/rename.rs42
-rw-r--r--crates/ra_ide/src/runnables.rs43
-rw-r--r--crates/ra_ide/src/source_change.rs4
-rw-r--r--crates/ra_ide/src/ssr.rs32
-rw-r--r--crates/ra_ide/src/test_utils.rs6
-rw-r--r--crates/ra_ide/src/typing.rs9
-rw-r--r--crates/ra_ide/src/typing/on_enter.rs5
-rw-r--r--crates/ra_ide_db/src/line_index.rs17
-rw-r--r--crates/ra_ide_db/src/line_index_utils.rs6
-rw-r--r--crates/ra_mbe/src/syntax_bridge.rs2
-rw-r--r--crates/ra_project_model/src/cargo_workspace.rs7
-rw-r--r--crates/ra_project_model/src/lib.rs12
-rw-r--r--crates/ra_syntax/src/ast/edit.rs6
-rw-r--r--crates/ra_syntax/src/ast/generated/nodes.rs36
-rw-r--r--crates/ra_syntax/src/fuzz.rs6
-rw-r--r--crates/ra_syntax/src/lib.rs15
-rw-r--r--crates/ra_syntax/src/parsing/reparsing.rs28
-rw-r--r--crates/ra_text_edit/src/lib.rs146
-rw-r--r--crates/ra_text_edit/src/text_edit.rs102
-rw-r--r--crates/rust-analyzer/src/caps.rs6
-rw-r--r--crates/rust-analyzer/src/cargo_target_spec.rs26
-rw-r--r--crates/rust-analyzer/src/cli/load_cargo.rs2
-rw-r--r--crates/rust-analyzer/src/config.rs5
-rw-r--r--crates/rust-analyzer/src/conv.rs29
-rw-r--r--crates/rust-analyzer/src/main_loop.rs22
-rw-r--r--crates/rust-analyzer/src/main_loop/handlers.rs2
-rw-r--r--crates/rust-analyzer/src/world.rs2
-rw-r--r--crates/rust-analyzer/tests/heavy_tests/main.rs16
-rw-r--r--docs/user/readme.adoc12
-rw-r--r--editors/code/package-lock.json5
-rw-r--r--editors/code/package.json14
-rw-r--r--editors/code/src/color_theme.ts129
-rw-r--r--xtask/src/ast_src.rs4
-rw-r--r--xtask/tests/tidy-tests/main.rs1
61 files changed, 657 insertions, 567 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;
4use ra_fmt::{leading_indent, reindent}; 4use ra_fmt::{leading_indent, reindent};
5use ra_ide_db::RootDatabase; 5use ra_ide_db::RootDatabase;
6use ra_syntax::{ 6use 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};
11use ra_text_edit::TextEditBuilder; 11use ra_text_edit::TextEditBuilder;
12 12
13use crate::{AssistAction, AssistFile, AssistId, AssistLabel, GroupLabel, ResolvedAssist}; 13use crate::{AssistAction, AssistFile, AssistId, AssistLabel, GroupLabel, ResolvedAssist};
14use algo::SyntaxRewriter;
15 14
16#[derive(Clone, Debug)] 15#[derive(Clone, Debug)]
17pub(crate) struct Assist(pub(crate) Vec<AssistInfo>); 16pub(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
46pub(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
147pub(crate) struct AssistGroup<'a> { 144pub(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
11use crate::{ 11use 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
163fn has_new_fn(imp: &ast::ImplDef) -> bool { 163fn 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;
17pub mod utils; 17pub mod utils;
18pub mod ast_transform; 18pub mod ast_transform;
19 19
20use hir::Semantics;
20use ra_db::{FileId, FileRange}; 21use ra_db::{FileId, FileRange};
21use ra_ide_db::RootDatabase; 22use ra_ide_db::RootDatabase;
22use ra_syntax::{TextRange, TextSize}; 23use ra_syntax::{TextRange, TextSize};
23use ra_text_edit::TextEdit; 24use ra_text_edit::TextEdit;
24 25
25pub(crate) use crate::assist_ctx::{Assist, AssistCtx, AssistHandler}; 26pub(crate) use crate::assist_ctx::{Assist, AssistCtx};
26use 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)]
34pub struct AssistLabel { 34pub 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)]
41pub struct GroupLabel(pub String); 42pub struct GroupLabel(pub String);
42 43
43impl AssistLabel { 44impl 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)]
61pub struct ResolvedAssist { 62pub 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
111mod handlers { 111mod 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 {
195mod helpers { 199mod 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
14pub(crate) use insert_use::insert_use_statement; 14pub(crate) use insert_use::insert_use_statement;
15 15
16pub fn get_missing_impl_items( 16pub 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)]
26pub enum FlycheckConfig { 26pub 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};
10use ra_prof::profile; 10use ra_prof::profile;
11use ra_syntax::ast::{ 11use 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(
325fn collect_items( 325fn 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
153impl PerNs { 153impl 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)]
159pub(super) enum StructDefKind {
160 Record,
161 Tuple,
162 Unit,
163}
164
165#[derive(Debug, PartialEq, Eq, Clone, Copy)]
159pub(super) enum DefKind { 166pub(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]
272fn infer_impl_items_generated_by_macros() { 272fn 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]
291fn infer_impl_items_generated_by_macros_chain() { 291fn 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]
538fn issue_4235_name_conflicts() {
539 assert_snapshot!(
540 infer(r#"
541struct FOO {}
542static FOO:FOO = FOO {};
543
544impl FOO {
545 fn foo(&self) {}
546}
547
548fn 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]
538fn issue_4053_diesel_where_clauses() { 567fn 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
3use ra_assists::{resolved_assists, AssistAction, AssistLabel}; 3use ra_assists::{resolved_assists, AssistAction};
4use ra_db::{FilePosition, FileRange}; 4use ra_db::{FilePosition, FileRange};
5use ra_ide_db::RootDatabase; 5use 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
35fn action_to_edit( 34fn 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
34use hir::{self, Docs, HasSource}; 34use hir::{self, Docs, HasSource};
35use ra_assists::utils::get_missing_impl_items; 35use ra_assists::utils::get_missing_assoc_items;
36use ra_syntax::{ 36use 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};
12use ra_text_edit::AtomTextEdit; 12use ra_text_edit::Indel;
13 13
14use crate::{call_info::ActiveParameter, completion::CompletionConfig, FilePosition}; 14use 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
128fn rename_reference( 128fn 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
11use crate::FileId; 11use crate::FileId;
12use ast::DocCommentsOwner;
12use std::fmt::Display; 13use 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
124fn has_doc_test(fn_def: &ast::FnDef) -> bool {
125 fn_def.doc_comment_text().map_or(false, |comment| comment.contains("```"))
126}
127
120fn runnable_mod(sema: &Semantics<RootDatabase>, module: ast::Module) -> Option<Runnable> { 128fn 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
33impl Utf16Char { 33impl 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
39impl LineIndex { 49impl 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 @@
10use std::convert::TryInto; 10use std::convert::TryInto;
11 11
12use ra_syntax::{TextRange, TextSize}; 12use ra_syntax::{TextRange, TextSize};
13use ra_text_edit::{AtomTextEdit, TextEdit}; 13use ra_text_edit::{Indel, TextEdit};
14 14
15use crate::line_index::{LineCol, LineIndex, Utf16Char}; 15use crate::line_index::{LineCol, LineIndex, Utf16Char};
16 16
@@ -182,14 +182,14 @@ struct TranslatedEdit<'a> {
182} 182}
183 183
184struct Edits<'a> { 184struct 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
190impl<'a> Edits<'a> { 190impl<'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
69pub fn token_tree_to_syntax_node( 69pub 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
61impl Default for CargoConfig { 64impl 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
546pub fn get_rustc_cfg_options() -> CfgOptions { 546pub 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
80impl ast::ItemList { 80impl 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 {
196impl ast::ModuleItemOwner for ItemList {} 196impl ast::ModuleItemOwner for ItemList {}
197impl ItemList { 197impl 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 {}
1429impl ast::VisibilityOwner for ModuleItem {} 1429impl ast::VisibilityOwner for ModuleItem {}
1430 1430
1431#[derive(Debug, Clone, PartialEq, Eq, Hash)] 1431#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1432pub enum ImplItem { 1432pub enum AssocItem {
1433 FnDef(FnDef), 1433 FnDef(FnDef),
1434 TypeAliasDef(TypeAliasDef), 1434 TypeAliasDef(TypeAliasDef),
1435 ConstDef(ConstDef), 1435 ConstDef(ConstDef),
1436} 1436}
1437impl ast::NameOwner for ImplItem {} 1437impl ast::NameOwner for AssocItem {}
1438impl ast::AttrsOwner for ImplItem {} 1438impl ast::AttrsOwner for AssocItem {}
1439 1439
1440#[derive(Debug, Clone, PartialEq, Eq, Hash)] 1440#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1441pub enum ExternItem { 1441pub enum ExternItem {
@@ -3167,16 +3167,16 @@ impl AstNode for ModuleItem {
3167 } 3167 }
3168 } 3168 }
3169} 3169}
3170impl From<FnDef> for ImplItem { 3170impl From<FnDef> for AssocItem {
3171 fn from(node: FnDef) -> ImplItem { ImplItem::FnDef(node) } 3171 fn from(node: FnDef) -> AssocItem { AssocItem::FnDef(node) }
3172} 3172}
3173impl From<TypeAliasDef> for ImplItem { 3173impl From<TypeAliasDef> for AssocItem {
3174 fn from(node: TypeAliasDef) -> ImplItem { ImplItem::TypeAliasDef(node) } 3174 fn from(node: TypeAliasDef) -> AssocItem { AssocItem::TypeAliasDef(node) }
3175} 3175}
3176impl From<ConstDef> for ImplItem { 3176impl From<ConstDef> for AssocItem {
3177 fn from(node: ConstDef) -> ImplItem { ImplItem::ConstDef(node) } 3177 fn from(node: ConstDef) -> AssocItem { AssocItem::ConstDef(node) }
3178} 3178}
3179impl AstNode for ImplItem { 3179impl 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}
3644impl std::fmt::Display for ImplItem { 3644impl 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
8use ra_text_edit::AtomTextEdit; 8use ra_text_edit::Indel;
9 9
10use crate::{validation, AstNode, SourceFile, TextRange}; 10use 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)]
23pub struct CheckReparse { 23pub 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
40use std::{marker::PhantomData, sync::Arc}; 40use std::{marker::PhantomData, sync::Arc};
41 41
42use ra_text_edit::AtomTextEdit; 42use ra_text_edit::Indel;
43use stdx::format_to; 43use stdx::format_to;
44 44
45use crate::syntax_node::GreenNode; 45use 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
9use ra_parser::Reparser; 9use ra_parser::Reparser;
10use ra_text_edit::AtomTextEdit; 10use ra_text_edit::Indel;
11 11
12use crate::{ 12use crate::{
13 algo, 13 algo,
@@ -24,7 +24,7 @@ use crate::{
24 24
25pub(crate) fn incremental_reparse( 25pub(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
40fn reparse_token<'node>( 40fn 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
89fn reparse_block<'node>( 89fn 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
111fn get_text_after_edit(element: SyntaxElement, edit: &AtomTextEdit) -> String { 111fn 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
122fn is_contextual_kw(text: &str) -> bool { 122fn 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//!
3mod 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
5use text_size::{TextRange, TextSize}; 7use text_size::{TextRange, TextSize};
6 8
7pub 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)]
11pub struct AtomTextEdit { 13pub 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
17impl AtomTextEdit { 19#[derive(Debug, Clone)]
18 pub fn replace(range: TextRange, replace_with: String) -> AtomTextEdit { 20pub 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()) 25pub struct TextEditBuilder {
24 } 26 indels: Vec<Indel>,
27}
25 28
26 pub fn insert(offset: TextSize, text: String) -> AtomTextEdit { 29impl 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
47impl 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
128impl 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
3use crate::AtomTextEdit;
4
5use text_size::{TextRange, TextSize};
6
7#[derive(Debug, Clone)]
8pub struct TextEdit {
9 atoms: Vec<AtomTextEdit>,
10}
11
12#[derive(Debug, Default)]
13pub struct TextEditBuilder {
14 atoms: Vec<AtomTextEdit>,
15}
16
17impl 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
35impl 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};
17use ra_syntax::{SyntaxKind, TextRange, TextSize}; 17use ra_syntax::{SyntaxKind, TextRange, TextSize};
18use ra_text_edit::{AtomTextEdit, TextEdit}; 18use ra_text_edit::{Indel, TextEdit};
19use ra_vfs::LineEndings; 19use ra_vfs::LineEndings;
20 20
21use crate::{ 21use 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
264impl ConvWith<(&LineIndex, LineEndings)> for &AtomTextEdit { 263impl 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 {
diff --git a/docs/user/readme.adoc b/docs/user/readme.adoc
index b1af72ce6..69f5b13d6 100644
--- a/docs/user/readme.adoc
+++ b/docs/user/readme.adoc
@@ -57,7 +57,11 @@ To disable this notification put the following to `settings.json`
57---- 57----
58==== 58====
59 59
60The server binary is stored in `~/.config/Code/User/globalStorage/matklad.rust-analyzer` (Linux) or in `~/.Library/Application Support/Code/User/globalStorage/matklad.rust-analyzer` (macOS) or in `%APPDATA%\Code\User\globalStorage` (Windows). 60The server binary is stored in:
61
62* Linux: `~/.config/Code/User/globalStorage/matklad.rust-analyzer`
63* macOS: `~/Library/Application Support/Code/User/globalStorage/matklad.rust-analyzer`
64* Windows: `%APPDATA%\Code\User\globalStorage`
61 65
62Note that we only support the latest version of VS Code. 66Note that we only support the latest version of VS Code.
63 67
@@ -159,11 +163,11 @@ Emacs support is maintained as part of the https://github.com/emacs-lsp/lsp-mode
1593. Run `lsp` in a Rust buffer. 1633. Run `lsp` in a Rust buffer.
1604. (Optionally) bind commands like `lsp-rust-analyzer-join-lines`, `lsp-extend-selection` and `lsp-rust-analyzer-expand-macro` to keys. 1644. (Optionally) bind commands like `lsp-rust-analyzer-join-lines`, `lsp-extend-selection` and `lsp-rust-analyzer-expand-macro` to keys.
161 165
162=== Vim 166=== Vim/NeoVim
163 167
164Prerequisites: You have installed the <<rust-analyzer-language-server-binary,`rust-analyzer` binary>>. 168Prerequisites: You have installed the <<rust-analyzer-language-server-binary,`rust-analyzer` binary>>. Not needed if the extension can install/update it on its own, coc-rust-analyzer is one example.
165 169
166The are several LSP client implementations for vim: 170The are several LSP client implementations for vim or neovim:
167 171
168==== coc-rust-analyzer 172==== coc-rust-analyzer
169 173
diff --git a/editors/code/package-lock.json b/editors/code/package-lock.json
index 55ba5351d..71c627a2a 100644
--- a/editors/code/package-lock.json
+++ b/editors/code/package-lock.json
@@ -1066,11 +1066,6 @@
1066 "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", 1066 "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=",
1067 "dev": true 1067 "dev": true
1068 }, 1068 },
1069 "jsonc-parser": {
1070 "version": "2.2.1",
1071 "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-2.2.1.tgz",
1072 "integrity": "sha512-o6/yDBYccGvTz1+QFevz6l6OBZ2+fMVu2JZ9CIhzsYRX4mjaK5IyX9eldUdCmga16zlgQxyrj5pt9kzuj2C02w=="
1073 },
1074 "leven": { 1069 "leven": {
1075 "version": "3.1.0", 1070 "version": "3.1.0",
1076 "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", 1071 "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz",
diff --git a/editors/code/package.json b/editors/code/package.json
index 442b9de98..eeb3d3513 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -33,7 +33,6 @@
33 "fix": " tsfmt -r && eslint -c .eslintrc.js --ext ts ./src --fix" 33 "fix": " tsfmt -r && eslint -c .eslintrc.js --ext ts ./src --fix"
34 }, 34 },
35 "dependencies": { 35 "dependencies": {
36 "jsonc-parser": "^2.2.1",
37 "node-fetch": "^2.6.0", 36 "node-fetch": "^2.6.0",
38 "vscode-languageclient": "7.0.0-next.1" 37 "vscode-languageclient": "7.0.0-next.1"
39 }, 38 },
@@ -233,6 +232,14 @@
233 "default": false, 232 "default": false,
234 "markdownDescription": "Run `cargo check` on startup to get the correct value for package OUT_DIRs" 233 "markdownDescription": "Run `cargo check` on startup to get the correct value for package OUT_DIRs"
235 }, 234 },
235 "rust-analyzer.cargo.target": {
236 "type": [
237 "null",
238 "string"
239 ],
240 "default": null,
241 "description": "Specify the compilation target"
242 },
236 "rust-analyzer.rustfmt.extraArgs": { 243 "rust-analyzer.rustfmt.extraArgs": {
237 "type": "array", 244 "type": "array",
238 "items": { 245 "items": {
@@ -288,6 +295,11 @@
288 "default": true, 295 "default": true,
289 "markdownDescription": "Check all targets and tests (will be passed as `--all-targets`)" 296 "markdownDescription": "Check all targets and tests (will be passed as `--all-targets`)"
290 }, 297 },
298 "rust-analyzer.checkOnSave.allFeatures": {
299 "type": "boolean",
300 "default": true,
301 "markdownDescription": "Check with all features (will be passed as `--all-features`)"
302 },
291 "rust-analyzer.inlayHints.typeHints": { 303 "rust-analyzer.inlayHints.typeHints": {
292 "type": "boolean", 304 "type": "boolean",
293 "default": true, 305 "default": true,
diff --git a/editors/code/src/color_theme.ts b/editors/code/src/color_theme.ts
deleted file mode 100644
index 5b9327b28..000000000
--- a/editors/code/src/color_theme.ts
+++ /dev/null
@@ -1,129 +0,0 @@
1import * as fs from 'fs';
2import * as jsonc from 'jsonc-parser';
3import * as path from 'path';
4import * as vscode from 'vscode';
5
6export interface TextMateRuleSettings {
7 foreground?: string;
8 background?: string;
9 fontStyle?: string;
10}
11
12export class ColorTheme {
13 private rules: Map<string, TextMateRuleSettings> = new Map();
14
15 static load(): ColorTheme {
16 // Find out current color theme
17 const themeName = vscode.workspace
18 .getConfiguration('workbench')
19 .get('colorTheme');
20
21 if (typeof themeName !== 'string') {
22 // console.warn('workbench.colorTheme is', themeName)
23 return new ColorTheme();
24 }
25 return loadThemeNamed(themeName);
26 }
27
28 static fromRules(rules: TextMateRule[]): ColorTheme {
29 const res = new ColorTheme();
30 for (const rule of rules) {
31 const scopes = typeof rule.scope === 'undefined'
32 ? []
33 : typeof rule.scope === 'string'
34 ? [rule.scope]
35 : rule.scope;
36
37 for (const scope of scopes) {
38 res.rules.set(scope, rule.settings);
39 }
40 }
41 return res;
42 }
43
44 lookup(scopes: string[]): TextMateRuleSettings {
45 let res: TextMateRuleSettings = {};
46 for (const scope of scopes) {
47 this.rules.forEach((value, key) => {
48 if (scope.startsWith(key)) {
49 res = mergeRuleSettings(res, value);
50 }
51 });
52 }
53 return res;
54 }
55
56 mergeFrom(other: ColorTheme) {
57 other.rules.forEach((value, key) => {
58 const merged = mergeRuleSettings(this.rules.get(key), value);
59 this.rules.set(key, merged);
60 });
61 }
62}
63
64function loadThemeNamed(themeName: string): ColorTheme {
65 function isTheme(extension: vscode.Extension<unknown>): boolean {
66 return (
67 extension.extensionKind === vscode.ExtensionKind.UI &&
68 extension.packageJSON.contributes &&
69 extension.packageJSON.contributes.themes
70 );
71 }
72
73 const themePaths: string[] = vscode.extensions.all
74 .filter(isTheme)
75 .flatMap(
76 ext => ext.packageJSON.contributes.themes
77 .filter((it: any) => (it.id || it.label) === themeName)
78 .map((it: any) => path.join(ext.extensionPath, it.path))
79 );
80
81 const res = new ColorTheme();
82 for (const themePath of themePaths) {
83 res.mergeFrom(loadThemeFile(themePath));
84 }
85
86 const globalCustomizations: any = vscode.workspace.getConfiguration('editor').get('tokenColorCustomizations');
87 res.mergeFrom(ColorTheme.fromRules(globalCustomizations?.textMateRules ?? []));
88
89 const themeCustomizations: any = vscode.workspace.getConfiguration('editor.tokenColorCustomizations').get(`[${themeName}]`);
90 res.mergeFrom(ColorTheme.fromRules(themeCustomizations?.textMateRules ?? []));
91
92
93 return res;
94}
95
96function loadThemeFile(themePath: string): ColorTheme {
97 let text;
98 try {
99 text = fs.readFileSync(themePath, 'utf8');
100 } catch {
101 return new ColorTheme();
102 }
103 const obj = jsonc.parse(text);
104 const tokenColors: TextMateRule[] = obj?.tokenColors ?? [];
105 const res = ColorTheme.fromRules(tokenColors);
106
107 for (const include of obj?.include ?? []) {
108 const includePath = path.join(path.dirname(themePath), include);
109 res.mergeFrom(loadThemeFile(includePath));
110 }
111
112 return res;
113}
114
115interface TextMateRule {
116 scope: string | string[];
117 settings: TextMateRuleSettings;
118}
119
120function mergeRuleSettings(
121 defaultSetting: TextMateRuleSettings | undefined,
122 override: TextMateRuleSettings,
123): TextMateRuleSettings {
124 return {
125 foreground: override.foreground ?? defaultSetting?.foreground,
126 background: override.background ?? defaultSetting?.background,
127 fontStyle: override.fontStyle ?? defaultSetting?.fontStyle,
128 };
129}
diff --git a/xtask/src/ast_src.rs b/xtask/src/ast_src.rs
index 2f8065b73..fe3eb85de 100644
--- a/xtask/src/ast_src.rs
+++ b/xtask/src/ast_src.rs
@@ -373,7 +373,7 @@ pub(crate) const AST_SRC: AstSrc = AstSrc {
373 373
374 struct ItemList: ModuleItemOwner { 374 struct ItemList: ModuleItemOwner {
375 T!['{'], 375 T!['{'],
376 impl_items: [ImplItem], 376 assoc_items: [AssocItem],
377 T!['}'] 377 T!['}']
378 } 378 }
379 379
@@ -685,7 +685,7 @@ pub(crate) const AST_SRC: AstSrc = AstSrc {
685 } 685 }
686 686
687 /* impl blocks can also contain MacroCall */ 687 /* impl blocks can also contain MacroCall */
688 enum ImplItem: NameOwner, AttrsOwner { 688 enum AssocItem: NameOwner, AttrsOwner {
689 FnDef, TypeAliasDef, ConstDef 689 FnDef, TypeAliasDef, ConstDef
690 } 690 }
691 691
diff --git a/xtask/tests/tidy-tests/main.rs b/xtask/tests/tidy-tests/main.rs
index ead642acc..3213c4dfa 100644
--- a/xtask/tests/tidy-tests/main.rs
+++ b/xtask/tests/tidy-tests/main.rs
@@ -115,7 +115,6 @@ impl TidyDocs {
115 "ra_prof", 115 "ra_prof",
116 "ra_project_model", 116 "ra_project_model",
117 "ra_syntax", 117 "ra_syntax",
118 "ra_text_edit",
119 "ra_tt", 118 "ra_tt",
120 "ra_hir_ty", 119 "ra_hir_ty",
121 ]; 120 ];