removed most unused stuff (bash will be the minimal config, zsh the extendet)
This commit is contained in:
parent
33ba7df9ef
commit
38a0834f40
13 changed files with 192 additions and 1125 deletions
511
.bashrc
511
.bashrc
|
@ -1,20 +1,14 @@
|
|||
#!/bin/bash
|
||||
# ─< Helper functions >─────────────────────────────────────────────────────────────────
|
||||
function echo_error() { echo -e "\033[0;1;31merror:\033[0;31m\t${*}\033[0m"; }
|
||||
function echo_binfo() { echo -e "\033[0;1;34mInfo:\033[0;34m\t${*}\033[0m"; }
|
||||
function echo_info() { echo -e "\033[0;35m${*}\033[0m"; }
|
||||
function echo_error() { echo -e "\033[0;1;31m❌ ERROR:\033[0;31m\t${*}\033[0m"; }
|
||||
function echo_binfo() { echo -e "\033[0;1;34m⚠️ WARNING:\033[0;34m\t${*}\033[0m"; }
|
||||
function echo_info() { echo -e "\033[0;35mℹ️ INFO:${*}\033[0m"; }
|
||||
|
||||
# ─< check if command exists >────────────────────────────────────────────────────────────
|
||||
command_exists() {
|
||||
command -v "$1" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
if command_exists curl; then
|
||||
alias install_pkg='bash --norc -c "$(curl -sSL https://git.k4li.de/pika/scripts/raw/branch/main/bash/snippets/install_pkg.sh)" -- '
|
||||
else
|
||||
echo_error "curl is not installed, universal install disabled!"
|
||||
fi
|
||||
|
||||
# ─< Silent execution >─────────────────────────────────────────────────────────────────
|
||||
silentexec() {
|
||||
"$@" >/dev/null 2>&1
|
||||
|
@ -33,15 +27,6 @@ check_root() {
|
|||
fi
|
||||
}
|
||||
|
||||
# ─< ble.sh -- https://github.com/akinomyoga/ble.sh >─────────────────────────────────────
|
||||
p_blesh() {
|
||||
if [[ ! -f $HOME/.local/share/blesh/ble.sh ]]; then
|
||||
bash -norc -c "$(curl -sSL https://git.k4li.de/pika/scripts/raw/branch/main/bash/installs/install-blesh.sh)"
|
||||
else
|
||||
source "$HOME/.local/share/blesh/ble.sh" --attach=none
|
||||
fi
|
||||
}
|
||||
|
||||
_sensible.bash_() {
|
||||
# Sensible Bash - An attempt at saner Bash defaults
|
||||
# Maintainer: mrzool <http://mrzool.cc>
|
||||
|
@ -151,7 +136,7 @@ _sensible.bash_() {
|
|||
|
||||
_defaults_() {
|
||||
# ─< set keybinding mode >────────────────────────────────────────────────────────────────
|
||||
set -o emacs
|
||||
# set -o emacs
|
||||
# set -o vim
|
||||
|
||||
# If set, the pattern "**" used in a pathname expansion context will
|
||||
|
@ -165,6 +150,7 @@ _defaults_() {
|
|||
if [ -z "${debian_chroot:-}" ] && [ -r /etc/debian_chroot ]; then
|
||||
debian_chroot=$(cat /etc/debian_chroot)
|
||||
fi
|
||||
|
||||
# If this is an xterm set the title to user@host:dir
|
||||
case "$TERM" in
|
||||
xterm* | rxvt*)
|
||||
|
@ -175,141 +161,18 @@ _defaults_() {
|
|||
|
||||
alias alert='notify-send --urgency=low -i "$([ $? = 0 ] && echo terminal || echo error)" "$(history|tail -n1|sed -e '\''s/^\s*[0-9]\+\s*//;s/[;&|]\s*alert$//'\'')"'
|
||||
|
||||
if [ -f ~/.bash_aliases ]; then
|
||||
. ~/.bash_aliases
|
||||
if [ -f "$HOME/.bash_aliases" ]; then
|
||||
. "$HOME/.bash_aliases"
|
||||
fi
|
||||
if [ -f ~/.completions/ssh ]; then
|
||||
. ~/.completions/ssh
|
||||
|
||||
if [ -f "$HOME/.bash/ssh" ]; then
|
||||
. "$HOME/.bash/ssh"
|
||||
fi
|
||||
}
|
||||
|
||||
p_basher() {
|
||||
if [[ ! -d $HOME/.basher/ ]]; then
|
||||
git clone --depth=1 https://github.com/basherpm/basher.git ~/.basher
|
||||
else
|
||||
export PATH="$HOME/.basher/bin:$PATH"
|
||||
eval "$(basher init - bash)" && silentexec $(cd $HOME/.basher/ && git pull)
|
||||
fi
|
||||
}
|
||||
|
||||
p_has() {
|
||||
if ! command_exists has; then
|
||||
inst_has() {
|
||||
git clone https://github.com/kdabir/has.git /tmp/has && cd /tmp/has && sudo make install
|
||||
}
|
||||
echo_info "Installing has"
|
||||
silentexec inst_has
|
||||
else
|
||||
tools() {
|
||||
local pkgs="bash zsh git curl make cmake gcc g++ rg docker composer node npm php jre python3 go cargo"
|
||||
has $pkgs
|
||||
}
|
||||
fi
|
||||
}
|
||||
|
||||
# plugins() {
|
||||
# ─< qfc -- https://github.com/pindexis/qfc >─────────────────────────────────────────────
|
||||
# [[ -s "$HOME/.qfc/bin/qfc.sh" ]] && source "$HOME/.qfc/bin/qfc.sh"
|
||||
# p_basher
|
||||
# p_has
|
||||
# p_blesh
|
||||
# }
|
||||
|
||||
_color_prompt_() {
|
||||
# set a fancy prompt (non-color, unless we know we "want" color)
|
||||
case "$TERM" in
|
||||
xterm-color | *-256color) color_prompt=yes ;;
|
||||
esac
|
||||
|
||||
# uncomment for a colored prompt, if the terminal has the capability; turned
|
||||
# off by default to not distract the user: the focus in a terminal window
|
||||
# should be on the output of commands, not on the prompt
|
||||
# force_color_prompt=yes
|
||||
|
||||
if [ -n "$force_color_prompt" ]; then
|
||||
if [ -x /usr/bin/tput ] && tput setaf 1 >&/dev/null; then
|
||||
# We have color support; assume it's compliant with Ecma-48
|
||||
# (ISO/IEC-6429). (Lack of such support is extremely rare, and such
|
||||
# a case would tend to support setf rather than setaf.)
|
||||
color_prompt=yes
|
||||
else
|
||||
color_prompt=
|
||||
fi
|
||||
|
||||
if [ "$color_prompt" = yes ]; then
|
||||
PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '
|
||||
else
|
||||
PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w\$ '
|
||||
fi
|
||||
unset color_prompt force_color_prompt
|
||||
fi
|
||||
}
|
||||
|
||||
_zoxide_() {
|
||||
if command_exists zoxide; then
|
||||
eval "$(zoxide init bash)"
|
||||
fi
|
||||
}
|
||||
|
||||
_fancy_ls_() {
|
||||
# ─< colorized ls >─────────────────────────────────────────────────────────────────────────
|
||||
if command_exists exa; then
|
||||
alias ls="exa --icons -l --git"
|
||||
alias l="exa --long --no-filesize --no-permissions --no-time --git --colour-scale --icons"
|
||||
alias ll="exa --all --long --no-filesize --no-permissions --no-time --git --colour-scale --icons"
|
||||
alias tree="exa --icons -l --tree"
|
||||
elif command_exists lsd; then
|
||||
alias ls="lsd -l -1 -h1 --almost-all --git"
|
||||
alias l="lsd -1"
|
||||
alias ll="lsd -1 --almost-all"
|
||||
alias clearl="command clear && l"
|
||||
alias tree="lsd --tree"
|
||||
elif command_exists eza; then
|
||||
alias ls="eza --icons --long --git"
|
||||
alias l="eza --icons -l"
|
||||
alias ll="eza --icons -laa"
|
||||
alias tree="eza --icons -l --tree"
|
||||
else
|
||||
alias ls="ls --color=always -lAph"
|
||||
alias l="ls --color=always -lph -w1"
|
||||
alias ll="ls --color=always -lph"
|
||||
fi
|
||||
}
|
||||
|
||||
_tmux_() {
|
||||
# ─< t stands for tmux >────────────────────────────────────────────────────────────────────
|
||||
if command_exists tmux; then
|
||||
tmux_y="-- tmux-session active! | connecting to active session --"
|
||||
tmux_n="-- no tmux-session found! | creating one --"
|
||||
ta() {
|
||||
command tmux list-sessions >/dev/null 2>&1
|
||||
if [ $? -eq 0 ]; then
|
||||
if command_exists notify-send; then
|
||||
notify-send "$tmux_y"
|
||||
sleep 0.5
|
||||
tmux attach
|
||||
else
|
||||
echo_info "$tmux_y"
|
||||
sleep 0.5
|
||||
tmux attach
|
||||
fi
|
||||
else
|
||||
if command_exists notify-send; then
|
||||
notify-send "$tmux_n"
|
||||
sleep 0.5
|
||||
tmux
|
||||
else
|
||||
echo_info "$tmux_n"
|
||||
sleep 0.5
|
||||
tmux
|
||||
fi
|
||||
fi
|
||||
}
|
||||
alias ts="tmux source $HOME/.tmux.conf"
|
||||
fi
|
||||
}
|
||||
|
||||
_cli_qol_() {
|
||||
# ─< colored everything >───────────────────────────────────────────────────────────────────
|
||||
alias ip="ip --color=always"
|
||||
|
||||
|
@ -342,50 +205,13 @@ _cli_qol_() {
|
|||
else
|
||||
curl -s https://ohmyposh.dev/install.sh | sudo bash -s -- -d /usr/bin/
|
||||
fi
|
||||
}
|
||||
|
||||
_games() {
|
||||
alias g2048='bash --norc -c "$(curl -sSL https://git.k4li.de/pika/scripts/raw/branch/main/bash/games/2048.sh)"'
|
||||
alias gwordle='bash --norc -c "$(curl -sSL https://git.k4li.de/pika/scripts/raw/branch/main/bash/games/wordle.sh)"'
|
||||
}
|
||||
|
||||
# ─< t stands for trash(-cli) >───────────────────────────────────────────────────────────────
|
||||
_trash() {
|
||||
if command_exists trash; then
|
||||
alias rm="trash"
|
||||
alias t="trash"
|
||||
elif command_exists trash-cli; then
|
||||
alias rm="trash-cli"
|
||||
alias t="trash-cli"
|
||||
else
|
||||
echo_error "-- You do not have trash or trash-cli installed! Your 'rm' will be permanent! --"
|
||||
fi
|
||||
}
|
||||
|
||||
# ─< rsync >────────────────────────────────────────────────────────────────────────────────
|
||||
_rsync_() {
|
||||
_rrsync() {
|
||||
if command_exists find; then
|
||||
numbers=0
|
||||
for source in "${@:1:$#-1}"; do
|
||||
numbers=$(($numbers + $(find "$source" -type f | wc -l)))
|
||||
done
|
||||
destination="${@: -1}"
|
||||
|
||||
rsync -avP --info=progress2 "${@:1:$#-1}" "$destination" | pv -lpes $numbers
|
||||
else
|
||||
echo_error "-- We could not find 'find'. Please install 'find' to enable the progress bar. (hit 'CTRL + C' to exit now) --"
|
||||
sleep 5
|
||||
rsync -avP --info=progress2 "$@"
|
||||
fi
|
||||
}
|
||||
# ─< rsync >────────────────────────────────────────────────────────────────────────────────
|
||||
if command_exists rsync; then
|
||||
alias cp="_rrsync"
|
||||
alias scp="rsync -avP"
|
||||
alias cp="scp"
|
||||
fi
|
||||
}
|
||||
|
||||
_cat_() {
|
||||
# ─< bat alias >────────────────────────────────────────────────────────────────────────────
|
||||
if command_exists batcat; then
|
||||
alias cat="batcat --color=always -p --paging=never"
|
||||
|
@ -396,51 +222,17 @@ _cat_() {
|
|||
alias less="bat --paging always --color=always"
|
||||
alias gd="bat --diff"
|
||||
fi
|
||||
}
|
||||
|
||||
_fetches_() {
|
||||
# ─< fastfetch >────────────────────────────────────────────────────────────────────────────
|
||||
if command_exists fastfetch; then
|
||||
alias ff="fastfetch"
|
||||
alias clearff="command clear & fastfetch"
|
||||
alias clearf="command clear & fastfetch"
|
||||
if fastfetch --config os >/dev/null 2>&1; then
|
||||
alias f="fastfetch --config os"
|
||||
else
|
||||
git clone https://git.k4li.de/mirrors/fastfetch.git $HOME/.local/share/fastfetch >/dev/null 2>&1
|
||||
exec $SHELL
|
||||
fi
|
||||
command clear &
|
||||
fastfetch
|
||||
if command_exists trash; then
|
||||
alias rm="trash"
|
||||
alias t="trash"
|
||||
elif command_exists trash-cli; then
|
||||
alias rm="trash-cli"
|
||||
alias t="trash-cli"
|
||||
else
|
||||
echo_error "-- You do not have trash or trash-cli installed! Your 'rm' will be permanent! --"
|
||||
fi
|
||||
}
|
||||
|
||||
_nmap_() {
|
||||
# ─< set nmap-alias >───────────────────────────────────────────────────────────────────────
|
||||
if command_exists nmap; then
|
||||
alias scanvuln="sudo nmap --script vuln -vvv"
|
||||
alias sv="scanvuln"
|
||||
alias portscan="sudo nmap -sT"
|
||||
alias ps="portscan"
|
||||
fi
|
||||
}
|
||||
|
||||
_docker_() {
|
||||
# ─< d stands for docker >──────────────────────────────────────────────────────────────────
|
||||
if command_exists docker; then
|
||||
alias up="docker compose up"
|
||||
alias down="docker compose down"
|
||||
alias pull="docker compose pull"
|
||||
alias d="docker"
|
||||
alias dr="docker run --rm -it"
|
||||
alias drs="docker compose down && docker compose up -d --remove-orphans --force-recreate"
|
||||
alias ds="docker ps -a"
|
||||
alias dc="docker compose"
|
||||
alias appupdate="docker compose pull && docker compose up -d --force-recreate"
|
||||
fi
|
||||
}
|
||||
|
||||
_git_() {
|
||||
# ─< g stands for GIT >─────────────────────────────────────────────────────────────────────
|
||||
if command_exists git; then
|
||||
alias g="git"
|
||||
|
@ -460,6 +252,133 @@ _git_() {
|
|||
alias lg="lazygit"
|
||||
fi
|
||||
fi
|
||||
|
||||
# ─< d stands for docker >──────────────────────────────────────────────────────────────────
|
||||
if command_exists docker; then
|
||||
alias up="docker compose up"
|
||||
alias down="docker compose down"
|
||||
alias pull="docker compose pull"
|
||||
alias d="docker"
|
||||
alias dr="docker run --rm -it"
|
||||
alias drs="docker compose down && docker compose up -d --remove-orphans --force-recreate"
|
||||
alias ds="docker ps -a"
|
||||
alias dc="docker compose"
|
||||
alias appupdate="docker compose pull && docker compose up -d --force-recreate"
|
||||
fi
|
||||
|
||||
# ─< colorized ls >─────────────────────────────────────────────────────────────────────────
|
||||
if command_exists exa; then
|
||||
alias ls="exa --icons -l --git"
|
||||
alias l="exa --long --no-filesize --no-permissions --no-time --git --colour-scale --icons"
|
||||
alias ll="exa --all --long --no-filesize --no-permissions --no-time --git --colour-scale --icons"
|
||||
alias tree="exa --icons -l --tree"
|
||||
elif command_exists lsd; then
|
||||
alias ls="lsd -l -1 -h1 --almost-all --git"
|
||||
alias l="lsd -1"
|
||||
alias ll="lsd -1 --almost-all"
|
||||
alias clearl="command clear && l"
|
||||
alias tree="lsd --tree"
|
||||
elif command_exists eza; then
|
||||
alias ls="eza --icons --long --git"
|
||||
alias l="eza --icons -l"
|
||||
alias ll="eza --icons -laa"
|
||||
alias tree="eza --icons -l --tree"
|
||||
else
|
||||
alias ls="ls --color=always -lAph"
|
||||
alias l="ls --color=always -lph -w1"
|
||||
alias ll="ls --color=always -lph"
|
||||
fi
|
||||
|
||||
# ─< t stands for tmux >────────────────────────────────────────────────────────────────────
|
||||
if command_exists tmux; then
|
||||
local tmux_y="$(echo '-- tmux-session active! | connecting to active session --')"
|
||||
local tmux_n="$(echo '-- no tmux-session found! | creating one --')"
|
||||
ta() {
|
||||
# command tmux list-sessions >/dev/null 2>&1
|
||||
if command tmux list-sessions >/dev/null 2>&1; then
|
||||
if command_exists notify-send; then
|
||||
notify-send "TMUX" "${tmux_y}"
|
||||
sleep 0.5
|
||||
tmux attach
|
||||
else
|
||||
echo_info "${tmux_y}"
|
||||
sleep 0.5
|
||||
tmux attach
|
||||
fi
|
||||
else
|
||||
if command_exists notify-send; then
|
||||
notify-send "TMUX" "${tmux_n}"
|
||||
sleep 0.5
|
||||
tmux
|
||||
else
|
||||
echo_info "$tmux_n"
|
||||
sleep 0.5
|
||||
tmux
|
||||
fi
|
||||
fi
|
||||
}
|
||||
alias ts="tmux source ~/.tmux.conf"
|
||||
fi
|
||||
}
|
||||
|
||||
_color_prompt_() {
|
||||
# set a fancy prompt (non-color, unless we know we "want" color)
|
||||
case "$TERM" in
|
||||
xterm-color | *-256color) color_prompt=yes ;;
|
||||
esac
|
||||
|
||||
# uncomment for a colored prompt, if the terminal has the capability; turned
|
||||
# off by default to not distract the user: the focus in a terminal window
|
||||
# should be on the output of commands, not on the prompt
|
||||
# force_color_prompt=yes
|
||||
|
||||
if [ -n "$force_color_prompt" ]; then
|
||||
if [ -x /usr/bin/tput ] && tput setaf 1 >&/dev/null; then
|
||||
# We have color support; assume it's compliant with Ecma-48
|
||||
# (ISO/IEC-6429). (Lack of such support is extremely rare, and such
|
||||
# a case would tend to support setf rather than setaf.)
|
||||
color_prompt=yes
|
||||
else
|
||||
color_prompt=
|
||||
fi
|
||||
|
||||
if [ "$color_prompt" = yes ]; then
|
||||
PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '
|
||||
else
|
||||
PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w\$ '
|
||||
fi
|
||||
unset color_prompt force_color_prompt
|
||||
fi
|
||||
}
|
||||
|
||||
_games() {
|
||||
if command_exists curl; then
|
||||
echo_info "Games available. Try 'alias | grep g'"
|
||||
alias g2048='bash --norc -c "$(curl -sSL https://git.k4li.de/pika/scripts/raw/branch/main/bash/games/2048.sh)"'
|
||||
alias gwordle='bash --norc -c "$(curl -sSL https://git.k4li.de/pika/scripts/raw/branch/main/bash/games/wordle.sh)"'
|
||||
fi
|
||||
}
|
||||
|
||||
_end() {
|
||||
# ─< fastfetch >────────────────────────────────────────────────────────────────────────────
|
||||
if command_exists fastfetch; then
|
||||
alias ff="fastfetch"
|
||||
alias clearff="command clear & fastfetch"
|
||||
alias clearf="command clear & fastfetch"
|
||||
if fastfetch --config os >/dev/null 2>&1; then
|
||||
alias f="fastfetch --config os"
|
||||
else
|
||||
git clone https://git.k4li.de/mirrors/fastfetch.git $HOME/.local/share/fastfetch >/dev/null 2>&1
|
||||
exec $SHELL
|
||||
fi
|
||||
command clear &
|
||||
fastfetch
|
||||
fi
|
||||
|
||||
if command_exists cowsay; then
|
||||
alias clear='clear && cowsay -f tux "$(uptime --pretty)"'
|
||||
cowsay -f tux "$(uptime --pretty)"
|
||||
fi
|
||||
}
|
||||
|
||||
# ╭────────╮
|
||||
|
@ -471,26 +390,6 @@ _coding_() {
|
|||
alias h='hugo'
|
||||
alias hs='hugo server -D --noHTTPCache --disableFastRender'
|
||||
fi
|
||||
# ─< c stands for bin/cake >──────────────────────────────────────────────────────────────
|
||||
alias cake='bin/cake'
|
||||
alias c='cake'
|
||||
alias cs='c server -p 1313'
|
||||
# ─< VSCodium >─────────────────────────────────────────────────────────────────────────────
|
||||
if command_exists codium; then
|
||||
alias code="codium"
|
||||
export EDITOR="codium"
|
||||
fi
|
||||
# ─< neovide, the best frontend for any neovim-config >───────────────────────────────────
|
||||
if [ -d "$HOME/.local/share/neovide/" ]; then
|
||||
if command_exists neovide; then
|
||||
alias nvim='neovide --fork'
|
||||
else
|
||||
neovide_path=$(find "$HOME/" -name "*neovide*.appimage" 2>/dev/null)
|
||||
if [ -n "$neovide_path" ]; then
|
||||
alias nvim="$neovide_path --fork"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
# Function to get the IP address
|
||||
get_ip() {
|
||||
ip a | grep 'inet ' | grep -v '127.0.0.1' | awk '{print $2}' | cut -d/ -f1 | head -n 1
|
||||
|
@ -508,79 +407,65 @@ _coding_() {
|
|||
}
|
||||
|
||||
get_packager() {
|
||||
PKG=""
|
||||
install=""
|
||||
update=""
|
||||
refresh=""
|
||||
remove=""
|
||||
source /etc/os-release
|
||||
. /etc/os-release
|
||||
case "$ID" in
|
||||
ubuntu | debian)
|
||||
# Debian-based
|
||||
ubuntu | debian | pop | kali | zorin | rhinoh)
|
||||
if command_exists nala; then
|
||||
PKG="nala"
|
||||
install="nala install --assume-yes"
|
||||
upgrade="nala upgrade"
|
||||
refresh="nala update"
|
||||
clean="nala autoremove --assume-yes"
|
||||
remove="nala remove"
|
||||
purge="nala purge"
|
||||
search="nala search"
|
||||
alias update="$_sudo $refresh && $_sudo $upgrade"
|
||||
alias install="$_sudo $refresh && $_sudo $install"
|
||||
alias remove="$_sudo $purge"
|
||||
alias search="$search"
|
||||
elif command_exists apt-get; then
|
||||
PKG="apt-get"
|
||||
install="apt-get install --yes"
|
||||
upgrade="apt-get upgrade"
|
||||
refresh="apt-get update"
|
||||
clean="apt-get autoremove"
|
||||
remove="apt-get remove"
|
||||
purge="apt-get purge"
|
||||
search="apt-cache search"
|
||||
alias update="$_sudo $refresh && $_sudo $upgrade"
|
||||
alias install="$_sudo $refresh && $_sudo $install"
|
||||
alias remove="$_sudo $purge"
|
||||
alias search="$search"
|
||||
alias search="nala search"
|
||||
alias install="$_sudo nala install --assume-yes"
|
||||
alias update="$_sudo nala update && $_sudo nala upgrade --full"
|
||||
alias remove="$_sudo nala purge"
|
||||
else
|
||||
alias search="apt-cache search"
|
||||
alias install="$_sudo apt-get install --yes"
|
||||
alias update="$_sudo apt-get update && $_sudo apt-get upgrade"
|
||||
alias remove="$_sudo apt-get purge"
|
||||
fi
|
||||
alias unbreak="$_sudo dpkg --configure -a"
|
||||
;;
|
||||
arch | manjaro | endevouros)
|
||||
if command_exists yay; then
|
||||
PKG="yay"
|
||||
alias install="yay -S --noconfirm"
|
||||
alias update="yay -Syu"
|
||||
alias remove="yay -R"
|
||||
alias search="yay -Ss"
|
||||
elif command_exists paru; then
|
||||
PKG="paru"
|
||||
|
||||
# Arch-based
|
||||
arch | manjaro | endevouros | garuda)
|
||||
if command_exists paru; then
|
||||
alias search="paru -Ss"
|
||||
alias install="paru -S --noconfirm"
|
||||
alias update="paru -Syu"
|
||||
alias remove="paru -R"
|
||||
alias search="paru -Ss"
|
||||
elif command_exists pacman; then
|
||||
PKG="pacman"
|
||||
elif command_exists yay; then
|
||||
alias search="yay -Ss"
|
||||
alias install="yay -S --noconfirm"
|
||||
alias update="yay -Syu"
|
||||
alias remove="yay -R"
|
||||
else
|
||||
alias search="$_sudo pacman -Ss"
|
||||
alias install="$_sudo pacman -S --noconfirm"
|
||||
alias update="$_sudo pacman -Syu"
|
||||
alias remove="$_sudo pacman -R"
|
||||
alias search="$_sudo pacman -Ss"
|
||||
fi
|
||||
;;
|
||||
|
||||
# RHEL-based
|
||||
fedora | centos)
|
||||
PKG="dnf"
|
||||
alias install="dnf install --yes"
|
||||
alias update="dnf update"
|
||||
alias remove="dnf remove"
|
||||
alias search="dnf search"
|
||||
alias install="$_sudo dnf install"
|
||||
alias update="$_sudo dnf update"
|
||||
alias remove="$_sudo dnf remove"
|
||||
;;
|
||||
|
||||
# openSUSE
|
||||
opensuse-*)
|
||||
alias search="zypper search"
|
||||
alias install="$_sudo zypper install --no-confirm"
|
||||
alias update="$_sudo zypper update"
|
||||
alias remove="$_sudo zypper remove"
|
||||
;;
|
||||
|
||||
# Alpine
|
||||
alpine)
|
||||
PKG="apk"
|
||||
install="apk add"
|
||||
update="apk update"
|
||||
upgrade="apk upgrade"
|
||||
remove="apk del"
|
||||
alias install="$_sudo $install"
|
||||
alias update="$_sudo $update && $_sudo $upgrade"
|
||||
alias remove="$_sudo $remove"
|
||||
alias install="$_sudo apk add"
|
||||
alias update="$_sudo apk update && $_sudo apk upgrade"
|
||||
alias remove="$_sudo apk del"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
@ -589,18 +474,7 @@ get_alias() {
|
|||
_defaults_
|
||||
_sensible.bash_
|
||||
_color_prompt_
|
||||
_cli_qol_
|
||||
_cat_
|
||||
_trash
|
||||
_nmap_
|
||||
_tmux_
|
||||
_docker_
|
||||
_git_
|
||||
_rsync_
|
||||
_zoxide_
|
||||
_fancy_ls_
|
||||
_coding_
|
||||
_fetches_
|
||||
_games
|
||||
}
|
||||
|
||||
|
@ -608,8 +482,7 @@ main() {
|
|||
check_root
|
||||
get_packager
|
||||
get_alias
|
||||
# plugins
|
||||
# tools
|
||||
# [[ ${BLE_VERSION-} ]] && ble-attach
|
||||
_end
|
||||
}
|
||||
|
||||
main
|
||||
|
|
|
@ -1,50 +0,0 @@
|
|||
# qfc
|
||||
Quick Command-line File Completion
|
||||
|
||||

|
||||
|
||||
qfc is a shell auto-complete alternative which features real-time multi-directories matching: It provides results while you type against files in the current directory and its sub-directories.
|
||||
This is useful, to avoid the burden of writing the whole path whenever you want to `cd` or `vim` a file, which is frequent especially if you use the terminal as your IDE(The terminal is the best IDE, remember! :-) ).
|
||||
|
||||
|
||||
## Features:
|
||||
- Real-time matching: Results are displayed while you type.
|
||||
- Multi-directories && Context relevant matching: if you're in a cvs(git,mercurial) managed directory, qfc will matches against your tracked(or new) files only. This is very useful to avoid 10000+ of dependency files cluttering up the results. for unmanaged dirs, qfc looks for unhidden files up to a maximum depth(set to 3).
|
||||
- Enhanced Filtering/Sorting of matches.
|
||||
- No dependencies.
|
||||
|
||||
|
||||
## Requirements
|
||||
- python (2.7+ or 3.0+)
|
||||
- Bash-4.0+ or Zshell.
|
||||
- Linux Or OSX
|
||||
In OSX, it seems like Bash 3.x is the default shell which is not supported. you have to [update your Bash to 4.0+](http://apple.stackexchange.com/a/24635) or [change your shell to zshell](http://stackoverflow.com/a/1822126/1117720).
|
||||
|
||||
|
||||
## Installation:
|
||||
- `git clone https://github.com/pindexis/qfc $HOME/.qfc`
|
||||
- Add the following line to your *rc (.zshrc, .bashrc, .bash_profile in OSX):
|
||||
`[[ -s "$HOME/.qfc/bin/qfc.sh" ]] && source "$HOME/.qfc/bin/qfc.sh"`
|
||||
|
||||
|
||||
## Usage:
|
||||
- `Ctrl-f` : complete the word under cursor using qfc
|
||||
- while qfc is open:
|
||||
- `TAB`: Append the selected match to the current path.
|
||||
- `ENTER`: Append the selected match to the current path and returns the result.
|
||||
- `Ctrl-f`: Returns the current path.
|
||||
- `Arrow keys`: Navigation between files.
|
||||
|
||||
|
||||
## Even more Productivity:
|
||||
If you're using zshell or Bash 4.3+, You can combine qfc with commands you frequently use to get one key-stroke experience. For example, I have the following lines in my .zshrc:
|
||||
```
|
||||
qfc_quick_command 'cd' '\C-b' 'cd $0'
|
||||
qfc_quick_command 'vim' '\C-p' 'vim $0'
|
||||
```
|
||||
This allows me to switch directories by just pressing Ctrl-b(or editing a file by pressing Ctrl-p).
|
||||

|
||||
|
||||
`qfc_quick_command` expects an `id`, `a shortcut`, and a command with `$0` placeholder(which will be replaced with the completion path).
|
||||
It's recommended to choose a 2-5 length letters only `id`(else you may encounter issues).
|
||||
Also, be careful with what keyboard shortcuts to choose(mapping some keys can prevent the terminal from working correctly).
|
33
.qfc/bin/qfc
33
.qfc/bin/qfc
|
@ -1,33 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
import sys
|
||||
import argparse
|
||||
import os
|
||||
sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)),'..'))
|
||||
from qfc import core
|
||||
|
||||
def parse_arguments():
|
||||
parser = argparse.ArgumentParser(description="Qfc: Quick command-line file competion")
|
||||
|
||||
parser.add_argument("--search", type=str, help="Search string")
|
||||
parser.add_argument("--stdout", type=str, help="Where to store result(defaut to stdout)")
|
||||
parser.set_defaults(func=cmd_get)
|
||||
|
||||
args = parser.parse_args()
|
||||
return args.func(args)
|
||||
|
||||
def cmd_get(args):
|
||||
output = core.get_selected_command_or_input(args.search)
|
||||
if not output:
|
||||
output = ""
|
||||
if args.stdout:
|
||||
with open(args.stdout,'w+') as save_file:
|
||||
# the newline character is to make sure 'wc -l' executes correctly
|
||||
if output:
|
||||
output+="\n"
|
||||
save_file.write(output)
|
||||
else:
|
||||
print(output)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parse_arguments()
|
126
.qfc/bin/qfc.sh
126
.qfc/bin/qfc.sh
|
@ -1,126 +0,0 @@
|
|||
# default key bindings
|
||||
complete_shortcut="${qfc_complete_SHORTCUT:-\C-f}"
|
||||
|
||||
function get_cursor_position(){
|
||||
# based on a script from http://invisible-island.net/xterm/xterm.faq.html
|
||||
exec < /dev/tty
|
||||
oldstty=$(stty -g)
|
||||
stty raw -echo min 0
|
||||
# on my system, the following line can be replaced by the line below it
|
||||
echo -en "\033[6n" > /dev/tty
|
||||
# tput u7 > /dev/tty # when TERM=xterm (and relatives)
|
||||
IFS=';' read -r -d R row col
|
||||
stty $oldstty
|
||||
# change from one-based to zero based so they work with: tput cup $row $col
|
||||
row=$((${row:2} - 1)) # strip off the esc-[
|
||||
col=$((${col} - 1))
|
||||
echo "$row $col"
|
||||
}
|
||||
|
||||
if [[ -d ~/.qfc/ ]]; then
|
||||
export PATH=~/.qfc/bin:"${PATH}"
|
||||
fi
|
||||
|
||||
if [[ -n "$ZSH_VERSION" ]]; then
|
||||
# zshell
|
||||
function qfc_complete {
|
||||
# Add a letter and remove it from the buffer.
|
||||
# when using zsh autocomplete(pressing Tab), then running qfc, the BUFFER(qfc input) won't contain the trailing forward slash(which should happen when using zsh autocomplete for directories).
|
||||
# pressing a character then removing it makes sure that BUFFER contains what you see on the screen.
|
||||
BUFFER=${BUFFER}'a'
|
||||
BUFFER=${BUFFER[0,-2]}
|
||||
# get the cursor offset within the user input
|
||||
offset=${CURSOR}
|
||||
zle beginning-of-line
|
||||
# get the offset from the start of comandline prompt
|
||||
col=$(echo $(get_cursor_position) | cut -f 2 -d " ")
|
||||
# place the cursor at the next line
|
||||
</dev/tty echo ''
|
||||
|
||||
# get the word under cursor
|
||||
word=${BUFFER[0,offset]}
|
||||
word=${word##* }
|
||||
|
||||
# instruct qfc to store the result (completion path) into a temporary file
|
||||
tmp_file=$(mktemp -t qfc.XXXXXXX)
|
||||
</dev/tty qfc --search="$word" --stdout="$tmp_file"
|
||||
result=$(<$tmp_file)
|
||||
rm -f $tmp_file
|
||||
|
||||
# append the completion path to the user buffer
|
||||
word_length=${#word}
|
||||
result_length=${#result}
|
||||
BUFFER=${BUFFER[1,$((offset-word_length))]}${result}${BUFFER[$((offset+word_length)),-1]}
|
||||
let "offset = offset - word_length + result_length"
|
||||
|
||||
# reset the absolute and relative cursor position, note that it's necessary to get row position after qfc is run, because it may be changed during qfc execution
|
||||
row=$(echo $(get_cursor_position) | cut -f 1 -d " ")
|
||||
tput cup $(($row - 1)) $col
|
||||
CURSOR=${offset}
|
||||
}
|
||||
|
||||
zle -N qfc_complete
|
||||
bindkey "$complete_shortcut" qfc_complete
|
||||
|
||||
function qfc_quick_command(){
|
||||
if [[ ! -z $1 ]] && [[ ! -z $2 ]] && [[ ! -z $3 ]]; then
|
||||
func_name='quick_'$1
|
||||
eval $"function $func_name(){
|
||||
zle kill-whole-line
|
||||
qfc_complete
|
||||
if [[ ! -z \${BUFFER} ]]; then
|
||||
c='$3'
|
||||
BUFFER=\${c//'\$0'/\$BUFFER}
|
||||
zle accept-line
|
||||
fi
|
||||
}"
|
||||
zle -N $func_name
|
||||
bindkey "$2" $func_name
|
||||
fi
|
||||
}
|
||||
|
||||
elif [[ -n "$BASH" ]]; then
|
||||
|
||||
function qfc_complete {
|
||||
# pretty similar to zsh flow
|
||||
offset=${READLINE_POINT}
|
||||
READLINE_POINT=0
|
||||
col=$(get_cursor_position | cut -f 2 -d " ")
|
||||
|
||||
word=${READLINE_LINE:0:offset}
|
||||
word=${word##* }
|
||||
|
||||
tmp_file=$(mktemp -t qfc.XXXXXXX)
|
||||
</dev/tty qfc --search="$word" --stdout="$tmp_file"
|
||||
result=$(<$tmp_file)
|
||||
rm -f $tmp_file
|
||||
|
||||
word_length=${#word}
|
||||
result_length=${#result}
|
||||
READLINE_LINE=${READLINE_LINE:0:$((offset-word_length))}${result}${READLINE_LINE:$((offset))}
|
||||
offset=$(($offset - $word_length + $result_length))
|
||||
|
||||
row=$(get_cursor_position | cut -f 1 -d " ")
|
||||
tput cup $row $col
|
||||
READLINE_POINT=${offset}
|
||||
}
|
||||
|
||||
bind -x '"'"$complete_shortcut"'":"qfc_complete"'
|
||||
|
||||
function qfc_quick_command {
|
||||
if [[ ! -z $1 ]] && [[ ! -z $2 ]] && [[ ! -z $3 ]]; then
|
||||
func_name='quick_'$1
|
||||
eval $"function $func_name(){
|
||||
READLINE_LINE=''
|
||||
qfc_complete
|
||||
if [[ ! -z \${READLINE_LINE} ]]; then
|
||||
c='$3'
|
||||
READLINE_LINE=\${c//'\$0'/\$READLINE_LINE}
|
||||
fi
|
||||
}"
|
||||
bind -x '"\e-'"$1"'":"'"${func_name}"'"'
|
||||
bind '"'"$2"'":""\e-'"$1"'\n"'
|
||||
fi
|
||||
}
|
||||
|
||||
fi
|
|
@ -1,60 +0,0 @@
|
|||
import sys
|
||||
|
||||
BOLD = "\x1b[1m"
|
||||
CLEAR_FORMATTING = "\x1b[0m"
|
||||
ERASE_SCREEN = "\x1b[J"
|
||||
ERASE_LINE = "\x1b[2K"
|
||||
FOREGROUND_BLACK = "\x1b[30m"
|
||||
BACKGROUND_WHITE = "\x1b[47m"
|
||||
|
||||
def _CURSOR_COLUMN(pos):
|
||||
# ideally, CSI n G escape code is used to set the absolute horizental position
|
||||
# Sadly, it's not an Ansi.sys escape code (not supported in all terminals)
|
||||
# This shim try to simulate it by moving cursor backwards 1000 characters(terminal row width is assumed to be less than that number, which may not be the case for aliens laptops :))
|
||||
# Then, move cursor pos - 1 characthers forward (the - 1 is because the cursor is at position 1)
|
||||
c = "\x1b[1000D"
|
||||
if pos:
|
||||
c += "\x1b["+str(pos - 1)+"C"
|
||||
return c
|
||||
|
||||
def _CURSOR_PREVIOUS_LINES(number):
|
||||
return "\x1b["+str(number)+"A"
|
||||
|
||||
def _CURSOR_NEXT_LINES(number):
|
||||
return "\x1b["+str(number)+"B"
|
||||
|
||||
def select_text(text):
|
||||
return (FOREGROUND_BLACK +
|
||||
BACKGROUND_WHITE +
|
||||
text.replace(
|
||||
CLEAR_FORMATTING,
|
||||
CLEAR_FORMATTING + FOREGROUND_BLACK + BACKGROUND_WHITE)+
|
||||
CLEAR_FORMATTING)
|
||||
|
||||
def bold_text(text):
|
||||
return (BOLD +
|
||||
text.replace(
|
||||
CLEAR_FORMATTING,
|
||||
CLEAR_FORMATTING + BOLD)+
|
||||
CLEAR_FORMATTING)
|
||||
|
||||
def move_cursor_line_beggining():
|
||||
sys.stdout.write(_CURSOR_COLUMN(0))
|
||||
|
||||
def move_cursor_horizental(n):
|
||||
sys.stdout.write(_CURSOR_COLUMN(n))
|
||||
|
||||
def move_cursor_previous_lines(number_of_lines):
|
||||
sys.stdout.write(_CURSOR_PREVIOUS_LINES(number_of_lines))
|
||||
|
||||
def move_cursor_next_lines(number_of_lines):
|
||||
sys.stdout.write(_CURSOR_NEXT_LINES(number_of_lines))
|
||||
|
||||
def erase_from_cursor_to_end():
|
||||
sys.stdout.write(ERASE_SCREEN)
|
||||
|
||||
def erase_line():
|
||||
sys.stdout.write(ERASE_LINE)
|
||||
|
||||
def flush():
|
||||
sys.stdout.flush()
|
231
.qfc/qfc/core.py
231
.qfc/qfc/core.py
|
@ -1,231 +0,0 @@
|
|||
import os
|
||||
import re
|
||||
from . import keys
|
||||
from . import ui
|
||||
from . import readchar
|
||||
from . import dirhandler
|
||||
|
||||
|
||||
def get_selected_command_or_input(search):
|
||||
state = State(search)
|
||||
# draw the screen (prompt + matched strings)
|
||||
ui.refresh(state)
|
||||
# wait for user input
|
||||
prompt(state)
|
||||
# clear the screen
|
||||
ui.erase()
|
||||
# state.input holds the path selected by the user
|
||||
return state.input
|
||||
|
||||
|
||||
def prompt(state):
|
||||
while True:
|
||||
c = readchar.get_symbol()
|
||||
if c == keys.ENTER:
|
||||
if state.get_matches():
|
||||
state.append_match_to_input()
|
||||
break
|
||||
elif c == keys.CTRL_F:
|
||||
break
|
||||
elif c == keys.TAB:
|
||||
if state.get_matches():
|
||||
state.append_match_to_input()
|
||||
elif c == keys.CTRL_C or c == keys.ESC:
|
||||
state.reset_input()
|
||||
break
|
||||
elif c == keys.CTRL_U:
|
||||
state.clear_input()
|
||||
elif c == keys.BACKSPACE:
|
||||
state.set_input(state.input[0:-1])
|
||||
elif c == keys.UP or c == keys.CTRL_K:
|
||||
state.select_previous()
|
||||
elif c == keys.DOWN or c == keys.CTRL_J:
|
||||
state.select_next()
|
||||
elif c == keys.LEFT or c == keys.CTRL_H:
|
||||
state.go_back()
|
||||
elif c == keys.RIGHT or c == keys.CTRL_L:
|
||||
if state.get_matches():
|
||||
state.append_match_to_input()
|
||||
elif c < 256 and c >= 32:
|
||||
state.set_input(state.input + chr(c))
|
||||
ui.refresh(state)
|
||||
|
||||
|
||||
class State(object):
|
||||
''' The Current User state, including user written characters, matched commands, and selected one '''
|
||||
|
||||
def __init__(self, default_input):
|
||||
self._selected_command_index = 0
|
||||
self.matches = []
|
||||
self.default_input = default_input
|
||||
self.set_input(default_input)
|
||||
|
||||
def get_matches(self):
|
||||
return self.matches
|
||||
|
||||
def reset_input(self):
|
||||
self.input = self.default_input
|
||||
|
||||
def set_input(self, input):
|
||||
self.input = input if input else ""
|
||||
self._update()
|
||||
|
||||
def append_match_to_input(self):
|
||||
self.set_input(os.path.join(os.path.dirname(self.input), self.get_selected_match()))
|
||||
|
||||
def go_back(self):
|
||||
isdir = is_dir(self.input)
|
||||
input_stripped = self.input.rstrip(os.sep)
|
||||
if not input_stripped:
|
||||
return
|
||||
input_splitted = input_stripped.split(os.sep)
|
||||
entry_name = input_splitted[-1]
|
||||
if isdir:
|
||||
entry_name += os.sep
|
||||
new_input = os.sep.join(input_splitted[0:-1])
|
||||
if new_input:
|
||||
new_input += os.sep
|
||||
self.set_input(new_input)
|
||||
self.set_selected_entry(entry_name)
|
||||
|
||||
def clear_input(self):
|
||||
self.set_input("")
|
||||
|
||||
def clear_selection(self):
|
||||
self._selected_command_index = 0
|
||||
|
||||
def select_next(self):
|
||||
self._selected_command_index = (self._selected_command_index + 1) % len(self.matches) if len(self.matches) else 0
|
||||
|
||||
def select_previous(self):
|
||||
self._selected_command_index = (self._selected_command_index - 1) % len(self.matches) if len(self.matches) else 0
|
||||
|
||||
def _update(self):
|
||||
self.matches = get_matches(os.getcwd(),self.input)
|
||||
self._selected_command_index = 0
|
||||
|
||||
def get_output(self):
|
||||
return self.input
|
||||
|
||||
def get_selected_match(self):
|
||||
if len(self.matches):
|
||||
return self.matches[self._selected_command_index]
|
||||
else:
|
||||
raise Exception('No matches found')
|
||||
|
||||
def set_selected_entry(self, entry):
|
||||
if not (entry in self.matches):
|
||||
return
|
||||
self._selected_command_index = self.matches.index(entry)
|
||||
|
||||
|
||||
def get_matches(root_dir, user_input):
|
||||
start_dir = join_paths(root_dir, os.path.dirname(user_input))
|
||||
search_str = os.path.basename(user_input)
|
||||
if not os.path.isdir(start_dir):
|
||||
return []
|
||||
source_files = dirhandler.get_source_files(start_dir)
|
||||
filtered_files = filter_files(source_files, search_str)
|
||||
sorted_files = sort_matches(filtered_files, search_str)
|
||||
return sorted_files
|
||||
|
||||
|
||||
def filter_files(files, search_str):
|
||||
""" Filter a list of files based on a search string
|
||||
:param files: list of files to filter (ie ['/a','/a/b', '/a/b/c', '/b']), the order doesn't matter. 'a' and 'a/' are considered different.
|
||||
:param search_str, the filtering string (ie 'a')
|
||||
|
||||
This function return only first level files/dirs that match the given search string. That is, filtering ['/a','/a/b','/a/b/c'] with the string 'a' returns only ['/a'],
|
||||
This is to avoid polluting the screen with all files inside a directory when a user look for that directory
|
||||
"""
|
||||
matched = set()
|
||||
if not search_str:
|
||||
for f in files:
|
||||
# right strip to the first seperator(ie '/')
|
||||
f = f[:_index_or_len(f, os.sep)+1]
|
||||
matched.add(f)
|
||||
else:
|
||||
search_str = search_str.lower()
|
||||
for f in files:
|
||||
if search_str in f.lower():
|
||||
index = f.lower().index(search_str)
|
||||
trail = f[index:]
|
||||
f = f[:index + _index_or_len(trail, os.sep)+1]
|
||||
matched.add(f)
|
||||
return matched
|
||||
|
||||
def sort_matches(matches, string):
|
||||
""" Sort paths according to a string """
|
||||
files = sorted(matches, key=lambda s: s.lower())
|
||||
return sorted(files, key=lambda p:get_weight(p, string))
|
||||
|
||||
def get_weight(path, string):
|
||||
""" calculate how much a path matches a given string on a scale of 0->10000, a lower number means a better match.
|
||||
The string should be present in the path, or this function will fail
|
||||
|
||||
The weight is calculated using the following formula:
|
||||
0 00 0
|
||||
| | |
|
||||
Number of elements in the path <----------------| | |--------------------------> is a directory(0) or not(1)
|
||||
(ie 2 for 'aa/bb' and 3 for 'aa/bb/cc') |
|
||||
|
|
||||
v
|
||||
There is a word in the matched path element that exactly match the given string? ('aa_bb' matches user input 'aa' but doesn't match 'a')
|
||||
|
|
||||
No <--------------------|-----------------------------> yes
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
v v
|
||||
path element starts with the user input? There is only one word in the matched path element ('aa' but not 'aa_bb'),
|
||||
| which means the path elements exactly match the string
|
||||
| |
|
||||
No <-------|-----Yes(2) No -----|----- Yes (0)
|
||||
| |
|
||||
v v
|
||||
index of the string in the path element + 10 (index of the matched word within path element words + 1)
|
||||
|
||||
|
||||
Maximums are added to make sure things doesn't overlap
|
||||
"""
|
||||
weight = 0
|
||||
if not is_dir(path):
|
||||
weight += 1
|
||||
|
||||
string = string.lower()
|
||||
p = path.rstrip(os.sep).lower()
|
||||
path_elems = p.split(os.sep)
|
||||
weight += min(len(path_elems) * 1000, 10000) # Max 10 elements in path(set it to 10 for longer paths)
|
||||
|
||||
if not string:
|
||||
return weight
|
||||
|
||||
elm = next(e for e in path_elems if string in e)
|
||||
elm_words = [_f for _f in re.split('[\W_]+', elm) if _f]
|
||||
if string in elm_words:
|
||||
if len(elm_words) > 1:
|
||||
weight += 10 * (min(elm_words.index(string),8) + 1) # Max 8 words per path entry
|
||||
else:
|
||||
if elm.startswith(string):
|
||||
weight += 10 * 2
|
||||
else:
|
||||
weight += 10 * (10 + min(elm.index(string), 89)) # Max path element with 89 characters
|
||||
return weight
|
||||
|
||||
# Helper functions:
|
||||
|
||||
def join_paths(p1, p2):
|
||||
p1 = (os.path.expandvars(os.path.expanduser(p1)))
|
||||
p2 = (os.path.expandvars(os.path.expanduser(p2)))
|
||||
return os.path.normpath(os.path.join(p1, p2))
|
||||
|
||||
def _index_or_len(s, c):
|
||||
if c in s:
|
||||
return s.index(c)
|
||||
else:
|
||||
return len(s)
|
||||
|
||||
def is_dir(p):
|
||||
if p.endswith(os.sep):
|
||||
return True
|
||||
return False
|
|
@ -1,114 +0,0 @@
|
|||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
class CVSHandler():
|
||||
""" Handler of CVS (fir, mercurial...) directories,
|
||||
The main purpose of this class is to cache external cvs command output, and determine the appropriate files to yield when navigating to a subdirectory of a project.
|
||||
This basically means that the external command is run once (ie git ls-files), cached, and when calling get_source_files on a subdirectory of the project root (ie project-root/subdir),
|
||||
filtering from all project files of is done here.
|
||||
"""
|
||||
def __init__(self, cvs):
|
||||
self._roots_cache = {}
|
||||
self._not_tracked_cache = set()
|
||||
self.cvs = cvs
|
||||
|
||||
def _get_root_from_cache(self, directory):
|
||||
""" a directory is considered cached if it's the project root or a subdirectory of that project root.
|
||||
returns the project root dir, or None if the directory is not cached.
|
||||
"""
|
||||
if directory in self._roots_cache:
|
||||
return directory
|
||||
if os.path.dirname(directory) == directory:
|
||||
return None
|
||||
return self._get_root_from_cache(os.path.dirname(directory))
|
||||
|
||||
def get_source_files(self, directory):
|
||||
if directory in self._not_tracked_cache:
|
||||
return None
|
||||
|
||||
root_dir = self._get_root_from_cache(directory)
|
||||
if not root_dir:
|
||||
try:
|
||||
# check if it's a tracked cvs dir, if yes, get the project root and the source files
|
||||
root_dir = self.cvs._get_root(directory)
|
||||
self._roots_cache[root_dir] = self.cvs._get_tracked_files(root_dir)
|
||||
except Exception as e:
|
||||
# not a cvs tracked dir, save it to not issue that command again
|
||||
self._not_tracked_cache.add(directory)
|
||||
return None
|
||||
|
||||
files = self._roots_cache[root_dir]
|
||||
# the passed directory argument is a subdirectory of the project root
|
||||
if directory != root_dir:
|
||||
rel_dir = os.path.relpath(directory, root_dir)
|
||||
files = [f[len(rel_dir)+1:] for f in files if f.startswith(rel_dir)]
|
||||
return files
|
||||
|
||||
|
||||
class Git():
|
||||
@staticmethod
|
||||
def _get_root(directory):
|
||||
return run_command("cd %s && git rev-parse --show-toplevel" % directory).strip()
|
||||
@staticmethod
|
||||
def _get_tracked_files(directory):
|
||||
return run_command("cd %s && git ls-files && git ls-files --others --exclude-standard" % directory).strip().split('\n')
|
||||
|
||||
class Mercurial():
|
||||
@staticmethod
|
||||
def _get_root(directory):
|
||||
return run_command("cd %s && hg root" % directory).strip()
|
||||
@staticmethod
|
||||
def _get_tracked_files(directory):
|
||||
return run_command("cd %s && (hg status -marcu | cut -d' ' -f2)" % directory).strip().split('\n')
|
||||
|
||||
class DefaultDirHandler():
|
||||
""" The default directory handler uses the 'find' external program to return all the files inside a given directory up to MAX_depth depth (ie, if maxdepth=2, returns all files inside that dir, and all files in a subdir of that directory)"""
|
||||
|
||||
def __init__(self):
|
||||
self._cache = {}
|
||||
self.MAX_DEPTH = 3
|
||||
|
||||
def _walk_down(self, start_dir):
|
||||
try:
|
||||
out = run_command("find %s -maxdepth %s -type f -not -path '*/\.*'" % (start_dir, self.MAX_DEPTH))
|
||||
except subprocess.CalledProcessError as e:
|
||||
# Find returns a non 0 exit status if listing a directory fails (for example, permission denied), but still output all files in other dirs
|
||||
# ignore those failed directories.
|
||||
out = e.output
|
||||
if sys.version_info >= (3, 0):
|
||||
out = out.decode('utf-8')
|
||||
if not out:
|
||||
return []
|
||||
files = out.split('\n')
|
||||
return [os.path.relpath(f, start_dir) for f in files if f]
|
||||
|
||||
def get_source_files(self, start_dir):
|
||||
if not start_dir in self._cache:
|
||||
self._cache[start_dir] = self._walk_down(start_dir)
|
||||
return self._cache[start_dir]
|
||||
|
||||
def run_command(string):
|
||||
''' fork a process to execute the command string given as argument, returning the string written to STDOUT '''
|
||||
DEVNULL = open(os.devnull, 'wb')
|
||||
out = subprocess.check_output(string, stderr=DEVNULL, shell=True)
|
||||
if sys.version_info >= (3, 0):
|
||||
return out.decode('utf-8')
|
||||
return out
|
||||
|
||||
|
||||
git = CVSHandler(Git)
|
||||
hg = CVSHandler(Mercurial)
|
||||
default = DefaultDirHandler()
|
||||
|
||||
def get_source_files(directory):
|
||||
""" check first if the given directory is inside a git tracked project, if no, check with mercurial, if no, fallback to the default handler """
|
||||
files = git.get_source_files(directory)
|
||||
# if the returned files list is empty, it's considered not a tracked directory
|
||||
if files:
|
||||
return files
|
||||
files = hg.get_source_files(directory)
|
||||
if files:
|
||||
return files
|
||||
return default.get_source_files(directory)
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
CTRL_C = 3 # Ctrl-c
|
||||
CTRL_H = 8 # ctrl-h
|
||||
CTRL_J = 10 # ctrl-h
|
||||
CTRL_K = 11 # ctrl-h
|
||||
CTRL_L = 12 # ctrl-h
|
||||
CTRL_F = 6 # ctrl-f
|
||||
ENTER = 13 # Enter
|
||||
CTRL_U = 21 # Ctrl+u
|
||||
ESC = 27 # Escape
|
||||
BACKSPACE = 127 # Backspace
|
||||
TAB = 9 # Tab
|
||||
RIGHT = -1 # FAKE CODE to abstract away the fact that a multibyte string is needed to represent arrow keys
|
||||
DOWN = -2 # same
|
||||
UP = -3 # same
|
||||
LEFT = -4 # same
|
||||
SHIFTTAB = -5 # same
|
||||
SHIFTENTER = -6
|
||||
SPACE = 32
|
||||
ANTISLASH = 47
|
|
@ -1,65 +0,0 @@
|
|||
import sys
|
||||
import tty
|
||||
import termios
|
||||
import fcntl
|
||||
import os
|
||||
from . import keys
|
||||
|
||||
|
||||
def get_symbol():
|
||||
''' Read a symbol, which can be a single byte character or a multibyte string'''
|
||||
ch = read_char()
|
||||
ch_code = ord(ch)
|
||||
# check for multibyte string
|
||||
if ch_code == keys.ESC:
|
||||
ch = read_char_no_blocking()
|
||||
if ch == '':
|
||||
# ESC key pressed
|
||||
return keys.ESC
|
||||
elif ch != 'O' and ch != '[':
|
||||
return ord(ch)
|
||||
else:
|
||||
ch = read_char_no_blocking()
|
||||
if ch == 'A':
|
||||
return keys.UP
|
||||
elif ch == 'B':
|
||||
return keys.DOWN
|
||||
elif ch == 'C':
|
||||
return keys.RIGHT
|
||||
elif ch == 'D':
|
||||
return keys.LEFT
|
||||
elif ch == 'Z':
|
||||
return keys.SHIFTTAB
|
||||
return ch_code
|
||||
|
||||
|
||||
def read_char():
|
||||
''' Read a character '''
|
||||
fd = sys.stdin.fileno()
|
||||
old_settings = termios.tcgetattr(fd)
|
||||
try:
|
||||
tty.setraw(fd, termios.TCSADRAIN)
|
||||
ch = sys.stdin.read(1)
|
||||
finally:
|
||||
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
|
||||
return ch
|
||||
|
||||
|
||||
def read_char_no_blocking():
|
||||
''' Read a character in nonblocking mode, if no characters are present in the buffer, return an empty string '''
|
||||
fd = sys.stdin.fileno()
|
||||
old_settings = termios.tcgetattr(fd)
|
||||
old_flags = fcntl.fcntl(fd, fcntl.F_GETFL)
|
||||
try:
|
||||
tty.setraw(fd, termios.TCSADRAIN)
|
||||
fcntl.fcntl(fd, fcntl.F_SETFL, old_flags | os.O_NONBLOCK)
|
||||
return sys.stdin.read(1)
|
||||
except IOError as e:
|
||||
ErrorNumber = e[0]
|
||||
# IOError with ErrorNumber 11(35 in Mac) is thrown when there is nothing to read(Resource temporarily unavailable)
|
||||
if (sys.platform.startswith("linux") and ErrorNumber != 11) or (sys.platform == "darwin" and ErrorNumber != 35):
|
||||
raise
|
||||
return ""
|
||||
finally:
|
||||
fcntl.fcntl(fd, fcntl.F_SETFL, old_flags)
|
||||
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
|
|
@ -1,60 +0,0 @@
|
|||
from . import ansi
|
||||
import os
|
||||
import math
|
||||
|
||||
def _get_terminal_columns():
|
||||
''' get the number of terminal columns, used to determine spanned lines of a mark(reqansired for cursor placement) '''
|
||||
_, columns = os.popen('stty size', 'r').read().split()
|
||||
return int(columns)
|
||||
|
||||
def erase():
|
||||
''' the commandline cursor is always at the first line (user prompt)
|
||||
Therefore, erasing the current and following lines clear all output
|
||||
'''
|
||||
ansi.move_cursor_line_beggining()
|
||||
ansi.erase_from_cursor_to_end()
|
||||
|
||||
def refresh(state):
|
||||
''' Redraw the output, this function will be triggered on every user interaction(key pressed)'''
|
||||
erase()
|
||||
lines, num_rows = _construct_output(state)
|
||||
for line in lines:
|
||||
print(line)
|
||||
# go up
|
||||
ansi.move_cursor_previous_lines(num_rows)
|
||||
# palce the cursor at the end of first line)
|
||||
ansi.move_cursor_horizental(len(lines[0])+1)
|
||||
ansi.flush()
|
||||
|
||||
def _construct_output(state):
|
||||
columns = _get_terminal_columns()
|
||||
def number_of_rows(line):
|
||||
return int(math.ceil(float(len(line))/columns))
|
||||
displayed_lines = []
|
||||
# Number of terminal rows spanned by the output, used to determine how many lines we need to go up(to place the cursor after the prompt) after displaying the output
|
||||
num_rows = 0
|
||||
prompt_line = 'Path: ' + state.input
|
||||
displayed_lines.append(prompt_line)
|
||||
num_rows += number_of_rows(prompt_line)
|
||||
matches = state.get_matches()
|
||||
if matches:
|
||||
# display commands from Max(0,selected_command_index - 10 +1 ) to Max(10,SelectedCommandIndex + 1)
|
||||
selected_command_index = matches.index(state.get_selected_match())
|
||||
matches_to_display = matches[max(0, selected_command_index - 10 + 1):max(10, selected_command_index + 1)]
|
||||
for index, m in enumerate(matches_to_display):
|
||||
fm = ' ' + m
|
||||
num_rows += number_of_rows(fm)
|
||||
# Formatting text(make searched word bold)
|
||||
for w in state.input.split(' '):
|
||||
if w:
|
||||
fm = fm.replace(w, ansi.bold_text(w))
|
||||
# highlighting selected command
|
||||
if m == state.get_selected_match():
|
||||
fm = ansi.select_text(fm)
|
||||
displayed_lines.append(fm)
|
||||
else:
|
||||
not_found_line = 'Nothing found'
|
||||
displayed_lines.append(not_found_line)
|
||||
num_rows += number_of_rows(not_found_line)
|
||||
return displayed_lines, num_rows
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
import sys
|
||||
import os
|
||||
sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)),'..'))
|
||||
from qfc.core import filter_files, get_weight
|
||||
|
||||
def _equals(marks_list1, marks_list2):
|
||||
l1 = sorted(marks_list1)
|
||||
l2 = sorted(marks_list2)
|
||||
if len(l1) != len(l2):
|
||||
return False
|
||||
for i,_ in enumerate(l1):
|
||||
if l1[i] != l2[i]:
|
||||
return False
|
||||
return True
|
||||
|
||||
def test_filter_files():
|
||||
files = [
|
||||
'/',
|
||||
'/a/',
|
||||
'/b/',
|
||||
'/a/b',
|
||||
'/a/b/c',
|
||||
'/b/a/',
|
||||
'/b/a/c',
|
||||
'd',
|
||||
'da'
|
||||
]
|
||||
assert(_equals(filter_files(files,''), ['/','d','da']))
|
||||
assert(_equals(filter_files(files,'/'), ['/']))
|
||||
assert(_equals(filter_files(files,'a'), ['/a/', '/b/a/', 'da']))
|
||||
|
||||
|
||||
|
||||
def test_weight():
|
||||
assert(get_weight('a','') == 1001)
|
||||
assert(get_weight('a/','') == 1000)
|
||||
assert(get_weight('a/b/','') == 2000)
|
||||
assert(get_weight('a/b/c','') == 3001)
|
||||
assert(get_weight('a','a') == 1001)
|
||||
assert(get_weight('ab','a') == 1021)
|
||||
assert(get_weight('bab','a') == 1111)
|
||||
assert(get_weight('a_b','a') == 1011)
|
||||
assert(get_weight('root/a_b','a') == 2011)
|
||||
assert(get_weight('root/a_b_c_d_e_f_g_h_i_j_k','k') == 2091)
|
||||
assert(get_weight('a/b/c/d/e/f/g/h/i/j/k','k') == 10001)
|
||||
assert(get_weight('a/B/','b') == 2000)
|
||||
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue