Skip to main content

ls_types/
code_action.rs

1use crate::{
2    Command, Diagnostic, PartialResultParams, Range, TextDocumentIdentifier,
3    WorkDoneProgressOptions, WorkDoneProgressParams, WorkspaceEdit, macros::lsp_enum,
4};
5use serde::{Deserialize, Serialize};
6
7use serde_json::Value;
8
9use std::borrow::Cow;
10#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
11#[serde(untagged)]
12pub enum CodeActionProviderCapability {
13    Simple(bool),
14    Options(CodeActionOptions),
15}
16
17impl From<CodeActionOptions> for CodeActionProviderCapability {
18    fn from(from: CodeActionOptions) -> Self {
19        Self::Options(from)
20    }
21}
22
23impl From<bool> for CodeActionProviderCapability {
24    fn from(from: bool) -> Self {
25        Self::Simple(from)
26    }
27}
28
29#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
30#[serde(rename_all = "camelCase")]
31pub struct CodeActionClientCapabilities {
32    ///
33    /// This capability supports dynamic registration.
34    ///
35    #[serde(skip_serializing_if = "Option::is_none")]
36    pub dynamic_registration: Option<bool>,
37
38    /// The client support code action literals as a valid
39    /// response of the `textDocument/codeAction` request.
40    #[serde(skip_serializing_if = "Option::is_none")]
41    pub code_action_literal_support: Option<CodeActionLiteralSupport>,
42
43    /// Whether code action supports the `isPreferred` property.
44    ///
45    /// @since 3.15.0
46    #[serde(skip_serializing_if = "Option::is_none")]
47    pub is_preferred_support: Option<bool>,
48
49    /// Whether code action supports the `disabled` property.
50    ///
51    /// @since 3.16.0
52    #[serde(skip_serializing_if = "Option::is_none")]
53    pub disabled_support: Option<bool>,
54
55    /// Whether code action supports the `data` property which is
56    /// preserved between a `textDocument/codeAction` and a
57    /// `codeAction/resolve` request.
58    ///
59    /// @since 3.16.0
60    #[serde(skip_serializing_if = "Option::is_none")]
61    pub data_support: Option<bool>,
62
63    /// Whether the client supports resolving additional code action
64    /// properties via a separate `codeAction/resolve` request.
65    ///
66    /// @since 3.16.0
67    #[serde(skip_serializing_if = "Option::is_none")]
68    pub resolve_support: Option<CodeActionCapabilityResolveSupport>,
69
70    /// Whether the client honors the change annotations in
71    /// text edits and resource operations returned via the
72    /// `CodeAction#edit` property by for example presenting
73    /// the workspace edit in the user interface and asking
74    /// for confirmation.
75    ///
76    /// @since 3.16.0
77    #[serde(skip_serializing_if = "Option::is_none")]
78    pub honors_change_annotations: Option<bool>,
79}
80
81/// Whether the client supports resolving additional code action
82/// properties via a separate `codeAction/resolve` request.
83///
84/// @since 3.16.0
85#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
86#[serde(rename_all = "camelCase")]
87pub struct CodeActionCapabilityResolveSupport {
88    /// The properties that a client can resolve lazily.
89    pub properties: Vec<String>,
90}
91
92#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
93#[serde(rename_all = "camelCase")]
94pub struct CodeActionLiteralSupport {
95    /// The code action kind is support with the following value set.
96    pub code_action_kind: CodeActionKindLiteralSupport,
97}
98
99#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
100#[serde(rename_all = "camelCase")]
101pub struct CodeActionKindLiteralSupport {
102    /// The code action kind values the client supports. When this
103    /// property exists the client also guarantees that it will
104    /// handle values outside its set gracefully and falls back
105    /// to a default value when unknown.
106    pub value_set: Vec<String>,
107}
108
109/// Params for the `CodeActionRequest`
110#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
111#[serde(rename_all = "camelCase")]
112pub struct CodeActionParams {
113    /// The document in which the command was invoked.
114    pub text_document: TextDocumentIdentifier,
115
116    /// The range for which the command was invoked.
117    pub range: Range,
118
119    /// Context carrying additional information.
120    pub context: CodeActionContext,
121
122    #[serde(flatten)]
123    pub work_done_progress_params: WorkDoneProgressParams,
124
125    #[serde(flatten)]
126    pub partial_result_params: PartialResultParams,
127}
128
129/// Response for `CodeActionRequest`
130pub type CodeActionResponse = Vec<CodeActionOrCommand>;
131
132#[expect(clippy::large_enum_variant)]
133#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
134#[serde(untagged)]
135pub enum CodeActionOrCommand {
136    Command(Command),
137    CodeAction(CodeAction),
138}
139
140impl From<Command> for CodeActionOrCommand {
141    fn from(command: Command) -> Self {
142        Self::Command(command)
143    }
144}
145
146impl From<CodeAction> for CodeActionOrCommand {
147    fn from(action: CodeAction) -> Self {
148        Self::CodeAction(action)
149    }
150}
151
152#[derive(Debug, Eq, PartialEq, Hash, PartialOrd, Clone, Deserialize, Serialize)]
153pub struct CodeActionKind(Cow<'static, str>);
154
155impl CodeActionKind {
156    /// Empty kind.
157    pub const EMPTY: Self = Self::new("");
158
159    /// Base kind for quickfix actions: 'quickfix'
160    pub const QUICKFIX: Self = Self::new("quickfix");
161
162    /// Base kind for refactoring actions: 'refactor'
163    pub const REFACTOR: Self = Self::new("refactor");
164
165    /// Base kind for refactoring extraction actions: 'refactor.extract'
166    ///
167    /// Example extract actions:
168    ///
169    /// - Extract method
170    /// - Extract function
171    /// - Extract variable
172    /// - Extract interface from class
173    /// - ...
174    pub const REFACTOR_EXTRACT: Self = Self::new("refactor.extract");
175
176    /// Base kind for refactoring inline actions: 'refactor.inline'
177    ///
178    /// Example inline actions:
179    ///
180    /// - Inline function
181    /// - Inline variable
182    /// - Inline constant
183    /// - ...
184    pub const REFACTOR_INLINE: Self = Self::new("refactor.inline");
185
186    /// Base kind for refactoring rewrite actions: 'refactor.rewrite'
187    ///
188    /// Example rewrite actions:
189    ///
190    /// - Convert JavaScript function to class
191    /// - Add or remove parameter
192    /// - Encapsulate field
193    /// - Make method static
194    /// - Move method to base class
195    /// - ...
196    pub const REFACTOR_REWRITE: Self = Self::new("refactor.rewrite");
197
198    /// Base kind for source actions: `source`
199    ///
200    /// Source code actions apply to the entire file.
201    pub const SOURCE: Self = Self::new("source");
202
203    /// Base kind for an organize imports source action: `source.organizeImports`
204    pub const SOURCE_ORGANIZE_IMPORTS: Self = Self::new("source.organizeImports");
205
206    /// Base kind for a 'fix all' source action: `source.fixAll`.
207    ///
208    /// 'Fix all' actions automatically fix errors that have a clear fix that
209    /// do not require user input. They should not suppress errors or perform
210    /// unsafe fixes such as generating new types or classes.
211    ///
212    /// @since 3.17.0
213    pub const SOURCE_FIX_ALL: Self = Self::new("source.fixAll");
214
215    #[must_use]
216    pub const fn new(tag: &'static str) -> Self {
217        Self(Cow::Borrowed(tag))
218    }
219
220    #[must_use]
221    pub fn as_str(&self) -> &str {
222        &self.0
223    }
224}
225
226impl From<String> for CodeActionKind {
227    fn from(from: String) -> Self {
228        Self(Cow::from(from))
229    }
230}
231
232impl From<&'static str> for CodeActionKind {
233    fn from(from: &'static str) -> Self {
234        Self::new(from)
235    }
236}
237
238#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)]
239#[serde(rename_all = "camelCase")]
240pub struct CodeAction {
241    /// A short, human-readable, title for this code action.
242    pub title: String,
243
244    /// The kind of the code action.
245    /// Used to filter code actions.
246    #[serde(skip_serializing_if = "Option::is_none")]
247    pub kind: Option<CodeActionKind>,
248
249    /// The diagnostics that this code action resolves.
250    #[serde(skip_serializing_if = "Option::is_none")]
251    pub diagnostics: Option<Vec<Diagnostic>>,
252
253    /// The workspace edit this code action performs.
254    #[serde(skip_serializing_if = "Option::is_none")]
255    pub edit: Option<WorkspaceEdit>,
256
257    /// A command this code action executes. If a code action
258    /// provides an edit and a command, first the edit is
259    /// executed and then the command.
260    #[serde(skip_serializing_if = "Option::is_none")]
261    pub command: Option<Command>,
262
263    /// Marks this as a preferred action. Preferred actions are used by the `auto fix` command and can be targeted
264    /// by keybindings.
265    /// A quick fix should be marked preferred if it properly addresses the underlying error.
266    /// A refactoring should be marked preferred if it is the most reasonable choice of actions to take.
267    ///
268    /// @since 3.15.0
269    #[serde(skip_serializing_if = "Option::is_none")]
270    pub is_preferred: Option<bool>,
271
272    /// Marks that the code action cannot currently be applied.
273    ///
274    /// Clients should follow the following guidelines regarding disabled code actions:
275    ///
276    /// - Disabled code actions are not shown in automatic
277    ///   [lightbulb](https://code.visualstudio.com/docs/editor/editingevolved#_code-action)
278    ///   code action menu.
279    ///
280    /// - Disabled actions are shown as faded out in the code action menu when the user request
281    ///   a more specific type of code action, such as refactorings.
282    ///
283    /// - If the user has a keybinding that auto applies a code action and only a disabled code
284    ///   actions are returned, the client should show the user an error message with `reason`
285    ///   in the editor.
286    ///
287    /// @since 3.16.0
288    #[serde(skip_serializing_if = "Option::is_none")]
289    pub disabled: Option<CodeActionDisabled>,
290
291    /// A data entry field that is preserved on a code action between
292    /// a `textDocument/codeAction` and a `codeAction/resolve` request.
293    ///
294    /// @since 3.16.0
295    #[serde(skip_serializing_if = "Option::is_none")]
296    pub data: Option<Value>,
297}
298
299#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
300#[serde(rename_all = "camelCase")]
301pub struct CodeActionDisabled {
302    /// Human readable description of why the code action is currently disabled.
303    ///
304    /// This is displayed in the code actions UI.
305    pub reason: String,
306}
307
308/// The reason why code actions were requested.
309///
310/// @since 3.17.0
311#[derive(Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
312#[serde(transparent)]
313pub struct CodeActionTriggerKind(i32);
314
315lsp_enum! {
316    impl CodeActionTriggerKind {
317        /// Code actions were explicitly requested by the user or by an extension.
318        const INVOKED = 1;
319
320        /// Code actions were requested automatically.
321        ///
322        /// This typically happens when current selection in a file changes, but can
323        /// also be triggered when file content changes.
324        const AUTOMATIC = 2;
325    }
326}
327
328/// Contains additional diagnostic information about the context in which
329/// a code action is run.
330#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
331#[serde(rename_all = "camelCase")]
332pub struct CodeActionContext {
333    /// An array of diagnostics.
334    pub diagnostics: Vec<Diagnostic>,
335
336    /// Requested kind of actions to return.
337    ///
338    /// Actions not of this kind are filtered out by the client before being shown. So servers
339    /// can omit computing them.
340    #[serde(skip_serializing_if = "Option::is_none")]
341    pub only: Option<Vec<CodeActionKind>>,
342
343    /// The reason why code actions were requested.
344    ///
345    /// @since 3.17.0
346    #[serde(skip_serializing_if = "Option::is_none")]
347    pub trigger_kind: Option<CodeActionTriggerKind>,
348}
349
350#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize, Default)]
351#[serde(rename_all = "camelCase")]
352pub struct CodeActionOptions {
353    /// `CodeActionKinds` that this server may return.
354    ///
355    /// The list of kinds may be generic, such as `CodeActionKind.Refactor`, or the server
356    /// may list out every specific kind they provide.
357    #[serde(skip_serializing_if = "Option::is_none")]
358    pub code_action_kinds: Option<Vec<CodeActionKind>>,
359
360    #[serde(flatten)]
361    pub work_done_progress_options: WorkDoneProgressOptions,
362
363    /// The server provides support to resolve additional
364    /// information for a code action.
365    ///
366    /// @since 3.16.0
367    #[serde(skip_serializing_if = "Option::is_none")]
368    pub resolve_provider: Option<bool>,
369}
370
371#[cfg(test)]
372mod tests {
373    use super::*;
374    use crate::tests::test_serialization;
375
376    #[test]
377    fn test_code_action_response() {
378        test_serialization(
379            &vec![
380                CodeActionOrCommand::Command(Command {
381                    title: "title".to_string(),
382                    command: "command".to_string(),
383                    arguments: None,
384                }),
385                CodeActionOrCommand::CodeAction(CodeAction {
386                    title: "title".to_string(),
387                    kind: Some(CodeActionKind::QUICKFIX),
388                    command: None,
389                    diagnostics: None,
390                    edit: None,
391                    is_preferred: None,
392                    ..CodeAction::default()
393                }),
394            ],
395            r#"[{"title":"title","command":"command"},{"title":"title","kind":"quickfix"}]"#,
396        );
397    }
398}