mirror of
https://github.com/astral-sh/ruff.git
synced 2025-07-23 21:15:19 +00:00
Prefer the configured quote style
<!-- Thank you for contributing to Ruff! To help us out with reviewing, please consider the following: - Does this pull request include a summary of the change? (See below.) - Does this pull request include a descriptive title? - Does this pull request include references to any relevant issues? --> ## Summary This PR extends the string formatting to respect the configured quote style. <!-- What's the purpose of the change? What does it do, and why? --> ## Test Plan Extended the string test with new cases and set it up to run twice: Once with the `quote_style: Doube`, and once with `quote_style: Single` single and double quotes. <!-- How was it tested? -->
This commit is contained in:
parent
f18a1f70de
commit
313711aaf9
5 changed files with 165 additions and 24 deletions
8
crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/string.options.json
vendored
Normal file
8
crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/string.options.json
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
[
|
||||
{
|
||||
"quote_style": "double"
|
||||
},
|
||||
{
|
||||
"quote_style": "single"
|
||||
}
|
||||
]
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
# Prefer double quotes for string with equal amount of single and double quotes
|
||||
'" \' " " \'\''
|
||||
"' \" '' \" \" '"
|
||||
"' \" '' \" \""
|
||||
|
||||
"\\' \"\""
|
||||
'\\\' ""'
|
||||
|
@ -47,6 +47,16 @@ String ""
|
|||
String """
|
||||
'''
|
||||
|
||||
'''Multiline
|
||||
String "'''
|
||||
|
||||
"""Multiline
|
||||
String '''
|
||||
"""
|
||||
|
||||
"""Multiline
|
||||
String '"""
|
||||
|
||||
'''Multiline
|
||||
String \"\"\"
|
||||
'''
|
||||
|
|
|
@ -58,7 +58,8 @@ impl Format<PyFormatContext<'_>> for FormatStringPart {
|
|||
let raw_content_range = relative_raw_content_range + self.part_range.start();
|
||||
|
||||
let raw_content = &string_content[relative_raw_content_range];
|
||||
let (preferred_quotes, contains_newlines) = preferred_quotes(raw_content, quotes);
|
||||
let (preferred_quotes, contains_newlines) =
|
||||
preferred_quotes(raw_content, quotes, f.options().quote_style());
|
||||
|
||||
write!(f, [prefix, preferred_quotes])?;
|
||||
|
||||
|
@ -148,14 +149,20 @@ impl Format<PyFormatContext<'_>> for StringPrefix {
|
|||
/// Detects the preferred quotes for `input`.
|
||||
/// * single quoted strings: The preferred quote style is the one that requires less escape sequences.
|
||||
/// * triple quoted strings: Use double quotes except the string contains a sequence of `"""`.
|
||||
fn preferred_quotes(input: &str, quotes: StringQuotes) -> (StringQuotes, ContainsNewlines) {
|
||||
fn preferred_quotes(
|
||||
input: &str,
|
||||
quotes: StringQuotes,
|
||||
configured_style: QuoteStyle,
|
||||
) -> (StringQuotes, ContainsNewlines) {
|
||||
let mut contains_newlines = ContainsNewlines::No;
|
||||
|
||||
let preferred_style = if quotes.triple {
|
||||
let mut use_single_quotes = false;
|
||||
// True if the string contains a triple quote sequence of the configured quote style.
|
||||
let mut uses_triple_quotes = false;
|
||||
let mut chars = input.chars().peekable();
|
||||
|
||||
while let Some(c) = chars.next() {
|
||||
let configured_quote_char = configured_style.as_char();
|
||||
match c {
|
||||
'\n' | '\r' => contains_newlines = ContainsNewlines::Yes,
|
||||
'\\' => {
|
||||
|
@ -163,24 +170,25 @@ fn preferred_quotes(input: &str, quotes: StringQuotes) -> (StringQuotes, Contain
|
|||
chars.next();
|
||||
}
|
||||
}
|
||||
'"' => {
|
||||
// `"` or `'`
|
||||
c if c == configured_quote_char => {
|
||||
match chars.peek().copied() {
|
||||
Some('"') => {
|
||||
// `""`
|
||||
Some(c) if c == configured_quote_char => {
|
||||
// `""` or `''`
|
||||
chars.next();
|
||||
|
||||
if chars.peek().copied() == Some('"') {
|
||||
// `"""`
|
||||
if chars.peek().copied() == Some(configured_quote_char) {
|
||||
// `"""` or `'''`
|
||||
chars.next();
|
||||
use_single_quotes = true;
|
||||
uses_triple_quotes = true;
|
||||
}
|
||||
}
|
||||
Some(_) => {
|
||||
// Single quote, this is ok
|
||||
// A single quote char, this is ok
|
||||
}
|
||||
None => {
|
||||
// Trailing quote at the end of the comment
|
||||
use_single_quotes = true;
|
||||
uses_triple_quotes = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -188,10 +196,12 @@ fn preferred_quotes(input: &str, quotes: StringQuotes) -> (StringQuotes, Contain
|
|||
}
|
||||
}
|
||||
|
||||
if use_single_quotes {
|
||||
QuoteStyle::Single
|
||||
if uses_triple_quotes {
|
||||
// String contains a triple quote sequence of the configured quote style.
|
||||
// Keep the existing quote style.
|
||||
quotes.style
|
||||
} else {
|
||||
QuoteStyle::Double
|
||||
configured_style
|
||||
}
|
||||
} else {
|
||||
let mut single_quotes = 0u32;
|
||||
|
@ -215,11 +225,22 @@ fn preferred_quotes(input: &str, quotes: StringQuotes) -> (StringQuotes, Contain
|
|||
}
|
||||
}
|
||||
|
||||
match configured_style {
|
||||
QuoteStyle::Single => {
|
||||
if single_quotes > double_quotes {
|
||||
QuoteStyle::Double
|
||||
} else {
|
||||
QuoteStyle::Single
|
||||
}
|
||||
}
|
||||
QuoteStyle::Double => {
|
||||
if double_quotes > single_quotes {
|
||||
QuoteStyle::Single
|
||||
} else {
|
||||
QuoteStyle::Double
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
(
|
||||
|
@ -286,7 +307,7 @@ fn normalize_quotes(input: &str, quotes: StringQuotes) -> Cow<str> {
|
|||
|
||||
let style = quotes.style;
|
||||
let preferred_quote = style.as_char();
|
||||
let opposite_quote = style.opposite().as_char();
|
||||
let opposite_quote = style.invert().as_char();
|
||||
|
||||
let mut chars = input.char_indices();
|
||||
|
||||
|
|
|
@ -109,7 +109,7 @@ impl QuoteStyle {
|
|||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn opposite(self) -> QuoteStyle {
|
||||
pub const fn invert(self) -> QuoteStyle {
|
||||
match self {
|
||||
QuoteStyle::Single => QuoteStyle::Double,
|
||||
QuoteStyle::Double => QuoteStyle::Single,
|
||||
|
|
|
@ -18,7 +18,7 @@ input_file: crates/ruff_python_formatter/resources/test/fixtures/ruff/expression
|
|||
|
||||
# Prefer double quotes for string with equal amount of single and double quotes
|
||||
'" \' " " \'\''
|
||||
"' \" '' \" \" '"
|
||||
"' \" '' \" \""
|
||||
|
||||
"\\' \"\""
|
||||
'\\\' ""'
|
||||
|
@ -53,12 +53,30 @@ String ""
|
|||
String """
|
||||
'''
|
||||
|
||||
'''Multiline
|
||||
String "'''
|
||||
|
||||
"""Multiline
|
||||
String '''
|
||||
"""
|
||||
|
||||
"""Multiline
|
||||
String '"""
|
||||
|
||||
'''Multiline
|
||||
String \"\"\"
|
||||
'''
|
||||
```
|
||||
|
||||
## Output
|
||||
## Outputs
|
||||
### Output 1
|
||||
```
|
||||
indent-style = Spaces, size: 4
|
||||
line-width = 88
|
||||
quote-style = Double
|
||||
magic-trailing-comma = Respect
|
||||
```
|
||||
|
||||
```py
|
||||
"' test"
|
||||
'" test'
|
||||
|
@ -74,7 +92,7 @@ String \"\"\"
|
|||
|
||||
# Prefer double quotes for string with equal amount of single and double quotes
|
||||
"\" ' \" \" ''"
|
||||
"' \" '' \" \" '"
|
||||
"' \" '' \" \""
|
||||
|
||||
'\\\' ""'
|
||||
'\\\' ""'
|
||||
|
@ -109,10 +127,94 @@ String ""
|
|||
String """
|
||||
'''
|
||||
|
||||
'''Multiline
|
||||
String "'''
|
||||
|
||||
"""Multiline
|
||||
String '''
|
||||
"""
|
||||
|
||||
"""Multiline
|
||||
String '"""
|
||||
|
||||
"""Multiline
|
||||
String \"\"\"
|
||||
"""
|
||||
```
|
||||
|
||||
|
||||
### Output 2
|
||||
```
|
||||
indent-style = Spaces, size: 4
|
||||
line-width = 88
|
||||
quote-style = Single
|
||||
magic-trailing-comma = Respect
|
||||
```
|
||||
|
||||
```py
|
||||
"' test"
|
||||
'" test'
|
||||
|
||||
'" test'
|
||||
"' test"
|
||||
|
||||
# Prefer single quotes for string with more double quotes
|
||||
'\' " " \'\' " " \''
|
||||
|
||||
# Prefer double quotes for string with more single quotes
|
||||
'\' " " \'\' " " \''
|
||||
|
||||
# Prefer double quotes for string with equal amount of single and double quotes
|
||||
'" \' " " \'\''
|
||||
'\' " \'\' " "'
|
||||
|
||||
'\\\' ""'
|
||||
'\\\' ""'
|
||||
|
||||
|
||||
'Test'
|
||||
'Test'
|
||||
|
||||
r'Test'
|
||||
R'Test'
|
||||
|
||||
'This string will not include \
|
||||
backslashes or newline characters.'
|
||||
|
||||
if True:
|
||||
'This string will not include \
|
||||
backslashes or newline characters.'
|
||||
|
||||
'''Multiline
|
||||
String \"
|
||||
'''
|
||||
|
||||
'''Multiline
|
||||
String \'
|
||||
'''
|
||||
|
||||
'''Multiline
|
||||
String ""
|
||||
'''
|
||||
|
||||
'''Multiline
|
||||
String """
|
||||
'''
|
||||
|
||||
'''Multiline
|
||||
String "'''
|
||||
|
||||
"""Multiline
|
||||
String '''
|
||||
"""
|
||||
|
||||
"""Multiline
|
||||
String '"""
|
||||
|
||||
'''Multiline
|
||||
String \"\"\"
|
||||
'''
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue