diff --git a/crates/ra_assists/src/assist_ctx.rs b/crates/ra_assists/src/assist_ctx.rs index da288003763..83dd270c6ce 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; use ra_fmt::{leading_indent, reindent}; use ra_ide_db::RootDatabase; use ra_syntax::{ - algo::{self, find_covering_element, find_node_at_offset}, + algo::{self, find_covering_element, find_node_at_offset, SyntaxRewriter}, AstNode, SourceFile, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, TextRange, TextSize, TokenAtOffset, }; use ra_text_edit::TextEditBuilder; use crate::{AssistAction, AssistFile, AssistId, AssistLabel, GroupLabel, ResolvedAssist}; -use algo::SyntaxRewriter; #[derive(Clone, Debug)] pub(crate) struct Assist(pub(crate) Vec); @@ -38,13 +37,10 @@ impl AssistInfo { pub(crate) fn into_resolved(self) -> Option { let label = self.label; - let group_label = self.group_label; - self.action.map(|action| ResolvedAssist { label, group_label, action }) + self.action.map(|action| ResolvedAssist { label, action }) } } -pub(crate) type AssistHandler = fn(AssistCtx) -> Option; - /// `AssistCtx` allows to apply an assist or check if it could be applied. /// /// Assists use a somewhat over-engineered approach, given the current needs. The @@ -100,7 +96,7 @@ impl<'a> AssistCtx<'a> { label: impl Into, f: impl FnOnce(&mut ActionBuilder), ) -> Option { - let label = AssistLabel::new(label.into(), id); + let label = AssistLabel::new(id, label.into(), None); let mut info = AssistInfo::new(label); if self.should_compute_edit { @@ -116,7 +112,8 @@ impl<'a> AssistCtx<'a> { } pub(crate) fn add_assist_group(self, group_name: impl Into) -> AssistGroup<'a> { - AssistGroup { ctx: self, group_name: group_name.into(), assists: Vec::new() } + let group = GroupLabel(group_name.into()); + AssistGroup { ctx: self, group, assists: Vec::new() } } pub(crate) fn token_at_offset(&self) -> TokenAtOffset { @@ -146,7 +143,7 @@ impl<'a> AssistCtx<'a> { pub(crate) struct AssistGroup<'a> { ctx: AssistCtx<'a>, - group_name: String, + group: GroupLabel, assists: Vec, } @@ -157,9 +154,9 @@ impl<'a> AssistGroup<'a> { label: impl Into, f: impl FnOnce(&mut ActionBuilder), ) { - let label = AssistLabel::new(label.into(), id); + let label = AssistLabel::new(id, label.into(), Some(self.group.clone())); - let mut info = AssistInfo::new(label).with_group(GroupLabel(self.group_name.clone())); + let mut info = AssistInfo::new(label).with_group(self.group.clone()); if self.ctx.should_compute_edit { let action = { 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 c0f9bc1fbe7..f627f31dcd5 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) { ) }); - let actual = assist.action.edit.apply(&before); + let actual = { + let mut actual = before.clone(); + assist.action.edit.apply(&mut actual); + actual + }; assert_eq_text!(after, &actual); } 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 e466c9a86d8..e47feda71f2 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::{ use crate::{ ast_transform::{self, AstTransform, QualifyPaths, SubstituteTypeParams}, - utils::{get_missing_impl_items, resolve_target_trait}, + utils::{get_missing_assoc_items, resolve_target_trait}, Assist, AssistCtx, AssistId, }; @@ -112,25 +112,25 @@ fn add_missing_impl_members_inner( let trait_ = resolve_target_trait(&ctx.sema, &impl_node)?; - let def_name = |item: &ast::ImplItem| -> Option { + let def_name = |item: &ast::AssocItem| -> Option { match item { - ast::ImplItem::FnDef(def) => def.name(), - ast::ImplItem::TypeAliasDef(def) => def.name(), - ast::ImplItem::ConstDef(def) => def.name(), + ast::AssocItem::FnDef(def) => def.name(), + ast::AssocItem::TypeAliasDef(def) => def.name(), + ast::AssocItem::ConstDef(def) => def.name(), } .map(|it| it.text().clone()) }; - let missing_items = get_missing_impl_items(&ctx.sema, &impl_node) + let missing_items = get_missing_assoc_items(&ctx.sema, &impl_node) .iter() .map(|i| match i { - hir::AssocItem::Function(i) => ast::ImplItem::FnDef(i.source(ctx.db).value), - hir::AssocItem::TypeAlias(i) => ast::ImplItem::TypeAliasDef(i.source(ctx.db).value), - hir::AssocItem::Const(i) => ast::ImplItem::ConstDef(i.source(ctx.db).value), + hir::AssocItem::Function(i) => ast::AssocItem::FnDef(i.source(ctx.db).value), + hir::AssocItem::TypeAlias(i) => ast::AssocItem::TypeAliasDef(i.source(ctx.db).value), + hir::AssocItem::Const(i) => ast::AssocItem::ConstDef(i.source(ctx.db).value), }) .filter(|t| def_name(&t).is_some()) .filter(|t| match t { - ast::ImplItem::FnDef(def) => match mode { + ast::AssocItem::FnDef(def) => match mode { AddMissingImplMembersMode::DefaultMethodsOnly => def.body().is_some(), AddMissingImplMembersMode::NoDefaultMethods => def.body().is_none(), }, @@ -145,7 +145,7 @@ fn add_missing_impl_members_inner( let sema = ctx.sema; ctx.add_assist(AssistId(assist_id), label, |edit| { - let n_existing_items = impl_item_list.impl_items().count(); + let n_existing_items = impl_item_list.assoc_items().count(); let source_scope = sema.scope_for_def(trait_); let target_scope = sema.scope(impl_item_list.syntax()); let ast_transform = QualifyPaths::new(&target_scope, &source_scope) @@ -154,13 +154,13 @@ fn add_missing_impl_members_inner( .into_iter() .map(|it| ast_transform::apply(&*ast_transform, it)) .map(|it| match it { - ast::ImplItem::FnDef(def) => ast::ImplItem::FnDef(add_body(def)), + ast::AssocItem::FnDef(def) => ast::AssocItem::FnDef(add_body(def)), _ => it, }) .map(|it| edit::remove_attrs_and_docs(&it)); let new_impl_item_list = impl_item_list.append_items(items); let cursor_position = { - let first_new_item = new_impl_item_list.impl_items().nth(n_existing_items).unwrap(); + let first_new_item = new_impl_item_list.assoc_items().nth(n_existing_items).unwrap(); first_new_item.syntax().text_range().start() }; diff --git a/crates/ra_assists/src/handlers/add_new.rs b/crates/ra_assists/src/handlers/add_new.rs index 0f9174a2995..e8a36c7de99 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 bool { if let Some(il) = imp.item_list() { - for item in il.impl_items() { - if let ast::ImplItem::FnDef(f) = item { + for item in il.assoc_items() { + if let ast::AssocItem::FnDef(f) = item { if let Some(name) = f.name() { if name.text().eq_ignore_ascii_case("new") { return true; diff --git a/crates/ra_assists/src/handlers/change_visibility.rs b/crates/ra_assists/src/handlers/change_visibility.rs index 44f6a1dae02..1cd532e801d 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 { return None; } (vis_offset(&parent), keyword.text_range()) - } else { - let field_name: ast::Name = ctx.find_node_at_offset()?; + } else if let Some(field_name) = ctx.find_node_at_offset::() { let field = field_name.syntax().ancestors().find_map(ast::RecordFieldDef::cast)?; if field.name()? != field_name { tested_by!(change_visibility_field_false_positive); @@ -58,6 +57,13 @@ fn add_vis(ctx: AssistCtx) -> Option { return None; } (vis_offset(field.syntax()), field_name.syntax().text_range()) + } else if let Some(field) = ctx.find_node_at_offset::() { + if field.visibility().is_some() { + return None; + } + (vis_offset(field.syntax()), field.syntax().text_range()) + } else { + return None; }; ctx.add_assist(AssistId("change_visibility"), "Change visibility to pub(crate)", |edit| { @@ -129,7 +135,8 @@ mod tests { change_visibility, r"struct S { <|>field: u32 }", r"struct S { <|>pub(crate) field: u32 }", - ) + ); + check_assist(change_visibility, r"struct S ( <|>u32 )", r"struct S ( <|>pub(crate) u32 )"); } #[test] diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs index c5df86600f5..0f94f5ee89d 100644 --- a/crates/ra_assists/src/lib.rs +++ b/crates/ra_assists/src/lib.rs @@ -17,13 +17,13 @@ mod doc_tests; pub mod utils; pub mod ast_transform; +use hir::Semantics; use ra_db::{FileId, FileRange}; use ra_ide_db::RootDatabase; use ra_syntax::{TextRange, TextSize}; use ra_text_edit::TextEdit; -pub(crate) use crate::assist_ctx::{Assist, AssistCtx, AssistHandler}; -use hir::Semantics; +pub(crate) use crate::assist_ctx::{Assist, AssistCtx}; /// Unique identifier of the assist, should not be shown to the user /// directly. @@ -32,19 +32,20 @@ pub struct AssistId(pub &'static str); #[derive(Debug, Clone)] pub struct AssistLabel { + pub id: AssistId, /// Short description of the assist, as shown in the UI. pub label: String, - pub id: AssistId, + pub group: Option, } #[derive(Clone, Debug)] pub struct GroupLabel(pub String); impl AssistLabel { - pub(crate) fn new(label: String, id: AssistId) -> AssistLabel { + pub(crate) fn new(id: AssistId, label: String, group: Option) -> AssistLabel { // FIXME: make fields private, so that this invariant can't be broken assert!(label.starts_with(|c: char| c.is_uppercase())); - AssistLabel { label, id } + AssistLabel { id, label, group } } } @@ -60,7 +61,6 @@ pub struct AssistAction { #[derive(Debug, Clone)] pub struct ResolvedAssist { pub label: AssistLabel, - pub group_label: Option, pub action: AssistAction, } @@ -109,7 +109,9 @@ pub fn resolved_assists(db: &RootDatabase, range: FileRange) -> Vec Option; mod add_custom_impl; mod add_derive; @@ -145,12 +147,13 @@ mod handlers { mod reorder_fields; mod unwrap_block; - pub(crate) fn all() -> &'static [AssistHandler] { + pub(crate) fn all() -> &'static [Handler] { &[ // These are alphabetic for the foolish consistency add_custom_impl::add_custom_impl, add_derive::add_derive, add_explicit_type::add_explicit_type, + add_from_impl_for_enum::add_from_impl_for_enum, add_function::add_function, add_impl::add_impl, add_new::add_new, @@ -176,17 +179,18 @@ mod handlers { raw_string::remove_hash, remove_dbg::remove_dbg, remove_mut::remove_mut, + reorder_fields::reorder_fields, replace_if_let_with_match::replace_if_let_with_match, replace_let_with_if_let::replace_let_with_if_let, replace_qualified_name_with_use::replace_qualified_name_with_use, replace_unwrap_with_match::replace_unwrap_with_match, split_import::split_import, - add_from_impl_for_enum::add_from_impl_for_enum, unwrap_block::unwrap_block, // These are manually sorted for better priorities add_missing_impl_members::add_missing_impl_members, add_missing_impl_members::add_missing_default_members, - reorder_fields::reorder_fields, + // Are you sure you want to add new assist here, and not to the + // sorted list above? ] } } @@ -195,12 +199,12 @@ mod handlers { mod helpers { use std::sync::Arc; + use hir::Semantics; use ra_db::{fixture::WithFixture, FileId, FileRange, SourceDatabaseExt}; use ra_ide_db::{symbol_index::SymbolsDatabase, RootDatabase}; use test_utils::{add_cursor, assert_eq_text, extract_range_or_offset, RangeOrOffset}; - use crate::{AssistCtx, AssistFile, AssistHandler}; - use hir::Semantics; + use crate::{handlers::Handler, AssistCtx, AssistFile}; pub(crate) fn with_single_file(text: &str) -> (RootDatabase, FileId) { let (mut db, file_id) = RootDatabase::with_single_file(text); @@ -210,22 +214,18 @@ mod helpers { (db, file_id) } - pub(crate) fn check_assist( - assist: AssistHandler, - ra_fixture_before: &str, - ra_fixture_after: &str, - ) { + pub(crate) fn check_assist(assist: Handler, ra_fixture_before: &str, ra_fixture_after: &str) { check(assist, ra_fixture_before, ExpectedResult::After(ra_fixture_after)); } // FIXME: instead of having a separate function here, maybe use // `extract_ranges` and mark the target as ` ` in the // fixuture? - pub(crate) fn check_assist_target(assist: AssistHandler, ra_fixture: &str, target: &str) { + pub(crate) fn check_assist_target(assist: Handler, ra_fixture: &str, target: &str) { check(assist, ra_fixture, ExpectedResult::Target(target)); } - pub(crate) fn check_assist_not_applicable(assist: AssistHandler, ra_fixture: &str) { + pub(crate) fn check_assist_not_applicable(assist: Handler, ra_fixture: &str) { check(assist, ra_fixture, ExpectedResult::NotApplicable); } @@ -235,7 +235,7 @@ mod helpers { Target(&'a str), } - fn check(assist: AssistHandler, before: &str, expected: ExpectedResult) { + fn check(assist: Handler, before: &str, expected: ExpectedResult) { let (text_without_caret, file_with_caret_id, range_or_offset, db) = if before.contains("//-") { let (mut db, position) = RootDatabase::with_position(before); @@ -261,13 +261,13 @@ mod helpers { (Some(assist), ExpectedResult::After(after)) => { let action = assist.0[0].action.clone().unwrap(); - let assisted_file_text = if let AssistFile::TargetFile(file_id) = action.file { + let mut actual = if let AssistFile::TargetFile(file_id) = action.file { db.file_text(file_id).as_ref().to_owned() } else { text_without_caret }; + action.edit.apply(&mut actual); - let mut actual = action.edit.apply(&assisted_file_text); match action.cursor_position { None => { 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 6be704ce3af..2f15a3f1506 100644 --- a/crates/ra_assists/src/utils.rs +++ b/crates/ra_assists/src/utils.rs @@ -13,7 +13,7 @@ use rustc_hash::FxHashSet; pub(crate) use insert_use::insert_use_statement; -pub fn get_missing_impl_items( +pub fn get_missing_assoc_items( sema: &Semantics, impl_def: &ast::ImplDef, ) -> Vec { @@ -23,21 +23,21 @@ pub fn get_missing_impl_items( let mut impl_type = FxHashSet::default(); if let Some(item_list) = impl_def.item_list() { - for item in item_list.impl_items() { + for item in item_list.assoc_items() { match item { - ast::ImplItem::FnDef(f) => { + ast::AssocItem::FnDef(f) => { if let Some(n) = f.name() { impl_fns_consts.insert(n.syntax().to_string()); } } - ast::ImplItem::TypeAliasDef(t) => { + ast::AssocItem::TypeAliasDef(t) => { if let Some(n) = t.name() { impl_type.insert(n.syntax().to_string()); } } - ast::ImplItem::ConstDef(c) => { + ast::AssocItem::ConstDef(c) => { if let Some(n) = c.name() { impl_fns_consts.insert(n.syntax().to_string()); } diff --git a/crates/ra_flycheck/src/lib.rs b/crates/ra_flycheck/src/lib.rs index b54a30ab85f..f27252949bd 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; #[derive(Clone, Debug, PartialEq, Eq)] pub enum FlycheckConfig { - CargoCommand { command: String, all_targets: bool, extra_args: Vec }, + CargoCommand { command: String, all_targets: bool, all_features: bool, extra_args: Vec }, CustomCommand { command: String, args: Vec }, } @@ -215,7 +215,7 @@ impl FlycheckThread { self.check_process = None; let mut cmd = match &self.config { - FlycheckConfig::CargoCommand { command, all_targets, extra_args } => { + FlycheckConfig::CargoCommand { command, all_targets, all_features, extra_args } => { let mut cmd = Command::new(cargo_binary()); cmd.arg(command); cmd.args(&["--workspace", "--message-format=json", "--manifest-path"]); @@ -223,6 +223,9 @@ impl FlycheckThread { if *all_targets { cmd.arg("--all-targets"); } + if *all_features { + cmd.arg("--all-features"); + } cmd.args(extra_args); cmd } diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs index a004363ee1a..5f480c3040d 100644 --- a/crates/ra_hir/src/code_model.rs +++ b/crates/ra_hir/src/code_model.rs @@ -1211,7 +1211,7 @@ impl Type { // This would be nicer if it just returned an iterator, but that runs into // lifetime problems, because we need to borrow temp `CrateImplDefs`. - pub fn iterate_impl_items( + pub fn iterate_assoc_items( self, db: &dyn HirDatabase, krate: Crate, diff --git a/crates/ra_hir_def/src/adt.rs b/crates/ra_hir_def/src/adt.rs index d0912ddaa08..2bc34d449f2 100644 --- a/crates/ra_hir_def/src/adt.rs +++ b/crates/ra_hir_def/src/adt.rs @@ -118,11 +118,12 @@ fn lower_enum( module_id: ModuleId, ) { let expander = CfgExpander::new(db, ast.file_id, module_id.krate); - let variants = - ast.value.variant_list().into_iter().flat_map(|it| it.variants()).filter(|var| { - let attrs = expander.parse_attrs(var); - expander.is_cfg_enabled(&attrs) - }); + let variants = ast + .value + .variant_list() + .into_iter() + .flat_map(|it| it.variants()) + .filter(|var| expander.is_cfg_enabled(var)); for var in variants { trace.alloc( || var.clone(), @@ -215,8 +216,7 @@ fn lower_struct( match &ast.value { ast::StructKind::Tuple(fl) => { for (i, fd) in fl.fields().enumerate() { - let attrs = expander.parse_attrs(&fd); - if !expander.is_cfg_enabled(&attrs) { + if !expander.is_cfg_enabled(&fd) { continue; } @@ -233,8 +233,7 @@ fn lower_struct( } ast::StructKind::Record(fl) => { for fd in fl.fields() { - let attrs = expander.parse_attrs(&fd); - if !expander.is_cfg_enabled(&attrs) { + if !expander.is_cfg_enabled(&fd) { continue; } diff --git a/crates/ra_hir_def/src/body.rs b/crates/ra_hir_def/src/body.rs index 4edaad9600a..f5a7305dc09 100644 --- a/crates/ra_hir_def/src/body.rs +++ b/crates/ra_hir_def/src/body.rs @@ -60,7 +60,8 @@ impl CfgExpander { Attrs::new(owner, &self.hygiene) } - pub(crate) fn is_cfg_enabled(&self, attrs: &Attrs) -> bool { + pub(crate) fn is_cfg_enabled(&self, owner: &dyn ast::AttrsOwner) -> bool { + let attrs = self.parse_attrs(owner); attrs.is_cfg_enabled(&self.cfg_options) } } @@ -141,12 +142,8 @@ impl Expander { InFile { file_id: self.current_file_id, value } } - pub(crate) fn parse_attrs(&self, owner: &dyn ast::AttrsOwner) -> Attrs { - self.cfg_expander.parse_attrs(owner) - } - - pub(crate) fn is_cfg_enabled(&self, attrs: &Attrs) -> bool { - self.cfg_expander.is_cfg_enabled(attrs) + pub(crate) fn is_cfg_enabled(&self, owner: &dyn ast::AttrsOwner) -> bool { + self.cfg_expander.is_cfg_enabled(owner) } fn parse_path(&mut self, path: ast::Path) -> Option { diff --git a/crates/ra_hir_def/src/body/lower.rs b/crates/ra_hir_def/src/body/lower.rs index 687216dc35e..443b057abea 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<'_> { fn collect_expr(&mut self, expr: ast::Expr) -> ExprId { let syntax_ptr = AstPtr::new(&expr); - let attrs = self.expander.parse_attrs(&expr); - if !self.expander.is_cfg_enabled(&attrs) { + if !self.expander.is_cfg_enabled(&expr) { return self.missing_expr(); } match expr { @@ -329,8 +328,7 @@ impl ExprCollector<'_> { .fields() .inspect(|field| field_ptrs.push(AstPtr::new(field))) .filter_map(|field| { - let attrs = self.expander.parse_attrs(&field); - if !self.expander.is_cfg_enabled(&attrs) { + if !self.expander.is_cfg_enabled(&field) { return None; } let name = field.field_name()?.as_name(); @@ -575,9 +573,16 @@ impl ExprCollector<'_> { self.body.item_scope.define_def(def); if let Some(name) = name { let vis = crate::visibility::Visibility::Public; // FIXME determine correctly - self.body - .item_scope - .push_res(name.as_name(), crate::per_ns::PerNs::from_def(def, vis)); + let has_constructor = match def { + ModuleDefId::AdtId(AdtId::StructId(s)) => { + self.db.struct_data(s).variant_data.kind() != StructKind::Record + } + _ => true, + }; + self.body.item_scope.push_res( + name.as_name(), + crate::per_ns::PerNs::from_def(def, vis, has_constructor), + ); } } } diff --git a/crates/ra_hir_def/src/data.rs b/crates/ra_hir_def/src/data.rs index d4cba4d05ee..e7eb2bb1194 100644 --- a/crates/ra_hir_def/src/data.rs +++ b/crates/ra_hir_def/src/data.rs @@ -9,7 +9,7 @@ use hir_expand::{ }; use ra_prof::profile; use ra_syntax::ast::{ - self, AstNode, ImplItem, ModuleItemOwner, NameOwner, TypeAscriptionOwner, TypeBoundsOwner, + self, AssocItem, AstNode, ModuleItemOwner, NameOwner, TypeAscriptionOwner, TypeBoundsOwner, VisibilityOwner, }; @@ -164,7 +164,7 @@ impl TraitData { items.extend(collect_items( db, &mut expander, - item_list.impl_items(), + item_list.assoc_items(), src.file_id, container, )); @@ -219,7 +219,7 @@ impl ImplData { if let Some(item_list) = src.value.item_list() { let mut expander = Expander::new(db, impl_loc.ast_id.file_id, module_id); items.extend( - collect_items(db, &mut expander, item_list.impl_items(), src.file_id, container) + collect_items(db, &mut expander, item_list.assoc_items(), src.file_id, container) .into_iter() .map(|(_, item)| item), ); @@ -304,7 +304,7 @@ fn collect_items_in_macro( let mut res = collect_items( db, expander, - items.value.items().filter_map(|it| ImplItem::cast(it.syntax().clone())), + items.value.items().filter_map(|it| AssocItem::cast(it.syntax().clone())), items.file_id, container, ); @@ -325,31 +325,30 @@ fn collect_items_in_macro( fn collect_items( db: &dyn DefDatabase, expander: &mut Expander, - impl_items: impl Iterator, + assoc_items: impl Iterator, file_id: crate::HirFileId, container: AssocContainerId, ) -> Vec<(Name, AssocItemId)> { let items = db.ast_id_map(file_id); - impl_items + assoc_items .filter_map(|item_node| match item_node { - ast::ImplItem::FnDef(it) => { + ast::AssocItem::FnDef(it) => { let name = it.name().map_or_else(Name::missing, |it| it.as_name()); - let attrs = expander.parse_attrs(&it); - if !expander.is_cfg_enabled(&attrs) { + if !expander.is_cfg_enabled(&it) { return None; } let def = FunctionLoc { container, ast_id: AstId::new(file_id, items.ast_id(&it)) } .intern(db); Some((name, def.into())) } - ast::ImplItem::ConstDef(it) => { + ast::AssocItem::ConstDef(it) => { let name = it.name().map_or_else(Name::missing, |it| it.as_name()); let def = ConstLoc { container, ast_id: AstId::new(file_id, items.ast_id(&it)) } .intern(db); Some((name, def.into())) } - ast::ImplItem::TypeAliasDef(it) => { + ast::AssocItem::TypeAliasDef(it) => { let name = it.name().map_or_else(Name::missing, |it| it.as_name()); let def = 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 259b9ff035d..fc15948adf4 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 { } impl PerNs { - pub(crate) fn from_def(def: ModuleDefId, v: Visibility) -> PerNs { + pub(crate) fn from_def(def: ModuleDefId, v: Visibility, has_constructor: bool) -> PerNs { match def { ModuleDefId::ModuleId(_) => PerNs::types(def, v), ModuleDefId::FunctionId(_) => PerNs::values(def, v), ModuleDefId::AdtId(adt) => match adt { - AdtId::StructId(_) | AdtId::UnionId(_) => PerNs::both(def, def, v), + AdtId::UnionId(_) => PerNs::types(def, v), AdtId::EnumId(_) => PerNs::types(def, v), + AdtId::StructId(_) => { + if has_constructor { + PerNs::both(def, def, v) + } else { + PerNs::types(def, v) + } + } }, ModuleDefId::EnumVariantId(_) => PerNs::both(def, def, v), 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 bf3968bd62a..db994122ae9 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<'_, '_> { let module = ModuleId { krate: self.def_collector.def_map.krate, local_id: res }; let def: ModuleDefId = module.into(); self.def_collector.def_map.modules[self.module_id].scope.define_def(def); - self.def_collector.update(self.module_id, &[(name, PerNs::from_def(def, vis))], vis); + self.def_collector.update(self.module_id, &[(name, PerNs::from_def(def, vis, false))], vis); res } @@ -844,6 +844,8 @@ impl ModCollector<'_, '_> { let name = def.name.clone(); let container = ContainerId::ModuleId(module); let vis = &def.visibility; + let mut has_constructor = false; + let def: ModuleDefId = match def.kind { raw::DefKind::Function(ast_id) => FunctionLoc { container: container.into(), @@ -851,7 +853,8 @@ impl ModCollector<'_, '_> { } .intern(self.def_collector.db) .into(), - raw::DefKind::Struct(ast_id) => { + raw::DefKind::Struct(ast_id, mode) => { + has_constructor = mode != raw::StructDefKind::Record; StructLoc { container, ast_id: AstId::new(self.file_id, ast_id) } .intern(self.def_collector.db) .into() @@ -894,7 +897,11 @@ impl ModCollector<'_, '_> { .def_map .resolve_visibility(self.def_collector.db, self.module_id, vis) .unwrap_or(Visibility::Public); - self.def_collector.update(self.module_id, &[(name, PerNs::from_def(def, vis))], vis) + self.def_collector.update( + self.module_id, + &[(name, PerNs::from_def(def, vis, has_constructor))], + vis, + ) } 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 a71503c76ec..f2716a2950b 100644 --- a/crates/ra_hir_def/src/nameres/raw.rs +++ b/crates/ra_hir_def/src/nameres/raw.rs @@ -155,10 +155,17 @@ pub(super) struct DefData { pub(super) visibility: RawVisibility, } +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub(super) enum StructDefKind { + Record, + Tuple, + Unit, +} + #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub(super) enum DefKind { Function(FileAstId), - Struct(FileAstId), + Struct(FileAstId, StructDefKind), Union(FileAstId), Enum(FileAstId), Const(FileAstId), @@ -171,7 +178,7 @@ impl DefKind { pub fn ast_id(&self) -> FileAstId { match self { DefKind::Function(it) => it.upcast(), - DefKind::Struct(it) => it.upcast(), + DefKind::Struct(it, _) => it.upcast(), DefKind::Union(it) => it.upcast(), DefKind::Enum(it) => it.upcast(), DefKind::Const(it) => it.upcast(), @@ -236,9 +243,14 @@ impl RawItemsCollector { return; } ast::ModuleItem::StructDef(it) => { + let kind = match it.kind() { + ast::StructKind::Record(_) => StructDefKind::Record, + ast::StructKind::Tuple(_) => StructDefKind::Tuple, + ast::StructKind::Unit => StructDefKind::Unit, + }; let id = self.source_ast_id_map.ast_id(&it); let name = it.name(); - (DefKind::Struct(id), name) + (DefKind::Struct(id, kind), name) } ast::ModuleItem::UnionDef(it) => { 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 83120fa365d..1b66c1aacfa 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() { ⋮Baz: t v ⋮E: t ⋮EXT: v - ⋮U: t v + ⋮U: t ⋮ext: v "###) } diff --git a/crates/ra_hir_def/src/nameres/tests/macros.rs b/crates/ra_hir_def/src/nameres/tests/macros.rs index 9bc0e628747..40289e3ca52 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() { ); assert_snapshot!(map, @r###" ⋮crate - ⋮Foo: t v + ⋮Foo: t ⋮nested: t ⋮ ⋮crate::nested - ⋮Bar: t v - ⋮Baz: t v + ⋮Bar: t + ⋮Baz: t "###); } @@ -91,13 +91,13 @@ fn macro_rules_from_other_crates_are_visible() { ); assert_snapshot!(map, @r###" ⋮crate - ⋮Bar: t v - ⋮Foo: t v + ⋮Bar: t + ⋮Foo: t ⋮bar: t ⋮ ⋮crate::bar - ⋮Bar: t v - ⋮Foo: t v + ⋮Bar: t + ⋮Foo: t ⋮bar: t "###); } @@ -124,13 +124,13 @@ fn macro_rules_export_with_local_inner_macros_are_visible() { ); assert_snapshot!(map, @r###" ⋮crate - ⋮Bar: t v - ⋮Foo: t v + ⋮Bar: t + ⋮Foo: t ⋮bar: t ⋮ ⋮crate::bar - ⋮Bar: t v - ⋮Foo: t v + ⋮Bar: t + ⋮Foo: t ⋮bar: t "###); } @@ -161,13 +161,13 @@ fn local_inner_macros_makes_local_macros_usable() { ); assert_snapshot!(map, @r###" ⋮crate - ⋮Bar: t v - ⋮Foo: t v + ⋮Bar: t + ⋮Foo: t ⋮bar: t ⋮ ⋮crate::bar - ⋮Bar: t v - ⋮Foo: t v + ⋮Bar: t + ⋮Foo: t ⋮bar: t "###); } @@ -204,7 +204,7 @@ fn unexpanded_macro_should_expand_by_fixedpoint_loop() { ); assert_snapshot!(map, @r###" ⋮crate - ⋮Foo: t v + ⋮Foo: t ⋮bar: m ⋮foo: m "###); diff --git a/crates/ra_hir_ty/src/tests/macros.rs b/crates/ra_hir_ty/src/tests/macros.rs index 29e38a06c46..07398ddcc57 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()<|>; } } #[test] -fn infer_impl_items_generated_by_macros() { +fn infer_assoc_items_generated_by_macros() { let t = type_at( r#" //- /main.rs @@ -288,7 +288,7 @@ fn test() { S.foo()<|>; } } #[test] -fn infer_impl_items_generated_by_macros_chain() { +fn infer_assoc_items_generated_by_macros_chain() { let t = type_at( r#" //- /main.rs diff --git a/crates/ra_hir_ty/src/tests/regression.rs b/crates/ra_hir_ty/src/tests/regression.rs index 8a1292c7aa0..115ad832891 100644 --- a/crates/ra_hir_ty/src/tests/regression.rs +++ b/crates/ra_hir_ty/src/tests/regression.rs @@ -534,6 +534,35 @@ fn foo(b: Bar) { ); } +#[test] +fn issue_4235_name_conflicts() { + assert_snapshot!( + infer(r#" +struct FOO {} +static FOO:FOO = FOO {}; + +impl FOO { + fn foo(&self) {} +} + +fn main() { + let a = &FOO; + a.foo(); +} +"#), @r###" + 32..38 'FOO {}': FOO + 64..68 'self': &FOO + 70..72 '{}': () + 86..120 '{ ...o(); }': () + 96..97 'a': &FOO + 100..104 '&FOO': &FOO + 101..104 'FOO': FOO + 110..111 'a': &FOO + 110..117 'a.foo()': () +"### + ); +} + #[test] fn issue_4053_diesel_where_clauses() { assert_snapshot!( diff --git a/crates/ra_ide/src/assists.rs b/crates/ra_ide/src/assists.rs index 2b5d11681af..389339a0344 100644 --- a/crates/ra_ide/src/assists.rs +++ b/crates/ra_ide/src/assists.rs @@ -1,6 +1,6 @@ //! FIXME: write short doc here -use ra_assists::{resolved_assists, AssistAction, AssistLabel}; +use ra_assists::{resolved_assists, AssistAction}; use ra_db::{FilePosition, FileRange}; use ra_ide_db::RootDatabase; @@ -21,27 +21,22 @@ pub(crate) fn assists(db: &RootDatabase, frange: FileRange) -> Vec { .into_iter() .map(|assist| { let file_id = frange.file_id; - let assist_label = &assist.label; Assist { - id: assist_label.id, - label: assist_label.label.clone(), - group_label: assist.group_label.map(|it| it.0), - source_change: action_to_edit(assist.action, file_id, assist_label), + id: assist.label.id, + label: assist.label.label.clone(), + group_label: assist.label.group.map(|it| it.0), + source_change: action_to_edit(assist.action, file_id, assist.label.label.clone()), } }) .collect() } -fn action_to_edit( - action: AssistAction, - file_id: FileId, - assist_label: &AssistLabel, -) -> SourceChange { +fn action_to_edit(action: AssistAction, file_id: FileId, label: String) -> SourceChange { let file_id = match action.file { ra_assists::AssistFile::TargetFile(it) => it, _ => file_id, }; let file_edit = SourceFileEdit { file_id, edit: action.edit }; - SourceChange::source_file_edit(assist_label.label.clone(), file_edit) + SourceChange::source_file_edit(label, file_edit) .with_cursor_opt(action.cursor_position.map(|offset| FilePosition { offset, file_id })) } diff --git a/crates/ra_ide/src/completion/complete_qualified_path.rs b/crates/ra_ide/src/completion/complete_qualified_path.rs index d9ea92ef8df..7fcd2252596 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 }); // Iterate assoc types separately - ty.iterate_impl_items(ctx.db, krate, |item| { + ty.iterate_assoc_items(ctx.db, krate, |item| { if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) { return None; } diff --git a/crates/ra_ide/src/completion/complete_trait_impl.rs b/crates/ra_ide/src/completion/complete_trait_impl.rs index ee32d1ff600..039df03e054 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 @@ //! ``` use hir::{self, Docs, HasSource}; -use ra_assists::utils::get_missing_impl_items; +use ra_assists::utils::get_missing_assoc_items; use ra_syntax::{ ast::{self, edit, ImplDef}, AstNode, SyntaxKind, SyntaxNode, TextRange, T, @@ -50,7 +50,7 @@ pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext if let Some((trigger, impl_def)) = completion_match(ctx) { match trigger.kind() { SyntaxKind::NAME_REF => { - get_missing_impl_items(&ctx.sema, &impl_def).iter().for_each(|item| match item { + get_missing_assoc_items(&ctx.sema, &impl_def).iter().for_each(|item| match item { hir::AssocItem::Function(fn_item) => { add_function_impl(&trigger, acc, ctx, &fn_item) } @@ -64,34 +64,40 @@ pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext } SyntaxKind::FN_DEF => { - for missing_fn in get_missing_impl_items(&ctx.sema, &impl_def).iter().filter_map( - |item| match item { - hir::AssocItem::Function(fn_item) => Some(fn_item), - _ => None, - }, - ) { + for missing_fn in + get_missing_assoc_items(&ctx.sema, &impl_def).iter().filter_map(|item| { + match item { + hir::AssocItem::Function(fn_item) => Some(fn_item), + _ => None, + } + }) + { add_function_impl(&trigger, acc, ctx, &missing_fn); } } SyntaxKind::TYPE_ALIAS_DEF => { - for missing_fn in get_missing_impl_items(&ctx.sema, &impl_def).iter().filter_map( - |item| match item { - hir::AssocItem::TypeAlias(type_item) => Some(type_item), - _ => None, - }, - ) { + for missing_fn in + get_missing_assoc_items(&ctx.sema, &impl_def).iter().filter_map(|item| { + match item { + hir::AssocItem::TypeAlias(type_item) => Some(type_item), + _ => None, + } + }) + { add_type_alias_impl(&trigger, acc, ctx, &missing_fn); } } SyntaxKind::CONST_DEF => { - for missing_fn in get_missing_impl_items(&ctx.sema, &impl_def).iter().filter_map( - |item| match item { - hir::AssocItem::Const(const_item) => Some(const_item), - _ => None, - }, - ) { + for missing_fn in + get_missing_assoc_items(&ctx.sema, &impl_def).iter().filter_map(|item| { + match item { + hir::AssocItem::Const(const_item) => Some(const_item), + _ => None, + } + }) + { add_const_impl(&trigger, acc, ctx, &missing_fn); } } diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs index dd87bd119e1..b6b9627deab 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::{ SyntaxKind::*, SyntaxNode, SyntaxToken, TextRange, TextSize, }; -use ra_text_edit::AtomTextEdit; +use ra_text_edit::Indel; use crate::{call_info::ActiveParameter, completion::CompletionConfig, FilePosition}; @@ -76,7 +76,7 @@ impl<'a> CompletionContext<'a> { // actual completion. let file_with_fake_ident = { let parse = db.parse(position.file_id); - let edit = AtomTextEdit::insert(position.offset, "intellijRulezz".to_string()); + let edit = Indel::insert(position.offset, "intellijRulezz".to_string()); parse.reparse(&edit).tree() }; 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 5936fb8f7c2..383b23ac44e 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 { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let mut s = f.debug_struct("CompletionItem"); s.field("label", &self.label()).field("source_range", &self.source_range()); - if self.text_edit().as_atoms().len() == 1 { - let atom = &self.text_edit().as_atoms()[0]; + if self.text_edit().as_indels().len() == 1 { + let atom = &self.text_edit().as_indels()[0]; s.field("delete", &atom.delete); s.field("insert", &atom.insert); } else { diff --git a/crates/ra_ide/src/diagnostics.rs b/crates/ra_ide/src/diagnostics.rs index a6b4c2c2847..87a0b80f13e 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 .unwrap_or_else(|| RelativePath::new("")) .join(&d.candidate); let create_file = FileSystemEdit::CreateFile { source_root, path }; - let fix = SourceChange::file_system_edit("create module", create_file); + let fix = SourceChange::file_system_edit("Create module", create_file); res.borrow_mut().push(Diagnostic { range: sema.diagnostics_range(d).range, message: d.message(), @@ -92,7 +92,7 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec algo::diff(&d.ast(db).syntax(), &field_list.syntax()).into_text_edit(&mut builder); Some(SourceChange::source_file_edit_from( - "fill struct fields", + "Fill struct fields", file_id, builder.finish(), )) @@ -117,7 +117,7 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec let node = d.ast(db); let replacement = format!("Ok({})", node.syntax()); let edit = TextEdit::replace(node.syntax().text_range(), replacement); - let fix = SourceChange::source_file_edit_from("wrap with ok", file_id, edit); + let fix = SourceChange::source_file_edit_from("Wrap with ok", file_id, edit); res.borrow_mut().push(Diagnostic { range: sema.diagnostics_range(d).range, message: d.message(), @@ -199,7 +199,7 @@ fn check_struct_shorthand_initialization( message: "Shorthand struct initialization".to_string(), severity: Severity::WeakWarning, fix: Some(SourceChange::source_file_edit( - "use struct shorthand initialization", + "Use struct shorthand initialization", SourceFileEdit { file_id, edit }, )), }); @@ -241,7 +241,11 @@ mod tests { diagnostics.pop().unwrap_or_else(|| panic!("no diagnostics for:\n{}\n", before)); let mut fix = diagnostic.fix.unwrap(); let edit = fix.source_file_edits.pop().unwrap().edit; - let actual = edit.apply(&before); + let actual = { + let mut actual = before.to_string(); + edit.apply(&mut actual); + actual + }; assert_eq_text!(after, &actual); } @@ -256,7 +260,11 @@ mod tests { let mut fix = diagnostic.fix.unwrap(); let edit = fix.source_file_edits.pop().unwrap().edit; let target_file_contents = analysis.file_text(file_position.file_id).unwrap(); - let actual = edit.apply(&target_file_contents); + let actual = { + let mut actual = target_file_contents.to_string(); + edit.apply(&mut actual); + actual + }; // Strip indent and empty lines from `after`, to match the behaviour of // `parse_fixture` called from `analysis_and_position`. @@ -288,7 +296,11 @@ mod tests { let diagnostic = analysis.diagnostics(file_id).unwrap().pop().unwrap(); let mut fix = diagnostic.fix.unwrap(); let edit = fix.source_file_edits.pop().unwrap().edit; - let actual = edit.apply(&before); + let actual = { + let mut actual = before.to_string(); + edit.apply(&mut actual); + actual + }; assert_eq_text!(after, &actual); } @@ -606,7 +618,7 @@ mod tests { range: 0..8, fix: Some( SourceChange { - label: "create module", + label: "Create module", source_file_edits: [], file_system_edits: [ CreateFile { @@ -655,17 +667,17 @@ mod tests { range: 224..233, fix: Some( SourceChange { - label: "fill struct fields", + label: "Fill struct fields", source_file_edits: [ SourceFileEdit { file_id: FileId( 1, ), edit: TextEdit { - atoms: [ - AtomTextEdit { - delete: 3..9, + indels: [ + Indel { insert: "{a:42, b: ()}", + delete: 3..9, }, ], }, diff --git a/crates/ra_ide/src/join_lines.rs b/crates/ra_ide/src/join_lines.rs index 63fd6b3e457..d3af780c450 100644 --- a/crates/ra_ide/src/join_lines.rs +++ b/crates/ra_ide/src/join_lines.rs @@ -569,7 +569,11 @@ fn foo() { let (sel, before) = extract_range(before); let parse = SourceFile::parse(&before); let result = join_lines(&parse.tree(), sel); - let actual = result.apply(&before); + let actual = { + let mut actual = before.to_string(); + result.apply(&mut actual); + actual + }; assert_eq_text!(after, &actual); } diff --git a/crates/ra_ide/src/references/rename.rs b/crates/ra_ide/src/references/rename.rs index 916edaef27e..0398d53bc92 100644 --- a/crates/ra_ide/src/references/rename.rs +++ b/crates/ra_ide/src/references/rename.rs @@ -122,7 +122,7 @@ fn rename_mod( source_file_edits.extend(ref_edits); } - Some(SourceChange::from_edits("rename", source_file_edits, file_system_edits)) + Some(SourceChange::from_edits("Rename", source_file_edits, file_system_edits)) } fn rename_reference( @@ -141,7 +141,7 @@ fn rename_reference( return None; } - Some(RangeInfo::new(range, SourceChange::source_file_edits("rename", edit))) + Some(RangeInfo::new(range, SourceChange::source_file_edits("Rename", edit))) } #[cfg(test)] @@ -530,17 +530,17 @@ mod tests { RangeInfo { range: 4..7, info: SourceChange { - label: "rename", + label: "Rename", source_file_edits: [ SourceFileEdit { file_id: FileId( 2, ), edit: TextEdit { - atoms: [ - AtomTextEdit { - delete: 4..7, + indels: [ + Indel { insert: "foo2", + delete: 4..7, }, ], }, @@ -582,17 +582,17 @@ mod tests { RangeInfo { range: 4..7, info: SourceChange { - label: "rename", + label: "Rename", source_file_edits: [ SourceFileEdit { file_id: FileId( 1, ), edit: TextEdit { - atoms: [ - AtomTextEdit { - delete: 4..7, + indels: [ + Indel { insert: "foo2", + delete: 4..7, }, ], }, @@ -665,17 +665,17 @@ mod tests { RangeInfo { range: 8..11, info: SourceChange { - label: "rename", + label: "Rename", source_file_edits: [ SourceFileEdit { file_id: FileId( 2, ), edit: TextEdit { - atoms: [ - AtomTextEdit { - delete: 8..11, + indels: [ + Indel { insert: "foo2", + delete: 8..11, }, ], }, @@ -685,10 +685,10 @@ mod tests { 1, ), edit: TextEdit { - atoms: [ - AtomTextEdit { - delete: 27..30, + indels: [ + Indel { insert: "foo2", + delete: 27..30, }, ], }, @@ -720,13 +720,13 @@ mod tests { if let Some(change) = source_change { for edit in change.info.source_file_edits { file_id = Some(edit.file_id); - for atom in edit.edit.as_atoms() { - text_edit_builder.replace(atom.delete, atom.insert.clone()); + for indel in edit.edit.as_indels() { + text_edit_builder.replace(indel.delete, indel.insert.clone()); } } } - let result = - text_edit_builder.finish().apply(&*analysis.file_text(file_id.unwrap()).unwrap()); + let mut result = analysis.file_text(file_id.unwrap()).unwrap().to_string(); + text_edit_builder.finish().apply(&mut result); assert_eq_text!(expected, &*result); } } diff --git a/crates/ra_ide/src/runnables.rs b/crates/ra_ide/src/runnables.rs index 38637c19c20..fa8a9d92c99 100644 --- a/crates/ra_ide/src/runnables.rs +++ b/crates/ra_ide/src/runnables.rs @@ -9,6 +9,7 @@ use ra_syntax::{ }; use crate::FileId; +use ast::DocCommentsOwner; use std::fmt::Display; #[derive(Debug)] @@ -37,6 +38,7 @@ pub enum RunnableKind { Test { test_id: TestId, attr: TestAttr }, TestMod { path: String }, Bench { test_id: TestId }, + DocTest { test_id: TestId }, Bin, } @@ -81,6 +83,8 @@ fn runnable_fn(sema: &Semantics, fn_def: ast::FnDef) -> Option bool { .any(|attribute_text| attribute_text.contains("test")) } +fn has_doc_test(fn_def: &ast::FnDef) -> bool { + fn_def.doc_comment_text().map_or(false, |comment| comment.contains("```")) +} + fn runnable_mod(sema: &Semantics, module: ast::Module) -> Option { let has_test_function = module .item_list()? @@ -194,6 +202,41 @@ mod tests { ); } + #[test] + fn test_runnables_doc_test() { + let (analysis, pos) = analysis_and_position( + r#" + //- /lib.rs + <|> //empty + fn main() {} + + /// ``` + /// let x = 5; + /// ``` + fn foo() {} + "#, + ); + let runnables = analysis.runnables(pos.file_id).unwrap(); + assert_debug_snapshot!(&runnables, + @r###" + [ + Runnable { + range: 1..21, + kind: Bin, + }, + Runnable { + range: 22..64, + kind: DocTest { + test_id: Path( + "foo", + ), + }, + }, + ] + "### + ); + } + #[test] fn test_runnables_module() { let (analysis, pos) = analysis_and_position( diff --git a/crates/ra_ide/src/source_change.rs b/crates/ra_ide/src/source_change.rs index 71b0e8f757e..10afd78256e 100644 --- a/crates/ra_ide/src/source_change.rs +++ b/crates/ra_ide/src/source_change.rs @@ -35,8 +35,10 @@ impl SourceChange { /// Creates a new SourceChange with the given label, /// containing only the given `SourceFileEdits`. pub(crate) fn source_file_edits>(label: L, edits: Vec) -> Self { + let label = label.into(); + assert!(label.starts_with(char::is_uppercase)); SourceChange { - label: label.into(), + label: label, source_file_edits: edits, file_system_edits: vec![], cursor_position: None, diff --git a/crates/ra_ide/src/ssr.rs b/crates/ra_ide/src/ssr.rs index 7b93ff2d28e..8bf52d0fa82 100644 --- a/crates/ra_ide/src/ssr.rs +++ b/crates/ra_ide/src/ssr.rs @@ -401,16 +401,22 @@ fn render_replace( ignored_comments: &Vec, template: &SsrTemplate, ) -> String { - let mut builder = TextEditBuilder::default(); - for element in template.template.descendants() { - if let Some(var) = template.placeholders.get(&element) { - builder.replace(element.text_range(), binding[var].to_string()) + let edit = { + let mut builder = TextEditBuilder::default(); + for element in template.template.descendants() { + if let Some(var) = template.placeholders.get(&element) { + builder.replace(element.text_range(), binding[var].to_string()) + } } - } - for comment in ignored_comments { - builder.insert(template.template.text_range().end(), comment.syntax().to_string()) - } - builder.finish().apply(&template.template.text().to_string()) + for comment in ignored_comments { + builder.insert(template.template.text_range().end(), comment.syntax().to_string()) + } + builder.finish() + }; + + let mut text = template.template.text().to_string(); + edit.apply(&mut text); + text } #[cfg(test)] @@ -505,7 +511,9 @@ mod tests { ); let edit = replace(&matches, &query.template); - assert_eq!(edit.apply(input), "fn main() { bar(1+2); }"); + let mut after = input.to_string(); + edit.apply(&mut after); + assert_eq!(after, "fn main() { bar(1+2); }"); } fn assert_ssr_transform(query: &str, input: &str, result: &str) { @@ -513,7 +521,9 @@ mod tests { let code = SourceFile::parse(input).tree(); let matches = find(&query.pattern, code.syntax()); let edit = replace(&matches, &query.template); - assert_eq!(edit.apply(input), result); + let mut after = input.to_string(); + edit.apply(&mut after); + assert_eq!(after, result); } #[test] diff --git a/crates/ra_ide/src/test_utils.rs b/crates/ra_ide/src/test_utils.rs index f14533e14bb..48c8fd1f466 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 Option>( let (before_cursor_pos, before) = extract_offset(before); let file = SourceFile::parse(&before).ok().unwrap(); let result = f(&file, before_cursor_pos).expect("code action is not applicable"); - let actual = result.apply(&before); + let actual = { + let mut actual = before.to_string(); + result.apply(&mut actual); + actual + }; let actual_cursor_pos = result.apply_to_offset(before_cursor_pos).expect("cursor position is affected by the edit"); 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 2a8b4327f9c..a03da469380 100644 --- a/crates/ra_ide/src/typing.rs +++ b/crates/ra_ide/src/typing.rs @@ -142,10 +142,13 @@ mod tests { fn do_type_char(char_typed: char, before: &str) -> Option<(String, SingleFileChange)> { let (offset, before) = extract_offset(before); let edit = TextEdit::insert(offset, char_typed.to_string()); - let before = edit.apply(&before); + let mut before = before.to_string(); + edit.apply(&mut before); let parse = SourceFile::parse(&before); - on_char_typed_inner(&parse.tree(), offset, char_typed) - .map(|it| (it.edit.apply(&before), it)) + on_char_typed_inner(&parse.tree(), offset, char_typed).map(|it| { + it.edit.apply(&mut before); + (before.to_string(), it) + }) } 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 30c8c557201..78a40cc94c4 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 TextSize { self.end - self.start } + + /// Returns the length in 16-bit UTF-16 code units. + fn len_utf16(&self) -> usize { + if self.len() == TextSize::from(4) { + 2 + } else { + 1 + } + } } impl LineIndex { @@ -110,7 +120,7 @@ impl LineIndex { if let Some(utf16_chars) = self.utf16_lines.get(&line) { for c in utf16_chars { if c.end <= col { - res -= usize::from(c.len()) - 1; + res -= usize::from(c.len()) - c.len_utf16(); } else { // From here on, all utf16 characters come *after* the character we are mapping, // so we don't need to take them into account @@ -125,7 +135,7 @@ impl LineIndex { if let Some(utf16_chars) = self.utf16_lines.get(&line) { for c in utf16_chars { if col > u32::from(c.start) { - col += u32::from(c.len()) - 1; + col += u32::from(c.len()) - c.len_utf16() as u32; } else { // From here on, all utf16 characters come *after* the character we are mapping, // so we don't need to take them into account @@ -204,6 +214,9 @@ const C: char = 'メ'; // UTF-16 to UTF-8 assert_eq!(col_index.utf16_to_utf8_col(1, 19), TextSize::from(21)); + + let col_index = LineIndex::new("a𐐏b"); + assert_eq!(col_index.utf16_to_utf8_col(0, 3), TextSize::from(5)); } #[test] diff --git a/crates/ra_ide_db/src/line_index_utils.rs b/crates/ra_ide_db/src/line_index_utils.rs index 039a12c0d84..7fa6fc448e0 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 @@ use std::convert::TryInto; use ra_syntax::{TextRange, TextSize}; -use ra_text_edit::{AtomTextEdit, TextEdit}; +use ra_text_edit::{Indel, TextEdit}; use crate::line_index::{LineCol, LineIndex, Utf16Char}; @@ -182,14 +182,14 @@ struct TranslatedEdit<'a> { } struct Edits<'a> { - edits: &'a [AtomTextEdit], + edits: &'a [Indel], current: Option>, acc_diff: i64, } impl<'a> Edits<'a> { fn from_text_edit(text_edit: &'a TextEdit) -> Edits<'a> { - let mut x = Edits { edits: text_edit.as_atoms(), current: None, acc_diff: 0 }; + let mut x = Edits { edits: text_edit.as_indels(), current: None, acc_diff: 0 }; x.advance_edit(); x } diff --git a/crates/ra_mbe/src/syntax_bridge.rs b/crates/ra_mbe/src/syntax_bridge.rs index bb28acfd95d..fc4133a6753 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 // * Items(SmallVec<[P; 1]>) -> token_tree_to_items // // * TraitItems(SmallVec<[ast::TraitItem; 1]>) -// * ImplItems(SmallVec<[ast::ImplItem; 1]>) +// * AssocItems(SmallVec<[ast::AssocItem; 1]>) // * ForeignItems(SmallVec<[ast::ForeignItem; 1]> pub fn token_tree_to_syntax_node( diff --git a/crates/ra_project_model/src/cargo_workspace.rs b/crates/ra_project_model/src/cargo_workspace.rs index 362ee30fe04..59f46a2a05e 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 { /// Runs cargo check on launch to figure out the correct values of OUT_DIR pub load_out_dirs_from_check: bool, + + /// rustc target + pub target: Option, } impl Default for CargoConfig { @@ -65,6 +68,7 @@ impl Default for CargoConfig { all_features: true, features: Vec::new(), load_out_dirs_from_check: false, + target: None, } } } @@ -160,6 +164,9 @@ impl CargoWorkspace { if let Some(parent) = cargo_toml.parent() { meta.current_dir(parent); } + if let Some(target) = cargo_features.target.as_ref() { + meta.other_options(&[String::from("--filter-platform"), target.clone()]); + } let meta = meta.exec().with_context(|| { format!("Failed to run `cargo metadata --manifest-path {}`", cargo_toml.display()) })?; diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs index 731cbd29181..c2b33c1dcaf 100644 --- a/crates/ra_project_model/src/lib.rs +++ b/crates/ra_project_model/src/lib.rs @@ -543,7 +543,7 @@ impl ProjectWorkspace { } } -pub fn get_rustc_cfg_options() -> CfgOptions { +pub fn get_rustc_cfg_options(target: Option<&String>) -> CfgOptions { let mut cfg_options = CfgOptions::default(); // Some nightly-only cfgs, which are required for stdlib @@ -558,10 +558,12 @@ pub fn get_rustc_cfg_options() -> CfgOptions { match (|| -> Result { // `cfg(test)` and `cfg(debug_assertion)` are handled outside, so we suppress them here. - let output = Command::new("rustc") - .args(&["--print", "cfg", "-O"]) - .output() - .context("Failed to get output from rustc --print cfg -O")?; + let mut cmd = Command::new("rustc"); + cmd.args(&["--print", "cfg", "-O"]); + if let Some(target) = target { + cmd.args(&["--target", target.as_str()]); + } + let output = cmd.output().context("Failed to get output from rustc --print cfg -O")?; if !output.status.success() { bail!( "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 c507dc683c0..3e6dd6061cd 100644 --- a/crates/ra_syntax/src/ast/edit.rs +++ b/crates/ra_syntax/src/ast/edit.rs @@ -79,7 +79,7 @@ where impl ast::ItemList { #[must_use] - pub fn append_items(&self, items: impl IntoIterator) -> ast::ItemList { + pub fn append_items(&self, items: impl IntoIterator) -> ast::ItemList { let mut res = self.clone(); if !self.syntax().text().contains_char('\n') { res = make_multiline(res); @@ -89,8 +89,8 @@ impl ast::ItemList { } #[must_use] - pub fn append_item(&self, item: ast::ImplItem) -> ast::ItemList { - let (indent, position) = match self.impl_items().last() { + pub fn append_item(&self, item: ast::AssocItem) -> ast::ItemList { + let (indent, position) = match self.assoc_items().last() { Some(it) => ( leading_indent(it.syntax()).unwrap_or_default().to_string(), 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 c2cc25958c4..b00c156081e 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 { impl ast::ModuleItemOwner for ItemList {} impl ItemList { pub fn l_curly_token(&self) -> Option { support::token(&self.syntax, T!['{']) } - pub fn impl_items(&self) -> AstChildren { support::children(&self.syntax) } + pub fn assoc_items(&self) -> AstChildren { support::children(&self.syntax) } pub fn r_curly_token(&self) -> Option { support::token(&self.syntax, T!['}']) } } @@ -1429,13 +1429,13 @@ impl ast::AttrsOwner for ModuleItem {} impl ast::VisibilityOwner for ModuleItem {} #[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub enum ImplItem { +pub enum AssocItem { FnDef(FnDef), TypeAliasDef(TypeAliasDef), ConstDef(ConstDef), } -impl ast::NameOwner for ImplItem {} -impl ast::AttrsOwner for ImplItem {} +impl ast::NameOwner for AssocItem {} +impl ast::AttrsOwner for AssocItem {} #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum ExternItem { @@ -3167,16 +3167,16 @@ impl AstNode for ModuleItem { } } } -impl From for ImplItem { - fn from(node: FnDef) -> ImplItem { ImplItem::FnDef(node) } +impl From for AssocItem { + fn from(node: FnDef) -> AssocItem { AssocItem::FnDef(node) } } -impl From for ImplItem { - fn from(node: TypeAliasDef) -> ImplItem { ImplItem::TypeAliasDef(node) } +impl From for AssocItem { + fn from(node: TypeAliasDef) -> AssocItem { AssocItem::TypeAliasDef(node) } } -impl From for ImplItem { - fn from(node: ConstDef) -> ImplItem { ImplItem::ConstDef(node) } +impl From for AssocItem { + fn from(node: ConstDef) -> AssocItem { AssocItem::ConstDef(node) } } -impl AstNode for ImplItem { +impl AstNode for AssocItem { fn can_cast(kind: SyntaxKind) -> bool { match kind { FN_DEF | TYPE_ALIAS_DEF | CONST_DEF => true, @@ -3185,18 +3185,18 @@ impl AstNode for ImplItem { } fn cast(syntax: SyntaxNode) -> Option { let res = match syntax.kind() { - FN_DEF => ImplItem::FnDef(FnDef { syntax }), - TYPE_ALIAS_DEF => ImplItem::TypeAliasDef(TypeAliasDef { syntax }), - CONST_DEF => ImplItem::ConstDef(ConstDef { syntax }), + FN_DEF => AssocItem::FnDef(FnDef { syntax }), + TYPE_ALIAS_DEF => AssocItem::TypeAliasDef(TypeAliasDef { syntax }), + CONST_DEF => AssocItem::ConstDef(ConstDef { syntax }), _ => return None, }; Some(res) } fn syntax(&self) -> &SyntaxNode { match self { - ImplItem::FnDef(it) => &it.syntax, - ImplItem::TypeAliasDef(it) => &it.syntax, - ImplItem::ConstDef(it) => &it.syntax, + AssocItem::FnDef(it) => &it.syntax, + AssocItem::TypeAliasDef(it) => &it.syntax, + AssocItem::ConstDef(it) => &it.syntax, } } } @@ -3641,7 +3641,7 @@ impl std::fmt::Display for ModuleItem { std::fmt::Display::fmt(self.syntax(), f) } } -impl std::fmt::Display for ImplItem { +impl std::fmt::Display for AssocItem { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } diff --git a/crates/ra_syntax/src/fuzz.rs b/crates/ra_syntax/src/fuzz.rs index 10fbe31768c..39f9b12ab20 100644 --- a/crates/ra_syntax/src/fuzz.rs +++ b/crates/ra_syntax/src/fuzz.rs @@ -5,7 +5,7 @@ use std::{ str::{self, FromStr}, }; -use ra_text_edit::AtomTextEdit; +use ra_text_edit::Indel; use crate::{validation, AstNode, SourceFile, TextRange}; @@ -22,7 +22,7 @@ pub fn check_parser(text: &str) { #[derive(Debug, Clone)] pub struct CheckReparse { text: String, - edit: AtomTextEdit, + edit: Indel, edited_text: String, } @@ -43,7 +43,7 @@ impl CheckReparse { TextRange::at(delete_start.try_into().unwrap(), delete_len.try_into().unwrap()); let edited_text = format!("{}{}{}", &text[..delete_start], &insert, &text[delete_start + delete_len..]); - let edit = AtomTextEdit { delete, insert }; + let edit = Indel { delete, insert }; Some(CheckReparse { text, edit, edited_text }) } diff --git a/crates/ra_syntax/src/lib.rs b/crates/ra_syntax/src/lib.rs index d0234cadaf4..61e686da5e6 100644 --- a/crates/ra_syntax/src/lib.rs +++ b/crates/ra_syntax/src/lib.rs @@ -39,7 +39,7 @@ pub mod fuzz; use std::{marker::PhantomData, sync::Arc}; -use ra_text_edit::AtomTextEdit; +use ra_text_edit::Indel; use stdx::format_to; use crate::syntax_node::GreenNode; @@ -126,13 +126,13 @@ impl Parse { buf } - pub fn reparse(&self, edit: &AtomTextEdit) -> Parse { - self.incremental_reparse(edit).unwrap_or_else(|| self.full_reparse(edit)) + pub fn reparse(&self, indel: &Indel) -> Parse { + self.incremental_reparse(indel).unwrap_or_else(|| self.full_reparse(indel)) } - fn incremental_reparse(&self, edit: &AtomTextEdit) -> Option> { + fn incremental_reparse(&self, indel: &Indel) -> Option> { // FIXME: validation errors are not handled here - parsing::incremental_reparse(self.tree().syntax(), edit, self.errors.to_vec()).map( + parsing::incremental_reparse(self.tree().syntax(), indel, self.errors.to_vec()).map( |(green_node, errors, _reparsed_range)| Parse { green: green_node, errors: Arc::new(errors), @@ -141,8 +141,9 @@ impl Parse { ) } - fn full_reparse(&self, edit: &AtomTextEdit) -> Parse { - let text = edit.apply(self.tree().syntax().text().to_string()); + fn full_reparse(&self, indel: &Indel) -> Parse { + let mut text = self.tree().syntax().text().to_string(); + indel.apply(&mut text); SourceFile::parse(&text) } } diff --git a/crates/ra_syntax/src/parsing/reparsing.rs b/crates/ra_syntax/src/parsing/reparsing.rs index ffff0a7b202..edbc190f855 100644 --- a/crates/ra_syntax/src/parsing/reparsing.rs +++ b/crates/ra_syntax/src/parsing/reparsing.rs @@ -7,7 +7,7 @@ //! and try to parse only this block. use ra_parser::Reparser; -use ra_text_edit::AtomTextEdit; +use ra_text_edit::Indel; use crate::{ algo, @@ -24,7 +24,7 @@ use crate::{ pub(crate) fn incremental_reparse( node: &SyntaxNode, - edit: &AtomTextEdit, + edit: &Indel, errors: Vec, ) -> Option<(GreenNode, Vec, TextRange)> { if let Some((green, new_errors, old_range)) = reparse_token(node, &edit) { @@ -39,7 +39,7 @@ pub(crate) fn incremental_reparse( fn reparse_token<'node>( root: &'node SyntaxNode, - edit: &AtomTextEdit, + edit: &Indel, ) -> Option<(GreenNode, Vec, TextRange)> { let prev_token = algo::find_covering_element(root, edit.delete).as_token()?.clone(); let prev_token_kind = prev_token.kind(); @@ -88,7 +88,7 @@ fn reparse_token<'node>( fn reparse_block<'node>( root: &'node SyntaxNode, - edit: &AtomTextEdit, + edit: &Indel, ) -> Option<(GreenNode, Vec, TextRange)> { let (node, reparser) = find_reparsable_node(root, edit.delete)?; let text = get_text_after_edit(node.clone().into(), edit); @@ -108,15 +108,15 @@ fn reparse_block<'node>( Some((node.replace_with(green), new_parser_errors, node.text_range())) } -fn get_text_after_edit(element: SyntaxElement, edit: &AtomTextEdit) -> String { - let edit = - AtomTextEdit::replace(edit.delete - element.text_range().start(), edit.insert.clone()); +fn get_text_after_edit(element: SyntaxElement, edit: &Indel) -> String { + let edit = Indel::replace(edit.delete - element.text_range().start(), edit.insert.clone()); - let text = match element { + let mut text = match element { NodeOrToken::Token(token) => token.text().to_string(), NodeOrToken::Node(node) => node.text().to_string(), }; - edit.apply(text) + edit.apply(&mut text); + text } fn is_contextual_kw(text: &str) -> bool { @@ -167,7 +167,7 @@ fn merge_errors( old_errors: Vec, new_errors: Vec, range_before_reparse: TextRange, - edit: &AtomTextEdit, + edit: &Indel, ) -> Vec { let mut res = Vec::new(); @@ -198,8 +198,12 @@ mod tests { fn do_check(before: &str, replace_with: &str, reparsed_len: u32) { let (range, before) = extract_range(before); - let edit = AtomTextEdit::replace(range, replace_with.to_owned()); - let after = edit.apply(before.clone()); + let edit = Indel::replace(range, replace_with.to_owned()); + let after = { + let mut after = before.clone(); + edit.apply(&mut after); + after + }; let fully_reparsed = SourceFile::parse(&after); let incrementally_reparsed: Parse = { diff --git a/crates/ra_text_edit/src/lib.rs b/crates/ra_text_edit/src/lib.rs index e656260c73d..7138bbc6551 100644 --- a/crates/ra_text_edit/src/lib.rs +++ b/crates/ra_text_edit/src/lib.rs @@ -1,36 +1,144 @@ -//! FIXME: write short doc here - -mod text_edit; +//! Representation of a `TextEdit`. +//! +//! `rust-analyzer` never mutates text itself and only sends diffs to clients, +//! so `TextEdit` is the ultimate representation of the work done by +//! rust-analyzer. use text_size::{TextRange, TextSize}; -pub use crate::text_edit::{TextEdit, TextEditBuilder}; - -/// Must not overlap with other `AtomTextEdit`s +/// `InsertDelete` -- a single "atomic" change to text +/// +/// Must not overlap with other `InDel`s #[derive(Debug, Clone)] -pub struct AtomTextEdit { +pub struct Indel { + pub insert: String, /// Refers to offsets in the original text pub delete: TextRange, - pub insert: String, } -impl AtomTextEdit { - pub fn replace(range: TextRange, replace_with: String) -> AtomTextEdit { - AtomTextEdit { delete: range, insert: replace_with } +#[derive(Debug, Clone)] +pub struct TextEdit { + indels: Vec, +} + +#[derive(Debug, Default)] +pub struct TextEditBuilder { + indels: Vec, +} + +impl Indel { + pub fn insert(offset: TextSize, text: String) -> Indel { + Indel::replace(TextRange::empty(offset), text) + } + pub fn delete(range: TextRange) -> Indel { + Indel::replace(range, String::new()) + } + pub fn replace(range: TextRange, replace_with: String) -> Indel { + Indel { delete: range, insert: replace_with } } - pub fn delete(range: TextRange) -> AtomTextEdit { - AtomTextEdit::replace(range, String::new()) - } - - pub fn insert(offset: TextSize, text: String) -> AtomTextEdit { - AtomTextEdit::replace(TextRange::empty(offset), text) - } - - pub fn apply(&self, mut text: String) -> String { + pub fn apply(&self, text: &mut String) { let start: usize = self.delete.start().into(); let end: usize = self.delete.end().into(); text.replace_range(start..end, &self.insert); - text + } +} + +impl TextEdit { + pub fn insert(offset: TextSize, text: String) -> TextEdit { + let mut builder = TextEditBuilder::default(); + builder.insert(offset, text); + builder.finish() + } + + pub fn delete(range: TextRange) -> TextEdit { + let mut builder = TextEditBuilder::default(); + builder.delete(range); + builder.finish() + } + + pub fn replace(range: TextRange, replace_with: String) -> TextEdit { + let mut builder = TextEditBuilder::default(); + builder.replace(range, replace_with); + builder.finish() + } + + pub(crate) fn from_indels(mut indels: Vec) -> TextEdit { + indels.sort_by_key(|a| (a.delete.start(), a.delete.end())); + for (a1, a2) in indels.iter().zip(indels.iter().skip(1)) { + assert!(a1.delete.end() <= a2.delete.start()) + } + TextEdit { indels } + } + + pub fn as_indels(&self) -> &[Indel] { + &self.indels + } + + pub fn apply(&self, text: &mut String) { + match self.indels.len() { + 0 => return, + 1 => { + self.indels[0].apply(text); + return; + } + _ => (), + } + + let mut total_len = TextSize::of(&*text); + for indel in self.indels.iter() { + total_len += TextSize::of(&indel.insert); + total_len -= indel.delete.end() - indel.delete.start(); + } + let mut buf = String::with_capacity(total_len.into()); + let mut prev = 0; + for indel in self.indels.iter() { + let start: usize = indel.delete.start().into(); + let end: usize = indel.delete.end().into(); + if start > prev { + buf.push_str(&text[prev..start]); + } + buf.push_str(&indel.insert); + prev = end; + } + buf.push_str(&text[prev..text.len()]); + assert_eq!(TextSize::of(&buf), total_len); + + // FIXME: figure out a way to mutate the text in-place or reuse the + // memory in some other way + *text = buf + } + + pub fn apply_to_offset(&self, offset: TextSize) -> Option { + let mut res = offset; + for indel in self.indels.iter() { + if indel.delete.start() >= offset { + break; + } + if offset < indel.delete.end() { + return None; + } + res += TextSize::of(&indel.insert); + res -= indel.delete.len(); + } + Some(res) + } +} + +impl TextEditBuilder { + pub fn replace(&mut self, range: TextRange, replace_with: String) { + self.indels.push(Indel::replace(range, replace_with)) + } + pub fn delete(&mut self, range: TextRange) { + self.indels.push(Indel::delete(range)) + } + pub fn insert(&mut self, offset: TextSize, text: String) { + self.indels.push(Indel::insert(offset, text)) + } + pub fn finish(self) -> TextEdit { + TextEdit::from_indels(self.indels) + } + pub fn invalidates_offset(&self, offset: TextSize) -> bool { + self.indels.iter().any(|indel| indel.delete.contains_inclusive(offset)) } } 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 eabab4b4d11..00000000000 --- a/crates/ra_text_edit/src/text_edit.rs +++ /dev/null @@ -1,102 +0,0 @@ -//! FIXME: write short doc here - -use crate::AtomTextEdit; - -use text_size::{TextRange, TextSize}; - -#[derive(Debug, Clone)] -pub struct TextEdit { - atoms: Vec, -} - -#[derive(Debug, Default)] -pub struct TextEditBuilder { - atoms: Vec, -} - -impl TextEditBuilder { - pub fn replace(&mut self, range: TextRange, replace_with: String) { - self.atoms.push(AtomTextEdit::replace(range, replace_with)) - } - pub fn delete(&mut self, range: TextRange) { - self.atoms.push(AtomTextEdit::delete(range)) - } - pub fn insert(&mut self, offset: TextSize, text: String) { - self.atoms.push(AtomTextEdit::insert(offset, text)) - } - pub fn finish(self) -> TextEdit { - TextEdit::from_atoms(self.atoms) - } - pub fn invalidates_offset(&self, offset: TextSize) -> bool { - self.atoms.iter().any(|atom| atom.delete.contains_inclusive(offset)) - } -} - -impl TextEdit { - pub fn insert(offset: TextSize, text: String) -> TextEdit { - let mut builder = TextEditBuilder::default(); - builder.insert(offset, text); - builder.finish() - } - - pub fn delete(range: TextRange) -> TextEdit { - let mut builder = TextEditBuilder::default(); - builder.delete(range); - builder.finish() - } - - pub fn replace(range: TextRange, replace_with: String) -> TextEdit { - let mut builder = TextEditBuilder::default(); - builder.replace(range, replace_with); - builder.finish() - } - - pub(crate) fn from_atoms(mut atoms: Vec) -> TextEdit { - atoms.sort_by_key(|a| (a.delete.start(), a.delete.end())); - for (a1, a2) in atoms.iter().zip(atoms.iter().skip(1)) { - assert!(a1.delete.end() <= a2.delete.start()) - } - TextEdit { atoms } - } - - pub fn as_atoms(&self) -> &[AtomTextEdit] { - &self.atoms - } - - pub fn apply(&self, text: &str) -> String { - let mut total_len = TextSize::of(text); - for atom in self.atoms.iter() { - total_len += TextSize::of(&atom.insert); - total_len -= atom.delete.end() - atom.delete.start(); - } - let mut buf = String::with_capacity(total_len.into()); - let mut prev = 0; - for atom in self.atoms.iter() { - let start: usize = atom.delete.start().into(); - let end: usize = atom.delete.end().into(); - if start > prev { - buf.push_str(&text[prev..start]); - } - buf.push_str(&atom.insert); - prev = end; - } - buf.push_str(&text[prev..text.len()]); - assert_eq!(TextSize::of(&buf), total_len); - buf - } - - pub fn apply_to_offset(&self, offset: TextSize) -> Option { - let mut res = offset; - for atom in self.atoms.iter() { - if atom.delete.start() >= offset { - break; - } - if offset < atom.delete.end() { - return None; - } - res += TextSize::of(&atom.insert); - res -= atom.delete.len(); - } - Some(res) - } -} diff --git a/crates/rust-analyzer/src/caps.rs b/crates/rust-analyzer/src/caps.rs index 680415caca2..110c9a44297 100644 --- a/crates/rust-analyzer/src/caps.rs +++ b/crates/rust-analyzer/src/caps.rs @@ -17,10 +17,10 @@ pub fn server_capabilities() -> ServerCapabilities { ServerCapabilities { text_document_sync: Some(TextDocumentSyncCapability::Options(TextDocumentSyncOptions { open_close: Some(true), - change: Some(if env::var("RA_PROFILE").is_ok() { - TextDocumentSyncKind::Incremental - } else { + change: Some(if env::var("RA_NO_INCREMENTAL_SYNC").is_ok() { TextDocumentSyncKind::Full + } else { + TextDocumentSyncKind::Incremental }), will_save: None, 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 10c25666a0e..5e5a17943d2 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 { RunnableKind::Test { test_id, attr } => { args.push("test".to_string()); if let Some(spec) = spec { - spec.push_to(&mut args); + spec.push_to(&mut args, kind); } extra_args.push(test_id.to_string()); if let TestId::Path(_) = test_id { @@ -35,13 +35,13 @@ impl CargoTargetSpec { } extra_args.push("--nocapture".to_string()); if attr.ignore { - extra_args.push("--ignored".to_string()) + extra_args.push("--ignored".to_string()); } } RunnableKind::TestMod { path } => { args.push("test".to_string()); if let Some(spec) = spec { - spec.push_to(&mut args); + spec.push_to(&mut args, kind); } extra_args.push(path.to_string()); extra_args.push("--nocapture".to_string()); @@ -49,7 +49,7 @@ impl CargoTargetSpec { RunnableKind::Bench { test_id } => { args.push("bench".to_string()); if let Some(spec) = spec { - spec.push_to(&mut args); + spec.push_to(&mut args, kind); } extra_args.push(test_id.to_string()); if let TestId::Path(_) = test_id { @@ -57,10 +57,19 @@ impl CargoTargetSpec { } extra_args.push("--nocapture".to_string()); } + RunnableKind::DocTest { test_id } => { + args.push("test".to_string()); + args.push("--doc".to_string()); + if let Some(spec) = spec { + spec.push_to(&mut args, kind); + } + extra_args.push(test_id.to_string()); + extra_args.push("--nocapture".to_string()); + } RunnableKind::Bin => { args.push("run".to_string()); if let Some(spec) = spec { - spec.push_to(&mut args); + spec.push_to(&mut args, kind); } } } @@ -91,9 +100,14 @@ impl CargoTargetSpec { Ok(res) } - pub(crate) fn push_to(self, buf: &mut Vec) { + pub(crate) fn push_to(self, buf: &mut Vec, kind: &RunnableKind) { buf.push("--package".to_string()); buf.push(self.package); + + // Can't mix --doc with other target flags + if let RunnableKind::DocTest { .. } = kind { + return; + } match self.target_kind { TargetKind::Bin => { 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 d0a71120aaf..023ced6cfb9 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( // FIXME: cfg options? let default_cfg_options = { - let mut opts = get_rustc_cfg_options(); + let mut opts = get_rustc_cfg_options(None); opts.insert_atom("test".into()); opts.insert_atom("debug_assertion".into()); opts diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 15b7c691219..ccc38e3bbac 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -89,6 +89,7 @@ impl Default for Config { check: Some(FlycheckConfig::CargoCommand { command: "check".to_string(), all_targets: true, + all_features: true, extra_args: Vec::new(), }), @@ -131,6 +132,7 @@ impl Config { set(value, "/cargo/allFeatures", &mut self.cargo.all_features); set(value, "/cargo/features", &mut self.cargo.features); set(value, "/cargo/loadOutDirsFromCheck", &mut self.cargo.load_out_dirs_from_check); + set(value, "/cargo/target", &mut self.cargo.target); match get(value, "/procMacro/enable") { Some(true) => { @@ -172,12 +174,13 @@ impl Config { } // otherwise configure command customizations _ => { - if let Some(FlycheckConfig::CargoCommand { command, extra_args, all_targets }) + if let Some(FlycheckConfig::CargoCommand { command, extra_args, all_targets, all_features }) = &mut self.check { set(value, "/checkOnSave/extraArgs", extra_args); set(value, "/checkOnSave/command", command); set(value, "/checkOnSave/allTargets", all_targets); + set(value, "/checkOnSave/allFeatures", all_features); } } }; diff --git a/crates/rust-analyzer/src/conv.rs b/crates/rust-analyzer/src/conv.rs index 7be5ebcdb5a..f64c90b5b17 100644 --- a/crates/rust-analyzer/src/conv.rs +++ b/crates/rust-analyzer/src/conv.rs @@ -15,7 +15,7 @@ use ra_ide::{ ReferenceAccess, Severity, SourceChange, SourceFileEdit, }; use ra_syntax::{SyntaxKind, TextRange, TextSize}; -use ra_text_edit::{AtomTextEdit, TextEdit}; +use ra_text_edit::{Indel, TextEdit}; use ra_vfs::LineEndings; use crate::{ @@ -124,23 +124,22 @@ impl ConvWith<(&LineIndex, LineEndings)> for CompletionItem { let mut text_edit = None; // LSP does not allow arbitrary edits in completion, so we have to do a // non-trivial mapping here. - for atom_edit in self.text_edit().as_atoms() { - if atom_edit.delete.contains_range(self.source_range()) { - text_edit = Some(if atom_edit.delete == self.source_range() { - atom_edit.conv_with((ctx.0, ctx.1)) + for indel in self.text_edit().as_indels() { + if indel.delete.contains_range(self.source_range()) { + text_edit = Some(if indel.delete == self.source_range() { + indel.conv_with((ctx.0, ctx.1)) } else { - assert!(self.source_range().end() == atom_edit.delete.end()); - let range1 = - TextRange::new(atom_edit.delete.start(), self.source_range().start()); + assert!(self.source_range().end() == indel.delete.end()); + let range1 = TextRange::new(indel.delete.start(), self.source_range().start()); let range2 = self.source_range(); - let edit1 = AtomTextEdit::replace(range1, String::new()); - let edit2 = AtomTextEdit::replace(range2, atom_edit.insert.clone()); + let edit1 = Indel::replace(range1, String::new()); + let edit2 = Indel::replace(range2, indel.insert.clone()); additional_text_edits.push(edit1.conv_with((ctx.0, ctx.1))); edit2.conv_with((ctx.0, ctx.1)) }) } else { - assert!(self.source_range().intersect(atom_edit.delete).is_none()); - additional_text_edits.push(atom_edit.conv_with((ctx.0, ctx.1))); + assert!(self.source_range().intersect(indel.delete).is_none()); + additional_text_edits.push(indel.conv_with((ctx.0, ctx.1))); } } let text_edit = text_edit.unwrap(); @@ -257,11 +256,11 @@ impl ConvWith<(&LineIndex, LineEndings)> for TextEdit { type Output = Vec; fn conv_with(self, ctx: (&LineIndex, LineEndings)) -> Vec { - self.as_atoms().iter().map_conv_with(ctx).collect() + self.as_indels().iter().map_conv_with(ctx).collect() } } -impl ConvWith<(&LineIndex, LineEndings)> for &AtomTextEdit { +impl ConvWith<(&LineIndex, LineEndings)> for &Indel { type Output = lsp_types::TextEdit; fn conv_with( @@ -522,7 +521,7 @@ impl TryConvWith<&WorldSnapshot> for SourceFileEdit { let line_index = world.analysis().file_line_index(self.file_id)?; let line_endings = world.file_line_endings(self.file_id); let edits = - self.edit.as_atoms().iter().map_conv_with((&line_index, line_endings)).collect(); + self.edit.as_indels().iter().map_conv_with((&line_index, line_endings)).collect(); Ok(TextDocumentEdit { text_document, edits }) } } diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index 401fae75565..b77f0c5a9cf 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( // remember the last valid line in the index and only rebuild it if needed. enum IndexValid { All, - UpToLine(u64), + UpToLineExclusive(u64), } impl IndexValid { fn covers(&self, line: u64) -> bool { match *self { - IndexValid::UpToLine(to) => to >= line, + IndexValid::UpToLineExclusive(to) => to > line, _ => true, } } @@ -692,10 +692,10 @@ fn apply_document_changes( for change in content_changes { match change.range { Some(range) => { - if !index_valid.covers(range.start.line) { + if !index_valid.covers(range.end.line) { line_index = Cow::Owned(LineIndex::new(&old_text)); } - index_valid = IndexValid::UpToLine(range.start.line); + index_valid = IndexValid::UpToLineExclusive(range.start.line); let range = range.conv_with(&line_index); let mut text = old_text.to_owned(); match std::panic::catch_unwind(move || { @@ -713,7 +713,7 @@ fn apply_document_changes( } None => { *old_text = change.text; - index_valid = IndexValid::UpToLine(0); + index_valid = IndexValid::UpToLineExclusive(0); } } } @@ -1083,5 +1083,17 @@ mod tests { assert_eq!(text, "the quick foxes\nDREAM\nthey have quiet dreams\nDON'T THEY?\n"); run(&mut text, c![0, 10; 1, 5 => "", 2, 0; 2, 12 => ""]); assert_eq!(text, "the quick \nthey have quiet dreams\n"); + + text = String::from("❤️"); + run(&mut text, c![0, 0; 0, 0 => "a"]); + assert_eq!(text, "a❤️"); + + text = String::from("a\nb"); + run(&mut text, c![0, 1; 1, 0 => "\nțc", 0, 1; 1, 1 => "d"]); + assert_eq!(text, "adcb"); + + text = String::from("a\nb"); + run(&mut text, c![0, 1; 1, 0 => "ț\nc", 0, 2; 0, 2 => "c"]); + assert_eq!(text, "ațc\ncb"); } } diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs index c7a96ba9322..15e8305f884 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( for runnable in world.analysis().runnables(file_id)? { let title = match &runnable.kind { RunnableKind::Test { .. } | RunnableKind::TestMod { .. } => "▶️\u{fe0e}Run Test", + RunnableKind::DocTest { .. } => "▶️\u{fe0e}Run Doctest", RunnableKind::Bench { .. } => "Run Bench", RunnableKind::Bin => "Run", } @@ -1018,6 +1019,7 @@ fn to_lsp_runnable( RunnableKind::Test { test_id, .. } => format!("test {}", test_id), RunnableKind::TestMod { path } => format!("test-mod {}", path), RunnableKind::Bench { test_id } => format!("bench {}", test_id), + RunnableKind::DocTest { test_id, .. } => format!("doctest {}", test_id), RunnableKind::Bin => "run binary".to_string(), }; Ok(req::Runnable { diff --git a/crates/rust-analyzer/src/world.rs b/crates/rust-analyzer/src/world.rs index 34941931b78..16020648df6 100644 --- a/crates/rust-analyzer/src/world.rs +++ b/crates/rust-analyzer/src/world.rs @@ -131,7 +131,7 @@ impl WorldState { // FIXME: Read default cfgs from config let default_cfg_options = { - let mut opts = get_rustc_cfg_options(); + let mut opts = get_rustc_cfg_options(config.cargo.target.as_ref()); opts.insert_atom("test".into()); opts.insert_atom("debug_assertion".into()); opts diff --git a/crates/rust-analyzer/tests/heavy_tests/main.rs b/crates/rust-analyzer/tests/heavy_tests/main.rs index a218da76d6c..1efa5dd632d 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() {} "arguments": [ { "cursorPosition": null, - "label": "create module", + "label": "Create module", "workspaceEdit": { "documentChanges": [ { @@ -349,9 +349,9 @@ fn main() {} } ], "command": "rust-analyzer.applySourceChange", - "title": "create module" + "title": "Create module" }, - "title": "create module" + "title": "Create module" } ]), ); @@ -420,7 +420,7 @@ fn main() {{}} "arguments": [ { "cursorPosition": null, - "label": "create module", + "label": "Create module", "workspaceEdit": { "documentChanges": [ { @@ -432,9 +432,9 @@ fn main() {{}} } ], "command": "rust-analyzer.applySourceChange", - "title": "create module" + "title": "Create module" }, - "title": "create module" + "title": "Create module" } ]), ); @@ -500,7 +500,7 @@ fn main() {{}} "position": { "character": 4, "line": 1 }, "textDocument": { "uri": "file:///[..]src/m0.rs" } }, - "label": "on enter", + "label": "On enter", "workspaceEdit": { "documentChanges": [ { @@ -552,7 +552,7 @@ version = \"0.0.0\" "position": { "line": 1, "character": 4 }, "textDocument": { "uri": "file:///[..]src/main.rs" } }, - "label": "on enter", + "label": "On enter", "workspaceEdit": { "documentChanges": [ { diff --git a/docs/user/readme.adoc b/docs/user/readme.adoc index b1af72ce663..69f5b13d6b8 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` ---- ==== -The 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). +The server binary is stored in: + +* Linux: `~/.config/Code/User/globalStorage/matklad.rust-analyzer` +* macOS: `~/Library/Application Support/Code/User/globalStorage/matklad.rust-analyzer` +* Windows: `%APPDATA%\Code\User\globalStorage` Note that we only support the latest version of VS Code. @@ -159,11 +163,11 @@ Emacs support is maintained as part of the https://github.com/emacs-lsp/lsp-mode 3. Run `lsp` in a Rust buffer. 4. (Optionally) bind commands like `lsp-rust-analyzer-join-lines`, `lsp-extend-selection` and `lsp-rust-analyzer-expand-macro` to keys. -=== Vim +=== Vim/NeoVim -Prerequisites: You have installed the <>. +Prerequisites: You have installed the <>. Not needed if the extension can install/update it on its own, coc-rust-analyzer is one example. -The are several LSP client implementations for vim: +The are several LSP client implementations for vim or neovim: ==== coc-rust-analyzer diff --git a/editors/code/package-lock.json b/editors/code/package-lock.json index 55ba5351d17..71c627a2a06 100644 --- a/editors/code/package-lock.json +++ b/editors/code/package-lock.json @@ -1066,11 +1066,6 @@ "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", "dev": true }, - "jsonc-parser": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-2.2.1.tgz", - "integrity": "sha512-o6/yDBYccGvTz1+QFevz6l6OBZ2+fMVu2JZ9CIhzsYRX4mjaK5IyX9eldUdCmga16zlgQxyrj5pt9kzuj2C02w==" - }, "leven": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", diff --git a/editors/code/package.json b/editors/code/package.json index 442b9de98ec..eeb3d3513fe 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -33,7 +33,6 @@ "fix": " tsfmt -r && eslint -c .eslintrc.js --ext ts ./src --fix" }, "dependencies": { - "jsonc-parser": "^2.2.1", "node-fetch": "^2.6.0", "vscode-languageclient": "7.0.0-next.1" }, @@ -233,6 +232,14 @@ "default": false, "markdownDescription": "Run `cargo check` on startup to get the correct value for package OUT_DIRs" }, + "rust-analyzer.cargo.target": { + "type": [ + "null", + "string" + ], + "default": null, + "description": "Specify the compilation target" + }, "rust-analyzer.rustfmt.extraArgs": { "type": "array", "items": { @@ -288,6 +295,11 @@ "default": true, "markdownDescription": "Check all targets and tests (will be passed as `--all-targets`)" }, + "rust-analyzer.checkOnSave.allFeatures": { + "type": "boolean", + "default": true, + "markdownDescription": "Check with all features (will be passed as `--all-features`)" + }, "rust-analyzer.inlayHints.typeHints": { "type": "boolean", "default": true, diff --git a/editors/code/src/color_theme.ts b/editors/code/src/color_theme.ts deleted file mode 100644 index 5b9327b28a4..00000000000 --- a/editors/code/src/color_theme.ts +++ /dev/null @@ -1,129 +0,0 @@ -import * as fs from 'fs'; -import * as jsonc from 'jsonc-parser'; -import * as path from 'path'; -import * as vscode from 'vscode'; - -export interface TextMateRuleSettings { - foreground?: string; - background?: string; - fontStyle?: string; -} - -export class ColorTheme { - private rules: Map = new Map(); - - static load(): ColorTheme { - // Find out current color theme - const themeName = vscode.workspace - .getConfiguration('workbench') - .get('colorTheme'); - - if (typeof themeName !== 'string') { - // console.warn('workbench.colorTheme is', themeName) - return new ColorTheme(); - } - return loadThemeNamed(themeName); - } - - static fromRules(rules: TextMateRule[]): ColorTheme { - const res = new ColorTheme(); - for (const rule of rules) { - const scopes = typeof rule.scope === 'undefined' - ? [] - : typeof rule.scope === 'string' - ? [rule.scope] - : rule.scope; - - for (const scope of scopes) { - res.rules.set(scope, rule.settings); - } - } - return res; - } - - lookup(scopes: string[]): TextMateRuleSettings { - let res: TextMateRuleSettings = {}; - for (const scope of scopes) { - this.rules.forEach((value, key) => { - if (scope.startsWith(key)) { - res = mergeRuleSettings(res, value); - } - }); - } - return res; - } - - mergeFrom(other: ColorTheme) { - other.rules.forEach((value, key) => { - const merged = mergeRuleSettings(this.rules.get(key), value); - this.rules.set(key, merged); - }); - } -} - -function loadThemeNamed(themeName: string): ColorTheme { - function isTheme(extension: vscode.Extension): boolean { - return ( - extension.extensionKind === vscode.ExtensionKind.UI && - extension.packageJSON.contributes && - extension.packageJSON.contributes.themes - ); - } - - const themePaths: string[] = vscode.extensions.all - .filter(isTheme) - .flatMap( - ext => ext.packageJSON.contributes.themes - .filter((it: any) => (it.id || it.label) === themeName) - .map((it: any) => path.join(ext.extensionPath, it.path)) - ); - - const res = new ColorTheme(); - for (const themePath of themePaths) { - res.mergeFrom(loadThemeFile(themePath)); - } - - const globalCustomizations: any = vscode.workspace.getConfiguration('editor').get('tokenColorCustomizations'); - res.mergeFrom(ColorTheme.fromRules(globalCustomizations?.textMateRules ?? [])); - - const themeCustomizations: any = vscode.workspace.getConfiguration('editor.tokenColorCustomizations').get(`[${themeName}]`); - res.mergeFrom(ColorTheme.fromRules(themeCustomizations?.textMateRules ?? [])); - - - return res; -} - -function loadThemeFile(themePath: string): ColorTheme { - let text; - try { - text = fs.readFileSync(themePath, 'utf8'); - } catch { - return new ColorTheme(); - } - const obj = jsonc.parse(text); - const tokenColors: TextMateRule[] = obj?.tokenColors ?? []; - const res = ColorTheme.fromRules(tokenColors); - - for (const include of obj?.include ?? []) { - const includePath = path.join(path.dirname(themePath), include); - res.mergeFrom(loadThemeFile(includePath)); - } - - return res; -} - -interface TextMateRule { - scope: string | string[]; - settings: TextMateRuleSettings; -} - -function mergeRuleSettings( - defaultSetting: TextMateRuleSettings | undefined, - override: TextMateRuleSettings, -): TextMateRuleSettings { - return { - foreground: override.foreground ?? defaultSetting?.foreground, - background: override.background ?? defaultSetting?.background, - fontStyle: override.fontStyle ?? defaultSetting?.fontStyle, - }; -} diff --git a/xtask/src/ast_src.rs b/xtask/src/ast_src.rs index 2f8065b7312..fe3eb85de19 100644 --- a/xtask/src/ast_src.rs +++ b/xtask/src/ast_src.rs @@ -373,7 +373,7 @@ pub(crate) const AST_SRC: AstSrc = AstSrc { struct ItemList: ModuleItemOwner { T!['{'], - impl_items: [ImplItem], + assoc_items: [AssocItem], T!['}'] } @@ -685,7 +685,7 @@ pub(crate) const AST_SRC: AstSrc = AstSrc { } /* impl blocks can also contain MacroCall */ - enum ImplItem: NameOwner, AttrsOwner { + enum AssocItem: NameOwner, AttrsOwner { FnDef, TypeAliasDef, ConstDef } diff --git a/xtask/tests/tidy-tests/main.rs b/xtask/tests/tidy-tests/main.rs index ead642acc6d..3213c4dfa3c 100644 --- a/xtask/tests/tidy-tests/main.rs +++ b/xtask/tests/tidy-tests/main.rs @@ -115,7 +115,6 @@ impl TidyDocs { "ra_prof", "ra_project_model", "ra_syntax", - "ra_text_edit", "ra_tt", "ra_hir_ty", ];