::ruff

NOTE: this documentation describes an old version of Ruff!. For the current version, see https://ruff.magicsplat.com

Introduction

Ruff! generates reference documentation for Tcl programs using runtime introspection.

Unlike most source code based documentation generators, Ruff! generates documentation using Tcl's runtime system to extract proc, class and method definitions. The code for procedures and methods is parsed to extract documentation from free-form comments. Tcl introspection is used to retrieve information about namespaces, inheritance graphs, default parameter values and so on.

This document contains reference material for Ruff!. For more introductory and tutorial documentation, a user guide is available at http://woof.sourceforge.net/ruff_guide.html. The SourceForge site http://sourceforge.net/projects/woof hosts the Ruff! source tree as part of the Woof! project.

Usage

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. Include the following command to load the package into your script.

    package require ruff

Once loaded, you can use the ::ruff::document_namespaces command to document classes and commands within a namespace. For more flexibility in controlling what is to be documented, use the ::ruff::extract command and pass its results to the ::ruff::document command.

In the simple case, where only the namespace '::NS' and its children are to be documented, the following commands will create the file 'NS.html' using the built-in HTML formatter.

  package require ruff
  ::ruff::document_namespaces html [list ::NS] -output NS.html -recurse true

Other commands in the package are intended to be used if you want to roll your own custom formatting package.

Refer to the source code of the command ::ruff::document_self, which generates documentation for Ruff!, for an example of how the package might be used.

Input format

Ruff! extracts documentation from proc and class definitions and comments within the proc and method bodies. The comments are expected to have some simple structure but no extraneous markup is required.

The lines within the body of a proc or method are first filtered as described in documentation for the ::ruff::distill_body command. These lines are then parsed as described in the ::ruff::parse command to extract different documentation components.

Refer to those commands for the syntax and comment structure expected by Ruff!.

Output formats

Ruff! supports multiple output formats. HTML can be directly generated using the internal formatter which does not require any other external tool.

Alternatively, the output can also be generated in a format that is suitable to be fed to an external formatting program such as robodoc or doctools. The command ::ruff::formatters returns a list of supported formatters. These formatters in turn can produce documents in several different formats such as html, latex and docbook.

It is recommended that for HTML output, the built-in html formatter be used as it has the best support for cross-referencing, automatic link generation and navigation.

Commands

distill_body [::ruff]

ruff, Top

Given a procedure or method body, returns the documentation lines as a list.

distill_body text

Parameters
text text to be processed to collect all documentation lines.
Description

Given a procedure or method body, returns the documentation lines as a list.

The first block of contiguous comment lines preceding the first line of code are treated as documentation lines.

The string #ruff at the beginning of a comment line anywhere in the passed in text is considered the start of a documentation block. All subsequent contiguous comment lines are considered documentation lines.

Note a #ruff on a line by itself will terminate the previous text block. If #ruff is followed by additional text on the same line, it is treated as a continuation of the previous text block.

The leading comment character and a single space (if present) are trimmed from the returned lines.

proc ::ruff::distill_body {text} {

    # Given a procedure or method body,
    # returns the documentation lines as a list.
    # text - text to be processed to collect all documentation lines.
    # The first block of contiguous comment lines preceding the
    # first line of code are treated as documentation lines.

    set lines {}
    set state init;             # init, collecting or searching
    foreach line [split $text \n] {
        set line [string trim $line]; # Get rid of whitespace
        if {$line eq ""} {
            # Blank lines.
            # If in init state, we will stay in init state
            if {$state ne "init"} {
                set state searching
            }
            continue
        }

        if {[string index $line 0] ne "#"} {
            # Not a comment
            set state searching
            continue
        }

        # At this point, the line is a comment line
        if {$state eq "searching"} {
            #ruff
            # The string #ruff at the beginning of a comment line
            # anywhere in the passed in text is considered the start
            # of a documentation block. All subsequent contiguous
            # comment lines are considered documentation lines.
            if {[string match "#ruff*" $line]} {
                set state collecting
                #ruff
                # Note a #ruff on a line by itself will terminate
                # the previous text block.
                set line [string trimright $line]
                if {$line eq "#ruff"} {
                    lappend lines {}
                } else {
                    #ruff If #ruff is followed by additional text
                    # on the same line, it is treated as a continuation
                    # of the previous text block.
                    lappend lines [string range $line 6 end]
                }
            }
        } else {
            # State is init or collecting

            if {$line eq "#"} {
                # Empty comment line
                lappend lines {}
                continue;       # No change in state
            }

            #ruff
            # The leading comment character and a single space (if present)
            # are trimmed from the returned lines.
            if {[string index $line 1] eq " "} {
                lappend lines [string range $line 2 end]
            } else {
                lappend lines [string range $line 1 end]
            }
            set state collecting
            continue
        }
    }

    # Returns a list of lines that comprise the raw documentation.
    return $lines
}

distill_docstring [::ruff]

ruff, Top

Splits a documentation string to return the documentation lines as a list.

distill_docstring text

Parameters
text documentation string to be parsed
Description

Splits a documentation string to return the documentation lines as a list.

Initial blank lines are skipped and multiple empty lines are compressed into one empty line.

The very first non empty line determines the margin. This will be removed from all subsequent lines. Note that this assumes that if tabs are used for indentation, they are used on all lines in consistent fashion.

proc ::ruff::distill_docstring {text} {

    # Splits a documentation string to return the documentation lines
    # as a list.
    # text - documentation string to be parsed


    set lines {}
    set state init
    foreach line [split $text \n] {
        if {[regexp {^\s*$} $line]} {
            #ruff
            # Initial blank lines are skipped and
            # multiple empty lines are compressed into one empty line.
            if {$state eq "collecting"} {
                lappend lines ""
                set state empty
            }
            continue
        }
        #ruff
        # The very first non empty line determines the margin. This will
        # be removed from all subsequent lines. Note that this assumes that
        # if tabs are used for indentation, they are used on all lines
        # in consistent fashion.
        if {$state eq "init"} {
            regexp {^(\s*)\S} $line dontcare prefix
            set prefix_len [string length $prefix]
        }
        set state collecting

        # Remove the prefix if it exists from the line
        if {[string match ${prefix}* $line]} {
            set line [string range $line $prefix_len end]
        }

        lappend lines $line
    }

    # Returns a list of lines.
    return $lines
}

document [::ruff]

ruff, Top

Generates documentation for the specified namespaces using the specified formatter.

document formatter classprocinfodict docstrings args

Parameters
formatter the formatter to be used to produce the documentation
classprocinfodict dictionary containing the metainformation for classes and procs for which documentation is to be generated. This must be in the format returned by the extract command.
docstrings(optional, default ) a flat list of pairs consisting of a heading and corresponding content. These are inserted into the document before the actual class and command descriptions after being processed by extract_docstring.
argsAdditional options.
Return value

Returns the documentation string as generated by the specified formatter.

Description

Generates documentation for the specified namespaces using the specified formatter.

All additional arguments are passed through to the specified formatter's generate_document command.

proc ::ruff::document {formatter classprocinfodict {docstrings {}} args} {

    # Generates documentation for the specified namespaces using the
    # specified formatter.
    # formatter - the formatter to be used to produce the documentation
    # classprocinfodict - dictionary containing the metainformation for
    #  classes and procs for which documentation is to be generated. This
    #  must be in the format returned by the extract command.
    # docstrings - a flat list of pairs consisting of a heading and
    #    corresponding content. These are inserted into the document
    #    before the actual class and command descriptions after being
    #    processed by extract_docstring.
    #
    # All additional arguments are passed through to the specified
    # formatter's generate_document command.
    #
    # Returns the documentation string as generated by the specified formatter.

    _load_formatter $formatter

    set preamble [dict create]
    foreach {sec docstring} $docstrings {
        # Treate the preamble as a "toplevel" preamble
        dict lappend preamble "::" $sec [extract_docstring $docstring]
    }

    return [eval [list [namespace current]::formatter::${formatter}::generate_document $classprocinfodict -preamble $preamble] $args]
}

document_namespace [::ruff]

ruff, Top

Obsolete, use document_namespaces instead.

document_namespace formatter ns args

Parameters
formatter
ns
argsAdditional options.
Description

Obsolete, use document_namespaces instead.

proc ::ruff::document_namespace {formatter ns args} {

    # Obsolete, use document_namespaces instead.
    return [eval [list document_namespaces $formatter [list $ns] -title $ns] $args]
}

document_namespaces [::ruff]

ruff, Top

Generates documentation for the specified namespaces using the specified formatter.

document_namespaces formatter namespaces args

Parameters
formatter the formatter to be used to produce the documentation
namespaces list of namespaces for which documentation is to be generated
argsAdditional options.
-append BOOLEAN if true, the generated document is appended to the specified file instead of overwriting it.
-includeclasses BOOLEAN if true (default), class information is collected
-includeprivate BOOLEAN if true private methods are also included in the generated documentation. Default is false.
-includeprocs BOOLEAN if true (default), proc information is collected
-includesource BOOLEAN if true, the source code of the procedure is also included. Default value is false.
-output PATH if specified, the generated document is written to the specified file which will overwritten if it already exists.
-recurse BOOLEAN if true, child namespaces are recursively documented.
-title STRING specifies the title to use for the page
Return value

Returns the documentation string if the -output option is not specified, otherwise returns an empty string after writing the documentation to the specified file.

Description

Generates documentation for the specified namespaces using the specified formatter.

Any additional arguments are passed through to the document command.

proc ::ruff::document_namespaces {formatter namespaces args} {

    # Generates documentation for the specified namespaces using the
    # specified formatter.
    # formatter - the formatter to be used to produce the documentation
    # namespaces - list of namespaces for which documentation is to be generated
    # -includeclasses BOOLEAN - if true (default), class information
    #     is collected
    # -includeprocs BOOLEAN - if true (default), proc information is
    #     collected
    # -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.
    # -output PATH - if specified, the generated document is written
    #  to the specified file which will overwritten if it already exists.
    # -append BOOLEAN - if true, the generated document is appended
    #  to the specified file instead of overwriting it.
    # -title STRING - specifies the title to use for the page
    # -recurse BOOLEAN - if true, child namespaces are recursively
    #  documented.
    #
    # Any additional arguments are passed through to the document command.
    #
    # Returns the documentation string if the -output option is not
    # specified, otherwise returns an empty string after writing the
    # documentation to the specified file.

    array set opts {
        -includeclasses true
        -includeprocs true
        -includeprivate false
        -includesource false
        -output ""
        -append false
        -title ""
        -recurse false
    }
    array set opts $args

    if {$opts(-recurse)} {
        set namespaces [_namespace_tree $namespaces]
    }

    set preamble [dict create]
    foreach ns $namespaces {
        if {[info exists ${ns}::_ruffdoc]} {
            foreach {section docstring} [set ${ns}::_ruffdoc] {
                dict lappend preamble $ns $section [extract_docstring $docstring]
            }
        }
    }

    set classprocinfodict [extract_namespaces $namespaces  -includeclasses $opts(-includeclasses)  -includeprocs $opts(-includeprocs)  -includeprivate $opts(-includeprivate)]

    _load_formatter $formatter
    set doc  [eval  [list formatter::${formatter}::generate_document  $classprocinfodict  -preamble $preamble  -modulename $opts(-title)  ]  $args]
    if {$opts(-output) ne ""} {
        if {$opts(-append)} {
            set fd [open $opts(-output) a]
        } else {
            set fd [open $opts(-output) w]
        }
        if {[catch {
            puts $fd $doc
        } msg]} {
            close $fd
            error $msg
        }
        close $fd
        return
    } else {
        return $doc
    }
}

document_self [::ruff]

ruff, Top

Generates documentation for Ruff!

document_self formatter output_dir args

Parameters
formatter the formatter to use
output_dir the output directory where files will be stored. Note files in this directory with the same name as the output files will be overwritten!
argsAdditional options.
-formatterpath PATH path to the formatter. If unspecified, the the input files for the formatter are generated but the formatter is not run. This option is ignore for the built-in HTML formatter.
-includesource BOOLEAN if true, include source code in documentation.
Description

Generates documentation for Ruff!

proc ::ruff::document_self {formatter output_dir args} {

    # Generates documentation for Ruff!
    # formatter - the formatter to use
    # output_dir - the output directory where files will be stored. Note
    #  files in this directory with the same name as the output files
    #  will be overwritten!
    # -formatterpath PATH - path to the formatter. If unspecified, the
    #  the input files for the formatter are generated but the formatter
    #  is not run. This option is ignore for the built-in HTML formatter.
    # -includesource BOOLEAN - if true, include source code in documentation.

    variable names

    array set opts {
        -formatterpath ""
        -includesource FALSE
    }
    array set opts $args

    _load_all_formatters;       # So all will be documented!

    set modules [dict create]
    # Enumerate and set the descriptive text for all the modules.
    # TBD - use these settings to generate preambles for the modules
    dict set modules ::ruff  [dict create description "$names(display) main module"]
    dict set modules ::ruff::formatter  [dict create description "$names(display) formatters"]
    dict set modules ::ruff::formatter::doctools  [dict create description "$names(display) formatter for Doctools"]
    dict set modules ::ruff::formatter::naturaldocs  [dict create description "$names(display) formatter for NaturalDocs"]
    dict set modules ::ruff::formatter::robodoc  [dict create description "$names(display) formatter for ROBODoc"]
    dict set modules ::ruff::formatter::html  [dict create description "$names(display) formatter for HTML"]
    dict set modules ::ruff::app  [dict create description "$names(display) application callbacks"]

    file mkdir $output_dir
    if {$formatter ne "html"} {
        # For external formatters, we need input and output directories
        set outdir [file join $output_dir output]
        set indir  [file join $output_dir Ruff]
        file mkdir $indir;  # Input for formatter, output for us!
        file mkdir $outdir
    }
    switch -exact -- $formatter {
        naturaldocs {
            set projdir [file join $output_dir proj]
            file mkdir $projdir
            dict for {ns nsdata} $modules {
                set fn [string map {:: _} [string trimleft $ns :]]
                set fn "[file join $indir $fn].tcl"
                document_namespace naturaldocs $ns -output $fn -hidenamespace $ns
            }
            # We want to change the stylesheet for NaturalDocs
            set fd [open [file join $projdir ruff.css] w]
            puts $fd "p { text-indent: 0; margin-bottom: 1em; }"
            puts $fd "blockquote {margin-left: 5em;}"
            close $fd
            if {$opts(-formatterpath) ne ""} {
                if {[catch {
                    eval exec $opts(-formatterpath) [list --input $indir  --output HTML $outdir  --project $projdir  --rebuild  --style Default ruff  ]
                } msg]} {
                    app::log_error "Error executing NaturalDocs using path '$opts(-formatterpath)': $msg"
                }
            }
        }
        doctools {
            dict for {ns nsdata} $modules {
                set fn [string map {:: _} [string trimleft $ns :]]
                set fn "[file join $indir $fn].man"
                document_namespace doctools $ns -output $fn -hidenamespace $ns  -name $ns  -keywords [list "documentation generation"]  -modulename $ns  -titledesc [dict get $nsdata description]  -version $::ruff::version
            }
            if {$opts(-formatterpath) ne ""} {
                if {[catch {
                    eval exec $opts(-formatterpath) [list -o $outdir html $indir]
                } msg]} {
                    app::log_error "Error executing doctools using path '$opts(-formatterpath)': $msg"
                }
            }
        }
        html {
            # Note here we use $output_dir since will directly produce HTML
            # and not intermediate files
            document_namespaces html ::ruff -recurse true  -output [file join $output_dir ruff.html]  -titledesc "Ruff! - Runtime Formatting Function Reference (V$::ruff::version)"  -copyright "[clock format [clock seconds] -format %Y] Ashok P. Nadkarni"  -includesource $opts(-includesource)
        }
        default {
            # The formatter may exist but we do not support it for
            # out documentation.
            error "Formatter '$formatter' not implemented for generating Ruff! documentation."
        }
    }
    return
}

extract [::ruff]

ruff, Top

Extracts metainformation for procs and classes

extract pattern args

Parameters
pattern glob-style pattern to match against procedure and class names
argsAdditional options.
-includeclasses BOOLEAN if true (default), class information is collected
-includeimports BOOLEAN if true commands imported from other namespaces are also included. Default is false.
-includeprivate BOOLEAN if true private methods are also included. Default is false.
-includeprocs if true (default), proc information is collected
Return value

Returns a dictionary with keys 'classes' and 'procs'

Description

Extracts metainformation for procs and classes

The value of the classes key in the returned dictionary is a dictionary whose keys are class names and whose corresponding values are in the format returned by extract_ooclass. Similarly, the procs key contains a dictionary whose keys are proc names and whose corresponding values are in the format as returned by extract_proc.

Note that only the program elements in the same namespace as the namespace of $pattern are returned.

proc ::ruff::extract {pattern args} {

    # Extracts metainformation for procs and classes
    #
    # pattern - glob-style pattern to match against procedure and class names
    # -includeclasses BOOLEAN - if true (default), class information
    #     is collected
    # -includeprocs - if true (default), proc information is
    #     collected
    # -includeprivate BOOLEAN - if true private methods are also included.
    #  Default is false.
    #
    # The value of the classes key in the returned dictionary is
    # a dictionary whose keys are class names and whose corresponding values
    # are in the format returned by extract_ooclass.
    # Similarly, the procs key contains a dictionary whose keys
    # are proc names and whose corresponding values are in the format
    # as returned by extract_proc.
    #
    # Note that only the program elements in the same namespace as
    # the namespace of $pattern are returned.
    #
    # Returns a dictionary with keys 'classes' and 'procs'

    array set opts {
        -includeclasses true
        -includeprocs true
        -includeprivate false
        -includeimports false
    }
    array set opts $args

    set classes [dict create]
    if {$opts(-includeclasses)} {
        # We do a catch in case this Tcl version does not support objects
        set class_names {}
        catch {set class_names [info class instances ::oo::class $pattern]}
        foreach class_name $class_names {
            # This covers child namespaces as well which we do not want
            # so filter those out. The differing pattern interpretations in
            # Tcl commands 'info class instances' and 'info procs'
            # necessitates this.
            if {[namespace qualifiers $class_name] ne [namespace qualifiers $pattern]} {
                # Class is in not in desired namespace
                # TBD - do we need to do -includeimports processing here?
                continue
            }
            # Names beginning with _ are treated as private
            if {(!$opts(-includeprivate)) &&
                [string index [namespace tail $class_name] 0] eq "_"} {
                continue
            }

            if {[catch {
                set class_info [extract_ooclass $class_name -includeprivate $opts(-includeprivate)]
            } msg]} {
                app::log_error "Could not document class $class_name"
            } else {
                dict set classes $class_name $class_info
            }
        }
    }

    set procs [dict create]
    if {$opts(-includeprocs)} {
        foreach proc_name [info procs $pattern] {
            #ruff
            # -includeimports BOOLEAN - if true commands imported from other
            #  namespaces are also included. Default is false.
            if {(! $opts(-includeimports)) &&
                [namespace origin $proc_name] ne $proc_name} {
                continue;       # Do not want to include imported commands
            }
            # Names beginning with _ are treated as private
            if {(!$opts(-includeprivate)) &&
                [string index [namespace tail $proc_name] 0] eq "_"} {
                continue
            }

            if {[catch {
                set proc_info [extract_proc $proc_name]
            } msg]} {
                app::log_error "Could not document proc $proc_name"
            } else {
                dict set procs $proc_name $proc_info
            }
        }
    }

    return [dict create classes $classes procs $procs]
}

extract_docstring [::ruff]

ruff, Top

Parses a documentation string to return a structured text representation.

extract_docstring text

Parameters
text documentation string to be parsed
Description

Parses a documentation string to return a structured text representation.

The command extracts structured text from the given string as described in the documentation for the distill_docstring and parse commands. The result is further processed to return a list of type and value elements described below:

deflist the corresponding value is another list containing definition item name and its value as a string.
bulletlist the corresponding value is a list of strings each being one list item.
paragraph the corresponding value is a string comprising the paragraph.
preformatted the corresponding value is a string comprising preformatted text.
proc ::ruff::extract_docstring {text} {

    # Parses a documentation string to return a structured text representation.
    # text - documentation string to be parsed
    #
    # The command extracts structured text from the given string
    # as described in the documentation for the distill_docstring
    # and parse commands. The result is further processed to
    # return a list of type and value elements described below:
    # deflist - the corresponding value is another list containing
    #   definition item name and its value as a string.
    # bulletlist - the corresponding value is a list of strings
    #   each being one list item.
    # paragraph - the corresponding value is a string comprising
    #   the paragraph.
    # preformatted - the corresponding value is a string comprising
    #   preformatted text.


    set paragraphs {}

    # Loop and construct the documentation
    foreach {type content} [parse [distill_docstring $text]] {
        switch -exact -- $type {
            deflist {
                # Each named list is a list of pairs
                set deflist {}
                foreach {name desc} $content {
                    lappend deflist $name [join $desc " "]
                }
                lappend paragraphs deflist $deflist
            }
            bulletlist {
                # Bullet lists are lumped with paragraphs
                set bulletlist {}
                foreach desc $content {
                    lappend bulletlist [join $desc " "]
                }
                lappend paragraphs bulletlist $bulletlist
            }
            summary {
                # Do nothing. Summaries are same as the first
                # paragraph. For docstrings, we do not show
                # them separately like we do for procs
            }
            paragraph {
                lappend paragraphs paragraph [join $content " "]
            }
            preformatted {
                lappend paragraphs preformatted [join $content \n]
            }
            default {
                error "Text fragments of type '$type' not supported in docstrings"
            }
        }
    }
    return $paragraphs
}

extract_namespace [::ruff]

ruff, Top

Extracts metainformation for procs and objects in a namespace

extract_namespace ns args

Parameters
ns namespace to examine
argsAdditional options.
Return value

Returns a dictionary with keys 'classes' and 'procs'. See ruff::extract for details.

Description

Extracts metainformation for procs and objects in a namespace

Any additional options are passed on to the extract command.

proc ::ruff::extract_namespace {ns args} {

    # Extracts metainformation for procs and objects in a namespace
    # ns - namespace to examine
    #
    # Any additional options are passed on to the extract command.
    #
    # Returns a dictionary with keys 'classes' and 'procs'. See ruff::extract
    # for details.

    return [eval [list extract ${ns}::*] $args]
}

extract_namespaces [::ruff]

ruff, Top

Extracts metainformation for procs and objects in one or more namespace

extract_namespaces namespaces args

Parameters
namespaces list of namespace to examine
argsAdditional options.
Return value

Returns a dictionary with keys 'classes' and 'procs'. See ruff::extract for details.

Description

Extracts metainformation for procs and objects in one or more namespace

Any additional options are passed on to the extract_namespace command.

proc ::ruff::extract_namespaces {namespaces args} {

    # Extracts metainformation for procs and objects in one or more namespace
    # namespaces - list of namespace to examine
    #
    # Any additional options are passed on to the extract_namespace command.
    #
    # Returns a dictionary with keys 'classes' and 'procs'. See ruff::extract
    # for details.

    set procs [dict create]
    set classes [dict create]
    foreach ns $namespaces {
        set nscontent [eval [list extract ${ns}::*] $args]
        set procs   [dict merge $procs [dict get $nscontent procs]]
        set classes [dict merge $classes [dict get $nscontent classes]]
    }
    return [dict create procs $procs classes $classes]
}

extract_ooclass [::ruff]

ruff, Top

Extracts metainformation about the specified class

extract_ooclass classname args

Parameters
classname name of the class to be documented
argsAdditional options.
-includeprivate BOOLEAN if true private methods are also included in the metainformation. Default is false.
Description

Extracts metainformation about the specified class

The metainformation. returned is in the form of a dictionary with the following keys:

name name of the class
methods a list of method definitions for this class in the form returned by extract_ooclass_method with the additional key 'visibility' which may have values 'public' or 'private'.
external_methods a list of names of methods that are either inherited or mixed in
filters a list of filters defined by the class
forwards a list of forwarded methods, each element in the list being a dictionary with keys 'name' and 'forward' corresponding to the forwarded method name and the forwarding command.
mixins a list of names of classes mixed into the class
superclasses a list of names of classes which are direct superclasses of the class
subclasses a list of classes which are direct subclasses of this class
constructor method definition for the constructor in the format returned by extract_ooclass_method
destructor method definition for the destructor returned by extract_ooclass_method

Each method definition is in the format returned by the extract_ooclass_method command with an additional keys:

visibility indicates whether the method is 'public' or 'private'
proc ::ruff::extract_ooclass {classname args} {

    # Extracts metainformation about the specified class
    # classname - name of the class to be documented
    # -includeprivate BOOLEAN - if true private methods are also included
    #  in the metainformation. Default is false.
    #
    # The metainformation. returned is in the form of a dictionary with
    # the following keys:
    # name - name of the class
    # methods - a list of method definitions for this class in the form
    #  returned by extract_ooclass_method with the additional key
    #  'visibility' which may have values 'public' or 'private'.
    # external_methods - a list of names of methods that are
    #  either inherited or mixed in
    # filters - a list of filters defined by the class
    # forwards - a list of forwarded methods, each element in the
    #  list being a dictionary with keys 'name' and 'forward'
    #  corresponding to the forwarded method name and the forwarding command.
    # mixins - a list of names of classes mixed into the class
    # superclasses - a list of names of classes which are direct
    #   superclasses of the class
    # subclasses - a list of classes which are direct subclasses of this class
    # constructor - method definition for the constructor in the format
    #   returned by extract_ooclass_method
    # destructor - method definition for the destructor
    #   returned by extract_ooclass_method
    #
    # Each method definition is in the format returned by the
    # extract_ooclass_method command with an additional keys:
    # visibility - indicates whether the method is 'public' or 'private'

    array set opts {-includeprivate false}
    array set opts $args

    set result [dict create methods {} external_methods {}  filters {} forwards {}  mixins {} superclasses {} subclasses {}  name $classname  ]

    if {$opts(-includeprivate)} {
        set all_local_methods [info class methods $classname -private]
        set all_methods [info class methods $classname -all -private]
    } else {
        set all_local_methods [info class methods $classname]
        set all_methods [info class methods $classname -all]
    }
    set public_methods [info class methods $classname -all]
    set external_methods {}
    foreach name $all_methods {
        set implementing_class [locate_ooclass_method $classname $name]
        if {[lsearch -exact $all_local_methods $name] < 0} {
            # Skip the destroy method which is standard and
            # appears in all classes.
            if {$implementing_class ne "::oo::object" ||
                $name ne "destroy"} {
                lappend external_methods [list $name $implementing_class]
            }
            continue
        }

        # Even if a local method, it may be hidden by a mixin
        if {$implementing_class ne $classname} {
            # TBD - should we make a note in the documentation somewhere ?
            app::log_error "Method $name in class $classname is hidden by class $implementing_class."
        }

        if {[lsearch -exact $public_methods $name] >= 0} {
            set visibility public
        } else {
            set visibility private
        }

        if {! [catch {
            set method_info [extract_ooclass_method $classname $name]
        } msg]} {
            dict set method_info visibility $visibility
            #dict set method_info name $name
            dict lappend result methods $method_info
        } else {
            # Error, may be it is a forwarded method
            if {! [catch {
                set forward [info class forward $classname $name]
            }]} {
                dict lappend result forwards [dict create name $name forward $forward]
            } else {
                ruff::app::log_error "Could not introspect method $name in class $classname"
            }
        }
    }

    foreach name {constructor destructor} {
        if {[info class $name $classname] ne ""} {
            # Class has non-empty constructor or destructor
            dict set result $name [extract_ooclass_method $classname $name]
        }
    }

    dict set result name $classname;   # TBD - should we fully qualify this?
    dict set result external_methods $external_methods
    dict set result filters [info class filters $classname]
    dict set result mixins [info class mixins $classname]
    dict set result subclasses [info class subclasses $classname]
    # We do not want to list ::oo::object which is a superclass
    # of all classes.
    set classes {}
    foreach class [info class superclasses $classname] {
        if {$class ne "::oo::object"} {
            lappend classes $class
        }
    }
    dict set result superclasses $classes

    return $result
}

extract_ooclass_method [::ruff]

ruff, Top

Extracts metainformation for the method in oo:: class

extract_ooclass_method class method

Parameters
class name of the class
method
Return value

Returns a dictionary containing documentation related to the command.

Description

Extracts metainformation for the method in oo:: class

The command retrieves metainformation about a Tcl class method. See the command extract_proc_or_method for details.

proc ::ruff::extract_ooclass_method {class method} {


    # Extracts metainformation for the method in oo:: class
    # class - name of the class
    #
    # The command retrieves metainformation about
    # a Tcl class method. See the command extract_proc_or_method
    # for details.
    #
    # Returns a dictionary containing documentation related to the command.
    #


    switch -exact -- $method {
        constructor {
            foreach {params body} [info class constructor $class] break
        }
        destructor  {
            set body [lindex [info class destructor $class] 0]
            set params {}
        }
        default {
            foreach {params body} [info class definition $class $method] break
        }
    }


    set param_names {}
    set param_defaults {}
    foreach param $params {
        lappend param_names [lindex $param 0]
        if {[llength $param] > 1} {
            lappend param_defaults [lindex $param 0] [lindex $param 1]
        }
    }

    return [extract_proc_or_method method $method $param_names $param_defaults $body $class]
}

extract_proc [::ruff]

ruff, Top

Extracts meta information from a Tcl procedure.

extract_proc procname

Parameters
procname name of the procedure
Return value

Returns a dictionary containing metainformation for the command.

Description

Extracts meta information from a Tcl procedure.

The command retrieves metainformation about a Tcl procedure. See the command extract_proc_or_method for details.

proc ::ruff::extract_proc {procname} {


    # Extracts meta information from a Tcl procedure.
    # procname - name of the procedure
    #
    # The command retrieves metainformation about
    # a Tcl procedure. See the command extract_proc_or_method
    # for details.
    #
    # Returns a dictionary containing metainformation for the command.
    #

    set param_names [info args $procname]
    set param_defaults {}
    foreach name $param_names {
        if {[info default $procname $name val]} {
            lappend param_defaults $name $val
        }
    }
    return [extract_proc_or_method proc $procname [info args $procname] $param_defaults [info body $procname]]
}

extract_proc_or_method [::ruff]

ruff, Top

Helper procedure used by extract_proc and extract_ooclass_method to construct metainformation for a method or proc.

extract_proc_or_method proctype procname param_names param_defaults body class

Parameters
proctype should be either 'proc' or 'method'
procname name of the proc or method
param_names list of parameter names in order
param_defaults list of parameter name and default values
body the body of the proc or method
class(optional, default ) the name of the class to which the method belongs. Not used for proc types.
Description

Helper procedure used by extract_proc and extract_ooclass_method to construct metainformation for a method or proc.

The command parses the $body parameter as described by the distill_body and parse commands and then constructs the metainformation for the proc or method using this along with the other passed arguments. The metainformation is returned as a dictionary with the following keys:

name name of the proc or method
parameters a list of parameters. Each element of the list is a pair or a triple, consisting of the parameter name, the description and possibly the default value if there is one.
options a list of options. Each element is a pair consisting of the name and its description.
description a list of paragraphs describing the command. The list contains preformatted, paragraph, bulletlist and deflist elements as described for the extract_docstring command.
return a description of the return value of the command
summary a copy of the first paragraph if it was present before the parameter descriptions.
source the source code of the command
proc ::ruff::extract_proc_or_method {proctype procname param_names param_defaults body {class {}}} {

    # Helper procedure used by extract_proc and extract_ooclass_method to
    # construct metainformation for a method or proc.
    #  proctype - should be either 'proc' or 'method'
    #  procname - name of the proc or method
    #  param_names - list of parameter names in order
    #  param_defaults - list of parameter name and default values
    #  body - the body of the proc or method
    #  class - the name of the class to which the method belongs. Not used
    #   for proc types.
    #
    # The command parses the $body parameter as described by the distill_body
    # and parse commands and then constructs the metainformation for
    # the proc or method using this along with the other passed arguments.
    # The metainformation is returned as a dictionary with the following keys:
    #   name - name of the proc or method
    #   parameters - a list of parameters. Each element of the
    #     list is a pair or a triple, consisting of the parameter name,
    #     the description and possibly the default value if there is one.
    #   options - a list of options. Each element is a pair consisting
    #     of the name and its description.
    #   description - a list of paragraphs describing the command. The
    #     list contains preformatted, paragraph, bulletlist and deflist
    #     elements as described for the extract_docstring command.
    #   return - a description of the return value of the command
    #   summary - a copy of the first paragraph if it was present
    #     before the parameter descriptions.
    #   source - the source code of the command
    #

    array set param_default $param_defaults
    array set params {}
    array set options {}
    set paragraphs {}

    # Loop and construct the documentation
    foreach {type content} [parse [distill_body $body]] {
        switch -exact -- $type {
            parameter {
                # For each parameter, check if it is a
                # parameter in the proc/method definition
                foreach {name desc} $content {
                    if {[lsearch -exact $param_names $name] >= 0} {
                        set params($name) [join $desc " "]
                    } else {
                        #TBD - how to handle this? For now, assume it's
                        #a parameter as well
                        app::log_error "Parameter '$name' not listed in arguments for '$procname'"
                        set params($name) [join $desc " "]
                    }
                }
            }
            summary -
            return {
                set doc($type) [join $content " "]
            }
            deflist {
                # Named lists are lumped with paragraphs
                # Each named list is a list of pairs
                set deflist {}
                foreach {name desc} $content {
                    lappend deflist $name [join $desc " "]
                }
                lappend paragraphs deflist $deflist
            }
            bulletlist {
                # Bullet lists are lumped with paragraphs
                set bulletlist {}
                foreach desc $content {
                    lappend bulletlist [join $desc " "]
                }
                lappend paragraphs bulletlist $bulletlist
            }
            option {
                foreach {name desc} $content {
                    if {[lsearch -exact $param_names "args"] < 0} {
                        app::log_error "Documentation for '$procname' contains option '$name' but the procedure definition does not have an 'args' parameter"
                    }
                    set options($name) [join $desc " "]
                }
            }
            paragraph {
                lappend paragraphs paragraph [join $content " "]
            }
            preformatted {
                lappend paragraphs preformatted [join $content \n]
            }
            default {
                error "Unknown text fragment type '$type'."
            }
        }
    }

    set doc(name)        $procname
    set doc(class)       $class
    set doc(description) $paragraphs
    set doc(proctype)    $proctype

    # Construct parameter descriptions. Note those not listed in the
    # actual proc definition are left out even if they are in the params
    # table
    set doc(parameters) {}
    foreach name $param_names {
        if {[info exists params($name)]} {
            set paramdata [dict create name $name description $params($name) type parameter]
        } else {
            set paramdata [dict create name $name type parameter]
        }

        # Check if there is a default
        if {[info exists param_default($name)]} {
            dict set paramdata default $param_default($name)
        }

        lappend doc(parameters) $paramdata
    }

    # Add the options into the parameter table
    foreach name [lsort [array names options]] {
        lappend doc(parameters) [dict create name $name description $options($name) type option]
    }

    set source "$proctype $procname "
    set param_list {}
    foreach name $param_names {
        if {[info exists param_default($name)]} {
            lappend param_list [list $name $param_default($name)]
        } else {
            lappend param_list $name
        }
    }


    append source "{$param_list} {\n"
    # We need to reformat the body. If nested inside a namespace eval
    # for example, the body will be indented too much. So we undent the
    # least indented line to 0 spaces and then add 4 spaces for each line.
    append source [::textutil::adjust::indent [::textutil::adjust::undent $body] "    "]
    append source "\n}"
    set doc(source) $source

    return [eval dict create [array get doc]]
}

formatters [::ruff]

ruff, Top

Get the list of supported formatters.

formatters

Return value

Returns a list of available formatters.

Description

Get the list of supported formatters.

Ruff! can produce documentation in several formats each of which is produced by a specific formatter. This command returns the list of such formatters that can be used with commands like document.

proc ::ruff::formatters {} {

    # Get the list of supported formatters.
    #
    # Ruff! can produce documentation in several formats each of which
    # is produced by a specific formatter. This command returns the list
    # of such formatters that can be used with commands like
    # document.
    #
    # Returns a list of available formatters.
    variable ruff_dir
    set formatters {}
    set suffix "_formatter.tcl"
    foreach file [glob [file join $ruff_dir *$suffix]] {
        lappend formatters [string range [file tail $file] 0 end-[string length $suffix]]
    }
    return $formatters
}

get_ooclass_method_path [::ruff]

ruff, Top

Calculates the class search order for a method of the specified class

get_ooclass_method_path class_name method_name

Parameters
class_name name of the class to which the method belongs
method_name method name being searched for
Return value

Returns an ordered list containing the classes that are searched to locate a method for the specified class.

Description

Calculates the class search order for a method of the specified class

A method implementation may be provided by the class itself, a mixin or a superclass. This command calculates the order in which these are searched to locate the method. The primary purpose is to find exactly which class actually implements a method exposed by the class.

If a class occurs multiple times due to inheritance or mixins, the LAST occurence of the class is what determines the priority of that class in method selection. Therefore the returned search path may contain repeated elements.

Note that this routine only applies to a class and cannot be used with individual objects which may have their own mix-ins.

Search algorithm:

  • Filters are ignored. They may be invoked but are not considered implementation of the method itself.
  • The mixins of a class are searched even before the class itself as are the superclasses of the mixins.
  • next in the search path is the class itself
  • Last in the search order are the superclasses (in recursive fashion)
proc ::ruff::get_ooclass_method_path {class_name method_name} {

    # Calculates the class search order for a method of the specified class
    # class_name - name of the class to which the method belongs
    # method_name - method name being searched for
    #
    # A method implementation may be provided by the class itself,
    # a mixin or a superclass.
    # This command calculates the order in which these are searched
    # to locate the method. The primary purpose is to find exactly
    # which class actually implements a method exposed by the class.
    #
    # If a class occurs multiple times due to inheritance or
    # mixins, the LAST occurence of the class is what determines
    # the priority of that class in method selection. Therefore
    # the returned search path may contain repeated elements.
    #
    # Note that this routine only applies to a class and cannot be
    # used with individual objects which may have their own mix-ins.


    # TBD - do we need to distinguish private/public methods

    set method_path {}
    #ruff
    # Search algorithm:
    #  - Filters are ignored. They may be invoked but are not considered
    #    implementation of the method itself.
    #  - The mixins of a class are searched even before the class itself
    #    as are the superclasses of the mixins.
    foreach mixin [info class mixins $class_name] {
        # We first need to check if the method name is in the public interface
        # for this class. This step is NOT redundant since a derived
        # class may unexport a method from an inherited class in which
        # case we should not have the inherited classes in the path
        # either.
        if {[lsearch -exact [info class methods $mixin -all] $method_name] < 0} {
            continue
        }

        set method_path [concat $method_path [get_ooclass_method_path $mixin $method_name]]
    }

    #ruff - next in the search path is the class itself
    if {[lsearch -exact [info class methods $class_name] $method_name] >= 0} {
        lappend method_path $class_name
    }

    #ruff - Last in the search order are the superclasses (in recursive fashion)
    foreach super [info class superclasses $class_name] {
        # See comment in mixin code above.
        if {[lsearch -exact [info class methods $super -all] $method_name] < 0} {
            continue
        }
        set method_path [concat $method_path [get_ooclass_method_path $super $method_name]]
    }


    #ruff
    # Returns an ordered list containing the classes that are searched
    # to locate a method for the specified class.
    return $method_path
}

locate_ooclass_method [::ruff]

ruff, Top

Locates the classe that implement the specified method of a class

locate_ooclass_method class_name method_name

Parameters
class_name name of the class to which the method belongs
method_name method name being searched for
Return value

Returns the name of the implementing class or an empty string if the method is not implemented.

Description

Locates the classe that implement the specified method of a class

The matching class may implement the method itself or through one of its own mix-ins or superclasses.

proc ::ruff::locate_ooclass_method {class_name method_name} {

    # Locates the classe that implement the specified method of a class
    # class_name - name of the class to which the method belongs
    # method_name - method name being searched for
    #
    # The matching class may implement the method itself or through
    # one of its own mix-ins or superclasses.
    #
    # Returns the name of the implementing class or an empty string
    # if the method is not implemented.

    # Note: we CANNOT just calculate a canonical search path for a
    # given class and then search along that for a class that
    # implements a method. The search path itself will depend on the
    # specific method being searched for due to the fact that a
    # superclass may not appear in a particular search path if a
    # derived class hides a method (this is just one case, there may
    # be others). Luckily, get_ooclass_method_path does exactly this.


    set class_path [get_ooclass_method_path $class_name $method_name]

    if {[llength $class_path] == 0} {
        return "";              # Method not found
    }

    # Now we cannot just pick the first element in the path. We have
    # to find the *last* occurence of each class - that will decide
    # the priority order
    set order [dict create]
    set pos 0
    foreach path_elem $class_path {
        dict set order $path_elem $pos
        incr pos
    }

    return [lindex $class_path [lindex [lsort -integer [dict values $order]] 0] 0]
}

parse [::ruff]

ruff, Top

Creates a parse structure given a list of lines that are assumed to be documentation for a programming structure

parse lines

Parameters
lines a list of lines comprising the documentation
Description

Creates a parse structure given a list of lines that are assumed to be documentation for a programming structure

Empty lines or lines with only whitespace terminate the preceding text block (such as a paragraph or a list).

A bulleted list item starts with a '-' or '*' character. A list item may be continued across multiple lines by indending succeeding lines belonging to the same list item. Note an indented line will terminate the previous list item if it itself looks like a new list item. A bulleted list is returned as a list containing the list items, each of which is a list of lines.

A definition list or parameter list begins with a word followed by whitespace, a '-' character, whitespace and descriptive text. Whether it is treated as a parameter list or a definition list depends on whether it occurs in the comment block. If it occurs at the beginning or just after the summary line, it is treated as a parameter list. In all other cases, it is treated as a definition list. Like a bulleted list, each list item may be continued on succeeding lines by indenting them. Definition and parameter lists are returned as flat list of alternating list item name and list item value pairs. The list item value is itself a list of lines.

An option list is similar to a parameter list except that the first word on the line begins with a '-' character and is possibly followed by more words before the '-' character that separates the descriptive text. The '-' separator must be surrounded by whitespace. The value returned for an option list follows the same structure as for parameter or definition list items. Any line in any documentation block that matches this is always added to the option list, irrespective of where it occurs. This means option descriptions can be mingled with other documentation fragments and will show up in the options section.

Any paragraph that begins with the word 'Returns' is treated as a description of the return value irrespective of where it occurs. It is returned as a list of lines.

Lines beginning with spaces are treated as preformatted text unless they are part of a list item. Preformatted text is returned as a list of lines.

All other text blocks are descriptive text paragraphs. Paragraphs may extend across multiple lines and are terminated either when the line matches one of the list items patterns, an indented line (which is treated as preformatted text), or an empty line. Paragraphs are returned as a list of lines.

proc ::ruff::parse {lines} {

    # Creates a parse structure given a list of lines that are assumed
    # to be documentation for a programming structure
    #
    # lines - a list of lines comprising the documentation
    #
    set result(name) ""
    set result(listcollector) {}
    set result(fragment) {}
    set result(state) init
    set result(output) {}
    foreach line $lines {
        switch -regexp -- $line {
            {^\s*$} {
                #ruff
                # Empty lines or lines with only whitespace
                # terminate the preceding
                # text block (such as a paragraph or a list).
                switch -exact -- $result(state) {
                    init -
                    postsummary {
                        # No change
                    }
                    summary {
                        _change_state postsummary result
                    }
                    default {
                        _change_state blank result
                    }
                }
            }
            {^\s*[-\*]\s+(.*)$} {
                #ruff
                # A bulleted list item starts with a '-' or '*' character.
                # A list item may be continued across multiple lines by
                # indending succeeding lines belonging to the same list item.
                # Note an indented line will terminate the previous list
                # item if it itself looks like a new list item.
                # A bulleted
                # list is returned as a list containing the list items, each
                # of which is a list of lines.
                _change_state bulletlist result
                if {![regexp {^\s*[-\*]\s+(.*)$} $line dontcare fragment]} {
                    error "Internal error: regexp did not match after switch statement matched."
                }
                lappend result(fragment) $fragment
            }
            {^\s*(\w+)\s+-(\s+.*)$} {
                #ruff
                # A definition list or parameter list begins with a word
                # followed by whitespace, a '-' character, whitespace
                # and descriptive
                # text. Whether it is treated as a parameter list or a
                # definition list depends on whether it occurs in the comment
                # block. If it occurs at the beginning or just after the
                # summary line, it is treated as a parameter list.
                # In all other cases, it is treated as a definition list.
                # Like a bulleted list, each list item may be continued
                # on succeeding lines by indenting them.
                # Definition and parameter lists
                # are returned as flat list
                # of alternating list item name and list item value
                # pairs. The list item value is itself a list of lines.
                if {[lsearch -exact {init summary postsummary parameter} $result(state)] >= 0} {
                    _change_state parameter result
                } else {
                    _change_state deflist result
                }
                if {![regexp {^\s*(\w+)\s+-(\s+.*)$} $line dontcare result(name) fragment]} {
                    error "Internal error: regexp did not match after switch statement matched."
                }
                lappend result(fragment) $fragment
            }
            {^\s*(-\w+.*)\s+-(\s+.*)$} {
                #ruff
                # An option list is similar to a parameter list except that
                # the first word on the line begins with a '-' character and
                # is possibly followed by more words before the '-' character
                # that separates the descriptive text. The '-' separator
                # must be surrounded by whitespace. The value returned
                # for an option list follows the same structure as for
                # parameter or definition list items. Any line in any
                # documentation block that matches this is always added
                # to the option list, irrespective of where it occurs. This
                # means option descriptions can be mingled with other
                # documentation fragments and will show up in the options
                # section.

                _change_state option result

                if {![regexp {^\s*(-\w+.*)\s+-(.*)$} $line dontcare result(name) fragment]} {
                    error "Internal error: regexp did not match after switch statement matched."
                }
                lappend result(fragment) $fragment
            }
            {^Returns($|\s.*$)} {
                #ruff
                # Any paragraph that begins with the word 'Returns' is treated
                # as a description of the return value irrespective of where
                # it occurs. It is returned as a list of lines.
                _change_state return result
                lappend result(fragment) $line
            }
            {^\s+} {
                #ruff
                # Lines beginning with spaces
                # are treated as preformatted text unless they are part
                # of a list item. Preformatted text is returned as a list
                # of lines.
                switch -exact -- $result(state) {
                    preformatted -
                    bulletlist -
                    deflist -
                    parameter -
                    option {
                        # No change. Keep adding to existing block
                    }
                    default {
                        _change_state preformatted result
                    }
                }
                lappend result(fragment) $line
            }
            default {
                #ruff
                # All other text blocks are descriptive text paragraphs.
                # Paragraphs may extend across multiple lines and are
                # terminated either when the line matches one of the list
                # items patterns, an indented line (which is treated
                # as preformatted text), or an empty line. Paragraphs
                # are returned as a list of lines.

                switch -exact -- $result(state) {
                    init { _change_state summary result }
                    postsummary -
                    blank -
                    bulletlist -
                    parameter -
                    deflist -
                    option -
                    preformatted { _change_state paragraph result }
                    default {
                        # Stay in same state
                    }
                }
                lappend result(fragment) $line
            }
        }
    }
    _change_state finish result; # To process any leftovers in result(fragment)

    # Returns a list of key value pairs where key is one
    # of 'parameter', 'option', 'bulletlist', 'deflist', 'parameter',
    # 'preformatted', 'paragraph' or 'return',
    # and the value
    # is the corresponding value.
    return $result(output)
}

