mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-27 12:29:48 +00:00
Add tab width option (#6848)
This commit is contained in:
parent
f91bacbb94
commit
9d77552e18
20 changed files with 345 additions and 44 deletions
|
@ -4,7 +4,7 @@ use crate::prelude::tag::GroupMode;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::printer::LineEnding;
|
use crate::printer::LineEnding;
|
||||||
use crate::source_code::SourceCode;
|
use crate::source_code::SourceCode;
|
||||||
use crate::{format, write};
|
use crate::{format, write, TabWidth};
|
||||||
use crate::{
|
use crate::{
|
||||||
BufferExtensions, Format, FormatContext, FormatElement, FormatOptions, FormatResult, Formatter,
|
BufferExtensions, Format, FormatContext, FormatElement, FormatOptions, FormatResult, Formatter,
|
||||||
IndentStyle, LineWidth, PrinterOptions,
|
IndentStyle, LineWidth, PrinterOptions,
|
||||||
|
@ -215,13 +215,17 @@ impl FormatOptions for IrFormatOptions {
|
||||||
IndentStyle::Space(2)
|
IndentStyle::Space(2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn tab_width(&self) -> TabWidth {
|
||||||
|
TabWidth::default()
|
||||||
|
}
|
||||||
|
|
||||||
fn line_width(&self) -> LineWidth {
|
fn line_width(&self) -> LineWidth {
|
||||||
LineWidth(80)
|
LineWidth(80)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_print_options(&self) -> PrinterOptions {
|
fn as_print_options(&self) -> PrinterOptions {
|
||||||
PrinterOptions {
|
PrinterOptions {
|
||||||
tab_width: 2,
|
tab_width: TabWidth::default(),
|
||||||
print_width: self.line_width().into(),
|
print_width: self.line_width().into(),
|
||||||
line_ending: LineEnding::LineFeed,
|
line_ending: LineEnding::LineFeed,
|
||||||
indent_style: IndentStyle::Space(2),
|
indent_style: IndentStyle::Space(2),
|
||||||
|
|
|
@ -51,7 +51,7 @@ pub use crate::diagnostics::{ActualStart, FormatError, InvalidDocumentError, Pri
|
||||||
pub use format_element::{normalize_newlines, FormatElement, LINE_TERMINATORS};
|
pub use format_element::{normalize_newlines, FormatElement, LINE_TERMINATORS};
|
||||||
pub use group_id::GroupId;
|
pub use group_id::GroupId;
|
||||||
use ruff_text_size::{TextRange, TextSize};
|
use ruff_text_size::{TextRange, TextSize};
|
||||||
use std::num::ParseIntError;
|
use std::num::{NonZeroU8, ParseIntError, TryFromIntError};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq, Clone, Copy, Hash)]
|
#[derive(Debug, Eq, PartialEq, Clone, Copy, Hash)]
|
||||||
|
@ -108,6 +108,33 @@ impl std::fmt::Display for IndentStyle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The visual width of a `\t` character.
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
|
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||||
|
pub struct TabWidth(NonZeroU8);
|
||||||
|
|
||||||
|
impl TabWidth {
|
||||||
|
/// Return the numeric value for this [`LineWidth`]
|
||||||
|
pub const fn value(&self) -> u32 {
|
||||||
|
self.0.get() as u32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for TabWidth {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self(NonZeroU8::new(2).unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<u8> for TabWidth {
|
||||||
|
type Error = TryFromIntError;
|
||||||
|
|
||||||
|
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
||||||
|
NonZeroU8::try_from(value).map(Self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Validated value for the `line_width` formatter options
|
/// Validated value for the `line_width` formatter options
|
||||||
///
|
///
|
||||||
/// The allowed range of values is 1..=320
|
/// The allowed range of values is 1..=320
|
||||||
|
@ -213,6 +240,17 @@ pub trait FormatOptions {
|
||||||
/// The indent style.
|
/// The indent style.
|
||||||
fn indent_style(&self) -> IndentStyle;
|
fn indent_style(&self) -> IndentStyle;
|
||||||
|
|
||||||
|
/// The visual width of a tab character.
|
||||||
|
fn tab_width(&self) -> TabWidth;
|
||||||
|
|
||||||
|
/// The visual width of an indent
|
||||||
|
fn indent_width(&self) -> u32 {
|
||||||
|
match self.indent_style() {
|
||||||
|
IndentStyle::Tab => self.tab_width().value(),
|
||||||
|
IndentStyle::Space(spaces) => u32::from(spaces),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// What's the max width of a line. Defaults to 80.
|
/// What's the max width of a line. Defaults to 80.
|
||||||
fn line_width(&self) -> LineWidth;
|
fn line_width(&self) -> LineWidth;
|
||||||
|
|
||||||
|
@ -264,6 +302,10 @@ impl FormatOptions for SimpleFormatOptions {
|
||||||
self.indent_style
|
self.indent_style
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn tab_width(&self) -> TabWidth {
|
||||||
|
TabWidth::default()
|
||||||
|
}
|
||||||
|
|
||||||
fn line_width(&self) -> LineWidth {
|
fn line_width(&self) -> LineWidth {
|
||||||
self.line_width
|
self.line_width
|
||||||
}
|
}
|
||||||
|
@ -271,6 +313,7 @@ impl FormatOptions for SimpleFormatOptions {
|
||||||
fn as_print_options(&self) -> PrinterOptions {
|
fn as_print_options(&self) -> PrinterOptions {
|
||||||
PrinterOptions::default()
|
PrinterOptions::default()
|
||||||
.with_indent(self.indent_style)
|
.with_indent(self.indent_style)
|
||||||
|
.with_tab_width(self.tab_width())
|
||||||
.with_print_width(self.line_width.into())
|
.with_print_width(self.line_width.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -732,7 +732,7 @@ impl<'a> Printer<'a> {
|
||||||
|
|
||||||
#[allow(clippy::cast_possible_truncation)]
|
#[allow(clippy::cast_possible_truncation)]
|
||||||
let char_width = if char == '\t' {
|
let char_width = if char == '\t' {
|
||||||
u32::from(self.options.tab_width)
|
self.options.tab_width.value()
|
||||||
} else {
|
} else {
|
||||||
// SAFETY: A u32 is sufficient to represent the width of a file <= 4GB
|
// SAFETY: A u32 is sufficient to represent the width of a file <= 4GB
|
||||||
char.width().unwrap_or(0) as u32
|
char.width().unwrap_or(0) as u32
|
||||||
|
@ -1283,13 +1283,12 @@ impl<'a, 'print> FitsMeasurer<'a, 'print> {
|
||||||
|
|
||||||
fn fits_text(&mut self, text: &str, args: PrintElementArgs) -> Fits {
|
fn fits_text(&mut self, text: &str, args: PrintElementArgs) -> Fits {
|
||||||
let indent = std::mem::take(&mut self.state.pending_indent);
|
let indent = std::mem::take(&mut self.state.pending_indent);
|
||||||
self.state.line_width += u32::from(indent.level())
|
self.state.line_width +=
|
||||||
* u32::from(self.options().indent_width())
|
u32::from(indent.level()) * self.options().indent_width() + u32::from(indent.align());
|
||||||
+ u32::from(indent.align());
|
|
||||||
|
|
||||||
for c in text.chars() {
|
for c in text.chars() {
|
||||||
let char_width = match c {
|
let char_width = match c {
|
||||||
'\t' => u32::from(self.options().tab_width),
|
'\t' => self.options().tab_width.value(),
|
||||||
'\n' => {
|
'\n' => {
|
||||||
if self.must_be_flat {
|
if self.must_be_flat {
|
||||||
return Fits::No;
|
return Fits::No;
|
||||||
|
@ -1428,7 +1427,9 @@ mod tests {
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::printer::{LineEnding, PrintWidth, Printer, PrinterOptions};
|
use crate::printer::{LineEnding, PrintWidth, Printer, PrinterOptions};
|
||||||
use crate::source_code::SourceCode;
|
use crate::source_code::SourceCode;
|
||||||
use crate::{format_args, write, Document, FormatState, IndentStyle, Printed, VecBuffer};
|
use crate::{
|
||||||
|
format_args, write, Document, FormatState, IndentStyle, Printed, TabWidth, VecBuffer,
|
||||||
|
};
|
||||||
|
|
||||||
fn format(root: &dyn Format<SimpleFormatContext>) -> Printed {
|
fn format(root: &dyn Format<SimpleFormatContext>) -> Printed {
|
||||||
format_with_options(
|
format_with_options(
|
||||||
|
@ -1578,7 +1579,7 @@ two lines`,
|
||||||
fn it_use_the_indent_character_specified_in_the_options() {
|
fn it_use_the_indent_character_specified_in_the_options() {
|
||||||
let options = PrinterOptions {
|
let options = PrinterOptions {
|
||||||
indent_style: IndentStyle::Tab,
|
indent_style: IndentStyle::Tab,
|
||||||
tab_width: 4,
|
tab_width: TabWidth::try_from(4).unwrap(),
|
||||||
print_width: PrintWidth::new(19),
|
print_width: PrintWidth::new(19),
|
||||||
..PrinterOptions::default()
|
..PrinterOptions::default()
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
use crate::{FormatOptions, IndentStyle, LineWidth};
|
use crate::{FormatOptions, IndentStyle, LineWidth, TabWidth};
|
||||||
|
|
||||||
/// Options that affect how the [`crate::Printer`] prints the format tokens
|
/// Options that affect how the [`crate::Printer`] prints the format tokens
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
#[derive(Clone, Debug, Eq, PartialEq, Default)]
|
||||||
pub struct PrinterOptions {
|
pub struct PrinterOptions {
|
||||||
/// Width of a single tab character (does it equal 2, 4, ... spaces?)
|
/// Width of a single tab character (does it equal 2, 4, ... spaces?)
|
||||||
pub tab_width: u8,
|
pub tab_width: TabWidth,
|
||||||
|
|
||||||
/// What's the max width of a line. Defaults to 80
|
/// What's the max width of a line. Defaults to 80
|
||||||
pub print_width: PrintWidth,
|
pub print_width: PrintWidth,
|
||||||
|
@ -74,23 +74,31 @@ impl PrinterOptions {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn with_tab_width(mut self, width: TabWidth) -> Self {
|
||||||
|
self.tab_width = width;
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn indent_style(&self) -> IndentStyle {
|
pub(crate) fn indent_style(&self) -> IndentStyle {
|
||||||
self.indent_style
|
self.indent_style
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Width of an indent in characters.
|
/// Width of an indent in characters.
|
||||||
pub(super) const fn indent_width(&self) -> u8 {
|
pub(super) const fn indent_width(&self) -> u32 {
|
||||||
match self.indent_style {
|
match self.indent_style {
|
||||||
IndentStyle::Tab => self.tab_width,
|
IndentStyle::Tab => self.tab_width.value(),
|
||||||
IndentStyle::Space(count) => count,
|
IndentStyle::Space(count) => count as u32,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
#[derive(Clone, Debug, Eq, PartialEq, Default)]
|
||||||
pub enum LineEnding {
|
pub enum LineEnding {
|
||||||
/// Line Feed only (\n), common on Linux and macOS as well as inside git repos
|
/// Line Feed only (\n), common on Linux and macOS as well as inside git repos
|
||||||
|
#[default]
|
||||||
LineFeed,
|
LineFeed,
|
||||||
|
|
||||||
/// Carriage Return + Line Feed characters (\r\n), common on Windows
|
/// Carriage Return + Line Feed characters (\r\n), common on Windows
|
||||||
|
@ -110,14 +118,3 @@ impl LineEnding {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for PrinterOptions {
|
|
||||||
fn default() -> Self {
|
|
||||||
PrinterOptions {
|
|
||||||
tab_width: 2,
|
|
||||||
print_width: PrintWidth::default(),
|
|
||||||
indent_style: IndentStyle::default(),
|
|
||||||
line_ending: LineEnding::LineFeed,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"tab_width": 8
|
||||||
|
}
|
|
@ -2,14 +2,21 @@
|
||||||
{
|
{
|
||||||
"indent_style": {
|
"indent_style": {
|
||||||
"Space": 4
|
"Space": 4
|
||||||
}
|
},
|
||||||
|
"tab_width": 8
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"indent_style": {
|
"indent_style": {
|
||||||
"Space": 2
|
"Space": 2
|
||||||
}
|
},
|
||||||
|
"tab_width": 8
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"indent_style": "Tab"
|
"indent_style": "Tab",
|
||||||
|
"tab_width": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indent_style": "Tab",
|
||||||
|
"tab_width": 4
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
8
crates/ruff_python_formatter/resources/test/fixtures/ruff/tab_width.options.json
vendored
Normal file
8
crates/ruff_python_formatter/resources/test/fixtures/ruff/tab_width.options.json
vendored
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"tab_width": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tab_width": 4
|
||||||
|
}
|
||||||
|
]
|
8
crates/ruff_python_formatter/resources/test/fixtures/ruff/tab_width.py
vendored
Normal file
8
crates/ruff_python_formatter/resources/test/fixtures/ruff/tab_width.py
vendored
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
# Fits with tab width 2
|
||||||
|
1 + " 012345678901234567890123456789012345678901234567890123456789012345678901234567890"
|
||||||
|
|
||||||
|
# Fits with tab width 4
|
||||||
|
1 + " 0123456789012345678901234567890123456789012345678901234567890123456789012345678"
|
||||||
|
|
||||||
|
# Fits with tab width 8
|
||||||
|
1 + " 012345678901234567890123456789012345678901234567890123456789012345678901234"
|
|
@ -43,7 +43,10 @@ where
|
||||||
// of 5 characters to avoid it exceeding the line width by 1 reduces the readability.
|
// of 5 characters to avoid it exceeding the line width by 1 reduces the readability.
|
||||||
// * The text is know to never fit: The text can never fit even when parenthesizing if it is longer
|
// * The text is know to never fit: The text can never fit even when parenthesizing if it is longer
|
||||||
// than the configured line width (minus indent).
|
// than the configured line width (minus indent).
|
||||||
text_len > 5 && text_len < context.options().line_width().value() as usize
|
text_len > 5
|
||||||
|
&& text_len
|
||||||
|
<= context.options().line_width().value() as usize
|
||||||
|
- context.options().indent_width() as usize
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) trait NeedsParentheses {
|
pub(crate) trait NeedsParentheses {
|
||||||
|
|
|
@ -2,7 +2,7 @@ use std::borrow::Cow;
|
||||||
|
|
||||||
use bitflags::bitflags;
|
use bitflags::bitflags;
|
||||||
|
|
||||||
use ruff_formatter::{format_args, write, FormatError};
|
use ruff_formatter::{format_args, write, FormatError, FormatOptions, TabWidth};
|
||||||
use ruff_python_ast::node::AnyNodeRef;
|
use ruff_python_ast::node::AnyNodeRef;
|
||||||
use ruff_python_ast::{self as ast, ExprConstant, ExprFString, Ranged};
|
use ruff_python_ast::{self as ast, ExprConstant, ExprFString, Ranged};
|
||||||
use ruff_python_parser::lexer::{lex_starts_at, LexicalError, LexicalErrorType};
|
use ruff_python_parser::lexer::{lex_starts_at, LexicalError, LexicalErrorType};
|
||||||
|
@ -682,13 +682,14 @@ fn normalize_string(
|
||||||
/// to the next multiple of 8. This is effectively a port of
|
/// to the next multiple of 8. This is effectively a port of
|
||||||
/// [`str.expandtabs`](https://docs.python.org/3/library/stdtypes.html#str.expandtabs),
|
/// [`str.expandtabs`](https://docs.python.org/3/library/stdtypes.html#str.expandtabs),
|
||||||
/// which black [calls with the default tab width of 8](https://github.com/psf/black/blob/c36e468794f9256d5e922c399240d49782ba04f1/src/black/strings.py#L61)
|
/// which black [calls with the default tab width of 8](https://github.com/psf/black/blob/c36e468794f9256d5e922c399240d49782ba04f1/src/black/strings.py#L61)
|
||||||
fn count_indentation_like_black(line: &str) -> TextSize {
|
fn count_indentation_like_black(line: &str, tab_width: TabWidth) -> TextSize {
|
||||||
let tab_width: u32 = 8;
|
|
||||||
let mut indentation = TextSize::default();
|
let mut indentation = TextSize::default();
|
||||||
for char in line.chars() {
|
for char in line.chars() {
|
||||||
if char == '\t' {
|
if char == '\t' {
|
||||||
// Pad to the next multiple of tab_width
|
// Pad to the next multiple of tab_width
|
||||||
indentation += TextSize::from(tab_width - (indentation.to_u32().rem_euclid(tab_width)));
|
indentation += TextSize::from(
|
||||||
|
tab_width.value() - (indentation.to_u32().rem_euclid(tab_width.value())),
|
||||||
|
);
|
||||||
} else if char.is_whitespace() {
|
} else if char.is_whitespace() {
|
||||||
indentation += char.text_len();
|
indentation += char.text_len();
|
||||||
} else {
|
} else {
|
||||||
|
@ -868,7 +869,7 @@ fn format_docstring(string_part: &FormatStringPart, f: &mut PyFormatter) -> Form
|
||||||
.clone()
|
.clone()
|
||||||
// We don't want to count whitespace-only lines as miss-indented
|
// We don't want to count whitespace-only lines as miss-indented
|
||||||
.filter(|line| !line.trim().is_empty())
|
.filter(|line| !line.trim().is_empty())
|
||||||
.map(count_indentation_like_black)
|
.map(|line| count_indentation_like_black(line, f.options().tab_width()))
|
||||||
.min()
|
.min()
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
@ -943,7 +944,8 @@ fn format_docstring_line(
|
||||||
// overindented, in which case we strip the additional whitespace (see example in
|
// overindented, in which case we strip the additional whitespace (see example in
|
||||||
// [`format_docstring`] doc comment). We then prepend the in-docstring indentation to the
|
// [`format_docstring`] doc comment). We then prepend the in-docstring indentation to the
|
||||||
// string.
|
// string.
|
||||||
let indent_len = count_indentation_like_black(trim_end) - stripped_indentation;
|
let indent_len =
|
||||||
|
count_indentation_like_black(trim_end, f.options().tab_width()) - stripped_indentation;
|
||||||
let in_docstring_indent = " ".repeat(indent_len.to_usize()) + trim_end.trim_start();
|
let in_docstring_indent = " ".repeat(indent_len.to_usize()) + trim_end.trim_start();
|
||||||
dynamic_text(&in_docstring_indent, Some(offset)).fmt(f)?;
|
dynamic_text(&in_docstring_indent, Some(offset)).fmt(f)?;
|
||||||
} else {
|
} else {
|
||||||
|
@ -976,12 +978,23 @@ fn format_docstring_line(
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::expression::string::count_indentation_like_black;
|
use crate::expression::string::count_indentation_like_black;
|
||||||
|
use ruff_formatter::TabWidth;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_indentation_like_black() {
|
fn test_indentation_like_black() {
|
||||||
assert_eq!(count_indentation_like_black("\t \t \t").to_u32(), 24);
|
let tab_width = TabWidth::try_from(8).unwrap();
|
||||||
assert_eq!(count_indentation_like_black("\t \t").to_u32(), 24);
|
assert_eq!(
|
||||||
assert_eq!(count_indentation_like_black("\t\t\t").to_u32(), 24);
|
count_indentation_like_black("\t \t \t", tab_width).to_u32(),
|
||||||
assert_eq!(count_indentation_like_black(" ").to_u32(), 4);
|
24
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
count_indentation_like_black("\t \t", tab_width).to_u32(),
|
||||||
|
24
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
count_indentation_like_black("\t\t\t", tab_width).to_u32(),
|
||||||
|
24
|
||||||
|
);
|
||||||
|
assert_eq!(count_indentation_like_black(" ", tab_width).to_u32(), 4);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use ruff_formatter::printer::{LineEnding, PrinterOptions};
|
use ruff_formatter::printer::{LineEnding, PrinterOptions};
|
||||||
use ruff_formatter::{FormatOptions, IndentStyle, LineWidth};
|
use ruff_formatter::{FormatOptions, IndentStyle, LineWidth, TabWidth};
|
||||||
use ruff_python_ast::PySourceType;
|
use ruff_python_ast::PySourceType;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
@ -24,6 +24,10 @@ pub struct PyFormatOptions {
|
||||||
#[cfg_attr(feature = "serde", serde(default = "default_line_width"))]
|
#[cfg_attr(feature = "serde", serde(default = "default_line_width"))]
|
||||||
line_width: LineWidth,
|
line_width: LineWidth,
|
||||||
|
|
||||||
|
/// The visual width of a tab character.
|
||||||
|
#[cfg_attr(feature = "serde", serde(default = "default_tab_width"))]
|
||||||
|
tab_width: TabWidth,
|
||||||
|
|
||||||
/// The preferred quote style to use (single vs double quotes).
|
/// The preferred quote style to use (single vs double quotes).
|
||||||
quote_style: QuoteStyle,
|
quote_style: QuoteStyle,
|
||||||
|
|
||||||
|
@ -39,12 +43,17 @@ fn default_indent_style() -> IndentStyle {
|
||||||
IndentStyle::Space(4)
|
IndentStyle::Space(4)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn default_tab_width() -> TabWidth {
|
||||||
|
TabWidth::try_from(4).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
impl Default for PyFormatOptions {
|
impl Default for PyFormatOptions {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
source_type: PySourceType::default(),
|
source_type: PySourceType::default(),
|
||||||
indent_style: default_indent_style(),
|
indent_style: default_indent_style(),
|
||||||
line_width: default_line_width(),
|
line_width: default_line_width(),
|
||||||
|
tab_width: default_tab_width(),
|
||||||
quote_style: QuoteStyle::default(),
|
quote_style: QuoteStyle::default(),
|
||||||
magic_trailing_comma: MagicTrailingComma::default(),
|
magic_trailing_comma: MagicTrailingComma::default(),
|
||||||
}
|
}
|
||||||
|
@ -106,13 +115,17 @@ impl FormatOptions for PyFormatOptions {
|
||||||
self.indent_style
|
self.indent_style
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn tab_width(&self) -> TabWidth {
|
||||||
|
self.tab_width
|
||||||
|
}
|
||||||
|
|
||||||
fn line_width(&self) -> LineWidth {
|
fn line_width(&self) -> LineWidth {
|
||||||
self.line_width
|
self.line_width
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_print_options(&self) -> PrinterOptions {
|
fn as_print_options(&self) -> PrinterOptions {
|
||||||
PrinterOptions {
|
PrinterOptions {
|
||||||
tab_width: 4,
|
tab_width: self.tab_width,
|
||||||
print_width: self.line_width.into(),
|
print_width: self.line_width.into(),
|
||||||
line_ending: LineEnding::LineFeed,
|
line_ending: LineEnding::LineFeed,
|
||||||
indent_style: self.indent_style,
|
indent_style: self.indent_style,
|
||||||
|
|
|
@ -253,9 +253,11 @@ impl fmt::Display for DisplayPyOptions<'_> {
|
||||||
f,
|
f,
|
||||||
r#"indent-style = {indent_style}
|
r#"indent-style = {indent_style}
|
||||||
line-width = {line_width}
|
line-width = {line_width}
|
||||||
|
tab-width = {tab_width}
|
||||||
quote-style = {quote_style:?}
|
quote-style = {quote_style:?}
|
||||||
magic-trailing-comma = {magic_trailing_comma:?}"#,
|
magic-trailing-comma = {magic_trailing_comma:?}"#,
|
||||||
indent_style = self.0.indent_style(),
|
indent_style = self.0.indent_style(),
|
||||||
|
tab_width = self.0.tab_width().value(),
|
||||||
line_width = self.0.line_width().value(),
|
line_width = self.0.line_width().value(),
|
||||||
quote_style = self.0.quote_style(),
|
quote_style = self.0.quote_style(),
|
||||||
magic_trailing_comma = self.0.magic_trailing_comma()
|
magic_trailing_comma = self.0.magic_trailing_comma()
|
||||||
|
|
|
@ -113,6 +113,7 @@ class TabbedIndent:
|
||||||
```
|
```
|
||||||
indent-style = Spaces, size: 4
|
indent-style = Spaces, size: 4
|
||||||
line-width = 88
|
line-width = 88
|
||||||
|
tab-width = 8
|
||||||
quote-style = Double
|
quote-style = Double
|
||||||
magic-trailing-comma = Respect
|
magic-trailing-comma = Respect
|
||||||
```
|
```
|
||||||
|
@ -225,6 +226,7 @@ class TabbedIndent:
|
||||||
```
|
```
|
||||||
indent-style = Spaces, size: 2
|
indent-style = Spaces, size: 2
|
||||||
line-width = 88
|
line-width = 88
|
||||||
|
tab-width = 8
|
||||||
quote-style = Double
|
quote-style = Double
|
||||||
magic-trailing-comma = Respect
|
magic-trailing-comma = Respect
|
||||||
```
|
```
|
||||||
|
@ -337,6 +339,7 @@ class TabbedIndent:
|
||||||
```
|
```
|
||||||
indent-style = Tab
|
indent-style = Tab
|
||||||
line-width = 88
|
line-width = 88
|
||||||
|
tab-width = 8
|
||||||
quote-style = Double
|
quote-style = Double
|
||||||
magic-trailing-comma = Respect
|
magic-trailing-comma = Respect
|
||||||
```
|
```
|
||||||
|
@ -445,4 +448,117 @@ class TabbedIndent:
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### Output 4
|
||||||
|
```
|
||||||
|
indent-style = Tab
|
||||||
|
line-width = 88
|
||||||
|
tab-width = 4
|
||||||
|
quote-style = Double
|
||||||
|
magic-trailing-comma = Respect
|
||||||
|
```
|
||||||
|
|
||||||
|
```py
|
||||||
|
def single_line_backslashes1():
|
||||||
|
"""content\ """
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
def single_line_backslashes2():
|
||||||
|
"""content\\"""
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
def single_line_backslashes3():
|
||||||
|
"""content\\\ """
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
def multiline_backslashes1():
|
||||||
|
"""This is a docstring with
|
||||||
|
some lines of text\ """
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
def multiline_backslashes2():
|
||||||
|
"""This is a docstring with
|
||||||
|
some lines of text\\"""
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
def multiline_backslashes3():
|
||||||
|
"""This is a docstring with
|
||||||
|
some lines of text\\\ """
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
def multiple_negatively_indented_docstring_lines():
|
||||||
|
"""a
|
||||||
|
b
|
||||||
|
c
|
||||||
|
d
|
||||||
|
e
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def overindentend_docstring():
|
||||||
|
"""a
|
||||||
|
over-indented
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def comment_before_docstring():
|
||||||
|
# don't lose this function comment ...
|
||||||
|
"""Does nothing.
|
||||||
|
|
||||||
|
But it has comments
|
||||||
|
""" # ... neither lose this function comment
|
||||||
|
|
||||||
|
|
||||||
|
class CommentBeforeDocstring:
|
||||||
|
# don't lose this class comment ...
|
||||||
|
"""Empty class.
|
||||||
|
|
||||||
|
But it has comments
|
||||||
|
""" # ... neither lose this class comment
|
||||||
|
|
||||||
|
|
||||||
|
class IndentMeSome:
|
||||||
|
def doc_string_without_linebreak_after_colon(self):
|
||||||
|
"""This is somewhat strange
|
||||||
|
a
|
||||||
|
b
|
||||||
|
We format this a is the docstring had started properly indented on the next
|
||||||
|
line if the target indentation. This may we incorrect since source and target
|
||||||
|
indentation can be incorrect, but this is also an edge case.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class IgnoreImplicitlyConcatenatedStrings:
|
||||||
|
"""""" ""
|
||||||
|
|
||||||
|
|
||||||
|
def docstring_that_ends_with_quote_and_a_line_break1():
|
||||||
|
"""
|
||||||
|
he said "the news of my death have been greatly exaggerated"
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def docstring_that_ends_with_quote_and_a_line_break2():
|
||||||
|
"""he said "the news of my death have been greatly exaggerated" """
|
||||||
|
|
||||||
|
|
||||||
|
def docstring_that_ends_with_quote_and_a_line_break3():
|
||||||
|
"""he said "the news of my death have been greatly exaggerated" """
|
||||||
|
|
||||||
|
|
||||||
|
class TabbedIndent:
|
||||||
|
def tabbed_indent(self):
|
||||||
|
"""check for correct tabbed formatting
|
||||||
|
^^^^^^^^^^
|
||||||
|
Normal indented line
|
||||||
|
- autor
|
||||||
|
"""
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -131,6 +131,7 @@ test_particular = [
|
||||||
```
|
```
|
||||||
indent-style = Spaces, size: 4
|
indent-style = Spaces, size: 4
|
||||||
line-width = 88
|
line-width = 88
|
||||||
|
tab-width = 4
|
||||||
quote-style = Double
|
quote-style = Double
|
||||||
magic-trailing-comma = Respect
|
magic-trailing-comma = Respect
|
||||||
```
|
```
|
||||||
|
@ -279,6 +280,7 @@ test_particular = [
|
||||||
```
|
```
|
||||||
indent-style = Spaces, size: 4
|
indent-style = Spaces, size: 4
|
||||||
line-width = 88
|
line-width = 88
|
||||||
|
tab-width = 4
|
||||||
quote-style = Single
|
quote-style = Single
|
||||||
magic-trailing-comma = Respect
|
magic-trailing-comma = Respect
|
||||||
```
|
```
|
||||||
|
|
|
@ -143,6 +143,7 @@ x = (b"""aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa""" b"""bbbbbbbbbbbbbbbbbbbbbbbbbbb
|
||||||
```
|
```
|
||||||
indent-style = Spaces, size: 4
|
indent-style = Spaces, size: 4
|
||||||
line-width = 88
|
line-width = 88
|
||||||
|
tab-width = 4
|
||||||
quote-style = Double
|
quote-style = Double
|
||||||
magic-trailing-comma = Respect
|
magic-trailing-comma = Respect
|
||||||
```
|
```
|
||||||
|
@ -312,6 +313,7 @@ x = (
|
||||||
```
|
```
|
||||||
indent-style = Spaces, size: 4
|
indent-style = Spaces, size: 4
|
||||||
line-width = 88
|
line-width = 88
|
||||||
|
tab-width = 4
|
||||||
quote-style = Single
|
quote-style = Single
|
||||||
magic-trailing-comma = Respect
|
magic-trailing-comma = Respect
|
||||||
```
|
```
|
||||||
|
|
|
@ -30,6 +30,7 @@ def test():
|
||||||
```
|
```
|
||||||
indent-style = Spaces, size: 4
|
indent-style = Spaces, size: 4
|
||||||
line-width = 88
|
line-width = 88
|
||||||
|
tab-width = 4
|
||||||
quote-style = Double
|
quote-style = Double
|
||||||
magic-trailing-comma = Respect
|
magic-trailing-comma = Respect
|
||||||
```
|
```
|
||||||
|
@ -61,6 +62,7 @@ def test():
|
||||||
```
|
```
|
||||||
indent-style = Spaces, size: 2
|
indent-style = Spaces, size: 2
|
||||||
line-width = 88
|
line-width = 88
|
||||||
|
tab-width = 4
|
||||||
quote-style = Double
|
quote-style = Double
|
||||||
magic-trailing-comma = Respect
|
magic-trailing-comma = Respect
|
||||||
```
|
```
|
||||||
|
|
|
@ -66,6 +66,7 @@ formatted;
|
||||||
```
|
```
|
||||||
indent-style = Spaces, size: 4
|
indent-style = Spaces, size: 4
|
||||||
line-width = 88
|
line-width = 88
|
||||||
|
tab-width = 4
|
||||||
quote-style = Double
|
quote-style = Double
|
||||||
magic-trailing-comma = Respect
|
magic-trailing-comma = Respect
|
||||||
```
|
```
|
||||||
|
@ -135,6 +136,7 @@ formatted
|
||||||
```
|
```
|
||||||
indent-style = Spaces, size: 1
|
indent-style = Spaces, size: 1
|
||||||
line-width = 88
|
line-width = 88
|
||||||
|
tab-width = 4
|
||||||
quote-style = Double
|
quote-style = Double
|
||||||
magic-trailing-comma = Respect
|
magic-trailing-comma = Respect
|
||||||
```
|
```
|
||||||
|
@ -204,6 +206,7 @@ formatted
|
||||||
```
|
```
|
||||||
indent-style = Tab
|
indent-style = Tab
|
||||||
line-width = 88
|
line-width = 88
|
||||||
|
tab-width = 4
|
||||||
quote-style = Double
|
quote-style = Double
|
||||||
magic-trailing-comma = Respect
|
magic-trailing-comma = Respect
|
||||||
```
|
```
|
||||||
|
|
|
@ -26,6 +26,7 @@ not_fixed
|
||||||
```
|
```
|
||||||
indent-style = Spaces, size: 4
|
indent-style = Spaces, size: 4
|
||||||
line-width = 88
|
line-width = 88
|
||||||
|
tab-width = 4
|
||||||
quote-style = Double
|
quote-style = Double
|
||||||
magic-trailing-comma = Respect
|
magic-trailing-comma = Respect
|
||||||
```
|
```
|
||||||
|
@ -52,6 +53,7 @@ not_fixed
|
||||||
```
|
```
|
||||||
indent-style = Spaces, size: 2
|
indent-style = Spaces, size: 2
|
||||||
line-width = 88
|
line-width = 88
|
||||||
|
tab-width = 4
|
||||||
quote-style = Double
|
quote-style = Double
|
||||||
magic-trailing-comma = Respect
|
magic-trailing-comma = Respect
|
||||||
```
|
```
|
||||||
|
@ -78,6 +80,7 @@ not_fixed
|
||||||
```
|
```
|
||||||
indent-style = Tab
|
indent-style = Tab
|
||||||
line-width = 88
|
line-width = 88
|
||||||
|
tab-width = 4
|
||||||
quote-style = Double
|
quote-style = Double
|
||||||
magic-trailing-comma = Respect
|
magic-trailing-comma = Respect
|
||||||
```
|
```
|
||||||
|
|
|
@ -44,6 +44,7 @@ with (a,): # magic trailing comma
|
||||||
```
|
```
|
||||||
indent-style = Spaces, size: 4
|
indent-style = Spaces, size: 4
|
||||||
line-width = 88
|
line-width = 88
|
||||||
|
tab-width = 4
|
||||||
quote-style = Double
|
quote-style = Double
|
||||||
magic-trailing-comma = Respect
|
magic-trailing-comma = Respect
|
||||||
```
|
```
|
||||||
|
@ -95,6 +96,7 @@ with (
|
||||||
```
|
```
|
||||||
indent-style = Spaces, size: 4
|
indent-style = Spaces, size: 4
|
||||||
line-width = 88
|
line-width = 88
|
||||||
|
tab-width = 4
|
||||||
quote-style = Double
|
quote-style = Double
|
||||||
magic-trailing-comma = Ignore
|
magic-trailing-comma = Ignore
|
||||||
```
|
```
|
||||||
|
|
|
@ -0,0 +1,69 @@
|
||||||
|
---
|
||||||
|
source: crates/ruff_python_formatter/tests/fixtures.rs
|
||||||
|
input_file: crates/ruff_python_formatter/resources/test/fixtures/ruff/tab_width.py
|
||||||
|
---
|
||||||
|
## Input
|
||||||
|
```py
|
||||||
|
# Fits with tab width 2
|
||||||
|
1 + " 012345678901234567890123456789012345678901234567890123456789012345678901234567890"
|
||||||
|
|
||||||
|
# Fits with tab width 4
|
||||||
|
1 + " 0123456789012345678901234567890123456789012345678901234567890123456789012345678"
|
||||||
|
|
||||||
|
# Fits with tab width 8
|
||||||
|
1 + " 012345678901234567890123456789012345678901234567890123456789012345678901234"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Outputs
|
||||||
|
### Output 1
|
||||||
|
```
|
||||||
|
indent-style = Spaces, size: 4
|
||||||
|
line-width = 88
|
||||||
|
tab-width = 2
|
||||||
|
quote-style = Double
|
||||||
|
magic-trailing-comma = Respect
|
||||||
|
```
|
||||||
|
|
||||||
|
```py
|
||||||
|
# Fits with tab width 2
|
||||||
|
(
|
||||||
|
1
|
||||||
|
+ " 012345678901234567890123456789012345678901234567890123456789012345678901234567890"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Fits with tab width 4
|
||||||
|
1 + " 0123456789012345678901234567890123456789012345678901234567890123456789012345678"
|
||||||
|
|
||||||
|
# Fits with tab width 8
|
||||||
|
1 + " 012345678901234567890123456789012345678901234567890123456789012345678901234"
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### Output 2
|
||||||
|
```
|
||||||
|
indent-style = Spaces, size: 4
|
||||||
|
line-width = 88
|
||||||
|
tab-width = 4
|
||||||
|
quote-style = Double
|
||||||
|
magic-trailing-comma = Respect
|
||||||
|
```
|
||||||
|
|
||||||
|
```py
|
||||||
|
# Fits with tab width 2
|
||||||
|
(
|
||||||
|
1
|
||||||
|
+ " 012345678901234567890123456789012345678901234567890123456789012345678901234567890"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Fits with tab width 4
|
||||||
|
(
|
||||||
|
1
|
||||||
|
+ " 0123456789012345678901234567890123456789012345678901234567890123456789012345678"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Fits with tab width 8
|
||||||
|
1 + " 012345678901234567890123456789012345678901234567890123456789012345678901234"
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue