Index: Makefile.in
===================================================================
diff -u -r444fa56b72c6d35bd3cbbe46a44b12a4ea33088f -r187fbd20a453ae9d73e9b48f88b8d6a8c79685c2
--- Makefile.in (.../Makefile.in) (revision 444fa56b72c6d35bd3cbbe46a44b12a4ea33088f)
+++ Makefile.in (.../Makefile.in) (revision 187fbd20a453ae9d73e9b48f88b8d6a8c79685c2)
@@ -297,7 +297,7 @@
$(xotcl_target_doc_dir)/XOTcl-langRef/index.html: library/xotcl/doc/langRef.xotcl
$(TCLSH) $(src_app_dir_native)/utils/nxdoc -doctitle XOTcl-langRef \
-docurl "http://next-scripting.org/" -docversion $(PACKAGE_VERSION) \
- -outdir $(xotcl_target_doc_dir) "@package:XOTcl-langRef"
+ -outdir $(xotcl_target_doc_dir) -frontend xodoc -- package:XOTcl-langRef
yuidoc-dev: NXDFLAGS := -validation
yuidoc-dev: yuidoc
@@ -325,7 +325,8 @@
$(xotcl_target_doc_dir)/XOTcl-langRef.xowiki : library/xotcl/doc/langRef.xotcl
$(TCLSH) $(src_app_dir_native)/utils/nxdoc -doctitle XOTcl-langRef \
-docurl "http://next-scripting.org/" -docversion $(PACKAGE_VERSION) \
- -outdir $(target_doc_dir) -format xowiki -layout many-to-1 "@package:XOTcl-langRef"
+ -outdir $(target_doc_dir) -format xowiki -layout many-to-1 \
+ -frontend xodoc -- package:XOTcl-langRef
xowiki-dev: NXDFLAGS := -validation
xowiki-dev: xowiki
Index: TODO
===================================================================
diff -u -r61a1c5c9e11a0277be442d98572bae6a3162cf1f -r187fbd20a453ae9d73e9b48f88b8d6a8c79685c2
--- TODO (.../TODO) (revision 61a1c5c9e11a0277be442d98572bae6a3162cf1f)
+++ TODO (.../TODO) (revision 187fbd20a453ae9d73e9b48f88b8d6a8c79685c2)
@@ -3548,7 +3548,6 @@
- nsf.c: make ":" a full equivalent vor nsf::my
(i.e. support -local, -system and -intrinsic)
- extend regression test
-
nsf.c:
- reform of argument parse. new parser uses NsfFlagObjType
to reuse earlier parse resuslts. Improved speed for
@@ -3606,8 +3605,39 @@
- nx.tcl: added handling of parameter option "noleadingdash"
in objectParameterSlots
+- doc:
+ * integrate ::nx::doc::make with Makefile.in
+ (provide shell calls and, targets and dependencies)
+ * provide a different flag for the generation of the documentation
+ (-develop, .... or -final) to show/hide it.
+ SS: By "it", you refer to the glossary?
+ * separate entries for methods and hooks (can't be called if not defined)?
+ hooks:
+ * recreate should only be called internally, similarly "init" etc.
+ * __unknown
+ unknown is a hook for Object but a method for Class
+
+- strange refcounting bug in 8.6b2 bug-is-86.tcl
+ where 2 refcounted items are not freed (value:class,
+ issued from nx.tcl around line 120). Compile with DEBUG86B2
+ for more info
+=================================================
+# -*- Tcl -*-
+package req nx
+package require nx::test
+
+nx::Test case ensemble-next-with-colon-prefix {
+ nx::Object create obj {
+ :public method foo {} { return [:info class] }
+ #:public method bar {} { return [:info] }
+ :method info {} {;}
+ }
+ ? {obj foo} {wrong # args: should be ":info"}
+}
+=================================================
+
TODO:
- warnings for "numeric" names for args and nonpos-args?
- special handling of values looking like nonpos-flags,
@@ -3635,27 +3665,6 @@
o1 info children ?-type class? ?pattern?
- - doc/langRef2.xotcl vs library/xotcl/doc/langRef.xotcl
-
- - strange refcounting bug in 8.6b2 bug-is-86.tcl
- where 2 refcounted items are not freed (value:class,
- issued from nx.tcl around line 120). Compile with DEBUG86B2
- for more info
-=================================================
-# -*- Tcl -*-
-package req nx
-package require nx::test
-
-nx::Test case ensemble-next-with-colon-prefix {
- nx::Object create obj {
- :public method foo {} { return [:info class] }
- #:public method bar {} { return [:info] }
- :method info {} {;}
- }
- ? {obj foo} {wrong # args: should be ":info"}
-}
-=================================================
-
- from parameters.test
# TODO: currently, we need two converters (or a converter on nx::Slot), since
# variable uses nsf::is and attribute uses the slot obj. method variable should
@@ -3756,27 +3765,26 @@
- default/initcmd/subsdefault: can we simplify these?
or add messages for conflicting usages.
+- Makefile/::nsf::config: Integrate git meta-data (commit hash, branch/tag labels)
+
- doc:
- * integrate ::nx::doc::make with Makefile.in
- (provide shell calls and, targets and dependencies)
- NextScriptingLanguage/index.html:
+ * @package.@require(): really needed?
- * glossary entries in nsf.nxd should be sorted (in the source)
- ...... Maybe, a single glossary.nxd file?
- SS: Right now, the name of the nxd file derives from the the
- script name. I mark this as TODO for the future.
+ * @package.@version: fix validation mode ... expected/actual
+ version numbers are not compared ...
- * provide a different flag for the generation of the documentation
- (-develop, .... or -final) to show/hide it.
- SS: By "it", you refer to the glossary?
+ * NextScriptingLanguage/index.html: glossary entries in nsf.nxd
+ should be sorted (in the source) ...... Maybe, a single glossary.nxd
+ file? SS: Right now, the name of the nxd file derives from the the
+ script name. I mark this as TODO for the future.
- * separate entries for methods and hooks (can't be called if not defined)?
- hooks:
- * recreate should only be called internally, similarly "init" etc.
- * __unknown
- unknown is a hook for Object but a method for Class
+ * doc/langRef2.xotcl vs library/xotcl/doc/langRef.xotcl
+ * @author: how to visualise the authorship in the generated markup
+ (yuidoc)?
+
+
- do we need contains in nx?
- nsf::proc
Index: apps/utils/nxdoc
===================================================================
diff -u -redea1a9c44f4c685e45e59e7f8864ae11d84ff42 -r187fbd20a453ae9d73e9b48f88b8d6a8c79685c2
--- apps/utils/nxdoc (.../nxdoc) (revision edea1a9c44f4c685e45e59e7f8864ae11d84ff42)
+++ apps/utils/nxdoc (.../nxdoc) (revision 187fbd20a453ae9d73e9b48f88b8d6a8c79685c2)
@@ -25,13 +25,15 @@
:property {validation:switch false}
#
- # input
+ # frontend
#
+ :property {frontend dc}
:property includes
- :property {excludes ""}
+ :property excludes
:property indexfiles:alias
:protected property sources:1..* {
+ set :incremental 1
set :config false
}
@@ -44,7 +46,11 @@
}
:protected method ... args {
- :sources [concat {*}[split $args :]]
+ foreach i $args {
+ set idx [string first : $i]
+ if {$idx == -1} continue;
+ dict lappend :sources [string range $i 0 [expr {$idx - 1}]] [string range $i [expr {$idx + 1}] end]
+ }
}
:protected method indexfiles {paths} {
@@ -104,24 +110,20 @@
}
:protected method init {} {
- set prj [@project new \
+
+ set prj [@project newFromSources \
+ -frontend ${:frontend} \
+ {*}[expr {[info exists :includes]?[list -include ${:includes}]:""}] \
+ {*}[expr {[info exists :excludes]?[list -exclude ${:excludes}]:""}] \
+ ${:sources} \
-name ${:doctitle} \
-url ${:docurl} \
-version ${:docversion} \
- -sources ${:sources}]
+ {*}[expr {${:validation}?"-mixin ::nx::doc::@project::Validator":""}]]
- processor process \
- -sandboxed \
- {*}[expr {${:validation}?"-validate":""}] \
- {*}[expr {[info exists :includes]?"-include [list ${:includes}]":""}] \
- $prj
+
- make doc \
- -format ${:format} \
- $prj \
- -theme ${:theme} \
- -layout ${:layout} \
- -outdir ${:outdir}
+ $prj write -format ${:format} -theme ${:theme} -layout ${:layout} -outdir ${:outdir}
}
}
namespace export CLI
Index: library/lib/nxdoc-assets/@project.html.asciidoc
===================================================================
diff -u -rfa7635cbfe2309b8e6282e2c7925fa2617b061aa -r187fbd20a453ae9d73e9b48f88b8d6a8c79685c2
--- library/lib/nxdoc-assets/@project.html.asciidoc (.../@project.html.asciidoc) (revision fa7635cbfe2309b8e6282e2c7925fa2617b061aa)
+++ library/lib/nxdoc-assets/@project.html.asciidoc (.../@project.html.asciidoc) (revision 187fbd20a453ae9d73e9b48f88b8d6a8c79685c2)
@@ -78,7 +78,7 @@
[:?objvar $entry refs {
[:? {[$entry eval [concat dict exists \${:refs} [current]]]} {
- [:!let refs [sort_by_value [$entry eval [concat dict get \${:refs} [current]]]]]
+ [:!let refs [sortByValue [$entry eval [concat dict get \${:refs} [current]]]]]
[:for src [dict keys $refs] {
[$src make_link [current]]
Index: library/lib/nxdoc-assets/@project.html.yuidoc
===================================================================
diff -u -r0db47dd8ab885de3deebf56293bf8e052e1f0965 -r187fbd20a453ae9d73e9b48f88b8d6a8c79685c2
--- library/lib/nxdoc-assets/@project.html.yuidoc (.../@project.html.yuidoc) (revision 0db47dd8ab885de3deebf56293bf8e052e1f0965)
+++ library/lib/nxdoc-assets/@project.html.yuidoc (.../@project.html.yuidoc) (revision 187fbd20a453ae9d73e9b48f88b8d6a8c79685c2)
@@ -10,6 +10,13 @@
+
+
Index: library/lib/nxdoc-assets/body.html.yuidoc
===================================================================
diff -u -r0db47dd8ab885de3deebf56293bf8e052e1f0965 -r187fbd20a453ae9d73e9b48f88b8d6a8c79685c2
--- library/lib/nxdoc-assets/body.html.yuidoc (.../body.html.yuidoc) (revision 0db47dd8ab885de3deebf56293bf8e052e1f0965)
+++ library/lib/nxdoc-assets/body.html.yuidoc (.../body.html.yuidoc) (revision 187fbd20a453ae9d73e9b48f88b8d6a8c79685c2)
@@ -1,17 +1,28 @@
-
+
-
-
-
-
-
+
+
+
+
@@ -24,13 +35,6 @@
[$project version]
}]
-
[$project name]
- [:? {[:info has type ::nx::doc::@package]} {
- >
${:name}
- } ? {[:info has type ::nx::doc::@class]} {
- > [:?var :partof {
[${:partof} name] >}] ${:name}
- }]
-
[:include leftbar]
@@ -53,10 +57,18 @@
-
- Copyright © [clock format [clock seconds] -format "%Y"]
-
-
-
Index: library/lib/nxdoc-assets/glossary.html.yuidoc
===================================================================
diff -u -rfa7635cbfe2309b8e6282e2c7925fa2617b061aa -r187fbd20a453ae9d73e9b48f88b8d6a8c79685c2
--- library/lib/nxdoc-assets/glossary.html.yuidoc (.../glossary.html.yuidoc) (revision fa7635cbfe2309b8e6282e2c7925fa2617b061aa)
+++ library/lib/nxdoc-assets/glossary.html.yuidoc (.../glossary.html.yuidoc) (revision 187fbd20a453ae9d73e9b48f88b8d6a8c79685c2)
@@ -25,7 +25,7 @@
$ddesc
[:?objvar $entry refs {
[:? {[$entry eval [concat dict exists \${:refs} [current]]]} {
- [:!let refs [sort_by_value [$entry eval [concat dict get \${:refs} [current]]]]]
+ [:!let refs [sortByValue [$entry eval [concat dict get \${:refs} [current]]]]]
Index: library/lib/nxdoc-assets/leftbar.html.yuidoc
===================================================================
diff -u -rfa7635cbfe2309b8e6282e2c7925fa2617b061aa -r187fbd20a453ae9d73e9b48f88b8d6a8c79685c2
--- library/lib/nxdoc-assets/leftbar.html.yuidoc (.../leftbar.html.yuidoc) (revision fa7635cbfe2309b8e6282e2c7925fa2617b061aa)
+++ library/lib/nxdoc-assets/leftbar.html.yuidoc (.../leftbar.html.yuidoc) (revision 187fbd20a453ae9d73e9b48f88b8d6a8c79685c2)
@@ -1,5 +1,4 @@
-[:!let self_owned_parts [:navigatable_parts]]
-[:!let owned_parts [dict merge $project_entities $self_owned_parts]]
+[:!let owned_parts [:navigatable_parts $project_entities]]
[:for feature [dict keys $owned_parts] {
Index: library/lib/nxdoc-assets/yuidoc/api-next.css
===================================================================
diff -u -r0db47dd8ab885de3deebf56293bf8e052e1f0965 -r187fbd20a453ae9d73e9b48f88b8d6a8c79685c2
--- library/lib/nxdoc-assets/yuidoc/api-next.css (.../api-next.css) (revision 0db47dd8ab885de3deebf56293bf8e052e1f0965)
+++ library/lib/nxdoc-assets/yuidoc/api-next.css (.../api-next.css) (revision 187fbd20a453ae9d73e9b48f88b8d6a8c79685c2)
@@ -1,46 +1,358 @@
-#bd {
- border:none;
+#doc3 {margin:0;}
+#doc3 #hd, #doc3 #bd, #doc3 #ft {
+ padding: 0 1em;
+ background-color:transparent;
}
+body { background-color:transparent; }
+/* main page */
+a:link { color: #002A4B; }
+a:visited { color: #00589d;}
-#yui-main .yui-b {
- /*float:left;*/
+#doc3 h1, #doc4 h1 {
+ border-bottom:1px solid #89A618;
+ font-weight:bold;
+ margin-left:34%;
+ margin-top:0;
+ padding:0;
+ font-size:138.5%;
+ margin-bottom:0em;
}
+#doc3 h1 a, #doc4 h1 a {
+ color:#89A618;
+ text-decoration:none;
+}
+
+#doc3 #hd { margin-bottom:1em; position: relative; zoom: 1; }
+#doc3 #hd h1 { color: #545454; font-size: 170%; padding: 0px 0 8px 180px; background: url(nx.png) 15px 9px no-repeat; height: 60px; font-weight: bold; }
+#doc3 #hd h1 a { position: relative; top: 14px; }
+#doc3 #hd a { text-decoration: none; color:#002A4B; }
+#doc3 #hd h3 {
+ background: #98AAB1; background-image: url(bg_hd.gif); color: #000; font-size: 100%; padding: 4px 10px; margin: 0 0 7px 0;
+ border: 1px solid #98AAB1;
+}
+#ft hr {
+ display: none;
+}
#ft {
- background:none;
+ background: #fff; background-image:none; color: #000; font-size: 100%; padding:0;0; border:none;
+}
+#doc3 #hd h3 A { color: #FFF; text-decoration: none; }
+#doc3 #hd .breadcrumbs { font-size: 85%; margin-bottom:10px;}
+#doc3 #hd .subtitle {position: absolute; right:1em; padding: 0px;margin:0px}
+
+#doc3 dl { margin: 2px 0; }
+#doc3 dd { margin-left: 20px; }
+#doc3 .requires dt { font-style: italic; }
+#doc3 .default { margin-top:6px; }
+#doc3 .detail .deprecated { margin-top:4px; padding:4px; background-color: #EFECCA }
+#doc3 .detail .deprecated strong { color:#441054; }
+#doc3 code, #doc4 code, pre {font-size:95%}
+
+#doc3 #hd h1 {
+ border: 1px solid #98AAB1;
+ background-color: #fff;
+ margin-bottom: .5em;
+
+}
+#bd {
border:none;
+ background-color: #fff;
}
-#yui-main .yui-b {
- /*margin-left:327px;*/
+#doc3 #bd h3.subheading {margin-left:34%;font-size:138.5%;font-weight:bold;}
+#doc4 #bd h3.subheading {margin-left:327px;font-size:138.5%;font-weight:bold;}
+
+.submodules dd {
+ font-size: 93%;
+ font-weight: italic;
}
-h1 {
- border-bottom:1px solid #89A618;
- font-weight:bold;
- margin-left:327px;
- margin-top:0;
- padding:0 0 25px;
- font-size:123.1%;
- margin-bottom:2em;
- text-transform:uppercase;
+
+
+#doc3 .classopts, #doc4 .classopts { font-size: 85%; float:right; margin:2px; padding: 2px; background-color:#fafcf2;border: 1px solid #89a618;}
+#yui-classopts-form fieldset legend { display: none; }
+
+/* undo reset.css styles for description block formatting */
+#doc3 .description ul { padding: 10px 0 10px 28px; font-size: 90%; list-style: disc}
+#doc3 .description li { list-style: disc}
+#doc3 .description p { padding-bottom: 10px}
+#doc3 .description strong { font-weight: bold;}
+#doc3 .description em {padding: 2px; background-color: #EFECCA}
+#doc3 pre, #doc4 pre { padding: 10px;}
+
+#doc3 pre.nx, #doc4 pre.nx { background-color:#fafcf2;border:1px solid #cedaa1;margin-top:1em;}
+
+#doc3 .summary,#doc4 .summary {
+ border:none;
+ background:#E7EDD3 url('boxarrow.png') no-repeat scroll left 10px;
+ border-bottom:3px solid #89a618;
+ margin: 0 0 1.5em 0;
+ padding:0.5em 40px;
}
-#doc3 #bd {
- margin-bottom:0;
+#doc3 .extends {font-weight: normal; font-size: 90%}
+
+#doc3 .nav {min-height: 400px;}
+#doc3 .nav .module, #doc4 .nav .module {
+ width:100%;
+ border-right:none;
+ border-bottom: none;
+ padding: 0;
+ overflow:hidden;
}
-.nav .module h4 {
+#doc3 .nav .module h4, #doc4 .nav .module h4 {
background:transparent;
text-transform:uppercase;
color:#89A618;
font-weight:bold;
border:none;
border-bottom:1px solid #89A618;
+ border-top:1px solid #89A618;
display:block;
+ padding:1px 0;
}
-.nav .module {
- border:none;
+#doc3 .nav .module h4 A, #doc4 .nav .module h4 A { color: #002A4B; text-decoration: none; }
+#doc3 .nav .module .content, #doc4 .nav .module .content { padding:0; }
+#doc3 .nav .module UL.content LI, #doc4 .nav .module UL.content LI { font-size: 100%; }
+#doc3 .nav .module UL.content A,#doc4 .nav .module UL.content A { text-decoration: none; color: #002A4B; display: block; padding:0; }
+#doc3 .nav .module LI,
+#doc3 .nav .module LI A {
+ zoom: 1;
}
-.summary {
- background:#E7EDD3 url(assets/boxarrow.png) no-repeat scroll left 10px;
+#doc3 .nav .module LI.selected A, #doc3 .nav .module LI A:hover,
+#doc4 .nav .module LI.selected A, #doc4 .nav .module LI A:hover {
+ background-color: #e7edd3;
+ zoom: 1;
+}
+
+#doc3 .section { margin: 0 7px 7px 0; }
+#doc3 .section strong { font-weight: bold;}
+#doc3 .section hr, #doc4 .section hr { border: none 0; border-top: 1px solid #CEDAA1; }
+#doc3 .section h4 { font-size:110%;}
+#doc3 .section h3, #doc4 .section h3 { background:#fff; background-image:none; width: 100%; color: #89A618; padding:0 5px; margin:0.5em 0;
+ border:none; font-size:140%;font-weight:normal; text-transform:uppercase; border-top:1px solid #89A618;border-bottom:1px solid #89A618;
+
+}
+#doc3 .section h3 .top { font-size: 60%; font-weight: normal; width: 100%; font-family: verdana; padding-left: 20px; }
+#doc3 .section h3 .top A { color: #000; text-decoration: none; }
+
+#doc3 .section.details .content { padding: 0 0 0 10px; }
+#doc3 .section.details .description { padding: 10px 0 0 20px; }
+#doc3 .section.details .description dt { font-weight: bold; }
+#doc3 .section.details .description td { border:1px solid #ccc; margin:2px;padding:2px;}
+
+#doc3 .members { padding:10px; border:1px solid #98AAB1; }
+#doc3 .members h4 { font-size: 100%;}
+
+#doc3 .inheritance {
+ border:none;
+ background:#E7EDD3 url('boxarrow.png') no-repeat scroll left 10px;
border-bottom:3px solid #89a618;
- margin-bottom:1.5em;
+ margin: 0 0 1.5em 0;
padding:0.5em 40px;
+ }
+#doc3 .inheritance h4 { font-size: 100%;}
+
+/* index page autocomplete */
+/*
+#propertysearch {;position:absolute;margin:1em;width:35em;}
+#searchinput {position:absolute;width:100%;height:1.4em;}
+#searchresults {position:absolute;top:1.7em;width:100%;}
+#searchresults .yui-ac-content {position:absolute;top:4px; left:0px; width:100%;height:20em;border:1px solid #aaa;background:#fff;overflow:auto;overflow-x:hidden;z-index:9050;}
+#searchresults .yui-ac-shadow {position:absolute;margin:.3em;width:100%;background:#a0a0a0;z-index:9049;}:
+#searchresults ul {padding:5px 0;width:100%;}
+#searchresults li {padding:0 5px;cursor:default;white-space:nowrap;}
+#searchresults li.yui-ac-highlight {background:#D1C6DA;}
+#searchresults li em { position:absolute; width:44%; overflow:hidden; color:#654D6C;}
+#searchresults li span { position:relative; left:46% }
+*/
+
+#propertysearch {
+ width: 25em;
+ position: absolute;
+ right: 5px;
+ bottom: 4px;
+ color:#002A4B;
}
+#searchinput {
+ width: 83%;
+ height: 1.4em;
+ border:1px solid #ccc;
+}
+#searchresults {
+ position: absolute;
+ right: 25em;
+ top: 25px;
+ height: 0;
+}
+#searchresults .yui-ac-content {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 25em;
+ height: 20em;
+ border: 1px solid #98AAB1;
+ background: #fff;
+ overflow: auto;
+ overflow-x: hidden;
+ z-index: 9050;
+}
+#searchresults li.yui-ac-highlight {
+ background-color: #ECF0F6;
+}
+#searchresults li em {
+ width:44%;
+ overflow: hidden;
+ color: #98AAB1;
+}
+
+
+.filter.deprecated,
+.filter.protected,
+.filter.missing,
+.filter.extra,
+.filter.extra,
+.filter.mismatch {
+ /*display: inherit;*/
+ display: none;
+}
+
+body.show_deprecated .filter.deprecated,
+body.show_protected .filter.protected,
+body.show_missing .filter.missing,
+body.show_extra .filter.extra,
+body.show_mismatch .filter.mismatch {
+ display: inherit;
+}
+
+
+#splash_classList ul {
+ margin: 1em;
+ margin-left:2em;
+}
+#splash_classList ul li {
+ list-style: disc outside;
+}
+
+
+/* source code view */
+#srcout {min-width: 580px; }
+*html #srcout { width: 100%; padding-bottom:1em; overflow-x:auto }
+
+.highlight .c { color: #60a0b0; font-style: italic } /* Comment */
+.highlight .err { border: 1px solid #FF0000 } /* Error */
+.highlight .k { color: #007020; font-weight: bold } /* Keyword */
+.highlight .o { color: #666666 } /* Operator */
+.highlight .cm { color: #60a0b0; font-style: italic } /* Comment.Multiline */
+.highlight .cp { color: #007020 } /* Comment.Preproc */
+.highlight .c1 { color: #60a0b0; font-style: italic } /* Comment.Single */
+.highlight .gd { color: #A00000 } /* Generic.Deleted */
+.highlight .ge { font-style: italic } /* Generic.Emph */
+.highlight .gr { color: #FF0000 } /* Generic.Error */
+.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */
+.highlight .gi { color: #00A000 } /* Generic.Inserted */
+.highlight .go { color: #808080 } /* Generic.Output */
+.highlight .gp { color: #c65d09; font-weight: bold } /* Generic.Prompt */
+.highlight .gs { font-weight: bold } /* Generic.Strong */
+.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
+.highlight .gt { color: #0040D0 } /* Generic.Traceback */
+.highlight .kc { color: #007020; font-weight: bold } /* Keyword.Constant */
+.highlight .kd { color: #007020; font-weight: bold } /* Keyword.Declaration */
+#.highlight .kp { color: #007020 } /* Keyword.Pseudo */
+.highlight .kr { color: #007020; font-weight: bold } /* Keyword.Reserved */
+.highlight .kt { color: #007020; font-weight: bold } /* Keyword.Type */
+.highlight .m { color: #40a070 } /* Literal.Number */
+.highlight .s { color: #4070a0 } /* Literal.String */
+.highlight .na { color: #4070a0 } /* Name.Attribute */
+.highlight .nb { color: #007020 } /* Name.Builtin */
+.highlight .nc { color: #0e84b5; font-weight: bold } /* Name.Class */
+.highlight .no { color: #60add5 } /* Name.Constant */
+.highlight .nd { color: #555555; font-weight: bold } /* Name.Decorator */
+.highlight .ni { color: #d55537; font-weight: bold } /* Name.Entity */
+.highlight .ne { color: #007020 } /* Name.Exception */
+.highlight .nf { color: #06287e } /* Name.Function */
+.highlight .nl { color: #002070; font-weight: bold } /* Name.Label */
+.highlight .nn { color: #0e84b5; font-weight: bold } /* Name.Namespace */
+.highlight .nt { color: #062873; font-weight: bold } /* Name.Tag */
+.highlight .nv { color: #bb60d5 } /* Name.Variable */
+.highlight .ow { color: #007020; font-weight: bold } /* Operator.Word */
+.highlight .mf { color: #40a070 } /* Literal.Number.Float */
+.highlight .mh { color: #40a070 } /* Literal.Number.Hex */
+.highlight .mi { color: #40a070 } /* Literal.Number.Integer */
+.highlight .mo { color: #40a070 } /* Literal.Number.Oct */
+.highlight .sb { color: #4070a0 } /* Literal.String.Backtick */
+.highlight .sc { color: #4070a0 } /* Literal.String.Char */
+.highlight .sd { color: #4070a0; font-style: italic } /* Literal.String.Doc */
+.highlight .s2 { color: #4070a0 } /* Literal.String.Double */
+.highlight .se { color: #4070a0; font-weight: bold } /* Literal.String.Escape */
+.highlight .sh { color: #4070a0 } /* Literal.String.Heredoc */
+.highlight .si { color: #70a0d0; font-style: italic } /* Literal.String.Interpol */
+.highlight .sx { color: #c65d09 } /* Literal.String.Other */
+.highlight .sr { color: #235388 } /* Literal.String.Regex */
+.highlight .s1 { color: #4070a0 } /* Literal.String.Single */
+.highlight .ss { color: #517918 } /* Literal.String.Symbol */
+.highlight .bp { color: #007020 } /* Name.Builtin.Pseudo */
+.highlight .vc { color: #bb60d5 } /* Name.Variable.Class */
+.highlight .vg { color: #bb60d5 } /* Name.Variable.Global */
+.highlight .vi { color: #bb60d5 } /* Name.Variable.Instance */
+.highlight .il { color: #40a070 } /* Literal.Number.Integer.Long */
+
+/* additions */
+
+#glossary dt {
+ font-weight:bold;
+}
+
+#glossary dt, dd {
+ line-height:1.8em;
+}
+
+a:link.nsfdoc-gloss,
+a:visited.nsfdoc-gloss,
+a:hover.nsfdoc-gloss,
+a:active.nsfdoc-gloss {
+ color: #000000; /* should be the same color as text */
+ text-decoration: none;
+ border-bottom-width: 1px;
+ border-bottom-style: dotted;
+ border-bottom-color: #000000; /* for IE 5, same color as above */
+ font-style: normal; /* for use with dfn */
+}
+
+a:link.nsfdoc-link,
+a:visited.nsfdoc-link,
+a:hover.nsfdoc-link,
+a:active.nsfdoc-link {
+ text-decoration: none;
+ border-bottom-style: none;
+ border-bottom-color: #000000; /* for IE 5, same color as above */
+ font-family:"Courier New",Courier,mono;
+ font-style: normal; /* for use with dfn */
+}
+span.status {
+ display:none;
+ padding: 0 5px 0 5px;
+ background:url(status.png) no-repeat scroll 0 0;
+ margin-left:0px;
+}
+
+span.missing {
+ display:inline;
+ background-position: -16px 0;
+}
+
+span.extra {
+ display:inline;
+ background-position: 0px 0;
+}
+
+span.mismatch {
+ display:inline;
+ background-position: -32px 0;
+}
+code span.var {
+ font-style: italic;
+}
+
+/* ONLY Stand-alone Version with doc3, not Web Version with doc4*/
+
+#doc3 #bd {margin-bottom:0;}
+#doc3 .header-sub-doc {margin-top:2em;border-bottom:1px solid #89A618; }
Index: library/lib/nxdoc-assets/yuidoc/base.css
===================================================================
diff -u -r0db47dd8ab885de3deebf56293bf8e052e1f0965 -r187fbd20a453ae9d73e9b48f88b8d6a8c79685c2
--- library/lib/nxdoc-assets/yuidoc/base.css (.../base.css) (revision 0db47dd8ab885de3deebf56293bf8e052e1f0965)
+++ library/lib/nxdoc-assets/yuidoc/base.css (.../base.css) (revision 187fbd20a453ae9d73e9b48f88b8d6a8c79685c2)
@@ -176,7 +176,7 @@
}
a.link-with-arrow {
- background:transparent url(assets/arrow-download-blue.png) no-repeat left 4px;
+ background:transparent url(arrow-download-blue.png) no-repeat left 4px;
padding-left:10px;
text-decoration:none;
}
@@ -307,7 +307,7 @@
width:148px;
}
#searchwrapper input[type="submit"] {
- background:#fff url(assets/arrow-blue.png) no-repeat center center;
+ background:#fff url(arrow-blue.png) no-repeat center center;
cursor:pointer;
width:20px;
/*height:20px;*/
@@ -349,13 +349,13 @@
.download-inner {
display:block;
border:1px solid #fff;
- background:transparent url(assets/bg-download.png) no-repeat left top;
+ background:transparent url(bg-download.png) no-repeat left top;
height:100%;
padding:8px 10px 8px 100px;
text-transform:uppercase;
}
.download-inner:hover {
- background:transparent url(assets/bg-download-hover.png) no-repeat left top;
+ background:transparent url(bg-download-hover.png) no-repeat left top;
}
@@ -454,7 +454,7 @@
text-align:right;
margin-bottom:20px;
padding:40px 150px 0 0;
- background:transparent url(assets/next-logo-s.png) no-repeat top right;
+ background:transparent url(next-logo-s.png) no-repeat top right;
}
Index: library/lib/nxdoc-assets/yuidoc/green.css
===================================================================
diff -u -r0db47dd8ab885de3deebf56293bf8e052e1f0965 -r187fbd20a453ae9d73e9b48f88b8d6a8c79685c2
--- library/lib/nxdoc-assets/yuidoc/green.css (.../green.css) (revision 0db47dd8ab885de3deebf56293bf8e052e1f0965)
+++ library/lib/nxdoc-assets/yuidoc/green.css (.../green.css) (revision 187fbd20a453ae9d73e9b48f88b8d6a8c79685c2)
@@ -1,20 +1,20 @@
html {
- background:#fff url(assets/footer-bg-green.png) no-repeat bottom center;
+ background:#fff url(footer-bg-green.png) no-repeat bottom center;
}
body {
- background:transparent url(assets/header-bg-green.png) no-repeat top center;
+ background:transparent url(header-bg-green.png) no-repeat top center;
}
.topnews-box {
- background:#e7edd3 url(assets/boxarrow.png) no-repeat left 30px;
+ background:#e7edd3 url(boxarrow.png) no-repeat left 30px;
}
div.no-date { /* eg section iverview*/
- background:#e7edd3 url(assets/boxarrow.png) no-repeat left 15px;
+ background:#e7edd3 url(boxarrow.png) no-repeat left 15px;
}
#searchwrapper input[type="submit"]:hover, input[type="submit"]:hover {
- background:#89a618 url(assets/arrow-white.png) no-repeat center center;
+ background:#89a618 url(arrow-white.png) no-repeat center center;
}
.list-table th, input.submit-button:hover {
Index: library/lib/nxdoc-assets/yuidoc/status.png
===================================================================
diff -u -rfa7635cbfe2309b8e6282e2c7925fa2617b061aa -r187fbd20a453ae9d73e9b48f88b8d6a8c79685c2
Binary files differ
Index: library/lib/nxdoc-core.tcl
===================================================================
diff -u -r6ef6700b363d0fcc6a4ccf78a9b51e27f5598936 -r187fbd20a453ae9d73e9b48f88b8d6a8c79685c2
--- library/lib/nxdoc-core.tcl (.../nxdoc-core.tcl) (revision 6ef6700b363d0fcc6a4ccf78a9b51e27f5598936)
+++ library/lib/nxdoc-core.tcl (.../nxdoc-core.tcl) (revision 187fbd20a453ae9d73e9b48f88b8d6a8c79685c2)
@@ -1,95 +1,50 @@
# @package nx::doc
#
-# Study for documentation classes for the Next Scripting Langauge
+# The NXDoc infrastructure is built upon a representational model of
+# NSF/NX code units; e.g., packages, commands, objects, and
+# classes. This package declares the essential entities for
+# representing NSF/NX programs, in terms of a special-purpose NSF/NX
+# program. In addition, some utilities for implementing front- and
+# backends for NXDoc are provided.
#
-# Compared to the "old" @ docmentation effort, this is a rather
-# light-weight structure based on xotcl 2 (next) language
-# features. The documentation classes build an (extensible) object
-# structure which is used as a basis for some renderers. In general,
-# the classes are defined in a way they can be used for
-#
-# a) building documentation outside the source code artefacts, or
-#
-# b) inside code artefacts (value added method definition commands
-# providing extra arguments for the documentation). The
-# documentation commands could reuse there names/arguments
-# etc. directly from the method definition by issuing these
-# commands inside the method definition methods.
-#
-# One could provide lint-like features to signal, whether the
-# documentation is in sync with actually defined methods (when these
-# are available).
-#
+# @author stefan.sobernig@wu.ac.at
# @require nx
-# @version 0.1
+# @version 1.0
+# @namespace ::nx::doc
package provide nx::doc 1.0
namespace eval ::nx::doc {}
package require nx
-package require nx::pp
namespace eval ::nx::doc {
namespace import -force ::nx::*
# @command ::nx::doc::@
#
- # The helper proc "@" is a conveniant way for creating new
- # documentation objects with less syntactic overhead.
+ # The helper proc "@" is a conveniant mean for creating new
+ # documentation objects with minimal syntactic overhead.
#
- # @param class Request an instance of a particular entity class (e.g., ...)
- # @param name What is the entity name (e.g., nx::doc for a package)
- # @param args A vector of arbitrary arguments, provided to the
+ # @parameter class Request an instance of a particular entity class (e.g., ...)
+ # @parameter name What is the entity name (e.g., nx::doc for a package)
+ # @parameter args A vector of arbitrary arguments, provided to the
# entity when being constructed
# @return The identifier of the newly created entity object
- # @subcommand ::nx::doc::@#foo
- #
- # This is the first subcommand foo of "@"
- # {{{
- # set do 1;
- # }}}
- #
- # @param -param1 do it
- # @param param2 do it a second time
- # @return Gives you a "foo" object
-
- # @subcommand ::nx::doc::@#bar
- #
- # This is the second subcommand bar of "@"
- #
- # @param -param1 do it
- # @param param2 do it a second time
- # @return Gives you a "bar" object
-
proc @ {class name args} {$class new -name $name {*}$args}
# @command ::nx::doc::sorted
#
- # This proc is used to sort instances by values of a specified
- # attribute. {{{ set
- # code 1; puts stderr $code; puts stderr [info script]; set l \{x\}
- # }}} Und nun gehen wir in eine zweite Zeile ... und fügen einen Link ein (e.g., {{@object ::nx::doc::@object}})
+ # This utility proc is used to sort entities by values of a
+ # specified attribute.
#
- # ... um nach einem Zeilenbruch weiterzumachen
- # {{{
- # \# Some comment
- # set instances [list [Object new] [Object new]]
- # ::nx::doc::sorted $instances; set l {{{x}}}; # Some comment
- # {{{ }}}
- # set instances [list [Object new] [Object new]]
- # ::nx::doc::sorted $instances
- # }}}
- # Here it goes wider ...
- # {{{
- # set instances [list [Object new] [Object new]]
- # ::nx::doc::sorted $instances
- # }}}
- #
- # @param instances Points to a list of entity instances to sort e.g. {{@object ::nx::doc::@object}}
- # @param sortedBy Indicates the attribte name whose values the sorting will be based on
- # @return A list of sorted documentation entity instances {{{instances of @object}}}
+ # @parameter instances Points to a list of entity instances
+ # to sort e.g. <<@class ::nx::doc::@object>>
+ # @parameter sortedBy Indicates the attribte name whose
+ # values the sorting will be based on
+ # @return A list of sorted documentation entity
+ # instances <<@class ::nx::doc::@object>>
proc sorted {instances sortedBy} {
set order [list]
foreach v $instances {lappend order [list $v [$v eval [list set :$sortedBy]]]}
@@ -109,15 +64,15 @@
}
- proc sort_by_value {d} {
+ proc sortByValue {d} {
set haystack [list]
dict for {key value} $d {
lappend haystack [list $key $value]
}
return [dict create {*}[concat {*}[lsort -integer -index 1 -decreasing $haystack]]]
}
- proc find_asset_path {{subdir library/lib/nxdoc-assets}} {
+ proc findAssetPath {{subdir library/lib/nxdoc-assets}} {
# This helper tries to identify the file system path of the
# asset ressources.
#
@@ -131,7 +86,7 @@
}
- Class create MixinLayer {
+ Class create MixinLayer -superclass Class {
:property {prefix ""}
:public method init {} {
set :active_mixins [dict create]
@@ -187,8 +142,7 @@
namespace eval ::nx::doc::entities {}
:public class method normalise {tagpath names} {
- # puts stderr "tagpath $tagpath names $names"
- # 1) verify balancedness of
+ # 1) verify balancedness of path spec elements
if {[llength $tagpath] != [llength $names]} {
return [list 1 "Imbalanced tag line spec: '$tagpath' vs. '$names'"]
}
@@ -231,7 +185,7 @@
#
# TODO interp-aliasing objects under different command names
# is currently not transparent to some ::nsf::* helpers,
- # such as ::nsf::object::exists. Should this be changed?
+ # such as ::nsf::object::exists. Do we need to tackle this?
#
if {$cmd ne ""} {
set cmd [namespace origin $cmd]
@@ -253,7 +207,6 @@
if {[$entity info lookup methods -source application @$axis] eq ""} {
return [list 1 "The tag '$axis' is not supported for the entity type '[namespace tail [$entity info class]]'"]
}
- #puts stderr "$entity @$axis id $value"
set entity [$entity @$axis id $value]
set last_axis $axis
set last_name $value
@@ -269,7 +222,7 @@
return [list 0 [expr {$all?$entity_path:$entity}]]
}
- # @method id
+ # @class.method {Tag id}
#
# A basic generator for the characteristic ideas, based on the
# root_namespace, the tag label, and the fully qualified name of
@@ -322,12 +275,12 @@
if {[::nsf::object::exists $id]} {
$id configure {*}$args
} else {
- :create $id {*}$args
+ set id [:create $id {*}$args]
}
return $id
}
- # @method get_unqualified_name
+ # @class.method {Tag get_unqualified_name}
#
# @param qualified_name The fully qualified name (i.e., including the root namespace)
:public method get_unqualified_name {qualified_name} {
@@ -339,12 +292,47 @@
#return [string trimleft [string map [list ${:tag} ""] [:get_unqualified_name $qualified_name]] ":"]
return [join [lrange [concat {*}[split [:get_unqualified_name $qualified_name] "::"]] 1 end] "::"]
}
+
+ # / / / / / / / / / / / / / / / / / / / / / / / /
+ # Manage chains of responsible container entities
+ #
+ # TODO: We don't need the stack-like dispensing of containers,
+ # make it a simple one-element store
+
+ :public class property containers:0..*,object,type=::nx::doc::ContainerEntity {
+ set :incremental 1
+ }
+
+ :public method "containers empty" {} -returns boolean {
+ return [[current class] eval {expr {![info exists :containers] || ![llength ${:containers}]}}]
+ }
+
+ :public method "containers peek" {} {
+ if {![:containers empty]} {
+ return [lindex [[current class] containers] end]
+ }
+ }
+
+ :public method "containers push" {container:object,type=::nx::doc::ContainerEntity} {
+ set prev [:containers peek]
+ if {$prev ne ""} {
+ $container previous $prev
+ }
+ [current class] containers add $container end
+ }
+
+ :public method "containers reset" {{v ""}} {
+ [current class] containers $v
+ }
+
}
Class create QualifierTag -superclass Tag {
:method get_fully_qualified_name {name} {
if {![string match "::*" $name]} {
- error "You need to provide a fully-qualified (absolute) entity name for '$name'."
+ set container [:containers peek]
+ set ns [$container getAuthoritativeNS]
+ set name ${ns}::$name
}
return $name
}
@@ -355,12 +343,9 @@
name
} {
if {[info exists partof_name]} {
- #puts stderr "QUALIFIER=[join [list $partof_name $name] ::]"
- #next [join [list $partof_name $name] ::]
next
} else {
set n [:get_fully_qualified_name $name]
-# puts stderr FINALNAME=$n
next $n
}
}
@@ -373,7 +358,6 @@
} {
set id_name $name
if {[info exists partof]} {
- #set name [join [list [$partof name] $name] ::]
set id_name ::[join [list [[$partof info class] get_tail_name $partof] $name] ::]
} else {
set name [:get_fully_qualified_name $name]
@@ -400,7 +384,7 @@
}
}
- # @object ::nx::doc::PartAttribute
+ # @class ::nx::doc::PartAttribute
#
# This special-purpose Attribute variant realises (1) a cumulative
# value management and (2) support for distinguishing between
@@ -415,13 +399,20 @@
# part_class is given, the values will be transformed accordingly
# before being pushed into the internal storage.
- ::nx::MetaSlot create PartAttribute -superclass ::nx::VariableSlot {
-
- # @param part_class
+ nx::MetaSlot create PartAttribute -superclass ::nx::VariableSlot {
+ # @.parameter part_class
#
# The property refers to a concrete subclass of Part which
# describes the parts being managed by the property.
- :property part_class:optional,class
+ :property part_class:optional,class {
+ :public method assign {domain prop value} {
+ set owningClass [[$domain info parent] info parent]
+ if {"::nx::doc::ContainerEntity" in [concat $owningClass [$owningClass info heritage]]} {
+ $value class mixin add ::nx::doc::ContainerEntity::Containable
+ }
+ next
+ }
+ }
:property scope
:property {pretty_name {[string totitle [string trimleft [namespace tail [current]] @]]}}
@@ -506,48 +497,57 @@
}
}
- Class create Entity {
+ #
+ # Sketch the entire hierarchy of documentation entities
+ # supported. Entity behaviour is defined further below
+ #
+
+ Class create Entity
+ Class create StructuredEntity -superclass Entity
+ Class create ContainerEntity -superclass StructuredEntity
+ Class create PartEntity -superclass Entity
+
+ Tag create @glossary -superclass Entity
+ Tag create @project -superclass ContainerEntity
+ Tag create @package -superclass ContainerEntity
+ QualifierTag create @command -superclass StructuredEntity
+ QualifierTag create @object -superclass StructuredEntity
+ QualifierTag create @class -superclass @object
+
+ PartTag create @method -superclass StructuredEntity
+ PartTag create @param -superclass PartEntity
+
+
+
+ Entity eval {
#
# Entity is the base class for the documentation classes
#
- # @param name
+ # @.parameter name
#
# gives you the name (i.e., the Nx object identifier) of the documented entity
:property name:any,required
- # every Entity must be created with a "@doc" value and can have
- # an optional initcmd
- #:method objectparameter args {
- #next [list [list @doc:optional __initcmd:initcmd,optional]]
- #}
-
:class property current_project:object,type=::nx::doc::@project,0..1
:public forward current_project [current] %method
:property partof:object,type=::nx::doc::StructuredEntity
:property part_attribute:object,type=::nx::doc::PartAttribute
-
+
+
+ :public method get_fqn_command_name {} {
+ return ${:name}
+ }
+
#
# TODO: the pdata/pinfo/validate combo only makes sense for
# entities which reflect Tcl program structures -> refactor into a
# dedicated PEntity class or the like
#
-
- :public method get_fqn_command_name {} {
- return ${:name}
- }
-
+
:property pdata
- :public method validate {} {
- if {[info exists :pdata] && \
- [:pinfo get -default complete status] ne "missing"} {
- if {[[:origin] as_list] eq ""} {
- :pinfo propagate status mismatch
- :pinfo lappend validation "Provide a short, summarising description!"
- }
- }
- }
+
:public method "pinfo get" {{-default ?} args} {
if {![info exists :pdata] || ![dict exists ${:pdata} {*}$args]} {
return $default;
@@ -618,7 +618,6 @@
set :default 0
}
- # :property @properties -class ::nx::doc::PartAttribute
:public method @property {props} {
foreach prop $props {
:@$prop
@@ -627,24 +626,9 @@
:property @use {
:public method assign {domain prop value} {
- # @command nx
- #
- # @use ::nsf::command
-
- # or
-
- # class.method {X foo}
- #
- # @use {Class foo}
- # @use object.method {Object foo}
-
lassign $value pathspec pathnames
if {$pathnames eq ""} {
set pathnames $pathspec
- # puts stderr PATH=[$domain get_upward_path \
- # -attribute {[:info class] tag}]
- # puts stderr "dict create {*}[$domain get_upward_path \
- # -attribute {[:info class] tag}]"
set pathspec [dict create {*}[$domain get_upward_path \
-attribute {[:info class] tag}]]
set pathspec [dict values $pathspec]
@@ -657,22 +641,19 @@
}
lassign $res pathspec pathnames
- #puts stderr "PATHSPEC $pathspec PATHNAMES $pathnames"
lassign [::nx::doc::Tag find $pathspec $pathnames] err res
if {$err} {
error "Generating an entity handle failed: $res"
}
- # puts stderr "NEXT $domain $prop $res"
next [list $domain $prop $res]
}
}
:public method origin {} {
if {[info exists :@use]} {
- # puts stderr ORIGIN(${:@use})=isobj-[::nsf::object::exists ${:@use}]
if {![::nsf::object::exists ${:@use}] || ![${:@use} info has type [:info class]]} {
- error "Referring to a non-existing doc entity or a doc entity of a different type."
+ return -code error "Referring to a non-existing doc entity or a doc entity of a different type."
}
return [${:@use} origin]
}
@@ -686,7 +667,7 @@
}
}
- # @method as_text
+ # @.method as_text
#
# text is used to access the content of doc of an Entity, and
# performs substitution on it. The substitution is not essential,
@@ -700,16 +681,23 @@
}
return [subst [join $doc " "]]
}
+
+ :public method error {msg} {
+ return -code error "[current].[uplevel 1 [list ::nsf::current method]](): $msg"
+ }
+
}
- Tag create @glossary -superclass Entity {
+
+ # @class @glossary
+ @glossary eval {
:property @pretty_name
:property @pretty_plural
:property @acronym
}
- Class create StructuredEntity -superclass Entity {
+ StructuredEntity eval {
:public method part_attributes {} {
set slots [:info lookup slots]
@@ -753,145 +741,87 @@
}
return $__owned_parts
}
-
- :public method validate {} {
- next
- dict for {s entities} [:owned_parts -where "!\${:@stashed}"] {
- foreach e $entities {
- # TODO: for now, it is sufficient to escape @use chains
- # here. review later ...
- if {![$e eval {info exists :@use}]} {
- $e [current method]
- }
- }
- }
- }
+
}
- Class create ContainerEntity -superclass StructuredEntity {
-
- Class create [current]::Resolvable {
- :class property container:object,type=[:info parent]
- :method get_fully_qualified_name {name} {
- set container [[current class] container]
- if {![string match "::*" $name]} {
-# puts -nonewline stderr "--- EXPANDING name $name"
- set name [$container @namespace]::$name
-# puts stderr " to name $name"
- }
- next $name
- }
- }
+ ContainerEntity eval {
Class create [current]::Containable {
- # TODO: check the interaction of required, per-object property and ::nsf::assertion
- #:object property container:object,type=[:info parent],required
- :property container:object,type=[:info parent]
:method create args {
- #
- # Note: preserve the container currently set at this callstack
- # level. [next] will cause the container to change if another
- # container entity is initialised in the following!
- #
- if {[[current class] eval {info exists :container}]} {
- set container [[current class] container]
- set obj [next]
- if {![$obj eval {info exists :partof}]} {
- $container register $obj
- }
- return $obj
- } else {
- next
+ #
+ # Note: preserve the container currently set at this callstack
+ # level. [next] might cause another container to be pushed on
+ # top.
+ #
+ set cont [:containers peek]
+ set obj [next]
+ if {![$obj eval {info exists :partof}] && $cont ne ""} {
+ $cont register $obj
}
+ return $obj
}
- :method create args {
- #
- # Note: preserve the container currently set at this callstack
- # level. [next] will cause the container to change if another
- # container entity is initialised in the following!
- #
- if {[info exists :container]} {
- set cont ${:container}
- set obj [next]
- if {![$obj eval {info exists :partof}]} {
- $cont register $obj
- }
- return $obj
- } else {
- next
- }
- }
-
}
+
# Note: The default "" corresponds to the top-level namespace "::"!
:property {@namespace ""}
-
+
:property -class ::nx::doc::PartAttribute @class {
:pretty_name "Class"
:pretty_plural "Classes"
- set :part_class ::nx::doc::@class
+ :part_class ::nx::doc::@class
}
:property -class ::nx::doc::PartAttribute @object {
:pretty_name "Object"
:pretty_plural "Objects"
- set :part_class ::nx::doc::@object
+ :part_class ::nx::doc::@object
}
:property -class ::nx::doc::PartAttribute @command {
:pretty_name "Command"
:pretty_plural "Commands"
- set :part_class ::nx::doc::@command
+ :part_class ::nx::doc::@command
}
- # :property @class:object,type=::nx::doc::@class,multivalued {
- # set :incremental 1
- # }
+ :public method register {containable:object,type=::nx::doc::Entity} {
+ set tag [[$containable info class] tag]
+ if {[:info lookup methods -source application "@$tag"] ne ""} {
+ :@$tag $containable
+ } elseif {[info exists :previous]} {
+ ${:previous} register $containable
+ }
+ }
- # :property @object:object,type=::nx::doc::@object,multivalued {
- # set :incremental 1
- # }
+ :property previous:object,type=[current]
- # :property @command:object,type=::nx::doc::@command,multivalued {
- # set :incremental 1
- # }
+ :public method announceAsContainer {tag:object,type=::nx::doc::Tag} {
+ $tag containers push [current]
+ }
- # :method init {} {
- # next
-
- # QualifierTag mixin add [current class]::Resolvable
- # [current class]::Resolvable container [current]
- # foreach {attr part_class} [:part_attributes] {
- # $part_class class mixin add [current class]::Containable
- # $part_class container [current]
- # }
- # }
-
- :method destroy {} {
- foreach {attr part_class} [:part_attributes] {
- #$part_class class mixin add [current class]::Containable
- if {[$part_class eval {info exists :container}] && \
- [$part_class container] eq [current]} {
- $part_class eval {unset :container}
- }
+ :public method getAuthoritativeNS {} {
+ if {${:@namespace} eq "" && [info exists :previous]} {
+ return ${:previous} [current method]
+ } else {
+ return ${:@namespace}; # defaults to top-level/global NS
}
- next
}
- :public method register {containable:object,type=::nx::doc::Entity} {
- set tag [[$containable info class] tag]
- if {[:info lookup methods -source application "@$tag"] ne ""} {
- :@$tag $containable
- }
+ :protected method init args {
+ next
+ :announceAsContainer [:info class]
}
+
}
- Tag create @project -superclass ContainerEntity {
+ @project eval {
- :property sandbox:object,type=::nx::doc::Sandbox
- :property sources
+ # / / / / / / / / / / / / / / / / / /
+ # Doc entity interface
+ # / / / / / / / / / / / / / / / / / /
+ :public property sandbox:object,type=::nx::doc::Sandbox
+
:property url
:property license
:property creationdate
@@ -901,7 +831,7 @@
:property depends:0..*,object,type=[current]
:property -class ::nx::doc::PartAttribute @glossary {
- set :part_class ::nx::doc::@glossary
+ :part_class ::nx::doc::@glossary
:public method get {domain prop} {
set l [next]
if {[$domain eval {info exists :depends}]} {
@@ -916,9 +846,167 @@
:property -class ::nx::doc::PartAttribute @package {
:pretty_name "Package"
:pretty_plural "Packages"
- set :part_class ::nx::doc::@package
+ :part_class ::nx::doc::@package
}
+ # / / / / / / / / / / / / / / / / / /
+ # Frontend interface
+ # / / / / / / / / / / / / / / / / / /
+
+ :private method "frontend unknown" {m args} {
+ :error "The NXDoc frontend '$m' is not available."
+ }
+
+ :public method read {frontend srcs cmds} -returns 0..*,object,type=::nx::doc::Entity {
+ :frontend $frontend $srcs $cmds
+ }
+
+ :public class method newFromSources {
+ {-frontend dc}
+ {-sandboxed:boolean 1}
+ -include
+ -exclude
+ sources
+ args
+ } {
+
+ #
+ # Action 1) Object creation
+ #
+ set newPrj [:new {*}$args]
+
+ #
+ # Action 2) Initialise a sandbox
+ #
+ set sandbox [$newPrj sandbox [Sandbox new -interp [expr {$sandboxed?[interp create]:""}]]]
+
+ #
+ # Action 3) Extract documentation sources (1pass)
+ #
+ $sandbox do [$newPrj get1PassScript $sources]
+ set sourceScripts [$sandbox getDocumentationScripts]
+
+ #
+ # Action 4) Determine command population through introspection (2pass)
+ #
+ $sandbox do [$newPrj get2PassScript $sourceScripts]
+
+
+ #
+ # Action 5) Applying command filters and obtain the workspace in
+ # terms of commands ...
+ #
+ if {[info exists include] && [info exists exclude]} {
+ $newPrj error "Inclusion and exclusion constraints are mutually exclusive!"
+ }
+
+ set nsFilters [list]
+ if {[info exists include] && $include ne ""} {
+ set nsFilters [list $include]
+ }
+ if {[info exists exclude] && $exclude ne ""} {
+ set nsFilters [list -not $exclude]
+ }
+
+ set commandsFound [$sandbox getCommandsFound {*}$nsFilters]
+
+ #
+ # Action 6) Load the requested frontend extension
+ #
+ package req nx::doc::$frontend
+
+ #
+ # Action 7) Have the intended documentation entities processed
+ # (documented, and meant to be visible)
+ #
+ # $newPrj readSrcs $frontend $sourceScripts $commandsFound
+ $newPrj read $frontend $sourceScripts $commandsFound
+
+ return $newPrj
+ }
+
+ :public method get1PassScript {sources} {
+ set 1pass "::nx::doc::__trace_pkg\n"
+ dict for {srcType items} $sources {
+ if {![llength $items]} continue;
+ switch -exact -- $srcType {
+ package {
+ foreach i $items {
+ append 1pass "package require $i\n"
+ }
+ }
+ source {
+ foreach i $items {
+ append 1pass "source $i\n"
+ }
+ }
+ eval {
+ error "Not implemented!"
+ # foreach i $items {
+ # append 1pass "info script X-EVAL\n"
+ # append 1pass "$i\n"
+ # }
+ # set srcType source
+ # set items X-EVAL
+ }
+ default {
+ error "Unsupported documentation source type '$srcType'"
+ }
+ }
+ ${:sandbox} permissive lappend $srcType $items
+ }
+ return $1pass
+ }
+
+ :public method get2PassScript {sourceScripts} {
+ set 2pass "::nx::doc::__init\n"
+ dict for {id info} $sourceScripts {
+ set block "%s"
+ dict with info {
+ # Available vars:
+ #
+ # package
+ # path
+ # script
+ # dependency
+ #
+ if {$dependency || ![info exists script]} continue;
+
+ if {[info exists package]} {
+ set fragment "
+ ::nx::doc::__cpackage push $package;
+ %s
+ ::nx::doc::__cpackage pop;
+ "
+ set block [format $block $fragment]
+ unset package
+ }
+
+ if {[info exists path]} {
+ set block [format $block "info script $path;\n%s"]
+ unset path
+ }
+ }
+ append 2pass [format $block $script]
+ unset script
+ }
+ return $2pass
+ }
+
+ # / / / / / / / / / / / / / / / / / /
+ # Backend interface
+ # / / / / / / / / / / / / / / / / / /
+
+ :public method write {{-format html} args} {
+ package req nx::doc::$format
+ $format run -project [current] {*}$args
+ }
+
+
+ # / / / / / / / / / / / / / / / / / /
+ # Lifecycling
+ # / / / / / / / / / / / / / / / / / /
+
:public method destroy {} {
#
# TODO: Using the auto-cleanup feature in [Test case ...] does
@@ -936,46 +1024,37 @@
:method init {} {
#
# TODO: the way we provide the project as a context object to
- # all entities is not easily restricted. Review later ...
- #
- :current_project [current]; # sets a per-class-object variable on Entity!
+ # all entities is not easily restricted. Review later (e.g.,
+ # relocate into the Validator) ...
+ #
+ [current class] containers reset
+ :current_project [current]; # side effect: sets a per-class-object variable on Entity!
next
}
+
}
- #
- # Now, define some kinds of documentation entities. The toplevel
- # docEntities are named objects in the ::nx::doc::entities namespace
- # to ease access to it.
- #
- # For now, we define here the following toplevel docEntities:
- #
- # - @package
- # - @command
- # - @object
- # - ...
- #
- # These can contain multiple parts.
- # - @method
- # - @param
- # - ...
- #
+
+ # TODO: decide how to deal with @package and @project names (don't
+ # need namespace delimiters!)
- Tag create @package -superclass ContainerEntity {
+ @package eval {
:property -class ::nx::doc::PartAttribute @require
- :property -class ::nx::doc::PartAttribute @version
+ :property @version
+ :property -class ::nx::doc::PartAttribute @author
+
}
- QualifierTag create @command -superclass StructuredEntity {
+ @command eval {
:property -class ::nx::doc::PartAttribute @parameter {
- set :part_class ::nx::doc::@param
+ :part_class ::nx::doc::@param
}
:property -class ::nx::doc::PartAttribute @return {
:method require_part {domain prop value} {
set value [expr {![string match ":*" $value] ? "__out__: $value": "__out__$value"}]
next [list $domain $prop $value]
}
- set :part_class ::nx::doc::@param
+ :part_class ::nx::doc::@param
}
:public forward @sub-command %self @command
@@ -984,103 +1063,48 @@
:pretty_name "Subcommand"
:pretty_plural "Subcommands"
:public method id {domain prop value} {
- # TODO: [${:part_class}] resolves to the property slot
- # object, not the global @command object. is this intended, in
- # line with the intended semantics?
return [${:part_class} [current method] \
-partof_name [$domain name] \
-scope ${:scope} -- $value]
}
- set :part_class ::nx::doc::@command
+ :part_class ::nx::doc::@command
}
-
- :public method validate {} {
- if {[info exists :pdata] && \
- [:pinfo get -default complete status] ne "missing"} {
-
- if {![info exists :@command]} {
- set params [list]
- set param_names [list]
- if {[info exists :@parameter]} {
- foreach p [:@parameter] {
- set value [$p name]
- lappend param_names $value
- if {[$p eval {info exists :default}] || $value eq "args" } {
- set value "?$value?"
- }
- lappend params $value
- }
- }
-
- set ps [:pinfo get -default "" bundle parameter]
- dict for {actualparam paraminfo} $ps {
- if {$actualparam ni $param_names} {
- set p [:@parameter $actualparam]
- $p pdata [lappend paraminfo status missing]
- }
- }
- }
-
- if {![:pinfo exists bundle parametersyntax]} {
- :pinfo set bundle parametersyntax $params
- }
-
- # Note: [next] will cause the missing parameter created to
- # be validated and will have the appropriate status
- # propagated upstream!
- next
- }
- }
}
- QualifierTag create @object \
- -superclass StructuredEntity \
- -mixin ContainerEntity::Containable {
+ @object eval {
+
+ :public forward @object %self @child-object
+
+ :property -class ::nx::doc::PartAttribute @child-object {
+ :part_class ::nx::doc::@object
+ :public method id {domain prop value} {
+ return [${:part_class} id [join [list [$domain name] $value] ::]]
+ }
+
+ }
+
+ :public forward @class %self @child-class
+
+ :property -class ::nx::doc::PartAttribute @child-class {
+ :part_class ::nx::doc::@class
+ :public method id {domain prop value} {
+ return [${:part_class} id [join [list [$domain name] $value] ::]]
+ }
+ }
- :property -class ::nx::doc::PartAttribute @author
-
- :public forward @object %self @child-object
-
- :property -class ::nx::doc::PartAttribute @child-object {
- set :part_class ::nx::doc::@object
- :public method id {domain prop value} {
-# puts stderr "CHILD-OBJECT: [current args]"
- # if {![info exists :part_class]} {
- # error "Requested id generation from a simple part property!"
- # }
- return [${:part_class} id [join [list [$domain name] $value] ::]]
-# return [${:part_class} id -partof_name [$domain name] -scope ${:scope} $value]
- }
-
- }
-
- :public forward @class %self @child-class
-
- :property -class ::nx::doc::PartAttribute @child-class {
- set :part_class ::nx::doc::@class
- :public method id {domain prop value} {
- #puts stderr "CHILD-CLASS: [current args]"
- # if {![info exists :part_class]} {
- # error "Requested id generation from a simple part property!"
- # }
- return [${:part_class} id [join [list [$domain name] $value] ::]]
- #return [${:part_class} id -partof_name [$domain name] -scope ${:scope} $value]
- }
- }
-
:public forward @method %self @object-method
:property -class ::nx::doc::PartAttribute @object-method {
:pretty_name "Object method"
:pretty_plural "Object methods"
- set :part_class ::nx::doc::@method
+ :part_class ::nx::doc::@method
}
:public forward @property %self @object-property
#:forward @param %self @object-param
:property -class ::nx::doc::PartAttribute @object-property {
- set :part_class ::nx::doc::@param
+ :part_class ::nx::doc::@param
}
:method undocumented {} {
@@ -1097,8 +1121,7 @@
}
}
- QualifierTag create @class \
- -superclass @object {
+ @class eval {
:property -class ::nx::doc::PartAttribute @superclass
@@ -1107,7 +1130,7 @@
:property -class ::nx::doc::PartAttribute @class-property {
:pretty_name "Per-class attribute"
:pretty_plural "Per-class attributes"
- set :part_class ::nx::doc::@param
+ :part_class ::nx::doc::@param
}
:public forward @class-object-method %self @object-method
@@ -1118,15 +1141,15 @@
:property -class ::nx::doc::PartAttribute @class-hook {
:pretty_name "Hook method"
:pretty_plural "Hook methods"
- set :part_class ::nx::doc::@method
+ :part_class ::nx::doc::@method
}
:public forward @method %self @class-method
:property -class ::nx::doc::PartAttribute @class-method {
:pretty_name "Provided method"
:pretty_plural "Provided methods"
- set :part_class ::nx::doc::@method
+ :part_class ::nx::doc::@method
:method require_part {domain prop value} {
# TODO: verify whether these scoping checks are sufficient
# and/or generalisable: For instance, is the scope
@@ -1142,50 +1165,29 @@
next
}
}
- :public method validate {} {
- next
- #
- # TODO: Certain metadata could also be valid in "missing"
- # state, e.g., paramtersyntax? Re-arrange later ...
- #
- if {[info exists :pdata] &&
- [:pinfo get -default complete status] ne "missing"} {
- #
- # Note: Some metadata on classes cannot be retrieved from
- # within the tracers, as they might not be set local to the
- # class definition. Hence, we gather them at this point.
- #
- set prj [:current_project]
- set box [$prj sandbox]
- set statement [list ::nsf::dispatch ${:name} \
- ::nsf::methods::class::info::objectparameter \
- parametersyntax]
- :pinfo set bundle parametersyntax [$box eval $statement]
- }
- }
}
- Class create PartEntity -superclass Entity {
+
+ PartEntity eval {
:property partof:object,type=::nx::doc::StructuredEntity,required
:property part_attribute:object,type=::nx::doc::PartAttribute,required
}
- # @object ::nx::doc::@method
+ # @class ::nx::doc::@method
#
# "@method" is a named entity, which is part of some other
# docEntity (a class or an object). We might be able to use the
# "use" parameter for registered aliases to be able to refer to the
# documentation of the original method.
#
- PartTag create @method \
- -superclass StructuredEntity {
+ @method eval {
:property -class ::nx::doc::SwitchAttribute @syshook:boolean {
set :default 0
}
:property -class ::nx::doc::PartAttribute @parameter {
- set :part_class ::nx::doc::@param
+ :part_class ::nx::doc::@param
}
:property -class ::nx::doc::PartAttribute @return {
#
@@ -1200,7 +1202,7 @@
set value [expr {![string match ":*" $value] ? "__out__: $value": "__out__$value"}]
next [list $domain $prop $value]
}
- set :part_class ::nx::doc::@param
+ :part_class ::nx::doc::@param
}
:public class method new {
@@ -1224,7 +1226,7 @@
:public forward @sub-method %self @method
:property -class ::nx::doc::PartAttribute @method {
- set :part_class ::nx::doc::@method
+ :part_class ::nx::doc::@method
:public method id {domain prop name} {
# TODO: ${:part_class} resolves to the local slot
# [current], rather than ::nx::doc::@method. Why?
@@ -1242,62 +1244,6 @@
return ::nsf::${scope}::[string trimleft [[:partof] name] :]::${:name}
}
- # @method->validate()
- :public method validate {} {
- set partof [:get_owning_partof]
- if {[info exists :pdata] &&
- [:pinfo get -default complete status] ne "missing"} {
- #
- # Note: Some information on methods cannot be retrieved from
- # within the tracers as they might not be set local to the
- # method definition. Hence, we gather them at this point. I
- # will review whether there is a more appropriate way of
- # dealing with this issue ...
- #
- set prj [:current_project]
- set box [$prj sandbox]
- set obj [$partof name]
-
- if {[:pinfo exists bundle handle]} {
- set handle [:pinfo get bundle handle]
- :pinfo set bundle redefine-protected [$box eval [list ::nsf::method::property $obj $handle redefine-protected]]
- :pinfo set bundle call-protected [$box eval [list ::nsf::method::property $obj $handle call-protected]]
- }
-
- set params [list]
- set param_names [list]
- if {[info exists :@parameter]} {
- foreach p [:@parameter] {
- set value [$p name]
- lappend param_names $value
- if {[$p eval {info exists :default}] || $value eq "args" } {
- set value "?$value?"
- }
- lappend params $value
- }
- }
-
- dict for {actualparam paraminfo} [:pinfo get -default "" bundle parameter] {
- if {$actualparam ni $param_names} {
- set p [:@parameter $actualparam]
- $p pdata [lappend paraminfo status missing]
- }
- }
-
- if {![:pinfo exists bundle parametersyntax]} {
- :pinfo set bundle parametersyntax $params
- }
-
- # Note: [next] will cause the missing parameter created to
- # be validated and will have the appropriate status
- # upstream!
- next
- } else {
- # To realise upward status propagation for submethods, use:
- # ${:partof} pinfo propagate status mismatch
- $partof pinfo propagate status mismatch
- }
- }
:public method get_sub_methods {} {
if {[info exists :@method]} {
@@ -1337,36 +1283,20 @@
}; # @method
- # PartTag create @subcommand -superclass {Part @command}
- # PartTag create @subcommand -superclass {Part @command}
-
- # @object ::nx::doc::@param
+ # @class ::nx::doc::@param
#
# The entity type "@param" represents the documentation unit
# for several parameter types, e.g., object, method, and
# command parameters.
#
- PartTag create @param \
- -superclass PartEntity {
-
- #:property spec
+ @param eval {
:property -class ::nx::doc::PartAttribute @spec
:property default
:public class method id {partof_name scope name} {
next [list [:get_unqualified_name ${partof_name}] $scope $name]
}
-
- # :class method id {partof_name name} {
- # # The method contains the parameter-specific name production rules.
- # #
- # # @param partof Refers to the entity object which contains this part
- # # @param name Stores the name of the documented parameter
-
- # set partof_fragment [:get_unqualified_name ${partof_name}]
- # return [:root_namespace]::${:tag}::${partof_fragment}::${name}
- # }
-
+
# @class-object-method new
#
# The per-object method refinement indirects entity creation
@@ -1403,46 +1333,6 @@
next
}
}
-
-
- # @param->validate()
- :public method validate {} {
- #
- # TODO: For now, we escape from @param validaton on command
- # parameters. There is no equivalent to [info parameter]
- # available, so we would need to cook a substitute based on
- # the parametersyntax. Review later ...
- #
- if {${:name} eq "__out__" && \
- [${:partof} info has type ::nx::doc::@command]} return;
-
- #
- # Here, we escape from any parameter verification for
- # parameters on forwards & alias, as there is no basis for
- # comparison!
- #
- if {[${:partof} info has type ::nx::doc::@method] && \
- [${:partof} pinfo get bundle type] in [list forward alias]} {
- dict set :pdata status ""
- return;
- }
-
- if {[info exists :pdata] && \
- [:pinfo get -default complete status] ne "missing"} {
-
- # valid for both object and method parameters
- set pspec [:pinfo get -default "" bundle spec]
- if {[info exists :spec] && \
- ${:spec} ne $pspec} {
- :pinfo propagate status mismatch
- :pinfo lappend validation "Specification mismatch. Expected: \
- '${:spec}' Got: '$pspec'."
- }
- next
- } else {
- ${:partof} pinfo propagate status mismatch
- }
- }
}
#
@@ -1667,7 +1557,6 @@
return $line
}
-
:method as_list {} {
set preprocessed [list]
set is_code_block 0
@@ -1677,11 +1566,8 @@
set is_code_block [expr {!$is_code_block}]
append line \n
} elseif {${is_code_block}} {
- # set line [:map $line unescape]
append line \n
} else {
- # set line [:map $line sub]
- # set line [:map $line unescape]
set line [string trimleft $line]
if {$line eq {}} {
set line "\n\n"
@@ -1696,6 +1582,7 @@
set preprocessed [join [:as_list] " "]
set preprocessed [:map $preprocessed]
set preprocessed [:unescape $preprocessed]
+
# TODO: For now, we take a passive approach: Some docstrings
# might fail because they contain substitution characters
# ($,[]); see nx.tcl. The same goes for legacy xodoc docstrings,
@@ -1704,6 +1591,7 @@
# escape/unescape evaluation chars; at the same time, we can't
# distinguish errors on unintended and intended evaluations.
# ...
+
if {[catch {set preprocessed [subst $preprocessed]} msg]} {
puts stderr SELF=[current]
puts stderr MSG=$msg
@@ -1716,6 +1604,7 @@
}
#
+ # NXDoc backend infrastructure:
# A Renderer base class ...
#
Class create Renderer -superclass MixinLayer {
@@ -1791,7 +1680,7 @@
}
:method readAsset {assetName} {
- set assetDir [find_asset_path]
+ set assetDir [findAssetPath]
set assetPath [file join $assetDir $assetName]
return [:read $assetPath]
}
@@ -1909,9 +1798,6 @@
{-theme yuidoc}
args
} {
- # TODO: Relocate trigger validation!
- $project validate
- # --
:apply
:current_theme $theme
:layout $layout $project $theme {*}$args
@@ -1966,49 +1852,84 @@
return $value
}
- :protected property {current_packages "*"}
- :property {permissive_pkgs:1..* "*"} {
- set :incremental 1
+ :protected property {current_packages ""}
+
+ :public method "permissive lappend" {type value} {
+ set d [lindex [current methodpath] 0]
+ dict [current method] :$d $type {*}$value
}
+ :public method "permissive get" {type} {
+ if {![info exists :permissive]} {
+ set :permissive [dict create]
+ }
+ dict [current method] ${:permissive} $type
+ }
+
+ :public method getDocumentationScripts {} {
+ if {[info exists :dSources]} {
+ return ${:dSources}
+ }
+ }
+
#
# some callbacks invoked from within the sandbox interp
#
-
:public method "cpackage pop" {} {
set :current_packages [lrange ${:current_packages} 0 end-1]
}
:public method "cpackage push" {p} {
- lappend :current_packages [string tolower $p]
+ lappend :current_packages $p
}
:public method "cpackage top" {} {
- return [lindex ${:current_packages} end]
+ if {[info exists :current_packages]} {
+ return [lindex ${:current_packages} end]
+ }
}
- :public method at_source {filepath} {
+ :public method at_source {filePath} {
set cpackage [:cpackage top]
- if {$cpackage in ${:permissive_pkgs}} {
- lappend :source $cpackage $filepath
+ set fh [open $filePath r]
+ set script [read $fh]
+ catch {close $fh}
+
+ set info [dict create]
+ set key ""
+ if {$cpackage ne ""} {
+ set key "$cpackage."
+ dict set info package $cpackage
+ dict set info dependency [expr {$cpackage ni [:permissive get package]}]
} else {
- dict set :deps $filepath $cpackage
+ # TODO: dict set info dependency [expr {$filePath ni [:permissive get source]}]
+ dict set info dependency 1
}
+ dict set info path $filePath
+ dict set info script $script
+
+ dict set :dSources $key$filePath $info
}
:public method at_load {filepath} {
set cpackage [:cpackage top]
- if {$cpackage ne ${:permissive_pkgs}} {
- dict set :deps $filepath $cpackage
+ set key ""
+ set info [dict create]
+ dict set info path $filepath
+ if {$cpackage ne ""} {
+ set key "$cpackage."
+ dict set info package $cpackage
+ dict set info dependency [expr {$cpackage ni [:permissive get package]}]
+ } else {
+ # TODO: dict set info dependency [expr {$filePath ni [:permissive get source]}]
+ dict set info dependency 1
}
- }
+ dict set :dSources $key$filepath $info
+ }
:public method at_register_package {pkg_name version} {
dict set :registered_packages $pkg_name version $version
}
-# :public method at_deregister_package {} {
-# set :current_packages [lrange ${:current_packages} 0 end-1]
-# }
- # [list ->status:in,arg=complete|missing|prototype|mismatch,slot=[current] missing]
+
:public method at_register_command [list \
name:fqn,slot=[current] \
->cmdtype:in,arg=@object|@class|@command|@method,slot=[current] \
@@ -2018,32 +1939,31 @@
->docstring:optional,slot=[current] \
->bundle
] {
- # peek the currently processed package (if any)
set storable_vars [info vars >*]
- # set cpackage [lindex ${:current_packages} end]
+ foreach svar $storable_vars {
+ dict set :registered_commands $name [string trimleft $svar >] [set $svar]
+ }
+
+ # peek the currently processed package (if any)
set cpackage [:cpackage top]
- if {$cpackage in ${:permissive_pkgs}} {
+ if {$cpackage ne ""} {
dict set :registered_commands $name package $cpackage
- foreach svar $storable_vars {
- dict set :registered_commands $name [string trimleft $svar >] [set $svar]
- }
+ dict set :registered_commands $name dependency \
+ [expr {$cpackage ni [:permissive get package]}]
+ } else {
+ # FIXME dict set :registered_commands $name dependency \
+ # [expr {$source ni [:permissive get source]}]
+ dict set :registered_commands $name dependency 1
}
}
:public method at_deregister_command [list name:fqn,slot=[current]] {
- set cpackage [:cpackage top]
- if {$cpackage in ${:permissive_pkgs}} {
- dict unset :registered_commands $name
- }
+ dict unset :registered_commands $name
}
:public method init args {
:do {
-
#
- # hide selected built-in Tcl commands and put simple
- # forwarding proxies in place ...
- #
# TODO: refactor the proxy handling ...
#
interp hide "" proc
@@ -2054,14 +1974,13 @@
interp hide "" auto_import
interp invokehidden "" proc ::proc args {
- #set ns [uplevel [list interp invokehidden "" namespace current]]
uplevel [list interp invokehidden "" proc {*}$args]
}
proc ::namespace args {
- #set ns [uplevel [list interp invokehidden "" namespace current]]
- #interp invokehidden "" -namespace $ns namespace {*}$args
+ # set ns [uplevel [list interp invokehidden "" namespace current]]
uplevel [list interp invokehidden "" namespace {*}$args]
+ # interp invokehidden {} -namespace $ns namespace {*}$args
}
proc ::source args {
@@ -2131,7 +2050,6 @@
proc list_commands {{parent ""}} {
set ns [dict create]
- #set cmds [string trim "[join [info commands ${parent}::*] \" 0 \"] 0" 0]
#
# Note: We trigger a [namespace import] for the
# currently processed namespace before requesting the
@@ -2140,14 +2058,12 @@
# i.e. after having computed the [info
# commands] snapshot!
#
-# namespace eval ::nx::doc::__x [list namespace import -force ${parent}::*]
+
set cmds [info commands ${parent}::*]
set exported [list]
foreach cmd $cmds {
dict set ns ::[string trimleft $parent :] $cmd [is_exported $cmd]
-
-#[expr {[info commands ::nx::doc::__x::[namespace tail $cmd]] ne ""}]
}
foreach nsp [namespace children ${parent}::] {
@@ -2164,8 +2080,6 @@
#
# pre-state
#
- # set pre_loaded [dict values \
- # [dict create {*}[concat {*}[info loaded ""]]]]
set pre_loaded [lreverse [concat {*}[info loaded ""]]]
set pre [::nx::doc::list_commands]
set pre_commands [dict create {*}[concat {*}[dict values $pre]]]
@@ -2176,7 +2090,6 @@
#
# post-state
#
- #set post_loaded [dict create {*}[concat {*}[info loaded ""]]]
set post_loaded [lreverse [concat {*}[info loaded ""]]]
set post [::nx::doc::list_commands]
set post_commands [dict create {*}[concat {*}[dict values $post]]]
@@ -2192,10 +2105,6 @@
set delta_pkg [dict remove \
[dict create {*}$post_loaded] \
[dict keys [dict create {*}$pre_loaded]]]
-
- #puts stderr "DELTAS pkg $delta_pkg"
- #puts stderr "DELTAS namespace $delta_namespaces"
- #puts stderr "DELTAS commands $delta_commands"
lassign $delta_pkg pkg_name filepath
set filepath [file normalize $filepath]
@@ -2207,8 +2116,9 @@
# parametersyntax prints.
if {[info commands ::nsf::objectsystem::create] ne "" && \
[::nsf::configure objectsystem] eq ""} {
- set rootclass ::nx::doc::_%&obj
- set rootmclass ::nx::doc::_%&cls
+ namespace eval ::nx::doc::_%& {}
+ set rootclass ::nx::doc::_%&::obj
+ set rootmclass ::nx::doc::_%&::cls
::nsf::objectsystem::create $rootclass $rootmclass
} else {
lassign {*}[::nsf::configure objectsystem] rootclass rootmclass
@@ -2268,10 +2178,6 @@
}
}
interp invokehidden "" -namespace $ns package $subcmd {*}$args
- # uplevel [list interp invokehidden "" package $subcmd {*}$args]
-# if {$was_registered} {
-# ::nx::doc::__at_deregister_package
-# }
}
#
@@ -2303,7 +2209,7 @@
# helper objsys to retrieve command parameter specs and
# parametersyntax prints.
#
- if {$rootclass ne "::nx::doc::_%&obj"} {
+ if {$rootclass ne "::nx::doc::_%&::obj"} {
::nsf::configure keepinitcmd true;
@@ -2333,7 +2239,6 @@
{*}[expr {[::nsf::var::exists $obj __initcmd] && [::nsf::var::set $obj __initcmd] ne ""?[list ->docstring [::nsf::var::set $obj __initcmd]]:[list]}]
return $obj
}
- # ::nsf::relation $rootmclass class-mixin ${::nx::doc::rootns}::__Tracer
if {[info commands "::nx::Object"] ne ""} {
$rootmclass $sysmeths(-class.create) ${::nx::doc::rootns}::__ObjTracer
@@ -2481,35 +2386,6 @@
return $handle
}
- # if {[$object info method type ${:name}] eq "forward"} {
- # set cmd ""
- # foreach w [lrange [$object info method definition ${:name}] 2 end] {
- # if {[string match ::* $w]} {
- # set cmd $w
- # break
- # }
- # }
- # if {$cmd ne "" && [string match ::nsf::* $cmd]} {
- # # TODO: we assume here, the cmd is a primitive
- # # command and we intend only to handle cases from
- # # predefined or xotcl2. Make sure this is working
- # # reasonable for other cases, such as forwards to
- # # other objects, as well
- # if {![catch {set actualParams [::nx::Object info method parameter $cmd]}]} {
- # # drop usual object
- # set actualParams [lrange $actualParams 1 end]
- # # drop per object ; TODO: always?
- # if {[lindex $actualParams 0] eq "-per-object"} {
- # set actualParams [lrange $actualParams 1 end]
- # set syntax [lrange [::nx::Object info method parametersyntax $cmd] 2 end]
- # } else {
- # set syntax [lrange [::nx::Object info method parametersyntax $cmd] 1 end]
- # }
- # }
- # }
- # }
-
-
rename ::nsf::method::forward ::nsf::_%&forward
::interp invokehidden "" proc ::nsf::method::forward {
args
@@ -2660,28 +2536,6 @@
:public property registered_commands
- :public method getCompanions {identifiers} {
- set scripts [list]
- dict for {source pkg} $identifiers {
- set rootname [file rootname $source]
- set dir [file dirname $source]
- set companion $rootname.nxd
- set srcs [dict create {*}"[join [list $source $rootname.nxd [file join $dir $pkg].nxd] " _ "] _"]
- foreach src [dict keys $srcs] {
- if {![file isfile $src] || ![file readable $src]} continue;
- if {[file extension $src] eq [info sharedlibextension]} continue;
- set fh [open $src r]
- if {[catch {lappend scripts [read $fh]} msg]} {
- catch {close $fh}
- :log "error reading the file '$thing', i.e.: '$msg'"
- }
- catch {close $fh}
- }
- }
- return $scripts
-
- }
-
:public method get_companions {} {
set companions [dict create]
dict for {cmd props} ${:registered_commands} {
@@ -2693,8 +2547,9 @@
return [:getCompanions $companions]
}
- :public method get_registered_commands {
- -exported:switch
+ :public method getCommandsFound {
+ -exported:boolean
+ -imported:switch
-types
-not:switch
nspatterns:optional
@@ -2709,31 +2564,26 @@
dict filter ${:registered_commands} script {cmd props} {
dict with props {
expr {[expr {[info exists nspatterns]?[expr {[regexp -- $nspatterns $cmd _] != $not}]:1}] && \
- [expr {$exported?[expr {$nsexported == $exported}]:1}] && \
- [expr {[info exists types]?[expr {$cmdtype in $types}]:1}]}
+ [expr {[info exists exported]?[expr {$nsexported == $exported}]:1}] && \
+ [expr {$nsimported == $imported}] && \
+ [expr {[info exists types]?[expr {$cmdtype in $types}]:1}]}
}
}
- #lsearch -inline -all -regexp $additions {^::nsf::[^\:]+$}]
}
-
-# :forward do ::interp %1 {% set :interp}
:public method do {script} {
::interp eval ${:interp} $script
}
:public method destroy {} {
- #
- # TODO: Why am I called twice in doc.test? Because of the test
- # enviroment (the auto-cleanup feature?)
- #
- # puts stderr "SELF [current object] interp ${:interp}"
- # ::nsf::__db_show_stack
if {${:interp} ne ""} {
if {[interp exists ${:interp}]} {
interp delete ${:interp}
}
} else {
+ #
+ # TODO: complete the coverage of the cleanup ...
+ #
:do {
if {[info commands ::nsf::configure] ne ""} {
::nsf::configure keepinitcmd false;
@@ -2777,362 +2627,82 @@
}
-namespace eval ::nx::doc::xodoc {
- namespace import -force ::nx::*
- namespace import -force ::nx::doc::*
- # xodoc -> nxdoc
- # - - - - - - - - - - - - - - - -
- # MetadataToken Entity
- # FileToken @package
- # PackageToken @package
- # ConstraintToken n/a
- # MethodToken n/a
- # ProcToken @method (scope = object)
- # InstprocToken @method (scope = class)
- # ObjToken @object
- # ClassToken @class
- # MetaClassToken n/a
-
- Class create MetadataToken {
- :class property analyzer
- :public forward analyzer [current] %method
- :method as {partof:object,type=::nx::doc::StructuredEntity} \
- -returns object,type=::nx::doc::Entity {
- error "Subclass responsibility"
- }
- :public method emit {partof:object,type=::nx::doc::StructuredEntity} \
- -returns object,type=::nx::doc::Entity {
- set entity [:as $partof]
- set props [:get_properties]
- if {[dict exists $props description]} {
- $entity @doc [dict get $props description]
- }
- return $entity
- }
- :method get_properties {} {
- if {[info exists :properties]} {
- set props [dict create]
- foreach p ${:properties} {
- if {[info exists :$p]} {
- dict set props [string tolower $p] \
- [:format [set :$p]]
- }
- }
- return $props
- }
- }
- :method format {value} {
- #
- # 1. replace @-prefixed tags etc.
- #
- set value [[:analyzer] replaceFormatTags $value]
-
- #
- # 2. escape Tcl evaluation chars in code listings
- #
- set value [string map {
- "\\" "\\\\"
- "{" "\\{"
- "}" "\\}"
- "\"" "\\\""
- "[" "\\["
- "]" "\\]"
- "$" "\\$"
- } $value]
-
- #
- # 3. box the prop value in a list (this avoids unwanted
- # interactions with the line-by-line as_text post-processor)
- #
- return [list $value]
- }
- }
+#
+# Validator
+#
+namespace eval ::nx::doc {
- Class create PackageToken -superclass MetadataToken
- Class create FileToken -superclass MetadataToken {
- :method as {partof:object,type=::nx::doc::StructuredEntity} \
- -returns object,type=::nx::doc::Entity {
- #
- # TODO: Where to retrieve the package name from?
- #
- return [@package new -name XOTcl]
- }
- :public method emit {partof:object,type=::nx::doc::StructuredEntity} \
- -returns object,type=::nx::doc::Entity {
- set entity [next]
- set props [dict remove [:get_properties] description]
- dict for {prop value} $props {
- $entity @doc add "
$prop [join $value]" end
- }
- $entity @namespace [[$entity current_project] @namespace]
- return $entity
- }
- }
-
- #
- # Note: For whatever reason, InstprocToken is provided but never
- # used, at least in XOTcl-langRef. while most probably due to a lack
- # of attention or a silent revocation of a design decision in xodoc,
- # it forces us into code replication for differentiating the
- # per-class and per-object scopes ... in xodoc, these scopes are
- # double-encoded, both in proper token subclassifications as well as
- # aggregation properties: procList, instprocList ... well, I will
- # have to live with it.
- #
+ MixinLayer create @project::Validator -prefix ::nx::doc {
- Class create MethodToken -superclass MetadataToken
-
- Class create ProcToken -superclass MethodToken {
- :method as {scope partof:object,type=::nx::doc::StructuredEntity} \
- -returns object,type=::nx::doc::Entity {
- return [$partof @${scope}-method [:name]]
- }
- :public method emit {scope partof:object,type=::nx::doc::StructuredEntity} {
- set entity [:as $scope $partof]
- set props [:get_properties]
- if {[dict exists $props description]} {
- $entity @doc [dict get $props description]
- }
- if {[dict exists $props return]} {
- $entity @return [dict get $props return]
- }
- return $entity
+ namespace eval [[current] info parent] {
+ namespace import -force ::nx::doc::*
}
- }
-
- Class create InstprocToken -superclass MethodToken
-
- Class create ObjToken -superclass MetadataToken {
- :method as {partof:object,type=::nx::doc::ContainerEntity} \
- -returns object,type=::nx::doc::Entity {
- return [@object new -name [:name]]
- }
- :public method emit {entity:object,type=::nx::doc::Entity} \
- -returns object,type=::nx::doc::Entity {
- set entity [next]
- foreach p [:procList] {
- $p emit object $entity
- }
- return $entity
- }
- }
-
- Class create ClassToken -superclass ObjToken {
- :method as {partof:object,type=::nx::doc::ContainerEntity} \
- -returns object,type=::nx::doc::Entity {
- return [@class new -name [:name]]
- }
- :public method emit {entity:object,type=::nx::doc::Entity} \
- -returns object,type=::nx::doc::Entity {
- set entity [next]
- foreach iproc [:instprocList] {
- $iproc emit class $entity
- }
- return $entity
- }
- }
-
- Class create MetaClassToken -superclass ClassToken
-
- namespace export MetadataToken FileToken MethodToken ProcToken \
- InstprocToken ObjToken ClassToken MetaClassToken
-}
-
-#
-# post processor for initcmds and method bodies
-#
-namespace eval ::nx {
-
- namespace import -force ::nx::doc::*
-
- MixinLayer create processor -prefix ::nx::doc {
- namespace eval ::nx::doc {
- namespace eval ::nx::doc::MixinLayer {
+ namespace eval ::nx::doc::MixinLayer {
namespace export Mixin
- }
- namespace import -force ::nx::doc::MixinLayer::*
- namespace export Mixin
- }
+ }
- namespace import -force ::nx::doc::*
-
- Mixin create [current]::Entity {
- :public method init args {
- next
- set prj [:current_project]
- if {$prj ne ""} {
- set box [$prj sandbox]
- set cmdname [:get_fqn_command_name]
- if {[$box eval {info exists :registered_commands}] && \
- [$box eval [concat dict exists \${:registered_commands} $cmdname]]} {
- :pdata [$box eval [concat dict get \${:registered_commands} $cmdname]]
- }
- }
- [[current class] info parent] at_processed [current]
- }
- }
+ namespace import -force ::nx::doc::MixinLayer::*
- Mixin create [current]::ContainerEntity {
- :method init {} {
- next
- ::nx::doc::QualifierTag mixin add ::nx::doc::ContainerEntity::Resolvable
- ::nx::doc::ContainerEntity::Resolvable container [current]
- foreach {attr part_class} [:part_attributes] {
- $part_class class mixin add ::nx::doc::ContainerEntity::Containable
- $part_class container [current]
- }
- }
- }
+ :public method read {frontend srcs cmds} {
+ set box ${:sandbox}
+ [current class] apply
+ set provided_entities [next]
- Mixin create [current]::@package {
- :public method init args {
- next
- set prj [:current_project]
- if {$prj ne ""} {
- set box [$prj sandbox]
- if {[$box eval [concat dict exists \${:registered_packages} ${:name}]]} {
- :pdata [$box eval [concat dict get \${:registered_packages} ${:name}]]
- }
- }
- }
- }
+ #
+ # trigger the validation (top-down)
+ #
+ :validate
+ # --
- Mixin create [current]::@method -superclass [current]::Entity {
- :method init args {
- next
- set scope [expr {[${:part_attribute} scope] eq "class"?"class":"object"}]
- set obj [:get_owning_object]
- set method_name [:get_combined name]
- set prj [:current_project]
- if {$prj ne ""} {
- set box [$prj sandbox]
- set script "if {\[::nsf::object::exists $obj\]} {array set \"\" \[$obj eval {:__resolve_method_path \"$method_name\"}\]; ::nsf::dispatch \$(object) ::nsf::methods::${scope}::info::method handle \$(methodName)}"
- set cmdname [$box do $script]
- if {$cmdname ne "" && [$box eval [concat dict exists \${:registered_commands} $cmdname]]} {
- :pdata [$box eval [concat dict get \${:registered_commands} $cmdname]]
- }
+ [current class] revoke
+ [:info class] containers reset [current]
+ #
+ # TODO: is_validated to later to become a derived/computed
+ # property ... for now, we just need to escape from setting
+ # validation-related info in non-validated projects!
+ #
+ :is_validated 1; # is_validated = 1
+
+ set present_entities [::nx::doc::filtered $provided_entities {[[:origin] eval {info exists :pdata}]}]
+
+ # / / / / / / / / / / / / / / / / / / / / /
+ # Handling "missing" documentation entities
+ #
+ # 1) Add support for "generated" packages and their validation.
+ #
+
+ set pkgMap [dict create]
+ if {[$box eval {info exists :registered_packages}] && [$box permissive get package] ne ""} {
+ set generatedPackages [$box eval {set :registered_packages}]
+ set providedPackages [@package info instances]
+ set presentPackages [::nx::doc::filtered $providedPackages {[[:origin] eval {info exists :pdata}]}]
+ foreach presPkgName $presentPackages {
+ set generatedPackages [dict remove $generatedPackages [$presPkgName name]]
+ dict set pkgMap [$presPkgName name] $presPkgName
}
-
- }
- }
-
- Mixin create [current]::@param -superclass [current]::Entity {
- :public method init args {
- next
- if {${:name} eq "__out__"} {
- if {[${:partof} pinfo exists bundle returns]} {
- :pdata [list bundle [list spec [${:partof} pinfo get bundle returns]]]
+ set permissivePkgs [$box permissive get package]
+ dict for {genPkgName info} $generatedPackages {
+ if {$genPkgName in $permissivePkgs} {
+ dict with info {
+ set pkgObj [@package new -name $genPkgName -@version $version]
+ $pkgObj pdata [list status missing]
+ }
+ dict set pkgMap $genPkgName $pkgObj
+ }
}
- } elseif {[${:partof} pinfo exists bundle parameter ${:name}]} {
- lassign [${:partof} pinfo get bundle parameter ${:name}] spec default
- :pdata [list bundle [list spec $spec default $default]]
}
- }
- }
-
-
- #
- # mixin layer interface
- #
-
- :method apply {} {
- unset -nocomplain :processed_entities
- next
- }
-
- :method revoke {} {
- next
- if {[info exists :processed_entities]} {
- return [dict keys ${:processed_entities}]
- }
- }
-
- :public method at_processed {entity} {
- dict set :processed_entities $entity _
- }
-
- #
- # processor interface
- #
-
- :method log {msg} {
- puts stderr "[current]->[uplevel 1 [list ::nsf::current method]]: $msg"
- }
-
- :public method process {
- -sandboxed:switch
- -validate:switch
- {-type project}
- -include
- -exclude
- thing
- } {
- if {$type ne "project"} {
- # TODO: Fix the naming requirements ...
- set project [@project new -name "_%@"]
- $project sources [list $type $thing]
- } else {
- set project $thing
- }
-
- set box [$project sandbox [Sandbox new \
- -interp [expr {$sandboxed?[interp create]:""}]]]
- set sources [dict create]
- foreach {type name} [$project sources] {
- dict lappend sources $type $name
- }
-
-
- set nsFilters [list]
- if {[info exists include] && $include ne ""} {
- set nsFilters [list $include]
- }
- if {[info exists exclude] && $exclude ne ""} {
- set nsFilters [list -not $exclude]
- }
-
-
- set provided_entities [list]
- dict for {type instances} $sources {
- lappend provided_entities {*}[:[current method]=$type $project $instances {*}$nsFilters]
- }
-
- if {$validate} {
- #
- # TODO: is_validated to later to become a derived/computed
- # property ... for now, we just need to escape from setting
- # validation-related info in non-validated projects!
- #
- $project is_validated $validate; # is_validated = 1
-
- set present_entities [::nx::doc::filtered $provided_entities {[[:origin] eval {info exists :pdata}]}]
- # TODO: the nspatterns should be consumed from the source
- # specification and should not be hardcoded here ... review
- # later ...
- #puts stderr "NSF: [join [dict keys [$box get_registered_commands -exported -types @command]] \n]"
- # ISSUE: -exported turns out to be a weak filter criterion, it
- # excludes slot objects from being processed!
-
- #
- # TODO: Add support for "generated" packages and their
- # validation later on, i.e. a @package.validate() method.
- #
-
- set generated_commands [dict merge \
- [$box get_registered_commands -types {
- @object
- @class
- @command
- }] \
- [$box get_registered_commands -types {
- @method
- }]]
-
- #puts stderr generated_commands=$generated_commands
- #puts stderr present_entities=$present_entities
+ set generated_commands [dict filter $cmds script {k v} {
+ dict with v { expr {$cmdtype in {@object @class @command} && !$dependency } }
+ }]
+
+ set generated_commands [dict merge $generated_commands [dict filter $cmds script {k v} {
+ dict with v { expr {$cmdtype eq "@method" && !$dependency} }
+ }]]
+
+
set map [dict create]
foreach pe $present_entities {
if {[$pe pinfo exists bundle handle]} {
@@ -3143,11 +2713,19 @@
dict unset generated_commands $fqn
dict set map $fqn $pe
}
-
- # 2. generated entities (doc[no]->program[yes])
- dict for {cmd info} $generated_commands {
- dict with info {
- if {$cmdtype ni [list @command @object @class @method]} continue;
+
+ #
+ # 2.) Generate entities for undocumented ("missing") program entities
+ #
+ dict for {cmd info} $generated_commands {
+ dict with info {
+ if {$cmdtype ni [list @command @object @class @method]} continue;
+ if {[info exists package] && [dict exists $pkgMap $package]} {
+ set pkgObj [dict get $pkgMap $package]
+ [:info class] containers push $pkgObj
+ unset package
+ }
+
if {$cmdtype eq "@object" && [string match *::slot::* $cmd]} {
if {[dict exists $info bundle objtype] && [dict get $info bundle objtype] eq "ensemble"} continue;
set name [namespace tail $cmd]
@@ -3188,889 +2766,316 @@
set entity [@ $cmdtype $cmd]
}
- #puts stderr "SETTING missing PDATA $entity $cmd"
$entity pdata [lappend info status missing]
dict set map [$entity get_fqn_command_name] $entity
}
- }
}
- return $project
}
+
+ #
+ # The actual Validator mixin layer. The mixins trace the creation
+ # process of provided entities (and assign corresponding pdata, if
+ # present) and provide validation hooks for the various entity
+ # types.
+ #
- :protected method process=@package {project pkgs} {
- set box [$project sandbox]
- $box permissive_pkgs [string tolower $pkgs]
- set 1pass ""
- foreach pkg $pkgs {
- if {[catch {namespace eval :: [list package req $pkg]} _]} {
- error "Tcl package '$pkg' cannot be found."
+ Mixin create [current]::Entity {
+ :public method init args {
+ next
+ set prj [:current_project]
+ if {$prj ne ""} {
+ set box [$prj sandbox]
+ set cmdname [:get_fqn_command_name]
+ if {[$box eval {info exists :registered_commands}] && \
+ [$box eval [concat dict exists \${:registered_commands} $cmdname]]} {
+ :pdata [$box eval [concat dict get \${:registered_commands} $cmdname]]
+ }
}
- append 1pass "package req $pkg\n"
+ [[current class] info parent] at_processed [current]
}
- #
- # a) 1-pass: requiring the packages first will provide
- # all dependencies (also those not to be documented).
- #
- $box do "::nx::doc::__trace_pkg; $1pass"
-
- #
- # b) 2-pass: [source] will re-evaluate the package scripts
- # (note, [load]-based extension packages are not covered by this!)
- #"
- if {[$box eval {info exists :source}]} {
+ :public method validate {} {
#
- # Note: Expects the XOTcl2 utilities to be in place and
- # accessible by the [package req] mechanism, use e.g.:
- # export TCLLIBPATH=". ./library/xotcl/library/lib"
+ # TODO: At some validate() spots, we still assume that the
+ # missing entities are processed by the validator (e.g., to
+ # mark container entities as a mismatch if a child entity is
+ # missing etc.) ... This needs to be relocated ...
#
- package req xotcl::xodoc
- namespace eval :: {namespace import -force ::xotcl::@}
-
- set docdb [XODoc new]
- ::@ set analyzerObj $docdb
- foreach {pkg src} [$box eval {set :source}] {
- $docdb analyzeFile $src
- }
-
- foreach m [namespace eval ::nx::doc::xodoc {namespace export}] {
- if {[::xotcl::Class info instances -closure ::xotcl::metadataAnalyzer::$m] ne ""} {
- ::xotcl::metadataAnalyzer::$m instmixin add ::nx::doc::xodoc::$m
+ if {[info exists :pdata] && \
+ [:pinfo get -default complete status] ne "missing"} {
+ if {[[:origin] as_list] eq ""} {
+ :pinfo propagate status mismatch
+ :pinfo lappend validation "Provide a short, summarising description!"
}
}
-
- ::nx::doc::xodoc::MetadataToken eval [list set :analyzer $docdb]
- set provided_entites [list]
- #
- # as we analyze file by file, there is only one FileToken to
- # be molded into an @package
- #
- set ft [::xotcl::metadataAnalyzer::FileToken allinstances]
- if {[llength $ft] > 1} {
- error "Too many xodoc file tokens processed. Expecting just one!"
- }
-
- $project @namespace "::xotcl"
- ::nx::doc::QualifierTag mixin add ::nx::doc::ContainerEntity::Resolvable
- ::nx::doc::ContainerEntity::Resolvable container $project
-
- foreach {attr part_class} [$project part_attributes] {
- $part_class class mixin add ::nx::doc::ContainerEntity::Containable
- $part_class container $project
- }
-
- set partof $project
- if {$ft ne ""} {
- set pkg [$ft emit $project]
- lappend provided_entities $pkg
- set partof $pkg
- }
-
- foreach token [::xotcl::metadataAnalyzer::ObjToken allinstances] {
- lappend provided_entities [$token emit $partof]
- }
-
- return $provided_entities
+ next
}
- }
-
- :protected method process=package {project pkgs nsFilters:optional} {
- set box [$project sandbox]
- $box permissive_pkgs $pkgs
- set 1pass ""
- foreach pkg $pkgs {
- if {[catch {package req $pkg} _]} {
- error "Tcl package '$pkg' cannot be found."
- }
- append 1pass "package req $pkg\n"
- }
- #
- # a) 1-pass: requiring the packages first will provide
- # all dependencies (also those not to be documented).
- #
- $box do "::nx::doc::__trace_pkg; $1pass"
-
- #
- # b) 2-pass: [source] will re-evaluate the package scripts
- # (note, [load]-based extension packages are not covered by this!)
- #"
- if {[$box eval {info exists :source}]} {
- foreach {pkg src} [$box eval {set :source}] {
- #
- # TODO: 2-pass [source]s should not trigger transitive [source]s. we
- # have flattened the relevant [source] hierarchy in the
- # 1-pass.
- #
- append 2pass \
- "::nx::doc::__cpackage push $pkg;\n" \
- "source $src;\n" \
- "::nx::doc::__cpackage pop;\n"
- }
- $box do "::nx::doc::__init; $2pass"
- }
-
- #
- # filter registered commands for includes/excludes
- #
- if {[info exists nsFilters]} {
- $box registered_commands [$box get_registered_commands $nsFilters]
- }
-
- # puts stderr REGISTERED_COMMANDS=[dict keys [$box registered_commands]]
-
-
- foreach {attr part_class} [$project part_attributes] {
- $part_class class mixin add ::nx::doc::ContainerEntity::Containable
- $part_class container $project
- }
-
- set deps_entities [list]
- foreach dep [$box getCompanions [$box eval {set :deps}]] {
- lappend deps_entities {*}[:readin $dep]
- }
- foreach de $deps_entities {
- $de @stashed
- }
-
- set scripts [$box get_companions]
- set provided_entities [list]
-
- foreach script $scripts {
- lappend provided_entities {*}[:readin $script]
- }
- return $provided_entities
}
- :protected method process=source {project filepath} {;}
-
- :protected method process=eval {project scripts} {
- set box [$project sandbox]
- #
- # 1a) 1pass ... TODO: should tracing be enabled in this scenario? ...
- #
- foreach script $scripts {
- $box do $script
- }
-
- #
- # 2) 2pass ...
- #
- $box do [list ::nx::doc::__init]
-
- foreach script $scripts {
- $box do $script
- }
- #
- # 3) documentation processing
- #
-
- # 3a) top-level processing
- foreach script $scripts {
- :readin $script
- }
-
- # 3b) initcmds, proc bodies ...
-
- dict for {cmd info} [$box get_registered_commands] {
- dict with info {
- #
- # TODO: for now, we assume objects beyond this point
- # ... relax later!
- #
- if {$cmdtype ni [list @object @class]} continue;
- if {[info exists docstring]} {
- lassign [:readin \
- -docstring \
- -tag $cmdtype \
- -name $cmd \
- -parsing_level 1 \
- $docstring] entity processed_entities
- unset docstring
- } else {
- set entity [@ $cmdtype $cmd]
+ Mixin create [current]::StructuredEntity -superclass [current]::Entity {
+ :public method validate {} {
+ next
+ dict for {s entities} [:owned_parts -where "!\${:@stashed}"] {
+ foreach e $entities {
+ if {![$e eval {info exists :@use}]} {
+ $e [current method]
+ }
}
- :process=$cmdtype $project $entity
- }
+ }
}
}
-
- :public method readin {
- -docstring:switch
- -tag
- -name
- -partof_entity:object,type=::nx::doc::StructuredEntity
- {-parsing_level:integer 0}
- script
- } {
- set blocks [:comment_blocks $script]
- set first_block 1
- set processed_entities [list]
- foreach {line_offset block} $blocks {
- array set arguments [list -initial_section context \
- -parsing_level $parsing_level]
- if {$docstring} {
- if {[info exists partof_entity]} {
- set arguments(-partof_entity) $partof_entity
- }
- if {![info exists tag] || ![info exists name]} {
- error "In docstring mode, provide the tag and the name of
- a docstring-owning documentation entity object."
- }
- if {$first_block} {
- #
- # TODO: Note that the two "creation procedures" are not
- # idempotent; the relative one overwrites description
- # blocks of pre-exisiting entities, the freestanding @
- # does not ... fix later when reviewing these parts of the
- # program ...
- #
- set docentity [expr {[info exists partof_entity]?\
- [$partof_entity $tag $name]:[@ $tag $name]}]
- set arguments(-partof_entity) $docentity
- if {$line_offset <= 1} {
- set arguments(-initial_section) description
- set arguments(-entity) $docentity
+ Mixin create [current]::@command -superclass [current]::StructuredEntity {
+ :public method validate {} {
+ if {[info exists :pdata] && \
+ [:pinfo get -default complete status] ne "missing"} {
+
+ if {![info exists :@command]} {
+ set params [list]
+ set param_names [list]
+ if {[info exists :@parameter]} {
+ foreach p [:@parameter] {
+ set value [$p name]
+ lappend param_names $value
+ if {[$p eval {info exists :default}] || $value eq "args" } {
+ set value "?$value?"
+ }
+ lappend params $value
+ }
}
+
+ set ps [:pinfo get -default "" bundle parameter]
+ dict for {actualparam paraminfo} $ps {
+ if {$actualparam ni $param_names} {
+ set p [:@parameter $actualparam]
+ $p pdata [lappend paraminfo status missing]
+ }
+ }
+ }
+
+ if {![:pinfo exists bundle parametersyntax]} {
+ :pinfo set bundle parametersyntax $params
}
+
+ # TODO (Review!): [next] will cause the missing parameter
+ # created to be validated and will have the appropriate
+ # status propagated upstream!b
+ next
}
-
- set args [array get arguments]
- lappend args $block
- # puts stderr "::nx::doc::CommentBlockParser process {*}$args"
- #::nx::doc::Entity mixin add [current]::Entity
- :apply
- ::nx::doc::CommentBlockParser process {*}$args
- lappend processed_entities {*}[:revoke]
- set first_block 0
}
- if {$docstring && [info exists arguments(-partof_entity)]} {
- return [list $arguments(-partof_entity) $processed_entities]
- } else {
- return $processed_entities
- }
}
- :public method analyze_line {line} {
- set regex {^[\s#]*#+(.*)$}
- if {[regexp -- $regex $line --> comment]} {
- return [list 1 [string trimright $comment]]
- } else {
- return [list 0 $line]
- }
- }
+ Mixin create [current]::@object -superclass [current]::StructuredEntity
- :public method comment_blocks {script} {
- set lines [split $script \n]
- set comment_blocks [list]
- set was_comment 0
-
- set spec {
- 0,1 {
- set line_offset $line_counter;
- set comment_block [list];
- lappend comment_block $text}
- 1,0 {
- lappend comment_blocks $line_offset $comment_block;
- unset comment_block
- }
- 1,1 {lappend comment_block $text}
- 0,0 {}
- }
- array set do $spec
- set line_counter -1
- foreach line $lines {
- incr line_counter
- # foreach {is_comment text} [:analyze_line $line] break;
- lassign [:analyze_line $line] is_comment text;
- eval $do($was_comment,$is_comment)
- set was_comment $is_comment
- }
- if {[info exists comment_block]} {
- lappend comment_blocks $line_offset $comment_block
- }
- return $comment_blocks
- }
-
- # TODO: how can I obtain some reuse here when later @class is
- # distinguished from @object (dispatch along the inheritance
- # hierarchy?)
-
- :public method process=@command {project entity} {;}
-
- :public method process=@class {project entity} {
- set name [$entity name]
- set box [$project sandbox]
- # attributes
- foreach slot [$box do [list $name info slot objects]] {
- if {[$box do [list $slot eval {info exists :__initcmd}]]} {
+ Mixin create [current]::@class -superclass [current]::@object {
+ :public method validate {} {
+ next
+ #
+ # TODO: Certain metadata could also be valid in "missing"
+ # state, e.g., paramtersyntax? Re-arrange later ...
+ #
+ if {[info exists :pdata] &&
+ [:pinfo get -default complete status] ne "missing"} {
#
- # TODO: Here, we eagerly create doc entities, is this an issue?
- # Should we mark them for removal if not further processed?
- # This might be contradicting to the requirement of
- # identifying documented/undocumented program structures.
+ # Note: Some metadata on classes cannot be retrieved from
+ # within the tracers, as they might not be set local to the
+ # class definition. Hence, we gather them at this point.
#
- # There are two alternatives:
- # -> use a freestanding identity generator (preferred!)
- # -> mark the entity for deletion
- #
- # set id [$entity @${scope}-attribute [$box do [list $slot name]]]
-
- set scope [expr {[$box do [list $slot per-object]]?"class-object":"class"}]
- :readin \
- -partof_entity $entity \
- -docstring \
- -tag @${scope}-property \
- -name [$box do [list $slot name]] \
- -parsing_level 2 \
- [$box do [list $slot eval {set :__initcmd}]]
-
+ set prj [:current_project]
+ set box [$prj sandbox]
+ set statement [list ::nsf::dispatch ${:name} \
+ ::nsf::methods::class::info::objectparameter \
+ parametersyntax]
+ :pinfo set bundle parametersyntax [$box eval $statement]
}
}
-
- foreach methodName [$box do [list $name info methods \
- -methodtype scripted \
- -callprotection public]] {
- :readin \
- -partof_entity $entity \
- -docstring \
- -tag @class-method \
- -name $methodName \
- -parsing_level 2 \
- [$box do [list ${name} info method body $methodName]]
- }
-
- :process=@object $project $entity class
-
}
-
- #
- # TODO: how to resolve to the current project's context. For now,
- # we pass a parameter value, revisit this decision once we decide
- # on a location for this behaviour.
- #
- :public method process=@object {project entity {scope ""}} {
- set name [$entity name]
- set box [$project sandbox]
- # methods
- foreach methodName [$box do [list ${name} {*}$scope info methods\
- -methodtype scripted \
- -callprotection public]] {
-
- set tag [join [list {*}[expr {$scope eq "class"?"class-object":""}] method] -]
- # set id [$entity @$tag $methodName]
- :readin \
- -partof_entity $entity \
- -docstring \
- -tag @$tag \
- -name $methodName \
- -parsing_level 2 \
- [$box do [list ${name} {*}$scope info method body $methodName]]
- }
- }
+ Mixin create [current]::ContainerEntity -superclass [current]::StructuredEntity
- }
-}
-
-
-#
-# toplevel interface
-# ::nx::doc::make all
-# ::nx::doc::make doc
-#
-namespace eval ::nx::doc {
-
- Object create make {
-
- :method all {{-verbose:switch} {-class ::nx::Class}} {
- foreach c [$class info instances -closure] {
- if {$verbose} {puts "postprocess $c"}
- ::nx::doc::postprocessor process $c
+ Mixin create [current]::@package -superclass [current]::ContainerEntity {
+ :public method init args {
+ next
+ set prj [:current_project]
+ if {$prj ne ""} {
+ set box [$prj sandbox]
+ if {[$box eval [concat dict exists \${:registered_packages} ${:name}]]} {
+ :pdata [$box eval [concat dict get \${:registered_packages} ${:name}]]
+ }
+ }
}
- }
-
- :method write {content path} {
- set fh [open $path w]
- puts $fh $content
- catch {close $fh}
- }
- :public method doc {
- {-format html}
- project:object,type=::nx::doc::@project
- args
- } {
- package req nx::doc::$format
- $format run -project $project {*}$args
- }
- }
-
- #
- # This is a mixin class which adds comment block parsing
- # capabilities to documentation entities (Entity, ...), once
- # identified.
- #
- # It acts as the event source external to the modal parser (i.e.,
- # the parsed entity). Expressing a modal behavioural design itself
- # (around the line queue of a comment block), it produces certain
- # events which are then signalled to the parsed entity.
- #
- Class create CommentBlockParser {
-
- :property {parsing_level:integer 0}
-
- :property {message ""}
- :property {status:in "COMPLETED"} {
-
- set :incremental 1
-
- set :statuscodes {
- COMPLETED
- INVALIDTAG
- MISSINGPARTOF
- STYLEVIOLATION
- LEVELMISMATCH
- }
-
- :public method type=in {name value} {
- if {$value ni ${:statuscodes}} {
- error "Invalid statuscode '$code'."
+ :public method validate {} {
+ if {[info exists :pdata] && \
+ [:pinfo get -default complete status] ne "missing"} {
+ set orig [:origin]
+ if {[$orig eval {info exists :@version}]} {
+ set docVersion [$orig @version]
+ set actualVersion [:pinfo get version]
+ if {$docVersion ne $actualVersion} {
+ :pinfo propagate status mismatch
+ :pinfo lappend validation "Package version mismatch: $docVersion vs. $actualVersion"
+ }
+ }
+ next
}
- return $value
}
-
- :public method ? [list obj var value:in,slot=[current object]] {
- return [expr {[:get $obj $var] eq $value}]
- }
-
- :public method is {obj var value} {
- return [expr {$value in ${:statuscodes}}]
- }
}
- :property processed_section {
- set :incremental 1
- :public method assign {domain prop value} {
- set current_entity [$domain current_entity]
- set scope [expr {[$current_entity info is class]?"class mixin":"mixin"}]
- # puts stderr "Switching: [$current_entity {*}$scope] --> target $value"
- if {[$domain eval [list info exists :$prop]] && [:get $domain $prop] in [$current_entity {*}$scope]} {
- $current_entity {*}$scope delete [:get $domain $prop]
+ Mixin create [current]::@method -superclass [current]::Entity {
+ :method init args {
+ next
+ set scope [expr {[${:part_attribute} scope] eq "class"?"class":"object"}]
+ set obj [:get_owning_object]
+ set method_name [:get_combined name]
+ set prj [:current_project]
+ if {$prj ne ""} {
+ set box [$prj sandbox]
+ set script "if {\[::nsf::object::exists $obj\]} {array set \"\" \[$obj eval {:__resolve_method_path \"$method_name\"}\]; ::nsf::dispatch \$(object) ::nsf::methods::${scope}::info::method handle \$(methodName)}"
+ set cmdname [$box do $script]
+ if {$cmdname ne "" && [$box eval [concat dict exists \${:registered_commands} $cmdname]]} {
+ :pdata [$box eval [concat dict get \${:registered_commands} $cmdname]]
+ }
}
- $current_entity {*}$scope add [next [list $domain $prop $value]]
- }
- }
- :property current_entity:object
-
- :public class method process {
- {-partof_entity ""}
- {-initial_section context}
- {-parsing_level 0}
- -entity
- block
- } {
- if {![info exists entity]} {
- set entity [Entity]
- }
-
- set parser_obj [:new -current_entity $entity -parsing_level $parsing_level]
- $parser_obj [current proc] \
- -partof_entity $partof_entity \
- -initial_section $initial_section \
- $block
- return $parser_obj
}
-
- :public forward has_next expr {${:idx} < [llength ${:comment_block}]}
- :public method dequeue {} {
- set r [lindex ${:comment_block} ${:idx}]
- incr :idx
- return $r
- }
- :public forward rewind incr :idx -1
- :public forward fastforward set :idx {% llength ${:comment_block}}
- :public method cancel {statuscode {msg ""}} {
- :fastforward
- :status $statuscode
- :message $msg
- uplevel 1 [list ::return -code error $statuscode]
- }
- #
- # everything below assumes that the current class is an active mixin
- # on an instance of an Entity subclass!
- #
-
- :public method process {
- {-partof_entity ""}
- {-initial_section context}
- block
- } {
-
- set :comment_block $block
- set :idx 0
-
- :processed_section [$initial_section]
-
- # TODO: currently, default values are not initialised for
- # property slots defined in mixin classes; so do it manually
- # for the time being.
- ${:current_entity} current_comment_line_type ""
-
- ${:current_entity} block_parser [current]
- ${:current_entity} eval [list set :partof_entity $partof_entity]
-
- set is_first_iteration 1
-# set failure ""
-
- #
- # Note: Within the while-loop, two object variables constantly
- # change (as "wanted" side-effects): processed_section: reflects
- # the currently processed comment section; see event=next()
- # current_entity: reflects the currently documentation entity
- # (once resolved); see context->event=parse@tag()
- #
- while {[:has_next]} {
- set line [:dequeue]
- if {$is_first_iteration} {
- ${:current_entity} on_enter $line
- set is_first_iteration 0
- }
-
- if {[catch {
- # puts stderr "PROCESS ${:current_entity} event=process $line"
- ${:current_entity} event=process $line
- } failure]} {
- if {![:status is $failure]} {
- ::return -code error -errorinfo $::errorInfo
+ :public method validate {} {
+ set partof [:get_owning_partof]
+ if {[info exists :pdata] &&
+ [:pinfo get -default complete status] ne "missing"} {
+ #
+ # Note: Some information on methods cannot be retrieved from
+ # within the tracers as they might not be set local to the
+ # method definition. Hence, we gather them at this point. I
+ # will review whether there is a more appropriate way of
+ # dealing with this issue ...
+ #
+ set prj [:current_project]
+ set box [$prj sandbox]
+ set obj [$partof name]
+
+ if {[:pinfo exists bundle handle]} {
+ set handle [:pinfo get bundle handle]
+ :pinfo set bundle redefine-protected [$box eval [list ::nsf::method::property $obj $handle redefine-protected]]
+ :pinfo set bundle call-protected [$box eval [list ::nsf::method::property $obj $handle call-protected]]
}
- }
+
+ set params [list]
+ set param_names [list]
+ if {[info exists :@parameter]} {
+ foreach p [:@parameter] {
+ set value [$p name]
+ lappend param_names $value
+ if {[$p eval {info exists :default}] || $value eq "args" } {
+ set value "?$value?"
+ }
+ lappend params $value
+ }
+ }
+
+ dict for {actualparam paraminfo} [:pinfo get -default "" bundle parameter] {
+ if {$actualparam ni $param_names} {
+ set p [:@parameter $actualparam]
+ $p pdata [lappend paraminfo status missing]
+ }
+ }
+
+ if {![:pinfo exists bundle parametersyntax]} {
+ :pinfo set bundle parametersyntax $params
+ }
+
+ # Note: [next] will cause the missing parameter created to
+ # be validated and will have the appropriate status
+ # upstream!
+ next
+ } else {
+ $partof pinfo propagate status mismatch
+ }
}
- if {!$is_first_iteration} {
- ${:current_entity} on_exit $line
- }
-
- # ISSUE: In case of some sub-method definitions (namely "info
- # mixin"), the sub-method entity object for "mixin" replaces the
- # forward handlers of the mixin relation slot. So, any slot-like
- # interactions such as delete() won't work anymore. We need to
- # bypass it by using ::nsf::relation, for the time being. This
- # is a clear con of the explicit naming of entity objects (or at
- # least the current scheme)!
-
- # if {[${:processed_section} info mixinof -scope object ${:current_entity}] ne ""} {
- # ${:current_entity} {*}$scope mixin delete ${:processed_section}
- # }
-
- set scope [expr {[${:current_entity} info is class]?"class":""}]
- set mixins [${:current_entity} {*}$scope info mixin classes]
- if {${:processed_section} in $mixins} {
- set idx [lsearch -exact $mixins ${:processed_section}]
- set mixins [lreplace $mixins $idx $idx]
- ::nsf::relation ${:current_entity} object-mixin $mixins
- }
-
- }; # CommentBlockParser->process()
-
- }
-
- Class create CommentBlockParsingState -superclass Class {
-
- :property next_comment_section
- :property comment_line_transitions:required
-
- }
-
- Class create CommentSection {
-
- :property block_parser:object,type=::nx::doc::CommentBlockParser
- :property {current_comment_line_type ""}
-
- set :line_types {
- tag {regexp -- {^\s*@[^[:space:]@]+} $line}
- text {regexp -- {^\s*([^[:space:]@]+|@[[:space:]@]+)} $line}
- space {expr {$line eq {}}}
}
- :method get_transition {src_line_type tgt_line} {
- set section [${:block_parser} processed_section]
- array set transitions [$section comment_line_transitions]
- # expected outcome
- # 1. new state -> becomes current_comment_line
- # 2. actions to be triggered from the transition
-
- foreach {line_type expression} [[current class] eval {set :line_types}] {
- set line $tgt_line
- if {[eval $expression]} {
- set tgt_line_type $line_type
- break
+ Mixin create [current]::@param -superclass [current]::Entity {
+ :public method init args {
+ next
+ if {${:name} eq "__out__"} {
+ if {[${:partof} pinfo exists bundle returns]} {
+ :pdata [list bundle [list spec [${:partof} pinfo get bundle returns]]]
+ }
+ } elseif {[${:partof} pinfo exists bundle parameter ${:name}]} {
+ lassign [${:partof} pinfo get bundle parameter ${:name}] spec default
+ :pdata [list bundle [list spec $spec default $default]]
}
}
- if {![info exists tgt_line_type]} {
- error "Could not resolve the type of line '$line'"
- }
+ :public method validate {} {
+ #
+ # TODO: For now, we escape from @param validaton on command
+ # parameters. There is no equivalent to [info parameter]
+ # available, so we would need to cook a substitute based on
+ # the parametersyntax. Review later ...
+ #
+ if {${:name} eq "__out__" && \
+ [${:partof} info has type ::nx::doc::@command]} return;
- if {![info exists transitions(${src_line_type}->${tgt_line_type})]} {
- set msg "Style violation in a [namespace tail [:info class]] section:\n"
- if {$src_line_type eq ""} {
- append msg "Invalid first line ('${tgt_line_type}')"
+ #
+ # Here, we escape from any parameter verification for
+ # parameters on forwards & alias, as there is no basis for
+ # comparison!
+ #
+ if {[${:partof} info has type ::nx::doc::@method] && \
+ [${:partof} pinfo get bundle type] in [list forward alias]} {
+ dict set :pdata status ""
+ return;
+ }
+
+ if {[info exists :pdata] && \
+ [:pinfo get -default complete status] ne "missing"} {
+
+ # valid for both object and method parameters
+ set pspec [:pinfo get -default "" bundle spec]
+ if {[info exists :spec] && \
+ ${:spec} ne $pspec} {
+ :pinfo propagate status mismatch
+ :pinfo lappend validation "Specification mismatch. Expected: \
+ '${:spec}' Got: '$pspec'."
+ }
+ next
} else {
- append msg "A ${src_line_type} line is followed by a ${tgt_line_type} line"
+ ${:partof} pinfo propagate status mismatch
}
- ${:block_parser} cancel STYLEVIOLATION $msg
- # [StyleViolation new -message $msg] throw
}
- return [list $tgt_line_type $transitions(${src_line_type}->${tgt_line_type})]
}
- # the actual events to be signalled to and sensed within the
- # super-states and sub-states
+ #
+ # mixin layer interface
+ #
- :public method event=process {line} {
- lassign [:get_transition ${:current_comment_line_type} $line] \
- :current_comment_line_type actions
- foreach action $actions {
- :event=$action $line
- }
+ :public class method apply {} {
+ unset -nocomplain :processed_entities
+ next
}
- :public forward event=parse %self {% subst {parse@${:current_comment_line_type}}}
- :method event=next {line} {
- set next_section [[${:block_parser} processed_section] next_comment_section]
- :on_exit $line
-
- ${:block_parser} rewind
- :current_comment_line_type ""
-
- ${:block_parser} processed_section [$next_section]
- :on_enter $line
- }
-
-
- # realise the sub-state (a variant of METHOD-FOR-STATES) and their
- # specific event handling
- # set :lineproc {{tag args} {return [concat {*}$args]}}
- # set :lineproc {{tag args} {puts stderr LINE=[list $tag {*}$args]; return [list $tag {*}$args]}}
- set :lineproc {{tag args} {return [list $tag [expr {$args eq ""?$args:[list $args]}]]}}
- :method parse@tag {line} {
- lassign [apply [[current class] eval {set :lineproc}] {*}$line] tag line
- #set line [lassign [apply [[current class] eval {set :lineproc}] {*}$line] tag]
- if {[:info lookup methods -source application $tag] eq ""} {
- set msg "The tag '$tag' is not supported for the entity type '[namespace tail [:info class]]"
- ${:block_parser} cancel INVALIDTAG $msg
+ :public class method revoke {} {
+ #
+ # TODO: reset current_project here?
+ #
+ next
+ if {[info exists :processed_entities]} {
+ return [dict keys ${:processed_entities}]
}
- #:$tag [lrange $line 1 end]
- #:$tag {*}[expr {$line eq ""?$line:[list $line]}]
- #:$tag $line
- :$tag {*}$line
}
-
- :method parse@text {line} {
- #puts stderr "ADDLINE([current]) :@doc add $line end"
- :@doc add $line end
- }
- :method parse@space {line} {;}
- #
- # so far, we only need enter and exit handlers at the level of the
- # superstates: context, description, part
- #
- :public method on_enter {line} {;}
- :public method on_exit {line} {;}
+ :public class method at_processed {entity} {
+ dict set :processed_entities $entity _
+ }
}
-
- # NOTE: add these transitions for supporting multiple text lines for
- # the context element
- # tag->text parse
- # text->text parse
- # text->space ""
-
-
- CommentBlockParsingState create context -superclass CommentSection \
- -next_comment_section description \
- -comment_line_transitions {
- ->tag parse
- tag->space ""
- space->space ""
- space->text next
- space->tag next
- } {
-
- :method resolve_partof_entity {tag name} {
- # a) unqualified: attr1
- # b) qualified: Bar#attr1
- if {[regexp -- {([^\s#]*)#([^\s#]*)} $name _ qualifier nq_name]} {
- # TODO: Currently, I only foresee @object and @command as
- # possible qualifiers; however, this should be fixed asap, as
- # soon as the variety of entities has been decided upon!
- foreach entity_type {@class @command @object} {
- set partof_entity [$entity_type id $qualifier]
- # TODO: Also, we expect the qualifier to resolve against an
- # already existing entity object? Is this intended?
- if {[::nsf::is object $partof_entity]} {
- return [list $nq_name $partof_entity]
- }
- }
- return [list $nq_name ${:partof_entity}]
- } else {
- return [list $name ${:partof_entity}]
- }
- }
-
- set :lineproc {{tag name args} {return [list $tag $name $args]}}
- :method parse@tag {line} {
- lassign [apply [[current class] eval {set :lineproc}] {*}$line] axes names args
- set entity ${:partof_entity}
- set axes [split [string trimleft $axes @] .]
-
- # 1) get the parsing level from the comment block parser
- set start_idx [lindex [lsearch -all -not -exact $axes ""] 0]
+}
- set pl [${:block_parser} parsing_level]
- if {$pl != $start_idx} {
- ${:block_parser} cancel LEVELMISMATCH "Parsing level mismatch: Tag is meant for level '$start_idx', we are at '$pl'."
- }
-
- # 2) stash away a number of empty axes according to the parsing level
- set axes [lrange $axes $pl end]
-
- lassign [::nx::doc::Tag normalise $axes $names] err res
- if {$err} {
- ${:block_parser} cancel STYLEVIOLATION $res
- }
+namespace eval ::nx::doc {
- lassign $res tagpath names
-
- set leaf(axis) [lindex $tagpath end]
- set tagpath [lrange $tagpath 0 end-1]
- set leaf(name) [lindex $names end]
- set names [lrange $names 0 end-1]
-
- lassign [::nx::doc::Tag find -strict $tagpath $names $entity] err res
- if {$err} {
- ${:block_parser} cancel INVALIDTAG $res
- }
-
- set entity $res
-
- if {$entity eq ""} {
- set cmd [info commands @$leaf(axis)]
-
- # TODO interp-aliasing objects under different command names
- # is currently not transparent to some ::nsf::* helpers,
- # such as ::nsf::object::exists. Should this be changed?
- #
- if {$cmd ne ""} {
- set cmd [namespace origin $cmd]
- set target [interp alias {} $cmd]
- if {$target ne ""} {
- set cmd $target
- }
- }
-
- if {$cmd eq "" || ![::nsf::object::exists $cmd] || \
- ![$cmd info has type Tag]} {
-
- ${:block_parser} cancel INVALIDTAG "The entity type '@$leaf(axis)' is not available."
- }
-
- # VERIFY! Still an issue? TODO: @object-method raises some
- # issues (at least when processed without a resolved
- # context = its partof entity). It is not an entity type,
- # because it merely is a "scoped" @method. It won't
- # resolve then as a proper instance of Tag, hence we
- # observe an InvalidTag exception. For now, we just ignore
- # and bypass this issue by allowing InvalidTag exceptions
- # in analyze()
-
- set entity [@$leaf(axis) new -name $leaf(name) {*}$args]
- } else {
- if {[$entity info lookup methods -source application @$leaf(axis)] eq ""} {
- ${:block_parser} cancel INVALIDTAG \
- "The tag '$leaf(axis)' is not supported for the entity type '[namespace tail [$entity info class]]'"
- }
- set entity [$entity @$leaf(axis) [list $leaf(name) {*}$args]]
- }
-
- ${:block_parser} current_entity $entity
- ${:block_parser} processed_section [current class]
- $entity current_comment_line_type ${:current_comment_line_type}
- $entity block_parser ${:block_parser}
- }
-
- # :method parse@text {line} { next }
- # :method parse@space {line} { next }
-
- }
-
- CommentBlockParsingState create description -superclass CommentSection \
- -next_comment_section part \
- -comment_line_transitions {
- ->text parse
- ->tag next
- text->text parse
- text->space parse
- space->text parse
- space->space parse
- space->tag next
- } {
-
- :public method on_enter {line} {
- unset -nocomplain :@doc
- next
- }
-
- # tag lines are not allowed in description blocks!
- # :method parse@tag {line} {;}
- :method parse@space {line} {
- :@doc add "" end
- next
- }
-
- }
-
- CommentBlockParsingState create part -superclass CommentSection \
- -next_comment_section part \
- -comment_line_transitions {
- ->tag parse
- tag->text parse
- text->text parse
- text->tag next
- text->space ""
- space->space ""
- tag->space ""
- space->tag next
- tag->tag next
- } {
- # realise the parse events specific to the substates of description
- :public method on_enter {line} {
-# puts stderr "ENTERING part $line, current section [${:block_parser} processed_section]"
- unset -nocomplain :current_part
- next
- }
- :method parse@tag {line} {
- set r [next]
-# puts stderr GOT=$r
- if {[::nsf::object::exists $r] && [$r info has type ::nx::doc::Entity]} {
- set :current_part $r
- }
- return $r
- }
- :method parse@text {line} {
- if {[info exists :current_part]} {
- ${:current_part} @doc add $line end
- } else {
- :event=next $line
- }
- }
- # :method parse@space {line} {;}
- }
-
::nsf::proc mkIndex {{-documentAll:switch 0} {-indexfiles:0..* ""} {-outdir "[pwd]"} args} {
if {![llength $args]} {
@@ -4122,6 +3127,4 @@
puts -nonewline $fid $index
close $fid
}
-}
-
-# puts stderr "Doc Tools loaded: [info command ::nx::doc::*]"
\ No newline at end of file
+}
\ No newline at end of file
Index: library/lib/nxdoc-dc.tcl
===================================================================
diff -u
--- library/lib/nxdoc-dc.tcl (revision 0)
+++ library/lib/nxdoc-dc.tcl (revision 187fbd20a453ae9d73e9b48f88b8d6a8c79685c2)
@@ -0,0 +1,949 @@
+
+package provide nx::doc::dc 1.0
+namespace eval ::nx::doc {}
+
+package require nx::doc 1.0
+
+namespace eval ::nx::doc {
+
+ @project eval {
+
+ :private method "frontend dc" {srcs cmds} {
+
+ #
+ # Action 7) Have the documentation dependencies processed
+ # (documented, but stashed entities)
+ #
+ :readDeps $srcs $cmds
+
+ return [:readSrcs $srcs $cmds]
+ }
+
+
+ nx::Class create [current]::DependencyEntity {
+ :class property {instances:0..*,object,type=::nx::doc::Entity ""} {
+ set :incremental 1
+ }
+ :public method init args {
+ next
+ #
+ # stash the entity
+ #
+ :@stashed
+ [current class] instances add [current]
+ }
+ }
+
+ :public method readDeps {srcs cmds} -returns 0..*,object,type=::nx::doc::Entity {
+ #
+ # 1) Get dep sources/cmds
+ #
+
+ set dSrcs [dict filter $srcs script {k v} { dict with v {set dependency}}]
+ set dCmds [dict filter $cmds script {k v} { dict with v {set dependency}}]
+
+ #
+ # 2) Forward to the frontend, while tracking the creation of doc
+ # entities ...
+ #
+ Entity mixin add [current class]::DependencyEntity
+ foreach companion [:getCompanions $dSrcs] {
+ :readin $companion
+ }
+ Entity mixin delete [current class]::DependencyEntity
+
+ set inst [[current class]::DependencyEntity instances]
+
+ #
+ # cleanup
+ #
+ [current class]::DependencyEntity instances [list]
+
+ return $inst
+ }
+
+ nx::Class create [current]::ProvidedEntity {
+ :class property {instances:0..*,object,type=::nx::doc::Entity ""} {
+ set :incremental 1
+ }
+ :public method init args {
+ next
+ [current class] instances add [current]
+ }
+ }
+
+
+ :public method readSrcs {srcs cmds} {
+ set aSrcs [dict filter $srcs script {k v} { dict with v {expr {!$dependency}} }]
+ set aCmds [dict filter $cmds script {k v} { dict with v {expr {!$dependency}} }]
+
+ Entity mixin add [current class]::ProvidedEntity
+ #
+ # 1) Process the doc sources
+ #
+ foreach companion [:getCompanions $aSrcs] {
+ :readin $companion
+ }
+
+ if {0} {
+ #
+ # 2) FIXME: Process the cmds, for docstring occurrences
+ #
+
+ dict for {cmd info} $aCmds {
+ dict with info {
+ #
+ # TODO: for now, we assume objects beyond this point
+ # ... relax later!
+ #
+ if {$cmdtype ni [list @object @class]} continue;
+ if {[info exists docstring]} {
+ lassign [:readin \
+ -docstring \
+ -tag $cmdtype \
+ -name $cmd \
+ -parsing_level 1 \
+ $docstring] entity processed_entities
+ unset docstring
+ }
+ if {$entity ne ""} {
+ :process=$cmdtype [current] $entity
+ }
+ }
+ }
+ }
+ #:frontend $frontend $aSrcs $aCmds
+ Entity mixin delete [current class]::ProvidedEntity
+ set inst [[current class]::ProvidedEntity instances]
+ #
+ # cleanup
+ #
+ [current class]::ProvidedEntity instances [list]
+ return $inst
+ }
+
+
+ :protected method getCompanions {sourceScripts} {
+ set scripts [list]
+ array set sourcables [list]
+ dict for {key info} $sourceScripts {
+ dict with info {
+ if {[info exists script]} {
+ lappend scripts $script
+ unset script
+ }
+ if {[info exists path]} {
+ set rootname [file rootname $path]
+ set dir [file dirname $path]
+ set sourcables($rootname.nxd) ""
+ if {[info exists package]} {
+ set sourcables([file join $dir $package].nxd) ""
+ unset package
+ }
+ unset path
+ }
+ }
+ }
+
+ foreach s [array names sourcables] {
+ if {![file isfile $s] || ![file readable $s]} continue;
+ set fh [open $s r]
+ if {[catch {lappend scripts [read $fh]} msg]} {
+ catch {close $fh}
+ :log "error reading the file '$s', i.e.: '$msg'"
+ }
+ catch {close $fh}
+ }
+ return $scripts
+ }
+
+
+
+
+ :protected method process=package {project pkgs nsFilters:optional} {
+ # / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
+ # VALIDATION
+ set box [$project sandbox]
+ $box permissive_pkgs $pkgs
+ set 1pass ""
+ foreach pkg $pkgs {
+ if {[catch {package req $pkg} _]} {
+ error "Tcl package '$pkg' cannot be found."
+ }
+ append 1pass "package req $pkg\n"
+ }
+
+ #
+ # a) 1-pass: requiring the packages first will provide
+ # all dependencies (also those not to be documented).
+ #
+ $box do "::nx::doc::__trace_pkg; $1pass"
+ #
+ # b) 2-pass: [source] will re-evaluate the package scripts
+ # (note, [load]-based extension packages are not covered by this!)
+ #"
+ if {[$box eval {info exists :source}]} {
+ foreach {pkg src} [$box eval {set :source}] {
+ #
+ # TODO: 2-pass [source]s should not trigger transitive [source]s. we
+ # have flattened the relevant [source] hierarchy in the
+ # 1-pass.
+ #
+ append 2pass \
+ "::nx::doc::__cpackage push $pkg;\n" \
+ "source $src;\n" \
+ "::nx::doc::__cpackage pop;\n"
+ }
+ $box do "::nx::doc::__init; $2pass"
+ }
+
+ #
+ # Filter registered commands for includes/excludes
+ #
+ # ISSUE: Filtering can apply to two different populations: a)
+ # for validation, the registered commands; b) without
+ # validation, the provided doc ones ... Should we stress (and
+ # implement) the difference?!
+ #
+ #
+ if {[info exists nsFilters]} {
+ $box registered_commands [$box get_registered_commands $nsFilters]
+ }
+
+ # VALIDATION
+ # \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \
+
+ # / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
+ # PROCESSOR
+ foreach {attr part_class} [$project part_attributes] {
+ $part_class class mixin add ::nx::doc::ContainerEntity::Containable
+ $part_class container $project
+ }
+
+ set deps_entities [list]
+ foreach dep [$box getCompanions [$box eval {set :deps}]] {
+ lappend deps_entities {*}[:readin $dep]
+ }
+ foreach de $deps_entities {
+ $de @stashed
+ }
+
+ set scripts [$box get_companions]
+ set provided_entities [list]
+
+ foreach script $scripts {
+ lappend provided_entities {*}[:readin $script]; # -> TODO: rather dispatch to process=source()?!
+ }
+ return $provided_entities
+ # PROCESSOR
+ # \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \
+
+ }
+
+ :protected method process=source {project filepath} {;}
+
+ :protected method process=eval {project scripts} {
+ # / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
+ # VALIDATION
+
+ set box [$project sandbox]
+ #
+ # 1a) 1pass ... TODO: should tracing be enabled in this scenario? ...
+ #
+ foreach script $scripts {
+ $box do $script
+ }
+
+ #
+ # 2) 2pass ...
+ #
+ $box do [list ::nx::doc::__init]
+
+ foreach script $scripts {
+ $box do $script
+ }
+ #
+ # 3) documentation processing
+ #
+
+ # VALIDATION
+ # \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \
+
+
+ # / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
+ # PROCESSOR
+
+ # 3a) top-level processing
+ foreach script $scripts {
+ :readin $script
+ }
+
+
+ # 3b) initcmds, proc bodies ...
+
+ dict for {cmd info} [$box get_registered_commands] {
+ dict with info {
+ #
+ # TODO: for now, we assume objects beyond this point
+ # ... relax later!
+ #
+ if {$cmdtype ni [list @object @class]} continue;
+ if {[info exists docstring]} {
+ lassign [:readin \
+ -docstring \
+ -tag $cmdtype \
+ -name $cmd \
+ -parsing_level 1 \
+ $docstring] entity processed_entities
+ unset docstring
+ } else {
+ set entity [@ $cmdtype $cmd]
+ }
+ :process=$cmdtype $project $entity
+ }
+ }
+
+ # PROCESSOR
+ # \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \
+
+ }
+
+ :public method readin {
+ -docstring:switch
+ -tag
+ -name
+ -partof_entity:object,type=::nx::doc::StructuredEntity
+ {-parsing_level:integer 0}
+ script
+ } {
+
+ set blocks [:comment_blocks $script]
+ set first_block 1
+ set processed_entities [list]
+ foreach {line_offset block} $blocks {
+ array set arguments [list -initial_section context \
+ -parsing_level $parsing_level]
+
+ if {$docstring} {
+ if {[info exists partof_entity]} {
+ set arguments(-partof_entity) $partof_entity
+ }
+ if {![info exists tag] || ![info exists name]} {
+ error "In docstring mode, provide the tag and the name of
+ a docstring-owning documentation entity object."
+ }
+ if {$first_block} {
+ #
+ # TODO: Note that the two "creation procedures" are not
+ # idempotent; the relative one overwrites description
+ # blocks of pre-exisiting entities, the freestanding @
+ # does not ... fix later when reviewing these parts of the
+ # program ...
+ #
+ set docentity [expr {[info exists partof_entity]?\
+ [$partof_entity $tag $name]:[@ $tag $name]}]
+ if {$line_offset <= 1} {
+ set arguments(-partof_entity) $docentity
+ set arguments(-initial_section) description
+ set arguments(-entity) $docentity
+ }
+ }
+ }
+
+ set args [array get arguments]
+ lappend args $block
+ #:apply
+ ::nx::doc::CommentBlockParser process {*}$args
+ #lappend processed_entities {*}[:revoke]
+ set first_block 0
+ }
+ #
+ # FIXME /YYYY
+ #
+ set processed_entities [list]
+ if {$docstring && [info exists arguments(-partof_entity)]} {
+ return [list $arguments(-partof_entity) $processed_entities]
+ } else {
+ return $processed_entities
+ }
+ }
+
+ :public method analyze_line {line} {
+ set regex {^[\s#]*#+(.*)$}
+ if {[regexp -- $regex $line --> comment]} {
+ return [list 1 [string trimright $comment]]
+ } else {
+ return [list 0 $line]
+ }
+ }
+
+ :public method comment_blocks {script} {
+ set lines [split $script \n]
+ set comment_blocks [list]
+ set was_comment 0
+
+ set spec {
+ 0,1 {
+ set line_offset $line_counter;
+ set comment_block [list];
+ lappend comment_block $text}
+ 1,0 {
+ lappend comment_blocks $line_offset $comment_block;
+ unset comment_block
+ }
+ 1,1 {lappend comment_block $text}
+ 0,0 {}
+ }
+ array set do $spec
+ set line_counter -1
+ foreach line $lines {
+ incr line_counter
+ # foreach {is_comment text} [:analyze_line $line] break;
+ lassign [:analyze_line $line] is_comment text;
+ eval $do($was_comment,$is_comment)
+ set was_comment $is_comment
+ }
+ if {[info exists comment_block]} {
+ lappend comment_blocks $line_offset $comment_block
+ }
+ return $comment_blocks
+ }
+
+ # TODO: how can I obtain some reuse here when later @class is
+ # distinguished from @object (dispatch along the inheritance
+ # hierarchy?)
+
+ :public method process=@command {project entity} {;}
+
+ :public method process=@class {project entity} {
+ set name [$entity name]
+ set box [$project sandbox]
+ # attributes
+ foreach slot [$box do [list $name info slot objects]] {
+ if {[$box do [list $slot eval {info exists :__initcmd}]]} {
+ #
+ # TODO: Here, we eagerly create doc entities, is this an issue?
+ # Should we mark them for removal if not further processed?
+ # This might be contradicting to the requirement of
+ # identifying documented/undocumented program structures.
+ #
+ # There are two alternatives:
+ # -> use a freestanding identity generator (preferred!)
+ # -> mark the entity for deletion
+ #
+ # set id [$entity @${scope}-attribute [$box do [list $slot name]]]
+
+ set scope [expr {[$box do [list $slot per-object]]?"class-object":"class"}]
+ :readin \
+ -partof_entity $entity \
+ -docstring \
+ -tag @${scope}-property \
+ -name [$box do [list $slot name]] \
+ -parsing_level 2 \
+ [$box do [list $slot eval {set :__initcmd}]]
+
+ }
+ }
+
+ foreach methodName [$box do [list $name info methods \
+ -methodtype scripted \
+ -callprotection public]] {
+ :readin \
+ -partof_entity $entity \
+ -docstring \
+ -tag @class-method \
+ -name $methodName \
+ -parsing_level 2 \
+ [$box do [list ${name} info method body $methodName]]
+ }
+
+ :process=@object $project $entity class
+
+ }
+
+ #
+ # TODO: how to resolve to the current project's context. For now,
+ # we pass a parameter value, revisit this decision once we decide
+ # on a location for this behaviour.
+ #
+ :public method process=@object {project entity {scope ""}} {
+ set name [$entity name]
+ set box [$project sandbox]
+ # methods
+
+ foreach methodName [$box do [list ${name} {*}$scope info methods\
+ -methodtype scripted \
+ -callprotection public]] {
+
+ set tag [join [list {*}[expr {$scope eq "class"?"class-object":""}] method] -]
+ # set id [$entity @$tag $methodName]
+ :readin \
+ -partof_entity $entity \
+ -docstring \
+ -tag @$tag \
+ -name $methodName \
+ -parsing_level 2 \
+ [$box do [list ${name} {*}$scope info method body $methodName]]
+ }
+ }
+ }
+
+ #
+ # This is a mixin class which adds comment block parsing
+ # capabilities to documentation entities (Entity, ...), once
+ # identified.
+ #
+ # It acts as the event source external to the modal parser (i.e.,
+ # the parsed entity). Expressing a modal behavioural design itself
+ # (around the line queue of a comment block), it produces certain
+ # events which are then signalled to the parsed entity.
+ #
+ Class create CommentBlockParser {
+
+ :property {parsing_level:integer 0}
+
+ :property {message ""}
+ :property {status:in "COMPLETED"} {
+
+ set :incremental 1
+
+ set :statuscodes {
+ COMPLETED
+ INVALIDTAG
+ MISSINGPARTOF
+ STYLEVIOLATION
+ LEVELMISMATCH
+ }
+
+ :public method type=in {name value} {
+ if {$value ni ${:statuscodes}} {
+ error "Invalid statuscode '$code'."
+ }
+ return $value
+ }
+
+ :public method ? [list obj var value:in,slot=[current object]] {
+ return [expr {[:get $obj $var] eq $value}]
+ }
+
+ :public method is {obj var value} {
+ return [expr {$value in ${:statuscodes}}]
+ }
+ }
+
+ :property processed_section {
+ :public method assign {domain prop value} {
+ set current_entity [$domain current_entity]
+ set scope [expr {[$current_entity info is class]?"class":""}]
+ if {[$domain eval [list info exists :$prop]] && [:get $domain $prop] in [$current_entity {*}$scope info mixin classes]} {
+ $current_entity {*}$scope mixin delete [:get $domain $prop]
+ }
+ $current_entity {*}$scope mixin add [next [list $domain $prop $value]]
+ }
+ }
+ :property current_entity:object
+
+ :public class method process {
+ {-partof_entity ""}
+ {-initial_section context}
+ {-parsing_level 0}
+ -entity
+ block
+ } {
+
+ if {![info exists entity]} {
+ set entity [Entity]
+ }
+
+ set parser_obj [:new -current_entity $entity -parsing_level $parsing_level]
+ $parser_obj [current proc] \
+ -partof_entity $partof_entity \
+ -initial_section $initial_section \
+ $block
+ return $parser_obj
+ }
+
+ :public forward has_next expr {${:idx} < [llength ${:comment_block}]}
+ :public method dequeue {} {
+ set r [lindex ${:comment_block} ${:idx}]
+ incr :idx
+ return $r
+ }
+ :public forward rewind incr :idx -1
+ :public forward fastforward set :idx {% llength ${:comment_block}}
+
+ :public method cancel {statuscode {msg ""}} {
+ :fastforward
+ :status $statuscode
+ :message $msg
+ uplevel 1 [list ::return -code error $statuscode]
+ }
+ #
+ # everything below assumes that the current class is an active mixin
+ # on an instance of an Entity subclass!
+ #
+
+ :public method process {
+ {-partof_entity ""}
+ {-initial_section context}
+ block
+ } {
+
+ set :comment_block $block
+ set :idx 0
+
+ :processed_section [$initial_section]
+
+ # TODO: currently, default values are not initialised for
+ # property slots defined in mixin classes; so do it manually
+ # for the time being.
+ ${:current_entity} current_comment_line_type ""
+
+ ${:current_entity} block_parser [current]
+ ${:current_entity} eval [list set :partof_entity $partof_entity]
+
+ set is_first_iteration 1
+ # set failure ""
+
+ #
+ # Note: Within the while-loop, two object variables constantly
+ # change (as "wanted" side-effects): processed_section: reflects
+ # the currently processed comment section; see event=next()
+ # current_entity: reflects the currently documentation entity
+ # (once resolved); see context->event=parse@tag()
+ #
+ while {[:has_next]} {
+ set line [:dequeue]
+ if {$is_first_iteration} {
+ ${:current_entity} on_enter $line
+ set is_first_iteration 0
+ }
+
+ if {[catch {
+ ${:current_entity} event=process $line
+ } failure]} {
+ if {![:status is $failure]} {
+ ::return -code error -errorinfo $::errorInfo
+ }
+ }
+ }
+ if {!$is_first_iteration} {
+ ${:current_entity} on_exit $line
+ }
+
+ # ISSUE: In case of some sub-method definitions (namely "info
+ # mixin"), the sub-method entity object for "mixin" replaces the
+ # forward handlers of the mixin relation slot. So, any slot-like
+ # interactions such as delete() won't work anymore. We need to
+ # bypass it by using ::nsf::relation, for the time being. This
+ # is a clear con of the explicit naming of entity objects (or at
+ # least the current scheme)!
+
+ # if {[${:processed_section} info mixinof -scope object ${:current_entity}] ne ""} {
+ # ${:current_entity} {*}$scope mixin delete ${:processed_section}
+ # }
+
+ set scope [expr {[${:current_entity} info is class]?"class":""}]
+ set mixins [${:current_entity} {*}$scope info mixin classes]
+ if {${:processed_section} in $mixins} {
+ set idx [lsearch -exact $mixins ${:processed_section}]
+ set mixins [lreplace $mixins $idx $idx]
+ ::nsf::relation ${:current_entity} object-mixin $mixins
+ }
+
+ }; # CommentBlockParser->process()
+
+ }
+
+ Class create CommentBlockParsingState -superclass Class {
+
+ :property next_comment_section
+ :property comment_line_transitions:required
+
+ }
+
+ Class create CommentSection {
+
+ :property block_parser:object,type=::nx::doc::CommentBlockParser
+ :property {current_comment_line_type ""}
+
+ set :line_types {
+ tag {regexp -- {^\s*@[^[:space:]@]+} $line}
+ text {regexp -- {^\s*([^[:space:]@]+|@[[:space:]@]+)} $line}
+ space {expr {$line eq {}}}
+ }
+
+ :method get_transition {src_line_type tgt_line} {
+ set section [${:block_parser} processed_section]
+ array set transitions [$section comment_line_transitions]
+ # expected outcome
+ # 1. new state -> becomes current_comment_line
+ # 2. actions to be triggered from the transition
+
+ foreach {line_type expression} [[current class] eval {set :line_types}] {
+ set line $tgt_line
+ if {[eval $expression]} {
+ set tgt_line_type $line_type
+ break
+ }
+ }
+
+ if {![info exists tgt_line_type]} {
+ error "Could not resolve the type of line '$line'"
+ }
+
+ if {![info exists transitions(${src_line_type}->${tgt_line_type})]} {
+ set msg "Style violation in a [namespace tail [:info class]] section:\n"
+ if {$src_line_type eq ""} {
+ append msg "Invalid first line ('${tgt_line_type}')"
+ } else {
+ append msg "A ${src_line_type} line is followed by a ${tgt_line_type} line"
+ }
+ ${:block_parser} cancel STYLEVIOLATION $msg
+ # [StyleViolation new -message $msg] throw
+ }
+ return [list $tgt_line_type $transitions(${src_line_type}->${tgt_line_type})]
+ }
+
+ # the actual events to be signalled to and sensed within the
+ # super-states and sub-states
+
+ :public method event=process {line} {
+ lassign [:get_transition ${:current_comment_line_type} $line] \
+ :current_comment_line_type actions
+ foreach action $actions {
+ :event=$action $line
+ }
+ }
+
+ :public forward event=parse %self {% subst {parse@${:current_comment_line_type}}}
+ :method event=next {line} {
+ set next_section [[${:block_parser} processed_section] next_comment_section]
+ :on_exit $line
+
+ ${:block_parser} rewind
+ :current_comment_line_type ""
+
+ ${:block_parser} processed_section [$next_section]
+ :on_enter $line
+ }
+
+
+ # realise the sub-state (a variant of METHOD-FOR-STATES) and their
+ # specific event handling
+ # set :lineproc {{tag args} {return [concat {*}$args]}}
+ # set :lineproc {{tag args} {puts stderr LINE=[list $tag {*}$args]; return [list $tag {*}$args]}}
+ set :lineproc {{tag args} {return [list $tag [expr {$args eq ""?$args:[list $args]}]]}}
+ :method parse@tag {line} {
+ lassign [apply [[current class] eval {set :lineproc}] {*}$line] tag line
+ #set line [lassign [apply [[current class] eval {set :lineproc}] {*}$line] tag]
+ if {[:info lookup methods -source application $tag] eq ""} {
+ set msg "The tag '$tag' is not supported for the entity type '[namespace tail [:info class]]"
+ ${:block_parser} cancel INVALIDTAG $msg
+ }
+ #:$tag [lrange $line 1 end]
+ #:$tag {*}[expr {$line eq ""?$line:[list $line]}]
+ #:$tag $line
+ :$tag {*}$line
+ }
+
+ :method parse@text {line} {
+ :@doc add $line end
+ }
+ :method parse@space {line} {;}
+
+ #
+ # so far, we only need enter and exit handlers at the level of the
+ # superstates: context, description, part
+ #
+ :public method on_enter {line} {;}
+ :public method on_exit {line} {;}
+ }
+
+ # NOTE: add these transitions for supporting multiple text lines for
+ # the context element
+ # tag->text parse
+ # text->text parse
+ # text->space ""
+
+
+ CommentBlockParsingState create context -superclass CommentSection \
+ -next_comment_section description \
+ -comment_line_transitions {
+ ->tag parse
+ tag->space ""
+ space->space ""
+ space->text next
+ space->tag next
+ } {
+
+ :method resolve_partof_entity {tag name} {
+ # a) unqualified: attr1
+ # b) qualified: Bar#attr1
+ if {[regexp -- {([^\s#]*)#([^\s#]*)} $name _ qualifier nq_name]} {
+ # TODO: Currently, I only foresee @object and @command as
+ # possible qualifiers; however, this should be fixed asap, as
+ # soon as the variety of entities has been decided upon!
+ foreach entity_type {@class @command @object} {
+ set partof_entity [$entity_type id $qualifier]
+ # TODO: Also, we expect the qualifier to resolve against an
+ # already existing entity object? Is this intended?
+ if {[::nsf::is object $partof_entity]} {
+ return [list $nq_name $partof_entity]
+ }
+ }
+ return [list $nq_name ${:partof_entity}]
+ } else {
+ return [list $name ${:partof_entity}]
+ }
+ }
+
+ set :lineproc {{tag name args} {return [list $tag $name $args]}}
+ :method parse@tag {line} {
+ lassign [apply [[current class] eval {set :lineproc}] {*}$line] axes names args
+ set entity ${:partof_entity}
+ set axes [split [string trimleft $axes @] .]
+
+ # 1) get the parsing level from the comment block parser
+ set start_idx [lindex [lsearch -all -not -exact $axes ""] 0]
+
+ set pl [${:block_parser} parsing_level]
+ if {$pl != $start_idx} {
+ ${:block_parser} cancel LEVELMISMATCH "Parsing level mismatch: Tag is meant for level '$start_idx', we are at '$pl'."
+ }
+
+ # 2) stash away a number of empty axes according to the parsing level
+ set axes [lrange $axes $pl end]
+
+ lassign [::nx::doc::Tag normalise $axes $names] err res
+ if {$err} {
+ ${:block_parser} cancel STYLEVIOLATION $res
+ }
+
+ lassign $res tagpath names
+
+ set leaf(axis) [lindex $tagpath end]
+ set tagpath [lrange $tagpath 0 end-1]
+ set leaf(name) [lindex $names end]
+ set names [lrange $names 0 end-1]
+
+ lassign [::nx::doc::Tag find -strict $tagpath $names $entity] err res
+ if {$err} {
+ ${:block_parser} cancel INVALIDTAG $res
+ }
+
+ set entity $res
+
+ if {$entity eq ""} {
+ set cmd [info commands @$leaf(axis)]
+
+ # TODO interp-aliasing objects under different command names
+ # is currently not transparent to some ::nsf::* helpers,
+ # such as ::nsf::object::exists. Should this be changed?
+ #
+ if {$cmd ne ""} {
+ set cmd [namespace origin $cmd]
+ set target [interp alias {} $cmd]
+ if {$target ne ""} {
+ set cmd $target
+ }
+ }
+
+ if {$cmd eq "" || ![::nsf::object::exists $cmd] || \
+ ![$cmd info has type Tag]} {
+
+ ${:block_parser} cancel INVALIDTAG "The entity type '@$leaf(axis)' is not available."
+ }
+
+ # VERIFY! Still an issue? TODO: @object-method raises some
+ # issues (at least when processed without a resolved
+ # context = its partof entity). It is not an entity type,
+ # because it merely is a "scoped" @method. It won't
+ # resolve then as a proper instance of Tag, hence we
+ # observe an InvalidTag exception. For now, we just ignore
+ # and bypass this issue by allowing InvalidTag exceptions
+ # in analyze()
+
+ set entity [@$leaf(axis) new -name $leaf(name) {*}$args]
+ } else {
+ if {[$entity info lookup methods -source application @$leaf(axis)] eq ""} {
+ ${:block_parser} cancel INVALIDTAG \
+ "The tag '$leaf(axis)' is not supported for the entity type '[namespace tail [$entity info class]]'"
+ }
+ set entity [$entity @$leaf(axis) [list $leaf(name) {*}$args]]
+ }
+
+ ${:block_parser} current_entity $entity
+ ${:block_parser} processed_section [current class]
+ $entity current_comment_line_type ${:current_comment_line_type}
+ $entity block_parser ${:block_parser}
+ }
+
+ # :method parse@text {line} { next }
+ # :method parse@space {line} { next }
+
+ }
+
+ CommentBlockParsingState create description -superclass CommentSection \
+ -next_comment_section part \
+ -comment_line_transitions {
+ ->text parse
+ ->tag next
+ text->text parse
+ text->space parse
+ space->text parse
+ space->space parse
+ space->tag next
+ } {
+
+ :public method on_enter {line} {
+ unset -nocomplain :@doc
+ next
+ }
+
+ # tag lines are not allowed in description blocks!
+ # :method parse@tag {line} {;}
+ :method parse@space {line} {
+ :@doc add "" end
+ next
+ }
+
+ }
+
+ CommentBlockParsingState create part -superclass CommentSection \
+ -next_comment_section part \
+ -comment_line_transitions {
+ ->tag parse
+ tag->text parse
+ text->text parse
+ text->tag next
+ text->space ""
+ space->space ""
+ tag->space ""
+ space->tag next
+ tag->tag next
+ } {
+ # realise the parse events specific to the substates of description
+ :public method on_enter {line} {
+ unset -nocomplain :current_part
+ next
+ }
+ :method parse@tag {line} {
+ set r [next]
+ if {[::nsf::object::exists $r] && [$r info has type ::nx::doc::Entity]} {
+ set :current_part $r
+ }
+ return $r
+ }
+ :method parse@text {line} {
+ if {[info exists :current_part]} {
+ ${:current_part} @doc add $line end
+ } else {
+ :event=next $line
+ }
+ }
+ # :method parse@space {line} {;}
+ }
+
+ namespace export CommentBlockParser
+}
\ No newline at end of file
Index: library/lib/nxdoc-html.tcl
===================================================================
diff -u -rfa7635cbfe2309b8e6282e2c7925fa2617b061aa -r187fbd20a453ae9d73e9b48f88b8d6a8c79685c2
--- library/lib/nxdoc-html.tcl (.../nxdoc-html.tcl) (revision fa7635cbfe2309b8e6282e2c7925fa2617b061aa)
+++ library/lib/nxdoc-html.tcl (.../nxdoc-html.tcl) (revision 187fbd20a453ae9d73e9b48f88b8d6a8c79685c2)
@@ -2,12 +2,13 @@
namespace eval ::nx::doc {}
package require nx::doc 1.0
+package require nx::pp
namespace eval ::nx::doc {
Renderer create html {
- :method render {project entity theme {tmplName ""}} {
+ :class method render {project entity theme {tmplName ""}} {
set top_level_entities [$project navigatable_parts]
set init [subst {
set project $project
@@ -18,8 +19,8 @@
$entity render -initscript $init -theme $theme {*}$tmplName
}
- :method installAssets {project theme targetDir} {
- set assets [glob -directory [file join [::nx::doc::find_asset_path] $theme] *]
+ :class method installAssets {project theme targetDir} {
+ set assets [glob -directory [file join [findAssetPath] $theme] *]
file mkdir $targetDir
if {$assets eq ""} return;
file copy -force -- {*}$assets $targetDir
@@ -103,14 +104,28 @@
return "\[[join $js_array ,\n]\]"
}
- :public method navigatable_parts {} {
+ :public method navigatable_parts args {
#
# TODO: Should I wrap up delegating calls to the originator
# entity behind a unified interface (a gatekeeper?)
#
- return [[:origin] owned_parts \
+ set ownedParts [[:origin] owned_parts \
-where "!\${:@stashed}" \
-class ::nx::doc::StructuredEntity]
+
+ foreach mergeParts $args {
+ dict for {feature featureInstances} $mergeParts {
+ if {![dict exists $ownedParts $feature]} {
+ dict set ownedParts $feature $featureInstances
+ } else {
+ set prevInst [lindex [dict get $ownedParts $feature] 1]
+ lappend prevInst {*}$featureInstances
+ dict set ownedParts $feature [lsort -unique $prevInst]
+ }
+ }
+ }
+
+ return $ownedParts
}
:method listing {{-inline true} script} {
@@ -241,12 +256,12 @@
:public method filename {} {
return "index"
}
- :public method navigatable_parts {} {
+ :public method navigatable_parts args {
#
# TODO: Should I wrap up delegating calls to the originator
# entity behind a unified interface (a gatekeeper?)
#
- set top_level_entities [next]
+ set top_level_entities [next [list]]
dict for {feature instances} $top_level_entities {
if {[$feature name] eq "@package"} {
foreach pkg $instances {
@@ -364,7 +379,7 @@
dict set inherited $entity [$entity !get \
-sortedby name \
-with name $member]
- if {[info exists previous_entity]} {
+ if {[info exists previous_entity] && [dict exists $inherited $previous_entity]} {
dict set inherited $previous_entity \
[dict remove [dict get $inherited $previous_entity] \
{*}[dict keys [dict get $inherited $entity]]]
Index: library/lib/nxdoc-xodoc.tcl
===================================================================
diff -u
--- library/lib/nxdoc-xodoc.tcl (revision 0)
+++ library/lib/nxdoc-xodoc.tcl (revision 187fbd20a453ae9d73e9b48f88b8d6a8c79685c2)
@@ -0,0 +1,232 @@
+package provide nx::doc::xodoc 1.0
+namespace eval ::nx::doc::xodoc {}
+
+package require nx::doc 1.0
+
+namespace eval ::nx::doc::xodoc {
+
+ namespace import -force ::nx::*
+ namespace import -force ::nx::doc::*
+
+ # xodoc -> nxdoc
+ # - - - - - - - - - - - - - - - -
+ # MetadataToken Entity
+ # FileToken @package
+ # PackageToken @package
+ # ConstraintToken n/a
+ # MethodToken n/a
+ # ProcToken @method (scope = object)
+ # InstprocToken @method (scope = class)
+ # ObjToken @object
+ # ClassToken @class
+ # MetaClassToken n/a
+
+ Class create MetadataToken {
+ :class property analyzer
+ :public forward analyzer [current] %method
+ :method as {partof:object,type=::nx::doc::StructuredEntity} \
+ -returns object,type=::nx::doc::Entity {
+ error "Subclass responsibility"
+ }
+ :public method emit {partof:object,type=::nx::doc::StructuredEntity} \
+ -returns object,type=::nx::doc::Entity {
+ set entity [:as $partof]
+ set props [:get_properties]
+ if {[dict exists $props description]} {
+ $entity @doc [dict get $props description]
+ }
+ return $entity
+ }
+ :method get_properties {} {
+ if {[info exists :properties]} {
+ set props [dict create]
+ foreach p ${:properties} {
+ if {[info exists :$p]} {
+ dict set props [string tolower $p] \
+ [:format [set :$p]]
+ }
+ }
+ return $props
+ }
+ }
+ :method format {value} {
+ #
+ # 1. replace @-prefixed tags etc.
+ #
+ set value [[:analyzer] replaceFormatTags $value]
+
+ #
+ # 2. escape Tcl evaluation chars in code listings
+ #
+ set value [string map {
+ "\\" "\\\\"
+ "{" "\\{"
+ "}" "\\}"
+ "\"" "\\\""
+ "[" "\\["
+ "]" "\\]"
+ "$" "\\$"
+ } $value]
+
+ #
+ # 3. box the prop value in a list (this avoids unwanted
+ # interactions with the line-by-line as_text post-processor)
+ #
+ return [list $value]
+ }
+ }
+
+ Class create PackageToken -superclass MetadataToken
+ Class create FileToken -superclass MetadataToken {
+ :method as {partof:object,type=::nx::doc::StructuredEntity} \
+ -returns object,type=::nx::doc::Entity {
+ #
+ # TODO: Where to retrieve the package name from?
+ #
+ return [@package new -name XOTcl]
+ }
+ :public method emit {partof:object,type=::nx::doc::StructuredEntity} \
+ -returns object,type=::nx::doc::Entity {
+ set entity [next]
+ set props [dict remove [:get_properties] description]
+ dict for {prop value} $props {
+ $entity @doc add "$prop [join $value]" end
+ }
+ $entity @namespace [[$entity current_project] @namespace]
+ return $entity
+ }
+ }
+
+ #
+ # Note: For whatever reason, InstprocToken is provided but never
+ # used, at least in XOTcl-langRef. while most probably due to a lack
+ # of attention or a silent revocation of a design decision in xodoc,
+ # it forces us into code replication for differentiating the
+ # per-class and per-object scopes ... in xodoc, these scopes are
+ # double-encoded, both in proper token subclassifications as well as
+ # aggregation properties: procList, instprocList ... well, I will
+ # have to live with it.
+ #
+
+ Class create MethodToken -superclass MetadataToken
+
+ Class create ProcToken -superclass MethodToken {
+ :method as {scope partof:object,type=::nx::doc::StructuredEntity} \
+ -returns object,type=::nx::doc::Entity {
+ return [$partof @${scope}-method [:name]]
+ }
+ :public method emit {scope partof:object,type=::nx::doc::StructuredEntity} {
+ set entity [:as $scope $partof]
+ set props [:get_properties]
+ if {[dict exists $props description]} {
+ $entity @doc [dict get $props description]
+ }
+ if {[dict exists $props return]} {
+ $entity @return [dict get $props return]
+ }
+ return $entity
+ }
+ }
+
+ Class create InstprocToken -superclass MethodToken
+
+ Class create ObjToken -superclass MetadataToken {
+ :method as {partof:object,type=::nx::doc::ContainerEntity} \
+ -returns object,type=::nx::doc::Entity {
+ return [@object new -name [:name]]
+ }
+ :public method emit {entity:object,type=::nx::doc::Entity} \
+ -returns object,type=::nx::doc::Entity {
+ set entity [next]
+ foreach p [:procList] {
+ $p emit object $entity
+ }
+ return $entity
+ }
+ }
+
+ Class create ClassToken -superclass ObjToken {
+ :method as {partof:object,type=::nx::doc::ContainerEntity} \
+ -returns object,type=::nx::doc::Entity {
+ return [@class new -name [:name]]
+ }
+ :public method emit {entity:object,type=::nx::doc::Entity} \
+ -returns object,type=::nx::doc::Entity {
+ set entity [next]
+ foreach iproc [:instprocList] {
+ $iproc emit class $entity
+ }
+ return $entity
+ }
+ }
+
+ Class create MetaClassToken -superclass ClassToken
+
+ namespace export MetadataToken FileToken MethodToken ProcToken \
+ InstprocToken ObjToken ClassToken MetaClassToken
+
+ @project eval {
+ :protected method "frontend xodoc" {srcs cmds} {
+
+ set aSrcs [dict filter $srcs script {k v} { dict with v {expr {!$dependency}} }]
+
+ #
+ # Note: Expects the XOTcl2 utilities to be in place and
+ # accessible by the [package req] mechanism, use e.g.:
+ # export TCLLIBPATH=". ./library/xotcl/library/lib"
+ #
+ package req xotcl::xodoc
+ namespace eval :: {namespace import -force ::xotcl::@}
+
+ set docdb [XODoc new]
+ ::@ set analyzerObj $docdb
+
+ foreach m [namespace eval ::nx::doc::xodoc {namespace export}] {
+ if {[::xotcl::Class info instances -closure ::xotcl::metadataAnalyzer::$m] ne ""} {
+ ::xotcl::metadataAnalyzer::$m instmixin add ::nx::doc::xodoc::$m
+ }
+ }
+
+ dict for {s info} $aSrcs {
+ dict with info {
+ if {![info exists script]} continue
+ $docdb analyzeFile $path
+ unset script
+
+ ::nx::doc::xodoc::MetadataToken eval [list set :analyzer $docdb]
+ set provided_entites [list]
+ #
+ # as we analyze file by file, there is only one FileToken to
+ # be molded into an @package
+ #
+ set ft [::xotcl::metadataAnalyzer::FileToken allinstances]
+ if {[llength $ft] > 1} {
+ error "Too many xodoc file tokens processed. Expecting just one!"
+ }
+
+ :@namespace "::xotcl"
+ # ::nx::doc::QualifierTag mixin add ::nx::doc::ContainerEntity::Resolvable
+ # ::nx::doc::ContainerEntity::Resolvable container $project
+
+ # foreach {attr part_class} [$project part_attributes] {
+ # $part_class class mixin add ::nx::doc::ContainerEntity::Containable
+ # $part_class container $project
+ # }
+
+ set partof [current]
+ if {$ft ne ""} {
+ set pkg [$ft emit [current]]
+ lappend provided_entities $pkg
+ set partof $pkg
+ }
+
+ foreach token [::xotcl::metadataAnalyzer::ObjToken allinstances] {
+ lappend provided_entities [$token emit $partof]
+ }
+
+ }
+ }
+ return $provided_entities
+ }
+ }
+}
Index: library/lib/nxdoc-xowiki.tcl
===================================================================
diff -u -rfa7635cbfe2309b8e6282e2c7925fa2617b061aa -r187fbd20a453ae9d73e9b48f88b8d6a8c79685c2
--- library/lib/nxdoc-xowiki.tcl (.../nxdoc-xowiki.tcl) (revision fa7635cbfe2309b8e6282e2c7925fa2617b061aa)
+++ library/lib/nxdoc-xowiki.tcl (.../nxdoc-xowiki.tcl) (revision 187fbd20a453ae9d73e9b48f88b8d6a8c79685c2)
@@ -18,7 +18,7 @@
\[\[$basename|$source_anchor\]\]
}
- :method render {project entity theme {tmplName ""}} {
+ :class method render {project entity theme {tmplName ""}} {
@@ -36,7 +36,7 @@
return [$p serialize]
}
- :method installAssets {project theme targetDir} {
+ :class method installAssets {project theme targetDir} {
#
# render and append single glossary page to the output
#
@@ -60,7 +60,7 @@
#
# TODO: assets (js, css, img must be wrapped as ::xowiki::Files)
#
- set assets [glob -directory [file join [::nx::doc::find_asset_path] $theme] *]
+ set assets [glob -directory [file join [findAssetPath] $theme] *]
array set mime {
js application/x-javascript
Index: library/lib/pkgIndex.tcl
===================================================================
diff -u -re52f2dd0f35e8a12230a20e90575f242da4e0e5c -r187fbd20a453ae9d73e9b48f88b8d6a8c79685c2
--- library/lib/pkgIndex.tcl (.../pkgIndex.tcl) (revision e52f2dd0f35e8a12230a20e90575f242da4e0e5c)
+++ library/lib/pkgIndex.tcl (.../pkgIndex.tcl) (revision 187fbd20a453ae9d73e9b48f88b8d6a8c79685c2)
@@ -9,7 +9,9 @@
# full path name of this file's directory.
package ifneeded nx::doc 1.0 [list source [file join $dir nxdoc-core.tcl]]
+package ifneeded nx::doc::dc 1.0 [list source [file join $dir nxdoc-dc.tcl]]
package ifneeded nx::doc::html 1.0 [list source [file join $dir nxdoc-html.tcl]]
+package ifneeded nx::doc::xodoc 1.0 [list source [file join $dir nxdoc-xodoc.tcl]]
package ifneeded nx::doc::xowiki 1.0 [list source [file join $dir nxdoc-xowiki.tcl]]
package ifneeded nx::pp 1.0 [list source [file join $dir pp.tcl]]
package ifneeded nx::test 1.0 [list source [file join $dir test.tcl]]
Index: tests/doc.test
===================================================================
diff -u -rfa7635cbfe2309b8e6282e2c7925fa2617b061aa -r187fbd20a453ae9d73e9b48f88b8d6a8c79685c2
--- tests/doc.test (.../doc.test) (revision fa7635cbfe2309b8e6282e2c7925fa2617b061aa)
+++ tests/doc.test (.../doc.test) (revision 187fbd20a453ae9d73e9b48f88b8d6a8c79685c2)
@@ -24,6 +24,11 @@
# --
+#
+# Source the "Document Comment" backend
+#
+package require nx::doc::dc
+
Test case scanning {
set lines {
@@ -49,8 +54,9 @@
}
+ set ::prj [@project new -name _PROJECT_]
foreach {::line ::result} $lines {
- ? {foreach {is_comment text} [processor analyze_line $::line] break; set is_comment} $::result "processor analyze_line '$::line'"
+ ? {foreach {is_comment text} [$::prj analyze_line $::line] break; set is_comment} $::result "processor analyze_line '$::line'"
}
set script {
@@ -81,20 +87,22 @@
set blocks {1 {{ @package o} { 1 2 3}} 5 {{ @object o} { 1 2 3} {} { 345} { @tag1 part1} { @tag2 part2}} 17 {{ @object o # ####} { 1 2 3} {} { 345} { @tag1 part1} { @tag2 part2}}}
- ? [list ::lcompare [processor comment_blocks $script] $blocks] 1
+ ? [list ::lcompare [$::prj comment_blocks $script] $blocks] 1
}
-
+
Test case parsing {
+ set ::prj [@project new -name _PROJECT_]
+
+ namespace import -force ::nx::doc::CommentBlockParser
#
# TODO: Add tests for doc-parsing state machine.
#
set block {
{@command ::cc}
}
+ set ::cbp [CommentBlockParser process $block]
+ ? [list $::cbp status ? COMPLETED] 1
- set cbp [CommentBlockParser process $block]
- ? [list $cbp status ? COMPLETED] 1
-
set block {
{}
}
@@ -285,8 +293,13 @@
set cbp [CommentBlockParser process $block]
? [list $cbp status ? INVALIDTAG] 1
-
+
#
+ # TODO: Where shall we allow the @author tag?! Re-activate
+ # later, if necessary ...
+ #
+ if {0} {
+ #
# testing the doc object construction
#
set block {
@@ -300,13 +313,17 @@
}
set cbp [CommentBlockParser process $block]
+
? [list $cbp status ? COMPLETED] 1
+
set entity [$cbp current_entity]
? [list ::nsf::is object $entity] 1
? [list $entity info has type ::nx::doc::@object] 1
? [list $entity @author] "stefan.sobernig@wu.ac.at gustaf.neumann@wu-wien.ac.at";
? [list $entity as_text] "some more text and another line for the description";
+ }
+
set block {
{@command ::c}
{}
@@ -346,6 +363,7 @@
}
+ if {0} {
Test case in-situ-basics {
#
# basic test for in-situ documentation (initcmd block)
@@ -379,9 +397,10 @@
:method foo {a b} {;}
}
}
+
+ # set prj [processor process -sandboxed -type eval $script]
+ set prj [@project new -name _PROJECT_]
- set prj [processor process -sandboxed -type eval $script]
-
set entity [@class id ::Foo]
? [list ::nsf::is object $entity] 1
? [list $entity info has type ::nx::doc::@class] 1
@@ -1068,272 +1087,10 @@
? [list $entity eval {set :@c-implemented}] 1
? [list $entity @c-implemented] 1
? [list $entity eval {set :@syshook}] 1
- ? [list $entity @syshook] 1
-
-
+ ? [list $entity @syshook] 1
}
-
- if {0} {
- puts stderr =================================================
-
- # TODO: Figure out where to place nsf.nxd for convenient location ...
-
- puts stderr >>>>>>>NextScriptingFramework<<<<<<<<
- set project [::nx::doc::@project new \
- -name NextScriptingFramework \
- -url http://www.next-scripting.org/ \
- -version 1.0.0a \
- -@namespace "::nsf" \
- -sources {
- package nsf
- }]
-
- set ::nsf::includes {
- ::nsf::alias
- ::nsf::configure
- ::nsf::current
- ::nsf::finalize
- ::nsf::interp
- ::nsf::is
- ::nsf::log
- ::nsf::my
- ::nsf::next
- ::nsf::relation
- ::nsf::tmpdir
- ::nsf::assertion
- ::nsf::objectsystem::create
- ::nsf::dispatch
- ::nsf::var::exists
- ::nsf::exithandler
- ::nsf::forward
- ::nsf::var::import
- ::nsf::object::exists
- ::nsf::method
- ::nsf::method::property
- ::nsf::method::provide
- ::nsf::object::qualify
- ::nsf::method::require
- ::nsf::setter
- ::nsf::var::set
- }
-
- set project [processor process \
- -sandboxed \
- -validate \
- -include $::nsf::includes $project]
-
- ::nx::doc::make doc \
- -format html \
- $project \
- -theme yuidoc \
- -layout many-to-many \
- -outdir [::nsf::tmpdir]
-
- ::nx::doc::make doc \
- -format xowiki \
- $project \
- -theme yuidoc \
- -layout many-to-1 \
- -outdir [::nsf::tmpdir]
-
-
-
- puts stderr >>>>>>>NextScriptingLanguage<<<<<<<<
-
- set ::nx::excludes {
- ::nsf::parametersfromslots
- ::nx::infoOptions
- ::nx::isSlotContainer
- ::nx::slotObj
- }
-
- # lappend ::nx::excludes {*}::nsf::classes::nx::Object::[join {
- # __default_property_call_protection
- # __default_method_call_protection
- # __resolve_method_path
- # cleanup
- # defaultmethod
- # init
- # objectparameter
- # residualargs
- # uplevel
- # upvar
- # } " ::nsf::classes::nx::Object::"]
-
- set project [::nx::doc::@project new \
- -name NextScriptingLanguage \
- -url http://www.next-scripting.org/ \
- -version 1.0.0a \
- -@namespace "::nx" \
- -sources {
- package nx
- } -depends $project]
-
- dict set timings process [time {
-
- # ISSUE: If calling '-namespace "::nx"' instead of '-@namespace
- # "::nx"', we get an irritating failure. VERIFY!
- processor process \
- -validate \
- -sandboxed \
- -exclude $::nx::excludes \
- $project
- } 1]
-
-
- dict set timings make.html.yuidoc [time {
- ::nx::doc::make doc $project \
- -outdir [::nsf::tmpdir]
- } 1]
-
- dict set timings make.html.asciidoc [time {
- ::nx::doc::make doc \
- -format html \
- $project \
- -theme asciidoc \
- -layout 1-to-1 \
- -outdir [::nsf::tmpdir]
- } 1]
-
- dict set timings make.xowiki.yuidoc [time {
- ::nx::doc::make doc \
- -format xowiki \
- $project \
- -theme yuidoc \
- -layout many-to-1 \
- -outdir [::nsf::tmpdir]
- } 1]
-
- dict for {probe t} $timings {
- puts stderr "\t$probe -> $t"
- }
-
- set project [::nx::doc::@project new \
- -name XOTcl2 \
- -url http://www.xotcl.org/ \
- -version 2.0.0 \
- -@namespace "::xotcl" \
- -sources {
- @package XOTcl-langRef
- }]
-
- processor process \
- -sandboxed \
- $project
-
- ::nx::doc::make doc \
- -format html \
- $project \
- -theme yuidoc \
- -outdir [::nsf::tmpdir]
-
- ::nx::doc::make doc \
- -format xowiki \
- $project \
- -theme yuidoc \
- -layout many-to-1 \
- -outdir [::nsf::tmpdir]
-
}
+
# # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # #
-
- # 1) Test case scoping rules -> in Object->eval()
-
- Test case issues? {
-
- # TODO: where to locate the @ comments (in predefined.xotcl, in
- # gentclAPI.decls)? how to deal with ::nsf::* vs. ::nx::*
-
- # TODO: which values are returned from Object->configure() and
- # passed to init()? how to document residualargs()?
-
- # TODO: Object->cleanup() said: "Resets an object or class into an
- # initial state, as after construction." If by construction it means
- # after create(), then cleanup() is missing a configure() call to
- # set defaults, etc!
- # ?? cleanup does not set defaults; depending on "softrecreate", it
- # deletes instances, childobjects, procs, instprocs, ...
-
- # TODO: what is Object->__next() for?
-
- # See the following script:
- #
-
- # Object instproc defaultmethod {} {puts "[self proc]"; return [self]}
- # Class A
- # A instproc defaultmethod {} {puts "[self proc]"; [::xotcl::my info parent] __next}
- # Class D -instproc t {} {puts "my stuff"}
- # D create d1
- # puts [d1 t]
- # ### we create a subobject named t, which shadows effectively D->t
- # A create d1::t
- # puts ===
- # # when we call "d1 t", we effectively call "d1::t", which calls "default method".
- # # the defaultmethod should do a next on object d1.
- # puts [d1 t]
- # puts ===EXIT
-
- # but seems - at least in this usecase broken. Deactivated
- # in source for now.
-
- # TODO: why is XOTclOUplevelMethodStub/XOTclOUplevelMethod defined
- # with "args" while it logically uses the stipulated parameter
- # signature (level ...). is this because of the first pos, optional
- # parameter? ... same goes for upvar() ...
-
- # the logic is a tribute to the argument logic in Tcl, which complex.
- # uplevel ?level? arg ?arg ...?
- # It is a combination between an optional first argument and
- # and an args logic.
- #
- # Most likely, it could be partly solved with a logic for optional
- # first arguments (if the number of actual arguments is
- # higher than the minimal number of arguments, one could fill optional
- # parameter up..... but this calculation requires as well the interactions
- # with nonpos arguments, which might be values for positional arguments
- # as well.... not, sure, it is worth to invest much time here.
-
- # TODO: how is upvar affected by the ":"-prefixing? -> AVOID_RESOLVERS ...
-
- # this is a tcl question, maybe version dependent.
-
-
-
- # TODO: the objectsystems subcommand of ::nsf::configure does
- # not really fit in there because it does not allow for configuring
- # anything. it is a mere introspection-only command. relocate (can
- # we extend standard [info] somehow, i.e., [info objectsystems]
-
- # what means "configuring anything"?
- # maybe you are refering to "configure objectsystems" which is parked there.
- # there would be an option to change the internally called methods via
- # configure as well, but i think, one is asking for troubles by allowing
- # this.
- # extending info is possible via the shadowcommands, but the tct
- # does not like it.
- #
- # ad configure: we could fold as well methodproperty and
- # objectproperty into configure since these allow as well setting
- # and querying....
- #
- # configure method METHODHANDLE public
- # configure object OBJECT metaclass
- #
- # but there, the object property is just for quering.
- # Another option is define and "info"
- #
- # ::nsf::info object OBJECT metaclass
- # ::nsf::info objectsystems
- #
- # but if we would fold these into tcl-info, conflicts with
- # tcl will arise.
-
- # ISSUE: Object->info->parameter() still needed to retrieve
- # objectparameters?
-
- # TODO: decide how to deal with @package and @project names (don't
- # need namespace delimiters!)
-
- }