Worked on literate output using Markdown as the text markup language.

* Added CSS based on Docco (blatantly copied).
* Updated sample text to better fit the emerging usage patterns. Some of the
  things I did to make it render nicely for the Literate output may cause
  problems when we go to render API output. I will cross that bridge when I come
  to it.
* Added parsing infrastructure to the Generator behaviour to allow a
  pre-processing pass over the document. Current the LiterateMarkdownGenerator
  is using this to compile a map of `@org` references.
* Tweaked the HTML output slightly.
* Added a layer over the PegDownProcessor in LiterateMarkdownGenerator to
  capture and transform `jlp://` links into relative links.
This commit is contained in:
Jonathan Bernard 2011-09-06 16:13:21 -05:00
parent c2c2f9da3d
commit 7a5870dc09
8 changed files with 520 additions and 90 deletions

2
doc/issues/0000tn3.rst Normal file
View File

@ -0,0 +1,2 @@
Implement working internal links using ``jlp://`` schema.
=========================================================

1
doc/issues/0001tn3.rst Normal file
View File

@ -0,0 +1 @@
Implement language-specific code parsing and comprehension.

193
resources/main/docco.css Normal file
View File

@ -0,0 +1,193 @@
/*--------------------- Layout and Typography ----------------------------*/
body {
font-family: 'Palatino Linotype', 'Book Antiqua', Palatino, FreeSerif, serif;
font-size: 15px;
line-height: 22px;
color: #252519;
margin: 0; padding: 0;
}
a { color: #261a3b; }
a:visited { color: #261a3b; }
p { margin: 0 0 15px 0; }
h1, h2, h3, h4, h5, h6 { margin: 0px 0 15px 0; }
h1 { margin-top: 40px; }
dt { font-weight: bold; }
ul {
margin: 0;
padding: 0 0 0 2em; }
#container { position: relative; }
#background {
position: fixed;
top: 0; left: 525px; right: 0; bottom: 0;
background: #f5f5ff;
border-left: 1px solid #e5e5ee;
z-index: -1;
}
#jump_to, #jump_page {
background: white;
-webkit-box-shadow: 0 0 25px #777; -moz-box-shadow: 0 0 25px #777;
-webkit-border-bottom-left-radius: 5px; -moz-border-radius-bottomleft: 5px;
font: 10px Arial;
text-transform: uppercase;
cursor: pointer;
text-align: right;
}
#jump_to, #jump_wrapper {
position: fixed;
right: 0; top: 0;
padding: 5px 10px;
}
#jump_wrapper {
padding: 0;
display: none;
}
#jump_to:hover #jump_wrapper {
display: block;
}
#jump_page {
padding: 5px 0 3px;
margin: 0 0 25px 25px;
}
#jump_page .source {
display: block;
padding: 5px 10px;
text-decoration: none;
border-top: 1px solid #eee;
}
#jump_page .source:hover {
background: #f5f5ff;
}
#jump_page .source:first-child {
}
table td {
border: 0;
outline: 0;
}
td.docs, th.docs {
max-width: 450px;
min-width: 450px;
min-height: 5px;
padding: 10px 25px 1px 50px;
overflow-x: hidden;
vertical-align: top;
text-align: left;
}
.docs pre {
background: #f8f8ff;
border: 1px solid #dedede;
margin: 15px 0 15px;
padding-left: 15px;
}
.docs p tt, .docs p code {
background: #f8f8ff;
border: 1px solid #dedede;
font-size: 12px;
padding: 0 0.2em;
}
.pilwrap {
position: relative;
}
.pilcrow {
font: 12px Arial;
text-decoration: none;
color: #454545;
position: absolute;
top: 3px; left: -20px;
padding: 1px 2px;
opacity: 0;
-webkit-transition: opacity 0.2s linear;
}
td.docs:hover .pilcrow {
opacity: 1;
}
td.code, th.code {
padding: 14px 15px 16px 25px;
width: 100%;
vertical-align: top;
background: #f5f5ff;
border-left: 1px solid #e5e5ee;
}
pre, tt, code {
font-size: 12px; line-height: 18px;
font-family: Menlo, Monaco, Consolas, "Lucida Console", monospace;
margin: 0; padding: 0;
}
/*---------------------- Syntax Highlighting -----------------------------*/
td.linenos { background-color: #f0f0f0; padding-right: 10px; }
span.lineno { background-color: #f0f0f0; padding: 0 5px 0 5px; }
body .hll { background-color: #ffffcc }
body .c { color: #408080; font-style: italic } /* Comment */
body .err { border: 1px solid #FF0000 } /* Error */
body .k { color: #954121 } /* Keyword */
body .o { color: #666666 } /* Operator */
body .cm { color: #408080; font-style: italic } /* Comment.Multiline */
body .cp { color: #BC7A00 } /* Comment.Preproc */
body .c1 { color: #408080; font-style: italic } /* Comment.Single */
body .cs { color: #408080; font-style: italic } /* Comment.Special */
body .gd { color: #A00000 } /* Generic.Deleted */
body .ge { font-style: italic } /* Generic.Emph */
body .gr { color: #FF0000 } /* Generic.Error */
body .gh { color: #000080; font-weight: bold } /* Generic.Heading */
body .gi { color: #00A000 } /* Generic.Inserted */
body .go { color: #808080 } /* Generic.Output */
body .gp { color: #000080; font-weight: bold } /* Generic.Prompt */
body .gs { font-weight: bold } /* Generic.Strong */
body .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
body .gt { color: #0040D0 } /* Generic.Traceback */
body .kc { color: #954121 } /* Keyword.Constant */
body .kd { color: #954121; font-weight: bold } /* Keyword.Declaration */
body .kn { color: #954121; font-weight: bold } /* Keyword.Namespace */
body .kp { color: #954121 } /* Keyword.Pseudo */
body .kr { color: #954121; font-weight: bold } /* Keyword.Reserved */
body .kt { color: #B00040 } /* Keyword.Type */
body .m { color: #666666 } /* Literal.Number */
body .s { color: #219161 } /* Literal.String */
body .na { color: #7D9029 } /* Name.Attribute */
body .nb { color: #954121 } /* Name.Builtin */
body .nc { color: #0000FF; font-weight: bold } /* Name.Class */
body .no { color: #880000 } /* Name.Constant */
body .nd { color: #AA22FF } /* Name.Decorator */
body .ni { color: #999999; font-weight: bold } /* Name.Entity */
body .ne { color: #D2413A; font-weight: bold } /* Name.Exception */
body .nf { color: #0000FF } /* Name.Function */
body .nl { color: #A0A000 } /* Name.Label */
body .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */
body .nt { color: #954121; font-weight: bold } /* Name.Tag */
body .nv { color: #19469D } /* Name.Variable */
body .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */
body .w { color: #bbbbbb } /* Text.Whitespace */
body .mf { color: #666666 } /* Literal.Number.Float */
body .mh { color: #666666 } /* Literal.Number.Hex */
body .mi { color: #666666 } /* Literal.Number.Integer */
body .mo { color: #666666 } /* Literal.Number.Oct */
body .sb { color: #219161 } /* Literal.String.Backtick */
body .sc { color: #219161 } /* Literal.String.Char */
body .sd { color: #219161; font-style: italic } /* Literal.String.Doc */
body .s2 { color: #219161 } /* Literal.String.Double */
body .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */
body .sh { color: #219161 } /* Literal.String.Heredoc */
body .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */
body .sx { color: #954121 } /* Literal.String.Other */
body .sr { color: #BB6688 } /* Literal.String.Regex */
body .s1 { color: #219161 } /* Literal.String.Single */
body .ss { color: #19469D } /* Literal.String.Symbol */
body .bp { color: #954121 } /* Name.Builtin.Pseudo */
body .vc { color: #19469D } /* Name.Variable.Class */
body .vg { color: #19469D } /* Name.Variable.Global */
body .vi { color: #19469D } /* Name.Variable.Instance */
body .il { color: #666666 } /* Literal.Number.Integer.Long */

132
resources/main/jlp.css Normal file
View File

@ -0,0 +1,132 @@
body {
font-family: 'Palatine Linotype', 'Book Antiqua', Palatino, FreeSerif, serif;
font-size: 15px;
line-height: 22px;
color: #252519;
margin: 0;
padding 0; }
a { color: #261a3b; }
a:visited { color: #261a3b; }
p{ margin: 0 0 15px 0; }
h1, h2, h3, h4, h5, h6 { margin: 0 0 15px 0; }
h1 { margin-top: 40px; }
dt { font-weight: bold; }
ul {
margin: 0;
padding-top: 0; }
#container { position: relative; }
table td {
border: 0;
outline: 0; }
td.docs, th.docs {
max-width: 450px;
min-width: 450px;
min-height: 5pc;
padding: 10px 25px 1px 50px;
overflow-x: hidden;
vertical-align: top;
text-align: left; }
.docs pre {
background: #f8f8ff;
border: 1px solid #dedede;
margin: 15px 0 15px;
padding-left: 15px; }
.docs p tt, .docs p code {
background: #f8f8ff;
border: 1px solid #dedede;
font-size: 12px;
padding: 0 0.2em; }
.docs table {
border: thin solid #dedede;
margin-left: 30px; }
td.code, th.code {
padding: 14px 15px 16px 25px;
width: 100%;
vertical-align: top;
background: #f5f5ff;
border-left: 1px solid #e5e5ee; }
pre, tt, code {
font-size: 12px;
line-height: 18px;
font-family: Menlo, Monaco, Consolas, "Lucida Console", monospace;
margin: 0; padding: 0; }
/*---------------------- Syntax Highlighting -----------------------------*/
td.linenos { background-color: #f0f0f0; padding-right: 10px; }
span.lineno { background-color: #f0f0f0; padding: 0 5px 0 5px; }
body .hll { background-color: #ffffcc }
body .c { color: #408080; font-style: italic } /* Comment */
body .err { border: 1px solid #FF0000 } /* Error */
body .k { color: #954121 } /* Keyword */
body .o { color: #666666 } /* Operator */
body .cm { color: #408080; font-style: italic } /* Comment.Multiline */
body .cp { color: #BC7A00 } /* Comment.Preproc */
body .c1 { color: #408080; font-style: italic } /* Comment.Single */
body .cs { color: #408080; font-style: italic } /* Comment.Special */
body .gd { color: #A00000 } /* Generic.Deleted */
body .ge { font-style: italic } /* Generic.Emph */
body .gr { color: #FF0000 } /* Generic.Error */
body .gh { color: #000080; font-weight: bold } /* Generic.Heading */
body .gi { color: #00A000 } /* Generic.Inserted */
body .go { color: #808080 } /* Generic.Output */
body .gp { color: #000080; font-weight: bold } /* Generic.Prompt */
body .gs { font-weight: bold } /* Generic.Strong */
body .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
body .gt { color: #0040D0 } /* Generic.Traceback */
body .kc { color: #954121 } /* Keyword.Constant */
body .kd { color: #954121; font-weight: bold } /* Keyword.Declaration */
body .kn { color: #954121; font-weight: bold } /* Keyword.Namespace */
body .kp { color: #954121 } /* Keyword.Pseudo */
body .kr { color: #954121; font-weight: bold } /* Keyword.Reserved */
body .kt { color: #B00040 } /* Keyword.Type */
body .m { color: #666666 } /* Literal.Number */
body .s { color: #219161 } /* Literal.String */
body .na { color: #7D9029 } /* Name.Attribute */
body .nb { color: #954121 } /* Name.Builtin */
body .nc { color: #0000FF; font-weight: bold } /* Name.Class */
body .no { color: #880000 } /* Name.Constant */
body .nd { color: #AA22FF } /* Name.Decorator */
body .ni { color: #999999; font-weight: bold } /* Name.Entity */
body .ne { color: #D2413A; font-weight: bold } /* Name.Exception */
body .nf { color: #0000FF } /* Name.Function */
body .nl { color: #A0A000 } /* Name.Label */
body .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */
body .nt { color: #954121; font-weight: bold } /* Name.Tag */
body .nv { color: #19469D } /* Name.Variable */
body .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */
body .w { color: #bbbbbb } /* Text.Whitespace */
body .mf { color: #666666 } /* Literal.Number.Float */
body .mh { color: #666666 } /* Literal.Number.Hex */
body .mi { color: #666666 } /* Literal.Number.Integer */
body .mo { color: #666666 } /* Literal.Number.Oct */
body .sb { color: #219161 } /* Literal.String.Backtick */
body .sc { color: #219161 } /* Literal.String.Char */
body .sd { color: #219161; font-style: italic } /* Literal.String.Doc */
body .s2 { color: #219161 } /* Literal.String.Double */
body .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */
body .sh { color: #219161 } /* Literal.String.Heredoc */
body .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */
body .sx { color: #954121 } /* Literal.String.Other */
body .sr { color: #BB6688 } /* Literal.String.Regex */
body .s1 { color: #219161 } /* Literal.String.Single */
body .ss { color: #19469D } /* Literal.String.Symbol */
body .bp { color: #954121 } /* Name.Builtin.Pseudo */
body .vc { color: #19469D } /* Name.Variable.Class */
body .vg { color: #19469D } /* Name.Variable.Global */
body .vi { color: #19469D } /* Name.Variable.Instance */
body .il { color: #666666 } /* Literal.Number.Integer.Long */

View File

@ -1,4 +1,5 @@
import com.jdblabs.jlp.* import com.jdblabs.jlp.*
import com.jdblabs.jlp.experimental.LiterateMarkdownGenerator
import org.parboiled.Parboiled import org.parboiled.Parboiled
import org.parboiled.parserunners.ReportingParseRunner import org.parboiled.parserunners.ReportingParseRunner
import org.parboiled.parserunners.RecoveringParseRunner import org.parboiled.parserunners.RecoveringParseRunner
@ -54,3 +55,24 @@ vbsTest = {
return [vbsParsed, vbsResult] return [vbsParsed, vbsResult]
} }
experimentalTest = {
makeExperimentalParser()
println "Parsing vbs_db_records.hrl into 'vbsResult'."
println "--------------------------------------------"
vbsTestFile = new File('vbs_db_records.hrl')
println "vbsTestFile is ${vbsTestFile.exists() ? 'present' : 'absent'}."
vbsTestInput = vbsTestFile.text
vbsParsed = parseRunner.run(vbsTestInput)
vbsResult = LiterateMarkdownGenerator.generateDocuments(["vbs_db_records.hrl": vbsParsed.resultValue])."vbs_db_records.hrl"
println "Writing to file 'vbs_result.html'."
println "----------------------------------"
(new File('vbs_result.html')).withWriter { out -> out.println vbsResult }
}

View File

@ -2,6 +2,8 @@
%% @author Jonathan Bernard <jdb@jdb-labs.com> %% @author Jonathan Bernard <jdb@jdb-labs.com>
%% @copyright 2010-2011 JDB Labs Inc. %% @copyright 2010-2011 JDB Labs Inc.
%% #Overview
%%
%% The VBS database API is centered around the data records: %% The VBS database API is centered around the data records:
%% %%
%% * Tables are named after the records. ``vbs_adult`` records are stored in %% * Tables are named after the records. ``vbs_adult`` records are stored in
@ -14,61 +16,68 @@
%% #Record Definitions %% #Record Definitions
%% Here are the record definitions: %% Here are the record definitions:
%% ## vbs_adult ##
%% @api Information about an adult in the VBS system. %% @api Information about an adult in the VBS system.
%% @org records/vbs_adult %% @org records/vbs_adult
-record(vbs_adult, { -record(vbs_adult, {
%% @api A unique number. This is the record's primary identification and %% @api * A unique number. This is the record's primary identification and
%% the table's primary key %% the table's primary key
%% @org records/vbs_adult/id %% @org records/vbs_adult/id
id, id,
%% @api A unique full name. %% @api * A unique full name.
%% @example "John Smith", "Fae Alice McDonald" %%
%% *Examples:* `"John Smith", "Fae Alice McDonald"`
%% @org records/vbs_adult/name %% @org records/vbs_adult/name
name, name,
%% @api The adult's age (optional). %% @api * The adult's age (optional).
%% @org records/vbs_adult/age %% @org records/vbs_adult/age
age = 0, age = 0,
%% @api A list of phone numbers (strings). %% @api * A list of phone numbers (strings).
%% @example ["512-555-1155", "123-456-7890"] %%
%% *Examples:* `["512-555-1155", "123-456-7890"]`
%% @org records/vbs_adult/phone_numbers %% @org records/vbs_adult/phone_numbers
phone_numbers, phone_numbers,
%% @api The adult's address (optional). There is not pre-defined format, %% @api * The adult's address (optional). There is not pre-defined format,
%% this is a string that can be formatted s desired (linebreaks are ok, %% this is a string that can be formatted s desired (linebreaks are ok,
%% for example). %% for example).
%% @example %%
%% *Example:*
%% %%
%% "123 Grant Drive %% "123 Grant Drive
%% Plainsville, TX, 78707" %% Plainsville, TX, 78707"
%%
%% @org records/vbs_adult/address %% @org records/vbs_adult/address
address = "", address = "",
%% @api The adult's email address as a string. %% @api * The adult's email address as a string.
%% @example "john_smith@mailco.com" %%
%% *Example:* `"john_smith@mailco.com"`
%% @org records/vbs_adult/email %% @org records/vbs_adult/email
email = ""}). email = ""}).
%% ## vbs_attendance ##
%% @api An entry recording a person's attendance. %% @api An entry recording a person's attendance.
%% @org records/vbs_attendance %% @org records/vbs_attendance
-record(vbs_attendance, { -record(vbs_attendance, {
%% @api A unique number. This is the record's primary identification and the %% @api * A unique number. This is the record's primary identification and the
%% table's primary key. %% table's primary key.
%% @org records/vbs_attendance/id %% @org records/vbs_attendance/id
id, id,
%% @api The id of person who attended. This is a foreign key onto either the %% @api * The id of person who attended. This is a foreign key onto either the
%% [`vbs_worker`](jlp://records/vbs_worker) or %% [`vbs_worker`](jlp://records/vbs_worker) or
%% [`vbs_child`](jlp://records/vbs_child) table, depending on the value of %% [`vbs_child`](jlp://records/vbs_child) table, depending on the value of
%% the [`person_type`](jlp://records/vbs_attendance/person_type) field. %% the [`person_type`](jlp://records/vbs_attendance/person_type) field.
%% @org records/vbs_attendance/person_id %% @org records/vbs_attendance/person_id
person_id, person_id,
%% @api The type of person who attended. This determines which table the %% @api * The type of person who attended. This determines which table the
%% [`person_id`](jlp://records/vbs_attendance/person_id) links on. The %% [`person_id`](jlp://records/vbs_attendance/person_id) links on. The
%% possible values and the corresponding link tables are: %% possible values and the corresponding link tables are:
%% %%
@ -80,23 +89,26 @@
%% @org records/vbs_attendance/person_type %% @org records/vbs_attendance/person_type
person_type, person_type,
%% @api The date of attendance, stored as {Year, Month, Day}. %% @api * The date of attendance, stored as `{Year, Month, Day}.`
%% @example {2011, 6, 14} %%
%% *Example:* `{2011, 6, 14}`
%%
%% @org records/vbs_attendance/date %% @org records/vbs_attendance/date
date = {1900, 1, 1}, date = {1900, 1, 1},
%% @api A timestamp taken when the person was signed in, stored as %% @api * A timestamp taken when the person was signed in, stored as
%% {Hour, Minute, Second} %% `{Hour, Minute, Second}`
%% @example {5, 22, 13} %%
%% *Example:* `{5, 22, 13}`
%% @org records/vbs_attendance/sign_in %% @org records/vbs_attendance/sign_in
sign_in = false, % {hour, minute, second} sign_in = false, % {hour, minute, second}
%% @api A timestamp taken when the person is signed out, stored as %% @api * A timestamp taken when the person is signed out, stored as
%% {Hour, Minute, Second} %% `{Hour, Minute, Second}`
%% @org records/vbs_attendance/sign_out %% @org records/vbs_attendance/sign_out
sign_out = false, % {hour, minute, second} sign_out = false, % {hour, minute, second}
%% @api A list of {Key, Value} pairs that can be used to store additional %% @api * A list of {Key, Value} pairs that can be used to store additional
%% information. This is intended to allow callers to store optional data, %% information. This is intended to allow callers to store optional data,
%% or client-specific data, without having to alter the database schema. %% or client-specific data, without having to alter the database schema.
%% When working with `vbs_attendance` records, a caller should ignore %% When working with `vbs_attendance` records, a caller should ignore
@ -104,196 +116,207 @@
%% @org records/vbs_attendance/ext_data %% @org records/vbs_attendance/ext_data
ext_data = [], ext_data = [],
%% @api Any comments for the day about this person. %% @api * Any comments for the day about this person.
%% @org records/vbs_attendance/comments %% @org records/vbs_attendance/comments
comments = ""}). comments = ""}).
%% @api Information about a child in the VBS program. %% ## vbs_child ##
%% @org records/vbs_child %% @org records/vbs_child
%% @api Information about a child in the VBS program.
-record(vbs_child, { -record(vbs_child, {
%% @api A unique number. This is the record's primary identification and the %% @api * A unique number. This is the record's primary identification and the
%% table's primary key. %% table's primary key.
%% @org records/vbs_child/id %% @org records/vbs_child/id
id, id,
%% @api The id of the crew to which this child has been assigned. This is a %% @api * The id of the crew to which this child has been assigned. This is a
%% foreign key linking to a [`vbs_crew.id`](jlp://records/vbs_crew/id). %% foreign key linking to a [`vbs_crew.id`](jlp://records/vbs_crew/id).
%% @org records/vbs_child/crew_id %% @org records/vbs_child/crew_id
crew_id, crew_id,
%% @api The child's full name. %% @api * The child's full name.
%% @example "Mary Scott", "Gregory Brown" %%
%% *Example:* `"Mary Scott", "Gregory Brown"`
%% @org records/vbs_child/name %% @org records/vbs_child/name
name, name,
%% @api The child's date of birth, stored as {Year, Month, Day} %% @api * The child's date of birth, stored as `{Year, Month, Day}`
%% @example {1998, 12, 22} %%
%% *Example:* `{1998, 12, 22}`
%% @org records/vbs_child/date_of_birth %% @org records/vbs_child/date_of_birth
date_of_birth, date_of_birth,
%% @api The child's gender, either `male` or `female` %% @api * The child's gender, either `male` or `female`
%% @org records/vbs_child/gender %% @org records/vbs_child/gender
gender, gender,
%% @api The child's grade level in school. %% @api * The child's grade level in school.
%% @org records/vbs_child/grade %% @org records/vbs_child/grade
grade, grade,
%% @api A list of ids representing the child's legal guardians. These link %% @api * A list of ids representing the child's legal guardians. These link
%% the child record to adult records by the %% the child record to adult records by the
%% [`vbs_adult.id`](jlp://records/vbs_adult/id) %% [`vbs_adult.id`](jlp://records/vbs_adult/id)
%% @example [4, 5] %%
%% *Example:* `[4, 5]`
%% @org records/vbs_child/guardian_ids %% @org records/vbs_child/guardian_ids
guardian_ids, guardian_ids,
%% @api A list of ids, similar to `guardian_ids`, but representing the %% @api * A list of ids, similar to `guardian_ids`, but representing the
%% adults that are allowed to pick the children up. These link the child %% adults that are allowed to pick the children up. These link the child
%% record to adult records by %% record to adult records by
%% ['vbs_adult.id`](jlp://records/vbs_adult/id). %% ['vbs_adult.id`](jlp://records/vbs_adult/id).
%% @org records/vbs_child/pickup_ids %% @org records/vbs_child/pickup_ids
pickup_ids, pickup_ids,
%% @api A list of ids, similar to `guardian_ids` and `pickup_ids`, but %% @api * A list of ids, similar to `guardian_ids` and `pickup_ids`, but
%% representing adults that should be contacted if there is an emergency %% representing adults that should be contacted if there is an emergency
%% involving this child (injury, for example). These link the child record %% involving this child (injury, for example). These link the child record
%% to adult records by [`vbs_adult.id`](jlp://records/vbs_adult/id). %% to adult records by [`vbs_adult.id`](jlp://records/vbs_adult/id).
%% @org records/vbs_child/emerency_ids %% @org records/vbs_child/emerency_ids
emerency_ids, emerency_ids,
%% @api The child's home church, usually used if they are not a member of %% @api * The child's home church, usually used if they are not a member of
%% the hosting church. %% the hosting church.
%% @org records/vbs_child/home_church %% @org records/vbs_child/home_church
home_church, home_church,
%% @api If this child is a visitor, this is used to track who invited them, %% @api * If this child is a visitor, this is used to track who invited them,
%% or who brought them. %% or who brought them.
%% @org records/vbs_child/visitor_of %% @org records/vbs_child/visitor_of
visitor_of, visitor_of,
%% @api Answers the question: Is this child a visitor? Valid values are %% @api * Answers the question: Is this child a visitor? Valid values are
%% `true` and `false`. %% `true` and `false`.
%% @org records/vbs_child/is_visitor %% @org records/vbs_child/is_visitor
is_visitor, is_visitor,
%% @api The date the child registered, stored as {Year, Month, Day} %% @api * The date the child registered, stored as `{Year, Month, Day}`
%% @org records/vbs_child/registration_date %% @org records/vbs_child/registration_date
registration_date, registration_date,
%% @api The child's shirt size, stored as a string. %% @api * The child's shirt size, stored as a string.
%% @org records/vbs_child/shirt_size %% @org records/vbs_child/shirt_size
shirt_size, shirt_size,
%% @api Any special needs this child has that should be accomodated. %% @api * Any special needs this child has that should be accomodated.
%% @org records/vbs_child/special_needs %% @org records/vbs_child/special_needs
special_needs, special_needs,
%% @api Any known allergies this child has. %% @api * Any known allergies this child has.
%% @org records/vbs_child/allergies %% @org records/vbs_child/allergies
allergies, allergies,
%% @api Additional comments about this child. %% @api * Additional comments about this child.
%% @org records/vbs_child/comments %% @org records/vbs_child/comments
comments}). comments}).
%% @api Information about a crew in the VBS system. %% ## vbs_crew ##
%% @api * Information about a crew in the VBS system.
%% @org records/vbs_crew %% @org records/vbs_crew
-record(vbs_crew, { -record(vbs_crew, {
%% @api A unique number. This is the record's primary identification and the %% @api * A unique number. This is the record's primary identification and the
%% table's primary key. %% table's primary key.
%% @org records/vbs_crew/id %% @org records/vbs_crew/id
id, % primary key id, % primary key
%% @api The crew number. %% @api * The crew number.
%% @org records/vbs_crew/number %% @org records/vbs_crew/number
number, number,
%% @api The crew type. This is a foreign key on %% @api * The crew type. This is a foreign key on
%% [`vbs_crew_type.id`](jlp://records/vbs_crew_type/id). %% [`vbs_crew_type.id`](jlp://records/vbs_crew_type/id).
%% @org records/vbs_crew/crew_type_id %% @org records/vbs_crew/crew_type_id
crew_type_id, % foreign key onto crew_type crew_type_id, % foreign key onto crew_type
%% @api The name of the crew, stored as a string. %% @api * The name of the crew, stored as a string.
%% @org records/vbs_crew/name %% @org records/vbs_crew/name
name, name,
%% @api Any comments about the crew. %% @api * Any comments about the crew.
%% @org records/vbs_crew/comments %% @org records/vbs_crew/comments
comments = ""}). comments = ""}).
%% @api Information about a crew type. Crew types are often used when a VBS %% ## vbs_crew_type ##
%% @api * Information about a crew type. Crew types are often used when a VBS
%% program has seperate activities set up for different types of children %% program has seperate activities set up for different types of children
%% (usually based on age). For example, having two type: Elementary and Pre-K %% (usually based on age). For example, having two type: Elementary and Pre-K
%% is common when there is a seperate set of activities for smaller children. %% is common when there is a seperate set of activities for smaller children.
%% @org records/vbs_crew_type %% @org records/vbs_crew_type
-record(vbs_crew_type, { -record(vbs_crew_type, {
%% @api A unique number. This is the record's primary identification and the %% @api * A unique number. This is the record's primary identification and the
%% table's primary key. %% table's primary key.
%% @org records/vbs_crew_type/id %% @org records/vbs_crew_type/id
id, id,
%% @api The displayed name of the crew type. %% @api * The displayed name of the crew type.
%% @org records/vbs_crew_type/name %% @org records/vbs_crew_type/name
name}). name}).
%% @api The id counter records are used to keep track of the next valid id for a %% ## vbs_id_counter ##
%% @api * The id counter records are used to keep track of the next valid id for a
%% specific purpose. This is how the unique id fields in other records is %% specific purpose. This is how the unique id fields in other records is
%% implmented. %% implmented.
%% @org records/vbs_id_counter %% @org records/vbs_id_counter
-record(vbs_id_counter, { -record(vbs_id_counter, {
%% @api A name for the counter. This is the primary key for the table and %% @api * A name for the counter. This is the primary key for the table and
%% must be unique. %% must be unique.
%% @example `vbs_adult_id` %%
%% *Example:* `vbs_adult_id`
%% @org records/vbs_id_counter/name %% @org records/vbs_id_counter/name
name, name,
%% @api The next value for this counter. %% @api * The next value for this counter.
%% @org records/vbs_id_counter/next_value %% @org records/vbs_id_counter/next_value
next_value = 0}). next_value = 0}).
%% @api Information about workers involved in the VBS program. %% ## vbs_worker ##
%% @api * Information about workers involved in the VBS program.
%% @org records/vbs_worker %% @org records/vbs_worker
-record(vbs_worker, { -record(vbs_worker, {
%% @api A unique number. This is the record's primary identification and the %% @api * A unique number. This is the record's primary identification and the
%% table's primary key. %% table's primary key.
%% @org records/vbs_worker/id %% @org records/vbs_worker/id
id, id,
%% @api Links this worker record to a [`vbs_adult`](jlp://records/vbs_adult) %% @api * Links this worker record to a [`vbs_adult`](jlp://records/vbs_adult)
%% @org records/vbs_worker/adult_id %% @org records/vbs_worker/adult_id
adult_id, % foreign key on adult adult_id, % foreign key on adult
%% @api The crew this worker is assigned to. This is a link to %% @api * The crew this worker is assigned to. This is a link to
%% [`vbs_crew.id`](jlp://records/vbs_crew/id). The most common way to deal %% [`vbs_crew.id`](jlp://records/vbs_crew/id). The most common way to deal
%% with workers who are not assigned to a particular crew is to create a %% with workers who are not assigned to a particular crew is to create a
%% special administrative crew and assign all these workers to that crew. %% special administrative crew and assign all these workers to that crew.
%% @org records/vbs_worker/crew_id %% @org records/vbs_worker/crew_id
crew_id = 0, crew_id = 0,
%% @api %% @api *
%% @org records/vbs_worker/worker_type_id %% @org records/vbs_worker/worker_type_id
worker_type_id, % foreign key on worker_type worker_type_id, % foreign key on worker_type
%% @api %% @api *
%% @org records/vbs_worker/shirt_size %% @org records/vbs_worker/shirt_size
shirt_size, shirt_size,
%% @api %% @api *
%% @org records/vbs_worker/ext_data %% @org records/vbs_worker/ext_data
ext_data = []}). ext_data = []}).
%% @api %% ## vbs_worker_type ##
%% @api * Worker types.
%% @org records/vbs_worker_type %% @org records/vbs_worker_type
-record(vbs_worker_type, { -record(vbs_worker_type, {
%% @api %% @api *
%% @org records/vbs_worker_type/id %% @org records/vbs_worker_type/id
id, id,
%% @api %% @api *
%% @org records/vbs_worker_type/name %% @org records/vbs_worker_type/name
name}). name}).

View File

@ -15,18 +15,43 @@ public abstract class JLPBaseGenerator {
protected Map<String, String> generate(Map<String, SourceFile> sources) { protected Map<String, String> generate(Map<String, SourceFile> sources) {
Map result = [:] Map result = [:]
// run the parse phase
sources.each { sourceId, sourceAST -> sources.each { sourceId, sourceAST ->
// set up the current generator state for this source // set up the current generator state for this source
docState.currentDocId = sourceId docState.currentDocId = sourceId
docState.codeTrees[sourceId] = sourceAST.codeAST docState.codeTrees[sourceId] = sourceAST.codeAST
parse(sourceAST) }
// run the emit phase
sources.each { sourceId, sourceAST ->
// set up the current generator state for this source
docState.currentDocId = sourceId
// generate the doc for this source // generate the doc for this source
result[sourceId] = emit(sourceAST) } result[sourceId] = emit(sourceAST) }
// return our results // return our results
return result } return result }
protected void parse(SourceFile sourceFile) {
sourceFile.blocks.each { block -> parse(block) } }
protected void parse(Block block) {
parse(block.docBlock)
parse(block.codeBlock) }
protected void parse(DocBlock docBlock) {
docBlock.directives.each { directive -> parse(directive) }
docBlock.docTexts.each { docText -> parse(docText) } }
protected abstract void parse(Directive directive)
protected abstract void parse(CodeBlock codeBlock)
protected abstract void parse(DocText docText)
protected abstract String emit(SourceFile sourceFile) protected abstract String emit(SourceFile sourceFile)
protected abstract String emit(Block block) protected abstract String emit(Block block)
protected abstract String emit(DocBlock docBlock) protected abstract String emit(DocBlock docBlock)

View File

@ -16,13 +16,29 @@ public class LiterateMarkdownGenerator extends JLPBaseGenerator {
super() super()
pegdown = new PegDownProcessor( pegdown = new PegDownProcessor(
Extensions.TABLES & Extensions.DEFINITIONS) } Extensions.TABLES | Extensions.DEFINITIONS) }
protected static Map<String, String> generateDocuments( protected static Map<String, String> generateDocuments(
Map<String, SourceFile> sources) { Map<String, SourceFile> sources) {
LiterateMarkdownGenerator inst = new LiterateMarkdownGenerator() LiterateMarkdownGenerator inst = new LiterateMarkdownGenerator()
return inst.generate(sources) } return inst.generate(sources) }
protected void parse(Directive directive) {
switch(directive.type) {
case DirectiveType.Org:
def orgMap = [:]
orgMap.id = directive.value
orgMap.directive = directive
orgMap.sourceDocId = docState.currentDocId
docState.orgs[directive.value] = orgMap
break;
default:
break // do nothing
} }
protected void parse(CodeBlock codeBlock) {} // nothing to do
protected void parse(DocText docText) {} // nothing to do
protected String emit(SourceFile sourceFile) { protected String emit(SourceFile sourceFile) {
StringBuilder sb = new StringBuilder() StringBuilder sb = new StringBuilder()
@ -34,13 +50,13 @@ public class LiterateMarkdownGenerator extends JLPBaseGenerator {
<head> <head>
<title>${docState.currentDocId}</title> <title>${docState.currentDocId}</title>
<meta http-equiv="content-type" content="text/html; charset=UTF-8"> <meta http-equiv="content-type" content="text/html; charset=UTF-8">
<!-- <link rel="syltesheet" media="all" href=""/> --> <link rel="stylesheet" media="all" href="docco.css"/>
</head> </head>
<body> <body>
<div id="container"> <div id="container">
<table cellpadding="0" cellspacing="0"> <table cellpadding="0" cellspacing="0">
<thead><tr> <thead><tr>
<th class="doc"><h1>${docState.currentDocId}</h1></th> <th class="docs"><h1>${docState.currentDocId}</h1></th>
<th class="code"/> <th class="code"/>
</tr></thead> </tr></thead>
<tbody>""") <tbody>""")
@ -70,7 +86,7 @@ public class LiterateMarkdownGenerator extends JLPBaseGenerator {
else { sb.append("<tr>") } else { sb.append("<tr>") }
// Create the `td` for the documentation. // Create the `td` for the documentation.
sb.append('\n<td class="doc">') sb.append('\n<td class="docs">')
sb.append(emit(block.docBlock)) sb.append(emit(block.docBlock))
sb.append('</td>') sb.append('</td>')
@ -94,14 +110,11 @@ public class LiterateMarkdownGenerator extends JLPBaseGenerator {
emitQueue = docBlock.directives.collect { directive -> emitQueue = docBlock.directives.collect { directive ->
def queueItem = [lineNumber: directive.lineNumber, value: directive] def queueItem = [lineNumber: directive.lineNumber, value: directive]
switch(directive.type) { switch(directive.type) {
case DirectiveType.Api: queueItem.priority = 12; break case DirectiveType.Api: queueItem.priority = 50; break
case DirectiveType.Author: queueItem.priority = 10; break case DirectiveType.Author: queueItem.priority = 10; break
case DirectiveType.Copyright: queueItem.priority = 11; break case DirectiveType.Copyright: queueItem.priority = 11; break
case DirectiveType.Example: queueItem.priority = 50; break case DirectiveType.Example: queueItem.priority = 50; break
case DirectiveType.Org: case DirectiveType.Org: queueItem.priority = 0; break }
docState.orgs[directive.value] = directive
queueItem.priority = 0
break }
return queueItem } return queueItem }
@ -110,14 +123,11 @@ public class LiterateMarkdownGenerator extends JLPBaseGenerator {
[lineNumber: docText.lineNumber, priority: 50, value: docText] }) [lineNumber: docText.lineNumber, priority: 50, value: docText] })
println emitQueue
println "----------"
// Sort the emit queue by priority, then line number. // Sort the emit queue by priority, then line number.
emitQueue.sort( emitQueue.sort(
{i1, i2 -> i1.priority != i2.priority ? {i1, i2 -> i1.priority != i2.priority ?
i1.priority - i2.priority : i1.priority - i2.priority :
i1.line - i2.line} as Comparator) i1.lineNumber - i2.lineNumber} as Comparator)
// Finally, we want to treat the whole block as one markdown chunk, so // Finally, we want to treat the whole block as one markdown chunk, so
// we will concatenate the values in the emit queue and then process // we will concatenate the values in the emit queue and then process
@ -125,7 +135,7 @@ public class LiterateMarkdownGenerator extends JLPBaseGenerator {
sb = new StringBuilder() sb = new StringBuilder()
emitQueue.each { queueItem -> sb.append(emit(queueItem.value)) } emitQueue.each { queueItem -> sb.append(emit(queueItem.value)) }
return pegdown.markdownToHtml(sb.toString()) return processMarkdown(sb.toString())
} }
protected String emit(CodeBlock codeBlock) { protected String emit(CodeBlock codeBlock) {
@ -153,19 +163,41 @@ public class LiterateMarkdownGenerator extends JLPBaseGenerator {
// process input inside HTML elements). // process input inside HTML elements).
case DirectiveType.Api: case DirectiveType.Api:
return "<div class='api'>" + return "<div class='api'>" +
pegdown.markdownToHtml(directive.value) + "</div>\n" processMarkdown(directive.value) + "</div>\n"
// An `@author` directive is turned into a definition list. // An `@author` directive is turned into a definition list.
case DirectiveType.Author: case DirectiveType.Author:
return "Author\n: ${directive.value}\n" return "Author\n: ${directive.value}\n"
case DirectiveType.Copyright: case DirectiveType.Copyright:
return "&copy; ${directive.value}\n" return "\n&copy; ${directive.value}\n"
// An `@example` directive is returned as is // An `@example` directive is returned as is
case DirectiveType.Example: return directive.value case DirectiveType.Example:
return directive.value
// An `@org` directive is ignored. // An `@org` directive is ignored.
case DirectiveType.Org: return "" } case DirectiveType.Org: return "" }
} }
protected String processMarkdown(String markdown) {
// convert to HTML from Markdown
String html = pegdown.markdownToHtml(markdown)
// replace internal `jlp://` links with actual links based on`@org`
// references
html = html.replaceAll(/jlp:\/\/([^\s"]+)/) { wholeMatch, linkId ->
def link = docState.orgs[linkId]
String newLink
if (!link) {
/* TODO: log error */
newLink = "broken_link(${linkId})" }
else if (docState.currentDocId == link.sourceDocId) {
newLink = "#$linkId" }
else { newLink = "${link.sourceDocId}#${linkId}" }
return newLink }
return html;
}
} }