From faf3676fecc06e8b081eda760d213ecbe8411dd5 Mon Sep 17 00:00:00 2001 From: Anton Bachin Date: Sat, 27 Jun 2015 13:56:27 -0500 Subject: [PATCH] Made it easier to generate offline documentation. Documentation can be generated by going to doc/ and running "make". This requires Python. Before this change, the user had to install the mistune library, which is used by the generator. The mistune library is now included in the Better Enums distribution. The generated docs are available at doc/html/index.html. Note that some links won't be local (the GitHub repo, the download link, outgoing links to MSDN, tutorial source in the GitHub repo, and so on). All the pages belonging to the actual docs will be local, however. The online version of the docs can be generated by running "make web". The only difference between the online and offline versions is that the former includes Google Analytics tracking code, and may include social communication buttons, comment section, or other useless things in the future. Also included errata since the last release. Resolves #2. --- LICENSE | 45 ++ doc/DesignDecisionsFAQ.md | 4 +- doc/Makefile | 17 +- doc/better-enums.css | 38 +- doc/demo/102-any-underlying.md | 10 +- doc/docs.py | 10 + doc/image/tweet.png | Bin 0 -> 8250 bytes doc/image/twsupport.png | Bin 0 -> 10206 bytes doc/index.md | 17 +- doc/mistune.py | 1102 ++++++++++++++++++++++++++++++++ doc/template/ga.tmpl | 10 + doc/template/ghfork.tmpl | 1 + doc/template/ghstar.tmpl | 1 + doc/template/ghwatch.tmpl | 1 + doc/template/header.tmpl | 11 +- doc/template/tweet.tmpl | 3 + doc/template/twsupport.tmpl | 3 + doc/transform.py | 2 +- example/1-hello-world.cc | 2 +- example/102-any-underlying.cc | 10 +- example/5-iostreams.cc | 7 +- 21 files changed, 1268 insertions(+), 26 deletions(-) create mode 100644 doc/image/tweet.png create mode 100644 doc/image/twsupport.png create mode 100755 doc/mistune.py create mode 100644 doc/template/ga.tmpl create mode 100644 doc/template/ghfork.tmpl create mode 100644 doc/template/ghstar.tmpl create mode 100644 doc/template/ghwatch.tmpl create mode 100644 doc/template/tweet.tmpl create mode 100644 doc/template/twsupport.tmpl diff --git a/LICENSE b/LICENSE index 5c00ae3..22e5c21 100644 --- a/LICENSE +++ b/LICENSE @@ -1,3 +1,7 @@ +Better Enums is distributed under the terms of the 2-clause BSD license. Its +text is given below. + + Copyright (c) 2012-2015, Anton Bachin All rights reserved. @@ -21,3 +25,44 @@ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + + + +Better Enums uses the mistune library as part of its documentation generator. +Its web address and license are given below. + + +http://mistune.readthedocs.org/en/latest/ + + +Copyright (c) 2014 - 2015, Hsiaoming Yang + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, this +list of conditions and the following disclaimer in the documentation and/or +other materials provided with the distribution. + +* Neither the name of the creator nor the names of its contributors may be used +to endorse or promote products derived from this software without specific prior +written permission. + + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/doc/DesignDecisionsFAQ.md b/doc/DesignDecisionsFAQ.md index 188b423..674b724 100644 --- a/doc/DesignDecisionsFAQ.md +++ b/doc/DesignDecisionsFAQ.md @@ -52,8 +52,6 @@ interesting option, and it has [its own section][traits]. I have tried it, but the verbosity increase is much greater than the benefit of dropping underscores, so I chose not to do it. -%% description = Better Enums design decisions and tradeoffs. - ### Why does Better Enums use a macro at all? Better Enums needs to turn the names of declared constants into strings, and I @@ -248,3 +246,5 @@ generation. [underlying]: ${prefix}demo/NonIntegralUnderlyingTypes.html [traits-branch]: $repo/tree/traits [traits-samples]: $repo/tree/traits/samples + +%% description = Better Enums design decisions and tradeoffs. diff --git a/doc/Makefile b/doc/Makefile index 758c612..d16fcd8 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -1,12 +1,23 @@ SOURCE_MARKDOWN := $(wildcard tutorial/*) $(wildcard demo/*) SOURCE_CXX := $(SOURCE_MARKDOWN:.md=.cc) -.PHONY : all -all : html examples - .PHONY : html html : python docs.py + @echo "See html/index.html" + +.PHONY : publish +publish : prepare + cp -r html ../doc-publish + git commit --amend + git push -f + +.PHONY : prepare +prepare : examples web + +.PHONY : web +web : examples + python docs.py --web .PHONY : examples examples : clean-examples $(SOURCE_CXX) diff --git a/doc/better-enums.css b/doc/better-enums.css index d585746..f9ca9bf 100644 --- a/doc/better-enums.css +++ b/doc/better-enums.css @@ -122,7 +122,6 @@ header { background: linear-gradient(#395E7E, #4A79A0); color: white; padding: 50px 0; - margin-bottom: 50px; } h1 { @@ -226,12 +225,12 @@ footer { a { text-decoration: none; color: white; - background-color: red; + /*background-color: red;*/ } a[href=""] { color: white !important; - background-color: red !important; + /*background-color: red !important;*/ } a:hover { @@ -242,6 +241,10 @@ header a:hover { text-decoration: none; } +.main { + margin-top: 50px; +} + .main a[href], footer a[href] { background-color: #edd; color: #844; @@ -545,3 +548,32 @@ h3.contents { margin-top: 0; padding-bottom: 1em; } + +.buttons-bar { + background-color: #f4f4f4; + padding-top: 0.5em; + padding-bottom: 0.5em; + height: 20px; +} + +.buttons-bar a img { + margin-right: 25px; +} + +.buttons-bar iframe.gh-button { + width: 95px; +} + +.buttons-bar .tweet-share, +.buttons-bar .gh-button { + display: none; +} + +.index .buttons-bar .tweet-share, +.index .buttons-bar .gh-button { + display: initial; +} + +.buttons-bar iframe.gh-watch { + width: 103px; +} diff --git a/doc/demo/102-any-underlying.md b/doc/demo/102-any-underlying.md index 46f8efc..870a74a 100644 --- a/doc/demo/102-any-underlying.md +++ b/doc/demo/102-any-underlying.md @@ -7,7 +7,15 @@ between `T` and an integral type of your choosing. This also works in $cxx98 doesn't have to be `constexpr`. In $cxx98, everything involving `T` will simply be done by Better Enums at run time. -Here's how to do it. +This feature is semi-experimental. I am considering relaxing the requirements on +`T` so that it doesn't have to be literal. I can use a `reinterpret_cast` to +make a mapping automatically. This will make non-integral underlying types +easier to use, but will also prevent usage at compile time, which unfortunately +has structural consequences for the implementation of Better Enums, and +additional semantic consequences for usage, even at run time. + +In the meantime, here's how to have a non-integral underlying type in the +current version. #include #include diff --git a/doc/docs.py b/doc/docs.py index 8f015f0..36c49e3 100755 --- a/doc/docs.py +++ b/doc/docs.py @@ -7,6 +7,8 @@ import string import transform import os import os.path +import sys +import urllib TEMPLATE_DIRECTORY = "template" OUTPUT_DIRECTORY = "html" @@ -83,6 +85,9 @@ def compose_page(relative_path, definitions): definitions["canonical"] = canonical definitions["prefix"] = prefix + definitions["quoted_url"] = urllib.quote(definitions["canonical"], "") + definitions["quoted_title"] = urllib.quote(definitions["title"], "") + if "class" not in definitions: definitions["class"] = "" @@ -202,6 +207,9 @@ def generate_sitemap(): def main(): load_templates() + if not (len(sys.argv) >= 2 and sys.argv[1] == "--web"): + templates["ga"] = "" + remove_output_directory() process_threaded(TUTORIAL_DIRECTORY) @@ -212,6 +220,8 @@ def main(): compose_general_page(page) copy_static_file("better-enums.css") + copy_static_file("image/twsupport.png") + copy_static_file("image/tweet.png") generate_sitemap() diff --git a/doc/image/tweet.png b/doc/image/tweet.png new file mode 100644 index 0000000000000000000000000000000000000000..dbbd61d71534b843c997fb1a7a7887cb3cd14e8f GIT binary patch literal 8250 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 literal 0 HcmV?d00001 diff --git a/doc/image/twsupport.png b/doc/image/twsupport.png new file mode 100644 index 0000000000000000000000000000000000000000..ddc4b1f0651f854089d18df92ff224417f247cd5 GIT binary patch literal 10206 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::_from_integral(3); reflective enum types.

