mirror of
https://github.com/astral-sh/ruff.git
synced 2025-10-01 14:21:53 +00:00
Consider --preview
flag for server
subcommand (#12208)
## Summary This PR removes the requirement of `--preview` flag to run the `ruff server` and instead considers it to be an indicator to turn on preview mode for the linter and the formatter. resolves: #12161 ## Test Plan Add test cases to assert the `preview` value is updated accordingly. In an editor context, I used the local `ruff` executable in Neovim with the `--preview` flag and verified that the preview-only violations are being highlighted. Running with: ```lua require('lspconfig').ruff.setup({ cmd = { '/Users/dhruv/work/astral/ruff/target/debug/ruff', 'server', '--preview', }, }) ``` The screenshot shows that `E502` is highlighted with the below config in `pyproject.toml`: <img width="877" alt="Screenshot 2024-07-17 at 16 43 09" src="https://github.com/user-attachments/assets/c7016ef3-55b1-4a14-bbd3-a07b1bcdd323">
This commit is contained in:
parent
ebe5b06c95
commit
2e77b775b0
7 changed files with 145 additions and 37 deletions
|
@ -494,9 +494,20 @@ pub struct FormatCommand {
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, clap::Parser)]
|
#[derive(Copy, Clone, Debug, clap::Parser)]
|
||||||
pub struct ServerCommand {
|
pub struct ServerCommand {
|
||||||
/// Enable preview mode; required for regular operation
|
/// Enable preview mode. Use `--no-preview` to disable.
|
||||||
#[arg(long)]
|
///
|
||||||
pub(crate) preview: bool,
|
/// This enables unstable server features and turns on the preview mode for the linter
|
||||||
|
/// and the formatter.
|
||||||
|
#[arg(long, overrides_with("no_preview"))]
|
||||||
|
preview: bool,
|
||||||
|
#[clap(long, overrides_with("preview"), hide = true)]
|
||||||
|
no_preview: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ServerCommand {
|
||||||
|
pub(crate) fn resolve_preview(self) -> Option<bool> {
|
||||||
|
resolve_bool_arg(self.preview, self.no_preview)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, clap::ValueEnum)]
|
#[derive(Debug, Clone, Copy, clap::ValueEnum)]
|
||||||
|
|
|
@ -4,13 +4,11 @@ use crate::ExitStatus;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use ruff_server::Server;
|
use ruff_server::Server;
|
||||||
|
|
||||||
pub(crate) fn run_server(preview: bool, worker_threads: NonZeroUsize) -> Result<ExitStatus> {
|
pub(crate) fn run_server(
|
||||||
if !preview {
|
worker_threads: NonZeroUsize,
|
||||||
tracing::error!("--preview needs to be provided as a command line argument while the server is still unstable.\nFor example: `ruff server --preview`");
|
preview: Option<bool>,
|
||||||
return Ok(ExitStatus::Error);
|
) -> Result<ExitStatus> {
|
||||||
}
|
let server = Server::new(worker_threads, preview)?;
|
||||||
|
|
||||||
let server = Server::new(worker_threads)?;
|
|
||||||
|
|
||||||
server.run().map(|()| ExitStatus::Success)
|
server.run().map(|()| ExitStatus::Success)
|
||||||
}
|
}
|
||||||
|
|
|
@ -200,15 +200,13 @@ fn format(args: FormatCommand, global_options: GlobalConfigArgs) -> Result<ExitS
|
||||||
}
|
}
|
||||||
|
|
||||||
fn server(args: ServerCommand) -> Result<ExitStatus> {
|
fn server(args: ServerCommand) -> Result<ExitStatus> {
|
||||||
let ServerCommand { preview } = args;
|
|
||||||
|
|
||||||
let four = NonZeroUsize::new(4).unwrap();
|
let four = NonZeroUsize::new(4).unwrap();
|
||||||
|
|
||||||
// by default, we set the number of worker threads to `num_cpus`, with a maximum of 4.
|
// by default, we set the number of worker threads to `num_cpus`, with a maximum of 4.
|
||||||
let worker_threads = std::thread::available_parallelism()
|
let worker_threads = std::thread::available_parallelism()
|
||||||
.unwrap_or(four)
|
.unwrap_or(four)
|
||||||
.max(four);
|
.max(four);
|
||||||
commands::server::run_server(preview, worker_threads)
|
commands::server::run_server(worker_threads, args.resolve_preview())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn check(args: CheckCommand, global_options: GlobalConfigArgs) -> Result<ExitStatus> {
|
pub fn check(args: CheckCommand, global_options: GlobalConfigArgs) -> Result<ExitStatus> {
|
||||||
|
|
16
crates/ruff_server/resources/test/fixtures/settings/empty_multiple_workspace.json
vendored
Normal file
16
crates/ruff_server/resources/test/fixtures/settings/empty_multiple_workspace.json
vendored
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
"settings": [
|
||||||
|
{
|
||||||
|
"cwd": "/Users/test/projects/first",
|
||||||
|
"workspace": "file:///Users/test/projects/first"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cwd": "/Users/test/projects/second",
|
||||||
|
"workspace": "file:///Users/test/projects/second"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"globalSettings": {
|
||||||
|
"cwd": "/",
|
||||||
|
"workspace": "/"
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"settings": [
|
"settings": [
|
||||||
{
|
{
|
||||||
"experimentalServer": true,
|
"nativeServer": "on",
|
||||||
"cwd": "/Users/test/projects/pandas",
|
"cwd": "/Users/test/projects/pandas",
|
||||||
"workspace": "file:///Users/test/projects/pandas",
|
"workspace": "file:///Users/test/projects/pandas",
|
||||||
"path": [],
|
"path": [],
|
||||||
|
@ -21,9 +21,7 @@
|
||||||
"lint": {
|
"lint": {
|
||||||
"enable": true,
|
"enable": true,
|
||||||
"run": "onType",
|
"run": "onType",
|
||||||
"args": [
|
"args": []
|
||||||
"--preview"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"format": {
|
"format": {
|
||||||
"args": []
|
"args": []
|
||||||
|
@ -31,10 +29,11 @@
|
||||||
"enable": true,
|
"enable": true,
|
||||||
"organizeImports": true,
|
"organizeImports": true,
|
||||||
"fixAll": true,
|
"fixAll": true,
|
||||||
"showNotifications": "off"
|
"showNotifications": "off",
|
||||||
|
"showSyntaxErrors": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"experimentalServer": true,
|
"nativeServer": "on",
|
||||||
"cwd": "/Users/test/projects/scipy",
|
"cwd": "/Users/test/projects/scipy",
|
||||||
"workspace": "file:///Users/test/projects/scipy",
|
"workspace": "file:///Users/test/projects/scipy",
|
||||||
"path": [],
|
"path": [],
|
||||||
|
@ -55,9 +54,7 @@
|
||||||
"enable": true,
|
"enable": true,
|
||||||
"preview": false,
|
"preview": false,
|
||||||
"run": "onType",
|
"run": "onType",
|
||||||
"args": [
|
"args": []
|
||||||
"--preview"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"format": {
|
"format": {
|
||||||
"args": []
|
"args": []
|
||||||
|
@ -65,11 +62,12 @@
|
||||||
"enable": true,
|
"enable": true,
|
||||||
"organizeImports": true,
|
"organizeImports": true,
|
||||||
"fixAll": true,
|
"fixAll": true,
|
||||||
"showNotifications": "off"
|
"showNotifications": "off",
|
||||||
|
"showSyntaxErrors": true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"globalSettings": {
|
"globalSettings": {
|
||||||
"experimentalServer": true,
|
"nativeServer": "on",
|
||||||
"cwd": "/",
|
"cwd": "/",
|
||||||
"workspace": "/",
|
"workspace": "/",
|
||||||
"path": [],
|
"path": [],
|
||||||
|
@ -89,9 +87,7 @@
|
||||||
"preview": true,
|
"preview": true,
|
||||||
"select": ["F", "I"],
|
"select": ["F", "I"],
|
||||||
"run": "onType",
|
"run": "onType",
|
||||||
"args": [
|
"args": []
|
||||||
"--preview"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"format": {
|
"format": {
|
||||||
"args": []
|
"args": []
|
||||||
|
@ -99,6 +95,7 @@
|
||||||
"enable": true,
|
"enable": true,
|
||||||
"organizeImports": true,
|
"organizeImports": true,
|
||||||
"fixAll": false,
|
"fixAll": false,
|
||||||
"showNotifications": "off"
|
"showNotifications": "off",
|
||||||
|
"showSyntaxErrors": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,7 +48,7 @@ pub struct Server {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Server {
|
impl Server {
|
||||||
pub fn new(worker_threads: NonZeroUsize) -> crate::Result<Self> {
|
pub fn new(worker_threads: NonZeroUsize, preview: Option<bool>) -> crate::Result<Self> {
|
||||||
let connection = ConnectionInitializer::stdio();
|
let connection = ConnectionInitializer::stdio();
|
||||||
|
|
||||||
let (id, init_params) = connection.initialize_start()?;
|
let (id, init_params) = connection.initialize_start()?;
|
||||||
|
@ -70,14 +70,18 @@ impl Server {
|
||||||
|
|
||||||
crate::message::init_messenger(connection.make_sender());
|
crate::message::init_messenger(connection.make_sender());
|
||||||
|
|
||||||
let AllSettings {
|
let mut all_settings = AllSettings::from_value(
|
||||||
global_settings,
|
|
||||||
mut workspace_settings,
|
|
||||||
} = AllSettings::from_value(
|
|
||||||
init_params
|
init_params
|
||||||
.initialization_options
|
.initialization_options
|
||||||
.unwrap_or_else(|| serde_json::Value::Object(serde_json::Map::default())),
|
.unwrap_or_else(|| serde_json::Value::Object(serde_json::Map::default())),
|
||||||
);
|
);
|
||||||
|
if let Some(preview) = preview {
|
||||||
|
all_settings.set_preview(preview);
|
||||||
|
}
|
||||||
|
let AllSettings {
|
||||||
|
global_settings,
|
||||||
|
mut workspace_settings,
|
||||||
|
} = all_settings;
|
||||||
|
|
||||||
crate::trace::init_tracing(
|
crate::trace::init_tracing(
|
||||||
connection.make_sender(),
|
connection.make_sender(),
|
||||||
|
|
|
@ -84,6 +84,20 @@ pub struct ClientSettings {
|
||||||
pub(crate) tracing: TracingSettings,
|
pub(crate) tracing: TracingSettings,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ClientSettings {
|
||||||
|
/// Update the preview flag for the linter and the formatter with the given value.
|
||||||
|
pub(crate) fn set_preview(&mut self, preview: bool) {
|
||||||
|
match self.lint.as_mut() {
|
||||||
|
None => self.lint = Some(LintOptions::default().with_preview(preview)),
|
||||||
|
Some(lint) => lint.set_preview(preview),
|
||||||
|
}
|
||||||
|
match self.format.as_mut() {
|
||||||
|
None => self.format = Some(FormatOptions::default().with_preview(preview)),
|
||||||
|
Some(format) => format.set_preview(preview),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Settings needed to initialize tracing. These will only be
|
/// Settings needed to initialize tracing. These will only be
|
||||||
/// read from the global configuration.
|
/// read from the global configuration.
|
||||||
#[derive(Debug, Deserialize, Default)]
|
#[derive(Debug, Deserialize, Default)]
|
||||||
|
@ -107,7 +121,7 @@ struct WorkspaceSettings {
|
||||||
workspace: Url,
|
workspace: Url,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Default, Deserialize)]
|
||||||
#[cfg_attr(test, derive(PartialEq, Eq))]
|
#[cfg_attr(test, derive(PartialEq, Eq))]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
struct LintOptions {
|
struct LintOptions {
|
||||||
|
@ -118,6 +132,17 @@ struct LintOptions {
|
||||||
ignore: Option<Vec<String>>,
|
ignore: Option<Vec<String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl LintOptions {
|
||||||
|
fn with_preview(mut self, preview: bool) -> LintOptions {
|
||||||
|
self.preview = Some(preview);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_preview(&mut self, preview: bool) {
|
||||||
|
self.preview = Some(preview);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Deserialize)]
|
#[derive(Debug, Default, Deserialize)]
|
||||||
#[cfg_attr(test, derive(PartialEq, Eq))]
|
#[cfg_attr(test, derive(PartialEq, Eq))]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
|
@ -125,6 +150,17 @@ struct FormatOptions {
|
||||||
preview: Option<bool>,
|
preview: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl FormatOptions {
|
||||||
|
fn with_preview(mut self, preview: bool) -> FormatOptions {
|
||||||
|
self.preview = Some(preview);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_preview(&mut self, preview: bool) {
|
||||||
|
self.preview = Some(preview);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Deserialize)]
|
#[derive(Debug, Default, Deserialize)]
|
||||||
#[cfg_attr(test, derive(PartialEq, Eq))]
|
#[cfg_attr(test, derive(PartialEq, Eq))]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
|
@ -159,6 +195,7 @@ enum InitializationOptions {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Built from the initialization options provided by the client.
|
/// Built from the initialization options provided by the client.
|
||||||
|
#[derive(Debug)]
|
||||||
pub(crate) struct AllSettings {
|
pub(crate) struct AllSettings {
|
||||||
pub(crate) global_settings: ClientSettings,
|
pub(crate) global_settings: ClientSettings,
|
||||||
/// If this is `None`, the client only passed in global settings.
|
/// If this is `None`, the client only passed in global settings.
|
||||||
|
@ -179,6 +216,16 @@ impl AllSettings {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Update the preview flag for both the global and all workspace settings.
|
||||||
|
pub(crate) fn set_preview(&mut self, preview: bool) {
|
||||||
|
self.global_settings.set_preview(preview);
|
||||||
|
if let Some(workspace_settings) = self.workspace_settings.as_mut() {
|
||||||
|
for settings in workspace_settings.values_mut() {
|
||||||
|
settings.set_preview(preview);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn from_init_options(options: InitializationOptions) -> Self {
|
fn from_init_options(options: InitializationOptions) -> Self {
|
||||||
let (global_settings, workspace_settings) = match options {
|
let (global_settings, workspace_settings) = match options {
|
||||||
InitializationOptions::GlobalOnly { settings } => (settings, None),
|
InitializationOptions::GlobalOnly { settings } => (settings, None),
|
||||||
|
@ -393,6 +440,11 @@ mod tests {
|
||||||
const EMPTY_INIT_OPTIONS_FIXTURE: &str =
|
const EMPTY_INIT_OPTIONS_FIXTURE: &str =
|
||||||
include_str!("../../resources/test/fixtures/settings/empty.json");
|
include_str!("../../resources/test/fixtures/settings/empty.json");
|
||||||
|
|
||||||
|
// This fixture contains multiple workspaces with empty initialization options. It only sets
|
||||||
|
// the `cwd` and the `workspace` value.
|
||||||
|
const EMPTY_MULTIPLE_WORKSPACE_INIT_OPTIONS_FIXTURE: &str =
|
||||||
|
include_str!("../../resources/test/fixtures/settings/empty_multiple_workspace.json");
|
||||||
|
|
||||||
fn deserialize_fixture<T: DeserializeOwned>(content: &str) -> T {
|
fn deserialize_fixture<T: DeserializeOwned>(content: &str) -> T {
|
||||||
serde_json::from_str(content).expect("test fixture JSON should deserialize")
|
serde_json::from_str(content).expect("test fixture JSON should deserialize")
|
||||||
}
|
}
|
||||||
|
@ -456,7 +508,9 @@ mod tests {
|
||||||
exclude: None,
|
exclude: None,
|
||||||
line_length: None,
|
line_length: None,
|
||||||
configuration_preference: None,
|
configuration_preference: None,
|
||||||
show_syntax_errors: None,
|
show_syntax_errors: Some(
|
||||||
|
true,
|
||||||
|
),
|
||||||
tracing: TracingSettings {
|
tracing: TracingSettings {
|
||||||
log_level: None,
|
log_level: None,
|
||||||
log_file: None,
|
log_file: None,
|
||||||
|
@ -509,7 +563,9 @@ mod tests {
|
||||||
exclude: None,
|
exclude: None,
|
||||||
line_length: None,
|
line_length: None,
|
||||||
configuration_preference: None,
|
configuration_preference: None,
|
||||||
show_syntax_errors: None,
|
show_syntax_errors: Some(
|
||||||
|
true,
|
||||||
|
),
|
||||||
tracing: TracingSettings {
|
tracing: TracingSettings {
|
||||||
log_level: None,
|
log_level: None,
|
||||||
log_file: None,
|
log_file: None,
|
||||||
|
@ -575,7 +631,9 @@ mod tests {
|
||||||
exclude: None,
|
exclude: None,
|
||||||
line_length: None,
|
line_length: None,
|
||||||
configuration_preference: None,
|
configuration_preference: None,
|
||||||
show_syntax_errors: None,
|
show_syntax_errors: Some(
|
||||||
|
true,
|
||||||
|
),
|
||||||
tracing: TracingSettings {
|
tracing: TracingSettings {
|
||||||
log_level: None,
|
log_level: None,
|
||||||
log_file: None,
|
log_file: None,
|
||||||
|
@ -771,4 +829,30 @@ mod tests {
|
||||||
|
|
||||||
assert_eq!(options, InitializationOptions::default());
|
assert_eq!(options, InitializationOptions::default());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn assert_preview_client_settings(settings: &ClientSettings, preview: bool) {
|
||||||
|
assert_eq!(settings.lint.as_ref().unwrap().preview.unwrap(), preview);
|
||||||
|
assert_eq!(settings.format.as_ref().unwrap().preview.unwrap(), preview);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn assert_preview_all_settings(all_settings: &AllSettings, preview: bool) {
|
||||||
|
assert_preview_client_settings(&all_settings.global_settings, preview);
|
||||||
|
if let Some(workspace_settings) = all_settings.workspace_settings.as_ref() {
|
||||||
|
for settings in workspace_settings.values() {
|
||||||
|
assert_preview_client_settings(settings, preview);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_preview_flag() {
|
||||||
|
let options = deserialize_fixture(EMPTY_MULTIPLE_WORKSPACE_INIT_OPTIONS_FIXTURE);
|
||||||
|
let mut all_settings = AllSettings::from_init_options(options);
|
||||||
|
|
||||||
|
all_settings.set_preview(false);
|
||||||
|
assert_preview_all_settings(&all_settings, false);
|
||||||
|
|
||||||
|
all_settings.set_preview(true);
|
||||||
|
assert_preview_all_settings(&all_settings, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue