From fd83d0e9ba4605d8060ff0eca83ae9de0202a8a7 Mon Sep 17 00:00:00 2001 From: George Fraser Date: Fri, 15 May 2020 19:27:18 -0700 Subject: [PATCH 01/70] Color macros (fixes #4462) --- editors/code/package.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/editors/code/package.json b/editors/code/package.json index 4e7e3faf70..2dbbde8527 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -604,6 +604,9 @@ { "language": "rust", "scopes": { + "macro": [ + "entity.name.function.macro.rust" + ], "attribute": [ "meta.attribute.rust" ], From 811d25b7237cfb74a312369a7820bf6fbb81483e Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sat, 16 May 2020 18:32:15 +0200 Subject: [PATCH 02/70] Allow calling dyn trait super trait methods without the super trait in scope This also removes some vestiges of the old impl trait support which I think aren't currently in use. --- crates/ra_hir_ty/src/lib.rs | 14 ++++----- crates/ra_hir_ty/src/method_resolution.rs | 10 ++---- .../ra_hir_ty/src/tests/method_resolution.rs | 31 +++++++++++++++++++ 3 files changed, 40 insertions(+), 15 deletions(-) diff --git a/crates/ra_hir_ty/src/lib.rs b/crates/ra_hir_ty/src/lib.rs index ccc4348f42..daea02f881 100644 --- a/crates/ra_hir_ty/src/lib.rs +++ b/crates/ra_hir_ty/src/lib.rs @@ -808,15 +808,13 @@ impl Ty { } } - /// If this is an `impl Trait` or `dyn Trait`, returns that trait. - pub fn inherent_trait(&self) -> Option { + /// If this is a `dyn Trait`, returns that trait. + pub fn dyn_trait(&self) -> Option { match self { - Ty::Dyn(predicates) | Ty::Opaque(predicates) => { - predicates.iter().find_map(|pred| match pred { - GenericPredicate::Implemented(tr) => Some(tr.trait_), - _ => None, - }) - } + Ty::Dyn(predicates) => predicates.iter().find_map(|pred| match pred { + GenericPredicate::Implemented(tr) => Some(tr.trait_), + _ => None, + }), _ => None, } } diff --git a/crates/ra_hir_ty/src/method_resolution.rs b/crates/ra_hir_ty/src/method_resolution.rs index 657284fd01..0851e16a89 100644 --- a/crates/ra_hir_ty/src/method_resolution.rs +++ b/crates/ra_hir_ty/src/method_resolution.rs @@ -408,8 +408,9 @@ fn iterate_trait_method_candidates( receiver_ty: Option<&Canonical>, mut callback: impl FnMut(&Ty, AssocItemId) -> Option, ) -> Option { - // if ty is `impl Trait` or `dyn Trait`, the trait doesn't need to be in scope - let inherent_trait = self_ty.value.inherent_trait().into_iter(); + // if ty is `dyn Trait`, the trait doesn't need to be in scope + let inherent_trait = + self_ty.value.dyn_trait().into_iter().flat_map(|t| all_super_traits(db.upcast(), t)); let env_traits = if let Ty::Placeholder(_) = self_ty.value { // if we have `T: Trait` in the param env, the trait doesn't need to be in scope env.trait_predicates_for_self_ty(&self_ty.value) @@ -601,11 +602,6 @@ pub fn implements_trait( krate: CrateId, trait_: TraitId, ) -> bool { - if ty.value.inherent_trait() == Some(trait_) { - // FIXME this is a bit of a hack, since Chalk should say the same thing - // anyway, but currently Chalk doesn't implement `dyn/impl Trait` yet - return true; - } let goal = generic_implements_goal(db, env, trait_, ty.clone()); let solution = db.trait_solve(krate, goal); diff --git a/crates/ra_hir_ty/src/tests/method_resolution.rs b/crates/ra_hir_ty/src/tests/method_resolution.rs index 67f964ab5d..9c2c9e1d28 100644 --- a/crates/ra_hir_ty/src/tests/method_resolution.rs +++ b/crates/ra_hir_ty/src/tests/method_resolution.rs @@ -1096,3 +1096,34 @@ fn test() { (S {}).method()<|>; } ); assert_eq!(t, "()"); } + +#[test] +fn dyn_trait_super_trait_not_in_scope() { + assert_snapshot!( + infer(r#" +mod m { + pub trait SuperTrait { + fn foo(&self) -> u32 { 0 } + } +} +trait Trait: m::SuperTrait {} + +struct S; +impl m::SuperTrait for S {} +impl Trait for S {} + +fn test(d: &dyn Trait) { + d.foo(); +} +"#), + @r###" + 52..56 'self': &Self + 65..70 '{ 0 }': u32 + 67..68 '0': u32 + 177..178 'd': &dyn Trait + 192..208 '{ ...o(); }': () + 198..199 'd': &dyn Trait + 198..205 'd.foo()': u32 + "### + ); +} From ef6d53521f07caa9c524749116d5fe53e1e8408d Mon Sep 17 00:00:00 2001 From: Simon Vandel Sillesen Date: Sat, 16 May 2020 18:06:23 +0200 Subject: [PATCH 03/70] Shrink ra_parser::Event from 32 bytes to 16 bytes This boxes the Error variant with the assumption that it is rarely constructed --- crates/ra_parser/src/lib.rs | 2 +- crates/ra_parser/src/parser.rs | 2 +- crates/ra_syntax/src/syntax_node.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/ra_parser/src/lib.rs b/crates/ra_parser/src/lib.rs index e08ad4dae6..eeb8ad66bd 100644 --- a/crates/ra_parser/src/lib.rs +++ b/crates/ra_parser/src/lib.rs @@ -25,7 +25,7 @@ pub(crate) use token_set::TokenSet; pub use syntax_kind::SyntaxKind; #[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct ParseError(pub String); +pub struct ParseError(pub Box); /// `TokenSource` abstracts the source of the tokens parser operates on. /// diff --git a/crates/ra_parser/src/parser.rs b/crates/ra_parser/src/parser.rs index faa63d53f2..4f59b0a235 100644 --- a/crates/ra_parser/src/parser.rs +++ b/crates/ra_parser/src/parser.rs @@ -192,7 +192,7 @@ impl<'t> Parser<'t> { /// structured errors with spans and notes, like rustc /// does. pub(crate) fn error>(&mut self, message: T) { - let msg = ParseError(message.into()); + let msg = ParseError(Box::new(message.into())); self.push_event(Event::Error { msg }) } diff --git a/crates/ra_syntax/src/syntax_node.rs b/crates/ra_syntax/src/syntax_node.rs index f9d379abf3..e566af7e87 100644 --- a/crates/ra_syntax/src/syntax_node.rs +++ b/crates/ra_syntax/src/syntax_node.rs @@ -70,6 +70,6 @@ impl SyntaxTreeBuilder { } pub fn error(&mut self, error: ra_parser::ParseError, text_pos: TextSize) { - self.errors.push(SyntaxError::new_at_offset(error.0, text_pos)) + self.errors.push(SyntaxError::new_at_offset(*error.0, text_pos)) } } From b606399095cc5357a93f40fb0d695eab8530bc98 Mon Sep 17 00:00:00 2001 From: Simon Vandel Sillesen Date: Sat, 16 May 2020 18:24:17 +0200 Subject: [PATCH 04/70] Reduce reallocations in ra_tt::buffer::TokenBuffer::new_inner --- crates/ra_tt/src/buffer.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/ra_tt/src/buffer.rs b/crates/ra_tt/src/buffer.rs index 14b3f707df..5967f44cd0 100644 --- a/crates/ra_tt/src/buffer.rs +++ b/crates/ra_tt/src/buffer.rs @@ -42,7 +42,9 @@ impl<'t> TokenBuffer<'t> { buffers: &mut Vec]>>, next: Option, ) -> usize { - let mut entries = vec![]; + // Must contain everything in tokens and then the Entry::End + let start_capacity = tokens.len() + 1; + let mut entries = Vec::with_capacity(start_capacity); let mut children = vec![]; for (idx, tt) in tokens.iter().enumerate() { From 1e9172d70cf714dc006c1cbc4a029ae6e848d35b Mon Sep 17 00:00:00 2001 From: Simon Vandel Sillesen Date: Sat, 16 May 2020 21:56:08 +0200 Subject: [PATCH 05/70] Reuse Vec allocations --- crates/ra_mbe/src/mbe_expander/transcriber.rs | 33 ++++++++++++------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/crates/ra_mbe/src/mbe_expander/transcriber.rs b/crates/ra_mbe/src/mbe_expander/transcriber.rs index 4b173edd3f..7c9bb4d00e 100644 --- a/crates/ra_mbe/src/mbe_expander/transcriber.rs +++ b/crates/ra_mbe/src/mbe_expander/transcriber.rs @@ -1,4 +1,4 @@ -//! Transcraber takes a template, like `fn $ident() {}`, a set of bindings like +//! Transcriber takes a template, like `fn $ident() {}`, a set of bindings like //! `$ident => foo`, interpolates variables in the template, to get `fn foo() {}` use ra_syntax::SmolStr; @@ -53,7 +53,8 @@ impl Bindings { pub(super) fn transcribe(template: &tt::Subtree, bindings: &Bindings) -> ExpandResult { assert!(template.delimiter == None); let mut ctx = ExpandCtx { bindings: &bindings, nesting: Vec::new() }; - expand_subtree(&mut ctx, template) + let mut arena: Vec = Vec::new(); + expand_subtree(&mut ctx, template, &mut arena) } #[derive(Debug)] @@ -73,8 +74,13 @@ struct ExpandCtx<'a> { nesting: Vec, } -fn expand_subtree(ctx: &mut ExpandCtx, template: &tt::Subtree) -> ExpandResult { - let mut buf: Vec = Vec::new(); +fn expand_subtree( + ctx: &mut ExpandCtx, + template: &tt::Subtree, + arena: &mut Vec, +) -> ExpandResult { + // remember how many elements are in the arena now - when returning, we want to drain exactly how many elements we added. This way, the recursive uses of the arena get their own "view" of the arena, but will reuse the allocation + let start_elements = arena.len(); let mut err = None; for op in parse_template(template) { let op = match op { @@ -85,25 +91,27 @@ fn expand_subtree(ctx: &mut ExpandCtx, template: &tt::Subtree) -> ExpandResult buf.push(tt.clone()), + Op::TokenTree(tt @ tt::TokenTree::Leaf(..)) => arena.push(tt.clone()), Op::TokenTree(tt::TokenTree::Subtree(tt)) => { - let ExpandResult(tt, e) = expand_subtree(ctx, tt); + let ExpandResult(tt, e) = expand_subtree(ctx, tt, arena); err = err.or(e); - buf.push(tt.into()); + arena.push(tt.into()); } Op::Var { name, kind: _ } => { let ExpandResult(fragment, e) = expand_var(ctx, name); err = err.or(e); - push_fragment(&mut buf, fragment); + push_fragment(arena, fragment); } Op::Repeat { subtree, kind, separator } => { - let ExpandResult(fragment, e) = expand_repeat(ctx, subtree, kind, separator); + let ExpandResult(fragment, e) = expand_repeat(ctx, subtree, kind, separator, arena); err = err.or(e); - push_fragment(&mut buf, fragment) + push_fragment(arena, fragment) } } } - ExpandResult(tt::Subtree { delimiter: template.delimiter, token_trees: buf }, err) + // drain the elements added in this instance of expand_subtree + let tts = arena.drain(start_elements..arena.len()).collect(); + ExpandResult(tt::Subtree { delimiter: template.delimiter, token_trees: tts }, err) } fn expand_var(ctx: &mut ExpandCtx, v: &SmolStr) -> ExpandResult { @@ -155,6 +163,7 @@ fn expand_repeat( template: &tt::Subtree, kind: RepeatKind, separator: Option, + arena: &mut Vec, ) -> ExpandResult { let mut buf: Vec = Vec::new(); ctx.nesting.push(NestingState { idx: 0, at_end: false, hit: false }); @@ -165,7 +174,7 @@ fn expand_repeat( let mut counter = 0; loop { - let ExpandResult(mut t, e) = expand_subtree(ctx, template); + let ExpandResult(mut t, e) = expand_subtree(ctx, template, arena); let nesting_state = ctx.nesting.last_mut().unwrap(); if nesting_state.at_end || !nesting_state.hit { break; From 03735be3174d195c3de53051dd7194283266368c Mon Sep 17 00:00:00 2001 From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Date: Sun, 17 May 2020 00:43:42 +0200 Subject: [PATCH 06/70] fix doctest inside impl block #4449 Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> --- crates/ra_ide/src/runnables.rs | 70 +++++++++++++++++++++++++++++++--- 1 file changed, 65 insertions(+), 5 deletions(-) diff --git a/crates/ra_ide/src/runnables.rs b/crates/ra_ide/src/runnables.rs index fa8a9d92c9..009ea813a0 100644 --- a/crates/ra_ide/src/runnables.rs +++ b/crates/ra_ide/src/runnables.rs @@ -1,6 +1,6 @@ //! FIXME: write short doc here -use hir::Semantics; +use hir::{AsAssocItem, HirDisplay, Semantics}; use itertools::Itertools; use ra_ide_db::RootDatabase; use ra_syntax::{ @@ -65,14 +65,36 @@ fn runnable_fn(sema: &Semantics, fn_def: ast::FnDef) -> Option { + Some(trait_item.name(sema.db).to_string()) + } + hir::AssocItemContainer::ImplDef(impl_def) => impl_def + .target_ty(sema.db) + .as_adt() + .map(|adt| adt.name(sema.db).to_string()), + } + }); + + let path_iter = module .path_to_root(sema.db) .into_iter() .rev() .filter_map(|it| it.name(sema.db)) - .map(|name| name.to_string()) - .chain(std::iter::once(name_string)) - .join("::"); + .map(|name| name.to_string()); + + let path = if let Some(impl_trait_name) = impl_trait_name { + path_iter + .chain(std::iter::once(impl_trait_name)) + .chain(std::iter::once(name_string)) + .join("::") + } else { + path_iter.chain(std::iter::once(name_string)).join("::") + }; + TestId::Path(path) } else { TestId::Name(name_string) @@ -237,6 +259,44 @@ mod tests { ); } + #[test] + fn test_runnables_doc_test_in_impl() { + let (analysis, pos) = analysis_and_position( + r#" + //- /lib.rs + <|> //empty + fn main() {} + + struct Data; + impl Data { + /// ``` + /// 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: 52..106, + kind: DocTest { + test_id: Path( + "Data::foo", + ), + }, + }, + ] + "### + ); + } + #[test] fn test_runnables_module() { let (analysis, pos) = analysis_and_position( From 947f95193a52ebdda3f727500144fe1f6d292868 Mon Sep 17 00:00:00 2001 From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Date: Sun, 17 May 2020 11:09:38 +0200 Subject: [PATCH 07/70] fix doctest inside impl block #4449 Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> --- crates/ra_ide/src/runnables.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/ra_ide/src/runnables.rs b/crates/ra_ide/src/runnables.rs index 009ea813a0..72eced343b 100644 --- a/crates/ra_ide/src/runnables.rs +++ b/crates/ra_ide/src/runnables.rs @@ -1,6 +1,6 @@ //! FIXME: write short doc here -use hir::{AsAssocItem, HirDisplay, Semantics}; +use hir::{AsAssocItem, Semantics}; use itertools::Itertools; use ra_ide_db::RootDatabase; use ra_syntax::{ @@ -266,7 +266,7 @@ mod tests { //- /lib.rs <|> //empty fn main() {} - + struct Data; impl Data { /// ``` From 01688f8d4f21a8c22582ff68ee995dd0d4a134b9 Mon Sep 17 00:00:00 2001 From: Coenen Benjamin Date: Sun, 17 May 2020 12:10:35 +0200 Subject: [PATCH 08/70] Fix doctests in an Impl block --- crates/ra_ide/src/runnables.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ra_ide/src/runnables.rs b/crates/ra_ide/src/runnables.rs index 72eced343b..131b8f307c 100644 --- a/crates/ra_ide/src/runnables.rs +++ b/crates/ra_ide/src/runnables.rs @@ -285,7 +285,7 @@ mod tests { kind: Bin, }, Runnable { - range: 52..106, + range: 51..105, kind: DocTest { test_id: Path( "Data::foo", From 12a3bf3c31d4c9a6d9ee110db174604f688ca0f0 Mon Sep 17 00:00:00 2001 From: Edwin Cheng Date: Sun, 17 May 2020 23:37:30 +0800 Subject: [PATCH 09/70] Create LowerCtx on the fly --- crates/ra_hir_def/src/body/lower.rs | 19 ++++++++------- crates/ra_hir_ty/src/tests/regression.rs | 31 ++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 9 deletions(-) diff --git a/crates/ra_hir_def/src/body/lower.rs b/crates/ra_hir_def/src/body/lower.rs index 443b057abe..c69e0efea6 100644 --- a/crates/ra_hir_def/src/body/lower.rs +++ b/crates/ra_hir_def/src/body/lower.rs @@ -60,13 +60,10 @@ pub(super) fn lower( params: Option, body: Option, ) -> (Body, BodySourceMap) { - let ctx = LowerCtx::new(db, expander.current_file_id.clone()); - ExprCollector { db, def, expander, - ctx, source_map: BodySourceMap::default(), body: Body { exprs: Arena::default(), @@ -83,7 +80,6 @@ struct ExprCollector<'a> { db: &'a dyn DefDatabase, def: DefWithBodyId, expander: Expander, - ctx: LowerCtx, body: Body, source_map: BodySourceMap, } @@ -122,6 +118,10 @@ impl ExprCollector<'_> { (self.body, self.source_map) } + fn ctx(&self) -> LowerCtx { + LowerCtx::new(self.db, self.expander.current_file_id) + } + fn alloc_expr(&mut self, expr: Expr, ptr: AstPtr) -> ExprId { let src = self.expander.to_source(ptr); let id = self.make_expr(expr, Ok(src.clone())); @@ -268,7 +268,7 @@ impl ExprCollector<'_> { }; let method_name = e.name_ref().map(|nr| nr.as_name()).unwrap_or_else(Name::missing); let generic_args = - e.type_arg_list().and_then(|it| GenericArgs::from_ast(&self.ctx, it)); + e.type_arg_list().and_then(|it| GenericArgs::from_ast(&self.ctx(), it)); self.alloc_expr( Expr::MethodCall { receiver, method_name, args, generic_args }, syntax_ptr, @@ -373,7 +373,7 @@ impl ExprCollector<'_> { } ast::Expr::CastExpr(e) => { let expr = self.collect_expr_opt(e.expr()); - let type_ref = TypeRef::from_ast_opt(&self.ctx, e.type_ref()); + let type_ref = TypeRef::from_ast_opt(&self.ctx(), e.type_ref()); self.alloc_expr(Expr::Cast { expr, type_ref }, syntax_ptr) } ast::Expr::RefExpr(e) => { @@ -396,7 +396,7 @@ impl ExprCollector<'_> { for param in pl.params() { let pat = self.collect_pat_opt(param.pat()); let type_ref = - param.ascribed_type().map(|it| TypeRef::from_ast(&self.ctx, it)); + param.ascribed_type().map(|it| TypeRef::from_ast(&self.ctx(), it)); args.push(pat); arg_types.push(type_ref); } @@ -404,7 +404,7 @@ impl ExprCollector<'_> { let ret_type = e .ret_type() .and_then(|r| r.type_ref()) - .map(|it| TypeRef::from_ast(&self.ctx, it)); + .map(|it| TypeRef::from_ast(&self.ctx(), it)); let body = self.collect_expr_opt(e.body()); self.alloc_expr(Expr::Lambda { args, arg_types, ret_type, body }, syntax_ptr) } @@ -507,7 +507,8 @@ impl ExprCollector<'_> { .map(|s| match s { ast::Stmt::LetStmt(stmt) => { let pat = self.collect_pat_opt(stmt.pat()); - let type_ref = stmt.ascribed_type().map(|it| TypeRef::from_ast(&self.ctx, it)); + let type_ref = + stmt.ascribed_type().map(|it| TypeRef::from_ast(&self.ctx(), it)); let initializer = stmt.initializer().map(|e| self.collect_expr(e)); Statement::Let { pat, type_ref, initializer } } diff --git a/crates/ra_hir_ty/src/tests/regression.rs b/crates/ra_hir_ty/src/tests/regression.rs index 115ad83289..c2168222ee 100644 --- a/crates/ra_hir_ty/src/tests/regression.rs +++ b/crates/ra_hir_ty/src/tests/regression.rs @@ -563,6 +563,37 @@ fn main() { ); } +#[test] +fn issue_4465_dollar_crate_at_type() { + assert_snapshot!( + infer(r#" +pub struct Foo {} +pub fn anything() -> T { + loop {} +} +macro_rules! foo { + () => {{ + let r: $crate::Foo = anything(); + r + }}; +} +fn main() { + let _a = foo!(); +} +"#), @r###" + 45..60 '{ loop {} }': T + 51..58 'loop {}': ! + 56..58 '{}': () + !0..31 '{letr:...g();r}': Foo + !4..5 'r': Foo + !18..26 'anything': fn anything() -> Foo + !18..28 'anything()': Foo + !29..30 'r': Foo + 164..188 '{ ...!(); }': () + 174..176 '_a': Foo +"###); +} + #[test] fn issue_4053_diesel_where_clauses() { assert_snapshot!( From dc217bdf90d555eaa1780041fc3a14e64173994d Mon Sep 17 00:00:00 2001 From: vsrs Date: Sun, 17 May 2020 19:51:44 +0300 Subject: [PATCH 10/70] CodeLens configuration options. --- crates/rust-analyzer/src/config.rs | 62 +++++-- .../rust-analyzer/src/main_loop/handlers.rs | 172 ++++++++++-------- editors/code/package.json | 15 ++ editors/code/src/config.ts | 11 ++ 4 files changed, 168 insertions(+), 92 deletions(-) diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 53aee833d6..b99b95bfca 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -33,6 +33,34 @@ pub struct Config { pub inlay_hints: InlayHintsConfig, pub completion: CompletionConfig, pub call_info_full: bool, + pub lens: LensConfig, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct LensConfig { + pub run: bool, + pub debug: bool, + pub impementations: bool, +} + +impl Default for LensConfig { + fn default() -> Self { + Self { run: true, debug: true, impementations: true } + } +} + +impl LensConfig { + pub fn any(&self) -> bool { + self.impementations || self.runnable() + } + + pub fn none(&self) -> bool { + !self.any() + } + + pub fn runnable(&self) -> bool { + self.run || self.debug + } } #[derive(Debug, Clone)] @@ -107,6 +135,7 @@ impl Default for Config { ..CompletionConfig::default() }, call_info_full: true, + lens: LensConfig::default(), } } } @@ -195,6 +224,9 @@ impl Config { set(value, "/completion/addCallParenthesis", &mut self.completion.add_call_parenthesis); set(value, "/completion/addCallArgumentSnippets", &mut self.completion.add_call_argument_snippets); set(value, "/callInfo/full", &mut self.call_info_full); + set(value, "/lens/run", &mut self.lens.run); + set(value, "/lens/debug", &mut self.lens.debug); + set(value, "/lens/implementations", &mut self.lens.impementations); log::info!("Config::update() = {:#?}", self); @@ -212,35 +244,35 @@ impl Config { pub fn update_caps(&mut self, caps: &ClientCapabilities) { if let Some(doc_caps) = caps.text_document.as_ref() { if let Some(value) = doc_caps.definition.as_ref().and_then(|it| it.link_support) { - self.client_caps.location_link = value; - } + self.client_caps.location_link = value; + } if let Some(value) = doc_caps.folding_range.as_ref().and_then(|it| it.line_folding_only) { - self.client_caps.line_folding_only = value - } + self.client_caps.line_folding_only = value + } if let Some(value) = doc_caps .document_symbol .as_ref() .and_then(|it| it.hierarchical_document_symbol_support) - { - self.client_caps.hierarchical_symbols = value - } + { + self.client_caps.hierarchical_symbols = value + } if let Some(value) = doc_caps .code_action .as_ref() .and_then(|it| Some(it.code_action_literal_support.is_some())) - { - self.client_caps.code_action_literals = value; - } - self.completion.allow_snippets(false); + { + self.client_caps.code_action_literals = value; + } + self.completion.allow_snippets(false); if let Some(completion) = &doc_caps.completion { - if let Some(completion_item) = &completion.completion_item { - if let Some(value) = completion_item.snippet_support { - self.completion.allow_snippets(value); - } + if let Some(completion_item) = &completion.completion_item { + if let Some(value) = completion_item.snippet_support { + self.completion.allow_snippets(value); } } } + } if let Some(window_caps) = caps.window.as_ref() { if let Some(value) = window_caps.work_done_progress { diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs index 6b14830b6d..808532d237 100644 --- a/crates/rust-analyzer/src/main_loop/handlers.rs +++ b/crates/rust-analyzer/src/main_loop/handlers.rs @@ -812,88 +812,106 @@ pub fn handle_code_lens( params: lsp_types::CodeLensParams, ) -> Result>> { let _p = profile("handle_code_lens"); - let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; - let line_index = world.analysis().file_line_index(file_id)?; - let mut lenses: Vec = Default::default(); - let cargo_spec = CargoTargetSpec::for_file(&world, file_id)?; - // Gather runnables - 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 => { - // Do not suggest binary run on other target than binary - match &cargo_spec { - Some(spec) => match spec.target_kind { - TargetKind::Bin => "Run", - _ => continue, - }, - None => continue, - } - } - } - .to_string(); - let mut r = to_lsp_runnable(&world, file_id, runnable)?; - let lens = CodeLens { - range: r.range, - command: Some(Command { - title, - command: "rust-analyzer.runSingle".into(), - arguments: Some(vec![to_value(&r).unwrap()]), - }), - data: None, - }; - lenses.push(lens); - - if r.args[0] == "run" { - r.args[0] = "build".into(); - } else { - r.args.push("--no-run".into()); - } - let debug_lens = CodeLens { - range: r.range, - command: Some(Command { - title: "Debug".into(), - command: "rust-analyzer.debugSingle".into(), - arguments: Some(vec![to_value(r).unwrap()]), - }), - data: None, - }; - lenses.push(debug_lens); + if world.config.lens.none() { + // early return before any db query! + return Ok(Some(lenses)); } - // Handle impls - lenses.extend( - world - .analysis() - .file_structure(file_id)? - .into_iter() - .filter(|it| match it.kind { - SyntaxKind::TRAIT_DEF | SyntaxKind::STRUCT_DEF | SyntaxKind::ENUM_DEF => true, - _ => false, - }) - .map(|it| { - let range = to_proto::range(&line_index, it.node_range); - let pos = range.start; - let lens_params = lsp_types::request::GotoImplementationParams { - text_document_position_params: lsp_types::TextDocumentPositionParams::new( - params.text_document.clone(), - pos, - ), - work_done_progress_params: Default::default(), - partial_result_params: Default::default(), - }; - CodeLens { - range, - command: None, - data: Some(to_value(CodeLensResolveData::Impls(lens_params)).unwrap()), - } - }), - ); + let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; + let line_index = world.analysis().file_line_index(file_id)?; + let cargo_spec = CargoTargetSpec::for_file(&world, file_id)?; + if world.config.lens.runnable() { + // Gather runnables + for runnable in world.analysis().runnables(file_id)? { + let (run_title, debugee ) = match &runnable.kind { + RunnableKind::Test { .. } | RunnableKind::TestMod { .. } => ("▶️\u{fe0e}Run Test", true), + RunnableKind::DocTest { .. } => { + // cargo does not support -no-run for doctests + ("▶️\u{fe0e}Run Doctest", false) + } + RunnableKind::Bench { .. } => { + // Nothing wrong with bench debugging + ("Run Bench", true) + }, + RunnableKind::Bin => { + // Do not suggest binary run on other target than binary + match &cargo_spec { + Some(spec) => match spec.target_kind { + TargetKind::Bin => ("Run", true), + _ => continue, + }, + None => continue, + } + } + }; + + let mut r = to_lsp_runnable(&world, file_id, runnable)?; + if world.config.lens.run { + let lens = CodeLens { + range: r.range, + command: Some(Command { + title: run_title.to_string(), + command: "rust-analyzer.runSingle".into(), + arguments: Some(vec![to_value(&r).unwrap()]), + }), + data: None, + }; + lenses.push(lens); + } + + if debugee && world.config.lens.debug { + if r.args[0] == "run" { + r.args[0] = "build".into(); + } else { + r.args.push("--no-run".into()); + } + let debug_lens = CodeLens { + range: r.range, + command: Some(Command { + title: "Debug".into(), + command: "rust-analyzer.debugSingle".into(), + arguments: Some(vec![to_value(r).unwrap()]), + }), + data: None, + }; + lenses.push(debug_lens); + } + } + } + + if world.config.lens.impementations { + // Handle impls + lenses.extend( + world + .analysis() + .file_structure(file_id)? + .into_iter() + .filter(|it| match it.kind { + SyntaxKind::TRAIT_DEF | SyntaxKind::STRUCT_DEF | SyntaxKind::ENUM_DEF => true, + _ => false, + }) + .map(|it| { + let range = to_proto::range(&line_index, it.node_range); + let pos = range.start; + let lens_params = lsp_types::request::GotoImplementationParams { + text_document_position_params: lsp_types::TextDocumentPositionParams::new( + params.text_document.clone(), + pos, + ), + work_done_progress_params: Default::default(), + partial_result_params: Default::default(), + }; + CodeLens { + range, + command: None, + data: Some(to_value(CodeLensResolveData::Impls(lens_params)).unwrap()), + } + }), + ); + } Ok(Some(lenses)) } diff --git a/editors/code/package.json b/editors/code/package.json index 2dbbde8527..efed4c7f2f 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -443,6 +443,21 @@ "type": "object", "default": {}, "description": "Optional settings passed to the debug engine. Example:\n{ \"lldb\": { \"terminal\":\"external\"} }" + }, + "rust-analyzer.lens.run": { + "description": "Whether to show Run lens.", + "type": "boolean", + "default": true + }, + "rust-analyzer.lens.debug": { + "description": "Whether to show Debug lens.", + "type": "boolean", + "default": true + }, + "rust-analyzer.lens.implementations": { + "description": "Whether to show Implementations lens.", + "type": "boolean", + "default": true } } }, diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts index 1652827c32..93d9aa1607 100644 --- a/editors/code/src/config.ts +++ b/editors/code/src/config.ts @@ -16,6 +16,9 @@ export class Config { "files", "highlighting", "updates.channel", + "lens.run", + "lens.debug", + "lens.implementations", ] .map(opt => `${this.rootSection}.${opt}`); @@ -119,4 +122,12 @@ export class Config { sourceFileMap: sourceFileMap }; } + + get lens() { + return { + run: this.get("lens.run"), + debug: this.get("lens.debug"), + implementations: this.get("lens.implementations"), + }; + } } From dec2f3fa657a2700f9db1962891e7be71c299543 Mon Sep 17 00:00:00 2001 From: vsrs Date: Sun, 17 May 2020 20:29:59 +0300 Subject: [PATCH 11/70] Runnable QuickPick with debuggees only --- editors/code/src/commands/runnables.ts | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/editors/code/src/commands/runnables.ts b/editors/code/src/commands/runnables.ts index b1d93fc34e..a408021e78 100644 --- a/editors/code/src/commands/runnables.ts +++ b/editors/code/src/commands/runnables.ts @@ -7,7 +7,7 @@ import { startDebugSession, getDebugConfiguration } from '../debug'; const quickPickButtons = [{ iconPath: new vscode.ThemeIcon("save"), tooltip: "Save as a launch.json configurtation." }]; -async function selectRunnable(ctx: Ctx, prevRunnable?: RunnableQuickPick, showButtons: boolean = true): Promise { +async function selectRunnable(ctx: Ctx, prevRunnable?: RunnableQuickPick, debuggeeOnly = false, showButtons: boolean = true): Promise { const editor = ctx.activeRustEditor; const client = ctx.client; if (!editor || !client) return; @@ -33,9 +33,20 @@ async function selectRunnable(ctx: Ctx, prevRunnable?: RunnableQuickPick, showBu ) { continue; } + + if (debuggeeOnly && (r.label.startsWith('doctest') || r.label.startsWith('cargo'))) { + continue; + } items.push(new RunnableQuickPick(r)); } + if( items.length === 0 ) { + // it is the debug case, run always has at least 'cargo check ...' + // see crates\rust-analyzer\src\main_loop\handlers.rs, handle_runnables + vscode.window.showErrorMessage("There's no debug target!"); + return; + } + return await new Promise((resolve) => { const disposables: vscode.Disposable[] = []; const close = (result?: RunnableQuickPick) => { @@ -107,7 +118,7 @@ export function debug(ctx: Ctx): Cmd { let prevDebuggee: RunnableQuickPick | undefined; return async () => { - const item = await selectRunnable(ctx, prevDebuggee); + const item = await selectRunnable(ctx, prevDebuggee, true); if (!item) return; item.detail = 'restart'; @@ -147,7 +158,7 @@ async function makeDebugConfig(ctx: Ctx, item: RunnableQuickPick): Promise export function newDebugConfig(ctx: Ctx): Cmd { return async () => { - const item = await selectRunnable(ctx, undefined, false); + const item = await selectRunnable(ctx, undefined, true, false); if (!item) return; await makeDebugConfig(ctx, item); From 3d445256fe56f4a7ead64514fb57b79079973d84 Mon Sep 17 00:00:00 2001 From: vsrs Date: Sun, 17 May 2020 20:38:50 +0300 Subject: [PATCH 12/70] code formatting --- crates/rust-analyzer/src/config.rs | 30 +++++++++---------- .../rust-analyzer/src/main_loop/handlers.rs | 10 ++++--- editors/code/src/commands/runnables.ts | 2 +- 3 files changed, 22 insertions(+), 20 deletions(-) diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index b99b95bfca..2038ef89b3 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -244,35 +244,35 @@ impl Config { pub fn update_caps(&mut self, caps: &ClientCapabilities) { if let Some(doc_caps) = caps.text_document.as_ref() { if let Some(value) = doc_caps.definition.as_ref().and_then(|it| it.link_support) { - self.client_caps.location_link = value; - } + self.client_caps.location_link = value; + } if let Some(value) = doc_caps.folding_range.as_ref().and_then(|it| it.line_folding_only) { - self.client_caps.line_folding_only = value - } + self.client_caps.line_folding_only = value + } if let Some(value) = doc_caps .document_symbol .as_ref() .and_then(|it| it.hierarchical_document_symbol_support) - { - self.client_caps.hierarchical_symbols = value - } + { + self.client_caps.hierarchical_symbols = value + } if let Some(value) = doc_caps .code_action .as_ref() .and_then(|it| Some(it.code_action_literal_support.is_some())) - { - self.client_caps.code_action_literals = value; - } - self.completion.allow_snippets(false); + { + self.client_caps.code_action_literals = value; + } + self.completion.allow_snippets(false); if let Some(completion) = &doc_caps.completion { - if let Some(completion_item) = &completion.completion_item { - if let Some(value) = completion_item.snippet_support { - self.completion.allow_snippets(value); + if let Some(completion_item) = &completion.completion_item { + if let Some(value) = completion_item.snippet_support { + self.completion.allow_snippets(value); + } } } } - } if let Some(window_caps) = caps.window.as_ref() { if let Some(value) = window_caps.work_done_progress { diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs index 808532d237..e675567528 100644 --- a/crates/rust-analyzer/src/main_loop/handlers.rs +++ b/crates/rust-analyzer/src/main_loop/handlers.rs @@ -826,16 +826,18 @@ pub fn handle_code_lens( if world.config.lens.runnable() { // Gather runnables for runnable in world.analysis().runnables(file_id)? { - let (run_title, debugee ) = match &runnable.kind { - RunnableKind::Test { .. } | RunnableKind::TestMod { .. } => ("▶️\u{fe0e}Run Test", true), - RunnableKind::DocTest { .. } => { + let (run_title, debugee) = match &runnable.kind { + RunnableKind::Test { .. } | RunnableKind::TestMod { .. } => { + ("▶️\u{fe0e}Run Test", true) + } + RunnableKind::DocTest { .. } => { // cargo does not support -no-run for doctests ("▶️\u{fe0e}Run Doctest", false) } RunnableKind::Bench { .. } => { // Nothing wrong with bench debugging ("Run Bench", true) - }, + } RunnableKind::Bin => { // Do not suggest binary run on other target than binary match &cargo_spec { diff --git a/editors/code/src/commands/runnables.ts b/editors/code/src/commands/runnables.ts index a408021e78..0bd30fb077 100644 --- a/editors/code/src/commands/runnables.ts +++ b/editors/code/src/commands/runnables.ts @@ -40,7 +40,7 @@ async function selectRunnable(ctx: Ctx, prevRunnable?: RunnableQuickPick, debugg items.push(new RunnableQuickPick(r)); } - if( items.length === 0 ) { + if (items.length === 0) { // it is the debug case, run always has at least 'cargo check ...' // see crates\rust-analyzer\src\main_loop\handlers.rs, handle_runnables vscode.window.showErrorMessage("There's no debug target!"); From 19a8c1450c1fd7263b49e2e176f8fef0d93d923b Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sun, 17 May 2020 15:57:30 +0200 Subject: [PATCH 13/70] Relax VS Code version requirement --- docs/user/readme.adoc | 2 +- editors/code/package.json | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/user/readme.adoc b/docs/user/readme.adoc index d750c7705b..03836e6e2c 100644 --- a/docs/user/readme.adoc +++ b/docs/user/readme.adoc @@ -63,7 +63,7 @@ The server binary is stored in: * macOS: `~/Library/Application Support/Code/User/globalStorage/matklad.rust-analyzer` * Windows: `%APPDATA%\Code\User\globalStorage\matklad.rust-analyzer` -Note that we only support the latest version of VS Code. +Note that we only support two most recent versions of VS Code. ==== Updates diff --git a/editors/code/package.json b/editors/code/package.json index 2dbbde8527..4045ab3d27 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -21,7 +21,7 @@ "Programming Languages" ], "engines": { - "vscode": "^1.45.0" + "vscode": "^1.44.0" }, "enableProposedApi": true, "scripts": { @@ -41,7 +41,7 @@ "@rollup/plugin-node-resolve": "^7.1.3", "@types/node": "^12.12.39", "@types/node-fetch": "^2.5.7", - "@types/vscode": "^1.45.0", + "@types/vscode": "^1.44.0", "@typescript-eslint/eslint-plugin": "^2.33.0", "@typescript-eslint/parser": "^2.33.0", "eslint": "^6.8.0", From 68db49c8534fa99768c7f600455ea76176f61994 Mon Sep 17 00:00:00 2001 From: Hrvoje Ban Date: Mon, 18 May 2020 08:07:31 +0200 Subject: [PATCH 14/70] Add more tests for Fn traits --- crates/ra_hir_ty/src/tests/traits.rs | 132 +++++++++++++++++++++++++++ 1 file changed, 132 insertions(+) diff --git a/crates/ra_hir_ty/src/tests/traits.rs b/crates/ra_hir_ty/src/tests/traits.rs index 9d32cbc7a6..c49aacf98d 100644 --- a/crates/ra_hir_ty/src/tests/traits.rs +++ b/crates/ra_hir_ty/src/tests/traits.rs @@ -1616,6 +1616,138 @@ fn test u128>(f: F) { ); } +#[test] +fn fn_ptr_and_item() { + assert_snapshot!( + infer(r#" +#[lang="fn_once"] +trait FnOnce { + type Output; + + fn call_once(self, args: Args) -> Self::Output; +} + +trait Foo { + fn foo(&self) -> T; +} + +struct Bar(T); + +impl R> Foo<(A1, R)> for Bar { + fn foo(&self) -> (A1, R) {} +} + +enum Opt { None, Some(T) } +impl Opt { + fn map U>(self, f: F) -> Opt {} +} + +fn test() { + let bar: Bar u32>; + bar.foo(); + + let opt: Opt; + let f: fn(u8) -> u32; + opt.map(f); +} +"#), + @r###" +75..79 'self': Self +81..85 'args': Args +140..144 'self': &Self +244..248 'self': &Bar +261..263 '{}': () +347..351 'self': Opt +353..354 'f': F +369..371 '{}': () +385..501 '{ ...(f); }': () +395..398 'bar': Bar u32> +424..427 'bar': Bar u32> +424..433 'bar.foo()': {unknown} +444..447 'opt': Opt +466..467 'f': fn(u8) -> u32 +488..491 'opt': Opt +488..498 'opt.map(f)': Opt u32, (u8,)>> +496..497 'f': fn(u8) -> u32 +"### + ); +} + +#[test] +fn fn_trait_deref_with_ty_default() { + assert_snapshot!( + infer(r#" +#[lang = "deref"] +trait Deref { + type Target; + + fn deref(&self) -> &Self::Target; +} + +#[lang="fn_once"] +trait FnOnce { + type Output; + + fn call_once(self, args: Args) -> Self::Output; +} + +struct Foo; + +impl Foo { + fn foo(&self) -> usize {} +} + +struct Lazy T>(F); + +impl Lazy { + pub fn new(f: F) -> Lazy {} +} + +impl T> Deref for Lazy { + type Target = T; +} + +fn test() { + let lazy1: Lazy = Lazy::new(|| Foo); + let r1 = lazy1.foo(); + + fn make_foo_fn() -> Foo {} + let make_foo_fn_ptr: fn() -> Foo = make_foo_fn; + let lazy2: Lazy = Lazy::new(make_foo_fn_ptr); + let r2 = lazy2.foo(); +} +"#), + @r###" +65..69 'self': &Self +166..170 'self': Self +172..176 'args': Args +240..244 'self': &Foo +255..257 '{}': () +335..336 'f': F +355..357 '{}': () +444..690 '{ ...o(); }': () +454..459 'lazy1': Lazy T> +476..485 'Lazy::new': fn new T>(fn() -> T) -> Lazy T> +476..493 'Lazy::...| Foo)': Lazy T> +486..492 '|| Foo': || -> T +489..492 'Foo': Foo +503..505 'r1': {unknown} +508..513 'lazy1': Lazy T> +508..519 'lazy1.foo()': {unknown} +561..576 'make_foo_fn_ptr': fn() -> Foo +592..603 'make_foo_fn': fn make_foo_fn() -> Foo +613..618 'lazy2': Lazy T> +635..644 'Lazy::new': fn new T>(fn() -> T) -> Lazy T> +635..661 'Lazy::...n_ptr)': Lazy T> +645..660 'make_foo_fn_ptr': fn() -> Foo +671..673 'r2': {unknown} +676..681 'lazy2': Lazy T> +676..687 'lazy2.foo()': {unknown} +550..552 '{}': () +"### + ); +} + #[test] fn closure_1() { assert_snapshot!( From 78817a319476d8af40c4f78e8c47dc958781f88f Mon Sep 17 00:00:00 2001 From: vsrs Date: Mon, 18 May 2020 10:27:00 +0300 Subject: [PATCH 15/70] Add "rust-analyzer.lens.enable" --- crates/rust-analyzer/src/config.rs | 15 ++++++++++++--- editors/code/package.json | 11 ++++++++--- editors/code/src/config.ts | 2 ++ 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 2038ef89b3..b5dc6f0fa9 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -50,6 +50,8 @@ impl Default for LensConfig { } impl LensConfig { + pub const NO_LENS: LensConfig = Self { run: false, debug: false, impementations: false }; + pub fn any(&self) -> bool { self.impementations || self.runnable() } @@ -224,9 +226,16 @@ impl Config { set(value, "/completion/addCallParenthesis", &mut self.completion.add_call_parenthesis); set(value, "/completion/addCallArgumentSnippets", &mut self.completion.add_call_argument_snippets); set(value, "/callInfo/full", &mut self.call_info_full); - set(value, "/lens/run", &mut self.lens.run); - set(value, "/lens/debug", &mut self.lens.debug); - set(value, "/lens/implementations", &mut self.lens.impementations); + + let mut lens_enabled = true; + set(value, "/lens/enable", &mut lens_enabled); + if lens_enabled { + set(value, "/lens/run", &mut self.lens.run); + set(value, "/lens/debug", &mut self.lens.debug); + set(value, "/lens/implementations", &mut self.lens.impementations); + } else { + self.lens = LensConfig::NO_LENS; + } log::info!("Config::update() = {:#?}", self); diff --git a/editors/code/package.json b/editors/code/package.json index efed4c7f2f..38c77533cd 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -444,18 +444,23 @@ "default": {}, "description": "Optional settings passed to the debug engine. Example:\n{ \"lldb\": { \"terminal\":\"external\"} }" }, + "rust-analyzer.lens.enable": { + "description": "Whether to show CodeLens in Rust files.", + "type": "boolean", + "default": true + }, "rust-analyzer.lens.run": { - "description": "Whether to show Run lens.", + "markdownDescription": "Whether to show Run lens. Only applies when `#rust-analyzer.lens.enable#` is set.", "type": "boolean", "default": true }, "rust-analyzer.lens.debug": { - "description": "Whether to show Debug lens.", + "markdownDescription": "Whether to show Debug lens. Only applies when `#rust-analyzer.lens.enable#` is set.", "type": "boolean", "default": true }, "rust-analyzer.lens.implementations": { - "description": "Whether to show Implementations lens.", + "markdownDescription": "Whether to show Implementations lens. Only applies when `#rust-analyzer.lens.enable#` is set.", "type": "boolean", "default": true } diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts index 93d9aa1607..ee294fbe31 100644 --- a/editors/code/src/config.ts +++ b/editors/code/src/config.ts @@ -16,6 +16,7 @@ export class Config { "files", "highlighting", "updates.channel", + "lens.enable", "lens.run", "lens.debug", "lens.implementations", @@ -125,6 +126,7 @@ export class Config { get lens() { return { + enable: this.get("lens.enable"), run: this.get("lens.run"), debug: this.get("lens.debug"), implementations: this.get("lens.implementations"), From 8f80df111722a1f1685a8aea02a2612f642ea8f6 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Mon, 18 May 2020 21:42:39 +0200 Subject: [PATCH 16/70] Querify `importable_locations_in_crate` This brings the time needed to compute the `add_missing_impl_members` assist down from ~5 minutes to 20 seconds --- crates/ra_hir_def/src/db.rs | 12 +++++++++++- crates/ra_hir_def/src/find_path.rs | 11 +++++++---- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/crates/ra_hir_def/src/db.rs b/crates/ra_hir_def/src/db.rs index e665ab45d0..498a4c9172 100644 --- a/crates/ra_hir_def/src/db.rs +++ b/crates/ra_hir_def/src/db.rs @@ -1,7 +1,7 @@ //! Defines database & queries for name resolution. use std::sync::Arc; -use hir_expand::{db::AstDatabase, HirFileId}; +use hir_expand::{db::AstDatabase, name::Name, HirFileId}; use ra_db::{salsa, CrateId, SourceDatabase, Upcast}; use ra_prof::profile; use ra_syntax::SmolStr; @@ -12,9 +12,12 @@ use crate::{ body::{scope::ExprScopes, Body, BodySourceMap}, data::{ConstData, FunctionData, ImplData, StaticData, TraitData, TypeAliasData}, docs::Documentation, + find_path, generics::GenericParams, + item_scope::ItemInNs, lang_item::{LangItemTarget, LangItems}, nameres::{raw::RawItems, CrateDefMap}, + visibility::Visibility, AttrDefId, ConstId, ConstLoc, DefWithBodyId, EnumId, EnumLoc, FunctionId, FunctionLoc, GenericDefId, ImplId, ImplLoc, ModuleId, StaticId, StaticLoc, StructId, StructLoc, TraitId, TraitLoc, TypeAliasId, TypeAliasLoc, UnionId, UnionLoc, @@ -108,6 +111,13 @@ pub trait DefDatabase: InternDatabase + AstDatabase + Upcast { // Remove this query completely, in favor of `Attrs::docs` method #[salsa::invoke(Documentation::documentation_query)] fn documentation(&self, def: AttrDefId) -> Option; + + #[salsa::invoke(find_path::importable_locations_in_crate)] + fn importable_locations_of( + &self, + item: ItemInNs, + krate: CrateId, + ) -> Arc<[(ModuleId, Name, Visibility)]>; } fn crate_def_map_wait(db: &impl DefDatabase, krate: CrateId) -> Arc { diff --git a/crates/ra_hir_def/src/find_path.rs b/crates/ra_hir_def/src/find_path.rs index 70dcb03e6e..1ca20fabd0 100644 --- a/crates/ra_hir_def/src/find_path.rs +++ b/crates/ra_hir_def/src/find_path.rs @@ -8,6 +8,7 @@ use crate::{ CrateId, ModuleDefId, ModuleId, }; use hir_expand::name::{known, AsName, Name}; +use std::sync::Arc; use test_utils::tested_by; const MAX_PATH_LEN: usize = 15; @@ -45,6 +46,7 @@ impl ModPath { /// Find a path that can be used to refer to a certain item. This can depend on /// *from where* you're referring to the item, hence the `from` parameter. pub fn find_path(db: &dyn DefDatabase, item: ItemInNs, from: ModuleId) -> Option { + let _p = ra_prof::profile("find_path"); find_path_inner(db, item, from, MAX_PATH_LEN) } @@ -198,7 +200,7 @@ fn find_importable_locations( .chain(crate_graph[from.krate].dependencies.iter().map(|dep| dep.crate_id)) { result.extend( - importable_locations_in_crate(db, item, krate) + db.importable_locations_of(item, krate) .iter() .filter(|(_, _, vis)| vis.is_visible_from(db, from)) .map(|(m, n, _)| (*m, n.clone())), @@ -213,11 +215,11 @@ fn find_importable_locations( /// /// Note that the crate doesn't need to be the one in which the item is defined; /// it might be re-exported in other crates. -fn importable_locations_in_crate( +pub(crate) fn importable_locations_in_crate( db: &dyn DefDatabase, item: ItemInNs, krate: CrateId, -) -> Vec<(ModuleId, Name, Visibility)> { +) -> Arc<[(ModuleId, Name, Visibility)]> { let def_map = db.crate_def_map(krate); let mut result = Vec::new(); for (local_id, data) in def_map.modules.iter() { @@ -243,7 +245,8 @@ fn importable_locations_in_crate( result.push((ModuleId { krate, local_id }, name.clone(), vis)); } } - result + + Arc::from(result) } #[cfg(test)] From 0fe876925e59aad4765b415d9caaf262a6d43c4c Mon Sep 17 00:00:00 2001 From: Roland Ruckerbauer Date: Mon, 18 May 2020 23:39:10 +0200 Subject: [PATCH 17/70] Infer return type of loops with value breaks. --- crates/ra_hir_ty/src/infer.rs | 1 + crates/ra_hir_ty/src/infer/expr.rs | 21 +++++++++++++++------ 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/crates/ra_hir_ty/src/infer.rs b/crates/ra_hir_ty/src/infer.rs index 2876cb141a..957d6e0b57 100644 --- a/crates/ra_hir_ty/src/infer.rs +++ b/crates/ra_hir_ty/src/infer.rs @@ -218,6 +218,7 @@ struct InferenceContext<'a> { #[derive(Clone, Debug)] struct BreakableContext { pub may_break: bool, + pub break_ty: Ty, } impl<'a> InferenceContext<'a> { diff --git a/crates/ra_hir_ty/src/infer/expr.rs b/crates/ra_hir_ty/src/infer/expr.rs index 0b67d216a8..c7aa67fbe1 100644 --- a/crates/ra_hir_ty/src/infer/expr.rs +++ b/crates/ra_hir_ty/src/infer/expr.rs @@ -93,7 +93,7 @@ impl<'a> InferenceContext<'a> { Ty::Unknown } Expr::Loop { body } => { - self.breakables.push(BreakableContext { may_break: false }); + self.breakables.push(BreakableContext { may_break: false, break_ty: Ty::Unknown }); self.infer_expr(*body, &Expectation::has_type(Ty::unit())); let ctxt = self.breakables.pop().expect("breakable stack broken"); @@ -102,13 +102,13 @@ impl<'a> InferenceContext<'a> { } // FIXME handle break with value if ctxt.may_break { - Ty::unit() + ctxt.break_ty } else { Ty::simple(TypeCtor::Never) } } Expr::While { condition, body } => { - self.breakables.push(BreakableContext { may_break: false }); + self.breakables.push(BreakableContext { may_break: false, break_ty: Ty::Unknown }); // while let is desugared to a match loop, so this is always simple while self.infer_expr(*condition, &Expectation::has_type(Ty::simple(TypeCtor::Bool))); self.infer_expr(*body, &Expectation::has_type(Ty::unit())); @@ -120,7 +120,7 @@ impl<'a> InferenceContext<'a> { Expr::For { iterable, body, pat } => { let iterable_ty = self.infer_expr(*iterable, &Expectation::none()); - self.breakables.push(BreakableContext { may_break: false }); + self.breakables.push(BreakableContext { may_break: false, break_ty: Ty::Unknown }); let pat_ty = self.resolve_associated_type(iterable_ty, self.resolve_into_iter_item()); @@ -229,12 +229,21 @@ impl<'a> InferenceContext<'a> { } Expr::Continue => Ty::simple(TypeCtor::Never), Expr::Break { expr } => { + let mut has_val_ty = None; + if let Some(expr) = expr { - // FIXME handle break with value - self.infer_expr(*expr, &Expectation::none()); + has_val_ty = Some(self.infer_expr(*expr, &Expectation::none())); } + if let Some(ctxt) = self.breakables.last_mut() { ctxt.may_break = true; + if let Some(val_ty) = has_val_ty { + if ctxt.break_ty == Ty::Unknown { + ctxt.break_ty = val_ty; + } else if ctxt.break_ty != val_ty { + // TODO: Unify partially matching type information (Option<{unknown}> + Option => Option) + } + } } else { self.push_diagnostic(InferenceDiagnostic::BreakOutsideOfLoop { expr: tgt_expr, From f76f737b91ceff076d77a9626ae1a796a1b38d50 Mon Sep 17 00:00:00 2001 From: kjeremy Date: Tue, 19 May 2020 10:11:34 -0400 Subject: [PATCH 18/70] Update crates --- Cargo.lock | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4412b07083..c062366923 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,9 +2,9 @@ # It is not intended for manual editing. [[package]] name = "addr2line" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "456d75cbb82da1ad150c8a9d97285ffcd21c9931dcb11e995903e7d75141b38b" +checksum = "a49806b9dadc843c61e7c97e72490ad7f7220ae249012fbda9ad0609457c0543" dependencies = [ "gimli", ] @@ -20,9 +20,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.29" +version = "1.0.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc98824304f5513bb8f862f9e5985219003de4d730689e59d8f28818283a6fe4" +checksum = "85bb70cc08ec97ca5450e6eba421deeea5f172c0fc61f78b5357b2a8e8be195f" [[package]] name = "anymap" @@ -55,9 +55,9 @@ checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" [[package]] name = "backtrace" -version = "0.3.47" +version = "0.3.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5393cb2f40a6fae0014c9af00018e95846f3b241b331a6b7733c326d3e58108" +checksum = "0df2f85c8a2abbe3b7d7e748052fdd9b76a0458fdeb16ad4223f5eca78c7c130" dependencies = [ "addr2line", "cfg-if", @@ -101,9 +101,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.52" +version = "1.0.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d87b23d6a92cd03af510a5ade527033f6aa6fa92161e2d5863a907d4c5e31d" +checksum = "404b1fe4f65288577753b17e3b36a04596ee784493ec249bf81c7f2d2acd751c" [[package]] name = "cfg-if" @@ -360,9 +360,9 @@ checksum = "86d4de0081402f5e88cdac65c8dcdcc73118c1a7a465e2a05f0da05843a8ea33" [[package]] name = "fnv" -version = "1.0.6" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "fs_extra" @@ -463,9 +463,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61565ff7aaace3525556587bd2dc31d4a07071957be715e63ce7b1eccf51a8f4" +checksum = "91780f809e750b0a89f5544be56617ff6b1227ee485bcb06ebe10cdf89bd3b71" dependencies = [ "libc", ] @@ -809,9 +809,9 @@ checksum = "9cbca9424c482ee628fa549d9c812e2cd22f1180b9222c9200fdfa6eb31aecb2" [[package]] name = "once_cell" -version = "1.3.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1c601810575c99596d4afc46f78a678c80105117c379eb3650cf99b8a21ce5b" +checksum = "0b631f7e854af39a1739f401cf34a8a013dfe09eac4fa4dba91e9768bd28168d" [[package]] name = "ordermap" @@ -895,9 +895,9 @@ checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" [[package]] name = "ppv-lite86" -version = "0.2.6" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" +checksum = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea" [[package]] name = "proc-macro-hack" @@ -907,18 +907,18 @@ checksum = "0d659fe7c6d27f25e9d80a1a094c223f5246f6a6596453e09d7229bf42750b63" [[package]] name = "proc-macro2" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8872cf6f48eee44265156c111456a700ab3483686b3f96df4cf5481c89157319" +checksum = "53f5ffe53a6b28e37c9c1ce74893477864d64f74778a93a4beb43c8fa167f639" dependencies = [ "unicode-xid", ] [[package]] name = "quote" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42934bc9c8ab0d3b273a16d8551c8f0fcff46be73276ca083ec2414c15c4ba5e" +checksum = "54a21852a652ad6f610c9510194f398ff6f8692e334fd1145fed931f7fbe44ea" dependencies = [ "proc-macro2", ] @@ -1610,9 +1610,9 @@ checksum = "ab16ced94dbd8a46c82fd81e3ed9a8727dac2977ea869d217bcc4ea1f122e81f" [[package]] name = "syn" -version = "1.0.21" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4696caa4048ac7ce2bcd2e484b3cef88c1004e41b8e945a277e2c25dc0b72060" +checksum = "1425de3c33b0941002740a420b1a906a350b88d08b82b2c8a01035a3f9447bac" dependencies = [ "proc-macro2", "quote", From 5c9ebbeaa415a5ff811b3be5058d8cb374e9baac Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 19 May 2020 16:43:26 +0200 Subject: [PATCH 19/70] Cleanup imports --- crates/ra_hir_def/src/find_path.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/crates/ra_hir_def/src/find_path.rs b/crates/ra_hir_def/src/find_path.rs index 1ca20fabd0..c7976535c4 100644 --- a/crates/ra_hir_def/src/find_path.rs +++ b/crates/ra_hir_def/src/find_path.rs @@ -1,5 +1,10 @@ //! An algorithm to find a path to refer to a certain item. +use std::sync::Arc; + +use hir_expand::name::{known, AsName, Name}; +use test_utils::tested_by; + use crate::{ db::DefDatabase, item_scope::ItemInNs, @@ -7,9 +12,6 @@ use crate::{ visibility::Visibility, CrateId, ModuleDefId, ModuleId, }; -use hir_expand::name::{known, AsName, Name}; -use std::sync::Arc; -use test_utils::tested_by; const MAX_PATH_LEN: usize = 15; From 908da9ac1b04a0eb27552ec9ca68a5f88c4f42c7 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 19 May 2020 16:45:57 +0200 Subject: [PATCH 20/70] Simplify --- crates/ra_hir_def/src/find_path.rs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/crates/ra_hir_def/src/find_path.rs b/crates/ra_hir_def/src/find_path.rs index c7976535c4..aee7e75118 100644 --- a/crates/ra_hir_def/src/find_path.rs +++ b/crates/ra_hir_def/src/find_path.rs @@ -17,18 +17,14 @@ const MAX_PATH_LEN: usize = 15; impl ModPath { fn starts_with_std(&self) -> bool { - self.segments.first().filter(|&first_segment| first_segment == &known::std).is_some() + self.segments.first() == Some(&known::std) } // When std library is present, paths starting with `std::` // should be preferred over paths starting with `core::` and `alloc::` fn can_start_with_std(&self) -> bool { - self.segments - .first() - .filter(|&first_segment| { - first_segment == &known::alloc || first_segment == &known::core - }) - .is_some() + let first_segment = self.segments.first(); + first_segment == Some(&known::alloc) || first_segment == Some(&known::core) } fn len(&self) -> usize { From 01bd1e1296d31dbb102d198e9fd82e1e36f1193b Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 19 May 2020 16:46:33 +0200 Subject: [PATCH 21/70] Move public API to the top --- crates/ra_hir_def/src/find_path.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/crates/ra_hir_def/src/find_path.rs b/crates/ra_hir_def/src/find_path.rs index aee7e75118..15bc04c1ac 100644 --- a/crates/ra_hir_def/src/find_path.rs +++ b/crates/ra_hir_def/src/find_path.rs @@ -13,6 +13,15 @@ use crate::{ CrateId, ModuleDefId, ModuleId, }; +// FIXME: handle local items + +/// Find a path that can be used to refer to a certain item. This can depend on +/// *from where* you're referring to the item, hence the `from` parameter. +pub fn find_path(db: &dyn DefDatabase, item: ItemInNs, from: ModuleId) -> Option { + let _p = ra_prof::profile("find_path"); + find_path_inner(db, item, from, MAX_PATH_LEN) +} + const MAX_PATH_LEN: usize = 15; impl ModPath { @@ -39,15 +48,6 @@ impl ModPath { } } -// FIXME: handle local items - -/// Find a path that can be used to refer to a certain item. This can depend on -/// *from where* you're referring to the item, hence the `from` parameter. -pub fn find_path(db: &dyn DefDatabase, item: ItemInNs, from: ModuleId) -> Option { - let _p = ra_prof::profile("find_path"); - find_path_inner(db, item, from, MAX_PATH_LEN) -} - fn find_path_inner( db: &dyn DefDatabase, item: ItemInNs, From dce31efdde9ca0311ed60f04b97049d42ed49ba8 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 19 May 2020 16:54:45 +0200 Subject: [PATCH 22/70] Cleanup query fn naming --- crates/ra_hir_def/src/db.rs | 2 +- crates/ra_hir_def/src/find_path.rs | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/crates/ra_hir_def/src/db.rs b/crates/ra_hir_def/src/db.rs index 498a4c9172..2f71511ba9 100644 --- a/crates/ra_hir_def/src/db.rs +++ b/crates/ra_hir_def/src/db.rs @@ -112,7 +112,7 @@ pub trait DefDatabase: InternDatabase + AstDatabase + Upcast { #[salsa::invoke(Documentation::documentation_query)] fn documentation(&self, def: AttrDefId) -> Option; - #[salsa::invoke(find_path::importable_locations_in_crate)] + #[salsa::invoke(find_path::importable_locations_of_query)] fn importable_locations_of( &self, item: ItemInNs, diff --git a/crates/ra_hir_def/src/find_path.rs b/crates/ra_hir_def/src/find_path.rs index 15bc04c1ac..2eb12ec8f2 100644 --- a/crates/ra_hir_def/src/find_path.rs +++ b/crates/ra_hir_def/src/find_path.rs @@ -3,6 +3,7 @@ use std::sync::Arc; use hir_expand::name::{known, AsName, Name}; +use ra_prof::profile; use test_utils::tested_by; use crate::{ @@ -18,7 +19,7 @@ use crate::{ /// Find a path that can be used to refer to a certain item. This can depend on /// *from where* you're referring to the item, hence the `from` parameter. pub fn find_path(db: &dyn DefDatabase, item: ItemInNs, from: ModuleId) -> Option { - let _p = ra_prof::profile("find_path"); + let _p = profile("find_path"); find_path_inner(db, item, from, MAX_PATH_LEN) } @@ -213,11 +214,12 @@ fn find_importable_locations( /// /// Note that the crate doesn't need to be the one in which the item is defined; /// it might be re-exported in other crates. -pub(crate) fn importable_locations_in_crate( +pub(crate) fn importable_locations_of_query( db: &dyn DefDatabase, item: ItemInNs, krate: CrateId, ) -> Arc<[(ModuleId, Name, Visibility)]> { + let _p = profile("importable_locations_of_query"); let def_map = db.crate_def_map(krate); let mut result = Vec::new(); for (local_id, data) in def_map.modules.iter() { From c847c079fd66d7ed09c64ff398baf05317b16500 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sun, 17 May 2020 12:09:53 +0200 Subject: [PATCH 23/70] Add AssistConfig --- crates/ra_assists/src/assist_config.rs | 27 ++++++++++ crates/ra_assists/src/assist_context.rs | 51 ++++++++++++++++--- crates/ra_assists/src/lib.rs | 15 ++++-- crates/ra_assists/src/tests.rs | 38 +++++++------- crates/ra_ide/src/completion.rs | 2 +- crates/ra_ide/src/completion/test_utils.rs | 2 +- crates/ra_ide/src/diagnostics.rs | 2 + crates/ra_ide/src/lib.rs | 10 ++-- crates/ra_ide/src/references/rename.rs | 3 ++ crates/ra_ide_db/src/source_change.rs | 5 ++ .../rust-analyzer/src/cli/analysis_bench.rs | 2 +- crates/rust-analyzer/src/config.rs | 5 +- .../rust-analyzer/src/main_loop/handlers.rs | 6 ++- 13 files changed, 129 insertions(+), 39 deletions(-) create mode 100644 crates/ra_assists/src/assist_config.rs diff --git a/crates/ra_assists/src/assist_config.rs b/crates/ra_assists/src/assist_config.rs new file mode 100644 index 0000000000..c0a0226fb2 --- /dev/null +++ b/crates/ra_assists/src/assist_config.rs @@ -0,0 +1,27 @@ +//! Settings for tweaking assists. +//! +//! The fun thing here is `SnippetCap` -- this type can only be created in this +//! module, and we use to statically check that we only produce snippet +//! assists if we are allowed to. + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct AssistConfig { + pub snippet_cap: Option, +} + +impl AssistConfig { + pub fn allow_snippets(&mut self, yes: bool) { + self.snippet_cap = if yes { Some(SnippetCap { _private: () }) } else { None } + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct SnippetCap { + _private: (), +} + +impl Default for AssistConfig { + fn default() -> Self { + AssistConfig { snippet_cap: Some(SnippetCap { _private: () }) } + } +} diff --git a/crates/ra_assists/src/assist_context.rs b/crates/ra_assists/src/assist_context.rs index a680f752b3..b90bbf8b2e 100644 --- a/crates/ra_assists/src/assist_context.rs +++ b/crates/ra_assists/src/assist_context.rs @@ -15,7 +15,10 @@ use ra_syntax::{ }; use ra_text_edit::TextEditBuilder; -use crate::{Assist, AssistId, GroupLabel, ResolvedAssist}; +use crate::{ + assist_config::{AssistConfig, SnippetCap}, + Assist, AssistId, GroupLabel, ResolvedAssist, +}; /// `AssistContext` allows to apply an assist or check if it could be applied. /// @@ -48,6 +51,7 @@ use crate::{Assist, AssistId, GroupLabel, ResolvedAssist}; /// moment, because the LSP API is pretty awkward in this place, and it's much /// easier to just compute the edit eagerly :-) pub(crate) struct AssistContext<'a> { + pub(crate) config: &'a AssistConfig, pub(crate) sema: Semantics<'a, RootDatabase>, pub(crate) db: &'a RootDatabase, pub(crate) frange: FileRange, @@ -55,10 +59,14 @@ pub(crate) struct AssistContext<'a> { } impl<'a> AssistContext<'a> { - pub fn new(sema: Semantics<'a, RootDatabase>, frange: FileRange) -> AssistContext<'a> { + pub(crate) fn new( + sema: Semantics<'a, RootDatabase>, + config: &'a AssistConfig, + frange: FileRange, + ) -> AssistContext<'a> { let source_file = sema.parse(frange.file_id); let db = sema.db; - AssistContext { sema, db, frange, source_file } + AssistContext { config, sema, db, frange, source_file } } // NB, this ignores active selection. @@ -165,11 +173,17 @@ pub(crate) struct AssistBuilder { edit: TextEditBuilder, cursor_position: Option, file: FileId, + is_snippet: bool, } impl AssistBuilder { pub(crate) fn new(file: FileId) -> AssistBuilder { - AssistBuilder { edit: TextEditBuilder::default(), cursor_position: None, file } + AssistBuilder { + edit: TextEditBuilder::default(), + cursor_position: None, + file, + is_snippet: false, + } } /// Remove specified `range` of text. @@ -180,10 +194,30 @@ impl AssistBuilder { pub(crate) fn insert(&mut self, offset: TextSize, text: impl Into) { self.edit.insert(offset, text.into()) } + /// Append specified `text` at the given `offset` + pub(crate) fn insert_snippet( + &mut self, + _cap: SnippetCap, + offset: TextSize, + text: impl Into, + ) { + self.is_snippet = true; + self.edit.insert(offset, text.into()) + } /// Replaces specified `range` of text with a given string. pub(crate) fn replace(&mut self, range: TextRange, replace_with: impl Into) { self.edit.replace(range, replace_with.into()) } + /// Append specified `text` at the given `offset` + pub(crate) fn replace_snippet( + &mut self, + _cap: SnippetCap, + range: TextRange, + replace_with: impl Into, + ) { + self.is_snippet = true; + self.edit.replace(range, replace_with.into()) + } pub(crate) fn replace_ast(&mut self, old: N, new: N) { algo::diff(old.syntax(), new.syntax()).into_text_edit(&mut self.edit) } @@ -227,7 +261,12 @@ impl AssistBuilder { if edit.is_empty() && self.cursor_position.is_none() { panic!("Only call `add_assist` if the assist can be applied") } - SingleFileChange { label: change_label, edit, cursor_position: self.cursor_position } - .into_source_change(self.file) + let mut res = + SingleFileChange { label: change_label, edit, cursor_position: self.cursor_position } + .into_source_change(self.file); + if self.is_snippet { + res.is_snippet = true; + } + res } } diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs index b6dc7cb1bf..7f0a723c9e 100644 --- a/crates/ra_assists/src/lib.rs +++ b/crates/ra_assists/src/lib.rs @@ -10,6 +10,7 @@ macro_rules! eprintln { ($($tt:tt)*) => { stdx::eprintln!($($tt)*) }; } +mod assist_config; mod assist_context; mod marks; #[cfg(test)] @@ -24,6 +25,8 @@ use ra_syntax::TextRange; pub(crate) use crate::assist_context::{AssistContext, Assists}; +pub use assist_config::AssistConfig; + /// Unique identifier of the assist, should not be shown to the user /// directly. #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -54,9 +57,9 @@ impl Assist { /// /// Assists are returned in the "unresolved" state, that is only labels are /// returned, without actual edits. - pub fn unresolved(db: &RootDatabase, range: FileRange) -> Vec { + pub fn unresolved(db: &RootDatabase, config: &AssistConfig, range: FileRange) -> Vec { let sema = Semantics::new(db); - let ctx = AssistContext::new(sema, range); + let ctx = AssistContext::new(sema, config, range); let mut acc = Assists::new_unresolved(&ctx); handlers::all().iter().for_each(|handler| { handler(&mut acc, &ctx); @@ -68,9 +71,13 @@ impl Assist { /// /// Assists are returned in the "resolved" state, that is with edit fully /// computed. - pub fn resolved(db: &RootDatabase, range: FileRange) -> Vec { + pub fn resolved( + db: &RootDatabase, + config: &AssistConfig, + range: FileRange, + ) -> Vec { let sema = Semantics::new(db); - let ctx = AssistContext::new(sema, range); + let ctx = AssistContext::new(sema, config, range); let mut acc = Assists::new_resolved(&ctx); handlers::all().iter().for_each(|handler| { handler(&mut acc, &ctx); diff --git a/crates/ra_assists/src/tests.rs b/crates/ra_assists/src/tests.rs index a3eacb8f11..9ba3da7862 100644 --- a/crates/ra_assists/src/tests.rs +++ b/crates/ra_assists/src/tests.rs @@ -11,7 +11,7 @@ use test_utils::{ RangeOrOffset, }; -use crate::{handlers::Handler, Assist, AssistContext, Assists}; +use crate::{handlers::Handler, Assist, AssistConfig, AssistContext, Assists}; pub(crate) fn with_single_file(text: &str) -> (RootDatabase, FileId) { let (mut db, file_id) = RootDatabase::with_single_file(text); @@ -41,14 +41,14 @@ fn check_doc_test(assist_id: &str, before: &str, after: &str) { let (db, file_id) = crate::tests::with_single_file(&before); let frange = FileRange { file_id, range: selection.into() }; - let mut assist = Assist::resolved(&db, frange) + let mut assist = Assist::resolved(&db, &AssistConfig::default(), frange) .into_iter() .find(|assist| assist.assist.id.0 == assist_id) .unwrap_or_else(|| { panic!( "\n\nAssist is not applicable: {}\nAvailable assists: {}", assist_id, - Assist::resolved(&db, frange) + Assist::resolved(&db, &AssistConfig::default(), frange) .into_iter() .map(|assist| assist.assist.id.0) .collect::>() @@ -90,7 +90,8 @@ fn check(handler: Handler, before: &str, expected: ExpectedResult) { let frange = FileRange { file_id: file_with_caret_id, range: range_or_offset.into() }; let sema = Semantics::new(&db); - let ctx = AssistContext::new(sema, frange); + let config = AssistConfig::default(); + let ctx = AssistContext::new(sema, &config, frange); let mut acc = Assists::new_resolved(&ctx); handler(&mut acc, &ctx); let mut res = acc.finish_resolved(); @@ -103,19 +104,20 @@ fn check(handler: Handler, before: &str, expected: ExpectedResult) { let mut actual = db.file_text(change.file_id).as_ref().to_owned(); change.edit.apply(&mut actual); - match source_change.cursor_position { - None => { - if let RangeOrOffset::Offset(before_cursor_pos) = range_or_offset { - let off = change - .edit - .apply_to_offset(before_cursor_pos) - .expect("cursor position is affected by the edit"); - actual = add_cursor(&actual, off) + if !source_change.is_snippet { + match source_change.cursor_position { + None => { + if let RangeOrOffset::Offset(before_cursor_pos) = range_or_offset { + let off = change + .edit + .apply_to_offset(before_cursor_pos) + .expect("cursor position is affected by the edit"); + actual = add_cursor(&actual, off) + } } - } - Some(off) => actual = add_cursor(&actual, off.offset), - }; - + Some(off) => actual = add_cursor(&actual, off.offset), + }; + } assert_eq_text!(after, &actual); } (Some(assist), ExpectedResult::Target(target)) => { @@ -136,7 +138,7 @@ fn assist_order_field_struct() { let (before_cursor_pos, before) = extract_offset(before); let (db, file_id) = with_single_file(&before); let frange = FileRange { file_id, range: TextRange::empty(before_cursor_pos) }; - let assists = Assist::resolved(&db, frange); + let assists = Assist::resolved(&db, &AssistConfig::default(), frange); let mut assists = assists.iter(); assert_eq!( @@ -159,7 +161,7 @@ fn assist_order_if_expr() { let (range, before) = extract_range(before); let (db, file_id) = with_single_file(&before); let frange = FileRange { file_id, range }; - let assists = Assist::resolved(&db, frange); + let assists = Assist::resolved(&db, &AssistConfig::default(), frange); let mut assists = assists.iter(); assert_eq!(assists.next().expect("expected assist").assist.label, "Extract into variable"); diff --git a/crates/ra_ide/src/completion.rs b/crates/ra_ide/src/completion.rs index 8bdc43b1a0..191300704b 100644 --- a/crates/ra_ide/src/completion.rs +++ b/crates/ra_ide/src/completion.rs @@ -59,8 +59,8 @@ pub use crate::completion::{ /// with ordering of completions (currently this is done by the client). pub(crate) fn completions( db: &RootDatabase, - position: FilePosition, config: &CompletionConfig, + position: FilePosition, ) -> Option { let ctx = CompletionContext::new(db, position, config)?; diff --git a/crates/ra_ide/src/completion/test_utils.rs b/crates/ra_ide/src/completion/test_utils.rs index eb90b5279c..bf22452a28 100644 --- a/crates/ra_ide/src/completion/test_utils.rs +++ b/crates/ra_ide/src/completion/test_utils.rs @@ -20,7 +20,7 @@ pub(crate) fn do_completion_with_options( } else { single_file_with_position(code) }; - let completions = analysis.completions(position, options).unwrap().unwrap(); + let completions = analysis.completions(options, position).unwrap().unwrap(); let completion_items: Vec = completions.into(); let mut kind_completions: Vec = completion_items.into_iter().filter(|c| c.completion_kind == kind).collect(); diff --git a/crates/ra_ide/src/diagnostics.rs b/crates/ra_ide/src/diagnostics.rs index 87a0b80f13..54c2bcc094 100644 --- a/crates/ra_ide/src/diagnostics.rs +++ b/crates/ra_ide/src/diagnostics.rs @@ -629,6 +629,7 @@ mod tests { }, ], cursor_position: None, + is_snippet: false, }, ), severity: Error, @@ -685,6 +686,7 @@ mod tests { ], file_system_edits: [], cursor_position: None, + is_snippet: false, }, ), severity: Error, diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs index 78149ddfcb..66125f2f59 100644 --- a/crates/ra_ide/src/lib.rs +++ b/crates/ra_ide/src/lib.rs @@ -82,7 +82,7 @@ pub use crate::{ }; pub use hir::Documentation; -pub use ra_assists::AssistId; +pub use ra_assists::{AssistConfig, AssistId}; pub use ra_db::{ Canceled, CrateGraph, CrateId, Edition, FileId, FilePosition, FileRange, SourceRootId, }; @@ -458,17 +458,17 @@ impl Analysis { /// Computes completions at the given position. pub fn completions( &self, - position: FilePosition, config: &CompletionConfig, + position: FilePosition, ) -> Cancelable>> { - self.with_db(|db| completion::completions(db, position, config).map(Into::into)) + self.with_db(|db| completion::completions(db, config, position).map(Into::into)) } /// Computes assists (aka code actions aka intentions) for the given /// position. - pub fn assists(&self, frange: FileRange) -> Cancelable> { + pub fn assists(&self, config: &AssistConfig, frange: FileRange) -> Cancelable> { self.with_db(|db| { - ra_assists::Assist::resolved(db, frange) + ra_assists::Assist::resolved(db, config, frange) .into_iter() .map(|assist| Assist { id: assist.assist.id, diff --git a/crates/ra_ide/src/references/rename.rs b/crates/ra_ide/src/references/rename.rs index 410dae75cb..68a53ad4b0 100644 --- a/crates/ra_ide/src/references/rename.rs +++ b/crates/ra_ide/src/references/rename.rs @@ -670,6 +670,7 @@ mod tests { }, ], cursor_position: None, + is_snippet: false, }, }, ) @@ -722,6 +723,7 @@ mod tests { }, ], cursor_position: None, + is_snippet: false, }, }, ) @@ -818,6 +820,7 @@ mod tests { }, ], cursor_position: None, + is_snippet: false, }, }, ) diff --git a/crates/ra_ide_db/src/source_change.rs b/crates/ra_ide_db/src/source_change.rs index af81a91a4a..c64165f3a9 100644 --- a/crates/ra_ide_db/src/source_change.rs +++ b/crates/ra_ide_db/src/source_change.rs @@ -13,6 +13,7 @@ pub struct SourceChange { pub source_file_edits: Vec, pub file_system_edits: Vec, pub cursor_position: Option, + pub is_snippet: bool, } impl SourceChange { @@ -28,6 +29,7 @@ impl SourceChange { source_file_edits, file_system_edits, cursor_position: None, + is_snippet: false, } } @@ -41,6 +43,7 @@ impl SourceChange { source_file_edits: edits, file_system_edits: vec![], cursor_position: None, + is_snippet: false, } } @@ -52,6 +55,7 @@ impl SourceChange { source_file_edits: vec![], file_system_edits: edits, cursor_position: None, + is_snippet: false, } } @@ -115,6 +119,7 @@ impl SingleFileChange { source_file_edits: vec![SourceFileEdit { file_id, edit: self.edit }], file_system_edits: Vec::new(), cursor_position: self.cursor_position.map(|offset| FilePosition { file_id, offset }), + is_snippet: false, } } } diff --git a/crates/rust-analyzer/src/cli/analysis_bench.rs b/crates/rust-analyzer/src/cli/analysis_bench.rs index 6147ae2074..b20efe98d8 100644 --- a/crates/rust-analyzer/src/cli/analysis_bench.rs +++ b/crates/rust-analyzer/src/cli/analysis_bench.rs @@ -105,7 +105,7 @@ pub fn analysis_bench( if is_completion { let options = CompletionConfig::default(); let res = do_work(&mut host, file_id, |analysis| { - analysis.completions(file_position, &options) + analysis.completions(&options, file_position) }); if verbosity.is_verbose() { println!("\n{:#?}", res); diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index b5dc6f0fa9..063b1b3161 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -11,7 +11,7 @@ use std::{ffi::OsString, path::PathBuf}; use lsp_types::ClientCapabilities; use ra_flycheck::FlycheckConfig; -use ra_ide::{CompletionConfig, InlayHintsConfig}; +use ra_ide::{AssistConfig, CompletionConfig, InlayHintsConfig}; use ra_project_model::CargoConfig; use serde::Deserialize; @@ -32,6 +32,7 @@ pub struct Config { pub inlay_hints: InlayHintsConfig, pub completion: CompletionConfig, + pub assist: AssistConfig, pub call_info_full: bool, pub lens: LensConfig, } @@ -136,6 +137,7 @@ impl Default for Config { add_call_argument_snippets: true, ..CompletionConfig::default() }, + assist: AssistConfig::default(), call_info_full: true, lens: LensConfig::default(), } @@ -281,6 +283,7 @@ impl Config { } } } + self.assist.allow_snippets(false); } if let Some(window_caps) = caps.window.as_ref() { diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs index e675567528..13ae061fa0 100644 --- a/crates/rust-analyzer/src/main_loop/handlers.rs +++ b/crates/rust-analyzer/src/main_loop/handlers.rs @@ -476,7 +476,7 @@ pub fn handle_completion( return Ok(None); } - let items = match world.analysis().completions(position, &world.config.completion)? { + let items = match world.analysis().completions(&world.config.completion, position)? { None => return Ok(None), Some(items) => items, }; @@ -740,7 +740,9 @@ pub fn handle_code_action( } let mut grouped_assists: FxHashMap)> = FxHashMap::default(); - for assist in world.analysis().assists(FileRange { file_id, range })?.into_iter() { + for assist in + world.analysis().assists(&world.config.assist, FileRange { file_id, range })?.into_iter() + { match &assist.group_label { Some(label) => grouped_assists .entry(label.to_owned()) From fa2e5299c3332b99fcd09fd54e8d812a6c34b0cc Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sun, 17 May 2020 14:21:24 +0200 Subject: [PATCH 24/70] Add snippet support for some assists --- .../src/handlers/add_custom_impl.rs | 49 ++++++++++--------- crates/ra_assists/src/handlers/add_derive.rs | 28 ++++++----- crates/ra_assists/src/handlers/add_impl.rs | 34 +++++++------ crates/ra_assists/src/tests/generated.rs | 10 ++-- docs/user/assists.md | 10 ++-- 5 files changed, 71 insertions(+), 60 deletions(-) diff --git a/crates/ra_assists/src/handlers/add_custom_impl.rs b/crates/ra_assists/src/handlers/add_custom_impl.rs index 2baeb8607f..fa70c84968 100644 --- a/crates/ra_assists/src/handlers/add_custom_impl.rs +++ b/crates/ra_assists/src/handlers/add_custom_impl.rs @@ -25,7 +25,7 @@ use crate::{ // struct S; // // impl Debug for S { -// +// $0 // } // ``` pub(crate) fn add_custom_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { @@ -52,7 +52,7 @@ pub(crate) fn add_custom_impl(acc: &mut Assists, ctx: &AssistContext) -> Option< format!("Add custom impl `{}` for `{}`", trait_token.text().as_str(), annotated_name); let target = attr.syntax().text_range(); - acc.add(AssistId("add_custom_impl"), label, target, |edit| { + acc.add(AssistId("add_custom_impl"), label, target, |builder| { let new_attr_input = input .syntax() .descendants_with_tokens() @@ -63,20 +63,11 @@ pub(crate) fn add_custom_impl(acc: &mut Assists, ctx: &AssistContext) -> Option< let has_more_derives = !new_attr_input.is_empty(); let new_attr_input = new_attr_input.iter().sep_by(", ").surround_with("(", ")").to_string(); - let mut buf = String::new(); - buf.push_str("\n\nimpl "); - buf.push_str(trait_token.text().as_str()); - buf.push_str(" for "); - buf.push_str(annotated_name.as_str()); - buf.push_str(" {\n"); - - let cursor_delta = if has_more_derives { - let delta = input.syntax().text_range().len() - TextSize::of(&new_attr_input); - edit.replace(input.syntax().text_range(), new_attr_input); - delta + if has_more_derives { + builder.replace(input.syntax().text_range(), new_attr_input); } else { let attr_range = attr.syntax().text_range(); - edit.delete(attr_range); + builder.delete(attr_range); let line_break_range = attr .syntax() @@ -84,14 +75,24 @@ pub(crate) fn add_custom_impl(acc: &mut Assists, ctx: &AssistContext) -> Option< .filter(|t| t.kind() == WHITESPACE) .map(|t| t.text_range()) .unwrap_or_else(|| TextRange::new(TextSize::from(0), TextSize::from(0))); - edit.delete(line_break_range); + builder.delete(line_break_range); + } - attr_range.len() + line_break_range.len() - }; - - edit.set_cursor(start_offset + TextSize::of(&buf) - cursor_delta); - buf.push_str("\n}"); - edit.insert(start_offset, buf); + match ctx.config.snippet_cap { + Some(cap) => { + builder.insert_snippet( + cap, + start_offset, + format!("\n\nimpl {} for {} {{\n $0\n}}", trait_token, annotated_name), + ); + } + None => { + builder.insert( + start_offset, + format!("\n\nimpl {} for {} {{\n\n}}", trait_token, annotated_name), + ); + } + } }) } @@ -117,7 +118,7 @@ struct Foo { } impl Debug for Foo { -<|> + $0 } ", ) @@ -139,7 +140,7 @@ pub struct Foo { } impl Debug for Foo { -<|> + $0 } ", ) @@ -158,7 +159,7 @@ struct Foo {} struct Foo {} impl Debug for Foo { -<|> + $0 } ", ) diff --git a/crates/ra_assists/src/handlers/add_derive.rs b/crates/ra_assists/src/handlers/add_derive.rs index fb08c19e93..b123b84988 100644 --- a/crates/ra_assists/src/handlers/add_derive.rs +++ b/crates/ra_assists/src/handlers/add_derive.rs @@ -18,31 +18,37 @@ use crate::{AssistContext, AssistId, Assists}; // ``` // -> // ``` -// #[derive()] +// #[derive($0)] // struct Point { // x: u32, // y: u32, // } // ``` pub(crate) fn add_derive(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { + let cap = ctx.config.snippet_cap?; let nominal = ctx.find_node_at_offset::()?; let node_start = derive_insertion_offset(&nominal)?; let target = nominal.syntax().text_range(); - acc.add(AssistId("add_derive"), "Add `#[derive]`", target, |edit| { + acc.add(AssistId("add_derive"), "Add `#[derive]`", target, |builder| { let derive_attr = nominal .attrs() .filter_map(|x| x.as_simple_call()) .filter(|(name, _arg)| name == "derive") .map(|(_name, arg)| arg) .next(); - let offset = match derive_attr { + match derive_attr { None => { - edit.insert(node_start, "#[derive()]\n"); - node_start + TextSize::of("#[derive(") + builder.insert_snippet(cap, node_start, "#[derive($0)]\n"); + } + Some(tt) => { + // Just move the cursor. + builder.insert_snippet( + cap, + tt.syntax().text_range().end() - TextSize::of(')'), + "$0", + ) } - Some(tt) => tt.syntax().text_range().end() - TextSize::of(')'), }; - edit.set_cursor(offset) }) } @@ -66,12 +72,12 @@ mod tests { check_assist( add_derive, "struct Foo { a: i32, <|>}", - "#[derive(<|>)]\nstruct Foo { a: i32, }", + "#[derive($0)]\nstruct Foo { a: i32, }", ); check_assist( add_derive, "struct Foo { <|> a: i32, }", - "#[derive(<|>)]\nstruct Foo { a: i32, }", + "#[derive($0)]\nstruct Foo { a: i32, }", ); } @@ -80,7 +86,7 @@ mod tests { check_assist( add_derive, "#[derive(Clone)]\nstruct Foo { a: i32<|>, }", - "#[derive(Clone<|>)]\nstruct Foo { a: i32, }", + "#[derive(Clone$0)]\nstruct Foo { a: i32, }", ); } @@ -96,7 +102,7 @@ struct Foo { a: i32<|>, } " /// `Foo` is a pretty important struct. /// It does stuff. -#[derive(<|>)] +#[derive($0)] struct Foo { a: i32, } ", ); diff --git a/crates/ra_assists/src/handlers/add_impl.rs b/crates/ra_assists/src/handlers/add_impl.rs index df114a0d84..eceba7d0ae 100644 --- a/crates/ra_assists/src/handlers/add_impl.rs +++ b/crates/ra_assists/src/handlers/add_impl.rs @@ -1,7 +1,4 @@ -use ra_syntax::{ - ast::{self, AstNode, NameOwner, TypeParamsOwner}, - TextSize, -}; +use ra_syntax::ast::{self, AstNode, NameOwner, TypeParamsOwner}; use stdx::{format_to, SepBy}; use crate::{AssistContext, AssistId, Assists}; @@ -12,17 +9,17 @@ use crate::{AssistContext, AssistId, Assists}; // // ``` // struct Ctx { -// data: T,<|> +// data: T,<|> // } // ``` // -> // ``` // struct Ctx { -// data: T, +// data: T, // } // // impl Ctx { -// +// $0 // } // ``` pub(crate) fn add_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { @@ -50,30 +47,37 @@ pub(crate) fn add_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { let generic_params = lifetime_params.chain(type_params).sep_by(", "); format_to!(buf, "<{}>", generic_params) } - buf.push_str(" {\n"); - edit.set_cursor(start_offset + TextSize::of(&buf)); - buf.push_str("\n}"); - edit.insert(start_offset, buf); + match ctx.config.snippet_cap { + Some(cap) => { + buf.push_str(" {\n $0\n}"); + edit.insert_snippet(cap, start_offset, buf); + } + None => { + buf.push_str(" {\n}"); + edit.insert(start_offset, buf); + } + } }) } #[cfg(test)] mod tests { - use super::*; use crate::tests::{check_assist, check_assist_target}; + use super::*; + #[test] fn test_add_impl() { - check_assist(add_impl, "struct Foo {<|>}\n", "struct Foo {}\n\nimpl Foo {\n<|>\n}\n"); + check_assist(add_impl, "struct Foo {<|>}\n", "struct Foo {}\n\nimpl Foo {\n $0\n}\n"); check_assist( add_impl, "struct Foo {<|>}", - "struct Foo {}\n\nimpl Foo {\n<|>\n}", + "struct Foo {}\n\nimpl Foo {\n $0\n}", ); check_assist( add_impl, "struct Foo<'a, T: Foo<'a>> {<|>}", - "struct Foo<'a, T: Foo<'a>> {}\n\nimpl<'a, T: Foo<'a>> Foo<'a, T> {\n<|>\n}", + "struct Foo<'a, T: Foo<'a>> {}\n\nimpl<'a, T: Foo<'a>> Foo<'a, T> {\n $0\n}", ); } diff --git a/crates/ra_assists/src/tests/generated.rs b/crates/ra_assists/src/tests/generated.rs index 972dbd251d..9487c9239f 100644 --- a/crates/ra_assists/src/tests/generated.rs +++ b/crates/ra_assists/src/tests/generated.rs @@ -15,7 +15,7 @@ struct S; struct S; impl Debug for S { - + $0 } "#####, ) @@ -32,7 +32,7 @@ struct Point { } "#####, r#####" -#[derive()] +#[derive($0)] struct Point { x: u32, y: u32, @@ -108,16 +108,16 @@ fn doctest_add_impl() { "add_impl", r#####" struct Ctx { - data: T,<|> + data: T,<|> } "#####, r#####" struct Ctx { - data: T, + data: T, } impl Ctx { - + $0 } "#####, ) diff --git a/docs/user/assists.md b/docs/user/assists.md index 692fd4f52b..41c5df5287 100644 --- a/docs/user/assists.md +++ b/docs/user/assists.md @@ -17,7 +17,7 @@ struct S; struct S; impl Debug for S { - + $0 } ``` @@ -33,7 +33,7 @@ struct Point { } // AFTER -#[derive()] +#[derive($0)] struct Point { x: u32, y: u32, @@ -105,16 +105,16 @@ Adds a new inherent impl for a type. ```rust // BEFORE struct Ctx { - data: T,┃ + data: T,┃ } // AFTER struct Ctx { - data: T, + data: T, } impl Ctx { - + $0 } ``` From a752853350639a915178ea900a51f3c45443795e Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sun, 17 May 2020 21:24:33 +0200 Subject: [PATCH 25/70] Add snippetTextEdit protocol extension --- crates/rust-analyzer/src/config.rs | 9 +++++++- docs/dev/lsp-extensions.md | 34 ++++++++++++++++++++++++++++++ editors/code/src/client.ts | 19 +++++++++++++---- 3 files changed, 57 insertions(+), 5 deletions(-) create mode 100644 docs/dev/lsp-extensions.md diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 063b1b3161..d75c48597b 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -275,6 +275,7 @@ impl Config { { self.client_caps.code_action_literals = value; } + self.completion.allow_snippets(false); if let Some(completion) = &doc_caps.completion { if let Some(completion_item) = &completion.completion_item { @@ -283,7 +284,6 @@ impl Config { } } } - self.assist.allow_snippets(false); } if let Some(window_caps) = caps.window.as_ref() { @@ -291,5 +291,12 @@ impl Config { self.client_caps.work_done_progress = value; } } + + self.assist.allow_snippets(false); + if let Some(experimental) = &caps.experimental { + let enable = + experimental.get("snippetTextEdit").and_then(|it| it.as_bool()) == Some(true); + self.assist.allow_snippets(enable); + } } } diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md new file mode 100644 index 0000000000..d2ec6c0215 --- /dev/null +++ b/docs/dev/lsp-extensions.md @@ -0,0 +1,34 @@ +# LSP Extensions + +This document describes LSP extensions used by rust-analyzer. +It's a best effort document, when in doubt, consult the source (and send a PR with clarification ;-) ). +We aim to upstream all non Rust-specific extensions to the protocol, but this is not a top priority. +All capabilities are enabled via `experimental` field of `ClientCapabilities`. + +## `SnippetTextEdit` + +**Capability** + +```typescript +{ + "snippetTextEdit": boolean +} +``` + +If this capability is set, `WorkspaceEdit`s returned from `codeAction` requests might contain `SnippetTextEdit`s instead of usual `TextEdit`s: + +```typescript +interface SnippetTextEdit extends TextEdit { + insertTextFormat?: InsertTextFormat; +} +``` + +```typescript +export interface TextDocumentEdit { + textDocument: VersionedTextDocumentIdentifier; + edits: (TextEdit | SnippetTextEdit)[]; +} +``` + +When applying such code action, the editor should insert snippet, with tab stops and placeholder. +At the moment, rust-analyzer guarantees that only a single edit will have `InsertTextFormat.Snippet`. diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts index cffdcf11ac..2067738ea6 100644 --- a/editors/code/src/client.ts +++ b/editors/code/src/client.ts @@ -35,7 +35,7 @@ export function createClient(serverPath: string, cwd: string): lc.LanguageClient } as any }; - const res = new lc.LanguageClient( + const client = new lc.LanguageClient( 'rust-analyzer', 'Rust Analyzer Language Server', serverOptions, @@ -47,8 +47,19 @@ export function createClient(serverPath: string, cwd: string): lc.LanguageClient // since they are available on stable. // Note that while these features are stable in vscode their LSP protocol // implementations are still in the "proposed" category for 3.16. - res.registerFeature(new CallHierarchyFeature(res)); - res.registerFeature(new SemanticTokensFeature(res)); + client.registerFeature(new CallHierarchyFeature(client)); + client.registerFeature(new SemanticTokensFeature(client)); + client.registerFeature(new SnippetTextEditFeature()); - return res; + return client; +} + +class SnippetTextEditFeature implements lc.StaticFeature { + fillClientCapabilities(capabilities: lc.ClientCapabilities): void { + const caps: any = capabilities.experimental ?? {}; + caps.snippetTextEdit = true; + capabilities.experimental = caps + } + initialize(_capabilities: lc.ServerCapabilities, _documentSelector: lc.DocumentSelector | undefined): void { + } } From 2bf6b16a7f174ea3f581f26f642b4febff0b9ce8 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Mon, 18 May 2020 00:11:40 +0200 Subject: [PATCH 26/70] Server side of SnippetTextEdit --- crates/ra_assists/src/assist_context.rs | 10 -- crates/rust-analyzer/src/diagnostics.rs | 10 +- ..._to_proto__tests__snap_multi_line_fix.snap | 6 +- ...to__tests__snap_rustc_unused_variable.snap | 6 +- .../rust-analyzer/src/diagnostics/to_proto.rs | 20 +-- crates/rust-analyzer/src/lsp_ext.rs | 53 ++++++- crates/rust-analyzer/src/main_loop.rs | 2 +- .../rust-analyzer/src/main_loop/handlers.rs | 82 ++++------- crates/rust-analyzer/src/to_proto.rs | 130 ++++++++++++++---- 9 files changed, 200 insertions(+), 119 deletions(-) diff --git a/crates/ra_assists/src/assist_context.rs b/crates/ra_assists/src/assist_context.rs index b90bbf8b2e..0dcd9df61f 100644 --- a/crates/ra_assists/src/assist_context.rs +++ b/crates/ra_assists/src/assist_context.rs @@ -208,16 +208,6 @@ impl AssistBuilder { pub(crate) fn replace(&mut self, range: TextRange, replace_with: impl Into) { self.edit.replace(range, replace_with.into()) } - /// Append specified `text` at the given `offset` - pub(crate) fn replace_snippet( - &mut self, - _cap: SnippetCap, - range: TextRange, - replace_with: impl Into, - ) { - self.is_snippet = true; - self.edit.replace(range, replace_with.into()) - } pub(crate) fn replace_ast(&mut self, old: N, new: N) { algo::diff(old.syntax(), new.syntax()).into_text_edit(&mut self.edit) } diff --git a/crates/rust-analyzer/src/diagnostics.rs b/crates/rust-analyzer/src/diagnostics.rs index 4bdd45a7de..25856c5436 100644 --- a/crates/rust-analyzer/src/diagnostics.rs +++ b/crates/rust-analyzer/src/diagnostics.rs @@ -3,9 +3,11 @@ pub(crate) mod to_proto; use std::{collections::HashMap, sync::Arc}; -use lsp_types::{CodeActionOrCommand, Diagnostic, Range}; +use lsp_types::{Diagnostic, Range}; use ra_ide::FileId; +use crate::lsp_ext; + pub type CheckFixes = Arc>>; #[derive(Debug, Default, Clone)] @@ -18,13 +20,13 @@ pub struct DiagnosticCollection { #[derive(Debug, Clone)] pub struct Fix { pub range: Range, - pub action: CodeActionOrCommand, + pub action: lsp_ext::CodeAction, } #[derive(Debug)] pub enum DiagnosticTask { ClearCheck, - AddCheck(FileId, Diagnostic, Vec), + AddCheck(FileId, Diagnostic, Vec), SetNative(FileId, Vec), } @@ -38,7 +40,7 @@ impl DiagnosticCollection { &mut self, file_id: FileId, diagnostic: Diagnostic, - fixes: Vec, + fixes: Vec, ) { let diagnostics = self.check.entry(file_id).or_default(); for existing_diagnostic in diagnostics.iter() { diff --git a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_multi_line_fix.snap b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_multi_line_fix.snap index 076b3cf273..96466b5c90 100644 --- a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_multi_line_fix.snap +++ b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_multi_line_fix.snap @@ -68,9 +68,9 @@ expression: diag kind: Some( "quickfix", ), - diagnostics: None, + command: None, edit: Some( - WorkspaceEdit { + SnippetWorkspaceEdit { changes: Some( { "file:///test/src/main.rs": [ @@ -106,8 +106,6 @@ expression: diag document_changes: None, }, ), - command: None, - is_preferred: None, }, ], }, diff --git a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable.snap b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable.snap index 69138c15b2..8f962277f0 100644 --- a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable.snap +++ b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable.snap @@ -53,9 +53,9 @@ expression: diag kind: Some( "quickfix", ), - diagnostics: None, + command: None, edit: Some( - WorkspaceEdit { + SnippetWorkspaceEdit { changes: Some( { "file:///test/driver/subcommand/repl.rs": [ @@ -78,8 +78,6 @@ expression: diag document_changes: None, }, ), - command: None, - is_preferred: None, }, ], }, diff --git a/crates/rust-analyzer/src/diagnostics/to_proto.rs b/crates/rust-analyzer/src/diagnostics/to_proto.rs index eabf4908ff..afea595254 100644 --- a/crates/rust-analyzer/src/diagnostics/to_proto.rs +++ b/crates/rust-analyzer/src/diagnostics/to_proto.rs @@ -7,13 +7,13 @@ use std::{ }; use lsp_types::{ - CodeAction, Diagnostic, DiagnosticRelatedInformation, DiagnosticSeverity, DiagnosticTag, - Location, NumberOrString, Position, Range, TextEdit, Url, WorkspaceEdit, + Diagnostic, DiagnosticRelatedInformation, DiagnosticSeverity, DiagnosticTag, Location, + NumberOrString, Position, Range, TextEdit, Url, }; use ra_flycheck::{Applicability, DiagnosticLevel, DiagnosticSpan, DiagnosticSpanMacroExpansion}; use stdx::format_to; -use crate::Result; +use crate::{lsp_ext, Result}; /// Converts a Rust level string to a LSP severity fn map_level_to_severity(val: DiagnosticLevel) -> Option { @@ -110,7 +110,7 @@ fn is_deprecated(rd: &ra_flycheck::Diagnostic) -> bool { enum MappedRustChildDiagnostic { Related(DiagnosticRelatedInformation), - SuggestedFix(CodeAction), + SuggestedFix(lsp_ext::CodeAction), MessageLine(String), } @@ -143,13 +143,15 @@ fn map_rust_child_diagnostic( message: rd.message.clone(), }) } else { - MappedRustChildDiagnostic::SuggestedFix(CodeAction { + MappedRustChildDiagnostic::SuggestedFix(lsp_ext::CodeAction { title: rd.message.clone(), kind: Some("quickfix".to_string()), - diagnostics: None, - edit: Some(WorkspaceEdit::new(edit_map)), + edit: Some(lsp_ext::SnippetWorkspaceEdit { + // FIXME: there's no good reason to use edit_map here.... + changes: Some(edit_map), + document_changes: None, + }), command: None, - is_preferred: None, }) } } @@ -158,7 +160,7 @@ fn map_rust_child_diagnostic( pub(crate) struct MappedRustDiagnostic { pub location: Location, pub diagnostic: Diagnostic, - pub fixes: Vec, + pub fixes: Vec, } /// Converts a Rust root diagnostic to LSP form diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs index 313a8c7697..c7a3a69114 100644 --- a/crates/rust-analyzer/src/lsp_ext.rs +++ b/crates/rust-analyzer/src/lsp_ext.rs @@ -1,6 +1,6 @@ //! rust-analyzer extensions to the LSP. -use std::path::PathBuf; +use std::{collections::HashMap, path::PathBuf}; use lsp_types::request::Request; use lsp_types::{Location, Position, Range, TextDocumentIdentifier}; @@ -137,7 +137,7 @@ pub struct Runnable { #[serde(rename_all = "camelCase")] pub struct SourceChange { pub label: String, - pub workspace_edit: lsp_types::WorkspaceEdit, + pub workspace_edit: SnippetWorkspaceEdit, pub cursor_position: Option, } @@ -183,3 +183,52 @@ pub struct SsrParams { pub query: String, pub parse_only: bool, } + +pub enum CodeActionRequest {} + +impl Request for CodeActionRequest { + type Params = lsp_types::CodeActionParams; + type Result = Option>; + const METHOD: &'static str = "textDocument/codeAction"; +} + +#[derive(Debug, PartialEq, Clone, Default, Deserialize, Serialize)] +pub struct CodeAction { + pub title: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub kind: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub command: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub edit: Option, +} + +#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct SnippetWorkspaceEdit { + pub changes: Option>>, + pub document_changes: Option>, +} + +#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)] +#[serde(untagged, rename_all = "lowercase")] +pub enum SnippetDocumentChangeOperation { + Op(lsp_types::ResourceOp), + Edit(SnippetTextDocumentEdit), +} + +#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct SnippetTextDocumentEdit { + pub text_document: lsp_types::VersionedTextDocumentIdentifier, + pub edits: Vec, +} + +#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct SnippetTextEdit { + pub range: Range, + pub new_text: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub insert_text_format: Option, +} diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index 15e5bb3549..87795fffbd 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -518,6 +518,7 @@ fn on_request( .on::(handlers::handle_parent_module)? .on::(handlers::handle_runnables)? .on::(handlers::handle_inlay_hints)? + .on::(handlers::handle_code_action)? .on::(handlers::handle_on_type_formatting)? .on::(handlers::handle_document_symbol)? .on::(handlers::handle_workspace_symbol)? @@ -525,7 +526,6 @@ fn on_request( .on::(handlers::handle_goto_implementation)? .on::(handlers::handle_goto_type_definition)? .on::(handlers::handle_completion)? - .on::(handlers::handle_code_action)? .on::(handlers::handle_code_lens)? .on::(handlers::handle_code_lens_resolve)? .on::(handlers::handle_folding_range)? diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs index 13ae061fa0..4ff8fa69e9 100644 --- a/crates/rust-analyzer/src/main_loop/handlers.rs +++ b/crates/rust-analyzer/src/main_loop/handlers.rs @@ -11,12 +11,11 @@ use lsp_server::ErrorCode; use lsp_types::{ CallHierarchyIncomingCall, CallHierarchyIncomingCallsParams, CallHierarchyItem, CallHierarchyOutgoingCall, CallHierarchyOutgoingCallsParams, CallHierarchyPrepareParams, - CodeAction, CodeActionResponse, CodeLens, Command, CompletionItem, Diagnostic, - DocumentFormattingParams, DocumentHighlight, DocumentSymbol, FoldingRange, FoldingRangeParams, - Hover, HoverContents, Location, MarkupContent, MarkupKind, Position, PrepareRenameResponse, - Range, RenameParams, SemanticTokensParams, SemanticTokensRangeParams, - SemanticTokensRangeResult, SemanticTokensResult, SymbolInformation, TextDocumentIdentifier, - TextEdit, Url, WorkspaceEdit, + CodeLens, Command, CompletionItem, Diagnostic, DocumentFormattingParams, DocumentHighlight, + DocumentSymbol, FoldingRange, FoldingRangeParams, Hover, HoverContents, Location, + MarkupContent, MarkupKind, Position, PrepareRenameResponse, Range, RenameParams, + SemanticTokensParams, SemanticTokensRangeParams, SemanticTokensRangeResult, + SemanticTokensResult, SymbolInformation, TextDocumentIdentifier, TextEdit, Url, WorkspaceEdit, }; use ra_ide::{ Assist, FileId, FilePosition, FileRange, Query, RangeInfo, Runnable, RunnableKind, SearchScope, @@ -585,9 +584,8 @@ pub fn handle_rename(world: WorldSnapshot, params: RenameParams) -> Result return Ok(None), Some(it) => it.info, }; - - let source_change = to_proto::source_change(&world, source_change)?; - Ok(Some(source_change.workspace_edit)) + let workspace_edit = to_proto::workspace_edit(&world, source_change)?; + Ok(Some(workspace_edit)) } pub fn handle_references( @@ -696,14 +694,21 @@ pub fn handle_formatting( pub fn handle_code_action( world: WorldSnapshot, params: lsp_types::CodeActionParams, -) -> Result> { +) -> Result>> { let _p = profile("handle_code_action"); + // We intentionally don't support command-based actions, as those either + // requires custom client-code anyway, or requires server-initiated edits. + // Server initiated edits break causality, so we avoid those as well. + if !world.config.client_caps.code_action_literals { + return Ok(None); + } + let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; let line_index = world.analysis().file_line_index(file_id)?; let range = from_proto::text_range(&line_index, params.range); let diagnostics = world.analysis().diagnostics(file_id)?; - let mut res = CodeActionResponse::default(); + let mut res: Vec = Vec::new(); let fixes_from_diagnostics = diagnostics .into_iter() @@ -713,22 +718,9 @@ pub fn handle_code_action( for source_edit in fixes_from_diagnostics { let title = source_edit.label.clone(); - let edit = to_proto::source_change(&world, source_edit)?; - - let command = Command { - title, - command: "rust-analyzer.applySourceChange".to_string(), - arguments: Some(vec![to_value(edit).unwrap()]), - }; - let action = CodeAction { - title: command.title.clone(), - kind: None, - diagnostics: None, - edit: None, - command: Some(command), - is_preferred: None, - }; - res.push(action.into()); + let edit = to_proto::snippet_workspace_edit(&world, source_edit)?; + let action = lsp_ext::CodeAction { title, kind: None, edit: Some(edit), command: None }; + res.push(action); } for fix in world.check_fixes.get(&file_id).into_iter().flatten() { @@ -748,8 +740,13 @@ pub fn handle_code_action( .entry(label.to_owned()) .or_insert_with(|| { let idx = res.len(); - let dummy = Command::new(String::new(), String::new(), None); - res.push(dummy.into()); + let dummy = lsp_ext::CodeAction { + title: String::new(), + kind: None, + command: None, + edit: None, + }; + res.push(dummy); (idx, Vec::new()) }) .1 @@ -777,35 +774,10 @@ pub fn handle_code_action( command: "rust-analyzer.selectAndApplySourceChange".to_string(), arguments: Some(vec![serde_json::Value::Array(arguments)]), }); - res[idx] = CodeAction { - title, - kind: None, - diagnostics: None, - edit: None, - command, - is_preferred: None, - } - .into(); + res[idx] = lsp_ext::CodeAction { title, kind: None, edit: None, command }; } } - // If the client only supports commands then filter the list - // and remove and actions that depend on edits. - if !world.config.client_caps.code_action_literals { - // FIXME: use drain_filter once it hits stable. - res = res - .into_iter() - .filter_map(|it| match it { - cmd @ lsp_types::CodeActionOrCommand::Command(_) => Some(cmd), - lsp_types::CodeActionOrCommand::CodeAction(action) => match action.command { - Some(cmd) if action.edit.is_none() => { - Some(lsp_types::CodeActionOrCommand::Command(cmd)) - } - _ => None, - }, - }) - .collect(); - } Ok(Some(res)) } diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index a8e2e535f9..2b1a3378f8 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs @@ -112,6 +112,22 @@ pub(crate) fn text_edit( lsp_types::TextEdit { range, new_text } } +pub(crate) fn snippet_text_edit( + line_index: &LineIndex, + line_endings: LineEndings, + is_snippet: bool, + indel: Indel, +) -> lsp_ext::SnippetTextEdit { + let text_edit = text_edit(line_index, line_endings, indel); + let insert_text_format = + if is_snippet { Some(lsp_types::InsertTextFormat::Snippet) } else { None }; + lsp_ext::SnippetTextEdit { + range: text_edit.range, + new_text: text_edit.new_text, + insert_text_format, + } +} + pub(crate) fn text_edit_vec( line_index: &LineIndex, line_endings: LineEndings, @@ -441,10 +457,11 @@ pub(crate) fn goto_definition_response( } } -pub(crate) fn text_document_edit( +pub(crate) fn snippet_text_document_edit( world: &WorldSnapshot, + is_snippet: bool, source_file_edit: SourceFileEdit, -) -> Result { +) -> Result { let text_document = versioned_text_document_identifier(world, source_file_edit.file_id, None)?; let line_index = world.analysis().file_line_index(source_file_edit.file_id)?; let line_endings = world.file_line_endings(source_file_edit.file_id); @@ -452,9 +469,9 @@ pub(crate) fn text_document_edit( .edit .as_indels() .iter() - .map(|it| text_edit(&line_index, line_endings, it.clone())) + .map(|it| snippet_text_edit(&line_index, line_endings, is_snippet, it.clone())) .collect(); - Ok(lsp_types::TextDocumentEdit { text_document, edits }) + Ok(lsp_ext::SnippetTextDocumentEdit { text_document, edits }) } pub(crate) fn resource_op( @@ -500,20 +517,70 @@ pub(crate) fn source_change( }) } }; - let mut document_changes: Vec = Vec::new(); + let label = source_change.label.clone(); + let workspace_edit = self::snippet_workspace_edit(world, source_change)?; + Ok(lsp_ext::SourceChange { label, workspace_edit, cursor_position }) +} + +pub(crate) fn snippet_workspace_edit( + world: &WorldSnapshot, + source_change: SourceChange, +) -> Result { + let mut document_changes: Vec = Vec::new(); for op in source_change.file_system_edits { let op = resource_op(&world, op)?; - document_changes.push(lsp_types::DocumentChangeOperation::Op(op)); + document_changes.push(lsp_ext::SnippetDocumentChangeOperation::Op(op)); } for edit in source_change.source_file_edits { - let edit = text_document_edit(&world, edit)?; - document_changes.push(lsp_types::DocumentChangeOperation::Edit(edit)); + let edit = snippet_text_document_edit(&world, source_change.is_snippet, edit)?; + document_changes.push(lsp_ext::SnippetDocumentChangeOperation::Edit(edit)); + } + let workspace_edit = + lsp_ext::SnippetWorkspaceEdit { changes: None, document_changes: Some(document_changes) }; + Ok(workspace_edit) +} + +pub(crate) fn workspace_edit( + world: &WorldSnapshot, + source_change: SourceChange, +) -> Result { + assert!(!source_change.is_snippet); + snippet_workspace_edit(world, source_change).map(|it| it.into()) +} + +impl From for lsp_types::WorkspaceEdit { + fn from(snippet_workspace_edit: lsp_ext::SnippetWorkspaceEdit) -> lsp_types::WorkspaceEdit { + lsp_types::WorkspaceEdit { + changes: None, + document_changes: snippet_workspace_edit.document_changes.map(|changes| { + lsp_types::DocumentChanges::Operations( + changes + .into_iter() + .map(|change| match change { + lsp_ext::SnippetDocumentChangeOperation::Op(op) => { + lsp_types::DocumentChangeOperation::Op(op) + } + lsp_ext::SnippetDocumentChangeOperation::Edit(edit) => { + lsp_types::DocumentChangeOperation::Edit( + lsp_types::TextDocumentEdit { + text_document: edit.text_document, + edits: edit + .edits + .into_iter() + .map(|edit| lsp_types::TextEdit { + range: edit.range, + new_text: edit.new_text, + }) + .collect(), + }, + ) + } + }) + .collect(), + ) + }), + } } - let workspace_edit = lsp_types::WorkspaceEdit { - changes: None, - document_changes: Some(lsp_types::DocumentChanges::Operations(document_changes)), - }; - Ok(lsp_ext::SourceChange { label: source_change.label, workspace_edit, cursor_position }) } pub fn call_hierarchy_item( @@ -571,22 +638,25 @@ fn main() { } } -pub(crate) fn code_action(world: &WorldSnapshot, assist: Assist) -> Result { - let source_change = source_change(&world, assist.source_change)?; - let arg = serde_json::to_value(source_change)?; - let title = assist.label; - let command = lsp_types::Command { - title: title.clone(), - command: "rust-analyzer.applySourceChange".to_string(), - arguments: Some(vec![arg]), - }; +pub(crate) fn code_action(world: &WorldSnapshot, assist: Assist) -> Result { + let res = if assist.source_change.is_snippet { + lsp_ext::CodeAction { + title: assist.label, + kind: Some(String::new()), + edit: Some(snippet_workspace_edit(world, assist.source_change)?), + command: None, + } + } else { + let source_change = source_change(&world, assist.source_change)?; + let arg = serde_json::to_value(source_change)?; + let title = assist.label; + let command = lsp_types::Command { + title: title.clone(), + command: "rust-analyzer.applySourceChange".to_string(), + arguments: Some(vec![arg]), + }; - Ok(lsp_types::CodeAction { - title, - kind: Some(String::new()), - diagnostics: None, - edit: None, - command: Some(command), - is_preferred: None, - }) + lsp_ext::CodeAction { title, kind: Some(String::new()), edit: None, command: Some(command) } + }; + Ok(res) } From 3dd68c1ba3e72a0959bcdaa46e730a7ae4d9ed4c Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Mon, 18 May 2020 01:53:55 +0200 Subject: [PATCH 27/70] Implement client-side of SnippetTextEdit --- editors/code/src/client.ts | 48 ++++++++++++++++++++++++++++-- editors/code/src/commands/index.ts | 34 +++++++++++++++++++++ editors/code/src/main.ts | 1 + 3 files changed, 81 insertions(+), 2 deletions(-) diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts index 2067738ea6..fac1a0be31 100644 --- a/editors/code/src/client.ts +++ b/editors/code/src/client.ts @@ -31,7 +31,39 @@ export function createClient(serverPath: string, cwd: string): lc.LanguageClient const res = await next(document, token); if (res === undefined) throw new Error('busy'); return res; + }, + async provideCodeActions(document: vscode.TextDocument, range: vscode.Range, context: vscode.CodeActionContext, token: vscode.CancellationToken, _next: lc.ProvideCodeActionsSignature) { + const params: lc.CodeActionParams = { + textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document), + range: client.code2ProtocolConverter.asRange(range), + context: client.code2ProtocolConverter.asCodeActionContext(context) + }; + return client.sendRequest(lc.CodeActionRequest.type, params, token).then((values) => { + if (values === null) return undefined; + const result: (vscode.CodeAction | vscode.Command)[] = []; + for (const item of values) { + if (lc.CodeAction.is(item)) { + const action = client.protocol2CodeConverter.asCodeAction(item); + if (isSnippetEdit(item)) { + action.command = { + command: "rust-analyzer.applySnippetWorkspaceEdit", + title: "", + arguments: [action.edit], + }; + action.edit = undefined; + } + result.push(action); + } else { + const command = client.protocol2CodeConverter.asCommand(item); + result.push(command); + } + } + return result; + }, + (_error) => undefined + ); } + } as any }; @@ -42,7 +74,7 @@ export function createClient(serverPath: string, cwd: string): lc.LanguageClient clientOptions, ); - // To turn on all proposed features use: res.registerProposedFeatures(); + // To turn on all proposed features use: client.registerProposedFeatures(); // Here we want to enable CallHierarchyFeature and SemanticTokensFeature // since they are available on stable. // Note that while these features are stable in vscode their LSP protocol @@ -58,8 +90,20 @@ class SnippetTextEditFeature implements lc.StaticFeature { fillClientCapabilities(capabilities: lc.ClientCapabilities): void { const caps: any = capabilities.experimental ?? {}; caps.snippetTextEdit = true; - capabilities.experimental = caps + capabilities.experimental = caps; } initialize(_capabilities: lc.ServerCapabilities, _documentSelector: lc.DocumentSelector | undefined): void { } } + +function isSnippetEdit(action: lc.CodeAction): boolean { + const documentChanges = action.edit?.documentChanges ?? []; + for (const edit of documentChanges) { + if (lc.TextDocumentEdit.is(edit)) { + if (edit.edits.some((indel) => (indel as any).insertTextFormat === lc.InsertTextFormat.Snippet)) { + return true; + } + } + } + return false; +} diff --git a/editors/code/src/commands/index.ts b/editors/code/src/commands/index.ts index bdb7fc3b03..770d11bd36 100644 --- a/editors/code/src/commands/index.ts +++ b/editors/code/src/commands/index.ts @@ -4,6 +4,7 @@ import * as ra from '../rust-analyzer-api'; import { Ctx, Cmd } from '../ctx'; import * as sourceChange from '../source_change'; +import { assert } from '../util'; export * from './analyzer_status'; export * from './matching_brace'; @@ -51,3 +52,36 @@ export function selectAndApplySourceChange(ctx: Ctx): Cmd { } }; } + +export function applySnippetWorkspaceEdit(_ctx: Ctx): Cmd { + return async (edit: vscode.WorkspaceEdit) => { + assert(edit.entries().length === 1, `bad ws edit: ${JSON.stringify(edit)}`); + const [uri, edits] = edit.entries()[0]; + + const editor = vscode.window.visibleTextEditors.find((it) => it.document.uri.toString() === uri.toString()); + if (!editor) return; + + let editWithSnippet: vscode.TextEdit | undefined = undefined; + let lineDelta = 0; + await editor.edit((builder) => { + for (const indel of edits) { + if (indel.newText.indexOf('$0') !== -1) { + editWithSnippet = indel; + } else { + if (!editWithSnippet) { + lineDelta = (indel.newText.match(/\n/g) || []).length - (indel.range.end.line - indel.range.start.line); + } + builder.replace(indel.range, indel.newText); + } + } + }); + if (editWithSnippet) { + const snip = editWithSnippet as vscode.TextEdit; + const range = snip.range.with( + snip.range.start.with(snip.range.start.line + lineDelta), + snip.range.end.with(snip.range.end.line + lineDelta), + ); + await editor.insertSnippet(new vscode.SnippetString(snip.newText), range); + } + }; +} diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts index c015460b88..ac3bb365e2 100644 --- a/editors/code/src/main.ts +++ b/editors/code/src/main.ts @@ -91,6 +91,7 @@ export async function activate(context: vscode.ExtensionContext) { ctx.registerCommand('debugSingle', commands.debugSingle); ctx.registerCommand('showReferences', commands.showReferences); ctx.registerCommand('applySourceChange', commands.applySourceChange); + ctx.registerCommand('applySnippetWorkspaceEdit', commands.applySnippetWorkspaceEdit); ctx.registerCommand('selectAndApplySourceChange', commands.selectAndApplySourceChange); ctx.pushCleanup(activateTaskProvider(workspaceFolder)); From 3e9bf7ebabdaa8e9a2972af2dd8e8089a3a0341e Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 19 May 2020 20:27:14 +0200 Subject: [PATCH 28/70] Update test data --- crates/rust-analyzer/src/lsp_ext.rs | 2 + .../rust-analyzer/tests/heavy_tests/main.rs | 52 +++++-------------- 2 files changed, 16 insertions(+), 38 deletions(-) diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs index c7a3a69114..f75a26eb79 100644 --- a/crates/rust-analyzer/src/lsp_ext.rs +++ b/crates/rust-analyzer/src/lsp_ext.rs @@ -206,7 +206,9 @@ pub struct CodeAction { #[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct SnippetWorkspaceEdit { + #[serde(skip_serializing_if = "Option::is_none")] pub changes: Option>>, + #[serde(skip_serializing_if = "Option::is_none")] pub document_changes: Option>, } diff --git a/crates/rust-analyzer/tests/heavy_tests/main.rs b/crates/rust-analyzer/tests/heavy_tests/main.rs index 5011cc2734..74676b3eed 100644 --- a/crates/rust-analyzer/tests/heavy_tests/main.rs +++ b/crates/rust-analyzer/tests/heavy_tests/main.rs @@ -333,29 +333,17 @@ fn main() {} partial_result_params: PartialResultParams::default(), work_done_progress_params: WorkDoneProgressParams::default(), }, - json!([ - { - "command": { - "arguments": [ + json!([{ + "edit": { + "documentChanges": [ { - "cursorPosition": null, - "label": "Create module", - "workspaceEdit": { - "documentChanges": [ - { - "kind": "create", - "uri": "file:///[..]/src/bar.rs" - } - ] - } + "kind": "create", + "uri": "file:///[..]/src/bar.rs" } - ], - "command": "rust-analyzer.applySourceChange", - "title": "Create module" + ] }, "title": "Create module" - } - ]), + }]), ); server.request::( @@ -416,29 +404,17 @@ fn main() {{}} partial_result_params: PartialResultParams::default(), work_done_progress_params: WorkDoneProgressParams::default(), }, - json!([ - { - "command": { - "arguments": [ + json!([{ + "edit": { + "documentChanges": [ { - "cursorPosition": null, - "label": "Create module", - "workspaceEdit": { - "documentChanges": [ - { - "kind": "create", - "uri": "file:///[..]/src/bar.rs" - } - ] - } + "kind": "create", + "uri": "file://[..]/src/bar.rs" } - ], - "command": "rust-analyzer.applySourceChange", - "title": "Create module" + ] }, "title": "Create module" - } - ]), + }]), ); server.request::( From 6eaa669da0c7b3730a309db5e320126653b88997 Mon Sep 17 00:00:00 2001 From: Roland Ruckerbauer Date: Tue, 19 May 2020 21:03:59 +0200 Subject: [PATCH 29/70] loop return value inference: coerce_merge branches --- crates/ra_hir_ty/src/infer/expr.rs | 38 ++++++++++++++++++------------ 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/crates/ra_hir_ty/src/infer/expr.rs b/crates/ra_hir_ty/src/infer/expr.rs index c7aa67fbe1..83702ada04 100644 --- a/crates/ra_hir_ty/src/infer/expr.rs +++ b/crates/ra_hir_ty/src/infer/expr.rs @@ -93,14 +93,17 @@ impl<'a> InferenceContext<'a> { Ty::Unknown } Expr::Loop { body } => { - self.breakables.push(BreakableContext { may_break: false, break_ty: Ty::Unknown }); + self.breakables.push(BreakableContext { + may_break: false, + break_ty: self.table.new_type_var(), + }); self.infer_expr(*body, &Expectation::has_type(Ty::unit())); let ctxt = self.breakables.pop().expect("breakable stack broken"); if ctxt.may_break { self.diverges = Diverges::Maybe; } - // FIXME handle break with value + if ctxt.may_break { ctxt.break_ty } else { @@ -229,26 +232,31 @@ impl<'a> InferenceContext<'a> { } Expr::Continue => Ty::simple(TypeCtor::Never), Expr::Break { expr } => { - let mut has_val_ty = None; + let val_ty = if let Some(expr) = expr { + self.infer_expr(*expr, &Expectation::none()) + } else { + Ty::unit() + }; - if let Some(expr) = expr { - has_val_ty = Some(self.infer_expr(*expr, &Expectation::none())); - } + let mut has_brkctx = false; - if let Some(ctxt) = self.breakables.last_mut() { - ctxt.may_break = true; - if let Some(val_ty) = has_val_ty { - if ctxt.break_ty == Ty::Unknown { - ctxt.break_ty = val_ty; - } else if ctxt.break_ty != val_ty { - // TODO: Unify partially matching type information (Option<{unknown}> + Option => Option) - } - } + if self.breakables.last().is_some() { + has_brkctx = true; } else { self.push_diagnostic(InferenceDiagnostic::BreakOutsideOfLoop { expr: tgt_expr, }); } + + if has_brkctx { + let last_ty = self.breakables.last().expect("This is a bug").break_ty.clone(); + let merged_type = self.coerce_merge_branch(&last_ty, &val_ty); + + let ctxt = self.breakables.last_mut().expect("This is a bug"); + ctxt.may_break = true; + ctxt.break_ty = merged_type; + } + Ty::simple(TypeCtor::Never) } Expr::Return { expr } => { From 6e36ad3d910ebec5af5f4f208b0f98c613687c41 Mon Sep 17 00:00:00 2001 From: Roland Ruckerbauer Date: Tue, 19 May 2020 21:18:43 +0200 Subject: [PATCH 30/70] Move false negative expr_diverges_missing_arm() to working tests --- crates/ra_hir_ty/src/_match.rs | 39 ++++++++++++++++------------------ 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/crates/ra_hir_ty/src/_match.rs b/crates/ra_hir_ty/src/_match.rs index 149f650424..3e6e1e3331 100644 --- a/crates/ra_hir_ty/src/_match.rs +++ b/crates/ra_hir_ty/src/_match.rs @@ -1946,6 +1946,23 @@ mod tests { check_no_diagnostic(content); } + + #[test] + fn expr_diverges_missing_arm() { + let content = r" + enum Either { + A, + B, + } + fn test_fn() { + match loop {} { + Either::A => (), + } + } + "; + + check_no_diagnostic(content); + } } #[cfg(test)] @@ -1997,26 +2014,6 @@ mod false_negatives { check_no_diagnostic(content); } - #[test] - fn expr_diverges_missing_arm() { - let content = r" - enum Either { - A, - B, - } - fn test_fn() { - match loop {} { - Either::A => (), - } - } - "; - - // This is a false negative. - // Even though the match expression diverges, rustc fails - // to compile here since `Either::B` is missing. - check_no_diagnostic(content); - } - #[test] fn expr_loop_missing_arm() { let content = r" @@ -2035,7 +2032,7 @@ mod false_negatives { // We currently infer the type of `loop { break Foo::A }` to `!`, which // causes us to skip the diagnostic since `Either::A` doesn't type check // with `!`. - check_no_diagnostic(content); + check_diagnostic(content); } #[test] From da09f967469127576d9a87a7c143f754777a4f6b Mon Sep 17 00:00:00 2001 From: Roland Ruckerbauer Date: Tue, 19 May 2020 21:49:45 +0200 Subject: [PATCH 31/70] loop return value inference: add tests --- crates/ra_hir_ty/src/tests/simple.rs | 63 ++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/crates/ra_hir_ty/src/tests/simple.rs b/crates/ra_hir_ty/src/tests/simple.rs index 72122c070a..fd2208af28 100644 --- a/crates/ra_hir_ty/src/tests/simple.rs +++ b/crates/ra_hir_ty/src/tests/simple.rs @@ -1860,3 +1860,66 @@ fn test() { "### ); } + +#[test] +fn infer_loop_break_with_val() { + assert_snapshot!( + infer(r#" +enum Option { Some(T), None } +use Option::*; + +fn test() { + let x = loop { + if false { + break None; + } + + break Some(true); + }; +} +"#), + @r###" + 60..169 '{ ... }; }': () + 70..71 'x': Option + 74..166 'loop {... }': Option + 79..166 '{ ... }': () + 89..133 'if fal... }': () + 92..97 'false': bool + 98..133 '{ ... }': () + 112..122 'break None': ! + 118..122 'None': Option + 143..159 'break ...(true)': ! + 149..153 'Some': Some(bool) -> Option + 149..159 'Some(true)': Option + 154..158 'true': bool + "### + ); +} + +#[test] +fn infer_loop_break_without_val() { + assert_snapshot!( + infer(r#" +enum Option { Some(T), None } +use Option::*; + +fn test() { + let x = loop { + if false { + break; + } + }; +} +"#), + @r###" + 60..137 '{ ... }; }': () + 70..71 'x': () + 74..134 'loop {... }': () + 79..134 '{ ... }': () + 89..128 'if fal... }': () + 92..97 'false': bool + 98..128 '{ ... }': () + 112..117 'break': ! + "### + ); +} From 45021cae551826727c32c7499c68ca48d046890f Mon Sep 17 00:00:00 2001 From: Roland Ruckerbauer Date: Tue, 19 May 2020 22:52:15 +0200 Subject: [PATCH 32/70] Apply suggestion of @flodiebold: Get rid of multiple unwraps --- crates/ra_hir_ty/src/infer/expr.rs | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/crates/ra_hir_ty/src/infer/expr.rs b/crates/ra_hir_ty/src/infer/expr.rs index 83702ada04..b28724f0e9 100644 --- a/crates/ra_hir_ty/src/infer/expr.rs +++ b/crates/ra_hir_ty/src/infer/expr.rs @@ -238,25 +238,23 @@ impl<'a> InferenceContext<'a> { Ty::unit() }; - let mut has_brkctx = false; + let last_ty = if let Some(ctxt) = self.breakables.last() { + ctxt.break_ty.clone() + } else { + Ty::Unknown + }; - if self.breakables.last().is_some() { - has_brkctx = true; + let merged_type = self.coerce_merge_branch(&last_ty, &val_ty); + + if let Some(ctxt) = self.breakables.last_mut() { + ctxt.break_ty = merged_type; + ctxt.may_break = true; } else { self.push_diagnostic(InferenceDiagnostic::BreakOutsideOfLoop { expr: tgt_expr, }); } - if has_brkctx { - let last_ty = self.breakables.last().expect("This is a bug").break_ty.clone(); - let merged_type = self.coerce_merge_branch(&last_ty, &val_ty); - - let ctxt = self.breakables.last_mut().expect("This is a bug"); - ctxt.may_break = true; - ctxt.break_ty = merged_type; - } - Ty::simple(TypeCtor::Never) } Expr::Return { expr } => { From 80545e5d3a72ef05a77ff9584234f030c69bfe9f Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 20 May 2020 00:07:00 +0200 Subject: [PATCH 33/70] New assist: add turbo fish --- .../ra_assists/src/handlers/add_turbo_fish.rs | 134 ++++++++++++++++++ crates/ra_assists/src/lib.rs | 2 + crates/ra_assists/src/marks.rs | 2 + crates/ra_assists/src/tests/generated.rs | 19 +++ docs/user/assists.md | 18 +++ xtask/tests/tidy.rs | 1 + 6 files changed, 176 insertions(+) create mode 100644 crates/ra_assists/src/handlers/add_turbo_fish.rs diff --git a/crates/ra_assists/src/handlers/add_turbo_fish.rs b/crates/ra_assists/src/handlers/add_turbo_fish.rs new file mode 100644 index 0000000000..a0363bc78b --- /dev/null +++ b/crates/ra_assists/src/handlers/add_turbo_fish.rs @@ -0,0 +1,134 @@ +use ra_ide_db::defs::{classify_name_ref, Definition, NameRefClass}; +use ra_syntax::{ast, AstNode, SyntaxKind, T}; + +use crate::{ + assist_context::{AssistContext, Assists}, + AssistId, +}; +use test_utils::tested_by; + +// Assist: add_turbo_fish +// +// Adds `::<_>` to a call of a generic method or function. +// +// ``` +// fn make() -> T { todo!() } +// fn main() { +// let x = make<|>(); +// } +// ``` +// -> +// ``` +// fn make() -> T { todo!() } +// fn main() { +// let x = make::<${0:_}>(); +// } +// ``` +pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { + let ident = ctx.find_token_at_offset(SyntaxKind::IDENT)?; + let next_token = ident.next_token()?; + if next_token.kind() == T![::] { + tested_by!(add_turbo_fish_one_fish_is_enough); + return None; + } + let name_ref = ast::NameRef::cast(ident.parent())?; + let def = match classify_name_ref(&ctx.sema, &name_ref)? { + NameRefClass::Definition(def) => def, + NameRefClass::FieldShorthand { .. } => return None, + }; + let fun = match def { + Definition::ModuleDef(hir::ModuleDef::Function(it)) => it, + _ => return None, + }; + let generics = hir::GenericDef::Function(fun).params(ctx.sema.db); + if generics.is_empty() { + tested_by!(add_turbo_fish_non_generic); + return None; + } + acc.add(AssistId("add_turbo_fish"), "Add `::<>`", ident.text_range(), |builder| { + match ctx.config.snippet_cap { + Some(cap) => builder.insert_snippet(cap, ident.text_range().end(), "::<${0:_}>"), + None => builder.insert(ident.text_range().end(), "::<_>"), + } + }) +} + +#[cfg(test)] +mod tests { + use crate::tests::{check_assist, check_assist_not_applicable}; + + use super::*; + use test_utils::covers; + + #[test] + fn add_turbo_fish_function() { + check_assist( + add_turbo_fish, + r#" +fn make() -> T {} +fn main() { + make<|>(); +} +"#, + r#" +fn make() -> T {} +fn main() { + make::<${0:_}>(); +} +"#, + ); + } + + #[test] + fn add_turbo_fish_method() { + check_assist( + add_turbo_fish, + r#" +struct S; +impl S { + fn make(&self) -> T {} +} +fn main() { + S.make<|>(); +} +"#, + r#" +struct S; +impl S { + fn make(&self) -> T {} +} +fn main() { + S.make::<${0:_}>(); +} +"#, + ); + } + + #[test] + fn add_turbo_fish_one_fish_is_enough() { + covers!(add_turbo_fish_one_fish_is_enough); + check_assist_not_applicable( + add_turbo_fish, + r#" +fn make() -> T {} +fn main() { + make<|>::<()>(); +} +"#, + ); + } + + #[test] + fn add_turbo_fish_non_generic() { + covers!(add_turbo_fish_non_generic); + check_assist_not_applicable( + add_turbo_fish, + r#" +fn make() -> () {} +fn main() { + make<|>(); +} +"#, + ); + } +} diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs index 7f0a723c9e..339f24100d 100644 --- a/crates/ra_assists/src/lib.rs +++ b/crates/ra_assists/src/lib.rs @@ -110,6 +110,7 @@ mod handlers { mod add_impl; mod add_missing_impl_members; mod add_new; + mod add_turbo_fish; mod apply_demorgan; mod auto_import; mod change_return_type_to_result; @@ -147,6 +148,7 @@ mod handlers { add_function::add_function, add_impl::add_impl, add_new::add_new, + add_turbo_fish::add_turbo_fish, apply_demorgan::apply_demorgan, auto_import::auto_import, change_return_type_to_result::change_return_type_to_result, diff --git a/crates/ra_assists/src/marks.rs b/crates/ra_assists/src/marks.rs index 8d910205f0..d579e627f0 100644 --- a/crates/ra_assists/src/marks.rs +++ b/crates/ra_assists/src/marks.rs @@ -9,4 +9,6 @@ test_utils::marks![ test_not_applicable_if_variable_unused change_visibility_field_false_positive test_add_from_impl_already_exists + add_turbo_fish_one_fish_is_enough + add_turbo_fish_non_generic ]; diff --git a/crates/ra_assists/src/tests/generated.rs b/crates/ra_assists/src/tests/generated.rs index 9487c9239f..32fbcdef4a 100644 --- a/crates/ra_assists/src/tests/generated.rs +++ b/crates/ra_assists/src/tests/generated.rs @@ -211,6 +211,25 @@ impl Ctx { ) } +#[test] +fn doctest_add_turbo_fish() { + check_doc_test( + "add_turbo_fish", + r#####" +fn make() -> T { todo!() } +fn main() { + let x = make<|>(); +} +"#####, + r#####" +fn make() -> T { todo!() } +fn main() { + let x = make::<${0:_}>(); +} +"#####, + ) +} + #[test] fn doctest_apply_demorgan() { check_doc_test( diff --git a/docs/user/assists.md b/docs/user/assists.md index 41c5df5287..c72b50a4da 100644 --- a/docs/user/assists.md +++ b/docs/user/assists.md @@ -203,6 +203,24 @@ impl Ctx { ``` +## `add_turbo_fish` + +Adds `::<_>` to a call of a generic method or function. + +```rust +// BEFORE +fn make() -> T { todo!() } +fn main() { + let x = make┃(); +} + +// AFTER +fn make() -> T { todo!() } +fn main() { + let x = make::<${0:_}>(); +} +``` + ## `apply_demorgan` Apply [De Morgan's law](https://en.wikipedia.org/wiki/De_Morgan%27s_laws). diff --git a/xtask/tests/tidy.rs b/xtask/tests/tidy.rs index b8e8860ba1..2e9fcf07c5 100644 --- a/xtask/tests/tidy.rs +++ b/xtask/tests/tidy.rs @@ -57,6 +57,7 @@ fn check_todo(path: &Path, text: &str) { "tests/generated.rs", "handlers/add_missing_impl_members.rs", "handlers/add_function.rs", + "handlers/add_turbo_fish.rs", // To support generating `todo!()` in assists, we have `expr_todo()` in ast::make. "ast/make.rs", ]; From 39ec581bf6e01cf2d7f33aacbe8879abf7ea3199 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 20 May 2020 00:49:08 +0200 Subject: [PATCH 34/70] Fix client-side snippets --- editors/code/src/commands/index.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/editors/code/src/commands/index.ts b/editors/code/src/commands/index.ts index 770d11bd36..0937b495c2 100644 --- a/editors/code/src/commands/index.ts +++ b/editors/code/src/commands/index.ts @@ -65,7 +65,8 @@ export function applySnippetWorkspaceEdit(_ctx: Ctx): Cmd { let lineDelta = 0; await editor.edit((builder) => { for (const indel of edits) { - if (indel.newText.indexOf('$0') !== -1) { + const isSnippet = indel.newText.indexOf('$0') !== -1 || indel.newText.indexOf('${') !== -1; + if (isSnippet) { editWithSnippet = indel; } else { if (!editWithSnippet) { From 4a3a525ea93cc5761e03aa066f6703dc9563bcaa Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 19 May 2020 21:39:27 +0200 Subject: [PATCH 35/70] Use new format for all assists that don't change cursor positon --- crates/rust-analyzer/src/to_proto.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index 2b1a3378f8..af54f81b7d 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs @@ -639,7 +639,7 @@ fn main() { } pub(crate) fn code_action(world: &WorldSnapshot, assist: Assist) -> Result { - let res = if assist.source_change.is_snippet { + let res = if assist.source_change.cursor_position.is_none() { lsp_ext::CodeAction { title: assist.label, kind: Some(String::new()), @@ -647,6 +647,7 @@ pub(crate) fn code_action(world: &WorldSnapshot, assist: Assist) -> Result Date: Tue, 19 May 2020 21:40:47 +0200 Subject: [PATCH 36/70] Cleanup --- crates/ra_assists/src/handlers/add_explicit_type.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/crates/ra_assists/src/handlers/add_explicit_type.rs b/crates/ra_assists/src/handlers/add_explicit_type.rs index 0c7d5e355f..770049212d 100644 --- a/crates/ra_assists/src/handlers/add_explicit_type.rs +++ b/crates/ra_assists/src/handlers/add_explicit_type.rs @@ -25,9 +25,8 @@ pub(crate) fn add_explicit_type(acc: &mut Assists, ctx: &AssistContext) -> Optio let stmt = ctx.find_node_at_offset::()?; let module = ctx.sema.scope(stmt.syntax()).module()?; let expr = stmt.initializer()?; - let pat = stmt.pat()?; // Must be a binding - let pat = match pat { + let pat = match stmt.pat()? { ast::Pat::BindPat(bind_pat) => bind_pat, _ => return None, }; @@ -46,7 +45,7 @@ pub(crate) fn add_explicit_type(acc: &mut Assists, ctx: &AssistContext) -> Optio // Assist not applicable if the type has already been specified // and it has no placeholders let ascribed_ty = stmt.ascribed_type(); - if let Some(ref ty) = ascribed_ty { + if let Some(ty) = &ascribed_ty { if ty.syntax().descendants().find_map(ast::PlaceholderType::cast).is_none() { return None; } From 9c3acd3028d32b6cf099e8d5dffe435f15f241a2 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 19 May 2020 21:43:14 +0200 Subject: [PATCH 37/70] Cleanup --- crates/ra_assists/src/handlers/add_from_impl_for_enum.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs b/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs index 6a49b7dbd1..eb57b7231f 100644 --- a/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs +++ b/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs @@ -1,6 +1,5 @@ use ra_ide_db::RootDatabase; use ra_syntax::ast::{self, AstNode, NameOwner}; -use stdx::format_to; use test_utils::tested_by; use crate::{utils::FamousDefs, AssistContext, AssistId, Assists}; @@ -35,7 +34,7 @@ pub(crate) fn add_from_impl_for_enum(acc: &mut Assists, ctx: &AssistContext) -> } let field_type = field_list.fields().next()?.type_ref()?; let path = match field_type { - ast::TypeRef::PathType(p) => p, + ast::TypeRef::PathType(it) => it, _ => return None, }; @@ -51,9 +50,7 @@ pub(crate) fn add_from_impl_for_enum(acc: &mut Assists, ctx: &AssistContext) -> target, |edit| { let start_offset = variant.parent_enum().syntax().text_range().end(); - let mut buf = String::new(); - format_to!( - buf, + let buf = format!( r#" impl From<{0}> for {1} {{ From 8eb3272ad6f774bccb967ee640b72a9a17273e7b Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 19 May 2020 22:25:07 +0200 Subject: [PATCH 38/70] Use snippets in add function --- .../ra_assists/src/handlers/add_function.rs | 79 ++++++++++--------- crates/ra_assists/src/tests/generated.rs | 2 +- crates/ra_assists/src/utils.rs | 24 +++++- crates/stdx/src/lib.rs | 8 ++ docs/user/assists.md | 2 +- 5 files changed, 75 insertions(+), 40 deletions(-) diff --git a/crates/ra_assists/src/handlers/add_function.rs b/crates/ra_assists/src/handlers/add_function.rs index de016ae4e1..69fede00fb 100644 --- a/crates/ra_assists/src/handlers/add_function.rs +++ b/crates/ra_assists/src/handlers/add_function.rs @@ -10,7 +10,7 @@ use ra_syntax::{ }; use rustc_hash::{FxHashMap, FxHashSet}; -use crate::{AssistContext, AssistId, Assists}; +use crate::{utils::render_snippet, AssistContext, AssistId, Assists}; // Assist: add_function // @@ -33,7 +33,7 @@ use crate::{AssistContext, AssistId, Assists}; // } // // fn bar(arg: &str, baz: Baz) { -// todo!() +// ${0:todo!()} // } // // ``` @@ -58,18 +58,27 @@ pub(crate) fn add_function(acc: &mut Assists, ctx: &AssistContext) -> Option<()> let function_builder = FunctionBuilder::from_call(&ctx, &call, &path, target_module)?; let target = call.syntax().text_range(); - acc.add(AssistId("add_function"), "Add function", target, |edit| { + acc.add(AssistId("add_function"), "Add function", target, |builder| { let function_template = function_builder.render(); - edit.set_file(function_template.file); - edit.set_cursor(function_template.cursor_offset); - edit.insert(function_template.insert_offset, function_template.fn_def.to_string()); + builder.set_file(function_template.file); + match ctx.config.snippet_cap { + Some(cap) => { + let snippet = render_snippet( + function_template.fn_def.syntax(), + function_template.placeholder_expr.syntax(), + ); + builder.insert_snippet(cap, function_template.insert_offset, snippet) + } + None => builder + .insert(function_template.insert_offset, function_template.fn_def.to_string()), + } }) } struct FunctionTemplate { insert_offset: TextSize, - cursor_offset: TextSize, fn_def: ast::SourceFile, + placeholder_expr: ast::MacroCall, file: FileId, } @@ -136,9 +145,7 @@ impl FunctionBuilder { let placeholder_expr = fn_def.syntax().descendants().find_map(ast::MacroCall::cast).unwrap(); - let cursor_offset_from_fn_start = placeholder_expr.syntax().text_range().start(); - let cursor_offset = insert_offset + cursor_offset_from_fn_start; - FunctionTemplate { insert_offset, cursor_offset, fn_def, file: self.file } + FunctionTemplate { insert_offset, placeholder_expr, fn_def, file: self.file } } } @@ -316,7 +323,7 @@ fn foo() { } fn bar() { - <|>todo!() + ${0:todo!()} } ", ) @@ -343,7 +350,7 @@ impl Foo { } fn bar() { - <|>todo!() + ${0:todo!()} } ", ) @@ -367,7 +374,7 @@ fn foo1() { } fn bar() { - <|>todo!() + ${0:todo!()} } fn foo2() {} @@ -393,7 +400,7 @@ mod baz { } fn bar() { - <|>todo!() + ${0:todo!()} } } ", @@ -419,7 +426,7 @@ fn foo() { } fn bar(baz: Baz) { - <|>todo!() + ${0:todo!()} } ", ); @@ -452,7 +459,7 @@ impl Baz { } fn bar(baz: Baz) { - <|>todo!() + ${0:todo!()} } ", ) @@ -473,7 +480,7 @@ fn foo() { } fn bar(arg: &str) { - <|>todo!() + ${0:todo!()} } "#, ) @@ -494,7 +501,7 @@ fn foo() { } fn bar(arg: char) { - <|>todo!() + ${0:todo!()} } "#, ) @@ -515,7 +522,7 @@ fn foo() { } fn bar(arg: i32) { - <|>todo!() + ${0:todo!()} } ", ) @@ -536,7 +543,7 @@ fn foo() { } fn bar(arg: u8) { - <|>todo!() + ${0:todo!()} } ", ) @@ -561,7 +568,7 @@ fn foo() { } fn bar(x: u8) { - <|>todo!() + ${0:todo!()} } ", ) @@ -584,7 +591,7 @@ fn foo() { } fn bar(worble: ()) { - <|>todo!() + ${0:todo!()} } ", ) @@ -613,7 +620,7 @@ fn baz() { } fn bar(foo: impl Foo) { - <|>todo!() + ${0:todo!()} } ", ) @@ -640,7 +647,7 @@ fn foo() { } fn bar(baz: &Baz) { - <|>todo!() + ${0:todo!()} } ", ) @@ -669,7 +676,7 @@ fn foo() { } fn bar(baz: Baz::Bof) { - <|>todo!() + ${0:todo!()} } ", ) @@ -692,7 +699,7 @@ fn foo(t: T) { } fn bar(t: T) { - <|>todo!() + ${0:todo!()} } ", ) @@ -723,7 +730,7 @@ fn foo() { } fn bar(arg: fn() -> Baz) { - <|>todo!() + ${0:todo!()} } ", ) @@ -748,7 +755,7 @@ fn foo() { } fn bar(closure: impl Fn(i64) -> i64) { - <|>todo!() + ${0:todo!()} } ", ) @@ -769,7 +776,7 @@ fn foo() { } fn bar(baz: ()) { - <|>todo!() + ${0:todo!()} } ", ) @@ -794,7 +801,7 @@ fn foo() { } fn bar(baz_1: Baz, baz_2: Baz) { - <|>todo!() + ${0:todo!()} } ", ) @@ -819,7 +826,7 @@ fn foo() { } fn bar(baz_1: Baz, baz_2: Baz, arg_1: &str, arg_2: &str) { - <|>todo!() + ${0:todo!()} } "#, ) @@ -839,7 +846,7 @@ fn foo() { r" mod bar { pub(crate) fn my_fn() { - <|>todo!() + ${0:todo!()} } } @@ -878,7 +885,7 @@ fn bar() { } fn baz(foo: foo::Foo) { - <|>todo!() + ${0:todo!()} } ", ) @@ -902,7 +909,7 @@ mod bar { fn something_else() {} pub(crate) fn my_fn() { - <|>todo!() + ${0:todo!()} } } @@ -930,7 +937,7 @@ fn foo() { mod bar { mod baz { pub(crate) fn my_fn() { - <|>todo!() + ${0:todo!()} } } } @@ -959,7 +966,7 @@ fn main() { pub(crate) fn bar() { - <|>todo!() + ${0:todo!()} }", ) } diff --git a/crates/ra_assists/src/tests/generated.rs b/crates/ra_assists/src/tests/generated.rs index 32fbcdef4a..1d82c245d0 100644 --- a/crates/ra_assists/src/tests/generated.rs +++ b/crates/ra_assists/src/tests/generated.rs @@ -78,7 +78,7 @@ fn foo() { } fn bar(arg: &str, baz: Baz) { - todo!() + ${0:todo!()} } "#####, diff --git a/crates/ra_assists/src/utils.rs b/crates/ra_assists/src/utils.rs index f3fc92ebf2..bb9749b06f 100644 --- a/crates/ra_assists/src/utils.rs +++ b/crates/ra_assists/src/utils.rs @@ -1,18 +1,38 @@ //! Assorted functions shared by several assists. pub(crate) mod insert_use; -use std::iter; +use std::{iter, ops}; use hir::{Adt, Crate, Semantics, Trait, Type}; use ra_ide_db::RootDatabase; use ra_syntax::{ ast::{self, make, NameOwner}, - AstNode, T, + AstNode, SyntaxNode, T, }; use rustc_hash::FxHashSet; pub(crate) use insert_use::insert_use_statement; +pub(crate) fn render_snippet(node: &SyntaxNode, placeholder: &SyntaxNode) -> String { + assert!(placeholder.ancestors().any(|it| it == *node)); + let range = placeholder.text_range() - node.text_range().start(); + let range: ops::Range = range.into(); + + let mut placeholder = placeholder.to_string(); + escape(&mut placeholder); + let tab_stop = format!("${{0:{}}}", placeholder); + + let mut buf = node.to_string(); + buf.replace_range(range, &tab_stop); + return buf; + + fn escape(buf: &mut String) { + stdx::replace(buf, '{', r"\{"); + stdx::replace(buf, '}', r"\}"); + stdx::replace(buf, '$', r"\$"); + } +} + pub fn get_missing_assoc_items( sema: &Semantics, impl_def: &ast::ImplDef, diff --git a/crates/stdx/src/lib.rs b/crates/stdx/src/lib.rs index 0f34ce70e1..71a57fba23 100644 --- a/crates/stdx/src/lib.rs +++ b/crates/stdx/src/lib.rs @@ -116,3 +116,11 @@ pub fn to_lower_snake_case(s: &str) -> String { } buf } + +pub fn replace(buf: &mut String, from: char, to: &str) { + if !buf.contains(from) { + return; + } + // FIXME: do this in place. + *buf = buf.replace(from, to) +} diff --git a/docs/user/assists.md b/docs/user/assists.md index c72b50a4da..10ab67b2e7 100644 --- a/docs/user/assists.md +++ b/docs/user/assists.md @@ -77,7 +77,7 @@ fn foo() { } fn bar(arg: &str, baz: Baz) { - todo!() + ${0:todo!()} } ``` From 4de2749db8281c00aba37270fa9ae8d4bd2572d8 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 20 May 2020 01:28:11 +0200 Subject: [PATCH 39/70] Explain the purpose of `ast::make` module more clearly --- .../src/handlers/replace_unwrap_with_match.rs | 2 +- crates/ra_syntax/src/ast/make.rs | 11 +++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs b/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs index c4b56f6e90..b379b55a83 100644 --- a/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs +++ b/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs @@ -51,7 +51,7 @@ pub(crate) fn replace_unwrap_with_match(acc: &mut Assists, ctx: &AssistContext) let bind_path = make::path_unqualified(make::path_segment(make::name_ref("a"))); let ok_arm = make::match_arm(iter::once(ok_tuple), make::expr_path(bind_path)); - let unreachable_call = make::unreachable_macro_call().into(); + let unreachable_call = make::expr_unreachable(); let err_arm = make::match_arm(iter::once(make::placeholder_pat().into()), unreachable_call); let match_arm_list = make::match_arm_list(vec![ok_arm, err_arm]); diff --git a/crates/ra_syntax/src/ast/make.rs b/crates/ra_syntax/src/ast/make.rs index d0e960fb49..2db0170388 100644 --- a/crates/ra_syntax/src/ast/make.rs +++ b/crates/ra_syntax/src/ast/make.rs @@ -1,5 +1,9 @@ //! This module contains free-standing functions for creating AST fragments out //! of smaller pieces. +//! +//! Note that all functions here intended to be stupid constructors, which just +//! assemble a finish node from immediate children. If you want to do something +//! smarter than that, it probably doesn't belong in this module. use itertools::Itertools; use stdx::format_to; @@ -95,6 +99,9 @@ pub fn expr_empty_block() -> ast::Expr { pub fn expr_unimplemented() -> ast::Expr { expr_from_text("unimplemented!()") } +pub fn expr_unreachable() -> ast::Expr { + expr_from_text("unreachable!()") +} pub fn expr_todo() -> ast::Expr { expr_from_text("todo!()") } @@ -264,10 +271,6 @@ pub fn token(kind: SyntaxKind) -> SyntaxToken { .unwrap_or_else(|| panic!("unhandled token: {:?}", kind)) } -pub fn unreachable_macro_call() -> ast::MacroCall { - ast_from_text(&format!("unreachable!()")) -} - pub fn param(name: String, ty: String) -> ast::Param { ast_from_text(&format!("fn f({}: {}) {{ }}", name, ty)) } From e6fc0bdffb213f6e94c5bb4081e6d175ccbd518f Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 19 May 2020 23:12:01 +0200 Subject: [PATCH 40/70] Moderate cleanup of add_function --- .../ra_assists/src/handlers/add_function.rs | 78 +++++++++++-------- crates/ra_assists/src/utils.rs | 8 +- crates/ra_syntax/src/algo.rs | 36 +++++++-- crates/ra_syntax/src/ast/edit.rs | 44 +++++++++-- crates/ra_syntax/src/ast/make.rs | 25 +++--- 5 files changed, 127 insertions(+), 64 deletions(-) diff --git a/crates/ra_assists/src/handlers/add_function.rs b/crates/ra_assists/src/handlers/add_function.rs index 69fede00fb..a0709630d2 100644 --- a/crates/ra_assists/src/handlers/add_function.rs +++ b/crates/ra_assists/src/handlers/add_function.rs @@ -4,13 +4,13 @@ use ra_syntax::{ ast::{ self, edit::{AstNodeEdit, IndentLevel}, - ArgListOwner, AstNode, ModuleItemOwner, + make, ArgListOwner, AstNode, ModuleItemOwner, }, SyntaxKind, SyntaxNode, TextSize, }; use rustc_hash::{FxHashMap, FxHashSet}; -use crate::{utils::render_snippet, AssistContext, AssistId, Assists}; +use crate::{assist_config::SnippetCap, utils::render_snippet, AssistContext, AssistId, Assists}; // Assist: add_function // @@ -61,27 +61,33 @@ pub(crate) fn add_function(acc: &mut Assists, ctx: &AssistContext) -> Option<()> acc.add(AssistId("add_function"), "Add function", target, |builder| { let function_template = function_builder.render(); builder.set_file(function_template.file); + let new_fn = function_template.to_string(ctx.config.snippet_cap); match ctx.config.snippet_cap { - Some(cap) => { - let snippet = render_snippet( - function_template.fn_def.syntax(), - function_template.placeholder_expr.syntax(), - ); - builder.insert_snippet(cap, function_template.insert_offset, snippet) - } - None => builder - .insert(function_template.insert_offset, function_template.fn_def.to_string()), + Some(cap) => builder.insert_snippet(cap, function_template.insert_offset, new_fn), + None => builder.insert(function_template.insert_offset, new_fn), } }) } struct FunctionTemplate { insert_offset: TextSize, - fn_def: ast::SourceFile, placeholder_expr: ast::MacroCall, + leading_ws: String, + fn_def: ast::FnDef, + trailing_ws: String, file: FileId, } +impl FunctionTemplate { + fn to_string(&self, cap: Option) -> String { + let f = match cap { + Some(cap) => render_snippet(cap, self.fn_def.syntax(), self.placeholder_expr.syntax()), + None => self.fn_def.to_string(), + }; + format!("{}{}{}", self.leading_ws, f, self.trailing_ws) + } +} + struct FunctionBuilder { target: GeneratedFunctionTarget, fn_name: ast::Name, @@ -119,33 +125,41 @@ impl FunctionBuilder { } fn render(self) -> FunctionTemplate { - let placeholder_expr = ast::make::expr_todo(); - let fn_body = ast::make::block_expr(vec![], Some(placeholder_expr)); - let mut fn_def = ast::make::fn_def(self.fn_name, self.type_params, self.params, fn_body); - if self.needs_pub { - fn_def = ast::make::add_pub_crate_modifier(fn_def); - } + let placeholder_expr = make::expr_todo(); + let fn_body = make::block_expr(vec![], Some(placeholder_expr)); + let visibility = if self.needs_pub { Some(make::visibility_pub_crate()) } else { None }; + let mut fn_def = + make::fn_def(visibility, self.fn_name, self.type_params, self.params, fn_body); + let leading_ws; + let trailing_ws; - let (fn_def, insert_offset) = match self.target { + let insert_offset = match self.target { GeneratedFunctionTarget::BehindItem(it) => { - let with_leading_blank_line = ast::make::add_leading_newlines(2, fn_def); - let indented = with_leading_blank_line.indent(IndentLevel::from_node(&it)); - (indented, it.text_range().end()) + let indent = IndentLevel::from_node(&it); + leading_ws = format!("\n\n{}", indent); + fn_def = fn_def.indent(indent); + trailing_ws = String::new(); + it.text_range().end() } GeneratedFunctionTarget::InEmptyItemList(it) => { - let indent_once = IndentLevel(1); let indent = IndentLevel::from_node(it.syntax()); - let fn_def = ast::make::add_leading_newlines(1, fn_def); - let fn_def = fn_def.indent(indent_once); - let fn_def = ast::make::add_trailing_newlines(1, fn_def); - let fn_def = fn_def.indent(indent); - (fn_def, it.syntax().text_range().start() + TextSize::of('{')) + leading_ws = format!("\n{}", indent + 1); + fn_def = fn_def.indent(indent + 1); + trailing_ws = format!("\n{}", indent); + it.syntax().text_range().start() + TextSize::of('{') } }; let placeholder_expr = fn_def.syntax().descendants().find_map(ast::MacroCall::cast).unwrap(); - FunctionTemplate { insert_offset, placeholder_expr, fn_def, file: self.file } + FunctionTemplate { + insert_offset, + placeholder_expr, + leading_ws, + fn_def, + trailing_ws, + file: self.file, + } } } @@ -165,7 +179,7 @@ impl GeneratedFunctionTarget { fn fn_name(call: &ast::Path) -> Option { let name = call.segment()?.syntax().to_string(); - Some(ast::make::name(&name)) + Some(make::name(&name)) } /// Computes the type variables and arguments required for the generated function @@ -187,8 +201,8 @@ fn fn_args( }); } deduplicate_arg_names(&mut arg_names); - let params = arg_names.into_iter().zip(arg_types).map(|(name, ty)| ast::make::param(name, ty)); - Some((None, ast::make::param_list(params))) + let params = arg_names.into_iter().zip(arg_types).map(|(name, ty)| make::param(name, ty)); + Some((None, make::param_list(params))) } /// Makes duplicate argument names unique by appending incrementing numbers. diff --git a/crates/ra_assists/src/utils.rs b/crates/ra_assists/src/utils.rs index bb9749b06f..8a26a68082 100644 --- a/crates/ra_assists/src/utils.rs +++ b/crates/ra_assists/src/utils.rs @@ -11,9 +11,15 @@ use ra_syntax::{ }; use rustc_hash::FxHashSet; +use crate::assist_config::SnippetCap; + pub(crate) use insert_use::insert_use_statement; -pub(crate) fn render_snippet(node: &SyntaxNode, placeholder: &SyntaxNode) -> String { +pub(crate) fn render_snippet( + _cap: SnippetCap, + node: &SyntaxNode, + placeholder: &SyntaxNode, +) -> String { assert!(placeholder.ancestors().any(|it| it == *node)); let range = placeholder.text_range() - node.text_range().start(); let range: ops::Range = range.into(); diff --git a/crates/ra_syntax/src/algo.rs b/crates/ra_syntax/src/algo.rs index 2a8dac757b..664894d1f8 100644 --- a/crates/ra_syntax/src/algo.rs +++ b/crates/ra_syntax/src/algo.rs @@ -266,6 +266,15 @@ impl<'a> SyntaxRewriter<'a> { let replacement = Replacement::Single(with.clone().into()); self.replacements.insert(what, replacement); } + pub fn replace_with_many>( + &mut self, + what: &T, + with: Vec, + ) { + let what = what.clone().into(); + let replacement = Replacement::Many(with); + self.replacements.insert(what, replacement); + } pub fn replace_ast(&mut self, what: &T, with: &T) { self.replace(what.syntax(), with.syntax()) } @@ -302,31 +311,41 @@ impl<'a> SyntaxRewriter<'a> { fn rewrite_children(&self, node: &SyntaxNode) -> SyntaxNode { // FIXME: this could be made much faster. - let new_children = - node.children_with_tokens().flat_map(|it| self.rewrite_self(&it)).collect::>(); + let mut new_children = Vec::new(); + for child in node.children_with_tokens() { + self.rewrite_self(&mut new_children, &child); + } with_children(node, new_children) } fn rewrite_self( &self, + acc: &mut Vec>, element: &SyntaxElement, - ) -> Option> { + ) { if let Some(replacement) = self.replacement(&element) { - return match replacement { + match replacement { Replacement::Single(NodeOrToken::Node(it)) => { - Some(NodeOrToken::Node(it.green().clone())) + acc.push(NodeOrToken::Node(it.green().clone())) } Replacement::Single(NodeOrToken::Token(it)) => { - Some(NodeOrToken::Token(it.green().clone())) + acc.push(NodeOrToken::Token(it.green().clone())) } - Replacement::Delete => None, + Replacement::Many(replacements) => { + acc.extend(replacements.iter().map(|it| match it { + NodeOrToken::Node(it) => NodeOrToken::Node(it.green().clone()), + NodeOrToken::Token(it) => NodeOrToken::Token(it.green().clone()), + })) + } + Replacement::Delete => (), }; + return; } let res = match element { NodeOrToken::Token(it) => NodeOrToken::Token(it.green().clone()), NodeOrToken::Node(it) => NodeOrToken::Node(self.rewrite_children(it).green().clone()), }; - Some(res) + acc.push(res) } } @@ -341,6 +360,7 @@ impl ops::AddAssign for SyntaxRewriter<'_> { enum Replacement { Delete, Single(SyntaxElement), + Many(Vec), } fn with_children( diff --git a/crates/ra_syntax/src/ast/edit.rs b/crates/ra_syntax/src/ast/edit.rs index 24a1e1d918..29eb3fcb9c 100644 --- a/crates/ra_syntax/src/ast/edit.rs +++ b/crates/ra_syntax/src/ast/edit.rs @@ -1,7 +1,10 @@ //! This module contains functions for editing syntax trees. As the trees are //! immutable, all function here return a fresh copy of the tree, instead of //! doing an in-place modification. -use std::{iter, ops::RangeInclusive}; +use std::{ + fmt, iter, + ops::{self, RangeInclusive}, +}; use arrayvec::ArrayVec; @@ -437,6 +440,28 @@ impl From for IndentLevel { } } +impl fmt::Display for IndentLevel { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let spaces = " "; + let buf; + let len = self.0 as usize * 4; + let indent = if len <= spaces.len() { + &spaces[..len] + } else { + buf = iter::repeat(' ').take(len).collect::(); + &buf + }; + fmt::Display::fmt(indent, f) + } +} + +impl ops::Add for IndentLevel { + type Output = IndentLevel; + fn add(self, rhs: u8) -> IndentLevel { + IndentLevel(self.0 + rhs) + } +} + impl IndentLevel { pub fn from_node(node: &SyntaxNode) -> IndentLevel { let first_token = match node.first_token() { @@ -453,6 +478,14 @@ impl IndentLevel { IndentLevel(0) } + /// XXX: this intentionally doesn't change the indent of the very first token. + /// Ie, in something like + /// ``` + /// fn foo() { + /// 92 + /// } + /// ``` + /// if you indent the block, the `{` token would stay put. fn increase_indent(self, node: SyntaxNode) -> SyntaxNode { let mut rewriter = SyntaxRewriter::default(); node.descendants_with_tokens() @@ -463,12 +496,7 @@ impl IndentLevel { text.contains('\n') }) .for_each(|ws| { - let new_ws = make::tokens::whitespace(&format!( - "{}{:width$}", - ws.syntax().text(), - "", - width = self.0 as usize * 4 - )); + let new_ws = make::tokens::whitespace(&format!("{}{}", ws.syntax(), self,)); rewriter.replace(ws.syntax(), &new_ws) }); rewriter.rewrite(&node) @@ -485,7 +513,7 @@ impl IndentLevel { }) .for_each(|ws| { let new_ws = make::tokens::whitespace( - &ws.syntax().text().replace(&format!("\n{:1$}", "", self.0 as usize * 4), "\n"), + &ws.syntax().text().replace(&format!("\n{}", self), "\n"), ); rewriter.replace(ws.syntax(), &new_ws) }); diff --git a/crates/ra_syntax/src/ast/make.rs b/crates/ra_syntax/src/ast/make.rs index d0e960fb49..b275780eca 100644 --- a/crates/ra_syntax/src/ast/make.rs +++ b/crates/ra_syntax/src/ast/make.rs @@ -277,7 +277,12 @@ pub fn param_list(pats: impl IntoIterator) -> ast::ParamList ast_from_text(&format!("fn f({}) {{ }}", args)) } +pub fn visibility_pub_crate() -> ast::Visibility { + ast_from_text("pub(crate) struct S") +} + pub fn fn_def( + visibility: Option, fn_name: ast::Name, type_params: Option, params: ast::ParamList, @@ -285,21 +290,11 @@ pub fn fn_def( ) -> ast::FnDef { let type_params = if let Some(type_params) = type_params { format!("<{}>", type_params) } else { "".into() }; - ast_from_text(&format!("fn {}{}{} {}", fn_name, type_params, params, body)) -} - -pub fn add_leading_newlines(amount_of_newlines: usize, t: impl AstNode) -> ast::SourceFile { - let newlines = "\n".repeat(amount_of_newlines); - ast_from_text(&format!("{}{}", newlines, t.syntax())) -} - -pub fn add_trailing_newlines(amount_of_newlines: usize, t: impl AstNode) -> ast::SourceFile { - let newlines = "\n".repeat(amount_of_newlines); - ast_from_text(&format!("{}{}", t.syntax(), newlines)) -} - -pub fn add_pub_crate_modifier(fn_def: ast::FnDef) -> ast::FnDef { - ast_from_text(&format!("pub(crate) {}", fn_def)) + let visibility = match visibility { + None => String::new(), + Some(it) => format!("{} ", it), + }; + ast_from_text(&format!("{}fn {}{}{} {}", visibility, fn_name, type_params, params, body)) } fn ast_from_text(text: &str) -> N { From a04cababaa144d7a6db7b1dd114494b33d281ab9 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 20 May 2020 01:53:21 +0200 Subject: [PATCH 41/70] Use snippets in add_missing_members --- crates/ra_assists/src/assist_context.rs | 16 ++++- .../ra_assists/src/handlers/add_function.rs | 12 +++- .../src/handlers/add_missing_impl_members.rs | 62 +++++++++++-------- crates/ra_assists/src/tests/generated.rs | 4 +- crates/ra_assists/src/utils.rs | 31 +++++++--- docs/user/assists.md | 4 +- 6 files changed, 84 insertions(+), 45 deletions(-) diff --git a/crates/ra_assists/src/assist_context.rs b/crates/ra_assists/src/assist_context.rs index 0dcd9df61f..005c177765 100644 --- a/crates/ra_assists/src/assist_context.rs +++ b/crates/ra_assists/src/assist_context.rs @@ -194,20 +194,30 @@ impl AssistBuilder { pub(crate) fn insert(&mut self, offset: TextSize, text: impl Into) { self.edit.insert(offset, text.into()) } - /// Append specified `text` at the given `offset` + /// Append specified `snippet` at the given `offset` pub(crate) fn insert_snippet( &mut self, _cap: SnippetCap, offset: TextSize, - text: impl Into, + snippet: impl Into, ) { self.is_snippet = true; - self.edit.insert(offset, text.into()) + self.insert(offset, snippet); } /// Replaces specified `range` of text with a given string. pub(crate) fn replace(&mut self, range: TextRange, replace_with: impl Into) { self.edit.replace(range, replace_with.into()) } + /// Replaces specified `range` of text with a given `snippet`. + pub(crate) fn replace_snippet( + &mut self, + _cap: SnippetCap, + range: TextRange, + snippet: impl Into, + ) { + self.is_snippet = true; + self.replace(range, snippet); + } pub(crate) fn replace_ast(&mut self, old: N, new: N) { algo::diff(old.syntax(), new.syntax()).into_text_edit(&mut self.edit) } diff --git a/crates/ra_assists/src/handlers/add_function.rs b/crates/ra_assists/src/handlers/add_function.rs index a0709630d2..24f931a85e 100644 --- a/crates/ra_assists/src/handlers/add_function.rs +++ b/crates/ra_assists/src/handlers/add_function.rs @@ -10,7 +10,11 @@ use ra_syntax::{ }; use rustc_hash::{FxHashMap, FxHashSet}; -use crate::{assist_config::SnippetCap, utils::render_snippet, AssistContext, AssistId, Assists}; +use crate::{ + assist_config::SnippetCap, + utils::{render_snippet, Cursor}, + AssistContext, AssistId, Assists, +}; // Assist: add_function // @@ -81,7 +85,11 @@ struct FunctionTemplate { impl FunctionTemplate { fn to_string(&self, cap: Option) -> String { let f = match cap { - Some(cap) => render_snippet(cap, self.fn_def.syntax(), self.placeholder_expr.syntax()), + Some(cap) => render_snippet( + cap, + self.fn_def.syntax(), + Cursor::Replace(self.placeholder_expr.syntax()), + ), None => self.fn_def.to_string(), }; format!("{}{}{}", self.leading_ws, f, self.trailing_ws) 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 22e1156d2a..d7aa069476 100644 --- a/crates/ra_assists/src/handlers/add_missing_impl_members.rs +++ b/crates/ra_assists/src/handlers/add_missing_impl_members.rs @@ -11,7 +11,7 @@ use ra_syntax::{ use crate::{ assist_context::{AssistContext, Assists}, ast_transform::{self, AstTransform, QualifyPaths, SubstituteTypeParams}, - utils::{get_missing_assoc_items, resolve_target_trait}, + utils::{get_missing_assoc_items, render_snippet, resolve_target_trait, Cursor}, AssistId, }; @@ -45,7 +45,7 @@ enum AddMissingImplMembersMode { // } // // impl Trait for () { -// fn foo(&self) -> u32 { +// $0fn foo(&self) -> u32 { // todo!() // } // @@ -89,7 +89,7 @@ pub(crate) fn add_missing_impl_members(acc: &mut Assists, ctx: &AssistContext) - // impl Trait for () { // Type X = (); // fn foo(&self) {} -// fn bar(&self) {} +// $0fn bar(&self) {} // // } // ``` @@ -147,7 +147,7 @@ fn add_missing_impl_members_inner( } let target = impl_def.syntax().text_range(); - acc.add(AssistId(assist_id), label, target, |edit| { + acc.add(AssistId(assist_id), label, target, |builder| { let n_existing_items = impl_item_list.assoc_items().count(); let source_scope = ctx.sema.scope_for_def(trait_); let target_scope = ctx.sema.scope(impl_item_list.syntax()); @@ -162,13 +162,21 @@ fn add_missing_impl_members_inner( }) .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.assoc_items().nth(n_existing_items).unwrap(); - first_new_item.syntax().text_range().start() - }; + let first_new_item = new_impl_item_list.assoc_items().nth(n_existing_items).unwrap(); - edit.replace_ast(impl_item_list, new_impl_item_list); - edit.set_cursor(cursor_position); + let original_range = impl_item_list.syntax().text_range(); + match ctx.config.snippet_cap { + None => builder.replace(original_range, new_impl_item_list.to_string()), + Some(cap) => builder.replace_snippet( + cap, + original_range, + render_snippet( + cap, + new_impl_item_list.syntax(), + Cursor::Before(first_new_item.syntax()), + ), + ), + }; }) } @@ -222,7 +230,7 @@ struct S; impl Foo for S { fn bar(&self) {} - <|>type Output; + $0type Output; const CONST: usize = 42; fn foo(&self) { todo!() @@ -263,7 +271,7 @@ struct S; impl Foo for S { fn bar(&self) {} - <|>fn foo(&self) { + $0fn foo(&self) { todo!() } @@ -283,7 +291,7 @@ impl Foo for S { <|> }"#, trait Foo { fn foo(&self); } struct S; impl Foo for S { - <|>fn foo(&self) { + $0fn foo(&self) { todo!() } }"#, @@ -302,7 +310,7 @@ impl Foo for S { <|> }"#, trait Foo { fn foo(&self, t: T) -> &T; } struct S; impl Foo for S { - <|>fn foo(&self, t: u32) -> &u32 { + $0fn foo(&self, t: u32) -> &u32 { todo!() } }"#, @@ -321,7 +329,7 @@ impl Foo for S { <|> }"#, trait Foo { fn foo(&self, t: T) -> &T; } struct S; impl Foo for S { - <|>fn foo(&self, t: U) -> &U { + $0fn foo(&self, t: U) -> &U { todo!() } }"#, @@ -340,7 +348,7 @@ impl Foo for S {}<|>"#, trait Foo { fn foo(&self); } struct S; impl Foo for S { - <|>fn foo(&self) { + $0fn foo(&self) { todo!() } }"#, @@ -365,7 +373,7 @@ mod foo { } struct S; impl foo::Foo for S { - <|>fn foo(&self, bar: foo::Bar) { + $0fn foo(&self, bar: foo::Bar) { todo!() } }"#, @@ -390,7 +398,7 @@ mod foo { } struct S; impl foo::Foo for S { - <|>fn foo(&self, bar: foo::Bar) { + $0fn foo(&self, bar: foo::Bar) { todo!() } }"#, @@ -415,7 +423,7 @@ mod foo { } struct S; impl foo::Foo for S { - <|>fn foo(&self, bar: foo::Bar) { + $0fn foo(&self, bar: foo::Bar) { todo!() } }"#, @@ -443,7 +451,7 @@ mod foo { struct Param; struct S; impl foo::Foo for S { - <|>fn foo(&self, bar: Param) { + $0fn foo(&self, bar: Param) { todo!() } }"#, @@ -470,7 +478,7 @@ mod foo { } struct S; impl foo::Foo for S { - <|>fn foo(&self, bar: foo::Bar::Assoc) { + $0fn foo(&self, bar: foo::Bar::Assoc) { todo!() } }"#, @@ -497,7 +505,7 @@ mod foo { } struct S; impl foo::Foo for S { - <|>fn foo(&self, bar: foo::Bar) { + $0fn foo(&self, bar: foo::Bar) { todo!() } }"#, @@ -522,7 +530,7 @@ mod foo { } struct S; impl foo::Foo for S { - <|>fn foo(&self, bar: dyn Fn(u32) -> i32) { + $0fn foo(&self, bar: dyn Fn(u32) -> i32) { todo!() } }"#, @@ -580,7 +588,7 @@ trait Foo { } struct S; impl Foo for S { - <|>type Output; + $0type Output; fn foo(&self) { todo!() } @@ -614,7 +622,7 @@ trait Foo { } struct S; impl Foo for S { - <|>fn valid(some: u32) -> bool { false } + $0fn valid(some: u32) -> bool { false } }"#, ) } @@ -637,7 +645,7 @@ trait Foo { struct S; impl Foo for S { - <|>fn bar(&self, other: &Self) { + $0fn bar(&self, other: &Self) { todo!() } }"#, @@ -662,7 +670,7 @@ trait Foo { struct S; impl Foo for S { - <|>fn bar(&self, this: &T, that: &Self) { + $0fn bar(&self, this: &T, that: &Self) { todo!() } }"#, diff --git a/crates/ra_assists/src/tests/generated.rs b/crates/ra_assists/src/tests/generated.rs index 1d82c245d0..2522ec5df8 100644 --- a/crates/ra_assists/src/tests/generated.rs +++ b/crates/ra_assists/src/tests/generated.rs @@ -150,7 +150,7 @@ trait Trait { impl Trait for () { Type X = (); fn foo(&self) {} - fn bar(&self) {} + $0fn bar(&self) {} } "#####, @@ -180,7 +180,7 @@ trait Trait { } impl Trait for () { - fn foo(&self) -> u32 { + $0fn foo(&self) -> u32 { todo!() } diff --git a/crates/ra_assists/src/utils.rs b/crates/ra_assists/src/utils.rs index 8a26a68082..9af27180bc 100644 --- a/crates/ra_assists/src/utils.rs +++ b/crates/ra_assists/src/utils.rs @@ -15,18 +15,31 @@ use crate::assist_config::SnippetCap; pub(crate) use insert_use::insert_use_statement; -pub(crate) fn render_snippet( - _cap: SnippetCap, - node: &SyntaxNode, - placeholder: &SyntaxNode, -) -> String { - assert!(placeholder.ancestors().any(|it| it == *node)); - let range = placeholder.text_range() - node.text_range().start(); +#[derive(Clone, Copy, Debug)] +pub(crate) enum Cursor<'a> { + Replace(&'a SyntaxNode), + Before(&'a SyntaxNode), +} + +impl<'a> Cursor<'a> { + fn node(self) -> &'a SyntaxNode { + match self { + Cursor::Replace(node) | Cursor::Before(node) => node, + } + } +} + +pub(crate) fn render_snippet(_cap: SnippetCap, node: &SyntaxNode, cursor: Cursor) -> String { + assert!(cursor.node().ancestors().any(|it| it == *node)); + let range = cursor.node().text_range() - node.text_range().start(); let range: ops::Range = range.into(); - let mut placeholder = placeholder.to_string(); + let mut placeholder = cursor.node().to_string(); escape(&mut placeholder); - let tab_stop = format!("${{0:{}}}", placeholder); + let tab_stop = match cursor { + Cursor::Replace(placeholder) => format!("${{0:{}}}", placeholder), + Cursor::Before(placeholder) => format!("$0{}", placeholder), + }; let mut buf = node.to_string(); buf.replace_range(range, &tab_stop); diff --git a/docs/user/assists.md b/docs/user/assists.md index 10ab67b2e7..b687330f33 100644 --- a/docs/user/assists.md +++ b/docs/user/assists.md @@ -146,7 +146,7 @@ trait Trait { impl Trait for () { Type X = (); fn foo(&self) {} - fn bar(&self) {} + $0fn bar(&self) {} } ``` @@ -175,7 +175,7 @@ trait Trait { } impl Trait for () { - fn foo(&self) -> u32 { + $0fn foo(&self) -> u32 { todo!() } From 767d169a2ae543f28544e85e15bac1b6aa1cab23 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 20 May 2020 02:07:21 +0200 Subject: [PATCH 42/70] Better cursor placement when adding impl members --- .../src/handlers/add_missing_impl_members.rs | 84 ++++++++++--------- crates/ra_assists/src/tests/generated.rs | 4 +- docs/user/assists.md | 4 +- 3 files changed, 50 insertions(+), 42 deletions(-) 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 d7aa069476..abacd4065f 100644 --- a/crates/ra_assists/src/handlers/add_missing_impl_members.rs +++ b/crates/ra_assists/src/handlers/add_missing_impl_members.rs @@ -45,8 +45,8 @@ enum AddMissingImplMembersMode { // } // // impl Trait for () { -// $0fn foo(&self) -> u32 { -// todo!() +// fn foo(&self) -> u32 { +// ${0:todo!()} // } // // } @@ -167,15 +167,23 @@ fn add_missing_impl_members_inner( let original_range = impl_item_list.syntax().text_range(); match ctx.config.snippet_cap { None => builder.replace(original_range, new_impl_item_list.to_string()), - Some(cap) => builder.replace_snippet( - cap, - original_range, - render_snippet( + Some(cap) => { + let mut cursor = Cursor::Before(first_new_item.syntax()); + let placeholder; + if let ast::AssocItem::FnDef(func) = &first_new_item { + if let Some(m) = func.syntax().descendants().find_map(ast::MacroCall::cast) { + if m.syntax().text() == "todo!()" { + placeholder = m; + cursor = Cursor::Replace(placeholder.syntax()); + } + } + } + builder.replace_snippet( cap, - new_impl_item_list.syntax(), - Cursor::Before(first_new_item.syntax()), - ), - ), + original_range, + render_snippet(cap, new_impl_item_list.syntax(), cursor), + ) + } }; }) } @@ -271,8 +279,8 @@ struct S; impl Foo for S { fn bar(&self) {} - $0fn foo(&self) { - todo!() + fn foo(&self) { + ${0:todo!()} } }"#, @@ -291,8 +299,8 @@ impl Foo for S { <|> }"#, trait Foo { fn foo(&self); } struct S; impl Foo for S { - $0fn foo(&self) { - todo!() + fn foo(&self) { + ${0:todo!()} } }"#, ); @@ -310,8 +318,8 @@ impl Foo for S { <|> }"#, trait Foo { fn foo(&self, t: T) -> &T; } struct S; impl Foo for S { - $0fn foo(&self, t: u32) -> &u32 { - todo!() + fn foo(&self, t: u32) -> &u32 { + ${0:todo!()} } }"#, ); @@ -329,8 +337,8 @@ impl Foo for S { <|> }"#, trait Foo { fn foo(&self, t: T) -> &T; } struct S; impl Foo for S { - $0fn foo(&self, t: U) -> &U { - todo!() + fn foo(&self, t: U) -> &U { + ${0:todo!()} } }"#, ); @@ -348,8 +356,8 @@ impl Foo for S {}<|>"#, trait Foo { fn foo(&self); } struct S; impl Foo for S { - $0fn foo(&self) { - todo!() + fn foo(&self) { + ${0:todo!()} } }"#, ) @@ -373,8 +381,8 @@ mod foo { } struct S; impl foo::Foo for S { - $0fn foo(&self, bar: foo::Bar) { - todo!() + fn foo(&self, bar: foo::Bar) { + ${0:todo!()} } }"#, ); @@ -398,8 +406,8 @@ mod foo { } struct S; impl foo::Foo for S { - $0fn foo(&self, bar: foo::Bar) { - todo!() + fn foo(&self, bar: foo::Bar) { + ${0:todo!()} } }"#, ); @@ -423,8 +431,8 @@ mod foo { } struct S; impl foo::Foo for S { - $0fn foo(&self, bar: foo::Bar) { - todo!() + fn foo(&self, bar: foo::Bar) { + ${0:todo!()} } }"#, ); @@ -451,8 +459,8 @@ mod foo { struct Param; struct S; impl foo::Foo for S { - $0fn foo(&self, bar: Param) { - todo!() + fn foo(&self, bar: Param) { + ${0:todo!()} } }"#, ); @@ -478,8 +486,8 @@ mod foo { } struct S; impl foo::Foo for S { - $0fn foo(&self, bar: foo::Bar::Assoc) { - todo!() + fn foo(&self, bar: foo::Bar::Assoc) { + ${0:todo!()} } }"#, ); @@ -505,8 +513,8 @@ mod foo { } struct S; impl foo::Foo for S { - $0fn foo(&self, bar: foo::Bar) { - todo!() + fn foo(&self, bar: foo::Bar) { + ${0:todo!()} } }"#, ); @@ -530,8 +538,8 @@ mod foo { } struct S; impl foo::Foo for S { - $0fn foo(&self, bar: dyn Fn(u32) -> i32) { - todo!() + fn foo(&self, bar: dyn Fn(u32) -> i32) { + ${0:todo!()} } }"#, ); @@ -645,8 +653,8 @@ trait Foo { struct S; impl Foo for S { - $0fn bar(&self, other: &Self) { - todo!() + fn bar(&self, other: &Self) { + ${0:todo!()} } }"#, ) @@ -670,8 +678,8 @@ trait Foo { struct S; impl Foo for S { - $0fn bar(&self, this: &T, that: &Self) { - todo!() + fn bar(&self, this: &T, that: &Self) { + ${0:todo!()} } }"#, ) diff --git a/crates/ra_assists/src/tests/generated.rs b/crates/ra_assists/src/tests/generated.rs index 2522ec5df8..3808aded1f 100644 --- a/crates/ra_assists/src/tests/generated.rs +++ b/crates/ra_assists/src/tests/generated.rs @@ -180,8 +180,8 @@ trait Trait { } impl Trait for () { - $0fn foo(&self) -> u32 { - todo!() + fn foo(&self) -> u32 { + ${0:todo!()} } } diff --git a/docs/user/assists.md b/docs/user/assists.md index b687330f33..f329fcc107 100644 --- a/docs/user/assists.md +++ b/docs/user/assists.md @@ -175,8 +175,8 @@ trait Trait { } impl Trait for () { - $0fn foo(&self) -> u32 { - todo!() + fn foo(&self) -> u32 { + ${0:todo!()} } } From 63ffc1773399129b936cdabba241923725ba7553 Mon Sep 17 00:00:00 2001 From: Aaron Loucks Date: Tue, 19 May 2020 20:29:49 -0400 Subject: [PATCH 43/70] Use a flat play icon instead of the blue emoji with test code lens --- crates/rust-analyzer/src/main_loop/handlers.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs index 4ff8fa69e9..be6a0aece9 100644 --- a/crates/rust-analyzer/src/main_loop/handlers.rs +++ b/crates/rust-analyzer/src/main_loop/handlers.rs @@ -802,11 +802,11 @@ pub fn handle_code_lens( for runnable in world.analysis().runnables(file_id)? { let (run_title, debugee) = match &runnable.kind { RunnableKind::Test { .. } | RunnableKind::TestMod { .. } => { - ("▶️\u{fe0e}Run Test", true) + ("▶\u{fe0e} Run Test", true) } RunnableKind::DocTest { .. } => { // cargo does not support -no-run for doctests - ("▶️\u{fe0e}Run Doctest", false) + ("▶\u{fe0e} Run Doctest", false) } RunnableKind::Bench { .. } => { // Nothing wrong with bench debugging From d264d7b9f264c31fe2b1ea7e623611a816979789 Mon Sep 17 00:00:00 2001 From: vsrs Date: Wed, 20 May 2020 09:42:00 +0300 Subject: [PATCH 44/70] Debug lens fix for a binary. --- editors/code/src/cargo.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/editors/code/src/cargo.ts b/editors/code/src/cargo.ts index 28c7de992b..6a41873d00 100644 --- a/editors/code/src/cargo.ts +++ b/editors/code/src/cargo.ts @@ -51,10 +51,14 @@ export class Cargo { // arguments for a runnable from the quick pick should be updated. // see crates\rust-analyzer\src\main_loop\handlers.rs, handle_code_lens - if (cargoArgs[0] === "run") { - cargoArgs[0] = "build"; - } else if (cargoArgs.indexOf("--no-run") === -1) { - cargoArgs.push("--no-run"); + switch (cargoArgs[0]) { + case "run": cargoArgs[0] = "build"; break; + case "test": { + if (cargoArgs.indexOf("--no-run") === -1) { + cargoArgs.push("--no-run"); + } + break; + } } let artifacts = await this.artifactsFromArgs(cargoArgs); From 9b2bd022dc6fbe13356622ada5b6499f012cb5ae Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 20 May 2020 10:17:46 +0200 Subject: [PATCH 45/70] Snippetify add_new --- crates/ra_assists/src/handlers/add_new.rs | 56 +++++++++++------------ crates/ra_assists/src/tests/generated.rs | 2 +- docs/user/assists.md | 2 +- 3 files changed, 29 insertions(+), 31 deletions(-) diff --git a/crates/ra_assists/src/handlers/add_new.rs b/crates/ra_assists/src/handlers/add_new.rs index fe7451dcfd..837aa83774 100644 --- a/crates/ra_assists/src/handlers/add_new.rs +++ b/crates/ra_assists/src/handlers/add_new.rs @@ -3,7 +3,7 @@ use ra_syntax::{ ast::{ self, AstNode, NameOwner, StructKind, TypeAscriptionOwner, TypeParamsOwner, VisibilityOwner, }, - TextSize, T, + T, }; use stdx::{format_to, SepBy}; @@ -25,7 +25,7 @@ use crate::{AssistContext, AssistId, Assists}; // } // // impl Ctx { -// fn new(data: T) -> Self { Self { data } } +// fn $0new(data: T) -> Self { Self { data } } // } // // ``` @@ -42,31 +42,26 @@ pub(crate) fn add_new(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { let impl_def = find_struct_impl(&ctx, &strukt)?; let target = strukt.syntax().text_range(); - acc.add(AssistId("add_new"), "Add default constructor", target, |edit| { + acc.add(AssistId("add_new"), "Add default constructor", target, |builder| { let mut buf = String::with_capacity(512); if impl_def.is_some() { buf.push('\n'); } - let vis = strukt.visibility().map(|v| format!("{} ", v)); - let vis = vis.as_deref().unwrap_or(""); + let vis = strukt.visibility().map_or(String::new(), |v| format!("{} ", v)); let params = field_list .fields() .filter_map(|f| { - Some(format!( - "{}: {}", - f.name()?.syntax().text(), - f.ascribed_type()?.syntax().text() - )) + Some(format!("{}: {}", f.name()?.syntax(), f.ascribed_type()?.syntax())) }) .sep_by(", "); let fields = field_list.fields().filter_map(|f| f.name()).sep_by(", "); format_to!(buf, " {}fn new({}) -> Self {{ Self {{ {} }} }}", vis, params, fields); - let (start_offset, end_offset) = impl_def + let start_offset = impl_def .and_then(|impl_def| { buf.push('\n'); let start = impl_def @@ -76,17 +71,20 @@ pub(crate) fn add_new(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { .text_range() .end(); - Some((start, TextSize::of("\n"))) + Some(start) }) .unwrap_or_else(|| { buf = generate_impl_text(&strukt, &buf); - let start = strukt.syntax().text_range().end(); - - (start, TextSize::of("\n}\n")) + strukt.syntax().text_range().end() }); - edit.set_cursor(start_offset + TextSize::of(&buf) - end_offset); - edit.insert(start_offset, buf); + match ctx.config.snippet_cap { + None => builder.insert(start_offset, buf), + Some(cap) => { + buf = buf.replace("fn new", "fn $0new"); + builder.insert_snippet(cap, start_offset, buf); + } + } }) } @@ -191,7 +189,7 @@ mod tests { "struct Foo {} impl Foo { - fn new() -> Self { Self { } }<|> + fn $0new() -> Self { Self { } } } ", ); @@ -201,7 +199,7 @@ impl Foo { "struct Foo {} impl Foo { - fn new() -> Self { Self { } }<|> + fn $0new() -> Self { Self { } } } ", ); @@ -211,7 +209,7 @@ impl Foo { "struct Foo<'a, T: Foo<'a>> {} impl<'a, T: Foo<'a>> Foo<'a, T> { - fn new() -> Self { Self { } }<|> + fn $0new() -> Self { Self { } } } ", ); @@ -221,7 +219,7 @@ impl<'a, T: Foo<'a>> Foo<'a, T> { "struct Foo { baz: String } impl Foo { - fn new(baz: String) -> Self { Self { baz } }<|> + fn $0new(baz: String) -> Self { Self { baz } } } ", ); @@ -231,7 +229,7 @@ impl Foo { "struct Foo { baz: String, qux: Vec } impl Foo { - fn new(baz: String, qux: Vec) -> Self { Self { baz, qux } }<|> + fn $0new(baz: String, qux: Vec) -> Self { Self { baz, qux } } } ", ); @@ -243,7 +241,7 @@ impl Foo { "struct Foo { pub baz: String, pub qux: Vec } impl Foo { - fn new(baz: String, qux: Vec) -> Self { Self { baz, qux } }<|> + fn $0new(baz: String, qux: Vec) -> Self { Self { baz, qux } } } ", ); @@ -258,7 +256,7 @@ impl Foo {} "struct Foo {} impl Foo { - fn new() -> Self { Self { } }<|> + fn $0new() -> Self { Self { } } } ", ); @@ -273,7 +271,7 @@ impl Foo { "struct Foo {} impl Foo { - fn new() -> Self { Self { } }<|> + fn $0new() -> Self { Self { } } fn qux(&self) {} } @@ -294,7 +292,7 @@ impl Foo { "struct Foo {} impl Foo { - fn new() -> Self { Self { } }<|> + fn $0new() -> Self { Self { } } fn qux(&self) {} fn baz() -> i32 { @@ -311,7 +309,7 @@ impl Foo { "pub struct Foo {} impl Foo { - pub fn new() -> Self { Self { } }<|> + pub fn $0new() -> Self { Self { } } } ", ); @@ -321,7 +319,7 @@ impl Foo { "pub(crate) struct Foo {} impl Foo { - pub(crate) fn new() -> Self { Self { } }<|> + pub(crate) fn $0new() -> Self { Self { } } } ", ); @@ -414,7 +412,7 @@ pub struct Source { } impl Source { - pub fn new(file_id: HirFileId, ast: T) -> Self { Self { file_id, ast } }<|> + pub fn $0new(file_id: HirFileId, ast: T) -> Self { Self { file_id, ast } } pub fn map U, U>(self, f: F) -> Source { Source { file_id: self.file_id, ast: f(self.ast) } diff --git a/crates/ra_assists/src/tests/generated.rs b/crates/ra_assists/src/tests/generated.rs index 3808aded1f..d860cfefcc 100644 --- a/crates/ra_assists/src/tests/generated.rs +++ b/crates/ra_assists/src/tests/generated.rs @@ -204,7 +204,7 @@ struct Ctx { } impl Ctx { - fn new(data: T) -> Self { Self { data } } + fn $0new(data: T) -> Self { Self { data } } } "#####, diff --git a/docs/user/assists.md b/docs/user/assists.md index f329fcc107..03c01d6c05 100644 --- a/docs/user/assists.md +++ b/docs/user/assists.md @@ -198,7 +198,7 @@ struct Ctx { } impl Ctx { - fn new(data: T) -> Self { Self { data } } + fn $0new(data: T) -> Self { Self { data } } } ``` From 45e343a0ef6f4caf078811d0664907610f085d54 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 20 May 2020 10:20:21 +0200 Subject: [PATCH 46/70] Minor --- crates/ra_assists/src/handlers/auto_import.rs | 7 ++++++- .../src/handlers/replace_qualified_name_with_use.rs | 2 +- crates/ra_assists/src/utils/insert_use.rs | 6 +++--- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/crates/ra_assists/src/handlers/auto_import.rs b/crates/ra_assists/src/handlers/auto_import.rs index 78d23150d3..f6d25579e0 100644 --- a/crates/ra_assists/src/handlers/auto_import.rs +++ b/crates/ra_assists/src/handlers/auto_import.rs @@ -50,7 +50,12 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()> format!("Import `{}`", &import), range, |builder| { - insert_use_statement(&auto_import_assets.syntax_under_caret, &import, ctx, builder); + insert_use_statement( + &auto_import_assets.syntax_under_caret, + &import, + ctx, + builder.text_edit_builder(), + ); }, ); } diff --git a/crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs b/crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs index 1a81d8a0e0..d9f84208df 100644 --- a/crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs +++ b/crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs @@ -39,7 +39,7 @@ pub(crate) fn replace_qualified_name_with_use( target, |builder| { let path_to_import = hir_path.mod_path().clone(); - insert_use_statement(path.syntax(), &path_to_import, ctx, builder); + insert_use_statement(path.syntax(), &path_to_import, ctx, builder.text_edit_builder()); if let Some(last) = path.segment() { // Here we are assuming the assist will provide a correct use statement diff --git a/crates/ra_assists/src/utils/insert_use.rs b/crates/ra_assists/src/utils/insert_use.rs index 1214e3cd47..0ee43482f7 100644 --- a/crates/ra_assists/src/utils/insert_use.rs +++ b/crates/ra_assists/src/utils/insert_use.rs @@ -11,7 +11,7 @@ use ra_syntax::{ }; use ra_text_edit::TextEditBuilder; -use crate::assist_context::{AssistBuilder, AssistContext}; +use crate::assist_context::AssistContext; /// Creates and inserts a use statement for the given path to import. /// The use statement is inserted in the scope most appropriate to the @@ -21,7 +21,7 @@ pub(crate) fn insert_use_statement( position: &SyntaxNode, path_to_import: &ModPath, ctx: &AssistContext, - builder: &mut AssistBuilder, + builder: &mut TextEditBuilder, ) { let target = path_to_import.to_string().split("::").map(SmolStr::new).collect::>(); let container = ctx.sema.ancestors_with_macros(position.clone()).find_map(|n| { @@ -33,7 +33,7 @@ pub(crate) fn insert_use_statement( if let Some(container) = container { let action = best_action_for_target(container, position.clone(), &target); - make_assist(&action, &target, builder.text_edit_builder()); + make_assist(&action, &target, builder); } } From 36a5ca9a843aa807b8152d4c5631be605fc80e80 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 20 May 2020 10:26:14 +0200 Subject: [PATCH 47/70] Minor --- crates/ra_assists/src/handlers/change_return_type_to_result.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/ra_assists/src/handlers/change_return_type_to_result.rs b/crates/ra_assists/src/handlers/change_return_type_to_result.rs index 5c907097e5..c2ea87dd74 100644 --- a/crates/ra_assists/src/handlers/change_return_type_to_result.rs +++ b/crates/ra_assists/src/handlers/change_return_type_to_result.rs @@ -19,8 +19,7 @@ use crate::{AssistContext, AssistId, Assists}; // fn foo() -> Result { Ok(42i32) } // ``` pub(crate) fn change_return_type_to_result(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { - let fn_def = ctx.find_node_at_offset::(); - let fn_def = &mut fn_def?; + let fn_def = ctx.find_node_at_offset::()?; let ret_type = &fn_def.ret_type()?.type_ref()?; if ret_type.syntax().text().to_string().starts_with("Result<") { return None; From 2e74df4e2b79955a44dee79811e06b8fabff4da2 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 20 May 2020 10:28:58 +0200 Subject: [PATCH 48/70] Cleanup --- .../handlers/change_return_type_to_result.rs | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/crates/ra_assists/src/handlers/change_return_type_to_result.rs b/crates/ra_assists/src/handlers/change_return_type_to_result.rs index c2ea87dd74..ae8237c5de 100644 --- a/crates/ra_assists/src/handlers/change_return_type_to_result.rs +++ b/crates/ra_assists/src/handlers/change_return_type_to_result.rs @@ -19,23 +19,21 @@ use crate::{AssistContext, AssistId, Assists}; // fn foo() -> Result { Ok(42i32) } // ``` pub(crate) fn change_return_type_to_result(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { - let fn_def = ctx.find_node_at_offset::()?; - let ret_type = &fn_def.ret_type()?.type_ref()?; - if ret_type.syntax().text().to_string().starts_with("Result<") { + let ret_type = ctx.find_node_at_offset::()?; + // FIXME: extend to lambdas as well + let fn_def = ret_type.syntax().parent().and_then(ast::FnDef::cast)?; + + let type_ref = &ret_type.type_ref()?; + if type_ref.syntax().text().to_string().starts_with("Result<") { return None; } let block_expr = &fn_def.body()?; - let cursor_in_ret_type = - fn_def.ret_type()?.syntax().text_range().contains_range(ctx.frange.range); - if !cursor_in_ret_type { - return None; - } acc.add( AssistId("change_return_type_to_result"), "Change return type to Result", - ret_type.syntax().text_range(), + type_ref.syntax().text_range(), |edit| { let mut tail_return_expr_collector = TailReturnCollector::new(); tail_return_expr_collector.collect_jump_exprs(block_expr, false); @@ -44,10 +42,10 @@ pub(crate) fn change_return_type_to_result(acc: &mut Assists, ctx: &AssistContex for ret_expr_arg in tail_return_expr_collector.exprs_to_wrap { edit.replace_node_and_indent(&ret_expr_arg, format!("Ok({})", ret_expr_arg)); } - edit.replace_node_and_indent(ret_type.syntax(), format!("Result<{}, >", ret_type)); + edit.replace_node_and_indent(type_ref.syntax(), format!("Result<{}, >", type_ref)); - if let Some(node_start) = result_insertion_offset(&ret_type) { - edit.set_cursor(node_start + TextSize::of(&format!("Result<{}, ", ret_type))); + if let Some(node_start) = result_insertion_offset(&type_ref) { + edit.set_cursor(node_start + TextSize::of(&format!("Result<{}, ", type_ref))); } }, ) From d790a443f396ad53037a3f6ba794b6f4df5b3748 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 20 May 2020 10:30:18 +0200 Subject: [PATCH 49/70] wip --- .../src/handlers/change_return_type_to_result.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/crates/ra_assists/src/handlers/change_return_type_to_result.rs b/crates/ra_assists/src/handlers/change_return_type_to_result.rs index ae8237c5de..c9f909f9c5 100644 --- a/crates/ra_assists/src/handlers/change_return_type_to_result.rs +++ b/crates/ra_assists/src/handlers/change_return_type_to_result.rs @@ -34,18 +34,22 @@ pub(crate) fn change_return_type_to_result(acc: &mut Assists, ctx: &AssistContex AssistId("change_return_type_to_result"), "Change return type to Result", type_ref.syntax().text_range(), - |edit| { + |builder| { let mut tail_return_expr_collector = TailReturnCollector::new(); tail_return_expr_collector.collect_jump_exprs(block_expr, false); tail_return_expr_collector.collect_tail_exprs(block_expr); for ret_expr_arg in tail_return_expr_collector.exprs_to_wrap { - edit.replace_node_and_indent(&ret_expr_arg, format!("Ok({})", ret_expr_arg)); + builder.replace_node_and_indent(&ret_expr_arg, format!("Ok({})", ret_expr_arg)); } - edit.replace_node_and_indent(type_ref.syntax(), format!("Result<{}, >", type_ref)); + match ctx.config.snippet_cap { + Some(_) => {} + None => {} + } + builder.replace_node_and_indent(type_ref.syntax(), format!("Result<{}, >", type_ref)); if let Some(node_start) = result_insertion_offset(&type_ref) { - edit.set_cursor(node_start + TextSize::of(&format!("Result<{}, ", type_ref))); + builder.set_cursor(node_start + TextSize::of(&format!("Result<{}, ", type_ref))); } }, ) From d8881d98d3d1c88335c5f00328f1f2fc0100dd62 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 20 May 2020 10:51:48 +0200 Subject: [PATCH 50/70] Fix Some|None order in fill_match_arms --- .../src/handlers/fill_match_arms.rs | 44 +++++++++++++++++-- crates/ra_assists/src/marks.rs | 1 + crates/ra_assists/src/utils.rs | 35 ++++++++++++--- 3 files changed, 69 insertions(+), 11 deletions(-) diff --git a/crates/ra_assists/src/handlers/fill_match_arms.rs b/crates/ra_assists/src/handlers/fill_match_arms.rs index 13c1e7e801..b57ff75aed 100644 --- a/crates/ra_assists/src/handlers/fill_match_arms.rs +++ b/crates/ra_assists/src/handlers/fill_match_arms.rs @@ -4,8 +4,9 @@ use hir::{Adt, HasSource, ModuleDef, Semantics}; use itertools::Itertools; use ra_ide_db::RootDatabase; use ra_syntax::ast::{self, make, AstNode, MatchArm, NameOwner, Pat}; +use test_utils::tested_by; -use crate::{AssistContext, AssistId, Assists}; +use crate::{utils::FamousDefs, AssistContext, AssistId, Assists}; // Assist: fill_match_arms // @@ -49,12 +50,18 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option< let missing_arms: Vec = if let Some(enum_def) = resolve_enum_def(&ctx.sema, &expr) { let variants = enum_def.variants(ctx.db); - variants + let mut variants = variants .into_iter() .filter_map(|variant| build_pat(ctx.db, module, variant)) .filter(|variant_pat| is_variant_missing(&mut arms, variant_pat)) .map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block())) - .collect() + .collect::>(); + if Some(enum_def) == FamousDefs(&ctx.sema, module.krate()).core_option_Option() { + // Match `Some` variant first. + tested_by!(option_order); + variants.reverse() + } + variants } else if let Some(enum_defs) = resolve_tuple_of_enum_def(&ctx.sema, &expr) { // Partial fill not currently supported for tuple of enums. if !arms.is_empty() { @@ -167,9 +174,13 @@ fn build_pat(db: &RootDatabase, module: hir::Module, var: hir::EnumVariant) -> O #[cfg(test)] mod tests { - use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target}; + use crate::{ + tests::{check_assist, check_assist_not_applicable, check_assist_target}, + utils::FamousDefs, + }; use super::fill_match_arms; + use test_utils::covers; #[test] fn all_match_arms_provided() { @@ -736,4 +747,29 @@ mod tests { "#, ); } + + #[test] + fn option_order() { + covers!(option_order); + let before = r#" +fn foo(opt: Option) { + match opt<|> { + } +}"#; + let before = + &format!("//- main.rs crate:main deps:core\n{}{}", before, FamousDefs::FIXTURE); + + check_assist( + fill_match_arms, + before, + r#" +fn foo(opt: Option) { + match <|>opt { + Some(_) => {} + None => {} + } +} +"#, + ); + } } diff --git a/crates/ra_assists/src/marks.rs b/crates/ra_assists/src/marks.rs index d579e627f0..722f3c6a4b 100644 --- a/crates/ra_assists/src/marks.rs +++ b/crates/ra_assists/src/marks.rs @@ -1,6 +1,7 @@ //! See test_utils/src/marks.rs test_utils::marks![ + option_order introduce_var_in_comment_is_not_applicable test_introduce_var_expr_stmt test_introduce_var_last_expr diff --git a/crates/ra_assists/src/utils.rs b/crates/ra_assists/src/utils.rs index 9af27180bc..0038a9764b 100644 --- a/crates/ra_assists/src/utils.rs +++ b/crates/ra_assists/src/utils.rs @@ -3,7 +3,7 @@ pub(crate) mod insert_use; use std::{iter, ops}; -use hir::{Adt, Crate, Semantics, Trait, Type}; +use hir::{Adt, Crate, Enum, ScopeDef, Semantics, Trait, Type}; use ra_ide_db::RootDatabase; use ra_syntax::{ ast::{self, make, NameOwner}, @@ -200,13 +200,19 @@ impl FamousDefs<'_, '_> { #[cfg(test)] pub(crate) const FIXTURE: &'static str = r#" //- /libcore.rs crate:core -pub mod convert{ +pub mod convert { pub trait From { fn from(T) -> Self; } } -pub mod prelude { pub use crate::convert::From } +pub mod option { + pub enum Option { None, Some(T)} +} + +pub mod prelude { + pub use crate::{convert::From, option::Option::{self, *}}; +} #[prelude_import] pub use prelude::*; "#; @@ -215,7 +221,25 @@ pub use prelude::*; self.find_trait("core:convert:From") } + pub(crate) fn core_option_Option(&self) -> Option { + self.find_enum("core:option:Option") + } + fn find_trait(&self, path: &str) -> Option { + match self.find_def(path)? { + hir::ScopeDef::ModuleDef(hir::ModuleDef::Trait(it)) => Some(it), + _ => None, + } + } + + fn find_enum(&self, path: &str) -> Option { + match self.find_def(path)? { + hir::ScopeDef::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Enum(it))) => Some(it), + _ => None, + } + } + + fn find_def(&self, path: &str) -> Option { let db = self.0.db; let mut path = path.split(':'); let trait_ = path.next_back()?; @@ -240,9 +264,6 @@ pub use prelude::*; } let def = module.scope(db, None).into_iter().find(|(name, _def)| &name.to_string() == trait_)?.1; - match def { - hir::ScopeDef::ModuleDef(hir::ModuleDef::Trait(it)) => Some(it), - _ => None, - } + Some(def) } } From 33e111483fbc80c017037e0b158ee652ed41b3e8 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 20 May 2020 11:10:15 +0200 Subject: [PATCH 51/70] Use snippets in change_return_type_to_result --- .../handlers/change_return_type_to_result.rs | 81 ++++++++----------- crates/ra_assists/src/tests/generated.rs | 2 +- docs/user/assists.md | 2 +- 3 files changed, 37 insertions(+), 48 deletions(-) diff --git a/crates/ra_assists/src/handlers/change_return_type_to_result.rs b/crates/ra_assists/src/handlers/change_return_type_to_result.rs index c9f909f9c5..c6baa0a57c 100644 --- a/crates/ra_assists/src/handlers/change_return_type_to_result.rs +++ b/crates/ra_assists/src/handlers/change_return_type_to_result.rs @@ -1,8 +1,6 @@ use ra_syntax::{ ast::{self, BlockExpr, Expr, LoopBodyOwner}, - AstNode, - SyntaxKind::{COMMENT, WHITESPACE}, - SyntaxNode, TextSize, + AstNode, SyntaxNode, }; use crate::{AssistContext, AssistId, Assists}; @@ -16,7 +14,7 @@ use crate::{AssistContext, AssistId, Assists}; // ``` // -> // ``` -// fn foo() -> Result { Ok(42i32) } +// fn foo() -> Result { Ok(42i32) } // ``` pub(crate) fn change_return_type_to_result(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { let ret_type = ctx.find_node_at_offset::()?; @@ -42,14 +40,14 @@ pub(crate) fn change_return_type_to_result(acc: &mut Assists, ctx: &AssistContex for ret_expr_arg in tail_return_expr_collector.exprs_to_wrap { builder.replace_node_and_indent(&ret_expr_arg, format!("Ok({})", ret_expr_arg)); } - match ctx.config.snippet_cap { - Some(_) => {} - None => {} - } - builder.replace_node_and_indent(type_ref.syntax(), format!("Result<{}, >", type_ref)); - if let Some(node_start) = result_insertion_offset(&type_ref) { - builder.set_cursor(node_start + TextSize::of(&format!("Result<{}, ", type_ref))); + match ctx.config.snippet_cap { + Some(cap) => { + let snippet = format!("Result<{}, ${{0:_}}>", type_ref); + builder.replace_snippet(cap, type_ref.syntax().text_range(), snippet) + } + None => builder + .replace(type_ref.syntax().text_range(), format!("Result<{}, _>", type_ref)), } }, ) @@ -251,17 +249,8 @@ fn get_tail_expr_from_block(expr: &Expr) -> Option> { } } -fn result_insertion_offset(ret_type: &ast::TypeRef) -> Option { - let non_ws_child = ret_type - .syntax() - .children_with_tokens() - .find(|it| it.kind() != COMMENT && it.kind() != WHITESPACE)?; - Some(non_ws_child.text_range().start()) -} - #[cfg(test)] mod tests { - use crate::tests::{check_assist, check_assist_not_applicable}; use super::*; @@ -274,7 +263,7 @@ mod tests { let test = "test"; return 42i32; }"#, - r#"fn foo() -> Result> { + r#"fn foo() -> Result { let test = "test"; return Ok(42i32); }"#, @@ -289,7 +278,7 @@ mod tests { let test = "test"; return 42i32; }"#, - r#"fn foo() -> Result> { + r#"fn foo() -> Result { let test = "test"; return Ok(42i32); }"#, @@ -315,7 +304,7 @@ mod tests { let test = "test"; return 42i32; }"#, - r#"fn foo() -> Result> { + r#"fn foo() -> Result { let test = "test"; return Ok(42i32); }"#, @@ -330,7 +319,7 @@ mod tests { let test = "test"; 42i32 }"#, - r#"fn foo() -> Result> { + r#"fn foo() -> Result { let test = "test"; Ok(42i32) }"#, @@ -344,7 +333,7 @@ mod tests { r#"fn foo() -> i32<|> { 42i32 }"#, - r#"fn foo() -> Result> { + r#"fn foo() -> Result { Ok(42i32) }"#, ); @@ -360,7 +349,7 @@ mod tests { 24i32 } }"#, - r#"fn foo() -> Result> { + r#"fn foo() -> Result { if true { Ok(42i32) } else { @@ -385,7 +374,7 @@ mod tests { 24i32 } }"#, - r#"fn foo() -> Result> { + r#"fn foo() -> Result { if true { if false { Ok(1) @@ -414,7 +403,7 @@ mod tests { 24i32.await } }"#, - r#"async fn foo() -> Result> { + r#"async fn foo() -> Result { if true { if false { Ok(1.await) @@ -435,7 +424,7 @@ mod tests { r#"fn foo() -> [i32;<|> 3] { [1, 2, 3] }"#, - r#"fn foo() -> Result<[i32; 3], <|>> { + r#"fn foo() -> Result<[i32; 3], ${0:_}> { Ok([1, 2, 3]) }"#, ); @@ -456,7 +445,7 @@ mod tests { 24 as i32 } }"#, - r#"fn foo() -> Result> { + r#"fn foo() -> Result { if true { if false { Ok(1 as i32) @@ -481,7 +470,7 @@ mod tests { _ => 24i32, } }"#, - r#"fn foo() -> Result> { + r#"fn foo() -> Result { let my_var = 5; match my_var { 5 => Ok(42i32), @@ -504,7 +493,7 @@ mod tests { my_var }"#, - r#"fn foo() -> Result> { + r#"fn foo() -> Result { let my_var = 5; loop { println!("test"); @@ -527,7 +516,7 @@ mod tests { my_var }"#, - r#"fn foo() -> Result> { + r#"fn foo() -> Result { let my_var = let x = loop { break 1; }; @@ -550,7 +539,7 @@ mod tests { res }"#, - r#"fn foo() -> Result> { + r#"fn foo() -> Result { let my_var = 5; let res = match my_var { 5 => 42i32, @@ -573,7 +562,7 @@ mod tests { res }"#, - r#"fn foo() -> Result> { + r#"fn foo() -> Result { let my_var = 5; let res = if my_var == 5 { 42i32 @@ -609,7 +598,7 @@ mod tests { }, } }"#, - r#"fn foo() -> Result> { + r#"fn foo() -> Result { let my_var = 5; match my_var { 5 => { @@ -642,7 +631,7 @@ mod tests { } 53i32 }"#, - r#"fn foo() -> Result> { + r#"fn foo() -> Result { let test = "test"; if test == "test" { return Ok(24i32); @@ -673,7 +662,7 @@ mod tests { the_field }"#, - r#"fn foo(the_field: u32) -> Result> { + r#"fn foo(the_field: u32) -> Result { let true_closure = || { return true; }; @@ -712,7 +701,7 @@ mod tests { t.unwrap_or_else(|| the_field) }"#, - r#"fn foo(the_field: u32) -> Result> { + r#"fn foo(the_field: u32) -> Result { let true_closure = || { return true; }; @@ -750,7 +739,7 @@ mod tests { i += 1; } }"#, - r#"fn foo() -> Result> { + r#"fn foo() -> Result { let test = "test"; if test == "test" { return Ok(24i32); @@ -782,7 +771,7 @@ mod tests { } } }"#, - r#"fn foo() -> Result> { + r#"fn foo() -> Result { let test = "test"; if test == "test" { return Ok(24i32); @@ -820,7 +809,7 @@ mod tests { } } }"#, - r#"fn foo() -> Result> { + r#"fn foo() -> Result { let test = "test"; let other = 5; if test == "test" { @@ -861,7 +850,7 @@ mod tests { the_field }"#, - r#"fn foo(the_field: u32) -> Result> { + r#"fn foo(the_field: u32) -> Result { if the_field < 5 { let mut i = 0; loop { @@ -895,7 +884,7 @@ mod tests { the_field }"#, - r#"fn foo(the_field: u32) -> Result> { + r#"fn foo(the_field: u32) -> Result { if the_field < 5 { let mut i = 0; @@ -924,7 +913,7 @@ mod tests { the_field }"#, - r#"fn foo(the_field: u32) -> Result> { + r#"fn foo(the_field: u32) -> Result { if the_field < 5 { let mut i = 0; @@ -954,7 +943,7 @@ mod tests { the_field }"#, - r#"fn foo(the_field: u32) -> Result> { + r#"fn foo(the_field: u32) -> Result { if the_field < 5 { let mut i = 0; diff --git a/crates/ra_assists/src/tests/generated.rs b/crates/ra_assists/src/tests/generated.rs index d860cfefcc..cd6129dc58 100644 --- a/crates/ra_assists/src/tests/generated.rs +++ b/crates/ra_assists/src/tests/generated.rs @@ -276,7 +276,7 @@ fn doctest_change_return_type_to_result() { fn foo() -> i32<|> { 42i32 } "#####, r#####" -fn foo() -> Result { Ok(42i32) } +fn foo() -> Result { Ok(42i32) } "#####, ) } diff --git a/docs/user/assists.md b/docs/user/assists.md index 03c01d6c05..006ec4d547 100644 --- a/docs/user/assists.md +++ b/docs/user/assists.md @@ -268,7 +268,7 @@ Change the function's return type to Result. fn foo() -> i32┃ { 42i32 } // AFTER -fn foo() -> Result { Ok(42i32) } +fn foo() -> Result { Ok(42i32) } ``` ## `change_visibility` From d18d1c05949eaa890e7bb75710816a61b09a93dd Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 20 May 2020 00:19:59 +0200 Subject: [PATCH 52/70] Significantly more glorious marks --- .../ra_assists/src/handlers/add_turbo_fish.rs | 12 +-- crates/ra_assists/src/marks.rs | 2 - crates/test_utils/src/lib.rs | 2 + crates/test_utils/src/mark.rs | 78 +++++++++++++++++++ 4 files changed, 86 insertions(+), 8 deletions(-) create mode 100644 crates/test_utils/src/mark.rs diff --git a/crates/ra_assists/src/handlers/add_turbo_fish.rs b/crates/ra_assists/src/handlers/add_turbo_fish.rs index a0363bc78b..26acf81f28 100644 --- a/crates/ra_assists/src/handlers/add_turbo_fish.rs +++ b/crates/ra_assists/src/handlers/add_turbo_fish.rs @@ -1,11 +1,11 @@ use ra_ide_db::defs::{classify_name_ref, Definition, NameRefClass}; use ra_syntax::{ast, AstNode, SyntaxKind, T}; +use test_utils::mark; use crate::{ assist_context::{AssistContext, Assists}, AssistId, }; -use test_utils::tested_by; // Assist: add_turbo_fish // @@ -28,7 +28,7 @@ pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext) -> Option<( let ident = ctx.find_token_at_offset(SyntaxKind::IDENT)?; let next_token = ident.next_token()?; if next_token.kind() == T![::] { - tested_by!(add_turbo_fish_one_fish_is_enough); + mark::hit!(add_turbo_fish_one_fish_is_enough); return None; } let name_ref = ast::NameRef::cast(ident.parent())?; @@ -42,7 +42,7 @@ pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext) -> Option<( }; let generics = hir::GenericDef::Function(fun).params(ctx.sema.db); if generics.is_empty() { - tested_by!(add_turbo_fish_non_generic); + mark::hit!(add_turbo_fish_non_generic); return None; } acc.add(AssistId("add_turbo_fish"), "Add `::<>`", ident.text_range(), |builder| { @@ -58,7 +58,7 @@ mod tests { use crate::tests::{check_assist, check_assist_not_applicable}; use super::*; - use test_utils::covers; + use test_utils::mark; #[test] fn add_turbo_fish_function() { @@ -106,7 +106,7 @@ fn main() { #[test] fn add_turbo_fish_one_fish_is_enough() { - covers!(add_turbo_fish_one_fish_is_enough); + mark::check!(add_turbo_fish_one_fish_is_enough); check_assist_not_applicable( add_turbo_fish, r#" @@ -120,7 +120,7 @@ fn main() { #[test] fn add_turbo_fish_non_generic() { - covers!(add_turbo_fish_non_generic); + mark::check!(add_turbo_fish_non_generic); check_assist_not_applicable( add_turbo_fish, r#" diff --git a/crates/ra_assists/src/marks.rs b/crates/ra_assists/src/marks.rs index 722f3c6a4b..525ec4abc3 100644 --- a/crates/ra_assists/src/marks.rs +++ b/crates/ra_assists/src/marks.rs @@ -10,6 +10,4 @@ test_utils::marks![ test_not_applicable_if_variable_unused change_visibility_field_false_positive test_add_from_impl_already_exists - add_turbo_fish_one_fish_is_enough - add_turbo_fish_non_generic ]; diff --git a/crates/test_utils/src/lib.rs b/crates/test_utils/src/lib.rs index b1e3c328f3..4e05d464f0 100644 --- a/crates/test_utils/src/lib.rs +++ b/crates/test_utils/src/lib.rs @@ -8,6 +8,8 @@ #[macro_use] pub mod marks; +#[macro_use] +pub mod mark; use std::{ fs, diff --git a/crates/test_utils/src/mark.rs b/crates/test_utils/src/mark.rs new file mode 100644 index 0000000000..7c309a8945 --- /dev/null +++ b/crates/test_utils/src/mark.rs @@ -0,0 +1,78 @@ +//! This module implements manually tracked test coverage, which is useful for +//! quickly finding a test responsible for testing a particular bit of code. +//! +//! See +//! for details, but the TL;DR is that you write your test as +//! +//! ``` +//! #[test] +//! fn test_foo() { +//! mark::check!(test_foo); +//! } +//! ``` +//! +//! and in the code under test you write +//! +//! ``` +//! # use test_utils::mark; +//! # fn some_condition() -> bool { true } +//! fn foo() { +//! if some_condition() { +//! mark::hit!(test_foo); +//! } +//! } +//! ``` +//! +//! This module then checks that executing the test indeed covers the specified +//! function. This is useful if you come back to the `foo` function ten years +//! later and wonder where the test are: now you can grep for `test_foo`. +use std::sync::atomic::{AtomicUsize, Ordering}; + +#[macro_export] +macro_rules! _hit { + ($ident:ident) => {{ + #[cfg(test)] + { + extern "C" { + #[no_mangle] + static $ident: std::sync::atomic::AtomicUsize; + } + unsafe { + $ident.fetch_add(1, std::sync::atomic::Ordering::SeqCst); + } + } + }}; +} +pub use _hit as hit; + +#[macro_export] +macro_rules! _check { + ($ident:ident) => { + #[no_mangle] + static $ident: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0); + let _checker = $crate::mark::MarkChecker::new(&$ident); + }; +} +pub use _check as check; + +pub struct MarkChecker { + mark: &'static AtomicUsize, + value_on_entry: usize, +} + +impl MarkChecker { + pub fn new(mark: &'static AtomicUsize) -> MarkChecker { + let value_on_entry = mark.load(Ordering::SeqCst); + MarkChecker { mark, value_on_entry } + } +} + +impl Drop for MarkChecker { + fn drop(&mut self) { + if std::thread::panicking() { + return; + } + let value_on_exit = self.mark.load(Ordering::SeqCst); + assert!(value_on_exit > self.value_on_entry, "mark was not hit") + } +} From 5258c817f78ecdfe12d7eec44ab3169134cba71d Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 20 May 2020 11:54:57 +0200 Subject: [PATCH 53/70] Remove cross-crate marks They create quite a bit of friction. Really, we should just move the tests to the same crate, rather than paper over existing split. --- crates/ra_ide/src/goto_definition.rs | 10 +--------- crates/ra_ide/src/references.rs | 3 --- crates/ra_ide_db/src/defs.rs | 10 ++-------- crates/ra_ide_db/src/lib.rs | 1 - crates/ra_ide_db/src/marks.rs | 12 ------------ crates/ra_ide_db/src/search.rs | 2 -- crates/test_utils/src/marks.rs | 11 +---------- 7 files changed, 4 insertions(+), 45 deletions(-) delete mode 100644 crates/ra_ide_db/src/marks.rs diff --git a/crates/ra_ide/src/goto_definition.rs b/crates/ra_ide/src/goto_definition.rs index 150895abb4..90e85d4197 100644 --- a/crates/ra_ide/src/goto_definition.rs +++ b/crates/ra_ide/src/goto_definition.rs @@ -93,7 +93,7 @@ pub(crate) fn reference_definition( #[cfg(test)] mod tests { - use test_utils::{assert_eq_text, covers}; + use test_utils::assert_eq_text; use crate::mock_analysis::analysis_and_position; @@ -208,7 +208,6 @@ mod tests { #[test] fn goto_def_for_macros() { - covers!(ra_ide_db::goto_def_for_macros); check_goto( " //- /lib.rs @@ -225,7 +224,6 @@ mod tests { #[test] fn goto_def_for_macros_from_other_crates() { - covers!(ra_ide_db::goto_def_for_macros); check_goto( " //- /lib.rs @@ -245,7 +243,6 @@ mod tests { #[test] fn goto_def_for_use_alias() { - covers!(ra_ide_db::goto_def_for_use_alias); check_goto( " //- /lib.rs @@ -370,7 +367,6 @@ mod tests { #[test] fn goto_def_for_methods() { - covers!(ra_ide_db::goto_def_for_methods); check_goto( " //- /lib.rs @@ -390,7 +386,6 @@ mod tests { #[test] fn goto_def_for_fields() { - covers!(ra_ide_db::goto_def_for_fields); check_goto( r" //- /lib.rs @@ -409,7 +404,6 @@ mod tests { #[test] fn goto_def_for_record_fields() { - covers!(ra_ide_db::goto_def_for_record_fields); check_goto( r" //- /lib.rs @@ -430,7 +424,6 @@ mod tests { #[test] fn goto_def_for_record_pat_fields() { - covers!(ra_ide_db::goto_def_for_record_field_pats); check_goto( r" //- /lib.rs @@ -873,7 +866,6 @@ mod tests { #[test] fn goto_def_for_field_init_shorthand() { - covers!(ra_ide_db::goto_def_for_field_init_shorthand); check_goto( " //- /lib.rs diff --git a/crates/ra_ide/src/references.rs b/crates/ra_ide/src/references.rs index 074284b42e..96444bf6a5 100644 --- a/crates/ra_ide/src/references.rs +++ b/crates/ra_ide/src/references.rs @@ -190,8 +190,6 @@ fn get_struct_def_name_for_struct_literal_search( #[cfg(test)] mod tests { - use test_utils::covers; - use crate::{ mock_analysis::{analysis_and_position, single_file_with_position, MockAnalysis}, Declaration, Reference, ReferenceSearchResult, SearchScope, @@ -301,7 +299,6 @@ mod tests { #[test] fn search_filters_by_range() { - covers!(ra_ide_db::search_filters_by_range); let code = r#" fn foo() { let spam<|> = 92; diff --git a/crates/ra_ide_db/src/defs.rs b/crates/ra_ide_db/src/defs.rs index 60c11178ee..8b06cbfc54 100644 --- a/crates/ra_ide_db/src/defs.rs +++ b/crates/ra_ide_db/src/defs.rs @@ -14,7 +14,6 @@ use ra_syntax::{ ast::{self, AstNode}, match_ast, }; -use test_utils::tested_by; use crate::RootDatabase; @@ -118,7 +117,6 @@ fn classify_name_inner(sema: &Semantics, name: &ast::Name) -> Opti match_ast! { match parent { ast::Alias(it) => { - tested_by!(goto_def_for_use_alias; force); let use_tree = it.syntax().parent().and_then(ast::UseTree::cast)?; let path = use_tree.path()?; let path_segment = path.segment()?; @@ -203,6 +201,8 @@ impl NameRefClass { } } +// Note: we don't have unit-tests for this rather important function. +// It is primarily exercised via goto definition tests in `ra_ide`. pub fn classify_name_ref( sema: &Semantics, name_ref: &ast::NameRef, @@ -212,22 +212,18 @@ pub fn classify_name_ref( let parent = name_ref.syntax().parent()?; if let Some(method_call) = ast::MethodCallExpr::cast(parent.clone()) { - tested_by!(goto_def_for_methods; force); if let Some(func) = sema.resolve_method_call(&method_call) { return Some(NameRefClass::Definition(Definition::ModuleDef(func.into()))); } } if let Some(field_expr) = ast::FieldExpr::cast(parent.clone()) { - tested_by!(goto_def_for_fields; force); if let Some(field) = sema.resolve_field(&field_expr) { return Some(NameRefClass::Definition(Definition::Field(field))); } } if let Some(record_field) = ast::RecordField::for_field_name(name_ref) { - tested_by!(goto_def_for_record_fields; force); - tested_by!(goto_def_for_field_init_shorthand; force); if let Some((field, local)) = sema.resolve_record_field(&record_field) { let field = Definition::Field(field); let res = match local { @@ -239,7 +235,6 @@ pub fn classify_name_ref( } if let Some(record_field_pat) = ast::RecordFieldPat::cast(parent.clone()) { - tested_by!(goto_def_for_record_field_pats; force); if let Some(field) = sema.resolve_record_field_pat(&record_field_pat) { let field = Definition::Field(field); return Some(NameRefClass::Definition(field)); @@ -247,7 +242,6 @@ pub fn classify_name_ref( } if let Some(macro_call) = parent.ancestors().find_map(ast::MacroCall::cast) { - tested_by!(goto_def_for_macros; force); if let Some(macro_def) = sema.resolve_macro_call(¯o_call) { return Some(NameRefClass::Definition(Definition::Macro(macro_def))); } diff --git a/crates/ra_ide_db/src/lib.rs b/crates/ra_ide_db/src/lib.rs index 52fcd7b6f0..4f37954bf7 100644 --- a/crates/ra_ide_db/src/lib.rs +++ b/crates/ra_ide_db/src/lib.rs @@ -2,7 +2,6 @@ //! //! It is mainly a `HirDatabase` for semantic analysis, plus a `SymbolsDatabase`, for fuzzy search. -pub mod marks; pub mod line_index; pub mod line_index_utils; pub mod symbol_index; diff --git a/crates/ra_ide_db/src/marks.rs b/crates/ra_ide_db/src/marks.rs deleted file mode 100644 index 386fe605c7..0000000000 --- a/crates/ra_ide_db/src/marks.rs +++ /dev/null @@ -1,12 +0,0 @@ -//! See test_utils/src/marks.rs - -test_utils::marks![ - goto_def_for_macros - goto_def_for_use_alias - goto_def_for_methods - goto_def_for_fields - goto_def_for_record_fields - goto_def_for_field_init_shorthand - goto_def_for_record_field_pats - search_filters_by_range -]; diff --git a/crates/ra_ide_db/src/search.rs b/crates/ra_ide_db/src/search.rs index b464959fce..589f447719 100644 --- a/crates/ra_ide_db/src/search.rs +++ b/crates/ra_ide_db/src/search.rs @@ -12,7 +12,6 @@ use ra_db::{FileId, FileRange, SourceDatabaseExt}; use ra_prof::profile; use ra_syntax::{ast, match_ast, AstNode, TextRange, TextSize}; use rustc_hash::FxHashMap; -use test_utils::tested_by; use crate::{ defs::{classify_name_ref, Definition, NameRefClass}, @@ -209,7 +208,6 @@ impl Definition { for (idx, _) in text.match_indices(pat) { let offset: TextSize = idx.try_into().unwrap(); if !search_range.contains_inclusive(offset) { - tested_by!(search_filters_by_range; force); continue; } diff --git a/crates/test_utils/src/marks.rs b/crates/test_utils/src/marks.rs index c3185e860c..f20fb978ee 100644 --- a/crates/test_utils/src/marks.rs +++ b/crates/test_utils/src/marks.rs @@ -30,12 +30,6 @@ use std::sync::atomic::{AtomicUsize, Ordering}; #[macro_export] macro_rules! tested_by { - ($ident:ident; force) => {{ - { - // sic! use call-site crate - crate::marks::$ident.fetch_add(1, std::sync::atomic::Ordering::SeqCst); - } - }}; ($ident:ident) => {{ #[cfg(test)] { @@ -49,10 +43,7 @@ macro_rules! tested_by { macro_rules! covers { // sic! use call-site crate ($ident:ident) => { - $crate::covers!(crate::$ident) - }; - ($krate:ident :: $ident:ident) => { - let _checker = $crate::marks::MarkChecker::new(&$krate::marks::$ident); + let _checker = $crate::marks::MarkChecker::new(&crate::marks::$ident); }; } From ecac5d7de2192873c24b7b06d4964d188d8abe6a Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 20 May 2020 12:59:20 +0200 Subject: [PATCH 54/70] Switch to new magic marks --- .../src/handlers/add_from_impl_for_enum.rs | 8 +- .../src/handlers/change_visibility.rs | 8 +- .../src/handlers/fill_match_arms.rs | 9 ++- .../src/handlers/inline_local_variable.rs | 16 ++-- .../src/handlers/introduce_variable.rs | 16 ++-- crates/ra_assists/src/lib.rs | 1 - crates/ra_assists/src/marks.rs | 13 --- crates/ra_hir_def/src/body/lower.rs | 4 +- crates/ra_hir_def/src/body/scope.rs | 4 +- crates/ra_hir_def/src/find_path.rs | 67 +++++++-------- crates/ra_hir_def/src/lib.rs | 2 - crates/ra_hir_def/src/marks.rs | 17 ---- crates/ra_hir_def/src/nameres/collector.rs | 14 ++-- .../ra_hir_def/src/nameres/path_resolution.rs | 8 +- crates/ra_hir_def/src/nameres/raw.rs | 4 +- crates/ra_hir_def/src/nameres/tests.rs | 8 +- crates/ra_hir_def/src/nameres/tests/globs.rs | 7 +- crates/ra_hir_def/src/nameres/tests/macros.rs | 9 +-- .../src/nameres/tests/mod_resolution.rs | 2 +- crates/ra_hir_def/src/path/lower/lower_use.rs | 4 +- crates/ra_hir_ty/src/infer/coerce.rs | 6 +- crates/ra_hir_ty/src/infer/pat.rs | 4 +- crates/ra_hir_ty/src/infer/unify.rs | 8 +- crates/ra_hir_ty/src/lib.rs | 1 - crates/ra_hir_ty/src/lower.rs | 2 +- crates/ra_hir_ty/src/marks.rs | 12 --- crates/ra_hir_ty/src/method_resolution.rs | 2 +- crates/ra_hir_ty/src/tests/coercion.rs | 6 +- .../ra_hir_ty/src/tests/method_resolution.rs | 2 +- crates/ra_hir_ty/src/tests/patterns.rs | 4 +- crates/ra_hir_ty/src/tests/regression.rs | 15 ++-- crates/ra_hir_ty/src/tests/traits.rs | 8 +- crates/ra_ide/src/call_info.rs | 8 +- .../src/completion/complete_qualified_path.rs | 8 +- .../completion/complete_unqualified_path.rs | 8 +- crates/ra_ide/src/completion/presentation.rs | 24 +++--- crates/ra_ide/src/lib.rs | 2 - crates/ra_ide/src/marks.rs | 16 ---- crates/ra_ide/src/parent_module.rs | 8 +- crates/ra_ide/src/references/rename.rs | 12 +-- crates/test_utils/src/lib.rs | 2 - crates/test_utils/src/marks.rs | 81 ------------------- 42 files changed, 157 insertions(+), 303 deletions(-) delete mode 100644 crates/ra_assists/src/marks.rs delete mode 100644 crates/ra_hir_def/src/marks.rs delete mode 100644 crates/ra_hir_ty/src/marks.rs delete mode 100644 crates/ra_ide/src/marks.rs delete mode 100644 crates/test_utils/src/marks.rs diff --git a/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs b/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs index eb57b7231f..5f6c8b19a3 100644 --- a/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs +++ b/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs @@ -1,6 +1,6 @@ use ra_ide_db::RootDatabase; use ra_syntax::ast::{self, AstNode, NameOwner}; -use test_utils::tested_by; +use test_utils::mark; use crate::{utils::FamousDefs, AssistContext, AssistId, Assists}; @@ -39,7 +39,7 @@ pub(crate) fn add_from_impl_for_enum(acc: &mut Assists, ctx: &AssistContext) -> }; if existing_from_impl(&ctx.sema, &variant).is_some() { - tested_by!(test_add_from_impl_already_exists); + mark::hit!(test_add_from_impl_already_exists); return None; } @@ -90,7 +90,7 @@ fn existing_from_impl( #[cfg(test)] mod tests { - use test_utils::covers; + use test_utils::mark; use crate::tests::{check_assist, check_assist_not_applicable}; @@ -149,7 +149,7 @@ impl From for A { #[test] fn test_add_from_impl_already_exists() { - covers!(test_add_from_impl_already_exists); + mark::check!(test_add_from_impl_already_exists); check_not_applicable( r#" enum A { <|>One(u32), } diff --git a/crates/ra_assists/src/handlers/change_visibility.rs b/crates/ra_assists/src/handlers/change_visibility.rs index 40cf4b4229..71d55e0c30 100644 --- a/crates/ra_assists/src/handlers/change_visibility.rs +++ b/crates/ra_assists/src/handlers/change_visibility.rs @@ -9,7 +9,7 @@ use ra_syntax::{ }; use hir::{db::HirDatabase, HasSource, HasVisibility, PathResolution}; -use test_utils::tested_by; +use test_utils::mark; use crate::{AssistContext, AssistId, Assists}; use ra_db::FileId; @@ -55,7 +55,7 @@ fn add_vis(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { } 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); + mark::hit!(change_visibility_field_false_positive); return None; } if field.visibility().is_some() { @@ -255,7 +255,7 @@ fn change_vis(acc: &mut Assists, vis: ast::Visibility) -> Option<()> { #[cfg(test)] mod tests { - use test_utils::covers; + use test_utils::mark; use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target}; @@ -288,7 +288,7 @@ mod tests { #[test] fn change_visibility_field_false_positive() { - covers!(change_visibility_field_false_positive); + mark::check!(change_visibility_field_false_positive); check_assist_not_applicable( change_visibility, r"struct S { field: [(); { let <|>x = ();}] }", diff --git a/crates/ra_assists/src/handlers/fill_match_arms.rs b/crates/ra_assists/src/handlers/fill_match_arms.rs index b57ff75aed..bbdcdc6263 100644 --- a/crates/ra_assists/src/handlers/fill_match_arms.rs +++ b/crates/ra_assists/src/handlers/fill_match_arms.rs @@ -4,7 +4,7 @@ use hir::{Adt, HasSource, ModuleDef, Semantics}; use itertools::Itertools; use ra_ide_db::RootDatabase; use ra_syntax::ast::{self, make, AstNode, MatchArm, NameOwner, Pat}; -use test_utils::tested_by; +use test_utils::mark; use crate::{utils::FamousDefs, AssistContext, AssistId, Assists}; @@ -58,7 +58,7 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option< .collect::>(); if Some(enum_def) == FamousDefs(&ctx.sema, module.krate()).core_option_Option() { // Match `Some` variant first. - tested_by!(option_order); + mark::hit!(option_order); variants.reverse() } variants @@ -174,13 +174,14 @@ fn build_pat(db: &RootDatabase, module: hir::Module, var: hir::EnumVariant) -> O #[cfg(test)] mod tests { + use test_utils::mark; + use crate::{ tests::{check_assist, check_assist_not_applicable, check_assist_target}, utils::FamousDefs, }; use super::fill_match_arms; - use test_utils::covers; #[test] fn all_match_arms_provided() { @@ -750,7 +751,7 @@ mod tests { #[test] fn option_order() { - covers!(option_order); + mark::check!(option_order); let before = r#" fn foo(opt: Option) { match opt<|> { diff --git a/crates/ra_assists/src/handlers/inline_local_variable.rs b/crates/ra_assists/src/handlers/inline_local_variable.rs index 5b26814d30..46d675a4e8 100644 --- a/crates/ra_assists/src/handlers/inline_local_variable.rs +++ b/crates/ra_assists/src/handlers/inline_local_variable.rs @@ -3,7 +3,7 @@ use ra_syntax::{ ast::{self, AstNode, AstToken}, TextRange, }; -use test_utils::tested_by; +use test_utils::mark; use crate::{ assist_context::{AssistContext, Assists}, @@ -33,11 +33,11 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O _ => return None, }; if bind_pat.mut_token().is_some() { - tested_by!(test_not_inline_mut_variable); + mark::hit!(test_not_inline_mut_variable); return None; } if !bind_pat.syntax().text_range().contains_inclusive(ctx.offset()) { - tested_by!(not_applicable_outside_of_bind_pat); + mark::hit!(not_applicable_outside_of_bind_pat); return None; } let initializer_expr = let_stmt.initializer()?; @@ -46,7 +46,7 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O let def = Definition::Local(def); let refs = def.find_usages(ctx.db, None); if refs.is_empty() { - tested_by!(test_not_applicable_if_variable_unused); + mark::hit!(test_not_applicable_if_variable_unused); return None; }; @@ -122,7 +122,7 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O #[cfg(test)] mod tests { - use test_utils::covers; + use test_utils::mark; use crate::tests::{check_assist, check_assist_not_applicable}; @@ -330,7 +330,7 @@ fn foo() { #[test] fn test_not_inline_mut_variable() { - covers!(test_not_inline_mut_variable); + mark::check!(test_not_inline_mut_variable); check_assist_not_applicable( inline_local_variable, r" @@ -663,7 +663,7 @@ fn foo() { #[test] fn test_not_applicable_if_variable_unused() { - covers!(test_not_applicable_if_variable_unused); + mark::check!(test_not_applicable_if_variable_unused); check_assist_not_applicable( inline_local_variable, r" @@ -676,7 +676,7 @@ fn foo() { #[test] fn not_applicable_outside_of_bind_pat() { - covers!(not_applicable_outside_of_bind_pat); + mark::check!(not_applicable_outside_of_bind_pat); check_assist_not_applicable( inline_local_variable, r" diff --git a/crates/ra_assists/src/handlers/introduce_variable.rs b/crates/ra_assists/src/handlers/introduce_variable.rs index fdf3ada0d7..56c610fedd 100644 --- a/crates/ra_assists/src/handlers/introduce_variable.rs +++ b/crates/ra_assists/src/handlers/introduce_variable.rs @@ -7,7 +7,7 @@ use ra_syntax::{ SyntaxNode, TextSize, }; use stdx::format_to; -use test_utils::tested_by; +use test_utils::mark; use crate::{AssistContext, AssistId, Assists}; @@ -33,7 +33,7 @@ pub(crate) fn introduce_variable(acc: &mut Assists, ctx: &AssistContext) -> Opti } let node = ctx.covering_element(); if node.kind() == COMMENT { - tested_by!(introduce_var_in_comment_is_not_applicable); + mark::hit!(introduce_var_in_comment_is_not_applicable); return None; } let expr = node.ancestors().find_map(valid_target_expr)?; @@ -61,7 +61,7 @@ pub(crate) fn introduce_variable(acc: &mut Assists, ctx: &AssistContext) -> Opti false }; if is_full_stmt { - tested_by!(test_introduce_var_expr_stmt); + mark::hit!(test_introduce_var_expr_stmt); if full_stmt.unwrap().semicolon_token().is_none() { buf.push_str(";"); } @@ -113,7 +113,7 @@ fn anchor_stmt(expr: ast::Expr) -> Option<(SyntaxNode, bool)> { expr.syntax().ancestors().find_map(|node| { if let Some(expr) = node.parent().and_then(ast::BlockExpr::cast).and_then(|it| it.expr()) { if expr.syntax() == &node { - tested_by!(test_introduce_var_last_expr); + mark::hit!(test_introduce_var_last_expr); return Some((node, false)); } } @@ -134,7 +134,7 @@ fn anchor_stmt(expr: ast::Expr) -> Option<(SyntaxNode, bool)> { #[cfg(test)] mod tests { - use test_utils::covers; + use test_utils::mark; use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target}; @@ -158,13 +158,13 @@ fn foo() { #[test] fn introduce_var_in_comment_is_not_applicable() { - covers!(introduce_var_in_comment_is_not_applicable); + mark::check!(introduce_var_in_comment_is_not_applicable); check_assist_not_applicable(introduce_variable, "fn main() { 1 + /* <|>comment<|> */ 1; }"); } #[test] fn test_introduce_var_expr_stmt() { - covers!(test_introduce_var_expr_stmt); + mark::check!(test_introduce_var_expr_stmt); check_assist( introduce_variable, " @@ -209,7 +209,7 @@ fn foo() { #[test] fn test_introduce_var_last_expr() { - covers!(test_introduce_var_last_expr); + mark::check!(test_introduce_var_last_expr); check_assist( introduce_variable, " diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs index 339f24100d..a4a497c736 100644 --- a/crates/ra_assists/src/lib.rs +++ b/crates/ra_assists/src/lib.rs @@ -12,7 +12,6 @@ macro_rules! eprintln { mod assist_config; mod assist_context; -mod marks; #[cfg(test)] mod tests; pub mod utils; diff --git a/crates/ra_assists/src/marks.rs b/crates/ra_assists/src/marks.rs deleted file mode 100644 index 525ec4abc3..0000000000 --- a/crates/ra_assists/src/marks.rs +++ /dev/null @@ -1,13 +0,0 @@ -//! See test_utils/src/marks.rs - -test_utils::marks![ - option_order - introduce_var_in_comment_is_not_applicable - test_introduce_var_expr_stmt - test_introduce_var_last_expr - not_applicable_outside_of_bind_pat - test_not_inline_mut_variable - test_not_applicable_if_variable_unused - change_visibility_field_false_positive - test_add_from_impl_already_exists -]; diff --git a/crates/ra_hir_def/src/body/lower.rs b/crates/ra_hir_def/src/body/lower.rs index c69e0efea6..e08d62dd68 100644 --- a/crates/ra_hir_def/src/body/lower.rs +++ b/crates/ra_hir_def/src/body/lower.rs @@ -15,7 +15,7 @@ use ra_syntax::{ }, AstNode, AstPtr, }; -use test_utils::tested_by; +use test_utils::mark; use crate::{ adt::StructKind, @@ -226,7 +226,7 @@ impl ExprCollector<'_> { None => self.collect_expr_opt(condition.expr()), // if let -- desugar to match Some(pat) => { - tested_by!(infer_resolve_while_let); + mark::hit!(infer_resolve_while_let); let pat = self.collect_pat(pat); let match_expr = self.collect_expr_opt(condition.expr()); let placeholder_pat = self.missing_pat(); diff --git a/crates/ra_hir_def/src/body/scope.rs b/crates/ra_hir_def/src/body/scope.rs index 86f953c802..09e92b74e1 100644 --- a/crates/ra_hir_def/src/body/scope.rs +++ b/crates/ra_hir_def/src/body/scope.rs @@ -174,7 +174,7 @@ mod tests { use hir_expand::{name::AsName, InFile}; use ra_db::{fixture::WithFixture, FileId, SourceDatabase}; use ra_syntax::{algo::find_node_at_offset, ast, AstNode}; - use test_utils::{assert_eq_text, covers, extract_offset}; + use test_utils::{assert_eq_text, extract_offset, mark}; use crate::{db::DefDatabase, test_db::TestDB, FunctionId, ModuleDefId}; @@ -388,7 +388,7 @@ mod tests { #[test] fn while_let_desugaring() { - covers!(infer_resolve_while_let); + mark::check!(infer_resolve_while_let); do_check_local_name( r#" fn test() { diff --git a/crates/ra_hir_def/src/find_path.rs b/crates/ra_hir_def/src/find_path.rs index 2eb12ec8f2..68d3cde08a 100644 --- a/crates/ra_hir_def/src/find_path.rs +++ b/crates/ra_hir_def/src/find_path.rs @@ -4,7 +4,7 @@ use std::sync::Arc; use hir_expand::name::{known, AsName, Name}; use ra_prof::profile; -use test_utils::tested_by; +use test_utils::mark; use crate::{ db::DefDatabase, @@ -164,17 +164,19 @@ fn find_path_inner( fn select_best_path(old_path: ModPath, new_path: ModPath, prefer_no_std: bool) -> ModPath { if old_path.starts_with_std() && new_path.can_start_with_std() { - tested_by!(prefer_std_paths); if prefer_no_std { + mark::hit!(prefer_no_std_paths); new_path } else { + mark::hit!(prefer_std_paths); old_path } } else if new_path.starts_with_std() && old_path.can_start_with_std() { - tested_by!(prefer_std_paths); if prefer_no_std { + mark::hit!(prefer_no_std_paths); old_path } else { + mark::hit!(prefer_std_paths); new_path } } else if new_path.len() < old_path.len() { @@ -251,12 +253,14 @@ pub(crate) fn importable_locations_of_query( #[cfg(test)] mod tests { - use super::*; - use crate::test_db::TestDB; use hir_expand::hygiene::Hygiene; use ra_db::fixture::WithFixture; use ra_syntax::ast::AstNode; - use test_utils::covers; + use test_utils::mark; + + use crate::test_db::TestDB; + + use super::*; /// `code` needs to contain a cursor marker; checks that `find_path` for the /// item the `path` refers to returns that same path when called from the @@ -511,7 +515,7 @@ mod tests { #[test] fn prefer_std_paths_over_alloc() { - covers!(prefer_std_paths); + mark::check!(prefer_std_paths); let code = r#" //- /main.rs crate:main deps:alloc,std <|> @@ -529,33 +533,9 @@ mod tests { check_found_path(code, "std::sync::Arc"); } - #[test] - fn prefer_alloc_paths_over_std() { - covers!(prefer_std_paths); - let code = r#" - //- /main.rs crate:main deps:alloc,std - #![no_std] - - <|> - - //- /std.rs crate:std deps:alloc - - pub mod sync { - pub use alloc::sync::Arc; - } - - //- /zzz.rs crate:alloc - - pub mod sync { - pub struct Arc; - } - "#; - check_found_path(code, "alloc::sync::Arc"); - } - #[test] fn prefer_core_paths_over_std() { - covers!(prefer_std_paths); + mark::check!(prefer_no_std_paths); let code = r#" //- /main.rs crate:main deps:core,std #![no_std] @@ -577,6 +557,29 @@ mod tests { check_found_path(code, "core::fmt::Error"); } + #[test] + fn prefer_alloc_paths_over_std() { + let code = r#" + //- /main.rs crate:main deps:alloc,std + #![no_std] + + <|> + + //- /std.rs crate:std deps:alloc + + pub mod sync { + pub use alloc::sync::Arc; + } + + //- /zzz.rs crate:alloc + + pub mod sync { + pub struct Arc; + } + "#; + check_found_path(code, "alloc::sync::Arc"); + } + #[test] fn prefer_shorter_paths_if_not_alloc() { let code = r#" diff --git a/crates/ra_hir_def/src/lib.rs b/crates/ra_hir_def/src/lib.rs index 518772e8ab..5325a27608 100644 --- a/crates/ra_hir_def/src/lib.rs +++ b/crates/ra_hir_def/src/lib.rs @@ -46,8 +46,6 @@ pub mod find_path; #[cfg(test)] mod test_db; -#[cfg(test)] -mod marks; use std::hash::Hash; diff --git a/crates/ra_hir_def/src/marks.rs b/crates/ra_hir_def/src/marks.rs deleted file mode 100644 index daa49d5f10..0000000000 --- a/crates/ra_hir_def/src/marks.rs +++ /dev/null @@ -1,17 +0,0 @@ -//! See test_utils/src/marks.rs - -test_utils::marks!( - bogus_paths - name_res_works_for_broken_modules - can_import_enum_variant - glob_enum - glob_enum_group - glob_across_crates - std_prelude - macro_rules_from_other_crates_are_visible_with_macro_use - prelude_is_macro_use - macro_dollar_crate_self - macro_dollar_crate_other - infer_resolve_while_let - prefer_std_paths -); diff --git a/crates/ra_hir_def/src/nameres/collector.rs b/crates/ra_hir_def/src/nameres/collector.rs index db994122ae..353a31ad47 100644 --- a/crates/ra_hir_def/src/nameres/collector.rs +++ b/crates/ra_hir_def/src/nameres/collector.rs @@ -14,7 +14,7 @@ use ra_cfg::CfgOptions; use ra_db::{CrateId, FileId, ProcMacroId}; use ra_syntax::ast; use rustc_hash::FxHashMap; -use test_utils::tested_by; +use test_utils::mark; use crate::{ attr::Attrs, @@ -302,7 +302,7 @@ impl DefCollector<'_> { ); if let Some(ModuleDefId::ModuleId(m)) = res.take_types() { - tested_by!(macro_rules_from_other_crates_are_visible_with_macro_use); + mark::hit!(macro_rules_from_other_crates_are_visible_with_macro_use); self.import_all_macros_exported(current_module_id, m.krate); } } @@ -412,10 +412,10 @@ impl DefCollector<'_> { match def.take_types() { Some(ModuleDefId::ModuleId(m)) => { if import.is_prelude { - tested_by!(std_prelude); + mark::hit!(std_prelude); self.def_map.prelude = Some(m); } else if m.krate != self.def_map.krate { - tested_by!(glob_across_crates); + mark::hit!(glob_across_crates); // glob import from other crate => we can just import everything once let item_map = self.db.crate_def_map(m.krate); let scope = &item_map[m.local_id].scope; @@ -461,7 +461,7 @@ impl DefCollector<'_> { } } Some(ModuleDefId::AdtId(AdtId::EnumId(e))) => { - tested_by!(glob_enum); + mark::hit!(glob_enum); // glob import from enum => just import all the variants // XXX: urgh, so this works by accident! Here, we look at @@ -510,7 +510,7 @@ impl DefCollector<'_> { self.update(module_id, &[(name, def)], vis); } - None => tested_by!(bogus_paths), + None => mark::hit!(bogus_paths), } } } @@ -683,7 +683,7 @@ impl ModCollector<'_, '_> { // Prelude module is always considered to be `#[macro_use]`. if let Some(prelude_module) = self.def_collector.def_map.prelude { if prelude_module.krate != self.def_collector.def_map.krate { - tested_by!(prelude_is_macro_use); + mark::hit!(prelude_is_macro_use); self.def_collector.import_all_macros_exported(self.module_id, prelude_module.krate); } } diff --git a/crates/ra_hir_def/src/nameres/path_resolution.rs b/crates/ra_hir_def/src/nameres/path_resolution.rs index 35a0a0c988..19692e70cf 100644 --- a/crates/ra_hir_def/src/nameres/path_resolution.rs +++ b/crates/ra_hir_def/src/nameres/path_resolution.rs @@ -14,7 +14,7 @@ use std::iter::successors; use hir_expand::name::Name; use ra_db::Edition; -use test_utils::tested_by; +use test_utils::mark; use crate::{ db::DefDatabase, @@ -108,7 +108,7 @@ impl CrateDefMap { let mut curr_per_ns: PerNs = match path.kind { PathKind::DollarCrate(krate) => { if krate == self.krate { - tested_by!(macro_dollar_crate_self); + mark::hit!(macro_dollar_crate_self); PerNs::types( ModuleId { krate: self.krate, local_id: self.root }.into(), Visibility::Public, @@ -116,7 +116,7 @@ impl CrateDefMap { } else { let def_map = db.crate_def_map(krate); let module = ModuleId { krate, local_id: def_map.root }; - tested_by!(macro_dollar_crate_other); + mark::hit!(macro_dollar_crate_other); PerNs::types(module.into(), Visibility::Public) } } @@ -221,7 +221,7 @@ impl CrateDefMap { } ModuleDefId::AdtId(AdtId::EnumId(e)) => { // enum variant - tested_by!(can_import_enum_variant); + mark::hit!(can_import_enum_variant); let enum_data = db.enum_data(e); match enum_data.variant(&segment) { Some(local_id) => { diff --git a/crates/ra_hir_def/src/nameres/raw.rs b/crates/ra_hir_def/src/nameres/raw.rs index f2716a2950..4e628b14d9 100644 --- a/crates/ra_hir_def/src/nameres/raw.rs +++ b/crates/ra_hir_def/src/nameres/raw.rs @@ -18,7 +18,7 @@ use ra_syntax::{ ast::{self, AttrsOwner, NameOwner, VisibilityOwner}, AstNode, }; -use test_utils::tested_by; +use test_utils::mark; use crate::{ attr::Attrs, @@ -346,7 +346,7 @@ impl RawItemsCollector { self.push_item(current_module, attrs, RawItemKind::Module(item)); return; } - tested_by!(name_res_works_for_broken_modules); + mark::hit!(name_res_works_for_broken_modules); } fn add_use_item(&mut self, current_module: Option>, use_item: ast::UseItem) { diff --git a/crates/ra_hir_def/src/nameres/tests.rs b/crates/ra_hir_def/src/nameres/tests.rs index 1b66c1aacf..05cd0297d1 100644 --- a/crates/ra_hir_def/src/nameres/tests.rs +++ b/crates/ra_hir_def/src/nameres/tests.rs @@ -8,7 +8,7 @@ use std::sync::Arc; use insta::assert_snapshot; use ra_db::{fixture::WithFixture, SourceDatabase}; -use test_utils::covers; +use test_utils::mark; use crate::{db::DefDatabase, nameres::*, test_db::TestDB}; @@ -132,7 +132,7 @@ fn crate_def_map_fn_mod_same_name() { #[test] fn bogus_paths() { - covers!(bogus_paths); + mark::check!(bogus_paths); let map = def_map( " //- /lib.rs @@ -247,7 +247,7 @@ fn re_exports() { #[test] fn std_prelude() { - covers!(std_prelude); + mark::check!(std_prelude); let map = def_map( " //- /main.rs crate:main deps:test_crate @@ -271,7 +271,7 @@ fn std_prelude() { #[test] fn can_import_enum_variant() { - covers!(can_import_enum_variant); + mark::check!(can_import_enum_variant); let map = def_map( " //- /lib.rs diff --git a/crates/ra_hir_def/src/nameres/tests/globs.rs b/crates/ra_hir_def/src/nameres/tests/globs.rs index ee8df3a26f..2b12c0daad 100644 --- a/crates/ra_hir_def/src/nameres/tests/globs.rs +++ b/crates/ra_hir_def/src/nameres/tests/globs.rs @@ -152,7 +152,7 @@ fn glob_privacy_2() { #[test] fn glob_across_crates() { - covers!(glob_across_crates); + mark::check!(glob_across_crates); let map = def_map( r" //- /main.rs crate:main deps:test_crate @@ -171,7 +171,6 @@ fn glob_across_crates() { #[test] fn glob_privacy_across_crates() { - covers!(glob_across_crates); let map = def_map( r" //- /main.rs crate:main deps:test_crate @@ -191,7 +190,7 @@ fn glob_privacy_across_crates() { #[test] fn glob_enum() { - covers!(glob_enum); + mark::check!(glob_enum); let map = def_map( " //- /lib.rs @@ -212,7 +211,7 @@ fn glob_enum() { #[test] fn glob_enum_group() { - covers!(glob_enum_group); + mark::check!(glob_enum_group); let map = def_map( r" //- /lib.rs diff --git a/crates/ra_hir_def/src/nameres/tests/macros.rs b/crates/ra_hir_def/src/nameres/tests/macros.rs index 40289e3ca5..84480d9f6c 100644 --- a/crates/ra_hir_def/src/nameres/tests/macros.rs +++ b/crates/ra_hir_def/src/nameres/tests/macros.rs @@ -212,7 +212,7 @@ fn unexpanded_macro_should_expand_by_fixedpoint_loop() { #[test] fn macro_rules_from_other_crates_are_visible_with_macro_use() { - covers!(macro_rules_from_other_crates_are_visible_with_macro_use); + mark::check!(macro_rules_from_other_crates_are_visible_with_macro_use); let map = def_map( " //- /main.rs crate:main deps:foo @@ -262,7 +262,7 @@ fn macro_rules_from_other_crates_are_visible_with_macro_use() { #[test] fn prelude_is_macro_use() { - covers!(prelude_is_macro_use); + mark::check!(prelude_is_macro_use); let map = def_map( " //- /main.rs crate:main deps:foo @@ -544,8 +544,7 @@ fn path_qualified_macros() { #[test] fn macro_dollar_crate_is_correct_in_item() { - covers!(macro_dollar_crate_self); - covers!(macro_dollar_crate_other); + mark::check!(macro_dollar_crate_self); let map = def_map( " //- /main.rs crate:main deps:foo @@ -603,7 +602,7 @@ fn macro_dollar_crate_is_correct_in_item() { #[test] fn macro_dollar_crate_is_correct_in_indirect_deps() { - covers!(macro_dollar_crate_other); + mark::check!(macro_dollar_crate_other); // From std let map = def_map( r#" diff --git a/crates/ra_hir_def/src/nameres/tests/mod_resolution.rs b/crates/ra_hir_def/src/nameres/tests/mod_resolution.rs index 37fcdfb8cc..b43b294cab 100644 --- a/crates/ra_hir_def/src/nameres/tests/mod_resolution.rs +++ b/crates/ra_hir_def/src/nameres/tests/mod_resolution.rs @@ -2,7 +2,7 @@ use super::*; #[test] fn name_res_works_for_broken_modules() { - covers!(name_res_works_for_broken_modules); + mark::check!(name_res_works_for_broken_modules); let map = def_map( r" //- /lib.rs diff --git a/crates/ra_hir_def/src/path/lower/lower_use.rs b/crates/ra_hir_def/src/path/lower/lower_use.rs index 5b6854b0f0..7cc655487e 100644 --- a/crates/ra_hir_def/src/path/lower/lower_use.rs +++ b/crates/ra_hir_def/src/path/lower/lower_use.rs @@ -6,7 +6,7 @@ use std::iter; use either::Either; use hir_expand::{hygiene::Hygiene, name::AsName}; use ra_syntax::ast::{self, NameOwner}; -use test_utils::tested_by; +use test_utils::mark; use crate::path::{ImportAlias, ModPath, PathKind}; @@ -54,7 +54,7 @@ pub(crate) fn lower_use_tree( // FIXME: report errors somewhere // We get here if we do } else if is_glob { - tested_by!(glob_enum_group); + mark::hit!(glob_enum_group); if let Some(prefix) = prefix { cb(prefix, &tree, is_glob, None) } diff --git a/crates/ra_hir_ty/src/infer/coerce.rs b/crates/ra_hir_ty/src/infer/coerce.rs index 173ec59edf..2ee9adb164 100644 --- a/crates/ra_hir_ty/src/infer/coerce.rs +++ b/crates/ra_hir_ty/src/infer/coerce.rs @@ -5,7 +5,7 @@ //! See: https://doc.rust-lang.org/nomicon/coercions.html use hir_def::{lang_item::LangItemTarget, type_ref::Mutability}; -use test_utils::tested_by; +use test_utils::mark; use crate::{autoderef, traits::Solution, Obligation, Substs, TraitRef, Ty, TypeCtor}; @@ -34,7 +34,7 @@ impl<'a> InferenceContext<'a> { ty1.clone() } else { if let (ty_app!(TypeCtor::FnDef(_)), ty_app!(TypeCtor::FnDef(_))) = (ty1, ty2) { - tested_by!(coerce_fn_reification); + mark::hit!(coerce_fn_reification); // Special case: two function types. Try to coerce both to // pointers to have a chance at getting a match. See // https://github.com/rust-lang/rust/blob/7b805396bf46dce972692a6846ce2ad8481c5f85/src/librustc_typeck/check/coercion.rs#L877-L916 @@ -44,7 +44,7 @@ impl<'a> InferenceContext<'a> { let ptr_ty2 = Ty::fn_ptr(sig2); self.coerce_merge_branch(&ptr_ty1, &ptr_ty2) } else { - tested_by!(coerce_merge_fail_fallback); + mark::hit!(coerce_merge_fail_fallback); // For incompatible types, we use the latter one as result // to be better recovery for `if` without `else`. ty2.clone() diff --git a/crates/ra_hir_ty/src/infer/pat.rs b/crates/ra_hir_ty/src/infer/pat.rs index 54ec870dfc..4006f595d1 100644 --- a/crates/ra_hir_ty/src/infer/pat.rs +++ b/crates/ra_hir_ty/src/infer/pat.rs @@ -10,7 +10,7 @@ use hir_def::{ FieldId, }; use hir_expand::name::Name; -use test_utils::tested_by; +use test_utils::mark; use super::{BindingMode, Expectation, InferenceContext}; use crate::{utils::variant_data, Substs, Ty, TypeCtor}; @@ -111,7 +111,7 @@ impl<'a> InferenceContext<'a> { } } } else if let Pat::Ref { .. } = &body[pat] { - tested_by!(match_ergonomics_ref); + mark::hit!(match_ergonomics_ref); // When you encounter a `&pat` pattern, reset to Move. // This is so that `w` is by value: `let (_, &w) = &(1, &2);` default_bm = BindingMode::Move; diff --git a/crates/ra_hir_ty/src/infer/unify.rs b/crates/ra_hir_ty/src/infer/unify.rs index ab0bc8b70b..269495ca0b 100644 --- a/crates/ra_hir_ty/src/infer/unify.rs +++ b/crates/ra_hir_ty/src/infer/unify.rs @@ -4,7 +4,7 @@ use std::borrow::Cow; use ena::unify::{InPlaceUnificationTable, NoError, UnifyKey, UnifyValue}; -use test_utils::tested_by; +use test_utils::mark; use super::{InferenceContext, Obligation}; use crate::{ @@ -313,7 +313,7 @@ impl InferenceTable { // more than once for i in 0..3 { if i > 0 { - tested_by!(type_var_resolves_to_int_var); + mark::hit!(type_var_resolves_to_int_var); } match &*ty { Ty::Infer(tv) => { @@ -342,7 +342,7 @@ impl InferenceTable { Ty::Infer(tv) => { let inner = tv.to_inner(); if tv_stack.contains(&inner) { - tested_by!(type_var_cycles_resolve_as_possible); + mark::hit!(type_var_cycles_resolve_as_possible); // recursive type return tv.fallback_value(); } @@ -369,7 +369,7 @@ impl InferenceTable { Ty::Infer(tv) => { let inner = tv.to_inner(); if tv_stack.contains(&inner) { - tested_by!(type_var_cycles_resolve_completely); + mark::hit!(type_var_cycles_resolve_completely); // recursive type return tv.fallback_value(); } diff --git a/crates/ra_hir_ty/src/lib.rs b/crates/ra_hir_ty/src/lib.rs index daea02f881..c87ee06ce8 100644 --- a/crates/ra_hir_ty/src/lib.rs +++ b/crates/ra_hir_ty/src/lib.rs @@ -42,7 +42,6 @@ pub mod expr; mod tests; #[cfg(test)] mod test_db; -mod marks; mod _match; use std::ops::Deref; diff --git a/crates/ra_hir_ty/src/lower.rs b/crates/ra_hir_ty/src/lower.rs index 9ad6dbe075..35ac86a461 100644 --- a/crates/ra_hir_ty/src/lower.rs +++ b/crates/ra_hir_ty/src/lower.rs @@ -812,7 +812,7 @@ impl TraitEnvironment { // add `Self: Trait` to the environment in trait // function default implementations (and hypothetical code // inside consts or type aliases) - test_utils::tested_by!(trait_self_implements_self); + test_utils::mark::hit!(trait_self_implements_self); let substs = Substs::type_params(db, trait_id); let trait_ref = TraitRef { trait_: trait_id, substs }; let pred = GenericPredicate::Implemented(trait_ref); diff --git a/crates/ra_hir_ty/src/marks.rs b/crates/ra_hir_ty/src/marks.rs deleted file mode 100644 index a397401434..0000000000 --- a/crates/ra_hir_ty/src/marks.rs +++ /dev/null @@ -1,12 +0,0 @@ -//! See test_utils/src/marks.rs - -test_utils::marks!( - type_var_cycles_resolve_completely - type_var_cycles_resolve_as_possible - type_var_resolves_to_int_var - impl_self_type_match_without_receiver - match_ergonomics_ref - coerce_merge_fail_fallback - coerce_fn_reification - trait_self_implements_self -); diff --git a/crates/ra_hir_ty/src/method_resolution.rs b/crates/ra_hir_ty/src/method_resolution.rs index 0851e16a89..e19628fdf7 100644 --- a/crates/ra_hir_ty/src/method_resolution.rs +++ b/crates/ra_hir_ty/src/method_resolution.rs @@ -469,7 +469,7 @@ fn iterate_inherent_methods( // already happens in `is_valid_candidate` above; if not, we // check it here if receiver_ty.is_none() && inherent_impl_substs(db, impl_def, self_ty).is_none() { - test_utils::tested_by!(impl_self_type_match_without_receiver); + test_utils::mark::hit!(impl_self_type_match_without_receiver); continue; } if let Some(result) = callback(&self_ty.value, item) { diff --git a/crates/ra_hir_ty/src/tests/coercion.rs b/crates/ra_hir_ty/src/tests/coercion.rs index 6dc4b2cd1d..2cc4f4bf96 100644 --- a/crates/ra_hir_ty/src/tests/coercion.rs +++ b/crates/ra_hir_ty/src/tests/coercion.rs @@ -1,6 +1,6 @@ use super::infer_with_mismatches; use insta::assert_snapshot; -use test_utils::covers; +use test_utils::mark; // Infer with some common definitions and impls. fn infer(source: &str) -> String { @@ -339,7 +339,7 @@ fn test(i: i32) { #[test] fn coerce_merge_one_by_one1() { - covers!(coerce_merge_fail_fallback); + mark::check!(coerce_merge_fail_fallback); assert_snapshot!( infer(r#" @@ -547,7 +547,7 @@ fn test() { #[test] fn coerce_fn_items_in_match_arms() { - covers!(coerce_fn_reification); + mark::check!(coerce_fn_reification); assert_snapshot!( infer_with_mismatches(r#" fn foo1(x: u32) -> isize { 1 } diff --git a/crates/ra_hir_ty/src/tests/method_resolution.rs b/crates/ra_hir_ty/src/tests/method_resolution.rs index 9c2c9e1d28..558a70022d 100644 --- a/crates/ra_hir_ty/src/tests/method_resolution.rs +++ b/crates/ra_hir_ty/src/tests/method_resolution.rs @@ -984,7 +984,7 @@ fn test() { S2.into()<|>; } #[test] fn method_resolution_overloaded_method() { - test_utils::covers!(impl_self_type_match_without_receiver); + test_utils::mark::check!(impl_self_type_match_without_receiver); let t = type_at( r#" //- main.rs diff --git a/crates/ra_hir_ty/src/tests/patterns.rs b/crates/ra_hir_ty/src/tests/patterns.rs index d83ff5e0ea..0c5f972a2c 100644 --- a/crates/ra_hir_ty/src/tests/patterns.rs +++ b/crates/ra_hir_ty/src/tests/patterns.rs @@ -1,5 +1,5 @@ use insta::assert_snapshot; -use test_utils::covers; +use test_utils::mark; use super::{infer, infer_with_mismatches}; @@ -197,7 +197,7 @@ fn test() { #[test] fn infer_pattern_match_ergonomics_ref() { - covers!(match_ergonomics_ref); + mark::check!(match_ergonomics_ref); assert_snapshot!( infer(r#" fn test() { diff --git a/crates/ra_hir_ty/src/tests/regression.rs b/crates/ra_hir_ty/src/tests/regression.rs index c2168222ee..1f004bd630 100644 --- a/crates/ra_hir_ty/src/tests/regression.rs +++ b/crates/ra_hir_ty/src/tests/regression.rs @@ -1,9 +1,10 @@ use insta::assert_snapshot; -use test_utils::covers; +use ra_db::fixture::WithFixture; +use test_utils::mark; + +use crate::test_db::TestDB; use super::infer; -use crate::test_db::TestDB; -use ra_db::fixture::WithFixture; #[test] fn bug_484() { @@ -89,8 +90,8 @@ fn quux() { #[test] fn recursive_vars() { - covers!(type_var_cycles_resolve_completely); - covers!(type_var_cycles_resolve_as_possible); + mark::check!(type_var_cycles_resolve_completely); + mark::check!(type_var_cycles_resolve_as_possible); assert_snapshot!( infer(r#" fn test() { @@ -112,8 +113,6 @@ fn test() { #[test] fn recursive_vars_2() { - covers!(type_var_cycles_resolve_completely); - covers!(type_var_cycles_resolve_as_possible); assert_snapshot!( infer(r#" fn test() { @@ -170,7 +169,7 @@ fn write() { #[test] fn infer_std_crash_2() { - covers!(type_var_resolves_to_int_var); + mark::check!(type_var_resolves_to_int_var); // caused "equating two type variables, ...", taken from std assert_snapshot!( infer(r#" diff --git a/crates/ra_hir_ty/src/tests/traits.rs b/crates/ra_hir_ty/src/tests/traits.rs index c49aacf98d..34f4b9039c 100644 --- a/crates/ra_hir_ty/src/tests/traits.rs +++ b/crates/ra_hir_ty/src/tests/traits.rs @@ -1,9 +1,10 @@ use insta::assert_snapshot; - use ra_db::fixture::WithFixture; +use test_utils::mark; + +use crate::test_db::TestDB; use super::{infer, infer_with_mismatches, type_at, type_at_pos}; -use crate::test_db::TestDB; #[test] fn infer_await() { @@ -301,7 +302,7 @@ fn test() { #[test] fn trait_default_method_self_bound_implements_trait() { - test_utils::covers!(trait_self_implements_self); + mark::check!(trait_self_implements_self); assert_snapshot!( infer(r#" trait Trait { @@ -324,7 +325,6 @@ trait Trait { #[test] fn trait_default_method_self_bound_implements_super_trait() { - test_utils::covers!(trait_self_implements_self); assert_snapshot!( infer(r#" trait SuperTrait { diff --git a/crates/ra_ide/src/call_info.rs b/crates/ra_ide/src/call_info.rs index 780a03c138..aa039e6fcd 100644 --- a/crates/ra_ide/src/call_info.rs +++ b/crates/ra_ide/src/call_info.rs @@ -5,7 +5,7 @@ use ra_syntax::{ ast::{self, ArgListOwner}, match_ast, AstNode, SyntaxNode, SyntaxToken, }; -use test_utils::tested_by; +use test_utils::mark; use crate::{CallInfo, FilePosition, FunctionSignature}; @@ -84,7 +84,7 @@ fn call_info_for_token(sema: &Semantics, token: SyntaxToken) -> Op let arg_list_range = arg_list.syntax().text_range(); if !arg_list_range.contains_inclusive(token.text_range().start()) { - tested_by!(call_info_bad_offset); + mark::hit!(call_info_bad_offset); return None; } @@ -213,7 +213,7 @@ impl CallInfo { #[cfg(test)] mod tests { - use test_utils::covers; + use test_utils::mark; use crate::mock_analysis::single_file_with_position; @@ -529,7 +529,7 @@ By default this method stops actor's `Context`."# #[test] fn call_info_bad_offset() { - covers!(call_info_bad_offset); + mark::check!(call_info_bad_offset); let (analysis, position) = single_file_with_position( r#"fn foo(x: u32, y: u32) -> u32 {x + y} fn bar() { foo <|> (3, ); }"#, diff --git a/crates/ra_ide/src/completion/complete_qualified_path.rs b/crates/ra_ide/src/completion/complete_qualified_path.rs index db7430454c..02ac0166b6 100644 --- a/crates/ra_ide/src/completion/complete_qualified_path.rs +++ b/crates/ra_ide/src/completion/complete_qualified_path.rs @@ -3,7 +3,7 @@ use hir::{Adt, HasVisibility, PathResolution, ScopeDef}; use ra_syntax::AstNode; use rustc_hash::FxHashSet; -use test_utils::tested_by; +use test_utils::mark; use crate::completion::{CompletionContext, Completions}; @@ -40,7 +40,7 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon if let Some(name_ref) = ctx.name_ref_syntax.as_ref() { if name_ref.syntax().text() == name.to_string().as_str() { // for `use self::foo<|>`, don't suggest `foo` as a completion - tested_by!(dont_complete_current_use); + mark::hit!(dont_complete_current_use); continue; } } @@ -147,7 +147,7 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon #[cfg(test)] mod tests { - use test_utils::covers; + use test_utils::mark; use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind}; use insta::assert_debug_snapshot; @@ -158,7 +158,7 @@ mod tests { #[test] fn dont_complete_current_use() { - covers!(dont_complete_current_use); + mark::check!(dont_complete_current_use); let completions = do_completion(r"use self::foo<|>;", CompletionKind::Reference); assert!(completions.is_empty()); } diff --git a/crates/ra_ide/src/completion/complete_unqualified_path.rs b/crates/ra_ide/src/completion/complete_unqualified_path.rs index bd40af1cb2..db791660a1 100644 --- a/crates/ra_ide/src/completion/complete_unqualified_path.rs +++ b/crates/ra_ide/src/completion/complete_unqualified_path.rs @@ -1,7 +1,7 @@ //! Completion of names from the current scope, e.g. locals and imported items. use hir::ScopeDef; -use test_utils::tested_by; +use test_utils::mark; use crate::completion::{CompletionContext, Completions}; use hir::{Adt, ModuleDef, Type}; @@ -30,7 +30,7 @@ pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC if ctx.use_item_syntax.is_some() { if let (ScopeDef::Unknown, Some(name_ref)) = (&res, &ctx.name_ref_syntax) { if name_ref.syntax().text() == name.to_string().as_str() { - tested_by!(self_fulfilling_completion); + mark::hit!(self_fulfilling_completion); return; } } @@ -66,7 +66,7 @@ fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &T #[cfg(test)] mod tests { use insta::assert_debug_snapshot; - use test_utils::covers; + use test_utils::mark; use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind}; @@ -76,7 +76,7 @@ mod tests { #[test] fn self_fulfilling_completion() { - covers!(self_fulfilling_completion); + mark::check!(self_fulfilling_completion); assert_debug_snapshot!( do_reference_completion( r#" diff --git a/crates/ra_ide/src/completion/presentation.rs b/crates/ra_ide/src/completion/presentation.rs index 077cf96477..440ffa31d4 100644 --- a/crates/ra_ide/src/completion/presentation.rs +++ b/crates/ra_ide/src/completion/presentation.rs @@ -3,7 +3,7 @@ use hir::{Docs, HasAttrs, HasSource, HirDisplay, ModPath, ScopeDef, StructKind, Type}; use ra_syntax::ast::NameOwner; use stdx::SepBy; -use test_utils::tested_by; +use test_utils::mark; use crate::{ completion::{ @@ -121,7 +121,7 @@ impl Completions { _ => false, }; if has_non_default_type_params { - tested_by!(inserts_angle_brackets_for_generics); + mark::hit!(inserts_angle_brackets_for_generics); completion_item = completion_item .lookup_by(local_name.clone()) .label(format!("{}<…>", local_name)) @@ -176,7 +176,7 @@ impl Completions { } None if needs_bang => builder.insert_text(format!("{}!", name)), _ => { - tested_by!(dont_insert_macro_call_parens_unncessary); + mark::hit!(dont_insert_macro_call_parens_unncessary); builder.insert_text(name) } }; @@ -330,14 +330,14 @@ pub(crate) fn compute_score( // FIXME: this should not fall back to string equality. let ty = &ty.display(ctx.db).to_string(); let (active_name, active_type) = if let Some(record_field) = &ctx.record_field_syntax { - tested_by!(test_struct_field_completion_in_record_lit); + mark::hit!(test_struct_field_completion_in_record_lit); let (struct_field, _local) = ctx.sema.resolve_record_field(record_field)?; ( struct_field.name(ctx.db).to_string(), struct_field.signature_ty(ctx.db).display(ctx.db).to_string(), ) } else if let Some(active_parameter) = &ctx.active_parameter { - tested_by!(test_struct_field_completion_in_func_call); + mark::hit!(test_struct_field_completion_in_func_call); (active_parameter.name.clone(), active_parameter.ty.clone()) } else { return None; @@ -398,7 +398,7 @@ impl Builder { None => return self, }; // If not an import, add parenthesis automatically. - tested_by!(inserts_parens_for_function_calls); + mark::hit!(inserts_parens_for_function_calls); let (snippet, label) = if params.is_empty() { (format!("{}()$0", name), format!("{}()", name)) @@ -457,7 +457,7 @@ fn guess_macro_braces(macro_name: &str, docs: &str) -> (&'static str, &'static s #[cfg(test)] mod tests { use insta::assert_debug_snapshot; - use test_utils::covers; + use test_utils::mark; use crate::completion::{ test_utils::{do_completion, do_completion_with_options}, @@ -607,7 +607,7 @@ mod tests { #[test] fn inserts_parens_for_function_calls() { - covers!(inserts_parens_for_function_calls); + mark::check!(inserts_parens_for_function_calls); assert_debug_snapshot!( do_reference_completion( r" @@ -992,7 +992,7 @@ mod tests { #[test] fn inserts_angle_brackets_for_generics() { - covers!(inserts_angle_brackets_for_generics); + mark::check!(inserts_angle_brackets_for_generics); assert_debug_snapshot!( do_reference_completion( r" @@ -1115,7 +1115,7 @@ mod tests { #[test] fn dont_insert_macro_call_parens_unncessary() { - covers!(dont_insert_macro_call_parens_unncessary); + mark::check!(dont_insert_macro_call_parens_unncessary); assert_debug_snapshot!( do_reference_completion( r" @@ -1181,7 +1181,7 @@ mod tests { #[test] fn test_struct_field_completion_in_func_call() { - covers!(test_struct_field_completion_in_func_call); + mark::check!(test_struct_field_completion_in_func_call); assert_debug_snapshot!( do_reference_completion( r" @@ -1271,7 +1271,7 @@ mod tests { #[test] fn test_struct_field_completion_in_record_lit() { - covers!(test_struct_field_completion_in_record_lit); + mark::check!(test_struct_field_completion_in_record_lit); assert_debug_snapshot!( do_reference_completion( r" diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs index 66125f2f59..83cb498f79 100644 --- a/crates/ra_ide/src/lib.rs +++ b/crates/ra_ide/src/lib.rs @@ -42,8 +42,6 @@ mod inlay_hints; mod expand_macro; mod ssr; -#[cfg(test)] -mod marks; #[cfg(test)] mod test_utils; diff --git a/crates/ra_ide/src/marks.rs b/crates/ra_ide/src/marks.rs deleted file mode 100644 index 51ca4dde3f..0000000000 --- a/crates/ra_ide/src/marks.rs +++ /dev/null @@ -1,16 +0,0 @@ -//! See test_utils/src/marks.rs - -test_utils::marks!( - inserts_angle_brackets_for_generics - inserts_parens_for_function_calls - call_info_bad_offset - dont_complete_current_use - test_resolve_parent_module_on_module_decl - search_filters_by_range - dont_insert_macro_call_parens_unncessary - self_fulfilling_completion - test_struct_field_completion_in_func_call - test_struct_field_completion_in_record_lit - test_rename_struct_field_for_shorthand - test_rename_local_for_field_shorthand -); diff --git a/crates/ra_ide/src/parent_module.rs b/crates/ra_ide/src/parent_module.rs index aaf4460dfa..a083fb1eb3 100644 --- a/crates/ra_ide/src/parent_module.rs +++ b/crates/ra_ide/src/parent_module.rs @@ -7,7 +7,7 @@ use ra_syntax::{ algo::find_node_at_offset, ast::{self, AstNode}, }; -use test_utils::tested_by; +use test_utils::mark; use crate::NavigationTarget; @@ -25,7 +25,7 @@ pub(crate) fn parent_module(db: &RootDatabase, position: FilePosition) -> Vec Vec { mod tests { use ra_cfg::CfgOptions; use ra_db::Env; - use test_utils::covers; + use test_utils::mark; use crate::{ mock_analysis::{analysis_and_position, MockAnalysis}, @@ -81,7 +81,7 @@ mod tests { #[test] fn test_resolve_parent_module_on_module_decl() { - covers!(test_resolve_parent_module_on_module_decl); + mark::check!(test_resolve_parent_module_on_module_decl); let (analysis, pos) = analysis_and_position( " //- /lib.rs diff --git a/crates/ra_ide/src/references/rename.rs b/crates/ra_ide/src/references/rename.rs index 68a53ad4b0..62ec8d85dd 100644 --- a/crates/ra_ide/src/references/rename.rs +++ b/crates/ra_ide/src/references/rename.rs @@ -9,7 +9,7 @@ use ra_syntax::{ }; use ra_text_edit::TextEdit; use std::convert::TryInto; -use test_utils::tested_by; +use test_utils::mark; use crate::{ references::find_all_refs, FilePosition, FileSystemEdit, RangeInfo, Reference, ReferenceKind, @@ -57,13 +57,13 @@ fn source_edit_from_reference(reference: Reference, new_name: &str) -> SourceFil let file_id = reference.file_range.file_id; let range = match reference.kind { ReferenceKind::FieldShorthandForField => { - tested_by!(test_rename_struct_field_for_shorthand); + mark::hit!(test_rename_struct_field_for_shorthand); replacement_text.push_str(new_name); replacement_text.push_str(": "); TextRange::new(reference.file_range.range.start(), reference.file_range.range.start()) } ReferenceKind::FieldShorthandForLocal => { - tested_by!(test_rename_local_for_field_shorthand); + mark::hit!(test_rename_local_for_field_shorthand); replacement_text.push_str(": "); replacement_text.push_str(new_name); TextRange::new(reference.file_range.range.end(), reference.file_range.range.end()) @@ -260,7 +260,7 @@ fn rename_reference( mod tests { use insta::assert_debug_snapshot; use ra_text_edit::TextEditBuilder; - use test_utils::{assert_eq_text, covers}; + use test_utils::{assert_eq_text, mark}; use crate::{ mock_analysis::analysis_and_position, mock_analysis::single_file_with_position, FileId, @@ -492,7 +492,7 @@ mod tests { #[test] fn test_rename_struct_field_for_shorthand() { - covers!(test_rename_struct_field_for_shorthand); + mark::check!(test_rename_struct_field_for_shorthand); test_rename( r#" struct Foo { @@ -522,7 +522,7 @@ mod tests { #[test] fn test_rename_local_for_field_shorthand() { - covers!(test_rename_local_for_field_shorthand); + mark::check!(test_rename_local_for_field_shorthand); test_rename( r#" struct Foo { diff --git a/crates/test_utils/src/lib.rs b/crates/test_utils/src/lib.rs index 4e05d464f0..be2cfbaa24 100644 --- a/crates/test_utils/src/lib.rs +++ b/crates/test_utils/src/lib.rs @@ -6,8 +6,6 @@ //! * Extracting markup (mainly, `<|>` markers) out of fixture strings. //! * marks (see the eponymous module). -#[macro_use] -pub mod marks; #[macro_use] pub mod mark; diff --git a/crates/test_utils/src/marks.rs b/crates/test_utils/src/marks.rs deleted file mode 100644 index f20fb978ee..0000000000 --- a/crates/test_utils/src/marks.rs +++ /dev/null @@ -1,81 +0,0 @@ -//! This module implements manually tracked test coverage, which is useful for -//! quickly finding a test responsible for testing a particular bit of code. -//! -//! See -//! for details, but the TL;DR is that you write your test as -//! -//! ``` -//! #[test] -//! fn test_foo() { -//! covers!(test_foo); -//! } -//! ``` -//! -//! and in the code under test you write -//! -//! ``` -//! # use test_utils::tested_by; -//! # fn some_condition() -> bool { true } -//! fn foo() { -//! if some_condition() { -//! tested_by!(test_foo); -//! } -//! } -//! ``` -//! -//! This module then checks that executing the test indeed covers the specified -//! function. This is useful if you come back to the `foo` function ten years -//! later and wonder where the test are: now you can grep for `test_foo`. -use std::sync::atomic::{AtomicUsize, Ordering}; - -#[macro_export] -macro_rules! tested_by { - ($ident:ident) => {{ - #[cfg(test)] - { - // sic! use call-site crate - crate::marks::$ident.fetch_add(1, std::sync::atomic::Ordering::SeqCst); - } - }}; -} - -#[macro_export] -macro_rules! covers { - // sic! use call-site crate - ($ident:ident) => { - let _checker = $crate::marks::MarkChecker::new(&crate::marks::$ident); - }; -} - -#[macro_export] -macro_rules! marks { - ($($ident:ident)*) => { - $( - #[allow(bad_style)] - pub static $ident: std::sync::atomic::AtomicUsize = - std::sync::atomic::AtomicUsize::new(0); - )* - }; -} - -pub struct MarkChecker { - mark: &'static AtomicUsize, - value_on_entry: usize, -} - -impl MarkChecker { - pub fn new(mark: &'static AtomicUsize) -> MarkChecker { - let value_on_entry = mark.load(Ordering::SeqCst); - MarkChecker { mark, value_on_entry } - } -} - -impl Drop for MarkChecker { - fn drop(&mut self) { - if std::thread::panicking() { - return; - } - let value_on_exit = self.mark.load(Ordering::SeqCst); - assert!(value_on_exit > self.value_on_entry, "mark was not hit") - } -} From cec773926f08e2d46b05d923165f8e73c420aa8c Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 20 May 2020 13:33:13 +0200 Subject: [PATCH 55/70] Split change_ and fix_ visibility assists --- .../src/handlers/change_visibility.rs | 507 +--------------- .../ra_assists/src/handlers/fix_visibility.rs | 555 ++++++++++++++++++ crates/ra_assists/src/lib.rs | 2 + crates/ra_assists/src/tests/generated.rs | 23 + docs/user/assists.md | 22 + 5 files changed, 603 insertions(+), 506 deletions(-) create mode 100644 crates/ra_assists/src/handlers/fix_visibility.rs diff --git a/crates/ra_assists/src/handlers/change_visibility.rs b/crates/ra_assists/src/handlers/change_visibility.rs index 71d55e0c30..1d9b8e645e 100644 --- a/crates/ra_assists/src/handlers/change_visibility.rs +++ b/crates/ra_assists/src/handlers/change_visibility.rs @@ -5,14 +5,11 @@ use ra_syntax::{ ATTR, COMMENT, CONST_DEF, ENUM_DEF, FN_DEF, MODULE, STRUCT_DEF, TRAIT_DEF, VISIBILITY, WHITESPACE, }, - SyntaxNode, TextRange, TextSize, T, + SyntaxNode, TextSize, T, }; - -use hir::{db::HirDatabase, HasSource, HasVisibility, PathResolution}; use test_utils::mark; use crate::{AssistContext, AssistId, Assists}; -use ra_db::FileId; // Assist: change_visibility // @@ -30,8 +27,6 @@ pub(crate) fn change_visibility(acc: &mut Assists, ctx: &AssistContext) -> Optio return change_vis(acc, vis); } add_vis(acc, ctx) - .or_else(|| add_vis_to_referenced_module_def(acc, ctx)) - .or_else(|| add_vis_to_referenced_record_field(acc, ctx)) } fn add_vis(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { @@ -77,143 +72,6 @@ fn add_vis(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { }) } -fn add_vis_to_referenced_module_def(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { - let path: ast::Path = ctx.find_node_at_offset()?; - let path_res = ctx.sema.resolve_path(&path)?; - let def = match path_res { - PathResolution::Def(def) => def, - _ => return None, - }; - - let current_module = ctx.sema.scope(&path.syntax()).module()?; - let target_module = def.module(ctx.db)?; - - let vis = target_module.visibility_of(ctx.db, &def)?; - if vis.is_visible_from(ctx.db, current_module.into()) { - return None; - }; - - let (offset, target, target_file, target_name) = target_data_for_def(ctx.db, def)?; - - let missing_visibility = - if current_module.krate() == target_module.krate() { "pub(crate)" } else { "pub" }; - - let assist_label = match target_name { - None => format!("Change visibility to {}", missing_visibility), - Some(name) => format!("Change visibility of {} to {}", name, missing_visibility), - }; - - acc.add(AssistId("change_visibility"), assist_label, target, |edit| { - edit.set_file(target_file); - edit.insert(offset, format!("{} ", missing_visibility)); - edit.set_cursor(offset); - }) -} - -fn add_vis_to_referenced_record_field(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { - let record_field: ast::RecordField = ctx.find_node_at_offset()?; - let (record_field_def, _) = ctx.sema.resolve_record_field(&record_field)?; - - let current_module = ctx.sema.scope(record_field.syntax()).module()?; - let visibility = record_field_def.visibility(ctx.db); - if visibility.is_visible_from(ctx.db, current_module.into()) { - return None; - } - - let parent = record_field_def.parent_def(ctx.db); - let parent_name = parent.name(ctx.db); - let target_module = parent.module(ctx.db); - - let in_file_source = record_field_def.source(ctx.db); - let (offset, target) = match in_file_source.value { - hir::FieldSource::Named(it) => { - let s = it.syntax(); - (vis_offset(s), s.text_range()) - } - hir::FieldSource::Pos(it) => { - let s = it.syntax(); - (vis_offset(s), s.text_range()) - } - }; - - let missing_visibility = - if current_module.krate() == target_module.krate() { "pub(crate)" } else { "pub" }; - let target_file = in_file_source.file_id.original_file(ctx.db); - - let target_name = record_field_def.name(ctx.db); - let assist_label = - format!("Change visibility of {}.{} to {}", parent_name, target_name, missing_visibility); - - acc.add(AssistId("change_visibility"), assist_label, target, |edit| { - edit.set_file(target_file); - edit.insert(offset, format!("{} ", missing_visibility)); - edit.set_cursor(offset) - }) -} - -fn target_data_for_def( - db: &dyn HirDatabase, - def: hir::ModuleDef, -) -> Option<(TextSize, TextRange, FileId, Option)> { - fn offset_target_and_file_id( - db: &dyn HirDatabase, - x: S, - ) -> (TextSize, TextRange, FileId) - where - S: HasSource, - Ast: AstNode, - { - let source = x.source(db); - let in_file_syntax = source.syntax(); - let file_id = in_file_syntax.file_id; - let syntax = in_file_syntax.value; - (vis_offset(syntax), syntax.text_range(), file_id.original_file(db.upcast())) - } - - let target_name; - let (offset, target, target_file) = match def { - hir::ModuleDef::Function(f) => { - target_name = Some(f.name(db)); - offset_target_and_file_id(db, f) - } - hir::ModuleDef::Adt(adt) => { - target_name = Some(adt.name(db)); - match adt { - hir::Adt::Struct(s) => offset_target_and_file_id(db, s), - hir::Adt::Union(u) => offset_target_and_file_id(db, u), - hir::Adt::Enum(e) => offset_target_and_file_id(db, e), - } - } - hir::ModuleDef::Const(c) => { - target_name = c.name(db); - offset_target_and_file_id(db, c) - } - hir::ModuleDef::Static(s) => { - target_name = s.name(db); - offset_target_and_file_id(db, s) - } - hir::ModuleDef::Trait(t) => { - target_name = Some(t.name(db)); - offset_target_and_file_id(db, t) - } - hir::ModuleDef::TypeAlias(t) => { - target_name = Some(t.name(db)); - offset_target_and_file_id(db, t) - } - hir::ModuleDef::Module(m) => { - target_name = m.name(db); - let in_file_source = m.declaration_source(db)?; - let file_id = in_file_source.file_id.original_file(db.upcast()); - let syntax = in_file_source.value.syntax(); - (vis_offset(syntax), syntax.text_range(), file_id) - } - // Enum variants can't be private, we can't modify builtin types - hir::ModuleDef::EnumVariant(_) | hir::ModuleDef::BuiltinType(_) => return None, - }; - - Some((offset, target, target_file, target_name)) -} - fn vis_offset(node: &SyntaxNode) -> TextSize { node.children_with_tokens() .skip_while(|it| match it.kind() { @@ -333,193 +191,6 @@ mod tests { ) } - #[test] - fn change_visibility_of_fn_via_path() { - check_assist( - change_visibility, - r"mod foo { fn foo() {} } - fn main() { foo::foo<|>() } ", - r"mod foo { <|>pub(crate) fn foo() {} } - fn main() { foo::foo() } ", - ); - check_assist_not_applicable( - change_visibility, - r"mod foo { pub fn foo() {} } - fn main() { foo::foo<|>() } ", - ) - } - - #[test] - fn change_visibility_of_adt_in_submodule_via_path() { - check_assist( - change_visibility, - r"mod foo { struct Foo; } - fn main() { foo::Foo<|> } ", - r"mod foo { <|>pub(crate) struct Foo; } - fn main() { foo::Foo } ", - ); - check_assist_not_applicable( - change_visibility, - r"mod foo { pub struct Foo; } - fn main() { foo::Foo<|> } ", - ); - check_assist( - change_visibility, - r"mod foo { enum Foo; } - fn main() { foo::Foo<|> } ", - r"mod foo { <|>pub(crate) enum Foo; } - fn main() { foo::Foo } ", - ); - check_assist_not_applicable( - change_visibility, - r"mod foo { pub enum Foo; } - fn main() { foo::Foo<|> } ", - ); - check_assist( - change_visibility, - r"mod foo { union Foo; } - fn main() { foo::Foo<|> } ", - r"mod foo { <|>pub(crate) union Foo; } - fn main() { foo::Foo } ", - ); - check_assist_not_applicable( - change_visibility, - r"mod foo { pub union Foo; } - fn main() { foo::Foo<|> } ", - ); - } - - #[test] - fn change_visibility_of_adt_in_other_file_via_path() { - check_assist( - change_visibility, - r" - //- /main.rs - mod foo; - fn main() { foo::Foo<|> } - - //- /foo.rs - struct Foo; - ", - r"<|>pub(crate) struct Foo; - -", - ); - } - - #[test] - fn change_visibility_of_struct_field_via_path() { - check_assist( - change_visibility, - r"mod foo { pub struct Foo { bar: (), } } - fn main() { foo::Foo { <|>bar: () }; } ", - r"mod foo { pub struct Foo { <|>pub(crate) bar: (), } } - fn main() { foo::Foo { bar: () }; } ", - ); - check_assist( - change_visibility, - r"//- /lib.rs - mod foo; - fn main() { foo::Foo { <|>bar: () }; } - //- /foo.rs - pub struct Foo { bar: () } - ", - r"pub struct Foo { <|>pub(crate) bar: () } - -", - ); - check_assist_not_applicable( - change_visibility, - r"mod foo { pub struct Foo { pub bar: (), } } - fn main() { foo::Foo { <|>bar: () }; } ", - ); - check_assist_not_applicable( - change_visibility, - r"//- /lib.rs - mod foo; - fn main() { foo::Foo { <|>bar: () }; } - //- /foo.rs - pub struct Foo { pub bar: () } - ", - ); - } - - #[test] - fn change_visibility_of_enum_variant_field_via_path() { - check_assist( - change_visibility, - r"mod foo { pub enum Foo { Bar { bar: () } } } - fn main() { foo::Foo::Bar { <|>bar: () }; } ", - r"mod foo { pub enum Foo { Bar { <|>pub(crate) bar: () } } } - fn main() { foo::Foo::Bar { bar: () }; } ", - ); - check_assist( - change_visibility, - r"//- /lib.rs - mod foo; - fn main() { foo::Foo::Bar { <|>bar: () }; } - //- /foo.rs - pub enum Foo { Bar { bar: () } } - ", - r"pub enum Foo { Bar { <|>pub(crate) bar: () } } - -", - ); - check_assist_not_applicable( - change_visibility, - r"mod foo { pub struct Foo { pub bar: (), } } - fn main() { foo::Foo { <|>bar: () }; } ", - ); - check_assist_not_applicable( - change_visibility, - r"//- /lib.rs - mod foo; - fn main() { foo::Foo { <|>bar: () }; } - //- /foo.rs - pub struct Foo { pub bar: () } - ", - ); - } - - #[test] - #[ignore] - // FIXME reenable this test when `Semantics::resolve_record_field` works with union fields - fn change_visibility_of_union_field_via_path() { - check_assist( - change_visibility, - r"mod foo { pub union Foo { bar: (), } } - fn main() { foo::Foo { <|>bar: () }; } ", - r"mod foo { pub union Foo { <|>pub(crate) bar: (), } } - fn main() { foo::Foo { bar: () }; } ", - ); - check_assist( - change_visibility, - r"//- /lib.rs - mod foo; - fn main() { foo::Foo { <|>bar: () }; } - //- /foo.rs - pub union Foo { bar: () } - ", - r"pub union Foo { <|>pub(crate) bar: () } - -", - ); - check_assist_not_applicable( - change_visibility, - r"mod foo { pub union Foo { pub bar: (), } } - fn main() { foo::Foo { <|>bar: () }; } ", - ); - check_assist_not_applicable( - change_visibility, - r"//- /lib.rs - mod foo; - fn main() { foo::Foo { <|>bar: () }; } - //- /foo.rs - pub union Foo { pub bar: () } - ", - ); - } - #[test] fn not_applicable_for_enum_variants() { check_assist_not_applicable( @@ -529,182 +200,6 @@ mod tests { ); } - #[test] - fn change_visibility_of_const_via_path() { - check_assist( - change_visibility, - r"mod foo { const FOO: () = (); } - fn main() { foo::FOO<|> } ", - r"mod foo { <|>pub(crate) const FOO: () = (); } - fn main() { foo::FOO } ", - ); - check_assist_not_applicable( - change_visibility, - r"mod foo { pub const FOO: () = (); } - fn main() { foo::FOO<|> } ", - ); - } - - #[test] - fn change_visibility_of_static_via_path() { - check_assist( - change_visibility, - r"mod foo { static FOO: () = (); } - fn main() { foo::FOO<|> } ", - r"mod foo { <|>pub(crate) static FOO: () = (); } - fn main() { foo::FOO } ", - ); - check_assist_not_applicable( - change_visibility, - r"mod foo { pub static FOO: () = (); } - fn main() { foo::FOO<|> } ", - ); - } - - #[test] - fn change_visibility_of_trait_via_path() { - check_assist( - change_visibility, - r"mod foo { trait Foo { fn foo(&self) {} } } - fn main() { let x: &dyn foo::<|>Foo; } ", - r"mod foo { <|>pub(crate) trait Foo { fn foo(&self) {} } } - fn main() { let x: &dyn foo::Foo; } ", - ); - check_assist_not_applicable( - change_visibility, - r"mod foo { pub trait Foo { fn foo(&self) {} } } - fn main() { let x: &dyn foo::Foo<|>; } ", - ); - } - - #[test] - fn change_visibility_of_type_alias_via_path() { - check_assist( - change_visibility, - r"mod foo { type Foo = (); } - fn main() { let x: foo::Foo<|>; } ", - r"mod foo { <|>pub(crate) type Foo = (); } - fn main() { let x: foo::Foo; } ", - ); - check_assist_not_applicable( - change_visibility, - r"mod foo { pub type Foo = (); } - fn main() { let x: foo::Foo<|>; } ", - ); - } - - #[test] - fn change_visibility_of_module_via_path() { - check_assist( - change_visibility, - r"mod foo { mod bar { fn bar() {} } } - fn main() { foo::bar<|>::bar(); } ", - r"mod foo { <|>pub(crate) mod bar { fn bar() {} } } - fn main() { foo::bar::bar(); } ", - ); - - check_assist( - change_visibility, - r" - //- /main.rs - mod foo; - fn main() { foo::bar<|>::baz(); } - - //- /foo.rs - mod bar { - pub fn baz() {} - } - ", - r"<|>pub(crate) mod bar { - pub fn baz() {} -} - -", - ); - - check_assist_not_applicable( - change_visibility, - r"mod foo { pub mod bar { pub fn bar() {} } } - fn main() { foo::bar<|>::bar(); } ", - ); - } - - #[test] - fn change_visibility_of_inline_module_in_other_file_via_path() { - check_assist( - change_visibility, - r" - //- /main.rs - mod foo; - fn main() { foo::bar<|>::baz(); } - - //- /foo.rs - mod bar; - - //- /foo/bar.rs - pub fn baz() {} - } - ", - r"<|>pub(crate) mod bar; -", - ); - } - - #[test] - fn change_visibility_of_module_declaration_in_other_file_via_path() { - check_assist( - change_visibility, - r"//- /main.rs - mod foo; - fn main() { foo::bar<|>>::baz(); } - - //- /foo.rs - mod bar { - pub fn baz() {} - }", - r"<|>pub(crate) mod bar { - pub fn baz() {} -} -", - ); - } - - #[test] - #[ignore] - // FIXME handle reexports properly - fn change_visibility_of_reexport() { - check_assist( - change_visibility, - r" - mod foo { - use bar::Baz; - mod bar { pub(super) struct Baz; } - } - foo::Baz<|> - ", - r" - mod foo { - <|>pub(crate) use bar::Baz; - mod bar { pub(super) struct Baz; } - } - foo::Baz - ", - ) - } - - #[test] - fn adds_pub_when_target_is_in_another_crate() { - check_assist( - change_visibility, - r"//- /main.rs crate:a deps:foo - foo::Bar<|> - //- /lib.rs crate:foo - struct Bar;", - r"<|>pub struct Bar; -", - ) - } - #[test] fn change_visibility_target() { check_assist_target(change_visibility, "<|>fn foo() {}", "fn"); diff --git a/crates/ra_assists/src/handlers/fix_visibility.rs b/crates/ra_assists/src/handlers/fix_visibility.rs new file mode 100644 index 0000000000..48ce07ca5a --- /dev/null +++ b/crates/ra_assists/src/handlers/fix_visibility.rs @@ -0,0 +1,555 @@ +use hir::{db::HirDatabase, HasSource, HasVisibility, PathResolution}; +use ra_db::FileId; +use ra_syntax::{ + ast, AstNode, + SyntaxKind::{ATTR, COMMENT, WHITESPACE}, + SyntaxNode, TextRange, TextSize, +}; + +use crate::{AssistContext, AssistId, Assists}; + +// FIXME: this really should be a fix for diagnostic, rather than an assist. + +// Assist: fix_visibility +// +// Makes inaccessible item public. +// +// ``` +// mod m { +// fn frobnicate() {} +// } +// fn main() { +// m::frobnicate<|>() {} +// } +// ``` +// -> +// ``` +// mod m { +// pub(crate) fn frobnicate() {} +// } +// fn main() { +// m::frobnicate() {} +// } +// ``` +pub(crate) fn fix_visibility(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { + add_vis_to_referenced_module_def(acc, ctx) + .or_else(|| add_vis_to_referenced_record_field(acc, ctx)) +} + +fn add_vis_to_referenced_module_def(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { + let path: ast::Path = ctx.find_node_at_offset()?; + let path_res = ctx.sema.resolve_path(&path)?; + let def = match path_res { + PathResolution::Def(def) => def, + _ => return None, + }; + + let current_module = ctx.sema.scope(&path.syntax()).module()?; + let target_module = def.module(ctx.db)?; + + let vis = target_module.visibility_of(ctx.db, &def)?; + if vis.is_visible_from(ctx.db, current_module.into()) { + return None; + }; + + let (offset, target, target_file, target_name) = target_data_for_def(ctx.db, def)?; + + let missing_visibility = + if current_module.krate() == target_module.krate() { "pub(crate)" } else { "pub" }; + + let assist_label = match target_name { + None => format!("Change visibility to {}", missing_visibility), + Some(name) => format!("Change visibility of {} to {}", name, missing_visibility), + }; + + acc.add(AssistId("fix_visibility"), assist_label, target, |edit| { + edit.set_file(target_file); + edit.insert(offset, format!("{} ", missing_visibility)); + edit.set_cursor(offset); + }) +} + +fn add_vis_to_referenced_record_field(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { + let record_field: ast::RecordField = ctx.find_node_at_offset()?; + let (record_field_def, _) = ctx.sema.resolve_record_field(&record_field)?; + + let current_module = ctx.sema.scope(record_field.syntax()).module()?; + let visibility = record_field_def.visibility(ctx.db); + if visibility.is_visible_from(ctx.db, current_module.into()) { + return None; + } + + let parent = record_field_def.parent_def(ctx.db); + let parent_name = parent.name(ctx.db); + let target_module = parent.module(ctx.db); + + let in_file_source = record_field_def.source(ctx.db); + let (offset, target) = match in_file_source.value { + hir::FieldSource::Named(it) => { + let s = it.syntax(); + (vis_offset(s), s.text_range()) + } + hir::FieldSource::Pos(it) => { + let s = it.syntax(); + (vis_offset(s), s.text_range()) + } + }; + + let missing_visibility = + if current_module.krate() == target_module.krate() { "pub(crate)" } else { "pub" }; + let target_file = in_file_source.file_id.original_file(ctx.db); + + let target_name = record_field_def.name(ctx.db); + let assist_label = + format!("Change visibility of {}.{} to {}", parent_name, target_name, missing_visibility); + + acc.add(AssistId("fix_visibility"), assist_label, target, |edit| { + edit.set_file(target_file); + edit.insert(offset, format!("{} ", missing_visibility)); + edit.set_cursor(offset) + }) +} + +fn target_data_for_def( + db: &dyn HirDatabase, + def: hir::ModuleDef, +) -> Option<(TextSize, TextRange, FileId, Option)> { + fn offset_target_and_file_id( + db: &dyn HirDatabase, + x: S, + ) -> (TextSize, TextRange, FileId) + where + S: HasSource, + Ast: AstNode, + { + let source = x.source(db); + let in_file_syntax = source.syntax(); + let file_id = in_file_syntax.file_id; + let syntax = in_file_syntax.value; + (vis_offset(syntax), syntax.text_range(), file_id.original_file(db.upcast())) + } + + let target_name; + let (offset, target, target_file) = match def { + hir::ModuleDef::Function(f) => { + target_name = Some(f.name(db)); + offset_target_and_file_id(db, f) + } + hir::ModuleDef::Adt(adt) => { + target_name = Some(adt.name(db)); + match adt { + hir::Adt::Struct(s) => offset_target_and_file_id(db, s), + hir::Adt::Union(u) => offset_target_and_file_id(db, u), + hir::Adt::Enum(e) => offset_target_and_file_id(db, e), + } + } + hir::ModuleDef::Const(c) => { + target_name = c.name(db); + offset_target_and_file_id(db, c) + } + hir::ModuleDef::Static(s) => { + target_name = s.name(db); + offset_target_and_file_id(db, s) + } + hir::ModuleDef::Trait(t) => { + target_name = Some(t.name(db)); + offset_target_and_file_id(db, t) + } + hir::ModuleDef::TypeAlias(t) => { + target_name = Some(t.name(db)); + offset_target_and_file_id(db, t) + } + hir::ModuleDef::Module(m) => { + target_name = m.name(db); + let in_file_source = m.declaration_source(db)?; + let file_id = in_file_source.file_id.original_file(db.upcast()); + let syntax = in_file_source.value.syntax(); + (vis_offset(syntax), syntax.text_range(), file_id) + } + // Enum variants can't be private, we can't modify builtin types + hir::ModuleDef::EnumVariant(_) | hir::ModuleDef::BuiltinType(_) => return None, + }; + + Some((offset, target, target_file, target_name)) +} + +fn vis_offset(node: &SyntaxNode) -> TextSize { + node.children_with_tokens() + .skip_while(|it| match it.kind() { + WHITESPACE | COMMENT | ATTR => true, + _ => false, + }) + .next() + .map(|it| it.text_range().start()) + .unwrap_or_else(|| node.text_range().start()) +} + +#[cfg(test)] +mod tests { + use crate::tests::{check_assist, check_assist_not_applicable}; + + use super::*; + + #[test] + fn fix_visibility_of_fn() { + check_assist( + fix_visibility, + r"mod foo { fn foo() {} } + fn main() { foo::foo<|>() } ", + r"mod foo { <|>pub(crate) fn foo() {} } + fn main() { foo::foo() } ", + ); + check_assist_not_applicable( + fix_visibility, + r"mod foo { pub fn foo() {} } + fn main() { foo::foo<|>() } ", + ) + } + + #[test] + fn fix_visibility_of_adt_in_submodule() { + check_assist( + fix_visibility, + r"mod foo { struct Foo; } + fn main() { foo::Foo<|> } ", + r"mod foo { <|>pub(crate) struct Foo; } + fn main() { foo::Foo } ", + ); + check_assist_not_applicable( + fix_visibility, + r"mod foo { pub struct Foo; } + fn main() { foo::Foo<|> } ", + ); + check_assist( + fix_visibility, + r"mod foo { enum Foo; } + fn main() { foo::Foo<|> } ", + r"mod foo { <|>pub(crate) enum Foo; } + fn main() { foo::Foo } ", + ); + check_assist_not_applicable( + fix_visibility, + r"mod foo { pub enum Foo; } + fn main() { foo::Foo<|> } ", + ); + check_assist( + fix_visibility, + r"mod foo { union Foo; } + fn main() { foo::Foo<|> } ", + r"mod foo { <|>pub(crate) union Foo; } + fn main() { foo::Foo } ", + ); + check_assist_not_applicable( + fix_visibility, + r"mod foo { pub union Foo; } + fn main() { foo::Foo<|> } ", + ); + } + + #[test] + fn fix_visibility_of_adt_in_other_file() { + check_assist( + fix_visibility, + r" + //- /main.rs + mod foo; + fn main() { foo::Foo<|> } + + //- /foo.rs + struct Foo; + ", + r"<|>pub(crate) struct Foo; + +", + ); + } + + #[test] + fn fix_visibility_of_struct_field() { + check_assist( + fix_visibility, + r"mod foo { pub struct Foo { bar: (), } } + fn main() { foo::Foo { <|>bar: () }; } ", + r"mod foo { pub struct Foo { <|>pub(crate) bar: (), } } + fn main() { foo::Foo { bar: () }; } ", + ); + check_assist( + fix_visibility, + r"//- /lib.rs + mod foo; + fn main() { foo::Foo { <|>bar: () }; } + //- /foo.rs + pub struct Foo { bar: () } + ", + r"pub struct Foo { <|>pub(crate) bar: () } + +", + ); + check_assist_not_applicable( + fix_visibility, + r"mod foo { pub struct Foo { pub bar: (), } } + fn main() { foo::Foo { <|>bar: () }; } ", + ); + check_assist_not_applicable( + fix_visibility, + r"//- /lib.rs + mod foo; + fn main() { foo::Foo { <|>bar: () }; } + //- /foo.rs + pub struct Foo { pub bar: () } + ", + ); + } + + #[test] + fn fix_visibility_of_enum_variant_field() { + check_assist( + fix_visibility, + r"mod foo { pub enum Foo { Bar { bar: () } } } + fn main() { foo::Foo::Bar { <|>bar: () }; } ", + r"mod foo { pub enum Foo { Bar { <|>pub(crate) bar: () } } } + fn main() { foo::Foo::Bar { bar: () }; } ", + ); + check_assist( + fix_visibility, + r"//- /lib.rs + mod foo; + fn main() { foo::Foo::Bar { <|>bar: () }; } + //- /foo.rs + pub enum Foo { Bar { bar: () } } + ", + r"pub enum Foo { Bar { <|>pub(crate) bar: () } } + +", + ); + check_assist_not_applicable( + fix_visibility, + r"mod foo { pub struct Foo { pub bar: (), } } + fn main() { foo::Foo { <|>bar: () }; } ", + ); + check_assist_not_applicable( + fix_visibility, + r"//- /lib.rs + mod foo; + fn main() { foo::Foo { <|>bar: () }; } + //- /foo.rs + pub struct Foo { pub bar: () } + ", + ); + } + + #[test] + #[ignore] + // FIXME reenable this test when `Semantics::resolve_record_field` works with union fields + fn fix_visibility_of_union_field() { + check_assist( + fix_visibility, + r"mod foo { pub union Foo { bar: (), } } + fn main() { foo::Foo { <|>bar: () }; } ", + r"mod foo { pub union Foo { <|>pub(crate) bar: (), } } + fn main() { foo::Foo { bar: () }; } ", + ); + check_assist( + fix_visibility, + r"//- /lib.rs + mod foo; + fn main() { foo::Foo { <|>bar: () }; } + //- /foo.rs + pub union Foo { bar: () } + ", + r"pub union Foo { <|>pub(crate) bar: () } + +", + ); + check_assist_not_applicable( + fix_visibility, + r"mod foo { pub union Foo { pub bar: (), } } + fn main() { foo::Foo { <|>bar: () }; } ", + ); + check_assist_not_applicable( + fix_visibility, + r"//- /lib.rs + mod foo; + fn main() { foo::Foo { <|>bar: () }; } + //- /foo.rs + pub union Foo { pub bar: () } + ", + ); + } + + #[test] + fn fix_visibility_of_const() { + check_assist( + fix_visibility, + r"mod foo { const FOO: () = (); } + fn main() { foo::FOO<|> } ", + r"mod foo { <|>pub(crate) const FOO: () = (); } + fn main() { foo::FOO } ", + ); + check_assist_not_applicable( + fix_visibility, + r"mod foo { pub const FOO: () = (); } + fn main() { foo::FOO<|> } ", + ); + } + + #[test] + fn fix_visibility_of_static() { + check_assist( + fix_visibility, + r"mod foo { static FOO: () = (); } + fn main() { foo::FOO<|> } ", + r"mod foo { <|>pub(crate) static FOO: () = (); } + fn main() { foo::FOO } ", + ); + check_assist_not_applicable( + fix_visibility, + r"mod foo { pub static FOO: () = (); } + fn main() { foo::FOO<|> } ", + ); + } + + #[test] + fn fix_visibility_of_trait() { + check_assist( + fix_visibility, + r"mod foo { trait Foo { fn foo(&self) {} } } + fn main() { let x: &dyn foo::<|>Foo; } ", + r"mod foo { <|>pub(crate) trait Foo { fn foo(&self) {} } } + fn main() { let x: &dyn foo::Foo; } ", + ); + check_assist_not_applicable( + fix_visibility, + r"mod foo { pub trait Foo { fn foo(&self) {} } } + fn main() { let x: &dyn foo::Foo<|>; } ", + ); + } + + #[test] + fn fix_visibility_of_type_alias() { + check_assist( + fix_visibility, + r"mod foo { type Foo = (); } + fn main() { let x: foo::Foo<|>; } ", + r"mod foo { <|>pub(crate) type Foo = (); } + fn main() { let x: foo::Foo; } ", + ); + check_assist_not_applicable( + fix_visibility, + r"mod foo { pub type Foo = (); } + fn main() { let x: foo::Foo<|>; } ", + ); + } + + #[test] + fn fix_visibility_of_module() { + check_assist( + fix_visibility, + r"mod foo { mod bar { fn bar() {} } } + fn main() { foo::bar<|>::bar(); } ", + r"mod foo { <|>pub(crate) mod bar { fn bar() {} } } + fn main() { foo::bar::bar(); } ", + ); + + check_assist( + fix_visibility, + r" + //- /main.rs + mod foo; + fn main() { foo::bar<|>::baz(); } + + //- /foo.rs + mod bar { + pub fn baz() {} + } + ", + r"<|>pub(crate) mod bar { + pub fn baz() {} +} + +", + ); + + check_assist_not_applicable( + fix_visibility, + r"mod foo { pub mod bar { pub fn bar() {} } } + fn main() { foo::bar<|>::bar(); } ", + ); + } + + #[test] + fn fix_visibility_of_inline_module_in_other_file() { + check_assist( + fix_visibility, + r" + //- /main.rs + mod foo; + fn main() { foo::bar<|>::baz(); } + + //- /foo.rs + mod bar; + + //- /foo/bar.rs + pub fn baz() {} + } + ", + r"<|>pub(crate) mod bar; +", + ); + } + + #[test] + fn fix_visibility_of_module_declaration_in_other_file() { + check_assist( + fix_visibility, + r"//- /main.rs + mod foo; + fn main() { foo::bar<|>>::baz(); } + + //- /foo.rs + mod bar { + pub fn baz() {} + }", + r"<|>pub(crate) mod bar { + pub fn baz() {} +} +", + ); + } + + #[test] + fn adds_pub_when_target_is_in_another_crate() { + check_assist( + fix_visibility, + r"//- /main.rs crate:a deps:foo + foo::Bar<|> + //- /lib.rs crate:foo + struct Bar;", + r"<|>pub struct Bar; +", + ) + } + + #[test] + #[ignore] + // FIXME handle reexports properly + fn fix_visibility_of_reexport() { + check_assist( + fix_visibility, + r" + mod foo { + use bar::Baz; + mod bar { pub(super) struct Baz; } + } + foo::Baz<|> + ", + r" + mod foo { + <|>pub(crate) use bar::Baz; + mod bar { pub(super) struct Baz; } + } + foo::Baz + ", + ) + } +} diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs index a4a497c736..464bc03dde 100644 --- a/crates/ra_assists/src/lib.rs +++ b/crates/ra_assists/src/lib.rs @@ -116,6 +116,7 @@ mod handlers { mod change_visibility; mod early_return; mod fill_match_arms; + mod fix_visibility; mod flip_binexpr; mod flip_comma; mod flip_trait_bound; @@ -154,6 +155,7 @@ mod handlers { change_visibility::change_visibility, early_return::convert_to_guarded_return, fill_match_arms::fill_match_arms, + fix_visibility::fix_visibility, flip_binexpr::flip_binexpr, flip_comma::flip_comma, flip_trait_bound::flip_trait_bound, diff --git a/crates/ra_assists/src/tests/generated.rs b/crates/ra_assists/src/tests/generated.rs index cd6129dc58..417ee89a88 100644 --- a/crates/ra_assists/src/tests/generated.rs +++ b/crates/ra_assists/src/tests/generated.rs @@ -344,6 +344,29 @@ fn handle(action: Action) { ) } +#[test] +fn doctest_fix_visibility() { + check_doc_test( + "fix_visibility", + r#####" +mod m { + fn frobnicate() {} +} +fn main() { + m::frobnicate<|>() {} +} +"#####, + r#####" +mod m { + pub(crate) fn frobnicate() {} +} +fn main() { + m::frobnicate() {} +} +"#####, + ) +} + #[test] fn doctest_flip_binexpr() { check_doc_test( diff --git a/docs/user/assists.md b/docs/user/assists.md index 006ec4d547..0ae242389e 100644 --- a/docs/user/assists.md +++ b/docs/user/assists.md @@ -331,6 +331,28 @@ fn handle(action: Action) { } ``` +## `fix_visibility` + +Makes inaccessible item public. + +```rust +// BEFORE +mod m { + fn frobnicate() {} +} +fn main() { + m::frobnicate┃() {} +} + +// AFTER +mod m { + pub(crate) fn frobnicate() {} +} +fn main() { + m::frobnicate() {} +} +``` + ## `flip_binexpr` Flips operands of a binary expression. From 74da16f6f95aec839842d1f920fec9e40b6f80a4 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 20 May 2020 13:41:58 +0200 Subject: [PATCH 56/70] Cleanup imports --- crates/ra_assists/src/handlers/unwrap_block.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/crates/ra_assists/src/handlers/unwrap_block.rs b/crates/ra_assists/src/handlers/unwrap_block.rs index e52ec557e1..b76182d792 100644 --- a/crates/ra_assists/src/handlers/unwrap_block.rs +++ b/crates/ra_assists/src/handlers/unwrap_block.rs @@ -1,8 +1,10 @@ -use crate::{AssistContext, AssistId, Assists}; - -use ast::{ElseBranch, Expr, LoopBodyOwner}; use ra_fmt::unwrap_trivial_block; -use ra_syntax::{ast, match_ast, AstNode, TextRange, T}; +use ra_syntax::{ + ast::{self, ElseBranch, Expr, LoopBodyOwner}, + match_ast, AstNode, TextRange, T, +}; + +use crate::{AssistContext, AssistId, Assists}; // Assist: unwrap_block // From a622b54ac029994328a99e30ee3169c21de498b7 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 20 May 2020 13:48:31 +0200 Subject: [PATCH 57/70] Don't set cursor in change_visibility --- crates/ra_assists/src/handlers/change_visibility.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/crates/ra_assists/src/handlers/change_visibility.rs b/crates/ra_assists/src/handlers/change_visibility.rs index 1d9b8e645e..fbe459c9ca 100644 --- a/crates/ra_assists/src/handlers/change_visibility.rs +++ b/crates/ra_assists/src/handlers/change_visibility.rs @@ -68,7 +68,6 @@ fn add_vis(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { acc.add(AssistId("change_visibility"), "Change visibility to pub(crate)", target, |edit| { edit.insert(offset, "pub(crate) "); - edit.set_cursor(offset); }) } @@ -92,7 +91,6 @@ fn change_vis(acc: &mut Assists, vis: ast::Visibility) -> Option<()> { target, |edit| { edit.replace(vis.syntax().text_range(), "pub(crate)"); - edit.set_cursor(vis.syntax().text_range().start()) }, ); } @@ -104,7 +102,6 @@ fn change_vis(acc: &mut Assists, vis: ast::Visibility) -> Option<()> { target, |edit| { edit.replace(vis.syntax().text_range(), "pub"); - edit.set_cursor(vis.syntax().text_range().start()); }, ); } @@ -122,15 +119,15 @@ mod tests { #[test] fn change_visibility_adds_pub_crate_to_items() { check_assist(change_visibility, "<|>fn foo() {}", "<|>pub(crate) fn foo() {}"); - check_assist(change_visibility, "f<|>n foo() {}", "<|>pub(crate) fn foo() {}"); + check_assist(change_visibility, "f<|>n foo() {}", "pub(crate) f<|>n foo() {}"); check_assist(change_visibility, "<|>struct Foo {}", "<|>pub(crate) struct Foo {}"); check_assist(change_visibility, "<|>mod foo {}", "<|>pub(crate) mod foo {}"); check_assist(change_visibility, "<|>trait Foo {}", "<|>pub(crate) trait Foo {}"); - check_assist(change_visibility, "m<|>od {}", "<|>pub(crate) mod {}"); + check_assist(change_visibility, "m<|>od {}", "pub(crate) m<|>od {}"); check_assist( change_visibility, "unsafe f<|>n foo() {}", - "<|>pub(crate) unsafe fn foo() {}", + "pub(crate) unsafe f<|>n foo() {}", ); } From c446fd76a2a6191adce87b20707a37bd46cb85a9 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 20 May 2020 14:00:37 +0200 Subject: [PATCH 58/70] Snippetify fill_match_arms --- .../src/handlers/fill_match_arms.rs | 193 +++++++----------- crates/ra_assists/src/tests/generated.rs | 2 +- docs/user/assists.md | 2 +- 3 files changed, 79 insertions(+), 118 deletions(-) diff --git a/crates/ra_assists/src/handlers/fill_match_arms.rs b/crates/ra_assists/src/handlers/fill_match_arms.rs index bbdcdc6263..cc303285b3 100644 --- a/crates/ra_assists/src/handlers/fill_match_arms.rs +++ b/crates/ra_assists/src/handlers/fill_match_arms.rs @@ -6,7 +6,10 @@ use ra_ide_db::RootDatabase; use ra_syntax::ast::{self, make, AstNode, MatchArm, NameOwner, Pat}; use test_utils::mark; -use crate::{utils::FamousDefs, AssistContext, AssistId, Assists}; +use crate::{ + utils::{render_snippet, Cursor, FamousDefs}, + AssistContext, AssistId, Assists, +}; // Assist: fill_match_arms // @@ -27,7 +30,7 @@ use crate::{utils::FamousDefs, AssistContext, AssistId, Assists}; // // fn handle(action: Action) { // match action { -// Action::Move { distance } => {} +// $0Action::Move { distance } => {} // Action::Stop => {} // } // } @@ -100,10 +103,23 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option< } let target = match_expr.syntax().text_range(); - acc.add(AssistId("fill_match_arms"), "Fill match arms", target, |edit| { - let new_arm_list = match_arm_list.remove_placeholder().append_arms(missing_arms); - edit.set_cursor(expr.syntax().text_range().start()); - edit.replace_ast(match_arm_list, new_arm_list); + acc.add(AssistId("fill_match_arms"), "Fill match arms", target, |builder| { + let new_arm_list = match_arm_list.remove_placeholder(); + let n_old_arms = new_arm_list.arms().count(); + let new_arm_list = new_arm_list.append_arms(missing_arms); + let first_new_arm = new_arm_list.arms().nth(n_old_arms); + let old_range = match_arm_list.syntax().text_range(); + match (first_new_arm, ctx.config.snippet_cap) { + (Some(first_new_arm), Some(cap)) => { + let snippet = render_snippet( + cap, + new_arm_list.syntax(), + Cursor::Before(first_new_arm.syntax()), + ); + builder.replace_snippet(cap, old_range, snippet); + } + _ => builder.replace(old_range, new_arm_list.to_string()), + } }) } @@ -226,12 +242,12 @@ mod tests { r#" enum A { As, - Bs{x:i32, y:Option}, + Bs { x: i32, y: Option }, Cs(i32, Option), } fn main() { match A::As<|> { - A::Bs{x,y:Some(_)} => {} + A::Bs { x, y: Some(_) } => {} A::Cs(_, Some(_)) => {} } } @@ -239,14 +255,14 @@ mod tests { r#" enum A { As, - Bs{x:i32, y:Option}, + Bs { x: i32, y: Option }, Cs(i32, Option), } fn main() { - match <|>A::As { - A::Bs{x,y:Some(_)} => {} + match A::As { + A::Bs { x, y: Some(_) } => {} A::Cs(_, Some(_)) => {} - A::As => {} + $0A::As => {} } } "#, @@ -276,9 +292,9 @@ mod tests { Cs(Option), } fn main() { - match <|>A::As { + match A::As { A::Cs(_) | A::Bs => {} - A::As => {} + $0A::As => {} } } "#, @@ -322,11 +338,11 @@ mod tests { Ys, } fn main() { - match <|>A::As { + match A::As { A::Bs if 0 < 1 => {} A::Ds(_value) => { let x = 1; } A::Es(B::Xs) => (), - A::As => {} + $0A::As => {} A::Cs => {} } } @@ -344,7 +360,7 @@ mod tests { Bs, Cs(String), Ds(String, String), - Es{ x: usize, y: usize } + Es { x: usize, y: usize } } fn main() { @@ -358,13 +374,13 @@ mod tests { Bs, Cs(String), Ds(String, String), - Es{ x: usize, y: usize } + Es { x: usize, y: usize } } fn main() { let a = A::As; - match <|>a { - A::As => {} + match a { + $0A::As => {} A::Bs => {} A::Cs(_) => {} A::Ds(_, _) => {} @@ -380,14 +396,8 @@ mod tests { check_assist( fill_match_arms, r#" - enum A { - One, - Two, - } - enum B { - One, - Two, - } + enum A { One, Two } + enum B { One, Two } fn main() { let a = A::One; @@ -396,20 +406,14 @@ mod tests { } "#, r#" - enum A { - One, - Two, - } - enum B { - One, - Two, - } + enum A { One, Two } + enum B { One, Two } fn main() { let a = A::One; let b = B::One; - match <|>(a, b) { - (A::One, B::One) => {} + match (a, b) { + $0(A::One, B::One) => {} (A::One, B::Two) => {} (A::Two, B::One) => {} (A::Two, B::Two) => {} @@ -424,14 +428,8 @@ mod tests { check_assist( fill_match_arms, r#" - enum A { - One, - Two, - } - enum B { - One, - Two, - } + enum A { One, Two } + enum B { One, Two } fn main() { let a = A::One; @@ -440,20 +438,14 @@ mod tests { } "#, r#" - enum A { - One, - Two, - } - enum B { - One, - Two, - } + enum A { One, Two } + enum B { One, Two } fn main() { let a = A::One; let b = B::One; - match <|>(&a, &b) { - (A::One, B::One) => {} + match (&a, &b) { + $0(A::One, B::One) => {} (A::One, B::Two) => {} (A::Two, B::One) => {} (A::Two, B::Two) => {} @@ -468,14 +460,8 @@ mod tests { check_assist_not_applicable( fill_match_arms, r#" - enum A { - One, - Two, - } - enum B { - One, - Two, - } + enum A { One, Two } + enum B { One, Two } fn main() { let a = A::One; @@ -493,14 +479,8 @@ mod tests { check_assist_not_applicable( fill_match_arms, r#" - enum A { - One, - Two, - } - enum B { - One, - Two, - } + enum A { One, Two } + enum B { One, Two } fn main() { let a = A::One; @@ -524,10 +504,7 @@ mod tests { check_assist_not_applicable( fill_match_arms, r#" - enum A { - One, - Two, - } + enum A { One, Two } fn main() { let a = A::One; @@ -543,9 +520,7 @@ mod tests { check_assist( fill_match_arms, r#" - enum A { - As, - } + enum A { As } fn foo(a: &A) { match a<|> { @@ -553,13 +528,11 @@ mod tests { } "#, r#" - enum A { - As, - } + enum A { As } fn foo(a: &A) { - match <|>a { - A::As => {} + match a { + $0A::As => {} } } "#, @@ -569,7 +542,7 @@ mod tests { fill_match_arms, r#" enum A { - Es{ x: usize, y: usize } + Es { x: usize, y: usize } } fn foo(a: &mut A) { @@ -579,12 +552,12 @@ mod tests { "#, r#" enum A { - Es{ x: usize, y: usize } + Es { x: usize, y: usize } } fn foo(a: &mut A) { - match <|>a { - A::Es { x, y } => {} + match a { + $0A::Es { x, y } => {} } } "#, @@ -623,8 +596,8 @@ mod tests { enum E { X, Y } fn main() { - match <|>E::X { - E::X => {} + match E::X { + $0E::X => {} E::Y => {} } } @@ -651,8 +624,8 @@ mod tests { use foo::E::X; fn main() { - match <|>X { - X => {} + match X { + $0X => {} foo::E::Y => {} } } @@ -665,10 +638,7 @@ mod tests { check_assist( fill_match_arms, r#" - enum A { - One, - Two, - } + enum A { One, Two } fn foo(a: A) { match a { // foo bar baz<|> @@ -678,16 +648,13 @@ mod tests { } "#, r#" - enum A { - One, - Two, - } + enum A { One, Two } fn foo(a: A) { - match <|>a { + match a { // foo bar baz A::One => {} // This is where the rest should be - A::Two => {} + $0A::Two => {} } } "#, @@ -699,10 +666,7 @@ mod tests { check_assist( fill_match_arms, r#" - enum A { - One, - Two, - } + enum A { One, Two } fn foo(a: A) { match a { // foo bar baz<|> @@ -710,14 +674,11 @@ mod tests { } "#, r#" - enum A { - One, - Two, - } + enum A { One, Two } fn foo(a: A) { - match <|>a { + match a { // foo bar baz - A::One => {} + $0A::One => {} A::Two => {} } } @@ -740,8 +701,8 @@ mod tests { r#" enum A { One, Two, } fn foo(a: A) { - match <|>a { - A::One => {} + match a { + $0A::One => {} A::Two => {} } } @@ -765,8 +726,8 @@ fn foo(opt: Option) { before, r#" fn foo(opt: Option) { - match <|>opt { - Some(_) => {} + match opt { + $0Some(_) => {} None => {} } } diff --git a/crates/ra_assists/src/tests/generated.rs b/crates/ra_assists/src/tests/generated.rs index 417ee89a88..d841b6f565 100644 --- a/crates/ra_assists/src/tests/generated.rs +++ b/crates/ra_assists/src/tests/generated.rs @@ -336,7 +336,7 @@ enum Action { Move { distance: u32 }, Stop } fn handle(action: Action) { match action { - Action::Move { distance } => {} + $0Action::Move { distance } => {} Action::Stop => {} } } diff --git a/docs/user/assists.md b/docs/user/assists.md index 0ae242389e..a33c490b8d 100644 --- a/docs/user/assists.md +++ b/docs/user/assists.md @@ -325,7 +325,7 @@ enum Action { Move { distance: u32 }, Stop } fn handle(action: Action) { match action { - Action::Move { distance } => {} + $0Action::Move { distance } => {} Action::Stop => {} } } From ba3a58d1b2d76bae2ac84923d12918a32ad680f6 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 20 May 2020 14:13:17 +0200 Subject: [PATCH 59/70] Snippetify fix_visibility --- .../ra_assists/src/handlers/fix_visibility.rs | 64 ++++++++++--------- crates/ra_assists/src/tests/generated.rs | 2 +- docs/user/assists.md | 2 +- 3 files changed, 36 insertions(+), 32 deletions(-) diff --git a/crates/ra_assists/src/handlers/fix_visibility.rs b/crates/ra_assists/src/handlers/fix_visibility.rs index 48ce07ca5a..9ec42f568c 100644 --- a/crates/ra_assists/src/handlers/fix_visibility.rs +++ b/crates/ra_assists/src/handlers/fix_visibility.rs @@ -25,7 +25,7 @@ use crate::{AssistContext, AssistId, Assists}; // -> // ``` // mod m { -// pub(crate) fn frobnicate() {} +// $0pub(crate) fn frobnicate() {} // } // fn main() { // m::frobnicate() {} @@ -62,10 +62,12 @@ fn add_vis_to_referenced_module_def(acc: &mut Assists, ctx: &AssistContext) -> O Some(name) => format!("Change visibility of {} to {}", name, missing_visibility), }; - acc.add(AssistId("fix_visibility"), assist_label, target, |edit| { - edit.set_file(target_file); - edit.insert(offset, format!("{} ", missing_visibility)); - edit.set_cursor(offset); + acc.add(AssistId("fix_visibility"), assist_label, target, |builder| { + builder.set_file(target_file); + match ctx.config.snippet_cap { + Some(cap) => builder.insert_snippet(cap, offset, format!("$0{} ", missing_visibility)), + None => builder.insert(offset, format!("{} ", missing_visibility)), + } }) } @@ -103,10 +105,12 @@ fn add_vis_to_referenced_record_field(acc: &mut Assists, ctx: &AssistContext) -> let assist_label = format!("Change visibility of {}.{} to {}", parent_name, target_name, missing_visibility); - acc.add(AssistId("fix_visibility"), assist_label, target, |edit| { - edit.set_file(target_file); - edit.insert(offset, format!("{} ", missing_visibility)); - edit.set_cursor(offset) + acc.add(AssistId("fix_visibility"), assist_label, target, |builder| { + builder.set_file(target_file); + match ctx.config.snippet_cap { + Some(cap) => builder.insert_snippet(cap, offset, format!("$0{} ", missing_visibility)), + None => builder.insert(offset, format!("{} ", missing_visibility)), + } }) } @@ -196,7 +200,7 @@ mod tests { fix_visibility, r"mod foo { fn foo() {} } fn main() { foo::foo<|>() } ", - r"mod foo { <|>pub(crate) fn foo() {} } + r"mod foo { $0pub(crate) fn foo() {} } fn main() { foo::foo() } ", ); check_assist_not_applicable( @@ -212,7 +216,7 @@ mod tests { fix_visibility, r"mod foo { struct Foo; } fn main() { foo::Foo<|> } ", - r"mod foo { <|>pub(crate) struct Foo; } + r"mod foo { $0pub(crate) struct Foo; } fn main() { foo::Foo } ", ); check_assist_not_applicable( @@ -224,7 +228,7 @@ mod tests { fix_visibility, r"mod foo { enum Foo; } fn main() { foo::Foo<|> } ", - r"mod foo { <|>pub(crate) enum Foo; } + r"mod foo { $0pub(crate) enum Foo; } fn main() { foo::Foo } ", ); check_assist_not_applicable( @@ -236,7 +240,7 @@ mod tests { fix_visibility, r"mod foo { union Foo; } fn main() { foo::Foo<|> } ", - r"mod foo { <|>pub(crate) union Foo; } + r"mod foo { $0pub(crate) union Foo; } fn main() { foo::Foo } ", ); check_assist_not_applicable( @@ -258,7 +262,7 @@ mod tests { //- /foo.rs struct Foo; ", - r"<|>pub(crate) struct Foo; + r"$0pub(crate) struct Foo; ", ); @@ -270,7 +274,7 @@ mod tests { fix_visibility, r"mod foo { pub struct Foo { bar: (), } } fn main() { foo::Foo { <|>bar: () }; } ", - r"mod foo { pub struct Foo { <|>pub(crate) bar: (), } } + r"mod foo { pub struct Foo { $0pub(crate) bar: (), } } fn main() { foo::Foo { bar: () }; } ", ); check_assist( @@ -281,7 +285,7 @@ mod tests { //- /foo.rs pub struct Foo { bar: () } ", - r"pub struct Foo { <|>pub(crate) bar: () } + r"pub struct Foo { $0pub(crate) bar: () } ", ); @@ -307,7 +311,7 @@ mod tests { fix_visibility, r"mod foo { pub enum Foo { Bar { bar: () } } } fn main() { foo::Foo::Bar { <|>bar: () }; } ", - r"mod foo { pub enum Foo { Bar { <|>pub(crate) bar: () } } } + r"mod foo { pub enum Foo { Bar { $0pub(crate) bar: () } } } fn main() { foo::Foo::Bar { bar: () }; } ", ); check_assist( @@ -318,7 +322,7 @@ mod tests { //- /foo.rs pub enum Foo { Bar { bar: () } } ", - r"pub enum Foo { Bar { <|>pub(crate) bar: () } } + r"pub enum Foo { Bar { $0pub(crate) bar: () } } ", ); @@ -346,7 +350,7 @@ mod tests { fix_visibility, r"mod foo { pub union Foo { bar: (), } } fn main() { foo::Foo { <|>bar: () }; } ", - r"mod foo { pub union Foo { <|>pub(crate) bar: (), } } + r"mod foo { pub union Foo { $0pub(crate) bar: (), } } fn main() { foo::Foo { bar: () }; } ", ); check_assist( @@ -357,7 +361,7 @@ mod tests { //- /foo.rs pub union Foo { bar: () } ", - r"pub union Foo { <|>pub(crate) bar: () } + r"pub union Foo { $0pub(crate) bar: () } ", ); @@ -383,7 +387,7 @@ mod tests { fix_visibility, r"mod foo { const FOO: () = (); } fn main() { foo::FOO<|> } ", - r"mod foo { <|>pub(crate) const FOO: () = (); } + r"mod foo { $0pub(crate) const FOO: () = (); } fn main() { foo::FOO } ", ); check_assist_not_applicable( @@ -399,7 +403,7 @@ mod tests { fix_visibility, r"mod foo { static FOO: () = (); } fn main() { foo::FOO<|> } ", - r"mod foo { <|>pub(crate) static FOO: () = (); } + r"mod foo { $0pub(crate) static FOO: () = (); } fn main() { foo::FOO } ", ); check_assist_not_applicable( @@ -415,7 +419,7 @@ mod tests { fix_visibility, r"mod foo { trait Foo { fn foo(&self) {} } } fn main() { let x: &dyn foo::<|>Foo; } ", - r"mod foo { <|>pub(crate) trait Foo { fn foo(&self) {} } } + r"mod foo { $0pub(crate) trait Foo { fn foo(&self) {} } } fn main() { let x: &dyn foo::Foo; } ", ); check_assist_not_applicable( @@ -431,7 +435,7 @@ mod tests { fix_visibility, r"mod foo { type Foo = (); } fn main() { let x: foo::Foo<|>; } ", - r"mod foo { <|>pub(crate) type Foo = (); } + r"mod foo { $0pub(crate) type Foo = (); } fn main() { let x: foo::Foo; } ", ); check_assist_not_applicable( @@ -447,7 +451,7 @@ mod tests { fix_visibility, r"mod foo { mod bar { fn bar() {} } } fn main() { foo::bar<|>::bar(); } ", - r"mod foo { <|>pub(crate) mod bar { fn bar() {} } } + r"mod foo { $0pub(crate) mod bar { fn bar() {} } } fn main() { foo::bar::bar(); } ", ); @@ -463,7 +467,7 @@ mod tests { pub fn baz() {} } ", - r"<|>pub(crate) mod bar { + r"$0pub(crate) mod bar { pub fn baz() {} } @@ -493,7 +497,7 @@ mod tests { pub fn baz() {} } ", - r"<|>pub(crate) mod bar; + r"$0pub(crate) mod bar; ", ); } @@ -510,7 +514,7 @@ mod tests { mod bar { pub fn baz() {} }", - r"<|>pub(crate) mod bar { + r"$0pub(crate) mod bar { pub fn baz() {} } ", @@ -525,7 +529,7 @@ mod tests { foo::Bar<|> //- /lib.rs crate:foo struct Bar;", - r"<|>pub struct Bar; + r"$0pub struct Bar; ", ) } @@ -545,7 +549,7 @@ mod tests { ", r" mod foo { - <|>pub(crate) use bar::Baz; + $0pub(crate) use bar::Baz; mod bar { pub(super) struct Baz; } } foo::Baz diff --git a/crates/ra_assists/src/tests/generated.rs b/crates/ra_assists/src/tests/generated.rs index d841b6f565..3e6654c175 100644 --- a/crates/ra_assists/src/tests/generated.rs +++ b/crates/ra_assists/src/tests/generated.rs @@ -358,7 +358,7 @@ fn main() { "#####, r#####" mod m { - pub(crate) fn frobnicate() {} + $0pub(crate) fn frobnicate() {} } fn main() { m::frobnicate() {} diff --git a/docs/user/assists.md b/docs/user/assists.md index a33c490b8d..51807ffdac 100644 --- a/docs/user/assists.md +++ b/docs/user/assists.md @@ -346,7 +346,7 @@ fn main() { // AFTER mod m { - pub(crate) fn frobnicate() {} + $0pub(crate) fn frobnicate() {} } fn main() { m::frobnicate() {} From d58d6412d837a9f6475e04c74709d78f52c9ff6a Mon Sep 17 00:00:00 2001 From: Yuki Kodama Date: Thu, 21 May 2020 03:01:37 +0900 Subject: [PATCH 60/70] Fix names of launch configuration in dev docs --- docs/dev/README.md | 4 ++-- docs/dev/debugging.md | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/dev/README.md b/docs/dev/README.md index a20ead0b6c..65cc9fc12c 100644 --- a/docs/dev/README.md +++ b/docs/dev/README.md @@ -74,7 +74,7 @@ relevant test and execute it (VS Code includes an action for running a single test). However, launching a VS Code instance with locally build language server is -possible. There's **"Run Extension (Dev Server)"** launch configuration for this. +possible. There's **"Run Extension (Debug Build)"** launch configuration for this. In general, I use one of the following workflows for fixing bugs and implementing features. @@ -86,7 +86,7 @@ then just do printf-driven development/debugging. As a sanity check after I'm done, I use `cargo xtask install --server` and **Reload Window** action in VS Code to sanity check that the thing works as I expect. -If the problem concerns only the VS Code extension, I use **Run Extension** +If the problem concerns only the VS Code extension, I use **Run Installed Extension** launch configuration from `launch.json`. Notably, this uses the usual `rust-analyzer` binary from `PATH`. For this it is important to have the following in `setting.json` file: diff --git a/docs/dev/debugging.md b/docs/dev/debugging.md index 1aa3929358..59a83f7d76 100644 --- a/docs/dev/debugging.md +++ b/docs/dev/debugging.md @@ -22,8 +22,8 @@ where **only** the `rust-analyzer` extension being debugged is enabled. ## Debug TypeScript VSCode extension -- `Run Extension` - runs the extension with the globally installed `rust-analyzer` binary. -- `Run Extension (Dev Server)` - runs extension with the locally built LSP server (`target/debug/rust-analyzer`). +- `Run Installed Extension` - runs the extension with the globally installed `rust-analyzer` binary. +- `Run Extension (Debug Build)` - runs extension with the locally built LSP server (`target/debug/rust-analyzer`). TypeScript debugging is configured to watch your source edits and recompile. To apply changes to an already running debug process, press Ctrl+Shift+P and run the following command in your `[Extension Development Host]` @@ -47,7 +47,7 @@ To apply changes to an already running debug process, press Ctrl+Shift+P Date: Wed, 20 May 2020 20:11:14 +0200 Subject: [PATCH 61/70] Fix GNOME spelling GNOME is a trademark. :-) --- docs/user/readme.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/user/readme.adoc b/docs/user/readme.adoc index 03836e6e2c..40ed548091 100644 --- a/docs/user/readme.adoc +++ b/docs/user/readme.adoc @@ -249,7 +249,7 @@ If it worked, you should see "rust-analyzer, Line X, Column Y" on the left side If you get an error saying `No such file or directory: 'rust-analyzer'`, see the <> section on installing the language server binary. -=== Gnome Builder +=== GNOME Builder Prerequisites: You have installed the <>. From 65fa5864105280267e6ccdaa61957cd9953e444e Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 20 May 2020 22:55:37 +0200 Subject: [PATCH 62/70] Relax cursor position tests in assists Those will be replaced with snippets anyway --- .../src/handlers/add_explicit_type.rs | 18 ++++----- .../src/handlers/add_from_impl_for_enum.rs | 6 +-- .../ra_assists/src/handlers/apply_demorgan.rs | 8 ++-- crates/ra_assists/src/handlers/auto_import.rs | 22 +++++------ .../src/handlers/change_visibility.rs | 30 +++++++-------- .../ra_assists/src/handlers/flip_binexpr.rs | 14 +++---- crates/ra_assists/src/handlers/flip_comma.rs | 2 +- .../src/handlers/flip_trait_bound.rs | 14 +++---- crates/ra_assists/src/handlers/invert_if.rs | 6 +-- crates/ra_assists/src/handlers/move_bounds.rs | 8 ++-- crates/ra_assists/src/handlers/raw_string.rs | 26 ++++++------- .../ra_assists/src/handlers/reorder_fields.rs | 6 +-- .../replace_qualified_name_with_use.rs | 38 +++++++++---------- crates/ra_assists/src/tests.rs | 15 ++------ 14 files changed, 96 insertions(+), 117 deletions(-) diff --git a/crates/ra_assists/src/handlers/add_explicit_type.rs b/crates/ra_assists/src/handlers/add_explicit_type.rs index 770049212d..ab20c66493 100644 --- a/crates/ra_assists/src/handlers/add_explicit_type.rs +++ b/crates/ra_assists/src/handlers/add_explicit_type.rs @@ -86,11 +86,7 @@ mod tests { #[test] fn add_explicit_type_works_for_simple_expr() { - check_assist( - add_explicit_type, - "fn f() { let a<|> = 1; }", - "fn f() { let a<|>: i32 = 1; }", - ); + check_assist(add_explicit_type, "fn f() { let a<|> = 1; }", "fn f() { let a: i32 = 1; }"); } #[test] @@ -98,7 +94,7 @@ mod tests { check_assist( add_explicit_type, "fn f() { let a<|>: _ = 1; }", - "fn f() { let a<|>: i32 = 1; }", + "fn f() { let a: i32 = 1; }", ); } @@ -122,7 +118,7 @@ mod tests { } fn f() { - let a<|>: Option = Option::Some(1); + let a: Option = Option::Some(1); }"#, ); } @@ -132,7 +128,7 @@ mod tests { check_assist( add_explicit_type, r"macro_rules! v { () => {0u64} } fn f() { let a<|> = v!(); }", - r"macro_rules! v { () => {0u64} } fn f() { let a<|>: u64 = v!(); }", + r"macro_rules! v { () => {0u64} } fn f() { let a: u64 = v!(); }", ); } @@ -140,8 +136,8 @@ mod tests { fn add_explicit_type_works_for_macro_call_recursive() { check_assist( add_explicit_type, - "macro_rules! u { () => {0u64} } macro_rules! v { () => {u!()} } fn f() { let a<|> = v!(); }", - "macro_rules! u { () => {0u64} } macro_rules! v { () => {u!()} } fn f() { let a<|>: u64 = v!(); }", + r#"macro_rules! u { () => {0u64} } macro_rules! v { () => {u!()} } fn f() { let a<|> = v!(); }"#, + r#"macro_rules! u { () => {0u64} } macro_rules! v { () => {u!()} } fn f() { let a: u64 = v!(); }"#, ); } @@ -208,7 +204,7 @@ struct Test { } fn main() { - let test<|>: Test = Test { t: 23, k: 33 }; + let test: Test = Test { t: 23, k: 33 }; }"#, ); } diff --git a/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs b/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs index 5f6c8b19a3..6a675e8126 100644 --- a/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs +++ b/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs @@ -101,7 +101,7 @@ mod tests { check_assist( add_from_impl_for_enum, "enum A { <|>One(u32) }", - r#"enum A { <|>One(u32) } + r#"enum A { One(u32) } impl From for A { fn from(v: u32) -> Self { @@ -116,7 +116,7 @@ impl From for A { check_assist( add_from_impl_for_enum, r#"enum A { <|>One(foo::bar::baz::Boo) }"#, - r#"enum A { <|>One(foo::bar::baz::Boo) } + r#"enum A { One(foo::bar::baz::Boo) } impl From for A { fn from(v: foo::bar::baz::Boo) -> Self { @@ -178,7 +178,7 @@ impl From for A { pub trait From { fn from(T) -> Self; }"#, - r#"enum A { <|>One(u32), Two(String), } + r#"enum A { One(u32), Two(String), } impl From for A { fn from(v: u32) -> Self { diff --git a/crates/ra_assists/src/handlers/apply_demorgan.rs b/crates/ra_assists/src/handlers/apply_demorgan.rs index 0feba5e11f..233e8fb8e6 100644 --- a/crates/ra_assists/src/handlers/apply_demorgan.rs +++ b/crates/ra_assists/src/handlers/apply_demorgan.rs @@ -63,22 +63,22 @@ mod tests { #[test] fn demorgan_turns_and_into_or() { - check_assist(apply_demorgan, "fn f() { !x &&<|> !x }", "fn f() { !(x ||<|> x) }") + check_assist(apply_demorgan, "fn f() { !x &&<|> !x }", "fn f() { !(x || x) }") } #[test] fn demorgan_turns_or_into_and() { - check_assist(apply_demorgan, "fn f() { !x ||<|> !x }", "fn f() { !(x &&<|> x) }") + check_assist(apply_demorgan, "fn f() { !x ||<|> !x }", "fn f() { !(x && x) }") } #[test] fn demorgan_removes_inequality() { - check_assist(apply_demorgan, "fn f() { x != x ||<|> !x }", "fn f() { !(x == x &&<|> x) }") + check_assist(apply_demorgan, "fn f() { x != x ||<|> !x }", "fn f() { !(x == x && x) }") } #[test] fn demorgan_general_case() { - check_assist(apply_demorgan, "fn f() { x ||<|> x }", "fn f() { !(!x &&<|> !x) }") + check_assist(apply_demorgan, "fn f() { x ||<|> x }", "fn f() { !(!x && !x) }") } #[test] diff --git a/crates/ra_assists/src/handlers/auto_import.rs b/crates/ra_assists/src/handlers/auto_import.rs index f6d25579e0..edf96d50ec 100644 --- a/crates/ra_assists/src/handlers/auto_import.rs +++ b/crates/ra_assists/src/handlers/auto_import.rs @@ -298,7 +298,7 @@ mod tests { } ", r" - <|>use PubMod::PubStruct; + use PubMod::PubStruct; PubStruct @@ -329,7 +329,7 @@ mod tests { macro_rules! foo { ($i:ident) => { fn foo(a: $i) {} } } - foo!(Pub<|>Struct); + foo!(PubStruct); pub mod PubMod { pub struct PubStruct; @@ -360,7 +360,7 @@ mod tests { use PubMod::{PubStruct2, PubStruct1}; struct Test { - test: Pub<|>Struct2, + test: PubStruct2, } pub mod PubMod { @@ -393,7 +393,7 @@ mod tests { r" use PubMod3::PubStruct; - PubSt<|>ruct + PubStruct pub mod PubMod1 { pub struct PubStruct; @@ -474,7 +474,7 @@ mod tests { r" use PubMod::test_function; - test_function<|> + test_function pub mod PubMod { pub fn test_function() {}; @@ -501,7 +501,7 @@ mod tests { r"use crate_with_macro::foo; fn main() { - foo<|> + foo } ", ); @@ -587,7 +587,7 @@ fn main() { } fn main() { - TestStruct::test_function<|> + TestStruct::test_function } ", ); @@ -620,7 +620,7 @@ fn main() { } fn main() { - TestStruct::TEST_CONST<|> + TestStruct::TEST_CONST } ", ); @@ -659,7 +659,7 @@ fn main() { } fn main() { - test_mod::TestStruct::test_function<|> + test_mod::TestStruct::test_function } ", ); @@ -730,7 +730,7 @@ fn main() { } fn main() { - test_mod::TestStruct::TEST_CONST<|> + test_mod::TestStruct::TEST_CONST } ", ); @@ -803,7 +803,7 @@ fn main() { fn main() { let test_struct = test_mod::TestStruct {}; - test_struct.test_meth<|>od() + test_struct.test_method() } ", ); diff --git a/crates/ra_assists/src/handlers/change_visibility.rs b/crates/ra_assists/src/handlers/change_visibility.rs index fbe459c9ca..c21d75be08 100644 --- a/crates/ra_assists/src/handlers/change_visibility.rs +++ b/crates/ra_assists/src/handlers/change_visibility.rs @@ -118,17 +118,13 @@ mod tests { #[test] fn change_visibility_adds_pub_crate_to_items() { - check_assist(change_visibility, "<|>fn foo() {}", "<|>pub(crate) fn foo() {}"); - check_assist(change_visibility, "f<|>n foo() {}", "pub(crate) f<|>n foo() {}"); - check_assist(change_visibility, "<|>struct Foo {}", "<|>pub(crate) struct Foo {}"); - check_assist(change_visibility, "<|>mod foo {}", "<|>pub(crate) mod foo {}"); - check_assist(change_visibility, "<|>trait Foo {}", "<|>pub(crate) trait Foo {}"); - check_assist(change_visibility, "m<|>od {}", "pub(crate) m<|>od {}"); - check_assist( - change_visibility, - "unsafe f<|>n foo() {}", - "pub(crate) unsafe f<|>n foo() {}", - ); + check_assist(change_visibility, "<|>fn foo() {}", "pub(crate) fn foo() {}"); + check_assist(change_visibility, "f<|>n foo() {}", "pub(crate) fn foo() {}"); + check_assist(change_visibility, "<|>struct Foo {}", "pub(crate) struct Foo {}"); + check_assist(change_visibility, "<|>mod foo {}", "pub(crate) mod foo {}"); + check_assist(change_visibility, "<|>trait Foo {}", "pub(crate) trait Foo {}"); + check_assist(change_visibility, "m<|>od {}", "pub(crate) mod {}"); + check_assist(change_visibility, "unsafe f<|>n foo() {}", "pub(crate) unsafe fn foo() {}"); } #[test] @@ -136,9 +132,9 @@ mod tests { check_assist( change_visibility, r"struct S { <|>field: u32 }", - r"struct S { <|>pub(crate) field: u32 }", + r"struct S { pub(crate) field: u32 }", ); - check_assist(change_visibility, r"struct S ( <|>u32 )", r"struct S ( <|>pub(crate) u32 )"); + check_assist(change_visibility, r"struct S ( <|>u32 )", r"struct S ( pub(crate) u32 )"); } #[test] @@ -152,17 +148,17 @@ mod tests { #[test] fn change_visibility_pub_to_pub_crate() { - check_assist(change_visibility, "<|>pub fn foo() {}", "<|>pub(crate) fn foo() {}") + check_assist(change_visibility, "<|>pub fn foo() {}", "pub(crate) fn foo() {}") } #[test] fn change_visibility_pub_crate_to_pub() { - check_assist(change_visibility, "<|>pub(crate) fn foo() {}", "<|>pub fn foo() {}") + check_assist(change_visibility, "<|>pub(crate) fn foo() {}", "pub fn foo() {}") } #[test] fn change_visibility_const() { - check_assist(change_visibility, "<|>const FOO = 3u8;", "<|>pub(crate) const FOO = 3u8;"); + check_assist(change_visibility, "<|>const FOO = 3u8;", "pub(crate) const FOO = 3u8;"); } #[test] @@ -183,7 +179,7 @@ mod tests { // comments #[derive(Debug)] - <|>pub(crate) struct Foo; + pub(crate) struct Foo; ", ) } diff --git a/crates/ra_assists/src/handlers/flip_binexpr.rs b/crates/ra_assists/src/handlers/flip_binexpr.rs index 692ba4895c..5731965766 100644 --- a/crates/ra_assists/src/handlers/flip_binexpr.rs +++ b/crates/ra_assists/src/handlers/flip_binexpr.rs @@ -85,17 +85,13 @@ mod tests { check_assist( flip_binexpr, "fn f() { let res = 1 ==<|> 2; }", - "fn f() { let res = 2 ==<|> 1; }", + "fn f() { let res = 2 == 1; }", ) } #[test] fn flip_binexpr_works_for_gt() { - check_assist( - flip_binexpr, - "fn f() { let res = 1 ><|> 2; }", - "fn f() { let res = 2 <<|> 1; }", - ) + check_assist(flip_binexpr, "fn f() { let res = 1 ><|> 2; }", "fn f() { let res = 2 < 1; }") } #[test] @@ -103,7 +99,7 @@ mod tests { check_assist( flip_binexpr, "fn f() { let res = 1 <=<|> 2; }", - "fn f() { let res = 2 >=<|> 1; }", + "fn f() { let res = 2 >= 1; }", ) } @@ -112,7 +108,7 @@ mod tests { check_assist( flip_binexpr, "fn f() { let res = (1 + 1) ==<|> (2 + 2); }", - "fn f() { let res = (2 + 2) ==<|> (1 + 1); }", + "fn f() { let res = (2 + 2) == (1 + 1); }", ) } @@ -132,7 +128,7 @@ mod tests { fn dyn_eq(&self, other: &dyn Diagnostic) -> bool { match other.downcast_ref::() { None => false, - Some(it) => self ==<|> it, + Some(it) => self == it, } } "#, diff --git a/crates/ra_assists/src/handlers/flip_comma.rs b/crates/ra_assists/src/handlers/flip_comma.rs index dfe2a7fedc..a57a1c463c 100644 --- a/crates/ra_assists/src/handlers/flip_comma.rs +++ b/crates/ra_assists/src/handlers/flip_comma.rs @@ -45,7 +45,7 @@ mod tests { check_assist( flip_comma, "fn foo(x: i32,<|> y: Result<(), ()>) {}", - "fn foo(y: Result<(), ()>,<|> x: i32) {}", + "fn foo(y: Result<(), ()>, x: i32) {}", ) } diff --git a/crates/ra_assists/src/handlers/flip_trait_bound.rs b/crates/ra_assists/src/handlers/flip_trait_bound.rs index 8a08702ab2..0115adc8b5 100644 --- a/crates/ra_assists/src/handlers/flip_trait_bound.rs +++ b/crates/ra_assists/src/handlers/flip_trait_bound.rs @@ -60,7 +60,7 @@ mod tests { check_assist( flip_trait_bound, "struct S where T: A <|>+ B { }", - "struct S where T: B <|>+ A { }", + "struct S where T: B + A { }", ) } @@ -69,13 +69,13 @@ mod tests { check_assist( flip_trait_bound, "impl X for S where T: A +<|> B { }", - "impl X for S where T: B +<|> A { }", + "impl X for S where T: B + A { }", ) } #[test] fn flip_trait_bound_works_for_fn() { - check_assist(flip_trait_bound, "fn f+ B>(t: T) { }", "fn f+ A>(t: T) { }") + check_assist(flip_trait_bound, "fn f+ B>(t: T) { }", "fn f(t: T) { }") } #[test] @@ -83,7 +83,7 @@ mod tests { check_assist( flip_trait_bound, "fn f(t: T) where T: A +<|> B { }", - "fn f(t: T) where T: B +<|> A { }", + "fn f(t: T) where T: B + A { }", ) } @@ -92,7 +92,7 @@ mod tests { check_assist( flip_trait_bound, "fn f(t: T) where T: A <|>+ 'static { }", - "fn f(t: T) where T: 'static <|>+ A { }", + "fn f(t: T) where T: 'static + A { }", ) } @@ -101,7 +101,7 @@ mod tests { check_assist( flip_trait_bound, "struct S where T: A <|>+ b_mod::B + C { }", - "struct S where T: b_mod::B <|>+ A + C { }", + "struct S where T: b_mod::B + A + C { }", ) } @@ -110,7 +110,7 @@ mod tests { check_assist( flip_trait_bound, "struct S where T: A + B + C + D + E + F +<|> G + H + I + J { }", - "struct S where T: A + B + C + D + E + G +<|> F + H + I + J { }", + "struct S where T: A + B + C + D + E + G + F + H + I + J { }", ) } } diff --git a/crates/ra_assists/src/handlers/invert_if.rs b/crates/ra_assists/src/handlers/invert_if.rs index 527c7caef1..59d278eb9b 100644 --- a/crates/ra_assists/src/handlers/invert_if.rs +++ b/crates/ra_assists/src/handlers/invert_if.rs @@ -72,7 +72,7 @@ mod tests { check_assist( invert_if, "fn f() { i<|>f x != 3 { 1 } else { 3 + 2 } }", - "fn f() { i<|>f x == 3 { 3 + 2 } else { 1 } }", + "fn f() { if x == 3 { 3 + 2 } else { 1 } }", ) } @@ -81,7 +81,7 @@ mod tests { check_assist( invert_if, "fn f() { <|>if !cond { 3 * 2 } else { 1 } }", - "fn f() { <|>if cond { 1 } else { 3 * 2 } }", + "fn f() { if cond { 1 } else { 3 * 2 } }", ) } @@ -90,7 +90,7 @@ mod tests { check_assist( invert_if, "fn f() { i<|>f cond { 3 * 2 } else { 1 } }", - "fn f() { i<|>f !cond { 1 } else { 3 * 2 } }", + "fn f() { if !cond { 1 } else { 3 * 2 } }", ) } diff --git a/crates/ra_assists/src/handlers/move_bounds.rs b/crates/ra_assists/src/handlers/move_bounds.rs index a41aacfc3d..be2a7eddcf 100644 --- a/crates/ra_assists/src/handlers/move_bounds.rs +++ b/crates/ra_assists/src/handlers/move_bounds.rs @@ -99,7 +99,7 @@ mod tests { fn fooF: FnOnce(T) -> T>() {} "#, r#" - fn fooF>() where T: u32, F: FnOnce(T) -> T {} + fn foo() where T: u32, F: FnOnce(T) -> T {} "#, ); } @@ -112,7 +112,7 @@ mod tests { implT> A {} "#, r#" - implT> A where U: u32 {} + impl A where U: u32 {} "#, ); } @@ -125,7 +125,7 @@ mod tests { struct A<<|>T: Iterator> {} "#, r#" - struct A<<|>T> where T: Iterator {} + struct A where T: Iterator {} "#, ); } @@ -138,7 +138,7 @@ mod tests { struct Pair<<|>T: u32>(T, T); "#, r#" - struct Pair<<|>T>(T, T) where T: u32; + struct Pair(T, T) where T: u32; "#, ); } diff --git a/crates/ra_assists/src/handlers/raw_string.rs b/crates/ra_assists/src/handlers/raw_string.rs index c20ffe0b30..16002d2ace 100644 --- a/crates/ra_assists/src/handlers/raw_string.rs +++ b/crates/ra_assists/src/handlers/raw_string.rs @@ -164,7 +164,7 @@ mod test { "#, r##" fn f() { - let s = <|>r#"random + let s = r#"random string"#; } "##, @@ -182,7 +182,7 @@ string"#; "#, r##" fn f() { - format!(<|>r#"x = {}"#, 92) + format!(r#"x = {}"#, 92) } "##, ) @@ -199,7 +199,7 @@ string"#; "###, r####" fn f() { - let s = <|>r#"#random## + let s = r#"#random## string"#; } "####, @@ -217,7 +217,7 @@ string"#; "###, r####" fn f() { - let s = <|>r###"#random"## + let s = r###"#random"## string"###; } "####, @@ -235,7 +235,7 @@ string"###; "#, r##" fn f() { - let s = <|>r#"random string"#; + let s = r#"random string"#; } "##, ) @@ -289,7 +289,7 @@ string"###; "#, r##" fn f() { - let s = <|>r#"random string"#; + let s = r#"random string"#; } "##, ) @@ -306,7 +306,7 @@ string"###; "##, r###" fn f() { - let s = <|>r##"random"string"##; + let s = r##"random"string"##; } "###, ) @@ -348,7 +348,7 @@ string"###; "##, r#" fn f() { - let s = <|>r"random string"; + let s = r"random string"; } "#, ) @@ -365,7 +365,7 @@ string"###; "##, r#" fn f() { - let s = <|>r"random\"str\"ing"; + let s = r"random\"str\"ing"; } "#, ) @@ -382,7 +382,7 @@ string"###; "###, r##" fn f() { - let s = <|>r#"random string"#; + let s = r#"random string"#; } "##, ) @@ -436,7 +436,7 @@ string"###; "##, r#" fn f() { - let s = <|>"random string"; + let s = "random string"; } "#, ) @@ -453,7 +453,7 @@ string"###; "##, r#" fn f() { - let s = <|>"random\"str\"ing"; + let s = "random\"str\"ing"; } "#, ) @@ -470,7 +470,7 @@ string"###; "###, r##" fn f() { - let s = <|>"random string"; + let s = "random string"; } "##, ) diff --git a/crates/ra_assists/src/handlers/reorder_fields.rs b/crates/ra_assists/src/handlers/reorder_fields.rs index 757f6406e9..30229edc2f 100644 --- a/crates/ra_assists/src/handlers/reorder_fields.rs +++ b/crates/ra_assists/src/handlers/reorder_fields.rs @@ -140,7 +140,7 @@ mod tests { "#, r#" struct Foo {foo: i32, bar: i32}; - const test: Foo = <|>Foo {foo: 1, bar: 0} + const test: Foo = Foo {foo: 1, bar: 0} "#, ) } @@ -164,7 +164,7 @@ mod tests { fn f(f: Foo) -> { match f { - <|>Foo { ref mut bar, baz: 0, .. } => (), + Foo { ref mut bar, baz: 0, .. } => (), _ => () } } @@ -202,7 +202,7 @@ mod tests { impl Foo { fn new() -> Foo { let foo = String::new(); - <|>Foo { + Foo { foo, bar: foo.clone(), extra: "Extra field", diff --git a/crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs b/crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs index d9f84208df..0197a8cf06 100644 --- a/crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs +++ b/crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs @@ -89,7 +89,7 @@ std::fmt::Debug<|> " use std::fmt::Debug; -Debug<|> +Debug ", ); } @@ -106,7 +106,7 @@ fn main() { " use std::fmt::Debug; -Debug<|> +Debug fn main() { } @@ -130,7 +130,7 @@ use std::fmt::Debug; fn main() { } -Debug<|> +Debug ", ); } @@ -145,7 +145,7 @@ std::fmt<|>::Debug " use std::fmt; -fmt<|>::Debug +fmt::Debug ", ); } @@ -164,7 +164,7 @@ impl std::fmt::Debug<|> for Foo { use stdx; use std::fmt::Debug; -impl Debug<|> for Foo { +impl Debug for Foo { } ", ); @@ -181,7 +181,7 @@ impl std::fmt::Debug<|> for Foo { " use std::fmt::Debug; -impl Debug<|> for Foo { +impl Debug for Foo { } ", ); @@ -198,7 +198,7 @@ impl Debug<|> for Foo { " use std::fmt::Debug; - impl Debug<|> for Foo { + impl Debug for Foo { } ", ); @@ -217,7 +217,7 @@ impl std::io<|> for Foo { " use std::{io, fmt}; -impl io<|> for Foo { +impl io for Foo { } ", ); @@ -236,7 +236,7 @@ impl std::fmt::Debug<|> for Foo { " use std::fmt::{self, Debug, }; -impl Debug<|> for Foo { +impl Debug for Foo { } ", ); @@ -255,7 +255,7 @@ impl std::fmt<|> for Foo { " use std::fmt::{self, Debug}; -impl fmt<|> for Foo { +impl fmt for Foo { } ", ); @@ -274,7 +274,7 @@ impl std::fmt::nested<|> for Foo { " use std::fmt::{Debug, nested::{Display, self}}; -impl nested<|> for Foo { +impl nested for Foo { } ", ); @@ -293,7 +293,7 @@ impl std::fmt::nested<|> for Foo { " use std::fmt::{Debug, nested::{self, Display}}; -impl nested<|> for Foo { +impl nested for Foo { } ", ); @@ -312,7 +312,7 @@ impl std::fmt::nested::Debug<|> for Foo { " use std::fmt::{Debug, nested::{Display, Debug}}; -impl Debug<|> for Foo { +impl Debug for Foo { } ", ); @@ -331,7 +331,7 @@ impl std::fmt::nested::Display<|> for Foo { " use std::fmt::{nested::Display, Debug}; -impl Display<|> for Foo { +impl Display for Foo { } ", ); @@ -350,7 +350,7 @@ impl std::fmt::Display<|> for Foo { " use std::fmt::{Display, nested::Debug}; -impl Display<|> for Foo { +impl Display for Foo { } ", ); @@ -374,7 +374,7 @@ use crate::{ AssocItem, }; -fn foo() { lower<|>::trait_env() } +fn foo() { lower::trait_env() } ", ); } @@ -392,7 +392,7 @@ impl foo::Debug<|> for Foo { " use std::fmt as foo; -impl Debug<|> for Foo { +impl Debug for Foo { } ", ); @@ -435,7 +435,7 @@ mod foo { mod bar { use std::fmt::Debug; - Debug<|> + Debug } } ", @@ -458,7 +458,7 @@ fn main() { use std::fmt::Debug; fn main() { - Debug<|> + Debug } ", ); diff --git a/crates/ra_assists/src/tests.rs b/crates/ra_assists/src/tests.rs index 9ba3da7862..373a7f7cc1 100644 --- a/crates/ra_assists/src/tests.rs +++ b/crates/ra_assists/src/tests.rs @@ -105,18 +105,9 @@ fn check(handler: Handler, before: &str, expected: ExpectedResult) { change.edit.apply(&mut actual); if !source_change.is_snippet { - match source_change.cursor_position { - None => { - if let RangeOrOffset::Offset(before_cursor_pos) = range_or_offset { - let off = change - .edit - .apply_to_offset(before_cursor_pos) - .expect("cursor position is affected by the edit"); - actual = add_cursor(&actual, off) - } - } - Some(off) => actual = add_cursor(&actual, off.offset), - }; + if let Some(off) = source_change.cursor_position { + actual = add_cursor(&actual, off.offset) + } } assert_eq_text!(after, &actual); } From fd771707187a505c826096fc62ced6ba9b65460e Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 20 May 2020 23:07:17 +0200 Subject: [PATCH 63/70] Snippetify introduce/inline var --- .../src/handlers/inline_local_variable.rs | 47 ++++--- .../src/handlers/introduce_variable.rs | 116 ++++++++++-------- crates/ra_assists/src/tests/generated.rs | 2 +- docs/user/assists.md | 2 +- 4 files changed, 90 insertions(+), 77 deletions(-) diff --git a/crates/ra_assists/src/handlers/inline_local_variable.rs b/crates/ra_assists/src/handlers/inline_local_variable.rs index 46d675a4e8..d26e688479 100644 --- a/crates/ra_assists/src/handlers/inline_local_variable.rs +++ b/crates/ra_assists/src/handlers/inline_local_variable.rs @@ -116,7 +116,6 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O let replacement = if should_wrap { init_in_paren.clone() } else { init_str.clone() }; builder.replace(desc.file_range.range, replacement) } - builder.set_cursor(delete_range.start()) }) } @@ -149,7 +148,7 @@ fn foo() { r" fn bar(a: usize) {} fn foo() { - <|>1 + 1; + 1 + 1; if 1 > 10 { } @@ -183,7 +182,7 @@ fn foo() { r" fn bar(a: usize) {} fn foo() { - <|>(1 + 1) + 1; + (1 + 1) + 1; if (1 + 1) > 10 { } @@ -217,7 +216,7 @@ fn foo() { r" fn bar(a: usize) {} fn foo() { - <|>bar(1) + 1; + bar(1) + 1; if bar(1) > 10 { } @@ -251,7 +250,7 @@ fn foo() { r" fn bar(a: usize): usize { a } fn foo() { - <|>(bar(1) as u64) + 1; + (bar(1) as u64) + 1; if (bar(1) as u64) > 10 { } @@ -283,7 +282,7 @@ fn foo() { }", r" fn foo() { - <|>{ 10 + 1 } + 1; + { 10 + 1 } + 1; if { 10 + 1 } > 10 { } @@ -315,7 +314,7 @@ fn foo() { }", r" fn foo() { - <|>( 10 + 1 ) + 1; + ( 10 + 1 ) + 1; if ( 10 + 1 ) > 10 { } @@ -353,7 +352,7 @@ fn foo() { }", r" fn foo() { - <|>let b = bar(10 + 1) * 10; + let b = bar(10 + 1) * 10; let c = bar(10 + 1) as usize; }", ); @@ -373,7 +372,7 @@ fn foo() { r" fn foo() { let x = vec![1, 2, 3]; - <|>let b = x[0] * 10; + let b = x[0] * 10; let c = x[0] as usize; }", ); @@ -393,7 +392,7 @@ fn foo() { r" fn foo() { let bar = vec![1]; - <|>let b = bar.len() * 10; + let b = bar.len() * 10; let c = bar.len() as usize; }", ); @@ -421,7 +420,7 @@ struct Bar { fn foo() { let bar = Bar { foo: 1 }; - <|>let b = bar.foo * 10; + let b = bar.foo * 10; let c = bar.foo as usize; }", ); @@ -442,7 +441,7 @@ fn foo() -> Option { r" fn foo() -> Option { let bar = Some(1); - <|>let b = bar? * 10; + let b = bar? * 10; let c = bar? as usize; None }", @@ -462,7 +461,7 @@ fn foo() { r" fn foo() { let bar = 10; - <|>let b = &bar * 10; + let b = &bar * 10; }", ); } @@ -478,7 +477,7 @@ fn foo() { }", r" fn foo() { - <|>let b = (10, 20)[0]; + let b = (10, 20)[0]; }", ); } @@ -494,7 +493,7 @@ fn foo() { }", r" fn foo() { - <|>let b = [1, 2, 3].len(); + let b = [1, 2, 3].len(); }", ); } @@ -511,7 +510,7 @@ fn foo() { }", r" fn foo() { - <|>let b = (10 + 20) * 10; + let b = (10 + 20) * 10; let c = (10 + 20) as usize; }", ); @@ -531,7 +530,7 @@ fn foo() { r" fn foo() { let d = 10; - <|>let b = d * 10; + let b = d * 10; let c = d as usize; }", ); @@ -549,7 +548,7 @@ fn foo() { }", r" fn foo() { - <|>let b = { 10 } * 10; + let b = { 10 } * 10; let c = { 10 } as usize; }", ); @@ -569,7 +568,7 @@ fn foo() { }", r" fn foo() { - <|>let b = (10 + 20) * 10; + let b = (10 + 20) * 10; let c = (10 + 20, 20); let d = [10 + 20, 10]; let e = (10 + 20); @@ -588,7 +587,7 @@ fn foo() { }", r" fn foo() { - <|>for i in vec![10, 20] {} + for i in vec![10, 20] {} }", ); } @@ -604,7 +603,7 @@ fn foo() { }", r" fn foo() { - <|>while 1 > 0 {} + while 1 > 0 {} }", ); } @@ -622,7 +621,7 @@ fn foo() { }", r" fn foo() { - <|>loop { + loop { break 1 + 1; } }", @@ -640,7 +639,7 @@ fn foo() { }", r" fn foo() { - <|>return 1 > 0; + return 1 > 0; }", ); } @@ -656,7 +655,7 @@ fn foo() { }", r" fn foo() { - <|>match 1 > 0 {} + match 1 > 0 {} }", ); } diff --git a/crates/ra_assists/src/handlers/introduce_variable.rs b/crates/ra_assists/src/handlers/introduce_variable.rs index 56c610fedd..31d6539f7a 100644 --- a/crates/ra_assists/src/handlers/introduce_variable.rs +++ b/crates/ra_assists/src/handlers/introduce_variable.rs @@ -4,7 +4,7 @@ use ra_syntax::{ BLOCK_EXPR, BREAK_EXPR, COMMENT, LAMBDA_EXPR, LOOP_EXPR, MATCH_ARM, PATH_EXPR, RETURN_EXPR, WHITESPACE, }, - SyntaxNode, TextSize, + SyntaxNode, }; use stdx::format_to; use test_utils::mark; @@ -23,7 +23,7 @@ use crate::{AssistContext, AssistId, Assists}; // -> // ``` // fn main() { -// let var_name = (1 + 2); +// let $0var_name = (1 + 2); // var_name * 4; // } // ``` @@ -46,14 +46,13 @@ pub(crate) fn introduce_variable(acc: &mut Assists, ctx: &AssistContext) -> Opti acc.add(AssistId("introduce_variable"), "Extract into variable", target, move |edit| { let mut buf = String::new(); - let cursor_offset = if wrap_in_block { + if wrap_in_block { buf.push_str("{ let var_name = "); - TextSize::of("{ let ") } else { buf.push_str("let var_name = "); - TextSize::of("let ") }; format_to!(buf, "{}", expr.syntax()); + let full_stmt = ast::ExprStmt::cast(anchor_stmt.clone()); let is_full_stmt = if let Some(expr_stmt) = &full_stmt { Some(expr.syntax().clone()) == expr_stmt.expr().map(|e| e.syntax().clone()) @@ -65,28 +64,43 @@ pub(crate) fn introduce_variable(acc: &mut Assists, ctx: &AssistContext) -> Opti if full_stmt.unwrap().semicolon_token().is_none() { buf.push_str(";"); } - edit.replace(expr.syntax().text_range(), buf); - } else { - buf.push_str(";"); - - // We want to maintain the indent level, - // but we do not want to duplicate possible - // extra newlines in the indent block - let text = indent.text(); - if text.starts_with('\n') { - buf.push_str("\n"); - buf.push_str(text.trim_start_matches('\n')); - } else { - buf.push_str(text); - } - - edit.replace(expr.syntax().text_range(), "var_name".to_string()); - edit.insert(anchor_stmt.text_range().start(), buf); - if wrap_in_block { - edit.insert(anchor_stmt.text_range().end(), " }"); + let offset = expr.syntax().text_range(); + match ctx.config.snippet_cap { + Some(cap) => { + let snip = buf.replace("let var_name", "let $0var_name"); + edit.replace_snippet(cap, offset, snip) + } + None => edit.replace(offset, buf), } + return; + } + + buf.push_str(";"); + + // We want to maintain the indent level, + // but we do not want to duplicate possible + // extra newlines in the indent block + let text = indent.text(); + if text.starts_with('\n') { + buf.push_str("\n"); + buf.push_str(text.trim_start_matches('\n')); + } else { + buf.push_str(text); + } + + edit.replace(expr.syntax().text_range(), "var_name".to_string()); + let offset = anchor_stmt.text_range().start(); + match ctx.config.snippet_cap { + Some(cap) => { + let snip = buf.replace("let var_name", "let $0var_name"); + edit.insert_snippet(cap, offset, snip) + } + None => edit.insert(offset, buf), + } + + if wrap_in_block { + edit.insert(anchor_stmt.text_range().end(), " }"); } - edit.set_cursor(anchor_stmt.text_range().start() + cursor_offset); }) } @@ -144,15 +158,15 @@ mod tests { fn test_introduce_var_simple() { check_assist( introduce_variable, - " + r#" fn foo() { foo(<|>1 + 1<|>); -}", - " +}"#, + r#" fn foo() { - let <|>var_name = 1 + 1; + let $0var_name = 1 + 1; foo(var_name); -}", +}"#, ); } @@ -167,14 +181,14 @@ fn foo() { mark::check!(test_introduce_var_expr_stmt); check_assist( introduce_variable, - " + r#" fn foo() { <|>1 + 1<|>; -}", - " +}"#, + r#" fn foo() { - let <|>var_name = 1 + 1; -}", + let $0var_name = 1 + 1; +}"#, ); check_assist( introduce_variable, @@ -185,7 +199,7 @@ fn foo() { }", " fn foo() { - let <|>var_name = { let x = 0; x }; + let $0var_name = { let x = 0; x }; something_else(); }", ); @@ -201,7 +215,7 @@ fn foo() { }", " fn foo() { - let <|>var_name = 1; + let $0var_name = 1; var_name + 1; }", ); @@ -218,7 +232,7 @@ fn foo() { }", " fn foo() { - let <|>var_name = 1 + 1; + let $0var_name = 1 + 1; bar(var_name) }", ); @@ -230,7 +244,7 @@ fn foo() { }", " fn foo() { - let <|>var_name = bar(1 + 1); + let $0var_name = bar(1 + 1); var_name }", ) @@ -253,7 +267,7 @@ fn main() { fn main() { let x = true; let tuple = match x { - true => { let <|>var_name = 2 + 2; (var_name, true) } + true => { let $0var_name = 2 + 2; (var_name, true) } _ => (0, false) }; } @@ -283,7 +297,7 @@ fn main() { let tuple = match x { true => { let y = 1; - let <|>var_name = 2 + y; + let $0var_name = 2 + y; (var_name, true) } _ => (0, false) @@ -304,7 +318,7 @@ fn main() { ", " fn main() { - let lambda = |x: u32| { let <|>var_name = x * 2; var_name }; + let lambda = |x: u32| { let $0var_name = x * 2; var_name }; } ", ); @@ -321,7 +335,7 @@ fn main() { ", " fn main() { - let lambda = |x: u32| { let <|>var_name = x * 2; var_name }; + let lambda = |x: u32| { let $0var_name = x * 2; var_name }; } ", ); @@ -338,7 +352,7 @@ fn main() { ", " fn main() { - let <|>var_name = Some(true); + let $0var_name = Some(true); let o = var_name; } ", @@ -356,7 +370,7 @@ fn main() { ", " fn main() { - let <|>var_name = bar.foo(); + let $0var_name = bar.foo(); let v = var_name; } ", @@ -374,7 +388,7 @@ fn foo() -> u32 { ", " fn foo() -> u32 { - let <|>var_name = 2 + 2; + let $0var_name = 2 + 2; return var_name; } ", @@ -396,7 +410,7 @@ fn foo() -> u32 { fn foo() -> u32 { - let <|>var_name = 2 + 2; + let $0var_name = 2 + 2; return var_name; } ", @@ -413,7 +427,7 @@ fn foo() -> u32 { " fn foo() -> u32 { - let <|>var_name = 2 + 2; + let $0var_name = 2 + 2; return var_name; } ", @@ -438,7 +452,7 @@ fn foo() -> u32 { // bar - let <|>var_name = 2 + 2; + let $0var_name = 2 + 2; return var_name; } ", @@ -459,7 +473,7 @@ fn main() { " fn main() { let result = loop { - let <|>var_name = 2 + 2; + let $0var_name = 2 + 2; break var_name; }; } @@ -478,7 +492,7 @@ fn main() { ", " fn main() { - let <|>var_name = 0f32 as u32; + let $0var_name = 0f32 as u32; let v = var_name; } ", diff --git a/crates/ra_assists/src/tests/generated.rs b/crates/ra_assists/src/tests/generated.rs index 3e6654c175..0eeb5c1994 100644 --- a/crates/ra_assists/src/tests/generated.rs +++ b/crates/ra_assists/src/tests/generated.rs @@ -443,7 +443,7 @@ fn main() { "#####, r#####" fn main() { - let var_name = (1 + 2); + let $0var_name = (1 + 2); var_name * 4; } "#####, diff --git a/docs/user/assists.md b/docs/user/assists.md index 51807ffdac..a6e27d67f3 100644 --- a/docs/user/assists.md +++ b/docs/user/assists.md @@ -426,7 +426,7 @@ fn main() { // AFTER fn main() { - let var_name = (1 + 2); + let $0var_name = (1 + 2); var_name * 4; } ``` From 5e13e4eba17fe0d55afa76c8d5ff495228283c4e Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 20 May 2020 23:14:31 +0200 Subject: [PATCH 64/70] More snippets --- .../ra_assists/src/handlers/early_return.rs | 18 ++++----- .../ra_assists/src/handlers/merge_imports.rs | 22 +++++------ .../src/handlers/merge_match_arms.rs | 25 +++--------- crates/ra_assists/src/handlers/move_guard.rs | 33 ++++++---------- crates/ra_assists/src/handlers/remove_dbg.rs | 39 +++++-------------- crates/ra_assists/src/handlers/remove_mut.rs | 5 +-- 6 files changed, 45 insertions(+), 97 deletions(-) diff --git a/crates/ra_assists/src/handlers/early_return.rs b/crates/ra_assists/src/handlers/early_return.rs index 66b296081d..4cc75a7ce2 100644 --- a/crates/ra_assists/src/handlers/early_return.rs +++ b/crates/ra_assists/src/handlers/early_return.rs @@ -97,7 +97,6 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext) } then_block.syntax().last_child_or_token().filter(|t| t.kind() == R_CURLY)?; - let cursor_position = ctx.offset(); let target = if_expr.syntax().text_range(); acc.add(AssistId("convert_to_guarded_return"), "Convert to guarded return", target, |edit| { @@ -148,7 +147,6 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext) } }; edit.replace_ast(parent_block, ast::BlockExpr::cast(new_block).unwrap()); - edit.set_cursor(cursor_position); fn replace( new_expr: &SyntaxNode, @@ -207,7 +205,7 @@ mod tests { r#" fn main() { bar(); - if<|> !true { + if !true { return; } foo(); @@ -237,7 +235,7 @@ mod tests { r#" fn main(n: Option) { bar(); - le<|>t n = match n { + let n = match n { Some(it) => it, _ => return, }; @@ -263,7 +261,7 @@ mod tests { "#, r#" fn main() { - le<|>t x = match Err(92) { + let x = match Err(92) { Ok(it) => it, _ => return, }; @@ -291,7 +289,7 @@ mod tests { r#" fn main(n: Option) { bar(); - le<|>t n = match n { + let n = match n { Ok(it) => it, _ => return, }; @@ -321,7 +319,7 @@ mod tests { r#" fn main() { while true { - if<|> !true { + if !true { continue; } foo(); @@ -349,7 +347,7 @@ mod tests { r#" fn main() { while true { - le<|>t n = match n { + let n = match n { Some(it) => it, _ => continue, }; @@ -378,7 +376,7 @@ mod tests { r#" fn main() { loop { - if<|> !true { + if !true { continue; } foo(); @@ -406,7 +404,7 @@ mod tests { r#" fn main() { loop { - le<|>t n = match n { + let n = match n { Some(it) => it, _ => continue, }; diff --git a/crates/ra_assists/src/handlers/merge_imports.rs b/crates/ra_assists/src/handlers/merge_imports.rs index ac3e53c273..972d162419 100644 --- a/crates/ra_assists/src/handlers/merge_imports.rs +++ b/crates/ra_assists/src/handlers/merge_imports.rs @@ -58,8 +58,6 @@ pub(crate) fn merge_imports(acc: &mut Assists, ctx: &AssistContext) -> Option<() let target = tree.syntax().text_range(); acc.add(AssistId("merge_imports"), "Merge imports", target, |builder| { builder.rewrite(rewriter); - // FIXME: we only need because our diff is imprecise - builder.set_cursor(offset); }) } @@ -142,7 +140,7 @@ use std::fmt<|>::Debug; use std::fmt::Display; ", r" -use std::fmt<|>::{Debug, Display}; +use std::fmt::{Debug, Display}; ", ) } @@ -156,7 +154,7 @@ use std::fmt::Debug; use std::fmt<|>::Display; ", r" -use std::fmt:<|>:{Display, Debug}; +use std::fmt::{Display, Debug}; ", ); } @@ -169,7 +167,7 @@ use std::fmt:<|>:{Display, Debug}; use std::{fmt<|>::Debug, fmt::Display}; ", r" -use std::{fmt<|>::{Debug, Display}}; +use std::{fmt::{Debug, Display}}; ", ); check_assist( @@ -178,7 +176,7 @@ use std::{fmt<|>::{Debug, Display}}; use std::{fmt::Debug, fmt<|>::Display}; ", r" -use std::{fmt::<|>{Display, Debug}}; +use std::{fmt::{Display, Debug}}; ", ); } @@ -192,7 +190,7 @@ use std<|>::cell::*; use std::str; ", r" -use std<|>::{cell::*, str}; +use std::{cell::*, str}; ", ) } @@ -206,7 +204,7 @@ use std<|>::cell::*; use std::str::*; ", r" -use std<|>::{cell::*, str::*}; +use std::{cell::*, str::*}; ", ) } @@ -222,7 +220,7 @@ use foo::baz; /// Doc comment ", r" -use foo<|>::{bar, baz}; +use foo::{bar, baz}; /// Doc comment ", @@ -241,7 +239,7 @@ use { ", r" use { - foo<|>::{bar, baz}, + foo::{bar, baz}, }; ", ); @@ -255,7 +253,7 @@ use { ", r" use { - foo::{bar<|>, baz}, + foo::{bar, baz}, }; ", ); @@ -272,7 +270,7 @@ use foo::<|>{ }; ", r" -use foo::{<|> +use foo::{ FooBar, bar::baz}; ", diff --git a/crates/ra_assists/src/handlers/merge_match_arms.rs b/crates/ra_assists/src/handlers/merge_match_arms.rs index d4e38aa6a5..ca04ec671a 100644 --- a/crates/ra_assists/src/handlers/merge_match_arms.rs +++ b/crates/ra_assists/src/handlers/merge_match_arms.rs @@ -3,7 +3,7 @@ use std::iter::successors; use ra_syntax::{ algo::neighbor, ast::{self, AstNode}, - Direction, TextSize, + Direction, }; use crate::{AssistContext, AssistId, Assists, TextRange}; @@ -41,17 +41,6 @@ pub(crate) fn merge_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option let current_expr = current_arm.expr()?; let current_text_range = current_arm.syntax().text_range(); - enum CursorPos { - InExpr(TextSize), - InPat(TextSize), - } - let cursor_pos = ctx.offset(); - let cursor_pos = if current_expr.syntax().text_range().contains(cursor_pos) { - CursorPos::InExpr(current_text_range.end() - cursor_pos) - } else { - CursorPos::InPat(cursor_pos) - }; - // We check if the following match arms match this one. We could, but don't, // compare to the previous match arm as well. let arms_to_merge = successors(Some(current_arm), |it| neighbor(it, Direction::Next)) @@ -87,10 +76,6 @@ pub(crate) fn merge_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option let start = arms_to_merge.first().unwrap().syntax().text_range().start(); let end = arms_to_merge.last().unwrap().syntax().text_range().end(); - edit.set_cursor(match cursor_pos { - CursorPos::InExpr(back_offset) => start + TextSize::of(&arm) - back_offset, - CursorPos::InPat(offset) => offset, - }); edit.replace(TextRange::new(start, end), arm); }) } @@ -132,7 +117,7 @@ mod tests { fn main() { let x = X::A; let y = match x { - X::A | X::B => { 1i32<|> } + X::A | X::B => { 1i32 } X::C => { 2i32 } } } @@ -164,7 +149,7 @@ mod tests { fn main() { let x = X::A; let y = match x { - X::A | X::B | X::C | X::D => {<|> 1i32 }, + X::A | X::B | X::C | X::D => { 1i32 }, X::E => { 2i32 }, } } @@ -197,7 +182,7 @@ mod tests { let x = X::A; let y = match x { X::A => { 1i32 }, - _ => { 2i<|>32 } + _ => { 2i32 } } } "#, @@ -226,7 +211,7 @@ mod tests { fn main() { match X::A { - X::A<|> | X::B | X::C => 92, + X::A | X::B | X::C => 92, X::D => 62, _ => panic!(), } diff --git a/crates/ra_assists/src/handlers/move_guard.rs b/crates/ra_assists/src/handlers/move_guard.rs index fc0335b578..7edcf07489 100644 --- a/crates/ra_assists/src/handlers/move_guard.rs +++ b/crates/ra_assists/src/handlers/move_guard.rs @@ -1,7 +1,6 @@ use ra_syntax::{ - ast, - ast::{AstNode, AstToken, IfExpr, MatchArm}, - TextSize, + ast::{AstNode, IfExpr, MatchArm}, + SyntaxKind::WHITESPACE, }; use crate::{AssistContext, AssistId, Assists}; @@ -42,24 +41,15 @@ pub(crate) fn move_guard_to_arm_body(acc: &mut Assists, ctx: &AssistContext) -> let target = guard.syntax().text_range(); acc.add(AssistId("move_guard_to_arm_body"), "Move guard to arm body", target, |edit| { - let offseting_amount = match space_before_guard.and_then(|it| it.into_token()) { - Some(tok) => { - if ast::Whitespace::cast(tok.clone()).is_some() { - let ele = tok.text_range(); - edit.delete(ele); - ele.len() - } else { - TextSize::from(0) - } + match space_before_guard { + Some(element) if element.kind() == WHITESPACE => { + edit.delete(element.text_range()); } - _ => TextSize::from(0), + _ => (), }; edit.delete(guard.syntax().text_range()); edit.replace_node_and_indent(arm_expr.syntax(), buf); - edit.set_cursor( - arm_expr.syntax().text_range().start() + TextSize::from(3) - offseting_amount, - ); }) } @@ -124,7 +114,6 @@ pub(crate) fn move_arm_cond_to_match_guard(acc: &mut Assists, ctx: &AssistContex } edit.insert(match_pat.syntax().text_range().end(), buf); - edit.set_cursor(match_pat.syntax().text_range().end() + TextSize::from(1)); }, ) } @@ -172,7 +161,7 @@ mod tests { let t = 'a'; let chars = "abcd"; match t { - '\r' => if chars.clone().next() == Some('\n') { <|>false }, + '\r' => if chars.clone().next() == Some('\n') { false }, _ => true } } @@ -195,7 +184,7 @@ mod tests { r#" fn f() { match x { - y @ 4 | y @ 5 => if y > 5 { <|>true }, + y @ 4 | y @ 5 => if y > 5 { true }, _ => false } } @@ -222,7 +211,7 @@ mod tests { let t = 'a'; let chars = "abcd"; match t { - '\r' <|>if chars.clone().next() == Some('\n') => false, + '\r' if chars.clone().next() == Some('\n') => false, _ => true } } @@ -266,7 +255,7 @@ mod tests { let t = 'a'; let chars = "abcd"; match t { - '\r' <|>if chars.clone().next().is_some() => { }, + '\r' if chars.clone().next().is_some() => { }, _ => true } } @@ -296,7 +285,7 @@ mod tests { let mut t = 'a'; let chars = "abcd"; match t { - '\r' <|>if chars.clone().next().is_some() => { + '\r' if chars.clone().next().is_some() => { t = 'e'; false }, diff --git a/crates/ra_assists/src/handlers/remove_dbg.rs b/crates/ra_assists/src/handlers/remove_dbg.rs index 8eef578cf4..961ee1731a 100644 --- a/crates/ra_assists/src/handlers/remove_dbg.rs +++ b/crates/ra_assists/src/handlers/remove_dbg.rs @@ -29,26 +29,6 @@ pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { let macro_range = macro_call.syntax().text_range(); - // If the cursor is inside the macro call, we'll try to maintain the cursor - // position by subtracting the length of dbg!( from the start of the file - // range, otherwise we'll default to using the start of the macro call - let cursor_pos = { - let file_range = ctx.frange.range; - - let offset_start = file_range - .start() - .checked_sub(macro_range.start()) - .unwrap_or_else(|| TextSize::from(0)); - - let dbg_size = TextSize::of("dbg!("); - - if offset_start > dbg_size { - file_range.start() - dbg_size - } else { - macro_range.start() - } - }; - let macro_content = { let macro_args = macro_call.token_tree()?.syntax().clone(); @@ -58,9 +38,8 @@ pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { }; let target = macro_call.syntax().text_range(); - acc.add(AssistId("remove_dbg"), "Remove dbg!()", target, |edit| { - edit.replace(macro_range, macro_content); - edit.set_cursor(cursor_pos); + acc.add(AssistId("remove_dbg"), "Remove dbg!()", target, |builder| { + builder.replace(macro_range, macro_content); }) } @@ -94,13 +73,13 @@ mod tests { #[test] fn test_remove_dbg() { - check_assist(remove_dbg, "<|>dbg!(1 + 1)", "<|>1 + 1"); + check_assist(remove_dbg, "<|>dbg!(1 + 1)", "1 + 1"); - check_assist(remove_dbg, "dbg!<|>((1 + 1))", "<|>(1 + 1)"); + check_assist(remove_dbg, "dbg!<|>((1 + 1))", "(1 + 1)"); - check_assist(remove_dbg, "dbg!(1 <|>+ 1)", "1 <|>+ 1"); + check_assist(remove_dbg, "dbg!(1 <|>+ 1)", "1 + 1"); - check_assist(remove_dbg, "let _ = <|>dbg!(1 + 1)", "let _ = <|>1 + 1"); + check_assist(remove_dbg, "let _ = <|>dbg!(1 + 1)", "let _ = 1 + 1"); check_assist( remove_dbg, @@ -113,7 +92,7 @@ fn foo(n: usize) { ", " fn foo(n: usize) { - if let Some(_) = n.<|>checked_sub(4) { + if let Some(_) = n.checked_sub(4) { // ... } } @@ -122,8 +101,8 @@ fn foo(n: usize) { } #[test] fn test_remove_dbg_with_brackets_and_braces() { - check_assist(remove_dbg, "dbg![<|>1 + 1]", "<|>1 + 1"); - check_assist(remove_dbg, "dbg!{<|>1 + 1}", "<|>1 + 1"); + check_assist(remove_dbg, "dbg![<|>1 + 1]", "1 + 1"); + check_assist(remove_dbg, "dbg!{<|>1 + 1}", "1 + 1"); } #[test] diff --git a/crates/ra_assists/src/handlers/remove_mut.rs b/crates/ra_assists/src/handlers/remove_mut.rs index dce546db79..fe4eada034 100644 --- a/crates/ra_assists/src/handlers/remove_mut.rs +++ b/crates/ra_assists/src/handlers/remove_mut.rs @@ -26,8 +26,7 @@ pub(crate) fn remove_mut(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { }; let target = mut_token.text_range(); - acc.add(AssistId("remove_mut"), "Remove `mut` keyword", target, |edit| { - edit.set_cursor(delete_from); - edit.delete(TextRange::new(delete_from, delete_to)); + acc.add(AssistId("remove_mut"), "Remove `mut` keyword", target, |builder| { + builder.delete(TextRange::new(delete_from, delete_to)); }) } From 8300132ed0676496ec4adda49eb682ac24bdb5b9 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 20 May 2020 23:50:29 +0200 Subject: [PATCH 65/70] More snippets --- .../src/handlers/replace_if_let_with_match.rs | 29 +++++++++---------- .../src/handlers/replace_let_with_if_let.rs | 5 +--- .../ra_assists/src/handlers/split_import.rs | 6 ++-- 3 files changed, 17 insertions(+), 23 deletions(-) diff --git a/crates/ra_assists/src/handlers/replace_if_let_with_match.rs b/crates/ra_assists/src/handlers/replace_if_let_with_match.rs index 65f5fc6abe..e016f51c3e 100644 --- a/crates/ra_assists/src/handlers/replace_if_let_with_match.rs +++ b/crates/ra_assists/src/handlers/replace_if_let_with_match.rs @@ -68,7 +68,6 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext) .indent(IndentLevel::from_node(if_expr.syntax())) }; - edit.set_cursor(if_expr.syntax().text_range().start()); edit.replace_ast::(if_expr.into(), match_expr); }) } @@ -83,7 +82,7 @@ mod tests { fn test_replace_if_let_with_match_unwraps_simple_expressions() { check_assist( replace_if_let_with_match, - " + r#" impl VariantData { pub fn is_struct(&self) -> bool { if <|>let VariantData::Struct(..) = *self { @@ -92,16 +91,16 @@ impl VariantData { false } } -} ", - " +} "#, + r#" impl VariantData { pub fn is_struct(&self) -> bool { - <|>match *self { + match *self { VariantData::Struct(..) => true, _ => false, } } -} ", +} "#, ) } @@ -109,7 +108,7 @@ impl VariantData { fn test_replace_if_let_with_match_doesnt_unwrap_multiline_expressions() { check_assist( replace_if_let_with_match, - " + r#" fn foo() { if <|>let VariantData::Struct(..) = a { bar( @@ -118,10 +117,10 @@ fn foo() { } else { false } -} ", - " +} "#, + r#" fn foo() { - <|>match a { + match a { VariantData::Struct(..) => { bar( 123 @@ -129,7 +128,7 @@ fn foo() { } _ => false, } -} ", +} "#, ) } @@ -137,7 +136,7 @@ fn foo() { fn replace_if_let_with_match_target() { check_assist_target( replace_if_let_with_match, - " + r#" impl VariantData { pub fn is_struct(&self) -> bool { if <|>let VariantData::Struct(..) = *self { @@ -146,7 +145,7 @@ impl VariantData { false } } -} ", +} "#, "if let VariantData::Struct(..) = *self { true } else { @@ -176,7 +175,7 @@ enum Option { Some(T), None } use Option::*; fn foo(x: Option) { - <|>match x { + match x { Some(x) => println!("{}", x), None => println!("none"), } @@ -206,7 +205,7 @@ enum Result { Ok(T), Err(E) } use Result::*; fn foo(x: Result) { - <|>match x { + match x { Ok(x) => println!("{}", x), Err(_) => println!("none"), } diff --git a/crates/ra_assists/src/handlers/replace_let_with_if_let.rs b/crates/ra_assists/src/handlers/replace_let_with_if_let.rs index 482957dc60..761557ac05 100644 --- a/crates/ra_assists/src/handlers/replace_let_with_if_let.rs +++ b/crates/ra_assists/src/handlers/replace_let_with_if_let.rs @@ -58,12 +58,9 @@ pub(crate) fn replace_let_with_if_let(acc: &mut Assists, ctx: &AssistContext) -> let stmt = make::expr_stmt(if_); let placeholder = stmt.syntax().descendants().find_map(ast::PlaceholderPat::cast).unwrap(); - let target_offset = - let_stmt.syntax().text_range().start() + placeholder.syntax().text_range().start(); let stmt = stmt.replace_descendant(placeholder.into(), original_pat); edit.replace_ast(ast::Stmt::from(let_stmt), ast::Stmt::from(stmt)); - edit.set_cursor(target_offset); }) } @@ -88,7 +85,7 @@ fn main() { enum E { X(T), Y(T) } fn main() { - if let <|>x = E::X(92) { + if let x = E::X(92) { } } ", diff --git a/crates/ra_assists/src/handlers/split_import.rs b/crates/ra_assists/src/handlers/split_import.rs index b2757e50ce..c7a8744802 100644 --- a/crates/ra_assists/src/handlers/split_import.rs +++ b/crates/ra_assists/src/handlers/split_import.rs @@ -26,12 +26,10 @@ pub(crate) fn split_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()> if new_tree == use_tree { return None; } - let cursor = ctx.offset(); let target = colon_colon.text_range(); acc.add(AssistId("split_import"), "Split import", target, |edit| { edit.replace_ast(use_tree, new_tree); - edit.set_cursor(cursor); }) } @@ -46,7 +44,7 @@ mod tests { check_assist( split_import, "use crate::<|>db::RootDatabase;", - "use crate::<|>{db::RootDatabase};", + "use crate::{db::RootDatabase};", ) } @@ -55,7 +53,7 @@ mod tests { check_assist( split_import, "use crate:<|>:db::{RootDatabase, FileSymbol}", - "use crate:<|>:{db::{RootDatabase, FileSymbol}}", + "use crate::{db::{RootDatabase, FileSymbol}}", ) } From 6cdfd1c3cf3d58eee90b5034d4e2d702fdc0f8f5 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Tue, 19 May 2020 00:39:50 +0200 Subject: [PATCH 66/70] Make `find_path_inner` a query This eliminates any remaining performance problems in the "Implement default members" assist (at least that I've found). --- crates/ra_hir_def/src/db.rs | 4 ++++ crates/ra_hir_def/src/find_path.rs | 7 +++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/crates/ra_hir_def/src/db.rs b/crates/ra_hir_def/src/db.rs index 2f71511ba9..945a0025e5 100644 --- a/crates/ra_hir_def/src/db.rs +++ b/crates/ra_hir_def/src/db.rs @@ -17,6 +17,7 @@ use crate::{ item_scope::ItemInNs, lang_item::{LangItemTarget, LangItems}, nameres::{raw::RawItems, CrateDefMap}, + path::ModPath, visibility::Visibility, AttrDefId, ConstId, ConstLoc, DefWithBodyId, EnumId, EnumLoc, FunctionId, FunctionLoc, GenericDefId, ImplId, ImplLoc, ModuleId, StaticId, StaticLoc, StructId, StructLoc, TraitId, @@ -118,6 +119,9 @@ pub trait DefDatabase: InternDatabase + AstDatabase + Upcast { item: ItemInNs, krate: CrateId, ) -> Arc<[(ModuleId, Name, Visibility)]>; + + #[salsa::invoke(find_path::find_path_inner_query)] + fn find_path_inner(&self, item: ItemInNs, from: ModuleId, max_len: usize) -> Option; } fn crate_def_map_wait(db: &impl DefDatabase, krate: CrateId) -> Arc { diff --git a/crates/ra_hir_def/src/find_path.rs b/crates/ra_hir_def/src/find_path.rs index 68d3cde08a..4db7984730 100644 --- a/crates/ra_hir_def/src/find_path.rs +++ b/crates/ra_hir_def/src/find_path.rs @@ -20,7 +20,7 @@ use crate::{ /// *from where* you're referring to the item, hence the `from` parameter. pub fn find_path(db: &dyn DefDatabase, item: ItemInNs, from: ModuleId) -> Option { let _p = profile("find_path"); - find_path_inner(db, item, from, MAX_PATH_LEN) + db.find_path_inner(item, from, MAX_PATH_LEN) } const MAX_PATH_LEN: usize = 15; @@ -49,7 +49,7 @@ impl ModPath { } } -fn find_path_inner( +pub(crate) fn find_path_inner_query( db: &dyn DefDatabase, item: ItemInNs, from: ModuleId, @@ -140,8 +140,7 @@ fn find_path_inner( let mut best_path = None; let mut best_path_len = max_len; for (module_id, name) in importable_locations { - let mut path = match find_path_inner( - db, + let mut path = match db.find_path_inner( ItemInNs::Types(ModuleDefId::ModuleId(module_id)), from, best_path_len - 1, From 4ac0abd2960acf1b3a357c681e64b3cddba6fc8e Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 21 May 2020 00:01:08 +0200 Subject: [PATCH 67/70] Snippetify unwrap -> match --- .../src/handlers/replace_unwrap_with_match.rs | 41 +++++++++++++------ crates/ra_assists/src/tests/generated.rs | 2 +- docs/user/assists.md | 2 +- 3 files changed, 31 insertions(+), 14 deletions(-) diff --git a/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs b/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs index b379b55a83..cff7dfb812 100644 --- a/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs +++ b/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs @@ -9,7 +9,10 @@ use ra_syntax::{ AstNode, }; -use crate::{utils::TryEnum, AssistContext, AssistId, Assists}; +use crate::{ + utils::{render_snippet, Cursor, TryEnum}, + AssistContext, AssistId, Assists, +}; // Assist: replace_unwrap_with_match // @@ -29,7 +32,7 @@ use crate::{utils::TryEnum, AssistContext, AssistId, Assists}; // let x: Result = Result::Ok(92); // let y = match x { // Ok(a) => a, -// _ => unreachable!(), +// $0_ => unreachable!(), // }; // } // ``` @@ -43,7 +46,7 @@ pub(crate) fn replace_unwrap_with_match(acc: &mut Assists, ctx: &AssistContext) let ty = ctx.sema.type_of_expr(&caller)?; let happy_variant = TryEnum::from_ty(&ctx.sema, &ty)?.happy_case(); let target = method_call.syntax().text_range(); - acc.add(AssistId("replace_unwrap_with_match"), "Replace unwrap with match", target, |edit| { + acc.add(AssistId("replace_unwrap_with_match"), "Replace unwrap with match", target, |builder| { let ok_path = make::path_unqualified(make::path_segment(make::name_ref(happy_variant))); let it = make::bind_pat(make::name("a")).into(); let ok_tuple = make::tuple_struct_pat(ok_path, iter::once(it)).into(); @@ -58,16 +61,30 @@ pub(crate) fn replace_unwrap_with_match(acc: &mut Assists, ctx: &AssistContext) let match_expr = make::expr_match(caller.clone(), match_arm_list) .indent(IndentLevel::from_node(method_call.syntax())); - edit.set_cursor(caller.syntax().text_range().start()); - edit.replace_ast::(method_call.into(), match_expr); + let range = method_call.syntax().text_range(); + match ctx.config.snippet_cap { + Some(cap) => { + let err_arm = match_expr + .syntax() + .descendants() + .filter_map(ast::MatchArm::cast) + .last() + .unwrap(); + let snippet = + render_snippet(cap, match_expr.syntax(), Cursor::Before(err_arm.syntax())); + builder.replace_snippet(cap, range, snippet) + } + None => builder.replace(range, match_expr.to_string()), + } }) } #[cfg(test)] mod tests { - use super::*; use crate::tests::{check_assist, check_assist_target}; + use super::*; + #[test] fn test_replace_result_unwrap_with_match() { check_assist( @@ -85,9 +102,9 @@ enum Result { Ok(T), Err(E) } fn i(a: T) -> T { a } fn main() { let x: Result = Result::Ok(92); - let y = <|>match i(x) { + let y = match i(x) { Ok(a) => a, - _ => unreachable!(), + $0_ => unreachable!(), }; } ", @@ -111,9 +128,9 @@ enum Option { Some(T), None } fn i(a: T) -> T { a } fn main() { let x = Option::Some(92); - let y = <|>match i(x) { + let y = match i(x) { Some(a) => a, - _ => unreachable!(), + $0_ => unreachable!(), }; } ", @@ -137,9 +154,9 @@ enum Result { Ok(T), Err(E) } fn i(a: T) -> T { a } fn main() { let x: Result = Result::Ok(92); - let y = <|>match i(x) { + let y = match i(x) { Ok(a) => a, - _ => unreachable!(), + $0_ => unreachable!(), }.count_zeroes(); } ", diff --git a/crates/ra_assists/src/tests/generated.rs b/crates/ra_assists/src/tests/generated.rs index 0eeb5c1994..250e56a696 100644 --- a/crates/ra_assists/src/tests/generated.rs +++ b/crates/ra_assists/src/tests/generated.rs @@ -764,7 +764,7 @@ fn main() { let x: Result = Result::Ok(92); let y = match x { Ok(a) => a, - _ => unreachable!(), + $0_ => unreachable!(), }; } "#####, diff --git a/docs/user/assists.md b/docs/user/assists.md index a6e27d67f3..4ad7ea59d2 100644 --- a/docs/user/assists.md +++ b/docs/user/assists.md @@ -733,7 +733,7 @@ fn main() { let x: Result = Result::Ok(92); let y = match x { Ok(a) => a, - _ => unreachable!(), + $0_ => unreachable!(), }; } ``` From 70930d3bb2ba1d4a7a7d4a489da714096294acca Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 21 May 2020 00:03:42 +0200 Subject: [PATCH 68/70] Remove set_cursor --- crates/ra_assists/src/assist_context.rs | 20 +++------------- .../ra_assists/src/handlers/unwrap_block.rs | 24 ++++++++----------- 2 files changed, 13 insertions(+), 31 deletions(-) diff --git a/crates/ra_assists/src/assist_context.rs b/crates/ra_assists/src/assist_context.rs index 005c177765..9f6ca449b4 100644 --- a/crates/ra_assists/src/assist_context.rs +++ b/crates/ra_assists/src/assist_context.rs @@ -171,19 +171,13 @@ impl Assists { pub(crate) struct AssistBuilder { edit: TextEditBuilder, - cursor_position: Option, file: FileId, is_snippet: bool, } impl AssistBuilder { pub(crate) fn new(file: FileId) -> AssistBuilder { - AssistBuilder { - edit: TextEditBuilder::default(), - cursor_position: None, - file, - is_snippet: false, - } + AssistBuilder { edit: TextEditBuilder::default(), file, is_snippet: false } } /// Remove specified `range` of text. @@ -241,10 +235,6 @@ impl AssistBuilder { algo::diff(&node, &new).into_text_edit(&mut self.edit) } - /// Specify desired position of the cursor after the assist is applied. - pub(crate) fn set_cursor(&mut self, offset: TextSize) { - self.cursor_position = Some(offset) - } // FIXME: better API pub(crate) fn set_file(&mut self, assist_file: FileId) { self.file = assist_file; @@ -258,12 +248,8 @@ impl AssistBuilder { fn finish(self, change_label: String) -> SourceChange { let edit = self.edit.finish(); - if edit.is_empty() && self.cursor_position.is_none() { - panic!("Only call `add_assist` if the assist can be applied") - } - let mut res = - SingleFileChange { label: change_label, edit, cursor_position: self.cursor_position } - .into_source_change(self.file); + let mut res = SingleFileChange { label: change_label, edit, cursor_position: None } + .into_source_change(self.file); if self.is_snippet { res.is_snippet = true; } diff --git a/crates/ra_assists/src/handlers/unwrap_block.rs b/crates/ra_assists/src/handlers/unwrap_block.rs index b76182d792..8440c7d0f4 100644 --- a/crates/ra_assists/src/handlers/unwrap_block.rs +++ b/crates/ra_assists/src/handlers/unwrap_block.rs @@ -62,7 +62,6 @@ pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()> let range_to_del_else_if = TextRange::new(ancestor_then_branch.syntax().text_range().end(), l_curly_token.text_range().start()); let range_to_del_rest = TextRange::new(then_branch.syntax().text_range().end(), if_expr.syntax().text_range().end()); - edit.set_cursor(ancestor_then_branch.syntax().text_range().end()); edit.delete(range_to_del_rest); edit.delete(range_to_del_else_if); edit.replace(target, update_expr_string(then_branch.to_string(), &[' ', '{'])); @@ -79,7 +78,6 @@ pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()> return acc.add(assist_id, assist_label, target, |edit| { let range_to_del = TextRange::new(then_branch.syntax().text_range().end(), l_curly_token.text_range().start()); - edit.set_cursor(then_branch.syntax().text_range().end()); edit.delete(range_to_del); edit.replace(target, update_expr_string(else_block.to_string(), &[' ', '{'])); }); @@ -97,8 +95,6 @@ pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()> let target = expr_to_unwrap.syntax().text_range(); acc.add(assist_id, assist_label, target, |edit| { - edit.set_cursor(expr.syntax().text_range().start()); - edit.replace( expr.syntax().text_range(), update_expr_string(expr_to_unwrap.to_string(), &[' ', '{', '\n']), @@ -154,7 +150,7 @@ mod tests { r#" fn main() { bar(); - <|>foo(); + foo(); //comment bar(); @@ -188,7 +184,7 @@ mod tests { //comment bar(); - }<|> + } println!("bar"); } "#, @@ -222,7 +218,7 @@ mod tests { //comment //bar(); - }<|> + } println!("bar"); } "#, @@ -258,7 +254,7 @@ mod tests { //bar(); } else if false { println!("bar"); - }<|> + } println!("foo"); } "#, @@ -298,7 +294,7 @@ mod tests { println!("bar"); } else if true { println!("foo"); - }<|> + } println!("else"); } "#, @@ -336,7 +332,7 @@ mod tests { //bar(); } else if false { println!("bar"); - }<|> + } println!("foo"); } "#, @@ -383,7 +379,7 @@ mod tests { "#, r#" fn main() { - <|>if true { + if true { foo(); //comment @@ -417,7 +413,7 @@ mod tests { r#" fn main() { for i in 0..5 { - <|>foo(); + foo(); //comment bar(); @@ -447,7 +443,7 @@ mod tests { "#, r#" fn main() { - <|>if true { + if true { foo(); //comment @@ -480,7 +476,7 @@ mod tests { "#, r#" fn main() { - <|>if true { + if true { foo(); //comment From 04a8daaa33ad8aa068ccc24011c0844d06ba0ee7 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 21 May 2020 00:46:08 +0200 Subject: [PATCH 69/70] Remove unused cursor positions --- crates/ra_ide/src/typing.rs | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/crates/ra_ide/src/typing.rs b/crates/ra_ide/src/typing.rs index 6f04f0be4e..867a0f0bdc 100644 --- a/crates/ra_ide/src/typing.rs +++ b/crates/ra_ide/src/typing.rs @@ -111,7 +111,7 @@ fn on_dot_typed(file: &SourceFile, offset: TextSize) -> Option Some(SingleFileChange { label: "reindent dot".to_string(), edit: TextEdit::replace(TextRange::new(offset - current_indent_len, offset), target_indent), - cursor_position: Some(offset + target_indent_len - current_indent_len + TextSize::of('.')), + cursor_position: None, }) } @@ -130,7 +130,7 @@ fn on_arrow_typed(file: &SourceFile, offset: TextSize) -> Option Option<(String, SingleFileChange)> { + fn do_type_char(char_typed: char, before: &str) -> Option { let (offset, before) = extract_offset(before); let edit = TextEdit::insert(offset, char_typed.to_string()); let mut before = before.to_string(); @@ -148,21 +148,15 @@ mod tests { let parse = SourceFile::parse(&before); on_char_typed_inner(&parse.tree(), offset, char_typed).map(|it| { it.edit.apply(&mut before); - (before.to_string(), it) + before.to_string() }) } fn type_char(char_typed: char, before: &str, after: &str) { - let (actual, file_change) = do_type_char(char_typed, before) + let actual = do_type_char(char_typed, before) .unwrap_or_else(|| panic!("typing `{}` did nothing", char_typed)); - if after.contains("<|>") { - let (offset, after) = extract_offset(after); - assert_eq_text!(&after, &actual); - assert_eq!(file_change.cursor_position, Some(offset)) - } else { - assert_eq_text!(after, &actual); - } + assert_eq_text!(after, &actual); } fn type_char_noop(char_typed: char, before: &str) { @@ -350,6 +344,6 @@ fn foo() { #[test] fn adds_space_after_return_type() { - type_char('>', "fn foo() -<|>{ 92 }", "fn foo() -><|> { 92 }") + type_char('>', "fn foo() -<|>{ 92 }", "fn foo() -> { 92 }") } } From 4fdb1eac08bc29029fe888967dcc11d38d25c205 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 21 May 2020 00:46:08 +0200 Subject: [PATCH 70/70] Remove unused cursor positions --- crates/ra_assists/src/assist_context.rs | 3 +-- crates/ra_ide/src/typing.rs | 3 --- crates/ra_ide_db/src/source_change.rs | 5 ++--- 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/crates/ra_assists/src/assist_context.rs b/crates/ra_assists/src/assist_context.rs index 9f6ca449b4..f3af70a3ec 100644 --- a/crates/ra_assists/src/assist_context.rs +++ b/crates/ra_assists/src/assist_context.rs @@ -248,8 +248,7 @@ impl AssistBuilder { fn finish(self, change_label: String) -> SourceChange { let edit = self.edit.finish(); - let mut res = SingleFileChange { label: change_label, edit, cursor_position: None } - .into_source_change(self.file); + let mut res = SingleFileChange { label: change_label, edit }.into_source_change(self.file); if self.is_snippet { res.is_snippet = true; } diff --git a/crates/ra_ide/src/typing.rs b/crates/ra_ide/src/typing.rs index 867a0f0bdc..cd48cad93b 100644 --- a/crates/ra_ide/src/typing.rs +++ b/crates/ra_ide/src/typing.rs @@ -82,7 +82,6 @@ fn on_eq_typed(file: &SourceFile, offset: TextSize) -> Option Some(SingleFileChange { label: "add semicolon".to_string(), edit: TextEdit::insert(offset, ";".to_string()), - cursor_position: None, }) } @@ -111,7 +110,6 @@ fn on_dot_typed(file: &SourceFile, offset: TextSize) -> Option Some(SingleFileChange { label: "reindent dot".to_string(), edit: TextEdit::replace(TextRange::new(offset - current_indent_len, offset), target_indent), - cursor_position: None, }) } @@ -130,7 +128,6 @@ fn on_arrow_typed(file: &SourceFile, offset: TextSize) -> Option, } impl SingleFileChange { @@ -118,7 +117,7 @@ impl SingleFileChange { label: self.label, source_file_edits: vec![SourceFileEdit { file_id, edit: self.edit }], file_system_edits: Vec::new(), - cursor_position: self.cursor_position.map(|offset| FilePosition { file_id, offset }), + cursor_position: None, is_snippet: false, } }