İmleci bash içindeki argümanlarla hareket ettirmenin yolu?


5

Kısaca, imleci bir sözcük ileri ve geri hareket ettirmek için Mf ve Mb kullanabilirsiniz, ancak bir argümanı ileri veya geri hareket ettirmenin bir yolu var mı? Kutudan çıkarılmadıysa, belki bazı yapılandırmalarla?

Başka bir deyişle, imleci aşağıdaki işaretli konumlar arasında gezinmek isterim.

cp "foo bar.txt" "/tmp/directory with space"
^  ^             ^
|  |             |

3
İmleç hareketi, her bir argümanın nerede başladığını ve bittiğini belirlemek için komut satırını ayrıştırmayan okuma satırı kütüphanesi tarafından yönetilir.
chepner

Yanıtlar:


5

Bash kullandığını biliyorum ve sorduğun şeyin bash'ta mümkün olduğundan emin değilim. Size göstereceğim şey, ZSH'de istenen özelliğin nasıl uygulanacağı. (ZSH biraz gelişmiş bir bash gibidir - değiştirirseniz o zaman hala yeterli kalmalısınız).

ZSH'de ZSH Çizgi Düzenleyicisi var (kısaca zle). Bu, tüm hareket tuşlarını, bash'e benzer şekilde ikili komutlar olarak sağlar. Daha ileri gittiğinde özel komutları tanımlayabilme yeteneğidir. Özel bir komut, bir pencere aracına dönüştürülen herhangi bir kabuk işlevidir.

Bu işlevler diğer komutları da uygulayabilir ve probleminizle ilgilenen çeşitli değişkenlere de erişebilirler. Konuşacaklarım:

  • $ BUFFER - Bu, düzenlemekte olduğunuz satırın tamamı
  • $ CURSOR - bu, satırın geçerli satırdaki konumu

Gibi mevcut diğerleri de var:

  • $ LBUFFER - bu imleçten önceki her şey
  • $ RBUFFER - bu imleçten sonraki her şey

Şimdi ZSH'nin yalnızca özel anahtarlıklar sağlama yeteneğine sahip olmadığı, değişkenler üzerinde gerçekleştirebileceğiniz çok daha kapsamlı bir işlem kümesi olduğu da oluyor. Bu problem için ilginç olanlardan biri:

  • z - Genişletme sonucunu kelimeleri bulmak için kabuk ayrıştırmayı kullanarak kelimelere ayırın, yani değerdeki herhangi bir alıntıyı dikkate alın.

Genişletilmiş $ BUFFER işlevini doğrudan bir değişkene atayabilirsiniz, şöyle:

line=${(z)BUFFER}

(satır şimdi bir dizidir, ama sinir bozucu bir şekilde bu dizi bash!

Bu, genel karakterlerin genişlemesini gerçekleştirmez, bu nedenle geçerli satırınızdaki gerçek bağımsız değişkenlerin bir dizisini döndürür. Bunu yaptıktan sonra, tampondaki her kelimenin başlangıç ​​noktasının konumu ile ilgileniyorsunuz. Ne yazık ki, iki kelimenin arasında ve tekrarlanan kelimelerin arasında birden fazla boşluk olabilir. Bu noktada düşünebildiğim en iyi şey, düşündüğünüz gibi geçerli arabellekten düşünülen her sözcüğü kaldırmaktır. Gibi bir şey:

buffer=$BUFFER
words=${(z)buffer}
for word in $words[@]
do
    # doing regular expression matching here,
    # so need to quote every special char in $word.
    escaped_word=${(q)word}
    # Fancy ZSH to the rescue! (q) will quote the special characters in a string.

    # Pattern matching like this creates $MBEGIN $MEND and $MATCH, when successful
    if [[ ! ${buffer} =~ ${${(q)word}:gs#\\\'#\'#} ]]
    then
        echo "Something strange happened... no match for current word"
        return 1
    fi
    buffer=${buffer[$MEND,-1]}
done

Neredeyse şimdi oradayız! İhtiyaç duyulan şey, imlecin önündeki son kelimenin hangi kelimenin, imlecin ardından bir sonraki kelimenin başlangıcı olduğunu görmenin bir yoludur.

buffer=$BUFFER
words=${(z)buffer}
index=1
for word in $words[@]
do
    if [[ ! ${buffer} =~ ${${(q)word}:gs#\\\'#\'#} ]]
    then
        echo "Something strange happened... no match for current word"
        return 1
    fi

    old_length=${#buffer}
    buffer=${buffer[$MEND,-1]}
    new_length=${#buffer}
    old_index=$index
    index=$(($index + $old_length - $new_length))

    if [[ $old_index -lt $CURSOR && $index -ge $CURSOR ]]
    then
        # $old_index is the start of the last argument.
        # you could move back to it.
    elif [[ $old_index -le $CURSOR && $index -gt $CURSOR ]]
    then
        # $index is the start of the next argument.
        # you could move forward to it.
    fi
    # Obviously both of the above conditions could be true, you would
    # have to have a way to tell which one you wanted to test - but since
    # you will have two widgets (one forward, one back) you can tell quite easily. 
done

Şimdiye kadar imlecin ilerleyeceği uygun dizini nasıl türetebileceğinizi gösterdim. Ancak size imleci nasıl hareket ettireceğinizi ya da bu fonksiyonları tuşlara nasıl bağlayacağınızı göstermedim.

$ CURSOR değişkeni güncellenebilir ve bunu yaparsanız geçerli ekleme noktasını taşıyabilirsiniz. Çok kolay!

İşlevleri anahtarlara bağlama, önce bir pencere öğesine bağlanma ara adımını içerir:

zle -N WIDGET_NAME FUNCTION_NAME

Ardından widget'ı bir anahtara bağlayabilirsiniz. Büyük olasılıkla belirli anahtar tanımlayıcıları aramak zorunda kalacaksınız, ancak genellikle yeterince kolay olan Ctrl-LETTER'e bağlarım:

bindkey '^LETTER' WIDGET_NAME

Bunları bir araya getirip probleminizi çözelim:

function move_word {
    local direction=$1

    buffer=$BUFFER
    words=${(z)buffer}
    index=1
    old_index=0
    for word in $words[@]
    do
        if [[ ! ${buffer} =~ ${${(q)word}:gs#\\\'#\'#} ]]
        then
            echo "Something strange happened... no match for current word $word in $buffer"
            return 1
        fi

        old_length=${#buffer}
        buffer=${buffer[$MEND,-1]}
        new_length=${#buffer}
        index=$(($index + $old_length - $new_length))

        case "$direction" in
            forward)
                if [[ $old_index -le $CURSOR && $index -gt $CURSOR ]]
                then
                    CURSOR=$index
                    return
                fi
                ;;
            backward)
                if [[ $old_index -lt $CURSOR && $index -ge $CURSOR ]]
                then
                    CURSOR=$old_index
                    return
                fi
                ;;
        esac
        old_index=$index
    done
    case "$direction" in
        forward)
            CURSOR=${#BUFFER}
            ;;
        backward)
            CURSOR=0
            ;;
    esac
}

function move_forward_word {
    move_word "forward"
}

function move_backward_word {
    move_word "backward"
}

zle -N my_move_backwards move_backward_word
zle -N my_move_forwards move_forward_word
bindkey '^w' my_move_backwards
bindkey '^e' my_move_forwards

Kendi testlerime göre, bu işi yapıyor gibi görünüyor. Muhtemelen bağlandığı anahtarları değiştirmek isteyeceksiniz. Referans için, çizgi ile test ettim:

one 'two three' "four five"    "'six seven' eight nine" * **/ **/**/*
^  ^           ^           ^                           ^ ^   ^       ^

ve şapkalar arasında gezindi. Kaymaz.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.