diff --git a/src/cargo/util/toml_mut/manifest.rs b/src/cargo/util/toml_mut/manifest.rs
index 79fd24029de..03f43afe1d4 100644
--- a/src/cargo/util/toml_mut/manifest.rs
+++ b/src/cargo/util/toml_mut/manifest.rs
@@ -300,7 +300,7 @@ impl LocalManifest {
/// Write changes back to the file.
pub fn write(&self) -> CargoResult<()> {
let mut manifest = self.manifest.data.to_string();
- let raw = match self.embedded.as_ref() {
+ let mut raw = match self.embedded.as_ref() {
Some(Embedded::Implicit(start)) => {
if !manifest.ends_with("\n") {
manifest.push_str("\n");
@@ -321,6 +321,11 @@ impl LocalManifest {
}
None => manifest,
};
+
+ if self.is_crlf() {
+ raw = to_crlf_line_ending(&raw);
+ }
+
let new_contents_bytes = raw.as_bytes();
cargo_util::paths::write_atomic(&self.path, new_contents_bytes)
@@ -566,6 +571,10 @@ impl LocalManifest {
}
status
}
+
+ fn is_crlf(&self) -> bool {
+ return self.raw.contains("\r\n");
+ }
}
impl std::fmt::Display for LocalManifest {
@@ -753,3 +762,25 @@ fn remove_array_index(array: &mut toml_edit::Array, index: usize) {
array.set_trailing(merged_lines);
}
}
+
+fn to_crlf_line_ending(input: &str) -> String {
+ let mut result = String::with_capacity(input.len());
+ let mut chars = input.chars().peekable();
+
+ while let Some(c) = chars.next() {
+ match c {
+ '\r' if chars.peek() == Some(&'\n') => {
+ chars.next();
+ result.push('\r');
+ result.push('\n');
+ }
+ '\n' => {
+ result.push('\r');
+ result.push('\n');
+ }
+ _ => result.push(c),
+ }
+ }
+
+ result
+}
diff --git a/tests/testsuite/cargo_add/mod.rs b/tests/testsuite/cargo_add/mod.rs
index 35ab71346c6..28ba890bc9c 100644
--- a/tests/testsuite/cargo_add/mod.rs
+++ b/tests/testsuite/cargo_add/mod.rs
@@ -120,6 +120,7 @@ mod path_base_unstable;
mod path_dev;
mod path_inferred_name;
mod path_inferred_name_conflicts_full_feature;
+mod preserve_carriage_returns;
mod preserve_dep_std_table;
mod preserve_features_sorted;
mod preserve_features_table;
diff --git a/tests/testsuite/cargo_add/preserve_carriage_returns/in/Cargo.toml b/tests/testsuite/cargo_add/preserve_carriage_returns/in/Cargo.toml
new file mode 100644
index 00000000000..48c5359a5ba
--- /dev/null
+++ b/tests/testsuite/cargo_add/preserve_carriage_returns/in/Cargo.toml
@@ -0,0 +1,6 @@
+[workspace]
+
+[package]
+name = "cargo-list-test-fixture"
+version = "0.0.0"
+edition = "2015"
diff --git a/tests/testsuite/cargo_add/preserve_carriage_returns/in/src/lib.rs b/tests/testsuite/cargo_add/preserve_carriage_returns/in/src/lib.rs
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/tests/testsuite/cargo_add/preserve_carriage_returns/mod.rs b/tests/testsuite/cargo_add/preserve_carriage_returns/mod.rs
new file mode 100644
index 00000000000..59e64083de0
--- /dev/null
+++ b/tests/testsuite/cargo_add/preserve_carriage_returns/mod.rs
@@ -0,0 +1,36 @@
+use cargo_test_support::compare::assert_ui;
+use cargo_test_support::current_dir;
+use cargo_test_support::file;
+use cargo_test_support::prelude::*;
+use cargo_test_support::str;
+use cargo_test_support::Project;
+
+#[cargo_test]
+fn case() {
+ cargo_test_support::registry::init();
+ cargo_test_support::registry::Package::new("my-package", "0.1.0").publish();
+
+ let project = Project::from_template(current_dir!().join("in"));
+ let project_root = project.root();
+ let cwd = &project_root;
+
+ snapbox::cmd::Command::cargo_ui()
+ .arg("add")
+ .arg_line("my-package")
+ .current_dir(cwd)
+ .assert()
+ .success()
+ .stdout_eq(str![""])
+ .stderr_eq(file!["stderr.term.svg"]);
+
+ // Verify the content matches first (for nicer error output)
+ assert_ui().subset_matches(current_dir!().join("out"), &project_root);
+
+ // Snapbox normalizes lines so we also need to do a string comparision to verify line endings
+ let expected = current_dir!().join("out/Cargo.toml");
+ let actual = project_root.join("Cargo.toml");
+ assert_eq!(
+ std::fs::read_to_string(expected).unwrap(),
+ std::fs::read_to_string(actual).unwrap()
+ );
+}
diff --git a/tests/testsuite/cargo_add/preserve_carriage_returns/out/Cargo.toml b/tests/testsuite/cargo_add/preserve_carriage_returns/out/Cargo.toml
new file mode 100644
index 00000000000..f9c0a3d7665
--- /dev/null
+++ b/tests/testsuite/cargo_add/preserve_carriage_returns/out/Cargo.toml
@@ -0,0 +1,9 @@
+[workspace]
+
+[package]
+name = "cargo-list-test-fixture"
+version = "0.0.0"
+edition = "2015"
+
+[dependencies]
+my-package = "0.1.0"
diff --git a/tests/testsuite/cargo_add/preserve_carriage_returns/stderr.term.svg b/tests/testsuite/cargo_add/preserve_carriage_returns/stderr.term.svg
new file mode 100644
index 00000000000..eaa9c860056
--- /dev/null
+++ b/tests/testsuite/cargo_add/preserve_carriage_returns/stderr.term.svg
@@ -0,0 +1,31 @@
+
diff --git a/tests/testsuite/cargo_remove/mod.rs b/tests/testsuite/cargo_remove/mod.rs
index 3510ece4b40..d9d77a3994f 100644
--- a/tests/testsuite/cargo_remove/mod.rs
+++ b/tests/testsuite/cargo_remove/mod.rs
@@ -24,6 +24,7 @@ mod optional_dep_feature;
mod optional_dep_feature_formatting;
mod optional_feature;
mod package;
+mod preserve_carriage_returns;
mod remove_basic;
mod script;
mod script_last;
diff --git a/tests/testsuite/cargo_remove/preserve_carriage_returns/in/Cargo.toml b/tests/testsuite/cargo_remove/preserve_carriage_returns/in/Cargo.toml
new file mode 100644
index 00000000000..f9c0a3d7665
--- /dev/null
+++ b/tests/testsuite/cargo_remove/preserve_carriage_returns/in/Cargo.toml
@@ -0,0 +1,9 @@
+[workspace]
+
+[package]
+name = "cargo-list-test-fixture"
+version = "0.0.0"
+edition = "2015"
+
+[dependencies]
+my-package = "0.1.0"
diff --git a/tests/testsuite/cargo_remove/preserve_carriage_returns/in/src/lib.rs b/tests/testsuite/cargo_remove/preserve_carriage_returns/in/src/lib.rs
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/tests/testsuite/cargo_remove/preserve_carriage_returns/mod.rs b/tests/testsuite/cargo_remove/preserve_carriage_returns/mod.rs
new file mode 100644
index 00000000000..08b812822af
--- /dev/null
+++ b/tests/testsuite/cargo_remove/preserve_carriage_returns/mod.rs
@@ -0,0 +1,36 @@
+use cargo_test_support::compare::assert_ui;
+use cargo_test_support::current_dir;
+use cargo_test_support::file;
+use cargo_test_support::prelude::*;
+use cargo_test_support::str;
+use cargo_test_support::Project;
+
+#[cargo_test]
+fn case() {
+ cargo_test_support::registry::init();
+ cargo_test_support::registry::Package::new("my-package", "0.1.0").publish();
+
+ let project = Project::from_template(current_dir!().join("in"));
+ let project_root = project.root();
+ let cwd = &project_root;
+
+ snapbox::cmd::Command::cargo_ui()
+ .arg("remove")
+ .arg_line("my-package")
+ .current_dir(cwd)
+ .assert()
+ .success()
+ .stdout_eq(str![""])
+ .stderr_eq(file!["stderr.term.svg"]);
+
+ // Verify the content matches first (for nicer error output)
+ assert_ui().subset_matches(current_dir!().join("out"), &project_root);
+
+ // Snapbox normalizes lines so we also need to do a string comparision to verify line endings
+ let expected = current_dir!().join("out/Cargo.toml");
+ let actual = project_root.join("Cargo.toml");
+ assert_eq!(
+ std::fs::read_to_string(expected).unwrap(),
+ std::fs::read_to_string(actual).unwrap()
+ );
+}
diff --git a/tests/testsuite/cargo_remove/preserve_carriage_returns/out/Cargo.toml b/tests/testsuite/cargo_remove/preserve_carriage_returns/out/Cargo.toml
new file mode 100644
index 00000000000..48c5359a5ba
--- /dev/null
+++ b/tests/testsuite/cargo_remove/preserve_carriage_returns/out/Cargo.toml
@@ -0,0 +1,6 @@
+[workspace]
+
+[package]
+name = "cargo-list-test-fixture"
+version = "0.0.0"
+edition = "2015"
diff --git a/tests/testsuite/cargo_remove/preserve_carriage_returns/stderr.term.svg b/tests/testsuite/cargo_remove/preserve_carriage_returns/stderr.term.svg
new file mode 100644
index 00000000000..583ddb69f38
--- /dev/null
+++ b/tests/testsuite/cargo_remove/preserve_carriage_returns/stderr.term.svg
@@ -0,0 +1,27 @@
+