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..81d5f15 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,709 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "anyhow" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" + +[[package]] +name = "backtrace" +version = "0.3.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "backtrace-ext" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "537beee3be4a18fb023b570f80e3ae28003db9167a751266b259926e25539d50" +dependencies = [ + "backtrace", +] + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "bstr" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706" +dependencies = [ + "memchr", + "serde", +] + +[[package]] +name = "cc" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47de7e88bbbd467951ae7f5a6f34f70d1b4d9cfce53d5fd70f74ebe118b3db56" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "gimli" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" + +[[package]] +name = "globset" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1" +dependencies = [ + "aho-corasick", + "bstr", + "log", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "ignore" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b46810df39e66e925525d6e38ce1e7f6e1d208f72dc39757880fcb66e2c58af1" +dependencies = [ + "crossbeam-deque", + "globset", + "log", + "memchr", + "regex-automata", + "same-file", + "walkdir", + "winapi-util", +] + +[[package]] +name = "is_ci" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7655c9839580ee829dfacba1d1278c2b7883e50a277ff7541299489d6bdfdc45" + +[[package]] +name = "libc" +version = "0.2.155" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "miette" +version = "7.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4edc8853320c2a0dab800fbda86253c8938f6ea88510dc92c5f1ed20e794afc1" +dependencies = [ + "backtrace", + "backtrace-ext", + "cfg-if", + "miette-derive", + "owo-colors", + "supports-color", + "supports-hyperlinks", + "supports-unicode", + "terminal_size", + "textwrap", + "thiserror", + "unicode-width", +] + +[[package]] +name = "miette-derive" +version = "7.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf09caffaac8068c346b6df2a7fc27a177fd20b39421a39ce0a211bde679a6c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "miniz_oxide" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" +dependencies = [ + "adler", +] + +[[package]] +name = "object" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "081b846d1d56ddfc18fdf1a922e4f6e07a11768ea1b92dec44e42b72712ccfce" +dependencies = [ + "memchr", +] + +[[package]] +name = "orthotypos" +version = "0.0.0" +dependencies = [ + "anyhow", + "ignore", + "miette", + "thiserror", + "tree-sitter", + "tree-sitter-c", + "tree-sitter-cpp", + "tree-sitter-go", + "tree-sitter-json", + "tree-sitter-python", + "tree-sitter-rust", + "tree-sitter-toml-ng", + "tree-sitter-yaml", + "winnow", +] + +[[package]] +name = "owo-colors" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caff54706df99d2a78a5a4e3455ff45448d81ef1bb63c22cd14052ca0e993a3f" + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex" +version = "1.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "rustix" +version = "0.38.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[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 = "serde" +version = "1.0.204" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.204" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "smawk" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" + +[[package]] +name = "supports-color" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9829b314621dfc575df4e409e79f9d6a66a3bd707ab73f23cb4aa3a854ac854f" +dependencies = [ + "is_ci", +] + +[[package]] +name = "supports-hyperlinks" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c0a1e5168041f5f3ff68ff7d95dcb9c8749df29f6e7e89ada40dd4c9de404ee" + +[[package]] +name = "supports-unicode" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7401a30af6cb5818bb64852270bb722533397edcfc7344954a38f420819ece2" + +[[package]] +name = "syn" +version = "2.0.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b146dcf730474b4bcd16c311627b31ede9ab149045db4d6088b3becaea046462" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "terminal_size" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7" +dependencies = [ + "rustix", + "windows-sys 0.48.0", +] + +[[package]] +name = "textwrap" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9" +dependencies = [ + "smawk", + "unicode-linebreak", + "unicode-width", +] + +[[package]] +name = "thiserror" +version = "1.0.62" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2675633b1499176c2dff06b0856a27976a8f9d436737b4cf4f312d4d91d8bbb" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.62" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d20468752b09f49e909e55a5d338caa8bedf615594e9d80bc4c565d30faf798c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tree-sitter" +version = "0.22.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df7cc499ceadd4dcdf7ec6d4cbc34ece92c3fa07821e287aedecd4416c516dca" +dependencies = [ + "cc", + "regex", +] + +[[package]] +name = "tree-sitter-c" +version = "0.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f956d5351d62652864a4ff3ae861747e7a1940dc96c9998ae400ac0d3ce30427" +dependencies = [ + "cc", + "tree-sitter", +] + +[[package]] +name = "tree-sitter-cpp" +version = "0.22.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "537b7e0f0d8c89b8dd6f4d195814da94832f20720c09016c2a3ac3dc3c437993" +dependencies = [ + "cc", + "tree-sitter", +] + +[[package]] +name = "tree-sitter-go" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55cb318be5ccf75f44e054acf6898a5c95d59b53443eed578e16be0cd7ec037f" +dependencies = [ + "cc", + "tree-sitter", +] + +[[package]] +name = "tree-sitter-json" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b737dcb73c35d74b7d64a5f3dde158113c86a012bf3cee2bfdf2150d23b05db" +dependencies = [ + "cc", + "tree-sitter", +] + +[[package]] +name = "tree-sitter-python" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4066c6cf678f962f8c2c4561f205945c84834cce73d981e71392624fdc390a9" +dependencies = [ + "cc", + "tree-sitter", +] + +[[package]] +name = "tree-sitter-rust" +version = "0.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "277690f420bf90741dea984f3da038ace46c4fe6047cba57a66822226cde1c93" +dependencies = [ + "cc", + "tree-sitter", +] + +[[package]] +name = "tree-sitter-toml-ng" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "695d20cd83acf16c02c773f03e76d7b43b19883d4e2ce3652a8f06b5e0da7455" +dependencies = [ + "cc", + "tree-sitter", +] + +[[package]] +name = "tree-sitter-yaml" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aad27ec46ad343d8b514f64dd3fdffb478c592ece561b6c935d90ef55589c6b6" +dependencies = [ + "cc", + "tree-sitter", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-linebreak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" + +[[package]] +name = "unicode-width" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "winapi-util" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "0.6.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59b5e5f6c299a3c7890b876a2a587f3115162487e704907d9b6cd29473052ba1" +dependencies = [ + "memchr", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..719b42c --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,73 @@ +[package] +name = "orthotypos" +edition = "2021" +description = "Source code orthotypography checker" +publish = false + +[features] +default = ["lang-all"] +lang-all = [ + "lang-c", + "lang-cpp", + "lang-go", + "lang-json", + "lang-python", + "lang-rust", + "lang-toml", + "lang-yaml", + +] +lang-c = ["dep:tree-sitter-c"] +lang-cpp = ["dep:tree-sitter-cpp"] +lang-go = ["dep:tree-sitter-go"] +lang-json = ["dep:tree-sitter-json"] +lang-python = ["dep:tree-sitter-python"] +lang-rust = ["dep:tree-sitter-rust"] +lang-toml = ["dep:tree-sitter-toml-ng"] +lang-yaml = ["dep:tree-sitter-yaml"] + +[dependencies] +anyhow = "1.0.86" +ignore = "0.4.22" +miette = { version = "7.2.0", features = ["fancy"] } +thiserror = "1.0.61" +tree-sitter = "0.22.6" +tree-sitter-c = { version = "0.21.4", optional = true } +tree-sitter-cpp = { version = "0.22.2", optional = true } +tree-sitter-go = { version = "0.21.0", optional = true } +tree-sitter-json = { version = "0.21.0", optional = true } +tree-sitter-python = { version = "0.21.0", optional = true } +tree-sitter-rust = { version = "0.21.2", optional = true } +tree-sitter-toml-ng = { version = "0.6.0", optional = true } +tree-sitter-yaml = { version = "0.6.1", optional = true } +winnow = "0.6.13" + +[lints.rust] +trivial_numeric_casts = "warn" +improper_ctypes = "warn" +improper_ctypes_definitions = "warn" +non_ascii_idents = "warn" +rust_2018_idioms = "warn" +rust_2024_compatibility = "warn" +single_use_lifetimes = "warn" + +[lints.clippy] +unwrap_used = "warn" +expect_used = "warn" +indexing_slicing = "warn" +panic_in_result_fn = "warn" +dbg_macro = "warn" +panic = "warn" +todo = "warn" +unnecessary_safety_comment = "warn" +unnecessary_safety_doc = "warn" +unimplemented = "warn" +cast_lossless = "warn" +trailing_empty_array = "warn" +as_ptr_cast_mut = "warn" +lint_groups_priority = "allow" + +[profile.release] +lto = true +strip = "symbols" +panic = "abort" diff --git a/clippy.toml b/clippy.toml new file mode 100644 index 0000000..bbcd22a --- /dev/null +++ b/clippy.toml @@ -0,0 +1,4 @@ +allow-unwrap-in-tests = true +allow-expect-in-tests = true +allow-dbg-in-tests = true +avoid-breaking-exported-api = false diff --git a/src/lint.rs b/src/lint.rs new file mode 100644 index 0000000..2e903d8 --- /dev/null +++ b/src/lint.rs @@ -0,0 +1,9 @@ +pub trait Lint { + type Typo: miette::Diagnostic; + + fn check(s: &str) -> Vec; +} + +mod space_before; + +pub use space_before::SpaceBeforePunctuationMarks; diff --git a/src/lint/space_before.rs b/src/lint/space_before.rs new file mode 100644 index 0000000..00a5196 --- /dev/null +++ b/src/lint/space_before.rs @@ -0,0 +1,236 @@ +use std::ops::Range; + +use miette::{Diagnostic, SourceSpan}; + +use thiserror::Error; + +use winnow::ascii::space1; +use winnow::combinator::{alt, not, preceded, repeat, repeat_till}; +use winnow::error::InputError; +use winnow::token::{none_of, take}; +use winnow::{Located, PResult, Parser}; + +use crate::SharedSource; + +use super::Lint; + +/// A space *before* a punctuation mark has been detected. +/// +/// In English typography, one must not put a space before a colon (`:`), +/// a semicolon (`;`), a question mark (`?`), an exclamation mark (`!`), +/// or an interrobang (`‽`). +/// +/// # References +/// - +#[derive(Error, Debug, Diagnostic)] +#[error("In English typography there is no space before a punctuation mark")] +#[diagnostic(code("orthotypos::space-before-punctuation-mark"), url(docsrs))] +pub struct Typo { + #[source_code] + src: Option, + + #[label("Invalid space here")] + span: SourceSpan, + + #[help] + help: String, +} + +impl Typo { + pub fn new(range: Range, punctuation_mark: char) -> Self { + Self { + src: None, + span: range.into(), + help: format!("remove the space before `{punctuation_mark}`"), + } + } + + pub fn with_source(mut self, src: SharedSource, offset: usize) -> Self { + self.src = Some(src); + self.span = (self.span.offset() + offset, self.span.len()).into(); + self + } +} + +pub struct SpaceBeforePunctuationMarks; + +impl Lint for SpaceBeforePunctuationMarks { + type Typo = Typo; + + fn check(s: &str) -> Vec { + fn space_before_colon<'s>( + input: &mut Located<&'s str>, + ) -> PResult>> { + let colon = preceded(space1, ':').parse_next(input)?; + + // Handles cases when we have an emoji like `:fire:` or `:)`. + // In such cases, we should not mark them as a typo. + // not(terminated(alpha1, ':')).parse_next(input)?; + not(none_of([' '])).parse_next(input)?; + + Ok(colon) + } + + fn space_before_punctuation<'s>( + input: &mut Located<&'s str>, + ) -> PResult>> { + let punctuation_mark = alt(( + space_before_colon, + preceded(space1, alt(('!', '?', '‽', '⸘'))), + )) + .parse_next(input)?; + + Ok(punctuation_mark) + } + + fn locate_space_before_punctuation<'s>( + input: &mut Located<&'s str>, + ) -> PResult>> { + let (_, (punctuation_mark, range)): (String, (char, Range)) = repeat_till( + 1.., + take::<_, _, InputError<_>>(1usize), + space_before_punctuation.with_span(), + ) + .parse_next(input)?; + + Ok(Typo::new(range, punctuation_mark)) + } + + repeat(0.., locate_space_before_punctuation) + .parse_next(&mut Located::new(s)) + .unwrap_or_default() + } +} + +#[cfg(test)] +mod tests { + use crate::lint::Lint; + use crate::SharedSource; + + use super::SpaceBeforePunctuationMarks; + + #[test] + fn empty() { + assert!(SpaceBeforePunctuationMarks::check("").is_empty()); + } + + #[test] + fn space_after_colon() { + let typos = SpaceBeforePunctuationMarks::check("test: foobar"); + assert!(typos.is_empty()); + } + + #[test] + fn typo_colon() { + let mut typos = SpaceBeforePunctuationMarks::check("test : foobar"); + let typo = typos.pop().unwrap(); + assert_eq!(typo.span, (4, 2).into()); + assert!(typos.is_empty()); + } + + #[test] + fn typo_question_mark() { + let mut typos = SpaceBeforePunctuationMarks::check("footest ? foobar"); + let typo = typos.pop().unwrap(); + assert_eq!(typo.span, (7, 2).into()); + assert!(typos.is_empty()); + } + + #[test] + fn typo_exclamation_mark() { + let mut typos = SpaceBeforePunctuationMarks::check("footest ! barfoobar"); + let typo = typos.pop().unwrap(); + assert_eq!(typo.span, (7, 2).into()); + assert!(typos.is_empty()); + } + + #[test] + fn typo_exclamation_mark_repeated() { + let mut typos = SpaceBeforePunctuationMarks::check("footest !!!! barfoobar"); + let typo = typos.pop().unwrap(); + assert_eq!(typo.span, (7, 2).into()); + assert!(typos.is_empty()); + } + + #[test] + fn typo_before_end_of_line() { + let mut typos = SpaceBeforePunctuationMarks::check("footest !"); + let typo = typos.pop().unwrap(); + assert_eq!(typo.span, (7, 2).into()); + assert!(typos.is_empty()); + + let mut typos = SpaceBeforePunctuationMarks::check("footest ?"); + let typo = typos.pop().unwrap(); + assert_eq!(typo.span, (7, 2).into()); + assert!(typos.is_empty()); + + let mut typos = SpaceBeforePunctuationMarks::check("footest :"); + let typo = typos.pop().unwrap(); + assert_eq!(typo.span, (7, 2).into()); + assert!(typos.is_empty()); + } + + #[test] + fn multiple_typos() { + let mut typos = SpaceBeforePunctuationMarks::check("footest ! barfoobar : oh no ?"); + + let typo = typos.pop().unwrap(); + assert_eq!(typo.span, (27, 2).into()); + let typo = typos.pop().unwrap(); + assert_eq!(typo.span, (19, 2).into()); + let typo = typos.pop().unwrap(); + assert_eq!(typo.span, (7, 2).into()); + + assert!(typos.is_empty()); + } + + #[test] + fn typo_colon_multiple_spaces() { + let mut typos = SpaceBeforePunctuationMarks::check("test : foobar"); + let typo = typos.pop().unwrap(); + assert_eq!(typo.span, (4, 3).into()); + assert!(typos.is_empty()); + } + + #[test] + fn emoji() { + assert!(SpaceBeforePunctuationMarks::check(":waving_hand:").is_empty()); + assert!(SpaceBeforePunctuationMarks::check("footest :fire: bar").is_empty()); + assert!(SpaceBeforePunctuationMarks::check("foobar :)").is_empty()); + assert!(SpaceBeforePunctuationMarks::check(":D").is_empty()); + assert!(SpaceBeforePunctuationMarks::check(" :> ").is_empty()); + assert!(SpaceBeforePunctuationMarks::check("foo :'( bar").is_empty()); + } + + #[test] + fn typo_source() { + let source = "\"test : foobar\""; + let mut typos = SpaceBeforePunctuationMarks::check(source.trim_matches('"')); + let typo = typos.pop().unwrap(); + assert_eq!(typo.span, (4, 3).into()); + let source = SharedSource::new("fake.rs", source); + let typo = typo.with_source(source, 1); + assert_eq!(typo.span, (5, 3).into()); + assert!(typos.is_empty()); + } + + #[test] + fn interrobang() { + assert!(SpaceBeforePunctuationMarks::check("test‽").is_empty()); + assert!(SpaceBeforePunctuationMarks::check("test?!").is_empty()); + assert!(SpaceBeforePunctuationMarks::check("test!?").is_empty()); + assert!(SpaceBeforePunctuationMarks::check("test⸘").is_empty()); + + let mut typos = SpaceBeforePunctuationMarks::check("test ‽"); + let typo = typos.pop().unwrap(); + assert_eq!(typo.span, (4, 4).into()); + assert!(typos.is_empty()); + + let mut typos = SpaceBeforePunctuationMarks::check("test ?! abc ⸘"); + let typo = typos.pop().unwrap(); + assert_eq!(typo.span, (11, 4).into()); + let typo = typos.pop().unwrap(); + assert_eq!(typo.span, (4, 2).into()); + assert!(typos.is_empty()); + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..c2e1af0 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,166 @@ +use std::cell::OnceCell; +use std::collections::HashMap; +use std::ffi::OsStr; +use std::fs::Metadata; +use std::ops::Deref; +use std::sync::{Arc, OnceLock}; + +use miette::{MietteError, NamedSource, SourceCode, SpanContents}; + +use tree_sitter::{Language, Node, Parser, TreeCursor}; + +use crate::lint::{Lint, SpaceBeforePunctuationMarks}; + +mod lint; + +fn for_each_node<'a>(mut c: TreeCursor<'a>, mut callback: impl FnMut(Node<'a>)) { + loop { + callback(c.node()); + + if c.goto_first_child() { + continue; + } + + if c.goto_next_sibling() { + continue; + } + + loop { + if !c.goto_parent() { + return; + } + + if c.goto_next_sibling() { + break; + } + } + } +} + +#[derive(Debug, Clone)] +pub struct SharedSource(Arc>>); + +impl SharedSource { + pub fn new(name: impl AsRef, bytes: impl AsRef<[u8]>) -> Self { + Self(Arc::new(NamedSource::new(name, bytes.as_ref().to_owned()))) + } +} + +impl SourceCode for SharedSource { + fn read_span<'a>( + &'a self, + span: &miette::SourceSpan, + context_lines_before: usize, + context_lines_after: usize, + ) -> Result + 'a>, MietteError> { + self.0 + .read_span(span, context_lines_before, context_lines_after) + } +} + +struct Lazy { + cell: OnceLock, + init: fn() -> T, +} + +impl Lazy { + pub const fn new(init: fn() -> T) -> Self { + Self { + cell: OnceLock::new(), + init, + } + } +} + +impl Deref for Lazy { + type Target = T; + + #[inline] + fn deref(&self) -> &'_ T { + self.cell.get_or_init(self.init) + } +} + +static EXTENSION_LANGUAGE: Lazy> = Lazy::new(|| { + let mut map = HashMap::new(); + + #[cfg(feature = "lang-rust")] + map.insert(OsStr::new("rs"), tree_sitter_rust::language()); + #[cfg(feature = "lang-cpp")] + map.insert(OsStr::new("cpp"), tree_sitter_cpp::language()); + #[cfg(feature = "lang-c")] + map.insert(OsStr::new("c"), tree_sitter_c::language()); + #[cfg(feature = "lang-go")] + map.insert(OsStr::new("go"), tree_sitter_go::language()); + #[cfg(feature = "lang-python")] + map.insert(OsStr::new("py"), tree_sitter_python::language()); + #[cfg(feature = "lang-toml")] + map.insert(OsStr::new("toml"), tree_sitter_toml_ng::language()); + #[cfg(feature = "lang-yaml")] + map.insert(OsStr::new("yml"), tree_sitter_yaml::language()); + #[cfg(feature = "lang-json")] + map.insert(OsStr::new("json"), tree_sitter_json::language()); + + map +}); + +fn main() -> anyhow::Result<()> { + let valid_kinds = [ + "line_comment", + "string_content", + "string", + "interpreted_string_literal", + "string_scalar", + "double_quote_scalar", + ]; + let mut parser = Parser::new(); + + for file in ignore::Walk::new(".") + .filter_map(Result::ok) + .filter(|entry| { + entry + .metadata() + .as_ref() + .map(Metadata::is_file) + .unwrap_or(false) + }) + { + let extension = file.path().extension().unwrap_or_default(); + let Some(language) = EXTENSION_LANGUAGE.get(extension) else { + continue; + }; + parser.set_language(language)?; + + let source_content = std::fs::read(file.path())?; + let Some(tree) = parser.parse(&source_content, None) else { + continue; + }; + + let source = OnceCell::new(); + + for_each_node(tree.walk(), |node| { + let kind = node.kind(); + if !valid_kinds.contains(&kind) { + return; + } + if node.byte_range().len() <= 3 { + return; + } + let Some(string) = source_content.get(node.byte_range()) else { + return; + }; + + let string = String::from_utf8_lossy(string); + for typo in SpaceBeforePunctuationMarks::check(&string) { + let source = source.get_or_init(|| { + SharedSource::new(file.path().to_string_lossy(), &source_content) + }); + let typo = typo.with_source(source.clone(), node.start_byte()); + let typo: miette::Report = typo.into(); + eprintln!("{typo:?}"); + } + }); + } + + Ok(()) +}