File: tabkey.ce

package info (click to toggle)
epic4 1%3A3.0-2.1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 3,780 kB
  • sloc: ansic: 56,285; makefile: 630; sh: 161; perl: 30
file content (221 lines) | stat: -rw-r--r-- 7,559 bytes parent folder | download | duplicates (10)
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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
#
# OK, here's the plan:
#
# Tabkey cuts the input line up into edible chunks, and selects a function
# to do the actual completion.  The function will receive a single word as
# its argument, and return a list of potential matches.  The word is whatever
# is under the cursor, up to the insertion point at the current time.  The
# word may be empty or contain spaces and the function should be able to
# cope with this.
#
# The exact function chosen works like this:
# * If the current _word_ is a command (first word, begins with $cmdchars),
#   call command_completion by way of parsekey (don't call any aliases).
# * Or, if the current line is a command, it gets handed to $tabkey.cmd().
# * Otherwise, call tabkey.default, which is a stub that can be changed
#   to suit your preferences.  The default tabkey.default calls
#   tabkey.nickchan which matches against all channels you're currently on
#   and either the nicks in your current channel, or failing that, the
#   nicks in all channels.
# * tabkey.default is also called if tabkey.cmd() returns nothing.  If
#   necessary, the function can prevent this behaviour by returning a single
#   space, but this is discouraged.
# * tabkey.default is also called by $tabkey.cmd() if it cannot find an
#   appropriate command handler.
# * $tabkey.cmd() will search for an appropriate function by joining the
#   command and all its arguments together with dots and progressively
#   removing those arguments until a function is found.
#
# tabkey "exports" a number of local variables that the functions may use
#   or alter after using "bless":
# * $curword is what tabkey believes the current word is.  The function
#   may make use of this for context sensitivity.  I say believes because
#   this may be open to different interpretations.
# * $wordind is the character index of the _beginning_ of the word that is
#   being completed.  If it is equal to $curpos(), then the first argument
#   will be empty and the user has hit tab at the beginning of the word or
#   in between words and the function should return all possible matches.
#   The reason you may need to use it is if the function is designed to
#   complete something that is not a single word.  It, and the current
#   cursor position may be altered to cause tabkey to replace the chosen
#   part of the string instead of just the word.
#
# The organisation of the supporting functions goes like this:
# * "Context sensitive" functions are to be named with "tabkey.cmd." as a
#   prefix followed by the name of the command that they are used to
#   complete.  This is where tabkey.cmd will look for them.
# * Generally, completion "methods" should be placed under tabkey.method
#   Context sensitive functions should be as simple as possible and the bulk
#   of the work should be done by the methods.
#
# NOTE:  The "first argument" description above is no longer true.  The
#        functions argument list is to be treated as a single argument.
#
# Remaining known issues:
#   Doesn't work (very well) with commands with dots in them.

package tabkey.ce
bind ^i parse_command tabkey.main
alias tabkey.default tabkey.method.nickchan

