Skip to content

Commit

Permalink
Enable ruff-specific source actions (#10916)
Browse files Browse the repository at this point in the history
## Summary

Fixes #10780.

The server now send code actions to the client with a Ruff-specific
kind, `source.*.ruff`. The kind filtering logic has also been reworked
to support this.

## Test Plan

Add this to your `settings.json` in VS Code:

```json
{
  "[python]": {
    "editor.codeActionsOnSave": {
      "source.organizeImports.ruff": "explicit",
    },
  }
}
```

Imports should be automatically organized when you manually save with
`Ctrl/Cmd+S`.
  • Loading branch information
snowsignal authored Apr 16, 2024
1 parent cffc555 commit eab3c4e
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 47 deletions.
34 changes: 12 additions & 22 deletions crates/ruff_server/src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ impl Server {
CodeActionOptions {
code_action_kinds: Some(
SupportedCodeAction::all()
.flat_map(|action| action.kinds().into_iter())
.map(SupportedCodeAction::to_kind)
.collect(),
),
work_done_progress_options: WorkDoneProgressOptions {
Expand Down Expand Up @@ -284,18 +284,21 @@ pub(crate) enum SupportedCodeAction {
}

impl SupportedCodeAction {
/// Returns the possible LSP code action kind(s) that map to this code action.
fn kinds(self) -> Vec<CodeActionKind> {
/// Returns the LSP code action kind that map to this code action.
fn to_kind(self) -> CodeActionKind {
match self {
Self::QuickFix => vec![CodeActionKind::QUICKFIX],
Self::SourceFixAll => vec![CodeActionKind::SOURCE_FIX_ALL, crate::SOURCE_FIX_ALL_RUFF],
Self::SourceOrganizeImports => vec![
CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
crate::SOURCE_ORGANIZE_IMPORTS_RUFF,
],
Self::QuickFix => CodeActionKind::QUICKFIX,
Self::SourceFixAll => crate::SOURCE_FIX_ALL_RUFF,
Self::SourceOrganizeImports => crate::SOURCE_ORGANIZE_IMPORTS_RUFF,
}
}

fn from_kind(kind: CodeActionKind) -> impl Iterator<Item = Self> {
Self::all().filter(move |supported_kind| {
supported_kind.to_kind().as_str().starts_with(kind.as_str())
})
}

/// Returns all code actions kinds that the server currently supports.
fn all() -> impl Iterator<Item = Self> {
[
Expand All @@ -306,16 +309,3 @@ impl SupportedCodeAction {
.into_iter()
}
}

impl TryFrom<CodeActionKind> for SupportedCodeAction {
type Error = ();

fn try_from(kind: CodeActionKind) -> std::result::Result<Self, Self::Error> {
for supported_kind in Self::all() {
if supported_kind.kinds().contains(&kind) {
return Ok(supported_kind);
}
}
Err(())
}
}
28 changes: 11 additions & 17 deletions crates/ruff_server/src/server/api/requests/code_action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,14 +112,14 @@ fn fix_all(snapshot: &DocumentSnapshot) -> crate::Result<CodeActionOrCommand> {
None,
)
};
let action = types::CodeAction {

Ok(CodeActionOrCommand::CodeAction(types::CodeAction {
title: format!("{DIAGNOSTIC_NAME}: Fix all auto-fixable problems"),
kind: Some(types::CodeActionKind::SOURCE_FIX_ALL),
kind: Some(crate::SOURCE_FIX_ALL_RUFF),
edit,
data,
..Default::default()
};
Ok(types::CodeActionOrCommand::CodeAction(action))
}))
}

fn organize_imports(snapshot: &DocumentSnapshot) -> crate::Result<CodeActionOrCommand> {
Expand Down Expand Up @@ -147,14 +147,14 @@ fn organize_imports(snapshot: &DocumentSnapshot) -> crate::Result<CodeActionOrCo
None,
)
};
let action = types::CodeAction {

Ok(CodeActionOrCommand::CodeAction(types::CodeAction {
title: format!("{DIAGNOSTIC_NAME}: Organize imports"),
kind: Some(types::CodeActionKind::SOURCE_ORGANIZE_IMPORTS),
kind: Some(crate::SOURCE_ORGANIZE_IMPORTS_RUFF),
edit,
data,
..Default::default()
};
Ok(types::CodeActionOrCommand::CodeAction(action))
}))
}

/// If `action_filter` is `None`, this returns [`SupportedCodeActionKind::all()`]. Otherwise,
Expand All @@ -166,14 +166,8 @@ fn supported_code_actions(
return SupportedCodeAction::all().collect();
};

SupportedCodeAction::all()
.filter(move |action| {
action_filter.iter().any(|filter| {
action
.kinds()
.iter()
.any(|kind| kind.as_str().starts_with(filter.as_str()))
})
})
action_filter
.into_iter()
.flat_map(SupportedCodeAction::from_kind)
.collect()
}
25 changes: 17 additions & 8 deletions crates/ruff_server/src/server/api/requests/code_action_resolve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,23 @@ impl super::BackgroundDocumentRequestHandler for CodeActionResolve {
) -> Result<types::CodeAction> {
let document = snapshot.document();

let action_kind: SupportedCodeAction = action
.kind
.clone()
.ok_or(anyhow::anyhow!("No kind was given for code action"))
.with_failure_code(ErrorCode::InvalidParams)?
.try_into()
.map_err(|()| anyhow::anyhow!("Code action was of an invalid kind"))
.with_failure_code(ErrorCode::InvalidParams)?;
let code_actions = SupportedCodeAction::from_kind(
action
.kind
.clone()
.ok_or(anyhow::anyhow!("No kind was given for code action"))
.with_failure_code(ErrorCode::InvalidParams)?,
)
.collect::<Vec<_>>();

// Ensure that the code action maps to _exactly one_ supported code action
let [action_kind] = code_actions.as_slice() else {
return Err(anyhow::anyhow!(
"Code action resolver did not expect code action kind {:?}",
action.kind.as_ref().unwrap()
))
.with_failure_code(ErrorCode::InvalidParams);
};

action.edit = match action_kind {
SupportedCodeAction::SourceFixAll => Some(
Expand Down

0 comments on commit eab3c4e

Please sign in to comment.