diff --git a/Cargo.lock b/Cargo.lock index 21f92c0..cf012da 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,17 +17,40 @@ version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4361135be9122e0870de935d7c439aef945b9f9ddd4199a553b5270b49c82a27" +[[package]] +name = "async-trait" +version = "0.1.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed6aa3524a2dfcf9fe180c51eae2b58738348d819517ceadf95789c51fff7600" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "atty" version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ - "hermit-abi", + "hermit-abi 0.1.19", "libc", "winapi", ] +[[package]] +name = "auto_impl" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7862e21c893d65a1650125d157eaeec691439379a1cee17ee49031b79236ada4" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "autocfg" version = "1.1.0" @@ -40,6 +63,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bytes" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c" + [[package]] name = "cfg-if" version = "1.0.0" @@ -91,6 +120,16 @@ dependencies = [ "syn", ] +[[package]] +name = "dashmap" +version = "4.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e77a43b28d0668df09411cb0bc9a8c2adc40f9a048afe863e05fd43251e8e39c" +dependencies = [ + "cfg-if", + "num_cpus", +] + [[package]] name = "dirs" version = "4.0.0" @@ -117,6 +156,92 @@ version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" +[[package]] +name = "form_urlencoded" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38390104763dc37a5145a53c29c63c1290b5d316d6086ec32c293f6736051bb0" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac" + +[[package]] +name = "futures-io" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb" + +[[package]] +name = "futures-macro" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9" + +[[package]] +name = "futures-task" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea" + +[[package]] +name = "futures-util" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + [[package]] name = "getrandom" version = "0.2.6" @@ -164,6 +289,31 @@ dependencies = [ "libc", ] +[[package]] +name = "hermit-abi" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +dependencies = [ + "libc", +] + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "idna" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + [[package]] name = "indexmap" version = "1.8.1" @@ -183,6 +333,12 @@ dependencies = [ "either", ] +[[package]] +name = "itoa" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" + [[package]] name = "lazy_static" version = "1.4.0" @@ -204,12 +360,72 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "lsp-types" +version = "0.91.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2368312c59425dd133cb9a327afee65be0a633a8ce471d248e2202a48f8f68ae" +dependencies = [ + "bitflags", + "serde", + "serde_json", + "serde_repr", + "url", +] + +[[package]] +name = "lspower" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc2242d4cb4071c7b9ae53001c8c658402b55bb8c3669a70d3ce9565f1144b30" +dependencies = [ + "anyhow", + "async-trait", + "auto_impl", + "bytes", + "dashmap", + "futures", + "httparse", + "log", + "lsp-types", + "lspower-macros", + "serde", + "serde_json", + "thiserror", + "tokio", + "tokio-util", + "tower-service", + "twoway", +] + +[[package]] +name = "lspower-macros" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca1d48da0e4a6100b4afd52fae99f36d47964a209624021280ad9ffdd410e83d" +dependencies = [ + "heck 0.3.3", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "memchr" version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" +[[package]] +name = "num_cpus" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +dependencies = [ + "hermit-abi 0.2.6", + "libc", +] + [[package]] name = "os_str_bytes" version = "6.0.0" @@ -246,6 +462,24 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c719dcf55f09a3a7e764c6649ab594c18a177e3599c467983cdf644bfc0a4088" +[[package]] +name = "percent-encoding" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" + +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + [[package]] name = "proc-macro-error" version = "1.0.4" @@ -308,12 +542,19 @@ dependencies = [ "thiserror", ] +[[package]] +name = "ryu" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" + [[package]] name = "satysfi-formatter" version = "0.1.0" dependencies = [ "clap 3.1.8", "dirs", + "lspower", "satysfi-parser", ] @@ -331,6 +572,57 @@ dependencies = [ "thiserror", ] +[[package]] +name = "serde" +version = "1.0.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d193d69bae983fc11a79df82342761dfbf28a99fc8d203dca4c3c1b590948965" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f1d362ca8fc9c3e3a7484440752472d68a6caa98f1ab81d99b5dfe517cec852" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877c235533714907a8c2464236f5c4b2a17262ef1bd71f38f35ea592c8da6883" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_repr" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a5ec9fa74a20ebbe5d9ac23dac1fc96ba0ecfe9f50f2843b52e537b10fbcb4e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "slab" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" +dependencies = [ + "autocfg", +] + [[package]] name = "strsim" version = "0.8.0" @@ -422,6 +714,83 @@ dependencies = [ "syn", ] +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" + +[[package]] +name = "tokio" +version = "1.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a12a59981d9e3c38d216785b0c37399f6e415e8d0712047620f189371b0bb" +dependencies = [ + "autocfg", + "pin-project-lite", + "windows-sys", +] + +[[package]] +name = "tokio-util" +version = "0.6.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36943ee01a6d67977dd3f84a5a1d2efeb4ada3a1ae771cadfaa535d9d9fc6507" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "log", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "twoway" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c57ffb460d7c24cd6eda43694110189030a3d1dfe418416d9468fd1c1d290b47" +dependencies = [ + "memchr", + "unchecked-index", +] + +[[package]] +name = "unchecked-index" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eeba86d422ce181a719445e51872fa30f1f7413b62becb52e95ec91aa262d85c" + +[[package]] +name = "unicode-bidi" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + [[package]] name = "unicode-segmentation" version = "1.9.0" @@ -440,6 +809,18 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" +[[package]] +name = "url" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + [[package]] name = "vec_map" version = "0.8.2" @@ -488,3 +869,60 @@ name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" diff --git a/Cargo.toml b/Cargo.toml index 7aa557a..6e6a05c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ repository = "https://github.com/usagrada/" satysfi-parser = { git = "https://github.com/usagrada/satysfi-parser.git" } clap = { version = "3", features = ["derive"] } dirs = "*" +lspower = "1.4.0" [[bin]] name = "satysfi-fmt" diff --git a/src/comment.rs b/src/comment.rs index b0fe59d..ecb555f 100644 --- a/src/comment.rs +++ b/src/comment.rs @@ -111,7 +111,7 @@ fn cst_insert_comment(cst: &mut Cst, comments: &mut VecDeque) { pub fn to_comment_string(text: String) -> String { let index = text.find('%').unwrap(); let comment = text[index + 1..].trim_end(); - if comment.len() == 0 || comment.starts_with(char::is_whitespace) || comment.starts_with('%') { + if comment.is_empty() || comment.starts_with(char::is_whitespace) || comment.starts_with('%') { // 空白or複数の%で始まっていたらそのまま表示 format!("%{}", comment) } else { diff --git a/src/formatter.rs b/src/formatter.rs index e3bf370..913b8b3 100644 --- a/src/formatter.rs +++ b/src/formatter.rs @@ -1,6 +1,6 @@ -use super::OptionData; use crate::comment::{get_comments, to_comment_string, Comment}; use crate::reserved_words::*; +use lspower::lsp::FormattingOptions; use satysfi_parser::{Cst, CstText}; use std::collections::VecDeque; @@ -10,11 +10,11 @@ pub struct Formatter<'a> { pub comments: VecDeque, pub depth: usize, pub output: String, - option: OptionData, + option: FormattingOptions, } impl<'a> Formatter<'a> { - pub fn new(csttext: &'a CstText, option: OptionData) -> Self { + pub fn new(csttext: &'a CstText, option: FormattingOptions) -> Self { let comments = get_comments(csttext); Self { text: &csttext.text, @@ -29,10 +29,10 @@ impl<'a> Formatter<'a> { /// 文字列を format して出力する /// 前処理後処理もここで行う pub fn format(&self, input: &str, cst: &Cst, depth: usize) -> String { - let mut output = self.to_string_cst(input, &cst, depth); + let mut output = self.to_string_cst(input, cst, depth); // 末尾スペースを全て除去 output = output - .split("\n") + .split('\n') .map(|line| line.trim_end()) .collect::>() .join("\n"); @@ -56,7 +56,7 @@ impl<'a> Formatter<'a> { use satysfi_parser::Rule; let csts = cst.inner.clone(); // 関数内で改行するときはこれを使用する - let indent = indent_space(self.option.indent_space, depth); + let indent = indent_space(self.option.tab_size as usize, depth); let newline = format!("\n{indent}"); let sep = &match cst.rule { Rule::block_cmd | Rule::inline_cmd => " ".to_string(), @@ -75,6 +75,7 @@ impl<'a> Formatter<'a> { } Rule::horizontal_single => "".to_string(), // Rule::variant_constructor => " ".to_string(), + Rule::program_saty | Rule::program_satyh => newline.clone(), _ => " ".to_string(), }; @@ -109,7 +110,7 @@ impl<'a> Formatter<'a> { current + " <-" + &newline - + &indent_space(self.option.indent_space, 1) + + &indent_space(self.option.tab_size as usize, 1) + s.trim_start() } else { current + " <- " + &s @@ -217,7 +218,10 @@ impl<'a> Formatter<'a> { Rule::constraint => { // 1つインデントを深くする let s = self.to_string_cst(text, now_cst, depth + 1); - current + &newline + &indent_space(self.option.indent_space, 1) + &s + current + + &newline + + &indent_space(self.option.tab_size as usize, 1) + + &s } Rule::expr => { // 直前がコメント @@ -236,7 +240,7 @@ impl<'a> Formatter<'a> { current + " =" + &newline - + &indent_space(self.option.indent_space, 1) + + &indent_space(self.option.tab_size as usize, 1) + s.trim_start() } else { current + " = " + &s @@ -249,7 +253,7 @@ impl<'a> Formatter<'a> { current + " =" + &newline - + &indent_space(self.option.indent_space, 1) + + &indent_space(self.option.tab_size as usize, 1) + &s } else { current + &s @@ -346,7 +350,7 @@ impl<'a> Formatter<'a> { } let mut iter = csts.into_iter().peekable(); let mut output = String::new(); - while iter.peek() != None { + while iter.peek().is_some() { let now_cst = &iter.next().unwrap(); let s = self.to_string_cst(text, now_cst, depth); let s = if now_cst.rule == Rule::unary { @@ -376,17 +380,17 @@ impl<'a> Formatter<'a> { }; // 次の要素が存在すれば結合 let next = iter.peek(); - if next != None + if next.is_some() && now_cst.rule != Rule::comments && next.unwrap().rule == Rule::comments { output += sep; - } else if next != None + } else if next.is_some() && (next.unwrap().rule == Rule::record_unit || next.unwrap().rule == Rule::type_record_unit) { output += sep; - } else if next == None && now_cst.rule == Rule::comments { + } else if next.is_none() && now_cst.rule == Rule::comments { output = output.trim_end().to_string(); }; } @@ -503,14 +507,14 @@ impl<'a> Formatter<'a> { let output = csts.iter().fold(String::new(), |current, now_cst| { let s = self.to_string_cst(text, now_cst, depth); - let current = match now_cst.rule { + + match now_cst.rule { Rule::expr => current + "if " + &s, Rule::ctrl_then => current + &newline + &s, Rule::ctrl_else => current + &newline + &s, Rule::comments => current + &newline + &s, _ => unreachable!(), - }; - current + } }); output @@ -518,7 +522,8 @@ impl<'a> Formatter<'a> { Rule::ctrl_then | Rule::ctrl_else => { let output = csts.iter().fold(String::new(), |current, now_cst| { let s = self.to_string_cst(text, now_cst, depth); - let output = if current.is_empty() { + + if current.is_empty() { newline.clone() + &s } else if s.is_empty() { current @@ -526,11 +531,10 @@ impl<'a> Formatter<'a> { current + &s } else { current + sep + &s - }; - output + } }); output - .split("\n") + .split('\n') .filter(|line| !line.trim().is_empty()) .collect::>() .join("\n") @@ -703,7 +707,7 @@ impl<'a> Formatter<'a> { let s = self.to_string_cst(text, now_cst, depth + 1); // 1つ深くする current - + &indent_space(self.option.indent_space, 1) + + &indent_space(self.option.tab_size as usize, 1) + s.trim_start() } else { current + s.trim_start() @@ -725,7 +729,7 @@ impl<'a> Formatter<'a> { let mut iter = csts.into_iter().peekable(); let mut now_cst = iter.next().unwrap(); let mut output = self.to_string_cst(text, &now_cst, depth); - while iter.peek() != None { + while iter.peek().is_some() { // 次の要素が存在すれば結合 if now_cst.rule == Rule::type_optional { output += " ?-> "; @@ -751,7 +755,7 @@ impl<'a> Formatter<'a> { let mut iter = csts.into_iter().peekable(); let first = iter.next().unwrap(); let mut output = self.to_string_cst(text, &first, depth); - while iter.peek() != None { + while iter.peek().is_some() { let now_cst = &iter.next().unwrap(); let s = self.to_string_cst(text, now_cst, depth); match now_cst.rule { @@ -903,20 +907,22 @@ impl<'a> Formatter<'a> { .trim_end() .to_string(), Rule::block_cmd | Rule::inline_cmd => { + let mut last_cst_rule = Rule::comments; // 無視できるルールを入れておく csts.iter().fold(String::new(), |current, now_cst| { let s = self.to_string_cst(text, now_cst, depth); - if current.is_empty() { + let output = if current.is_empty() { s } else if s.is_empty() { current } else if current.ends_with(&newline) { current + &s - } else if now_cst.rule == Rule::cmd_text_arg && !self.option.command_args_space - { + } else if last_cst_rule == Rule::cmd_text_arg { current + &s } else { current + sep + &s - } + }; + last_cst_rule = now_cst.rule; + output }) } Rule::math_single => { @@ -931,7 +937,7 @@ impl<'a> Formatter<'a> { current } else if current.ends_with(&newline) { current + &s - } else if last_token.starts_with("\\") { + } else if last_token.starts_with('\\') { current + sep + &s } else if now_cst.rule == Rule::comments { format!("{}{newline}{s}", current.trim_end()) @@ -942,7 +948,7 @@ impl<'a> Formatter<'a> { } else { current + sep + &s }; - last_token = s.clone(); + last_token = s; output }); output @@ -972,6 +978,8 @@ impl<'a> Formatter<'a> { current } else if current.ends_with(&newline) { current + &s + } else if s.starts_with("%\n") { + current + &s } else { // 複数行の改行を省略して1行にする let start = now_cst.span.start; @@ -997,16 +1005,14 @@ impl<'a> Formatter<'a> { current } else if current.ends_with(&newline) { current + &s + } else if now_cst.rule == Rule::bin_operator && s.trim() == "|>" { + current + &s } else { - if now_cst.rule == Rule::bin_operator && s.trim() == "|>" { - current + &s - } else { - current + sep + &s - } + current + sep + &s }; output }), - _ => { + Rule::program_saty => { csts.iter().fold(String::new(), |current, now_cst| { let s = self.to_string_cst(text, now_cst, depth); let output = if current.is_empty() { @@ -1015,17 +1021,33 @@ impl<'a> Formatter<'a> { current } else if current.ends_with(&newline) { current + &s + } else if s.starts_with("%\n") { + current + &s } else { current + sep + &s }; - - if cst.rule == Rule::program_saty && now_cst.rule == Rule::preamble { + if now_cst.rule == Rule::preamble { // program saty だった場合、in を入れる output + "\n" + RESERVED_WORD.in_stmt + "\n\n" } else { output } }) + }, + _ => { + csts.iter().fold(String::new(), |current, now_cst| { + let s = self.to_string_cst(text, now_cst, depth); + + if current.is_empty() { + s + } else if s.is_empty() { + current + } else if current.ends_with(&newline) { + current + &s + } else { + current + sep + &s + } + }) } }; @@ -1046,8 +1068,9 @@ impl<'a> Formatter<'a> { Rule::ctrl_then | Rule::ctrl_else => depth + 1, _ => depth, }; - let start_indent = "\n".to_string() + &indent_space(self.option.indent_space, new_depth); - let end_indent = "\n".to_string() + &indent_space(self.option.indent_space, depth); + let start_indent = + "\n".to_string() + &indent_space(self.option.tab_size as usize, new_depth); + let end_indent = "\n".to_string() + &indent_space(self.option.tab_size as usize, depth); let output = self.to_string_cst_inner(text, cst, new_depth); let self_text = text.get(cst.span.start..cst.span.end).unwrap().to_string(); @@ -1158,6 +1181,8 @@ impl<'a> Formatter<'a> { Rule::block_text => { if !output.is_empty() { format!("'<{start_indent}{output}{end_indent}>") + } else if output.starts_with("%\n") { + format!("'<{output}{end_indent}>") } else { format!("'<{output}>") } @@ -1199,7 +1224,7 @@ impl<'a> Formatter<'a> { // 1つ深くする format!( "{start_indent}{}{self_text}", - indent_space(self.option.indent_space, 1) + indent_space(self.option.tab_size as usize, 1) ) } else { self_text @@ -1230,23 +1255,32 @@ impl<'a> Formatter<'a> { } } Rule::cmd_text_arg | Rule::horizontal_text => { + let output = output.trim(); // 括弧の種類を取得 let start_arg = self_text.chars().next().unwrap(); let end_arg = self_text.chars().last().unwrap(); // コメントで開始 or 改行を含んでいたら、改行を入れる let include_comment = output.starts_with('%'); let include_kaigyou = - output.find('\n') != None || start_arg == '<' || include_comment; - match output.trim().len() { - 0 => format!("{start_arg}{end_arg}"), - // easytable - _ if output.starts_with(char::is_whitespace) => { - format!("{start_arg}\n{output}{end_arg}") + output.find('\n').is_some() || start_arg == '<' || include_comment; + if output.starts_with("%\n") { + if include_kaigyou { + format!("{start_arg}{output}{end_indent}{end_arg}") + } else { + format!("{start_arg} {output} {end_arg}") } - num if include_kaigyou || num > self.option.row_length => { - format!("{start_arg}{start_indent}{output}{end_indent}{end_arg}") + } else { + match output.trim().len() { + 0 => format!("{start_arg}{end_arg}"), + // easytable + _ if output.starts_with(char::is_whitespace) => { + format!("{start_arg}\n{output}{end_arg}") + } + _num if include_kaigyou => { + format!("{start_arg}{start_indent}{output}{end_indent}{end_arg}") + } + _ => format!("{start_arg} {output} {end_arg}"), } - _ => format!("{start_arg} {output} {end_arg}"), } } Rule::inline_cmd => { @@ -1320,7 +1354,10 @@ impl<'a> Formatter<'a> { // horizontal Rule::horizontal_single => output, Rule::horizontal_list => { - let sep = format!("\n{}", indent_space(self.option.indent_space, new_depth)); + let sep = format!( + "\n{}", + indent_space(self.option.tab_size as usize, new_depth) + ); let output = self_text .split('\n') .into_iter() @@ -1336,18 +1373,18 @@ impl<'a> Formatter<'a> { // output format!( "{}{output}", - indent_space(self.option.indent_space, new_depth) + indent_space(self.option.tab_size as usize, new_depth) ) } Rule::horizontal_bullet_list => output, // TODO Rule::horizontal_bullet => output, // TODO Rule::horizontal_bullet_star => { - " ".repeat(self.option.indent_space / 2) + " ".repeat(self.option.tab_size as usize / 2) .repeat(self_text.len() - 1) + &self_text } Rule::regular_text => { - let sep = format!("\n{}", indent_space(self.option.indent_space, depth)); + let sep = format!("\n{}", indent_space(self.option.tab_size as usize, depth)); let output = self_text .split('\n') .into_iter() diff --git a/src/helper.rs b/src/helper.rs new file mode 100644 index 0000000..09c9a9e --- /dev/null +++ b/src/helper.rs @@ -0,0 +1,13 @@ +#[inline] +pub fn indent_space(depth: usize) -> String { + let mut result = String::new(); + for _ in 0..depth { + result.push(' '); + } + result +} + +#[inline] +pub fn indent_tab(depth: usize) -> String { + "\t".repeat(depth) +} diff --git a/src/lib.rs b/src/lib.rs index d741e75..b950e63 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,6 @@ mod comment; mod formatter; +mod helper; mod reserved_words; #[cfg(test)] mod tests; @@ -7,29 +8,14 @@ mod visualize; use comment::*; use formatter::Formatter; +use lspower::lsp::{FormattingOptions, TextEdit}; use satysfi_parser::{grammar, CstText}; pub use visualize::*; -pub struct OptionData { - pub row_length: usize, - pub indent_space: usize, - pub command_args_space: bool, -} - -impl Default for OptionData { - fn default() -> Self { - Self { - row_length: 80, - indent_space: 4, - command_args_space: true, - } - } -} - /// satysfi の文字列を渡すと format したものを返す /// * `input` - satysfi のコード /// * `output` - format された文字列 -pub fn format(input: &str, option: OptionData) -> String { +pub fn format(input: &str, option: FormattingOptions) -> String { /* CstText { text: string, @@ -42,7 +28,10 @@ pub fn format(input: &str, option: OptionData) -> String { let err = csttext.unwrap_err(); let line = err.0.line; let col = err.0.column; - eprintln!("disable to format\n[parse error] line: {}, column: {}", line, col); + eprintln!( + "disable to format\n[parse error] line: {}, column: {}", + line, col + ); return input.to_string(); } let csttext = csttext.unwrap(); @@ -53,7 +42,40 @@ pub fn format(input: &str, option: OptionData) -> String { visualize_csttext_tree(&csttext); let depth = 0; - let output = formatter.format(input, &csttext.cst, depth); + + + formatter.format(input, &csttext.cst, depth) +} - output +pub fn formatting(input: &str, option: FormattingOptions) -> Vec { + let csttext = CstText::parse(input, grammar::program); + if csttext.is_err() { + let err = csttext.unwrap_err(); + let line = err.0.line; + let col = err.0.column; + eprintln!( + "disable to format\n[parse error] line: {}, column: {}", + line, col + ); + return Vec::new(); + } + let csttext = csttext.unwrap(); + let csttext = csttext_insert_comments(csttext); + let formatter = Formatter::new(&csttext, option); + let output = formatter.format(input, &csttext.cst, 0); + let mut edits = Vec::new(); + edits.push(TextEdit { + range: lspower::lsp::Range { + start: lspower::lsp::Position { + line: 0, + character: 0, + }, + end: lspower::lsp::Position { + line: csttext.lines.len() as u32, + character: csttext.text.split('\n').last().unwrap().len() as u32, + }, + }, + new_text: output, + }); + edits } diff --git a/src/main.rs b/src/main.rs index 9e4a394..d0cff46 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,6 @@ use clap::Parser; -use satysfi_formatter::{format, OptionData}; +use lspower::lsp::FormattingOptions; +use satysfi_formatter::format; use std::{fs, path::PathBuf}; #[derive(Parser, Debug)] @@ -25,16 +26,16 @@ struct Cli { fn main() { let cli = Cli::parse(); let code = fs::read_to_string(&cli.file).expect("Failed to read file"); - let option = OptionData { - indent_space: cli.indent_space, - command_args_space: cli.cspace, + let option = FormattingOptions { + tab_size: cli.indent_space as u32, ..Default::default() }; let output = format(&code, option); match (cli.output, cli.write) { - (Some(path), _) => fs::write(&path, output).expect("Failed to write file"), - (None, true) => fs::write(&cli.file, output).expect("Failed to write file"), + (Some(path), _) => fs::write(path, &output).expect("Failed to write file"), + (None, true) => fs::write(&cli.file, &output).expect("Failed to write file"), (None, false) => println!("{}", output), } + println!("{:?}", output) } diff --git a/src/tests/comment.rs b/src/tests/comment.rs index 0089e6d..fdece29 100644 --- a/src/tests/comment.rs +++ b/src/tests/comment.rs @@ -178,3 +178,28 @@ document(|title = { hello }|)'< "#; test_tmpl(text, expect) } + +#[test] +fn test_comment8() { + let text = r#"@import: hello + @require: local %comment + +document(|title = {hello}|)'< ++section{hello}<% + +p{ + hello + } +>% +>"#; + + let expect = r#"@import: hello +@require: local %comment + +document(|title = { hello }|)'< + +section { hello } <% + +p { hello } + >% +> +"#; + test_tmpl(text, expect) +} diff --git a/src/tests/mod.rs b/src/tests/mod.rs index 87d89b4..5c1399d 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -1,4 +1,5 @@ -use crate::{format, OptionData}; +use crate::format; +use lspower::lsp::FormattingOptions; mod comment; mod common; @@ -10,7 +11,11 @@ mod module; mod space; fn test_tmpl(input: &str, expect: &str) { - let option = OptionData::default(); + let option = FormattingOptions { + tab_size: 4, + insert_spaces: true, + ..Default::default() + }; let output = format(input, option); assert_eq!(output, expect); }