mirror of https://github.com/astral-sh/uv
Preserve end-of-line comment whitespace when editing `pyproject.toml` (#16734)
Resolves https://github.com/astral-sh/uv/issues/16719 `uv add` collapses multiple spaces before inline comments in `[project.dependencies]`, causing unrelated diffs and moving comments onto the wrong columns. This diff captures the exact whitespace padding that preceded each end-of-line comment when parsing the array and reuses it when formatting. --------- Co-authored-by: konstin <konstin@mailbox.org>
This commit is contained in:
parent
5b4446f086
commit
79bfa2b4cd
|
|
@ -65,18 +65,18 @@ pub enum ArrayEdit {
|
||||||
Add(usize),
|
Add(usize),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
enum CommentType {
|
enum CommentType {
|
||||||
/// A comment that appears on its own line.
|
/// A comment that appears on its own line.
|
||||||
OwnLine,
|
OwnLine,
|
||||||
/// A comment that appears at the end of a line.
|
/// A comment that appears at the end of a line.
|
||||||
EndOfLine,
|
EndOfLine { leading_whitespace: String },
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
struct Comment {
|
struct Comment {
|
||||||
text: String,
|
text: String,
|
||||||
comment_type: CommentType,
|
kind: CommentType,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ArrayEdit {
|
impl ArrayEdit {
|
||||||
|
|
@ -1627,18 +1627,31 @@ fn reformat_array_multiline(deps: &mut Array) {
|
||||||
(false, false),
|
(false, false),
|
||||||
|(prev_line_was_empty, prev_line_was_comment), line| {
|
|(prev_line_was_empty, prev_line_was_comment), line| {
|
||||||
let trimmed_line = line.trim();
|
let trimmed_line = line.trim();
|
||||||
if let Some(index) = trimmed_line.find('#') {
|
|
||||||
let comment_text = trimmed_line[index..].trim().to_string();
|
if let Some((before, comment)) = line.split_once('#') {
|
||||||
let comment_type = if (*prev_line_was_empty) || (*prev_line_was_comment) {
|
let comment_text = format!("#{}", comment.trim_end());
|
||||||
|
|
||||||
|
let comment_kind = if (*prev_line_was_empty) || (*prev_line_was_comment) {
|
||||||
CommentType::OwnLine
|
CommentType::OwnLine
|
||||||
} else {
|
} else {
|
||||||
CommentType::EndOfLine
|
CommentType::EndOfLine {
|
||||||
|
leading_whitespace: before
|
||||||
|
.chars()
|
||||||
|
.rev()
|
||||||
|
.take_while(|c| c.is_whitespace())
|
||||||
|
.collect::<String>()
|
||||||
|
.chars()
|
||||||
|
.rev()
|
||||||
|
.collect(),
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
*prev_line_was_empty = trimmed_line.is_empty();
|
*prev_line_was_empty = trimmed_line.is_empty();
|
||||||
*prev_line_was_comment = true;
|
*prev_line_was_comment = true;
|
||||||
|
|
||||||
Some(Some(Comment {
|
Some(Some(Comment {
|
||||||
text: comment_text,
|
text: comment_text,
|
||||||
comment_type,
|
kind: comment_kind,
|
||||||
}))
|
}))
|
||||||
} else {
|
} else {
|
||||||
*prev_line_was_empty = trimmed_line.is_empty();
|
*prev_line_was_empty = trimmed_line.is_empty();
|
||||||
|
|
@ -1678,12 +1691,12 @@ fn reformat_array_multiline(deps: &mut Array) {
|
||||||
let mut prefix = String::new();
|
let mut prefix = String::new();
|
||||||
|
|
||||||
for comment in find_comments(decor.prefix()).chain(find_comments(decor.suffix())) {
|
for comment in find_comments(decor.prefix()).chain(find_comments(decor.suffix())) {
|
||||||
match comment.comment_type {
|
match &comment.kind {
|
||||||
CommentType::OwnLine => {
|
CommentType::OwnLine => {
|
||||||
prefix.push_str(&indentation_prefix_str);
|
prefix.push_str(&indentation_prefix_str);
|
||||||
}
|
}
|
||||||
CommentType::EndOfLine => {
|
CommentType::EndOfLine { leading_whitespace } => {
|
||||||
prefix.push(' ');
|
prefix.push_str(leading_whitespace);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
prefix.push_str(&comment.text);
|
prefix.push_str(&comment.text);
|
||||||
|
|
@ -1698,14 +1711,14 @@ fn reformat_array_multiline(deps: &mut Array) {
|
||||||
let mut rv = String::new();
|
let mut rv = String::new();
|
||||||
if comments.peek().is_some() {
|
if comments.peek().is_some() {
|
||||||
for comment in comments {
|
for comment in comments {
|
||||||
match comment.comment_type {
|
match &comment.kind {
|
||||||
CommentType::OwnLine => {
|
CommentType::OwnLine => {
|
||||||
let indentation_prefix_str =
|
let indentation_prefix_str =
|
||||||
format!("\n{}", indentation_prefix.as_deref().unwrap_or(" "));
|
format!("\n{}", indentation_prefix.as_deref().unwrap_or(" "));
|
||||||
rv.push_str(&indentation_prefix_str);
|
rv.push_str(&indentation_prefix_str);
|
||||||
}
|
}
|
||||||
CommentType::EndOfLine => {
|
CommentType::EndOfLine { leading_whitespace } => {
|
||||||
rv.push(' ');
|
rv.push_str(leading_whitespace);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rv.push_str(&comment.text);
|
rv.push_str(&comment.text);
|
||||||
|
|
@ -1737,8 +1750,9 @@ fn split_specifiers(req: &str) -> (&str, &str) {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::{AddBoundsKind, split_specifiers};
|
use super::{AddBoundsKind, reformat_array_multiline, split_specifiers};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
use toml_edit::DocumentMut;
|
||||||
use uv_pep440::Version;
|
use uv_pep440::Version;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -1761,6 +1775,56 @@ mod test {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn reformat_preserves_inline_comment_spacing() {
|
||||||
|
let mut doc: DocumentMut = r#"
|
||||||
|
[project]
|
||||||
|
dependencies = [
|
||||||
|
"attrs>=25.4.0", # comment
|
||||||
|
]
|
||||||
|
"#
|
||||||
|
.parse()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
reformat_array_multiline(
|
||||||
|
doc["project"]["dependencies"]
|
||||||
|
.as_array_mut()
|
||||||
|
.expect("dependencies array"),
|
||||||
|
);
|
||||||
|
|
||||||
|
let serialized = doc.to_string();
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
serialized.contains("\"attrs>=25.4.0\", # comment"),
|
||||||
|
"inline comment spacing should be preserved:\n{serialized}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn reformat_preserves_inline_comment_without_padding() {
|
||||||
|
let mut doc: DocumentMut = r#"
|
||||||
|
[project]
|
||||||
|
dependencies = [
|
||||||
|
"attrs>=25.4.0",#comment
|
||||||
|
]
|
||||||
|
"#
|
||||||
|
.parse()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
reformat_array_multiline(
|
||||||
|
doc["project"]["dependencies"]
|
||||||
|
.as_array_mut()
|
||||||
|
.expect("dependencies array"),
|
||||||
|
);
|
||||||
|
|
||||||
|
let serialized = doc.to_string();
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
serialized.contains("\"attrs>=25.4.0\",#comment"),
|
||||||
|
"inline comment spacing without padding should be preserved:\n{serialized}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn bound_kind_to_specifiers_exact() {
|
fn bound_kind_to_specifiers_exact() {
|
||||||
let tests = [
|
let tests = [
|
||||||
|
|
|
||||||
|
|
@ -11498,7 +11498,7 @@ fn add_preserves_trailing_depth() -> Result<()> {
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyio==3.7.0",
|
"anyio==3.7.0",
|
||||||
"idna",
|
"idna",
|
||||||
"iniconfig", # Use iniconfig.
|
"iniconfig",# Use iniconfig.
|
||||||
# First line.
|
# First line.
|
||||||
# Second line.
|
# Second line.
|
||||||
]
|
]
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue