Split crates

This commit is contained in:
Ajeet D'Souza 2020-10-18 14:52:13 +05:30
parent eaf63bcc69
commit aca2477b68
40 changed files with 2095 additions and 2151 deletions

120
.gitignore vendored
View File

@ -1,123 +1,7 @@
# Created by https://www.gitignore.io/api/rust
# Edit at https://www.gitignore.io/?templates=rust
### Rust ###
# Generated by Cargo
# will have compiled files and executables
/target/
debug/
target/
# These are backup files generated by rustfmt
**/*.rs.bk
# End of https://www.gitignore.io/api/rust
# Created by https://www.gitignore.io/api/python
# Edit at https://www.gitignore.io/?templates=python
### Python ###
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
pip-wheel-metadata/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/
.pytest_cache/
# Translations
*.mo
*.pot
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# pyenv
.python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# celery beat schedule file
celerybeat-schedule
# SageMath parsed files
*.sage.py
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# Mr Developer
.mr.developer.cfg
.project
.pydevproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# End of https://www.gitignore.io/api/python

View File

@ -17,6 +17,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `zoxide init --no-aliases` no longer generates `z` or `zi`.
### Removed
- Deprecated PWD hooks for POSIX shells.
## [0.4.3] - 2020-07-04
### Fixed

547
Cargo.lock generated
View File

@ -1,474 +1,619 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "ansi_term"
version = "0.11.0"
name = "anyhow"
version = "1.0.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1fd36ffbb1fb7c834eac128ea8d0e310c5aeb635548f9d58861e1308d46e71c"
[[package]]
name = "askama"
version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70a6e7ebd44d0047fd48206c83c5cd3214acc7b9d87f001da170145c47ef7d12"
dependencies = [
"winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"askama_derive",
"askama_escape",
"askama_shared",
]
[[package]]
name = "anyhow"
version = "1.0.32"
name = "askama_derive"
version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1d7169690c4f56343dcd821ab834972a22570a2662a19a84fd7775d5e1c3881"
dependencies = [
"askama_shared",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "arrayref"
version = "0.3.6"
name = "askama_escape"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90c108c1a94380c89d2215d0ac54ce09796823cca0fd91b299cfff3b33e346fb"
[[package]]
name = "arrayvec"
version = "0.5.1"
name = "askama_shared"
version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62fc272363345c8cdc030e4c259d9d028237f8b057dc9bb327772a257bde6bb5"
dependencies = [
"askama_escape",
"nom",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "assert_cmd"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c88b9ca26f9c16ec830350d309397e74ee9abdfd8eb1f71cb6ecc71a3fc818da"
dependencies = [
"doc-comment",
"predicates",
"predicates-core",
"predicates-tree",
"wait-timeout",
]
[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"hermit-abi 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.78 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"hermit-abi",
"libc",
"winapi",
]
[[package]]
name = "autocfg"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "base64"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
[[package]]
name = "bincode"
version = "1.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f30d3a39baa26f9651f17b375061f3233dde33424a8b72b0dbe93a68a0bc896d"
dependencies = [
"byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.116 (registry+https://github.com/rust-lang/crates.io-index)",
"byteorder",
"serde",
]
[[package]]
name = "bitflags"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "blake2b_simd"
version = "0.5.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"arrayref 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"constant_time_eq 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
[[package]]
name = "byteorder"
version = "1.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de"
[[package]]
name = "cfg-if"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "clap"
version = "2.33.3"
version = "3.0.0-beta.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4bd1061998a501ee7d4b6d449020df3266ca3124b941ec56cf2005c3779ca142"
dependencies = [
"ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-width 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
"vec_map 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
"atty",
"bitflags",
"clap_derive",
"indexmap",
"lazy_static",
"os_str_bytes",
"strsim",
"termcolor",
"textwrap",
"unicode-width",
"vec_map",
]
[[package]]
name = "constant_time_eq"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "crossbeam-utils"
version = "0.7.2"
name = "clap_derive"
version = "3.0.0-beta.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "370f715b81112975b1b69db93e0b56ea4cd4e5002ac43b2da8474106a54096a1"
dependencies = [
"autocfg 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"heck",
"proc-macro-error",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "dirs"
version = "3.0.1"
name = "difference"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198"
[[package]]
name = "dirs-next"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf36e65a80337bea855cd4ef9b8401ffce06a7baedf2e85ec467b1ac3f6e82b6"
dependencies = [
"dirs-sys 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 1.0.0",
"dirs-sys-next",
]
[[package]]
name = "dirs-sys"
version = "0.3.5"
name = "dirs-sys-next"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99de365f605554ae33f115102a02057d4fc18b01f3284d6870be0938743cfe7d"
dependencies = [
"libc 0.2.78 (registry+https://github.com/rust-lang/crates.io-index)",
"redox_users 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"libc",
"redox_users",
"winapi",
]
[[package]]
name = "doc-comment"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
[[package]]
name = "dunce"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "float-ord"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2641c4a7c0c4101df53ea572bffdc561c146f6c2eb09e4df02bc4811e3feeb4"
[[package]]
name = "getrandom"
version = "0.1.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc587bc0ec293155d5bfa6b9891ec18a1e330c234f896ea47fbada4cadbe47e6"
dependencies = [
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.78 (registry+https://github.com/rust-lang/crates.io-index)",
"wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.10",
"libc",
"wasi",
]
[[package]]
name = "glob"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
[[package]]
name = "hashbrown"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04"
[[package]]
name = "heck"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205"
dependencies = [
"unicode-segmentation 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-segmentation",
]
[[package]]
name = "hermit-abi"
version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c30f6d0bc6b00693347368a67d41b58f2fb851215ff1da49e90fe2c5c667151"
dependencies = [
"libc 0.2.78 (registry+https://github.com/rust-lang/crates.io-index)",
"libc",
]
[[package]]
name = "indexmap"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55e2e4c765aa53a0424761bf9f41aa7a6ac1efa87238f59560640e27fca028f2"
dependencies = [
"autocfg",
"hashbrown",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.78"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa7087f49d294270db4e1928fc110c976cd4b9e5a16348e0a1df09afa99e6c98"
[[package]]
name = "memchr"
version = "2.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400"
[[package]]
name = "nom"
version = "5.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af"
dependencies = [
"memchr",
"version_check",
]
[[package]]
name = "num-traits"
version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611"
dependencies = [
"autocfg",
]
[[package]]
name = "once_cell"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "260e51e7efe62b592207e9e13a68e43692a7a279171d6ba57abd208bf23645ad"
[[package]]
name = "ordered-float"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fe9037165d7023b1228bc4ae9a2fa1a2b0095eca6c2998c624723dfd01314a5"
dependencies = [
"num-traits",
]
[[package]]
name = "os_str_bytes"
version = "2.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2ac6fe3538f701e339953a3ebbe4f39941aababa8a3f6964635b24ab526daeac"
[[package]]
name = "ppv-lite86"
version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c36fa947111f5c62a733b652544dd0016a43ce89619538a8ef92724a6f501a20"
[[package]]
name = "predicates"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96bfead12e90dccead362d62bb2c90a5f6fc4584963645bc7f71a735e0b0735a"
dependencies = [
"difference",
"predicates-core",
]
[[package]]
name = "predicates-core"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06075c3a3e92559ff8929e7a280684489ea27fe44805174c3ebd9328dcb37178"
[[package]]
name = "predicates-tree"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e63c4859013b38a76eca2414c64911fba30def9e3202ac461a2d22831220124"
dependencies = [
"predicates-core",
"treeline",
]
[[package]]
name = "proc-macro-error"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
dependencies = [
"proc-macro-error-attr 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-macro2 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 1.0.42 (registry+https://github.com/rust-lang/crates.io-index)",
"version_check 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-macro-error-attr",
"proc-macro2",
"quote",
"syn",
"version_check",
]
[[package]]
name = "proc-macro-error-attr"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
dependencies = [
"proc-macro2 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
"version_check 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-macro2",
"quote",
"version_check",
]
[[package]]
name = "proc-macro2"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71"
dependencies = [
"unicode-xid 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-xid",
]
[[package]]
name = "quote"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37"
dependencies = [
"proc-macro2 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-macro2",
]
[[package]]
name = "rand"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
dependencies = [
"getrandom 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.78 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"getrandom",
"libc",
"rand_chacha",
"rand_core",
"rand_hc",
]
[[package]]
name = "rand_chacha"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
dependencies = [
"ppv-lite86 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
dependencies = [
"getrandom 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)",
"getrandom",
]
[[package]]
name = "rand_hc"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
dependencies = [
"rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core",
]
[[package]]
name = "redox_syscall"
version = "0.1.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
[[package]]
name = "redox_users"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de0737333e7a9502c789a36d7c7fa6092a49895d4faa31ca5df163857ded2e9d"
dependencies = [
"getrandom 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)",
"redox_syscall 0.1.57 (registry+https://github.com/rust-lang/crates.io-index)",
"rust-argon2 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
"getrandom",
"redox_syscall",
]
[[package]]
name = "rust-argon2"
version = "0.8.2"
name = "remove_dir_all"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
dependencies = [
"base64 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)",
"blake2b_simd 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)",
"constant_time_eq 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi",
]
[[package]]
name = "serde"
version = "1.0.116"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96fe57af81d28386a513cbc6858332abc6117cfdb5999647c6444b8f43a370a5"
dependencies = [
"serde_derive 1.0.116 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.116"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f630a6370fd8e457873b4bd2ffdae75408bc291ba72be773772a4c2a065d9ae8"
dependencies = [
"proc-macro2 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 1.0.42 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "strsim"
version = "0.8.0"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "structopt"
version = "0.3.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"clap 2.33.3 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"structopt-derive 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "structopt-derive"
version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-macro-error 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-macro2 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 1.0.42 (registry+https://github.com/rust-lang/crates.io-index)",
]
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "syn"
version = "1.0.42"
version = "1.0.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea9c5432ff16d6152371f808fb5a871cd67368171b09bb21b43df8e4a47a3556"
dependencies = [
"proc-macro2 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-xid 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-macro2",
"quote",
"unicode-xid",
]
[[package]]
name = "tempfile"
version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9"
dependencies = [
"cfg-if 0.1.10",
"libc",
"rand",
"redox_syscall",
"remove_dir_all",
"winapi",
]
[[package]]
name = "termcolor"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f"
dependencies = [
"winapi-util",
]
[[package]]
name = "textwrap"
version = "0.11.0"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "203008d98caf094106cfaba70acfed15e18ed3ddb7d94e49baec153a2b462789"
dependencies = [
"unicode-width 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-width",
]
[[package]]
name = "treeline"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7f741b240f1a48843f9b8e0444fb55fb2a4ff67293b50a9179dfd5ea67f8d41"
[[package]]
name = "unicode-segmentation"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0"
[[package]]
name = "unicode-width"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
[[package]]
name = "unicode-xid"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "uuid"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
[[package]]
name = "vec_map"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
[[package]]
name = "version_check"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed"
[[package]]
name = "wait-timeout"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6"
dependencies = [
"libc",
]
[[package]]
name = "wasi"
version = "0.9.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
dependencies = [
"winapi",
]
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "zoxide"
version = "0.4.3"
dependencies = [
"anyhow 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)",
"bincode 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"clap 2.33.3 (registry+https://github.com/rust-lang/crates.io-index)",
"dirs 3.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"dunce 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"float-ord 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.116 (registry+https://github.com/rust-lang/crates.io-index)",
"structopt 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)",
"uuid 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
"anyhow",
"clap",
"dirs-next",
"dunce",
"glob",
"once_cell",
"zoxide-engine",
"zoxide-shell",
]
[metadata]
"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
"checksum anyhow 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)" = "6b602bfe940d21c130f3895acd65221e8a61270debe89d628b9cb4e3ccb8569b"
"checksum arrayref 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544"
"checksum arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8"
"checksum atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
"checksum autocfg 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
"checksum base64 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff"
"checksum bincode 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f30d3a39baa26f9651f17b375061f3233dde33424a8b72b0dbe93a68a0bc896d"
"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
"checksum blake2b_simd 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)" = "d8fb2d74254a3a0b5cac33ac9f8ed0e44aa50378d9dbb2e5d83bd21ed1dc2c8a"
"checksum byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de"
"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
"checksum clap 2.33.3 (registry+https://github.com/rust-lang/crates.io-index)" = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
"checksum constant_time_eq 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
"checksum crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8"
"checksum dirs 3.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "142995ed02755914747cc6ca76fc7e4583cd18578746716d0508ea6ed558b9ff"
"checksum dirs-sys 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "8e93d7f5705de3e49895a2b5e0b8855a1c27f080192ae9c32a6432d50741a57a"
"checksum dunce 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b2641c4a7c0c4101df53ea572bffdc561c146f6c2eb09e4df02bc4811e3feeb4"
"checksum float-ord 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7bad48618fdb549078c333a7a8528acb57af271d0433bdecd523eb620628364e"
"checksum getrandom 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "fc587bc0ec293155d5bfa6b9891ec18a1e330c234f896ea47fbada4cadbe47e6"
"checksum glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
"checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205"
"checksum hermit-abi 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4c30f6d0bc6b00693347368a67d41b58f2fb851215ff1da49e90fe2c5c667151"
"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
"checksum libc 0.2.78 (registry+https://github.com/rust-lang/crates.io-index)" = "aa7087f49d294270db4e1928fc110c976cd4b9e5a16348e0a1df09afa99e6c98"
"checksum ppv-lite86 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)" = "c36fa947111f5c62a733b652544dd0016a43ce89619538a8ef92724a6f501a20"
"checksum proc-macro-error 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
"checksum proc-macro-error-attr 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
"checksum proc-macro2 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)" = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71"
"checksum quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37"
"checksum rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
"checksum rand_chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
"checksum rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
"checksum rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
"checksum redox_syscall 0.1.57 (registry+https://github.com/rust-lang/crates.io-index)" = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
"checksum redox_users 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "de0737333e7a9502c789a36d7c7fa6092a49895d4faa31ca5df163857ded2e9d"
"checksum rust-argon2 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9dab61250775933275e84053ac235621dfb739556d5c54a2f2e9313b7cf43a19"
"checksum serde 1.0.116 (registry+https://github.com/rust-lang/crates.io-index)" = "96fe57af81d28386a513cbc6858332abc6117cfdb5999647c6444b8f43a370a5"
"checksum serde_derive 1.0.116 (registry+https://github.com/rust-lang/crates.io-index)" = "f630a6370fd8e457873b4bd2ffdae75408bc291ba72be773772a4c2a065d9ae8"
"checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
"checksum structopt 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)" = "a33f6461027d7f08a13715659b2948e1602c31a3756aeae9378bfe7518c72e82"
"checksum structopt-derive 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "c92e775028122a4b3dd55d58f14fc5120289c69bee99df1d117ae30f84b225c9"
"checksum syn 1.0.42 (registry+https://github.com/rust-lang/crates.io-index)" = "9c51d92969d209b54a98397e1b91c8ae82d8c87a7bb87df0b29aa2ad81454228"
"checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
"checksum unicode-segmentation 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0"
"checksum unicode-width 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
"checksum unicode-xid 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
"checksum uuid 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9fde2f6a4bea1d6e007c4ad38c6839fa71cbb63b6dbf5b595aa38dc9b1093c11"
"checksum vec_map 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
"checksum version_check 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed"
"checksum wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)" = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
"checksum winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "zoxide-engine"
version = "0.1.0"
dependencies = [
"anyhow",
"bincode",
"ordered-float",
"serde",
"tempfile",
]
[[package]]
name = "zoxide-shell"
version = "0.1.0"
dependencies = [
"anyhow",
"askama",
"assert_cmd",
"once_cell",
]

View File

@ -5,24 +5,21 @@ authors = ["Ajeet D'Souza <98ajeet@gmail.com>"]
description = "A faster way to navigate your filesystem"
repository = "https://github.com/ajeetdsouza/zoxide/"
edition = "2018"
keywords = ["cli"]
categories = ["command-line-utilities", "filesystem"]
license = "MIT"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
anyhow = "1.0.28"
bincode = "1.2.1"
clap = "2.33.0"
dirs = "3.0.0"
dunce = "1.0.0"
float-ord = "0.2.0"
anyhow = "1.0.32"
clap = "3.0.0-beta.2"
dirs-next = "1.0.2"
dunce = "1.0.1"
glob = "0.3.0"
serde = { version = "1.0.106", features = ["derive"] }
structopt = "0.3.12"
uuid = { version = "0.8.1", features = ["v4"] }
once_cell = "1.4.1"
zoxide-engine = { path = "crates/zoxide-engine" }
zoxide-shell = { path = "crates/zoxide-shell" }
[workspace]
[profile.release]
codegen-units = 1

View File

@ -1,5 +1,7 @@
use std::process::Command;
fn main() {
let git_describe = std::process::Command::new("git")
let git_describe = Command::new("git")
.args(&["describe", "--tags", "--broken"])
.output()
.ok()

11
crates/zoxide-engine/.gitignore vendored Normal file
View File

@ -0,0 +1,11 @@
# Generated by Cargo
# will have compiled files and executables
debug/
target/
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
Cargo.lock
# These are backup files generated by rustfmt
**/*.rs.bk

