home *** CD-ROM | disk | FTP | other *** search
- # (auto-install)
- #############################################################################
- # pythonMode.tcl
- # Howard Oakley, EHN & DIJ Oakley
- # howard@quercus.demon.co.uk
- #
- # Description:
- # This auto-installing file implements a simple mode for editing
- # Python using Alpha. It has been tested with Alpha version 7.2.
- # The features of this mode include:
- # syntax colouring - comments in red, strings (some) in green,
- # keywords in blue, and colons (a Python particular) in magenta
- # easy production of comment boxes etc.
- # def and class marking - automatically generated marks (M popup) give
- # the function name first, and then the class (if any), whilst class
- # definitions give the class name twice; the {} popup takes
- # you to class definitions
- # automatic indentation - given the importance of this in Python control
- # structures, this was an essential, and is accomplished
- # using tabs in syntactic context.
- # The code below is a cobbling together of code stolen from other sources.
- # Whilst the fine code of the original sources is reliable, there are
- # all sorts of nasty kludges which I have used to get it to do what
- # I needed. Tcl purists who can improve on it are invited to do so:
- # please e-mail your corrections to me so that I can maintain this.
- # My thanks and apologies to those from whom I have stolen code.
- # Version: 1.0 dated 17 Sep 1999 [or if you prefer it, 1999-09-17].
- #
- # (Vince fixed some stuff up for Alphatk, and removed some obsolete things)
- #############################################################################
-
- alpha::mode Pyth 1.0.1 source {*.py} {
- electricBraces electricTab electricReturn
- } {} help {
- Python Mode provides syntax coloring - comments in red, strings (some) in
- green, keywords in blue, and colons (a Python particular) in magenta; easy
- production of comment boxes etc.; def and class marking - automatically
- generated file marks (M popup) give the function name first, and then the
- class (if any), whilst class definitions give the class name twice; the {}
- popup (ParseFuncs) takes you to class definitions; automatic indentation -
- given the importance of this in Python control structures, this is an
- essential, and is accomplished using tabs in syntactic context.
- }
-
- # We need at least this version
- alpha::package require AlphaTcl 7.4a7
-
- newPref v leftFillColumn {1} Pyth
- newPref v wordBreak {\w+} Pyth
- newPref f wordWrap {0} Pyth
- newPref v funcExpr {^[ \t]*(def|class)[ \t]+([A-Za-z0-9_]+)} Pyth
- newPref v parseExpr {^[ \t]*([A-Za-z0-9_]*)} Pyth
- newPref v wordBreakPreface {\W} Pyth
- newPref f autoMark 0 Pyth
-
- # I extend the range of keywords a little, to include some type conversions # and other important items
-
- set pythKeyWords {
- access and break class continue def del elif else
- except exec finally for from global if import in
- is lambda not or pass print raise return self try while
- = < > <= >= + * - / != <> % | ^ &
- len min max ~ abs int long float complex divmod pow
- list map tuple eval string repr assert
- }
-
- regModeKeywords -e {#} -s green -c red -i : -I magenta -k blue Pyth $pythKeyWords
- unset pythKeyWords
-
- #================================================================================
- # for Tcl 8.0 compatibility, hopefully
- namespace eval Pyth {}
- #================================================================================
-
- # for easy production of comment boxes etc.
-
- set Pyth::commentCharacters(General) "\#"
- set Pyth::commentCharacters(Paragraph) [list "#----" "#----" "#"]
- set Pyth::commentCharacters(Box) [list "#" 2 "#" 2 "#" 3]
-
- # the mark routine, which has to append the class name *if* the definition is
- # part of a class definition, but reset the empty class name if it is not,
- # i.e. there is no leading whitespace before the 'def'
- # - this therefore builds the M popup menu.
-
- proc Pyth::MarkFile {} {
- global PythmodeVars
- set pos [minPos]
- set classnom ""
-
- while {![catch {search -s -f 1 -r 1 -m 0 -i 1 $PythmodeVars(funcExpr) $pos} res]} {
- set start [lindex $res 0]
- set end [pos::math [lindex $res 1] + 1]
- set text [getText $start $end]
-
- if {[regexp -indices {(class)[ \t]+([a-zA-Z0-9_]+)} $text dummy dummy0 pname]} {
- set i1 [pos::math $start + [lindex $pname 0]]
- set i2 [pos::math $start + [lindex $pname 1] + 1]
- # this is the start of a class definition, so save the class name
- set classnom [getText $i1 $i2]
- } else {
- if {[pos::compare $pos > [minPos]]} {
- set pp [pos::math $start - 1]
- set pq [pos::math $start + 1]
- set pr [getText $pp $pq]
- if {![regexp {[ \t]+} $pr]} {
- # this is a standalone def, therefore reset the class name to an empty string
- set classnom ""
- }
- }
- }
-
- if {[regexp -indices {(def|class)[ \t]+([a-zA-Z0-9_]+)} $text dummy dummy0 pname]} {
- set i1 [pos::math $start + [lindex $pname 0]]
- set i2 [pos::math $start + [lindex $pname 1] + 1]
- set word [getText $i1 $i2]
- set tmp [concat $i1 $i2]
- # assemble the marker name with the def element first, followed by any class name
- set ol_word [join [concat $word " " $classnom ""]] set inds($ol_word) $tmp
- }
-
- set pos $end
- }
- if {[info exists inds]} {
- foreach f [lsort -ignore [array names inds]] {
- set res $inds($f)
- setNamedMark $f [lineStart [lindex $res 0]] [lindex $res 0] [lindex $res 1]
- }
- }
- }
-
- # this builds the {} menu along similar lines, but this time with just
- # class definitions
- proc Pyth::parseFuncs {} {
- global PythmodeVars
- set pos [minPos]
-
- while {![catch {search -s -f 1 -r 1 -m 0 -i 1 $PythmodeVars(funcExpr) $pos} res]} {
- set start [lindex $res 0]
- set end [pos::math [lindex $res 1] + 1]
- set text [getText $start $end]
-
- if {[regexp -indices {(class)[ \t]+([a-zA-Z0-9_]+)} $text dummy dummy0 pname]} {
- set i1 [pos::math $start + [lindex $pname 0]]
- set i2 [pos::math $start + [lindex $pname 1] + 1]
- set word [getText $i1 $i2]
- set tmp [concat $i1 $i2]
- set inds($word) $tmp
- }
- set pos $end
- }
- set rtnRes {}
-
- if {[info exists inds]} {
- foreach f [lsort -ignore [array names inds]] {
- set next [nextLineStart $inds($f)]
- lappend rtnRes $f $next
- }
- }
- return $rtnRes
- }
-
- proc Pyth::indentLine {} {
- # get details of current line
- set beg [lineStart [getPos]]
- set text [getText $beg [nextLineStart $beg]]
- regexp "^\[ \t\]*" $text white
- set len [string length $white]
- set epos [pos::math $beg + $len]
-
- # Find last previous non-comment line and get its leading
- # whitespace
- set pos $beg
- while 1 {
- if {[catch {search -s -f 0 -r 1 -i 0 -m 0 "^\[ \t\]*\[^ \t\r\n\]" [pos::math $pos - 1]} lst]} {
- # search failed at top of file
- set line "#"
- set lwhite 0
- break
- }
- if {![catch {text::inCommentBlock [lindex $lst 0]} res]} {
- set pos [lindex $res 0]
- } else {
- set line [getText [lindex $lst 0] [pos::math [nextLineStart [lindex $lst 0]] - 1]]
- set lwhite [posX [pos::math [lindex $lst 1] - 1]]
- break
- }
- }
- # we need (syntactically) to increase the tabs by 1, so first do
- # this using spaces, and then convert the spaces to a tab. This is
- # not elegant, but it works!
- if {[regexp ":\[ \t\]*$" $line]} {
- getWinInfo a
- set ps $a(tabsize)
- incr lwhite $ps
- }
- set lwhite [text::indentOf $lwhite]
- if {$white != $lwhite} {
- replaceText $beg $epos $lwhite
- select $beg [pos::math $beg + [string length $lwhite]]
- spacesToTabs
- }
- goto [pos::math $beg + [string length $lwhite]]
- }
-