Skip to content

Commit

Permalink
Improve bash and zsh widgets (#486)
Browse files Browse the repository at this point in the history
  • Loading branch information
denisidoro authored Apr 9, 2021
1 parent 1f3560f commit 307f0c1
Show file tree
Hide file tree
Showing 7 changed files with 89 additions and 172 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "navi"
version = "2.14.0"
version = "2.15.0"
authors = ["Denis Isidoro <[email protected]>"]
edition = "2018"
description = "An interactive cheatsheet tool for the command-line"
Expand Down
2 changes: 0 additions & 2 deletions docs/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,6 @@ Feel free to be the maintainer of **navi** for any package manager you'd like!

### Installing the shell widget

The shell widget maintains your `.history`-like file consistent and allows you to edit commands before executing them.

If you want to install it, add this line to your `.bashrc`-like file:
```sh
# bash
Expand Down
57 changes: 23 additions & 34 deletions shell/navi.plugin.bash
Original file line number Diff line number Diff line change
@@ -1,46 +1,35 @@
#!/usr/bin/env bash

_call_navi() {
local selected

if [ -n "${READLINE_LINE}" ]; then
if selected="$(printf "%s" "$(navi --print --fzf-overrides '--no-select-1' --query "${READLINE_LINE}" </dev/tty)")"; then
READLINE_LINE="$selected"
READLINE_POINT=${#READLINE_LINE}
fi
else
if selected="$(printf "%s" "$(navi --print </dev/tty)")"; then
READLINE_LINE="$selected"
READLINE_POINT=${#READLINE_LINE}
fi
_navi_call() {
local result="$(navi "$@" </dev/tty)"
if [ -z "${result}" ]; then
result="$(navi --print </dev/tty)"
fi
printf "%s" "$result"
}

__call_navi_legacy_versions() {
local -r result="$(navi --print)"
local -r linecount="$(echo "$result" | wc -l)"
_navi_widget() {
local -r input="${READLINE_LINE}"
local -r last_command="$(echo "${input}" | navi fn widget::last_command)"

if [[ "$linecount" -lt 2 ]]; then
printf "%s" "$result"
return 0
if [ -z "${last_command}" ]; then
local -r output="$(_navi_call --print --fzf-overrides '--no-select-1')"
else
local -r find="$last_command"
local -r replacement="$(_navi_call --print --query "${last_command}")"
local -r output="${input//$find/$replacement}"
fi

IFS=$'\n'
local i=1;
for line in $result; do
if echo "$line" | grep -q '\\$'; then
printf "${line::-1} "
elif [[ "$i" -eq "$linecount" ]]; then
printf "$line "
else
printf "${line}; "
fi
i=$((i+1))
done
READLINE_LINE="$output"
READLINE_POINT=${#READLINE_LINE}
}

_navi_widget_legacy() {
_navi_call --print
}

if [ ${BASH_VERSION:0:1} -lt 4 ]; then
bind '"\C-g": " \C-b\C-k \C-u`__call_navi_legacy_versions`\e\C-e\C-a\C-y\C-h\C-e\e \C-y\ey\C-x\C-x\C-f"'
bind '"\C-g": " \C-b\C-k \C-u`_navi_widget_legacy`\e\C-e\C-a\C-y\C-h\C-e\e \C-y\ey\C-x\C-x\C-f"'
else
bind -x '"\C-g": _call_navi'
fi
bind -x '"\C-g": _navi_widget'
fi
155 changes: 22 additions & 133 deletions shell/navi.plugin.zsh
Original file line number Diff line number Diff line change
@@ -1,147 +1,36 @@
#!/usr/bin/env zsh

# Copy-pasted from https://gist.github.com/enisozgen/2109cc80ea9f405f80c6c383f2375e77


# Change last part of the command
# NOTE Creates sometime problem if there is same word in the input
ChangeLastCommand()
{
[[ -z "${2// }" ]] && printf "%s" "$(echo "${1}${3}")" || printf "%s" "$(echo "${1/$2/ $3}")"
}

SendLastCommandAfterPIPE()
{
# Send last command after pipe
INPUT_STRING="${1}"
a=("${(@s/|/)${INPUT_STRING}}") # | modifier
printf "%s" "$(echo "${a[-1]}")"
}


# Is there pipe in the input string
IsPipeExist()
{
INPUT_STRING=$1
if [[ $INPUT_STRING == *\|* ]]; then
NAVI_PIPE="true"
else
NAVI_PIPE="false"
fi
}


IsOnlySpace ()
{
if [[ -z "${1// }" ]] ; then
return 0
else
return 1
fi
}

NaviUISearch()
{
NAVI_RET=$(printf "%s" "$(navi --print --fzf-overrides '--no-select-1' --query "${1}" </dev/tty)")
printf ${NAVI_RET}
}


NaviOutputControl ()
{
if ! [[ -z "$1" ]] ; then
return 0
else
return 1
_navi_call() {
local result="$(navi "$@" </dev/tty)"
if [ -z "${result}" ]; then
result="$(navi --print </dev/tty)"
fi
printf "%s" "$result"
}

_navi_widget() {
local -r input="${LBUFFER}"
local -r last_command="$(echo "${input}" | navi fn widget::last_command)"
local find="$last_command"
local replacement="$last_command"


SmartNavi()
{
if IsOnlySpace $1 ; then
NAVI_RET=$(printf "%s" "$(navi --print --fzf-overrides '--no-select-1' </dev/tty)")
if [ -z "${last_command}" ]; then
replacement="$(_navi_call --print --fzf-overrides '--no-select-1')"
elif [ "${LASTWIDGET}" = "_navi_widget" ] && [ "$input" = "$previous_output" ]; then
find="$input"
replacement="$(_navi_call --print --query "${previous_last_command:-$last_command}")"
else
NAVI_RET=$(printf "%s" "$(navi --print --best-match --fzf-overrides '--no-select-1' --query "${1}" </dev/tty)")
replacement="$(_navi_call --print --best-match --query "${last_command}")"
fi

previous_last_command="$last_command"
previous_output="${input//$find/$replacement}"

# Return warning if there is no output else return best match
if NaviOutputControl ${NAVI_RET}; then
printf ${NAVI_RET}
else
printf "Navi Returned Empty"
fi
}


_call_navi() {
local selected
if [ -n "$LBUFFER" ]; then
if selected="$(printf "%s" "$(navi --print --fzf-overrides '--no-select-1' --query "${LBUFFER}" </dev/tty)")"; then
LBUFFER="$selected"
fi
else
# If there is not any word on list
if selected="$(printf "%s" "$(navi --print </dev/tty)")"; then
LBUFFER="$selected"
fi
fi
zle kill-whole-line
LBUFFER="${previous_output}"
region_highlight=("P0 100 bold")
zle redisplay
}

_call_smart_navi() {

# set -x
INPUT_STRING=$LBUFFER
IsPipeExist ${INPUT_STRING}


# Is there some written stuff in LBUFFER ?
if ! [ -z "$INPUT_STRING" ] ; then
# If last navi output same as current input
# Use this part when you don't like navi best match
if [ "${LASTWIDGET}" = "_call_smart_navi" ] && [ "${OUTPUT_STRING}" = "$INPUT_STRING" ];then
LBUFFER_LAST_COMMAND=$(SendLastCommandAfterPIPE "${INPUT_STRING}")

# Searching with same input as before but this time we are using navi interactive UI since navi didn't return us what we want
OUTPUT_STRING=$(NaviUISearch ${PREVIOUS_LAST})
OUTPUT_STRING=$(ChangeLastCommand "$INPUT_STRING" "$LBUFFER_LAST_COMMAND" "$OUTPUT_STRING")

else
# First search always start from here!!!
if [ "${NAVI_PIPE}" = "false" ] ; then

# LBUFFER_LAST_COMMAND=$(SendLastCommandAfterPIPE "${INPUT_STRING}")
# PREVIOUS_LAST=$LBUFFER_LAST_COMMAND

# Remember what was last command after pipe
PREVIOUS_LAST=$INPUT_STRING
OUTPUT_STRING=$(SmartNavi ${INPUT_STRING})

else
LBUFFER_LAST_COMMAND=$(SendLastCommandAfterPIPE "${INPUT_STRING}")

# Remember what was last command after pipe
PREVIOUS_LAST=$LBUFFER_LAST_COMMAND

OUTPUT_STRING=$(SmartNavi ${LBUFFER_LAST_COMMAND})
OUTPUT_STRING=$(ChangeLastCommand "$INPUT_STRING" "$LBUFFER_LAST_COMMAND" "$OUTPUT_STRING")

fi
fi
LBUFFER="$OUTPUT_STRING"
else
# There is nothing use default navi command
_call_navi
fi

region_highlight=("P0 100 bold")
zle redisplay
}


zle -N _call_smart_navi
bindkey '^g' _call_smart_navi
zle -N _navi_widget
bindkey '^g' _navi_widget
40 changes: 40 additions & 0 deletions src/cmds/func.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ use crate::handler;
use crate::structures::config;
use crate::url;
use anyhow::Error;
use std::io::{self, Read};

#[derive(Debug)]
pub enum Func {
UrlOpen,
Welcome,
WidgetLastCommand,
}

pub fn main(func: &Func, args: Vec<String>) -> Result<(), Error> {
Expand All @@ -15,5 +17,43 @@ pub fn main(func: &Func, args: Vec<String>) -> Result<(), Error> {
Func::Welcome => handler::handle_config(config::config_from_iter(
"navi --path /tmp/navi/irrelevant".split(' ').collect(),
)),
Func::WidgetLastCommand => widget_last_command(),
}
}

fn widget_last_command() -> Result<(), Error> {
let mut text = String::new();
io::stdin().read_to_string(&mut text)?;

let replacements = vec![("|", "ඛ"), ("||", "ග"), ("&&", "ඝ")];

let parts = shellwords::split(&text).unwrap_or_else(|_| text.split('|').map(|s| s.to_string()).collect());

for p in parts {
for (pattern, escaped) in replacements.clone() {
if p.contains(pattern) && p != pattern {
let replacement = p.replace(pattern, escaped);
text = text.replace(&p, &replacement);
}
}
}

let mut extracted = text.clone();
for (pattern, _) in replacements.clone() {
let mut new_parts = text.rsplit(pattern);
if let Some(extracted_attempt) = new_parts.next() {
if extracted_attempt.len() <= extracted.len() {
extracted = extracted_attempt.to_string();
}
}
}

for (pattern, escaped) in replacements.clone() {
text = text.replace(&escaped, &pattern);
extracted = extracted.replace(&escaped, &pattern);
}

println!("{}", extracted.trim_start());

Ok(())
}
3 changes: 2 additions & 1 deletion src/structures/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ impl FromStr for Func {
match s {
"url::open" => Ok(Func::UrlOpen),
"welcome" => Ok(Func::Welcome),
"widget::last_command" => Ok(Func::WidgetLastCommand),
_ => Err("no match"),
}
}
Expand Down Expand Up @@ -147,7 +148,7 @@ pub enum Command {
/// Performs ad-hoc functions provided by navi
Fn {
/// Function name (example: "url::open")
#[clap(possible_values = &["url::welcome", "open"], case_insensitive = true)]
#[clap(possible_values = &["url::welcome", "open", "widget::last_command"], case_insensitive = true)]
func: Func,
/// List of arguments (example: "https://google.com")
args: Vec<String>,
Expand Down

0 comments on commit 307f0c1

Please sign in to comment.