Skip to content

Commit

Permalink
Add supported commands in server capabilities (#11850)
Browse files Browse the repository at this point in the history
## Summary

This PR updates the server capabilities to include the commands that
Ruff supports. This is similar to how there's a list of possible code
actions supported by the server.

I noticed this when I was trying to find whether Helix supported
workspace commands or not based on Jane's comment
(#11831 (comment))
and I found the `:lsp-workspace-command` in the editor but it didn't
show up anything in the picker.

So, I looked at the implementation in Helix
(https://github.com/helix-editor/helix/blob/9c479e6d2de3bca9dec304f9182cee2b1c0ad766/helix-term/src/commands/typed.rs#L1372-L1384)
which made me realize that Ruff doesn't provide this in its
capabilities. Currently, this does require `ruff` to be first in the
list of language servers in the user config but that should be resolved
by helix-editor/helix#10176. So, the following
config should work:

```toml
[[language]]
name = "python"
# Ruff should come first until helix-editor/helix#10176 is released
language-servers = ["ruff", "pyright"]
```

## Test Plan

1. Neovim's server capabilities output should include the supported
commands:

```
  executeCommandProvider = {                                                                                                                          
    commands = { "ruff.applyFormat", "ruff.applyAutofix", "ruff.applyOrganizeImports", "ruff.printDebugInformation" },                                
    workDoneProgress = false                                                                                                                          
  },
```

2. Helix should now display the commands to pick from when
`:lsp-workspace-command` is invoked:

<img width="832" alt="Screenshot 2024-06-13 at 08 47 14"
src="https://github.com/astral-sh/ruff/assets/67177269/09048ecd-c974-4e09-ab56-9482ff3d780b">
  • Loading branch information
dhruvmanila authored Jun 13, 2024
1 parent bcbddac commit 9dc226b
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 41 deletions.
62 changes: 62 additions & 0 deletions crates/ruff_server/src/server.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! Scheduling, I/O, and API endpoints.
use std::num::NonZeroUsize;
use std::str::FromStr;

use lsp_server as lsp;
use lsp_types as types;
Expand Down Expand Up @@ -276,6 +277,14 @@ impl Server {
},
},
)),
execute_command_provider: Some(types::ExecuteCommandOptions {
commands: SupportedCommand::all()
.map(|command| command.identifier().to_string())
.to_vec(),
work_done_progress_options: WorkDoneProgressOptions {
work_done_progress: Some(false),
},
}),
hover_provider: Some(types::HoverProviderCapability::Simple(true)),
notebook_document_sync: Some(types::OneOf::Left(NotebookDocumentSyncOptions {
save: Some(false),
Expand Down Expand Up @@ -354,3 +363,56 @@ impl SupportedCodeAction {
.into_iter()
}
}

#[derive(Clone, Copy, Debug, PartialEq)]
pub(crate) enum SupportedCommand {
Debug,
Format,
FixAll,
OrganizeImports,
}

impl SupportedCommand {
const fn label(self) -> &'static str {
match self {
Self::FixAll => "Fix all auto-fixable problems",
Self::Format => "Format document",
Self::OrganizeImports => "Format imports",
Self::Debug => "Print debug information",
}
}

/// Returns the identifier of the command.
const fn identifier(self) -> &'static str {
match self {
SupportedCommand::Format => "ruff.applyFormat",
SupportedCommand::FixAll => "ruff.applyAutofix",
SupportedCommand::OrganizeImports => "ruff.applyOrganizeImports",
SupportedCommand::Debug => "ruff.printDebugInformation",
}
}

/// Returns all the commands that the server currently supports.
const fn all() -> [SupportedCommand; 4] {
[
SupportedCommand::Format,
SupportedCommand::FixAll,
SupportedCommand::OrganizeImports,
SupportedCommand::Debug,
]
}
}

