compl.mods - dotfiles - leot's dotfiles
(HTM) hg clone https://bitbucket.org/iamleot/dotfiles
(DIR) Log
(DIR) Files
(DIR) Refs
---
compl.mods
---
1 # $eterna: compl.mods,v 1.3 2003/12/08 05:05:59 mrg Exp $
2 #
3 # Complete Modules by Ian Frechette
4 # Last updated 2-2-94
5 # example modules
6 # Don't let the size scare you. This is a whole collection of
7 # examples and comments about the design of modules
8 # meat of all this are the interface functions to 'complete'
9 # compl.add, and compl.list (soon to be compl.del)
10 # the function that does the work is compl.parse which is very small
11 # and whatever parsing routine it calls.
12
13 # These should actually all be in their individual modules
14 # but this is just an example file..
15
16 # Note.. compl.list is internal to complete.. shoulnd't normally be used
17 if (compl.list == [])
18 {
19 # Note here.. Currently, if you compl.add /blah blahparse
20 # followed by compl.add /blah otherparse only the latter will
21 # called. No conflict message is shown unless you replace the
22 # default 'null' and 'nomatch' parsers.
23 compl.add -null -nomatch defaultparse
24 compl.add /c chanparse
25 compl.add /channel chanparse
26 compl.add /l chanparse
27 compl.add /leave chanparse
28 compl.add /part chanparse
29 }
30
31
32 # testparse <cmd> <stuff....pat>
33 # this is called by the complete routines.
34 @ dp.tmp = [0 :]
35 # [0 :] represents a count of 0.. and a null pattern :<pat>
36 alias defaultparse {
37 @ dp.cnt = [$*]
38 @ dp.cnt = #dp.cnt
39 ^assign -dp.pat
40 @ dplist = chanusers($C)
41 # all the cnt stuff is in case you do /command word1 word2 pattern<tab>
42 @ dp.pat = [$(${dp.cnt - 1})]
43 # '/command pattern ' leaves <pat> null
44 # '/command pattern' sets <pat> to a new pattern
45 # important because smartparse may leave a space
46 if (right(1 $L) != [ ]) {@ dp.tmp = [0 :$dp.pat]}
47
48 # call testparse with current cnt :<pat> <listname> <newpat>
49 @ dp.tmp = smartparse($dp.tmp dplist $dp.pat)
50 # note dp.tmp accounts for two arguments.. and is modified and saved
51
52 if (left(1 $word(1 $dp.tmp)) == [,])
53 { }
54 {if (left(1 $word(1 $dp.tmp)) == [.])
55 { }}
56 }
57
58 alias chanparse {
59 @ cp.cnt = [$*]
60 @ cp.cnt = #cp.cnt
61 ^assign -cp.pat
62 @ cplist = mychannels()
63 # all the cnt stuff is in case you do /command word1 word2 pattern<tab>
64 @ cp.pat = [$(${cp.cnt - 1})]
65 # '/command pattern ' leaves <pat> null
66 # '/command pattern' sets <pat> to a new pattern
67 # important because smartparse may leave a space
68 if (right(1 $L) != [ ]) {@ cp.tmp = [0 :$cp.pat]}
69
70 # call testparse with current cnt :<pat> <listname> <newpat>
71 @ cp.tmp = smartparse($cp.tmp cplist $cp.pat)
72 # note cp.tmp accounts for two arguments.. and is modified and saved
73
74 if (left(1 $word(1 $cp.tmp)) == [,])
75 { }
76 {if (left(1 $word(1 $cp.tmp)) == [.])
77 { }}
78 }
79
80 # message parser module.. Compatible with included tabscript
81 # currently calls the tabkey script under 3 conditions
82 # the input line has more than 2 argments on it.. '/msg bob thud<TAB>'
83 # the input line has nothing on it '<TAB>'
84 # the input line has only one argument and the character before the
85 # cursor is a space '/msg bob <TAB>'
86 # plus it now does nickname completion
87 # /m D<TAB> expands to /m Daemon and so on
88 alias messparse {
89 if (([$1] != []) && ([$2] == []))
90 {
91 if (right(1 $*) != [ ])
92 {
93 # this is the simple match.. just match first occurance
94 # and expand
95 if (mp.cnt = match($(1)* $tk.msglist))
96 {
97 parsekey delete_previous_word
98 # in case one does /m =ni<TAB> it must delete the =
99 if (index(#=% $1) >= 0) {parsekey backspace}
100 xtype -literal $word(${mp.cnt - 1} $tk.msglist)
101 xtype -literal ${[ ]}
102 }
103 }
104 {
105 ^tk.getmsg 1 $tk.msglist
106 }
107 }
108 {
109 ^tk.getmsg 1 $tk.msglist
110 }
111 }
112
113
114 # connect module for opers.. easily changeable to kicks, bans.. etc..
115 # simply use /connect <TAB> for list or /connect <uniqid tag><TAB>
116 # eg. /connect pen<TAB> expands to /connect penfold.ece.uiuc.edu
117 # It always expands first matching string. Look at testparse for
118 # a more intelligent way to do it.
119
120 @ connlist = [irc.uiuc.edu goren1.u.washington.edu ircserver.iastate.edu w6yx.stanford.edu hamblin.math.byu.edu freedom.nmsu.edu dreamtime.unm.edu ircserver.santafe.edu irc.netsys.com irc-2.mit.edu cs-mail.bu.edu]
121
122 alias connparse {
123 if ([$1] != [])
124 {
125 @ cp.line = [$*]
126 if ((right(1 $*) == [ ]) && ([$2] == [])&& ([$0] == [/connect]))
127 {
128 xtype -literal 6667;xtype -literal ${[ ]}
129 # note.. if converted to use smartparse.. the port number must
130 # be removed here.. the above logic conflicts with SP
131 }
132 {
133 # expand only the first match found (See 'testparse' for better way
134 if (cp.cnt = match($(${#cp.line -1})* $connlist))
135 {
136 delword
137 xtype -literal $word(${cp.cnt - 1} $connlist)
138 xtype -literal ${[ ]}
139 }
140 {
141 echo *** connlist $connlist
142 }
143 }
144 }
145 {
146 echo *** connlist $connlist
147 }
148 }
149
150 # Load module
151 # /load net<TAB> expand to /load netsplit and so on.
152 # Note the problem right now is that it only finds and expands the first
153 # name in the list I think we can get around this.
154 @ loadlist = [netsplit ircgrep cut-paste compl.mods]
155 alias loadparse {
156 if ([$1] != []) {
157 if (lp.cnt = match($(1)* $loadlist))
158 {
159 parsekey delete_previous_word
160 xtype -literal $word(${lp.cnt - 1} $loadlist)
161 }
162 }
163 {
164 echo *** loadlist = $loadlist
165 }
166 ^assign -lp.cnt
167 }
168
169 # ############ stuff related to SMARTPARSE ###################
170 # The new testparse rewritten to use the
171 # extremely awsome smartparse routine.
172
173 if (!match(/test $compl.list)) {compl.add /test testparse}
174
175 # just a quick alias for making LINKS lists.. /makelist testlist *.edu
176 # will make a list of all *.edu servers.. Note that just * is
177 # generally too big a list for ircII
178 alias makelist {
179 if ([$1])
180 {
181 ^on ^364 * push $0 $$1
182 links $1-
183 wait -cmd eval ^on 364 -\;echo *** makelist finished
184 }
185 }
186
187 @ testlist = [aone atwo athree bone btwo bthree ircserver.iastate.edu ircserver.santafe.edu]
188
189 # testparse <cmd> <stuff....pat>
190 # this is called by the complete routines.
191 @ tp.tmp = [0 :]
192 # [0 :] represents a count of 0.. and a null pattern :<pat>
193 alias testparse {
194 # ignore this first line..
195 @ tp.cnt = [$*]
196 @ tp.cnt = #tp.cnt
197 ^assign -tp.pat
198 # all the cnt stuff is in case you do /command word1 word2 pattern<tab>
199 if (tp.cnt > 1)
200 {
201 @ tp.pat = [$(${tp.cnt - 1})]
202 # '/command pattern ' leaves <pat> null
203 # '/command pattern' sets <pat> to a new pattern
204 # important because smartparse may leave a space
205 if (right(1 $L) != [ ]) {@ tp.tmp = [0 :$tp.pat]}
206
207 # Uncomment the following line to see how it works from here.. debugging
208 # echo smartparse\($tp.tmp testlist $tp.pat\)
209
210 # call testparse with current cnt :<pat> <listname> <newpat>
211 @ tp.tmp = smartparse($tp.tmp testlist $tp.pat)
212 # note tp.tmp accounts for two arguments.. and is modified and saved
213
214 if (left(1 $word(1 $tp.tmp)) == [,])
215 {echo *** no match for pattern [$tp.pat] found in list}
216 {if (left(1 $word(1 $tp.tmp)) == [.])
217 {echo *** testlist: $testlist}}
218 }
219 {
220 echo *** testlist : $testlist
221 }
222 }
223
224 alias test echo *** TEST: You've matched: $*
225
226 # test module
227 # Trying to make some sort of intelligent handling of the tab lists.
228 @ sp.cnt = 0
229
230 # call it with smartparse <cnt> :<pat> <listname> <newpat>
231 # $0 $1 $2 $3
232 # returns <counter> [:,.]<pattern>
233 # : == successful match , == no match . == null
234 #
235 # Look at how testparse uses it.. you shouldn't have to touch any
236 # smartparse vars.. It's all handled through the interface.. basically
237 # you're telling it where to start looking in the list and how
238 # long the list is.. Each time smartparse is called it returns a counter
239 # value indicating where it left off last time. You can save it
240 # or not.. testparse saves it.. and passes it back as the new
241 # starting position <cnt>
242 #
243 # Assuming the counter, pattern, and list are maintained through each call
244 # it'll assume you're searching forward in the list from some place
245 # after the last word matched in the list..
246 #
247 # If you feed it a <newpat> for which <pat> is not a subset, it'll reassign
248 # <pat> to <newpat> and restart the process..
249 # It defaults to expansion.. so.. <pat> = blah
250 # will match blahone, blahtwo etc. Works with wildcards.. *a* matches a lot
251 # Try it.
252
253 ^assign -sp.tmp
254 alias smartparse {
255 # int sp.tmp - index of last match found
256 # int sp.cnt - position in list
257 # int sp.max - max number of elements in list
258 # string sp.pat - match pattern
259 if ([$3] != [])
260 {
261 # Extract <pat> from :<pat> Note.. It may be null
262 @ sp.pat = mid(1 50 $1)
263 @ sp.max = [$(#$2)]
264 @ sp.cnt = [$0]
265 # set pattern. Determine if we've changed the base pattern or not
266 if (sp.pat == [])
267 {@ sp.pat = [$3]}
268 { if (!match($(sp.pat)* $3)) {@sp.pat = [$3]} }
269 @ sp.run = 1
270
271 while (((sp.list = words($sp.cnt $sp.max $($2))) != []) && (!sp.tmp) && sp.run)
272 {
273 # look for match in list
274 if (sp.tmp = match($(sp.pat)* $sp.list))
275 {
276 # sp.cnt is absolute position in list. Jump over found item.
277 # to set up for the next call to smartparse
278 @ sp.cnt = sp.cnt + sp.tmp
279
280 # parsekey delete_previous_word
281 delword
282 xtype -literal $word(${sp.tmp - 1} $sp.list)
283 xtype -literal ${[ ]}
284 }
285 {
286 # nothing found.. drop out of loop
287 # for this condidtion to occur we must be at the beginning
288 # of the loop... either first pass.. or just looped back
289 if (!sp.cnt && !sp.tmp)
290 {
291 # notfound condition set for return value later
292 @ sp.notfound = [$sp.cnt ,$sp.pat]
293 @ sp.run = 0
294 # echo *** smartparse: no matching pattern
295 }
296 # loop back
297 @ sp.cnt = 0
298 }
299 }
300 ^assign -sp.tmp
301 if (!sp.list) {@sp.cnt = 0}
302 ^assign -sp.list
303 if (sp.notfound == [])
304 {@ function_return = [$sp.cnt :$sp.pat]}
305 {@ function_return = sp.notfound;^assign -sp.notfound}
306 }
307 {
308 # echo *** sp NULL
309 # echo *** $2: $($2)
310 @ function_return = [$sp.cnt .$sp.pat]
311 }
312 ^assign -sp.run
313 }
314
315 # alias words.. usage $word(<begin> <end> <words>)
316 # words(0 2 zero one two three ... ) == 'zero one two' and so on
317 alias words {
318 @ function_return = [$(${[$0]+2}-${[$1] +2})]
319 }
320
321 # This is like DELETE_PREVIOUS_WORD except that it delets to the
322 # previous space which is much more useful than the current
323 # behavior of deleting to the previos non- [a-zA-Z0-9] char. :-?
324 alias delword {
325 parsekey erase_to_end_of_line
326 if ((dw.char = right(1 $L)) != [ ]) {@ dw.nw = 0} {@dw.nw = 1}
327 while (((dw.char != [ ]) || dw.nw) && (dw.char != []))
328 {
329 parsekey backspace
330 if ((dw.char = right(1 $L)) != [ ]) {@ dw.nw = 0}
331 }
332 ^assign -dw.char;^assign -dw.nw
333 }
334
335
336 # it is of course possible to do command completion with something like
337 alias expand.con parsekey erase_to_beg_of_line;type /connect $()
338 # /con<tab> expands to /connect
339
340 # Be careful though.. note the $() at the end of the line.
341 # Without this 'complete' sends expand.con /con and the above
342 # alias will add the '/con' back onto the end of the line resulting
343 # in /connect /con on the input line when you're done
344
345
346 # # # # # # # # # # MODULE WRITING PHILOSOPHY # # # # # # # # # #
347 # Some thoughts about using complete and designing complete modules.
348 #
349 # Basically for any given time you hit TAB there are three states
350 # normal, null, and nomatch
351 # normal - there is something on the command line to process
352 # and the command is in the command list maintained by 'complete'
353 # The associated command parser is called with the contents of the
354 # input line in its entirety as the arguments.
355 # null - there is nothing at all on the command line and some
356 # default action must be taken. No parser need be called at all as well.
357 # nomatch - the command at the head of the input line is not
358 # found in the list of commands maintained by 'complete'.
359 # A default 'nomatch' parser may or may not be called but if it is called
360 # it's passed the entire contents of the input line.
361 #
362 # This is not the end of the story however.
363 # If you're writing a completion module of some sort there are the same
364 # 3 states plus 1 more. Let's say you want to write something to
365 # find a match for a given keyword prefix out of a list when you hit
366 # TAB. e.g. /eat ap<TAB> looks for matching words that start with ap
367 # The 4 actions are
368 # normal - There is a single match for ap and it expands /eat apple
369 # multiple matches - There is more than one match for ap and thus
370 # a choice must be made. Possible choices include
371 # 1. do nothing
372 # 2. list possible matches (like ^D) or set showmatch in tcsh shell
373 # *** matches for prefix 'ap': apple apricot apendage
374 # 3. match only the first occurance Currently what the /connect module
375 # does /eat apple
376 # 4. cycle through the possible matches for the keyword 'ap'.
377 # The 'testparse' modules uses this scheme and it's my favorite
378 # albiet a tad more expensive in terms of CPU cycles and responce
379 # time. (I'm sure someone could see the diff.. I can't ;)
380 # /eat ap<TAB> -> /eat apple<TAB> -> /eat apricot<TAB> etc..
381 # 5. display worthless error message
382 # *** non-unique matches found
383 # nomatch - as as before, nomatching keywords are found, the choices are
384 # limited to things like displaying the whole list or just cycling through
385 # to the next item in the list like the 'tabkey' script's 'messparse' does.
386 # null - This one is more likely to happen only if 'complete' saw the
387 # input line as null, but then the null action is ussually special anyway.
388 # Otherwise this may occur when you say just /eat<TAB> and the obvious
389 # thing to do here is just to display the list of items to choose from
390 # in an appropriate format.
391
392 # Just remember.. the parsing routine can really do anything it wants..
393 # it could simply 'sendline' the line on to the server and push a
394 # button to start WWIII when you hit tab.. It doesn't have to mess with the
395 # command line but it's more useful that way. Although.. you could write
396 # a tab completion module that when tab was hit.. it spell checked
397 # the line.. anything is possible..