Ruff! is not intended to be a standalone script. Rather the package provides commands that should be driven from a script that controls which particular namespaces, classes etc. are to be included.
To document a package, first load it into a Tcl interpreter. Then load ruff
and invoke the document command to document classes and commands within one or more namespaces.
For example, the following command will document the NS
namespace using the built-in HTML formatter.
package require ruff ::ruff::document ::NS
The output will be written to NS.html
.
The following will document the namespace NS
, NS2
and their children, splitting the output across multiple pages.
::ruff::document {::NS ::NS2} -output docs.html -recurse true -pagesplit namespace
Refer to document for various other options.
Ruff! generates documentation using Tcl's runtime system to gather proc and class definitions. Comments in procedure and method bodies are further parsed to extract the documentation.
The structure Ruff! expects is described below. In practice, the structure is simple and intuitive though the description may be a bit long winded. You can simply look at the documentation of the sample namespace instead, and click on the Show source links for each procedure or method there to see the formatting.
An example procedure may look as follows:
proc ruff::sample::character_at {text {pos 0}} { # Get the character from a string. # text - Text string. # pos - Character position. # The command will treat negative values of $pos as offset from # the end of the string. # # Returns the character at index $pos in string $text. set n [string length $text] if {[tcl::mathfunc::abs $pos] >= [string length $text]} { #ruff # An error exception is raised if $pos is not within bounds. error "Index $pos out of bounds." } if {$pos < 0} { return [string index $text end$pos] } else { return [string index $text $pos] } }
You can see the generated documentation for the above at sample::character_at.
The first block of comments within a procedure before the first line of code are always processed by Ruff!. Note preceding blank lines are OK. We will refer to this block as the lead comment block. It is terminated by either a line of code or a blank line.
Any comments appearing after the first line of code are not processed by Ruff! unless immediately preceded by a line beginning with #ruff
which indicates the start of another Ruff! comment block.
The lead comment block begins with a summary that will be used anywhere the document output inserts a procedure summary, for example, a tooltip. The summary is terminated with a blank comment or by the parameter block.
The parameter block is a definition list (see below) and follows its syntactic structure. It only differs from definition lists in that it must directly follow the summary line and receives special treatment in that the default value, if any for the argument, is automatically inserted by Ruff!. Options and switches may also be documented here. The parameter block is terminated in the same fashion as definition blocks.
Any blocks following the parameter block, whether part of the lead block or appearing in a subsequent comment block marked with a leading #ruff
, are processed as follows.
#
character and a single following space if there is one.-
or a *
character followed by at least one space begins a bulleted list item block. A list item may be continued across multiple lines and is terminated by another list item, a blank line or a line with lesser indentation. Note in particular that lines of other types will not terminate a list item unless they have less indentation.-
surrounded by whitespace begins a definition list element. The text before the -
separator is the definition term and the text after is the description. Both the term and description are subject to inline formatting. Definition blocks follow the same rules for termination as bullet lists described above.Returns
is treated as description of the return value. It follows the same rules as normal paragraphs below.See also:
(note the colon) is assumed to begin a reference block consisting of a list of program element names (such as procedures, classes etc.). These are then automatically linked and listed in the See also section of a procedure documentation. The list may continue over multiple lines following normal paragraph rules. Note the program element names can, but need not be, explicitly marked as a program element reference using surrounding square brackets. For example, within a See also:
section, both document
and [document]
will generate a cross-reference link to the documentation for the document
procedure.Note that the block level parsing is similar but not identical to Markdown. Amongst other differences, Ruff! has
Ruff! adds
Documentation for classes primarily concerns documentation of its methods. The format for method documentation is exactly as described above for procedures. Information about class relationships is automatically collected and need not be explicitly provided. Note that unlike for procedures and methods, Tcl does not provide a means to retrieve the body of the class so that comments can be extracted from them. Thus to document information about the class as a whole, you can either include it in the comments for the constructor, which is often a reasonable place for such information, or include it in the general information section as described in the next section.
In addition to procedures and classes within a namespace, there may be a need to document general information such as the sections you are currently reading. For this purpose, Ruff! looks for a variable _ruff_preamble
within each namespace. The indentation of the first line of section content is stripped off from all subsequent lines before processing (This impacts what constitutes a preformatted line). The result is then processed in the same manner as procedure or method bodies except for the following differences:
#
characters followed by at least one space.The documentation generated from the _ruff_preamble
content is placed before the documentation of the commands in classes for that namespace.
Note: Older versions supported the _ruffdoc
variable. Though this will still work, it is deprecated.
Content that should lie outside of any namespace can be passed through the -preamble
option to document. When generating single page output, this is included at the top of the documentation. When generating multipage output this forms the content of the main documentation page.
Once documentation blocks are parsed as above, their content is subject to inline formatting rules using Markdown syntax with some minor extensions. Markdown compatibility is only for inline elements noted below.
` | Text surrounded by backquotes is formatted as inline code . |
* | Text surrounded by single asterisks is emphasized. |
** | Text surrounded by double asterisks is bolded. |
*** | Text surrounded by triple asterisks is bold emphasized. |
[] | Text surrounded by square brackets is treated as a link (more below). |
<> | Text in angle brackets are treated as HTML tags and auto-links as in Markdown. |
$ | Words beginning with $ are treated as variable names and shown as inline code similar to backquotes (non-standard Markdown). |
The default HTML formatter supports other Markdown inline elements but other formatters might not.
Text enclosed in []
is checked whether it references a section heading or a program element name (namespaces, classes, methods, procedures). If so, it is replaced by a link to the section or documentation of that element. If the text is not a fully qualified name, it is treated relative to the namespace or class within whose documentation the link appears. If it is fully qualified, it is displayed relative to the namespace of the link location. For example,
[document]
is displayed as document[::ruff::formatters]
is displayed as formatters if referenced from within a section documenting the ::ruff
namespace.If the text does not match a section heading or program element name, it is treated as a normal Markdown reference but a warning is emitted.
Ruff! is designed to support multiple output formats through pluggable formatters. The command formatters returns a list of supported formatters. Currently formatters for producing HTML and Markdown are implemented.
In addition, the output may be produced in single or multipage format.
The generated documentation may be either in a single output file or spread across multiple files. This is controlled by the -pagesplit
option to the document command. Some formatters may not support this feature.
When generating multipage output, the toplevel generated page contains links to the other pages which contain per-namespace documentation. The preamble (passed as the -preamble
option to the document command) is also placed in this page.
The internal HTML formatter offers (in the author's humble opinion) the best cross-linking and navigation support with a table of contents in addition to cosmetic enhancements such as tooltips and optional hiding/display of source code. It is also the simplest to use as no other external tools are required.
The following is a simple example of generating the documentation for Ruff! itself in a single page format:
ruff::document ::ruff -title "Ruff! reference"
To generate documentation, including private namespaces, in multipage format:
ruff::document ::ruff -recurse true -pagesplit namespace -output full/ruff.html -title "Ruff! internal reference"
The Markdown formatter generates output in generic Markdown syntax. It includes cross-linking but does not include a table of contents, tooltips or source code display. On the other hand, it allows conversion to other formats using external tools.
The following generates Ruff! documentation in Markdown format and then uses pandoc
to convert it to HTML.
ruff::document ::ruff -format markdown -output ruff.md -title "Ruff! reference"
Then from the shell or Windows command line,
pandoc -s -o ruff.html -c ../ruff-md.css --metadata pagetitle="My package" ruff.md
When generating HTML from Markdown, it is generally desirable to specify a CSS style file. The ruff-md.css
file provides some minimal CSS that resembles the output of the internal HTML formatter.
Generates documentation for commands and classes.
namespaces | List of namespaces for which documentation is to be generated. |
args | Options described below. |
-autopunctuate BOOLEAN | If true , the first letter of definition descriptions (including parameter descriptions) is capitalized and a period added at the end if necessary. |
-compact BOOLEAN | If true , documentation is generated in a more compact form, primarily by omitting headers within procedure and method definitions. |
-excludeclasses REGEXP | If specified, any classes whose names match REGEXPR will not be included in the documentation. |
-excludeprocs REGEXP | If specified, any procedures whose names match REGEXPR will not be included in the documentation. |
-format FORMAT | The output format. FORMAT defaults to html . |
-hidenamespace NAMESPACE | By default, documentation generated by Ruff! includes namespace qualifiers in all class and proc names. It is possible to have the generated output leave out the namespace qualifers by adding the -hidenamespace NAMESPACE qualifier to the document generation commands. This will omit NAMESPACE in displayed program element names and provides a more visually pleasing output with less noise. However, it may result in ambiguities in case of names being present in more than one namespace. In particular, some formatters may not cross-link correctly in such cases. |
-include LIST | Specifies which program elements are to be documented. LIST must be a list from one or both amongst classes or procs . Defaults to both. |
-includeprivate BOOLEAN | If true private methods are also included in the generated documentation. Default is false. |
-includesource BOOLEAN | If true, the source code of the procedure is also included. Default value is false. |
-navigation OPTS | OPTS must be a list of elements from amongst left , right , narrow , normal and wide . The first two specify the position of the navigation pane. The last three specify its width. Not supported by all formatters. |
-output PATH | Specifies the path of the output file. If the output is to multiple files, this is the path of the documentation top. Other files will named accordingly by appending the namespace. Existing files are overwritten. By default, the output file is written to the current directory with a name constructed from the first namespace specified. |
-pagesplit SPLIT | If none , a single documentation file is produced. If namespace , a separate file is output for every namespace. |
-preamble TEXT | Any text that should be appear at the beginning outside of any namespace documentation, for example an introduction or overview of a package. TEXT is assumed to be in Ruff! syntax. |
-recurse BOOLEAN | If true, child namespaces are recursively documented. |
-sortnamespaces BOOLEAN | If true (default) the namespaces are sorted in the navigation otherwise they are in the order passed in. |
-title STRING | Specifies the title to use for the page. |
The command generates documentation for one or more namespaces and writes it out to file(s) as per the options shown above. See Documenting procedures, Documenting classes and Documenting namespaces for details of the expected source formats and the generation process.
proc ::ruff::document {namespaces args} { # Generates documentation for commands and classes. # namespaces - list of namespaces for which documentation is to be generated. # args - Options described below. # -autopunctuate BOOLEAN - If `true`, the first letter of definition # descriptions (including parameter descriptions) is capitalized # and a period added at the end if necessary. # -compact BOOLEAN - If `true`, documentation is generated in a more # compact form, primarily by omitting headers within procedure and method # definitions. # -excludeclasses REGEXP - If specified, any classes whose names # match `REGEXPR` will not be included in the documentation. # -excludeprocs REGEXP - If specified, any procedures whose names # match `REGEXPR` will not be included in the documentation. # -format FORMAT - The output format. `FORMAT` defaults to `html`. # -hidenamespace NAMESPACE - By default, documentation generated by Ruff! # includes namespace qualifiers in all class and proc names. It is possible # to have the generated output leave out the namespace qualifers by adding # the `-hidenamespace NAMESPACE` qualifier to the document generation # commands. This will omit `NAMESPACE` in displayed program element names # and provides a more visually pleasing output with less noise. However, # it may result in ambiguities in case of names being present in more than # one namespace. In particular, some formatters may not cross-link correctly # in such cases. # -include LIST - Specifies which program elements are to be documented. # `LIST` must be a list from one or both amongst `classes` or `procs`. # Defaults to both. # -includeprivate BOOLEAN - if true private methods are also included # in the generated documentation. Default is false. # -includesource BOOLEAN - if true, the source code of the # procedure is also included. Default value is false. # -navigation OPTS - `OPTS` must be a list of elements from amongst # `left`, `right`, `narrow`, `normal` and `wide`. The first two specify # the position of the navigation pane. The last three specify its width. # Not supported by all formatters. # -output PATH - Specifies the path of the output file. # If the output is to multiple files, this is the path of the # documentation top. Other files will named accordingly by # appending the namespace. **Existing files are overwritten.** # By default, the output file is written to the current directory # with a name constructed from the first namespace specified. # -pagesplit SPLIT - if `none`, a single documentation file is produced. # If `namespace`, a separate file is output for every namespace. # -preamble TEXT - Any text that should be appear at the beginning # outside of any namespace documentation, for example an introduction # or overview of a package. `TEXT` is assumed to be in Ruff! syntax. # -recurse BOOLEAN - if true, child namespaces are recursively # documented. # -sortnamespaces BOOLEAN - if `true` (default) the namespaces are # sorted in the navigation otherwise they are in the order passed in. # -title STRING - specifies the title to use for the page # # The command generates documentation for one or more namespaces # and writes it out to file(s) as per the options shown above. # See [Documenting procedures], [Documenting classes] and # [Documenting namespaces] for details of the expected source # formats and the generation process. # TBD - not documented because not tested # -stylesheet URLLIST - if specified, the stylesheets passed in URLLIST # are used instead of the built-in styles. Note the built-in YUI is # always included. array set opts { -compact 0 -excludeprocs {} -excludeclasses {} -format html -hidesourcecomments false -include {procs classes} -includeprivate false -includesource false -output "" -preamble "" -recurse false -pagesplit none -sortnamespaces true -title "" } array set opts $args namespace upvar private ProgramOptions ProgramOptions set ProgramOptions(-hidesourcecomments) $opts(-hidesourcecomments) if {$opts(-pagesplit) ni {none namespace}} { error "Option -pagesplit must be \"none\" or \"namespace\" " } set ProgramOptions(-pagesplit) $opts(-pagesplit) # Fully qualify namespaces set namespaces [lmap ns $namespaces { if {![string match ::* $ns]} { set ns "[string trimright [uplevel 1 {namespace current}] ::]::$ns" } if {![namespace exists $ns]} { error "Namespace $ns does not exist." } set ns }] if {[llength $namespaces] == 0} { error "At least one namespace needs to be specified." } set formatter [[load_formatter $opts(-format)] new] array unset private::ns_file_base_cache if {$opts(-output) eq ""} { set opts(-output) [namespace tail [lindex $namespaces 0]] } set private::output_file_base [file root [file tail $opts(-output)]] set private::output_file_ext [file extension $opts(-output)] if {$private::output_file_ext in {{} .}} { set private::output_file_ext .[$formatter extension] } if {$opts(-recurse)} { set namespaces [namespace_tree $namespaces] } # TBD - make sane the use of -modulename lappend args -modulename $opts(-title) if {$opts(-preamble) ne ""} { # TBD - format of -preamble argument passed to formatters # is different so override what was passed in. lappend args -preamble [extract_docstring $opts(-preamble)] } set classprocinfodict [extract_namespaces $namespaces -excludeprocs $opts(-excludeprocs) -excludeclasses $opts(-excludeclasses) -include $opts(-include) -includeprivate $opts(-includeprivate)] set docs [$formatter generate_document $classprocinfodict {*}$args] $formatter destroy set dir [file dirname $opts(-output)] file mkdir $dir foreach {ns doc} $docs { set fn [private::ns_file_base $ns] set fd [open [file join $dir $fn] w] fconfigure $fd -encoding utf-8 if {[catch { puts $fd $doc } msg]} { close $fd error $msg } close $fd } return }
Gets the available output formatters.
The returned values can be passed to document to generate documentation in that format.
Returns a list of available formatters.
proc ::ruff::formatters {} { # Gets the available output formatters. # # The returned values can be passed to [document] to generate # documentation in that format. # # Returns a list of available formatters. return {html markdown} }
Returns the Ruff! version.
Returns the Ruff! version.
proc ::ruff::version {} { # Returns the Ruff! version. variable version return $version }