impl FromStr for SupportedCommand {
type Err = anyhow::Error;

fn from_str(name: &str) -> anyhow::Result<Self, Self::Err> {
Ok(match name {
"ruff.applyAutofix" => Self::FixAll,
"ruff.applyFormat" => Self::Format,
"ruff.applyOrganizeImports" => Self::OrganizeImports,
"ruff.printDebugInformation" => Self::Debug,
_ => return Err(anyhow::anyhow!("Invalid command `{name}`")),
})
}
}
49 changes: 8 additions & 41 deletions crates/ruff_server/src/server/api/requests/execute_command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,15 @@ use std::str::FromStr;

use crate::edit::WorkspaceEditTracker;
use crate::server::api::LSPResult;
use crate::server::client;
use crate::server::schedule::Task;
use crate::server::{client, SupportedCommand};
use crate::session::Session;
use crate::DIAGNOSTIC_NAME;
use crate::{edit::DocumentVersion, server};
use lsp_server::ErrorCode;
use lsp_types::{self as types, request as req};
use serde::Deserialize;

#[derive(Debug, PartialEq)]
enum Command {
Debug,
Format,
FixAll,
OrganizeImports,
}

pub(crate) struct ExecuteCommand;

#[derive(Deserialize)]
Expand All @@ -38,10 +30,10 @@ impl super::SyncRequestHandler for ExecuteCommand {
requester: &mut client::Requester,
params: types::ExecuteCommandParams,
) -> server::Result<Option<serde_json::Value>> {
let command =
Command::from_str(&params.command).with_failure_code(ErrorCode::InvalidParams)?;
let command = SupportedCommand::from_str(&params.command)
.with_failure_code(ErrorCode::InvalidParams)?;

if command == Command::Debug {
if command == SupportedCommand::Debug {
let output = debug_information(session);
notifier
.notify::<types::notification::LogMessage>(types::LogMessageParams {
Expand Down Expand Up @@ -74,7 +66,7 @@ impl super::SyncRequestHandler for ExecuteCommand {
return Ok(None);
};
match command {
Command::FixAll => {
SupportedCommand::FixAll => {
let fixes = super::code_action_resolve::fix_all_edit(
snapshot.query(),
snapshot.encoding(),
Expand All @@ -84,13 +76,13 @@ impl super::SyncRequestHandler for ExecuteCommand {
.set_fixes_for_document(fixes, snapshot.query().version())
.with_failure_code(ErrorCode::InternalError)?;
}
Command::Format => {
SupportedCommand::Format => {
let fixes = super::format::format_full_document(&snapshot)?;
edit_tracker
.set_fixes_for_document(fixes, version)
.with_failure_code(ErrorCode::InternalError)?;
}
Command::OrganizeImports => {
SupportedCommand::OrganizeImports => {
let fixes = super::code_action_resolve::organize_imports_edit(
snapshot.query(),
snapshot.encoding(),
Expand All @@ -100,7 +92,7 @@ impl super::SyncRequestHandler for ExecuteCommand {
.set_fixes_for_document(fixes, snapshot.query().version())
.with_failure_code(ErrorCode::InternalError)?;
}
Command::Debug => {
SupportedCommand::Debug => {
unreachable!("The debug command should have already been handled")
}
}
Expand All @@ -119,31 +111,6 @@ impl super::SyncRequestHandler for ExecuteCommand {
}
}

impl Command {
fn label(&self) -> &str {
match self {
Self::FixAll => "Fix all auto-fixable problems",
Self::Format => "Format document",
Self::OrganizeImports => "Format imports",
Self::Debug => "Print debug information",
}
}
}

impl FromStr for Command {
type Err = anyhow::Error;

fn from_str(name: &str) -> Result<Self, Self::Err> {
Ok(match name {
"ruff.applyAutofix" => Self::FixAll,
"ruff.applyFormat" => Self::Format,
"ruff.applyOrganizeImports" => Self::OrganizeImports,
"ruff.printDebugInformation" => Self::Debug,
_ => return Err(anyhow::anyhow!("Invalid command `{name}`")),
})
}
}

fn apply_edit(
requester: &mut client::Requester,
label: &str,
Expand Down

0 comments on commit 9dc226b

Please sign in to comment.