home *** CD-ROM | disk | FTP | other *** search
Text File | 1999-04-17 | 15.8 KB | 429 lines | [TEXT/ALFA] |
- ## -*-Tcl-*-
- # ###################################################################
- # Vince's Additions - an extension package for Alpha
- #
- # FILE: "elecCompletion.tcl"
- # created: 8/3/96 {12:06:40 pm}
- # last update: 17/4/1999 {4:13:15 pm}
- # Author: Vince Darley
- # E-mail: <darley@fas.harvard.edu>
- # mail: Division of Applied Sciences, Harvard University
- # Oxford Street, Cambridge MA 02138, USA
- # www: <http://www.fas.harvard.edu/~darley/>
- #
- # modified by rev reason
- # -------- --- --- -----------
- # 8/3/96 VMD 1.0 original
- # 20/11/96 VMD 1.1 many, many improvements.
- # 24/2/97 VMD 1.2 added some support of trf's code, plus some fixes
- # 1/9/97 VMD 1.5 added 'completion::contraction' and improved g-elec.
- # 12/1/97 trf 1.6 added 'Tutorial Shell' stuff, bumped to 9.0b2
- # 12/2/97 trf 1.7 corrected corrections, bumped to 9.0b3
- # 4/12/97 VMD 1.8 various fixes, better tcl8 compatibility
- # ###################################################################
- ##
-
- alpha::extension elecCompletions 9.0.1 {
- alpha::package require elecBindings 9.0
- alpha::package require -loose Alpha 7.1
- menu::insert mode items end "completionsTutorial" "editCompletions"
- # load completion code for a mode the first time that mode is used
- hook::register mode::init completion::load "*"
- namespace eval completion {}
- completion::initialise
- } maintainer {
- "Vince Darley" darley@fas.harvard.edu <http://www.fas.harvard.edu/~darley/>
- } uninstall this-file help {file "ElecCompletions Help"}
-
- proc completion::initialise {} {}
-
- namespace eval elec {}
-
- proc completion::load {} {
- global HOME
- foreach f [glob -nocomplain [file join ${HOME} Tcl Completions [modeALike]Completions*.tcl]] {
- message "loading [file tail $f]…"
- namespace eval ::[modeALike]::Completion {}
- uplevel \#0 [list source $f]
- }
- }
-
- ##
- # -------------------------------------------------------------------------
- #
- # "completion::cmd" --
- #
- # General purpose proc for extending a given command to its full extent
- # in a mode-dependent fashion. If we hit a unique match, we call
- # '${mode}completion::Electric'; if we can extend, we do so, and set
- # things up so the calling procedure '${mode}completion::Cmd' will be
- # called if the user tries to cmd-Tab again; if we don't recognise
- # anything, we return 0
- #
- # We normally use the list ${m}cmds to look for completions, but the
- # caller can supply a different name. This is useful to prioritise
- # lists, so we first call with one, then another,... I currently use
- # this feature for TeX-completions, in which I call with a second list,
- # containing fake commands, which expand into environments.
- # -------------------------------------------------------------------------
- ##
- proc completion::cmd { {cmd ""} {listExt "cmds"} {prematch ""}} {
- if {![string length $cmd]} {
- set cmd [completion::lastWord]
- # if there's any whitespace in the command then it's no good to us
- if {[containsSpace $cmd]} { return 0 }
- }
-
- set m [modeALike]
- # do an electric if we already match exactly
- global ${m}electrics
- if {[info exists ${m}electrics($cmd)]} {
- return [completion ${m} Electric "${prematch}${cmd}"]
- }
- if {[llength [set matches [completion::fromList $cmd ${m}${listExt}]]] == 0} {
- return 0
- } else {
- return [completion::matchUtil Cmd $cmd $matches $prematch]
- }
- }
-
- proc completion::matchUtil {proc what matches {prematch ""}} {
- if {[llength $matches] == 0} { return 0 }
- set match [completion::Find $what $matches]
- if {[string length $match]} {
- # we completed or cancelled, so move on
- #completion::already error
- if { $match == 1 } {
- return 1
- } else {
- return [completion [modeALike] Electric "${prematch}${match}"]
- }
- } else {
- completion::already $proc
- return 1
- }
- }
-
- ##
- # -------------------------------------------------------------------------
- #
- # "completion::ensemble" --
- #
- # Complete and do electrics for commands which have two parts separated
- # by a space. Very useful for Tcl's "string compare ..." etc.
- # -------------------------------------------------------------------------
- ##
- proc completion::ensemble {dummy} {
- set lastword [completion::lastTwoWords prevword]
- set prevword [string trim $prevword]
- set m [modeALike]
- # Need catch to avoid namespace problems
- if {[catch {global ${m}${prevword}cmds}] || ![info exists ${m}${prevword}cmds]} {
- return 0
- } else {
- return [completion::cmd $lastword "${prevword}cmds" "${prevword} "]
- }
- }
-
- ##
- # -------------------------------------------------------------------------
- #
- # "completion::electric" --
- #
- # Given a command, and an optional list of defaults, check the command is
- # ok and if so try and insert an electric entry.
- # -------------------------------------------------------------------------
- ##
- proc completion::electric { {cmd ""} args } {
- set m [modeALike]
- if {![string length $cmd]} {
- set cmd [completion::lastWord]
- # only check for space if we're doing it
- if {[containsSpace $cmd]} { return 0 }
- }
-
- return [eval [list elec::findCmd $cmd ${m}electrics] $args]
- }
-
- ##
- # -------------------------------------------------------------------------
- #
- # "completion::contraction" --
- #
- # Complete and do electrics for commands which have two parts separated
- # by a apostrophe. Useful for making shortcuts to things. ex: s'c Tcl's
- # "string compare ..." etc.
- # -------------------------------------------------------------------------
- ##
- proc completion::contraction {dummy} {
- set lastword [completion::lastTwoWords hint]
- if {![regexp "'\$" $hint]} {return 0}
- append hint $lastword
- return [completion::electric $hint]
- }
-
- ##
- # -------------------------------------------------------------------------
- #
- # "elec::findCmd" --
- #
- # General purpose proc for extending a command in some predetermined
- # fashion (such as mapping 'for' to a template 'for (;;)…'). Mode
- # specific procedures may use this if desired. The given command is
- # looked up in the given array '$arrayn', and if there is an entry, some
- # electric procedure happens. By default, if an entry is '0', then '0'
- # is returned (which can be used by the calling procedure to take some
- # other action, usually more sophisticated such as TeX-ref- completion),
- # and if the entry is an integer corresponding to a list element of the
- # list 'args', then that element is inserted. In this case list elements
- # start with '1' (because zero has a special meaning). Template stops in
- # the electric completion are marked by pairs of bullets '••'. If there
- # is any text between the bullets, that can be used to inform the user of
- # what ought to go there. All strings must contain at least one such
- # template stop, to which the insertion point moves.
- #
- # '$arrayn' ought not to be a large array or this proc may be slow. (we
- # first look for an exact array element match $arrayn($cmd), but if that
- # fails we look for a glob'ed match)
- #
- # The array element may contain control sequences. These start with '◊',
- # and may be followed by:
- #
- # kill0 --- delete the string which triggered this template before
- # inserting anything.
- #
- # killN --- delete all except N characters of the string.
- #
- # N --- use the N'th element of 'args' for the template.
- #
- # [ --- the string must be evaluated first (usually triggering some proc
- # which perhaps interacts with the user a bit)
- #
- # » --- an indirection; use the template insertion corresponding to
- # the given text item instead.
- #
- # In order to provide backward compatiblity of this proc with any new
- # control sequences that may be developed, any 'unknown' control
- # sequence is just deleted, a package that deals with the new sequences
- # thus has to overide this proc in order to make the now sequences
- # functionality available.
- #
- # So, what are some of the possible future control sequences? Well, I've
- # played with;
- #
- # sequences bound to a stop
- #
- # « --- an extended prompt, provides a longer, more pedalogical explanation
- # for a stop that the curt, fill in 'xxx' in the statusline.
- # ¶ --- a name that acts as an index into an array of code snippets, so a
- # bit of code can be executed when visiting a stop, perhaps aiding
- # in filling in options, validating entries, or anything else that
- # makes sense.
- # ø --- marks a stop of such an obvious nature, that the marking of the
- # stop with a dot, or and in-text prompt is superflous. In fact, such
- # stops often have existing statements dragged into their position,
- # so leaving them unmarked has a speed advantage. Perhaps this
- # action is best toggled depending on a flag value.
- #
- # Any stop that falls in the above class, will occur after any regular
- # prompting text, and should trigger the removal of itself and any
- # other characters up until the occurrence of the stop ending bullet.
- # That can be acomplished in one of two ways, here with a regsub of this
- # form:
- # regsub -all {•([^◊]*)◊[^•]+•} <template> {•\1•} result
- # or by applying the regsub to the entire set of electrics for a mode
- # as soon as its completions are loaded. (first method implemented)
- #
- # sequences that occurr at the start of a template
- # and apply to the template as a whole
- #
- # < --- means that certain conditions that must be meet by the text
- # proceeding where this template is to be inserted must be met
- # before the insertion is allowed, (e.g. a tcl command must be
- # proceeded by whitespace, a [, a ", or eval for the insertion
- # to be syntactically correct and thus , allowable)
- #
- # Sequences in this class will have to be of a single character, as will
- # get rid of any unknown sequence by
- # regsub {◊[^k0-9»\[]} [string range <template 0
- # [string first • <template>]] head set <template> $head
- # append <template> rest
- #
- # Includes some fixes by Tom Fetherston
- # -------------------------------------------------------------------------
- ##
- proc elec::findCmd { cmd arrayn args } {
- if {[set action [elec::_findCmd $cmd $arrayn]] == ""} { return 0 }
- # we have the action; check for control sequences
- while {[string index $action 0] == "◊"} {
- # control sequence: kill, procedure or choice of default value?
- set action [string range $action 1 end]
- if { [string range $action 0 3] == "kill" } {
- set dpos [pos::math [getPos] - [expr {[string length $cmd] + [string index $action 4]}]]
- deleteText $dpos [getPos]
- regsub -all "kill" [string range $action 5 end] $cmd action
- } elseif {[string index $action 0] == "\[" } {
- set action [subst $action]
- } elseif {[string index $action 0] == "»" } {
- set key [string range $action 1 end]
- global $arrayn
- set text [set ${arrayn}($key)]
- set action "◊kill0${key}${text}"
- } elseif {([scan $action %d idx]) \
- && (![ catch {lindex $args [expr {$idx-1}]} act]) } {
- set action $act
- } else {
- if {[info commands [set proc elec::action::[string index $action 1]]] == $proc} {
- set action [$proc $action]
- } else {
- set action [string range $action 2 end]
- }
- }
- }
- # then, we pull out any "bulleted-stop control sequences" that are
- # unknown to this version of elec::findCmd -trf
- regsub -all {•([^◊]*)◊[^•]+•} $action {•\1•} action
- elec::Insertion $action
- # The idea here is to continue with other completions (return 0)
- # if the character before the insertion point is non white-space
- global wordBreakPreface
- if {![regexp $wordBreakPreface [lookAt [pos::math [getPos] - 1]]]} {
- if {[isSelection]} {deleteText [getPos] [selEnd]}
- return 0
- } else {
- return 1
- }
- }
-
- ##
- # -------------------------------------------------------------------------
- #
- # "elec::_findCmd" --
- #
- # Find the electric command in the given array, or return ""
- # -------------------------------------------------------------------------
- ##
- proc elec::_findCmd {cmd arrayn} {
- global $arrayn
- if {[info exists ${arrayn}($cmd)]} {
- return [set "${arrayn}($cmd)"]
- } else {
- if {[string first "*" [set elec_ar [array names $arrayn]]] != -1 } {
- # some of the array matches are glob'ed; we must go one at a time
- foreach elec $elec_ar {
- if {[string match $elec $cmd]} {
- return [set "${arrayn}($elec)"]
- }
- }
- }
- }
- return ""
- }
-
- # just so we have one!
- set userCompletions(date) {◊kill0◊[lindex [mtime [now]] 0]}
-
- # ensure old version loaded:
- catch "completion::user"
- ##
- # -------------------------------------------------------------------------
- #
- # "completion::user" --
- #
- # A user completion is used for small
- # mode-independent snippets, like your email address, name etc.
- # For instance I have the following defined:
- #
- # set userCompletions(vmd) "◊kill0Vince Darley"
- # set userCompletions(www) "◊kill0<[icGetPref WWWHomePage]>"
- # set userCompletions(e-) "◊kill0<[icGetPref Email]>"
- #
- # Here '◊kill0' is a control sequence which means kill exactly what
- # I just typed before carrying out this completion.
- # -------------------------------------------------------------------------
- ##
- proc completion::user { {cmd ""} } {
- if {![string length $cmd]} { set cmd [completion::lastWord] }
- if {[containsSpace $cmd]} { return 0 }
-
- return [elec::findCmd $cmd userCompletions]
- }
-
- proc mode::completionsTutorial {} {
- global HOME
- set f [file join ${HOME} Tcl Completions "[modeALike] Tutorial"]
- set files [glob -nocomplain $f*]
- if {[llength $files] == 1} {
- set fName [lindex $files 0]
- set mode [file::whichModeForWin "dummy[file extension $fName]"]
- new -n "*Tutorial shell*" -m $mode \
- -text [file::readAll $fName] -shell 1
- goto [minPos]
- Bind '`' vsp $mode
- } else {
- alertnote "No tutorial exists for this mode. Why don't you write one?"
- }
- }
-
- proc vsp {} {
- if {[win::Current] != "*Tutorial shell*"} {
- typeText "`"
- return
- }
- searchString "◊"
- goto [pos::math [getPos] + 2]
- findAgain
- centerRedraw
- if {[isSelection]} {
- deleteText [getPos] [selEnd]
- # add the following to prevent the 'non-use' of a template from
- # messing up the next completion
- ring::clear
- }
- }
-
- proc mode::editCompletions {} {
- global HOME
- set f [file join ${HOME} Tcl Completions [modeALike]Completions.tcl]
- if {[catch {file::openQuietly $f}]} {
- beep
- if {[askyesno "No completions exist for this mode. Do you want to create some?"] == "yes"} {
- set fd [open $f "w"]
- close $fd
- edit $f
- insertText {##
- # This file will be sourced automatically, immediately after
- # the _first_ time the file which defines its mode is sourced.
- # Use this file to declare completion items and procedures
- # for this mode.
- #
- # Some common defaults are included below.
- ##
-
- ##
- # These declare, in order, the names of the completion
- # procedures for this mode. The actual procedure
- # must be named '${mode}Completion::${listItem}', unless
- # the item is 'completion::*' in which case that actual
- # procedure is called. The procedure 'modeALike' will
- # map modes to similar modes so procs don't need to be
- # repeated. However each mode requires its own array entry
- # here.
- ##
- set completions(<mode>) {contraction completion::cmd Ensemble completion::electric Var}
-
- }\
- {# ◊◊◊◊ Data for <mode> completions ◊◊◊◊ #
-
- # cmds to be completed to full length (no need for short ones)
- set <mode>cmds { class default enum register return struct switch typedef volatile while }
- # electrics
- set <mode>electrics(for) " \{•start•\} \{•test•\} \{•increment•\} \{\r\t•body•\r\}\r••"
- set <mode>electrics(while) " \{•test•\} \{\r\t•body•\r\}\r••"
- # contractions
- set <mode>electrics(s'c) "◊»string compare"
- set <mode>electrics(s'f) "◊»string first"
- }}}
- }
-
-