home *** CD-ROM | disk | FTP | other *** search
/ Internet File Formats / InternetFileFormatsCD.bin / text / latex / mac / alpha60.hqx / Tcl / SystemCode / latexEngine.tcl < prev    next >
Encoding:
Text File  |  1995-07-16  |  29.7 KB  |  954 lines

  1. #############################################################################
  2. #############################################################################
  3. #
  4. # latexEngine.tcl (called from latex.tcl)
  5. #
  6. # The LaTeX engine
  7. #
  8. #############################################################################
  9. #
  10. # Author:  Tom Scavo (trscavo@syr.edu)
  11. #
  12. #############################################################################
  13. #############################################################################
  14.  
  15.  
  16. #--------------------------------------------------------------------------
  17. # Mark Menu:
  18. #--------------------------------------------------------------------------
  19.  
  20. proc TeXMarkFile {} {
  21.     set end [maxPos]
  22.     set pos 0
  23.     set l {}
  24.  
  25.     # Remove all previous marks in this file?
  26.     set exp {\\((sub)*section|chapter)(\[.*\]|\*)?{([^{}]*)}}
  27.     while {![catch {search -s -f 1 -r 1 -m 0 -i 0 $exp $pos} res]} {
  28.         set start [lindex $res 0]
  29.         set end [lindex $res 1]
  30.         set text [getText $start $end]
  31.         if {[regexp {\{(.*)\}} $text dummy mtch]} {
  32.             set lab ""
  33.             if {[regexp {(sub)*section} $text title]} then {
  34.                 append lab $l [format "%[expr [string length $title] - 7]\s" ""]
  35.             } else {
  36.                 set l {   }
  37.             }
  38.             # WTP 
  39.             regsub -all "\[\ \r\t\]\+" $mtch { } mtch
  40.             if {[string length $mtch] > 30} { set mtch "[string range $mtch 0 26]..." }
  41.             #
  42.             append lab $mtch
  43.             regsub -all {[<(]} $lab {} lab
  44.             regsub -all {[)>]} $lab {╚} lab
  45.             regsub -all {[!^/]} $lab {|} lab
  46.             setNamedMark $lab [lineStart [expr $start - 1]] $start $start
  47.         }
  48.         set pos [expr $end+1]
  49.     }
  50. }
  51.  
  52. #--------------------------------------------------------------------------
  53. # Command-double-clicking:
  54. #--------------------------------------------------------------------------
  55. # In TeX mode, use Cmd-double-clicks to follow references and citations,
  56. # or open input files.
  57. #
  58. # (written by Tom Pollard and Andreas Amann)
  59. #
  60. # Remaining bugs:
  61. #   - searches are successful even if the pattern is commented out
  62.     
  63. proc TeXDblClick {from to} {
  64.     global TeXmodeVars TeXSearchPath
  65.  
  66.     # Reset the search path list, forcing it to be rebuilt.
  67.     # (It would improve responsiveness if the searchPath were only
  68.     # rebuilt as needed, perhaps at startup and when requested by the user.)
  69.     #
  70.     set TeXSearchPath {}
  71.     
  72.     set bibPat "\\bibliography\{"
  73.     set theBibPat "\\begin\{thebibliography"
  74.     set bibitemPat {\bibitem(\[[^\]*\])?\{}
  75.     set labelPat "\\label\{"
  76.      set bibTopPat {@([a-zA-Z]+)[\{\(][     ]*}
  77.     
  78.     # Extend selection to largest string delimited by commas or curly-braces
  79.     set text [string trim [eval getText [TeXExtendArg $from $to]]]
  80.  
  81.     # Quote regexp-active characters in case we use $text in a regexp search
  82.     set qtext [quoteExpr $text]
  83.     
  84.     # Set $cmd to TeX command for which the selection is an argument, but
  85.     # only if user clicked on a valid command argument.
  86.     set cmd {}
  87.     if {[set mtch [findCommandWithParts $from 0]] != ""} {
  88.         set beg [lindex $mtch 0]
  89.         set arg [lindex $mtch 2]
  90.         set end [lindex $mtch 3]
  91.         # Make sure the user clicked within a TeX argument and not
  92.         # on the command name itself
  93.         if {$from > $arg && $to < $end} {
  94.             set cmd [extractCommandName [getText $beg $arg]]
  95.         }
  96.     }
  97.     
  98.     # \cite, \nocite, etc.
  99.     # 
  100.     if {[lsearch -exact $TeXmodeVars(citeCommands) $cmd] >= 0} {
  101.         
  102.         # Check first for a \thebibliography environment...
  103.         if {![catch {search -f 0 -r 0 -m 0 -s $theBibPat [maxPos]} mtch]} {
  104.             set bibPos [lindex $mtch 1]
  105.             if {![catch {search -f 1 -r 1 -m 0 -s "$bibitemPat$qtext\}" $bibPos} mtch]} {
  106.                 pushMark
  107.                 eval select $mtch
  108.                 message "press <Ctl .> to return to original cursor position"
  109.                 return
  110.             }
  111.         }
  112.         
  113.         # ...otherwise get the bib file name(s) from a \bibliography command
  114.         if {![catch {search -f 0 -r 0 -s -m 0 $bibPat [maxPos]} mtch]} {
  115.             # Get ALL the bib file names
  116.             set beg [lindex $mtch 1]
  117.             set end [matchIt "\{" $beg]
  118.             set fnames "[split [getText $beg $end] ,]"
  119.             # Check every file
  120.             foreach fname $fnames {
  121.                 set filename [absolutePath "$fname.bib"]
  122.                 if {[set pos [searchInFile $filename  $bibTopPat$qtext]] >= 0} {
  123.                     openFileQuietly $filename
  124.                     goto $pos
  125.                     return
  126.                 }    
  127.             }
  128.             if {[llength $TeXSearchPath] == 0} {
  129.                 set TeXSearchPath [buildTeXSearchPath]
  130.             }
  131.             foreach fname $fnames {
  132.                     foreach folder $TeXSearchPath {
  133.                         set filename "$folder$fname.bib"
  134.                             if {[set pos [searchInFile $filename $bibTopPat$qtext]] >= 0} {
  135.                                 openFileQuietly $filename
  136.                                 goto $pos
  137.                                 return
  138.                             }
  139.                     }
  140.             }
  141.             beep
  142.             message "can't find \"$text\" in the .bib file(s)"
  143.             
  144.         } else {
  145.             beep
  146.             message "can't find a \\bibliography"
  147.         }
  148.         
  149.     # \ref, \pageref, etc.
  150.     } elseif {[lsearch -exact $TeXmodeVars(refCommands) $cmd] >= 0} {
  151.         if {![catch {search -f 1 -r 0 -m 0 -s "$labelPat$text\}" 0} mtch]} {
  152.             pushMark
  153.             eval select $mtch
  154.             message "press <Ctl .> to return to original cursor position"
  155.         } else {
  156.             message {No matching \label found}
  157.         }
  158.         
  159.     #    \input
  160.     } elseif { $cmd == "input" } {
  161.         openTeXFile $text ".tex"
  162.  
  163.     #    \include
  164.     } elseif { $cmd == "include" } {
  165.         openTeXFile "${text}.tex"
  166.  
  167.     # \documentclass or \LoadClass
  168.     } elseif { $cmd == "documentclass" || $cmd == "LoadClass" } {
  169.         openTeXFile "${text}.cls"
  170.         
  171.     # \usepackage or \RequirePackage
  172.     } elseif { $cmd == "usepackage" || $cmd == "RequirePackage" } {
  173.         openTeXFile "${text}.sty"
  174.         
  175.     # \bibliography
  176.     } elseif {$cmd == "bibliography"} {
  177.         openTeXFile "${text}.bib"
  178.         
  179.     # \bibliographystyle
  180.     } elseif {$cmd == "bibliographystyle"} {
  181.         openTeXFile "${text}.bst"
  182.         
  183.     # box-making macro ($boxMacroName)
  184.     } elseif {$cmd == $TeXmodeVars(boxMacroName)} {
  185.         openTeXFile $text 
  186.         
  187.     # Other
  188.     } else {
  189.         select $from $to
  190.         message {Command-double-click on the required argument of an underlined LaTeX command}
  191.     }
  192. }
  193.  
  194. # Open a TeX source file. 
  195. #
  196. proc openTeXFile {file {ext ""}} {
  197.     global TeXSearchPath
  198.     # Determine absolute file specification
  199.     # Ignore $ext if $file already has an extension
  200.     if {[string length [file extension $file]] == 0} {
  201.         set file $file$ext
  202.     }
  203.     set filename [absolutePath $file]
  204.     if {![catch {openFileQuietly $filename}]} { 
  205.         message $filename
  206.         return 
  207.     }
  208.     if {[llength $TeXSearchPath] == 0} {
  209.         set TeXSearchPath [buildTeXSearchPath]
  210.     }
  211.     foreach folder $TeXSearchPath {
  212.         set filename "$folder$file$ext"
  213.         if {![catch {openFileQuietly $filename}]} {
  214.             message $filename
  215.             return     
  216.         }
  217.     }
  218.     beep
  219.     message "can't find TeX input file \"$file\""
  220. }
  221.  
  222. # Return a list of folders in which to search for TeX input files, 
  223. # including the TeXInputs folder (if it exists) and any folders of 
  224. # the form "*input*" in the TeX application directory.  The current
  225. # folder is not included in the list.
  226. #
  227. # (The TeXInputs folder is assigned from the AppPaths submenu.)
  228. #
  229. proc buildTeXSearchPath {} {
  230.     global TeXInputs texPath
  231.     message "building TeX search path..."
  232.     set folders {}
  233.     
  234.     # The local 'TeXInputs' folder:
  235.     if {[info exists TeXInputs] && [string length $TeXInputs] > 0} { 
  236.         set folders [concat $folders [list $TeXInputs]]
  237.         # Search subfolders one level deep:
  238.         set folders [concat $folders [listSubfolders $TeXInputs 1]]
  239.     }
  240.  
  241.     # Any "*inputs*" folders in the TeX application folder:
  242.     if {[info exists texPath] && [string length $texPath] > 0} { 
  243.         set TeXDir [file dirname $texPath]
  244.         set folders [concat $folders [list $TeXDir]]
  245.         # Bug:  'glob' is case sensitive!
  246.         foreach folder [glob "$TeXDir:*\[Ii\]nputs*"] {
  247.             set folders [concat $folders [list $folder]]
  248.             # Search subfolders one level deep:
  249.             set folders [concat $folders [listSubfolders $folder 1]]
  250.         }
  251.     }
  252.  
  253.     # Make sure each folder ends with a colon
  254.     set result {}
  255.     foreach folder $folders {
  256.         set folder "[string trimright $folder {:}]:"
  257.         set result [concat $result [list $folder]]
  258.     }
  259.     return $result
  260. }
  261.  
  262.  
  263. # Extend the argument around the position $from.
  264. # (Args are delimited by commas or curly-braces.)
  265. proc TeXExtendArg {from {to 0}} {
  266.     if {$to == 0} { set to $from }
  267.     set result [list $from $to]
  268.     if {![catch {search -f 0 -r 1 -s -m 0 "\[,\{\]" $from} mtch0]} {
  269.         if {![catch {search -f 1 -r 1 -s -m 0 "\[,\}\]" $to} mtch1]} {
  270.             set from [lindex $mtch0 1]
  271.             set to [lindex $mtch1 0]
  272.             ## Embedded braces in the arg probably mean that the user
  273.             ## clicked outside a valid command argument
  274.             if {[regexp "\[\{\}\]" [getText $from $to]] == 0} {
  275.                 set result [list $from $to]
  276.             }
  277.         }
  278.     }
  279.     return $result
  280. }
  281.  
  282. # Find a LaTeX command with arguments in either direction.
  283. # (see findCommandWithArgs in latexMacros.tcl)
  284. # This version returns the positions at which the command options 
  285. # and arguments start, as well.
  286. proc findCommandWithParts {pos direction} {
  287.     set searchString {\\([^a-zA-Z\t\r]|[a-zA-Z]+\*?)(\[.*\])*({[^{}]*})?}
  288.     if {![catch {search -s -f $direction -r 1 $searchString $pos} mtch]} {
  289.         set beg [lindex $mtch 0]
  290.         set end [lindex $mtch 1]
  291.         regexp -indices $searchString [getText $beg $end] all cmd opt arg
  292.         set opt [expr $beg + [lindex $opt 0]]
  293.         set arg [expr $beg + [lindex $arg 0]]
  294.         return [list $beg $opt $arg $end]
  295.     } else {
  296.         return ""
  297.     }
  298. }
  299.  
  300. #--------------------------------------------------------------------------
  301. # Insertion routines:
  302. #--------------------------------------------------------------------------
  303.  
  304. # Returns a modified text string if the string $text is non-null, 
  305. # and the null string otherwise.  The argument 'operation' is a 
  306. # string directing 'doPrefixText' to either "insert" or "remove" 
  307. # $prefixString to/from each line of $text.  (This routine is
  308. # adapted from strings.tcl.)
  309. proc doPrefixText {operation prefixString text} {
  310.     if {$text == ""} {return ""}
  311.     set pref [quoteExpr $prefixString]
  312.     if {$operation == "insert"} then {
  313.         set trailChar ""
  314.         set textLen [string length $text]
  315.         if {[string index $text [expr $textLen-1]] == "\r"} then {
  316.             set text [string range $text 0 [expr $textLen-2]]
  317.             set trailChar "\r"
  318.         }
  319.         set str \r$prefixString
  320.         regsub -all \r $text $str text
  321.         return $prefixString$text$trailChar
  322.     } elseif {$operation == "remove"} then {
  323.         regsub -all \r$pref $text \r text
  324.         regsub ^$pref $text "" text
  325.         return $text
  326.     }
  327. }
  328.  
  329. # Shift each line of $text to the right by inserting a string of
  330. # $whitespace characters at the beginning of each line, returning
  331. # the resulting string.
  332. proc shiftTextRight {text whitespace} {
  333.     return [doPrefixText "insert" $whitespace $text]
  334. }
  335.  
  336. # Return a string of whitespace characters from the beginning 
  337. # of the line containing $pos up to the first non-whitespace 
  338. # character.
  339. proc getIndentation {pos} {
  340.     set text [getText [lineStart $pos] [nextLineStart $pos]]
  341.     regexp {^[ \t]*} $text theIndentation
  342.     return $theIndentation
  343. }
  344.  
  345. # Return an "indented carriage return" if any character preceding 
  346. # the insertion point (on the same line) is a non-whitespace 
  347. # character.  Otherwise, return the null string.
  348. proc openingCarriageReturn {} {
  349.     set pos [getPos]
  350.     set end $pos
  351.     set start [lineStart $pos]
  352.     set text [getText $start $end]
  353.     if {[isWhitespace $text]} then {
  354.         if {$start == $end} {return [getIndentation $pos]} {return ""}
  355.     } else {
  356.         return "\r[getIndentation $pos]"
  357.     }
  358. }
  359. # Return an "indented carriage return" if any character following 
  360. # the insertion point (on the same line) is a non-whitespace 
  361. # character.  Otherwise, return the null string.
  362. proc closingCarriageReturn {} {
  363.     set pos [selEnd]
  364.     if {[isSelection] && ($pos == [lineStart $pos])} then {
  365.         return "\r"
  366.     } else {
  367.         set start $pos
  368.         set end [nextLineStart $start]
  369.         set text [getText $start $end]
  370.         if {[isWhitespace $text]} then {
  371.             return ""
  372.         } else {
  373.             return "\r[getIndentation $pos]"
  374.         }
  375.     }
  376. }
  377.  
  378. # Insert an object at the insertion point. If there is a selection and the 
  379. # global variable 'deleteObjectNoisily' is false, quietly delete the selection 
  380. # (like 'paste'). Otherwise, prompt the user for the appropriate action. 
  381. # Returns true if the object is ultimately inserted, and false if the 
  382. # user cancels the operation. 
  383. proc insertObject {objectName} {
  384.     global TeXmodeVars
  385. #     if {$TeXmodeVars(expertMode) == 0} then {
  386. #         if {![isInDocument]} then {
  387. #             if {$TeXmodeVars(searchNoisily)} {beep}
  388. #             case [askyesno "The cursor is not in the document environment.\
  389. #                             \rContinue the operation?"] in {
  390. #                 "yes" {}
  391. #                 "no" {error "insertObject:  out of context"}
  392. #             }
  393. #         }
  394. #     }
  395.     if {[isSelection]} then {
  396.         if {$TeXmodeVars(deleteObjectNoisily)} then {
  397.             case [askyesno -c "Delete selection?"] in {
  398.                 "yes" {deleteText [getPos] [selEnd]}
  399.                 "no" {backwardChar}
  400.                 "cancel" {return 0}
  401.             }
  402.         } else {
  403.             deleteText [getPos] [selEnd]
  404.         }
  405.     }
  406.     insertText $objectName
  407.     return 1
  408. }
  409.  
  410. # Insert an object at the insertion point. If there is a selection, wrap 
  411. # it inside the parameters $left and $right. Returns true if there is a 
  412. # selection (in which case it will wrap), and false otherwise. 
  413. proc wrapObject {left right} {
  414. #     global TeXmodeVars
  415. #     if {$TeXmodeVars(expertMode) == 0} then {
  416. #         if {![isInDocument]} then {
  417. #             if {$TeXmodeVars(searchNoisily)} {beep}
  418. #             case [askyesno "The cursor is not in the document environment.\
  419. #                             \rContinue the operation?"] in {
  420. #                 "yes" {}
  421. #                 "no" {error "wrapObject:  out of context"}
  422. #             }
  423. #         }
  424. #     }
  425.     set currentPos [getPos]
  426.     set selected [isSelection]
  427.     if {$selected} then {
  428.         replaceText $currentPos [selEnd] $left [getSelect] $right
  429.     } else {
  430.         insertText $left "Ñ" $right
  431.     }
  432.     goto $currentPos
  433.     nextTabStop
  434.     return $selected
  435. }
  436.  
  437. # Builds and returns a LaTeX environment, that is, a \begin...\end 
  438. # pair, given the name of the environment, an argument string, 
  439. # and the environment body.  The body should be passed to this 
  440. # procedure fully formatted, including indentation.
  441. proc buildEnvironment {envName envArg envBody trailingComment} {
  442.     set indent [getIndentation [getPos]]
  443.     set envStr [openingCarriageReturn]
  444.     append envStr "\\begin{" $envName "}"
  445.     append envStr $envArg "\r"
  446.     append envStr $envBody
  447.     append envStr $indent "\\end{" $envName "}$trailingComment"
  448.     append envStr [closingCarriageReturn]
  449.     return $envStr
  450. }
  451.  
  452. # Inserts a LaTeX environment with the specified name, argument, 
  453. # and body at the insertion point.  Positions the cursor at the 
  454. # beginning of the environment, leaving any subsequent action to the 
  455. # calling procedure.  Deletes the current selection quietly if the 
  456. # global variable 'deleteEnvironmentNoisily' is false; otherwise 
  457. # the user is prompted for directions.  Returns true if the 
  458. # environment is ultimately inserted, and false if the user cancels 
  459. # the operation.
  460. proc insertEnvironment {envName envArg envBody} {
  461.     global TeXmodeVars
  462. #     if {$TeXmodeVars(expertMode) == 0} then {
  463. #         if {![isInDocument]} then {
  464. #             if {$TeXmodeVars(searchNoisily)} {beep}
  465. #             case [askyesno "The cursor is not in the document environment.\
  466. #                             \rContinue the operation?"] in {
  467. #                 "yes" {}
  468. #                 "no" {error "insertEnvironment:  out of context"}
  469. #             }
  470. #         }
  471. #     }
  472.     if {[isSelection]} then {
  473.         if {$TeXmodeVars(deleteEnvironmentNoisily)} then {
  474.             case [askyesno -c "Delete selection?"] in {
  475.                 "yes" {}
  476.                 "no" {backwardChar}
  477.                 "cancel" {return 0}
  478.             }
  479.         }
  480.     }
  481.     set start [getPos]
  482.     set end [selEnd]
  483.     set body [shiftTextRight $envBody [getIndentation $start]]
  484.     replaceText $start $end [buildEnvironment $envName $envArg $body "Ñ"]
  485.     goto $start
  486.     return 1
  487. }
  488.  
  489. # Inserts an environment with the given name, argument, and body at 
  490. # the insertion point.  Positions the cursor at the beginning of 
  491. # the environment, leaving any subsequent action to the calling 
  492. # procedure.  If there is currently a selection, cut and paste it 
  493. # into the body of the new environment, maintaining proper 
  494. # indentation; otherwise, insert a tab stop into the body of the
  495. # environment.  Returns true if there is a selection, and false 
  496. # otherwise.
  497. proc wrapEnvironment {envName envArg envBody} {
  498. #     global TeXmodeVars
  499. #     if {$TeXmodeVars(expertMode) == 0} then {
  500. #         if {![isInDocument]} then {
  501. #             if {$TeXmodeVars(searchNoisily)} {beep}
  502. #             case [askyesno "The cursor is not in the document environment.\
  503. #                             \rContinue the operation?"] in {
  504. #                 "yes" {}
  505. #                 "no" {error "wrapEnvironment:  out of context"}
  506. #             }
  507. #         }
  508. #     }
  509.     set start [getPos]
  510.     set end [selEnd]
  511.     set indent [getIndentation $start]
  512.     if {[isSelection]} then {
  513.         set text [getSelect]
  514.         set textLen [string length $text]
  515.         if {[string index $text [expr $textLen-1]] != "\r"} then {
  516.             append text "\r"
  517.         }
  518.         if {$start == [lineStart $start]} then {
  519.             set body [shiftTextRight $text \t]
  520.         } else {
  521.             set body "$indent[shiftTextRight $text \t]"
  522.         }
  523.         append body [shiftTextRight $envBody $indent]
  524.         set environment [buildEnvironment $envName $envArg $body "Ñ"]
  525.         set returnFlag 1
  526.     } else {
  527.         append body "$indent\tÑ\r" [shiftTextRight $envBody $indent]
  528.         set environment [buildEnvironment $envName $envArg $body "Ñ"]
  529.         set returnFlag 0
  530.     }
  531.     replaceText $start $end $environment
  532.     goto $start
  533.     return $returnFlag
  534. }
  535.  
  536. # A generic call to 'wrapEnvironment' used throughout latex.tcl:
  537. proc doWrapEnvironment {envName} {
  538.     if {[wrapEnvironment $envName "" ""]} then {
  539.         set msgText "selection wrapped"
  540.     } else {
  541.         set msgText "enter body of $envName environment"
  542.     }
  543.     nextTabStop
  544.     message $msgText
  545. }
  546.  
  547. # Inserts a structured document template at the insertion point.  
  548. # Three arguments are required:  the class name of the document, a 
  549. # preamble string, and a string containing the body of the document.  
  550. # If the preamble is null, a generic \usepackage statement is 
  551. # inserted; otherwise, the preamble is inserted as is.  This routine 
  552. # does absolutely no error-checking (this is totally left up to the 
  553. # calling procedure) and returns nothing.
  554. proc insertDocument {className preamble docBody} {
  555.     set docStr "\\documentclass\[Ñ\]{$className}\r"
  556.     if {$preamble == ""} then {
  557.         append docStr "\\usepackage\[Ñ\]{Ñ}\r\rÑ\r\r"
  558.     } else {
  559.         append docStr $preamble
  560.     }
  561.     append docStr [buildEnvironment "document" "" $docBody "\r"]
  562.     set start [getPos]
  563.     set end [selEnd]
  564.     replaceText $start $end $docStr
  565.     goto $start
  566.     return
  567. }
  568.  
  569. # Inserts a document template at the insertion point given the 
  570. # class name of the document to be inserted.  If ALL of the current
  571. # document is selected, then the routine wraps the text inside a
  572. # generic document template.  If the file is empty, a bullet is 
  573. # inserted in place of the document body.  If neither of these 
  574. # conditions is true, an alert is displayed, and no action is taken.  
  575. # Returns true if wrapping occurs, and false otherwise.
  576. proc wrapDocument {className} {
  577.     if {[isEmptyFile]} then {
  578.         append body "\rÑ\r\r"
  579.         set returnFlag 0
  580.     } else {
  581.         if {[isDocumentSelected]} then {
  582.             set text [getSelect]
  583.             append body "\r$text\r"
  584.             set returnFlag 1
  585.         } else {
  586.             alertnote "nonempty file:  delete text or \'Select All\'\
  587.                 from the Edit menu"
  588.             return 0
  589.         }
  590.     }
  591.     set docStr "\\documentclass\[Ñ\]{$className}\r"
  592.     append docStr "\\usepackage\[Ñ\]{Ñ}\r\rÑ\r\r"
  593.     append docStr [buildEnvironment "document" "" $body "\r"]
  594.     set start [getPos]
  595.     set end [selEnd]
  596.     replaceText $start $end $docStr
  597.     goto $start
  598.     nextTabStop
  599.     message "enter style (or leave blank)"
  600.     return $returnFlag
  601. }
  602.  
  603. #--------------------------------------------------------------------------
  604. # Booleans to determine the location of the insertion point
  605. #--------------------------------------------------------------------------
  606.  
  607. # Return true if the insertion point is before the preamble, and 
  608. # false otherwise.   Define "before the preamble" to be all text to 
  609. # the left of "\" in "\documentclass".
  610. proc isBeforePreamble {} {
  611.     set searchString "\\documentclass"
  612.     set searchResult [search -s -f 1 -r 0 -n $searchString [getPos]]
  613.     if {[llength $searchResult]} {
  614.         return 1
  615.     } else {
  616.         return 0
  617.     }
  618. }
  619.  
  620. # Return true if the insertion point is in the preamble, and false
  621. # otherwise.  Define "preamble" to be all text to the left of "\" 
  622. # in "\begin{document}", but not before the "\" in "\documentclass".
  623. proc isInPreamble {} {
  624.     set searchString "\\begin\{document\}"
  625.     set searchResult [search -s -f 1 -r 0 -n $searchString [getPos]]
  626.     if {[llength $searchResult]} {
  627.         return 1
  628.     } else {
  629.         return 0
  630.     }
  631. }
  632.  
  633. # Return true if the insertion point is in the document environment,
  634. # and false otherwise.  Define "document" to be all text between 
  635. # "\begin{document}" and "\end{document}", inclusive.
  636. proc isInDocument {} {
  637.     set pos [getPos]
  638.     if {$pos == 0} then {return 0}
  639.     set searchString "\\begin\{document\}"
  640.     set searchResult [search -s -f 0 -r 0 -n $searchString [expr $pos - 1]]
  641.     if {[llength $searchResult]} then {
  642.         set searchString "\\end\{document\}"
  643.         # adjust for the length of the search string:
  644.         set len [string length $searchString]
  645.         set searchResult [search -s -f 1 -r 0 -n $searchString [expr $pos - $len + 1]]
  646.         if {[llength $searchResult]} then {
  647.             return 1
  648.         } else {
  649.             return 0
  650.         }
  651.     } else {
  652.         return 0
  653.     }
  654. }
  655.  
  656. # Return true if the insertion point is after the document
  657. # environment, and false otherwise.  Define "after the document 
  658. # environment" to be all text to the right of "}" in "\end{document}".
  659. proc isAfterDocument {} {
  660.     set pos [getPos]
  661.     set searchString "\\end\{document\}"
  662.     set searchResult [search -s -f 0 -r 0 -n $searchString $pos]
  663.     if {[llength $searchResult]} {
  664.         set pos1 [lindex $searchResult 0]
  665.         set pos2 [lindex $searchResult 1]
  666.         if {$pos1 <= $pos && $pos < $pos2} then {
  667.             return 0
  668.         } else {
  669.             return 1
  670.         }
  671.     } else {
  672.         return 0
  673.     }
  674. }
  675.  
  676. proc isInMathMode {} {
  677.     set pos [getPos]
  678.     # Check to see if in LaTeX math mode:
  679.     set searchString {\\[()]}
  680.     set searchResult1 [search -s -f 0 -r 1 -n $searchString [expr $pos - 1]]
  681.     if {[llength $searchResult1]} then {
  682.         set delim1 [eval getText $searchResult1]
  683.     } else {
  684.         set delim1 "none"
  685.     }
  686.     set searchResult2 [search -s -f 1 -r 1 -n $searchString $pos]
  687.     if {[llength $searchResult2]} then {
  688.         set delim2 [eval getText $searchResult2]
  689.     } else {
  690.         set delim2 "none"
  691.     }
  692.     set flag1 [expr [string match {none} $delim1] && [string match {\\)} $delim2]]
  693.     set flag2 [expr [string match {\\(} $delim1] && [string match {none} $delim2]]
  694.     set flag3 [expr [string match {\\(} $delim1] && [string match {\\)} $delim2]]
  695.     set flag4 [expr [string match {\\(} $delim1] && [string match {\\(} $delim2]]
  696.     set flag5 [expr [string match {\\)} $delim1] && [string match {\\)} $delim2]]
  697.     if {$flag3} then {
  698.         return 1
  699.     } elseif {$flag1 || $flag2 || $flag4 || $flag5} then {
  700.         set messageString "unbalanced math mode delimiters"
  701.         beep
  702.         alertnote $messageString
  703.         error "isInMathMode:  $messageString"
  704.     }
  705.     # Check to see if in LaTeX displaymath mode:
  706.     set searchString {[^\\]\\\[|\\\]}
  707.     set searchResult1 [search -s -f 0 -r 1 -n $searchString [expr $pos - 1]]
  708.     if {[llength $searchResult1]} then {
  709.         set begPos [lindex $searchResult1 0]
  710.         set endPos [lindex $searchResult1 1]
  711.         if {[lookAt [expr $endPos - 1]] == "\["} then {
  712.             set delim1 [getText [expr $begPos + 1] $endPos]
  713.         } else {
  714.             set delim1 [eval getText $searchResult1]
  715.         }
  716.     } else {
  717.         set delim1 "none"
  718.     }
  719.     set searchResult2 [search -s -f 1 -r 1 -n $searchString $pos]
  720.     if {[llength $searchResult2]} then {
  721.         set begPos [lindex $searchResult2 0]
  722.         set endPos [lindex $searchResult2 1]
  723.         if {[lookAt [expr $endPos - 1]] == "\["} then {
  724.             set delim2 [getText [expr $begPos + 1] $endPos]
  725.         } else {
  726.             set delim2 [eval getText $searchResult2]
  727.         }
  728.     } else {
  729.         set delim2 "none"
  730.     }
  731.     set flag1 [expr [string match {none} $delim1] && [string match {\\\]} $delim2]]
  732.     set flag2 [expr [string match {\\\[} $delim1] && [string match {none} $delim2]]
  733.     set flag3 [expr [string match {\\\[} $delim1] && [string match {\\\]} $delim2]]
  734.     set flag4 [expr [string match {\\\[} $delim1] && [string match {\\\[} $delim2]]
  735.     set flag5 [expr [string match {\\\]} $delim1] && [string match {\\\]} $delim2]]
  736.     if {$flag3} then {
  737.         return 1
  738.     } elseif {$flag1 || $flag2 || $flag4 || $flag5} then {
  739.         set messageString "unbalanced math mode delimiters"
  740.         beep
  741.         alertnote $messageString
  742.         error "isInMathMode:  $messageString"
  743.     }
  744.     # Check to see if in math environment:
  745.     set envName [extractCommandArg [eval getText [searchEnvironment]]]
  746.     set envList [list "math" "displaymath" "equation" "eqnarray" "eqnarray*" "array"]
  747.     if {[lsearch -exact $envList $envName] == -1} {return 0} {return 1}
  748. }
  749.  
  750. #--------------------------------------------------------------------------
  751. # Routines to dissect LaTeX commands
  752. #--------------------------------------------------------------------------
  753.  
  754. # Given a LaTeX command string, extract and return the command name.
  755. proc extractCommandName {commandStr} {
  756.     if {[regexp {\\([^a-zA-Z\t\r]|[a-zA-Z]+\*?)} $commandStr dummy commandName]} then {
  757.         return $commandName
  758.     } else {
  759.         return ""
  760.     }
  761. }
  762.  
  763. # Given a LaTeX command string with at most one required argument and
  764. # no embedded carriage returns, extract and return the argument.
  765. # (Note:  this proc needs more testing.)
  766. proc extractCommandArg {commandStr} {
  767.     if {[regexp {\{(.*)\}} $commandStr dummy arg]} then {
  768.         return $arg
  769.     } else {
  770.         return ""
  771.     }
  772. }
  773.  
  774. #--------------------------------------------------------------------------
  775. # An environment search routine
  776. #--------------------------------------------------------------------------
  777.  
  778. # Search for the closest surrounding environment and return a list 
  779. # of two positions if the environment is found; otherwise return the 
  780. # empty list.  Assumes the LaTeX document is syntactically correct.   
  781. # The command 'eval select [searchEnvironment]' selects the "\begin" 
  782. # statement (with argument) of the surrounding environment.
  783. proc searchEnvironment {} {
  784.     watchCursor
  785.     set pos [getPos]
  786.     if {$pos == 0} {
  787.         return [list]
  788.     }
  789.     # adjust position if insertion point is contained in "\end{...}"
  790.     set searchPos [expr $pos - 1]
  791.     set searchString {\\end\{[^\{\}]*\}}
  792.     set searchResult [search -s -f 0 -r 1 -n $searchString $searchPos]
  793.     if {[llength $searchResult]} {
  794.         set pos1 [lindex $searchResult 0]
  795.         set pos2 [lindex $searchResult 1]
  796.         if {$pos1 < $pos && $pos < $pos2} then {
  797.             set searchPos [expr $pos1 - 1]
  798.         }
  799.     }
  800.     # begin reverse search:
  801.     set searchString {\\(begin|end)\{[^\{\}]*\}}
  802.     set searchResult [search -s -f 0 -r 1 -n $searchString $searchPos]
  803.     if {[llength $searchResult]} {
  804. #         set text [getText [lindex $searchResult 0] [lindex $searchResult 1]]
  805.         set text [eval getText $searchResult]
  806.     } else {
  807.         return [list]
  808.     }
  809.     set depthCounter 0
  810.     set commandName [extractCommandName $text]
  811.     while {$commandName == "end" || $depthCounter > 0} {
  812.         if {$commandName == "end"} {
  813.             set depthCounter [expr $depthCounter + 1]
  814.         } else {
  815.             set depthCounter [expr $depthCounter - 1]
  816.         }
  817.         set searchPos [expr [lindex $searchResult 0] - 1]
  818.         set searchResult [search -s -f 0 -r 1 -n $searchString $searchPos]
  819.         if {[llength $searchResult]} {
  820. #             set text [getText [lindex $searchResult 0] [lindex $searchResult 1]]
  821.             set text [eval getText $searchResult]
  822.         } else {
  823.             return [list]
  824.         }
  825.         set commandName [extractCommandName $text]
  826.     }
  827.     return $searchResult
  828. }
  829.  
  830. #--------------------------------------------------------------------------
  831. # Misc:
  832. #--------------------------------------------------------------------------
  833.  
  834. proc isInteger {str1} {
  835.     if { [regexp {(\+|-)?[0-9]+} $str1 str2] } then {
  836.         if {![string compare $str1 $str2]} {return 1} {return 0}
  837.     } else {
  838.         return 0
  839.     }
  840. }
  841.  
  842. proc isUnsignedInteger {str1} {
  843.     if { [regexp {[0-9]+} $str1 str2] } then {
  844.         if {![string compare $str1 $str2]} {return 1} {return 0}
  845.     } else {
  846.         return 0
  847.     }
  848. }
  849.  
  850. # Checks to see if there is a current selection.
  851. proc isSelection {} {
  852.     return [string length [getSelect]]
  853. }
  854. # proc isSelection {} {
  855. #     return [string length [getSelect]]
  856. # }
  857.  
  858. # Checks to see if the current window is empty, sans whitespace.
  859. proc isEmptyFile {} {
  860.     return [isWhitespace [getText 0 [maxPos]]]
  861. }
  862.  
  863. # If there is a selection, make sure it's uppercase.  Otherwise, 
  864. # check to see if the character after the insertion point is uppercase.
  865. proc isUppercase {} {
  866.     if {[isSelection]} then {
  867.         set text [getSelect]
  868.     } else {
  869.         set text [lookAt [getPos]]
  870.     }
  871.     return [expr {[string toupper $text] == $text}]
  872. }
  873.  
  874. # Returns true if the entire window is selected, and false otherwise.
  875. proc isDocumentSelected {} {
  876.     return [expr {([getPos] == 0) && ([selEnd] == [maxPos])}]
  877. }
  878.  
  879. # Takes any string and tests whether or not that string contains all 
  880. # whitespace characters.  Carriage returns are considered whitespace, 
  881. # as are spaces and tabs.  Also returns true for the null string.
  882. proc isWhitespace {anyString} {
  883.     set len [string length $anyString]
  884.     for {set i 0} {$i < $len} {incr i} {
  885.         set c [string index $anyString $i]
  886.         if {($c != "\ ") && ($c != "\t") && ($c != "\r")} then {return 0}
  887.     }
  888.     return 1
  889. }
  890.  
  891. # Select the line containing the insertion point.
  892. proc lineSelect {} {
  893.     goto [lineStart [getPos]]
  894.     nextLineSelect
  895. }
  896.  
  897. proc checkMathMode {procName mathModeFlag} {
  898. #     global TeXmodeVars
  899. #     if {$TeXmodeVars(expertMode) == 0} then {
  900. #         if {[isInDocument] && [isInMathMode] != $mathModeFlag} then {
  901. #             if {$TeXmodeVars(searchNoisily)} {beep}
  902. #             if {$mathModeFlag} then {
  903. #                 set messageString "You are not in math mode"
  904. #             } else {
  905. #                 set messageString "You are already in math mode"
  906. #             }
  907. #             case [askyesno "$messageString.\rContinue the operation?"] in {
  908. #                 "yes" {}
  909. #                 "no" {error "$procName:  out of context"}
  910. #             }
  911. #         }
  912. #     }
  913. }
  914.  
  915. # Check to see if the LaTeX symbol package has been loaded; if not, ask 
  916. # the user for directions.  Returns false if the package is not loaded
  917. # AND the user cancels the operation; otherwise, returns true.
  918. proc isSymbolPackageLoaded {} {
  919.     global searchNoisily
  920.     set begPos [getPos]
  921.     set endPos [selEnd]
  922.     set searchString {\\usepackage\{.*latexsym.*\}}
  923.     set searchResult [search -s -n -f 0 -m 0 -i 1 -r 1 $searchString $begPos]
  924.     if {[llength $searchResult] == 0} then {
  925.         case [askyesno -c "Insert the LaTeX symbol package?"] in {
  926.             "yes" {
  927.                 set searchString {\\documentclass(\[.*\])?\{.*\}}
  928.                 set searchResult [search -s -n -f 0 -m 0 -i 1 -r 1 $searchString $begPos]
  929.                 if {[llength $searchResult] == 0} then {
  930.                     set returnVal 0
  931.                     if {$searchNoisily} {beep}
  932.                     message "can't find \\documentclass"
  933.                 } else {
  934.                     goto [lindex $searchResult 1]
  935.                     set txt "\r\\usepackage\{latexsym\}"
  936.                     set offset [string length $txt]
  937.                     set begPos [expr $begPos + $offset]
  938.                     set endPos [expr $endPos + $offset]
  939.                     insertText $txt
  940.                     set returnVal 1
  941.                 }
  942.             }
  943.             "no" {set returnVal 1}
  944.             "cancel" {set returnVal 0}
  945.         }
  946.     } else {
  947.         set returnVal 1
  948.     }
  949.     select $begPos $endPos
  950.     return $returnVal
  951. }
  952.  
  953.  
  954.