From d050d6da2e4d807297afe23808a283ff25c90868 Mon Sep 17 00:00:00 2001 From: Jane Lewis Date: Thu, 4 Apr 2024 15:20:50 -0700 Subject: [PATCH] `ruff server` now supports the `source.organizeImports` source action (#10652) ## Summary This builds on top of the work in https://github.com/astral-sh/ruff/pull/10597 to support `Ruff: Organize imports` as an available source action. To do this, we have to support `Clone`-ing for linter settings, since we need to modify them in place to select import-related diagnostics specifically (`I001` and `I002`). ## Test Plan https://github.com/astral-sh/ruff/assets/19577865/04282d01-dfda-4ac5-aa8f-6a92d5f85bfd --- .../src/rules/flake8_annotations/settings.rs | 2 +- .../src/rules/flake8_bandit/settings.rs | 2 +- .../src/rules/flake8_boolean_trap/settings.rs | 2 +- .../src/rules/flake8_bugbear/settings.rs | 2 +- .../src/rules/flake8_builtins/settings.rs | 2 +- .../rules/flake8_comprehensions/settings.rs | 2 +- .../src/rules/flake8_copyright/settings.rs | 2 +- .../src/rules/flake8_errmsg/settings.rs | 2 +- .../src/rules/flake8_gettext/settings.rs | 2 +- .../flake8_implicit_str_concat/settings.rs | 2 +- .../flake8_import_conventions/settings.rs | 2 +- .../src/rules/flake8_pytest_style/settings.rs | 2 +- .../src/rules/flake8_quotes/settings.rs | 2 +- .../src/rules/flake8_self/settings.rs | 2 +- .../src/rules/flake8_tidy_imports/settings.rs | 2 +- .../rules/flake8_type_checking/settings.rs | 2 +- .../rules/flake8_unused_arguments/settings.rs | 2 +- .../ruff_linter/src/rules/isort/categorize.rs | 2 +- .../ruff_linter/src/rules/isort/settings.rs | 2 +- .../ruff_linter/src/rules/mccabe/settings.rs | 2 +- .../src/rules/pep8_naming/settings.rs | 4 +- .../src/rules/pycodestyle/settings.rs | 2 +- .../src/rules/pydocstyle/settings.rs | 2 +- .../src/rules/pyflakes/settings.rs | 2 +- .../ruff_linter/src/rules/pylint/settings.rs | 2 +- .../src/rules/pyupgrade/settings.rs | 2 +- .../src/settings/fix_safety_table.rs | 2 +- crates/ruff_linter/src/settings/mod.rs | 2 +- crates/ruff_linter/src/settings/rule_table.rs | 2 +- crates/ruff_linter/src/settings/types.rs | 2 +- crates/ruff_server/src/server.rs | 2 +- .../src/server/api/requests/code_action.rs | 41 +++++++++++++++++-- .../api/requests/code_action_resolve.rs | 40 ++++++++++++++++-- 33 files changed, 106 insertions(+), 39 deletions(-) diff --git a/crates/ruff_linter/src/rules/flake8_annotations/settings.rs b/crates/ruff_linter/src/rules/flake8_annotations/settings.rs index 011cf01f4a926..342a56023a0e1 100644 --- a/crates/ruff_linter/src/rules/flake8_annotations/settings.rs +++ b/crates/ruff_linter/src/rules/flake8_annotations/settings.rs @@ -4,7 +4,7 @@ use crate::display_settings; use ruff_macros::CacheKey; use std::fmt::{Display, Formatter}; -#[derive(Debug, Default, CacheKey)] +#[derive(Debug, Clone, Default, CacheKey)] #[allow(clippy::struct_excessive_bools)] pub struct Settings { pub mypy_init_return: bool, diff --git a/crates/ruff_linter/src/rules/flake8_bandit/settings.rs b/crates/ruff_linter/src/rules/flake8_bandit/settings.rs index 17a018a25c64c..ee96e6ee667d2 100644 --- a/crates/ruff_linter/src/rules/flake8_bandit/settings.rs +++ b/crates/ruff_linter/src/rules/flake8_bandit/settings.rs @@ -10,7 +10,7 @@ pub fn default_tmp_dirs() -> Vec { .to_vec() } -#[derive(Debug, CacheKey)] +#[derive(Debug, Clone, CacheKey)] pub struct Settings { pub hardcoded_tmp_directory: Vec, pub check_typed_exception: bool, diff --git a/crates/ruff_linter/src/rules/flake8_boolean_trap/settings.rs b/crates/ruff_linter/src/rules/flake8_boolean_trap/settings.rs index 3b88395c9847a..9825442ad55ff 100644 --- a/crates/ruff_linter/src/rules/flake8_boolean_trap/settings.rs +++ b/crates/ruff_linter/src/rules/flake8_boolean_trap/settings.rs @@ -6,7 +6,7 @@ use ruff_macros::CacheKey; use crate::display_settings; -#[derive(Debug, CacheKey, Default)] +#[derive(Debug, Clone, CacheKey, Default)] pub struct Settings { pub extend_allowed_calls: Vec, } diff --git a/crates/ruff_linter/src/rules/flake8_bugbear/settings.rs b/crates/ruff_linter/src/rules/flake8_bugbear/settings.rs index 03c4d5cdf1d98..6a13a3c8b79ba 100644 --- a/crates/ruff_linter/src/rules/flake8_bugbear/settings.rs +++ b/crates/ruff_linter/src/rules/flake8_bugbear/settings.rs @@ -4,7 +4,7 @@ use crate::display_settings; use ruff_macros::CacheKey; use std::fmt::{Display, Formatter}; -#[derive(Debug, Default, CacheKey)] +#[derive(Debug, Clone, Default, CacheKey)] pub struct Settings { pub extend_immutable_calls: Vec, } diff --git a/crates/ruff_linter/src/rules/flake8_builtins/settings.rs b/crates/ruff_linter/src/rules/flake8_builtins/settings.rs index d3fc3a70f74bd..e11537efb7ff4 100644 --- a/crates/ruff_linter/src/rules/flake8_builtins/settings.rs +++ b/crates/ruff_linter/src/rules/flake8_builtins/settings.rs @@ -4,7 +4,7 @@ use crate::display_settings; use ruff_macros::CacheKey; use std::fmt::{Display, Formatter}; -#[derive(Debug, Default, CacheKey)] +#[derive(Debug, Clone, Default, CacheKey)] pub struct Settings { pub builtins_ignorelist: Vec, } diff --git a/crates/ruff_linter/src/rules/flake8_comprehensions/settings.rs b/crates/ruff_linter/src/rules/flake8_comprehensions/settings.rs index 41110886a5d4e..778d5601ef01d 100644 --- a/crates/ruff_linter/src/rules/flake8_comprehensions/settings.rs +++ b/crates/ruff_linter/src/rules/flake8_comprehensions/settings.rs @@ -4,7 +4,7 @@ use crate::display_settings; use ruff_macros::CacheKey; use std::fmt::{Display, Formatter}; -#[derive(Debug, Default, CacheKey)] +#[derive(Debug, Clone, Default, CacheKey)] pub struct Settings { pub allow_dict_calls_with_keyword_arguments: bool, } diff --git a/crates/ruff_linter/src/rules/flake8_copyright/settings.rs b/crates/ruff_linter/src/rules/flake8_copyright/settings.rs index b62c221c769fa..03551b0a0a93f 100644 --- a/crates/ruff_linter/src/rules/flake8_copyright/settings.rs +++ b/crates/ruff_linter/src/rules/flake8_copyright/settings.rs @@ -7,7 +7,7 @@ use std::fmt::{Display, Formatter}; use crate::display_settings; use ruff_macros::CacheKey; -#[derive(Debug, CacheKey)] +#[derive(Debug, Clone, CacheKey)] pub struct Settings { pub notice_rgx: Regex, pub author: Option, diff --git a/crates/ruff_linter/src/rules/flake8_errmsg/settings.rs b/crates/ruff_linter/src/rules/flake8_errmsg/settings.rs index ec239435cee07..c51ec3a31356d 100644 --- a/crates/ruff_linter/src/rules/flake8_errmsg/settings.rs +++ b/crates/ruff_linter/src/rules/flake8_errmsg/settings.rs @@ -4,7 +4,7 @@ use crate::display_settings; use ruff_macros::CacheKey; use std::fmt::{Display, Formatter}; -#[derive(Debug, Default, CacheKey)] +#[derive(Debug, Clone, Default, CacheKey)] pub struct Settings { pub max_string_length: usize, } diff --git a/crates/ruff_linter/src/rules/flake8_gettext/settings.rs b/crates/ruff_linter/src/rules/flake8_gettext/settings.rs index 6e3a6c367cd10..76180b45ac59d 100644 --- a/crates/ruff_linter/src/rules/flake8_gettext/settings.rs +++ b/crates/ruff_linter/src/rules/flake8_gettext/settings.rs @@ -2,7 +2,7 @@ use crate::display_settings; use ruff_macros::CacheKey; use std::fmt::{Display, Formatter}; -#[derive(Debug, CacheKey)] +#[derive(Debug, Clone, CacheKey)] pub struct Settings { pub functions_names: Vec, } diff --git a/crates/ruff_linter/src/rules/flake8_implicit_str_concat/settings.rs b/crates/ruff_linter/src/rules/flake8_implicit_str_concat/settings.rs index 90c6a9a1812fe..ab805839b67a0 100644 --- a/crates/ruff_linter/src/rules/flake8_implicit_str_concat/settings.rs +++ b/crates/ruff_linter/src/rules/flake8_implicit_str_concat/settings.rs @@ -4,7 +4,7 @@ use crate::display_settings; use ruff_macros::CacheKey; use std::fmt::{Display, Formatter}; -#[derive(Debug, CacheKey)] +#[derive(Debug, Clone, CacheKey)] pub struct Settings { pub allow_multiline: bool, } diff --git a/crates/ruff_linter/src/rules/flake8_import_conventions/settings.rs b/crates/ruff_linter/src/rules/flake8_import_conventions/settings.rs index 50c5aacc67eb1..292658a6cbca0 100644 --- a/crates/ruff_linter/src/rules/flake8_import_conventions/settings.rs +++ b/crates/ruff_linter/src/rules/flake8_import_conventions/settings.rs @@ -57,7 +57,7 @@ impl FromIterator for BannedAliases { } } -#[derive(Debug, CacheKey)] +#[derive(Debug, Clone, CacheKey)] pub struct Settings { pub aliases: FxHashMap, pub banned_aliases: FxHashMap, diff --git a/crates/ruff_linter/src/rules/flake8_pytest_style/settings.rs b/crates/ruff_linter/src/rules/flake8_pytest_style/settings.rs index cab3d2d5a3eed..50b08c48c1f91 100644 --- a/crates/ruff_linter/src/rules/flake8_pytest_style/settings.rs +++ b/crates/ruff_linter/src/rules/flake8_pytest_style/settings.rs @@ -24,7 +24,7 @@ pub fn default_broad_exceptions() -> Vec { .to_vec() } -#[derive(Debug, CacheKey)] +#[derive(Debug, Clone, CacheKey)] pub struct Settings { pub fixture_parentheses: bool, pub parametrize_names_type: types::ParametrizeNameType, diff --git a/crates/ruff_linter/src/rules/flake8_quotes/settings.rs b/crates/ruff_linter/src/rules/flake8_quotes/settings.rs index 5e0c93beadab0..b241e70b49350 100644 --- a/crates/ruff_linter/src/rules/flake8_quotes/settings.rs +++ b/crates/ruff_linter/src/rules/flake8_quotes/settings.rs @@ -31,7 +31,7 @@ impl From for Quote { } } -#[derive(Debug, CacheKey)] +#[derive(Debug, Clone, CacheKey)] pub struct Settings { pub inline_quotes: Quote, pub multiline_quotes: Quote, diff --git a/crates/ruff_linter/src/rules/flake8_self/settings.rs b/crates/ruff_linter/src/rules/flake8_self/settings.rs index c59a0ec89d221..cb3027fa90c23 100644 --- a/crates/ruff_linter/src/rules/flake8_self/settings.rs +++ b/crates/ruff_linter/src/rules/flake8_self/settings.rs @@ -17,7 +17,7 @@ pub const IGNORE_NAMES: [&str; 7] = [ "_value_", ]; -#[derive(Debug, CacheKey)] +#[derive(Debug, Clone, CacheKey)] pub struct Settings { pub ignore_names: Vec, } diff --git a/crates/ruff_linter/src/rules/flake8_tidy_imports/settings.rs b/crates/ruff_linter/src/rules/flake8_tidy_imports/settings.rs index 8f9e29ea21aac..fee7a12482cff 100644 --- a/crates/ruff_linter/src/rules/flake8_tidy_imports/settings.rs +++ b/crates/ruff_linter/src/rules/flake8_tidy_imports/settings.rs @@ -39,7 +39,7 @@ impl Display for Strictness { } } -#[derive(Debug, CacheKey, Default)] +#[derive(Debug, Clone, CacheKey, Default)] pub struct Settings { pub ban_relative_imports: Strictness, pub banned_api: FxHashMap, diff --git a/crates/ruff_linter/src/rules/flake8_type_checking/settings.rs b/crates/ruff_linter/src/rules/flake8_type_checking/settings.rs index fa8214e5b74af..11f5f87e500cf 100644 --- a/crates/ruff_linter/src/rules/flake8_type_checking/settings.rs +++ b/crates/ruff_linter/src/rules/flake8_type_checking/settings.rs @@ -4,7 +4,7 @@ use crate::display_settings; use ruff_macros::CacheKey; use std::fmt::{Display, Formatter}; -#[derive(Debug, CacheKey)] +#[derive(Debug, Clone, CacheKey)] pub struct Settings { pub strict: bool, pub exempt_modules: Vec, diff --git a/crates/ruff_linter/src/rules/flake8_unused_arguments/settings.rs b/crates/ruff_linter/src/rules/flake8_unused_arguments/settings.rs index 7e13bc6495a9a..5cd02d876a890 100644 --- a/crates/ruff_linter/src/rules/flake8_unused_arguments/settings.rs +++ b/crates/ruff_linter/src/rules/flake8_unused_arguments/settings.rs @@ -4,7 +4,7 @@ use crate::display_settings; use ruff_macros::CacheKey; use std::fmt::{Display, Formatter}; -#[derive(Debug, Default, CacheKey)] +#[derive(Debug, Clone, Default, CacheKey)] pub struct Settings { pub ignore_variadic_names: bool, } diff --git a/crates/ruff_linter/src/rules/isort/categorize.rs b/crates/ruff_linter/src/rules/isort/categorize.rs index 874070135c21e..7f5a10bfd0983 100644 --- a/crates/ruff_linter/src/rules/isort/categorize.rs +++ b/crates/ruff_linter/src/rules/isort/categorize.rs @@ -270,7 +270,7 @@ pub(crate) fn categorize_imports<'a>( block_by_type } -#[derive(Debug, Default, CacheKey)] +#[derive(Debug, Clone, Default, CacheKey)] pub struct KnownModules { /// A map of known modules to their section. known: Vec<(glob::Pattern, ImportSection)>, diff --git a/crates/ruff_linter/src/rules/isort/settings.rs b/crates/ruff_linter/src/rules/isort/settings.rs index 8ae6464932123..7307b6664a08d 100644 --- a/crates/ruff_linter/src/rules/isort/settings.rs +++ b/crates/ruff_linter/src/rules/isort/settings.rs @@ -44,7 +44,7 @@ impl Display for RelativeImportsOrder { } } -#[derive(Debug, CacheKey)] +#[derive(Debug, Clone, CacheKey)] #[allow(clippy::struct_excessive_bools)] pub struct Settings { pub required_imports: BTreeSet, diff --git a/crates/ruff_linter/src/rules/mccabe/settings.rs b/crates/ruff_linter/src/rules/mccabe/settings.rs index 65abe3c91d1a0..d5e2db45f73a4 100644 --- a/crates/ruff_linter/src/rules/mccabe/settings.rs +++ b/crates/ruff_linter/src/rules/mccabe/settings.rs @@ -4,7 +4,7 @@ use crate::display_settings; use ruff_macros::CacheKey; use std::fmt::{Display, Formatter}; -#[derive(Debug, CacheKey)] +#[derive(Debug, Clone, CacheKey)] pub struct Settings { pub max_complexity: usize, } diff --git a/crates/ruff_linter/src/rules/pep8_naming/settings.rs b/crates/ruff_linter/src/rules/pep8_naming/settings.rs index 7ad3830c8107f..9705b7cde05d5 100644 --- a/crates/ruff_linter/src/rules/pep8_naming/settings.rs +++ b/crates/ruff_linter/src/rules/pep8_naming/settings.rs @@ -11,7 +11,7 @@ use ruff_macros::CacheKey; use crate::display_settings; -#[derive(Debug, CacheKey)] +#[derive(Debug, Clone, CacheKey)] pub struct Settings { pub ignore_names: IgnoreNames, pub classmethod_decorators: Vec, @@ -85,7 +85,7 @@ static DEFAULTS: &[&str] = &[ "maxDiff", ]; -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum IgnoreNames { Default, UserProvided { diff --git a/crates/ruff_linter/src/rules/pycodestyle/settings.rs b/crates/ruff_linter/src/rules/pycodestyle/settings.rs index 1ce1d1c029ea6..b034a778740a7 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/settings.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/settings.rs @@ -6,7 +6,7 @@ use std::fmt; use crate::line_width::LineLength; -#[derive(Debug, Default, CacheKey)] +#[derive(Debug, Clone, Default, CacheKey)] pub struct Settings { pub max_line_length: LineLength, pub max_doc_length: Option, diff --git a/crates/ruff_linter/src/rules/pydocstyle/settings.rs b/crates/ruff_linter/src/rules/pydocstyle/settings.rs index 1b3a177af64eb..974c8742f9ec0 100644 --- a/crates/ruff_linter/src/rules/pydocstyle/settings.rs +++ b/crates/ruff_linter/src/rules/pydocstyle/settings.rs @@ -83,7 +83,7 @@ impl fmt::Display for Convention { } } -#[derive(Debug, Default, CacheKey)] +#[derive(Debug, Clone, Default, CacheKey)] pub struct Settings { pub convention: Option, pub ignore_decorators: BTreeSet, diff --git a/crates/ruff_linter/src/rules/pyflakes/settings.rs b/crates/ruff_linter/src/rules/pyflakes/settings.rs index b87c9aebf2108..2aa404fbe4688 100644 --- a/crates/ruff_linter/src/rules/pyflakes/settings.rs +++ b/crates/ruff_linter/src/rules/pyflakes/settings.rs @@ -4,7 +4,7 @@ use crate::display_settings; use ruff_macros::CacheKey; use std::fmt; -#[derive(Debug, Default, CacheKey)] +#[derive(Debug, Clone, Default, CacheKey)] pub struct Settings { pub extend_generics: Vec, } diff --git a/crates/ruff_linter/src/rules/pylint/settings.rs b/crates/ruff_linter/src/rules/pylint/settings.rs index c98698d5a283c..383f5136c8de0 100644 --- a/crates/ruff_linter/src/rules/pylint/settings.rs +++ b/crates/ruff_linter/src/rules/pylint/settings.rs @@ -48,7 +48,7 @@ impl fmt::Display for ConstantType { } } -#[derive(Debug, CacheKey)] +#[derive(Debug, Clone, CacheKey)] pub struct Settings { pub allow_magic_value_types: Vec, pub allow_dunder_method_names: FxHashSet, diff --git a/crates/ruff_linter/src/rules/pyupgrade/settings.rs b/crates/ruff_linter/src/rules/pyupgrade/settings.rs index 4e228351f3639..72fedbdd339c7 100644 --- a/crates/ruff_linter/src/rules/pyupgrade/settings.rs +++ b/crates/ruff_linter/src/rules/pyupgrade/settings.rs @@ -4,7 +4,7 @@ use crate::display_settings; use ruff_macros::CacheKey; use std::fmt; -#[derive(Debug, Default, CacheKey)] +#[derive(Debug, Clone, Default, CacheKey)] pub struct Settings { pub keep_runtime_typing: bool, } diff --git a/crates/ruff_linter/src/settings/fix_safety_table.rs b/crates/ruff_linter/src/settings/fix_safety_table.rs index e3b280be0cddc..8f4382452f3c0 100644 --- a/crates/ruff_linter/src/settings/fix_safety_table.rs +++ b/crates/ruff_linter/src/settings/fix_safety_table.rs @@ -14,7 +14,7 @@ use crate::{ /// A table to keep track of which rules fixes should have /// their safety overridden. -#[derive(Debug, CacheKey, Default)] +#[derive(Debug, Clone, CacheKey, Default)] pub struct FixSafetyTable { forced_safe: RuleSet, forced_unsafe: RuleSet, diff --git a/crates/ruff_linter/src/settings/mod.rs b/crates/ruff_linter/src/settings/mod.rs index 510dc3a512634..42a4ab88b8c19 100644 --- a/crates/ruff_linter/src/settings/mod.rs +++ b/crates/ruff_linter/src/settings/mod.rs @@ -206,7 +206,7 @@ macro_rules! display_settings { }; } -#[derive(Debug, CacheKey)] +#[derive(Debug, Clone, CacheKey)] pub struct LinterSettings { pub exclude: FilePatternSet, pub extension: ExtensionMapping, diff --git a/crates/ruff_linter/src/settings/rule_table.rs b/crates/ruff_linter/src/settings/rule_table.rs index f6b8482afc71c..2e598cbf44d36 100644 --- a/crates/ruff_linter/src/settings/rule_table.rs +++ b/crates/ruff_linter/src/settings/rule_table.rs @@ -6,7 +6,7 @@ use ruff_macros::CacheKey; use crate::registry::{Rule, RuleSet, RuleSetIterator}; /// A table to keep track of which rules are enabled and whether they should be fixed. -#[derive(Debug, CacheKey, Default)] +#[derive(Debug, Clone, CacheKey, Default)] pub struct RuleTable { /// Maps rule codes to a boolean indicating if the rule should be fixed. enabled: RuleSet, diff --git a/crates/ruff_linter/src/settings/types.rs b/crates/ruff_linter/src/settings/types.rs index 4e4c8fbf978c5..99aec6740cab2 100644 --- a/crates/ruff_linter/src/settings/types.rs +++ b/crates/ruff_linter/src/settings/types.rs @@ -590,7 +590,7 @@ impl Display for RequiredVersion { /// pattern matching. pub type IdentifierPattern = glob::Pattern; -#[derive(Debug, CacheKey, Default)] +#[derive(Debug, Clone, CacheKey, Default)] pub struct PerFileIgnores { // Ordered as (absolute path matcher, basename matcher, rules) ignores: Vec<(GlobMatcher, GlobMatcher, RuleSet)>, diff --git a/crates/ruff_server/src/server.rs b/crates/ruff_server/src/server.rs index 53bafa06a49ae..4f28dbd81a1fe 100644 --- a/crates/ruff_server/src/server.rs +++ b/crates/ruff_server/src/server.rs @@ -271,7 +271,7 @@ impl SupportedCodeAction { [ Self::QuickFix, Self::SourceFixAll, - // Self::SourceOrganizeImports, + Self::SourceOrganizeImports, ] .into_iter() } diff --git a/crates/ruff_server/src/server/api/requests/code_action.rs b/crates/ruff_server/src/server/api/requests/code_action.rs index a16eab75af9c3..e9bc8d1842d85 100644 --- a/crates/ruff_server/src/server/api/requests/code_action.rs +++ b/crates/ruff_server/src/server/api/requests/code_action.rs @@ -9,7 +9,7 @@ use lsp_types::{self as types, request as req}; use rustc_hash::FxHashSet; use types::{CodeActionKind, CodeActionOrCommand}; -use super::code_action_resolve::resolve_edit_for_fix_all; +use super::code_action_resolve::{resolve_edit_for_fix_all, resolve_edit_for_organize_imports}; pub(crate) struct CodeActions; @@ -26,11 +26,11 @@ impl super::BackgroundDocumentRequestHandler for CodeActions { ) -> Result> { let mut response: types::CodeActionResponse = types::CodeActionResponse::default(); - let supported_code_actions = supported_code_actions(params.context.only); + let supported_code_actions = supported_code_actions(params.context.only.clone()); if supported_code_actions.contains(&SupportedCodeAction::QuickFix) { response.extend( - quick_fix(&snapshot, params.context.diagnostics) + quick_fix(&snapshot, params.context.diagnostics.clone()) .with_failure_code(ErrorCode::InternalError)?, ); } @@ -40,7 +40,7 @@ impl super::BackgroundDocumentRequestHandler for CodeActions { } if supported_code_actions.contains(&SupportedCodeAction::SourceOrganizeImports) { - todo!("Implement the `source.organizeImports` code action"); + response.push(organize_imports(&snapshot).with_failure_code(ErrorCode::InternalError)?); } Ok(Some(response)) @@ -109,6 +109,39 @@ fn fix_all(snapshot: &DocumentSnapshot) -> crate::Result { Ok(types::CodeActionOrCommand::CodeAction(action)) } +fn organize_imports(snapshot: &DocumentSnapshot) -> crate::Result { + let document = snapshot.document(); + + let (edit, data) = if snapshot + .resolved_client_capabilities() + .code_action_deferred_edit_resolution + { + // The edit will be resolved later in the `CodeActionsResolve` request + ( + None, + Some(serde_json::to_value(snapshot.url()).expect("document url to serialize")), + ) + } else { + ( + Some(resolve_edit_for_organize_imports( + document, + snapshot.url(), + &snapshot.configuration().linter, + snapshot.encoding(), + )?), + None, + ) + }; + let action = types::CodeAction { + title: format!("{DIAGNOSTIC_NAME}: Organize imports"), + kind: Some(types::CodeActionKind::SOURCE_ORGANIZE_IMPORTS), + edit, + data, + ..Default::default() + }; + Ok(types::CodeActionOrCommand::CodeAction(action)) +} + /// If `action_filter` is `None`, this returns [`SupportedCodeActionKind::all()`]. Otherwise, /// the list is filtered. fn supported_code_actions( diff --git a/crates/ruff_server/src/server/api/requests/code_action_resolve.rs b/crates/ruff_server/src/server/api/requests/code_action_resolve.rs index ffb05c621062f..326c99ecb9e97 100644 --- a/crates/ruff_server/src/server/api/requests/code_action_resolve.rs +++ b/crates/ruff_server/src/server/api/requests/code_action_resolve.rs @@ -7,6 +7,7 @@ use crate::session::DocumentSnapshot; use crate::PositionEncoding; use lsp_server::ErrorCode; use lsp_types::{self as types, request as req}; +use ruff_linter::codes::Rule; use ruff_linter::settings::LinterSettings; pub(crate) struct CodeActionResolve; @@ -47,9 +48,15 @@ impl super::BackgroundDocumentRequestHandler for CodeActionResolve { ) .with_failure_code(ErrorCode::InternalError)?, ), - SupportedCodeAction::SourceOrganizeImports => { - todo!("Support `source.organizeImports`") - } + SupportedCodeAction::SourceOrganizeImports => Some( + resolve_edit_for_organize_imports( + document, + snapshot.url(), + &snapshot.configuration().linter, + snapshot.encoding(), + ) + .with_failure_code(ErrorCode::InternalError)?, + ), SupportedCodeAction::QuickFix => { return Err(anyhow::anyhow!( "Got a code action that should not need additional resolution: {action_kind:?}" @@ -80,3 +87,30 @@ pub(super) fn resolve_edit_for_fix_all( ..Default::default() }) } + +pub(super) fn resolve_edit_for_organize_imports( + document: &crate::edit::Document, + url: &types::Url, + linter_settings: &ruff_linter::settings::LinterSettings, + encoding: PositionEncoding, +) -> crate::Result { + let mut linter_settings = linter_settings.clone(); + linter_settings.rules = [ + Rule::UnsortedImports, // I001 + Rule::MissingRequiredImport, // I002 + ] + .into_iter() + .collect(); + + Ok(types::WorkspaceEdit { + changes: Some( + [( + url.clone(), + crate::fix::fix_all(document, &linter_settings, encoding)?, + )] + .into_iter() + .collect(), + ), + ..Default::default() + }) +}