mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-28 12:55:05 +00:00
Alternate quotes for strings inside f-strings in preview (#13860)
This commit is contained in:
parent
f335fe4d4a
commit
2f88f84972
12 changed files with 556 additions and 118 deletions
|
@ -1,12 +1,12 @@
|
|||
use ruff_formatter::write;
|
||||
use ruff_python_ast::{AnyStringFlags, FString, StringFlags};
|
||||
use ruff_source_file::Locator;
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::preview::{
|
||||
is_f_string_formatting_enabled, is_f_string_implicit_concatenated_string_literal_quotes_enabled,
|
||||
};
|
||||
use crate::string::{Quoting, StringNormalizer, StringQuotes};
|
||||
use ruff_formatter::write;
|
||||
use ruff_python_ast::{AnyStringFlags, FString, StringFlags};
|
||||
use ruff_source_file::Locator;
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
use super::f_string_element::FormatFStringElement;
|
||||
|
||||
|
@ -35,7 +35,7 @@ impl Format<PyFormatContext<'_>> for FormatFString<'_> {
|
|||
// f-string instead of globally for the entire f-string expression.
|
||||
let quoting =
|
||||
if is_f_string_implicit_concatenated_string_literal_quotes_enabled(f.context()) {
|
||||
f_string_quoting(self.value, &locator)
|
||||
Quoting::CanChange
|
||||
} else {
|
||||
self.quoting
|
||||
};
|
||||
|
@ -92,17 +92,21 @@ impl Format<PyFormatContext<'_>> for FormatFString<'_> {
|
|||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub(crate) struct FStringContext {
|
||||
flags: AnyStringFlags,
|
||||
/// The string flags of the enclosing f-string part.
|
||||
enclosing_flags: AnyStringFlags,
|
||||
layout: FStringLayout,
|
||||
}
|
||||
|
||||
impl FStringContext {
|
||||
const fn new(flags: AnyStringFlags, layout: FStringLayout) -> Self {
|
||||
Self { flags, layout }
|
||||
Self {
|
||||
enclosing_flags: flags,
|
||||
layout,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn flags(self) -> AnyStringFlags {
|
||||
self.flags
|
||||
self.enclosing_flags
|
||||
}
|
||||
|
||||
pub(crate) const fn layout(self) -> FStringLayout {
|
||||
|
@ -149,20 +153,3 @@ impl FStringLayout {
|
|||
matches!(self, FStringLayout::Multiline)
|
||||
}
|
||||
}
|
||||
|
||||
fn f_string_quoting(f_string: &FString, locator: &Locator) -> Quoting {
|
||||
let triple_quoted = f_string.flags.is_triple_quoted();
|
||||
|
||||
if f_string.elements.expressions().any(|expression| {
|
||||
let string_content = locator.slice(expression.range());
|
||||
if triple_quoted {
|
||||
string_content.contains(r#"""""#) || string_content.contains("'''")
|
||||
} else {
|
||||
string_content.contains(['"', '\''])
|
||||
}
|
||||
}) {
|
||||
Quoting::Preserve
|
||||
} else {
|
||||
Quoting::CanChange
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,8 +2,8 @@ use std::borrow::Cow;
|
|||
|
||||
use ruff_formatter::{format_args, write, Buffer, RemoveSoftLinesBuffer};
|
||||
use ruff_python_ast::{
|
||||
ConversionFlag, Expr, FStringElement, FStringExpressionElement, FStringLiteralElement,
|
||||
StringFlags,
|
||||
AnyStringFlags, ConversionFlag, Expr, FStringElement, FStringExpressionElement,
|
||||
FStringLiteralElement, StringFlags,
|
||||
};
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
|
@ -33,7 +33,7 @@ impl Format<PyFormatContext<'_>> for FormatFStringElement<'_> {
|
|||
fn fmt(&self, f: &mut PyFormatter) -> FormatResult<()> {
|
||||
match self.element {
|
||||
FStringElement::Literal(string_literal) => {
|
||||
FormatFStringLiteralElement::new(string_literal, self.context).fmt(f)
|
||||
FormatFStringLiteralElement::new(string_literal, self.context.flags()).fmt(f)
|
||||
}
|
||||
FStringElement::Expression(expression) => {
|
||||
FormatFStringExpressionElement::new(expression, self.context).fmt(f)
|
||||
|
@ -45,19 +45,23 @@ impl Format<PyFormatContext<'_>> for FormatFStringElement<'_> {
|
|||
/// Formats an f-string literal element.
|
||||
pub(crate) struct FormatFStringLiteralElement<'a> {
|
||||
element: &'a FStringLiteralElement,
|
||||
context: FStringContext,
|
||||
/// Flags of the enclosing F-string part
|
||||
fstring_flags: AnyStringFlags,
|
||||
}
|
||||
|
||||
impl<'a> FormatFStringLiteralElement<'a> {
|
||||
pub(crate) fn new(element: &'a FStringLiteralElement, context: FStringContext) -> Self {
|
||||
Self { element, context }
|
||||
pub(crate) fn new(element: &'a FStringLiteralElement, fstring_flags: AnyStringFlags) -> Self {
|
||||
Self {
|
||||
element,
|
||||
fstring_flags,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Format<PyFormatContext<'_>> for FormatFStringLiteralElement<'_> {
|
||||
fn fmt(&self, f: &mut PyFormatter) -> FormatResult<()> {
|
||||
let literal_content = f.context().locator().slice(self.element.range());
|
||||
let normalized = normalize_string(literal_content, 0, self.context.flags(), true);
|
||||
let normalized = normalize_string(literal_content, 0, self.fstring_flags, true);
|
||||
match &normalized {
|
||||
Cow::Borrowed(_) => source_text_slice(self.element.range()).fmt(f),
|
||||
Cow::Owned(normalized) => text(normalized).fmt(f),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue