From a91071b57be6e64ad2fd277998ada0ae6206457b Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Mon, 14 Jun 2021 13:27:11 +0300 Subject: internal: cut deps between assists and diagnostics --- crates/ide_db/src/assists.rs | 136 +++++++++++++++++++++++++++++++++++++++++++ crates/ide_db/src/lib.rs | 1 + 2 files changed, 137 insertions(+) create mode 100644 crates/ide_db/src/assists.rs (limited to 'crates/ide_db') diff --git a/crates/ide_db/src/assists.rs b/crates/ide_db/src/assists.rs new file mode 100644 index 000000000..7881d8369 --- /dev/null +++ b/crates/ide_db/src/assists.rs @@ -0,0 +1,136 @@ +//! This module defines the `Assist` data structure. The actual assist live in +//! the `ide_assists` downstream crate. We want to define the data structures in +//! this low-level crate though, because `ide_diagnostics` also need them +//! (fixits for diagnostics and assists are the same thing under the hood). We +//! want to compile `ide_assists` and `ide_diagnostics` in parallel though, so +//! we pull the common definitions upstream, to this crate. + +use std::str::FromStr; + +use syntax::TextRange; + +use crate::{label::Label, source_change::SourceChange}; + +#[derive(Debug, Clone)] +pub struct Assist { + pub id: AssistId, + /// Short description of the assist, as shown in the UI. + pub label: Label, + pub group: Option, + /// Target ranges are used to sort assists: the smaller the target range, + /// the more specific assist is, and so it should be sorted first. + pub target: TextRange, + /// Computing source change sometimes is much more costly then computing the + /// other fields. Additionally, the actual change is not required to show + /// the lightbulb UI, it only is needed when the user tries to apply an + /// assist. So, we compute it lazily: the API allow requesting assists with + /// or without source change. We could (and in fact, used to) distinguish + /// between resolved and unresolved assists at the type level, but this is + /// cumbersome, especially if you want to embed an assist into another data + /// structure, such as a diagnostic. + pub source_change: Option, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum AssistKind { + // FIXME: does the None variant make sense? Probably not. + None, + + QuickFix, + Generate, + Refactor, + RefactorExtract, + RefactorInline, + RefactorRewrite, +} + +impl AssistKind { + pub fn contains(self, other: AssistKind) -> bool { + if self == other { + return true; + } + + match self { + AssistKind::None | AssistKind::Generate => true, + AssistKind::Refactor => match other { + AssistKind::RefactorExtract + | AssistKind::RefactorInline + | AssistKind::RefactorRewrite => true, + _ => false, + }, + _ => false, + } + } + + pub fn name(&self) -> &str { + match self { + AssistKind::None => "None", + AssistKind::QuickFix => "QuickFix", + AssistKind::Generate => "Generate", + AssistKind::Refactor => "Refactor", + AssistKind::RefactorExtract => "RefactorExtract", + AssistKind::RefactorInline => "RefactorInline", + AssistKind::RefactorRewrite => "RefactorRewrite", + } + } +} + +impl FromStr for AssistKind { + type Err = String; + + fn from_str(s: &str) -> Result { + match s { + "None" => Ok(AssistKind::None), + "QuickFix" => Ok(AssistKind::QuickFix), + "Generate" => Ok(AssistKind::Generate), + "Refactor" => Ok(AssistKind::Refactor), + "RefactorExtract" => Ok(AssistKind::RefactorExtract), + "RefactorInline" => Ok(AssistKind::RefactorInline), + "RefactorRewrite" => Ok(AssistKind::RefactorRewrite), + unknown => Err(format!("Unknown AssistKind: '{}'", unknown)), + } + } +} + +/// Unique identifier of the assist, should not be shown to the user +/// directly. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct AssistId(pub &'static str, pub AssistKind); + +/// A way to control how many asssist to resolve during the assist resolution. +/// When an assist is resolved, its edits are calculated that might be costly to always do by default. +#[derive(Debug)] +pub enum AssistResolveStrategy { + /// No assists should be resolved. + None, + /// All assists should be resolved. + All, + /// Only a certain assist should be resolved. + Single(SingleResolve), +} + +/// Hold the [`AssistId`] data of a certain assist to resolve. +/// The original id object cannot be used due to a `'static` lifetime +/// and the requirement to construct this struct dynamically during the resolve handling. +#[derive(Debug)] +pub struct SingleResolve { + /// The id of the assist. + pub assist_id: String, + // The kind of the assist. + pub assist_kind: AssistKind, +} + +impl AssistResolveStrategy { + pub fn should_resolve(&self, id: &AssistId) -> bool { + match self { + AssistResolveStrategy::None => false, + AssistResolveStrategy::All => true, + AssistResolveStrategy::Single(single_resolve) => { + single_resolve.assist_id == id.0 && single_resolve.assist_kind == id.1 + } + } + } +} + +#[derive(Clone, Debug)] +pub struct GroupLabel(pub String); diff --git a/crates/ide_db/src/lib.rs b/crates/ide_db/src/lib.rs index 105607dca..2ac215c06 100644 --- a/crates/ide_db/src/lib.rs +++ b/crates/ide_db/src/lib.rs @@ -3,6 +3,7 @@ //! It is mainly a `HirDatabase` for semantic analysis, plus a `SymbolsDatabase`, for fuzzy search. mod apply_change; +pub mod assists; pub mod label; pub mod line_index; pub mod symbol_index; -- cgit v1.2.3