Skip to content

Commit c269439

Browse files
committed
Auto merge of #12837 - epage:remove, r=weihanglo
fix(remove): Preserve feature comments ### What does this PR try to resolve? We've been having a hard time balancing leaving the feature list in a good looking start and preserving formatting. With our new formatting policy (#12836), we can just choose to preserve formatting instead. Fixes #11743 ### How should we test and review this PR? The first commit copies an existing test. The second is where the fun begins, customizing the test for some weird cases. The follow up commits do the slow walk for improving it. We ended up preserving some line-trailing comments because they come after the comma and toml_edit treats that as part of the prefix of the next item. Tracking removal of that was going to require us to determine if the newline existed in the suffix or in the next item's prefix and edit accordingly and I decided to skip that to keep this initial implementation simpler. ### Additional information
2 parents 220767c + 6281109 commit c269439

File tree

10 files changed

+165
-8
lines changed

10 files changed

+165
-8
lines changed

src/cargo/util/toml_mut/manifest.rs

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -497,12 +497,7 @@ fn fix_feature_activations(
497497

498498
// Remove found idx in revers order so we don't invalidate the idx.
499499
for idx in remove_list.iter().rev() {
500-
feature_values.remove(*idx);
501-
}
502-
if !remove_list.is_empty() {
503-
// HACK: Instead of cleaning up the users formatting from having removed a feature, we just
504-
// re-format the whole feature list
505-
feature_values.fmt();
500+
remove_array_index(feature_values, *idx);
506501
}
507502

508503
if status == DependencyStatus::Required {
@@ -546,3 +541,44 @@ fn non_existent_dependency_err(
546541
) -> anyhow::Error {
547542
anyhow::format_err!("the dependency `{name}` could not be found in `{table}`.")
548543
}
544+
545+
fn remove_array_index(array: &mut toml_edit::Array, index: usize) {
546+
let value = array.remove(index);
547+
548+
// Captures all lines before leading whitespace
549+
let prefix_lines = value
550+
.decor()
551+
.prefix()
552+
.and_then(|p| p.as_str().expect("spans removed").rsplit_once('\n'))
553+
.map(|(lines, _current)| lines);
554+
// Captures all lines after trailing whitespace, before the next comma
555+
let suffix_lines = value
556+
.decor()
557+
.suffix()
558+
.and_then(|p| p.as_str().expect("spans removed").split_once('\n'))
559+
.map(|(_current, lines)| lines);
560+
let mut merged_lines = String::new();
561+
if let Some(prefix_lines) = prefix_lines {
562+
merged_lines.push_str(prefix_lines);
563+
merged_lines.push('\n');
564+
}
565+
if let Some(suffix_lines) = suffix_lines {
566+
merged_lines.push_str(suffix_lines);
567+
merged_lines.push('\n');
568+
}
569+
570+
let next_index = index; // Since `index` was removed, that effectively auto-advances us
571+
if let Some(next) = array.get_mut(next_index) {
572+
let next_decor = next.decor_mut();
573+
let next_prefix = next_decor
574+
.prefix()
575+
.map(|s| s.as_str().expect("spans removed"))
576+
.unwrap_or_default();
577+
merged_lines.push_str(next_prefix);
578+
next_decor.set_prefix(merged_lines);
579+
} else {
580+
let trailing = array.trailing().as_str().expect("spans removed");
581+
merged_lines.push_str(trailing);
582+
array.set_trailing(merged_lines);
583+
}
584+
}

tests/testsuite/cargo_remove/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ mod multiple_dev;
2020
mod no_arg;
2121
mod offline;
2222
mod optional_dep_feature;
23+
mod optional_dep_feature_formatting;
2324
mod optional_feature;
2425
mod package;
2526
mod remove_basic;

tests/testsuite/cargo_remove/multiple_dev/out/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,4 @@ toml = "0.1"
1717
clippy = "0.4"
1818

1919
[features]
20-
std = ["semver/std"]
20+
std = [ "semver/std"]

tests/testsuite/cargo_remove/optional_dep_feature/out/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,4 @@ clippy = "0.4"
2020
regex = "0.1.1"
2121

2222
[features]
23-
std = ["semver/std"]
23+
std = [ "semver/std"]
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
[package]
2+
name = "cargo-remove-test-fixture"
3+
version = "0.1.0"
4+
5+
[[bin]]
6+
name = "main"
7+
path = "src/main.rs"
8+
9+
[build-dependencies]
10+
semver = "0.1.0"
11+
12+
[dependencies]
13+
docopt = { version = "0.6", optional = true }
14+
rustc-serialize = { version = "0.4", optional = true }
15+
semver = "0.1"
16+
toml = { version = "0.1", optional = true }
17+
clippy = { version = "0.4", optional = true }
18+
19+
[dev-dependencies]
20+
regex = "0.1.1"
21+
serde = "1.0.90"
22+
23+
[features]
24+
std = [
25+
# Leading clippy
26+
"dep:clippy", # trailing clippy
27+
28+
# Leading docopt
29+
"dep:docopt", # trailing docopt
30+
31+
# Leading rustc-serialize
32+
"dep:rustc-serialize", # trailing rustc-serialize
33+
34+
# Leading serde/std
35+
"serde/std", # trailing serde/std
36+
37+
# Leading semver/std
38+
"semver/std", # trailing semver/std
39+
40+
# Leading toml
41+
"dep:toml", # trailing toml
42+
]
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
use cargo_test_support::compare::assert_ui;
2+
use cargo_test_support::curr_dir;
3+
use cargo_test_support::CargoCommand;
4+
use cargo_test_support::Project;
5+
6+
#[cargo_test]
7+
fn case() {
8+
cargo_test_support::registry::init();
9+
cargo_test_support::registry::Package::new("clippy", "0.4.0+my-package").publish();
10+
cargo_test_support::registry::Package::new("docopt", "0.6.2+my-package").publish();
11+
cargo_test_support::registry::Package::new("regex", "0.1.1+my-package").publish();
12+
cargo_test_support::registry::Package::new("rustc-serialize", "0.4.0+my-package").publish();
13+
cargo_test_support::registry::Package::new("toml", "0.1.1+my-package").publish();
14+
cargo_test_support::registry::Package::new("semver", "0.1.1")
15+
.feature("std", &[])
16+
.publish();
17+
cargo_test_support::registry::Package::new("serde", "1.0.90")
18+
.feature("std", &[])
19+
.publish();
20+
21+
let project = Project::from_template(curr_dir!().join("in"));
22+
let project_root = project.root();
23+
let cwd = &project_root;
24+
25+
snapbox::cmd::Command::cargo_ui()
26+
.arg("remove")
27+
.args(["docopt", "toml"])
28+
.current_dir(cwd)
29+
.assert()
30+
.success()
31+
.stdout_matches_path(curr_dir!().join("stdout.log"))
32+
.stderr_matches_path(curr_dir!().join("stderr.log"));
33+
34+
assert_ui().subset_matches(curr_dir!().join("out"), &project_root);
35+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
[package]
2+
name = "cargo-remove-test-fixture"
3+
version = "0.1.0"
4+
5+
[[bin]]
6+
name = "main"
7+
path = "src/main.rs"
8+
9+
[build-dependencies]
10+
semver = "0.1.0"
11+
12+
[dependencies]
13+
rustc-serialize = { version = "0.4", optional = true }
14+
semver = "0.1"
15+
clippy = { version = "0.4", optional = true }
16+
17+
[dev-dependencies]
18+
regex = "0.1.1"
19+
serde = "1.0.90"
20+
21+
[features]
22+
std = [
23+
# Leading clippy
24+
"dep:clippy", # trailing clippy
25+
26+
# Leading docopt
27+
# trailing docopt
28+
29+
# Leading rustc-serialize
30+
"dep:rustc-serialize", # trailing rustc-serialize
31+
32+
# Leading serde/std
33+
"serde/std", # trailing serde/std
34+
35+
# Leading semver/std
36+
"semver/std", # trailing semver/std
37+
38+
# Leading toml
39+
# trailing toml
40+
]
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Removing docopt from dependencies
2+
Removing toml from dependencies

tests/testsuite/cargo_remove/optional_dep_feature_formatting/stdout.log

Whitespace-only changes.

0 commit comments

Comments
 (0)