Zsh Mailing List Archive
Messages sorted by:
Reverse Date,
Date,
Thread,
Author
Re: [tip] mouse support
- X-seq: zsh-users 8202
- From: Stephane Chazelas <Stephane_Chazelas@xxxxxxxx>
- To: Zsh users list <zsh-users@xxxxxxxxxx>
- Subject: Re: [tip] mouse support
- Date: Thu, 11 Nov 2004 16:22:09 +0000
- In-reply-to: <4483.1100184032@xxxxxxx>
- Mail-followup-to: Zsh users list <zsh-users@xxxxxxxxxx>
- Mailing-list: contact zsh-users-help@xxxxxxxxxx; run by ezmlm
- References: <20041111122011.GB4451@sc> <4483.1100184032@xxxxxxx>
On Thu, Nov 11, 2004 at 02:40:32PM +0000, Peter Stephenson wrote:
> Stephane Chazelas wrote:
> > I just posted that to comp.unix.shell, I thought it might be of
> > some interest for some of you. Basically, it's cursor
> > positionning with the mouse under xterm like terminals (works
> > with xterm, gnome-terminal and rxvt AFAICS, probably also with
> > putty, not if there are tabs or NLs (or multi-byte characters)
> > in the zle buffer).
>
> This is very useful. (Other people should note it's only useful with
> the current zsh editing buffer; there's no way of accessing other
> information on the screen.)
In that new one, I tried to address the tab/newline issue.
It tries to calculate the length of the last line of the prompt,
it may fail if PS1 contains %%s or %{ %{ ... %} %} or if two
consecutive expansions of PS1 may not generate the same thing,
it would be nice to have access at what zsh thinks is the
cursor horizontal position at begining of buffer.
> I've extended it. Other people may have suggestions, too. This
> probably requires zsh 4.2 to use the cutbuffer and killring.
>
> Button 1 is as before.
>
> Button 2 pastes the zsh cutbuffer at the mouse position. This
> is identical to yank. It also emulates normal xterm behaviour
> by pasting at the cursor, not the mouse position. This seemed
> less surprising but is easy to change or make optional.
I changed for "pasting at the mouse cursor".
> Button 3 copies the region from the cursor to the mouse position
> into the zsh cutbuffer. This is like copy-region-as-kill but
> doesn't use the zsh mark. (I could set that too, I suppose.)
> Note I'm not 100% confident the limits (i.e. indices into the editing
> buffer) used are the best ones.
Not sure of mines either ;).
> Other additions
> - It takes care to change the existing precmd and preexec rather
> than overwriting them. This won't work if there is a "return"
> earlier in either. Possibly the new lines should go at the top.
Thanks for that. I didn't know about the $functions hash.
> - There's a function/widget zle-toggle-mouse to switch between normal
> xterm mouse and zle mouse. Positive prefix forces zle mouse,
> negative or zero prefix forces normal xterm mouse.
You can use hold the <Shift> key to have the normal xterm
behavior.
Here's the code. Be careful when you bullet proof it, it may be
full of bugs...
### code begin
set-status() { return $1; }
zle-xterm-mouse() {
local last_status=$?
emulate -L zsh
setopt extendedglob # for (#b)
local bt mx my cy i buf
read -k bt # mouse button, x, y reported after \e[M
read -k mx
read -k my
if [[ $bt != "#" ]]; then
# Process on release, but record the button on press.
ZLE_MOUSE_BUTTON=$bt
return 0
fi
(( my = #my - 32 ))
(( mx = #mx - 32 ))
print -n '\e[6n' # query cursor position
while read -k i && [[ $i != R ]]; do buf+=$i; done
local match mbegin mend
[[ $buf = (#b)??(*)\;* ]] || return
cy=$match[1]
# we don't need cx
local cur_prompt
if [[ -n $PREBUFFER ]]; then
cur_prompt=$PS2
# decide wether we're at the PS2 or PS1 prompt
else
cur_prompt=$PS1
fi
if [[ -o promptsubst ]]; then
cur_prompt=${(e)cur_prompt}
else
cur_prompt=$cur_prompt
fi
# remove visual effects:
cur_prompt=${(S)cur_prompt//("%{"*"%}"|%[BbEuUsS])/}
# restore the exit status in case $PS<n> relies on it
set-status $last_status
cur_prompt=${(%)cur_prompt}
cur_prompt=${cur_prompt##*$'\n'}
local -a pos # array holding the possible positions of
# the mouse pointer
local -i x=0 y=1 cursor=$((${#cur_prompt}+$CURSOR+1))
local Y
buf=$cur_prompt$BUFFER
for ((i=1; i<=$#buf; i++)); do
(( i == cursor )) && Y=$y
case $buf[i] in
($'\n') # newline
: ${pos[y]=$i}
(( y++, x=0 ));;
($'\t') # tab advance til next tab stop
(( x = x/8*8+8 ));;
([$'\0'-$'\037'$'\0200'-$'\0237'])
# characters like ^M
(( x += 2 ));;
# may cause trouble if spanned on two lines but well...
(*)
(( x++ ));;
esac
(( x >= mx )) && : ${pos[y]=$i}
(( x >= COLUMNS )) && (( x=0, y++ ))
done
: ${pos[y]=$i} ${Y:=$y}
local mouse_CURSOR
if ((my + Y - cy > y)); then
mouse_CURSOR=$#BUFFER
elif ((my + Y - cy < 1)); then
mouse_CURSOR=0
else
mouse_CURSOR=$(($pos[my + Y - cy] - ${#cur_prompt} - 1))
fi
case $ZLE_MOUSE_BUTTON in
(' ')
# Button 1. Move cursor.
CURSOR=$mouse_CURSOR
;;
('!')
# Button 2. Insert selection at mouse cursor postion.
BUFFER=$BUFFER[1,mouse_CURSOR]$CUTBUFFER$BUFFER[mouse_CURSOR+1,-1]
(( CURSOR = $mouse_CURSOR + $#CUTBUFFER ))
;;
('"')
# Button 3. Copy from cursor to mouse to cutbuffer.
killring=("$CUTBUFFER" "${(@)killring[1,-2]}")
if (( mouse_CURSOR < CURSOR )); then
CUTBUFFER=$BUFFER[mouse_CURSOR+1,CURSOR+1]
else
CUTBUFFER=$BUFFER[CURSOR+1,mouse_CURSOR+1]
fi
;;
esac
}
### code ends
--
Stéphane
Messages sorted by:
Reverse Date,
Date,
Thread,
Author