]> git.lizzy.rs Git - rust.git/blob - src/tools/rust-analyzer/crates/ide-db/src/assists.rs
Auto merge of #102692 - nnethercote:TokenStreamBuilder, r=Aaron1011
[rust.git] / src / tools / rust-analyzer / crates / ide-db / src / assists.rs
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 {
49     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
66     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
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 asssist 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 {
125     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);