diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 00000000..c12f526c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,30 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: bug +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Platform** +What platform has the issue? Is it in Obsidian, Neovim, or Visual Studio Code? Something else? + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/report-grammatical-error.md b/.github/ISSUE_TEMPLATE/report-grammatical-error.md new file mode 100644 index 00000000..29243d91 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/report-grammatical-error.md @@ -0,0 +1,20 @@ +--- +name: Report Grammatical Error +about: If a grammatical error is not found by Harper, let us tus now +title: '' +labels: enhancement, harper-core, linting +assignees: '' + +--- + +## Description + +Give a detailed description of the grammatical error Harper should be able to find. + +## Resources + +If there are any resources online we can reference that described grammatical rules, let us know. + +## Examples + +Please provide at least three examples of the grammatical error in real text. diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 53f8242a..fe09da75 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -4,3 +4,7 @@ updates: directory: "/" schedule: interval: "weekly" + - package-ecosystem: "npm" + directory: "/packages" + schedule: + interval: "weekly" diff --git a/.github/workflows/build_harper_binaries.yml b/.github/workflows/build_harper_binaries.yml index a6222aae..16fd74fa 100644 --- a/.github/workflows/build_harper_binaries.yml +++ b/.github/workflows/build_harper_binaries.yml @@ -1,4 +1,4 @@ -name: Release +name: Build Binaries on: push: diff --git a/.github/workflows/build_web.yml b/.github/workflows/build_web.yml index 97c9b92b..a26236fa 100644 --- a/.github/workflows/build_web.yml +++ b/.github/workflows/build_web.yml @@ -2,18 +2,26 @@ name: Build Web on: push: - branches: ["master", "web-prod"] + branches: ['master', 'web-prod'] pull_request: - branches: ["master"] + branches: ['master'] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + - name: Setup node + uses: actions/setup-node@v4 + with: + node-version-file: '.node-version' + - name: Retrieve version after install + id: nodenv + run: echo "node-version=$(node -v | sed 's/^v//')" >> $GITHUB_OUTPUT - uses: redhat-actions/buildah-build@v2 with: image: web layers: false containerfiles: | Dockerfile + build-args: NODE_VERSION=${{ steps.nodenv.outputs.node-version }}-slim diff --git a/.github/workflows/release_harper_binaries.yml b/.github/workflows/release_harper_binaries.yml new file mode 100644 index 00000000..18d48064 --- /dev/null +++ b/.github/workflows/release_harper_binaries.yml @@ -0,0 +1,118 @@ +name: Release Binaries + +on: + push: + tags: + - "v*" + +jobs: + release: + name: Release ${{ matrix.platform.project }} - ${{ matrix.platform.release_for }} + if: github.event.pull_request.draft == false + strategy: + matrix: + platform: + - release_for: Windows-x86_64 + os: windows-latest + target: x86_64-pc-windows-msvc + project: harper-ls + bin: harper-ls.exe + name: harper-ls-x86_64-pc-windows-msvc.zip + command: build + - release_for: macOS-x86_64 + os: macOS-latest + target: x86_64-apple-darwin + project: harper-ls + bin: harper-ls + name: harper-ls-x86_64-apple-darwin.tar.gz + command: build + - release_for: macOS-aarch64 + os: macOS-latest + target: aarch64-apple-darwin + project: harper-ls + bin: harper-ls + name: harper-ls-aarch64-apple-darwin.tar.gz + command: build + - release_for: Linux-x86_64-GNU + os: ubuntu-20.04 + target: x86_64-unknown-linux-gnu + project: harper-ls + bin: harper-ls + name: harper-ls-x86_64-unknown-linux-gnu.tar.gz + command: build + - release_for: Linux-aarch64-GNU + os: ubuntu-20.04 + target: aarch64-unknown-linux-gnu + project: harper-ls + bin: harper-ls + name: harper-ls-aarch64-unknown-linux-gnu.tar.gz + command: build + + - release_for: Windows-x86_64 + os: windows-latest + target: x86_64-pc-windows-msvc + project: harper-cli + bin: harper-cli.exe + name: harper-cli-x86_64-pc-windows-msvc.zip + command: build + - release_for: macOS-x86_64 + os: macOS-latest + target: x86_64-apple-darwin + project: harper-cli + bin: harper-cli + name: harper-cli-x86_64-apple-darwin.tar.gz + command: build + - release_for: macOS-aarch64 + os: macOS-latest + target: aarch64-apple-darwin + project: harper-cli + bin: harper-cli + name: harper-cli-aarch64-apple-darwin.tar.gz + command: build + - release_for: Linux-x86_64-GNU + os: ubuntu-20.04 + target: x86_64-unknown-linux-gnu + project: harper-cli + bin: harper-cli + name: harper-cli-x86_64-unknown-linux-gnu.tar.gz + command: build + - release_for: Linux-aarch64-GNU + os: ubuntu-20.04 + target: aarch64-unknown-linux-gnu + project: harper-cli + bin: harper-cli + name: harper-cli-aarch64-unknown-linux-gnu.tar.gz + command: build + + runs-on: ${{ matrix.platform.os }} + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Build binary + uses: houseabsolute/actions-rust-cross@v0 + with: + command: ${{ matrix.platform.command }} + target: ${{ matrix.platform.target }} + args: "--locked --release --bin ${{ matrix.platform.project }}" + strip: true + - name: Package as archive + shell: bash + run: | + cd target/${{ matrix.platform.target }}/release + if [[ "${{ matrix.platform.os }}" == "windows-latest" ]]; then + 7z a ../../../${{ matrix.platform.name }} ${{ matrix.platform.bin }} + else + tar czvf ../../../${{ matrix.platform.name }} ${{ matrix.platform.bin }} + fi + cd - + - uses: ncipollo/release-action@v1 + with: + artifacts: ${{ matrix.platform.name }} + allowUpdates: true + draft: true + - name: Upload Artifacts + uses: actions/upload-artifact@v4 + with: + name: ${{ matrix.platform.bin }}-${{ matrix.platform.target }} + path: ${{ matrix.platform.name }} + diff --git a/.github/workflows/release_vscode_plugin.yml b/.github/workflows/release_vscode_plugin.yml new file mode 100644 index 00000000..2210cc56 --- /dev/null +++ b/.github/workflows/release_vscode_plugin.yml @@ -0,0 +1,77 @@ +name: Release VS Code Plugin + +on: + push: + tags: + - "v*" + +jobs: + package: + name: Package - ${{ matrix.platform.code_target }} + if: github.event.pull_request.draft == false + strategy: + matrix: + platform: + - os: windows-latest + rust_target: x86_64-pc-windows-msvc + code_target: win32-x64 + - os: macOS-latest + rust_target: x86_64-apple-darwin + code_target: darwin-x64 + - os: macOS-latest + rust_target: aarch64-apple-darwin + code_target: darwin-arm64 + - os: ubuntu-20.04 + rust_target: x86_64-unknown-linux-gnu + code_target: linux-x64 + - os: ubuntu-20.04 + rust_target: aarch64-unknown-linux-gnu + code_target: linux-arm64 + runs-on: ${{ matrix.platform.os }} + steps: + - uses: actions/checkout@v4 + - uses: extractions/setup-just@v2 + - uses: actions/setup-node@v4 + with: + node-version: 20 + - name: Build harper-ls + uses: houseabsolute/actions-rust-cross@v0 + with: + target: ${{ matrix.platform.rust_target }} + args: "--locked --release --bin harper-ls" + strip: true + - name: Package extension + id: package_extension + shell: bash + run: | + bin_dir="packages/vscode-plugin/bin" + release_dir="target/${{ matrix.platform.rust_target }}/release" + + mkdir "$bin_dir" + + if [[ "${{ matrix.platform.os }}" == "windows-latest" ]]; then + cp "${release_dir}/harper-ls.exe" "$bin_dir" + else + cp "${release_dir}/harper-ls" "$bin_dir" + fi + + just package-vscode ${{ matrix.platform.code_target }} + echo artifact=$(echo packages/vscode-plugin/*.vsix) >> $GITHUB_OUTPUT + - uses: ncipollo/release-action@v1 + with: + artifacts: "./packages/vscode-plugin/*.vsix" + allowUpdates: true + draft: true + - name: Publish to OpenVSX + uses: HaaLeo/publish-vscode-extension@v1 + with: + pat: ${{ secrets.OPEN_VSX_TOKEN }} + packagePath: "./packages/vscode-plugin/" + extensionFile: ${{ steps.package_extension.outputs.artifact }} + - name: Publish to the Visual Studio Marketplace + uses: HaaLeo/publish-vscode-extension@v1 + with: + pat: ${{ secrets.VS_MARKETPLACE_TOKEN }} + packagePath: "./packages/vscode-plugin/" + extensionFile: ${{ steps.package_extension.outputs.artifact }} + registryUrl: https://marketplace.visualstudio.com diff --git a/.node-version b/.node-version new file mode 100644 index 00000000..0a47c855 --- /dev/null +++ b/.node-version @@ -0,0 +1 @@ +lts/iron \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index e475126b..db200f7b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -79,11 +79,12 @@ dependencies = [ [[package]] name = "anstyle-wincon" -version = "3.0.6" +version = "3.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" +checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" dependencies = [ "anstyle", + "once_cell", "windows-sys 0.59.0", ] @@ -105,24 +106,24 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.84" +version = "0.1.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b1244b10dcd56c92219da4e14caa97e312079e185f04ba3eea25061561dc0a0" +checksum = "3f934833b4b7233644e5848f235df3f57ed8c80f1528a26c3dfa13d2147fa056" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] name = "auto_impl" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" +checksum = "e12882f59de5360c748c4cbf569a042d5fb0eb515f7bea9c1f470b47f6ffbd73" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -154,9 +155,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.6.0" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" [[package]] name = "blanket" @@ -166,7 +167,7 @@ checksum = "56791e4bd64c99fc361e01008f45c984baa93f12a0957d1b3c51dd2c6baab453" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -263,7 +264,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -324,9 +325,9 @@ dependencies = [ [[package]] name = "crossbeam-deque" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" dependencies = [ "crossbeam-epoch", "crossbeam-utils", @@ -343,15 +344,15 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.20" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "crunchy" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" +checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" [[package]] name = "dashmap" @@ -415,7 +416,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -526,7 +527,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -612,7 +613,7 @@ dependencies = [ [[package]] name = "harper-comments" -version = "0.17.0" +version = "0.18.1" dependencies = [ "harper-core", "harper-html", @@ -625,12 +626,14 @@ dependencies = [ "tree-sitter-c-sharp", "tree-sitter-cmake", "tree-sitter-cpp", + "tree-sitter-dart", "tree-sitter-go", "tree-sitter-haskell", "tree-sitter-java", "tree-sitter-javascript", "tree-sitter-lua", "tree-sitter-nix", + "tree-sitter-php", "tree-sitter-python", "tree-sitter-ruby", "tree-sitter-rust", @@ -641,7 +644,7 @@ dependencies = [ [[package]] name = "harper-core" -version = "0.17.0" +version = "0.18.1" dependencies = [ "blanket", "criterion", @@ -662,12 +665,13 @@ dependencies = [ "smallvec", "thiserror 2.0.11", "unicode-blocks", + "unicode-script", "unicode-width 0.2.0", ] [[package]] name = "harper-html" -version = "0.17.0" +version = "0.18.1" dependencies = [ "harper-core", "harper-tree-sitter", @@ -678,7 +682,7 @@ dependencies = [ [[package]] name = "harper-literate-haskell" -version = "0.17.0" +version = "0.18.1" dependencies = [ "harper-comments", "harper-core", @@ -689,7 +693,7 @@ dependencies = [ [[package]] name = "harper-ls" -version = "0.17.0" +version = "0.18.1" dependencies = [ "anyhow", "clap", @@ -714,7 +718,7 @@ dependencies = [ [[package]] name = "harper-tree-sitter" -version = "0.17.0" +version = "0.18.1" dependencies = [ "harper-core", "tree-sitter", @@ -722,7 +726,7 @@ dependencies = [ [[package]] name = "harper-typst" -version = "0.17.0" +version = "0.18.1" dependencies = [ "harper-core", "itertools 0.14.0", @@ -897,7 +901,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -923,9 +927,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.6.0" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" +checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652" dependencies = [ "equivalent", "hashbrown 0.15.2", @@ -949,18 +953,18 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] name = "is-terminal" -version = "0.4.13" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b" +checksum = "e19b23d53f35ce9f56aebc7d1bb4e6ac1e9c0db7ac85c8d1760c04379edced37" dependencies = [ "hermit-abi", "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -1005,9 +1009,9 @@ checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" [[package]] name = "js-sys" -version = "0.3.76" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" dependencies = [ "once_cell", "wasm-bindgen", @@ -1040,7 +1044,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "libc", ] @@ -1062,9 +1066,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.22" +version = "0.4.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" [[package]] name = "lsp-types" @@ -1087,9 +1091,9 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "miniz_oxide" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ffbe83022cedc1d264172192511ae958937694cd57ce297164951b8b3568394" +checksum = "b8402cab7aefae129c6977bb0ff1b8fd9a04eb5b51efc50a70bea51cda0c7924" dependencies = [ "adler2", ] @@ -1227,7 +1231,7 @@ checksum = "d56a66c0c55993aa927429d0f8a0abfd74f084e4d9c192cffed01e418d83eefb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -1259,9 +1263,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.92" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" +checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" dependencies = [ "unicode-ident", ] @@ -1272,7 +1276,7 @@ version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f86ba2052aebccc42cbbb3ed234b8b13ce76f75c3551a303cb2bcffcff12bb14" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "getopts", "memchr", "pulldown-cmark-escape", @@ -1374,7 +1378,7 @@ version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", ] [[package]] @@ -1443,6 +1447,12 @@ version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +[[package]] +name = "rustversion" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" + [[package]] name = "ryu" version = "1.0.18" @@ -1492,7 +1502,7 @@ checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -1515,7 +1525,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -1595,9 +1605,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.95" +version = "2.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46f71c0377baf4ef1cc3e3402ded576dccc315800fbc62dfc7fe04b009773b4a" +checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" dependencies = [ "proc-macro2", "quote", @@ -1612,7 +1622,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -1647,7 +1657,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -1658,7 +1668,7 @@ checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -1715,7 +1725,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -1816,7 +1826,7 @@ checksum = "84fd902d4e0b9a4b27f2f440108dc034e1758628a9b702f8ec61ad66355422fa" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -1844,7 +1854,7 @@ checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -1953,6 +1963,16 @@ dependencies = [ "tree-sitter", ] +[[package]] +name = "tree-sitter-dart" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fb6a2192689dd0554c558cfb81d96e446c41d101d701521fd1b452774d132ba" +dependencies = [ + "cc", + "tree-sitter", +] + [[package]] name = "tree-sitter-go" version = "0.20.0" @@ -2023,6 +2043,16 @@ dependencies = [ "tree-sitter", ] +[[package]] +name = "tree-sitter-php" +version = "0.22.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7b46f2b021f0e1e51d4fd3b78bec588ce478b291c1affeb7e2acadda3b5fda5" +dependencies = [ + "cc", + "tree-sitter", +] + [[package]] name = "tree-sitter-python" version = "0.20.4" @@ -2128,9 +2158,9 @@ checksum = "6b12e05d9e06373163a9bb6bb8c263c261b396643a99445fe6b9811fd376581b" [[package]] name = "unicode-ident" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" +checksum = "11cd88e12b17c6494200a9c1b683a04fcac9573ed74cd1b62aeb2727c5592243" [[package]] name = "unicode-math-class" @@ -2200,9 +2230,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "valuable" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" [[package]] name = "walkdir" @@ -2222,34 +2252,35 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ "cfg-if", "once_cell", + "rustversion", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" dependencies = [ "bumpalo", "log", "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2257,22 +2288,25 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] [[package]] name = "winapi" @@ -2389,9 +2423,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.6.20" +version = "0.6.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" +checksum = "c8d71a593cc5c42ad7876e2c1fda56f314f3754c084128833e64f1345ff8a03a" dependencies = [ "memchr", ] @@ -2434,7 +2468,7 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", "synstructure", ] @@ -2456,7 +2490,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -2476,7 +2510,7 @@ checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", "synstructure", ] @@ -2499,5 +2533,5 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] diff --git a/Dockerfile b/Dockerfile index f757fcef..1c554f91 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,3 +1,5 @@ +ARG NODE_VERSION=slim + FROM rust:latest AS wasm-build RUN mkdir -p /usr/build/ @@ -10,7 +12,7 @@ COPY . . WORKDIR /usr/build/harper-wasm RUN wasm-pack build --release --target web -FROM node:slim AS node-build +FROM node:${NODE_VERSION} AS node-build RUN apt-get update && apt-get install git pandoc -y @@ -31,7 +33,7 @@ WORKDIR /usr/build/packages/web RUN yarn install && yarn build -FROM node:slim +FROM node:${NODE_VERSION} COPY --from=node-build /usr/build/packages/web/build /usr/build/packages/web/build COPY --from=node-build /usr/build/packages/web/package.json /usr/build/packages/web/package.json diff --git a/harper-cli/Cargo.toml b/harper-cli/Cargo.toml index b2a5c8a5..80938d3c 100644 --- a/harper-cli/Cargo.toml +++ b/harper-cli/Cargo.toml @@ -10,10 +10,10 @@ repository = "https://github.com/automattic/harper" anyhow = "1.0.95" ariadne = "0.4.1" clap = { version = "4.5.27", features = ["derive"] } -harper-literate-haskell = { path = "../harper-literate-haskell", version = "0.17.0" } -harper-core = { path = "../harper-core", version = "0.17.0" } -harper-comments = { path = "../harper-comments", version = "0.17.0" } -harper-typst = { path = "../harper-typst", version = "0.17.0" } +harper-literate-haskell = { path = "../harper-literate-haskell", version = "0.18.0" } +harper-core = { path = "../harper-core", version = "0.18.0" } +harper-comments = { path = "../harper-comments", version = "0.18.0" } +harper-typst = { path = "../harper-typst", version = "0.18.0" } serde = { version = "1.0.214", features = ["derive"] } serde_json = "1.0.137" diff --git a/harper-comments/Cargo.toml b/harper-comments/Cargo.toml index 8d8726ea..fe579c0c 100644 --- a/harper-comments/Cargo.toml +++ b/harper-comments/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "harper-comments" -version = "0.17.0" +version = "0.18.1" edition = "2021" description = "The language checker for developers." license = "Apache-2.0" @@ -8,9 +8,9 @@ readme = "README.md" repository = "https://github.com/automattic/harper" [dependencies] -harper-core = { path = "../harper-core", version = "0.17.0" } -harper-html = { path = "../harper-html", version = "0.17.0" } -harper-tree-sitter = { path = "../harper-tree-sitter", version = "0.17.0" } +harper-core = { path = "../harper-core", version = "0.18.0" } +harper-html = { path = "../harper-html", version = "0.18.0" } +harper-tree-sitter = { path = "../harper-tree-sitter", version = "0.18.0" } tree-sitter = "0.20.10" tree-sitter-rust = "0.20.4" tree-sitter-typescript = "0.20.3" @@ -30,6 +30,8 @@ tree-sitter-java = "0.20.0" tree-sitter-nix = "0.0.1" itertools = "0.14.0" tree-sitter-haskell = "0.15.0" +tree-sitter-php = "=0.22.2" +tree-sitter-dart = "0.0.3" [dev-dependencies] paste = "1.0.15" diff --git a/harper-comments/src/comment_parser.rs b/harper-comments/src/comment_parser.rs index bf631c7b..297901c7 100644 --- a/harper-comments/src/comment_parser.rs +++ b/harper-comments/src/comment_parser.rs @@ -41,6 +41,8 @@ impl CommentParser { "shellscript" => tree_sitter_bash::language(), "java" => tree_sitter_java::language(), "haskell" => tree_sitter_haskell::language(), + "php" => tree_sitter_php::language_php(), + "dart" => tree_sitter_dart::language(), _ => return None, }; @@ -94,6 +96,8 @@ impl CommentParser { "bash" => "shellscript", "java" => "java", "hs" => "haskell", + "php" => "php", + "dart" => "dart", _ => return None, }) } diff --git a/harper-comments/tests/language_support.rs b/harper-comments/tests/language_support.rs index 7d816e35..2cf3cf28 100644 --- a/harper-comments/tests/language_support.rs +++ b/harper-comments/tests/language_support.rs @@ -52,6 +52,7 @@ create_test!(merged_lines.ts, 1); create_test!(javadoc_clean_simple.java, 0); create_test!(javadoc_complex.java, 4); create_test!(issue_132.rs, 1); +create_test!(laravel_app.php, 2); // These are to make sure nothing crashes. create_test!(empty.js, 0); diff --git a/harper-comments/tests/language_support_sources/laravel_app.php b/harper-comments/tests/language_support_sources/laravel_app.php new file mode 100644 index 00000000..f4672673 --- /dev/null +++ b/harper-comments/tests/language_support_sources/laravel_app.php @@ -0,0 +1,126 @@ + env('APP_NAME', 'Laravel'), + + /* + |-------------------------------------------------------------------------- + | Application Environment + |-------------------------------------------------------------------------- + | + | This value determines the "environment" your application is currently + | running in. This may determine how you prefer to configure various + | services the application utilizes. Set this in your ".env" file. + | + */ + + 'env' => env('APP_ENV', 'production'), + + /* + |-------------------------------------------------------------------------- + | Application Debug Mode + |-------------------------------------------------------------------------- + | + | When your application is in debug mode, detailed error messages with + | stack traces will be shown on every error that occurs within your + | application. If disabled, a simple generic error page is shown. + | + */ + + 'debug' => (bool) env('APP_DEBUG', false), + + /* + |-------------------------------------------------------------------------- + | Application URL + |-------------------------------------------------------------------------- + | + | This URL is used by the console to properly generate URLs when using + | the Artisan command line tool. You should set this to the root of + | the application so that it's available within Artisan commands. + | + */ + + 'url' => env('APP_URL', 'http://localhost'), + + /* + |-------------------------------------------------------------------------- + | Application Timezone + |-------------------------------------------------------------------------- + | + | Here you may specify the default timezone for your application, which + | will be used by the PHP date and date-time functions. The timezone + | is set to "UTC" by default as it is suitable for most use cases. + | + */ + + 'timezone' => env('APP_TIMEZONE', 'UTC'), + + /* + |-------------------------------------------------------------------------- + | Application Locale Configuration + |-------------------------------------------------------------------------- + | + | The application locale determines the default locale that will be used + | by Laravel's translation / localization methods. This option can be + | set to any locale for which you plan to have translation strings. + | + */ + + 'locale' => env('APP_LOCALE', 'en'), + + 'fallback_locale' => env('APP_FALLBACK_LOCALE', 'en'), + + 'faker_locale' => env('APP_FAKER_LOCALE', 'en_US'), + + /* + |-------------------------------------------------------------------------- + | Encryption Key + |-------------------------------------------------------------------------- + | + | This key is utilized by Laravel's encryption services and should be set + | to a random, 32 character string to ensure that all encrypted values + | are secure. You should do this prior to deploying the application. + | + */ + + 'cipher' => 'AES-256-CBC', + + 'key' => env('APP_KEY'), + + 'previous_keys' => [ + ...array_filter( + explode(',', env('APP_PREVIOUS_KEYS', '')) + ), + ], + + /* + |-------------------------------------------------------------------------- + | Maintenance Mode Driver + |-------------------------------------------------------------------------- + | + | These configuration options determine the driver used to determine and + | manage Laravel's "maintenance mode" status. The "cache" driver will + | allow maintenance mode to be controlled across multiple machines. + | + | Supported drivers: "file", "cache" + | + */ + + 'maintenance' => [ + 'driver' => env('APP_MAINTENANCE_DRIVER', 'file'), + 'store' => env('APP_MAINTENANCE_STORE', 'database'), + ], + +]; diff --git a/harper-core/Cargo.toml b/harper-core/Cargo.toml index 4ae24b4b..4b524ff9 100644 --- a/harper-core/Cargo.toml +++ b/harper-core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "harper-core" -version = "0.17.0" +version = "0.18.1" edition = "2021" description = "The language checker for developers." license = "Apache-2.0" @@ -22,6 +22,7 @@ serde_json = "1.0.137" smallvec = { version = "1.13.2", features = ["serde"] } thiserror = "2.0.11" unicode-blocks = "0.1.9" +unicode-script = "0.5.7" unicode-width = "0.2.0" levenshtein_automata = { version = "0.2.1", features = ["fst_automaton"] } diff --git a/harper-core/dictionary.dict b/harper-core/dictionary.dict index 18368adf..a1a0eff3 100644 --- a/harper-core/dictionary.dict +++ b/harper-core/dictionary.dict @@ -7819,6 +7819,7 @@ Pinkerton/21M Pinocchio/21M Pinochet/2M Pinter/2M +Pinterest/21M Pinyin/21 Pippin/2M Piraeus/2M @@ -41527,7 +41528,7 @@ shawl/14MS shay/1MS she'd/ she'll/ -she/81DSM~ +she/81DM~ sheaf/14M shear/415MDRSZG shearer/1M @@ -49777,3 +49778,24 @@ intergenerational etc. vs. et al. +Laravel/M +Typst/SM +Lua/SM +CMake/SM +DBMS/12SM +RDBMS/12SM +WhatsApp/124MG +VARCHAR/1MS +DATETIME/1MS +hardcode/4GDS +subquery/14SGD +CTE/1S +unnest/4SGD +ORM/12S +NoSQL/1 +HQL/1 +CQL/1 +KSQL/1 +PRQL/1 +MapReduce/1 +SQLAlchemy/1 diff --git a/harper-core/src/char_ext.rs b/harper-core/src/char_ext.rs index a13be38b..83879f94 100644 --- a/harper-core/src/char_ext.rs +++ b/harper-core/src/char_ext.rs @@ -1,3 +1,4 @@ +use unicode_script::{Script, UnicodeScript}; use unicode_width::UnicodeWidthChar; use crate::Punctuation; @@ -19,6 +20,7 @@ impl CharExt for char { && !self.is_punctuation() && self.is_alphabetic() && !self.is_cjk() + && self.script() == Script::Latin } fn is_emoji(&self) -> bool { diff --git a/harper-core/src/char_string.rs b/harper-core/src/char_string.rs index 899f5bff..fa151adc 100644 --- a/harper-core/src/char_string.rs +++ b/harper-core/src/char_string.rs @@ -4,6 +4,7 @@ use smallvec::SmallVec; /// Most English words are fewer than 12 characters. pub type CharString = SmallVec<[char; 12]>; +/// Extensions to character sequences that make them easier to wrangle. pub trait CharStringExt { fn to_lower(&self) -> CharString; fn to_string(&self) -> String; diff --git a/harper-core/src/document.rs b/harper-core/src/document.rs index fb22f993..009e3992 100644 --- a/harper-core/src/document.rs +++ b/harper-core/src/document.rs @@ -27,6 +27,16 @@ impl Default for Document { } impl Document { + /// Locate all the tokens that intersect a provided span. + /// + /// Desperately needs optimization. + pub fn token_indices_intersecting(&self, span: Span) -> Vec { + self.tokens() + .enumerate() + .filter_map(|(idx, tok)| tok.span.overlaps_with(span).then_some(idx)) + .collect() + } + /// Lexes and parses text to produce a document using a provided language /// parser and dictionary. pub fn new(text: &str, parser: &impl Parser, dictionary: &impl Dictionary) -> Self { diff --git a/harper-core/src/fat_token.rs b/harper-core/src/fat_token.rs index 26db3b52..9f1fbd83 100644 --- a/harper-core/src/fat_token.rs +++ b/harper-core/src/fat_token.rs @@ -4,7 +4,7 @@ use crate::TokenKind; /// A [`Token`](crate::Token) that holds its content as a fat [`Vec`] rather than as a /// [`Span`](crate::Span). -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, PartialOrd)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, PartialOrd, Hash)] pub struct FatToken { pub content: Vec, pub kind: TokenKind, diff --git a/harper-core/src/ignored_lints/lint_context.rs b/harper-core/src/ignored_lints/lint_context.rs new file mode 100644 index 00000000..9487cc4b --- /dev/null +++ b/harper-core/src/ignored_lints/lint_context.rs @@ -0,0 +1,54 @@ +use serde::{Deserialize, Serialize}; + +use crate::{ + linting::{Lint, LintKind, Suggestion}, + Document, FatToken, +}; + +/// A location-agnostic structure that attempts to captures the context and content that a [`Lint`] +/// occurred. +#[derive(Debug, Hash, Serialize, Deserialize)] +pub struct LintContext { + pub lint_kind: LintKind, + pub suggestions: Vec, + pub message: String, + pub priority: u8, + pub tokens: Vec, +} + +impl LintContext { + pub fn from_lint(lint: &Lint, document: &Document) -> Self { + let Lint { + lint_kind, + suggestions, + message, + priority, + .. + } = lint.clone(); + + let problem_tokens = document.token_indices_intersecting(lint.span); + let prequel_tokens = lint + .span + .with_len(2) + .pulled_by(2) + .map(|v| document.token_indices_intersecting(v)) + .unwrap_or_default(); + let sequel_tokens = document.token_indices_intersecting(lint.span.with_len(2).pushed_by(2)); + + let tokens = prequel_tokens + .into_iter() + .chain(problem_tokens) + .chain(sequel_tokens) + .flat_map(|idx| document.get_token(idx)) + .map(|t| t.to_fat(document.get_source())) + .collect(); + + Self { + lint_kind, + suggestions, + message, + priority, + tokens, + } + } +} diff --git a/harper-core/src/ignored_lints/mod.rs b/harper-core/src/ignored_lints/mod.rs new file mode 100644 index 00000000..6846baee --- /dev/null +++ b/harper-core/src/ignored_lints/mod.rs @@ -0,0 +1,139 @@ +mod lint_context; + +use std::hash::{DefaultHasher, Hash, Hasher}; + +use hashbrown::HashSet; +use lint_context::LintContext; +use serde::{Deserialize, Serialize}; + +use crate::{linting::Lint, Document}; + +/// A structure that keeps track of lints that have been ignored by users. +#[derive(Debug, Default, Serialize, Deserialize)] +pub struct IgnoredLints { + context_hashes: HashSet, +} + +impl IgnoredLints { + pub fn new() -> Self { + Self::default() + } + + /// Move entries from another instance to this one. + pub fn append(&mut self, other: Self) { + self.context_hashes.extend(other.context_hashes) + } + + fn hash_lint_context(&self, lint: &Lint, document: &Document) -> u64 { + let context = LintContext::from_lint(lint, document); + + let mut hasher = DefaultHasher::default(); + context.hash(&mut hasher); + + hasher.finish() + } + + /// Add a lint to the list. + pub fn ignore_lint(&mut self, lint: &Lint, document: &Document) { + let context_hash = self.hash_lint_context(lint, document); + + self.context_hashes.insert(context_hash); + } + + pub fn is_ignored(&self, lint: &Lint, document: &Document) -> bool { + let hash = self.hash_lint_context(lint, document); + + self.context_hashes.contains(&hash) + } + + /// Remove ignored Lints from a [`Vec`]. + pub fn remove_ignored(&self, lints: &mut Vec, document: &Document) { + lints.retain(|lint| !self.is_ignored(lint, document)); + } +} + +#[cfg(test)] +mod tests { + use quickcheck::TestResult; + use quickcheck_macros::quickcheck; + + use super::IgnoredLints; + use crate::{ + linting::{LintGroup, LintGroupConfig, Linter}, + Document, FstDictionary, + }; + + #[quickcheck] + fn can_ignore_all(text: String) -> bool { + let document = Document::new_markdown_default_curated(&text); + + let mut lints = + LintGroup::new(LintGroupConfig::default(), FstDictionary::curated()).lint(&document); + + let mut ignored = IgnoredLints::new(); + + for lint in &lints { + ignored.ignore_lint(lint, &document); + } + + ignored.remove_ignored(&mut lints, &document); + lints.is_empty() + } + + #[quickcheck] + fn can_ignore_first(text: String) -> TestResult { + let document = Document::new_markdown_default_curated(&text); + + let mut lints = + LintGroup::new(LintGroupConfig::default(), FstDictionary::curated()).lint(&document); + + let Some(first) = lints.first().cloned() else { + return TestResult::discard(); + }; + + let mut ignored = IgnoredLints::new(); + ignored.ignore_lint(&first, &document); + + ignored.remove_ignored(&mut lints, &document); + + TestResult::from_bool(!lints.contains(&first)) + } + + // Check that ignoring the nth lint found in source text actually removes it (and no others). + fn assert_ignore_lint_reduction(source: &str, nth_lint: usize) { + let document = Document::new_markdown_default_curated(&source); + + let mut lints = + LintGroup::new(LintGroupConfig::default(), FstDictionary::curated()).lint(&document); + + let nth = lints.get(nth_lint).cloned().unwrap_or_else(|| { + panic!("If ignoring the lint at {nth_lint}, make sure there are enough problems.") + }); + + let mut ignored = IgnoredLints::new(); + ignored.ignore_lint(&nth, &document); + + let prev_count = lints.len(); + + ignored.remove_ignored(&mut lints, &document); + + assert_eq!(prev_count, lints.len() + 1); + assert!(!lints.contains(&nth)); + } + + #[test] + fn an_a() { + let source = "There is an problem in this text. Here is an second one."; + + assert_ignore_lint_reduction(source, 0); + assert_ignore_lint_reduction(source, 1); + } + + #[test] + fn spelling() { + let source = "There is a problm in this text. Here is a scond one."; + + assert_ignore_lint_reduction(source, 0); + assert_ignore_lint_reduction(source, 1); + } +} diff --git a/harper-core/src/lib.rs b/harper-core/src/lib.rs index 37801a97..4871c44d 100644 --- a/harper-core/src/lib.rs +++ b/harper-core/src/lib.rs @@ -6,6 +6,7 @@ mod char_string; mod currency; mod document; mod fat_token; +mod ignored_lints; pub mod language_detection; mod lexing; pub mod linting; @@ -28,6 +29,7 @@ pub use char_string::{CharString, CharStringExt}; pub use currency::Currency; pub use document::Document; pub use fat_token::FatToken; +pub use ignored_lints::IgnoredLints; use linting::Lint; pub use mask::{Mask, Masker}; pub use punctuation::{Punctuation, Quote}; diff --git a/harper-core/src/linting/an_a.rs b/harper-core/src/linting/an_a.rs index eda870a6..fb1e3271 100644 --- a/harper-core/src/linting/an_a.rs +++ b/harper-core/src/linting/an_a.rs @@ -33,7 +33,9 @@ impl Linter for AnA { let is_a_an = match chars_first { ['a'] => Some(true), + ['A'] => Some(true), ['a', 'n'] => Some(false), + ['A', 'n'] => Some(false), _ => None, }; @@ -52,7 +54,10 @@ impl Linter for AnA { lints.push(Lint { span: first.span, lint_kind: LintKind::Miscellaneous, - suggestions: vec![Suggestion::ReplaceWith(replacement)], + suggestions: vec![Suggestion::replace_with_match_case( + replacement, + chars_first, + )], message: "Incorrect indefinite article.".to_string(), priority: 31, }) @@ -113,7 +118,8 @@ fn starts_with_vowel(word: &[char]) -> bool { | ['u', 'n', 'i', 'n' | 'm', ..] | ['u', 'n', 'a' | 'u', ..] | ['h', 'e', 'r', 'b', ..] - | ['u', 'r', 'b', ..]) + | ['u', 'r', 'b', ..] + | ['i', 'n', 't', ..]) { return true; } @@ -250,4 +256,13 @@ mod tests { fn disallows_uppercase_consonants() { assert_lint_count("not an Crash", AnA, 1); } + + #[test] + fn disallows_a_interface() { + assert_lint_count( + "A interface for an object that can perform linting actions.", + AnA, + 1, + ); + } } diff --git a/harper-core/src/linting/correct_number_suffix.rs b/harper-core/src/linting/correct_number_suffix.rs index f5f7d5f0..7237b49c 100644 --- a/harper-core/src/linting/correct_number_suffix.rs +++ b/harper-core/src/linting/correct_number_suffix.rs @@ -11,7 +11,9 @@ impl Linter for CorrectNumberSuffix { let mut output = Vec::new(); for number_tok in document.iter_numbers() { - let suffix_span = Span::new_with_len(number_tok.span.end, 2).pulled_by(2); + let Some(suffix_span) = Span::new_with_len(number_tok.span.end, 2).pulled_by(2) else { + continue; + }; if let TokenKind::Number(number, Some(suffix)) = number_tok.kind { if let Some(correct_suffix) = NumberSuffix::correct_suffix_for(number) { diff --git a/harper-core/src/linting/despite_of.rs b/harper-core/src/linting/despite_of.rs new file mode 100644 index 00000000..fb66e8ab --- /dev/null +++ b/harper-core/src/linting/despite_of.rs @@ -0,0 +1,81 @@ +use crate::{ + patterns::{Pattern, SequencePattern}, + Token, TokenStringExt, +}; + +use super::{Lint, LintKind, PatternLinter, Suggestion}; + +pub struct DespiteOf { + pattern: Box, +} + +impl Default for DespiteOf { + fn default() -> Self { + let pattern = SequencePattern::aco("despite") + .then_whitespace() + .then_exact_word("of"); + + Self { + pattern: Box::new(pattern), + } + } +} + +impl PatternLinter for DespiteOf { + fn pattern(&self) -> &dyn Pattern { + self.pattern.as_ref() + } + + fn match_to_lint(&self, matched: &[Token], source: &[char]) -> Lint { + let span = matched.span().unwrap(); + let matched = span.get_content(source); + + Lint { + span, + lint_kind: LintKind::WordChoice, + suggestions: vec![ + Suggestion::replace_with_match_case_str("despite", matched), + Suggestion::replace_with_match_case_str("in spite of", matched) + ], + message: "The phrase “despite of” is incorrect. Please use either “despite” or “in spite of” instead.".to_string(), + priority: 126, + } + } + + fn description(&self) -> &'static str { + "Corrects the misuse of `despite of` and suggests the proper alternatives `despite` or `in spite of`." + } +} + +#[cfg(test)] +mod tests { + use super::DespiteOf; + use crate::linting::tests::{assert_lint_count, assert_suggestion_result}; + + #[test] + fn catches_lowercase() { + assert_suggestion_result( + "The team performed well, despite of the difficulties they faced.", + DespiteOf::default(), + "The team performed well, despite the difficulties they faced.", + ); + } + + #[test] + fn catches_different_cases() { + assert_lint_count( + "Despite of the rain, we went for a walk.", + DespiteOf::default(), + 1, + ); + } + + #[test] + fn likes_correction() { + assert_lint_count( + "The team performed well, despite the difficulties they faced. In spite of the rain, we went for a walk.", + DespiteOf::default(), + 0, + ); + } +} diff --git a/harper-core/src/linting/lint.rs b/harper-core/src/linting/lint.rs index 4759a625..2ff8b0d1 100644 --- a/harper-core/src/linting/lint.rs +++ b/harper-core/src/linting/lint.rs @@ -1,21 +1,49 @@ -use std::fmt::Display; +use std::hash::{DefaultHasher, Hash, Hasher}; -use is_macro::Is; use serde::{Deserialize, Serialize}; use crate::Span; -#[derive(Debug, Clone, Serialize, Deserialize)] +use super::{LintKind, Suggestion}; + +/// An error found in text. +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub struct Lint { + /// The location in the source text the error lies. + /// Important for automatic lint resolution through [`Self::suggestions`]. pub span: Span, + /// The general category the lint belongs to. + /// Mostly used for UI elements in integrations. pub lint_kind: LintKind, + /// A list of zero or more suggested edits that would resolve the underlying problem. + /// See [`Suggestion`]. pub suggestions: Vec, + /// A message to be displayed to the user describing the specific error found. + /// + /// You may use the [`format`] macro to generate more complex messages. pub message: String, /// A numerical value for the importance of a lint. /// Lower = more important. pub priority: u8, } +impl Lint { + /// Creates a SHA-3 hash of all elements of the lint, sans [`Self::span`]. + /// This is useful for comparing lints while ignoring their position within the document. + /// + /// Do not assume that these hash values are stable across Harper versions. + pub fn spanless_hash(&self) -> u64 { + let mut hasher = DefaultHasher::new(); + + self.lint_kind.hash(&mut hasher); + self.suggestions.hash(&mut hasher); + self.message.hash(&mut hasher); + self.priority.hash(&mut hasher); + + hasher.finish() + } +} + impl Default for Lint { fn default() -> Self { Self { @@ -27,144 +55,3 @@ impl Default for Lint { } } } - -#[derive(Debug, Clone, Copy, Serialize, Deserialize, Is, Default)] -pub enum LintKind { - Spelling, - Capitalization, - Style, - Formatting, - Repetition, - Enhancement, - Readability, - WordChoice, - #[default] - Miscellaneous, -} - -impl Display for LintKind { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let s = match self { - LintKind::Spelling => "Spelling", - LintKind::Capitalization => "Capitalization", - LintKind::Formatting => "Formatting", - LintKind::Repetition => "Repetition", - LintKind::Readability => "Readability", - LintKind::Miscellaneous => "Miscellaneous", - LintKind::Enhancement => "Enhancement", - LintKind::WordChoice => "Word Choice", - LintKind::Style => "Style", - }; - - write!(f, "{}", s) - } -} - -#[derive(Debug, Clone, Serialize, Deserialize, Is, PartialEq, Eq)] -pub enum Suggestion { - ReplaceWith(Vec), - /// Insert the provided characters _after_ the offending text. - InsertAfter(Vec), - Remove, -} - -impl Suggestion { - /// Variant of [`Self::replace_with_match_case`] that accepts a static string. - pub fn replace_with_match_case_str(value: &'static str, template: &[char]) -> Self { - Self::replace_with_match_case(value.chars().collect(), template) - } - - /// Construct an instance of [`Self::ReplaceWith`], but make the content match the case of the - /// provided template. - /// - /// For example, if we want to replace "You're" with "You are", we can provide "you are" and - /// "You're". - pub fn replace_with_match_case(mut value: Vec, template: &[char]) -> Self { - for (v, t) in value.iter_mut().zip(template.iter()) { - if v.is_ascii_uppercase() != t.is_ascii_uppercase() { - if t.is_uppercase() { - *v = v.to_ascii_uppercase(); - } else { - *v = v.to_ascii_lowercase(); - } - } - } - - Self::ReplaceWith(value) - } - - /// Apply a suggestion to a given text. - pub fn apply(&self, span: Span, source: &mut Vec) { - match self { - Self::ReplaceWith(chars) => { - // Avoid allocation if possible - if chars.len() == span.len() { - for (index, c) in chars.iter().enumerate() { - source[index + span.start] = *c - } - } else { - let popped = source.split_off(span.start); - - source.extend(chars); - source.extend(popped.into_iter().skip(span.len())); - } - } - Self::Remove => { - for i in span.end..source.len() { - source[i - span.len()] = source[i]; - } - - source.truncate(source.len() - span.len()); - } - Self::InsertAfter(chars) => { - let popped = source.split_off(span.end); - source.extend(chars); - source.extend(popped); - } - } - } -} - -impl Display for Suggestion { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Suggestion::ReplaceWith(with) => { - write!(f, "Replace with: “{}”", with.iter().collect::()) - } - Suggestion::InsertAfter(with) => { - write!(f, "Insert “{}”", with.iter().collect::()) - } - Suggestion::Remove => write!(f, "Remove error"), - } - } -} - -#[cfg(test)] -mod tests { - use crate::Span; - - use super::Suggestion; - - #[test] - fn insert_comma_after() { - let source = "This is a test"; - let mut source_chars = source.chars().collect(); - let sug = Suggestion::InsertAfter(vec![',']); - sug.apply(Span::new(0, 4), &mut source_chars); - - assert_eq!(source_chars, "This, is a test".chars().collect::>()); - } - - #[test] - fn suggestion_your_match_case() { - let template: Vec<_> = "You're".chars().collect(); - let value: Vec<_> = "you are".chars().collect(); - - let correct = "You are".chars().collect(); - - assert_eq!( - Suggestion::replace_with_match_case(value, &template), - Suggestion::ReplaceWith(correct) - ) - } -} diff --git a/harper-core/src/linting/lint_group.rs b/harper-core/src/linting/lint_group.rs index f1e9174d..4f2563c6 100644 --- a/harper-core/src/linting/lint_group.rs +++ b/harper-core/src/linting/lint_group.rs @@ -6,6 +6,7 @@ use super::avoid_curses::AvoidCurses; use super::boring_words::BoringWords; use super::capitalize_personal_pronouns::CapitalizePersonalPronouns; use super::correct_number_suffix::CorrectNumberSuffix; +use super::despite_of::DespiteOf; use super::dot_initialisms::DotInitialisms; use super::ellipsis_length::EllipsisLength; use super::lets_confusion::LetsConfusion; @@ -56,6 +57,7 @@ macro_rules! create_lint_group_config { } } + /// A collection of all officially supported #[derive(Debug, Clone, Copy, Serialize, Deserialize, Default)] pub struct LintGroupConfig { $( @@ -192,6 +194,7 @@ create_lint_group_config!( CurrencyPlacement => true, SomewhatSomething => true, LetsConfusion => true, + DespiteOf => true, ); impl Default for LintGroup { diff --git a/harper-core/src/linting/lint_kind.rs b/harper-core/src/linting/lint_kind.rs new file mode 100644 index 00000000..0f1a70f0 --- /dev/null +++ b/harper-core/src/linting/lint_kind.rs @@ -0,0 +1,39 @@ +use std::fmt::Display; + +use is_macro::Is; +use serde::{Deserialize, Serialize}; + +/// The general category a [`Lint`] falls into. +/// There's no reason not to add a new item here if you are adding a new rule that doesn't fit +/// the existing categories. +#[derive(Debug, Clone, Copy, Serialize, Deserialize, Is, Default, Hash, PartialEq)] +pub enum LintKind { + Spelling, + Capitalization, + Style, + Formatting, + Repetition, + Enhancement, + Readability, + WordChoice, + #[default] + Miscellaneous, +} + +impl Display for LintKind { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let s = match self { + LintKind::Spelling => "Spelling", + LintKind::Capitalization => "Capitalization", + LintKind::Formatting => "Formatting", + LintKind::Repetition => "Repetition", + LintKind::Readability => "Readability", + LintKind::Miscellaneous => "Miscellaneous", + LintKind::Enhancement => "Enhancement", + LintKind::WordChoice => "Word Choice", + LintKind::Style => "Style", + }; + + write!(f, "{}", s) + } +} diff --git a/harper-core/src/linting/mod.rs b/harper-core/src/linting/mod.rs index a68b2aca..48f26dd4 100644 --- a/harper-core/src/linting/mod.rs +++ b/harper-core/src/linting/mod.rs @@ -1,3 +1,7 @@ +//! Frameworks and rules that locate errors in text. +//! +//! See the [`Linter`] trait and the [documentation for authoring a rule](https://writewithharper.com/docs/contributors/author-a-rule) for more information. + mod an_a; mod avoid_curses; mod boring_words; @@ -5,12 +9,14 @@ mod capitalize_personal_pronouns; mod correct_number_suffix; mod currency_placement; mod dashes; +mod despite_of; mod dot_initialisms; mod ellipsis_length; mod lets_confusion; mod linking_verbs; mod lint; mod lint_group; +mod lint_kind; mod long_sentences; mod matcher; mod merge_linters; @@ -28,6 +34,7 @@ mod somewhat_something; mod spaces; mod spell_check; mod spelled_numbers; +mod suggestion; mod terminating_conjunctions; mod that_which; mod unclosed_quotes; @@ -40,12 +47,14 @@ pub use boring_words::BoringWords; pub use capitalize_personal_pronouns::CapitalizePersonalPronouns; pub use correct_number_suffix::CorrectNumberSuffix; pub use currency_placement::CurrencyPlacement; +pub use despite_of::DespiteOf; pub use dot_initialisms::DotInitialisms; pub use ellipsis_length::EllipsisLength; pub use lets_confusion::LetsConfusion; pub use linking_verbs::LinkingVerbs; -pub use lint::{Lint, LintKind, Suggestion}; +pub use lint::Lint; pub use lint_group::{LintGroup, LintGroupConfig}; +pub use lint_kind::LintKind; pub use long_sentences::LongSentences; pub use matcher::Matcher; pub use merge_words::MergeWords; @@ -65,6 +74,7 @@ pub use somewhat_something::SomewhatSomething; pub use spaces::Spaces; pub use spell_check::SpellCheck; pub use spelled_numbers::SpelledNumbers; +pub use suggestion::Suggestion; pub use terminating_conjunctions::TerminatingConjunctions; pub use that_which::ThatWhich; pub use unclosed_quotes::UnclosedQuotes; @@ -73,15 +83,33 @@ pub use wrong_quotes::WrongQuotes; use crate::Document; +/// A __stateless__ rule that searches documents for grammatical errors. +/// +/// Commonly implemented via [`PatternLinter`]. +/// +/// See also: [`LintGroup`]. #[cfg(not(feature = "concurrent"))] pub trait Linter { + /// Analyzes a document and produces zero or more [`Lint`]s. + /// We pass `self` mutably for caching purposes. fn lint(&mut self, document: &Document) -> Vec; + /// A user-facing description of what kinds of grammatical errors this rule looks for. + /// It is usually shown in settings menus. fn description(&self) -> &str; } +/// A __stateless__ rule that searches documents for grammatical errors. +/// +/// Commonly implemented via [`PatternLinter`]. +/// +/// See also: [`LintGroup`]. #[cfg(feature = "concurrent")] pub trait Linter: Send + Sync { + /// Analyzes a document and produces zero or more [`Lint`]s. + /// We pass `self` mutably for caching purposes. fn lint(&mut self, document: &Document) -> Vec; + /// A user-facing description of what kinds of grammatical errors this rule looks for. + /// It is usually shown in settings menus. fn description(&self) -> &str; } diff --git a/harper-core/src/linting/number_suffix_capitalization.rs b/harper-core/src/linting/number_suffix_capitalization.rs index 350b198b..7c46950f 100644 --- a/harper-core/src/linting/number_suffix_capitalization.rs +++ b/harper-core/src/linting/number_suffix_capitalization.rs @@ -15,7 +15,9 @@ impl Linter for NumberSuffixCapitalization { continue; } - let suffix_span = Span::new_with_len(number_tok.span.end, 2).pulled_by(2); + let suffix_span = Span::new_with_len(number_tok.span.end, 2) + .pulled_by(2) + .unwrap(); let chars = document.get_span_content(suffix_span); if chars.iter().any(|c| !c.is_lowercase()) { diff --git a/harper-core/src/linting/pattern_linter.rs b/harper-core/src/linting/pattern_linter.rs index 4fed2072..f66a97fb 100644 --- a/harper-core/src/linting/pattern_linter.rs +++ b/harper-core/src/linting/pattern_linter.rs @@ -2,19 +2,35 @@ use super::{Lint, Linter}; use crate::patterns::Pattern; use crate::{Token, TokenStringExt}; +/// A trait that searches for [`Pattern`]s in [`Document`](crate::Document)s. +/// +/// Makes use of [`TokenStringExt::iter_chunks`] to avoid matching across sentence or clause +/// boundaries. #[cfg(not(feature = "concurrent"))] pub trait PatternLinter { /// A simple getter for the pattern to be searched for. fn pattern(&self) -> &dyn Pattern; + /// If any portions of a [`Document`](crate::Document) match [`Self::pattern`], they are passed through [`PatternLinter::match_to_lint`] to be + /// transformed into a [`Lint`] for editor consumption. fn match_to_lint(&self, matched_tokens: &[Token], source: &[char]) -> Lint; - fn description<'a>(&'a self) -> &'a str; + /// A user-facing description of what kinds of grammatical errors this rule looks for. + /// It is usually shown in settings menus. + fn description(&self) -> &str; } +/// A trait that searches for [`Pattern`]s in [`Document`](crate::Document)s. +/// +/// Makes use of [`TokenStringExt::iter_chunks`] to avoid matching across sentence or clause +/// boundaries. #[cfg(feature = "concurrent")] pub trait PatternLinter: Send + Sync { /// A simple getter for the pattern to be searched for. fn pattern(&self) -> &dyn Pattern; + /// If any portions of a [`Document`](crate::Document) match [`Self::pattern`], they are passed through [`PatternLinter::match_to_lint`] to be + /// transformed into a [`Lint`] for editor consumption. fn match_to_lint(&self, matched_tokens: &[Token], source: &[char]) -> Lint; + /// A user-facing description of what kinds of grammatical errors this rule looks for. + /// It is usually shown in settings menus. fn description(&self) -> &str; } diff --git a/harper-core/src/linting/proper_noun_capitalization_linters.rs b/harper-core/src/linting/proper_noun_capitalization_linters.rs index 0ad9444f..feadeb0e 100644 --- a/harper-core/src/linting/proper_noun_capitalization_linters.rs +++ b/harper-core/src/linting/proper_noun_capitalization_linters.rs @@ -114,25 +114,107 @@ create_linter_for!( SequencePattern::default() .then(Box::new(EitherPattern::new(vec![ Box::new(WordSet::all(&[ - "Presidents'", - "Valentine's", + "Absolution", + "Admission", + "Alaska", + "Anzac", + "ANZAC", + "Arbor", + "Armistice", + "Ascension", + "Australia", + "Ayurveda", + "Bastille", + "Bonifacio", + "Boxing", + "Canada", + "Career", + "Chewidden", "Christmas", + "Class", + "Columbus", + "Commonwealth", + "D", + "Darwin", + "Discovery", + "Distaff", + "Dominion", + "Earth", "Easter", + "Election", + "Emancipation", + "Empire", + "Evolution", + "Family", + "Father's", + "Fathers'", "Flag", + "Forefathers'", + "Foundation", + "Freedom", + "Galentine's", + "Groundhog", + "Gypsy", + "Halloween", "Independence", - "Mothers'", - "Years", - "Fathers'", - "Columbus", - "Thanksgiving", - "Memorial", + "Invasion", + "Ivy", + "Jamhuri", + "Jubilee", + "Kamehameha", + "Kenyatta", + "Labor", + "Labour", + "Lady", + "Land", + "Lei", + "Madaraka", + "Mashujaa", "May", - "Halloween", - "Tax", + "Memorial", + "Merdeka", + "Midsummer", + "Midsummer's", + "Mother's", + "Mothers'", + "Nakba", + "Nevada", + "Occupation", "Parents", + "Patrick's", + "Patriots'", + "Pi", + "Picrous", + "Pioneer", + "Presidents'", + "Remembrance", + "Republic", + "Restoration", + "Rizal", + "Roc", + "Rock", + "Seward's", + "Singles'", + "Statehood", + "Tax", + "Thanksgiving", + "Treason", + "Ulster", + "Valentine's", + "VE", + "VJ", + "VP", "Veterans", - "Armistice", - "Groundhog" + "Victoria", + "Victory", + "Waffle", + "Waitangi", + "Wattle", + "White", + "Wren", + "Years", + "Year's", + "Youth", ])), Box::new( SequencePattern::default() diff --git a/harper-core/src/linting/repeated_words.rs b/harper-core/src/linting/repeated_words.rs index 068e6053..61dd1bc2 100644 --- a/harper-core/src/linting/repeated_words.rs +++ b/harper-core/src/linting/repeated_words.rs @@ -13,12 +13,20 @@ pub struct RepeatedWords { impl RepeatedWords { pub fn new() -> Self { Self { - special_cases: vec![smallvec!['i', 's'], smallvec!['a']], + special_cases: vec![ + smallvec!['i', 's'], + smallvec!['a'], + smallvec!['a', 'n', 'd'], + ], } } fn is_special_case(&self, chars: &[char]) -> bool { - self.special_cases.iter().any(|v| v.as_slice() == chars) + let lower = chars.to_lower(); + + self.special_cases + .iter() + .any(|v| v.as_slice() == lower.as_slice()) } } @@ -113,4 +121,13 @@ mod tests { "This is a test", ); } + + #[test] + fn double_and() { + assert_suggestion_result( + "And and this is also a test", + RepeatedWords::default(), + "And this is also a test", + ); + } } diff --git a/harper-core/src/linting/sentence_capitalization.rs b/harper-core/src/linting/sentence_capitalization.rs index 449cb331..7f441482 100644 --- a/harper-core/src/linting/sentence_capitalization.rs +++ b/harper-core/src/linting/sentence_capitalization.rs @@ -1,6 +1,6 @@ use itertools::Itertools; -use super::lint::Suggestion; +use super::Suggestion; use super::{Lint, LintKind, Linter}; use crate::document::Document; use crate::{Token, TokenKind, TokenStringExt}; diff --git a/harper-core/src/linting/spell_check.rs b/harper-core/src/linting/spell_check.rs index 60c16d58..1a9e2e08 100644 --- a/harper-core/src/linting/spell_check.rs +++ b/harper-core/src/linting/spell_check.rs @@ -1,7 +1,7 @@ use hashbrown::HashMap; use smallvec::ToSmallVec; -use super::lint::Suggestion; +use super::Suggestion; use super::{Lint, LintKind, Linter}; use crate::document::Document; use crate::spell::suggest_correct_spelling; diff --git a/harper-core/src/linting/suggestion.rs b/harper-core/src/linting/suggestion.rs new file mode 100644 index 00000000..8c0bc109 --- /dev/null +++ b/harper-core/src/linting/suggestion.rs @@ -0,0 +1,118 @@ +use std::fmt::Display; + +use is_macro::Is; +use serde::{Deserialize, Serialize}; + +use crate::Span; + +/// A suggested edit that could resolve a [`Lint`]. +#[derive(Debug, Clone, Serialize, Deserialize, Is, PartialEq, Eq, Hash)] +pub enum Suggestion { + /// Replace the offending text with a specific character sequence. + ReplaceWith(Vec), + /// Insert the provided characters _after_ the offending text. + InsertAfter(Vec), + /// Remove the offending text. + Remove, +} + +impl Suggestion { + /// Variant of [`Self::replace_with_match_case`] that accepts a static string. + pub fn replace_with_match_case_str(value: &'static str, template: &[char]) -> Self { + Self::replace_with_match_case(value.chars().collect(), template) + } + + /// Construct an instance of [`Self::ReplaceWith`], but make the content match the case of the + /// provided template. + /// + /// For example, if we want to replace "You're" with "You are", we can provide "you are" and + /// "You're". + pub fn replace_with_match_case(mut value: Vec, template: &[char]) -> Self { + for (v, t) in value.iter_mut().zip(template.iter()) { + if v.is_ascii_uppercase() != t.is_ascii_uppercase() { + if t.is_uppercase() { + *v = v.to_ascii_uppercase(); + } else { + *v = v.to_ascii_lowercase(); + } + } + } + + Self::ReplaceWith(value) + } + + /// Apply a suggestion to a given text. + pub fn apply(&self, span: Span, source: &mut Vec) { + match self { + Self::ReplaceWith(chars) => { + // Avoid allocation if possible + if chars.len() == span.len() { + for (index, c) in chars.iter().enumerate() { + source[index + span.start] = *c + } + } else { + let popped = source.split_off(span.start); + + source.extend(chars); + source.extend(popped.into_iter().skip(span.len())); + } + } + Self::Remove => { + for i in span.end..source.len() { + source[i - span.len()] = source[i]; + } + + source.truncate(source.len() - span.len()); + } + Self::InsertAfter(chars) => { + let popped = source.split_off(span.end); + source.extend(chars); + source.extend(popped); + } + } + } +} + +impl Display for Suggestion { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Suggestion::ReplaceWith(with) => { + write!(f, "Replace with: “{}”", with.iter().collect::()) + } + Suggestion::InsertAfter(with) => { + write!(f, "Insert “{}”", with.iter().collect::()) + } + Suggestion::Remove => write!(f, "Remove error"), + } + } +} + +#[cfg(test)] +mod tests { + use crate::Span; + + use super::Suggestion; + + #[test] + fn insert_comma_after() { + let source = "This is a test"; + let mut source_chars = source.chars().collect(); + let sug = Suggestion::InsertAfter(vec![',']); + sug.apply(Span::new(0, 4), &mut source_chars); + + assert_eq!(source_chars, "This, is a test".chars().collect::>()); + } + + #[test] + fn suggestion_your_match_case() { + let template: Vec<_> = "You're".chars().collect(); + let value: Vec<_> = "you are".chars().collect(); + + let correct = "You are".chars().collect(); + + assert_eq!( + Suggestion::replace_with_match_case(value, &template), + Suggestion::ReplaceWith(correct) + ) + } +} diff --git a/harper-core/src/patterns/is_not_title_case.rs b/harper-core/src/patterns/is_not_title_case.rs index ecad0b8e..5821159c 100644 --- a/harper-core/src/patterns/is_not_title_case.rs +++ b/harper-core/src/patterns/is_not_title_case.rs @@ -2,8 +2,8 @@ use crate::{make_title_case, Dictionary, Token, TokenStringExt}; use super::Pattern; -/// Will match full length of wrapped pattern __only if the matched -/// text is not already title case__. +/// Will match full length of wrapped pattern only if the matched +/// text is not already title case. pub struct IsNotTitleCase { inner: Box, dict: D, diff --git a/harper-core/src/patterns/mod.rs b/harper-core/src/patterns/mod.rs index 92ed0a54..9499b459 100644 --- a/harper-core/src/patterns/mod.rs +++ b/harper-core/src/patterns/mod.rs @@ -1,3 +1,10 @@ +//! [`Pattern`]s are one of the more powerful ways to query text inside Harper, especially for beginners. +//! +//! Through the [`PatternLinter`](crate::linting::PatternLinter) trait, they make it much easier to +//! build Harper [rules](crate::linting::Linter). +//! +//! See the page about [`SequencePattern`] for a concrete example of their use. + use std::collections::VecDeque; use crate::{Document, Span, Token, VecExt}; diff --git a/harper-core/src/patterns/sequence_pattern.rs b/harper-core/src/patterns/sequence_pattern.rs index c49ccd0a..02db7c7a 100644 --- a/harper-core/src/patterns/sequence_pattern.rs +++ b/harper-core/src/patterns/sequence_pattern.rs @@ -6,6 +6,26 @@ use super::{NounPhrase, Pattern, RepeatingPattern, WordSet}; use crate::{CharStringExt, Lrc, Token, TokenKind}; /// A pattern that checks that a sequence of other patterns match. +/// There are specific extension methods available, but you can also use [`Self::then`] to add +/// arbitrary patterns. +/// +/// ## Example +/// +/// Let's say we wanted to locate places in a [`Document`] where an article is followed by a noun. +/// We can do that with a `SequencePattern`. +/// +/// ```rust +/// use harper_core::patterns::{SequencePattern, DocPattern}; +/// use harper_core::{Document, Span}; +/// +/// let document = Document::new_markdown_default_curated("This is a test."); +/// +/// let pattern = SequencePattern::default().then_article().then_whitespace().then_noun(); +/// let matches = pattern.find_all_matches_in_doc(&document); +/// +/// // The pattern found that the tokens at indexes 4, 5, and 6 fit the criteria. +/// assert_eq!(matches, vec![Span::new(4, 7)]); +/// ``` #[derive(Default)] pub struct SequencePattern { token_patterns: Vec>, @@ -60,6 +80,7 @@ impl SequencePattern { gen_then_from_is!(adjective); gen_then_from_is!(apostrophe); gen_then_from_is!(hyphen); + gen_then_from_is!(article); pub fn then_word_set(self, set: WordSet) -> Self { self.then(Box::new(set)) diff --git a/harper-core/src/span.rs b/harper-core/src/span.rs index c12b8a9b..227b243f 100644 --- a/harper-core/src/span.rs +++ b/harper-core/src/span.rs @@ -95,11 +95,15 @@ impl Span { } // Subtract an amount to a copy of both [`Self::start`] and [`Self::end`] - pub fn pulled_by(&self, by: usize) -> Self { + pub fn pulled_by(&self, by: usize) -> Option { + if by > self.start { + return None; + } + let mut clone = *self; clone.start -= by; clone.end -= by; - clone + Some(clone) } // Add an amount a copy of both [`Self::start`] and [`Self::end`] diff --git a/harper-core/src/spell/dictionary.rs b/harper-core/src/spell/dictionary.rs index 7fb4e5c6..d7860dc8 100644 --- a/harper-core/src/spell/dictionary.rs +++ b/harper-core/src/spell/dictionary.rs @@ -3,6 +3,10 @@ use blanket::blanket; use super::FuzzyMatchResult; use crate::WordMetadata; +/// An in-memory database that contains everything necessary to parse and analyze English text. +/// +/// See also: [`FstDictionary`](super::FstDictionary) and +/// [`FullDictionary`](super::FullDictionary). #[blanket(derive(Arc))] pub trait Dictionary: Send + Sync { /// Check if the dictionary contains a given word. diff --git a/harper-core/src/token.rs b/harper-core/src/token.rs index f33c4a2b..83eb246f 100644 --- a/harper-core/src/token.rs +++ b/harper-core/src/token.rs @@ -71,6 +71,7 @@ macro_rules! create_fns_for { }; } +/// Extension methods for [`Token`] sequences that make them easier to wrangle and query. pub trait TokenStringExt { fn first_sentence_word(&self) -> Option; fn first_non_whitespace(&self) -> Option; diff --git a/harper-core/tests/run_tests.rs b/harper-core/tests/run_tests.rs index 09c161bf..d7967706 100644 --- a/harper-core/tests/run_tests.rs +++ b/harper-core/tests/run_tests.rs @@ -50,6 +50,7 @@ create_test!(issue_358.md, 0); create_test!(issue_195.md, 0); create_test!(issue_118.md, 0); create_test!(lots_of_latin.md, 0); +create_test!(pr_504.md, 1); create_test!(pr_452.md, 2); // Make sure it doesn't panic diff --git a/harper-core/tests/test_sources/pr_504.md b/harper-core/tests/test_sources/pr_504.md new file mode 100644 index 00000000..3ff95566 --- /dev/null +++ b/harper-core/tests/test_sources/pr_504.md @@ -0,0 +1,7 @@ +These say "This is in Greek/Georgian/Thai" in those languages: + +Αυτό είναι στα ελληνικά. +ეს ქართულად. +นี่มันภาษาไทย + +This is English with misstakes. diff --git a/harper-html/Cargo.toml b/harper-html/Cargo.toml index 7547af09..fff22413 100644 --- a/harper-html/Cargo.toml +++ b/harper-html/Cargo.toml @@ -1,14 +1,14 @@ [package] name = "harper-html" -version = "0.17.0" +version = "0.18.1" edition = "2021" description = "The language checker for developers." license = "Apache-2.0" repository = "https://github.com/automattic/harper" [dependencies] -harper-core = { path = "../harper-core", version = "0.17.0" } -harper-tree-sitter = { path = "../harper-tree-sitter", version = "0.17.0" } +harper-core = { path = "../harper-core", version = "0.18.0" } +harper-tree-sitter = { path = "../harper-tree-sitter", version = "0.18.0" } tree-sitter-html = "0.19.0" tree-sitter = "0.20.10" diff --git a/harper-html/src/lib.rs b/harper-html/src/lib.rs index f8c7bff8..f4fb6d89 100644 --- a/harper-html/src/lib.rs +++ b/harper-html/src/lib.rs @@ -1,9 +1,10 @@ use harper_core::parsers::{self, Parser, PlainEnglish}; -use harper_core::Token; +use harper_core::{Token, TokenKind}; use harper_tree_sitter::TreeSitterMasker; use tree_sitter::Node; pub struct HtmlParser { + /// Used to grab the text nodes. inner: parsers::Mask, } @@ -26,6 +27,14 @@ impl Default for HtmlParser { impl Parser for HtmlParser { fn parse(&self, source: &[char]) -> Vec { - self.inner.parse(source) + let mut tokens = self.inner.parse(source); + + for token in &mut tokens { + if let TokenKind::Space(v) = &mut token.kind { + *v = (*v).clamp(0, 1); + } + } + + tokens } } diff --git a/harper-html/tests/run_tests.rs b/harper-html/tests/run_tests.rs index dcea7097..f1c23b3d 100644 --- a/harper-html/tests/run_tests.rs +++ b/harper-html/tests/run_tests.rs @@ -38,3 +38,4 @@ macro_rules! create_test { create_test!(run_on.html, 0); create_test!(issue_156.html, 0); +create_test!(issue_541.html, 0); diff --git a/harper-html/tests/test_sources/issue_541.html b/harper-html/tests/test_sources/issue_541.html new file mode 100644 index 00000000..d83fdbbf --- /dev/null +++ b/harper-html/tests/test_sources/issue_541.html @@ -0,0 +1,15 @@ + + + + + + + + +

