mirror of
https://github.com/astral-sh/ruff.git
synced 2025-12-09 03:40:36 +00:00
ruff server: Introduce settings for directly configuring the linter and formatter (#10984)
## Summary The following client settings have been introduced to the language server: * `lint.preview` * `format.preview` * `lint.select` * `lint.extendSelect` * `lint.ignore` * `exclude` * `lineLength` `exclude` and `lineLength` apply to both the linter and formatter. This does not actually use the settings yet, but makes them available for future use. ## Test Plan Snapshot tests have been updated.
This commit is contained in:
parent
5da7299b32
commit
2cc487eb22
3 changed files with 222 additions and 29 deletions
|
|
@ -6,9 +6,12 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"lint": {
|
"lint": {
|
||||||
|
"ignore": ["RUF001"],
|
||||||
"run": "onSave"
|
"run": "onSave"
|
||||||
},
|
},
|
||||||
"fixAll": false,
|
"fixAll": false,
|
||||||
"logLevel": "warn"
|
"logLevel": "warn",
|
||||||
|
"lineLength": 80,
|
||||||
|
"exclude": ["third_party"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,7 @@
|
||||||
},
|
},
|
||||||
"lint": {
|
"lint": {
|
||||||
"enable": true,
|
"enable": true,
|
||||||
|
"preview": false,
|
||||||
"run": "onType",
|
"run": "onType",
|
||||||
"args": [
|
"args": [
|
||||||
"--preview"
|
"--preview"
|
||||||
|
|
@ -85,6 +86,8 @@
|
||||||
},
|
},
|
||||||
"lint": {
|
"lint": {
|
||||||
"enable": true,
|
"enable": true,
|
||||||
|
"preview": true,
|
||||||
|
"select": ["F", "I"],
|
||||||
"run": "onType",
|
"run": "onType",
|
||||||
"args": [
|
"args": [
|
||||||
"--preview"
|
"--preview"
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
use std::ops::Deref;
|
use std::{ffi::OsString, ops::Deref, path::PathBuf, str::FromStr};
|
||||||
|
|
||||||
use lsp_types::Url;
|
use lsp_types::Url;
|
||||||
|
use ruff_linter::{line_width::LineLength, RuleSelector};
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
|
@ -21,6 +22,25 @@ pub(crate) struct ResolvedClientSettings {
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
disable_rule_comment_enable: bool,
|
disable_rule_comment_enable: bool,
|
||||||
fix_violation_enable: bool,
|
fix_violation_enable: bool,
|
||||||
|
// TODO(jane): Remove once editor settings resolution is implemented
|
||||||
|
#[allow(dead_code)]
|
||||||
|
editor_settings: ResolvedEditorSettings,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Contains the resolved values of 'editor settings' - Ruff configuration for the linter/formatter that was passed in via
|
||||||
|
/// LSP client settings. These fields are optional because we don't want to override file-based linter/formatting settings
|
||||||
|
/// if these were un-set.
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
#[cfg_attr(test, derive(PartialEq, Eq))]
|
||||||
|
#[allow(dead_code)] // TODO(jane): Remove once editor settings resolution is implemented
|
||||||
|
pub(crate) struct ResolvedEditorSettings {
|
||||||
|
lint_preview: Option<bool>,
|
||||||
|
format_preview: Option<bool>,
|
||||||
|
select: Option<Vec<RuleSelector>>,
|
||||||
|
extend_select: Option<Vec<RuleSelector>>,
|
||||||
|
ignore: Option<Vec<RuleSelector>>,
|
||||||
|
exclude: Option<Vec<PathBuf>>,
|
||||||
|
line_length: Option<LineLength>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This is a direct representation of the settings schema sent by the client.
|
/// This is a direct representation of the settings schema sent by the client.
|
||||||
|
|
@ -30,8 +50,11 @@ pub(crate) struct ResolvedClientSettings {
|
||||||
pub(crate) struct ClientSettings {
|
pub(crate) struct ClientSettings {
|
||||||
fix_all: Option<bool>,
|
fix_all: Option<bool>,
|
||||||
organize_imports: Option<bool>,
|
organize_imports: Option<bool>,
|
||||||
lint: Option<Lint>,
|
lint: Option<LintOptions>,
|
||||||
code_action: Option<CodeAction>,
|
format: Option<FormatOptions>,
|
||||||
|
code_action: Option<CodeActionOptions>,
|
||||||
|
exclude: Option<Vec<String>>,
|
||||||
|
line_length: Option<LineLength>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This is a direct representation of the workspace settings schema,
|
/// This is a direct representation of the workspace settings schema,
|
||||||
|
|
@ -49,22 +72,33 @@ struct WorkspaceSettings {
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
#[cfg_attr(test, derive(PartialEq, Eq))]
|
#[cfg_attr(test, derive(PartialEq, Eq))]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
struct Lint {
|
struct LintOptions {
|
||||||
enable: Option<bool>,
|
enable: Option<bool>,
|
||||||
|
preview: Option<bool>,
|
||||||
|
select: Option<Vec<String>>,
|
||||||
|
extend_select: Option<Vec<String>>,
|
||||||
|
ignore: Option<Vec<String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[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")]
|
||||||
struct CodeAction {
|
struct FormatOptions {
|
||||||
disable_rule_comment: Option<CodeActionSettings>,
|
preview: Option<bool>,
|
||||||
fix_violation: Option<CodeActionSettings>,
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Deserialize)]
|
||||||
|
#[cfg_attr(test, derive(PartialEq, Eq))]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
struct CodeActionOptions {
|
||||||
|
disable_rule_comment: Option<CodeActionParameters>,
|
||||||
|
fix_violation: Option<CodeActionParameters>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
#[cfg_attr(test, derive(PartialEq, Eq))]
|
#[cfg_attr(test, derive(PartialEq, Eq))]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
struct CodeActionSettings {
|
struct CodeActionParameters {
|
||||||
enable: Option<bool>,
|
enable: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -179,22 +213,80 @@ impl ResolvedClientSettings {
|
||||||
},
|
},
|
||||||
true,
|
true,
|
||||||
),
|
),
|
||||||
|
editor_settings: ResolvedEditorSettings {
|
||||||
|
lint_preview: Self::resolve_optional(all_settings, |settings| {
|
||||||
|
settings.lint.as_ref()?.preview
|
||||||
|
}),
|
||||||
|
format_preview: Self::resolve_optional(all_settings, |settings| {
|
||||||
|
settings.format.as_ref()?.preview
|
||||||
|
}),
|
||||||
|
select: Self::resolve_optional(all_settings, |settings| {
|
||||||
|
settings
|
||||||
|
.lint
|
||||||
|
.as_ref()?
|
||||||
|
.select
|
||||||
|
.as_ref()?
|
||||||
|
.iter()
|
||||||
|
.map(|rule| RuleSelector::from_str(rule).ok())
|
||||||
|
.collect()
|
||||||
|
}),
|
||||||
|
extend_select: Self::resolve_optional(all_settings, |settings| {
|
||||||
|
settings
|
||||||
|
.lint
|
||||||
|
.as_ref()?
|
||||||
|
.extend_select
|
||||||
|
.as_ref()?
|
||||||
|
.iter()
|
||||||
|
.map(|rule| RuleSelector::from_str(rule).ok())
|
||||||
|
.collect()
|
||||||
|
}),
|
||||||
|
ignore: Self::resolve_optional(all_settings, |settings| {
|
||||||
|
settings
|
||||||
|
.lint
|
||||||
|
.as_ref()?
|
||||||
|
.ignore
|
||||||
|
.as_ref()?
|
||||||
|
.iter()
|
||||||
|
.map(|rule| RuleSelector::from_str(rule).ok())
|
||||||
|
.collect()
|
||||||
|
}),
|
||||||
|
exclude: Self::resolve_optional(all_settings, |settings| {
|
||||||
|
Some(
|
||||||
|
settings
|
||||||
|
.exclude
|
||||||
|
.as_ref()?
|
||||||
|
.iter()
|
||||||
|
.map(|path| PathBuf::from(OsString::from(path)))
|
||||||
|
.collect(),
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
line_length: Self::resolve_optional(all_settings, |settings| settings.line_length),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Attempts to resolve a setting using a list of available client settings as sources.
|
||||||
|
/// Client settings that come earlier in the list take priority. This function is for fields
|
||||||
|
/// that do not have a default value and should be left unset.
|
||||||
|
/// Use [`ResolvedClientSettings::resolve_or`] for settings that should have default values.
|
||||||
|
fn resolve_optional<T>(
|
||||||
|
all_settings: &[&ClientSettings],
|
||||||
|
get: impl Fn(&ClientSettings) -> Option<T>,
|
||||||
|
) -> Option<T> {
|
||||||
|
all_settings.iter().map(Deref::deref).find_map(get)
|
||||||
|
}
|
||||||
|
|
||||||
/// Attempts to resolve a setting using a list of available client settings as sources.
|
/// Attempts to resolve a setting using a list of available client settings as sources.
|
||||||
/// Client settings that come earlier in the list take priority. `default` will be returned
|
/// Client settings that come earlier in the list take priority. `default` will be returned
|
||||||
/// if none of the settings specify the requested setting.
|
/// if none of the settings specify the requested setting.
|
||||||
|
/// Use [`ResolvedClientSettings::resolve_optional`] if the setting should be optional instead
|
||||||
|
/// of having a default value.
|
||||||
fn resolve_or<T>(
|
fn resolve_or<T>(
|
||||||
all_settings: &[&ClientSettings],
|
all_settings: &[&ClientSettings],
|
||||||
get: impl Fn(&ClientSettings) -> Option<T>,
|
get: impl Fn(&ClientSettings) -> Option<T>,
|
||||||
default: T,
|
default: T,
|
||||||
) -> T {
|
) -> T {
|
||||||
all_settings
|
Self::resolve_optional(all_settings, get).unwrap_or(default)
|
||||||
.iter()
|
|
||||||
.map(Deref::deref)
|
|
||||||
.find_map(get)
|
|
||||||
.unwrap_or(default)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -225,6 +317,7 @@ impl Default for InitializationOptions {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use insta::assert_debug_snapshot;
|
use insta::assert_debug_snapshot;
|
||||||
|
use ruff_linter::registry::Linter;
|
||||||
use serde::de::DeserializeOwned;
|
use serde::de::DeserializeOwned;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
@ -254,23 +347,39 @@ mod tests {
|
||||||
true,
|
true,
|
||||||
),
|
),
|
||||||
lint: Some(
|
lint: Some(
|
||||||
Lint {
|
LintOptions {
|
||||||
enable: Some(
|
enable: Some(
|
||||||
true,
|
true,
|
||||||
),
|
),
|
||||||
|
preview: Some(
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
select: Some(
|
||||||
|
[
|
||||||
|
"F",
|
||||||
|
"I",
|
||||||
|
],
|
||||||
|
),
|
||||||
|
extend_select: None,
|
||||||
|
ignore: None,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
format: Some(
|
||||||
|
FormatOptions {
|
||||||
|
preview: None,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
code_action: Some(
|
code_action: Some(
|
||||||
CodeAction {
|
CodeActionOptions {
|
||||||
disable_rule_comment: Some(
|
disable_rule_comment: Some(
|
||||||
CodeActionSettings {
|
CodeActionParameters {
|
||||||
enable: Some(
|
enable: Some(
|
||||||
false,
|
false,
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
fix_violation: Some(
|
fix_violation: Some(
|
||||||
CodeActionSettings {
|
CodeActionParameters {
|
||||||
enable: Some(
|
enable: Some(
|
||||||
false,
|
false,
|
||||||
),
|
),
|
||||||
|
|
@ -278,6 +387,8 @@ mod tests {
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
exclude: None,
|
||||||
|
line_length: None,
|
||||||
},
|
},
|
||||||
workspace_settings: [
|
workspace_settings: [
|
||||||
WorkspaceSettings {
|
WorkspaceSettings {
|
||||||
|
|
@ -289,23 +400,32 @@ mod tests {
|
||||||
true,
|
true,
|
||||||
),
|
),
|
||||||
lint: Some(
|
lint: Some(
|
||||||
Lint {
|
LintOptions {
|
||||||
enable: Some(
|
enable: Some(
|
||||||
true,
|
true,
|
||||||
),
|
),
|
||||||
|
preview: None,
|
||||||
|
select: None,
|
||||||
|
extend_select: None,
|
||||||
|
ignore: None,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
format: Some(
|
||||||
|
FormatOptions {
|
||||||
|
preview: None,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
code_action: Some(
|
code_action: Some(
|
||||||
CodeAction {
|
CodeActionOptions {
|
||||||
disable_rule_comment: Some(
|
disable_rule_comment: Some(
|
||||||
CodeActionSettings {
|
CodeActionParameters {
|
||||||
enable: Some(
|
enable: Some(
|
||||||
false,
|
false,
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
fix_violation: Some(
|
fix_violation: Some(
|
||||||
CodeActionSettings {
|
CodeActionParameters {
|
||||||
enable: Some(
|
enable: Some(
|
||||||
false,
|
false,
|
||||||
),
|
),
|
||||||
|
|
@ -313,6 +433,8 @@ mod tests {
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
exclude: None,
|
||||||
|
line_length: None,
|
||||||
},
|
},
|
||||||
workspace: Url {
|
workspace: Url {
|
||||||
scheme: "file",
|
scheme: "file",
|
||||||
|
|
@ -335,23 +457,34 @@ mod tests {
|
||||||
true,
|
true,
|
||||||
),
|
),
|
||||||
lint: Some(
|
lint: Some(
|
||||||
Lint {
|
LintOptions {
|
||||||
enable: Some(
|
enable: Some(
|
||||||
true,
|
true,
|
||||||
),
|
),
|
||||||
|
preview: Some(
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
select: None,
|
||||||
|
extend_select: None,
|
||||||
|
ignore: None,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
format: Some(
|
||||||
|
FormatOptions {
|
||||||
|
preview: None,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
code_action: Some(
|
code_action: Some(
|
||||||
CodeAction {
|
CodeActionOptions {
|
||||||
disable_rule_comment: Some(
|
disable_rule_comment: Some(
|
||||||
CodeActionSettings {
|
CodeActionParameters {
|
||||||
enable: Some(
|
enable: Some(
|
||||||
true,
|
true,
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
fix_violation: Some(
|
fix_violation: Some(
|
||||||
CodeActionSettings {
|
CodeActionParameters {
|
||||||
enable: Some(
|
enable: Some(
|
||||||
false,
|
false,
|
||||||
),
|
),
|
||||||
|
|
@ -359,6 +492,8 @@ mod tests {
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
exclude: None,
|
||||||
|
line_length: None,
|
||||||
},
|
},
|
||||||
workspace: Url {
|
workspace: Url {
|
||||||
scheme: "file",
|
scheme: "file",
|
||||||
|
|
@ -399,6 +534,18 @@ mod tests {
|
||||||
lint_enable: true,
|
lint_enable: true,
|
||||||
disable_rule_comment_enable: false,
|
disable_rule_comment_enable: false,
|
||||||
fix_violation_enable: false,
|
fix_violation_enable: false,
|
||||||
|
editor_settings: ResolvedEditorSettings {
|
||||||
|
lint_preview: Some(true),
|
||||||
|
format_preview: None,
|
||||||
|
select: Some(vec![
|
||||||
|
RuleSelector::Linter(Linter::Pyflakes),
|
||||||
|
RuleSelector::Linter(Linter::Isort)
|
||||||
|
]),
|
||||||
|
extend_select: None,
|
||||||
|
ignore: None,
|
||||||
|
exclude: None,
|
||||||
|
line_length: None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
let url = Url::parse("file:///Users/test/projects/scipy").expect("url should parse");
|
let url = Url::parse("file:///Users/test/projects/scipy").expect("url should parse");
|
||||||
|
|
@ -415,6 +562,18 @@ mod tests {
|
||||||
lint_enable: true,
|
lint_enable: true,
|
||||||
disable_rule_comment_enable: true,
|
disable_rule_comment_enable: true,
|
||||||
fix_violation_enable: false,
|
fix_violation_enable: false,
|
||||||
|
editor_settings: ResolvedEditorSettings {
|
||||||
|
lint_preview: Some(false),
|
||||||
|
format_preview: None,
|
||||||
|
select: Some(vec![
|
||||||
|
RuleSelector::Linter(Linter::Pyflakes),
|
||||||
|
RuleSelector::Linter(Linter::Isort)
|
||||||
|
]),
|
||||||
|
extend_select: None,
|
||||||
|
ignore: None,
|
||||||
|
exclude: None,
|
||||||
|
line_length: None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -432,14 +591,23 @@ mod tests {
|
||||||
),
|
),
|
||||||
organize_imports: None,
|
organize_imports: None,
|
||||||
lint: Some(
|
lint: Some(
|
||||||
Lint {
|
LintOptions {
|
||||||
enable: None,
|
enable: None,
|
||||||
|
preview: None,
|
||||||
|
select: None,
|
||||||
|
extend_select: None,
|
||||||
|
ignore: Some(
|
||||||
|
[
|
||||||
|
"RUF001",
|
||||||
|
],
|
||||||
|
),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
format: None,
|
||||||
code_action: Some(
|
code_action: Some(
|
||||||
CodeAction {
|
CodeActionOptions {
|
||||||
disable_rule_comment: Some(
|
disable_rule_comment: Some(
|
||||||
CodeActionSettings {
|
CodeActionParameters {
|
||||||
enable: Some(
|
enable: Some(
|
||||||
false,
|
false,
|
||||||
),
|
),
|
||||||
|
|
@ -448,6 +616,16 @@ mod tests {
|
||||||
fix_violation: None,
|
fix_violation: None,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
exclude: Some(
|
||||||
|
[
|
||||||
|
"third_party",
|
||||||
|
],
|
||||||
|
),
|
||||||
|
line_length: Some(
|
||||||
|
LineLength(
|
||||||
|
80,
|
||||||
|
),
|
||||||
|
),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
@ -469,6 +647,15 @@ mod tests {
|
||||||
lint_enable: true,
|
lint_enable: true,
|
||||||
disable_rule_comment_enable: false,
|
disable_rule_comment_enable: false,
|
||||||
fix_violation_enable: true,
|
fix_violation_enable: true,
|
||||||
|
editor_settings: ResolvedEditorSettings {
|
||||||
|
lint_preview: None,
|
||||||
|
format_preview: None,
|
||||||
|
select: None,
|
||||||
|
extend_select: None,
|
||||||
|
ignore: Some(vec![RuleSelector::from_str("RUF001").unwrap()]),
|
||||||
|
exclude: Some(vec![PathBuf::from_str("third_party").unwrap()]),
|
||||||
|
line_length: Some(LineLength::try_from(80).unwrap())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue