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.