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:
Micha Reiser 2023-06-26 14:24:25 +02:00 committed by GitHub
parent f18a1f70de
commit 313711aaf9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 165 additions and 24 deletions

View file

@ -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,10 +225,21 @@ fn preferred_quotes(input: &str, quotes: StringQuotes) -> (StringQuotes, Contain
}
}
if double_quotes > single_quotes {
QuoteStyle::Single
} else {
QuoteStyle::Double
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();

View file

@ -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,