+ This block contains multiple lines of HTML. If Harper is throwing a "too many spaces" lint, it's + because harper-html isn't properly parsing the indent. +

+ + + diff --git a/harper-literate-haskell/Cargo.toml b/harper-literate-haskell/Cargo.toml index 51622f4e..44d9a5c2 100644 --- a/harper-literate-haskell/Cargo.toml +++ b/harper-literate-haskell/Cargo.toml @@ -1,14 +1,14 @@ [package] name = "harper-literate-haskell" -version = "0.17.0" +version = "0.18.1" edition = "2021" description = "The language checker for developers." license = "Apache-2.0" repository = "https://github.com/automattic/harper" [dependencies] -harper-core = { path = "../harper-core", version = "0.17.0" } -harper-tree-sitter = { path = "../harper-tree-sitter", version = "0.17.0" } -harper-comments = { path = "../harper-comments", version = "0.17.0" } +harper-core = { path = "../harper-core", version = "0.18.0" } +harper-tree-sitter = { path = "../harper-tree-sitter", version = "0.18.0" } +harper-comments = { path = "../harper-comments", version = "0.18.0" } itertools = "0.14.0" paste = "1.0.14" diff --git a/harper-ls/Cargo.toml b/harper-ls/Cargo.toml index 883b554e..490b7ab0 100644 --- a/harper-ls/Cargo.toml +++ b/harper-ls/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "harper-ls" -version = "0.17.0" +version = "0.18.1" edition = "2021" description = "The language checker for developers." license = "Apache-2.0" @@ -8,11 +8,11 @@ readme = "README.md" repository = "https://github.com/automattic/harper" [dependencies] -harper-literate-haskell = { path = "../harper-literate-haskell", version = "0.17.0" } -harper-core = { path = "../harper-core", version = "0.17.0", features = ["concurrent"] } -harper-comments = { path = "../harper-comments", version = "0.17.0" } -harper-typst = { path = "../harper-typst", version = "0.17.0" } -harper-html = { path = "../harper-html", version = "0.17.0" } +harper-literate-haskell = { path = "../harper-literate-haskell", version = "0.18.0" } +harper-core = { path = "../harper-core", version = "0.18.0", features = ["concurrent"] } +harper-comments = { path = "../harper-comments", version = "0.18.0" } +harper-typst = { path = "../harper-typst", version = "0.18.0" } +harper-html = { path = "../harper-html", version = "0.18.0" } tower-lsp = "0.20.0" tokio = { version = "1.43.0", features = ["fs", "rt", "rt-multi-thread", "macros", "io-std", "io-util", "net"] } clap = { version = "4.5.27", features = ["derive"] } diff --git a/harper-ls/src/backend.rs b/harper-ls/src/backend.rs index a98b8351..4342f1e9 100644 --- a/harper-ls/src/backend.rs +++ b/harper-ls/src/backend.rs @@ -1,14 +1,13 @@ use std::collections::HashMap; -use std::path::{Component, PathBuf}; +use std::path::PathBuf; use std::sync::Arc; use anyhow::{anyhow, Context, Result}; use harper_comments::CommentParser; -use harper_core::linting::{LintGroup, Linter}; +use harper_core::linting::LintGroup; use harper_core::parsers::{CollapseIdentifiers, IsolateEnglish, Markdown, Parser, PlainEnglish}; use harper_core::{ - Dictionary, Document, FstDictionary, FullDictionary, MergedDictionary, Token, TokenKind, - WordMetadata, + Dictionary, Document, FstDictionary, FullDictionary, MergedDictionary, WordMetadata, }; use harper_html::HtmlParser; use harper_literate_haskell::LiterateHaskellParser; @@ -19,24 +18,21 @@ use tower_lsp::jsonrpc::Result as JsonResult; use tower_lsp::lsp_types::notification::PublishDiagnostics; use tower_lsp::lsp_types::{ CodeActionOrCommand, CodeActionParams, CodeActionProviderCapability, CodeActionResponse, - Command, ConfigurationItem, Diagnostic, DidChangeConfigurationParams, - DidChangeTextDocumentParams, DidChangeWatchedFilesParams, - DidChangeWatchedFilesRegistrationOptions, DidCloseTextDocumentParams, - DidOpenTextDocumentParams, DidSaveTextDocumentParams, ExecuteCommandOptions, - ExecuteCommandParams, FileChangeType, FileSystemWatcher, GlobPattern, InitializeParams, - InitializeResult, InitializedParams, MessageType, PublishDiagnosticsParams, Range, - Registration, ServerCapabilities, TextDocumentSyncCapability, TextDocumentSyncKind, + ConfigurationItem, Diagnostic, DidChangeConfigurationParams, DidChangeTextDocumentParams, + DidChangeWatchedFilesParams, DidChangeWatchedFilesRegistrationOptions, + DidCloseTextDocumentParams, DidOpenTextDocumentParams, DidSaveTextDocumentParams, + ExecuteCommandOptions, ExecuteCommandParams, FileChangeType, FileSystemWatcher, GlobPattern, + InitializeParams, InitializeResult, InitializedParams, MessageType, PublishDiagnosticsParams, + Range, Registration, ServerCapabilities, TextDocumentSyncCapability, TextDocumentSyncKind, TextDocumentSyncOptions, TextDocumentSyncSaveOptions, Url, WatchKind, }; use tower_lsp::{Client, LanguageServer}; use tracing::{error, info, warn}; use crate::config::Config; -use crate::diagnostics::{lint_to_code_actions, lints_to_diagnostics}; -use crate::dictionary_io::{load_dict, save_dict}; +use crate::dictionary_io::{file_dict_name, load_dict, save_dict}; use crate::document_state::DocumentState; use crate::git_commit_parser::GitCommitParser; -use crate::pos_conv::range_to_span; pub struct Backend { client: Client, @@ -53,33 +49,6 @@ impl Backend { } } - /// Rewrites a path to a filename using the same conventions as - /// [Neovim's undo-files](https://neovim.io/doc/user/options.html#'undodir'). - fn file_dict_name(url: &Url) -> anyhow::Result { - let mut rewritten = String::new(); - - // We assume all URLs are local files and have a base. - for seg in url - .to_file_path() - .map_err(|_| anyhow!("Unable to convert URL to file path."))? - .components() - { - if !matches!(seg, Component::RootDir) { - rewritten.push_str(&seg.as_os_str().to_string_lossy()); - rewritten.push('%'); - } - } - - Ok(rewritten.into()) - } - - /// Get the location of the file's specific dictionary - async fn get_file_dict_path(&self, url: &Url) -> anyhow::Result { - let config = self.config.read().await; - - Ok(config.file_dict_path.join(Self::file_dict_name(url)?)) - } - /// Load a specific file's dictionary async fn load_file_dictionary(&self, url: &Url) -> anyhow::Result { let path = self @@ -93,6 +62,13 @@ impl Backend { .or(Ok(FullDictionary::new())) } + /// Compute the location of the file's specific dictionary + async fn get_file_dict_path(&self, url: &Url) -> anyhow::Result { + let config = self.config.read().await; + + Ok(config.file_dict_path.join(file_dict_name(url)?)) + } + async fn save_file_dictionary(&self, url: &Url, dict: impl Dictionary) -> Result<()> { save_dict( self.get_file_dict_path(url) @@ -176,6 +152,7 @@ impl Backend { linter: LintGroup::new(config_lock.lint_config, dict.clone()), language_id: language_id.map(|v| v.to_string()), dict: dict.clone(), + url: url.clone(), ..Default::default() }); @@ -294,36 +271,7 @@ impl Backend { return Ok(Vec::new()); }; - let mut lints = doc_state.linter.lint(&doc_state.document); - lints.sort_by_key(|l| l.priority); - - let source_chars = doc_state.document.get_full_content(); - - // Find lints whole span overlaps with range - let span = range_to_span(source_chars, range).with_len(1); - - let mut actions: Vec = lints - .into_iter() - .filter(|lint| lint.span.overlaps_with(span)) - .flat_map(|lint| { - lint_to_code_actions(&lint, url, source_chars, &config.code_action_config) - }) - .collect(); - - if let Some(Token { - kind: TokenKind::Url, - span, - .. - }) = doc_state.document.get_token_at_char_index(span.start) - { - actions.push(CodeActionOrCommand::Command(Command::new( - "Open URL".to_string(), - "HarperOpen".to_string(), - Some(vec![doc_state.document.get_span_content_str(span).into()]), - ))) - } - - Ok(actions) + Ok(doc_state.generate_code_actions(range, &config.code_action_config)) } async fn generate_diagnostics(&self, url: &Url) -> Vec { @@ -332,14 +280,9 @@ impl Backend { return Vec::new(); }; - let lints = doc_state.linter.lint(&doc_state.document); let config = self.config.read().await; - lints_to_diagnostics( - doc_state.document.get_full_content(), - &lints, - config.diagnostic_severity, - ) + doc_state.generate_diagnostics(config.diagnostic_severity) } async fn publish_diagnostics(&self, url: &Url) { @@ -393,6 +336,7 @@ impl LanguageServer for Backend { "HarperAddToUserDict".to_owned(), "HarperAddToFileDict".to_owned(), "HarperOpen".to_owned(), + "HarperIgnoreLint".to_owned(), ], ..Default::default() }), @@ -513,8 +457,8 @@ impl LanguageServer for Backend { async fn execute_command(&self, params: ExecuteCommandParams) -> JsonResult> { let mut string_args = params .arguments - .into_iter() - .map(|v| serde_json::from_value::(v).unwrap()); + .iter() + .map(|v| serde_json::from_value::(v.clone()).unwrap()); let Some(first) = string_args.next() else { return Ok(None); @@ -590,6 +534,34 @@ impl LanguageServer for Backend { error!("Unable to open URL: {}", err); } }, + "HarperIgnoreLint" => { + let Ok(url) = Url::parse(&first) else { + error!("Unable to parse URL from command: {first}"); + return Ok(None); + }; + + let Some(second) = params.arguments.into_iter().nth(1) else { + error!("Not enough arguments to HarperIgnoreLint"); + return Ok(None); + }; + + let Ok(lint) = serde_json::from_value(second) else { + error!("Unable to parse lint."); + return Ok(None); + }; + + let mut doc_lock = self.doc_state.lock().await; + let Some(doc_state) = doc_lock.get_mut(&url) else { + error!("Requested document has not been loaded."); + return Ok(None); + }; + + doc_state.ignore_lint(&lint); + + drop(doc_lock); + + self.publish_diagnostics(&url).await; + } _ => (), } diff --git a/harper-ls/src/diagnostics.rs b/harper-ls/src/diagnostics.rs index e30710d4..581a9a42 100644 --- a/harper-ls/src/diagnostics.rs +++ b/harper-ls/src/diagnostics.rs @@ -69,6 +69,15 @@ pub fn lint_to_code_actions<'a>( .map(CodeActionOrCommand::CodeAction), ); + results.push(CodeActionOrCommand::Command(Command { + title: "Ignore Harper error.".to_owned(), + command: "HarperIgnoreLint".to_owned(), + arguments: Some(vec![ + serde_json::Value::String(url.to_string()), + serde_json::to_value(lint).unwrap(), + ]), + })); + if lint.lint_kind.is_spelling() { let orig = lint.span.get_content_string(source); @@ -83,10 +92,10 @@ pub fn lint_to_code_actions<'a>( "HarperAddToFileDict".to_string(), Some(vec![orig.into(), url.to_string().into()]), ))); + } - if config.force_stable { - results.reverse(); - } + if config.force_stable { + results.reverse(); } results diff --git a/harper-ls/src/dictionary_io.rs b/harper-ls/src/dictionary_io.rs index 164e484b..5583cb4b 100644 --- a/harper-ls/src/dictionary_io.rs +++ b/harper-ls/src/dictionary_io.rs @@ -1,8 +1,10 @@ -use std::path::Path; +use anyhow::anyhow; +use std::path::{Component, Path, PathBuf}; use harper_core::{Dictionary, FullDictionary, WordMetadata}; use tokio::fs::{self, File}; use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt, BufReader, BufWriter, Result}; +use tower_lsp::lsp_types::Url; /// Save the contents of a dictionary to a file. /// Ensures that the path to the destination exists. @@ -20,6 +22,7 @@ pub async fn save_dict(path: impl AsRef, dict: impl Dictionary) -> Result< Ok(()) } +/// Write a dictionary somewhere. async fn write_word_list(dict: impl Dictionary, mut w: impl AsyncWrite + Unpin) -> Result<()> { let mut cur_str = String::new(); @@ -34,6 +37,7 @@ async fn write_word_list(dict: impl Dictionary, mut w: impl AsyncWrite + Unpin) Ok(()) } +/// Load a dictionary from a file on disk. pub async fn load_dict(path: impl AsRef) -> Result { let file = File::open(path.as_ref()).await?; let read = BufReader::new(file); @@ -41,7 +45,8 @@ pub async fn load_dict(path: impl AsRef) -> Result { dict_from_word_list(read).await } -/// This function could definitely be optimized to use less memory. +/// Load a dictionary from a list of words. +/// It could definitely be optimized to use less memory. /// Right now it isn't an issue. async fn dict_from_word_list(mut r: impl AsyncRead + Unpin) -> Result { let mut str = String::new(); @@ -56,3 +61,23 @@ async fn dict_from_word_list(mut r: impl AsyncRead + Unpin) -> Result anyhow::Result { + let mut rewritten = String::new(); + + // We assume all URLs are local files and have a base. + for seg in url + .to_file_path() + .map_err(|_| anyhow!("Unable to convert URL to file path."))? + .components() + { + if !matches!(seg, Component::RootDir) { + rewritten.push_str(&seg.as_os_str().to_string_lossy()); + rewritten.push('%'); + } + } + + Ok(rewritten.into()) +} diff --git a/harper-ls/src/document_state.rs b/harper-ls/src/document_state.rs index 11983326..1bab0e0e 100644 --- a/harper-ls/src/document_state.rs +++ b/harper-ls/src/document_state.rs @@ -1,11 +1,86 @@ -use harper_core::linting::LintGroup; -use harper_core::{Document, FullDictionary, Lrc, MergedDictionary}; +use crate::config::{CodeActionConfig, DiagnosticSeverity}; +use crate::diagnostics::{lint_to_code_actions, lints_to_diagnostics}; +use crate::pos_conv::range_to_span; +use harper_core::linting::{Lint, LintGroup, Linter}; +use harper_core::{Document, FullDictionary, IgnoredLints, MergedDictionary, TokenKind}; +use harper_core::{Lrc, Token}; +use tower_lsp::lsp_types::{CodeActionOrCommand, Command, Diagnostic, Range, Url}; -#[derive(Default)] pub struct DocumentState { pub document: Document, pub ident_dict: Lrc, pub dict: Lrc, pub linter: LintGroup>, pub language_id: Option, + pub ignored_lints: IgnoredLints, + pub url: Url, +} + +impl DocumentState { + pub fn ignore_lint(&mut self, lint: &Lint) { + self.ignored_lints.ignore_lint(lint, &self.document); + } + + pub fn generate_diagnostics(&mut self, severity: DiagnosticSeverity) -> Vec { + let mut lints = self.linter.lint(&self.document); + self.ignored_lints + .remove_ignored(&mut lints, &self.document); + + lints_to_diagnostics(self.document.get_full_content(), &lints, severity) + } + + /// Generate code actions results for a selected area. + pub fn generate_code_actions( + &mut self, + range: Range, + code_action_config: &CodeActionConfig, + ) -> Vec { + let mut lints = self.linter.lint(&self.document); + self.ignored_lints + .remove_ignored(&mut lints, &self.document); + + lints.sort_by_key(|l| l.priority); + + let source_chars = self.document.get_full_content(); + + // Find lints whole span overlaps with range + let span = range_to_span(source_chars, range).with_len(1); + + let mut actions: Vec = lints + .into_iter() + .filter(|lint| lint.span.overlaps_with(span)) + .flat_map(|lint| { + lint_to_code_actions(&lint, &self.url, source_chars, code_action_config) + }) + .collect(); + + if let Some(Token { + kind: TokenKind::Url, + span, + .. + }) = self.document.get_token_at_char_index(span.start) + { + actions.push(CodeActionOrCommand::Command(Command::new( + "Open URL".to_string(), + "HarperOpen".to_string(), + Some(vec![self.document.get_span_content_str(span).into()]), + ))) + } + + actions + } +} + +impl Default for DocumentState { + fn default() -> Self { + Self { + document: Default::default(), + ident_dict: Default::default(), + dict: Default::default(), + linter: Default::default(), + language_id: Default::default(), + ignored_lints: Default::default(), + url: Url::parse("https://example.net").unwrap(), + } + } } diff --git a/harper-tree-sitter/Cargo.toml b/harper-tree-sitter/Cargo.toml index 91747ebd..57986a2f 100644 --- a/harper-tree-sitter/Cargo.toml +++ b/harper-tree-sitter/Cargo.toml @@ -1,11 +1,11 @@ [package] name = "harper-tree-sitter" -version = "0.17.0" +version = "0.18.1" edition = "2021" description = "The language checker for developers." license = "Apache-2.0" repository = "https://github.com/automattic/harper" [dependencies] -harper-core = { path = "../harper-core", version = "0.17.0" } +harper-core = { path = "../harper-core", version = "0.18.0" } tree-sitter = "0.20.10" diff --git a/harper-typst/Cargo.toml b/harper-typst/Cargo.toml index 3e9d42b7..5f77c9b5 100644 --- a/harper-typst/Cargo.toml +++ b/harper-typst/Cargo.toml @@ -1,13 +1,13 @@ [package] name = "harper-typst" -version = "0.17.0" +version = "0.18.1" edition = "2021" description = "The language checker for developers." license = "Apache-2.0" repository = "https://github.com/automattic/harper" [dependencies] -harper-core = { path = "../harper-core", version = "0.17.0" } +harper-core = { path = "../harper-core", version = "0.18.0" } typst-syntax = { version = "0.12.0" } ordered-float = { version = "4.6.0", features = ["serde"] } itertools = "0.14.0" diff --git a/harper-wasm/Cargo.toml b/harper-wasm/Cargo.toml index a9b0a459..fcc4a6cb 100644 --- a/harper-wasm/Cargo.toml +++ b/harper-wasm/Cargo.toml @@ -14,7 +14,7 @@ console_error_panic_hook = "0.1.7" tracing = "0.1.41" tracing-wasm = "0.2.1" wasm-bindgen = "0.2.97" -harper-core = { path = "../harper-core", version = "0.17.0", features = ["concurrent"] } +harper-core = { path = "../harper-core", version = "0.18.0", features = ["concurrent"] } once_cell = "1.20.2" serde-wasm-bindgen = "0.6.5" serde_json = "1.0.137" diff --git a/harper-wasm/src/lib.rs b/harper-wasm/src/lib.rs index 8fd811f5..6befc85b 100644 --- a/harper-wasm/src/lib.rs +++ b/harper-wasm/src/lib.rs @@ -6,7 +6,7 @@ use std::sync::Arc; use harper_core::language_detection::is_doc_likely_english; use harper_core::linting::{LintGroup, LintGroupConfig, Linter as _}; use harper_core::parsers::{IsolateEnglish, Markdown, Parser, PlainEnglish}; -use harper_core::{remove_overlaps, Document, FstDictionary, FullDictionary, Lrc}; +use harper_core::{remove_overlaps, Document, FstDictionary, FullDictionary, IgnoredLints, Lrc}; use serde::{Deserialize, Serialize}; use wasm_bindgen::prelude::wasm_bindgen; use wasm_bindgen::JsValue; @@ -43,15 +43,27 @@ make_serialize_fns_for!(Lint); make_serialize_fns_for!(Span); #[wasm_bindgen] +#[derive(Serialize, Deserialize, Debug, Copy, Clone)] pub enum Language { Plain, Markdown, } +impl Language { + fn create_parser(&self) -> Box { + match self { + Language::Plain => Box::new(PlainEnglish), + // TODO: Have a way to configure the Markdown parser + Language::Markdown => Box::new(Markdown::default()), + } + } +} + #[wasm_bindgen] pub struct Linter { lint_group: LintGroup>, dictionary: Arc, + ignored_lints: IgnoredLints, } #[wasm_bindgen] @@ -65,6 +77,7 @@ impl Linter { Self { lint_group: LintGroup::new(LintGroupConfig::default(), dictionary.clone()), dictionary, + ignored_lints: IgnoredLints::default(), } } @@ -117,16 +130,22 @@ impl Linter { Ok(()) } + pub fn ignore_lint(&mut self, lint: Lint) { + let document = Document::new_from_vec( + lint.source.into(), + &lint.language.create_parser(), + &self.dictionary, + ); + + self.ignored_lints.ignore_lint(&lint.inner, &document); + } + /// Perform the configured linting on the provided text. pub fn lint(&mut self, text: String, language: Language) -> Vec { let source: Vec<_> = text.chars().collect(); let source = Lrc::new(source); - let parser: Box = match language { - Language::Plain => Box::new(PlainEnglish), - // TODO: Have a way to configure the Markdown parser - Language::Markdown => Box::new(Markdown::default()), - }; + let parser = language.create_parser(); let document = Document::new_from_vec(source.clone(), &parser, &FullDictionary::curated()); @@ -134,11 +153,31 @@ impl Linter { remove_overlaps(&mut lints); + self.ignored_lints.remove_ignored(&mut lints, &document); + lints .into_iter() - .map(|l| Lint::new(l, source.to_vec())) + .map(|l| Lint::new(l, source.to_vec(), language)) .collect() } + + /// Export the linter's ignored lints as a privacy-respecting JSON list of hashes. + pub fn export_ignored_lints(&self) -> String { + serde_json::to_string(&self.ignored_lints).unwrap() + } + + /// Import into the linter's ignored lints from a privacy-respecting JSON list of hashes. + pub fn import_ignored_lints(&mut self, json: String) -> Result<(), String> { + let list: IgnoredLints = serde_json::from_str(&json).map_err(|err| err.to_string())?; + + self.ignored_lints.append(list); + + Ok(()) + } + + pub fn clear_ignored_lints(&mut self) { + self.ignored_lints = IgnoredLints::new(); + } } impl Default for Linter { @@ -219,12 +258,21 @@ impl Suggestion { pub struct Lint { inner: harper_core::linting::Lint, source: Vec, + language: Language, } #[wasm_bindgen] impl Lint { - pub(crate) fn new(inner: harper_core::linting::Lint, source: Vec) -> Self { - Self { inner, source } + pub(crate) fn new( + inner: harper_core::linting::Lint, + source: Vec, + language: Language, + ) -> Self { + Self { + inner, + source, + language, + } } /// Get the content of the source material pointed to by [`Self::span`] diff --git a/justfile b/justfile index bf093d6c..8b5fb9eb 100644 --- a/justfile +++ b/justfile @@ -14,7 +14,7 @@ build-harperjs: just build-wasm web # Removes a duplicate copy of the WASM binary if Vite is left to its devices. - sed -i 's/new URL(.*)/new URL()/g' "{{justfile_directory()}}/harper-wasm/pkg/harper_wasm.js" + perl -pi -e 's/new URL\(.*\)/new URL()/g' "{{justfile_directory()}}/harper-wasm/pkg/harper_wasm.js" cd "{{justfile_directory()}}/packages/harper.js" yarn install -f @@ -283,7 +283,7 @@ bump-versions: #! /bin/bash set -eo pipefail - cargo ws version --no-git-push --no-git-tag + cargo ws version --no-git-push --no-git-tag --force '*' HARPER_VERSION=$(tq --file harper-core/Cargo.toml .package.version) diff --git a/packages/harper.js/docs.sh b/packages/harper.js/docs.sh index 488b30e5..a8e59982 100755 --- a/packages/harper.js/docs.sh +++ b/packages/harper.js/docs.sh @@ -14,7 +14,7 @@ for file in ./markdown/*.md do BASE=$(basename $file .md) pandoc $file -o html/$BASE.html - sed -i 's/"\(.*\).md"/"\1.html"/g' html/$BASE.html + perl -pi -e 's/"\K([^"]+)\.md(?=")/\1.html/g' html/$BASE.html echo '' >> html/$BASE.html done diff --git a/packages/harper.js/examples/commonjs-simple/index.js b/packages/harper.js/examples/commonjs-simple/index.js index 69bba9e6..d21606e3 100644 --- a/packages/harper.js/examples/commonjs-simple/index.js +++ b/packages/harper.js/examples/commonjs-simple/index.js @@ -1,6 +1,5 @@ -import * as harper from 'harper.js'; - async function main() { + const harper = await import('harper.js'); // We cannot use `WorkerLinter` on Node.js since it relies on web-specific APIs. let linter = new harper.LocalLinter(); diff --git a/packages/harper.js/package.json b/packages/harper.js/package.json index 4c99b924..6ac833b1 100644 --- a/packages/harper.js/package.json +++ b/packages/harper.js/package.json @@ -1,6 +1,6 @@ { "name": "harper.js", - "version": "0.17.0", + "version": "0.18.1", "license": "Apache-2.0", "author": "Elijah Potter", "description": "The grammar checker for developers.", diff --git a/packages/harper.js/src/Linter.test.ts b/packages/harper.js/src/Linter.test.ts index 10cfe916..84fd96bd 100644 --- a/packages/harper.js/src/Linter.test.ts +++ b/packages/harper.js/src/Linter.test.ts @@ -131,6 +131,44 @@ for (const [linterName, Linter] of Object.entries(linters)) { expect(value).not.toBeNull(); } }); + + test(`${linterName} can ignore lints`, async () => { + const linter = new Linter(); + const source = 'This is an test.'; + + const firstRound = await linter.lint(source); + + expect(firstRound.length).toBeGreaterThanOrEqual(1); + + await linter.ignoreLint(firstRound[0]); + + const secondRound = await linter.lint(source); + + expect(secondRound.length).toBeLessThan(firstRound.length); + }); + + test(`${linterName} can reimport ignored lints.`, async () => { + const source = 'This is an test of xporting lints.'; + + const firstLinter = new Linter(); + + const firstLints = await firstLinter.lint(source); + + for (const lint of firstLints) { + await firstLinter.ignoreLint(lint); + } + + const exported = await firstLinter.exportIgnoredLints(); + + /// Create a new instance and reimport the lints. + const secondLinter = new Linter(); + await secondLinter.importIgnoredLints(exported); + + const secondLints = await secondLinter.lint(source); + + expect(firstLints.length).toBeGreaterThan(secondLints.length); + expect(secondLints.length).toBe(0); + }); } test('Linters have the same config format', async () => { diff --git a/packages/harper.js/src/Linter.ts b/packages/harper.js/src/Linter.ts index 4976aa73..90630da7 100644 --- a/packages/harper.js/src/Linter.ts +++ b/packages/harper.js/src/Linter.ts @@ -50,4 +50,17 @@ export default interface Linter { /** Convert a string to Chicago-style title case. */ toTitleCase(text: string): Promise; + + /** Ignore future instances of a lint from a previous linting run in future invocations. */ + ignoreLint(lint: Lint): Promise; + + /** Export the ignored lints to a JSON list of privacy-respecting hashes. */ + exportIgnoredLints(): Promise; + + /** Import ignored lints from a JSON list to the linter. + * This function appends to the existing lints, if any. */ + importIgnoredLints(json: string): Promise; + + /** Clear records of all previously ignored lints. */ + clearIgnoredLints(): Promise; } diff --git a/packages/harper.js/src/LocalLinter.ts b/packages/harper.js/src/LocalLinter.ts index 03196248..1a72abd9 100644 --- a/packages/harper.js/src/LocalLinter.ts +++ b/packages/harper.js/src/LocalLinter.ts @@ -97,4 +97,28 @@ export default class LocalLinter implements Linter { await this.initialize(); return this.inner!.get_lint_descriptions_as_json(); } + + async ignoreLint(lint: Lint): Promise { + await this.initialize(); + + this.inner!.ignore_lint(lint); + } + + async exportIgnoredLints(): Promise { + await this.initialize(); + + return this.inner!.export_ignored_lints(); + } + + async importIgnoredLints(json: string): Promise { + await this.initialize(); + + return this.inner!.import_ignored_lints(json); + } + + async clearIgnoredLints(): Promise { + await this.initialize(); + + return this.inner!.clear_ignored_lints(); + } } diff --git a/packages/harper.js/src/WorkerLinter/index.ts b/packages/harper.js/src/WorkerLinter/index.ts index 675d1644..e2dfdfe1 100644 --- a/packages/harper.js/src/WorkerLinter/index.ts +++ b/packages/harper.js/src/WorkerLinter/index.ts @@ -113,6 +113,22 @@ export default class WorkerLinter implements Linter { return JSON.parse(await this.getDefaultLintConfigAsJSON()) as LintConfig; } + async ignoreLint(lint: Lint): Promise { + return this.rpc('ignoreLint', [lint]); + } + + async exportIgnoredLints(): Promise { + return this.rpc('exportIgnoredLints', []); + } + + async importIgnoredLints(json: string): Promise { + return this.rpc('importIgnoredLints', [json]); + } + + async clearIgnoredLints(): Promise { + return this.rpc('clearIgnoredLints', []); + } + /** Run a procedure on the remote worker. */ private async rpc(procName: string, args: any[]): Promise { const promise = new Promise((resolve, reject) => { diff --git a/packages/obsidian-plugin/src/HarperSettingTab.ts b/packages/obsidian-plugin/src/HarperSettingTab.ts index 482d4f3f..318b6111 100644 --- a/packages/obsidian-plugin/src/HarperSettingTab.ts +++ b/packages/obsidian-plugin/src/HarperSettingTab.ts @@ -56,6 +56,16 @@ export class HarperSettingTab extends PluginSettingTab { }) ); } + + new Setting(containerEl).setName('The Danger Zone').addButton((button) => { + button + .setButtonText('Forget Ignored Suggestions') + .onClick(() => { + this.settings.ignoredLints = undefined; + this.plugin.initializeFromSettings(this.settings); + }) + .setWarning(); + }); } } diff --git a/packages/obsidian-plugin/src/index.ts b/packages/obsidian-plugin/src/index.ts index 3f08de77..de6c1de0 100644 --- a/packages/obsidian-plugin/src/index.ts +++ b/packages/obsidian-plugin/src/index.ts @@ -17,6 +17,7 @@ function suggestionToLabel(sug: Suggestion) { } export type Settings = { + ignoredLints?: string; useWebWorker: boolean; lintSettings: LintConfig; }; @@ -36,10 +37,20 @@ export default class HarperPlugin extends Plugin { settings = { useWebWorker: true, lintSettings: {} }; } - if (settings.useWebWorker) { - this.harper = new WorkerLinter(); + const oldSettings = await this.getSettings(); + + if (settings.useWebWorker != oldSettings.useWebWorker) { + if (settings.useWebWorker) { + this.harper = new WorkerLinter(); + } else { + this.harper = new LocalLinter(); + } } else { - this.harper = new LocalLinter(); + await this.harper.clearIgnoredLints(); + } + + if (settings.ignoredLints !== undefined) { + await this.harper.importIgnoredLints(settings.ignoredLints); } await this.harper.setLintConfig(settings.lintSettings); @@ -54,10 +65,16 @@ export default class HarperPlugin extends Plugin { await this.saveData(settings); } + public async reinitialize() { + const settings = await this.getSettings(); + await this.initializeFromSettings(settings); + } + public async getSettings(): Promise { const usingWebWorker = this.harper instanceof WorkerLinter; return { + ignoredLints: await this.harper.exportIgnoredLints(), useWebWorker: usingWebWorker, lintSettings: await this.harper.getLintConfig() }; @@ -157,6 +174,10 @@ export default class HarperPlugin extends Plugin { severity: 'error', title: lint.lint_kind(), message: lint.message(), + ignore: async () => { + await this.harper.ignoreLint(lint); + await this.reinitialize(); + }, actions: lint.suggestions().map((sug) => { return { name: suggestionToLabel(sug), diff --git a/packages/obsidian-plugin/src/lint.ts b/packages/obsidian-plugin/src/lint.ts index 10d05fc8..a8ea8cad 100644 --- a/packages/obsidian-plugin/src/lint.ts +++ b/packages/obsidian-plugin/src/lint.ts @@ -50,6 +50,8 @@ export interface Diagnostic { /// An optional array of actions that can be taken on this /// diagnostic. actions?: readonly Action[]; + /// A callback for when the user selects to "ignore" the diagnostic. + ignore?: () => void; } /// An action associated with a diagnostic. @@ -420,6 +422,20 @@ function renderDiagnostic(view: EditorView, diagnostic: Diagnostic, inPanel: boo nameElt ); }), + diagnostic.ignore && + elt( + 'div', + { + class: 'cm-diagnosticIgnore', + onclick: (e) => { + e.preventDefault(); + if (diagnostic.ignore) { + diagnostic.ignore(); + } + } + }, + 'Ignore Diagnostic' + ), diagnostic.source && elt('div', { class: 'cm-diagnosticSource' }, diagnostic.source) ); } @@ -500,6 +516,16 @@ const baseTheme = EditorView.baseTheme({ opacity: 0.7 }, + '.cm-diagnosticIgnore': { + color: 'black', + padding: 'var(--size-4-1) 0px', + fontSize: 'var(--font-ui-small)' + }, + + '.cm-diagnosticIgnore:hover': { + textDecoration: 'underline' + }, + '.cm-lintRange': { backgroundPosition: 'left bottom', backgroundRepeat: 'repeat-x', diff --git a/packages/package.json b/packages/package.json index 0159e4fa..44e84a87 100644 --- a/packages/package.json +++ b/packages/package.json @@ -8,7 +8,7 @@ ], "devDependencies": { "eslint": "^8.28.0", - "eslint-config-prettier": "^9.1.0", + "eslint-config-prettier": "^10.0.1", "eslint-plugin-prettier": "^5.2.1", "eslint-plugin-svelte": "^2.46.1", "prettier": "^3.4.2", diff --git a/packages/vscode-plugin/package.json b/packages/vscode-plugin/package.json index e04cde5a..3d636043 100644 --- a/packages/vscode-plugin/package.json +++ b/packages/vscode-plugin/package.json @@ -2,7 +2,7 @@ "name": "harper", "displayName": "Harper", "description": "The grammar checker for developers", - "version": "0.17.0", + "version": "0.18.1", "private": true, "author": "Elijah Potter", "publisher": "elijah-potter", @@ -55,7 +55,9 @@ "onLanguage:toml", "onLanguage:typescript", "onLanguage:typescriptreact", - "onLanguage:typst" + "onLanguage:typst", + "onLanguage:php", + "onLanguage:dart" ], "main": "./build/extension.js", "contributes": { @@ -363,7 +365,7 @@ "@typescript-eslint/parser": "^7.7.1", "@vscode/test-electron": "^2.3.9", "@vscode/vsce": "^3.0.0", - "esbuild": "^0.20.2", + "esbuild": "^0.24.2", "eslint": "^8.57.0", "jasmine": "^5.3.0", "typescript": "^5.4.5" diff --git a/packages/web/package.json b/packages/web/package.json index 0e4c4912..f653a922 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -16,7 +16,7 @@ "@sveltejs/vite-plugin-svelte": "^5.0.3", "@types/reveal.js": "^5.0.3", "autoprefixer": "^10.4.16", - "flowbite": "^1.8.1", + "flowbite": "^3.0.0", "flowbite-svelte": "^0.44.18", "postcss": "^8.4.31", "svelte": "^5.15.0", @@ -26,14 +26,13 @@ "typescript": "^5.0.0", "vite": "^6.0.9", "vite-plugin-pwa": "^0.21.1", - "vite-plugin-top-level-await": "^1.4.1", + "vite-plugin-top-level-await": "^1.4.4", "vite-plugin-wasm": "^3.3.0" }, "type": "module", "dependencies": { - "@sveltepress/theme-default": "^5.0.5", + "@sveltepress/theme-default": "^5.0.7", "@sveltepress/vite": "^1.1.2", - "@zachleat/snow-fall": "^1.0.3", "harper.js": "link:../harper.js", "lodash-es": "^4.17.21", "reveal.js": "^5.1.0", diff --git a/packages/web/src/routes/+layout.svelte b/packages/web/src/routes/+layout.svelte index 28eba86c..4127ef5b 100644 --- a/packages/web/src/routes/+layout.svelte +++ b/packages/web/src/routes/+layout.svelte @@ -2,14 +2,11 @@ import '../app.css'; import GutterCenter from '$lib/GutterCenter.svelte'; import AutomatticLogo from '$lib/AutomatticLogo.svelte'; - import '@zachleat/snow-fall'; let names = ['Grammar Guru', 'Grammar Checker', 'Grammar Savior']; let displayName = names[Math.floor(Math.random() * names.length)]; - - /target/release/harper-ls`. ![How to change the `harper-ls` path](/images/vscode_harper_path.webp) -Now every time you want to test a change, you'll have to recompile `harper-ls` and reload Visual Studio Code using `Developer: Reload Window`. +Every time you want to test a change, you'll have to recompile `harper-ls` and reload Visual Studio Code with the `Developer: Reload Window` command in the command palette. ```bash cargo build --release # Run in the monorepo to compile `harper-ls`. @@ -163,5 +163,5 @@ cargo build --release # Run in the monorepo to compile `harper-ls`. ## Elevate Your Pull Request -Once you're satisfied with your rule, you can go ahead and elevate your pull requests to mark it as "ready for review." +Once you're satisfied with your rule, you can go ahead and elevate your pull request to mark it as "ready for review." At that point, a maintainer on the Harper team take a look at it and (hopefully) merge it. diff --git a/packages/web/src/routes/docs/contributors/dictionary/+page.md b/packages/web/src/routes/docs/contributors/dictionary/+page.md index f265a485..19f4e3a3 100644 --- a/packages/web/src/routes/docs/contributors/dictionary/+page.md +++ b/packages/web/src/routes/docs/contributors/dictionary/+page.md @@ -41,7 +41,7 @@ In `affixes.json`, this expansion rule looks like this: ``` Those familiar with `hunspell` might notice some similarities with their dictionary format. -The main difference are the [metadata fields.](https://docs.rs/harper-core/latest/harper_core/struct.WordMetadata.html) +The main differences are the [metadata fields.](https://docs.rs/harper-core/latest/harper_core/struct.WordMetadata.html) Most words in `dictionary.dict` have simple rules applied to them that result in no expansion, but apply metadata through the `gifts_metadata`. Those particular rules are done automatically. diff --git a/packages/web/src/routes/docs/integrations/language-server/+page.md b/packages/web/src/routes/docs/integrations/language-server/+page.md index 843be18f..4d778390 100644 --- a/packages/web/src/routes/docs/integrations/language-server/+page.md +++ b/packages/web/src/routes/docs/integrations/language-server/+page.md @@ -111,6 +111,8 @@ If you use Neovim, [read this documentation](./neovim#Configuration). | TypeScript | `typescript` | ✅ | | TypeScript React | `typescriptreact` | ✅ | | Typst | `typst` | | +| PHP | `php` | ✅ | +| Dart | `dart` | ✅ | Want your language added? Let us know by [commenting on this issue](https://github.com/Automattic/harper/issues/79). diff --git a/packages/yarn.lock b/packages/yarn.lock index 50b9d1f6..b2ad6575 100644 --- a/packages/yarn.lock +++ b/packages/yarn.lock @@ -1154,11 +1154,6 @@ "@docsearch/css" "3.8.2" algoliasearch "^5.14.2" -"@esbuild/aix-ppc64@0.20.2": - version "0.20.2" - resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz#a70f4ac11c6a1dfc18b8bbb13284155d933b9537" - integrity sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g== - "@esbuild/aix-ppc64@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz#c7184a326533fcdf1b8ee0733e21c713b975575f" @@ -1174,11 +1169,6 @@ resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.24.2.tgz#38848d3e25afe842a7943643cbcd387cc6e13461" integrity sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA== -"@esbuild/android-arm64@0.20.2": - version "0.20.2" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz#db1c9202a5bc92ea04c7b6840f1bbe09ebf9e6b9" - integrity sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg== - "@esbuild/android-arm64@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz#09d9b4357780da9ea3a7dfb833a1f1ff439b4052" @@ -1194,11 +1184,6 @@ resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.24.2.tgz#f592957ae8b5643129fa889c79e69cd8669bb894" integrity sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg== -"@esbuild/android-arm@0.20.2": - version "0.20.2" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.20.2.tgz#3b488c49aee9d491c2c8f98a909b785870d6e995" - integrity sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w== - "@esbuild/android-arm@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.21.5.tgz#9b04384fb771926dfa6d7ad04324ecb2ab9b2e28" @@ -1214,11 +1199,6 @@ resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.24.2.tgz#72d8a2063aa630308af486a7e5cbcd1e134335b3" integrity sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q== -"@esbuild/android-x64@0.20.2": - version "0.20.2" - resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.20.2.tgz#3b1628029e5576249d2b2d766696e50768449f98" - integrity sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg== - "@esbuild/android-x64@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.21.5.tgz#29918ec2db754cedcb6c1b04de8cd6547af6461e" @@ -1234,11 +1214,6 @@ resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.24.2.tgz#9a7713504d5f04792f33be9c197a882b2d88febb" integrity sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw== -"@esbuild/darwin-arm64@0.20.2": - version "0.20.2" - resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz#6e8517a045ddd86ae30c6608c8475ebc0c4000bb" - integrity sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA== - "@esbuild/darwin-arm64@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz#e495b539660e51690f3928af50a76fb0a6ccff2a" @@ -1254,11 +1229,6 @@ resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.24.2.tgz#02ae04ad8ebffd6e2ea096181b3366816b2b5936" integrity sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA== -"@esbuild/darwin-x64@0.20.2": - version "0.20.2" - resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz#90ed098e1f9dd8a9381695b207e1cff45540a0d0" - integrity sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA== - "@esbuild/darwin-x64@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz#c13838fa57372839abdddc91d71542ceea2e1e22" @@ -1274,11 +1244,6 @@ resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.24.2.tgz#9ec312bc29c60e1b6cecadc82bd504d8adaa19e9" integrity sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA== -"@esbuild/freebsd-arm64@0.20.2": - version "0.20.2" - resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz#d71502d1ee89a1130327e890364666c760a2a911" - integrity sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw== - "@esbuild/freebsd-arm64@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz#646b989aa20bf89fd071dd5dbfad69a3542e550e" @@ -1294,11 +1259,6 @@ resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.2.tgz#5e82f44cb4906d6aebf24497d6a068cfc152fa00" integrity sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg== -"@esbuild/freebsd-x64@0.20.2": - version "0.20.2" - resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz#aa5ea58d9c1dd9af688b8b6f63ef0d3d60cea53c" - integrity sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw== - "@esbuild/freebsd-x64@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz#aa615cfc80af954d3458906e38ca22c18cf5c261" @@ -1314,11 +1274,6 @@ resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.24.2.tgz#3fb1ce92f276168b75074b4e51aa0d8141ecce7f" integrity sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q== -"@esbuild/linux-arm64@0.20.2": - version "0.20.2" - resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz#055b63725df678379b0f6db9d0fa85463755b2e5" - integrity sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A== - "@esbuild/linux-arm64@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz#70ac6fa14f5cb7e1f7f887bcffb680ad09922b5b" @@ -1334,11 +1289,6 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.24.2.tgz#856b632d79eb80aec0864381efd29de8fd0b1f43" integrity sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg== -"@esbuild/linux-arm@0.20.2": - version "0.20.2" - resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz#76b3b98cb1f87936fbc37f073efabad49dcd889c" - integrity sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg== - "@esbuild/linux-arm@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz#fc6fd11a8aca56c1f6f3894f2bea0479f8f626b9" @@ -1354,11 +1304,6 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.24.2.tgz#c846b4694dc5a75d1444f52257ccc5659021b736" integrity sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA== -"@esbuild/linux-ia32@0.20.2": - version "0.20.2" - resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz#c0e5e787c285264e5dfc7a79f04b8b4eefdad7fa" - integrity sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig== - "@esbuild/linux-ia32@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz#3271f53b3f93e3d093d518d1649d6d68d346ede2" @@ -1374,11 +1319,6 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.24.2.tgz#f8a16615a78826ccbb6566fab9a9606cfd4a37d5" integrity sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw== -"@esbuild/linux-loong64@0.20.2": - version "0.20.2" - resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz#a6184e62bd7cdc63e0c0448b83801001653219c5" - integrity sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ== - "@esbuild/linux-loong64@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz#ed62e04238c57026aea831c5a130b73c0f9f26df" @@ -1394,11 +1334,6 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.24.2.tgz#1c451538c765bf14913512c76ed8a351e18b09fc" integrity sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ== -"@esbuild/linux-mips64el@0.20.2": - version "0.20.2" - resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz#d08e39ce86f45ef8fc88549d29c62b8acf5649aa" - integrity sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA== - "@esbuild/linux-mips64el@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz#e79b8eb48bf3b106fadec1ac8240fb97b4e64cbe" @@ -1414,11 +1349,6 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.24.2.tgz#0846edeefbc3d8d50645c51869cc64401d9239cb" integrity sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw== -"@esbuild/linux-ppc64@0.20.2": - version "0.20.2" - resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz#8d252f0b7756ffd6d1cbde5ea67ff8fd20437f20" - integrity sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg== - "@esbuild/linux-ppc64@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz#5f2203860a143b9919d383ef7573521fb154c3e4" @@ -1434,11 +1364,6 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.24.2.tgz#8e3fc54505671d193337a36dfd4c1a23b8a41412" integrity sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw== -"@esbuild/linux-riscv64@0.20.2": - version "0.20.2" - resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz#19f6dcdb14409dae607f66ca1181dd4e9db81300" - integrity sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg== - "@esbuild/linux-riscv64@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz#07bcafd99322d5af62f618cb9e6a9b7f4bb825dc" @@ -1454,11 +1379,6 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.24.2.tgz#6a1e92096d5e68f7bb10a0d64bb5b6d1daf9a694" integrity sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q== -"@esbuild/linux-s390x@0.20.2": - version "0.20.2" - resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz#3c830c90f1a5d7dd1473d5595ea4ebb920988685" - integrity sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ== - "@esbuild/linux-s390x@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz#b7ccf686751d6a3e44b8627ababc8be3ef62d8de" @@ -1474,11 +1394,6 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.24.2.tgz#ab18e56e66f7a3c49cb97d337cd0a6fea28a8577" integrity sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw== -"@esbuild/linux-x64@0.20.2": - version "0.20.2" - resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz#86eca35203afc0d9de0694c64ec0ab0a378f6fff" - integrity sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw== - "@esbuild/linux-x64@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz#6d8f0c768e070e64309af8004bb94e68ab2bb3b0" @@ -1499,11 +1414,6 @@ resolved "https://registry.yarnpkg.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.24.2.tgz#65f19161432bafb3981f5f20a7ff45abb2e708e6" integrity sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw== -"@esbuild/netbsd-x64@0.20.2": - version "0.20.2" - resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz#e771c8eb0e0f6e1877ffd4220036b98aed5915e6" - integrity sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ== - "@esbuild/netbsd-x64@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz#bbe430f60d378ecb88decb219c602667387a6047" @@ -1529,11 +1439,6 @@ resolved "https://registry.yarnpkg.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.2.tgz#58b00238dd8f123bfff68d3acc53a6ee369af89f" integrity sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A== -"@esbuild/openbsd-x64@0.20.2": - version "0.20.2" - resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz#9a795ae4b4e37e674f0f4d716f3e226dd7c39baf" - integrity sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ== - "@esbuild/openbsd-x64@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz#99d1cf2937279560d2104821f5ccce220cb2af70" @@ -1549,11 +1454,6 @@ resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.24.2.tgz#0ac843fda0feb85a93e288842936c21a00a8a205" integrity sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA== -"@esbuild/sunos-x64@0.20.2": - version "0.20.2" - resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz#7df23b61a497b8ac189def6e25a95673caedb03f" - integrity sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w== - "@esbuild/sunos-x64@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz#08741512c10d529566baba837b4fe052c8f3487b" @@ -1569,11 +1469,6 @@ resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.24.2.tgz#8b7aa895e07828d36c422a4404cc2ecf27fb15c6" integrity sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig== -"@esbuild/win32-arm64@0.20.2": - version "0.20.2" - resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz#f1ae5abf9ca052ae11c1bc806fb4c0f519bacf90" - integrity sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ== - "@esbuild/win32-arm64@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz#675b7385398411240735016144ab2e99a60fc75d" @@ -1589,11 +1484,6 @@ resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.24.2.tgz#c023afb647cabf0c3ed13f0eddfc4f1d61c66a85" integrity sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ== -"@esbuild/win32-ia32@0.20.2": - version "0.20.2" - resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz#241fe62c34d8e8461cd708277813e1d0ba55ce23" - integrity sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ== - "@esbuild/win32-ia32@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz#1bfc3ce98aa6ca9a0969e4d2af72144c59c1193b" @@ -1609,11 +1499,6 @@ resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.24.2.tgz#96c356132d2dda990098c8b8b951209c3cd743c2" integrity sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA== -"@esbuild/win32-x64@0.20.2": - version "0.20.2" - resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz#9c907b21e30a52db959ba4f80bb01a0cc403d5cc" - integrity sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ== - "@esbuild/win32-x64@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz#acad351d582d157bb145535db2a6ff53dd514b5c" @@ -2316,17 +2201,17 @@ magic-string "^0.30.15" vitefu "^1.0.4" -"@sveltepress/theme-default@^5.0.5": - version "5.0.5" - resolved "https://registry.yarnpkg.com/@sveltepress/theme-default/-/theme-default-5.0.5.tgz#03f9a15709ac312ed1cf692f6fd9fc468e99ce4f" - integrity sha512-vV1a1qDo+PDKI0OR/Mce8QRJ89d3mpF1JXiqdreozRsQ+wHLeKe2HGFK6GwYZ2y7AVgVKXSl9pDyXbHGZDOZDA== +"@sveltepress/theme-default@^5.0.7": + version "5.0.7" + resolved "https://registry.yarnpkg.com/@sveltepress/theme-default/-/theme-default-5.0.7.tgz#5dd10be6f1a39cbded5f6d51303f8b1972ff76d2" + integrity sha512-gbjb2qVrOQ7jfr0owm+I4x7CDZKPW6yCXOrjvMi72RBWULGyHJbmzJq6Alo8RGL47LjIMYtYti+S7TzjjjFhbg== dependencies: uid "^2.0.2" "@docsearch/css" "^3.5.2" "@docsearch/js" "^3.5.2" "@shikijs/twoslash" "^1.24.0" "@sveltejs/vite-plugin-svelte" "^4.0.1" - "@sveltepress/twoslash" "1.1.5" + "@sveltepress/twoslash" "1.1.6" "@unocss/extractor-svelte" "^0.61.5" "@vite-pwa/sveltekit" "^0.6.6" lru-cache "^10.2.0" @@ -2338,10 +2223,10 @@ workbox-precaching "^7.0.0" workbox-routing "^7.0.0" -"@sveltepress/twoslash@1.1.5": - version "1.1.5" - resolved "https://registry.yarnpkg.com/@sveltepress/twoslash/-/twoslash-1.1.5.tgz#6956c00f74ffabd0432aa27d7f2b4a24d99ee7ee" - integrity sha512-9uFJCLAlc/iGndqWjm9livltcrxGHcmZWlkg0fu6Rnzz8SOWzPvIFZknaDirzEyOA5aSORYbgEmMevCaYL5XCw== +"@sveltepress/twoslash@1.1.6": + version "1.1.6" + resolved "https://registry.yarnpkg.com/@sveltepress/twoslash/-/twoslash-1.1.6.tgz#053536c26786b61299ae56473c8ab85e691adc7b" + integrity sha512-ittvzgiC7lZUONK31Go95i4EtfacmdAoF0fnNuYdUzL2sN8MZZKYoYEkTkdUk7h+7TUhKjbGNQjVaRnZHHfwOQ== dependencies: "@floating-ui/dom" "^1.6.3" "@shikijs/twoslash" "^1.24.0" @@ -2380,84 +2265,84 @@ vite-plugin-inspect "^0.8.3" yaml "^2.4.0" -"@swc/core-darwin-arm64@1.5.7": - version "1.5.7" - resolved "https://registry.yarnpkg.com/@swc/core-darwin-arm64/-/core-darwin-arm64-1.5.7.tgz#2b5cdbd34e4162e50de6147dd1a5cb12d23b08e8" - integrity sha512-bZLVHPTpH3h6yhwVl395k0Mtx8v6CGhq5r4KQdAoPbADU974Mauz1b6ViHAJ74O0IVE5vyy7tD3OpkQxL/vMDQ== - -"@swc/core-darwin-x64@1.5.7": - version "1.5.7" - resolved "https://registry.yarnpkg.com/@swc/core-darwin-x64/-/core-darwin-x64-1.5.7.tgz#6aa7e3c01ab8e5e41597f8a24ff24c4e50936a46" - integrity sha512-RpUyu2GsviwTc2qVajPL0l8nf2vKj5wzO3WkLSHAHEJbiUZk83NJrZd1RVbEknIMO7+Uyjh54hEh8R26jSByaw== - -"@swc/core-linux-arm-gnueabihf@1.5.7": - version "1.5.7" - resolved "https://registry.yarnpkg.com/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.5.7.tgz#160108633b9e1d1ad05f815bedc7e9eb5d59fc2a" - integrity sha512-cTZWTnCXLABOuvWiv6nQQM0hP6ZWEkzdgDvztgHI/+u/MvtzJBN5lBQ2lue/9sSFYLMqzqff5EHKlFtrJCA9dQ== - -"@swc/core-linux-arm64-gnu@1.5.7": - version "1.5.7" - resolved "https://registry.yarnpkg.com/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.5.7.tgz#cbfa512683c73227ad25552f3b3e722b0e7fbd1d" - integrity sha512-hoeTJFBiE/IJP30Be7djWF8Q5KVgkbDtjySmvYLg9P94bHg9TJPSQoC72tXx/oXOgXvElDe/GMybru0UxhKx4g== - -"@swc/core-linux-arm64-musl@1.5.7": - version "1.5.7" - resolved "https://registry.yarnpkg.com/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.5.7.tgz#80239cb58fe57f3c86b44617fe784530ec55ee2b" - integrity sha512-+NDhK+IFTiVK1/o7EXdCeF2hEzCiaRSrb9zD7X2Z7inwWlxAntcSuzZW7Y6BRqGQH89KA91qYgwbnjgTQ22PiQ== - -"@swc/core-linux-x64-gnu@1.5.7": - version "1.5.7" - resolved "https://registry.yarnpkg.com/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.5.7.tgz#a699c1632de60b6a63b7fdb7abcb4fef317e57ca" - integrity sha512-25GXpJmeFxKB+7pbY7YQLhWWjkYlR+kHz5I3j9WRl3Lp4v4UD67OGXwPe+DIcHqcouA1fhLhsgHJWtsaNOMBNg== - -"@swc/core-linux-x64-musl@1.5.7": - version "1.5.7" - resolved "https://registry.yarnpkg.com/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.5.7.tgz#8e4c203d6bc41e7f85d7d34d0fdf4ef751fa626c" - integrity sha512-0VN9Y5EAPBESmSPPsCJzplZHV26akC0sIgd3Hc/7S/1GkSMoeuVL+V9vt+F/cCuzr4VidzSkqftdP3qEIsXSpg== - -"@swc/core-win32-arm64-msvc@1.5.7": - version "1.5.7" - resolved "https://registry.yarnpkg.com/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.5.7.tgz#31e3d42b8c0aa79f0ea1a980c0dd1a999d378ed7" - integrity sha512-RtoNnstBwy5VloNCvmvYNApkTmuCe4sNcoYWpmY7C1+bPR+6SOo8im1G6/FpNem8AR5fcZCmXHWQ+EUmRWJyuA== - -"@swc/core-win32-ia32-msvc@1.5.7": - version "1.5.7" - resolved "https://registry.yarnpkg.com/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.5.7.tgz#a235285f9f62850aefcf9abb03420f2c54f63638" - integrity sha512-Xm0TfvcmmspvQg1s4+USL3x8D+YPAfX2JHygvxAnCJ0EHun8cm2zvfNBcsTlnwYb0ybFWXXY129aq1wgFC9TpQ== - -"@swc/core-win32-x64-msvc@1.5.7": - version "1.5.7" - resolved "https://registry.yarnpkg.com/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.5.7.tgz#f84641393b5223450d00d97bfff877b8b69d7c9b" - integrity sha512-tp43WfJLCsKLQKBmjmY/0vv1slVywR5Q4qKjF5OIY8QijaEW7/8VwPyUyVoJZEnDgv9jKtUTG5PzqtIYPZGnyg== - -"@swc/core@^1.3.100": - version "1.5.7" - resolved "https://registry.yarnpkg.com/@swc/core/-/core-1.5.7.tgz#e1db7b9887d5f34eb4a3256a738d0c5f1b018c33" - integrity sha512-U4qJRBefIJNJDRCCiVtkfa/hpiZ7w0R6kASea+/KLp+vkus3zcLSB8Ub8SvKgTIxjWpwsKcZlPf5nrv4ls46SQ== - dependencies: - "@swc/counter" "^0.1.2" - "@swc/types" "0.1.7" +"@swc/core-darwin-arm64@1.10.11": + version "1.10.11" + resolved "https://registry.yarnpkg.com/@swc/core-darwin-arm64/-/core-darwin-arm64-1.10.11.tgz#37e79f35f1d0226465d4b5fe8e1c957e35661bd5" + integrity sha512-ZpgEaNcx2e5D+Pd0yZGVbpSrEDOEubn7r2JXoNBf0O85lPjUm3HDzGRfLlV/MwxRPAkwm93eLP4l7gYnc50l3g== + +"@swc/core-darwin-x64@1.10.11": + version "1.10.11" + resolved "https://registry.yarnpkg.com/@swc/core-darwin-x64/-/core-darwin-x64-1.10.11.tgz#da0354121088d73808634efb213a264400587c8f" + integrity sha512-szObinnq2o7spXMDU5pdunmUeLrfV67Q77rV+DyojAiGJI1RSbEQotLOk+ONOLpoapwGUxOijFG4IuX1xiwQ2g== + +"@swc/core-linux-arm-gnueabihf@1.10.11": + version "1.10.11" + resolved "https://registry.yarnpkg.com/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.10.11.tgz#4357c4f8fd1a4d53020c2e5caf048ff5ca134eec" + integrity sha512-tVE8aXQwd8JUB9fOGLawFJa76nrpvp3dvErjozMmWSKWqtoeO7HV83aOrVtc8G66cj4Vq7FjTE9pOJeV1FbKRw== + +"@swc/core-linux-arm64-gnu@1.10.11": + version "1.10.11" + resolved "https://registry.yarnpkg.com/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.10.11.tgz#2b92ddb2500c1292a999fc617044bed8318ac0dc" + integrity sha512-geFkENU5GMEKO7FqHOaw9HVlpQEW10nICoM6ubFc0hXBv8dwRXU4vQbh9s/isLSFRftw1m4jEEWixAnXSw8bxQ== + +"@swc/core-linux-arm64-musl@1.10.11": + version "1.10.11" + resolved "https://registry.yarnpkg.com/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.10.11.tgz#8545fd335bd5234522bc23a8a11582ff549e26eb" + integrity sha512-2mMscXe/ivq8c4tO3eQSbQDFBvagMJGlalXCspn0DgDImLYTEnt/8KHMUMGVfh0gMJTZ9q4FlGLo7mlnbx99MQ== + +"@swc/core-linux-x64-gnu@1.10.11": + version "1.10.11" + resolved "https://registry.yarnpkg.com/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.10.11.tgz#79e94f3d59e29016c8294d401d8b35bc13d6586a" + integrity sha512-eu2apgDbC4xwsigpl6LS+iyw6a3mL6kB4I+6PZMbFF2nIb1Dh7RGnu70Ai6mMn1o80fTmRSKsCT3CKMfVdeNFg== + +"@swc/core-linux-x64-musl@1.10.11": + version "1.10.11" + resolved "https://registry.yarnpkg.com/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.10.11.tgz#edceb8cf38ad00d82db56349b919d4482bc5ba96" + integrity sha512-0n+wPWpDigwqRay4IL2JIvAqSKCXv6nKxPig9M7+epAlEQlqX+8Oq/Ap3yHtuhjNPb7HmnqNJLCXT1Wx+BZo0w== + +"@swc/core-win32-arm64-msvc@1.10.11": + version "1.10.11" + resolved "https://registry.yarnpkg.com/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.10.11.tgz#000e82ccd079a7eab382b9124b216e2310d7d013" + integrity sha512-7+bMSIoqcbXKosIVd314YjckDRPneA4OpG1cb3/GrkQTEDXmWT3pFBBlJf82hzJfw7b6lfv6rDVEFBX7/PJoLA== + +"@swc/core-win32-ia32-msvc@1.10.11": + version "1.10.11" + resolved "https://registry.yarnpkg.com/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.10.11.tgz#6138bdf97a221d2194f4181bf87255063520210d" + integrity sha512-6hkLl4+3KjP/OFTryWxpW7YFN+w4R689TSPwiII4fFgsFNupyEmLWWakKfkGgV2JVA59L4Oi02elHy/O1sbgtw== + +"@swc/core-win32-x64-msvc@1.10.11": + version "1.10.11" + resolved "https://registry.yarnpkg.com/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.10.11.tgz#d1571d46a5c0efe17ef12898bc605e301d266b2d" + integrity sha512-kKNE2BGu/La2k2WFHovenqZvGQAHRIU+rd2/6a7D6EiQ6EyimtbhUqjCCZ+N1f5fIAnvM+sMdLiQJq4jdd/oOQ== + +"@swc/core@^1.7.0": + version "1.10.11" + resolved "https://registry.yarnpkg.com/@swc/core/-/core-1.10.11.tgz#cbc72a940b127dd6313b5c6c5ab67a746cd39751" + integrity sha512-3zGU5y3S20cAwot9ZcsxVFNsSVaptG+dKdmAxORSE3EX7ixe1Xn5kUwLlgIsM4qrwTUWCJDLNhRS+2HLFivcDg== + dependencies: + "@swc/counter" "^0.1.3" + "@swc/types" "^0.1.17" optionalDependencies: - "@swc/core-darwin-arm64" "1.5.7" - "@swc/core-darwin-x64" "1.5.7" - "@swc/core-linux-arm-gnueabihf" "1.5.7" - "@swc/core-linux-arm64-gnu" "1.5.7" - "@swc/core-linux-arm64-musl" "1.5.7" - "@swc/core-linux-x64-gnu" "1.5.7" - "@swc/core-linux-x64-musl" "1.5.7" - "@swc/core-win32-arm64-msvc" "1.5.7" - "@swc/core-win32-ia32-msvc" "1.5.7" - "@swc/core-win32-x64-msvc" "1.5.7" - -"@swc/counter@^0.1.2", "@swc/counter@^0.1.3": + "@swc/core-darwin-arm64" "1.10.11" + "@swc/core-darwin-x64" "1.10.11" + "@swc/core-linux-arm-gnueabihf" "1.10.11" + "@swc/core-linux-arm64-gnu" "1.10.11" + "@swc/core-linux-arm64-musl" "1.10.11" + "@swc/core-linux-x64-gnu" "1.10.11" + "@swc/core-linux-x64-musl" "1.10.11" + "@swc/core-win32-arm64-msvc" "1.10.11" + "@swc/core-win32-ia32-msvc" "1.10.11" + "@swc/core-win32-x64-msvc" "1.10.11" + +"@swc/counter@^0.1.3": version "0.1.3" resolved "https://registry.yarnpkg.com/@swc/counter/-/counter-0.1.3.tgz#cc7463bd02949611c6329596fccd2b0ec782b0e9" integrity sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ== -"@swc/types@0.1.7": - version "0.1.7" - resolved "https://registry.yarnpkg.com/@swc/types/-/types-0.1.7.tgz#ea5d658cf460abff51507ca8d26e2d391bafb15e" - integrity sha512-scHWahbHF0eyj3JsxG9CFJgFdFNaVQCNAimBlT6PzS3n/HptxqREjsm4OH6AN3lYcffZYSPxXW8ua2BEHp0lJQ== +"@swc/types@^0.1.17": + version "0.1.17" + resolved "https://registry.yarnpkg.com/@swc/types/-/types-0.1.17.tgz#bd1d94e73497f27341bf141abdf4c85230d41e7c" + integrity sha512-V5gRru+aD8YVyCOMAjMpWR1Ui577DD5KSJsHP8RAxopAH22jFz6GZd/qxqjO6MJHQhcsjvjOFXyDhyLQUnMveQ== dependencies: "@swc/counter" "^0.1.3" @@ -3255,11 +3140,6 @@ resolved "https://registry.yarnpkg.com/@yr/monotone-cubic-spline/-/monotone-cubic-spline-1.0.3.tgz#7272d89f8e4f6fb7a1600c28c378cc18d3b577b9" integrity sha512-FQXkOta0XBSUPHndIKON2Y9JeQz5ZeMqLYZVVK93FliNBFm7LNMIZmY6FrMEB9XPcDbE2bekMbZD6kzDkxwYjA== -"@zachleat/snow-fall@^1.0.3": - version "1.0.3" - resolved "https://registry.yarnpkg.com/@zachleat/snow-fall/-/snow-fall-1.0.3.tgz#0ce77508e535a976a30c64dffce47173a382764b" - integrity sha512-Y9srRbmO+k31vSm+eINYRV9DRoeWGV5/hlAn9o34bLpoWo+T5945v6XGBrFzQYjhyEGB4j/4zXuTW1zTxp2Reg== - acorn-jsx@^5.3.2: version "5.3.2" resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" @@ -4516,35 +4396,6 @@ es-to-primitive@^1.3.0: is-date-object "^1.0.5" is-symbol "^1.0.4" -esbuild@^0.20.2: - version "0.20.2" - resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.20.2.tgz#9d6b2386561766ee6b5a55196c6d766d28c87ea1" - integrity sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g== - optionalDependencies: - "@esbuild/aix-ppc64" "0.20.2" - "@esbuild/android-arm" "0.20.2" - "@esbuild/android-arm64" "0.20.2" - "@esbuild/android-x64" "0.20.2" - "@esbuild/darwin-arm64" "0.20.2" - "@esbuild/darwin-x64" "0.20.2" - "@esbuild/freebsd-arm64" "0.20.2" - "@esbuild/freebsd-x64" "0.20.2" - "@esbuild/linux-arm" "0.20.2" - "@esbuild/linux-arm64" "0.20.2" - "@esbuild/linux-ia32" "0.20.2" - "@esbuild/linux-loong64" "0.20.2" - "@esbuild/linux-mips64el" "0.20.2" - "@esbuild/linux-ppc64" "0.20.2" - "@esbuild/linux-riscv64" "0.20.2" - "@esbuild/linux-s390x" "0.20.2" - "@esbuild/linux-x64" "0.20.2" - "@esbuild/netbsd-x64" "0.20.2" - "@esbuild/openbsd-x64" "0.20.2" - "@esbuild/sunos-x64" "0.20.2" - "@esbuild/win32-arm64" "0.20.2" - "@esbuild/win32-ia32" "0.20.2" - "@esbuild/win32-x64" "0.20.2" - "esbuild@^0.20.2 || ^0.21.0 || ^0.22.0 || ^0.23.0", esbuild@~0.23.0: version "0.23.1" resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.23.1.tgz#40fdc3f9265ec0beae6f59824ade1bd3d3d2dab8" @@ -4667,10 +4518,10 @@ eslint-compat-utils@^0.5.1: dependencies: semver "^7.5.4" -eslint-config-prettier@^9.1.0: - version "9.1.0" - resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz#31af3d94578645966c082fcb71a5846d3c94867f" - integrity sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw== +eslint-config-prettier@^10.0.1: + version "10.0.1" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-10.0.1.tgz#fbb03bfc8db0651df9ce4e8b7150d11c5fe3addf" + integrity sha512-lZBts941cyJyeaooiKxAtzoPHTN+GbQTJFAIdQbRhA4/8whaAraEh47Whw/ZFfrjNSnlAxqfm9i0XVAEkULjCw== eslint-plugin-prettier@^5.2.1: version "5.2.1" @@ -4951,6 +4802,14 @@ flatted@^3.2.9: resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.1.tgz#21db470729a6734d4997002f439cb308987f567a" integrity sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw== +flowbite-datepicker@^1.3.0, flowbite-datepicker@^1.3.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/flowbite-datepicker/-/flowbite-datepicker-1.3.2.tgz#ad830d73f923344fb5614978f0d87e790cc69c4b" + integrity sha512-6Nfm0MCVX3mpaR7YSCjmEO2GO8CDt6CX8ZpQnGdeu03WUCWtEPQ/uy0PUiNtIJjJZWnX0Cm3H55MOhbD1g+E/g== + dependencies: + "@rollup/plugin-node-resolve" "^15.2.3" + flowbite "^2.0.0" + flowbite-svelte@^0.44.18: version "0.44.24" resolved "https://registry.yarnpkg.com/flowbite-svelte/-/flowbite-svelte-0.44.24.tgz#c7b74e3c40742bcd15b164621f1934e5fb7c509b" @@ -4961,20 +4820,22 @@ flowbite-svelte@^0.44.18: flowbite "^2.3.0" tailwind-merge "^2.2.1" -flowbite@^1.8.1: - version "1.8.1" - resolved "https://registry.yarnpkg.com/flowbite/-/flowbite-1.8.1.tgz#a1f5fb039c4c275414a457089b4917a67e9153a5" - integrity sha512-lXTcO8a6dRTPFpINyOLcATCN/pK1Of/jY4PryklPllAiqH64tSDUsOdQpar3TO59ZXWwugm2e92oaqwH6X90Xg== +flowbite@^2.0.0, flowbite@^2.3.0: + version "2.5.2" + resolved "https://registry.yarnpkg.com/flowbite/-/flowbite-2.5.2.tgz#4a14b87ad3f2abd8bcd7b0fb52a6b06fd7a74685" + integrity sha512-kwFD3n8/YW4EG8GlY3Od9IoKND97kitO+/ejISHSqpn3vw2i5K/+ZI8Jm2V+KC4fGdnfi0XZ+TzYqQb4Q1LshA== dependencies: "@popperjs/core" "^2.9.3" + flowbite-datepicker "^1.3.0" mini-svg-data-uri "^1.4.3" -flowbite@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/flowbite/-/flowbite-2.3.0.tgz#0730e35d8b0d1dcdea26bb27d848bd9c0141cde1" - integrity sha512-pm3JRo8OIJHGfFYWgaGpPv8E+UdWy0Z3gEAGufw+G/1dusaU/P1zoBLiQpf2/+bYAi+GBQtPVG86KYlV0W+AFQ== +flowbite@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/flowbite/-/flowbite-3.0.0.tgz#943bfb6724e6c8a495cacd2c55723e1b65da8956" + integrity sha512-5FyZOzsD6iER+iEm+NnqtzTdBVXOjG2HhuuAjOWmqTlz4GK/tVkXHJoY41lnHBMSHlFPM4aPcppnXrzPYL3vYA== dependencies: "@popperjs/core" "^2.9.3" + flowbite-datepicker "^1.3.1" mini-svg-data-uri "^1.4.3" for-each@^0.3.3: @@ -5298,7 +5159,7 @@ gzip-size@^6.0.0: duplexer "^0.1.2" "harper.js@link:harper.js": - version "0.16.0" + version "0.18.0" has-bigints@^1.0.2: version "1.1.0" @@ -9178,16 +9039,16 @@ util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== +uuid@^10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-10.0.0.tgz#5a95aa454e6e002725c79055fd42aaba30ca6294" + integrity sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ== + uuid@^8.3.0: version "8.3.2" resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== -uuid@^9.0.1: - version "9.0.1" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30" - integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA== - vfile-message@^4.0.0: version "4.0.2" resolved "https://registry.yarnpkg.com/vfile-message/-/vfile-message-4.0.2.tgz#c883c9f677c72c166362fd635f21fc165a7d1181" @@ -9256,14 +9117,14 @@ vite-plugin-pwa@^0.21.1: workbox-build "^7.3.0" workbox-window "^7.3.0" -vite-plugin-top-level-await@^1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/vite-plugin-top-level-await/-/vite-plugin-top-level-await-1.4.1.tgz#607dfe084157550fa33df18062b99ceea774cd9c" - integrity sha512-hogbZ6yT7+AqBaV6lK9JRNvJDn4/IJvHLu6ET06arNfo0t2IsyCaon7el9Xa8OumH+ESuq//SDf8xscZFE0rWw== +vite-plugin-top-level-await@^1.4.4: + version "1.4.4" + resolved "https://registry.yarnpkg.com/vite-plugin-top-level-await/-/vite-plugin-top-level-await-1.4.4.tgz#4900e06bfb7179de20aaa9b4730d04022a9e259e" + integrity sha512-QyxQbvcMkgt+kDb12m2P8Ed35Sp6nXP+l8ptGrnHV9zgYDUpraO0CPdlqLSeBqvY2DToR52nutDG7mIHuysdiw== dependencies: "@rollup/plugin-virtual" "^3.0.2" - "@swc/core" "^1.3.100" - uuid "^9.0.1" + "@swc/core" "^1.7.0" + uuid "^10.0.0" vite-plugin-virtual@^0.3.0: version "0.3.0"