home *** CD-ROM | disk | FTP | other *** search
/ MacWorld 1999 September (IDG) / Sep99.iso / Shareware World / Utilities / Text Processing / Alpha / Tcl / Modes / cMode.tcl < prev    next >
Encoding:
Text File  |  1999-04-12  |  22.4 KB  |  711 lines  |  [TEXT/ALFA]

  1. ## -*-Tcl-*- (install) (nowrap)
  2.  # ###################################################################
  3.  #    Vince's    Additions -    an extension package for Alpha
  4.  # 
  5.  #    FILE: "cMode.tcl"
  6.  #                    created: 19/4/96 {4:53:38 pm}    
  7.  #                 last update: 12/4/1999 {8:29:41 am}    
  8.  #    Author:    Vince Darley
  9.  #    E-mail:    <darley@fas.harvard.edu>
  10.  #      mail:    Division of    Applied    Sciences, Harvard University
  11.  #            Oxford Street, Cambridge MA    02138, USA
  12.  #       www:    <http://www.fas.harvard.edu/~darley/>
  13.  #    
  14.  # ###################################################################
  15.  ##
  16.  
  17. alpha::mode C 1.2.4 dummyC { *.r } \
  18.   {electricReturn electricBraces electricSemicolon}
  19. alpha::mode C++ 1.2.4 dummyC++ \
  20.   {*.H *.c *.h *.cc *.cp *.cpp *.CPP *.C *.pch *.pch++ *.icc *.exp *.c++} \
  21.   {electricReturn electricBraces electricSemicolon}
  22.  
  23. # not really sure this is required any more
  24. # the CW menu registers are proc to call cw IDE.
  25. hook::register saveHook modified "C" "C++"
  26.  
  27. proc dummyC {} {}
  28. proc dummyC++ {} {}
  29.  
  30. newPref f elecColon {1} C
  31. newPref v leftFillColumn {3} C
  32. newPref v prefixString {//} C
  33. newPref f elecElse {1} C
  34. newPref f wordWrap {0} C
  35. newPref v funcExpr {^[^ \t\(#\r/@].*\(.*\)$} C
  36. newPref v parseExpr {\b([_:\w]+)\s*\(} C
  37. newPref v wordBreak {[_\w]+} C
  38. newPref v wordBreakPreface {[^_\w]} C
  39. newPref f autoMark 0 C
  40. newPref v stringColor green C
  41. newPref v commentColor red C
  42. newPref v funcColor yellow C
  43. newPref v keywordColor blue C
  44. newPref v CWCompSig CWIE C
  45. newPref v CWDbgSig MWDB C
  46. newPref v SymCompSig KAHL C
  47. newPref v SymDbgSig {◊LSD} C
  48. newPref f includeMenu {1} C
  49. newPref f launchIDEifRequired {1} C
  50. newPref v sourceSuffices { .c } C
  51. newPref v headerSuffices { .h } C
  52. newPref v indentComments "code 0" C
  53. newPref v indentMacros "fixed 0" C
  54. newPref v IDE 0 C "" [list "CodeWarrior" "Symantec" "none"] index
  55. newPref f useFasterButWorseIndentation 0 C
  56. set C::commentRegexp    {/\*(([^*]/)|[^*]|\r)*\*/}
  57. set cPreRegexp        {^\#[\t ]*[a-z]*}
  58. set cKeyWords {
  59.     void break register short enum extern int for if while struct static 
  60.     long continue switch case char unsigned double float return else 
  61.     default goto do pascal Boolean typedef volatile union auto sizeof 
  62.     size_t
  63. }
  64. if {[info exists Cwords]} {set cKeyWords [concat $cKeyWords $Cwords]}
  65. regModeKeywords -e {//} -b {/*} {*/} -c $CmodeVars(commentColor) \
  66.   -f $CmodeVars(funcColor) -k $CmodeVars(keywordColor) \
  67.   -s $CmodeVars(stringColor) -m {#} C $cKeyWords
  68.  
  69. #================================================================================
  70.  
  71. newPref f elecColon {1} C++
  72. newPref v leftFillColumn {3} C++
  73. newPref v prefixString {//} C++
  74. newPref v wordBreak {[\w_]+} C++
  75. newPref v wordBreakPreface {[^_\w]} C++
  76. newPref f elecElse {1} C++
  77. newPref f wordWrap {0} C++
  78. newPref v funcExpr {^([^ \t\(#\r/@].*[ \t]+)?\*?([A-Za-z0-9~_]+(<[^>]*>)?::[-A-Za-z0-9~_+= <>\|\*/]+|[A-Za-z0-9~_]+)[ \t\r]*\(} C++
  79. newPref v parseExpr {\b([_:\w]+)\s*\(} C++
  80. newPref f autoMark 0 C++
  81. newPref v stringColor green C++
  82. newPref v commentColor red C++
  83. newPref v keywordColor blue C++
  84. newPref v funcColor yellow C++
  85. newPref v CWCompSig CWIE C++
  86. newPref v CWDbgSig MWDB C++
  87. newPref v SymCompSig KAHL C++
  88. newPref v SymDbgSig {◊LSD} C++
  89. newPref f includeMenu {1} C++
  90. newPref f launchIDEifRequired {1} C++
  91. newPref v sourceSuffices { .cc .cp .cpp .c .icc .C } C++
  92. newPref v headerSuffices { .h .hh } C++
  93. # These three are pairs:
  94. newPref v indentComments "code 0" C++ "" indentationTypes varitem
  95. newPref v indentC++Comments "code 0" C++ "" indentationTypes varitem
  96. newPref v indentMacros "fixed 0" C++ "" indentationTypes varitem
  97. newPref v IDE 0 C++ "" [list "CodeWarrior" "Symantec" "none"] index
  98. newPref f useFasterButWorseIndentation 0 C++
  99. newPref folder universalHeadersFolder "" C++
  100.  
  101. proc C++::openUniversalHeader {} {
  102.     global universalHeadersFolder tabSize
  103.     set filename [prompt::statusLineComplete "Open which header" \
  104.       [list file::completeFromDir $universalHeadersFolder] -nocache \
  105.       -tryuppercase]
  106.     set old $tabSize
  107.     set tabSize 4
  108.     file::openQuietly [file join $universalHeadersFolder $filename]
  109.     set tabSize $old
  110. }
  111.  
  112. Bind 'q' <o> C++::openUniversalHeader C++
  113.  
  114. set C++::commentRegexp    {/\*(([^*]/)|[^*]|\r)*\*/}
  115.  
  116. set {c++KeyWords} {
  117.     new delete explicit class friend protected private public template try 
  118.     catch throw operator const mutable virtual asm inline this and and_eq 
  119.     bitand bitor compl not or or_eq xor xor_eq not_eq wchar_t bool true 
  120.     false bool inline mutable static_cast dynamic_cast reinterpret_cast 
  121.     typeid using namespace inherited
  122. }
  123. if {[info exists {C++words}]} {
  124.     set {c++KeyWords} [concat ${c++KeyWords} ${C++words} $cKeyWords]
  125. } else {
  126.     set {c++KeyWords} [concat ${c++KeyWords} $cKeyWords]
  127. }
  128.  
  129. regModeKeywords -e {//} -b {/*} {*/} -c [set C++modeVars(commentColor)] \
  130.   -f [set C++modeVars(funcColor)] -k [set C++modeVars(keywordColor)] \
  131.   -s [set C++modeVars(stringColor)] -m {#} {C++} ${c++KeyWords}
  132. unset cKeyWords
  133. unset {c++KeyWords}
  134.  
  135. proc C++::DblClick {from to shift option control} {    
  136.     if {[regexp {#include.*("|<)(.*)("|>)} [getText \
  137.       [lineStart [getPos]] [nextLineStart [getPos]]] "" "" inc]} {
  138.     return [file::tryAndOpen $inc]
  139.     }
  140.     
  141.     select $from $to
  142.     set text [getSelect]
  143.     
  144.     global tagFile
  145.     set lines [grep "^$text'" $tagFile]
  146.     if {[regexp {'(.*)'(.*[^\t])(\t)+∞} $lines "" one two]} {
  147.     file::openQuietly $one
  148.     set inds [search -s -f 1 -r 0 "$two" [minPos]]
  149.     display [lindex $inds 0]
  150.     eval select $inds
  151.     } else {
  152.     app::launchFore DanR
  153.     AEBuild {'DanR'} DanR {REF } "----" "“$text”"
  154.     }
  155. }
  156.  
  157. proc C++::parseFuncs {} {
  158.     global mode sortFuncsMenu
  159.     global funcExpr parseExpr
  160.     
  161.     set pos [minPos]
  162.     while {[set res [search -s -f 1 -r 1 -i 0 -n $funcExpr $pos]] != ""} {
  163.     if {[regexp -- $parseExpr [eval getText $res] "" word]} {
  164.         lappend m [list $word [lindex $res 0]]
  165.     }
  166.     set pos [lindex $res 1]
  167.     }
  168.     if {$sortFuncsMenu} {
  169.     regsub -all "\[\{\}\]" [lsort -ignore $m] "" m
  170.     } else {
  171.     regsub -all "\[\{\}\]" $m "" m
  172.     }    
  173.     set files ""
  174.     foreach f [getIncludeFiles] {
  175.     lappend files $f -1
  176.     }
  177.     return [concat $files [list "(-" 0] $m]
  178. }
  179.  
  180. # for C mode
  181.  
  182. proc C::DblClick {args} { eval C++::DblClick $args }
  183.  
  184. proc C::parseFuncs {} {
  185.     return [C++::parseFuncs]
  186. }
  187.  
  188.  
  189. #############################################################################
  190. #                                                                            #
  191. # Stuff above this point has only minor modifications from the original        #
  192. # "cMode.tcl", stuff below is largely or totally new.                #
  193. #                                                                            #
  194. #############################################################################
  195.  
  196. # ◊◊◊◊ File marking ◊◊◊◊ #
  197.  
  198. ## 
  199.  # -------------------------------------------------------------------------
  200.  #     
  201.  # "C++::MarkFile" --
  202.  #    
  203.  #    Improved version which handles templates, operators    etc.
  204.  #    Makes use of the new mark menu in Alpha    6.5 which can handle
  205.  #    more weird characters.  Handles most 'operator =+-*...' functions
  206.  #  for C++
  207.  #  
  208.  #  Better marking of templates recently added.
  209.  # -------------------------------------------------------------------------
  210.  ##
  211. proc C++::MarkFile {} {
  212.     if {[file extension [win::Current]] == ".exp"} { return }
  213.     set pos [minPos]
  214.     set markExpr "^(\[A-Za-z0-9~_\]+\[ \t\n\r\]*\\(|(\[^ \t\(#\n\r/@\*\].*\[ \t\]+\\*?)?(\[A-Za-z0-9~_\]+(<\[^>\]*>)?(::)?\[-A-Za-z0-9~_+= <>\|\\*/\]+|\[A-Za-z0-9~_\]+)\[ \n\t\r\]*\\()\[^(\]"
  215.     while {![catch {search -s -f 1 -r 1 -m 0 -i 0 "$markExpr" $pos} res]} {
  216.     if {[catch {search -s -f 0 -r 1 -m 0 -l [lindex $res 0] -i 0 \
  217.       {[ *][a-zA-Z]} [set pos [pos::math [lindex $res 1] + 1]]} start]} {
  218.         set start [lindex $res 0]
  219.         if {[regexp "^\[A-Za-z0-9~_\]+\[ \t\r\n\]*\\(" \
  220.           [getText $start [nextLineStart $start]] thistext]} {
  221.         # nothing
  222.         } else {
  223.         continue
  224.         }
  225.         
  226.     } else {
  227.         set start [lindex $start 0]
  228.         set thistext [getText $start $pos]
  229.     }
  230.     #regexp doesn't like carriage returns or tabs
  231.     #if the open paren was the last character on the line the selected text 
  232.     #included the last carriage return as well
  233.     #trim this off now that it is changed into a space
  234.     regsub -all "\[\r\n\t\]" [string trimright $thistext] " " thistext
  235.     if {[regexp "(\[A-Za-z0-9~_\]+(<\[^>\]*>)?(::)?\[-A-Za-z0-9~_+= <>\|\*/\]+|\[A-Za-z0-9~_\]+)\[ \t\]*\\(" $thistext dummy word]} {
  236.         if { [string first "::" $word] != -1 } {
  237.         regsub {(<\w+>)?::} $word " " it
  238.         set l [lindex $it 0]
  239.         if { $l == [lindex $it 1] } {
  240.             set word "Construct '$l'"
  241.         } elseif { "~$l" == [lindex $it 1] } {
  242.             set word "Destruct '$l'"
  243.         }
  244.         }
  245.         set inds($word) [lineStart [pos::math $start - 1]]
  246.     }
  247.     }
  248.     if {[info exists inds]} {
  249.     foreach f [lsort -ignore [array names inds]] {
  250.         set next [nextLineStart $inds($f)]
  251.         set it $f
  252.         if {[string length $it] > 57} { set it "[string range $it 0 53]..." }
  253.         setNamedMark "${it}" "$inds($f)" $next $next
  254.     }
  255.     }
  256. }
  257.  
  258. proc C::MarkFile {} { C++::MarkFile }
  259.  
  260.  
  261. # ◊◊◊◊ Indentation routines ◊◊◊◊ #
  262.  
  263. proc C::indentLine {} {C++::indentLine}
  264.  
  265.  ## 
  266.   # -------------------------------------------------------------------------
  267.   #     
  268.   #    "C++indentLine" --
  269.   #    
  270.   #     More sophisticated    version    of Pete's.    Handles    things like    '(...)'
  271.   #     expressions split over    multiple lines,    if/elseif/else both    with and
  272.   #     without curly braces, multiple    line stream    manipulation with '<<'
  273.   #     or    '>>', C and C++ style comments, ...  Assumes indentation is '4'
  274.   #   but any tab-size may be used.
  275.   #     
  276.   #     Current bugs: multi-line ',' separated lists are poorly indented.
  277.   #
  278.   #  Problems:
  279.   #   matchIt's limit doesn't seem to work, so if there is no match and we're
  280.   #   in a large file, we wait up to seconds sometimes.  Alpha bug.
  281.   #           
  282.   #     Currently checking whether we're in a /*...*/ comment is quite
  283.   #   time consuming.  It would be nice if Alpha supplied a hook to do
  284.   #   this for us.
  285.   #   
  286.   #    Results:
  287.   #     Indents the current line correctly    ;-)    for    C, C++ coding
  288.   #     
  289.   # --Version--Author------------------Changes-------------------------------  
  290.   #      1.0      Pete Keleher              original
  291.   #    2.0     <darley@fas.harvard.edu> updated as described above.
  292.   #    2.1     <darley@fas.harvard.edu> faster, better, uses positions not strings
  293.   #    2.2     <darley@fas.harvard.edu> uses 'correctIndentation' sub proc
  294.   # -------------------------------------------------------------------------
  295.   ##
  296. proc C++::indentLine {} {
  297.     global gotoEol
  298.     set gotoEol 0
  299.     
  300.     # preliminaries
  301.     set beg [lineStart [getPos]]
  302.     # are we in a C comment /*...*/ if so indent specially and return
  303.     # we really need to work out how to put this in 'correctIndentation'
  304.     if {![catch {C_inCComment $beg} comment]} {
  305.     set fChar [search -s -f 1 -r 1 "\[^ \t\r\n\]" $beg]
  306.     if { [lookAt [lindex $fChar 0]] == "*" } {
  307.         return [eval C_indentCommentLine [list $beg] $comment]
  308.     }
  309.     }
  310.     set text [getText $beg [nextLineStart $beg]]
  311.     if {[regexp "^\[ \t\]*" $text white]} {
  312.     set len [string length $white]
  313.     set rest [string range $text $len end]
  314.     } else {
  315.     set white ""
  316.     set rest ""
  317.     set len 0
  318.     }
  319.     # get indentation level    
  320.     set lwhite [text::indentOf [C++::correctIndentation [getPos] [string trim $rest]]]
  321.     if {$white != $lwhite} {
  322.     replaceText $beg [pos::math $beg + $len] $lwhite
  323.     }
  324.     if {$gotoEol} {
  325.     goto [pos::math [nextLineStart $beg] - 1]
  326.     } else {
  327.     goto [pos::math $beg + [string length $lwhite]] 
  328.     }
  329.     
  330. }
  331. proc C::correctIndentation {args} {eval C++::correctIndentation $args}
  332. ## 
  333.  # -------------------------------------------------------------------------
  334.  # 
  335.  # "C++::correctIndentation" --
  336.  # 
  337.  #  Known bugs:
  338.  #  
  339.  #  Lines which contain a URL with :// embedded tend to be considered
  340.  #  a ':' followed by a comment, and are indented as if they were
  341.  #  part of a 'case://comment' statement which is wrong.
  342.  # -------------------------------------------------------------------------
  343.  ##
  344. proc C++::correctIndentation {pos {nextword ""}} {
  345.     global gotoEol
  346.     # preliminaries
  347.     set beg [lineStart $pos]
  348.     set nextCh [string range $nextword 0 3]
  349.     set nextC [string index $nextCh 0]
  350.     set nextP [string range $nextCh 0 1]
  351.     # check for forced indentation of C, C++ comments and '#' macros
  352.     set ind "code 0"
  353.     switch -- $nextC {
  354.     "\#" {
  355.         global indentMacros
  356.         set ignore_trailers ""
  357.         set ind $indentMacros
  358.     }
  359.     "/" {
  360.         global indentComments indentC++Comments
  361.         set ignore_trailers ""
  362.         if {$nextP == "/*"} {set ind $indentComments}
  363.         if {$nextP == "//"} {set ind ${indentC++Comments}}
  364.     }
  365.     }
  366.     if {[lindex $ind 0] == "fixed" } {
  367.     # force indentation to given level
  368.     return [lindex $ind 1]
  369.     }
  370.     
  371.     # (1) first we get the indent of the last line:
  372.     # this may involve looking back a fair way
  373.     set lst [C_prevCodeIndent [pos::math $beg - 1]]
  374.     
  375.     if {[pos::compare [set pstart [lindex $lst 0]] == [minPos]]} {
  376.     return 0
  377.     }
  378.     set lwhite [posX [pos::math [lindex $lst 1] - 1]]    
  379.     # have we just finished an if-elseif-else with no '{}'?
  380.     if {$nextCh == "else"} {set iselse 1} else { set iselse 0}
  381.     if {![C_isLineNBI $pstart]} {
  382.     incr lwhite [C_recurseNoBraceIndent $pstart 0 $iselse]
  383.     }
  384.     if { [set multi [C_isLineMulti $pstart]] != "-1" } {
  385.     set lwhite $multi
  386.     }
  387.     
  388.     # (2) now we indent this line accordingly
  389.     
  390.     set pbeg [prevLineStart $beg]
  391.     set backpos [nextLineStart [lindex $lst 0]]
  392.     # is there a comment at the end of the line? if so scan back to the character we want
  393.     if {![catch {search -s -f 0 -r 1 -l $pbeg "//\[^\r\n\]*\[\n\r\]" $backpos} compos]} {
  394.     set compos [lindex $compos 0]
  395.     if {[pos::compare $compos > $pbeg]} {
  396.         set backpos [pos::math $compos + 1]
  397.     }    
  398.     }
  399.     global indentationAmount
  400.     if {[pos::compare [set backpos [pos::math $backpos - 2]] > [minPos]]} {
  401.     set lst [search -s -f 0 -r 1 -m 0 "\[^ \t\r\n\]" $backpos]
  402.     switch -- [lookAt [lindex $lst 0]] {
  403.         "\{" {
  404.         incr lwhite $indentationAmount
  405.         } 
  406.         ":" {
  407.         # expression is better for odd indentationAmounts
  408.         incr lwhite [expr {$indentationAmount - $indentationAmount/2}]
  409.         } 
  410.         "\)" {
  411.         # see if we're in a if-elseif-else with no '{}' and indent
  412.         if {[C_isLineNBI $pstart]} {
  413.             incr lwhite $indentationAmount
  414.         }
  415.         }
  416.         "e" {
  417.         if { [getText [pos::math [lindex $lst 0] - 3] \
  418.           [pos::math [lindex $lst 0] + 1]] == "else" } {
  419.             if {[C_isLineNBI $pstart]} {
  420.             incr lwhite $indentationAmount
  421.             }
  422.         }
  423.         
  424.         }
  425.     }
  426.     }
  427.     
  428.     switch -- $nextC {
  429.     "\}" {
  430.         incr lwhite [expr -$indentationAmount]
  431.     }
  432.     "<" -
  433.     ">" {            
  434.         # indent for '<<' and '>>' in multi-line C++ stream manipulation
  435.         if {$nextP == "<<" || $nextP == ">>"} {
  436.         set strm [search -s -f 1 -r 1 "^\[^${nextC}\]+${nextP}" $pbeg]
  437.         set lwhite [posX [pos::math [lindex $strm 1] - 2]]
  438.         set gotoEol 1
  439.         }
  440.     }
  441.     }
  442.     # Check if we're in a multi-line '(.....)' if so align to start
  443.     global useFasterButWorseIndentation
  444.     if {!$useFasterButWorseIndentation && ![catch {matchIt ")" $beg 200} paren]} {
  445.     set lwhite [posX [pos::math $paren + 1]]
  446.     set gotoEol 1
  447.     }
  448.  
  449.     if {[regexp "^(case\[ \t\].*|\[a-zA-Z\]+):(\[^:\]|\$)" $nextword] && $lwhite > 3 \
  450.       && ![info exists ignore_trailers]} {
  451.     incr lwhite [expr -$indentationAmount/2]
  452.     }
  453.     # get indentation level    
  454.     return [incr lwhite [lindex $ind 1]]
  455. }
  456.  
  457. ## 
  458.  # -------------------------------------------------------------------------
  459.  # 
  460.  # "C++::electricLeft" --
  461.  # 
  462.  #  For those who like to place left braces after 'for', 'else' etc. on 
  463.  #  their own line, this will ensure the left brace is correctly placed.
  464.  # -------------------------------------------------------------------------
  465.  ##
  466. proc C++::electricLeft {} {
  467.     if {[string trim [getText [lineStart [getPos]] [getPos]]] == ""} {
  468.     if {[C_isLineNBI [prevLineStart [getPos]]]} {
  469.         set p [lineStart [getPos]]
  470.         insertText "\{\r"
  471.         goto $p
  472.         shiftLeft
  473.         goto [nextLineStart $p]
  474.         bind::IndentLine
  475.         return
  476.     }
  477.     }
  478.     ::electricLeft
  479. }
  480.  
  481. proc C::electricLeft {} {C++::electricLeft}
  482.  
  483. ## 
  484.  # -------------------------------------------------------------------------
  485.  #     
  486.  # "recurseNoBraceIndent" --
  487.  #    
  488.  # Scans back until we no longer have a 'no brace indent'.  A 'no brace
  489.  # indent' is a 'for', 'if' etc which didn't use '{ ...  }'
  490.  # -------------------------------------------------------------------------
  491.  ##
  492. proc C_recurseNoBraceIndent {pos offset {iselse 0}} {
  493.     set pos [prevLineStart $pos]
  494.     if {[C_isLineNBI $pos]} {
  495.     global indentationAmount
  496.     if {$iselse} {
  497.         set p [text::firstNonWsPos $pos]
  498.         set t [getText $p [pos::math $p + 10]]
  499.         if {[regexp  "(else\[ \t\]+)?if.*" $t]} {
  500.         return [expr {$offset -$indentationAmount}]
  501.         }
  502.     }
  503.     return [C_recurseNoBraceIndent $pos [incr offset [expr -$indentationAmount]] $iselse]
  504.     }
  505.     return $offset
  506.     
  507. }
  508.  
  509. ## 
  510.  # -------------------------------------------------------------------------
  511.  #     
  512.  # "isLineNBI" --
  513.  #    
  514.  #  Tests if the given line is a 'no brace indent'.  'pos' is the beginning
  515.  #  of the line in question, else this proc will fail.
  516.  # -------------------------------------------------------------------------
  517.  ##
  518. set C_recNBI "^\[ \t\]*((\}?\[ \t\]*(if|else\[ \t\]+if)|for)\[ \t\]*\\(.*\\)|\}?\[ \t\]*else)\[ \t\]*(//\[^\r\n\]*)?\[ \t\r\n\]*"
  519. proc C_isLineNBI {pos} {
  520.     global C_recNBI
  521.     if {![catch {search -s -f 1 -r 1 -l [nextLineStart [nextLineStart $pos]] $C_recNBI $pos} ifelse] } {
  522.     if {[pos::compare $pos == [lindex $ifelse 0]]} {
  523.         if {[pos::compare [lindex $ifelse 1] == [maxPos]] || ([lookAt [lindex $ifelse 1]] != "\{")} {
  524.         return 1 
  525.         }
  526.     }
  527.     }
  528.     return 0
  529. }
  530.  
  531. # use 'catch' to call this proc: error = no, otherwise returns st,end pos
  532. proc C_inCComment {pos} {
  533.     set cS [search -s -f 0 -r 0 -l [pos::math $pos - 1000] "/*" $pos]
  534.     set cE [search -s -f 1 -r 0 -l [pos::math $pos + 1000] "*/" [lindex $cS 1]]
  535.     if {[pos::compare $pos >= [lindex $cE 1]] } {
  536.     error "No"
  537.     } else {
  538.     return [list [lindex $cS 0] [lindex $cE 1]]
  539.     }
  540. }
  541.  
  542. # look for '<<' and '(...)' multi lines.
  543. proc C_isLineMulti {pos} {
  544.     # look for multi-line '(...)'
  545.     if { ![catch {search -s -f 0 -r 1 -l $pos {\).*$} [nextLineStart $pos]} paren] \
  546.       && [pos::compare [nextLineStart $pos] == [pos::math [lindex $paren 1] + 1 ]] } {
  547.     if {[catch {matchIt "\)" [pos::math [lindex $paren 0] - 1] 200} realStart]} {
  548.         return -1
  549.     }
  550.     if {[pos::compare [lineStart $realStart] != [lineStart [lindex $paren 0]]] } {
  551.         set lst [search -s -f 0 -r 1 -i 0 "^\[ \t\]*\[^ \t\r\n\]" $realStart]
  552.         return [posX [pos::math [lindex $lst 1] - 1]]
  553.     }
  554.     }
  555.     # look for multi-line '<<' or '>>'
  556.     set p $pos
  557.     while {![catch {search -s -f 1 -r 1 -l [nextLineStart $p] "^\[ \t\]*(<<|>>)" $p} strm] } {
  558.     set p [prevLineStart $p]
  559.     }
  560.     if { $p != $pos } {
  561.     set lst [search -s -f 1 -r 1 -i 0 "^\[ \t\]*\[^ \t\r\n\]" $p]
  562.     return [posX [pos::math [lindex $lst 1] - 1]]
  563.     }
  564.     
  565.     return -1
  566.     
  567. }
  568.  
  569. ## 
  570.  # -------------------------------------------------------------------------
  571.  #   
  572.  # "C_indentCommentLine" --
  573.  #  
  574.  #  Indents a line within a multi-line /* ... */ comment correctly.
  575.  # -------------------------------------------------------------------------
  576.  ##
  577. proc C_indentCommentLine {beg cS cE} {
  578.     set lwhite [text::minSpaceForm [getText [lineStart $cS] $cS]]
  579.     if {[pos::compare $beg != [lineStart [lindex $cE 0]]] \
  580.       || [text::firstNonWs [pos::math $beg - 1]] == "*" } {
  581.     append lwhite " "    
  582.     }
  583.     
  584.     set text [getText $beg [nextLineStart $beg]]
  585.     regexp "^\[ \t\]*" $text white
  586.     set len [string length $white]
  587.     if {$white != $lwhite} {
  588.     replaceText $beg [pos::math $beg + $len] $lwhite
  589.     }
  590.     goto [pos::math $beg + [expr {[string length $lwhite] +1}]]
  591. }
  592.  
  593.  
  594. ## 
  595.  # -------------------------------------------------------------------------
  596.  #   
  597.  # "C_prevCodeIndent" --
  598.  #  
  599.  #  Find the indent of the previous line
  600.  #  -  If it's the start of the file, return 0 0 (special case)
  601.  #  else
  602.  #  -  if it's a C++ comment, keep looking backwards (so you can offset
  603.  #     C++ comments if you so desire)
  604.  #  -  if it's a C comment, get the indentation of the '/*' not some
  605.  #     intermediate point.
  606.  # -------------------------------------------------------------------------
  607.  ##
  608. proc C_prevCodeIndent {pos} { 
  609.     while {1} {
  610.     if {[pos::compare $pos == [minPos]] \
  611.       || ([catch {search -s -m 0 -f 0 -r 1 -i 0 "^\[ \t\]*\[^ \t\r\n\]" $pos} p]) \
  612.       || $p == "[minPos] [minPos]" } {
  613.         return [list [minPos] [pos::math [minPos] + 1]]
  614.     } else {
  615.         set pp [doubleLookAt [pos::math [lindex $p 1] - 1]]
  616.         if { $pp == "//" } {
  617.         set pos [pos::math [lindex $p 0] - 1]
  618.         } elseif { [string index $pp 0] == "#" } {
  619.         global indentMacros
  620.         if {$indentMacros == "code 0"} {
  621.             break
  622.         }
  623.         set pos [pos::math [lindex $p 0] - 1]
  624.         } elseif { $pp == "*/" } {
  625.         set pos [lindex [search -s -f 0 -r 0 "/*" \
  626.           [pos::math [lindex $p 0] - 1]] 0]
  627.         } elseif { ![catch {set comment [C_inCComment [lindex $p 0]]} ] } {
  628.         set pos [pos::math [lineStart [lindex $comment 0]] - 1]
  629.         #return [text::indentation [lindex $comment 0]] (old style)
  630.         } else {
  631.         break
  632.         }
  633.     }
  634.     }
  635.     return $p
  636. }
  637.  
  638.  
  639. # ◊◊◊◊ Electric routines ◊◊◊◊ #
  640.  
  641. proc C::carriageReturn {} {C++::carriageReturn}
  642. proc C::OptionTitlebar {} {C++::OptionTitlebar}
  643. proc C::OptionTitlebarSelect {item} {C++::OptionTitlebarSelect $item}
  644.  
  645. ## 
  646.  # -------------------------------------------------------------------------
  647.  #     
  648.  # "C++::carriageReturn" --
  649.  #    
  650.  #    Called by the general routine 'carriageReturn'.    We know    no selection 
  651.  #    exists, and we are not inside a block comment.
  652.  # -------------------------------------------------------------------------
  653.  ##
  654. proc C++::carriageReturn {} {
  655.     if {[lookAt [pos::math [getPos] - 1]] == ":"} {
  656.     if { [lookAt [getPos]] == "\r" } {
  657.         bind::IndentLine
  658.         endOfLine
  659.         insertText "\r"
  660.     } else {
  661.         set pos [getPos]
  662.         endOfLine
  663.         set t [getText $pos [getPos]]
  664.         replaceText $pos [getPos] ""
  665.         bind::IndentLine
  666.         endOfLine
  667.         insertText "\r"
  668.         insertText $t
  669.     }
  670.     } else {
  671.     insertText "\r"
  672.     }
  673.     catch {bind::IndentLine}
  674. }
  675.  
  676. proc C++::OptionTitlebar {} {
  677.     if {![catch {C++::tryIDEget} ret] && ![regexp {\(No} $ret] } { return $ret }
  678.     # else just scan through, provided the scan will function
  679.     set cid [scancontext create]
  680.     set lines {}
  681.     scanmatch $cid {#.*include.*(<|")(.*)(>|")}  {lappend lines $matchInfo(submatch1)}
  682.     set fid [open [win::Current] "r"]
  683.     scanfile $cid $fid
  684.     close $fid
  685.     scancontext delete $cid
  686.     return [lsort -ignore $lines]
  687. }
  688.  
  689. proc C++::OptionTitlebarSelect {fname} {
  690.     C++::tryIDEedit $fname
  691. }
  692.  
  693. proc C++::tryIDEget {} {
  694.     global IDE
  695.     switch -- $IDE {
  696.     1 {thinkGetIncludeFiles}
  697.     0 {cw::getIncludeFiles}
  698.     2 {error "No IDE at all!"}
  699.     }
  700. }
  701.  
  702. proc C++::tryIDEedit {fname} {
  703.     global IDE
  704.     switch -- $IDE {
  705.     1 {thinkEditIncludeFile $fname}
  706.     0 {cw::editIncludeFile $fname}
  707.     2 {error "No IDE at all!"}
  708.     }
  709. }
  710.  
  711.