Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/ruff_server/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ ruff_db = { workspace = true }
ruff_diagnostics = { workspace = true }
ruff_formatter = { workspace = true }
ruff_linter = { workspace = true }
ruff_markdown = { workspace = true }
ruff_notebook = { workspace = true }
ruff_python_ast = { workspace = true }
ruff_python_codegen = { workspace = true }
Expand Down
2 changes: 2 additions & 0 deletions crates/ruff_server/src/edit/text_document.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,15 @@ pub struct TextDocument {
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum LanguageId {
Python,
Markdown,
Other,
}

impl From<&str> for LanguageId {
fn from(language_id: &str) -> Self {
match language_id {
"python" => Self::Python,
"markdown" => Self::Markdown,
_ => Self::Other,
}
}
Expand Down
5 changes: 4 additions & 1 deletion crates/ruff_server/src/fix.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::borrow::Cow;

use ruff_python_ast::SourceType;
use rustc_hash::FxHashMap;

use crate::{
Expand Down Expand Up @@ -53,7 +54,9 @@ pub(crate) fn fix_all(
None
};

let source_type = query.source_type();
let SourceType::Python(source_type) = query.source_type() else {
return Ok(Fixes::default());
};

// We need to iteratively apply all safe fixes onto a single file and then
// create a diff between the modified file and the original source to use as a single workspace
Expand Down
116 changes: 81 additions & 35 deletions crates/ruff_server/src/format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ use std::process::{Command, Stdio};
use anyhow::Context;

use ruff_formatter::{FormatOptions, PrintedRange};
use ruff_python_ast::PySourceType;
use ruff_markdown::{MarkdownResult, format_code_blocks};
use ruff_python_ast::{PySourceType, SourceType};
use ruff_python_formatter::{FormatModuleError, PyFormatOptions, format_module_source};
use ruff_source_file::LineIndex;
use ruff_text_size::TextRange;
Expand All @@ -30,7 +31,7 @@ pub(crate) enum FormatBackend {

pub(crate) fn format(
document: &TextDocument,
source_type: PySourceType,
source_type: SourceType,
formatter_settings: &FormatterSettings,
path: &Path,
backend: FormatBackend,
Expand All @@ -44,58 +45,103 @@ pub(crate) fn format(
/// Format using the built-in Ruff formatter.
fn format_internal(
document: &TextDocument,
source_type: PySourceType,
source_type: SourceType,
formatter_settings: &FormatterSettings,
path: &Path,
) -> crate::Result<Option<String>> {
let format_options =
formatter_settings.to_format_options(source_type, document.contents(), Some(path));
match format_module_source(document.contents(), format_options) {
Ok(formatted) => {
let formatted = formatted.into_code();
if formatted == document.contents() {
Ok(None)
} else {
Ok(Some(formatted))
match source_type {
SourceType::Python(py_source_type) => {
let format_options = formatter_settings.to_format_options(
py_source_type,
document.contents(),
Some(path),
);
match format_module_source(document.contents(), format_options) {
Ok(formatted) => {
let formatted = formatted.into_code();
if formatted == document.contents() {
Ok(None)
} else {
Ok(Some(formatted))
}
}
// Special case - syntax/parse errors are handled here instead of
// being propagated as visible server errors.
Err(FormatModuleError::ParseError(error)) => {
tracing::warn!("Unable to format document: {error}");
Ok(None)
}
Err(err) => Err(err.into()),
}
}
// Special case - syntax/parse errors are handled here instead of
// being propagated as visible server errors.
Err(FormatModuleError::ParseError(error)) => {
tracing::warn!("Unable to format document: {error}");
SourceType::Markdown => {
if !formatter_settings.preview.is_enabled() {
tracing::warn!("Markdown formatting is experimental, enable preview mode.");
return Ok(None);
}

match format_code_blocks(document.contents(), Some(path), formatter_settings) {
MarkdownResult::Formatted(formatted) => Ok(Some(formatted)),
MarkdownResult::Unchanged => Ok(None),
}
}
SourceType::Toml(_) => {
tracing::warn!("Formatting TOML files not supported");
Ok(None)
}
Err(err) => Err(err.into()),
}
}

/// Format using an external uv command.
fn format_external(
document: &TextDocument,
source_type: PySourceType,
source_type: SourceType,
formatter_settings: &FormatterSettings,
path: &Path,
) -> crate::Result<Option<String>> {
let format_options =
formatter_settings.to_format_options(source_type, document.contents(), Some(path));
let format_options = match source_type {
SourceType::Python(py_source_type) => {
formatter_settings.to_format_options(py_source_type, document.contents(), Some(path))
}
SourceType::Markdown => formatter_settings.to_format_options(
PySourceType::Python,
document.contents(),
Some(path),
),
SourceType::Toml(_) => {
tracing::warn!("Formatting TOML files not supported");
return Ok(None);
}
};
let uv_command = UvFormatCommand::from(format_options);
uv_command.format_document(document.contents(), path)
}

pub(crate) fn format_range(
document: &TextDocument,
source_type: PySourceType,
source_type: SourceType,
formatter_settings: &FormatterSettings,
range: TextRange,
path: &Path,
backend: FormatBackend,
) -> crate::Result<Option<PrintedRange>> {
let py_source_type = match source_type {
SourceType::Python(py_source_type) => py_source_type,
SourceType::Markdown => {
tracing::warn!("Range formatting for Markdown files not supported");
return Ok(None);
}
SourceType::Toml(_) => {
tracing::warn!("Formatting TOML files not supported");
return Ok(None);
}
};
match backend {
FormatBackend::Uv => {
format_range_external(document, source_type, formatter_settings, range, path)
format_range_external(document, py_source_type, formatter_settings, range, path)
}
FormatBackend::Internal => {
format_range_internal(document, source_type, formatter_settings, range, path)
format_range_internal(document, py_source_type, formatter_settings, range, path)
}
}
}
Expand Down Expand Up @@ -327,7 +373,7 @@ mod tests {

use insta::assert_snapshot;
use ruff_linter::settings::types::{CompiledPerFileTargetVersionList, PerFileTargetVersion};
use ruff_python_ast::{PySourceType, PythonVersion};
use ruff_python_ast::{PySourceType, PythonVersion, SourceType};
use ruff_text_size::{TextRange, TextSize};
use ruff_workspace::FormatterSettings;

Expand All @@ -349,7 +395,7 @@ with open("a_really_long_foo") as foo, open("a_really_long_bar") as bar, open("a
.unwrap();
let result = format(
&document,
PySourceType::Python,
SourceType::Python(PySourceType::Python),
&FormatterSettings {
unresolved_target_version: PythonVersion::PY38,
per_file_target_version,
Expand All @@ -373,7 +419,7 @@ with open("a_really_long_foo") as foo, open("a_really_long_bar") as bar, open("a
// same as above but without the per_file_target_version override
let result = format(
&document,
PySourceType::Python,
SourceType::Python(PySourceType::Python),
&FormatterSettings {
unresolved_target_version: PythonVersion::PY38,
..Default::default()
Expand Down Expand Up @@ -420,7 +466,7 @@ sys.exit(
.unwrap();
let result = format_range(
&document,
PySourceType::Python,
SourceType::Python(PySourceType::Python),
&FormatterSettings {
unresolved_target_version: PythonVersion::PY38,
per_file_target_version,
Expand All @@ -445,7 +491,7 @@ sys.exit(
// same as above but without the per_file_target_version override
let result = format_range(
&document,
PySourceType::Python,
SourceType::Python(PySourceType::Python),
&FormatterSettings {
unresolved_target_version: PythonVersion::PY38,
..Default::default()
Expand Down Expand Up @@ -488,7 +534,7 @@ def world( ):

let result = format(
&document,
PySourceType::Python,
SourceType::Python(PySourceType::Python),
&FormatterSettings::default(),
Path::new("test.py"),
FormatBackend::Uv,
Expand Down Expand Up @@ -529,7 +575,7 @@ def another_function(x,y,z):

let result = format_range(
&document,
PySourceType::Python,
SourceType::Python(PySourceType::Python),
&FormatterSettings::default(),
range,
Path::new("test.py"),
Expand Down Expand Up @@ -571,7 +617,7 @@ def hello(very_long_parameter_name_1, very_long_parameter_name_2, very_long_para

let result = format(
&document,
PySourceType::Python,
SourceType::Python(PySourceType::Python),
&formatter_settings,
Path::new("test.py"),
FormatBackend::Uv,
Expand Down Expand Up @@ -618,7 +664,7 @@ def hello():

let result = format(
&document,
PySourceType::Python,
SourceType::Python(PySourceType::Python),
&formatter_settings,
Path::new("test.py"),
FormatBackend::Uv,
Expand Down Expand Up @@ -650,7 +696,7 @@ def broken(:
// uv should return None for syntax errors (as indicated by the TODO comment)
let result = format(
&document,
PySourceType::Python,
SourceType::Python(PySourceType::Python),
&FormatterSettings::default(),
Path::new("test.py"),
FormatBackend::Uv,
Expand Down Expand Up @@ -684,7 +730,7 @@ line'''

let result = format(
&document,
PySourceType::Python,
SourceType::Python(PySourceType::Python),
&formatter_settings,
Path::new("test.py"),
FormatBackend::Uv,
Expand Down Expand Up @@ -726,7 +772,7 @@ bar = [1, 2, 3,]

let result = format(
&document,
PySourceType::Python,
SourceType::Python(PySourceType::Python),
&formatter_settings,
Path::new("test.py"),
FormatBackend::Uv,
Expand Down
5 changes: 4 additions & 1 deletion crates/ruff_server/src/lint.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! Access to the Ruff linting API for the LSP

use ruff_python_ast::SourceType;
use rustc_hash::FxHashMap;
use serde::{Deserialize, Serialize};

Expand Down Expand Up @@ -95,7 +96,9 @@ pub(crate) fn check(
None
};

let source_type = query.source_type();
let SourceType::Python(source_type) = query.source_type() else {
return DiagnosticsMap::default();
};

let target_version = settings.linter.resolve_target_version(&document_path);

Expand Down
5 changes: 5 additions & 0 deletions crates/ruff_server/src/resolve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,11 @@ fn is_document_excluded(
} else if let Some(LanguageId::Python) = language_id {
tracing::debug!("Included path via Python language ID: {}", path.display());
false
} else if let Some(LanguageId::Markdown) = language_id
&& formatter_settings.is_some()
{
tracing::debug!("Included path via Markdown language ID: {}", path.display());
false
Comment on lines +76 to +80
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you say why there's an additional check for whether formatter_settings.is_some() or not? Or is it because to check whether preview mode is enabled or not?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was primarily to include Markdown files when formatting, but exclude them when linting (where the linter settings would be provided and formatter settings would be None).

} else {
tracing::debug!(
"Ignored path as it's not in the inclusion set: {}",
Expand Down
8 changes: 5 additions & 3 deletions crates/ruff_server/src/session/index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -583,10 +583,12 @@ impl DocumentQuery {
}

/// Get the source type of the document associated with this query.
pub(crate) fn source_type(&self) -> ruff_python_ast::PySourceType {
pub(crate) fn source_type(&self) -> ruff_python_ast::SourceType {
match self {
Self::Text { .. } => ruff_python_ast::PySourceType::from(self.virtual_file_path()),
Self::Notebook { .. } => ruff_python_ast::PySourceType::Ipynb,
Self::Text { .. } => ruff_python_ast::SourceType::from(self.virtual_file_path()),
Self::Notebook { .. } => {
ruff_python_ast::SourceType::Python(ruff_python_ast::PySourceType::Ipynb)
}
}
}

Expand Down