::ruff::app

Commands

log_error [::ruff::app]

app, Top

Stub function to log Ruff! errors.

log_error msg

Parameters
msg the message to be logged
Description

Stub function to log Ruff! errors.

When Ruff! encounters errors, it calls this command to notify the user. By default, the command writes $msg to stderr output. An application using the ruff package can redefine this command after loading ruff.

proc ::ruff::app::log_error {msg} {

    # Stub function to log Ruff! errors.
    # msg - the message to be logged
    #
    # When Ruff! encounters errors, it calls this command to
    # notify the user. By default, the command writes $msg
    # to stderr output. An application using the ruff package
    # can redefine this command after loading ruff.
    puts stderr "$msg"
}

::ruff::formatter::doctools

Commands

escape [::ruff::formatter::doctools]

doctools, Top

escape s

Parameters
s string to be escaped
Description

Protects a string against doctools substitution in text (not to be used inside a doctools command argument as that follows Tcl escaping rules and are easiest escaped by enclosing in braces)

proc ::ruff::formatter::doctools::escape {s} {

    # s - string to be escaped
    # Protects a string against doctools substitution in text
    # (not to be used inside a doctools command argument as that
    # follows Tcl escaping rules and are easiest escaped by enclosing
    # in braces)

    # It appears as though the only characters needing replacing are
    # [ and ]. Other Tcl special chars ($ \ etc.) do not matter
    # return [string map [list \\ \\\\ \[ \[lb\] \] \[rb\] \$ \\\$] $s]
    return [string map [list \[ \[lb\] \] \[rb\]] $s]
}

generate_document [::ruff::formatter::doctools]

doctools, Top

Produces documentation in doctools format from the passed in class and proc metainformation.

generate_document classprocinfodict args

Parameters
classprocinfodict dictionary containing meta information about the classes and procs
argsAdditional options.
-hidenamespace NAMESPACE if specified as non-empty, program element names beginning with NAMESPACE are shown with that namespace component removed.
-includesource BOOLEAN if true, the source code of the procedure is also included. Default value is false.
-preamble DICT a dictionary indexed by a namespace. Each value is a flat list of pairs consisting of a heading and corresponding content. These are inserted into the document before the actual class and command descriptions for a namespace. The key "::" corresponds to documentation to be printed at the very beginning.
Description

Produces documentation in doctools format from the passed in class and proc metainformation.

In addition to options described in the ruff::document command, the following additional ones may be specified:

proc ::ruff::formatter::doctools::generate_document {classprocinfodict args} {

    # Produces documentation in doctools format from the passed in
    # class and proc metainformation.
    #   classprocinfodict - dictionary containing meta information about the
    #     classes and procs
    #
    # In addition to options described in the ruff::document command,
    # the following additional ones may be specified:
    #   -preamble DICT - a dictionary indexed by a namespace. Each value is
    #    a flat list of pairs consisting of a heading and
    #    corresponding content. These are inserted into the document
    #    before the actual class and command descriptions for a namespace.
    #    The key "::" corresponds to documentation to be printed at
    #    the very beginning.
    #   -includesource BOOLEAN - if true, the source code of the
    #     procedure is also included. Default value is false.
    #   -hidenamespace NAMESPACE - if specified as non-empty,
    #    program element names beginning with NAMESPACE are shown
    #    with that namespace component removed.

    array set opts  [list  -includeclasses true  -includeprocs true  -includeprivate false  -includesource false  -hidenamespace ""  -section "n"  -version "0.0"  -name ""  -titledesc ""  -modulename ""  -require {}  -author ""  -keywords {}  -year [clock format [clock seconds] -format %Y]  -preamble [dict create]  ]

    array set opts $args

    # TBD - does anything need to be escape'ed here?
    set doc "\[manpage_begin \"$opts(-name)\" \"$opts(-section)\" \"$opts(-version)\"\]\n"
    if {$opts(-author) ne ""} {
        append doc "\[copyright {$opts(-year) \"$opts(-author)\"}\]\n"
    }
    if {$opts(-titledesc) ne ""} {
        append doc "\[titledesc \"$opts(-titledesc)\"\]\n"
    }
    if {$opts(-modulename) ne ""} {
        append doc "\[moddesc \"$opts(-modulename)\"\]\n"
    }
    if {[llength $opts(-require)]} {
        foreach require $opts(-require) {
            append doc "\[require $require\]\n"
        }
    }

    # Begin the description section
    append doc "\[description\]\n"

    if {[dict exists $opts(-preamble) "::"]} {
        # Print the toplevel (global stuff)
        foreach {sec paras} [dict get $opts(-preamble) "::"] {
            append doc [subsection $sec]
            append doc [_fmtparas $paras]
        }
    }

    set info_by_ns [_sift_classprocinfo $classprocinfodict]

    foreach ns [lsort -dictionary [dict keys $info_by_ns]] {
        append doc [section "Module $ns"]
        if {[dict exists $opts(-preamble) $ns]} {
            foreach {sec paras} [dict get $opts(-preamble) $ns] {
                append doc [section $sec]
                append doc [_fmtparas $paras]
            }
        }

        if {[dict exists $info_by_ns $ns classes]} {
            append doc [section Classes]\n
            append doc [generate_ooclasses [dict get $info_by_ns $ns classes]  -includesource $opts(-includesource)  -hidenamespace $opts(-hidenamespace)  ]
        }
        if {[dict exists $info_by_ns $ns procs]} {
            append doc [section Commands]\n
            append doc [generate_procs [dict get $info_by_ns $ns procs]  -includesource $opts(-includesource)  -hidenamespace $opts(-hidenamespace)  ]
        }
    }

    if {[llength $opts(-keywords)] == 0} {
        # dtplite will barf if no keywords in man page. Logged on sf.net
        # as a bug against doctools
        ::ruff::app::log_error "Warning: no keywords specified in this module. If no modules have keywords some versions of the doctools indexer may generate an error in some modes."
    }

    if {[llength $opts(-keywords)]} {
        append doc [eval keywords $opts(-keywords)]
    }


    append doc "\[manpage_end\]\n"

    return $doc
}

generate_ooclass [::ruff::formatter::doctools]

doctools, Top

Formats the documentation for a class in doctools format

generate_ooclass classinfo args

Parameters
classinfo class information in the format returned by extract_ooclass
argsAdditional options.
-hidenamespace NAMESPACE if specified as non-empty, program element names beginning with NAMESPACE are shown with that namespace component removed.
-includesource BOOLEAN if true, the source code of the procedure is also included. Default value is false.
Return value

Returns the class documentation as a doctools formatted string.

Description

Formats the documentation for a class in doctools format

The following options may be specified:

proc ::ruff::formatter::doctools::generate_ooclass {classinfo args} {


    # Formats the documentation for a class in doctools format
    # classinfo - class information in the format returned
    #   by extract_ooclass
    #
    # The following options may be specified:
    #   -includesource BOOLEAN - if true, the source code of the
    #     procedure is also included. Default value is false.
    #   -hidenamespace NAMESPACE - if specified as non-empty,
    #    program element names beginning with NAMESPACE are shown
    #    with that namespace component removed.
    #
    # Returns the class documentation as a doctools formatted string.

    array set opts {-includesource false -hidenamespace ""}
    array set opts $args

    array set aclass $classinfo
    set doc ""

    # The quoting of strings below follows what I understand of doctools
    # - only [ and ] are special in text outside of doctools commands.
    # Such strings are quoted using the escape command. Arguments to
    # doctools commands are quoted using {}.

    set class_name [_trim_namespace $aclass(name) $opts(-hidenamespace)]
    set displayprefix "$class_name."

    append doc [section "Class $class_name"]

    if {[info exists aclass(constructor)]} {
        append doc [list_begin definitions]
        append doc [generate_proc_or_method $aclass(constructor)  -includesource $opts(-includesource)  -hidenamespace $opts(-hidenamespace)  -displayprefix $displayprefix]
        append doc [list_end]
    }


    if {[llength $aclass(superclasses)]} {
        append doc [subsection "Superclasses"]
        # Don't sort - order matters!
        append doc [escape [join [_trim_namespace_multi $aclass(superclasses) $opts(-hidenamespace)]]]\n
    }
    if {[llength $aclass(subclasses)]} {
        append doc [subsection "Subclasses"]
        # Don't sort - order matters!
        append doc [escape [join [_trim_namespace_multi $aclass(subclasses) $opts(-hidenamespace)]]]\n
    }
    if {[llength $aclass(mixins)]} {
        append doc [subsection "Mixins"]
        # Don't sort - order matters!
        append doc [escape [join [_trim_namespace_multi $aclass(mixins) $opts(-hidenamespace)]]]\n
    }
    if {[llength $aclass(filters)]} {
        append doc [subsection "Filters"]
        # Don't sort - order matters!
        append doc [escape [join $aclass(filters) ", "]]\n
    }
    if {[llength $aclass(external_methods)]} {
        append doc [subsection "External Methods"]
        set external_methods {}
        foreach external_method $aclass(external_methods) {
            # Qualify the name with the name of the implenting class
            foreach {name imp_class} $external_method break
            if {$imp_class ne ""} {
                set name [_trim_namespace $imp_class $opts(-hidenamespace)].$name
            }
            lappend external_methods $name
        }
        append doc [escape [join [lsort $external_methods] ", "]]\n
    }

    append doc [subsection Methods]

    append doc [list_begin definitions]
    if {0} {
        # We are showing constructor as part of class definition
        if {[info exists aclass(constructor)]} {
            append doc [generate_proc_or_method $aclass(constructor)  -includesource $opts(-includesource)  -hidenamespace $opts(-hidenamespace)  -displayprefix $displayprefix]
        }
    }
    if {[info exists aclass(destructor)]} {
        append doc [generate_proc_or_method $aclass(destructor)  -includesource $opts(-includesource)  -hidenamespace $opts(-hidenamespace)  -displayprefix $displayprefix]
    }

    # We want methods and forwarded methods listed together and sorted
    array set methods {}
    foreach methodinfo $aclass(methods) {
        set methods([dict get $methodinfo name]) [list method $methodinfo]
    }
    if {[info exists aclass(forwards)]} {
        foreach forwardinfo $aclass(forwards) {
            set methods([dict get $forwardinfo name]) [list forward $forwardinfo]
        }
    }

    foreach name [lsort [array names methods]] {
        foreach {type info} $methods($name) break
        if {$type eq "method"} {
            append doc [generate_proc_or_method $info  -includesource $opts(-includesource)  -hidenamespace $opts(-hidenamespace)  -displayprefix $displayprefix]
        } else {
            # TBD - check formatting of forwarded methods
            # append doc [call [cmd "${displayprefix}[_trim_namespace [dict get $info name] $opts(-hidenamespace)]"]]
            append doc [call "[arg OBJECT] [cmd $name]"]
            # TBD - link to forwarded method if possible
            append doc "Method forwarded to [cmd [escape [dict get $info forward]]].\n"
        }
    }
    append doc [list_end]

    return $doc
}

generate_ooclasses [::ruff::formatter::doctools]

doctools, Top

Given a list of class information elements returns as string containing class documentation formatted for doctools

generate_ooclasses classinfodict args

Parameters
classinfodict dictionary keyed by class name and each element of which is in the format returned by extract_ooclass
argsAdditional options.
Description

Given a list of class information elements returns as string containing class documentation formatted for doctools

Additional parameters are passed on to the generate_ooclass procedure.

proc ::ruff::formatter::doctools::generate_ooclasses {classinfodict args} {

    # Given a list of class information elements returns as string
    # containing class documentation formatted for doctools
    # classinfodict - dictionary keyed by class name and each element
    #   of which is in the format returned by extract_ooclass
    #
    # Additional parameters are passed on to the generate_ooclass procedure.

    set doc ""
    foreach name [lsort [dict keys $classinfodict]] {
        append doc  [eval [list generate_ooclass [dict get $classinfodict $name]] $args]
        append doc "\n\n"
    }
    return $doc
}

generate_proc_or_method [::ruff::formatter::doctools]

doctools, Top

Formats the documentation for a proc in doctools format

generate_proc_or_method procinfo args

Parameters
procinfo class information in the format returned by extract_ooclass
argsAdditional options.
-displayprefix METHODNAME the string to use as a prefix for the method or proc name. Usually caller supplies this as the class name for the method.
-hidenamespace NAMESPACE if specified as non-empty, program element names beginning with NAMESPACE are shown with that namespace component removed.
-includesource BOOLEAN if true, the source code of the procedure is also included. Default value is false.
Return value

Returns the proc documentation as a doctools formatted string.

Description

Formats the documentation for a proc in doctools format

The following options may be specified:

