mirror of
https://github.com/denisidoro/navi
synced 2024-11-22 03:23:05 +00:00
Rust implementation (#197)
This commit is contained in:
parent
684702e770
commit
c5cd85bb41
75 changed files with 1550 additions and 1955 deletions
|
@ -1,26 +0,0 @@
|
|||
# Clojure CircleCI 2.0 configuration file
|
||||
#
|
||||
# Check https://circleci.com/docs/2.0/language-clojure/ for more details
|
||||
#
|
||||
version: 2
|
||||
|
||||
job_defaults: &defaults
|
||||
docker:
|
||||
- image: frolvlad/alpine-bash
|
||||
working_directory: ~/repo
|
||||
environment:
|
||||
ENVIRONMENT: "test"
|
||||
TERM: "dumb"
|
||||
|
||||
jobs:
|
||||
tests:
|
||||
<<: *defaults
|
||||
steps:
|
||||
- checkout
|
||||
- run: ./test/run
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
run-tests:
|
||||
jobs:
|
||||
- tests
|
42
.github/workflows/cd.yml
vendored
Normal file
42
.github/workflows/cd.yml
vendored
Normal file
|
@ -0,0 +1,42 @@
|
|||
name: Publish
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- '*'
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
name: Publish for ${{ matrix.os }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
# This should work with only the `include`s but it currently doesn't because of this bug:
|
||||
# https://github.community/t5/How-to-use-Git-and-GitHub/GitHub-Actions-Matrix-options-dont-work-as-documented/td-p/29558
|
||||
target: [x86_64-osx, x86_64-unknown-linux-musl, armv7-unknown-linux-musleabihf, armv7-linux-androideabi, aarch64-linux-android]
|
||||
include:
|
||||
- os: macos-latest
|
||||
target: x86_64-osx
|
||||
- os: ubuntu-latest
|
||||
target: x86_64-unknown-linux-musl
|
||||
- os: ubuntu-latest
|
||||
target: armv7-unknown-linux-musleabihf
|
||||
- os: ubuntu-latest
|
||||
target: armv7-linux-androideabi
|
||||
- os: ubuntu-latest
|
||||
target: aarch64-linux-android
|
||||
|
||||
steps:
|
||||
- uses: hecrj/setup-rust-action@v1.3.1
|
||||
with:
|
||||
rust-version: stable
|
||||
- uses: actions/checkout@v1
|
||||
- name: Build
|
||||
run: scripts/action release ${{ matrix.target }}
|
||||
- name: Upload binaries to release
|
||||
uses: svenstaro/upload-release-action@v1-release
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
file: target/tar/navi.tar.gz
|
||||
asset_name: navi-${{ matrix.target }}.tar.gz
|
||||
tag: ${{ github.ref }}
|
79
.github/workflows/ci.yml
vendored
Normal file
79
.github/workflows/ci.yml
vendored
Normal file
|
@ -0,0 +1,79 @@
|
|||
# Based on https://github.com/actions-rs/meta/blob/master/recipes/quickstart.md
|
||||
#
|
||||
# While our "example" application has the platform-specific code,
|
||||
# for simplicity we are compiling and testing everything on the Ubuntu environment only.
|
||||
# For multi-OS testing see the `cross.yml` workflow.
|
||||
|
||||
on: [push]
|
||||
|
||||
name: Quickstart
|
||||
|
||||
jobs:
|
||||
check:
|
||||
name: Check
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Install stable toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: stable
|
||||
override: true
|
||||
|
||||
- name: Run cargo check
|
||||
uses: actions-rs/cargo@v1
|
||||
continue-on-error: false
|
||||
with:
|
||||
command: check
|
||||
|
||||
test:
|
||||
name: Test Suite
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Install stable toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: stable
|
||||
override: true
|
||||
|
||||
- name: Run cargo test
|
||||
uses: actions-rs/cargo@v1
|
||||
continue-on-error: false
|
||||
with:
|
||||
command: test
|
||||
|
||||
lints:
|
||||
name: Lints
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Install stable toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: stable
|
||||
override: true
|
||||
components: rustfmt, clippy
|
||||
|
||||
- name: Run cargo fmt
|
||||
uses: actions-rs/cargo@v1
|
||||
continue-on-error: false
|
||||
with:
|
||||
command: fmt
|
||||
args: --all -- --check
|
||||
|
||||
- name: Run cargo clippy
|
||||
uses: actions-rs/cargo@v1
|
||||
continue-on-error: false
|
||||
with:
|
||||
command: clippy
|
||||
args: -- -D warnings
|
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
/target
|
||||
**/*.rs.bk
|
320
Cargo.lock
generated
Normal file
320
Cargo.lock
generated
Normal file
|
@ -0,0 +1,320 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.7.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ansi_term"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"hermit-abi 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "2.33.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"unicode-segmentation 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.67"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "navi"
|
||||
version = "2.0.0"
|
||||
dependencies = [
|
||||
"regex 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"structopt 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"termion 1.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "numtoa"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error"
|
||||
version = "0.4.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro-error-attr 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustversion 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error-attr"
|
||||
version = "0.4.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustversion 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn-mid 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.1.56"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "redox_termios"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"aho-corasick 0.7.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex-syntax 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"thread_local 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "structopt"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"clap 2.33.0 (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.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "structopt-derive"
|
||||
version = "0.4.2"
|
||||
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 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn-mid"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termion"
|
||||
version = "1.5.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"numtoa 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "textwrap"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thread_local"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
version = "1.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "vec_map"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[metadata]
|
||||
"checksum aho-corasick 0.7.8 (registry+https://github.com/rust-lang/crates.io-index)" = "743ad5a418686aad3b87fd14c43badd828cf26e214a00f92a384291cf22e1811"
|
||||
"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
|
||||
"checksum atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||
"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
||||
"checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9"
|
||||
"checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205"
|
||||
"checksum hermit-abi 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "1010591b26bbfe835e9faeabeb11866061cc7dcebffd56ad7d0942d0e61aefd8"
|
||||
"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
"checksum libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)" = "eb147597cdf94ed43ab7a9038716637d2d1bf2bc571da995d0028dec06bd3018"
|
||||
"checksum memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400"
|
||||
"checksum numtoa 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef"
|
||||
"checksum proc-macro-error 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)" = "052b3c9af39c7e5e94245f820530487d19eb285faedcb40e0c3275132293f242"
|
||||
"checksum proc-macro-error-attr 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)" = "d175bef481c7902e63e3165627123fff3502f06ac043d3ef42d08c1246da9253"
|
||||
"checksum proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)" = "6c09721c6781493a2a492a96b5a5bf19b65917fe6728884e7c44dd0c60ca3435"
|
||||
"checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe"
|
||||
"checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84"
|
||||
"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76"
|
||||
"checksum regex 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "322cf97724bea3ee221b78fe25ac9c46114ebb51747ad5babd51a2fc6a8235a8"
|
||||
"checksum regex-syntax 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)" = "b28dfe3fe9badec5dbf0a79a9cccad2cfc2ab5484bdb3e44cbd1ae8b3ba2be06"
|
||||
"checksum rustversion 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b3bba175698996010c4f6dce5e7f173b6eb781fce25d2cfc45e27091ce0b79f6"
|
||||
"checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
|
||||
"checksum structopt 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "a1bcbed7d48956fcbb5d80c6b95aedb553513de0a1b451ea92679d999c010e98"
|
||||
"checksum structopt-derive 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "095064aa1f5b94d14e635d0a5684cf140c43ae40a0fd990708d38f5d669e5f64"
|
||||
"checksum syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)" = "123bd9499cfb380418d509322d7a6d52e5315f064fe4b3ad18a53d6b92c07859"
|
||||
"checksum syn-mid 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7be3539f6c128a931cf19dcee741c1af532c7fd387baa739c03dd2e96479338a"
|
||||
"checksum termion 1.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "c22cec9d8978d906be5ac94bceb5a010d885c626c4c8855721a4dbd20e3ac905"
|
||||
"checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
|
||||
"checksum thread_local 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14"
|
||||
"checksum unicode-segmentation 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0"
|
||||
"checksum unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479"
|
||||
"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
|
||||
"checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a"
|
||||
"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6"
|
||||
"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"
|
13
Cargo.toml
Normal file
13
Cargo.toml
Normal file
|
@ -0,0 +1,13 @@
|
|||
[package]
|
||||
name = "navi"
|
||||
version = "2.0.0"
|
||||
authors = ["Denis Isidoro <denis_isidoro@live.com>"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
regex = "1.3.4"
|
||||
structopt = "0.3"
|
||||
termion = "1.5.5"
|
||||
# lazy_static = "1.4.0"
|
14
Makefile
14
Makefile
|
@ -1,14 +1,8 @@
|
|||
install:
|
||||
scripts/install
|
||||
|
||||
install:
|
||||
scripts/make install
|
||||
|
||||
uninstall:
|
||||
scripts/uninstall
|
||||
|
||||
release:
|
||||
scripts/release
|
||||
|
||||
update:
|
||||
scripts/update
|
||||
|
||||
lint:
|
||||
scripts/lint
|
||||
scripts/make uninstall
|
|
@ -1,5 +1,7 @@
|
|||
# navi <img src="https://user-images.githubusercontent.com/3226564/65362934-b4432500-dbdf-11e9-8f75-815fbc5cbf8f.png" alt="icon" height="28px"/> [![CircleCI](https://circleci.com/gh/denisidoro/navi.svg?style=svg)](https://circleci.com/gh/denisidoro/navi) ![GitHub release (latest by date including pre-releases)](https://img.shields.io/github/v/release/denisidoro/navi?include_prereleases)
|
||||
|
||||
> :information_source: This project has recently been rewritten from bash to Rust. If you're facing any issue after updating, please check [this thread](https://github.com/denisidoro/navi/issues/201).
|
||||
|
||||
> :information_source: This whole codebase is being rewritten in [Rust](https://www.rust-lang.org/). If you want to contribute, please refer to [this issue](https://github.com/denisidoro/navi/issues/154). :+1:
|
||||
|
||||
An interactive cheatsheet tool for the command-line so that you won't say the following anymore:
|
||||
|
|
|
@ -24,4 +24,7 @@ $ device: adb devices --- --headers 1 --column 1
|
|||
# Start emulator
|
||||
"$ANDROID_HOME/tools/emulator" -avd <emulator> -netdelay none -netspeed full
|
||||
|
||||
# TODO: remove this
|
||||
echo "$(ls -la)"
|
||||
|
||||
$ emulator: "$ANDROID_HOME/tools/emulator" -list-avds
|
||||
|
|
|
@ -15,12 +15,13 @@ netstat -tn 2>/dev/null \
|
|||
| sort -nr \
|
||||
| head
|
||||
|
||||
# Find external, public IP address
|
||||
dig +short myip.opendns.com @resolver1.opendns.com
|
||||
|
||||
# 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' \
|
||||
| tail -n1
|
||||
|
||||
|
||||
# Find external, public IP address
|
||||
dig +short myip.opendns.com @resolver1.opendns.com
|
15
navi
15
navi
|
@ -2,10 +2,15 @@
|
|||
set -euo pipefail
|
||||
|
||||
export NAVI_HOME="$(cd "$(dirname "$0")" && pwd)"
|
||||
source "${NAVI_HOME}/src/main.sh"
|
||||
cd "$NAVI_HOME"
|
||||
|
||||
VERSION="0.18.3"
|
||||
NAVI_ENV="${NAVI_ENV:-prod}"
|
||||
release_bin="${NAVI_HOME}/target/release/navi"
|
||||
debug_bin="${NAVI_HOME}/target/debug/navi"
|
||||
|
||||
opts::eval "$@"
|
||||
main "$@"
|
||||
if [ -f "$release_bin" ]; then
|
||||
"$release_bin" "$@"
|
||||
elif [ -f "$debug_bin" ]; then
|
||||
"$debug_bin" "$@"
|
||||
else
|
||||
cargo run -- "$@"
|
||||
fi
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
bind '"\C-g": " \C-u \C-a\C-k`printf \"\\e\" && NAVI_USE_FZF_ALL_INPUTS=true navi --print`\e\C-e\C-y\C-a\C-d\C-y\ey\C-h\C-e\C-b"'
|
|
@ -1,13 +0,0 @@
|
|||
function navi-widget -d "Show cheat sheets"
|
||||
begin
|
||||
stty sane
|
||||
env NAVI_USE_FZF_ALL_INPUTS=true navi --print (commandline) | perl -pe 'chomp if eof' | read -lz result
|
||||
and commandline -- $result
|
||||
end
|
||||
commandline -f repaint
|
||||
end
|
||||
|
||||
bind \cg navi-widget
|
||||
if bind -M insert > /dev/null 2>&1
|
||||
bind -M insert \cg navi-widget
|
||||
end
|
|
@ -1,12 +0,0 @@
|
|||
_navi_path=$(dirname $0:A)
|
||||
|
||||
_call_navi() {
|
||||
local buff="$BUFFER"
|
||||
zle kill-whole-line
|
||||
local cmd="$(NAVI_USE_FZF_ALL_INPUTS=true "${_navi_path}/navi" --print <> /dev/tty)"
|
||||
zle -U "${buff}${cmd}"
|
||||
}
|
||||
|
||||
zle -N _call_navi
|
||||
|
||||
bindkey '^g' _call_navi
|
50
scripts/action
Executable file
50
scripts/action
Executable file
|
@ -0,0 +1,50 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
##? action release
|
||||
|
||||
export NAVI_HOME="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
source "${NAVI_HOME}/scripts/install"
|
||||
|
||||
release() {
|
||||
|
||||
TAR_DIR="${NAVI_HOME}/target/tar"
|
||||
|
||||
target="${1:-}"
|
||||
if [[ $target == *"osx"* ]]; then
|
||||
echoerr "OSX cross-compile is impossible. Fallbacking to cargo..."
|
||||
target=""
|
||||
fi
|
||||
|
||||
cd "$NAVI_HOME"
|
||||
|
||||
rm -rf "${NAVI_HOME}/target" 2> /dev/null || true
|
||||
|
||||
if [ -n "$target" ]; then
|
||||
cargo install cross 2> /dev/null || true
|
||||
cross build --release --locked --target "$target"
|
||||
bin_folder="${target}/release"
|
||||
else
|
||||
cargo build --release --locked
|
||||
bin_folder="release"
|
||||
fi
|
||||
|
||||
bin_path="${NAVI_HOME}/target/${bin_folder}/navi"
|
||||
chmod +x "$bin_path"
|
||||
mkdir -p "$TAR_DIR" 2> /dev/null || true
|
||||
|
||||
cp -r "${NAVI_HOME}/cheats" "$TAR_DIR"
|
||||
cp -r "${NAVI_HOME}/shell" "$TAR_DIR"
|
||||
cp "$bin_path" "$TAR_DIR"
|
||||
|
||||
cd "${NAVI_HOME}/target/tar"
|
||||
tar -czf navi.tar.gz *
|
||||
|
||||
}
|
||||
|
||||
cmd="$1"
|
||||
shift
|
||||
|
||||
case "$cmd" in
|
||||
"release") release "$@" ;;
|
||||
esac
|
33
scripts/brew
Executable file
33
scripts/brew
Executable file
|
@ -0,0 +1,33 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
##? brew formula
|
||||
|
||||
export NAVI_HOME="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
source "${NAVI_HOME}/scripts/install"
|
||||
|
||||
gen_formula() {
|
||||
version="$(latest_version_released)"
|
||||
header "version: ${version}"
|
||||
|
||||
header "sha_for linux-amd64..."
|
||||
sha_linux="$(sha_for_asset_on_github "$version" "linux-amd64")"
|
||||
|
||||
header "sha_for macos-amd64..."
|
||||
sha_osx="$(sha_for_asset_on_github "$version" "macos-amd64")"
|
||||
|
||||
header "rb..."
|
||||
curl -s https://raw.githubusercontent.com/denisidoro/homebrew-tools/master/navirs.rb \
|
||||
| sed -E "s/version ['\"].*/version '${version}'/" \
|
||||
| awk '!x{x=sub("sha256","sha_osx")}7' \
|
||||
| awk '!x{x=sub("sha256","sha_linux")}7' \
|
||||
| sed -E "s/sha_osx.*/sha256 \"${sha_osx}\"/" \
|
||||
| sed -E "s/sha_linux.*/sha256 \"${sha_linux}\"/"
|
||||
}
|
||||
|
||||
cmd="$1"
|
||||
shift
|
||||
|
||||
case "$cmd" in
|
||||
"formula") gen_formula "$@" ;;
|
||||
esac
|
|
@ -1,18 +1,11 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
debian="${1:-false}"
|
||||
export NAVI_HOME="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
|
||||
if $debian; then
|
||||
image="debian"
|
||||
setup_cmd="apt update; apt install -y git curl;"
|
||||
else
|
||||
image="ellerbrock/alpine-bash-git"
|
||||
fi
|
||||
cd "$NAVI_HOME"
|
||||
|
||||
docker run \
|
||||
-it \
|
||||
--entrypoint /bin/bash \
|
||||
-v "$(pwd):/navi" \
|
||||
"$image" \
|
||||
-c "${setup_cmd:-}git clone --depth 1 https://github.com/junegunn/fzf.git ~/.fzf && yes | ~/.fzf/install; export PATH=$PATH:/navi; bash"
|
||||
-e HOMEBREW_NO_AUTO_UPDATE=1 \
|
||||
-it linuxbrew/alpine \
|
||||
bash -c 'brew install denisidoro/tools/navirs; bash'
|
||||
|
|
8
scripts/fix
Executable file
8
scripts/fix
Executable file
|
@ -0,0 +1,8 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
export NAVI_HOME="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
|
||||
cargo +nightly fix --clippy -Z unstable-options 2> /dev/null || true
|
||||
cargo fix 2> /dev/null || true
|
||||
cargo fmt 2> /dev/null || true
|
127
scripts/install
127
scripts/install
|
@ -1,15 +1,126 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
export NAVI_HOME="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
FIRST_INSTALL_DIR="/opt/navi"
|
||||
SECOND_INSTALL_DIR="${HOME}/.navi"
|
||||
|
||||
script() {
|
||||
echo "#!/usr/bin/env bash"
|
||||
echo "${NAVI_HOME}/navi" '"$@"'
|
||||
get_install_dir() {
|
||||
local -r useless_folder="${FIRST_INSTALL_DIR}/useless"
|
||||
local folder
|
||||
mkdir -p "$useless_folder" 2>/dev/null \
|
||||
&& folder="$FIRST_INSTALL_DIR" \
|
||||
|| folder="$SECOND_INSTALL_DIR"
|
||||
rm -r "$useless_folder" 2>/dev/null
|
||||
echo "$folder"
|
||||
}
|
||||
|
||||
folder="${1:-/usr/local/bin}"
|
||||
bin="${folder}/navi"
|
||||
echoerr() {
|
||||
echo "$@" 1>&2
|
||||
}
|
||||
|
||||
script > "$bin"
|
||||
chmod +x "$bin"
|
||||
command_exists() {
|
||||
type "$1" &>/dev/null
|
||||
}
|
||||
|
||||
sha256() {
|
||||
if command_exists sha256sum; then
|
||||
sha256sum
|
||||
elif command_exists shasum; then
|
||||
shasum -a 256
|
||||
elif command_exists openssl; then
|
||||
openssl dgst -sha256
|
||||
else
|
||||
echoerr "Unable to calculate sha256!"
|
||||
exit 43
|
||||
fi
|
||||
}
|
||||
|
||||
header() {
|
||||
echoerr "$*"
|
||||
echoerr
|
||||
}
|
||||
|
||||
latest_version_released() {
|
||||
curl -s 'https://api.github.com/repos/denisidoro/navi/releases/latest' \
|
||||
| grep -Eo 'releases/tag/v([0-9\.]+)' \
|
||||
| sed 's|releases/tag/v||'
|
||||
}
|
||||
|
||||
asset_url() {
|
||||
local -r version="$1"
|
||||
local -r variant="$2"
|
||||
|
||||
echo "https://github.com/denisidoro/navi/releases/download/v${version}/navi-${variant}.tar.gz"
|
||||
}
|
||||
|
||||
download_asset() {
|
||||
local -r url="$(asset_url "$@")"
|
||||
mkdir -p "$DEFAULT_INSTALL_DIR"
|
||||
cd "$DEFAULT_INSTALL_DIR"
|
||||
rm -f navi.tar.gz
|
||||
echoerr "Downloading $url"
|
||||
curl -L "$url" -o navi.tar.gz
|
||||
tar xvzf navi.tar.gz
|
||||
rm -f navi.tar.gz
|
||||
ln -s "${DEFAULT_INSTALL_DIR}/navi" "/usr/bin/navi" \
|
||||
|| ln -s "${DEFAULT_INSTALL_DIR}/navi" "/usr/local/bin/navi"
|
||||
}
|
||||
|
||||
sha_for_asset_on_github() {
|
||||
local -r url="$(asset_url "$@")"
|
||||
curl -sL "$url" | sha256 | awk '{print $1}'
|
||||
}
|
||||
|
||||
get_target() {
|
||||
local -r unamea="$(uname -a)"
|
||||
local -r archi="$(uname -sm)"
|
||||
local is_android
|
||||
|
||||
[[ $unamea = *Android* ]] && is_android=true || is_android=false
|
||||
|
||||
local target
|
||||
case "$archi" in
|
||||
Darwin*) target="x86_64-osx" ;;
|
||||
Linux\ *64) target="x86_64-unknown-linux-musl" ;;
|
||||
*arm*) target="armv7-unknown-linux-musleabihf" ;;
|
||||
*) target="" ;;
|
||||
esac
|
||||
|
||||
echo "$target"
|
||||
}
|
||||
|
||||
no_binary_warning() {
|
||||
echoerr "There's no precompiled binary for your platform: $(uname -a)"
|
||||
}
|
||||
|
||||
get_shell() {
|
||||
echo $SHELL | xargs basename
|
||||
}
|
||||
|
||||
install_navi() {
|
||||
export DEFAULT_INSTALL_DIR="$(get_install_dir)"
|
||||
local -r target="$(get_target)"
|
||||
if [[ -n "$target" ]]; then
|
||||
local -r version="$(latest_version_released)"
|
||||
download_asset "$version" "$target"
|
||||
elif command_exists cargo; then
|
||||
no_binary_warning
|
||||
echoerr "Building sources..."
|
||||
git clone https://github.com/denisidoro/navi "$DEFAULT_INSTALL_DIR"
|
||||
cd "$DEFAULT_INSTALL_DIR"
|
||||
make install
|
||||
else
|
||||
no_binary_warning
|
||||
echoerr "You don't have the necessary tools to build it"
|
||||
echoerr "Please open an issue at https://github.com/denisidoro/navi"
|
||||
echoerr "Aborting..."
|
||||
exit 33
|
||||
fi
|
||||
|
||||
local -r shell="$(get_shell)"
|
||||
echoerr -e "Finished. To call navi, restart your shell or reload the config file:\n source ~/.${shell}rc"
|
||||
echoerr -e "\nTo add the Ctrl-G keybinding, add the following to ~/.${shell}rc:\n source \"$(navi widget ${shell})\""
|
||||
return 0
|
||||
}
|
||||
|
||||
(return 0 2>/dev/null) || install_navi "$@"
|
||||
|
|
17
scripts/lint
17
scripts/lint
|
@ -1,17 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# please refer to https://github.com/denisidoro/dotfiles/blob/master/scripts/code/beautify
|
||||
|
||||
export NAVI_HOME="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
|
||||
files_to_lint() {
|
||||
find . -iname '*.sh'
|
||||
find scripts/*
|
||||
echo "${NAVI_HOME}/test/run"
|
||||
echo "${NAVI_HOME}/navi"
|
||||
}
|
||||
|
||||
for f in $(files_to_lint); do
|
||||
dot code beautify "$f"
|
||||
done
|
27
scripts/make
Executable file
27
scripts/make
Executable file
|
@ -0,0 +1,27 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
##? make install
|
||||
##? make uninstall
|
||||
|
||||
export NAVI_HOME="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
source "${NAVI_HOME}/scripts/install"
|
||||
|
||||
install() {
|
||||
"${NAVI_HOME}/scripts/action" release
|
||||
ln -s "${NAVI_HOME}/target/tar/navi" /usr/bin/navi \
|
||||
|| ln -s "${NAVI_HOME}/target/tar/navi" /usr/local/bin/navi
|
||||
}
|
||||
|
||||
uninstall() {
|
||||
rm -rf "${NAVI_HOME}/target"
|
||||
rm /usr/local/bin/navi || rm /usr/bin/navi
|
||||
}
|
||||
|
||||
cmd="$1"
|
||||
shift
|
||||
|
||||
case "$cmd" in
|
||||
"install") install "$@" ;;
|
||||
"uninstall") uninstall "$@" ;;
|
||||
esac
|
|
@ -1,7 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
export NAVI_HOME="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
export NAVI_PATH="${NAVI_HOME}/test:${NAVI_HOME}/cheats"
|
||||
|
||||
"${NAVI_HOME}/navi" "$@"
|
|
@ -1,52 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
export NAVI_HOME="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
source "${NAVI_HOME}/src/main.sh"
|
||||
|
||||
sha256() {
|
||||
if command_exists sha256sum; then
|
||||
sha256sum
|
||||
elif command_exists shasum; then
|
||||
shasum -a 256
|
||||
elif command_exists openssl; then
|
||||
openssl dgst -sha256
|
||||
else
|
||||
echoerr "Unable to calculate sha256!"
|
||||
exit 43
|
||||
fi
|
||||
}
|
||||
|
||||
header() {
|
||||
echo "$*"
|
||||
echo
|
||||
}
|
||||
|
||||
cd "$NAVI_HOME"
|
||||
|
||||
header "git pull"
|
||||
git pull
|
||||
|
||||
version="$(grep VERSION "${NAVI_HOME}/navi" | grep -Eo '[0-9\.]+')"
|
||||
tag="v${version}"
|
||||
tar="https://github.com/denisidoro/navi/archive/${tag}.tar.gz"
|
||||
formula="/usr/local/Homebrew/Library/Taps/homebrew/homebrew-core/Formula/navi.rb"
|
||||
|
||||
header "Attempting to release version ${tag}..."
|
||||
read -p "Press enter to continue"
|
||||
|
||||
header "git tag"
|
||||
git tag -a "$tag" -m "$tag"
|
||||
|
||||
header "git push"
|
||||
git push --follow-tags
|
||||
|
||||
header "rm formula"
|
||||
rm "$formula" 2>/dev/null || true
|
||||
|
||||
header "tar sha256"
|
||||
sha="$(curl -sL "$tar" | sha256 | awk '{print $1}')"
|
||||
|
||||
header "output"
|
||||
echo " url \"${tar}\""
|
||||
echo " sha256 \"${sha}\""
|
9
scripts/run
Executable file
9
scripts/run
Executable file
|
@ -0,0 +1,9 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
export NAVI_HOME="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
|
||||
cd "$NAVI_HOME"
|
||||
rm -rf "target/release/navi" 2> /dev/null || true
|
||||
rm -rf "target/debug/navi" 2> /dev/null || true
|
||||
cargo run -- "$@"
|
9
scripts/tag
Executable file
9
scripts/tag
Executable file
|
@ -0,0 +1,9 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
export NAVI_HOME="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
|
||||
version="$1"
|
||||
|
||||
git tag -a "v${version}" -m 'WIP version written in Rust. Use 1.0.0 instead'
|
||||
git push origin --tags
|
|
@ -1,5 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
BIN="/usr/local/bin/navi"
|
||||
rm -rf "$BIN" || true
|
|
@ -1,9 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
export NAVI_HOME="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
|
||||
cd "$NAVI_HOME"
|
||||
|
||||
git pull
|
||||
"${NAVI_HOME}/scripts/install" || true
|
11
shell/navi.plugin.bash
Normal file
11
shell/navi.plugin.bash
Normal file
|
@ -0,0 +1,11 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
__call_navi() {
|
||||
local -r f="$(mktemp || echo "${HOME}/.naviresult")"
|
||||
navi --save "$f" </dev/tty >/dev/tty
|
||||
local -r r="$(cat "$f")"
|
||||
rm "$f" 2> /dev/null || true
|
||||
echo "$r"
|
||||
}
|
||||
|
||||
bind '"\C-g": " \C-b\C-k \C-u`__call_navi`\e\C-e\C-a\C-y\C-h\C-e\e \C-y\ey\C-x\C-x\C-f"'
|
23
shell/navi.plugin.fish
Normal file
23
shell/navi.plugin.fish
Normal file
|
@ -0,0 +1,23 @@
|
|||
#!/usr/bin/env fish
|
||||
|
||||
function __call_navi
|
||||
set -l f (mktemp || echo "${HOME}/.naviresult")
|
||||
navi --save "$f" </dev/tty >/dev/tty
|
||||
set -l r (cat "$f")
|
||||
rm "$f" 2> /dev/null || true
|
||||
echo "$r"
|
||||
end
|
||||
|
||||
function navi-widget -d "Show cheat sheets"
|
||||
begin
|
||||
stty sane
|
||||
__call_navi | perl -pe 'chomp if eof' | read -lz result
|
||||
and commandline -- $result
|
||||
end
|
||||
commandline -f repaint
|
||||
end
|
||||
|
||||
bind \cg navi-widget
|
||||
if bind -M insert > /dev/null 2>&1
|
||||
bind -M insert \cg navi-widget
|
||||
end
|
15
shell/navi.plugin.zsh
Normal file
15
shell/navi.plugin.zsh
Normal file
|
@ -0,0 +1,15 @@
|
|||
#!/usr/bin/env zsh
|
||||
|
||||
_call_navi() {
|
||||
local -r buff="$BUFFER"
|
||||
local -r f="$(mktemp || echo "${HOME}/.naviresult")" 2>/dev/null
|
||||
navi --save "$f" </dev/tty >/dev/tty
|
||||
local -r r="$(cat "$f")" 2>/dev/null
|
||||
rm "$f" 2> /dev/null || true
|
||||
zle kill-whole-line
|
||||
zle -U "${buff}${r}"
|
||||
}
|
||||
|
||||
zle -N _call_navi
|
||||
|
||||
bindkey '^g' _call_navi
|
117
src/arg.sh
117
src/arg.sh
|
@ -1,117 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
ARG_REGEX="<[a-zA-Z_]+([- ]?\w+)*>"
|
||||
|
||||
arg::dict() {
|
||||
local -r input="$(cat)"
|
||||
|
||||
local -r fn="$(echo "$input" | awk -F'---' '{print $1}')"
|
||||
local -r opts="$(echo "$input" | awk -F'---' '{print $2}')"
|
||||
|
||||
dict::new fn "$fn" opts "$opts"
|
||||
}
|
||||
|
||||
arg::escape() {
|
||||
echo "$*" \
|
||||
| tr '-' '_' \
|
||||
| tr ' ' '_'
|
||||
}
|
||||
|
||||
arg::interpolate() {
|
||||
local -r arg="$1"
|
||||
local -r value="$(echo "$2" | tr "$ESCAPE_CHAR_3" '\n')"
|
||||
|
||||
local -r lines="$(echo "$value" | wc -l)"
|
||||
|
||||
if [ $lines -lt 2 ]; then
|
||||
|
||||
local -r words="$(echo "$value" | wc -w | xargs)"
|
||||
|
||||
if [[ $words > 1 ]]; then
|
||||
sed "s|<${arg}>|\"${value}\"|g"
|
||||
else
|
||||
sed "s|<${arg}>|${value}|g"
|
||||
fi
|
||||
|
||||
else
|
||||
|
||||
local -r newvalue="$(echo "$value" \
|
||||
| xargs -I% echo '"%"' \
|
||||
| tr '\n' ' ')"
|
||||
|
||||
sed "s|<${arg}>|${newvalue}|g"
|
||||
|
||||
fi
|
||||
}
|
||||
|
||||
arg::next() {
|
||||
grep -Eo "$ARG_REGEX" \
|
||||
| head -n1 \
|
||||
| tr -d '<' \
|
||||
| tr -d '>'
|
||||
}
|
||||
|
||||
arg::deserialize() {
|
||||
local arg="${1:-}"
|
||||
|
||||
arg="${arg:1:${#arg}-2}"
|
||||
echo "$arg" \
|
||||
| tr "${ESCAPE_CHAR}" " " \
|
||||
| tr "${ESCAPE_CHAR_2}" "'" \
|
||||
| tr "${ESCAPE_CHAR_3}" '"'
|
||||
}
|
||||
|
||||
arg::serialize_code() {
|
||||
printf "tr ' ' '${ESCAPE_CHAR}'"
|
||||
printf " | "
|
||||
printf "tr \"'\" '${ESCAPE_CHAR_2}'"
|
||||
printf " | "
|
||||
printf "tr '\"' '${ESCAPE_CHAR_3}'"
|
||||
}
|
||||
|
||||
arg::pick() {
|
||||
local -r arg="$1"
|
||||
local -r cheat="$2"
|
||||
|
||||
local -r prefix="$ ${arg}:"
|
||||
local -r length="$(echo "$prefix" | str::length)"
|
||||
local -r arg_dict="$(echo "$cheat" | grep "$prefix" | str::sub $((length + 1)) | arg::dict)"
|
||||
|
||||
local -r fn="$(dict::get "$arg_dict" fn)"
|
||||
local -r args_str="$(dict::get "$arg_dict" opts)"
|
||||
local arg_name=""
|
||||
|
||||
for arg_str in $args_str; do
|
||||
if [ -z $arg_name ]; then
|
||||
arg_name="$(echo "$arg_str" | str::sub 2)"
|
||||
else
|
||||
eval "local $arg_name"='$arg_str'
|
||||
arg_name=""
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -n "$fn" ]; then
|
||||
local suggestions="$(eval "$fn" 2>/dev/null)"
|
||||
|
||||
local args
|
||||
args+=("--prompt"); args+=("${arg}: ")
|
||||
args+=("--header-lines"); args+=("${headers:-0}")
|
||||
if ${multi:-false}; then
|
||||
args+=("-m")
|
||||
fi
|
||||
|
||||
if [ -n "$suggestions" ]; then
|
||||
echo "$suggestions" \
|
||||
| ui::fzf ${args[@]:-} \
|
||||
| str::column "${column:-}" "${separator:-}" \
|
||||
| tr '\n' "$ESCAPE_CHAR_3"
|
||||
fi
|
||||
elif ${NAVI_USE_FZF_ALL_INPUTS:-false}; then
|
||||
echo "" | ui::fzf --prompt "$arg: " --print-query --no-select-1 --height 1
|
||||
else
|
||||
printf "\033[0;36m${arg}:\033[0;0m " > /dev/tty
|
||||
read -r value
|
||||
ui::clear_previous_line > /dev/tty
|
||||
printf "$value"
|
||||
fi
|
||||
}
|
147
src/cheat.rs
Normal file
147
src/cheat.rs
Normal file
|
@ -0,0 +1,147 @@
|
|||
use crate::display;
|
||||
use crate::filesystem;
|
||||
use crate::option::Config;
|
||||
|
||||
use regex::Regex;
|
||||
use std::collections::HashMap;
|
||||
use std::fs;
|
||||
use std::io::Write;
|
||||
|
||||
pub struct SuggestionOpts {
|
||||
pub header_lines: u8,
|
||||
pub column: Option<u8>,
|
||||
pub multi: bool,
|
||||
}
|
||||
|
||||
pub type Value = (String, Option<SuggestionOpts>);
|
||||
|
||||
fn gen_snippet(snippet: &str, line: &str) -> String {
|
||||
if snippet.is_empty() {
|
||||
line.to_string()
|
||||
} else {
|
||||
format!("{}{}", &snippet[..snippet.len() - 2], line)
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_opts(text: &str) -> SuggestionOpts {
|
||||
let mut header_lines: u8 = 0;
|
||||
let mut column: Option<u8> = None;
|
||||
let mut multi = false;
|
||||
|
||||
let mut parts = text.split(' ');
|
||||
|
||||
while let Some(p) = parts.next() {
|
||||
match p {
|
||||
"--multi" => multi = true,
|
||||
"--header" | "--header-lines" => {
|
||||
header_lines = parts.next().unwrap().parse::<u8>().unwrap()
|
||||
}
|
||||
"--column" => column = Some(parts.next().unwrap().parse::<u8>().unwrap()),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
SuggestionOpts {
|
||||
header_lines,
|
||||
column,
|
||||
multi,
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_variable_line(line: &str) -> (&str, &str, Option<SuggestionOpts>) {
|
||||
let re = Regex::new(r"^\$\s*([^:]+):(.*)").unwrap();
|
||||
let caps = re.captures(line).unwrap();
|
||||
let variable = caps.get(1).unwrap().as_str().trim();
|
||||
let mut command_plus_opts = caps.get(2).unwrap().as_str().split("---");
|
||||
let command = command_plus_opts.next().unwrap();
|
||||
let opts = match command_plus_opts.next() {
|
||||
Some(o) => Some(parse_opts(o)),
|
||||
None => None,
|
||||
};
|
||||
(variable, command, opts)
|
||||
}
|
||||
|
||||
fn read_file(
|
||||
path: &str,
|
||||
variables: &mut HashMap<String, Value>,
|
||||
stdin: &mut std::process::ChildStdin,
|
||||
) {
|
||||
let mut tags = String::from("");
|
||||
let mut comment = String::from("");
|
||||
let mut snippet = String::from("");
|
||||
|
||||
let (tag_width, comment_width) = display::widths();
|
||||
|
||||
if let Ok(lines) = filesystem::read_lines(path) {
|
||||
for l in lines {
|
||||
let line = l.unwrap();
|
||||
|
||||
// tag
|
||||
if line.starts_with('%') {
|
||||
tags = String::from(&line[2..]);
|
||||
}
|
||||
// comment
|
||||
else if line.starts_with('#') {
|
||||
comment = String::from(&line[2..]);
|
||||
}
|
||||
// variable
|
||||
else if line.starts_with('$') {
|
||||
let (variable, command, opts) = parse_variable_line(&line[..]);
|
||||
variables.insert(
|
||||
format!("{};{}", tags, variable),
|
||||
(String::from(command), opts),
|
||||
);
|
||||
}
|
||||
// snippet with line break
|
||||
else if line.ends_with('\\') {
|
||||
snippet = if !snippet.is_empty() {
|
||||
format!("{}{}", &snippet[..snippet.len() - 2], line)
|
||||
} else {
|
||||
line
|
||||
}
|
||||
}
|
||||
// blank
|
||||
else if line.is_empty() {
|
||||
}
|
||||
// snippet
|
||||
else {
|
||||
let full_snippet = gen_snippet(&snippet, &line);
|
||||
match stdin.write(
|
||||
display::format_line(
|
||||
&tags[..],
|
||||
&comment[..],
|
||||
&full_snippet[..],
|
||||
tag_width,
|
||||
comment_width,
|
||||
)
|
||||
.as_bytes(),
|
||||
) {
|
||||
Ok(_) => snippet = String::from(""),
|
||||
Err(_) => break,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_all(config: &Config, stdin: &mut std::process::ChildStdin) -> HashMap<String, Value> {
|
||||
let mut variables: HashMap<String, Value> = HashMap::new();
|
||||
|
||||
let fallback = format!("{}/cheats", filesystem::exe_path_string());
|
||||
let folders_str = config.path.as_ref().unwrap_or(&fallback);
|
||||
let folders = folders_str.split(':');
|
||||
|
||||
for folder in folders {
|
||||
if let Ok(paths) = fs::read_dir(folder) {
|
||||
for path in paths {
|
||||
read_file(
|
||||
path.unwrap().path().into_os_string().to_str().unwrap(),
|
||||
&mut variables,
|
||||
stdin,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
variables
|
||||
}
|
114
src/cheat.sh
114
src/cheat.sh
|
@ -1,114 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
cheat::find() {
|
||||
for path in $(echo "$NAVI_PATH" | tr ':' '\n'); do
|
||||
find -L "${path/#\~/$HOME}" -iname '*.cheat'
|
||||
done
|
||||
}
|
||||
|
||||
cheat::export_cache() {
|
||||
if [ -z "${NAVI_CACHE:-}" ]; then
|
||||
export NAVI_CACHE="$*"
|
||||
fi
|
||||
}
|
||||
|
||||
cheat::join_lines() {
|
||||
if command_exists perl; then
|
||||
perl -0pe 's/\\\n *//g'
|
||||
else
|
||||
tr '\n' "$ESCAPE_CHAR" \
|
||||
| sed -E 's/\\'$(printf "$ESCAPE_CHAR")' *//g' \
|
||||
| tr "$ESCAPE_CHAR" '\n'
|
||||
fi
|
||||
}
|
||||
|
||||
cheat::read_all() {
|
||||
for cheat in $(cheat::find); do
|
||||
echo
|
||||
cat "$cheat"
|
||||
echo
|
||||
done
|
||||
}
|
||||
|
||||
cheat::memoized_read_all() {
|
||||
if [ -n "${NAVI_CACHE:-}" ]; then
|
||||
echo "$NAVI_CACHE"
|
||||
return
|
||||
fi
|
||||
|
||||
local -r cheats="$(cheat::read_all)"
|
||||
echo "$cheats" \
|
||||
| cheat::join_lines
|
||||
}
|
||||
|
||||
cheat::prettify() {
|
||||
local -r print="$(dict::get "$OPTIONS" print)"
|
||||
|
||||
local -r comment_width="$(style::comment_width)"
|
||||
local -r snippet_width="$(style::snippet_width)"
|
||||
local -r tag_width="$(style::tag_width)"
|
||||
|
||||
local -r comment_color="$(style::comment_color)"
|
||||
local -r snippet_color="$(style::snippet_color)"
|
||||
local -r tag_color="$(style::tag_color)"
|
||||
|
||||
local -r columns="$(ui::width || echo 0)"
|
||||
|
||||
awk \
|
||||
-v COMMENT_COLOR=$comment_color \
|
||||
-v SNIPPET_COLOR=$snippet_color \
|
||||
-v TAG_COLOR=$tag_color \
|
||||
-v COMMENT_MAX=$((columns * comment_width / 100)) \
|
||||
-v SNIPPET_MAX=$((columns * snippet_width / 100)) \
|
||||
-v TAG_MAX=$((columns * tag_width / 100)) \
|
||||
-v SEP="$ESCAPE_CHAR_3" \
|
||||
'function color(c,s,max) {
|
||||
if (max > 0 && length(s) > max) {
|
||||
s=substr(s, 0, max)
|
||||
s=s"…"
|
||||
}
|
||||
printf("\033[%dm%s", c, s)
|
||||
}
|
||||
|
||||
/^%/ { tags=substr($0, 3); next }
|
||||
/^#/ { comment=substr($0, 3); next }
|
||||
/^\$/ { next }
|
||||
BEGIN { ORS="" }
|
||||
NF {
|
||||
print color(COMMENT_COLOR, comment, COMMENT_MAX)
|
||||
print color(0, SEP, 0)
|
||||
print color(SNIPPET_COLOR, $0, SNIPPET_MAX)
|
||||
print color(0, SEP, 0)
|
||||
print color(TAG_COLOR, tags, TAG_MAX);
|
||||
print color(0, SEP, 0)
|
||||
print color(DEFAULT, "\033", 0);
|
||||
print "\n"
|
||||
next
|
||||
}'
|
||||
}
|
||||
|
||||
cheat::until_percentage() {
|
||||
awk 'BEGIN { count=0; }
|
||||
|
||||
/^%/ { if (count >= 1) exit;
|
||||
else { count++; print $0; next; } }
|
||||
{ print $0 }'
|
||||
}
|
||||
|
||||
cheat::from_tags() {
|
||||
local -r cheats="$1"
|
||||
local -r tags="$2"
|
||||
|
||||
echo "$cheats" \
|
||||
| grep "% ${tags}" -A99999 \
|
||||
| cheat::until_percentage
|
||||
}
|
||||
|
||||
cheat::from_selection() {
|
||||
local -r cheats="$1"
|
||||
local -r selection="$2"
|
||||
|
||||
local -r tags="$(dict::get "$selection" tags)"
|
||||
|
||||
cheat::from_tags "$cheats" "$tags"
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
clip::set() {
|
||||
local -r input="${1:-}"
|
||||
|
||||
if command_exists pbcopy; then
|
||||
echo "$input" | pbcopy
|
||||
elif command_exists clip.exe; then
|
||||
echo "$input" | clip.exe
|
||||
elif command_exists xclip; then
|
||||
echo "$input" | xclip -sel clip
|
||||
elif command_exists xsel; then
|
||||
echo "$input" | xsel -i -b
|
||||
else
|
||||
echo "$input"
|
||||
die "Unable to set clipboard"
|
||||
fi
|
||||
}
|
53
src/cmd.sh
53
src/cmd.sh
|
@ -1,53 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
cmd::escape() {
|
||||
tr '\\' "$ESCAPE_CHAR_3"
|
||||
}
|
||||
|
||||
cmd::unescape() {
|
||||
tr "$ESCAPE_CHAR_3" '\\'
|
||||
}
|
||||
|
||||
cmd::loop() {
|
||||
local -r cmd="$1"
|
||||
local -r cheat="$2"
|
||||
|
||||
local arg escaped_arg value escaped_cmd
|
||||
|
||||
arg="$(echo "$cmd" | arg::next)"
|
||||
if [ -z "$arg" ]; then
|
||||
dict::new cmd "$cmd"
|
||||
return
|
||||
fi
|
||||
|
||||
escaped_arg="$(arg::escape "$arg")"
|
||||
|
||||
escaped_cmd="$(echo "$cmd" | sed "s|<${arg}>|<${escaped_arg}>|g")"
|
||||
arg="$escaped_arg"
|
||||
|
||||
local -r values="$(dict::get "$OPTIONS" values)"
|
||||
value="$(echo "$values" | coll::get $i)"
|
||||
[ -z "$value" ] && value="$(arg::pick "$arg" "$cheat")"
|
||||
|
||||
dict::new \
|
||||
cmd "${escaped_cmd:-}" \
|
||||
value "$value" \
|
||||
arg "$arg"
|
||||
}
|
||||
|
||||
cmd::finish() {
|
||||
local -r cmd="$(echo "$1" | cmd::unescape)"
|
||||
local -r selection="${2:-}"
|
||||
|
||||
local -r key="$(selection::key "$selection")"
|
||||
local -r unresolved_arg="$(echo "$cmd" | arg::next)"
|
||||
|
||||
local -r print="$(dict::get "$OPTIONS" print)"
|
||||
if [[ "$key" = "ctrl-y" ]]; then
|
||||
clip::set "$cmd"
|
||||
elif $print || [ -n "$unresolved_arg" ]; then
|
||||
echo "$cmd" | ui::remove_dep_order
|
||||
else
|
||||
eval "$cmd"
|
||||
fi
|
||||
}
|
16
src/cmds/aux.rs
Normal file
16
src/cmds/aux.rs
Normal file
|
@ -0,0 +1,16 @@
|
|||
use std::error::Error;
|
||||
use std::process;
|
||||
|
||||
pub fn abort(operation: &str, issue_number: u32) -> Result<(), Box<dyn Error>> {
|
||||
eprintln!("This version of navi doesn't support {}.", operation);
|
||||
eprintln!(
|
||||
"Please check https://github.com/denisidoro/navi/issues/{} for more info.",
|
||||
issue_number
|
||||
);
|
||||
eprintln!("");
|
||||
eprintln!("You were probably using the bash implementation of navi and are now using the Rust one, which isn't feature complete yet.");
|
||||
eprintln!("In the near future, the Rust version will have all previous features.");
|
||||
eprintln!("");
|
||||
eprintln!("I'm sorry for the inconvenience.");
|
||||
process::exit(42)
|
||||
}
|
13
src/cmds/best.rs
Normal file
13
src/cmds/best.rs
Normal file
|
@ -0,0 +1,13 @@
|
|||
use std::error::Error;
|
||||
|
||||
use crate::cmds;
|
||||
use crate::cmds::core::Variant;
|
||||
use crate::option::Config;
|
||||
|
||||
pub fn main(query: String, args: Vec<String>, config: Config) -> Result<(), Box<dyn Error>> {
|
||||
if !args.is_empty() {
|
||||
cmds::aux::abort("passing arguments to 'navi best'", 201)
|
||||
} else {
|
||||
cmds::core::main(Variant::Filter(query), config)
|
||||
}
|
||||
}
|
163
src/cmds/core.rs
Normal file
163
src/cmds/core.rs
Normal file
|
@ -0,0 +1,163 @@
|
|||
use crate::cheat;
|
||||
use crate::cmds;
|
||||
use crate::display;
|
||||
use crate::fzf;
|
||||
use crate::option::Config;
|
||||
|
||||
use regex::Regex;
|
||||
use std::collections::HashMap;
|
||||
use std::error::Error;
|
||||
use std::fs;
|
||||
use std::io::Write;
|
||||
use std::process;
|
||||
use std::process::{Command, Stdio};
|
||||
|
||||
pub enum Variant {
|
||||
Core,
|
||||
Filter(String),
|
||||
Query(String),
|
||||
}
|
||||
|
||||
fn gen_core_fzf_opts(variant: Variant, config: &Config) -> fzf::Opts {
|
||||
let mut opts = fzf::Opts {
|
||||
preview: !config.no_preview,
|
||||
autoselect: !config.no_autoselect,
|
||||
overrides: config.fzf_overrides.as_ref(),
|
||||
copyable: true,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
match variant {
|
||||
Variant::Core => (),
|
||||
Variant::Filter(f) => opts.filter = Some(f),
|
||||
Variant::Query(q) => opts.query = Some(q),
|
||||
}
|
||||
|
||||
opts
|
||||
}
|
||||
|
||||
fn extract_from_selections(raw_output: &str) -> (&str, &str, &str) {
|
||||
let mut lines = raw_output.split('\n');
|
||||
let key = lines.next().unwrap();
|
||||
let mut parts = lines.next().unwrap().split('\t');
|
||||
parts.next();
|
||||
parts.next();
|
||||
parts.next();
|
||||
let tags = parts.next().unwrap();
|
||||
parts.next();
|
||||
let snippet = parts.next().unwrap();
|
||||
(key, tags, snippet)
|
||||
}
|
||||
|
||||
fn prompt_with_suggestions(config: &Config, suggestion: &cheat::Value) -> String {
|
||||
let child = Command::new("bash")
|
||||
.stdout(Stdio::piped())
|
||||
.arg("-c")
|
||||
.arg(&suggestion.0)
|
||||
.spawn()
|
||||
.unwrap();
|
||||
|
||||
let suggestions = String::from_utf8(child.wait_with_output().unwrap().stdout).unwrap();
|
||||
|
||||
let mut opts = fzf::Opts {
|
||||
preview: false,
|
||||
autoselect: !config.no_autoselect,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
if let Some(o) = &suggestion.1 {
|
||||
opts.multi = o.multi;
|
||||
opts.header_lines = o.header_lines;
|
||||
opts.nth = o.column;
|
||||
};
|
||||
|
||||
let (output, _) = fzf::call(opts, |stdin| {
|
||||
stdin.write_all(suggestions.as_bytes()).unwrap();
|
||||
None
|
||||
});
|
||||
|
||||
String::from_utf8(output.stdout).unwrap()
|
||||
}
|
||||
|
||||
fn prompt_without_suggestions(varname: &str) -> String {
|
||||
let opts = fzf::Opts {
|
||||
preview: false,
|
||||
autoselect: false,
|
||||
suggestions: false,
|
||||
prompt: Some(display::variable_prompt(varname)),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let (output, _) = fzf::call(opts, |_stdin| None);
|
||||
|
||||
String::from_utf8(output.stdout).unwrap()
|
||||
}
|
||||
|
||||
fn gen_replacement(value: &str) -> String {
|
||||
if value.contains(' ') {
|
||||
format!("\"{}\"", &value[..value.len() - 1])
|
||||
} else {
|
||||
value[..value.len() - 1].to_string()
|
||||
}
|
||||
}
|
||||
|
||||
fn replace_variables_from_snippet(
|
||||
snippet: &str,
|
||||
tags: &str,
|
||||
variables: HashMap<String, cheat::Value>,
|
||||
config: &Config,
|
||||
) -> String {
|
||||
let mut interpolated_snippet = String::from(snippet);
|
||||
|
||||
let re = Regex::new(r"<(\w[\w\d\-_]*)>").unwrap();
|
||||
for cap in re.captures_iter(snippet) {
|
||||
let bracketed_varname = &cap[0];
|
||||
let varname = &bracketed_varname[1..bracketed_varname.len() - 1];
|
||||
let k = format!("{};{}", tags, varname);
|
||||
|
||||
let value = match variables.get(&k[..]) {
|
||||
Some(suggestion) => prompt_with_suggestions(&config, suggestion),
|
||||
None => prompt_without_suggestions(varname),
|
||||
};
|
||||
|
||||
interpolated_snippet =
|
||||
interpolated_snippet.replace(bracketed_varname, gen_replacement(&value[..]).as_str());
|
||||
}
|
||||
|
||||
interpolated_snippet
|
||||
}
|
||||
|
||||
pub fn main(variant: Variant, config: Config) -> Result<(), Box<dyn Error>> {
|
||||
let (output, variables) = fzf::call(gen_core_fzf_opts(variant, &config), |stdin| {
|
||||
Some(cheat::read_all(&config, stdin))
|
||||
});
|
||||
|
||||
match output.status.code() {
|
||||
Some(0) => {
|
||||
let raw_output = String::from_utf8(output.stdout)?;
|
||||
let (key, tags, snippet) = extract_from_selections(&raw_output[..]);
|
||||
let interpolated_snippet =
|
||||
replace_variables_from_snippet(snippet, tags, variables.unwrap(), &config);
|
||||
|
||||
if key == "ctrl-y" {
|
||||
cmds::aux::abort("copying snippets to the clipboard", 201)?
|
||||
} else if config.print {
|
||||
println!("{}", interpolated_snippet);
|
||||
} else if let Some(s) = config.save {
|
||||
fs::write(s, interpolated_snippet)?;
|
||||
} else {
|
||||
Command::new("bash")
|
||||
.arg("-c")
|
||||
.arg(&interpolated_snippet[..])
|
||||
.spawn()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Some(130) => process::exit(130),
|
||||
_ => {
|
||||
let err = String::from_utf8(output.stderr)?;
|
||||
panic!("External command failed:\n {}", err)
|
||||
}
|
||||
}
|
||||
}
|
7
src/cmds/func.rs
Normal file
7
src/cmds/func.rs
Normal file
|
@ -0,0 +1,7 @@
|
|||
use std::error::Error;
|
||||
|
||||
use super::aux;
|
||||
|
||||
pub fn main(_func: String, _args: Vec<String>) -> Result<(), Box<dyn Error>> {
|
||||
aux::abort("calling `navi fn`", 201)
|
||||
}
|
8
src/cmds/home.rs
Normal file
8
src/cmds/home.rs
Normal file
|
@ -0,0 +1,8 @@
|
|||
use std::error::Error;
|
||||
|
||||
use crate::filesystem;
|
||||
|
||||
pub fn main() -> Result<(), Box<dyn Error>> {
|
||||
println!("{}", filesystem::exe_path_string());
|
||||
Ok(())
|
||||
}
|
9
src/cmds/mod.rs
Normal file
9
src/cmds/mod.rs
Normal file
|
@ -0,0 +1,9 @@
|
|||
pub mod aux;
|
||||
pub mod best;
|
||||
pub mod core;
|
||||
pub mod func;
|
||||
pub mod home;
|
||||
pub mod preview;
|
||||
pub mod query;
|
||||
pub mod search;
|
||||
pub mod shell;
|
18
src/cmds/preview.rs
Normal file
18
src/cmds/preview.rs
Normal file
|
@ -0,0 +1,18 @@
|
|||
use std::error::Error;
|
||||
use std::process;
|
||||
|
||||
use crate::display;
|
||||
|
||||
fn extract_elements(argstr: &str) -> (&str, &str, &str) {
|
||||
let mut parts = argstr.split('\t').skip(3);
|
||||
let tags = parts.next().unwrap();
|
||||
let comment = parts.next().unwrap();
|
||||
let snippet = parts.next().unwrap();
|
||||
(tags, comment, snippet)
|
||||
}
|
||||
|
||||
pub fn main(line: String) -> Result<(), Box<dyn Error>> {
|
||||
let (tags, comment, snippet) = extract_elements(&line[..]);
|
||||
display::preview(comment, tags, snippet);
|
||||
process::exit(0)
|
||||
}
|
9
src/cmds/query.rs
Normal file
9
src/cmds/query.rs
Normal file
|
@ -0,0 +1,9 @@
|
|||
use std::error::Error;
|
||||
|
||||
use crate::cmds;
|
||||
use crate::cmds::core::Variant;
|
||||
use crate::option::Config;
|
||||
|
||||
pub fn main(query: String, config: Config) -> Result<(), Box<dyn Error>> {
|
||||
cmds::core::main(Variant::Query(query), config)
|
||||
}
|
8
src/cmds/search.rs
Normal file
8
src/cmds/search.rs
Normal file
|
@ -0,0 +1,8 @@
|
|||
use std::error::Error;
|
||||
|
||||
use super::aux;
|
||||
use crate::option::Config;
|
||||
|
||||
pub fn main(_query: String, _config: Config) -> Result<(), Box<dyn Error>> {
|
||||
aux::abort("searching for cheats online", 201)
|
||||
}
|
15
src/cmds/shell.rs
Normal file
15
src/cmds/shell.rs
Normal file
|
@ -0,0 +1,15 @@
|
|||
use std::error::Error;
|
||||
|
||||
use crate::filesystem;
|
||||
|
||||
pub fn main(shell: &str) -> Result<(), Box<dyn Error>> {
|
||||
let file = match shell {
|
||||
"zsh" => "navi.plugin.zsh",
|
||||
"fish" => "navi.plugin.fish",
|
||||
_ => "navi.plugin.bash",
|
||||
};
|
||||
|
||||
println!("{}/shell/{}", filesystem::exe_path_string(), file);
|
||||
|
||||
Ok(())
|
||||
}
|
84
src/coll.sh
84
src/coll.sh
|
@ -1,84 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
coll::new() {
|
||||
for x in "$@"; do
|
||||
echo "$x"
|
||||
done
|
||||
}
|
||||
|
||||
coll::first() {
|
||||
head -n1
|
||||
}
|
||||
|
||||
coll::rest() {
|
||||
tail -n +2
|
||||
}
|
||||
|
||||
coll::map() {
|
||||
local -r fn="$1"
|
||||
|
||||
for x in $(cat); do
|
||||
"$fn" "$x"
|
||||
done
|
||||
}
|
||||
|
||||
coll::filter() {
|
||||
local -r pred="$1"
|
||||
|
||||
for x in $(cat); do
|
||||
"$pred" "$x" && echo "$x" || true
|
||||
done
|
||||
}
|
||||
|
||||
coll::remove() {
|
||||
local -r pred="$1"
|
||||
|
||||
for x in $(cat); do
|
||||
"$pred" "$x" || echo "$x"
|
||||
done
|
||||
}
|
||||
|
||||
coll::without_empty_line() {
|
||||
local -r input="$(cat)"
|
||||
local -r words="$(echo "$input" | wc -w | xargs)"
|
||||
if [[ $words > 0 ]]; then
|
||||
echo "$input"
|
||||
fi
|
||||
}
|
||||
|
||||
coll::add() {
|
||||
cat | coll::without_empty_line
|
||||
for x in "$@"; do
|
||||
echo "$x"
|
||||
done
|
||||
}
|
||||
|
||||
coll::reverse() {
|
||||
str::reverse_lines "$@"
|
||||
}
|
||||
|
||||
coll::set() {
|
||||
sort -u
|
||||
}
|
||||
|
||||
coll::get() {
|
||||
local n="$1"
|
||||
n=$((n+1))
|
||||
sed "${n}q;d"
|
||||
}
|
||||
|
||||
# TODO: implement tailrec
|
||||
coll::reduce() {
|
||||
local -r fn="$1"
|
||||
local state="$2"
|
||||
|
||||
local -r coll="$(cat)"
|
||||
local -r x="$(echo "$coll" | coll::first)"
|
||||
|
||||
if [ -z "$x" ]; then
|
||||
echo "$state"
|
||||
else
|
||||
local -r new_state="$("$fn" "$state" "$x")"
|
||||
echo "$coll" | coll::rest | coll::reduce "$fn" "$new_state"
|
||||
fi
|
||||
}
|
113
src/dict.sh
113
src/dict.sh
|
@ -1,113 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# for an explanation behind this namespace, please check
|
||||
# https://medium.com/@den.isidoro/dictionaries-in-shell-scripts-61d34e1c91c6
|
||||
|
||||
# LIMITATIONS:
|
||||
# values with non-trivial whitespaces (newlines, subsequent spaces, etc)
|
||||
# aren't handled very well
|
||||
|
||||
dict::new() {
|
||||
if [ $# = 0 ]; then
|
||||
echo ""
|
||||
else
|
||||
echo "" | dict::assoc "$@" | str::remove_empty_lines
|
||||
fi
|
||||
}
|
||||
|
||||
dict::dissoc() {
|
||||
local -r key="$1"
|
||||
|
||||
grep -Ev "^[\s]*${key}[^:]*:"
|
||||
}
|
||||
|
||||
dict::escape_value() {
|
||||
tr '\n' "$ESCAPE_CHAR" | sed 's/\\n/'$(printf "$ESCAPE_CHAR")'/g'
|
||||
}
|
||||
|
||||
str::without_trailing_newline() {
|
||||
printf "%s" "$(cat)"
|
||||
echo
|
||||
}
|
||||
|
||||
dict::unescape_value() {
|
||||
tr "$ESCAPE_CHAR" '\n' | str::without_trailing_newline
|
||||
}
|
||||
|
||||
dict::assoc() {
|
||||
local -r key="${1:-}"
|
||||
local -r input="$(cat)"
|
||||
|
||||
if [ -z $key ]; then
|
||||
printf "$(echo "$input" | tr '%' "$ESCAPE_CHAR_2")" | tr "$ESCAPE_CHAR_2" '%'
|
||||
return
|
||||
fi
|
||||
|
||||
local -r value="$(echo "${2:-}" | dict::escape_value)"
|
||||
|
||||
shift 2
|
||||
echo "$(echo "$input" | dict::dissoc "$key")${key}: ${value}\n" | dict::assoc "$@"
|
||||
}
|
||||
|
||||
dict::get() {
|
||||
if [ $# = 1 ]; then
|
||||
local -r input="$(cat)"
|
||||
local -r key="$1"
|
||||
else
|
||||
local -r input="$1"
|
||||
local -r key="$2"
|
||||
fi
|
||||
|
||||
local -r prefix="${key}[^:]*: "
|
||||
local -r result="$(echo "$input" | grep -E "^${prefix}")"
|
||||
local -r matches="$(echo "$result" | wc -l || echo 0)"
|
||||
|
||||
if [ $matches -gt 1 ]; then
|
||||
echo "$result" | dict::unescape_value
|
||||
else
|
||||
echo "$result" | sed -E "s/${prefix}//" | dict::unescape_value
|
||||
fi
|
||||
}
|
||||
|
||||
dict::keys() {
|
||||
grep -Eo '^[^:]+: ' \
|
||||
| sed 's/: //g'
|
||||
}
|
||||
|
||||
dict::values() {
|
||||
awk -F':' '{$1=""; print $0}' \
|
||||
| cut -c3-
|
||||
}
|
||||
|
||||
dict::zipmap() {
|
||||
IFS='\n'
|
||||
|
||||
local -r keys_str="$1"
|
||||
local -r values_str="$2"
|
||||
|
||||
keys=()
|
||||
values=()
|
||||
for key in $keys_str; do
|
||||
keys+=("$key")
|
||||
done
|
||||
for value in $values_str; do
|
||||
values+=("$value")
|
||||
done
|
||||
|
||||
for ((i=0; i<${#keys[@]}; ++i)); do
|
||||
if [ -n "${keys[i]}" ]; then
|
||||
echo "${keys[i]}: ${values[i]}"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
dict::update() {
|
||||
local -r key="$1"
|
||||
local -r fn="$2"
|
||||
local -r input="$(cat)"
|
||||
|
||||
local -r value="$(echo "$input" | dict::get "$key")"
|
||||
local -r updated_value="$("$fn" "$value")"
|
||||
|
||||
echo "$input" | dict::assoc "$key" "$updated_value"
|
||||
}
|
56
src/display.rs
Normal file
56
src/display.rs
Normal file
|
@ -0,0 +1,56 @@
|
|||
use termion::{color, terminal_size};
|
||||
|
||||
static COMMENT_COLOR: color::LightCyan = color::LightCyan;
|
||||
static TAG_COLOR: color::LightGreen = color::LightGreen;
|
||||
static SNIPPET_COLOR: color::White = color::White;
|
||||
|
||||
pub fn widths() -> (usize, usize) {
|
||||
let full_width = terminal_size().unwrap().0;
|
||||
let tag_width = full_width * 10 / 100;
|
||||
let comment_width = full_width * 50 / 100;
|
||||
(usize::from(tag_width), usize::from(comment_width))
|
||||
}
|
||||
|
||||
pub fn variable_prompt(varname: &str) -> String {
|
||||
format!("{}: ", varname)
|
||||
}
|
||||
|
||||
pub fn preview(comment: &str, tags: &str, snippet: &str) {
|
||||
println!(
|
||||
"{comment_color}{comment} {tag_color}{tags} \n{snippet_color}{snippet}",
|
||||
comment = format!("# {}", comment),
|
||||
tags = format!("[{}]", tags),
|
||||
snippet = snippet,
|
||||
comment_color = color::Fg(COMMENT_COLOR),
|
||||
tag_color = color::Fg(TAG_COLOR),
|
||||
snippet_color = color::Fg(SNIPPET_COLOR),
|
||||
);
|
||||
}
|
||||
|
||||
fn limit_str(text: &str, length: usize) -> String {
|
||||
if text.len() > length {
|
||||
format!("{}…", &text[..length - 1])
|
||||
} else {
|
||||
format!("{:width$}", text, width = length)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format_line(
|
||||
tags: &str,
|
||||
comment: &str,
|
||||
full_snippet: &str,
|
||||
tag_width: usize,
|
||||
comment_width: usize,
|
||||
) -> String {
|
||||
format!(
|
||||
"{tag_color}{tags_short}\t{comment_color}{comment_short}\t{snippet_color}{snippet_short}\t{tags}\t{comment}\t{snippet}\t\n",
|
||||
tags_short = limit_str(tags, tag_width),
|
||||
comment_short = limit_str(comment, comment_width),
|
||||
snippet_short = full_snippet,
|
||||
comment_color = color::Fg(COMMENT_COLOR),
|
||||
tag_color = color::Fg(TAG_COLOR),
|
||||
snippet_color = color::Fg(SNIPPET_COLOR),
|
||||
tags = tags,
|
||||
comment = comment,
|
||||
snippet = &full_snippet)
|
||||
}
|
|
@ -1,63 +0,0 @@
|
|||
An interactive cheatsheet tool for the command-line
|
||||
|
||||
Usage:
|
||||
navi [command] [<args>...] [options]
|
||||
|
||||
Commands:
|
||||
query <cmd> Pre-filter results
|
||||
search <cmd> Search for cheatsheets on online repositories
|
||||
best <cmd> <args>... Considers the best match
|
||||
widget <shell> Prints the path for the widget file to be sourced
|
||||
|
||||
Options:
|
||||
--print Prevent script execution
|
||||
--path List of :-separated paths to look for cheats
|
||||
--no-interpolation Prevent argument interpolation
|
||||
--no-preview Hide command preview window
|
||||
--no-autoselect Prevents autoselecting single entries in a list
|
||||
--fzf-overrides Overrides for fzf commands [default: --with-nth 3,1,2 --exact]
|
||||
--col-widths Set column widths [default: 15,50,0]
|
||||
--col-colors Set color ANSI codes [default: 90,34,37]
|
||||
|
||||
Keybindings:
|
||||
Ctrl-y Copy snippet to clipboard
|
||||
Ctrl-j Move down one entry
|
||||
Ctrl-k Move up one entry
|
||||
|
||||
Environment variables:
|
||||
NAVI_PATH List of :-separated paths to look for cheats
|
||||
FZF_DEFAULT_OPTS Default fzf options (e.g. '--layout=reverse')
|
||||
|
||||
Examples:
|
||||
navi # default behavior
|
||||
navi --print # doesn't execute the snippet
|
||||
navi --path '/some/dir:/other/dir' # uses custom cheats
|
||||
navi search docker # uses online data
|
||||
navi query git # filters results by "git"
|
||||
navi best 'sql create db' root mydb # uses a snippet as a CLI
|
||||
source "$(navi widget zsh)" # loads the zsh widget
|
||||
navi --col-widths 10,50,0 # limits the first two columns's width
|
||||
navi --col-colors 32,32,32 # make all columns green
|
||||
navi --fzf-overrides '--with-nth 1,2' # shows only the comment and tag columns
|
||||
navi --fzf-overrides '--nth 1,2' # search will consider only the first two columns
|
||||
navi --fzf-overrides '--no-exact' # looser search algorithm
|
||||
|
||||
More info:
|
||||
|
||||
search
|
||||
http://cheat.sh is used for cheatsheet retrieval
|
||||
please note that these cheatsheets may not work out of the box
|
||||
always check the preview window before executing commands!
|
||||
|
||||
--col-widths
|
||||
the number is the percentage relative to the terminal width
|
||||
0 means that a column will be as wide as necessary
|
||||
|
||||
fzf properties
|
||||
please refer to https://github.com/junegunn/fzf for further details
|
||||
|
||||
fzf's --with-nth
|
||||
1 refers to the comment column; 2, snippet; 3, tag
|
||||
|
||||
full docs
|
||||
please refer to the README at https://github.com/denisidoro/navi
|
33
src/filesystem.rs
Normal file
33
src/filesystem.rs
Normal file
|
@ -0,0 +1,33 @@
|
|||
use std::fs::File;
|
||||
use std::io::{self, BufRead, BufReader, Lines};
|
||||
use std::path::Path;
|
||||
|
||||
pub fn read_lines<P>(filename: P) -> io::Result<Lines<BufReader<File>>>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
let file = File::open(filename)?;
|
||||
Ok(io::BufReader::new(file).lines())
|
||||
}
|
||||
|
||||
pub fn exe_string() -> String {
|
||||
String::from(
|
||||
std::env::current_exe()
|
||||
.unwrap()
|
||||
.as_os_str()
|
||||
.to_str()
|
||||
.unwrap(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn exe_path_string() -> String {
|
||||
String::from(
|
||||
std::env::current_exe()
|
||||
.unwrap()
|
||||
.parent()
|
||||
.unwrap()
|
||||
.as_os_str()
|
||||
.to_str()
|
||||
.unwrap(),
|
||||
)
|
||||
}
|
135
src/fzf.rs
Normal file
135
src/fzf.rs
Normal file
|
@ -0,0 +1,135 @@
|
|||
use crate::cheat;
|
||||
use crate::filesystem;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::process;
|
||||
use std::process::{Command, Stdio};
|
||||
|
||||
pub struct Opts<'a> {
|
||||
pub query: Option<String>,
|
||||
pub filter: Option<String>,
|
||||
pub prompt: Option<String>,
|
||||
pub preview: bool,
|
||||
pub autoselect: bool,
|
||||
pub overrides: Option<&'a String>, // TODO: remove &'a
|
||||
pub header_lines: u8,
|
||||
pub multi: bool,
|
||||
pub copyable: bool,
|
||||
pub suggestions: bool,
|
||||
pub nth: Option<u8>,
|
||||
}
|
||||
|
||||
impl Default for Opts<'_> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
query: None,
|
||||
filter: None,
|
||||
preview: true,
|
||||
autoselect: true,
|
||||
overrides: None,
|
||||
header_lines: 0,
|
||||
prompt: None,
|
||||
multi: false,
|
||||
copyable: false,
|
||||
suggestions: true,
|
||||
nth: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn call<F>(opts: Opts, stdin_fn: F) -> (process::Output, Option<HashMap<String, cheat::Value>>)
|
||||
where
|
||||
F: Fn(&mut process::ChildStdin) -> Option<HashMap<String, cheat::Value>>,
|
||||
{
|
||||
let mut c = Command::new("fzf");
|
||||
|
||||
c.args(&[
|
||||
"--preview-window",
|
||||
"up:2",
|
||||
"--with-nth",
|
||||
"1,2,3",
|
||||
"--delimiter",
|
||||
"\t",
|
||||
"--ansi",
|
||||
"--bind",
|
||||
"ctrl-j:down,ctrl-k:up",
|
||||
"--exact",
|
||||
]);
|
||||
|
||||
if opts.autoselect {
|
||||
c.arg("--select-1");
|
||||
}
|
||||
|
||||
if opts.multi {
|
||||
c.arg("--multi");
|
||||
}
|
||||
|
||||
if opts.copyable {
|
||||
c.args(&["--expect", "ctrl-y,enter"]);
|
||||
}
|
||||
|
||||
if opts.preview {
|
||||
c.args(&[
|
||||
"--preview",
|
||||
format!("{} preview {{}}", filesystem::exe_string()).as_str(),
|
||||
]);
|
||||
}
|
||||
|
||||
if let Some(q) = opts.query {
|
||||
c.args(&["--query", &q]);
|
||||
}
|
||||
|
||||
if let Some(f) = opts.filter {
|
||||
c.args(&["--filter", &f]);
|
||||
}
|
||||
|
||||
if let Some(p) = opts.prompt {
|
||||
c.args(&["--prompt", &p]);
|
||||
}
|
||||
|
||||
if let Some(n) = opts.nth {
|
||||
c.args(&["--nth", &n.to_string()]);
|
||||
}
|
||||
|
||||
if opts.header_lines > 0 {
|
||||
c.args(&["--header-lines", format!("{}", opts.header_lines).as_str()]);
|
||||
}
|
||||
|
||||
if let Some(o) = opts.overrides {
|
||||
o.as_str()
|
||||
.split(' ')
|
||||
.map(|s| s.to_string())
|
||||
.filter(|s| !s.is_empty())
|
||||
.for_each(|s| {
|
||||
c.arg(s);
|
||||
});
|
||||
}
|
||||
|
||||
if !opts.suggestions {
|
||||
c.args(&["--print-query", "--no-select-1", "--height", "1"]);
|
||||
}
|
||||
|
||||
let child = c
|
||||
.stdin(Stdio::piped())
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::inherit())
|
||||
.spawn();
|
||||
|
||||
let mut child = match child {
|
||||
Ok(x) => x,
|
||||
Err(_) => {
|
||||
eprintln!( "navi was unable to call fzf.\nPlease make sure it's correctly installed\nRefer to https://github.com/junegunn/fzf for more info.");
|
||||
process::exit(33)
|
||||
}
|
||||
};
|
||||
|
||||
let stdin = child
|
||||
.stdin
|
||||
.as_mut()
|
||||
.ok_or("Child process stdin has not been captured!")
|
||||
.unwrap();
|
||||
|
||||
let result = stdin_fn(stdin);
|
||||
|
||||
(child.wait_with_output().unwrap(), result)
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
health::fzf() {
|
||||
if ! command_exists fzf && ! [ $NAVI_ENV -eq "test" ]; then
|
||||
echoerr "You need to install fzf before using navi"
|
||||
echoerr "Please refer to https://github.com/junegunn/fzf for install instructions"
|
||||
exit 66
|
||||
fi
|
||||
}
|
36
src/main.rs
Normal file
36
src/main.rs
Normal file
|
@ -0,0 +1,36 @@
|
|||
// #[macro_use]
|
||||
// extern crate lazy_static;
|
||||
|
||||
use std::error::Error;
|
||||
|
||||
mod cheat;
|
||||
mod cmds;
|
||||
mod display;
|
||||
mod filesystem;
|
||||
mod fzf;
|
||||
mod option;
|
||||
|
||||
use crate::cmds::core::Variant;
|
||||
use option::{Command, InternalCommand};
|
||||
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
match option::internal_command() {
|
||||
Some(InternalCommand::Preview { line }) => cmds::preview::main(line),
|
||||
_ => {
|
||||
let mut config = option::parse();
|
||||
match config.cmd.as_mut() {
|
||||
None => cmds::core::main(Variant::Core, config),
|
||||
Some(c) => match c {
|
||||
Command::Query { query } => cmds::query::main(query.clone(), config),
|
||||
Command::Best { query, args } => {
|
||||
cmds::best::main(query.clone(), args.to_vec(), config)
|
||||
}
|
||||
Command::Search { query } => cmds::search::main(query.clone(), config),
|
||||
Command::Widget { shell } => cmds::shell::main(&shell[..]),
|
||||
Command::Func { func, args } => cmds::func::main(func.clone(), args.to_vec()),
|
||||
Command::Home => cmds::home::main(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
148
src/main.sh
148
src/main.sh
|
@ -1,148 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
if ${NAVI_FORCE_GNU:-false} && [ -n "${DOTFILES:-}" ]; then
|
||||
source "${DOTFILES}/scripts/core/main.sh"
|
||||
fi
|
||||
|
||||
source "${NAVI_HOME}/src/arg.sh"
|
||||
source "${NAVI_HOME}/src/cheat.sh"
|
||||
source "${NAVI_HOME}/src/cmd.sh"
|
||||
source "${NAVI_HOME}/src/clipboard.sh"
|
||||
source "${NAVI_HOME}/src/coll.sh"
|
||||
source "${NAVI_HOME}/src/dict.sh"
|
||||
source "${NAVI_HOME}/src/health.sh"
|
||||
source "${NAVI_HOME}/src/misc.sh"
|
||||
source "${NAVI_HOME}/src/opts.sh"
|
||||
source "${NAVI_HOME}/src/search.sh"
|
||||
source "${NAVI_HOME}/src/selection.sh"
|
||||
source "${NAVI_HOME}/src/str.sh"
|
||||
source "${NAVI_HOME}/src/style.sh"
|
||||
source "${NAVI_HOME}/src/ui.sh"
|
||||
|
||||
handler::main() {
|
||||
local -r cheats="$(cheat::memoized_read_all)"
|
||||
cheat::export_cache "$cheats"
|
||||
local -r selection="$(ui::select "$cheats")"
|
||||
local -r cheat="$(cheat::from_selection "$cheats" "$selection")"
|
||||
[ -z "$cheat" ] && die "No valid cheatsheet!"
|
||||
|
||||
local -r interpolation="$(dict::get "$OPTIONS" interpolation)"
|
||||
|
||||
local cmd="$(selection::snippet "$selection")"
|
||||
local result arg value
|
||||
|
||||
local i=0
|
||||
while $interpolation; do
|
||||
result="$(cmd::loop "$cmd" "$cheat")"
|
||||
arg="$(dict::get "$result" arg)"
|
||||
value="$(dict::get "$result" value)"
|
||||
cmd="$(dict::get "$result" cmd)"
|
||||
|
||||
[ -z "$arg" ] && break
|
||||
[ -z "$value" ] && die "Unable to fetch suggestions for '$arg'!"
|
||||
|
||||
eval "local $arg"='$value'
|
||||
cmd="$(echo "$cmd" | arg::interpolate "$arg" "$value")"
|
||||
|
||||
i=$((i+1))
|
||||
done
|
||||
|
||||
cmd::finish "$cmd" "$selection"
|
||||
}
|
||||
|
||||
handler::preview() {
|
||||
local -r query="$1"
|
||||
local -r cheats="$(cheat::memoized_read_all)"
|
||||
local -r selection="$(echo "$query" | selection::dict "$cheats")"
|
||||
local -r cheat="$(cheat::from_selection "$cheats" "$selection")"
|
||||
[ -n "$cheat" ] && ui::print_preview "$selection"
|
||||
}
|
||||
|
||||
handler::help() {
|
||||
opts::extract_help "$0"
|
||||
}
|
||||
|
||||
handler::version() {
|
||||
local -r full="${1:-false}"
|
||||
|
||||
echo "${VERSION:-unknown}"
|
||||
|
||||
if $full; then
|
||||
source "${NAVI_HOME}/src/version.sh"
|
||||
version::code 2>/dev/null \
|
||||
|| die "unknown code"
|
||||
fi
|
||||
}
|
||||
|
||||
handler::script() {
|
||||
"${NAVI_HOME}/scripts/"${SCRIPT_ARGS[@]}
|
||||
}
|
||||
|
||||
handler::fn() {
|
||||
${SCRIPT_ARGS[@]}
|
||||
}
|
||||
|
||||
handler::home() {
|
||||
echo "${NAVI_HOME}"
|
||||
}
|
||||
|
||||
handler::widget() {
|
||||
local widget
|
||||
local -r print="$(dict::get "$OPTIONS" print)"
|
||||
|
||||
case "$SH" in
|
||||
zsh) widget="${NAVI_HOME}/navi.plugin.zsh" ;;
|
||||
bash) widget="${NAVI_HOME}/navi.plugin.bash" ;;
|
||||
fish) widget="${NAVI_HOME}/navi.plugin.fish" ;;
|
||||
*) die "Invalid shell: $SH" ;;
|
||||
esac
|
||||
|
||||
$print \
|
||||
&& cat "$widget" \
|
||||
|| echo "$widget"
|
||||
}
|
||||
|
||||
handler::search() {
|
||||
local -r query="$(dict::get "$OPTIONS" query)"
|
||||
search::save "$query" || true
|
||||
handler::main
|
||||
}
|
||||
|
||||
main() {
|
||||
case "$(dict::get "$OPTIONS" entry_point)" in
|
||||
preview)
|
||||
local -r query="$(dict::get "$OPTIONS" query)"
|
||||
handler::preview "$query" \
|
||||
|| echoerr "Unable to find command for '$query'"
|
||||
;;
|
||||
search)
|
||||
health::fzf
|
||||
handler::search
|
||||
;;
|
||||
version)
|
||||
handler::version false
|
||||
;;
|
||||
full-version)
|
||||
handler::version true
|
||||
;;
|
||||
home)
|
||||
handler::home
|
||||
;;
|
||||
script)
|
||||
handler::script
|
||||
;;
|
||||
fn)
|
||||
handler::fn
|
||||
;;
|
||||
help)
|
||||
handler::help
|
||||
;;
|
||||
widget)
|
||||
handler::widget
|
||||
;;
|
||||
*)
|
||||
health::fzf
|
||||
handler::main
|
||||
;;
|
||||
esac
|
||||
}
|
49
src/misc.sh
49
src/misc.sh
|
@ -1,49 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
command_exists() {
|
||||
local -r cmd="${1:-}"
|
||||
[ -n $cmd ] && type "$cmd" &> /dev/null
|
||||
}
|
||||
|
||||
platform::existing_command() {
|
||||
local cmd
|
||||
for cmd in "$@"; do
|
||||
if command_exists "$cmd"; then
|
||||
echo "$cmd"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
echoerr() {
|
||||
echo "$@" 1>&2
|
||||
}
|
||||
|
||||
url::open() {
|
||||
local -r cmd="$(platform::existing_command "${BROWSER:-}" xdg-open open google-chrome firefox)"
|
||||
"$cmd" "$@" & disown
|
||||
}
|
||||
|
||||
tap() {
|
||||
local -r input="$(cat)"
|
||||
echoerr "$input"
|
||||
echo "$input"
|
||||
}
|
||||
|
||||
die() {
|
||||
echoerr "$@"
|
||||
exit 42
|
||||
}
|
||||
|
||||
or() {
|
||||
local -r x="$(cat)"
|
||||
local -r y="${1:-}"
|
||||
|
||||
if [ -n "$x" ]; then
|
||||
echo "$x"
|
||||
elif [ $# -gt 0 ]; then
|
||||
shift
|
||||
echo "$y" | or "$@"
|
||||
fi
|
||||
}
|
82
src/option.rs
Normal file
82
src/option.rs
Normal file
|
@ -0,0 +1,82 @@
|
|||
use std::env;
|
||||
use structopt::StructOpt;
|
||||
|
||||
#[derive(Debug, StructOpt)]
|
||||
#[structopt(after_help = "EXAMPLES:
|
||||
navi # default behavior
|
||||
navi --print # doesn't execute the snippet
|
||||
navi --path '/some/dir:/other/dir' # uses custom cheats
|
||||
navi search docker # uses online data
|
||||
navi query git # filters results by \"git\"
|
||||
navi best 'sql create db' root mydb # uses a snippet as a CLI
|
||||
source \"$(navi widget zsh)\" # loads the zsh widget
|
||||
navi --fzf-overrides ' --with-nth 1,2' # shows only the comment and tag columns
|
||||
navi --fzf-overrides ' --nth 1,2' # search will consider only the first two columns
|
||||
navi --fzf-overrides ' --no-exact' # looser search algorithm")]
|
||||
pub struct Config {
|
||||
/// List of :-separated paths containing .cheat files
|
||||
#[structopt(short, long, env = "NAVI_PATH")]
|
||||
pub path: Option<String>,
|
||||
|
||||
/// [alpha] Instead of executing a snippet, saves it to a file
|
||||
#[structopt(short, long)]
|
||||
pub save: Option<String>,
|
||||
|
||||
/// Instead of executing a snippet, prints it to stdout
|
||||
#[structopt(long)]
|
||||
pub print: bool,
|
||||
|
||||
/// Prevents autoselection in case of single entry
|
||||
#[structopt(long)]
|
||||
pub no_autoselect: bool,
|
||||
|
||||
/// Hides preview window
|
||||
#[structopt(long)]
|
||||
pub no_preview: bool,
|
||||
|
||||
// #[structopt(long)]
|
||||
// pub col_widths: Option<String>,
|
||||
/// Overrides for fzf commands (must start with an empty space)
|
||||
#[structopt(long)]
|
||||
#[structopt(long)]
|
||||
pub fzf_overrides: Option<String>,
|
||||
|
||||
#[structopt(subcommand)]
|
||||
pub cmd: Option<Command>,
|
||||
}
|
||||
|
||||
#[derive(Debug, StructOpt)]
|
||||
pub enum Command {
|
||||
/// Filters results
|
||||
Query { query: String },
|
||||
/// Shows navi's home directory
|
||||
Home,
|
||||
/// Uses online repositories for cheatsheets
|
||||
Search { query: String },
|
||||
/// Autoselects the snippet that best matches the query
|
||||
Best { query: String, args: Vec<String> },
|
||||
/// Performs ad-hoc functions provided by navi
|
||||
Func { func: String, args: Vec<String> },
|
||||
/// Shows the path for shell widget files
|
||||
Widget { shell: String },
|
||||
}
|
||||
|
||||
pub enum InternalCommand {
|
||||
Preview { line: String },
|
||||
}
|
||||
|
||||
pub fn parse() -> Config {
|
||||
Config::from_args()
|
||||
}
|
||||
|
||||
pub fn internal_command() -> Option<InternalCommand> {
|
||||
let mut args = env::args();
|
||||
args.next();
|
||||
if args.next() == Some(String::from("preview")) {
|
||||
Some(InternalCommand::Preview {
|
||||
line: args.next().unwrap(),
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
82
src/opts.sh
82
src/opts.sh
|
@ -1,82 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
opts::extract_help() {
|
||||
local -r file="${NAVI_HOME}/src/docstring.txt"
|
||||
cat "$file"
|
||||
}
|
||||
|
||||
opts::eval() {
|
||||
local wait_for=""
|
||||
local entry_point="main"
|
||||
local print=false
|
||||
local interpolation=true
|
||||
local preview=true
|
||||
local path="${NAVI_PATH:-${NAVI_DIR:-${NAVI_HOME}/cheats}}"
|
||||
local autoselect=true
|
||||
local best=false
|
||||
local query=""
|
||||
local values=""
|
||||
local col_widths="15,50,0"
|
||||
local col_colors="90,34,37"
|
||||
local fzf_overrides="--with-nth 3,1,2 --exact"
|
||||
local fzf_opts="${FZF_DEFAULT_OPTS:---height 70% --reverse --border --inline-info --cycle}"
|
||||
|
||||
case "${1:-}" in
|
||||
--version|version) entry_point="version"; shift ;;
|
||||
--full-version|full-version) entry_point="full-version"; shift ;;
|
||||
--help|help) entry_point="help"; shift ;;
|
||||
search) entry_point="search"; wait_for="search"; shift ;;
|
||||
preview) entry_point="preview"; wait_for="preview"; shift ;;
|
||||
query|q) wait_for="query"; shift ;;
|
||||
best|b) best=true; wait_for="best"; shift ;;
|
||||
home) entry_point="home"; shift ;;
|
||||
script) entry_point="script"; shift; SCRIPT_ARGS="$@" ;;
|
||||
fn) entry_point="fn"; shift; SCRIPT_ARGS="$@" ;;
|
||||
widget) entry_point="widget"; shift; wait_for="widget" ;;
|
||||
esac
|
||||
|
||||
for arg in "$@"; do
|
||||
case $wait_for in
|
||||
path) path="$arg"; wait_for=""; continue ;;
|
||||
preview) query="$(arg::deserialize "$arg")"; wait_for=""; continue ;;
|
||||
search) query="$arg"; wait_for=""; path="${path}:$(search::full_path "$query")"; continue ;;
|
||||
query|best) query="$arg"; wait_for=""; continue ;;
|
||||
widget) SH="$arg"; wait_for=""; continue ;;
|
||||
col-widths) col_widths="$(echo "$arg" | xargs | tr ' ' ',')"; wait_for=""; continue ;;
|
||||
col-colors) col_colors="$(echo "$arg")"; wait_for=""; continue ;;
|
||||
fzf-overrides) fzf_overrides="$arg" ; wait_for=""; continue ;;
|
||||
esac
|
||||
|
||||
case $arg in
|
||||
--print) print=true ;;
|
||||
--no-interpolation) interpolation=false ;;
|
||||
--interpolation) interpolation=true ;;
|
||||
--no-preview) preview=false ;;
|
||||
--preview) preview=true ;;
|
||||
--path|--dir) wait_for="path" ;;
|
||||
--no-autoselect) autoselect=false ;;
|
||||
--autoselect) autoselect=true ;;
|
||||
--col-widths) wait_for="col-widths" ;;
|
||||
--col-colors) wait_for="col-colors" ;;
|
||||
--fzf-overrides) wait_for="fzf-overrides" ;;
|
||||
*) values="$(echo "$values" | coll::add "$arg")" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
OPTIONS="$(dict::new \
|
||||
entry_point "$entry_point" \
|
||||
print "$print" \
|
||||
interpolation "$interpolation" \
|
||||
preview "$preview" \
|
||||
autoselect "$autoselect" \
|
||||
query "$query" \
|
||||
best "$best" \
|
||||
values "$values" \
|
||||
fzf-overrides "$fzf_overrides" \
|
||||
col-colors "$col_colors" \
|
||||
col-widths "$col_widths")"
|
||||
|
||||
export NAVI_PATH="$path"
|
||||
export FZF_DEFAULT_OPTS="$fzf_opts"
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
search::cheat() {
|
||||
local -r cmd="$(echo "$1" | str::first_word)"
|
||||
|
||||
echo "% ${cmd}, cheatsh"
|
||||
echo
|
||||
curl -s "${CHTSH_URL:-http://cht.sh}/${cmd}?T"
|
||||
}
|
||||
|
||||
search::filename() {
|
||||
local -r cmd="$(echo "$1" | str::first_word)"
|
||||
|
||||
echo "${cmd}_cheatsh" \
|
||||
| head -n1 \
|
||||
| awk '{print $NF}' \
|
||||
| xargs \
|
||||
| tr ' ' '_'
|
||||
}
|
||||
|
||||
search::full_path() {
|
||||
local -r cmd="$(echo "$1" | str::first_word)"
|
||||
|
||||
echo "/tmp/navi/$(search::filename "$cmd").cheat"
|
||||
}
|
||||
|
||||
search::save() {
|
||||
local -r cmd="$(echo "$1" | str::first_word)"
|
||||
|
||||
local -r filepath="$(search::full_path "$cmd")"
|
||||
local -r filedir="$(dirname "$filepath")"
|
||||
|
||||
if [ -f "$filepath" ]; then
|
||||
return
|
||||
fi
|
||||
|
||||
mkdir -p "$filedir" &> /dev/null || true
|
||||
search::cheat "$cmd" > "$filepath"
|
||||
}
|
|
@ -1,75 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
SELECTION_ESCAPE_STR=" "
|
||||
|
||||
selection_str::cleanup() {
|
||||
sed -E "s/ +/${SELECTION_ESCAPE_STR}/g"
|
||||
}
|
||||
|
||||
selection_str::without_ellipsis() {
|
||||
tr -d "…"
|
||||
}
|
||||
|
||||
selection_str::comment() {
|
||||
echo "$*" | awk -F "${SELECTION_ESCAPE_STR}" '{print $1}' | selection_str::without_ellipsis
|
||||
}
|
||||
|
||||
selection_str::snippet() {
|
||||
echo "$*" | awk -F "${SELECTION_ESCAPE_STR}" '{print $2}' | selection_str::without_ellipsis
|
||||
}
|
||||
|
||||
selection_str::tags() {
|
||||
echo "$*" | awk -F "${SELECTION_ESCAPE_STR}" '{print $3}' | selection_str::without_ellipsis
|
||||
}
|
||||
|
||||
selection::resolve_ellipsis() {
|
||||
local -r str="$(selection_str::cleanup)"
|
||||
local -r cheats="$*"
|
||||
|
||||
if echo "$str" | grep -q "…"; then
|
||||
local -r comment="$(selection_str::comment "$str")"
|
||||
local -r snippet="$(selection_str::snippet "$str")"
|
||||
local -r tags="$(selection_str::tags "$str")"
|
||||
local -r cheat="$(cheat::from_tags "$cheats" "$tags")"
|
||||
|
||||
local -r tags2="$(echo "$cheat" | head -n1 | str::sub 2)"
|
||||
local -r comment2="$(echo "$cheat" | grep "$comment" | str::last_line | str::sub 2)"
|
||||
local -r snippet2="$(echo "$cheat" | grep "$comment2" -A 999| str::last_paragraph_line)"
|
||||
|
||||
echo "${comment2}${SELECTION_ESCAPE_STR}${snippet2}${SELECTION_ESCAPE_STR}${tags2}"
|
||||
else
|
||||
echo "$str"
|
||||
fi
|
||||
}
|
||||
|
||||
selection::dict() {
|
||||
local -r cheats="$1"
|
||||
local -r key="${2:-}"
|
||||
local -r str="$(selection::resolve_ellipsis "$cheats")"
|
||||
|
||||
local -r comment="$(selection_str::comment "$str")"
|
||||
local -r snippet="$(selection_str::snippet "$str")"
|
||||
local -r tags="$(selection_str::tags "$str")"
|
||||
|
||||
dict::new comment "$comment" snippet "$snippet" tags "$tags" key "$key" | sed "s/'''/'/g"
|
||||
}
|
||||
|
||||
selection::comment() {
|
||||
local -r selection="$1"
|
||||
dict::get "$selection" comment
|
||||
}
|
||||
|
||||
selection::snippet() {
|
||||
local -r selection="$1"
|
||||
dict::get "$selection" snippet
|
||||
}
|
||||
|
||||
selection::tags() {
|
||||
local -r selection="$1"
|
||||
dict::get "$selection" tags
|
||||
}
|
||||
|
||||
selection::key() {
|
||||
local -r selection="$1"
|
||||
dict::get "$selection" key
|
||||
}
|
85
src/str.sh
85
src/str.sh
|
@ -1,85 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
ESCAPE_CHAR="\034"
|
||||
ESCAPE_CHAR_2="\035"
|
||||
ESCAPE_CHAR_3="\036"
|
||||
|
||||
str::length() {
|
||||
awk '{print length}'
|
||||
}
|
||||
|
||||
str::sub() {
|
||||
local -r start="${1:-0}"
|
||||
local -r finish="${2:-99999}"
|
||||
|
||||
cut -c "$((start + 1))-$((finish - 1))"
|
||||
}
|
||||
|
||||
str::column() {
|
||||
local -r n="${1:-}"
|
||||
local -r separator="$(echo "${2:-}" | or " +")"
|
||||
|
||||
if [ -n "$n" ]; then
|
||||
awk -F "${separator:- }" "{print \$$n}"
|
||||
else
|
||||
cat
|
||||
fi
|
||||
}
|
||||
|
||||
str::last_paragraph_line() {
|
||||
awk '(!NF) { exit } { print $0 }' \
|
||||
| tail -n1
|
||||
}
|
||||
|
||||
str::first_word() {
|
||||
awk '{print $1}'
|
||||
}
|
||||
|
||||
str::index_last_occurrence() {
|
||||
local -r char="$1"
|
||||
|
||||
awk 'BEGIN{FS=""}{ for(i=1;i<=NF;i++){ if($i=="'"$char"'"){ p=i } }}END{ print p }'
|
||||
}
|
||||
|
||||
str::reverse_lines() {
|
||||
if command_exists tac; then
|
||||
tac
|
||||
elif command_exists perl; then
|
||||
perl -e 'print reverse <>'
|
||||
else
|
||||
awk '{a[i++]=$0} END {for (j=i-1; j>=0;) print a[j--] }'
|
||||
fi
|
||||
}
|
||||
|
||||
str::not_empty() {
|
||||
local -r input="$(cat)"
|
||||
|
||||
if [ -n $input ]; then
|
||||
echo "$input"
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
str::remove_empty_lines() {
|
||||
sed '/^$/d'
|
||||
}
|
||||
|
||||
str::last_line() {
|
||||
tail -n1
|
||||
}
|
||||
|
||||
str::as_column() {
|
||||
local -r txt="$(cat)"
|
||||
local -r separator="$1"
|
||||
|
||||
if command_exists column; then
|
||||
echo "$txt" | column -t -s "$separator"
|
||||
else
|
||||
echo "$txt" | awk -F "$separator" -vOFS=' ' 'NF > 0 { $1 = $1 } 1'
|
||||
fi
|
||||
}
|
||||
|
||||
str::with_line_numbers() {
|
||||
awk '{printf("%d %s\n", NR,$0)}'
|
||||
}
|
52
src/style.sh
52
src/style.sh
|
@ -1,52 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# TODO: move this elsewhere
|
||||
style::get_index() {
|
||||
local -r txt="$1"
|
||||
local -r ref="$2"
|
||||
|
||||
local -r i="$(echo "$txt" | grep "${ref}\$" | awk '{print $1}')"
|
||||
echo $((i - 1))
|
||||
}
|
||||
|
||||
# TODO: move this elsewhere
|
||||
style::with_nth() {
|
||||
grep -Eo 'with\-nth +([^ ]+)' | awk '{print $NF}'
|
||||
}
|
||||
|
||||
style::width() {
|
||||
local -r col="$1"
|
||||
local -r print="$(dict::get "$OPTIONS" print)"
|
||||
local -r widths="$(dict::get "$OPTIONS" col-widths | tr ',' $'\n')"
|
||||
local -r numbered_with_nth="$(dict::get "$OPTIONS" fzf-overrides | style::with_nth | tr ',' $'\n' | str::with_line_numbers)"
|
||||
|
||||
if [ -n "$numbered_with_nth" ]; then
|
||||
local -r index="$(style::get_index "$numbered_with_nth" $col 2>/dev/null)"
|
||||
echo "$widths" | coll::get $index 2>/dev/null || echo 0
|
||||
else
|
||||
echo 0
|
||||
fi
|
||||
}
|
||||
|
||||
style::color() {
|
||||
local -r col="$1"
|
||||
local -r print="$(dict::get "$OPTIONS" print)"
|
||||
local -r colors="$(dict::get "$OPTIONS" col-colors | tr ',' $'\n')"
|
||||
local -r numbered_with_nth="$(dict::get "$OPTIONS" fzf-overrides | style::with_nth | tr ',' $'\n' | str::with_line_numbers)"
|
||||
|
||||
if [ -n "$numbered_with_nth" ]; then
|
||||
local -r index="$(style::get_index "$numbered_with_nth" $col 2>/dev/null)"
|
||||
echo "$colors" | coll::get $index 2>/dev/null || echo 0
|
||||
else
|
||||
echo 30
|
||||
fi
|
||||
}
|
||||
|
||||
style::comment_width() { style::width 1 "$@"; }
|
||||
style::snippet_width() { style::width 2 "$@"; }
|
||||
style::tag_width() { style::width 3 "$@"; }
|
||||
|
||||
style::comment_color() { style::color 1 "$@"; }
|
||||
style::snippet_color() { style::color 2 "$@"; }
|
||||
style::tag_color() { style::color 3 "$@"; }
|
||||
|
98
src/ui.sh
98
src/ui.sh
|
@ -1,98 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
ui::fzf() {
|
||||
local -r autoselect="$(dict::get "$OPTIONS" autoselect)"
|
||||
local -r with_nth="$(dict::get "$OPTIONS" with-nth)"
|
||||
local -r nth="$(dict::get "$OPTIONS" nth)"
|
||||
|
||||
local args
|
||||
args+=("--height")
|
||||
args+=("100%")
|
||||
if ${autoselect:-false}; then
|
||||
args+=("--select-1")
|
||||
fi
|
||||
args+=("--bind"); args+=("ctrl-j:down,ctrl-k:up")
|
||||
|
||||
local -r fzf_cmd="$([ $NAVI_ENV == "test" ] && echo "fzf_mock" || echo "fzf")"
|
||||
"$fzf_cmd" ${args[@]:-} --inline-info "$@"
|
||||
}
|
||||
|
||||
ui::select() {
|
||||
local -r cheats="$1"
|
||||
|
||||
[[ "${SHELL:-}" =~ 'fish' ]] || local -r sub='$'
|
||||
|
||||
local -r script_path="${NAVI_HOME}/navi"
|
||||
local -r preview_cmd="\"${script_path}\" preview ${sub:-}(echo \'{}\' | $(arg::serialize_code))"
|
||||
|
||||
local -r query="$(dict::get "$OPTIONS" query)"
|
||||
local -r entry_point="$(dict::get "$OPTIONS" entry_point)"
|
||||
local -r preview="$(dict::get "$OPTIONS" preview)"
|
||||
local -r best="$(dict::get "$OPTIONS" best)"
|
||||
local -r fzf_overrides="$(dict::get "$OPTIONS" fzf-overrides)"
|
||||
|
||||
local args=()
|
||||
args+=("-i")
|
||||
args+=("--ansi")
|
||||
if $preview; then
|
||||
args+=("--preview"); args+=("$preview_cmd")
|
||||
args+=("--preview-window"); args+=("up:2")
|
||||
fi
|
||||
if [[ -n "$query" ]] && $best; then
|
||||
args+=("--filter"); args+=("${query} ")
|
||||
elif [[ -n "$query" ]] && ! $best; then
|
||||
args+=("--query"); args+=("${query} ")
|
||||
fi
|
||||
if [ "$entry_point" = "search" ]; then
|
||||
args+=("--header"); args+=("Displaying online results. Please refer to 'navi --help' for details")
|
||||
fi
|
||||
args+=("--delimiter"); args+=('\s\s+')
|
||||
args+=("--expect"); args+=("ctrl-y")
|
||||
|
||||
export FZF_DEFAULT_OPTS="${FZF_DEFAULT_OPTS} ${fzf_overrides}"
|
||||
|
||||
local -r result="$(echo "$cheats" \
|
||||
| cheat::prettify \
|
||||
| str::as_column $(printf "$ESCAPE_CHAR_3") \
|
||||
| ui::fzf "${args[@]}")"
|
||||
|
||||
local -r key="$(echo "$result" | head -n1)"
|
||||
|
||||
echo "$result" \
|
||||
| ($best && head -n1 || tail -n +2) \
|
||||
| selection::dict "$cheats" "$key"
|
||||
}
|
||||
|
||||
ui::clear_previous_line() {
|
||||
tput cuu1 2>/dev/null && tput el || true
|
||||
}
|
||||
|
||||
ui::width() {
|
||||
shopt -s checkwinsize; (:;:) 2> /dev/null || true
|
||||
if command_exists tput; then
|
||||
tput cols
|
||||
else
|
||||
echo 130
|
||||
fi
|
||||
}
|
||||
|
||||
ui::remove_dep_order() {
|
||||
sed -E 's/^: [^;]+; //'
|
||||
}
|
||||
|
||||
ui::print_preview() {
|
||||
local -r selection="$1"
|
||||
|
||||
local -r comment="$(selection::comment "$selection" | cmd::unescape)"
|
||||
local -r snippet="$(selection::snippet "$selection" | cmd::unescape)"
|
||||
local -r tags="$(selection::tags "$selection" | cmd::unescape)"
|
||||
|
||||
local -r comment_color="$(style::comment_color)"
|
||||
local -r snippet_color="$(style::snippet_color)"
|
||||
local -r tag_color="$(style::tag_color)"
|
||||
|
||||
printf "\033[${comment_color}m# "; echo -n "$comment"
|
||||
printf " \033[${tag_color}m["; echo -n "$tags"; echo "]"
|
||||
printf "\033[${snippet_color}m"
|
||||
echo "$snippet" | ui::remove_dep_order
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
version::code() {
|
||||
cd "$NAVI_HOME"
|
||||
local -r git_info=$(git log -n 1 --pretty=format:'%h%n%ad%n%s' --date=format:'%Y-%m-%d %Hh%M')
|
||||
if [ -z "$git_info" ]; then
|
||||
return 1
|
||||
else
|
||||
echo -e "$git_info"
|
||||
fi
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
interpolation_one_word() {
|
||||
echo "curl http://mysite.com/<user>/profile" \
|
||||
| arg::interpolate "user" "john" \
|
||||
| test::equals "curl http://mysite.com/john/profile"
|
||||
}
|
||||
|
||||
interpolation_multiple_words() {
|
||||
echo "cp <file> <new_file>" \
|
||||
| arg::interpolate "file" "C:/Program Files/app/foo.exe" \
|
||||
| arg::interpolate "new_file" "/mnt/c/Users/john/foo.exe" \
|
||||
| test::equals 'cp "C:/Program Files/app/foo.exe" /mnt/c/Users/john/foo.exe'
|
||||
}
|
||||
|
||||
test::set_suite "arg"
|
||||
test::run "if there's only one word, interpolation doesn't include quotes" interpolation_one_word
|
||||
test::run "if there are multiple words, interpolation includes quotes" interpolation_multiple_words
|
|
@ -1,8 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
assert_docker_cheat() {
|
||||
cheat::find | test::contains "docker.cheat"
|
||||
}
|
||||
|
||||
test::set_suite "cheat"
|
||||
test::run "We can find at least one known cheatsheet" assert_docker_cheat
|
|
@ -1,109 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
test::coll_equals() {
|
||||
local -r actual="$(cat)"
|
||||
local -r expected="$(coll::new "$@")"
|
||||
|
||||
echo "$actual" | test::equals "$expected"
|
||||
}
|
||||
|
||||
inc() {
|
||||
local -r x="$1"
|
||||
echo $((x+1))
|
||||
}
|
||||
|
||||
sum() {
|
||||
local -r x="$1"
|
||||
local -r y="$2"
|
||||
echo $((x*y))
|
||||
}
|
||||
|
||||
powers() {
|
||||
local x="$1"
|
||||
coll::new $((x*10)) $((x*100))
|
||||
}
|
||||
|
||||
odd() {
|
||||
local x="$1"
|
||||
[ $((x%2)) -eq 1 ]
|
||||
}
|
||||
|
||||
coll_map() {
|
||||
coll::new 1 2 3 \
|
||||
| coll::map inc \
|
||||
| test::coll_equals 2 3 4
|
||||
}
|
||||
|
||||
coll_flatmap() {
|
||||
coll::new 1 2 3 \
|
||||
| coll::map powers \
|
||||
| test::coll_equals 10 100 20 200 30 300
|
||||
}
|
||||
|
||||
coll_reduce() {
|
||||
coll::new 1 2 3 \
|
||||
| coll::reduce sum 10 \
|
||||
| test::equals 60
|
||||
}
|
||||
|
||||
coll_filter() {
|
||||
coll::new 1 2 3 4 5 \
|
||||
| coll::filter odd \
|
||||
| test::coll_equals 1 3 5
|
||||
}
|
||||
|
||||
coll_remove() {
|
||||
coll::new 1 2 3 4 5 \
|
||||
| coll::remove odd \
|
||||
| test::coll_equals 2 4
|
||||
}
|
||||
|
||||
coll_first() {
|
||||
coll::new 1 2 3 \
|
||||
| coll::first \
|
||||
| test::coll_equals 1
|
||||
}
|
||||
|
||||
coll_rest() {
|
||||
coll::new 1 2 3 \
|
||||
| coll::rest \
|
||||
| test::coll_equals 2 3
|
||||
}
|
||||
|
||||
coll_add() {
|
||||
coll::new 1 2 3 \
|
||||
| coll::add 4 5 \
|
||||
| coll::add 6 7 \
|
||||
| test::coll_equals 1 2 3 4 5 6 7
|
||||
}
|
||||
|
||||
coll_concat() {
|
||||
coll::new 1 2 3 \
|
||||
| coll::add "$(coll::new 4 5)" \
|
||||
| test::coll_equals 1 2 3 4 5
|
||||
}
|
||||
|
||||
coll_reverse() {
|
||||
coll::new 1 2 3 \
|
||||
| coll::reverse \
|
||||
| test::coll_equals 3 2 1
|
||||
}
|
||||
|
||||
coll_set() {
|
||||
coll::new 1 2 3 2 4 2 \
|
||||
| coll::set \
|
||||
| test::coll_equals 1 2 3 4
|
||||
}
|
||||
|
||||
test::set_suite "coll"
|
||||
test::run "map" coll_map
|
||||
test::run "filter" coll_filter
|
||||
test::run "remove" coll_remove
|
||||
test::run "first" coll_first
|
||||
test::run "rest" coll_rest
|
||||
test::run "add" coll_add
|
||||
test::run "add can be used as concat" coll_concat
|
||||
test::run "reduce" coll_reduce
|
||||
test::run "we can use map as flatmap" coll_flatmap
|
||||
test::run "reverse" coll_reverse
|
||||
test::run "set" coll_set
|
78
test/core.sh
78
test/core.sh
|
@ -1,78 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
export NAVI_FORCE_GNU=true
|
||||
|
||||
source "${NAVI_HOME}/src/main.sh"
|
||||
source "${NAVI_HOME}/test/log.sh"
|
||||
|
||||
opts::eval "$@"
|
||||
TEST_DIR="${NAVI_HOME}/test"
|
||||
|
||||
PASSED=0
|
||||
FAILED=0
|
||||
SKIPPED=0
|
||||
SUITE=""
|
||||
|
||||
test::set_suite() {
|
||||
SUITE="$*"
|
||||
}
|
||||
|
||||
test::success() {
|
||||
PASSED=$((PASSED+1))
|
||||
log::success "Test passed!"
|
||||
}
|
||||
|
||||
test::fail() {
|
||||
FAILED=$((FAILED+1))
|
||||
log::error "Test failed..."
|
||||
return
|
||||
}
|
||||
|
||||
test::skip() {
|
||||
echo
|
||||
log::note "${SUITE:-unknown} - ${1:-unknown}"
|
||||
SKIPPED=$((SKIPPED+1))
|
||||
log::warning "Test skipped..."
|
||||
return
|
||||
}
|
||||
|
||||
test::run() {
|
||||
echo
|
||||
log::note "${SUITE:-unknown} - ${1:-unknown}"
|
||||
shift
|
||||
eval "$*" && test::success || test::fail
|
||||
}
|
||||
|
||||
test::equals() {
|
||||
local -r actual="$(cat)"
|
||||
local -r expected="$(echo "${1:-}")"
|
||||
|
||||
if [[ "$actual" != "$expected" ]]; then
|
||||
log::error "Expected '${expected}' but got '${actual}'"
|
||||
return 2
|
||||
fi
|
||||
}
|
||||
|
||||
test::contains() {
|
||||
local -r actual="$(cat)"
|
||||
local -r regex="$(echo "${1:-}")"
|
||||
|
||||
if ! echo "$actual" | grep -qE "$regex"; then
|
||||
log::error "Expected to contain '${regex}' but got '${actual}'"
|
||||
return 2
|
||||
fi
|
||||
}
|
||||
|
||||
test::finish() {
|
||||
echo
|
||||
if [ $SKIPPED -gt 0 ]; then
|
||||
log::warning "${SKIPPED} tests skipped!"
|
||||
fi
|
||||
if [ $FAILED -gt 0 ]; then
|
||||
log::error "${PASSED} tests passed but ${FAILED} failed... :("
|
||||
exit "${FAILED}"
|
||||
else
|
||||
log::success "All ${PASSED} tests passed! :)"
|
||||
exit 0
|
||||
fi
|
||||
}
|
|
@ -1,133 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
inc() {
|
||||
local -r x="$1"
|
||||
echo $((x+1))
|
||||
}
|
||||
|
||||
test::map_equals() {
|
||||
local -r actual="$(cat | dict::unescape_value | sort)"
|
||||
local -r expected="$(dict::new "$@" | dict::unescape_value | sort)"
|
||||
|
||||
echo "$actual" | test::equals "$expected"
|
||||
}
|
||||
|
||||
dict_assoc() {
|
||||
dict::new \
|
||||
| dict::assoc "foo" "42" \
|
||||
| tr -d "$ESCAPE_CHAR" \
|
||||
| tr -d "$ESCAPE_CHAR_2" \
|
||||
| tr -d "$ESCAPE_CHAR_3" \
|
||||
| test::equals "foo: 42"
|
||||
}
|
||||
|
||||
dict_assoc_perc() {
|
||||
dict::new \
|
||||
| dict::assoc "foo" "42 %" bar "% 7" \
|
||||
| dict::get bar \
|
||||
| test::equals "% 7"
|
||||
}
|
||||
|
||||
dict_assoc_multiple() {
|
||||
dict::new \
|
||||
| dict::assoc "foo" "42" "bar" "5" \
|
||||
| test::map_equals "bar" 5 "foo" 42
|
||||
}
|
||||
|
||||
dict_dissoc() {
|
||||
dict::new \
|
||||
| dict::assoc "foo" "42" "bar" "5" \
|
||||
| dict::dissoc "bar" \
|
||||
| test::map_equals "foo" 42
|
||||
}
|
||||
|
||||
dict_assoc_again() {
|
||||
dict::new \
|
||||
| dict::assoc "foo" "42" \
|
||||
| dict::assoc "foo" "42" \
|
||||
| test::map_equals "foo" 42
|
||||
}
|
||||
|
||||
dict_dissoc_nested() {
|
||||
dict::new \
|
||||
| dict::assoc "foo" "42" "bar.a" 5 "bar.b" 6 "baz" 63 \
|
||||
| dict::dissoc "bar" \
|
||||
| test::map_equals "baz" 63 "foo" 42
|
||||
}
|
||||
|
||||
dict_assoc_nested() {
|
||||
dict::new \
|
||||
| dict::assoc "foo" "42" "bar.a" 5 "bar.c" 7 "baz" 63 \
|
||||
| dict::assoc "bar.b" 6 \
|
||||
| dict::get "bar.b" \
|
||||
| test::equals "asdfsadf"
|
||||
}
|
||||
|
||||
dict_get() {
|
||||
dict::new \
|
||||
| dict::assoc "foo" "42" \
|
||||
| dict::get "foo" \
|
||||
| test::equals "42"
|
||||
}
|
||||
|
||||
dict_get_nested() {
|
||||
dict::new \
|
||||
| dict::assoc "foo" "42" "bar.a" 5 "bar.b" 6 "baz" 63 \
|
||||
| dict::get "bar.a" \
|
||||
| test::equals "5"
|
||||
}
|
||||
|
||||
dict_get_dict() {
|
||||
dict::new \
|
||||
| dict::assoc "foo" "42" "bar.a" 5 "bar.b" 6 "baz" 63 \
|
||||
| dict::get "bar" \
|
||||
| test::map_equals "bar.a" 5 "bar.b" 6
|
||||
}
|
||||
|
||||
dict_get_keys() {
|
||||
dict::new \
|
||||
| dict::assoc "foo" "42" "bar.a" 5 "bar.b" 6 "baz" 63 \
|
||||
| dict::keys \
|
||||
| test::equals "$(echo -e "foo\nbar.a\nbar.b\nbaz")"
|
||||
}
|
||||
|
||||
dict_get_values() {
|
||||
dict::new \
|
||||
| dict::assoc "foo" "42" "bar.a" 5 "bar.b" 6 "baz" 63 \
|
||||
| dict::values \
|
||||
| test::equals "$(echo -e "5\n6\n42\n63")"
|
||||
}
|
||||
|
||||
dict_zipmap() {
|
||||
dict::zipmap "key1\nkey2\nkey3" "value1\nvalue2\nvalue3" \
|
||||
| test::map_equals "key1" "value1" "key2" "value2" "key3" "value3"
|
||||
}
|
||||
|
||||
dict_update() {
|
||||
dict::new "foo" 42 "bar" 5 \
|
||||
| dict::update "bar" inc \
|
||||
| test::map_equals "foo" 42 "bar" 6
|
||||
}
|
||||
|
||||
dict_merge() {
|
||||
dict::new "foo" 42 "bar" 5 \
|
||||
| dict::merge "$(dict::new "bar" 7 "lorem" "ipsum")" \
|
||||
| test::map_equals "foo" 42
|
||||
}
|
||||
|
||||
test::set_suite "dict"
|
||||
test::run "We can assoc a value" dict_assoc
|
||||
test::skip "We can merge dicts" dict_merge
|
||||
test::run "We can assoc values with %" dict_assoc_perc
|
||||
test::run "We can assoc multiple values" dict_assoc_multiple
|
||||
test::skip "We can assoc a nested value" dict_assoc_nested
|
||||
test::run "We can dissoc a value" dict_dissoc
|
||||
test::run "Associng the same value is a no-op" dict_assoc_again
|
||||
test::run "Dissocing a key will replace all its subvalues" dict_dissoc_nested
|
||||
test::run "We can get a value" dict_get
|
||||
test::run "We can get a nested value" dict_get_nested
|
||||
test::run "We can get a dictionary" dict_get_dict
|
||||
test::run "We can get all keys" dict_get_keys
|
||||
test::skip "We can get all values" dict_get_values
|
||||
test::skip "We can get create a dict from a zipmap" dict_zipmap
|
||||
test::skip "We can update a value" dict_update
|
|
@ -1,57 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
NAVI_BIN="${NAVI_HOME}/navi"
|
||||
TEST_DIR="${NAVI_HOME}/test"
|
||||
|
||||
_navi() {
|
||||
"$NAVI_BIN" "$@"
|
||||
}
|
||||
|
||||
fzf_mock() {
|
||||
head -n1 | sed 's/\x1b\[[0-9;]*m//g'
|
||||
}
|
||||
|
||||
assert_version() {
|
||||
local -r version="$(cat "$NAVI_BIN" | grep VERSION | cut -d'=' -f2 | tr -d '"')"
|
||||
|
||||
_navi --version \
|
||||
| test::equals "$version"
|
||||
}
|
||||
|
||||
assert_help() {
|
||||
_navi --help \
|
||||
| grep -q 'Options:'
|
||||
}
|
||||
|
||||
assert_home() {
|
||||
_navi home \
|
||||
| grep -q '/'
|
||||
}
|
||||
|
||||
assert_best() {
|
||||
_navi best constant --path "$TEST_DIR" \
|
||||
| test::equals 42
|
||||
}
|
||||
|
||||
assert_query() {
|
||||
NAVI_ENV="test" _navi --path "$TEST_DIR" \
|
||||
| test::equals "2 12"
|
||||
}
|
||||
|
||||
export HAS_FZF="$(command_exists fzf && echo true || echo false)"
|
||||
|
||||
test::fzf() {
|
||||
if $HAS_FZF; then
|
||||
test::run "$@"
|
||||
else
|
||||
test::skip "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
test::set_suite "integration"
|
||||
export -f fzf_mock
|
||||
test::run "version" assert_version
|
||||
test::run "help" assert_help
|
||||
test::run "home" assert_home
|
||||
test::fzf "best" assert_best # FZF setup needed in CircleCI
|
||||
test::fzf "query" assert_query # FZF setup needed in CircleCI
|
102
test/log.sh
102
test/log.sh
|
@ -1,102 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
_export_colors() {
|
||||
if ! ${DOT_COLORS_EXPORTED:-false}; then
|
||||
if [ -z ${TERM:-} ] || [ $TERM = "dumb" ]; then
|
||||
bold=""
|
||||
underline=""
|
||||
freset=""
|
||||
purple=""
|
||||
red=""
|
||||
green=""
|
||||
tan=""
|
||||
blue=""
|
||||
else
|
||||
bold=$(tput bold)
|
||||
underline=$(tput sgr 0 1)
|
||||
freset=$(tput sgr0)
|
||||
purple=$(tput setaf 171)
|
||||
red=$(tput setaf 1)
|
||||
green=$(tput setaf 76)
|
||||
tan=$(tput setaf 3)
|
||||
blue=$(tput setaf 38)
|
||||
fi
|
||||
|
||||
log_black=30
|
||||
log_red=31
|
||||
log_green=32
|
||||
log_yellow=33
|
||||
log_blue=34
|
||||
log_purple=35
|
||||
log_cyan=36
|
||||
log_white=37
|
||||
|
||||
log_regular=0
|
||||
log_bold=1
|
||||
log_underline=4
|
||||
|
||||
readonly DOT_COLORS_EXPORTED=true
|
||||
fi
|
||||
}
|
||||
|
||||
log::color() {
|
||||
_export_colors
|
||||
local bg=false
|
||||
case "$@" in
|
||||
*reset*) echo "\e[0m"; exit 0 ;;
|
||||
*black*) color=$log_black ;;
|
||||
*red*) color=$log_red ;;
|
||||
*green*) color=$log_green ;;
|
||||
*yellow*) color=$log_yellow ;;
|
||||
*blue*) color=$log_blue ;;
|
||||
*purple*) color=$log_purple ;;
|
||||
*cyan*) color=$log_cyan ;;
|
||||
*white*) color=$log_white ;;
|
||||
esac
|
||||
case "$@" in
|
||||
*regular*) mod=$log_regular ;;
|
||||
*bold*) mod=$log_bold ;;
|
||||
*underline*) mod=$log_underline ;;
|
||||
esac
|
||||
case "$@" in
|
||||
*background*) bg=true ;;
|
||||
*bg*) bg=true ;;
|
||||
esac
|
||||
|
||||
if $bg; then
|
||||
echo "\e[${color}m"
|
||||
else
|
||||
echo "\e[${mod:-$log_regular};${color}m"
|
||||
fi
|
||||
}
|
||||
|
||||
if [ -z ${LOG_FILE+x} ]; then
|
||||
readonly LOG_FILE="/tmp/$(basename "$0").log"
|
||||
fi
|
||||
|
||||
_log() {
|
||||
local template=$1
|
||||
shift
|
||||
if ${log_to_file:-false}; then
|
||||
echo -e $(printf "$template" "$@") | tee -a "$LOG_FILE" >&2
|
||||
else
|
||||
echo -e $(printf "$template" "$@")
|
||||
fi
|
||||
}
|
||||
|
||||
_header() {
|
||||
local TOTAL_CHARS=60
|
||||
local total=$TOTAL_CHARS-2
|
||||
local size=${#1}
|
||||
local left=$((($total - $size) / 2))
|
||||
local right=$(($total - $size - $left))
|
||||
printf "%${left}s" '' | tr ' ' =
|
||||
printf " $1 "
|
||||
printf "%${right}s" '' | tr ' ' =
|
||||
}
|
||||
|
||||
log::header() { _export_colors && _log "\n${bold}${purple}$(_header "$1")${freset}\n"; }
|
||||
log::success() { _export_colors && _log "${green}✔ %s${freset}\n" "$@"; }
|
||||
log::error() { _export_colors && _log "${red}✖ %s${freset}\n" "$@"; }
|
||||
log::warning() { _export_colors && _log "${tan}➜ %s${freset}\n" "$@"; }
|
||||
log::note() { _export_colors && _log "${blue}➜ %s${freset}\n" "$@"; }
|
|
@ -1,9 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
existing() {
|
||||
platform::existing_command oasida fngo ni awk aoisdn oafm \
|
||||
| test::equals awk
|
||||
}
|
||||
|
||||
test::set_suite "platform"
|
||||
test::run "existing_command" existing
|
|
@ -1,16 +0,0 @@
|
|||
% test, playground
|
||||
|
||||
# this should be the first test. single and double quotes + newlines
|
||||
echo <x> <y>
|
||||
|
||||
# variable names
|
||||
echo <x> <foo> <foo_bar> <lorem-ipsum> <dolor sit>
|
||||
|
||||
# use %
|
||||
coll::new 1 2 3 | xargs -I% echo "hello %"
|
||||
|
||||
# return a constant number
|
||||
echo 42
|
||||
|
||||
$ x: echo -e '2\n3\n4'
|
||||
$ y: echo -e "$((x+10))\n$((x+20))"
|
15
test/run
15
test/run
|
@ -1,15 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
export NAVI_HOME="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
source "${NAVI_HOME}/test/core.sh"
|
||||
|
||||
tests="$(find "$NAVI_HOME/test" -iname "${1:-}*_test.sh")"
|
||||
|
||||
NAVI_PATH="${NAVI_HOME}/cheats"
|
||||
|
||||
for test in $tests; do
|
||||
source "$test"
|
||||
done
|
||||
|
||||
test::finish
|
|
@ -1,20 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
check_all_vars() {
|
||||
local arg
|
||||
IFS=$'\n'
|
||||
for var in $(cat "$1" | grep -Eo "<[^>]*>"); do
|
||||
if ! echo "$var" | grep -qE "$ARG_REGEX"; then
|
||||
echoerr "$var isn't a valid variable name!"
|
||||
return 1
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
path="$NAVI_PATH"
|
||||
NAVI_PATH="${NAVI_PATH}:${TEST_DIR}"
|
||||
for cheat in $(cheat::find); do
|
||||
test::run "All variables in $(basename $cheat) are valid" \
|
||||
'check_all_vars "$cheat"'
|
||||
done
|
||||
NAVI_PATH="$path"
|
7
tests/tests.rs
Normal file
7
tests/tests.rs
Normal file
|
@ -0,0 +1,7 @@
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
fn it_works() {
|
||||
assert_eq!(2 + 2, 4);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue