diff --git a/Cargo.lock b/Cargo.lock index 1483bec7f2f..102c7550424 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -260,7 +260,7 @@ dependencies = [ "crossbeam-utils 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -912,7 +912,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -979,7 +979,7 @@ dependencies = [ [[package]] name = "racer" -version = "2.1.4" +version = "2.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -990,7 +990,7 @@ dependencies = [ "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", "rls-span 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-ap-syntax 218.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-ap-syntax 237.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1099,7 +1099,7 @@ dependencies = [ "log 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "ordslice 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "racer 2.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "racer 2.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "rayon 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "regex 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "rls-analysis 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1109,7 +1109,7 @@ dependencies = [ "rls-span 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "rls-vfs 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-workspace-hack 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rustfmt-nightly 0.99.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rustfmt-nightly 0.99.4 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.71 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.71 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1173,15 +1173,15 @@ dependencies = [ [[package]] name = "rustc-ap-arena" -version = "218.0.0" +version = "237.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "rustc-ap-rustc_data_structures 218.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-ap-rustc_data_structures 237.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "rustc-ap-rustc_cratesio_shim" -version = "218.0.0" +version = "237.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1190,7 +1190,7 @@ dependencies = [ [[package]] name = "rustc-ap-rustc_data_structures" -version = "218.0.0" +version = "237.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1198,67 +1198,72 @@ dependencies = [ "log 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot_core 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-ap-rustc_cratesio_shim 218.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-ap-serialize 218.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-ap-rustc_cratesio_shim 237.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-ap-serialize 237.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-rayon 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-rayon-core 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "rustc-ap-rustc_errors" -version = "218.0.0" +version = "237.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-ap-rustc_data_structures 218.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-ap-serialize 218.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-ap-syntax_pos 218.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-ap-rustc_data_structures 237.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-ap-serialize 237.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-ap-syntax_pos 237.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "termcolor 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "rustc-ap-rustc_target" -version = "218.0.0" +version = "237.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-ap-rustc_cratesio_shim 218.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-ap-serialize 218.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-ap-rustc_cratesio_shim 237.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-ap-serialize 237.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "rustc-ap-serialize" -version = "218.0.0" +version = "237.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "smallvec 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "rustc-ap-syntax" -version = "218.0.0" +version = "237.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-ap-rustc_data_structures 218.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-ap-rustc_errors 218.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-ap-rustc_target 218.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-ap-serialize 218.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-ap-syntax_pos 218.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-ap-rustc_data_structures 237.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-ap-rustc_errors 237.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-ap-rustc_target 237.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-ap-serialize 237.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-ap-syntax_pos 237.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "scoped-tls 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "rustc-ap-syntax_pos" -version = "218.0.0" +version = "237.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-ap-arena 218.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-ap-rustc_data_structures 218.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-ap-serialize 218.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-ap-arena 237.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-ap-rustc_data_structures 237.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-ap-serialize 237.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "scoped-tls 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1329,7 +1334,7 @@ dependencies = [ [[package]] name = "rustfmt-nightly" -version = "0.99.2" +version = "0.99.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cargo_metadata 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1342,9 +1347,9 @@ dependencies = [ "itertools 0.7.8 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", "regex 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-ap-rustc_target 218.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-ap-syntax 218.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-ap-syntax_pos 218.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-ap-rustc_target 237.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-ap-syntax 237.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-ap-syntax_pos 237.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.71 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.71 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1439,7 +1444,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "smallvec" -version = "0.6.4" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1826,7 +1831,7 @@ dependencies = [ "checksum quine-mc_cluskey 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "07589615d719a60c8dd8a4622e7946465dfef20d1a428f969e3443e7386d5f45" "checksum quote 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9949cfe66888ffe1d53e6ec9d9f3b70714083854be20fd5e271b232a017401e8" "checksum quote 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ed7d650913520df631972f21e104a4fa2f9c82a14afc65d17b388a2e29731e7c" -"checksum racer 2.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "dcbc5ababaffee8d8f34910f925287c8f716b1ead48561c4278a152d08264f7c" +"checksum racer 2.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "1e4bc3847329b20ff5ba56c298938c179ae9911af15c9c10553f683b65164533" "checksum rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eba5f8cb59cc50ed56be8880a5c7b496bfd9bd26394e176bc67884094145c2c5" "checksum rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e464cd887e869cddcae8792a4ee31d23c7edd516700695608f5b98c67ee0131c" "checksum rand_core 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "edecf0f94da5551fc9b492093e30b041a891657db7940ee221f9d2f66e82eef2" @@ -1843,14 +1848,14 @@ dependencies = [ "checksum rls-rustc 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9dba7390427aefa953608429701e3665192ca810ba8ae09301e001b7c7bed0" "checksum rls-span 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5d7c7046dc6a92f2ae02ed302746db4382e75131b9ce20ce967259f6b5867a6a" "checksum rls-vfs 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ecbc8541b4c341d6271eae10f869dd9d36db871afe184f5b6f9bffbd6ed0373f" -"checksum rustc-ap-arena 218.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6e905467184ce31ccdbd33ac33b9ba377f8cc7aefb340a733ab7e5efe34cddda" -"checksum rustc-ap-rustc_cratesio_shim 218.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c2a1a45817e78d0c1e2800fb933c526747ef2c5ee4b2dc0946e0c2d901329b88" -"checksum rustc-ap-rustc_data_structures 218.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "17b4e9e5588883318e0e58bb7ea7cde2a66eaca55b25e32908f0982365988657" -"checksum rustc-ap-rustc_errors 218.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d233c0d9beda42a52d329a5df865c8f20c64773d2ab7aa6b4ae4248bacf3188" -"checksum rustc-ap-rustc_target 218.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "eec0bc13feecf9e88e39439b24b4b3ca54db8caf12fb7172d0c430451c8b377c" -"checksum rustc-ap-serialize 218.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ffcfb1102cd7cbf5f25c008a00f7253427af9dfac8989ede48c19bd47f556893" -"checksum rustc-ap-syntax 218.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3a2ca0ef078a735c81a0d33589e04148dcf41f80ee7ebe30e72904a631b7c669" -"checksum rustc-ap-syntax_pos 218.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b1bbd31d1bbc7210983c3bbbcb9ee35bac443c6c899f979b8114e58bb7101c28" +"checksum rustc-ap-arena 237.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2d24c8b3c1437fad023cb9472381216a1d41d82dbb2d2e6c7858bd6f50317719" +"checksum rustc-ap-rustc_cratesio_shim 237.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9c5b02c76cd1ee4e9c97c8228701796d6b7431e8f100dea2d8af1d6c2c2bad56" +"checksum rustc-ap-rustc_data_structures 237.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4076388154497fb9a007e3badd78e415402a5594111cd6bc7ce1420dd1b1818b" +"checksum rustc-ap-rustc_errors 237.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c6c11e4789cbc276ceaa87d326c234b1a2d1e0fe6017b88a8a25903200060acb" +"checksum rustc-ap-rustc_target 237.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "25f711bb152b9d7cdd69410cfe6d99aeb1409c959e0fdf3c8ca4d220e568aa52" +"checksum rustc-ap-serialize 237.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "57638db658d4942d3f30a12566836f9a67a636ed8002c8cae1c9231214e39929" +"checksum rustc-ap-syntax 237.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d6dbcf07abf7a9957dce8d34353d55dfb4cd882153181f24349f4690facb58f0" +"checksum rustc-ap-syntax_pos 237.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0915cb5e166cabe588a129dec2d47357077e96fb1f9b57318fbe217eac4ce508" "checksum rustc-demangle 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "bcfe5b13211b4d78e5c2cadfebd7769197d95c639c35a50057eb4c05de811395" "checksum rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7540fc8b0c49f096ee9c961cda096467dce8084bec6bdca2fc83895fd9b28cb8" "checksum rustc-rayon 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c6d5a683c6ba4ed37959097e88d71c9e8e26659a3cb5be8b389078e7ad45306" @@ -1859,7 +1864,7 @@ dependencies = [ "checksum rustc-workspace-hack 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc71d2faa173b74b232dedc235e3ee1696581bb132fc116fa3626d6151a1a8fb" "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" "checksum rustfix 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "756567f00f7d89c9f89a5c401b8b1caaa122e27240b9eaadd0bb52ee0b680b1b" -"checksum rustfmt-nightly 0.99.2 (registry+https://github.com/rust-lang/crates.io-index)" = "56f5442163d82d1f2fb52dd8572e2ac40c1498a8b71964865d4fe464e93766d4" +"checksum rustfmt-nightly 0.99.4 (registry+https://github.com/rust-lang/crates.io-index)" = "df2124994af9da1062649cf5ac3738ae8c1bc292153e991b39a98904e342b7a7" "checksum ryu 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "16aa12da69951804cddf5f74d96abcc414a31b064e610dc81e37c1536082f491" "checksum same-file 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "cfb6eded0b06a0b512c8ddbcf04089138c9b4362c2f696f3c3d76039d68f3637" "checksum schannel 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "dc1fabf2a7b6483a141426e1afd09ad543520a77ac49bd03c286e7696ccfd77f" @@ -1872,7 +1877,7 @@ dependencies = [ "checksum serde_ignored 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "190e9765dcedb56be63b6e0993a006c7e3b071a016a304736e4a315dc01fb142" "checksum serde_json 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)" = "44dd2cfde475037451fa99b7e5df77aa3cfd1536575fa8e7a538ab36dcde49ae" "checksum shell-escape 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "170a13e64f2a51b77a45702ba77287f5c6829375b04a69cf2222acd17d0cfab9" -"checksum smallvec 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "211a489e65e94b103926d2054ae515a1cdb5d515ea0ef414fee23b7e043ce748" +"checksum smallvec 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "153ffa32fd170e9944f7e0838edf824a754ec4c1fc64746fcc9fe1f8fa602e5d" "checksum socket2 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "962a516af4d3a7c272cb3a1d50a8cc4e5b41802e4ad54cfb7bee8ba61d37d703" "checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" "checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550" diff --git a/Cargo.toml b/Cargo.toml index be82ec29b26..e7ff825c13a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,7 +24,7 @@ languageserver-types = "0.45" lazy_static = "1" log = "0.4" num_cpus = "1" -racer = { version = "2.1.4", default-features = false } +racer = { version = "2.1.5", default-features = false } rayon = "1" rls-analysis = "0.16" rls-blacklist = "0.1.2" @@ -32,7 +32,7 @@ rls-data = { version = "0.18", features = ["serialize-serde"] } rls-rustc = "0.5.0" rls-span = { version = "0.4", features = ["serialize-serde"] } rls-vfs = "0.4.6" -rustfmt-nightly = "0.99.2" +rustfmt-nightly = "0.99.4" serde = "1.0" serde_json = "1.0" serde_derive = "1.0" diff --git a/src/actions/format.rs b/src/actions/format.rs new file mode 100644 index 00000000000..9c2a0fb0ff4 --- /dev/null +++ b/src/actions/format.rs @@ -0,0 +1,145 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Code formatting using Rustfmt - by default using statically-linked one or +//! possibly running Rustfmt binary specified by the user. + +use std::env::temp_dir; +use std::fs::File; +use std::io::Write; +use std::path::{Path, PathBuf}; +use std::process::{Command, Stdio}; + +use rand::{Rng, thread_rng}; +use log::{log, debug}; +use rustfmt_nightly::{Config, Session, Input}; +use serde_json; + +/// Specified which `rustfmt` to use. +#[derive(Clone)] +pub enum Rustfmt { + /// (Path to external `rustfmt`, cwd where it should be spawned at) + External(PathBuf, PathBuf), + /// Statically linked `rustfmt` + Internal +} + +impl From> for Rustfmt { + fn from(value: Option<(String, PathBuf)>) -> Rustfmt { + match value { + Some((path, cwd)) => Rustfmt::External(PathBuf::from(path), cwd), + None => Rustfmt::Internal + } + } +} + +impl Rustfmt { + pub fn format(&self, input: String, cfg: Config) -> Result { + match self { + Rustfmt::Internal => format_internal(input, cfg), + Rustfmt::External(path, cwd) => format_external(path, cwd, input, cfg), + } + } +} + +fn format_external(path: &PathBuf, cwd: &PathBuf, input: String, cfg: Config) -> Result { + let (_file_handle, config_path) = gen_config_file(&cfg)?; + let args = rustfmt_args(&cfg, &config_path); + + let mut rustfmt = Command::new(path) + .args(args) + .current_dir(cwd) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .spawn() + .map_err(|_| format!("Couldn't spawn `{}`", path.display()))?; + + { + let stdin = rustfmt.stdin.as_mut() + .ok_or_else(|| "Failed to open rustfmt stdin".to_string())?; + stdin.write_all(input.as_bytes()) + .map_err(|_| "Failed to pass input to rustfmt".to_string())?; + } + + rustfmt.wait_with_output() + .map_err(|err| format!("Error running rustfmt: {}", err)) + .and_then(|out| String::from_utf8(out.stdout) + .map_err(|_| "Formatted code is not valid UTF-8".to_string())) +} + +fn format_internal(input: String, config:Config) -> Result { + let mut buf = Vec::::new(); + + { + let mut session = Session::new(config, Some(&mut buf)); + + match session.format(Input::Text(input)) { + Ok(report) => { + // Session::format returns Ok even if there are any errors, i.e., parsing errors. + if session.has_operational_errors() || session.has_parsing_errors() { + debug!( + "reformat: format_input failed: has errors, report = {}", + report + ); + + return Err("Reformat failed to complete successfully".into()); + } + } + Err(e) => { + debug!("Reformat failed: {:?}", e); + + return Err("Reformat failed to complete successfully".into()); + } + } + } + + String::from_utf8(buf) + .map_err(|_| "Reformat output is not a valid UTF-8".into()) +} + +fn random_file() -> Result<(File, PathBuf), String> { + const SUFFIX_LEN: usize = 10; + + let suffix: String = thread_rng().gen_ascii_chars().take(SUFFIX_LEN).collect(); + let path = temp_dir().join(suffix); + + Ok(File::create(&path) + .map(|file| (file, path)) + .map_err(|_| "Config file could not be created".to_string())?) +} + +fn gen_config_file(config: &Config) -> Result<(File, PathBuf), String> { + let (mut file, path) = random_file()?; + let toml = config.all_options().to_toml()?; + file.write(toml.as_bytes()) + .map_err(|_| "Could not write config TOML file contents".to_string())?; + + Ok((file, path)) +} + +fn rustfmt_args(config: &Config, config_path: &Path) -> Vec { + let mut args = vec![ + "--unstable-features".into(), + "--skip-children".into(), + "--emit".into(), + "stdout".into(), + "--quiet".into(), + ]; + + args.push("--file-lines".into()); + let file_lines_json = config.file_lines().to_json_spans(); + let lines: String = serde_json::to_string(&file_lines_json).unwrap(); + args.push(lines); + + args.push("--config-path".into()); + args.push(config_path.to_str().map(|x| x.to_string()).unwrap()); + + args +} diff --git a/src/actions/hover.rs b/src/actions/hover.rs index b7773c9e577..8be71968687 100644 --- a/src/actions/hover.rs +++ b/src/actions/hover.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use crate::actions::format::Rustfmt; use crate::actions::requests; use crate::actions::InitActionContext; use crate::config::FmtConfig; @@ -18,7 +19,7 @@ use racer; use rls_analysis::{Def, DefKind}; use rls_span::{Column, Row, Span, ZeroIndexed}; use rls_vfs::{self as vfs, Vfs}; -use rustfmt_nightly::{Session as FmtSession, Input as FmtInput, NewlineStyle}; +use rustfmt_nightly::NewlineStyle; use std::path::{Path, PathBuf}; use log::*; @@ -327,6 +328,8 @@ fn tooltip_struct_enum_union_trait( let vfs = ctx.vfs.clone(); let fmt_config = ctx.fmt_config(); + // We hover often so use the in-process one to speed things up + let fmt = Rustfmt::Internal; // fallback in case source extration fails let the_type = || match def.kind { @@ -339,7 +342,7 @@ fn tooltip_struct_enum_union_trait( let decl = def_decl(def, &vfs, the_type); - let the_type = format_object(&fmt_config, decl); + let the_type = format_object(fmt, &fmt_config, decl); let docs = def_docs(def, &vfs); let context = None; @@ -380,6 +383,8 @@ fn tooltip_function_method( let vfs = ctx.vfs.clone(); let fmt_config = ctx.fmt_config(); + // We hover often so use the in-process one to speed things up + let fmt = Rustfmt::Internal; let the_type = || { def.value @@ -391,7 +396,7 @@ fn tooltip_function_method( let decl = def_decl(def, &vfs, the_type); - let the_type = format_method(&fmt_config, decl); + let the_type = format_method(fmt, &fmt_config, decl); let docs = def_docs(def, &vfs); let context = None; @@ -725,7 +730,7 @@ fn racer_def(ctx: &InitActionContext, span: &Span) -> Option { /// Formats a struct, enum, union, or trait. The original type is returned /// in the event of an error. -fn format_object(fmt_config: &FmtConfig, the_type: String) -> String { +fn format_object(rustfmt: Rustfmt, fmt_config: &FmtConfig, the_type: String) -> String { debug!("format_object: {}", the_type); let mut config = fmt_config.get_rustfmt_config().clone(); config.set().newline_style(NewlineStyle::Unix); @@ -743,21 +748,11 @@ fn format_object(fmt_config: &FmtConfig, the_type: String) -> String { format!("{}{{}}", trimmed) }; - let mut out = Vec::::with_capacity(the_type.len()); - let input = FmtInput::Text(object.clone()); - let mut session = FmtSession::new(config, Some(&mut out)); - let formatted = match session.format(input) { - Ok(_) => { - let utf8 = session.out - .as_ref() - .map(|out| String::from_utf8(out.to_vec())) - .unwrap_or_else(|| Ok(trimmed.to_string())); - match utf8.map(|lines| (lines.rfind('{'), lines)) { - Ok((Some(pos), lines)) => lines[0..pos].into(), - Ok((None, lines)) => lines, - _ => trimmed.into(), - } - } + let formatted = match rustfmt.format(object.clone(), config) { + Ok(lines) => match lines.rfind('{') { + Some(pos) => lines[0..pos].into(), + None => lines, + }, Err(e) => { error!("format_object: error: {:?}, input: {:?}", e, object); trimmed.to_string() @@ -802,44 +797,37 @@ fn format_object(fmt_config: &FmtConfig, the_type: String) -> String { /// Formats a method or function. The original type is returned /// in the event of an error. -fn format_method(fmt_config: &FmtConfig, the_type: String) -> String { +fn format_method(rustfmt: Rustfmt, fmt_config: &FmtConfig, the_type: String) -> String { trace!("format_method: {}", the_type); let the_type = the_type.trim().trim_right_matches(';').to_string(); + let mut config = fmt_config.get_rustfmt_config().clone(); config.set().newline_style(NewlineStyle::Unix); let tab_spaces = config.tab_spaces(); - let method = format!("impl Dummy {{ {} {{ unimplmented!() }} }}", the_type); - let mut out = Vec::::with_capacity(the_type.len()); - let input = FmtInput::Text(method.clone()); - let mut session = FmtSession::new(config, Some(&mut out)); - let result = match session.format(input) { - Ok(_) => { - let lines = session.out - .as_ref() - .map(|out| String::from_utf8(out.to_vec())); - if let Some(Ok(mut lines)) = lines { - if let Some(front_pos) = lines.find('{') { - lines = lines[front_pos..].chars().skip(1).collect(); - } - if let Some(back_pos) = lines.rfind('{') { - lines = lines[0..back_pos].into(); - } - lines - .lines() - .filter(|line| line.trim() != "") - .map(|line| { - let mut spaces = tab_spaces + 1; - let should_trim = |c: char| { - spaces = spaces.saturating_sub(1); - spaces > 0 && c.is_whitespace() - }; - let line = line.trim_left_matches(should_trim); - format!("{}\n", line) - }) - .collect() - } else { - the_type + + let method = format!("impl Dummy {{ {} {{ unimplemented!() }} }}", the_type); + + let result = match rustfmt.format(method.clone(), config) { + Ok(mut lines) => { + if let Some(front_pos) = lines.find('{') { + lines = lines[front_pos..].chars().skip(1).collect(); } + if let Some(back_pos) = lines.rfind('{') { + lines = lines[0..back_pos].into(); + } + lines + .lines() + .filter(|line| line.trim() != "") + .map(|line| { + let mut spaces = tab_spaces + 1; + let should_trim = |c: char| { + spaces = spaces.saturating_sub(1); + spaces > 0 && c.is_whitespace() + }; + let line = line.trim_left_matches(should_trim); + format!("{}\n", line) + }) + .collect() } Err(e) => { error!("format_method: error: {:?}, input: {:?}", e, method); @@ -947,6 +935,7 @@ pub fn tooltip( pub mod test { use super::*; + use crate::actions::format::Rustfmt; use crate::build::BuildPriority; use crate::config; use crate::lsp_data::{ClientCapabilities, InitializationOptions}; @@ -1479,25 +1468,26 @@ pub mod test { #[test] fn test_format_method() { + let fmt = Rustfmt::Internal; let config = &FmtConfig::default(); let input = "fn foo() -> ()"; - let result = format_method(config, input.into()); + let result = format_method(fmt.clone(), config, input.into()); assert_eq!(input, &result, "function explicit void return"); let input = "fn foo()"; let expected = "fn foo()"; - let result = format_method(config, input.into()); + let result = format_method(fmt.clone(), config, input.into()); assert_eq!(expected, &result, "function"); let input = "fn foo() -> Thing"; let expected = "fn foo() -> Thing"; - let result = format_method(config, input.into()); + let result = format_method(fmt.clone(), config, input.into()); assert_eq!(expected, &result, "function with return"); let input = "fn foo(&self);"; let expected = "fn foo(&self)"; - let result = format_method(config, input.into()); + let result = format_method(fmt.clone(), config, input.into()); assert_eq!(expected, &result, "method"); let input = "fn foo(t: T) where T: Copy"; @@ -1508,7 +1498,7 @@ pub mod test { T: Copy, ", ); - let result = format_method(config, input.into()); + let result = format_method(fmt.clone(), config, input.into()); assert_eq!(expected, result, "function with generic parameters"); let input = "fn foo(&self, t: T) where T: Copy"; @@ -1519,7 +1509,7 @@ pub mod test { T: Copy, ", ); - let result = format_method(config, input.into()); + let result = format_method(fmt.clone(), config, input.into()); assert_eq!(expected, result, "method with type parameters"); let input = noindent( @@ -1538,7 +1528,7 @@ pub mod test { T: Copy, ", ); - let result = format_method(config, input); + let result = format_method(fmt.clone(), config, input); assert_eq!( expected, result, "method with type parameters; corrected spacing" @@ -1556,7 +1546,7 @@ pub mod test { ) -> Thing ", ); - let result = format_method(config, input.into()); + let result = format_method(fmt.clone(), config, input.into()); assert_eq!(expected, result, "long function signature"); let input = "fn really_really_really_really_long_name(&self, foo_thing: String, bar_thing: Thing, baz_thing: Vec, foo_other: u32, bar_other: i32) -> Thing"; @@ -1572,7 +1562,7 @@ pub mod test { ) -> Thing ", ); - let result = format_method(config, input.into()); + let result = format_method(fmt.clone(), config, input.into()); assert_eq!(expected, result, "long method signature with generic"); let input = noindent( @@ -1595,7 +1585,7 @@ pub mod test { ) ", ); - let result = format_method(config, input); + let result = format_method(fmt.clone(), config, input); assert_eq!(expected, result, "function with multiline args"); } @@ -1697,57 +1687,58 @@ pub mod test { #[test] fn test_format_object() { + let fmt = Rustfmt::Internal; let config = &FmtConfig::default(); let input = "pub struct Box(Unique);"; - let result = format_object(config, input.into()); + let result = format_object(fmt.clone(), config, input.into()); assert_eq!( "pub struct Box", &result, "tuple struct with all private fields has hidden components" ); let input = "pub struct Thing(pub u32);"; - let result = format_object(config, input.into()); + let result = format_object(fmt.clone(), config, input.into()); assert_eq!( "pub struct Thing(pub u32)", &result, "tuple struct with trailing ';' from racer" ); let input = "pub struct Thing(pub u32)"; - let result = format_object(config, input.into()); + let result = format_object(fmt.clone(), config, input.into()); assert_eq!("pub struct Thing(pub u32)", &result, "pub tuple struct"); let input = "pub struct Thing(pub u32, i32)"; - let result = format_object(config, input.into()); + let result = format_object(fmt.clone(), config, input.into()); assert_eq!( "pub struct Thing(pub u32, _)", &result, "non-pub components of pub tuples should be hidden" ); let input = "struct Thing(u32, i32)"; - let result = format_object(config, input.into()); + let result = format_object(fmt.clone(), config, input.into()); assert_eq!( "struct Thing(u32, i32)", &result, "private tuple struct may show private components" ); let input = "pub struct Thing"; - let result = format_object(config, input.into()); + let result = format_object(fmt.clone(), config, input.into()); assert_eq!("pub struct Thing", &result, "pub struct"); let input = "pub struct Thing {"; - let result = format_object(config, input.into()); + let result = format_object(fmt.clone(), config, input.into()); assert_eq!( "pub struct Thing", &result, "pub struct with trailing '{{' from racer" ); let input = "pub struct Thing { x: i32 }"; - let result = format_object(config, input.into()); + let result = format_object(fmt.clone(), config, input.into()); assert_eq!("pub struct Thing", &result, "pub struct with body"); let input = "pub enum Foobar { Foo, Bar }"; - let result = format_object(config, input.into()); + let result = format_object(fmt.clone(), config, input.into()); assert_eq!("pub enum Foobar", &result, "pub enum with body"); let input = "pub trait Thing where T: Copy + Sized, U: Clone"; @@ -1759,7 +1750,7 @@ pub mod test { U: Clone, ", ); - let result = format_object(config, input.into()); + let result = format_object(fmt.clone(), config, input.into()); assert_eq!(expected, result, "trait with where clause"); } diff --git a/src/actions/mod.rs b/src/actions/mod.rs index e2e4b0e32cb..12e2eb65e8a 100644 --- a/src/actions/mod.rs +++ b/src/actions/mod.rs @@ -22,6 +22,7 @@ use crate::Span; use walkdir::WalkDir; use log::{debug, log, trace, error, info}; +use crate::actions::format::Rustfmt; use crate::actions::post_build::{BuildResults, PostBuildHandler, AnalysisQueue}; use crate::actions::progress::{BuildProgressNotifier, BuildDiagnosticsNotifier}; use crate::build::*; @@ -66,6 +67,7 @@ pub mod progress; pub mod diagnostics; pub mod run; pub mod hover; +pub mod format; /// Persistent context shared across all requests and notifications. pub enum ActionContext { @@ -268,6 +270,16 @@ impl InitActionContext { racer::Session::with_project_model(cache, pm) } + /// Depending on user configuration, we might use either external Rustfmt or + /// the one we're shipping with. + /// Locks config to read `rustfmt_path` key. + fn formatter(&self) -> Rustfmt { + let rustfmt = self.config.lock().unwrap().rustfmt_path.clone() + .map(|path| (path, self.current_project.clone())); + + Rustfmt::from(rustfmt) + } + fn fmt_config(&self) -> FmtConfig { FmtConfig::from(&self.current_project) } diff --git a/src/actions/requests.rs b/src/actions/requests.rs index e26ad4f9781..67d69edb44c 100644 --- a/src/actions/requests.rs +++ b/src/actions/requests.rs @@ -15,7 +15,7 @@ use rls_data as data; use url::Url; use rls_vfs::{FileContents}; use racer; -use rustfmt_nightly::{Session, FileLines, FileName, Input as FmtInput, Range as RustfmtRange}; +use rustfmt_nightly::{FileLines, FileName, Range as RustfmtRange}; use serde_json; use rls_span as span; use itertools::Itertools; @@ -719,7 +719,7 @@ fn reformat( let path = parse_file_path!(&doc.uri, "reformat")?; let input = match ctx.vfs.load_file(&path) { - Ok(FileContents::Text(s)) => FmtInput::Text(s), + Ok(FileContents::Text(s)) => s, Ok(_) => { debug!("Reformat failed, found binary file"); return Err(ResponseError::Message( @@ -757,44 +757,12 @@ fn reformat( config.set().file_lines(file_lines); }; - let mut buf = Vec::::new(); + let formatted_text = ctx.formatter().format(input, config) + .map_err(|msg| ResponseError::Message(ErrorCode::InternalError, msg))?; - { - let mut session = Session::new(config, Some(&mut buf)); - - match session.format(input) { - Ok(report) => { - // Session::format returns Ok even if there are any errors, i.e., parsing errors. - if session.has_operational_errors() || session.has_parsing_errors() { - debug!( - "reformat: format_input failed: has errors, report = {}", - report - ); - - return Err(ResponseError::Message( - ErrorCode::InternalError, - "Reformat failed to complete successfully".into(), - )); - } - } - Err(e) => { - debug!("Reformat failed: {:?}", e); - - return Err(ResponseError::Message( - ErrorCode::InternalError, - "Reformat failed to complete successfully".into(), - )); - } - }; - } + // Note that we don't need to update the VFS, the client echos back the + // change to us when it applies the returned TextEdit. - let text = String::from_utf8(buf).unwrap(); - - // Note that we don't need to update the VFS, the client - // echos back the change to us. - - // If Rustfmt returns range of text that changed, - // we will be able to pass only range of changed text to the client. if !ctx.quiescent.load(Ordering::SeqCst) { return Err(ResponseError::Message( ErrorCode::InternalError, @@ -802,10 +770,12 @@ fn reformat( )) } + // If Rustfmt returns range of text that changed, + // we will be able to pass only range of changed text to the client. Ok([ TextEdit { range: range_whole_file, - new_text: text, + new_text: formatted_text, }, ]) } diff --git a/src/config.rs b/src/config.rs index af7289ccc03..e31b45e4a57 100644 --- a/src/config.rs +++ b/src/config.rs @@ -157,6 +157,9 @@ pub struct Config { /// local variable declaration. When set to false, the content is only availabe when /// holding the `ctrl` key in some editors. pub show_hover_context: bool, + /// Use provided rustfmt binary instead of the statically linked one. + /// (requires unstable features) + pub rustfmt_path: Option, } impl Default for Config { @@ -185,6 +188,7 @@ impl Default for Config { clippy_preference: ClippyPreference::OptIn, full_docs: Inferrable::Inferred(false), show_hover_context: true, + rustfmt_path: None, }; result.normalise(); result @@ -221,6 +225,7 @@ impl Config { self.build_bin = Inferrable::Inferred(None); self.build_lib = Inferrable::Inferred(false); self.cfg_test = false; + self.rustfmt_path = None; } }