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..