├── .github └── workflows │ └── release.yaml ├── .gitignore ├── CHANGELOG.md ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md └── src ├── crates ├── api.rs ├── cache.rs ├── mod.rs └── sparse.rs ├── main.rs ├── parse.rs └── settings.rs /.github/workflows/release.yaml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - main 10 | 11 | jobs: 12 | lint: 13 | name: Linting (rustfmt + clippy) 14 | permissions: 15 | checks: write 16 | runs-on: ubuntu-latest 17 | steps: 18 | - name: Checkout repository 19 | uses: actions/checkout@v2 20 | 21 | - name: Install stable toolchain 22 | uses: actions-rs/toolchain@v1 23 | with: 24 | profile: minimal 25 | toolchain: stable 26 | override: true 27 | 28 | - name: Install rustup components (rustfmt, clippy) 29 | run: rustup component add rustfmt clippy 30 | 31 | - name: Run cargo fmt 32 | uses: actions-rs/cargo@v1 33 | with: 34 | command: fmt 35 | args: --all -- --check 36 | 37 | - uses: giraffate/clippy-action@v1 38 | with: 39 | reporter: "github-pr-review" 40 | github_token: ${{ secrets.GITHUB_TOKEN }} 41 | 42 | test: 43 | name: Test 44 | runs-on: ubuntu-latest 45 | steps: 46 | - name: Checkout main branch 47 | uses: actions/checkout@v2 48 | 49 | - name: Install stable toolchain 50 | uses: actions-rs/toolchain@v1 51 | with: 52 | profile: minimal 53 | toolchain: stable 54 | override: true 55 | 56 | - name: Run all tests 57 | uses: actions-rs/cargo@v1 58 | with: 59 | command: test 60 | args: --all-features 61 | 62 | tag: 63 | name: Tag & Publish 64 | needs: [lint, test] 65 | permissions: 66 | contents: write 67 | if: github.event_name == 'push' && github.ref == 'refs/heads/main' 68 | runs-on: ubuntu-latest 69 | outputs: 70 | tag: ${{ steps.crate-version.outputs.version }} 71 | steps: 72 | - name: Checkout repository 73 | uses: actions/checkout@v2 74 | 75 | - name: Crate Version 76 | id: crate-version 77 | uses: colathro/crate-version@1.0.0 78 | with: 79 | file: "./Cargo.toml" 80 | 81 | - uses: mukunku/tag-exists-action@v1.6.0 82 | id: check-tag 83 | with: 84 | tag: v${{ steps.crate-version.outputs.version }} 85 | 86 | - name: Fail if tag already exists 87 | if: ${{ steps.check-tag.outputs.exists == 'true' }} 88 | uses: actions/github-script@v3 89 | with: 90 | script: | 91 | core.setFailed('Tag already exists') 92 | 93 | - name: Publish crate to crates.io 94 | uses: katyo/publish-crates@v2 95 | with: 96 | registry-token: ${{ secrets.CARGO_LOGIN_TOKEN }} 97 | ignore-unpublished-changes: true 98 | 99 | - name: Push the crate version as a tag 100 | id: tag_version 101 | uses: mathieudutour/github-tag-action@v5.4 102 | with: 103 | github_token: ${{ secrets.GITHUB_TOKEN }} 104 | tag_prefix: "v" 105 | custom_tag: ${{ steps.crate-version.outputs.version }} 106 | 107 | create-release: 108 | needs: tag 109 | permissions: 110 | contents: write 111 | runs-on: ubuntu-latest 112 | steps: 113 | - uses: actions/checkout@v3 114 | - uses: taiki-e/create-gh-release-action@v1 115 | with: 116 | changelog: "./CHANGELOG.md" 117 | token: ${{ secrets.GITHUB_TOKEN }} 118 | ref: "refs/tags/v${{needs.tag.outputs.tag}}" 119 | 120 | upload-assets: 121 | needs: [tag, create-release] 122 | permissions: 123 | contents: write 124 | strategy: 125 | matrix: 126 | include: 127 | - target: aarch64-unknown-linux-gnu 128 | os: ubuntu-latest 129 | - target: aarch64-apple-darwin 130 | os: macos-latest 131 | - target: x86_64-unknown-linux-gnu 132 | os: ubuntu-latest 133 | - target: x86_64-apple-darwin 134 | os: macos-latest 135 | - target: universal-apple-darwin 136 | os: macos-latest 137 | - target: x86_64-pc-windows-msvc 138 | os: windows-latest 139 | runs-on: ${{ matrix.os }} 140 | steps: 141 | - uses: actions/checkout@v3 142 | - uses: taiki-e/upload-rust-binary-action@v1 143 | with: 144 | bin: crates-lsp 145 | target: ${{ matrix.target }} 146 | token: ${{ secrets.GITHUB_TOKEN }} 147 | ref: "refs/tags/v${{needs.tag.outputs.tag}}" 148 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | ## 0.1.7 4 | 5 | ### Fixed 6 | 7 | * Updated tokio dependency. Resolves a vulnerability warning, which does not affect crates-lsp. 8 | 9 | ## 0.1.6 10 | 11 | ### Fixed 12 | 13 | * Updated ring dependency in response to vulnerability. 14 | * Renamed crates cache directory from .lapce to .crates-lsp 15 | 16 | ## 0.1.5 17 | 18 | ### Added 19 | 20 | * Added crates.io build pipeline (#14) 21 | 22 | ## 0.1.4 23 | 24 | ### Fixed 25 | 26 | * Replaced OpenSSL dependency with rustls/webpki 27 | * Updated dependencies 28 | 29 | ## 0.1.3 30 | 31 | ### Added 32 | 33 | * Add *Code Action* for updating version by [@Vulpesx](https://github.com/MathiasPius/crates-lsp/pull/9) 34 | * Add *Inlay Hints* by [@Vulpesx](https://github.com/MathiasPius/crates-lsp/pull/10) 35 | 36 | ## 0.1.2 37 | 38 | ### Added 39 | 40 | * Implement diagnostic levels by [@Vulpesx](https://github.com/MathiasPius/crates-lsp/pull/8) 41 | 42 | ## 0.1.1 43 | 44 | ### Fixed 45 | 46 | * Updated hyper-rustls from 0.26.0 to 0.27.0 47 | 48 | ## 0.1.0 49 | 50 | ### Fixed 51 | * Update to hyper 0.x to 1.2.0 52 | * Update mio dependency to resolve [CVE-2024-27308](https://github.com/advisories/GHSA-r8w9-5wcg-vfj7/dependabot) 53 | * Switch to rustls webpki roots instead of native ones. Potentially breaking change. 54 | 55 | ## 0.0.6 56 | 57 | ### Added 58 | * Added crate name search courtesy of @jm-observer 59 | 60 | ## 0.0.4 61 | 62 | ### Added 63 | * Implemented CrateCache which can be used with either of the crates.io backends. 64 | * Implemented crates.io sparse index backend and set it as the default. 65 | 66 | ### Fixed 67 | * Left-over test code would create file CRATE_CACHE_DIR/test. 68 | 69 | ## 0.0.3 70 | 71 | ### Fixed 72 | * Fix vulnerability in [rustls-webpki](https://github.com/briansmith/webpki/issues/69) 73 | * Check crate versions immediately on open, instead of only on change. 74 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "addr2line" 7 | version = "0.24.2" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" 10 | dependencies = [ 11 | "gimli", 12 | ] 13 | 14 | [[package]] 15 | name = "adler2" 16 | version = "2.0.0" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" 19 | 20 | [[package]] 21 | name = "async-trait" 22 | version = "0.1.88" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" 25 | dependencies = [ 26 | "proc-macro2", 27 | "quote", 28 | "syn", 29 | ] 30 | 31 | [[package]] 32 | name = "auto_impl" 33 | version = "1.3.0" 34 | source = "registry+https://github.com/rust-lang/crates.io-index" 35 | checksum = "ffdcb70bdbc4d478427380519163274ac86e52916e10f0a8889adf0f96d3fee7" 36 | dependencies = [ 37 | "proc-macro2", 38 | "quote", 39 | "syn", 40 | ] 41 | 42 | [[package]] 43 | name = "autocfg" 44 | version = "1.4.0" 45 | source = "registry+https://github.com/rust-lang/crates.io-index" 46 | checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" 47 | 48 | [[package]] 49 | name = "backtrace" 50 | version = "0.3.74" 51 | source = "registry+https://github.com/rust-lang/crates.io-index" 52 | checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" 53 | dependencies = [ 54 | "addr2line", 55 | "cfg-if", 56 | "libc", 57 | "miniz_oxide", 58 | "object", 59 | "rustc-demangle", 60 | "windows-targets 0.52.6", 61 | ] 62 | 63 | [[package]] 64 | name = "base64" 65 | version = "0.22.1" 66 | source = "registry+https://github.com/rust-lang/crates.io-index" 67 | checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" 68 | 69 | [[package]] 70 | name = "bitflags" 71 | version = "1.3.2" 72 | source = "registry+https://github.com/rust-lang/crates.io-index" 73 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 74 | 75 | [[package]] 76 | name = "bitflags" 77 | version = "2.9.0" 78 | source = "registry+https://github.com/rust-lang/crates.io-index" 79 | checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" 80 | 81 | [[package]] 82 | name = "bumpalo" 83 | version = "3.17.0" 84 | source = "registry+https://github.com/rust-lang/crates.io-index" 85 | checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" 86 | 87 | [[package]] 88 | name = "bytes" 89 | version = "1.10.1" 90 | source = "registry+https://github.com/rust-lang/crates.io-index" 91 | checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" 92 | 93 | [[package]] 94 | name = "cc" 95 | version = "1.2.19" 96 | source = "registry+https://github.com/rust-lang/crates.io-index" 97 | checksum = "8e3a13707ac958681c13b39b458c073d0d9bc8a22cb1b2f4c8e55eb72c13f362" 98 | dependencies = [ 99 | "shlex", 100 | ] 101 | 102 | [[package]] 103 | name = "cfg-if" 104 | version = "1.0.0" 105 | source = "registry+https://github.com/rust-lang/crates.io-index" 106 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 107 | 108 | [[package]] 109 | name = "cfg_aliases" 110 | version = "0.2.1" 111 | source = "registry+https://github.com/rust-lang/crates.io-index" 112 | checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" 113 | 114 | [[package]] 115 | name = "crates-lsp" 116 | version = "0.1.7" 117 | dependencies = [ 118 | "async-trait", 119 | "indoc", 120 | "reqwest", 121 | "semver", 122 | "serde", 123 | "serde_json", 124 | "time", 125 | "tokio", 126 | "tower-lsp", 127 | ] 128 | 129 | [[package]] 130 | name = "dashmap" 131 | version = "5.5.3" 132 | source = "registry+https://github.com/rust-lang/crates.io-index" 133 | checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" 134 | dependencies = [ 135 | "cfg-if", 136 | "hashbrown", 137 | "lock_api", 138 | "once_cell", 139 | "parking_lot_core", 140 | ] 141 | 142 | [[package]] 143 | name = "deranged" 144 | version = "0.4.0" 145 | source = "registry+https://github.com/rust-lang/crates.io-index" 146 | checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" 147 | dependencies = [ 148 | "powerfmt", 149 | "serde", 150 | ] 151 | 152 | [[package]] 153 | name = "displaydoc" 154 | version = "0.2.5" 155 | source = "registry+https://github.com/rust-lang/crates.io-index" 156 | checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" 157 | dependencies = [ 158 | "proc-macro2", 159 | "quote", 160 | "syn", 161 | ] 162 | 163 | [[package]] 164 | name = "either" 165 | version = "1.15.0" 166 | source = "registry+https://github.com/rust-lang/crates.io-index" 167 | checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" 168 | 169 | [[package]] 170 | name = "fnv" 171 | version = "1.0.7" 172 | source = "registry+https://github.com/rust-lang/crates.io-index" 173 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 174 | 175 | [[package]] 176 | name = "form_urlencoded" 177 | version = "1.2.1" 178 | source = "registry+https://github.com/rust-lang/crates.io-index" 179 | checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" 180 | dependencies = [ 181 | "percent-encoding", 182 | ] 183 | 184 | [[package]] 185 | name = "futures" 186 | version = "0.3.31" 187 | source = "registry+https://github.com/rust-lang/crates.io-index" 188 | checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" 189 | dependencies = [ 190 | "futures-channel", 191 | "futures-core", 192 | "futures-io", 193 | "futures-sink", 194 | "futures-task", 195 | "futures-util", 196 | ] 197 | 198 | [[package]] 199 | name = "futures-channel" 200 | version = "0.3.31" 201 | source = "registry+https://github.com/rust-lang/crates.io-index" 202 | checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" 203 | dependencies = [ 204 | "futures-core", 205 | "futures-sink", 206 | ] 207 | 208 | [[package]] 209 | name = "futures-core" 210 | version = "0.3.31" 211 | source = "registry+https://github.com/rust-lang/crates.io-index" 212 | checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" 213 | 214 | [[package]] 215 | name = "futures-io" 216 | version = "0.3.31" 217 | source = "registry+https://github.com/rust-lang/crates.io-index" 218 | checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" 219 | 220 | [[package]] 221 | name = "futures-macro" 222 | version = "0.3.31" 223 | source = "registry+https://github.com/rust-lang/crates.io-index" 224 | checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" 225 | dependencies = [ 226 | "proc-macro2", 227 | "quote", 228 | "syn", 229 | ] 230 | 231 | [[package]] 232 | name = "futures-sink" 233 | version = "0.3.31" 234 | source = "registry+https://github.com/rust-lang/crates.io-index" 235 | checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" 236 | 237 | [[package]] 238 | name = "futures-task" 239 | version = "0.3.31" 240 | source = "registry+https://github.com/rust-lang/crates.io-index" 241 | checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" 242 | 243 | [[package]] 244 | name = "futures-util" 245 | version = "0.3.31" 246 | source = "registry+https://github.com/rust-lang/crates.io-index" 247 | checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" 248 | dependencies = [ 249 | "futures-channel", 250 | "futures-core", 251 | "futures-io", 252 | "futures-macro", 253 | "futures-sink", 254 | "futures-task", 255 | "memchr", 256 | "pin-project-lite", 257 | "pin-utils", 258 | "slab", 259 | ] 260 | 261 | [[package]] 262 | name = "getrandom" 263 | version = "0.2.16" 264 | source = "registry+https://github.com/rust-lang/crates.io-index" 265 | checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" 266 | dependencies = [ 267 | "cfg-if", 268 | "js-sys", 269 | "libc", 270 | "wasi 0.11.0+wasi-snapshot-preview1", 271 | "wasm-bindgen", 272 | ] 273 | 274 | [[package]] 275 | name = "getrandom" 276 | version = "0.3.2" 277 | source = "registry+https://github.com/rust-lang/crates.io-index" 278 | checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" 279 | dependencies = [ 280 | "cfg-if", 281 | "js-sys", 282 | "libc", 283 | "r-efi", 284 | "wasi 0.14.2+wasi-0.2.4", 285 | "wasm-bindgen", 286 | ] 287 | 288 | [[package]] 289 | name = "gimli" 290 | version = "0.31.1" 291 | source = "registry+https://github.com/rust-lang/crates.io-index" 292 | checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" 293 | 294 | [[package]] 295 | name = "hashbrown" 296 | version = "0.14.5" 297 | source = "registry+https://github.com/rust-lang/crates.io-index" 298 | checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" 299 | 300 | [[package]] 301 | name = "http" 302 | version = "1.3.1" 303 | source = "registry+https://github.com/rust-lang/crates.io-index" 304 | checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" 305 | dependencies = [ 306 | "bytes", 307 | "fnv", 308 | "itoa", 309 | ] 310 | 311 | [[package]] 312 | name = "http-body" 313 | version = "1.0.1" 314 | source = "registry+https://github.com/rust-lang/crates.io-index" 315 | checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" 316 | dependencies = [ 317 | "bytes", 318 | "http", 319 | ] 320 | 321 | [[package]] 322 | name = "http-body-util" 323 | version = "0.1.3" 324 | source = "registry+https://github.com/rust-lang/crates.io-index" 325 | checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" 326 | dependencies = [ 327 | "bytes", 328 | "futures-core", 329 | "http", 330 | "http-body", 331 | "pin-project-lite", 332 | ] 333 | 334 | [[package]] 335 | name = "httparse" 336 | version = "1.10.1" 337 | source = "registry+https://github.com/rust-lang/crates.io-index" 338 | checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" 339 | 340 | [[package]] 341 | name = "hyper" 342 | version = "1.6.0" 343 | source = "registry+https://github.com/rust-lang/crates.io-index" 344 | checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" 345 | dependencies = [ 346 | "bytes", 347 | "futures-channel", 348 | "futures-util", 349 | "http", 350 | "http-body", 351 | "httparse", 352 | "itoa", 353 | "pin-project-lite", 354 | "smallvec", 355 | "tokio", 356 | "want", 357 | ] 358 | 359 | [[package]] 360 | name = "hyper-rustls" 361 | version = "0.27.5" 362 | source = "registry+https://github.com/rust-lang/crates.io-index" 363 | checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2" 364 | dependencies = [ 365 | "futures-util", 366 | "http", 367 | "hyper", 368 | "hyper-util", 369 | "rustls", 370 | "rustls-pki-types", 371 | "tokio", 372 | "tokio-rustls", 373 | "tower-service", 374 | "webpki-roots", 375 | ] 376 | 377 | [[package]] 378 | name = "hyper-util" 379 | version = "0.1.11" 380 | source = "registry+https://github.com/rust-lang/crates.io-index" 381 | checksum = "497bbc33a26fdd4af9ed9c70d63f61cf56a938375fbb32df34db9b1cd6d643f2" 382 | dependencies = [ 383 | "bytes", 384 | "futures-channel", 385 | "futures-util", 386 | "http", 387 | "http-body", 388 | "hyper", 389 | "libc", 390 | "pin-project-lite", 391 | "socket2", 392 | "tokio", 393 | "tower-service", 394 | "tracing", 395 | ] 396 | 397 | [[package]] 398 | name = "icu_collections" 399 | version = "1.5.0" 400 | source = "registry+https://github.com/rust-lang/crates.io-index" 401 | checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" 402 | dependencies = [ 403 | "displaydoc", 404 | "yoke", 405 | "zerofrom", 406 | "zerovec", 407 | ] 408 | 409 | [[package]] 410 | name = "icu_locid" 411 | version = "1.5.0" 412 | source = "registry+https://github.com/rust-lang/crates.io-index" 413 | checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" 414 | dependencies = [ 415 | "displaydoc", 416 | "litemap", 417 | "tinystr", 418 | "writeable", 419 | "zerovec", 420 | ] 421 | 422 | [[package]] 423 | name = "icu_locid_transform" 424 | version = "1.5.0" 425 | source = "registry+https://github.com/rust-lang/crates.io-index" 426 | checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" 427 | dependencies = [ 428 | "displaydoc", 429 | "icu_locid", 430 | "icu_locid_transform_data", 431 | "icu_provider", 432 | "tinystr", 433 | "zerovec", 434 | ] 435 | 436 | [[package]] 437 | name = "icu_locid_transform_data" 438 | version = "1.5.1" 439 | source = "registry+https://github.com/rust-lang/crates.io-index" 440 | checksum = "7515e6d781098bf9f7205ab3fc7e9709d34554ae0b21ddbcb5febfa4bc7df11d" 441 | 442 | [[package]] 443 | name = "icu_normalizer" 444 | version = "1.5.0" 445 | source = "registry+https://github.com/rust-lang/crates.io-index" 446 | checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" 447 | dependencies = [ 448 | "displaydoc", 449 | "icu_collections", 450 | "icu_normalizer_data", 451 | "icu_properties", 452 | "icu_provider", 453 | "smallvec", 454 | "utf16_iter", 455 | "utf8_iter", 456 | "write16", 457 | "zerovec", 458 | ] 459 | 460 | [[package]] 461 | name = "icu_normalizer_data" 462 | version = "1.5.1" 463 | source = "registry+https://github.com/rust-lang/crates.io-index" 464 | checksum = "c5e8338228bdc8ab83303f16b797e177953730f601a96c25d10cb3ab0daa0cb7" 465 | 466 | [[package]] 467 | name = "icu_properties" 468 | version = "1.5.1" 469 | source = "registry+https://github.com/rust-lang/crates.io-index" 470 | checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" 471 | dependencies = [ 472 | "displaydoc", 473 | "icu_collections", 474 | "icu_locid_transform", 475 | "icu_properties_data", 476 | "icu_provider", 477 | "tinystr", 478 | "zerovec", 479 | ] 480 | 481 | [[package]] 482 | name = "icu_properties_data" 483 | version = "1.5.1" 484 | source = "registry+https://github.com/rust-lang/crates.io-index" 485 | checksum = "85fb8799753b75aee8d2a21d7c14d9f38921b54b3dbda10f5a3c7a7b82dba5e2" 486 | 487 | [[package]] 488 | name = "icu_provider" 489 | version = "1.5.0" 490 | source = "registry+https://github.com/rust-lang/crates.io-index" 491 | checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" 492 | dependencies = [ 493 | "displaydoc", 494 | "icu_locid", 495 | "icu_provider_macros", 496 | "stable_deref_trait", 497 | "tinystr", 498 | "writeable", 499 | "yoke", 500 | "zerofrom", 501 | "zerovec", 502 | ] 503 | 504 | [[package]] 505 | name = "icu_provider_macros" 506 | version = "1.5.0" 507 | source = "registry+https://github.com/rust-lang/crates.io-index" 508 | checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" 509 | dependencies = [ 510 | "proc-macro2", 511 | "quote", 512 | "syn", 513 | ] 514 | 515 | [[package]] 516 | name = "idna" 517 | version = "1.0.3" 518 | source = "registry+https://github.com/rust-lang/crates.io-index" 519 | checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" 520 | dependencies = [ 521 | "idna_adapter", 522 | "smallvec", 523 | "utf8_iter", 524 | ] 525 | 526 | [[package]] 527 | name = "idna_adapter" 528 | version = "1.2.0" 529 | source = "registry+https://github.com/rust-lang/crates.io-index" 530 | checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" 531 | dependencies = [ 532 | "icu_normalizer", 533 | "icu_properties", 534 | ] 535 | 536 | [[package]] 537 | name = "indoc" 538 | version = "2.0.6" 539 | source = "registry+https://github.com/rust-lang/crates.io-index" 540 | checksum = "f4c7245a08504955605670dbf141fceab975f15ca21570696aebe9d2e71576bd" 541 | 542 | [[package]] 543 | name = "ipnet" 544 | version = "2.11.0" 545 | source = "registry+https://github.com/rust-lang/crates.io-index" 546 | checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" 547 | 548 | [[package]] 549 | name = "itoa" 550 | version = "1.0.15" 551 | source = "registry+https://github.com/rust-lang/crates.io-index" 552 | checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" 553 | 554 | [[package]] 555 | name = "js-sys" 556 | version = "0.3.77" 557 | source = "registry+https://github.com/rust-lang/crates.io-index" 558 | checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" 559 | dependencies = [ 560 | "once_cell", 561 | "wasm-bindgen", 562 | ] 563 | 564 | [[package]] 565 | name = "libc" 566 | version = "0.2.172" 567 | source = "registry+https://github.com/rust-lang/crates.io-index" 568 | checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" 569 | 570 | [[package]] 571 | name = "litemap" 572 | version = "0.7.5" 573 | source = "registry+https://github.com/rust-lang/crates.io-index" 574 | checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" 575 | 576 | [[package]] 577 | name = "lock_api" 578 | version = "0.4.12" 579 | source = "registry+https://github.com/rust-lang/crates.io-index" 580 | checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" 581 | dependencies = [ 582 | "autocfg", 583 | "scopeguard", 584 | ] 585 | 586 | [[package]] 587 | name = "log" 588 | version = "0.4.27" 589 | source = "registry+https://github.com/rust-lang/crates.io-index" 590 | checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" 591 | 592 | [[package]] 593 | name = "lsp-types" 594 | version = "0.94.1" 595 | source = "registry+https://github.com/rust-lang/crates.io-index" 596 | checksum = "c66bfd44a06ae10647fe3f8214762e9369fd4248df1350924b4ef9e770a85ea1" 597 | dependencies = [ 598 | "bitflags 1.3.2", 599 | "serde", 600 | "serde_json", 601 | "serde_repr", 602 | "url", 603 | ] 604 | 605 | [[package]] 606 | name = "memchr" 607 | version = "2.7.4" 608 | source = "registry+https://github.com/rust-lang/crates.io-index" 609 | checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" 610 | 611 | [[package]] 612 | name = "mime" 613 | version = "0.3.17" 614 | source = "registry+https://github.com/rust-lang/crates.io-index" 615 | checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" 616 | 617 | [[package]] 618 | name = "miniz_oxide" 619 | version = "0.8.8" 620 | source = "registry+https://github.com/rust-lang/crates.io-index" 621 | checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" 622 | dependencies = [ 623 | "adler2", 624 | ] 625 | 626 | [[package]] 627 | name = "mio" 628 | version = "1.0.3" 629 | source = "registry+https://github.com/rust-lang/crates.io-index" 630 | checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" 631 | dependencies = [ 632 | "libc", 633 | "wasi 0.11.0+wasi-snapshot-preview1", 634 | "windows-sys 0.52.0", 635 | ] 636 | 637 | [[package]] 638 | name = "num-conv" 639 | version = "0.1.0" 640 | source = "registry+https://github.com/rust-lang/crates.io-index" 641 | checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" 642 | 643 | [[package]] 644 | name = "object" 645 | version = "0.36.7" 646 | source = "registry+https://github.com/rust-lang/crates.io-index" 647 | checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" 648 | dependencies = [ 649 | "memchr", 650 | ] 651 | 652 | [[package]] 653 | name = "once_cell" 654 | version = "1.21.3" 655 | source = "registry+https://github.com/rust-lang/crates.io-index" 656 | checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" 657 | 658 | [[package]] 659 | name = "parking_lot_core" 660 | version = "0.9.10" 661 | source = "registry+https://github.com/rust-lang/crates.io-index" 662 | checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" 663 | dependencies = [ 664 | "cfg-if", 665 | "libc", 666 | "redox_syscall", 667 | "smallvec", 668 | "windows-targets 0.52.6", 669 | ] 670 | 671 | [[package]] 672 | name = "percent-encoding" 673 | version = "2.3.1" 674 | source = "registry+https://github.com/rust-lang/crates.io-index" 675 | checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" 676 | 677 | [[package]] 678 | name = "pin-project" 679 | version = "1.1.10" 680 | source = "registry+https://github.com/rust-lang/crates.io-index" 681 | checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" 682 | dependencies = [ 683 | "pin-project-internal", 684 | ] 685 | 686 | [[package]] 687 | name = "pin-project-internal" 688 | version = "1.1.10" 689 | source = "registry+https://github.com/rust-lang/crates.io-index" 690 | checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" 691 | dependencies = [ 692 | "proc-macro2", 693 | "quote", 694 | "syn", 695 | ] 696 | 697 | [[package]] 698 | name = "pin-project-lite" 699 | version = "0.2.16" 700 | source = "registry+https://github.com/rust-lang/crates.io-index" 701 | checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" 702 | 703 | [[package]] 704 | name = "pin-utils" 705 | version = "0.1.0" 706 | source = "registry+https://github.com/rust-lang/crates.io-index" 707 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 708 | 709 | [[package]] 710 | name = "powerfmt" 711 | version = "0.2.0" 712 | source = "registry+https://github.com/rust-lang/crates.io-index" 713 | checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" 714 | 715 | [[package]] 716 | name = "ppv-lite86" 717 | version = "0.2.21" 718 | source = "registry+https://github.com/rust-lang/crates.io-index" 719 | checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" 720 | dependencies = [ 721 | "zerocopy", 722 | ] 723 | 724 | [[package]] 725 | name = "proc-macro2" 726 | version = "1.0.95" 727 | source = "registry+https://github.com/rust-lang/crates.io-index" 728 | checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" 729 | dependencies = [ 730 | "unicode-ident", 731 | ] 732 | 733 | [[package]] 734 | name = "quinn" 735 | version = "0.11.7" 736 | source = "registry+https://github.com/rust-lang/crates.io-index" 737 | checksum = "c3bd15a6f2967aef83887dcb9fec0014580467e33720d073560cf015a5683012" 738 | dependencies = [ 739 | "bytes", 740 | "cfg_aliases", 741 | "pin-project-lite", 742 | "quinn-proto", 743 | "quinn-udp", 744 | "rustc-hash", 745 | "rustls", 746 | "socket2", 747 | "thiserror 2.0.12", 748 | "tokio", 749 | "tracing", 750 | "web-time", 751 | ] 752 | 753 | [[package]] 754 | name = "quinn-proto" 755 | version = "0.11.11" 756 | source = "registry+https://github.com/rust-lang/crates.io-index" 757 | checksum = "bcbafbbdbb0f638fe3f35f3c56739f77a8a1d070cb25603226c83339b391472b" 758 | dependencies = [ 759 | "bytes", 760 | "getrandom 0.3.2", 761 | "rand", 762 | "ring", 763 | "rustc-hash", 764 | "rustls", 765 | "rustls-pki-types", 766 | "slab", 767 | "thiserror 2.0.12", 768 | "tinyvec", 769 | "tracing", 770 | "web-time", 771 | ] 772 | 773 | [[package]] 774 | name = "quinn-udp" 775 | version = "0.5.11" 776 | source = "registry+https://github.com/rust-lang/crates.io-index" 777 | checksum = "541d0f57c6ec747a90738a52741d3221f7960e8ac2f0ff4b1a63680e033b4ab5" 778 | dependencies = [ 779 | "cfg_aliases", 780 | "libc", 781 | "once_cell", 782 | "socket2", 783 | "tracing", 784 | "windows-sys 0.59.0", 785 | ] 786 | 787 | [[package]] 788 | name = "quote" 789 | version = "1.0.40" 790 | source = "registry+https://github.com/rust-lang/crates.io-index" 791 | checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" 792 | dependencies = [ 793 | "proc-macro2", 794 | ] 795 | 796 | [[package]] 797 | name = "r-efi" 798 | version = "5.2.0" 799 | source = "registry+https://github.com/rust-lang/crates.io-index" 800 | checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" 801 | 802 | [[package]] 803 | name = "rand" 804 | version = "0.9.1" 805 | source = "registry+https://github.com/rust-lang/crates.io-index" 806 | checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" 807 | dependencies = [ 808 | "rand_chacha", 809 | "rand_core", 810 | ] 811 | 812 | [[package]] 813 | name = "rand_chacha" 814 | version = "0.9.0" 815 | source = "registry+https://github.com/rust-lang/crates.io-index" 816 | checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" 817 | dependencies = [ 818 | "ppv-lite86", 819 | "rand_core", 820 | ] 821 | 822 | [[package]] 823 | name = "rand_core" 824 | version = "0.9.3" 825 | source = "registry+https://github.com/rust-lang/crates.io-index" 826 | checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" 827 | dependencies = [ 828 | "getrandom 0.3.2", 829 | ] 830 | 831 | [[package]] 832 | name = "redox_syscall" 833 | version = "0.5.11" 834 | source = "registry+https://github.com/rust-lang/crates.io-index" 835 | checksum = "d2f103c6d277498fbceb16e84d317e2a400f160f46904d5f5410848c829511a3" 836 | dependencies = [ 837 | "bitflags 2.9.0", 838 | ] 839 | 840 | [[package]] 841 | name = "reqwest" 842 | version = "0.12.15" 843 | source = "registry+https://github.com/rust-lang/crates.io-index" 844 | checksum = "d19c46a6fdd48bc4dab94b6103fccc55d34c67cc0ad04653aad4ea2a07cd7bbb" 845 | dependencies = [ 846 | "base64", 847 | "bytes", 848 | "futures-core", 849 | "futures-util", 850 | "http", 851 | "http-body", 852 | "http-body-util", 853 | "hyper", 854 | "hyper-rustls", 855 | "hyper-util", 856 | "ipnet", 857 | "js-sys", 858 | "log", 859 | "mime", 860 | "once_cell", 861 | "percent-encoding", 862 | "pin-project-lite", 863 | "quinn", 864 | "rustls", 865 | "rustls-pemfile", 866 | "rustls-pki-types", 867 | "serde", 868 | "serde_json", 869 | "serde_urlencoded", 870 | "sync_wrapper", 871 | "tokio", 872 | "tokio-rustls", 873 | "tokio-socks", 874 | "tower 0.5.2", 875 | "tower-service", 876 | "url", 877 | "wasm-bindgen", 878 | "wasm-bindgen-futures", 879 | "web-sys", 880 | "webpki-roots", 881 | "windows-registry", 882 | ] 883 | 884 | [[package]] 885 | name = "ring" 886 | version = "0.17.14" 887 | source = "registry+https://github.com/rust-lang/crates.io-index" 888 | checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" 889 | dependencies = [ 890 | "cc", 891 | "cfg-if", 892 | "getrandom 0.2.16", 893 | "libc", 894 | "untrusted", 895 | "windows-sys 0.52.0", 896 | ] 897 | 898 | [[package]] 899 | name = "rustc-demangle" 900 | version = "0.1.24" 901 | source = "registry+https://github.com/rust-lang/crates.io-index" 902 | checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" 903 | 904 | [[package]] 905 | name = "rustc-hash" 906 | version = "2.1.1" 907 | source = "registry+https://github.com/rust-lang/crates.io-index" 908 | checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" 909 | 910 | [[package]] 911 | name = "rustls" 912 | version = "0.23.26" 913 | source = "registry+https://github.com/rust-lang/crates.io-index" 914 | checksum = "df51b5869f3a441595eac5e8ff14d486ff285f7b8c0df8770e49c3b56351f0f0" 915 | dependencies = [ 916 | "once_cell", 917 | "ring", 918 | "rustls-pki-types", 919 | "rustls-webpki", 920 | "subtle", 921 | "zeroize", 922 | ] 923 | 924 | [[package]] 925 | name = "rustls-pemfile" 926 | version = "2.2.0" 927 | source = "registry+https://github.com/rust-lang/crates.io-index" 928 | checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" 929 | dependencies = [ 930 | "rustls-pki-types", 931 | ] 932 | 933 | [[package]] 934 | name = "rustls-pki-types" 935 | version = "1.11.0" 936 | source = "registry+https://github.com/rust-lang/crates.io-index" 937 | checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c" 938 | dependencies = [ 939 | "web-time", 940 | ] 941 | 942 | [[package]] 943 | name = "rustls-webpki" 944 | version = "0.103.1" 945 | source = "registry+https://github.com/rust-lang/crates.io-index" 946 | checksum = "fef8b8769aaccf73098557a87cd1816b4f9c7c16811c9c77142aa695c16f2c03" 947 | dependencies = [ 948 | "ring", 949 | "rustls-pki-types", 950 | "untrusted", 951 | ] 952 | 953 | [[package]] 954 | name = "rustversion" 955 | version = "1.0.20" 956 | source = "registry+https://github.com/rust-lang/crates.io-index" 957 | checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" 958 | 959 | [[package]] 960 | name = "ryu" 961 | version = "1.0.20" 962 | source = "registry+https://github.com/rust-lang/crates.io-index" 963 | checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" 964 | 965 | [[package]] 966 | name = "scopeguard" 967 | version = "1.2.0" 968 | source = "registry+https://github.com/rust-lang/crates.io-index" 969 | checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" 970 | 971 | [[package]] 972 | name = "semver" 973 | version = "1.0.26" 974 | source = "registry+https://github.com/rust-lang/crates.io-index" 975 | checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" 976 | dependencies = [ 977 | "serde", 978 | ] 979 | 980 | [[package]] 981 | name = "serde" 982 | version = "1.0.219" 983 | source = "registry+https://github.com/rust-lang/crates.io-index" 984 | checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" 985 | dependencies = [ 986 | "serde_derive", 987 | ] 988 | 989 | [[package]] 990 | name = "serde_derive" 991 | version = "1.0.219" 992 | source = "registry+https://github.com/rust-lang/crates.io-index" 993 | checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" 994 | dependencies = [ 995 | "proc-macro2", 996 | "quote", 997 | "syn", 998 | ] 999 | 1000 | [[package]] 1001 | name = "serde_json" 1002 | version = "1.0.140" 1003 | source = "registry+https://github.com/rust-lang/crates.io-index" 1004 | checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" 1005 | dependencies = [ 1006 | "itoa", 1007 | "memchr", 1008 | "ryu", 1009 | "serde", 1010 | ] 1011 | 1012 | [[package]] 1013 | name = "serde_repr" 1014 | version = "0.1.20" 1015 | source = "registry+https://github.com/rust-lang/crates.io-index" 1016 | checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" 1017 | dependencies = [ 1018 | "proc-macro2", 1019 | "quote", 1020 | "syn", 1021 | ] 1022 | 1023 | [[package]] 1024 | name = "serde_urlencoded" 1025 | version = "0.7.1" 1026 | source = "registry+https://github.com/rust-lang/crates.io-index" 1027 | checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" 1028 | dependencies = [ 1029 | "form_urlencoded", 1030 | "itoa", 1031 | "ryu", 1032 | "serde", 1033 | ] 1034 | 1035 | [[package]] 1036 | name = "shlex" 1037 | version = "1.3.0" 1038 | source = "registry+https://github.com/rust-lang/crates.io-index" 1039 | checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 1040 | 1041 | [[package]] 1042 | name = "slab" 1043 | version = "0.4.9" 1044 | source = "registry+https://github.com/rust-lang/crates.io-index" 1045 | checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" 1046 | dependencies = [ 1047 | "autocfg", 1048 | ] 1049 | 1050 | [[package]] 1051 | name = "smallvec" 1052 | version = "1.15.0" 1053 | source = "registry+https://github.com/rust-lang/crates.io-index" 1054 | checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" 1055 | 1056 | [[package]] 1057 | name = "socket2" 1058 | version = "0.5.9" 1059 | source = "registry+https://github.com/rust-lang/crates.io-index" 1060 | checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef" 1061 | dependencies = [ 1062 | "libc", 1063 | "windows-sys 0.52.0", 1064 | ] 1065 | 1066 | [[package]] 1067 | name = "stable_deref_trait" 1068 | version = "1.2.0" 1069 | source = "registry+https://github.com/rust-lang/crates.io-index" 1070 | checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" 1071 | 1072 | [[package]] 1073 | name = "subtle" 1074 | version = "2.6.1" 1075 | source = "registry+https://github.com/rust-lang/crates.io-index" 1076 | checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" 1077 | 1078 | [[package]] 1079 | name = "syn" 1080 | version = "2.0.100" 1081 | source = "registry+https://github.com/rust-lang/crates.io-index" 1082 | checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" 1083 | dependencies = [ 1084 | "proc-macro2", 1085 | "quote", 1086 | "unicode-ident", 1087 | ] 1088 | 1089 | [[package]] 1090 | name = "sync_wrapper" 1091 | version = "1.0.2" 1092 | source = "registry+https://github.com/rust-lang/crates.io-index" 1093 | checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" 1094 | dependencies = [ 1095 | "futures-core", 1096 | ] 1097 | 1098 | [[package]] 1099 | name = "synstructure" 1100 | version = "0.13.1" 1101 | source = "registry+https://github.com/rust-lang/crates.io-index" 1102 | checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" 1103 | dependencies = [ 1104 | "proc-macro2", 1105 | "quote", 1106 | "syn", 1107 | ] 1108 | 1109 | [[package]] 1110 | name = "thiserror" 1111 | version = "1.0.69" 1112 | source = "registry+https://github.com/rust-lang/crates.io-index" 1113 | checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" 1114 | dependencies = [ 1115 | "thiserror-impl 1.0.69", 1116 | ] 1117 | 1118 | [[package]] 1119 | name = "thiserror" 1120 | version = "2.0.12" 1121 | source = "registry+https://github.com/rust-lang/crates.io-index" 1122 | checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" 1123 | dependencies = [ 1124 | "thiserror-impl 2.0.12", 1125 | ] 1126 | 1127 | [[package]] 1128 | name = "thiserror-impl" 1129 | version = "1.0.69" 1130 | source = "registry+https://github.com/rust-lang/crates.io-index" 1131 | checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" 1132 | dependencies = [ 1133 | "proc-macro2", 1134 | "quote", 1135 | "syn", 1136 | ] 1137 | 1138 | [[package]] 1139 | name = "thiserror-impl" 1140 | version = "2.0.12" 1141 | source = "registry+https://github.com/rust-lang/crates.io-index" 1142 | checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" 1143 | dependencies = [ 1144 | "proc-macro2", 1145 | "quote", 1146 | "syn", 1147 | ] 1148 | 1149 | [[package]] 1150 | name = "time" 1151 | version = "0.3.41" 1152 | source = "registry+https://github.com/rust-lang/crates.io-index" 1153 | checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" 1154 | dependencies = [ 1155 | "deranged", 1156 | "itoa", 1157 | "num-conv", 1158 | "powerfmt", 1159 | "serde", 1160 | "time-core", 1161 | "time-macros", 1162 | ] 1163 | 1164 | [[package]] 1165 | name = "time-core" 1166 | version = "0.1.4" 1167 | source = "registry+https://github.com/rust-lang/crates.io-index" 1168 | checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" 1169 | 1170 | [[package]] 1171 | name = "time-macros" 1172 | version = "0.2.22" 1173 | source = "registry+https://github.com/rust-lang/crates.io-index" 1174 | checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" 1175 | dependencies = [ 1176 | "num-conv", 1177 | "time-core", 1178 | ] 1179 | 1180 | [[package]] 1181 | name = "tinystr" 1182 | version = "0.7.6" 1183 | source = "registry+https://github.com/rust-lang/crates.io-index" 1184 | checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" 1185 | dependencies = [ 1186 | "displaydoc", 1187 | "zerovec", 1188 | ] 1189 | 1190 | [[package]] 1191 | name = "tinyvec" 1192 | version = "1.9.0" 1193 | source = "registry+https://github.com/rust-lang/crates.io-index" 1194 | checksum = "09b3661f17e86524eccd4371ab0429194e0d7c008abb45f7a7495b1719463c71" 1195 | dependencies = [ 1196 | "tinyvec_macros", 1197 | ] 1198 | 1199 | [[package]] 1200 | name = "tinyvec_macros" 1201 | version = "0.1.1" 1202 | source = "registry+https://github.com/rust-lang/crates.io-index" 1203 | checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" 1204 | 1205 | [[package]] 1206 | name = "tokio" 1207 | version = "1.44.2" 1208 | source = "registry+https://github.com/rust-lang/crates.io-index" 1209 | checksum = "e6b88822cbe49de4185e3a4cbf8321dd487cf5fe0c5c65695fef6346371e9c48" 1210 | dependencies = [ 1211 | "backtrace", 1212 | "bytes", 1213 | "libc", 1214 | "mio", 1215 | "pin-project-lite", 1216 | "socket2", 1217 | "tokio-macros", 1218 | "windows-sys 0.52.0", 1219 | ] 1220 | 1221 | [[package]] 1222 | name = "tokio-macros" 1223 | version = "2.5.0" 1224 | source = "registry+https://github.com/rust-lang/crates.io-index" 1225 | checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" 1226 | dependencies = [ 1227 | "proc-macro2", 1228 | "quote", 1229 | "syn", 1230 | ] 1231 | 1232 | [[package]] 1233 | name = "tokio-rustls" 1234 | version = "0.26.2" 1235 | source = "registry+https://github.com/rust-lang/crates.io-index" 1236 | checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" 1237 | dependencies = [ 1238 | "rustls", 1239 | "tokio", 1240 | ] 1241 | 1242 | [[package]] 1243 | name = "tokio-socks" 1244 | version = "0.5.2" 1245 | source = "registry+https://github.com/rust-lang/crates.io-index" 1246 | checksum = "0d4770b8024672c1101b3f6733eab95b18007dbe0847a8afe341fcf79e06043f" 1247 | dependencies = [ 1248 | "either", 1249 | "futures-util", 1250 | "thiserror 1.0.69", 1251 | "tokio", 1252 | ] 1253 | 1254 | [[package]] 1255 | name = "tokio-util" 1256 | version = "0.7.15" 1257 | source = "registry+https://github.com/rust-lang/crates.io-index" 1258 | checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df" 1259 | dependencies = [ 1260 | "bytes", 1261 | "futures-core", 1262 | "futures-sink", 1263 | "pin-project-lite", 1264 | "tokio", 1265 | ] 1266 | 1267 | [[package]] 1268 | name = "tower" 1269 | version = "0.4.13" 1270 | source = "registry+https://github.com/rust-lang/crates.io-index" 1271 | checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" 1272 | dependencies = [ 1273 | "futures-core", 1274 | "futures-util", 1275 | "pin-project", 1276 | "pin-project-lite", 1277 | "tower-layer", 1278 | "tower-service", 1279 | ] 1280 | 1281 | [[package]] 1282 | name = "tower" 1283 | version = "0.5.2" 1284 | source = "registry+https://github.com/rust-lang/crates.io-index" 1285 | checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" 1286 | dependencies = [ 1287 | "futures-core", 1288 | "futures-util", 1289 | "pin-project-lite", 1290 | "sync_wrapper", 1291 | "tokio", 1292 | "tower-layer", 1293 | "tower-service", 1294 | ] 1295 | 1296 | [[package]] 1297 | name = "tower-layer" 1298 | version = "0.3.3" 1299 | source = "registry+https://github.com/rust-lang/crates.io-index" 1300 | checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" 1301 | 1302 | [[package]] 1303 | name = "tower-lsp" 1304 | version = "0.20.0" 1305 | source = "registry+https://github.com/rust-lang/crates.io-index" 1306 | checksum = "d4ba052b54a6627628d9b3c34c176e7eda8359b7da9acd497b9f20998d118508" 1307 | dependencies = [ 1308 | "async-trait", 1309 | "auto_impl", 1310 | "bytes", 1311 | "dashmap", 1312 | "futures", 1313 | "httparse", 1314 | "lsp-types", 1315 | "memchr", 1316 | "serde", 1317 | "serde_json", 1318 | "tokio", 1319 | "tokio-util", 1320 | "tower 0.4.13", 1321 | "tower-lsp-macros", 1322 | "tracing", 1323 | ] 1324 | 1325 | [[package]] 1326 | name = "tower-lsp-macros" 1327 | version = "0.9.0" 1328 | source = "registry+https://github.com/rust-lang/crates.io-index" 1329 | checksum = "84fd902d4e0b9a4b27f2f440108dc034e1758628a9b702f8ec61ad66355422fa" 1330 | dependencies = [ 1331 | "proc-macro2", 1332 | "quote", 1333 | "syn", 1334 | ] 1335 | 1336 | [[package]] 1337 | name = "tower-service" 1338 | version = "0.3.3" 1339 | source = "registry+https://github.com/rust-lang/crates.io-index" 1340 | checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" 1341 | 1342 | [[package]] 1343 | name = "tracing" 1344 | version = "0.1.41" 1345 | source = "registry+https://github.com/rust-lang/crates.io-index" 1346 | checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" 1347 | dependencies = [ 1348 | "pin-project-lite", 1349 | "tracing-attributes", 1350 | "tracing-core", 1351 | ] 1352 | 1353 | [[package]] 1354 | name = "tracing-attributes" 1355 | version = "0.1.28" 1356 | source = "registry+https://github.com/rust-lang/crates.io-index" 1357 | checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" 1358 | dependencies = [ 1359 | "proc-macro2", 1360 | "quote", 1361 | "syn", 1362 | ] 1363 | 1364 | [[package]] 1365 | name = "tracing-core" 1366 | version = "0.1.33" 1367 | source = "registry+https://github.com/rust-lang/crates.io-index" 1368 | checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" 1369 | dependencies = [ 1370 | "once_cell", 1371 | ] 1372 | 1373 | [[package]] 1374 | name = "try-lock" 1375 | version = "0.2.5" 1376 | source = "registry+https://github.com/rust-lang/crates.io-index" 1377 | checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" 1378 | 1379 | [[package]] 1380 | name = "unicode-ident" 1381 | version = "1.0.18" 1382 | source = "registry+https://github.com/rust-lang/crates.io-index" 1383 | checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" 1384 | 1385 | [[package]] 1386 | name = "untrusted" 1387 | version = "0.9.0" 1388 | source = "registry+https://github.com/rust-lang/crates.io-index" 1389 | checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" 1390 | 1391 | [[package]] 1392 | name = "url" 1393 | version = "2.5.4" 1394 | source = "registry+https://github.com/rust-lang/crates.io-index" 1395 | checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" 1396 | dependencies = [ 1397 | "form_urlencoded", 1398 | "idna", 1399 | "percent-encoding", 1400 | "serde", 1401 | ] 1402 | 1403 | [[package]] 1404 | name = "utf16_iter" 1405 | version = "1.0.5" 1406 | source = "registry+https://github.com/rust-lang/crates.io-index" 1407 | checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" 1408 | 1409 | [[package]] 1410 | name = "utf8_iter" 1411 | version = "1.0.4" 1412 | source = "registry+https://github.com/rust-lang/crates.io-index" 1413 | checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" 1414 | 1415 | [[package]] 1416 | name = "want" 1417 | version = "0.3.1" 1418 | source = "registry+https://github.com/rust-lang/crates.io-index" 1419 | checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" 1420 | dependencies = [ 1421 | "try-lock", 1422 | ] 1423 | 1424 | [[package]] 1425 | name = "wasi" 1426 | version = "0.11.0+wasi-snapshot-preview1" 1427 | source = "registry+https://github.com/rust-lang/crates.io-index" 1428 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 1429 | 1430 | [[package]] 1431 | name = "wasi" 1432 | version = "0.14.2+wasi-0.2.4" 1433 | source = "registry+https://github.com/rust-lang/crates.io-index" 1434 | checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" 1435 | dependencies = [ 1436 | "wit-bindgen-rt", 1437 | ] 1438 | 1439 | [[package]] 1440 | name = "wasm-bindgen" 1441 | version = "0.2.100" 1442 | source = "registry+https://github.com/rust-lang/crates.io-index" 1443 | checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" 1444 | dependencies = [ 1445 | "cfg-if", 1446 | "once_cell", 1447 | "rustversion", 1448 | "wasm-bindgen-macro", 1449 | ] 1450 | 1451 | [[package]] 1452 | name = "wasm-bindgen-backend" 1453 | version = "0.2.100" 1454 | source = "registry+https://github.com/rust-lang/crates.io-index" 1455 | checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" 1456 | dependencies = [ 1457 | "bumpalo", 1458 | "log", 1459 | "proc-macro2", 1460 | "quote", 1461 | "syn", 1462 | "wasm-bindgen-shared", 1463 | ] 1464 | 1465 | [[package]] 1466 | name = "wasm-bindgen-futures" 1467 | version = "0.4.50" 1468 | source = "registry+https://github.com/rust-lang/crates.io-index" 1469 | checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" 1470 | dependencies = [ 1471 | "cfg-if", 1472 | "js-sys", 1473 | "once_cell", 1474 | "wasm-bindgen", 1475 | "web-sys", 1476 | ] 1477 | 1478 | [[package]] 1479 | name = "wasm-bindgen-macro" 1480 | version = "0.2.100" 1481 | source = "registry+https://github.com/rust-lang/crates.io-index" 1482 | checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" 1483 | dependencies = [ 1484 | "quote", 1485 | "wasm-bindgen-macro-support", 1486 | ] 1487 | 1488 | [[package]] 1489 | name = "wasm-bindgen-macro-support" 1490 | version = "0.2.100" 1491 | source = "registry+https://github.com/rust-lang/crates.io-index" 1492 | checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" 1493 | dependencies = [ 1494 | "proc-macro2", 1495 | "quote", 1496 | "syn", 1497 | "wasm-bindgen-backend", 1498 | "wasm-bindgen-shared", 1499 | ] 1500 | 1501 | [[package]] 1502 | name = "wasm-bindgen-shared" 1503 | version = "0.2.100" 1504 | source = "registry+https://github.com/rust-lang/crates.io-index" 1505 | checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" 1506 | dependencies = [ 1507 | "unicode-ident", 1508 | ] 1509 | 1510 | [[package]] 1511 | name = "web-sys" 1512 | version = "0.3.77" 1513 | source = "registry+https://github.com/rust-lang/crates.io-index" 1514 | checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" 1515 | dependencies = [ 1516 | "js-sys", 1517 | "wasm-bindgen", 1518 | ] 1519 | 1520 | [[package]] 1521 | name = "web-time" 1522 | version = "1.1.0" 1523 | source = "registry+https://github.com/rust-lang/crates.io-index" 1524 | checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" 1525 | dependencies = [ 1526 | "js-sys", 1527 | "wasm-bindgen", 1528 | ] 1529 | 1530 | [[package]] 1531 | name = "webpki-roots" 1532 | version = "0.26.8" 1533 | source = "registry+https://github.com/rust-lang/crates.io-index" 1534 | checksum = "2210b291f7ea53617fbafcc4939f10914214ec15aace5ba62293a668f322c5c9" 1535 | dependencies = [ 1536 | "rustls-pki-types", 1537 | ] 1538 | 1539 | [[package]] 1540 | name = "windows-link" 1541 | version = "0.1.1" 1542 | source = "registry+https://github.com/rust-lang/crates.io-index" 1543 | checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" 1544 | 1545 | [[package]] 1546 | name = "windows-registry" 1547 | version = "0.4.0" 1548 | source = "registry+https://github.com/rust-lang/crates.io-index" 1549 | checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3" 1550 | dependencies = [ 1551 | "windows-result", 1552 | "windows-strings", 1553 | "windows-targets 0.53.0", 1554 | ] 1555 | 1556 | [[package]] 1557 | name = "windows-result" 1558 | version = "0.3.2" 1559 | source = "registry+https://github.com/rust-lang/crates.io-index" 1560 | checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252" 1561 | dependencies = [ 1562 | "windows-link", 1563 | ] 1564 | 1565 | [[package]] 1566 | name = "windows-strings" 1567 | version = "0.3.1" 1568 | source = "registry+https://github.com/rust-lang/crates.io-index" 1569 | checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319" 1570 | dependencies = [ 1571 | "windows-link", 1572 | ] 1573 | 1574 | [[package]] 1575 | name = "windows-sys" 1576 | version = "0.52.0" 1577 | source = "registry+https://github.com/rust-lang/crates.io-index" 1578 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 1579 | dependencies = [ 1580 | "windows-targets 0.52.6", 1581 | ] 1582 | 1583 | [[package]] 1584 | name = "windows-sys" 1585 | version = "0.59.0" 1586 | source = "registry+https://github.com/rust-lang/crates.io-index" 1587 | checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" 1588 | dependencies = [ 1589 | "windows-targets 0.52.6", 1590 | ] 1591 | 1592 | [[package]] 1593 | name = "windows-targets" 1594 | version = "0.52.6" 1595 | source = "registry+https://github.com/rust-lang/crates.io-index" 1596 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 1597 | dependencies = [ 1598 | "windows_aarch64_gnullvm 0.52.6", 1599 | "windows_aarch64_msvc 0.52.6", 1600 | "windows_i686_gnu 0.52.6", 1601 | "windows_i686_gnullvm 0.52.6", 1602 | "windows_i686_msvc 0.52.6", 1603 | "windows_x86_64_gnu 0.52.6", 1604 | "windows_x86_64_gnullvm 0.52.6", 1605 | "windows_x86_64_msvc 0.52.6", 1606 | ] 1607 | 1608 | [[package]] 1609 | name = "windows-targets" 1610 | version = "0.53.0" 1611 | source = "registry+https://github.com/rust-lang/crates.io-index" 1612 | checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b" 1613 | dependencies = [ 1614 | "windows_aarch64_gnullvm 0.53.0", 1615 | "windows_aarch64_msvc 0.53.0", 1616 | "windows_i686_gnu 0.53.0", 1617 | "windows_i686_gnullvm 0.53.0", 1618 | "windows_i686_msvc 0.53.0", 1619 | "windows_x86_64_gnu 0.53.0", 1620 | "windows_x86_64_gnullvm 0.53.0", 1621 | "windows_x86_64_msvc 0.53.0", 1622 | ] 1623 | 1624 | [[package]] 1625 | name = "windows_aarch64_gnullvm" 1626 | version = "0.52.6" 1627 | source = "registry+https://github.com/rust-lang/crates.io-index" 1628 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 1629 | 1630 | [[package]] 1631 | name = "windows_aarch64_gnullvm" 1632 | version = "0.53.0" 1633 | source = "registry+https://github.com/rust-lang/crates.io-index" 1634 | checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" 1635 | 1636 | [[package]] 1637 | name = "windows_aarch64_msvc" 1638 | version = "0.52.6" 1639 | source = "registry+https://github.com/rust-lang/crates.io-index" 1640 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 1641 | 1642 | [[package]] 1643 | name = "windows_aarch64_msvc" 1644 | version = "0.53.0" 1645 | source = "registry+https://github.com/rust-lang/crates.io-index" 1646 | checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" 1647 | 1648 | [[package]] 1649 | name = "windows_i686_gnu" 1650 | version = "0.52.6" 1651 | source = "registry+https://github.com/rust-lang/crates.io-index" 1652 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 1653 | 1654 | [[package]] 1655 | name = "windows_i686_gnu" 1656 | version = "0.53.0" 1657 | source = "registry+https://github.com/rust-lang/crates.io-index" 1658 | checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" 1659 | 1660 | [[package]] 1661 | name = "windows_i686_gnullvm" 1662 | version = "0.52.6" 1663 | source = "registry+https://github.com/rust-lang/crates.io-index" 1664 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 1665 | 1666 | [[package]] 1667 | name = "windows_i686_gnullvm" 1668 | version = "0.53.0" 1669 | source = "registry+https://github.com/rust-lang/crates.io-index" 1670 | checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" 1671 | 1672 | [[package]] 1673 | name = "windows_i686_msvc" 1674 | version = "0.52.6" 1675 | source = "registry+https://github.com/rust-lang/crates.io-index" 1676 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 1677 | 1678 | [[package]] 1679 | name = "windows_i686_msvc" 1680 | version = "0.53.0" 1681 | source = "registry+https://github.com/rust-lang/crates.io-index" 1682 | checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" 1683 | 1684 | [[package]] 1685 | name = "windows_x86_64_gnu" 1686 | version = "0.52.6" 1687 | source = "registry+https://github.com/rust-lang/crates.io-index" 1688 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 1689 | 1690 | [[package]] 1691 | name = "windows_x86_64_gnu" 1692 | version = "0.53.0" 1693 | source = "registry+https://github.com/rust-lang/crates.io-index" 1694 | checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" 1695 | 1696 | [[package]] 1697 | name = "windows_x86_64_gnullvm" 1698 | version = "0.52.6" 1699 | source = "registry+https://github.com/rust-lang/crates.io-index" 1700 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 1701 | 1702 | [[package]] 1703 | name = "windows_x86_64_gnullvm" 1704 | version = "0.53.0" 1705 | source = "registry+https://github.com/rust-lang/crates.io-index" 1706 | checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" 1707 | 1708 | [[package]] 1709 | name = "windows_x86_64_msvc" 1710 | version = "0.52.6" 1711 | source = "registry+https://github.com/rust-lang/crates.io-index" 1712 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 1713 | 1714 | [[package]] 1715 | name = "windows_x86_64_msvc" 1716 | version = "0.53.0" 1717 | source = "registry+https://github.com/rust-lang/crates.io-index" 1718 | checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" 1719 | 1720 | [[package]] 1721 | name = "wit-bindgen-rt" 1722 | version = "0.39.0" 1723 | source = "registry+https://github.com/rust-lang/crates.io-index" 1724 | checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" 1725 | dependencies = [ 1726 | "bitflags 2.9.0", 1727 | ] 1728 | 1729 | [[package]] 1730 | name = "write16" 1731 | version = "1.0.0" 1732 | source = "registry+https://github.com/rust-lang/crates.io-index" 1733 | checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" 1734 | 1735 | [[package]] 1736 | name = "writeable" 1737 | version = "0.5.5" 1738 | source = "registry+https://github.com/rust-lang/crates.io-index" 1739 | checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" 1740 | 1741 | [[package]] 1742 | name = "yoke" 1743 | version = "0.7.5" 1744 | source = "registry+https://github.com/rust-lang/crates.io-index" 1745 | checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" 1746 | dependencies = [ 1747 | "serde", 1748 | "stable_deref_trait", 1749 | "yoke-derive", 1750 | "zerofrom", 1751 | ] 1752 | 1753 | [[package]] 1754 | name = "yoke-derive" 1755 | version = "0.7.5" 1756 | source = "registry+https://github.com/rust-lang/crates.io-index" 1757 | checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" 1758 | dependencies = [ 1759 | "proc-macro2", 1760 | "quote", 1761 | "syn", 1762 | "synstructure", 1763 | ] 1764 | 1765 | [[package]] 1766 | name = "zerocopy" 1767 | version = "0.8.24" 1768 | source = "registry+https://github.com/rust-lang/crates.io-index" 1769 | checksum = "2586fea28e186957ef732a5f8b3be2da217d65c5969d4b1e17f973ebbe876879" 1770 | dependencies = [ 1771 | "zerocopy-derive", 1772 | ] 1773 | 1774 | [[package]] 1775 | name = "zerocopy-derive" 1776 | version = "0.8.24" 1777 | source = "registry+https://github.com/rust-lang/crates.io-index" 1778 | checksum = "a996a8f63c5c4448cd959ac1bab0aaa3306ccfd060472f85943ee0750f0169be" 1779 | dependencies = [ 1780 | "proc-macro2", 1781 | "quote", 1782 | "syn", 1783 | ] 1784 | 1785 | [[package]] 1786 | name = "zerofrom" 1787 | version = "0.1.6" 1788 | source = "registry+https://github.com/rust-lang/crates.io-index" 1789 | checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" 1790 | dependencies = [ 1791 | "zerofrom-derive", 1792 | ] 1793 | 1794 | [[package]] 1795 | name = "zerofrom-derive" 1796 | version = "0.1.6" 1797 | source = "registry+https://github.com/rust-lang/crates.io-index" 1798 | checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" 1799 | dependencies = [ 1800 | "proc-macro2", 1801 | "quote", 1802 | "syn", 1803 | "synstructure", 1804 | ] 1805 | 1806 | [[package]] 1807 | name = "zeroize" 1808 | version = "1.8.1" 1809 | source = "registry+https://github.com/rust-lang/crates.io-index" 1810 | checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" 1811 | 1812 | [[package]] 1813 | name = "zerovec" 1814 | version = "0.10.4" 1815 | source = "registry+https://github.com/rust-lang/crates.io-index" 1816 | checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" 1817 | dependencies = [ 1818 | "yoke", 1819 | "zerofrom", 1820 | "zerovec-derive", 1821 | ] 1822 | 1823 | [[package]] 1824 | name = "zerovec-derive" 1825 | version = "0.10.3" 1826 | source = "registry+https://github.com/rust-lang/crates.io-index" 1827 | checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" 1828 | dependencies = [ 1829 | "proc-macro2", 1830 | "quote", 1831 | "syn", 1832 | ] 1833 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "crates-lsp" 3 | version = "0.1.7" 4 | edition = "2021" 5 | keywords = ["crates", "cargo", "lsp"] 6 | description = "Language Server Protocol implementation for Cargo.toml manifests." 7 | license = "MIT" 8 | 9 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 10 | 11 | [dependencies] 12 | tokio = { version = "1.44.2", features = ["rt", "macros", "io-std"] } 13 | tower-lsp = "0.20.0" 14 | async-trait = "0.1" 15 | 16 | semver = { version = "1", features = ["serde"] } 17 | serde_json = "1.0.104" 18 | serde = { version = "1.0.180", features = ["derive"] } 19 | time = { version = "0.3", features = ["serde", "parsing", "formatting"] } 20 | reqwest = { version = "0.12.12", default-features = false, features = [ 21 | "json", 22 | "socks", 23 | "rustls-tls-webpki-roots", 24 | ] } 25 | 26 | [dev-dependencies] 27 | indoc = "2" 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Mathias Pius 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # crates-lsp 2 | Language Server implementation targeted specifically towards the `Cargo.toml` 3 | file of Rust projects, providing auto-completion for crate versions and 4 | in-editor hints when the selected crate versions are out of date. 5 | 6 | This project was started specifically to be used with the Lapce editor plugin [lapce-crates](https://github.com/MathiasPius/lapce-crates/), but should work with any LSP-capable editor. 7 | 8 | Project is heavily inspired by the [crates](https://github.com/serayuzgur/crates) plugin for VSCode. 9 | 10 | # Usage 11 | 12 | 13 | ## Lapce 14 | To use this with Lapce, install the Crates plugin from within the Lapce editor. 15 | 16 | ## Helix 17 | @ameknite kindly provided this example configuration for the [Helix](https://helix-editor.com/) editor: 18 | 19 | ```toml 20 | [[language]] 21 | name = "toml" 22 | language-servers = [ 23 | { name = "crates-lsp", except-features = [ 24 | "format", 25 | ] }, 26 | "taplo", 27 | ] 28 | 29 | formatter = { command = "taplo", args = ["fmt", "-"] } 30 | ``` 31 | -------------------------------------------------------------------------------- /src/crates/api.rs: -------------------------------------------------------------------------------- 1 | use async_trait::async_trait; 2 | use reqwest::Client; 3 | use semver::Version; 4 | use serde::Deserialize; 5 | 6 | use super::{default_client, CrateError, CrateLookup}; 7 | 8 | #[derive(Debug, Clone)] 9 | pub struct CrateApi { 10 | client: Client, 11 | } 12 | 13 | #[async_trait] 14 | impl CrateLookup for CrateApi { 15 | fn client(&self) -> &Client { 16 | &self.client 17 | } 18 | 19 | async fn get_latest_version(self, crate_name: String) -> Result { 20 | let response = self 21 | .client 22 | .get(&format!("https://crates.io/api/v1/crates/{crate_name}")) 23 | .send() 24 | .await 25 | .map_err(CrateError::transport)?; 26 | 27 | #[derive(Deserialize)] 28 | struct CrateInner { 29 | pub max_stable_version: Version, 30 | } 31 | 32 | #[derive(Deserialize)] 33 | struct Crate { 34 | #[serde(rename = "crate")] 35 | pub inner: CrateInner, 36 | } 37 | let details: Crate = response.json().await?; 38 | 39 | Ok(details.inner.max_stable_version) 40 | } 41 | } 42 | 43 | impl Default for CrateApi { 44 | fn default() -> Self { 45 | CrateApi { 46 | client: default_client(), 47 | } 48 | } 49 | } 50 | 51 | #[cfg(test)] 52 | mod tests { 53 | use crate::crates::{api::CrateApi, cache::CrateCache, CrateLookup}; 54 | 55 | #[tokio::test] 56 | async fn get_common_crates() { 57 | let api = CrateApi::default(); 58 | 59 | let cache = CrateCache::default(); 60 | 61 | let versions = api 62 | .fetch_versions(cache, &["serde", "log", "tracing", "crate-does-not-exist"]) 63 | .await; 64 | 65 | println!("{versions:#?}"); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/crates/cache.rs: -------------------------------------------------------------------------------- 1 | use std::{collections::HashMap, path::Path, sync::Arc}; 2 | 3 | use semver::Version; 4 | use serde::{Deserialize, Serialize}; 5 | use time::OffsetDateTime; 6 | use tokio::sync::RwLock; 7 | 8 | const CRATE_CACHE_DIR: &str = "./.crates-lsp/plugins/crates-lsp/crates.io"; 9 | 10 | #[derive(Debug, Clone, Serialize, Deserialize)] 11 | struct Fetch { 12 | pub version: Option, 13 | #[serde(with = "time::serde::iso8601")] 14 | pub expires_at: OffsetDateTime, 15 | } 16 | 17 | #[derive(Debug, Clone)] 18 | pub struct CrateCache { 19 | crates: Arc>>, 20 | } 21 | 22 | impl Default for CrateCache { 23 | fn default() -> Self { 24 | std::fs::create_dir_all(CRATE_CACHE_DIR) 25 | .expect("Failed to create cargo crate version cache dir."); 26 | 27 | std::fs::write(Path::new(CRATE_CACHE_DIR).join(".gitignore"), "*") 28 | .expect("failed to create crates-lsp .gitignore file."); 29 | 30 | CrateCache { 31 | crates: Arc::new(RwLock::new(HashMap::default())), 32 | } 33 | } 34 | } 35 | 36 | pub enum CachedVersion { 37 | /// Crate was found, and a latest stable version was determined. 38 | Known(Version), 39 | 40 | /// The crate name is unknown, and does not exist in cache, nor 41 | /// do we know if the crate might be present in an upstream registry. 42 | Unknown, 43 | 44 | /// Crate was looked up in upstream registries, and was not found. 45 | DoesNotExist, 46 | } 47 | 48 | impl From> for CachedVersion { 49 | fn from(value: Option) -> Self { 50 | match value { 51 | Some(version) => CachedVersion::Known(version), 52 | None => CachedVersion::DoesNotExist, 53 | } 54 | } 55 | } 56 | 57 | impl CrateCache { 58 | pub async fn get(&self, crate_name: &str) -> CachedVersion { 59 | // Check the in-memory cache first. 60 | if let Some(cached) = self.crates.read().await.get(crate_name).cloned() { 61 | // Only return the cached result if it is still valid. 62 | if OffsetDateTime::now_utc() < cached.expires_at { 63 | return cached.version.into(); 64 | } 65 | }; 66 | 67 | // Attempt to load crate informtion from file cache. 68 | if let Ok(content) = 69 | std::fs::read_to_string(std::path::Path::new(CRATE_CACHE_DIR).join(crate_name)) 70 | { 71 | if let Ok(fetch) = serde_json::from_str::(&content) { 72 | if OffsetDateTime::now_utc() < fetch.expires_at { 73 | self.put(crate_name, fetch.version.clone(), fetch.expires_at) 74 | .await; 75 | 76 | return fetch.version.into(); 77 | } 78 | } 79 | } 80 | 81 | CachedVersion::Unknown 82 | } 83 | 84 | pub async fn put( 85 | &self, 86 | crate_name: &str, 87 | version: Option, 88 | expires_at: OffsetDateTime, 89 | ) { 90 | let fetch = Fetch { 91 | version, 92 | expires_at, 93 | }; 94 | 95 | std::fs::write( 96 | Path::new(CRATE_CACHE_DIR).join(crate_name), 97 | serde_json::to_string(&fetch).as_deref().unwrap_or("{}"), 98 | ) 99 | .unwrap(); 100 | 101 | self.crates 102 | .write() 103 | .await 104 | .insert(crate_name.to_string(), fetch); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/crates/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod api; 2 | pub mod cache; 3 | pub mod sparse; 4 | 5 | use std::collections::HashMap; 6 | 7 | use async_trait::async_trait; 8 | use reqwest::{Client, Error}; 9 | use semver::Version; 10 | use serde::Deserialize; 11 | use time::OffsetDateTime; 12 | use tokio::sync::mpsc; 13 | 14 | use self::cache::{CachedVersion, CrateCache}; 15 | 16 | #[allow(dead_code)] 17 | #[derive(Debug)] 18 | pub enum CrateError { 19 | NoVersionsFound, 20 | InvalidCrateName(String), 21 | Transport(Box), 22 | Deserialization(serde_json::Error), 23 | Reqwest(Error), 24 | } 25 | 26 | impl CrateError { 27 | pub fn transport(error: impl std::error::Error + Send + 'static) -> Self { 28 | CrateError::Transport(Box::new(error)) 29 | } 30 | } 31 | 32 | impl From for CrateError { 33 | fn from(value: Error) -> Self { 34 | Self::Reqwest(value) 35 | } 36 | } 37 | 38 | #[derive(Deserialize)] 39 | pub struct Crate { 40 | pub name: String, 41 | } 42 | 43 | #[derive(Deserialize)] 44 | struct Crates { 45 | pub crates: Vec, 46 | } 47 | 48 | #[async_trait] 49 | pub trait CrateLookup: Clone + Send + 'static { 50 | fn client(&self) -> &Client; 51 | async fn search_crates(&self, crate_name: &String) -> Result, CrateError> { 52 | let response = self 53 | .client() 54 | .get(&format!( 55 | "https://crates.io/api/v1/crates?q={}&per_page=5", 56 | crate_name 57 | )) 58 | .send() 59 | .await 60 | .map_err(CrateError::transport)?; 61 | 62 | let details: Crates = response.json().await?; 63 | Ok(details.crates) 64 | } 65 | 66 | async fn get_latest_version(self, crate_name: String) -> Result; 67 | 68 | // How long to cache a result for. 69 | fn time_to_live(_version: &Option) -> time::Duration { 70 | time::Duration::days(1) 71 | } 72 | 73 | async fn fetch_versions( 74 | &self, 75 | cache: CrateCache, 76 | crate_names: &[&str], 77 | ) -> HashMap> { 78 | let crate_names: Vec<_> = crate_names.iter().map(|name| name.to_string()).collect(); 79 | 80 | let mut versions = HashMap::new(); 81 | 82 | let mut dispatched_tasks = 0; 83 | let (tx, mut rx) = mpsc::channel(crate_names.len()); 84 | for crate_name in crate_names { 85 | let tx = tx.clone(); 86 | 87 | match cache.get(&crate_name).await { 88 | CachedVersion::Known(version) => { 89 | versions.insert(crate_name, Some(version)); 90 | } 91 | CachedVersion::DoesNotExist => { 92 | versions.insert(crate_name, None); 93 | } 94 | CachedVersion::Unknown => { 95 | dispatched_tasks += 1; 96 | let cloned_self = self.clone(); 97 | 98 | tokio::spawn(async move { 99 | match cloned_self.get_latest_version(crate_name.clone()).await { 100 | Ok(version) => tx.send((crate_name, Some(version))).await, 101 | Err(err) => { 102 | println!("{:?}", err); 103 | tx.send((crate_name, None)).await 104 | } 105 | } 106 | }); 107 | } 108 | }; 109 | } 110 | 111 | for _ in 0..dispatched_tasks { 112 | let Some((name, version)) = rx.recv().await else { 113 | // If the receiver is broken, just ignore the rest of the dispatched tasks 114 | // and return whatever we have already. 115 | break; 116 | }; 117 | 118 | // Set 24h expiration regardless of whether a package was found or not. 119 | let expires_at = OffsetDateTime::now_utc().saturating_add(Self::time_to_live(&version)); 120 | 121 | // Store the result in the cache. 122 | cache.put(&name, version.clone(), expires_at).await; 123 | 124 | versions.insert(name, version); 125 | } 126 | 127 | versions 128 | } 129 | } 130 | 131 | pub fn default_client() -> Client { 132 | _default_client().unwrap_or_default() 133 | } 134 | fn _default_client() -> reqwest::Result { 135 | let builder = Client::builder() 136 | .timeout(std::time::Duration::from_secs(10)) 137 | .user_agent("crates-lsp (github.com/MathiasPius/crates-lsp)"); 138 | 139 | if let Ok(proxy) = std::env::var("https_proxy") { 140 | if let Ok(proxy) = reqwest::Proxy::all(proxy) { 141 | return builder.proxy(proxy).build(); 142 | } 143 | }; 144 | builder.build() 145 | } 146 | -------------------------------------------------------------------------------- /src/crates/sparse.rs: -------------------------------------------------------------------------------- 1 | use async_trait::async_trait; 2 | use reqwest::Client; 3 | use semver::Version; 4 | use serde::Deserialize; 5 | 6 | use super::{default_client, CrateError, CrateLookup}; 7 | 8 | #[derive(Debug, Clone)] 9 | pub struct CrateIndex { 10 | client: Client, 11 | } 12 | 13 | #[async_trait] 14 | impl CrateLookup for CrateIndex { 15 | fn client(&self) -> &Client { 16 | &self.client 17 | } 18 | 19 | async fn get_latest_version(self, crate_name: String) -> Result { 20 | let crate_index_path = match crate_name.len() { 21 | 0 => return Err(CrateError::InvalidCrateName(crate_name)), 22 | 1 => format!("1/{crate_name}"), 23 | 2 => format!("2/{crate_name}"), 24 | 3 => format!("3/{}/{crate_name}", &crate_name[0..1]), 25 | _ => format!("{}/{}/{crate_name}", &crate_name[0..2], &crate_name[2..4]), 26 | }; 27 | 28 | let response = self 29 | .client 30 | .get(&format!("https://index.crates.io/{crate_index_path}")) 31 | .send() 32 | .await 33 | .map_err(CrateError::transport)?; 34 | 35 | let stringified = response.text().await?; 36 | 37 | let mut all_releases = Vec::new(); 38 | for line in stringified.lines() { 39 | #[derive(Deserialize)] 40 | struct CrateVersion { 41 | pub vers: Version, 42 | pub yanked: bool, 43 | } 44 | 45 | let version: CrateVersion = 46 | serde_json::from_str(line).map_err(CrateError::Deserialization)?; 47 | 48 | all_releases.push(version); 49 | } 50 | 51 | let unyanked_versions: Vec<_> = all_releases 52 | .into_iter() 53 | .filter(|release| !release.yanked) 54 | .map(|release| release.vers) 55 | .collect(); 56 | 57 | // Try to find the latest non-prerelease version first, falling back to whichever 58 | // latest pre-release version is available. 59 | unyanked_versions 60 | .iter() 61 | .filter(|version| version.pre.is_empty()) 62 | .max() 63 | .or(unyanked_versions.iter().max()) 64 | .cloned() 65 | .ok_or(CrateError::NoVersionsFound) 66 | } 67 | } 68 | 69 | impl Default for CrateIndex { 70 | fn default() -> Self { 71 | CrateIndex { 72 | client: default_client(), 73 | } 74 | } 75 | } 76 | 77 | #[cfg(test)] 78 | mod tests { 79 | use crate::crates::{cache::CrateCache, sparse::CrateIndex, CrateLookup}; 80 | 81 | #[tokio::test] 82 | async fn get_common_crates() { 83 | let api = CrateIndex::default(); 84 | 85 | let cache = CrateCache::default(); 86 | 87 | let versions = api 88 | .fetch_versions(cache, &["serde", "log", "tracing", "crate-does-not-exist"]) 89 | .await; 90 | 91 | println!("{versions:#?}"); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use crate::parse::{Dependency, DependencyWithVersion}; 2 | use crates::api::CrateApi; 3 | use crates::cache::CrateCache; 4 | use crates::sparse::CrateIndex; 5 | use crates::CrateLookup; 6 | use parse::{DependencyVersion, ManifestTracker}; 7 | use settings::Settings; 8 | use tower_lsp::jsonrpc::Result; 9 | use tower_lsp::lsp_types::*; 10 | use tower_lsp::{Client, LanguageServer, LspService, Server}; 11 | 12 | mod crates; 13 | mod parse; 14 | mod settings; 15 | 16 | mod diagnostic_codes { 17 | pub const UP_TO_DATE: i32 = 0; 18 | pub const NEEDS_UPDATE: i32 = 1; 19 | pub const UNKNOWN_DEP: i32 = 2; 20 | } 21 | 22 | #[derive(Debug, Clone)] 23 | struct Backend { 24 | client: Client, 25 | settings: Settings, 26 | manifests: ManifestTracker, 27 | api: CrateApi, 28 | sparse: CrateIndex, 29 | cache: CrateCache, 30 | } 31 | 32 | impl Backend { 33 | async fn calculate_diagnostics(&self, url: Url, content: &str) -> Vec { 34 | if !self.settings.diagnostics().await { 35 | return Vec::new(); 36 | } 37 | 38 | let packages = self.manifests.update_from_source(url, content).await; 39 | 40 | // Retrieve just the package names, so we can fetch the latest 41 | // versions via the crate registry. 42 | let dependency_with_versions: Vec<&DependencyWithVersion> = packages 43 | .iter() 44 | .filter_map(|dependency| match dependency { 45 | Dependency::Partial { .. } => None, 46 | Dependency::WithVersion(dep) => Some(dep), 47 | Dependency::Other { .. } => None, 48 | }) 49 | .collect(); 50 | 51 | if dependency_with_versions.is_empty() { 52 | return Vec::new(); 53 | } 54 | 55 | let crate_names: Vec<&str> = dependency_with_versions 56 | .iter() 57 | .map(|x| x.name.as_str()) 58 | .collect(); 59 | // Get the newest version of each crate that appears in the manifest. 60 | let newest_packages = if self.settings.use_api().await { 61 | self.api 62 | .fetch_versions(self.cache.clone(), &crate_names) 63 | .await 64 | } else { 65 | self.sparse 66 | .fetch_versions(self.cache.clone(), &crate_names) 67 | .await 68 | }; 69 | 70 | // Produce diagnostic hints for each crate where we might be helpful. 71 | let nu_sev = self.settings.needs_update_severity().await; 72 | let utd_sev = self.settings.up_to_date_severity().await; 73 | let ud_sev = self.settings.unknown_dep_severity().await; 74 | let diagnostics: Vec<_> = dependency_with_versions 75 | .into_iter() 76 | .map(|dependency| { 77 | if let Some(Some(newest_version)) = newest_packages.get(&dependency.name) { 78 | match &dependency.version { 79 | DependencyVersion::Complete { range, version } => { 80 | if !version.matches(newest_version) { 81 | Diagnostic { 82 | range: *range, 83 | severity: Some(nu_sev), 84 | code: Some(NumberOrString::Number( 85 | diagnostic_codes::NEEDS_UPDATE, 86 | )), 87 | code_description: None, 88 | source: None, 89 | message: format!("{}: {newest_version}", &dependency.name), 90 | related_information: None, 91 | tags: None, 92 | data: Some(serde_json::json!({ 93 | "newest_version": newest_version, 94 | })), 95 | } 96 | } else { 97 | let range = Range { 98 | start: Position::new(range.start.line, 0), 99 | end: Position::new(range.start.line, 0), 100 | }; 101 | Diagnostic::new( 102 | range, 103 | Some(utd_sev), 104 | Some(NumberOrString::Number(diagnostic_codes::UP_TO_DATE)), 105 | None, 106 | "✓".to_string(), 107 | None, 108 | None, 109 | ) 110 | } 111 | } 112 | DependencyVersion::Partial { range, .. } => Diagnostic { 113 | range: *range, 114 | severity: Some(nu_sev), 115 | code: Some(NumberOrString::Number(diagnostic_codes::NEEDS_UPDATE)), 116 | code_description: None, 117 | source: None, 118 | message: format!("{}: {newest_version}", &dependency.name), 119 | related_information: None, 120 | tags: None, 121 | data: Some(serde_json::json!({ 122 | "newest_version": newest_version, 123 | })), 124 | }, 125 | } 126 | } else { 127 | Diagnostic { 128 | range: dependency.version.range(), 129 | severity: Some(ud_sev), 130 | code: Some(NumberOrString::Number(diagnostic_codes::UNKNOWN_DEP)), 131 | code_description: None, 132 | source: None, 133 | message: format!("{}: Unknown crate", &dependency.name), 134 | related_information: None, 135 | tags: None, 136 | data: None, 137 | } 138 | } 139 | }) 140 | .collect(); 141 | 142 | diagnostics 143 | } 144 | } 145 | 146 | #[tower_lsp::async_trait] 147 | impl LanguageServer for Backend { 148 | async fn initialize(&self, params: InitializeParams) -> Result { 149 | if let Some(settings) = params.initialization_options { 150 | self.settings.populate_from(settings).await; 151 | } 152 | 153 | Ok(InitializeResult { 154 | server_info: None, 155 | capabilities: ServerCapabilities { 156 | text_document_sync: Some(TextDocumentSyncCapability::Kind( 157 | TextDocumentSyncKind::FULL, 158 | )), 159 | completion_provider: Some(CompletionOptions { 160 | resolve_provider: Some(false), 161 | trigger_characters: Some(vec![ 162 | "=".to_string(), 163 | ".".to_string(), 164 | "\"".to_string(), 165 | ]), 166 | work_done_progress_options: Default::default(), 167 | all_commit_characters: None, 168 | ..Default::default() 169 | }), 170 | inlay_hint_provider: Some(OneOf::Left(true)), 171 | code_action_provider: Some(CodeActionProviderCapability::Simple(true)), 172 | execute_command_provider: Some(ExecuteCommandOptions { 173 | commands: vec!["dummy.do_something".to_string()], 174 | work_done_progress_options: Default::default(), 175 | }), 176 | workspace: Some(WorkspaceServerCapabilities { 177 | workspace_folders: Some(WorkspaceFoldersServerCapabilities { 178 | supported: Some(true), 179 | change_notifications: Some(OneOf::Left(true)), 180 | }), 181 | file_operations: None, 182 | }), 183 | 184 | ..ServerCapabilities::default() 185 | }, 186 | }) 187 | } 188 | 189 | async fn initialized(&self, _: InitializedParams) { 190 | self.client 191 | .log_message(MessageType::INFO, "crates-lsp initialized.") 192 | .await; 193 | } 194 | 195 | async fn shutdown(&self) -> Result<()> { 196 | Ok(()) 197 | } 198 | 199 | async fn did_change(&self, params: DidChangeTextDocumentParams) { 200 | if let Some(content) = params.content_changes.first() { 201 | let diagnostics = self 202 | .calculate_diagnostics(params.text_document.uri.clone(), &content.text) 203 | .await; 204 | 205 | self.client 206 | .publish_diagnostics( 207 | params.text_document.uri, 208 | diagnostics, 209 | Some(params.text_document.version), 210 | ) 211 | .await; 212 | } 213 | } 214 | 215 | async fn did_open(&self, params: DidOpenTextDocumentParams) { 216 | let diagnostics = self 217 | .calculate_diagnostics(params.text_document.uri.clone(), ¶ms.text_document.text) 218 | .await; 219 | 220 | self.client 221 | .publish_diagnostics( 222 | params.text_document.uri, 223 | diagnostics, 224 | Some(params.text_document.version), 225 | ) 226 | .await; 227 | } 228 | 229 | async fn completion(&self, params: CompletionParams) -> Result> { 230 | let cursor = params.text_document_position.position; 231 | 232 | let Some(dependencies) = self 233 | .manifests 234 | .get(¶ms.text_document_position.text_document.uri) 235 | .await 236 | else { 237 | return Ok(None); 238 | }; 239 | 240 | let Some(dependency) = dependencies 241 | .into_iter() 242 | .find(|dependency| match dependency { 243 | Dependency::Partial { line, .. } => *line == cursor.line, 244 | Dependency::WithVersion(dep) => { 245 | dep.version.range().start.line == cursor.line 246 | && dep.version.range().start.character <= cursor.character 247 | && dep.version.range().end.character >= cursor.character 248 | } 249 | Dependency::Other { .. } => false, 250 | }) 251 | else { 252 | return Ok(None); 253 | }; 254 | 255 | match dependency { 256 | Dependency::Partial { name, .. } => { 257 | let Ok(crates) = self.sparse.search_crates(&name).await else { 258 | return Ok(None); 259 | }; 260 | let range = Range::new(Position::new(cursor.line, 0), cursor); 261 | Ok(Some(CompletionResponse::Array( 262 | crates 263 | .into_iter() 264 | .map(|x| CompletionItem { 265 | text_edit: Some(CompletionTextEdit::Edit(TextEdit::new( 266 | range, 267 | x.name.clone(), 268 | ))), 269 | label: x.name, 270 | ..CompletionItem::default() 271 | }) 272 | .collect(), 273 | ))) 274 | } 275 | Dependency::WithVersion(dependency) => { 276 | let packages = self 277 | .sparse 278 | .fetch_versions(self.cache.clone(), &[&dependency.name]) 279 | .await; 280 | 281 | if let Some(Some(newest_version)) = packages.get(&dependency.name) { 282 | let specified_version = dependency.version.to_string(); 283 | 284 | let newest_version = newest_version.to_string(); 285 | 286 | let truncated_version = newest_version 287 | .as_str() 288 | .strip_prefix( 289 | specified_version 290 | .trim_start_matches(&['<', '>', '=', '^', '~'] as &[_]), 291 | ) 292 | .unwrap_or(&newest_version) 293 | .to_string(); 294 | 295 | Ok(Some(CompletionResponse::Array(vec![CompletionItem { 296 | insert_text: Some(truncated_version.clone()), 297 | label: newest_version.clone(), 298 | 299 | ..CompletionItem::default() 300 | }]))) 301 | } else { 302 | Ok(None) 303 | } 304 | } 305 | Dependency::Other { .. } => { 306 | return Ok(None); 307 | } 308 | } 309 | } 310 | 311 | async fn inlay_hint(&self, params: InlayHintParams) -> Result>> { 312 | if !self.settings.inlay_hints().await { 313 | return Ok(None); 314 | } 315 | 316 | let utd_hint = self.settings.up_to_date_hint().await; 317 | let nu_hint = self.settings.needs_update_hint().await; 318 | 319 | if utd_hint.is_empty() && nu_hint.is_empty() { 320 | return Ok(None); 321 | } 322 | 323 | let Some(dependencies) = self.manifests.get(¶ms.text_document.uri).await else { 324 | return Ok(None); 325 | }; 326 | let dependencies_with_versions: Vec = dependencies 327 | .into_iter() 328 | .filter_map(|d| match d { 329 | Dependency::WithVersion(v) => (v.version.range().start >= params.range.start 330 | && v.version.range().end <= params.range.end) 331 | .then_some(v), 332 | Dependency::Other { .. } | Dependency::Partial { .. } => None, 333 | }) 334 | .collect(); 335 | 336 | if dependencies_with_versions.is_empty() { 337 | return Ok(None); 338 | } 339 | 340 | let crate_names: Vec<&str> = dependencies_with_versions 341 | .iter() 342 | .map(|x| x.name.as_str()) 343 | .collect(); 344 | 345 | let newest_packages = if self.settings.use_api().await { 346 | self.api 347 | .fetch_versions(self.cache.clone(), &crate_names) 348 | .await 349 | } else { 350 | self.sparse 351 | .fetch_versions(self.cache.clone(), &crate_names) 352 | .await 353 | }; 354 | 355 | let mut v = if utd_hint.is_empty() || nu_hint.is_empty() { 356 | Vec::new() // if either is empty we dont know how many elements there are 357 | } else { 358 | Vec::with_capacity(dependencies_with_versions.len()) 359 | }; 360 | 361 | for dep in dependencies_with_versions { 362 | let Some(Some(newest_version)) = newest_packages.get(&dep.name) else { 363 | continue; 364 | }; 365 | let (hint, tip, pos) = match dep.version { 366 | DependencyVersion::Complete { range, version } => { 367 | let (hint, tip) = if version.matches(newest_version) { 368 | if utd_hint.is_empty() { 369 | continue; 370 | } 371 | ( 372 | utd_hint.replace("{}", &version.to_string()), 373 | "up to date".to_string(), 374 | ) 375 | } else { 376 | if nu_hint.is_empty() { 377 | continue; 378 | } 379 | ( 380 | nu_hint.replace("{}", &newest_version.to_string()), 381 | "latest stable version".to_string(), 382 | ) 383 | }; 384 | ( 385 | hint, 386 | tip, 387 | Position::new(range.end.line, range.end.character + 1), 388 | ) 389 | } 390 | DependencyVersion::Partial { range, .. } => { 391 | if nu_hint.is_empty() { 392 | continue; 393 | } 394 | ( 395 | nu_hint.replace("{}", &newest_version.to_string()), 396 | "latest stable version".to_string(), 397 | Position::new(range.end.line, range.end.character + 1), 398 | ) 399 | } 400 | }; 401 | v.push(InlayHint { 402 | position: pos, 403 | label: InlayHintLabel::String(hint), 404 | kind: None, 405 | text_edits: None, 406 | tooltip: Some(InlayHintTooltip::String(tip)), 407 | padding_left: Some(true), 408 | padding_right: None, 409 | data: None, 410 | }); 411 | } 412 | Ok(Some(v)) 413 | } 414 | 415 | async fn code_action(&self, params: CodeActionParams) -> Result> { 416 | let mut response = CodeActionResponse::new(); 417 | for d in params 418 | .context 419 | .diagnostics 420 | .into_iter() 421 | .filter(|d| d.range.start <= params.range.start && d.range.end >= params.range.end) 422 | { 423 | let Some(NumberOrString::Number(diagnostic_codes::NEEDS_UPDATE)) = d.code else { 424 | continue; 425 | }; 426 | 427 | let Some(serde_json::Value::Object(ref data)) = d.data else { 428 | continue; 429 | }; 430 | 431 | let Some(serde_json::Value::String(newest_version)) = data.get("newest_version") else { 432 | continue; 433 | }; 434 | 435 | let range = d.range; 436 | let newest_version = newest_version.clone(); 437 | 438 | response.push(CodeActionOrCommand::CodeAction(CodeAction { 439 | title: format!("Update Version to: {newest_version}"), 440 | kind: Some(CodeActionKind::QUICKFIX), 441 | diagnostics: Some(vec![d]), 442 | edit: Some(WorkspaceEdit { 443 | changes: Some( 444 | [( 445 | params.text_document.uri.clone(), 446 | vec![TextEdit { 447 | range, 448 | new_text: newest_version, 449 | }], 450 | )] 451 | .into(), 452 | ), 453 | document_changes: None, 454 | change_annotations: None, 455 | }), 456 | command: None, 457 | is_preferred: None, 458 | disabled: None, 459 | data: None, 460 | })) 461 | } 462 | Ok(Some(response)) 463 | } 464 | } 465 | 466 | #[tokio::main(flavor = "current_thread")] 467 | async fn main() { 468 | let (stdin, stdout) = (tokio::io::stdin(), tokio::io::stdout()); 469 | 470 | let (service, socket) = LspService::new(|client| Backend { 471 | client, 472 | manifests: ManifestTracker::default(), 473 | settings: Settings::default(), 474 | sparse: CrateIndex::default(), 475 | api: CrateApi::default(), 476 | cache: CrateCache::default(), 477 | }); 478 | Server::new(stdin, stdout, socket).serve(service).await; 479 | } 480 | -------------------------------------------------------------------------------- /src/parse.rs: -------------------------------------------------------------------------------- 1 | use std::{collections::HashMap, fmt::Display, sync::Arc}; 2 | 3 | use semver::VersionReq; 4 | use tokio::sync::RwLock; 5 | use tower_lsp::lsp_types::{Position, Range, Url}; 6 | 7 | #[derive(Debug, Clone, PartialEq, Eq)] 8 | pub enum Dependency { 9 | /// e.g: anyho 10 | Partial { 11 | name: String, 12 | line: u32, 13 | }, 14 | WithVersion(DependencyWithVersion), 15 | /// e.g: anyhow = { git = ".."} 16 | Other { 17 | name: String, 18 | }, 19 | } 20 | #[derive(Debug, Clone, PartialEq, Eq)] 21 | pub struct DependencyWithVersion { 22 | pub name: String, 23 | pub version: DependencyVersion, 24 | } 25 | // pub struct Dependency { 26 | // pub name: String, 27 | // pub version: Option, 28 | // } 29 | 30 | impl Dependency { 31 | pub fn name(&self) -> Option<&String> { 32 | match self { 33 | Dependency::Partial { .. } => None, 34 | Dependency::WithVersion(dep) => Some(&dep.name), 35 | Dependency::Other { name } => Some(name), 36 | } 37 | } 38 | 39 | pub fn name_mut(&mut self) -> Option<&mut String> { 40 | match self { 41 | Dependency::Partial { .. } => None, 42 | Dependency::WithVersion(dep) => Some(&mut dep.name), 43 | Dependency::Other { name } => Some(name), 44 | } 45 | } 46 | 47 | pub fn version_mut(&mut self) -> Option<&mut DependencyVersion> { 48 | match self { 49 | Dependency::Partial { .. } => None, 50 | Dependency::WithVersion(dep) => Some(&mut dep.version), 51 | Dependency::Other { .. } => None, 52 | } 53 | } 54 | } 55 | 56 | impl Display for Dependency { 57 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 58 | match self { 59 | Dependency::Partial { name, .. } => { 60 | write!(f, "{} = \"?\"", name) 61 | } 62 | Dependency::WithVersion(dep) => { 63 | write!(f, "{} = \"{}\"", dep.name, dep.version) 64 | } 65 | Dependency::Other { name } => { 66 | write!(f, "{} = \"?\"", name) 67 | } 68 | } 69 | } 70 | } 71 | 72 | #[derive(Debug, Clone, PartialEq, Eq)] 73 | pub enum DependencyVersion { 74 | Partial { range: Range, version: String }, 75 | Complete { range: Range, version: VersionReq }, 76 | } 77 | 78 | impl DependencyVersion { 79 | pub fn range(&self) -> Range { 80 | match self { 81 | DependencyVersion::Partial { range, .. } 82 | | DependencyVersion::Complete { range, .. } => *range, 83 | } 84 | } 85 | 86 | fn range_mut(&mut self) -> &mut Range { 87 | match self { 88 | DependencyVersion::Partial { range, .. } 89 | | DependencyVersion::Complete { range, .. } => range, 90 | } 91 | } 92 | } 93 | 94 | impl Display for DependencyVersion { 95 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 96 | match self { 97 | DependencyVersion::Partial { version, .. } => f.write_str(version), 98 | DependencyVersion::Complete { version, .. } => write!(f, "{}", version), 99 | } 100 | } 101 | } 102 | 103 | enum DocumentState { 104 | Dependencies, 105 | Dependency(String), 106 | Other, 107 | } 108 | 109 | #[derive(Debug)] 110 | enum Line<'a> { 111 | Start, 112 | PartialName { 113 | start: usize, 114 | }, 115 | Name { 116 | name: &'a str, 117 | }, 118 | Struct { 119 | name: &'a str, 120 | remainder: &'static str, 121 | }, 122 | VersionSelector { 123 | name: &'a str, 124 | start: usize, 125 | first: bool, 126 | }, 127 | Complete { 128 | name: &'a str, 129 | start: usize, 130 | end: usize, 131 | version: &'a str, 132 | }, 133 | Partial { 134 | name: &'a str, 135 | start: usize, 136 | version: &'a str, 137 | }, 138 | } 139 | 140 | impl<'a> Line<'a> { 141 | pub fn parse(line: &'a str, line_no: usize) -> Option { 142 | use Line::*; 143 | let mut state = Start; 144 | 145 | for (i, c) in line.chars().enumerate() { 146 | state = match state { 147 | Complete { .. } | Partial { .. } => break, 148 | Start => { 149 | if c.is_alphabetic() { 150 | PartialName { start: i } 151 | } else { 152 | return None; 153 | } 154 | } 155 | PartialName { start } => match c { 156 | '-' | '_' => state, 157 | c if c.is_alphanumeric() => state, 158 | _ => Name { 159 | name: &line[start..i], 160 | }, 161 | }, 162 | Name { name } => match c { 163 | '{' => Struct { 164 | name, 165 | remainder: "version", 166 | }, 167 | '"' => VersionSelector { 168 | name, 169 | start: i + 1, 170 | first: true, 171 | }, 172 | _ => Name { name }, 173 | }, 174 | Struct { name, remainder } => { 175 | if remainder.is_empty() { 176 | if c == '"' { 177 | VersionSelector { 178 | name, 179 | start: i + 1, 180 | first: true, 181 | } 182 | } else { 183 | Struct { name, remainder } 184 | } 185 | } else if let Some(remainder) = remainder.strip_prefix(c) { 186 | Struct { name, remainder } 187 | } else { 188 | Struct { 189 | name, 190 | remainder: "version", 191 | } 192 | } 193 | } 194 | VersionSelector { name, start, first } => match c { 195 | '"' => Complete { 196 | name, 197 | start, 198 | end: i, 199 | version: &line[start..i], 200 | }, 201 | 'a'..='z' | 'A'..='Z' => { 202 | if first { 203 | Partial { 204 | name, 205 | start, 206 | version: &line[start..i], 207 | } 208 | } else { 209 | VersionSelector { 210 | name, 211 | start, 212 | first: false, 213 | } 214 | } 215 | } 216 | '0'..='9' | '.' | '*' | '_' | '-' | '<' | '>' | '=' | ',' => VersionSelector { 217 | name, 218 | start, 219 | first: false, 220 | }, 221 | ' ' => VersionSelector { 222 | name, 223 | start, 224 | first: true, 225 | }, 226 | _ => Partial { 227 | name, 228 | start, 229 | version: &line[start..i], 230 | }, 231 | }, 232 | }; 233 | } 234 | 235 | match state { 236 | Complete { 237 | name, 238 | version, 239 | start, 240 | end, 241 | } => { 242 | let version = version.trim(); 243 | let version = if let Ok(version) = VersionReq::parse(version) { 244 | DependencyVersion::Complete { 245 | version, 246 | range: Range::new( 247 | Position::new(0, start as u32), 248 | Position::new(0, end as u32), 249 | ), 250 | } 251 | } else { 252 | DependencyVersion::Partial { 253 | version: version.to_string(), 254 | range: Range::new( 255 | Position::new(0, start as u32), 256 | Position::new(0, end as u32), 257 | ), 258 | } 259 | }; 260 | Some(Dependency::WithVersion(DependencyWithVersion { 261 | name: name.to_string(), 262 | version, 263 | })) 264 | } 265 | Partial { 266 | name, 267 | version, 268 | start, 269 | } => { 270 | let version = DependencyVersion::Partial { 271 | version: version.trim().trim_matches(',').to_string(), 272 | range: Range::new( 273 | Position::new(0, start as u32), 274 | Position::new(0, line.len() as u32), 275 | ), 276 | }; 277 | Some(Dependency::WithVersion(DependencyWithVersion { 278 | name: name.to_string(), 279 | version, 280 | })) 281 | } 282 | Name { name, .. } | Struct { name, .. } => Some(Dependency::Other { 283 | name: name.to_string(), 284 | }), 285 | VersionSelector { name, start, .. } => { 286 | Some(Dependency::WithVersion(DependencyWithVersion { 287 | name: name.to_string(), 288 | version: DependencyVersion::Partial { 289 | version: line[start..].trim().to_string(), 290 | range: Range::new( 291 | Position::new(0, start as u32), 292 | Position::new(0, line.len() as u32), 293 | ), 294 | }, 295 | })) 296 | } 297 | PartialName { start } => Some(Dependency::Partial { 298 | name: line[start..].to_string(), 299 | line: line_no as u32, 300 | }), 301 | Start => None, 302 | } 303 | } 304 | } 305 | 306 | #[derive(Default, Debug, Clone)] 307 | pub struct ManifestTracker { 308 | manifests: Arc>>>, 309 | } 310 | 311 | impl ManifestTracker { 312 | pub async fn update_from_source(&self, url: Url, source: &str) -> Vec { 313 | use DocumentState::*; 314 | let mut packages = Vec::new(); 315 | 316 | // We use this to keep track of our current context within the document, 317 | // since we only want to act on dependencies in actual dependency sections, 318 | // and not pick up `version = "1.2.3"` as a dependency on a "version" crate 319 | // in the middle of the package section. 320 | let mut document = DocumentState::Other; 321 | 322 | for (i, line) in source.lines().enumerate() { 323 | let line = line.trim(); 324 | 325 | if line.is_empty() { 326 | continue; 327 | } 328 | 329 | // Detect start of new section. 330 | if line.starts_with('[') { 331 | if line.starts_with("[dependencies") { 332 | if let Some(package) = line.strip_prefix("[dependencies.") { 333 | // This is the case where a dependency is specified over multiple lines, for example: 334 | // 335 | // ```toml 336 | // [dependencies.serde] 337 | // version = "1.0.108" 338 | // ``` 339 | document = 340 | DocumentState::Dependency(package.trim_end_matches(']').to_string()); 341 | } else { 342 | // This is just a plain old [dependencies] section 343 | document = DocumentState::Dependencies; 344 | } 345 | } else if line.ends_with("dependencies]") { 346 | // Covers [build-dependencies], [dev-dependencies], [target.'cfg(unix)'.dependencies], etc. 347 | // Crucially does *not* break specifying packages ending in "dependencies" in the verbose way 348 | // since that case is covered by the previous if-branch matching on '[dependencies': 349 | // 350 | // ```toml 351 | // [dependencies.crate-ending-in-dependencies] 352 | // version = "1" 353 | // ``` 354 | document = DocumentState::Dependencies; 355 | } else { 356 | document = DocumentState::Other; 357 | } 358 | 359 | // Section starts cannot contain version information, so skip the rest of the loop. 360 | continue; 361 | } 362 | 363 | match document { 364 | Dependencies => { 365 | // If we're in a generic dependency section, and find a line 366 | // which can be parsed as a versioned dependency, push it as a package. 367 | if let Some(mut dependency) = Line::parse(line, i) { 368 | // Line::parse assumes line 0, modify so we have to fix this manually. 369 | if let Some(version) = dependency.version_mut() { 370 | version.range_mut().start.line = i as u32; 371 | version.range_mut().end.line = i as u32; 372 | } 373 | packages.push(dependency) 374 | } 375 | } 376 | Dependency(ref name) => { 377 | // We parse the line as a regular dependency, and check if the dependency name is "version" 378 | // This is a hack, but it means we don't have to write custom parsing code for sections like this: 379 | 380 | // ```toml 381 | // [dependencies.serde] 382 | // version = "1" 383 | // ``` 384 | if let Some(mut dependency) = Line::parse(line, i) { 385 | if dependency 386 | .name() 387 | .map(|x| x != "version") 388 | .unwrap_or_default() 389 | { 390 | continue; 391 | } else { 392 | // Rename to the package section, since the dependency is currently 393 | // named "version" because of the Line::parse logic assuming this is 394 | // a regular dependencies section. 395 | if let Some(x) = dependency.name_mut() { 396 | x.clone_from(name) 397 | } 398 | } 399 | // Line::parse assumes line 0, modify so we have to fix this manually. 400 | if let Some(version) = dependency.version_mut() { 401 | version.range_mut().start.line = i as u32; 402 | version.range_mut().end.line = i as u32; 403 | } 404 | packages.push(dependency) 405 | } 406 | } 407 | // We're either at the start of the document, or in an irrelevant section 408 | // such as [package], do nothing. 409 | Other => (), 410 | }; 411 | } 412 | 413 | let mut lock = self.manifests.write().await; 414 | lock.insert(url, packages.clone()); 415 | 416 | packages 417 | } 418 | 419 | pub async fn get(&self, url: &Url) -> Option> { 420 | let dependencies = { 421 | let lock = self.manifests.read().await; 422 | lock.get(url).cloned() 423 | }; 424 | 425 | dependencies 426 | } 427 | } 428 | 429 | #[cfg(test)] 430 | mod tests { 431 | use indoc::indoc; 432 | use semver::VersionReq; 433 | use tower_lsp::lsp_types::Position; 434 | use tower_lsp::lsp_types::Range; 435 | use tower_lsp::lsp_types::Url; 436 | 437 | use crate::parse::DependencyVersion; 438 | use crate::parse::Line; 439 | use crate::parse::ManifestTracker; 440 | use crate::parse::{Dependency, DependencyWithVersion}; 441 | 442 | #[tokio::test] 443 | async fn detect_plain_version() { 444 | let url = Url::parse("file:///test").unwrap(); 445 | 446 | let cargo = indoc! {r#" 447 | [dependencies] 448 | complete_simple_major = "1" 449 | complete_simple_minor = "1.2" 450 | complete_simple_patch = "1.2.3" 451 | complete_simple_range = ">=1, <2" 452 | complete = { version = "1.2.3" } 453 | partial_simple = "1.2 454 | partial_simple_pre = "1.2.0-alpha.1" 455 | partial_struct1 = { version = "1.20 } 456 | partial_struct2 = { version = "1.20 457 | partial_struct3 = { version = "1.20 features = ["serde"] } 458 | partial_struct4 = { version = "1.20 feature } 459 | partial_struct5 = { version = "1.20, features = } 460 | "#}; 461 | 462 | let manifests = ManifestTracker::default(); 463 | manifests.update_from_source(url.clone(), cargo).await; 464 | 465 | for dependency in manifests.get(&url).await.unwrap() { 466 | println!("{dependency}"); 467 | } 468 | } 469 | 470 | fn matches_complete(line: &str, name: &str, version: &str) { 471 | let line = Line::parse(line, 0).unwrap(); 472 | let Dependency::WithVersion(line) = line else { 473 | panic!("expected complete version selector") 474 | }; 475 | let expected_version = VersionReq::parse(version).unwrap(); 476 | 477 | assert_eq!(line.name, name); 478 | 479 | match line.version { 480 | DependencyVersion::Partial { .. } => panic!("expected complete version selector"), 481 | DependencyVersion::Complete { version, .. } => { 482 | assert_eq!(version, expected_version) 483 | } 484 | } 485 | } 486 | 487 | fn matches_partial(line: &str, name: &str, expected_version: &str) { 488 | let line = Line::parse(line, 0).unwrap(); 489 | let Dependency::WithVersion(line) = line else { 490 | panic!("expected complete version selector") 491 | }; 492 | assert_eq!(line.name, name); 493 | 494 | match line.version { 495 | DependencyVersion::Complete { .. } => panic!("expected partial version selector"), 496 | DependencyVersion::Partial { version, .. } => { 497 | assert_eq!(version.as_str(), expected_version) 498 | } 499 | } 500 | } 501 | 502 | #[test] 503 | fn parse_complete() { 504 | matches_complete("complete = \"1.2.3\"", "complete", "1.2.3"); 505 | matches_complete("complete = \"=1.2.3\"", "complete", "=1.2.3"); 506 | matches_complete("complete = \"1.2\"", "complete", "1.2"); 507 | matches_complete("complete = \"=1.2\"", "complete", "=1.2"); 508 | matches_complete("complete = \"1\"", "complete", "1"); 509 | matches_complete("complete = \"=1\"", "complete", "=1"); 510 | } 511 | 512 | #[test] 513 | fn parse_complete_version_field() { 514 | matches_complete("complete = { version = \"1.2.3\" }", "complete", "1.2.3"); 515 | matches_complete("complete = { version = \"=1.2.3\" }", "complete", "=1.2.3"); 516 | matches_complete("complete = { version = \"1.2\" }", "complete", "1.2"); 517 | matches_complete("complete = { version = \"=1.2\" }", "complete", "=1.2"); 518 | matches_complete("complete = { version = \"1\" }", "complete", "1"); 519 | matches_complete("complete = { version = \"=1\" }", "complete", "=1"); 520 | } 521 | 522 | #[test] 523 | fn parse_partial() { 524 | matches_partial("partial = \"1.2.3", "partial", "1.2.3"); 525 | matches_partial("partial = \"1.2.", "partial", "1.2."); 526 | matches_partial("partial = \"1.2", "partial", "1.2"); 527 | matches_partial("partial \"1.", "partial", "1."); 528 | matches_partial("partial \"1", "partial", "1"); 529 | 530 | matches_partial("partial \"1.2.3, features = [", "partial", "1.2.3"); 531 | matches_partial("partial \"1.2., features = [", "partial", "1.2."); 532 | matches_partial("partial \"1.2, features = [", "partial", "1.2"); 533 | matches_partial("partial \"1., features = [", "partial", "1."); 534 | matches_partial("partial \"1, features = [", "partial", "1"); 535 | } 536 | 537 | #[tokio::test] 538 | async fn parse_independent_dependency_section() { 539 | let url = Url::parse("file:///test").unwrap(); 540 | 541 | let cargo = indoc! {r#" 542 | [dependencies] 543 | log = "1" 544 | 545 | [dependencies.serde] 546 | version = "1" 547 | 548 | [dependencies.tokio] 549 | version = "1" 550 | "#}; 551 | 552 | let manifests = ManifestTracker::default(); 553 | manifests.update_from_source(url.clone(), cargo).await; 554 | 555 | assert_eq!( 556 | manifests.get(&url).await.unwrap(), 557 | vec![ 558 | Dependency::WithVersion(DependencyWithVersion { 559 | name: "log".to_string(), 560 | version: DependencyVersion::Complete { 561 | range: Range { 562 | start: Position::new(1, 7), 563 | end: Position::new(1, 8) 564 | }, 565 | version: VersionReq::parse("1").unwrap() 566 | } 567 | }), 568 | Dependency::WithVersion(DependencyWithVersion { 569 | name: "serde".to_string(), 570 | version: DependencyVersion::Complete { 571 | range: Range { 572 | start: Position::new(4, 11), 573 | end: Position::new(4, 12) 574 | }, 575 | version: VersionReq::parse("1").unwrap() 576 | } 577 | }), 578 | Dependency::WithVersion(DependencyWithVersion { 579 | name: "tokio".to_string(), 580 | version: DependencyVersion::Complete { 581 | range: Range { 582 | start: Position::new(7, 11), 583 | end: Position::new(7, 12) 584 | }, 585 | version: VersionReq::parse("1").unwrap() 586 | } 587 | }) 588 | ] 589 | ); 590 | } 591 | } 592 | -------------------------------------------------------------------------------- /src/settings.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use serde::Deserialize; 4 | use tokio::sync::RwLock; 5 | use tower_lsp::lsp_types::DiagnosticSeverity; 6 | 7 | #[derive(Default, Debug, Clone)] 8 | pub struct Settings { 9 | inner: Arc>, 10 | } 11 | 12 | impl Settings { 13 | pub async fn populate_from(&self, value: serde_json::Value) { 14 | if let Ok(new_settings) = serde_json::from_value(value) { 15 | let mut internal_settings = self.inner.write().await; 16 | *internal_settings = new_settings; 17 | } 18 | } 19 | 20 | pub async fn use_api(&self) -> bool { 21 | self.inner.read().await.lsp.use_api.unwrap_or_default() 22 | } 23 | 24 | pub async fn inlay_hints(&self) -> bool { 25 | self.inner.read().await.lsp.inlay_hints.unwrap_or(true) 26 | } 27 | 28 | pub async fn diagnostics(&self) -> bool { 29 | self.inner.read().await.lsp.diagnostics.unwrap_or(true) 30 | } 31 | 32 | pub async fn needs_update_severity(&self) -> DiagnosticSeverity { 33 | self.inner 34 | .read() 35 | .await 36 | .lsp 37 | .needs_update_severity 38 | .filter(verify_severity) 39 | .unwrap_or(DiagnosticSeverity::INFORMATION) 40 | } 41 | 42 | pub async fn up_to_date_severity(&self) -> DiagnosticSeverity { 43 | self.inner 44 | .read() 45 | .await 46 | .lsp 47 | .up_to_date_severity 48 | .filter(verify_severity) 49 | .unwrap_or(DiagnosticSeverity::HINT) 50 | } 51 | 52 | pub async fn unknown_dep_severity(&self) -> DiagnosticSeverity { 53 | self.inner 54 | .read() 55 | .await 56 | .lsp 57 | .unknown_dep_severity 58 | .filter(verify_severity) 59 | .unwrap_or(DiagnosticSeverity::WARNING) 60 | } 61 | 62 | pub async fn up_to_date_hint(&self) -> String { 63 | self.inner 64 | .read() 65 | .await 66 | .lsp 67 | .up_to_date_hint 68 | .clone() 69 | .unwrap_or_else(|| "✓".to_string()) 70 | } 71 | 72 | pub async fn needs_update_hint(&self) -> String { 73 | self.inner 74 | .read() 75 | .await 76 | .lsp 77 | .needs_update_hint 78 | .clone() 79 | .unwrap_or_else(|| " {}".to_string()) 80 | } 81 | } 82 | 83 | // verify the config is a valid severity level 84 | fn verify_severity(d: &DiagnosticSeverity) -> bool { 85 | *d >= DiagnosticSeverity::ERROR && *d <= DiagnosticSeverity::HINT 86 | } 87 | 88 | #[derive(Default, Debug, Clone, Deserialize)] 89 | #[serde(rename_all = "camelCase")] 90 | pub struct LspSettings { 91 | #[serde(default)] 92 | pub use_api: Option, 93 | #[serde(default)] 94 | pub inlay_hints: Option, 95 | #[serde(default)] 96 | pub diagnostics: Option, 97 | #[serde(default)] 98 | pub needs_update_severity: Option, 99 | #[serde(default)] 100 | pub up_to_date_severity: Option, 101 | #[serde(default)] 102 | pub unknown_dep_severity: Option, 103 | #[serde(default)] 104 | pub up_to_date_hint: Option, 105 | #[serde(default)] 106 | pub needs_update_hint: Option, 107 | } 108 | 109 | #[derive(Default, Debug, Clone, Deserialize)] 110 | pub struct InnerSettings { 111 | lsp: LspSettings, 112 | } 113 | --------------------------------------------------------------------------------