From 7a5870dc0906022eb5cc1d0547d3f55590b0f489 Mon Sep 17 00:00:00 2001 From: Jonathan Bernard Date: Tue, 6 Sep 2011 16:13:21 -0500 Subject: [PATCH] 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. --- doc/issues/0000tn3.rst | 2 + doc/issues/0001tn3.rst | 1 + resources/main/docco.css | 193 ++++++++++++++++++ resources/main/jlp.css | 132 ++++++++++++ resources/main/test.groovy | 22 ++ resources/main/vbs_db_records.hrl | 169 ++++++++------- .../jlp/experimental/JLPBaseGenerator.groovy | 25 +++ .../LiterateMarkdownGenerator.groovy | 66 ++++-- 8 files changed, 520 insertions(+), 90 deletions(-) create mode 100644 doc/issues/0000tn3.rst create mode 100644 doc/issues/0001tn3.rst create mode 100644 resources/main/docco.css create mode 100644 resources/main/jlp.css diff --git a/doc/issues/0000tn3.rst b/doc/issues/0000tn3.rst new file mode 100644 index 0000000..3549837 --- /dev/null +++ b/doc/issues/0000tn3.rst @@ -0,0 +1,2 @@ +Implement working internal links using ``jlp://`` schema. +========================================================= diff --git a/doc/issues/0001tn3.rst b/doc/issues/0001tn3.rst new file mode 100644 index 0000000..f81faf7 --- /dev/null +++ b/doc/issues/0001tn3.rst @@ -0,0 +1 @@ +Implement language-specific code parsing and comprehension. diff --git a/resources/main/docco.css b/resources/main/docco.css new file mode 100644 index 0000000..17c03f4 --- /dev/null +++ b/resources/main/docco.css @@ -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 */ diff --git a/resources/main/jlp.css b/resources/main/jlp.css new file mode 100644 index 0000000..bcff1ad --- /dev/null +++ b/resources/main/jlp.css @@ -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 */ diff --git a/resources/main/test.groovy b/resources/main/test.groovy index 5f97efe..56497c5 100644 --- a/resources/main/test.groovy +++ b/resources/main/test.groovy @@ -1,4 +1,5 @@ import com.jdblabs.jlp.* +import com.jdblabs.jlp.experimental.LiterateMarkdownGenerator import org.parboiled.Parboiled import org.parboiled.parserunners.ReportingParseRunner import org.parboiled.parserunners.RecoveringParseRunner @@ -54,3 +55,24 @@ vbsTest = { 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 } + +} diff --git a/resources/main/vbs_db_records.hrl b/resources/main/vbs_db_records.hrl index 303d5d9..b16f92d 100644 --- a/resources/main/vbs_db_records.hrl +++ b/resources/main/vbs_db_records.hrl @@ -2,6 +2,8 @@ %% @author Jonathan Bernard %% @copyright 2010-2011 JDB Labs Inc. +%% #Overview +%% %% The VBS database API is centered around the data records: %% %% * Tables are named after the records. ``vbs_adult`` records are stored in @@ -14,89 +16,99 @@ %% #Record Definitions %% Here are the record definitions: +%% ## vbs_adult ## %% @api Information about an adult in the VBS system. %% @org records/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 %% @org records/vbs_adult/id id, - %% @api A unique full name. - %% @example "John Smith", "Fae Alice McDonald" + %% @api * A unique full name. + %% + %% *Examples:* `"John Smith", "Fae Alice McDonald"` %% @org records/vbs_adult/name name, - %% @api The adult's age (optional). + %% @api * The adult's age (optional). %% @org records/vbs_adult/age age = 0, - %% @api A list of phone numbers (strings). - %% @example ["512-555-1155", "123-456-7890"] + %% @api * A list of phone numbers (strings). + %% + %% *Examples:* `["512-555-1155", "123-456-7890"]` %% @org records/vbs_adult/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, %% for example). - %% @example - %% - %% "123 Grant Drive - %% Plainsville, TX, 78707" + %% + %% *Example:* + %% + %% "123 Grant Drive + %% Plainsville, TX, 78707" + %% %% @org records/vbs_adult/address address = "", - %% @api The adult's email address as a string. - %% @example "john_smith@mailco.com" + %% @api * The adult's email address as a string. + %% + %% *Example:* `"john_smith@mailco.com"` %% @org records/vbs_adult/email email = ""}). +%% ## vbs_attendance ## %% @api An entry recording a person's attendance. %% @org records/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. %% @org records/vbs_attendance/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_child`](jlp://records/vbs_child) table, depending on the value of %% the [`person_type`](jlp://records/vbs_attendance/person_type) field. %% @org records/vbs_attendance/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 %% possible values and the corresponding link tables are: %% - %% Value | Link Table - %% --------|---------------------------------------- - %% `child` |[`vbs_child`](jlp://records/vbs_child) - %% `worker`|[`vbs_worker`](jlp://records/vbs_worker) + %% Value | Link Table + %% --------|---------------------------------------- + %% `child` |[`vbs_child`](jlp://records/vbs_child) + %% `worker`|[`vbs_worker`](jlp://records/vbs_worker) %% %% @org records/vbs_attendance/person_type person_type, - %% @api The date of attendance, stored as {Year, Month, Day}. - %% @example {2011, 6, 14} + %% @api * The date of attendance, stored as `{Year, Month, Day}.` + %% + %% *Example:* `{2011, 6, 14}` + %% %% @org records/vbs_attendance/date date = {1900, 1, 1}, - %% @api A timestamp taken when the person was signed in, stored as - %% {Hour, Minute, Second} - %% @example {5, 22, 13} + %% @api * A timestamp taken when the person was signed in, stored as + %% `{Hour, Minute, Second}` + %% + %% *Example:* `{5, 22, 13}` %% @org records/vbs_attendance/sign_in sign_in = false, % {hour, minute, second} - %% @api A timestamp taken when the person is signed out, stored as - %% {Hour, Minute, Second} + %% @api * A timestamp taken when the person is signed out, stored as + %% `{Hour, Minute, Second}` %% @org records/vbs_attendance/sign_out 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, %% or client-specific data, without having to alter the database schema. %% When working with `vbs_attendance` records, a caller should ignore @@ -104,196 +116,207 @@ %% @org records/vbs_attendance/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 comments = ""}). -%% @api Information about a child in the VBS program. +%% ## vbs_child ## %% @org records/vbs_child + +%% @api Information about a child in the VBS program. -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. %% @org records/vbs_child/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). %% @org records/vbs_child/crew_id crew_id, - %% @api The child's full name. - %% @example "Mary Scott", "Gregory Brown" + %% @api * The child's full name. + %% + %% *Example:* `"Mary Scott", "Gregory Brown"` %% @org records/vbs_child/name name, - %% @api The child's date of birth, stored as {Year, Month, Day} - %% @example {1998, 12, 22} + %% @api * The child's date of birth, stored as `{Year, Month, Day}` + %% + %% *Example:* `{1998, 12, 22}` %% @org records/vbs_child/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 gender, - %% @api The child's grade level in school. + %% @api * The child's grade level in school. %% @org records/vbs_child/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 %% [`vbs_adult.id`](jlp://records/vbs_adult/id) - %% @example [4, 5] + %% + %% *Example:* `[4, 5]` %% @org records/vbs_child/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 %% record to adult records by %% ['vbs_adult.id`](jlp://records/vbs_adult/id). %% @org records/vbs_child/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 %% involving this child (injury, for example). These link the child record %% to adult records by [`vbs_adult.id`](jlp://records/vbs_adult/id). %% @org records/vbs_child/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. %% @org records/vbs_child/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. %% @org records/vbs_child/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`. %% @org records/vbs_child/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 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 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 special_needs, - %% @api Any known allergies this child has. + %% @api * Any known allergies this child has. %% @org records/vbs_child/allergies allergies, - %% @api Additional comments about this child. + %% @api * Additional comments about this child. %% @org records/vbs_child/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 -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. %% @org records/vbs_crew/id id, % primary key - %% @api The crew number. + %% @api * The crew number. %% @org records/vbs_crew/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). %% @org records/vbs_crew/crew_type_id 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 name, - %% @api Any comments about the crew. + %% @api * Any comments about the crew. %% @org records/vbs_crew/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 %% (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. %% @org records/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. %% @org records/vbs_crew_type/id id, - %% @api The displayed name of the crew type. + %% @api * The displayed name of the crew type. %% @org records/vbs_crew_type/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 %% implmented. %% @org records/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. - %% @example `vbs_adult_id` + %% + %% *Example:* `vbs_adult_id` %% @org records/vbs_id_counter/name name, - %% @api The next value for this counter. + %% @api * The next value for this counter. %% @org records/vbs_id_counter/next_value 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 -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. %% @org records/vbs_worker/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 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 %% 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. %% @org records/vbs_worker/crew_id crew_id = 0, - %% @api + %% @api * %% @org records/vbs_worker/worker_type_id worker_type_id, % foreign key on worker_type - %% @api + %% @api * %% @org records/vbs_worker/shirt_size shirt_size, - %% @api + %% @api * %% @org records/vbs_worker/ext_data ext_data = []}). -%% @api +%% ## vbs_worker_type ## +%% @api * Worker types. %% @org records/vbs_worker_type -record(vbs_worker_type, { - %% @api + %% @api * %% @org records/vbs_worker_type/id id, - %% @api + %% @api * %% @org records/vbs_worker_type/name name}). diff --git a/src/main/com/jdblabs/jlp/experimental/JLPBaseGenerator.groovy b/src/main/com/jdblabs/jlp/experimental/JLPBaseGenerator.groovy index 0642efc..8f84dcb 100644 --- a/src/main/com/jdblabs/jlp/experimental/JLPBaseGenerator.groovy +++ b/src/main/com/jdblabs/jlp/experimental/JLPBaseGenerator.groovy @@ -15,18 +15,43 @@ public abstract class JLPBaseGenerator { protected Map generate(Map sources) { Map result = [:] + + // run the parse phase sources.each { sourceId, sourceAST -> // set up the current generator state for this source docState.currentDocId = sourceId 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 result[sourceId] = emit(sourceAST) } // return our results 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(Block block) protected abstract String emit(DocBlock docBlock) diff --git a/src/main/com/jdblabs/jlp/experimental/LiterateMarkdownGenerator.groovy b/src/main/com/jdblabs/jlp/experimental/LiterateMarkdownGenerator.groovy index 787e058..c877f69 100644 --- a/src/main/com/jdblabs/jlp/experimental/LiterateMarkdownGenerator.groovy +++ b/src/main/com/jdblabs/jlp/experimental/LiterateMarkdownGenerator.groovy @@ -16,13 +16,29 @@ public class LiterateMarkdownGenerator extends JLPBaseGenerator { super() pegdown = new PegDownProcessor( - Extensions.TABLES & Extensions.DEFINITIONS) } + Extensions.TABLES | Extensions.DEFINITIONS) } protected static Map generateDocuments( Map sources) { LiterateMarkdownGenerator inst = new LiterateMarkdownGenerator() 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) { StringBuilder sb = new StringBuilder() @@ -34,13 +50,13 @@ public class LiterateMarkdownGenerator extends JLPBaseGenerator { ${docState.currentDocId} - +
- + """) @@ -70,7 +86,7 @@ public class LiterateMarkdownGenerator extends JLPBaseGenerator { else { sb.append("") } // Create the `td` for the documentation. - sb.append('\n') @@ -94,14 +110,11 @@ public class LiterateMarkdownGenerator extends JLPBaseGenerator { emitQueue = docBlock.directives.collect { directive -> def queueItem = [lineNumber: directive.lineNumber, value: directive] 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.Copyright: queueItem.priority = 11; break case DirectiveType.Example: queueItem.priority = 50; break - case DirectiveType.Org: - docState.orgs[directive.value] = directive - queueItem.priority = 0 - break } + case DirectiveType.Org: queueItem.priority = 0; break } return queueItem } @@ -110,14 +123,11 @@ public class LiterateMarkdownGenerator extends JLPBaseGenerator { [lineNumber: docText.lineNumber, priority: 50, value: docText] }) - println emitQueue - println "----------" - // Sort the emit queue by priority, then line number. emitQueue.sort( {i1, i2 -> 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 // we will concatenate the values in the emit queue and then process @@ -125,7 +135,7 @@ public class LiterateMarkdownGenerator extends JLPBaseGenerator { sb = new StringBuilder() emitQueue.each { queueItem -> sb.append(emit(queueItem.value)) } - return pegdown.markdownToHtml(sb.toString()) + return processMarkdown(sb.toString()) } protected String emit(CodeBlock codeBlock) { @@ -153,19 +163,41 @@ public class LiterateMarkdownGenerator extends JLPBaseGenerator { // process input inside HTML elements). case DirectiveType.Api: return "
" + - pegdown.markdownToHtml(directive.value) + "
\n" + processMarkdown(directive.value) + "\n" // An `@author` directive is turned into a definition list. case DirectiveType.Author: return "Author\n: ${directive.value}\n" case DirectiveType.Copyright: - return "© ${directive.value}\n" + return "\n© ${directive.value}\n" // An `@example` directive is returned as is - case DirectiveType.Example: return directive.value + case DirectiveType.Example: + return directive.value // An `@org` directive is ignored. 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; + } }

${docState.currentDocId}

${docState.currentDocId}

') + sb.append('\n') sb.append(emit(block.docBlock)) sb.append('