Skip to content

Commit 9d16e46

Browse files
authored
Add most formatter options to ruff.toml / pyproject.toml (#7566)
1 parent 82978ac commit 9d16e46

File tree

24 files changed

+734
-145
lines changed

24 files changed

+734
-145
lines changed

Cargo.lock

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/ruff_cli/src/commands/format.rs

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ use ruff_linter::fs;
1515
use ruff_linter::logging::LogLevel;
1616
use ruff_linter::warn_user_once;
1717
use ruff_python_ast::{PySourceType, SourceType};
18-
use ruff_python_formatter::{format_module, FormatModuleError, PyFormatOptions};
19-
use ruff_source_file::{find_newline, LineEnding};
18+
use ruff_python_formatter::{format_module, FormatModuleError};
2019
use ruff_workspace::resolver::python_files_in_path;
20+
use ruff_workspace::FormatterSettings;
2121

2222
use crate::args::{CliOverrides, FormatArguments};
2323
use crate::panic::{catch_unwind, PanicError};
@@ -73,15 +73,17 @@ pub(crate) fn format(
7373
};
7474

7575
let resolved_settings = resolver.resolve(path, &pyproject_config);
76-
let options = resolved_settings.formatter.to_format_options(source_type);
77-
debug!("Formatting {} with {:?}", path.display(), options);
7876

79-
Some(match catch_unwind(|| format_path(path, options, mode)) {
80-
Ok(inner) => inner,
81-
Err(error) => {
82-
Err(FormatCommandError::Panic(Some(path.to_path_buf()), error))
83-
}
84-
})
77+
Some(
78+
match catch_unwind(|| {
79+
format_path(path, &resolved_settings.formatter, source_type, mode)
80+
}) {
81+
Ok(inner) => inner,
82+
Err(error) => {
83+
Err(FormatCommandError::Panic(Some(path.to_path_buf()), error))
84+
}
85+
},
86+
)
8587
}
8688
Err(err) => Some(Err(FormatCommandError::Ignore(err))),
8789
}
@@ -139,19 +141,15 @@ pub(crate) fn format(
139141
#[tracing::instrument(skip_all, fields(path = %path.display()))]
140142
fn format_path(
141143
path: &Path,
142-
options: PyFormatOptions,
144+
settings: &FormatterSettings,
145+
source_type: PySourceType,
143146
mode: FormatMode,
144147
) -> Result<FormatCommandResult, FormatCommandError> {
145148
let unformatted = std::fs::read_to_string(path)
146149
.map_err(|err| FormatCommandError::Read(Some(path.to_path_buf()), err))?;
147150

148-
let line_ending = match find_newline(&unformatted) {
149-
Some((_, LineEnding::Lf)) | None => ruff_formatter::printer::LineEnding::LineFeed,
150-
Some((_, LineEnding::Cr)) => ruff_formatter::printer::LineEnding::CarriageReturn,
151-
Some((_, LineEnding::CrLf)) => ruff_formatter::printer::LineEnding::CarriageReturnLineFeed,
152-
};
153-
154-
let options = options.with_line_ending(line_ending);
151+
let options = settings.to_format_options(source_type, &unformatted);
152+
debug!("Formatting {} with {:?}", path.display(), options);
155153

156154
let formatted = format_module(&unformatted, options)
157155
.map_err(|err| FormatCommandError::FormatModule(Some(path.to_path_buf()), err))?;

crates/ruff_cli/src/commands/format_stdin.rs

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@ use anyhow::Result;
55
use log::warn;
66

77
use ruff_python_ast::PySourceType;
8-
use ruff_python_formatter::{format_module, PyFormatOptions};
8+
use ruff_python_formatter::format_module;
99
use ruff_workspace::resolver::python_file_at_path;
10+
use ruff_workspace::FormatterSettings;
1011

1112
use crate::args::{CliOverrides, FormatArguments};
1213
use crate::commands::format::{FormatCommandError, FormatCommandResult, FormatMode};
@@ -37,12 +38,7 @@ pub(crate) fn format_stdin(cli: &FormatArguments, overrides: &CliOverrides) -> R
3738
// Format the file.
3839
let path = cli.stdin_filename.as_deref();
3940

40-
let options = pyproject_config
41-
.settings
42-
.formatter
43-
.to_format_options(path.map(PySourceType::from).unwrap_or_default());
44-
45-
match format_source(path, options, mode) {
41+
match format_source(path, &pyproject_config.settings.formatter, mode) {
4642
Ok(result) => match mode {
4743
FormatMode::Write => Ok(ExitStatus::Success),
4844
FormatMode::Check => {
@@ -63,11 +59,17 @@ pub(crate) fn format_stdin(cli: &FormatArguments, overrides: &CliOverrides) -> R
6359
/// Format source code read from `stdin`.
6460
fn format_source(
6561
path: Option<&Path>,
66-
options: PyFormatOptions,
62+
settings: &FormatterSettings,
6763
mode: FormatMode,
6864
) -> Result<FormatCommandResult, FormatCommandError> {
6965
let unformatted = read_from_stdin()
7066
.map_err(|err| FormatCommandError::Read(path.map(Path::to_path_buf), err))?;
67+
68+
let options = settings.to_format_options(
69+
path.map(PySourceType::from).unwrap_or_default(),
70+
&unformatted,
71+
);
72+
7173
let formatted = format_module(&unformatted, options)
7274
.map_err(|err| FormatCommandError::FormatModule(path.map(Path::to_path_buf), err))?;
7375
let formatted = formatted.as_code();

crates/ruff_cli/tests/format.rs

Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
#![cfg(not(target_family = "wasm"))]
2+
3+
use std::fs;
4+
use std::process::Command;
5+
use std::str;
6+
7+
use anyhow::Result;
8+
use insta_cmd::{assert_cmd_snapshot, get_cargo_bin};
9+
use tempfile::TempDir;
10+
11+
const BIN_NAME: &str = "ruff";
12+
13+
#[test]
14+
fn default_options() {
15+
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
16+
.args(["format", "--isolated"])
17+
.arg("-")
18+
.pass_stdin(r#"
19+
def foo(arg1, arg2,):
20+
print('Should\'t change quotes')
21+
22+
23+
if condition:
24+
25+
print('Hy "Micha"') # Should not change quotes
26+
27+
"#), @r###"
28+
success: true
29+
exit_code: 0
30+
----- stdout -----
31+
def foo(
32+
arg1,
33+
arg2,
34+
):
35+
print("Should't change quotes")
36+
37+
38+
if condition:
39+
print('Hy "Micha"') # Should not change quotes
40+
41+
----- stderr -----
42+
warning: `ruff format` is a work-in-progress, subject to change at any time, and intended only for experimentation.
43+
"###);
44+
}
45+
46+
#[test]
47+
fn format_options() -> Result<()> {
48+
let tempdir = TempDir::new()?;
49+
let ruff_toml = tempdir.path().join("ruff.toml");
50+
fs::write(
51+
&ruff_toml,
52+
r#"
53+
[format]
54+
indent-style = "tab"
55+
quote-style = "single"
56+
skip-magic-trailing-comma = true
57+
line-ending = "cr-lf"
58+
"#,
59+
)?;
60+
61+
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
62+
.args(["format", "--config"])
63+
.arg(&ruff_toml)
64+
.arg("-")
65+
.pass_stdin(r#"
66+
def foo(arg1, arg2,):
67+
print("Shouldn't change quotes")
68+
69+
70+
if condition:
71+
72+
print("Should change quotes")
73+
74+
"#), @r###"
75+
success: true
76+
exit_code: 0
77+
----- stdout -----
78+
def foo(arg1, arg2):
79+
print("Shouldn't change quotes")
80+
81+
82+
if condition:
83+
print('Should change quotes')
84+
85+
----- stderr -----
86+
warning: `ruff format` is a work-in-progress, subject to change at any time, and intended only for experimentation.
87+
"###);
88+
Ok(())
89+
}
90+
91+
#[test]
92+
fn format_option_inheritance() -> Result<()> {
93+
let tempdir = TempDir::new()?;
94+
let ruff_toml = tempdir.path().join("ruff.toml");
95+
let base_toml = tempdir.path().join("base.toml");
96+
fs::write(
97+
&ruff_toml,
98+
r#"
99+
extend = "base.toml"
100+
101+
[format]
102+
quote-style = "single"
103+
"#,
104+
)?;
105+
106+
fs::write(
107+
base_toml,
108+
r#"
109+
[format]
110+
indent-style = "tab"
111+
"#,
112+
)?;
113+
114+
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
115+
.args(["format", "--config"])
116+
.arg(&ruff_toml)
117+
.arg("-")
118+
.pass_stdin(r#"
119+
def foo(arg1, arg2,):
120+
print("Shouldn't change quotes")
121+
122+
123+
if condition:
124+
125+
print("Should change quotes")
126+
127+
"#), @r###"
128+
success: true
129+
exit_code: 0
130+
----- stdout -----
131+
def foo(
132+
arg1,
133+
arg2,
134+
):
135+
print("Shouldn't change quotes")
136+
137+
138+
if condition:
139+
print('Should change quotes')
140+
141+
----- stderr -----
142+
warning: `ruff format` is a work-in-progress, subject to change at any time, and intended only for experimentation.
143+
"###);
144+
Ok(())
145+
}
146+
147+
/// Tests that the legacy `format` option continues to work but emits a warning.
148+
#[test]
149+
fn legacy_format_option() -> Result<()> {
150+
let tempdir = TempDir::new()?;
151+
let ruff_toml = tempdir.path().join("ruff.toml");
152+
fs::write(
153+
&ruff_toml,
154+
r#"
155+
format = "json"
156+
"#,
157+
)?;
158+
159+
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
160+
.args(["check", "--select", "F401", "--no-cache", "--config"])
161+
.arg(&ruff_toml)
162+
.arg("-")
163+
.pass_stdin(r#"
164+
import os
165+
"#), @r###"
166+
success: false
167+
exit_code: 1
168+
----- stdout -----
169+
[
170+
{
171+
"code": "F401",
172+
"end_location": {
173+
"column": 10,
174+
"row": 2
175+
},
176+
"filename": "-",
177+
"fix": {
178+
"applicability": "Automatic",
179+
"edits": [
180+
{
181+
"content": "",
182+
"end_location": {
183+
"column": 1,
184+
"row": 3
185+
},
186+
"location": {
187+
"column": 1,
188+
"row": 2
189+
}
190+
}
191+
],
192+
"message": "Remove unused import: `os`"
193+
},
194+
"location": {
195+
"column": 8,
196+
"row": 2
197+
},
198+
"message": "`os` imported but unused",
199+
"noqa_row": 2,
200+
"url": "https://docs.astral.sh/ruff/rules/unused-import"
201+
}
202+
]
203+
----- stderr -----
204+
warning: The option `format` has been deprecated to avoid ambiguity with Ruff's upcoming formatter. Use `output-format` instead.
205+
"###);
206+
Ok(())
207+
}

crates/ruff_dev/src/format_dev.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -549,7 +549,6 @@ fn format_dir_entry(
549549

550550
let settings = resolver.resolve(&path, pyproject_config);
551551
// That's a bad way of doing this but it's not worth doing something better for format_dev
552-
// TODO(micha) use formatter settings instead
553552
if settings.formatter.line_width != LineWidth::default() {
554553
options = options.with_line_width(settings.formatter.line_width);
555554
}

crates/ruff_formatter/src/lib.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,11 @@ use ruff_macros::CacheKey;
5555
use ruff_text_size::{TextRange, TextSize};
5656

5757
#[derive(Debug, Eq, PartialEq, Clone, Copy, Hash, CacheKey)]
58-
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
58+
#[cfg_attr(
59+
feature = "serde",
60+
derive(serde::Serialize, serde::Deserialize),
61+
serde(rename_all = "kebab-case")
62+
)]
5963
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6064
#[derive(Default)]
6165
pub enum IndentStyle {

crates/ruff_formatter/src/printer/printer_options/mod.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
use crate::{FormatOptions, IndentStyle, IndentWidth, LineWidth};
2-
use ruff_macros::CacheKey;
32

43
/// Options that affect how the [`crate::Printer`] prints the format tokens
54
#[derive(Clone, Debug, Eq, PartialEq, Default)]
@@ -121,7 +120,7 @@ impl SourceMapGeneration {
121120
}
122121
}
123122

124-
#[derive(Copy, Clone, Debug, Eq, PartialEq, Default, CacheKey)]
123+
#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
125124
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
126125
pub enum LineEnding {
127126
/// Line Feed only (\n), common on Linux and macOS as well as inside git repos

crates/ruff_python_formatter/Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ memchr = { workspace = true }
3030
once_cell = { workspace = true }
3131
rustc-hash = { workspace = true }
3232
serde = { workspace = true, optional = true }
33+
schemars = { workspace = true, optional = true }
3334
smallvec = { workspace = true }
3435
static_assertions = { workspace = true }
3536
thiserror = { workspace = true }
@@ -52,4 +53,5 @@ required-features = ["serde"]
5253

5354
[features]
5455
serde = ["dep:serde", "ruff_formatter/serde", "ruff_source_file/serde", "ruff_python_ast/serde"]
55-
default = ["serde"]
56+
schemars = ["dep:schemars", "ruff_formatter/schemars"]
57+
default = []

0 commit comments

Comments
 (0)