zsh-workers
 help / color / mirror / code / Atom feed
From: Sebastian Gniazdowski <sgniazdowski@gmail.com>
To: Bart Schaefer <schaefer@brasslantern.com>
Cc: Zsh hackers list <zsh-workers@zsh.org>
Subject: Re: compadd -Q -U completes $(( without inserting upon it
Date: Sun, 17 Jan 2016 16:21:48 +0100	[thread overview]
Message-ID: <CAKc7PVCfvBb6pxRAo=HnLuBLT++QAy4GhMZiepq9QAMPh9a2eg@mail.gmail.com> (raw)
In-Reply-To: <160116145505.ZM6664@torch.brasslantern.com>

[-- Attachment #1: Type: text/plain, Size: 1277 bytes --]

On 16 January 2016 at 23:55, Bart Schaefer <schaefer@brasslantern.com> wrote:
> B) Don't invoke completion directly; instead, invoke a normal editing
> widget to modify the buffer so the tokens do not begin a context, then
> call the completion widget, and finally clean up the buffer again when
> the completion widget returns.

I went this path and it was quite easy. I have zew-complete-shell-word
widget (attached) that does a simple grep:
        __zew_csw_found=(
"${(@M)historywords:#(#i)$__zew_csw_left*$__zew_csw_right}" )

then zle -M displays the results and Alt-h/H navigates among them. One
minute video:

https://asciinema.org/a/9smut3m7l6njvwfhrkfb6emdp

> The unfortunate bit of (B)
> is that it can't work at all as a completer function (zstyle element).

So no automatic multi-column zle -M, no actual highlighting (tried to
pass ANSI codes to zle -M but it doesn't process them), no zstyles
that configure pagination etc. These are the drawbacks? All this is in
the queue to implement, e.g. I currently display LINES / 3 matches and
there is no access to matches beyond that.

Code is at:

https://github.com/psprint/zsh-editing-workbench/blob/master/zew-complete-shell-word

only 74 lines for a robust _history_complete_older

Thanks,
Sebastian Gniazdowski

[-- Attachment #2: zew-complete-shell-word --]
[-- Type: application/octet-stream, Size: 2088 bytes --]

emulate -LR zsh
setopt typesetsilent extendedglob noshortloops

# Prepare output variables for zew-process-buffer
local ZEW_PB_WORDS ZEW_PB_WORDS_BEGINNINGS ZEW_PB_SPACES 
local ZEW_PB_SELECTED_WORD ZEW_PB_LEFT ZEW_PB_RIGHT

autoload zew-process-buffer
zew-process-buffer "$BUFFER"

typeset -g __zew_csw_index __zew_csw_left __zew_csw_right
typeset -ga __zew_csw_found
__zew_csw_found=( "$reply[@]" )

# Consecutive call?
if [ "${WIDGET%-backwards}" = "${LASTWIDGET%-backwards}" ]; then
    if [[ "$WIDGET" != *-backwards ]]; then
        (( __zew_csw_index ++ ))
    else
        (( __zew_csw_index -- ))
    fi
else
    if [[ "$WIDGET" != *-backwards ]]; then
        __zew_csw_index="1"
    else
        # Will get changed into $to_display limit
        __zew_csw_index="0"
    fi
    __zew_csw_left="$ZEW_PB_LEFT"
    __zew_csw_right="$ZEW_PB_RIGHT"
    __zew_csw_found=( )
fi

# Find history words matching $left ... $right
if [ "$#__zew_csw_found" -eq "0" ]; then
    typeset -U __zew_csw_found
    repeat 1; do
        __zew_csw_found=( "${(@M)historywords:#(#i)$__zew_csw_left*$__zew_csw_right}" )
        # Remember for consecutive calls
        reply=( "$__zew_csw_found[@]" )
    done
fi

# Guard values of the index
integer to_display=$(( LINES / 2 ))
[ "$to_display" -gt "$#__zew_csw_found" ] && to_display="$#__zew_csw_found"
[ "$__zew_csw_index" -le 0 ] && __zew_csw_index="$to_display"
[ "$__zew_csw_index" -gt "$to_display" ] && __zew_csw_index=1

# Display matches
typeset -a disp_list
disp_list=( "${(@)__zew_csw_found[1,to_display]}" )
disp_list[__zew_csw_index]="> ${disp_list[__zew_csw_index]} <"
zle -M -- "${(F)disp_list}"

# Regenerate command line
buf=""
integer nwords="${#ZEW_PB_WORDS}"
integer newcursor=0
for (( i=1; i<=nwords; i++ )); do
    if [ "$i" = "$ZEW_PB_SELECTED_WORD" ]; then
        buf+="${ZEW_PB_SPACES[i]}${__zew_csw_found[__zew_csw_index]}"
        newcursor="$#buf"
    else
        buf+="${ZEW_PB_SPACES[i]}${ZEW_PB_WORDS[i]}"
    fi
done

# Set command line
BUFFER="$buf"
# Move cursor to the end of word
CURSOR="$newcursor"

# vim:ft=zsh

[-- Attachment #3: zew-process-buffer --]
[-- Type: application/octet-stream, Size: 3070 bytes --]

# Input:
# $1 - buffer to process
#
# Output:
# ZEW_PB_WORDS - split of "$1" into shell words; array
# ZEW_PB_WORDS_BEGINNINGS - indexes of first letters of corresponding ZEW_PB_WORDS in ZEW_PB_WORDS
# ZEW_PB_SPACES - white spaces before corresponding ZEW_PB_WORDS in ZEW_PB_WORDS
# ZEW_PB_SELECTED_WORD - index in ZEW_PB_WORDS pointing to word activated by CURSOR position
# ZEW_PB_LEFT - left part of active word
# ZEW_PB_RIGHT - right part of active word
#

emulate -LR zsh
setopt typesetsilent extendedglob noshortloops

local MBEGIN MEND MATCH mbegin mend match

local buf="$1"
ZEW_PB_WORDS=( "${(Z+n+)BUFFER}" )
ZEW_PB_SPACES=( )
ZEW_PB_WORDS_BEGINNINGS=( )
ZEW_PB_SELECTED_WORD="-1"

integer nwords="${#ZEW_PB_WORDS}"

# Remove ZEW_PB_WORDS one by one, counting characters,
# computing beginning of each word, to find
# place to break the word into 2 halves (for
# complete_in_word option)

local i word
integer char_count=0

# (Z) handles spaces nicely, but we need them for the user
# Also compute words beginnings and the selected word
for (( i=1; i<=nwords; i++ )); do
    # Remove spurious space generated by Z-flag when
    # input is an unbound '$(' (happens with zsh < 5.1)
    # and also real spaces gathered by an unbound '$(',
    # to handle them in a way normal to this loop
    ZEW_PB_WORDS[i]="${ZEW_PB_WORDS[i]%% ##}"
    word="${ZEW_PB_WORDS[i]}"

    # In general, $buf can start with white spaces
    # We will not search for them, but instead for
    # leading character of current shell word,
    # negated. This is an ambition to completely
    # avoid character classes

    # Remove white spaces
    buf="${buf##(#m)[^$word[1]]#}"
    # Count them
    char_count=char_count+"$#MATCH"
    # This is the beginning of current word
    ZEW_PB_WORDS_BEGINNINGS[i]=$(( char_count + 1 ))
    # Remember the spaces
    ZEW_PB_SPACES[i]="$MATCH"

    # Remove the word
    MATCH=""
    buf="${buf#(#m)$word}"

    # If shell word not found, return. This shoudln't happen
    [ -z "$MATCH" ] && return 0

    # Spaces point to previous shell word
    # Visual cursor right after spaces (-ge) -> not enough to select previous word (-gt required)
    [[ "$ZEW_PB_SELECTED_WORD" -eq "-1" && "$char_count" -gt "$CURSOR" ]] && ZEW_PB_SELECTED_WORD=$(( i-1 ))

    # Actual characters point to current shell word
    # Visual cursor right after letters (-ge) -> enough to select current word
    char_count=char_count+"$#word"
    [[ "$ZEW_PB_SELECTED_WORD" -eq "-1" && "$char_count" -ge "$CURSOR" ]] && ZEW_PB_SELECTED_WORD="$i"
done 

# What's left in $buf can be only white spaces
char_count=char_count+"$#buf"
ZEW_PB_SPACES[i]="$buf"

# Visual cursor right after spaces (-ge) -> enough to select last word
[[ "$ZEW_PB_SELECTED_WORD" -eq "-1" && "$char_count" -ge "$CURSOR" ]] && ZEW_PB_SELECTED_WORD=$(( i-1 ))

# Divide active word into two halves
integer diff=$(( CURSOR - ZEW_PB_WORDS_BEGINNINGS[ZEW_PB_SELECTED_WORD] + 1 ))
word="${ZEW_PB_WORDS[ZEW_PB_SELECTED_WORD]}"
ZEW_PB_LEFT="${word[1,diff]}"
ZEW_PB_RIGHT="${word[diff+1,-1]}"

# vim:ft=zsh

      reply	other threads:[~2016-01-17 15:22 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-01-16 17:25 Sebastian Gniazdowski
2016-01-16 22:55 ` Bart Schaefer
2016-01-17 15:21   ` Sebastian Gniazdowski [this message]

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='CAKc7PVCfvBb6pxRAo=HnLuBLT++QAy4GhMZiepq9QAMPh9a2eg@mail.gmail.com' \
    --to=sgniazdowski@gmail.com \
    --cc=schaefer@brasslantern.com \
    --cc=zsh-workers@zsh.org \
    /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).