├── .envrc ├── .github └── workflows │ └── test.yml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── default.nix ├── flake.lock ├── flake.nix ├── nix ├── sources.json └── sources.nix ├── playground ├── imported-set.nix └── main.nix ├── shell.nix └── src ├── completion.rs ├── lookup.rs ├── main.rs └── utils.rs /.envrc: -------------------------------------------------------------------------------- 1 | use nix 2 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: "Test" 2 | on: 3 | pull_request: 4 | push: 5 | jobs: 6 | tests: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v2 10 | - uses: cachix/install-nix-action@v8 11 | - run: nix build -f . -I nixpkgs=https://github.com/NixOS/nixpkgs-channels/archive/nixpkgs-unstable.tar.gz 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | 4 | result 5 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "aho-corasick" 5 | version = "0.7.13" 6 | source = "registry+https://github.com/rust-lang/crates.io-index" 7 | dependencies = [ 8 | "memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 9 | ] 10 | 11 | [[package]] 12 | name = "ansi_term" 13 | version = "0.11.0" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | dependencies = [ 16 | "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", 17 | ] 18 | 19 | [[package]] 20 | name = "anyhow" 21 | version = "1.0.32" 22 | source = "registry+https://github.com/rust-lang/crates.io-index" 23 | 24 | [[package]] 25 | name = "arrayref" 26 | version = "0.3.6" 27 | source = "registry+https://github.com/rust-lang/crates.io-index" 28 | 29 | [[package]] 30 | name = "arrayvec" 31 | version = "0.5.1" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | 34 | [[package]] 35 | name = "atty" 36 | version = "0.2.14" 37 | source = "registry+https://github.com/rust-lang/crates.io-index" 38 | dependencies = [ 39 | "hermit-abi 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", 40 | "libc 0.2.76 (registry+https://github.com/rust-lang/crates.io-index)", 41 | "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", 42 | ] 43 | 44 | [[package]] 45 | name = "autocfg" 46 | version = "1.0.1" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | 49 | [[package]] 50 | name = "base64" 51 | version = "0.12.3" 52 | source = "registry+https://github.com/rust-lang/crates.io-index" 53 | 54 | [[package]] 55 | name = "bincode" 56 | version = "1.3.1" 57 | source = "registry+https://github.com/rust-lang/crates.io-index" 58 | dependencies = [ 59 | "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", 60 | "serde 1.0.115 (registry+https://github.com/rust-lang/crates.io-index)", 61 | ] 62 | 63 | [[package]] 64 | name = "bitflags" 65 | version = "1.2.1" 66 | source = "registry+https://github.com/rust-lang/crates.io-index" 67 | 68 | [[package]] 69 | name = "blake2b_simd" 70 | version = "0.5.10" 71 | source = "registry+https://github.com/rust-lang/crates.io-index" 72 | dependencies = [ 73 | "arrayref 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", 74 | "arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", 75 | "constant_time_eq 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 76 | ] 77 | 78 | [[package]] 79 | name = "bstr" 80 | version = "0.2.13" 81 | source = "registry+https://github.com/rust-lang/crates.io-index" 82 | dependencies = [ 83 | "memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 84 | ] 85 | 86 | [[package]] 87 | name = "byteorder" 88 | version = "1.3.4" 89 | source = "registry+https://github.com/rust-lang/crates.io-index" 90 | 91 | [[package]] 92 | name = "cbitset" 93 | version = "0.2.0" 94 | source = "registry+https://github.com/rust-lang/crates.io-index" 95 | dependencies = [ 96 | "num-traits 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", 97 | ] 98 | 99 | [[package]] 100 | name = "cfg-if" 101 | version = "0.1.10" 102 | source = "registry+https://github.com/rust-lang/crates.io-index" 103 | 104 | [[package]] 105 | name = "clap" 106 | version = "2.33.3" 107 | source = "registry+https://github.com/rust-lang/crates.io-index" 108 | dependencies = [ 109 | "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", 110 | "atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", 111 | "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 112 | "strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", 113 | "textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", 114 | "unicode-width 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", 115 | "vec_map 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", 116 | ] 117 | 118 | [[package]] 119 | name = "colored" 120 | version = "2.0.0" 121 | source = "registry+https://github.com/rust-lang/crates.io-index" 122 | dependencies = [ 123 | "atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", 124 | "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 125 | "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", 126 | ] 127 | 128 | [[package]] 129 | name = "constant_time_eq" 130 | version = "0.1.5" 131 | source = "registry+https://github.com/rust-lang/crates.io-index" 132 | 133 | [[package]] 134 | name = "crc32fast" 135 | version = "1.2.0" 136 | source = "registry+https://github.com/rust-lang/crates.io-index" 137 | dependencies = [ 138 | "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", 139 | ] 140 | 141 | [[package]] 142 | name = "crossbeam-channel" 143 | version = "0.3.9" 144 | source = "registry+https://github.com/rust-lang/crates.io-index" 145 | dependencies = [ 146 | "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", 147 | ] 148 | 149 | [[package]] 150 | name = "crossbeam-channel" 151 | version = "0.4.3" 152 | source = "registry+https://github.com/rust-lang/crates.io-index" 153 | dependencies = [ 154 | "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", 155 | "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", 156 | ] 157 | 158 | [[package]] 159 | name = "crossbeam-deque" 160 | version = "0.7.3" 161 | source = "registry+https://github.com/rust-lang/crates.io-index" 162 | dependencies = [ 163 | "crossbeam-epoch 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", 164 | "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", 165 | "maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 166 | ] 167 | 168 | [[package]] 169 | name = "crossbeam-epoch" 170 | version = "0.8.2" 171 | source = "registry+https://github.com/rust-lang/crates.io-index" 172 | dependencies = [ 173 | "autocfg 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", 174 | "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", 175 | "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", 176 | "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 177 | "maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 178 | "memoffset 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", 179 | "scopeguard 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 180 | ] 181 | 182 | [[package]] 183 | name = "crossbeam-utils" 184 | version = "0.6.6" 185 | source = "registry+https://github.com/rust-lang/crates.io-index" 186 | dependencies = [ 187 | "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", 188 | "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 189 | ] 190 | 191 | [[package]] 192 | name = "crossbeam-utils" 193 | version = "0.7.2" 194 | source = "registry+https://github.com/rust-lang/crates.io-index" 195 | dependencies = [ 196 | "autocfg 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", 197 | "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", 198 | "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 199 | ] 200 | 201 | [[package]] 202 | name = "dirs" 203 | version = "2.0.2" 204 | source = "registry+https://github.com/rust-lang/crates.io-index" 205 | dependencies = [ 206 | "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", 207 | "dirs-sys 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", 208 | ] 209 | 210 | [[package]] 211 | name = "dirs-sys" 212 | version = "0.3.5" 213 | source = "registry+https://github.com/rust-lang/crates.io-index" 214 | dependencies = [ 215 | "libc 0.2.76 (registry+https://github.com/rust-lang/crates.io-index)", 216 | "redox_users 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", 217 | "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", 218 | ] 219 | 220 | [[package]] 221 | name = "either" 222 | version = "1.6.0" 223 | source = "registry+https://github.com/rust-lang/crates.io-index" 224 | 225 | [[package]] 226 | name = "env_logger" 227 | version = "0.7.1" 228 | source = "registry+https://github.com/rust-lang/crates.io-index" 229 | dependencies = [ 230 | "atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", 231 | "humantime 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", 232 | "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", 233 | "regex 1.3.9 (registry+https://github.com/rust-lang/crates.io-index)", 234 | "termcolor 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 235 | ] 236 | 237 | [[package]] 238 | name = "fnv" 239 | version = "1.0.7" 240 | source = "registry+https://github.com/rust-lang/crates.io-index" 241 | 242 | [[package]] 243 | name = "getrandom" 244 | version = "0.1.14" 245 | source = "registry+https://github.com/rust-lang/crates.io-index" 246 | dependencies = [ 247 | "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", 248 | "libc 0.2.76 (registry+https://github.com/rust-lang/crates.io-index)", 249 | "wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)", 250 | ] 251 | 252 | [[package]] 253 | name = "globset" 254 | version = "0.4.5" 255 | source = "registry+https://github.com/rust-lang/crates.io-index" 256 | dependencies = [ 257 | "aho-corasick 0.7.13 (registry+https://github.com/rust-lang/crates.io-index)", 258 | "bstr 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", 259 | "fnv 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", 260 | "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", 261 | "regex 1.3.9 (registry+https://github.com/rust-lang/crates.io-index)", 262 | ] 263 | 264 | [[package]] 265 | name = "heck" 266 | version = "0.3.1" 267 | source = "registry+https://github.com/rust-lang/crates.io-index" 268 | dependencies = [ 269 | "unicode-segmentation 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", 270 | ] 271 | 272 | [[package]] 273 | name = "hermit-abi" 274 | version = "0.1.15" 275 | source = "registry+https://github.com/rust-lang/crates.io-index" 276 | dependencies = [ 277 | "libc 0.2.76 (registry+https://github.com/rust-lang/crates.io-index)", 278 | ] 279 | 280 | [[package]] 281 | name = "humantime" 282 | version = "1.3.0" 283 | source = "registry+https://github.com/rust-lang/crates.io-index" 284 | dependencies = [ 285 | "quick-error 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", 286 | ] 287 | 288 | [[package]] 289 | name = "idna" 290 | version = "0.2.0" 291 | source = "registry+https://github.com/rust-lang/crates.io-index" 292 | dependencies = [ 293 | "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", 294 | "unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", 295 | "unicode-normalization 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", 296 | ] 297 | 298 | [[package]] 299 | name = "ignore" 300 | version = "0.4.16" 301 | source = "registry+https://github.com/rust-lang/crates.io-index" 302 | dependencies = [ 303 | "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", 304 | "globset 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", 305 | "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 306 | "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", 307 | "memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 308 | "regex 1.3.9 (registry+https://github.com/rust-lang/crates.io-index)", 309 | "same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", 310 | "thread_local 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", 311 | "walkdir 2.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 312 | "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 313 | ] 314 | 315 | [[package]] 316 | name = "itertools" 317 | version = "0.9.0" 318 | source = "registry+https://github.com/rust-lang/crates.io-index" 319 | dependencies = [ 320 | "either 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", 321 | ] 322 | 323 | [[package]] 324 | name = "itoa" 325 | version = "0.4.6" 326 | source = "registry+https://github.com/rust-lang/crates.io-index" 327 | 328 | [[package]] 329 | name = "lazy_static" 330 | version = "1.4.0" 331 | source = "registry+https://github.com/rust-lang/crates.io-index" 332 | 333 | [[package]] 334 | name = "libc" 335 | version = "0.2.76" 336 | source = "registry+https://github.com/rust-lang/crates.io-index" 337 | 338 | [[package]] 339 | name = "log" 340 | version = "0.4.11" 341 | source = "registry+https://github.com/rust-lang/crates.io-index" 342 | dependencies = [ 343 | "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", 344 | ] 345 | 346 | [[package]] 347 | name = "lsp-server" 348 | version = "0.3.4" 349 | source = "registry+https://github.com/rust-lang/crates.io-index" 350 | dependencies = [ 351 | "crossbeam-channel 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", 352 | "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", 353 | "serde 1.0.115 (registry+https://github.com/rust-lang/crates.io-index)", 354 | "serde_json 1.0.57 (registry+https://github.com/rust-lang/crates.io-index)", 355 | ] 356 | 357 | [[package]] 358 | name = "lsp-types" 359 | version = "0.79.0" 360 | source = "registry+https://github.com/rust-lang/crates.io-index" 361 | dependencies = [ 362 | "base64 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", 363 | "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 364 | "serde 1.0.115 (registry+https://github.com/rust-lang/crates.io-index)", 365 | "serde_json 1.0.57 (registry+https://github.com/rust-lang/crates.io-index)", 366 | "serde_repr 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", 367 | "url 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 368 | ] 369 | 370 | [[package]] 371 | name = "manix" 372 | version = "0.5.1" 373 | source = "registry+https://github.com/rust-lang/crates.io-index" 374 | dependencies = [ 375 | "anyhow 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)", 376 | "bincode 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 377 | "colored 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 378 | "crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 379 | "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 380 | "rayon 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 381 | "rnix 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", 382 | "roxmltree 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", 383 | "serde 1.0.115 (registry+https://github.com/rust-lang/crates.io-index)", 384 | "serde_json 1.0.57 (registry+https://github.com/rust-lang/crates.io-index)", 385 | "structopt 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)", 386 | "thiserror 1.0.20 (registry+https://github.com/rust-lang/crates.io-index)", 387 | "walkdir 2.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 388 | "xdg 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 389 | ] 390 | 391 | [[package]] 392 | name = "matches" 393 | version = "0.1.8" 394 | source = "registry+https://github.com/rust-lang/crates.io-index" 395 | 396 | [[package]] 397 | name = "maybe-uninit" 398 | version = "2.0.0" 399 | source = "registry+https://github.com/rust-lang/crates.io-index" 400 | 401 | [[package]] 402 | name = "memchr" 403 | version = "2.3.3" 404 | source = "registry+https://github.com/rust-lang/crates.io-index" 405 | 406 | [[package]] 407 | name = "memoffset" 408 | version = "0.5.5" 409 | source = "registry+https://github.com/rust-lang/crates.io-index" 410 | dependencies = [ 411 | "autocfg 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", 412 | ] 413 | 414 | [[package]] 415 | name = "nixpkgs-fmt" 416 | version = "0.9.0" 417 | source = "registry+https://github.com/rust-lang/crates.io-index" 418 | dependencies = [ 419 | "clap 2.33.3 (registry+https://github.com/rust-lang/crates.io-index)", 420 | "crossbeam-channel 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", 421 | "ignore 0.4.16 (registry+https://github.com/rust-lang/crates.io-index)", 422 | "rnix 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", 423 | "rowan 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", 424 | "serde_json 1.0.57 (registry+https://github.com/rust-lang/crates.io-index)", 425 | ] 426 | 427 | [[package]] 428 | name = "num-traits" 429 | version = "0.2.12" 430 | source = "registry+https://github.com/rust-lang/crates.io-index" 431 | dependencies = [ 432 | "autocfg 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", 433 | ] 434 | 435 | [[package]] 436 | name = "num_cpus" 437 | version = "1.13.0" 438 | source = "registry+https://github.com/rust-lang/crates.io-index" 439 | dependencies = [ 440 | "hermit-abi 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", 441 | "libc 0.2.76 (registry+https://github.com/rust-lang/crates.io-index)", 442 | ] 443 | 444 | [[package]] 445 | name = "percent-encoding" 446 | version = "2.1.0" 447 | source = "registry+https://github.com/rust-lang/crates.io-index" 448 | 449 | [[package]] 450 | name = "proc-macro-error" 451 | version = "1.0.4" 452 | source = "registry+https://github.com/rust-lang/crates.io-index" 453 | dependencies = [ 454 | "proc-macro-error-attr 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", 455 | "proc-macro2 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)", 456 | "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", 457 | "syn 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", 458 | "version_check 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", 459 | ] 460 | 461 | [[package]] 462 | name = "proc-macro-error-attr" 463 | version = "1.0.4" 464 | source = "registry+https://github.com/rust-lang/crates.io-index" 465 | dependencies = [ 466 | "proc-macro2 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)", 467 | "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", 468 | "version_check 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", 469 | ] 470 | 471 | [[package]] 472 | name = "proc-macro2" 473 | version = "1.0.19" 474 | source = "registry+https://github.com/rust-lang/crates.io-index" 475 | dependencies = [ 476 | "unicode-xid 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 477 | ] 478 | 479 | [[package]] 480 | name = "quick-error" 481 | version = "1.2.3" 482 | source = "registry+https://github.com/rust-lang/crates.io-index" 483 | 484 | [[package]] 485 | name = "quote" 486 | version = "1.0.7" 487 | source = "registry+https://github.com/rust-lang/crates.io-index" 488 | dependencies = [ 489 | "proc-macro2 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)", 490 | ] 491 | 492 | [[package]] 493 | name = "rayon" 494 | version = "1.4.0" 495 | source = "registry+https://github.com/rust-lang/crates.io-index" 496 | dependencies = [ 497 | "autocfg 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", 498 | "crossbeam-deque 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", 499 | "either 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", 500 | "rayon-core 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", 501 | ] 502 | 503 | [[package]] 504 | name = "rayon-core" 505 | version = "1.8.0" 506 | source = "registry+https://github.com/rust-lang/crates.io-index" 507 | dependencies = [ 508 | "crossbeam-channel 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", 509 | "crossbeam-deque 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", 510 | "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", 511 | "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 512 | "num_cpus 1.13.0 (registry+https://github.com/rust-lang/crates.io-index)", 513 | ] 514 | 515 | [[package]] 516 | name = "redox_syscall" 517 | version = "0.1.57" 518 | source = "registry+https://github.com/rust-lang/crates.io-index" 519 | 520 | [[package]] 521 | name = "redox_users" 522 | version = "0.3.5" 523 | source = "registry+https://github.com/rust-lang/crates.io-index" 524 | dependencies = [ 525 | "getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", 526 | "redox_syscall 0.1.57 (registry+https://github.com/rust-lang/crates.io-index)", 527 | "rust-argon2 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", 528 | ] 529 | 530 | [[package]] 531 | name = "regex" 532 | version = "1.3.9" 533 | source = "registry+https://github.com/rust-lang/crates.io-index" 534 | dependencies = [ 535 | "aho-corasick 0.7.13 (registry+https://github.com/rust-lang/crates.io-index)", 536 | "memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 537 | "regex-syntax 0.6.18 (registry+https://github.com/rust-lang/crates.io-index)", 538 | "thread_local 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", 539 | ] 540 | 541 | [[package]] 542 | name = "regex-syntax" 543 | version = "0.6.18" 544 | source = "registry+https://github.com/rust-lang/crates.io-index" 545 | 546 | [[package]] 547 | name = "rnix" 548 | version = "0.7.2" 549 | source = "registry+https://github.com/rust-lang/crates.io-index" 550 | dependencies = [ 551 | "cbitset 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 552 | "rowan 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", 553 | ] 554 | 555 | [[package]] 556 | name = "rnix" 557 | version = "0.8.0" 558 | source = "registry+https://github.com/rust-lang/crates.io-index" 559 | dependencies = [ 560 | "cbitset 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 561 | "rowan 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", 562 | ] 563 | 564 | [[package]] 565 | name = "rnix-lsp" 566 | version = "0.1.0" 567 | dependencies = [ 568 | "dirs 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 569 | "env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", 570 | "itertools 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", 571 | "libc 0.2.76 (registry+https://github.com/rust-lang/crates.io-index)", 572 | "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", 573 | "lsp-server 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", 574 | "lsp-types 0.79.0 (registry+https://github.com/rust-lang/crates.io-index)", 575 | "manix 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", 576 | "nixpkgs-fmt 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", 577 | "rnix 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", 578 | "serde 1.0.115 (registry+https://github.com/rust-lang/crates.io-index)", 579 | "serde_json 1.0.57 (registry+https://github.com/rust-lang/crates.io-index)", 580 | "xdg 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 581 | ] 582 | 583 | [[package]] 584 | name = "rowan" 585 | version = "0.6.3" 586 | source = "registry+https://github.com/rust-lang/crates.io-index" 587 | dependencies = [ 588 | "rustc-hash 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 589 | "serde 1.0.115 (registry+https://github.com/rust-lang/crates.io-index)", 590 | "smol_str 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", 591 | "text_unit 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", 592 | ] 593 | 594 | [[package]] 595 | name = "rowan" 596 | version = "0.9.1" 597 | source = "registry+https://github.com/rust-lang/crates.io-index" 598 | dependencies = [ 599 | "rustc-hash 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 600 | "smol_str 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", 601 | "text_unit 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", 602 | "thin-dst 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 603 | ] 604 | 605 | [[package]] 606 | name = "roxmltree" 607 | version = "0.13.0" 608 | source = "registry+https://github.com/rust-lang/crates.io-index" 609 | dependencies = [ 610 | "xmlparser 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)", 611 | ] 612 | 613 | [[package]] 614 | name = "rust-argon2" 615 | version = "0.8.2" 616 | source = "registry+https://github.com/rust-lang/crates.io-index" 617 | dependencies = [ 618 | "base64 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", 619 | "blake2b_simd 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)", 620 | "constant_time_eq 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 621 | "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", 622 | ] 623 | 624 | [[package]] 625 | name = "rustc-hash" 626 | version = "1.1.0" 627 | source = "registry+https://github.com/rust-lang/crates.io-index" 628 | 629 | [[package]] 630 | name = "ryu" 631 | version = "1.0.5" 632 | source = "registry+https://github.com/rust-lang/crates.io-index" 633 | 634 | [[package]] 635 | name = "same-file" 636 | version = "1.0.6" 637 | source = "registry+https://github.com/rust-lang/crates.io-index" 638 | dependencies = [ 639 | "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 640 | ] 641 | 642 | [[package]] 643 | name = "scopeguard" 644 | version = "1.1.0" 645 | source = "registry+https://github.com/rust-lang/crates.io-index" 646 | 647 | [[package]] 648 | name = "serde" 649 | version = "1.0.115" 650 | source = "registry+https://github.com/rust-lang/crates.io-index" 651 | dependencies = [ 652 | "serde_derive 1.0.115 (registry+https://github.com/rust-lang/crates.io-index)", 653 | ] 654 | 655 | [[package]] 656 | name = "serde_derive" 657 | version = "1.0.115" 658 | source = "registry+https://github.com/rust-lang/crates.io-index" 659 | dependencies = [ 660 | "proc-macro2 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)", 661 | "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", 662 | "syn 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", 663 | ] 664 | 665 | [[package]] 666 | name = "serde_json" 667 | version = "1.0.57" 668 | source = "registry+https://github.com/rust-lang/crates.io-index" 669 | dependencies = [ 670 | "itoa 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", 671 | "ryu 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", 672 | "serde 1.0.115 (registry+https://github.com/rust-lang/crates.io-index)", 673 | ] 674 | 675 | [[package]] 676 | name = "serde_repr" 677 | version = "0.1.6" 678 | source = "registry+https://github.com/rust-lang/crates.io-index" 679 | dependencies = [ 680 | "proc-macro2 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)", 681 | "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", 682 | "syn 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", 683 | ] 684 | 685 | [[package]] 686 | name = "smol_str" 687 | version = "0.1.16" 688 | source = "registry+https://github.com/rust-lang/crates.io-index" 689 | 690 | [[package]] 691 | name = "strsim" 692 | version = "0.8.0" 693 | source = "registry+https://github.com/rust-lang/crates.io-index" 694 | 695 | [[package]] 696 | name = "structopt" 697 | version = "0.3.17" 698 | source = "registry+https://github.com/rust-lang/crates.io-index" 699 | dependencies = [ 700 | "clap 2.33.3 (registry+https://github.com/rust-lang/crates.io-index)", 701 | "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 702 | "structopt-derive 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", 703 | ] 704 | 705 | [[package]] 706 | name = "structopt-derive" 707 | version = "0.4.10" 708 | source = "registry+https://github.com/rust-lang/crates.io-index" 709 | dependencies = [ 710 | "heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 711 | "proc-macro-error 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", 712 | "proc-macro2 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)", 713 | "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", 714 | "syn 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", 715 | ] 716 | 717 | [[package]] 718 | name = "syn" 719 | version = "1.0.39" 720 | source = "registry+https://github.com/rust-lang/crates.io-index" 721 | dependencies = [ 722 | "proc-macro2 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)", 723 | "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", 724 | "unicode-xid 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 725 | ] 726 | 727 | [[package]] 728 | name = "termcolor" 729 | version = "1.1.0" 730 | source = "registry+https://github.com/rust-lang/crates.io-index" 731 | dependencies = [ 732 | "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 733 | ] 734 | 735 | [[package]] 736 | name = "text_unit" 737 | version = "0.1.10" 738 | source = "registry+https://github.com/rust-lang/crates.io-index" 739 | dependencies = [ 740 | "serde 1.0.115 (registry+https://github.com/rust-lang/crates.io-index)", 741 | ] 742 | 743 | [[package]] 744 | name = "textwrap" 745 | version = "0.11.0" 746 | source = "registry+https://github.com/rust-lang/crates.io-index" 747 | dependencies = [ 748 | "unicode-width 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", 749 | ] 750 | 751 | [[package]] 752 | name = "thin-dst" 753 | version = "1.1.0" 754 | source = "registry+https://github.com/rust-lang/crates.io-index" 755 | 756 | [[package]] 757 | name = "thiserror" 758 | version = "1.0.20" 759 | source = "registry+https://github.com/rust-lang/crates.io-index" 760 | dependencies = [ 761 | "thiserror-impl 1.0.20 (registry+https://github.com/rust-lang/crates.io-index)", 762 | ] 763 | 764 | [[package]] 765 | name = "thiserror-impl" 766 | version = "1.0.20" 767 | source = "registry+https://github.com/rust-lang/crates.io-index" 768 | dependencies = [ 769 | "proc-macro2 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)", 770 | "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", 771 | "syn 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", 772 | ] 773 | 774 | [[package]] 775 | name = "thread_local" 776 | version = "1.0.1" 777 | source = "registry+https://github.com/rust-lang/crates.io-index" 778 | dependencies = [ 779 | "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 780 | ] 781 | 782 | [[package]] 783 | name = "tinyvec" 784 | version = "0.3.4" 785 | source = "registry+https://github.com/rust-lang/crates.io-index" 786 | 787 | [[package]] 788 | name = "unicode-bidi" 789 | version = "0.3.4" 790 | source = "registry+https://github.com/rust-lang/crates.io-index" 791 | dependencies = [ 792 | "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", 793 | ] 794 | 795 | [[package]] 796 | name = "unicode-normalization" 797 | version = "0.1.13" 798 | source = "registry+https://github.com/rust-lang/crates.io-index" 799 | dependencies = [ 800 | "tinyvec 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", 801 | ] 802 | 803 | [[package]] 804 | name = "unicode-segmentation" 805 | version = "1.6.0" 806 | source = "registry+https://github.com/rust-lang/crates.io-index" 807 | 808 | [[package]] 809 | name = "unicode-width" 810 | version = "0.1.8" 811 | source = "registry+https://github.com/rust-lang/crates.io-index" 812 | 813 | [[package]] 814 | name = "unicode-xid" 815 | version = "0.2.1" 816 | source = "registry+https://github.com/rust-lang/crates.io-index" 817 | 818 | [[package]] 819 | name = "url" 820 | version = "2.1.1" 821 | source = "registry+https://github.com/rust-lang/crates.io-index" 822 | dependencies = [ 823 | "idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 824 | "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", 825 | "percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 826 | "serde 1.0.115 (registry+https://github.com/rust-lang/crates.io-index)", 827 | ] 828 | 829 | [[package]] 830 | name = "vec_map" 831 | version = "0.8.2" 832 | source = "registry+https://github.com/rust-lang/crates.io-index" 833 | 834 | [[package]] 835 | name = "version_check" 836 | version = "0.9.2" 837 | source = "registry+https://github.com/rust-lang/crates.io-index" 838 | 839 | [[package]] 840 | name = "walkdir" 841 | version = "2.3.1" 842 | source = "registry+https://github.com/rust-lang/crates.io-index" 843 | dependencies = [ 844 | "same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", 845 | "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", 846 | "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 847 | ] 848 | 849 | [[package]] 850 | name = "wasi" 851 | version = "0.9.0+wasi-snapshot-preview1" 852 | source = "registry+https://github.com/rust-lang/crates.io-index" 853 | 854 | [[package]] 855 | name = "winapi" 856 | version = "0.3.9" 857 | source = "registry+https://github.com/rust-lang/crates.io-index" 858 | dependencies = [ 859 | "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 860 | "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 861 | ] 862 | 863 | [[package]] 864 | name = "winapi-i686-pc-windows-gnu" 865 | version = "0.4.0" 866 | source = "registry+https://github.com/rust-lang/crates.io-index" 867 | 868 | [[package]] 869 | name = "winapi-util" 870 | version = "0.1.5" 871 | source = "registry+https://github.com/rust-lang/crates.io-index" 872 | dependencies = [ 873 | "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", 874 | ] 875 | 876 | [[package]] 877 | name = "winapi-x86_64-pc-windows-gnu" 878 | version = "0.4.0" 879 | source = "registry+https://github.com/rust-lang/crates.io-index" 880 | 881 | [[package]] 882 | name = "xdg" 883 | version = "2.2.0" 884 | source = "registry+https://github.com/rust-lang/crates.io-index" 885 | 886 | [[package]] 887 | name = "xmlparser" 888 | version = "0.13.2" 889 | source = "registry+https://github.com/rust-lang/crates.io-index" 890 | 891 | [metadata] 892 | "checksum aho-corasick 0.7.13 (registry+https://github.com/rust-lang/crates.io-index)" = "043164d8ba5c4c3035fec9bbee8647c0261d788f3474306f93bb65901cae0e86" 893 | "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" 894 | "checksum anyhow 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)" = "6b602bfe940d21c130f3895acd65221e8a61270debe89d628b9cb4e3ccb8569b" 895 | "checksum arrayref 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" 896 | "checksum arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" 897 | "checksum atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 898 | "checksum autocfg 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" 899 | "checksum base64 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" 900 | "checksum bincode 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f30d3a39baa26f9651f17b375061f3233dde33424a8b72b0dbe93a68a0bc896d" 901 | "checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" 902 | "checksum blake2b_simd 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)" = "d8fb2d74254a3a0b5cac33ac9f8ed0e44aa50378d9dbb2e5d83bd21ed1dc2c8a" 903 | "checksum bstr 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "31accafdb70df7871592c058eca3985b71104e15ac32f64706022c58867da931" 904 | "checksum byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" 905 | "checksum cbitset 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "29b6ad25ae296159fb0da12b970b2fe179b234584d7cd294c891e2bbb284466b" 906 | "checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" 907 | "checksum clap 2.33.3 (registry+https://github.com/rust-lang/crates.io-index)" = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" 908 | "checksum colored 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b3616f750b84d8f0de8a58bda93e08e2a81ad3f523089b05f1dffecab48c6cbd" 909 | "checksum constant_time_eq 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" 910 | "checksum crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1" 911 | "checksum crossbeam-channel 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "c8ec7fcd21571dc78f96cc96243cab8d8f035247c3efd16c687be154c3fa9efa" 912 | "checksum crossbeam-channel 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "09ee0cc8804d5393478d743b035099520087a5186f3b93fa58cec08fa62407b6" 913 | "checksum crossbeam-deque 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "9f02af974daeee82218205558e51ec8768b48cf524bd01d550abe5573a608285" 914 | "checksum crossbeam-epoch 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace" 915 | "checksum crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "04973fa96e96579258a5091af6003abde64af786b860f18622b82e026cca60e6" 916 | "checksum crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" 917 | "checksum dirs 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3" 918 | "checksum dirs-sys 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "8e93d7f5705de3e49895a2b5e0b8855a1c27f080192ae9c32a6432d50741a57a" 919 | "checksum either 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cd56b59865bce947ac5958779cfa508f6c3b9497cc762b7e24a12d11ccde2c4f" 920 | "checksum env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" 921 | "checksum fnv 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 922 | "checksum getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" 923 | "checksum globset 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "7ad1da430bd7281dde2576f44c84cc3f0f7b475e7202cd503042dff01a8c8120" 924 | "checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" 925 | "checksum hermit-abi 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "3deed196b6e7f9e44a2ae8d94225d80302d81208b1bb673fd21fe634645c85a9" 926 | "checksum humantime 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" 927 | "checksum idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" 928 | "checksum ignore 0.4.16 (registry+https://github.com/rust-lang/crates.io-index)" = "22dcbf2a4a289528dbef21686354904e1c694ac642610a9bff9e7df730d9ec72" 929 | "checksum itertools 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b" 930 | "checksum itoa 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6" 931 | "checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 932 | "checksum libc 0.2.76 (registry+https://github.com/rust-lang/crates.io-index)" = "755456fae044e6fa1ebbbd1b3e902ae19e73097ed4ed87bb79934a867c007bc3" 933 | "checksum log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" 934 | "checksum lsp-server 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "87fce8851309a325974ec76efe7c9d954d152c9ff4fded6520eb3c96d0aa3a96" 935 | "checksum lsp-types 0.79.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7f1f86677fdbe8df5f88b99131b1424e50aad27bbe3e5900d221bc414bd72e9b" 936 | "checksum manix 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e1c48f1b9d644999daf0ecae4097d67e79b775bf7c025f48c8cd2e810e9f5d1" 937 | "checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" 938 | "checksum maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" 939 | "checksum memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" 940 | "checksum memoffset 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "c198b026e1bbf08a937e94c6c60f9ec4a2267f5b0d2eec9c1b21b061ce2be55f" 941 | "checksum nixpkgs-fmt 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2f6eca38e3ef7e74b62d4ad0c9adc79e122f85163edc69ce5943e1ec325a2989" 942 | "checksum num-traits 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)" = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611" 943 | "checksum num_cpus 1.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" 944 | "checksum percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" 945 | "checksum proc-macro-error 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" 946 | "checksum proc-macro-error-attr 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" 947 | "checksum proc-macro2 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)" = "04f5f085b5d71e2188cb8271e5da0161ad52c3f227a661a3c135fdf28e258b12" 948 | "checksum quick-error 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" 949 | "checksum quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" 950 | "checksum rayon 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cfd016f0c045ad38b5251be2c9c0ab806917f82da4d36b2a327e5166adad9270" 951 | "checksum rayon-core 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "91739a34c4355b5434ce54c9086c5895604a9c278586d1f1aa95e04f66b525a0" 952 | "checksum redox_syscall 0.1.57 (registry+https://github.com/rust-lang/crates.io-index)" = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" 953 | "checksum redox_users 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "de0737333e7a9502c789a36d7c7fa6092a49895d4faa31ca5df163857ded2e9d" 954 | "checksum regex 1.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "9c3780fcf44b193bc4d09f36d2a3c87b251da4a046c87795a0d35f4f927ad8e6" 955 | "checksum regex-syntax 0.6.18 (registry+https://github.com/rust-lang/crates.io-index)" = "26412eb97c6b088a6997e05f69403a802a92d520de2f8e63c2b65f9e0f47c4e8" 956 | "checksum rnix 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2192039d0f383be6e0edb37718152c764509ce800cafe51f58f88b27bf0e4714" 957 | "checksum rnix 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cbbea4c714e5bbf462fa4316ddf45875d8f0e28e5db81050b5f9ce99746c6863" 958 | "checksum rowan 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "fc3a6fb2a35518af7cab43ec4e21ca82eb086a8b3bb1739e426dc3923d459607" 959 | "checksum rowan 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1ea7cadf87a9d8432e85cb4eb86bd2e765ace60c24ef86e79084dcae5d1c5a19" 960 | "checksum roxmltree 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "17dfc6c39f846bfc7d2ec442ad12055d79608d501380789b965d22f9354451f2" 961 | "checksum rust-argon2 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9dab61250775933275e84053ac235621dfb739556d5c54a2f2e9313b7cf43a19" 962 | "checksum rustc-hash 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" 963 | "checksum ryu 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" 964 | "checksum same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" 965 | "checksum scopeguard 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 966 | "checksum serde 1.0.115 (registry+https://github.com/rust-lang/crates.io-index)" = "e54c9a88f2da7238af84b5101443f0c0d0a3bbdc455e34a5c9497b1903ed55d5" 967 | "checksum serde_derive 1.0.115 (registry+https://github.com/rust-lang/crates.io-index)" = "609feed1d0a73cc36a0182a840a9b37b4a82f0b1150369f0536a9e3f2a31dc48" 968 | "checksum serde_json 1.0.57 (registry+https://github.com/rust-lang/crates.io-index)" = "164eacbdb13512ec2745fb09d51fd5b22b0d65ed294a1dcf7285a360c80a675c" 969 | "checksum serde_repr 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2dc6b7951b17b051f3210b063f12cc17320e2fe30ae05b0fe2a3abb068551c76" 970 | "checksum smol_str 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "2f7909a1d8bc166a862124d84fdc11bda0ea4ed3157ccca662296919c2972db1" 971 | "checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" 972 | "checksum structopt 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)" = "6cc388d94ffabf39b5ed5fadddc40147cb21e605f53db6f8f36a625d27489ac5" 973 | "checksum structopt-derive 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "5e2513111825077552a6751dfad9e11ce0fba07d7276a3943a037d7e93e64c5f" 974 | "checksum syn 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)" = "891d8d6567fe7c7f8835a3a98af4208f3846fba258c1bc3c31d6e506239f11f9" 975 | "checksum termcolor 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f" 976 | "checksum text_unit 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "20431e104bfecc1a40872578dbc390e10290a0e9c35fffe3ce6f73c15a9dbfc2" 977 | "checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" 978 | "checksum thin-dst 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "db3c46be180f1af9673ebb27bc1235396f61ef6965b3fe0dbb2e624deb604f0e" 979 | "checksum thiserror 1.0.20 (registry+https://github.com/rust-lang/crates.io-index)" = "7dfdd070ccd8ccb78f4ad66bf1982dc37f620ef696c6b5028fe2ed83dd3d0d08" 980 | "checksum thiserror-impl 1.0.20 (registry+https://github.com/rust-lang/crates.io-index)" = "bd80fc12f73063ac132ac92aceea36734f04a1d93c1240c6944e23a3b8841793" 981 | "checksum thread_local 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" 982 | "checksum tinyvec 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "238ce071d267c5710f9d31451efec16c5ee22de34df17cc05e56cbc92e967117" 983 | "checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" 984 | "checksum unicode-normalization 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6fb19cf769fa8c6a80a162df694621ebeb4dafb606470b2b2fce0be40a98a977" 985 | "checksum unicode-segmentation 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0" 986 | "checksum unicode-width 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" 987 | "checksum unicode-xid 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" 988 | "checksum url 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "829d4a8476c35c9bf0bbce5a3b23f4106f79728039b726d292bb93bc106787cb" 989 | "checksum vec_map 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" 990 | "checksum version_check 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" 991 | "checksum walkdir 2.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d" 992 | "checksum wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)" = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" 993 | "checksum winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 994 | "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 995 | "checksum winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 996 | "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 997 | "checksum xdg 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d089681aa106a86fade1b0128fb5daf07d5867a509ab036d99988dec80429a57" 998 | "checksum xmlparser 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)" = "52613e655f6f11f63c0fe7d1c3b5ef69e44d96df9b65dab296b441ed0e1125f5" 999 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["jD91mZM2 "] 3 | description = "A work-in-progress language server for Nix, with syntax checking and basic completion" 4 | keywords = ["rnix", "nix", "languageserver", "lsp"] 5 | categories = ["text-editors", "parser-implementations", "development-tools::testing"] 6 | readme = "README.md" 7 | license = "MIT" 8 | repository = "https://github.com/nix-community/rnix-lsp" 9 | edition = "2018" 10 | name = "rnix-lsp" 11 | version = "0.1.0" 12 | 13 | [dependencies] 14 | dirs = "2.0.2" 15 | env_logger = "0.7.1" 16 | libc = "0.2.66" 17 | log = "0.4.8" 18 | lsp-server = "0.3.1" 19 | lsp-types = { version = "0.79.0", features = ["proposed"] } 20 | nixpkgs-fmt = "0.9.0" 21 | rnix = "0.7.2" 22 | serde = "1.0.104" 23 | serde_json = "1.0.44" 24 | manix = "0.5.1" 25 | xdg = "2.2" 26 | itertools = "0.9" 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 jD91mZM2 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 | # rnix-lsp ![Crates.io](https://img.shields.io/crates/v/rnix-lsp) 2 | 3 | A syntax-checking language server using 4 | [rnix](https://github.com/nix-community/rnix-parser). 5 | 6 | - [x] Syntax-checking diagnostics 7 | - [x] Basic completion 8 | - [x] Basic renaming 9 | - [x] Basic goto definition 10 | - [x] Expand selection proposal 11 | - [x] Formatting using [nixpkgs-fmt](https://github.com/nix-community/nixpkgs-fmt) 12 | 13 | This is beta-level quality *at best* - I didn't expect maintaining a 14 | language server when writing rnix, the goal was that others would 15 | flock around the parser and write a bunch of editor tooling :) 16 | 17 | Breakages are expected. No semver compatibility before 1.x.y. 18 | 19 | Turn on logging with `RUST_LOG=trace`, and redirect stderr to a file. 20 | 21 | ```sh 22 | bash -c "env RUST_LOG=trace rnix-lsp 2> /tmp/rnix-lsp.log" 23 | ``` 24 | 25 | ## Install 26 | 27 | ``` 28 | $ nix-env -i -f https://github.com/elkowar/rnix-lsp/archive/master.tar.gz 29 | ``` 30 | 31 | ## Integrate with your editor 32 | 33 | These instructions are not fully tested - see issue #3. Please raise 34 | an issue and/or send a PR if a config below didn't work out of the box. 35 | 36 | ### Vim/Neovim 37 | 38 | #### [coc.nvim](https://github.com/neoclide/coc.nvim) 39 | 40 | ```vim 41 | { 42 | "languageserver": { 43 | "nix": { 44 | "command": "rnix-lsp", 45 | "filetypes": [ 46 | "nix" 47 | ] 48 | } 49 | } 50 | } 51 | 52 | ``` 53 | 54 | #### [LanguageClient-neovim](https://github.com/autozimu/LanguageClient-neovim) 55 | 56 | ```vim 57 | let g:LanguageClient_serverCommands = { 58 | \ 'nix': ['rnix-lsp'] 59 | \ } 60 | ``` 61 | 62 | #### [vim-lsp](https://github.com/prabirshrestha/vim-lsp) 63 | 64 | ```vim 65 | if executable('rnix-lsp') 66 | au User lsp_setup call lsp#register_server({ 67 | \ 'name': 'rnix-lsp', 68 | \ 'cmd': {server_info->[&shell, &shellcmdflag, 'rnix-lsp']}, 69 | \ 'whitelist': ['nix'], 70 | \ }) 71 | endif 72 | ``` 73 | 74 | ### Emacs 75 | 76 | #### [lsp-mode](https://github.com/emacs-lsp/lsp-mode) 77 | 78 | ```elisp 79 | (add-to-list 'lsp-language-id-configuration '(nix-mode . "nix")) 80 | (lsp-register-client 81 | (make-lsp-client :new-connection (lsp-stdio-connection '("rnix-lsp")) 82 | :major-modes '(nix-mode) 83 | :server-id 'nix)) 84 | ``` 85 | -------------------------------------------------------------------------------- /default.nix: -------------------------------------------------------------------------------- 1 | { 2 | sources ? import ./nix/sources.nix, 3 | pkgs ? import {}, 4 | naersk ? pkgs.callPackage sources.naersk {}, 5 | }: naersk.buildPackage ./. 6 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "inputs": { 3 | "jd91mzm2": { 4 | "inputs": { 5 | "nixpkgs": { 6 | "inputs": {}, 7 | "narHash": "sha256-NB+H7zK3BB//zM127FqgbG4iAfY+nS/IOyO+uGWA5Ho=", 8 | "originalUrl": "nixpkgs", 9 | "url": "github:edolstra/nixpkgs/7845bf5f4b3013df1cf036e9c9c3a55a30331db9" 10 | } 11 | }, 12 | "narHash": "sha256-9ofYvO8AajH3HcFYQKnmUMmC9G6SPU76GLY4AW49loM=", 13 | "originalUrl": "git+https://gitlab.com/jD91mZM2/nur-packages", 14 | "url": "git+https://gitlab.com/jD91mZM2/nur-packages?ref=master&rev=6a6bc95258d16659e5c37d4cd77b3643c6c16395" 15 | }, 16 | "mozilla": { 17 | "inputs": {}, 18 | "narHash": "sha256-PMliZ6YzYoMY2dt7Gb85KEI+t9bXFnCS4ixadQIab8A=", 19 | "originalUrl": "github:mozilla/nixpkgs-mozilla", 20 | "url": "github:mozilla/nixpkgs-mozilla/5300241b41243cb8962fad284f0004afad187dad" 21 | }, 22 | "naersk": { 23 | "inputs": {}, 24 | "narHash": "sha256-vQ0lqJvMSEnwYLOYh1KM2PhD/9jTl3VZT+2LHUpCPUk=", 25 | "originalUrl": "github:nmattia/naersk", 26 | "url": "github:nmattia/naersk/efafcff2c5a2216cb14ccf9600b7c03e3f9baae1" 27 | }, 28 | "nixpkgs": { 29 | "inputs": {}, 30 | "narHash": "sha256-NB+H7zK3BB//zM127FqgbG4iAfY+nS/IOyO+uGWA5Ho=", 31 | "originalUrl": "nixpkgs", 32 | "url": "github:edolstra/nixpkgs/7845bf5f4b3013df1cf036e9c9c3a55a30331db9" 33 | } 34 | }, 35 | "version": 3 36 | } 37 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | description = "A work-in-progress language server for Nix, with syntax checking and basic completion"; 3 | 4 | edition = 201909; 5 | 6 | inputs.naersk = { 7 | url = "github:nmattia/naersk"; 8 | flake = false; 9 | }; 10 | inputs.mozilla = { 11 | url = "github:mozilla/nixpkgs-mozilla"; 12 | flake = false; 13 | }; 14 | inputs.jd91mzm2 = { 15 | url = "git+https://gitlab.com/jD91mZM2/nur-packages"; 16 | }; 17 | 18 | outputs = { self, nixpkgs, mozilla, naersk, jd91mzm2 }: let 19 | forAllSystems = nixpkgs.lib.genAttrs [ "x86_64-linux" "x86_64-darwin" "i686-linux" "aarch64-linux" ]; 20 | in 21 | rec { 22 | # Main package 23 | packages = forAllSystems (system: { 24 | rnix-lsp = let 25 | mozillaBuilt = nixpkgs.legacyPackages."${system}".callPackage "${mozilla}/package-set.nix" {}; 26 | naerskBuilt = nixpkgs.legacyPackages."${system}".callPackage naersk {}; 27 | 28 | rust = mozillaBuilt.latest.rustChannels.stable.rust; 29 | in naerskBuilt.buildPackage { 30 | name = "rnix-lsp"; 31 | src = jd91mzm2.lib.cleanSourceRust ./.; 32 | root = ./.; 33 | 34 | cargo = rust.cargo; 35 | rustc = rust.rustc; 36 | }; 37 | }); 38 | defaultPackage = forAllSystems (system: packages."${system}".rnix-lsp); 39 | 40 | # Make it runnable with `nix app` 41 | apps = forAllSystems (system: { 42 | rnix-lsp = { 43 | type = "app"; 44 | program = "${self.packages."${system}".rnix-lsp}/bin/rnix-lsp"; 45 | }; 46 | }); 47 | defaultApp = forAllSystems (system: apps."${system}".rnix-lsp); 48 | }; 49 | } 50 | -------------------------------------------------------------------------------- /nix/sources.json: -------------------------------------------------------------------------------- 1 | { 2 | "NUR": { 3 | "branch": "master", 4 | "description": "Nix User Repository: User contributed nix packages [maintainer=@Mic92]", 5 | "homepage": "", 6 | "owner": "nix-community", 7 | "repo": "NUR", 8 | "rev": "074982bb0e40aec9d841461828eb9754aa455135", 9 | "sha256": "0j7gr2s13ysphq2is1l4igylgy4ryxmnxgalw5m7q7qc1lxfv4dv", 10 | "type": "tarball", 11 | "url": "https://github.com/nix-community/NUR/archive/074982bb0e40aec9d841461828eb9754aa455135.tar.gz", 12 | "url_template": "https://github.com///archive/.tar.gz" 13 | }, 14 | "naersk": { 15 | "branch": "master", 16 | "description": "Build rust crates in Nix. No configuration, no code generation. IFD and sandbox friendly.", 17 | "homepage": "", 18 | "owner": "nmattia", 19 | "repo": "naersk", 20 | "rev": "529e910a3f423a8211f8739290014b754b2555b6", 21 | "sha256": "0bcy9nmyaan5jvp0wg80wkizc9j166ns685rdr1kbhkvdpywv46y", 22 | "type": "tarball", 23 | "url": "https://github.com/nmattia/naersk/archive/529e910a3f423a8211f8739290014b754b2555b6.tar.gz", 24 | "url_template": "https://github.com///archive/.tar.gz" 25 | }, 26 | "niv": { 27 | "branch": "master", 28 | "description": "Easy dependency management for Nix projects", 29 | "homepage": "https://github.com/nmattia/niv", 30 | "owner": "nmattia", 31 | "repo": "niv", 32 | "rev": "e82eb322ea32a747a51c431d7787221bcc6d9038", 33 | "sha256": "1fy4dcr05d80diwlxmh42xnjm5ki1pkbky38smvlqjaky2y2f71f", 34 | "type": "tarball", 35 | "url": "https://github.com/nmattia/niv/archive/e82eb322ea32a747a51c431d7787221bcc6d9038.tar.gz", 36 | "url_template": "https://github.com///archive/.tar.gz" 37 | }, 38 | "nixpkgs": { 39 | "branch": "nixos-19.09", 40 | "description": "A read-only mirror of NixOS/nixpkgs tracking the released channels. Send issues and PRs to", 41 | "homepage": "https://github.com/NixOS/nixpkgs", 42 | "owner": "NixOS", 43 | "repo": "nixpkgs-channels", 44 | "rev": "289466dd6a11c65a7de4a954d6ebf66c1ad07652", 45 | "sha256": "0r5ja052s86fr54fm1zlhld3fwawz2w1d1gd6vbvpjrpjfyajibn", 46 | "type": "tarball", 47 | "url": "https://github.com/NixOS/nixpkgs-channels/archive/289466dd6a11c65a7de4a954d6ebf66c1ad07652.tar.gz", 48 | "url_template": "https://github.com///archive/.tar.gz" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /nix/sources.nix: -------------------------------------------------------------------------------- 1 | # This file has been generated by Niv. 2 | 3 | let 4 | 5 | # 6 | # The fetchers. fetch_ fetches specs of type . 7 | # 8 | 9 | fetch_file = pkgs: spec: 10 | if spec.builtin or true then 11 | builtins_fetchurl { inherit (spec) url sha256; } 12 | else 13 | pkgs.fetchurl { inherit (spec) url sha256; }; 14 | 15 | fetch_tarball = pkgs: spec: 16 | if spec.builtin or true then 17 | builtins_fetchTarball { inherit (spec) url sha256; } 18 | else 19 | pkgs.fetchzip { inherit (spec) url sha256; }; 20 | 21 | fetch_git = spec: 22 | builtins.fetchGit { url = spec.repo; inherit (spec) rev ref; }; 23 | 24 | fetch_builtin-tarball = spec: 25 | builtins.trace 26 | '' 27 | WARNING: 28 | The niv type "builtin-tarball" will soon be deprecated. You should 29 | instead use `builtin = true`. 30 | 31 | $ niv modify -a type=tarball -a builtin=true 32 | '' 33 | builtins_fetchTarball { inherit (spec) url sha256; }; 34 | 35 | fetch_builtin-url = spec: 36 | builtins.trace 37 | '' 38 | WARNING: 39 | The niv type "builtin-url" will soon be deprecated. You should 40 | instead use `builtin = true`. 41 | 42 | $ niv modify -a type=file -a builtin=true 43 | '' 44 | (builtins_fetchurl { inherit (spec) url sha256; }); 45 | 46 | # 47 | # Various helpers 48 | # 49 | 50 | # The set of packages used when specs are fetched using non-builtins. 51 | mkPkgs = sources: 52 | if hasNixpkgsPath 53 | then 54 | if hasThisAsNixpkgsPath 55 | then import (builtins_fetchTarball { inherit (mkNixpkgs sources) url sha256; }) {} 56 | else import {} 57 | else 58 | import (builtins_fetchTarball { inherit (mkNixpkgs sources) url sha256; }) {}; 59 | 60 | mkNixpkgs = sources: 61 | if builtins.hasAttr "nixpkgs" sources 62 | then sources.nixpkgs 63 | else abort 64 | '' 65 | Please specify either (through -I or NIX_PATH=nixpkgs=...) or 66 | add a package called "nixpkgs" to your sources.json. 67 | ''; 68 | 69 | hasNixpkgsPath = (builtins.tryEval ).success; 70 | hasThisAsNixpkgsPath = 71 | (builtins.tryEval ).success && == ./.; 72 | 73 | # The actual fetching function. 74 | fetch = pkgs: name: spec: 75 | 76 | if ! builtins.hasAttr "type" spec then 77 | abort "ERROR: niv spec ${name} does not have a 'type' attribute" 78 | else if spec.type == "file" then fetch_file pkgs spec 79 | else if spec.type == "tarball" then fetch_tarball pkgs spec 80 | else if spec.type == "git" then fetch_git spec 81 | else if spec.type == "builtin-tarball" then fetch_builtin-tarball spec 82 | else if spec.type == "builtin-url" then fetch_builtin-url spec 83 | else 84 | abort "ERROR: niv spec ${name} has unknown type ${builtins.toJSON spec.type}"; 85 | 86 | # Ports of functions for older nix versions 87 | 88 | # a Nix version of mapAttrs if the built-in doesn't exist 89 | mapAttrs = builtins.mapAttrs or ( 90 | f: set: with builtins; 91 | listToAttrs (map (attr: { name = attr; value = f attr set.${attr}; }) (attrNames set)) 92 | ); 93 | 94 | # fetchTarball version that is compatible between all the versions of Nix 95 | builtins_fetchTarball = { url, sha256 }@attrs: 96 | let 97 | inherit (builtins) lessThan nixVersion fetchTarball; 98 | in 99 | if lessThan nixVersion "1.12" then 100 | fetchTarball { inherit url; } 101 | else 102 | fetchTarball attrs; 103 | 104 | # fetchurl version that is compatible between all the versions of Nix 105 | builtins_fetchurl = { url, sha256 }@attrs: 106 | let 107 | inherit (builtins) lessThan nixVersion fetchurl; 108 | in 109 | if lessThan nixVersion "1.12" then 110 | fetchurl { inherit url; } 111 | else 112 | fetchurl attrs; 113 | 114 | # Create the final "sources" from the config 115 | mkSources = config: 116 | mapAttrs ( 117 | name: spec: 118 | if builtins.hasAttr "outPath" spec 119 | then abort 120 | "The values in sources.json should not have an 'outPath' attribute" 121 | else 122 | spec // { outPath = fetch config.pkgs name spec; } 123 | ) config.sources; 124 | 125 | # The "config" used by the fetchers 126 | mkConfig = 127 | { sourcesFile ? ./sources.json 128 | }: rec { 129 | # The sources, i.e. the attribute set of spec name to spec 130 | sources = builtins.fromJSON (builtins.readFile sourcesFile); 131 | # The "pkgs" (evaluated nixpkgs) to use for e.g. non-builtin fetchers 132 | pkgs = mkPkgs sources; 133 | }; 134 | in 135 | mkSources (mkConfig {}) // 136 | { __functor = _: settings: mkSources (mkConfig settings); } 137 | -------------------------------------------------------------------------------- /playground/imported-set.nix: -------------------------------------------------------------------------------- 1 | { 2 | imported_shared_prefix_for_first_item = 1; 3 | imported_shared_prefix_for_second_item = 2; 4 | imported_shared_prefix_for_third_item = 3; 5 | imported_nested_completions = { 6 | imported_also_shared_prefix_for_first_item = 4; 7 | imported_also_shared_prefix_for_second_item = 5; 8 | imported_also_shared_prefix_for_third_item = 6; 9 | }; 10 | } 11 | -------------------------------------------------------------------------------- /playground/main.nix: -------------------------------------------------------------------------------- 1 | let 2 | # This data can be used for checking tab completions 3 | inline_set = { 4 | shared_prefix_for_first_item = 1; 5 | shared_prefix_for_second_item = 2; 6 | shared_prefix_for_third_item = 3; 7 | nested_completions = { 8 | also_shared_prefix_for_first_item = 4; 9 | also_shared_prefix_for_second_item = 5; 10 | also_shared_prefix_for_third_item = 6; 11 | }; 12 | nested_completions_alternative = { 13 | another_shared_prefix_for_first_item = 7; 14 | another_shared_prefix_for_second_item = 8; 15 | another_shared_prefix_for_third_item = 9; 16 | }; 17 | }; 18 | imported_set = import ./imported-set.nix; 19 | 20 | # This can be used for checking renames 21 | rename_me = "the original"; 22 | 23 | used_in_set = { 24 | this_is_an_interpolation = "${rename_me}"; 25 | this_is_a_value = rename_me; 26 | used_in_nested_set = { 27 | this_is_also_an_interpolation = "${rename_me}"; 28 | this_is_also_a_value = rename_me; 29 | }; 30 | 31 | # Ignoring shadowed variables is currently broken 32 | shadowed = rec { 33 | rename_me = "unrelated, so if possible, don't *actually* rename me"; 34 | hopefully_unchanged_usage = rename_me; 35 | }; 36 | }; 37 | in 38 | [ 39 | inline_set.shared_prefix_for_first_item 40 | ] 41 | -------------------------------------------------------------------------------- /shell.nix: -------------------------------------------------------------------------------- 1 | { 2 | sources ? import ./nix/sources.nix, 3 | pkgs ? import sources.nixpkgs {}, 4 | nur ? pkgs.callPackage sources.NUR {}, 5 | }: 6 | 7 | let 8 | rustVersion = nur.repos.mozilla.latest.rustChannels.stable; 9 | in pkgs.mkShell { 10 | PATH = "${builtins.getEnv "PATH"}:${toString ./target/debug}"; 11 | buildInputs = [ rustVersion.rust ]; 12 | } 13 | -------------------------------------------------------------------------------- /src/completion.rs: -------------------------------------------------------------------------------- 1 | use crate::{utils, App}; 2 | use itertools::Itertools; 3 | use lsp_types::{ 4 | CompletionItem, CompletionList, CompletionResponse, CompletionTextEdit, Documentation, Range, 5 | TextDocumentPositionParams, TextEdit, 6 | }; 7 | use manix::{DocEntry, DocSource}; 8 | use rnix::{ 9 | types::{ParsedType, TokenWrapper, TypedNode}, 10 | NixLanguage, SyntaxKind, SyntaxNode, TextUnit, 11 | }; 12 | use std::convert::TryFrom; 13 | 14 | impl App { 15 | fn scope_completions( 16 | &mut self, 17 | params: &TextDocumentPositionParams, 18 | ) -> Option> { 19 | let (ast, content) = self.files.get(¶ms.text_document.uri)?; 20 | let offset = utils::lookup_pos(content, params.position)?; 21 | let root_node = ast.node(); 22 | 23 | let (name, scope) = 24 | self.scope_for_ident(params.text_document.uri.clone(), &root_node, offset)?; 25 | let (_, content) = self.files.get(¶ms.text_document.uri)?; 26 | 27 | let scope_completions = scope 28 | .keys() 29 | .filter(|var| var.starts_with(&name.as_str())) 30 | .map(|var| CompletionItem { 31 | label: var.clone(), 32 | text_edit: Some(CompletionTextEdit::Edit(TextEdit { 33 | range: utils::range(content, name.node().text_range()), 34 | new_text: var.clone(), 35 | })), 36 | ..CompletionItem::default() 37 | }) 38 | .collect_vec(); 39 | Some(scope_completions) 40 | } 41 | 42 | fn manix_options_completions( 43 | &self, 44 | params: &TextDocumentPositionParams, 45 | ) -> Option> { 46 | // TODO implement this 47 | None 48 | } 49 | 50 | fn manix_value_completions( 51 | &self, 52 | params: &TextDocumentPositionParams, 53 | ) -> Option> { 54 | let (ast, content) = self.files.get(¶ms.text_document.uri)?; 55 | let offset = utils::lookup_pos(content, params.position)?; 56 | let root_node = ast.node(); 57 | 58 | let node = utils::closest_node_to(&root_node, offset)?; 59 | let (full_ident_node, full_ident_name) = self.full_ident_name(&node)?; 60 | dbg!(node.text_range()); 61 | 62 | let node_range = Range { 63 | start: utils::offset_to_pos( 64 | content, 65 | full_ident_node 66 | .first_token()? 67 | .text_range() 68 | .start() 69 | .to_usize(), 70 | ), 71 | end: utils::offset_to_pos( 72 | content, 73 | full_ident_node 74 | .descendants_with_tokens() 75 | .take_while(|n| match n { 76 | rnix::NodeOrToken::Node(_) => true, 77 | rnix::NodeOrToken::Token(t) => { 78 | t.kind() == SyntaxKind::TOKEN_DOT || t.kind() == SyntaxKind::TOKEN_IDENT 79 | } 80 | }) 81 | .last()? 82 | .text_range() 83 | .end() 84 | .to_usize(), 85 | ), 86 | }; 87 | 88 | let search_results = self.manix_values.search(&manix::Lowercase( 89 | &full_ident_name.clone().join(".").as_bytes(), 90 | )); 91 | 92 | let (namespace, namespace_items) = 93 | self.next_namespace_step_completions(full_ident_name.clone(), search_results); 94 | 95 | let manix_completions = namespace_items 96 | .iter() 97 | .unique_by(|x| x.name()) 98 | .map(|def| CompletionItem { 99 | label: def.name().clone(), 100 | text_edit: Some(CompletionTextEdit::Edit(TextEdit { 101 | range: node_range, 102 | new_text: def.name().clone(), 103 | })), 104 | documentation: def 105 | .try_as_doc_entry() 106 | .map(|entry| Documentation::String(entry.pretty_printed())), 107 | ..CompletionItem::default() 108 | }) 109 | .collect_vec(); 110 | Some(manix_completions) 111 | } 112 | 113 | #[allow(clippy::shadow_unrelated)] // false positive 114 | pub fn completions( 115 | &mut self, 116 | params: &TextDocumentPositionParams, 117 | ) -> Option> { 118 | // let scope_completions = self.scope_completions(params)?; 119 | let mut manix_value_completions = self.manix_value_completions(params).unwrap_or_default(); 120 | let mut manix_options_completions = 121 | self.manix_options_completions(params).unwrap_or_default(); 122 | let mut completions = Vec::new(); 123 | completions.append(&mut manix_value_completions); 124 | completions.append(&mut manix_options_completions); 125 | 126 | Some(completions) 127 | } 128 | 129 | fn next_namespace_step_completions( 130 | &self, 131 | current_ns: Vec, 132 | search_results: Vec, 133 | ) -> (Vec, Vec) { 134 | // TODO handle things like `with pkgs;` 135 | 136 | let query_ns_iter = current_ns.iter(); 137 | let longest_match = search_results 138 | .iter() 139 | .map(|result| { 140 | result 141 | .name() 142 | .split('.') 143 | .zip(query_ns_iter.clone()) 144 | .take_while(|(a, b)| a == b) 145 | .map(|(a, _)| a.to_string()) 146 | .collect_vec() 147 | }) 148 | .max(); 149 | if let Some(longest_match) = longest_match { 150 | dbg!(¤t_ns, &longest_match); 151 | let completions = search_results 152 | .into_iter() 153 | .filter(|result| { 154 | result 155 | .name() 156 | .split('.') 157 | .zip(query_ns_iter.clone()) 158 | .take_while(|(a, b)| a == b) 159 | .count() 160 | > 0 161 | }) 162 | .map(|result| { 163 | use NamespaceCompletionResult::*; 164 | if result.name().split('.').count() - 1 == longest_match.len() { 165 | FinalNode(result) 166 | } else { 167 | let presented_result = 168 | result.name().split('.').take(longest_match.len()).join("."); 169 | Set(presented_result) 170 | } 171 | }) 172 | .unique_by(|x| x.name()) 173 | .collect_vec(); 174 | (current_ns, completions) 175 | } else { 176 | (current_ns, Vec::new()) 177 | } 178 | } 179 | } 180 | 181 | #[derive(Debug, PartialEq, Eq)] 182 | enum NamespaceCompletionResult { 183 | Set(String), 184 | FinalNode(DocEntry), 185 | } 186 | 187 | impl NamespaceCompletionResult { 188 | fn name(&self) -> String { 189 | use NamespaceCompletionResult::*; 190 | match self { 191 | Set(s) => s.to_owned(), 192 | FinalNode(entry) => entry.name(), 193 | } 194 | } 195 | 196 | fn try_as_doc_entry(&self) -> Option<&DocEntry> { 197 | use NamespaceCompletionResult::*; 198 | match self { 199 | Set(_) => None, 200 | FinalNode(entry) => Some(entry), 201 | } 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /src/lookup.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | utils::{self, Var}, 3 | App, 4 | }; 5 | use lsp_types::Url; 6 | use rnix::{types::*, value::Value as ParsedValue, NodeOrToken, SyntaxKind, SyntaxNode}; 7 | use std::{ 8 | collections::{hash_map::Entry, HashMap}, 9 | convert::TryFrom, 10 | fs, 11 | rc::Rc, 12 | }; 13 | 14 | impl App { 15 | pub fn scope_for_ident( 16 | &mut self, 17 | file: Url, 18 | root: &SyntaxNode, 19 | offset: usize, 20 | ) -> Option<(Ident, HashMap)> { 21 | let mut file = Rc::new(file); 22 | let info = utils::ident_at(&root, offset)?; 23 | let ident = info.ident; 24 | let mut entries = utils::scope_for(&file, ident.node().clone())?; 25 | for var in info.path { 26 | let node = entries.get(&var)?.value.clone()?; 27 | entries = self.scope_from_node(&mut file, node)?; 28 | } 29 | Some((Ident::cast(ident.node().clone()).unwrap(), entries)) 30 | } 31 | pub fn scope_from_node( 32 | &mut self, 33 | file: &mut Rc, 34 | mut node: SyntaxNode, 35 | ) -> Option> { 36 | let mut scope = HashMap::new(); 37 | 38 | if let Some(entry) = KeyValue::cast(node.clone()) { 39 | node = entry.value()?; 40 | } 41 | 42 | // Resolve simple imports 43 | loop { 44 | let apply = match Apply::cast(node.clone()) { 45 | None => break, 46 | Some(apply) => apply, 47 | }; 48 | if Ident::cast(apply.lambda()?).map_or(true, |ident| ident.as_str() != "import") { 49 | break; 50 | } 51 | let (_anchor, path) = match Value::cast(apply.value()?) { 52 | None => break, 53 | Some(value) => match value.to_value() { 54 | Ok(ParsedValue::Path(anchor, path)) => (anchor, path), 55 | _ => break, 56 | }, 57 | }; 58 | 59 | // TODO use anchor 60 | *file = Rc::new(file.join(&path).ok()?); 61 | let path = utils::uri_path(&file)?; 62 | node = match self.files.entry((**file).clone()) { 63 | Entry::Occupied(entry) => { 64 | let (ast, _code) = entry.get(); 65 | ast.root().inner()?.clone() 66 | } 67 | Entry::Vacant(placeholder) => { 68 | let content = fs::read_to_string(&path).ok()?; 69 | let ast = rnix::parse(&content); 70 | let node = ast.root().inner()?.clone(); 71 | placeholder.insert((ast, content)); 72 | node 73 | } 74 | }; 75 | } 76 | 77 | if let Some(set) = AttrSet::cast(node) { 78 | utils::populate(&file, &mut scope, &set); 79 | } 80 | Some(scope) 81 | } 82 | 83 | pub fn full_ident_name(&self, node: &SyntaxNode) -> Option<(SyntaxNode, Vec)> { 84 | let try_get_ident_name = |x: SyntaxNode| match ParsedType::try_from(x) { 85 | Ok(ParsedType::Ident(ident)) => Some(ident.as_str().to_string()), 86 | _ => None, 87 | }; 88 | 89 | let node_path_pair: Option<(SyntaxNode, Vec)> = node.ancestors().find_map(|node| { 90 | let path = match ParsedType::try_from(node.clone()) { 91 | Ok(ParsedType::Key(key)) => { 92 | let path = key 93 | .node() 94 | .children_with_tokens() 95 | .take_while(|n| match n { 96 | NodeOrToken::Node(n) => n.kind() == SyntaxKind::NODE_IDENT, 97 | NodeOrToken::Token(t) => t.kind() == SyntaxKind::TOKEN_DOT, 98 | }) 99 | .filter_map(|n| n.as_node().cloned()) 100 | .filter_map(try_get_ident_name) 101 | .filter(|name| !name.trim().trim_end_matches("\n").is_empty()) 102 | .map(|x| x.replace("\n", "")) 103 | .collect::>(); 104 | Some(path) 105 | } 106 | _ => None, 107 | }; 108 | path.map(|x| (node, x)) 109 | }); 110 | 111 | let node_path_pair = node_path_pair.or_else(|| { 112 | let mut outermost_select = None; 113 | for ancestor in node.ancestors() { 114 | match ParsedType::try_from(ancestor.clone()) { 115 | Ok(ParsedType::Select(select)) => { 116 | outermost_select = Some(select); 117 | } 118 | _ if outermost_select.is_some() => { 119 | break; 120 | } 121 | _ => {} 122 | } 123 | } 124 | 125 | let mut path = Vec::new(); 126 | for child in outermost_select.clone()?.node().descendants_with_tokens() { 127 | match child { 128 | NodeOrToken::Node(_) => {} 129 | NodeOrToken::Token(t) if t.kind() == SyntaxKind::TOKEN_DOT => {} 130 | NodeOrToken::Token(t) if t.kind() == SyntaxKind::TOKEN_IDENT => { 131 | path.push(t.text().to_string()); 132 | } 133 | NodeOrToken::Token(_) => { 134 | break; 135 | } 136 | } 137 | } 138 | Some((outermost_select?.node().clone(), path)) 139 | }); 140 | 141 | // Ok(ParsedType::Select(key)) => { 142 | // let path = key 143 | // .node() 144 | // .children_with_tokens() 145 | // .take_while(|n| match n { 146 | // NodeOrToken::Node(n) => n.kind() == SyntaxKind::NODE_IDENT, 147 | // NodeOrToken::Token(t) => t.kind() == SyntaxKind::TOKEN_DOT, 148 | // }) 149 | // .filter_map(|n| n.as_node().cloned()) 150 | // .filter_map(try_get_ident_name) 151 | // .filter(|name| !name.trim().trim_end_matches("\n").is_empty()) 152 | // .map(|x| x.replace("\n", "")) 153 | // .collect::>(); 154 | // Some(path) 155 | // } 156 | dbg!(&node_path_pair); 157 | 158 | Some(node_path_pair?) 159 | } 160 | 161 | pub fn namespace_for_node(&self, node: &SyntaxNode) -> Vec { 162 | let mut path = node 163 | .parent() 164 | .map(|p| self.namespace_for_node(&p)) 165 | .unwrap_or_default(); 166 | 167 | if let Ok(ParsedType::KeyValue(key_value)) = ParsedType::try_from(node.clone()) { 168 | let mut my_path = key_value 169 | .key() 170 | .unwrap() 171 | .path() 172 | .map(|x| x.to_string()) 173 | .collect::>(); 174 | path.append(&mut my_path); 175 | } 176 | path 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | #![warn( 2 | // Harden built-in lints 3 | missing_copy_implementations, 4 | missing_debug_implementations, 5 | 6 | // Harden clippy lints 7 | clippy::cargo_common_metadata, 8 | clippy::clone_on_ref_ptr, 9 | clippy::dbg_macro, 10 | clippy::decimal_literal_representation, 11 | clippy::float_cmp_const, 12 | clippy::get_unwrap, 13 | clippy::integer_arithmetic, 14 | clippy::integer_division, 15 | clippy::pedantic, 16 | )] 17 | #![allow( 18 | // filter().map() can sometimes be more readable 19 | clippy::filter_map, 20 | // Most integer arithmetics are within an allocated region, so we know it's safe 21 | clippy::integer_arithmetic, 22 | )] 23 | 24 | mod completion; 25 | mod lookup; 26 | mod utils; 27 | 28 | use dirs::home_dir; 29 | use itertools::Itertools; 30 | use log::{error, trace, warn}; 31 | use lsp_server::{Connection, ErrorCode, Message, Notification, Request, RequestId, Response}; 32 | use lsp_types::{ 33 | notification::{Notification as _, *}, 34 | request::{Request as RequestTrait, *}, 35 | *, 36 | }; 37 | use manix::{ 38 | comments_docsource::CommentsDatabase, 39 | nixpkgs_tree_docsource, 40 | options_docsource::{self, OptionsDatabase}, 41 | xml_docsource, AggregateDocSource, Cache, DocSource, 42 | }; 43 | use nixpkgs_tree_docsource::NixpkgsTreeDatabase; 44 | use rnix::{ 45 | parser::*, 46 | types::*, 47 | value::{Anchor as RAnchor, Value as RValue}, 48 | SyntaxNode, 49 | }; 50 | use std::{ 51 | collections::HashMap, 52 | fs, panic, 53 | path::{Path, PathBuf}, 54 | process, 55 | rc::Rc, 56 | }; 57 | use xml_docsource::XmlFuncDocDatabase; 58 | 59 | type Error = Box; 60 | 61 | fn main() { 62 | if let Err(err) = real_main() { 63 | error!("Error: {} ({:?})", err, err); 64 | error!("A fatal error has occured and rnix-lsp will shut down."); 65 | drop(err); 66 | process::exit(libc::EXIT_FAILURE); 67 | } 68 | } 69 | fn real_main() -> Result<(), Error> { 70 | env_logger::init(); 71 | panic::set_hook(Box::new(move |panic| { 72 | error!("----- Panic -----"); 73 | error!("{}", panic); 74 | })); 75 | 76 | let (connection, io_threads) = Connection::stdio(); 77 | let capabilities = serde_json::to_value(&ServerCapabilities { 78 | text_document_sync: Some(TextDocumentSyncCapability::Options( 79 | TextDocumentSyncOptions { 80 | open_close: Some(true), 81 | change: Some(TextDocumentSyncKind::Full), 82 | ..TextDocumentSyncOptions::default() 83 | }, 84 | )), 85 | completion_provider: Some(CompletionOptions { 86 | ..CompletionOptions::default() 87 | }), 88 | definition_provider: Some(true), 89 | document_formatting_provider: Some(true), 90 | document_link_provider: Some(DocumentLinkOptions { 91 | resolve_provider: Some(false), 92 | work_done_progress_options: WorkDoneProgressOptions::default(), 93 | }), 94 | rename_provider: Some(RenameProviderCapability::Simple(true)), 95 | selection_range_provider: Some(SelectionRangeProviderCapability::Simple(true)), 96 | hover_provider: Some(HoverProviderCapability::Simple(true)), 97 | ..ServerCapabilities::default() 98 | }) 99 | .unwrap(); 100 | 101 | connection.initialize(capabilities)?; 102 | 103 | let (cache_invalid, manix_values) = load_manix_values().unwrap(); 104 | let manix_options = load_manix_options(cache_invalid).unwrap(); 105 | App { 106 | files: HashMap::new(), 107 | manix_options, 108 | manix_values, 109 | conn: connection, 110 | } 111 | .main(); 112 | 113 | io_threads.join()?; 114 | 115 | Ok(()) 116 | } 117 | 118 | fn build_source_and_add( 119 | mut source: T, 120 | name: &str, 121 | path: &PathBuf, 122 | aggregate: &mut AggregateDocSource, 123 | ) where 124 | T: 'static + DocSource + manix::Cache + Sync, 125 | { 126 | eprintln!("Building {} cache...", name); 127 | if let Err(e) = source.update() { 128 | eprintln!("{:?}", e); 129 | return; 130 | } 131 | 132 | if let Err(e) = source.save(&path) { 133 | eprintln!("{:?}", e); 134 | return; 135 | } 136 | 137 | aggregate.add_source(Box::new(source)); 138 | } 139 | 140 | fn load_manix_values() -> Option<(bool, AggregateDocSource)> { 141 | let cache_dir = xdg::BaseDirectories::with_prefix("manix").ok()?; 142 | let comment_cache_path = cache_dir.place_cache_file("database.bin").ok()?; 143 | let nixpkgs_tree_cache_path = cache_dir.place_cache_file("nixpkgs_tree.bin").ok()?; 144 | let nixpkgs_doc_cache_path = cache_dir 145 | .place_cache_file("nixpkgs_doc_database.bin") 146 | .ok()?; 147 | let mut aggregate_source = AggregateDocSource::default(); 148 | 149 | let mut comment_db = if comment_cache_path.exists() { 150 | CommentsDatabase::load(&std::fs::read(&comment_cache_path).ok()?).ok()? 151 | } else { 152 | CommentsDatabase::new() 153 | }; 154 | if comment_db.hash_to_defs.len() == 0 { 155 | eprintln!("Building NixOS comments cache..."); 156 | } 157 | let cache_invalid = comment_db.update().ok()?; 158 | comment_db.save(&comment_cache_path).ok()?; 159 | aggregate_source.add_source(Box::new(comment_db)); 160 | if cache_invalid { 161 | build_source_and_add( 162 | nixpkgs_tree_docsource::NixpkgsTreeDatabase::new(), 163 | "Nixpkgs Tree", 164 | &nixpkgs_tree_cache_path, 165 | &mut aggregate_source, 166 | ); 167 | 168 | build_source_and_add( 169 | xml_docsource::XmlFuncDocDatabase::new(), 170 | "Nixpkgs Documentation", 171 | &nixpkgs_doc_cache_path, 172 | &mut aggregate_source, 173 | ); 174 | } else { 175 | aggregate_source.add_source(Box::new( 176 | NixpkgsTreeDatabase::load(&fs::read(&nixpkgs_tree_cache_path).ok()?).ok()?, 177 | )); 178 | 179 | aggregate_source.add_source(Box::new( 180 | XmlFuncDocDatabase::load(&fs::read(&nixpkgs_doc_cache_path).ok()?).ok()?, 181 | )); 182 | } 183 | Some((cache_invalid, aggregate_source)) 184 | } 185 | 186 | fn load_manix_options(reload_cache: bool) -> Option { 187 | let cache_dir = xdg::BaseDirectories::with_prefix("manix").ok()?; 188 | 189 | let options_hm_cache_path = cache_dir.place_cache_file("options_hm_database.bin").ok()?; 190 | let options_nixos_cache_path = cache_dir 191 | .place_cache_file("options_nixos_database.bin") 192 | .ok()?; 193 | 194 | let mut aggregate_source = AggregateDocSource::default(); 195 | 196 | if reload_cache { 197 | build_source_and_add( 198 | OptionsDatabase::new(options_docsource::OptionsDatabaseType::HomeManager), 199 | "Home Manager Options", 200 | &options_hm_cache_path, 201 | &mut aggregate_source, 202 | ); 203 | 204 | build_source_and_add( 205 | OptionsDatabase::new(options_docsource::OptionsDatabaseType::NixOS), 206 | "NixOS Options", 207 | &options_nixos_cache_path, 208 | &mut aggregate_source, 209 | ); 210 | } else { 211 | aggregate_source.add_source(Box::new( 212 | OptionsDatabase::load(&fs::read(&options_hm_cache_path).ok()?).ok()?, 213 | )); 214 | 215 | aggregate_source.add_source(Box::new( 216 | OptionsDatabase::load(&fs::read(&options_nixos_cache_path).ok()?).ok()?, 217 | )); 218 | } 219 | Some(aggregate_source) 220 | } 221 | 222 | struct App { 223 | files: HashMap, 224 | manix_options: manix::AggregateDocSource, 225 | manix_values: manix::AggregateDocSource, 226 | conn: Connection, 227 | } 228 | impl App { 229 | fn reply(&mut self, response: Response) { 230 | trace!("Sending response: {:#?}", response); 231 | self.conn.sender.send(Message::Response(response)).unwrap(); 232 | } 233 | fn notify(&mut self, notification: Notification) { 234 | trace!("Sending notification: {:#?}", notification); 235 | self.conn 236 | .sender 237 | .send(Message::Notification(notification)) 238 | .unwrap(); 239 | } 240 | fn err(&mut self, id: RequestId, err: E) 241 | where 242 | E: std::fmt::Display, 243 | { 244 | warn!("{}", err); 245 | self.reply(Response::new_err( 246 | id, 247 | ErrorCode::UnknownErrorCode as i32, 248 | err.to_string(), 249 | )); 250 | } 251 | fn main(&mut self) { 252 | while let Ok(msg) = self.conn.receiver.recv() { 253 | trace!("Message: {:#?}", msg); 254 | match msg { 255 | Message::Request(req) => { 256 | let id = req.id.clone(); 257 | match self.conn.handle_shutdown(&req) { 258 | Ok(true) => break, 259 | Ok(false) => { 260 | if let Err(err) = self.handle_request(req) { 261 | self.err(id, err); 262 | } 263 | } 264 | Err(err) => { 265 | // This only fails if a shutdown was 266 | // requested in the first place, so it 267 | // should definitely break out of the 268 | // loop. 269 | self.err(id, err); 270 | break; 271 | } 272 | } 273 | } 274 | Message::Notification(notification) => { 275 | let _ = self.handle_notification(notification); 276 | } 277 | Message::Response(_) => (), 278 | } 279 | } 280 | } 281 | fn handle_request(&mut self, req: Request) -> Result<(), Error> { 282 | fn cast(req: &mut Option) -> Option<(RequestId, Kind::Params)> 283 | where 284 | Kind: RequestTrait, 285 | Kind::Params: serde::de::DeserializeOwned, 286 | { 287 | match req.take().unwrap().extract::(Kind::METHOD) { 288 | Ok(value) => Some(value), 289 | Err(owned) => { 290 | *req = Some(owned); 291 | None 292 | } 293 | } 294 | } 295 | let mut req = Some(req); 296 | if let Some((id, params)) = cast::(&mut req) { 297 | if let Some(pos) = self.lookup_definition(params.text_document_position_params) { 298 | self.reply(Response::new_ok(id, pos)); 299 | } else { 300 | self.reply(Response::new_ok(id, ())); 301 | } 302 | } else if let Some((id, params)) = cast::(&mut req) { 303 | let documentation = self 304 | .documentation(¶ms.text_document_position_params) 305 | .unwrap_or_default(); 306 | self.reply(Response::new_ok( 307 | id, 308 | Hover { 309 | contents: HoverContents::Markup(MarkupContent { 310 | kind: MarkupKind::Markdown, 311 | value: documentation, 312 | }), 313 | range: None, 314 | }, 315 | )); 316 | } else if let Some((id, params)) = cast::(&mut req) { 317 | // look at params.context for trigger reasons, etc 318 | let completions = self 319 | .completions(¶ms.text_document_position) 320 | .unwrap_or_default(); 321 | // .unwrap_or_else(|| CompletionResponse::Array(Vec::new())); 322 | self.reply(Response::new_ok(id, completions)); 323 | } else if let Some((id, params)) = cast::(&mut req) { 324 | let changes = self.rename(params); 325 | self.reply(Response::new_ok( 326 | id, 327 | WorkspaceEdit { 328 | changes, 329 | ..WorkspaceEdit::default() 330 | }, 331 | )); 332 | } else if let Some((id, params)) = cast::(&mut req) { 333 | let document_links = self.document_links(¶ms).unwrap_or_default(); 334 | self.reply(Response::new_ok(id, document_links)); 335 | } else if let Some((id, params)) = cast::(&mut req) { 336 | let changes = if let Some((ast, code)) = self.files.get(¶ms.text_document.uri) { 337 | let fmt = nixpkgs_fmt::reformat_node(&ast.node()); 338 | fmt.text_diff() 339 | .iter() 340 | .filter(|range| !range.delete.is_empty() || !range.insert.is_empty()) 341 | .map(|edit| TextEdit { 342 | range: utils::range(&code, edit.delete), 343 | new_text: edit.insert.to_string(), 344 | }) 345 | .collect() 346 | } else { 347 | Vec::new() 348 | }; 349 | self.reply(Response::new_ok(id, changes)); 350 | } else if let Some((id, params)) = cast::(&mut req) { 351 | let mut selections = Vec::new(); 352 | if let Some((ast, code)) = self.files.get(¶ms.text_document.uri) { 353 | for pos in params.positions { 354 | selections.push(utils::selection_ranges(&ast.node(), code, pos)); 355 | } 356 | } 357 | self.reply(Response::new_ok(id, selections)); 358 | } 359 | Ok(()) 360 | } 361 | fn handle_notification(&mut self, req: Notification) -> Result<(), Error> { 362 | match &*req.method { 363 | DidOpenTextDocument::METHOD => { 364 | let params: DidOpenTextDocumentParams = serde_json::from_value(req.params)?; 365 | let text = params.text_document.text; 366 | let parsed = rnix::parse(&text); 367 | self.send_diagnostics(params.text_document.uri.clone(), &text, &parsed)?; 368 | self.files.insert(params.text_document.uri, (parsed, text)); 369 | } 370 | DidChangeTextDocument::METHOD => { 371 | let params: DidChangeTextDocumentParams = serde_json::from_value(req.params)?; 372 | if let Some(change) = params.content_changes.into_iter().last() { 373 | let parsed = rnix::parse(&change.text); 374 | self.send_diagnostics(params.text_document.uri.clone(), &change.text, &parsed)?; 375 | self.files 376 | .insert(params.text_document.uri, (parsed, change.text)); 377 | } 378 | } 379 | _ => (), 380 | } 381 | Ok(()) 382 | } 383 | fn lookup_definition(&mut self, params: TextDocumentPositionParams) -> Option { 384 | let (current_ast, current_content) = self.files.get(¶ms.text_document.uri)?; 385 | let offset = utils::lookup_pos(current_content, params.position)?; 386 | let node = current_ast.node(); 387 | let (name, scope) = self.scope_for_ident(params.text_document.uri, &node, offset)?; 388 | 389 | let var = scope.get(name.as_str())?; 390 | let (_definition_ast, definition_content) = self.files.get(&var.file)?; 391 | Some(Location { 392 | uri: (*var.file).clone(), 393 | range: utils::range(definition_content, var.key.text_range()), 394 | }) 395 | } 396 | 397 | fn documentation(&mut self, params: &TextDocumentPositionParams) -> Option { 398 | let (ast, content) = self.files.get(¶ms.text_document.uri)?; 399 | let offset = utils::lookup_pos(content, params.position)?; 400 | let cursor = utils::ident_at(&ast.node(), offset)?; 401 | let ident = cursor.ident.as_str(); 402 | 403 | let query = manix::Lowercase(ident.as_bytes()); 404 | 405 | let mut definitions = self.manix_values.search(&query); 406 | definitions.append(&mut self.manix_options.search(&query)); 407 | 408 | Some( 409 | definitions 410 | .iter() 411 | .map(|def| def.pretty_printed()) 412 | .collect::>() 413 | .join("\n"), 414 | ) 415 | } 416 | 417 | fn rename(&mut self, params: RenameParams) -> Option>> { 418 | struct Rename<'a> { 419 | edits: Vec, 420 | code: &'a str, 421 | old: &'a str, 422 | new_name: String, 423 | } 424 | fn rename_in_node(rename: &mut Rename, node: &SyntaxNode) -> Option<()> { 425 | if let Some(ident) = Ident::cast(node.clone()) { 426 | if ident.as_str() == rename.old { 427 | rename.edits.push(TextEdit { 428 | range: utils::range(rename.code, node.text_range()), 429 | new_text: rename.new_name.clone(), 430 | }); 431 | } 432 | } else if let Some(index) = Select::cast(node.clone()) { 433 | rename_in_node(rename, &index.set()?); 434 | } else if let Some(attr) = Key::cast(node.clone()) { 435 | let mut path = attr.path(); 436 | if let Some(ident) = path.next() { 437 | rename_in_node(rename, &ident); 438 | } 439 | } else { 440 | for child in node.children() { 441 | rename_in_node(rename, &child); 442 | } 443 | } 444 | Some(()) 445 | } 446 | 447 | let uri = params.text_document_position.text_document.uri; 448 | let (ast, code) = self.files.get(&uri)?; 449 | let offset = utils::lookup_pos(code, params.text_document_position.position)?; 450 | let info = utils::ident_at(&ast.node(), offset)?; 451 | if !info.path.is_empty() { 452 | // Renaming within a set not supported 453 | return None; 454 | } 455 | let old = info.ident; 456 | let scope = utils::scope_for(&Rc::new(uri.clone()), old.node().clone())?; 457 | 458 | let mut rename = Rename { 459 | edits: Vec::new(), 460 | code, 461 | old: old.as_str(), 462 | new_name: params.new_name, 463 | }; 464 | let definition = scope.get(old.as_str())?; 465 | rename_in_node(&mut rename, &definition.set); 466 | 467 | let mut changes = HashMap::new(); 468 | changes.insert(uri, rename.edits); 469 | Some(changes) 470 | } 471 | fn document_links(&mut self, params: &DocumentLinkParams) -> Option> { 472 | let (current_ast, current_content) = self.files.get(¶ms.text_document.uri)?; 473 | let parent_dir = Path::new(params.text_document.uri.path()).parent(); 474 | let home_dir = home_dir(); 475 | let home_dir = home_dir.as_ref(); 476 | let mut document_links = vec![]; 477 | for node in current_ast.node().descendants() { 478 | let value = Value::cast(node.clone()).and_then(|v| v.to_value().ok()); 479 | if let Some(RValue::Path(anchor, path)) = value { 480 | let file_url = match anchor { 481 | RAnchor::Absolute => Some(PathBuf::from(&path)), 482 | RAnchor::Relative => parent_dir.map(|p| p.join(path)), 483 | RAnchor::Home => home_dir.map(|home| home.join(path)), 484 | RAnchor::Store => None, 485 | } 486 | .and_then(|path| std::fs::canonicalize(&path).ok()) 487 | .filter(|path| path.is_file()) 488 | .and_then(|s| Url::parse(&format!("file://{}", s.to_string_lossy())).ok()); 489 | if let Some(file_url) = file_url { 490 | document_links.push(DocumentLink { 491 | target: Some(file_url), 492 | range: utils::range(current_content, node.text_range()), 493 | tooltip: None, 494 | data: None, 495 | }) 496 | } 497 | } 498 | } 499 | Some(document_links) 500 | } 501 | fn send_diagnostics(&mut self, uri: Url, code: &str, ast: &AST) -> Result<(), Error> { 502 | let errors = ast.errors(); 503 | let mut diagnostics = Vec::with_capacity(errors.len()); 504 | for err in errors { 505 | if let ParseError::Unexpected(node) = err { 506 | diagnostics.push(Diagnostic { 507 | range: utils::range(code, node), 508 | severity: Some(DiagnosticSeverity::Error), 509 | message: err.to_string(), 510 | ..Diagnostic::default() 511 | }); 512 | } 513 | } 514 | self.notify(Notification::new( 515 | "textDocument/publishDiagnostics".into(), 516 | PublishDiagnosticsParams { 517 | uri, 518 | diagnostics, 519 | version: None, 520 | }, 521 | )); 522 | Ok(()) 523 | } 524 | } 525 | -------------------------------------------------------------------------------- /src/utils.rs: -------------------------------------------------------------------------------- 1 | use lsp_types::*; 2 | use rnix::{types::*, SyntaxNode, TextRange, TextUnit, TokenAtOffset}; 3 | use std::{collections::HashMap, convert::TryFrom, path::PathBuf, rc::Rc}; 4 | 5 | pub fn uri_path(uri: &Url) -> Option { 6 | if uri.scheme() != "file" || uri.has_host() { 7 | return None; 8 | } 9 | Some(PathBuf::from(uri.path())) 10 | } 11 | pub fn lookup_pos(code: &str, pos: Position) -> Option { 12 | let mut lines = code.split('\n'); 13 | 14 | let mut offset = 0; 15 | for _ in 0..pos.line { 16 | let line = lines.next()?; 17 | 18 | offset += line.len() + 1; 19 | } 20 | 21 | lines.next().and_then(|line| { 22 | Some( 23 | offset 24 | + line 25 | .chars() 26 | .take(usize::try_from(pos.character).ok()?) 27 | .map(char::len_utf8) 28 | .sum::(), 29 | ) 30 | }) 31 | } 32 | pub fn offset_to_pos(code: &str, offset: usize) -> Position { 33 | let start_of_line = code[..offset].rfind('\n').map_or(0, |n| n + 1); 34 | Position { 35 | line: code[..start_of_line].chars().filter(|&c| c == '\n').count() as u64, 36 | character: code[start_of_line..offset] 37 | .chars() 38 | .map(|c| c.len_utf16() as u64) 39 | .sum(), 40 | } 41 | } 42 | pub fn range(code: &str, range: TextRange) -> Range { 43 | Range { 44 | start: offset_to_pos(code, range.start().to_usize()), 45 | end: offset_to_pos(code, range.end().to_usize()), 46 | } 47 | } 48 | pub struct CursorInfo { 49 | pub path: Vec, 50 | pub ident: Ident, 51 | } 52 | 53 | pub fn closest_node_to(root: &SyntaxNode, offset: usize) -> Option { 54 | match root.token_at_offset(TextUnit::from_usize(offset)) { 55 | TokenAtOffset::None => None, 56 | TokenAtOffset::Single(node) => Some(node.parent()), 57 | TokenAtOffset::Between(left, _) => Some(left.parent()), 58 | } 59 | } 60 | 61 | pub fn ident_at(root: &SyntaxNode, offset: usize) -> Option { 62 | let ident = match root.token_at_offset(TextUnit::from_usize(offset)) { 63 | TokenAtOffset::None => None, 64 | TokenAtOffset::Single(node) => Ident::cast(node.parent()), 65 | TokenAtOffset::Between(left, right) => { 66 | Ident::cast(left.parent()).or_else(|| Ident::cast(right.parent())) 67 | } 68 | }?; 69 | let parent = ident.node().parent(); 70 | if let Some(attr) = parent.clone().and_then(Key::cast) { 71 | let mut path = Vec::new(); 72 | for item in attr.path() { 73 | if item == *ident.node() { 74 | return Some(CursorInfo { path, ident }); 75 | } 76 | 77 | path.push(Ident::cast(item)?.as_str().into()); 78 | } 79 | panic!("identifier at cursor is somehow not a child of its parent",); 80 | } else if let Some(mut index) = parent.and_then(Select::cast) { 81 | let mut path = Vec::new(); 82 | while let Some(new) = Select::cast(index.set()?) { 83 | path.push(Ident::cast(new.index()?)?.as_str().into()); 84 | index = new; 85 | } 86 | if index.set()? != *ident.node() { 87 | // Only push if not the cursor ident, so that 88 | // a . b 89 | // ^ 90 | // is not [a] and a, but rather [] and a 91 | path.push(Ident::cast(index.set()?)?.as_str().into()); 92 | } 93 | path.reverse(); 94 | Some(CursorInfo { path, ident }) 95 | } else { 96 | Some(CursorInfo { 97 | path: Vec::new(), 98 | ident, 99 | }) 100 | } 101 | } 102 | 103 | #[derive(Debug)] 104 | pub struct Var { 105 | pub file: Rc, 106 | pub set: SyntaxNode, 107 | pub key: SyntaxNode, 108 | pub value: Option, 109 | } 110 | pub fn populate( 111 | file: &Rc, 112 | scope: &mut HashMap, 113 | set: &T, 114 | ) -> Option<()> { 115 | for entry in set.entries() { 116 | let attr = entry.key()?; 117 | let mut path = attr.path(); 118 | if let Some(ident) = path.next().and_then(Ident::cast) { 119 | if !scope.contains_key(ident.as_str()) { 120 | scope.insert( 121 | ident.as_str().into(), 122 | Var { 123 | file: Rc::clone(file), 124 | set: set.node().to_owned(), 125 | key: ident.node().to_owned(), 126 | value: Some(entry.value()?.to_owned()), 127 | }, 128 | ); 129 | } 130 | } 131 | } 132 | Some(()) 133 | } 134 | pub fn scope_for(file: &Rc, node: SyntaxNode) -> Option> { 135 | let mut scope = HashMap::new(); 136 | 137 | let mut current = Some(node); 138 | while let Some(node) = current { 139 | match ParsedType::try_from(node.clone()) { 140 | Ok(ParsedType::LetIn(let_in)) => { 141 | populate(&file, &mut scope, &let_in); 142 | } 143 | Ok(ParsedType::LegacyLet(let_)) => { 144 | populate(&file, &mut scope, &let_); 145 | } 146 | Ok(ParsedType::AttrSet(set)) => { 147 | if set.recursive() { 148 | populate(&file, &mut scope, &set); 149 | } 150 | } 151 | Ok(ParsedType::Lambda(lambda)) => match ParsedType::try_from(lambda.arg()?) { 152 | Ok(ParsedType::Ident(ident)) => { 153 | if !scope.contains_key(ident.as_str()) { 154 | scope.insert( 155 | ident.as_str().into(), 156 | Var { 157 | file: Rc::clone(&file), 158 | set: lambda.node().clone(), 159 | key: ident.node().clone(), 160 | value: None, 161 | }, 162 | ); 163 | } 164 | } 165 | Ok(ParsedType::Pattern(pattern)) => { 166 | for entry in pattern.entries() { 167 | let ident = entry.name()?; 168 | if !scope.contains_key(ident.as_str()) { 169 | scope.insert( 170 | ident.as_str().into(), 171 | Var { 172 | file: Rc::clone(&file), 173 | set: lambda.node().to_owned(), 174 | key: ident.node().to_owned(), 175 | value: None, 176 | }, 177 | ); 178 | } 179 | } 180 | } 181 | _ => (), 182 | }, 183 | _ => (), 184 | } 185 | current = node.parent(); 186 | } 187 | 188 | Some(scope) 189 | } 190 | pub fn selection_ranges(root: &SyntaxNode, content: &str, pos: Position) -> Option { 191 | let pos = lookup_pos(content, pos)?; 192 | let node = root 193 | .token_at_offset(TextUnit::from_usize(pos)) 194 | .left_biased()?; 195 | 196 | let mut root = None; 197 | let mut cursor = &mut root; 198 | 199 | let mut last = None; 200 | for parent in node.ancestors() { 201 | // De-duplicate 202 | if last.as_ref() == Some(&parent) { 203 | continue; 204 | } 205 | 206 | let text_range = parent.text_range(); 207 | *cursor = Some(Box::new(SelectionRange { 208 | range: range(content, text_range), 209 | parent: None, 210 | })); 211 | cursor = &mut cursor.as_mut().unwrap().parent; 212 | 213 | last = Some(parent); 214 | } 215 | 216 | root.map(|b| *b) 217 | } 218 | --------------------------------------------------------------------------------