From e0c6872cf40896c7be36b11dcc744620f10adf1d Mon Sep 17 00:00:00 2001 From: Norbert Preining Date: Mon, 2 Sep 2019 13:46:59 +0900 Subject: Initial commit --- macros/latex/contrib/tclldoc/README.txt | 45 + macros/latex/contrib/tclldoc/examples/README.txt | 40 + macros/latex/contrib/tclldoc/examples/parsetcl.dtx | 1155 +++++ macros/latex/contrib/tclldoc/examples/parsetcl.ins | 54 + macros/latex/contrib/tclldoc/examples/pdf.dtx | 796 ++++ macros/latex/contrib/tclldoc/examples/pdf.ins | 55 + macros/latex/contrib/tclldoc/tclldoc.dtx | 4065 ++++++++++++++++++ macros/latex/contrib/tclldoc/tclldoc.ins | 51 + macros/latex/contrib/tclldoc/tclldoc.pdf | Bin 0 -> 436262 bytes macros/latex/contrib/tclldoc/tools/README.txt | 46 + macros/latex/contrib/tclldoc/tools/eemenu.dtx | 4477 ++++++++++++++++++++ macros/latex/contrib/tclldoc/tools/eemenu.ins | 66 + macros/latex/contrib/tclldoc/tools/sourcedtx.dtx | 234 + macros/latex/contrib/tclldoc/tools/sourcedtx.ins | 31 + 14 files changed, 11115 insertions(+) create mode 100644 macros/latex/contrib/tclldoc/README.txt create mode 100644 macros/latex/contrib/tclldoc/examples/README.txt create mode 100644 macros/latex/contrib/tclldoc/examples/parsetcl.dtx create mode 100644 macros/latex/contrib/tclldoc/examples/parsetcl.ins create mode 100644 macros/latex/contrib/tclldoc/examples/pdf.dtx create mode 100644 macros/latex/contrib/tclldoc/examples/pdf.ins create mode 100644 macros/latex/contrib/tclldoc/tclldoc.dtx create mode 100644 macros/latex/contrib/tclldoc/tclldoc.ins create mode 100644 macros/latex/contrib/tclldoc/tclldoc.pdf create mode 100644 macros/latex/contrib/tclldoc/tools/README.txt create mode 100644 macros/latex/contrib/tclldoc/tools/eemenu.dtx create mode 100644 macros/latex/contrib/tclldoc/tools/eemenu.ins create mode 100644 macros/latex/contrib/tclldoc/tools/sourcedtx.dtx create mode 100644 macros/latex/contrib/tclldoc/tools/sourcedtx.ins (limited to 'macros/latex/contrib/tclldoc') diff --git a/macros/latex/contrib/tclldoc/README.txt b/macros/latex/contrib/tclldoc/README.txt new file mode 100644 index 0000000000..8ee02ad0c7 --- /dev/null +++ b/macros/latex/contrib/tclldoc/README.txt @@ -0,0 +1,45 @@ +README for the tclldoc package: +=============================== + +The purpose of the tclldoc package and class is to +simplify using the classical doc/docstrip style of +literate programming with Dr. John Ousterhout's +Tool Command Language (Tcl, pronounced "tickle", +a.k.a. The Cool Language). The tclldoc package is +a bit like the doc package is for LaTeX, whereas +the tclldoc class more parallels the ltxdoc class. + +tclldoc.dtx contains an introduction to how one +uses the .dtx file format for writing Tcl code. + +The tclldoc class uses the tclldoc package for most +of its functionality. The tclldoc package depends +on the xdoc2 and docidx2e packages for some of its +functionality. The latter two packages can be found +in the CTAN directory + macros/latex/contrib/xdoc + +The tools subdirectory of this directory contains +two tools that you may find useful complements to +the tclldoc package. Both are Tcl scripts that make +it possible to source Tcl code stored in a .dtx file +without using docstrip to generate any .tcl file in +between. Naturally, they are both distributed as +pairs of .dtx and .ins files, so they can also be +used as examples of using the tclldoc package. + +The examples subdirectory of this directory contains +two more examples of Tcl packages written in the +doc/docstrip style of literate programming. One is +a package for writing PDF files, the other a package +for parsing Tcl scripts. + +The LaTeX Project Public License applies for the +tclldoc package and class, as well as to all the +examples provided in the tools and examples folders. + +More information about Tcl can be found on +http://www.tcl.tk/ + +Lars Hellström, +20 July 2003 diff --git a/macros/latex/contrib/tclldoc/examples/README.txt b/macros/latex/contrib/tclldoc/examples/README.txt new file mode 100644 index 0000000000..2d3d8abe5d --- /dev/null +++ b/macros/latex/contrib/tclldoc/examples/README.txt @@ -0,0 +1,40 @@ +README for the parsetcl and writepdf packages: +============================================== + +The parsetcl package implements basic parsing +of Tcl scripts, and is written completely in +Tcl. To generate parsetcl.tcl, run LaTeX on +parsetcl.ins. To typeset the documentation, +run LaTeX on parsetcl.dtx. To see an example +of what it does, you can try the following +commands in a Tcl shell: + + source parsetcl.tcl + auto_load parray + set tree [parsetcl::simple_parse_script [info body parray]] + parsetcl::format_tree $tree {} { } + +The tree variable will be set to a parser tree +for the body of the parray procedure. The last +command reformats this tree (mostly replacing +whitespace between list elements) to make the +structure more visible. + + +The writepdf package is written completely in +Tcl and provides basic commands for writing +well-formed PDF files. To generate writepdf.tcl, +run LaTeX on pdf.ins. To typeset the +documentation, run LaTeX on pdf.dtx. To see an +example of what it does, you can try the following +commands in a Tcl shell: + + source writepdf.tcl + source hellopdf.tcl + +This will generate a file hello.pdf that contains +a single page with the two words "Hello world!". + + +Lars Hellström, +20 July 2003 diff --git a/macros/latex/contrib/tclldoc/examples/parsetcl.dtx b/macros/latex/contrib/tclldoc/examples/parsetcl.dtx new file mode 100644 index 0000000000..22609d6fa4 --- /dev/null +++ b/macros/latex/contrib/tclldoc/examples/parsetcl.dtx @@ -0,0 +1,1155 @@ +% +% \iffalse +%<*driver> +\documentclass{tclldoc} + +\newcommand{\ParseTcl}{Parse\Tcllogo} +\providecommand{\href}[2]{#2} + +\begin{document} +\DocInput{parsetcl.dtx} +\end{document} +% +% \fi +% +% +% \title{\ParseTcl\ --- a parser for \Tcllogo\ code} +% \author{Lars Hellstr\"om} +% +% \maketitle +% +% +% \begin{abstract} +% \ParseTcl\ is a \Tcllogo\ package for parsing \Tcllogo\ code. +% It is written completely in \Tcllogo, and make heavy use of some +% new additions in \Tcllogo\,8.4. +% \end{abstract} +% +% \begin{tcl} +%<*pkg> +namespace eval parsetcl {} +package require Tcl 8.4 +package provide parsetcl 0.1 +% \end{tcl} +% \setnamespace{parsetcl} +% +% \tableofcontents +% +% \section{Parser results} +% +% The values returned by the parser are conceptually rooted trees, but +% technically the return values are lists. The general format of a +% subtree is +% \begin{quote} +% \word{type} \word{interval} \word{text} \word{subtree}\regstar +% \end{quote} +% where there is one \word{subtree} for each component that remains if +% the root of the subtree under consideration is deleted. The +% \word{type} specifies what kind of construction the subtree +% corresponds to. The \word{interval} is a list with the structure +% \begin{quote} +% \word{first} \word{last} +% \end{quote} +% where \word{first} and \word{last} are integers; the indices into +% the input string of the first and last respectively character that +% corresponds to this part of the parser tree. The \word{text} is, +% depending on the \word{type}, either empty or the explicit string +% that corresponds to this subtree. +% +% The \word{type}s are generally two-character strings, where the first +% letter specifies a major category and the second letter an exact type +% within that category, but this system is not enforced by any code. +% \begin{description} +% \item[Null types] +% The \emph{null} types all begin with an |N|. These correspond to +% information which isn't part of the script proper, but still is +% useful to include in the parse result. It is generally possible +% to add or remove subtrees of this type without changing the +% meaning of the reconstruction of the script. +% \begin{enumerate} +% \item[\texttt{Nc}] \describestring[subtree type]{Nc} +% This type is for comments, and it normally appears only in +% places where one would expect a command subtree. The +% \word{interval} includes the |#| beginning the comment, but not +% the newline ending it. The \word{text} is the text in the +% comment (initial |#| not included). +% \item[\texttt{Ne}] \describestring[subtree type]{Ne} +% This type is for syntax errors; rather than stopping at every +% error, the parser routines will try to recover as best they can, +% and an error item will be inserted in the parser tree. This +% type of item generally appears as a \word{subtree} of whatever +% was being parsed when the error was detected. The \word{text} is +% a human-readable error message. The \word{interval} specifies +% the erroneous characters; if the error is that some character(s) +% is missing then the end of the interval is the index of the +% character before that which is missing, and the start of the +% interval is the character after that which is missing. +% \item[\texttt{Np}] \describestring[subtree type]{Np} +% This type is merely a placeholder; both the \word{interval} and +% the \word{text} are empty strings. It is used when there was +% nothing to return, but call syntax requires that a subtree is +% returned. If some call returns an item of this type then it +% usually means that its caller also has reached the end of +% whatever that was parsing. +% \end{enumerate} +% +% \item[Literate types] +% The literate, or verbatim, types all begin with an |L|. Words (of +% a command) where no variable or command substitution will happen +% usually belong in this category. There are three types of +% literates: \describestring[subtree type]{Lb}|Lb|s are delimited by +% braces and \describestring[subtree type]{Lq}|Lq|s are delimited by +% quotes, whereas \describestring[subtree type]{Lr}|Lr|s have no +% particular delimiters. In each case, the \word{text} is the string +% that this interval of the input gets parsed into. +% +% Backslash--newline substitution is silently performed, but other +% types of substitution will result in that the item is considered +% to consist of one or more parts, each of which will be +% represented as a separate \word{subtree}. The delimiting quotes +% of an |Lq| is never included in the \word{interval} of any +% \word{subtree}. An |Lb| never has any \word{subtree}s due to +% substitution, since it can only be subject to backslash--newline +% substitution, but it may have |Ne| subtrees. +% +% \item[Merge types] +% The merge types all begin with an |M|. They are similar to the +% literate types, but are used for intervals of the input for which +% the \emph{text} cannot be completely determined. +% \describestring[subtree type]{Mq}|Mq|s are delimited by quotes, +% whereas \describestring[subtree type]{Mr}|Mr|s have no particular +% delimiters. The \word{text} is set to an empty string. There is +% always at least one \word{subtree}. +% +% \item[Substitution types] +% The substitution types all begin with an |S|. As the name +% indicates, they are used for intervals of the input that will be +% substituted by a \Tcllogo\ parser. +% \begin{enumerate} +% \item[\texttt{Sb}] \describestring[subtree type]{Sb} +% This type is for backslash substitution. The \word{text} is +% the character produced by this substitution. There is always +% an |Lr| subtree for the text following the initial backslash. +% \item[\texttt{Sv}] \describestring[subtree type]{Sv} +% This type is for scalar variable substitution. The +% \word{text} is empty, and there is one \word{subtree} for the +% name of the variable being substituted. The type of this +% subtree is either |Lr| or |Lb|, depending on whether the name +% is surrounded by braces or not. +% \item[\texttt{Sa}] \describestring[subtree type]{Sa} +% This type is for array variable substitution. The +% \word{text} is empty, and there are two \word{subtree}s: one +% for the name of the array (always an |Lr|) and one for the +% index into the array (either an |Lr| or an |Mr|). +% \item[\texttt{Sc}] \describestring[subtree type]{Sc} +% This type is for command substitution. The \word{text} is +% empty, and there is one \word{subtree} for each command in +% the script. +% \end{enumerate} +% +% \item[The command type] +% The \describestring[subtree type]{Cd}|Cd| type is for complete +% commands. The \word{text} is empty, and each word in the command +% has its own \word{subtree} (naturally, these are in the order of +% the words in the command). +% +% \item[Root types] +% The root types are used for the root in a parser tree, but they +% may also appear as proper subtrees if the parser is invoked +% recursively. This is for example what one does with the body +% argument of the |while| command: technically it is a string just +% like any other string, but it makes most sense to parse that +% string as a script and replace the original literate item by the +% entire tree generated by parsing this string. +% +% The root types all begin with an |R|. They are +% \begin{enumerate} +% \item[\texttt{Rs}] \describestring[subtree type]{Rs} +% This type is for script parsing. The \word{text} is usually +% empty, and each \word{subtree} is a command of the script. +% \item[\texttt{Rx}] \describestring[subtree type]{Rx} +% This type is for |expr| expression parsing. +% \end{enumerate} +% +% \end{description} +% +% \subsection{Some examples} +% +% The script +% \begin{quote} +% |set a "b\nc"| +% \end{quote} +% will be parsed as the list +%\begin{verbatim} +% Rs {0 11} {} \ +% {Cd {0 11} {} +% {Lr {0 2} set} +% {Lr {4 4} a} +% {Lq {6 11} b\nc +% {Lr {7 7} b} +% {Sb {8 9} \n {Lr {9 9} n}} +% {Lr {10 10} c} +% } +% } +%\end{verbatim} +% NB: This is not the canonical string representation, but one that was +% chosen for clarity of exposition. The |parsetcl::format_tree| +% procedure can be used to reformat a parser tree with indentation as +% shown above. +% +% +% \section{Basic script parsing} +% +% Basic script parsing makes a purely syntactic parsing of a script. It +% does not make any assumptions about the meaning of the commands used +% in the script, and hence it will not try to parse as scripts any +% argument of a commands (not even the byte-compiled commands). +% +% The parsing relies on a set of procedures which parses the next item +% of a particular sort, and returns the corresponding parse subtree. +% These procedures can call each other recursively in complicated ways, +% following the actual structure of the script that is being parsed. +% The general syntax for calling such a procedure is +% \begin{quote} +% \word{proc name} \word{string} \word{index-var-name} +% \word{extra}\regstar +% \end{quote} +% where \word{string} is the string from which something should be +% parsed and \word{index-var-name} is the name of a variable in the +% local context of the caller which holds the index of the first +% character that hasn't been parsed yet (or possibly the last character +% that has been parsed, it may depend on what is most convenient for +% that particular procedure). There are two reasons for using this +% set-up. One is that the calling procedure usually needs to continue +% parsing the string after the part that it made a recursive call to +% have parsed, and for that purpose this variable provides a convenient +% method of returning the value. The other reason is one of efficiency: +% If the script as a whole is passed as an argument to all procedures +% that are involved in the parsing process, as opposed to passing only +% the part that hasn't been parsed yet, then the need to copy the data +% decreases drastically. +% +% +% \subsection{Commands and scripts} +% +% \begin{proc}{flush_whitespace} +% The |flush_whitespace| procedure advances the index past a +% whitespace sequence. The syntax is +% \begin{quote} +% |parsetcl::flush_whitespace| \word{script} \word{index-var-name} +% \word{cmdsep} +% \end{quote} +% where \word{cmdsep} is |1| if command separators (newlines and +% semicolons) should be flushed as well, but |0| if they should be +% treated separately. The return value is the number of characters +% that were flushed. +% \begin{tcl} +proc parsetcl::flush_whitespace {script index_var cmdsep} { + upvar 1 $index_var index + if {[ + if {$cmdsep} then { + regexp -start $index -- {\A([ \t-\r;]|\\\n)+} $script match + } else { + regexp -start $index -- {\A([ \t\v\f\r]|\\\n)+} $script match + } + ]} then { + incr index [string length $match] + return [string length $match] + } else { + return 0 + } +} +% \end{tcl} +% \end{proc} +% +% \begin{proc}{parse_command} +% The |parse_command| procedure parses the next command, returning a +% |Cd|, |Nc|, or |Np| item to its caller. The syntax is +% \begin{quote} +% |parsetcl::parse_command| \word{script} \word{index-var-name} +% \word{nested} +% \end{quote} +% The \word{nested} argument is |1| if the context is that of command +% substitution (in which case |]| acts as command terminator), and |0| +% otherwise. Upon return, the index variable generally points to the +% character that terminates the command. +% +% The procedure skips past any whitespace or command separators in front +% of the actual command. +% \begin{tcl} +proc parsetcl::parse_command {script index_var nested} { + upvar 1 $index_var index +% \end{tcl} +% The first step is to scan past the whitespace in front of the +% command. +% \begin{tcl} + flush_whitespace $script index 1 +% \end{tcl} +% Then there are two ``not a command'' cases to take care of: there +% might be a comment or the command sequence might have ended. In the +% former case, the extent of the comment is determined and a +% corresponding |Nc| item is returned. Backslash--newline +% substitution adds a bit complexity to the problem of finding the end +% of the comment, since an escaped newline may be part of the comment, +% although a backslash is only escaping if it isn't itself escaped. +% \begin{tcl} + switch -- "[string index $script $index]$nested" {#0} - {#1} { + regexp -start $index -indices -- {\A#([^\n\\]|\\.)*(\\$)?}\ + $script interval + incr index + regsub -all -- {\\\n[ \t]*}\ + [string range $script $index [lindex $interval 1]]\ + { } text + set index [expr {[lindex $interval 1] + 1}] + return [list Nc $interval $text] + } 0 - 1 - \]1 { +% \end{tcl} +% In the latter case, an |Np| item is returned. It happens if +% |$index| is past the last character in the string, but also if +% |$index| is at a right bracket and the command is \word{nested}. +% \begin{tcl} + return [list Np "" ""] + } +% \end{tcl} +% But if we get this far, then there is a command to parse. One only +% has to parse all the words. +% \begin{tcl} + set res [list Cd [list $index ""] ""] + set next [parse_word $script index $nested] + while {[lindex $next 0] ne "Np"} { + lappend res $next + set next [parse_word $script index $nested] + } + lset res 1 1 [lindex $res end 1 1] + return $res +} +% \end{tcl} +% \end{proc} +% +% \begin{proc}{basic_parse_script} +% The |basic_parse_script| procedure parses a script (that is to say, a +% sequence of commands) and returns the corresponding |Rs| item. +% The syntax is +% \begin{quote} +% |parsetcl::basic_parse_script| \word{script} +% \end{quote} +% \begin{tcl} +proc parsetcl::basic_parse_script {script} { + set index 0 + set res [list Rs [list $index ""] ""] + while {[lindex [set next [parse_command $script index 0]] 0] ne "Np"} { + lappend res $next + } + incr index -1 + lset res 1 1 $index + return $res +} +% \end{tcl} +% \end{proc} +% +% +% \subsection{Parsing words} +% +% \begin{proc}{parse_word} +% The |parse_word| procedure parses the next command word, returning +% an |Lb|, |Lq|, |Lr|, |Mq|, |Mr|, or |Np| item to its caller. The +% syntax is +% \begin{quote} +% |parsetcl::parse_word| \word{script} \word{index-var-name} +% \word{nested} +% \end{quote} +% The \word{nested} argument is |1| if the context is that of command +% substitution (in which case |]| acts as command terminator), and |0| +% otherwise. It is assumed that the index variable points to the first +% character of the word when this procedure is called. Upon return, +% the index variable points to the first none-whitespace character +% (or command separator) after the word that was parsed. +% +% Most of the actual work is done by the helper procedures +% |parse_raw_word|, |parse_quoted_word|, and |parse_braced_word|. +% \begin{tcl} +proc parsetcl::parse_word {script index_var nested} { + upvar 1 $index_var index + switch -- [string index $script $index] \{ { + parse_braced_word $script index $nested + } \" { + parse_quoted_word $script index $nested + } "" - \; - \n { + list Np "" "" + } \] { + if {$nested} then { + list Np "" "" + } else { + parse_raw_word $script index $nested + } + } default { + parse_raw_word $script index $nested + } +} +% \end{tcl} +% \end{proc} +% +% \begin{proc}{parse_braced_word} +% The |parse_braced_word| procedure parses a brace-delimited word and +% returns the corresponding |Lb| item to its caller. The syntax is +% \begin{quote} +% |parsetcl::parse_braced_word| \word{script} \word{index-var-name} +% \word{nested} +% \end{quote} +% where the arguments and call conventions are as for |parse_word|. +% In case the braced word is followed by some non-whitespace +% characters, then this will be interpreted as though a space was +% missing after the brace. A corresponding |Ne| subtree will be +% attached to the |Lb| item returned, and the index variable will be +% pointed to that none-whitespace character after the brace. +% \begin{tcl} +proc parsetcl::parse_braced_word {script index_var nested} { + upvar 1 $index_var index + set res [list Lb [list $index ""]] + set depth 1 + set text "" + incr index + while {$depth>0} { +% \end{tcl} +% Each iteration of this loop takes care of one non-trivial character +% combination and whatever piece of trivial material that preceeds +% it. Braces which are not escaped obviously constitute non-trivial +% material (because they change the nesting depth), but escaped +% newlines are also non-trivial (because they are subject to +% substitution). +% \begin{tcl} + regexp -start $index -- {\A([^{}\\]|\\[^\n])*} $script match + append text $match + incr index [string length $match] + switch -- [string index $script $index] \{ { + incr depth + incr index + } \} { + incr depth -1 + incr index + } \\ { + if {[regexp -start $index -- {\A\\\n[ \t]*} $script match]}\ + then { + incr index [string length $match] + append text { } + } else { +% \end{tcl} +% In this (rather curious) case, the \word{script} ended with an +% escaping backslash, but there is nothing to escape. This is +% interpreted as just the backslash. However, there will be an error +% since the braces apparently haven't been properly balanced. +% \begin{tcl} + append text \\ + break + } + } "" { + break + } + } + if {$depth>0} then { + lset res 1 1 $index + lappend res $text [list Ne [list "" $index] {missing close-brace}] + lset res 3 1 0 [incr index] + return $res + } + lset res 1 1 [expr {$index - 1}] + lappend res $text +% \end{tcl} +% What remains now is to check that there aren't any stray characters +% following the close-brace. If there is whitespace to flush then +% everything is alright. Things are also alright if the next character +% is a command terminator or we're at the end of the string. +% \begin{tcl} + if {[flush_whitespace $script index 0]} then {return $res} + switch -- [string index $script $index] \n - \; - {} { + return $res + } \] { + if {$nested} then {return $res} + } +% \end{tcl} +% But if that is not the case then there is an error, and an |Ne| item +% should be appended to |res|. The exact appearence of this item does +% however depend on how the parser attempts to recover from the error, +% and that is not immediately obvious. The \Tcllogo\ parser makes no +% attempt to recover---and can therefore report the error as ``extra +% characters after close-brace''---but the best way to recover rather +% seems to be to assume that a space is missing. That is why the +% following error message is non-standard. +% \begin{tcl} + lappend res [list Ne [list $index [expr {$index - 1}]]\ + {missing space after close-brace}] + return $res +} +% \end{tcl} +% \end{proc} +% +% \begin{proc}{parse_quoted_word} +% The |parse_quoted_word| procedure parses a quote-delimited word and +% returns the corresponding |Lq| or |Mq| item to its caller. The +% syntax is +% \begin{quote} +% |parsetcl::parse_quoted_word| \word{script} \word{index-var-name} +% \word{nested} +% \end{quote} +% where the arguments and call conventions are as for |parse_word|. +% In case the quoted word is followed by some non-whitespace +% characters, then this will be interpreted as though a space was +% missing after the last quote. A corresponding |Ne| subtree will +% be be put last in the |Lq| or |Mq| item returned, and the index +% variable will be pointed to that none-whitespace character after +% the quote. +% \begin{tcl} +proc parsetcl::parse_quoted_word {script index_var nested} { + upvar 1 $index_var index + set res [list Lq [list $index ""] ""] + set text "" + incr index + while {1} { +% \end{tcl} +% Each iteration of this loop adds one subtree to |res| and the +% corresponding literate material to |text|. If some substitution +% happens which does not produce literate material then the |Lq| +% in |res| is changed to an |Mq|. +% \begin{tcl} + switch -- [string index $script $index] \\ { + lappend res [parse_backslash $script index] + append text [lindex $res end 2] + } \$ { + lappend res [parse_dollar $script index] + lset res 0 Mq + } \[ { + lappend res [parse_bracket $script index] + lset res 0 Mq + } \" { + incr index + break + } "" { + lappend res [list Ne [list $index [expr {$index - 1}]]\ + {missing close-quote}] + break + } default { + regexp -start $index -- {[^\\$\["]*} $script match + set t $index + incr index [string length $match] + lappend res [list Lr [list $t [expr {$index - 1}]] $match] + append text $match + } + } + lset res 1 1 [expr {$index - 1}] + if {[lindex $res 0] eq "Lq"} then { + lset res 2 $text +% \end{tcl} +% An |Lq| item that has precisely one subtree that furthermore is of +% type |Lr| can do without that subtree, since all the interesting +% information is in the |text|. +% \begin{tcl} + if {[llength $res] == 4 && [lindex $res 3 0] eq "Lr"} then { + set res [lrange $res 0 2] + } + } +% \end{tcl} +% What remains now is to check that there aren't any stray characters +% following the close-quote. If there is whitespace to flush then +% everything is alright. Things are also alright if the next character +% is a command terminator or we're at the end of the string. +% \begin{tcl} + if {[flush_whitespace $script index 0]} then {return $res} + switch -- [string index $script $index] \n - \; - {} { + return $res + } \] { + if {$nested} then {return $res} + } +% \end{tcl} +% But if that is not the case then there is an error, and an |Ne| item +% should be appended to |res|. As with characters after close-quotes, +% it is assumed that a space is missing. +% \begin{tcl} + lappend res [list Ne [list $index [expr {$index - 1}]]\ + {missing space after close-quote}] + return $res +} +% \end{tcl} +% \end{proc} +% +% \begin{proc}{parse_raw_word} +% The |parse_raw_word| procedure parses a whitespace-delimited word +% of a command and returns the corresponding |Lr| or |Mr| item to its +% caller. The syntax is +% \begin{quote} +% |parsetcl::parse_raw_word| \word{script} \word{index-var-name} +% \word{nested} +% \end{quote} +% where the arguments and call conventions are as for |parse_word|. +% \begin{tcl} +proc parsetcl::parse_raw_word {script index_var nested} { + upvar 1 $index_var index + set res [list] + set type Lr + set interval [list $index] + set text "" + while {1} { +% \end{tcl} +% Each iteration of this loop adds one subtree to |res| and the +% corresponding literate material to |text|. If some substitution +% happens which does not produce literate material then |type| is set +% to an |Mr|. +% \begin{tcl} + switch -- [string index $script $index] \\ { + if {[string index $script [expr {$index+1}]] eq "\n"} then { + break + } + lappend res [parse_backslash $script index] + append text [lindex $res end 2] + continue + } \$ { + lappend res [parse_dollar $script index] + set type Mr + continue + } \[ { + lappend res [parse_bracket $script index] + set type Mr + continue + } \t - \n - \v - \f - \r - " " - \; - "" { + break + } + if {$nested} then { + if {![ + regexp -start $index -- {\A[^\\$\[\]\t-\r ;]+} $script match + ]} then {break} + } else { + regexp -start $index -- {\A[^\\$\[\t-\r ;]+} $script match + } + set t $index + incr index [string length $match] + lappend res [list Lr [list $t [expr {$index - 1}]] $match] + append text $match + } +% \end{tcl} +% In case there is only a single element in |res| then that will be +% the result. +% \begin{tcl} + if {[llength $res]==1} then { + set res [lindex $res 0] + } else { + lappend interval [expr {$index - 1}] + if {$type ne "Lr"} then {set text ""} + set res [linsert $res 0 $type $interval $text] + } + flush_whitespace $script index 0 + return $res +} +% \end{tcl} +% \end{proc} +% +% +% \subsection{Parsing substitutions} +% +% \begin{proc}{parse_backslash} +% The |parse_backslash| procedure parses a backslash sequence and +% returns the corresponding |Sb| item. The syntax is +% \begin{quote} +% |parsetcl::parse_backslash| \word{script} \word{index-var-name} +% \end{quote} +% and it is assumed that the index character in \word{script} is the +% initial backslash in the sequence. Upon return, the index character +% is the first character after the backslash sequence. +% \begin{tcl} +proc parsetcl::parse_backslash {script index_var} { + upvar 1 $index_var index + set start $index + incr index + set ch [string index $script $index] + set res [list Lr [list $index $index] $ch] + switch -- $ch a { + set res [list Sb [list $start $index] \a $res] + } b { + set res [list Sb [list $start $index] \b $res] + } f { + set res [list Sb [list $start $index] \f $res] + } n { + set res [list Sb [list $start $index] \n $res] + } r { + set res [list Sb [list $start $index] \r $res] + } t { + set res [list Sb [list $start $index] \t $res] + } v { + set res [list Sb [list $start $index] \v $res] + } x { + if {[regexp -start [expr {$index + 1}] -- {\A[0-9A-Fa-f]+}\ + $script match]} then { + scan [string range $match end-1 end] %x code + incr index [string length $match] + lset res 1 1 $index + lset res 2 "x$match" + set res [list Sb [list $start $index]\ + [format %c $code] $res] + } else { + set res [list Sb [list $start $index] x $res] + } + } u { + if {[regexp -start [expr {$index + 1}] -- {\A[0-9A-Fa-f]{1,4}}\ + $script match]} then { + scan $match %x code + incr index [string length $match] + lset res 1 1 $index + lset res 2 "u$match" + set res [list Sb [list $start $index]\ + [format %c $code] $res] + } else { + set res [list Sb [list $start $index] u $res] + } + } \n { + regexp -start [expr {$index + 1}] -- {\A[ \t]*} $script match + incr index [string length $match] + lset res 1 1 $index + lset res 2 "\n$match" + set res [list Sb [list $start $index] " " $res] + } "" { + return [list Sb [list $start $start] \\] + } default { + if {[regexp -start $index -- {\A[0-7]{1,3}} $script match]} then { + scan $match %o code + incr index [expr {[string length $match]-1}] + lset res 1 1 $index + lset res 2 $match + set res [list Sb [list $start $index] [format %c $code] $res] + } else { + set res [list Sb [list $start $index] $ch $res] + } + + } + incr index + return $res +} +% \end{tcl} +% \end{proc} +% +% \begin{proc}{parse_bracket} +% The |parse_bracket| procedure parses one pair of command +% substitution brackets and returns the corresponding |Sc| item. +% The syntax is +% \begin{quote} +% |parsetcl::parse_bracket| \word{script} \word{index-var-name} +% \end{quote} +% and it is assumed that the index character in \word{script} is the +% initial bracket in the sequence. Upon return, the index character +% is the first character after the close-bracket. +% \begin{tcl} +proc parsetcl::parse_bracket {script index_var} { + upvar 1 $index_var index + set res [list Sc [list $index ""] ""] + incr index + while {[lindex [set next [parse_command $script index 1]] 0] ne "Np"} { + lappend res $next + } + if {[string index $script $index] eq "\]"} then { + lset res 1 1 $index + incr index + return $res + } else { + lappend res [list Ne [list $index [expr {$index-1}]]\ + {missing close-bracket}] + lset res 1 1 [expr {$index-1}] + return $res + } +} +% \end{tcl} +% \end{proc} +% +% \begin{variable}{varname_RE} +% The |varname_RE| is the regular expression used with |$| +% substitution for grabbing the name of the variable. +% \begin{tcl} +set parsetcl::varname_RE {\A(\w|::)+} +% \end{tcl} +% The reason for factoring this out into a variable is that +% \Tcllogo\ isn't using the natural (Unicode) definition of an +% alphanumeric character here, but relies on a locale-dependent +% C~library function to perform this test. They generally agree on +% ASCII characters, but outside that one probably cannot rely on that +% the test produces sensible results. Hence if a script needs to be +% parsed where some non-Unicode behaviour of this library function is +% significant, then you might need to change the above regexp. +% +% There is a \Tcllogo\ bug report~\cite{Letter-bug} on the matter, +% and hopefully there will be no need to ever change this regexp +% once that has been resolved. +% \end{variable} +% +% \begin{proc}{parse_dollar} +% The |parse_dollar| procedure parses one |$|-sequence and returns +% the corresponding |Sv|, |Sa|, or |Lr| item. The syntax is +% \begin{quote} +% |parsetcl::parse_dollar| \word{script} \word{index-var-name} +% \end{quote} +% and it is assumed that the index character in \word{script} is the +% initial |$| in the sequence. Upon return, the index character +% is the first character after the parsed sequence. +% \begin{tcl} +proc parsetcl::parse_dollar {script index_var} { + upvar 1 $index_var index + set res [list "" [list $index ""] ""] + incr index +% \end{tcl} +% If the first character after the |$| is a left brace, then the +% variable is scalar and its name is terminated by the next right +% brace. Note that braces do not nest in this case. +% \begin{tcl} + if {[string index $script $index] eq "\{"} then { + lset res 0 Sv + set end [string first \} $script $index] + if {$end<0} then { + set end [expr {[string length $script] - 1}] + lappend res [list Lb [list $index $end]\ + [string range $script [expr {$index + 1}] end]]\ + [list Ne [list [expr {$end+1}] $end]\ + {missing close-brace for variable name}] + } else { + lappend res [list Lb [list $index $end]\ + [string range $script [expr {$index + 1}] [expr {$end-1}]]] + } + lset res 1 1 $end + set index [expr {$end + 1}] + return $res + } +% \end{tcl} +% Otherwise see if there is something which can be interpreted as an +% alphanumeric variable name. First treat the case when there isn't; +% in that case the |$| is just a literate |$| and it is returned as an +% |Lr| iterm. Then treat the case that the variable is scalar. +% \begin{tcl} + variable varname_RE + if {![regexp -start $index -- $varname_RE $script match]} then { + if {[string index $script $index] eq "("} then { + set match "" + } else { + return [list Lr [list [lindex $res 1 0] [lindex $res 1 0]] \$] + } + } + set t $index + incr index [string length $match] + lappend res [list Lr [list $t [expr {$index-1}]] $match] + if {[string index $script $index] ne "("} then { + lset res 0 Sv + lset res 1 1 [lindex $res 3 1 1] + return $res + } +% \end{tcl} +% What remains is to treat the case of an array variable. This is +% very much like |parse_quoted_word|, but it is the right parenthesis +% rather than |"| that acts as terminator. +% \begin{tcl} + lset res 0 Sa + incr index + set subres [list Lr [list $index ""] ""] + lappend res "" + set text "" + while {1} { +% \end{tcl} +% Each iteration of this loop adds one subtree to |subres| and the +% corresponding literate material to |text|. If some substitution +% happens which does not produce literate material then the |Lr| +% in |subres| is changed to an |Mr|. +% \begin{tcl} + switch -- [string index $script $index] \\ { + lappend subres [parse_backslash $script index] + append text [lindex $subres end 2] + } \$ { + lappend subres [parse_dollar $script index] + lset subres 0 Mr + } \[ { + lappend subres [parse_bracket $script index] + lset subres 0 Mr + } ) { + lset subres 1 1 [expr {$index - 1}] + break + } "" { + lappend res\ + [list Ne [list $index [incr index -1]] {missing )}] + lset subres 1 1 $index + break + } default { + regexp -start $index -- {[^\\$\[)]*} $script match + set t $index + incr index [string length $match] + lappend subres [list Lr [list $t [expr {$index - 1}]] $match] + append text $match + } + } + if {[lindex $subres 0] eq "Lr"} then {lset subres 2 $text} + if {[llength $subres] == 4} then {set subres [lindex $subres 3]} + lset res 1 1 $index + incr index + lset res 4 $subres + return $res +} +% \end{tcl} +% \end{proc} +% +% +% +% \section{Some utility procdures} +% +% \subsection{Viewing parser trees} +% +% It is useful to have a surveyable presentation of parser trees. An +% easy step towards this would be to ensure that nesting depth is +% mirrored in the indentation. +% +% \begin{proc}{format_tree} +% The |format_tree| procedure takes a parser tree, a base indentation +% (string of whitespace), and an indentation step (string of whitespace) +% as argument. It returns a string that is list-wise equivalent to +% |[list]| of the original tree, but has indentation mirroring the +% nesting depth. +% \begin{tcl} +proc parsetcl::format_tree {tree base step} { + set res $base + append res \{ [lrange $tree 0 1] { } +% \end{tcl} +% The following is a trick to make a list element string +% representation for the \word{text} that does not contain any sort +% of newline: making the element unbalanced with respect to braces +% forces escape-quoting also for newlines. +% \begin{tcl} + if {[regexp {[\n\r]} [lindex $tree 2]]} then { + append res [string range [list "[lindex $tree 2]\{"] 0 end-2] + } else { + append res [lrange $tree 2 2] + } + if {[llength $tree]<=3} then { + append res \} + return $res + } elseif {[llength $tree] == 4 &&\ + [string match {S[bv]} [lindex $tree 0]]} then { +% \end{tcl} +% This is a slight optimization: |Sb| and |Sv| trees look better on +% one line than on three. +% \begin{tcl} + append res " " [format_tree [lindex $tree 3] "" ""] \} + return $res + } + append res \n + foreach subtree [lrange $tree 3 end] { + append res [format_tree $subtree $base$step $step] \n + } + append res $base \} +} +% \end{tcl} +% \end{proc} +% +% +% \subsection{Offsetting intervals} +% +% \begin{proc}{offset_intervals} +% This procedure modifies a \word{tree} by adding \word{offset} to +% all endpoints of each interval in that tree, and returns the +% modified tree. The syntax is +% \begin{quote} +% |parsetcl::offset_intervals| \word{tree} \word{offset} +% \end{quote} +% \begin{tcl} +proc parsetcl::offset_intervals {tree offset} { + set res [lrange $tree 0 2] + foreach i {0 1} { + lset res 1 $i [expr {[lindex $res 1 $i] + $offset}] + } + foreach subtree [lrange $tree 3 end] { + lappend res [offset_intervals $subtree $offset] + } + return $res +} +% \end{tcl} +% \end{proc} +% +% \begin{proc}{reparse_Lb_as_script} +% The |reparse_Lb_as_script| procedure replaces an |Lb| node from an +% existing parser tree with the |Rs| node produced by parsing the text +% between the braces as a script. The syntax is +% \begin{quote} +% |parsetcl::reparse_Lb_as_script| \word{tree-var} \word{node-index} +% \word{parsed string} +% \end{quote} +% where \word{tree-var} is the name in the caller's local context of +% the variable in which the tree to substitute is stored and +% \word{node-index} is the index list of the node to replace. The +% procedure returns |2| if the node was an |Lb| node, |1| if is was +% an |Lr| or |Lq| node, and |0| if the node was not of any of these +% types. In the last case, the tree is not modified. In the case that +% |1| is returned, the node has been substituted, but the intervals +% may be slightly off since the string to parse was taken from the +% \word{text} part of the substituted node. The normal case is that |2| +% is returned, and in this case the intervals unambiguously refer to +% positions in the \word{parsed string}. +% \begin{tcl} +proc parsetcl::reparse_Lb_as_script {tree_var index parsed} { + upvar 1 $tree_var tree + set node [lindex $tree $index] + switch -- [lindex $node 0] Lb - Lr - Lq { + set base [expr {[lindex $node 1 0] + 1}] + if {[lindex $node 0] eq "Lb"} then { + set script [string range $parsed $base\ + [expr {[lindex $node 1 1] - 1}]] + } else { + set script [lindex $node 2] + } + lset tree $index\ + [offset_intervals [basic_parse_script $script] $base] + if {[lindex $node 0] eq "Lb"} then { + return 2 + } else { + return 1 + } + } default { + return 0 + } +} +% \end{tcl} +% \end{proc} + + +% +% +% \subsection{Traversing parser trees} +% +% \begin{proc}{walk_tree} +% The |walk_tree| procedure has the syntax +% \begin{quote} +% |parsetcl::walk_tree| \word{tree-var} \word{index-var} +% \begin{regblock}[\regplus]\word{type-pattern} +% \word{body}\end{regblock} +% \end{quote} +% It walks through each node in the tree stored in the +% \word{tree-var} in the order that they appear in the string, i.e., +% first the root of a tree, then it walks through each subtree in +% sequence. When visiting a node, the type of the node is +% regexp-matched against the \word{type-pattern}s, and the first +% matching \word{body} is evaluated (entirely using |switch -regexp|) +% in the local context of the caller. While walking, the procedure is +% updating the \word{index-var} variable so that it always contains +% the index list specifying the current node in the tree. +% +% It is OK to modify the tree while walking through it, provided that +% the index list of the current node remains valid throughout. It is +% particular possible to change the contents of the current node (add +% or remove children) without messing things up. +% +% There is no particular return value from this procedure. +% \begin{tcl} +proc parsetcl::walk_tree {tree_var index_var args} { + upvar 1 $tree_var tree $index_var idxL + set idxL [list] + set i 0 + while {$i>=0} { + if {$i==0} then { + uplevel 1 [list switch -regexp --\ + [lindex [lindex $tree $idxL] 0] $args] + set i 3 + } elseif {$i < [llength [lindex $tree $idxL]]} then { + lappend idxL $i + set i 0 + } elseif {[llength $idxL]} then { + set i [lindex $idxL end] + set idxL [lrange $idxL 0 end-1] + incr i + } else { + set i -1 + } + } +} +% \end{tcl} +% \end{proc} +% +% \begin{proc}{simple_parse_script} +% The |simple_parse_script| procedure is similar to +% |basic_parse_script|, but it tries to recognise some common control +% structures (|if|, |for|, etc.) and reparses those arguments that +% (for standard command definitions) are scripts. The syntax is +% \begin{quote} +% |parsetcl::simple_parse_script| \word{script} +% \end{quote} +% and it returns the parsed script. +% \begin{tcl} +proc parsetcl::simple_parse_script {script} { + set tree [parsetcl::basic_parse_script $script] + walk_tree tree indices Cd { + switch -- [lindex [lindex $tree $indices] 3 2] if { + for {set i 3} {$i < [llength [lindex $tree $indices]]}\ + {incr i} { + switch -- [lindex [lindex $tree $indices] $i 2]\ + if - elseif { + incr i; continue + } then - else { + incr i + } + parsetcl::reparse_Lb_as_script tree\ + [linsert $indices end $i] $script + } + } while { + parsetcl::reparse_Lb_as_script tree [linsert $indices end 5]\ + $script + } for { + parsetcl::reparse_Lb_as_script tree [linsert $indices end 4]\ + $script + parsetcl::reparse_Lb_as_script tree [linsert $indices end 6]\ + $script + parsetcl::reparse_Lb_as_script tree [linsert $indices end 7]\ + $script + } foreach { + parsetcl::reparse_Lb_as_script tree [linsert $indices end end]\ + $script + } catch { + parsetcl::reparse_Lb_as_script tree [linsert $indices end 4]\ + $script + } proc { + parsetcl::reparse_Lb_as_script tree [linsert $indices end 6]\ + $script + } + } + return $tree +} +% \end{tcl} +% \end{proc} +% +% \section{Parsing expressions} +% +% This hasn't been implemented yet. +% +% +% \section{Advanced script parsing} +% +% Most of the meaning of a \Tcllogo\ script depends on its commands +% rather than being given by the syntax---there is e.g. nothing in the +% general syntax that makes the last argument of a |foreach| command +% more likely to contain a script than any of the other arguments---and +% therefore any higher level parsing of \Tcllogo\ scripts must employ a +% table of commands +% +% This hasn't been implemented yet. +% +% +% \section{Script reconstruction} +% +% This hasn't been implemented yet, but the parser trees contain +% the necessary information. Applications of script reconstruction +% includes writing a |proc|-like command that inlines code in the body +% argument before the procedure is created. +% +% +% \begin{thebibliography}{9} +% \bibitem{Letter-bug} +% Kevin B. Kenny and Jeffrey Hobbs: +% \textit{Dollar-substitution and non-Latin-1}, +% \Tcllogo~project bug \#408568 (2001, still open April~2003); +% \href{https://sourceforge.net/tracker/^^A +% ?func=detail&aid=408568&group_id=10894&atid=110894}^^A +% {\textsc{https}:/\slash \texttt{sourceforge.net}\slash +% \texttt{tracker}\slash +% \texttt{?func=detail\&}\penalty\exhyphenpenalty +% \texttt{aid=408568\&}\penalty\exhyphenpenalty +% \texttt{group\_id=10894\&}\penalty\exhyphenpenalty +% \texttt{atid=110894}.} +% +% \end{thebibliography} +% +\endinput \ No newline at end of file diff --git a/macros/latex/contrib/tclldoc/examples/parsetcl.ins b/macros/latex/contrib/tclldoc/examples/parsetcl.ins new file mode 100644 index 0000000000..645dc77f8a --- /dev/null +++ b/macros/latex/contrib/tclldoc/examples/parsetcl.ins @@ -0,0 +1,54 @@ +% parsetcl.ins --- DOCSTRIP installation script for parsetcl. +\input docstrip + +% Redefine the \MetaPrefix; it should be something which starts a +% until-end-of-line comment: +\edef\MetaPrefix{\string#\string#} + + +% Redefine the file preamble and postamble; this is necessary because +% otherwise the old \metaPrefix is inserted at the beginning of these +% lines. +\preamble + +In other words: + *************************************** + * This Source is not the True Source. * + *************************************** +The True Source is parsetcl.dtx in + http://ftp.ctan.org/tex-archive/macros/latex/contrib/tclldoc/examples + +It is preferred that you apply the distribution and modification +conditions of the LaTeX Project Public License (LPPL) for this file, +but you may alternatively choose to apply BSD/Tcl-style license +conditions (either is OK). The latest version of the LPPL is in + http://www.latex-project.org/lppl.txt +and version 1.2 or later is part of all distributions of LaTeX +version 1999/12/01 or later. + +\endpreamble + +\postamble +\endpostamble + + +% Actually make the files: +\generate{ + \file{parsetcl.tcl}{\from{parsetcl.dtx}{pkg}} +} + + +\Msg{} +\Msg{************************************************} +\Msg{*} +\Msg{* To complete installation,} +\Msg{* place parsetcl.tcl in a Tcl library directory} +\Msg{* and run pkg_mkIndex to rebuild the package} +\Msg{* index.} +\Msg{*} +\Msg{************************************************} +\Msg{} + + +\end + diff --git a/macros/latex/contrib/tclldoc/examples/pdf.dtx b/macros/latex/contrib/tclldoc/examples/pdf.dtx new file mode 100644 index 0000000000..249a28ee6c --- /dev/null +++ b/macros/latex/contrib/tclldoc/examples/pdf.dtx @@ -0,0 +1,796 @@ +% +% \iffalse +%<*driver> +\documentclass{tclldoc} +\PageIndex\CodelineNumbered +\IndexPrologue{% + \section*{Index}% + All numbers in this index are page numbers. + Underlined entries refer to places where the item in + question is defined.% +} +\setcounter{IndexColumns}{2} +\begin{document} +\DocInput{pdf.dtx} +\end{document} +% +% \fi +% +% \title{A simple PDF writer in \Tcllogo} +% \author{Lars Hellstr\"om} +% \maketitle +% +% \begin{abstract} +% This file contains some basic routines that allow a \Tcllogo\ +% script to write PDF files. +% \end{abstract} +% +% \tableofcontents +% +% \section{PDF files and objects} +% +% A Portable Document Format (PDF) file is, when compared with for example +% a PostScript file or HTML file, a rather disorganised document. This +% is because at the basic level, a PDF file is a heap rather than a +% text; it \emph{can} be ``disorganised'' since its logical structure +% is based on cross-referencing rather than on sequentiality. The first +% step is therefore to provide support for writing well-formed heaps. +% +% \changes{0.0}{2003/01/02}{Initial version. (LH)} +% +% \begin{tcl} +%<*pkg> +package require Tcl 8.1 +package provide writepdf 0.1 +namespace eval pdf {} +% \end{tcl} +% \setnamespace{pdf} +% +% +% \subsection{Building objects} +% +% The independent units in a PDF file are called objects. An +% \emph{object} is essentially a value (which includes a type). The +% procedures below construct strings of PDF code that encode objects of +% various types. The strings returned are generally such that one must +% insert whitespace between two such strings if the data is to be +% properly encoded. The strings may contain newlines if some building +% routine thinks the lines should otherwise be too long. +% +% \begin{proc}{boolean_obj} +% The |boolean_obj| procedure returns a boolean object, corresponding +% to the string passed as its only argument. Most of it is about +% parsing this string: \texttt{true}, \texttt{yes}, \texttt{on}, and +% nonzero numbers are interpreted as boolean true, whereas +% \texttt{false}, \texttt{no}, \texttt{off}, and the zero number is +% interpreted as boolean false. The matching is case-insensitive. +% \begin{tcl} +proc pdf::boolean_obj {value} { + switch -nocase -- $value { + true - yes - on {return true} + false - no - off {return false} + default { + if {$value} then {return true} else {return false} + } + } +} +% \end{tcl} +% \end{proc} +% +% \begin{proc}{int_obj} +% The |int_obj| procedure returns the PDF object corresponding to +% the integer supplied as argument. +% \begin{tcl} +proc pdf::int_obj {value} {format %d $value} +% \end{tcl} +% \end{proc} +% +% \begin{proc}{real_obj} +% \begin{variable}{precision} +% The |real_obj| procedure returns the PDF object corresponding to +% the real number supplied as argument. The syntax is +% \begin{quote} +% |pdf::real_obj| \word{value} \word{precision}\regopt +% \end{quote} +% where \word{precision} is the number of decimals that will be +% included in the object. If omitted, the value of the |precision| +% variable is used, and that defaults to $3$. +% \begin{tcl} +set pdf::precision 3 +proc pdf::real_obj {value {precision -1}} { + if {$precision<0} then { + unset precision + variable precision + } + format %.[format %d $precision]f $value +} +% \end{tcl} +% \end{variable}\end{proc} +% +% \begin{proc}{string_obj} +% The |string_obj| procedure returns the PDF string object +% corresponding to the argument. Backslashes and parentheses are +% escaped. Control characters are converted to escape sequences. +% Characters with character code above 255 cause an error to be +% thrown (PDF strings correspond more to \Tcllogo\ byte arrays than to +% general strings). If the string produced is longer than 100 +% characters, then backslash--newline sequences are inserted in +% suitable places. +% \begin{tcl} +proc pdf::string_obj {str} { + if {[regexp "\[^\n\r -\xff\]" $str]} then { + set estr [string map [list \\ \\\\ ( \\( ) \\) \r \\r \n \\n] $str] + set str "(" + set len 1 + foreach ch [split $estr {}] { + scan $ch %c code + if {$code==92} then { + append str \\ + incr len + continue + } elseif {$code<32} then { + append str "\\[format %03o $code]" + incr len 4 + } elseif {$code<256} then { + append str $ch + incr len + } else { + error "Bad character $ch [format (U+%04x) $code]\ + in PDF string." + } + if {$len > 100} then { + append str \\\n + set len 0 + } + } + append str ")" + } else { + set L [list] + while {[string length $str]>=100} { + lappend L [string map [list \\ \\\\ ( \\( ) \\) \r \\r \n \\n]\ + [string range $str 0 99]] + set str [string range $str 100 end] + } + if {[string length $str]} then { + lappend L\ + [string map [list \\ \\\\ ( \\( ) \\) \r \\r \n \\n] $str] + } + set str ([join $L \\\n]) + } + return $str +} +% \end{tcl} +% \end{proc} +% +% \begin{proc}{hexstring_obj} +% The |hexstring_obj| procedure returns the PDF string object, +% encoded as hexadecimal digits, that corresponds to the argument. +% If the string is longer than 31 characters then it will be broken +% on several lines. +% \begin{tcl} +proc pdf::hexstring_obj {str} { + set hstr "<" + set len 1 + foreach ch [split $str {}] { + scan $ch %c code + if {$len>=63} then { + append hstr \n + set len 0 + } + if {$code<256} then { + append hstr [format %02x $code] + incr len 2 + } else { + error "Bad character $ch [format (U+%04x) $code]\ + in PDF string." + } + } + append hstr ">" +} +% \end{tcl} +% \end{proc} +% +% \begin{proc}{text_obj} +% The |text_obj| procedure returns the PDF string object, encoded as +% hexadecimal digits, that corresponds to the argument string. This is +% meant to be used for \emph{text strings} +% (see~\cite[Ssec.~4.4.1]{PDFspec}), which are \Tcllogo-style strings +% rather than byte arrays. Text strings may contain general Unicode +% characters, although they will then be encoded using UTF-16BE. +% +% The implementation constructs the 8-bit and 16-bits encodings of the +% string in parallel, and only when it is done will it decide which to +% return. +% \begin{tcl} +proc pdf::text_obj {str} { + set hstr "<" + set ustr "=62} then { + append ustr \n + set lenu 0 + } + append ustr [format %02x%02x $codeh $codel] + incr lenu 4 + if {$codeh>0} then {set eight_ok 0} + if {!$eight_ok} then {continue} + if {$lenh>=63} then { + append hstr \n + set lenh 0 + } + append hstr [format %02x $codel] + incr lenh 2 + } + append hstr ">" + append ustr ">" + if {$eight_ok && ![string match {126} then { + error "String too long to be a PDF name." + } + set res / + foreach ch [split $str {}] { + switch -glob -- $ch { + ( - ) - < - > - \\[ - \\] - \{ - \} - / - % - # { + scan $ch %c code + append res [format #%02x $code] + } + [!-~] {append res $ch} + default { + scan $ch %c code + append res [format #%02x $code] + } + } + } + return $res +} +% \end{tcl} +% \end{proc} +% +% \begin{proc}{array_obj} +% The |array_obj| procedure builds an array object of the objects it +% is given as arguments. The syntax is +% \begin{quote} +% |pdf::array_obj| \word{object}\regstar +% \end{quote} +% Newlines are inserted between the objects if it does not appear as +% if the object would fit on a single (100 character) line. +% \begin{tcl} +proc pdf::array_obj {args} { + set res \[ + set len 1 + foreach item $args { + if {[string length $item] + $len >= 100} then { + append res \n + set len 0 + } elseif {[string length $res]>1} then { + append res " " + incr len + } + append res $item + incr len [string length $item] + } + if {$len >= 100} then { + append res \n + } + append res \] +} +% \end{tcl} +% \end{proc} +% +% \begin{proc}{dict_obj} +% The |dict_obj| procedure builds a dictionary object from its +% arguments. The syntax is +% \begin{quote} +% |pdf::dict_obj| +% \begin{regexp}[\regstar]\word{key} \word{value}\end{regexp} +% \end{quote} +% where each \word{key} must be a name object and each \word{value} +% must be an object. It is checked that the number of elements is +% correct and that the keys begin with a slash. +% \begin{tcl} +proc pdf::dict_obj {args} { + if {[llength $args] % 2 != 0} then { + error "Not the same number of keys and values." + } + set res "<<\n" + foreach {key value} $args { + if {![string match {/*} $key]} then { + error "'$key' isn't a name object." + } + if {[string length $key] + [string length $value]>99} then { + append res $key \n $value \n + } else { + append res $key { } $value \n + } + } + append res ">>" +} +% \end{tcl} +% \end{proc} +% +% \begin{proc}{null_obj} +% The |null_obj| procedure returns a null object. It has no arguments. +% \begin{tcl} +proc pdf::null_obj {} {return null} +% \end{tcl} +% \end{proc} +% +% +% Objects can also be \emph{streams}, but those have a special relation +% to the file structure and are therefore best treated in conjunction +% with that. In particular, streams cannot be used as arguments of +% |array_obj| or |dict_obj|. The arguments of these procedures can +% however be \emph{indirect references} to objects of any type, but +% these too are best treated in the context of the basic PDF file +% structure. +% +% +% \subsection{File structure} +% +% The body of a PDF file consists of a sequence of \emph{indirect +% objects}, which are mainly a sort of declarations: a pair of integers +% are associated with an object value. Since any composite object can +% (and in several cases must) contain a reference to any indirect object, +% this makes it possible to build up arbitrary data structures. It is +% however also a complication, since it requires that there is a +% mechanism for allocating these numbers. +% +% \begin{arrayvar}{file\meta{fnum}} +% Every file that \Tcllogo\ opens gets a unique identifier which is +% used in calls to |puts| and such. This identifier is also used as +% the name of an array in the |pdf| namespace, in which the +% procedures below store all auxiliary information they need to create +% a proper PDF file. +% \end{arrayvar} +% +% \begin{arrayentry}{file\meta{fnum}}{!\meta{reference label}} +% \begin{arrayentry}{file\meta{fnum}}{last_object_num} +% In this API, references to indirect objects can be arbitrary +% strings, called \emph{reference labels}. The correspondence to the +% object numbers actually found in the file is given by the +% \texttt{!}\meta{reference label} entries in the array of the file in +% question. The entries in this array are lists with the structure +% \begin{quote} +% \word{object number} \word{generation number} \word{file +% position}\regopt +% \end{quote} +% where the \word{file position} is present only if the indirect +% object in question has been written to file already. The +% \word{object number} is the number of the object referred to. The +% \word{generation number} is currently always zero; it appears that +% it can only be nonzero for files that have incrementally updated, +% and this API only supports creating a file from scratch. The +% \word{file position} is the position in the file of the beginning +% of the indirect object begin referred to. +% +% The |last_object_num| entry in the array holds the most recently +% allocated object number. It is incremented whenever a new reference +% label is encountered. +% \end{arrayentry}\end{arrayentry} +% +% \begin{proc}{obj_ref} +% The |obj_ref| procedure returns PDF code for an indirect reference +% to an object. The syntax is +% \begin{quote} +% |pdf::obj_ref| \word{file} \word{reference label} +% \end{quote} +% where \word{file} is the indentifier of the PDF file in question. +% If the \word{reference label} has not been encountered before for +% this particular file, then a new object number is allocated for it. +% \begin{tcl} +proc pdf::obj_ref {F label} { + upvar #0 [namespace current]::$F A + if {![info exists A(!$label)]} then { + incr A(last_object_num) + set A(!$label) [list $A(last_object_num) 0] + } + format {%d %d R} [lindex $A(!$label) 0] [lindex $A(!$label) 1] +} +% \end{tcl} +% \end{proc} +% +% \begin{proc}{begin_stream} +% \begin{proc}{end_stream} +% The |begin_stream| and |end_stream| procedures delimit the creation +% of a \emph{stream object}. Between two such commands, it is possible +% to write arbitrary text (usually page descriptors or some sort of +% embedded data) to the PDF file and have it inserted correctly into +% the file as the data stored in the stream object. +% +% The syntax for |begin_stream| is +% \begin{quote} +% |pdf::begin_stream| \word{file} \word{reference label} +% \begin{regexp}[\regstar]\word{key} \word{value}\end{regexp} +% \end{quote} +% where \word{file} of course is the file to write to and +% \word{reference label} is the string that should be used to +% reference this object. Each stream consists of one dictionary part +% and one data part, where the primary task of the dictionary part is +% to specify how the data part should be interpreted. The most +% important element in the dictionary is the \texttt{/Length} key and +% its value---these are inserted by the |begin_stream| and +% |end_stream| commands, so one needs not worry about those---but if +% for example the data part is encoded in some special way (for +% example, it might be compressed) then it is necessary to include +% additional elements in the dictionary. This is what the \word{key} +% and \word{value} arguments are for. +% +% \begin{arrayentry}{file\meta{fnum}}{current_stream} +% The |current_stream| entry in a PDF file array is set if and only +% if the current position in that file is inside a stream. It is +% not possible to begin a new stream when this entry is set. The +% value of this entry is a list with the structure +% \begin{quote} +% \word{reference label} \word{start} +% \end{quote} +% where \word{reference label} is the reference label of the stream +% and \word{start} is the position in the file of the first byte in +% the stream data. Both of these are needed at |end_stream| to +% record the length of the stream data. +% \end{arrayentry} +% +% \begin{arrayentry}{file\meta{fnum}}{?\meta{reference label}} +% This kind of entry is used for indirect objects that are lengths +% of the stream whose reference label is the \meta{reference label}. +% They have the same syntax as their |!| ordinary counterparts. +% \end{arrayentry} +% +% \begin{tcl} +proc pdf::begin_stream {F label args} { + upvar #0 [namespace current]::$F A + if {[info exists A(current_stream)]} then { + error "There is already a stream ([lindex $A(current_stream) 0])\ + being written to in this file." + } + if {![info exists A(!$label)]} then { + incr A(last_object_num) + set A(!$label) [list $A(last_object_num) 0] + } + set A(?$label) [list [incr A(last_object_num)] 0] + lappend A(!$label) [tell $F] + puts $F\ + [format {%d %d obj} [lindex $A(!$label) 0] [lindex $A(!$label) 1]] + puts $F [eval\ + [list dict_obj /Length [format {%d 0 R} $A(last_object_num)]]\ + $args] + puts $F stream + set A(current_stream) [list $label [tell $F]] +} +% \end{tcl} +% +% The |end_stream| procedure takes the target file as its only argument. +% It finishes off the stream as necessary. It also evaluates +% everything that has been placed in the backlog of the file. +% +% \begin{arrayentry}{file\meta{fnum}}{backlog} +% It is not possible to output a new indirect object when a stream +% is being written to, but it can still be at such a time that the +% need for such an object is discovered. The |backlog| entry +% provides a way around that limitation---this entry is a script +% that is evaluated (and cleared) at the end of every |end_stream|, +% hence commands can be delayed by appending them to this script, +% instead of evaluating them immediately. +% +% New commands are appended to the |backlog|, and must be preceeded +% by a command separator. +% \end{arrayentry} +% +% \begin{tcl} +proc pdf::end_stream {F} { + upvar #0 [namespace current]::$F A + if {![info exists A(current_stream)]} then { + error "There is no stream to end." + } + set length [expr {[tell $F] - [lindex $A(current_stream) 1]}] + set label [lindex $A(current_stream) 0] + unset A(current_stream) + puts $F "endstream endobj" + lappend A(?$label) [tell $F] + puts $F [format {%d %d obj %d endobj} [lindex $A(?$label) 0]\ + [lindex $A(?$label) 1] $length] + eval "set A(backlog) {}; $A(backlog)" +} +% \end{tcl} +% \end{proc}\end{proc} +% +% \begin{proc}{put_obj} +% The |put_obj| procedure writes a direct object to a PDF file. The +% syntax is +% \begin{quote} +% |pdf::put_obj| \word{file} \word{reference label} \word{object} +% \end{quote} +% \begin{tcl} +proc pdf::put_obj {F label obj} { + upvar #0 [namespace current]::$F A + if {[info exists A(current_stream)]} then { + append A(backlog) \n [list put_obj $F $label $obj] + return + } + if {![info exists A(!$label)]} then { + incr A(last_object_num) + set A(!$label) [list $A(last_object_num) 0] + } + lappend A(!$label) [tell $F] + puts $F\ + [format {%d %d obj} [lindex $A(!$label) 0] [lindex $A(!$label) 1]] + puts $F $obj + puts $F endobj +} +% \end{tcl} +% \end{proc} +% +% \begin{proc}{rewrite_pdf} +% The |rewrite_pdf| procedure opens a new PDF file for writing and +% initialises the associated data structures. The syntax is +% \begin{quote} +% |pdf::rewrite_pdf| \word{file name} \meta{options} +% \end{quote} +% and the return value is the identifier of the file opened. The +% \word{file name} is of course the name of that file. The +% \meta{options} is zero or more of +% \begin{quote} +% |-permissions| \word{integer}\\ +% |-header| \word{string} +% \end{quote} +% The permissions are the default permissions for the file in question. +% If this is not specified, then no such value is specified to |open|, +% The header is a string that will be put first in the file (as header). +% It defaults to +% \begin{quote} +% \texttt{\%PDF-1.3}\\ +% \texttt{\%\r{a}\"a\"o} +% \end{quote} +% where the first line is a standard header line, and the second line +% is there to help some software understand that the file should be +% treated as a binary file. \textbf{Note} that no newline is inserted +% after this string; be sure to include it in the string if necessary. +% \begin{tcl} +proc pdf::rewrite_pdf {name args} { + set Opt(-header) %PDF-1.3\n%\xe5\xe4\xf6\n + array set Opt $args + if {[info exists Opt(-permissions)]} then { + set F [open $name w $Opt(-permissions)] + } else { + set F [open $name w] + } + fconfigure $F -translation binary + puts -nonewline $F $Opt(-header) + upvar #0 [namespace current]::$F A + array unset A + set A(last_object_num) 0 + set A(backlog) "" + return $F +} +% \end{tcl} +% \end{proc} +% +% +% \begin{proc}{close_pdf} +% The |close_pdf| procedure performs the non-trivial task of finishing +% off the PDF file and closing it. The syntax is +% \begin{quote} +% |pdf::close_pdf| \word{file} \word{catalog label} +% \begin{regexp}[\regstar]\word{key} \word{value}\end{regexp} +% \end{quote} +% and the return value is a report detailing any problems +% encountered (such as objects that are referred to but never +% defined). This is a report rather than an error, because there is +% in many cases no sharp distinction. If the return value is +% non-empty, then there is probably a bug in your program that needs +% to be fixed. +% +% The \word{file} is the identifier of the file to write. The +% \word{catalog label} is the reference label of the Catalog object +% in the document. The remaining arguments can be used to insert +% additional information (such as a reference to the Info dictionary +% of the document) in the trailer dictionary. +% +% \begin{tcl} +proc pdf::close_pdf {F label args} { + upvar #0 [namespace current]::$F A + set reportL [list] +% \end{tcl} +% The first step is to compile the cross-reference table of the +% document. I originally made one subsection for each range of defined +% indirect objects, giving the mandatory free entry \#0 a separate +% subsection, but for some reason Adobe software didn't like that at +% all.\footnote{Whether this means Adobe isn't following their own +% standard I leave to others to decide. Neither GhostScript nor +% Quartz (the PDF-based graphics system in Mac~OS~X) seemed to have +% any problems with this arrangement.} Hence the current +% implementation is to make a cross-reference table with only one +% subsection, with an explicit free entry for every missing item. +% +% \changes{0.1}{2003/02/24}{Changed cross-reference section to avoid +% what is probably a bug in Adobe PDF readers. (LH)} +% +% The |xrA| array constructed below is a prototype for the +% cross-reference section. It is indexed by object number and the +% entries have the list structure +% \begin{quote} +% \word{file position} \word{generation number} \word{type} +% \end{quote} +% Just as in a PDF file, the \word{type} is either \texttt{f} or +% \texttt{n} depending on whether the entry is ``free'' or +% ``in use''. The \word{file position} and \word{generation number} +% are however not padded with zeros, and the \word{file position} is +% initially an empty string in the ``free'' entries. +% +% This first round simply collects the information and detects +% collisions. +% \begin{tcl} + set xrA(0) [list "" 65535 f] + foreach lbl [array names A {[!?]*}] { + set idx [lindex $A($lbl) 0] + set ent [list [lindex $A($lbl) 2] [lindex $A($lbl) 1] n] + if {[llength $A($lbl)]<3} then { + lappend reportL "There is no indirect object with label:\ + [string range $lbl 1 end]" + set ent [replace $ent 2 2 f] + } elseif {[llength $A($lbl)]>3} then { + lappend reportL "Multiple indirect objects for label\ + [string range $lbl 1 end]; at\ + [join [lrange $A($lbl) 2 end]]." + } + if {![info exists xrA($idx)]} then { + set xrA($idx) $ent + } elseif {[lindex $xrA($idx) 2]=="f" && [lindex $ent 2]=="n"}\ + then { + lappend reportL "This shouldn't happen:\ + There are several reference labels for\ + indirect object $idx. Using that with label:\ + [string range $lbl 1 end]" + set xrA($idx) $ent + } else { + lappend reportL "This shouldn't happen:\ + There are several reference labels for\ + indirect object $idx. Ignoring that with label:\ + [string range $lbl 1 end]" + } + } +% \end{tcl} +% The second round makes sure that there is a contiguous sequence of +% reference numbers and constructs the linked list of free entries. +% \begin{tcl} + set last_free 0 + set maxidx [lindex [lsort -integer -decreasing [array names xrA]] 0] + for {set n $maxidx} {$n>=0} {incr n -1} { + if {![info exists xrA($n)]} then { + set xrA($n) [list "" 0 f] + lappend reportL "This shouldn't happen:\ + Object number $n was allocated, but not\ + assigned a reference label." + } + if {[lindex $xrA($n) 2]=="f"} then { + set xrA($n) [lreplace $xrA($n) 0 0 $last_free] + set last_free $n + } + } +% \end{tcl} +% Now the cross-reference section can be written to file. +% \begin{tcl} + set startxref [tell $F] + puts $F xref + puts $F [format {%d %d} 0 [expr {$maxidx + 1}]] + for {set n 0} {$n<=$maxidx} {incr n} { + puts $F [eval [list format {%010d %05d %1s }] $xrA($n)] + } +% \end{tcl} +% Having completed the cross-reference table, the second step is +% to write the trailer. +% \begin{tcl} + puts $F trailer + if {[info exists A(!$label)]} then { + lappend args /Root [format {%d %d R} [lindex $A(!$label) 0]\ + [lindex $A(!$label) 1]] + } else { + lappend reportL "ERROR: The document does not have a catalog."\ + "There is no indirect object with label: $label" + } + lappend args /Size [array size xrA] + puts $F [eval [list dict_obj] $args] + puts $F "startxref\n${startxref}\n%%EOF" +% \end{tcl} +% The final step is to close the file and compile the report. +% \begin{tcl} + close $F + join $reportL \n +} +% +% \end{tcl} +% \end{proc} +% +% +% \subsection{Hello World} +% +% The code below creates a PDF file matching the basic ``Hello World'' +% example \cite[Sec.~A.2]{PDFspec}. +% +% \begin{tcl} +%<*example1> +set F [pdf::rewrite_pdf hello.pdf] +pdf::put_obj $F "The catalog" [pdf::dict_obj\ +% + /Type /Catalog\ +% + /Pages [pdf::obj_ref $F "The pages"]\ +% + /Outlines [pdf::obj_ref $F "The outlines"]] +pdf::put_obj $F "The outlines"\ + [pdf::dict_obj /Type /Outlines /Count [pdf::int_obj 0]] +pdf::put_obj $F "The pages" [pdf::dict_obj\ +% + /Type /Pages\ +% + /Count [pdf::int_obj 1]\ +% + /Kids [pdf::array_obj [pdf::obj_ref $F "Page 1"]]] +pdf::put_obj $F "Page 1" [pdf::dict_obj\ +% + /Type /Page\ +% + /Parent [pdf::obj_ref $F "The pages"]\ +% + /Resources [pdf::dict_obj\ +% + /Font [pdf::dict_obj /F1 [pdf::obj_ref $F "Helvetica"]]\ +% + /ProcSet [pdf::obj_ref $F "The procs"]]\ +% + /MediaBox [pdf::array_obj [pdf::int_obj 0] [pdf::int_obj 0]\ + [pdf::int_obj 612] [pdf::int_obj 792]]\ +% + /Contents [pdf::obj_ref $F "Page 1 contents"]] +pdf::begin_stream $F "Page 1 contents" +puts $F {BT} +puts $F {/F1 24 Tf} +puts $F {100 100 Td (Hello World) Tj} +puts $F {ET} +pdf::end_stream $F +pdf::put_obj $F "The procs" [pdf::array_obj /PDF /Text] +pdf::put_obj $F "Helvetica" [pdf::dict_obj\ + /Type /Font /Subtype /Type1\ + /Name /F1 /BaseFont /Helvetica /Encoding /MacRomanEncoding] +pdf::close_pdf $F "The catalog" +% +% \end{tcl} +% +% +% \begin{thebibliography}{9} +% \bibitem{PDFspec} +% Adobe Systems Incorporated: +% \textit{Portable Document Format Reference Manual}, +% version~1.3 (second edition), Addison--Wesley, 1999; +% ISBN 0-201-61588-6; +% \textsc{http:}/\slash \texttt{partners.adobe.com}\slash +% \texttt{asn}\slash \texttt{developer}\slash \texttt{acrosdk}\slash +% \texttt{docs}\slash \texttt{filefmtspecs}\slash +% \texttt{PDFReference13.pdf}. +% \emph{Note:} There is now a version~1.4 of this specification. +% \end{thebibliography} +% +% \PrintIndex +% +% +\endinput \ No newline at end of file diff --git a/macros/latex/contrib/tclldoc/examples/pdf.ins b/macros/latex/contrib/tclldoc/examples/pdf.ins new file mode 100644 index 0000000000..00bea9f775 --- /dev/null +++ b/macros/latex/contrib/tclldoc/examples/pdf.ins @@ -0,0 +1,55 @@ +% pdf.ins --- DOCSTRIP installation script for writepdf. +\input docstrip + +% Redefine the \MetaPrefix; it should be something which starts a +% until-end-of-line comment: +\edef\MetaPrefix{\string#\string#} + + +% Redefine the file preamble and postamble; this is necessary because +% otherwise the old \metaPrefix is inserted at the beginning of these +% lines. +\preamble + +In other words: + *************************************** + * This Source is not the True Source. * + *************************************** +The True Source is pdf.dtx in + http://ftp.ctan.org/tex-archive/macros/latex/contrib/tclldoc/examples + +It is preferred that you apply the distribution and modification +conditions of the LaTeX Project Public License (LPPL) for this file, +but you may alternatively choose to apply BSD/Tcl-style license +conditions (either is OK). The latest version of the LPPL is in + http://www.latex-project.org/lppl.txt +and version 1.2 or later is part of all distributions of LaTeX +version 1999/12/01 or later. + +\endpreamble + +\postamble +\endpostamble + + +% Actually make the files: +\generate{ + \file{writepdf.tcl}{\from{pdf.dtx}{pkg}} + \file{hellopdf.tcl}{\from{pdf.dtx}{example1}} +} + + +\Msg{} +\Msg{************************************************} +\Msg{*} +\Msg{* To complete installation,} +\Msg{* place pdfwrite.tcl in a Tcl library directory} +\Msg{* and run pkg_mkIndex to rebuild the package} +\Msg{* index.} +\Msg{*} +\Msg{************************************************} +\Msg{} + + +\end + diff --git a/macros/latex/contrib/tclldoc/tclldoc.dtx b/macros/latex/contrib/tclldoc/tclldoc.dtx new file mode 100644 index 0000000000..6106dcce36 --- /dev/null +++ b/macros/latex/contrib/tclldoc/tclldoc.dtx @@ -0,0 +1,4065 @@ +% \CheckSum{1849} +% +% \title{The \package{tclldoc} package and class} +% \author{Lars Hellstr\"om^^A +% \thanks{E-mail: \texttt{Lars.Hellstrom@math.umu.se}} +% \emph{et al.}} +% \date{2003/07/19} +% \maketitle +% +% \iffalse +% +%\NeedsTeXFormat{LaTeX2e} +%\ProvidesClass{tclldoc} +%\ProvidesClass{tcldoc} +%\ProvidesPackage{tclldoc} +%\ProvidesPackage{tcldoc} +% [2003/04/05 v2.40 +% Tcl documentation +% tclldoc compatibility +% class] +% package] +% +%<*driver> +\documentclass{ltxdoc} +\usepackage{amsmath} +\IfFileExists{xdoc2.sty}{\usepackage[dolayout]{xdoc2}[2000/11/18]}{} + +\makeatletter +\@ifpackageloaded{xdoc2}{}{% + \@ifpackagelater{doc}{2000/05/20}{}{% + \let\XD@fragile@meta=\meta + \def\meta{% + \ifx \protect\@typeset@protect + \expandafter\futurelet \expandafter\@let@token + \expandafter\XD@fragile@meta + \else + \noexpand\meta + \fi + }% + }% +} +\@ifundefined{option}{% + \newenvironment{option}[1]{% + \trivlist + \if@inlabel\else \macro@cnt=\z@ \fi + \item[]\ignorespaces + }{\endtrivlist}% +}{} +\@ifundefined{instance}{% + \newenvironment{instance}[2]{% + \trivlist + \if@inlabel\else \macro@cnt=\z@ \fi + \item[]\ignorespaces + }{\endtrivlist}% +}{} +\makeatother + +\DeclareRobustCommand\package[1]{\textsf{#1}} +\DeclareRobustCommand\Tcl{T\kern-0.1em cl} +\DeclareRobustCommand\word[1]{\mbox{$\{$\itshape#1\/$\}$}} + +\makeatletter +\newcommand{\swtpc}{% + \sbox\z@{ }% + \expandafter\makebox \expandafter[\the\wd\z@]% + {\textperiodcentered}% +} +\makeatother + +\providecommand*\describeoption[1]{\marginpar{\texttt{#1} option}} +\providecommand*\PrintChar[1]{\char#1\relax} +\providecommand*{\DoNotIndexHarmless}[1]{} +\providecommand*{\DoNotIndexBy}[1]{} +\DoNotIndexBy{@} +\DoNotIndexBy{@@} +\DoNotIndexBy{TD@} +\newenvironment{isyntax}{% + \list{}{% + \setlength\parsep{0pt plus 1pt}% + \setlength\listparindent{-1em}% + \setlength\itemindent{\listparindent}% + \addtolength\leftmargin{1em}% + }% + \item\relax \rightskip=1.0\rightskip plus 5cm +}{\endlist} + +\setcounter{IndexColumns}{2} +\hfuzz=10pt +\expandafter\def \expandafter\GlossaryParms \expandafter{% + \GlossaryParms \hbadness=10000 +} + +\CodelineIndex +\EnableCrossrefs +\RecordChanges + +\begin{document} +\DocInput{tclldoc.dtx} +\PrintChanges +\PrintIndex +\end{document} +% +% +% \fi +% +% \begin{abstract} +% The \package{tclldoc} package defines a couple of environments and +% macros for documenting \Tcl\ source code in \texttt{.dtx}-style +% documented source files, much like what the \package{doc} +% package~\cite{doc} does for \TeX\ source code. The \package{tclldoc} +% class is analogous to the \package{ltxdoc} document +% class~\cite{ltxdoc}---it loads the package to gain the basic +% functionality and changes some layout parameters to values that +% are better suited for documented source than those set by the +% standard \package{article} document class. +% +% The \package{tclldoc} package builds on the \package{doc}, +% \package{xdoc}~\cite{xdoc}, and \package{docindex}~\cite{docindex} +% packages. +% +% Note: The \package{tclldoc} package and class used to be called +% \package{tcldoc}, but it turned out that there already existed a +% Perl~(!!)\ script \Tcl Doc that has a similar function. Therefore I +% added an extra `\textsf{l}'---which stands for both \LaTeX\ and +% Literate---to avoid confusing the community of \Tcl\ programmers. +% For compatibility, a package and a class named \package{tcldoc} are +% installed with \package{tclldoc}, but they shouldn't be used for +% new documents. +% \end{abstract} +% +% \tableofcontents +% +% \DoNotIndex{\advance,\begingroup,\bgroup,\big,\Big,\bigg,\Bigg,\box} +% \DoNotIndex{\catcode,\char,\chardef,\cleaders,\color@begingroup} +% \DoNotIndex{\color@endgroup,\copy,\csname,\DeclareOption} +% \DoNotIndex{\DeclareRobustCommand,\DeclareTextCommand} +% \DoNotIndex{\DeclareTextSymbol,\def,\discretionary,\divide,\do} +% \DoNotIndex{\edef,\egroup,\else,\@empty,\endcsname,\endgroup} +% \DoNotIndex{\expandafter,\fi,\@firstoftwo,\font,\fontdimen} +% \DoNotIndex{\futurelet,\gdef,\global,\@gobble,\hb@xt@,\hbox} +% \DoNotIndex{\hfill,\if,\ifcase,\ifcat,\ifmmode,\ifnum,\ifvbox} +% \DoNotIndex{\ifvmode,\ifvoid,\ifx,\ignorespaces,\immediate,\indent} +% \DoNotIndex{\input,\@@input,\InputIfFileExists,\IfFileExists} +% \DoNotIndex{\item,\kern,\language,\lastbox,\lastpenalty,\lastskip} +% \DoNotIndex{\lccode,\leavevmode,\let,\llap,\LoadClass,\lowercase} +% \DoNotIndex{\m@ne,\m@th,\@makeother,\mathord,\mkern,\multiply} +% \DoNotIndex{\@ne,\newbox,\newcommand,\newcount,\NewDescribeCommand} +% \DoNotIndex{\newenvironment,\newif,\NewMacroEnvironment,\nfss@text} +% \DoNotIndex{\noexpand,\normalfont,\null,\number,\or,\p@,\@@par} +% \DoNotIndex{\parshape,\PassOptionsToClass,\PassOptionsToPackage} +% \DoNotIndex{\penalty,\@plus,\predisplaypenalty,\prevdepth,\prevgraf} +% \DoNotIndex{\ProcessOptions,\protect,\protected@edef} +% \DoNotIndex{\providecommand,\ProvideTextCommandDefault,\relax} +% \DoNotIndex{\RequirePackage,\RequirePackageWithOptions,\rightarrow} +% \DoNotIndex{\sbox,\selectfont,\setbox,\skewchar,\space,\string} +% \DoNotIndex{\TextSymbolUnavailable,\texttt,\the,\tracingparagraphs} +% \DoNotIndex{\ttfamily,\tw@,\typeout,\unhbox,\unpenalty,\unskip} +% \DoNotIndex{\unrestored@protected@xdef,\unskip,\unvbox,\vadjust} +% \DoNotIndex{\vbox,\vert,\vrule,\vskip,\wd,\write,\xdef,\z@,\z@skip} +% +% \section{Introduction} +% \label{Sec:Introduction} +% +% This introduction is meant to be comprehensible even for readers +% unfamiliar with writing \texttt{.dtx} files and using the \package{doc} +% package. Readers who are experienced with this will probably want to +% skip right to the next section. +% +% A \texttt{.dtx} file has a dual nature. On one hand it is a container +% for some lines of code---it could be a program, a macro package, a +% configuration file for some program, merely a part of any of the +% aforementioned, or even arbitrary combinations of the above---and on +% the other hand it is a \LaTeX\ document which documents this code. One +% important advantage with this arrangement is that one can keep all the +% pieces of a project that has to do with a specific task at one place; +% experience has shown that this greatly furthers really keeping all +% parts of a project up to date with each other. +% +% Slightly simplified, one can say that a \texttt{.dtx} file contains +% three kinds of lines. A \emph{code line} is a line that doesn't begin +% with a `|%|' character; such lines can be extracted (copied) using the +% \package{docstrip} program~\cite{docstrip}. A \emph{guard line} is a +% line that begins with the two characters `|%<|'; guards are used to +% structure the set of code lines so that \package{docstrip} can extract +% different code lines to different generated files. A \emph{comment +% line}, finally, is a line that begins with a `|%|' character that is +% not immediately followed by a `|<|' character. The comment lines are +% ignored by \package{docstrip}, but are part of (and usually make up +% most of) the \LaTeX\ document in the \texttt{.dtx} file. +% +% +% \subsection{Special conventions and basic features in a \texttt{.dtx} +% \LaTeX\ document} +% +% An important difference between normal \LaTeX\ documents and +% \texttt{.dtx} \LaTeX\ documents is that the percent character `|%|' +% doesn't start a comment in the latter; in fact it is usually ignored. +% This allows \LaTeX\ to see and typeset the text in the comment lines +% of a \texttt{.dtx} file. Hence if one wants to include the sentence +% ``Your hovercraft is full of eels!'', which in a normal \LaTeX\ +% document could have been written as the line\iffalse +%<*example> +% \fi +%\begin{verbatim} +%Your hovercraft is full of eels! +%\end{verbatim} +% one would instead write the line as +%\begin{verbatim} +%% Your hovercraft is full of eels! +%\end{verbatim} +% in a \texttt{.dtx} document. The space after the |%| is not +% necessary, but most \texttt{.dtx} documents you see include +% it---probably because the ``comment out \TeX\ code'' action of most +% text editors consists of inserting a percent \emph{and} a space at +% the beginning of each line. +% +% The code lines present the opposite problem, as they usually +% shouldn't be treated as normal \LaTeX\ code although the normal \LaTeX\ +% reading conventions would make them the entire document. The usual way +% around this is to surround each group of code lines with two comment +% lines that begin and end an environment in which the code lines get +% typeset verbatim. The \package{tclldoc} package provides the \texttt{tcl} +% environment for this purpose, so the code lines +%\begin{verbatim} +%proc factorial {n} { +% set result 1 +% for {set i 1} {$i<=$n} {incr i} { +% set result [expr {$result * $i}] +% } +% return $result +%} +%\end{verbatim} +% could be included in a \texttt{.dtx} document as the lines +%\begin{verbatim} +%% \begin{tcl} +%proc factorial {n} { +% set result 1 +% for {set i 1} {$i<=$n} {incr i} { +% set result [expr {$result * $i}] +% } +% return $result +%} +%% \end{tcl} +%\end{verbatim} +% When typeset, this will look as +% \begin{macrocode} +proc factorial {n} { + set result 1 + for {set i 1} {$i<=$n} {incr i} { + set result [expr {$result * $i}] + } + return $result +} +% \end{macrocode} +% The tiny numbers at the beginning of each line enumerate the code lines. +% Index references to code usually specify such code line numbers, but +% the enumeration can be switched off. +% +% In mathematical papers, the statements of e.g.\ theorems are usually +% made inside a \texttt{theorem} (or whatever) environment which +% provides certain text formatting, a heading, and a position in the +% document that can be referenced from other parts of it. In \texttt{.dtx} +% documents one usually does something similar for each named piece of +% code: macros, environments, templates, etc. In particular, the +% \package{tclldoc} package provides two environments \texttt{proc} (for +% procedures) and \texttt{variable} (for variables). +% Figure~\ref{Fig:procex} contains an example of how \texttt{proc} +% might be used in describing a procedure for computing the greatest +% common divisor of two integers. +% +% \begin{figure} +% +% \begin{trivlist} +% \item[]\leavevmode\llap{\texttt{gcd} (proc)\kern\marginparsep}^^A +% The |gcd| procedure takes two arguments $a$ and $b$ which must be +% integers and returns their greatest common divisor $\gcd(a,b)$, +% which is computed using Euclid's algorithm. As a special case, +% $\gcd(0,0)$ is considered to be $0$, so formally |gcd $a $b| +% computes $\lvert a \rvert \wedge \lvert b \rvert$, where $\wedge$ +% denotes the meet operation in the divisor lattice of non-negative +% integers. +% \begin{macrocode} +proc gcd {a b} { + set a [expr {abs($a)}] + set b [expr {abs($b)}] + while {$b>0} { + set r [expr {$a%$b}] + set a $b + set b $r + } + return $a +} +% \end{macrocode} +% \end{trivlist} +% +% \begin{center} +% (a) A typeset procedure with description +% \end{center} +% +%\begin{verbatim} +%% \begin{proc}{gcd} +%% The |gcd| procedure takes two arguments $a$ and $b$ which must be +%% integers and returns their greatest common divisor $\gcd(a,b)$, +%% which is computed using Euclid's algorithm. As a special case, +%% $\gcd(0,0)$ is considered to be $0$, so formally |gcd $a $b| +%% computes $\lvert a \rvert \wedge \lvert b \rvert$, where $\wedge$ +%% denotes the meet operation in the divisor lattice of non-negative +%% integers. +%% \begin{tcl} +%proc gcd {a b} { +% set a [expr {abs($a)}] +% set b [expr {abs($b)}] +% while {$b>0} { +% set r [expr {$a%$b}] +% set a $b +% set b $r +% } +% return $a +%} +%% \end{tcl} +%% \end{proc} +%\end{verbatim} +% +% \begin{center} +% (b) The code for the example in (a) +% \end{center} +% +% \caption{An example of the \texttt{proc} environment} +% \label{Fig:procex} +% \end{figure} +% +% What does the \texttt{proc} environment do more precisely? First +% there's the marginal heading which can be seen in Figure~^^A +% \ref{Fig:procex}. Such headings make it easier to find the procedure in +% the typeset form of the document. Then the \texttt{proc} environment +% makes an index entry which tells you where the procedure is defined, +% and finally it stores the procedure name in a variable so that +% subsequent |\changes|\footnote{The \cs{changes} command is defined by +% the \package{doc} package~\cite{doc}. It is used for adding entries +% to a global list of changes for code in the \texttt{.dtx} document.} +% commands know to what the change that they are recording was made. +% +% The \texttt{variable} environment does the same things except that it +% writes ``(var.)'' rather than ``(proc)''. This environment wasn't used +% for describing the three local variables |a|, |b|, and |r| in the +% example; this is since there is no point in referring to these +% variables from elsewhere in the program. Instead the \texttt{variable} +% environment is primarily meant for global variables (although it could +% also be useful for local variables that are meant to be accessed using +% |upvar| or |uplevel|), and as such it can often be of great help, since +% the description of a global variable can otherwise be hard to find, +% especially with languages like \Tcl\ where variables don't have to be +% declared and thus have no natural ``home'' in the code. +% +% Another noteworthy feature in the example is the use of vertical +% bar `\verb+|+' characters to delimit short pieces of verbatim \Tcl\ +% code in the comment lines. It is often necessary for the explanation +% to include short examples of code in the documentation, and the +% standard \LaTeX\ |\verb| command is exactly what one would need for +% this. As such code sections are rather frequent however, it has become +% the custom to use a single character for both starting and ending such +% a piece of code. The \package{tclldoc} document class defines \verb+|+ +% as a shorthand for \verb+\tclverb|+, where |\tclverb| is a variant of +% |\verb| which has been designed specifically for \Tcl\ code. +% +% The above description was meant to give a basic understanding of how +% \Tcl\ code and documentation thereof can be mixed in a \texttt{.dtx} +% file, it neither explains all the environments and commands that +% the \package{tclldoc} package provides, nor mentions all the +% features of the environments that were described. That information can +% instead be found in Section~\ref{Sec:Manual} of this paper. It should +% also be mentioned that the \package{doc} package~\cite{doc} defines +% several commands and environments that may be of use for describing +% code, and it is well worth getting acquainted with the features of +% that package as well (although parts of its documentation has become +% rather archaic). +% +% +% \subsection{Guards and \package{docstrip} installation scripts} +% +% The central command in a \package{docstrip} installation script is +% |\generate|, since this is the command which actually causes code to +% be extracted. |\generate|'s syntax is +% \begin{quote} +% |\generate|\marg{files} +% \end{quote} +% where \meta{files} consists of one or several |\file| commands, each +% of which has the syntax +% \begin{quote} +% |\file|\marg{output}\marg{sources} +% \end{quote} +% where \meta{output} is a filename and \meta{sources} consists of one +% or several |\from| commands, each of which has the syntax +% \begin{quote} +% |\from|\marg{input}\marg{options} +% \end{quote} +% where, finally, \meta{input} is a filename and \meta{options} is a +% comma-separated list of alphanumerical strings. Thus a |\generate| +% command might look like +%\begin{verbatim} +% \generate{\file{p1.sty}{\from{s1.dtx}{foo,bar}} +% \file{p2.sty}{\from{s2.dtx}{baz} +% \from{s3.dtx}{baz}} +% \file{p3.sty}{\from{s1.dtx}{zip} +% \from{s2.dtx}{zap}} +% } +%\end{verbatim} +% The meaning of this command is +% \begin{quote} +% Generate the three files \texttt{p1.sty}, \texttt{p2.sty}, and +% \texttt{p3.sty}. Extract the code for \texttt{p1.sty} from +% \texttt{s1.dtx} with options \texttt{foo} and \texttt{bar}, extract +% the code for \texttt{p2.sty} from \texttt{s2.dtx} with option +% \texttt{baz} and \texttt{s3.dtx} (the code from \texttt{s2.dtx} +% will be put before the code from \texttt{s3.dtx}) with option +% \texttt{baz}, and finally extract the code for \texttt{p3.sty} from +% \texttt{s1.dtx} with option \texttt{zip} and \texttt{s2.dtx} with +% option \texttt{zap}. +% \end{quote} +% The \emph{options} are used to control which parts of the source files +% should be extracted to which generated file. A source file can +% contain a number of \emph{modules}, and at the beginning of each +% module \package{docstrip} decides, for each output file separately, +% whether the code lines in that module should be extracted to the +% output file. The beginning of a module is marked by a guard line which +% has the syntax +% \begin{quote} +% |%<*|\meta{expression}|>| +% \end{quote} +% and the end by a corresponding +% \begin{quote} +% |%| +% \end{quote} +% guard line. In their simplest form, the \meta{expression}s are names +% of options, and in that case the code lines in the module are only +% extracted if that option appears in the \meta{options} for that +% combination of input file and output file. The \meta{expression}s can +% however be arbitrarily complicated boolean expressions; +% see~\cite{docstrip} for more information. +% Modules may nest inside each other, and in that case the code lines in +% an inner module can only be included if all surrounding modules are +% being included. It is checked that matching |*| and |/| guard lines +% contain the same (as strings) \meta{expression}, and case is +% significant in the names of options. +% +% One application of modules which has already been mentioned is to +% bundle code for several different generated files in the same +% \texttt{.dtx} file---one example of this is the file +% \texttt{doc.dtx} (part of the \LaTeX\ base distribution) which +% contains both the \package{doc} package (\texttt{doc.sty}), the +% \package{shortvrb} package (\texttt{shortvrb.sty}), and two +% \package{makeindex} style files (\texttt{gglo.ist} and +% \texttt{gind.ist}). Another application is to keep variant sections +% of code---such as special code for debugging or gathering +% statistics---in the \texttt{.dtx} source file for a program without +% thereby making it a part of the normal form of that program. It is +% quite possible to use \package{docstrip} as a simple pre-processor +% for languages whose compiler\slash interpreter has not got one built +% in. +% +% There are many other commands available in a \package{docstrip} +% installation script beside those listed above, but those are well +% described in the \package{docstrip} manual~\cite{docstrip} and need +% little attention here. Instead I'm going to finish this subsection +% with a quick guide to the particular difficulties one faces when using +% \package{docstrip} to extract \Tcl\ code, and how to overcome them. +% +% The main problem is that \package{docstrip} insert a few comment lines +% at the beginning and end of each file it generates. This is a good +% thing, because a file consisting entirely of extracted code lines +% would normally be completely void of commentary and quite +% unintelligible for the casual user. These few comment lines explain +% that the file was generated by \package{docstrip} from other files +% (which contain the documentation of the code), lists those files, +% and normally also contains a copyright (or more commonly some kind of +% copyleft) notice. The problem lies in that comments look different in +% different languages, and as the default is to write \TeX\ style +% comments, one must tell \package{docstrip} to write \Tcl\ style +% comments. This can be done through the command +%\begin{verbatim} +%\edef\MetaPrefix{\string#} +%\end{verbatim} +% which tells \package{docstrip} to begin each inserted comment line +% with the character `|#|'. +% +% The comment lines inserted at the beginning of a generated file are +% called the \emph{preamble} and those at the end the \emph{postamble}. +% To set the preamble, one writes +% \begin{trivlist} +% \item |\preamble|\\ +% \meta{preamble lines}\\ +% |\endpreamble| +% \end{trivlist} +% and correspondingly to set the postamble +% \begin{trivlist} +% \item |\postamble|\\ +% \meta{postamble lines}\\ +% |\endpostamble| +% \end{trivlist} +% The \meta{preamble lines} and \meta{postamble lines} can be any number +% of lines (including zero). Unlike the text in source files, the text in +% these preamble and postamble lines is not read verbatim, so things in +% these lines which have special meaning to \TeX\ (such as control +% sequences) will be treated as such; the only exception is that spaces +% and newlines are preserved (instead of concatenated to single spaces +% as they normally would). It is important that the preamble and +% postamble \emph{are} set after |\MetaPrefix| is changed, because each +% line specified between |\preamble| and |\endpreamble| or |\postamble| +% and |\endpostamble| respectively will be prefixed by the current value +% of |\MetaPrefix|. +% +% Finally, some programs (such as the UNIX core) assign special meaning +% to the first line of a file, so one might want to control what gets +% put there. Merely using |\preamble| doesn't achieve this, because the +% \meta{preamble lines} specified that way are put after the lines +% saying ``this is a generated file \textellipsis''. You can however +% add things to the preamble by explicitly setting the macro +% |\defaultpreamble|, which is where \package{docstrip} stores the +% preamble. To make the first line a comment which simply contains the +% text `|-*-Tcl-*-|', you could give the command +%\begin{verbatim} +%\edef\defaultpreamble{\MetaPrefix\space -*-Tcl-*-^^J\defaultpreamble} +%\end{verbatim} +% Similarly to begin the file by the three standard lines +%\begin{verbatim} +%#! /bin/sh +%#\ +%exec tclsh "$0" ${1+"$@"} +%\end{verbatim} +% (for an explanation see~\cite{execMagic})---which on UNIX allow the file +% to function both as a \Tcl\ script and a shell script which terminates +% the shell and runs \texttt{tclsh} on the script instead---you can use +% the command +%\begin{verbatim} +%\edef\defaultpreamble{% +% \MetaPrefix! /bin/sh^^J% +% \MetaPrefix\string\^^J% +% exec tclsh "$0" ${1+"$@"}^^J% +% \defaultpreamble +%} +%\end{verbatim} +% The full explanation of these commands is however far beyond this +% introduction.\footnote{Those who want to fully understand them should +% read \emph{The \TeX book}~\cite{TeXbook}, in particular Chapter~8.} +% +% In summary, a \package{docstrip} installation script for extracting a +% file \texttt{foo.tcl} from \texttt{foo.dtx}, using \Tcl\ style +% comments, inserting a BSD-style license notice in the preamble, and +% beginning with the line |# -*-Tcl-*-| could look as follows: +%\begin{verbatim} +%\input docstrip.tex +% +%\edef\MetaPrefix{\string#} +% +%\preamble +% +%Copyright (c) , +%All rights reserved. +% +%Redistribution and use in source and binary forms, with or without +%modification, are permitted provided that the following conditions +%are met: +%* Redistributions of source code must retain the above copyright +%notice, this list of conditions and the following disclaimer. +%* Redistributions in binary form must reproduce the above +%copyright notice, this list of conditions and the following +%disclaimer in the documentation and/or other materials provided +%with the distribution. +% +%THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +%"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +%LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +%FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +%COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +%INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +%BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +%LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +%CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +%LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +%ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +%POSSIBILITY OF SUCH DAMAGE. +% +%\endpreamble +% +%\postamble +%\endpostamble +% +%\edef\defaultpreamble{\MetaPrefix\space -*-Tcl-*-^^J\defaultpreamble} +% +%\generate{\file{foo.tcl}{\from{foo.dtx}{bar}}} +% +%\end +%\end{verbatim} +% +% The generated file \texttt{foo.tcl} will contain +%\begin{verbatim} +%# -*-Tcl-*- +%# +%# This is file `foo.tcl', +%# generated with the docstrip utility. +%# +%# The original source files were: +%# +%# foo.dtx (with options: `bar') +%# +%# Copyright (c) , +%# All rights reserved. +%# +%# Redistribution and use in source and binary forms, with or without +%# modification, are permitted provided that the following conditions +%# are met: +%# * Redistributions of source code must retain the above copyright +%# notice, this list of conditions and the following disclaimer. +%# * Redistributions in binary form must reproduce the above +%# copyright notice, this list of conditions and the following +%# disclaimer in the documentation and/or other materials provided +%# with the distribution. +%# +%# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +%# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +%# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +%# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +%# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +%# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +%# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +%# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +%# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +%# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +%# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +%# POSSIBILITY OF SUCH DAMAGE. +%# +%\end{verbatim} +% \meta{lines extracted from \texttt{foo.dtx}} +%\begin{verbatim} +%# +%# +%# End of file `foo.tcl'. +%\end{verbatim} +% +% +% +% \subsection{The structure of the \LaTeX\ document} +% +% All this has been about the local appearance of a \texttt{.dtx} file, +% but what about the overall structure? There are several points to +% raise about that as well. +% +% The first is that \LaTeX\ to begin with treat \texttt{.dtx} documents +% just like any other document---a `|%|' starts a comment and only lines +% \emph{not} beginning with a `|%|' contain anything that \LaTeX\ can +% see. Somehow \LaTeX\ must be instructed to start applying the special +% reading rules that were described above. This is the job of the +% so-called \emph{driver}, which (for a file \texttt{myfile.dtx}) in its +% simplest form can look like +%\begin{verbatim} +%\documentclass{tclldoc} +%\begin{document} +%\DocInput{myfile.dtx} +%\end{document} +%\end{verbatim} +% The important command here is |\DocInput|, because that is what tells +% \LaTeX\ to apply the special \texttt{.dtx} reading rules. More +% precisely it means ``Start ignoring `|%|' characters in the text you +% read, input the file \texttt{myfile.dtx}, and when you're done return +% to treating `|%|' characters as before.'' +% +% The driver is usually put in the very first stretch of code lines in +% the \texttt{.dtx} file. This means that \LaTeX, when ordered to typeset +% the \texttt{.dtx} file, will start to read along, possibly ignoring +% hundreds of lines beginning with `|%|' because they are comments. Then +% it encounters the driver, and after the |\documentclass| and +% |\begin{document}| commands it executes the |\DocInput|. This will +% cause it to not ignore lines beginning with `|%|', so when it starts +% reading the file again it will see all the lines it skipped the first +% time through. The file will be read to end, after which \LaTeX\ +% returns to the command after the |\DocInput|. As that command happens +% to be |\end{document}|, it finishes the typeset document and stops. +% This stop prevents it from seeing and interpreting as \LaTeX\ commands +% the remaining code lines in the file. +% +% The second time through the driver shouldn't be interpreted as +% \LaTeX\ commands, since for example the |\documentclass| command may +% only be used once in a \LaTeX\ document. One way of achieving this is +% to put an |\iffalse| command right before the driver and a |\fi| +% command right after it. This says to \LaTeX\ that the driver code is +% conditional material, and since the condition evaluates to false +% (|\iffalse| always evaluates to false), this conditional material +% should be skipped. Thus the first few lines of \texttt{myfile.dtx} +% typically might be +%\begin{verbatim} +%% \iffalse +%%<*driver> +%\documentclass{tclldoc} +%\begin{document} +%\DocInput{myfile.dtx} +%\end{document} +%% +%% \fi +%\end{verbatim} +% The \texttt{driver} guard lines are there to stop \package{docstrip} +% from including the driver code in the generated files. +% +% After the driver comes the actual \LaTeX\ document. The document +% usually consists of two parts, where the first part is a manual for +% the \emph{usage} of the code defined in the file, and the second part +% contains the actual \emph{implementation} (documented code). The idea +% is that most people are (at least the first time) quite content with +% learning how to use something, so one should make it simple for them +% to find that information.\footnote{One needn't take this as an absolute +% rule---I for one haven't written all my packages that way---but +% structuring the document like this generally makes it more accessible.} +% To further this approach one puts the command |\StopEventually| at the +% start of the implementation part and the command |\Finale| at the end +% of it. Normally |\StopEventually| doesn't make itself felt, but if one +% previously has given the command |\OnlyDescription| then rest of the +% file will not be read; this can be used to produce a ``manual only'' +% version of the documentation. |\StopEventually| takes one argument and +% the code in this argument is executed at the |\Finale| (if the +% implementation part is being included) or immediately (if the +% implementation part isn't being included). Thus this argument is the +% place to put things that should appear at the very end of the +% document. +% +% The \texttt{tcl}, \texttt{proc}, and \texttt{variable} environments +% described above all typically appear in the implementation part of the +% document. +% +% +% \section{Usage of commands and environments} +% \label{Sec:Manual} +% +% +% \subsection{The actual source code} +% +% The \DescribeEnv{tcl}\texttt{tcl} environment is used for wrapping up +% a group of code lines which contain \Tcl\ code. Lines inside this +% environment which begin with a percent character are called +% \emph{command} lines and can contain \LaTeX\ commands which get +% executed, whereas lines that do not begin with a percent character are +% called \emph{normal} lines and get typeset verbatim (or nearly +% verbatim). Lines that begin with |%<| (\package{docstrip} guard lines) +% do however constitute a special case, as the guard expression will get +% typeset as in \package{doc}'s \texttt{macrocode} environment and the +% remainder of the line will get processed in command mode if it is a +% |*| or |/| guard, but in normal mode if the guard line was of any other +% type. +% +% The \texttt{tcl} environment uses the same general formatting +% parameters as \package{doc}'s \texttt{macrocode} environment. In +% particular this means that the text on a normal line is typeset in +% |\MacroFont| (by default the same thing as \cs{normalfont}\penalty0 +% \cs{ttfamily}\penalty0\cs{small}) or |\AltMacroFont| (by default the same +% thing as \cs{normalfont}\penalty0\cs{ttfamily}\penalty0\cs{itshape}^^A +% \penalty0\cs{small}) depending on the current \package{docstrip} module +% nesting level. The \package{tclldoc} class sets the +% \texttt{StandardModuleDepth} counter to 1, which means that the +% |\AltMacroFont| is used when the modules are nested two levels deep or +% more. +% +% If a normal line is too long to fit on one line then the \texttt{tcl} +% environment will try to break it. Legal breakpoints are spaces which +% could be replaced by a backslash and a newline without changing the +% meaning of the command; thus most spaces are legal breakpoints. When a +% line is broken at a space like this, the space is replaced by a +% backslash so that the line is still ``syntactically correct''. The +% opposite happens to lines which actually end with an escaped newline; +% such lines are concatenated with the following line and are treated as +% one long line. This is so that a sequence of breakpoints can be chosen +% which is optimal for the actual line width of the document (as opposed +% to the line width used in the text file, which can be something quite +% different). For example +%\begin{verbatim} +%% \begin{tcl} +%lsearch -exact\ +% [concat $a [lrange $b $first end] c d e f]\ +% [lindex $g $h] +%% \end{tcl} +%\end{verbatim} +% could be typeset as any of the code examples in +% Figure~\ref{Fig:Radbrytning}, depending on how wide a line is. +% \begin{figure} +% \begin{center} +% \small +% \(\left\lvert +% \begin{minipage}{350pt} +% \begin{flushleft} +% |lsearch -exact [concat $a [lrange $b $first end] c d e f]|^^A +% | [lindex $g $h]| +% \end{flushleft} +% \end{minipage} +% \right\rvert\) +% +% \smallskip +% +% \(\left\lvert +% \begin{minipage}{298pt} +% \begin{flushright} +% \noindent |lsearch -exact [concat $a [lrange $b $first|^^A +% | end] c d e f]|\textbackslash\hfill\vadjust{}\\ +% |[lindex $g $h]| +% \end{flushright} +% \end{minipage} +% \right\rvert\) +% +% \smallskip +% +% \(\left\lvert +% \begin{minipage}{241pt} +% \begin{flushright} +% \noindent |lsearch -exact|\textbackslash\hfill\vadjust{}\\ +% |[concat $a [lrange $b $first end] c d e f]|\textbackslash\\ +% |[lindex $g $h]| +% \end{flushright} +% \end{minipage} +% \right\rvert\) +% +% \smallskip +% +% \(\left\lvert +% \begin{minipage}{184pt} +% \begin{flushright} +% \noindent |lsearch -exact [concat $a|\textbackslash\hfill +% \vadjust{}\\ +% |[lrange $b $first end] c d e f]|\textbackslash\\ +% |[lindex $g $h]| +% \end{flushright} +% \end{minipage} +% \right\rvert\) +% \end{center} +% +% \caption{The same \Tcl\ code, set in different linewidths} +% \label{Fig:Radbrytning} +% \end{figure} +% The environment tries to put the linebreaks at the lowest possible +% nesting (of braces and brackets) level; I believe this gives the best +% readability.\footnote{When I first started programming in \Tcl\ I +% used a completely different method for breaking long lines in the +% code---I mainly implemented the current method because it was +% simple to program---but I was quite surprised by how readable it made +% the code.} There is however a way to override this automatic choice +% of breakpoints: if a normal line which ends with an escaped newline is +% followed by a command line (even a command line that doesn't contain +% any commands) then it will not be concatenated with the next normal +% line. +% +% There are a couple of restrictions on the code in command lines. First +% of all it is not allowed to start a new paragraph (there will be an +% error message). Secondly a command may not be broken across several +% lines---all the arguments must appear on the same line as the +% control sequence. Thirdly some characters have other catcodes than +% in normal \LaTeX, so it is not certain that all commands work. Some +% commands that do work and may be useful are: +% \begin{itemize} +% \item Vertical space commands (|\smallskip|, |\medskip|, etc.) +% The command line `|% \medskip|' is more to type than a blank +% normal line, but it looks slightly better. +% \item Indexing commands (|\index|, |\IndexEntry|,\footnote{This +% command is defined by the \package{xdoc} package~\cite{xdoc}.} +% etc.) +% \item The \cs{TclInput} and |\settabsize| commands (see below). +% \end{itemize} +% And of course the |\end{tcl}| command works in a command line, since +% that is how one ends a \texttt{tcl} environment. +% +% Besides the \texttt{tcl} environment there is also a +% \DescribeEnv{tcl*}\texttt{tcl*} environment which is different from +% \texttt{tcl} only in that spaces and tabs are typeset as special +% visible space `\textvisiblespace' and visible tab +% `\makebox[6\fontdimen2\font]{\( - \mkern-7mu +% \cleaders\hbox{$\mkern-2mu - \mkern-2mu$}\hfill \mkern-7mu +% \mathord\rightarrow \mkern-1mu \vrule \mkern1mu\)}' characters. This +% can be useful for pieces of code where the exact number of spaces in +% a sequence is significant, such as code for writing tables that align. +% +% For shorter pieces of \Tcl\ code, e.g.\ examples, there's the +% \DescribeMacro{\tclverb}|\tclverb| command. |\tclverb| is very similar +% to the standard \LaTeX\ command |\verb|, but there are two differences. +% The first is that text typeset by |\tclverb| can contain breakpoints +% at whitespace; these behave just as in the \texttt{tcl} environment. +% The second is that the verbatim text that follows |\tclverb| may contain +% newlines, provided that these newlines are escaped by a backslash. Like +% a \Tcl\ interpreter, |\tclverb| ignores whitespace following an escaped +% newline. Unlike a \Tcl\ interpreter, |\tclverb| also ignores one percent +% character before the ignored whitespace, if it is the first character +% on the following line. Thus +%\begin{verbatim} +%% \tclverb|append a $b| is much more efficient than \tclverb|set a\ +%% $a$b| if \tclverb|$a| is long. +%\end{verbatim} +% is perfectly legal, and the escaped newline between |a| and |$a$b| is +% treated just like the space between |set| and |a|. Like |\verb|, the +% |\tclverb| command has a starred form \DescribeMacro{\tclverb*}^^A +% |\tclverb*| which also typesets spaces and tabs as visible characters. +% +% +% The \DescribeMacro{\MakeShortTclverb}|\MakeShortTclverb| command works +% just like the \package{doc}\slash\package{shortvrb} command +% |\MakeShortVerb|, except that it makes the active character a shorthand +% reference for |\tclverb|\textellipsis\ instead of |\verb|\textellipsis. +% Use |\DeleteShortVerb| to undo the effect of a |\MakeShortTclverb|. The +% \package{tclldoc} class executes the command +% \begin{quote} +% \verb"\MakeShortTclverb{\|}" +% \end{quote} +% which makes \verb"|" a shorthand for \verb"\tclverb|". +% +% Since there is no universally accepted standard for the size +% (equivalent number of spaces) of a tab, there is a command +% \DescribeMacro{\settabsize}|\settabsize| for changing this. +% |\settabsize| takes as its only argument the new tab size, which must +% be an integer in the range 2--255. The default value is 8. +% |\settabsize| makes a local assignment to the tab size. The tab size +% can be changed inside a \texttt{tcl} (or \texttt{tcl*}) environment. +% +% There is also a command \DescribeMacro{\TclInput}|\TclInput| which +% is used for typesetting ``raw'' (not in \texttt{.dtx} format) \Tcl\ code +% files. |\TclInput| is meant to be used on a \emph{command} line of a +% \texttt{tcl} or \texttt{tcl*} environment, and it efficiently makes +% things look as if the |\TclInput| command had been replaced by the +% inputted file in its entirety (preceded by a newline, and followed +% by a percent and a space). |\TclInput| takes as its only argument the +% name of the file to input. +% +% To typeset the file \texttt{myscript.tcl} one would write +%\begin{verbatim} +%% \begin{tcl} +%% \TclInput{myscript.tcl} +%% \end{tcl} +%\end{verbatim} \iffalse +% +% \fi +% or even +%\begin{verbatim} +% \begin{tcl}\TclInput{myscript.tcl}\end{tcl} +%\end{verbatim} +% anywhere in a \package{tclldoc} document. This works since the +% \texttt{tcl} environment is in command mode right after the initial +% |\begin{tcl}|, and the |\end{tcl}| needs not be the first command on a +% command mode line. +% +% +% \subsection{Markup of named things} +% +% The two environments \DescribeEnv{proc}\texttt{proc} and +% \DescribeEnv{variable}\texttt{variable}, which are analogues of +% \package{doc}'s \texttt{macro} environment, for procedures and +% variables respectively have already been mentioned in +% Section~\ref{Sec:Introduction}. In addition to those there are two +% environments \DescribeEnv{arrayentry}\texttt{arrayentry} and +% \DescribeEnv{arrayvar}\texttt{arrayvar} which are meant for entries +% in array variables and array variables as a whole. The complete +% syntaxes of these environments are +% \begin{quote} +% |\begin{proc}|\oarg{namespace}\marg{proc name}\\ +% \vadjust{}\quad$\vdots$\\ +% |\end{proc}| +% +% |\begin{variable}|\oarg{namespace}\marg{variable name}\\ +% \vadjust{}\quad$\vdots$\\ +% \quad|\end{variable}| +% +% |\begin{arrayentry}|\oarg{namespace}\marg{array name}^^A +% \marg{entry name}\\ +% \vadjust{}\quad$\vdots$\\ +% |\end{arrayentry}| +% +% |\begin{arrayvar}|\oarg{namespace}\marg{array name}^^A +% \oarg{index-des}\\ +% \vadjust{}\quad$\vdots$\\ +% |\end{arrayvar}| +% \end{quote} +% The \meta{proc name}, \meta{variable name}, and \meta{array name} +% arguments are quite evidently the names of the procedure, variable, +% and array respectively. The \meta{namespace} argument can be to +% specify the namespace part of a qualified name; having the name split +% like this makes it easier to treat the namespace differently from the +% rest of the qualified name. The command \DescribeMacro{\buildname} +% |\buildname| is used by the commands and environments described here to +% construct a qualified name from a namespace and a name. +% If there is no \meta{namespace} argument then the namespace used will +% be the default namespace. The default namespace is set using the +% \DescribeMacro{\setnamespace}|\setnamespace| command, which takes the +% namespace name as its only argument. The default namespace at the +% beginning of the document is the global namespace, whose name is the +% empty string. +% +% The \texttt{arrayentry} environment is intended for certain +% distinguished entries in an array, such as entries inserted to +% make the boundary cases of an algorithm work correctly and entries +% which have a special meaning to the program. Not all arrays contain +% such special entries, but when they do it is a good practice +% to explain them explicitly. The \meta{index-des} argument of the +% \texttt{arrayvar} environment can be used to specify what is used as +% index into the array; the text in this argument will appear both in +% the margin and in the index, but note that \meta{index-des} is a +% moving argument. There is little difference between the +% \texttt{variable} and \texttt{arrayvar} environments when the +% \meta{index-des} argument of the latter isn't used, but the index +% entries they make behave differently with respect to +% \texttt{arrayentry} index entries. An \texttt{arrayentry} index entry +% will be put as a subentry of the corresponding \texttt{arrayvar} +% entry, whereas a \texttt{variable} entry would appear separately. +% +% The above environments usually only appear in the implementation part +% of a \texttt{.dtx} file. For the usage part there is a command +% \DescribeMacro{\describestring}|\describestring| which produces +% marginal headings and index entries. The syntax of |\describestring| +% is +% \begin{quote} +% |\describestring|\oarg{type}\oarg{namespace}\marg{text} +% \end{quote} +% The \meta{text} is the string for which a heading and index entry will +% be made, whereas the \meta{type} (if given) is put after the text. +% If the \meta{namespace} is given then the thing described is supposed +% to be the name of something namespace-relative (like a procedure or +% global variable) and in this case the complete name is formed by +% passing \meta{namespace} and \meta{text} to |\buildname|. If +% \meta{type} is \texttt{proc}, \texttt{var.}, or \texttt{array} and a +% namespace is given then the index entry made will fit that made by +% a corresponding \texttt{proc}, \texttt{variable}, or +% \texttt{arrayvar} respectively environment. The \meta{type} argument +% is, in \LaTeX\ terminology, moving. +% +% The \meta{text} and \meta{namespace} arguments can contain arbitrary +% characters and most characters can be entered verbatim. Amongst the +% exceptions are `|%|', `|\|', `|{|', and `|}|', which instead can be +% entered as +% |\PrintChar|\discretionary{}{}{}|{`\%}|, +% |\PrintChar|\discretionary{}{}{}|{`\\}|, +% |\PrintChar|\discretionary{}{}{}|{`\{}|, and +% |\PrintChar|\discretionary{}{}{}|{`\}}| respectively. See the +% \package{xdoc} package~\cite{xdoc} documentation for an explanation of +% the |\PrintChar| command. The \meta{text} argument can also contain +% ``variant'' parts made using the \DescribeMacro{\meta}|\meta| command. +% As an example, +% \begin{quote} +% |\describestring[array]{\meta{mode}modeVars}| +% \end{quote} +% puts the text +% \begin{quote} +% \meta{mode}|modeVars| (array) +% \end{quote} +% in the margin and index. The arguments of such |\meta| commands are +% moving. +% +% A case which deserves special treatment is that of options of +% commands and for that there is the \DescribeMacro{\describeopt} +% |\describeopt| command. The syntax of this command is +% \begin{quote} +% |\describeopt|*\oarg{namespace}\marg{command}\oarg{type}^^A +% \marg{option} +% \end{quote} +% where \meta{command} is the command whose option is being described +% and \meta{option} is the name of the option. \meta{namespace} is the +% namespace of the \meta{command} and defaults to the global namespace. +% \meta{type} is the type of the command and defaults to |proc| for +% procedure. The \meta{namespace}, \meta{command}, and \meta{type} are +% (currently) only used in the index entry that is generated. Options +% for procedures will be put as subentries of the main procedure entry. +% For built-in commands it might be more appropriate to use |command| as +% \meta{type}, e.g. +% \begin{quote} +% |\describeopt{lsort}[command]{-real}| +% \end{quote} +% will put ``\texttt{-real} option'' in the margin and in the index as +% a subentry of ``\texttt{lsort} (command), global namespace''. The |*| +% is optional---including it will supress the marginal note normally +% generated by this command. +% +% Since \Tcl\ is often used together with C, it might be useful to +% also have something similar to the \texttt{proc} environment for C +% things. This is what the \DescribeEnv{Cfunction}\texttt{Cfunction}, +% \DescribeEnv{Cvariable}\texttt{Cvariable}, and \DescribeEnv{Ctype} +% \texttt{Ctype} environments are for. These take as their only +% argument the name of the identifier that is defined, e.g. +%\begin{verbatim} +% \begin{Cfunction}{main} +%\end{verbatim} +% Since there seems to be several schools on how C code should be +% formatted when typeset, the formatting of identifiers passed to these +% environments is configurable. The three commands +% \DescribeMacro{\Cfunctionidentifier}|\Cfunctionidentifier|, +% \DescribeMacro{\Cvariableidentifier}|\Cvariableidentifier|, and +% \DescribeMacro{\Ctypeidentifier}|\Ctypeidentifier| handle all +% typesetting of identifiers; each takes as its only argument the +% identifier (as a harmless string) to typeset. The default is to set +% the argument in italic; this is what CWEB does. +% +% If you are using the \texttt{C}\dots\ environments for identifiers +% whose names contain underscores (\_), you may want to pass the +% \describeoption{notrawchar}\texttt{notrawchar} to \package{tclldoc} +% (it is really an option of the \package{xdoc} package and will be +% passed on to that automatically). This option addresses a problem +% with \texttt{OT1}-encoded fonts that may cause underscores to display +% as a quite different character (the \texttt{cmtt} typewriter fonts are +% however not affected by this problem). +% +% +% +% \subsection{Describing command syntaxes} +% +% One important part of documentation is to describe the syntaxes of +% commands. The previous subsection contains examples of the conventions +% for this that has been developed for \LaTeX\ +% commands---mandatory arguments are denoted as `\marg{argument}' and +% optional arguments are denoted as `\oarg{argument}'. These two classes +% suffice rather well for \LaTeX\ commands, but the syntaxes of \Tcl\ +% commands are not seldom much more complex. Therefore a more powerful +% form of syntax specification is called for, and one which is close at +% hand is that used in regular expressions since it is already part of +% the \Tcl\ language anyway. +% +% \DescribeMacro\regopt +% \DescribeMacro\regstar +% \DescribeMacro\regplus +% The simplest commands available are the modifiers |\regopt|, +% |\regstar|, and |\regplus|, which correspond to the |?|, +% |*|, and |+| metacharacters in a regular expression; using |\regopt| +% after a term says that it is optional, |\regstar| says that the term +% can be repeated an arbitrary number of times (including zero), and +% |\regplus| says that the term occurs at least once. The typeset +% results of these commands are $^?$, $^*$, and $^+$ respectively +% (recall that exponents are sometimes used to denote repetition). +% +% The terminals in the expression are best made using |\tclverb| (for +% ``fixed'' material, e.g.\ procedure names) and +% \DescribeMacro{\word}|\word| (for variable material, e.g.\ +% arguments). The syntax of |\word| is +% \begin{quote} +% |\word|\marg{text} +% \end{quote} +% and e.g.\ |\word{script}| gets typeset as +% \begin{quote} +% \word{script} +% \end{quote} +% Using these, one can for example specify the syntaxes of the \Tcl\ +% commands |append| and |catch| through +% \begin{quote} +% \verb"|append| \word{var-name} \word{value}\regplus"\\ +% \verb"|catch| \word{script} \word{var-name}\regopt" +% \end{quote} +% (recall that `\verb"|"' is a shorthand for `\verb"\tclverb|"'). These +% get typeset as +% \begin{quote} +% |append| \word{var-name} \word{value}\(^+\)\\ +% |catch| \word{script} \word{var-name}\(^?\) +% \end{quote} +% +% Terms in regular expressions can also consist of parenthesised +% subexpressions, which are made using the \DescribeEnv{regblock}^^A +% \texttt{regblock} environment. The syntax of this environment is +% \begin{quote} +% |\begin{regblock}|\oarg{modifier}\quad\dots\quad |\end{regblock}| +% \end{quote} +% If \texttt{regblock} environments are nested then the parentheses of +% the outer environment will grow to be larger than those of the inner +% environment. A side-effect of this is that the \texttt{regblock} +% environment wants to know if a modifier will be applied to it, since +% the amount by which the modifier should be raised in this case depends +% on the size of the parenthesis before it, and this is what the +% \meta{modifier} optional argument is for. \LaTeX\ does not provide +% for arguments at the |\end| of an environment, so it has to be placed +% at the |\begin|. Using these elements, the syntax of |if| can be +% specified through +% \begin{quote} +% \verb"|if| \word{expression} |then|\regopt\ \word{script}"\\ +% \verb"\begin{regblock}[\regstar]|elseif| \word{expression}"\\ +% \verb"|then|\regopt\ \word{script}\end{regblock}"\\ +% \verb"\begin{regblock}[\regopt]|else| \word{script}\end{regblock}" +% \end{quote} +% which typesets as +% \begin{isyntax} +% |if| \word{expression} |then|\(^?\) \word{script} \(\bigl(\)^^A +% |elseif| \word{expression} |then|\(^?\) \word{script}\(\bigr)^*\) +% \(\bigl(\)|else|~\word{script}\(\bigr)^?\) +% \end{isyntax} +% In versions of \package{tclldoc} before 2.40, the \texttt{regblock} +% environment used to be called \DescribeEnv{regexp}\texttt{regexp}. +% That other name is still supported, but should be avoided in new +% documents. +% +% The final regular expression construction that is supported is that +% of branches of a regular expression. A \texttt{regblock} environment +% consist of one or several branches that are separated by +% \DescribeMacro{\regalt}|\regalt| commands. Visually the |\regalt| +% command gets typeset as a vertical bar that has the same size as the +% parentheses of the surrounding \texttt{regblock} environment. The +% |\regalt| command may only be used inside a \texttt{regblock} +% environment. +% An example of the use of |\regalt| is the following specification of +% the syntax of \Tcl's |regexp| command: +% \begin{quote} +% \verb"|regexp| \begin{regblock}[\regstar]|-nocase|\regalt"\\ +% \verb"|-indices|\end{regblock} |--|\regopt \word{regular expression}"\\ +% \verb"\word{string} \word{var-name}\regstar" +% \end{quote} +% which typesets as +% \begin{isyntax} +% |regexp| \((\)|-nocase|~\(\mid\) |-indices|\()^*\) |--|\(^?\) +% \word{regular expression} \word{string} \word{var-name}\(^*\) +% \end{isyntax} +% +% Finally a note about the relationship between the |\word| command and +% \package{doc}'s |\meta| command. Whereas the argument of |\word| is +% encapsulated in braces (and thus ought to be a separate word for a +% \Tcl\ interpreter), the argument of |\meta| is encapsulated in angle +% brackets. The idea is that |\word| should be used for things which are +% separate words to \Tcl, whereas |\meta| should be used for things which +% corresponds to parts of words or to several words. Thus in the command +% |set b Z${a}Y|, the second word |b| would be a `\word{var-name}' and +% the third word |Z${a}Y| would be a `|Z|\meta{string}|Y|'. In the +% command |label .a -text "Hello there!"|, the last two arguments could +% be summarised as an \meta{option}, but not as an \word{option}. +% +% +% \subsection{Non-ASCII characters} +% +% One problem, which is only going to be more common in the future, is +% how to deal with non-ASCII characters in scripts. The main problem +% here lies not on the output side, as \LaTeX\ is actually pretty good +% at producing a requested character as long as it is available in some +% font, but on the input side. \LaTeX\ can handle input in most 8-bit +% encodings, but in order for that to work the file must contain an +% |\inputencoding| command which tells \LaTeX\ which encoding is being +% used. As transporting a file from one platform to another most likely +% changes the encoding, but not the argument of |\inputencoding|, this +% method is rather fragile. Certainly there is room for improvements +% but the world of 8-bit encodings is generally such a mess anyway that +% it probably isn't worth the effort. +% +% A more progressive approach is to decide that all source code is in +% Unicode (more precisely in UTF-8). The main arguments for this are: +% (i)~\Tcl\ uses Unicode internally, (ii)~it is equally foreign on +% all platforms and can be treated as binary data rather than ``extended +% ASCII'' text, and (iii)~since it isn't converted, there is no loss of +% data. Interestingly enough, \emph{it is possible to use UTF-8 ``out of +% the box'' today!} Using the \package{ucs} package~\cite{ucs-package} +% allows \LaTeX\ to interpret UTF-8 input and this works just as well +% for the \Tcl\ code in a \texttt{tcl} environment as for the normal +% \LaTeX\ text outside it. If \package{docstrip} is run on a \LaTeX\ +% format\footnote{Or, in some implementations, the \TeX\ program gets a +% suitable option.} that preserves characters whose most significant +% bit is set\footnote{Rather than converting them to +% \texttt{\^{}\^{}}-sequences, which is the default.} then the non-ASCII +% characters are simply copied verbatim and it makes no difference that +% they may occupy more than one byte of data. Alternatively one can run +% \package{docstrip} on Omega and (with a little extra work) get the +% ability to have the \Tcl\ code translated to some other encoding as +% the files are being generated!\footnote{At least in theory; I have to +% admit I haven't actually tested the \package{docstrip} part of it.} +% +% But although the above paragraph describes the way to go in the long +% run, there are some matters which make this approach slightly +% unfeasible in the near future. This is of course my own subjective +% opinion, but I find that two good reasons not to start using Unicode +% throughout quite yet are that (i)~my favourite text editor doesn't +% support Unicode (yet) and (ii)~even if I do start using it, there +% wouldn't be that much people around who could make sense of such files +% if I were to send it to them. Therefore I \emph{intend} to implement, +% but as yet haven't, a kind of intermediate format where non-ASCII +% Unicode characters are encoded using only ASCII characters plus an +% extra escape character. The basic idea is simply that any string +% `\meta{escape}\meta{hex digits}\meta{escape}' should be interpreted as +% the Unicode character \texttt{U+}\meta{hex digits}, so that arbitrary +% Unicode characters can be encoded using a character set that only +% comprises ASCII plus one extra \meta{escape} character. Supposing that +% this \meta{escape} character is the centered dot `\textperiodcentered', +% I could then encode my name as +% \begin{quote} +% \texttt{Lars Hellstr\swtpc 00f6\swtpc m} +% \end{quote} +% whereas the centered dot itself would be \texttt{\swtpc 00b7\swtpc}. +% The idea is that the file itself should contain the declaration of +% which character is used as this Unicode escape, so that a change due +% to translation from one 8-bit encoding to another will identically +% alter both declaration and use of the escape character, thereby +% preserving the internal logic of the file. +% +% The weak point with this scheme is that \package{docstrip} would have +% to translate the escape sequences to proper characters when it +% generates files. Implementing that under \TeX\ is highly non-trivial. +% It can be done with a reasonable effort under Omega, but it still +% requires hacking \package{docstrip}. The really interesting approach +% would however be to implement it in a port of \package{docstrip} to +% \Tcl, as that would remove the need to have \TeX\ to install the files. +% Porting \package{docstrip} to \Tcl\ is by the way a project of mine +% which I unfortunately haven't spent much time on, but if it is to be +% of any use to have the Unicode escape format described above +% implemented in \package{tclldoc} then I will have to make some +% progress with it. +% +% \medskip +% +% One rather recent advancement in this direction is the code in +% \texttt{sourcedtx.tcl}, which can be generated from the file +% \texttt{sourcedtx.dtx} that is distributed with \package{tclldoc} as +% an example. This implements a \Tcl\ command |dtx::source| that makes +% it possible to source \Tcl\ code in a \texttt{.dtx} file without +% docstripping it to a file first. This code does currently not bother +% about encodings, but that is easy enough to add. +% +% \medskip +% +% Finally, a few notes on the old mechanism for non-ASCII characters +% that is included in the \package{tcldoc} compatibility class. It +% cooperates with the \package{rtkinenc} package~\cite{rtkinenc}, when +% that has been loaded, in order to detect when an input character isn't +% avaiable in any active font encoding. Rather than raising an error and +% printing nothing it these cases, missing characters are written as the +% corresponding |\x|\meta{hh} backslash sequence in a slightly different +% font than the rest of the text. A problem here is however that most +% input encodings contain a few characters which are interpreted as +% \emph{math} character by \LaTeX. When such a character appears on a +% code line it makes \TeX\ switch to math mode and things generally get +% quite messy afterwards. +% +% The cure for this is to redeclare these input characters to \LaTeX\ +% so that they work as intended in text mode, but that does take some +% lines of code. The \package{tcldoc} class does contain the +% declarations needed for the \texttt{applemac} input encoding; passing +% it the \describeoption{macinputenc}\texttt{macinputenc} option will +% load the \package{rtkinenc} package, the \texttt{applemac} (macRoman) +% input encoding, the \texttt{TS1} output encoding, and make the +% necessary redeclarations to allow all input characters to work in +% text. As nothing is provided for any other input encoding however, +% this solution never was a good solution. The \texttt{macinputenc} +% should be considered as unsupported as of \package{tclldoc} v\,2.30. +% +% +% +% \subsection{Options and customisation} +% +% The \package{tclldoc} package does not have any options of its own, but +% all options passed to it are passed on to the \package{xdoc} package. +% The \package{tclldoc} class accepts all options that the standard +% \LaTeX\ document class \package{article} accepts, with the exception +% of \texttt{a5paper}. +% +% Like the \package{ltxdoc} class, the \package{tclldoc} class will look +% for a special configuration file \texttt{tclldoc.cfg} and input that +% file if it exists. This file can be used to declare extra options for +% the class, have certain options always given, etc. Section~2 of +% \texttt{ltxdoc.dtx}~\cite{ltxdoc} is a good introduction to how such +% configuration files can be used with \texttt{.dtx} sources in general. +% +% When you use a \texttt{tclldoc.cfg} file to customise the +% \package{tclldoc} document class, you affect how all documents using +% that class will be typeset in your particular \TeX\ installation. It +% is \emph{not} something you have to do, but it can +% make \package{tclldoc} documents work better with the printers, paper +% formats, fonts, etc.\ that are available in your installation. It +% will usually cause line and page breaks to occur at other places than +% they would do if typeset using an uncustomised \package{tclldoc} +% class, so the typographical quality of the document can be decreased, +% but it is uncommon to find an \texttt{.dtx} document whose author +% have given these matters much attention anyway. Hence the typographic +% arguments against customisation are weak. +% +% A common form of customisation is to use additional packages, since +% various kinds of document-wide font selection is often done by +% packages. Due to that the code in \texttt{tclldoc.cfg} is executed when +% the \package{tclldoc} class does its option processing, at which time +% \LaTeX\ does not allow loading packages, such customisation is not +% straightforward. There is a way around that however; to load e.g.\ the +% \package{times} package, use the command +%\begin{verbatim} +%\AtEndOfClass{\usepackage{times}} +%\end{verbatim} +% Using |\AtEndOfClass| like this delays the command until it may be +% executed. +% +% +% \subsection{Miscellanea} +% +% For writing ``\Tcl'', the \package{tclldoc} package defines the command +% \DescribeMacro{\Tcllogo}|\Tcllogo|, which for most fonts look slightly +% better than simply typing |Tcl|. (|\Tcllogo| becomes \Tcl, whereas +% |Tcl| becomes Tcl.) +% +% Between the namespace and the tail part of a qualified name, the +% \package{tclldoc} package commands naturally put the name\-space +% separator `|::|'. This text is stored in the macro +% \DescribeMacro{\namespaceseparator}|\namespaceseparator|, which can be +% redefined using the |\Declare|\-|Robust|\-|Command| command. This is +% mainly useful for modifying how this separator behaves with respect to +% line breaking; the default behaviour is that a line break can occur +% between the colons. +% +% Another configurable piece of text is stored in the +% \DescribeMacro{\namespacephrase}|\namespacephrase| macro. This +% contains word `namespace' as that appears in index entries, e.g.~in +% the last word of +% \begin{quote} +% \texttt{platform} (var.), \texttt{alpha} name\-space +% \end{quote} +% It is often convenient to replace this by something shorter. The +% redefinition +% \begin{quote} +% |\renewcommand{\namespacephrase}{\textsc{ns}}| +% \end{quote} +% turns the above into +% \begin{quote} +% \texttt{platform} (var.), \texttt{alpha} \textsc{ns} +% \end{quote} +% Note however that either |\namespacephrase| itself or its expansion +% must be robust. +% +% +% \section{Acknowledgements} +% +% The \package{tclldoc} document class and \LaTeX\ package were +% constructed starting from three other sources: (i)~the \package{ltxdoc} +% document class~\cite{ltxdoc} by David Carlisle, +% (ii)~the \package{doc} package~\cite{doc} by Frank Mittelbach, +% B.~Hamilton Kelly, Andrew Mills, Dave Love, and Joachim Schrod, and +% (iii)~my own \package{pasdoc} document class. Hence the `et al.'\ in +% the author field above. This complicated heritage in the code is +% mirrored by the documented source---there are paragraphs below that +% are rather about one of (i)--(iii), than about \package{tclldoc}. +% +% +% +% \StopEventually{ +% \begin{thebibliography}{9} +% \bibitem{ltoutenc} +% Johannes Braams, David Carlisle, Alan Jeffrey, Frank +% Mittelbach, Chris Rowley, and Rainer Sch\"opf: +% \textit{ltoutenc.dtx}, The \LaTeX3 Project; +% \textsc{ctan}:\discretionary{}{}{\thinspace}\texttt{macros}\slash +% \texttt{latex}\slash \texttt{base}\slash \texttt{ltoutenc.dtx}. +% \bibitem{ltxdoc} +% David Carlisle: +% \textit{The file \texttt{ltxdoc.dtx} for use with \LaTeXe}, +% The \LaTeX3 Project; +% \textsc{ctan}:\discretionary{}{}{\thinspace}\texttt{macros}\slash +% \texttt{latex}\slash \texttt{base}\slash \texttt{ltxdoc.dtx}. +% \bibitem{rtkinenc} +% Lars Hellstr\"om: +% \textit{The \package{rtkinenc} package}, 2000; +% \textsc{ctan}:\discretionary{}{}{\thinspace}\texttt{macros}\slash +% \texttt{latex}\slash \texttt{contrib}\slash +% \texttt{supported}\slash \texttt{rtkinenc}\slash +% \texttt{rtkinenc.dtx}. +% \bibitem{xdoc} +% Lars Hellstr\"om: +% \textit{The \package{xdoc} package --- experimental +% reimplementations of features from \package{doc}, +% second~prototype}, 2000, 2001; +% \textsc{ctan}:\discretionary{}{}{\thinspace}\texttt{macros}\slash +% \texttt{latex}\slash \texttt{exptl}\slash \texttt{xdoc}\slash +% \texttt{xdoc2.dtx}. +% \bibitem{docindex} +% Lars Hellstr\"om: +% \textit{The \package{docindex} package}, 2001; +% \textsc{ctan}:\discretionary{}{}{\thinspace}\texttt{macros}\slash +% \texttt{latex}\slash \texttt{exptl}\slash \texttt{xdoc}\slash +% \texttt{docindex.dtx}. +% \bibitem{TeXbook} +% Donald E.\ Knuth, Duane Bibby (illustrations): +% \textit{The \TeX book}, Ad\-di\-son--Wes\-ley, 1984; +% ISBN~0-201-13448-9 and~0-201-13447-0. +% \bibitem{docstrip} +% Frank Mittelbach, Denys Duchier, Johannes Braams, Marcin +% Woli\'nski, and Mark Wooding: \textit{The \textsf{DocStrip} +% program}, The \LaTeX3 Project; +% \textsc{ctan}:\discretionary{}{}{\thinspace}\texttt{macros}\slash +% \texttt{latex}\slash \texttt{base}\slash \texttt{docstrip.dtx}. +% \bibitem{doc} +% Frank Mittelbach, B.~Hamilton Kelly, Andrew Mills, Dave Love, and +% Joachim \mbox{Schrod}: \textit{The \package{doc} and +% \package{shortvrb} Packages}, The \LaTeX3 Project; +% \textsc{ctan}:\discretionary{}{}{\thinspace}\texttt{macros}\slash +% \texttt{latex}\slash \texttt{base}\slash \texttt{doc.dtx}. +% \bibitem{execMagic} +% ``Tom'', Donal Fellows, Larry Virden, Richard Suchenwirth: +% \textit{exec magic}, The \Tcl'ers Wiki page \textbf{812}; +% \textsc{http}:/\slash \texttt{mini.net}\slash \texttt{tcl}\slash +% \texttt{812.html}. +% \bibitem{ucs-package} +% Dominique~P.~G.~Unruh: +% \textit{\texttt{ucs.sty} - Unicode Support}, 2000; +% \textsc{ctan}:\discretionary{}{}{\thinspace}\texttt{macros}\slash +% \texttt{latex}\slash \texttt{contrib}\slash +% \texttt{supported}\slash \texttt{unicode}/. +% \end{thebibliography} +% \begin{flushleft}\footnotesize +% The ``\textsc{ctan}:'' above is short for ``any of the servers +% in the Comprehensive \TeX\ Archive Network (or mirror thereof)''. +% You get a working URL if you replace this by e.g.\ +% ``\texttt{ftp://ftp.tex.ac.uk/tex-archive/}''.% +% \end{flushleft} +% } +% +% +% \changes{v\,1.90}{2000/01/02}{Took a copy of \texttt{pasdoc.dtx} and +% started modifying it into \texttt{tcldoc.dtx}. (LH)} +% +% \section{Initial stuff in the \package{tclldoc} package} +% +% The |\NeedsTeXFormat| and |\ProvidesPackage| commands for the +% \package{tclldoc} package appear at the top of \texttt{tclldoc.dtx} (not +% shown in the typeset document). Apart from that the only initial +% action required is to load the \package{xdoc} package (and thereby +% the \package{doc} package) and the \package{docindex} (or rather the +% \LaTeXe\ version \package{docidx2e}) package. +% \SortIndex{docindex package}{\package{docindex} package} +% +% \changes{v\,2.23}{2001/07/21}{Added message about using +% docindex.ist. (LH)} +% \begin{macrocode} +%<*pkg> +\RequirePackageWithOptions{xdoc2}[2001/11/03] +\RequirePackage{docidx2e} +\AtEndDocument{% + \typeout{********************************}% + \typeout{* Use docindex.ist when\@spaces\@spaces*}% + \typeout{* sorting .idx and .glo files. *}% + \typeout{********************************}% +}% +% \end{macrocode} +% +% +% \section{Verbatim typesetting of \Tcl\ code} +% +% \changes{v\,2.10}{2000/10/03}{Major overhaul of the \texttt{tcl} +% environment, to make it possible to assign different penalties to +% different breakpoints. (LH)} +% +% The main feature in the \package{tclldoc} package is the \texttt{tcl} +% environment, which typesets embedded code in a way more adapted to +% \Tcl\ code than what the \texttt{macrocode} environment does. +% +% +% \subsection{Beginning of line processing} +% +% The organization of the text in \emph{lines} is crucial for its +% interpretation by the \texttt{tcl} environment. In particular the +% following features rely on being at the beginning of a line: +% \begin{itemize} +% \item indentation of \Tcl\ code, +% \item invoking command mode, +% \item recognizing \package{docstrip} guards. +% \end{itemize} +% +% \begin{macro}{\TD@percent@token} +% A control sequence |\let| to a |%|$_{12}$, mainly for use in |\ifx| +% comparisons. +% \DoNotIndex{\%} +% \begin{macrocode} +\begingroup + \catcode`\%=12 + \global\let\TD@percent@token=% +\endgroup +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\TD@start@n@line} +% \begin{macro}{\TD@start@n@line@} +% \begin{macro}{\TD@start@n@line@i} +% The macro |\TD@start@n@line| is used at the beginning of every line. +% The macro first clears the |\TD@line@indent| and |\TD@nesting@level| +% registers and the |\TD@nesting@stack| stack. Then it checks if the +% next character is a percent. If it is then this is a command line. +% If it isn't then it is a normal line and it is time to start a +% codeline paragraph and count the character indentation. +% +% |\TD@|\-|start@|\-|n@|\-|line@| is the macro which actually does +% these things; |\TD@|\-|start@|\-|n@|\-|line| is usually |\let| to +% |\TD@|\-|start@|\-|n@|\-|line@|. When a file is being |\TclInput|ed +% however, |\TD@|\-|start@|\-|n@|\-|line| is |\let| to +% |\TD@|\-|start@|\-|n@|\-|line@i| which peeks at the next token +% before calling |\TD@|\-|start@|\-|n@|\-|line@|. If it didn't, +% |\TD@|\-|start@|\-|n@|\-|line@| would have a problem at the end of +% the file, since the end of a file counts as being |\outer|. +% \begin{macrocode} +\def\TD@start@n@line@#1{% + \global\TD@line@indent=\z@ + \TD@nesting@level=\z@ + \def\TD@nesting@stack{\TD@nesting@level}% + \ifx #1\TD@percent@token + \expandafter\TD@module + \else + \global\advance \c@codelineno \@ne + \TD@begin@tclpar + \expandafter\TD@count@indent \expandafter#1% + \fi +} +% \end{macrocode} +% \begin{macrocode} +\let\TD@start@n@line=\TD@start@n@line@ +\def\TD@start@n@line@i{\futurelet\next\TD@start@n@line@} +% \end{macrocode} +% \end{macro}\end{macro}\end{macro} +% +% +% \begin{macro}{\TD@count@indent} +% The purpose of the |\TD@count@indent| macro is to compute the +% indentation of the current line. It grabs the next character, and +% as long as that is a space or tab it will just increment +% |\TD@line@indent| accordingly and continue with the next character. +% |!| and |"| are used to make space and tab respectively with +% suitable catcode. +% \DoNotIndex{\!,\",\ ,\^} +% \begin{macrocode} +\begingroup + \catcode`\!=\active + \catcode`\"=\active + \lccode`\!=`\ % + \lccode`\"=`\^^I% + \lowercase{% +\endgroup + \def\TD@count@indent#1{% + \ifx !#1% + \global\advance \TD@line@indent \@ne + \expandafter\TD@count@indent + \else \ifx "#1% + \global\divide \TD@line@indent \TD@tab@size + \global\advance \TD@line@indent \@ne + \global\multiply \TD@line@indent \TD@tab@size + \expandafter\expandafter \expandafter\TD@count@indent + \else + \TD@setup@parshape + \expandafter\expandafter \expandafter#1% + \fi\fi + }% +} +% \end{macrocode} +% \end{macro} +% +% +% \begin{macro}{\TD@module} +% When command mode has been properly established it is time to +% check if the next character is |<|, in which case the following +% \package{docstrip} guard must be processed. |\TD@command| must +% appear before the |\futurelet| so that the following character +% won't get tokenized with incorrect catcodes. +% \changes{v\,2.13}{2000/12/20}{Always check whether the current line +% is a guard line, since the \texttt{\PrintChar{\number`\<}} in +% the guard would otherwise start a new paragraph, which is an +% error. (LH)} +% \begin{macrocode} +\def\TD@module{% + \TD@command + \futurelet\next + \TD@ch@angle +} +% \end{macrocode} +% \end{macro} +% +% +% \begin{macro}{\TD@ch@angle} +% The module checking in \package{tclldoc} takes advantage of the +% macros for this in \package{doc}---|\ch@plus@etc| is a \package{doc} +% macro. |<| is active because it is in the nolig list +% (|\verbatim@|\-|nolig@|\-|list|). +% \changes{v\,2.20}{2001/03/12}{Moved \cs{TD@normal} previously in +% \cs{TD@pm@module} to here. This means \package{docstrip} guard are +% now read in normal mode rather than command mode. This change +% was made because of grouping problems that occured on non-block +% guard lines. Also added local redefinition of \PrintChar{13} to +% prevent that guard lines are concatenated with the following +% line. (LH)} +% \DoNotIndex{\^} +% \begin{macrocode} +\begingroup + \catcode`<=\active + \catcode`\^^I=\catcode`\^^M \endlinechar=`\^^I \relax + \catcode`\^^M=\active + \gdef\TD@ch@angle{% + \ifx <\next + \TD@normal + \global\advance \c@codelineno \@ne + \TD@begin@tclpar + \def^^M{\TD@active@CR}% + \expandafter\futurelet \expandafter\next + \expandafter\ch@plus@etc + \fi + } +\endgroup +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\TD@pm@module} +% The |\TD@pm@module| macro is used instead of \package{doc}'s +% |\pm@module| when in the \texttt{tcl} environment. This is necessary +% since the paragraph man\oe uvres of the \texttt{tcl} environment would +% not work right with \package{doc}'s |\pm@module|. +% +% If we're not dealing with a block directive (|*| or |/|), i.e., +% it's a single special line, we set everything up to the next |>| +% appropriately, return to normal mode, possibly change font, and +% start counting indentation spaces. +% \DoNotIndex{\>} +% \begin{macrocode} +\begingroup + \catcode`\>=\active + \gdef\TD@pm@module#1>{% + \Module{#1}% + \ifnum \guard@level<\c@StandardModuleDepth \else + \AltMacroFont + \fi + \TD@count@indent + } +\endgroup +% \end{macrocode} +% \end{macro} +% +% +% \begin{macro}{\TD@star@module} +% \changes{v\,2.20}{2001/03/12}{Removed \cs{TD@end@tclpar}. (LH)} +% \changes{v\,2.22}{2001/06/13}{Reinserted \cs{TD@end@tclpar} and +% added \cs{TD@command}; this solves a problem with grouping that +% prevented a font change from getting effect. (LH)} +% \begin{macro}{\TD@slash@module} +% \changes{v\,2.20}{2001/03/12}{Removed \cs{TD@end@tclpar}. (LH)} +% \changes{v\,2.22}{2001/06/13}{Same change as in +% \cs{TD@star@module}. (LH)} +% The |\TD@star@module| and |\TD@slash@module| macros are, like +% |\TD@pm@module|, used instead of \package{doc}'s |\star@module| and +% |\slash@module| respectively when in the \texttt{tcl} environment. +% \DoNotIndex{\>} +% \begin{macrocode} +\begingroup + \catcode`\>=\active + \gdef\TD@star@module#1>{% + \Module{#1}% + \TD@end@tclpar + \TD@command + \global\advance \guard@level \@ne + \ifnum \c@StandardModuleDepth<\guard@level + \global\let\macro@font=\AltMacroFont + \macro@font + \fi + } + \gdef\TD@slash@module#1>{% + \Module{#1}% + \TD@end@tclpar + \TD@command + \global\advance \guard@level \m@ne + \ifnum \guard@level=\c@StandardModuleDepth + \global\let\macro@font\MacroFont + \macro@font + \fi + } +\endgroup +% \end{macrocode} +% \end{macro}\end{macro} +% +% +% \begin{macro}{\TD@gobble@whitespace} +% This macro gobbles all normal mode spaces and tabs following it. +% \begin{macrocode} +\def\TD@gobble@whitespace#1{% + \if \ifx #1\TD@active@space + 0% + \else + \ifx #1\TD@active@tab 0\else 1\fi + \fi 0% + \expandafter\TD@gobble@whitespace + \else + \expandafter#1% + \fi +} +% \end{macrocode} +% \end{macro} +% +% +% +% \subsection{Typographical elements} +% +% This subsection contains definitions of the typographical +% elements---special symbols and the like--that are used in the +% \texttt{tcl} and \texttt{tcl*} environments. +% +% +% \begin{macro}{\TD@typography} +% \begin{macro}{\TD@nontcl@font} +% The |\TD@typography| macro does all the typographic set-up that is +% common to the \texttt{tcl} environment, the \texttt{tcl*} +% environment, and the |\tclverb| command. +% +% The |\TD@nontcl@font| macro selects an ``escape'' font for \Tcl\ +% material. This font is used for various kinds of escape codes that +% are typeset instead of some character---either because the +% character is not available, or because the line needs to be broken. +% The escape font will have the same font family (and same encoding) +% as the text surrounding the \texttt{tcl} environment or |\tclverb| +% command. Therefore |\TD@nontcl@font| must be defined before the +% \Tcl\ material font is selected. I don't think it is necessary to +% use |\protected@edef| below, but it can't hurt either. +% \begin{macrocode} +\def\TD@typography{% + \protected@edef\TD@nontcl@font{% + \noexpand\fontencoding{\cf@encoding}% + \noexpand\fontfamily{\f@family}\noexpand\selectfont + }% +% \end{macrocode} +% The most frequent escape symbol is the single backslash used for +% escaping the end of lines, and this is stored in the +% |\TD@backslash@box| box. +% +% The following code have to do with how characters outside +% visible ASCII are typeset when they appear in the \texttt{tcl} or +% \texttt{tcl*} environment. The commands are defined by the +% \package{rtkinenc} package. +% \begin{macrocode} + \InputModeCode + \SetUnavailableAction{\leavevmode{% + \TD@nontcl@font\textbackslash x\TypesetHexNumber{##1}% + }}% + \DeclareInputMath{0}{\RIE@undefined{0}}% + \DeclareInputMath{12}{\RIE@undefined{12}}% +} +% \end{macrocode} +% \end{macro}\end{macro} +% +% \begin{macro}{\TD@typeset@space} +% This macro typesets a space, as it should be in the \texttt{tcl} +% environment (and other places too). It generates a kern, not glue, so +% that it can be used in a |\discretionary|. +% \begin{macrocode} +\def\TD@typeset@space{\kern\fontdimen\tw@\font} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\TD@discretionary@space} +% This is the macro which offers all the breakpoints in |\tclverb| +% code. The name comes from the fact that all the breakpoints are +% discretionaries which appear to be spaces when not broken. When +% they are broken, they are backslashes. To give some visual hint +% that these backslashes need not be backslashes in the source, they +% are typeset in the same font family (and same encoding) as the +% text surrounding the \texttt{tcl} environment. +% \begin{macrocode} +\def\TD@discretionary@space{% + \discretionary{\copy\TD@backslash@box}{}{\TD@typeset@space}% +}% +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\TD@breakable@space} +% The valid breakpoints inside \texttt{tcl} and \texttt{tcl*} +% environments are made in the |\TD@|\-|breakable@|\-|space| macro. +% This macro contributes an |\hbox| (which contains a +% |\TD@typeset@space|), a penalty, and an empty |\vadjust| to the +% current horizontal list. If a line break is made at the penalty, +% the |\hbox| will later be removed and replaced by a non-macro font +% backslash (from |\TD@backslash@box|). The |\vadjust| is there to +% prevent that any discardable items disappears after the breakpoint. +% \begin{macrocode} +\def\TD@breakable@space{% + \hbox{\TD@typeset@space}% + \TD@nesting@penalty + \vadjust{}% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\TD@typeset@tab} +% This is an unbreakable space whose width equals that of +% |\TD@tab@size| spaces. +% \begin{macrocode} +\def\TD@typeset@tab{\kern\TD@tab@size\fontdimen\tw@\font} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\TD@visible@whitespace} +% The |\TD@visible@whitespace| macro locally redefines the macros +% which typesets spaces and tabs to make visible symbols. The visible +% tab is roughly a |\rightarrowfill| and a |\vrule|, but a difference +% is that the minuses that form the extensible ``tail'' to the +% |\rightarrow| are not smashed. Not doing that gives the |\vrule| a +% suitable height, but I had rather expected the nominal height of +% the |\rightarrow| to be larger \textellipsis\ It's yet another +% mysterious math font feature, I suppose. +% +% \begin{macrocode} +\def\TD@visible@whitespace{% + \def\TD@typeset@space{\char32 }% + \def\TD@typeset@tab{% + \hb@xt@\TD@tab@size\fontdimen\tw@\font{% + $\m@th-\mkern-7mu% + \cleaders\hbox{$\mkern-2mu-\mkern-2mu$}\hfill + \mkern-7mu\mathord\rightarrow\mkern-1mu\vrule\mkern1mu$% + }% + }% +} +% \end{macrocode} +% \end{macro} +% +% +% \subsection{Catcodes and active characters} +% +% Like any \texttt{verbatim}-like environment, the \texttt{tcl} +% environment does some rather extensive alterations of catcodes. This +% is handled by the three macros |\TD@general|, |\TD@normal|, and +% |\TD@command|. Set-up that is common for both modes is made by +% |\TD@general| when the \texttt{tcl} environment is entered. +% Mode-dependent set-up is made by |\TD@normal| and |\TD@command| +% whenever a mode switch occurs. +% +% \begin{macro}{\TD@let@active} +% One technicality here is that all of these routines need to set the +% definition of some active character, and this character is often not +% active with normal \LaTeX\ catcodes. To overcome this difficulty, +% there is a macro |\TD@let@active| which takes two arguments: a +% control sequence with a single character name, and an arbitrary +% control sequence. The active character with the same name as the +% first control sequence is |\let| locally to the second control +% sequence. +% \begin{macrocode} +\begingroup + \catcode\z@=\active + \gdef\TD@let@active#1{% + \begingroup + \lccode\z@=`#1% + \lowercase{% + \endgroup + \let^^@% + }% + }% +\endgroup +% \end{macrocode} +% \end{macro} +% +% +% \begin{macro}{\TD@general} +% The |\TD@general| macro does the initial set-ups that are common for +% both modes of the \texttt{tcl} environment and for the |\tclverb| +% command. These include definitions of various active characters and +% |\catcode| assignments. +% +% \DoNotIndex{\^,\ ,\\,\[,\]} +% \DoNotIndexHarmless{\PrintChar{`\{}} +% \DoNotIndexHarmless{\PrintChar{`\}}} +% \begin{macrocode} +\def\TD@general{% + \let\do\do@noligs + \verbatim@nolig@list + \let\do\@makeother + \dospecials + \catcode`\^^M=\active + \TD@let@active\^^I\TD@active@tab + \TD@let@active\^^M\TD@active@CR + \TD@let@active\ \TD@active@space + \TD@let@active\\\TD@active@backslash + \TD@let@active\{\TD@active@braceleft + \TD@let@active\}\TD@active@braceright + \TD@let@active\[\TD@active@bracketleft + \TD@let@active\]\TD@active@bracketright +} +% \end{macrocode} +% \end{macro} +% +% +% \begin{macro}{\TD@normal} +% This macro sets up the normal mode, changing everything that is +% different in any of the other modes. Note that the catcodes in +% normal mode are such that no character is a space. This makes it +% possible for macros used in normal mode to look ahead by simply +% grabbing the next character in an undelimited argument. +% \DoNotIndex{\^,\ ,\\,\[,\]} +% \DoNotIndexHarmless{\PrintChar{`\{}} +% \DoNotIndexHarmless{\PrintChar{`\}}} +% \begin{macrocode} +\def\TD@normal{% + \catcode`\^^I=\active + \catcode`\ =\active + \catcode`\\=\active + \catcode`\{=\active + \catcode`\}=\active + \catcode`\[=\active + \catcode`\]=\active +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\TD@command} +% This macro switches from normal mode to command mode. +% \DoNotIndex{\^,\ ,\\,\[,\]} +% \DoNotIndexHarmless{\PrintChar{`\{}} +% \DoNotIndexHarmless{\PrintChar{`\}}} +% \begin{macrocode} +\def\TD@command{% + \catcode`\^^I=10% + \catcode`\ =10% + \catcode`\[=12% + \catcode`\]=12% + \catcode`\\=\z@ + \catcode`\{=\@ne + \catcode`\}=\tw@ +} +% \end{macrocode} +% \end{macro} +% +% +% \begin{macro}{\TD@active@space} +% \begin{macro}{\TD@active@space@} +% This macro is used for spaces in normal mode. Its main problem is +% to determine whether it is followed by more whitespace or not. If it +% is, then it is an unbreakable space. If it isn't, then it is a +% breakable space. +% \begin{macrocode} +\def\TD@active@space#1{% + \ifx #1\TD@active@backslash + \expandafter\TD@active@space@ + \else + \ifx #1\TD@active@space + \TD@typeset@space + \else\ifx #1\TD@active@tab + \TD@typeset@space + \else + \TD@breakable@space + \fi\fi + \expandafter#1% + \fi +} +% \end{macrocode} +% The special case to look out for is that the next character is a +% backslash which escapes a newline. +% \begin{macrocode} +\def\TD@active@space@#1{% + \ifx #1\TD@active@CR + \TD@typeset@space + \else + \TD@breakable@space + \fi + \TD@active@backslash #1% +} +% \end{macrocode} +% \end{macro}\end{macro} +% +% \begin{macro}{\TD@active@tab} +% The |\TD@active@tab| macro is what the active character tab is +% |\let| to inside the \texttt{tcl} and \texttt{tcl*} environments. +% \begin{macrocode} +\def\TD@active@tab{\TD@typeset@tab} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\TD@active@CR} +% This macro is called at the end of a line. The |\catcode| tests the +% current mode. +% \DoNotIndex{\\} +% \begin{macrocode} +\def\TD@active@CR{% + \ifnum \catcode`\\=\z@ + \ifvmode\else + \PackageError{tclldoc}{Horizontal material on command line}\@ehc + \@@par + \fi + \TD@normal + \else + \TD@end@tclpar + \fi + \TD@start@n@line +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\TD@active@CRv} +% This is what |\tclverb| defines the |^^M| active character as +% being. Cf.\ the standard \LaTeX\ macro |\verb@eol@error|. +% \begin{macrocode} +\def\TD@active@CRv{% + \verb@egroup + \PackageError{tclldoc}{\protect\tclverb\space ended by end of line}% + \@ehc +} +% \end{macrocode} +% \end{macro} +% +% +% \begin{macro}{\TD@active@backslash} +% \changes{v\,2.10}{2000/10/08}{First checking if next character is +% active, and only expands the macro containing the detailed tests +% if it is. (LH)} +% \begin{macro}{\TD@active@backslash@} +% \begin{macro}{\TD@active@backslash@v} +% |\TD@active@backslash| is used for backslashes in normal mode. Its +% main problem is to determine whether the backslash is escaping +% something (which would normally be treated specially by the +% environment) or not. The primary test for this is whether the +% argument grabbed is an active character, and if it is then +% processing continues (in |\TD@active@backslash@|) to determine what +% should be done. +% \begin{macrocode} +\def\TD@active@backslash#1{% + \ifcat \noexpand#1\noexpand~% + \expandafter\TD@active@backslash@ + \else + \@backslashchar + \fi + #1% +} +% \end{macrocode} +% +% In order to have guard lines processed correctly, the first test +% below really must be for |\TD@active@CR| rather than for an +% active |^^M|. +% \changes{v\,2.11}{2000/11/06}{Escaped spaces are no longer breakable. +% (LH)} +% \begin{macrocode} +\def\TD@active@backslash@#1{% + \ifcase + \ifx #1\TD@active@CR 0% + \else\ifx #1\TD@active@backslash 1% + \else\ifx #1\TD@active@braceleft 2% + \else\ifx #1\TD@active@braceright 2% + \else\ifx #1\TD@active@bracketleft 2% + \else\ifx #1\TD@active@bracketright 2% + \else\ifx #1\TD@active@space 3% + \else 4\fi\fi\fi\fi\fi\fi\fi + \space + \expandafter\TD@active@backslash@i + \or + \@backslashchar\@backslashchar + \or + \@backslashchar \string#1% + \or + \@backslashchar \TD@typeset@space + \else + \@backslashchar + \expandafter#1% + \fi +} +% \end{macrocode} +% Escaped newlines that are followed by command mode lines are not +% converted to discretionary spaces. +% \begin{macrocode} +\def\TD@active@backslash@i#1{% + \ifx #1\TD@percent@token + \copy\TD@backslash@box + \expandafter\TD@active@CR + \else + \TD@breakable@space + \global\advance \c@codelineno \@ne + \expandafter\TD@gobble@whitespace + \fi + #1% +} +% \end{macrocode} +% The |\tclverb| command uses |\TD@active|\-|@backslash@v| instead of +% |\TD|\-|@active|\-|@backslash@i|, since it is intended for use on +% lines that may begin with |%|. +% \begin{macrocode} +\def\TD@active@backslash@v#1{% + \TD@discretionary@space + \ifx #1\TD@percent@token + \expandafter\@firstoftwo + \fi + \TD@gobble@whitespace #1% +} +% \end{macrocode} +% \end{macro}\end{macro}\end{macro} +% +% +% \begin{macro}{\TD@active@bracketleft} +% \begin{macro}{\TD@active@bracketright} +% These macros mainly typeset the corresponding characters, but they +% also increase or decrease the |\TD@nesting@level| count by one. +% \begin{macrocode} +\def\TD@active@bracketleft{[\advance\TD@nesting@level\@ne} +\def\TD@active@bracketright{]\advance\TD@nesting@level\m@ne} +% \end{macrocode} +% \end{macro}\end{macro} +% +% \begin{macro}{\TD@active@braceleft} +% \begin{macro}{\TD@active@braceright} +% \begin{macro}{\TD@nesting@stack} +% \changes{v\,2.14}{2001/01/19}{Nesting stack added to cope with code +% (brace-delimited strings) where brackets aren't properly balanced. +% (LH)} +% The |\TD@active@braceleft| and |\TD@active@braceright| work very +% much like their \texttt{bracket} counterparts, but they also push +% and pop the current nesting level on and off the |\TD@nesting@stack| +% macro. The reason for doing this is that brackets aren't always +% properly balanced in \Tcl\ code, which may result in a +% |\TD@nesting@level| value which does not mirror how the code really +% will be interpreted. Braces must however be properly balanced, and +% therefore it is always right to restore the value of +% |\TD@nesting@level| that was in force before a brace group began +% when it is ended. +% +% The |\TD@nesting@stack| macro is a \meta{nesting stack}, which is +% one of the following two things +% \begin{quote} +% |\TD@nesting@level| +% \meta{number}|\def\TD@nesting@stack{|\meta{nesting stack}|}| +% \end{quote} +% This means a value is popped off the stack by doing +% \begin{quote} +% |\TD@nesting@level=\TD@nesting@stack| +% \end{quote} +% and |\TD@nesting@level| is unchanged if the stack is empty. +% \DoNotIndex{\[,\]} +% \DoNotIndexHarmless{\PrintChar{`\{}} +% \DoNotIndexHarmless{\PrintChar{`\}}} +% \begin{macrocode} +\begingroup + \catcode`\{=12 \catcode`\}=12 + \catcode`\[=1 \catcode`\]=2 + \gdef\TD@active@braceleft[{% + \expandafter\def \expandafter\TD@nesting@stack \expandafter[% + \the\expandafter\TD@nesting@level + \expandafter\def \expandafter\TD@nesting@stack + \expandafter[\TD@nesting@stack]% + ]% + \advance\TD@nesting@level\@ne + ] + \gdef\TD@active@braceright[}% + \advance\TD@nesting@level\m@ne + \TD@nesting@level=\TD@nesting@stack + ] +\endgroup +\def\TD@nesting@stack{\TD@nesting@level} +% \end{macrocode} +% \end{macro}\end{macro}\end{macro} +% +% +% +% \subsection{Paragraph formatting} +% +% This subsection contains definitions of control sequences that are +% mainly related to the formatting of paragraphs. +% +% The new implementation of the \texttt{tcl} and \texttt{tcl*} +% environments is based on inserting the ``discretionary'' backslashes +% at the linebreaks \emph{after} the paragraph has been broken. +% Doing this involves reboxing every line in the paragraph, and in this +% process the line adjustments are changed as well. +% +% \begin{macro}{\TD@backslash@box} +% The |\TD@backslash@box| box register is set to a backslash in the +% special |\TD@nontcl@font| at the beginning of each \texttt{tcl} and +% \texttt{tcl*} environment. These backslashes are used to denote +% ``backslash escaping newline''. The primary reason for keeping this +% glyph in a box is not that this might be slightly faster to +% typeset, but to have the \emph{width} of it easily accessible. +% \begin{macrocode} +\newbox\TD@backslash@box +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\TD@nesting@level} +% \begin{macro}{\TD@nesting@penalty} +% |\TD@nesting@level| is a count register which keeps track of how +% many brace\slash bracket groups surrounds the current position in +% the line. It is cleared to zero at the beginning of each paragraph. +% \begin{macrocode} +\newcount\TD@nesting@level +% \end{macrocode} +% |\TD@nesting@penalty| makes a |\penalty| whose value should depend +% on the nesting level (the deeper the nest, the larger the penalty). +% It may also adjust the value of |\linepenalty| to counter that +% the nesting level has become negative. This is typically needed for +% lines which say things like ``|} else {|''. The default definition is +% to make the nesting penalty $100$ times the nesting level and to +% make the |\linepenalty| equal to +% \(10 - \min\{0,\mbox{least penalty}\}\). +% \begin{macrocode} +\def\TD@nesting@penalty{% + \penalty \the\TD@nesting@level 00\relax + \ifnum \lastpenalty<-\linepenalty + \linepenalty=10% + \advance \linepenalty -\lastpenalty + \fi +} +% \end{macrocode} +% I doubt that this is necessarily the best algorithm for computing +% these penalties, so if you are really thorough about your +% typography you may well want to experiment with other definitions of +% |\TD@nesting@penalty|. In case you should then find something which +% works even better then I'm interested to learn about it. +% \end{macro}\end{macro} +% +% \begin{macro}{\TD@reformat@lines} +% The |\TD@reformat@lines| macro calls itself recursively to reformat +% all lines on the current vertical list. The first line will remain +% flush left, but all other lines will be reset flush right. The +% visible material on the last line will be left as it is, but the +% last box in all other lines will be replaced by a non-macro font +% backslash. +% +% It is very important that the current vertical list is not the main +% vertical list. +% It is assumed that the current vertical list consists of a sequence +% of \meta{box}, \meta{penalty}, \meta{glue}, with an extra glue item +% at the top of the list. It is OK if some penalty or glue item is +% missing. In case the list contains other material as well the line +% reformatting may be stopped prematurely, but there is a trick that +% allows one to put arbitrary material between the lines of the +% reformatted paragraph: rather than doing e.g. +% \begin{quote} +% |\mark|\marg{text} +% \end{quote} +% in the paragraph, do +% \begin{quote} +% |\vadjust{\vbox{\mark{|\meta{text}|}}}| +% \end{quote} +% The |\vbox| will be recognised by the paragraph reformatting +% mechanism as a container for vertical mode material that appears +% between the lines of the paragraph, so it will simply be unboxed. +% \changes{v\,2.12}{2000/11/11}{\cs{vbox} containers for vertical +% material are allowed between the lines of a reformatted +% paragraph. (LH)} +% +% Each line's horizontal list ends with +% \begin{itemize} +% \item a box (which contains the space that is to be replaced by a +% backslash), +% \item a penalty (at which the paragraph was broken), and +% \item a glue item (the |\rightskip|). +% \end{itemize} +% +% A tricky feature in the implementation is that the +% |\bgroup|--|\egroup| nesting will be off by one. The |\bgroup| at +% the beginning of a |\TD@reformat@line| will be matched by the +% |\egroup| at the end of the |\TD@reformat@line| that the first one +% calls! +% \begin{macrocode} +\def\TD@reformat@lines{% + \bgroup + \unskip + \count@=\lastpenalty \unpenalty + \setbox\z@=\lastbox + \ifvoid\z@ +% \end{macrocode} +% The recursion had already descended down to the last line of the +% paragraph, and it is now time to reformat it. +% \begin{macrocode} + \egroup + \prevdepth=\TD@prevdepth + \hbox{% + \unhbox\z@ + \unskip \unpenalty + \setbox\z@=\lastbox + \copy\TD@backslash@box + }% + \else +% \end{macrocode} +% Else there may be another line, and the |\TD@reformat@lines| recursion +% must continue to descend. Upon return the box currently in box +% register zero must be reformatted as a non-first line (flush +% right) and it cannot be the last line in the paragraph, so it is +% always correct to replace the last box by a backslash. +% \begin{macrocode} + \TD@reformat@lines + \ifvbox\z@ \unvbox\z@ \else + \hb@xt@\dimen@{% + \hfill + \unhbox\z@ + \unskip \unpenalty + \setbox\z@=\lastbox + \copy\TD@backslash@box + }% + \fi + \fi + \ifnum \count@=\z@ \else \penalty\count@ \fi + \egroup +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\TD@reformat@par} +% The |\TD@reformat@par| macro reformats all lines (they're supposed +% to constitute a paragraph, but that isn't so important) in the +% current vertical list. The restrictions of |\TD@reformat@lines| on +% what may appear in the list apply and there must be at least two +% lines in the list. |\dimen@| is used to hold the desired width of +% reformatted paragraphs. +% +% More precisely |\TD@reformat@par| takes care of the last line of +% the paragraph and the possible |\vbox| containers for vertical +% material that may follow it. Everything in the paragraph that comes +% before the last line is handled by |\TD@reformat@lines|. +% \changes{v\,2.12}{2000/11/11}{\cs{vbox} containers for vertical +% material are allowed after the last line of a reformatted +% paragraph. (LH)} +% \begin{macrocode} +\def\TD@reformat@par{% + \unskip + \count@=\lastpenalty \unpenalty + \setbox\z@=\lastbox + \ifvbox\z@ + \bgroup + \TD@reformat@par + \egroup + \unvbox\z@ + \else\ifnum \prevgraf>\@ne + \dimen@=\@totalleftmargin + \advance \dimen@ \linewidth + \bgroup + \unskip + \count@=\lastpenalty \unpenalty + \setbox\z@=\lastbox + \TD@reformat@lines + \hb@xt@\dimen@{\hfill \unhbox\z@ \unskip}% + \else + \unskip + \prevdepth=\TD@prevdepth + \box\z@ + \fi\fi + \ifnum \count@=\z@ \else \penalty\count@ \fi +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\TD@prevdepth} +% |\TD@prevdepth| is a macro which is used for storing the value of +% |\prevdepth| at times where \TeX\ modifies the primitive in unwanted +% ways. It should always be set globally. +% \end{macro} +% +% +% \begin{macro}{\TD@begin@tclpar} +% The |\TD@begin@tclpar| macro is called when a paragraph in a +% \texttt{tcl} or \texttt{tcl*} environment is about to start. It +% takes care of setting up things so that the paragraph can later be +% reformatted using |\TD@reformat@par|, but it also has to make sure +% that this reformatting doesn't affect the way the paragraph blends +% in with vertical material before and after it. +% +% Reformatting requires that the paragraph is first built in +% restricted vertical mode, i.e., it has to be built in an explicit +% |\vbox|. A problem with this is however that it changes the value of +% |\prevdepth|, which must therefore be explicitly restored. +% \begin{macrocode} +\def\TD@begin@tclpar{% + \xdef\TD@prevdepth{\the\prevdepth}% + \setbox\z@=\vbox\bgroup + \color@begingroup + \prevdepth=\TD@prevdepth + \indent +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\TD@end@tclpar} +% The |\TD@end@tclpar| macro ends a paragraph begun by +% |\TD@begin@tclpar|, reformats it (|\TD@reformat@par|), and +% contributes it to the surrounding vertical list. The |\begingroup| +% and |\endgroup| are there to sort things out in case the recursion +% in |\TD@reformat@par| fails to match as intended. The second +% |\@@par| sees to that the page builder is exercised (without it, +% several pages may go onto the main vertical list without anything +% being shipped out). +% \begin{macrocode} +\def\TD@end@tclpar{% + \@@par + \begingroup + \skip@=\lastskip + \TD@reformat@par + \vskip\skip@ + \endgroup + \xdef\TD@prevdepth{\the\prevdepth}% + \color@endgroup + \egroup + \unvbox\z@ + \prevdepth=\TD@prevdepth + \@@par +} +% \end{macrocode} +% \end{macro} +% +% +% \begin{macro}{\TD@line@indent} +% |\TD@line@indent| is a |\count| register holding the indentation of +% the current line, in number of spaces. |\TD@line@indent| should +% always be assigned globally. +% \begin{macrocode} +\newcount\TD@line@indent +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\TD@par@setup} +% This macro sets up various paragraph formatting parameters for the +% normal mode. It mainly consists of code from |\macro@code| of +% \package{doc}. +% \begin{macrocode} +\def\TD@par@setup{% +% \end{macrocode} +% +% In theory a \texttt{tcl} environment should consist of a +% \texttt{trivlist} environment, but the empty space before and after +% the environment should not be too large. +% \begin{macrocode} + \topsep\MacrocodeTopsep +% \end{macrocode} +% +% The next parameter we set is |\@beginparpenalty|, in order +% to prevent a page break before such an environment. +% \begin{macrocode} + \@beginparpenalty\predisplaypenalty +% \end{macrocode} +% +% We then start a |\trivlist|, set |\parskip| back to +% zero and start an empty |\item|. +% \changes{ \package{doc} v\,1.9b}{1993/12/03}{Forcing any label +% from macro env.} +% \begin{macrocode} + \if@inlabel\leavevmode\fi + \trivlist + \parskip\z@skip + \item[]% +% \end{macrocode} +% However, the \texttt{trivlist} environment is only started to make +% standard \LaTeX\ happy (and to have the various list-related +% measurements properly calculated). Everything below will instead +% by done with \TeX\ primitives. +% \changes{ \package{pasdoc} v\,1.31}{1999/09/02}{\cs{parshape} +% assignment added. (LH)} +% \changes{v\,2.10}{2000/10/08}{\cs{parshape} assignment removed (now +% done in \cs{TD@begin@tclpar}). (LH)} +% +% It is important that the change to a special font does not take +% place before the above |\item|, otherwise a change to +% |\baselineskip| will affect the paragraph above. +% \begin{macrocode} + \macro@font + \frenchspacing +% \end{macrocode} +% +% \leavevmode +% \changes{v\,2.12}{2000/11/11}{Assignment to \cs{TD@backslash@box} +% moved from \cs{TD@typography} to here, so that the size will be +% right. (LH)}^^A +% One escape symbol that is particularly common is a single +% backslash, and this is stored in the |\TD@backslash@box| box. As +% some encodings (e.g.\ \texttt{OT1}) does not contain a backslash, +% these escape backslashes are made with |\textbackslash|, rather +% than an explicit character. +% \begin{macrocode} + \sbox\TD@backslash@box{\TD@nontcl@font\textbackslash}% +% \end{macrocode} +% +% The |\rightskip| is given a generous stretchability of |\linewidth| +% so that it doesn't matter too much if the lengths of lines in the +% paragraph varies. Decreasing this value would make the nesting in +% the code less important. +% \begin{macrocode} + \parindent=\@totalleftmargin + \advance \parindent \MacroIndent + \leftskip=\z@skip + \rightskip=\z@ \@plus \linewidth\relax +% \end{macrocode} +% The next two lines are from the definition of the +% \texttt{macrocode} environment, and I (LH) have no idea what they are +% good for, but I suppose I can keep them, at least for now. +% \begin{macrocode} + \global\@newlistfalse + \global\@minipagefalse +% \end{macrocode} +% \changes{ \package{pasdoc} v\,1.31}{1999/09/02}{Made list +% environments indent line numbers. (LH)} +% \changes{v\,2.33}{2001/12/12}{Made list environments not indent +% line numbers per default, but providing a way for the user to +% change this. (LH)} +% Line numbers are inserted using |\everypar|. +% \begin{macrocode} + \ifcodeline@index + \everypar={\llap{% + \PrintCodelineNo\ \hskip\codelineindentfactor\@totalleftmargin + }}% + \else + \everypar={}% + \fi +% \end{macrocode} +% +% These commands replace \package{doc}'s final module processing +% macros with macros that work in the \texttt{tcl} environment. +% \begin{macrocode} + \let\pm@module=\TD@pm@module + \let\star@module=\TD@star@module + \let\slash@module=\TD@slash@module +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\codelineindentfactor} +% The |\codelineindentfactor| macro specifies by which fraction of +% |\@totalleftmargin| that the list environment indentation of +% codeline numbers will be countered. |0| means that the numbers will +% be indented, |1| or empty (the default) that they will not be +% indented. |0.5| would mean that the effective indentation is half +% that of other material. +% \begin{macrocode} +\let\codelineindentfactor\@empty +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\TD@setup@parshape} +% This macro sets the shape of the current paragraph, primarily based +% on the value in |\TD@line@indent|, and makes the final indentation +% of the first line. By the time it gets called, there usually already +% is some material (|\indent|, codeline number, and possibly a +% \package{docstrip} guard) in the paragraph horizontal list. +% +% The basic area in which the paragraph material is to be put has a +% left margin at |\@totalleftmargin|${}+{}$|\MacroIndent| and a right +% margin at |\@totalleftmargin|${}+{}$|\linewidth| (in both cases the +% position is that much to the right of the left edge of the galley). +% The printed codeline number (if there is one) will be put to the +% left of the left margin, but all other material is indented at least +% the width of another |\TD@line@indent| spaces. In the first line +% that will be done through an explicit kern, but in subsequent lines +% it will instead be done through a |\parshape| assignment. On these +% subsequent lines, there will be an additional indentation of\/ +% $1\,\mathrm{em}$ to mark the continuation. +% +% When the paragraph is broken, the right margin will be moved to the +% right by the width of a space minus the width of the +% |\TD@backslash@box|. The reason for this is that the paragraph will +% be broken with spaces (from |\TD@breakable@space|) at the end of +% lines, but that these spaces will later be replaced by the +% backslash in |\TD@backslash@box|. As that doesn't happen in the +% very last line of the paragraph however, something special must be +% done there. The option chosen is to give the |\parfillskip| a +% nonzero natural width. +% +% The length |\@totalleftmargin|${}+{}$|\MacroIndent| is put in +% |\parindent| at the beginning of the environment. +% \begin{macrocode} +\def\TD@setup@parshape{% + \parfillskip=\fontdimen\tw@\font \@plus 1fil% + \advance \parfillskip -\wd\TD@backslash@box + \dimen@=\MacroIndent + \advance \dimen@ \TD@line@indent\fontdimen\tw@\font + \advance \dimen@ 1em% + \dimen@ii=\linewidth + \advance \dimen@ii -\dimen@ + \dimen4=\linewidth + \advance \dimen@ \@totalleftmargin + \advance \dimen4 \@totalleftmargin + \advance \dimen@ii -\parfillskip + \advance \dimen4 -\parfillskip + \parshape \tw@ \z@ \dimen4 \dimen@ \dimen@ii +% \end{macrocode} +% Then one just needs one more kern to indent the first line +% properly. +% \begin{macrocode} + \kern \TD@line@indent\fontdimen\tw@\font +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\TD@trace@pardimen} +% This macro is for tracing dimens that are of interest for paragraph +% breaking. Its call syntax is +% \begin{quote} +% |\TD@trace@pardimen|\marg{text}\marg{dimen} +% \end{quote} +% where \meta{text} will be written to the log file to identify the +% dimension, and \meta{dimen} is the dimen register in question (it +% can be anything that can appear after |\the|). +% +% To reduce the amount of text written, this macro only does anything +% if the \TeX\ parameter |\tracingparagraphs| is positive. +% \begin{macrocode} +%<*partrace> +\def\TD@trace@pardimen#1#2{% + \ifnum \tracingparagraphs>\z@ + \immediate\write\m@ne{#1 \the#2}% + \fi +} +% +% \end{macrocode} +% \changes{ \package{pasdoc} v\,1.21}{1999/08/15}{Added macro and +% some calls of it. All of those are however surrounded by +% \texttt{partrace} guards. (LH)} +% \end{macro} +% +% +% +% \subsection{User interface} +% +% This subsection defines the actual environment and the +% commands and parameters intended to be user-definable. +% +% +% \begin{macro}{\settabsize} +% \changes{v\,2.12}{2000/11/09}{New command for setting the tab size. +% (LH)} +% \begin{macro}{\TD@tab@size} +% \changes{v\,2.12}{2000/11/09}{New name for \cs{c@tabSize}; no longer a +% count register. (LH)} +% The |\TD@tab@size| control sequence stores (as a |\chardef| token) +% the number of spaces that are equivalent to one tab; the default +% value is 8. To set this number, one uses the command |\settabsize|, +% which has the syntax +% \begin{quote} +% |\settabsize|\marg{new size} +% \end{quote} +% The effect of |\settabsize| is local. +% \begin{macrocode} +\newcommand*\settabsize[1]{\chardef\TD@tab@size=#1\relax} +\settabsize{8} +% \end{macrocode} +% \end{macro}\end{macro} +% +% +% \begin{environment}{tcl} +% \begin{environment}{tcl*} +% This is some kind of a definition. There are aspects of the +% interaction with \texttt{trivlist} that I (LH) don't understand, +% but it appears to work. +% \begin{macrocode} +\newenvironment{tcl}{% + \TD@general + \TD@typography + \TD@par@setup + \TD@command +}{% + \global\@inlabelfalse + \endtrivlist +} +% \end{macrocode} +% \begin{macrocode} +\newenvironment{tcl*}{% + \TD@visible@whitespace + \TD@general + \TD@typography + \TD@par@setup + \TD@command +}{% + \global\@inlabelfalse + \endtrivlist +} +% \end{macrocode} +% \end{environment}\end{environment} +% +% \begin{macro}{\tclverb} +% \begin{macro}{\TD@verb} +% The |\tclverb| macro is for typesetting short passages of \Tcl\ code +% which one does not want to have a special paragraph for. It is +% similar to the standard \LaTeX\ command |\verb|, but it has a couple +% of extra features. One is that spaces that are not followed by more +% whitespace are discretionary breakpoints, just as in the \texttt{tcl} +% environment. Another is that a newline can be escaped by putting a +% backslash at the end of an input line. Such an escaped newline will +% count as a space, and whitespace at the beginning of the line +% following it will be ignored. A percent character immediately +% following an escaped newline will also be ignored. +% +% |\tclverb| has a |*| form |\tclverb*|, which is analogous to the +% standard \LaTeX\ |\verb*|, i.e., spaces are typeset using ``visible +% space'' symbols. +% \begin{macrocode} +\newcommand\tclverb{% + \relax\ifmmode\hbox\else\leavevmode\null\fi + \bgroup + \@ifstar{\TD@visible@whitespace\TD@verb}\TD@verb +} +% \end{macrocode} +% \DoNotIndex{\ ,\^,\\} +% \begin{macrocode} +\def\TD@verb{% + \let\TD@active@backslash@i=\TD@active@backslash@v + \let\TD@active@CR=\TD@active@CRv + \let\TD@breakable@space=\TD@discretionary@space + \TD@general + \catcode`\^^I=\active + \catcode`\ =\active + \catcode`\\=\active + \TD@typography + \verbatim@font + \sbox\TD@backslash@box{\TD@nontcl@font\textbackslash}% + \@sverb +} +% \end{macrocode} +% \end{macro}\end{macro} +% +% +% \begin{macro}{\TclInput} +% The |\TclInput| macro is very much like the standard \LaTeX\ +% |\input|, but it is intended to be used in command mode, for +% inputting \Tcl\ files that have not been marked up. |\input| would +% work in this case, but there would be errors at the first and last +% lines of the |\input|ted file. +% \begin{macrocode} +\newcommand\TclInput[1]{% + \IfFileExists{#1}{% + \@addtofilelist{#1}% + \begingroup + \TD@normal + \let\TD@start@n@line=\TD@start@n@line@i + \expandafter\TD@start@n@line + \@@input\@filef@und\TD@percent@token + \endgroup + }{\PackageError{tclldoc}{No file #1}\@eha}% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\MakeShortTclverb} +% This definition follows \package{doc}'s |\MakeShortVerb| pretty +% close. +% \DoNotIndex{\~} +% \begin{macrocode} +\newcommand\MakeShortTclverb[1]{% + \expandafter\ifx \csname cc\string#1\endcsname\relax + \PackageInfo{tclldoc}{% + Made \expandafter\@gobble\string#1 a short % + reference for \string\tclverb}% + \add@special{#1}% + \expandafter\xdef \csname cc\string#1\endcsname{\the\catcode`#1}% + \begingroup + \lccode`\~=`#1% + \lowercase{% + \global\expandafter\let + \csname ac\string#1\endcsname ~% + \gdef~{\tclverb~}}% + \endgroup + \global\catcode`#1\active + \else + \PackageInfo{tclldoc}{% + \expandafter\@gobble\string#1 already a short verb % + reference}% + \fi +} +% \end{macrocode} +% \end{macro} +% +% +% \section{Miscellaneous markup features} +% +% \subsection{Namespaces} +% +% \changes{v\,2.10}{2000/10/13}{Added namespace support. (LH)} +% +% \begin{macro}{\setnamespace} +% \begin{macro}{\TD@convert@colons} +% Since it is common that many identifiers in the same namespace are +% defined in sequence, one can specify a default namespace to use for +% all commands where no explicit namespace is given. This is done +% using the |\setnamespace| command, whose syntax is +% \begin{quote} +% |\setnamespace|\marg{namespace} +% \end{quote} +% This converts the \meta{namespace} to a harmless character string +% and locally assigns it to the |\TD@namespace| macro, which stores +% the current default namespace. |\TD@convert@colons| replaces all +% |::| substrings in the namespace by |\namespaceseparator|s. +% \changes{v\,2.33}{2001/12/12}{Converting \texttt{::} in namespace +% names. (LH)} +% \begin{macrocode} +\newcommand\setnamespace[1]{% + \MakeHarmless\TD@namespace{#1}% + \protected@edef\TD@namespace{% + \expandafter\TD@convert@colons \TD@namespace ::\relax + } +} +% \end{macrocode} +% \begin{macrocode} +\def\TD@convert@colons#1::#2{% + #1% + \ifx \relax#2\else + \noexpand\namespaceseparator + \TD@convert@colons #2% + \fi +} +% \end{macrocode} +% \end{macro}\end{macro} +% +% \begin{macro}{\TD@namespace} +% At the beginning of a document, the default namespace is the global +% namespace, which is represented by the empty string. +% \begin{macrocode} +\let\TD@namespace\@empty +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\buildname} +% \changes{v\,2.13}{2000/12/16}{Added \cs{@empty} after test for +% \cs{NoValue}, so that it works for empty arguments. (LH)} +% \begin{macro}{\namespaceseparator} +% \changes{v\,2.12}{2000/11/25}{Command added. (LH)} +% The |\buildname| macro builds a qualified name from a namespace and +% a name tail. It has the syntax +% \begin{quote} +% |\buildname|\marg{namespace}\marg{name} +% \end{quote} +% where \meta{name} is a harmless character sequence and +% \meta{namespace} is a harmless character sequence or the token +% |\NoValue|. If \meta{namespace} is |\NoValue| then the default +% namespace is used. |\buildname| does all processing at expand-time. +% +% If the namespace is the empty string (the global namespace) then +% \meta{name} is returned without a `|::|' prefix; this is for the +% sake of compatibility with pre-v\,8 \Tcl s. +% \begin{macrocode} +\newcommand\buildname[2]{% + \ifx \NoValue#1\@empty + \ifx \@empty\TD@namespace \else + \TD@namespace\namespaceseparator + \fi + \else + \ifx $#1$\else #1\namespaceseparator \fi + \fi + #2% +} +% \end{macrocode} +% The |\namespaceseparator| command typesets a namespace separator. By +% default it is two colons separated by a penalty, but the user may +% redefine it. +% \begin{macrocode} +\DeclareRobustCommand\namespaceseparator{:\penalty\hyphenpenalty:} +% \end{macrocode} +% \end{macro}\end{macro} +% +% \begin{macro}{\namespacephrase} +% The |\namespacephrase| macro stores the phrase that will be used +% for `namespace' in indices and the like. It is meant to be +% redefinable. The contents must be robust. +% \changes{v\,2.25}{2001/08/14}{Macro added. (LH)} +% \begin{macrocode} +\providecommand*\namespacephrase{name\-space} +% \end{macrocode} +% \end{macro} +% +% \begin{instance}{docindex}{index} +% \changes{v\,2.21}{2001/03/26}{Modified as per the v\,1.00 change to +% \package{docindex}. (LH)} +% The \texttt{index} instance of template type \texttt{docindex} +% (defined by the \package{docindex} package) is redefined to allow +% name and namespace levels to be joined. +% \begin{macrocode} +\@ifpackagewith{docidx2e}{usedocindexps}{% + \@namedef{TP@I{}{docindex}{index}}#1#2{% + \begingroup + \@letinstance\DI@indexitem@i{indexitem}{fixed-r1a}% + \@letinstance\DI@indexitem@ii{indexitem}{aloneaccept2}% + \@letinstance\DI@indexitem@iii{indexitem}{fixed3}% + \columnsep=10pt% + \parskip=0pt plus 1pt% + \def\DI@letter@skip{10pt plus 2pt minus 3pt}% + \def\DI@letter@format##1{% + \par + \hb@xt@\hsize{\hfil\textbf{##1}\hfil}% + \nopagebreak + }% + \def\+{-}% + \IfFileExists{\jobname.ind}{% + \ifnum \c@IndexColumns>\@ne + \begin{multicols}{\c@IndexColumns}[#1][\IndexMin]% + \else + \enough@room{\IndexMin}% + #1\par + \addvspace\multicolsep + \fi + \pagestyle{docindex}% + \small + \@nobreakfalse + \DI@ind@setup + \input{\jobname.ind}% + \DI@item@nojoin + \thispagestyle{docindex} + \ifnum \c@IndexColumns>\@ne + \end{multicols}% + \else + \enough@room\postmulticols + \addvspace\multicolsep + \fi + \normalsize #2\par + }{\typeout{No file \jobname.ind.}}% + \endgroup + } +}{% + \@namedef{TP@I{}{docindex}{index}}#1#2{% + \begingroup + \@letinstance\DI@indexitem@i{indexitem}{fixed-r1a}% + \@letinstance\DI@indexitem@ii{indexitem}{aloneaccept2}% + \@letinstance\DI@indexitem@iii{indexitem}{fixed3}% + \columnsep=10pt% + \parskip=0pt plus 1pt% + \def\DI@letter@skip{10pt plus 2pt minus 3pt}% + \def\DI@letter@format##1{% + \par + \hb@xt@\hsize{\hfil\textbf{##1}\hfil}% + \nopagebreak + }% + \def\+{-}% + \IfFileExists{\jobname.ind}{% + \ifnum \c@IndexColumns>\@ne + \begin{multicols}{\c@IndexColumns}[#1][\IndexMin]% + \else + \enough@room{\IndexMin}% + #1\par + \addvspace\multicolsep + \fi + \small + \@nobreakfalse + \DI@ind@setup + \input{\jobname.ind}% + \DI@item@nojoin + \ifnum \c@IndexColumns>\@ne + \end{multicols}% + \else + \enough@room\postmulticols + \addvspace\multicolsep + \fi + \normalsize #2\par + }{\typeout{No file \jobname.ind.}}% + \endgroup + } +} +% \end{macrocode} +% \end{instance} +% +% +% +% \subsection{\texttt{macro}-like environments} +% +% This subsection contains the definitions of a couple of environments +% which, like \package{doc}'s \texttt{macro}, are for marking up the +% document (what is defined and where). These environments are defined +% using the |\New|\-|Macro|\-|Environment| command of the \package{xdoc2} +% package. +% \changes{ \package{pasdoc} v\,1.40}{1999/12/05}{\texttt{macro}-like +% environments redefined. (LH)} +% \changes{v\,2.00}{2000/07/17}{\texttt{macro}-like environments are +% defined using the \cs{NewMacroEnvironment} command. The +% \cs{TD@identifier}, \cs{SpecialMainTclIndex}, and +% \cs{PrintTclName} macros were removed. (LH)} +% \changes{v\,2.12}{2000/11/26}{Using starred form of +% \cs{NewMacroEnvironment} for the \texttt{variable}, \texttt{proc}, +% \texttt{arrayvar}, and \texttt{arrayentry} environments, so that +% the marginal headings can be broken. (LH)} +% +% \begin{macrocode} +\MetaNormalfont +% \end{macrocode} +% \changes{v\,2.32}{2001/11/30}{Added \cs{MetaNormalfont} as a global +% declaration and removed it from the commands that did this locally. +% There were simply so many places where it should have been +% otherwise that the resulting code would have looked rudiculous. +% (LH)} +% +% \begin{environment}{variable} +% The \texttt{variable} environment has the syntax +% \begin{quote} +% |\begin{variable}|\oarg{namespace}\marg{variable} +% \end{quote} +% It makes a marginal heading, index entry, etc.\@ for the +% \meta{variable} variable in the \meta{namespace} namespace +% (if specified, otherwise in the default namespace). +% \begin{macrocode} +\NewMacroEnvironment*{variable}% + {\XD@grab@harmless@oarg\XD@grab@harmless\relax}{2}% + {\XDParToMargin{\MacroFont\buildname{#1}{#2} \normalfont (var.)}}% + {\TD@main@index{#1}{#2}{var.}{}}% + {{\buildname{#1}{#2}}{\texttt{\buildname{#1}{#2}} variable}} + {}% +% \end{macrocode} +% \end{environment} +% +% \begin{environment}{proc} +% The \texttt{proc} environment has the syntax +% \begin{quote} +% |\begin{proc}|\oarg{namespace}\marg{proc} +% \end{quote} +% It makes a marginal heading, index entry, etc.\@ for the \meta{proc} +% procedure in the \meta{namespace} namespace (if specified, otherwise +% in the default namespace). +% \begin{macrocode} +\NewMacroEnvironment*{proc}% + {\XD@grab@harmless@oarg\XD@grab@harmless\relax}{2}% + {\XDParToMargin{\MacroFont\buildname{#1}{#2} \normalfont (proc)}}% + {\TD@main@index{#1}{#2}{proc}{}}% + {{\buildname{#1}{#2}}{\texttt{\buildname{#1}{#2}} proc}} + {}% +% \end{macrocode} +% \end{environment} +% +% \begin{environment}{arrayvar} +% \changes{v\,2.00}{2000/07/17}{Environment added. (LH)} +% \changes{v\,2.12}{2000/11/09}{Made last argument optional. (LH)} +% \changes{v\,2.13}{2000/12/15}{Not using \cs{meta} for the +% \meta{index-des} argument. (LH)} +% \begin{environment}{arrayentry} +% \changes{v\,2.02}{2000/08/21}{Environment added. (LH)} +% The \texttt{arrayvar} and \texttt{arrayentry} environments are for +% array variables; \texttt{arrayvar} is for the array as a whole, +% whereas \texttt{arrayentry} is for individual entries in the array +% that (for some reason) are important enough to warrant special +% attention. The respective syntaxes are +% \begin{quote} +% |\begin{arrayvar}|\oarg{namespace}\marg{array}\oarg{index-des}\\ +% |\begin{arrayentry}|\oarg{namespace}\marg{array}\marg{entry} +% \end{quote} +% Here \meta{array} is the name of an array variable, \meta{entry} is +% the name of an entry, and \meta{index-des} is a short piece of text +% (seldom more than one word) which describes what is used as indices +% into that array. +% \changes{v\,2.14}{2001/01/12}{Corrected grabbing of the third +% argument of the \texttt{arrayvar} environment: it should be an +% oarg, not a harmless oarg. (LH)} +% \begin{macrocode} +\NewMacroEnvironment*{arrayvar}{% + \XD@grab@harmless@oarg\XD@grab@harmless\relax\XD@grab@oarg +}{3}% + {\XDParToMargin{\MacroFont \buildname{#1}{#2}% + \ifx \NoValue#3% + \space\normalfont (array)% + \else + \penalty\hyphenpenalty(\mbox{\meta@font@select#3})% + \fi + }}% + {\TD@main@index{#1}{#2}{array}{% + \ifx \NoValue#3\@empty\else + \LevelSorted{#3}{\protect\mbox{\textit{#3}} entries}% + \fi + }}% + {{\buildname{#1}{#2}}{\texttt{\buildname{#1}{#2}} array}} + {}% +% \end{macrocode} +% \begin{macrocode} +\NewMacroEnvironment*{arrayentry}{% + \XD@grab@harmless@oarg\XD@grab@harmless\relax\XD@grab@harmless\relax +}{3}% + {\XDParToMargin{% + \MacroFont \buildname{#1}{#2}% + \penalty\hyphenpenalty(#3)% + }}% + {\TD@main@index{#1}{#2}{array}{\LevelSorted{#3}{\texttt{#3}}}}% + {{\buildname{#1}{#2}(#3)}{\texttt{\buildname{#1}{#2}(#3)}}} + {}% +% \end{macrocode} +% \end{environment}\end{environment} +% +% \begin{macro}{\describestring} +% \changes{v\,2.01}{2000/08/12}{Command added. (LH)} +% \changes{v\,2.12}{2000/11/21}{Optional argument added. (LH)} +% \changes{v\,2.13}{2000/12/16}{Second optional argument added. (LH)} +% \changes{v\,2.20}{2001/03/03}{Putting namespace on the second +% level. (LH)} +% The |\describestring| command has the syntax +% \begin{quote} +% |\describestring|\oarg{type}\oarg{namespace}\marg{string} +% \end{quote} +% It is similar to \package{xdoc}'s own |\describe|\-|cs|\-|family| +% command, but it has a couple of extra features which are connected +% to the optional arguments and it doesn't prepend a backslash to +% the name of the thing being described. When the \meta{type} argument +% is present, it is taken as a ``type declaration'' of the +% \meta{string} being described and `(\meta{type})' will be put as a +% suffix to the \meta{string}. When the \oarg{namespace} argument is +% present it means that the string being described is really the name +% of something (e.g.\ a procedure or variable) which exists in a +% namespace, and the complete name is formed by passing both +% \meta{namespace} and \meta{string} as arguments to |\buildname|. +% Note that an empty \meta{namespace} argument is not quite the +% same thing as no \meta{namespace} argument. +% \begin{macrocode} +\NewDescribeCommand{\describestring}{% + \XD@grab@oarg\XD@grab@harmless@oarg\XD@grab@harmless{}% +}{3}{% + \GenericDescribePrint{% + \MacroFont + \ifx \NoValue#2\@empty + #3% + \else + \buildname{#2}{#3}% + \fi + \ifx \NoValue#1\@empty \else\ \normalfont(#1)\fi + }% + \begingroup + \def\meta##1{(##1)}% + \unrestored@protected@xdef\@gtempa{#3}% + \endgroup + \IndexEntry{% + \ifx \NoValue#2\@empty + \LevelSorted{\@gtempa}{% + \texttt{#3}% + \ifx \NoValue#1\@empty \else\space(#1)\fi + }% + \else + \LevelSorted{\@gtempa}{% + \texttt{#3}\ifx \NoValue#1\@empty \else\space(#1)\fi + }% + \ifx $#2$% + \LevelSorted{ }{global \namespacephrase}% + \else + \LevelSorted{#2}{\texttt{#2} \namespacephrase}% + \fi + \fi + }{usage}{\thepage}% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\describeopt} +% \changes{v\,2.24}{2001/08/11}{Command added. (LH)} +% \changes{v\,2.31}{2001/11/03}{Star-form added. (LH)} +% The |\describeopt| macro is a |\describe|\dots\ command for options +% of commands. The syntax is +% \begin{quote} +% |\describeopt|*\oarg{namespace}\marg{command}\oarg{type}^^A +% \marg{option} +% \end{quote} +% where \meta{command} is the name of the command and \meta{option} is +% the name of the option. \meta{namespace} is the namespace of the +% \meta{command}, if that is not the global namespace. \meta{type} is +% the type description for the command, if different from `proc'. +% The |*| form of this command does not print a marginal note: it +% only makes an index entry. +% \begin{macrocode} +\NewDescribeCommand{\describeopt}{% + \XD@grab@sarg{*}\XD@grab@harmless@oarg\XD@grab@harmless{}% + \XD@grab@oarg\XD@grab@harmless{}% +}{5}{% + \ifx \BooleanFalse#1% + \GenericDescribePrint{% + \MacroFont #5\ \normalfont option% + }% + \fi + \begingroup + \def\meta##1{(##1)}% + \unrestored@protected@xdef\@gtempa{#3}% + \endgroup + \IndexEntry{% + \LevelSorted{\@gtempa}{% + \texttt{#3} (\ifx \NoValue#4\@empty proc\else #4\fi)% + }% + \ifx \NoValue#2\@empty + \LevelSorted{ }{global \namespacephrase}% + \else + \LevelSorted{#2}{\texttt{#2} \namespacephrase}% + \fi + \LevelSorted{#5}{\texttt{#5} option}% + }{usage}{\thepage}% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\TD@main@index} +% \changes{v\,2.10}{2000/10/14}{Generalized macro. (LH)} +% \changes{v\,2.12}{2000/11/25}{Sorting by base name first, not by +% namespace first. (LH)} +% \changes{v\,2.20}{2001/03/03}{Putting namespace on the second +% level. (LH)} +% The |\TD@main@index| macro has the syntax +% \begin{quote} +% |\TD@main@index|\marg{namespace}\marg{name}\marg{type}^^A +% \marg{extra} +% \end{quote} +% It makes a \texttt{main} index entry. The top level of this entry +% is like an entry for the \meta{name} object of type \meta{type} in +% the \meta{namespace} namespace. The entry may have one or more +% sublevels\footnote{But no more than one extra level is recommended, +% as \package{makeindex} can handle at most three levels.} specified +% in the \meta{extra}; these are then made using explicit +% |\LevelSame| or |\LevelSorted| commands. \meta{name} and +% \meta{namespace} are as for the |\buildname| macro, whereas +% \meta{type} is a description, e.g. `\texttt{proc}', that gets +% appended to the printed text of the entry's top level. +% \begin{macrocode} +\def\TD@main@index#1#2#3#4{% + \XDMainIndex{% + \LevelSorted{#2}{\texttt{#2} (#3)}% + \ifx \NoValue#1\@empty + \ifx \@empty\TD@namespace + \LevelSorted{ }{global \namespacephrase}% + \else + \LevelSorted{\TD@namespace}% + {\texttt{\TD@namespace} \namespacephrase}% + \fi + \else + \ifx $#1$% + \LevelSorted{ }{global \namespacephrase}% + \else + \LevelSorted{#1}{\texttt{#1} \namespacephrase}% + \fi + \fi + #4% + }% +} +% \end{macrocode} +% \end{macro} +% +% \begin{environment}{Cfunction} +% \changes{v\,2.40}{2003/04/05}{Environment added. (LH)} +% \begin{environment}{Cvariable} +% \changes{v\,2.40}{2003/04/05}{Environment added. (LH)} +% \begin{environment}{Ctype} +% \changes{v\,2.40}{2003/04/05}{Environment added. (LH)} +% The \texttt{Cfunction}, \texttt{Cvariable}, and \texttt{Ctype} +% environments are for making a marginal heading, index entry, etc.\@ +% for a C function, variable, or type respectively. They all take as +% their single argument the name of the thing that will be defined. +% \begin{macrocode} +\NewMacroEnvironment*{Cfunction}{\XD@grab@harmless\relax}{1}% + {\XDParToMargin{\small\Cfunctionidentifier{#1} (C~function)}}% + {\XDMainIndex{% + \LevelSorted{#1}{\Cfunctionidentifier{#1} (C~function)}% + }}% + {{#1}{\Cfunctionidentifier{#1} C~function}} + {}% +\NewMacroEnvironment*{Cvariable}{\XD@grab@harmless\relax}{1}% + {\XDParToMargin{\small\Cvariableidentifier{#1} (C~variable)}}% + {\XDMainIndex{% + \LevelSorted{#1}{\Cvariableidentifier{#1} (C~variable)}% + }}% + {{#1}{\Cvariableidentifier{#1} C~variable}} + {}% +\NewMacroEnvironment*{Ctype}{\XD@grab@harmless\relax}{1}% + {\XDParToMargin{\small\Ctypeidentifier{#1} (C~type)}}% + {\XDMainIndex{% + \LevelSorted{#1}{\Ctypeidentifier{#1} (C~type)}% + }}% + {{#1}{\Ctypeidentifier{#1} C~type}} + {}% +% \end{macrocode} +% \end{environment}\end{environment}\end{environment} +% +% \begin{macro}{\Cfunctionidentifier} +% \changes{v\,2.40}{2003/04/05}{Macro added. (LH)} +% \begin{macro}{\Cvariableidentifier} +% \changes{v\,2.40}{2003/04/05}{Macro added. (LH)} +% \begin{macro}{\Ctypeidentifier} +% \changes{v\,2.40}{2003/04/05}{Macro added. (LH)} +% These macros are used by \texttt{Cfunction}, \texttt{Cvariable}, and +% \texttt{Ctype} to format the identifiers of C functions, variables, +% and types respectively; the syntax is e.g. +% \begin{quote} +% |\Cfunctionidentifier|\marg{function name} +% \end{quote} +% They default to typeset them in an italic font---this is what CWEB +% uses---but it is meant to be configurable. +% \begin{macrocode} +\newcommand*{\Cfunctionidentifier}[1]{\textit{#1}} +\newcommand*{\Cvariableidentifier}[1]{\textit{#1}} +\newcommand*{\Ctypeidentifier}[1]{\textit{#1}} +% \end{macrocode} +% \end{macro}\end{macro}\end{macro} +% +% +% \subsection{Regular expressions and friends} +% +% I have noticed that when one is describing the syntax of some \Tcl\ +% command\slash procedure, one often ends up writing argument lists with +% lots of optional, repeated, or variant parts. In general one can of +% course specify these using BNF syntax diagrams, but whereas occurrences +% of the three syntactic constructions mentioned above are plenty, +% there are much fewer occurrences of for example nesting. Since +% furthermore even the simplest optional or repeated element +% requires an extra syntactic term to be defined, it becomes interesting +% to search for an alternative way of specifying these constructions. +% One that is close at hand is that of regular expressions, as that is +% anyway a part of the \Tcl\ language. +% +% \begin{macro}{\regstar} +% \begin{macro}{\regplus} +% \begin{macro}{\regopt} +% These commands are used to modify the meaning of another syntactic +% element. |\regstar| puts a star $^*$ on something, meaning it is +% repeated zero or more times. |\regplus| puts a plus $^+$ on +% something, meaning it is repeated one or more times. |\regopt| puts +% a question mark $^?$ on something, meaning it is optional. Normally +% these commands are simply put right after what they should modify, +% but if the ``something'' is a regular expression block, i.e., a +% \texttt{regblock} environment, then these things should be put as +% the \meta{modifier} in +% \begin{quote} +% |\begin{regblock}|\oarg{modifier}\\ +% | |\textellipsis\\ +% |\end{regblock}| +% \end{quote} +% In particular, they should \emph{not} be put after the +% |\end{regblock}|, as that may impair the placement of the exponent. +% \begin{macrocode} +\newcommand\regstar{\ensuremath{^*}} +\newcommand\regplus{\ensuremath{^+}} +\newcommand\regopt{\ensuremath{^?}} +% \end{macrocode} +% \end{macro}\end{macro}\end{macro} +% +% \begin{macro}{\TD@delim@size} +% \begin{macro}{\TD@delim@size@G} +% These control sequences are used in determining what size the +% delimiters (parentheses) around a regular expression block should +% have. The values specify what should be the least size of a +% delimiter that bracket a piece of text, and are interpreted as +% follows: \(0={}\)normal size, \(1={}\)|\big| size, \(2={}\)|\Big| +% size, \(3={}\)|\bigg| size, and $4$ and above is |\Bigg| size. +% +% |\TD@delim@size@G| is a macro which is always set globally and +% which keeps track of the delimiter size at the innermost level. +% |\TD@delim@size| is a |\count| register that is assigned locally +% and is used for keeping track of delimiter sizes at all other +% levels. Commands like |\word|, which contribute a delimiter to some +% piece of text, should set |\TD@delim@size@G|. +% \begin{macrocode} +\newcount\TD@delim@size +\gdef\TD@delim@size@G{0} +% \end{macrocode} +% \end{macro}\end{macro} +% +% \begin{environment}{regblock} +% The \texttt{regblock} environment embarks upon a rather tricky scheme +% of boxing and unboxing to determine the proper delimiter size. The +% basic idea is that the contents of the environment are first typeset +% in an \texttt{lrbox} environment, and |\TD@delim@size@G| is set to +% its proper value. Then the left delimiter is set at this size, the +% saved box is unboxed, and finally the right delimiter is set. All +% through the process, the value of |\TD@delim@size@G| at the +% beginning of the \texttt{regblock} environment has been saved in +% |\TD@delim@size| and at the end |\TD@delim@size@G| is set to the +% maximum of its old value and one plus its value inside the +% \texttt{regblock} environment. This makes it possible to nest +% \texttt{regblock} environments. +% +% It is not quite that easy, though. A regular expression can consist +% of several branches, which must be separated by vertical bars. +% These bars should be the same size as the delimiters around the +% regular expression, and hence the bars cannot be typeset before +% every branch has been typeset. Therefore the \texttt{regblock} +% environment starts an extra horizontal list (the |\hbox|) for the +% sole purpose of storing the branches that have already been typeset +% (and \emph{only} these). Each branch is put on the list as a box. +% When all branches have been typeset, they are first removed from the +% list (by |\TD@join@branches|), and then they are inserted again, but +% this time they are unboxed and separated by suitable delimiters. +% Finally the |\hbox| is ended and almost immediately unboxed. +% +% \changes{v\,2.40}{2003/04/05}{Renamed the \texttt{regexp} +% environment to \texttt{regblock}. A \texttt{regexp} environment +% is provided for compatibility. (LH)} +% \begin{macrocode} +\newenvironment{regblock}[1][]{% + \leavevmode + \def\TD@modifier{#1}% + \TD@delim@size=\TD@delim@size@G + \let\regalt=\TD@regalt + \setbox\z@=\hbox\bgroup + \TD@delim@size=\z@ + \begin{lrbox}{\z@}% + \gdef\TD@delim@size@G{0}% + \ignorespaces +}{% + \end{lrbox}% + \ifnum \TD@delim@size@G>\TD@delim@size + \TD@delim@size=\TD@delim@size@G\relax + \fi + {\TD@join@branches}% + \unhbox\z@ + $\TD@size@delimiter)\TD@modifier$% + \advance \TD@delim@size \@ne + \xdef\TD@delim@size@G{\the\TD@delim@size}% + \egroup + \ifnum \TD@delim@size@G<\TD@delim@size + \xdef\TD@delim@size@G{\the\TD@delim@size}% + \fi + \unhbox\z@ +} +% \end{macrocode} +% \end{environment} +% +% \begin{environment}{regexp} +% This is an alias to the \texttt{regblock} environment, provided for +% compatibility. +% \begin{macrocode} +\newenvironment{regexp}{\regblock}{\endregblock} +% \end{macrocode} +% \end{environment} +% +% \begin{macro}{\TD@join@branches} +% \changes{v\,2.12}{2000/11/13}{Made penalty for linebreak after +% \cs{regalt} depend on the delimiter size. (LH)} +% The |\TD@join@branches| macro uses |\lastbox| to retrieve all boxes +% in the current horizontal list, and unboxes them. Between the +% unboxed boxes it puts a |\vert| delimiter, and before the first it +% puts a left parenthesis. Both will be the current delimiter size. +% \begin{macrocode} +\def\TD@join@branches{% + \setbox\z@=\lastbox + \ifvoid\z@ + $\TD@size@delimiter($% + \else + {\TD@join@branches}% + \unhbox\z@ + ~$\TD@size@delimiter\vert$% + \count@=\TD@delim@size + \advance \count@ \@ne + \multiply \count@ -\@lowpenalty + \penalty\count@\ % + \fi +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\regalt} +% \begin{macro}{\TD@regalt} +% The |\regalt| command is used in a \texttt{regblock} environment +% to separate two branches. It ends an \texttt{lrbox} environment, +% puts the box collected in the surrounding horizontal list (which +% was begun by the |\hbox| in |\lrbox|), and begins a new +% \texttt{lrbox} environment. As this would not make sense outside a +% \texttt{regblock} environment however, the |\regalt| command does by +% default only produce an error message. The ``real'' definition is +% kept in |\TD@regalt| and the \texttt{regblock} environment locally +% redefines |\regalt|. +% \begin{macrocode} +\newcommand\regalt{% + \PackageError{tclldoc}{% + Lonely \protect\regalt--perhaps a missing regblock environment% + }\@eha +} +\def\TD@regalt{% + \end{lrbox}% + \box\z@ + \ifnum \TD@delim@size@G>\TD@delim@size + \TD@delim@size=\TD@delim@size@G\relax + \fi + \begin{lrbox}{\z@}% + \gdef\TD@delim@size@G{0}% +} +% \end{macrocode} +% \end{macro}\end{macro} +% +% \begin{macro}{\TD@size@delimiter} +% This macro expands to nothing, |\big|, |\Big|, |\bigg|, or |\Bigg| +% depending on the value of |\TD@delim@size|. +% \begin{macrocode} +\def\TD@size@delimiter{% + \ifcase\TD@delim@size + \or \expandafter\big \or \expandafter\Big \or + \expandafter\bigg \else \expandafter\Bigg + \fi +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\word} +% \changes{v\,2.10}{2000/10/13}{Reimplemented like the new \cs{meta}. +% (LH)} +% \changes{v\,2.12}{2000/11/21}{Using braces instead of angle brackets. +% (LH)} +% This command works like |\meta|, but the argument is enclosed in +% braces rather than angle brackets and it communicates with the +% \texttt{regblock} environment (the braces count as a smallest-size +% delimiter). Something marked out using |\word| rather than |\meta| +% is meant to always denote a separate \Tcl\ word. +% \begin{macrocode} +\DeclareRobustCommand\word[1]{% + \leavevmode + \ifmmode \expandafter \nfss@text \fi + {% + $\{$% + \meta@font@select + \edef\meta@hyphen@restore + {\hyphenchar\the\font\the\hyphenchar\font}% + \hyphenchar\font\m@ne + \language\l@nohyphenation + #1\/% + \meta@hyphen@restore + $\}$% + }% + \ifnum \TD@delim@size@G<\@ne \gdef\TD@delim@size@G{1}\fi +} +% \end{macrocode} +% \end{macro} +% +% +% \subsection{Logos} +% +% \begin{macro}{\Tcllogo} +% \changes{v\,2.03}{2000/09/15}{Macro added. (LH)} +% The |\Tcllogo| command makes a \Tcl\ ``logo'', which simply consists +% of the three letters `T', `c', and `l' in the current font. The only +% special thing about it is that there is a kern between the `T' and +% the `c', since most fonts doesn't have that kern. No work has been +% put into tuning the kern, but the size is about right. As a +% comparison the kern between `T' and `o' in \texttt{cmr10} is +% $-0.083334\,\mathrm{em}$. +% \begin{macrocode} +\DeclareRobustCommand\Tcllogo{T\kern-0.1em cl} +% +% \end{macrocode} +% \end{macro} +% +% \changes{v\,2.03}{2000/09/15}{\cs{DescribeResource} command removed. +% Just in case anyone is interested in using it, I have included it as +% a code example in the source for the \package{xdoc} package. (LH)} +% +% +% \section{The \package{tclldoc} class} +% +% \subsection{Option declarations and processing} +% +% \begin{option}{a5paper} +% \begin{option}{olddocinclude} +% The \texttt{a5paper} (to the \package{article} document class) and +% the \texttt{olddocinclude} (to the \package{xdoc} package) are not +% supported for the \package{tclldoc} class. +% \begin{macrocode} +%<*class> +\DeclareOption{a5paper}{% + \ClassError{tclldoc}{The a5paper option is not supported}\@eha +} +\DeclareOption{olddocinclude}{% + \ClassError{tclldoc}{The olddocinclude option is not supported}\@eha +} +% \end{macrocode} +% \end{option}\end{option} +% +% \begin{option}{notrawchar} +% The \texttt{notrawchar} option is passed on to the +% \package{tclldoc} package (which will pass it on to the +% \package{xdoc} package). +% \changes{v\,2.40}{2003/04/05}{Added processing of this option; it +% wouldn't take if it was simply given as a global option. (LH)} +% \begin{macrocode} +\DeclareOption{notrawchar}{% + \PassOptionsToPackage{notrawchar}{tclldoc}% +} +% \end{macrocode} +% \end{option} +% +% +% \begin{macrocode} +\DeclareOption*{% + \PassOptionsToClass{\CurrentOption}{article}} +% \end{macrocode} +% +% +% +% Input a local configuration file, if it exists. +% \begin{macrocode} +\InputIfFileExists{tclldoc.cfg} + {\typeout{*************************************^^J% + * Local config file tclldoc.cfg used^^J% + *************************************}} + {} +% \end{macrocode} +% +% +% +% \begin{macrocode} +\ProcessOptions\relax +% \end{macrocode} +% +% \subsection{Loading the base class} +% +% \begin{macrocode} +\LoadClass{article} +% \end{macrocode} +% +% +% \subsection{The layout} +% +% Increase the text width slightly so that with the standard fonts +% 72 columns of code may appear in a |macrocode| environment. +% \changes{ \package{ltxdoc} v\,2.0c}{1994/03/15}{Set \cs{textwidth}.} +% \begin{macrocode} +\setlength{\textwidth}{355pt} +% \end{macrocode} +% +% Increase the marginpar width slightly, for long command names. +% And increase the left margin by a similar amount +% \changes{ \package{ltxdoc} v\,2.0l} +% {1994/05/25}{Increase \cs{marginparwidth}} +% \changes{ \package{ltxdoc} v\,2.0q}{1995/11/28} +% {Increase \cs{marginparwidth} and page margin.} +% \begin{macrocode} +\addtolength\oddsidemargin{20pt} +\addtolength\evensidemargin{20pt} +% \end{macrocode} +% +% +% Change some defaults for list formatting. In particular, continue to +% indent paragraphs in |\list| environments and don't put extra space +% between them. +% \changes{ \package{pasdoc} v\,1.30}{1999/08/31}{Modifying the list +% formatting. (LH)} +% +% \begin{macro}{\TD@list} +% \begin{macro}{\@listi} +% \begin{macro}{\@listI} +% \begin{macro}{\@listii} +% \begin{macro}{\@listiii} +% \begin{macro}{\@listiv} +% \begin{macro}{\@listv} +% \begin{macro}{\@listvi} +% \begin{macrocode} +\def\@tempa#1{% + \expandafter\def \expandafter#1\expandafter{#1\TD@list}% +} +\def\TD@list{% + \advance \itemsep \parsep + \parsep=\z@\@plus\p@\relax + \advance \itemsep -\parsep + \listparindent=1em\relax +} +\@tempa\@listi +\let\@listI\@listi +\@tempa\@listii +\@tempa\@listiii +\@tempa\@listiv +\@tempa\@listv +\@tempa\@listvi +% \end{macrocode} +% \end{macro}\end{macro}\end{macro}\end{macro}\end{macro}\end{macro} +% \end{macro}\end{macro} +% +% +% \subsection{Loading the \package{tclldoc} package} +% +% \begin{macrocode} +\RequirePackage[dolayout]{tclldoc} +% \end{macrocode} +% +% \begingroup +% Make \verb+|+ be a `short verb' character, but not in the document +% preamble, where an active character may interfere with packages that +% are loaded. +% \DoNotIndex{\|} +% \begin{macrocode} +\AtBeginDocument{\MakeShortTclverb{\|}} +% \end{macrocode} +% \endgroup +% +% As \texttt{.dtx} documents tend to have a lot of monospaced material, +% set up some \texttt{cmtt} substitutions to occur silently. +% \changes{ \package{ltxdoc} v\,2.0p}{1995/11/02}{Add font substitutions} +% \begin{macrocode} +\DeclareFontShape{OT1}{cmtt}{bx}{n}{<-> ssub * cmtt/m/n}{} +\DeclareFontFamily{OMS}{cmtt}{\skewchar\font'60} +\DeclareFontShape{OMS}{cmtt}{m}{n}{<-> ssub * cmsy/m/n}{} +\DeclareFontShape{OMS}{cmtt}{bx}{n}{<-> ssub * cmsy/b/n}{} +% \end{macrocode} +% This substitution is in the standard fd file, but not silent. +% \begin{macrocode} +\DeclareFontShape{OT1}{cmss}{m}{it}{<->ssub*cmss/m/sl}{} +% \end{macrocode} +% +% +% \subsection{More layout} +% +% \changes{v\,2.12}{2000/11/26}{Setting \cs{marginparwidth} so that the +% outer edge of the marginpar is $1\,\mathrm{cm}$ from the paper +% edge. (LH)} +% \changes{v\,2.12}{2000/11/27}{Rearranged code so that +% the \cs{marginparwidth} is set after \package{tclldoc} has been +% loaded; otherwise \package{doc} might overwrite it! (LH)} +% \begin{macrocode} +\setlength\marginparwidth{\evensidemargin} +\addtolength\marginparwidth{1in} +\addtolength\marginparwidth{-\marginparsep} +\addtolength\marginparwidth{-1cm} +% \end{macrocode} +% +% +% \begin{macrocode} +\CodelineNumbered +\DisableCrossrefs +% \end{macrocode} +% +% \begin{macrocode} +\setcounter{StandardModuleDepth}{1} +% +% \end{macrocode} +% +% +% \section{The \package{tcldoc} compatibility package and class} +% +% \changes{v\,2.30}{2001/09/02}{Renamed the package and class to +% \package{tclldoc} to prevent confusion with the \Tcl Doc Perl +% script. Added files \texttt{tcldoc.sty} and \texttt{tcldoc.cls} +% for compatibility. Moved code related to the \texttt{macinputenc} +% option to \texttt{tcldoc.cls}; this option is now unsupported. +% (LH)} +% +% \subsection{Pre-options definitions} +% +% \begin{macro}{\if@rtkinenc@} +% Switch. True iff the \package{rtkinenc} package should be loaded. +% It starts out false. +% \begin{macrocode} +%<*compclass> +\newif\if@rtkinenc@ +% \end{macrocode} +% \end{macro} +% +% +% \subsection{Option declarations and processing} +% +% \begin{option}{macinputenc} +% The \texttt{macinputenc} option enables some special code for +% supporting the typesetting of \Tcl\ code written for the Macintosh +% input encoding (cf.\ the \texttt{applemac} option to the +% \package{inputenc} package). +% \begin{macrocode} +\DeclareOption{macinputenc}{% + \PassOptionsToPackage{applemac}{rtkinenc}% + \@rtkinenc@true +} +% \end{macrocode} +% \end{option} +% +% +% \begin{macrocode} +\DeclareOption*{% + \PassOptionsToClass{\CurrentOption}{tclldoc}} +% \end{macrocode} +% +% +% +% Input a local configuration file, if it exists. +% \begin{macrocode} +\InputIfFileExists{tcldoc.cfg} + {\typeout{*************************************^^J% + * Local config file tcldoc.cfg used^^J% + *************************************}} + {} +% \end{macrocode} +% +% +% +% \begin{macrocode} +\ProcessOptions\relax +% \end{macrocode} +% +% \subsection{Loading the base package or class} +% +% \begin{macrocode} +\LoadClass{tclldoc} +% +%\LoadPackageWithOptions{tclldoc} +% \end{macrocode} +% +% +% \subsection{Input encoding and font representations} +% +% There are some rather tricky issues concerning interpretation of the +% input in \Tcl\ code. If the |\MacroFont| font contains a character that +% correctly represents a character in the input, then that character is +% obviously what one would like to appear in the typeset output. If no +% font in the entire \TeX\ system contains a character that looks like +% the one in the input, then just as obviously must one examine whether +% there exists some other method of specifying that character. In the +% case of \Tcl\ code, a suitable method seems to be to typeset the +% equivalent |\x|\meta{dd} escape sequence instead. It is for the +% purpose of being able to detect these cases that the +% \package{tcldoc} class (if the user so requests) loads the +% \package{rtkinenc} package, rather than the standard \LaTeX\ +% \package{inputenc} package. +% +% The problem of determining for each input character which method of +% representation that should be used---single character or escape +% sequence---does not have a unique solution. Therefore the default +% set-up is to not define any special fakes at all, which means the +% escape sequence method will be chosen in the doubtful cases. There are +% also a couple of options for the \package{tcldoc} class that define +% all (or nearly all) possible fakes---the difference between them lie +% in which fonts are assumed to be present in the system. A user who +% does not like any of these predefined options also has the option of +% putting the corresponding |\Declare|\-|Text|\-|Default|\textellipsis +% \ commands directly into the \texttt{tcldoc.cfg} file. +% +% \begin{macro}{\InputModeCode} +% \begin{macro}{\SetUnavailableAction} +% \begin{macro}{\DeclareInputMath} +% \begin{macro}{\RIE@undefined} +% The \package{tclldoc} package calls some commands defined by the +% \package{rtkinenc} package, but since \package{rtkinenc} is not +% required by the \package{tclldoc} package, the \package{tclldoc} +% package must provide its own definitions, even though these can be +% no-ops. Definition of these commands is delayed until +% |\begin|\nolinebreak[1]|{document}| to avoid problems if the user +% loads the \package{rtkinenc} package after the \package{tclldoc} +% package. +% \begin{macrocode} +%<*pkg> +\AtBeginDocument{% + \providecommand*\InputModeCode{}% + \providecommand*\SetUnavailableAction[1]{}% + \providecommand*\DeclareInputMath[2]{}% + \providecommand*\RIE@undefined[1]{\@inpenc@undefined}% +} +% +% \end{macrocode} +% \end{macro}\end{macro}\end{macro}\end{macro} +% +% \begin{macrocode} +%<*compclass> +\if@rtkinenc@ + \RequirePackage{rtkinenc} +% \end{macrocode} +% +% \begin{macro}{\.} +% \begin{macro}{\H} +% \begin{macro}{\textemdash} +% \begin{macro}{\textendash} +% \begin{macro}{\textexclamdown} +% \begin{macro}{\textquestiondown} +% \begin{macro}{\textquotedblleft} +% The following text commands are defined by \texttt{OT1enc.def}, but +% their definitions will not work for \texttt{OT1}-encoded typewriter +% fonts. Therefore they are redefined, either so that they work +% anyway (|\textexclamdown| and |\textquestiondown|) or so that they +% call |\TextSymbolUnavailable| when \package{rtkinenc} is in code +% mode. This should make things work for the best in most +% reasonable situations. +% +% It could be noted here that the definitions below for |\.| and |\H| +% does not quite behave as if the commands had not been defined at +% all (I'm assuming that \package{rtkinenc} is in code mode here); an +% undefined command would leave its argument to be typeset, but the +% definitions below will swallow it in those cases. +% \begin{macrocode} + \DeclareTextCommand{\.}{OT1}[1]{% + \IfInputModeCode{\TextSymbolUnavailable\.}% + {\add@accent{95}{#1}}% + } + \DeclareTextCommand{\H}{OT1}[1]{% + \IfInputModeCode{\TextSymbolUnavailable\H}% + {\add@accent{125}{#1}}% + } + \DeclareTextCommand{\textemdash}{OT1}{% + \IfInputModeCode{\TextSymbolUnavailable\textemdash}{\char 124 }% + } + \DeclareTextCommand{\textendash}{OT1}{% + \IfInputModeCode{\TextSymbolUnavailable\textendash}{\char 123 }% + } + \DeclareTextCommand{\textexclamdown}{OT1}{!`} + \DeclareTextCommand{\textquestiondown}{OT1}{?`} + \DeclareTextCommand{\textquotedblleft}{OT1}{% + \IfInputModeCode{\TextSymbolUnavailable\textquotedblleft}% + {\char 92 }% + } +% \end{macrocode} +% \end{macro}\end{macro}\end{macro}\end{macro}\end{macro}\end{macro} +% \end{macro} +% +% Several of the definitions in \texttt{applemac.def} can be improved. +% Most of it is about seeing to that all characters have a proper text +% mode definition, so that their unavailability can be trapped. +% +% \begin{macrocode} + \def\@tempa{applemac} + \ifx \@tempa\RIE@encoding + \DeclareInputBoth{173}{\textneq}{\neq} + \DeclareInputBoth{176}{\textinfty}{\infty} + \DeclareInputBoth{177}{\textpm}{\pm} + \DeclareInputBoth{178}{\textleq}{\leq} + \DeclareInputBoth{179}{\textgeq}{\geq} + \DeclareInputBoth{181}{\textmu}{\mu} + \DeclareInputBoth{182}{\textpartial}{\partial} + \DeclareInputBoth{183}{\textSigma}{\Sigma} + \DeclareInputBoth{184}{\textPi}{\Pi} + \DeclareInputBoth{185}{\textpi}{\pi} + \DeclareInputBoth{186}{\textint}{\int} + \DeclareInputBoth{189}{\textohm}{\Omega} + \DeclareInputBoth{194}{\textlnot}{\lnot} + \DeclareInputBoth{195}{\textsurd}{\surd} + \DeclareInputBoth{197}{\textapprox}{\approx} + \DeclareInputBoth{198}{\textDelta}{\Delta} + \DeclareInputBoth{214}{\textdiv}{\div} + \DeclareInputBoth{215}{\textdiamond}{\diamond} + \DeclareInputBoth{218}{\textfractionsolidus}{/} +% \end{macrocode} +% +% Several of the above text commands are not defined in the \LaTeX\ +% kernel. Therefore one must either define them in some encoding, or +% define a default for them, to make them proper text commands. If they +% are not then their unavailability cannot be trapped. |\textapplelogo| +% appears in \texttt{applemac.def}. +% \begin{macrocode} + \ProvideTextCommandDefault{\textneq}{% + \TextSymbolUnavailable\textneq + } + \ProvideTextCommandDefault{\textapplelogo}{% + \TextSymbolUnavailable\textapplelogo + } + \DeclareTextSymbol{\textinfty}{OMS}{49} + \DeclareTextSymbol{\textpm}{OMS}{6} + \DeclareTextSymbol{\textleq}{OMS}{20} + \DeclareTextSymbol{\textgeq}{OMS}{21} +% \DeclareTextSymbol{\textmu}{TS1}{181} + \DeclareTextSymbol{\textpartial}{OML}{64} + \DeclareTextSymbol{\textSigma}{OT1}{6} + \DeclareTextSymbol{\textPi}{OT1}{5} + \DeclareTextSymbol{\textpi}{OML}{25} + \DeclareTextSymbol{\textint}{OMS}{115} + \DeclareTextSymbol{\textohm}{OT1}{10} % Kind of \textOmega +% \DeclareTextSymbol{\textlnot}{TS1}{172} +% \DeclareTextSymbol{\textsurd}{TS1}{187} + \DeclareTextSymbol{\textapprox}{OMS}{25} + \DeclareTextSymbol{\textDelta}{OT1}{1} +% \DeclareTextSymbol{\textdiv}{TS1}{246} + \DeclareTextSymbol{\textdiamond}{OMS}{5} +% \DeclareTextSymbol{\textfractionsolidus}{TS1}{47} + \input{TS1enc.def} + \fi +\fi +% \end{macrocode} +% \begin{macrocode} +\let\if@rtkinenc@=\@undefined +\let\@rtkinenc@true=\@undefined +\let\@rtkinenc@false=\@undefined +% \end{macrocode} +% The |@rtkinenc@| switch isn't needed any more, so it might just as +% well be undefined. +% +% +% \subsection{Useful abbreviations} +% \changes{v\,2.10}{2000/10/11}{\LaTeX\ abbreviation commands from +% \package{ltxdoc} commented out. (LH)} +% +% The commands currently found in this section are inherited from the +% \package{ltxdoc} package, but as they aren't of much use with \Tcl, +% they have been commented out. +% +% |\cmd{\foo}| Prints |\foo| verbatim. It may be used inside moving +% arguments. |\cs{foo}| also prints |\foo|, for those who prefer that +% syntax. (This second form may even be used when |\foo| is |\outer|). +% \begin{macro}{\cmd} +% \begin{macro}{\cs} +% \DoNotIndex{\\} +% \begin{macrocode} +% \def\cmd#1{\cs{\expandafter\cmd@to@cs\string#1}} +% \def\cmd@to@cs#1#2{\char\number`#2\relax} +% \DeclareRobustCommand\cs[1]{\texttt{\char`\\#1}} +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\marg} +% |\marg{text}| prints \marg{text}, `mandatory argument'. +% \DoNotIndexHarmless{\PrintChar{`\{}} +% \DoNotIndexHarmless{\PrintChar{`\}}} +% \begin{macrocode} +% \providecommand\marg[1]{% +% {\ttfamily\char`\{}\meta{#1}{\ttfamily\char`\}}} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\oarg} +% |\oarg{text}| prints \oarg{text}, `optional argument'. +% \begin{macrocode} +% \providecommand\oarg[1]{% +% {\ttfamily[}\meta{#1}{\ttfamily]}} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\parg} +% |\parg{te,xt}| prints \parg{te,xt}, `picture mode argument'. +% \begin{macrocode} +% \providecommand\parg[1]{% +% {\ttfamily(}\meta{#1}{\ttfamily)}} +% +% \end{macrocode} +% \end{macro} +% +% +% +% +% \changes{v\,2.00}{2000/07/17}{Section with \cs{DocInclude} removed, +% since that is defined by \package{xdoc}. (LH)} +% +% \Finale +% +\endinput + diff --git a/macros/latex/contrib/tclldoc/tclldoc.ins b/macros/latex/contrib/tclldoc/tclldoc.ins new file mode 100644 index 0000000000..aeb0a96841 --- /dev/null +++ b/macros/latex/contrib/tclldoc/tclldoc.ins @@ -0,0 +1,51 @@ +% This file is the docstrip installation script for the tclldoc package. +% +% It may be distributed under the conditions of the LaTeX +% Project Public License, either version 1.2 of this license or +% (at your option) any later version. The latest version of this +% license is in +% http://www.latex-project.org/lppl.txt +% and version 1.2 or later is part of all distributions of LaTeX +% version 1999/12/01 or later. +% +% Copyright 2000 2001 Lars Hellstr\"om + +\input docstrip.tex + +\preamble + +This file may be distributed and/or modified under the conditions +of the LaTeX Project Public License, either version 1.2 of this +license or (at your option) any later version. The latest version +of this license is in + http://www.latex-project.org/lppl.txt +and version 1.2 or later is part of all distributions of LaTeX +version 1999/12/01 or later. + +This file may only be distributed together with a copy of the source +file from which it was generated. You may distribute that source file +without this generated file. + +\endpreamble + +\usedir{tex/latex/tclldoc} + +\keepsilent + +\generate{% + \file{tclldoc.cls}{% + \from{tclldoc.dtx}{class} + }% + \file{tclldoc.sty}{% + \from{tclldoc.dtx}{pkg} + }% + \file{tcldoc.cls}{% + \from{tclldoc.dtx}{compclass} + }% + \file{tcldoc.sty}{% + \from{tclldoc.dtx}{comppkg} + }% +} + +\endbatchfile +\endinput diff --git a/macros/latex/contrib/tclldoc/tclldoc.pdf b/macros/latex/contrib/tclldoc/tclldoc.pdf new file mode 100644 index 0000000000..c2646fff73 Binary files /dev/null and b/macros/latex/contrib/tclldoc/tclldoc.pdf differ diff --git a/macros/latex/contrib/tclldoc/tools/README.txt b/macros/latex/contrib/tclldoc/tools/README.txt new file mode 100644 index 0000000000..c8fd744afd --- /dev/null +++ b/macros/latex/contrib/tclldoc/tools/README.txt @@ -0,0 +1,46 @@ +README for eemenu and sourcetcl: +===================================== + +eemenu is a package for the Alpha/Alphatk text +editors. Among other things, it implements +a command that makes it possible to evaluate +all Tcl code in an open .dtx file window with +a single keypress. For more information, run +LaTeX on eemenu.dtx. + +Running LaTeX on eemenu.ins generates the two +files + eemenu.tcl + eefor7menu.tcl +Both implement the same commands, but eemenu.tcl +requires version 8 of the AlphaTcl library, +whereas eefor7menu.tcl requires version 7.5 or +7.6 of that library. + +See http://alphatcl.sourceforge.net/ and the +links there for more information about the +Alpha/Alphatk text editors. + + + +sourcedtx implements a Tcl command dtx::source +that one can use to source Tcl code directly from +a .dtx file. To generate sourcetcl.tcl, run LaTeX +on sourcetcl.ins. To typeset the documentation, +run LaTeX on sourcetcl.dtx. To see an example of +what it does, you can try the following commands +in a Tcl shell: + + source sourcedtx.tcl + dtx::source ../examples/pdf.dtx {pkg example1} + +(It assumes the tclldoc/examples directory is a +sibling of the current directory.) This should +generate a file hello.pdf in the current +directory, which contains a single page with the +two words "Hello world!". + + + +Lars Hellström, +20 July 2003 diff --git a/macros/latex/contrib/tclldoc/tools/eemenu.dtx b/macros/latex/contrib/tclldoc/tools/eemenu.dtx new file mode 100644 index 0000000000..d2383b7c1e --- /dev/null +++ b/macros/latex/contrib/tclldoc/tools/eemenu.dtx @@ -0,0 +1,4477 @@ +% +% \iffalse +%<*driver> +\documentclass[a4paper]{tclldoc} + +\newcommand{\eemenu}{\textsf{EE} menu} +\DeclareRobustCommand\Alpha{\textit{Alpha}} +\DeclareRobustCommand\AlphaEight{\textit{Alpha\,8}} +\DeclareRobustCommand\AlphaTk{\textit{Alphatk}} +\DeclareRobustCommand\AlphaTcl{\textit{Alpha\Tcllogo}} +\DeclareRobustCommand\TclAE{\Tcllogo\AE} +\DeclareRobustCommand\package[1]{\textsf{#1}} + +\providecommand{\href}[2]{#2} + +\usepackage{xdoc2} +\newenvironment{details}[1]{% + \description + \def\commandmethod{#1}% +}{\enddescription} +\makeatletter +\newcommand{\detailitem}{% + \XD@grab@arguments{\@detailitem}{\XD@grab@harmless\relax}% +} +\def\@detailitem#1{% + \item[\texttt{#1}% + \IndexEntry{\LevelSame{\commandmethod\ method}% + \LevelSorted{#1}{\texttt{#1} detail}% + }{main}{\TheXDIndexNumber}% + ]% +} +\makeatother + +% \addtolength{\marginparwidth}{0.7cm} + +\CodelineIndex +\setcounter{IndexColumns}{2} + +\begin{document} +\DocInput{eemenu.dtx} +\PrintIndex +\end{document} +% +% \fi +% +% \title{The \eemenu} +% \author{Lars Hellstr\"om} +% \date{17 July 2003} +% \maketitle +% +% +% \begin{abstract} +% The \eemenu\ package for the \Alpha\ family of text editors provides +% commands for sending small pieces of code from a source file window +% to various interpreters. It can handle multiple languages and +% multiple source file formats. +% \end{abstract} +% +% \tableofcontents +% +% +% \section{Introduction} +% +% When writing code for an interpreted language, it is a great +% convenience to be able to test arbitrary pieces of code by sending +% them to a relevant interpreter and examining the results. Many of +% \Alpha's modes provide such functionality, but not always with the +% full generality one would need. Furthermore these features are usually +% tied to the specific mode and thus become much less convenient if code +% in several different languages are mixed in the same file. The +% \eemenu\ provides a general mechanism for constructing new commands +% of this kind, thereby making it possible to better tailor them for the +% case at hand. +% +% +% \subsection{Using an \eemenu\ command} +% +% The basic usage is straightforward: you select a piece of text and +% invoke one of the \eemenu\ commands. This will \emph{extract} +% the part of that selection which is code and send it to the relevant +% interpreter, where it will be \emph{evaluated}. (Indeed, the \textsf{EE} +% in \eemenu\ is for ``extract and evaluate''.) You will also get +% a \emph{report} (by default a message on the status line) about the +% result of the command. This is all quite similar to how the +% \textsf{Evaluate} command in the \Tcllogo\ menu works, but it is much +% more general. +% +% Two very natural questions at this point are of course ``How does it +% know what part of the selection is `code'?'' and ``How does it know +% which the `relevant interpreter' is?''. The answer to both these +% questions is that this information is built into the particular command +% you invoke. Just as the standard \textsf{Evaluate} command has its +% definition of what `code' is (the entire selection is code) and which +% the `relevant interpreter' is (\Alpha's internal \Tcllogo\ +% interpreter), each \eemenu\ command has built-in definitions of +% these things. The difference is that with \textsf{Evaluate} you would +% have to do some programming to change these things, whereas with the +% \eemenu\ commands you don't, since there is a multitude of ways in +% which each part of the exatract-and-evaluate process can be configured. +% +% To begin with, each \eemenu\ command consists of four pieces which +% are quite independent of each other. When the \emph{extract} piece is +% called it looks at the selection and returns the next line of code in +% it, or says that there are no more code lines that can be extracted. +% What happens to the extracted lines of code is of no concern to the +% extractor. The \emph{evaluate} piece takes a list of lines and sends +% them to an interpreter (internal or external). It also compiles a +% sort of report on the result of the evaluation, based on the reply +% (if there was any) that it recieves from the interpreter. This is +% then given to the \emph{reporter}, which displays the report (or more +% often, for brevity, the central part of the report) to the user. The +% fourth piece is called the \emph{completeness test} and determines +% whether a list of code lines constitute a unit that can be evaluated. +% +% The way it goes is that lines are extracted until the completeness +% test says OK, then the lines are sent for evaluation and the report on +% evaluation is sent to the reporter, after which the whole thing starts +% again and lines are extracted until the form a new complete unit, +% which is then evaluated, and so on. The \eemenu\ command doesn't stop +% until all code lines in the selection have been extracted or evaluation +% reports an error. +% If there is an error in evaluation then the report will say so, and +% futhermore the cursor will be moved to the beginning of the first +% line in the code unit that failed to evaluate correctly. Furthermore +% the pin will be moved to the end of the selection, so that a simple +% \textsf{Hilite} will reselect the part of the original selection that +% still has not been successfully evaluated. If there wasn't any +% selection to start with then the extractor behaves as if the entire +% file was selected. +% +% +% \subsection{Editing and invoking \eemenu\ commands} +% +% Each of the four pieces of what a command does are handled by a +% \emph{method}. There are extraction methods, completeness test +% methods, evaluation methods, and report methods, all of which can be +% combined arbitrarily to make up a new \eemenu\ command. This is done +% in the \textsf{Edit Commands} dialog, which is brought up by +% choosing that item in the \textsf{EE} menu. +% +% The \textsf{Edit Commands} dialog has one page for each \eemenu\ +% command that is defined. At the bottom of the dialog there is a row +% of four buttons \textsf{New}, \textsf{Duplicate}, \textsf{Rename}, and +% \textsf{Delete} which add, remove, or rename \eemenu\ commands. Above +% those are four pop-up menus, in which one chooses which four methods +% the command should consist of. Above these are four other items which +% control how the command is invoked. +% +% There are two ways of invoking an \eemenu\ command. One possibility +% is to choose it directly in the \textsf{EE} menu, although it is not +% necessarily the case that all commands are available there. Whether a +% command is available is controlled through the \textsf{Put in menu} +% item on the \textsf{Edit Commands} dialog page of that command. The +% other possibility of invoking a command is through a key binding, and +% that is controlled through the three remaining items \textsf{Active}, +% \textsf{Command keybinding}, and \textsf{Binding mode} in the +% \textsf{Edit Commands} dialog. The \textsf{Command keybinding} item +% controls which key binding one should use for a command. The +% \textsf{Binding mode} item is used to specify a mode for this +% binding; a binding without mode will be global. Finally, a command is +% said to be \emph{active} if there is a key binding to it. The +% \textsf{Active} item controls whether the command should be active by +% default (at startup). +% +% For temporary activation and deactivation of commands, one should +% instead use the \textsf{Active Commands} submenu of the \textsf{EE} +% menu. This submenu lists all \eemenu\ commands. Those that are +% currently active have a check mark next to them, and selecting an item +% in this menu toggles activation state of that command. If two commands +% have the same binding then activating one will deactivate the other. +% +% Besides the items mentioned above, the \textsf{Edit Commands} dialog +% also has a \textsf{Details} button in the lower left corner. This +% button is very important, since it brings up a subdialog that shows +% all the method-specific settings. The methods and their details are +% described below, but it should be observed here that it is to a large +% extent these detail settings that decide, amogst other things, what +% text should count as extractable code and which interpreter should +% evaluate the code. To get back from the details subdialog to the main +% \textsf{Edit Commands} dialog, one presses the \textsf{Back} button. +% The details subdialog is logically a part (which is normally hidden) +% of the main \textsf{Edit Commands} dialog. Changes to items in the +% dialog, whether those appear in the main dialog or in the subdialog, +% are kept if one presses \textsf{OK} in the main dialog and forgotten +% if one presses the \textsf{Cancel} button. +% +% +% \subsection{Extraction methods and their details} +% +% The default extraction method is the \textsf{Raw} method. It +% considers the entire selection to be extractable code and has no +% detail settings. +% +% The \textsf{Regexp} extraction method uses line-oriented regular +% expressions to decide what is extractable code. This extraction +% process works in two steps. First the lines in the selection are +% tested against a filter, and those lines that passes are considered to +% contain code. The filter works in one of three modes: \texttt{off} +% (all lines pass), \texttt{grep} (only those lines that match a +% specific regular expression passes), and \texttt{anti-grep} (only +% those lines that \emph{don't} match a specific regular expression +% passes). The filter mode and the regular expression used in the +% filter are the two detail settings that affect the filter. +% The second step in \textsf{Regexp} extraction is to feed each line +% that passes the filter through |regsub|. This can be used to remove +% from the lines some piece of text that is markup rather than part of +% the code. Both the search and the replace regular expressions in this +% step are detail settings of the \textsf{Regexp} extraction method. +% +% The \textsf{Docstrip} extraction method, finally, emulates what the +% \textsf{docstrip} program\footnote{\textsf{docstrip} is part of the +% standard \LaTeX\ distribution.} would do with the selection. It +% recognises all kinds of guards and processes them correctly, but it +% does not attempt to evaluate the guard expressions---instead it will +% ask the user whether modules guarded by a given expression should be +% included whenever it needs to know this. The values assigned to the +% guard expressions will be forgotten each time the entire selection +% is evaluated without error. Unlike \textsf{docstrip} the program, +% \textsf{Docstrip} the extraction method will not include lines +% beginning with |%%| in what is extracted, since those anyway only +% contains comments. +% +% Besides the filtering that \textsf{docstrip} the program would do, +% \textsf{Docstrip} the extraction method has two extra filtering +% mechanisms that are designed mainly to prevent user errors. The first +% is that extraction will only take place if the file name matches one +% listed in the \textsf{File patterns} detail. The default pattern is +% \texttt{*.dtx} which is usually what one wants, but changing it to +% \texttt{*} will let anything through. The second mechanism, which is +% off by default, sees to that code lines will only be considered for +% extraction if they appear to be in an environment for code lines +% (such as \texttt{macrocode}). This is useful if you in places tend to +% leave a few lines of documentation not commented out during +% development. To activate this mechanism, check the \textsf{Filter by +% environments} detail. The \textsf{Source environments} detail is the +% list of environments that are OK to extract from. +% +% As an example of how this works, consider a file that looks as +% follows: +% \DontCheckModules\iffalse +%<*example> +%< +append a B +% +append a C +%<*andD> +append a D +% +expr $a +string length $a +% \end{tcl} +% text text text. +% \end{macrocode}\iffalse +%EXAMPLE +% +% \fi \CheckModules +% If lines 1--7 above are selected and the \textsf{dtx -\textgreater\ +% internal Tcl} command is invoked (the \eemenu\ by default binds this +% to \textsf{Cmd--L}), then three messages will flash by on the +% status line, the last (and therefore the lasting) of which is the +% message \texttt{Tcl eval OK: ABC}. If using instead a command that +% uses the \textsf{Log Window} report method, then in addition to this +% one would have the following lines added to the log window: +% \begin{trivlist}\item\small +% |% set a A|\\ +% |A|\\[\smallskipamount] +% |% append a B|\\ +% |AB|\\[\smallskipamount] +% |% append a C|\\ +% |ABC| +% \end{trivlist} +% The |%| lines show the commands that were evaluated and the +% lines after these contain the respective results of these commands. +% Before the |append a B| command is evaluated, \Alpha\ will stop and +% ask +% \begin{quote} +% \textsf{Should \textless andB\textgreater\ modules be included?} +% \end{quote} +% In the case above the answer given was `Yes'; if it had been `No' then +% the |append a B| command would neither be logged nor evaluated, and +% the result of the |append a C| command would have been |AC|. +% +% If one continues by selecting lines 9--12 and invokes the same +% command again then the status line will settle for the message +% ``\texttt{Tcl\ eval\ error:\ syntax\ error\ in\ expression\ "ABCD"}'', +% the insertion point will be moved to the beginning of line 11, and +% the following information will be found in the log: +% \begin{trivlist}\item\small +% |% append a D|\\ +% |ABCD|\\[\smallskipamount] +% |% expr $a|\\ +% |Error: syntax error in expression "ABCD"|\\ +% |Error info:|\\ +% |syntax error in expression "ABCD"|\\ +% | while executing|\\ +% |"expr $a"| +% \end{trivlist} +% If then line 11 is changed to |expr {$a}|, lines 11--12 are again +% selected (e.g. using \textsf{Mark Hilite}), and the command is invoked +% then logged material will be: +% \begin{trivlist}\item\small +% |% expr {$a}|\\ +% |ABCD|\\[\smallskipamount] +% |% string length $a|\\ +% |4| +% \end{trivlist} +% +% Finally, if lines 3--12 are selected and the command is invoked then +% the user will be asked the following questions: +% \begin{itemize} +% \item \textsf{Should \textless andB\textgreater\ modules be included?} +% (Possible answers are `Yes' and `No'.) +% \item \textsf{Should \textless andD\textgreater\ modules be included?} +% (Possible answers are `Yes' and `No'.) +% \item \textsf{Module nesting error: \textless*andD\textgreater\ +% module ended by \textless/andB\textgreater. For which +% guards should the positions be pushed?} +% (Possible answers are `None', `Start', `End', and `Both'.) +% \end{itemize} +% In the last case, the extraction method has noticed that the guard +% expressions that began (\textsf{andD}) and ended (\textsf{andB}) a +% module are not equal (which is an error). Answering `Both' here will +% push the positions of (the beginning of) both guard lines (8 and 10) onto +% the bookmark stack so that they can easily be jumped to and corrected +% later. After this, the \eemenu\ command will continue as if nothing had +% happened. +% +% +% \subsection{Completeness tests} +% +% The three completeness tests that come with \eemenu\ are +% \textsf{\Tcllogo\ Info Complete}, \textsf{Entire Selection}, and +% \textsf{Every Line}. The first of these uses the |info complete| +% command in \Alpha's built-in \Tcllogo\ interpreter test whether the +% list of lines extracted so far constitute a complete \Tcllogo\ +% script. \textsf{Entire Selection} and \textsf{Every Line} don't look +% at the extracted lines at all however; instead the \textsf{Entire +% Selection} test waits until the entire selection has been extracted +% before it says it does constitute a complete unit, whereas +% \textsf{Every Line} thinks every line is a complete unit. None of +% these methods have any details. +% +% +% \subsection{Evaluation methods} +% +% The list of available evaluation methods depends very much on the +% current platform. The basic \eemenu\ package contains code for four +% methods named \textsf{Internal Tcl}, \textsf{Do Script AE}, \textsf{Tk +% Send}, and \textsf{Windoze DDE}, but only those for which the +% underlying command can be found in the interpreter are actually +% defined. The effect is usually that only two methods are available: +% the \textsf{Internal Tcl} method and one method which depends on the +% platform. +% +% The \textsf{Internal Tcl} method joins the lines of code with +% linefeeds and passes the resulting string to \Alpha's internal +% \Tcllogo\ interpreter for evaluation (at the global level). Errors +% are caught and reported with a complete |errorInfo|. There are no +% details for this method. +% +% The \textsf{Do Script AE} method sends a \texttt{dosc} AppleEvent with +% the code to evaluate; hence it is only available on Mac~OS. The target +% application for this event is one of the detail settings. The `Wait +% for reply' detail setting controls whether the AppleEvent requests a +% reply (not all target applications will provide one). If no reply is +% requested then the report will only tell how many lines were sent. +% If a reply is requested then that reply will be the report. Finally, +% there are three detail settings that control how the list of lines +% are converted to the string put in the \texttt{dosc} AppleEvent. The +% lines can be joined using either of the following four strings: line +% feed, carriage return, carriage return plus line feed, or just a +% single space. It is also possible to specify a prefix and a suffix, +% which are strings put before and after the extracted code that should +% be evaluated. +% +% The \textsf{Tk Send} method uses the Tk |send| command to communicate +% with the target interpreter; hence it is probably only useful with +% \AlphaTk\ in an X-windows environment (typically on some Unix platform). +% Most of the details work as for the \textsf{Do Script AE} method, but +% there is a difference in how the target is identified. One detail +% setting is the string used for identifying the target, but since the +% names of targets for |send| are made unique on each server, there is +% a chance that the intended target has not got the expected name. In +% that case, a mechanism called `aliasing' will be invoked. It usually +% displays a list of the available targets (which includes the option to +% start a new Wish process) for the user to choose from, and the target +% detail setting will then be considered to be an \emph{alias} for the +% actual name of that target. If the alias behaviour is to ``Ask once'' +% then this choice will be remembered as long as \Alpha\ is running, but +% if it is to ``Ask each time'' then it will only work for this invocation +% of the \eemenu\ command. The behaviour can also be to fail, in which +% case the command stops as for an error without presenting the user with +% a list of available targets. +% +% The \textsf{Windoze DDE} method uses Dynamic Data Exchange to communicate +% with the target interpreter; hence it is probably only useful with +% \AlphaTk\ on the Windows platform. Most of the details work as for the +% \textsf{Tk Send} method, but there are a few extra details that have +% to do with how the target is identified. The target is identified with +% two details called \textsf{Service} and \textsf{Target}. For sending +% commands to a \Tcllogo\ interpreter it seems like the \textsf{Service} +% should be \texttt{TclEval} and the \textsf{Target} is assigned as the +% \textsf{Target} for the \textsf{Tk Send} method, but the \Tcllogo\ +% manual seems to suggest that the normal case is that the +% \textsf{Service} is the name of an application and the +% \textsf{Target} the name of a document open in that application (I +% haven't had any chance to try it out in practice). Aliasing works as +% for the \textsf{Tk Send} method, but since the target isn't +% necessarily a \Tcllogo sh or Wish in this case, the actual command for +% launching the new target must be configurable as well. This is the +% purpose of the \textsf{Launch command} detail, whose value is a piece +% of \Tcllogo\ code that \Alpha\ evaluates at the global level whenever +% the user requests that a new target is launched. It is typically an +% |exec| command. +% +% I am well aware that there are interpreters for which neither of these +% methods may be appropriate. The method concept has however deliberately +% been designed so that separate \AlphaTcl\ extensions can define new +% methods that are just as easily available as the built-in ones. I +% recommend that anyone who finds the available methods insufficient +% tries to work out how one might otherwise communicate with the target +% interpreter, so that the capabilities of \eemenu\ can be extended. One +% particular possibility that should be explored is to (on Unix) hook up +% interpreters as `slaves' of \Alpha\ (seizing control of +% \texttt{stdin}, \texttt{stdout}, and \texttt{stderr}), so that the +% target interpreter thinks it is getting text typed at an interactive +% prompt. Since one would typically not want to start a new such `slave' +% for each time an \eemenu\ command is invoked, it would probably be +% necessary to add some functionality for managing slaves to the +% \eemenu. +% +% +% \subsection{Report methods} +% +% The default report method is \textsf{Status Line}, which shows the +% most recent evaluation result on the status line. This is often +% sufficient when all you're interested in is whether there was an +% error or not. +% +% The \textsf{Log Window} report method creates a detailed log of the +% commands evaluated and the results returned. This can be useful for +% example when one is trying to debug a complex mathematical calculation +% with several intermediate results. The first two details of this +% method---\textsf{Window name} and \textsf{Window mode}---refer to the +% window used to log the results. The logged text is inserted at the +% end of this window. If no window with the specified name exists, then +% a new one with mode \textsf{Window mode} and the shell flag set is +% opened. The other details---\textsf{Prompt} and +% \textsf{Antiprompt}---are text strings that are prepended to the +% lines inserted into the log window. The \textsf{Prompt} is prepended +% to extracted code lines, whereas the \textsf{Antiprompt} is prepended +% to result lines. +% +% +% \subsection{Writing new methods} +% +% A method consists of a couple of procedures which reside in a child +% namespace of the \texttt{eemenu} namespace. Declaring a new method +% simply amounts to writing these procedures and defining the detail +% settings for the method. The general relation between method names and +% namespace names is explained in Subsection~\ref{Ssec:Namespaces}. It +% is suggested that one uses the \texttt{eemenu}\namespaceseparator +% \texttt{define\_detail} command of Subsection~\ref{Ssec:CmdDialogs} +% for defining detail settings. Different kinds of methods need to +% provide different procedures for the main executive to call. The +% interfaces used are described in the beginnings of +% Sections~\ref{Sec:Extraction}--\ref{Sec:Report}. +% +% +% +% +% \section{Main menu functionality} +% +% The rest of this paper describes the implementation of \eemenu\ and +% its methods. The information here is useful mainly for those who want +% to extend \eemenu\ by writing additional methods for it or otherwise +% modify the code, but it could of course be of interest also for +% advanced users. +% +% +% \subsection{Initialization} +% +% \changes{v\,0.1}{2001/12/15}{Renamed package from \textsf{Eval~Menu} +% to \textsf{EE~Menu}. (LH)} +% \changes{v\,0.3}{2003/04/02}{Updated code for \AlphaTcl\,8. (LH)} +% \changes{v\,0.3.1}{2003/07/19}{Added help. (LH)} +% +% \begin{tcl} +%<*pkg> +alpha::menu eeMenu 0.3.1 global "EE" { + auto_load eemenu::main +} { + eemenu::init_bindings + eemenu::build_menu +} { + eval eemenu::deactivate [array names eemenu::cmdA] +} requirements { +% alpha::package require AlphaTcl 7.5b1 +% alpha::package require AlphaTcl 8.0d1 +} uninstall {this-file} help { + The EE menu provides commands for sending small pieces of code + from a source file window to various interpreters. It can handle + multiple languages and multiple source file formats. + + The commands are built by combining four simple pieces called + methods. The choice of methods and parameters for methods to + use in a command is completely configurable through a dialog. + It is also possible to write extensions that define additional + methods and provide these through the same easy interface. + + See the file eemenu.dtx for the full documentation. +} maintainer\ +% [list "Lars Hellstr[text::Ascii 154 1]m" Lars.Hellstrom@math.umu.se] +% [list "Lars Hellstr\u00f6m" Lars.Hellstrom@math.umu.se] +namespace eval eemenu {} +% \end{tcl} +% \setnamespace{eemenu} +% +% +% \subsection{Methods and namespaces} +% \label{Ssec:Namespaces} +% +% Each method for doing something is put in its own namespace, which +% must be a child of the |eemenu| namespace. +% +% \begin{proc}{Prettify} +% \begin{proc}{Unprettify} +% In dialogs and preferences, method names are kept in ``prettified'' +% form, similar to that used in most of \Alpha's menus. Since the +% preference values must be restored to ``unpretty'' form before they +% can be used, \eemenu\ has two procedures of its own that handle +% these conversions, although the conversion is almost always the same +% as the conversion that the standard |quote::Prettify| procedure +% would make. The syntaxes are +% \begin{quote} +% |eemenu::Prettify| \word{unpretty string}\\ +% |eemenu::Unprettify| \word{pretty string} +% \end{quote} +% \begin{tcl} +proc eemenu::Prettify str { + set a [string toupper [string index $str 0]] + regsub -all {([^A-Z])([A-Z])} [string range $str 1 end] {\1 \2} b + regsub -all {((La|Bib|Oz|CMac) )?Te X} $a$b {\2TeX } a + return $a +} +proc eemenu::Unprettify str { + regsub -all { } $str {} a + return "[string tolower [string index $a 0]][string range $a 1 end]" +} +% \end{tcl} +% For these procedures to be inverses of each other, unpretty strings +% must begin with a lower case letter and not contain any spaces. +% \end{proc}\end{proc} +% +% \begin{arrayvar}{extract}[method] +% \begin{arrayvar}{complete}[method] +% \begin{arrayvar}{evaluate}[method] +% \begin{arrayvar}{report}[method] +% A method declares itself by setting its entry in the |extract|, +% |complete|, |evaluate|, or |report| respectively array. The +% entries in these arrays are lists of dialog item names, more +% precisely the names of those setting details that are relevant for +% the method in question. The indices into these arrays are unpretty +% method names. +% \end{arrayvar}\end{arrayvar}\end{arrayvar}\end{arrayvar} +% +% Most method types employ a model with distinct ``begin'', ``iterate'', +% and ``end'' procedures that the main executive calls. For each command +% and method, the ``begin'' is called first once, then the ``iterate'' +% may be called any number of times, and last the ``end'' is called once. +% The details to use for the method are only provided in the call to +% ``begin'' as that procedure is expected to store the relevant data +% into variables private to the method (residing in its namespace). In +% most cases, the actual code being evaluated is only handled by the +% ``iterate'' procedure, and only the ``end'' procedure gets to know +% how it all turned out. +% +% In many methods, some of these procedures do nothing at all. This is +% then because there is nothing that needs to be done for that particular +% method. +% +% \begin{proc}{multiupvar} +% The |multiupvar| procedure does |upvar #0| for a number of +% variables, prepending the namespace of the caller to each of the +% \word{other-var} arguments of |upvar|. The syntax is +% \begin{quote} +% |eemenu::multiupvar| \word{my-var}\regplus +% \end{quote} +% \changes{v\,0.2}{2002/12/26}{Corrected a minor bug: one must look +% at the first element in the list that info level returns. (LH)} +% \begin{tcl} +%<*!atcl7> +proc eemenu::multiupvar {args} { + foreach var $args {uplevel 1 [list variable $var]} +} +% +%<*atcl7> +proc eemenu::multiupvar {args} { + if {[info level]>1} then { + set ns [lindex [info level -1] 0] + set t [string last :: $ns] + if {$t<0} then {set ns {}} else { + set ns [string range $ns 0 [expr {$t+1}]] + } + } else {set ns {}} + set t 1 + set call [list upvar #0] + foreach var $args {lappend call ${ns}${var} $var} + uplevel 1 $call +} +% +% \end{tcl} +% \end{proc} +% +% +% \subsection{The main executive} +% +% \begin{arrayvar}{cmdA}[command name] +% The |cmdA| array stores all definitions of \eemenu\ commands. The +% indices into the array are the command names. The entries are +% key--value lists which contain the settings for the command. +% +% The \describestring[key]{active}|active|, +% \describestring[key]{binding}|binding|, and +% \describestring[key]{mode}|mode| keys have to do with the key +% binding for this command. If |active| is |1| then the binding should +% be made upon startup. |binding| is the actual binding to use. |mode| +% is the mode for which the binding should be made; if this is +% || then the binding should be global. +% +% The \describestring[key]{in_menu}|in_menu| key controls whether +% this command should appear as a command in the main \textsf{EE} +% menu. +% +% The \describestring[key]{extractor}|extractor|, +% \describestring[key]{complete}|complete|, +% \describestring[key]{evaluator}|evaluator|, and +% \describestring[key]{reporter}|reporter| keys store the particular +% methods that are used by the command. The values are prettified +% method names. For each of these keys there is also an \dots|_extra| +% key whose value is the key--value list of detail settings for that +% method. The exact names are \describestring[key]{extract_extra} +% |extract_extra|, \describestring[key]{complete_extra} +% |complete_extra|, \describestring[key]{eval_extra}|eval_extra|, and +% \describestring[key]{report_extra}|report_extra|. +% +% If the |cmdA| array isn't set then it will be initialised in +% Section~\ref{Sec:DefCmdA}. +% \end{arrayvar} +% +% +% +% \begin{proc}{main} +% The |main| procedure is meant to be called from key binding scripts +% and menu procs to actually carry out an \eemenu\ command. The +% syntax is +% \begin{quote} +% |eemenu::main| \word{command name} \meta{selection-args} +% \end{quote} +% The \word{command name} is the name of the \eemenu\ command. The +% \meta{selection-args} can be used to specify what the command +% should extract from; they are passed straight on to the extraction +% method |start| procedure. +% +% If there was an error during the execution of the command then |main| +% terminates with an error. There is no particular result from this +% procedure. +% +% The first part of |main| calls the begin procedures of the methods +% used. +% \begin{tcl} +proc eemenu::main {command args} { + global eemenu::cmdA + if {![info exists eemenu::cmdA($command)]} then { + error "Command '$command' undefined." + } + array set CMD [set eemenu::cmdA($command)] +% \smallskip + set extract [eemenu::Unprettify $CMD(extractor)] + set cmpl [eemenu::Unprettify $CMD(complete)] + set eval [eemenu::Unprettify $CMD(evaluator)] + set report [eemenu::Unprettify $CMD(reporter)] +% \smallskip + set win\ + [eval [list eemenu::${extract}::start $CMD(extract_extra)] $args] + upvar #0 eemenu::${cmpl}::complete complete + eemenu::${report}::log_open $CMD(report_extra) $win +% \end{tcl} +% The main part of |main| is the following loop over extracted lines. +% The |res| variable stores the last result returned by the evaluation +% method. The |safe| variable is a boolean for whether the current +% position should be considered safe. The |lines| variable contains +% the lines extracted but not yet evaluated. The |at_end| variable is +% |0| until the end of the extractable portion of the code has been +% encountered. +% \begin{tcl} + set res [eemenu::${eval}::begin $CMD(eval_extra) $win] + eval [list eemenu::${report}::log_result] $res + set lines [list] + set safe 1 + while {![lindex $res 0]} { +% \end{tcl} +% First extract the next line of code and append it to |lines|. +% \begin{tcl} + set at_end [catch {eemenu::${extract}::next $safe} line] + if {$at_end == 1} then { + global errorInfo + return -code error -errorinfo $errorInfo + } elseif {$at_end == 0} then { + lappend lines $line + } elseif {![llength $lines]} then {break} +% \end{tcl} +% Secondly check if there is a complete block of code in |lines|. +% \begin{tcl} + set safe [expr $complete] + if {$at_end && !$safe} then { + set safe [expr {![dialog::yesno -y No -n Yes\ + "The last [llength $lines] lines do not appear to be a\ + complete block of code. Evaluate anyway?"]}] + if {!$safe} then { + set res [list 1 "" ""] + break + } + } +% \end{tcl} +% Finally log code, evaluate it, and log the result. This should +% happen when |lines| contains a complete block of code. If there is +% no error in evaluating the code then the current position will be +% safe at the next iteration of the loop, and if there is an error +% then the loop will not be iterated anyway. Hence the use of |safe| +% is correct. +% \begin{tcl} + if {$safe} then { + eemenu::${report}::log_code $lines + set res [eemenu::${eval}::item $lines] + eval [list eemenu::${report}::log_result] $res + set lines [list] + } + } +% \end{tcl} +% If an error has already been detected then it doesn't matter if +% |end| evaluates without error, but it could also be that no error +% results are returned before |end|. +% \begin{tcl} + if {[lindex $res 0]} then { + eval [list eemenu::${report}::log_result] [eemenu::${eval}::end] + } else { + set res [eemenu::${eval}::end] + eval [list eemenu::${report}::log_result] $res + } + eemenu::${extract}::finish [lindex $res 0] + eemenu::${report}::log_close +} +% \end{tcl} +% \end{proc} +% +% +% \subsection{The key bindings} +% +% There is a small system for managing the \eemenu\ key bindings. There +% are two reasons for this: deactivating a command should only unbind a +% binding if that binding is really currently used for that command, +% and the \textsf{Active~Commands} submenu should match the actual +% bindings. +% +% \begin{proc}{binding_index} +% The |binding_index| procedure makes an index into the |binding| array. +% The syntax is +% \begin{quote} +% |eemenu::binding_index| \word{mode} \word{binding} +% \end{quote} +% Both an empty string and || as \word{mode} are interpreted as +% ``global''. +% \begin{tcl} +proc eemenu::binding_index {mode binding} { + if {""==$mode} then {set mode ""} + set mode [string range "[string trim $mode] " 0 3] + return "$mode$binding" +} +% \end{tcl} +% \end{proc} +% +% \begin{arrayvar}{binding} +% The |binding| array is used to keep track of which command is +% currently using a particular binding. The entries in this array are +% command names. The indices have the form +% \begin{quote} +% \meta{mode}\meta{binding} +% \end{quote} +% where \meta{mode} is the mode and \meta{binding} is the menu-style +% code for the binding. \meta{mode} is padded on the right with +% spaces to exactly four characters. The \meta{mode} for global +% bindings is four spaces. +% \end{arrayvar} +% +% \begin{proc}{init_bindings} +% The |init_bindings| procedure initialises the |binding| array and +% the actual key bindings to match the data in the |cmdA| array. It +% should be called when the menu is activated, but before the menu +% itself is built. +% \begin{tcl} +proc eemenu::init_bindings {} { + eemenu::multiupvar cmdA binding + foreach cmd [array names cmdA] { +% \end{tcl} +% Only commands that are active needs to be considered. +% \begin{tcl} + array set A $cmdA($cmd) + if {!$A(active)} then {continue} +% \end{tcl} +% The state of the binding must also be determined. It could be that +% some other \textsf{EE~Menu} command is using the same binding, in +% which case the user should be informed of this. +% \begin{tcl} + set idx [eemenu::binding_index $A(mode) $A(binding)] + if {[info exists binding($idx)] &&\ + ![dialog::yesno "To activate '$cmd', I must first deativate\ + '$binding($idx)'. Proceed?"]}\ +% + then {continue} +% \end{tcl} +% Now the binding can be made for the command. +% \begin{tcl} + set binding($idx) $cmd + set call [keys::bindKey $A(binding)] + lappend call [list eemenu::main $cmd] + if {[string compare "" $A(mode)]} then {lappend call $A(mode)} + eval $call + } +} +% \end{tcl} +% \end{proc} +% +% \begin{proc}{deactivate} +% The |deactivate| procedure has the syntax +% \begin{quote} +% |eemenu::deactivate| \word{\eemenu\ command}\regplus +% \end{quote} +% It deactivates the bindings for all commands listed as arguments +% and updates the \textsf{Active Commands} submenu accordingly. +% \begin{tcl} +proc eemenu::deactivate {args} { + eemenu::multiupvar binding cmdA + foreach cmd $args { +% \end{tcl} +% First make sure the binding is actually active. +% \begin{tcl} + array set A $cmdA($cmd) + set idx [eemenu::binding_index $A(mode) $A(binding)] + if {![info exists binding($idx)] ||\ + [string compare $binding($idx) $cmd]} then {continue} +% \end{tcl} +% Then unset everything that has to do with it. +% \begin{tcl} + unset binding($idx) + set call [keys::unbindKey $A(binding)] + lappend call "" + if {[string compare "" $A(mode)]} then {lappend call $A(mode)} + eval $call + markMenuItem -m "Active Commands" $cmd off + } +} +% \end{tcl} +% \end{proc} +% +% \begin{proc}{activate} +% The |activate| procedure activates the bindings for the \eemenu\ +% commands listed as arguments. It has the syntax +% \begin{quote} +% |eemenu::activate| \word{noisy} \word{\eemenu\ command}\regplus +% \end{quote} +% The procedure notices and handles the case that the binding for an +% already active command coincides with that of some command to +% activate, usually by deactivating the active command. The \word{noisy} +% argument controls how it will behave in this. If \word{noisy} is |0| +% then it will merely show a list of deactivations on the status bar, +% but if it is |1| then it will put up a dialog and ask first. +% \begin{tcl} +proc eemenu::activate {noisy args} { + eemenu::multiupvar binding cmdA + set deactiveL [list] + foreach cmd $args { +% \end{tcl} +% First determine the state of the binding. +% \begin{tcl} + array set A $cmdA($cmd) + set idx [eemenu::binding_index $A(mode) $A(binding)] + if {[info exists binding($idx)]} then { + if {![string compare $binding($idx) $cmd]} then {continue} +% \end{tcl} +% In this case the binding is being used by some other command. +% Should that be deactivated? +% \begin{tcl} + if {$noisy && ![dialog::yesno "To activate '$cmd', I must\ + first deativate '$binding($idx)'. Proceed?"]} then {continue} +% \end{tcl} +% Here we know it should. Deactivation can be simplified since the +% binding will anyway be overwritten later. +% \begin{tcl} + lappend deactiveL $binding($idx) + markMenuItem -m "Active Commands" $binding($idx) off + } +% \end{tcl} +% Now activate the binding, as was the original intention. +% \begin{tcl} + set binding($idx) $cmd + set call [keys::bindKey $A(binding)] + lappend call [list eemenu::main $cmd] + if {[string compare "" $A(mode)]} then {lappend call $A(mode)} + eval $call + markMenuItem -m "Active Commands" $binding($idx) on\ + [text::Ascii 18 1] + } + if {!$noisy && [llength $deactiveL]} then { + status::msg "Deactivated commands: [join $deactiveL ", "]" + } +} +% \end{tcl} +% \end{proc} +% +% +% \subsection{Building and handling the menu} +% +% \begin{proc}{build_menu} +% The |build_menu| procedure builds the menu. It is called when the +% menu is initially created and whenever some command settings have +% been edited. +% \begin{tcl} +proc eemenu::build_menu {} { + eemenu::multiupvar cmdA binding + set items [list "Edit Commands[text::Ascii 201 1]"] + if {[array size cmdA] > 0} then { +% set cmdL [lsort -ignore [array names cmdA]] +% set cmdL [lsort -dictionary [array names cmdA]] + lappend items [list Menu -n "Active Commands" -m\ + -p eemenu::menu_proc $cmdL] + lappend items "(-)" + foreach cmd $cmdL { + array set A $cmdA($cmd) + if {$A(in_menu)} then {lappend items $cmd} + } + } + global eeMenu + Menu -n $eeMenu -m -p eemenu::menu_proc $items + if {[array size cmdA]} then { + foreach idx [array names binding] { + markMenuItem -m "Active Commands" $binding($idx) on\ + [text::Ascii 18 1] + } + } +} +% \end{tcl} +% \end{proc} +% +% \begin{proc}{menu_proc} +% The |menu_proc| handles selection of items in the \emph{EE} menu and +% its submenu. The syntax is the standard +% \begin{quote} +% |eemenu::menu_proc| \word{menu} \word{item} +% \end{quote} +% \begin{tcl} +proc eemenu::menu_proc {menu item} { + global eeMenu + eemenu::multiupvar cmdA binding + switch -- $menu $eeMenu { + switch -- $item "Edit Commands" { + eemenu::edit_commands + } default { + eemenu::main $item + } + } "Active Commands" { + array set A $cmdA($item) + set idx [eemenu::binding_index $A(mode) $A(binding)] + if {[info exists binding($idx)] &&\ + ![string compare $binding($idx) $item]} then { + eemenu::deactivate $item + } else { + eemenu::activate 1 $item + } + } +} +% \end{tcl} +% \end{proc} +% +% +% \subsection{The command dialogs} +% \label{Ssec:CmdDialogs} +% +% The dialog for editing command settings has two levels. On the top +% level, the dialog has one page per command. This dialog allows one to +% select methods, but does not show any detail settings for the methods. +% The other level has one page for each method type (or less) and shows +% only the detail settings for that method. +% +% \begin{arrayvar}{detail_typeA} +% \begin{arrayvar}{detail_helpA} +% \begin{arrayvar}{detail_keyA} +% The |detail_typeA| and |detail_helpA| arrays define the items in the +% setting details dialog. They are sent as arguments to +% |dialog::handle|, and thus the indices have the form +% \meta{page}|,|\meta{name}. The \meta{page} is either |Extract| +% for the extraction method page, |Complete| for the completion +% method page, |Evaluate| for the evaulation method page, or |Report| +% for the report method page. The |detail_keyA| array gives the keys +% associated with the items. +% \end{arrayvar}\end{arrayvar}\end{arrayvar} +% +% \begin{arrayvar}{detail_defaultA} +% The |detail_defaultA| array is indexed by the name of a detail page +% (|Extract|, |Complete|, |Evaluate|, or |Report|). The entries are +% key--value lists that constitute suggested defaults for the +% \dots|_extra| items in the main dialog. +% \begin{tcl} +array set eemenu::detail_defaultA\ + {Extract {} Complete {} Evaluate {} Report {}} +% \end{tcl} +% \end{arrayvar} +% +% \begin{proc}{define_detail} +% The |define_detail| procedure is a helper to simplify the definition +% of items in the detail dialogs by handling internally all the odd +% variable names that are involved. The syntax is +% \begin{quote} +% |eemenu::define_detail| \word{page} \word{name} \word{key} +% \word{type} \word{value}\regopt\ \word{help}\regopt +% \end{quote} +% \word{page} and \word{name} specify the item. \word{key}, +% \word{type}, \word{value}, and \word{help} are what will be stored +% into the arrays. The return value is precisely \word{name}, so that +% one can put that in the |extract|, |complete|, |evaluate|, or +% |report| array in the same command as the item is defined. +% +% It is OK to define the same item several times, provided that the +% definitions are equal (or different only in the \word{value}s or +% in that one has a \word{help} and the other doesn't), but it is an +% error if they are different. +% \begin{tcl} +proc eemenu::define_detail {page name key type {val ""} {help ""}} { + upvar #0 eemenu::detail_typeA typeA eemenu::detail_helpA helpA\ + eemenu::detail_keyA keyA eemenu::detail_defaultA valA + set error "" + set idx "$page,$name" + if {![info exists keyA($idx)]} then { + set keyA($idx) $key + } elseif {"$key" != "$keyA($idx)"} then { + append error " Different keys: '$keyA($idx)' and '$key'." + } + if {![info exists typeA($idx)]} then { + set typeA($idx) $type + } elseif {"$type" != "$typeA($idx)"} then { + append error " Different types: '$typeA($idx)' and '$type'." + } + if {[string length $val]} then { + if {[info exists valA($page)]} then {array set A $valA($page)} + if {![info exists A($key)] || ![string length $A($key)]} then { + set A($key) $val + set valA($page) [array get A] + } + } + if {[string length $help]} then { + if {![info exists helpA($idx)]} then { + set helpA($idx) $help + } elseif {"$help" != "$helpA($idx)"} then { + append error " Different help texts: '$helpA($idx)' and '$help'." + } + } + if {[string length $error]} then { + dialog::alert "The detail setting '$name' for '$page' methods\ + has conficting definitions.$error This is an error that should + be fixed; for now, the second definition will be ignored." + } + return $name +} +% \end{tcl} +% \end{proc} +% +% +% \begin{proc}{edit_commands} +% The |edit_commands| procedure puts up a dialog that lets the user +% edit the command settings. It takes no arguments and returns nothing. +% \begin{tcl} +proc eemenu::edit_commands {} { + global eemenu::extract eemenu::complete eemenu::evaluate\ + eemenu::report +% \end{tcl} +% The first thing to do is to determine the layout for command pages. +% \begin{tcl} + set layout [list] + lappend layout [list active flag Active "Is the binding active?"] + lappend layout [list binding binding "Command keybinding"] + lappend layout [list mode mode "Binding mode"] + lappend layout [list in_menu flag "Put in menu"] +% \smallskip +%<*atcl7> + set M [list] + foreach method [lsort -ignore [array names eemenu::extract]] { + lappend M [eemenu::Prettify $method] + } + lappend layout [list extractor [list menu $M] "Extraction method"] + lappend layout [list extract_extra [list hidden keyval] extract_extra] +% \smallskip + set M [list] + foreach method [lsort -ignore [array names eemenu::complete]] { + lappend M [eemenu::Prettify $method] + } + lappend layout [list complete [list menu $M] "Completion test"] + lappend layout [list complete_extra [list hidden keyval] complete_extra] +% \smallskip + set M [list] + foreach method [lsort -ignore [array names eemenu::evaluate]] { + lappend M [eemenu::Prettify $method] + } + lappend layout [list evaluator [list menu $M] "Evaluation method"] + lappend layout [list eval_extra [list hidden keyval] eval_extra] +% \smallskip + set M [list] + foreach method [lsort -ignore [array names eemenu::report]] { + lappend M [eemenu::Prettify $method] + } + lappend layout [list reporter [list menu $M] "Report method"] + lappend layout [list report_extra [list hidden keyval] report_extra] +% +%<*!atcl7> + set M [list] + foreach method [lsort -dictionary [array names eemenu::extract]] { + lappend M [eemenu::Prettify $method] + } + lappend layout [list extractor [list menu $M] "Extraction method"] + lappend layout [list extract_extra [list hidden keyval] extract_extra] +% \smallskip + set M [list] + foreach method [lsort -dictionary [array names eemenu::complete]] { + lappend M [eemenu::Prettify $method] + } + lappend layout [list complete [list menu $M] "Completion test"] + lappend layout [list complete_extra [list hidden keyval] complete_extra] +% \smallskip + set M [list] + foreach method [lsort -dictionary [array names eemenu::evaluate]] { + lappend M [eemenu::Prettify $method] + } + lappend layout [list evaluator [list menu $M] "Evaluation method"] + lappend layout [list eval_extra [list hidden keyval] eval_extra] +% \smallskip + set M [list] + foreach method [lsort -dictionary [array names eemenu::report]] { + lappend M [eemenu::Prettify $method] + } + lappend layout [list reporter [list menu $M] "Report method"] + lappend layout [list report_extra [list hidden keyval] report_extra] +% +% \end{tcl} +% Then the call to |dialog::make_paged| can be constructed (in the +% |call| variable), but as it is convenient to do that in order, the +% page arguments (which are the easiest) will be constructed last. +% \begin{tcl} + global dialog::ellipsis + if {![info exists dialog::ellipsis]} then {auto_load dialog::make_paged} + set call [list dialog::make_paged] + set buttons [list] + lappend buttons "New${dialog::ellipsis}" "Add new command"\ + {eemenu::add_command ""} + lappend buttons "Duplicate${dialog::ellipsis}"\ + "Duplicate this command" {eemenu::add_command $currentpage} + lappend buttons "Rename${dialog::ellipsis}"\ + "Rename this command" {eemenu::rename_command} + lappend buttons "Delete${dialog::ellipsis}"\ + "Delete this command" { + if {[dialog::yesno "Are you sure you want to\ + delete '$currentpage'?"]} { + set pages [dialog::delete_pages $pages\ + [list $currentpage] delta_pages] + } + } + lappend buttons "Details${dialog::ellipsis}"\ + "Setting details for this command" { + eemenu::command_details $dial $currentpage + } + lappend call -addbuttons $buttons -changeditems mods\ + -alpha7pagelimit 2 +% \end{tcl} +% Finally the page arguments to |dialog::make_paged| can be appended +% and the actuall call made. +% \begin{tcl} + global eemenu::cmdA +% foreach cmd [lsort -ignore [array names eemenu::cmdA]] { +% foreach cmd [lsort -dictionary [array names eemenu::cmdA]] { + lappend call [list $cmd [set eemenu::cmdA($cmd)] $layout] + } + set res [eval $call] +% \end{tcl} +% If |make_paged| terminates with an error (the user pressed +% \textsf{Cancel}) then processing of this procedure should stop here. +% Thus the |eval| above is quite in order. +% +% After that, the settings should be updated. The first step is to +% deactivate those commands whose activation state, binding, or mode +% has changed, since that has to be done before the old settings +% are cleared. +% \begin{tcl} + set call [list eemenu::deactivate] + newforeach {cmd keys} $mods { + if {[lsearch -regexp $keys {^(active|binding|mode)$}]>=0 &&\ + [info exists eemenu::cmdA($cmd)]} then { + lappend call $cmd + } + } + if {[llength $call]>1} then {eval $call} +% \end{tcl} +% Now the new settings can replace the old. +% \begin{tcl} + unset eemenu::cmdA + array set eemenu::cmdA $res +% \end{tcl} +% Then what remains is (i) to inform \Alpha\ that the changed command +% settings need to be saved, (ii) to make bindings for those commands +% that are active, and (iii) to add or remove items from the menu. +% \begin{tcl} + set call [list eemenu::activate 0] + set build_menu 0 +% newforeach {cmd keys} $mods { +% foreach {cmd keys} $mods { + prefs::modified eemenu::cmdA($cmd) + if {![info exists eemenu::cmdA($cmd)]}\ + then {set build_menu 1; continue} + array set A [set eemenu::cmdA($cmd)] + if {$A(active) &&\ + [lsearch -regexp $keys {^(active|binding|mode)$}]>=0}\ + then {lappend call $cmd} + if {[lsearch -exact $keys in_menu]>=0} then {set build_menu 1} + } + if {[llength $call]>2} then {eval $call} + if {$build_menu} then {eemenu::build_menu} +} +% \end{tcl} +% The conditions used above rely to some extent on the fact that +% \emph{all} items on a dialog page that is added or deleted count as +% having been changed. +% \end{proc} +% +% \begin{proc}{add_command} +% The |add_command| procedure is called by a button script to add a +% new command page to the dialog. The syntax is +% \begin{quote} +% |eemenu::add_command| \word{template} +% \end{quote} +% where \word{template} is the name of a command whose settings should +% be copied, or an empty string if the default settings should be used. +% \begin{tcl} +proc eemenu::add_command {templ} { + set name [getline "Name of new command"] + if {![string length $name]} then {return} +% newforeach {page items} [uplevel 1 {set pages}] { +% foreach {page items} [uplevel 1 {set pages}] { + if {![string compare $page $name]} then { + alternote "That name is already in use!" + return + } + } +% \end{tcl} +% By default, make new commands inactive and don't put them in the +% menu. This is to avoid silly errors in case the user wants to do +% a ``backup'' of a command. +% \begin{tcl} + set keyvals [list active 0 in_menu 0] + if {[string length $templ]} then { + set dial [uplevel 1 {set dial}] + set cpage [uplevel 1 {set currentpage}] +% newforeach {key item}\ +% foreach {key item}\ + {binding {Command keybinding} mode {Binding mode} + extractor {Extraction method} extract_extra extract_extra + complete {Completion test} complete_extra complete_extra + evaluator {Evaluation method} eval_extra eval_extra + reporter {Report method} report_extra report_extra} { + lappend keyvals $key [dialog::valGet $dial "${cpage},${item}"] + } + } else { + lappend keyvals binding "" mode "" extractor "Raw" + upvar #0 eemenu::detail_defaultA defaultA + lappend keyvals extract_extra $defaultA(Extract) + lappend keyvals complete "Entire Selection" complete_extra\ + $defaultA(Complete) + lappend keyvals evaluator "Internal Tcl" eval_extra\ + $defaultA(Evaluate) + lappend keyvals reporter "Status Line" report_extra $defaultA(Report) + } + uplevel 1 [list dialog::add_page $name $keyvals [uplevel 2 set layout]] + uplevel 1 [list set currentpage $name] +} +% \end{tcl} +% \end{proc} +% +% \begin{proc}{rename_command} +% The |rename_command| procedure is called by a button script to rename +% an existing command. There are no arguments. +% \begin{tcl} +proc eemenu::rename_command {} { + upvar 1 currentpage cpage dial dial + set name [getline "New name for command '$cpage'"] + if {![string length $name]} then {return} +% newforeach {page items} [uplevel 1 {set pages}] { +% foreach {page items} [uplevel 1 {set pages}] { + if {![string compare $page $name]} then { + alternote "That name is already in use!" + return + } + } +% \end{tcl} +% Technically, the page with the old name is deleted and a new page +% with the new name is added. The keyvals have to saved to a list in +% between. +% \begin{tcl} + set keyvals [list] +% newforeach {key item} {active Active in_menu {Put in menu}\ +% foreach {key item} {active Active in_menu {Put in menu}\ + binding {Command keybinding} mode {Binding mode}\ + extractor {Extraction method} extract_extra extract_extra\ + complete {Completion test} complete_extra complete_extra\ + evaluator {Evaluation method} eval_extra eval_extra\ + reporter {Report method} report_extra report_extra} { + lappend keyvals $key [dialog::valGet $dial "${cpage},${item}"] + } + uplevel 1 { + set pages [dialog::delete_pages $pages [list $currentpage]\ + delta_pages] + } + uplevel 1 [list dialog::add_page $name $keyvals [uplevel 2 set layout]] + set cpage $name +} +% \end{tcl} +% \end{proc} +% +% \begin{proc}{command_details} +% The |command_details| procedure is called by a button script to let +% the user see and edit the details of a command. The syntax is +% \begin{quote} +% |eemenu::command_details| \word{dialog ref.} \word{command} +% \end{quote} +% The \word{dialog ref.} is the reference to pass on to +% |dialog::valGet| and the like, as that is the only way to access the +% data that should be shown in the dialog. The \word{command} is the +% name of the command whose details should be shown, which is also the +% \meta{page} part of the array index that |dialog::valGet| needs. +% +% Most of this procedure consists of moving data from the data +% structures of one dialog to those of another, and back again. More +% precisely, data is taken from the \dots|_extra| items in the main +% dialog and put (using |array set|) into the local arrays |A1|, |A2|, +% |A3|, and |A4|. Then those entries in these arrays which correspond +% to details for the current set of methods are stored as dialog item +% values for |dial2|. At the end, those |dial2| items values that were +% changed are copied back to the corresponding |A| array, and then +% these arrays are put back (using |array get|) into the corresponding +% \dots|_extra| items in the main dialog. +% +% At the same time as the data are copied from the main dialog to the +% details dialog, the list of pages and items in the latter is being +% built. The information about this is taken from the |extract|, +% |complete|, |evaluate|, and |report| arrays. +% \begin{tcl} +proc eemenu::command_details {dial command} { + global eemenu::detail_typeA eemenu::detail_helpA\ + eemenu::detail_keyA eemenu::extract eemenu::complete\ + eemenu::evaluate eemenu::report + set pages [list] + set dial2 [dialog::create] +% \end{tcl} +% Exactly the same processing is done for all four pages of the +% details dialog, but since the location of the source data varies a +% bit irregularly between the pages, it is easiest to make a table of +% which variables and indices should be used. There is one line per +% page in the |newforeach| below. +% \begin{tcl} +% newforeach {which page layoutarr data arr} { +% foreach {which page layoutarr data arr} { + {Extraction method} Extract eemenu::extract extract_extra A1 + {Completion test} Complete eemenu::complete complete_extra A2 + {Evaluation method} Evaluate eemenu::evaluate eval_extra A3 + {Report method} Report eemenu::report report_extra A4 + } { + set method [eemenu::Unprettify\ + [dialog::valGet $dial ${command},${which}]] + set L [set ${layoutarr}($method)] + if {[llength $L]} then { + lappend pages $page $L + array set $arr [dialog::valGet $dial ${command},${data}] + foreach l $L { + set v [set eemenu::detail_keyA($page,$l)] + if {[info exists ${arr}($v)]} then { + dialog::valSet $dial2 "$page,$l" [set ${arr}($v)] + } else { +% \end{tcl} +% It turns out \AlphaEight\ doesn't like an empty string as value for +% a checkbox, so the value is explicitly set to |0| for flags. +% \changes{v\,0.3}{2003/04/11}{Added special default for flag items. +% A similar fix should be put in the dialogs code. (LH)} +% \begin{tcl} + switch -- [set eemenu::detail_typeA($page,$l)] flag { + dialog::valSet $dial2 "$page,$l" 0 + } default { + dialog::valSet $dial2 "$page,$l" "" + } + } + } + } + } +% \smallskip + if {![llength $pages]} then { + dialog::alert "There are no details for these methods." + dialog::cleanup $dial2 + return + } + dialog::handle $pages eemenu::detail_typeA $dial2\ + eemenu::detail_helpA page [list]\ + [list [list "Back" "Return to overall dialog" ""] right first] +% \smallskip + set L [dialog::changed_items $dial2] + foreach item $L { + switch -glob $item { + Extract,* {set arr A1} + Complete,* {set arr A2} + Evaluate,* {set arr A3} + Report,* {set arr A4} + } + set ${arr}([set eemenu::detail_keyA($item)])\ + [dialog::valGet $dial2 $item] + } +% newforeach {page data arr} { +% foreach {page data arr} { + Extract extract_extra A1 + Complete complete_extra A2 + Evaluate eval_extra A3 + Report report_extra A4 + } { + if {[lsearch -glob $L "${page},*"] >= 0} then { + dialog::valChanged $dial "${command},${data}" [array get $arr] + } + } + dialog::cleanup $dial2 +} +% \end{tcl} +% \end{proc} +% +% +% \subsection{\texttt{dialogsNew} patch} +% +% In the testing stage, I discovered that the |edit_commands| procedure +% managed to overstress \Alpha7's |dialog| command almost without +% trying. It is probably the large number of popup menus that is causing +% trouble, but regardless of the cause, I found that I couldn't use this +% dialog if it tried to show more than two commands. +% +% The \textsf{Edit Filesets} command faces a similar limitation and to +% work around that there is a separate \textsf{Edit A Fileset} command. +% As I didn't feel that much like reconstructing the \textsf{Edit +% Commands} dialog however, I instead redesigned |dialog::make_paged| +% so that \emph{it} automatically splits the dialog when there are too +% many pages in it. The redesigned |make_paged| will however probably +% not make it into Alpha\Tcllogo\ before v\,7.6d3 and at the moment +% that seems like a bit restrictive to me. Hence \emph{EE~Menu} will +% patch the \texttt{dialogsNew} code if this change hasn't already +% been incorporated. +% +% \begin{tcl} +%<*dialogspatch&atcl7> +if {![llength [info commands dialog::make_paged]]} then { + auto_load dialog::make_paged +} +if {![regexp -- -alpha7pagelimit [info body dialog::make_paged]]} then { +% \end{tcl} +% The following is copied from \texttt{dialogsNew.dtx} v\,1.4. +% +% \setnamespace{dialog} +% +% \begin{proc}{make_paged} +% The |make_paged| procedure is similar to the |make| procedure, but +% its argument argument structure is slightly different, its return +% value is very different, and it does have a couple of features that +% |make| doesn't (such as adding or removing pages or items in a +% dialog). The basic syntax is the same +% \begin{quote} +% |dialog::make_paged| \meta{option}\regstar\ \word{page}\regplus +% \end{quote} +% but here each \word{page} is a list with the structure +% \begin{quote} +% \word{page name} \word{key--value list} \word{item list} +% \end{quote} +% and each \word{item list} in turn is a list of items, each of with +% are themselves lists and have the structure +% \begin{quote} +% \word{key} \word{type} \word{name} \word{help}\regopt +% \end{quote} +% The return value is a list with the structure +% \begin{quote} +% \begin{regexp}[\regplus]\word{page name} +% \word{key--value list}\end{regexp} +% \end{quote} +% and in both cases the \word{key--value list} has the format of a +% list returned by |array get|, i.e., +% \begin{quote} +% \begin{regexp}[\regstar]\word{key} \word{value}\end{regexp} +% \end{quote} +% +% Rather than (as with |make|) including the value of an item in its +% \word{item} list, that list contains a \word{key} which references +% a value stored in the \word{key--value list} of that page. The idea +% with this is that the input and output formats for values should be +% the same, so that the caller has little overhead in converting from +% one data format to another. The \word{key--value list} format is +% furthermore flexible in that is completely insensitive to changes +% that add, remove, or rearrange items within a page. Extra +% key--value pairs in the input are ignored and an empty string is +% substituted as value for pairs that are missing. +% +% The \meta{option}s understood by |make_paged| are +% \begin{quote} +% |-ok| \word{OK button title}\\ +% |-cancel| \word{cancel button title}\\ +% |-title| \word{dialog window title}\\ +% |-defaultpage| \word{name of default page}\\ +% |-addbuttons| \word{button list}\\ +% |-width| \word{dialog window width}\\ +% |-alpha7pagelimit| \word{maximal number of pages}\\ +% |-debug| \word{debug level}\\ +% |-changedpages| \word{var-name}\\ +% |-changeditems| \word{var-name} +% \end{quote} +% Those that are common with |make| work exactly the same. The +% |-changedpages| option specifies that the caller wants to know on +% which pages something was changed. The \word{var-name} is the name +% of a variable in the caller's local context which will be set to +% the list of (names of) pages where some item value was changed. The +% |-changeditems| option is similar, but here the variable will be set +% to a list with the structure +% \begin{quote} +% \begin{regexp}[\regstar]\word{page name} +% \word{key list}\end{regexp} +% \end{quote} +% where the \word{key list}s are lists of the \emph{keys} of items on +% that page whose values were changed. +% +% \begin{tcl} +proc dialog::make_paged {args} { +% \end{tcl} +% +% |make_paged| largely has the same local variables as |make|, but +% there are some additions. The major arrays are +% \begin{description} +% \item[\texttt{pageA}] +% The index into this array is the name of a page. An entry +% contains the list of names of items on that page. +% \item[\texttt{typeA}] +% The index into this array has the form +% \meta{page}|,|\meta{item}, where \meta{page} is the name of a +% page and \meta{item} is the name of an item on that page. An +% entry contains the type of that item. +% \item[\texttt{keyA}] +% The index has the same form as in the |typeA| array. An entry +% contains the \word{key} for that item. +% \item[\texttt{helpA}] +% The index has the same form as in the |typeA| array. An entry +% contains the help text for that entry, but an item needs not +% have an entry in this array (it can be left unset). +% \end{description} +% There are a couple of additional scalar variables that are of +% interest. +% \begin{description} +% \item[\texttt{retCode}, \texttt{retVal}] +% When the |retCode| variable is set, the dialog is logically +% closed and the procedure returns. If the variable is set to |0| +% then |make| executes a normal return and the returned value will +% be the list of item values. If the variable is set to anything +% else then that will used for the |-code| option of |return| and +% the returned value will be taken from the |retVal| variable, +% which must then be initalised. +% \item[\texttt{dial}] +% This contains the reference string to use with |valGet|, +% |valSet|, and friends when accessing the values of items in +% the dialog. +% \item[\texttt{currentpage}] +% This contains the name of the current page in the dialog. +% \item[\texttt{delta\_pages}] +% This is the list of all pages which have been added to or +% deleted from the dialog since it was called. The |add_page| and +% |delete_page| procedures both directly access this list. It is +% needed to get the information for the |-changedpages| and +% |-changeditems| correct. +% \item[\texttt{pages}] +% This is a list of pages and items to show in the dialog. It is +% similar to the result of |array get pageA|, but the order of +% pages is as specified in the call and hidden pages are not +% included. +% \item[\texttt{opts(-addbuttons)}] +% This is \word{button list} specified by the caller. Button +% scripts can modify this list to change the text on their button. +% \item[\texttt{state}] +% This is initialized to |0| before the first time the dialog is +% shown and then the procedure leaves it alone. Button scripts may +% change it to keep track of what ``state'' (mostly: which +% items\slash pages are currently hidden) the dialog is in. +% \item[\texttt{splitstate}] +% This is the dialog splitting state and works as for +% |dialog::make|. +% \item[\texttt{optionL}] +% The list of additional options to pass to |dialog::handle|. +% \end{description} +% +% The first part of |dialog::make_paged| processes the arguments. +% \begin{tcl} + set opts(-ok) OK + set opts(-cancel) Cancel + set opts(-title) "" + set opts(-width) 400 + set opts(-debug) 0 + getOpts {-title -defaultpage -ok -cancel -addbuttons -width -debug\ + -alpha7pagelimit -changedpages -changeditems} + set dial [dialog::create] +% \end{tcl} +% The page arguments are interpreted by the |add_page| procedure. +% Since these pages aren't new in the sense that is relevant for the +% |delta_pages| list, that variable is reset afterwards. The +% |splitstate| variable is implicitly updated by |add_page|. +% \begin{tcl} + set pages [list] + set delta_pages [list] + if {[info exists opts(-alpha7pagelimit)] && [info tclversion]<8.0}\ + then { + set splitstate below + } else { + set splitstate off + } + foreach pagearg $args { + eval [list dialog::add_page] $pagearg + } + set delta_pages [list] + if {$splitstate=="page"} then {set splitstate menu} + if {[info exists opts(-defaultpage)]} then { + set currentpage $opts(-defaultpage) + } else { + set currentpage [lindex $pages 0] + } + set optionL [list -width $opts(-width) -title $opts(-title)] + set main_buttons [list\ +% + [list $opts(-ok) "Click here to use the current settings."\ + {set retCode 0}\ +% + $opts(-cancel) "Click here to discard any\ + changes you've made to the settings."\ + {set retCode 1; set retVal "cancel"}]\ +% + first right] + set view_button [list [list {View dialog page}\ + {Click here to see the items on this page.}\ + {set splitstate page}]] + set back_button [list [list "Back"\ + {Click here to go back to the pages menu.}\ + {set splitstate menu}] first right] +% \end{tcl} +% +% The second part is the loop which lets the user edit the settings. +% \begin{tcl} + set state 0 + while {![info exists retCode]} { + switch -exact -- $splitstate off - below { + if {[info exists opts(-addbuttons)]} then { + set script [dialog::handle $pages typeA $dial helpA\ + currentpage $optionL [list $opts(-addbuttons)]\ + $main_buttons] + } else { + set script [dialog::handle $pages typeA $dial helpA\ + currentpage $optionL $main_buttons] + } + } menu { + set altpages [list] + set n 1 + foreach item $pages { + if {$n} then { + lappend altpages $item + set n 0 + } else { + lappend altpages {} + set n 1 + } + } + set script [dialog::handle $altpages typeA $dial helpA\ + currentpage $optionL $view_button $main_buttons] + } page { +% \end{tcl} +% This is a small test to make sure that the value of |currentpage| +% is valid. If it isn't then one should return to the |menu| state. +% \begin{tcl} + if {![info exists pageA($currentpage)]} then { + set splitstate menu + continue + } + set altpages [list $currentpage $pageA($currentpage)] + if {[info exists opts(-addbuttons)]} then { + set script [dialog::handle $altpages typeA $dial helpA\ + currentpage $optionL [list $opts(-addbuttons)]\ + $back_button] + } else { + set script [dialog::handle $altpages typeA $dial helpA\ + currentpage $optionL $back_button] + } + } + if {[catch $script err]} then { +% \end{tcl} +% The rest of this loop is simply for gracefully handling errors that +% occur when button scripts are evaluated. +% \begin{tcl} + global errorInfo + set errinfo $errorInfo + if {$opts(-debug)} then { + tclLog "Error in button script '$script'" + tclLog $err + } + dialog::cleanup $dial + return -code 1 -errorinfo $errinfo\ + "Error '$err' when evaluating button script." + } + } +% \end{tcl} +% +% The third part is as in |make| responsible for constructing the result +% to return (at normal returns). Unlike the case with |make|, the +% return value covers only the items currently in |pages|. This +% part is also responsible for constructing the lists of changed +% pages and items. Two important variables in this are |cS| and |cA|. +% |cS| is an array which is used to test whether a certain item has +% been changed (via |valChanged|), but the only thing that matters is +% whether an entry has been set or not. |cA| is an array indexed by +% page name, whereas the entries are lists of keys of items on that +% page which have been changed. +% \begin{tcl} + if {$retCode==0} then { + set retVal [list] + global dialog::mute_types + foreach page $delta_pages { + foreach name $pageA($page) { + lappend cA($page) $keyA($page,$name) + } + } + foreach item [dialog::changed_items $dial] {set cS($item) ""} + newforeach {page items} $pages { + set res [list] + foreach name $items { + set T "$page,$name" + if {[lsearch -exact ${dialog::mute_types}\ + [lindex $typeA($T) 0]] < 0} then { + lappend res $keyA($T) [dialog::valGet $dial $T] + if {[info exists cS($T)]} then { + lunion cA($page) $keyA($T) + } + } + } + lappend retVal $page $res + } + if {[info exists opts(-changedpages)]} then { + upvar 1 $opts(-changedpages) cp + set cp [array names cA] + } + if {[info exists opts(-changeditems)]} then { + upvar 1 $opts(-changeditems) ci + set ci [array get cA] + } + } + dialog::cleanup $dial + return -code $retCode $retVal +} +% \end{tcl} +% \end{proc} +% +% \begin{proc}{add_page} +% The |add_page| procedure can be called from within the |make_paged| +% procedure to add a new page to the dialog. The syntax is +% \begin{quote} +% |dialog::add_page| \word{page name} \word{key--value list} +% \word{item list} \word{position}\regopt +% \end{quote} +% Here the \word{page name}, \word{key--value list}, and \word{item +% list} coincide with those parts of a \word{page} argument of +% |make_paged|. +% +% |add_page| works by modifying the arrays |typeA|, |keyA|, |helpA|, +% and |pageA|, the lists |pages| and |delta_pages|, and the variable +% |splitstate| in the caller's local context. It also uses the value +% in the |dial| variable there as an argument to |valSet| and the +% |opts| array to access the |-alpha7pagelimit| value. All of these +% variables are assumed to function as they do in the |make_paged| +% procedure. +% +% The \word{position} argument can be used to specify where in the +% |pages| list that the new page should be inserted. It defaults to +% |end|, which puts the new page last. Otherwise the argument should +% be numeric: |0| means put first, |1| means put second, |2| means put +% third, etc. +% +% If |splitstate| is |below| and the number of pages equals (or is +% greater than) the |-alpha7pagelimit| then the |splitstate| is +% changed to |page|. If |splitstate| is |menu| then it is also +% changed to |page|. +% \begin{tcl} +proc dialog::add_page {page keyvalL itemsL {pos end}} { + upvar pageA pageA typeA typeA helpA helpA keyA keyA\ + dial dial pages pages delta_pages delta_pages\ + splitstate splitstate opts opts + array set local $keyvalL + set pageA($page) [list] + lunion delta_pages $page + foreach item $itemsL { + set key [lindex $item 0] + set name [lindex $item 2] + set keyA($page,$name) $key + if {[info exists local($key)]} then { + dialog::valSet $dial $page,$name $local($key) + } else { + dialog::valSet $dial $page,$name "" + } + set typeA($page,$name) [lindex $item 1] + if {[llength $item]>3} then { + set helpA($page,$name) [lindex $item 3] + } + lappend pageA($page) $name + } + if {$pos!="end"} then { + set pages [linsert $pages [expr {2*$pos}] $page $pageA($page)] + } else { + lappend pages $page $pageA($page) + } + if {$splitstate=="menu" || ($splitstate=="below" &&\ + [llength $pages]>2*$opts(-alpha7pagelimit))} then { + set splitstate page + } +} +% \end{tcl} +% \end{proc} +% +% \begin{proc}{delete_pages} +% In one sense, this procedure does the opposite of |add_page|, but +% it can be used to achieve different effects as well. Basically it +% takes a list of page names and items, in the format for the first +% argument of |handle|, and returns the same list with some pages +% removed. The syntax is +% \begin{quote} +% |dialog::delete_pages| \word{pages} \word{delete-list} +% \word{deleted-var}\regopt +% \end{quote} +% where the \word{delete-list} is the list of names of pages to +% remove. \word{deleted-var} is, if it is given, the name of a +% variable in the caller's local context containing a list of page +% names. The deleted pages are then unioned with this list. The most +% common value for \word{deleted-var} is |delta_pages|. +% +% If there is a \word{deleted-var} argument then this procedure might +% also modify the |splitstate| variable in the caller's local +% context. A value of |page| is changed to |menu| or |below| depending +% on how meny pages are returned and the value of +% |opts(-alpha7pagelimit)| in the caller's local context. (Both these +% variables must exist if |delete_pages| is called with a +% \word{deleted-var} argument.) +% \begin{tcl} +proc dialog::delete_pages {pages deleteL {deletedvar {}}} { + set res [list] + if {[string length $deletedvar]} then {upvar 1 $deletedvar diffL} + newforeach {page items} $pages { + if {[lsearch -exact $deleteL $page] == -1} then { + lappend res $page $items + } else { + lunion diffL $page + } + } + if {[string length $deletedvar]} then { + upvar splitstate state opts(-alpha7pagelimit) limit + switch -exact -- $state page - menu { + if {[llength $res]<=2*$limit} then { + set state below + } else { + set state menu + } + } + } + return $res +} +% \end{tcl} +% \end{proc} +% +% +% \begin{tcl} +} +% +% \end{tcl} +% \setnamespace{eemenu} +% +% +% \section{Extraction methods} +% \label{Sec:Extraction} +% +% The extraction method is mainly a source of input for an \eemenu\ +% command, but it is also responsible for providing some feedback to the +% user in the event that an error occurs. This feedback should take the +% simple form of positioning the cursor at the beginning of the first +% piece of code that could not be evaluated successfully. Extraction +% methods are therefore expected to constantly keep track of the +% position where the cursor should be put if an error occurs: this is +% called the \emph{safe} position. As no other part of an \eemenu\ +% cares about positions, these are regarded as internal matters for +% the extraction mathod. The feedback from the main executive is +% restricted to answers to the simple questions ``Is the current position +% safe?'' and ``Did an error occur?'' +% +% Extractions are begun by a call to the +% \describestring[proc][eemenu::\meta{method}]{start}|start| +% procedure, which has the syntax +% \begin{quote} +% |eemenu::|\meta{method}|::start| \word{settings} +% \word{window}\regopt\ +% \begin{regexp}[\regopt]\word{start} \word{end}\end{regexp} +% \end{quote} +% The \word{settings} is the |extract_extra| list of the command, and +% contain the settings that should be used until the next call to +% |start|. This list may contain key--value pairs for details not used +% by the current method (but which presumably are used by others), and +% it needs not contain key--value pairs for every detail that the +% current method uses. Therefore the method should provide defaults +% when necessary. +% +% The \word{window} is the name of the window from which code should be +% extracted. If this is omitted then the extraction should default to +% using the current window. The \word{start} and \word{end} are +% positions of the beginning and end respectively of the interval from +% which code should be extracted, but they are normally not used. When +% these are omitted then the method may do whatever seems reasonable, +% but the following algorith is recommended: +% \begin{itemize} +% \item If there is a selection in the window, then substitute the +% position of its beginning for \word{start} and the position of its +% end for \word{end}. +% \item Otherwise do the entire window, i.e., let \word{start} be the +% |minPos| and \word{end} be the |maxPos|. +% \end{itemize} +% The main reason that \word{start} and \word{end} at all exist as +% arguments is that this simplifies testing and debugging, but it also +% enables virtual methods to make their own decisions about from where +% the code should be extracted. +% +% The |start| procedure returns the file name (complete path, no count) +% of the source window. This is passed on to the begin procedures of +% other methods, but most of them ignore it anyway. If something is +% wrong with the arguments supplied to it then |start| terminates with +% an error. +% +% To actually extract a line, the main executive calls the +% \describestring[proc][eemenu::\meta{method}]{next}|next| procedure, +% which has the syntax +% \begin{quote} +% |eemenu::|\meta{method}|::next| \word{is safe} +% \end{quote} +% This procedure returns the next extractable code line, or terminates +% with return code 9 if there is no more line. +% \changes{v\,0.2}{2002/12/26}{Throwing abort instead of error, since +% it otherwise catches too many real errors. (LH)} +% The \word{is safe} argument is |1| if the current position should be +% considered safe and |0| otherwise. +% +% Extraction is terminated by a call to the +% \describestring[proc][eemenu::\meta{method}]{finish}|finish| +% procedure. This has the syntax +% \begin{quote} +% |eemenu::|\meta{method}|::finish| \word{error?} +% \end{quote} +% where the \word{error?} argument is |1| if there was an error in +% evaluation and |0| otherwise. This procedure does not return any data. +% +% +% \subsection{Some position utilities} +% +% \textit{\textbf{Note:} The procedures in this subsection were probably +% not a good idea to start with, so they are no longer included by +% default.} For the historically interested reader, it may be remarked +% that the code below predates the addition in \AlphaTcl\,7.6 of |-w| +% options to most standard position procedures. +% +% Extraction procedures should generally not assume that the window from +% which code is being extracted is the current window. The reason for +% this is that if \Alpha\ is evaulating internally then the extracted +% code may bring some other window to front or, more commonly, create a +% new window (which will by default go on top of all others). This is +% kind of tricky since many of \Alpha's commands for working with +% positions implicitly assume that the positions are in the current +% window, but it can be delt with by storing positions are a pair +% |{|\word{row} \word{column}|}| instead, as the |rowColToPos| command +% which converts this to a standard position does accept a window +% argument. +% +% Handling explicitly such positions is however more work that it +% should be, so here follows some generic utility procs to deal with +% positions specified as lists with the structure +% \begin{quote} +% \word{window} \word{row} \word{column} \word{standard pos}\regopt +% \end{quote} +% Here, \word{window} is a full window name (as returned by the +% |win::Current| procedure), whereas \word{row} and \word{column} +% are integers. The optional \word{standard pos} element is the standard +% position (that which should be used in calls to \Alpha\ primitives) if +% it has been computed by a call to |rowColToPos|. If it has not been +% computed then this element should not be present in the list. +% +% For \AlphaTk, there isn't much point in a distinction between the +% ``standard'' and row--column position formats as standard +% positions there are composed from a row and a column anyway. +% +% These procedures are put in the +% \describestring[namespace]{pos}|pos| namespace, as there is nothing +% in them that is specific to \eemenu. +% \begin{tcl} +%<*rowcol> +namespace eval pos {} +% \end{tcl} +% \setnamespace{pos} +% +% \begin{proc}{incr_row} +% \begin{proc}{incr_col} +% The |incr_row| and |incr_col| procedures increment the row and +% column respectively component of a position list. Their syntaxes are +% \begin{quote} +% |pos::incr_row| \word{pos-var} \word{row increment}\\ +% |pos::incr_col| \word{pos-var} \word{column increment} +% \end{quote} +% Incrementing the column can not cause the row to be incremented. +% Incrementing the row sets the column to |0|. The minimal column +% value is |0| and the minimal row value is |1|. There are no +% maximums in either. +% \begin{tcl} +proc pos::incr_row {var diff} { + upvar 1 $var V + set t [expr {[lindex $V 1] + $diff}] + if {$t<1} then { + set V [list [lindex $V 0] 1 0] + } else { + set V [list [lindex $V 0] $t 0] + } +} +proc pos::incr_col {var diff} { + upvar 1 $var V + set t [expr {[lindex $V 2] + $diff}] + set V [lrange $V 0 1] + if {$t<0} then {lappend V 0} else {lappend V $t} +} +% \end{tcl} +% \end{proc}\end{proc} +% +% \begin{proc}{get_standard} +% The |get_standard| procedure returns the standard position +% corresponding the row--column position stored in a variable. It has +% the syntax +% \begin{quote} +% |pos::get_standard| \word{pos-var} +% \end{quote} +% \begin{tcl} +proc pos::get_standard {var} { + upvar 1 $var V + if {[llength $V]<4} then { + lappend V [eval [list rowColToPos -w] $V] + } + return [lindex $V 3] +} +% \end{tcl} +% \end{proc} +% +% \begin{proc}{polyset} +% The |polyset| procedure is used to convert a list of standard +% positions in a certain window to row--column form and store those +% in variables, also given as a list. The syntax is +% \begin{quote} +% |pos::polyset| \word{window} \word{var-list} \word{pos-list} +% \end{quote} +% If there are more elements in the \word{var-list} than in the +% \word{pos-list} then the remaining elements will not be set. +% +% The reason this procedure works on lists is that \Alpha\ cannot +% (or at least will not) determine the row and column for a position +% unless that is in the current window. Therefore this procedure might +% have to temporarily bring some other window to front to do its stuff. +% \begin{tcl} +proc pos::polyset {win varL posL} { + if {$win != [win::Current]} then { + set top [win::Current] + bringToFront $win + } + set N [llength $varL] + if {$N > [llength $posL]} then {set N [llength $posL]} + for {set n 0} {$n < $N} {incr n} { + upvar 1 [lindex $varL $n] V + set V [concat [list $win]\ + [posToRowCol [lindex $posL $n]] [lindex $posL $n]] + } + if {[info exists top]} then {bringToFront $top} +} +% \end{tcl} +% \end{proc} +% +% \begin{proc}{RC_compare} +% The |RC_compare| procedure can be used to compare two row--column +% positions. The syntax is +% \begin{quote} +% |pos::RC_compare| \word{first pos} \word{second pos} +% \end{quote} +% and the result is as for |string compare|. It is an error if the +% \word{window} elements of the two arguments are different. +% \begin{tcl} +proc pos::RC_compare {a b} { + if {"[lindex $a 0]" != "[lindex $b 0]"} then { + error "Not the same ." + } + if {[lindex $a 1] < [lindex $b 1]} then { + return -1 + } elseif {[lindex $a 1] > [lindex $b 1]} then { + return 1 + } elseif {[lindex $a 2] < [lindex $b 2]} then { + return -1 + } elseif {[lindex $a 2] > [lindex $b 2]} then { + return 1 + } else { + return 0 + } +} +% \end{tcl} +% \end{proc} +% +% +% \begin{proc}[eemenu]{get_extract_positions} +% The |get_extract_positions| procedure interprets the three optional +% arguments for a |start| procedure and sets two specified variables +% in the caller's local context to the row--column positions +% corresponding to the beginning and the end respectively of interval +% from which code should be extracted. The syntax is +% \begin{quote} +% |eemenu::get_extract_positions| \word{start-var} \word{end-var} +% \word{window}\regopt\ +% \begin{regexp}[\regopt]\word{start} \word{end}\end{regexp} +% \end{quote} +% This procedure returns the complete window name (including count). +% \begin{tcl} +proc eemenu::get_extract_positions {startvar endvar {win ""}\ + {startpos ""} {endpos ""}} { + if {![string length $win]} then {set win [win::Current]} + if {![string length $startpos]} then { + set startpos [getPos -w $win] + set endpos [selEnd -w $win] + if {[pos::compare $startpos == $endpos]} then { + set startpos [minPos] + set endpos [maxPos -w $win] + } + } + uplevel 1 [list pos::polyset $win [list $startvar $endvar]\ + [list $startpos $endpos]] + return $win +} +% \end{tcl} +% \end{proc} +% +% \begin{proc}[eemenu]{get_text} +% The |get_text| procedure is mainly a wrapper around the |getText| +% command. The syntax is +% \begin{quote} +% |eemenu::get_text| \word{start-var} \word{end-var} +% \end{quote} +% where \word{start-var} and \word{end-var} are names of variables +% in the caller's local context of row--column positions. The return +% value is the text between those positions. +% \begin{tcl} +proc eemenu::get_text {startvar endvar} { + upvar 1 $startvar S $endvar E + if {"[lindex $S 0]" != "[lindex $E 0]"} then { + error "Not the same ." + } + getText -w [lindex $S 0] [pos::get_standard S] [pos::get_standard E] +} +% +% \end{tcl} +% \end{proc} +% +% +% +% +% \subsection{Raw extraction} +% +% \begin{arrayentry}[eemenu]{extract}{raw} +% Raw extraction has no details. +% \begin{tcl} +set eemenu::extract(raw) [list] +namespace eval eemenu::raw {} +% \end{tcl} +% \end{arrayentry} +% \setnamespace{eemenu::raw} +% +% \subsubsection{Row--column based extraction} +% +% This was my original implementation of raw extraction. Unfortunately +% it seems that |rowColToPos| is rather slow in \AlphaEight. Hence +% there is an alternative implementation below. +% +% \begin{proc}{start} +% \begin{variable}{cur_pos} +% \begin{variable}{safe_pos} +% \begin{variable}{end_pos} +% As this |start| procedure has no details to deal with, it only +% has to initialize the three position variables |cur_pos| (current +% position), |safe_pos| (most recent safe position), and |end_pos| +% (the end position). +% \begin{tcl} +%<*rowcol> +proc eemenu::raw::start {details args} { + eemenu::multiupvar cur_pos safe_pos end_pos + set win\ + [eval [list eemenu::get_extract_positions cur_pos end_pos] $args] + set safe_pos $cur_pos + return [win::StripCount $win] +} +% \end{tcl} +% \end{variable}\end{variable}\end{variable}\end{proc} +% +% +% \begin{proc}{next} +% The |next| procedure is also as simple as it can possibly be. The +% only nontrivial thing about it is that it removes carriage returns +% and linefeeds at the end of the text returned by +% |eemenu::get_text|. +% \begin{tcl} +proc eemenu::raw::next {at_safe} { + eemenu::multiupvar cur_pos safe_pos end_pos + if {[pos::RC_compare $cur_pos $end_pos]>=0} then {return -code 9 "Done"} + set this $cur_pos + if {$at_safe} then {set safe_pos $this} + pos::incr_row cur_pos 1 + if {[pos::RC_compare $cur_pos $end_pos]>0} then { + set cur_pos $end_pos + } + string trimright [eemenu::get_text this cur_pos] "\n\r" +} +% \end{tcl} +% \end{proc} +% +% \begin{proc}{finish} +% The |finish| procedure is slightly trickier that it has to be. +% Rather than doing a |goto| to the safe position, it does a |select| +% followed by a |markHilite|, with the end position as end, so that +% the mark will be at the end. +% \begin{tcl} +proc eemenu::raw::finish {was_error} { + if {!$was_error} then {return} + eemenu::multiupvar safe_pos end_pos + bringToFront [lindex $safe_pos 0] + select [pos::get_standard safe_pos] [pos::get_standard end_pos] + markHilite +} +% +% \end{tcl} +% \end{proc} +% +% +% \subsubsection{Queued extraction} +% +% This alternative implementation of the \textsf{raw} method is based on +% extracting a bit more than one would usually need and store the +% surplus in a queue of lines that haven't been extracted yet. +% +% \begin{variable}{line_queue} +% \begin{variable}{window} +% \begin{variable}{cur_pos} +% \begin{variable}{from_pos} +% \begin{variable}{safe_pos} +% \begin{variable}{end_pos} +% The |window| variable holds the name of the window from which text +% is being extracted. +% The |line_queue| variable is the text that has been gotten from the +% window but not yet been properly extracted, split into lines. The +% last line is incomplete and thus text must be added to the queue +% whenever it has length less than two. +% +% The |cur_pos| variable is the standard position of the beginning of +% the first line in |line_queue|. The |from_pos| variable is the +% first position after the last piece of data in |line_queue|. The +% |safe_pos| is the last position reported to be safe and the |end_pos| +% is the position at which extraction should stop. +% \end{variable}\end{variable}\end{variable}\end{variable}\end{variable} +% \end{variable} +% +% \begin{proc}{start} +% As this |start| procedure has no details to deal with, it only +% has to initialize the six above variables. +% \changes{v\,0.3}{2003/05/28}{Storing the tail of the file name, not +% the whole path. This avoids unnecessary file system accesses. (LH) +% Cf. bug 919.} +% \changes{v\,0.3.1}{2003/07/19}{Using complete paths in \AlphaTk, due +% to bug~1026. (LH)} +% \begin{tcl} +%<*!rowcol> +proc eemenu::raw::start {details {win ""} {startpos ""} {endpos ""}} { + eemenu::multiupvar line_queue window cur_pos from_pos safe_pos end_pos + global alpha::platform + if {![string length $win]} then {set win [win::Current]} + switch -- ${alpha::platform} alpha { + set win_tail [file tail $win] + } default { + set win_tail $win + } + if {![string length $startpos]} then { + set startpos [getPos -w $win_tail] + set endpos [selEnd -w $win_tail] + if {[pos::compare $startpos == $endpos]} then { + set startpos [minPos] + set endpos [maxPos -w $win_tail] + } + } + set line_queue [list ""] + set window $win_tail + set cur_pos $startpos + set from_pos $startpos + set safe_pos $startpos + set end_pos $endpos + return [win::StripCount $win] +} +% \end{tcl} +% \end{proc} +% +% +% \begin{proc}{next} +% The |next| procedure first retrieves data from the window (if +% necessary) and then it extracts the first line from the queue. Data +% beyond the |end_pos| are never read into the queue. +% +% There are two versions of this procedure to account for the new +% addition of a |-w| option to |pos::compare| and |pos::math|. +% \changes{v\,0.1.2}{2002/12/26}{Fixed a bug concerning pos::compare, +% where string compare syntax was used. (LH, who is sure he has +% fixed this bug before.)} +% \begin{tcl} +%<*atcl7> +if {[alpha::package vsatisfies -loose\ + [alpha::package versions AlphaTcl] 7.6d2]} then { +% + proc eemenu::raw::next {at_safe} { + eemenu::multiupvar line_queue window cur_pos from_pos safe_pos\ + end_pos + if {[llength $line_queue] < 2} then { + if {[pos::compare -w $window $cur_pos >= $end_pos]} then { + return -code 9 "Done" + } + set next_pos [pos::math -w $window $from_pos + 1024] + if {[pos::compare -w $window $next_pos >= $end_pos]} then { + set next_pos $end_pos + } + set text [lindex $line_queue end] + append text [getText -w $window $from_pos $next_pos] + set line_queue [split $text "\n\r"] + set from_pos $next_pos + } + if {$at_safe} then {set safe_pos $cur_pos} + set line [lindex $line_queue 0] + set line_queue [lreplace $line_queue 0 0] + set cur_pos\ + [pos::math -w $window $cur_pos + [expr {[string length $line]+1}]] + set line + } +%<*atcl7> +} else { + proc eemenu::raw::next {at_safe} { + eemenu::multiupvar line_queue window cur_pos from_pos safe_pos end_pos + if {[llength $line_queue] < 2} then { + if {[pos::compare $cur_pos >= $end_pos]} then { + return -code 9 "Done" + } + set next_pos [pos::math $from_pos + 1024] + if {[pos::compare $next_pos >= $end_pos]} then { + set next_pos $end_pos + } + set text [lindex $line_queue end] + append text [getText -w $window $from_pos $next_pos] + set line_queue [split $text "\n\r"] + set from_pos $next_pos + } + if {$at_safe} then {set safe_pos $cur_pos} + set line [lindex $line_queue 0] + set line_queue [lreplace $line_queue 0 0] + set cur_pos [pos::math $cur_pos + [expr {[string length $line]+1}]] + set line + } +} +% +% \end{tcl} +% \end{proc} +% +% \begin{proc}{finish} +% This |finish| procedure clears the |line_queue|. It should usually +% be empty already, but better safe than sorry! +% \begin{tcl} +proc eemenu::raw::finish {was_error} { + eemenu::multiupvar line_queue window safe_pos end_pos + set line_queue [list] + if {!$was_error} then {return} + bringToFront $window + select $safe_pos $end_pos + markHilite +} +% +% \end{tcl} +% \end{proc} +% +% +% \subsection{Regexp extraction} +% +% Regular expression extraction adds two features to what the raw +% extraction does. One is the filter---only lines meeting a certain +% condition are extracted, the others are skipped---and the other is the +% substitution---each line returned is first fed through a |regsub|. +% It would of course be possible to implement this from scratch, but it +% is more instructive (and slightly shorter) to base it on the |raw| +% method. +% +% \begin{arrayentry}[eemenu]{extract}{regexp} +% Regular expression extraction has four detail settings. +% \begin{tcl} +set eemenu::extract(regexp) [list\ +% \end{tcl} +% The first +% two are used in a ``filter'' that selects which lines will be +% extracted: +% \begin{details}{Extraction} +% \detailitem{filter_mode} +% This is the mode of operation for the filter. There are three +% possible values: |off| (no filtering, all lines are extracted), +% |grep| (only lines matching the |filterRE| regular expression +% are extracted), and |anti-grep| (only lines \emph{not} matching +% the |filterRE| regular expression are extracted). +% \begin{tcl} + [eemenu::define_detail Extract "Filter mode"\ + filter_mode [list menu [list off grep anti-grep]] off]\ +% \end{tcl} +% \detailitem{filterRE} +% This is the regular expression for the filter +% \begin{tcl} + [eemenu::define_detail Extract "Filter regular expression"\ + filterRE var]\ +% \end{tcl} +% \end{details} +% The last two are used in a straightforward |regsub| that is applied +% to each extracted line before it is returned to the main executive. +% \begin{details}{Extraction} +% \detailitem{searchRE} +% This is the regular expression searched for in the line. +% \begin{tcl} + [eemenu::define_detail Extract "Search (regexp):" searchRE var]\ +% \end{tcl} +% \detailitem{replaceRE} +% This is the regular expression for the replacement. +% \begin{tcl} + [eemenu::define_detail Extract "Replace (regexp):" replaceRE var]] +% \end{tcl} +% \end{details} +% \end{arrayentry} +% \begin{tcl} +namespace eval eemenu::regexp {} +% \end{tcl} +% \setnamespace{eemenu::regexp} +% +% \begin{proc}{start} +% \begin{arrayvar}{detA} +% This |start| procedure stores its details in the |D| array and calls +% the |evalmemu::raw::start| procedure to handle everything else. +% Note how it provides defaults for the detail settings it +% unconditionally uses, in case these are not present in the +% \word{details}. +% \begin{tcl} +proc eemenu::regexp::start {details args} { + global eemenu::regexp::detA + array set eemenu::regexp::detA\ + {filter_mode off searchRE {} replaceRE {}} + array set eemenu::regexp::detA $details + eval [list eemenu::raw::start $details] $args +} +% \end{tcl} +% \end{arrayvar}\end{proc} +% +% \begin{proc}{next} +% The |next| procedure starts with a loop that continues until it +% finds a line that passes the filter. +% \begin{tcl} +proc eemenu::regexp::next {at_safe} { + upvar #0 eemenu::regexp::detA D + while {1} { + set line [eemenu::raw::next $at_safe] + switch -- $D(filter_mode) { + off {break} + grep {if {[regexp -- $D(filterRE) $line]} then {break}} + anti-grep { + if {![regexp -- $D(filterRE) $line]} then {break} + } + } + } +% \end{tcl} +% Then it applies the |regsub| to the line. +% \begin{tcl} + regsub -all -- $D(searchRE) $line $D(replaceRE) line + return $line +} +% \end{tcl} +% \end{proc} +% +% \begin{proc}{finish} +% The |finish| procedure simply calls its equivalent for the |raw| +% method. +% \begin{tcl} +proc eemenu::regexp::finish {err} {eemenu::raw::finish $err} +% \end{tcl} +% \end{proc} +% +% +% +% \subsection{Docstrip extraction} +% +% Docstrip extraction is a descendant of the extraction mechanisms from +% the \textsf{dtxload} package. +% +% +% \begin{arrayentry}[eemenu]{extract}{docstrip} +% There are three detail settings for |docstrip| extraction. +% \begin{tcl} +set eemenu::extract(docstrip) [list\ +% \end{tcl} +% \begin{details}{Extraction} +% \detailitem{filePatL} +% This is a list of file patterns that the file name is tested +% against using |string match|. If none of them fits then +% extraction is stopped before is begins. +% \begin{tcl} + [eemenu::define_detail Extract "File patterns" filePatL var\ + "*.dtx" "List of glob-style file patterns the window must match"]\ +% \end{tcl} +% \detailitem{lookAtEnvs} +% If this is checked then extraction will be further conditioned +% by that the code line is in one of the environements listed in +% the next setting. I find this very convenient, since I often +% forget to comment out some lines here and there in the file. +% \begin{tcl} + [eemenu::define_detail Extract "Filter by environments"\ + lookAtEnvs flag 0]\ +% \end{tcl} +% \detailitem{sourceEnvsL} +% The list of environments from which code may be extracted. +% \begin{tcl} + [eemenu::define_detail Extract "Source environments"\ + sourceEnvsL var {tcl tcl*}]] +% \end{tcl} +% \end{details} +% \end{arrayentry} +% \begin{tcl} +namespace eval eemenu::docstrip {} +% \end{tcl} +% \setnamespace{eemenu::docstrip} +% +% \begin{variable}{cur_pos} +% \begin{variable}{safe_pos} +% \begin{variable}{end_pos} +% The |cur_pos|, |safe_pos|, and |end_pos| variables are as for the +% |raw| mathod. A special case is however that |safe_pos| will never +% be inside a verbatim section, even if it is safe there, since the +% extraction wouldn't remember that is was inside a verbatim section +% if it was restarted. Therefore the \word{is safe} argument to |next| +% will be ignored in verbatim sections. +% +% These variables are only used by the \Module{rowcol} variant. +% \end{variable}\end{variable}\end{variable} +% +% \begin{variable}{module_included} +% \begin{variable}{in_code_env} +% \begin{variable}{in_verbatim} +% The |module_included| variable keeps track of whether the current +% position is in an included module. It is |1| if the current +% position should be included and |0| if it shouldn't. +% The |in_code_env| variable is used for keeping track of the markup +% context of the currently considered line in the file when the +% |lookAtEnvs| setting is on. |in_code_env| is set to |1| when a +% |\begin{|\meta{env}|}| for an \meta{env} in the |sourceEnvsL| is +% scanned, and set to |0| when an |\end{|\meta{env}|}| is scanned. +% The |in_verbatim| variable is |1| when the current position is in +% a \textsf{docstrip} verbatim module and |0| (which is the normal +% case) otherwise. +% \end{variable}\end{variable}\end{variable} +% +% \begin{variable}{module_stack} +% \begin{variable}{next_module_idx} +% The |module_stack| variable is keeps track of how modules +% are nested around the currently considered line. The variable is a +% stack implemented as a list in which the last element corresponds to +% the innermost module. Each element in this list is itself a list +% with the structure +% \begin{quote} +% \word{expression} \word{start pos.} \word{previous}\regopt +% \end{quote} +% The \word{expression} is the guard expression, as a string. The +% \word{start pos.} is the (row--column) position in the file where +% the module started. +% Finally, the \word{previous} is a boolean value which +% records the inclusion status (value of |module_included|) +% that was in force before the module was entered. +% +% The |next_module_idx| variable holds the index in the +% |module_stack| list of the next module whose expression has not yet +% been evaluated; it is equal to |llength ${module_stack}| if all +% module expressions have been evaluated. Those elements in the +% |module_stack| list which have index $\geq$ |next_module_idx| do +% not have a \word{previous} element. +% \end{variable}\end{variable} +% +% +% \begin{proc}{start} +% The |docstrip| extraction |start| procedure is by far the most +% complicated, due to the many mechanisms it must initialize. +% \begin{tcl} +proc eemenu::docstrip::start {details args} { + eemenu::multiupvar detA module_included module_stack\ + next_module_idx in_code_env in_verbatim +% \end{tcl} +% First there are the position variables +% \begin{tcl} +%<*rowcol> + eemenu::multiupvar cur_pos safe_pos end_pos + set win [win::StripCount\ + [eval [list eemenu::get_extract_positions cur_pos end_pos] $args]] + set safe_pos $cur_pos +% +%<*!rowcol> + set win [eval [list eemenu::raw::start $details] $args] +% +% \end{tcl} +% and secondly the setting details +% \begin{tcl} + array set detA {filePatL *.dtx lookAtEnvs 0} + array set detA $details +% \end{tcl} +% Then the method must check whether the detail setting permit +% extracting from this window. If not, the procedure raises an error +% (which isn't caught by |eemenu::main|). +% \begin{tcl} + set ok 0 + foreach pat $detA(filePatL) { + if {[string match $pat $win]} then {set ok 1; break} + } + if {!$ok} then { + status::msg "Can't extract from that window. Must be\ + [join $detA(filePatL) " or "]." + return -code 9 "Bad window name" + } +% \end{tcl} +% Now that it has been established that extraction will take place, +% the various variables defining the ``state of \textsf{dostrip}'' +% must be initialised. Four of them have standard values at start and +% present no problem. +% \begin{tcl} + set module_included 1 + set in_verbatim 0 + set module_stack [list] + set next_module_idx 0 +% \end{tcl} +% The |in_code_env| variable is however another matter, as the +% reasonable value for it depends very much on what characters follow +% the start position. The special value |-1| for |in_code_env| means +% ``change to |0| or |1| as appropriate when you get the first line''. +% \begin{tcl} + if {!$detA(lookAtEnvs)} then { + set in_code_env 1 + } else { +%<*rowcol> + set t_pos $cur_pos + pos::incr_col t_pos 2 + switch -regexp -- [eemenu::get_text cur_pos t_pos] { + ^%< {set in_code_env 1} + ^% {set in_code_env 0} + default {set in_code_env 1} + } +% +% set in_code_env -1 + set detA(envsRE) {\\(begin|end) *\{(} + foreach env $detA(sourceEnvsL) { + append detA(envsRE) [quote::Regfind $env] "|" + } + append detA(envsRE) ")\}(.*)\$" + } + return $win +} +% \end{tcl} +% \end{proc} +% +% \begin{arrayentry}{detA}{envsRE} +% The |envsRE| entry in the |detA| array is used for storing the +% regular expression that is used when searching for code environment +% markup in comment lines. +% \end{arrayentry} +% +% Next comes a couple of utility procs that are called from the |next| +% procedure. +% +% \begin{arrayvar}{known}[expression] +% The |known| array records the values of all known expressions, +% i.e., those expressions about whose values the user has been +% inquired. The entries are either |0| (for ``don't include'') or +% |1| (for ``include''). Note that this array is not initialised by +% the |start| procedure---instead is cleared by the |finish| +% procedure if no error occurred. +% \end{arrayvar} +% +% \begin{proc}{eval_guard} +% The |eval_guard| procedure takes the string that is a guard +% expression as argument. It returns |1| if the expression evaluates +% to true and |0| if it evaluates to false. +% +% If the expression has an entry in the |known| array then the +% |eval_guard| procedure returns that value, and if it does not the +% user is inquired for the value. +% \begin{tcl} +proc eemenu::docstrip::eval_guard {e} { + global eemenu::docstrip::known + if {![info exists eemenu::docstrip::known($e)]} then { + set eemenu::docstrip::known($e)\ + [dialog::yesno "Should <$e> modules be included?"] + } + set eemenu::docstrip::known($e) +} +% \end{tcl} +% \end{proc} +% +% +% \begin{proc}{push_module} +% The |push_module| procedure pushes a block module onto the +% module stack, but it does not modify the value of +% |module_included|. The syntax is +% \begin{quote} +% |eemenu::docstrip::push_module| \word{expression} \word{position} +% \end{quote} +% where \word{expression} is the expression string and +% \word{position} is the position (row--column or standard, depending +% on which kind of positions are being used) where the guard begins. +% \begin{tcl} +proc eemenu::docstrip::push_module {e p} { + global eemenu::docstrip::module_stack + lappend eemenu::docstrip::module_stack [list $e $p] +} +% \end{tcl} +% \end{proc} +% +% \begin{proc}{pop_module} +% The |pop_module| procedure pops a block module off the module +% stack and checks that the modules are properly nested. Its +% syntax is +% \begin{quote} +% |eemenu::docstrip::pop_module| \word{expression} \word{position} +% \end{quote} +% where \word{expression} is the expression string of the module to +% pop and \word{position} is the position (row--column or standard, +% depending on which kind of positions are being used) where the +% end of module guard begins. +% +% If the module nesting is incorrect then the user is presented with +% a dialog with buttons for pushing the positions of the incorrect +% modules. It is not an error if the module stack is empty, since it +% is perfectly reasonable that the beginning of the code to evaluate +% is in the middle of a module. +% +% \changes{ \textsf{dtxload} v\,1.11}{2001/05/11}{Corrected ugly bug +% which messed up the value of \texttt{next\_module\_idx}. (LH)} +% \begin{tcl} +proc eemenu::docstrip::pop_module {e p} { + eemenu::multiupvar module_stack module_included next_module_idx + set len [llength $module_stack] + if {$len==0} then {return} + set L [lindex $module_stack [expr {$len-1}]] + if {[lindex $L 0] != $e} then { + switch [buttonAlert "Module nesting error: <*[lindex $L 0]>\ + module ended by . For which guards should the positions\ + be pushed?" None Start End Both]\ + { + Start {eemenu::docstrip::push_bookmarks [lindex $L 1]} + End {eemenu::docstrip::push_bookmarks $p} + Both {eemenu::docstrip::push_bookmarks [lindex $L 1] $p} + } + } + set module_stack [lreplace $module_stack end end] + if {$len<=$next_module_idx} then { + set next_module_idx [llength $module_stack] + } + if {[llength $L]>2} then { + set module_included [lindex $L 2] + } +} +% \end{tcl} +% \end{proc} +% +% \begin{proc}{push_bookmarks} +% The |push_bookmarks| procedure pushes one or several positions +% (row--column or standard, depending on which kind of positions are +% being used) onto the bookmarks stack. The arguments are the +% positions to push. +% \changes{v\,0.3}{2003/05/28}{Changed some \texttt{win::Current} to +% \texttt{win::CurrentTail}. This avoids unnecessary file system +% accesses. (LH) Cf. bug 919.} +% \begin{tcl} +proc eemenu::docstrip::push_bookmarks {args} { + global markStack markName +% upvar #0 eemenu::raw::window win + set topWin [win::CurrentTail] + foreach pos $args { + set name mark$markName + incr markName +%<*rowcol> + set win [lindex $pos 0] + if {"[win::CurrentTail]" != "$win"} then {bringToFront $win} + createTMark $name [pos::get_standard pos] + set fileName [win::Current] + set markStack [linsert $markStack 0\ + [list $fileName $name [pos::get_standard pos]]] +% +%<*!rowcol> + if {[string compare [win::CurrentTail] $win]}\ + then {bringToFront $win} + createTMark $name $pos + set fileName [win::Current] + set markStack [linsert $markStack 0 [list $fileName $name $pos]] +% + } + if {[string compare [win::CurrentTail] $topWin]}\ + then {bringToFront $topWin} +} +% \end{tcl} +% \end{proc} +% +% \begin{proc}{update_included} +% Simply pushing a module onto the module stack does not update the +% |module_included| variable; that is instead done by the +% |update_included| procedure, which should be called right before the +% value of |module_included| is used. The reason for this arrangement +% is that it avoids evaluating guard expressions whose values do not +% matter. +% +% |update_included| has no arguments. +% \begin{tcl} +proc eemenu::docstrip::update_included {} { + eemenu::multiupvar module_stack module_included next_module_idx + while {$module_included && $next_module_idx<[llength $module_stack]} { + set L [lindex $module_stack $next_module_idx] + lappend L $module_included + set module_stack\ + [lreplace $module_stack $next_module_idx $next_module_idx $L] + incr next_module_idx + set module_included [eemenu::docstrip::eval_guard [lindex $L 0]] + } +} +% \end{tcl} +% \end{proc} +% +% \begin{variable}{end_verb_line} +% The |end_verb_line| variable stores the line which terminates +% verbatim mode. It is only initialised when verbatim mode is begun. +% \end{variable} +% +% \begin{proc}{next} +% The overall structure of the |next| procedure is that of an endless +% loop, from which the code |break|s out when it has found an +% extractable line. Should this loop run into the |end_pos| it +% terminates instead with a |return -code 9|. The local variable +% |next_pos| stores the position of the beginning of the next line. +% \begin{tcl} +proc eemenu::docstrip::next {is_safe} { + eemenu::multiupvar detA module_included in_code_env\ + in_verbatim module_stack next_module_idx end_verb_line +%<*rowcol> + eemenu::multiupvar cur_pos safe_pos end_pos + set next_pos $cur_pos +% +% upvar #0 eemenu::raw::cur_pos raw_cur_pos + while {1} { +%<*rowcol> + pos::incr_row next_pos 1 + if {[pos::RC_compare $next_pos $end_pos]>0} then { + set next_pos $end_pos + } + if {[pos::RC_compare $cur_pos $next_pos]>=0} then { + return -code 9 "Done" + } +% \end{tcl} +% No matter what state the extraction process is in, it will need to +% look at the current line, so it is extracted and put in the |line| +% variable. Spaces at end of the line are ignored for compatibility +% with the \TeX\ \package{docstrip} (\TeX\ throws away spaces at the +% end of a line as part of the reading process, so \package{docstrip} +% cannot see them). +% \begin{tcl} + set line [string trimright [eemenu::get_text cur_pos next_pos]\ + "\r\n "] +% +% \end{tcl} +% In the standard positions implementation, getting text from the +% window is instead done by calling |eemenu::raw::next|. The position +% at the beginning of the line (the value of |eemenu::raw::cur_pos| +% before the call to |eemenu::raw::next|) is saved since it might be +% needed for making bookmarks later. +% \begin{tcl} +%<*!rowcol> + set cur_pos $raw_cur_pos + set line [eemenu::raw::next [expr {$is_safe && !$in_verbatim}]] + set line [string trimright $line "\r\n "] + if {$in_code_env<0} then { + switch -glob -- $line { + %<* {set in_code_env 1} + %* {set in_code_env 0} + default {set in_code_env 1} + } + } +% +% \end{tcl} +% Verbatim mode is pretty simple. Either a line is an extractable +% code line or it is the end of verbatim mode guard. +% \begin{tcl} + if {$in_verbatim} then { + if {![string compare $line $end_verb_line]} then { + set in_verbatim 0 + } elseif {$module_included && $in_code_env} then { + break + } + } else { +% \end{tcl} +% In non-verbatim mode, lines are classified based on the first few +% characters of the line. +% \begin{tcl} + switch -glob -- $line { +% \end{tcl} +% Lines that begin with |%<<| are \package{docstrip} guard lines that +% start verbatim mode. The part of the guard line which comes after +% the |%<<| is called an end-tag. Verbatim mode ends with the next line +% which consists of a percent character followed by this end-tag +% \begin{tcl} + %<<* { + eemenu::docstrip::update_included +%<*rowcol> + if {$module_included && $in_code_env && $is_safe} then { + set safe_pos $cur_pos + } +% + set in_verbatim 1 + set end_verb_line "%[string range $line 3 end]" + } +% \end{tcl} +% Lines that begin with |%<*| or |%]+)>} $line foo modifier\ + expression]}\ + then { + if {[dialog::yesno "Malformed guard line '$line'\ + encountered. Push position?"]}\ +% + then {eemenu::docstrip::push_bookmarks $cur_pos} + } elseif {$modifier=="*"} then { + eemenu::docstrip::push_module $expression $cur_pos + } else { + eemenu::docstrip::pop_module $expression $cur_pos + } + } +% \end{tcl} +% Other lines that begin with |%<| are also \package{docstrip} +% guard lines, but in these cases the guard is followed by some code +% and the guard only affects the code on that particular line. There +% must be a |>| somewhere on the line, and the part of the line after +% the |>| is code that \package{docstrip} can extract. +% \begin{tcl} + %<* { + if {![regexp {^%<(-|\+|)([^>]+)>(.*)$} $line\ + foo modifier expression code]}\ + then { + if {[dialog::yesno "Malformed guard line '$line'\ + encountered. Push position?"]}\ + then {eemenu::docstrip::push_bookmarks $cur_pos} + } elseif {$in_code_env} then { + eemenu::docstrip::update_included + if {$module_included &&\ + [eemenu::docstrip::eval_guard $expression] !=\ + ($modifier=="-")} then { + set line $code + break + } + } + } +% \end{tcl} +% Remaining lines that begin with |%| are considered comment lines, +% but they could technically be metacomment lines (begin with |%%|). +% In that case \package{docstrip} would copy the line, but as there +% is little point in using this \package{docstrip} features for +% lines that the \Tcllogo\ interpreter would be interested in, +% the |docstrip| extraction method ignores that subtlety. Comment +% lines are ignored unless the |lookAtEnvs| details is |1|, in which +% case they are scanned for code environment markup. +% \begin{tcl} + %* { + while {$detA(lookAtEnvs) &&\ + [regexp -- $detA(envsRE) $line foo type env line]} { + set in_code_env [expr {"$type"=="begin"}] + } + } +% \end{tcl} +% Finally, all lines that do not begin with a |%| are code lines. +% \begin{tcl} + default { + if {$in_code_env} then { + eemenu::docstrip::update_included + if {$module_included} then {break} + } + } + } + } +% \end{tcl} +% That ends the large |switch| command and the large |if|. Now all +% that remains in the loop is to update |cur_pos|. +% \begin{tcl} +% set cur_pos $next_pos + } +% \end{tcl} +% Whenever the procedure gets past the loop, the line that has been +% extracted is in |line| and the only thing that remains is to update +% the positions. +% \begin{tcl} +%<*rowcol> + if {$is_safe && !$in_verbatim} then {set safe_pos $cur_pos} + set cur_pos $next_pos +% + return $line +} +% \end{tcl} +% \end{proc} +% +% \begin{proc}{finish} +% In addition to the |raw| method |finish| procedure, this |finish| +% also unsets the |known| array when there wasn't an error. +% \begin{tcl} +proc eemenu::docstrip::finish {was_error} { +% eemenu::raw::finish $was_error + if {$was_error} then { +%<*rowcol> + eemenu::multiupvar safe_pos end_pos + bringToFront [lindex $safe_pos 0] + select [pos::get_standard safe_pos] [pos::get_standard end_pos] + markHilite +% + } else { + global eemenu::docstrip::known + catch {unset eemenu::docstrip::known} + } +} +% \end{tcl} +% \end{proc} +% +% +% \section{Completeness tests} +% +% \setnamespace{eemenu} +% +% Unlike the other method types, completion methods are not implemented +% as procedures that can be called, but as an expression string stored +% in a global variable. The string in question is called +% \describestring[var.][eemenu::\meta{method}]{complete}|complete| +% and it will be evaluated as an \emph{expression} in the local context +% of the |main| procedure. The reason for this different set-up is that +% these methods are usually much simpler to handle that way. +% +% Completion methods are expected to get their data from the following +% variables: +% \begin{description} +% \item[\texttt{lines}] +% A list of the lines that have been extracted by not yet evaluated. +% This is (at least in theory) what the completion method is testing +% for being complete. +% \item[\texttt{at\_end}] +% A boolean. It is |0| if the extraction has not yet reached the +% end, and then it is set to |1|. +% \item[\texttt{CMD(complete\_extra)}] +% The key--value list of detail settings for completion methods. +% None of the methods currently implemented have any detail settings. +% \end{description} +% +% \begin{arrayentry}{complete}{entireSelection} +% \setnamespace{eemenu::entireSelection} +% \begin{variable}{complete} +% The |entireSelection| method says `No' until the entire selection +% has been extracted, then `Yes'. It has no detail settings. +% \begin{tcl} +set eemenu::complete(entireSelection) [list] +namespace eval eemenu::entireSelection {} +set eemenu::entireSelection::complete {$at_end} +% \end{tcl} +% \end{variable}\end{arrayentry} +% +% \begin{arrayentry}{complete}{everyLine} +% \setnamespace{eemenu::everyLine} +% \begin{variable}{complete} +% The |everyLine| method says `Yes' after every line. +% \begin{tcl} +set eemenu::complete(everyLine) [list] +namespace eval eemenu::everyLine {} +set eemenu::everyLine::complete {1} +% \end{tcl} +% \end{variable}\end{arrayentry} +% +% \begin{arrayentry}{complete}{tclInfoComplete} +% \setnamespace{eemenu::tclInfoComplete} +% \begin{variable}{complete} +% The |tclInfoComplete| method is the only one (defined so far) which +% actually cares to look at what has been extracted. It calls the +% |info complete| of \Alpha's \Tcllogo\ interpreter. +% \begin{tcl} +set eemenu::complete(tclInfoComplete) [list] +namespace eval eemenu::tclInfoComplete {} +set eemenu::tclInfoComplete::complete\ + {[info complete [join $lines \n]\n]} +% \end{tcl} +% \end{variable}\end{arrayentry} +% +% +% \section{Evaluation methods} +% +% The evaluation\slash execution method is what actually does anything +% (all the other methods just move data around). From an input\slash +% output perspective it takes the input from the extraction method and +% generates the output that the report method should handle. Like +% extraction methods, evaluation methods must declare three procedures +% for |main| to call. These are: |begin|, |item|, and |end|. All of +% these procedures return a \emph{result list}, which has the structure +% \begin{quote} +% \word{error?} \word{passing} \word{permanent} +% \end{quote} +% \word{error?} is a boolean that the main executive interprets to see +% if there was an error. It is |0| when there wasn't an error. +% \word{passing} and \word{permanent} are result strings. \word{passing} +% should be suitable for output channels where new results replace old, +% whereas \word{permanent} should be suitable for output channels where +% the results are appended to each other. If the \word{passing} string +% is empty then there is no reason to have it replace a previous +% \word{passing} string. If the \word{permanent} string is nonempty +% then it must end with a newline character. +% +% The \describestring[proc][eemenu::\meta{method}]{begin}|begin| +% procedure is called to initialize the evaluation process. It has the +% syntax +% \begin{quote} +% |eemenu::|\meta{method}|::begin| \word{details} \word{source} +% \end{quote} +% where the \word{details} is the key--value list of setting details +% and \word{source} is the source window name as returned by the +% extraction method |start| procedure. If the result of |begin| has a +% nonzero \word{error?} then the corresponding |end| procedure will not +% be called. +% +% The \describestring[proc][eemenu::\meta{method}]{item}|item| +% procedure recieves a bunch of code to evaluate. It has the syntax +% \begin{quote} +% |eemenu::|\meta{method}|::item| \word{lines} +% \end{quote} +% where \word{lines} is the list of lines of code to evaluate. The +% \describestring[proc][eemenu::\meta{method}]{end}|end| procedure is +% called when no more lines of code will be sent for evaluation. |end| +% takes no arguments. +% +% It is not necessarily in the call to |item| that the code gets +% evaluated. The obvious alternative is that |item| simply stores the +% code somewhere, for example by writing it to a temporary file, and +% that it is the call to |end| that actually causes the code to be +% evaluated. This is an important reason why all the procedures have the +% same format for returned data. +% +% +% +% \subsection{Internal evaluation} +% +% The |internalTcl| evaluation method sends the code to \Alpha's internal +% \Tcllogo\ interpreter and evaluates it there. +% +% \begin{arrayentry}[eemenu]{evaluate}{internalTcl} +% There are no details for this method. +% \begin{tcl} +set eemenu::evaluate(internalTcl) [list] +% \end{tcl} +% \end{arrayentry} +% \begin{tcl} +namespace eval eemenu::internalTcl {} +% \end{tcl} +% \setnamespace{eemenu::internalTcl} +% +% \begin{proc}{begin} +% \begin{proc}{end} +% Neither the |begin| nor the |end| procedure does anything. +% \begin{tcl} +proc eemenu::internalTcl::begin {details source} { + list 0 "" "" +} +proc eemenu::internalTcl::end {} {list 0 "" ""} +% \end{tcl} +% \end{proc}\end{proc} +% +% \begin{proc}{item} +% The |item| procedure actually evaluates the code and suitably +% formats the result. +% \begin{tcl} +proc eemenu::internalTcl::item {lines} { + set code [catch [list uplevel #0 [join $lines \n]] res] + if {$code == 1} then { +% \end{tcl} +% If there was an error, the last part of the value of |errorInfo| +% will be due to the |uplevel| command. As that command was not +% requested by the user, it would be confusing and it is therefore +% best to remove it. +% \begin{tcl} + global errorInfo + set L [split $errorInfo \n] + set L [lrange $L 0 [expr {[llength $L] - 4}]] + list 1 "Tcl eval error: $res"\ + "Error: $res\nError info:\n[join $L \n]\n" +% \end{tcl} +% When there wasn't an error, a distinction is made between the case +% of an empty string result (very common, e.g.\ from |proc|) and that +% of a nonempty string result. This might seem inconsistent, but in +% practice it looks much better. +% \begin{tcl} + } elseif {[string length $res]} then { + list 0 "Tcl eval OK: $res" "$res\n" + } else { + list 0 "Tcl eval OK." "" + } +} +% \end{tcl} +% \end{proc} +% +% +% +% \subsection{DoScript AppleEvent evaluation} +% +% The |doScriptAE| evaluation method sends a |dosc| AppleEvent to some +% external application, to have the script evaluated that way. Of +% course, this only works if \TclAE\ is available, so this method is +% only defined if that is the case. +% \begin{tcl} +%if {[llength [info commands tclAE::send]]} then { +%if {![catch {package require tclAE}]} then { +% \end{tcl} +% +% \begin{arrayentry}[eemenu]{evaluate}{doScriptAE} +% The details for the |doScriptAE| method roughly fall into three +% categories: what is the target application, how should the +% extracted lines be combined to a script, and what about a reply? +% \begin{tcl} +set eemenu::evaluate(doScriptAE) [list\ +% \end{tcl} +% \begin{details}{Evaluate} +% \detailitem{targetApp} +% This is the appspec of the target application. +% \begin{tcl} + [eemenu::define_detail Evaluate "Target application"\ + targetApp appspec]\ +% \end{tcl} +% \detailitem{joinString} +% The extracted lines is a list, but the script to send should be +% a string, so they will have to be joined somehow. This item +% offers a choice between some common forms of whitespace. +% \begin{tcl} + [eemenu::define_detail Evaluate "Join lines using"\ + joinString [list menuindex [list Lf Cr CrLf Space]] 1]\ +% \end{tcl} +% \detailitem{prefix} +% This will be prepended to the script sent. It can for example be +% used to add some command that takes care of the result. +% \begin{tcl} + [eemenu::define_detail Evaluate "Prefix" prefix var]\ +% \end{tcl} +% \detailitem{suffix} +% This will be appended to the script sent. +% \begin{tcl} + [eemenu::define_detail Evaluate "Suffix" suffix var]\ +% \end{tcl} +% \detailitem{replyQ} +% This flag controls whether the method should request a reply to +% the AE sent. +% \begin{tcl} + [eemenu::define_detail Evaluate "Wait for reply" replyQ flag]] +% \end{tcl} +% \end{details} +% \end{arrayentry} +% \begin{tcl} +namespace eval eemenu::doScriptAE {} +% \end{tcl} +% \setnamespace{eemenu::doScriptAE} +% +% +% \begin{arrayvar}{A} +% The |doScriptAE| method keeps its detail settings, and some derived +% quantities, in the |A| array. Those are all the global variables it +% has. +% \end{arrayvar} +% +% +% \begin{proc}{begin} +% The main task of this |begin| procedure is to make sure that the +% target application is running. +% \begin{tcl} +proc eemenu::doScriptAE::begin {details source} { + upvar #0 eemenu::doScriptAE::A A + array set A {prefix {} suffix {} joinString 1 targetApp {} replyQ 0} + array set A $details +% \end{tcl} +% The |targetApp| is either a quoted application signature or a full +% path name. Exactly what will have to be done gets affected by this. +% \begin{tcl} + if {[regexp {'(....)'} $A(targetApp) foo sig]} then { + set err [catch {app::ensureRunning $sig} passing] + if {$err == 1} then {set permanent $passing} else { + set permanent "" + set A(app) $A(targetApp) + } + } else { + set tail [file tail $A(targetApp)] +%<*atcl7> + if {[info tclversion]<8.0} then { + set running\ + [regexp "\{\"[quote::Regfind $tail]\" \"" [processes]] + } else { +% + set running 0 + foreach pr [processes] { + if {![string compare $tail [lindex $pr 0]]} then { + set running 1 + break + } + } +% } + set err 0 + set permanent "" + set A(app) $tail + if {$running} then { + set passing "App is running." + } elseif {![catch {launch $A(targetApp)} passing]} then { + set passing "Launched '$A(targetApp)'" + } else { + set err 1 + if {![file exists $A(targetApp)]} then { + set passing "File '$A(targetApp)' not found." + } + set permanent $passing + unset A(app) + } + } +% \end{tcl} +% The |joinString| is also decoded. +% \begin{tcl} + set A(joinString) [lindex [list \n \r \r\n " "] $A(joinString)] + return [list $err $passing $permanent] +} +% \end{tcl} +% \end{proc} +% +% \begin{proc}{item} +% The |item| procedure is mainly about formatting data on its way +% back and forth. +% \begin{tcl} +proc eemenu::doScriptAE::item {lines} { + upvar #0 eemenu::doScriptAE::A A + set script "$A(prefix)[join $lines $A(joinString)]$A(suffix)" + if {$A(replyQ)} then { + if {[catch {tclAE::build::resultData -t 30000 $A(app) misc dosc\ + ---- [tclAE::build::TEXT $script]} res]} then { + list 1 "Error: $res" "Error: $res\n" + } elseif {[string length $res]} then { + list 0 $res "$res\n" + } else { + list 0 "Eval OK." "" + } + } else { + tclAE::send $A(app) misc dosc ---- [tclAE::build::TEXT $script] + list 0 "Sent [llength $lines] lines." "" + } +} +% \end{tcl} +% \end{proc} +% +% \begin{proc}{end} +% The |end| procedure does nothing +% \begin{tcl} +proc eemenu::doScriptAE::end {} {list 0 "" ""} +% \end{tcl} +% \end{proc} +% +% This ends the |if| begun at the top of this subsection. +% \begin{tcl} +} +% \end{tcl} +% +% +% \subsection{\texttt{send} evaluation} +% +% The \texttt{send} command can be used to communicate with other Tk +% applications via an X-windows server. This is a bit restrictive, but +% it is anyway what \AlphaTk\ uses for remote execution of \Tcllogo\ +% code, so I suppose it is what we've got. Right now this method is +% defined whenever the |send| command exists, but it is not uncommon +% that the definition of |send| merely provides communication between +% different interpreters in the same application. +% \begin{tcl} +if {[llength [info commands send]] && [info tclversion]>=8.0} then { +% \end{tcl} +% +% \begin{arrayentry}[eemenu]{evaluate}{tkSend} +% Many details for the |doScriptAE| method have natural counterparts +% for the |tkSend| method, the main difference lies in the +% identification of the target. Whereas \TclAE\ uses static +% information (creator code or file name) to identify the target, the +% name used by the |send| command is in principle dynamic. The +% relevant identifier may change between different times that an +% \textsf{EE~menu} command is used. This is handled by providing a +% fallback for unknown targets. +% \begin{tcl} +set eemenu::evaluate(tkSend) [list\ +% \end{tcl} +% \begin{details}{Evaluate} +% \detailitem{target} +% This is a string that identifies the target. +% \begin{tcl} + [eemenu::define_detail Evaluate "Target" target var]\ +% \end{tcl} +% \detailitem{aliasing} +% This detail controls how the |tkSend| method should behave when +% |begin| is called but the specified target isn't available. +% ``Ask each time'' means present the user with a list of available +% targets each time |begin| is called, and use what the user +% selected there. ``Ask once'' is similar, but the choice will be +% remembered as an \emph{alias} for the specified target (until +% \Alpha\ is quited or the alias target no longer is available). +% ``Fail'' means fail immediately. +% \begin{tcl} + [eemenu::define_detail Evaluate "Alias behaviour"\ + aliasing [list menu [list "Ask once" "Ask each time" "Fail"]]\ + "Ask once" "The thing to do when the target is unavailable."]\ +% \end{tcl} +% \detailitem{joinString} +% The extracted lines is a list, but the script to send should be +% a string, so they will have to be joined somehow. This item +% offers a choice between some common forms of whitespace. +% \begin{tcl} + [eemenu::define_detail Evaluate "Join lines using"\ + joinString [list menuindex [list Lf Cr CrLf Space]] 0]\ +% \end{tcl} +% \detailitem{prefix} +% This will be prepended to the script sent. It can for example be +% used to add some command that takes care of the result. +% \begin{tcl} + [eemenu::define_detail Evaluate "Prefix" prefix var]\ +% \end{tcl} +% \detailitem{suffix} +% This will be appended to the script sent. +% \begin{tcl} + [eemenu::define_detail Evaluate "Suffix" suffix var]\ +% \end{tcl} +% \detailitem{replyQ} +% This flag controls whether the method should wait for the +% remote shell to return a result. +% \begin{tcl} + [eemenu::define_detail Evaluate "Wait for reply" replyQ flag 1]] +% \end{tcl} +% \end{details} +% \end{arrayentry} +% \begin{tcl} +namespace eval eemenu::tkSend {} +% \end{tcl} +% \setnamespace{eemenu::tkSend} +% +% \begin{arrayvar}{alias} +% The |alias| array is used to keep track of which targets are treated +% as aliases for other targets. The index is a value of the +% \texttt{target} detail, the entry value is what it should be +% replaced by. +% \end{arrayvar} +% +% \begin{variable}{last_target} +% The |last_target| variable contains the name of the most recently +% selected target. This is so that the relevant |listpick| dialog +% can ``remember'' the user's preference in this respect. +% \begin{tcl} +set eemenu::tkSend::last_target {} +% \end{tcl} +% \end{variable} +% +% \begin{arrayvar}{A} +% The |tkSend| method keeps its detail settings, and some derived +% quantities, in the |A| array for convenience of access. +% \end{arrayvar} +% +% +% \begin{proc}{begin} +% The main task of this |begin| procedure is to verify that the target +% is available. +% \begin{tcl} +proc eemenu::tkSend::begin {details source} { + eemenu::multiupvar A alias last_target + array set A {prefix {} suffix {} joinString 1 target {} replyQ 1\ + aliasing "Ask once"} + array set A $details +% \end{tcl} +% The treatment of aliases depend on the |aliasing| setting. If it is +% \texttt{Ask once} then an alias will be used even if a target with +% the given name is available. The rationale for this is that the user +% has specified that the commands should be sent to that application. +% \begin{tcl} + set T [lsort -dictionary [winfo interps]] + switch -- $A(aliasing) "Ask each time" { + if {[lsearch -exact $T $A(target)]>=0} then { + set target $A(target) + } + } "Fail" { + if {[lsearch -exact $T $A(target)]>=0} then { + set target $A(target) + } else { + set passing "Target '$A(target)' unavailable." + return [list 1 $passing $passing] + } + } default { + set target $A(target) + if {[info exists alias($target)]} then { + set target $alias($target) + } + if {[lsearch -exact $T $target]<0} then {unset target} + } +% \end{tcl} +% If the local |target| variable is not set at this point then it is +% time to ask the user. +% \begin{tcl} + if {![info exists target]} then { + if {[catch {listpick -p "Target '$A(target)' unavailable.\ + Please pick a new one." -L [list $last_target]\ + [linsert $T end "------------------" "Launch new shell"]}\ + target]} then { + return [list 1 "Canceled" ""] + } + switch -exact -- $target\ + "------------------" - "Launch new shell" { + set before [clock seconds] + app::launchElseTryThese [uplevel #0 {set tclshSigs}] tclshSig\ + {Please locate the remote Tcl application} + while {![string compare $T\ + [lsort -dictionary [winfo interps]]] &&\ + [clock seconds] - $before < 60} {update} + set T2 [lsort -dictionary [winfo interps]] + if {![string compare $T $T2]} then { + return [list 1 {Timed out; no new shell started within\ + 60 seconds.} ""] + } + foreach target $T2 t $T { + if {[string compare $target $t]} then {break} + } + set passing "Launched new shell '$target'." + } default { + set passing "Sending code to '$target'." + } +% \end{tcl} +% The last |foreach| above locates the first |target| in |$T2| which +% is not in |$T|. This |foreach| is the reason this code requires +% \Tcllogo\,8. +% +% Now the target should be stored in the necessary variables. +% \begin{tcl} + set last_target $target + if {![string compare $A(aliasing) "Ask once"]} then { + set alias($A(target)) $target + } + } else { + set passing "Sending code to '$target'." + } + set A(target) $target +% \end{tcl} +% The |joinString| is also decoded. +% \begin{tcl} + set A(joinString) [lindex [list \n \r \r\n " "] $A(joinString)] + return [list 0 $passing ""] +} +% \end{tcl} +% \end{proc} +% +% \begin{proc}{item} +% The |item| procedure is mainly about formatting data on its way +% back and forth. +% \begin{tcl} +proc eemenu::tkSend::item {lines} { + upvar #0 eemenu::tkSend::A A + set script "$A(prefix)[join $lines $A(joinString)]$A(suffix)" + if {$A(replyQ)} then { + if {[catch {send $A(target) $script} res]} then { + list 1 "Error: $res" "Error: $res\n" + } elseif {[string length $res]} then { + list 0 "Eval OK: $res" "$res\n" + } else { + list 0 "Eval OK." "" + } + } else { + send -async $A(target) $script + list 0 "Sent [llength $lines] lines." "" + } +} +% \end{tcl} +% \end{proc} +% +% \begin{proc}{end} +% The |end| procedure does nothing +% \begin{tcl} +proc eemenu::tkSend::end {} {list 0 "" ""} +% \end{tcl} +% \end{proc} +% +% This ends the |if| begun at the top of this subsection. +% \begin{tcl} +} +% \end{tcl} +% +% \subsubsection*{Test code} +% +% The code below fakes definitions of |send| and |winfo interps| using +% \Tcllogo\AE\ and |processes| for the purpose of testing the above code. +% \begin{tcl} +%<*sendtest> +proc send {target cmd args} { + set sync 1 + if {![string compare $target -async]} then { + set sync 0 + set target $cmd + set cmd [lindex $args 0] + } + set cmd [tclAE::build::TEXT $cmd] + if {$sync} then { + tclAE::build::resultData -t 30000 $target misc dosc ---- $cmd + } else { + tclAE::send $target misc dosc ---- $cmd + } +} +proc winfo {subcmd args} { + if {[string compare $subcmd "interps"]} then { + error "Unimplemented winfo subcommand: $subcmd" + } + set res [list] + global tclshSigs + foreach pr [processes] { + if {[lsearch -exact $tclshSigs [lindex $pr 1]]>=0} then { + lappend res [file tail [lindex $pr 0]] + } + } + return $res +} +% +% \end{tcl} +% +% +% \subsection{\texttt{dde} evaluation} +% +% Dynamic Data Exchange (\texttt{dde}) is, as I understand it, the +% Windoze equivalent of AppleEvents, although the events sent have much +% less structure. The machinery needed here is quite similar to that +% for \texttt{send} evaluation, but there are a few extra details that +% can be set. +% \changes{v\,0.1.1}{2002/07/21}{Renamed the \textsf{Dde} method to +% \textsf{Windoze DDE}. (LH)} +% +% \begin{tcl} +if {[llength [info commands dde]] && [info tclversion]>=8.0} then { +% \end{tcl} +% +% \begin{arrayentry}[eemenu]{evaluate}{windozeDDE} +% The details for the |windozeDDE| method constitute a superset of those for +% the |tkSend| method. +% \begin{tcl} +set eemenu::evaluate(windozeDDE) [list\ +% \end{tcl} +% \begin{details}{Evaluate} +% \detailitem{service} +% This is a string that specifies the service to use. +% \begin{tcl} + [eemenu::define_detail Evaluate "Service" service var "TclEval"]\ +% \end{tcl} +% \detailitem{target} +% This is a string that identifies the target application, or as +% it appears to be called, the ``topic''. +% \begin{tcl} + [eemenu::define_detail Evaluate "Target" target var]\ +% \end{tcl} +% \detailitem{aliasing} +% This detail controls how the |windozeDDE| method should behave when +% |begin| is called but the specified target isn't available. +% ``Ask each time'' means present the user with a list of available +% targets each time |begin| is called, and use what the user +% selected there. ``Ask once'' is similar, but the choice will be +% remembered as an \emph{alias} for the specified target (until +% \Alpha\ is quited or the alias target no longer is available). +% ``Fail'' means fail immediately. +% \begin{tcl} + [eemenu::define_detail Evaluate "Alias behaviour"\ + aliasing [list menu [list "Ask once" "Ask each time" "Fail"]]\ + "Ask once" "The thing to do when the target is unavailable."]\ +% \end{tcl} +% \detailitem{launchCmd} +% This is a \Tcllogo\ command which, when executed by \Alpha, +% should launch an application that provides a new target for +% the given service. The command is evaluated at a global level. +% If it is an empty string, then no option for launching will be +% presented in the target selection dialog. +% \begin{tcl} + [eemenu::define_detail Evaluate "Launch command" launchCmd var2]\ +% \end{tcl} +% \detailitem{joinString} +% The extracted lines is a list, but the script to send should be +% a string, so they will have to be joined somehow. This item +% offers a choice between some common forms of whitespace. +% \begin{tcl} + [eemenu::define_detail Evaluate "Join lines using"\ + joinString [list menuindex [list Lf Cr CrLf Space]] 2]\ +% \end{tcl} +% \detailitem{prefix} +% This will be prepended to the script sent. It can for example be +% used to add some command that takes care of the result. +% \begin{tcl} + [eemenu::define_detail Evaluate "Prefix" prefix var]\ +% \end{tcl} +% \detailitem{suffix} +% This will be appended to the script sent. +% \begin{tcl} + [eemenu::define_detail Evaluate "Suffix" suffix var]\ +% \end{tcl} +% \detailitem{request} +% This is the name of the item to request as result from the +% evaluation. If it is an empty string then no request is made. +% \begin{tcl} + [eemenu::define_detail Evaluate "Requested data" request var]] +% \end{tcl} +% \end{details} +% \end{arrayentry} +% \begin{tcl} +namespace eval eemenu::windozeDDE {} +% \end{tcl} +% \setnamespace{eemenu::windozeDDE} +% +% \begin{arrayvar}{alias} +% The |alias| array is used to keep track of which targets are treated +% as aliases for other targets. The index is a value of the +% \texttt{target} detail, the entry value is what it should be +% replaced by. +% \end{arrayvar} +% +% \begin{variable}{last_target} +% The |last_target| variable contains the name of the most recently +% selected target. This is so that the relevant |listpick| dialog +% can ``remember'' the user's preference in this respect. +% \begin{tcl} +set eemenu::windozeDDE::last_target {} +% \end{tcl} +% \end{variable} +% +% \begin{arrayvar}{A} +% The |windozeDDE| method keeps its detail settings, and some derived +% quantities, in the |A| array for convenience of access. +% \end{arrayvar} +% +% +% \begin{proc}{begin} +% The main task of this |begin| procedure is to verify that the target +% is available. +% \begin{tcl} +proc eemenu::windozeDDE::begin {details source} { + eemenu::multiupvar A alias last_target + array set A {service {} target {} aliasing "Ask once" launchCmd {}\ + prefix {} suffix {} joinString 2 request {}} + array set A $details + if {![string length $A(service)]} then { + return [list 1 "No service has been specified." ""] + } +% \end{tcl} +% The treatment of aliases depend on the |aliasing| setting. If it is +% \texttt{Ask once} then an alias will be used even if a target with +% the given name is available. The rationale for this is that the user +% has specified that the commands should be sent to that application. +% \begin{tcl} + set T [list] + foreach st [dde services $A(service) {}] {lappend T [lindex $st 1]} + set T [lsort -dictionary $T] + switch -- $A(aliasing) "Ask each time" { + if {[lsearch -exact $T $A(target)]>=0} then { + set target $A(target) + } + } "Fail" { + if {[lsearch -exact $T $A(target)]>=0} then { + set target $A(target) + } else { + set passing "Target '$A(target)' unavailable." + return [list 1 $passing $passing] + } + } default { + set target $A(target) + if {[info exists alias($target)]} then { + set target $alias($target) + } + if {[lsearch -exact $T $target]<0} then {unset target} + } +% \end{tcl} +% If the local |target| variable is not set at this point then it is +% time to ask the user. +% \begin{tcl} + if {![info exists target]} then { + set T2 $T + if {[string length $A(launchCmd)]} then { + if {[llength $T2]} then {lappend T2 "------------------"} + lappend T2 "Launch another" + } + if {![llength $T2]} then { + return [list 1 "No '$A(service)' targets are, or could be\ + made, available." ""] + } + if {[catch {listpick -p "Target '$A(target)' unavailable.\ + Please pick a new one." -L [list $last_target] $T2}\ + target]} then { + return [list 1 "Canceled" ""] + } + switch -exact -- $target\ + "------------------" - "Launch another" { + set before [clock seconds] + uplevel #0 $A(launchCmd) + set target {} + while {[clock seconds] - $before < 60 &&\ + ![string length $target]} { + foreach st [dde services $A(service) {}] { + if {[lsearch -exact $T [lindex $st 1]]<0} then { + set target [lindex $st 1] + break + } + } + update + } + if {![string length $target]} then { + return [list 1 {Timed out; no new target launched within\ + 60 seconds.} ""] + } + set passing "Launched new shell '$target'." + } default { + set passing "Sending code to '$target'." + } +% \end{tcl} +% The last |foreach| above locates the first |target| in |$T2| which +% is not in |$T|. This |foreach| is the reason this code requires +% \Tcllogo\,8. +% +% Now the target should be stored in the necessary variables. +% \begin{tcl} + set last_target $target + if {![string compare $A(aliasing) "Ask once"]} then { + set alias($A(target)) $target + } + } else { + set passing "Sending code to '$target'." + } + set A(target) $target +% \end{tcl} +% The |joinString| is also decoded. +% \begin{tcl} + set A(joinString) [lindex [list \n \r \r\n " "] $A(joinString)] + return [list 0 $passing ""] +} +% \end{tcl} +% \end{proc} +% +% \begin{proc}{item} +% The |item| procedure is mainly about formatting data on its way +% back and forth. +% \begin{tcl} +proc eemenu::windozeDDE::item {lines} { + upvar #0 eemenu::windozeDDE::A A + set script "$A(prefix)[join $lines $A(joinString)]$A(suffix)" + if {[catch {dde execute $A(service) $A(target) $script} res]} then { + list 1 "dde error: $res" "$res\n" + } elseif {![string length $A(request)]} then { + list 0 "Sent [llength $lines] lines." "" + } else { + if {[catch {dde request $A(service) $A(target) $A(request)} res]}\ + then { + list 1 "dde error: $res" "dde error: $res\n" + } elseif {[string length $res]} then { + list 0 $res "$res\n" + } else { + list 0 "Eval OK." "" + } + } +} +% \end{tcl} +% \end{proc} +% +% \begin{proc}{end} +% The |end| procedure does nothing +% \begin{tcl} +proc eemenu::windozeDDE::end {} {list 0 "" ""} +% \end{tcl} +% \end{proc} +% +% This ends the |if| begun at the top of this subsection. +% \begin{tcl} +} +% \end{tcl} +% +% \subsubsection*{Test code} +% +% The code below fakes a definition of |dde| using |interp|. +% \begin{tcl} +%<*ddetest> +proc dde {subcmd args} { + switch -- $subcmd servername { + error "dde servername is not implemented." + } execute { + if {[llength $args] != 3} then { + error "Wrong number of arguments for dde execute." + } + if {"TclEval" != [lindex $args 0]} then { + error "Unknown servername: [lindex $args 0]" + } + interp eval [lindex $args 1] [lindex $args 2] + } poke { + if {[llength $args] != 4} then { + error "Wrong number of arguments for dde poke." + } + if {"TclEval" != [lindex $args 0]} then { + error "Unknown servername: [lindex $args 0]" + } + interp eval [lindex $args 1]\ + [list set [lindex $args 2] [lindex $args 3]] + } request { + if {[llength $args] != 3} then { + error "Wrong number of arguments for dde request." + } + if {"TclEval" != [lindex $args 0]} then { + error "Unknown servername: [lindex $args 0]" + } + interp eval [lindex $args 1] [list set [lindex $args 2]] + } services { + if {[llength $args] != 2} then { + error "Wrong number of arguments for dde services." + } + set server [lindex $args 0] + if {[string length $server] && $server != "TclEval"} then { + return "" + } + set topic [lindex $args 1] + if {![string length $topic]} then { + set res [list] + foreach slave [interp slaves] { + lappend res [list TclEval $slave] + } + return $res + } elseif {[interp exists $topic]} then { + return [list [list TclEval $topic]] + } else { + return "" + } + } default { + error "Unimplemented subcommand: $subcmd" + } +} +% +% \end{tcl} +% +% +% \section{Report methods} +% \label{Sec:Report} +% +% The report method takes care of sending output to the user. Like +% extraction and evaluation methods there is one `begin' procedure +% |log_open| and one `end' procedure |log_close| for each method, but +% there are actually two `iterate' procedures. This is because the +% extracted code is logged using |log_code| \emph{before} it is +% evaluated, whereas the result is only known after the code has been +% evaluated and is then logged using |log_result|. +% +% The \describestring[proc][eemenu::\meta{method}]{log_open}|log_open| +% procedure has the syntax +% \begin{quote} +% |eemenu::|\meta{method}|::log_open| \word{details} \word{source} +% \end{quote} +% where the \word{details} is the key--value list of setting details +% and \word{source} is the source window name as returned by the +% extraction method |start| procedure. There is no result from this +% procedure, but if there is something wrong with its argument then it +% may stop the \eemenu\ command by returning with an error. +% +% The reason the \word{source} argument is provided is that some report +% methods might want to make some aspect of their output, such as for +% example a window title or log file name, dependent on the input source. +% Neither the |statusLine| nor the |logWindow| method do this, though. +% +% The \describestring[proc][eemenu::\meta{method}]{log_code}|log_code| +% procedure has the syntax +% \begin{quote} +% |eemenu::|\meta{method}|::log_code| \word{lines} +% \end{quote} +% where \word{lines} is the list of lines that will be sent to the +% evaluation method. The +% \describestring[proc][eemenu::\meta{method}]{log_result}|log_result| +% procedure has the syntax +% \begin{quote} +% |eemenu::|\meta{method}|::log_result| \word{error?} +% \word{passing result} \word{permanent result} +% \end{quote} +% where the \word{error?} is |1| if there was an evaluation error and +% |0| otherwise. The \word{passing result} is a result string that +% describes the current state of the evaluation process; it is meant +% to be replaced by the next \word{passing result} the next time +% |log_result| is called. By constrast the \word{permanent result} is +% meant to be accumulated for each call to |log_result|. Whether a +% report method uses one or the other (or both) depends on which makes +% most sense for the output channels it employs. If the \word{passing +% result} string is empty then it should not replace a previous +% \word{passing result} string. If the \word{permanent} string is +% nonempty then it must end with a newline character. +% +% The \describestring[proc][eemenu::\meta{method}]{log_close}|log_close| +% procedure is responsible for cleaning up after the method, but only +% to the extent that this is necessary. If |log_open| opened a file then +% |log_close| should close it, but if |log_open| merely opened a window +% then |log_close| should most likely not close it since the user will +% want to see what is written in that window. The syntax is simply +% \begin{quote} +% |eemenu::|\meta{method}|::log_close| +% \end{quote} +% and there is no result from the procedure. +% +% +% +% \subsection{Status line reports} +% +% The |statusLine| method shows the \word{passing result}s on the +% status line, but does nothing else. +% +% \setnamespace{eemenu::statusLine} +% \begin{arrayentry}[eemenu]{report}{statusLine} +% There are no details settings for this method. +% \begin{tcl} +namespace eval eemenu::statusLine {} +set eemenu::report(statusLine) [list] +% \end{tcl} +% \end{arrayentry} +% +% \begin{proc}{log_open} +% \begin{proc}{log_close} +% \begin{proc}{log_code} +% \begin{proc}{log_result} +% Only the |log_result| procedure needs to do anything. +% \begin{tcl} +proc eemenu::statusLine::log_open {details source} {} +proc eemenu::statusLine::log_close {} {} +proc eemenu::statusLine::log_code {lines} {} +proc eemenu::statusLine::log_result {was_err passing permanent} { + if {[string length $passing]} then {status::msg $passing} +} +% \end{tcl} +% \end{proc}\end{proc}\end{proc}\end{proc} +% +% +% \subsection{Log window reports} +% +% The |logWindow| method opens a particular window and inserts +% everything that is logged there. +% +% \begin{arrayentry}[eemenu]{report}{logWindow} +% There are three detail settings for |logWindow| reports. +% \begin{tcl} +set eemenu::report(logWindow) [list\ +% \end{tcl} +% \begin{details}{Report} +% \detailitem{winName} +% This is the name of the log window. +% \begin{tcl} + [eemenu::define_detail Report "Window name" winName var "Log"]\ +% \end{tcl} +% \detailitem{winMode} +% This is the mode to use for the log window. +% \begin{tcl} + [eemenu::define_detail Report "Window mode" winMode mode "Text"]\ +% \end{tcl} +% \detailitem{prompt} +% This is a ``prompt'' string that will be put at the beginning of +% each code line. +% \begin{tcl} + [eemenu::define_detail Report "Prompt" prompt var\ + "[text::Ascii 200 1] "]\ +% \end{tcl} +% \detailitem{antiprompt} +% This is a string that will be put at the beginning of each line +% that is not a code line. +% \begin{tcl} + [eemenu::define_detail Report "Antiprompt" antiprompt var ""]] +% \end{tcl} +% \end{details} +% \end{arrayentry} +% \begin{tcl} +namespace eval eemenu::logWindow {} +% \end{tcl} +% \setnamespace{eemenu::logWindow} +% +% \begin{arrayvar}{A} +% The |logWindow| method keeps its detail settings in the |A| +% array. Those are all the global variables it has. +% \end{arrayvar} +% +% \begin{proc}{log_open} +% This procedure opens the log window (if it isn't open already) +% and positions the cursor at the end of the window. This is +% assumed to be at the beginning of a line. +% \begin{tcl} +proc eemenu::logWindow::log_open {details source} { + upvar #0 eemenu::logWindow::A A + array set A {winName {Log window} prompt {} antiprompt {}} + array set A $details + set win $A(winName) + if {[lsearch -exact [winNames] $win] == -1} then { + set call [list new -n $win -shell 1] + if {[mode::exists $A(winMode)]} then {lappend call -m $A(winMode)} + eval $call + } + set t [maxPos -w $win] + select -w $win $t $t +} +% \end{tcl} +% \end{proc} +% +% \begin{proc}{log_code} +% The |log_code| procedure inserts the lines at the current position +% in the file. This will be at the end of the file, unless the +% evaluation modifies this. +% \begin{tcl} +proc eemenu::logWindow::log_code {lines} { + upvar #0 eemenu::logWindow::A A + insertText -w $A(winName) "$A(prompt)[join $lines "\n$A(prompt)"]\n" +} +% \end{tcl} +% \end{proc} +% +% \begin{proc}{log_result} +% The |log_result| procedure inserts the \word{permanent result} at +% the current position in the file. If the |antiprompt| is +% nonempty then the result is first broken into lines and then +% reassembled, however. It also displays the \word{passing result} on +% the status line (this has to be done last, since |insertText| +% clears the status line). +% \begin{tcl} +proc eemenu::logWindow::log_result {was_err passing permanent} { + if {[string length $permanent]} then { + upvar #0 eemenu::logWindow::A A + if {[string length $A(antiprompt)]} then { + set L [lreplace [split $permanent \n\r] end end] + set permanent "[join $L "\n$A(antiprompt)"]\n" + } + insertText -w $A(winName) "$A(antiprompt)$permanent" + } + if {[string length $passing]} then {status::msg $passing} +} +% \end{tcl} +% \end{proc} +% +% \begin{proc}{log_close} +% This procedure does nothing. +% \begin{tcl} +proc eemenu::logWindow::log_close {} {} +% \end{tcl} +% \end{proc} +% +% +% \section{Default command definitions} +% \label{Sec:DefCmdA} +% +% This section contains some examples of \eemenu\ commands and +% it also provides defaults for the |cmdA| array. +% +% \setnamespace{eemenu} +% +% \begin{arrayvar}{cmdA} +% If this array is already set then there is no need to source the +% rest of the file. +% \begin{tcl} +if {[info exists eemenu::cmdA]} then {return} +% \end{tcl} +% \end{arrayvar} +% +% +% \begin{arrayentry}{cmdA}{dtx -> internal Tcl} +% This is the command that corresponds most closely to that provided +% by the old \textsf{dtxload} package. +% \begin{tcl} +set {eemenu::cmdA(dtx -> internal Tcl)} [list\ +% \end{tcl} +% By default the command is active and bound to \textsf{Cmd--L} in +% \TeX\ mode, but it is not put in the menu. +% \begin{tcl} + active 1 binding /L internal Tcl)} +% \end{tcl} +% \end{arrayentry} +% +% +% \begin{arrayentry}{cmdA}{dtx -> remote Tcl} +% This is a command that I have lately found to be increasingly useful. +% The functionality was provided in the \textsf{dtxload} package, but +% it was always slightly awkward as it was not a distinct command. +% Instead the |evaluateRemotely| variable was used as a selector +% between this and the previous command. +% +% Since these two commands are anyway quite similar, I'll copy +% settings from the above, only changing those that are different. +% \begin{tcl} +catch {unset A} +array set A [set {eemenu::cmdA(dtx -> internal Tcl)}] +% \end{tcl} +% Since keys combinations are sort of in short supply, this command +% is by default inactive but has the same binding and mode as its +% \textsf{internal~Tcl} counterpart. This means that if one of these +% commands is activated then the other is automatically deactivated +% and so it is anyway easy to switch between them. I also put this +% remote \Tcllogo\ command in the menu. +% \begin{tcl} +set A(active) 0 +set A(in_menu) 1 +% \end{tcl} +% +% Communication with remote interpreters is a messy business, due to +% the many technical details one has do deal with. It is also very +% platform-dependent. The Mac\,OS settings are however not too +% complicated, much thanks to \TclAE. +% \changes{v\,0.3}{2003/06/01}{Corrected condition used for selecting +% remote evaluation method. (LH)} +% \begin{tcl} +if {[info exists eemenu::evaluate(doScriptAE)]} then { + set A(evaluator) "Do Script AE" + set A(eval_extra)\ + [list targetApp '${tclshSig}' joinString 0 replyQ 1] +} \ +% \end{tcl} +% +% The Windoze settings are the most complicated. One reason for this +% is that |dde execute| does not return the result of the script, so +% that a separate call is needed for this. +% \begin{tcl} +elseif {[info exists eemenu::evaluate(windozeDDE)]} then { + set A(evaluator) "Windoze DDE" + set A(eval_extra) [list\ +% + service TclEval target Tcl_1 launchCmd\ + {exec $tclshSig [file join $HOME Tools winRemoteShell.tcl] &}\ +% + joinString 0 prefix "catch \{" suffix "\} alpha_result"\ + request alpha_result] +} \ +% \end{tcl} +% +% Finally, there are the settings for |send|, which are used on all +% other platforms. +% \begin{tcl} +else { + set A(evaluator) "Tk Send" + set A(eval_extra) [list target wish joinString 0 replyQ 1] +} +% \end{tcl} +% Then all that remains is to store the settings into the |cmdA| array. +% \begin{tcl} +set {eemenu::cmdA(dtx -> remote Tcl)} [array get A] +prefs::modified {eemenu::cmdA(dtx -> remote Tcl)} +unset A +% \end{tcl} +% \end{arrayentry} +% +% \begin{arrayentry}{cmdA}{Tcl example in AIDA} +% AIDA probably isn't that well known, but in brief it is an +% experimental source format for \Alpha\ documentation, which can +% be converted to plain text, \LaTeX, HTML, and some other formats too. +% Verbatim text lines (as needed for code examples) can of course be +% included expressed in AIDA; the required markup is mainly that each +% such line begins with the two characters \verb"(|". Such code can +% easily be extracted using the regexp extractor. +% +% This code is no longer included, since AIDA isn't much used anymore. +% \begin{tcl} +%<*aidacode> +set {eemenu::cmdA(Tcl example in AIDA)} [list\ +% + extractor Regexp extract_extra [list\ +% \end{tcl} +% The filter should get the lines matching the expression, i.e., it +% should work in \texttt{grep} mode, and the characters that are +% matched should be removed. +% \begin{tcl} + filter_mode grep filterRE {^\(\|} searchRE {^\(\|} replaceRE {}]\ +% \end{tcl} +% Evaluation is internally in \Alpha, and the completion test can use +% |info complete|. +% \begin{tcl} + complete "Tcl Info Complete" complete_extra {}\ +% + evaluator "Internal Tcl" eval_extra {}\ +% \end{tcl} +% Then there's the matter of how to report the result. Since that +% hasn't been used yet, it seems a good idea to try to use a log +% window here. +% \begin{tcl} + reporter "Log Window" report_extra {winName {AIDA example log}\ + winMode Tcl prompt {% } antiprompt {}}\ +% \end{tcl} +% Finally there's the matter of the binding and the menu. There isn't +% anything new here, except that the binding is only made active if +% \texttt{AIDA} mode is installed (since modes not installed will +% sometimes be interpreted as ||, i.e., global). +% \begin{tcl} + active [mode::exists AIDA] binding /L +% \end{tcl} +% \end{arrayentry} +% +% \begin{arrayentry}{cmdA}{Tcl Wiki} +% When editing a page on the \Tcllogo'ers Wiki~\cite{TclWiki} or the +% \AlphaTcl\ Wiki~\cite{AlphaTclWiki}, one occasionally finds the need +% to test some piece of \Tcllogo\ code on that page. This is down to a +% single keypress with the following command, that extracts lines of +% verbatim text from a Wiki page and feeds them to the internal +% interpreter. +% +% The syntax of verbatim lines on the \Tcllogo'ers Wiki is +% basically ``any line that starts with a space'', but unfortunately +% there are a few exceptions. The equivalents of \LaTeX\ \verb|\item|s +% for \texttt{itemize} or \texttt{enumerate} are coded as lines that +% begin with three spaces, a \texttt{*} or \meta{digit}\texttt{.} +% respectively, and another space. The equivalents of \LaTeX\ +% \verb|\item|s for \texttt{description} are coded as lines that +% begin with \emph{at least} three spaces, some other characters, a +% colon, and another three spaces (exactly). It is possible to write a +% regular expression to filter out such lines. +% \begin{tcl} +set {eemenu::cmdA(Tcl Wiki)} [list\ +% + extractor Regexp extract_extra [list\ +% \end{tcl} +% The filter works in \texttt{anti-grep} mode, and has to filter out +% four different types of non-code lines. There is however no need +% for further substitutions on the code lines. +% \begin{tcl} + filter_mode anti-grep searchRE {} replaceRE {}\ + filterRE {^[^ ]|^ (\*|[0-9]\.) |^ .+: [^ ]} ]\ +% \end{tcl} +% Evaluation is internally in \Alpha, and the completion test can use +% |info complete|. +% \begin{tcl} + complete "Tcl Info Complete" complete_extra {}\ +% + evaluator "Internal Tcl" eval_extra {}\ +% \end{tcl} +% Then there's the matter of how to report the result. Since that +% hasn't been used yet, it seems a good idea to try to use a log +% window here. +% \begin{tcl} + reporter "Log Window" report_extra {winName {Wiki code log}\ + winMode Tcl prompt {% } antiprompt {}}\ +% \end{tcl} +% Finally there's the matter of the binding and the menu. There isn't +% anything new here, except that the binding is only made active if +% \texttt{Wiki} mode is installed (since modes not installed will +% sometimes be interpreted as ||, i.e., global). +% \begin{tcl} + mode Wiki active [mode::exists Wiki] binding /L +\documentclass{tclldoc} +\begin{document} +\DocInput{sourcedtx.dtx} +\end{document} +% +% \fi +% +% \title{The \textsf{sourcedtx} package} +% \author{Lars Hellstr\"om} +% \date{18 July 2003} +% \maketitle +% +% \begin{abstract} +% The \textsf{sourcedtx} package provides a \Tcllogo\ command +% |dtx::source| that makes it possible to |source| \Tcllogo\ code +% from a \textsf{doc}-style \texttt{.dtx} file without docstripping +% it first. +% \end{abstract} +% +% +% \begin{tcl} +%<*pkg> +namespace eval dtx {} +% \end{tcl} +% \setnamespace{dtx} +% +% \begin{proc}{strip_string} +% The |strip_string| procedure does the actual docstripping for +% |source_dtx|. The syntax is +% \begin{quote} +% |dtx::strip_string| \word{text} \word{option list} +% \word{meta prefix}\regopt\ \word{interaction}\regopt +% \end{quote} +% where \word{text} is the string to docstrip and \word{option list} +% is the list of options to use. The \word{meta prefix} (by default +% two percent signs) is the string to use for the \textsc{docstrip} +% parameter \verb|\MetaPrefix|. The \word{interation} (by default 1) +% is a flag for the ``interaction'' level to use. |0| means error +% messages are simply written to |stderr|, whereas |1| means an error +% will be raised. (It is possible that \word{interaction} will be +% extended to a ``flag word'' where each bit controls some aspect of +% the interaction.) +% +% When errors are raised, the |errorCode| is set to a list with the +% format +% \begin{quote} +% |DOCSTRIP| \word{situation} \word{lineno} +% \end{quote} +% where \word{lineno} is the line number (starting at one) of the line +% where the error was detected. The \word{situation}s are described +% below, at the positions in the code where they can occur. +% +% \begin{tcl} +proc dtx::strip_string {text options {metaprefix %%} {interaction 1}} { +% \end{tcl} +% The |O| array has entires for precisely the specified options, so +% that an opion can be tested using |info exists|. +% \begin{tcl} + foreach option $options {set O($option) ""} +% \end{tcl} +% |stripped| is where the text that passes docstripping is collected. +% \begin{tcl} + set stripped "" +% \end{tcl} +% |block_stack| is the list of modules inside which the current line +% lies. |offlevel| is the number of modules that must be exited +% before code lines should once again be included. |verbatim| is a +% flag for whether verbatim mode is in force. +% \begin{tcl} + set block_stack [list] + set offlevel 0 + set verbatim 0 +% \end{tcl} +% |lineno| is the input line number counter, for use in error +% messages. +% \begin{tcl} + set lineno 1 +% \end{tcl} +% Here starts the main loop over lines in the \word{text}. It +% constitutes the majority of the procedure and is split in two +% parts. The smaller part handles lines in verbatim mode (unusual), +% the large part handles lines in normal mode (with comment lines, +% code lines, guard lines, and so on). +% \begin{tcl} + foreach line [split $text \n] { + if {$verbatim} then { + if {$line eq $endverbline} then { + set verbatim 0 + } elseif {!$offlevel} then { + append stripped $line \n + } + } else { + switch -glob -- $line %%* { + if {!$offlevel} then { + append stripped $metaprefix\ + [string range $line 2 end] \n + } + } %<<* { + set endverbline "%[string range $line 3 end]" + set verbatim 1 + } %<* { + if {[regexp -- {^%<([*/+-]?)([^>]*)>(.*)$} $line ""\ + modifier expression line]} then { +% \end{tcl} +% There is a well-formed guard line. First the expression is +% evaluated, by converting it to an |expr| expression. +% \begin{tcl} + regsub -all -- {\\|\{|\}|\$|\[|\]| |;} $expression\ + {\\&} E + regsub -all -- {,} $E {|} E + regsub -all -- {[^()|&!]+} $E {[info exists O(&)]} E + set val [expr $E] + switch -exact -- $modifier * { + lappend block_stack $expression + if {$offlevel || !$val} then {incr offlevel} + } / { + if {![llength $block_stack]} then { +% \end{tcl} +% In this case there was no open block for this guard to end. That +% is a \describestring[error situation]{SPURIOUS}|SPURIOUS| +% \word{situation}. +% \begin{tcl} + if {$interaction} then { + error "Spurious end block \ + ignored." ""\ + [list DOCSTRIP SPURIOUS $lineno] + } else { + puts stderr "docstrip: Spurious end\ + block ignored on line\ + $lineno." + } + } else { + if {[string compare $expression\ + [lindex $block_stack end]]} then { +% \end{tcl} +% In this case the expression of the block being closed does not match +% the expression on the block on top of the stack. That is a +% \describestring[error situation]{MISMATCH}|MISMATCH| +% \word{situation}. \textsc{docstrip} by default raises an error and +% recovers by treating this situation as a typo. +% \begin{tcl} + if {$interaction} then { + error "Found instead of\ + ." ""\ + [list DOCSTRIP MISMATCH $lineno] + } + puts stderr "docstrip:\ + Found instead of\ + on line\ + $lineno." + } +% \end{tcl} +% All that error processing makes it easy to lose track, but the +% following two lines are what does the real work for an end of block +% guard: pop a block off the stack and decrement the |offlevel|. +% \begin{tcl} + if {$offlevel} then {incr offlevel -1} + set block_stack [lreplace $block_stack end end] + } + } - { + if {!$offlevel && !$val} then { + append stripped $line \n + } + } default { + if {!$offlevel && $val} then { + append stripped $line \n + } + } + } else { +% \end{tcl} +% In this case the line looks like a guard line, but there is no |>| +% terminating the guard expression. This is a +% \describestring[error situation]{BADGUARD}|BADGUARD| +% \word{situation}. +% \begin{tcl} + if {$interaction} then { + error "Malformed guard on line $lineno." ""\ + [list DOCSTRIP BADGUARD $lineno] + } else { + puts stderr "docstrip: Malformed guard\ + on line $lineno:" + puts stderr $line + } + } + } %* {}\ +% \end{tcl} +% With comment lines, nothing is done. A line being the exact string +% |\endinput| terminates the stripping. +% \begin{tcl} + {\\endinput} { + break + } default { +% \end{tcl} +% Other lines are code lines. These are included or not, depending on +% the |offlevel|. +% \begin{tcl} + if {!$offlevel} then {append stripped $line \n} + } + } + incr lineno + } + return $stripped +} +% \end{tcl} +% \end{proc} +% +% +% \begin{proc}{source} +% This procedure behaves as a docstripping |source| command: it reads +% a file, docstrips its contents in memory, and evaluates the result +% as a \Tcllogo\ script in the context of the caller. The syntax is +% \begin{quote} +% |dtx::source| \word{dtx-file} \word{options} +% \end{quote} +% where \word{dtx-file} is the file name and \word{options} is the +% list of options. +% \begin{tcl} +proc dtx::source {name options} { + set F [open $name r] + set text [read $F] + close $F + uplevel 1 [dtx::strip_string $text $options #] +} +% +% \end{tcl} +% \end{proc} +% +% +\endinput + + \ No newline at end of file diff --git a/macros/latex/contrib/tclldoc/tools/sourcedtx.ins b/macros/latex/contrib/tclldoc/tools/sourcedtx.ins new file mode 100644 index 0000000000..f26acf7ee7 --- /dev/null +++ b/macros/latex/contrib/tclldoc/tools/sourcedtx.ins @@ -0,0 +1,31 @@ +% sourcedtx.ins --- DOCSTRIP installation script for sourcedtx.tcl. +\input docstrip + +% Redefine the \MetaPrefix; it should be something which starts a +% until-end-of-line comment: +\edef\MetaPrefix{\string#\string#} + + +% Redefine the file preamble and postamble; this is necessary because +% otherwise the old \metaPrefix is inserted at the beginning of these +% lines. +\preamble + +This file is put in the public domain. Note however that +************************************** +* This Source is not the True Source * +************************************** +the true source is the file from which this one was generated. + +\endpreamble + +\postamble +\endpostamble + +% Actually make sourcedtx.tcl: +\generate{ + \file{sourcedtx.tcl}{\from{sourcedtx.dtx}{pkg}} +} + + +\end -- cgit v1.2.3