membalikkan pencarian riwayat dengan ekspresi reguler

10

Saya sedang mencari alat untuk memungkinkan pencarian inkremental terbalik dengan dukungan ekspresi reguler sederhana (atau hanya beberapa pertandingan). Misalnya, jika saya ingin menemukan perintah 'foo bar baz', saya dapat melakukan sesuatu seperti berikut untuk menemukan perintah dengan cepat:

CRTL-R (memulai pencarian) ketik 'foo' (cocok dengan perintah terbaru menggunakan foo) lanjutkan mengetik 'foo | baz' (cocok dengan perintah terbaru yang mengandung 'foo' AND 'baz'.

Apakah ada yang seperti ini? Jika tidak, bagaimana saya bisa menerapkannya sendiri?

drewrobb
sumber

Jawaban:

4

Widget khusus history-incremental-multi-searchuntukzsh

Mendirikan

Buat direktori dan sertakan di Anda. $fpathMisalnya, saya buat direktori ~/.zsh/functions, dan baris fpath=($HOME/.zsh/functions $fpath)di .zshrc.

Masukkan yang berikut ini dalam file bernama history-incremental-multi-searchdi direktori itu.

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

Masukkan ini atau sumberkan dari .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

Menggunakan

Sekarang Anda harus dapat memulai pencarian tambahan mundur dengan Ctrl+X, r, maju dengan Ctrl+X, s.

Ketik istilah pencarian Anda dipisahkan dengan spasi. Kunci-kunci berikut tersedia untuk mengendalikannya:

  • ← Backspace: menghapus karakter

  • Ctrl+W: hapus kata

  • Ctrl+U: kill line

  • Ctrl+N: pertandingan selanjutnya

  • Ctrl+P: pertandingan sebelumnya

  • Ctrl+G/ Esc: batalkan pencarian

  • Enter: terima

Solusi ini mungkin dapat disederhanakan sedikit. Ini lebih merupakan bukti konsep fungsional, dengan banyak ruang untuk perbaikan.

peth
sumber
Terima kasih telah meluangkan waktu untuk menulis jawaban ini, ini sangat membantu saya. Saya harap lebih banyak orang menemukan ini dan bermanfaat.
drewrobb
6

Anda dapat melihat riwayat Anda:

history | egrep '(foo|baz)'

Saya harap itu membantu.

binfalse
sumber
0

Membangun pada jawaban @ peth:

Zsh sekarang hadir history-incremental-pattern-search-backward, Anda tidak perlu mendefinisikannya sendiri. Cukup tambahkan ikatan kunci. Saya lebih suka mengganti ^Rdengan menambahkan baris berikut ke .zshrc:

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

Sekarang Anda dapat menggunakan operator glob (sic! Not rexex) dalam pencarian Anda.

Sergey Romanovsky
sumber