recherche d'historique inversée avec des expressions régulières

10

Je suis à la recherche d'un outil pour permettre la recherche incrémentielle inverse avec une prise en charge des expressions régulières simples (ou simplement de plusieurs correspondances). Par exemple, si je veux trouver la commande 'foo bar baz', je pourrais faire quelque chose comme ceci pour trouver la commande rapidement:

CRTL-R (démarre la recherche) tapez 'foo' (correspond à la commande la plus récente en utilisant foo) continuez à taper 'foo | baz' (correspond à la commande la plus récente contenant 'foo' ET 'baz'.

Existe-t-il quelque chose comme ça? Si non, comment pourrais-je procéder à sa mise en œuvre moi-même?

drewrobb
la source

Réponses:

4

Widget personnalisé history-incremental-multi-searchpourzsh

Installer

Créer un répertoire et l'inclure dans votre $fpathPar exemple, j'ai créé un répertoire ~/.zsh/functionset la ligne fpath=($HOME/.zsh/functions $fpath)dans mon .zshrc.

Mettez ce qui suit dans un fichier nommé history-incremental-multi-searchdans ce répertoire.

emulate -L zsh
setopt extended_glob

local oldbuffer=$BUFFER
local -i oldcursor=$CURSOR

local dir                # search direction
local chars              # input buffer
local -a words           # search terms
local -a found           # all history items that match first term
local -i hindex=$HISTNO  # current 
local -i lmatch          # last matched history item (for prev/next)

if [[ $WIDGET == *forward* ]]; then
    dir=fwd
else
    dir=bck
fi

function find-next {
    # split the input buffer on spaces to get search terms
    words=(${(s: :)chars})

    # if we have at least one search term
    if (( $#words )); then
        # get all keys of history items that match the first
        found=(${(k)history[(R)*$words[1]*]})
        if (( $#found )); then
            # search in widget direction by default
            # but accept exception in $1 for "prev match"
            search-${1:-$dir}
        else
            # no matches
            lmatch=$HISTNO
        fi
    else
        # no search terms
        lmatch=$HISTNO
        BUFFER=$oldbuffer
        CURSOR=$oldcursor
    fi
}

function search-fwd {
    # search forward through matches
    local -i i
    for (( i = $#found; i > 0; i-- )); do
        # but not before hindex as we're searching forward
        if [[ $found[$i] -gt $hindex ]]; then
            set-match $found[$i]
        fi
    done
}

function search-bck {
    # search backward through matches
    local -i i
    for (( i = 1; i <= $#found; i++ )); do
        # but not beyond hindex as we're searching backward
        if [[ $found[$i] -lt $hindex ]]; then
            set-match $found[$i]
        fi
    done
}

function set-match {
    # match history item against all terms and select it if successful
    local match=1
    local -i i
    for (( i = 2; i <= $#words; i++ )); do
        if [[ $history[$1] != *$words[$i]* ]]; then
            match=0
            break
        fi
    done
    if [[ $match -ne 0 ]]; then
        lmatch=$1
        BUFFER=$history[$1]
        CURSOR=$#BUFFER
        break
    fi
}

# display sub prompt
zle -R "${dir}-i-search-multi:"

# handle input keys
while read -k; do
    case $REPLY in
        # next
        $'\C-n' )
            hindex=$lmatch
            find-next
            ;;
        # prev
        $'\C-p' )
            hindex=$lmatch
            if [[ $dir == fwd ]]; then
                find-next bck
            else
                find-next fwd
            fi
            ;;
        # break
        $'\e' | $'\C-g' )
            BUFFER=$oldbuffer
            CURSOR=$oldcursor
            break
            ;;
        # accept
        $'\C-m' | $'\C-j' )
            if [[ $lmatch -eq $HISTNO ]]; then
                BUFFER=$oldbuffer
                CURSOR=$oldcursor
            else
                HISTNO=$lmatch
            fi
            break
            ;;
        # erase char
        $'\C-h' | $'\C-?' )
            chars=$chars[1,-2]
            hindex=$HISTNO
            find-next
            ;;
        # erase word
        $'\C-w' )
            if [[ $chars =~ \  ]]; then
                chars=${chars% *}
            else
                chars=
            fi
            hindex=$HISTNO
            find-next
            ;;
        # kill line
        $'\C-u' )
            chars=
            hindex=$HISTNO
            find-next
            ;;
        # add unhandled chars to buffer
        * )
            chars=${chars}${REPLY}
            hindex=$HISTNO
            find-next
            ;;
    esac

    zle -R "${dir}-i-search-multi: $words"
done

Mettez-le ou sourcez-le à partir de votre .zshrc:

autoload -U history-incremental-multi-search

# make new widgets from function
zle -N history-incremental-multi-search-backward history-incremental-multi-search
zle -N history-incremental-multi-search-forward history-incremental-multi-search

# bind the widgets to keys
bindkey '^Xr' history-incremental-multi-search-backward
bindkey '^Xs' history-incremental-multi-search-forward

Utilisation

Vous devriez maintenant être en mesure de lancer une recherche incrémentale vers l' arrière avec Ctrl+X, r, en avant avec Ctrl+X, s.

Tapez vos termes de recherche séparés par un espace. Les touches suivantes sont disponibles pour le contrôler:

  • ← Backspace: effacer le caractère

  • Ctrl+W: effacer un mot

  • Ctrl+U: ligne de mise à mort

  • Ctrl+N: Prochain match

  • Ctrl+P: match précédent

  • Ctrl+G/ Esc: annuler la recherche

  • Enter: J'accepte

Cette solution peut probablement être un peu simplifiée. C'est plus une preuve de concept fonctionnelle, avec beaucoup de place pour l'amélioration.

peth
la source
Merci d'avoir pris le temps d'écrire ces réponses, cela m'aide énormément. J'espère que plus de gens tomberont dessus et trouveront cela utile.
drewrobb
6

Vous pouvez parcourir votre histoire:

history | egrep '(foo|baz)'

J'espère que ça aide.

binfalse
la source
0

S'appuyant sur la réponse de @ peth:

Zsh est maintenant livré avec history-incremental-pattern-search-backward, vous n'avez pas besoin de le définir vous-même. Ajoutez simplement la liaison de touches. Je préfère remplacer ^Ren ajoutant la ligne suivante à .zshrc:

bindkey '^R' history-incremental-pattern-search-backward

Vous pouvez maintenant utiliser les opérateurs glob (sic! Not rexex) dans votre recherche.

Sergey Romanovsky
la source