mirror of
https://github.com/denisidoro/navi
synced 2024-11-22 03:23:05 +00:00
Fix --multi with --column + refactors (#290)
This commit is contained in:
parent
84e28e7885
commit
2ca1d8fc85
15 changed files with 386 additions and 82 deletions
12
.github/workflows/ci.yml
vendored
12
.github/workflows/ci.yml
vendored
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
on: [push]
|
on: [push]
|
||||||
|
|
||||||
name: Tests
|
name: CI
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
check:
|
check:
|
||||||
|
@ -30,7 +30,7 @@ jobs:
|
||||||
command: check
|
command: check
|
||||||
|
|
||||||
test:
|
test:
|
||||||
name: Test Suite
|
name: Tests
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout sources
|
- name: Checkout sources
|
||||||
|
@ -49,6 +49,12 @@ jobs:
|
||||||
with:
|
with:
|
||||||
command: test
|
command: test
|
||||||
|
|
||||||
|
- name: Install fzf
|
||||||
|
run: git clone --depth 1 https://github.com/junegunn/fzf.git ~/.fzf; yes | ~/.fzf/install;
|
||||||
|
|
||||||
|
- name: Run bash tests
|
||||||
|
run: ./tests/run 'trivial\|multiple'
|
||||||
|
|
||||||
lints:
|
lints:
|
||||||
name: Lints
|
name: Lints
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
@ -76,4 +82,4 @@ jobs:
|
||||||
continue-on-error: false
|
continue-on-error: false
|
||||||
with:
|
with:
|
||||||
command: clippy
|
command: clippy
|
||||||
args: -- -D warnings
|
args: -- -D warnings
|
||||||
|
|
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -245,7 +245,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "navi"
|
name = "navi"
|
||||||
version = "2.1.4"
|
version = "2.2.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"dirs 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"dirs 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"git2 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"git2 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
|
12
Cargo.toml
12
Cargo.toml
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "navi"
|
name = "navi"
|
||||||
version = "2.1.4"
|
version = "2.2.0"
|
||||||
authors = ["Denis Isidoro <denis_isidoro@live.com>"]
|
authors = ["Denis Isidoro <denis_isidoro@live.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
description = "An interactive cheatsheet tool for the command-line"
|
description = "An interactive cheatsheet tool for the command-line"
|
||||||
|
@ -34,7 +34,11 @@ version = "0.10.0"
|
||||||
default-features = false
|
default-features = false
|
||||||
features = ["vendored-openssl"]
|
features = ["vendored-openssl"]
|
||||||
|
|
||||||
[[bin]]
|
[lib]
|
||||||
bench = false
|
|
||||||
path = "src/main.rs"
|
|
||||||
name = "navi"
|
name = "navi"
|
||||||
|
path = "src/lib.rs"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "navi"
|
||||||
|
path = "src/bin/main.rs"
|
||||||
|
bench = false
|
||||||
|
|
10
README.md
10
README.md
|
@ -39,6 +39,7 @@ Table of contents
|
||||||
* [Advanced variable options](#advanced-variable-options)
|
* [Advanced variable options](#advanced-variable-options)
|
||||||
* [Variable dependency](#variable-dependency)
|
* [Variable dependency](#variable-dependency)
|
||||||
* [Multiline snippets](#multiline-snippets)
|
* [Multiline snippets](#multiline-snippets)
|
||||||
|
* [Variable as multiple arguments](#variable-as-multiple-arguments)
|
||||||
* [List customization](#list-customization)
|
* [List customization](#list-customization)
|
||||||
* [Related projects](#related-projects)
|
* [Related projects](#related-projects)
|
||||||
* [Etymology](#etymology)
|
* [Etymology](#etymology)
|
||||||
|
@ -227,6 +228,15 @@ true \
|
||||||
|| echo no
|
|| echo no
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Variable as multiple arguments
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# This will result into: cat "file1.json" "file2.json"
|
||||||
|
jsons=($(echo "<jsons>"))
|
||||||
|
cat "${jsons[@]}"
|
||||||
|
|
||||||
|
$ jsons: find . -iname '*.rs' -type f -print --- --multi
|
||||||
|
```
|
||||||
|
|
||||||
List customization
|
List customization
|
||||||
------------------
|
------------------
|
||||||
|
|
|
@ -17,6 +17,8 @@ cargo fmt || true
|
||||||
|
|
||||||
header "dot code beautify..."
|
header "dot code beautify..."
|
||||||
find scripts -type f | xargs -I% dot code beautify % || true
|
find scripts -type f | xargs -I% dot code beautify % || true
|
||||||
|
dot code beautify "${NAVI_HOME}/tests/core.bash" || true
|
||||||
|
dot code beautify "${NAVI_HOME}/tests/run" || true
|
||||||
|
|
||||||
header "clippy..."
|
header "clippy..."
|
||||||
cargo clippy || true
|
cargo clippy || true
|
||||||
|
|
160
scripts/install
160
scripts/install
|
@ -1,6 +1,11 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
|
|
||||||
|
# =====================
|
||||||
|
# paths
|
||||||
|
# =====================
|
||||||
|
|
||||||
FIRST_SOURCE_DIR="/opt/navi"
|
FIRST_SOURCE_DIR="/opt/navi"
|
||||||
SECOND_SOURCE_DIR="${HOME}/.navi"
|
SECOND_SOURCE_DIR="${HOME}/.navi"
|
||||||
|
|
||||||
|
@ -27,14 +32,106 @@ get_bin_dir() {
|
||||||
get_dir "$FIRST_BIN_DIR" "$SECOND_BIN_DIR"
|
get_dir "$FIRST_BIN_DIR" "$SECOND_BIN_DIR"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# =====================
|
||||||
|
# logging
|
||||||
|
# =====================
|
||||||
|
|
||||||
echoerr() {
|
echoerr() {
|
||||||
echo "$@" 1>&2
|
echo "$@" 1>&2
|
||||||
}
|
}
|
||||||
|
|
||||||
command_exists() {
|
tap() {
|
||||||
type "$1" &>/dev/null
|
local -r x="$(cat)"
|
||||||
|
echoerr "$x"
|
||||||
|
echo "$x"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log::ansi() {
|
||||||
|
local bg=false
|
||||||
|
case "$@" in
|
||||||
|
*reset*) echo "\e[0m"; return 0 ;;
|
||||||
|
*black*) color=30 ;;
|
||||||
|
*red*) color=31 ;;
|
||||||
|
*green*) color=32 ;;
|
||||||
|
*yellow*) color=33 ;;
|
||||||
|
*blue*) color=34 ;;
|
||||||
|
*purple*) color=35 ;;
|
||||||
|
*cyan*) color=36 ;;
|
||||||
|
*white*) color=37 ;;
|
||||||
|
esac
|
||||||
|
case "$@" in
|
||||||
|
*regular*) mod=0 ;;
|
||||||
|
*bold*) mod=1 ;;
|
||||||
|
*underline*) mod=4 ;;
|
||||||
|
esac
|
||||||
|
case "$@" in
|
||||||
|
*background*) bg=true ;;
|
||||||
|
*bg*) bg=true ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if $bg; then
|
||||||
|
echo "\e[${color}m"
|
||||||
|
else
|
||||||
|
echo "\e[${mod:-0};${color}m"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
_log() {
|
||||||
|
local template="$1"
|
||||||
|
shift
|
||||||
|
echoerr -e "$(printf "$template" "$@")"
|
||||||
|
}
|
||||||
|
|
||||||
|
_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() { _log "\n$(log::ansi bold)$(log::ansi purple)$(_header "$1")$(log::ansi reset)\n"; }
|
||||||
|
log::success() { _log "$(log::ansi green)✔ %s$(log::ansi reset)\n" "$@"; }
|
||||||
|
log::error() { _log "$(log::ansi red)✖ %s$(log::ansi reset)\n" "$@"; }
|
||||||
|
log::warning() { _log "$(log::ansi yellow)➜ %s$(log::ansi reset)\n" "$@"; }
|
||||||
|
log::note() { _log "$(log::ansi blue)%s$(log::ansi reset)\n" "$@"; }
|
||||||
|
|
||||||
|
# TODO: remove
|
||||||
|
header() {
|
||||||
|
echoerr "$*"
|
||||||
|
echoerr
|
||||||
|
}
|
||||||
|
|
||||||
|
die() {
|
||||||
|
log::error "$@"
|
||||||
|
exit 42
|
||||||
|
}
|
||||||
|
|
||||||
|
no_binary_warning() {
|
||||||
|
echoerr "There's no precompiled binary for your platform: $(uname -a)"
|
||||||
|
}
|
||||||
|
|
||||||
|
installation_finish_instructions() {
|
||||||
|
local -r shell="$(get_shell)"
|
||||||
|
echoerr -e "Finished. To call navi, restart your shell or reload the config file:\n source ~/.${shell}rc"
|
||||||
|
local code
|
||||||
|
if [[ $shell = "zsh" ]]; then
|
||||||
|
code="navi widget ${shell} | source"
|
||||||
|
else
|
||||||
|
code='source <(navi widget '"$shell"')'
|
||||||
|
fi
|
||||||
|
echoerr -e "\nTo add the Ctrl-G keybinding, add the following to ~/.${shell}rc:\n ${code}"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# =====================
|
||||||
|
# security
|
||||||
|
# =====================
|
||||||
|
|
||||||
sha256() {
|
sha256() {
|
||||||
if command_exists sha256sum; then
|
if command_exists sha256sum; then
|
||||||
sha256sum
|
sha256sum
|
||||||
|
@ -48,10 +145,10 @@ sha256() {
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
header() {
|
|
||||||
echoerr "$*"
|
# =====================
|
||||||
echoerr
|
# github
|
||||||
}
|
# =====================
|
||||||
|
|
||||||
latest_version_released() {
|
latest_version_released() {
|
||||||
curl -s 'https://api.github.com/repos/denisidoro/navi/releases/latest' \
|
curl -s 'https://api.github.com/repos/denisidoro/navi/releases/latest' \
|
||||||
|
@ -59,15 +156,6 @@ latest_version_released() {
|
||||||
| sed 's|releases/tag/v||'
|
| sed 's|releases/tag/v||'
|
||||||
}
|
}
|
||||||
|
|
||||||
version_from_toml() {
|
|
||||||
cat "${NAVI_HOME}/Cargo.toml" \
|
|
||||||
| grep version \
|
|
||||||
| head -n1 \
|
|
||||||
| awk '{print $NF}' \
|
|
||||||
| tr -d '"' \
|
|
||||||
| tr -d "'"
|
|
||||||
}
|
|
||||||
|
|
||||||
asset_url() {
|
asset_url() {
|
||||||
local -r version="$1"
|
local -r version="$1"
|
||||||
local -r variant="${2:-}"
|
local -r variant="${2:-}"
|
||||||
|
@ -96,6 +184,29 @@ sha_for_asset_on_github() {
|
||||||
curl -sL "$url" | sha256 | awk '{print $1}'
|
curl -sL "$url" | sha256 | awk '{print $1}'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# =====================
|
||||||
|
# code
|
||||||
|
# =====================
|
||||||
|
|
||||||
|
version_from_toml() {
|
||||||
|
cat "${NAVI_HOME}/Cargo.toml" \
|
||||||
|
| grep version \
|
||||||
|
| head -n1 \
|
||||||
|
| awk '{print $NF}' \
|
||||||
|
| tr -d '"' \
|
||||||
|
| tr -d "'"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# =====================
|
||||||
|
# platform
|
||||||
|
# =====================
|
||||||
|
|
||||||
|
command_exists() {
|
||||||
|
type "$1" &>/dev/null
|
||||||
|
}
|
||||||
|
|
||||||
get_target() {
|
get_target() {
|
||||||
local -r unamea="$(uname -a)"
|
local -r unamea="$(uname -a)"
|
||||||
local -r archi="$(uname -sm)"
|
local -r archi="$(uname -sm)"
|
||||||
|
@ -114,25 +225,14 @@ get_target() {
|
||||||
echo "$target"
|
echo "$target"
|
||||||
}
|
}
|
||||||
|
|
||||||
no_binary_warning() {
|
|
||||||
echoerr "There's no precompiled binary for your platform: $(uname -a)"
|
|
||||||
}
|
|
||||||
|
|
||||||
get_shell() {
|
get_shell() {
|
||||||
echo $SHELL | xargs basename
|
echo $SHELL | xargs basename
|
||||||
}
|
}
|
||||||
|
|
||||||
installation_finish_instructions() {
|
|
||||||
local -r shell="$(get_shell)"
|
# =====================
|
||||||
echoerr -e "Finished. To call navi, restart your shell or reload the config file:\n source ~/.${shell}rc"
|
# main
|
||||||
local code
|
# =====================
|
||||||
if [[ $shell = "zsh" ]]; then
|
|
||||||
code="navi widget ${shell} | source"
|
|
||||||
else
|
|
||||||
code='source <(navi widget '"$shell"')'
|
|
||||||
fi
|
|
||||||
echoerr -e "\nTo add the Ctrl-G keybinding, add the following to ~/.${shell}rc:\n ${code}"
|
|
||||||
}
|
|
||||||
|
|
||||||
install_navi() {
|
install_navi() {
|
||||||
export SOURCE_DIR="${SOURCE_DIR:-"$(get_source_dir)"}"
|
export SOURCE_DIR="${SOURCE_DIR:-"$(get_source_dir)"}"
|
||||||
|
|
7
src/bin/main.rs
Normal file
7
src/bin/main.rs
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
extern crate navi;
|
||||||
|
|
||||||
|
use std::error::Error;
|
||||||
|
|
||||||
|
fn main() -> Result<(), Box<dyn Error>> {
|
||||||
|
navi::handle_config(navi::config_from_env())
|
||||||
|
}
|
31
src/fzf.rs
31
src/fzf.rs
|
@ -6,12 +6,22 @@ use std::process::{Command, Stdio};
|
||||||
|
|
||||||
fn get_column(text: String, column: Option<u8>, delimiter: Option<&str>) -> String {
|
fn get_column(text: String, column: Option<u8>, delimiter: Option<&str>) -> String {
|
||||||
if let Some(c) = column {
|
if let Some(c) = column {
|
||||||
|
let mut result = String::from("");
|
||||||
let re = regex::Regex::new(delimiter.unwrap_or(r"\s\s+")).unwrap();
|
let re = regex::Regex::new(delimiter.unwrap_or(r"\s\s+")).unwrap();
|
||||||
let mut parts = re.split(text.as_str());
|
for line in text.split('\n') {
|
||||||
for _ in 0..(c - 1) {
|
if (&line).is_empty() {
|
||||||
parts.next().unwrap();
|
continue;
|
||||||
|
}
|
||||||
|
let mut parts = re.split(line);
|
||||||
|
for _ in 0..(c - 1) {
|
||||||
|
parts.next().unwrap();
|
||||||
|
}
|
||||||
|
if !result.is_empty() {
|
||||||
|
result.push('\n');
|
||||||
|
}
|
||||||
|
result.push_str(parts.next().unwrap_or(""));
|
||||||
}
|
}
|
||||||
parts.next().unwrap().to_string()
|
result
|
||||||
} else {
|
} else {
|
||||||
text
|
text
|
||||||
}
|
}
|
||||||
|
@ -134,21 +144,28 @@ fn parse_output_single(mut text: String, suggestion_type: SuggestionType) -> Str
|
||||||
SuggestionType::MultipleSelections
|
SuggestionType::MultipleSelections
|
||||||
| SuggestionType::Disabled
|
| SuggestionType::Disabled
|
||||||
| SuggestionType::SnippetSelection => {
|
| SuggestionType::SnippetSelection => {
|
||||||
text.truncate(text.len() - 1);
|
let len = text.len();
|
||||||
|
if len > 1 {
|
||||||
|
text.truncate(len - 1);
|
||||||
|
}
|
||||||
text
|
text
|
||||||
}
|
}
|
||||||
SuggestionType::SingleRecommendation => {
|
SuggestionType::SingleRecommendation => {
|
||||||
let lines: Vec<&str> = text.lines().collect();
|
let lines: Vec<&str> = text.lines().collect();
|
||||||
|
|
||||||
match (lines.get(0), lines.get(1), lines.get(2)) {
|
match (lines.get(0), lines.get(1), lines.get(2)) {
|
||||||
(Some(one), Some(termination), Some(two)) if *termination == "enter" => {
|
(Some(one), Some(termination), Some(two))
|
||||||
|
if *termination == "enter" || termination.is_empty() =>
|
||||||
|
{
|
||||||
if two.is_empty() {
|
if two.is_empty() {
|
||||||
(*one).to_string()
|
(*one).to_string()
|
||||||
} else {
|
} else {
|
||||||
(*two).to_string()
|
(*two).to_string()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(Some(one), Some(termination), None) if *termination == "enter" => {
|
(Some(one), Some(termination), None)
|
||||||
|
if *termination == "enter" || termination.is_empty() =>
|
||||||
|
{
|
||||||
(*one).to_string()
|
(*one).to_string()
|
||||||
}
|
}
|
||||||
(Some(one), Some(termination), _) if *termination == "tab" => (*one).to_string(),
|
(Some(one), Some(termination), _) if *termination == "tab" => (*one).to_string(),
|
||||||
|
|
|
@ -12,8 +12,5 @@ mod structures;
|
||||||
mod terminal;
|
mod terminal;
|
||||||
mod welcome;
|
mod welcome;
|
||||||
|
|
||||||
use std::error::Error;
|
pub use handler::handle_config;
|
||||||
|
pub use structures::option::{config_from_env, config_from_iter};
|
||||||
fn main() -> Result<(), Box<dyn Error>> {
|
|
||||||
handler::handle_config(structures::option::config_from_env())
|
|
||||||
}
|
|
|
@ -99,11 +99,9 @@ fn read_file(
|
||||||
}
|
}
|
||||||
|
|
||||||
let line = l.unwrap();
|
let line = l.unwrap();
|
||||||
let hash = line.hash_line();
|
|
||||||
if visited_lines.contains(&hash) {
|
// duplicate
|
||||||
continue;
|
if !tags.is_empty() && !comment.is_empty() {}
|
||||||
}
|
|
||||||
visited_lines.insert(hash);
|
|
||||||
|
|
||||||
// blank
|
// blank
|
||||||
if line.is_empty() {
|
if line.is_empty() {
|
||||||
|
@ -136,13 +134,17 @@ fn read_file(
|
||||||
let (variable, command, opts) = parse_variable_line(&line);
|
let (variable, command, opts) = parse_variable_line(&line);
|
||||||
variables.insert(&tags, &variable, (String::from(command), opts));
|
variables.insert(&tags, &variable, (String::from(command), opts));
|
||||||
}
|
}
|
||||||
// first snippet line
|
// snippet
|
||||||
else if (&snippet).is_empty() {
|
|
||||||
snippet.push_str(&line);
|
|
||||||
}
|
|
||||||
// other snippet lines
|
|
||||||
else {
|
else {
|
||||||
snippet.push_str(display::LINE_SEPARATOR);
|
let hash = format!("{}{}", &comment, &line).hash_line();
|
||||||
|
if visited_lines.contains(&hash) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
visited_lines.insert(hash);
|
||||||
|
|
||||||
|
if !(&snippet).is_empty() {
|
||||||
|
snippet.push_str(display::LINE_SEPARATOR);
|
||||||
|
}
|
||||||
snippet.push_str(&line);
|
snippet.push_str(&line);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,25 +5,17 @@
|
||||||
# escape code + subshell
|
# escape code + subshell
|
||||||
echo -ne "\033]0;$(hostname)\007"
|
echo -ne "\033]0;$(hostname)\007"
|
||||||
|
|
||||||
# sed with replacement
|
|
||||||
echo "8.8.8.8 via 172.17.0.1 dev eth0 src 172.17.0.2" | sed -E 's/.*src ([0-9.]+).*/\1/p' | head -n1
|
|
||||||
|
|
||||||
# trivial case
|
|
||||||
echo "foo"
|
|
||||||
|
|
||||||
# multiline command: without backslash
|
|
||||||
echo "foo"
|
|
||||||
echo "bar"
|
|
||||||
|
|
||||||
# env var
|
# env var
|
||||||
echo "$HOME"
|
echo "$HOME"
|
||||||
|
|
||||||
# multiline command: with backslash
|
# multi + column
|
||||||
echo 'lorem ipsum'
|
myfn() {
|
||||||
echo "foo" \
|
for i in $@; do
|
||||||
| grep -q "bar" \
|
echo -e "arg: $i\n"
|
||||||
&& echo "match" \
|
done
|
||||||
|| echo "no match"
|
}
|
||||||
|
folders=($(echo "<multi_col>"))
|
||||||
|
myfn "${folders[@]}"
|
||||||
|
|
||||||
# second column: default delimiter
|
# second column: default delimiter
|
||||||
echo "<table_elem> is cool"
|
echo "<table_elem> is cool"
|
||||||
|
@ -37,17 +29,15 @@ echo "I like these languages: "$(printf '%s' "<langs>" | tr '\n' ',' | sed 's/,/
|
||||||
# return multiple results: multiple words
|
# return multiple results: multiple words
|
||||||
echo "I like these examples: "$(printf '%s' "<examples>" | sed 's/^..*$/"&"/' | awk 1 ORS=', ' | sed 's/, $//')""
|
echo "I like these examples: "$(printf '%s' "<examples>" | sed 's/^..*$/"&"/' | awk 1 ORS=', ' | sed 's/, $//')""
|
||||||
|
|
||||||
# multiple replacements
|
# multiple replacements -> "foo"
|
||||||
echo "<x> <y> <x> <z>"
|
echo "<x> <y> <x> <z>"
|
||||||
|
|
||||||
# multi-word
|
|
||||||
echo "<multiword>"
|
|
||||||
|
|
||||||
$ x: echo '1 2 3' | tr ' ' '\n'
|
$ x: echo '1 2 3' | tr ' ' '\n'
|
||||||
$ y: echo 'a b c' | tr ' ' '\n'
|
$ y: echo 'a b c' | tr ' ' '\n'
|
||||||
$ z: echo 'foo bar' | tr ' ' '\n'
|
$ z: echo 'foo bar' | tr ' ' '\n'
|
||||||
$ table_elem: echo -e '0 rust rust-lang.org\n1 clojure clojure.org' --- --column 2
|
$ table_elem: echo -e '0 rust rust-lang.org\n1 clojure clojure.org' --- --column 2
|
||||||
$ table_elem2: echo -e '0;rust;rust-lang.org\n1;clojure;clojure.org' --- --column 2 --delimiter ';'
|
$ table_elem2: echo -e '0;rust;rust-lang.org\n1;clojure;clojure.org' --- --column 2 --delimiter ';'
|
||||||
|
$ multi_col: ls -la | awk '{print $1, $9}' --- --column 2 --delimiter '\s' --multi
|
||||||
$ langs: echo 'clojure rust javascript' | tr ' ' '\n' --- --multi
|
$ langs: echo 'clojure rust javascript' | tr ' ' '\n' --- --multi
|
||||||
$ examples: echo -e 'foo bar\nlorem ipsum\ndolor sit' --- --multi
|
$ examples: echo -e 'foo bar\nlorem ipsum\ndolor sit' --- --multi
|
||||||
$ multiword: echo -e 'foo bar\nlorem ipsum\ndolor sit\nbaz'
|
$ multiword: echo -e 'foo bar\nlorem ipsum\ndolor sit\nbaz'
|
66
tests/core.bash
Normal file
66
tests/core.bash
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# vim: filetype=sh
|
||||||
|
|
||||||
|
source "${NAVI_HOME}/scripts/install"
|
||||||
|
|
||||||
|
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
|
||||||
|
"$@" && test::success || test::fail
|
||||||
|
}
|
||||||
|
|
||||||
|
test::equals() {
|
||||||
|
local -r actual="$(cat)"
|
||||||
|
local -r expected="$(echo "${1:-}")"
|
||||||
|
|
||||||
|
local -r actual2="$(echo "$actual" | xargs | sed -E 's/\s/ /g')"
|
||||||
|
local -r expected2="$(echo "$expected" | xargs | sed -E 's/\s/ /g')"
|
||||||
|
|
||||||
|
if [[ "$actual2" != "$expected2" ]]; then
|
||||||
|
log::error "Expected '${expected}' 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
|
||||||
|
}
|
42
tests/no_prompt_cheats/cases.cheat
Normal file
42
tests/no_prompt_cheats/cases.cheat
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
; author: CI/CD
|
||||||
|
|
||||||
|
% test, ci/cd
|
||||||
|
|
||||||
|
# trivial case -> "foo"
|
||||||
|
echo "foo"
|
||||||
|
|
||||||
|
# sed with replacement -> "172.17.0.2"
|
||||||
|
echo "8.8.8.8 via 172.17.0.1 dev eth0 src 172.17.0.2" | sed -E 's/.*src ([0-9.]+).*/\1/p' | head -n1
|
||||||
|
|
||||||
|
# multiline command: no backslash -> "foo\nbar"
|
||||||
|
echo "foo"
|
||||||
|
echo "bar"
|
||||||
|
|
||||||
|
# multiline command: with backslash -> "lorem ipsum\nno match"
|
||||||
|
echo 'lorem ipsum'
|
||||||
|
echo "foo" \
|
||||||
|
| grep -q "bar" \
|
||||||
|
&& echo "match" \
|
||||||
|
|| echo "no match"
|
||||||
|
|
||||||
|
# 2nd column with default delimiter -> "rust is cool"
|
||||||
|
echo "<language> is cool"
|
||||||
|
|
||||||
|
# 2nd column with custom delimiter -> "clojure is cool"
|
||||||
|
echo "<language2> is cool"
|
||||||
|
|
||||||
|
# multiple words -> "lorem foo bar ipsum"
|
||||||
|
echo "lorem <multiword> ipsum"
|
||||||
|
|
||||||
|
# variable dependency -> "2 12 a 2"
|
||||||
|
echo "<x> <x2> <y> <x>"
|
||||||
|
|
||||||
|
$ x: echo '2'
|
||||||
|
$ x2: echo "$((x+10))"
|
||||||
|
$ y: echo 'a'
|
||||||
|
$ language: echo '0 rust rust-lang.org' --- --column 2
|
||||||
|
$ language2: echo '1;clojure;clojure.org' --- --column 2 --delimiter ';'
|
||||||
|
$ multiword: echo 'foo bar'
|
||||||
|
|
||||||
|
# this should be displayed -> "hi"
|
||||||
|
echo hi
|
58
tests/run
Executable file
58
tests/run
Executable file
|
@ -0,0 +1,58 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
export NAVI_HOME="$(cd "$(dirname "$0")/.." && pwd)"
|
||||||
|
source "${NAVI_HOME}/tests/core.bash"
|
||||||
|
|
||||||
|
TEST_CHEAT_PATH="${NAVI_HOME}/tests/no_prompt_cheats"
|
||||||
|
|
||||||
|
_navi() {
|
||||||
|
stty sane || true
|
||||||
|
local -r navi="./target/debug/navi"
|
||||||
|
local filter="$*"
|
||||||
|
local filter="${filter::-2}"
|
||||||
|
RUST_BACKTRACE=1 NAVI_PATH="$TEST_CHEAT_PATH" "$navi" best "$filter"
|
||||||
|
}
|
||||||
|
|
||||||
|
_navi_test() {
|
||||||
|
_navi "$1" \
|
||||||
|
| test::equals "$2"
|
||||||
|
}
|
||||||
|
|
||||||
|
_get_all_tests() {
|
||||||
|
cat "${TEST_CHEAT_PATH}/cases.cheat" \
|
||||||
|
| grep '#' \
|
||||||
|
| grep ' ->' \
|
||||||
|
| sed 's/\\n/ /g' \
|
||||||
|
| sed -E 's/# (.*) -> "(.*)"/\1|\2/g'
|
||||||
|
}
|
||||||
|
|
||||||
|
_get_tests() {
|
||||||
|
local -r filter="$1"
|
||||||
|
|
||||||
|
if [ -n "$filter" ]; then
|
||||||
|
_get_all_tests \
|
||||||
|
| grep "$filter"
|
||||||
|
else
|
||||||
|
_get_all_tests
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
if ! command_exists fzf; then
|
||||||
|
export PATH="$PATH:$HOME/.fzf/bin"
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd "$NAVI_HOME"
|
||||||
|
|
||||||
|
filter="${1:-}"
|
||||||
|
|
||||||
|
test::set_suite "cases"
|
||||||
|
ifs="$IFS"
|
||||||
|
IFS=$'\n'
|
||||||
|
for i in $(_get_tests "$filter"); do
|
||||||
|
IFS="$ifs"
|
||||||
|
query="$(echo "$i" | cut -d'|' -f1)"
|
||||||
|
expected="$(echo "$i" | cut -d'|' -f2)"
|
||||||
|
test::run "$query" _navi_test "$query" "$expected"
|
||||||
|
done
|
||||||
|
|
||||||
|
test::finish
|
|
@ -2,6 +2,9 @@
|
||||||
mod tests {
|
mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn it_works() {
|
fn it_works() {
|
||||||
assert_eq!(2 + 2, 4);
|
//let _x = navi::handle_config(navi::config_from_iter(
|
||||||
|
//"navi best trivial".split(' ').collect(),
|
||||||
|
//));
|
||||||
|
// assert_eq!(x, 3);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue