mirror of
https://github.com/astral-sh/ruff.git
synced 2025-10-29 03:02:27 +00:00
This adds a new `backend: internal | uv` option to the LSP `FormatOptions` allowing users to perform document and range formatting operations though uv. The idea here is to prototype a solution for users to transition to a `uv format` command without encountering version mismatches (and consequently, formatting differences) between the LSP's version of `ruff` and uv's version of `ruff`. The primarily alternative to this would be to use uv to discover the `ruff` version used to start the LSP in the first place. However, this would increase the scope of a minimal `uv format` command beyond "run a formatter", and raise larger questions about how uv should be used to coordinate toolchain discovery. I think those are good things to explore, but I'm hesitant to let them block a `uv format` implementation. Another downside of using uv to discover `ruff`, is that it needs to be implemented _outside_ the LSP; e.g., we'd need to change the instructions on how to run the LSP and implement it in each editor integration, like the VS Code plugin. --------- Co-authored-by: Dhruv Manilawala <dhruvmanila@gmail.com>
173 lines
5.6 KiB
Rust
173 lines
5.6 KiB
Rust
use std::{path::PathBuf, sync::Arc};
|
|
|
|
use thiserror::Error;
|
|
|
|
use ruff_linter::RuleSelector;
|
|
use ruff_linter::line_width::LineLength;
|
|
use ruff_workspace::options::Options;
|
|
|
|
use crate::{
|
|
ClientOptions,
|
|
format::FormatBackend,
|
|
session::{
|
|
Client,
|
|
options::{ClientConfiguration, ConfigurationPreference},
|
|
},
|
|
};
|
|
|
|
pub struct GlobalClientSettings {
|
|
pub(super) options: ClientOptions,
|
|
|
|
/// Lazily initialized client settings to avoid showing error warnings
|
|
/// when a field of the global settings has any errors but the field is overridden
|
|
/// in the workspace settings. This can avoid showing unnecessary errors
|
|
/// when the workspace settings e.g. select some rules that aren't available in a specific workspace
|
|
/// and said workspace overrides the selected rules.
|
|
pub(super) settings: std::cell::OnceCell<Arc<ClientSettings>>,
|
|
|
|
pub(super) client: Client,
|
|
}
|
|
|
|
impl GlobalClientSettings {
|
|
pub(super) fn options(&self) -> &ClientOptions {
|
|
&self.options
|
|
}
|
|
|
|
fn settings_impl(&self) -> &Arc<ClientSettings> {
|
|
self.settings.get_or_init(|| {
|
|
let settings = self.options.clone().into_settings();
|
|
let settings = match settings {
|
|
Ok(settings) => settings,
|
|
Err(settings) => {
|
|
self.client.show_error_message(
|
|
"Ruff received invalid settings from the editor. Refer to the logs for more information."
|
|
);
|
|
settings
|
|
}
|
|
};
|
|
Arc::new(settings)
|
|
})
|
|
}
|
|
|
|
/// Lazily resolves the client options to the settings.
|
|
pub(super) fn to_settings(&self) -> &ClientSettings {
|
|
self.settings_impl()
|
|
}
|
|
|
|
/// Lazily resolves the client options to the settings.
|
|
pub(super) fn to_settings_arc(&self) -> Arc<ClientSettings> {
|
|
self.settings_impl().clone()
|
|
}
|
|
}
|
|
|
|
/// Resolved client settings for a specific document. These settings are meant to be
|
|
/// used directly by the server, and are *not* a 1:1 representation with how the client
|
|
/// sends them.
|
|
#[derive(Clone, Debug)]
|
|
#[cfg_attr(test, derive(PartialEq, Eq))]
|
|
#[expect(clippy::struct_excessive_bools)]
|
|
pub(crate) struct ClientSettings {
|
|
pub(super) fix_all: bool,
|
|
pub(super) organize_imports: bool,
|
|
pub(super) lint_enable: bool,
|
|
pub(super) disable_rule_comment_enable: bool,
|
|
pub(super) fix_violation_enable: bool,
|
|
pub(super) show_syntax_errors: bool,
|
|
pub(super) editor_settings: EditorSettings,
|
|
}
|
|
|
|
/// 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(Clone, Debug)]
|
|
#[cfg_attr(test, derive(Default, PartialEq, Eq))]
|
|
pub(crate) struct EditorSettings {
|
|
pub(super) configuration: Option<ResolvedConfiguration>,
|
|
pub(super) lint_preview: Option<bool>,
|
|
pub(super) format_preview: Option<bool>,
|
|
pub(super) format_backend: Option<FormatBackend>,
|
|
pub(super) select: Option<Vec<RuleSelector>>,
|
|
pub(super) extend_select: Option<Vec<RuleSelector>>,
|
|
pub(super) ignore: Option<Vec<RuleSelector>>,
|
|
pub(super) exclude: Option<Vec<String>>,
|
|
pub(super) line_length: Option<LineLength>,
|
|
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(Box<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(Box::new(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,
|
|
}
|
|
|
|
impl ClientSettings {
|
|
pub(crate) fn fix_all(&self) -> bool {
|
|
self.fix_all
|
|
}
|
|
|
|
pub(crate) fn organize_imports(&self) -> bool {
|
|
self.organize_imports
|
|
}
|
|
|
|
pub(crate) fn lint(&self) -> bool {
|
|
self.lint_enable
|
|
}
|
|
|
|
pub(crate) fn noqa_comments(&self) -> bool {
|
|
self.disable_rule_comment_enable
|
|
}
|
|
|
|
pub(crate) fn fix_violation(&self) -> bool {
|
|
self.fix_violation_enable
|
|
}
|
|
|
|
pub(crate) fn show_syntax_errors(&self) -> bool {
|
|
self.show_syntax_errors
|
|
}
|
|
|
|
pub(crate) fn editor_settings(&self) -> &EditorSettings {
|
|
&self.editor_settings
|
|
}
|
|
}
|
|
|
|
impl EditorSettings {
|
|
pub(crate) fn format_backend(&self) -> FormatBackend {
|
|
self.format_backend.unwrap_or_default()
|
|
}
|
|
}
|