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:
|
install:
|
||||||
scripts/install
|
scripts/install
|
||||||
|
|
||||||
|
install:
|
||||||
|
scripts/make install
|
||||||
|
|
||||||
uninstall:
|
uninstall:
|
||||||
scripts/uninstall
|
scripts/make uninstall
|
||||||
|
|
||||||
release:
|
|
||||||
scripts/release
|
|
||||||
|
|
||||||
update:
|
|
||||||
scripts/update
|
|
||||||
|
|
||||||
lint:
|
|
||||||
scripts/lint
|
|
|
@ -1,4 +1,6 @@
|
||||||
# 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)
|
# 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:
|
> :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:
|
||||||
|
|
||||||
|
|
|
@ -24,4 +24,7 @@ $ device: adb devices --- --headers 1 --column 1
|
||||||
# Start emulator
|
# Start emulator
|
||||||
"$ANDROID_HOME/tools/emulator" -avd <emulator> -netdelay none -netspeed full
|
"$ANDROID_HOME/tools/emulator" -avd <emulator> -netdelay none -netspeed full
|
||||||
|
|
||||||
|
# TODO: remove this
|
||||||
|
echo "$(ls -la)"
|
||||||
|
|
||||||
$ emulator: "$ANDROID_HOME/tools/emulator" -list-avds
|
$ emulator: "$ANDROID_HOME/tools/emulator" -list-avds
|
||||||
|
|
|
@ -15,12 +15,13 @@ netstat -tn 2>/dev/null \
|
||||||
| sort -nr \
|
| sort -nr \
|
||||||
| head
|
| head
|
||||||
|
|
||||||
# Find external, public IP address
|
|
||||||
dig +short myip.opendns.com @resolver1.opendns.com
|
|
||||||
|
|
||||||
# Find primary, local IP address
|
# Find primary, local IP address
|
||||||
ifconfig \
|
ifconfig \
|
||||||
| grep -Eo 'inet (addr:)?([0-9]*\.){3}[0-9]*' \
|
| grep -Eo 'inet (addr:)?([0-9]*\.){3}[0-9]*' \
|
||||||
| grep -Eo '([0-9]*\.){3}[0-9]*' \
|
| grep -Eo '([0-9]*\.){3}[0-9]*' \
|
||||||
| grep -v '127.0.0.1' \
|
| grep -v '127.0.0.1' \
|
||||||
| tail -n1
|
| 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
|
set -euo pipefail
|
||||||
|
|
||||||
export NAVI_HOME="$(cd "$(dirname "$0")" && pwd)"
|
export NAVI_HOME="$(cd "$(dirname "$0")" && pwd)"
|
||||||
source "${NAVI_HOME}/src/main.sh"
|
cd "$NAVI_HOME"
|
||||||
|
|
||||||
VERSION="0.18.3"
|
release_bin="${NAVI_HOME}/target/release/navi"
|
||||||
NAVI_ENV="${NAVI_ENV:-prod}"
|
debug_bin="${NAVI_HOME}/target/debug/navi"
|
||||||
|
|
||||||
opts::eval "$@"
|
if [ -f "$release_bin" ]; then
|
||||||
main "$@"
|
"$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
|
#!/usr/bin/env bash
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
debian="${1:-false}"
|
export NAVI_HOME="$(cd "$(dirname "$0")/.." && pwd)"
|
||||||
|
|
||||||
if $debian; then
|
cd "$NAVI_HOME"
|
||||||
image="debian"
|
|
||||||
setup_cmd="apt update; apt install -y git curl;"
|
|
||||||
else
|
|
||||||
image="ellerbrock/alpine-bash-git"
|
|
||||||
fi
|
|
||||||
|
|
||||||
docker run \
|
docker run \
|
||||||
-it \
|
-e HOMEBREW_NO_AUTO_UPDATE=1 \
|
||||||
--entrypoint /bin/bash \
|
-it linuxbrew/alpine \
|
||||||
-v "$(pwd):/navi" \
|
bash -c 'brew install denisidoro/tools/navirs; bash'
|
||||||
"$image" \
|
|
||||||
-c "${setup_cmd:-}git clone --depth 1 https://github.com/junegunn/fzf.git ~/.fzf && yes | ~/.fzf/install; export PATH=$PATH:/navi; 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
|
#!/usr/bin/env bash
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
export NAVI_HOME="$(cd "$(dirname "$0")/.." && pwd)"
|
FIRST_INSTALL_DIR="/opt/navi"
|
||||||
|
SECOND_INSTALL_DIR="${HOME}/.navi"
|
||||||
|
|
||||||
script() {
|
get_install_dir() {
|
||||||
echo "#!/usr/bin/env bash"
|
local -r useless_folder="${FIRST_INSTALL_DIR}/useless"
|
||||||
echo "${NAVI_HOME}/navi" '"$@"'
|
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}"
|
echoerr() {
|
||||||
bin="${folder}/navi"
|
echo "$@" 1>&2
|
||||||
|
}
|
||||||
|
|
||||||
script > "$bin"
|
command_exists() {
|
||||||
chmod +x "$bin"
|
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