%PDF- %PDF-
Direktori : /lib/python3/dist-packages/markdown/extensions/ |
Current File : //lib/python3/dist-packages/markdown/extensions/smarty.py |
# Smarty extension for Python-Markdown # ==================================== # Adds conversion of ASCII dashes, quotes and ellipses to their HTML # entity equivalents. # See https://Python-Markdown.github.io/extensions/smarty # for documentation. # Author: 2013, Dmitry Shachnev <mitya57@gmail.com> # All changes Copyright 2013-2014 The Python Markdown Project # License: [BSD](https://opensource.org/licenses/bsd-license.php) # SmartyPants license: # Copyright (c) 2003 John Gruber <https://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. """ Adds conversion of ASCII dashes, quotes and ellipses to their HTML entity equivalents. See the [documentation](https://Python-Markdown.github.io/extensions/smarty) for details. """ from __future__ import annotations from . import Extension from ..inlinepatterns import HtmlInlineProcessor, HTML_RE from ..treeprocessors import InlineProcessor from ..util import Registry from typing import TYPE_CHECKING, Sequence if TYPE_CHECKING: # pragma: no cover from markdown import Markdown from .. import inlinepatterns import re import xml.etree.ElementTree as etree # Constants for quote education. punctClass = r"""[!"#\$\%'()*+,-.\/:;<=>?\@\[\\\]\^_`{|}~]""" endOfWordClass = r"[\s.,;:!?)]" closeClass = r"[^\ \t\r\n\[\{\(\-\u0002\u0003]" openingQuotesBase = ( r'(\s' # a whitespace char r'| ' # or a non-breaking space entity r'|--' # or dashes r'|–|—' # or Unicode r'|&[mn]dash;' # or named dash entities r'|–|—' # or decimal entities r')' ) 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\b)" # All remaining quotes should be opening ones remainingSingleQuotesRegex = r"'" remainingDoubleQuotesRegex = r'"' HTML_STRICT_RE = HTML_RE + r'(?!\>)' class SubstituteTextPattern(HtmlInlineProcessor): def __init__(self, pattern: str, replace: Sequence[int | str | etree.Element], md: Markdown): """ Replaces matches with some text. """ HtmlInlineProcessor.__init__(self, pattern) self.replace = replace self.md = md def handleMatch(self, m: re.Match[str], data: str) -> tuple[str, int, int]: result = '' for part in self.replace: if isinstance(part, int): result += m.group(part) else: result += self.md.htmlStash.store(part) return result, m.start(0), m.end(0) class SmartyExtension(Extension): """ Add Smarty to Markdown. """ def __init__(self, **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'], } """ Default configuration options. """ super().__init__(**kwargs) self.substitutions: dict[str, str] = dict(substitutions) self.substitutions.update(self.getConfig('substitutions', default={})) def _addPatterns( self, md: Markdown, patterns: Sequence[tuple[str, Sequence[int | str | etree.Element]]], serie: str, priority: int, ): for ind, pattern in enumerate(patterns): pattern += (md,) pattern = SubstituteTextPattern(*pattern) name = 'smarty-%s-%d' % (serie, ind) self.inlinePatterns.register(pattern, name, priority-ind) def educateDashes(self, md: Markdown) -> None: emDashesPattern = SubstituteTextPattern( r'(?<!-)---(?!-)', (self.substitutions['mdash'],), md ) enDashesPattern = SubstituteTextPattern( r'(?<!-)--(?!-)', (self.substitutions['ndash'],), md ) self.inlinePatterns.register(emDashesPattern, 'smarty-em-dashes', 50) self.inlinePatterns.register(enDashesPattern, 'smarty-en-dashes', 45) def educateEllipses(self, md: Markdown) -> None: ellipsesPattern = SubstituteTextPattern( r'(?<!\.)\.{3}(?!\.)', (self.substitutions['ellipsis'],), md ) self.inlinePatterns.register(ellipsesPattern, 'smarty-ellipses', 10) def educateAngledQuotes(self, md: Markdown) -> None: leftAngledQuotePattern = SubstituteTextPattern( r'\<\<', (self.substitutions['left-angle-quote'],), md ) rightAngledQuotePattern = SubstituteTextPattern( r'\>\>', (self.substitutions['right-angle-quote'],), md ) self.inlinePatterns.register(leftAngledQuotePattern, 'smarty-left-angle-quotes', 40) self.inlinePatterns.register(rightAngledQuotePattern, 'smarty-right-angle-quotes', 35) def educateQuotes(self, md: Markdown) -> None: 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, (1, lsquo)), (closingSingleQuotesRegex, (rsquo,)), (closingSingleQuotesRegex2, (rsquo, 1)), (remainingSingleQuotesRegex, (lsquo,)), (openingDoubleQuotesRegex, (1, ldquo)), (closingDoubleQuotesRegex, (rdquo,)), (closingDoubleQuotesRegex2, (rdquo,)), (remainingDoubleQuotesRegex, (ldquo,)) ) self._addPatterns(md, patterns, 'quotes', 30) def extendMarkdown(self, md): configs = self.getConfigs() self.inlinePatterns: Registry[inlinepatterns.InlineProcessor] = Registry() if configs['smart_ellipses']: self.educateEllipses(md) if configs['smart_quotes']: self.educateQuotes(md) if configs['smart_angled_quotes']: self.educateAngledQuotes(md) # Override `HTML_RE` from `inlinepatterns.py` so that it does not # process tags with duplicate closing quotes. md.inlinePatterns.register(HtmlInlineProcessor(HTML_STRICT_RE, md), 'html', 90) if configs['smart_dashes']: self.educateDashes(md) inlineProcessor = InlineProcessor(md) inlineProcessor.inlinePatterns = self.inlinePatterns md.treeprocessors.register(inlineProcessor, 'smarty', 2) md.ESCAPED_CHARS.extend(['"', "'"]) def makeExtension(**kwargs): # pragma: no cover return SmartyExtension(**kwargs)