Overview
Comment: | add blockquote support for html, subdocument mechanisms, mode to generate epub-compatible XHTML5; various fixes and improvements |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA3-256: |
35ea3c5797d672b430bd3a8ce9ee9913 |
User & Date: | lexi on 2022-09-05 18:49:58 |
Other Links: | manifest | tags |
Context
2022-09-05
| ||
20:15 | cleanup check-in: e551f71321 user: lexi tags: trunk | |
18:49 | add blockquote support for html, subdocument mechanisms, mode to generate epub-compatible XHTML5; various fixes and improvements check-in: 35ea3c5797 user: lexi tags: trunk | |
2022-04-17
| ||
21:16 | various updates fuck man idr what all i did check-in: c0bdfa46df user: lexi tags: trunk | |
Changes
Modified cortav.ct from [2cc3a528df] to [03a705bddd].
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 .. 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 ... 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 ... 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 ... 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 ... 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 .... 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 |
* [*lists] use a different syntax from markdown. you can start a line with a [`*] to create an unordered list, or [`:] to create an ordered list; indentation doesn't matter. if you want to nest list items, instead of putting two spaces before the child item, you just add another star or colon. and of course, you can nest lists of different kinds within one another. * [*horizontal rules] use roughly the same syntax: three or more hyphens on a line of their own ([`\---]). underlines also work ([`___], [`-_-], [`__-__-__] etc), as do horizontal unicode box drawing characters ([`─ ━ ┈] etc). * some markdown implementations support [*tables]. cortav does too, using a very simple notation similar to the usual notation used in markdown. a key difference, however, is that cortav table cells can contain any formatting a paragraph can. * [*underlines] are supported by some markdown implementations. in cortav, you can apply them with the notation [`\[_my underlined text\]] -- please just use them sparingly when you render to HTML! * [*strikethrough] is supported by some extended versions of markdown. cortav uses the notation [`\[~my deleted text\]], with the intended semantics of text that is being removed by some revision of a document. (you can also denote text that is being [!added] by using a plus sign instead of a tilde) * [*images] are a bit more complicated, but much more versatile. see the section on [>rsrc resources] for an explanation. * [*smart quotes] and [*em dashes] are inserted automatically, just as in markdown, provided you have the [>tsmog transmogrify] extension available. (it is part of the reference implementation and defined by the spec, but not required.) in fact, you can insert longer dashes than em dashes just by increasing the number of hyphens. the reference implementation's transmogrifier also translates ascii arrows like [`\-->] into their unicode equivalents ([`→]). * [*literals] (also known as [*code text]) can be inserted with the [`\[`int main(void);] syntax. note however that literals are not protected from the transmogrifier, and are parsed like any other span, which may cause problems if the source code you're quoting makes use of such forbidden runes. in this case, you'll want to wrap the code span in a raw span. the syntax for this is [`\[`[\\int main(void);\]]], but since this is a bit unwieldy it can also be abbreviated as [`\[`\\int main(void);\]]. of course, this is only a small taste of what cortav can do, not even touching on key features like macros, footnotes, or equation formatting. read the sections on [>onblocks blocks] and [>onspans spans] for all the gory details. ## encoding a cortav document is made up of a sequence of codepoints. UTF-8 must be supported, but other encodings (such as UTF-32 or C6B) may be supported as well. lines will be derived by splitting the codepoints at the linefeed character or equivalent. note that unearthly encodings like C6B or EBCDIC will need to select their own control sequences. ## file type ................................................................................ ##onblocks structure cortav is based on an HTML-like block model, where a document consists of sections, which are made up of blocks, which may contain a sequence of spans. flows of text are automatically conjoined into spans, and blocks are separated by one or more newlines. this means that, unlike in markdown, a single logical paragraph [*cannot] span multiple ASCII lines. the primary purpose of this was to ensure ease of parsing, but also, both markdown and cortav are supposed to be readable from within a plain text editor. this is the 21st century. every reasonable text editor supports soft word wrap, and if yours doesn't, that's entirely your own damn fault. hard-wrapping lines is incredibly user-hostile, especially to users on mobile devices with small screens. cortav does not allow it. the first character(s) of every line (the "control sequence") indicates the role of that line. if no control sequence is recognized, the line is treated as a paragraph. the currently supported control sequences are listed below. some control sequences have alternate forms, in order to support modern, readable unicode characters as well as plain ascii text. * [*paragraphs] ([`.] [` ¶] [`❡]): a paragraph is a simple block of text. the period control sequence is only necessary if the paragraph text starts with text that would be interpreted as a control sequence otherwise * newlines [` \\]: inserts a line break into previous paragraph and attaches the following text. mostly useful for poetry or lyrics * [*section starts] [`#] [`§]: starts a new section. all sections have an associated depth, determined by the number of sequence repetitions (e.g. "###" indicates depth three). sections may have headers and IDs; both are optional. IDs, if present, are a sequence of raw-text immediately following the hash marks. if the line has one or more space character followed by styled-text, a header will be attached. the character immediately following the hashes can specify a particular type of section. e.g.: ** [`#] is a simple section break. ** [`#anchor] opens a new section with the ID [`anchor]. ** [`# header] opens a new section with the title "header". ** [`#anchor header] opens a new section with both the ID [`anchor] and the title "header". * [*nonprinting sections] ([`^]): sometimes, you'll want to create a namespace without actually adding a visible new section to the document. you can achieve this by creating a [!nonprinting section] and defining resources within it. nonprinting sections can also be used to store comments, notes, to-dos, or other meta-information that is useful to have in the source file without it becoming a part of the output. nonprinting sections can be used for a sort of "literate markup," where resource and reference definitions can intermingle with human-readable narrative about those definitions. * [*resource] ([`@]): defines a [!resource]. a resource is a file or object that exists outside of the document but which will are to be included in the document somehow. common examples of resources include images, videos, iframes, or headers/footers. see [>rsrc resources] for more information. * [*lists] ([`*] [`:]): these are like paragraph nodes, but list nodes that occur next to each other will be arranged so as to show they compose a sequence. depth is determined by the number of stars/colons. like headers, a list entry may have an ID that can be used to refer back to it; it is indicated in the same way. if colons are used, this indicates that the order of the items is signifiant. [`:]-lists and [`*]-lists may be intermixed; however, note than only the last character in the sequence actually controls the type. a blank line terminates the current list. * [*directives] ([`%]): a directive issues a hint to the renderer in the form of an arbitrary string. directives are normally ignored if they are not supported, but you may cause a warning to be emitted where the directive is not supported with [`%!] or mark a directive critical with [`%!!] so that rendering will entirely fail if it cannot be obeyed. * [*comments] ([`%%]): a comment is a line of text that is simply ignored by the renderer. * [*asides] ([`!]): indicates text that diverges from the narrative, and can be skipped without interrupting it. think of it like block-level parentheses. asides which follow one another are merged as paragraphs of the same aside, usually represented as a sort of box. if the first line of an aside contains a colon, the stretch of styled-text from the beginning to the aside to the colon will be treated as a "type heading," e.g. "Warning:" * [*code] ([`~~~]): a line beginning with ~~~ begins or terminates a block of code. code blocks are by default not parsed, but parsing can be activated by preceding the code block with an [`%[*expand]] directive. the opening line should look like one of the below ** [`~~~] ** [`~~~ language] (markdown-style shorthand syntax) ................................................................................ ##onspans styled text most blocks contain a sequence of spans. these spans are produced by interpreting a stream of [*styled-text] following the control sequence. styled-text is a sequence of codepoints potentially interspersed with escapes. an escape is formed by an open square bracket [`\[] followed by a [*span control sequence], and arguments for that sequence like more styled-text. escapes can be nested. * strong {obj *|styled-text}: causes its text to stand out from the narrative, generally rendered as bold or a brighter color. * emphatic {obj !|styled-text}: indicates that its text should be spoken with emphasis, generally rendered as italics * custom style {span .|id|[$styled-text]}: applies a specially defined font style. for example, if you have defined [`caution] to mean "demibold italic underline", cortav will try to apply the proper weight and styling within the constraints of the current font to the span [$styled-text]. see the [>fonts-sty fonts section] for more information about this mechanism. * literal {obj `|styled-text}: indicates that its text is a reference to a literal sequence of characters or other discrete token. generally rendered in monospace * variable {obj $|styled-text}: indicates that its text is a stand-in that will be replaced with what it names. generally rendered in italic monospace, ideally of a different color * underline {obj _|styled-text}: underlines the text. use sparingly on text intended for webpages -- underlined text [!is] distinct from links, but underlining non-links is still a violation of convention. * strikeout {obj ~|styled-text}: indicates that its text should be struck through or otherwise indicated for deletion * insertion {obj +|styled-text}: indicates that its text should be indicated as a new addition to the text body. ** consider using a macro definition [`\edit: [~[#1]][+[#2]]] to save typing if you are doing editing work * link \[>[!ref] [!styled-text]\]: produces a hyperlink or cross-reference denoted by [$ref], which may be either a URL specified with a reference or the name of an object like an image or section elsewhere in the document. the unicode characters [`→] and [`🔗] can also be used instead of [`>] to denote a link. * footnote {span ^|ref|[$styled-text]}: annotates the text with a defined footnote. in interactive output media [`\[^citations.qtheo Quantum Theosophy: A Neophyte's Catechism]] will insert a link with the text [`Quantum Theosophy: A Neophyte's Catechism] that, when clicked, causes a footnote to pop up on the screen. for static output media, the text will simply have a superscript integer after it denoting where the footnote is to be found. * superscript {obj '|[$styled-text]} * subscript {obj ,|[$styled-text]} * raw {obj \\ |[$raw-text]}: causes all characters within to be interpreted literally, without expansion. the only special characters are square brackets, which must have a matching closing bracket, and backslashes. * raw literal \[$\\[!raw-text]\]: shorthand for [\[$[\…]]] * macro [` \{[!name] [!arguments]}]: invokes a [>ex.mac macro], specified with a reference * argument {obj #|var}: in macros only, inserts the [$var]-th argument. otherwise, inserts a context variable provided by the renderer. * raw argument {obj ##|var}: like above, but does not evaluate [$var]. ................................................................................ the interpreter should provide a [`cortav] table with the objects: * [`ctx]: contains context variables used files should return a table with the following members * [`macros]: an array of functions that return strings or arrays of strings when invoked. these will be injected into the global macro namespace. ###ts ts the [*ts] extension allows documents to be marked up for basic classification constraints and automatically redacted. if you are seriously relying on ts for confidentiality, make damn sure you start the file with [$%[*requires] ts], so that rendering will fail with an error if the extension isn't supported. ts enables the directives: * [`%[*ts] class [$scope level] ([$styled-text])]: indicates a classification level for either the while document (scope [$doc]) or the next section (scope [$sec]). if the ts level is below [$level], the section will be redacted or rendering will fail with an error, as appropriate. if styled-text is included, this will be treated as the name of the classification level. * [`%[*ts] word [$scope word] ([$styled-text])]: indicates a codeword clearance that must be present for the text to render. if styled-text is present, this will be used to render the name of the codeword instead of [$word]. * [`%[*when] ts level [$level]] * [`%[*when] ts word [$word]] ts enables the spans: * [`\[🔒#[!level] [$styled-text]\]]: redacts the span if the security level is below that specified. * [`\[🔒.[!word] [$styled-text]\]]: redacts the span if the specified codeword clearance is not enabled. (the padlock emoji is shorthand for [`%[*ts]].) ts redacts spans securely; that is, they are simply replaced with an indicator that they have been redacted, without visually leaking the length of the redacted text. ~~~#ts-example example [cortav] ~~~ %ts word doc sorrowful-pines SORROWFUL PINES # intercept R1440 TCT S3 this communication between the ambassador of [*POLITY DOORMAT CRIMSON] "Socialist League world Glory" and an unknown noble of [*POLITY ROSE] "the Empire of a Thousand Suns" was intercepted by [*SYSTEM SUPINE WARBLE]. ................................................................................ corran: http://ʞ.cc/fic/spirals/society tengwar: https://en.wikipedia.org/wiki/Tengwar ###refimpl-switches switches [`cortav.lua] offers various switches to control its behavior. + long + short + function + | [`\--out [!file]] :|:[`-o]:| sets the output file (default stdout) | | [`\--log [!file]] :|:[`-l]:| sets the log file (default stderr) | | [`\--define [!var] [!val]] :|:[`-d]:| sets the context variable [$var] to [$val] | | [`\--mode-set [!mode]] :|:[`-y]:| activates the [>refimpl-mode mode] with ID [!mode] | [`\--mode-clear [!mode]] :|:[`-n]:| disables the mode with ID [!mode] | | [`\--mode [!id] [!val]] :|:[`-m]:| configures mode [!id] with the value [!val] | | [`\--mode-set-weak [!mode]] :|:[`-Y]:| activates the [>refimpl-mode mode] with ID [!mode] if the source file does not specify otherwise | [`\--mode-clear-weak [!mode]] :|:[`-N]:| disables the mode with ID [$mode] if the source file does not specify otherwise | [`\--mode-weak [!id] [!val]] :|:[`-M]:| configures mode [$id] with the value [$val] if the source file does not specify otherwise | [`\--help] :|:[`-h]:| display online help | | [`\--version] :|:[`-V]:| display the interpreter version | ###refimpl-mode modes most of [`cortav.lua]'s implementation-specific behavior is controlled by use of [!modes]. these are namespaced options which may have a boolean, string, or numeric value. boolean modes are set with the [`-y] [`-n] flags; other modes use the [`-m] flags. most modes are defined by the renderer backend. the following modes affect the behavior of the frontend: + ID + type + effect ................................................................................ ####refimpl-rend-html-modes modes [`html] supports the following modes: * string (css length) [`html:width] sets a maximum width for the body content in order to make the page more readable on large displays * number [`html:accent] applies an accent hue to the generated webpage. the hue is specified in degrees, e.g. [$-m html:accent 0] applies a red accent. * flag [`html:dark-on-light] uses dark-on-light styling, instead of the default light-on-dark * flag [`html:fossil-uv] outputs an HTML snippet suitable for use with the Fossil VCS webserver. this is intended to be used with the unversioned content mechanism to host rendered versions of documentation written in cortav that's stored in a Fossil repository. * number [`html:hue-spread] generates a color palette based on the supplied accent hue. the larger the value, the more the other colors diverge from the accent hue. * string [`html:link-css] generates a document linking to the named stylesheet * flag [`html:gen-styles] embeds appropriate CSS styles in the document (default on) * flag [`html:snippet] produces a snippet of html instead of an entire web page. note that proper CSS scoping is not yet implemented (and can't be implemented hygienically since [$scoped] was removed 😢) * string [`html:title] specifies the webpage titlebar contents (normally autodetected from the document based on headings or directives) * string [`html:font] specifies the default font to use when rendering as a CSS font specification (e.g. [`-m html:font 'Alegreya, Junicode, Georgia, "Times New Roman"]) ~~~ $ cortav readme.ct --out readme.html \ -m render:format html \ -m html:width 40em \ ................................................................................ currently all internal colors are expressed and stored as HSL, and we're using code borrowed from my Minetest mod [`[>sorcrep sorcery]] & written by glowpelt for converting that into RGB for non-HTML outputs like groff. probably what should be done is to switch to LCH internally, accept both HSL and LCH input, and "downres" to the best representation each format can support. that's probably going to have to wait until someone who knows a bit more about color theory and math than me can do it (paging Lea Verou) sorcrep: https://c.hale.su/sorcery ### intent files there's currently no standard way to describe the intent and desired formatting of a document besides placing pragmata in the source file itself. this is extremely suboptimal, as when generating collections of documents, it's ideal to be able to keep all formatting information in one place. users should also be able to specify their own styling overrides that describe the way they prefer to read [`cortav] files, especially for uses like gemini or gopher integration. at some point soon [`cortav] needs to address this by adding intent files that can be activated from outside the source file, such as with a command line flag or a configuration file setting. these will probably consist of lines that are interpreted as pragmata. in addition to the standard intent format however, individual implementations should feel free to provide their own ways to provide intent metadata; e.g. the reference implementation, which has a lua interpreter available, should be able to take a lua script that runs after the parse stage and generates . this will be particularly useful for the end-user who wishes to specify a particular format she likes reading her files in without forcing that format on everyone she sends the compiled document to, as it will be able to interrogate the document and make intelligent decisions about what pragmata to apply. intent files should also be able to define [>rsrc resources], [>ctxvar context variables], and macros. |
| | | | | | > > | | | | | | | | | | | | | | | | | > > | | |
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 .. 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 ... 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 ... 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 ... 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 ... 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 .... 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 |
* [*lists] use a different syntax from markdown. you can start a line with a [`*] to create an unordered list, or [`:] to create an ordered list; indentation doesn't matter. if you want to nest list items, instead of putting two spaces before the child item, you just add another star or colon. and of course, you can nest lists of different kinds within one another. * [*horizontal rules] use roughly the same syntax: three or more hyphens on a line of their own ([`\---]). underlines also work ([`___], [`-_-], [`__-__-__] etc), as do horizontal unicode box drawing characters ([`─ ━ ┈] etc). * some markdown implementations support [*tables]. cortav does too, using a very simple notation similar to the usual notation used in markdown. a key difference, however, is that cortav table cells can contain any formatting a paragraph can. * [*underlines] are supported by some markdown implementations. in cortav, you can apply them with the notation [`\[_my underlined text\]] -- please just use them sparingly when you render to HTML! * [*strikethrough] is supported by some extended versions of markdown. cortav uses the notation [`\[~my deleted text\]], with the intended semantics of text that is being removed by some revision of a document. (you can also denote text that is being [!added] by using a plus sign instead of a tilde) * [*images] are a bit more complicated, but much more versatile. see the section on [>rsrc resources] for an explanation. * [*smart quotes] and [*em dashes] are inserted automatically, just as in markdown, provided you have the [>tsmog transmogrify] extension available. (it is part of the reference implementation and defined by the spec, but not required.) in fact, you can insert longer dashes than em dashes just by increasing the number of hyphens. the reference implementation's transmogrifier also translates ascii arrows like [`\-->] into their unicode equivalents ([`→]). * [*literals] (also known as [*code text]) can be inserted with the [`\[`int main(void);\]] syntax. note however that literals are not protected from the transmogrifier, and are parsed like any other span, which may cause problems if the source code you're quoting makes use of such forbidden runes. in this case, you'll want to wrap the code span in a raw span. the syntax for this is [`\[`[\\int main(void);\]]], but since this is a bit unwieldy it can also be abbreviated as [`\[`\\int main(void);\]]. of course, this is only a small taste of what cortav can do, not even touching on key features like macros, footnotes, or equation formatting. read the sections on [>onblocks blocks] and [>onspans spans] for all the gory details. ## encoding a cortav document is made up of a sequence of codepoints. UTF-8 must be supported, but other encodings (such as UTF-32 or C6B) may be supported as well. lines will be derived by splitting the codepoints at the linefeed character or equivalent. note that unearthly encodings like C6B or EBCDIC will need to select their own control sequences. ## file type ................................................................................ ##onblocks structure cortav is based on an HTML-like block model, where a document consists of sections, which are made up of blocks, which may contain a sequence of spans. flows of text are automatically conjoined into spans, and blocks are separated by one or more newlines. this means that, unlike in markdown, a single logical paragraph [*cannot] span multiple ASCII lines. the primary purpose of this was to ensure ease of parsing, but also, both markdown and cortav are supposed to be readable from within a plain text editor. this is the 21st century. every reasonable text editor supports soft word wrap, and if yours doesn't, that's entirely your own damn fault. hard-wrapping lines is incredibly user-hostile, especially to users on mobile devices with small screens. cortav does not allow it. the first character(s) of every line (the "control sequence") indicates the role of that line. if no control sequence is recognized, the line is treated as a paragraph. the currently supported control sequences are listed below. some control sequences have alternate forms, in order to support modern, readable unicode characters as well as plain ascii text. * [*paragraphs] ([`.] [` ¶] [`❡]): a paragraph is a simple block of text. the period control sequence is only necessary if the paragraph text starts with text that would be interpreted as a control sequence otherwise * [*newlines] [` \\]: inserts a line break into previous paragraph and attaches the following text. mostly useful for poetry or lyrics * [*section starts] [`#] [`§]: starts a new section. all sections have an associated depth, determined by the number of sequence repetitions (e.g. "###" indicates depth three). sections may have headers and IDs; both are optional. IDs, if present, are a sequence of raw-text immediately following the hash marks. if the line has one or more space character followed by styled-text, a header will be attached. the character immediately following the hashes can specify a particular type of section. e.g.: ** [`#] is a simple section break. ** [`#anchor] opens a new section with the ID [`anchor]. ** [`# header] opens a new section with the title "header". ** [`#anchor header] opens a new section with both the ID [`anchor] and the title "header". * [*nonprinting sections] ([`^]): sometimes, you'll want to create a namespace without actually adding a visible new section to the document. you can achieve this by creating a [!nonprinting section] and defining resources within it. nonprinting sections can also be used to store comments, notes, to-dos, or other meta-information that is useful to have in the source file without it becoming a part of the output. nonprinting sections can be used for a sort of "literate markup," where resource and reference definitions can intermingle with human-readable narrative about those definitions. * [*resource] ([`@]): defines a [!resource]. a resource is a file or object that is to be embedded in the document somehow. common examples of resources include images, videos, iframes, or headers/footers. resources can be defined inline, or reference external objects. see [>rsrc resources] for more information. * [*lists] ([`*] [`:]): these are like paragraph nodes, but list nodes that occur next to each other will be arranged so as to show they compose a sequence. depth is determined by the number of stars/colons. like headers, a list entry may have an ID that can be used to refer back to it; it is indicated in the same way. if colons are used, this indicates that the order of the items is signifiant. [`:]-lists and [`*]-lists may be intermixed; however, note than only the last character in the sequence actually controls the type. a blank line terminates the current list. * [*directives] ([`%]): a directive issues a hint to the renderer in the form of an arbitrary string. directives are normally ignored if they are not supported, but you may cause a warning to be emitted where the directive is not supported with [`%!] or mark a directive critical with [`%!!] so that rendering will entirely fail if it cannot be obeyed. * [*comments] ([`%%]): a comment is a line of text that is simply ignored by the renderer. * [*asides] ([`!]): indicates text that diverges from the narrative, and can be skipped without interrupting it. think of it like block-level parentheses. asides which follow one another are merged as paragraphs of the same aside, usually represented as a sort of box. if the first line of an aside contains a colon, the stretch of styled-text from the beginning to the aside to the colon will be treated as a "type heading," e.g. "Warning:" * [*code] ([`~~~]): a line beginning with ~~~ begins or terminates a block of code. code blocks are by default not parsed, but parsing can be activated by preceding the code block with an [`%[*expand]] directive. the opening line should look like one of the below ** [`~~~] ** [`~~~ language] (markdown-style shorthand syntax) ................................................................................ ##onspans styled text most blocks contain a sequence of spans. these spans are produced by interpreting a stream of [*styled-text] following the control sequence. styled-text is a sequence of codepoints potentially interspersed with escapes. an escape is formed by an open square bracket [`\[] followed by a [*span control sequence], and arguments for that sequence like more styled-text. escapes can be nested. * strong {obj *|styled-text}: causes its text to stand out from the narrative, generally rendered as bold or a brighter color. * emphatic {obj !|styled-text}: indicates that its text should be spoken with emphasis, generally rendered as italics * custom style {span .|id|[$styled-text]}: applies a specially defined font style. for example, if you have defined [`caution] to mean "demibold italic underline", cortav will try to apply the proper weight and styling within the constraints of the current font to the span [$styled-text]. see the [>fonts-sty fonts section] for more information about this mechanism. * literal {obj `|styled-text}: indicates that its text is a reference to a literal sequence of characters or other discrete token. generally rendered in monospace * variable {obj $|styled-text}: indicates to the reader that its text is a placeholder, rather than a literal representation. generally rendered in italic monospace, ideally of a different color * underline {obj _|styled-text}: underlines the text. use sparingly on text intended for webpages -- underlined text [!is] distinct from links, but underlining non-links is still a violation of convention. * strikeout {obj ~|styled-text}: indicates that its text should be struck through or otherwise indicated for deletion * insertion {obj +|styled-text}: indicates that its text should be indicated as a new addition to the text body. ** consider using a macro definition [`\edit: [~[#1]][+[#2]]] to save typing if you are doing editing work * link \[>[!ref] [!styled-text]\]: produces a hyperlink or cross-reference denoted by [$ref], which may be either a URL specified with a reference or the name of an object like an image or section elsewhere in the document. the unicode characters [`→] and [`🔗] can also be used instead of [`>] to denote a link. * footnote {span ^|ref|[$styled-text]}: annotates the text with a defined footnote. in interactive output media [`\[^citations.qtheo Quantum Theosophy: A Neophyte's Catechism\]] will insert a link with the text [`Quantum Theosophy: A Neophyte's Catechism] that, when clicked, causes a footnote to pop up on the screen. for static output media, the text will simply have a superscript integer after it denoting where the footnote is to be found. * superscript {obj '|[$styled-text]} * subscript {obj ,|[$styled-text]} * raw {obj \\ |[$raw-text]}: causes all characters within to be interpreted literally, without expansion. the only special characters are square brackets, which must have a matching closing bracket, and backslashes. * raw literal \[$\\[!raw-text]\]: shorthand for [\[$[\…]]] * macro [` \{[!name] [!arguments]}]: invokes a [>ex.mac macro], specified with a reference * argument {obj #|var}: in macros only, inserts the [$var]-th argument. otherwise, inserts a context variable provided by the renderer. * raw argument {obj ##|var}: like above, but does not evaluate [$var]. ................................................................................ the interpreter should provide a [`cortav] table with the objects: * [`ctx]: contains context variables used files should return a table with the following members * [`macros]: an array of functions that return strings or arrays of strings when invoked. these will be injected into the global macro namespace. ###ts ts the [*ts] extension allows documents to be marked up for basic classification constraints and automatically redacted. if you are seriously relying on [`ts] for confidentiality, make damn sure you start the file with [$%[*requires] ts], so that rendering will fail with an error if the extension isn't supported. [`ts] currently has no support for misinformation. [`ts] enables the directives: * [`%[*ts] class [$scope level] ([$styled-text])]: indicates a classification level for either the whole document (scope [$doc]) or the next section (scope [$sec]). if the ts level is below [$level], the section will be redacted or rendering will fail with an error, as appropriate. if styled-text is included, this will be treated as the name of the classification level. * [`%[*ts] word [$scope word] ([$styled-text])]: indicates a codeword clearance that must be present for the text to render. if styled-text is present, this will be used to render the name of the codeword instead of [$word]. * [`%[*when] ts level [$level]] * [`%[*when] ts word [$word]] [`ts] enables the spans: * [` \[🔒#[$level] [$styled-text]\]]: redacts the span if the security level is below that specified. * [` \[🔒.[$word] [$styled-text]\]]: redacts the span if the specified codeword clearance is not enabled. (the padlock emoji is shorthand for [`%[*ts]].) [`ts] redacts spans securely; that is, they are simply replaced with an indicator that they have been redacted, without visually leaking the length of the redacted text. redacted sections are simply omitted. ~~~#ts-example example [cortav] ~~~ %ts word doc sorrowful-pines SORROWFUL PINES # intercept R1440 TCT S3 this communication between the ambassador of [*POLITY DOORMAT CRIMSON] "Socialist League world Glory" and an unknown noble of [*POLITY ROSE] "the Empire of a Thousand Suns" was intercepted by [*SYSTEM SUPINE WARBLE]. ................................................................................ corran: http://ʞ.cc/fic/spirals/society tengwar: https://en.wikipedia.org/wiki/Tengwar ###refimpl-switches switches [`cortav.lua] offers various switches to control its behavior. + long + short + function + | [`--out [$file]] :|:[`-o]:| sets the output file (default stdout) | | [`--log [$file]] :|:[`-l]:| sets the log file (default stderr) | | [`--define [$var] [$val]] :|:[`-d]:| sets the context variable [$var] to [$val] | | [`--mode-set [$mode]] :|:[`-y]:| activates the [>refimpl-mode mode] with ID [!mode] | [`--mode-clear [$mode]] :|:[`-n]:| disables the mode with ID [!mode] | | [`--mode [$id] [$val]] :|:[`-m]:| configures mode [$id] with the value [$val] | | [`--mode-set-weak [$mode]] :|:[`-Y]:| activates the [>refimpl-mode mode] with ID [$mode] if the source file does not specify otherwise | [`--mode-clear-weak [$mode]] :|:[`-N]:| disables the mode with ID [$mode] if the source file does not specify otherwise | [`--mode-weak [$id] [$val]] :|:[`-M]:| configures mode [$id] with the value [$val] if the source file does not specify otherwise | [`--help] :|:[`-h]:| display online help | | [`--version] :|:[`-V]:| display the interpreter version | ###refimpl-mode modes most of [`cortav.lua]'s implementation-specific behavior is controlled by use of [!modes]. these are namespaced options which may have a boolean, string, or numeric value. boolean modes are set with the [`-y] [`-n] flags; other modes use the [`-m] flags. most modes are defined by the renderer backend. the following modes affect the behavior of the frontend: + ID + type + effect ................................................................................ ####refimpl-rend-html-modes modes [`html] supports the following modes: * string (css length) [`html:width] sets a maximum width for the body content in order to make the page more readable on large displays * number [`html:accent] applies an accent hue to the generated webpage. the hue is specified in degrees, e.g. [$-m html:accent 0] applies a red accent. * flag [`html:dark-on-light] uses dark-on-light styling, instead of the default light-on-dark * flag [`html:fossil-uv] outputs an HTML snippet suitable for use with the Fossil VCS webserver. this is intended to be used with the unversioned content mechanism to host rendered versions of documentation written in cortav that's stored in a Fossil repository. * flag [`html:xhtml] generates syntactically-`valid' XHTML5 * flag [`html:epub] generates XHTML5 suitable for use in an EPUB3 archive * number [`html:hue-spread] generates a color palette based on the supplied accent hue. the larger the value, the more the other colors diverge from the accent hue. * string [`html:link-css] generates a document linking to the named stylesheet * flag [`html:gen-styles] embeds appropriate CSS styles in the document (default on) * flag [`html:snippet] produces a snippet of html instead of an entire web page. note that proper CSS scoping is not yet implemented (and can't be implemented hygienically since [`scoped] was removed 😢) * string [`html:title] specifies the webpage titlebar contents (normally autodetected from the document based on headings or directives) * string [`html:font] specifies the default font to use when rendering as a CSS font specification (e.g. [`-m html:font 'Alegreya, Junicode, Georgia, "Times New Roman"]) ~~~ $ cortav readme.ct --out readme.html \ -m render:format html \ -m html:width 40em \ ................................................................................ currently all internal colors are expressed and stored as HSL, and we're using code borrowed from my Minetest mod [`[>sorcrep sorcery]] & written by glowpelt for converting that into RGB for non-HTML outputs like groff. probably what should be done is to switch to LCH internally, accept both HSL and LCH input, and "downres" to the best representation each format can support. that's probably going to have to wait until someone who knows a bit more about color theory and math than me can do it (paging Lea Verou) sorcrep: https://c.hale.su/sorcery ### intent files there's currently no standard way to describe the intent and desired formatting of a document besides placing pragmata in the source file itself. this is extremely suboptimal, as when generating collections of documents, it's ideal to be able to keep all formatting information in one place. users should also be able to specify their own styling overrides that describe the way they prefer to read [`cortav] files, especially for uses like gemini or gopher integration. at some point soon [`cortav] needs to address this by adding intent files that can be activated from outside the source file, such as with a command line flag or a configuration file setting. these will probably consist of lines that are interpreted as pragmata. in addition to the standard intent format however, individual implementations should feel free to provide their own ways to provide intent metadata; e.g. the reference implementation, which has a lua interpreter available, should be able to take a lua script that runs after the parse stage and makes arbitrary alterations to the AST. this will be particularly useful for the end-user who wishes to specify a particular format she likes reading her files in without forcing that format on everyone she sends the compiled document to, as it will be able to interrogate the document and make intelligent decisions about what pragmata to apply. intent files should also be able to define [>rsrc resources], [>ctxvar context variables], and macros. |
Modified cortav.lua from [c52f4282f5] to [18c311a386].
113 114 115 116 117 118 119 120 121 122 123 124 125 126 ... 184 185 186 187 188 189 190 191 192 193 194 195 196 197 ... 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 ... 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 ... 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 .... 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 .... 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 .... 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 .... 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 |
ct.exns.tx(msg, self.src.file, self.line or 0, ...):throw() end; insert = function(self, block) block.origin = self:clone() table.insert(self.sec.blocks,block) return block end; ref = function(self,id) if not id:find'%.' then local rid = self.sec.refs[id] if self.sec.refs[id] then return self.sec.refs[id], id, self.sec else self:fail("no such ref %s in current section", id or '') end else ................................................................................ end return ct.ext.loaded[name].default end; context_var = function(self, var, ctx, test) local fail = function(...) if test then return false end ctx:fail(...) end if startswith(var, 'cortav.') then local v = var:sub(8) if v == 'page' then if ctx.page then return tostring(ctx.page) else return '(unpaged)' end elseif v == 'renderer' then ................................................................................ local val = os.getenv(v) if not val then return fail('undefined environment variable %s', v) end elseif self.stage.kind == 'render' and startswith(var, self.stage.format..'.') then -- TODO query the renderer somehow return fail('renderer %s does not implement variable %s', self.stage.format, var) elseif self.vars[var] then return self.vars[var] else if test then return false end return '' -- is this desirable behavior? end end; job = function(self, name, pred, ...) -- convenience func return self.docjob:fork(name, pred, ...) end }; mk = function() return { sections = {}; secorder = {}; embed = {}; meta = {}; vars = {}; ext = { inhibit = {}; need = {}; use = {}; }; enc = ss.str.enc.utf8; } end; ................................................................................ cmd = cmd; args = args; crit = crit; failthru = failthru; spans = spans; } end end ct.spanctls = { {seq = '!', parse = formatter 'emph'}; {seq = '*', parse = formatter 'strong'}; {seq = '~', parse = formatter 'strike'}; {seq = '+', parse = formatter 'insert'}; {seq = '\\', parse = function(s, c) -- raw return { kind = 'raw'; spans = {s}; origin = c:clone(); } end}; {seq = '`\\', parse = function(s, c) -- raw local o = c:clone(); local str = '' for c, p in ss.str.each(c.doc.enc, s) do local q = p:esc() if q then str = str .. q p.next.byte = p.next.byte + #q else str = str .. c end end return { kind = 'format'; style = 'literal'; spans = {{ kind = 'raw'; spans = {str}; origin = o; }}; origin = o; } end}; {seq = '`', parse = formatter 'literal'}; {seq = '$', parse = formatter 'variable'}; {seq = '^', parse = function(s,c) --footnotes local r, t = s:match '^([^%s]+)%s*(.-)$' return { kind = 'footnote'; ................................................................................ local sp = ct.parse_span(txt, c) c.doc.docjob:hook('meddle_span', sp, last) table.insert(last.lines, sp) j:hook('block_aside_attach', c, last, sp, l) j:hook('block_aside_line_insert', c, last, sp, l) end end}; {pred = function(s,c) return s:match'^[*:]' end, fn = blockwrap(function(l,c) -- list local stars = l:match '^([*:]+)' local depth = utf8.len(stars) local id, txt = l:sub(#stars+1):match '^(.-)%s*(.-)$' local ordered = stars:sub(#stars) == ':' if id == '' then id = nil end return { kind = 'list-item'; ................................................................................ c:fail('extension %s does not support critical directive %s', cmd, topcmd) end end elseif crit == '!' then c:fail('critical directive %s not supported',cmd) end end;}; {seq = '~~~', fn = blockwrap(function(l,c,j) local extract = function(ptn, str) local start, stop = str:find(ptn) if not start then return nil, str end local ex = str:sub(start,stop) local n = str:sub(1,start-1) .. str:sub(stop+1) return ex, n ................................................................................ if ctx.mode.expand then newline = ct.parse_span(l, ctx) else newline = {l} end table.insert(ctx.mode.listing.lines, newline) job:hook('block_listing_newline',ctx,ctx.mode.listing,newline) end else local mf = job:proc('modes', ctx.mode.kind) if not mf then ctx:fail('unimplemented syntax mode %s', ctx.mode.kind) end mf(job, ctx, l, dest) --NOTE: you are responsible for triggering the appropriate hooks if you insert anything! end else ................................................................................ end return false end if not tryseqs(ct.ctlseqs) then local found = false for eb, ext, state in job:each('blocks') do if tryseqs(eb, state) then found = true break end end if not found then ctx:fail 'incomprehensible input line' end end ................................................................................ end function ct.parse(file, src, mode, setup) -- this object is threaded down through the parse tree -- and copied to store information like the origin of the -- element in the source code local ctx = ct.ctx.mk(src) ctx.line = 0 ctx.doc = ct.doc.mk() ctx.doc.src = src ctx.sec = ctx.doc:mksec() -- toplevel section ctx.sec.origin = ctx:clone() ctx.lang = mode['meta:lang'] if mode['parse:enc'] then local e = ss.str.enc[mode['parse:enc']] if not e then ct.exns.enc('requested encoding not supported',mode['parse:enc']):throw() end ctx.doc.enc = e |
> > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > < < < < < < < < < < < < < < < < < < < < < < < | > > > > > > > > > > > > > > > > > > > > > > > | | < | < < < |
113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 ... 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 ... 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 ... 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 .... 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 .... 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 .... 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 .... 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 .... 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 |
ct.exns.tx(msg, self.src.file, self.line or 0, ...):throw() end; insert = function(self, block) block.origin = self:clone() table.insert(self.sec.blocks,block) return block end; init = function(ctx, doc, src) ctx.line = 0 ctx.doc = doc ctx.doc.src = src ctx.sec = doc:mksec() -- toplevel section ctx.sec.origin = ctx:clone() end; ref = function(self,id) if not id:find'%.' then local rid = self.sec.refs[id] if self.sec.refs[id] then return self.sec.refs[id], id, self.sec else self:fail("no such ref %s in current section", id or '') end else ................................................................................ end return ct.ext.loaded[name].default end; context_var = function(self, var, ctx, test) local fail = function(...) if test then return false end ctx:fail(...) end local scanParents = function(k) for k,p in pairs(self.parents) do local v = p:context_var(k, ctx, true) if v ~= false then return v end end end if startswith(var, 'cortav.') then local v = var:sub(8) if v == 'page' then if ctx.page then return tostring(ctx.page) else return '(unpaged)' end elseif v == 'renderer' then ................................................................................ local val = os.getenv(v) if not val then return fail('undefined environment variable %s', v) end elseif self.stage.kind == 'render' and startswith(var, self.stage.format..'.') then -- TODO query the renderer somehow return fail('renderer %s does not implement variable %s', self.stage.format, var) elseif startswith(var, 'super.') then local sp = scanParents(var:sub(8)) if sp == nil then if test then return false else return '' end else return sp end elseif self.vars[var] then return self.vars[var] else local sp = scanParents(var) if sp then return sp end if test then return false end return '' -- is this desirable behavior? end end; job = function(self, name, pred, ...) -- convenience func return self.docjob:fork(name, pred, ...) end; sub = function(self, ctx) -- convenience function for single-inheritance structure -- sets up a doc/ctx pair for a subdocument embedded in the source -- of a gretaer document, pointing subdoc props to global tables/values local newdoc = ct.doc.mk(self) newdoc.meta = self.meta newdoc.ext = self.ext newdoc.enc = self.enc newdoc.stage = self.stage -- vars are handled through proper recursion across all parents and -- are intentionally excluded here; subdocs can have their own vars -- without losing access to parent vars local nctx = ctx:clone() nctx:init(newdoc, ctx.src) nctx.line = ctx.line return newdoc, nctx end; }; mk = function(...) return { sections = {}; secorder = {}; embed = {}; meta = {}; vars = {}; parents = {...}; ext = { inhibit = {}; need = {}; use = {}; }; enc = ss.str.enc.utf8; } end; ................................................................................ cmd = cmd; args = args; crit = crit; failthru = failthru; spans = spans; } end end local function rawcode(s, c) -- raw local o = c:clone(); local str = '' for c, p in ss.str.each(c.doc.enc, s) do local q = p:esc() if q then str = str .. q p.next.byte = p.next.byte + #q else str = str .. c end end return { kind = 'format'; style = 'literal'; spans = {{ kind = 'raw'; spans = {str}; origin = o; }}; origin = o; } end ct.spanctls = { {seq = '!', parse = formatter 'emph'}; {seq = '*', parse = formatter 'strong'}; {seq = '~', parse = formatter 'strike'}; {seq = '+', parse = formatter 'insert'}; {seq = '`\\', parse = rawcode}; {seq = '\\\\', parse = rawcode}; {seq = '\\', parse = function(s, c) -- raw return { kind = 'raw'; spans = {s}; origin = c:clone(); } end}; {seq = '`', parse = formatter 'literal'}; {seq = '$', parse = formatter 'variable'}; {seq = '^', parse = function(s,c) --footnotes local r, t = s:match '^([^%s]+)%s*(.-)$' return { kind = 'footnote'; ................................................................................ local sp = ct.parse_span(txt, c) c.doc.docjob:hook('meddle_span', sp, last) table.insert(last.lines, sp) j:hook('block_aside_attach', c, last, sp, l) j:hook('block_aside_line_insert', c, last, sp, l) end end}; {pred = function(s,c) return s:match'^[*:]' end, fn = blockwrap(function(l,c) -- list local stars = l:match '^([*:]+)' local depth = utf8.len(stars) local id, txt = l:sub(#stars+1):match '^(.-)%s*(.-)$' local ordered = stars:sub(#stars) == ':' if id == '' then id = nil end return { kind = 'list-item'; ................................................................................ c:fail('extension %s does not support critical directive %s', cmd, topcmd) end end elseif crit == '!' then c:fail('critical directive %s not supported',cmd) end end;}; {pred = function(s) return s:match '^(>+)([^%s]*)%s*(.*)$' end, fn = function(l,c,j,d) local lvl,id,txt = l:match '^(>+)([^%s]*)%s*(.*)$' lvl = utf8.len(lvl) local last = d[#d] local node local ctx if last and last.kind == 'quote' and (id == nil or id == '' or id == last.id) then node = last ctx = node.ctx ctx.line = c.line -- is this enough?? else local doc doc, ctx = c.doc:sub(c) node = { kind = 'quote', doc = doc, ctx = ctx, id = id } j:hook('block_insert', c, node, l) table.insert(d, node) end ct.parse_line(txt, ctx, ctx.sec.blocks) end}; {seq = '~~~', fn = blockwrap(function(l,c,j) local extract = function(ptn, str) local start, stop = str:find(ptn) if not start then return nil, str end local ex = str:sub(start,stop) local n = str:sub(1,start-1) .. str:sub(stop+1) return ex, n ................................................................................ if ctx.mode.expand then newline = ct.parse_span(l, ctx) else newline = {l} end table.insert(ctx.mode.listing.lines, newline) job:hook('block_listing_newline',ctx,ctx.mode.listing,newline) end elseif ctx.mode.kind == 'quote' then else local mf = job:proc('modes', ctx.mode.kind) if not mf then ctx:fail('unimplemented syntax mode %s', ctx.mode.kind) end mf(job, ctx, l, dest) --NOTE: you are responsible for triggering the appropriate hooks if you insert anything! end else ................................................................................ end return false end if not tryseqs(ct.ctlseqs) then local found = false for eb, ext, state in job:each 'blocks' do if tryseqs(eb, state) then found = true break end end if not found then ctx:fail 'incomprehensible input line' end end ................................................................................ end function ct.parse(file, src, mode, setup) -- this object is threaded down through the parse tree -- and copied to store information like the origin of the -- element in the source code local ctx = ct.ctx.mk(src) ctx:init(ct.doc.mk(), src) ctx.lang = mode['meta:lang'] if mode['parse:enc'] then local e = ss.str.enc[mode['parse:enc']] if not e then ct.exns.enc('requested encoding not supported',mode['parse:enc']):throw() end ctx.doc.enc = e |
Modified desk/cortav.xml from [d0eba8f9ef] to [bda20d41cd].
109
110
111
112
113
114
115
116
117
118
119
120
121
122
...
129
130
131
132
133
134
135
136
137
138
139
140
141
142
|
<context name='span-del' attribute='Deleted Text' lineEndContext='#pop'>
<IncludeRules context='span'/>
</context>
<context name='span-cue' attribute='Span Cue' lineEndContext='#pop' fallthroughContext="error">
<StringDetect attribute='Span Cue' String='`\' context='#pop!flat-span' />
<DetectChar attribute='Span Cue' char='!' context='#pop!span-emph' />
<DetectChar attribute='Span Cue' char='*' context='#pop!span-strong' />
<DetectChar attribute='Span Cue' char='~' context='#pop!span-del' />
<AnyChar attribute='Span Cue' String='`$+🔒' context='#pop!span' />
<StringDetect attribute='Span Cue' String='→' context='#pop!ref' />
................................................................................
<DetectChar attribute='Span Cue' char='\' context='#pop!flat-span' />
<Detect2Chars attribute='Comment' char='%' char1='%' context='#pop!inline-comment' />
<Detect2Chars attribute='Critical Directive Cue' char='%' char1='!' context='#pop!inline-directive' />
<DetectChar attribute='Directive Cue' char='%' context='#pop!inline-directive' />
</context>
<context name='flat-span' attribute='Unstyled Text' lineEndContext='#pop'>
<Detect2Chars attribute='Escaped Char' context='#stay' char='\' char1=']'/>
<DetectChar attribute='Span Delimiter' context='#pop' char=']'/>
</context>
<context name='inline-comment' attribute='Comment' lineEndContext='#pop'>
<IncludeRules context='flat-span'/>
</context>
|
>
>
|
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
...
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
|
<context name='span-del' attribute='Deleted Text' lineEndContext='#pop'> <IncludeRules context='span'/> </context> <context name='span-cue' attribute='Span Cue' lineEndContext='#pop' fallthroughContext="error"> <StringDetect attribute='Span Cue' String='`\' context='#pop!flat-span' /> <StringDetect attribute='Span Cue' String='\\' context='#pop!flat-span' /> <DetectChar attribute='Span Cue' char='!' context='#pop!span-emph' /> <DetectChar attribute='Span Cue' char='*' context='#pop!span-strong' /> <DetectChar attribute='Span Cue' char='~' context='#pop!span-del' /> <AnyChar attribute='Span Cue' String='`$+🔒' context='#pop!span' /> <StringDetect attribute='Span Cue' String='→' context='#pop!ref' /> ................................................................................ <DetectChar attribute='Span Cue' char='\' context='#pop!flat-span' /> <Detect2Chars attribute='Comment' char='%' char1='%' context='#pop!inline-comment' /> <Detect2Chars attribute='Critical Directive Cue' char='%' char1='!' context='#pop!inline-directive' /> <DetectChar attribute='Directive Cue' char='%' context='#pop!inline-directive' /> </context> <context name='flat-span' attribute='Unstyled Text' lineEndContext='#pop'> <DetectChar attribute='Unstyled Text' context='flat-span' char='['/> <Detect2Chars attribute='Escaped Char' context='#stay' char='\' char1=']'/> <DetectChar attribute='Span Delimiter' context='#pop' char=']'/> </context> <context name='inline-comment' attribute='Comment' lineEndContext='#pop'> <IncludeRules context='flat-span'/> </context> |
Modified ext/transmogrify.lua from [ffa0ca0a64] to [ad59e4c740].
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
...
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
|
['(SM)'] = '℠'; }; }; } local quotes = { [ss.str.enc.utf8] = { ['en'] = {'“', '”'; '‘', '’'}; ['de'] = {'„', '“'; '‚', '‘'}; ['sp'] = {'«', '»'; '‹', '›'}; ['ja'] = {'「', '」'; '『', '』'}; ['fr'] = {'« ', ' »'; '‹ ', ' ›'}; [true] = {'“', '”'; '‘', '’'}; }; } local function meddle(ctx, t) local pts = patterns[ctx.doc.enc] if not pts then return t end local str = '' ................................................................................ version = ss.version {0,1; 'devel'}; contributors = {{name='lexi hale', handle='velartrill', mail='lexi@hale.su', homepage='https://hale.su'}}; default = true; -- on unless inhibited slow = true; hook = { doc_meddle_ast = function(job) for n, sec in pairs(job.doc.secorder) do if sec.kind=='ordinary' or sec.kind=='blockquote' or sec.kind=='footnote' then for i, block in pairs(sec.blocks) do if type(block.spans) == 'table' then enterspan(block.origin, block.spans) elseif type(block.spans) == 'string' then block.spans = meddle(block.origin, block.spans) end end end end end; }; } |
>
|
|
|
|
|
|
|
|
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
...
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
|
['(SM)'] = '℠'; }; }; } local quotes = { [ss.str.enc.utf8] = { -- 5 = elision char ['en'] = {'“', '”'; '‘', '’'; '’'}; ['de'] = {'„', '“'; '‚', '‘'; '’'}; ['sp'] = {'«', '»'; '‹', '›'; "’"}; ['ja'] = {'「', '」'; '『', '』'; "'"}; ['fr'] = {'« ',' »'; '‹ ',' ›'; "’"}; [true] = {'“', '”'; '‘', '’'; '’'}; }; } local function meddle(ctx, t) local pts = patterns[ctx.doc.enc] if not pts then return t end local str = '' ................................................................................ version = ss.version {0,1; 'devel'}; contributors = {{name='lexi hale', handle='velartrill', mail='lexi@hale.su', homepage='https://hale.su'}}; default = true; -- on unless inhibited slow = true; hook = { doc_meddle_ast = function(job) for n, sec in pairs(job.doc.secorder) do if sec.kind=='ordinary' or sec.kind=='quote' or sec.kind=='footnote' then for i, block in pairs(sec.blocks) do if type(block.spans) == 'table' then enterspan(block.origin, block.spans) elseif type(block.spans) == 'string' then block.spans = meddle(block.origin, block.spans) end end end end end; }; } |
Modified render/html.lua from [39c7338664] to [5903337619].
13 14 15 16 17 18 19 20 21 22 23 24 25 26 .. 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 .. 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 ... 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 ... 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 ... 251 252 253 254 255 256 257 258 259 260 261 262 263 264 ... 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 ... 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 ... 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 ... 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 ... 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 ... 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 .... 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 .... 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 |
function ct.render.html(doc, opts, setup) local doctitle = opts['title'] local f = string.format local getSafeID = ct.tool.namespace() local footnotes = {} local footnotecount = 0 local langsused = {} local langpairs = { lua = { color = 0x9377ff }; terra = { color = 0xff77c8 }; c = { name = 'C', color = 0x77ffe8 }; html = { color = 0xfff877 }; ................................................................................ li { padding: 0.1em 0; } ]]; list_ordered = [[]]; list_unordered = [[]]; footnote = [[ div.footnote { font-family: 90%; grid-template-columns: 1em 1fr min-content; grid-template-rows: 1fr min-content; position: fixed; padding: 1em; background: @tone(0.03); margin:auto; } @media screen { div.footnote { display: grid; left: 10em; right: 10em; max-width: calc(@width + 2em); max-height: 30vw; bottom: 1em; border: 1px solid black; transform: translateY(200%); transition: 0.4s; z-index: 100; } div.footnote:target { transform: translateY(0%); } #cover { position: fixed; top: 0; left: 0; height: 100vh; width: 100vw; ................................................................................ @tone/0.8(-0.07), @tone/0.4(-0.07)); opacity: 0%; transition: 1s; pointer-events: none; backdrop-filter: blur(0px); } div.footnote:target ~ #cover { opacity: 100%; pointer-events: all; backdrop-filter: blur(5px); } } @media print { div.footnote { display: grid; position: relative; } div.footnote:first-of-type { border-top: 1px solid black; } } div.footnote > a[href="#0"]{ grid-row: 2/3; grid-column: 3/4; display: block; text-align: center; padding: 0 0.3em; text-decoration: none; background: @tone(0.2); ................................................................................ font-size: 150%; -webkit-user-select: none; -ms-user-select: none; user-select: none; -webkit-user-drag: none; user-drag: none; } div.footnote > a[href="#0"]:hover { background: @tone(0.3); color: @tone(2); } div.footnote > a[href="#0"]:active { background: @tone(0.05); color: @tone(0.4); } @media print { div.footnote > a[href="#0"]{ display:none; } } div.footnote > div.number { text-align:right; grid-row: 1/2; grid-column: 1/2; } div.footnote > div.text { grid-row: 1/2; grid-column: 2/4; padding-left: 1em; overflow-y: auto; } div.footnote > div.text > p:first-child { margin-top: 0; } ]]; header = [[ body { padding: 0 2.5em !important } h1,h2,h3,h4,h5,h6 { border-bottom: 1px solid @tone(0.7); } h1 { font-size: 200%; border-bottom-style: double !important; border-bottom-width: 3px !important; margin: 0em -1em; } ................................................................................ h1,h2,h3,h4,h5,h6 { margin-top: 0; margin-bottom: 0; } :is(h1,h2,h3,h4,h5,h6) + p { margin-top: 0.4em; } ]]; headingAnchors = [[ :is(h1,h2,h3,h4,h5,h6) > a[href].anchor { text-decoration: none; font-size: 1.2em; padding: 0.3em; opacity: 0%; ................................................................................ } section > aside p { margin: 0; margin-top: 0.6em; } section > aside p:first-child { margin: 0; } ]]; code = [[ code { display: inline-block; background: @tone(-1); color: @tone(0.7); ................................................................................ doc.stage.job = renderJob; local runhook = function(h, ...) return renderJob:hook(h, render_state_handle, ...) end local tagproc do local elt = function(t,attrs) return f('<%s%s>', t, attrs and ss.reduce(function(a,b) return a..b end, '', ss.map(function(v,k) if v == true then return ' '..k elseif v then return f(' %s="%s"', k, v) end end, attrs)) or '') end tagproc = { toTXT = { tag = function(t,a,v) return v end; elt = function(t,a) return '' end; catenate = table.concat; ................................................................................ } end; catenate = function(...) return ... end; }; toHTML = { elt = elt; tag = function(t,attrs,body) return f('%s%s</%s>', elt(t,attrs), body, t) end; catenate = table.concat; }; } end local function getBaseRenderers(procs, span_renderers) local tag, elt, catenate = procs.tag, procs.elt, procs.catenate local htmlDoc = function(title, head, body) return [[<!doctype html>]] .. tag('html',nil, tag('head', nil, elt('meta',{charset = 'utf-8'}) .. (title and tag('title', nil, title) or '') .. (head or '')) .. tag('body', nil, body or '')) end local function htmlSpan(spans, block, sec) local text = {} ................................................................................ elseif d.crit then b.origin:fail('critical extension %s unavailable', d.ext) elseif d.failthru then return htmlSpan(d.spans, b, s) end end function span_renderers.footnote(f,b,s) addStyle 'footnote' local source, sid, ssec = b.origin:ref(f.ref) local cnc = getSafeID(ssec) .. ' ' .. sid local fn if footnotes[cnc] then fn = footnotes[cnc] else footnotecount = footnotecount + 1 fn = {num = footnotecount, origin = b.origin, fnid=cnc, source = source} fn.id = getSafeID(fn) footnotes[cnc] = fn end return tag('a', {href='#'..fn.id}, htmlSpan(f.spans) .. tag('sup',nil, fn.num)) end return span_renderers end local function getBlockRenderers(procs, sr) local tag, elt, catenate = procs.tag, procs.elt, procs.catenate local null = function() return catenate{} end local block_renderers = { anchor = function(b,s) ................................................................................ return tag('aside', {}, bn) end; ['break'] = function() -- HACK -- lists need to be rewritten to work like asides return ''; end; } return block_renderers; end local function getRenderers(procs) local span_renderers = getSpanRenderers(procs) local r = getBaseRenderers(procs,span_renderers) r.block_renderers = getBlockRenderers(procs, r) return r end local astproc = { toHTML = getRenderers(tagproc.toHTML); toTXT = getRenderers(tagproc.toTXT); toIR = { }; } astproc.toIR.span_renderers = ss.clone(astproc.toHTML); astproc.toIR.block_renderers = getBlockRenderers(tagproc.toIR,astproc.toHTML); -- note we use HTML here instead of IR span renderers, because as things ................................................................................ render_state_handle.tagproc = tagproc; -- bind to legacy names -- yikes this needs to be cleaned up so badly local ir = {} local dr = astproc.toHTML -- default renderers local plainr = astproc.toTXT local irBlockRdrs = astproc.toIR.block_renderers; render_state_handle.ir = ir; local function renderBlocks(blocks, irs) for i, block in ipairs(blocks) do local rd if irBlockRdrs[block.kind] then rd = irBlockRdrs[block.kind](block,sec) else local rdr = renderJob:proc('render',block.kind,'html') if rdr then rd = rdr({ state = render_state_handle; tagproc = tagproc.toIR; astproc = astproc.toIR; ................................................................................ rd.attrs.lang = rd.src.origin.lang end table.insert(irs.nodes, rd) runhook('ir_section_node_insert', rd, irs, sec) end end end runhook('ir_assemble', ir) for i, sec in ipairs(doc.secorder) do if doctitle == nil and sec.depth == 1 and sec.heading_node then doctitle = astproc.toTXT.htmlSpan(sec.heading_node.spans, sec.heading_node, sec) end local irs if sec.kind == 'ordinary' then if #(sec.blocks) > 0 then irs = {tag='section',attrs={id = getSafeID(sec)},nodes={}} runhook('ir_section_build', irs, sec) renderBlocks(sec.blocks, irs) end elseif sec.kind == 'blockquote' then elseif sec.kind == 'listing' then elseif sec.kind == 'embed' then end if irs then table.insert(ir, irs) end end for _, fn in pairs(footnotes) do local tag = tagproc.toIR.tag local body = {nodes={}} local ftir = {} for l in fn.source:gmatch('([^\n]*)') do ct.parse_line(l, fn.origin, ftir) end renderBlocks(ftir,body) local note = tag('div',{class='footnote',id=fn.id}, { tag('div',{class='number'}, tostring(fn.num)), tag('div',{class='text'}, body.nodes), tag('a',{href='#0'},'⤫') }) table.insert(ir, note) end if next(footnotes) then table.insert(ir, tagproc.toIR.tag('div',{id='cover'},'')) end -- restructure passes runhook('ir_restructure_pre', ir) ---- list insertion pass ................................................................................ local styles = {} if opts.width then table.insert(styles, string.format([[body {padding:0 1em;margin:auto;max-width:%s}]], opts.width)) end if opts.accent then table.insert(styles, string.format(':root {--accent:%s}', opts.accent)) end if opts.accent or (not opts['dark-on-light']) and (not opts['fossil-uv']) then addStyle 'accent' end for _,k in pairs(stylesNeeded.order) do if not stylesets[k] then ct.exns.unimpl('styleset %s not implemented (!)', k):throw() end table.insert(styles, prepcss(stylesets[k])) ................................................................................ if opts['link-css'] then local css = opts['link-css'] if type(css) ~= 'string' then ct.exns.mode('must be a string', 'html:link-css'):throw() end styletag = styletag .. tagproc.toHTML.elt('link',{rel='stylesheet',type='text/css',href=opts['link-css']}) end if next(styles) then if opts['gen-styles'] then styletag = styletag .. tagproc.toHTML.tag('style',{type='text/css'},table.concat(styles)) end table.insert(head, styletag) end if opts['fossil-uv'] then return tagproc.toHTML.tag('div',{class='fossil-doc',['data-title']=doctitle},styletag .. body) elseif opts.snippet then return styletag .. body else return dr.htmlDoc(doctitle, next(head) and table.concat(head), body) end end |
> > > > > > > > > > > | | | | | | | | | | | | | < > > > | | | | > > > > > > > > | > | > > > > > > > > > | > > > > | > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > | < | | > | > | > > > > | | | | | | | < > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | | > | | | |
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 .. 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 .. 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 ... 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 ... 172 173 174 175 176 177 178 179 180 181 182 183 184 185 ... 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 ... 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 ... 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 ... 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 ... 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 ... 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 ... 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 .... 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 .... 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 |
function ct.render.html(doc, opts, setup) local doctitle = opts['title'] local f = string.format local getSafeID = ct.tool.namespace() local footnotes = {} local footnotecount = 0 local cdata = function(...) return ... end if opts.epub then opts.xhtml = true end if opts.xhtml then cdata = function(s) return '<![CDATA[' .. s .. ']]>' end end local langsused = {} local langpairs = { lua = { color = 0x9377ff }; terra = { color = 0xff77c8 }; c = { name = 'C', color = 0x77ffe8 }; html = { color = 0xfff877 }; ................................................................................ li { padding: 0.1em 0; } ]]; list_ordered = [[]]; list_unordered = [[]]; footnote = [[ aside.footnote { font-family: 90%; grid-template-columns: 1em 1fr min-content; grid-template-rows: 1fr min-content; position: fixed; padding: 1em; background: @tone(0.03); margin:auto; } @media screen { aside.footnote { display: grid; left: 10em; right: 10em; max-width: calc(@width + 2em); max-height: 30vw; bottom: 1em; border: 1px solid black; transform: translateY(200%); transition: 0.4s; z-index: 100; } aside.footnote:target { transform: translateY(0%); } #cover { position: fixed; top: 0; left: 0; height: 100vh; width: 100vw; ................................................................................ @tone/0.8(-0.07), @tone/0.4(-0.07)); opacity: 0%; transition: 1s; pointer-events: none; backdrop-filter: blur(0px); } aside.footnote:target ~ #cover { opacity: 100%; pointer-events: all; backdrop-filter: blur(5px); } } @media print { aside.footnote { display: grid; position: relative; } aside.footnote:first-of-type { border-top: 1px solid black; } } aside.footnote > a[href="#0"]{ grid-row: 2/3; grid-column: 3/4; display: block; text-align: center; padding: 0 0.3em; text-decoration: none; background: @tone(0.2); ................................................................................ font-size: 150%; -webkit-user-select: none; -ms-user-select: none; user-select: none; -webkit-user-drag: none; user-drag: none; } aside.footnote > a[href="#0"]:hover { background: @tone(0.3); color: @tone(2); } aside.footnote > a[href="#0"]:active { background: @tone(0.05); color: @tone(0.4); } @media print { aside.footnote > a[href="#0"]{ display:none; } } aside.footnote > div.number { text-align:right; grid-row: 1/2; grid-column: 1/2; } aside.footnote > div.text { grid-row: 1/2; grid-column: 2/4; padding-left: 1em; overflow-y: auto; } aside.footnote > div.text > p:first-child { margin-top: 0; } ]]; header = [[ body { padding: 0 2.5em !important } h1,h2,h3,h4,h5,h6 { border-bottom: 1px solid @tone(0.7); } h1 { font-size: 200%; border-bottom-style: double !important; border-bottom-width: 3px !important; margin: 0em -1em; } ................................................................................ h1,h2,h3,h4,h5,h6 { margin-top: 0; margin-bottom: 0; } :is(h1,h2,h3,h4,h5,h6) + p { margin-top: 0.4em; } ]]; headingAnchors = [[ :is(h1,h2,h3,h4,h5,h6) > a[href].anchor { text-decoration: none; font-size: 1.2em; padding: 0.3em; opacity: 0%; ................................................................................ } section > aside p { margin: 0; margin-top: 0.6em; } section > aside p:first-child { margin: 0; } section aside + aside { margin-top: 0.5em; } ]]; code = [[ code { display: inline-block; background: @tone(-1); color: @tone(0.7); ................................................................................ doc.stage.job = renderJob; local runhook = function(h, ...) return renderJob:hook(h, render_state_handle, ...) end local tagproc do local html_open = function(t,attrs) if attrs then return t .. ss.reduce(function(a,b) return a..b end, '', ss.map(function(v,k) if v == true then return ' '..k elseif v then return f(' %s="%s"', k, v) end end, attrs)) else return t end end local elt = function(t,attrs) if opts.xhtml then return f('<%s />', html_open(t,attrs)) end return f('<%s>', html_open(t,attrs)) end tagproc = { toTXT = { tag = function(t,a,v) return v end; elt = function(t,a) return '' end; catenate = table.concat; ................................................................................ } end; catenate = function(...) return ... end; }; toHTML = { elt = elt; tag = function(t,attrs,body) return f('<%s>%s</%s>', html_open(t,attrs), body, t) end; catenate = table.concat; }; } end local function getBaseRenderers(procs, span_renderers) local tag, elt, catenate = procs.tag, procs.elt, procs.catenate local htmlDoc = function(title, head, body) local attrs local header = [[<!doctype html>]] if opts['epub'] then -- so cursed attrs = { xmlns = "http://www.w3.org/1999/xhtml"; ['xmlns:epub'] = "http://www.idpf.org/2007/ops"; } header = [[<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE html>]] end return header .. tag('html',attrs, tag('head', nil, (opts.epub and '' or elt('meta',{charset = 'utf-8'})) .. (title and tag('title', nil, title) or '') .. (head or '')) .. tag('body', nil, body or '')) end local function htmlSpan(spans, block, sec) local text = {} ................................................................................ elseif d.crit then b.origin:fail('critical extension %s unavailable', d.ext) elseif d.failthru then return htmlSpan(d.spans, b, s) end end function span_renderers.footnote(f,b,s) local linkattr = {} if opts.epub then linkattr['epub:type'] = 'noteref' else addStyle 'footnote' end local source, sid, ssec = b.origin:ref(f.ref) local cnc = getSafeID(ssec) .. ' ' .. sid local fn if footnotes[cnc] then fn = footnotes[cnc] else footnotecount = footnotecount + 1 fn = {num = footnotecount, origin = b.origin, fnid=cnc, source = source} fn.id = getSafeID(fn) footnotes[cnc] = fn end linkattr.href = '#'..fn.id return tag('a', linkattr, htmlSpan(f.spans) .. tag('sup',nil, fn.num)) end return span_renderers end local astproc local function getBlockRenderers(procs, sr) local tag, elt, catenate = procs.tag, procs.elt, procs.catenate local null = function() return catenate{} end local block_renderers = { anchor = function(b,s) ................................................................................ return tag('aside', {}, bn) end; ['break'] = function() -- HACK -- lists need to be rewritten to work like asides return ''; end; } function block_renderers.quote(b,s) local ir = {} local toIR = block_renderers for i, sec in ipairs(b.doc.secorder) do local secnodes = {} for i, bl in ipairs(sec.blocks) do if toIR[bl.kind] then table.insert(secnodes, toIR[bl.kind](bl,sec)) end end if next(secnodes) then if b.doc.secorder[2] then --#secs>1? -- only wrap in a section if >1 section table.insert(ir, tag('section', {id = getSafeID(sec)}, secnodes)) else ir = secnodes end end end return tag('blockquote', b.id and {id=getSafeID(b)} or {}, catenate(ir)) end return block_renderers; end local function getRenderers(procs) local span_renderers = getSpanRenderers(procs) local r = getBaseRenderers(procs,span_renderers) r.block_renderers = getBlockRenderers(procs, r) return r end astproc = { toHTML = getRenderers(tagproc.toHTML); toTXT = getRenderers(tagproc.toTXT); toIR = { }; } astproc.toIR.span_renderers = ss.clone(astproc.toHTML); astproc.toIR.block_renderers = getBlockRenderers(tagproc.toIR,astproc.toHTML); -- note we use HTML here instead of IR span renderers, because as things ................................................................................ render_state_handle.tagproc = tagproc; -- bind to legacy names -- yikes this needs to be cleaned up so badly local ir = {} local dr = astproc.toHTML -- default renderers local plainr = astproc.toTXT render_state_handle.ir = ir; local function renderBlocks(blocks, irs) for i, block in ipairs(blocks) do local rd if astproc.toIR.block_renderers[block.kind] then rd = astproc.toIR.block_renderers[block.kind](block,sec) else local rdr = renderJob:proc('render',block.kind,'html') if rdr then rd = rdr({ state = render_state_handle; tagproc = tagproc.toIR; astproc = astproc.toIR; ................................................................................ rd.attrs.lang = rd.src.origin.lang end table.insert(irs.nodes, rd) runhook('ir_section_node_insert', rd, irs, sec) end end end runhook('ir_assemble', ir) for i, sec in ipairs(doc.secorder) do if doctitle == nil and sec.depth == 1 and sec.heading_node then doctitle = astproc.toTXT.htmlSpan(sec.heading_node.spans, sec.heading_node, sec) end local irs if sec.kind == 'ordinary' then if #(sec.blocks) > 0 then irs = {tag='section',attrs={id = getSafeID(sec)},nodes={}} runhook('ir_section_build', irs, sec) renderBlocks(sec.blocks, irs) end elseif sec.kind == 'quote' then elseif sec.kind == 'listing' then elseif sec.kind == 'embed' then end if irs then table.insert(ir, irs) end end do local fnsorted = {} for _, fn in pairs(footnotes) do fnsorted[fn.num] = fn end for _, fn in ipairs(fnsorted) do local tag = tagproc.toIR.tag local body = {nodes={}} local ftir = {} for l in fn.source:gmatch('([^\n]*)') do ct.parse_line(l, fn.origin, ftir) end renderBlocks(ftir,body) local fattr = {id=fn.id} if opts.epub then ---UUUUUUGHHH local npfx = string.format('(%u) ', fn.num) if next(body.nodes) then local n = body.nodes[1] repeat if n.nodes[1] then if type(n.nodes[1]) == 'string' then n.nodes[1] = npfx .. n.nodes[1] break end n = n.nodes[1] else n.nodes[1] = {tag='p',attrs={},nodes={npfx}} break end until false else body.nodes[1] = {tag='p',attrs={},nodes={npfx}} end fattr['epub:type'] = 'footnote' else fattr.class = 'footnote' end local note = tag('aside', fattr, opts.epub and body.nodes or { tag('div',{class='number'}, tostring(fn.num)), tag('div',{class='text'}, body.nodes), tag('a',{href='#0'},'⤫') }) table.insert(ir, note) end end if next(footnotes) and not opts.epub then table.insert(ir, tagproc.toIR.tag('div',{id='cover'},'')) end -- restructure passes runhook('ir_restructure_pre', ir) ---- list insertion pass ................................................................................ local styles = {} if opts.width then table.insert(styles, string.format([[body {padding:0 1em;margin:auto;max-width:%s}]], opts.width)) end if opts.accent then table.insert(styles, string.format(':root {--accent:%s}', opts.accent)) end if not opts.epub and (opts.accent or (not opts['dark-on-light']) and (not opts['fossil-uv'])) then addStyle 'accent' end for _,k in pairs(stylesNeeded.order) do if not stylesets[k] then ct.exns.unimpl('styleset %s not implemented (!)', k):throw() end table.insert(styles, prepcss(stylesets[k])) ................................................................................ if opts['link-css'] then local css = opts['link-css'] if type(css) ~= 'string' then ct.exns.mode('must be a string', 'html:link-css'):throw() end styletag = styletag .. tagproc.toHTML.elt('link',{rel='stylesheet',type='text/css',href=opts['link-css']}) end if next(styles) then if opts['gen-styles'] then styletag = styletag .. tagproc.toHTML.tag('style',{type='text/css'},cdata(table.concat(styles))) end table.insert(head, styletag) end if opts['fossil-uv'] then return tagproc.toHTML.tag('div',{class='fossil-doc',['data-title']=doctitle},styletag .. body) elseif opts.snippet then return styletag .. body else return dr.htmlDoc(doctitle, next(head) and table.concat(head), body) end end |