| Index: third_party/Python-Markdown/markdown/extensions/smarty.py
|
| diff --git a/third_party/Python-Markdown/markdown/extensions/smarty.py b/third_party/Python-Markdown/markdown/extensions/smarty.py
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..46e54c1ec157502d580b3835e0cc0933b32d8a4f
|
| --- /dev/null
|
| +++ b/third_party/Python-Markdown/markdown/extensions/smarty.py
|
| @@ -0,0 +1,263 @@
|
| +# -*- coding: utf-8 -*-
|
| +'''
|
| +Smarty extension for Python-Markdown
|
| +====================================
|
| +
|
| +Adds conversion of ASCII dashes, quotes and ellipses to their HTML
|
| +entity equivalents.
|
| +
|
| +See <https://pythonhosted.org/Markdown/extensions/smarty.html>
|
| +for documentation.
|
| +
|
| +Author: 2013, Dmitry Shachnev <mitya57@gmail.com>
|
| +
|
| +All changes Copyright 2013-2014 The Python Markdown Project
|
| +
|
| +License: [BSD](http://www.opensource.org/licenses/bsd-license.php)
|
| +
|
| +SmartyPants license:
|
| +
|
| + Copyright (c) 2003 John Gruber <http://daringfireball.net/>
|
| + 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 "SmartyPants" 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
|
| + owner 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.
|
| +
|
| +
|
| +smartypants.py license:
|
| +
|
| + smartypants.py is a derivative work of SmartyPants.
|
| + Copyright (c) 2004, 2007 Chad Miller <http://web.chad.org/>
|
| +
|
| + 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.
|
| +
|
| + 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
|
| + owner 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.
|
| +
|
| +'''
|
| +
|
| +
|
| +from __future__ import unicode_literals
|
| +from . import Extension
|
| +from ..inlinepatterns import HtmlPattern
|
| +from ..odict import OrderedDict
|
| +from ..treeprocessors import InlineProcessor
|
| +
|
| +
|
| +# Constants for quote education.
|
| +punctClass = r"""[!"#\$\%'()*+,-.\/:;<=>?\@\[\\\]\^_`{|}~]"""
|
| +endOfWordClass = r"[\s.,;:!?)]"
|
| +closeClass = "[^\ \t\r\n\[\{\(\-\u0002\u0003]"
|
| +
|
| +openingQuotesBase = (
|
| + '(\s' # a whitespace char
|
| + '| ' # or a non-breaking space entity
|
| + '|--' # or dashes
|
| + '|–|—' # or unicode
|
| + '|&[mn]dash;' # or named dash entities
|
| + '|–|—' # or decimal entities
|
| + ')'
|
| +)
|
| +
|
| +substitutions = {
|
| + 'mdash': '—',
|
| + 'ndash': '–',
|
| + 'ellipsis': '…',
|
| + 'left-angle-quote': '«',
|
| + 'right-angle-quote': '»',
|
| + 'left-single-quote': '‘',
|
| + 'right-single-quote': '’',
|
| + 'left-double-quote': '“',
|
| + 'right-double-quote': '”',
|
| +}
|
| +
|
| +
|
| +# Special case if the very first character is a quote
|
| +# followed by punctuation at a non-word-break. Close the quotes by brute force:
|
| +singleQuoteStartRe = r"^'(?=%s\B)" % punctClass
|
| +doubleQuoteStartRe = r'^"(?=%s\B)' % punctClass
|
| +
|
| +# Special case for double sets of quotes, e.g.:
|
| +# <p>He said, "'Quoted' words in a larger quote."</p>
|
| +doubleQuoteSetsRe = r""""'(?=\w)"""
|
| +singleQuoteSetsRe = r"""'"(?=\w)"""
|
| +
|
| +# Special case for decade abbreviations (the '80s):
|
| +decadeAbbrRe = r"(?<!\w)'(?=\d{2}s)"
|
| +
|
| +# Get most opening double quotes:
|
| +openingDoubleQuotesRegex = r'%s"(?=\w)' % openingQuotesBase
|
| +
|
| +# Double closing quotes:
|
| +closingDoubleQuotesRegex = r'"(?=\s)'
|
| +closingDoubleQuotesRegex2 = '(?<=%s)"' % closeClass
|
| +
|
| +# Get most opening single quotes:
|
| +openingSingleQuotesRegex = r"%s'(?=\w)" % openingQuotesBase
|
| +
|
| +# Single closing quotes:
|
| +closingSingleQuotesRegex = r"(?<=%s)'(?!\s|s\b|\d)" % closeClass
|
| +closingSingleQuotesRegex2 = r"(?<=%s)'(\s|s\b)" % closeClass
|
| +
|
| +# All remaining quotes should be opening ones
|
| +remainingSingleQuotesRegex = "'"
|
| +remainingDoubleQuotesRegex = '"'
|
| +
|
| +
|
| +class SubstituteTextPattern(HtmlPattern):
|
| + def __init__(self, pattern, replace, markdown_instance):
|
| + """ Replaces matches with some text. """
|
| + HtmlPattern.__init__(self, pattern)
|
| + self.replace = replace
|
| + self.markdown = markdown_instance
|
| +
|
| + def handleMatch(self, m):
|
| + result = ''
|
| + for part in self.replace:
|
| + if isinstance(part, int):
|
| + result += m.group(part)
|
| + else:
|
| + result += self.markdown.htmlStash.store(part, safe=True)
|
| + return result
|
| +
|
| +
|
| +class SmartyExtension(Extension):
|
| + def __init__(self, *args, **kwargs):
|
| + self.config = {
|
| + 'smart_quotes': [True, 'Educate quotes'],
|
| + 'smart_angled_quotes': [False, 'Educate angled quotes'],
|
| + 'smart_dashes': [True, 'Educate dashes'],
|
| + 'smart_ellipses': [True, 'Educate ellipses'],
|
| + 'substitutions': [{}, 'Overwrite default substitutions'],
|
| + }
|
| + super(SmartyExtension, self).__init__(*args, **kwargs)
|
| + self.substitutions = dict(substitutions)
|
| + self.substitutions.update(self.getConfig('substitutions', default={}))
|
| +
|
| + def _addPatterns(self, md, patterns, serie):
|
| + for ind, pattern in enumerate(patterns):
|
| + pattern += (md,)
|
| + pattern = SubstituteTextPattern(*pattern)
|
| + after = ('>smarty-%s-%d' % (serie, ind - 1) if ind else '_begin')
|
| + name = 'smarty-%s-%d' % (serie, ind)
|
| + self.inlinePatterns.add(name, pattern, after)
|
| +
|
| + def educateDashes(self, md):
|
| + emDashesPattern = SubstituteTextPattern(
|
| + r'(?<!-)---(?!-)', (self.substitutions['mdash'],), md
|
| + )
|
| + enDashesPattern = SubstituteTextPattern(
|
| + r'(?<!-)--(?!-)', (self.substitutions['ndash'],), md
|
| + )
|
| + self.inlinePatterns.add('smarty-em-dashes', emDashesPattern, '_begin')
|
| + self.inlinePatterns.add(
|
| + 'smarty-en-dashes', enDashesPattern, '>smarty-em-dashes'
|
| + )
|
| +
|
| + def educateEllipses(self, md):
|
| + ellipsesPattern = SubstituteTextPattern(
|
| + r'(?<!\.)\.{3}(?!\.)', (self.substitutions['ellipsis'],), md
|
| + )
|
| + self.inlinePatterns.add('smarty-ellipses', ellipsesPattern, '_begin')
|
| +
|
| + def educateAngledQuotes(self, md):
|
| + leftAngledQuotePattern = SubstituteTextPattern(
|
| + r'\<\<', (self.substitutions['left-angle-quote'],), md
|
| + )
|
| + rightAngledQuotePattern = SubstituteTextPattern(
|
| + r'\>\>', (self.substitutions['right-angle-quote'],), md
|
| + )
|
| + self.inlinePatterns.add(
|
| + 'smarty-left-angle-quotes', leftAngledQuotePattern, '_begin'
|
| + )
|
| + self.inlinePatterns.add(
|
| + 'smarty-right-angle-quotes',
|
| + rightAngledQuotePattern,
|
| + '>smarty-left-angle-quotes'
|
| + )
|
| +
|
| + def educateQuotes(self, md):
|
| + lsquo = self.substitutions['left-single-quote']
|
| + rsquo = self.substitutions['right-single-quote']
|
| + ldquo = self.substitutions['left-double-quote']
|
| + rdquo = self.substitutions['right-double-quote']
|
| + patterns = (
|
| + (singleQuoteStartRe, (rsquo,)),
|
| + (doubleQuoteStartRe, (rdquo,)),
|
| + (doubleQuoteSetsRe, (ldquo + lsquo,)),
|
| + (singleQuoteSetsRe, (lsquo + ldquo,)),
|
| + (decadeAbbrRe, (rsquo,)),
|
| + (openingSingleQuotesRegex, (2, lsquo)),
|
| + (closingSingleQuotesRegex, (rsquo,)),
|
| + (closingSingleQuotesRegex2, (rsquo, 2)),
|
| + (remainingSingleQuotesRegex, (lsquo,)),
|
| + (openingDoubleQuotesRegex, (2, ldquo)),
|
| + (closingDoubleQuotesRegex, (rdquo,)),
|
| + (closingDoubleQuotesRegex2, (rdquo,)),
|
| + (remainingDoubleQuotesRegex, (ldquo,))
|
| + )
|
| + self._addPatterns(md, patterns, 'quotes')
|
| +
|
| + def extendMarkdown(self, md, md_globals):
|
| + configs = self.getConfigs()
|
| + self.inlinePatterns = OrderedDict()
|
| + if configs['smart_ellipses']:
|
| + self.educateEllipses(md)
|
| + if configs['smart_quotes']:
|
| + self.educateQuotes(md)
|
| + if configs['smart_angled_quotes']:
|
| + self.educateAngledQuotes(md)
|
| + if configs['smart_dashes']:
|
| + self.educateDashes(md)
|
| + inlineProcessor = InlineProcessor(md)
|
| + inlineProcessor.inlinePatterns = self.inlinePatterns
|
| + md.treeprocessors.add('smarty', inlineProcessor, '_end')
|
| + md.ESCAPED_CHARS.extend(['"', "'"])
|
| +
|
| +
|
| +def makeExtension(*args, **kwargs):
|
| + return SmartyExtension(*args, **kwargs)
|
|
|