proc ::ruff::formatter::doctools::generate_proc_or_method {procinfo args} {

    # Formats the documentation for a proc in doctools format
    # procinfo - class information in the format returned
    #   by extract_ooclass
    #
    # The following options may be specified:
    #   -includesource BOOLEAN - if true, the source code of the
    #     procedure is also included. Default value is false.
    #   -displayprefix METHODNAME - the string to use as a prefix
    #     for the method or proc name. Usually caller supplies this
    #     as the class name for the method.
    #   -hidenamespace NAMESPACE - if specified as non-empty,
    #    program element names beginning with NAMESPACE are shown
    #    with that namespace component removed.
    #
    # Returns the proc documentation as a doctools formatted string.


    array set opts {-includesource false -displayprefix "" -hidenamespace ""}
    array set opts $args

    array set aproc $procinfo

    set doc ""

    # The quoting of strings below follows what I understand of doctools
    # - only [ and ] are special in text outside of doctools commands.
    # Such strings are quoted using the escape command. Arguments to
    # doctools commands are quoted using {}.

    set itemlist {};            # For the parameter descriptions
    set arglist {};             # For the synopsis
    # Construct command synopsis
    foreach param $aproc(parameters) {
        if {[dict get $param type] ne "parameter"} {
            # We do not deal with options here
            continue
        }
        set name [dict get $param name]
        set item [arg_def {} $name]
        if {[dict exists $param description]} {
            append item " [escape [dict get $param description]]"
        }
        if {[dict exists $param default]} {
            lappend arglist [opt [arg $name]]
            append item " (default \[const {[dict get $param default]}\])"
        } else {
            lappend arglist [arg $name]
        }
        lappend itemlist $item
    }
    set proc_name $opts(-displayprefix)[_trim_namespace $aproc(name) $opts(-hidenamespace)]

    if {$aproc(proctype) ne "method"} {
        append doc [eval [list call [cmd $proc_name]] $arglist]\n
    } else {
        switch -exact -- $aproc(name) {
            constructor {append doc [eval [list call [cmd "::oo::class create [_trim_namespace $aproc(class) $opts(-hidenamespace)]"]] $arglist]}
            destructor  {append doc [call "[arg OBJECT] [cmd destroy]"]}
            default  {append doc [eval [list call "[arg OBJECT] [cmd $aproc(name)]"] $arglist]}
        }
    }

    set sep ""
    # Parameter description
    if {[llength $itemlist]} {
        append doc [list_begin arguments]\n
        append doc [join $itemlist \n]\n
        append doc [list_end]\n
        set sep [para]
    }

    # Option description
    set itemlist {}
    foreach param $aproc(parameters) {
        if {[dict get $param type] ne "option"} {
            continue
        }
        set name [dict get $param name]
        if {[llength $name] > 1} {
            set arg  [arg [lrange $name 1 end]]
            set name [option [lindex $name 0]]
        } else {
            set name [option $name]
            set arg {}
        }
        if {[dict exists $param description]} {
            set desc [dict get $param description]
        } else {
            set desc "No description available."
        }
        lappend itemlist [opt_def $name $arg] [escape $desc]
    }
    if {[llength $itemlist]} {
        append doc $sep
        append doc [list_begin options]
        append doc [join $itemlist \n]\n
        append doc [list_end]\n
        set sep [para]
    }

    # Loop through all the paragraphs
    set paras [_fmtparas $aproc(description)]
    if {$paras ne ""} {
        append doc $sep$paras
        set sep [para]
    }

    if {[info exists aproc(return)] && $aproc(return) ne ""} {
        append doc $sep
        append doc [escape $aproc(return)]
    }

    if {$opts(-includesource)} {
        append doc $sep
        append doc "Source:"
        append doc [para]

        # Just [escape...] won't do it. We need the example_begin as well
        append doc "\[example_begin\]\n"
        append doc [escape $aproc(source)]
        append doc "\[example_end\]\n"
    }


    return "${doc}\n"
}

generate_procs [::ruff::formatter::doctools]

doctools, Top

Given a dictionary of proc information elements returns a string containing documentation formatted for doctools

generate_procs procinfodict args

Parameters
procinfodict dictionary keyed by name of the proc with the associated value being in the format returned by extract_proc
argsAdditional options.
Description

Given a dictionary of proc information elements returns a string containing documentation formatted for doctools

Additional parameters are passed on to the generate_proc procedure.

The returned procedure descriptions are sorted in alphabetical order.

proc ::ruff::formatter::doctools::generate_procs {procinfodict args} {

    # Given a dictionary of proc information elements returns a string
    # containing documentation formatted for doctools
    # procinfodict - dictionary keyed by name of the proc with the associated
    #   value being in the format returned by extract_proc
    #
    # Additional parameters are passed on to the generate_proc procedure.

    #ruff
    # The returned procedure descriptions are sorted in alphabetical order.
    set doc "\[list_begin definitions\]\n"
    foreach name [lsort -dictionary [dict keys $procinfodict]] {
        append doc  [eval [list generate_proc_or_method [dict get $procinfodict $name]] $args]\n\n
    }
    append doc "\[list_end\]\n"

    return $doc
}

::ruff::formatter::html

Commands

escape [::ruff::formatter::html]

html, Top

Returns the escaped string

escape s

Parameters
s string to be escaped
Return value

Returns the escaped string

Description

Protects characters in $s against interpretation as HTML special characters.

proc ::ruff::formatter::html::escape {s} {

    # s - string to be escaped
    # Protects characters in $s against interpretation as
    # HTML special characters.
    #
    # Returns the escaped string

    return [string map {
        &    &amp;
        \"   &quot;
        <    &lt;
        >    &gt;
    } $s]
}

generate_document [::ruff::formatter::html]

html, Top

Produces documentation in HTML format from the passed in class and proc metainformation.

generate_document classprocinfodict args

Parameters
classprocinfodict dictionary containing meta information about the classes and procs
argsAdditional options.
-hidenamespace NAMESPACE if specified as non-empty, program element names beginning with NAMESPACE are shown with that namespace component removed.
-includesource BOOLEAN if true, the source code of the procedure is also included. Default value is false.
-preamble DICT a dictionary indexed by a namespace. Each value is a flat list of pairs consisting of a heading and corresponding content. These are inserted into the document before the actual class and command descriptions for a namespace. The key "::" corresponds to documentation to be printed at the very beginning.
-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.
-titledesc STRING the title for the documentation. Used as the title for the document. If undefined, the string "Reference" is used.
Description

Produces documentation in HTML format from the passed in class and proc metainformation.

The following options may be specified:

proc ::ruff::formatter::html::generate_document {classprocinfodict args} {

    # Produces documentation in HTML format from the passed in
    # class and proc metainformation.
    #   classprocinfodict - dictionary containing meta information about the
    #    classes and procs
    # The following options may be specified:
    #   -preamble DICT - a dictionary indexed by a namespace. Each value is
    #    a flat list of pairs consisting of a heading and
    #    corresponding content. These are inserted into the document
    #    before the actual class and command descriptions for a namespace.
    #    The key "::" corresponds to documentation to be printed at
    #    the very beginning.
    #   -includesource BOOLEAN - if true, the source code of the
    #     procedure is also included. Default value is false.
    #   -hidenamespace NAMESPACE - if specified as non-empty,
    #    program element names beginning with NAMESPACE are shown
    #    with that namespace component removed.
    #   -titledesc STRING - the title for the documentation.
    #    Used as the title for the document.
    #    If undefined, the string "Reference" is used.
    #   -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.

    variable yui_style;         # Contains default YUI based layout
    variable ruff_style;        # Contains default Ruff style sheet
    variable javascript;        # Javascript used by the page
    variable navlinks;          # Links generated for navigation menu
    variable link_targets;    # Links for cross-reference purposes

    # Re-initialize in case of multiple invocations
    array unset link_targets
    array set link_targets {}
    set navlinks [dict create]

    array set opts  [list  -includesource false  -hidenamespace ""  -titledesc ""  -modulename "Reference"  ]

    array set opts $args

    # TBD - create a link_target entry for each namespace

    # First collect all "important" names so as to build a list of
    # linkable targets. These will be used for cross-referencing and
    # also to generate links correctly in the case of
    # duplicate names in different namespaces or classes.
    #
    # A class name is also treated as a namespace component
    # although that is not strictly true.
    # TBD - the linked_targets and navlinks should really be merged
    # in some fashion as they overlap in function. The difference is
    # that the former needs to be built before any text processing is
    # done so linking in paras can be done. The latter is created as
    # the text is processed and also contains only links to be displayed
    # in the navigation menu.
    foreach {class_name class_info} [dict get $classprocinfodict classes] {
        set ns [namespace qualifiers $class_name]
        set link_targets($class_name) [_anchor $class_name]
        set method_info_list [concat [dict get $class_info methods] [dict get $class_info forwards]]
        foreach name {constructor destructor} {
            if {[dict exists $class_info $name]} {
                lappend method_info_list [dict get $class_info $name]
            }
        }
        foreach method_info $method_info_list {
            # The class name is the scope for methods. Because of how
            # the link target lookup works, we use the namespace
            # operator to separate the class from method. We also
            # store it a second time using the "." separator as that
            # is how they are sometimes referenced.
            set method_name [dict get $method_info name]
            set anchor [_anchor ${class_name}::${method_name}]
            set link_targets(${class_name}::${method_name}) $anchor
            set link_targets(${class_name}.${method_name}) $anchor
        }
    }
    foreach proc_name [dict keys [dict get $classprocinfodict procs]] {
        set link_targets(${proc_name}) [_anchor $proc_name]
    }

    set doc {<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN">}
    append doc "<html><head><title>$opts(-titledesc)</title>\n"
    if {[info exists opts(-stylesheets)]} {
        append doc "<style>\n$yui_style\n</style>\n"
        foreach url $opts(-stylesheets) {
            append doc "<link rel='stylesheet' type='text/css' href='$url' />"
        }
    } else {
        # Use built-in styles
        append doc "<style>\n$yui_style\n$ruff_style\n</style>\n"
    }
    append doc "<script>$javascript</script>"
    append doc "</head><body>"

    # YUI stylesheet templates
    append doc "<div id='doc3' class='yui-t2'>"
    if {$opts(-titledesc) ne ""} {
        append doc "<div id='hd' class='banner'>\n$opts(-titledesc)\n</div>\n"
    }
    append doc "<div id='bd'>"
    append doc "<div id='yui-main'>"
    append doc "<div class='yui-b'>"
    append doc "<a name='_top'></a>"

    # Build a regexp that can be used to convert references to classes, methods
    # and procedures to links.
    set methods {}
    foreach {class_name class_info} [dict get $classprocinfodict classes] {
        # Note we add both forms of method qualification - using :: and . -
        # since comments might be use both forms.
        foreach name {constructor destructor} {
            if {[dict exists $class_info $name]} {
                lappend methods ${class_name}.$name ${class_name}::$name
            }
        }
        foreach method_info [dict get $class_info methods] {
            lappend methods ${class_name}.[dict get $method_info name] ${class_name}::[dict get $method_info name]
        }
        foreach method_info [dict get $class_info forwards] {
            lappend methods ${class_name}.[dict get $method_info name] ${class_name}::[dict get $method_info name]
        }
    }
    set ref_regexp [_build_symbol_regexp  [concat  [dict keys [dict get $classprocinfodict procs]]  [dict keys [dict get $classprocinfodict classes]]  $methods
                            ]
                   ]

    if {$opts(-modulename) ne ""} {
        append doc [_fmthead $opts(-modulename) 1]
    }

    if {[info exists opts(-preamble)] &&
        [dict exists $opts(-preamble) "::"]} {
        # Print the toplevel (global stuff)
        foreach {sec paras} [dict get $opts(-preamble) "::"] {
            append doc [_fmthead $sec 1]
            append doc [_fmtparas $paras $ref_regexp]
        }
    }

    set info_by_ns [_sift_classprocinfo $classprocinfodict]
    foreach ns [lsort [dict keys $info_by_ns]] {
        set link_targets($ns) [_anchor $ns]
        append doc [_fmthead $ns 1]

        if {[info exists opts(-preamble)] &&
            [dict exists $opts(-preamble) $ns]} {
            # Print the preamble for this namespace
            foreach {sec paras} [dict get $opts(-preamble) $ns] {
                append doc [_fmthead $sec 2]
                append doc [_fmtparas $paras $ref_regexp]
            }
        }

        if {[dict exists $info_by_ns $ns procs]} {
            append doc [_fmthead "Commands" 2 -namespace $ns]
            append doc [generate_procs [dict get $info_by_ns $ns procs]  -includesource $opts(-includesource)  -hidenamespace $opts(-hidenamespace)  -linkregexp $ref_regexp  ]
        }

        if {[dict exists $info_by_ns $ns classes]} {
            append doc [_fmthead "Classes" 2 -namespace $ns]
            append doc [generate_ooclasses [dict get $info_by_ns $ns classes]  -includesource $opts(-includesource)  -hidenamespace $opts(-hidenamespace)  -linkregexp $ref_regexp  ]
        }
    }
    append doc "</div>";        # <div class='yui-b'>
    append doc "</div>";        # <div id='yui-main'>

    # Add the navigation bits
    append doc "<div class='yui-b navbox'>"
    dict for {text link} $navlinks {
        set label [dict get $link label]
        set tag  [dict get $link tag]
        set href [dict get $link href]
        if {[dict exists $link tip]} {
            append doc "<$tag><a class='tooltip' href='$href'>$label<span>[dict get $link tip]</span></a></$tag>"
        } else {
            append doc "<$tag><a href='$href'>$label</a></$tag>"
        }
    }
    append doc "</div>";        # <div class='yui-b' for navigation>

    append doc "</div>";        # <div id='bd'>

    # The footer
    append doc "<div id='ft'>"
    append doc "<div style='float: right;'>Document generated by Ruff!</div>"
    if {[info exists opts(-copyright)]} {
        append doc "<div>&copy; [escape $opts(-copyright)]</div>"
    }
    append doc "</div>\n"

    append doc "</div>";        # <div id='doc3' class='t3'>
    append doc "</body></html>"

    return $doc
}

generate_ooclass [::ruff::formatter::html]

html, Top

Formats the documentation for a class in HTML format

generate_ooclass classinfo args

Parameters
classinfo class information in the format returned by extract_ooclass
argsAdditional options.
-hidenamespace NAMESPACE if specified as non-empty, program element names beginning with NAMESPACE are shown with that namespace component removed.
-includesource BOOLEAN if true, the source code of the procedure is also included. Default value is false.
-linkregexp REGEXP if specified, any word matching the regular expression REGEXP is marked as a link.
Return value

Returns the class documentation as a NaturalDocs formatted string.

Description

Formats the documentation for a class in HTML format

proc ::ruff::formatter::html::generate_ooclass {classinfo args} {


    # Formats the documentation for a class in HTML format
    # classinfo - class information in the format returned
    #   by extract_ooclass
    # -includesource BOOLEAN - if true, the source code of the
    #   procedure is also included. Default value is false.
    # -hidenamespace NAMESPACE - if specified as non-empty,
    #  program element names beginning with NAMESPACE are shown
    #  with that namespace component removed.
    # -linkregexp REGEXP - if specified, any word matching the
    #  regular expression REGEXP is marked as a link.
    #
    # Returns the class documentation as a NaturalDocs formatted string.

    variable header_levels
    array set opts {
        -includesource false
        -hidenamespace ""
        -mergeconstructor false
        -linkregexp ""
    }
    array set opts $args

    array set aclass $classinfo
    set class_name [_trim_namespace $aclass(name) $opts(-hidenamespace)]
    set scope [namespace qualifiers $aclass(name)]

    array set method_summaries {}

    # We want to put the class summary right after the header but cannot
    # generate it till the end so we put the header in a separate variable
    # to be merged at the end.
    append dochdr [_fmtprochead $aclass(name) -level $header_levels(class)]

    set doc ""
    # Include constructor in main class definition
    if {$opts(-mergeconstructor) && [info exists aclass(constructor)]} {
        error "-mergeconstructor not implemented"
        TBD
        append doc [generate_proc_or_method $aclass(constructor)  -includesource $opts(-includesource)  -hidenamespace $opts(-hidenamespace)  -skipsections [list header name]  -linkregexp $opts(-linkregexp)  ]
    }

    if {[llength $aclass(superclasses)]} {
        append doc [_fmthead Superclasses $header_levels(nonav)]
        # Don't sort - order matters!
        append doc [_fmtpara [join [_trim_namespace_multi $aclass(superclasses) $opts(-hidenamespace)] {, }] $opts(-linkregexp) $scope]
    }
    if {[llength $aclass(mixins)]} {
        append doc [_fmthead "Mixins" $header_levels(nonav)]

        # Don't sort - order matters!
        append doc [_fmtpara [join [_trim_namespace_multi $aclass(mixins) $opts(-hidenamespace)] {, }] $opts(-linkregexp) $scope]
    }

    if {[llength $aclass(subclasses)]} {
        # Don't sort - order matters!
        append doc [_fmthead "Subclasses" $header_levels(nonav)]
        append doc [_fmtpara [join [_trim_namespace_multi $aclass(subclasses) $opts(-hidenamespace)] {, }] $opts(-linkregexp) $scope]
    }

    # Inherited and derived methods are listed as such.
    if {[llength $aclass(external_methods)]} {
        array set external_methods {}
        foreach external_method $aclass(external_methods) {
            # Qualify the name with the name of the implenting class
            foreach {name imp_class} $external_method break
            if {$imp_class ne ""} {
                set imp_class [_trim_namespace_multi $imp_class $opts(-hidenamespace)]
            }
            lappend external_methods($imp_class) ${imp_class}.$name
            set method_summaries($name) [dict create label [escape $name] desc [_linkify "See ${imp_class}.$name" $opts(-linkregexp) $scope]]
        }
        append doc [_fmthead "Inherited and mixed-in methods" $header_levels(nonav)]
        # Construct a sorted list based on inherit/mixin class name
        set ext_list {}
        foreach imp_class [lsort -dictionary [array names external_methods]] {
            lappend ext_list  [_linkify $imp_class $opts(-linkregexp) $scope]  [_linkify $external_methods($imp_class)  $opts(-linkregexp)  $imp_class]
        }
        append doc [_fmtdeflist $ext_list -preformatted both]
    }
    if {[llength $aclass(filters)]} {
        append doc [_fmthead "Filters" $header_levels(nonav)]
        append doc [_fmtpara [join [lsort $aclass(filters)] {, }] $opts(-linkregexp) $scope]
    }

    if {[info exists aclass(constructor)] && !$opts(-mergeconstructor)} {
        set method_summaries($aclass(name).constructor) [dict create label [_linkify "$aclass(name).constructor" $opts(-linkregexp) $aclass(name)] desc "Constructor for the class" ]
        append doc [generate_proc_or_method $aclass(constructor)  -includesource $opts(-includesource)  -hidenamespace $opts(-hidenamespace)  -linkregexp $opts(-linkregexp)  ]
    }
    if {[info exists aclass(destructor)]} {
        set method_summaries($aclass(name).destructor) [dict create label [_linkify "$aclass(name).destructor" $opts(-linkregexp) $aclass(name)] desc "Destructor for the class" ]
        append doc [generate_proc_or_method $aclass(destructor)  -includesource $opts(-includesource)  -hidenamespace $opts(-hidenamespace)  -linkregexp $opts(-linkregexp)  ]
    }

    # We want methods and forwarded methods listed together and sorted
    array set methods {}
    foreach methodinfo $aclass(methods) {
        set methods([dict get $methodinfo name]) [list method $methodinfo]
    }
    if {[info exists aclass(forwards)]} {
        foreach forwardinfo $aclass(forwards) {
            set methods([dict get $forwardinfo name]) [list forward $forwardinfo]
        }
    }

    foreach name [lsort [array names methods]] {
        foreach {type info} $methods($name) break
        if {$type eq "method"} {
            append doc [generate_proc_or_method $info  -includesource $opts(-includesource)  -hidenamespace $opts(-hidenamespace)  -linkregexp $opts(-linkregexp)  ]
            if {[dict exists $info summary]} {
                set summary [escape [dict get $info summary]]
            } elseif {[dict exists $info return]} {
                set summary [escape [dict get $info return]]
            } else {
                set summary ""
            }
            set method_summaries($aclass(name).$name) [dict create label [_linkify $aclass(name).$name $opts(-linkregexp) $aclass(name)] desc $summary]
        } else {
            set forward_text "Method forwarded to [dict get $info forward]"
            append doc [_fmtprochead $aclass(name)::$name -tooltip $forward_text -level $header_levels(method)]
            append doc [_fmtpara $forward_text $opts(-linkregexp) $scope]
            set method_summaries($aclass(name).$name) [dict create label [_linkify $aclass(name).$name  $opts(-linkregexp) $aclass(name)] desc [_linkify $forward_text $opts(-linkregexp) $scope]]
        }
    }

    set summary_list {}
    foreach name [lsort -dictionary [array names method_summaries]] {
        lappend summary_list [dict get $method_summaries($name) label] [dict get $method_summaries($name) desc]
    }
    if {[llength $summary_list]} {
        # append dochdr [_fmthead "Method summary" $header_levels(nonav)]
        append dochdr [_fmtdeflist $summary_list -preformatted both]
    }

    return "$dochdr\n$doc"
}

generate_ooclasses [::ruff::formatter::html]

html, Top

Given a list of class information elements returns as string containing class documentation formatted for NaturalDocs

generate_ooclasses classinfodict args

Parameters
classinfodict dictionary keyed by class name and each element of which is in the format returned by extract_ooclass
argsAdditional options.
Description

Given a list of class information elements returns as string containing class documentation formatted for NaturalDocs

Additional parameters are passed on to the generate_ooclass procedure.

proc ::ruff::formatter::html::generate_ooclasses {classinfodict args} {

    # Given a list of class information elements returns as string
    # containing class documentation formatted for NaturalDocs
    # classinfodict - dictionary keyed by class name and each element
    #   of which is in the format returned by extract_ooclass
    #
    # Additional parameters are passed on to the generate_ooclass procedure.

    set doc ""
    foreach name [lsort [dict keys $classinfodict]] {
        append doc  [eval [list generate_ooclass [dict get $classinfodict $name]] $args]
        append doc "\n\n"
    }

    return $doc
}

generate_proc_or_method [::ruff::formatter::html]

html, Top

Formats the documentation for a proc in HTML format

generate_proc_or_method procinfo args

Parameters
procinfo proc or method information in the format returned by extract_ooclass
argsAdditional options.
-hidenamespace NAMESPACE if specified as non-empty, program element names beginning with NAMESPACE are shown with that namespace component removed.
-includesource BOOLEAN if true, the source code of the procedure is also included. Default value is false.
-linkregexp REGEXP if specified, any word matching the regular expression REGEXP is marked as a link.
-skipsections SECTIONLIST a list of sections to be left out from the generated document. This is generally useful if the return value is to be included as part of a larger section (e.g. constructor within a class)
Return value

Returns the proc documentation as a HTML formatted string.

Description

Formats the documentation for a proc in HTML format

The following options may be specified:

proc ::ruff::formatter::html::generate_proc_or_method {procinfo args} {

    # Formats the documentation for a proc in HTML format
    # procinfo - proc or method information in the format returned
    #   by extract_ooclass
    #
    # The following options may be specified:
    #   -includesource BOOLEAN - if true, the source code of the
    #     procedure is also included. Default value is false.
    #   -hidenamespace NAMESPACE - if specified as non-empty,
    #    program element names beginning with NAMESPACE are shown
    #    with that namespace component removed.
    #   -skipsections SECTIONLIST - a list of sections to be left
    #    out from the generated document. This is generally useful
    #    if the return value is to be included as part of a larger
    #    section (e.g. constructor within a class)
    #   -linkregexp REGEXP - if specified, any word matching the
    #    regular expression REGEXP is marked as a link.
    #
    # Returns the proc documentation as a HTML formatted string.

    variable header_levels

    array set opts {
        -includesource false
        -hidenamespace ""
        -skipsections {}
        -linkregexp ""
    }
    array set opts $args

    array set aproc $procinfo

    if {$aproc(proctype) ne "method"} {
        set scope [namespace qualifiers $aproc(name)]
    } else {
        set scope $aproc(class); # Scope is name of class
    }

    set doc "";                 # Document string

    set header_title [_trim_namespace $aproc(name) $opts(-hidenamespace)]
    set proc_name [_trim_namespace $aproc(name) $opts(-hidenamespace)]

    # Construct the synopsis and simultaneously the parameter descriptions
    # These are constructed as HTML (ie. already escaped) since we want
    # to format parameters etc.
    set desclist {};            # For the parameter descriptions
    set arglist {};             # Used later for synopsis
    foreach param $aproc(parameters) {
        set name [dict get $param name]
        set desc {}
        if {[dict get $param type] eq "parameter"} {
            lappend arglist [_arg $name]
            if {[dict exists $param default]} {
                lappend desc "(optional, default [_const [dict get $param default]])"
            }
        }
        if {[dict exists $param description]} {
            lappend desc [_linkify [dict get $param description] $opts(-linkregexp) $scope]
        } elseif {$name eq "args"} {
            lappend desc "Additional options."
        }

        lappend desclist [_arg $name] [join $desc " "]
    }

    if {$aproc(proctype) ne "method"} {
        set synopsis "[_cmd [namespace tail $proc_name]] [join $arglist { }]"
    } else {
        switch -exact -- $aproc(name) {
            constructor {set synopsis "[_cmd $aproc(class)] [_cmd create] [join $arglist { }]"}
            destructor  {set synopsis "[_arg OBJECT] [_cmd destroy]"}
            default  {set synopsis "[_arg OBJECT] [_cmd [namespace tail $aproc(name)]] [join $arglist { }]"}
        }
    }

    if {[info exists aproc(summary)] && $aproc(summary) ne ""} {
        set summary $aproc(summary)
    } elseif {[info exists aproc(return)] && $aproc(return) ne ""} {
        set summary $aproc(return)
    }

    if {[lsearch -exact $opts(-skipsections) header] < 0} {
        # We need a fully qualified name for cross-linking purposes
        if {$aproc(proctype) eq "method"} {
            set fqn $aproc(class)::$aproc(name)
        } else {
            set fqn $aproc(name)
        }

        if {[info exists summary]} {
            append doc [_fmtprochead $fqn -tooltip $summary -level $header_levels($aproc(proctype))]
        } else {
            append doc [_fmtprochead $fqn -level $header_levels($aproc(proctype))]
        }
    }

    if {[info exists summary]} {
        append doc [_fmtpara $summary $opts(-linkregexp) $scope]
    }

    append doc "<p><div class='ruff_synopsis'>$synopsis</div></p>\n"

    if {[llength $desclist]} {
        append doc [_fmthead Parameters $header_levels(nonav)]
        # Parameters are output as a list.
        append doc [_fmtdeflist $desclist -preformatted both]
    }

    if {[info exists aproc(return)] && $aproc(return) ne ""} {
        append doc [_fmthead "Return value" $header_levels(nonav)]
        append doc [_fmtpara $aproc(return) $opts(-linkregexp) $scope]
    }

    # Loop through all the paragraphs. Note the first para is also
    # the summary (already output) but we will show that in the general
    # description as well.
    if {[llength $aproc(description)]} {
        append doc [_fmthead "Description" $header_levels(nonav)]
        append doc [_fmtparas $aproc(description) $opts(-linkregexp) $scope]
    }

    # Do we include the source code in the documentation?
    if {$opts(-includesource)} {
        set src_id [_new_srcid]
        append doc "<div class='ruff_source'>"
        append doc "<p class='ruff_source_link'>"
        append doc "<a id='l_$src_id' href=\"javascript:toggleSource('$src_id')\">Show source</a>"
        append doc "</p>\n"
        append doc "<div id='$src_id' class='ruff_dyn_src'><pre>\n[escape $aproc(source)]\n</pre></div>\n"
        append doc "</div>";    # class='ruff_source'
    }


    return "${doc}\n"
}

generate_procs [::ruff::formatter::html]

html, Top

Given a dictionary of proc information elements returns a string containing HTML format documentation.

generate_procs procinfodict args

Parameters
procinfodict dictionary keyed by name of the proc with the associated value being in the format returned by extract_proc
argsAdditional options.
Return value

Returns documentation string in NaturalDocs format with procedure descriptions sorted in alphabetical order within each namespace.

Description

Given a dictionary of proc information elements returns a string containing HTML format documentation.

Additional parameters are passed on to the generate_proc procedure.

proc ::ruff::formatter::html::generate_procs {procinfodict args} {

    # Given a dictionary of proc information elements returns a string
    # containing HTML format documentation.
    # procinfodict - dictionary keyed by name of the proc with the associated
    #   value being in the format returned by extract_proc
    #
    # Additional parameters are passed on to the generate_proc procedure.
    #
    # Returns documentation string in NaturalDocs format with
    # procedure descriptions sorted in alphabetical order
    # within each namespace.

    set doc ""
    set namespaces [_sift_names [dict keys $procinfodict]]
    foreach ns [lsort -dictionary [dict keys $namespaces]] {
        foreach name [lsort -dictionary [dict get $namespaces $ns]] {
            append doc  [eval [list generate_proc_or_method [dict get $procinfodict $name]] $args]\n\n
        }
    }

    return $doc
}

::ruff::formatter::naturaldocs

Commands

generate_document [::ruff::formatter::naturaldocs]

naturaldocs, Top

Produces documentation in NaturalDocs format from the passed in class and proc metainformation.

generate_document classprocinfodict args

Parameters
classprocinfodict dictionary containing meta information about the classes and procs
argsAdditional options.
-hidenamespace NAMESPACE if specified as non-empty, program element names beginning with NAMESPACE are shown with that namespace component removed.
-includesource BOOLEAN if true, the source code of the procedure is also included. Default value is false.
-modulename NAME the name of the module. Used as the title for the document. If undefined, the string "Reference" is used.
-preamble DICT a dictionary indexed by a namespace. Each value is a flat list of pairs consisting of a heading and corresponding content. These are inserted into the document before the actual class and command descriptions for a namespace. The key "::" corresponds to documentation to be printed at the very beginning.
Description

Produces documentation in NaturalDocs format from the passed in class and proc metainformation.

The following options may be specified:

proc ::ruff::formatter::naturaldocs::generate_document {classprocinfodict args} {

    # Produces documentation in NaturalDocs format from the passed in
    # class and proc metainformation.
    #   classprocinfodict - dictionary containing meta information about the
    #    classes and procs
    #
    # The following options may be specified:
    #   -preamble DICT - a dictionary indexed by a namespace. Each value is
    #    a flat list of pairs consisting of a heading and
    #    corresponding content. These are inserted into the document
    #    before the actual class and command descriptions for a namespace.
    #    The key "::" corresponds to documentation to be printed at
    #    the very beginning.
    #   -includesource BOOLEAN - if true, the source code of the
    #     procedure is also included. Default value is false.
    #   -hidenamespace NAMESPACE - if specified as non-empty,
    #    program element names beginning with NAMESPACE are shown
    #    with that namespace component removed.
    #   -modulename NAME - the name of the module. Used as the title for the document.
    #    If undefined, the string "Reference" is used.

    array set opts  [list  -includesource false  -hidenamespace ""  -modulename "Reference"  -preamble [dict create]  ]

    array set opts $args
    set doc [_fmtheading Title $opts(-modulename)]

    # Build a regexp that can be used to convert references to classes, methods
    # and procedures to links.
    set methods {}
    foreach {class_name class_info} [dict get $classprocinfodict classes] {
        foreach method_info [dict get $class_info methods] {
            lappend methods ${class_name}.[dict get $method_info name]
        }
        foreach method_info [dict get $class_info forwards] {
            lappend methods ${class_name}.[dict get $method_info name]
        }
    }
    set ref_regexp [_build_symbol_regexp  [concat  [dict keys [dict get $classprocinfodict procs]]  [dict keys [dict get $classprocinfodict classes]]  $methods
                            ]
                   ]

    if {[dict exists $opts(-preamble) "::"]} {
        # Print the toplevel (global stuff)
        foreach {sec paras} [dict get $opts(-preamble) "::"] {
            append doc [_fmtheading $sec]
            append doc [_fmtparas $paras $ref_regexp]
        }
    }

    set info_by_ns [_sift_classprocinfo $classprocinfodict]

    foreach ns [lsort -dictionary [dict keys $info_by_ns]] {
        # append doc [_fmtheading Section $ns] <- this causes dup headings?
        if {[dict exists $opts(-preamble) $ns]} {
            foreach {sec paras} [dict get $opts(-preamble) $ns] {
                append doc [_fmtheading $sec]
                append doc [_fmtparas $paras $ref_regexp]
            }
        }

        # Output commands BEFORE classes else they show up as
        # part of a class definition unless they are grouped into a
        # separate section in which case summaries are duplicated.
        if {[dict exists $info_by_ns $ns procs]} {
            append doc [_fmtheading Section Commands]
            append doc [generate_procs [dict get $info_by_ns $ns procs]  -includesource $opts(-includesource)  -hidenamespace $opts(-hidenamespace)  -linkregexp $ref_regexp  ]
        }

        if {[dict exists $info_by_ns $ns classes]} {
            append doc [generate_ooclasses [dict get $info_by_ns $ns classes]  -includesource $opts(-includesource)  -hidenamespace $opts(-hidenamespace)  -linkregexp $ref_regexp  ]
        }
    }

    return $doc
}

generate_ooclass [::ruff::formatter::naturaldocs]

naturaldocs, Top

Formats the documentation for a class in NaturalDocs format

generate_ooclass classinfo args

Parameters
classinfo class information in the format returned by extract_ooclass
argsAdditional options.
-hidenamespace NAMESPACE if specified as non-empty, program element names beginning with NAMESPACE are shown with that namespace component removed.
-includesource BOOLEAN if true, the source code of the procedure is also included. Default value is false.
-linkregexp REGEXP if specified, any word matching the regular expression REGEXP is marked as a link.
Return value

Returns the class documentation as a NaturalDocs formatted string.

Description

Formats the documentation for a class in NaturalDocs format

The following options may be specified:

proc ::ruff::formatter::naturaldocs::generate_ooclass {classinfo args} {


    # Formats the documentation for a class in NaturalDocs format
    # classinfo - class information in the format returned
    #   by extract_ooclass
    #
    # The following options may be specified:
    #   -includesource BOOLEAN - if true, the source code of the
    #     procedure is also included. Default value is false.
    #   -hidenamespace NAMESPACE - if specified as non-empty,
    #    program element names beginning with NAMESPACE are shown
    #    with that namespace component removed.
    #   -linkregexp REGEXP - if specified, any word matching the
    #    regular expression REGEXP is marked as a link.
    #
    # Returns the class documentation as a NaturalDocs formatted string.

    variable markers
    variable leaders

    array set opts {
        -includesource false
        -hidenamespace ""
        -mergeconstructor false
        -linkregexp ""
    }
    array set opts $args

    array set aclass $classinfo
    set class_name [_trim_namespace $aclass(name) $opts(-hidenamespace)]

    set doc ""
    append doc [_fmtheading class $class_name]

    # Include constructor in main class definition
    if {$opts(-mergeconstructor) && [info exists aclass(constructor)]} {
        error "-mergeconstructor not implemented"
        append doc [generate_proc_or_method $aclass(constructor)  -includesource $opts(-includesource)  -hidenamespace $opts(-hidenamespace)  -skipsections [list header name]  -linkregexp $opts(-linkregexp)  ]
    }

    # TBD - in the various sections below we include some leading text
    # that is not really necessary. This is because NaturalDocs assumes
    # anything starting with a ":" is a code line and does not format
    # or link any words in it. This is a problem since class names etc.
    # may start with a ":" if namespace qualified

    if {[llength $aclass(superclasses)]} {
        append doc [_fmtheading Superclasses]
        # Don't sort - order matters!
        append doc [_fmtpara "The class inherits from the following classes: [join [_trim_namespace_multi $aclass(superclasses) $opts(-hidenamespace)] {, }]" $opts(-linkregexp)]
    }
    if {[llength $aclass(mixins)]} {
        append doc [_fmtheading "Mixins"]

        # Don't sort - order matters!
        append doc [_fmtpara "The class has the following classes mixed-in: [join [_trim_namespace_multi $aclass(mixins) $opts(-hidenamespace)] {, }]" $opts(-linkregexp)]
    }

    if {[llength $aclass(subclasses)]} {
        # Don't sort - order matters!
        append doc [_fmtheading "Subclasses"]
        append doc [_fmtpara "The following classes inherit from this class: [join [_trim_namespace_multi $aclass(subclasses) $opts(-hidenamespace)] {, }]" $opts(-linkregexp)]
    }

    # Inherited and derived methods are listed as such.
    if {[llength $aclass(external_methods)]} {
        set external_methods {}
        foreach external_method $aclass(external_methods) {
            # Qualify the name with the name of the implenting class
            foreach {name imp_class} $external_method break
            if {$imp_class ne ""} {
                set name [_trim_namespace_multi $imp_class $opts(-hidenamespace)].$name
            }
            lappend external_methods $name
        }
        append doc [_fmtheading "External methods"]
        append doc [_fmtpara "The following methods are either mixed-in or inherited: [join [lsort $external_methods] {, }]" $opts(-linkregexp)]
    }
    if {[llength $aclass(filters)]} {
        append doc [_fmtheading "Filters"]
        append doc [_fmtpara "The following methods are attached as filters for this class : [join [lsort $aclass(filters)] {, }]" $opts(-linkregexp)]
    }

    # In NaturalDocs, the Class heading establishes the "scope"
    # and we can straightaway list the methods directly after it and
    # have them tagged as belonging to the class.

    if {[info exists aclass(constructor)] && !$opts(-mergeconstructor)} {
        append doc [generate_proc_or_method $aclass(constructor)  -includesource $opts(-includesource)  -hidenamespace $opts(-hidenamespace)  -linkregexp $opts(-linkregexp)  ]
    }
    if {[info exists aclass(destructor)]} {
        append doc [generate_proc_or_method $aclass(destructor)  -includesource $opts(-includesource)  -hidenamespace $opts(-hidenamespace)  -linkregexp $opts(-linkregexp)  ]
    }

    # We want methods and forwarded methods listed together and sorted
    array set methods {}
    foreach methodinfo $aclass(methods) {
        set methods([dict get $methodinfo name]) [list method $methodinfo]
    }
    if {[info exists aclass(forwards)]} {
        foreach forwardinfo $aclass(forwards) {
            set methods([dict get $forwardinfo name]) [list forward $forwardinfo]
        }
    }

    foreach name [lsort [array names methods]] {
        foreach {type info} $methods($name) break
        if {$type eq "method"} {
            append doc [generate_proc_or_method $info  -includesource $opts(-includesource)  -hidenamespace $opts(-hidenamespace)  -linkregexp $opts(-linkregexp)  ]
        } else {
            # TBD - check formatting of forwarded methods
            append doc [_fmtheading "Method" $name]
            # TBD - link to forwarded method if possible
            append doc [_fmtpara "Method forwarded to [dict get $info forward]" $opts(-linkregexp)]
        }
    }

    return $doc
}

generate_ooclasses [::ruff::formatter::naturaldocs]

naturaldocs, Top

Given a list of class information elements returns as string containing class documentation formatted for NaturalDocs

generate_ooclasses classinfodict args

Parameters
classinfodict dictionary keyed by class name and each element of which is in the format returned by extract_ooclass
argsAdditional options.
Description

Given a list of class information elements returns as string containing class documentation formatted for NaturalDocs

Additional parameters are passed on to the generate_ooclass procedure.

proc ::ruff::formatter::naturaldocs::generate_ooclasses {classinfodict args} {

    # Given a list of class information elements returns as string
    # containing class documentation formatted for NaturalDocs
    # classinfodict - dictionary keyed by class name and each element
    #   of which is in the format returned by extract_ooclass
    #
    # Additional parameters are passed on to the generate_ooclass procedure.

    set doc ""
    foreach name [lsort [dict keys $classinfodict]] {
        append doc  [eval [list generate_ooclass [dict get $classinfodict $name]] $args]
        append doc "\n\n"
    }
    return $doc
}

generate_proc_or_method [::ruff::formatter::naturaldocs]

naturaldocs, Top

Formats the documentation for a proc in NaturalDocs format

generate_proc_or_method procinfo args

Parameters
procinfo proc or method information in the format returned by extract_ooclass
argsAdditional options.
-hidenamespace NAMESPACE if specified as non-empty, program element names beginning with NAMESPACE are shown with that namespace component removed.
-includesource BOOLEAN if true, the source code of the procedure is also included. Default value is false.
-linkregexp REGEXP if specified, any word matching the regular expression REGEXP is marked as a link.
-skipsections SECTIONLIST a list of sections to be left out from the generated document. This is generally useful if the return value is to be included as part of a larger section (e.g. constructor within a class)
Return value

Returns the proc documentation as a NaturalDocs formatted string.

Description

Formats the documentation for a proc in NaturalDocs format

The following options may be specified:

proc ::ruff::formatter::naturaldocs::generate_proc_or_method {procinfo args} {

    # Formats the documentation for a proc in NaturalDocs format
    # procinfo - proc or method information in the format returned
    #   by extract_ooclass
    #
    # The following options may be specified:
    #   -includesource BOOLEAN - if true, the source code of the
    #     procedure is also included. Default value is false.
    #   -hidenamespace NAMESPACE - if specified as non-empty,
    #    program element names beginning with NAMESPACE are shown
    #    with that namespace component removed.
    #   -skipsections SECTIONLIST - a list of sections to be left
    #    out from the generated document. This is generally useful
    #    if the return value is to be included as part of a larger
    #    section (e.g. constructor within a class)
    #   -linkregexp REGEXP - if specified, any word matching the
    #    regular expression REGEXP is marked as a link.
    #
    # Returns the proc documentation as a NaturalDocs formatted string.

    variable markers
    variable leaders

    array set opts {-includesource false
        -hidenamespace ""
        -skipsections {}
        -linkregexp ""
    }
    array set opts $args

    array set aproc $procinfo

    set doc "";                 # Document string

    # In NaturalDocs, method names are never qualifed by a class prefix
    # since they
    # automatically get scoped by previous Class or namespace headings.
    set header_title [_trim_namespace $aproc(name) $opts(-hidenamespace)]
    set proc_name [_trim_namespace $aproc(name) $opts(-hidenamespace)]

    if {[lsearch -exact $opts(-skipsections) header] < 0} {
        if {$aproc(proctype) eq "method"} {
            append doc [_fmtheading method $header_title]
        } else {
            append doc [_fmtheading proc $header_title]
        }
    }

    # Loop through all the paragraphs
    append doc [_fmtparas $aproc(description) $opts(-linkregexp)]

    if {[info exists aproc(return)] && $aproc(return) ne ""} {
        append doc [_fmtheading return]
        append doc [_fmtpara $aproc(return) $opts(-linkregexp)]
    }

    # Now spit out the parameter list. Note we do this AFTER
    # the paragraphs so NaturalDocs correctly picks up
    # a summary line (it wants it right after the topic.
    # Construct the synopsis and simultaneously the parameter descriptions
    set desclist {};            # For the parameter descriptions
    set arglist {};             # Used later for synopsis
    foreach param $aproc(parameters) {
        set name [dict get $param name]
        set desc {}
        if {[dict get $param type] eq "parameter"} {
            lappend arglist $name
            if {[dict exists $param default]} {
                # No visual way in NaturalDocs to show as optional so
                # explicitly state (although the synopsis will show the
                # default)
                lappend desc "(optional, default [dict get $param default])"
            }
        }
        if {[dict exists $param description]} {
            lappend desc "[dict get $param description]"
        }

        lappend desclist $name [join $desc " "]
    }

    if {[llength $desclist]} {
        append doc [_fmtheading parameters]
        # Parameters are output as a list.
        append doc [_fmtdeflist $desclist $opts(-linkregexp)]
    }

    # Do we include the source code in the documentation?
    if {$opts(-includesource)} {
        append doc [_fmtheading source]
        append doc "$leaders(text)(start code)"
        append doc [::textutil::adjust::indent $aproc(source) $leaders(text)]\n
        append doc "$leaders(text)(end code)"
    }

    # Synopsis - write a dummy proc WITHOUT any comment headers and
    # NaturalDocs will pick out the appropriate elements
    if {$aproc(proctype) ne "method"} {
        append doc "\nproc $proc_name \{$arglist\} {}\n"
    } else {
        switch -exact -- $aproc(name) {
            constructor {append doc "\nconstructor $arglist {}\n"}
            destructor  {append doc "\ndestructor {} {}\n"}
            default  {append doc "\nmethod $aproc(name) \{$arglist\} {}\n"}
        }
    }

    return "${doc}\n"
}

generate_procs [::ruff::formatter::naturaldocs]

naturaldocs, Top

Given a dictionary of proc information elements returns a string containing documentation formatted for NaturalDocs

generate_procs procinfodict args

Parameters
procinfodict dictionary keyed by name of the proc with the associated value being in the format returned by extract_proc
argsAdditional options.
Return value

Returns documentation string in NaturalDocs format with procedure descriptions sorted in alphabetical order.

Description

Given a dictionary of proc information elements returns a string containing documentation formatted for NaturalDocs

Additional parameters are passed on to the generate_proc procedure.

proc ::ruff::formatter::naturaldocs::generate_procs {procinfodict args} {

    # Given a dictionary of proc information elements returns a string
    # containing documentation formatted for NaturalDocs
    # procinfodict - dictionary keyed by name of the proc with the associated
    #   value being in the format returned by extract_proc
    #
    # Additional parameters are passed on to the generate_proc procedure.
    #
    # Returns documentation string in NaturalDocs format with
    # procedure descriptions sorted in alphabetical order.

    set doc ""
    foreach name [lsort -dictionary [dict keys $procinfodict]] {
        append doc  [eval [list generate_proc_or_method [dict get $procinfodict $name]] $args]\n\n
    }

    return $doc
}

::ruff::formatter::robodoc

Commands

generate_document [::ruff::formatter::robodoc]

robodoc, Top

Produces documentation in robodoc format from the passed in class and proc metainformation.

generate_document classprocinfodict args

Parameters
classprocinfodict dictionary containing meta information about the classes and procs
argsAdditional options.
-hidenamespace NAMESPACE if specified as non-empty, program element names beginning with NAMESPACE are shown with that namespace component removed.
-includesource BOOLEAN if true, the source code of the procedure is also included. Default value is false.
-preamble DICT a dictionary indexed by a namespace. Each value is a flat list of pairs consisting of a heading and corresponding content. These are inserted into the document before the actual class and command descriptions for a namespace. The key "::" corresponds to documentation to be printed at the very beginning.
Description

Produces documentation in robodoc format from the passed in class and proc metainformation.

proc ::ruff::formatter::robodoc::generate_document {classprocinfodict args} {

    # Produces documentation in robodoc format from the passed in
    # class and proc metainformation.
    #  classprocinfodict - dictionary containing meta information about the
    #    classes and procs
    #   -preamble DICT - a dictionary indexed by a namespace. Each value is
    #    a flat list of pairs consisting of a heading and
    #    corresponding content. These are inserted into the document
    #    before the actual class and command descriptions for a namespace.
    #    The key "::" corresponds to documentation to be printed at
    #    the very beginning.
    #   -includesource BOOLEAN - if true, the source code of the
    #     procedure is also included. Default value is false.
    #   -hidenamespace NAMESPACE - if specified as non-empty,
    #    program element names beginning with NAMESPACE are shown
    #    with that namespace component removed.

    variable markers

    array set opts  [list  -includesource false  -hidenamespace ""  -modulename MODULE  -preamble [dict create]  ]

    array set opts $args
    set doc ""

    if {[dict exists $opts(-preamble) "::"]} {
        # Print the toplevel (global stuff)
        foreach {sec paras} [dict get $opts(-preamble) "::"] {
            append doc "$markers(module) $opts(-modulename)/$sec\n"
            append doc [_fmtitem DESCRIPTION]
            append doc [_fmtparas $paras]
            append doc "$markers(end)\n"
        }
    }

    set info_by_ns [_sift_classprocinfo $classprocinfodict]
    foreach ns [lsort -dictionary [dict keys $info_by_ns]] {
        # TBD - does the following line cause dup headings?
        # append doc [_fmtheading Section $ns]
        if {[dict exists $opts(-preamble) $ns]} {
            foreach {sec paras} [dict get $opts(-preamble) $ns] {
                append doc "$markers(module) $ns/$sec\n"
                append doc [_fmtitem DESCRIPTION]
                append doc [_fmtparas $paras]
                append doc "$markers(end)\n"
            }
        }

        if {[dict exists $info_by_ns $ns classes]} {
            append doc [generate_ooclasses [dict get $info_by_ns $ns classes]  -includesource $opts(-includesource)  -hidenamespace $opts(-hidenamespace)  ]
        }
        if {[dict exists $info_by_ns $ns procs]} {
            append doc [generate_procs [dict get $info_by_ns $ns procs]  -includesource $opts(-includesource)  -hidenamespace $opts(-hidenamespace)  ]
        }
    }

    return $doc
}

generate_ooclass [::ruff::formatter::robodoc]

robodoc, Top

Formats the documentation for a class in robodoc format

generate_ooclass classinfo args

Parameters
classinfo class information in the format returned by extract_ooclass
argsAdditional options.
-hidenamespace NAMESPACE if specified as non-empty, program element names beginning with NAMESPACE are shown with that namespace component removed.
-includesource BOOLEAN if true, the source code of the procedure is also included. Default value is false.
Return value

Returns the class documentation as a robodoc formatted string.

Description

Formats the documentation for a class in robodoc format

The following options may be specified:

Because robodoc does not have a heading for mix-ins, they are include within the DERIVED FROM section.

Documentation for a class only lists the method names. The methods themselves are documented separately.

Inherited and derived methods are listed as such.

proc ::ruff::formatter::robodoc::generate_ooclass {classinfo args} {


    # Formats the documentation for a class in robodoc format
    # classinfo - class information in the format returned
    #   by extract_ooclass
    #
    # The following options may be specified:
    #   -includesource BOOLEAN - if true, the source code of the
    #     procedure is also included. Default value is false.
    #   -hidenamespace NAMESPACE - if specified as non-empty,
    #    program element names beginning with NAMESPACE are shown
    #    with that namespace component removed.
    #
    # Returns the class documentation as a robodoc formatted string.

    variable markers
    variable leaders

    array set opts {-includesource false -hidenamespace "" -mergeconstructor true}
    array set opts $args

    array set aclass $classinfo
    set class_name [_trim_namespace $aclass(name) $opts(-hidenamespace)]

    set doc ""
    set header_title "[namespace qualifiers $aclass(name)]/[namespace tail $aclass(name)]"
    append doc "$markers(class) $header_title\n"

    append doc [_fmtitem NAME $class_name]

    # Include constructor in main class definition
    if {$opts(-mergeconstructor) && [info exists aclass(constructor)]} {
        append doc [generate_proc_or_method $aclass(constructor)  -includesource $opts(-includesource)  -hidenamespace $opts(-hidenamespace)  -skipsections [list header name]  ]
    }

    #ruff
    # Because robodoc does not have a heading for mix-ins, they are include
    # within the DERIVED FROM section.
    if {[llength $aclass(superclasses)] || [llength $aclass(mixins)]} {
        append doc [_fmtitem "DERIVED FROM"]

        # Don't sort - order matters! Also, there is no heading for mixins
        # so we add them here.
        if {[llength $aclass(mixins)]} {
            # Don't sort - order matters!
            append doc [_fmtpara "Mixins: [join [_trim_namespace_multi $aclass(mixins) $opts(-hidenamespace)] {, }]"]
        }
        if {[llength $aclass(superclasses)]} {
            append doc [_fmtpara "Superclasses: [join [_trim_namespace_multi $aclass(superclasses) $opts(-hidenamespace)] {, }]"]
        }
    }


    if {[llength $aclass(subclasses)]} {
        # Don't sort - order matters!
        append doc [_fmtitem "DERIVED BY" [join [_trim_namespace_multi $aclass(subclasses) $opts(-hidenamespace)] ", "]]
    }

    #ruff
    # Documentation for a class only lists the method names. The
    # methods themselves are documented separately.
    set class_methods {}
    foreach methodinfo $aclass(methods) {
        lappend class_methods ${class_name}.[dict get $methodinfo name]
    }
    if {[info exists aclass(constructor)] && !$opts(-mergeconstructor)} {
        set class_methods [linsert $class_methods 0 constructor]
    }
    if {[info exists aclass(destructor)]} {
        set class_methods [linsert $class_methods 0 destructor]
    }
    append doc [_fmtitem METHODS]
    append doc [_fmtpara [join $class_methods ", "]]

    #ruff
    # Inherited and derived methods are listed as such.
    if {[llength $aclass(external_methods)]} {
        set external_methods {}
        foreach external_method $aclass(external_methods) {
            # Qualify the name with the name of the implenting class
            foreach {name imp_class} $external_method break
            if {$imp_class ne ""} {
                set name [_trim_namespace_multi $imp_class $opts(-hidenamespace)].$name
            }
            lappend external_methods $name
        }
        append doc [_fmtpara "External methods: [join [lsort $external_methods] {, }]"]
    }
    if {[llength $aclass(filters)]} {
        append doc [_fmtpara "Filters: [join [lsort $aclass(filters)] {, }]"]
    }

    # Finish up the class description
    append doc $markers(end)

    # Next we will generate the documentation for the methods themselves

    append doc "\n\n"

    if {[info exists aclass(constructor)] && !$opts(-mergeconstructor)} {
        append doc [generate_proc_or_method $aclass(constructor)  -includesource $opts(-includesource)  -hidenamespace $opts(-hidenamespace)]
    }
    if {[info exists aclass(destructor)]} {
        append doc [generate_proc_or_method $aclass(destructor)  -includesource $opts(-includesource)  -hidenamespace $opts(-hidenamespace)]
    }

    # We want methods and forwarded methods listed together and sorted
    array set methods {}
    foreach methodinfo $aclass(methods) {
        set methods([dict get $methodinfo name]) [list method $methodinfo]
    }
    if {[info exists aclass(forwards)]} {
        foreach forwardinfo $aclass(forwards) {
            set methods([dict get $forwardinfo name]) [list forward $forwardinfo]
        }
    }

    foreach name [lsort [array names methods]] {
        foreach {type info} $methods($name) break
        if {$type eq "method"} {
            append doc [generate_proc_or_method $info  -includesource $opts(-includesource)  -hidenamespace $opts(-hidenamespace)]
        } else {
            # Forwarded method
            set header_title "[namespace qualifiers $aclass(name)]/[namespace tail $aclass(name)].$name"
            append doc "\n$markers(method) $header_title\n"
            append doc [_fmtitem NAME "[_trim_namespace $aclass(name) $opts(-hidenamespace)].$name"]
            # TBD - link to forwarded method if possible
            append doc [_fmtpara "Method forwarded to [dict get $info forward]."]
            append doc "\n$markers(end)\n"
        }
    }

    return $doc
}

generate_ooclasses [::ruff::formatter::robodoc]

robodoc, Top

Given a list of class information elements returns as string containing class documentation formatted for robodoc

generate_ooclasses classinfodict args

Parameters
classinfodict dictionary keyed by class name and each element of which is in the format returned by extract_ooclass
argsAdditional options.
Description

Given a list of class information elements returns as string containing class documentation formatted for robodoc

Additional parameters are passed on to the generate_ooclass procedure.

proc ::ruff::formatter::robodoc::generate_ooclasses {classinfodict args} {

    # Given a list of class information elements returns as string
    # containing class documentation formatted for robodoc
    # classinfodict - dictionary keyed by class name and each element
    #   of which is in the format returned by extract_ooclass
    #
    # Additional parameters are passed on to the generate_ooclass procedure.

    set doc ""
    foreach name [lsort [dict keys $classinfodict]] {
        append doc  [eval [list generate_ooclass [dict get $classinfodict $name]] $args]
        append doc "\n\n"
    }
    return $doc
}

generate_proc_or_method [::ruff::formatter::robodoc]

robodoc, Top

Formats the documentation for a proc in robodoc format

generate_proc_or_method procinfo args

Parameters
procinfo proc or method information in the format returned by extract_ooclass
argsAdditional options.
-hidenamespace NAMESPACE if specified as non-empty, program element names beginning with NAMESPACE are shown with that namespace component removed.
-includesource BOOLEAN if true, the source code of the procedure is also included. Default value is false.
-skipsections SECTIONLIST a list of sections to be left out from the generated document. This is generally useful if the return value is to be included as part of a larger section (e.g. constructor within a class)
Return value

Returns the proc documentation as a robodoc formatted string.

Description

Formats the documentation for a proc in robodoc format

The following options may be specified:

In order for the Robodoc cross-links to work, the header markers generated use the namespace as the section followed by the command name or class name and method combined with a period.

proc ::ruff::formatter::robodoc::generate_proc_or_method {procinfo args} {

    # Formats the documentation for a proc in robodoc format
    # procinfo - proc or method information in the format returned
    #   by extract_ooclass
    #
    # The following options may be specified:
    #   -includesource BOOLEAN - if true, the source code of the
    #     procedure is also included. Default value is false.
    #   -hidenamespace NAMESPACE - if specified as non-empty,
    #    program element names beginning with NAMESPACE are shown
    #    with that namespace component removed.
    #   -skipsections SECTIONLIST - a list of sections to be left
    #    out from the generated document. This is generally useful
    #    if the return value is to be included as part of a larger
    #    section (e.g. constructor within a class)
    # Returns the proc documentation as a robodoc formatted string.

    variable markers
    variable leaders

    array set opts {-includesource false -hidenamespace "" -skipsections {}}
    array set opts $args

    array set aproc $procinfo

    set doc "";                 # Document string

    #ruff
    # In order for the Robodoc cross-links to work, the header markers
    # generated use the namespace as the section followed by the command
    # name or class name and method combined with a period.
    if {$aproc(proctype) eq "method"} {
        set header_title "[namespace qualifiers $aproc(class)]/[namespace tail $aproc(class)].$aproc(name)"
        set proc_name [_trim_namespace $aproc(class) $opts(-hidenamespace)].$aproc(name)
    } else {
        set header_title [namespace qualifiers $aproc(name)]/[namespace tail $aproc(name)]
        set proc_name [_trim_namespace $aproc(name) $opts(-hidenamespace)]
    }

    # TBD - mark header as internal depending on whether private method
    if {[lsearch -exact -nocase $opts(-skipsections) header] < 0} {
        if {$aproc(proctype) eq "method"} {
            append doc "$markers(method) $header_title\n"
        } else {
            append doc "$markers(function) $header_title\n"
        }
    }

    if {[lsearch -exact -nocase $opts(-skipsections) name] < 0} {
        append doc [_fmtitem NAME $proc_name]
    }


    # Construct the synopsis and simultaneously the parameter descriptions
    set desclist {};            # For the parameter descriptions
    set arglist {};             # For the synopsis
    # Construct command synopsis and parameter block
    # Unfortunately Robodoc does not seem to have any special way of
    # formatting these. Just output as text strings
    foreach param $aproc(parameters) {
        set name [dict get $param name]
        if {[dict get $param type] eq "parameter"} {
            lappend arglist $name
        }
        set desc "$name --"
        if {[dict exists $param default]} {
            # No visual way in robodoc to show as optional so explicitly state
            append desc " (optional, default [dict get $param default])"
        }
        if {[dict exists $param description]} {
            append desc " [dict get $param description]"
        }

        lappend desclist $desc
    }

    # Synopsis
    if {$aproc(proctype) ne "method"} {
        append doc [_fmtitem SYNOPSIS "$proc_name $arglist"]
    } else {
        switch -exact -- $aproc(name) {
            constructor {append doc [_fmtitem SYNOPSIS "::oo::class create [_trim_namespace $aproc(class) $opts(-hidenamespace)] $arglist"]}
            destructor  {append doc [_fmtitem SYNOPSIS "OBJECT destroy"]}
            default  {append doc [_fmtitem SYNOPSIS "OBJECT $aproc(name) $arglist"]}
        }
    }

    # Parameter descriptions
    if {[llength $desclist]} {
        append doc [_fmtitem PARAMETERS]
        # Parameters are output as a list.
        append doc [_fmtlist $desclist]
    }

    append doc [_fmtitem DESCRIPTION]


    # Loop through all the paragraphs
    # We need to remember the last character to detect possible errors
    # in Robodoc's list recognition. Either the list must follow a item
    # header or a line ending in colon (:). We pass : as the second
    # parameter below because we just put out a item header which
    # is equivalent.
    append doc [_fmtparas $aproc(description) :]

    if {[info exists aproc(return)] && $aproc(return) ne ""} {
        append doc [_fmtpara $aproc(return)]
    }

    if {$opts(-includesource)} {
        append doc [_fmtitem SOURCE]
        append doc [::textutil::adjust::indent $aproc(source) $leaders(text)]\n
    }

    if {[lsearch -exact -nocase $opts(-skipsections) header] < 0} {
        append doc $markers(end)
    }

    return "${doc}\n"
}

generate_procs [::ruff::formatter::robodoc]

robodoc, Top

Given a dictionary of proc information elements returns a string containing documentation formatted for robodoc

generate_procs procinfodict args

Parameters
procinfodict dictionary keyed by name of the proc with the associated value being in the format returned by extract_proc
argsAdditional options.
Description

Given a dictionary of proc information elements returns a string containing documentation formatted for robodoc

Additional parameters are passed on to the generate_proc procedure.

proc ::ruff::formatter::robodoc::generate_procs {procinfodict args} {

    # Given a dictionary of proc information elements returns a string
    # containing documentation formatted for robodoc
    # procinfodict - dictionary keyed by name of the proc with the associated
    #   value being in the format returned by extract_proc
    #
    # Additional parameters are passed on to the generate_proc procedure.

    set doc ""

    foreach name [lsort -dictionary [dict keys $procinfodict]] {
        append doc  [eval [list generate_proc_or_method [dict get $procinfodict $name]] $args]\n\n
    }

    return $doc
}
Document generated by Ruff!
© 2009 Ashok P. Nadkarni