mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-29 13:24:57 +00:00
[ruff
] Preserve relative whitespace in multi-line expressions (RUF033
) (#19647)
## Summary Fixes #19581 I decided to add in a `indent_first_line` function into [`textwrap.rs`](https://github.com/astral-sh/ruff/blob/main/crates/ruff_python_trivia/src/textwrap.rs), as it solely focuses on text manipulation utilities. It follows the same design as `indent()`, and there may be situations in the future where it can be reused as well. --------- Co-authored-by: Brent Westbrook <36778786+ntBre@users.noreply.github.com> Co-authored-by: Brent Westbrook <brentrwestbrook@gmail.com>
This commit is contained in:
parent
4b80f5fa4f
commit
89ca493fd9
4 changed files with 186 additions and 1 deletions
|
@ -71,6 +71,66 @@ pub fn indent<'a>(text: &'a str, prefix: &str) -> Cow<'a, str> {
|
|||
Cow::Owned(result)
|
||||
}
|
||||
|
||||
/// Indent only the first line by the given prefix.
|
||||
///
|
||||
/// This function is useful when you want to indent the first line of a multi-line
|
||||
/// expression while preserving the relative indentation of subsequent lines.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use ruff_python_trivia::textwrap::indent_first_line;
|
||||
///
|
||||
/// assert_eq!(indent_first_line("First line.\nSecond line.\n", " "),
|
||||
/// " First line.\nSecond line.\n");
|
||||
/// ```
|
||||
///
|
||||
/// When indenting, trailing whitespace is stripped from the prefix.
|
||||
/// This means that empty lines remain empty afterwards:
|
||||
///
|
||||
/// ```
|
||||
/// # use ruff_python_trivia::textwrap::indent_first_line;
|
||||
///
|
||||
/// assert_eq!(indent_first_line("\n\n\nSecond line.\n", " "),
|
||||
/// "\n\n\nSecond line.\n");
|
||||
/// ```
|
||||
///
|
||||
/// Leading and trailing whitespace coming from the text itself is
|
||||
/// kept unchanged:
|
||||
///
|
||||
/// ```
|
||||
/// # use ruff_python_trivia::textwrap::indent_first_line;
|
||||
///
|
||||
/// assert_eq!(indent_first_line(" \t Foo ", "->"), "-> \t Foo ");
|
||||
/// ```
|
||||
pub fn indent_first_line<'a>(text: &'a str, prefix: &str) -> Cow<'a, str> {
|
||||
if prefix.is_empty() {
|
||||
return Cow::Borrowed(text);
|
||||
}
|
||||
|
||||
let mut lines = text.universal_newlines();
|
||||
let Some(first_line) = lines.next() else {
|
||||
return Cow::Borrowed(text);
|
||||
};
|
||||
|
||||
let mut result = String::with_capacity(text.len() + prefix.len());
|
||||
|
||||
// Indent only the first line
|
||||
if first_line.trim_whitespace().is_empty() {
|
||||
result.push_str(prefix.trim_whitespace_end());
|
||||
} else {
|
||||
result.push_str(prefix);
|
||||
}
|
||||
result.push_str(first_line.as_full_str());
|
||||
|
||||
// Add remaining lines without indentation
|
||||
for line in lines {
|
||||
result.push_str(line.as_full_str());
|
||||
}
|
||||
|
||||
Cow::Owned(result)
|
||||
}
|
||||
|
||||
/// Removes common leading whitespace from each line.
|
||||
///
|
||||
/// This function will look at each non-empty line and determine the
|
||||
|
@ -409,6 +469,61 @@ mod tests {
|
|||
assert_eq!(dedent(text), text);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn indent_first_line_empty() {
|
||||
assert_eq!(indent_first_line("\n", " "), "\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[rustfmt::skip]
|
||||
fn indent_first_line_nonempty() {
|
||||
let text = [
|
||||
" foo\n",
|
||||
"bar\n",
|
||||
" baz\n",
|
||||
].join("");
|
||||
let expected = [
|
||||
"// foo\n",
|
||||
"bar\n",
|
||||
" baz\n",
|
||||
].join("");
|
||||
assert_eq!(indent_first_line(&text, "// "), expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[rustfmt::skip]
|
||||
fn indent_first_line_empty_line() {
|
||||
let text = [
|
||||
" foo",
|
||||
"bar",
|
||||
"",
|
||||
" baz",
|
||||
].join("\n");
|
||||
let expected = [
|
||||
"// foo",
|
||||
"bar",
|
||||
"",
|
||||
" baz",
|
||||
].join("\n");
|
||||
assert_eq!(indent_first_line(&text, "// "), expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[rustfmt::skip]
|
||||
fn indent_first_line_mixed_newlines() {
|
||||
let text = [
|
||||
" foo\r\n",
|
||||
"bar\n",
|
||||
" baz\r",
|
||||
].join("");
|
||||
let expected = [
|
||||
"// foo\r\n",
|
||||
"bar\n",
|
||||
" baz\r",
|
||||
].join("");
|
||||
assert_eq!(indent_first_line(&text, "// "), expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[rustfmt::skip]
|
||||
fn adjust_indent() {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue