├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── assets ├── demo.gif └── logo.png └── src ├── cli.rs ├── highlight.rs ├── main.rs ├── parser.rs ├── types.rs └── vcs ├── git.rs ├── hg.rs └── mod.rs /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | /target/ 3 | **/*.rs.bk 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | sudo: false 3 | rust: 4 | - stable 5 | - beta 6 | - nightly 7 | script: cargo test --all --verbose 8 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## v0.1.2 4 | 5 | - Add more metadata for crates.io. 6 | 7 | ## v0.1.1 8 | 9 | - Fix some slicing issues in cases where the input and terminal size don't 10 | match. 11 | -------------------------------------------------------------------------------- /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.6" 6 | source = "registry+https://github.com/rust-lang/crates.io-index" 7 | dependencies = [ 8 | "memchr 2.2.1 (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.8 (registry+https://github.com/rust-lang/crates.io-index)", 17 | ] 18 | 19 | [[package]] 20 | name = "approx" 21 | version = "0.1.1" 22 | source = "registry+https://github.com/rust-lang/crates.io-index" 23 | 24 | [[package]] 25 | name = "atty" 26 | version = "0.2.13" 27 | source = "registry+https://github.com/rust-lang/crates.io-index" 28 | dependencies = [ 29 | "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", 30 | "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", 31 | ] 32 | 33 | [[package]] 34 | name = "autocfg" 35 | version = "0.1.6" 36 | source = "registry+https://github.com/rust-lang/crates.io-index" 37 | 38 | [[package]] 39 | name = "backtrace" 40 | version = "0.3.37" 41 | source = "registry+https://github.com/rust-lang/crates.io-index" 42 | dependencies = [ 43 | "backtrace-sys 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)", 44 | "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", 45 | "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", 46 | "rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", 47 | ] 48 | 49 | [[package]] 50 | name = "backtrace-sys" 51 | version = "0.1.31" 52 | source = "registry+https://github.com/rust-lang/crates.io-index" 53 | dependencies = [ 54 | "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", 55 | "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", 56 | ] 57 | 58 | [[package]] 59 | name = "bitflags" 60 | version = "1.1.0" 61 | source = "registry+https://github.com/rust-lang/crates.io-index" 62 | 63 | [[package]] 64 | name = "cc" 65 | version = "1.0.45" 66 | source = "registry+https://github.com/rust-lang/crates.io-index" 67 | 68 | [[package]] 69 | name = "cfg-if" 70 | version = "0.1.9" 71 | source = "registry+https://github.com/rust-lang/crates.io-index" 72 | 73 | [[package]] 74 | name = "cgmath" 75 | version = "0.16.1" 76 | source = "registry+https://github.com/rust-lang/crates.io-index" 77 | dependencies = [ 78 | "approx 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 79 | "num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)", 80 | "rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", 81 | ] 82 | 83 | [[package]] 84 | name = "clap" 85 | version = "2.33.0" 86 | source = "registry+https://github.com/rust-lang/crates.io-index" 87 | dependencies = [ 88 | "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", 89 | "atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", 90 | "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 91 | "strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", 92 | "textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", 93 | "unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", 94 | "vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", 95 | ] 96 | 97 | [[package]] 98 | name = "clicolors-control" 99 | version = "1.0.1" 100 | source = "registry+https://github.com/rust-lang/crates.io-index" 101 | dependencies = [ 102 | "atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", 103 | "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 104 | "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", 105 | "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", 106 | ] 107 | 108 | [[package]] 109 | name = "cloudabi" 110 | version = "0.0.3" 111 | source = "registry+https://github.com/rust-lang/crates.io-index" 112 | dependencies = [ 113 | "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 114 | ] 115 | 116 | [[package]] 117 | name = "colored" 118 | version = "1.8.0" 119 | source = "registry+https://github.com/rust-lang/crates.io-index" 120 | dependencies = [ 121 | "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 122 | "winconsole 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", 123 | ] 124 | 125 | [[package]] 126 | name = "console" 127 | version = "0.7.7" 128 | source = "registry+https://github.com/rust-lang/crates.io-index" 129 | dependencies = [ 130 | "atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", 131 | "clicolors-control 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", 132 | "encode_unicode 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", 133 | "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 134 | "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", 135 | "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", 136 | "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 137 | "termios 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 138 | "unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", 139 | "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", 140 | ] 141 | 142 | [[package]] 143 | name = "either" 144 | version = "1.5.2" 145 | source = "registry+https://github.com/rust-lang/crates.io-index" 146 | 147 | [[package]] 148 | name = "encode_unicode" 149 | version = "0.3.6" 150 | source = "registry+https://github.com/rust-lang/crates.io-index" 151 | 152 | [[package]] 153 | name = "exitcode" 154 | version = "1.1.2" 155 | source = "registry+https://github.com/rust-lang/crates.io-index" 156 | 157 | [[package]] 158 | name = "failure" 159 | version = "0.1.5" 160 | source = "registry+https://github.com/rust-lang/crates.io-index" 161 | dependencies = [ 162 | "backtrace 0.3.37 (registry+https://github.com/rust-lang/crates.io-index)", 163 | "failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 164 | ] 165 | 166 | [[package]] 167 | name = "failure_derive" 168 | version = "0.1.5" 169 | source = "registry+https://github.com/rust-lang/crates.io-index" 170 | dependencies = [ 171 | "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", 172 | "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", 173 | "syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)", 174 | "synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", 175 | ] 176 | 177 | [[package]] 178 | name = "fuchsia-cprng" 179 | version = "0.1.1" 180 | source = "registry+https://github.com/rust-lang/crates.io-index" 181 | 182 | [[package]] 183 | name = "human-panic" 184 | version = "1.0.1" 185 | source = "registry+https://github.com/rust-lang/crates.io-index" 186 | dependencies = [ 187 | "backtrace 0.3.37 (registry+https://github.com/rust-lang/crates.io-index)", 188 | "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 189 | "os_type 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 190 | "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", 191 | "serde_derive 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", 192 | "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", 193 | "termcolor 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", 194 | "toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", 195 | "uuid 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", 196 | ] 197 | 198 | [[package]] 199 | name = "im" 200 | version = "10.2.0" 201 | source = "registry+https://github.com/rust-lang/crates.io-index" 202 | dependencies = [ 203 | "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", 204 | ] 205 | 206 | [[package]] 207 | name = "itertools" 208 | version = "0.8.0" 209 | source = "registry+https://github.com/rust-lang/crates.io-index" 210 | dependencies = [ 211 | "either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", 212 | ] 213 | 214 | [[package]] 215 | name = "lazy_static" 216 | version = "1.4.0" 217 | source = "registry+https://github.com/rust-lang/crates.io-index" 218 | 219 | [[package]] 220 | name = "libc" 221 | version = "0.2.62" 222 | source = "registry+https://github.com/rust-lang/crates.io-index" 223 | 224 | [[package]] 225 | name = "lock_api" 226 | version = "0.3.1" 227 | source = "registry+https://github.com/rust-lang/crates.io-index" 228 | dependencies = [ 229 | "scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 230 | ] 231 | 232 | [[package]] 233 | name = "memchr" 234 | version = "2.2.1" 235 | source = "registry+https://github.com/rust-lang/crates.io-index" 236 | 237 | [[package]] 238 | name = "nom" 239 | version = "4.2.3" 240 | source = "registry+https://github.com/rust-lang/crates.io-index" 241 | dependencies = [ 242 | "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 243 | "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 244 | ] 245 | 246 | [[package]] 247 | name = "num-traits" 248 | version = "0.1.43" 249 | source = "registry+https://github.com/rust-lang/crates.io-index" 250 | dependencies = [ 251 | "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 252 | ] 253 | 254 | [[package]] 255 | name = "num-traits" 256 | version = "0.2.8" 257 | source = "registry+https://github.com/rust-lang/crates.io-index" 258 | dependencies = [ 259 | "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", 260 | ] 261 | 262 | [[package]] 263 | name = "os_type" 264 | version = "2.2.0" 265 | source = "registry+https://github.com/rust-lang/crates.io-index" 266 | dependencies = [ 267 | "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 268 | ] 269 | 270 | [[package]] 271 | name = "parking_lot" 272 | version = "0.9.0" 273 | source = "registry+https://github.com/rust-lang/crates.io-index" 274 | dependencies = [ 275 | "lock_api 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 276 | "parking_lot_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", 277 | "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", 278 | ] 279 | 280 | [[package]] 281 | name = "parking_lot_core" 282 | version = "0.6.2" 283 | source = "registry+https://github.com/rust-lang/crates.io-index" 284 | dependencies = [ 285 | "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", 286 | "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", 287 | "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", 288 | "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", 289 | "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", 290 | "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", 291 | "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", 292 | ] 293 | 294 | [[package]] 295 | name = "proc-macro2" 296 | version = "0.4.30" 297 | source = "registry+https://github.com/rust-lang/crates.io-index" 298 | dependencies = [ 299 | "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 300 | ] 301 | 302 | [[package]] 303 | name = "proc-macro2" 304 | version = "1.0.3" 305 | source = "registry+https://github.com/rust-lang/crates.io-index" 306 | dependencies = [ 307 | "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 308 | ] 309 | 310 | [[package]] 311 | name = "quote" 312 | version = "0.6.13" 313 | source = "registry+https://github.com/rust-lang/crates.io-index" 314 | dependencies = [ 315 | "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", 316 | ] 317 | 318 | [[package]] 319 | name = "quote" 320 | version = "1.0.2" 321 | source = "registry+https://github.com/rust-lang/crates.io-index" 322 | dependencies = [ 323 | "proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", 324 | ] 325 | 326 | [[package]] 327 | name = "rand" 328 | version = "0.4.6" 329 | source = "registry+https://github.com/rust-lang/crates.io-index" 330 | dependencies = [ 331 | "fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 332 | "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", 333 | "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 334 | "rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 335 | "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", 336 | ] 337 | 338 | [[package]] 339 | name = "rand_core" 340 | version = "0.3.1" 341 | source = "registry+https://github.com/rust-lang/crates.io-index" 342 | dependencies = [ 343 | "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", 344 | ] 345 | 346 | [[package]] 347 | name = "rand_core" 348 | version = "0.4.2" 349 | source = "registry+https://github.com/rust-lang/crates.io-index" 350 | 351 | [[package]] 352 | name = "rdrand" 353 | version = "0.4.0" 354 | source = "registry+https://github.com/rust-lang/crates.io-index" 355 | dependencies = [ 356 | "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 357 | ] 358 | 359 | [[package]] 360 | name = "redox_syscall" 361 | version = "0.1.56" 362 | source = "registry+https://github.com/rust-lang/crates.io-index" 363 | 364 | [[package]] 365 | name = "regex" 366 | version = "1.3.1" 367 | source = "registry+https://github.com/rust-lang/crates.io-index" 368 | dependencies = [ 369 | "aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)", 370 | "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 371 | "regex-syntax 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", 372 | "thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", 373 | ] 374 | 375 | [[package]] 376 | name = "regex-syntax" 377 | version = "0.6.12" 378 | source = "registry+https://github.com/rust-lang/crates.io-index" 379 | 380 | [[package]] 381 | name = "remove_dir_all" 382 | version = "0.5.2" 383 | source = "registry+https://github.com/rust-lang/crates.io-index" 384 | dependencies = [ 385 | "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", 386 | ] 387 | 388 | [[package]] 389 | name = "revmenu" 390 | version = "0.1.2" 391 | dependencies = [ 392 | "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", 393 | "colored 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", 394 | "console 0.7.7 (registry+https://github.com/rust-lang/crates.io-index)", 395 | "exitcode 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 396 | "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 397 | "human-panic 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", 398 | "im 10.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 399 | "itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", 400 | "nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)", 401 | ] 402 | 403 | [[package]] 404 | name = "rgb" 405 | version = "0.8.14" 406 | source = "registry+https://github.com/rust-lang/crates.io-index" 407 | 408 | [[package]] 409 | name = "rustc-demangle" 410 | version = "0.1.16" 411 | source = "registry+https://github.com/rust-lang/crates.io-index" 412 | 413 | [[package]] 414 | name = "rustc_version" 415 | version = "0.2.3" 416 | source = "registry+https://github.com/rust-lang/crates.io-index" 417 | dependencies = [ 418 | "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", 419 | ] 420 | 421 | [[package]] 422 | name = "scopeguard" 423 | version = "1.0.0" 424 | source = "registry+https://github.com/rust-lang/crates.io-index" 425 | 426 | [[package]] 427 | name = "semver" 428 | version = "0.9.0" 429 | source = "registry+https://github.com/rust-lang/crates.io-index" 430 | dependencies = [ 431 | "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", 432 | ] 433 | 434 | [[package]] 435 | name = "semver-parser" 436 | version = "0.7.0" 437 | source = "registry+https://github.com/rust-lang/crates.io-index" 438 | 439 | [[package]] 440 | name = "serde" 441 | version = "1.0.100" 442 | source = "registry+https://github.com/rust-lang/crates.io-index" 443 | 444 | [[package]] 445 | name = "serde_derive" 446 | version = "1.0.100" 447 | source = "registry+https://github.com/rust-lang/crates.io-index" 448 | dependencies = [ 449 | "proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", 450 | "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 451 | "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", 452 | ] 453 | 454 | [[package]] 455 | name = "smallvec" 456 | version = "0.6.10" 457 | source = "registry+https://github.com/rust-lang/crates.io-index" 458 | 459 | [[package]] 460 | name = "strsim" 461 | version = "0.8.0" 462 | source = "registry+https://github.com/rust-lang/crates.io-index" 463 | 464 | [[package]] 465 | name = "syn" 466 | version = "0.15.44" 467 | source = "registry+https://github.com/rust-lang/crates.io-index" 468 | dependencies = [ 469 | "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", 470 | "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", 471 | "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 472 | ] 473 | 474 | [[package]] 475 | name = "syn" 476 | version = "1.0.5" 477 | source = "registry+https://github.com/rust-lang/crates.io-index" 478 | dependencies = [ 479 | "proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", 480 | "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 481 | "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 482 | ] 483 | 484 | [[package]] 485 | name = "synstructure" 486 | version = "0.10.2" 487 | source = "registry+https://github.com/rust-lang/crates.io-index" 488 | dependencies = [ 489 | "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", 490 | "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", 491 | "syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)", 492 | "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 493 | ] 494 | 495 | [[package]] 496 | name = "tempdir" 497 | version = "0.3.7" 498 | source = "registry+https://github.com/rust-lang/crates.io-index" 499 | dependencies = [ 500 | "rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", 501 | "remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", 502 | ] 503 | 504 | [[package]] 505 | name = "termcolor" 506 | version = "0.3.6" 507 | source = "registry+https://github.com/rust-lang/crates.io-index" 508 | dependencies = [ 509 | "wincolor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", 510 | ] 511 | 512 | [[package]] 513 | name = "termios" 514 | version = "0.3.1" 515 | source = "registry+https://github.com/rust-lang/crates.io-index" 516 | dependencies = [ 517 | "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", 518 | ] 519 | 520 | [[package]] 521 | name = "textwrap" 522 | version = "0.11.0" 523 | source = "registry+https://github.com/rust-lang/crates.io-index" 524 | dependencies = [ 525 | "unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", 526 | ] 527 | 528 | [[package]] 529 | name = "thread_local" 530 | version = "0.3.6" 531 | source = "registry+https://github.com/rust-lang/crates.io-index" 532 | dependencies = [ 533 | "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 534 | ] 535 | 536 | [[package]] 537 | name = "toml" 538 | version = "0.4.10" 539 | source = "registry+https://github.com/rust-lang/crates.io-index" 540 | dependencies = [ 541 | "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", 542 | ] 543 | 544 | [[package]] 545 | name = "unicode-width" 546 | version = "0.1.6" 547 | source = "registry+https://github.com/rust-lang/crates.io-index" 548 | 549 | [[package]] 550 | name = "unicode-xid" 551 | version = "0.1.0" 552 | source = "registry+https://github.com/rust-lang/crates.io-index" 553 | 554 | [[package]] 555 | name = "unicode-xid" 556 | version = "0.2.0" 557 | source = "registry+https://github.com/rust-lang/crates.io-index" 558 | 559 | [[package]] 560 | name = "uuid" 561 | version = "0.6.5" 562 | source = "registry+https://github.com/rust-lang/crates.io-index" 563 | dependencies = [ 564 | "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", 565 | "rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", 566 | ] 567 | 568 | [[package]] 569 | name = "vec_map" 570 | version = "0.8.1" 571 | source = "registry+https://github.com/rust-lang/crates.io-index" 572 | 573 | [[package]] 574 | name = "version_check" 575 | version = "0.1.5" 576 | source = "registry+https://github.com/rust-lang/crates.io-index" 577 | 578 | [[package]] 579 | name = "winapi" 580 | version = "0.3.8" 581 | source = "registry+https://github.com/rust-lang/crates.io-index" 582 | dependencies = [ 583 | "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 584 | "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 585 | ] 586 | 587 | [[package]] 588 | name = "winapi-i686-pc-windows-gnu" 589 | version = "0.4.0" 590 | source = "registry+https://github.com/rust-lang/crates.io-index" 591 | 592 | [[package]] 593 | name = "winapi-x86_64-pc-windows-gnu" 594 | version = "0.4.0" 595 | source = "registry+https://github.com/rust-lang/crates.io-index" 596 | 597 | [[package]] 598 | name = "wincolor" 599 | version = "0.1.6" 600 | source = "registry+https://github.com/rust-lang/crates.io-index" 601 | dependencies = [ 602 | "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", 603 | ] 604 | 605 | [[package]] 606 | name = "winconsole" 607 | version = "0.10.0" 608 | source = "registry+https://github.com/rust-lang/crates.io-index" 609 | dependencies = [ 610 | "cgmath 0.16.1 (registry+https://github.com/rust-lang/crates.io-index)", 611 | "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 612 | "rgb 0.8.14 (registry+https://github.com/rust-lang/crates.io-index)", 613 | "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", 614 | ] 615 | 616 | [metadata] 617 | "checksum aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "58fb5e95d83b38284460a5fda7d6470aa0b8844d283a0b614b8535e880800d2d" 618 | "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" 619 | "checksum approx 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "08abcc3b4e9339e33a3d0a5ed15d84a687350c05689d825e0f6655eef9e76a94" 620 | "checksum atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1803c647a3ec87095e7ae7acfca019e98de5ec9a7d01343f611cf3152ed71a90" 621 | "checksum autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b671c8fb71b457dd4ae18c4ba1e59aa81793daacc361d82fcd410cef0d491875" 622 | "checksum backtrace 0.3.37 (registry+https://github.com/rust-lang/crates.io-index)" = "5180c5a20655b14a819b652fd2378fa5f1697b6c9ddad3e695c2f9cedf6df4e2" 623 | "checksum backtrace-sys 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)" = "82a830b4ef2d1124a711c71d263c5abdc710ef8e907bd508c88be475cebc422b" 624 | "checksum bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3d155346769a6855b86399e9bc3814ab343cd3d62c7e985113d46a0ec3c281fd" 625 | "checksum cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)" = "4fc9a35e1f4290eb9e5fc54ba6cf40671ed2a2514c3eeb2b2a908dda2ea5a1be" 626 | "checksum cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33" 627 | "checksum cgmath 0.16.1 (registry+https://github.com/rust-lang/crates.io-index)" = "64a4b57c8f4e3a2e9ac07e0f6abc9c24b6fc9e1b54c3478cfb598f3d0023e51c" 628 | "checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9" 629 | "checksum clicolors-control 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90082ee5dcdd64dc4e9e0d37fbf3ee325419e39c0092191e0393df65518f741e" 630 | "checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" 631 | "checksum colored 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6cdb90b60f2927f8d76139c72dbde7e10c3a2bc47c8594c9c7a66529f2687c03" 632 | "checksum console 0.7.7 (registry+https://github.com/rust-lang/crates.io-index)" = "8ca57c2c14b8a2bf3105bc9d15574aad80babf6a9c44b1058034cdf8bd169628" 633 | "checksum either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5527cfe0d098f36e3f8839852688e63c8fff1c90b2b405aef730615f9a7bcf7b" 634 | "checksum encode_unicode 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" 635 | "checksum exitcode 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "de853764b47027c2e862a995c34978ffa63c1501f2e15f987ba11bd4f9bba193" 636 | "checksum failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "795bd83d3abeb9220f257e597aa0080a508b27533824adf336529648f6abf7e2" 637 | "checksum failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ea1063915fd7ef4309e222a5a07cf9c319fb9c7836b1f89b85458672dbb127e1" 638 | "checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" 639 | "checksum human-panic 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "21638c5955a6daf3ecc42cae702335fc37a72a4abcc6959ce457b31a7d43bbdd" 640 | "checksum im 10.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "006e5c34d6fc287f91a90f53f19a8a859bade826e3153b137b8e1022ea54250b" 641 | "checksum itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5b8467d9c1cebe26feb08c640139247fac215782d35371ade9a2136ed6085358" 642 | "checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 643 | "checksum libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)" = "34fcd2c08d2f832f376f4173a231990fa5aef4e99fb569867318a227ef4c06ba" 644 | "checksum lock_api 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f8912e782533a93a167888781b836336a6ca5da6175c05944c86cf28c31104dc" 645 | "checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e" 646 | "checksum nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6" 647 | "checksum num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31" 648 | "checksum num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6ba9a427cfca2be13aa6f6403b0b7e7368fe982bfa16fccc450ce74c46cd9b32" 649 | "checksum os_type 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7edc011af0ae98b7f88cf7e4a83b70a54a75d2b8cb013d6efd02e5956207e9eb" 650 | "checksum parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252" 651 | "checksum parking_lot_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b" 652 | "checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" 653 | "checksum proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e98a83a9f9b331f54b924e68a66acb1bb35cb01fb0a23645139967abefb697e8" 654 | "checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" 655 | "checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" 656 | "checksum rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" 657 | "checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" 658 | "checksum rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" 659 | "checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" 660 | "checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" 661 | "checksum regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dc220bd33bdce8f093101afe22a037b8eb0e5af33592e6a9caafff0d4cb81cbd" 662 | "checksum regex-syntax 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "11a7e20d1cce64ef2fed88b66d347f88bd9babb82845b2b858f3edbf59a4f716" 663 | "checksum remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e" 664 | "checksum rgb 0.8.14 (registry+https://github.com/rust-lang/crates.io-index)" = "2089e4031214d129e201f8c3c8c2fe97cd7322478a0d1cdf78e7029b0042efdb" 665 | "checksum rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" 666 | "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" 667 | "checksum scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d" 668 | "checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" 669 | "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" 670 | "checksum serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)" = "f4473e8506b213730ff2061073b48fa51dcc66349219e2e7c5608f0296a1d95a" 671 | "checksum serde_derive 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)" = "11e410fde43e157d789fc290d26bc940778ad0fdd47836426fbac36573710dbb" 672 | "checksum smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "ab606a9c5e214920bb66c458cd7be8ef094f813f20fe77a54cc7dbfff220d4b7" 673 | "checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" 674 | "checksum syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)" = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" 675 | "checksum syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "66850e97125af79138385e9b88339cbcd037e3f28ceab8c5ad98e64f0f1f80bf" 676 | "checksum synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "02353edf96d6e4dc81aea2d8490a7e9db177bf8acb0e951c24940bf866cb313f" 677 | "checksum tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8" 678 | "checksum termcolor 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "adc4587ead41bf016f11af03e55a624c06568b5a19db4e90fde573d805074f83" 679 | "checksum termios 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "72b620c5ea021d75a735c943269bb07d30c9b77d6ac6b236bc8b5c496ef05625" 680 | "checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" 681 | "checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" 682 | "checksum toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "758664fc71a3a69038656bee8b6be6477d2a6c315a6b81f7081f591bffa4111f" 683 | "checksum unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7007dbd421b92cc6e28410fe7362e2e0a2503394908f417b68ec8d1c364c4e20" 684 | "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" 685 | "checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" 686 | "checksum uuid 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e1436e58182935dcd9ce0add9ea0b558e8a87befe01c1a301e6020aeb0876363" 687 | "checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" 688 | "checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" 689 | "checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" 690 | "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 691 | "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 692 | "checksum wincolor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "eeb06499a3a4d44302791052df005d5232b927ed1a9658146d842165c4de7767" 693 | "checksum winconsole 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ef84b96d10db72dd980056666d7f1e7663ce93d82fa33b63e71c966f4cf5032" 694 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Pascal Hartig "] 3 | name = "revmenu" 4 | description = "Check out git and mercurial revisions from your terminal output" 5 | repository = "https://github.com/passy/revmenu" 6 | license = "MIT" 7 | version = "0.1.2" 8 | keywords = ["terminal", "git", "mercurial"] 9 | categories = ["command-line-interface"] 10 | readme = "README.md" 11 | edition = "2018" 12 | 13 | [dependencies] 14 | clap = "2.33.0" 15 | console = "0.7.7" 16 | exitcode = "1.1.2" 17 | failure = "0.1.5" 18 | nom = "4.2.3" 19 | itertools = "0.8.0" 20 | colored = "1.8.0" 21 | im = "10.2.0" 22 | human-panic = "1.0.1" 23 | 24 | [badges] 25 | travis-ci = { repository = "passy/revmenu" } 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) Pascal Hartig 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ![revmenu](https://raw.githubusercontent.com/passy/revmenu/master/assets/logo.png) 2 | 3 | [![crates.io](https://img.shields.io/crates/v/revmenu.svg)](https://crates.io/crates/revmenu) 4 | [![Build Status](https://travis-ci.org/passy/revmenu.svg?branch=master)](https://travis-ci.org/passy/revmenu) 5 | 6 | revmenu can be used with your terminal multiplexer or as stand-alone tool to 7 | select and check out any hash-like string of characters in the output. 8 | 9 | ## Usage 10 | 11 | This is best used when combined with a terminal multiplexer. For tmux, 12 | there is a [plugin available](https://github.com/passy/tmux-revmenu). 13 | 14 | Install via tpm: 15 | 16 | ```tmux 17 | set -g @plugin 'passy/tmux-revmenu' 18 | ``` 19 | 20 | ![demo gif](https://raw.githubusercontent.com/passy/revmenu/master/assets/demo.gif) 21 | 22 | ### Manual Usage 23 | 24 | `revmenu` can read from files and from stdin by passing `-` instead of 25 | a filename. 26 | 27 | ``` 28 | $ git log | head -n 20 | revmenu - 29 | ``` 30 | 31 | ## Installation 32 | 33 | ```bash 34 | $ cargo install revmenu 35 | ``` 36 | 37 | ## License 38 | 39 | [MIT](LICENSE) 40 | -------------------------------------------------------------------------------- /assets/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/passy/revmenu/4c6f4474a2f876c289935cff01689b0ee5001253/assets/demo.gif -------------------------------------------------------------------------------- /assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/passy/revmenu/4c6f4474a2f876c289935cff01689b0ee5001253/assets/logo.png -------------------------------------------------------------------------------- /src/cli.rs: -------------------------------------------------------------------------------- 1 | use clap::{crate_authors, crate_name, crate_version, App, AppSettings, Arg}; 2 | 3 | static ABOUT: &str = 4 | "A tool for finding and selecting VCS revision hashes in your terminal."; 5 | 6 | pub fn cli() -> App<'static, 'static> { 7 | App::new(crate_name!()) 8 | .setting(AppSettings::ColoredHelp) 9 | .version(crate_version!()) 10 | .author(crate_authors!()) 11 | .about(ABOUT) 12 | .arg( 13 | Arg::with_name("FILE") 14 | .required(true) 15 | .help("File to read from. Pass \"-\" to capture stdin."), 16 | ) 17 | } 18 | -------------------------------------------------------------------------------- /src/highlight.rs: -------------------------------------------------------------------------------- 1 | use crate::parser; 2 | use crate::types::RevLocation; 3 | use colored::Colorize; 4 | use im::{catlist, CatList}; 5 | use itertools::Itertools; 6 | 7 | pub fn revs( 8 | vlines: &[String], 9 | rls: &[RevLocation], 10 | selected: Option<&parser::Located>, 11 | ) -> CatList { 12 | let grouped = rls.iter().group_by(|e| e.line); 13 | let mut igrouped = grouped.into_iter().peekable(); 14 | let grouped_lines = vlines.iter().enumerate().map(|(vlno, vl)| { 15 | let matched = if let Some(&(lno, ref _ls)) = igrouped.peek() { 16 | lno == vlno 17 | } else { 18 | false 19 | }; 20 | 21 | if matched { 22 | match igrouped.next() { 23 | None => (vl, vec![]), 24 | Some((_, group)) => (vl, group.collect()), 25 | } 26 | } else { 27 | (vl, vec![]) 28 | } 29 | }); 30 | 31 | grouped_lines.fold(catlist![], |acc, (original_line, rlocs)| { 32 | acc.snoc(line(original_line, rlocs, selected)) 33 | }) 34 | } 35 | 36 | fn line<'a, I>(str: &str, rls: I, selected: Option<&RevLocation>) -> String 37 | where 38 | I: IntoIterator>, 39 | { 40 | let (i, res) = rls.into_iter().fold((0_usize, catlist![]), |(i, acc), x| { 41 | let s = x.el.hash.len(); 42 | let j = x.col + s; 43 | 44 | let el = if Some(x) == selected { 45 | x.el.hash.yellow().to_string() 46 | } else { 47 | x.el.hash.magenta().to_string() 48 | }; 49 | 50 | (j, acc.snoc(str[i..x.col].to_string()).snoc(el)) 51 | }); 52 | 53 | format!("{}{}", res.iter().join(""), &str[i..]) 54 | } 55 | 56 | #[cfg(test)] 57 | mod tests { 58 | use crate::parser; 59 | 60 | fn mk_located(hash: &str, col: usize) -> parser::Located { 61 | parser::Located { 62 | el: parser::RefLike { 63 | hash: hash.to_string(), 64 | }, 65 | line: 0, 66 | col, 67 | } 68 | } 69 | 70 | #[test] 71 | fn test_highlight_line() { 72 | let testline = "deadbeef-525-hello-faceb00c"; 73 | let revs = vec![mk_located("deadbeef", 0), mk_located("faceb00c", 19)]; 74 | assert_eq!( 75 | super::line(&testline, &revs, None), 76 | "\u{1b}[35mdeadbeef\u{1b}[0m-525-hello-\u{1b}[35mfaceb00c\u{1b}[0m" 77 | ); 78 | } 79 | 80 | #[test] 81 | fn test_highlight_select_line() { 82 | let testline = "deadbeef-525-hello-faceb00c"; 83 | let revs = vec![mk_located("deadbeef", 0), mk_located("faceb00c", 19)]; 84 | assert_eq!( 85 | super::line(&testline, &revs, revs.get(0)), 86 | "\u{1b}[33mdeadbeef\u{1b}[0m-525-hello-\u{1b}[35mfaceb00c\u{1b}[0m" 87 | ); 88 | } 89 | 90 | #[test] 91 | fn test_highlight_nothing() { 92 | let testline = "deadbeef-525-hello-faceb00c"; 93 | let revs = vec![]; 94 | assert_eq!(super::line(&testline, &revs, None), testline); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | // Enable clippy if our Cargo.toml file asked us to do so. 2 | #![cfg_attr(feature = "clippy", feature(plugin))] 3 | #![cfg_attr(feature = "clippy", plugin(clippy))] 4 | // Enable as many useful Rust and Clippy warnings as we can stand. We'd 5 | // also enable `trivial_casts`, but we're waiting for 6 | // https://github.com/rust-lang/rust/issues/23416. 7 | #![warn( 8 | missing_copy_implementations, 9 | missing_debug_implementations, 10 | trivial_casts, 11 | trivial_numeric_casts, 12 | unsafe_code, 13 | unused_extern_crates, 14 | unused_import_braces, 15 | unused_qualifications 16 | )] 17 | #![deny(bare_trait_objects, anonymous_parameters)] 18 | #![cfg_attr(feature = "clippy", warn(cast_possible_wrap))] 19 | #![cfg_attr(feature = "clippy", warn(cast_precision_loss))] 20 | #![cfg_attr(feature = "clippy", warn(mut_mut))] 21 | // This allows us to use `unwrap` on `Option` values (because doing makes 22 | // working with Regex matches much nicer) and when compiling in test mode 23 | // (because using it in tests is idiomatic). 24 | #![cfg_attr(all(not(test), feature = "clippy"), warn(result_unwrap_used))] 25 | #![cfg_attr(feature = "clippy", warn(unseparated_literal_suffix))] 26 | #![cfg_attr(feature = "clippy", warn(wrong_pub_self_convention))] 27 | 28 | use crate::types::RevLocation; 29 | use console::{Key, Term}; 30 | use failure::{bail, err_msg, Error}; 31 | use human_panic::setup_panic; 32 | use std::fs::File; 33 | use std::io::{stderr, stdin, BufRead, BufReader, Write}; 34 | use std::iter::Iterator; 35 | use std::ops::Rem; 36 | use std::process; 37 | 38 | mod cli; 39 | mod highlight; 40 | mod parser; 41 | mod types; 42 | mod vcs; 43 | 44 | fn main() { 45 | setup_panic!(); 46 | match run() { 47 | Err(e) => { 48 | let stderr = &mut stderr(); 49 | let errmsg = "Error writing to stderr."; 50 | writeln!(stderr, "{}", e).expect(errmsg); 51 | process::exit(1) 52 | } 53 | Ok(r) => process::exit(r), 54 | } 55 | } 56 | 57 | fn select(term: &Term, lines: &[String], revs: &[RevLocation]) -> Result, Error> { 58 | let mut selected = 0_usize; 59 | 60 | loop { 61 | for line in highlight::revs(lines, revs, revs.get(selected)) { 62 | term.write_line(&line)?; 63 | } 64 | 65 | match term.read_key()? { 66 | Key::ArrowDown | Key::ArrowRight | Key::Char('j') => { 67 | selected = (selected as u64 + 1).rem(revs.len() as u64) as usize; 68 | } 69 | Key::ArrowUp | Key::ArrowLeft | Key::Char('k') => { 70 | selected = ((selected as i64 - 1 + revs.len() as i64) % revs.len() as i64) as usize; 71 | } 72 | Key::Enter => { 73 | term.clear_last_lines(lines.len())?; 74 | break; 75 | } 76 | Key::Escape => return Ok(None), 77 | _ => {} 78 | } 79 | 80 | term.clear_last_lines(lines.len())?; 81 | } 82 | 83 | Ok(Some(selected)) 84 | } 85 | 86 | fn run() -> Result { 87 | let args = cli::cli().get_matches(); 88 | 89 | let file_val = args 90 | .value_of("FILE") 91 | .ok_or_else(|| err_msg("Expected FILE."))?; 92 | let reader: Box = if file_val == "-" { 93 | Box::new(BufReader::new(stdin())) 94 | } else { 95 | let file = File::open(file_val)?; 96 | Box::new(BufReader::new(file)) 97 | }; 98 | 99 | let lines: Vec = reader.lines().filter_map(Result::ok).collect(); 100 | let term = Term::stderr(); 101 | 102 | // If we can get the terminal size, truncate to the last (h - 1) lines. 103 | let truncated_lines = match term.size_checked() { 104 | Some((h, _w)) => { 105 | let len = lines.len(); 106 | let start = len - std::cmp::min(h as usize, len); 107 | let end = std::cmp::max(len - 1, start); 108 | lines[start..end].into() 109 | } 110 | None => lines, 111 | }; 112 | 113 | let cwd = std::env::current_dir()?; 114 | let vcs_ = vcs::detect_vcs(&cwd)?; 115 | let revs: Vec = parser::parse_lines(truncated_lines.iter()); 116 | 117 | if revs.is_empty() { 118 | return Ok(exitcode::OK); 119 | } 120 | 121 | let selected = match select(&term, &truncated_lines, &revs)? { 122 | Some(s) => s, 123 | None => return Ok(exitcode::OK), 124 | }; 125 | 126 | if let Some(rev) = revs.get(selected) { 127 | vcs_.checkout(&rev.el.hash)?; 128 | println!( 129 | "Checking out revision {} with {} ...", 130 | &rev.el.hash, 131 | vcs_.name() 132 | ); 133 | Ok(exitcode::OK) 134 | } else { 135 | bail!("Selected unavailable rev.") 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /src/parser.rs: -------------------------------------------------------------------------------- 1 | use failure::{bail, Error}; 2 | use nom::types::CompleteStr; 3 | use nom::{alt, eof, hex_digit, is_hex_digit, named, opt, take_while, terminated, Err, Offset}; 4 | 5 | #[derive(Debug, PartialEq, Eq)] 6 | pub struct RefLike { 7 | pub hash: String, 8 | } 9 | 10 | #[derive(Debug, PartialEq, Eq)] 11 | pub struct Located { 12 | pub el: A, 13 | pub col: usize, 14 | pub line: usize, 15 | } 16 | 17 | fn mk_reflike(hash: &str) -> Option { 18 | if hash.len() >= 6 { 19 | Some(RefLike { 20 | hash: hash.to_owned(), 21 | }) 22 | } else { 23 | None 24 | } 25 | } 26 | 27 | fn is_hex_digit_char(c: char) -> bool { 28 | is_hex_digit(c as u8) 29 | } 30 | 31 | named!( 32 | terminator, 33 | take_while!(|c| !is_hex_digit_char(c)) 34 | ); 35 | 36 | named!( 37 | token>, 38 | terminated!(opt!(hex_digit), alt!(eof!() | terminator)) 39 | ); 40 | 41 | pub fn parse_line(ls: &str, row: usize) -> Result>, Error> { 42 | // Holy mutable son of satan, this needs a refactor. 43 | let mut tokens: Vec> = vec![]; 44 | let mut offset: usize = 0_usize; 45 | let mut cls = CompleteStr(ls); 46 | 47 | while !cls.0.is_empty() { 48 | match token(cls) { 49 | Ok((remaining, None)) => { 50 | offset += cls.offset(&remaining); 51 | cls = remaining; 52 | } 53 | Ok((remaining, Some(value))) => { 54 | if let Some(v) = mk_reflike(value.0) { 55 | tokens.push(Located { 56 | line: row, 57 | col: offset, 58 | el: v, 59 | }); 60 | } 61 | offset += cls.offset(&remaining); 62 | cls = remaining; 63 | } 64 | Err(Err::Incomplete(needed)) => { 65 | bail!("Incomplete, needed: {:?}", needed); 66 | } 67 | Err(Err::Error(e)) | Err(Err::Failure(e)) => { 68 | bail!("Parsing failure: {:?}", e); 69 | } 70 | } 71 | } 72 | Ok(tokens) 73 | } 74 | 75 | #[allow(unused)] 76 | pub fn parse_bufread(reader: T) -> Vec> 77 | where 78 | T: ::std::io::BufRead, 79 | { 80 | // Pretty sure that this is a terrible idea, but I don't feel like 81 | // rewriting my tests right now. 82 | let lines: Vec<_> = reader.lines().filter_map(Result::ok).collect(); 83 | parse_lines(lines.iter()) 84 | } 85 | 86 | pub fn parse_lines<'a, I>(lines: I) -> Vec> 87 | where 88 | I: Iterator, 89 | { 90 | lines 91 | .enumerate() 92 | .filter_map(|(lineno, line)| parse_line(line, lineno).ok()) 93 | .flat_map(|a| a) 94 | .collect() 95 | } 96 | 97 | #[cfg(test)] 98 | mod tests { 99 | use nom::types::CompleteStr; 100 | fn mk_located(hash: &str, col: usize, line: usize) -> super::Located { 101 | super::Located { 102 | el: super::RefLike { 103 | hash: hash.to_string(), 104 | }, 105 | col, 106 | line, 107 | } 108 | } 109 | 110 | #[test] 111 | fn test_token() { 112 | assert_eq!( 113 | super::token(CompleteStr("deadbeef")), 114 | Ok((CompleteStr(""), Some(CompleteStr("deadbeef")))) 115 | ); 116 | 117 | assert_eq!( 118 | super::token(CompleteStr("deadbeef-faceb00c")), 119 | Ok((CompleteStr("faceb00c"), Some(CompleteStr("deadbeef")))) 120 | ); 121 | } 122 | 123 | #[test] 124 | fn test_full_parse_line() { 125 | assert_eq!( 126 | super::parse_line("deadbeef-525-hello-faceb00c", 0).unwrap(), 127 | vec![mk_located("deadbeef", 0, 0), mk_located("faceb00c", 19, 0)] 128 | ); 129 | } 130 | 131 | #[test] 132 | fn test_regression_split_line() { 133 | assert_eq!( 134 | super::parse_line(" Compiling dialoguer v0.1.0 (https://github.com/mitsuhiko/dialoguer?rev=5f28d3d74768b6ba532866ee3c83df9324f9df06#5f28d3d7)", 0).unwrap(), 135 | vec![mk_located("5f28d3d74768b6ba532866ee3c83df9324f9df06", 74, 0), mk_located("5f28d3d7", 115, 0)] 136 | ); 137 | } 138 | 139 | #[test] 140 | fn test_full_parse_lines() { 141 | let str = 142 | "hello deadbeef\nlorem ipsum\r\ndolor 9d393a816701d3e74f268f3b6c3f6ff43f25e811 sup\n"; 143 | let cursor = ::std::io::Cursor::new(str); 144 | 145 | assert_eq!( 146 | super::parse_bufread(cursor), 147 | vec![ 148 | mk_located("deadbeef", 6, 0), 149 | mk_located("9d393a816701d3e74f268f3b6c3f6ff43f25e811", 6, 2), 150 | ] 151 | ); 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /src/types.rs: -------------------------------------------------------------------------------- 1 | use crate::parser; 2 | 3 | pub type RevLocation = parser::Located; 4 | -------------------------------------------------------------------------------- /src/vcs/git.rs: -------------------------------------------------------------------------------- 1 | use super::VCS; 2 | 3 | use failure::{bail, Error}; 4 | use std::path::{Path, PathBuf}; 5 | use std::process::Command; 6 | 7 | #[derive(Debug)] 8 | pub struct Git { 9 | root: PathBuf, 10 | } 11 | 12 | impl Git { 13 | /// Create a new instance of the `Git` VCS impl based 14 | /// on the passed in root. Will succeed if the given 15 | /// root contains a root that contains a `.git` directory. 16 | // TODO: Learn what the idiomatic name for this kind of function is. 17 | #[allow(clippy::new_ret_no_self)] 18 | pub fn new(root: &Path) -> Option> { 19 | if root.join(".git").exists() { 20 | Some(Box::new(Git { 21 | root: root.to_path_buf(), 22 | })) 23 | } else { 24 | None 25 | } 26 | } 27 | } 28 | 29 | impl VCS for Git { 30 | fn name(&self) -> &str { 31 | "git" 32 | } 33 | 34 | fn checkout(&self, rev: &str) -> Result<(), Error> { 35 | let status = Command::new("git") 36 | .args(&["checkout", rev]) 37 | .current_dir(&self.root) 38 | .status()?; 39 | 40 | if status.success() { 41 | Ok(()) 42 | } else { 43 | bail!("git failed with exit code {:?}", status.code()) 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/vcs/hg.rs: -------------------------------------------------------------------------------- 1 | use super::VCS; 2 | 3 | use failure::{bail, Error}; 4 | use std::path::{Path, PathBuf}; 5 | use std::process::Command; 6 | 7 | #[derive(Debug)] 8 | pub struct Hg { 9 | root: PathBuf, 10 | } 11 | 12 | impl Hg { 13 | /// Create a new instance of the `Hg` VCS impl based 14 | /// on the passed in root. Will succeed if the given 15 | /// root contains a root that contains a `.hg` directory. 16 | #[allow(clippy::new_ret_no_self)] 17 | pub fn new(root: &Path) -> Option> { 18 | if root.join(".hg").exists() { 19 | Some(Box::new(Hg { 20 | root: root.to_path_buf(), 21 | })) 22 | } else { 23 | None 24 | } 25 | } 26 | } 27 | 28 | impl VCS for Hg { 29 | fn name(&self) -> &str { 30 | "hg" 31 | } 32 | 33 | fn checkout(&self, rev: &str) -> Result<(), Error> { 34 | let status = Command::new("hg") 35 | .args(&["update", rev]) 36 | .current_dir(&self.root) 37 | .status()?; 38 | 39 | if status.success() { 40 | Ok(()) 41 | } else { 42 | bail!("hg failed with exit code {:?}", status.code()) 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/vcs/mod.rs: -------------------------------------------------------------------------------- 1 | mod git; 2 | mod hg; 3 | 4 | use failure::{err_msg, Error}; 5 | use std::path::Path; 6 | 7 | use self::git::Git; 8 | use self::hg::Hg; 9 | 10 | pub trait VCS { 11 | fn name(&self) -> &str; 12 | fn checkout(&self, rev: &str) -> Result<(), Error>; 13 | } 14 | 15 | #[allow(clippy::type_complexity)] 16 | static SUPPORTED_VCS: [fn(&Path) -> Option>; 2] = [Git::new, Hg::new]; 17 | 18 | pub fn detect_vcs(path: &Path) -> Result, Error> { 19 | let mut pathbuf = path.to_path_buf(); 20 | 21 | loop { 22 | if let Some(v) = SUPPORTED_VCS 23 | .iter() 24 | .map(|f| f(&pathbuf)) 25 | .find(Option::is_some) 26 | .and_then(|a| a) 27 | { 28 | return Ok(v); 29 | } 30 | 31 | if !pathbuf.pop() { 32 | return Err(err_msg("Cannot find VCS in directory")); 33 | } 34 | } 35 | } 36 | --------------------------------------------------------------------------------