View File

@ -0,0 +1,14 @@
[package]
name = "zoxide-engine"
version = "0.1.0"
authors = ["Ajeet D'Souza <98ajeet@gmail.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
anyhow = "1.0.33"
bincode = "1.3.1"
ordered-float = "2.0.0"
serde = { version = "1.0.116", features = ["derive"] }
tempfile = "3.1.0"

View File

@ -0,0 +1,42 @@
use crate::query::Query;
use serde::{Deserialize, Serialize};
use std::path::Path;
#[derive(Debug, Deserialize, Serialize)]
pub struct Dir {
pub path: String,
pub rank: Rank,
pub last_accessed: Epoch,
}
impl Dir {
pub fn is_dir(&self) -> bool {
Path::new(&self.path).is_dir()
}
pub fn is_match(&self, query: &Query) -> bool {
query.matches(&self.path)
}
pub fn get_score(&self, now: Epoch) -> Rank {
const HOUR: Epoch = 60 * 60;
const DAY: Epoch = 24 * HOUR;
const WEEK: Epoch = 7 * DAY;
let duration = now - self.last_accessed;
if duration < HOUR {
self.rank * 4.0
} else if duration < DAY {
self.rank * 2.0
} else if duration < WEEK {
self.rank * 0.5
} else {
self.rank * 0.25
}
}
}
pub type Rank = f64;
pub type Epoch = i64; // use a signed integer so subtraction can be performed on it

View File

@ -0,0 +1,7 @@
pub mod dir;
mod query;
mod store;
pub use dir::{Dir, Epoch};
pub use query::Query;
pub use store::Store;

View File

@ -0,0 +1,100 @@
use std::path::Path;
pub struct Query(Vec<String>);
impl Query {
pub fn new<I, S>(keywords: I) -> Query
where
I: IntoIterator<Item = S>,
S: AsRef<str>,
{
Query(
keywords
.into_iter()
.map(|s: S| s.as_ref().to_lowercase())
.collect(),
)
}
pub fn keywords(&self) -> &[String] {
&self.0
}
pub fn matches<S: AsRef<str>>(&self, path: S) -> bool {
let path = path.as_ref().to_lowercase();
let keywords = self.keywords();
let get_filenames = || {
let query_name = Path::new(keywords.last()?).file_name()?.to_str().unwrap();
let dir_name = Path::new(&path).file_name()?.to_str().unwrap();
Some((query_name, dir_name))
};
if let Some((query_name, dir_name)) = get_filenames() {
if !dir_name.contains(query_name) {
return false;
}
}
let mut subpath = path.as_str();
for keyword in keywords.iter() {
match subpath.find(keyword) {
Some(idx) => subpath = &subpath[idx + keyword.len()..],
None => return false,
}
}
true
}
}
#[cfg(test)]
mod tests {
use super::Query;
#[test]
fn test_query_normalization() {
assert!(Query::new(&["fOo", "bAr"]).matches("/foo/bar"));
}
#[test]
fn test_query_filename() {
assert!(Query::new(&["ba"]).matches("/foo/bar"));
}
#[test]
fn test_query_not_filename() {
assert!(!Query::new(&["fo"]).matches("/foo/bar"));
}
#[test]
fn test_query_not_filename_slash() {
assert!(!Query::new(&["foo/"]).matches("/foo/bar"));
}
#[test]
fn test_query_path_separator() {
assert!(Query::new(&["/", "fo", "/", "ar"]).matches("/foo/bar"));
}
#[test]
fn test_query_path_separator_between() {
assert!(Query::new(&["oo/ba"]).matches("/foo/bar"));
}
#[test]
fn test_query_overlap_text() {
assert!(!Query::new(&["foo", "o", "bar"]).matches("/foo/bar"));
}
#[test]
fn test_query_overlap_slash() {
assert!(!Query::new(&["/foo/", "/bar"]).matches("/foo/bar"));
}
#[test]
fn test_query_consecutive_slash() {
assert!(Query::new(&["/foo/", "/baz"]).matches("/foo/bar/baz"));
}
}

View File

@ -0,0 +1,186 @@
use crate::dir::{Dir, Epoch, Rank};
use crate::query::Query;
use anyhow::{bail, Context, Result};
use bincode::Options;
use ordered_float::OrderedFloat;
use serde::{Deserialize, Serialize};
use tempfile::NamedTempFile;
use std::cmp::Reverse;
use std::fs;
use std::io::{self, Write};
use std::path::{Path, PathBuf};
#[derive(Debug)]
pub struct Store {
pub dirs: Vec<Dir>,
pub modified: bool,
data_dir: PathBuf,
}
impl Store {
pub const CURRENT_VERSION: StoreVersion = StoreVersion(3);
const MAX_SIZE: u64 = 8 * 1024 * 1024; // 8 MiB
pub fn open<P: Into<PathBuf>>(data_dir: P) -> Result<Store> {
let data_dir = data_dir.into();
let path = Self::get_path(&data_dir);
let buffer = match fs::read(&path) {
Ok(buffer) => buffer,
Err(e) if e.kind() == io::ErrorKind::NotFound => {
fs::create_dir_all(&data_dir).with_context(|| {
format!("unable to create data directory: {}", path.display())
})?;
return Ok(Store {
dirs: Vec::new(),
modified: false,
data_dir,
});
}
Err(e) => {
Err(e).with_context(|| format!("could not read from store: {}", path.display()))?
}
};
let deserializer = &mut bincode::options()
.with_fixint_encoding()
.with_limit(Self::MAX_SIZE);
let version_size = deserializer
.serialized_size(&Self::CURRENT_VERSION)
.unwrap() as _;
if buffer.len() < version_size {
bail!("data store may be corrupted: {}", path.display());
}
let (buffer_version, buffer_dirs) = buffer.split_at(version_size);
let version = deserializer
.deserialize(buffer_version)
.with_context(|| format!("could not deserialize store version: {}", path.display()))?;
let dirs = match version {
Self::CURRENT_VERSION => deserializer
.deserialize(buffer_dirs)
.with_context(|| format!("could not deserialize store: {}", path.display()))?,
version => bail!(
"unsupported store version, got={}, supported={}: {}",
version.0,
Self::CURRENT_VERSION.0,
path.display()
),
};
Ok(Store {
dirs,
modified: false,
data_dir,
})
}
pub fn save(&mut self) -> Result<()> {
if !self.modified {
return Ok(());
}
let (buffer, buffer_size) = (|| -> bincode::Result<_> {
let version_size = bincode::serialized_size(&Self::CURRENT_VERSION)?;
let dirs_size = bincode::serialized_size(&self.dirs)?;
let buffer_size = version_size + dirs_size;
let mut buffer = Vec::with_capacity(buffer_size as _);
bincode::serialize_into(&mut buffer, &Self::CURRENT_VERSION)?;
bincode::serialize_into(&mut buffer, &self.dirs)?;
Ok((buffer, buffer_size))
})()
.context("could not serialize store")?;
let mut file = NamedTempFile::new_in(&self.data_dir).unwrap();
let _ = file.as_file().set_len(buffer_size);
file.write_all(&buffer).unwrap();
file.persist(Self::get_path(&self.data_dir)).unwrap();
self.modified = false;
Ok(())
}
pub fn add<S: AsRef<str>>(&mut self, path: S, now: Epoch) {
let path = path.as_ref();
debug_assert!(Path::new(path).is_absolute());
match self.dirs.iter_mut().find(|dir| dir.path == path) {
None => self.dirs.push(Dir {
path: path.into(),
last_accessed: now,
rank: 1.0,
}),
Some(dir) => {
dir.last_accessed = now;
dir.rank += 1.0;
}
};
self.modified = true;
}
pub fn iter_matches<'a>(
&'a mut self,
query: &'a Query,
now: Epoch,
) -> impl DoubleEndedIterator<Item = &'a Dir> {
self.dirs
.sort_unstable_by_key(|dir| Reverse(OrderedFloat(dir.get_score(now))));
self.dirs.iter().filter(move |dir| dir.is_match(&query))
}
pub fn remove<S: AsRef<str>>(&mut self, path: S) -> bool {
let path = path.as_ref();
if let Some(idx) = self.dirs.iter().position(|dir| dir.path == path) {
self.dirs.swap_remove(idx);
self.modified = true;
return true;
}
false
}
pub fn age(&mut self, max_age: Rank) {
let sum_age = self.dirs.iter().map(|dir| dir.rank).sum::<Rank>();
if sum_age > max_age {
let factor = 0.9 * max_age / sum_age;
for idx in (0..self.dirs.len()).rev() {
let dir = &mut self.dirs[idx];
dir.rank *= factor;
if dir.rank < 1.0 {
self.dirs.swap_remove(idx);
}
}
self.modified = true;
}
}
fn get_path<P: AsRef<Path>>(data_dir: P) -> PathBuf {
data_dir.as_ref().join("db.zo")
}
}
impl Drop for Store {
fn drop(&mut self) {
if let Err(e) = self.save() {
println!("Error: {}", e)
}
}
}
#[derive(Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct StoreVersion(pub u32);

View File

@ -0,0 +1,43 @@
use zoxide_engine::Store;
#[test]
fn test_add() {
let path = "/foo/bar";
let now = 946684800;
let data_dir = tempfile::tempdir().unwrap();
{
let mut store = Store::open(data_dir.path()).unwrap();
store.add(path, now);
store.add(path, now);
}
{
let store = Store::open(data_dir.path()).unwrap();
assert_eq!(store.dirs.len(), 1);
let dir = &store.dirs[0];
assert_eq!(dir.path, path);
assert_eq!(dir.last_accessed, now);
}
}
#[test]
fn test_remove() {
let path = "/foo/bar";
let now = 946684800;
let data_dir = tempfile::tempdir().unwrap();
{
let mut store = Store::open(data_dir.path()).unwrap();
store.add(path, now);
}
{
let mut store = Store::open(data_dir.path()).unwrap();
assert!(store.remove(path));
}
{
let mut store = Store::open(data_dir.path()).unwrap();
assert!(store.dirs.is_empty());
assert!(!store.remove(path));
}
}

