about summary refs log tree commit diff
path: root/Functions/Zle/predict-on
blob: c56198a558d030af28a610d1a31d2426668961ae (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
# This set of functions implements a sort of magic history searching.
# After predict-on, typing characters causes the editor to look backward
# in the history for the first line beginning with what you have typed so
# far.  After predict-off, editing returns to normal for the line found.
# In fact, you often don't even need to use predict-off, because if the
# line doesn't match something in the history, adding a key performs
# standard completion --- though editing in the middle is liable to delete
# the rest of the line.
#
# With the function based completion system (which is needed for this),
# you should be able to type TAB at almost any point to advance the cursor
# to the next "interesting" character position (usually the end of the
# current word, but sometimes somewhere in the middle of the word).  And
# of course as soon as the entire line is what you want, you can accept
# with RETURN, without needing to move the cursor to the end first.
#
# To use it:
#   autoload -U predict-on
#   zle -N predict-on
#   zle -N predict-off
#   bindkey '...' predict-on
#   bindkey '...' predict-off
# Note that all functions are defined when you first type the predict-on
# key, which means typing the predict-off key before that gives a harmless
# error message.

predict-on() {
  zle -N self-insert insert-and-predict
  zle -N magic-space insert-and-predict
  zle -N backward-delete-char delete-backward-and-predict
  zle -N delete-char-or-list delete-no-predict
}
predict-off() {
  zle -A .self-insert self-insert
  zle -A .magic-space magic-space
  zle -A .backward-delete-char backward-delete-char
}
insert-and-predict () {
  setopt localoptions noshwordsplit noksharrays
  if [[ $LBUFFER = *$'\012'* ]]
  then
    # Editing a multiline buffer, it's unlikely prediction is wanted
    zle .$WIDGET "$@"
    return
  elif [[ ${RBUFFER[1]} = ${KEYS[-1]} ]]
  then
    # Same as what's typed, just move on
    ((++CURSOR))
  else
    LBUFFER="$LBUFFER$KEYS"
    if [[ $LASTWIDGET == (self-insert|magic-space|backward-delete-char) ]]
    then
      if ! zle .history-beginning-search-backward
      then
	RBUFFER=""
	if [[ ${KEYS[-1]} != ' ' ]]
	then
	  unsetopt automenu recexact
	  integer curs=$CURSOR pos nchar=${#LBUFFER//[^${KEYS[-1]}]}
	  local -a +h comppostfuncs
	  local crs curcontext="${curcontext}"

          [[ -z "$curcontext" ]] && curcontext=:::
          curcontext="predict:${curcontext#*:}"

	  comppostfuncs=( predict-limit-list )
	  zle complete-word
	  # Decide where to leave the cursor. The dummy loop is used to
	  # get out of that `case'.
	  repeat 1
	  do
	    zstyle -s ":completion:${curcontext}:" cursor crs
	    case $crs in
	    (complete)
	      # At the place where the completion left it, if it is after
	      # the character typed.
	      [[ ${LBUFFER[-1]} = ${KEYS[-1]} ]] && break
	      ;&
	    (key)
	      # Or maybe at the n'th occurrence of the character typed.
	      pos=${BUFFER[(in:nchar:)${KEYS[-1]}]}
	      if [[ pos -gt curs ]]
	      then
	        CURSOR=$pos
	        break
	      fi
	      ;&
	    (*)
	      # Or else at the previous position.
	      CURSOR=$curs
	    esac
	  done
	fi
      fi
    fi
  fi
  return 0
}
delete-backward-and-predict() {
  if [[ -n "$LBUFFER" ]]
  then
    setopt localoptions noshwordsplit noksharrays
    if [[ $LBUFFER = *$'\012'* ]] then
      # Editing a multiline buffer, it's unlikely prediction is wanted
      zle .$WIDGET "$@"
    # If the last widget was e.g. a motion, then probably the intent is
    # to actually edit the line, not change the search prefix.
    elif [[ $LASTWIDGET == (self-insert|magic-space|backward-delete-char) ]]
    then
      ((--CURSOR))
      zle .history-beginning-search-forward || RBUFFER=""
      return 0
    else
      # Depending on preference, you might call "predict-off" here.
      LBUFFER="$LBUFFER[1,-2]"
    fi
  fi
}
delete-no-predict() {
  [[ $WIDGET != delete-char-or-list || -n $RBUFFER ]] && predict-off
  zle .$WIDGET "$@"
}

# This is a helper function for autocompletion to prevent long lists
# of matches from forcing a "do you wish to see all ...?" prompt.

predict-limit-list() {
  if (( compstate[list_lines]+BUFFERLINES > LINES ||
	( compstate[list_max] != 0 &&
	    compstate[nmatches] > compstate[list_max] ) ))
  then
    compstate[list]=''
    compstate[force_list]=yes
  elif zstyle -t ":completion:::predict::" list always
  then
    compstate[force_list]=yes
  fi
}

# Handle zsh autoloading conventions

[[ -o kshautoload ]] || predict-on "$@"