-That means you can easily convert enums to and from strings, -validate them, and loop over them. In $cxx11, you can do it all at +That means you can easily convert enums to and from strings, +validate them, and loop over them. In $cxx11, you can do it all at compile time. It's what built-in enums ought to support. Better Enums simply adds the missing features. And, it is based on the best known techniques, thoroughly tested, fast, portable, and documented exhaustively. -All you have to do to use it is include enum.h. +To use it, just include enum.h. Try it live online in -[Wandbox](http://melpon.org/wandbox/permlink/pdlAAGoxnjqG6FRI), or begin the +[Wandbox](http://melpon.org/wandbox/permlink/wICNzu2LW2vEgqzh), or begin the [tutorial](${prefix}tutorial/HelloWorld.html)!
-### Features +### Highlights
  • @@ -218,9 +218,8 @@ Try it live online in %% title = Clean reflective enums for C++ -%% description = Better Enums is a single header C++ library providing -reflective enums with clean syntax. Better Enums can be converted to and from -strings, be iterated, counted, and used at run time or for template and -constexpr metaprogramming. Free and open source under the BSD license. +%% description = Reflective enums in a single header file, with clean syntax. +The enums can be converted to string, iterated, and counted, at run time or +as part of metaprogramming. Free and open source under the BSD license. %% class = index diff --git a/doc/mistune.py b/doc/mistune.py new file mode 100755 index 0000000..34860d9 --- /dev/null +++ b/doc/mistune.py @@ -0,0 +1,1102 @@ +# coding: utf-8 +""" + mistune + ~~~~~~~ + + The fastest markdown parser in pure Python with renderer feature. + + :copyright: (c) 2014 - 2015 by Hsiaoming Yang. +""" + +import re +import inspect + +__version__ = '0.5.1' +__author__ = 'Hsiaoming Yang ' +__all__ = [ + 'BlockGrammar', 'BlockLexer', + 'InlineGrammar', 'InlineLexer', + 'Renderer', 'Markdown', + 'markdown', 'escape', +] + + +def _pure_pattern(regex): + pattern = regex.pattern + if pattern.startswith('^'): + pattern = pattern[1:] + return pattern + + +_key_pattern = re.compile(r'\s+') + + +def _keyify(key): + return _key_pattern.sub(' ', key.lower()) + + +_escape_pattern = re.compile(r'&(?!#?\w+;)') + + +def escape(text, quote=False, smart_amp=True): + """Replace special characters "&", "<" and ">" to HTML-safe sequences. + + The original cgi.escape will always escape "&", but you can control + this one for a smart escape amp. + + :param quote: if set to True, " and ' will be escaped. + :param smart_amp: if set to False, & will always be escaped. + """ + if smart_amp: + text = _escape_pattern.sub('&', text) + else: + text = text.replace('&', '&') + text = text.replace('<', '<') + text = text.replace('>', '>') + if quote: + text = text.replace('"', '"') + text = text.replace("'", ''') + return text + + +def preprocessing(text, tab=4): + text = re.sub(r'\r\n|\r', '\n', text) + text = text.replace('\t', ' ' * tab) + text = text.replace('\u00a0', ' ') + text = text.replace('\u2424', '\n') + pattern = re.compile(r'^ +$', re.M) + return pattern.sub('', text) + + +_tag = ( + r'(?!(?:' + r'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|' + r'var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|' + r'span|br|wbr|ins|del|img)\b)\w+(?!:/|[^\w\s@]*@)\b' +) + + +class BlockGrammar(object): + """Grammars for block level tokens.""" + + def_links = re.compile( + r'^ *\[([^^\]]+)\]: *' # [key]: + r']+)>?' # or link + r'(?: +["(]([^\n]+)[")])? *(?:\n+|$)' + ) + def_footnotes = re.compile( + r'^\[\^([^\]]+)\]: *(' + r'[^\n]*(?:\n+|$)' # [^key]: + r'(?: {1,}[^\n]*(?:\n+|$))*' + r')' + ) + + newline = re.compile(r'^\n+') + block_code = re.compile(r'^( {4}[^\n]+\n*)+') + fences = re.compile( + r'^ *(`{3,}|~{3,}) *(\S+)? *\n' # ```lang + r'([\s\S]+?)\s*' + r'\1 *(?:\n+|$)' # ``` + ) + hrule = re.compile(r'^ {0,3}[-*_](?: *[-*_]){2,} *(?:\n+|$)') + heading = re.compile(r'^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)') + lheading = re.compile(r'^([^\n]+)\n *(=|-)+ *(?:\n+|$)') + block_quote = re.compile(r'^( *>[^\n]+(\n[^\n]+)*\n*)+') + list_block = re.compile( + r'^( *)([*+-]|\d+\.) [\s\S]+?' + r'(?:' + r'\n+(?=\1?(?:[-*_] *){3,}(?:\n+|$))' # hrule + r'|\n+(?=%s)' # def links + r'|\n+(?=%s)' # def footnotes + r'|\n{2,}' + r'(?! )' + r'(?!\1(?:[*+-]|\d+\.) )\n*' + r'|' + r'\s*$)' % ( + _pure_pattern(def_links), + _pure_pattern(def_footnotes), + ) + ) + list_item = re.compile( + r'^(( *)(?:[*+-]|\d+\.) [^\n]*' + r'(?:\n(?!\2(?:[*+-]|\d+\.) )[^\n]*)*)', + flags=re.M + ) + list_bullet = re.compile(r'^ *(?:[*+-]|\d+\.) +') + paragraph = re.compile( + r'^((?:[^\n]+\n?(?!' + r'%s|%s|%s|%s|%s|%s|%s|%s|%s' + r'))+)\n*' % ( + _pure_pattern(fences).replace(r'\1', r'\2'), + _pure_pattern(list_block).replace(r'\1', r'\3'), + _pure_pattern(hrule), + _pure_pattern(heading), + _pure_pattern(lheading), + _pure_pattern(block_quote), + _pure_pattern(def_links), + _pure_pattern(def_footnotes), + '<' + _tag, + ) + ) + block_html = re.compile( + r'^ *(?:%s|%s|%s) *(?:\n{2,}|\s*$)' % ( + r'', + r'<(%s)[\s\S]+?<\/\1>' % _tag, + r'''<%s(?:"[^"]*"|'[^']*'|[^'">])*?>''' % _tag, + ) + ) + table = re.compile( + r'^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*' + ) + nptable = re.compile( + r'^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*' + ) + text = re.compile(r'^[^\n]+') + + +class BlockLexer(object): + """Block level lexer for block grammars.""" + grammar_class = BlockGrammar + + default_rules = [ + 'newline', 'hrule', 'block_code', 'fences', 'heading', + 'nptable', 'lheading', 'block_quote', + 'list_block', 'block_html', 'def_links', + 'def_footnotes', 'table', 'paragraph', 'text' + ] + + list_rules = ( + 'newline', 'block_code', 'fences', 'lheading', 'hrule', + 'block_quote', 'list_block', 'block_html', 'text', + ) + + footnote_rules = ( + 'newline', 'block_code', 'fences', 'heading', + 'nptable', 'lheading', 'hrule', 'block_quote', + 'list_block', 'block_html', 'table', 'paragraph', 'text' + ) + + def __init__(self, rules=None, **kwargs): + self.tokens = [] + self.def_links = {} + self.def_footnotes = {} + + if not rules: + rules = self.grammar_class() + + self.rules = rules + + def __call__(self, text, rules=None): + return self.parse(text, rules) + + def parse(self, text, rules=None): + text = text.rstrip('\n') + + if not rules: + rules = self.default_rules + + def manipulate(text): + for key in rules: + rule = getattr(self.rules, key) + m = rule.match(text) + if not m: + continue + getattr(self, 'parse_%s' % key)(m) + return m + return False + + while text: + m = manipulate(text) + if m is not False: + text = text[len(m.group(0)):] + continue + if text: + raise RuntimeError('Infinite loop at: %s' % text) + return self.tokens + + def parse_newline(self, m): + length = len(m.group(0)) + if length > 1: + self.tokens.append({'type': 'newline'}) + + def parse_block_code(self, m): + code = m.group(0) + pattern = re.compile(r'^ {4}', re.M) + code = pattern.sub('', code) + self.tokens.append({ + 'type': 'code', + 'lang': None, + 'text': code, + }) + + def parse_fences(self, m): + self.tokens.append({ + 'type': 'code', + 'lang': m.group(2), + 'text': m.group(3), + }) + + def parse_heading(self, m): + self.tokens.append({ + 'type': 'heading', + 'level': len(m.group(1)), + 'text': m.group(2), + }) + + def parse_lheading(self, m): + """Parse setext heading.""" + self.tokens.append({ + 'type': 'heading', + 'level': 1 if m.group(2) == '=' else 2, + 'text': m.group(1), + }) + + def parse_hrule(self, m): + self.tokens.append({'type': 'hrule'}) + + def parse_list_block(self, m): + bull = m.group(2) + self.tokens.append({ + 'type': 'list_start', + 'ordered': '.' in bull, + }) + cap = m.group(0) + self._process_list_item(cap, bull) + self.tokens.append({'type': 'list_end'}) + + def _process_list_item(self, cap, bull): + cap = self.rules.list_item.findall(cap) + + _next = False + length = len(cap) + + for i in range(length): + item = cap[i][0] + + # remove the bullet + space = len(item) + item = self.rules.list_bullet.sub('', item) + + # outdent + if '\n ' in item: + space = space - len(item) + pattern = re.compile(r'^ {1,%d}' % space, flags=re.M) + item = pattern.sub('', item) + + # determin whether item is loose or not + loose = _next + if not loose and re.search(r'\n\n(?!\s*$)', item): + loose = True + + rest = len(item) + if i != length - 1 and rest: + _next = item[rest-1] == '\n' + if not loose: + loose = _next + + if loose: + t = 'loose_item_start' + else: + t = 'list_item_start' + + self.tokens.append({'type': t}) + # recurse + self.parse(item, self.list_rules) + self.tokens.append({'type': 'list_item_end'}) + + def parse_block_quote(self, m): + self.tokens.append({'type': 'block_quote_start'}) + cap = m.group(0) + pattern = re.compile(r'^ *> ?', flags=re.M) + cap = pattern.sub('', cap) + self.parse(cap) + self.tokens.append({'type': 'block_quote_end'}) + + def parse_def_links(self, m): + key = _keyify(m.group(1)) + self.def_links[key] = { + 'link': m.group(2), + 'title': m.group(3), + } + + def parse_def_footnotes(self, m): + key = _keyify(m.group(1)) + if key in self.def_footnotes: + # footnote is already defined + return + + self.def_footnotes[key] = 0 + + self.tokens.append({ + 'type': 'footnote_start', + 'key': key, + }) + + text = m.group(2) + + if '\n' in text: + lines = text.split('\n') + whitespace = None + for line in lines[1:]: + space = len(line) - len(line.lstrip()) + if space and (not whitespace or space < whitespace): + whitespace = space + newlines = [lines[0]] + for line in lines[1:]: + newlines.append(line[whitespace:]) + text = '\n'.join(newlines) + + self.parse(text, self.footnote_rules) + + self.tokens.append({ + 'type': 'footnote_end', + 'key': key, + }) + + def parse_table(self, m): + item = self._process_table(m) + + cells = re.sub(r'(?: *\| *)?\n$', '', m.group(3)) + cells = cells.split('\n') + for i, v in enumerate(cells): + v = re.sub(r'^ *\| *| *\| *$', '', v) + cells[i] = re.split(r' *\| *', v) + + item['cells'] = cells + self.tokens.append(item) + + def parse_nptable(self, m): + item = self._process_table(m) + + cells = re.sub(r'\n$', '', m.group(3)) + cells = cells.split('\n') + for i, v in enumerate(cells): + cells[i] = re.split(r' *\| *', v) + + item['cells'] = cells + self.tokens.append(item) + + def _process_table(self, m): + header = re.sub(r'^ *| *\| *$', '', m.group(1)) + header = re.split(r' *\| *', header) + align = re.sub(r' *|\| *$', '', m.group(2)) + align = re.split(r' *\| *', align) + + for i, v in enumerate(align): + if re.search(r'^ *-+: *$', v): + align[i] = 'right' + elif re.search(r'^ *:-+: *$', v): + align[i] = 'center' + elif re.search(r'^ *:-+ *$', v): + align[i] = 'left' + else: + align[i] = None + + item = { + 'type': 'table', + 'header': header, + 'align': align, + } + return item + + def parse_block_html(self, m): + pre = m.group(1) in ['pre', 'script', 'style'] + text = m.group(0) + self.tokens.append({ + 'type': 'block_html', + 'pre': pre, + 'text': text + }) + + def parse_paragraph(self, m): + text = m.group(1).rstrip('\n') + self.tokens.append({'type': 'paragraph', 'text': text}) + + def parse_text(self, m): + text = m.group(0) + self.tokens.append({'type': 'text', 'text': text}) + + +class InlineGrammar(object): + """Grammars for inline level tokens.""" + + escape = re.compile(r'^\\([\\`*{}\[\]()#+\-.!_>~|])') # \* \+ \! .... + tag = re.compile( + r'^|' # comment + r'^<\/\w+>|' # close tag + r'^<\w+[^>]*?>' # open tag + ) + autolink = re.compile(r'^<([^ >]+(@|:\/)[^ >]+)>') + link = re.compile( + r'^!?\[(' + r'(?:\[[^^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*' + r')\]\(' + r'''\s*?(?:\s+['"]([\s\S]*?)['"])?\s*''' + r'\)' + ) + reflink = re.compile( + r'^!?\[(' + r'(?:\[[^^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*' + r')\]\s*\[([^^\]]*)\]' + ) + nolink = re.compile(r'^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]') + url = re.compile(r'''^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])''') + double_emphasis = re.compile( + r'^_{2}(.+?)_{2}(?!_)' # __word__ + r'|' + r'^\*{2}(.+?)\*{2}(?!\*)' # **word** + ) + emphasis = re.compile( + r'^\b_((?:__|.)+?)_\b' # _word_ + r'|' + r'^\*((?:\*\*|.)+?)\*(?!\*)' # *word* + ) + code = re.compile(r'^(`+)\s*(.*?[^`])\s*\1(?!`)') # `code` + linebreak = re.compile(r'^ {2,}\n(?!\s*$)') + strikethrough = re.compile(r'^~~(?=\S)(.*?\S)~~') # ~~word~~ + footnote = re.compile(r'^\[\^([^\]]+)\]') + text = re.compile(r'^[\s\S]+?(?=[\\'): + self._in_link = False + return self.renderer.tag(text) + + def output_footnote(self, m): + key = _keyify(m.group(1)) + if key not in self.footnotes: + return None + if self.footnotes[key]: + return None + self.footnote_index += 1 + self.footnotes[key] = self.footnote_index + return self.renderer.footnote_ref(key, self.footnote_index) + + def output_link(self, m): + return self._process_link(m, m.group(2), m.group(3)) + + def output_reflink(self, m): + key = _keyify(m.group(2) or m.group(1)) + if key not in self.links: + return None + ret = self.links[key] + return self._process_link(m, ret['link'], ret['title']) + + def output_nolink(self, m): + key = _keyify(m.group(1)) + if key not in self.links: + return None + ret = self.links[key] + return self._process_link(m, ret['link'], ret['title']) + + def _process_link(self, m, link, title=None): + line = m.group(0) + text = m.group(1) + if line[0] == '!': + return self.renderer.image(link, title, text) + + self._in_link = True + text = self.output(text) + self._in_link = False + return self.renderer.link(link, title, text) + + def output_double_emphasis(self, m): + text = m.group(2) or m.group(1) + text = self.output(text) + return self.renderer.double_emphasis(text) + + def output_emphasis(self, m): + text = m.group(2) or m.group(1) + text = self.output(text) + return self.renderer.emphasis(text) + + def output_code(self, m): + text = m.group(2) + return self.renderer.codespan(text) + + def output_linebreak(self, m): + return self.renderer.linebreak() + + def output_strikethrough(self, m): + text = self.output(m.group(1)) + return self.renderer.strikethrough(text) + + def output_text(self, m): + text = m.group(0) + return self.renderer.text(text) + + +class Renderer(object): + """The default HTML renderer for rendering Markdown. + """ + + def __init__(self, **kwargs): + self.options = kwargs + + def placeholder(self): + """Returns the default, empty output value for the renderer. + + All renderer methods use the '+=' operator to append to this value. + Default is a string so rendering HTML can build up a result string with + the rendered Markdown. + + Can be overridden by Renderer subclasses to be types like an empty + list, allowing the renderer to create a tree-like structure to + represent the document (which can then be reprocessed later into a + separate format like docx or pdf). + """ + return '' + + def block_code(self, code, lang=None): + """Rendering block level code. ``pre > code``. + + :param code: text content of the code block. + :param lang: language of the given code. + """ + code = code.rstrip('\n') + if not lang: + code = escape(code, smart_amp=False) + return '
    %s\n
    \n' % code + code = escape(code, quote=True, smart_amp=False) + return '
    %s\n
    \n' % (lang, code) + + def block_quote(self, text): + """Rendering
    with the given text. + + :param text: text content of the blockquote. + """ + return '
    %s\n
    \n' % text.rstrip('\n') + + def block_html(self, html): + """Rendering block level pure html content. + + :param html: text content of the html snippet. + """ + if self.options.get('skip_style') and \ + html.lower().startswith('`` ``

    ``. + + :param text: rendered text content for the header. + :param level: a number for the header level, for example: 1. + :param raw: raw text content of the header. + """ + return '%s\n' % (level, text, level) + + def hrule(self): + """Rendering method for ``
    `` tag.""" + if self.options.get('use_xhtml'): + return '
    \n' + return '
    \n' + + def list(self, body, ordered=True): + """Rendering list tags like ``
      `` and ``
        ``. + + :param body: body contents of the list. + :param ordered: whether this list is ordered or not. + """ + tag = 'ul' + if ordered: + tag = 'ol' + return '<%s>\n%s\n' % (tag, body, tag) + + def list_item(self, text): + """Rendering list item snippet. Like ``
      1. ``.""" + return '
      2. %s
      3. \n' % text + + def paragraph(self, text): + """Rendering paragraph tags. Like ``

        ``.""" + return '

        %s

        \n' % text.strip(' ') + + def table(self, header, body): + """Rendering table element. Wrap header and body in it. + + :param header: header part of the table. + :param body: body part of the table. + """ + return ( + '\n%s\n' + '\n%s\n
        \n' + ) % (header, body) + + def table_row(self, content): + """Rendering a table row. Like ````. + + :param content: content of current table row. + """ + return '\n%s\n' % content + + def table_cell(self, content, **flags): + """Rendering a table cell. Like ```` ````. + + :param content: content of current table cell. + :param header: whether this is header or not. + :param align: align of current table cell. + """ + if flags['header']: + tag = 'th' + else: + tag = 'td' + align = flags['align'] + if not align: + return '<%s>%s\n' % (tag, content, tag) + return '<%s style="text-align:%s">%s\n' % ( + tag, align, content, tag + ) + + def double_emphasis(self, text): + """Rendering **strong** text. + + :param text: text content for emphasis. + """ + return '%s' % text + + def emphasis(self, text): + """Rendering *emphasis* text. + + :param text: text content for emphasis. + """ + return '%s' % text + + def codespan(self, text): + """Rendering inline `code` text. + + :param text: text content for inline code. + """ + text = escape(text.rstrip(), smart_amp=False) + return '%s' % text + + def linebreak(self): + """Rendering line break like ``
        ``.""" + if self.options.get('use_xhtml'): + return '
        \n' + return '
        \n' + + def strikethrough(self, text): + """Rendering ~~strikethrough~~ text. + + :param text: text content for strikethrough. + """ + return '%s' % text + + def text(self, text): + """Rendering unformatted text. + + :param text: text content. + """ + return escape(text) + + def autolink(self, link, is_email=False): + """Rendering a given link or email address. + + :param link: link content or email address. + :param is_email: whether this is an email or not. + """ + text = link = escape(link) + if is_email: + link = 'mailto:%s' % link + return '%s' % (link, text) + + def link(self, link, title, text): + """Rendering a given link with content and title. + + :param link: href link for ```` tag. + :param title: title content for `title` attribute. + :param text: text content for description. + """ + if link.startswith('javascript:'): + link = '' + if not title: + return '%s' % (link, text) + title = escape(title, quote=True) + return '%s' % (link, title, text) + + def image(self, src, title, text): + """Rendering a image with title and text. + + :param src: source link of the image. + :param title: title text of the image. + :param text: alt text of the image. + """ + if src.startswith('javascript:'): + src = '' + text = escape(text, quote=True) + if title: + title = escape(title, quote=True) + html = '%s' % html + return '%s>' % html + + def tag(self, html): + """Rendering span level html tag. + + :param html: html tag snippet. + """ + if self.options.get('skip_html'): + return '' + if self.options.get('escape'): + return escape(html) + return html + + def newline(self): + """Rendering newline element.""" + return '' + + def footnote_ref(self, key, index): + """Rendering the ref anchor of a footnote. + + :param key: identity key for the footnote. + :param index: the index count of current footnote. + """ + html = ( + '' + '%d' + ) % (escape(key), escape(key), index) + return html + + def footnote_item(self, key, text): + """Rendering a footnote item. + + :param key: identity key for the footnote. + :param text: text content of the footnote. + """ + back = ( + '' + ) % escape(key) + text = text.rstrip() + if text.endswith('

        '): + text = re.sub(r'<\/p>$', r'%s

        ' % back, text) + else: + text = '%s

        %s

        ' % (text, back) + html = '
      4. %s
      5. \n' % (escape(key), text) + return html + + def footnotes(self, text): + """Wrapper for all footnotes. + + :param text: contents of all footnotes. + """ + html = '
        \n%s
          %s
        \n
        \n' + return html % (self.hrule(), text) + + +class Markdown(object): + """The Markdown parser. + + :param renderer: An instance of ``Renderer``. + """ + + def __init__(self, renderer=None, inline=None, block=None, **kwargs): + if not renderer: + renderer = Renderer(**kwargs) + + self.renderer = renderer + + if inline and inspect.isclass(inline): + inline = inline(renderer, **kwargs) + if block and inspect.isclass(block): + block = block(**kwargs) + + if inline: + self.inline = inline + else: + rules = InlineGrammar() + if kwargs.get('hard_wrap'): + rules.hard_wrap() + self.inline = InlineLexer(renderer, rules=rules) + + self.block = block or BlockLexer(BlockGrammar()) + self.options = kwargs + self.footnotes = [] + self.tokens = [] + + def __call__(self, text): + return self.parse(text) + + def render(self, text): + """Render the Markdown text. + + :param text: markdown formatted text content. + """ + return self.parse(text) + + def parse(self, text): + out = self.output(preprocessing(text)) + + keys = self.block.def_footnotes + + # reset block + self.block.def_links = {} + self.block.def_footnotes = {} + + # reset inline + self.inline.links = {} + self.inline.footnotes = {} + + if not self.footnotes: + return out + + footnotes = filter(lambda o: keys.get(o['key']), self.footnotes) + self.footnotes = sorted( + footnotes, key=lambda o: keys.get(o['key']), reverse=True + ) + + body = self.renderer.placeholder() + while self.footnotes: + note = self.footnotes.pop() + body += self.renderer.footnote_item( + note['key'], note['text'] + ) + + out += self.renderer.footnotes(body) + return out + + def pop(self): + if not self.tokens: + return None + self.token = self.tokens.pop() + return self.token + + def peek(self): + if self.tokens: + return self.tokens[-1] + return None + + def output(self, text, rules=None): + self.tokens = self.block(text, rules) + self.tokens.reverse() + + self.inline.setup(self.block.def_links, self.block.def_footnotes) + + out = self.renderer.placeholder() + while self.pop(): + out += self.tok() + return out + + def tok(self): + t = self.token['type'] + + # sepcial cases + if t.endswith('_start'): + t = t[:-6] + + return getattr(self, 'output_%s' % t)() + + def tok_text(self): + text = self.token['text'] + while self.peek()['type'] == 'text': + text += '\n' + self.pop()['text'] + return self.inline(text) + + def output_newline(self): + return self.renderer.newline() + + def output_hrule(self): + return self.renderer.hrule() + + def output_heading(self): + return self.renderer.header( + self.inline(self.token['text']), + self.token['level'], + self.token['text'], + ) + + def output_code(self): + return self.renderer.block_code( + self.token['text'], self.token['lang'] + ) + + def output_table(self): + aligns = self.token['align'] + aligns_length = len(aligns) + cell = self.renderer.placeholder() + + # header part + header = self.renderer.placeholder() + for i, value in enumerate(self.token['header']): + align = aligns[i] if i < aligns_length else None + flags = {'header': True, 'align': align} + cell += self.renderer.table_cell(self.inline(value), **flags) + + header += self.renderer.table_row(cell) + + # body part + body = self.renderer.placeholder() + for i, row in enumerate(self.token['cells']): + cell = self.renderer.placeholder() + for j, value in enumerate(row): + align = aligns[j] if j < aligns_length else None + flags = {'header': False, 'align': align} + cell += self.renderer.table_cell(self.inline(value), **flags) + body += self.renderer.table_row(cell) + + return self.renderer.table(header, body) + + def output_block_quote(self): + body = self.renderer.placeholder() + while self.pop()['type'] != 'block_quote_end': + body += self.tok() + return self.renderer.block_quote(body) + + def output_list(self): + ordered = self.token['ordered'] + body = self.renderer.placeholder() + while self.pop()['type'] != 'list_end': + body += self.tok() + return self.renderer.list(body, ordered) + + def output_list_item(self): + body = self.renderer.placeholder() + while self.pop()['type'] != 'list_item_end': + if self.token['type'] == 'text': + body += self.tok_text() + else: + body += self.tok() + + return self.renderer.list_item(body) + + def output_loose_item(self): + body = self.renderer.placeholder() + while self.pop()['type'] != 'list_item_end': + body += self.tok() + return self.renderer.list_item(body) + + def output_footnote(self): + self.inline._in_footnote = True + body = self.renderer.placeholder() + key = self.token['key'] + while self.pop()['type'] != 'footnote_end': + body += self.tok() + self.footnotes.append({'key': key, 'text': body}) + self.inline._in_footnote = False + return self.renderer.placeholder() + + def output_block_html(self): + text = self.token['text'] + if self.options.get('parse_html') and not self.token.get('pre'): + text = self.inline(text) + return self.renderer.block_html(text) + + def output_paragraph(self): + return self.renderer.paragraph(self.inline(self.token['text'])) + + def output_text(self): + return self.renderer.paragraph(self.tok_text()) + + +def markdown(text, **kwargs): + """Render markdown formatted text to html. + + :param text: markdown formatted text content. + :param escape: if set to True, all html tags will be escaped. + :param use_xhtml: output with xhtml tags. + """ + return Markdown(**kwargs)(text) diff --git a/doc/template/ga.tmpl b/doc/template/ga.tmpl new file mode 100644 index 0000000..2894234 --- /dev/null +++ b/doc/template/ga.tmpl @@ -0,0 +1,10 @@ + \ No newline at end of file diff --git a/doc/template/ghfork.tmpl b/doc/template/ghfork.tmpl new file mode 100644 index 0000000..1aad88a --- /dev/null +++ b/doc/template/ghfork.tmpl @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/doc/template/ghstar.tmpl b/doc/template/ghstar.tmpl new file mode 100644 index 0000000..8aaa5de --- /dev/null +++ b/doc/template/ghstar.tmpl @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/doc/template/ghwatch.tmpl b/doc/template/ghwatch.tmpl new file mode 100644 index 0000000..d48a316 --- /dev/null +++ b/doc/template/ghwatch.tmpl @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/doc/template/header.tmpl b/doc/template/header.tmpl index ac19494..c1a4a4a 100644 --- a/doc/template/header.tmpl +++ b/doc/template/header.tmpl @@ -13,6 +13,8 @@ +$ga + @@ -41,8 +43,7 @@

        Version $version

        To install, just add enum.h to your project.

        - Visit the GitHub repo for issues, feedback, and let me know if you - found this library useful! + Visit the GitHub repo for issues, feedback, and the latest development.

        @@ -53,5 +54,11 @@ + +
        diff --git a/doc/template/tweet.tmpl b/doc/template/tweet.tmpl new file mode 100644 index 0000000..3d2c9d3 --- /dev/null +++ b/doc/template/tweet.tmpl @@ -0,0 +1,3 @@ + + Share this page + \ No newline at end of file diff --git a/doc/template/twsupport.tmpl b/doc/template/twsupport.tmpl new file mode 100644 index 0000000..5776b24 --- /dev/null +++ b/doc/template/twsupport.tmpl @@ -0,0 +1,3 @@ + + Tweet for suport + \ No newline at end of file diff --git a/doc/transform.py b/doc/transform.py index 63ef2a7..04652f2 100755 --- a/doc/transform.py +++ b/doc/transform.py @@ -50,7 +50,7 @@ def camel_case(text): components = re.split("[ -]+", text) components = map(lambda s: s.capitalize(), components) result = "".join(components) - result = filter(lambda c: c not in ",!:-$()", result) + result = filter(lambda c: c not in ",!:-$()?", result) return result class HtmlRenderer(mistune.Renderer): diff --git a/example/1-hello-world.cc b/example/1-hello-world.cc index 67d14dd..d93f1b1 100644 --- a/example/1-hello-world.cc +++ b/example/1-hello-world.cc @@ -2,7 +2,7 @@ // Hello, World! // -// Download enum.h, then build this program with it: +// Download enum.h, then compile this program: #include #include "enum.h" diff --git a/example/102-any-underlying.cc b/example/102-any-underlying.cc index 5bb8761..44a7244 100644 --- a/example/102-any-underlying.cc +++ b/example/102-any-underlying.cc @@ -9,7 +9,15 @@ // have to be constexpr. In C++98, everything involving T will simply be done by // Better Enums at run time. // -// Here's how to do it. +// This feature is semi-experimental. I am considering relaxing the requirements +// on T so that it doesn't have to be literal. I can use a reinterpret_cast to +// make a mapping automatically. This will make non-integral underlying types +// easier to use, but will also prevent usage at compile time, which +// unfortunately has structural consequences for the implementation of Better +// Enums, and additional semantic consequences for usage, even at run time. +// +// In the meantime, here's how to have a non-integral underlying type in the +// current version. #include #include diff --git a/example/5-iostreams.cc b/example/5-iostreams.cc index 2d108f0..0904843 100644 --- a/example/5-iostreams.cc +++ b/example/5-iostreams.cc @@ -2,8 +2,9 @@ // Stream operators // -// These work almost as you'd expect. Just make sure you include iostream before -// enum.h in any translation unit in which you intend to use the operators. +// These work almost as you'd expect. First, make sure you include iostream +// before enum.h in any translation unit in which you intend to use the +// operators: #include #include @@ -25,5 +26,5 @@ int main() // // Channel channel = Channel::Blue; // std::cin >> channel; // Expects input such as "Green". -// + // Only char streams are supported for the time being.