11
crates/zoxide-shell/.gitignore vendored Normal file
View File

@ -0,0 +1,11 @@
# Generated by Cargo
# will have compiled files and executables
debug/
target/
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
Cargo.lock
# These are backup files generated by rustfmt
**/*.rs.bk

View File

@ -0,0 +1,15 @@
[package]
name = "zoxide-shell"
version = "0.1.0"
authors = ["Ajeet D'Souza <98ajeet@gmail.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
anyhow = "1.0.33"
askama = { version = "0.10.3", default-features = false }
[dev-dependencies]
assert_cmd = "1.0.1"
once_cell = "1.4.1"

View File

@ -0,0 +1,97 @@
use anyhow::{Context, Result};
use askama::Template;
use std::io::Write;
use std::ops::Deref;
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Hook {
None,
Prompt,
Pwd,
}
pub trait Generator {
fn generate<W: Write>(&self, writer: &mut W) -> Result<()>;
}
impl<T: Template> Generator for T {
fn generate<W: Write>(&self, writer: &mut W) -> Result<()> {
let source = &self.render().context("could not render template")?;
writeln!(writer, "{}", source).context("could not write to output")?;
Ok(())
}
}
#[derive(Debug)]
pub struct Opts<'a> {
pub cmd: Option<&'a str>,
pub hook: Hook,
pub echo: bool,
pub resolve_symlinks: bool,
}
#[derive(Debug, Template)]
#[template(path = "bash.txt")]
pub struct Bash<'a>(pub &'a Opts<'a>);
impl<'a> Deref for Bash<'a> {
type Target = Opts<'a>;
fn deref(&self) -> &Self::Target {
self.0
}
}
#[derive(Debug, Template)]
#[template(path = "fish.txt")]
pub struct Fish<'a>(pub &'a Opts<'a>);
impl<'a> Deref for Fish<'a> {
type Target = Opts<'a>;
fn deref(&self) -> &Self::Target {
self.0
}
}
#[derive(Debug, Template)]
#[template(path = "posix.txt")]
pub struct Posix<'a>(pub &'a Opts<'a>);
impl<'a> Deref for Posix<'a> {
type Target = Opts<'a>;
fn deref(&self) -> &Self::Target {
self.0
}
}
#[derive(Debug, Template)]
#[template(path = "powershell.txt")]
pub struct PowerShell<'a>(pub &'a Opts<'a>);
impl<'a> Deref for PowerShell<'a> {
type Target = Opts<'a>;
fn deref(&self) -> &Self::Target {
self.0
}
}
#[derive(Debug, Template)]
#[template(path = "xonsh.txt")]
pub struct Xonsh<'a>(pub &'a Opts<'a>);
impl<'a> Deref for Xonsh<'a> {
type Target = Opts<'a>;
fn deref(&self) -> &Self::Target {
self.0
}
}
#[derive(Debug, Template)]
#[template(path = "zsh.txt")]
pub struct Zsh<'a>(pub &'a Opts<'a>);
impl<'a> Deref for Zsh<'a> {
type Target = Opts<'a>;
fn deref(&self) -> &Self::Target {
self.0
}
}

View File

@ -0,0 +1,142 @@
{%- let SECTION = "# =============================================================================\n#" -%}
{%- let NOT_CONFIGURED = "# -- not configured --" -%}
{{ SECTION }}
# Utility functions for zoxide.
#
# pwd based on the value of _ZO_RESOLVE_SYMLINKS.
function __zoxide_pwd() {
{%- if resolve_symlinks %}
pwd -P
{%- else %}
pwd -L
{%- endif %}
}
# cd + custom logic based on the value of _ZO_ECHO.
function __zoxide_cd() {
# shellcheck disable=SC2164
cd "$@" {%- if echo %} && __zoxide_pwd {%- endif %}
}
{{ SECTION }}
# Hook configuration for zoxide.
#
# Hook to add new entries to the database.
{%- match hook %}
{%- when Hook::None %}
{{ NOT_CONFIGURED }}
{%- when Hook::Prompt %}
function __zoxide_hook() {
zoxide add "$(__zoxide_pwd)"
}
{%- when Hook::Pwd %}
function __zoxide_hook() {
local -r __zoxide_pwd_tmp="$(__zoxide_pwd)"
if [ -z "$__zoxide_pwd_old" ]; then
__zoxide_pwd_old="$__zoxide_pwd_tmp"
elif [ "$__zoxide_pwd_old" != "$__zoxide_pwd_tmp" ]; then
__zoxide_pwd_old="$__zoxide_pwd_tmp"
zoxide add "$__zoxide_pwd_old"
fi
}
{%- endmatch %}
# Initialize hook.
{%- if hook == Hook::None %}
{{ NOT_CONFIGURED }}
{%- else %}
case "$PROMPT_COMMAND" in
*__zoxide_hook*) ;;
*) PROMPT_COMMAND="${PROMPT_COMMAND:+${PROMPT_COMMAND};}__zoxide_hook" ;;
esac
{%- endif %}
{{ SECTION }}
# When using zoxide with --no-aliases, alias these internal functions as
# desired.
#
# Jump to a directory using only keywords.
function __zoxide_z() {
if [ "$#" -eq 0 ]; then
__zoxide_cd ~
elif [ "$#" -eq 1 ] && [ "$1" = '-' ]; then
if [ -n "$OLDPWD" ]; then
__zoxide_cd "$OLDPWD"
else
echo "zoxide: \\$OLDPWD is not set"
return 1
fi
else
local __zoxide_result
__zoxide_result="$(zoxide query -- "$@")" && __zoxide_cd "$__zoxide_result"
fi
}
# Jump to a directory using interactive search.
function __zoxide_zi() {
local __zoxide_result
__zoxide_result="$(zoxide query -i -- "$@")" && __zoxide_cd "$__zoxide_result"
}
# Add a new entry to the database.
function __zoxide_za() {
zoxide add "$@"
}
# Query an entry from the database using only keywords.
function __zoxide_zq() {
zoxide query "$@"
}
# Query an entry from the database using interactive selection.
function __zoxide_zqi() {
zoxide query -i "$@"
}
# Remove an entry from the database using the exact path.
function __zoxide_zr() {
zoxide remove "$@"
}
# Remove an entry from the database using interactive selection.
function __zoxide_zri() {
local __zoxide_result
__zoxide_result="$(zoxide query -i -- "$@")" && zoxide remove "$__zoxide_result"
}
{{ SECTION }}
# Convenient aliases for zoxide. Disable these using --no-aliases.
#
{%- match cmd %}
{%- when Some with (cmd) %}
alias {{cmd}}='__zoxide_z'
alias {{cmd}}i='__zoxide_zi'
alias {{cmd}}a='__zoxide_za'
alias {{cmd}}q='__zoxide_zq'
alias {{cmd}}qi='__zoxide_zqi'
alias {{cmd}}r='__zoxide_zr'
alias {{cmd}}ri='__zoxide_zri'
{%- when None %}
{{ NOT_CONFIGURED }}
{%- endmatch %}
{{ SECTION }}
# To initialize zoxide with bash, add the following line to your bash
# configuration file (usually ~/.bashrc):
#
# eval "$(zoxide init bash)"

View File

@ -0,0 +1,139 @@
{%- let SECTION = "# =============================================================================\n#" -%}
{%- let NOT_CONFIGURED = "# -- not configured --" -%}
{{ SECTION }}
# Utility functions for zoxide.
#
# pwd based on the value of _ZO_RESOLVE_SYMLINKS.
function __zoxide_pwd
{%- if resolve_symlinks %}
pwd -P
{%- else %}
pwd -L
{%- endif %}
end
# cd + custom logic based on the value of _ZO_ECHO.
function __zoxide_cd
cd $argv
{%- if echo %}
and __zoxide_pwd
{%- endif %}
and commandline -f repaint
end
{{ SECTION }}
# Hook configuration for zoxide.
#
# Initialize hook to add new entries to the database.
{%- match hook %}
{%- when Hook::None %}
function __zoxide_hook
{%- when Hook::Prompt %}
function __zoxide_hook --on-event fish_prompt
{%- when Hook::Pwd %}
function __zoxide_hook --on-variable PWD
{%- endmatch %}
zoxide add (__zoxide_pwd)
end
{{ SECTION }}
# When using zoxide with --no-aliases, alias these internal functions as
# desired.
#
# Jump to a directory using only keywords.
function __zoxide_z
set argc (count $argv)
if test $argc -eq 0
__zoxide_cd $HOME
else if begin; test $argc -eq 1; and test $argv[1] = '-'; end
__zoxide_cd -
else
set -l __zoxide_result (zoxide query -- $argv)
and __zoxide_cd $__zoxide_result
end
end
# Jump to a directory using interactive search.
function __zoxide_zi
set -l __zoxide_result (zoxide query -i -- $argv)
and __zoxide_cd $__zoxide_result
end
# Add a new entry to the database.
function __zoxide_za
zoxide add $argv
end
# Query an entry from the database using only keywords.
function __zoxide_zq
zoxide query $argv
end
# Query an entry from the database using interactive selection.
function __zoxide_zqi
zoxide query -i $argv
end
# Remove an entry from the database using the exact path.
function __zoxide_zr
zoxide remove $argv
end
# Remove an entry from the database using interactive selection.
function __zoxide_zri
set -l __zoxide_result (zoxide query -i -- $argv)
and zoxide remove $__zoxide_result
end
{{ SECTION }}
# Convenient aliases for zoxide. Disable these using --no-aliases.
#
{%- match cmd %}
{%- when Some with (cmd) %}
function {{cmd}}
__zoxide_z $argv
end
function {{cmd}}i
__zoxide_zi $argv
end
function {{cmd}}a
__zoxide_za $argv
end
function {{cmd}}q
__zoxide_zq $argv
end
function {{cmd}}qi
__zoxide_zqi $argv
end
function {{cmd}}r
__zoxide_zr $argv
end
function {{cmd}}ri
__zoxide_zri $argv
end
{%- when None %}
{{ NOT_CONFIGURED }}
{%- endmatch %}
{{ SECTION }}
# To initialize zoxide with fish, add the following line to your fish
# configuration file (usually ~/.config/fish/config.fish):
#
# zoxide init fish | source

View File

@ -0,0 +1,142 @@
{%- let SECTION = "# =============================================================================\n#" -%}
{%- let NOT_CONFIGURED = "# -- not configured --" -%}
{% if hook == Hook::Pwd -%}
echo "\
zoxide: PWD hooks are not supported on POSIX shells.
Use '--hook prompt' when initializing zoxide."
{%- endif %}
{{ SECTION }}
# Utility functions for zoxide.
#
# pwd based on the value of _ZO_RESOLVE_SYMLINKS.
__zoxide_pwd() {
{%- if resolve_symlinks %}
pwd -P
{%- else %}
pwd -L
{%- endif %}
}
# cd + custom logic based on the value of _ZO_ECHO.
__zoxide_cd() {
# shellcheck disable=SC2164
cd "$@" {%- if echo %} && __zoxide_pwd {%- endif %}
}
{{ SECTION }}
# Hook configuration for zoxide.
#
# Hook to add new entries to the database.
{%- match hook %}
{%- when Hook::None %}
{{ NOT_CONFIGURED }}
{%- when Hook::Prompt %}
__zoxide_hook() {
zoxide add "$(__zoxide_pwd)"
}
{%- when Hook::Pwd %}
{{ NOT_CONFIGURED }}
{%- endmatch %}
# Initialize hook.
{%- match hook %}
{%- when Hook::None %}
{{ NOT_CONFIGURED }}
{%- when Hook::Prompt %}
case "$PS1" in
*\$\(__zoxide_hook\)*) ;;
*) PS1="${PS1}\$(__zoxide_hook)" ;;
esac
{%- when Hook::Pwd %}
{{ NOT_CONFIGURED }}
{%- endmatch %}
{{ SECTION }}
# When using zoxide with --no-aliases, alias these internal functions as
# desired.
#
# Jump to a directory using only keywords.
__zoxide_z() {
if [ "$#" -eq 0 ]; then
__zoxide_cd ~
elif [ "$#" -eq 1 ] && [ "$1" = '-' ]; then
if [ -n "$OLDPWD" ]; then
__zoxide_cd "$OLDPWD"
else
echo "zoxide: \\$OLDPWD is not set"
return 1
fi
else
__zoxide_result="$(zoxide query -- "$@")" && __zoxide_cd "$__zoxide_result"
fi
}
# Jump to a directory using interactive search.
__zoxide_zi() {
__zoxide_result="$(zoxide query -i -- "$@")" && __zoxide_cd "$__zoxide_result"
}
# Add a new entry to the database.
__zoxide_za() {
zoxide add "$@"
}
# Query an entry from the database using only keywords.
__zoxide_zq() {
zoxide query "$@"
}
# Query an entry from the database using interactive selection.
__zoxide_zqi() {
zoxide query -i "$@"
}
# Remove an entry from the database using the exact path.
__zoxide_zr() {
zoxide remove "$@"
}
# Remove an entry from the database using interactive selection.
__zoxide_zri() {
__zoxide_result="$(zoxide query -i -- "$@")" && zoxide remove "$__zoxide_result"
}
{{ SECTION }}
# Convenient aliases for zoxide. Disable these using --no-aliases.
#
{%- match cmd %}
{%- when Some with (cmd) %}
alias {{cmd}}='__zoxide_z'
alias {{cmd}}i='__zoxide_zi'
alias {{cmd}}a='__zoxide_za'
alias {{cmd}}q='__zoxide_zq'
alias {{cmd}}qi='__zoxide_zqi'
alias {{cmd}}r='__zoxide_zr'
alias {{cmd}}ri='__zoxide_zri'
{%- when None %}
{{ NOT_CONFIGURED }}
{%- endmatch %}
{{ SECTION }}
# To initialize zoxide with your POSIX shell, add the following line to your
# shell configuration file:
#
# eval "$(zoxide init posix --hook prompt)"

View File

@ -0,0 +1,146 @@
{%- let SECTION = "# =============================================================================\n#" -%}
{%- let NOT_CONFIGURED = "# -- not configured --" -%}
{{ SECTION }}
# Utility functions for zoxide.
#
# pwd based on the value of _ZO_RESOLVE_SYMLINKS.
function __zoxide_pwd {
$(Get-Location).Path
}
# cd + custom logic based on the value of _ZO_ECHO.
function __zoxide_cd($dir) {
Set-Location $dir -ea Stop
{%- if echo %}
__zoxide_pwd
{%- endif %}
}
{{ SECTION }}
# Hook configuration for zoxide.
#
# Hook to add new entries to the database.
function __zoxide_hook {
zoxide add $(__zoxide_pwd)
}
# Initialize hook.
{%- match hook %}
{%- when Hook::None %}
{{ NOT_CONFIGURED }}
{%- when Hook::Prompt %}
$PreZoxidePrompt = $function:prompt
function prompt {
$null = __zoxide_hook
& $PreZoxidePrompt
}
{%- when Hook::Pwd %}
if ($PSVersionTable.PSVersion.Major -ge 6) {
$ExecutionContext.InvokeCommand.LocationChangedAction = {
$null = __zoxide_hook
}
} else {
Write-Error "`
zoxide: PWD hooks are not supported below PowerShell 6.
Use '--hook prompt' when initializing zoxide."
}
{%- endmatch %}
{{ SECTION }}
# When using zoxide with --no-aliases, alias these internal functions as
# desired.
#
# Jump to a directory using only keywords.
function __zoxide_z {
if ($args.Length -eq 0) {
__zoxide_cd ~
}
elseif ($args.Length -eq 1 -and $args[0] -eq '-') {
__zoxide_cd -
}
else {
$__zoxide_result = zoxide query -- @args
if ($LASTEXITCODE -eq 0) {
__zoxide_cd $__zoxide_result
}
}
}
# Jump to a directory using interactive search.
function zi {
$__zoxide_result = zoxide query -i -- @args
if ($LASTEXITCODE -eq 0) {
__zoxide_cd $__zoxide_result
}
}
# Add a new entry to the database.
function __zoxide_za {
zoxide add @args
}
# Query an entry from the database using only keywords.
function __zoxide_zq {
zoxide query @args
}
# Query an entry from the database using interactive selection.
function __zoxide_zqi {
zoxide query -i @args
}
# Remove an entry from the database using the exact path.
function __zoxide_zr {
zoxide remove @args
}
# Remove an entry from the database using interactive selection.
function __zoxide_zri {
$_zoxide_result = zoxide query -i -- @args
if ($LASTEXITCODE -eq 0) {
zoxide remove $_zoxide_result
}
}
{{ SECTION }}
# Convenient aliases for zoxide. Disable these using --no-aliases.
#
{%- match cmd %}
{%- when Some with (cmd) %}
Set-Alias {{cmd}} __zoxide_z
Set-Alias {{cmd}}i __zoxide_zi
Set-Alias {{cmd}}a __zoxide_za
Set-Alias {{cmd}}q __zoxide_zq
Set-Alias {{cmd}}qi __zoxide_zqi
Set-Alias {{cmd}}r __zoxide_zr
Set-Alias {{cmd}}ri __zoxide_zri
{%- when None %}
{{ NOT_CONFIGURED }}
{%- endmatch %}
{{ SECTION }}
# To initialize zoxide with PowerShell, add the following line to your
# PowerShell configuration file (the location is stored in $profile):
#
# Invoke-Expression (& {
# $hook = if ($PSVersionTable.PSVersion.Major -ge 6) {
# 'pwd'
# } else {
# 'prompt'
# }
# (zoxide init powershell --hook $hook) -join "`n"
# })

View File

@ -0,0 +1,114 @@
{%- let SECTION = "# =============================================================================\n#" -%}
{%- let NOT_CONFIGURED = "# -- not configured --" -%}
{%- if resolve_symlinks -%}
import os
{%- endif %}
import os.path
{{ SECTION }}
# Utility functions for zoxide.
#
# pwd based on the value of _ZO_RESOLVE_SYMLINKS.
def __zoxide_pwd() -> str:
{%- if resolve_symlinks %}
return os.getcwd()
{%- else %}
return $PWD
{%- endif %}
# cd + custom logic based on the value of _ZO_ECHO.
def __zoxide_cd(path: str):
cd @(path) {%- if echo %} and print(__zoxide_pwd()) {%- endif %}
{{ SECTION }}
# Hook configuration for zoxide.
#
# Initialize hook to add new entries to the database.
{%- match hook %}
{%- when Hook::None %}
{{ NOT_CONFIGURED }}
{%- when Hook::Prompt %}
@events.on_post_prompt
{%- when Hook::Pwd %}
@events.on_chdir
{%- endmatch %}
def __zoxide_hook(**kwargs):
zoxide add @(__zoxide_pwd())
{{ SECTION }}
# When using zoxide with --no-aliases, alias these internal functions as
# desired.
#
# Jump to a directory using only keywords.
def __zoxide_z(keywords: [str]):
if keywords == []:
__zoxide_cd($HOME)
elif keywords == ['-']:
__zoxide_cd('-')
elif len(keywords) == 1 and os.path.isdir(keywords[0]):
__zoxide_cd(keywords[0])
else:
__zoxide_result = $(zoxide query -- @(keywords))[:-1]
if __zoxide_result:
__zoxide_cd(__zoxide_result)
# Jump to a directory using interactive search.
def __zoxide_zi(keywords: [str]):
__zoxide_result = $(zoxide query -- @(keywords))[:-1]
if __zoxide_result:
__zoxide_cd(__zoxide_result)
# Add a new entry to the database.
def __zoxide_za(args: [str]):
zoxide add @(args)
# Query an entry from the database using only keywords.
def __zoxide_zq(args: [str]):
zoxide query @(args)
# Query an entry from the database using interactive selection.
def __zoxide_zqi(args: [str]):
zoxide query -i @(args)
# Remove an entry from the database using the exact path.
def __zoxide_zr(args: [str]):
zoxide remove @(args)
# Remove an entry from the database using interactive selection.
def __zoxide_zri(keywords: [str]):
__zoxide_result = $(zoxide query -- @(keywords))[:-1]
if __zoxide_result:
zoxide remove @(__zoxide_result)
{{ SECTION }}
# Convenient aliases for zoxide. Disable these using --no-aliases.
#
{%- match cmd %}
{%- when Some with (cmd) %}
aliases['{{cmd}}'] = __zoxide_z
aliases['{{cmd}}i'] = __zoxide_zi
aliases['{{cmd}}a'] = __zoxide_za
aliases['{{cmd}}q'] = __zoxide_zq
aliases['{{cmd}}qi'] = __zoxide_zqi
aliases['{{cmd}}r'] = __zoxide_zr
aliases['{{cmd}}ri'] = __zoxide_zri
{%- when None %}
{{ NOT_CONFIGURED }}
{%- endmatch %}
{{ SECTION }}
# To initialize zoxide with xonsh, add the following line to your xonsh
# configuration file (usually ~/.xonshrc):
#
# execx($(zoxide init xonsh), 'exec', __xonsh__.ctx, filename='zoxide')

View File

@ -0,0 +1,129 @@
{%- let SECTION = "# =============================================================================\n#" -%}
{%- let NOT_CONFIGURED = "# -- not configured --" -%}
{{ SECTION }}
# Utility functions for zoxide.
#
# pwd based on the value of _ZO_RESOLVE_SYMLINKS.
function __zoxide_pwd() {
{%- if resolve_symlinks %}
pwd -P
{%- else %}
pwd -L
{%- endif %}
}
# cd + custom logic based on the value of _ZO_ECHO.
function __zoxide_cd() {
cd "$@" {%- if echo %} && __zoxide_pwd {%- endif %}
}
{{ SECTION }}
# Hook configuration for zoxide.
#
# Hook to add new entries to the database.
function __zoxide_hook() {
zoxide add "$(__zoxide_pwd)"
}
# Initialize hook.
{%- match hook %}
{%- when Hook::None %}
{{ NOT_CONFIGURED }}
{%- when Hook::Prompt %}
[[ -n "${precmd_functions[(r)__zoxide_hook]}" ]] || {
precmd_functions+=(__zoxide_hook)
}
{%- when Hook::Pwd %}
chpwd_functions=(${chpwd_functions[@]} "__zoxide_hook")
{%- endmatch %}
{{ SECTION }}
# When using zoxide with --no-aliases, alias these internal functions as
# desired.
#
# Jump to a directory using only keywords.
function __zoxide_z() {
if [ "$#" -eq 0 ]; then
__zoxide_cd ~
elif [ "$#" -eq 1 ] && [ "$1" = '-' ]; then
if [ -n "$OLDPWD" ]; then
__zoxide_cd "$OLDPWD"
else
echo "zoxide: \\$OLDPWD is not set"
return 1
fi
elif [ "$#" -eq 1 ] && [ -d "$1" ]; then
__zoxide_cd "$1"
else
local __zoxide_result
__zoxide_result="$(zoxide query -- "$@")" && __zoxide_cd "$__zoxide_result"
fi
}
# Jump to a directory using interactive search.
function __zoxide_zi() {
local __zoxide_result
__zoxide_result="$(zoxide query -i -- "$@")" && __zoxide_cd "$__zoxide_result"
}
# Add a new entry to the database.
function __zoxide_za() {
zoxide add "$@"
}
# Query an entry from the database using only keywords.
function __zoxide_zq() {
zoxide query "$@"
}
# Query an entry from the database using interactive selection.
function __zoxide_zqi() {
zoxide query -i "$@"
}
# Remove an entry from the database using the exact path.
function __zoxide_zr() {
zoxide remove "$@"
}
# Remove an entry from the database using interactive selection.
function __zoxide_zri() {
local __zoxide_result
__zoxide_result="$(zoxide query -i -- "$@")" && zoxide remove "$__zoxide_result"
}
{{ SECTION }}
# Convenient aliases for zoxide. Disable these using --no-aliases.
#
{%- match cmd %}
{%- when Some with (cmd) %}
alias {{cmd}}='__zoxide_z'
alias {{cmd}}i='__zoxide_zi'
alias {{cmd}}a='__zoxide_za'
alias {{cmd}}q='__zoxide_zq'
alias {{cmd}}qi='__zoxide_zqi'
alias {{cmd}}r='__zoxide_zr'
alias {{cmd}}ri='__zoxide_zri'
{%- when None %}
{{ NOT_CONFIGURED }}
{%- endmatch %}
{{ SECTION }}
# To initialize zoxide with zsh, add the following line to your zsh
# configuration file (usually ~/.zshrc):
#
# eval "$(zoxide init zsh)"

View File

@ -0,0 +1,151 @@
use askama::Template;
use assert_cmd::Command;
use once_cell::sync::OnceCell;
use zoxide_shell::{Bash, Fish, Hook, Opts, Posix, PowerShell, Xonsh, Zsh};
fn opts() -> &'static [Opts<'static>] {
static OPTS: OnceCell<Vec<Opts>> = OnceCell::new();
OPTS.get_or_init(|| {
let mut opts = Vec::new();
for &echo in &[false, true] {
for &resolve_symlinks in &[false, true] {
for &hook in &[Hook::None, Hook::Prompt, Hook::Pwd] {
for &cmd in &[None, Some("z")] {
opts.push(Opts {
echo,
resolve_symlinks,
hook,
cmd,
});
}
}
}
}
opts
})
}
#[test]
fn test_bash() {
for opts in opts() {
let source = crate::Bash(opts).render().unwrap();
Command::new("bash")
.args(&["-c", &source])
.assert()
.success()
.stdout("")
.stderr("");
}
}
#[test]
fn test_bash_posix() {
for opts in opts() {
let source = crate::Posix(opts).render().unwrap();
let assert = Command::new("bash")
.args(&["--posix", "-c", &source])
.assert()
.success()
.stderr("");
if opts.hook != Hook::Pwd {
assert.stdout("");
}
}
}
#[test]
fn test_dash() {
for opts in opts() {
let source = crate::Posix(opts).render().unwrap();
let assert = Command::new("bash")
.args(&["--posix", "-c", &source])
.assert()
.success()
.stderr("");
if opts.hook != Hook::Pwd {
assert.stdout("");
}
}
}
#[test]
fn test_fish() {
for opts in opts() {
let source = crate::Fish(opts).render().unwrap();
Command::new("fish")
.args(&["-c", &source])
.assert()
.success()
.stdout("")
.stderr("");
}
}
#[test]
fn test_pwsh() {
for opts in opts() {
let source = crate::PowerShell(opts).render().unwrap();
Command::new("pwsh")
.args(&["-c", &source])
.assert()
.success()
.stdout("")
.stderr("");
}
}
#[test]
fn test_shellcheck_bash() {
for opts in opts() {
let source = crate::Bash(opts).render().unwrap();
Command::new("shellcheck")
.args(&["--shell", "bash", "-"])
.write_stdin(source.as_bytes())
.assert()
.success()
.stdout("")
.stderr("");
}
}
#[test]
fn test_shellcheck_sh() {
for opts in opts() {
let source = crate::Posix(opts).render().unwrap();
Command::new("shellcheck")
.args(&["--shell", "sh", "-"])
.write_stdin(source.as_bytes())
.assert()
.success()
.stdout("")
.stderr("");
}
}
#[test]
fn test_xonsh() {
for opts in opts() {
let source = crate::Xonsh(opts).render().unwrap();
Command::new("xonsh")
.args(&["-c", &source])
.assert()
.success()
.stdout("")
.stderr("");
}
}
#[test]
fn test_zsh() {
for opts in opts() {
let source = crate::Zsh(opts).render().unwrap();
Command::new("zsh")
.args(&["-c", &source])
.assert()
.success()
.stdout("")
.stderr("");
}
}

View File

@ -1,10 +1,9 @@
use crate::db::Rank;
use anyhow::{bail, Context, Result};
use dirs_next as dirs;
use zoxide_engine::dir::Rank;
use std::env;
use std::ffi::OsString;
use std::fs;
use std::path::PathBuf;
pub fn zo_data_dir() -> Result<PathBuf> {
@ -19,11 +18,6 @@ pub fn zo_data_dir() -> Result<PathBuf> {
},
};
// This will fail when `data_dir` points to a file or a broken symlink, but
// will no-op on a valid symlink (to a directory), or an actual directory.
fs::create_dir_all(&data_dir)
.with_context(|| format!("could not create data directory: {}", data_dir.display()))?;
Ok(data_dir)
}

294
src/db.rs
View File

@ -1,294 +0,0 @@
use anyhow::{bail, Context, Result};
use bincode::Options;
use float_ord::FloatOrd;
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use std::cmp::Reverse;
use std::fmt::{self, Display, Formatter};
use std::fs::{self, OpenOptions};
use std::io::{self, Write};
use std::path::{Path, PathBuf};
#[derive(Debug, Deserialize, Eq, PartialEq, Serialize)]
struct DbVersion(u32);
pub struct Db {
pub dirs: Vec<Dir>,
pub modified: bool,
data_dir: PathBuf,
}
impl Db {
const CURRENT_VERSION: DbVersion = DbVersion(3);
const MAX_SIZE: u64 = 8 * 1024 * 1024; // 8 MiB
pub fn open(data_dir: PathBuf) -> Result<Db> {
fs::create_dir_all(&data_dir)
.with_context(|| format!("unable to create data directory: {}", data_dir.display()))?;
let path = Self::get_path(&data_dir);
let buffer = match fs::read(&path) {
Ok(buffer) => buffer,
Err(e) if e.kind() == io::ErrorKind::NotFound => {
return Ok(Db {
dirs: Vec::new(),
modified: false,
data_dir,
})
}
Err(e) => {
return Err(e)
.with_context(|| format!("could not read from database: {}", path.display()))
}
};
if buffer.is_empty() {
return Ok(Db {
dirs: Vec::new(),
modified: false,
data_dir,
});
}
let deserializer = &mut bincode::options()
.with_fixint_encoding()
.with_limit(Self::MAX_SIZE);
let version_size = deserializer
.serialized_size(&Self::CURRENT_VERSION)
.context("could not determine size of database version field")?
as _;
if buffer.len() < version_size {
bail!("database is corrupted: {}", path.display());
}
let (buffer_version, buffer_dirs) = buffer.split_at(version_size);
let version = deserializer.deserialize(buffer_version).with_context(|| {
format!("could not deserialize database version: {}", path.display())
})?;
let dirs = match version {
Self::CURRENT_VERSION => deserializer
.deserialize(buffer_dirs)
.with_context(|| format!("could not deserialize database: {}", path.display()))?,
DbVersion(version_num) => bail!(
"zoxide {} does not support schema v{}: {}",
env!("ZOXIDE_VERSION"),
version_num,
path.display(),
),
};
Ok(Db {
dirs,
modified: false,
data_dir,
})
}
pub fn save(&mut self) -> Result<()> {
if !self.modified {
return Ok(());
}
let (buffer, buffer_size) = (|| -> bincode::Result<_> {
let version_size = bincode::serialized_size(&Self::CURRENT_VERSION)?;
let dirs_size = bincode::serialized_size(&self.dirs)?;
let buffer_size = version_size + dirs_size;
let mut buffer = Vec::with_capacity(buffer_size as _);
bincode::serialize_into(&mut buffer, &Self::CURRENT_VERSION)?;
bincode::serialize_into(&mut buffer, &self.dirs)?;
Ok((buffer, buffer_size))
})()
.context("could not serialize database")?;
let db_path_tmp = Self::get_path_tmp(&self.data_dir);
let mut db_file_tmp = OpenOptions::new()
.create_new(true)
.write(true)
.open(&db_path_tmp)
.with_context(|| {
format!(
"could not create temporary database: {}",
db_path_tmp.display()
)
})?;
// File::set_len() can fail on some filesystems, so we ignore errors
let _ = db_file_tmp.set_len(buffer_size);
(|| -> anyhow::Result<()> {
db_file_tmp.write_all(&buffer).with_context(|| {
format!(
"could not write to temporary database: {}",
db_path_tmp.display()
)
})?;
let db_path = Self::get_path(&self.data_dir);
fs::rename(&db_path_tmp, &db_path)
.with_context(|| format!("could not create database: {}", db_path.display()))
})()
.map_err(|e| {
fs::remove_file(&db_path_tmp)
.with_context(|| {
format!(
"could not remove temporary database: {}",
db_path_tmp.display()
)
})
.err()
.unwrap_or(e)
})?;
self.modified = true;
Ok(())
}
pub fn matches<'a>(
&'a mut self,
now: Epoch,
keywords: &[String],
) -> impl Iterator<Item = &'a Dir> {
self.dirs
.sort_unstable_by_key(|dir| Reverse(FloatOrd(dir.get_score(now))));
let keywords: Vec<String> = keywords
.iter()
.map(|keyword| keyword.to_lowercase())
.collect();
self.dirs
.iter()
.filter(move |dir| dir.is_match(&keywords) && dir.is_valid())
}
fn get_path<P: AsRef<Path>>(data_dir: P) -> PathBuf {
data_dir.as_ref().join("db.zo")
}
fn get_path_tmp<P: AsRef<Path>>(data_dir: P) -> PathBuf {
let file_name = format!("db-{}.zo.tmp", Uuid::new_v4());
data_dir.as_ref().join(file_name)
}
}
impl Drop for Db {
fn drop(&mut self) {
if let Err(e) = self.save() {
eprintln!("{:#}", e);
}
}
}
pub type Rank = f64;
pub type Epoch = i64; // use a signed integer so subtraction can be performed on it
#[derive(Debug, Deserialize, Serialize)]
pub struct Dir {
pub path: String,
pub rank: Rank,
pub last_accessed: Epoch,
}
impl Dir {
pub fn is_valid(&self) -> bool {
self.rank.is_finite() && self.rank >= 1.0 && Path::new(&self.path).is_dir()
}
pub fn is_match(&self, query: &[String]) -> bool {
let path_lower = self.path.to_lowercase();
let get_filenames = || {
let query_name = Path::new(query.last()?).file_name()?.to_str()?;
let dir_name = Path::new(&path_lower).file_name()?.to_str()?;
Some((query_name, dir_name))
};
if let Some((query_name, dir_name)) = get_filenames() {
if !dir_name.contains(query_name) {
return false;
}
}
let mut subpath = path_lower.as_str();
for subquery in query.iter() {
match subpath.find(subquery) {
Some(idx) => subpath = &subpath[idx + subquery.len()..],
None => return false,
}
}
true
}
pub fn get_score(&self, now: Epoch) -> Rank {
const HOUR: Epoch = 60 * 60;
const DAY: Epoch = 24 * HOUR;
const WEEK: Epoch = 7 * DAY;
let duration = now - self.last_accessed;
if duration < HOUR {
self.rank * 4.0
} else if duration < DAY {
self.rank * 2.0
} else if duration < WEEK {
self.rank * 0.5
} else {
self.rank * 0.25
}
}
pub fn display(&self) -> DirDisplay {
DirDisplay { dir: self }
}
pub fn display_score(&self, now: Epoch) -> DirScoreDisplay {
DirScoreDisplay { dir: self, now }
}
}
pub struct DirDisplay<'a> {
dir: &'a Dir,
}
impl Display for DirDisplay<'_> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{}", self.dir.path)
}
}
pub struct DirScoreDisplay<'a> {
dir: &'a Dir,
now: Epoch,
}
impl Display for DirScoreDisplay<'_> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
const SCORE_MIN: Rank = 0.0;
const SCORE_MAX: Rank = 9999.0;
let score = self.dir.get_score(self.now);
let score_clamped = if score > SCORE_MAX {
SCORE_MAX
} else if score > SCORE_MIN {
score
} else {
SCORE_MIN
};
write!(f, "{:>4.0} {}", score_clamped, self.dir.path)
}
}

View File

@ -1,12 +0,0 @@
use std::fmt::{self, Display};
#[derive(Debug)]
pub struct SilentExit {
pub code: i32,
}
impl Display for SilentExit {
fn fmt(&self, _: &mut fmt::Formatter) -> fmt::Result {
Ok(())
}
}

View File

@ -1,62 +0,0 @@
use crate::config::zo_fzf_opts;
use crate::error::SilentExit;
use anyhow::{bail, Context, Result};
use std::io::Write;
use std::process::{Child, Command, Stdio};
pub struct Fzf {
child: Child,
}
impl Fzf {
pub fn new() -> Result<Self> {
let mut command = Command::new("fzf");
command
.arg("-n2..")
.stdin(Stdio::piped())
.stdout(Stdio::piped());
if let Some(fzf_opts) = zo_fzf_opts() {
command.env("FZF_DEFAULT_OPTS", fzf_opts);
}
let child = command.spawn().context("could not launch fzf")?;
Ok(Fzf { child })
}
pub fn write(&mut self, line: String) -> Result<()> {
// unwrap() is safe here since we have captured `stdin`
let stdin = self.child.stdin.as_mut().unwrap();
writeln!(stdin, "{}", line).context("could not write into fzf stdin")
}
pub fn wait_select(self) -> Result<Option<String>> {
let output = self
.child
.wait_with_output()
.context("wait failed on fzf")?;
match output.status.code() {
// normal exit
Some(0) => String::from_utf8(output.stdout)
.context("invalid utf-8 sequence in fzf output")
.map(Some),
// no match
Some(1) => Ok(None),
// error
Some(2) => bail!("fzf returned an error"),
// terminated by a signal
Some(code @ 130) => bail!(SilentExit { code }),
Some(128..=254) | None => bail!("fzf was terminated"),
// unknown
_ => bail!("fzf returned an unknown error"),
}
}
}

2
src/lib.rs Normal file
View File

@ -0,0 +1,2 @@
pub mod config;
pub mod util;

View File

@ -1,42 +1,232 @@
#![forbid(unsafe_code)]
use anyhow::{Context, Result};
use clap::{AppSettings, ArgEnum, Clap};
use once_cell::sync::OnceCell;
use zoxide::{config, util};
use zoxide_engine::{Dir, Query, Store};
use zoxide_shell::{self as zs, Generator};
mod config;
mod db;
mod error;
mod fzf;
mod subcommand;
mod util;
use std::io::{self, Write};
use std::path::{Path, PathBuf};
use crate::error::SilentExit;
fn env_help() -> &'static str {
static ENV_HELP: OnceCell<String> = OnceCell::new();
ENV_HELP.get_or_init(|| {
const PATH_SPLIT_SEPARATOR: u8 = if cfg!(any(target_os = "redox", target_os = "windows")) {
b';'
} else {
b':'
};
use anyhow::Result;
use structopt::StructOpt;
format!(
"\
ENVIRONMENT VARIABLES:
_ZO_DATA_DIR Path for zoxide data files (current: `{data_dir}`)
_ZO_ECHO Prints the matched directory before navigating to it when set to 1
_ZO_EXCLUDE_DIRS List of directories to be excluded, separated by `{split_paths_separator}`
_ZO_FZF_OPTS Custom flags to pass to fzf
_ZO_MAXAGE Maximum total age after which entries start getting deleted
_ZO_RESOLVE_SYMLINKS Resolve symlinks when storing paths",
data_dir=config::zo_data_dir().unwrap_or_else(|_| "none".into()).display(),
split_paths_separator=PATH_SPLIT_SEPARATOR as char)
})
}
use std::process;
// TODO: import
// TODO: query interactive
#[derive(Debug, Clap)]
#[clap(
about,
author,
global_setting(AppSettings::ColoredHelp),
global_setting(AppSettings::GlobalVersion),
global_setting(AppSettings::VersionlessSubcommands),
version = env!("ZOXIDE_VERSION"))]
enum Opts {
/// Adds a new directory or increments its rank
Add { path: Option<PathBuf> },
#[derive(Debug, StructOpt)]
#[structopt(about, version = env!("ZOXIDE_VERSION"))]
enum Zoxide {
Add(subcommand::Add),
Import(subcommand::Import),
Init(subcommand::Init),
Query(subcommand::Query),
Remove(subcommand::Remove),
/// Generates shell configuration
#[clap(after_help(env_help()))]
Init {
#[clap(arg_enum)]
shell: Shell,
/// Prevents zoxide from defining any commands
#[clap(long)]
no_aliases: bool,
/// Renames the 'z' command and corresponding aliases
#[clap(long, default_value = "z")]
cmd: String,
/// Chooses event upon which an entry is added to the database
#[clap(arg_enum, long, default_value = "pwd")]
hook: Hook,
},
/// Searches for a directory
Query {
keywords: Vec<String>,
/// Lists all matching directories
#[clap(long, short)]
list: bool,
/// Prints score with results
#[clap(long, short)]
score: bool,
},
/// Removes a directory
Remove { path: String },
}
#[derive(ArgEnum, Debug)]
enum Shell {
Bash,
Fish,
Posix,
Powershell,
Xonsh,
Zsh,
}
#[derive(ArgEnum, Debug)]
enum Hook {
None,
Prompt,
Pwd,
}
pub fn main() -> Result<()> {
let opt = Zoxide::from_args();
let opts = Opts::parse();
let res = match opt {
Zoxide::Add(add) => add.run(),
Zoxide::Import(import) => import.run(),
Zoxide::Init(init) => init.run(),
Zoxide::Query(query) => query.run(),
Zoxide::Remove(remove) => remove.run(),
};
match opts {
Opts::Add { path } => {
let path = match path {
Some(path) => {
if config::zo_resolve_symlinks() {
util::canonicalize(&path)
} else {
util::resolve_path(&path)
}
}
None => util::current_dir(),
}?;
res.map_err(|e| match e.downcast::<SilentExit>() {
Ok(SilentExit { code }) => process::exit(code),
Err(e) => e,
})
if config::zo_exclude_dirs()?
.iter()
.any(|pattern| pattern.matches_path(&path))
{
return Ok(());
}
let path = util::path_to_str(&path)?;
let now = util::current_time()?;
let data_dir = config::zo_data_dir()?;
let max_age = config::zo_maxage()?;
let mut store = Store::open(&data_dir)?;
store.add(path, now);
store.age(max_age);
Ok(())
}
Opts::Init {
shell,
no_aliases,
cmd,
hook,
} => {
let cmd = if no_aliases { None } else { Some(cmd.as_str()) };
let hook = match hook {
Hook::None => zs::Hook::None,
Hook::Prompt => zs::Hook::Prompt,
Hook::Pwd => zs::Hook::Pwd,
};
let echo = config::zo_echo();
let resolve_symlinks = config::zo_resolve_symlinks();
let opts = &zs::Opts {
cmd,
hook,
echo,
resolve_symlinks,
};
let stdout = io::stdout();
let handle = &mut stdout.lock();
match shell {
Shell::Bash => zs::Bash(opts).generate(handle),
Shell::Fish => zs::Bash(opts).generate(handle),
Shell::Posix => zs::Bash(opts).generate(handle),
Shell::Powershell => zs::Bash(opts).generate(handle),
Shell::Xonsh => zs::Xonsh(opts).generate(handle),
Shell::Zsh => zs::Zsh(opts).generate(handle),
}?;
Ok(())
}
Opts::Query {
keywords,
list,
score,
} => {
let data_dir = config::zo_data_dir()?;
let mut store = Store::open(&data_dir)?;
let query = Query::new(&keywords);
let now = util::current_time()?;
let stdout = io::stdout();
let mut handle = stdout.lock();
let mut print_dir = |dir: &Dir| {
if score {
let dir_score = dir.get_score(now);
let dir_score_clamped = if dir_score > 9999.0 {
9999
} else if dir_score > 0.0 {
dir_score as _
} else {
0
};
writeln!(&mut handle, "{:>4} {}", dir_score_clamped, dir.path)
} else {
writeln!(&mut handle, "{}", dir.path)
}
.unwrap()
};
let mut matches = store
.iter_matches(&query, now)
.filter(|dir| Path::new(&dir.path).is_dir());
if list {
for dir in matches {
print_dir(dir);
}
} else {
let dir = matches.next().context("no match found")?;
print_dir(dir);
}
Ok(())
}
Opts::Remove { path } => {
let data_dir = config::zo_data_dir()?;
let mut store = Store::open(&data_dir)?;
store.remove(path);
Ok(())
}
}
}

View File

@ -1,86 +0,0 @@
use crate::config;
use crate::db::{Db, Dir, Rank};
use crate::util;
use anyhow::Result;
use structopt::StructOpt;
use std::path::{Path, PathBuf};
/// Add a new directory or increment its rank
#[derive(Debug, StructOpt)]
#[structopt()]
pub struct Add {
path: Option<PathBuf>,
}
impl Add {
pub fn run(&self) -> Result<()> {
let current_dir;
let path = match &self.path {
Some(path) => path,
None => {
current_dir = util::get_current_dir()?;
&current_dir
}
};
add(&path)
}
}
fn add<P: AsRef<Path>>(path: P) -> Result<()> {
let path = path.as_ref();
let path = if config::zo_resolve_symlinks() {
util::canonicalize(&path)?
} else {
util::resolve_path(&path)?
};
if config::zo_exclude_dirs()?
.iter()
.any(|pattern| pattern.matches_path(&path))
{
return Ok(());
}
let mut db = util::get_db()?;
let now = util::get_current_time()?;
let path = util::path_to_str(&path)?;
let maxage = config::zo_maxage()?;
match db.dirs.iter_mut().find(|dir| dir.path == path) {
None => db.dirs.push(Dir {
path: path.to_string(),
last_accessed: now,
rank: 1.0,
}),
Some(dir) => {
dir.last_accessed = now;
dir.rank += 1.0;
}
};
age(&mut db, maxage);
db.modified = true;
Ok(())
}
fn age(db: &mut Db, maxage: Rank) {
let sum_age = db.dirs.iter().map(|dir| dir.rank).sum::<Rank>();
if sum_age > maxage {
let factor = 0.9 * maxage / sum_age;
for dir in &mut db.dirs {
dir.rank *= factor;
}
for idx in (0..db.dirs.len()).rev() {
let dir = &db.dirs[idx];
if dir.rank < 1.0 {
db.dirs.swap_remove(idx);
}
}
}
}

View File

@ -1,95 +0,0 @@
use crate::config;
use crate::db::{Db, Dir};
use crate::util;
use anyhow::{bail, Context, Result};
use structopt::StructOpt;
use std::fs;
use std::path::{Path, PathBuf};
/// Import from z database
#[derive(Debug, StructOpt)]
#[structopt()]
pub struct Import {
path: PathBuf,
/// Merge entries into existing database
#[structopt(long)]
merge: bool,
}
impl Import {
pub fn run(&self) -> Result<()> {
import(&self.path, self.merge)
}
}
fn import<P: AsRef<Path>>(path: P, merge: bool) -> Result<()> {
let path = path.as_ref();
let mut db = util::get_db()?;
if !db.dirs.is_empty() && !merge {
bail!(
"To prevent conflicts, you can only import from z with an empty zoxide database!\n\
If you wish to merge the two, specify the `--merge` flag."
);
}
let buffer = fs::read_to_string(&path)
.with_context(|| format!("could not read z database: {}", path.display()))?;
for (idx, line) in buffer.lines().enumerate() {
if let Err(e) = import_line(&mut db, line, config::zo_resolve_symlinks()) {
let line_num = idx + 1;
eprintln!("Error on line {}: {}", line_num, e);
}
}
db.modified = true;
println!("Completed import.");
Ok(())
}
fn import_line(db: &mut Db, line: &str, resolve_symlinks: bool) -> Result<()> {
let mut split_line = line.rsplitn(3, '|');
let (path, epoch_str, rank_str) = (|| {
let epoch_str = split_line.next()?;
let rank_str = split_line.next()?;
let path = split_line.next()?;
Some((path, epoch_str, rank_str))
})()
.with_context(|| format!("invalid entry: {}", line))?;
let epoch = epoch_str
.parse::<i64>()
.with_context(|| format!("invalid epoch: {}", epoch_str))?;
let rank = rank_str
.parse::<f64>()
.with_context(|| format!("invalid rank: {}", rank_str))?;
let path = if resolve_symlinks {
util::canonicalize(&path)?
} else {
util::resolve_path(&path)?
};
let path = util::path_to_str(&path)?;
// If the path exists in the database, add the ranks and set the epoch to
// the more recent of the parsed epoch and the already present epoch.
if let Some(dir) = db.dirs.iter_mut().find(|dir| dir.path == path) {
dir.rank += rank;
dir.last_accessed = epoch.max(dir.last_accessed);
} else {
db.dirs.push(Dir {
path: path.to_string(),
rank,
last_accessed: epoch,
});
}
Ok(())
}

View File

@ -1,182 +0,0 @@
use anyhow::Result;
use std::io::Write;
use super::{Hook, Init};
use crate::config;
pub fn run<W: Write>(writer: &mut W, options: &Init) -> Result<()> {
const NOT_CONFIGURED: &str = "\
# -- not configured --";
let __zoxide_pwd = if config::zo_resolve_symlinks() {
"\
__zoxide_pwd() {
pwd -P
}"
} else {
"\
__zoxide_pwd() {
pwd -L
}"
};
let __zoxide_cd = if config::zo_echo() {
"\
__zoxide_cd() {
cd \"$@\" || return \"$?\"
__zoxide_pwd
}"
} else {
"\
__zoxide_cd() {
cd \"$@\" || return \"$?\"
}"
};
let __zoxide_hook = match options.hook {
Hook::none => NOT_CONFIGURED,
Hook::prompt => {
"\
__zoxide_hook() {
zoxide add \"$(__zoxide_pwd)\"
}"
}
Hook::pwd => {
"\
__zoxide_hook() {
local -r __zoxide_pwd_tmp=\"$(__zoxide_pwd)\"
if [ -z \"$__zoxide_pwd_old\" ]; then
__zoxide_pwd_old=\"$__zoxide_pwd_tmp\"
elif [ \"$__zoxide_pwd_old\" != \"$__zoxide_pwd_tmp\" ]; then
__zoxide_pwd_old=\"$__zoxide_pwd_tmp\"
zoxide add \"$__zoxide_pwd_old\"
fi
}"
}
};
let hook_init = match options.hook {
Hook::none => NOT_CONFIGURED,
_ => {
"\
case \"$PROMPT_COMMAND\" in
*__zoxide_hook*) ;;
*) PROMPT_COMMAND=\"${PROMPT_COMMAND:+${PROMPT_COMMAND};}__zoxide_hook\" ;;
esac"
}
};
let aliases = if options.no_aliases {
NOT_CONFIGURED.into()
} else {
format!(
"\
alias {}='__zoxide_z'
alias {cmd}i='__zoxide_zi'
alias {cmd}a='__zoxide_za'
alias {cmd}q='__zoxide_zq'
alias {cmd}qi='__zoxide_zqi'
alias {cmd}r='__zoxide_zr'
alias {cmd}ri='__zoxide_zri'",
cmd = options.cmd
)
};
write!(
writer,
"\
# =============================================================================
#
# Utility functions for zoxide.
#
# pwd based on the value of _ZO_RESOLVE_SYMLINKS.
{__zoxide_pwd}
# cd + custom logic based on the value of _ZO_ECHO.
{__zoxide_cd}
# =============================================================================
#
# Hook configuration for zoxide.
#
# Hook to add new entries to the database.
{__zoxide_hook}
# Initialize hook.
{hook_init}
# =============================================================================
#
# When using zoxide with --no-aliases, alias these internal functions as
# desired.
#
# Jump to a directory using only keywords.
__zoxide_z() {{
if [ \"$#\" -eq 0 ]; then
__zoxide_cd ~
elif [ \"$#\" -eq 1 ] && [ \"$1\" = '-' ]; then
if [ -n \"$OLDPWD\" ]; then
__zoxide_cd \"$OLDPWD\"
else
echo \"zoxide: \\$OLDPWD is not set\"
return 1
fi
else
local __zoxide_result
__zoxide_result=\"$(zoxide query -- \"$@\")\" && __zoxide_cd \"$__zoxide_result\"
fi
}}
# Jump to a directory using interactive search.
__zoxide_zi() {{
local __zoxide_result
__zoxide_result=\"$(zoxide query -i -- \"$@\")\" && __zoxide_cd \"$__zoxide_result\"
}}
# Add a new entry to the database.
alias __zoxide_za='zoxide add'
# Query an entry from the database using only keywords.
alias __zoxide_zq='zoxide query'
# Query an entry from the database using interactive selection.
alias __zoxide_zqi='zoxide query -i'
# Remove an entry from the database using the exact path.
alias __zoxide_zr='zoxide remove'
# Remove an entry from the database using interactive selection.
__zoxide_zri() {{
local __zoxide_result
__zoxide_result=\"$(zoxide query -i -- \"$@\")\" && zoxide remove \"$__zoxide_result\"
}}
# =============================================================================
#
# Convenient aliases for zoxide. Disable these using --no-aliases.
#
{aliases}
# =============================================================================
#
# To initialize zoxide with bash, add the following line to your bash
# configuration file (usually ~/.bashrc):
#
# eval \"$(zoxide init bash)\"
",
__zoxide_pwd = __zoxide_pwd,
__zoxide_cd = __zoxide_cd,
__zoxide_hook = __zoxide_hook,
hook_init = hook_init,
aliases = aliases,
)?;
Ok(())
}

View File

@ -1,191 +0,0 @@
use anyhow::Result;
use std::io::Write;
use super::{Hook, Init};
use crate::config;
pub fn run<W: Write>(writer: &mut W, options: &Init) -> Result<()> {
const NOT_CONFIGURED: &str = "\
# -- not configured --";
let __zoxide_pwd = if config::zo_resolve_symlinks() {
"\
function __zoxide_pwd
pwd -P
end"
} else {
"\
function __zoxide_pwd
pwd -L
end"
};
let __zoxide_cd = if config::zo_echo() {
"\
function __zoxide_cd
cd $argv
or return $status
commandline -f repaint
__zoxide_pwd
end"
} else {
"\
function __zoxide_cd
cd $argv
or return $status
commandline -f repaint
end"
};
let __zoxide_hook = "\
function __zoxide_hook
zoxide add (__zoxide_pwd)
end";
let hook_init = match options.hook {
Hook::none => NOT_CONFIGURED,
Hook::prompt => {
"\
function __zoxide_hook_prompt --on-event fish_prompt
__zoxide_hook
end"
}
Hook::pwd => {
"\
function __zoxide_hook_pwd --on-variable PWD
__zoxide_hook
end"
}
};
let aliases = if options.no_aliases {
NOT_CONFIGURED.into()
} else {
format!(
"\
function {cmd}
__zoxide_z $argv
end
function {cmd}i
__zoxide_zi $argv
end
function {cmd}a
__zoxide_za $argv
end
function {cmd}q
__zoxide_zq $argv
end
function {cmd}qi
__zoxide_zqi $argv
end
function {cmd}r
__zoxide_zr $argv
end
function {cmd}ri
__zoxide_zri $argv
end",
cmd = options.cmd
)
};
writeln!(
writer,
"\
# =============================================================================
#
# Utility functions for zoxide.
#
# pwd based on the value of _ZO_RESOLVE_SYMLINKS.
{__zoxide_pwd}
# cd + custom logic based on the value of _ZO_ECHO.
{__zoxide_cd}
# =============================================================================
#
# Hook configuration for zoxide.
#
# Hook to add new entries to the database.
{__zoxide_hook}
# Initialize hook.
{hook_init}
# =============================================================================
#
# When using zoxide with --no-aliases, alias these internal functions as
# desired.
#
# Jump to a directory using only keywords.
function __zoxide_z
set argc (count $argv)
if test $argc -eq 0
__zoxide_cd $HOME
else if begin; test $argc -eq 1; and test $argv[1] = '-'; end
__zoxide_cd -
else
set -l __zoxide_result (zoxide query -- $argv)
and __zoxide_cd $__zoxide_result
end
end
# Jump to a directory using interactive search.
function __zoxide_zi
set -l __zoxide_result (zoxide query -i -- $argv)
and __zoxide_cd $__zoxide_result
end
# Add a new entry to the database.
abbr -a __zoxide_za 'zoxide add'
# Query an entry from the database using only keywords.
abbr -a __zoxide_zq 'zoxide query'
# Query an entry from the database using interactive selection.
abbr -a __zoxide_zqi 'zoxide query -i'
# Remove an entry from the database using the exact path.
abbr -a __zoxide_zr 'zoxide remove'
# Remove an entry from the database using interactive selection.
function __zoxide_zri
set -l __zoxide_result (zoxide query -i -- $argv)
and zoxide remove $__zoxide_result
end
# =============================================================================
#
# Convenient aliases for zoxide. Disable these using --no-aliases.
#
{aliases}
# =============================================================================
#
# To initialize zoxide with fish, add the following line to your fish
# configuration file (usually ~/.config/fish/config.fish):
#
# zoxide init fish | source
",
__zoxide_pwd = __zoxide_pwd,
__zoxide_cd = __zoxide_cd,
__zoxide_hook = __zoxide_hook,
hook_init = hook_init,
aliases = aliases,
)?;
Ok(())
}

View File

@ -1,74 +0,0 @@
mod bash;
mod fish;
mod posix;
mod powershell;
mod zsh;
use anyhow::{Context, Result};
use clap::arg_enum;
use structopt::StructOpt;
use std::io;
/// Generates shell configuration
#[derive(Debug, StructOpt)]
#[structopt()]
pub struct Init {
#[structopt(possible_values = &Shell::variants(), case_insensitive = true)]
shell: Shell,
/// Renames the 'z' command and corresponding aliases
#[structopt(long, alias = "z-cmd", default_value = "z")]
cmd: String,
/// Prevents zoxide from defining any commands
#[structopt(long, alias = "no-define-aliases")]
no_aliases: bool,
/// Chooses event on which an entry is added to the database
#[structopt(
long,
possible_values = &Hook::variants(),
default_value = "pwd",
case_insensitive = true
)]
hook: Hook,
}
impl Init {
pub fn run(&self) -> Result<()> {
let stdout = io::stdout();
let mut handle = stdout.lock();
match self.shell {
Shell::bash => bash::run(&mut handle, self),
Shell::fish => fish::run(&mut handle, self),
Shell::posix => posix::run(&mut handle, self),
Shell::powershell => powershell::run(&mut handle, self),
Shell::zsh => zsh::run(&mut handle, self),
}
.context("could not initialize zoxide")
}
}
arg_enum! {
#[allow(non_camel_case_types)]
#[derive(Debug)]
enum Shell {
bash,
fish,
posix,
powershell,
zsh,
}
}
arg_enum! {
#[allow(non_camel_case_types)]
#[derive(Debug)]
enum Hook {
none,
prompt,
pwd,
}
}

View File

@ -1,232 +0,0 @@
use super::{Hook, Init};
use crate::config;
use crate::util;
use std::io::Write;
use anyhow::Result;
use uuid::Uuid;
pub fn run<W: Write>(writer: &mut W, options: &Init) -> Result<()> {
const NOT_CONFIGURED: &str = "\
# -- not configured --";
let __zoxide_pwd = if config::zo_resolve_symlinks() {
"\
__zoxide_pwd() {
pwd -P
}"
} else {
"\
__zoxide_pwd() {
pwd -L
}"
};
let __zoxide_cd = if config::zo_echo() {
"\
__zoxide_cd() {
cd \"$@\" || return \"$?\"
__zoxide_pwd
}"
} else {
"\
__zoxide_cd() {
cd \"$@\" || return \"$?\"
}"
};
let __zoxide_hook = match options.hook {
Hook::none => NOT_CONFIGURED.into(),
Hook::prompt => "\
__zoxide_hook() {
zoxide add \"$(__zoxide_pwd)\"
}"
.into(),
Hook::pwd => {
let mut tmp_path = std::env::temp_dir();
tmp_path.push("zoxide");
let tmp_path_str = util::path_to_str(&tmp_path)?;
let pwd_path = tmp_path.join(format!("pwd-{}", Uuid::new_v4()));
let pwd_path_str = util::path_to_str(&pwd_path)?;
format!(
"\
# PWD hooks in POSIX use a temporary file, located at `$__zoxide_pwd_path`, to track
# changes in the current directory. These files are removed upon restart,
# but they should ideally also be cleaned up once the shell exits using traps.
#
# This can be done as follows:
#
# trap '__zoxide_cleanup' EXIT HUP KILL TERM
# trap '__zoxide_cleanup; trap - INT; kill -s INT \"$$\"' INT
# trap '__zoxide_cleanup; trap - QUIT; kill -s QUIT \"$$\"' QUIT
#
# By default, traps are not set up because they override all previous traps.
# It is therefore up to the user to add traps to their shell configuration.
__zoxide_tmp_path={tmp_path}
__zoxide_pwd_path={pwd_path}
__zoxide_cleanup() {{
rm -f \"$__zoxide_pwd_path\"
}}
__zoxide_setpwd() {{
mkdir -p \"$__zoxide_tmp_path\"
echo \"$PWD\" > \"$__zoxide_pwd_path\"
}}
__zoxide_setpwd
__zoxide_hook() {{
_ZO_OLDPWD=\"$(cat \"$__zoxide_pwd_path\")\"
if [ -z \"$_ZO_OLDPWD\" ] || [ \"$_ZO_OLDPWD\" != \"$PWD\" ]; then
__zoxide_setpwd && zoxide add \"$(pwd -L)\" > /dev/null
fi
}}",
tmp_path = posix_quote(tmp_path_str),
pwd_path = posix_quote(pwd_path_str),
)
}
};
let hook_init = match options.hook {
Hook::none => NOT_CONFIGURED,
_ => {
"\
case \"$PS1\" in
*\\$\\(__zoxide_hook\\)*) ;;
*) PS1=\"${PS1}\\$(__zoxide_hook)\" ;;
esac"
}
};
let aliases = if options.no_aliases {
NOT_CONFIGURED.into()
} else {
format!(
"\
alias {cmd}='__zoxide_z'
alias {cmd}i='__zoxide_zi'
alias {cmd}a='__zoxide_za'
alias {cmd}q='__zoxide_zq'
alias {cmd}qi='__zoxide_zqi'
alias {cmd}r='__zoxide_zr'
alias {cmd}ri='__zoxide_zri'",
cmd = options.cmd
)
};
writeln!(
writer,
"\
# =============================================================================
#
# Utility functions for zoxide.
#
# pwd based on the value of _ZO_RESOLVE_SYMLINKS.
{__zoxide_pwd}
# cd + custom logic based on the value of _ZO_ECHO.
{__zoxide_cd}
# =============================================================================
#
# Hook configuration for zoxide.
#
# Hook to add new entries to the database.
{__zoxide_hook}
# Initialize hook.
{hook_init}
# =============================================================================
#
# When using zoxide with --no-aliases, alias these internal functions as
# desired.
#
# Jump to a directory using only keywords.
__zoxide_z() {{
if [ \"$#\" -eq 0 ]; then
__zoxide_cd ~
elif [ \"$#\" -eq 1 ] && [ \"$1\" = '-' ]; then
if [ -n \"$OLDPWD\" ]; then
__zoxide_cd \"$OLDPWD\"
else
echo \"zoxide: \\$OLDPWD is not set\"
return 1
fi
else
__zoxide_result=\"$(zoxide query -- \"$@\")\" && __zoxide_cd \"$__zoxide_result\"
fi
}}
# Jump to a directory using interactive search.
__zoxide_zi() {{
__zoxide_result=\"$(zoxide query -i -- \"$@\")\" && __zoxide_cd \"$__zoxide_result\"
}}
# Add a new entry to the database.
alias __zoxide_za='zoxide add'
# Query an entry from the database using only keywords.
alias __zoxide_zq='zoxide query'
# Query an entry from the database using interactive selection.
alias __zoxide_zqi='zoxide query -i'
# Remove an entry from the database using the exact path.
alias __zoxide_zr='zoxide remove'
# Remove an entry from the database using interactive selection.
__zoxide_zri() {{
__zoxide_result=\"$(zoxide query -i -- \"$@\")\" && zoxide remove \"$__zoxide_result\"
}}
# =============================================================================
#
# Convenient aliases for zoxide. Disable these using --no-aliases.
#
{aliases}
# =============================================================================
#
# To initialize zoxide with your POSIX shell, add the following line to your
# shell configuration file:
#
# eval \"$(zoxide init posix --prompt hook)\"
",
__zoxide_pwd = __zoxide_pwd,
__zoxide_cd = __zoxide_cd,
__zoxide_hook = __zoxide_hook,
hook_init = hook_init,
aliases = aliases,
)?;
Ok(())
}
fn posix_quote(string: &str) -> String {
let mut quoted = String::with_capacity(string.len() + 2);
quoted.push('\'');
for ch in string.chars() {
match ch {
'\\' => quoted.push_str(r"\\"),
'\'' => quoted.push_str(r"'\''"),
_ => quoted.push(ch),
}
}
quoted.push('\'');
quoted
}

View File

@ -1,180 +0,0 @@
use anyhow::Result;
use std::io::Write;
use super::{Hook, Init};
use crate::config;
pub fn run<W: Write>(writer: &mut W, options: &Init) -> Result<()> {
const NOT_CONFIGURED: &str = "\
# -- not configured --";
let __zoxide_pwd = "\
function __zoxide_pwd {
$(Get-Location).Path
}";
let __zoxide_cd = if config::zo_echo() {
"\
function __zoxide_cd($dir) {
Set-Location $dir -ea Stop
__zoxide_pwd
}"
} else {
"\
function __zoxide_cd($dir) {
Set-Location $dir -ea Stop
}"
};
let __zoxide_hook = "\
function __zoxide_hook {
zoxide add $(__zoxide_pwd)
}";
let hook_init = match options.hook {
Hook::none => NOT_CONFIGURED,
Hook::prompt => {
"\
$PreZoxidePrompt = $function:prompt
function prompt {
$null = __zoxide_hook
& $PreZoxidePrompt
}"
}
Hook::pwd => {
"\
if ($PSVersionTable.PSVersion.Major -ge 6) {
$ExecutionContext.InvokeCommand.LocationChangedAction = {
$null = __zoxide_hook
}
} else {
Write-Error \"zoxide: PWD hooks are not supported below PowerShell 6, use 'zoxide init powershell --hook prompt' instead.\"
}"
}
};
let aliases = if options.no_aliases {
NOT_CONFIGURED.into()
} else {
format!(
"\
Set-Alias {cmd} __zoxide_z
Set-Alias {cmd}i __zoxide_zi
Set-Alias {cmd}a __zoxide_za
Set-Alias {cmd}q __zoxide_zq
Set-Alias {cmd}qi __zoxide_zqi
Set-Alias {cmd}r __zoxide_zr
Set-Alias {cmd}ri __zoxide_zri",
cmd = options.cmd
)
};
writeln!(
writer,
"\
# =============================================================================
#
# Utility functions for zoxide.
#
# pwd based on the value of _ZO_RESOLVE_SYMLINKS.
{__zoxide_pwd}
# cd + custom logic based on the value of _ZO_ECHO.
{__zoxide_cd}
# =============================================================================
#
# Hook configuration for zoxide.
#
# Hook to add new entries to the database.
{__zoxide_hook}
# Initialize hook.
{hook_init}
# =============================================================================
#
# When using zoxide with --no-aliases, alias these internal functions as
# desired.
#
# Jump to a directory using only keywords.
function __zoxide_z {{
if ($args.Length -eq 0) {{
__zoxide_cd ~
}}
elseif ($args.Length -eq 1 -and $args[0] -eq '-') {{
__zoxide_cd -
}}
else {{
$__zoxide_result = zoxide query -- @args
if ($LASTEXITCODE -eq 0) {{
__zoxide_cd $__zoxide_result
}}
}}
}}
# Jump to a directory using interactive search.
function zi {{
$__zoxide_result = zoxide query -i -- @args
if ($LASTEXITCODE -eq 0) {{
__zoxide_cd $__zoxide_result
}}
}}
# Add a new entry to the database.
function __zoxide_za {{ zoxide add @args }}
# Query an entry from the database using only keywords.
function __zoxide_zq {{ zoxide query @args }}
# Query an entry from the database using interactive selection.
function __zoxide_zqi {{ zoxide query -i @args }}
# Remove an entry from the database using the exact path.
function __zoxide_zr {{ zoxide remove @args }}
# Remove an entry from the database using interactive selection.
function __zoxide_zri {{
$_zoxide_result = zoxide query -i -- @args
if ($LASTEXITCODE -eq 0) {{
zoxide remove $_zoxide_result
}}
}}
# =============================================================================
#
# Convenient aliases for zoxide. Disable these using --no-aliases.
#
{aliases}
# =============================================================================
#
# To initialize zoxide with PowerShell, add the following line to your
# PowerShell configuration file (the location is stored in $profile):
#
# Invoke-Expression (& {{
# $hook = if ($PSVersionTable.PSVersion.Major -ge 6) {{
# 'pwd'
# }} else {{
# 'prompt'
# }}
# (zoxide init powershell --hook $hook) -join \"`n\"
# }})
",
__zoxide_pwd = __zoxide_pwd,
__zoxide_cd = __zoxide_cd,
__zoxide_hook = __zoxide_hook,
hook_init = hook_init,
aliases = aliases,
)?;
Ok(())
}

View File

@ -1,174 +0,0 @@
use anyhow::Result;
use std::io::Write;
use super::{Hook, Init};
use crate::config;
pub fn run<W: Write>(writer: &mut W, options: &Init) -> Result<()> {
const NOT_CONFIGURED: &str = "\
# -- not configured --";
let __zoxide_pwd = if config::zo_resolve_symlinks() {
"\
__zoxide_pwd() {
pwd -P
}"
} else {
"\
__zoxide_pwd() {
pwd -L
}"
};
let __zoxide_cd = if config::zo_echo() {
"\
__zoxide_cd() {
cd \"$@\" || return \"$?\"
__zoxide_pwd
}"
} else {
"\
__zoxide_cd() {
cd \"$@\" || return \"$?\"
}"
};
let __zoxide_hook = match options.hook {
Hook::none => NOT_CONFIGURED,
_ => {
"\
__zoxide_hook() {
zoxide add \"$(__zoxide_pwd)\"
}"
}
};
let hook_init = match options.hook {
Hook::none => NOT_CONFIGURED,
Hook::prompt => {
"\
[[ -n \"${precmd_functions[(r)__zoxide_hook]}\" ]] || {
precmd_functions+=(__zoxide_hook)
}"
}
Hook::pwd => {
"\
chpwd_functions=(${chpwd_functions[@]} \"__zoxide_hook\")"
}
};
let aliases = if options.no_aliases {
NOT_CONFIGURED.into()
} else {
format!(
"\
alias {cmd}='__zoxide_z'
alias {cmd}i='__zoxide_zi'
alias {cmd}a='__zoxide_za'
alias {cmd}q='__zoxide_zq'
alias {cmd}qi='__zoxide_zqi'
alias {cmd}r='__zoxide_zr'
alias {cmd}ri='__zoxide_zri'",
cmd = options.cmd
)
};
write!(
writer,
"\
# =============================================================================
#
# Utility functions for zoxide.
#
# pwd based on the value of _ZO_RESOLVE_SYMLINKS.
{__zoxide_pwd}
# cd + custom logic based on the value of _ZO_ECHO.
{__zoxide_cd}
# =============================================================================
#
# Hook configuration for zoxide.
#
# Hook to add new entries to the database.
{__zoxide_hook}
# Initialize hook.
{hook_init}
# =============================================================================
#
# When using zoxide with --no-aliases, alias these internal functions as
# desired.
#
# Jump to a directory using only keywords.
__zoxide_z() {{
if [ \"$#\" -eq 0 ]; then
__zoxide_cd ~
elif [ \"$#\" -eq 1 ] && [ \"$1\" = '-' ]; then
if [ -n \"$OLDPWD\" ]; then
__zoxide_cd \"$OLDPWD\"
else
echo \"zoxide: \\$OLDPWD is not set\"
return 1
fi
else
local __zoxide_result
__zoxide_result=\"$(zoxide query -- \"$@\")\" && __zoxide_cd \"$__zoxide_result\"
fi
}}
# Jump to a directory using interactive search.
__zoxide_zi() {{
local __zoxide_result
__zoxide_result=\"$(zoxide query -i -- \"$@\")\" && __zoxide_cd \"$__zoxide_result\"
}}
# Add a new entry to the database.
alias __zoxide_za='zoxide add'
# Query an entry from the database using only keywords.
alias __zoxide_zq='zoxide query'
# Query an entry from the database using interactive selection.
alias __zoxide_zqi='zoxide query -i'
# Remove an entry from the database using the exact path.
alias __zoxide_zr='zoxide remove'
# Remove an entry from the database using interactive selection.
__zoxide_zri() {{
local __zoxide_result
__zoxide_result=\"$(zoxide query -i -- \"$@\")\" && zoxide remove \"$__zoxide_result\"
}}
# =============================================================================
#
# Convenient aliases for zoxide. Disable these using --no-aliases.
#
{aliases}
# =============================================================================
#
# To initialize zoxide with zsh, add the following line to your zsh
# configuration file (usually ~/.zshrc):
#
# eval \"$(zoxide init zsh)\"
",
__zoxide_pwd = __zoxide_pwd,
__zoxide_cd = __zoxide_cd,
__zoxide_hook = __zoxide_hook,
hook_init = hook_init,
aliases = aliases,
)?;
Ok(())
}

View File

@ -1,11 +0,0 @@
mod add;
mod import;
mod init;
mod query;
mod remove;
pub use add::Add;
pub use import::Import;
pub use init::Init;
pub use query::Query;
pub use remove::Remove;

View File

@ -1,127 +0,0 @@
use crate::db::Dir;
use crate::fzf::Fzf;
use crate::util;
use anyhow::{bail, Context, Result};
use structopt::StructOpt;
use std::io::{self, Write};
use std::path::Path;
/// Search for a directory
#[derive(Debug, StructOpt)]
#[structopt()]
pub struct Query {
keywords: Vec<String>,
/// Opens an interactive selection menu using fzf
#[structopt(short, long, conflicts_with = "list")]
interactive: bool,
/// List all matching directories
#[structopt(short, long, conflicts_with = "interactive")]
list: bool,
/// Display score along with result
#[structopt(short, long)]
score: bool,
}
impl Query {
pub fn run(&self) -> Result<()> {
if self.list {
return self.query_list();
}
if self.interactive {
return self.query_interactive();
}
// if the input is already a valid path, simply print it as-is
if let [path] = self.keywords.as_slice() {
if Path::new(path).is_dir() {
let dir = Dir {
path: path.to_string(),
rank: 0.0,
last_accessed: 0,
};
if self.score {
println!("{}", dir.display_score(0))
} else {
println!("{}", dir.display());
}
return Ok(());
}
}
self.query()
}
fn query(&self) -> Result<()> {
let mut db = util::get_db()?;
let now = util::get_current_time()?;
let mut matches = db.matches(now, &self.keywords);
match matches.next() {
Some(dir) => {
if self.score {
println!("{}", dir.display_score(now))
} else {
println!("{}", dir.display());
}
}
None => bail!("no match found"),
}
Ok(())
}
fn query_interactive(&self) -> Result<()> {
let mut db = util::get_db()?;
let now = util::get_current_time()?;
let mut fzf = Fzf::new()?;
for dir in db.matches(now, &self.keywords) {
fzf.write(format!("{}", dir.display_score(now)))?;
}
match fzf.wait_select()? {
Some(selection) => {
if self.score {
print!("{}", selection)
} else {
let selection = selection
.get(5..)
.with_context(|| format!("fzf returned invalid output: {}", selection))?;
print!("{}", selection)
}
}
None => bail!("no match found"),
};
Ok(())
}
fn query_list(&self) -> Result<()> {
let mut db = util::get_db()?;
let now = util::get_current_time()?;
let stdout = io::stdout();
let mut handle = stdout.lock();
for dir in db.matches(now, &self.keywords) {
if self.score {
writeln!(handle, "{}", dir.display_score(now))
} else {
writeln!(handle, "{}", dir.display())
}
.unwrap();
}
Ok(())
}
}

View File

@ -1,38 +0,0 @@
use crate::util;
use anyhow::{bail, Result};
use structopt::StructOpt;
/// Remove a directory
#[derive(Debug, StructOpt)]
#[structopt()]
pub struct Remove {
path: String,
}
impl Remove {
pub fn run(&self) -> Result<()> {
remove(&self.path)
}
}
fn remove(path: &str) -> Result<()> {
let mut db = util::get_db()?;
if let Some(idx) = db.dirs.iter().position(|dir| dir.path == path) {
db.dirs.swap_remove(idx);
db.modified = true;
return Ok(());
}
let path = util::resolve_path(&path)?;
let path = util::path_to_str(&path)?;
if let Some(idx) = db.dirs.iter().position(|dir| dir.path == path) {
db.dirs.swap_remove(idx);
db.modified = true;
return Ok(());
}
bail!("could not find path in database: {}", path)
}

View File

@ -1,17 +1,21 @@
use crate::config;
use crate::db::{Db, Epoch};
use zoxide_engine::Epoch;
use anyhow::{bail, Context, Result};
use std::env;
use std::path::{Component, Path, PathBuf};
use std::time::SystemTime;
pub fn get_db() -> Result<Db> {
let data_dir = config::zo_data_dir()?;
Db::open(data_dir)
pub fn canonicalize<P: AsRef<Path>>(path: &P) -> Result<PathBuf> {
dunce::canonicalize(path)
.with_context(|| format!("could not resolve path: {}", path.as_ref().display()))
}
pub fn get_current_time() -> Result<Epoch> {
pub fn current_dir() -> Result<PathBuf> {
env::current_dir().context("could not get current directory")
}
pub fn current_time() -> Result<Epoch> {
let current_time = SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.context("system clock set to invalid time")?
@ -20,9 +24,10 @@ pub fn get_current_time() -> Result<Epoch> {
Ok(current_time as _)
}
pub fn canonicalize<P: AsRef<Path>>(path: &P) -> Result<PathBuf> {
pub fn path_to_str<P: AsRef<Path>>(path: &P) -> Result<&str> {
let path = path.as_ref();
dunce::canonicalize(path).with_context(|| format!("could not resolve path: {}", path.display()))
path.to_str()
.with_context(|| format!("invalid UTF-8 in path: {}", path.display()))
}
/// Resolves the absolute version of a path.
@ -31,7 +36,7 @@ pub fn canonicalize<P: AsRef<Path>>(path: &P) -> Result<PathBuf> {
/// character.
/// If path is relative, use the current directory to build the absolute path.
#[cfg(any(unix, windows))]
pub fn resolve_path<P: AsRef<Path>>(path: P) -> Result<PathBuf> {
pub fn resolve_path<P: AsRef<Path>>(path: &P) -> Result<PathBuf> {
let path = path.as_ref();
let base_path;
@ -46,7 +51,7 @@ pub fn resolve_path<P: AsRef<Path>>(path: P) -> Result<PathBuf> {
stack.push(root);
}
_ => {
base_path = get_current_dir()?;
base_path = current_dir()?;
stack.extend(base_path.components());
}
}
@ -73,7 +78,7 @@ pub fn resolve_path<P: AsRef<Path>>(path: P) -> Result<PathBuf> {
}
fn get_drive_relative(drive_letter: u8) -> Result<PathBuf> {
let path = get_current_dir()?;
let path = current_dir()?;
if Some(drive_letter) == get_drive_letter(&path) {
return Ok(path);
}
@ -124,7 +129,7 @@ pub fn resolve_path<P: AsRef<Path>>(path: P) -> Result<PathBuf> {
stack.extend(base_path.components());
}
_ => {
base_path = get_current_dir()?;
base_path = current_dir()?;
stack.extend(base_path.components());
}
}
@ -149,13 +154,3 @@ pub fn resolve_path<P: AsRef<Path>>(path: P) -> Result<PathBuf> {
}
Ok(result)
}
pub fn get_current_dir() -> Result<PathBuf> {
env::current_dir().context("could not get current path")
}
pub fn path_to_str<P: AsRef<Path>>(path: &P) -> Result<&str> {
let path = path.as_ref();
path.to_str()
.with_context(|| format!("invalid utf-8 sequence in path: {}", path.display()))
}