diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..c22e144 --- /dev/null +++ b/Makefile @@ -0,0 +1,8 @@ +BIN ?= navi +PREFIX ?= /usr/local + +install: + scripts/symlink + +uninstall: + rm -f $(PREFIX)/bin/$(BIN) diff --git a/README.md b/README.md new file mode 100644 index 0000000..0e7b160 --- /dev/null +++ b/README.md @@ -0,0 +1,72 @@ +# navi + +An interactive cheatsheet tool for the command-line so that you'll never say the following again: + +*- How to run that command again?* +*- Oh, it's not in my bash history* +*- Geez, it's almost what I wanted but I need to change some args* + +![Demo](https://user-images.githubusercontent.com/3226564/65281359-d158f480-db08-11e9-8e69-e380d76c343b.gif) + +**navi** allows you to browse through cheatsheets (that you may write yourself or downloaded from maintainers) and execute commands, prompting for argument values. + +## Installation + +**Using [brew](https://brew.sh/):** +``` +brew install denisidoro/tools/navi +``` + +**Without brew:** +``` +git clone http://github.com/denisidoro/navi /opt/navi +cd /opt/navi +sudo make install +``` + +## Usage + +Simply call `navi` + +## Motivation + +The main objectives are: +- to increase discoverability, by finding commands given keywords or descriptions; +- to prevent you from running auxiliar commands, copying the result into the clipboard and then pasting into the original command; +- to improve terminal usage as a whole. + +Sure, you can find autocompleters out there for all your favorite commands. However, they are very specific and each one may offer a different learning curve. + +Or you can launch and browser and search for instructions on Google, but that takes some time. + +**navi**, on the other hand, intends to be a general purpose platform for bookmarking any command at a very low cost. + +## .cheat syntax + +- lines starting with `%` should contain tags which will be added to any command in a given file; +- lines starting with `#` should be descriptions of commands; +- lines starting with `$` should contain commands that generate suggestion values for a given argument; +- all the other non-empty lines are considered as executable commands. + +For example, this is a valid `.cheat` file: +```sh +% git, code + +# Change branch +git checkout + +$ branch: git branch --format='%(refname:short)' +``` + +For advanced usage, please refer to the files in [/cheats](https://github.com/denisidoro/navi/tree/master/cheats). + +## Alternatives + +- [denisidoro/beavr](https://github.com/denisidoro/beavr); +- [how2](https://github.com/santinic/how2); +- [howdoi](https://github.com/gleitz/howdoi); +- [cheat](https://github.com/cheat/cheat). + +## Etymology + +In [The Legend of Zelda Ocarina of Time](https://zelda.gamepedia.com/Ocarina_of_Time), [navi](https://zelda.gamepedia.com/Navi) is a character that provides [Link](https://zelda.gamepedia.com/Link) with a variety of clues to help him solve puzzles and progress in his quest. diff --git a/cheats b/cheats deleted file mode 100755 index f3e7aa8..0000000 --- a/cheats +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -export DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null && pwd)" - -source "${DIR}/src/arg.sh" -source "${DIR}/src/cheat.sh" -source "${DIR}/src/docs.sh" -source "${DIR}/src/misc.sh" -source "${DIR}/src/selection.sh" -source "${DIR}/src/str.sh" -source "${DIR}/src/ui.sh" - -source "${DIR}/src/main.sh" - -##? Command cheatsheet tool -##? -##? Usage: -##? cheats [options] -##? -##? Options: -##? --print Prevent script execution [default: false] -##? --no-interpolation Prevent argument interpolation [default: false] -##? -c --cheat-folder Folder with cheatsheets - -docs::eval "$@" - -main "$@" diff --git a/sheets/awk.cheat b/cheats/awk.cheat similarity index 100% rename from sheets/awk.cheat rename to cheats/awk.cheat diff --git a/sheets/tar.cheat b/cheats/compression.cheat similarity index 100% rename from sheets/tar.cheat rename to cheats/compression.cheat diff --git a/sheets/crontab.cheat b/cheats/crontab.cheat similarity index 100% rename from sheets/crontab.cheat rename to cheats/crontab.cheat diff --git a/sheets/docker.cheat b/cheats/docker.cheat similarity index 100% rename from sheets/docker.cheat rename to cheats/docker.cheat diff --git a/sheets/git.cheat b/cheats/git.cheat similarity index 100% rename from sheets/git.cheat rename to cheats/git.cheat diff --git a/sheets/kubernetes.cheat b/cheats/kubernetes.cheat similarity index 100% rename from sheets/kubernetes.cheat rename to cheats/kubernetes.cheat diff --git a/sheets/mysql.cheat b/cheats/mysql.cheat similarity index 100% rename from sheets/mysql.cheat rename to cheats/mysql.cheat diff --git a/sheets/network.cheat b/cheats/network.cheat similarity index 100% rename from sheets/network.cheat rename to cheats/network.cheat diff --git a/navi b/navi new file mode 100755 index 0000000..427df05 --- /dev/null +++ b/navi @@ -0,0 +1,27 @@ +#!/usr/bin/env bash +set -euo pipefail + +export SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" + +source "${SCRIPT_DIR}/src/arg.sh" +source "${SCRIPT_DIR}/src/cheat.sh" +source "${SCRIPT_DIR}/src/docs.sh" +source "${SCRIPT_DIR}/src/misc.sh" +source "${SCRIPT_DIR}/src/selection.sh" +source "${SCRIPT_DIR}/src/str.sh" +source "${SCRIPT_DIR}/src/ui.sh" + +source "${SCRIPT_DIR}/src/main.sh" + +##? Command cheatsheet tool +##? +##? Usage: +##? cheats [options] +##? +##? Options: +##? --print Prevent script execution [default: false] +##? --no-interpolation Prevent argument interpolation [default: false] + +VERSION="0.3.0" +docs::eval "$@" +main "$@" diff --git a/scripts/symlink b/scripts/symlink new file mode 100755 index 0000000..2ab7ab9 --- /dev/null +++ b/scripts/symlink @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +set -euo pipefail + +export SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)" + +script() { + echo "#!/usr/bin/env bash" + echo "${SCRIPT_DIR}/navi" '$@' +} + +BIN="/usr/local/bin/navi" +script > "$BIN" +chmod +x "$BIN" \ No newline at end of file diff --git a/src/cheat.sh b/src/cheat.sh index 85b6d36..eadc795 100755 --- a/src/cheat.sh +++ b/src/cheat.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash cheat::find() { - find "${cheat_folder:-"${DIR}/sheets"}" -iname '*.cheat' + find "${cheat_dir:-"${SCRIPT_DIR}/cheats"}" -iname '*.cheat' } cheat::read_many() { diff --git a/src/docs.sh b/src/docs.sh index 73b8fe4..e178462 100644 --- a/src/docs.sh +++ b/src/docs.sh @@ -1,8 +1,28 @@ #!/usr/bin/env bash set -euo pipefail -docs::eval() { - print=false - no_interpolation=false - cheat_folder="" +docs::extract_help() { + local readonly file="$1" + grep "^##?" "$file" | cut -c 5- +} + +docs::eval() { + local wait_for="" + + print=false + interpolation=true + + for arg in $@; do + case $arg in + --print) print=true;; + --no-interpolation) interpolation=false;; + --version) echo "${VERSION:-unknown}" && exit 0;; + --help) docs::extract_help "$0" && exit 0;; + -d|--dir) wait_for="dir";; + esac + + case $wait_for in + dir) cheat_dir="$arg"; wait_for="";; + esac + done } diff --git a/src/main.sh b/src/main.sh index 4a2b1f6..b94f791 100644 --- a/src/main.sh +++ b/src/main.sh @@ -8,7 +8,7 @@ main() { local cmd="$(selection::command "$selection" "$cheat")" local arg value - if $no_interpolation; then + if ! $interpolation; then echo "$cmd" exit 0 fi