Add option for searching online repositories (#38)

This commit is contained in:
Denis Isidoro 2019-09-22 11:52:10 -03:00 committed by GitHub
parent f853181f83
commit 77acda383d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 261 additions and 80 deletions

146
README.md
View file

@ -6,37 +6,91 @@ An interactive cheatsheet tool for the command-line so that you'll never say the
*Oh, it's not in my bash history*<br> *Oh, it's not in my bash history*<br>
— *Geez, it's almost what I wanted but I need to change some args* — *Geez, it's almost what I wanted but I need to change some args*
![Demo](https://user-images.githubusercontent.com/3226564/65380182-69431380-dcac-11e9-9af8-0f7b3c869d0f.gif) ![Demo](https://user-images.githubusercontent.com/3226564/65389667-0181dc80-dd2f-11e9-9fac-c875ed7c7b53.gif)
**navi** allows you to browse through cheatsheets (that you may write yourself or download from maintainers) and execute commands, prompting for argument values. **navi** allows you to browse through cheatsheets (that you may write yourself or download from maintainers) and execute commands, prompting for argument values.
## Installation Table of Contents
-----------------
**Using [brew](https://brew.sh/):** * [Installation](#installation)
```sh * [Using Homebrew or Linuxbrew](#using-homebrew-or-linuxbrew)
brew install denisidoro/tools/navi * [Using git](#using-git)
``` * [Upgrading](#upgrading)
* [Usage](#usage)
* [Preventing execution](#preventing-execution)
* [Pre-filtering](#pre-filtering)
* [Searching online repositories](#searching-online-repositories)
* [More options](#more-options)
* [Trying out online](#trying-out-online)
* [Motivation](#motivation)
* [Cheatsheets](#cheatsheets)
* [Using your own custom cheatsheets](#using-your-own-custom-cheatsheets)
* [Submitting cheatsheets](#submitting-cheatsheets)
* [Cheatsheet syntax](#cheatsheet-syntax)
* [Syntax overview](#syntax-overview)
* [Variables](#variables)
* [FZF options](#fzf-options)
* [Related projects](#related-projects)
* [Etymology](#etymology)
Installation
------------
### Using Homebrew or Linuxbrew
You can use [Homebrew](http://brew.sh/) or [Linuxbrew](http://linuxbrew.sh/)
to install **navi**.
### Using git
Alternatively, you can `git clone` this repository and run `make`:
**Without brew:**
```sh ```sh
git clone http://github.com/denisidoro/navi /opt/navi git clone --depth 1 http://github.com/denisidoro/navi /opt/navi
cd /opt/navi cd /opt/navi
sudo make install sudo make install
# install fzf: https://github.com/junegunn/fzf # install fzf: https://github.com/junegunn/fzf
``` ```
## Usage Upgrading
---------
Simply call: **navi** is being actively developed and you might want to upgrade it once in a while. Please follow the instruction below depending on the installation method used:
```sh
navi
```
## Trying it out online - brew: `brew update; brew reinstall navi`
- git: `cd /opt/navi && sudo make update`
Head to [this playground](https://www.katacoda.com/denisidoro/scenarios/navi) for previewing **navi**. Usage
-----
## Motivation By simply running `navi` you will be prompted with the default cheatsheets.
### Preventing execution
If you run `navi --print`, the selected command won't be executed. It will be printed to stdout instead.
### Pre-filtering
If you run `navi query <cmd>`, the results will be pre-filtered.
### Searching online repositories
If you run `navi search <cmd>`, **navi** will try to download cheatsheets from online repositories as well.
Please note that these cheatsheets aren't curated by **navi**'s maintainers and should be taken with a grain of salt. If you're not sure about executing these commands, make sure to check the preview window or use the `--print` option.
### More options
Please refer to `navi --help` for more details.
Trying out online
--------------------
If you don't have access to bash at the moment and you want to live preview **navi**, head to [this playground](https://www.katacoda.com/denisidoro/scenarios/navi). It'll start a docker container with instructions for you to install and use the tool. Note: login required.
Motivation
----------
The main objectives are: The main objectives are:
- to increase discoverability, by finding commands given keywords or descriptions; - to increase discoverability, by finding commands given keywords or descriptions;
@ -50,23 +104,31 @@ Or you can launch a browser and search for instructions on Google, but that take
**navi**, on the other hand, intends to be a general purpose platform for bookmarking any command at a very low cost. **navi**, on the other hand, intends to be a general purpose platform for bookmarking any command at a very low cost.
## Using your own cheatsheets Cheatsheets
-----------
In this case, you need to pass the directory which contains `.cheat` files as in: ### Using your own custom cheatsheets
In this case, you need to pass a `:`-separated list of separated directories which contain `.cheat`:
```sh ```sh
navi --dir "/folder/with/cheats" navi --path "/folder/with/cheats"
``` ```
Alternatively, you can set an environment variable in your `.bashrc`-like file: Alternatively, you can set an environment variable in your `.bashrc`-like file:
```sh ```sh
export NAVI_DIR="/folder/with/cheats" export NAVI_PATH="/folder/with/cheats:/another/folder"
``` ```
## Submitting cheatsheets ### Submitting cheatsheets
Feel free to fork this project and open a PR for me to include your contributions. Feel free to fork this project and open a PR for me to include your contributions.
## .cheat syntax Cheatsheet syntax
-----------------
Cheatsheets are describe in `.cheat` files.
### Syntax overview
- lines starting with `%` should contain tags which will be added to any command in a given file; - 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 be descriptions of commands;
@ -83,16 +145,40 @@ git checkout <branch>
$ branch: git branch | awk '{print $NF}' $ branch: git branch | awk '{print $NF}'
``` ```
For advanced usage, please refer to the files in [/cheats](https://github.com/denisidoro/navi/tree/master/cheats). ### Variables
## Alternatives The interface prompts for variable names inside brackets (eg `<branch>`).
- [cmdmenu](https://github.com/amacfie/cmdmenu); The command for generating possible inputs can refer other variables:
- [denisidoro/beavr](https://github.com/denisidoro/beavr); ```sh
- [how2](https://github.com/santinic/how2); # If you select 2 for x, the possible values of y will be 12 and 22
- [howdoi](https://github.com/gleitz/howdoi); echo <x> <y>
- [cheat](https://github.com/cheat/cheat).
## Etymology $ x: echo -e '1\n2\n3' | tr '\n' ' '
$ y: echo "$((x+10))" "$((x+20))" | tr '\n' ' '
```
### FZF options
You can make pick a specific column of a selection and set the number of lines considered as headers:
```sh
# This will pick the 3rd column and use the first line as header
docker rmi <image_id>
$ image_id: docker images --- --headers 1 --column 3
```
Related projects
----------------
There are many similar projects out there ([bro](https://github.com/hubsmoke/bro), [eg](https://github.com/srsudar/eg), [cheat.sh](https://github.com/chubin/cheat.sh), [cmdmenu](https://github.com/amacfie/cmdmenu), [cheat](https://github.com/cheat/cheat), [beavr](https://github.com/denisidoro/beavr), [how2](https://github.com/santinic/how2) and [howdoi](https://github.com/gleitz/howdoi), to name a few).
Most of them provide excellent cheatsheet repositories, but lack a nice UI.
In any case, **navi** has the option to [search for some of these repositories](#installation).
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. 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.

View file

@ -1,4 +1,4 @@
% awk, text processing, string % awk, string
# Print nth column # Print last column
awk '{print $<n>} echo '1 2 3' | awk '{print $NF}'

View file

@ -10,4 +10,4 @@ netstat -tn 2>/dev/null | grep :<port> | awk '{print $5}' | cut -d: -f1 | sort |
dig +short myip.opendns.com @resolver1.opendns.com dig +short myip.opendns.com @resolver1.opendns.com
# Find primary, local IP address # Find primary, local IP address
ifconfig | grep -Eo 'inet (addr:)?([0-9]*\.){3}[0-9]*' | grep -Eo '([0-9]*\.){3}[0-9]*' | grep -v '127.0.0.1' ifconfig | grep -Eo 'inet (addr:)?([0-9]*\.){3}[0-9]*' | grep -Eo '([0-9]*\.){3}[0-9]*' | grep -v '127.0.0.1' | tail -n1

34
navi
View file

@ -5,17 +5,37 @@ export SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
source "${SCRIPT_DIR}/src/main.sh" source "${SCRIPT_DIR}/src/main.sh"
##? Command cheatsheet tool ##? An interactive cheatsheet tool for the command-line
##? ##?
##? Usage: ##? Usage:
##? cheats [options] ##? cheats [command] [<args>...] [options]
##?
##? Commands:
##? search <cmd> Search for cheatsheets on online repositories
##? query <cmd> Pre-filter results
##? ##?
##? Options: ##? Options:
##? --print Prevent script execution [default: false] ##? --print Prevent script execution
##? --no-interpolation Prevent argument interpolation [default: false] ##? --path List of paths to look for cheats
##? --no-preview Hide command preview window [default: false] ##? --no-interpolation Prevent argument interpolation
##? --no-preview Hide command preview window
##?
##? Examples:
##? cheats
##? cheats --path '/some/folder:/another/folder'
##? cheats search awk
##? cheats search docker --print
##? cheats query git
##?
##? More info:
##? search
##? Queries cheatsheets from http://cheat.sh
##? Please note that these cheatsheets may not work out of the box
##? Always check the preview window before executing commands!
##? full docs
##? Please refer to the README at https://github.com/denisidoro/navi
VERSION="0.7.0" VERSION="0.8.0"
docs::eval "$@" opts::eval "$@"
main "$@" main "$@"

View file

@ -1,7 +1,9 @@
#!/usr/bin/env bash #!/usr/bin/env bash
cheat::find() { cheat::find() {
find "${NAVI_DIR:-"${SCRIPT_DIR}/cheats"}" -iname '*.cheat' for path in $(echo "$NAVI_PATH" | tr ':' '\n'); do
find "$path" -iname '*.cheat'
done
} }
cheat::read_many() { cheat::read_many() {

View file

@ -1,33 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
docs::extract_help() {
local readonly file="$1"
grep "^##?" "$file" | cut -c 5-
}
docs::eval() {
local wait_for=""
entry_point="main"
print=false
interpolation=true
preview=true
for arg in $@; do
case $wait_for in
dir) NAVI_DIR="$arg"; wait_for="" ;;
command-for) query="$(echo "$arg" | tr "^" " ")"; entry_point="preview"; break ;;
esac
case $arg in
--print) print=true ;;
--no-interpolation) interpolation=false ;;
--version) echo "${VERSION:-unknown}" && exit 0 ;;
--help) docs::extract_help "$0" && exit 0 ;;
--command-for) wait_for="command-for" ;;
--no-preview) preview=false ;;
-d|--dir) wait_for="dir" ;;
esac
done
}

View file

@ -2,9 +2,10 @@
source "${SCRIPT_DIR}/src/arg.sh" source "${SCRIPT_DIR}/src/arg.sh"
source "${SCRIPT_DIR}/src/cheat.sh" source "${SCRIPT_DIR}/src/cheat.sh"
source "${SCRIPT_DIR}/src/docs.sh" source "${SCRIPT_DIR}/src/health.sh"
source "${SCRIPT_DIR}/src/healthcheck.sh"
source "${SCRIPT_DIR}/src/misc.sh" source "${SCRIPT_DIR}/src/misc.sh"
source "${SCRIPT_DIR}/src/opts.sh"
source "${SCRIPT_DIR}/src/search.sh"
source "${SCRIPT_DIR}/src/selection.sh" source "${SCRIPT_DIR}/src/selection.sh"
source "${SCRIPT_DIR}/src/str.sh" source "${SCRIPT_DIR}/src/str.sh"
source "${SCRIPT_DIR}/src/ui.sh" source "${SCRIPT_DIR}/src/ui.sh"
@ -51,7 +52,18 @@ handler::preview() {
main() { main() {
case ${entry_point:-} in case ${entry_point:-} in
preview) handler::preview "$@" 2>/dev/null || echo "Unable to find command for '${query:-}'" ;; preview)
*) health::fzf && handler::main "$@" ;; handler::preview "$@" \
|| echo "Unable to find command for '${query:-}'"
;;
search)
health::fzf
search::save "$query" || true
handler::main "$@"
;;
*)
health::fzf
handler::main "$@"
;;
esac esac
} }

44
src/opts.sh Normal file
View file

@ -0,0 +1,44 @@
#!/usr/bin/env bash
set -euo pipefail
opts::extract_help() {
local readonly file="$1"
grep "^##?" "$file" | cut -c 5-
}
opts::eval() {
local readonly wait_for=""
entry_point="main"
print=false
interpolation=true
preview=true
for arg in "$@"; do
case $wait_for in
path) NAVI_PATH="$arg"; wait_for="" ;;
preview) query="$(echo "$arg" | tr "^" " ")"; wait_for=""; break ;;
search) query="$arg"; wait_for=""; export NAVI_PATH="${NAVI_PATH}:$(search::full_path "$query")"; ;;
query) query="$arg"; wait_for="" ;;
esac
case $arg in
--print) print=true ;;
--no-interpolation) interpolation=false ;;
--version) echo "${VERSION:-unknown}" && exit 0 ;;
help|--help) opts::extract_help "$0" && exit 0 ;;
--command-for) wait_for="command-for" ;;
--no-preview) preview=false ;;
--path) wait_for="path" ;;
search) entry_point="search"; wait_for="search";;
preview) entry_point="preview"; wait_for="preview";;
q|query) wait_for="query";;
esac
done
}
opts::fallback_path() {
echo "${NAVI_DIR:-${SCRIPT_DIR}/cheats}"
}
export NAVI_PATH="${NAVI_PATH:-$(opts::fallback_path)}"

39
src/search.sh Normal file
View file

@ -0,0 +1,39 @@
#!/usr/bin/env bash
search::cheat() {
local readonly cmd="$(echo "$1" | str::first_word)"
echo "% ${cmd}, cheatsh"
echo
curl -s "${CHTSH_URL:-http://cht.sh}/${cmd}?T"
}
search::filename() {
local readonly cmd="$(echo "$1" | str::first_word)"
echo "${cmd}_cheatsh" \
| head -n1 \
| awk '{print $NF}' \
| xargs \
| tr ' ' '_'
}
search::full_path() {
local readonly cmd="$(echo "$1" | str::first_word)"
echo "/tmp/navi/$(search::filename "$cmd").cheat"
}
search::save() {
local readonly cmd="$(echo "$1" | str::first_word)"
local readonly filepath="$(search::full_path "$cmd")"
local readonly filedir="$(dirname "$filepath")"
if [ -f "$filepath" ]; then
return
fi
mkdir -p "$filedir" &> /dev/null || true
search::cheat "$cmd" > "$filepath"
}

View file

@ -25,3 +25,7 @@ str::last_paragraph_line() {
awk '(!NF) { exit } { print $0 }' \ awk '(!NF) { exit } { print $0 }' \
| tail -n1 | tail -n1
} }
str::first_word() {
awk '{print $1}'
}

View file

@ -7,7 +7,7 @@ ui::pick() {
ui::select() { ui::select() {
local readonly cheats="$1" local readonly cheats="$1"
local readonly script_path="$(which navi | head -n1 || echo "${SCRIPT_DIR}/navi")" local readonly script_path="$(which navi | head -n1 || echo "${SCRIPT_DIR}/navi")"
local readonly preview_cmd="echo {} | tr ' ' '^' | xargs -I% \"${script_path}\" --command-for %" local readonly preview_cmd="echo \"{}\" | tr ' ' '^' | xargs -I% \"${script_path}\" preview %"
local args=() local args=()
args+=("-i") args+=("-i")
@ -16,6 +16,13 @@ ui::select() {
args+=("--preview"); args+=("$preview_cmd") args+=("--preview"); args+=("$preview_cmd")
args+=("--preview-window"); args+=("up:1") args+=("--preview-window"); args+=("up:1")
fi fi
if [ -n "${query:-}" ]; then
args+=("--query=${query} ")
fi
if [ "${entry_point:-}" = "search" ]; then
args+=("--header")
args+=("Displaying online results. Please refer to 'navi --help' for details")
fi
echo "$cheats" \ echo "$cheats" \
| cheat::read_many \ | cheat::read_many \