diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..dd069ca --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,625 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "ansi_term" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +dependencies = [ + "winapi", +] + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bstr" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90682c8d613ad3373e66de8c6411e0ae2ab2571e879d2efbf73558cc66f21279" +dependencies = [ + "lazy_static", + "memchr", + "regex-automata", + "serde", +] + +[[package]] +name = "bumpalo" +version = "3.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9df67f7bf9ef8498769f994239c45613ef0c5899415fb58e9add412d2c1a538" + +[[package]] +name = "cast" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c24dab4283a142afa2fdca129b80ad2c6284e073930f964c3a1293c225ee39a" +dependencies = [ + "rustc_version", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clap" +version = "2.33.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" +dependencies = [ + "ansi_term", + "atty", + "bitflags", + "strsim", + "textwrap", + "unicode-width", + "vec_map", +] + +[[package]] +name = "criterion" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1604dafd25fba2fe2d5895a9da139f8dc9b319a5fe5354ca137cbbce4e178d10" +dependencies = [ + "atty", + "cast", + "clap", + "criterion-plot", + "csv", + "itertools", + "lazy_static", + "num-traits", + "oorandom", + "plotters", + "rayon", + "regex", + "serde", + "serde_cbor", + "serde_derive", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d00996de9f2f7559f7f4dc286073197f83e92256a59ed395f9aac01fe717da57" +dependencies = [ + "cast", + "itertools", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ed27e177f16d65f0f0c22a213e17c696ace5dd64b14258b52f9417ccb52db4" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" +dependencies = [ + "cfg-if", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec02e091aa634e2c3ada4a392989e7c3116673ef0ac5b72232439094d73b7fd" +dependencies = [ + "cfg-if", + "crossbeam-utils", + "lazy_static", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d82cfc11ce7f2c3faef78d8a684447b40d503d9681acebed6cb728d45940c4db" +dependencies = [ + "cfg-if", + "lazy_static", +] + +[[package]] +name = "csv" +version = "1.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22813a6dc45b335f9bade10bf7271dc477e81113e89eb251a0bc2a8a81c536e1" +dependencies = [ + "bstr", + "csv-core", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "csv-core" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90" +dependencies = [ + "memchr", +] + +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" + +[[package]] +name = "half" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62aca2aba2d62b4a7f5b33f3712cb1b0692779a56fb510499d5c0aa594daeaf3" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "itertools" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69ddb889f9d0d08a67338271fa9b62996bc788c7796a5c18cf057420aaed5eaf" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" + +[[package]] +name = "js-sys" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cc9ffccd38c451a86bf13657df244e9c3f37493cce8e5e21e940963777acc84" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2a5ac8f984bfcf3a823267e5fde638acc3325f6496633a5da6bb6eb2171e103" + +[[package]] +name = "log" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "memchr" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" + +[[package]] +name = "memoffset" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9" +dependencies = [ + "autocfg", +] + +[[package]] +name = "nufmt" +version = "0.1.0" +dependencies = [ + "clap", + "criterion", +] + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "oorandom" +version = "11.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" + +[[package]] +name = "plotters" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a3fd9ec30b9749ce28cd91f255d569591cdf937fe280c312143e3c4bad6f2a" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d88417318da0eaf0fdcdb51a0ee6c3bed624333bff8f946733049380be67ac1c" + +[[package]] +name = "plotters-svg" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521fa9638fa597e1dc53e9412a4f9cefb01187ee1f7413076f9e6749e2885ba9" +dependencies = [ + "plotters-backend", +] + +[[package]] +name = "proc-macro2" +version = "1.0.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9f5105d4fdaab20335ca9565e106a5d9b82b6219b5ba735731124ac6711d23d" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rayon" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90" +dependencies = [ + "autocfg", + "crossbeam-deque", + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d78120e2c850279833f1dd3582f730c4ab53ed95aeaaaa862a2a5c71b1656d8e" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "lazy_static", + "num_cpus", +] + +[[package]] +name = "regex" +version = "1.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" +dependencies = [ + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" + +[[package]] +name = "regex-syntax" +version = "0.6.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "ryu" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "semver" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "568a8e6258aa33c13358f81fd834adb854c6f7c9468520910a9b1e8fac068012" + +[[package]] +name = "serde" +version = "1.0.130" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913" + +[[package]] +name = "serde_cbor" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5" +dependencies = [ + "half", + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.130" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f690853975602e1bfe1ccbf50504d67174e3bcf340f23b5ea9992e0587a52d8" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + +[[package]] +name = "syn" +version = "1.0.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6f107db402c2c2055242dbf4d2af0e69197202e9faacbef9571bbe47f5a1b84" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "unicode-width" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + +[[package]] +name = "walkdir" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +dependencies = [ + "same-file", + "winapi", + "winapi-util", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "632f73e236b219150ea279196e54e610f5dbafa5d61786303d4da54f84e47fce" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a317bf8f9fba2476b4b2c85ef4c4af8ff39c3c7f0cdfeed4f82c34a880aa837b" +dependencies = [ + "bumpalo", + "lazy_static", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d56146e7c495528bf6587663bea13a8eb588d39b36b679d83972e1a2dbbdacf9" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7803e0eea25835f8abdc585cd3021b3deb11543c6fe226dcd30b228857c5c5ab" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0237232789cf037d5480773fe568aac745bfe2afbc11a863e97901780a6b47cc" + +[[package]] +name = "web-sys" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38eb105f1c59d9eaa6b5cdc92b859d85b926e82cb2e0945cd0c9259faa6fe9fb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..4eb9874 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,37 @@ +[package] +name = "nufmt" +version = "0.1.0" +edition = "2018" +authors = ["The NuShell Contributors"] +license = "MIT" +description = "Formats nushell extremely fast" +homepage = "https://github.com/fdncred/nufmt" +repository = "https://github.com/fdncred/nufmt" +readme = "README.md" +keywords = ["nu", "nushell", "formatting", "cli"] +categories = ["command-line-utilities"] + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +clap = { version= "2.33.3", optional = true } + +[dev-dependencies] +criterion = "0.3" + +[features] +default = ["bin"] +bin = ["clap"] + +[lib] +name = "nufmt" +path = "src/lib.rs" + +[[bin]] +name = "nufmt" +path = "src/main.rs" +required-features = ["bin"] + +[[bench]] +name = "bench" +harness = false diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f1754b8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 nilstrieb + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/benches/bench.rs b/benches/bench.rs new file mode 100644 index 0000000..ad0e79c --- /dev/null +++ b/benches/bench.rs @@ -0,0 +1,17 @@ +use criterion::{criterion_group, criterion_main, Criterion}; +use nufmt::{format_nu, Indentation}; +use std::{fs, io}; + +/// You need a nu file called massive.nu in your project root +fn format_massive_nu(file: &str) -> io::Result { + Ok(format_nu(&file, Indentation::Default)) +} + +fn criterion_benchmark(c: &mut Criterion) { + let file = fs::read_to_string("massive.nu").expect("massive.nu file in project directory"); + + c.bench_function("Format massive nu", |b| b.iter(|| format_massive_nu(&file))); +} + +criterion_group!(benches, criterion_benchmark); +criterion_main!(benches); diff --git a/g.nu b/g.nu new file mode 100644 index 0000000..97752de --- /dev/null +++ b/g.nu @@ -0,0 +1,35 @@ +# this script will print a blue gradient on the screen +# We can get the terminal width and height now with term size +# but we like to use the script as a benchmark, so let's keep +# it a constant size for now +let height = 40 # really need to get the terminal height here +let width = 160 # really need to get the terminal width here +let stamp = 'Nu'seq 0 $height | each { + let row_data = (seq 0 $width | each { + |col| let fgcolor = (iter_inc 2 2 $col) if $fgcolor > 200 && $fgcolor < 210 { + $"(ansi -e '48;2;0;0;')($fgcolor)m($stamp)(ansi -e '0m')" +} { + $"(ansi -e '48;2;0;0;')($fgcolor)m(char sp)(ansi -e '0m')" +} +} | str collect) $"($row_data)(char newline)" | autoview +} | str collectdef iter_inc [ +incr mult iter +] { + $incr + $mult * $iter +}# ╭────┬────────────────────┬───────────────────────────────────────────────────────────────────────────────────────────────────╮ +# │ # │ key │ value │ +# ├────┼────────────────────┼───────────────────────────────────────────────────────────────────────────────────────────────────┤ +# │ 0 │ version │ 0.31.1 │ +# │ 1 │ branch │ main │ +# │ 2 │ short_commit │ 751de20f │ +# │ 3 │ commit_hash │ 751de20f938ed200ae6128a30d06a5dd24a4fd33 │ +# │ 4 │ commit_date │ 2021-05-21 02:04:27 │ +# │ 5 │ build_os │ windows-x86_64 │ +# │ 6 │ rust_version │ rustc 1.52.1 (9bc8c42bb 2021-05-09) │ +# │ 7 │ rust_channel │ stable (default) │ +# │ 8 │ cargo_version │ cargo 1.52.0 (69767412a 2021-04-21) │ +# │ 9 │ pkg_version │ 0.31.1 │ +# │ 10 │ build_time │ 2021-05-21 07:20:25 │ +# │ 11 │ build_rust_channel │ release │ +# │ 12 │ features │ clipboard-cli, ctrlc, default, directories, dirs, ptree, rustyline, term, trash, uuid, which, zip │ +# ╰────┴────────────────────┴───────────────────────────────────────────────────────────────────────────────────────────────────╯ \ No newline at end of file diff --git a/gradient.nu b/gradient.nu new file mode 100644 index 0000000..5ba5a08 --- /dev/null +++ b/gradient.nu @@ -0,0 +1,41 @@ +# this script will print a blue gradient on the screen + +# We can get the terminal width and height now with term size +# but we like to use the script as a benchmark, so let's keep +# it a constant size for now +let height = 40 # really need to get the terminal height here +let width = 160 # really need to get the terminal width here +let stamp = 'Nu' +seq 0 $height | each { + let row_data = (seq 0 $width | each { |col| + let fgcolor = (iter_inc 2 2 $col) + if $fgcolor > 200 && $fgcolor < 210 { + $"(ansi -e '48;2;0;0;')($fgcolor)m($stamp)(ansi -e '0m')" + } { + $"(ansi -e '48;2;0;0;')($fgcolor)m(char sp)(ansi -e '0m')" + } + } | str collect) + $"($row_data)(char newline)" | autoview +} | str collect + +def iter_inc [incr mult iter] { + $incr + $mult * $iter +} + +# ╭────┬────────────────────┬───────────────────────────────────────────────────────────────────────────────────────────────────╮ +# │ # │ key │ value │ +# ├────┼────────────────────┼───────────────────────────────────────────────────────────────────────────────────────────────────┤ +# │ 0 │ version │ 0.31.1 │ +# │ 1 │ branch │ main │ +# │ 2 │ short_commit │ 751de20f │ +# │ 3 │ commit_hash │ 751de20f938ed200ae6128a30d06a5dd24a4fd33 │ +# │ 4 │ commit_date │ 2021-05-21 02:04:27 │ +# │ 5 │ build_os │ windows-x86_64 │ +# │ 6 │ rust_version │ rustc 1.52.1 (9bc8c42bb 2021-05-09) │ +# │ 7 │ rust_channel │ stable (default) │ +# │ 8 │ cargo_version │ cargo 1.52.0 (69767412a 2021-04-21) │ +# │ 9 │ pkg_version │ 0.31.1 │ +# │ 10 │ build_time │ 2021-05-21 07:20:25 │ +# │ 11 │ build_rust_channel │ release │ +# │ 12 │ features │ clipboard-cli, ctrlc, default, directories, dirs, ptree, rustyline, term, trash, uuid, which, zip │ +# ╰────┴────────────────────┴───────────────────────────────────────────────────────────────────────────────────────────────────╯ \ No newline at end of file diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..d3a34c9 --- /dev/null +++ b/readme.md @@ -0,0 +1 @@ +# Nushell script formatter diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..f43eb2e --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,272 @@ +//! +//! nufmt is a library for formatting nu. +//! +//! It does not do anything more than that, which makes it so fast. + +use std::error::Error; +use std::io::{BufReader, BufWriter, Read, Write}; + +/// +/// Set the indentation used for the formatting. +/// +/// Note: It is *not* recommended to set indentation to anything oder than some spaces or some tabs, +/// but nothing is stopping you from doing that. +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +pub enum Indentation<'a> { + /// Use the default indentation, which is two spaces + Default, + /// Use a custom indentation String + Custom(&'a str), +} + +/// +/// # Formats a nu string +/// +/// The indentation can be set to any value using [Indentation](nufmt::Indentation) +/// The default value is two spaces +/// The default indentation is faster than a custom one +/// +pub fn format_nu(nu: &str, indentation: Indentation) -> String { + let mut reader = BufReader::new(nu.as_bytes()); + let mut writer = BufWriter::new(Vec::new()); + + format_nu_buffered(&mut reader, &mut writer, indentation).unwrap(); + String::from_utf8(writer.into_inner().unwrap()).unwrap() +} + +/// +/// # Formats a nu string +/// +/// The indentation can be set to any value using [Indentation](nufmt::Indentation) +/// The default value is two spaces +/// The default indentation is faster than a custom one +/// +pub fn format_nu_buffered( + reader: &mut BufReader, + writer: &mut BufWriter, + indentation: Indentation, +) -> Result<(), Box> +where + R: Read, + W: Write, +{ + let mut escaped = false; + let mut in_string = false; + let mut indent_level = 0usize; + let mut newline_requested = false; // invalidated if next character is ] or } + let mut in_comment = false; + + for char in reader.bytes() { + let char = char?; + // if we're in a comment, ignore and write everything until a newline + if in_comment { + match char { + b'\n' => { + in_comment = false; + writer.write_all(&[char])?; + } + _ => { + writer.write_all(&[char])?; + continue; + } + } + } + if in_string { + let mut escape_here = false; + match char { + b'"' => { + if !escaped { + in_string = false; + } + } + b'\\' => { + if !escaped { + escape_here = true; + } + } + _ => {} + } + writer.write_all(&[char])?; + escaped = escape_here; + } else { + let mut auto_push = true; + let mut request_newline = false; + // let old_level = indent_level; + + match char { + b'#' => in_comment = true, + b'"' => in_string = true, + // b' ' | b'\n' | b'\t' => continue, + b'\n' => continue, + b'[' | b'{' => { + indent_level += 1; + request_newline = true; + } + b']' | b'}' => { + indent_level = indent_level.saturating_sub(1); + if !newline_requested { + // see comment below about newline_requested + writer.write_all(&[b'\n'])?; + indent_buffered(writer, indent_level, indentation)?; + } + } + // b'[' => { + // indent_level += 1; + // request_newline = true; + // } + // b'{' => { + // indent_level += 1; + // request_newline = true; + // } + // b'}' | b']' => { + // indent_level = indent_level.saturating_sub(1); + // if !newline_requested { + // // see comment below about newline_requested + // writer.write_all(&[b'\n'])?; + // indent_buffered(writer, indent_level, indentation)?; + // } + // } + b':' => { + auto_push = false; + writer.write_all(&[char])?; + writer.write_all(&[b' '])?; + } + b',' => { + request_newline = true; + } + _ => {} + } + + if newline_requested { + writer.write_all(&[b'\n'])?; + indent_buffered(writer, indent_level, indentation)?; + } + // if newline_requested && char != b']' && char != b'}' { + // // newline only happens after { [ and , + // // this means we can safely assume that it being followed up by } or ] + // // means an empty object/array + // writer.write_all(&[b'\n'])?; + // indent_buffered(writer, old_level, indentation)?; + // } + + if auto_push { + writer.write_all(&[char])?; + } + + newline_requested = request_newline; + } + } + + Ok(()) +} + +fn indent_buffered( + writer: &mut BufWriter, + level: usize, + indent_str: Indentation, +) -> Result<(), Box> +where + W: std::io::Write, +{ + for _ in 0..level { + match indent_str { + Indentation::Default => { + writer.write_all(b" ")?; + } + Indentation::Custom(indent) => { + writer.write_all(indent.as_bytes())?; + } + } + } + + Ok(()) +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn ignore_comments() { + let nu = "# this is a comment"; + let expected = "# this is a comment"; + assert_eq!(expected, format_nu(nu, Indentation::Default)); + } + + #[test] + fn echoes_primitive() { + let nu = "1.35"; + assert_eq!(nu, format_nu(nu, Indentation::Default)); + } + + #[test] + fn ignore_whitespace_in_string() { + let nu = "\" hallo \""; + assert_eq!(nu, format_nu(nu, Indentation::Default)); + } + + #[test] + fn remove_leading_whitespace() { + let nu = " 0"; + let expected = "0"; + assert_eq!(expected, format_nu(nu, Indentation::Default)); + } + + #[test] + fn handle_escaped_strings() { + let nu = " \" hallo \\\" \" "; + let expected = "\" hallo \\\" \""; + assert_eq!(expected, format_nu(nu, Indentation::Default)); + } + + #[test] + fn simple_object() { + let nu = "{\"a\":0}"; + let expected = "{ + \"a\": 0 +}"; + assert_eq!(expected, format_nu(nu, Indentation::Default)); + } + + #[test] + fn simple_array() { + let nu = "[1,2,null]"; + let expected = "[ + 1, + 2, + null +]"; + assert_eq!(expected, format_nu(nu, Indentation::Default)); + } + + #[test] + fn array_of_object() { + let nu = "[{\"a\": 0}, {}, {\"a\": null}]"; + let expected = "[ + { + \"a\": 0 + }, + {}, + { + \"a\": null + } +]"; + + assert_eq!(expected, format_nu(nu, Indentation::Default)); + } + + #[test] + fn already_formatted() { + let expected = "[ + { + \"a\": 0 + }, + {}, + { + \"a\": null + } +]"; + + assert_eq!(expected, format_nu(expected, Indentation::Default)); + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..4f7d9db --- /dev/null +++ b/src/main.rs @@ -0,0 +1,79 @@ +use clap::clap_app; +use nufmt::{format_nu_buffered, Indentation}; +use std::error::Error; +use std::fs::File; +use std::io::{BufReader, BufWriter, Read, Write}; + +fn main() -> Result<(), Box> { + let matches = clap_app!(nufmt => + (version: "1.1") + (author: "fdncred") + (about: "Formats nu from stdin or from a file") + (@arg stdout: -s --stdout "Output the result to stdout instead of the default output file. Windows only.") + (@arg indentation: -i --indent +takes_value "Set the indentation used (\\s for space, \\t for tab)") + (@arg output: -o --output +takes_value "The output file for the formatted nu") + (@arg input: "The input file to format") + ) + .get_matches(); + + // Note: on-stack dynamic dispatch + let (mut file, mut stdin); + let reader: &mut dyn Read = match matches.value_of("input") { + Some(path) => { + file = File::open(path)?; + &mut file + } + None => { + stdin = std::io::stdin(); + &mut stdin + } + }; + + let replaced_indent = matches.value_of("indentation").map(|value| { + value + .to_lowercase() + .chars() + .filter(|c| ['s', 't'].contains(c)) + .collect::() + .replace("s", " ") + .replace("t", "\t") + }); + + let indent = match replaced_indent { + Some(ref str) => Indentation::Custom(str), + None => Indentation::Default, + }; + + let mut output = matches.value_of("output"); + let mut windows_output_default_file: Option = None; + + #[cfg(windows)] + if !matches.is_present("stdout") { + if let Some(file) = matches.value_of("input") { + // on windows, set the default output file if no stdout flag is provided + // this makes it work with drag and drop in windows explorer + windows_output_default_file = Some(file.replace(".nu", "_f.nu")) + } + } + + output = windows_output_default_file.as_deref().or(output); + + // Note: on-stack dynamic dispatch + let (mut file, mut stdout); + let writer: &mut dyn Write = match output { + Some(filename) => { + file = File::create(filename)?; + &mut file + } + None => { + stdout = std::io::stdout(); + &mut stdout + } + }; + + let mut reader = BufReader::new(reader); + let mut writer = BufWriter::new(writer); + format_nu_buffered(&mut reader, &mut writer, indent)?; + + Ok(()) +}