alias tabkey.main {
	@ :oxd = xdebug(extractw)
	xdebug extractw
	@ :curpos = curpos()
	@ :curword = indextoword($curpos $L )
	@ :wordind = wordtoindex($curword $L)
	@ :cmdind = 0
	if (32 >= (127 & ascii($mid(${curpos-1} 1 $L )))) {
		@ curword++
		@ wordind = curpos
	}
	@ :word = mid($wordind ${curpos-wordind} $L)
	if (word=~["*"]) {
		@ :word = shift(word)
	} elsif (word=~["*]) {
		@ :word#=["]
		@ :word = shift(word)
	}
	if (!index($cmdchars $L)) {
		wait for @ :matches = tabkey.cmd($word)
	} else {
		wait for @ :matches = tabkey.default($word)
	}
	@ :prefix = prefix($matches)
	if (1 < numwords($matches) && word == prefix) {
		echo Completions for \"$word\": $matches
	} elsif (#matches && strlen($word) <= strlen($prefix)) {
		@ :prefix = 0 > index("$chr($jot(32 1))" $prefix) ? prefix : ["$prefix"]
		@ :line = mid(0 $wordind $L)
		parsekey erase_to_beg_of_line
		xtype -l $line$prefix${1<#matches?[]:[ ]}
	}
	xdebug $oxd
}

alias tabkey.cmd {
	bless
	@ :ret = :cmd = []
	@ :pass = chr($jot($ascii(AZ)))
	@ :pass#= chr($jot($ascii(az)))
	@ :pass#= chr($jot($ascii(09)))
	@ :args = wordtoindex($cmdind $L)
	@ :args = mid($args $curpos $L)
	@ :args = unsplit(. $args)
	@ :args = pass(._$pass $args)
	if (!curword) {
		@ ret = tabkey.method.commands($args)
		repeat $#ret {push ret /$shift(ret)}
		return $ret
	}
	while (args) {
		@ :matches = aliasctl(alias pmatch tabkey.cmd.$args*)
		@ :matches = prefix($matches)
		if (curword == count(. $args)) {
		} elsif (matches != [tabkey.cmd.$args]) {
		} elsif (aliasctl(alias exists $matches)) {
			@ args = matches
			break
		}
		@ args = before(-1 . $args)
	}
	if (args) {
		wait for @ ret = ${args}($*)
	}
	unless (strlen($ret)) {
		wait for @ ret = tabkey.default($*)
	}
	return $ret
}

# Used for things like /eval and /repeat where what you are completing
# is another command.  depth is the arg number where the command begins.
# This is not well cpu optimised.  See examples below.
alias tabkey.recurse (depth,...) {
	bless
	@ :curpos += wordtoindex($cmdind $L )
	@ :curpos -= wordtoindex(${cmdind+depth} $L )
	@ :cmdind += depth
	@ :curword -= depth
	@ function_return = 0 > curword ? [] : tabkey.cmd($*)
	@ :curword += depth
	@ :cmdind -= depth
	@ :curpos += wordtoindex(${cmdind+depth} $L )
	@ :curpos -= wordtoindex($cmdind $L )
}

# Context sensitive completion goes here.

alias tabkey.cmd.eval tabkey.recurse 1
alias tabkey.cmd.repeat tabkey.recurse 2

alias tabkey.cmd.dcc (args) {
	bless
	switch ($curword) {
		(1) {return $pattern("$args*" CHAT CLOSE CLOSEALL GET LIST RAW RENAME RESUME SEND)}
	}
}
alias tabkey.cmd.dcc.resume tabkey.cmd.dcc.send
alias tabkey.cmd.dcc.send {
	bless
	switch ($curword) {
		(0) (1) {echo This should never happen.;call;local}
		(2) {return $tabkey.method.nick($*)}
		(*) {return $tabkey.method.filei($*)}
	}
}

alias tabkey.cmd.help {
	bless
	@ :matches = []
	@ :path = restw(1 $left($curpos $L))
	if (wordind == curpos) {@ push(path *)}
	repeat $#path @ push(path $shift(path)*)
	@ matches = globi($unsplit(/ $getset(HELP_PATH) $path))
	repeat $#matches @ push(matches $rightw(1 $remws(/ $split(/ $shift(matches)))))
	return $matches
}

# Methods.

alias tabkey.method.commands {
	@ :ret = sort($uniq($getcommands($**) $aliasctl(alias match $*) $aliasctl(alias match $**)))
	return $ret
}

alias tabkey.method.chan (args) { return $pattern("$args*" $mychannels()); }
alias tabkey.method.notify (args) { return $pattern("$args*" $notify(on)); }

stack push alias alias.foo
alias alias.foo alias $*;alias $sar(g/globi/glob/$sar(g/filei/file/$*))
alias.foo tabkey.method.filei (f0) {
	@ :f0 = sar(gr/\\/\\\\/f0)
	@ :f0 = sar(gr/*/\\*/f0)
	@ :f0 = sar(gr/?/\\?/f0)
	@ :f0 = sar(gr/[/\\[/f0)
	@ :f0 = sar(gr/]/\\]/f0)
	@ :f0 = :f1 = globi("" $f0*)
	while (numwords($f0) == 1 && f0 =~ [*/]) {
		@ f0 = globi("" $f0*)
	}
	return ${f0 ? f0 : f1}
}
stack pop alias alias.foo

alias tabkey.method.nick (args) {
	if (match("$args*" $chanusers())) {
		return $pattern("$args*" $chanusers());
	} else {
		@ :chanusers = mychannels()
		repeat $#chanusers @ push(chanusers $chanusers($shift(chanusers)))
		return $pattern("$args*" $sort($uniq($chanusers)));
	}
}

alias tabkey.method.nickchan {
	return $remws(/ $tabkey.method.chan($*) $tabkey.method.nick($*))
}

alias tabkey.method.nickchannotify {
	return $remws(/ $tabkey.method.chan($*) $sort($uniq($tabkey.method.nick($*) $tabkey.method.notify($*))))
}