mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-29 13:24:57 +00:00
Expand ruff.configuration
to allow inline config (#16296)
## Summary [Internal design document](https://www.notion.so/astral-sh/In-editor-settings-19e48797e1ca807fa8c2c91b689d9070?pvs=4) This PR expands `ruff.configuration` to allow inline configuration directly in the editor. For example: ```json { "ruff.configuration": { "line-length": 100, "lint": { "unfixable": ["F401"], "flake8-tidy-imports": { "banned-api": { "typing.TypedDict": { "msg": "Use `typing_extensions.TypedDict` instead" } } } }, "format": { "quote-style": "single" } } } ``` This means that now `ruff.configuration` accepts either a path to configuration file or the raw config itself. It's _mostly_ similar to `--config` with one difference that's highlighted in the following section. So, it can be said that the format of `ruff.configuration` when provided the config map is same as the one on the [playground] [^1]. ## Limitations <details><summary><b>Casing (<code>kebab-case</code> v/s/ <code>camelCase</code>)</b></summary> <p> The config keys needs to be in `kebab-case` instead of `camelCase` which is being used for other settings in the editor. This could be a bit confusing. For example, the `line-length` option can be set directly via an editor setting or can be configured via `ruff.configuration`: ```json { "ruff.configuration": { "line-length": 100 }, "ruff.lineLength": 120 } ``` #### Possible solution We could use feature flag with [conditional compilation](https://doc.rust-lang.org/reference/conditional-compilation.html#the-cfg_attr-attribute) to indicate that when used in `ruff_server`, we need the `Options` fields to be renamed as `camelCase` while for other crates it needs to be renamed as `kebab-case`. But, this might not work very easily because it will require wrapping the `Options` struct and create two structs in which we'll have to add `#[cfg_attr(...)]` because otherwise `serde` will complain: ``` error: duplicate serde attribute `rename_all` --> crates/ruff_workspace/src/options.rs:43:38 | 43 | #[cfg_attr(feature = "editor", serde(rename_all = "camelCase"))] | ^^^^^^^^^^ ``` </p> </details> <details><summary><b>Nesting (flat v/s nested keys)</b></summary> <p> This is the major difference between `--config` flag on the command-line v/s `ruff.configuration` and it makes it such that `ruff.configuration` has same value format as [playground] [^1]. The config keys needs to be split up into keys which can result in nested structure instead of flat structure: So, the following **won't work**: ```json { "ruff.configuration": { "format.quote-style": "single", "lint.flake8-tidy-imports.banned-api.\"typing.TypedDict\".msg": "Use `typing_extensions.TypedDict` instead" } } ``` But, instead it would need to be split up like the following: ```json { "ruff.configuration": { "format": { "quote-style": "single" }, "lint": { "flake8-tidy-imports": { "banned-api": { "typing.TypedDict": { "msg": "Use `typing_extensions.TypedDict` instead" } } } } } } ``` #### Possible solution (1) The way we could solve this and make it same as `--config` would be to add a manual logic of converting the JSON map into an equivalent TOML string which would be then parsed into `Options`. So, the following JSON map: ```json { "lint.flake8-tidy-imports": { "banned-api": {"\"typing.TypedDict\".msg": "Use typing_extensions.TypedDict instead"}}} ``` would need to be converted into the following TOML string: ```toml lint.flake8-tidy-imports = { banned-api = { "typing.TypedDict".msg = "Use typing_extensions.TypedDict instead" } } ``` by recursively convering `"key": value` into `key = value` which is to remove the quotes from key and replacing `:` with `=`. #### Possible solution (2) Another would be to just accept `Map<String, String>` strictly and convert it into `key = value` and then parse it as a TOML string. This would also match `--config` but quotes might become a nuisance because JSON only allows double quotes and so it'll require escaping any inner quotes or use single quotes. </p> </details> ## Test Plan ### VS Code **Requires https://github.com/astral-sh/ruff-vscode/pull/702** **`settings.json`**: ```json { "ruff.lint.extendSelect": ["TID"], "ruff.configuration": { "line-length": 50, "format": { "quote-style": "single" }, "lint": { "unfixable": ["F401"], "flake8-tidy-imports": { "banned-api": { "typing.TypedDict": { "msg": "Use `typing_extensions.TypedDict` instead" } } } } } } ``` Following video showcases me doing the following: 1. Check diagnostics that it includes `TID` 2. Run `Ruff: Fix all auto-fixable problems` to test `unfixable` 3. Run `Format: Document` to test `line-length` and `quote-style` https://github.com/user-attachments/assets/0a38176f-3fb0-4960-a213-73b2ea5b1180 ### Neovim **`init.lua`**: ```lua require('lspconfig').ruff.setup { init_options = { settings = { lint = { extendSelect = { 'TID' }, }, configuration = { ['line-length'] = 50, format = { ['quote-style'] = 'single', }, lint = { unfixable = { 'F401' }, ['flake8-tidy-imports'] = { ['banned-api'] = { ['typing.TypedDict'] = { msg = 'Use typing_extensions.TypedDict instead', }, }, }, }, }, }, }, } ``` Same steps as in the VS Code test: https://github.com/user-attachments/assets/cfe49a9b-9a89-43d7-94f2-7f565d6e3c9d ## Documentation Preview https://github.com/user-attachments/assets/e0062f58-6ec8-4e01-889d-fac76fd8b3c7 [playground]: https://play.ruff.rs [^1]: This has one advantage that the value can be copy-pasted directly into the playground
This commit is contained in:
parent
78806361fd
commit
be03cb04c1
7 changed files with 336 additions and 33 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -3190,6 +3190,7 @@ dependencies = [
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"shellexpand",
|
"shellexpand",
|
||||||
"thiserror 2.0.11",
|
"thiserror 2.0.11",
|
||||||
|
"toml",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
]
|
]
|
||||||
|
|
|
@ -830,7 +830,7 @@ enum InvalidConfigFlagReason {
|
||||||
ValidTomlButInvalidRuffSchema(toml::de::Error),
|
ValidTomlButInvalidRuffSchema(toml::de::Error),
|
||||||
/// It was a valid ruff config file, but the user tried to pass a
|
/// It was a valid ruff config file, but the user tried to pass a
|
||||||
/// value for `extend` as part of the config override.
|
/// value for `extend` as part of the config override.
|
||||||
// `extend` is special, because it affects which config files we look at
|
/// `extend` is special, because it affects which config files we look at
|
||||||
/// in the first place. We currently only parse --config overrides *after*
|
/// in the first place. We currently only parse --config overrides *after*
|
||||||
/// we've combined them with all the arguments from the various config files
|
/// we've combined them with all the arguments from the various config files
|
||||||
/// that we found, so trying to override `extend` as part of a --config
|
/// that we found, so trying to override `extend` as part of a --config
|
||||||
|
|
|
@ -38,6 +38,7 @@ serde = { workspace = true }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
shellexpand = { workspace = true }
|
shellexpand = { workspace = true }
|
||||||
thiserror = { workspace = true }
|
thiserror = { workspace = true }
|
||||||
|
toml = { workspace = true }
|
||||||
tracing = { workspace = true }
|
tracing = { workspace = true }
|
||||||
tracing-subscriber = { workspace = true }
|
tracing-subscriber = { workspace = true }
|
||||||
|
|
||||||
|
|
16
crates/ruff_server/resources/test/fixtures/settings/inline_configuration.json
vendored
Normal file
16
crates/ruff_server/resources/test/fixtures/settings/inline_configuration.json
vendored
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
"settings": {
|
||||||
|
"configuration": {
|
||||||
|
"line-length": 100,
|
||||||
|
"lint": {
|
||||||
|
"extend-select": ["I001"]
|
||||||
|
},
|
||||||
|
"format": {
|
||||||
|
"quote-style": "single"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lint": {
|
||||||
|
"extendSelect": ["RUF001"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,7 +18,9 @@ use ruff_workspace::{
|
||||||
resolver::{ConfigurationTransformer, Relativity},
|
resolver::{ConfigurationTransformer, Relativity},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::session::settings::{ConfigurationPreference, ResolvedEditorSettings};
|
use crate::session::settings::{
|
||||||
|
ConfigurationPreference, ResolvedConfiguration, ResolvedEditorSettings,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct RuffSettings {
|
pub struct RuffSettings {
|
||||||
|
@ -363,13 +365,15 @@ impl ConfigurationTransformer for EditorConfigurationTransformer<'_> {
|
||||||
..Configuration::default()
|
..Configuration::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
// Merge in the editor-specified configuration file, if it exists.
|
// Merge in the editor-specified configuration.
|
||||||
let editor_configuration = if let Some(config_file_path) = configuration {
|
let editor_configuration = if let Some(configuration) = configuration {
|
||||||
|
match configuration {
|
||||||
|
ResolvedConfiguration::FilePath(path) => {
|
||||||
tracing::debug!(
|
tracing::debug!(
|
||||||
"Combining settings from editor-specified configuration file at: {}",
|
"Combining settings from editor-specified configuration file at: {}",
|
||||||
config_file_path.display()
|
path.display()
|
||||||
);
|
);
|
||||||
match open_configuration_file(&config_file_path) {
|
match open_configuration_file(&path) {
|
||||||
Ok(config_from_file) => editor_configuration.combine(config_from_file),
|
Ok(config_from_file) => editor_configuration.combine(config_from_file),
|
||||||
err => {
|
err => {
|
||||||
tracing::error!(
|
tracing::error!(
|
||||||
|
@ -380,6 +384,22 @@ impl ConfigurationTransformer for EditorConfigurationTransformer<'_> {
|
||||||
editor_configuration
|
editor_configuration
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
ResolvedConfiguration::Inline(options) => {
|
||||||
|
tracing::debug!(
|
||||||
|
"Combining settings from editor-specified inline configuration"
|
||||||
|
);
|
||||||
|
match Configuration::from_options(options, None, project_root) {
|
||||||
|
Ok(configuration) => editor_configuration.combine(configuration),
|
||||||
|
Err(err) => {
|
||||||
|
tracing::error!(
|
||||||
|
"Unable to load editor-specified inline configuration: {err:?}",
|
||||||
|
);
|
||||||
|
editor_configuration
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
editor_configuration
|
editor_configuration
|
||||||
};
|
};
|
||||||
|
@ -411,3 +431,47 @@ impl ConfigurationTransformer for IdentityTransformer {
|
||||||
config
|
config
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use ruff_linter::line_width::LineLength;
|
||||||
|
use ruff_workspace::options::Options;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
/// This test ensures that the inline configuration is correctly applied to the configuration.
|
||||||
|
#[test]
|
||||||
|
fn inline_settings() {
|
||||||
|
let editor_settings = ResolvedEditorSettings {
|
||||||
|
configuration: Some(ResolvedConfiguration::Inline(Options {
|
||||||
|
line_length: Some(LineLength::try_from(120).unwrap()),
|
||||||
|
..Default::default()
|
||||||
|
})),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let config = EditorConfigurationTransformer(&editor_settings, Path::new("/src/project"))
|
||||||
|
.transform(Configuration::default());
|
||||||
|
|
||||||
|
assert_eq!(config.line_length.unwrap().value(), 120);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This test ensures that between the inline configuration and specific settings, the specific
|
||||||
|
/// settings is prioritized.
|
||||||
|
#[test]
|
||||||
|
fn inline_and_specific_settings_resolution_order() {
|
||||||
|
let editor_settings = ResolvedEditorSettings {
|
||||||
|
configuration: Some(ResolvedConfiguration::Inline(Options {
|
||||||
|
line_length: Some(LineLength::try_from(120).unwrap()),
|
||||||
|
..Default::default()
|
||||||
|
})),
|
||||||
|
line_length: Some(LineLength::try_from(100).unwrap()),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let config = EditorConfigurationTransformer(&editor_settings, Path::new("/src/project"))
|
||||||
|
.transform(Configuration::default());
|
||||||
|
|
||||||
|
assert_eq!(config.line_length.unwrap().value(), 100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -3,8 +3,11 @@ use std::{ops::Deref, path::PathBuf, str::FromStr};
|
||||||
use lsp_types::Url;
|
use lsp_types::Url;
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
use serde_json::{Map, Value};
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
use ruff_linter::{line_width::LineLength, RuleSelector};
|
use ruff_linter::{line_width::LineLength, RuleSelector};
|
||||||
|
use ruff_workspace::options::Options;
|
||||||
|
|
||||||
/// Maps a workspace URI to its associated client settings. Used during server initialization.
|
/// Maps a workspace URI to its associated client settings. Used during server initialization.
|
||||||
pub(crate) type WorkspaceSettingsMap = FxHashMap<Url, ClientSettings>;
|
pub(crate) type WorkspaceSettingsMap = FxHashMap<Url, ClientSettings>;
|
||||||
|
@ -29,9 +32,9 @@ pub(crate) struct ResolvedClientSettings {
|
||||||
/// LSP client settings. These fields are optional because we don't want to override file-based linter/formatting settings
|
/// LSP client settings. These fields are optional because we don't want to override file-based linter/formatting settings
|
||||||
/// if these were un-set.
|
/// if these were un-set.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
#[cfg_attr(test, derive(PartialEq, Eq))]
|
#[cfg_attr(test, derive(Default, PartialEq, Eq))]
|
||||||
pub(crate) struct ResolvedEditorSettings {
|
pub(crate) struct ResolvedEditorSettings {
|
||||||
pub(super) configuration: Option<PathBuf>,
|
pub(super) configuration: Option<ResolvedConfiguration>,
|
||||||
pub(super) lint_preview: Option<bool>,
|
pub(super) lint_preview: Option<bool>,
|
||||||
pub(super) format_preview: Option<bool>,
|
pub(super) format_preview: Option<bool>,
|
||||||
pub(super) select: Option<Vec<RuleSelector>>,
|
pub(super) select: Option<Vec<RuleSelector>>,
|
||||||
|
@ -42,6 +45,48 @@ pub(crate) struct ResolvedEditorSettings {
|
||||||
pub(super) configuration_preference: ConfigurationPreference,
|
pub(super) configuration_preference: ConfigurationPreference,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The resolved configuration from the client settings.
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
#[cfg_attr(test, derive(PartialEq, Eq))]
|
||||||
|
pub(crate) enum ResolvedConfiguration {
|
||||||
|
FilePath(PathBuf),
|
||||||
|
Inline(Options),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&ClientConfiguration> for ResolvedConfiguration {
|
||||||
|
type Error = ResolvedConfigurationError;
|
||||||
|
|
||||||
|
fn try_from(value: &ClientConfiguration) -> Result<Self, Self::Error> {
|
||||||
|
match value {
|
||||||
|
ClientConfiguration::String(path) => Ok(ResolvedConfiguration::FilePath(
|
||||||
|
PathBuf::from(shellexpand::full(path)?.as_ref()),
|
||||||
|
)),
|
||||||
|
ClientConfiguration::Object(map) => {
|
||||||
|
let options = toml::Table::try_from(map)?.try_into::<Options>()?;
|
||||||
|
if options.extend.is_some() {
|
||||||
|
Err(ResolvedConfigurationError::ExtendNotSupported)
|
||||||
|
} else {
|
||||||
|
Ok(ResolvedConfiguration::Inline(options))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An error that can occur when trying to resolve the `configuration` value from the client
|
||||||
|
/// settings.
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub(crate) enum ResolvedConfigurationError {
|
||||||
|
#[error(transparent)]
|
||||||
|
EnvVarLookupError(#[from] shellexpand::LookupError<std::env::VarError>),
|
||||||
|
#[error("error serializing configuration to TOML: {0}")]
|
||||||
|
InvalidToml(#[from] toml::ser::Error),
|
||||||
|
#[error(transparent)]
|
||||||
|
InvalidRuffSchema(#[from] toml::de::Error),
|
||||||
|
#[error("using `extend` is unsupported for inline configuration")]
|
||||||
|
ExtendNotSupported,
|
||||||
|
}
|
||||||
|
|
||||||
/// Determines how multiple conflicting configurations should be resolved - in this
|
/// Determines how multiple conflicting configurations should be resolved - in this
|
||||||
/// case, the configuration from the client settings and configuration from local
|
/// case, the configuration from the client settings and configuration from local
|
||||||
/// `.toml` files (aka 'workspace' configuration).
|
/// `.toml` files (aka 'workspace' configuration).
|
||||||
|
@ -57,12 +102,23 @@ pub(crate) enum ConfigurationPreference {
|
||||||
EditorOnly,
|
EditorOnly,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A direct representation of of `configuration` schema within the client settings.
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
#[cfg_attr(test, derive(PartialEq, Eq))]
|
||||||
|
#[serde(untagged)]
|
||||||
|
enum ClientConfiguration {
|
||||||
|
/// A path to a configuration file.
|
||||||
|
String(String),
|
||||||
|
/// An object containing the configuration options.
|
||||||
|
Object(Map<String, Value>),
|
||||||
|
}
|
||||||
|
|
||||||
/// 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.
|
||||||
#[derive(Debug, Deserialize, Default)]
|
#[derive(Debug, Deserialize, Default)]
|
||||||
#[cfg_attr(test, derive(PartialEq, Eq))]
|
#[cfg_attr(test, derive(PartialEq, Eq))]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct ClientSettings {
|
pub struct ClientSettings {
|
||||||
configuration: Option<String>,
|
configuration: Option<ClientConfiguration>,
|
||||||
fix_all: Option<bool>,
|
fix_all: Option<bool>,
|
||||||
organize_imports: Option<bool>,
|
organize_imports: Option<bool>,
|
||||||
lint: Option<LintOptions>,
|
lint: Option<LintOptions>,
|
||||||
|
@ -306,11 +362,17 @@ impl ResolvedClientSettings {
|
||||||
),
|
),
|
||||||
editor_settings: ResolvedEditorSettings {
|
editor_settings: ResolvedEditorSettings {
|
||||||
configuration: Self::resolve_optional(all_settings, |settings| {
|
configuration: Self::resolve_optional(all_settings, |settings| {
|
||||||
settings
|
settings.configuration.as_ref().and_then(|configuration| {
|
||||||
.configuration
|
match ResolvedConfiguration::try_from(configuration) {
|
||||||
.as_ref()
|
Ok(configuration) => Some(configuration),
|
||||||
.and_then(|config_path| shellexpand::full(config_path).ok())
|
Err(err) => {
|
||||||
.map(|config_path| PathBuf::from(config_path.as_ref()))
|
tracing::error!(
|
||||||
|
"Failed to load settings from `configuration`: {err}"
|
||||||
|
);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
}),
|
}),
|
||||||
lint_preview: Self::resolve_optional(all_settings, |settings| {
|
lint_preview: Self::resolve_optional(all_settings, |settings| {
|
||||||
settings.lint.as_ref()?.preview
|
settings.lint.as_ref()?.preview
|
||||||
|
@ -425,6 +487,10 @@ impl Default for InitializationOptions {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use insta::assert_debug_snapshot;
|
use insta::assert_debug_snapshot;
|
||||||
|
use ruff_python_formatter::QuoteStyle;
|
||||||
|
use ruff_workspace::options::{
|
||||||
|
FormatOptions as RuffFormatOptions, LintCommonOptions, LintOptions,
|
||||||
|
};
|
||||||
use serde::de::DeserializeOwned;
|
use serde::de::DeserializeOwned;
|
||||||
|
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
|
@ -445,6 +511,9 @@ mod tests {
|
||||||
const EMPTY_MULTIPLE_WORKSPACE_INIT_OPTIONS_FIXTURE: &str =
|
const EMPTY_MULTIPLE_WORKSPACE_INIT_OPTIONS_FIXTURE: &str =
|
||||||
include_str!("../../resources/test/fixtures/settings/empty_multiple_workspace.json");
|
include_str!("../../resources/test/fixtures/settings/empty_multiple_workspace.json");
|
||||||
|
|
||||||
|
const INLINE_CONFIGURATION_FIXTURE: &str =
|
||||||
|
include_str!("../../resources/test/fixtures/settings/inline_configuration.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")
|
||||||
}
|
}
|
||||||
|
@ -855,4 +924,48 @@ mod tests {
|
||||||
all_settings.set_preview(true);
|
all_settings.set_preview(true);
|
||||||
assert_preview_all_settings(&all_settings, true);
|
assert_preview_all_settings(&all_settings, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn inline_configuration() {
|
||||||
|
let options: InitializationOptions = deserialize_fixture(INLINE_CONFIGURATION_FIXTURE);
|
||||||
|
|
||||||
|
let AllSettings {
|
||||||
|
global_settings,
|
||||||
|
workspace_settings: None,
|
||||||
|
} = AllSettings::from_init_options(options)
|
||||||
|
else {
|
||||||
|
panic!("Expected global settings only");
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
ResolvedClientSettings::global(&global_settings),
|
||||||
|
ResolvedClientSettings {
|
||||||
|
fix_all: true,
|
||||||
|
organize_imports: true,
|
||||||
|
lint_enable: true,
|
||||||
|
disable_rule_comment_enable: true,
|
||||||
|
fix_violation_enable: true,
|
||||||
|
show_syntax_errors: true,
|
||||||
|
editor_settings: ResolvedEditorSettings {
|
||||||
|
configuration: Some(ResolvedConfiguration::Inline(Options {
|
||||||
|
line_length: Some(LineLength::try_from(100).unwrap()),
|
||||||
|
lint: Some(LintOptions {
|
||||||
|
common: LintCommonOptions {
|
||||||
|
extend_select: Some(vec![RuleSelector::from_str("I001").unwrap()]),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
format: Some(RuffFormatOptions {
|
||||||
|
quote_style: Some(QuoteStyle::Single),
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
..Default::default()
|
||||||
|
})),
|
||||||
|
extend_select: Some(vec![RuleSelector::from_str("RUF001").unwrap()]),
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,10 +11,39 @@ as per the editor.
|
||||||
|
|
||||||
### `configuration`
|
### `configuration`
|
||||||
|
|
||||||
Path to a `ruff.toml` or `pyproject.toml` file to use for configuration.
|
The `configuration` setting allows you to configure editor-specific Ruff behavior. This can be done
|
||||||
|
in one of the following ways:
|
||||||
|
|
||||||
By default, Ruff will discover configuration for each project from the filesystem, mirroring the
|
1. **Configuration file path:** Specify the path to a `ruff.toml` or `pyproject.toml` file that
|
||||||
behavior of the Ruff CLI.
|
contains the configuration. User home directory and environment variables will be expanded.
|
||||||
|
1. **Inline JSON configuration:** Directly provide the configuration as a JSON object.
|
||||||
|
|
||||||
|
!!! note "Added in Ruff `0.9.8`"
|
||||||
|
|
||||||
|
The **Inline JSON configuration** option was introduced in Ruff `0.9.8`.
|
||||||
|
|
||||||
|
The default behavior, if `configuration` is unset, is to load the settings from the project's
|
||||||
|
configuration (a `ruff.toml` or `pyproject.toml` in the project's directory), consistent with when
|
||||||
|
running Ruff on the command-line.
|
||||||
|
|
||||||
|
The [`configurationPreference`](#configurationpreference) setting controls the precedence if both an
|
||||||
|
editor-provided configuration (`configuration`) and a project level configuration file are present.
|
||||||
|
|
||||||
|
#### Resolution order {: #configuration_resolution_order }
|
||||||
|
|
||||||
|
In an editor, Ruff supports three sources of configuration, prioritized as follows (from highest to
|
||||||
|
lowest):
|
||||||
|
|
||||||
|
1. **Specific settings:** Individual settings like [`lineLength`](#linelength) or
|
||||||
|
[`lint.select`](#select) defined in the editor
|
||||||
|
1. [**`ruff.configuration`**](#configuration): Settings provided via the
|
||||||
|
[`configuration`](#configuration) field (either a path to a configuration file or an inline
|
||||||
|
configuration object)
|
||||||
|
1. **Configuration file:** Settings defined in a `ruff.toml` or `pyproject.toml` file in the
|
||||||
|
project's directory (if present)
|
||||||
|
|
||||||
|
For example, if the line length is specified in all three sources, Ruff will use the value from the
|
||||||
|
[`lineLength`](#linelength) setting.
|
||||||
|
|
||||||
**Default value**: `null`
|
**Default value**: `null`
|
||||||
|
|
||||||
|
@ -22,6 +51,8 @@ behavior of the Ruff CLI.
|
||||||
|
|
||||||
**Example usage**:
|
**Example usage**:
|
||||||
|
|
||||||
|
_Using configuration file path:_
|
||||||
|
|
||||||
=== "VS Code"
|
=== "VS Code"
|
||||||
|
|
||||||
```json
|
```json
|
||||||
|
@ -35,11 +66,9 @@ behavior of the Ruff CLI.
|
||||||
```lua
|
```lua
|
||||||
require('lspconfig').ruff.setup {
|
require('lspconfig').ruff.setup {
|
||||||
init_options = {
|
init_options = {
|
||||||
settings = {
|
|
||||||
configuration = "~/path/to/ruff.toml"
|
configuration = "~/path/to/ruff.toml"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
=== "Zed"
|
=== "Zed"
|
||||||
|
@ -58,6 +87,87 @@ behavior of the Ruff CLI.
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
_Using inline configuration:_
|
||||||
|
|
||||||
|
=== "VS Code"
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"ruff.configuration": {
|
||||||
|
"lint": {
|
||||||
|
"unfixable": ["F401"],
|
||||||
|
"extend-select": ["TID251"],
|
||||||
|
"flake8-tidy-imports": {
|
||||||
|
"banned-api": {
|
||||||
|
"typing.TypedDict": {
|
||||||
|
"msg": "Use `typing_extensions.TypedDict` instead",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"format": {
|
||||||
|
"quote-style": "single"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
=== "Neovim"
|
||||||
|
|
||||||
|
```lua
|
||||||
|
require('lspconfig').ruff.setup {
|
||||||
|
init_options = {
|
||||||
|
configuration = {
|
||||||
|
lint = {
|
||||||
|
unfixable = {"F401"},
|
||||||
|
["extend-select"] = {"TID251"},
|
||||||
|
["flake8-tidy-imports"] = {
|
||||||
|
["banned-api"] = {
|
||||||
|
["typing.TypedDict"] = {
|
||||||
|
msg = "Use `typing_extensions.TypedDict` instead"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
format = {
|
||||||
|
["quote-style"] = "single"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
=== "Zed"
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"lsp": {
|
||||||
|
"ruff": {
|
||||||
|
"initialization_options": {
|
||||||
|
"settings": {
|
||||||
|
"configuration": {
|
||||||
|
"lint": {
|
||||||
|
"unfixable": ["F401"],
|
||||||
|
"extend-select": ["TID251"],
|
||||||
|
"flake8-tidy-imports": {
|
||||||
|
"banned-api": {
|
||||||
|
"typing.TypedDict": {
|
||||||
|
"msg": "Use `typing_extensions.TypedDict` instead"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"format": {
|
||||||
|
"quote-style": "single"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### `configurationPreference`
|
### `configurationPreference`
|
||||||
|
|
||||||
The strategy to use when resolving settings across VS Code and the filesystem. By default, editor
|
The strategy to use when resolving settings across VS Code and the filesystem. By default, editor
|
||||||
|
@ -594,7 +704,6 @@ Whether to enable linting. Set to `false` to use Ruff exclusively as a formatter
|
||||||
"initialization_options": {
|
"initialization_options": {
|
||||||
"settings": {
|
"settings": {
|
||||||
"lint": {
|
"lint": {
|
||||||
"enable" = {
|
|
||||||
"enable": false
|
"enable": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -602,7 +711,6 @@ Whether to enable linting. Set to `false` to use Ruff exclusively as a formatter
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### `preview` {: #lint_preview }
|
### `preview` {: #lint_preview }
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue