From 316795e074dff8f627f8c70c85d236420ecfb3a6 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Tue, 24 Dec 2019 02:19:09 +0200 Subject: Initial auto import action implementation --- crates/ra_assists/src/assist_ctx.rs | 2 -- 1 file changed, 2 deletions(-) (limited to 'crates/ra_assists/src/assist_ctx.rs') diff --git a/crates/ra_assists/src/assist_ctx.rs b/crates/ra_assists/src/assist_ctx.rs index 43f0d664b..2ab65ab99 100644 --- a/crates/ra_assists/src/assist_ctx.rs +++ b/crates/ra_assists/src/assist_ctx.rs @@ -101,7 +101,6 @@ impl<'a, DB: HirDatabase> AssistCtx<'a, DB> { Some(assist) } - #[allow(dead_code)] // will be used for auto import assist with multiple actions pub(crate) fn add_assist_group( self, id: AssistId, @@ -168,7 +167,6 @@ pub(crate) struct ActionBuilder { } impl ActionBuilder { - #[allow(dead_code)] /// Adds a custom label to the action, if it needs to be different from the assist label pub(crate) fn label(&mut self, label: impl Into) { self.label = Some(label.into()) -- cgit v1.2.3 From 2c922ef54958a82ce745e6db6834062f97f21bed Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 6 Feb 2020 16:53:42 +0100 Subject: Start switching assists to a root database --- crates/ra_assists/src/assist_ctx.rs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) (limited to 'crates/ra_assists/src/assist_ctx.rs') diff --git a/crates/ra_assists/src/assist_ctx.rs b/crates/ra_assists/src/assist_ctx.rs index 2ab65ab99..b6cac96fa 100644 --- a/crates/ra_assists/src/assist_ctx.rs +++ b/crates/ra_assists/src/assist_ctx.rs @@ -1,8 +1,9 @@ //! This module defines `AssistCtx` -- the API surface that is exposed to assists. use either::Either; use hir::{db::HirDatabase, InFile, SourceAnalyzer, SourceBinder}; -use ra_db::FileRange; +use ra_db::{FileRange, SourceDatabase}; use ra_fmt::{leading_indent, reindent}; +use ra_ide_db::RootDatabase; use ra_syntax::{ algo::{self, find_covering_element, find_node_at_offset}, AstNode, SourceFile, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, TextRange, TextUnit, @@ -67,17 +68,24 @@ impl<'a, DB> Clone for AssistCtx<'a, DB> { } } -impl<'a, DB: HirDatabase> AssistCtx<'a, DB> { - pub(crate) fn with_ctx(db: &DB, frange: FileRange, should_compute_edit: bool, f: F) -> T +impl<'a> AssistCtx<'a, RootDatabase> { + pub(crate) fn with_ctx( + db: &RootDatabase, + frange: FileRange, + should_compute_edit: bool, + f: F, + ) -> T where - F: FnOnce(AssistCtx) -> T, + F: FnOnce(AssistCtx) -> T, { let parse = db.parse(frange.file_id); let ctx = AssistCtx { db, frange, source_file: parse.tree(), should_compute_edit }; f(ctx) } +} +impl<'a, DB: HirDatabase> AssistCtx<'a, DB> { pub(crate) fn add_assist( self, id: AssistId, -- cgit v1.2.3 From cf812c12d1ac7944d1c18877ee93bea02d91e99f Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 6 Feb 2020 16:58:57 +0100 Subject: Assists are not generic --- crates/ra_assists/src/assist_ctx.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'crates/ra_assists/src/assist_ctx.rs') diff --git a/crates/ra_assists/src/assist_ctx.rs b/crates/ra_assists/src/assist_ctx.rs index b6cac96fa..f32072dbd 100644 --- a/crates/ra_assists/src/assist_ctx.rs +++ b/crates/ra_assists/src/assist_ctx.rs @@ -1,6 +1,6 @@ //! This module defines `AssistCtx` -- the API surface that is exposed to assists. use either::Either; -use hir::{db::HirDatabase, InFile, SourceAnalyzer, SourceBinder}; +use hir::{InFile, SourceAnalyzer, SourceBinder}; use ra_db::{FileRange, SourceDatabase}; use ra_fmt::{leading_indent, reindent}; use ra_ide_db::RootDatabase; @@ -50,14 +50,14 @@ pub(crate) enum Assist { /// moment, because the LSP API is pretty awkward in this place, and it's much /// easier to just compute the edit eagerly :-) #[derive(Debug)] -pub(crate) struct AssistCtx<'a, DB> { - pub(crate) db: &'a DB, +pub(crate) struct AssistCtx<'a> { + pub(crate) db: &'a RootDatabase, pub(crate) frange: FileRange, source_file: SourceFile, should_compute_edit: bool, } -impl<'a, DB> Clone for AssistCtx<'a, DB> { +impl<'a> Clone for AssistCtx<'a> { fn clone(&self) -> Self { AssistCtx { db: self.db, @@ -68,7 +68,7 @@ impl<'a, DB> Clone for AssistCtx<'a, DB> { } } -impl<'a> AssistCtx<'a, RootDatabase> { +impl<'a> AssistCtx<'a> { pub(crate) fn with_ctx( db: &RootDatabase, frange: FileRange, @@ -76,7 +76,7 @@ impl<'a> AssistCtx<'a, RootDatabase> { f: F, ) -> T where - F: FnOnce(AssistCtx) -> T, + F: FnOnce(AssistCtx) -> T, { let parse = db.parse(frange.file_id); @@ -85,7 +85,7 @@ impl<'a> AssistCtx<'a, RootDatabase> { } } -impl<'a, DB: HirDatabase> AssistCtx<'a, DB> { +impl<'a> AssistCtx<'a> { pub(crate) fn add_assist( self, id: AssistId, @@ -149,7 +149,7 @@ impl<'a, DB: HirDatabase> AssistCtx<'a, DB> { pub(crate) fn covering_element(&self) -> SyntaxElement { find_covering_element(self.source_file.syntax(), self.frange.range) } - pub(crate) fn source_binder(&self) -> SourceBinder<'a, DB> { + pub(crate) fn source_binder(&self) -> SourceBinder<'a, RootDatabase> { SourceBinder::new(self.db) } pub(crate) fn source_analyzer( -- cgit v1.2.3 From b831b17b3dbc475420924834b250f07bccd673d0 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Fri, 7 Feb 2020 14:53:50 +0100 Subject: Simplify --- crates/ra_assists/src/assist_ctx.rs | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) (limited to 'crates/ra_assists/src/assist_ctx.rs') diff --git a/crates/ra_assists/src/assist_ctx.rs b/crates/ra_assists/src/assist_ctx.rs index f32072dbd..b2381bd97 100644 --- a/crates/ra_assists/src/assist_ctx.rs +++ b/crates/ra_assists/src/assist_ctx.rs @@ -69,23 +69,11 @@ impl<'a> Clone for AssistCtx<'a> { } impl<'a> AssistCtx<'a> { - pub(crate) fn with_ctx( - db: &RootDatabase, - frange: FileRange, - should_compute_edit: bool, - f: F, - ) -> T - where - F: FnOnce(AssistCtx) -> T, - { + pub fn new(db: &RootDatabase, frange: FileRange, should_compute_edit: bool) -> AssistCtx { let parse = db.parse(frange.file_id); - - let ctx = AssistCtx { db, frange, source_file: parse.tree(), should_compute_edit }; - f(ctx) + AssistCtx { db, frange, source_file: parse.tree(), should_compute_edit } } -} -impl<'a> AssistCtx<'a> { pub(crate) fn add_assist( self, id: AssistId, -- cgit v1.2.3 From 6ac9c4ad6ae2ce246a8c70d468ae2dabb484a03d Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Fri, 7 Feb 2020 15:04:50 +0100 Subject: Cleanup --- crates/ra_assists/src/assist_ctx.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'crates/ra_assists/src/assist_ctx.rs') diff --git a/crates/ra_assists/src/assist_ctx.rs b/crates/ra_assists/src/assist_ctx.rs index b2381bd97..44d6f6808 100644 --- a/crates/ra_assists/src/assist_ctx.rs +++ b/crates/ra_assists/src/assist_ctx.rs @@ -57,7 +57,7 @@ pub(crate) struct AssistCtx<'a> { should_compute_edit: bool, } -impl<'a> Clone for AssistCtx<'a> { +impl Clone for AssistCtx<'_> { fn clone(&self) -> Self { AssistCtx { db: self.db, @@ -80,8 +80,7 @@ impl<'a> AssistCtx<'a> { label: impl Into, f: impl FnOnce(&mut ActionBuilder), ) -> Option { - let label = AssistLabel { label: label.into(), id }; - assert!(label.label.chars().nth(0).unwrap().is_uppercase()); + let label = AssistLabel::new(label.into(), id); let assist = if self.should_compute_edit { let action = { @@ -103,7 +102,7 @@ impl<'a> AssistCtx<'a> { label: impl Into, f: impl FnOnce() -> Vec, ) -> Option { - let label = AssistLabel { label: label.into(), id }; + let label = AssistLabel::new(label.into(), id); let assist = if self.should_compute_edit { let actions = f(); assert!(!actions.is_empty(), "Assist cannot have no"); -- cgit v1.2.3 From 561b4b11ff1d87ea1ff2477dcba6ae1f396573a3 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Fri, 7 Feb 2020 15:53:31 +0100 Subject: Name assist handlers --- crates/ra_assists/src/assist_ctx.rs | 2 ++ 1 file changed, 2 insertions(+) (limited to 'crates/ra_assists/src/assist_ctx.rs') diff --git a/crates/ra_assists/src/assist_ctx.rs b/crates/ra_assists/src/assist_ctx.rs index 44d6f6808..81f999090 100644 --- a/crates/ra_assists/src/assist_ctx.rs +++ b/crates/ra_assists/src/assist_ctx.rs @@ -19,6 +19,8 @@ pub(crate) enum Assist { Resolved { assist: ResolvedAssist }, } +pub(crate) type AssistHandler = fn(AssistCtx) -> Option; + /// `AssistCtx` allows to apply an assist or check if it could be applied. /// /// Assists use a somewhat over-engineered approach, given the current needs. The -- cgit v1.2.3 From fb99831cb044e5fbf171bdd950a7486a01ce0d51 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sun, 9 Feb 2020 14:30:27 +0100 Subject: Slightly simpler API for groups --- crates/ra_assists/src/assist_ctx.rs | 92 +++++++++++++++++++++++++------------ 1 file changed, 63 insertions(+), 29 deletions(-) (limited to 'crates/ra_assists/src/assist_ctx.rs') diff --git a/crates/ra_assists/src/assist_ctx.rs b/crates/ra_assists/src/assist_ctx.rs index 81f999090..5924a3fd5 100644 --- a/crates/ra_assists/src/assist_ctx.rs +++ b/crates/ra_assists/src/assist_ctx.rs @@ -98,30 +98,8 @@ impl<'a> AssistCtx<'a> { Some(assist) } - pub(crate) fn add_assist_group( - self, - id: AssistId, - label: impl Into, - f: impl FnOnce() -> Vec, - ) -> Option { - let label = AssistLabel::new(label.into(), id); - let assist = if self.should_compute_edit { - let actions = f(); - assert!(!actions.is_empty(), "Assist cannot have no"); - - Assist::Resolved { - assist: ResolvedAssist { - label, - action_data: Either::Right( - actions.into_iter().map(ActionBuilder::build).collect(), - ), - }, - } - } else { - Assist::Unresolved { label } - }; - - Some(assist) + pub(crate) fn add_assist_group(self, group_name: impl Into) -> AssistGroup<'a> { + AssistGroup { ctx: self, group_name: group_name.into(), assists: Vec::new() } } pub(crate) fn token_at_offset(&self) -> TokenAtOffset { @@ -155,6 +133,67 @@ impl<'a> AssistCtx<'a> { } } +pub(crate) struct AssistGroup<'a> { + ctx: AssistCtx<'a>, + group_name: String, + assists: Vec, +} + +impl<'a> AssistGroup<'a> { + pub(crate) fn add_assist( + &mut self, + id: AssistId, + label: impl Into, + f: impl FnOnce(&mut ActionBuilder), + ) { + let label = AssistLabel::new(label.into(), id); + + let assist = if self.ctx.should_compute_edit { + let action = { + let mut edit = ActionBuilder::default(); + f(&mut edit); + edit.build() + }; + Assist::Resolved { assist: ResolvedAssist { label, action_data: Either::Left(action) } } + } else { + Assist::Unresolved { label } + }; + + self.assists.push(assist) + } + + pub(crate) fn finish(self) -> Option { + assert!(!self.assists.is_empty()); + let mut label = match &self.assists[0] { + Assist::Unresolved { label } => label.clone(), + Assist::Resolved { assist } => assist.label.clone(), + }; + label.label = self.group_name; + let assist = if self.ctx.should_compute_edit { + Assist::Resolved { + assist: ResolvedAssist { + label, + action_data: Either::Right( + self.assists + .into_iter() + .map(|assist| match assist { + Assist::Resolved { + assist: + ResolvedAssist { label: _, action_data: Either::Left(it) }, + } => it, + _ => unreachable!(), + }) + .collect(), + ), + }, + } + } else { + Assist::Unresolved { label } + }; + Some(assist) + } +} + #[derive(Default)] pub(crate) struct ActionBuilder { edit: TextEditBuilder, @@ -164,11 +203,6 @@ pub(crate) struct ActionBuilder { } impl ActionBuilder { - /// Adds a custom label to the action, if it needs to be different from the assist label - pub(crate) fn label(&mut self, label: impl Into) { - self.label = Some(label.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()) -- cgit v1.2.3 From 9769c5140c9c406a4cc880e698593a6c4bcc6826 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sun, 9 Feb 2020 15:32:53 +0100 Subject: Simplify Assists interface Instead of building a physical tree structure, just "tag" related assists with the same group --- crates/ra_assists/src/assist_ctx.rs | 83 +++++++++++++++++-------------------- 1 file changed, 38 insertions(+), 45 deletions(-) (limited to 'crates/ra_assists/src/assist_ctx.rs') diff --git a/crates/ra_assists/src/assist_ctx.rs b/crates/ra_assists/src/assist_ctx.rs index 5924a3fd5..5aab5fb8b 100644 --- a/crates/ra_assists/src/assist_ctx.rs +++ b/crates/ra_assists/src/assist_ctx.rs @@ -1,5 +1,4 @@ //! This module defines `AssistCtx` -- the API surface that is exposed to assists. -use either::Either; use hir::{InFile, SourceAnalyzer, SourceBinder}; use ra_db::{FileRange, SourceDatabase}; use ra_fmt::{leading_indent, reindent}; @@ -11,12 +10,36 @@ use ra_syntax::{ }; use ra_text_edit::TextEditBuilder; -use crate::{AssistAction, AssistId, AssistLabel, ResolvedAssist}; +use crate::{AssistAction, AssistId, AssistLabel, GroupLabel, ResolvedAssist}; #[derive(Clone, Debug)] -pub(crate) enum Assist { - Unresolved { label: AssistLabel }, - Resolved { assist: ResolvedAssist }, +pub(crate) struct Assist(pub(crate) Vec); + +#[derive(Clone, Debug)] +pub(crate) struct AssistInfo { + pub(crate) label: AssistLabel, + pub(crate) group_label: Option, + pub(crate) action: Option, +} + +impl AssistInfo { + fn new(label: AssistLabel) -> AssistInfo { + AssistInfo { label, group_label: None, action: None } + } + + fn resolved(self, action: AssistAction) -> AssistInfo { + AssistInfo { action: Some(action), ..self } + } + + fn with_group(self, group_label: GroupLabel) -> AssistInfo { + AssistInfo { group_label: Some(group_label), ..self } + } + + pub(crate) fn into_resolved(self) -> Option { + let label = self.label; + let group_label = self.group_label; + self.action.map(|action| ResolvedAssist { label, group_label, action }) + } } pub(crate) type AssistHandler = fn(AssistCtx) -> Option; @@ -84,18 +107,17 @@ impl<'a> AssistCtx<'a> { ) -> Option { let label = AssistLabel::new(label.into(), id); - let assist = if self.should_compute_edit { + let mut info = AssistInfo::new(label); + if self.should_compute_edit { let action = { let mut edit = ActionBuilder::default(); f(&mut edit); edit.build() }; - Assist::Resolved { assist: ResolvedAssist { label, action_data: Either::Left(action) } } - } else { - Assist::Unresolved { label } + info = info.resolved(action) }; - Some(assist) + Some(Assist(vec![info])) } pub(crate) fn add_assist_group(self, group_name: impl Into) -> AssistGroup<'a> { @@ -136,7 +158,7 @@ impl<'a> AssistCtx<'a> { pub(crate) struct AssistGroup<'a> { ctx: AssistCtx<'a>, group_name: String, - assists: Vec, + assists: Vec, } impl<'a> AssistGroup<'a> { @@ -148,49 +170,22 @@ impl<'a> AssistGroup<'a> { ) { let label = AssistLabel::new(label.into(), id); - let assist = if self.ctx.should_compute_edit { + let mut info = AssistInfo::new(label).with_group(GroupLabel(self.group_name.clone())); + if self.ctx.should_compute_edit { let action = { let mut edit = ActionBuilder::default(); f(&mut edit); edit.build() }; - Assist::Resolved { assist: ResolvedAssist { label, action_data: Either::Left(action) } } - } else { - Assist::Unresolved { label } + info = info.resolved(action) }; - self.assists.push(assist) + self.assists.push(info) } pub(crate) fn finish(self) -> Option { assert!(!self.assists.is_empty()); - let mut label = match &self.assists[0] { - Assist::Unresolved { label } => label.clone(), - Assist::Resolved { assist } => assist.label.clone(), - }; - label.label = self.group_name; - let assist = if self.ctx.should_compute_edit { - Assist::Resolved { - assist: ResolvedAssist { - label, - action_data: Either::Right( - self.assists - .into_iter() - .map(|assist| match assist { - Assist::Resolved { - assist: - ResolvedAssist { label: _, action_data: Either::Left(it) }, - } => it, - _ => unreachable!(), - }) - .collect(), - ), - }, - } - } else { - Assist::Unresolved { label } - }; - Some(assist) + Some(Assist(self.assists)) } } @@ -199,7 +194,6 @@ pub(crate) struct ActionBuilder { edit: TextEditBuilder, cursor_position: Option, target: Option, - label: Option, } impl ActionBuilder { @@ -261,7 +255,6 @@ impl ActionBuilder { edit: self.edit.finish(), cursor_position: self.cursor_position, target: self.target, - label: self.label, } } } -- cgit v1.2.3