mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-04 10:49:50 +00:00
config: add new docstring-code-format
knob (#8854)
This PR does the plumbing to make a new formatting option,
`docstring-code-format`, available in the configuration for end users.
It is disabled by default (opt-in). It is opt-in at least initially to
reflect a conservative posture. The intent is to make it opt-out at some
point in the future.
This was split out from #8811 in order to make #8811 easier to merge.
Namely, once this is merged, docstring code snippet formatting will
become available to end users. (See comments below for how we arrived at
the name.)
Closes #7146
## Test Plan
Other than the standard test suite, I ran the formatter over the CPython
and polars projects to ensure both that the result looked sensible and
that tests still passed. At time of writing, one issue that currently
appears is that reformatting code snippets trips the long line lint:
1905886802
This commit is contained in:
parent
18452cf477
commit
b6fb972e6f
21 changed files with 505 additions and 55 deletions
|
@ -139,6 +139,99 @@ if condition:
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn docstring_options() -> Result<()> {
|
||||
let tempdir = TempDir::new()?;
|
||||
let ruff_toml = tempdir.path().join("ruff.toml");
|
||||
fs::write(
|
||||
&ruff_toml,
|
||||
r#"
|
||||
[format]
|
||||
docstring-code-format = true
|
||||
docstring-code-line-length = 20
|
||||
"#,
|
||||
)?;
|
||||
|
||||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||
.args(["format", "--config"])
|
||||
.arg(&ruff_toml)
|
||||
.arg("-")
|
||||
.pass_stdin(r#"
|
||||
def f(x):
|
||||
'''
|
||||
Something about `f`. And an example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
foo, bar, quux = this_is_a_long_line(lion, hippo, lemur, bear)
|
||||
|
||||
Another example:
|
||||
|
||||
```py
|
||||
foo, bar, quux = this_is_a_long_line(lion, hippo, lemur, bear)
|
||||
```
|
||||
|
||||
And another:
|
||||
|
||||
>>> foo, bar, quux = this_is_a_long_line(lion, hippo, lemur, bear)
|
||||
'''
|
||||
pass
|
||||
"#), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
def f(x):
|
||||
"""
|
||||
Something about `f`. And an example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
(
|
||||
foo,
|
||||
bar,
|
||||
quux,
|
||||
) = this_is_a_long_line(
|
||||
lion,
|
||||
hippo,
|
||||
lemur,
|
||||
bear,
|
||||
)
|
||||
|
||||
Another example:
|
||||
|
||||
```py
|
||||
(
|
||||
foo,
|
||||
bar,
|
||||
quux,
|
||||
) = this_is_a_long_line(
|
||||
lion,
|
||||
hippo,
|
||||
lemur,
|
||||
bear,
|
||||
)
|
||||
```
|
||||
|
||||
And another:
|
||||
|
||||
>>> (
|
||||
... foo,
|
||||
... bar,
|
||||
... quux,
|
||||
... ) = this_is_a_long_line(
|
||||
... lion,
|
||||
... hippo,
|
||||
... lemur,
|
||||
... bear,
|
||||
... )
|
||||
"""
|
||||
pass
|
||||
|
||||
----- stderr -----
|
||||
"###);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mixed_line_endings() -> Result<()> {
|
||||
let tempdir = TempDir::new()?;
|
||||
|
|
|
@ -175,6 +175,12 @@ impl PyFormatOptions {
|
|||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_docstring_code_line_width(mut self, line_width: DocstringCodeLineWidth) -> Self {
|
||||
self.docstring_code_line_width = line_width;
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_preview(mut self, preview: PreviewMode) -> Self {
|
||||
self.preview = preview;
|
||||
|
@ -302,13 +308,14 @@ impl DocstringCode {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, CacheKey)]
|
||||
#[derive(Copy, Clone, Default, Eq, PartialEq, CacheKey)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))]
|
||||
#[cfg_attr(feature = "serde", serde(untagged))]
|
||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||
pub enum DocstringCodeLineWidth {
|
||||
Fixed(LineWidth),
|
||||
#[default]
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
serde(deserialize_with = "deserialize_docstring_code_line_width_dynamic")
|
||||
|
@ -316,12 +323,6 @@ pub enum DocstringCodeLineWidth {
|
|||
Dynamic,
|
||||
}
|
||||
|
||||
impl Default for DocstringCodeLineWidth {
|
||||
fn default() -> DocstringCodeLineWidth {
|
||||
DocstringCodeLineWidth::Fixed(default_line_width())
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for DocstringCodeLineWidth {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
match *self {
|
||||
|
|
|
@ -173,7 +173,7 @@ quote-style = Double
|
|||
line-ending = LineFeed
|
||||
magic-trailing-comma = Respect
|
||||
docstring-code = Disabled
|
||||
docstring-code-line-width = 88
|
||||
docstring-code-line-width = "dynamic"
|
||||
preview = Disabled
|
||||
```
|
||||
|
||||
|
@ -347,7 +347,7 @@ quote-style = Double
|
|||
line-ending = LineFeed
|
||||
magic-trailing-comma = Respect
|
||||
docstring-code = Disabled
|
||||
docstring-code-line-width = 88
|
||||
docstring-code-line-width = "dynamic"
|
||||
preview = Disabled
|
||||
```
|
||||
|
||||
|
@ -521,7 +521,7 @@ quote-style = Double
|
|||
line-ending = LineFeed
|
||||
magic-trailing-comma = Respect
|
||||
docstring-code = Disabled
|
||||
docstring-code-line-width = 88
|
||||
docstring-code-line-width = "dynamic"
|
||||
preview = Disabled
|
||||
```
|
||||
|
||||
|
@ -695,7 +695,7 @@ quote-style = Double
|
|||
line-ending = LineFeed
|
||||
magic-trailing-comma = Respect
|
||||
docstring-code = Disabled
|
||||
docstring-code-line-width = 88
|
||||
docstring-code-line-width = "dynamic"
|
||||
preview = Disabled
|
||||
```
|
||||
|
||||
|
@ -869,7 +869,7 @@ quote-style = Single
|
|||
line-ending = LineFeed
|
||||
magic-trailing-comma = Respect
|
||||
docstring-code = Disabled
|
||||
docstring-code-line-width = 88
|
||||
docstring-code-line-width = "dynamic"
|
||||
preview = Disabled
|
||||
```
|
||||
|
||||
|
|
|
@ -1366,7 +1366,7 @@ quote-style = Double
|
|||
line-ending = LineFeed
|
||||
magic-trailing-comma = Respect
|
||||
docstring-code = Disabled
|
||||
docstring-code-line-width = 88
|
||||
docstring-code-line-width = "dynamic"
|
||||
preview = Disabled
|
||||
```
|
||||
|
||||
|
@ -2736,7 +2736,7 @@ quote-style = Double
|
|||
line-ending = LineFeed
|
||||
magic-trailing-comma = Respect
|
||||
docstring-code = Disabled
|
||||
docstring-code-line-width = 88
|
||||
docstring-code-line-width = "dynamic"
|
||||
preview = Disabled
|
||||
```
|
||||
|
||||
|
@ -4106,7 +4106,7 @@ quote-style = Double
|
|||
line-ending = LineFeed
|
||||
magic-trailing-comma = Respect
|
||||
docstring-code = Disabled
|
||||
docstring-code-line-width = 88
|
||||
docstring-code-line-width = "dynamic"
|
||||
preview = Disabled
|
||||
```
|
||||
|
||||
|
@ -5476,7 +5476,7 @@ quote-style = Double
|
|||
line-ending = LineFeed
|
||||
magic-trailing-comma = Respect
|
||||
docstring-code = Disabled
|
||||
docstring-code-line-width = 88
|
||||
docstring-code-line-width = "dynamic"
|
||||
preview = Disabled
|
||||
```
|
||||
|
||||
|
@ -6846,7 +6846,7 @@ quote-style = Double
|
|||
line-ending = LineFeed
|
||||
magic-trailing-comma = Respect
|
||||
docstring-code = Enabled
|
||||
docstring-code-line-width = 88
|
||||
docstring-code-line-width = "dynamic"
|
||||
preview = Disabled
|
||||
```
|
||||
|
||||
|
@ -7090,7 +7090,9 @@ def doctest_long_lines():
|
|||
This won't get wrapped even though it exceeds our configured
|
||||
line width because it doesn't exceed the line width within this
|
||||
docstring. e.g, the `f` in `foo` is treated as the first column.
|
||||
>>> foo, bar, quux = this_is_a_long_line(lion, giraffe, hippo, zeba, lemur, penguin, monkey)
|
||||
>>> foo, bar, quux = this_is_a_long_line(
|
||||
... lion, giraffe, hippo, zeba, lemur, penguin, monkey
|
||||
... )
|
||||
|
||||
But this one is long enough to get wrapped.
|
||||
>>> foo, bar, quux = this_is_a_long_line(
|
||||
|
@ -8211,7 +8213,7 @@ quote-style = Double
|
|||
line-ending = LineFeed
|
||||
magic-trailing-comma = Respect
|
||||
docstring-code = Enabled
|
||||
docstring-code-line-width = 88
|
||||
docstring-code-line-width = "dynamic"
|
||||
preview = Disabled
|
||||
```
|
||||
|
||||
|
@ -8455,7 +8457,9 @@ def doctest_long_lines():
|
|||
This won't get wrapped even though it exceeds our configured
|
||||
line width because it doesn't exceed the line width within this
|
||||
docstring. e.g, the `f` in `foo` is treated as the first column.
|
||||
>>> foo, bar, quux = this_is_a_long_line(lion, giraffe, hippo, zeba, lemur, penguin, monkey)
|
||||
>>> foo, bar, quux = this_is_a_long_line(
|
||||
... lion, giraffe, hippo, zeba, lemur, penguin, monkey
|
||||
... )
|
||||
|
||||
But this one is long enough to get wrapped.
|
||||
>>> foo, bar, quux = this_is_a_long_line(
|
||||
|
@ -9576,7 +9580,7 @@ quote-style = Double
|
|||
line-ending = LineFeed
|
||||
magic-trailing-comma = Respect
|
||||
docstring-code = Enabled
|
||||
docstring-code-line-width = 88
|
||||
docstring-code-line-width = "dynamic"
|
||||
preview = Disabled
|
||||
```
|
||||
|
||||
|
@ -9820,11 +9824,22 @@ def doctest_long_lines():
|
|||
This won't get wrapped even though it exceeds our configured
|
||||
line width because it doesn't exceed the line width within this
|
||||
docstring. e.g, the `f` in `foo` is treated as the first column.
|
||||
>>> foo, bar, quux = this_is_a_long_line(lion, giraffe, hippo, zeba, lemur, penguin, monkey)
|
||||
>>> foo, bar, quux = this_is_a_long_line(
|
||||
... lion, giraffe, hippo, zeba, lemur, penguin, monkey
|
||||
... )
|
||||
|
||||
But this one is long enough to get wrapped.
|
||||
>>> foo, bar, quux = this_is_a_long_line(
|
||||
... lion, giraffe, hippo, zeba, lemur, penguin, monkey, spider, bear, leopard
|
||||
... lion,
|
||||
... giraffe,
|
||||
... hippo,
|
||||
... zeba,
|
||||
... lemur,
|
||||
... penguin,
|
||||
... monkey,
|
||||
... spider,
|
||||
... bear,
|
||||
... leopard,
|
||||
... )
|
||||
"""
|
||||
# This demostrates a normal line that will get wrapped but won't
|
||||
|
@ -10941,7 +10956,7 @@ quote-style = Double
|
|||
line-ending = LineFeed
|
||||
magic-trailing-comma = Respect
|
||||
docstring-code = Enabled
|
||||
docstring-code-line-width = 88
|
||||
docstring-code-line-width = "dynamic"
|
||||
preview = Disabled
|
||||
```
|
||||
|
||||
|
@ -11185,7 +11200,9 @@ def doctest_long_lines():
|
|||
This won't get wrapped even though it exceeds our configured
|
||||
line width because it doesn't exceed the line width within this
|
||||
docstring. e.g, the `f` in `foo` is treated as the first column.
|
||||
>>> foo, bar, quux = this_is_a_long_line(lion, giraffe, hippo, zeba, lemur, penguin, monkey)
|
||||
>>> foo, bar, quux = this_is_a_long_line(
|
||||
... lion, giraffe, hippo, zeba, lemur, penguin, monkey
|
||||
... )
|
||||
|
||||
But this one is long enough to get wrapped.
|
||||
>>> foo, bar, quux = this_is_a_long_line(
|
||||
|
|
|
@ -25,7 +25,7 @@ quote-style = Double
|
|||
line-ending = CarriageReturnLineFeed
|
||||
magic-trailing-comma = Respect
|
||||
docstring-code = Enabled
|
||||
docstring-code-line-width = 88
|
||||
docstring-code-line-width = "dynamic"
|
||||
preview = Disabled
|
||||
```
|
||||
|
||||
|
|
|
@ -136,7 +136,7 @@ quote-style = Double
|
|||
line-ending = LineFeed
|
||||
magic-trailing-comma = Respect
|
||||
docstring-code = Disabled
|
||||
docstring-code-line-width = 88
|
||||
docstring-code-line-width = "dynamic"
|
||||
preview = Disabled
|
||||
```
|
||||
|
||||
|
@ -288,7 +288,7 @@ quote-style = Single
|
|||
line-ending = LineFeed
|
||||
magic-trailing-comma = Respect
|
||||
docstring-code = Disabled
|
||||
docstring-code-line-width = 88
|
||||
docstring-code-line-width = "dynamic"
|
||||
preview = Disabled
|
||||
```
|
||||
|
||||
|
|
|
@ -151,7 +151,7 @@ quote-style = Double
|
|||
line-ending = LineFeed
|
||||
magic-trailing-comma = Respect
|
||||
docstring-code = Disabled
|
||||
docstring-code-line-width = 88
|
||||
docstring-code-line-width = "dynamic"
|
||||
preview = Disabled
|
||||
```
|
||||
|
||||
|
@ -327,7 +327,7 @@ quote-style = Single
|
|||
line-ending = LineFeed
|
||||
magic-trailing-comma = Respect
|
||||
docstring-code = Disabled
|
||||
docstring-code-line-width = 88
|
||||
docstring-code-line-width = "dynamic"
|
||||
preview = Disabled
|
||||
```
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ quote-style = Double
|
|||
line-ending = LineFeed
|
||||
magic-trailing-comma = Respect
|
||||
docstring-code = Disabled
|
||||
docstring-code-line-width = 88
|
||||
docstring-code-line-width = "dynamic"
|
||||
preview = Disabled
|
||||
```
|
||||
|
||||
|
@ -71,7 +71,7 @@ quote-style = Double
|
|||
line-ending = LineFeed
|
||||
magic-trailing-comma = Respect
|
||||
docstring-code = Disabled
|
||||
docstring-code-line-width = 88
|
||||
docstring-code-line-width = "dynamic"
|
||||
preview = Disabled
|
||||
```
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ quote-style = Double
|
|||
line-ending = LineFeed
|
||||
magic-trailing-comma = Respect
|
||||
docstring-code = Disabled
|
||||
docstring-code-line-width = 88
|
||||
docstring-code-line-width = "dynamic"
|
||||
preview = Disabled
|
||||
```
|
||||
|
||||
|
@ -33,7 +33,7 @@ quote-style = Double
|
|||
line-ending = LineFeed
|
||||
magic-trailing-comma = Respect
|
||||
docstring-code = Disabled
|
||||
docstring-code-line-width = 88
|
||||
docstring-code-line-width = "dynamic"
|
||||
preview = Disabled
|
||||
```
|
||||
|
||||
|
@ -50,7 +50,7 @@ quote-style = Double
|
|||
line-ending = LineFeed
|
||||
magic-trailing-comma = Respect
|
||||
docstring-code = Disabled
|
||||
docstring-code-line-width = 88
|
||||
docstring-code-line-width = "dynamic"
|
||||
preview = Disabled
|
||||
```
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ quote-style = Double
|
|||
line-ending = LineFeed
|
||||
magic-trailing-comma = Respect
|
||||
docstring-code = Disabled
|
||||
docstring-code-line-width = 88
|
||||
docstring-code-line-width = "dynamic"
|
||||
preview = Disabled
|
||||
```
|
||||
|
||||
|
@ -64,7 +64,7 @@ quote-style = Double
|
|||
line-ending = LineFeed
|
||||
magic-trailing-comma = Respect
|
||||
docstring-code = Disabled
|
||||
docstring-code-line-width = 88
|
||||
docstring-code-line-width = "dynamic"
|
||||
preview = Disabled
|
||||
```
|
||||
|
||||
|
@ -97,7 +97,7 @@ quote-style = Double
|
|||
line-ending = LineFeed
|
||||
magic-trailing-comma = Respect
|
||||
docstring-code = Disabled
|
||||
docstring-code-line-width = 88
|
||||
docstring-code-line-width = "dynamic"
|
||||
preview = Disabled
|
||||
```
|
||||
|
||||
|
|
|
@ -82,7 +82,7 @@ quote-style = Double
|
|||
line-ending = LineFeed
|
||||
magic-trailing-comma = Respect
|
||||
docstring-code = Disabled
|
||||
docstring-code-line-width = 88
|
||||
docstring-code-line-width = "dynamic"
|
||||
preview = Disabled
|
||||
```
|
||||
|
||||
|
@ -164,7 +164,7 @@ quote-style = Double
|
|||
line-ending = LineFeed
|
||||
magic-trailing-comma = Respect
|
||||
docstring-code = Disabled
|
||||
docstring-code-line-width = 88
|
||||
docstring-code-line-width = "dynamic"
|
||||
preview = Enabled
|
||||
```
|
||||
|
||||
|
|
|
@ -66,7 +66,7 @@ quote-style = Single
|
|||
line-ending = LineFeed
|
||||
magic-trailing-comma = Respect
|
||||
docstring-code = Disabled
|
||||
docstring-code-line-width = 88
|
||||
docstring-code-line-width = "dynamic"
|
||||
preview = Disabled
|
||||
```
|
||||
|
||||
|
@ -137,7 +137,7 @@ quote-style = Double
|
|||
line-ending = LineFeed
|
||||
magic-trailing-comma = Respect
|
||||
docstring-code = Disabled
|
||||
docstring-code-line-width = 88
|
||||
docstring-code-line-width = "dynamic"
|
||||
preview = Disabled
|
||||
```
|
||||
|
||||
|
@ -208,7 +208,7 @@ quote-style = Preserve
|
|||
line-ending = LineFeed
|
||||
magic-trailing-comma = Respect
|
||||
docstring-code = Disabled
|
||||
docstring-code-line-width = 88
|
||||
docstring-code-line-width = "dynamic"
|
||||
preview = Disabled
|
||||
```
|
||||
|
||||
|
|
|
@ -49,7 +49,7 @@ quote-style = Double
|
|||
line-ending = LineFeed
|
||||
magic-trailing-comma = Respect
|
||||
docstring-code = Disabled
|
||||
docstring-code-line-width = 88
|
||||
docstring-code-line-width = "dynamic"
|
||||
preview = Disabled
|
||||
```
|
||||
|
||||
|
@ -105,7 +105,7 @@ quote-style = Double
|
|||
line-ending = LineFeed
|
||||
magic-trailing-comma = Ignore
|
||||
docstring-code = Disabled
|
||||
docstring-code-line-width = 88
|
||||
docstring-code-line-width = "dynamic"
|
||||
preview = Disabled
|
||||
```
|
||||
|
||||
|
|
|
@ -234,7 +234,7 @@ quote-style = Double
|
|||
line-ending = LineFeed
|
||||
magic-trailing-comma = Respect
|
||||
docstring-code = Disabled
|
||||
docstring-code-line-width = 88
|
||||
docstring-code-line-width = "dynamic"
|
||||
preview = Enabled
|
||||
```
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ quote-style = Double
|
|||
line-ending = LineFeed
|
||||
magic-trailing-comma = Respect
|
||||
docstring-code = Disabled
|
||||
docstring-code-line-width = 88
|
||||
docstring-code-line-width = "dynamic"
|
||||
preview = Disabled
|
||||
```
|
||||
|
||||
|
@ -49,7 +49,7 @@ quote-style = Double
|
|||
line-ending = LineFeed
|
||||
magic-trailing-comma = Respect
|
||||
docstring-code = Disabled
|
||||
docstring-code-line-width = 88
|
||||
docstring-code-line-width = "dynamic"
|
||||
preview = Disabled
|
||||
```
|
||||
|
||||
|
@ -77,7 +77,7 @@ quote-style = Double
|
|||
line-ending = LineFeed
|
||||
magic-trailing-comma = Respect
|
||||
docstring-code = Disabled
|
||||
docstring-code-line-width = 88
|
||||
docstring-code-line-width = "dynamic"
|
||||
preview = Disabled
|
||||
```
|
||||
|
||||
|
|
|
@ -34,7 +34,9 @@ use ruff_linter::settings::{
|
|||
use ruff_linter::{
|
||||
fs, warn_user, warn_user_once, warn_user_once_by_id, RuleSelector, RUFF_PKG_VERSION,
|
||||
};
|
||||
use ruff_python_formatter::{MagicTrailingComma, QuoteStyle};
|
||||
use ruff_python_formatter::{
|
||||
DocstringCode, DocstringCodeLineWidth, MagicTrailingComma, QuoteStyle,
|
||||
};
|
||||
|
||||
use crate::options::{
|
||||
Flake8AnnotationsOptions, Flake8BanditOptions, Flake8BugbearOptions, Flake8BuiltinsOptions,
|
||||
|
@ -189,6 +191,12 @@ impl Configuration {
|
|||
magic_trailing_comma: format
|
||||
.magic_trailing_comma
|
||||
.unwrap_or(format_defaults.magic_trailing_comma),
|
||||
docstring_code_format: format
|
||||
.docstring_code_format
|
||||
.unwrap_or(format_defaults.docstring_code_format),
|
||||
docstring_code_line_width: format
|
||||
.docstring_code_line_width
|
||||
.unwrap_or(format_defaults.docstring_code_line_width),
|
||||
};
|
||||
|
||||
let lint = self.lint;
|
||||
|
@ -1020,6 +1028,8 @@ pub struct FormatConfiguration {
|
|||
pub quote_style: Option<QuoteStyle>,
|
||||
pub magic_trailing_comma: Option<MagicTrailingComma>,
|
||||
pub line_ending: Option<LineEnding>,
|
||||
pub docstring_code_format: Option<DocstringCode>,
|
||||
pub docstring_code_line_width: Option<DocstringCodeLineWidth>,
|
||||
}
|
||||
|
||||
impl FormatConfiguration {
|
||||
|
@ -1046,6 +1056,14 @@ impl FormatConfiguration {
|
|||
}
|
||||
}),
|
||||
line_ending: options.line_ending,
|
||||
docstring_code_format: options.docstring_code_format.map(|yes| {
|
||||
if yes {
|
||||
DocstringCode::Enabled
|
||||
} else {
|
||||
DocstringCode::Disabled
|
||||
}
|
||||
}),
|
||||
docstring_code_line_width: options.docstring_code_line_length,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -1059,6 +1077,10 @@ impl FormatConfiguration {
|
|||
quote_style: self.quote_style.or(other.quote_style),
|
||||
magic_trailing_comma: self.magic_trailing_comma.or(other.magic_trailing_comma),
|
||||
line_ending: self.line_ending.or(other.line_ending),
|
||||
docstring_code_format: self.docstring_code_format.or(other.docstring_code_format),
|
||||
docstring_code_line_width: self
|
||||
.docstring_code_line_width
|
||||
.or(other.docstring_code_line_width),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ use ruff_linter::settings::types::{
|
|||
};
|
||||
use ruff_linter::{warn_user_once, RuleSelector};
|
||||
use ruff_macros::{CombineOptions, OptionsMetadata};
|
||||
use ruff_python_formatter::QuoteStyle;
|
||||
use ruff_python_formatter::{DocstringCodeLineWidth, QuoteStyle};
|
||||
|
||||
use crate::settings::LineEnding;
|
||||
|
||||
|
@ -2948,6 +2948,156 @@ pub struct FormatOptions {
|
|||
"#
|
||||
)]
|
||||
pub line_ending: Option<LineEnding>,
|
||||
|
||||
/// Whether to format code snippets in docstrings.
|
||||
///
|
||||
/// When this is enabled, Python code examples within docstrings are
|
||||
/// automatically reformatted.
|
||||
///
|
||||
/// For example, when this is enabled, the following code:
|
||||
///
|
||||
/// ```python
|
||||
/// def f(x):
|
||||
/// """
|
||||
/// Something about `f`. And an example in doctest format:
|
||||
///
|
||||
/// >>> f( x )
|
||||
///
|
||||
/// Markdown is also supported:
|
||||
///
|
||||
/// ```py
|
||||
/// f( x )
|
||||
/// ```
|
||||
///
|
||||
/// As are reStructuredText literal blocks::
|
||||
///
|
||||
/// f( x )
|
||||
///
|
||||
///
|
||||
/// And reStructuredText code blocks:
|
||||
///
|
||||
/// .. code-block:: python
|
||||
///
|
||||
/// f( x )
|
||||
/// """
|
||||
/// pass
|
||||
/// ```
|
||||
///
|
||||
/// ... will be reformatted (assuming the rest of the options are set to
|
||||
/// their defaults) as:
|
||||
///
|
||||
/// ```python
|
||||
/// def f(x):
|
||||
/// """
|
||||
/// Something about `f`. And an example in doctest format:
|
||||
///
|
||||
/// >>> f(x)
|
||||
///
|
||||
/// Markdown is also supported:
|
||||
///
|
||||
/// ```py
|
||||
/// f(x)
|
||||
/// ```
|
||||
///
|
||||
/// As are reStructuredText literal blocks::
|
||||
///
|
||||
/// f(x)
|
||||
///
|
||||
///
|
||||
/// And reStructuredText code blocks:
|
||||
///
|
||||
/// .. code-block:: python
|
||||
///
|
||||
/// f(x)
|
||||
/// """
|
||||
/// pass
|
||||
/// ```
|
||||
///
|
||||
/// If a code snippt in a docstring contains invalid Python code or if the
|
||||
/// formatter would otherwise write invalid Python code, then the code
|
||||
/// example is ignored by the formatter and kept as-is.
|
||||
///
|
||||
/// Currently, doctest, Markdown, reStructuredText literal blocks, and
|
||||
/// reStructuredText code blocks are all supported and automatically
|
||||
/// recognized. In the case of unlabeled fenced code blocks in Markdown and
|
||||
/// reStructuredText literal blocks, the contents are assumed to be Python
|
||||
/// and reformatted. As with any other format, if the contents aren't valid
|
||||
/// Python, then the block is left untouched automatically.
|
||||
#[option(
|
||||
default = "false",
|
||||
value_type = "bool",
|
||||
example = r#"
|
||||
# Enable reformatting of code snippets in docstrings.
|
||||
docstring-code-format = true
|
||||
"#
|
||||
)]
|
||||
pub docstring_code_format: Option<bool>,
|
||||
|
||||
/// Set the line length used when formatting code snippets in docstrings.
|
||||
///
|
||||
/// This only has an effect when the `docstring-code-format` setting is
|
||||
/// enabled.
|
||||
///
|
||||
/// The default value for this setting is `"dynamic"`, which has the effect
|
||||
/// of ensuring that any reformatted code examples in docstrings adhere to
|
||||
/// the global line length configuration that is used for the surrounding
|
||||
/// Python code. The point of this setting is that it takes the indentation
|
||||
/// of the docstring into account when reformatting code examples.
|
||||
///
|
||||
/// Alternatively, this can be set to a fixed integer, which will result
|
||||
/// in the same line length limit being applied to all reformatted code
|
||||
/// examples in docstrings. When set to a fixed integer, the indent of the
|
||||
/// docstring is not taken into account. That is, this may result in lines
|
||||
/// in the reformatted code example that exceed the globally configured
|
||||
/// line length limit.
|
||||
///
|
||||
/// For example, when this is set to `20` and `docstring-code-format` is
|
||||
/// enabled, then this code:
|
||||
///
|
||||
/// ```python
|
||||
/// def f(x):
|
||||
/// '''
|
||||
/// Something about `f`. And an example:
|
||||
///
|
||||
/// .. code-block:: python
|
||||
///
|
||||
/// foo, bar, quux = this_is_a_long_line(lion, hippo, lemur, bear)
|
||||
/// '''
|
||||
/// pass
|
||||
/// ```
|
||||
///
|
||||
/// ... will be reformatted (assuming the rest of the options are set
|
||||
/// to their defaults) as:
|
||||
///
|
||||
/// ```python
|
||||
/// def f(x):
|
||||
/// """
|
||||
/// Something about `f`. And an example:
|
||||
///
|
||||
/// .. code-block:: python
|
||||
///
|
||||
/// (
|
||||
/// foo,
|
||||
/// bar,
|
||||
/// quux,
|
||||
/// ) = this_is_a_long_line(
|
||||
/// lion,
|
||||
/// hippo,
|
||||
/// lemur,
|
||||
/// bear,
|
||||
/// )
|
||||
/// """
|
||||
/// pass
|
||||
/// ```
|
||||
#[option(
|
||||
default = r#""dynamic""#,
|
||||
value_type = r#"int | "dynamic""#,
|
||||
example = r#"
|
||||
# Format all docstring code snippets with a line length of 60.
|
||||
docstring-code-line-length = 60
|
||||
"#
|
||||
)]
|
||||
pub docstring_code_line_length: Option<DocstringCodeLineWidth>,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -5,7 +5,10 @@ use ruff_linter::settings::types::{FilePattern, FilePatternSet, SerializationFor
|
|||
use ruff_linter::settings::LinterSettings;
|
||||
use ruff_macros::CacheKey;
|
||||
use ruff_python_ast::PySourceType;
|
||||
use ruff_python_formatter::{MagicTrailingComma, PreviewMode, PyFormatOptions, QuoteStyle};
|
||||
use ruff_python_formatter::{
|
||||
DocstringCode, DocstringCodeLineWidth, MagicTrailingComma, PreviewMode, PyFormatOptions,
|
||||
QuoteStyle,
|
||||
};
|
||||
use ruff_source_file::find_newline;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
|
@ -124,6 +127,9 @@ pub struct FormatterSettings {
|
|||
pub magic_trailing_comma: MagicTrailingComma,
|
||||
|
||||
pub line_ending: LineEnding,
|
||||
|
||||
pub docstring_code_format: DocstringCode,
|
||||
pub docstring_code_line_width: DocstringCodeLineWidth,
|
||||
}
|
||||
|
||||
impl FormatterSettings {
|
||||
|
@ -157,6 +163,8 @@ impl FormatterSettings {
|
|||
.with_preview(self.preview)
|
||||
.with_line_ending(line_ending)
|
||||
.with_line_width(self.line_width)
|
||||
.with_docstring_code(self.docstring_code_format)
|
||||
.with_docstring_code_line_width(self.docstring_code_line_width)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -173,6 +181,8 @@ impl Default for FormatterSettings {
|
|||
indent_width: default_options.indent_width(),
|
||||
quote_style: default_options.quote_style(),
|
||||
magic_trailing_comma: default_options.magic_trailing_comma(),
|
||||
docstring_code_format: default_options.docstring_code(),
|
||||
docstring_code_line_width: default_options.docstring_code_line_width(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -71,6 +71,20 @@ If left unspecified, Ruff's default configuration is equivalent to:
|
|||
|
||||
# Like Black, automatically detect the appropriate line ending.
|
||||
line-ending = "auto"
|
||||
|
||||
# Enable auto-formatting of code examples in docstrings. Markdown,
|
||||
# reStructuredText code/literal blocks and doctests are all supported.
|
||||
#
|
||||
# This is currently disabled by default, but it is planned for this
|
||||
# to be opt-out in the future.
|
||||
docstring-code-format = false
|
||||
|
||||
# Set the line length limit used when formatting code snippets in
|
||||
# docstrings.
|
||||
#
|
||||
# This only has an effect when the `docstring-code-format` setting is
|
||||
# enabled.
|
||||
docstring-code-line-length = "dynamic"
|
||||
```
|
||||
|
||||
=== "ruff.toml"
|
||||
|
@ -134,6 +148,20 @@ If left unspecified, Ruff's default configuration is equivalent to:
|
|||
|
||||
# Like Black, automatically detect the appropriate line ending.
|
||||
line-ending = "auto"
|
||||
|
||||
# Enable auto-formatting of code examples in docstrings. Markdown,
|
||||
# reStructuredText code/literal blocks and doctests are all supported.
|
||||
#
|
||||
# This is currently disabled by default, but it is planned for this
|
||||
# to be opt-out in the future.
|
||||
docstring-code-format = false
|
||||
|
||||
# Set the line length limit used when formatting code snippets in
|
||||
# docstrings.
|
||||
#
|
||||
# This only has an effect when the `docstring-code-format` setting is
|
||||
# enabled.
|
||||
docstring-code-line-length = "dynamic"
|
||||
```
|
||||
|
||||
As an example, the following would configure Ruff to:
|
||||
|
|
|
@ -103,10 +103,12 @@ Going forward, the Ruff Formatter will support Black's preview style under Ruff'
|
|||
## Configuration
|
||||
|
||||
The Ruff Formatter exposes a small set of configuration options, some of which are also supported
|
||||
by Black (like line width), some of which are unique to Ruff (like quote and indentation style).
|
||||
by Black (like line width), some of which are unique to Ruff (like quote, indentation style and
|
||||
formatting code examples in docstrings).
|
||||
|
||||
For example, to configure the formatter to use single quotes, a line width of 100, and
|
||||
tab indentation, add the following to your configuration file:
|
||||
For example, to configure the formatter to use single quotes, format code
|
||||
examples in docstrings, a line width of 100, and tab indentation, add the
|
||||
following to your configuration file:
|
||||
|
||||
=== "pyproject.toml"
|
||||
|
||||
|
@ -117,6 +119,7 @@ tab indentation, add the following to your configuration file:
|
|||
[tool.ruff.format]
|
||||
quote-style = "single"
|
||||
indent-style = "tab"
|
||||
docstring-code-format = true
|
||||
```
|
||||
|
||||
=== "ruff.toml"
|
||||
|
@ -127,6 +130,7 @@ tab indentation, add the following to your configuration file:
|
|||
[format]
|
||||
quote-style = "single"
|
||||
indent-style = "tab"
|
||||
docstring-code-format = true
|
||||
```
|
||||
|
||||
|
||||
|
@ -137,6 +141,97 @@ Given the focus on Black compatibility (and unlike formatters like [YAPF](https:
|
|||
Ruff does not currently expose any configuration options to modify core formatting behavior outside
|
||||
of these trivia-related settings.
|
||||
|
||||
## Docstring formatting
|
||||
|
||||
The Ruff formatter provides an opt-in feature for automatically formatting
|
||||
Python code examples in docstrings. The Ruff formatter currently recognizes
|
||||
code examples in the following formats:
|
||||
|
||||
* The Python [doctest] format.
|
||||
* CommonMark [fenced code blocks] with the following info strings: `python`,
|
||||
`py`, `python3`, or `py3`. Fenced code blocks without an info string are
|
||||
assumed to be Python code examples and also formatted.
|
||||
* reStructuredText [literal blocks]. While literal blocks may contain things
|
||||
other than Python, this is meant to reflect a long-standing convention in the
|
||||
Python ecosystem where literal blocks often contain Python code.
|
||||
* reStructuredText [`code-block` and `sourcecode` directives]. As with
|
||||
Markdown, the language names recognized for Python are `python`, `py`,
|
||||
`python3`, or `py3`.
|
||||
|
||||
If a code example is recognized and treated as Python, the Ruff formatter will
|
||||
automatically skip it if the code does not parse as valid Python or if the
|
||||
reformatted code would produce an invalid Python program.
|
||||
|
||||
Users may also configure the line length limit used for reformatting Python
|
||||
code examples in docstrings. The default is a special value, `dynamic`, which
|
||||
instructs the formatter to respect the line length limit setting for the
|
||||
surrounding Python code. The `dynamic` setting ensures that even when code
|
||||
examples are found inside indented docstrings, the line length limit configured
|
||||
for the surrounding Python code will not be exceeded. Users may also configure
|
||||
a fixed line length limit for code examples in docstrings.
|
||||
|
||||
For example, this configuration shows how to enable docstring code formatting
|
||||
with a fixed line length limit:
|
||||
|
||||
=== "pyproject.toml"
|
||||
|
||||
```toml
|
||||
[tool.ruff.format]
|
||||
docstring-code-format = true
|
||||
docstring-code-line-length = 20
|
||||
```
|
||||
|
||||
=== "ruff.toml"
|
||||
|
||||
```toml
|
||||
[format]
|
||||
docstring-code-format = true
|
||||
docstring-code-line-length = 20
|
||||
```
|
||||
|
||||
With the above configuration, this code:
|
||||
|
||||
```python
|
||||
def f(x):
|
||||
'''
|
||||
Something about `f`. And an example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
foo, bar, quux = this_is_a_long_line(lion, hippo, lemur, bear)
|
||||
'''
|
||||
pass
|
||||
```
|
||||
|
||||
... will be reformatted (assuming the rest of the options are set
|
||||
to their defaults) as:
|
||||
|
||||
```python
|
||||
def f(x):
|
||||
"""
|
||||
Something about `f`. And an example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
(
|
||||
foo,
|
||||
bar,
|
||||
quux,
|
||||
) = this_is_a_long_line(
|
||||
lion,
|
||||
hippo,
|
||||
lemur,
|
||||
bear,
|
||||
)
|
||||
"""
|
||||
pass
|
||||
```
|
||||
|
||||
[doctest]: https://docs.python.org/3/library/doctest.html
|
||||
[fenced code blocks]: https://spec.commonmark.org/0.30/#fenced-code-blocks
|
||||
[literal blocks]: https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html#literal-blocks
|
||||
[`code-block` and `sourcecode` directives]: https://www.sphinx-doc.org/en/master/usage/restructuredtext/directives.html#directive-code-block
|
||||
|
||||
## Format suppression
|
||||
|
||||
Like Black, Ruff supports `# fmt: on`, `# fmt: off`, and `# fmt: skip` pragma comments, which can
|
||||
|
|
34
ruff.schema.json
generated
34
ruff.schema.json
generated
|
@ -747,6 +747,16 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"DocstringCodeLineWidth": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/LineWidth"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"Flake8AnnotationsOptions": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
@ -1248,6 +1258,24 @@
|
|||
"description": "Experimental: Configures how `ruff format` formats your code.\n\nPlease provide feedback in [this discussion](https://github.com/astral-sh/ruff/discussions/7310).",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"docstring-code-format": {
|
||||
"description": "Whether to format code snippets in docstrings.\n\nWhen this is enabled, Python code examples within docstrings are automatically reformatted.\n\nFor example, when this is enabled, the following code:\n\n```python def f(x): \"\"\" Something about `f`. And an example in doctest format:\n\n>>> f( x )\n\nMarkdown is also supported:\n\n```py f( x ) ```\n\nAs are reStructuredText literal blocks::\n\nf( x )\n\nAnd reStructuredText code blocks:\n\n.. code-block:: python\n\nf( x ) \"\"\" pass ```\n\n... will be reformatted (assuming the rest of the options are set to their defaults) as:\n\n```python def f(x): \"\"\" Something about `f`. And an example in doctest format:\n\n>>> f(x)\n\nMarkdown is also supported:\n\n```py f(x) ```\n\nAs are reStructuredText literal blocks::\n\nf(x)\n\nAnd reStructuredText code blocks:\n\n.. code-block:: python\n\nf(x) \"\"\" pass ```\n\nIf a code snippt in a docstring contains invalid Python code or if the formatter would otherwise write invalid Python code, then the code example is ignored by the formatter and kept as-is.\n\nCurrently, doctest, Markdown, reStructuredText literal blocks, and reStructuredText code blocks are all supported and automatically recognized. In the case of unlabeled fenced code blocks in Markdown and reStructuredText literal blocks, the contents are assumed to be Python and reformatted. As with any other format, if the contents aren't valid Python, then the block is left untouched automatically.",
|
||||
"type": [
|
||||
"boolean",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"docstring-code-line-length": {
|
||||
"description": "Set the line length used when formatting code snippets in docstrings.\n\nThis only has an effect when the `docstring-code-format` setting is enabled.\n\nThe default value for this setting is `\"dynamic\"`, which has the effect of ensuring that any reformatted code examples in docstrings adhere to the global line length configuration that is used for the surrounding Python code. The point of this setting is that it takes the indentation of the docstring into account when reformatting code examples.\n\nAlternatively, this can be set to a fixed integer, which will result in the same line length limit being applied to all reformatted code examples in docstrings. When set to a fixed integer, the indent of the docstring is not taken into account. That is, this may result in lines in the reformatted code example that exceed the globally configured line length limit.\n\nFor example, when this is set to `20` and `docstring-code-format` is enabled, then this code:\n\n```python def f(x): ''' Something about `f`. And an example:\n\n.. code-block:: python\n\nfoo, bar, quux = this_is_a_long_line(lion, hippo, lemur, bear) ''' pass ```\n\n... will be reformatted (assuming the rest of the options are set to their defaults) as:\n\n```python def f(x): \"\"\" Something about `f`. And an example:\n\n.. code-block:: python\n\n( foo, bar, quux, ) = this_is_a_long_line( lion, hippo, lemur, bear, ) \"\"\" pass ```",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/DocstringCodeLineWidth"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"exclude": {
|
||||
"description": "A list of file patterns to exclude from formatting in addition to the files excluded globally (see [`exclude`](#exclude), and [`extend-exclude`](#extend-exclude)).\n\nExclusions are based on globs, and can be either:\n\n- Single-path patterns, like `.mypy_cache` (to exclude any directory named `.mypy_cache` in the tree), `foo.py` (to exclude any file named `foo.py`), or `foo_*.py` (to exclude any file matching `foo_*.py` ). - Relative patterns, like `directory/foo.py` (to exclude that specific file) or `directory/*.py` (to exclude any Python files in `directory`). Note that these paths are relative to the project root (e.g., the directory containing your `pyproject.toml`).\n\nFor more information on the glob syntax, refer to the [`globset` documentation](https://docs.rs/globset/latest/globset/#syntax).",
|
||||
"type": [
|
||||
|
@ -1652,6 +1680,12 @@
|
|||
"maximum": 320.0,
|
||||
"minimum": 1.0
|
||||
},
|
||||
"LineWidth": {
|
||||
"description": "The maximum visual width to which the formatter should try to limit a line.",
|
||||
"type": "integer",
|
||||
"format": "uint16",
|
||||
"minimum": 1.0
|
||||
},
|
||||
"LintOptions": {
|
||||
"description": "Experimental section to configure Ruff's linting. This new section will eventually replace the top-level linting options.\n\nOptions specified in the `lint` section take precedence over the top-level settings.",
|
||||
"type": "object",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue