1 //! This module defines the `Assist` data structure. The actual assist live in 2 //! the `ide_assists` downstream crate. We want to define the data structures in 3 //! this low-level crate though, because `ide_diagnostics` also need them 4 //! (fixits for diagnostics and assists are the same thing under the hood). We 5 //! want to compile `ide_assists` and `ide_diagnostics` in parallel though, so 6 //! we pull the common definitions upstream, to this crate. 7 8 use std::str::FromStr; 9 10 use syntax::TextRange; 11 12 use crate::{label::Label, source_change::SourceChange}; 13 14 #[derive(Debug, Clone)] 15 pub struct Assist { 16 pub id: AssistId, 17 /// Short description of the assist, as shown in the UI. 18 pub label: Label, 19 pub group: Option<GroupLabel>, 20 /// Target ranges are used to sort assists: the smaller the target range, 21 /// the more specific assist is, and so it should be sorted first. 22 pub target: TextRange, 23 /// Computing source change sometimes is much more costly then computing the 24 /// other fields. Additionally, the actual change is not required to show 25 /// the lightbulb UI, it only is needed when the user tries to apply an 26 /// assist. So, we compute it lazily: the API allow requesting assists with 27 /// or without source change. We could (and in fact, used to) distinguish 28 /// between resolved and unresolved assists at the type level, but this is 29 /// cumbersome, especially if you want to embed an assist into another data 30 /// structure, such as a diagnostic. 31 pub source_change: Option<SourceChange>, 32 pub trigger_signature_help: bool, 33 } 34 35 #[derive(Debug, Clone, Copy, PartialEq, Eq)] 36 pub enum AssistKind { 37 // FIXME: does the None variant make sense? Probably not. 38 None, 39 40 QuickFix, 41 Generate, 42 Refactor, 43 RefactorExtract, 44 RefactorInline, 45 RefactorRewrite, 46 } 47 48 impl AssistKind { contains(self, other: AssistKind) -> bool49 pub fn contains(self, other: AssistKind) -> bool { 50 if self == other { 51 return true; 52 } 53 54 match self { 55 AssistKind::None | AssistKind::Generate => true, 56 AssistKind::Refactor => matches!( 57 other, 58 AssistKind::RefactorExtract 59 | AssistKind::RefactorInline 60 | AssistKind::RefactorRewrite 61 ), 62 _ => false, 63 } 64 } 65 name(&self) -> &str66 pub fn name(&self) -> &str { 67 match self { 68 AssistKind::None => "None", 69 AssistKind::QuickFix => "QuickFix", 70 AssistKind::Generate => "Generate", 71 AssistKind::Refactor => "Refactor", 72 AssistKind::RefactorExtract => "RefactorExtract", 73 AssistKind::RefactorInline => "RefactorInline", 74 AssistKind::RefactorRewrite => "RefactorRewrite", 75 } 76 } 77 } 78 79 impl FromStr for AssistKind { 80 type Err = String; 81 from_str(s: &str) -> Result<Self, Self::Err>82 fn from_str(s: &str) -> Result<Self, Self::Err> { 83 match s { 84 "None" => Ok(AssistKind::None), 85 "QuickFix" => Ok(AssistKind::QuickFix), 86 "Generate" => Ok(AssistKind::Generate), 87 "Refactor" => Ok(AssistKind::Refactor), 88 "RefactorExtract" => Ok(AssistKind::RefactorExtract), 89 "RefactorInline" => Ok(AssistKind::RefactorInline), 90 "RefactorRewrite" => Ok(AssistKind::RefactorRewrite), 91 unknown => Err(format!("Unknown AssistKind: '{unknown}'")), 92 } 93 } 94 } 95 96 /// Unique identifier of the assist, should not be shown to the user 97 /// directly. 98 #[derive(Debug, Clone, Copy, PartialEq, Eq)] 99 pub struct AssistId(pub &'static str, pub AssistKind); 100 101 /// A way to control how many assist to resolve during the assist resolution. 102 /// When an assist is resolved, its edits are calculated that might be costly to always do by default. 103 #[derive(Debug)] 104 pub enum AssistResolveStrategy { 105 /// No assists should be resolved. 106 None, 107 /// All assists should be resolved. 108 All, 109 /// Only a certain assist should be resolved. 110 Single(SingleResolve), 111 } 112 113 /// Hold the [`AssistId`] data of a certain assist to resolve. 114 /// The original id object cannot be used due to a `'static` lifetime 115 /// and the requirement to construct this struct dynamically during the resolve handling. 116 #[derive(Debug)] 117 pub struct SingleResolve { 118 /// The id of the assist. 119 pub assist_id: String, 120 // The kind of the assist. 121 pub assist_kind: AssistKind, 122 } 123 124 impl AssistResolveStrategy { should_resolve(&self, id: &AssistId) -> bool125 pub fn should_resolve(&self, id: &AssistId) -> bool { 126 match self { 127 AssistResolveStrategy::None => false, 128 AssistResolveStrategy::All => true, 129 AssistResolveStrategy::Single(single_resolve) => { 130 single_resolve.assist_id == id.0 && single_resolve.assist_kind == id.1 131 } 132 } 133 } 134 } 135 136 #[derive(Clone, Debug)] 137 pub struct GroupLabel(pub String); 138