zsh-users
 help / color / mirror / code / Atom feed
From: Stephane Chazelas <Stephane_Chazelas@yahoo.fr>
To: Zsh users list <zsh-users@sunsite.dk>
Subject: Re: [tip] mouse support
Date: Thu, 11 Nov 2004 16:22:09 +0000	[thread overview]
Message-ID: <20041111162209.GC4451@sc> (raw)
In-Reply-To: <4483.1100184032@csr.com>

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


  reply	other threads:[~2004-11-11 16:22 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2004-11-11 12:20 Stephane Chazelas
2004-11-11 14:40 ` Peter Stephenson
2004-11-11 16:22   ` Stephane Chazelas [this message]
2004-11-11 17:47     ` Bart Schaefer
2004-11-11 18:05       ` Stephane Chazelas
2004-11-11 18:22       ` Stephane Chazelas
2004-11-12  1:02         ` Bart Schaefer
2004-11-12  9:22           ` Stephane Chazelas
2004-11-11 16:07 ` Andy Spiegl
2004-11-11 17:26   ` Stephane Chazelas
2004-11-12 12:02 ` [tip] mouse and mouse-wheel support! Stephane Chazelas
2004-11-15 12:21   ` Stephane Chazelas
2004-11-15 13:14     ` Stephane Chazelas

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20041111162209.GC4451@sc \
    --to=stephane_chazelas@yahoo.fr \
    --cc=zsh-users@sunsite.dk \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.vuxu.org/mirror/zsh/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).