tags unless they have a 'no-copy' class.
2 | document.addEventListener("DOMContentLoaded", function() {
3 | if (!navigator.clipboard) {
4 | return;
5 | }
6 |
7 | document.querySelectorAll("pre").forEach(node => {
8 | if (node.classList.contains("no-copy")) {
9 | return;
10 | }
11 |
12 | var wrapper = document.createElement('div');
13 | wrapper.classList.add("pre-copy-wrapper");
14 | node.parentNode.insertBefore(wrapper, node);
15 | wrapper.appendChild(node);
16 |
17 | let copyBtn = document.createElement("button");
18 | copyBtn.innerText = "[COPY]";
19 | wrapper.appendChild(copyBtn);
20 |
21 | copyBtn.addEventListener("click", async () => {
22 | let text = node.innerText;
23 | await navigator.clipboard.writeText(text);
24 | copyBtn.innerText = "[COPIED]";
25 | setTimeout(() => copyBtn.innerText = "[COPY]", 1000);
26 | })
27 | })
28 | });
29 |
--------------------------------------------------------------------------------
/docs/out/assets/fonts.css:
--------------------------------------------------------------------------------
1 | /**
2 | * -------------------------------------------------------------------------
3 | * Google fonts served locally.
4 | * -------------------------------------------------------------------------
5 | */
6 |
7 | @font-face {
8 | font-family: 'Crimson Text';
9 | font-style: normal;
10 | font-weight: 400;
11 | src: local('Crimson Text Regular'),
12 | local('CrimsonText-Regular'),
13 | url(fonts/CrimsonText-Regular.woff2) format('woff2'),
14 | url(fonts/CrimsonText-Regular.ttf) format('truetype');
15 | }
16 |
17 | @font-face {
18 | font-family: 'Crimson Text';
19 | font-style: italic;
20 | font-weight: 400;
21 | src: local('Crimson Text Italic'),
22 | local('CrimsonText-Italic'),
23 | url(fonts/CrimsonText-Italic.woff2) format('woff2'),
24 | url(fonts/CrimsonText-Italic.ttf) format('truetype');
25 | }
26 |
27 | @font-face {
28 | font-family: 'Crimson Text';
29 | font-style: normal;
30 | font-weight: 700;
31 | src: local('Crimson Text Bold'),
32 | local('CrimsonText-Bold'),
33 | url(fonts/CrimsonText-Bold.woff2) format('woff2'),
34 | url(fonts/CrimsonText-Bold.ttf) format('truetype');
35 | }
36 |
37 | /*
38 | The fonts below are packaged with the theme and can be uncommented
39 | if needed.
40 | */
41 |
42 | /*
43 | @font-face {
44 | font-family: 'Crimson Text';
45 | font-style: italic;
46 | font-weight: 700;
47 | src: local('Crimson Text Bold Italic'),
48 | local('CrimsonText-BoldItalic'),
49 | url(fonts/CrimsonText-BoldItalic.woff2) format('woff2'),
50 | url(fonts/CrimsonText-BoldItalic.ttf) format('truetype');
51 | }
52 |
53 | @font-face {
54 | font-family: 'Crimson Text';
55 | font-style: normal;
56 | font-weight: 600;
57 | src: local('Crimson Text SemiBold'),
58 | local('CrimsonText-SemiBold'),
59 | url(fonts/CrimsonText-SemiBold.woff2) format('woff2'),
60 | url(fonts/CrimsonText-SemiBold.ttf) format('truetype');
61 | }
62 |
63 | @font-face {
64 | font-family: 'Crimson Text';
65 | font-style: italic;
66 | font-weight: 600;
67 | src: local('Crimson Text SemiBold Italic'),
68 | local('CrimsonText-SemiBoldItalic'),
69 | url(fonts/CrimsonText-SemiBoldItalic.woff2) format('woff2'),
70 | url(fonts/CrimsonText-SemiBoldItalic.ttf) format('truetype');
71 | }
72 | */
73 |
--------------------------------------------------------------------------------
/docs/out/assets/fonts/CrimsonText-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dmulholl/syntext/41187f7a5db9458c557d991357cb70dbfd85c18e/docs/out/assets/fonts/CrimsonText-Bold.ttf
--------------------------------------------------------------------------------
/docs/out/assets/fonts/CrimsonText-Bold.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dmulholl/syntext/41187f7a5db9458c557d991357cb70dbfd85c18e/docs/out/assets/fonts/CrimsonText-Bold.woff2
--------------------------------------------------------------------------------
/docs/out/assets/fonts/CrimsonText-BoldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dmulholl/syntext/41187f7a5db9458c557d991357cb70dbfd85c18e/docs/out/assets/fonts/CrimsonText-BoldItalic.ttf
--------------------------------------------------------------------------------
/docs/out/assets/fonts/CrimsonText-BoldItalic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dmulholl/syntext/41187f7a5db9458c557d991357cb70dbfd85c18e/docs/out/assets/fonts/CrimsonText-BoldItalic.woff2
--------------------------------------------------------------------------------
/docs/out/assets/fonts/CrimsonText-Italic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dmulholl/syntext/41187f7a5db9458c557d991357cb70dbfd85c18e/docs/out/assets/fonts/CrimsonText-Italic.ttf
--------------------------------------------------------------------------------
/docs/out/assets/fonts/CrimsonText-Italic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dmulholl/syntext/41187f7a5db9458c557d991357cb70dbfd85c18e/docs/out/assets/fonts/CrimsonText-Italic.woff2
--------------------------------------------------------------------------------
/docs/out/assets/fonts/CrimsonText-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dmulholl/syntext/41187f7a5db9458c557d991357cb70dbfd85c18e/docs/out/assets/fonts/CrimsonText-Regular.ttf
--------------------------------------------------------------------------------
/docs/out/assets/fonts/CrimsonText-Regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dmulholl/syntext/41187f7a5db9458c557d991357cb70dbfd85c18e/docs/out/assets/fonts/CrimsonText-Regular.woff2
--------------------------------------------------------------------------------
/docs/out/assets/fonts/CrimsonText-SemiBold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dmulholl/syntext/41187f7a5db9458c557d991357cb70dbfd85c18e/docs/out/assets/fonts/CrimsonText-SemiBold.ttf
--------------------------------------------------------------------------------
/docs/out/assets/fonts/CrimsonText-SemiBold.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dmulholl/syntext/41187f7a5db9458c557d991357cb70dbfd85c18e/docs/out/assets/fonts/CrimsonText-SemiBold.woff2
--------------------------------------------------------------------------------
/docs/out/assets/fonts/CrimsonText-SemiBoldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dmulholl/syntext/41187f7a5db9458c557d991357cb70dbfd85c18e/docs/out/assets/fonts/CrimsonText-SemiBoldItalic.ttf
--------------------------------------------------------------------------------
/docs/out/assets/fonts/CrimsonText-SemiBoldItalic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dmulholl/syntext/41187f7a5db9458c557d991357cb70dbfd85c18e/docs/out/assets/fonts/CrimsonText-SemiBoldItalic.woff2
--------------------------------------------------------------------------------
/docs/out/assets/fonts/OFL.txt:
--------------------------------------------------------------------------------
1 | Copyright (c) 2010, Sebastian Kosch (sebastian@aldusleaf.org),
2 | with Reserved Font Name "Crimson" and "Crimson Text".
3 | This Font Software is licensed under the SIL Open Font License, Version 1.1.
4 | This license is copied below, and is also available with a FAQ at:
5 | http://scripts.sil.org/OFL
6 |
7 |
8 | -----------------------------------------------------------
9 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
10 | -----------------------------------------------------------
11 |
12 | PREAMBLE
13 | The goals of the Open Font License (OFL) are to stimulate worldwide
14 | development of collaborative font projects, to support the font creation
15 | efforts of academic and linguistic communities, and to provide a free and
16 | open framework in which fonts may be shared and improved in partnership
17 | with others.
18 |
19 | The OFL allows the licensed fonts to be used, studied, modified and
20 | redistributed freely as long as they are not sold by themselves. The
21 | fonts, including any derivative works, can be bundled, embedded,
22 | redistributed and/or sold with any software provided that any reserved
23 | names are not used by derivative works. The fonts and derivatives,
24 | however, cannot be released under any other type of license. The
25 | requirement for fonts to remain under this license does not apply
26 | to any document created using the fonts or their derivatives.
27 |
28 | DEFINITIONS
29 | "Font Software" refers to the set of files released by the Copyright
30 | Holder(s) under this license and clearly marked as such. This may
31 | include source files, build scripts and documentation.
32 |
33 | "Reserved Font Name" refers to any names specified as such after the
34 | copyright statement(s).
35 |
36 | "Original Version" refers to the collection of Font Software components as
37 | distributed by the Copyright Holder(s).
38 |
39 | "Modified Version" refers to any derivative made by adding to, deleting,
40 | or substituting -- in part or in whole -- any of the components of the
41 | Original Version, by changing formats or by porting the Font Software to a
42 | new environment.
43 |
44 | "Author" refers to any designer, engineer, programmer, technical
45 | writer or other person who contributed to the Font Software.
46 |
47 | PERMISSION & CONDITIONS
48 | Permission is hereby granted, free of charge, to any person obtaining
49 | a copy of the Font Software, to use, study, copy, merge, embed, modify,
50 | redistribute, and sell modified and unmodified copies of the Font
51 | Software, subject to the following conditions:
52 |
53 | 1) Neither the Font Software nor any of its individual components,
54 | in Original or Modified Versions, may be sold by itself.
55 |
56 | 2) Original or Modified Versions of the Font Software may be bundled,
57 | redistributed and/or sold with any software, provided that each copy
58 | contains the above copyright notice and this license. These can be
59 | included either as stand-alone text files, human-readable headers or
60 | in the appropriate machine-readable metadata fields within text or
61 | binary files as long as those fields can be easily viewed by the user.
62 |
63 | 3) No Modified Version of the Font Software may use the Reserved Font
64 | Name(s) unless explicit written permission is granted by the corresponding
65 | Copyright Holder. This restriction only applies to the primary font name as
66 | presented to the users.
67 |
68 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
69 | Software shall not be used to promote, endorse or advertise any
70 | Modified Version, except to acknowledge the contribution(s) of the
71 | Copyright Holder(s) and the Author(s) or with their explicit written
72 | permission.
73 |
74 | 5) The Font Software, modified or unmodified, in part or in whole,
75 | must be distributed entirely under this license, and must not be
76 | distributed under any other license. The requirement for fonts to
77 | remain under this license does not apply to any document created
78 | using the Font Software.
79 |
80 | TERMINATION
81 | This license becomes null and void if any of the above conditions are
82 | not met.
83 |
84 | DISCLAIMER
85 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
86 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
87 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
88 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
89 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
90 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
91 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
92 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
93 | OTHER DEALINGS IN THE FONT SOFTWARE.
94 |
--------------------------------------------------------------------------------
/docs/out/assets/graphite.css:
--------------------------------------------------------------------------------
1 | /**
2 | * -------------------------------------------------------------------------
3 | * Micro Reset
4 | * -------------------------------------------------------------------------
5 | */
6 |
7 | *, *:before, *:after {
8 | box-sizing: inherit;
9 | }
10 |
11 | html {
12 | box-sizing: border-box;
13 | font-size: 100%;
14 | -webkit-text-size-adjust: none;
15 | text-size-adjust: none;
16 | }
17 |
18 | body {
19 | margin: 0;
20 | padding: 0;
21 | }
22 |
23 | form, fieldset, legend {
24 | margin: 0;
25 | padding: 0;
26 | }
27 |
28 | button, input, select, textarea, label {
29 | margin: 0;
30 | padding: 0;
31 | font-family: inherit;
32 | font-size: inherit;
33 | }
34 |
35 | img {
36 | border: 0;
37 | max-width: 100%;
38 | }
39 |
40 | /**
41 | * -------------------------------------------------------------------------
42 | * Typography
43 | * -------------------------------------------------------------------------
44 | */
45 |
46 | body {
47 | font-size: 17px;
48 | color: black;
49 | font-family: 'Crimson Text', Georgia, serif;
50 | line-height: 1.75;
51 | }
52 |
53 | h1 {
54 | padding: 0;
55 | margin: 80px 0 30px;
56 | line-height: 1.25;
57 | text-align: center;
58 | font-size: 32px;
59 | font-weight: normal;
60 | }
61 |
62 | h2 {
63 | padding: 0;
64 | margin: 80px 0 30px;
65 | line-height: 1.25;
66 | text-align: center;
67 | font-size: 22px;
68 | font-weight: bold;
69 | }
70 |
71 | h2.underline {
72 | padding: 0;
73 | margin: 80px 0 35px;
74 | line-height: 1.25;
75 | text-align: left;
76 | font-size: 22px;
77 | font-weight: bold;
78 | padding-bottom: 5px;
79 | border-bottom: 1px solid #ccc;
80 | }
81 |
82 | h3, h4, h5, h6 {
83 | padding: 0;
84 | margin: 80px 0 30px;
85 | line-height: 1.25;
86 | text-align: left;
87 | font-size: 19px;
88 | font-weight: bold;
89 | }
90 |
91 | p {
92 | margin: 25px 0;
93 | padding: 0;
94 | overflow-wrap: break-word;
95 | }
96 |
97 | blockquote {
98 | margin: 40px 0px;
99 | padding: 0 20px;
100 | border-left: 3px solid #ddd;
101 | font-style: italic;
102 | }
103 |
104 | .blockquote-caption {
105 | margin: -10px 20px 40px;
106 | text-align: right;
107 | }
108 |
109 | abbr[title] {
110 | border-bottom: 1px dotted;
111 | cursor: help;
112 | }
113 |
114 | sup, sub {
115 | font-size: 75%;
116 | line-height: 0;
117 | position: relative;
118 | vertical-align: baseline;
119 | }
120 |
121 | sup {
122 | bottom: 1.5ex;
123 | padding: 0 0.5ex;
124 | }
125 |
126 | sub {
127 | top: .5ex;
128 | }
129 |
130 | /**
131 | * Code
132 | */
133 |
134 | pre, code {
135 | font-size: 13px;
136 | font-family: Courier, monospace;
137 | }
138 |
139 | code {
140 | margin: 0 2px;
141 | padding: 1px 5px;
142 | white-space: nowrap;
143 | border: 1px solid #e8e8e8;
144 | border-radius: 3px;
145 | background-color: #f8f8f8;
146 | }
147 |
148 | pre {
149 | margin: 40px 0;
150 | padding: 16px 20px;
151 | border-top: 1px dotted #bbb;
152 | border-bottom: 1px dotted #bbb;
153 | background-color: #f8f8f8;
154 | overflow: auto;
155 | line-height: 1.4;
156 | }
157 |
158 | /* Markdown renderers tend to wrap code blocks in
140 | An unordered list can use an asterisk *, dash -, plus-symbol +, or unicode bullet-symbol \u2022 as its list-item marker:
141 |
142 |
143 | * foo - foo + foo • foo
144 | * bar - bar + bar • bar
145 | * baz - baz + baz • baz
146 |
147 |
148 | An ordered list uses either integer-period <int>. or hash-period #. as its list-item marker:
149 |
150 |
151 | 1. foo #. foo
152 | 2. bar #. bar
153 | 3. baz #. baz
154 |
155 |
156 | Ordered lists are numbered according to their opening integer:
157 |
158 |
159 | 5. This list starts.
160 | 6. With list item 5.
161 |
162 |
163 | List-item markers can be indented by up to three spaces. A list-item consists of its opening line plus all subsequent blank or indented lines:
164 |
165 |
166 | * This list item is split
167 | over two lines.
168 |
179 | Note that switching to a different list-item marker will begin a new list, i.e. the following markup will create two separate lists each containing a single item:
180 |
181 |
182 | - foo
183 | + bar
184 |
185 |
186 | Block Lists
187 |
188 |
189 | Block lists use bracketed list-item markers:
190 |
191 |
192 | (*) Unordered block list item.
193 |
194 | (#) Ordered block list item.
195 |
196 |
197 | Each list-item in a block list is parsed as a new block-level context and can contain any number of block-level elements, including paragraphs, headings, and nested lists.
198 |
199 |
200 | (1) This list item contains a paragraph
201 | and a compact list.
202 |
203 | 1. foo
204 | 2. bar
205 |
206 | (2) This list item contains two paragraphs.
207 | This is the first.
208 |
209 | And this is the second.
210 |
211 |
212 | A list-item consists of its opening line plus all subsequent blank or indented lines.
213 |
214 |
215 | Definition Lists
216 |
217 |
218 | Syntext supports definition lists with terms enclosed in double braces:
219 |
220 |
221 | [[ Term 1 ]]
222 |
223 | This is the definition of the first term.
224 |
225 | [[ Term 2 ]]
226 |
227 | This is the definition of the second term.
228 |
229 |
230 | A term's definition consists of all subsequent blank or indented lines and can contain any number of block-level elements.
231 |
232 |
233 | Code Blocks
234 |
235 |
236 | A block of text indented by one tab or four spaces is treated as a code block and wrapped in <pre> tags in the HTML output. The code block can contain blank lines and is ended by the first non-indented line.
237 |
238 |
239 | This paragraph is followed by a code block.
240 |
241 | <p>Hello world!</p>
242 |
243 |
244 | HTML in code blocks is automatically escaped. To specify the language, use an explicit code tag.
245 |
A lightweight, markdownish markup language for generating HTML.
27 |
28 |
29 |
Version 3.1.0
30 |
31 |
71 |
72 |
73 |
74 |
75 |
Changelog
76 |
77 |
78 |
79 |
80 | 3.1.0
81 |
82 |
83 |
84 | Add support for a raw argument to all shorthand HTML tags.
85 |
86 |
87 |
88 | 3.0.0
89 |
90 |
91 |
92 | This release removes support for backslashed character escapes. This feature was overly aggressive and could lead to unexpected and unintuitive changes in content. Use ``verbatim`` quotes or HTML instead to stop Syntext parsing characters as formatting instructions — Syntext ignores inline HTML tags and all content inside block-level HTML. Consider using HTML escape codes for particularly problematic characters, e.g. | for the | symbol inside tables.
93 |
94 |
95 | Add support for a ::: raw tag that passes its content through without any further processing.
96 |
97 |
98 |
99 | 2.10.0
100 |
101 |
102 |
103 | A missing block-level closing HTML tag now raises a SyntextError exception.
104 |
105 |
106 |
107 | 2.9.0
108 |
109 |
110 |
111 | Add support for boxed headings.
112 |
113 |
114 |
115 | 2.8.0
116 |
117 |
118 |
119 | The parser for recognising and ignoring block-level HTML in the input has been upgraded.
120 |
121 |
122 |
123 | 2.7.0
124 |
125 |
126 |
127 | Inline bold, italic, code, and verbatim delimiters can now be split over multiple lines.
128 |
129 |
130 |
131 | 2.6.0
132 |
133 |
134 |
135 | Add support for linked images, image alt text, and image captions.
136 |
137 |
138 | Add documentation for inline footnote references.
139 |
A lightweight, markdownish markup language for generating HTML.
27 |
28 |
29 |
Version 3.1.0
30 |
31 |
71 |
72 |
73 |
74 |
75 |
Home
76 |
77 |
78 |
79 |
80 | Syntext is a lightweight, markdownish markup language for generating HTML. It's implemented in Python and can be used as both a command line utility and a Python library.
81 |
82 |
83 | When used on the command line Syntext reads from stdin and prints to stdout:
84 |
85 |
86 | $ syntext < input.txt > output.html
87 |
88 |
89 | To use Syntext as a Python library call its render() function with a string of input:
90 |
99 | Syntext shares much of its basic syntax with Markdown:
100 |
101 |
102 | This paragraph contains *italic* and **bold** text.
103 | It also contains a `code sample` in backticks.
104 |
105 | This paragraph contains a [link](http://example.com).
106 |
107 |
108 | Syntext differs from Markdown in supporting an indentation-based shorthand syntax for generating arbitrary HTML:
109 |
110 |
111 | :div .outer
112 | :div .inner
113 | This is a paragraph.
114 |
115 |
116 | Syntext also includes out-of-the-box support for tables, tables-of-contents, definition lists, syntax highlighting, and footnotes.
117 |
118 |
119 | Installation
120 |
121 |
122 | Install directly from the Python Package Index using pip:
123 |
124 |
125 | $ pip install syntext
126 |
127 |
128 | Syntext requires Python 3.6 or later.
129 |
130 |
131 | Command Line Interface
132 |
133 |
134 | Use the syntext --help flag to view the utility's command line help:
135 |
136 |
137 | Usage: syntext [FLAGS]
138 |
139 | Renders input text in Syntext format into HTML. Reads
140 | from stdin and prints to stdout.
141 |
142 | Example:
143 |
144 | $ syntext < input.txt > output.html
145 |
146 | Flags:
147 | -d, --debug Run in debug mode.
148 | -h, --help Print the application's help text.
149 | -p, --pygmentize Add syntax highlighting to code.
150 | -v, --version Print the application's version.
151 |
152 |
153 | Syntext can use the Pygments package to add syntax highlighting to code blocks; this feature can be enabled via the --pygmentize flag. (Pygments is installed automatically when you install Syntext using pip).
154 |
155 |
156 | Only code blocks with a language attribute will have syntax highlighting applied.
157 |
153 | Syntext also supports Markdown-style reference images:
154 |
155 |
156 | ![alt text][ref]
157 |
158 |
159 | Image references can be specified anywhere in the document. Their form is identical to that of link references. If the reference text is omitted from the image, the alt text will be used instead when searching for references.
160 |
161 |
162 | Dashes
163 |
164 |
165 | Double hyphens -- are converted into en-dashes, –; triple hyphens --- are converted into em-dashes, —.
166 |
167 |
168 | Superscripts & Subscripts
169 |
170 |
171 | Superscripts are indicated using a caret:
172 |
173 |
174 | X^{2}
175 |
176 |
177 | Subscripts are indicated using an underscore:
178 |
179 |
180 | H_{2}O
181 |
182 |
183 | Text to be super or subscripted should be wrapped in curly braces.
184 |
185 |
186 | Verbatim Quotes
187 |
188 |
189 | Double backticks create a verbatim environment:
190 |
191 |
192 | ``verbatim``
193 |
194 |
195 | The content of a verbatim environment is passed through in its raw state without any further processing.
196 |
197 |
198 | This can be useful for escaping elements that would otherwise be parsed as formatting instructions by Syntext, e.g. inline LaTeX markup:
199 |
A lightweight, markdownish markup language for generating HTML.
27 |
28 |
29 |
Version 3.1.0
30 |
31 |
71 |
72 |
73 |
74 |
75 |
License
76 |
77 |
78 |
79 |
80 | This library is released under the Zero-Clause BSD licence (0BSD):
81 |
82 |
83 | Permission to use, copy, modify, and/or distribute this
84 | software for any purpose with or without fee is hereby
85 | granted.
86 |
87 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS
88 | ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
89 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO
90 | EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
91 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
92 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
93 | WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
94 | TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
95 | USE OR PERFORMANCE OF THIS SOFTWARE.
96 |
100 | Syntext can automatically generate a table of contents for a document. This table can be inserted using the insert tag:
101 |
102 |
103 | ::: insert toc
104 |
105 |
106 | The default listing skips H1 headings to avoid including the document title itself. A full listing of all the document's headings can be inserted using the fulltoc keyword:
107 |
108 |
109 | ::: insert fulltoc
110 |
111 |
112 | The table of contents is generated as an unordered list of links.
113 |
114 |
115 | Footnotes
116 |
117 |
118 | Syntext includes built-in support for footnotes:
119 |
120 |
121 | This sentence ends with a footnote reference.[^1]
122 |
123 |
124 | Footnote references can omit their index to take advantage of automatic numbering:
125 |
126 |
127 | This sentence ends with an automatically-numbered
128 | reference.[^]
129 |
130 |
131 | Footnotes themselves can be specified anywhere in the document using the footnote tag:
132 |
133 |
134 | ::: footnote 1
135 | This is a footnote. It can contain any kind of
136 | block-level markup.
137 |
138 |
139 | Footnotes can also omit their index to take advantage of automatic numbering:
140 |
141 |
142 | ::: footnote
143 | This is an automatically-numbered footnote.
144 |
145 |
146 | Note that footnote indices do not have to be numeric — any sequence of non-whitespace characters can be used.
147 |
148 |
149 | You can specify the insertion point for your footnotes using the insert tag:
150 |
151 |
152 | ::: insert footnotes
153 |
154 |
155 | Footnote references are wrapped in the following markup:
156 |
175 | Each footnote index is backlinked to its reference in the document.
176 |
177 |
178 | Alternate Footnote Styles
179 |
180 |
181 | Syntext supports two alternate styles for footnote references: superscripted1 and inline 2.
182 |
183 |
184 | Superscripted references look like this [^1] while inline references look like this [fn:2]. The only difference in markup is that superscripted references are wrapped in a <sup> tag while inline references are wrapped in a <span>.
185 |
186 |
187 | Tab Translation Size
188 |
189 |
190 | Tab characters in input text are translated into spaces — by default, 4 spaces per tab. This value can be customized via the tabsize argument:
191 |
192 |
193 | html=syntext.render(text,tabsize=8)
194 |
195 |
196 | This argument is also available on the command line:
197 |
A lightweight, markdownish markup language for generating HTML.
27 |
28 |
29 |
Version 3.1.0
30 |
31 |
71 |
72 |
73 |
74 |
75 |
Raw HTML
76 |
77 |
78 |
79 |
80 | Block HTML
81 |
82 |
83 | Syntext can recognise and ignore block-level HTML in its input so you can freely mix and match Syntext markup and raw HTML.
84 |
85 |
86 | :div .outer
87 | This is a paragraph.
88 |
89 | <div class="inner">
90 | <p>This is raw HTML.</p>
91 | </div>
92 |
93 | This is another paragraph.
94 |
95 |
96 | Syntext treats the content of block-level HTML tags as raw text and passes it through without any further processing.
97 |
98 |
99 | Inline HTML
100 |
101 |
102 | Syntext ignores inline HTML tags so it's fine to mix and match inline HTML and Syntext markup.
103 |
104 |
105 | The HTML syntax characters <, >, and & are automatically escaped unless they form part of a HTML tag or character entity.
106 |
103 | A shorthand block opens with a header line containing a colon, a HTML tag, and, optionally, a set of attributes. The block's content consists of all subsequent blank or indented lines following the header.
104 |
105 |
106 | The block's content can be indented by any number of spaces; the common indent is stripped and the first non-indented line ends the block. Leading and trailing blank lines are also trimmed from the content.
107 |
108 |
109 | To take a simple example, the markup below:
110 |
111 |
112 | :div
113 | This is a paragraph.
114 |
115 |
116 | generates the following HTML:
117 |
118 |
119 | <div>
120 | <p>This is a paragraph.</p>
121 | </div>
122 |
123 |
124 | Header Meta
125 |
126 |
127 | A block header can contain a single ID, any number of classes, and any number of named attributes, e.g:
128 |
129 |
130 | :div .foo .bar #baz
131 | This is a paragraph.
132 |
133 |
134 | generates the following HTML:
135 |
136 |
137 | <div class="foo bar" id="baz">
138 | <p>This is a paragraph.</p>
139 | </div>
140 |
141 |
142 | Boolean attributes can be specified using an & symbol, e.g. &checked. Attributes with values can be specified without quotes as long as the value does not contain spaces, e.g.
143 |
144 |
145 | :div attr1=foo attr2="bar baz"
146 |
147 |
148 | Nesting
149 |
150 |
151 | Blocks can be nested to any depth, e.g:
152 |
153 |
154 | :div .outer
155 | :div .inner
156 | This is a paragraph.
157 |
88 | A block header can contain a single ID, any number of classes, and any number of named attributes. Block syntax also allows for one or more arguments to be supplied (how these are interpreted depends on the tag).
89 |
90 |
91 | Boolean attributes can be specified using an & symbol, e.g. &checked. Attributes with values can be specified without quotes as long as the value does not contain spaces.
92 |
93 |
94 | A block's tag determines how its content is processed. In general blocks can be nested to any depth and can contain any block-level content.
95 |
96 |
97 | Although a block's content is determined solely by its indentation an optional end marker can be added to support syntax highlighting in editors which struggle with indentation-based syntax:
98 |
99 |
100 | ::: tag
101 | block content
102 | ...
103 | ::: end
104 |
105 |
106 | Trailing colons in the header line are optional:
107 |
108 |
109 | ::: hr :::
110 |
111 |
112 | is equivalent to:
113 |
114 |
115 | ::: hr
116 |
117 |
118 | A nl2lb / nl2br argument can be added to any block to turn on newline-to-linebreak mode for all nested content.
119 |
132 | Wraps a code sample in <pre> tags. Automatically escapes the HTML syntax characters <, >, and &.
133 |
134 |
135 | This tag accepts an optional argument specifying the language of the code. If a language is specified then a lang-<language> class and a data-lang="<language>" attribute are added to the <pre> element.
136 |
137 |
138 | If a language is specified and the Pygments package is installed then syntax highlighting can be applied to the code sample. This feature is turned off by default but can be enabled by supplying a pygmentize=True keyword argument to the render() function:
139 |
174 | Inserts an <img> element. The first argument is used as the source url.
175 |
176 |
177 |
178 | If the first line of the block's content is wrapped in square brackets it will be treated as the image's alt text.
179 |
180 |
181 | If a caption is provided, it will be wrapped in a <figcaption> element and the image and caption together will be wrapped in a <figure> element.
182 |
183 |
184 | If the !image tag is used, the image will be wrapped in a link to itself.
185 |
186 |
187 |
188 | Classes, attributes, etc. are attached to the outermost element.
189 |
198 | Creates an info box — a <div> element with the class infobox which can be styled appropriately using CSS.
199 |
200 |
201 | • link
202 |
203 |
204 | ::: link http://example.com/
205 | Link text or any other inline content.
206 |
207 |
208 | Inserts a link. The first argument is used as the url.
209 |
210 |
211 | • quote
212 |
213 |
214 | ::: quote "Oscar Wilde"
215 | Work is the curse of the drinking classes.
216 |
217 |
218 | Inserts a <blockquote> element. If a caption argument is provided it will follow the blockquote element wrapped in a <p> element with the class .blockquote-caption.
219 |
220 |
221 | • raw
222 |
223 |
224 | ::: raw
225 | This content will be passed through in its raw state.
226 |
227 |
228 | Content inside a raw tag will be passed through in its raw state without any further processing.
229 |
230 |
231 | • table
232 |
233 |
234 | Indicates that the block contains a table:
235 |
278 | Cells in the left column above receive the class left, cells in the center column receive the class center, and cells in the right column receive the class right.
279 |
280 |
281 | Attributes specified in the block header will be applied to the <table> element.
282 |
283 |
284 | Note that you can't use the | symbol inside table literals — use the HTML escape code | instead.
285 |
286 |
287 |
288 |
289 |
290 |
291 |
--------------------------------------------------------------------------------
/docs/src/block-syntax.stx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Block Syntax
3 | meta_title: Syntext — Block Syntax
4 | ---
5 |
6 | ::: insert toc :::
7 |
8 |
9 |
10 | ### Paragraphs
11 |
12 | A paragraph is a group of adjacent lines separated by one or more blank lines:
13 |
14 | This is a paragraph. Note that it includes
15 | all adjacent lines of text.
16 |
17 | This is a second paragraph.
18 |
19 |
20 |
21 | ### Headings
22 |
23 | Headings are indicated by hash symbols. The number of hash symbols indicates the level of the heading.
24 |
25 | # H1 Heading
26 |
27 | ## H2 Heading
28 |
29 | Trailing symbols are optional:
30 |
31 | ### H3 Heading ###
32 |
33 | A heading consisting entirely of dashes will be ignored. You can use this feature to create 'boxed' headings:
34 |
35 | # --------------- #
36 | # Boxed Heading #
37 | # --------------- #
38 |
39 |
40 |
41 | ### Lists
42 |
43 | An unordered list can use an asterisk `*`, dash `-`, plus-symbol `+`, or unicode bullet-symbol `\u2022` as its list-item marker:
44 |
45 | * foo - foo + foo • foo
46 | * bar - bar + bar • bar
47 | * baz - baz + baz • baz
48 |
49 | An ordered list uses either integer-period `.` or hash-period `#.` as its list-item marker:
50 |
51 | 1. foo #. foo
52 | 2. bar #. bar
53 | 3. baz #. baz
54 |
55 | Ordered lists are numbered according to their opening integer:
56 |
57 | 5. This list starts.
58 | 6. With list item 5.
59 |
60 | List-item markers can be indented by up to three spaces. A list-item consists of its opening line plus all subsequent blank or indented lines:
61 |
62 | * This list item is split
63 | over two lines.
64 |
65 | List items can contain nested lists:
66 |
67 | * foo
68 | 1. bar
69 | 2. baz
70 | * bam
71 |
72 | Note that switching to a different list-item marker will begin a new list, i.e. the following markup will create two separate lists each containing a single item:
73 |
74 | - foo
75 | + bar
76 |
77 |
78 |
79 | ### Block Lists
80 |
81 | Block lists use bracketed list-item markers:
82 |
83 | (*) Unordered block list item.
84 |
85 | (#) Ordered block list item.
86 |
87 | Each list-item in a block list is parsed as a new block-level context and can contain any number of block-level elements, including paragraphs, headings, and nested lists.
88 |
89 | (1) This list item contains a paragraph
90 | and a compact list.
91 |
92 | 1. foo
93 | 2. bar
94 |
95 | (2) This list item contains two paragraphs.
96 | This is the first.
97 |
98 | And this is the second.
99 |
100 | A list-item consists of its opening line plus all subsequent blank or indented lines.
101 |
102 |
103 |
104 | ### Definition Lists
105 |
106 | Syntext supports definition lists with terms enclosed in double braces:
107 |
108 | [[ Term 1 ]]
109 |
110 | This is the definition of the first term.
111 |
112 | [[ Term 2 ]]
113 |
114 | This is the definition of the second term.
115 |
116 | A term's definition consists of all subsequent blank or indented lines and can contain any number of block-level elements.
117 |
118 |
119 |
120 | ### Code Blocks
121 |
122 | A block of text indented by one tab or four spaces is treated as a code block and wrapped in `
` tags in the HTML output. The code block can contain blank lines and is ended by the first non-indented line.
123 |
124 | This paragraph is followed by a code block.
125 |
126 |
Hello world!
127 |
128 | HTML in code blocks is automatically escaped. To specify the language, use an explicit [code tag](@root/tagged-blocks//#code).
129 |
130 |
131 |
132 |
--------------------------------------------------------------------------------
/docs/src/changelog.stx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Changelog
3 | meta_title: Syntext — Changelog
4 | ---
5 |
6 | ### 3.1.0
7 |
8 | * Add support for a `raw` argument to all shorthand HTML tags.
9 |
10 |
11 |
12 | ### 3.0.0
13 |
14 | * This release removes support for backslashed character escapes. This feature was overly aggressive and could lead to unexpected and unintuitive changes in content. Use ``verbatim`` quotes or HTML instead to stop Syntext parsing characters as formatting instructions --- Syntext ignores inline HTML tags and all content inside block-level HTML. Consider using HTML escape codes for particularly problematic characters, e.g. `|` for the `|` symbol inside tables.
15 |
16 | * Add support for a `::: raw` tag that passes its content through without any further processing.
17 |
18 |
19 |
20 | ### 2.10.0
21 |
22 | * A missing block-level closing HTML tag now raises a `SyntextError` exception.
23 |
24 |
25 |
26 | ### 2.9.0
27 |
28 | * Add support for boxed headings.
29 |
30 |
31 |
32 | ### 2.8.0
33 |
34 | * The parser for recognising and ignoring block-level HTML in the input has been upgraded.
35 |
36 |
37 |
38 | ### 2.7.0
39 |
40 | * Inline bold, italic, code, and verbatim delimiters can now be split over multiple lines.
41 |
42 |
43 |
44 | ### 2.6.0
45 |
46 | * Add support for linked images, image alt text, and image captions.
47 |
48 | * Add documentation for inline footnote references.
49 |
50 |
51 |
52 | ### 2.5.0
53 |
54 | * Add support for blockquote captions.
55 |
56 | * Add `::: link`, `::: div`, and `::: span` tags.
57 |
58 | * Remove deprecated support for inline link and image titles.
59 |
60 | * Deprecate support for reference link titles.
61 |
62 | * Add `::$` as an alias for `::: end`.
63 |
64 | * Add `:$` as an alias for `:end`.
65 |
66 | * Make the tab translation size customizable.
67 |
68 |
69 |
70 | ### 2.4.0
71 |
72 | * Prune the list of escapable characters.
73 |
74 | * Add support for inline verbatim environments.
75 |
76 | * Fix bug recognising raw block-level HTML with attributes.
77 |
78 | * Fix bold/italic bug for consecutive single character blocks.
79 |
80 |
81 |
82 | ### 2.3.0
83 |
84 | * Add support for inline footnote references.
85 |
86 | * Add `infobox` tag.
87 |
88 |
89 |
90 | ### 2.2.0
91 |
92 | * Revised markup for footnotes and footnote references.
93 |
94 | * Add wrapper divs to definition list items.
95 |
96 |
97 |
98 | ### 2.1.0
99 |
100 | * Use `` and `` tags for bold and italic text instead of `` and ``.
101 |
102 |
103 |
104 | ### 2.0.0
105 |
106 | * Full revision incorporating multiple breaking changes. See the V2 documentation for details.
107 |
108 |
109 |
110 | ### 1.4.0
111 |
112 | * Rename the package to Syntext.
113 |
114 |
115 |
116 | ### 1.2.0
117 |
118 | * New custom syntax for definition lists. Support for the older syntax is deprecated.
119 |
120 |
121 |
122 | ### 1.1.0
123 |
124 | * Code blocks and backticks now escape quotes.
125 |
126 |
127 |
128 | ### 1.0.0
129 |
130 | * First stable release.
131 |
--------------------------------------------------------------------------------
/docs/src/index.stx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Home
3 | meta title: Syntext — a markdownish markup language
4 | ---
5 |
6 | Syntext is a lightweight, markdownish markup language for generating HTML. It's implemented in Python and can be used as both a command line utility and a Python library.
7 |
8 | When used on the command line Syntext reads from `stdin` and prints to `stdout`:
9 |
10 | $ syntext < input.txt > output.html
11 |
12 | To use Syntext as a Python library call its `render()` function with a string of input:
13 |
14 | ::: code python
15 |
16 | >>> import syntext
17 | >>> html = syntext.render(text)
18 |
19 |
20 |
21 | ### Overview
22 |
23 | Syntext shares much of its basic syntax with Markdown:
24 |
25 | This paragraph contains *italic* and **bold** text.
26 | It also contains a `code sample` in backticks.
27 |
28 | This paragraph contains a [link](http://example.com).
29 |
30 | Syntext differs from Markdown in supporting an indentation-based shorthand syntax for generating arbitrary HTML:
31 |
32 | :div .outer
33 | :div .inner
34 | This is a paragraph.
35 |
36 | Syntext also includes out-of-the-box support for tables, tables-of-contents, definition lists, syntax highlighting, and footnotes.
37 |
38 |
39 |
40 | ### Installation
41 |
42 | Install directly from the Python Package Index using `pip`:
43 |
44 | $ pip install syntext
45 |
46 | Syntext requires Python 3.6 or later.
47 |
48 |
49 |
50 | ### Command Line Interface
51 |
52 | Use the `syntext --help` flag to view the utility's command line help:
53 |
54 | Usage: syntext [FLAGS]
55 |
56 | Renders input text in Syntext format into HTML. Reads
57 | from stdin and prints to stdout.
58 |
59 | Example:
60 |
61 | $ syntext < input.txt > output.html
62 |
63 | Flags:
64 | -d, --debug Run in debug mode.
65 | -h, --help Print the application's help text.
66 | -p, --pygmentize Add syntax highlighting to code.
67 | -v, --version Print the application's version.
68 |
69 | Syntext can use the [Pygments][] package to add syntax highlighting to code blocks; this feature can be enabled via the `--pygmentize` flag. (Pygments is installed automatically when you install Syntext using `pip`).
70 |
71 | Only code blocks with a language attribute will have syntax highlighting applied.
72 |
73 | [pygments]: http://pygments.org/
74 |
75 |
76 |
77 | ### Links
78 |
79 | * [Github Homepage](https://github.com/dmulholl/syntext)
80 | * [Python Package Index](https://pypi.python.org/pypi/syntext/)
81 |
82 |
83 |
84 | ### License
85 |
86 | Zero-Clause BSD (0BSD).
87 |
--------------------------------------------------------------------------------
/docs/src/inline-syntax.stx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Inline Syntax
3 | meta_title: Syntext — Inline Syntax
4 | ---
5 |
6 | ::: insert toc :::
7 |
8 |
9 |
10 | ### Formatting
11 |
12 | Single asterisks indicate italic text, double asterisks indicate bold text, triple asterisks indicate bold italic text:
13 |
14 | This text is *italic*.
15 | This text is **bold**.
16 | This text is ***bold and italic***.
17 |
18 | Backticks are used to enclose code samples:
19 |
20 | This is a `code sample`.
21 |
22 | HTML in code samples is automatically escaped.
23 |
24 |
25 |
26 | ### Links
27 |
28 | Link syntax is borrowed directly from Markdown:
29 |
30 | [link text](http://example.com)
31 |
32 | Syntext also supports Markdown-style reference links:
33 |
34 | [link text][ref]
35 |
36 | Link references can be specified anywhere in the document:
37 |
38 | [ref]: http://example.com
39 |
40 | If the reference text is omitted from the link, the link text will be used instead when searching for link references.
41 |
42 |
43 |
44 | ### Images
45 |
46 | Image syntax is borrowed directly from Markdown:
47 |
48 | 
49 |
50 | Syntext also supports Markdown-style reference images:
51 |
52 | ![alt text][ref]
53 |
54 | Image references can be specified anywhere in the document. Their form is identical to that of link references. If the reference text is omitted from the image, the alt text will be used instead when searching for references.
55 |
56 |
57 |
58 | ### Dashes
59 |
60 | Double hyphens `--` are converted into en-dashes, `–`; triple hyphens `---` are converted into em-dashes, `—`.
61 |
62 |
63 |
64 | ### Superscripts & Subscripts
65 |
66 | Superscripts are indicated using a caret:
67 |
68 | X^{2}
69 |
70 | Subscripts are indicated using an underscore:
71 |
72 | H_{2}O
73 |
74 | Text to be super or subscripted should be wrapped in curly braces.
75 |
76 |
77 |
78 | ### Verbatim Quotes
79 |
80 | Double backticks create a verbatim environment:
81 |
82 | ``verbatim``
83 |
84 | The content of a verbatim environment is passed through in its raw state without any further processing.
85 |
86 | This can be useful for escaping elements that would otherwise be parsed as formatting instructions by Syntext, e.g. inline LaTeX markup:
87 |
88 | ``\( x^{2} + y^{2} = z^{2} \)``
89 |
--------------------------------------------------------------------------------
/docs/src/license.stx:
--------------------------------------------------------------------------------
1 | ---
2 | title: License
3 | meta_title: Syntext — License
4 | ---
5 |
6 | This library is released under the Zero-Clause BSD licence (0BSD):
7 |
8 | Permission to use, copy, modify, and/or distribute this
9 | software for any purpose with or without fee is hereby
10 | granted.
11 |
12 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS
13 | ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
14 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO
15 | EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
16 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
18 | WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
19 | TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
20 | USE OR PERFORMANCE OF THIS SOFTWARE.
21 |
22 | Reference: .
23 |
--------------------------------------------------------------------------------
/docs/src/miscellanea.stx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Miscellanea
3 | meta_title: Syntext — Miscellanea
4 | ---
5 |
6 | ::: insert toc :::
7 |
8 |
9 | ### Tables of Contents
10 |
11 | Syntext can automatically generate a table of contents for a document. This table can be inserted using the `insert` tag:
12 |
13 | ::: insert toc
14 |
15 | The default listing skips H1 headings to avoid including the document title itself. A full listing of all the document's headings can be inserted using the `fulltoc` keyword:
16 |
17 | ::: insert fulltoc
18 |
19 | The table of contents is generated as an unordered list of links.
20 |
21 |
22 |
23 | ### Footnotes
24 |
25 | Syntext includes built-in support for footnotes:
26 |
27 | This sentence ends with a footnote reference.[^1]
28 |
29 | Footnote references can omit their index to take advantage of automatic numbering:
30 |
31 | This sentence ends with an automatically-numbered
32 | reference.[^]
33 |
34 | Footnotes themselves can be specified anywhere in the document using the `footnote` tag:
35 |
36 | ::: footnote 1
37 | This is a footnote. It can contain any kind of
38 | block-level markup.
39 |
40 | Footnotes can also omit their index to take advantage of automatic numbering:
41 |
42 | ::: footnote
43 | This is an automatically-numbered footnote.
44 |
45 | Note that footnote indices do not have to be numeric --- any sequence of non-whitespace characters can be used.
46 |
47 | You can specify the insertion point for your footnotes using the `insert` tag:
48 |
49 | ::: insert footnotes
50 |
51 | Footnote references are wrapped in the following markup:
52 |
53 | ::: code html
54 |
55 |
56 | 1
57 |
58 |
59 | Footnotes are output as items in a description list:
60 |
61 | ::: code html
62 |
63 |
70 |
71 | Each footnote index is backlinked to its reference in the document.
72 |
73 |
74 |
75 | ### Alternate Footnote Styles
76 |
77 | Syntext supports two alternate styles for footnote references: superscripted[^1] and inline [fn:2].
78 |
79 | Superscripted references look like this `[^1]` while inline references look like this `[fn:2]`. The only difference in markup is that superscripted references are wrapped in a `` tag while inline references are wrapped in a ``.
80 |
81 | ::: footnote 1
82 | This is a sample footnote. Footnotes can contain any kind of block-level markup including multiple paragraphs.
83 |
84 | ::: footnote 2
85 | And this is another sample footnote.
86 |
87 |
88 |
89 | ### Tab Translation Size
90 |
91 | Tab characters in input text are translated into spaces --- by default, 4 spaces per tab. This value can be customized via the `tabsize` argument:
92 |
93 | ::: code python
94 |
95 | html = syntext.render(text, tabsize=8)
96 |
97 | This argument is also available on the command line:
98 |
99 | $ syntext --tabsize 8 < input.txt > output.html
100 |
101 |
102 |
103 | ### Notes
104 |
105 | ::: insert footnotes
106 |
--------------------------------------------------------------------------------
/docs/src/raw-html.stx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Raw HTML
3 | meta_title: Syntext — Raw HTML
4 | ---
5 |
6 | ### Block HTML
7 |
8 | Syntext can recognise and ignore block-level HTML in its input so you can freely mix and match Syntext markup and raw HTML.
9 |
10 | :div .outer
11 | This is a paragraph.
12 |
13 |
14 |
This is raw HTML.
15 |
16 |
17 | This is another paragraph.
18 |
19 | Syntext treats the content of block-level HTML tags as raw text and passes it through without any further processing.
20 |
21 |
22 | ### Inline HTML
23 |
24 | Syntext ignores inline HTML tags so it's fine to mix and match inline HTML and Syntext markup.
25 |
26 | The HTML syntax characters `<`, `>`, and `&` are automatically escaped unless they form part of a HTML tag or character entity.
27 |
--------------------------------------------------------------------------------
/docs/src/shorthand-html.stx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Shorthand HTML
3 | meta_title: Syntext — Shorthand HTML
4 | ---
5 |
6 | ::: insert toc
7 | ::: hr
8 |
9 | Syntext supports an indentation-based shorthand syntax for generating arbitrary HTML:
10 |
11 | :tag [arguments] [.class] [#id] [&attr] [attr="value"]
12 | block content
13 | ...
14 |
15 | A shorthand block opens with a header line containing a colon, a HTML tag, and, optionally, a set of attributes. The block's content consists of all subsequent blank or indented lines following the header.
16 |
17 | The block's content can be indented by any number of spaces; the common indent is stripped and the first non-indented line ends the block. Leading and trailing blank lines are also trimmed from the content.
18 |
19 | To take a simple example, the markup below:
20 |
21 | :div
22 | This is a paragraph.
23 |
24 | generates the following HTML:
25 |
26 |
27 |
This is a paragraph.
28 |
29 |
30 |
31 |
32 | ### Header Meta
33 |
34 | A block header can contain a single ID, any number of classes, and any number of named attributes, e.g:
35 |
36 | :div .foo .bar #baz
37 | This is a paragraph.
38 |
39 | generates the following HTML:
40 |
41 |
42 |
This is a paragraph.
43 |
44 |
45 | Boolean attributes can be specified using an `&` symbol, e.g. `&checked`. Attributes with values can be specified without quotes as long as the value does not contain spaces, e.g.
46 |
47 | :div attr1=foo attr2="bar baz"
48 |
49 |
50 |
51 | ### Nesting
52 |
53 | Blocks can be nested to any depth, e.g:
54 |
55 | :div .outer
56 | :div .inner
57 | This is a paragraph.
58 |
59 | will generate the following HTML:
60 |
61 |
62 |
63 |
This is a paragraph.
64 |
65 |
66 |
67 |
68 |
69 | ### Linebreak Mode
70 |
71 | A `nl2lb` argument can be added to any block to turn on newline-to-linebreak mode for all nested content:
72 |
73 | :div nl2lb
74 | Linebreaks in this paragraph
75 | will be preserved.
76 |
77 | This argument has an alias, `nl2br`, which is exactly equivalent.
78 |
79 |
80 |
81 | ### Raw Mode
82 |
83 | You can add a `raw` argument to any block. This instructs Syntext to preserve the block's content as-is without any further processing.
84 |
85 | :div raw
86 | This content will be preserved in its raw state.
87 |
--------------------------------------------------------------------------------
/docs/src/tagged-blocks.stx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Tagged Blocks
3 | meta_title: Syntext — Tagged Blocks
4 | ---
5 |
6 | Syntext supports an indentation-based tagged-block syntax for custom elements:
7 |
8 | ::: tag [arguments] [.class] [#id] [&attr] [attr="value"]
9 | block content
10 | ...
11 |
12 | A block header can contain a single ID, any number of classes, and any number of named attributes. Block syntax also allows for one or more arguments to be supplied (how these are interpreted depends on the tag).
13 |
14 | Boolean attributes can be specified using an `&` symbol, e.g. `&checked`. Attributes with values can be specified without quotes as long as the value does not contain spaces.
15 |
16 | A block's tag determines how its content is processed. In general blocks can be nested to any depth and can contain any block-level content.
17 |
18 | Although a block's content is determined solely by its indentation an optional end marker can be added to support syntax highlighting in editors which struggle with indentation-based syntax:
19 |
20 | ::: tag
21 | block content
22 | ...
23 | ::: end
24 |
25 | Trailing colons in the header line are optional:
26 |
27 | ::: hr :::
28 |
29 | is equivalent to:
30 |
31 | ::: hr
32 |
33 | A `nl2lb` / `nl2br` argument can be added to any block to turn on newline-to-linebreak mode for all nested content.
34 |
35 |
36 |
37 | ## Tag Reference
38 |
39 |
40 | ### • `code`
41 |
42 | ::: code python
43 | def hello():
44 | print('hello world')
45 |
46 | Wraps a code sample in `
` tags. Automatically escapes the HTML syntax characters `<`, `>`, and `&`.
47 |
48 | This tag accepts an optional argument specifying the language of the code. If a language is specified then a `lang-` class and a `data-lang=""` attribute are added to the `
` element.
49 |
50 | If a language is specified and the [Pygments](http://pygments.org) package is installed then syntax highlighting can be applied to the code sample. This feature is turned off by default but can be enabled by supplying a `pygmentize=True` keyword argument to the `render()` function:
51 |
52 | ::: code python
53 | >>> html = syntext.render(text, pygmentize=True)
54 |
55 |
56 |
57 | ### • `comment`
58 |
59 | ::: comment
60 | This is a comment.
61 |
62 | Creates a HTML comment. The block's content is not processed any further but is included in the output in its raw state.
63 |
64 |
65 |
66 | ### • `hr`
67 |
68 | ::: hr :::
69 |
70 | Inserts a horizontal rule. Any number of dashes can be used in place of the `hr` tag, e.g.
71 |
72 | ::: ------ :::
73 |
74 |
75 |
76 | ### • `image`, `!image`
77 |
78 | ::: image http://example.com/image.jpg
79 | [Optional alt text.]
80 | Optional image caption.
81 |
82 | Inserts an `` element. The first argument is used as the source url.
83 |
84 | * If the first line of the block's content is wrapped in square brackets it will be treated as the image's alt text.
85 | * If a caption is provided, it will be wrapped in a `` element and the image and caption together will be wrapped in a `
` element.
86 | * If the `!image` tag is used, the image will be wrapped in a link to itself.
87 |
88 | Classes, attributes, etc. are attached to the outermost element.
89 |
90 |
91 |
92 | ### • `infobox`
93 |
94 | ::: infobox .warning
95 | Careful now!
96 |
97 | Creates an info box --- a `
` element with the class `infobox` which can be styled appropriately using CSS.
98 |
99 |
100 |
101 | ### • `link`
102 |
103 | ::: link http://example.com/
104 | Link text or any other inline content.
105 |
106 | Inserts a link. The first argument is used as the url.
107 |
108 |
109 |
110 | ### • `quote`
111 |
112 | ::: quote "Oscar Wilde"
113 | Work is the curse of the drinking classes.
114 |
115 | Inserts a `
` element. If a caption argument is provided it will follow the blockquote element wrapped in a `
` element with the class `.blockquote-caption`.
116 |
117 |
118 |
119 | ### • `raw`
120 |
121 | ::: raw
122 | This content will be passed through in its raw state.
123 |
124 | Content inside a `raw` tag will be passed through in its raw state without any further processing.
125 |
126 |
127 |
128 | ### • `table`
129 |
130 | Indicates that the block contains a table:
131 |
132 | ::: table
133 |
134 | foo | bar | baz
135 | ---------------
136 | aaa | bbb | ccc
137 | ddd | eee | fff
138 | ---------------
139 | foo | bar | baz
140 |
141 | Header and footer lines are optional. All three tables below are valid:
142 |
143 | foo | bar | baz
144 | ---------------
145 | aaa | bbb | ccc aaa | bbb | ccc aaa | bbb | ccc
146 | ddd | eee | fff ddd | eee | fff ddd | eee | fff
147 | ---------------
148 | foo | bar | baz
149 |
150 | Note that all the tables above can be 'boxed' with decorative outer lines of pipes and dashes, e.g.:
151 |
152 | --------------- -------------------
153 | foo | bar | baz | foo | bar | baz |
154 | --------------- -------------------
155 | aaa | bbb | ccc | aaa | bbb | ccc |
156 | ddd | eee | fff | ddd | eee | fff |
157 | --------------- -------------------
158 |
159 | Column alignment can be specified using colons as below:
160 |
161 | default | left | center | right
162 | ------- | :--- | :----: | ----:
163 | aaaaa | bb | cccc | ddd
164 | eeeee | ff | gggg | hhh
165 |
166 | Cells in the left column above receive the class `left`, cells in the center column receive the class `center`, and cells in the right column receive the class `right`.
167 |
168 | Attributes specified in the block header will be applied to the `
` element.
169 |
170 | Note that you can't use the `|` symbol inside table literals --- use the HTML escape code `|` instead.
171 |
--------------------------------------------------------------------------------
/license.txt:
--------------------------------------------------------------------------------
1 | Permission to use, copy, modify, and/or distribute this software for any purpose
2 | with or without fee is hereby granted.
3 |
4 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
5 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
6 | FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
7 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
8 | OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
9 | TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
10 | THIS SOFTWARE.
11 |
--------------------------------------------------------------------------------
/makefile:
--------------------------------------------------------------------------------
1 | help:
2 | @cat ./makefile
3 |
4 | test:
5 | @./test_syntext.py
6 |
7 | build:
8 | ./setup.py sdist bdist_wheel
9 |
10 | publish:
11 | ./setup.py sdist bdist_wheel
12 | twine upload dist/*
13 |
14 | clean:
15 | rm -rf ./build
16 | rm -rf ./dist
17 | rm -rf ./*.egg-info
18 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # Syntext
2 |
3 | Syntext is a lightweight, Markdownish markup language for generating HTML. It shares many basic features with Markdown but adds an extensible, indentation-based syntax for generating arbitrary HTML:
4 |
5 | :div .outer
6 | :div .inner
7 | This is a paragraph.
8 |
9 | See the [documentation](http://www.dmulholl.com/docs/syntext/master/) for details.
10 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | """
3 | Syntext is a lightweight, markdownish markup language for generating HTML from
4 | plain text. This package can be used as both a command line tool and an importable library.
5 |
6 | On the command line::
7 |
8 | $ syntext < input.txt > output.html
9 |
10 | As a library::
11 |
12 | >>> import syntext
13 | >>> html = syntext.render(text)
14 |
15 | See the `package documentation `_ or the
16 | project's `Github homepage `_ for
17 | further details.
18 |
19 | """
20 |
21 | import os
22 | import re
23 | import io
24 | import setuptools
25 |
26 |
27 | filepath = os.path.join(os.path.dirname(__file__), 'syntext', '__init__.py')
28 | with io.open(filepath, encoding='utf-8') as metafile:
29 | regex = r'''^__([a-z]+)__ = ["'](.*)["']'''
30 | meta = dict(re.findall(regex, metafile.read(), flags=re.MULTILINE))
31 |
32 |
33 | setuptools.setup(
34 | name = 'syntext',
35 | version = meta['version'],
36 | packages = setuptools.find_packages(),
37 | entry_points = {
38 | 'console_scripts': [
39 | 'syntext = syntext:main',
40 | ],
41 | },
42 | install_requires = [
43 | 'pygments',
44 | ],
45 | author = 'Darren Mulholland',
46 | url = 'http://www.dmulholl.com/docs/syntext/master/',
47 | license = 'Public Domain',
48 | description = ('A markdownish markup language.'),
49 | long_description = __doc__,
50 | classifiers = [
51 | 'Programming Language :: Python :: 3',
52 | 'Operating System :: OS Independent',
53 | 'License :: Public Domain',
54 | 'Topic :: Text Processing :: Markup :: HTML',
55 | ],
56 | )
57 |
--------------------------------------------------------------------------------
/syntext/__init__.py:
--------------------------------------------------------------------------------
1 | # ---------------------------------------------------------
2 | # Syntext: a lightweight, markdownish markup language.
3 | # ---------------------------------------------------------
4 |
5 | __version__ = "3.1.2"
6 |
7 | from .interface import render
8 | from .interface import main
9 | from .utils import SyntextError
10 |
11 | from . import tags
12 | from . import nodes
13 | from . import parsers
14 | from . import utils
15 | from . import shorthand
16 |
--------------------------------------------------------------------------------
/syntext/__main__.py:
--------------------------------------------------------------------------------
1 | # ---------------------------------------------------------
2 | # This module makes the package directly executable. To
3 | # run a syntext package located on Python's import search
4 | # path:
5 | #
6 | # $ python -m syntext
7 | #
8 | # To run an arbitrary syntext package:
9 | #
10 | # $ python /path/to/syntext/package
11 | #
12 | # ---------------------------------------------------------
13 |
14 | import os
15 | import sys
16 |
17 |
18 | # Python doesn't automatically add the package's parent directory to the
19 | # module search path so we need to do so manually before we can import
20 | # syntext.
21 | sys.path.insert(0, os.path.dirname(os.path.dirname(__file__)))
22 |
23 |
24 | import syntext
25 | syntext.main()
26 |
--------------------------------------------------------------------------------
/syntext/inline.py:
--------------------------------------------------------------------------------
1 | # ------------------------------------------------------------------------------
2 | # Functions for parsing and rendering inline markup.
3 | # ------------------------------------------------------------------------------
4 |
5 | import html
6 | import hashlib
7 | import re
8 |
9 |
10 | # ------------------------------------------------------------------------------
11 | # Regular expressions for identifying inline markup.
12 | # ------------------------------------------------------------------------------
13 |
14 | # *x*
15 | re_italic_sc = re.compile(r"\*(\S)\*")
16 |
17 | # *foo bar*
18 | re_italic_mc = re.compile(r"\*(\S.*?\S)\*", re.DOTALL)
19 |
20 | # **x**
21 | re_bold_sc = re.compile(r"\*{2}(\S)\*{2}")
22 |
23 | # **foo bar**
24 | re_bold_mc = re.compile(r"\*{2}(\S.*?\S)\*{2}", re.DOTALL)
25 |
26 | # ***x***
27 | re_bolditalic_sc = re.compile(r"\*{3}(\S)\*{3}")
28 |
29 | # ***foo bar***
30 | re_bolditalic_mc = re.compile(r"\*{3}(\S.*?\S)\*{3}", re.DOTALL)
31 |
32 | # `foo bar`
33 | re_backticks = re.compile(r"`(.+?)`", re.DOTALL)
34 |
35 | # [link text](http://example.com)
36 | re_link = re.compile(r"\[([^\]]+)\]\(([^\)]+)\)")
37 |
38 | # [link text][ref]
39 | re_ref_link = re.compile(r"\[([^\]]+)\]\[([^\]]*)\]")
40 |
41 | # 
42 | re_img = re.compile(r"!\[([^\]]*)\]\(([^\)]+)\)")
43 |
44 | # ![alt text][ref]
45 | re_ref_img = re.compile(r"!\[([^\]]*)\]\[([^\]]*)\]")
46 |
47 | # [^ref] or [^]
48 | re_footnote_super = re.compile(r"\[\^([^\]]*?)\]")
49 |
50 | # [fn:ref] or [fn]
51 | re_footnote_span = re.compile(r"\[fn:?([^\]]*?)\]")
52 |
53 | # & '
54 | re_entity = re.compile(r"&[#a-zA-Z0-9]+;")
55 |
56 | # html tags: , , , etc.
57 | re_html_tag = re.compile(r"<([a-zA-Z/][^>]*?|!--.*?--)>")
58 |
59 | #
60 | re_bracketed_url = re.compile(r"<((?:https?|ftp)://[^>]+)>")
61 |
62 | # http://example.com
63 | re_bare_url = re.compile(r"""
64 | (^|\s)
65 | (https?|ftp)
66 | (://[-A-Z0-9+&@#/%?=~_|\[\]\(\)!:,\.;]*[-A-Z0-9+&@#/%=~_|\[\]])
67 | ($|\W)
68 | """, re.VERBOSE | re.MULTILINE | re.IGNORECASE)
69 |
70 | # n-dash
71 | re_ndash = re.compile(r"((?<=\s)|\b|^)--(?=[ ]|\b|$)", re.MULTILINE)
72 |
73 | # m-dash
74 | re_mdash = re.compile(r"((?<=\s)|\b|^)---(?=[ ]|\b|$)", re.MULTILINE)
75 |
76 | # x^{2}
77 | re_superscript = re.compile(r"\^\{(.+?)\}")
78 |
79 | # H_{2}O
80 | re_subscript = re.compile(r"_\{(.+?)\}")
81 |
82 | # ``foo bar``
83 | re_verbatim = re.compile(r"``(.+?)``", re.DOTALL)
84 |
85 |
86 | # ------------------------------------------------------------------------------
87 | # Renderers.
88 | # ------------------------------------------------------------------------------
89 |
90 |
91 | # Entry point.
92 | def render(text, meta):
93 | hashes = {}
94 |
95 | text = render_verbatim(text, hashes)
96 | text = render_backticks(text, hashes)
97 | text = render_bracketed_urls(text, hashes)
98 | text = render_inline_html(text, hashes)
99 | text = render_html_entities(text, hashes)
100 | text = render_dashes(text, hashes)
101 |
102 | text = html.escape(text, False)
103 |
104 | text = render_bolditalic(text)
105 | text = render_bold(text)
106 | text = render_italic(text)
107 | text = render_footnotes(text, meta)
108 | text = render_images(text)
109 | text = render_ref_images(text, meta)
110 | text = render_links(text)
111 | text = render_ref_links(text, meta)
112 | text = render_superscripts(text)
113 | text = render_subscripts(text)
114 |
115 | if 'nl2br' in meta.get('context', []):
116 | text = text.replace('\n', ' \n')
117 |
118 | for key, value in hashes.items():
119 | text = text.replace(key, value)
120 |
121 | return text
122 |
123 |
124 | # Hashes a string, stores it as a {digest: string} pair in 'hashes', and
125 | # returns the digest.
126 | def hashstr(text, hashes):
127 | digest = hashlib.sha1(text.encode()).hexdigest()
128 | hashes[digest] = text
129 | return digest
130 |
131 |
132 | def render_backticks(text, hashes):
133 | def callback(match):
134 | content = html.escape(match.group(1))
135 | return hashstr('%s' % content, hashes)
136 | return re_backticks.sub(callback, text)
137 |
138 |
139 | def render_bracketed_urls(text, hashes):
140 | def callback(match):
141 | url = '%s' % (match.group(1), match.group(1))
142 | return hashstr(url, hashes)
143 | return re_bracketed_url.sub(callback, text)
144 |
145 |
146 | def render_inline_html(text, hashes):
147 | return re_html_tag.sub(lambda match: hashstr(match.group(), hashes), text)
148 |
149 |
150 | def render_verbatim(text, hashes):
151 | return re_verbatim.sub(lambda match: hashstr(match.group(1), hashes), text)
152 |
153 |
154 | def render_html_entities(text, hashes):
155 | return re_entity.sub(lambda match: hashstr(match.group(), hashes), text)
156 |
157 |
158 | def render_dashes(text, hashes):
159 | text = re_ndash.sub(hashstr("–", hashes), text)
160 | text = re_mdash.sub(hashstr("—", hashes), text)
161 | return text
162 |
163 |
164 | def render_bold(text):
165 | text = re_bold_sc.sub(r"\1", text)
166 | text = re_bold_mc.sub(r"\1", text)
167 | return text
168 |
169 |
170 | def render_italic(text):
171 | text = re_italic_sc.sub(r"\1", text)
172 | text = re_italic_mc.sub(r"\1", text)
173 | return text
174 |
175 |
176 | def render_bolditalic(text):
177 | text = re_bolditalic_sc.sub(r"\1", text)
178 | text = re_bolditalic_mc.sub(r"\1", text)
179 | return text
180 |
181 |
182 | def render_superscripts(text):
183 | return re_superscript.sub(r"\1", text)
184 |
185 |
186 | def render_subscripts(text):
187 | return re_subscript.sub(r"\1", text)
188 |
189 |
190 | def render_images(text):
191 | def callback(match):
192 | alt = html.escape(match.group(1))
193 | url = match.group(2)
194 | return f''
195 | return re_img.sub(callback, text)
196 |
197 |
198 | def render_ref_images(text, meta):
199 | def callback(match):
200 | alt = html.escape(match.group(1))
201 | ref = match.group(2).lower() if match.group(2) else alt.lower()
202 | url, title = meta.get('linkrefs', {}).get(ref, ('', ''))
203 | if title:
204 | title = html.escape(title)
205 | return '' % (alt, url, title)
206 | else:
207 | return '' % (alt, url)
208 | return re_ref_img.sub(callback, text)
209 |
210 |
211 | def render_links(text):
212 | def callback(match):
213 | text = match.group(1)
214 | url = match.group(2)
215 | return f'{text}'
216 | return re_link.sub(callback, text)
217 |
218 |
219 | def render_ref_links(text, meta):
220 | def callback(match):
221 | text = match.group(1)
222 | ref = match.group(2).lower() if match.group(2) else text.lower()
223 | url, title = meta.get('linkrefs', {}).get(ref, ('', ''))
224 | if title:
225 | title = html.escape(title)
226 | return '%s' % (url, title, text)
227 | else:
228 | return '%s' % (url, text)
229 | return re_ref_link.sub(callback, text)
230 |
231 |
232 | def render_footnotes(text, meta):
233 | def callback(match):
234 | if match.group(1):
235 | ref = match.group(1)
236 | else:
237 | ref = meta.setdefault('footnote-ref-index', 1)
238 | meta['footnote-ref-index'] += 1
239 | link = f'{ref}'
240 | return f'<{tag} class="footnote" id="fnref:{ref}">{link}{tag}>'
241 |
242 | tag = "sup"
243 | text = re_footnote_super.sub(callback, text)
244 | tag = "span"
245 | text = re_footnote_span.sub(callback, text)
246 | return text
247 |
--------------------------------------------------------------------------------
/syntext/interface.py:
--------------------------------------------------------------------------------
1 | # ------------------------------------------------------------------------------
2 | # This module defines the package's API.
3 | # ------------------------------------------------------------------------------
4 |
5 | import argparse
6 | import sys
7 | import os
8 | import pprint
9 | import syntext
10 |
11 | from . import utils
12 | from . import parsers
13 | from . import nodes
14 | from . import toc
15 |
16 |
17 | # ------------------------------------------------------------------------------
18 | # Internal interface.
19 | # ------------------------------------------------------------------------------
20 |
21 |
22 | def parse(text, meta):
23 | expanded_text = text.expandtabs(meta.get('tabsize', 4))
24 | line_stream = utils.LineStream(expanded_text)
25 | root = nodes.Node()
26 | root.children = parsers.BlockParser().parse(line_stream, meta)
27 | tocbuilder = toc.TOCBuilder(root)
28 | meta['toc'] = tocbuilder.toc()
29 | meta['fulltoc'] = tocbuilder.fulltoc()
30 | html = root.render(meta)
31 | return html.strip(), root, meta
32 |
33 |
34 | # ------------------------------------------------------------------------------
35 | # Library interface.
36 | # ------------------------------------------------------------------------------
37 |
38 |
39 | def render(text, **meta):
40 | html, _, _ = parse(text, meta)
41 | return html
42 |
43 |
44 | # ------------------------------------------------------------------------------
45 | # Command line interface.
46 | # ------------------------------------------------------------------------------
47 |
48 |
49 | # Command line helptext.
50 | helptext = """
51 | Usage: %s [FLAGS] [OPTIONS]
52 |
53 | Renders input text in Syntext format into HTML. Reads from stdin and
54 | prints to stdout.
55 |
56 | Example:
57 |
58 | $ syntext < input.txt > output.html
59 |
60 | Options:
61 | -t, --tabsize Set tab translation size (default: 4).
62 |
63 | Flags:
64 | -d, --debug Run in debug mode.
65 | -h, --help Print the application's help text and exit.
66 | -p, --pygmentize Add syntax highlighting to code samples.
67 | -v, --version Print the application's version number and exit.
68 | """ % os.path.basename(sys.argv[0])
69 |
70 |
71 | # Custom argparse action to override the default help text.
72 | class HelpAction(argparse.Action):
73 | def __call__(self, parser, namespace, values, option_string=None):
74 | print(helptext.strip())
75 | sys.exit()
76 |
77 |
78 | # Entry point for the command line utility.
79 | def main():
80 | parser = argparse.ArgumentParser(add_help=False)
81 | parser.add_argument('-v', '--version',
82 | action="version",
83 | version=syntext.__version__,
84 | )
85 | parser.add_argument('-h', '--help',
86 | action = HelpAction,
87 | nargs=0,
88 | )
89 | parser.add_argument('-d', '--debug',
90 | action="store_true",
91 | help="run in debug mode",
92 | )
93 | parser.add_argument('-p', '--pygmentize',
94 | action="store_true",
95 | help="add syntax highlighting to code samples",
96 | )
97 | parser.add_argument('-t', '--tabsize',
98 | type=int,
99 | default=4,
100 | help="set tabsize",
101 | )
102 | args = parser.parse_args()
103 |
104 | text = sys.stdin.read()
105 | meta = {
106 | 'pygmentize': args.pygmentize,
107 | 'tabsize': args.tabsize,
108 | }
109 | html, root, meta = parse(text, meta)
110 |
111 | if args.debug:
112 | print(utils.title('AST'))
113 | print(root)
114 | print(utils.title('META'))
115 | print(pprint.pformat(meta) + '\n')
116 | print(utils.title('HTML'))
117 | print(html)
118 | else:
119 | print(html)
120 |
121 |
--------------------------------------------------------------------------------
/syntext/nodes.py:
--------------------------------------------------------------------------------
1 | # ------------------------------------------------------------------------------
2 | # Node classes for the package's AST.
3 | # ------------------------------------------------------------------------------
4 |
5 | import sys
6 | from . import inline
7 |
8 |
9 | # Input text is parsed into a tree of nodes.
10 | class Node:
11 |
12 | def __init__(self, tag=None, attributes=None, text=None, is_void=False):
13 | self.tag = tag
14 | self.text = text or ''
15 | self.children = []
16 | self.attributes = attributes or {}
17 | self.is_void = is_void
18 |
19 | def __str__(self):
20 | return self.str()
21 |
22 | def str(self, depth=0):
23 | output = ["· " * depth]
24 | output.append('%s' % self.__class__.__name__)
25 | if self.tag:
26 | output.append(' ' + self.opening_tag())
27 | if self.text:
28 | text = repr(self.text)
29 | if len(text) < 40:
30 | output.append(' ' + text)
31 | else:
32 | output.append(' ' + text[:18] + "..." + text[-18:])
33 | output.append('\n')
34 | for child in self.children:
35 | output.append(child.str(depth + 1))
36 | return ''.join(output)
37 |
38 | def render(self, meta):
39 | output = []
40 |
41 | if self.tag:
42 | output.append(self.opening_tag() + '\n')
43 |
44 | if self.children:
45 | output.append(''.join(child.render(meta) for child in self.children))
46 | elif self.text:
47 | output.append(self.text.rstrip() + '\n')
48 |
49 | if self.tag and not self.is_void:
50 | output.append(self.closing_tag() + '\n')
51 |
52 | return ''.join(output)
53 |
54 | def opening_tag(self):
55 | attributes = []
56 | for key, value in sorted(self.attributes.items()):
57 | if value is None:
58 | attributes.append(' %s' % key)
59 | else:
60 | attributes.append(' %s="%s"' % (key, value))
61 | return '<%s%s>' % (self.tag, ''.join(attributes))
62 |
63 | def closing_tag(self):
64 | return '%s>' % self.tag
65 |
66 | def append_child(self, node):
67 | self.children.append(node)
68 | return self
69 |
70 | def add_class(self, newclass):
71 | classes = self.attributes.get('class', '').split()
72 | if newclass not in classes:
73 | classes.append(newclass)
74 | self.attributes['class'] = ' '.join(sorted(classes))
75 |
76 |
77 | # Text nodes contain only text, they have no children. Their content is
78 | # processed for inline markup.
79 | class TextNode(Node):
80 |
81 | def __init__(self, text):
82 | Node.__init__(self)
83 | self.text = text
84 |
85 | def render(self, meta):
86 | return inline.render(self.text, meta) + '\n'
87 |
88 |
89 | # Comment nodes represent HTML comments.
90 | class CommentNode(Node):
91 |
92 | def render(self, meta):
93 | html = []
94 | html.append('\n')
98 | return ''.join(html)
99 |
100 |
101 | # Insert nodes provide a mechanism for inserting generated content,
102 | # e.g. tables of contents or footnotes.
103 | class InsertNode(Node):
104 |
105 | def render(self, meta):
106 | if self.text in meta:
107 | return meta[self.text].render(meta)
108 | else:
109 | sys.stderr.write('Error: missing insert %s.\n' % repr(self.text))
110 | return ''
111 |
112 |
113 | # Turns on newline-to-linebreak mode for all children.
114 | class LinebreakNode(Node):
115 |
116 | def render(self, meta):
117 | context = meta.setdefault('context', [])
118 | context.append('nl2br')
119 | rendered = ''.join(child.render(meta) for child in self.children)
120 | context.pop()
121 | return rendered
122 |
--------------------------------------------------------------------------------
/syntext/shorthand.py:
--------------------------------------------------------------------------------
1 | # ------------------------------------------------------------------------------
2 | # Functions for processing shorthand html tags.
3 | # ------------------------------------------------------------------------------
4 |
5 | import html
6 | import re
7 |
8 | from . import nodes
9 | from . import parsers
10 | from . import utils
11 |
12 |
13 | # Void elements have no content or closing tag.
14 | html_void_tags = set("""
15 | area base br col embed hr img input link meta param source track wbr
16 | """.split())
17 |
18 |
19 | # Leaf elements cannot contain nested block-level content.
20 | html_leaf_tags = set("""
21 | dt h1 h2 h3 h4 h5 h6 p title
22 | a abbr acronyn audio b bdi bdo big button canvas cite code data datalist
23 | del dfn em i iframe ins kbd label map mark meter noscript
24 | object output picture progress q ruby s samp select slot small span
25 | strong sub sup svg template textarea time u tt var video
26 | """.split())
27 |
28 |
29 | # Raw elements contain text which should be included in the output as-is.
30 | html_raw_tags = set("script style".split())
31 |
32 |
33 | # Process a tagged block.
34 | def process(header, line_stream, meta):
35 | match = re.fullmatch(r':([^ ]+)([ ].+)?', header)
36 | tag = match.group(1)
37 | argstring = match.group(2) or ''
38 | pargs, kwargs = utils.ArgParser().parse(argstring)
39 |
40 | if 'raw' in pargs:
41 | return nodes.Node(tag, kwargs, text=str(line_stream))
42 |
43 | if tag in html_void_tags:
44 | node = nodes.Node(tag, kwargs, is_void=True)
45 | elif tag in html_leaf_tags:
46 | node = nodes.Node(tag, kwargs)
47 | node.children = parsers.InlineParser().parse(line_stream, meta)
48 | elif tag in html_raw_tags:
49 | node = nodes.Node(tag, kwargs, text=str(line_stream))
50 | else:
51 | node = nodes.Node(tag, kwargs)
52 | node.children = parsers.BlockParser().parse(line_stream, meta)
53 |
54 | if 'nl2lb' in pargs or 'nl2br' in pargs:
55 | node = nodes.LinebreakNode().append_child(node)
56 |
57 | return node
58 |
--------------------------------------------------------------------------------
/syntext/tags.py:
--------------------------------------------------------------------------------
1 | # ------------------------------------------------------------------------------
2 | # Functions for registering and processing tags.
3 | # ------------------------------------------------------------------------------
4 |
5 | import html
6 | import re
7 |
8 | from . import nodes
9 | from . import parsers
10 | from . import utils
11 |
12 |
13 | # Use the Pygments module for syntax highlighting, if it's available.
14 | try:
15 | import pygments
16 | import pygments.lexers
17 | import pygments.formatters
18 | except ImportError:
19 | pygments = None
20 |
21 |
22 | # Map tags to registered handler functions.
23 | tagmap = {}
24 |
25 |
26 | # Decorator function for registering tag handlers.
27 | def register(*tags):
28 |
29 | def register_tag_handler(func):
30 | for tag in tags:
31 | tagmap[tag] = func
32 | return func
33 |
34 | return register_tag_handler
35 |
36 |
37 | # Process a tag.
38 | def process(tag, pargs, kwargs, line_stream, meta):
39 | if tag in tagmap:
40 | node = tagmap[tag](tag, pargs, kwargs, line_stream, meta)
41 | elif tag == 'hr' or re.fullmatch(r'-+', tag):
42 | node = nodes.Node('hr', kwargs, is_void=True)
43 | else:
44 | raise utils.SyntextError("Unrecognized tag '%s'." % tag)
45 |
46 | if 'nl2lb' in pargs or 'nl2br' in pargs:
47 | node = nodes.LinebreakNode().append_child(node)
48 | return node
49 |
50 |
51 | # Handler for the 'div' tag - useful as a test for tags with block-level content.
52 | @register('div')
53 | def div_tag_handler(tag, pargs, kwargs, line_stream, meta):
54 | node = nodes.Node('div', kwargs)
55 | node.children = parsers.BlockParser().parse(line_stream, meta)
56 | return node
57 |
58 |
59 | # Handler for the 'span' tag - useful as a test for tags with inline-level content.
60 | @register('span')
61 | def span_tag_handler(tag, pargs, kwargs, line_stream, meta):
62 | node = nodes.Node('span', kwargs)
63 | node.children = parsers.InlineParser().parse(line_stream, meta)
64 | return node
65 |
66 |
67 | # Handler for the 'link' tag. Uses the first keyword argument as the url.
68 | @register('link')
69 | def link_tag_handler(tag, pargs, kwargs, line_stream, meta):
70 | node = nodes.Node('a', kwargs)
71 | node.children = parsers.InlineParser().parse(line_stream, meta)
72 | if not 'href' in kwargs:
73 | node.attributes['href'] = pargs[0] if pargs else ''
74 | return node
75 |
76 |
77 | # Handler for blockquotes.
78 | @register('quote')
79 | def quote_tag_handler(tag, pargs, kwargs, line_stream, meta):
80 | quote = nodes.Node('blockquote', kwargs)
81 | quote.children = parsers.BlockParser().parse(line_stream, meta)
82 | if pargs:
83 | caption = nodes.Node('p')
84 | caption.add_class('blockquote-caption')
85 | caption.append_child(nodes.TextNode(pargs[0]))
86 | wrapper = nodes.Node()
87 | wrapper.append_child(quote)
88 | wrapper.append_child(caption)
89 | return wrapper
90 | return quote
91 |
92 |
93 | # Handler for infobox tags. Supports alertbox as deprecated alias.
94 | @register('infobox', 'alertbox')
95 | def infobox_tag_handler(tag, pargs, kwargs, line_stream, meta):
96 | node = nodes.Node('div', kwargs)
97 | node.add_class(tag)
98 | node.children = parsers.BlockParser().parse(line_stream, meta)
99 | return node
100 |
101 |
102 | # Handler for image tags. Uses the first keyword argument as the src.
103 | @register('image', '!image')
104 | def image_tag_handler(tag, pargs, kwargs, line_stream, meta):
105 | image = nodes.Node('img', is_void=True)
106 | image.attributes['src'] = pargs[0] if pargs else ''
107 | outernode = image
108 |
109 | if line_stream.has_next() and line_stream.peek().startswith('[') and line_stream.peek().endswith(']'):
110 | image.attributes['alt'] = html.escape(line_stream.next()[1:-1])
111 |
112 | if tag == '!image':
113 | link = nodes.Node('a')
114 | link.attributes['href'] = pargs[0] if pargs else ''
115 | link.append_child(image)
116 | outernode = link
117 |
118 | if line_stream.has_next():
119 | figcaption = nodes.Node('figcaption')
120 | figcaption.append_child(nodes.TextNode(str(line_stream).strip()))
121 | figure = nodes.Node('figure')
122 | figure.append_child(outernode)
123 | figure.append_child(figcaption)
124 | outernode = figure
125 |
126 | outernode.attributes.update(kwargs)
127 | return outernode
128 |
129 |
130 | # Handler for the 'comment' tag; creates a HTML comment.
131 | @register('comment')
132 | def comment_tag_handler(tag, pargs, kwargs, line_stream, meta):
133 | return nodes.CommentNode(text=str(line_stream.indent(4)))
134 |
135 |
136 | # Handler for the 'raw' tag.
137 | @register('raw')
138 | def raw_tag_handler(tag, pargs, kwargs, line_stream, meta):
139 | return nodes.Node(text=str(line_stream))
140 |
141 |
142 | # Handler for code samples.
143 | @register('code')
144 | def code_tag_handler(tag, pargs, kwargs, line_stream, meta):
145 | node = nodes.Node('pre', kwargs)
146 | lang = pargs[0] if pargs else None
147 | text = str(line_stream)
148 |
149 | if lang:
150 | node.attributes['data-lang'] = lang
151 | node.add_class('lang-%s' % lang)
152 |
153 | if meta.get('pygmentize') and pygments and lang:
154 | try:
155 | lexer = pygments.lexers.get_lexer_by_name(lang)
156 | except pygments.util.ClassNotFound:
157 | try:
158 | lexer = pygments.lexers.guess_lexer(text)
159 | except pygments.util.ClassNotFound:
160 | lexer = None
161 | if lexer:
162 | node.add_class('pygments')
163 | formatter = pygments.formatters.HtmlFormatter(nowrap=True)
164 | node.text = pygments.highlight(text, lexer, formatter).strip('\n')
165 | return node
166 |
167 | node.text = html.escape(text, quote=False)
168 | return node
169 |
170 |
171 | # Handler for the 'insert' tag. This tag is used to insert generated elements,
172 | # e.g. a table of contents or block of footnotes.
173 | @register('insert')
174 | def insert_tag_handler(tag, pargs, kwargs, line_stream, meta):
175 | node = nodes.InsertNode()
176 | node.text = pargs[0] if pargs else ''
177 | return node
178 |
179 |
180 | # Handler for the 'table' tag.
181 | @register('table')
182 | def table_tag_handler(tag, pargs, kwargs, line_stream, meta):
183 |
184 | # Strip any outer pipes and discard any blank lines.
185 | lines = [line.strip('|') for line in line_stream.lines if line]
186 |
187 | # Check for a line with colons specifying cell alignment.
188 | alignment = []
189 | for line in lines:
190 | match = re.fullmatch(r'[ |-]*[:][ |:-]*', line)
191 | if match:
192 | for cell in [cell.strip() for cell in line.split('|')]:
193 | if cell.startswith(':') and cell.endswith(':'):
194 | alignment.append('center')
195 | elif cell.startswith(':'):
196 | alignment.append('left')
197 | elif cell.endswith(':'):
198 | alignment.append('right')
199 | else:
200 | alignment.append(None)
201 | break
202 |
203 | # If we have a decorative top line, strip it.
204 | if lines and re.fullmatch(r'[ |:-]+', lines[0]):
205 | lines.pop(0)
206 |
207 | # If we have a decorative bottom line, strip it.
208 | if lines and re.fullmatch(r'[ |:-]+', lines[-1]):
209 | lines.pop()
210 |
211 | # Do we have a header?
212 | header = None
213 | if len(lines) > 2 and re.fullmatch(r'[ |:-]+', lines[1]):
214 | header = [cell.strip() for cell in lines[0].split('|')]
215 | lines = lines[2:]
216 |
217 | # Do we have a footer?
218 | footer = None
219 | if len(lines) > 2 and re.fullmatch(r'[ |:-]+', lines[-2]):
220 | footer = [cell.strip() for cell in lines[-1].split('|')]
221 | lines = lines[:-2]
222 |
223 | # Assemble the table body.
224 | body = [[cell.strip() for cell in line.split('|')] for line in lines]
225 |
226 | # Make a row of cells using the specified tag.
227 | def make_row(cells, tag):
228 | tr = nodes.Node('tr')
229 | for index, text in enumerate(cells):
230 | cell = nodes.Node(tag)
231 | cell.append_child(nodes.TextNode(text))
232 | if len(alignment) > index and alignment[index]:
233 | cell.add_class(alignment[index])
234 | tr.append_child(cell)
235 | return tr
236 |
237 | # Assemble the table node.
238 | table = nodes.Node('table', kwargs)
239 |
240 | if header:
241 | thead = nodes.Node('thead')
242 | thead.append_child(make_row(header, 'th'))
243 | table.append_child(thead)
244 |
245 | if footer:
246 | tfoot = nodes.Node('tfoot')
247 | tfoot.append_child(make_row(footer, 'th'))
248 | table.append_child(tfoot)
249 |
250 | tbody = nodes.Node('tbody')
251 | for row in body:
252 | tbody.append_child(make_row(row, 'td'))
253 | table.append_child(tbody)
254 |
255 | return table
256 |
257 |
258 | # Handler for the 'footnote' tag.
259 | @register('footnote')
260 | def footnote_tag_handler(tag, pargs, kwargs, line_stream, meta):
261 |
262 | # Autogenerate a footnote index if the user hasn't specified one.
263 | ref = pargs[0] if pargs else ''
264 | if not ref:
265 | ref = str(meta.setdefault('footnote-index', 1))
266 | meta['footnote-index'] += 1
267 |
268 | # Wrap each footnote in a
23 |
--------------------------------------------------------------------------------
/unittests/blocks/dl.txt:
--------------------------------------------------------------------------------
1 | [[ Term 1 ]]
2 |
3 | This is the definition of the term.
4 |
5 | [[ Term 2 ]]
6 |
7 | This is the definition of the term.
8 |
--------------------------------------------------------------------------------
/unittests/blocks/fancy-h1.html:
--------------------------------------------------------------------------------
1 |
28 | This footnote has two paragraphs.
29 | This is the first.
30 |
31 |
32 | And this is the second.
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/unittests/compound/autofootnotes.txt:
--------------------------------------------------------------------------------
1 | This line contains a footnote.[^]
2 |
3 | This line contains a footnote [fn].
4 |
5 | ::: insert footnotes :::
6 |
7 | ::: footnote 1
8 | This is footnote 1.
9 | ::: end
10 |
11 | ::: footnote 2
12 | This footnote has two paragraphs.
13 | This is the first.
14 |
15 | And this is the second.
16 | ::: end
17 |
--------------------------------------------------------------------------------
/unittests/compound/footnotes.html:
--------------------------------------------------------------------------------
1 |
28 | This footnote has two paragraphs.
29 | This is the first.
30 |
31 |
32 | And this is the second.
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/unittests/compound/footnotes.txt:
--------------------------------------------------------------------------------
1 | This line contains a footnote.[^1]
2 |
3 | This line contains a footnote [fn:2].
4 |
5 | ::: insert footnotes :::
6 |
7 | ::: footnote 1
8 | This is footnote 1.
9 | ::: end
10 |
11 | ::: footnote 2
12 | This footnote has two paragraphs.
13 | This is the first.
14 |
15 | And this is the second.
16 | ::: end
17 |
--------------------------------------------------------------------------------
/unittests/compound/fulltoc.html:
--------------------------------------------------------------------------------
1 |
17 | The case of the reference is not important.
18 |
19 |
20 | The case of the reference is not IMPORTANT.
21 |
22 |
--------------------------------------------------------------------------------
/unittests/compound/links.txt:
--------------------------------------------------------------------------------
1 | This paragraph contains a [link](http://example.com).
2 |
3 | This link has [a title](http://example.com).
4 |
5 | Here's a reference [link][1].
6 |
7 | This is a [reference][] link too.
8 |
9 | This reference [link][with title] has a title.
10 |
11 | The case of the reference is not [important][CAPSREF].
12 |
13 | The case of the reference is not [IMPORTANT][].
14 |
15 | [1]: http://example.com
16 | [reference]: http://example.com
17 | [with title]:
18 | http://example.com
19 | link title
20 | [capsref]: http://example.com
21 | [ImpoRTanT]: http://example.com
22 |
--------------------------------------------------------------------------------
/unittests/compound/toc.html:
--------------------------------------------------------------------------------
1 |