mirror of
https://github.com/astral-sh/ruff.git
synced 2025-07-23 13:05:06 +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
|
# Prefer double quotes for string with equal amount of single and double quotes
|
||||||
'" \' " " \'\''
|
'" \' " " \'\''
|
||||||
"' \" '' \" \" '"
|
"' \" '' \" \""
|
||||||
|
|
||||||
"\\' \"\""
|
"\\' \"\""
|
||||||
'\\\' ""'
|
'\\\' ""'
|
||||||
|
@ -47,6 +47,16 @@ String ""
|
||||||
String """
|
String """
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
'''Multiline
|
||||||
|
String "'''
|
||||||
|
|
||||||
|
"""Multiline
|
||||||
|
String '''
|
||||||
|
"""
|
||||||
|
|
||||||
|
"""Multiline
|
||||||
|
String '"""
|
||||||
|
|
||||||
'''Multiline
|
'''Multiline
|
||||||
String \"\"\"
|
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_range = relative_raw_content_range + self.part_range.start();
|
||||||
|
|
||||||
let raw_content = &string_content[relative_raw_content_range];
|
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])?;
|
write!(f, [prefix, preferred_quotes])?;
|
||||||
|
|
||||||
|
@ -148,14 +149,20 @@ impl Format<PyFormatContext<'_>> for StringPrefix {
|
||||||
/// Detects the preferred quotes for `input`.
|
/// Detects the preferred quotes for `input`.
|
||||||
/// * single quoted strings: The preferred quote style is the one that requires less escape sequences.
|
/// * 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 `"""`.
|
/// * 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 mut contains_newlines = ContainsNewlines::No;
|
||||||
|
|
||||||
let preferred_style = if quotes.triple {
|
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();
|
let mut chars = input.chars().peekable();
|
||||||
|
|
||||||
while let Some(c) = chars.next() {
|
while let Some(c) = chars.next() {
|
||||||
|
let configured_quote_char = configured_style.as_char();
|
||||||
match c {
|
match c {
|
||||||
'\n' | '\r' => contains_newlines = ContainsNewlines::Yes,
|
'\n' | '\r' => contains_newlines = ContainsNewlines::Yes,
|
||||||
'\\' => {
|
'\\' => {
|
||||||
|
@ -163,24 +170,25 @@ fn preferred_quotes(input: &str, quotes: StringQuotes) -> (StringQuotes, Contain
|
||||||
chars.next();
|
chars.next();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
'"' => {
|
// `"` or `'`
|
||||||
|
c if c == configured_quote_char => {
|
||||||
match chars.peek().copied() {
|
match chars.peek().copied() {
|
||||||
Some('"') => {
|
Some(c) if c == configured_quote_char => {
|
||||||
// `""`
|
// `""` or `''`
|
||||||
chars.next();
|
chars.next();
|
||||||
|
|
||||||
if chars.peek().copied() == Some('"') {
|
if chars.peek().copied() == Some(configured_quote_char) {
|
||||||
// `"""`
|
// `"""` or `'''`
|
||||||
chars.next();
|
chars.next();
|
||||||
use_single_quotes = true;
|
uses_triple_quotes = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(_) => {
|
Some(_) => {
|
||||||
// Single quote, this is ok
|
// A single quote char, this is ok
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
// Trailing quote at the end of the comment
|
// 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 {
|
if uses_triple_quotes {
|
||||||
QuoteStyle::Single
|
// String contains a triple quote sequence of the configured quote style.
|
||||||
|
// Keep the existing quote style.
|
||||||
|
quotes.style
|
||||||
} else {
|
} else {
|
||||||
QuoteStyle::Double
|
configured_style
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let mut single_quotes = 0u32;
|
let mut single_quotes = 0u32;
|
||||||
|
@ -215,10 +225,21 @@ fn preferred_quotes(input: &str, quotes: StringQuotes) -> (StringQuotes, Contain
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if double_quotes > single_quotes {
|
match configured_style {
|
||||||
QuoteStyle::Single
|
QuoteStyle::Single => {
|
||||||
} else {
|
if single_quotes > double_quotes {
|
||||||
QuoteStyle::Double
|
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 style = quotes.style;
|
||||||
let preferred_quote = style.as_char();
|
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();
|
let mut chars = input.char_indices();
|
||||||
|
|
||||||
|
|
|
@ -109,7 +109,7 @@ impl QuoteStyle {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn opposite(self) -> QuoteStyle {
|
pub const fn invert(self) -> QuoteStyle {
|
||||||
match self {
|
match self {
|
||||||
QuoteStyle::Single => QuoteStyle::Double,
|
QuoteStyle::Single => QuoteStyle::Double,
|
||||||
QuoteStyle::Double => QuoteStyle::Single,
|
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
|
# Prefer double quotes for string with equal amount of single and double quotes
|
||||||
'" \' " " \'\''
|
'" \' " " \'\''
|
||||||
"' \" '' \" \" '"
|
"' \" '' \" \""
|
||||||
|
|
||||||
"\\' \"\""
|
"\\' \"\""
|
||||||
'\\\' ""'
|
'\\\' ""'
|
||||||
|
@ -53,12 +53,30 @@ String ""
|
||||||
String """
|
String """
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
'''Multiline
|
||||||
|
String "'''
|
||||||
|
|
||||||
|
"""Multiline
|
||||||
|
String '''
|
||||||
|
"""
|
||||||
|
|
||||||
|
"""Multiline
|
||||||
|
String '"""
|
||||||
|
|
||||||
'''Multiline
|
'''Multiline
|
||||||
String \"\"\"
|
String \"\"\"
|
||||||
'''
|
'''
|
||||||
```
|
```
|
||||||
|
|
||||||
## Output
|
## Outputs
|
||||||
|
### Output 1
|
||||||
|
```
|
||||||
|
indent-style = Spaces, size: 4
|
||||||
|
line-width = 88
|
||||||
|
quote-style = Double
|
||||||
|
magic-trailing-comma = Respect
|
||||||
|
```
|
||||||
|
|
||||||
```py
|
```py
|
||||||
"' test"
|
"' test"
|
||||||
'" test'
|
'" test'
|
||||||
|
@ -74,7 +92,7 @@ String \"\"\"
|
||||||
|
|
||||||
# Prefer double quotes for string with equal amount of single and double quotes
|
# Prefer double quotes for string with equal amount of single and double quotes
|
||||||
"\" ' \" \" ''"
|
"\" ' \" \" ''"
|
||||||
"' \" '' \" \" '"
|
"' \" '' \" \""
|
||||||
|
|
||||||
'\\\' ""'
|
'\\\' ""'
|
||||||
'\\\' ""'
|
'\\\' ""'
|
||||||
|
@ -109,10 +127,94 @@ String ""
|
||||||
String """
|
String """
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
'''Multiline
|
||||||
|
String "'''
|
||||||
|
|
||||||
|
"""Multiline
|
||||||
|
String '''
|
||||||
|
"""
|
||||||
|
|
||||||
|
"""Multiline
|
||||||
|
String '"""
|
||||||
|
|
||||||
"""Multiline
|
"""Multiline
|
||||||
String \"\"\"
|
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