├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md └── src ├── crawler.rs ├── language_registry.rs ├── main.rs ├── schema.sql └── store.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | [[package]] 2 | name = "aho-corasick" 3 | version = "0.6.8" 4 | source = "registry+https://github.com/rust-lang/crates.io-index" 5 | dependencies = [ 6 | "memchr 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 7 | ] 8 | 9 | [[package]] 10 | name = "ansi_term" 11 | version = "0.11.0" 12 | source = "registry+https://github.com/rust-lang/crates.io-index" 13 | dependencies = [ 14 | "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", 15 | ] 16 | 17 | [[package]] 18 | name = "argon2rs" 19 | version = "0.2.5" 20 | source = "registry+https://github.com/rust-lang/crates.io-index" 21 | dependencies = [ 22 | "blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", 23 | "scoped_threadpool 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", 24 | ] 25 | 26 | [[package]] 27 | name = "arrayvec" 28 | version = "0.4.7" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | dependencies = [ 31 | "nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", 32 | ] 33 | 34 | [[package]] 35 | name = "atty" 36 | version = "0.2.11" 37 | source = "registry+https://github.com/rust-lang/crates.io-index" 38 | dependencies = [ 39 | "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", 40 | "termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", 41 | "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", 42 | ] 43 | 44 | [[package]] 45 | name = "backtrace" 46 | version = "0.3.9" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | dependencies = [ 49 | "backtrace-sys 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", 50 | "cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 51 | "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", 52 | "rustc-demangle 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", 53 | "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", 54 | ] 55 | 56 | [[package]] 57 | name = "backtrace-sys" 58 | version = "0.1.24" 59 | source = "registry+https://github.com/rust-lang/crates.io-index" 60 | dependencies = [ 61 | "cc 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)", 62 | "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", 63 | ] 64 | 65 | [[package]] 66 | name = "bitflags" 67 | version = "1.0.4" 68 | source = "registry+https://github.com/rust-lang/crates.io-index" 69 | 70 | [[package]] 71 | name = "blake2-rfc" 72 | version = "0.2.18" 73 | source = "registry+https://github.com/rust-lang/crates.io-index" 74 | dependencies = [ 75 | "arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", 76 | "constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", 77 | ] 78 | 79 | [[package]] 80 | name = "cc" 81 | version = "1.0.25" 82 | source = "registry+https://github.com/rust-lang/crates.io-index" 83 | 84 | [[package]] 85 | name = "cfg-if" 86 | version = "0.1.5" 87 | source = "registry+https://github.com/rust-lang/crates.io-index" 88 | 89 | [[package]] 90 | name = "clap" 91 | version = "2.32.0" 92 | source = "registry+https://github.com/rust-lang/crates.io-index" 93 | dependencies = [ 94 | "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", 95 | "atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", 96 | "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", 97 | "strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", 98 | "textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", 99 | "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 100 | "vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", 101 | ] 102 | 103 | [[package]] 104 | name = "cloudabi" 105 | version = "0.0.3" 106 | source = "registry+https://github.com/rust-lang/crates.io-index" 107 | dependencies = [ 108 | "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", 109 | ] 110 | 111 | [[package]] 112 | name = "constant_time_eq" 113 | version = "0.1.3" 114 | source = "registry+https://github.com/rust-lang/crates.io-index" 115 | 116 | [[package]] 117 | name = "crossbeam-channel" 118 | version = "0.2.6" 119 | source = "registry+https://github.com/rust-lang/crates.io-index" 120 | dependencies = [ 121 | "crossbeam-epoch 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", 122 | "crossbeam-utils 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", 123 | "parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", 124 | "rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", 125 | "smallvec 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", 126 | ] 127 | 128 | [[package]] 129 | name = "crossbeam-epoch" 130 | version = "0.6.0" 131 | source = "registry+https://github.com/rust-lang/crates.io-index" 132 | dependencies = [ 133 | "arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", 134 | "cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 135 | "crossbeam-utils 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", 136 | "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 137 | "memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 138 | "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 139 | ] 140 | 141 | [[package]] 142 | name = "crossbeam-utils" 143 | version = "0.5.0" 144 | source = "registry+https://github.com/rust-lang/crates.io-index" 145 | 146 | [[package]] 147 | name = "dirs" 148 | version = "1.0.4" 149 | source = "registry+https://github.com/rust-lang/crates.io-index" 150 | dependencies = [ 151 | "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", 152 | "redox_users 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 153 | "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", 154 | ] 155 | 156 | [[package]] 157 | name = "failure" 158 | version = "0.1.2" 159 | source = "registry+https://github.com/rust-lang/crates.io-index" 160 | dependencies = [ 161 | "backtrace 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", 162 | "failure_derive 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 163 | ] 164 | 165 | [[package]] 166 | name = "failure_derive" 167 | version = "0.1.2" 168 | source = "registry+https://github.com/rust-lang/crates.io-index" 169 | dependencies = [ 170 | "proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)", 171 | "quote 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", 172 | "syn 0.14.9 (registry+https://github.com/rust-lang/crates.io-index)", 173 | "synstructure 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", 174 | ] 175 | 176 | [[package]] 177 | name = "fnv" 178 | version = "1.0.6" 179 | source = "registry+https://github.com/rust-lang/crates.io-index" 180 | 181 | [[package]] 182 | name = "fuchsia-zircon" 183 | version = "0.3.3" 184 | source = "registry+https://github.com/rust-lang/crates.io-index" 185 | dependencies = [ 186 | "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", 187 | "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 188 | ] 189 | 190 | [[package]] 191 | name = "fuchsia-zircon-sys" 192 | version = "0.3.3" 193 | source = "registry+https://github.com/rust-lang/crates.io-index" 194 | 195 | [[package]] 196 | name = "globset" 197 | version = "0.4.2" 198 | source = "registry+https://github.com/rust-lang/crates.io-index" 199 | dependencies = [ 200 | "aho-corasick 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", 201 | "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", 202 | "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", 203 | "memchr 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 204 | "regex 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", 205 | ] 206 | 207 | [[package]] 208 | name = "ignore" 209 | version = "0.4.4" 210 | source = "registry+https://github.com/rust-lang/crates.io-index" 211 | dependencies = [ 212 | "crossbeam-channel 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", 213 | "globset 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", 214 | "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 215 | "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", 216 | "memchr 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 217 | "regex 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", 218 | "same-file 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", 219 | "thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", 220 | "walkdir 2.2.5 (registry+https://github.com/rust-lang/crates.io-index)", 221 | "winapi-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 222 | ] 223 | 224 | [[package]] 225 | name = "itoa" 226 | version = "0.4.3" 227 | source = "registry+https://github.com/rust-lang/crates.io-index" 228 | 229 | [[package]] 230 | name = "lazy_static" 231 | version = "1.1.0" 232 | source = "registry+https://github.com/rust-lang/crates.io-index" 233 | dependencies = [ 234 | "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 235 | ] 236 | 237 | [[package]] 238 | name = "libc" 239 | version = "0.2.43" 240 | source = "registry+https://github.com/rust-lang/crates.io-index" 241 | 242 | [[package]] 243 | name = "libloading" 244 | version = "0.5.0" 245 | source = "registry+https://github.com/rust-lang/crates.io-index" 246 | dependencies = [ 247 | "cc 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)", 248 | "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", 249 | ] 250 | 251 | [[package]] 252 | name = "libsqlite3-sys" 253 | version = "0.9.3" 254 | source = "registry+https://github.com/rust-lang/crates.io-index" 255 | dependencies = [ 256 | "pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", 257 | "vcpkg 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", 258 | ] 259 | 260 | [[package]] 261 | name = "linked-hash-map" 262 | version = "0.4.2" 263 | source = "registry+https://github.com/rust-lang/crates.io-index" 264 | 265 | [[package]] 266 | name = "lock_api" 267 | version = "0.1.4" 268 | source = "registry+https://github.com/rust-lang/crates.io-index" 269 | dependencies = [ 270 | "owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 271 | "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 272 | ] 273 | 274 | [[package]] 275 | name = "log" 276 | version = "0.4.5" 277 | source = "registry+https://github.com/rust-lang/crates.io-index" 278 | dependencies = [ 279 | "cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 280 | ] 281 | 282 | [[package]] 283 | name = "lru-cache" 284 | version = "0.1.1" 285 | source = "registry+https://github.com/rust-lang/crates.io-index" 286 | dependencies = [ 287 | "linked-hash-map 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", 288 | ] 289 | 290 | [[package]] 291 | name = "memchr" 292 | version = "2.1.0" 293 | source = "registry+https://github.com/rust-lang/crates.io-index" 294 | dependencies = [ 295 | "cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 296 | "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", 297 | "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 298 | ] 299 | 300 | [[package]] 301 | name = "memoffset" 302 | version = "0.2.1" 303 | source = "registry+https://github.com/rust-lang/crates.io-index" 304 | 305 | [[package]] 306 | name = "nodrop" 307 | version = "0.1.12" 308 | source = "registry+https://github.com/rust-lang/crates.io-index" 309 | 310 | [[package]] 311 | name = "owning_ref" 312 | version = "0.3.3" 313 | source = "registry+https://github.com/rust-lang/crates.io-index" 314 | dependencies = [ 315 | "stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 316 | ] 317 | 318 | [[package]] 319 | name = "parking_lot" 320 | version = "0.6.4" 321 | source = "registry+https://github.com/rust-lang/crates.io-index" 322 | dependencies = [ 323 | "lock_api 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", 324 | "parking_lot_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 325 | ] 326 | 327 | [[package]] 328 | name = "parking_lot_core" 329 | version = "0.3.1" 330 | source = "registry+https://github.com/rust-lang/crates.io-index" 331 | dependencies = [ 332 | "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", 333 | "rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", 334 | "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", 335 | "smallvec 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", 336 | "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", 337 | ] 338 | 339 | [[package]] 340 | name = "pkg-config" 341 | version = "0.3.14" 342 | source = "registry+https://github.com/rust-lang/crates.io-index" 343 | 344 | [[package]] 345 | name = "proc-macro2" 346 | version = "0.4.20" 347 | source = "registry+https://github.com/rust-lang/crates.io-index" 348 | dependencies = [ 349 | "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 350 | ] 351 | 352 | [[package]] 353 | name = "quote" 354 | version = "0.6.8" 355 | source = "registry+https://github.com/rust-lang/crates.io-index" 356 | dependencies = [ 357 | "proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)", 358 | ] 359 | 360 | [[package]] 361 | name = "rand" 362 | version = "0.4.3" 363 | source = "registry+https://github.com/rust-lang/crates.io-index" 364 | dependencies = [ 365 | "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 366 | "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", 367 | "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", 368 | ] 369 | 370 | [[package]] 371 | name = "rand" 372 | version = "0.5.5" 373 | source = "registry+https://github.com/rust-lang/crates.io-index" 374 | dependencies = [ 375 | "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", 376 | "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 377 | "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", 378 | "rand_core 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 379 | "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", 380 | ] 381 | 382 | [[package]] 383 | name = "rand_core" 384 | version = "0.2.2" 385 | source = "registry+https://github.com/rust-lang/crates.io-index" 386 | dependencies = [ 387 | "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", 388 | ] 389 | 390 | [[package]] 391 | name = "rand_core" 392 | version = "0.3.0" 393 | source = "registry+https://github.com/rust-lang/crates.io-index" 394 | 395 | [[package]] 396 | name = "redox_syscall" 397 | version = "0.1.40" 398 | source = "registry+https://github.com/rust-lang/crates.io-index" 399 | 400 | [[package]] 401 | name = "redox_termios" 402 | version = "0.1.1" 403 | source = "registry+https://github.com/rust-lang/crates.io-index" 404 | dependencies = [ 405 | "redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", 406 | ] 407 | 408 | [[package]] 409 | name = "redox_users" 410 | version = "0.2.0" 411 | source = "registry+https://github.com/rust-lang/crates.io-index" 412 | dependencies = [ 413 | "argon2rs 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", 414 | "failure 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 415 | "rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", 416 | "redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", 417 | ] 418 | 419 | [[package]] 420 | name = "regex" 421 | version = "1.0.5" 422 | source = "registry+https://github.com/rust-lang/crates.io-index" 423 | dependencies = [ 424 | "aho-corasick 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", 425 | "memchr 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 426 | "regex-syntax 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", 427 | "thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", 428 | "utf8-ranges 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", 429 | ] 430 | 431 | [[package]] 432 | name = "regex-syntax" 433 | version = "0.6.2" 434 | source = "registry+https://github.com/rust-lang/crates.io-index" 435 | dependencies = [ 436 | "ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 437 | ] 438 | 439 | [[package]] 440 | name = "rusqlite" 441 | version = "0.14.0" 442 | source = "registry+https://github.com/rust-lang/crates.io-index" 443 | dependencies = [ 444 | "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", 445 | "libsqlite3-sys 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", 446 | "lru-cache 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 447 | "time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", 448 | ] 449 | 450 | [[package]] 451 | name = "rustc-demangle" 452 | version = "0.1.9" 453 | source = "registry+https://github.com/rust-lang/crates.io-index" 454 | 455 | [[package]] 456 | name = "rustc_version" 457 | version = "0.2.3" 458 | source = "registry+https://github.com/rust-lang/crates.io-index" 459 | dependencies = [ 460 | "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", 461 | ] 462 | 463 | [[package]] 464 | name = "ryu" 465 | version = "0.2.6" 466 | source = "registry+https://github.com/rust-lang/crates.io-index" 467 | 468 | [[package]] 469 | name = "same-file" 470 | version = "1.0.3" 471 | source = "registry+https://github.com/rust-lang/crates.io-index" 472 | dependencies = [ 473 | "winapi-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 474 | ] 475 | 476 | [[package]] 477 | name = "scoped_threadpool" 478 | version = "0.1.9" 479 | source = "registry+https://github.com/rust-lang/crates.io-index" 480 | 481 | [[package]] 482 | name = "scopeguard" 483 | version = "0.3.3" 484 | source = "registry+https://github.com/rust-lang/crates.io-index" 485 | 486 | [[package]] 487 | name = "semver" 488 | version = "0.9.0" 489 | source = "registry+https://github.com/rust-lang/crates.io-index" 490 | dependencies = [ 491 | "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", 492 | ] 493 | 494 | [[package]] 495 | name = "semver-parser" 496 | version = "0.7.0" 497 | source = "registry+https://github.com/rust-lang/crates.io-index" 498 | 499 | [[package]] 500 | name = "serde" 501 | version = "1.0.79" 502 | source = "registry+https://github.com/rust-lang/crates.io-index" 503 | 504 | [[package]] 505 | name = "serde_derive" 506 | version = "1.0.79" 507 | source = "registry+https://github.com/rust-lang/crates.io-index" 508 | dependencies = [ 509 | "proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)", 510 | "quote 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", 511 | "syn 0.15.9 (registry+https://github.com/rust-lang/crates.io-index)", 512 | ] 513 | 514 | [[package]] 515 | name = "serde_json" 516 | version = "1.0.32" 517 | source = "registry+https://github.com/rust-lang/crates.io-index" 518 | dependencies = [ 519 | "itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", 520 | "ryu 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", 521 | "serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", 522 | ] 523 | 524 | [[package]] 525 | name = "smallvec" 526 | version = "0.6.5" 527 | source = "registry+https://github.com/rust-lang/crates.io-index" 528 | dependencies = [ 529 | "unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 530 | ] 531 | 532 | [[package]] 533 | name = "stable_deref_trait" 534 | version = "1.1.1" 535 | source = "registry+https://github.com/rust-lang/crates.io-index" 536 | 537 | [[package]] 538 | name = "strsim" 539 | version = "0.7.0" 540 | source = "registry+https://github.com/rust-lang/crates.io-index" 541 | 542 | [[package]] 543 | name = "syn" 544 | version = "0.14.9" 545 | source = "registry+https://github.com/rust-lang/crates.io-index" 546 | dependencies = [ 547 | "proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)", 548 | "quote 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", 549 | "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 550 | ] 551 | 552 | [[package]] 553 | name = "syn" 554 | version = "0.15.9" 555 | source = "registry+https://github.com/rust-lang/crates.io-index" 556 | dependencies = [ 557 | "proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)", 558 | "quote 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", 559 | "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 560 | ] 561 | 562 | [[package]] 563 | name = "synstructure" 564 | version = "0.9.0" 565 | source = "registry+https://github.com/rust-lang/crates.io-index" 566 | dependencies = [ 567 | "proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)", 568 | "quote 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", 569 | "syn 0.14.9 (registry+https://github.com/rust-lang/crates.io-index)", 570 | "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 571 | ] 572 | 573 | [[package]] 574 | name = "termion" 575 | version = "1.5.1" 576 | source = "registry+https://github.com/rust-lang/crates.io-index" 577 | dependencies = [ 578 | "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", 579 | "redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", 580 | "redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 581 | ] 582 | 583 | [[package]] 584 | name = "textwrap" 585 | version = "0.10.0" 586 | source = "registry+https://github.com/rust-lang/crates.io-index" 587 | dependencies = [ 588 | "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 589 | ] 590 | 591 | [[package]] 592 | name = "thread_local" 593 | version = "0.3.6" 594 | source = "registry+https://github.com/rust-lang/crates.io-index" 595 | dependencies = [ 596 | "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 597 | ] 598 | 599 | [[package]] 600 | name = "time" 601 | version = "0.1.40" 602 | source = "registry+https://github.com/rust-lang/crates.io-index" 603 | dependencies = [ 604 | "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", 605 | "redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", 606 | "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", 607 | ] 608 | 609 | [[package]] 610 | name = "tree-sitter" 611 | version = "0.3.1" 612 | dependencies = [ 613 | "cc 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)", 614 | "serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", 615 | "serde_derive 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", 616 | "serde_json 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)", 617 | ] 618 | 619 | [[package]] 620 | name = "tree-tags" 621 | version = "0.1.0" 622 | dependencies = [ 623 | "clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)", 624 | "dirs 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", 625 | "ignore 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", 626 | "libloading 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", 627 | "rusqlite 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", 628 | "serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", 629 | "serde_derive 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", 630 | "serde_json 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)", 631 | "tree-sitter 0.3.1", 632 | ] 633 | 634 | [[package]] 635 | name = "ucd-util" 636 | version = "0.1.1" 637 | source = "registry+https://github.com/rust-lang/crates.io-index" 638 | 639 | [[package]] 640 | name = "unicode-width" 641 | version = "0.1.5" 642 | source = "registry+https://github.com/rust-lang/crates.io-index" 643 | 644 | [[package]] 645 | name = "unicode-xid" 646 | version = "0.1.0" 647 | source = "registry+https://github.com/rust-lang/crates.io-index" 648 | 649 | [[package]] 650 | name = "unreachable" 651 | version = "1.0.0" 652 | source = "registry+https://github.com/rust-lang/crates.io-index" 653 | dependencies = [ 654 | "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 655 | ] 656 | 657 | [[package]] 658 | name = "utf8-ranges" 659 | version = "1.0.1" 660 | source = "registry+https://github.com/rust-lang/crates.io-index" 661 | 662 | [[package]] 663 | name = "vcpkg" 664 | version = "0.2.6" 665 | source = "registry+https://github.com/rust-lang/crates.io-index" 666 | 667 | [[package]] 668 | name = "vec_map" 669 | version = "0.8.1" 670 | source = "registry+https://github.com/rust-lang/crates.io-index" 671 | 672 | [[package]] 673 | name = "version_check" 674 | version = "0.1.5" 675 | source = "registry+https://github.com/rust-lang/crates.io-index" 676 | 677 | [[package]] 678 | name = "void" 679 | version = "1.0.2" 680 | source = "registry+https://github.com/rust-lang/crates.io-index" 681 | 682 | [[package]] 683 | name = "walkdir" 684 | version = "2.2.5" 685 | source = "registry+https://github.com/rust-lang/crates.io-index" 686 | dependencies = [ 687 | "same-file 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", 688 | "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", 689 | "winapi-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 690 | ] 691 | 692 | [[package]] 693 | name = "winapi" 694 | version = "0.3.6" 695 | source = "registry+https://github.com/rust-lang/crates.io-index" 696 | dependencies = [ 697 | "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 698 | "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 699 | ] 700 | 701 | [[package]] 702 | name = "winapi-i686-pc-windows-gnu" 703 | version = "0.4.0" 704 | source = "registry+https://github.com/rust-lang/crates.io-index" 705 | 706 | [[package]] 707 | name = "winapi-util" 708 | version = "0.1.1" 709 | source = "registry+https://github.com/rust-lang/crates.io-index" 710 | dependencies = [ 711 | "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", 712 | ] 713 | 714 | [[package]] 715 | name = "winapi-x86_64-pc-windows-gnu" 716 | version = "0.4.0" 717 | source = "registry+https://github.com/rust-lang/crates.io-index" 718 | 719 | [metadata] 720 | "checksum aho-corasick 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)" = "68f56c7353e5a9547cbd76ed90f7bb5ffc3ba09d4ea9bd1d8c06c8b1142eeb5a" 721 | "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" 722 | "checksum argon2rs 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3f67b0b6a86dae6e67ff4ca2b6201396074996379fba2b92ff649126f37cb392" 723 | "checksum arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a1e964f9e24d588183fcb43503abda40d288c8657dfc27311516ce2f05675aef" 724 | "checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652" 725 | "checksum backtrace 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "89a47830402e9981c5c41223151efcced65a0510c13097c769cede7efb34782a" 726 | "checksum backtrace-sys 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)" = "c66d56ac8dabd07f6aacdaf633f4b8262f5b3601a810a0dcddffd5c22c69daa0" 727 | "checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" 728 | "checksum blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "5d6d530bdd2d52966a6d03b7a964add7ae1a288d25214066fd4b600f0f796400" 729 | "checksum cc 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)" = "f159dfd43363c4d08055a07703eb7a3406b0dac4d0584d96965a3262db3c9d16" 730 | "checksum cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0c4e7bb64a8ebb0d856483e1e682ea3422f883c5f5615a90d51a2c82fe87fdd3" 731 | "checksum clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b957d88f4b6a63b9d70d5f454ac8011819c6efa7727858f458ab71c756ce2d3e" 732 | "checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" 733 | "checksum constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8ff012e225ce166d4422e0e78419d901719760f62ae2b7969ca6b564d1b54a9e" 734 | "checksum crossbeam-channel 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7b85741761b7f160bc5e7e0c14986ef685b7f8bf9b7ad081c60c604bb4649827" 735 | "checksum crossbeam-epoch 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9c90f1474584f38e270b5b613e898c8c328aa4f3dea85e0a27ac2e642f009416" 736 | "checksum crossbeam-utils 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "677d453a17e8bd2b913fa38e8b9cf04bcdbb5be790aa294f2389661d72036015" 737 | "checksum dirs 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "88972de891f6118092b643d85a0b28e0678e0f948d7f879aa32f2d5aafe97d2a" 738 | "checksum failure 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7efb22686e4a466b1ec1a15c2898f91fa9cb340452496dca654032de20ff95b9" 739 | "checksum failure_derive 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "946d0e98a50d9831f5d589038d2ca7f8f455b1c21028c0db0e84116a12696426" 740 | "checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" 741 | "checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" 742 | "checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" 743 | "checksum globset 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4743617a7464bbda3c8aec8558ff2f9429047e025771037df561d383337ff865" 744 | "checksum ignore 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "36ecfc5ad80f0b1226df948c562e2cddd446096be3f644c95106400eae8a5e01" 745 | "checksum itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1306f3464951f30e30d12373d31c79fbd52d236e5e896fd92f96ec7babbbe60b" 746 | "checksum lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca488b89a5657b0a2ecd45b95609b3e848cf1755da332a0da46e2b2b1cb371a7" 747 | "checksum libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)" = "76e3a3ef172f1a0b9a9ff0dd1491ae5e6c948b94479a3021819ba7d860c8645d" 748 | "checksum libloading 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9c3ad660d7cb8c5822cd83d10897b0f1f1526792737a179e73896152f85b88c2" 749 | "checksum libsqlite3-sys 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d3711dfd91a1081d2458ad2d06ea30a8755256e74038be2ad927d94e1c955ca8" 750 | "checksum linked-hash-map 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7860ec297f7008ff7a1e3382d7f7e1dcd69efc94751a2284bafc3d013c2aa939" 751 | "checksum lock_api 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "775751a3e69bde4df9b38dd00a1b5d6ac13791e4223d4a0506577f0dd27cfb7a" 752 | "checksum log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fcce5fa49cc693c312001daf1d13411c4a5283796bac1084299ea3e567113f" 753 | "checksum lru-cache 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4d06ff7ff06f729ce5f4e227876cb88d10bc59cd4ae1e09fbb2bde15c850dc21" 754 | "checksum memchr 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4b3629fe9fdbff6daa6c33b90f7c08355c1aca05a3d01fa8063b822fcf185f3b" 755 | "checksum memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0f9dc261e2b62d7a622bf416ea3c5245cdd5d9a7fcc428c0d06804dfce1775b3" 756 | "checksum nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "9a2228dca57108069a5262f2ed8bd2e82496d2e074a06d1ccc7ce1687b6ae0a2" 757 | "checksum owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf84f41639e037b484f93433aa3897863b561ed65c6e59c7073d7c561710f37" 758 | "checksum parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f0802bff09003b291ba756dc7e79313e51cc31667e94afbe847def490424cde5" 759 | "checksum parking_lot_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ad7f7e6ebdc79edff6fdcb87a55b620174f7a989e3eb31b65231f4af57f00b8c" 760 | "checksum pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "676e8eb2b1b4c9043511a9b7bea0915320d7e502b0a079fb03f9635a5252b18c" 761 | "checksum proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)" = "3d7b7eaaa90b4a90a932a9ea6666c95a389e424eff347f0f793979289429feee" 762 | "checksum quote 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)" = "dd636425967c33af890042c483632d33fa7a18f19ad1d7ea72e8998c6ef8dea5" 763 | "checksum rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8356f47b32624fef5b3301c1be97e5944ecdd595409cc5da11d05f211db6cfbd" 764 | "checksum rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e464cd887e869cddcae8792a4ee31d23c7edd516700695608f5b98c67ee0131c" 765 | "checksum rand_core 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1961a422c4d189dfb50ffa9320bf1f2a9bd54ecb92792fb9477f99a1045f3372" 766 | "checksum rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0905b6b7079ec73b314d4c748701f6931eb79fd97c668caa3f1899b22b32c6db" 767 | "checksum redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "c214e91d3ecf43e9a4e41e578973adeb14b474f2bee858742d127af75a0112b1" 768 | "checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" 769 | "checksum redox_users 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "214a97e49be64fd2c86f568dd0cb2c757d2cc53de95b273b6ad0a1c908482f26" 770 | "checksum regex 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "2069749032ea3ec200ca51e4a31df41759190a88edca0d2d86ee8bedf7073341" 771 | "checksum regex-syntax 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "747ba3b235651f6e2f67dfa8bcdcd073ddb7c243cb21c442fc12395dfcac212d" 772 | "checksum rusqlite 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c9d9118f1ce84d8d0b67f9779936432fb42bb620cef2122409d786892cce9a3c" 773 | "checksum rustc-demangle 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "bcfe5b13211b4d78e5c2cadfebd7769197d95c639c35a50057eb4c05de811395" 774 | "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" 775 | "checksum ryu 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7153dd96dade874ab973e098cb62fcdbb89a03682e46b144fd09550998d4a4a7" 776 | "checksum same-file 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "10f7794e2fda7f594866840e95f5c5962e886e228e68b6505885811a94dd728c" 777 | "checksum scoped_threadpool 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8" 778 | "checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" 779 | "checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" 780 | "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" 781 | "checksum serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)" = "84257ccd054dc351472528c8587b4de2dbf0dc0fe2e634030c1a90bfdacebaa9" 782 | "checksum serde_derive 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)" = "31569d901045afbff7a9479f793177fe9259819aff10ab4f89ef69bbc5f567fe" 783 | "checksum serde_json 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)" = "43344e7ce05d0d8280c5940cabb4964bea626aa58b1ec0e8c73fa2a8512a38ce" 784 | "checksum smallvec 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "153ffa32fd170e9944f7e0838edf824a754ec4c1fc64746fcc9fe1f8fa602e5d" 785 | "checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" 786 | "checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550" 787 | "checksum syn 0.14.9 (registry+https://github.com/rust-lang/crates.io-index)" = "261ae9ecaa397c42b960649561949d69311f08eeaea86a65696e6e46517cf741" 788 | "checksum syn 0.15.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b10ee269228fb723234fce98e9aac0eaed2bd5f1ad2f6930e8d5b93f04445a1a" 789 | "checksum synstructure 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "85bb9b7550d063ea184027c9b8c20ac167cd36d3e06b3a40bceb9d746dc1a7b7" 790 | "checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096" 791 | "checksum textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "307686869c93e71f94da64286f9a9524c0f308a9e1c87a583de8e9c9039ad3f6" 792 | "checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" 793 | "checksum time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "d825be0eb33fda1a7e68012d51e9c7f451dc1a69391e7fdc197060bb8c56667b" 794 | "checksum ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fd2be2d6639d0f8fe6cdda291ad456e23629558d466e2789d2c3e9892bda285d" 795 | "checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526" 796 | "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" 797 | "checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" 798 | "checksum utf8-ranges 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fd70f467df6810094968e2fce0ee1bd0e87157aceb026a8c083bcf5e25b9efe4" 799 | "checksum vcpkg 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "def296d3eb3b12371b2c7d0e83bfe1403e4db2d7a0bba324a12b21c4ee13143d" 800 | "checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" 801 | "checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" 802 | "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" 803 | "checksum walkdir 2.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "af464bc7be7b785c7ac72e266a6b67c4c9070155606f51655a650a6686204e35" 804 | "checksum winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92c1eb33641e276cfa214a0522acad57be5c56b10cb348b3c5117db75f3ac4b0" 805 | "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 806 | "checksum winapi-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "afc5508759c5bf4285e61feb862b6083c8480aec864fa17a81fdec6f69b461ab" 807 | "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 808 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tree-tags" 3 | version = "0.1.0" 4 | authors = ["Max Brunsfeld "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | clap = "2.32" 9 | dirs = "1.0.2" 10 | ignore = "0.4.4" 11 | libloading = "0.5" 12 | rusqlite = "0.14.0" 13 | serde = "1.0" 14 | serde_derive = "1.0" 15 | serde_json = "1.0" 16 | tree-sitter = "0.3.1" 17 | 18 | [patch.crates-io] 19 | tree-sitter = { path = "../rust-tree-sitter" } 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Max Brunsfeld 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 | Tree-tags 2 | ========= 3 | 4 | Tree-tags is a code search tool, similar to [Ctags](http://ctags.sourceforge.net/) but with greater accuracy thanks to syntax trees provided by [Tree-sitter](http://tree-sitter.github.io/tree-sitter). 5 | -------------------------------------------------------------------------------- /src/crawler.rs: -------------------------------------------------------------------------------- 1 | use crate::language_registry::LanguageRegistry; 2 | use crate::store::{Store, StoreFile}; 3 | use ignore::{WalkBuilder, WalkState}; 4 | use std::collections::HashMap; 5 | use std::fmt; 6 | use std::fs::File; 7 | use std::io::{self, Read}; 8 | use std::path::{Path, PathBuf}; 9 | use std::sync::{Arc, Mutex}; 10 | use tree_sitter::{Language, Parser, Point, PropertySheet, Tree, TreePropertyCursor}; 11 | 12 | pub struct DirCrawler { 13 | store: Store, 14 | language_registry: Arc>, 15 | parser: Parser, 16 | languages_by_extension: HashMap)>, 17 | } 18 | 19 | struct TreeCrawler<'a> { 20 | store: StoreFile<'a>, 21 | scope_stack: Vec>, 22 | module_stack: Vec>, 23 | property_matcher: TreePropertyCursor<'a>, 24 | source_code: &'a str, 25 | } 26 | 27 | struct Definition<'a> { 28 | name: Option<(&'a str, Point)>, 29 | kind: Option<&'a str>, 30 | start_position: Point, 31 | end_position: Point, 32 | } 33 | 34 | struct Module<'a> { 35 | name: Option<&'a str>, 36 | definitions: Vec>, 37 | pending_definition_stack: Vec>, 38 | } 39 | 40 | struct Scope<'a> { 41 | kind: Option<&'a str>, 42 | local_refs: Vec<(&'a str, Point)>, 43 | local_defs: Vec<(&'a str, Point)>, 44 | hoisted_local_defs: HashMap<&'a str, Point>, 45 | } 46 | 47 | #[derive(Debug)] 48 | pub enum Error { 49 | IO(io::Error), 50 | Ignore(ignore::Error), 51 | SQL(rusqlite::Error), 52 | } 53 | 54 | pub type Result = core::result::Result; 55 | 56 | impl<'a> TreeCrawler<'a> { 57 | fn new( 58 | store: StoreFile<'a>, 59 | tree: &'a Tree, 60 | property_sheet: &'a PropertySheet, 61 | source_code: &'a str, 62 | ) -> Self { 63 | Self { 64 | store, 65 | source_code, 66 | property_matcher: tree.walk_with_properties(property_sheet), 67 | scope_stack: Vec::new(), 68 | module_stack: Vec::new(), 69 | } 70 | } 71 | 72 | fn crawl_tree(&mut self) -> Result<()> { 73 | self.push_scope(None); 74 | self.push_module(); 75 | let mut visited_node = false; 76 | loop { 77 | if visited_node { 78 | if self.property_matcher.goto_next_sibling() { 79 | self.enter_node()?; 80 | visited_node = false; 81 | } else if self.property_matcher.goto_parent() { 82 | self.leave_node()?; 83 | } else { 84 | break; 85 | } 86 | } else if self.property_matcher.goto_first_child() { 87 | self.enter_node()?; 88 | } else { 89 | visited_node = true; 90 | } 91 | } 92 | self.pop_module()?; 93 | self.pop_scope()?; 94 | Ok(()) 95 | } 96 | 97 | fn enter_node(&mut self) -> Result<()> { 98 | let node = self.property_matcher.node(); 99 | let mut is_local_def = false; 100 | 101 | if self.has_property_value("local-definition", "true") { 102 | is_local_def = true; 103 | let scope_type = self.get_property("scope-type"); 104 | let is_hoisted = self.has_property("local-is-hoisted"); 105 | if let Some(text) = node.utf8_text(self.source_code).ok() { 106 | if is_hoisted { 107 | self.top_scope(scope_type) 108 | .hoisted_local_defs 109 | .insert(text, node.start_position()); 110 | } else { 111 | self.top_scope(scope_type) 112 | .local_defs 113 | .push((text, node.start_position())); 114 | } 115 | } 116 | } 117 | 118 | if self.has_property_value("local-reference", "true") && !is_local_def { 119 | if let Some(text) = node.utf8_text(self.source_code).ok() { 120 | self.top_scope(self.get_property("scope-type")) 121 | .local_refs 122 | .push((text, node.start_position())); 123 | } 124 | } 125 | 126 | if self.has_property_value("local-scope", "true") { 127 | self.push_scope(self.get_property("scope-type")); 128 | } 129 | 130 | if self.has_property_value("module", "true") { 131 | self.push_module(); 132 | } 133 | 134 | match self.get_property("module-part") { 135 | Some("name") => { 136 | if let Some(text) = node.utf8_text(self.source_code).ok() { 137 | let module = self.module_stack.last_mut().unwrap(); 138 | module.name = Some(text); 139 | } 140 | } 141 | _ => {} 142 | } 143 | 144 | if self.has_property_value("definition", "true") { 145 | let kind = self.get_property("definition-type"); 146 | self.top_module().pending_definition_stack.push(Definition { 147 | name: None, 148 | kind, 149 | start_position: node.start_position(), 150 | end_position: node.end_position(), 151 | }); 152 | } 153 | 154 | match self.get_property("definition-part") { 155 | Some("name") => { 156 | if let Some(text) = node.utf8_text(self.source_code).ok() { 157 | let def = self.top_definition().unwrap(); 158 | if def.name.is_none() { 159 | def.name = Some((text, node.start_position())); 160 | } 161 | } 162 | } 163 | Some("value") => { 164 | let kind = self.get_property("definition-type"); 165 | if kind.is_some() { 166 | self.top_definition().unwrap().kind = kind; 167 | } 168 | } 169 | _ => {} 170 | } 171 | 172 | if self.has_property_value("reference", "true") && !is_local_def { 173 | if let Some(text) = node.utf8_text(self.source_code).ok() { 174 | self.store.insert_ref( 175 | text, 176 | node.start_position(), 177 | self.get_property("reference-type"), 178 | )?; 179 | } 180 | } 181 | 182 | Ok(()) 183 | } 184 | 185 | fn leave_node(&mut self) -> Result<()> { 186 | if self.has_property("local-scope") { 187 | self.pop_scope()?; 188 | } 189 | 190 | if self.has_property("definition") { 191 | self.pop_definition()?; 192 | } 193 | 194 | if self.has_property("module") { 195 | self.pop_module()?; 196 | } 197 | 198 | Ok(()) 199 | } 200 | 201 | fn top_scope(&mut self, kind: Option<&'a str>) -> &mut Scope<'a> { 202 | self.scope_stack 203 | .iter_mut() 204 | .enumerate() 205 | .rev() 206 | .find_map(|(i, scope)| { 207 | if i == 0 || kind.map_or(true, |kind| Some(kind) == scope.kind) { 208 | Some(scope) 209 | } else { 210 | None 211 | } 212 | }).unwrap() 213 | } 214 | 215 | fn top_module(&mut self) -> &mut Module<'a> { 216 | self.module_stack.last_mut().unwrap() 217 | } 218 | 219 | fn top_definition(&mut self) -> Option<&mut Definition<'a>> { 220 | self.top_module().pending_definition_stack.last_mut() 221 | } 222 | 223 | fn push_scope(&mut self, kind: Option<&'a str>) { 224 | self.scope_stack.push(Scope { 225 | kind, 226 | local_refs: Vec::new(), 227 | local_defs: Vec::new(), 228 | hoisted_local_defs: HashMap::new(), 229 | }); 230 | } 231 | 232 | fn pop_scope(&mut self) -> Result<()> { 233 | let mut scope = self.scope_stack.pop().unwrap(); 234 | 235 | let mut local_def_ids = Vec::with_capacity(scope.local_defs.len()); 236 | for (name, position) in scope.local_defs.iter() { 237 | local_def_ids.push(self.store.insert_local_def(name, *position)?); 238 | } 239 | 240 | let mut hoisted_local_def_ids = HashMap::new(); 241 | for (name, position) in scope.hoisted_local_defs.iter() { 242 | hoisted_local_def_ids.insert(name, self.store.insert_local_def(name, *position)?); 243 | } 244 | 245 | let mut parent_scope = self.scope_stack.pop(); 246 | for local_ref in scope.local_refs.drain(..) { 247 | let mut local_def_id = None; 248 | 249 | for (i, local_def) in scope.local_defs.iter().enumerate() { 250 | if local_def.1 > local_ref.1 { 251 | break; 252 | } 253 | if local_def.0 == local_ref.0 { 254 | local_def_id = Some(local_def_ids[i]); 255 | } 256 | } 257 | 258 | if local_def_id.is_none() { 259 | local_def_id = hoisted_local_def_ids.get(&local_ref.0).cloned(); 260 | } 261 | 262 | if let Some(local_def_id) = local_def_id { 263 | self.store 264 | .insert_local_ref(local_def_id, local_ref.0, local_ref.1)?; 265 | } else if let Some(parent_scope) = parent_scope.as_mut() { 266 | parent_scope.local_refs.push(local_ref); 267 | } 268 | } 269 | parent_scope.map(|scope| self.scope_stack.push(scope)); 270 | 271 | Ok(()) 272 | } 273 | 274 | fn push_module(&mut self) { 275 | self.module_stack.push(Module { 276 | name: None, 277 | definitions: Vec::new(), 278 | pending_definition_stack: Vec::new(), 279 | }); 280 | } 281 | 282 | fn pop_module(&mut self) -> Result<()> { 283 | let mod_path = self 284 | .module_stack 285 | .iter() 286 | .filter_map(|m| m.name) 287 | .collect::>(); 288 | let module = self.module_stack.pop().unwrap(); 289 | for definition in module.definitions { 290 | if let Some((name, name_position)) = definition.name { 291 | self.store.insert_def( 292 | name, 293 | name_position, 294 | definition.start_position, 295 | definition.end_position, 296 | definition.kind, 297 | &mod_path, 298 | )?; 299 | } 300 | } 301 | Ok(()) 302 | } 303 | 304 | fn pop_definition(&mut self) -> Result<()> { 305 | let module = self.module_stack.last_mut().unwrap(); 306 | let definition = module.pending_definition_stack.pop().unwrap(); 307 | module.definitions.push(definition); 308 | Ok(()) 309 | } 310 | 311 | fn get_property(&self, prop: &'static str) -> Option<&'a str> { 312 | self.property_matcher 313 | .node_properties() 314 | .get(prop) 315 | .map(|v| v.as_str()) 316 | } 317 | 318 | fn has_property(&self, prop: &'static str) -> bool { 319 | self.get_property(prop).is_some() 320 | } 321 | 322 | fn has_property_value(&self, prop: &'static str, value: &'static str) -> bool { 323 | self.get_property(prop) == Some(value) 324 | } 325 | } 326 | 327 | impl DirCrawler { 328 | pub fn new(store: Store, language_registry: LanguageRegistry) -> Self { 329 | Self { 330 | store: store, 331 | language_registry: Arc::new(Mutex::new(language_registry)), 332 | parser: Parser::new(), 333 | languages_by_extension: HashMap::new(), 334 | } 335 | } 336 | 337 | fn clone(&self) -> Result { 338 | Ok(Self { 339 | store: self.store.clone()?, 340 | language_registry: self.language_registry.clone(), 341 | parser: Parser::new(), 342 | languages_by_extension: self.languages_by_extension.clone(), 343 | }) 344 | } 345 | 346 | pub fn crawl_path(&mut self, path: PathBuf) -> Result<()> { 347 | let last_error = Arc::new(Mutex::new(Ok(()))); 348 | 349 | WalkBuilder::new(path).build_parallel().run(|| { 350 | let last_error = last_error.clone(); 351 | match self.clone() { 352 | Ok(mut crawler) => Box::new({ 353 | move |entry| { 354 | match entry { 355 | Ok(entry) => { 356 | if let Some(t) = entry.file_type() { 357 | if t.is_file() { 358 | if let Err(e) = crawler.crawl_file(entry.path()) { 359 | *last_error.lock().unwrap() = Err(e); 360 | return WalkState::Quit; 361 | } 362 | } 363 | } 364 | } 365 | Err(e) => { 366 | *last_error.lock().unwrap() = Err(e.into()); 367 | } 368 | } 369 | WalkState::Continue 370 | } 371 | }), 372 | Err(error) => { 373 | *last_error.lock().unwrap() = Err(error.into()); 374 | Box::new(|_| WalkState::Quit) 375 | } 376 | } 377 | }); 378 | 379 | Arc::try_unwrap(last_error).unwrap().into_inner().unwrap() 380 | } 381 | 382 | fn crawl_file(&mut self, path: &Path) -> Result<()> { 383 | let mut file = File::open(path)?; 384 | if let Some(extension) = path.extension().and_then(|e| e.to_str()) { 385 | let language; 386 | let property_sheet; 387 | 388 | if let Some((l, p)) = self.languages_by_extension.get(extension) { 389 | language = *l; 390 | property_sheet = p.clone(); 391 | } else if let Some((l, p)) = self 392 | .language_registry 393 | .lock() 394 | .unwrap() 395 | .language_for_file_extension(extension)? 396 | { 397 | self.languages_by_extension.insert(extension.to_owned(), (l, p.clone())); 398 | language = l; 399 | property_sheet = p; 400 | } else { 401 | return Ok(()); 402 | } 403 | 404 | self.parser 405 | .set_language(language) 406 | .expect("Incompatible language version"); 407 | let mut source_code = String::new(); 408 | file.read_to_string(&mut source_code)?; 409 | let tree = self 410 | .parser 411 | .parse_str(&source_code, None) 412 | .expect("Parsing failed"); 413 | let store = self.store.file(path)?; 414 | let mut crawler = TreeCrawler::new(store, &tree, &property_sheet, &source_code); 415 | crawler.crawl_tree()?; 416 | crawler.store.commit()?; 417 | } 418 | Ok(()) 419 | } 420 | } 421 | 422 | impl fmt::Display for Error { 423 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 424 | match self { 425 | Error::IO(e) => e.fmt(f), 426 | Error::SQL(e) => e.fmt(f), 427 | Error::Ignore(e) => e.fmt(f), 428 | } 429 | } 430 | } 431 | 432 | impl std::error::Error for Error {} 433 | 434 | impl From for Error { 435 | fn from(e: io::Error) -> Error { 436 | Error::IO(e) 437 | } 438 | } 439 | 440 | impl From for Error { 441 | fn from(e: ignore::Error) -> Error { 442 | Error::Ignore(e) 443 | } 444 | } 445 | 446 | impl From for Error { 447 | fn from(e: rusqlite::Error) -> Error { 448 | Error::SQL(e) 449 | } 450 | } 451 | -------------------------------------------------------------------------------- /src/language_registry.rs: -------------------------------------------------------------------------------- 1 | use libloading::{Library, Symbol}; 2 | use std::collections::HashMap; 3 | use std::fs::{self, File}; 4 | use std::io::{self, Read}; 5 | use std::path::{Path, PathBuf}; 6 | use std::process::Command; 7 | use std::sync::Arc; 8 | use tree_sitter::{Language, PropertySheet}; 9 | 10 | const PACKAGE_JSON_PATH: &'static str = "package.json"; 11 | const PARSER_C_PATH: &'static str = "src/parser.c"; 12 | const SCANNER_C_PATH: &'static str = "src/scanner.c"; 13 | const SCANNER_CC_PATH: &'static str = "src/scanner.cc"; 14 | const DEFINITIONS_JSON_PATH: &'static str = "src/definitions.json"; 15 | 16 | #[cfg(unix)] 17 | const DYLIB_EXTENSION: &'static str = "so"; 18 | 19 | #[cfg(windows)] 20 | const DYLIB_EXTENSION: &'static str = "dll"; 21 | 22 | pub struct LanguageRegistry { 23 | parser_src_paths: Vec, 24 | parser_lib_path: PathBuf, 25 | language_names_by_extension: HashMap, 26 | loaded_languages: HashMap)>, 27 | } 28 | 29 | unsafe impl Send for LanguageRegistry {} 30 | unsafe impl Sync for LanguageRegistry {} 31 | 32 | impl LanguageRegistry { 33 | pub fn new(parser_lib_path: PathBuf, parser_src_paths: Vec) -> Self { 34 | LanguageRegistry { 35 | parser_lib_path, 36 | parser_src_paths, 37 | language_names_by_extension: HashMap::new(), 38 | loaded_languages: HashMap::new(), 39 | } 40 | } 41 | 42 | pub fn load_parsers(&mut self) -> io::Result<()> { 43 | for parser_container_dir in self.parser_src_paths.iter() { 44 | for entry in fs::read_dir(parser_container_dir)? { 45 | let entry = entry?; 46 | if let Some(parser_dir_name) = entry.file_name().to_str() { 47 | if parser_dir_name.starts_with("tree-sitter-") { 48 | let name = parser_dir_name.split_at("tree-sitter-".len()).1; 49 | let language_path = entry.path(); 50 | match file_extensions_for_language_path(&language_path) { 51 | Ok(None) => {}, 52 | Ok(Some(extensions)) => { 53 | for extension in extensions { 54 | self.language_names_by_extension.insert( 55 | extension.to_owned(), 56 | (name.to_owned(), entry.path()) 57 | ); 58 | } 59 | }, 60 | Err(e) => { 61 | eprintln!("{}: {}", parser_dir_name, e); 62 | } 63 | } 64 | } 65 | } 66 | } 67 | } 68 | Ok(()) 69 | } 70 | 71 | pub fn language_for_file_extension(&mut self, extension: &str) -> io::Result)>> { 72 | if let Some((name, path)) = self.language_names_by_extension.get(extension) { 73 | if let Some((_, language, sheet)) = self.loaded_languages.get(name) { 74 | return Ok(Some((*language, sheet.clone()))); 75 | } 76 | self.load_language_at_path(&name.clone(), &path.clone()) 77 | } else { 78 | Ok(None) 79 | } 80 | } 81 | 82 | fn load_language_at_path( 83 | &mut self, 84 | name: &str, 85 | language_path: &Path, 86 | ) -> io::Result)>> { 87 | let parser_c_path = language_path.join(PARSER_C_PATH); 88 | let mut library_path = self.parser_lib_path.join(name); 89 | library_path.set_extension(DYLIB_EXTENSION); 90 | 91 | if !library_path.exists() || was_modified_more_recently(&parser_c_path, &library_path)? { 92 | let compiler_name = std::env::var("CXX").unwrap_or("c++".to_owned()); 93 | let mut command = Command::new(compiler_name); 94 | command 95 | .arg("-shared") 96 | .arg("-fPIC") 97 | .arg("-I") 98 | .arg(language_path.join("src")) 99 | .arg("-o") 100 | .arg(&library_path) 101 | .arg("-xc") 102 | .arg(parser_c_path); 103 | let scanner_c_path = language_path.join(SCANNER_C_PATH); 104 | let scanner_cc_path = language_path.join(SCANNER_CC_PATH); 105 | if scanner_c_path.exists() { 106 | command.arg("-xc").arg(scanner_c_path); 107 | } else if scanner_cc_path.exists() { 108 | command.arg("-xc++").arg(scanner_cc_path); 109 | } 110 | command.output()?; 111 | } 112 | 113 | let library = Library::new(library_path)?; 114 | let language_fn_name = "tree_sitter_".to_owned() + name; 115 | let language = unsafe { 116 | let language_fn: Symbol Language> = 117 | library.get(language_fn_name.as_bytes())?; 118 | language_fn() 119 | }; 120 | 121 | let mut property_sheet_string = String::new(); 122 | let mut property_sheet_file = File::open(language_path.join(DEFINITIONS_JSON_PATH))?; 123 | property_sheet_file.read_to_string(&mut property_sheet_string)?; 124 | let property_sheet = Arc::new(PropertySheet::new(language, &property_sheet_string)?); 125 | self.loaded_languages.insert(name.to_string(), (library, language, property_sheet.clone())); 126 | Ok(Some((language, property_sheet))) 127 | } 128 | } 129 | 130 | fn file_extensions_for_language_path(path: &Path) -> io::Result>> { 131 | #[derive(Deserialize)] 132 | struct TreeSitterJSON { 133 | #[serde(rename = "file-types")] 134 | file_types: Option> 135 | } 136 | 137 | #[derive(Deserialize)] 138 | struct PackageJSON { 139 | #[serde(rename = "tree-sitter")] 140 | tree_sitter: Option 141 | } 142 | 143 | let mut package_json_contents = String::new(); 144 | let mut package_json_file = File::open(path.join(PACKAGE_JSON_PATH))?; 145 | package_json_file.read_to_string(&mut package_json_contents)?; 146 | let package_json: PackageJSON = serde_json::from_str(&package_json_contents)?; 147 | Ok(package_json.tree_sitter.and_then(|t| t.file_types)) 148 | } 149 | 150 | fn was_modified_more_recently(a: &Path, b: &Path) -> io::Result { 151 | Ok(fs::metadata(a)?.modified()? > fs::metadata(b)?.modified()?) 152 | } 153 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate serde_derive; 3 | 4 | mod crawler; 5 | mod language_registry; 6 | mod store; 7 | 8 | use std::io; 9 | use std::path::PathBuf; 10 | use clap::{App, Arg, SubCommand}; 11 | use tree_sitter::Point; 12 | 13 | fn main() -> crawler::Result<()> { 14 | let matches = App::new("Tree-tags") 15 | .version("0.1") 16 | .author("Max Brunsfeld ") 17 | .about("Indexes code") 18 | .subcommand( 19 | SubCommand::with_name("index") 20 | .about("Index a directory of source code") 21 | .arg(Arg::with_name("path").index(1)), 22 | ).subcommand( 23 | SubCommand::with_name("clear-index") 24 | .about("Clear the index for a directory of source code") 25 | .arg(Arg::with_name("path").index(1)), 26 | ).subcommand( 27 | SubCommand::with_name("find-definition") 28 | .about("Find the definition of a symbol") 29 | .arg(Arg::with_name("path").index(1).required(true)) 30 | .arg(Arg::with_name("line").index(2).required(true)) 31 | .arg(Arg::with_name("column").index(3).required(true)), 32 | ).subcommand( 33 | SubCommand::with_name("find-usages") 34 | .about("Find usages of a symbol") 35 | .arg(Arg::with_name("path").index(1).required(true)) 36 | .arg(Arg::with_name("line").index(2).required(true)) 37 | .arg(Arg::with_name("column").index(3).required(true)), 38 | ).get_matches(); 39 | 40 | let config_path = dirs::home_dir().unwrap().join(".config/tree-tags"); 41 | let db_path = config_path.join("db.sqlite"); 42 | let parsers_path = config_path.join("parsers"); 43 | let compiled_parsers_path = config_path.join("parsers-compiled"); 44 | 45 | let mut store = store::Store::new(db_path)?; 46 | let mut language_registry = language_registry::LanguageRegistry::new( 47 | compiled_parsers_path, 48 | vec![parsers_path] 49 | ); 50 | 51 | store 52 | .initialize() 53 | .expect("Failed to initialize database schema"); 54 | 55 | if let Some(matches) = matches.subcommand_matches("index") { 56 | language_registry.load_parsers()?; 57 | let mut crawler = crawler::DirCrawler::new(store, language_registry); 58 | crawler.crawl_path(get_path_arg(matches.value_of("path").unwrap())?)?; 59 | return Ok(()); 60 | } 61 | 62 | if let Some(matches) = matches.subcommand_matches("clear-index") { 63 | store.delete_files(&get_path_arg(matches.value_of("path").unwrap())?)?; 64 | return Ok(()); 65 | } 66 | 67 | if let Some(matches) = matches.subcommand_matches("find-definition") { 68 | let path = get_path_arg(matches.value_of("path").expect("Missing path"))?; 69 | let line_arg = matches.value_of("line").expect("Missing line"); 70 | let column_arg = matches.value_of("column").expect("Missing column"); 71 | let position = Point { 72 | row: u32::from_str_radix(line_arg, 10).expect("Invalid row"), 73 | column: u32::from_str_radix(column_arg, 10).expect("Invalid column"), 74 | }; 75 | for (path, position, length) in store.find_definition(&path, position)? { 76 | println!( 77 | "{} {} {} {}", 78 | path.display(), 79 | position.row, 80 | position.column, 81 | length 82 | ); 83 | } 84 | return Ok(()); 85 | } 86 | 87 | eprintln!("Unknown command"); 88 | Ok(()) 89 | } 90 | 91 | fn get_path_arg(arg: &str) -> io::Result { 92 | std::env::current_dir().and_then(|cwd| cwd.join(arg).canonicalize()) 93 | } 94 | -------------------------------------------------------------------------------- /src/schema.sql: -------------------------------------------------------------------------------- 1 | PRAGMA foreign_keys = ON; 2 | 3 | CREATE TABLE IF NOT EXISTS files ( 4 | id INTEGER NOT NULL PRIMARY KEY, 5 | path TEXT NOT NULL UNIQUE 6 | ); 7 | 8 | CREATE TABLE IF NOT EXISTS local_defs ( 9 | id INTEGER NOT NULL PRIMARY KEY, 10 | file_id INTEGER NOT NULL REFERENCES files (id) ON DELETE CASCADE, 11 | row UNSIGNED INTEGER NOT NULL, 12 | column UNSIGNED INTEGER NOT NULL, 13 | length UNSIGNED INTEGER NOT NULL 14 | ); 15 | 16 | CREATE TABLE IF NOT EXISTS local_refs ( 17 | file_id INTEGER NOT NULL REFERENCES files (id) ON DELETE CASCADE, 18 | definition_id INTEGER NOT NULL REFERENCES local_defs (id) ON DELETE CASCADE, 19 | row UNSIGNED INTEGER NOT NULL, 20 | column UNSIGNED INTEGER NOT NULL, 21 | length UNSIGNED INTEGER NOT NULL, 22 | PRIMARY KEY (file_id, row, column) 23 | ); 24 | 25 | CREATE TABLE IF NOT EXISTS defs ( 26 | file_id INTEGER NOT NULL REFERENCES files (id) ON DELETE CASCADE, 27 | start_row UNSIGNED INTEGER NOT NULL, 28 | start_column UNSIGNED INTEGER NOT NULL, 29 | name_start_row UNSIGNED INTEGER NOT NULL, 30 | name_start_column UNSIGNED INTEGER NOT NULL, 31 | end_row UNSIGNED INTEGER NOT NULL, 32 | end_column UNSIGNED INTEGER NOT NULL, 33 | name TEXT NOT NULL, 34 | kind TEXT NOT NULL, 35 | module_path TEXT NOT NULL, 36 | PRIMARY KEY (file_id, start_row, start_column, end_row, end_column) 37 | ); 38 | 39 | CREATE TABLE IF NOT EXISTS refs ( 40 | file_id INTEGER NOT NULL REFERENCES files (id) ON DELETE CASCADE, 41 | row UNSIGNED INTEGER NOT NULL, 42 | column UNSIGNED INTEGER NOT NULL, 43 | name TEXT NOT NULL, 44 | kind TEXT NOT NULL, 45 | PRIMARY KEY (file_id, row, column) 46 | ); 47 | 48 | CREATE INDEX IF NOT EXISTS file_paths ON files (path); 49 | -------------------------------------------------------------------------------- /src/store.rs: -------------------------------------------------------------------------------- 1 | use rusqlite::{self, Connection, Result, Transaction}; 2 | use std::ffi::OsString; 3 | use std::os::unix::ffi::{OsStrExt, OsStringExt}; 4 | use std::path::{Path, PathBuf}; 5 | use tree_sitter::Point; 6 | use std::thread; 7 | use std::time::Duration; 8 | 9 | pub struct Store { 10 | db: Connection, 11 | path: PathBuf, 12 | } 13 | 14 | pub struct StoreFile<'a> { 15 | file_id: i64, 16 | db: Transaction<'a>, 17 | } 18 | 19 | impl Store { 20 | pub fn new(db_path: PathBuf) -> rusqlite::Result { 21 | let db = Connection::open(&db_path)?; 22 | db.set_prepared_statement_cache_capacity(20); 23 | db.busy_handler(Some(|_| { 24 | thread::sleep(Duration::from_millis(25)); 25 | true 26 | }))?; 27 | Ok(Self { db, path: db_path }) 28 | } 29 | 30 | pub fn clone(&self) -> rusqlite::Result { 31 | Self::new(self.path.clone()) 32 | } 33 | 34 | pub fn initialize(&mut self) -> rusqlite::Result<()> { 35 | self.db.execute_batch(include_str!("./schema.sql")) 36 | } 37 | 38 | pub fn delete_files(&mut self, path: &Path) -> rusqlite::Result<()> { 39 | self.db.execute( 40 | "DELETE FROM files WHERE instr(path, ?1) = 1", 41 | &[&path.as_os_str().as_bytes()] 42 | )?; 43 | Ok(()) 44 | } 45 | 46 | pub fn file(&mut self, path: &Path) -> rusqlite::Result { 47 | let tx = self.db.transaction()?; 48 | { 49 | let mut stmt = tx.prepare_cached("DELETE FROM files WHERE path = ?1")?; 50 | stmt.execute(&[&path.as_os_str().as_bytes()])?; 51 | let mut stmt = tx.prepare_cached("INSERT INTO files (path) VALUES (?1)")?; 52 | stmt.execute(&[&path.as_os_str().as_bytes()])?; 53 | } 54 | let file_id = tx.last_insert_rowid(); 55 | Ok(StoreFile { file_id, db: tx }) 56 | } 57 | 58 | pub fn find_definition( 59 | &mut self, 60 | path: &Path, 61 | position: Point, 62 | ) -> Result> { 63 | let file_id: i64 = self.db.query_row( 64 | "SELECT id FROM files WHERE path = ?1", 65 | &[&path.as_os_str().as_bytes()], 66 | |row| row.get(0), 67 | )?; 68 | 69 | let local_result = self.db.query_row( 70 | " 71 | SELECT 72 | local_defs.row, 73 | local_defs.column, 74 | local_defs.length 75 | FROM 76 | local_refs, 77 | local_defs 78 | WHERE 79 | local_refs.definition_id = local_defs.id AND 80 | local_refs.file_id = ?1 AND 81 | local_refs.row = ?2 AND 82 | local_refs.column <= ?3 AND 83 | local_refs.column + local_refs.length > ?3 84 | ", 85 | &[&file_id, &(position.row as i64), &(position.column as i64)], 86 | |row| { 87 | ( 88 | Point { 89 | row: row.get(0), 90 | column: row.get(1), 91 | }, 92 | row.get::(2), 93 | ) 94 | }, 95 | ); 96 | 97 | match local_result { 98 | Err(rusqlite::Error::QueryReturnedNoRows) => {} 99 | Ok((position, length)) => return Ok(vec![(path.to_owned(), position, length as usize)]), 100 | Err(e) => return Err(e.into()), 101 | } 102 | 103 | let mut statement = self.db.prepare_cached( 104 | " 105 | SELECT 106 | files.path, 107 | defs.name_start_row, 108 | defs.name_start_column, 109 | length(defs.name) 110 | FROM 111 | files, 112 | defs, 113 | refs 114 | WHERE 115 | files.id == defs.file_id AND 116 | defs.name = refs.name AND 117 | refs.file_id = ?1 AND 118 | refs.row = ?2 AND 119 | refs.column <= ?3 AND 120 | refs.column + length(refs.name) > ?3 121 | LIMIT 122 | 50 123 | ", 124 | )?; 125 | 126 | let rows = statement.query_map( 127 | &[&file_id, &(position.row as i64), &(position.column as i64)], 128 | |row| { 129 | ( 130 | OsString::from_vec(row.get::>(0)).into(), 131 | Point::new(row.get(1), row.get(2)), 132 | row.get::(3) as usize, 133 | ) 134 | }, 135 | )?; 136 | 137 | let mut result = Vec::new(); 138 | for row in rows { 139 | result.push(row?); 140 | } 141 | 142 | Ok(result) 143 | } 144 | } 145 | 146 | impl<'a> StoreFile<'a> { 147 | pub fn insert_local_ref( 148 | &mut self, 149 | local_def_id: i64, 150 | name: &'a str, 151 | position: Point, 152 | ) -> Result<()> { 153 | let mut stmt = self.db.prepare_cached( 154 | " 155 | INSERT INTO local_refs 156 | (file_id, definition_id, row, column, length) 157 | VALUES 158 | (?1, ?2, ?3, ?4, ?5) 159 | ", 160 | )?; 161 | stmt.execute(&[ 162 | &self.file_id, 163 | &local_def_id, 164 | &position.row, 165 | &position.column, 166 | &(name.as_bytes().len() as i64), 167 | ])?; 168 | Ok(()) 169 | } 170 | 171 | pub fn insert_local_def(&mut self, name: &'a str, position: Point) -> Result { 172 | let mut stmt = self.db.prepare_cached( 173 | " 174 | INSERT INTO local_defs 175 | (file_id, row, column, length) 176 | VALUES 177 | (?1, ?2, ?3, ?4) 178 | ", 179 | )?; 180 | stmt.execute(&[ 181 | &self.file_id, 182 | &position.row, 183 | &position.column, 184 | &(name.as_bytes().len() as i64), 185 | ])?; 186 | Ok(self.db.last_insert_rowid()) 187 | } 188 | 189 | pub fn insert_ref( 190 | &mut self, 191 | name: &'a str, 192 | position: Point, 193 | kind: Option<&'a str>, 194 | ) -> Result<()> { 195 | let mut stmt = self.db.prepare_cached( 196 | " 197 | INSERT INTO refs 198 | (file_id, name, row, column, kind) 199 | VALUES 200 | (?1, ?2, ?3, ?4, ?5) 201 | ", 202 | )?; 203 | stmt.execute(&[&self.file_id, &name, &position.row, &position.column, &kind])?; 204 | Ok(()) 205 | } 206 | 207 | pub fn insert_def( 208 | &mut self, 209 | name: &'a str, 210 | name_position: Point, 211 | start_position: Point, 212 | end_position: Point, 213 | kind: Option<&'a str>, 214 | module_path: &Vec<&'a str>, 215 | ) -> Result<()> { 216 | let mut module_path_string = String::with_capacity( 217 | module_path 218 | .iter() 219 | .map(|entry| entry.as_bytes().len() + 1) 220 | .sum(), 221 | ); 222 | for entry in module_path { 223 | module_path_string += entry; 224 | module_path_string += "\t"; 225 | } 226 | let mut stmt = self.db.prepare_cached( 227 | " 228 | INSERT INTO defs 229 | ( 230 | file_id, 231 | start_row, start_column, 232 | end_row, end_column, 233 | name, name_start_row, name_start_column, 234 | kind, 235 | module_path 236 | ) 237 | VALUES 238 | (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10) 239 | ", 240 | )?; 241 | stmt.execute(&[ 242 | &self.file_id, 243 | &start_position.row, 244 | &start_position.column, 245 | &end_position.row, 246 | &end_position.column, 247 | &name, 248 | &name_position.row, 249 | &name_position.column, 250 | &kind, 251 | &module_path_string, 252 | ])?; 253 | Ok(()) 254 | } 255 | 256 | pub fn commit(self) -> rusqlite::Result<()> { 257 | self.db.commit() 258 | } 259 | } 260 | --------------------------------------------------------------------------------