From 146103ec781a4ad13aad52e4800b6d44fe478f59 Mon Sep 17 00:00:00 2001 From: octospacc Date: Tue, 17 May 2022 18:16:39 +0200 Subject: [PATCH] Add Pug support --- Source/Build.py | 45 +- Source/Libs/markdown/__init__.py | 61 -- Source/Libs/markdown/__main__.py | 151 --- Source/Libs/markdown/__meta__.py | 49 - .../__pycache__/__init__.cpython-310.pyc | Bin 1830 -> 0 bytes .../__pycache__/__meta__.cpython-310.pyc | Bin 1468 -> 0 bytes .../__pycache__/blockparser.cpython-310.pyc | Bin 4973 -> 0 bytes .../blockprocessors.cpython-310.pyc | Bin 17669 -> 0 bytes .../markdown/__pycache__/core.cpython-310.pyc | Bin 12072 -> 0 bytes .../__pycache__/htmlparser.cpython-310.pyc | Bin 9020 -> 0 bytes .../inlinepatterns.cpython-310.pyc | Bin 25217 -> 0 bytes .../__pycache__/pep562.cpython-310.pyc | Bin 8133 -> 0 bytes .../postprocessors.cpython-310.pyc | Bin 4860 -> 0 bytes .../__pycache__/preprocessors.cpython-310.pyc | Bin 3273 -> 0 bytes .../__pycache__/serializers.cpython-310.pyc | Bin 3143 -> 0 bytes .../treeprocessors.cpython-310.pyc | Bin 11875 -> 0 bytes .../markdown/__pycache__/util.cpython-310.pyc | Bin 14328 -> 0 bytes Source/Libs/markdown/blockparser.py | 125 --- Source/Libs/markdown/blockprocessors.py | 623 ------------ Source/Libs/markdown/core.py | 407 -------- Source/Libs/markdown/extensions/__init__.py | 107 --- .../__pycache__/__init__.cpython-310.pyc | Bin 4004 -> 0 bytes Source/Libs/markdown/extensions/abbr.py | 99 -- Source/Libs/markdown/extensions/admonition.py | 170 ---- Source/Libs/markdown/extensions/attr_list.py | 166 ---- Source/Libs/markdown/extensions/codehilite.py | 308 ------ Source/Libs/markdown/extensions/def_list.py | 111 --- Source/Libs/markdown/extensions/extra.py | 58 -- .../Libs/markdown/extensions/fenced_code.py | 174 ---- Source/Libs/markdown/extensions/footnotes.py | 402 -------- .../Libs/markdown/extensions/legacy_attrs.py | 67 -- Source/Libs/markdown/extensions/legacy_em.py | 49 - Source/Libs/markdown/extensions/md_in_html.py | 364 ------- Source/Libs/markdown/extensions/meta.py | 79 -- Source/Libs/markdown/extensions/nl2br.py | 33 - Source/Libs/markdown/extensions/sane_lists.py | 54 -- Source/Libs/markdown/extensions/smarty.py | 263 ------ Source/Libs/markdown/extensions/tables.py | 223 ----- Source/Libs/markdown/extensions/toc.py | 380 -------- Source/Libs/markdown/extensions/wikilinks.py | 87 -- Source/Libs/markdown/htmlparser.py | 323 ------- Source/Libs/markdown/inlinepatterns.py | 892 ------------------ Source/Libs/markdown/pep562.py | 245 ----- Source/Libs/markdown/postprocessors.py | 134 --- Source/Libs/markdown/preprocessors.py | 82 -- Source/Libs/markdown/serializers.py | 189 ---- Source/Libs/markdown/test_tools.py | 220 ----- Source/Libs/markdown/treeprocessors.py | 436 --------- Source/Libs/markdown/util.py | 485 ---------- 49 files changed, 20 insertions(+), 7641 deletions(-) delete mode 100644 Source/Libs/markdown/__init__.py delete mode 100644 Source/Libs/markdown/__main__.py delete mode 100644 Source/Libs/markdown/__meta__.py delete mode 100644 Source/Libs/markdown/__pycache__/__init__.cpython-310.pyc delete mode 100644 Source/Libs/markdown/__pycache__/__meta__.cpython-310.pyc delete mode 100644 Source/Libs/markdown/__pycache__/blockparser.cpython-310.pyc delete mode 100644 Source/Libs/markdown/__pycache__/blockprocessors.cpython-310.pyc delete mode 100644 Source/Libs/markdown/__pycache__/core.cpython-310.pyc delete mode 100644 Source/Libs/markdown/__pycache__/htmlparser.cpython-310.pyc delete mode 100644 Source/Libs/markdown/__pycache__/inlinepatterns.cpython-310.pyc delete mode 100644 Source/Libs/markdown/__pycache__/pep562.cpython-310.pyc delete mode 100644 Source/Libs/markdown/__pycache__/postprocessors.cpython-310.pyc delete mode 100644 Source/Libs/markdown/__pycache__/preprocessors.cpython-310.pyc delete mode 100644 Source/Libs/markdown/__pycache__/serializers.cpython-310.pyc delete mode 100644 Source/Libs/markdown/__pycache__/treeprocessors.cpython-310.pyc delete mode 100644 Source/Libs/markdown/__pycache__/util.cpython-310.pyc delete mode 100644 Source/Libs/markdown/blockparser.py delete mode 100644 Source/Libs/markdown/blockprocessors.py delete mode 100644 Source/Libs/markdown/core.py delete mode 100644 Source/Libs/markdown/extensions/__init__.py delete mode 100644 Source/Libs/markdown/extensions/__pycache__/__init__.cpython-310.pyc delete mode 100644 Source/Libs/markdown/extensions/abbr.py delete mode 100644 Source/Libs/markdown/extensions/admonition.py delete mode 100644 Source/Libs/markdown/extensions/attr_list.py delete mode 100644 Source/Libs/markdown/extensions/codehilite.py delete mode 100644 Source/Libs/markdown/extensions/def_list.py delete mode 100644 Source/Libs/markdown/extensions/extra.py delete mode 100644 Source/Libs/markdown/extensions/fenced_code.py delete mode 100644 Source/Libs/markdown/extensions/footnotes.py delete mode 100644 Source/Libs/markdown/extensions/legacy_attrs.py delete mode 100644 Source/Libs/markdown/extensions/legacy_em.py delete mode 100644 Source/Libs/markdown/extensions/md_in_html.py delete mode 100644 Source/Libs/markdown/extensions/meta.py delete mode 100644 Source/Libs/markdown/extensions/nl2br.py delete mode 100644 Source/Libs/markdown/extensions/sane_lists.py delete mode 100644 Source/Libs/markdown/extensions/smarty.py delete mode 100644 Source/Libs/markdown/extensions/tables.py delete mode 100644 Source/Libs/markdown/extensions/toc.py delete mode 100644 Source/Libs/markdown/extensions/wikilinks.py delete mode 100644 Source/Libs/markdown/htmlparser.py delete mode 100644 Source/Libs/markdown/inlinepatterns.py delete mode 100644 Source/Libs/markdown/pep562.py delete mode 100644 Source/Libs/markdown/postprocessors.py delete mode 100644 Source/Libs/markdown/preprocessors.py delete mode 100644 Source/Libs/markdown/serializers.py delete mode 100644 Source/Libs/markdown/test_tools.py delete mode 100644 Source/Libs/markdown/treeprocessors.py delete mode 100644 Source/Libs/markdown/util.py diff --git a/Source/Build.py b/Source/Build.py index fae1b7e..a41b9fb 100755 --- a/Source/Build.py +++ b/Source/Build.py @@ -1,8 +1,9 @@ #!/usr/bin/env python3 + import os import shutil from pathlib import Path -from Libs.markdown import Markdown +from markdown import Markdown def ReadFile(p): try: @@ -49,23 +50,25 @@ def PreProcessor(File): 'Style': ''} for l in File.splitlines(): ls = l.lstrip() - if ls.startswith('\%'): - Content += ls[1:] + '\n' - elif ls.startswith('% - '): - Content += ''.format(ls[4:]) + '\n' - elif ls.startswith('% Template: '): - Meta['Template'] = ls[len('% Template: '):] - elif ls.startswith('% Background: '): - Meta['Style'] += "#MainBox{Background:" + ls[len('% Background: '):] + ";} " - elif ls.startswith('% Style: '): - Meta['Style'] += ls[len('% Style: '):] + ' ' + if ls.startswith('//'): + if ls.startswith('// Template: '): + Meta['Template'] = ls[len('// Template: '):] + elif ls.startswith('// Background: '): + Meta['Style'] += "#MainBox{Background:" + ls[len('// Background: '):] + ";} " + elif ls.startswith('// Style: '): + Meta['Style'] += ls[len('// Style: '):] + ' ' else: Content += l + '\n' - Heading = ls.split(' ')[0].count('#') - if Heading > 0: - Titles += [ls] + if ls.startswith(('h1', 'h2', 'h3', 'h4', 'h5', 'h6')): + if not ls.startswith(("h1(class='NoTitle", 'h1(class="NoTitle')): + Titles += ['#'*int(ls[1]) + ls] return Content, Titles, Meta +def PugCompiler(c): + WriteFile('tmp.pug', c) + os.system('pug tmp.pug > /dev/null') + return ReadFile('tmp.html') + def PatchHTML(Template, Parts, Content, Titles, Meta): HTMLTitles = FormatTitles(Titles) @@ -76,12 +79,11 @@ def PatchHTML(Template, Parts, Content, Titles, Meta): for p in Parts: Template = Template.replace('[HTML:Part:{}]'.format(p), Parts[p]) - print(Template) return Template def MakeSite(Templates, Parts): Pages = [] - for File in Path('Pages').rglob('*.md'): + for File in Path('Pages').rglob('*.pug'): File = str(File)[len('Pages/'):] Content, Titles, Meta = PreProcessor('Pages/{}'.format(File)) @@ -90,15 +92,9 @@ def MakeSite(Templates, Parts): '[HTML:Page:CSS]', '{}{}.css'.format('../'*File.count('/'), Meta['Template'][:-5])) - print(Content, Titles, Meta) WriteFile( - 'public/{}.html'.format(File.rstrip('.md')), - PatchHTML( - Template, - Parts, - Markdown().convert(Content), - Titles, - Meta)) + 'public/{}.html'.format(File.rstrip('.pug')), + PatchHTML(Template, Parts, PugCompiler(Content), Titles, Meta)) def IgnoreFiles(dir, files): return [f for f in files if os.path.isfile(os.path.join(dir, f))] @@ -110,7 +106,6 @@ def Main(): ResetPublic() Templates = LoadFromDir('Templates') Parts = LoadFromDir('Parts') - print(Templates, Parts) CopyAssets() MakeSite(Templates, Parts) diff --git a/Source/Libs/markdown/__init__.py b/Source/Libs/markdown/__init__.py deleted file mode 100644 index e05af10..0000000 --- a/Source/Libs/markdown/__init__.py +++ /dev/null @@ -1,61 +0,0 @@ -""" -Python Markdown - -A Python implementation of John Gruber's Markdown. - -Documentation: https://python-markdown.github.io/ -GitHub: https://github.com/Python-Markdown/markdown/ -PyPI: https://pypi.org/project/Markdown/ - -Started by Manfred Stienstra (http://www.dwerg.net/). -Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org). -Currently maintained by Waylan Limberg (https://github.com/waylan), -Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser). - -Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later) -Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b) -Copyright 2004 Manfred Stienstra (the original version) - -License: BSD (see LICENSE.md for details). -""" - -import sys - -# TODO: Remove this check at some point in the future. -# (also remove flake8's 'ignore E402' comments below) -if sys.version_info[0] < 3: # pragma: no cover - raise ImportError('A recent version of Python 3 is required.') - -from .core import Markdown, markdown, markdownFromFile # noqa: E402 -from .util import PY37 # noqa: E402 -from .pep562 import Pep562 # noqa: E402 -from .__meta__ import __version__, __version_info__ # noqa: E402 -import warnings # noqa: E402 - -# For backward compatibility as some extensions expect it... -from .extensions import Extension # noqa - -__all__ = ['Markdown', 'markdown', 'markdownFromFile'] - -__deprecated__ = { - "version": ("__version__", __version__), - "version_info": ("__version_info__", __version_info__) -} - - -def __getattr__(name): - """Get attribute.""" - - deprecated = __deprecated__.get(name) - if deprecated: - warnings.warn( - "'{}' is deprecated. Use '{}' instead.".format(name, deprecated[0]), - category=DeprecationWarning, - stacklevel=(3 if PY37 else 4) - ) - return deprecated[1] - raise AttributeError("module '{}' has no attribute '{}'".format(__name__, name)) - - -if not PY37: - Pep562(__name__) diff --git a/Source/Libs/markdown/__main__.py b/Source/Libs/markdown/__main__.py deleted file mode 100644 index 0184008..0000000 --- a/Source/Libs/markdown/__main__.py +++ /dev/null @@ -1,151 +0,0 @@ -""" -Python Markdown - -A Python implementation of John Gruber's Markdown. - -Documentation: https://python-markdown.github.io/ -GitHub: https://github.com/Python-Markdown/markdown/ -PyPI: https://pypi.org/project/Markdown/ - -Started by Manfred Stienstra (http://www.dwerg.net/). -Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org). -Currently maintained by Waylan Limberg (https://github.com/waylan), -Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser). - -Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later) -Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b) -Copyright 2004 Manfred Stienstra (the original version) - -License: BSD (see LICENSE.md for details). -""" - -import sys -import optparse -import codecs -import warnings -import markdown -try: - # We use `unsafe_load` because users may need to pass in actual Python - # objects. As this is only available from the CLI, the user has much - # worse problems if an attacker can use this as an attach vector. - from yaml import unsafe_load as yaml_load -except ImportError: # pragma: no cover - try: - # Fall back to PyYAML <5.1 - from yaml import load as yaml_load - except ImportError: - # Fall back to JSON - from json import load as yaml_load - -import logging -from logging import DEBUG, WARNING, CRITICAL - -logger = logging.getLogger('MARKDOWN') - - -def parse_options(args=None, values=None): - """ - Define and parse `optparse` options for command-line usage. - """ - usage = """%prog [options] [INPUTFILE] - (STDIN is assumed if no INPUTFILE is given)""" - desc = "A Python implementation of John Gruber's Markdown. " \ - "https://Python-Markdown.github.io/" - ver = "%%prog %s" % markdown.__version__ - - parser = optparse.OptionParser(usage=usage, description=desc, version=ver) - parser.add_option("-f", "--file", dest="filename", default=None, - help="Write output to OUTPUT_FILE. Defaults to STDOUT.", - metavar="OUTPUT_FILE") - parser.add_option("-e", "--encoding", dest="encoding", - help="Encoding for input and output files.",) - parser.add_option("-o", "--output_format", dest="output_format", - default='xhtml', metavar="OUTPUT_FORMAT", - help="Use output format 'xhtml' (default) or 'html'.") - parser.add_option("-n", "--no_lazy_ol", dest="lazy_ol", - action='store_false', default=True, - help="Observe number of first item of ordered lists.") - parser.add_option("-x", "--extension", action="append", dest="extensions", - help="Load extension EXTENSION.", metavar="EXTENSION") - parser.add_option("-c", "--extension_configs", - dest="configfile", default=None, - help="Read extension configurations from CONFIG_FILE. " - "CONFIG_FILE must be of JSON or YAML format. YAML " - "format requires that a python YAML library be " - "installed. The parsed JSON or YAML must result in a " - "python dictionary which would be accepted by the " - "'extension_configs' keyword on the markdown.Markdown " - "class. The extensions must also be loaded with the " - "`--extension` option.", - metavar="CONFIG_FILE") - parser.add_option("-q", "--quiet", default=CRITICAL, - action="store_const", const=CRITICAL+10, dest="verbose", - help="Suppress all warnings.") - parser.add_option("-v", "--verbose", - action="store_const", const=WARNING, dest="verbose", - help="Print all warnings.") - parser.add_option("--noisy", - action="store_const", const=DEBUG, dest="verbose", - help="Print debug messages.") - - (options, args) = parser.parse_args(args, values) - - if len(args) == 0: - input_file = None - else: - input_file = args[0] - - if not options.extensions: - options.extensions = [] - - extension_configs = {} - if options.configfile: - with codecs.open( - options.configfile, mode="r", encoding=options.encoding - ) as fp: - try: - extension_configs = yaml_load(fp) - except Exception as e: - message = "Failed parsing extension config file: %s" % \ - options.configfile - e.args = (message,) + e.args[1:] - raise - - opts = { - 'input': input_file, - 'output': options.filename, - 'extensions': options.extensions, - 'extension_configs': extension_configs, - 'encoding': options.encoding, - 'output_format': options.output_format, - 'lazy_ol': options.lazy_ol - } - - return opts, options.verbose - - -def run(): # pragma: no cover - """Run Markdown from the command line.""" - - # Parse options and adjust logging level if necessary - options, logging_level = parse_options() - if not options: - sys.exit(2) - logger.setLevel(logging_level) - console_handler = logging.StreamHandler() - logger.addHandler(console_handler) - if logging_level <= WARNING: - # Ensure deprecation warnings get displayed - warnings.filterwarnings('default') - logging.captureWarnings(True) - warn_logger = logging.getLogger('py.warnings') - warn_logger.addHandler(console_handler) - - # Run - markdown.markdownFromFile(**options) - - -if __name__ == '__main__': # pragma: no cover - # Support running module as a commandline command. - # `python -m markdown [options] [args]`. - run() diff --git a/Source/Libs/markdown/__meta__.py b/Source/Libs/markdown/__meta__.py deleted file mode 100644 index 84884b6..0000000 --- a/Source/Libs/markdown/__meta__.py +++ /dev/null @@ -1,49 +0,0 @@ -""" -Python Markdown - -A Python implementation of John Gruber's Markdown. - -Documentation: https://python-markdown.github.io/ -GitHub: https://github.com/Python-Markdown/markdown/ -PyPI: https://pypi.org/project/Markdown/ - -Started by Manfred Stienstra (http://www.dwerg.net/). -Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org). -Currently maintained by Waylan Limberg (https://github.com/waylan), -Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser). - -Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later) -Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b) -Copyright 2004 Manfred Stienstra (the original version) - -License: BSD (see LICENSE.md for details). -""" - -# __version_info__ format: -# (major, minor, patch, dev/alpha/beta/rc/final, #) -# (1, 1, 2, 'dev', 0) => "1.1.2.dev0" -# (1, 1, 2, 'alpha', 1) => "1.1.2a1" -# (1, 2, 0, 'beta', 2) => "1.2b2" -# (1, 2, 0, 'rc', 4) => "1.2rc4" -# (1, 2, 0, 'final', 0) => "1.2" -__version_info__ = (3, 3, 7, 'final', 0) - - -def _get_version(version_info): - " Returns a PEP 440-compliant version number from version_info. " - assert len(version_info) == 5 - assert version_info[3] in ('dev', 'alpha', 'beta', 'rc', 'final') - - parts = 2 if version_info[2] == 0 else 3 - v = '.'.join(map(str, version_info[:parts])) - - if version_info[3] == 'dev': - v += '.dev' + str(version_info[4]) - elif version_info[3] != 'final': - mapping = {'alpha': 'a', 'beta': 'b', 'rc': 'rc'} - v += mapping[version_info[3]] + str(version_info[4]) - - return v - - -__version__ = _get_version(__version_info__) diff --git a/Source/Libs/markdown/__pycache__/__init__.cpython-310.pyc b/Source/Libs/markdown/__pycache__/__init__.cpython-310.pyc deleted file mode 100644 index 3598f4c8f41e8079da844adafa0d26df748db639..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1830 zcmZuyTW{Mo6c%M!mgP&6v>n!601nvD8fc^>U7Mj8hIL8WrAUfk?od=O0&UW^8cEbh zI-a`pW!>xc3--|NY5Plh-BbR;fL#t{t4_NL;4v@feCInC(!A4g4fuYs)L_*yj6e0_ z{8xj;moVh-AgB=-6j3uq0b=NhNzoO9){YIa%4*blj($Y6PVLxA>OnoR13PI14P?Mx zBX*Kz&@AUp>?WQSCZ9W6&YiL+P|S-jkZ{4WYRY4 zg6#Zi1{Z0UUiirfF0rQkhPBw`=LitYemQz8q%aM`J<8RDr}&VFW6Ec#>)yeOttiQ2 zmatS26@dmH;ro1&;(KB`VB)HLW#YN+E+0-`Y2L;Yr80Tj_p=gvJy~FT<48@W125vf zdoNO7PX|lY#nF%_{@nd~#mKMBec*buzck76Eb_P*`!#N{DYM{qc5#fjA zc%oR&PF_O+2W%Ee$`efpkang*K<45cCreDgctrA;r1&68Acx~QhR)Qv@Mon;@4CC2 zL`vkiKOw_OS~+|}8B}s|bGwI0O7XrVWQY%^lD!ppM20M8Qo^|aitY~2auJOu3U6&} zY_D%^Y<`9xPFPj`Dx3Ido?d+I#KW83cIhf63P4_m`1HC4x>?Fw|H~E_Z+Khlo8GO# zn|5z2kpdzd*hMLc@d*@hBvYWTIdS=lI!v)_kELN6_C>`-I1l8u=_X>i5fJ76V3H`W$U1MQY zv@3IMG1Ppj5ALgB!`Oi#?}Jdr3v_C{M9xYXo5mBJF`GQAdXfYbCDOWRAhJ@vlAAJvJM|5oKE8f!h}ei@}P_c{9%aq!04|{f5H>y^Py7yE<5ovE>*cWhz4@`5QSlsMk)-wEZ5}* zemZTi1`N3d!mU}zLS1Ac7ukP1^%W#OfbHHpMFWE4LmidOrRczB(b`WkE>uY~u!dYP zp))D!TIg~Y)>K7t8CGCtWuFQ7A2I@nDFNSMxNIbGQ5(3dYB4EVdIQ{&I0nU4Se0DF zyLzUhJ}2JSy3XhGDt)0VD?bKd8P4Ud<)BMu8@162a!|YGn0C!Z_CJnob=%1L_dk`! B8@>Pl diff --git a/Source/Libs/markdown/__pycache__/__meta__.cpython-310.pyc b/Source/Libs/markdown/__pycache__/__meta__.cpython-310.pyc deleted file mode 100644 index 6eba0c09acfb7ee19212885b84368cb7b3700f4d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1468 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 Kqc4uX{J#Nz^ImEI diff --git a/Source/Libs/markdown/__pycache__/core.cpython-310.pyc b/Source/Libs/markdown/__pycache__/core.cpython-310.pyc deleted file mode 100644 index e1c9c2262df86f9557ee498e06108c0cb9aef199..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12072 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 ND_UOvlR0xl{Xb?hrGo$f diff --git a/Source/Libs/markdown/__pycache__/inlinepatterns.cpython-310.pyc b/Source/Libs/markdown/__pycache__/inlinepatterns.cpython-310.pyc deleted file mode 100644 index 36311d0c82b26831c7b40ceaf9aebe41ab4ab9a5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25217 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 diff --git a/Source/Libs/markdown/__pycache__/pep562.cpython-310.pyc b/Source/Libs/markdown/__pycache__/pep562.cpython-310.pyc deleted file mode 100644 index 78221da6d28c8ec84916fed2264c7c37b1cd678f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8133 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 Cd5Ts5 diff --git a/Source/Libs/markdown/__pycache__/serializers.cpython-310.pyc b/Source/Libs/markdown/__pycache__/serializers.cpython-310.pyc deleted file mode 100644 index e20d0384f5c383aa578a729394cff64990316519..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3143 zcmb_eO>i7X74Gio`P-FN{|*TOuVf@VIQj|UCqaswl%pyVY~f&6QeNzu8f~}am1k$x zJ-wE+o;?tSTj0c%D(M6|z%i9W&TyhQaO*2o6uFX{F6O;nOL6R|a)90HH?Lp!>wfR+ z*Y9;cJX|60`z_ZSA3aaVKe6-VFM!S)(273-5k%0Gv}uD<krh=6Wy9@nRd&{`3tkc!TZ-FU7?i!x9 zvU`TNrB007Nnmm>n!9*;`r_rE+`jP3rTN$HTq)p8&Ek{j??mhk? z??Jl)tvCln!zmrml4w?veY))&vVHmijo5ncd^5?iT-OV!T1ndK$@;@-)vsUFdmUi= zQN&Gn6Zkcya%H^DT(uKzI1Za2jx+Emj*lovYK3J@eda7=54u{H?>}TOFmfa zBF z9D>k#EP&}v3-I>=QB_S2r~tnYS)T~U%BD(kz=R{XmAO`S-pX9zp{ykDlN*E+?P$Kq z_Ff*uqlTIL5a+ib(r=I*68Y=Lbne)lOWK{~W4dz8=TZ&KDll7m`N%WgFV^BWuU$7z z(cLh6GtgfqX zgH7r@k~)%UBr`}Z06C^J$KKriZmyRNt@PZv3okD%tMB0OS7B8Rw0W3vHfXyr+Pn1s2JNX3TRsDz@M{24-vjH^B_wB}GVx_pY#M8VM)3lWXQ4Tb zr3DynIzUc-SQk$7%D*AYlysZ)9iX(&OGmRVQ4=NP8ts&PpPsX~CHn`Aa2Tn>2>a5= zUrI+17LtidRts-fN#7!Og1(D4u5?Sb?@)4oA^Wo+eNTI^p1;pjqja_@)Ewv6 z?0F*iJ*M8X5?B)@P-9#HneI00dwnP4IusseJ+Q(ro#(7=B|WGdlD=R1>i4=@`uh}l z*n0`If_?~l1$M99G5{X~=!drdt1^Us2&)d2A!vnVP=;W^?@A^D`z0bQ!vO;KQ`oa| zFpP6B^MWsF>8U?}w5n?*ST&djxrwygu`=Khvw$ciU-JygoBq%ox`eRhY84YSTc(j8`;OtD}uu=)F|-*veuwZX8%t zVE`+PP@DG@LwiO}3y8FlZYo${3!P+;dR?617M--EP365L?LIDSZcEE{0XyYNOQA6G z6&6l~slm8uz9W^X{PgCvtMS^k>o;#fy-QkYR59+30#(B$JQ)TWKSDJ$%uv}F#sXP% zAnV6n)l$n=e9zDag9MM&(q?(}W8gHj;x-Tu@_9hVA2)7uJZ5+9ROmQ+kp>L2KDU{_ zLOoUiNx+~2uyMwr5=>H*pkBqMy&7<%u;w_{PTWa-A4n77I{9&6uw8*xyaWWogLG-( zy+b~rTcpoQhF4wF5=v-E@j}9r0;Go&!h@|F0xu{p>2zc!>=uS^=dBE$4_xKBw$K=k z6->PWt3Tzwsm2yZdtiS;6EM{bfs3AXLcIgsXEOAOy`N9Qs3u?=T(^chdjK3u{S_W?y9}f@SdrujqFx7nm?Q;ZblAgK0`WcMlUa!DcT}5&Y z$M@^^tm)S4GfnJKsd3t*6x3r{>gZ9os=8*Ls$FH_R^ah#-S94mYd z7{8gfJFQe2`nH-yfm29rLVUN{XWQUyfiD9a&V%T-VSun&Hx=6TgsrCrn_#7#i*71c n@v;|lK!(0M36Tf!#cGT}B;ql#M*$DKU_6)z#wxD`)gk_0ag@c( diff --git a/Source/Libs/markdown/__pycache__/treeprocessors.cpython-310.pyc b/Source/Libs/markdown/__pycache__/treeprocessors.cpython-310.pyc deleted file mode 100644 index 3337ab021a1d4d225de188e41a6f0c8a00e36227..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11875 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

k-W7q7EU6U=2g^>}^qbysk$=22v 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 W3z@=j;jx0Qe9C;uDB$Ae-Twy9D&w>O diff --git a/Source/Libs/markdown/blockparser.py b/Source/Libs/markdown/blockparser.py deleted file mode 100644 index 39219fd..0000000 --- a/Source/Libs/markdown/blockparser.py +++ /dev/null @@ -1,125 +0,0 @@ -""" -Python Markdown - -A Python implementation of John Gruber's Markdown. - -Documentation: https://python-markdown.github.io/ -GitHub: https://github.com/Python-Markdown/markdown/ -PyPI: https://pypi.org/project/Markdown/ - -Started by Manfred Stienstra (http://www.dwerg.net/). -Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org). -Currently maintained by Waylan Limberg (https://github.com/waylan), -Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser). - -Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later) -Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b) -Copyright 2004 Manfred Stienstra (the original version) - -License: BSD (see LICENSE.md for details). -""" - -import xml.etree.ElementTree as etree -from . import util - - -class State(list): - """ Track the current and nested state of the parser. - - This utility class is used to track the state of the BlockParser and - support multiple levels if nesting. It's just a simple API wrapped around - a list. Each time a state is set, that state is appended to the end of the - list. Each time a state is reset, that state is removed from the end of - the list. - - Therefore, each time a state is set for a nested block, that state must be - reset when we back out of that level of nesting or the state could be - corrupted. - - While all the methods of a list object are available, only the three - defined below need be used. - - """ - - def set(self, state): - """ Set a new state. """ - self.append(state) - - def reset(self): - """ Step back one step in nested state. """ - self.pop() - - def isstate(self, state): - """ Test that top (current) level is of given state. """ - if len(self): - return self[-1] == state - else: - return False - - -class BlockParser: - """ Parse Markdown blocks into an ElementTree object. - - A wrapper class that stitches the various BlockProcessors together, - looping through them and creating an ElementTree object. - """ - - def __init__(self, md): - self.blockprocessors = util.Registry() - self.state = State() - self.md = md - - @property - @util.deprecated("Use 'md' instead.") - def markdown(self): - # TODO: remove this later - return self.md - - def parseDocument(self, lines): - """ Parse a markdown document into an ElementTree. - - Given a list of lines, an ElementTree object (not just a parent - Element) is created and the root element is passed to the parser - as the parent. The ElementTree object is returned. - - This should only be called on an entire document, not pieces. - - """ - # Create a ElementTree from the lines - self.root = etree.Element(self.md.doc_tag) - self.parseChunk(self.root, '\n'.join(lines)) - return etree.ElementTree(self.root) - - def parseChunk(self, parent, text): - """ Parse a chunk of markdown text and attach to given etree node. - - While the ``text`` argument is generally assumed to contain multiple - blocks which will be split on blank lines, it could contain only one - block. Generally, this method would be called by extensions when - block parsing is required. - - The ``parent`` etree Element passed in is altered in place. - Nothing is returned. - - """ - self.parseBlocks(parent, text.split('\n\n')) - - def parseBlocks(self, parent, blocks): - """ Process blocks of markdown text and attach to given etree node. - - Given a list of ``blocks``, each blockprocessor is stepped through - until there are no blocks left. While an extension could potentially - call this method directly, it's generally expected to be used - internally. - - This is a public method as an extension may need to add/alter - additional BlockProcessors which call this method to recursively - parse a nested block. - - """ - while blocks: - for processor in self.blockprocessors: - if processor.test(parent, blocks[0]): - if processor.run(parent, blocks) is not False: - # run returns True or None - break diff --git a/Source/Libs/markdown/blockprocessors.py b/Source/Libs/markdown/blockprocessors.py deleted file mode 100644 index 3d0ff86..0000000 --- a/Source/Libs/markdown/blockprocessors.py +++ /dev/null @@ -1,623 +0,0 @@ -""" -Python Markdown - -A Python implementation of John Gruber's Markdown. - -Documentation: https://python-markdown.github.io/ -GitHub: https://github.com/Python-Markdown/markdown/ -PyPI: https://pypi.org/project/Markdown/ - -Started by Manfred Stienstra (http://www.dwerg.net/). -Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org). -Currently maintained by Waylan Limberg (https://github.com/waylan), -Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser). - -Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later) -Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b) -Copyright 2004 Manfred Stienstra (the original version) - -License: BSD (see LICENSE.md for details). - -CORE MARKDOWN BLOCKPARSER -=========================================================================== - -This parser handles basic parsing of Markdown blocks. It doesn't concern -itself with inline elements such as **bold** or *italics*, but rather just -catches blocks, lists, quotes, etc. - -The BlockParser is made up of a bunch of BlockProcessors, each handling a -different type of block. Extensions may add/replace/remove BlockProcessors -as they need to alter how markdown blocks are parsed. -""" - -import logging -import re -import xml.etree.ElementTree as etree -from . import util -from .blockparser import BlockParser - -logger = logging.getLogger('MARKDOWN') - - -def build_block_parser(md, **kwargs): - """ Build the default block parser used by Markdown. """ - parser = BlockParser(md) - parser.blockprocessors.register(EmptyBlockProcessor(parser), 'empty', 100) - parser.blockprocessors.register(ListIndentProcessor(parser), 'indent', 90) - parser.blockprocessors.register(CodeBlockProcessor(parser), 'code', 80) - parser.blockprocessors.register(HashHeaderProcessor(parser), 'hashheader', 70) - parser.blockprocessors.register(SetextHeaderProcessor(parser), 'setextheader', 60) - parser.blockprocessors.register(HRProcessor(parser), 'hr', 50) - parser.blockprocessors.register(OListProcessor(parser), 'olist', 40) - parser.blockprocessors.register(UListProcessor(parser), 'ulist', 30) - parser.blockprocessors.register(BlockQuoteProcessor(parser), 'quote', 20) - parser.blockprocessors.register(ReferenceProcessor(parser), 'reference', 15) - parser.blockprocessors.register(ParagraphProcessor(parser), 'paragraph', 10) - return parser - - -class BlockProcessor: - """ Base class for block processors. - - Each subclass will provide the methods below to work with the source and - tree. Each processor will need to define it's own ``test`` and ``run`` - methods. The ``test`` method should return True or False, to indicate - whether the current block should be processed by this processor. If the - test passes, the parser will call the processors ``run`` method. - - """ - - def __init__(self, parser): - self.parser = parser - self.tab_length = parser.md.tab_length - - def lastChild(self, parent): - """ Return the last child of an etree element. """ - if len(parent): - return parent[-1] - else: - return None - - def detab(self, text, length=None): - """ Remove a tab from the front of each line of the given text. """ - if length is None: - length = self.tab_length - newtext = [] - lines = text.split('\n') - for line in lines: - if line.startswith(' ' * length): - newtext.append(line[length:]) - elif not line.strip(): - newtext.append('') - else: - break - return '\n'.join(newtext), '\n'.join(lines[len(newtext):]) - - def looseDetab(self, text, level=1): - """ Remove a tab from front of lines but allowing dedented lines. """ - lines = text.split('\n') - for i in range(len(lines)): - if lines[i].startswith(' '*self.tab_length*level): - lines[i] = lines[i][self.tab_length*level:] - return '\n'.join(lines) - - def test(self, parent, block): - """ Test for block type. Must be overridden by subclasses. - - As the parser loops through processors, it will call the ``test`` - method on each to determine if the given block of text is of that - type. This method must return a boolean ``True`` or ``False``. The - actual method of testing is left to the needs of that particular - block type. It could be as simple as ``block.startswith(some_string)`` - or a complex regular expression. As the block type may be different - depending on the parent of the block (i.e. inside a list), the parent - etree element is also provided and may be used as part of the test. - - Keywords: - - * ``parent``: A etree element which will be the parent of the block. - * ``block``: A block of text from the source which has been split at - blank lines. - """ - pass # pragma: no cover - - def run(self, parent, blocks): - """ Run processor. Must be overridden by subclasses. - - When the parser determines the appropriate type of a block, the parser - will call the corresponding processor's ``run`` method. This method - should parse the individual lines of the block and append them to - the etree. - - Note that both the ``parent`` and ``etree`` keywords are pointers - to instances of the objects which should be edited in place. Each - processor must make changes to the existing objects as there is no - mechanism to return new/different objects to replace them. - - This means that this method should be adding SubElements or adding text - to the parent, and should remove (``pop``) or add (``insert``) items to - the list of blocks. - - Keywords: - - * ``parent``: A etree element which is the parent of the current block. - * ``blocks``: A list of all remaining blocks of the document. - """ - pass # pragma: no cover - - -class ListIndentProcessor(BlockProcessor): - """ Process children of list items. - - Example: - * a list item - process this part - - or this part - - """ - - ITEM_TYPES = ['li'] - LIST_TYPES = ['ul', 'ol'] - - def __init__(self, *args): - super().__init__(*args) - self.INDENT_RE = re.compile(r'^(([ ]{%s})+)' % self.tab_length) - - def test(self, parent, block): - return block.startswith(' '*self.tab_length) and \ - not self.parser.state.isstate('detabbed') and \ - (parent.tag in self.ITEM_TYPES or - (len(parent) and parent[-1] is not None and - (parent[-1].tag in self.LIST_TYPES))) - - def run(self, parent, blocks): - block = blocks.pop(0) - level, sibling = self.get_level(parent, block) - block = self.looseDetab(block, level) - - self.parser.state.set('detabbed') - if parent.tag in self.ITEM_TYPES: - # It's possible that this parent has a 'ul' or 'ol' child list - # with a member. If that is the case, then that should be the - # parent. This is intended to catch the edge case of an indented - # list whose first member was parsed previous to this point - # see OListProcessor - if len(parent) and parent[-1].tag in self.LIST_TYPES: - self.parser.parseBlocks(parent[-1], [block]) - else: - # The parent is already a li. Just parse the child block. - self.parser.parseBlocks(parent, [block]) - elif sibling.tag in self.ITEM_TYPES: - # The sibling is a li. Use it as parent. - self.parser.parseBlocks(sibling, [block]) - elif len(sibling) and sibling[-1].tag in self.ITEM_TYPES: - # The parent is a list (``ol`` or ``ul``) which has children. - # Assume the last child li is the parent of this block. - if sibling[-1].text: - # If the parent li has text, that text needs to be moved to a p - # The p must be 'inserted' at beginning of list in the event - # that other children already exist i.e.; a nested sublist. - p = etree.Element('p') - p.text = sibling[-1].text - sibling[-1].text = '' - sibling[-1].insert(0, p) - self.parser.parseChunk(sibling[-1], block) - else: - self.create_item(sibling, block) - self.parser.state.reset() - - def create_item(self, parent, block): - """ Create a new li and parse the block with it as the parent. """ - li = etree.SubElement(parent, 'li') - self.parser.parseBlocks(li, [block]) - - def get_level(self, parent, block): - """ Get level of indent based on list level. """ - # Get indent level - m = self.INDENT_RE.match(block) - if m: - indent_level = len(m.group(1))/self.tab_length - else: - indent_level = 0 - if self.parser.state.isstate('list'): - # We're in a tightlist - so we already are at correct parent. - level = 1 - else: - # We're in a looselist - so we need to find parent. - level = 0 - # Step through children of tree to find matching indent level. - while indent_level > level: - child = self.lastChild(parent) - if (child is not None and - (child.tag in self.LIST_TYPES or child.tag in self.ITEM_TYPES)): - if child.tag in self.LIST_TYPES: - level += 1 - parent = child - else: - # No more child levels. If we're short of indent_level, - # we have a code block. So we stop here. - break - return level, parent - - -class CodeBlockProcessor(BlockProcessor): - """ Process code blocks. """ - - def test(self, parent, block): - return block.startswith(' '*self.tab_length) - - def run(self, parent, blocks): - sibling = self.lastChild(parent) - block = blocks.pop(0) - theRest = '' - if (sibling is not None and sibling.tag == "pre" and - len(sibling) and sibling[0].tag == "code"): - # The previous block was a code block. As blank lines do not start - # new code blocks, append this block to the previous, adding back - # linebreaks removed from the split into a list. - code = sibling[0] - block, theRest = self.detab(block) - code.text = util.AtomicString( - '{}\n{}\n'.format(code.text, util.code_escape(block.rstrip())) - ) - else: - # This is a new codeblock. Create the elements and insert text. - pre = etree.SubElement(parent, 'pre') - code = etree.SubElement(pre, 'code') - block, theRest = self.detab(block) - code.text = util.AtomicString('%s\n' % util.code_escape(block.rstrip())) - if theRest: - # This block contained unindented line(s) after the first indented - # line. Insert these lines as the first block of the master blocks - # list for future processing. - blocks.insert(0, theRest) - - -class BlockQuoteProcessor(BlockProcessor): - - RE = re.compile(r'(^|\n)[ ]{0,3}>[ ]?(.*)') - - def test(self, parent, block): - return bool(self.RE.search(block)) and not util.nearing_recursion_limit() - - def run(self, parent, blocks): - block = blocks.pop(0) - m = self.RE.search(block) - if m: - before = block[:m.start()] # Lines before blockquote - # Pass lines before blockquote in recursively for parsing first. - self.parser.parseBlocks(parent, [before]) - # Remove ``> `` from beginning of each line. - block = '\n'.join( - [self.clean(line) for line in block[m.start():].split('\n')] - ) - sibling = self.lastChild(parent) - if sibling is not None and sibling.tag == "blockquote": - # Previous block was a blockquote so set that as this blocks parent - quote = sibling - else: - # This is a new blockquote. Create a new parent element. - quote = etree.SubElement(parent, 'blockquote') - # Recursively parse block with blockquote as parent. - # change parser state so blockquotes embedded in lists use p tags - self.parser.state.set('blockquote') - self.parser.parseChunk(quote, block) - self.parser.state.reset() - - def clean(self, line): - """ Remove ``>`` from beginning of a line. """ - m = self.RE.match(line) - if line.strip() == ">": - return "" - elif m: - return m.group(2) - else: - return line - - -class OListProcessor(BlockProcessor): - """ Process ordered list blocks. """ - - TAG = 'ol' - # The integer (python string) with which the lists starts (default=1) - # Eg: If list is initialized as) - # 3. Item - # The ol tag will get starts="3" attribute - STARTSWITH = '1' - # Lazy ol - ignore startswith - LAZY_OL = True - # List of allowed sibling tags. - SIBLING_TAGS = ['ol', 'ul'] - - def __init__(self, parser): - super().__init__(parser) - # Detect an item (``1. item``). ``group(1)`` contains contents of item. - self.RE = re.compile(r'^[ ]{0,%d}\d+\.[ ]+(.*)' % (self.tab_length - 1)) - # Detect items on secondary lines. they can be of either list type. - self.CHILD_RE = re.compile(r'^[ ]{0,%d}((\d+\.)|[*+-])[ ]+(.*)' % - (self.tab_length - 1)) - # Detect indented (nested) items of either type - self.INDENT_RE = re.compile(r'^[ ]{%d,%d}((\d+\.)|[*+-])[ ]+.*' % - (self.tab_length, self.tab_length * 2 - 1)) - - def test(self, parent, block): - return bool(self.RE.match(block)) - - def run(self, parent, blocks): - # Check fr multiple items in one block. - items = self.get_items(blocks.pop(0)) - sibling = self.lastChild(parent) - - if sibling is not None and sibling.tag in self.SIBLING_TAGS: - # Previous block was a list item, so set that as parent - lst = sibling - # make sure previous item is in a p- if the item has text, - # then it isn't in a p - if lst[-1].text: - # since it's possible there are other children for this - # sibling, we can't just SubElement the p, we need to - # insert it as the first item. - p = etree.Element('p') - p.text = lst[-1].text - lst[-1].text = '' - lst[-1].insert(0, p) - # if the last item has a tail, then the tail needs to be put in a p - # likely only when a header is not followed by a blank line - lch = self.lastChild(lst[-1]) - if lch is not None and lch.tail: - p = etree.SubElement(lst[-1], 'p') - p.text = lch.tail.lstrip() - lch.tail = '' - - # parse first block differently as it gets wrapped in a p. - li = etree.SubElement(lst, 'li') - self.parser.state.set('looselist') - firstitem = items.pop(0) - self.parser.parseBlocks(li, [firstitem]) - self.parser.state.reset() - elif parent.tag in ['ol', 'ul']: - # this catches the edge case of a multi-item indented list whose - # first item is in a blank parent-list item: - # * * subitem1 - # * subitem2 - # see also ListIndentProcessor - lst = parent - else: - # This is a new list so create parent with appropriate tag. - lst = etree.SubElement(parent, self.TAG) - # Check if a custom start integer is set - if not self.LAZY_OL and self.STARTSWITH != '1': - lst.attrib['start'] = self.STARTSWITH - - self.parser.state.set('list') - # Loop through items in block, recursively parsing each with the - # appropriate parent. - for item in items: - if item.startswith(' '*self.tab_length): - # Item is indented. Parse with last item as parent - self.parser.parseBlocks(lst[-1], [item]) - else: - # New item. Create li and parse with it as parent - li = etree.SubElement(lst, 'li') - self.parser.parseBlocks(li, [item]) - self.parser.state.reset() - - def get_items(self, block): - """ Break a block into list items. """ - items = [] - for line in block.split('\n'): - m = self.CHILD_RE.match(line) - if m: - # This is a new list item - # Check first item for the start index - if not items and self.TAG == 'ol': - # Detect the integer value of first list item - INTEGER_RE = re.compile(r'(\d+)') - self.STARTSWITH = INTEGER_RE.match(m.group(1)).group() - # Append to the list - items.append(m.group(3)) - elif self.INDENT_RE.match(line): - # This is an indented (possibly nested) item. - if items[-1].startswith(' '*self.tab_length): - # Previous item was indented. Append to that item. - items[-1] = '{}\n{}'.format(items[-1], line) - else: - items.append(line) - else: - # This is another line of previous item. Append to that item. - items[-1] = '{}\n{}'.format(items[-1], line) - return items - - -class UListProcessor(OListProcessor): - """ Process unordered list blocks. """ - - TAG = 'ul' - - def __init__(self, parser): - super().__init__(parser) - # Detect an item (``1. item``). ``group(1)`` contains contents of item. - self.RE = re.compile(r'^[ ]{0,%d}[*+-][ ]+(.*)' % (self.tab_length - 1)) - - -class HashHeaderProcessor(BlockProcessor): - """ Process Hash Headers. """ - - # Detect a header at start of any line in block - RE = re.compile(r'(?:^|\n)(?P#{1,6})(?P

(?:\\.|[^\\])*?)#*(?:\n|$)') - - def test(self, parent, block): - return bool(self.RE.search(block)) - - def run(self, parent, blocks): - block = blocks.pop(0) - m = self.RE.search(block) - if m: - before = block[:m.start()] # All lines before header - after = block[m.end():] # All lines after header - if before: - # As the header was not the first line of the block and the - # lines before the header must be parsed first, - # recursively parse this lines as a block. - self.parser.parseBlocks(parent, [before]) - # Create header using named groups from RE - h = etree.SubElement(parent, 'h%d' % len(m.group('level'))) - h.text = m.group('header').strip() - if after: - # Insert remaining lines as first block for future parsing. - blocks.insert(0, after) - else: # pragma: no cover - # This should never happen, but just in case... - logger.warn("We've got a problem header: %r" % block) - - -class SetextHeaderProcessor(BlockProcessor): - """ Process Setext-style Headers. """ - - # Detect Setext-style header. Must be first 2 lines of block. - RE = re.compile(r'^.*?\n[=-]+[ ]*(\n|$)', re.MULTILINE) - - def test(self, parent, block): - return bool(self.RE.match(block)) - - def run(self, parent, blocks): - lines = blocks.pop(0).split('\n') - # Determine level. ``=`` is 1 and ``-`` is 2. - if lines[1].startswith('='): - level = 1 - else: - level = 2 - h = etree.SubElement(parent, 'h%d' % level) - h.text = lines[0].strip() - if len(lines) > 2: - # Block contains additional lines. Add to master blocks for later. - blocks.insert(0, '\n'.join(lines[2:])) - - -class HRProcessor(BlockProcessor): - """ Process Horizontal Rules. """ - - # Python's re module doesn't officially support atomic grouping. However you can fake it. - # See https://stackoverflow.com/a/13577411/866026 - RE = r'^[ ]{0,3}(?=(?P(-+[ ]{0,2}){3,}|(_+[ ]{0,2}){3,}|(\*+[ ]{0,2}){3,}))(?P=atomicgroup)[ ]*$' - # Detect hr on any line of a block. - SEARCH_RE = re.compile(RE, re.MULTILINE) - - def test(self, parent, block): - m = self.SEARCH_RE.search(block) - if m: - # Save match object on class instance so we can use it later. - self.match = m - return True - return False - - def run(self, parent, blocks): - block = blocks.pop(0) - match = self.match - # Check for lines in block before hr. - prelines = block[:match.start()].rstrip('\n') - if prelines: - # Recursively parse lines before hr so they get parsed first. - self.parser.parseBlocks(parent, [prelines]) - # create hr - etree.SubElement(parent, 'hr') - # check for lines in block after hr. - postlines = block[match.end():].lstrip('\n') - if postlines: - # Add lines after hr to master blocks for later parsing. - blocks.insert(0, postlines) - - -class EmptyBlockProcessor(BlockProcessor): - """ Process blocks that are empty or start with an empty line. """ - - def test(self, parent, block): - return not block or block.startswith('\n') - - def run(self, parent, blocks): - block = blocks.pop(0) - filler = '\n\n' - if block: - # Starts with empty line - # Only replace a single line. - filler = '\n' - # Save the rest for later. - theRest = block[1:] - if theRest: - # Add remaining lines to master blocks for later. - blocks.insert(0, theRest) - sibling = self.lastChild(parent) - if (sibling is not None and sibling.tag == 'pre' and - len(sibling) and sibling[0].tag == 'code'): - # Last block is a codeblock. Append to preserve whitespace. - sibling[0].text = util.AtomicString( - '{}{}'.format(sibling[0].text, filler) - ) - - -class ReferenceProcessor(BlockProcessor): - """ Process link references. """ - RE = re.compile( - r'^[ ]{0,3}\[([^\[\]]*)\]:[ ]*\n?[ ]*([^\s]+)[ ]*(?:\n[ ]*)?((["\'])(.*)\4[ ]*|\((.*)\)[ ]*)?$', re.MULTILINE - ) - - def test(self, parent, block): - return True - - def run(self, parent, blocks): - block = blocks.pop(0) - m = self.RE.search(block) - if m: - id = m.group(1).strip().lower() - link = m.group(2).lstrip('<').rstrip('>') - title = m.group(5) or m.group(6) - self.parser.md.references[id] = (link, title) - if block[m.end():].strip(): - # Add any content after match back to blocks as separate block - blocks.insert(0, block[m.end():].lstrip('\n')) - if block[:m.start()].strip(): - # Add any content before match back to blocks as separate block - blocks.insert(0, block[:m.start()].rstrip('\n')) - return True - # No match. Restore block. - blocks.insert(0, block) - return False - - -class ParagraphProcessor(BlockProcessor): - """ Process Paragraph blocks. """ - - def test(self, parent, block): - return True - - def run(self, parent, blocks): - block = blocks.pop(0) - if block.strip(): - # Not a blank block. Add to parent, otherwise throw it away. - if self.parser.state.isstate('list'): - # The parent is a tight-list. - # - # Check for any children. This will likely only happen in a - # tight-list when a header isn't followed by a blank line. - # For example: - # - # * # Header - # Line 2 of list item - not part of header. - sibling = self.lastChild(parent) - if sibling is not None: - # Insetrt after sibling. - if sibling.tail: - sibling.tail = '{}\n{}'.format(sibling.tail, block) - else: - sibling.tail = '\n%s' % block - else: - # Append to parent.text - if parent.text: - parent.text = '{}\n{}'.format(parent.text, block) - else: - parent.text = block.lstrip() - else: - # Create a regular paragraph - p = etree.SubElement(parent, 'p') - p.text = block.lstrip() diff --git a/Source/Libs/markdown/core.py b/Source/Libs/markdown/core.py deleted file mode 100644 index d8c8196..0000000 --- a/Source/Libs/markdown/core.py +++ /dev/null @@ -1,407 +0,0 @@ -""" -Python Markdown - -A Python implementation of John Gruber's Markdown. - -Documentation: https://python-markdown.github.io/ -GitHub: https://github.com/Python-Markdown/markdown/ -PyPI: https://pypi.org/project/Markdown/ - -Started by Manfred Stienstra (http://www.dwerg.net/). -Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org). -Currently maintained by Waylan Limberg (https://github.com/waylan), -Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser). - -Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later) -Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b) -Copyright 2004 Manfred Stienstra (the original version) - -License: BSD (see LICENSE.md for details). -""" - -import codecs -import sys -import logging -import importlib -from . import util -from .preprocessors import build_preprocessors -from .blockprocessors import build_block_parser -from .treeprocessors import build_treeprocessors -from .inlinepatterns import build_inlinepatterns -from .postprocessors import build_postprocessors -from .extensions import Extension -from .serializers import to_html_string, to_xhtml_string - -__all__ = ['Markdown', 'markdown', 'markdownFromFile'] - - -logger = logging.getLogger('MARKDOWN') - - -class Markdown: - """Convert Markdown to HTML.""" - - doc_tag = "div" # Element used to wrap document - later removed - - output_formats = { - 'html': to_html_string, - 'xhtml': to_xhtml_string, - } - - def __init__(self, **kwargs): - """ - Creates a new Markdown instance. - - Keyword arguments: - - * extensions: A list of extensions. - If an item is an instance of a subclass of `markdown.extension.Extension`, the instance will be used - as-is. If an item is of type string, first an entry point will be loaded. If that fails, the string is - assumed to use Python dot notation (`path.to.module:ClassName`) to load a markdown.Extension subclass. If - no class is specified, then a `makeExtension` function is called within the specified module. - * extension_configs: Configuration settings for extensions. - * output_format: Format of output. Supported formats are: - * "xhtml": Outputs XHTML style tags. Default. - * "html": Outputs HTML style tags. - * tab_length: Length of tabs in the source. Default: 4 - - """ - - self.tab_length = kwargs.get('tab_length', 4) - - self.ESCAPED_CHARS = ['\\', '`', '*', '_', '{', '}', '[', ']', - '(', ')', '>', '#', '+', '-', '.', '!'] - - self.block_level_elements = [ - # Elements which are invalid to wrap in a `

` tag. - # See https://w3c.github.io/html/grouping-content.html#the-p-element - 'address', 'article', 'aside', 'blockquote', 'details', 'div', 'dl', - 'fieldset', 'figcaption', 'figure', 'footer', 'form', 'h1', 'h2', 'h3', - 'h4', 'h5', 'h6', 'header', 'hgroup', 'hr', 'main', 'menu', 'nav', 'ol', - 'p', 'pre', 'section', 'table', 'ul', - # Other elements which Markdown should not be mucking up the contents of. - 'canvas', 'colgroup', 'dd', 'body', 'dt', 'group', 'iframe', 'li', 'legend', - 'math', 'map', 'noscript', 'output', 'object', 'option', 'progress', 'script', - 'style', 'summary', 'tbody', 'td', 'textarea', 'tfoot', 'th', 'thead', 'tr', 'video' - ] - - self.registeredExtensions = [] - self.docType = "" - self.stripTopLevelTags = True - - self.build_parser() - - self.references = {} - self.htmlStash = util.HtmlStash() - self.registerExtensions(extensions=kwargs.get('extensions', []), - configs=kwargs.get('extension_configs', {})) - self.set_output_format(kwargs.get('output_format', 'xhtml')) - self.reset() - - def build_parser(self): - """ Build the parser from the various parts. """ - self.preprocessors = build_preprocessors(self) - self.parser = build_block_parser(self) - self.inlinePatterns = build_inlinepatterns(self) - self.treeprocessors = build_treeprocessors(self) - self.postprocessors = build_postprocessors(self) - return self - - def registerExtensions(self, extensions, configs): - """ - Register extensions with this instance of Markdown. - - Keyword arguments: - - * extensions: A list of extensions, which can either - be strings or objects. - * configs: A dictionary mapping extension names to config options. - - """ - for ext in extensions: - if isinstance(ext, str): - ext = self.build_extension(ext, configs.get(ext, {})) - if isinstance(ext, Extension): - ext._extendMarkdown(self) - logger.debug( - 'Successfully loaded extension "%s.%s".' - % (ext.__class__.__module__, ext.__class__.__name__) - ) - elif ext is not None: - raise TypeError( - 'Extension "{}.{}" must be of type: "{}.{}"'.format( - ext.__class__.__module__, ext.__class__.__name__, - Extension.__module__, Extension.__name__ - ) - ) - return self - - def build_extension(self, ext_name, configs): - """ - Build extension from a string name, then return an instance. - - First attempt to load an entry point. The string name must be registered as an entry point in the - `markdown.extensions` group which points to a subclass of the `markdown.extensions.Extension` class. - If multiple distributions have registered the same name, the first one found is returned. - - If no entry point is found, assume dot notation (`path.to.module:ClassName`). Load the specified class and - return an instance. If no class is specified, import the module and call a `makeExtension` function and return - the Extension instance returned by that function. - """ - configs = dict(configs) - - entry_points = [ep for ep in util.INSTALLED_EXTENSIONS if ep.name == ext_name] - if entry_points: - ext = entry_points[0].load() - return ext(**configs) - - # Get class name (if provided): `path.to.module:ClassName` - ext_name, class_name = ext_name.split(':', 1) if ':' in ext_name else (ext_name, '') - - try: - module = importlib.import_module(ext_name) - logger.debug( - 'Successfully imported extension module "%s".' % ext_name - ) - except ImportError as e: - message = 'Failed loading extension "%s".' % ext_name - e.args = (message,) + e.args[1:] - raise - - if class_name: - # Load given class name from module. - return getattr(module, class_name)(**configs) - else: - # Expect makeExtension() function to return a class. - try: - return module.makeExtension(**configs) - except AttributeError as e: - message = e.args[0] - message = "Failed to initiate extension " \ - "'%s': %s" % (ext_name, message) - e.args = (message,) + e.args[1:] - raise - - def registerExtension(self, extension): - """ This gets called by the extension """ - self.registeredExtensions.append(extension) - return self - - def reset(self): - """ - Resets all state variables so that we can start with a new text. - """ - self.htmlStash.reset() - self.references.clear() - - for extension in self.registeredExtensions: - if hasattr(extension, 'reset'): - extension.reset() - - return self - - def set_output_format(self, format): - """ Set the output format for the class instance. """ - self.output_format = format.lower().rstrip('145') # ignore num - try: - self.serializer = self.output_formats[self.output_format] - except KeyError as e: - valid_formats = list(self.output_formats.keys()) - valid_formats.sort() - message = 'Invalid Output Format: "%s". Use one of %s.' \ - % (self.output_format, - '"' + '", "'.join(valid_formats) + '"') - e.args = (message,) + e.args[1:] - raise - return self - - def is_block_level(self, tag): - """Check if the tag is a block level HTML tag.""" - if isinstance(tag, str): - return tag.lower().rstrip('/') in self.block_level_elements - # Some ElementTree tags are not strings, so return False. - return False - - def convert(self, source): - """ - Convert markdown to serialized XHTML or HTML. - - Keyword arguments: - - * source: Source text as a Unicode string. - - Markdown processing takes place in five steps: - - 1. A bunch of "preprocessors" munge the input text. - 2. BlockParser() parses the high-level structural elements of the - pre-processed text into an ElementTree. - 3. A bunch of "treeprocessors" are run against the ElementTree. One - such treeprocessor runs InlinePatterns against the ElementTree, - detecting inline markup. - 4. Some post-processors are run against the text after the ElementTree - has been serialized into text. - 5. The output is written to a string. - - """ - - # Fixup the source text - if not source.strip(): - return '' # a blank unicode string - - try: - source = str(source) - except UnicodeDecodeError as e: # pragma: no cover - # Customise error message while maintaining original trackback - e.reason += '. -- Note: Markdown only accepts unicode input!' - raise - - # Split into lines and run the line preprocessors. - self.lines = source.split("\n") - for prep in self.preprocessors: - self.lines = prep.run(self.lines) - - # Parse the high-level elements. - root = self.parser.parseDocument(self.lines).getroot() - - # Run the tree-processors - for treeprocessor in self.treeprocessors: - newRoot = treeprocessor.run(root) - if newRoot is not None: - root = newRoot - - # Serialize _properly_. Strip top-level tags. - output = self.serializer(root) - if self.stripTopLevelTags: - try: - start = output.index( - '<%s>' % self.doc_tag) + len(self.doc_tag) + 2 - end = output.rindex('' % self.doc_tag) - output = output[start:end].strip() - except ValueError as e: # pragma: no cover - if output.strip().endswith('<%s />' % self.doc_tag): - # We have an empty document - output = '' - else: - # We have a serious problem - raise ValueError('Markdown failed to strip top-level ' - 'tags. Document=%r' % output.strip()) from e - - # Run the text post-processors - for pp in self.postprocessors: - output = pp.run(output) - - return output.strip() - - def convertFile(self, input=None, output=None, encoding=None): - """Converts a markdown file and returns the HTML as a unicode string. - - Decodes the file using the provided encoding (defaults to utf-8), - passes the file content to markdown, and outputs the html to either - the provided stream or the file with provided name, using the same - encoding as the source file. The 'xmlcharrefreplace' error handler is - used when encoding the output. - - **Note:** This is the only place that decoding and encoding of unicode - takes place in Python-Markdown. (All other code is unicode-in / - unicode-out.) - - Keyword arguments: - - * input: File object or path. Reads from stdin if `None`. - * output: File object or path. Writes to stdout if `None`. - * encoding: Encoding of input and output files. Defaults to utf-8. - - """ - - encoding = encoding or "utf-8" - - # Read the source - if input: - if isinstance(input, str): - input_file = codecs.open(input, mode="r", encoding=encoding) - else: - input_file = codecs.getreader(encoding)(input) - text = input_file.read() - input_file.close() - else: - text = sys.stdin.read() - if not isinstance(text, str): # pragma: no cover - text = text.decode(encoding) - - text = text.lstrip('\ufeff') # remove the byte-order mark - - # Convert - html = self.convert(text) - - # Write to file or stdout - if output: - if isinstance(output, str): - output_file = codecs.open(output, "w", - encoding=encoding, - errors="xmlcharrefreplace") - output_file.write(html) - output_file.close() - else: - writer = codecs.getwriter(encoding) - output_file = writer(output, errors="xmlcharrefreplace") - output_file.write(html) - # Don't close here. User may want to write more. - else: - # Encode manually and write bytes to stdout. - html = html.encode(encoding, "xmlcharrefreplace") - try: - # Write bytes directly to buffer (Python 3). - sys.stdout.buffer.write(html) - except AttributeError: # pragma: no cover - # Probably Python 2, which works with bytes by default. - sys.stdout.write(html) - - return self - - -""" -EXPORTED FUNCTIONS -============================================================================= - -Those are the two functions we really mean to export: markdown() and -markdownFromFile(). -""" - - -def markdown(text, **kwargs): - """Convert a markdown string to HTML and return HTML as a unicode string. - - This is a shortcut function for `Markdown` class to cover the most - basic use case. It initializes an instance of Markdown, loads the - necessary extensions and runs the parser on the given text. - - Keyword arguments: - - * text: Markdown formatted text as Unicode or ASCII string. - * Any arguments accepted by the Markdown class. - - Returns: An HTML document as a string. - - """ - md = Markdown(**kwargs) - return md.convert(text) - - -def markdownFromFile(**kwargs): - """Read markdown code from a file and write it to a file or a stream. - - This is a shortcut function which initializes an instance of Markdown, - and calls the convertFile method rather than convert. - - Keyword arguments: - - * input: a file name or readable object. - * output: a file name or writable object. - * encoding: Encoding of input and output. - * Any arguments accepted by the Markdown class. - - """ - md = Markdown(**kwargs) - md.convertFile(kwargs.get('input', None), - kwargs.get('output', None), - kwargs.get('encoding', None)) diff --git a/Source/Libs/markdown/extensions/__init__.py b/Source/Libs/markdown/extensions/__init__.py deleted file mode 100644 index 4712e25..0000000 --- a/Source/Libs/markdown/extensions/__init__.py +++ /dev/null @@ -1,107 +0,0 @@ -""" -Python Markdown - -A Python implementation of John Gruber's Markdown. - -Documentation: https://python-markdown.github.io/ -GitHub: https://github.com/Python-Markdown/markdown/ -PyPI: https://pypi.org/project/Markdown/ - -Started by Manfred Stienstra (http://www.dwerg.net/). -Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org). -Currently maintained by Waylan Limberg (https://github.com/waylan), -Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser). - -Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later) -Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b) -Copyright 2004 Manfred Stienstra (the original version) - -License: BSD (see LICENSE.md for details). -""" - -import warnings -from ..util import parseBoolValue - - -class Extension: - """ Base class for extensions to subclass. """ - - # Default config -- to be overridden by a subclass - # Must be of the following format: - # { - # 'key': ['value', 'description'] - # } - # Note that Extension.setConfig will raise a KeyError - # if a default is not set here. - config = {} - - def __init__(self, **kwargs): - """ Initiate Extension and set up configs. """ - self.setConfigs(kwargs) - - def getConfig(self, key, default=''): - """ Return a setting for the given key or an empty string. """ - if key in self.config: - return self.config[key][0] - else: - return default - - def getConfigs(self): - """ Return all configs settings as a dict. """ - return {key: self.getConfig(key) for key in self.config.keys()} - - def getConfigInfo(self): - """ Return all config descriptions as a list of tuples. """ - return [(key, self.config[key][1]) for key in self.config.keys()] - - def setConfig(self, key, value): - """ Set a config setting for `key` with the given `value`. """ - if isinstance(self.config[key][0], bool): - value = parseBoolValue(value) - if self.config[key][0] is None: - value = parseBoolValue(value, preserve_none=True) - self.config[key][0] = value - - def setConfigs(self, items): - """ Set multiple config settings given a dict or list of tuples. """ - if hasattr(items, 'items'): - # it's a dict - items = items.items() - for key, value in items: - self.setConfig(key, value) - - def _extendMarkdown(self, *args): - """ Private wrapper around extendMarkdown. """ - md = args[0] - try: - self.extendMarkdown(md) - except TypeError as e: - if "missing 1 required positional argument" in str(e): - # Must be a 2.x extension. Pass in a dumby md_globals. - self.extendMarkdown(md, {}) - warnings.warn( - "The 'md_globals' parameter of '{}.{}.extendMarkdown' is " - "deprecated.".format(self.__class__.__module__, self.__class__.__name__), - category=DeprecationWarning, - stacklevel=2 - ) - else: - raise - - def extendMarkdown(self, md): - """ - Add the various processors and patterns to the Markdown Instance. - - This method must be overridden by every extension. - - Keyword arguments: - - * md: The Markdown instance. - - * md_globals: Global variables in the markdown module namespace. - - """ - raise NotImplementedError( - 'Extension "%s.%s" must define an "extendMarkdown"' - 'method.' % (self.__class__.__module__, self.__class__.__name__) - ) diff --git a/Source/Libs/markdown/extensions/__pycache__/__init__.cpython-310.pyc b/Source/Libs/markdown/extensions/__pycache__/__init__.cpython-310.pyc deleted file mode 100644 index 00d7ee9588531083d5f424faa0f1d13fa35f0a3c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4004 zcmZ`+TW=f36`mbZ61A2TM|M)HNQ0>X$7X6TCAYp*P>tG>;i|D!qcUPdZ9p$MLvrQi zE~M*zcb65WM#>jo!yx^bIy0Jn=UT;4A(!4 zw)Tf5#{NSO(^mlxzeO{jqLXZoNl)_F+w){W7GvHn>=iurfXNv-`-;igmwc}%`7SHZ zg&Akef84jNG!c(N{amKK#P{!t;j5_KiB(%AHnb5|qz&==w3Ublx?5BFvKem)e19vg zcgHK&Ma$ZbS+7(&j`p?okap0FY^z%fqO{^ai0tpWwaL=qR6T81^5d_KII4`cSHSDz z?a7u-zY_(iZdN)v-B)#68Sn7@T^nkvq^R}5X423Y?%GHt#_CX9A^yN#uh$D?Pw8fm zC|fB9{-ZF0v{8c9jZ}+JG*nOYRj3US`l72N@g#iSvZ}8Rj#0q@suvlVwuvE7+U#l# zbfnhVJK}b!B-*ZdKSN*MaWOZNcw!(TV869|HF_!eh8@I|L zOr+R0VOSTBx<;K!xDnP>tc=0D28;e?+Ue`4*|OsL>gug)*H_nWizh8LQvV1|@i>Q9 zTsa8DT5!u96^9l?j+6M+RU&l5^*7%^3!7Ji>(|zTo3)esPE(NuBPqCxk}wtrN*kD9 z+4mntbtqA-i+j6U;)+p9Jlx*Azq5NkXy-bU3JQ!3 z^DyqJI!#VrV=wv~Zxw_0ustrly+XvuUu5AG+nbT+ofmm~vvN+B@MccVOCR@=JR=uy zpO=er33p$S@x5nkEoC zK*h()&HP1~XGNppMmGDr7wV>Y!*mJs|F=(*Ab)Ip__8tWb&Mm00>%?bCF zYBd4})oRe`XQk1@C&Vyuzr+X2W5$BfbjIo=*65-!E#5>kAE7(+4%t3CV0wY>hu#Xr zU17WIkRll$+!lXSwyP5n3P@z5r0F0fBWgwmDiP0BALaxcglczeUmy-(Q6Myd`^L*= zb6Cm+J&PAv0qe6lsTyH7who89o2SY7<_I*3xP~_Pe90^F!MTZG<2A?BN2if&)V{lb z?jNk-QM`Ut`WkY5>%C@M>?J9(07Z^uisO;iM$#D(;xA-Wx2G5gadZyN&75G7&HmPGdYGs>tidd!T;ruEPHQBuX=&H zW$GqE=JIXOaSp*-n05$$%m%+WMZWE%kxqd)-lso2PIm&{;ut*IdxMs(pd0+;N2tH! zXGN^uL^BW3SxCAMTgm;)@|RpM*}@@z=^gRu8Ogg$|BPm+%J_z3c4~fuxe8+jez}XN z6AsOHWWCNepTQiSi5@BfF>%vp2Nay24gGTG2}0CjM=K<(1657ZM3sw~ADIYwJWT3p zXjnBwbxkfcbYEx4*^Hx=+ZshOojdP1;{BRrrqb#>FL}$}0@}0#kJr`797Y#`MW*2Z zTg3o?!GQtO{GmtgOGdKAzV!bLNRA4K&IC;1HTT$yGY*u(;En^OjmQ$g_)cJBh5;*g zZi-naO*NZ|tZJK`av_^*g(kFC>x1J}9|N#3>U!MVYzFip+Zb-+1h(;IY9a$t`er z=^=!#a|qMMI;UJoTK zg5+Zz9Z<6C>9EsL2=O}Yq83t=P35SD85-2!lXhfaJx#GDw0hBvs4hf8L%vL@&OqLC zW$a*+>Y&SQS#8E?4e{)OjRiWj|XwzCW9P((kDITBkZIAU|a#+j-jOEqAsVpw*wzg8lfF^6UCB|)=>%NaRSBe zh}4r7v;;-AQYqS9B!L?0Ow^Y;k`i?SN_40PRkwqJ3bm=dpQ(N?)pBemW_?2UQ_+^| zu6!P!K8jLP6lm4Rrq;y+XIz4$3TsG1B1#q^E{AHLOx+AnrgW} zp}FwE7dcFW|A6puVa(DA*FdSM&k)f?N86o}EmW&7x?w!D*|};}ruAyog>+3J##JVo z%tTWK=)#+eT$;)d$HWw4li)@c_ER)NDYL|iUU9y}{bFgZRKyS0!m_3{&kk@ -for documentation. - -Oringinal code Copyright 2007-2008 [Waylan Limberg](http://achinghead.com/) and - [Seemant Kulleen](http://www.kulleen.org/) - -All changes Copyright 2008-2014 The Python Markdown Project - -License: [BSD](https://opensource.org/licenses/bsd-license.php) - -''' - -from . import Extension -from ..blockprocessors import BlockProcessor -from ..inlinepatterns import InlineProcessor -from ..util import AtomicString -import re -import xml.etree.ElementTree as etree - - -class AbbrExtension(Extension): - """ Abbreviation Extension for Python-Markdown. """ - - def extendMarkdown(self, md): - """ Insert AbbrPreprocessor before ReferencePreprocessor. """ - md.parser.blockprocessors.register(AbbrPreprocessor(md.parser), 'abbr', 16) - - -class AbbrPreprocessor(BlockProcessor): - """ Abbreviation Preprocessor - parse text for abbr references. """ - - RE = re.compile(r'^[*]\[(?P[^\]]*)\][ ]?:[ ]*\n?[ ]*(?P.*)$', re.MULTILINE) - - def test(self, parent, block): - return True - - def run(self, parent, blocks): - ''' - Find and remove all Abbreviation references from the text. - Each reference is set as a new AbbrPattern in the markdown instance. - - ''' - block = blocks.pop(0) - m = self.RE.search(block) - if m: - abbr = m.group('abbr').strip() - title = m.group('title').strip() - self.parser.md.inlinePatterns.register( - AbbrInlineProcessor(self._generate_pattern(abbr), title), 'abbr-%s' % abbr, 2 - ) - if block[m.end():].strip(): - # Add any content after match back to blocks as separate block - blocks.insert(0, block[m.end():].lstrip('\n')) - if block[:m.start()].strip(): - # Add any content before match back to blocks as separate block - blocks.insert(0, block[:m.start()].rstrip('\n')) - return True - # No match. Restore block. - blocks.insert(0, block) - return False - - def _generate_pattern(self, text): - ''' - Given a string, returns an regex pattern to match that string. - - 'HTML' -> r'(?P<abbr>[H][T][M][L])' - - Note: we force each char as a literal match (in brackets) as we don't - know what they will be beforehand. - - ''' - chars = list(text) - for i in range(len(chars)): - chars[i] = r'[%s]' % chars[i] - return r'(?P<abbr>\b%s\b)' % (r''.join(chars)) - - -class AbbrInlineProcessor(InlineProcessor): - """ Abbreviation inline pattern. """ - - def __init__(self, pattern, title): - super().__init__(pattern) - self.title = title - - def handleMatch(self, m, data): - abbr = etree.Element('abbr') - abbr.text = AtomicString(m.group('abbr')) - abbr.set('title', self.title) - return abbr, m.start(0), m.end(0) - - -def makeExtension(**kwargs): # pragma: no cover - return AbbrExtension(**kwargs) diff --git a/Source/Libs/markdown/extensions/admonition.py b/Source/Libs/markdown/extensions/admonition.py deleted file mode 100644 index cb8d901..0000000 --- a/Source/Libs/markdown/extensions/admonition.py +++ /dev/null @@ -1,170 +0,0 @@ -""" -Admonition extension for Python-Markdown -======================================== - -Adds rST-style admonitions. Inspired by [rST][] feature with the same name. - -[rST]: http://docutils.sourceforge.net/docs/ref/rst/directives.html#specific-admonitions # noqa - -See <https://Python-Markdown.github.io/extensions/admonition> -for documentation. - -Original code Copyright [Tiago Serafim](https://www.tiagoserafim.com/). - -All changes Copyright The Python Markdown Project - -License: [BSD](https://opensource.org/licenses/bsd-license.php) - -""" - -from . import Extension -from ..blockprocessors import BlockProcessor -import xml.etree.ElementTree as etree -import re - - -class AdmonitionExtension(Extension): - """ Admonition extension for Python-Markdown. """ - - def extendMarkdown(self, md): - """ Add Admonition to Markdown instance. """ - md.registerExtension(self) - - md.parser.blockprocessors.register(AdmonitionProcessor(md.parser), 'admonition', 105) - - -class AdmonitionProcessor(BlockProcessor): - - CLASSNAME = 'admonition' - CLASSNAME_TITLE = 'admonition-title' - RE = re.compile(r'(?:^|\n)!!! ?([\w\-]+(?: +[\w\-]+)*)(?: +"(.*?)")? *(?:\n|$)') - RE_SPACES = re.compile(' +') - - def __init__(self, parser): - """Initialization.""" - - super().__init__(parser) - - self.current_sibling = None - self.content_indention = 0 - - def parse_content(self, parent, block): - """Get sibling admonition. - - Retrieve the appropriate sibling element. This can get tricky when - dealing with lists. - - """ - - old_block = block - the_rest = '' - - # We already acquired the block via test - if self.current_sibling is not None: - sibling = self.current_sibling - block, the_rest = self.detab(block, self.content_indent) - self.current_sibling = None - self.content_indent = 0 - return sibling, block, the_rest - - sibling = self.lastChild(parent) - - if sibling is None or sibling.get('class', '').find(self.CLASSNAME) == -1: - sibling = None - else: - # If the last child is a list and the content is sufficiently indented - # to be under it, then the content's sibling is in the list. - last_child = self.lastChild(sibling) - indent = 0 - while last_child: - if ( - sibling and block.startswith(' ' * self.tab_length * 2) and - last_child and last_child.tag in ('ul', 'ol', 'dl') - ): - - # The expectation is that we'll find an <li> or <dt>. - # We should get its last child as well. - sibling = self.lastChild(last_child) - last_child = self.lastChild(sibling) if sibling else None - - # Context has been lost at this point, so we must adjust the - # text's indentation level so it will be evaluated correctly - # under the list. - block = block[self.tab_length:] - indent += self.tab_length - else: - last_child = None - - if not block.startswith(' ' * self.tab_length): - sibling = None - - if sibling is not None: - indent += self.tab_length - block, the_rest = self.detab(old_block, indent) - self.current_sibling = sibling - self.content_indent = indent - - return sibling, block, the_rest - - def test(self, parent, block): - - if self.RE.search(block): - return True - else: - return self.parse_content(parent, block)[0] is not None - - def run(self, parent, blocks): - block = blocks.pop(0) - m = self.RE.search(block) - - if m: - if m.start() > 0: - self.parser.parseBlocks(parent, [block[:m.start()]]) - block = block[m.end():] # removes the first line - block, theRest = self.detab(block) - else: - sibling, block, theRest = self.parse_content(parent, block) - - if m: - klass, title = self.get_class_and_title(m) - div = etree.SubElement(parent, 'div') - div.set('class', '{} {}'.format(self.CLASSNAME, klass)) - if title: - p = etree.SubElement(div, 'p') - p.text = title - p.set('class', self.CLASSNAME_TITLE) - else: - # Sibling is a list item, but we need to wrap it's content should be wrapped in <p> - if sibling.tag in ('li', 'dd') and sibling.text: - text = sibling.text - sibling.text = '' - p = etree.SubElement(sibling, 'p') - p.text = text - - div = sibling - - self.parser.parseChunk(div, block) - - if theRest: - # This block contained unindented line(s) after the first indented - # line. Insert these lines as the first block of the master blocks - # list for future processing. - blocks.insert(0, theRest) - - def get_class_and_title(self, match): - klass, title = match.group(1).lower(), match.group(2) - klass = self.RE_SPACES.sub(' ', klass) - if title is None: - # no title was provided, use the capitalized classname as title - # e.g.: `!!! note` will render - # `<p class="admonition-title">Note</p>` - title = klass.split(' ', 1)[0].capitalize() - elif title == '': - # an explicit blank title should not be rendered - # e.g.: `!!! warning ""` will *not* render `p` with a title - title = None - return klass, title - - -def makeExtension(**kwargs): # pragma: no cover - return AdmonitionExtension(**kwargs) diff --git a/Source/Libs/markdown/extensions/attr_list.py b/Source/Libs/markdown/extensions/attr_list.py deleted file mode 100644 index 9a67551..0000000 --- a/Source/Libs/markdown/extensions/attr_list.py +++ /dev/null @@ -1,166 +0,0 @@ -""" -Attribute List Extension for Python-Markdown -============================================ - -Adds attribute list syntax. Inspired by -[maruku](http://maruku.rubyforge.org/proposal.html#attribute_lists)'s -feature of the same name. - -See <https://Python-Markdown.github.io/extensions/attr_list> -for documentation. - -Original code Copyright 2011 [Waylan Limberg](http://achinghead.com/). - -All changes Copyright 2011-2014 The Python Markdown Project - -License: [BSD](https://opensource.org/licenses/bsd-license.php) - -""" - -from . import Extension -from ..treeprocessors import Treeprocessor -import re - - -def _handle_double_quote(s, t): - k, v = t.split('=', 1) - return k, v.strip('"') - - -def _handle_single_quote(s, t): - k, v = t.split('=', 1) - return k, v.strip("'") - - -def _handle_key_value(s, t): - return t.split('=', 1) - - -def _handle_word(s, t): - if t.startswith('.'): - return '.', t[1:] - if t.startswith('#'): - return 'id', t[1:] - return t, t - - -_scanner = re.Scanner([ - (r'[^ =]+=".*?"', _handle_double_quote), - (r"[^ =]+='.*?'", _handle_single_quote), - (r'[^ =]+=[^ =]+', _handle_key_value), - (r'[^ =]+', _handle_word), - (r' ', None) -]) - - -def get_attrs(str): - """ Parse attribute list and return a list of attribute tuples. """ - return _scanner.scan(str)[0] - - -def isheader(elem): - return elem.tag in ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'] - - -class AttrListTreeprocessor(Treeprocessor): - - BASE_RE = r'\{\:?[ ]*([^\}\n ][^\}\n]*)[ ]*\}' - HEADER_RE = re.compile(r'[ ]+{}[ ]*$'.format(BASE_RE)) - BLOCK_RE = re.compile(r'\n[ ]*{}[ ]*$'.format(BASE_RE)) - INLINE_RE = re.compile(r'^{}'.format(BASE_RE)) - NAME_RE = re.compile(r'[^A-Z_a-z\u00c0-\u00d6\u00d8-\u00f6\u00f8-\u02ff' - r'\u0370-\u037d\u037f-\u1fff\u200c-\u200d' - r'\u2070-\u218f\u2c00-\u2fef\u3001-\ud7ff' - r'\uf900-\ufdcf\ufdf0-\ufffd' - r'\:\-\.0-9\u00b7\u0300-\u036f\u203f-\u2040]+') - - def run(self, doc): - for elem in doc.iter(): - if self.md.is_block_level(elem.tag): - # Block level: check for attrs on last line of text - RE = self.BLOCK_RE - if isheader(elem) or elem.tag in ['dt', 'td', 'th']: - # header, def-term, or table cell: check for attrs at end of element - RE = self.HEADER_RE - if len(elem) and elem.tag == 'li': - # special case list items. children may include a ul or ol. - pos = None - # find the ul or ol position - for i, child in enumerate(elem): - if child.tag in ['ul', 'ol']: - pos = i - break - if pos is None and elem[-1].tail: - # use tail of last child. no ul or ol. - m = RE.search(elem[-1].tail) - if m: - self.assign_attrs(elem, m.group(1)) - elem[-1].tail = elem[-1].tail[:m.start()] - elif pos is not None and pos > 0 and elem[pos-1].tail: - # use tail of last child before ul or ol - m = RE.search(elem[pos-1].tail) - if m: - self.assign_attrs(elem, m.group(1)) - elem[pos-1].tail = elem[pos-1].tail[:m.start()] - elif elem.text: - # use text. ul is first child. - m = RE.search(elem.text) - if m: - self.assign_attrs(elem, m.group(1)) - elem.text = elem.text[:m.start()] - elif len(elem) and elem[-1].tail: - # has children. Get from tail of last child - m = RE.search(elem[-1].tail) - if m: - self.assign_attrs(elem, m.group(1)) - elem[-1].tail = elem[-1].tail[:m.start()] - if isheader(elem): - # clean up trailing #s - elem[-1].tail = elem[-1].tail.rstrip('#').rstrip() - elif elem.text: - # no children. Get from text. - m = RE.search(elem.text) - if m: - self.assign_attrs(elem, m.group(1)) - elem.text = elem.text[:m.start()] - if isheader(elem): - # clean up trailing #s - elem.text = elem.text.rstrip('#').rstrip() - else: - # inline: check for attrs at start of tail - if elem.tail: - m = self.INLINE_RE.match(elem.tail) - if m: - self.assign_attrs(elem, m.group(1)) - elem.tail = elem.tail[m.end():] - - def assign_attrs(self, elem, attrs): - """ Assign attrs to element. """ - for k, v in get_attrs(attrs): - if k == '.': - # add to class - cls = elem.get('class') - if cls: - elem.set('class', '{} {}'.format(cls, v)) - else: - elem.set('class', v) - else: - # assign attr k with v - elem.set(self.sanitize_name(k), v) - - def sanitize_name(self, name): - """ - Sanitize name as 'an XML Name, minus the ":"'. - See https://www.w3.org/TR/REC-xml-names/#NT-NCName - """ - return self.NAME_RE.sub('_', name) - - -class AttrListExtension(Extension): - def extendMarkdown(self, md): - md.treeprocessors.register(AttrListTreeprocessor(md), 'attr_list', 8) - md.registerExtension(self) - - -def makeExtension(**kwargs): # pragma: no cover - return AttrListExtension(**kwargs) diff --git a/Source/Libs/markdown/extensions/codehilite.py b/Source/Libs/markdown/extensions/codehilite.py deleted file mode 100644 index a768b73..0000000 --- a/Source/Libs/markdown/extensions/codehilite.py +++ /dev/null @@ -1,308 +0,0 @@ -""" -CodeHilite Extension for Python-Markdown -======================================== - -Adds code/syntax highlighting to standard Python-Markdown code blocks. - -See <https://Python-Markdown.github.io/extensions/code_hilite> -for documentation. - -Original code Copyright 2006-2008 [Waylan Limberg](http://achinghead.com/). - -All changes Copyright 2008-2014 The Python Markdown Project - -License: [BSD](https://opensource.org/licenses/bsd-license.php) - -""" - -from . import Extension -from ..treeprocessors import Treeprocessor -from ..util import parseBoolValue - -try: # pragma: no cover - from pygments import highlight - from pygments.lexers import get_lexer_by_name, guess_lexer - from pygments.formatters import get_formatter_by_name - pygments = True -except ImportError: # pragma: no cover - pygments = False - - -def parse_hl_lines(expr): - """Support our syntax for emphasizing certain lines of code. - - expr should be like '1 2' to emphasize lines 1 and 2 of a code block. - Returns a list of ints, the line numbers to emphasize. - """ - if not expr: - return [] - - try: - return list(map(int, expr.split())) - except ValueError: # pragma: no cover - return [] - - -# ------------------ The Main CodeHilite Class ---------------------- -class CodeHilite: - """ - Determine language of source code, and pass it on to the Pygments highlighter. - - Usage: - code = CodeHilite(src=some_code, lang='python') - html = code.hilite() - - Arguments: - * src: Source string or any object with a .readline attribute. - - * lang: String name of Pygments lexer to use for highlighting. Default: `None`. - - * guess_lang: Auto-detect which lexer to use. Ignored if `lang` is set to a valid - value. Default: `True`. - - * use_pygments: Pass code to pygments for code highlighting. If `False`, the code is - instead wrapped for highlighting by a JavaScript library. Default: `True`. - - * linenums: An alias to Pygments `linenos` formatter option. Default: `None`. - - * css_class: An alias to Pygments `cssclass` formatter option. Default: 'codehilite'. - - * lang_prefix: Prefix prepended to the language when `use_pygments` is `False`. - Default: "language-". - - Other Options: - Any other options are accepted and passed on to the lexer and formatter. Therefore, - valid options include any options which are accepted by the `html` formatter or - whichever lexer the code's language uses. Note that most lexers do not have any - options. However, a few have very useful options, such as PHP's `startinline` option. - Any invalid options are ignored without error. - - Formatter options: https://pygments.org/docs/formatters/#HtmlFormatter - Lexer Options: https://pygments.org/docs/lexers/ - - Advanced Usage: - code = CodeHilite( - src = some_code, - lang = 'php', - startinline = True, # Lexer option. Snippet does not start with `<?php`. - linenostart = 42, # Formatter option. Snippet starts on line 42. - hl_lines = [45, 49, 50], # Formatter option. Highlight lines 45, 49, and 50. - linenos = 'inline' # Formatter option. Avoid alignment problems. - ) - html = code.hilite() - - """ - - def __init__(self, src, **options): - self.src = src - self.lang = options.pop('lang', None) - self.guess_lang = options.pop('guess_lang', True) - self.use_pygments = options.pop('use_pygments', True) - self.lang_prefix = options.pop('lang_prefix', 'language-') - - if 'linenos' not in options: - options['linenos'] = options.pop('linenums', None) - if 'cssclass' not in options: - options['cssclass'] = options.pop('css_class', 'codehilite') - if 'wrapcode' not in options: - # Override pygments default - options['wrapcode'] = True - # Disallow use of `full` option - options['full'] = False - - self.options = options - - def hilite(self, shebang=True): - """ - Pass code to the [Pygments](http://pygments.pocoo.org/) highliter with - optional line numbers. The output should then be styled with css to - your liking. No styles are applied by default - only styling hooks - (i.e.: <span class="k">). - - returns : A string of html. - - """ - - self.src = self.src.strip('\n') - - if self.lang is None and shebang: - self._parseHeader() - - if pygments and self.use_pygments: - try: - lexer = get_lexer_by_name(self.lang, **self.options) - except ValueError: - try: - if self.guess_lang: - lexer = guess_lexer(self.src, **self.options) - else: - lexer = get_lexer_by_name('text', **self.options) - except ValueError: # pragma: no cover - lexer = get_lexer_by_name('text', **self.options) - formatter = get_formatter_by_name('html', **self.options) - return highlight(self.src, lexer, formatter) - else: - # just escape and build markup usable by JS highlighting libs - txt = self.src.replace('&', '&') - txt = txt.replace('<', '<') - txt = txt.replace('>', '>') - txt = txt.replace('"', '"') - classes = [] - if self.lang: - classes.append('{}{}'.format(self.lang_prefix, self.lang)) - if self.options['linenos']: - classes.append('linenums') - class_str = '' - if classes: - class_str = ' class="{}"'.format(' '.join(classes)) - return '<pre class="{}"><code{}>{}\n</code></pre>\n'.format( - self.options['cssclass'], - class_str, - txt - ) - - def _parseHeader(self): - """ - Determines language of a code block from shebang line and whether the - said line should be removed or left in place. If the sheband line - contains a path (even a single /) then it is assumed to be a real - shebang line and left alone. However, if no path is given - (e.i.: #!python or :::python) then it is assumed to be a mock shebang - for language identification of a code fragment and removed from the - code block prior to processing for code highlighting. When a mock - shebang (e.i: #!python) is found, line numbering is turned on. When - colons are found in place of a shebang (e.i.: :::python), line - numbering is left in the current state - off by default. - - Also parses optional list of highlight lines, like: - - :::python hl_lines="1 3" - """ - - import re - - # split text into lines - lines = self.src.split("\n") - # pull first line to examine - fl = lines.pop(0) - - c = re.compile(r''' - (?:(?:^::+)|(?P<shebang>^[#]!)) # Shebang or 2 or more colons - (?P<path>(?:/\w+)*[/ ])? # Zero or 1 path - (?P<lang>[\w#.+-]*) # The language - \s* # Arbitrary whitespace - # Optional highlight lines, single- or double-quote-delimited - (hl_lines=(?P<quot>"|')(?P<hl_lines>.*?)(?P=quot))? - ''', re.VERBOSE) - # search first line for shebang - m = c.search(fl) - if m: - # we have a match - try: - self.lang = m.group('lang').lower() - except IndexError: # pragma: no cover - self.lang = None - if m.group('path'): - # path exists - restore first line - lines.insert(0, fl) - if self.options['linenos'] is None and m.group('shebang'): - # Overridable and Shebang exists - use line numbers - self.options['linenos'] = True - - self.options['hl_lines'] = parse_hl_lines(m.group('hl_lines')) - else: - # No match - lines.insert(0, fl) - - self.src = "\n".join(lines).strip("\n") - - -# ------------------ The Markdown Extension ------------------------------- - - -class HiliteTreeprocessor(Treeprocessor): - """ Highlight source code in code blocks. """ - - def code_unescape(self, text): - """Unescape code.""" - text = text.replace("<", "<") - text = text.replace(">", ">") - # Escaped '&' should be replaced at the end to avoid - # conflicting with < and >. - text = text.replace("&", "&") - return text - - def run(self, root): - """ Find code blocks and store in htmlStash. """ - blocks = root.iter('pre') - for block in blocks: - if len(block) == 1 and block[0].tag == 'code': - local_config = self.config.copy() - code = CodeHilite( - self.code_unescape(block[0].text), - tab_length=self.md.tab_length, - style=local_config.pop('pygments_style', 'default'), - **local_config - ) - placeholder = self.md.htmlStash.store(code.hilite()) - # Clear codeblock in etree instance - block.clear() - # Change to p element which will later - # be removed when inserting raw html - block.tag = 'p' - block.text = placeholder - - -class CodeHiliteExtension(Extension): - """ Add source code highlighting to markdown codeblocks. """ - - def __init__(self, **kwargs): - # define default configs - self.config = { - 'linenums': [None, - "Use lines numbers. True|table|inline=yes, False=no, None=auto"], - 'guess_lang': [True, - "Automatic language detection - Default: True"], - 'css_class': ["codehilite", - "Set class name for wrapper <div> - " - "Default: codehilite"], - 'pygments_style': ['default', - 'Pygments HTML Formatter Style ' - '(Colorscheme) - Default: default'], - 'noclasses': [False, - 'Use inline styles instead of CSS classes - ' - 'Default false'], - 'use_pygments': [True, - 'Use Pygments to Highlight code blocks. ' - 'Disable if using a JavaScript library. ' - 'Default: True'], - 'lang_prefix': [ - 'language-', - 'Prefix prepended to the language when use_pygments is false. Default: "language-"' - ] - } - - for key, value in kwargs.items(): - if key in self.config: - self.setConfig(key, value) - else: - # manually set unknown keywords. - if isinstance(value, str): - try: - # Attempt to parse str as a bool value - value = parseBoolValue(value, preserve_none=True) - except ValueError: - pass # Assume it's not a bool value. Use as-is. - self.config[key] = [value, ''] - - def extendMarkdown(self, md): - """ Add HilitePostprocessor to Markdown instance. """ - hiliter = HiliteTreeprocessor(md) - hiliter.config = self.getConfigs() - md.treeprocessors.register(hiliter, 'hilite', 30) - - md.registerExtension(self) - - -def makeExtension(**kwargs): # pragma: no cover - return CodeHiliteExtension(**kwargs) diff --git a/Source/Libs/markdown/extensions/def_list.py b/Source/Libs/markdown/extensions/def_list.py deleted file mode 100644 index 17549f0..0000000 --- a/Source/Libs/markdown/extensions/def_list.py +++ /dev/null @@ -1,111 +0,0 @@ -""" -Definition List Extension for Python-Markdown -============================================= - -Adds parsing of Definition Lists to Python-Markdown. - -See <https://Python-Markdown.github.io/extensions/definition_lists> -for documentation. - -Original code Copyright 2008 [Waylan Limberg](http://achinghead.com) - -All changes Copyright 2008-2014 The Python Markdown Project - -License: [BSD](https://opensource.org/licenses/bsd-license.php) - -""" - -from . import Extension -from ..blockprocessors import BlockProcessor, ListIndentProcessor -import xml.etree.ElementTree as etree -import re - - -class DefListProcessor(BlockProcessor): - """ Process Definition Lists. """ - - RE = re.compile(r'(^|\n)[ ]{0,3}:[ ]{1,3}(.*?)(\n|$)') - NO_INDENT_RE = re.compile(r'^[ ]{0,3}[^ :]') - - def test(self, parent, block): - return bool(self.RE.search(block)) - - def run(self, parent, blocks): - - raw_block = blocks.pop(0) - m = self.RE.search(raw_block) - terms = [term.strip() for term in - raw_block[:m.start()].split('\n') if term.strip()] - block = raw_block[m.end():] - no_indent = self.NO_INDENT_RE.match(block) - if no_indent: - d, theRest = (block, None) - else: - d, theRest = self.detab(block) - if d: - d = '{}\n{}'.format(m.group(2), d) - else: - d = m.group(2) - sibling = self.lastChild(parent) - if not terms and sibling is None: - # This is not a definition item. Most likely a paragraph that - # starts with a colon at the beginning of a document or list. - blocks.insert(0, raw_block) - return False - if not terms and sibling.tag == 'p': - # The previous paragraph contains the terms - state = 'looselist' - terms = sibling.text.split('\n') - parent.remove(sibling) - # Acquire new sibling - sibling = self.lastChild(parent) - else: - state = 'list' - - if sibling is not None and sibling.tag == 'dl': - # This is another item on an existing list - dl = sibling - if not terms and len(dl) and dl[-1].tag == 'dd' and len(dl[-1]): - state = 'looselist' - else: - # This is a new list - dl = etree.SubElement(parent, 'dl') - # Add terms - for term in terms: - dt = etree.SubElement(dl, 'dt') - dt.text = term - # Add definition - self.parser.state.set(state) - dd = etree.SubElement(dl, 'dd') - self.parser.parseBlocks(dd, [d]) - self.parser.state.reset() - - if theRest: - blocks.insert(0, theRest) - - -class DefListIndentProcessor(ListIndentProcessor): - """ Process indented children of definition list items. """ - - # Definition lists need to be aware of all list types - ITEM_TYPES = ['dd', 'li'] - LIST_TYPES = ['dl', 'ol', 'ul'] - - def create_item(self, parent, block): - """ Create a new dd or li (depending on parent) and parse the block with it as the parent. """ - - dd = etree.SubElement(parent, 'dd') - self.parser.parseBlocks(dd, [block]) - - -class DefListExtension(Extension): - """ Add definition lists to Markdown. """ - - def extendMarkdown(self, md): - """ Add an instance of DefListProcessor to BlockParser. """ - md.parser.blockprocessors.register(DefListIndentProcessor(md.parser), 'defindent', 85) - md.parser.blockprocessors.register(DefListProcessor(md.parser), 'deflist', 25) - - -def makeExtension(**kwargs): # pragma: no cover - return DefListExtension(**kwargs) diff --git a/Source/Libs/markdown/extensions/extra.py b/Source/Libs/markdown/extensions/extra.py deleted file mode 100644 index 909ba07..0000000 --- a/Source/Libs/markdown/extensions/extra.py +++ /dev/null @@ -1,58 +0,0 @@ -""" -Python-Markdown Extra Extension -=============================== - -A compilation of various Python-Markdown extensions that imitates -[PHP Markdown Extra](http://michelf.com/projects/php-markdown/extra/). - -Note that each of the individual extensions still need to be available -on your PYTHONPATH. This extension simply wraps them all up as a -convenience so that only one extension needs to be listed when -initiating Markdown. See the documentation for each individual -extension for specifics about that extension. - -There may be additional extensions that are distributed with -Python-Markdown that are not included here in Extra. Those extensions -are not part of PHP Markdown Extra, and therefore, not part of -Python-Markdown Extra. If you really would like Extra to include -additional extensions, we suggest creating your own clone of Extra -under a different name. You could also edit the `extensions` global -variable defined below, but be aware that such changes may be lost -when you upgrade to any future version of Python-Markdown. - -See <https://Python-Markdown.github.io/extensions/extra> -for documentation. - -Copyright The Python Markdown Project - -License: [BSD](https://opensource.org/licenses/bsd-license.php) - -""" - -from . import Extension - -extensions = [ - 'fenced_code', - 'footnotes', - 'attr_list', - 'def_list', - 'tables', - 'abbr', - 'md_in_html' -] - - -class ExtraExtension(Extension): - """ Add various extensions to Markdown class.""" - - def __init__(self, **kwargs): - """ config is a dumb holder which gets passed to actual ext later. """ - self.config = kwargs - - def extendMarkdown(self, md): - """ Register extension instances. """ - md.registerExtensions(extensions, self.config) - - -def makeExtension(**kwargs): # pragma: no cover - return ExtraExtension(**kwargs) diff --git a/Source/Libs/markdown/extensions/fenced_code.py b/Source/Libs/markdown/extensions/fenced_code.py deleted file mode 100644 index 409166a..0000000 --- a/Source/Libs/markdown/extensions/fenced_code.py +++ /dev/null @@ -1,174 +0,0 @@ -""" -Fenced Code Extension for Python Markdown -========================================= - -This extension adds Fenced Code Blocks to Python-Markdown. - -See <https://Python-Markdown.github.io/extensions/fenced_code_blocks> -for documentation. - -Original code Copyright 2007-2008 [Waylan Limberg](http://achinghead.com/). - - -All changes Copyright 2008-2014 The Python Markdown Project - -License: [BSD](https://opensource.org/licenses/bsd-license.php) -""" - - -from textwrap import dedent -from . import Extension -from ..preprocessors import Preprocessor -from .codehilite import CodeHilite, CodeHiliteExtension, parse_hl_lines -from .attr_list import get_attrs, AttrListExtension -from ..util import parseBoolValue -from ..serializers import _escape_attrib_html -import re - - -class FencedCodeExtension(Extension): - def __init__(self, **kwargs): - self.config = { - 'lang_prefix': ['language-', 'Prefix prepended to the language. Default: "language-"'] - } - super().__init__(**kwargs) - - def extendMarkdown(self, md): - """ Add FencedBlockPreprocessor to the Markdown instance. """ - md.registerExtension(self) - - md.preprocessors.register(FencedBlockPreprocessor(md, self.getConfigs()), 'fenced_code_block', 25) - - -class FencedBlockPreprocessor(Preprocessor): - FENCED_BLOCK_RE = re.compile( - dedent(r''' - (?P<fence>^(?:~{3,}|`{3,}))[ ]* # opening fence - ((\{(?P<attrs>[^\}\n]*)\})| # (optional {attrs} or - (\.?(?P<lang>[\w#.+-]*)[ ]*)? # optional (.)lang - (hl_lines=(?P<quot>"|')(?P<hl_lines>.*?)(?P=quot)[ ]*)?) # optional hl_lines) - \n # newline (end of opening fence) - (?P<code>.*?)(?<=\n) # the code block - (?P=fence)[ ]*$ # closing fence - '''), - re.MULTILINE | re.DOTALL | re.VERBOSE - ) - - def __init__(self, md, config): - super().__init__(md) - self.config = config - self.checked_for_deps = False - self.codehilite_conf = {} - self.use_attr_list = False - # List of options to convert to bool values - self.bool_options = [ - 'linenums', - 'guess_lang', - 'noclasses', - 'use_pygments' - ] - - def run(self, lines): - """ Match and store Fenced Code Blocks in the HtmlStash. """ - - # Check for dependent extensions - if not self.checked_for_deps: - for ext in self.md.registeredExtensions: - if isinstance(ext, CodeHiliteExtension): - self.codehilite_conf = ext.getConfigs() - if isinstance(ext, AttrListExtension): - self.use_attr_list = True - - self.checked_for_deps = True - - text = "\n".join(lines) - while 1: - m = self.FENCED_BLOCK_RE.search(text) - if m: - lang, id, classes, config = None, '', [], {} - if m.group('attrs'): - id, classes, config = self.handle_attrs(get_attrs(m.group('attrs'))) - if len(classes): - lang = classes.pop(0) - else: - if m.group('lang'): - lang = m.group('lang') - if m.group('hl_lines'): - # Support hl_lines outside of attrs for backward-compatibility - config['hl_lines'] = parse_hl_lines(m.group('hl_lines')) - - # If config is not empty, then the codehighlite extension - # is enabled, so we call it to highlight the code - if self.codehilite_conf and self.codehilite_conf['use_pygments'] and config.get('use_pygments', True): - local_config = self.codehilite_conf.copy() - local_config.update(config) - # Combine classes with cssclass. Ensure cssclass is at end - # as pygments appends a suffix under certain circumstances. - # Ignore ID as Pygments does not offer an option to set it. - if classes: - local_config['css_class'] = '{} {}'.format( - ' '.join(classes), - local_config['css_class'] - ) - highliter = CodeHilite( - m.group('code'), - lang=lang, - style=local_config.pop('pygments_style', 'default'), - **local_config - ) - - code = highliter.hilite(shebang=False) - else: - id_attr = lang_attr = class_attr = kv_pairs = '' - if lang: - prefix = self.config.get('lang_prefix', 'language-') - lang_attr = f' class="{prefix}{_escape_attrib_html(lang)}"' - if classes: - class_attr = f' class="{_escape_attrib_html(" ".join(classes))}"' - if id: - id_attr = f' id="{_escape_attrib_html(id)}"' - if self.use_attr_list and config and not config.get('use_pygments', False): - # Only assign key/value pairs to code element if attr_list ext is enabled, key/value pairs - # were defined on the code block, and the `use_pygments` key was not set to True. The - # `use_pygments` key could be either set to False or not defined. It is omitted from output. - kv_pairs = ''.join( - f' {k}="{_escape_attrib_html(v)}"' for k, v in config.items() if k != 'use_pygments' - ) - code = self._escape(m.group('code')) - code = f'<pre{id_attr}{class_attr}><code{lang_attr}{kv_pairs}>{code}</code></pre>' - - placeholder = self.md.htmlStash.store(code) - text = f'{text[:m.start()]}\n{placeholder}\n{text[m.end():]}' - else: - break - return text.split("\n") - - def handle_attrs(self, attrs): - """ Return tuple: (id, [list, of, classes], {configs}) """ - id = '' - classes = [] - configs = {} - for k, v in attrs: - if k == 'id': - id = v - elif k == '.': - classes.append(v) - elif k == 'hl_lines': - configs[k] = parse_hl_lines(v) - elif k in self.bool_options: - configs[k] = parseBoolValue(v, fail_on_errors=False, preserve_none=True) - else: - configs[k] = v - return id, classes, configs - - def _escape(self, txt): - """ basic html escaping """ - txt = txt.replace('&', '&') - txt = txt.replace('<', '<') - txt = txt.replace('>', '>') - txt = txt.replace('"', '"') - return txt - - -def makeExtension(**kwargs): # pragma: no cover - return FencedCodeExtension(**kwargs) diff --git a/Source/Libs/markdown/extensions/footnotes.py b/Source/Libs/markdown/extensions/footnotes.py deleted file mode 100644 index 1cc7118..0000000 --- a/Source/Libs/markdown/extensions/footnotes.py +++ /dev/null @@ -1,402 +0,0 @@ -""" -Footnotes Extension for Python-Markdown -======================================= - -Adds footnote handling to Python-Markdown. - -See <https://Python-Markdown.github.io/extensions/footnotes> -for documentation. - -Copyright The Python Markdown Project - -License: [BSD](https://opensource.org/licenses/bsd-license.php) - -""" - -from . import Extension -from ..blockprocessors import BlockProcessor -from ..inlinepatterns import InlineProcessor -from ..treeprocessors import Treeprocessor -from ..postprocessors import Postprocessor -from .. import util -from collections import OrderedDict -import re -import copy -import xml.etree.ElementTree as etree - -FN_BACKLINK_TEXT = util.STX + "zz1337820767766393qq" + util.ETX -NBSP_PLACEHOLDER = util.STX + "qq3936677670287331zz" + util.ETX -RE_REF_ID = re.compile(r'(fnref)(\d+)') - - -class FootnoteExtension(Extension): - """ Footnote Extension. """ - - def __init__(self, **kwargs): - """ Setup configs. """ - - self.config = { - 'PLACE_MARKER': - ["///Footnotes Go Here///", - "The text string that marks where the footnotes go"], - 'UNIQUE_IDS': - [False, - "Avoid name collisions across " - "multiple calls to reset()."], - "BACKLINK_TEXT": - ["↩", - "The text string that links from the footnote " - "to the reader's place."], - "BACKLINK_TITLE": - ["Jump back to footnote %d in the text", - "The text string used for the title HTML attribute " - "of the backlink. %d will be replaced by the " - "footnote number."], - "SEPARATOR": - [":", - "Footnote separator."] - } - super().__init__(**kwargs) - - # In multiple invocations, emit links that don't get tangled. - self.unique_prefix = 0 - self.found_refs = {} - self.used_refs = set() - - self.reset() - - def extendMarkdown(self, md): - """ Add pieces to Markdown. """ - md.registerExtension(self) - self.parser = md.parser - self.md = md - # Insert a blockprocessor before ReferencePreprocessor - md.parser.blockprocessors.register(FootnoteBlockProcessor(self), 'footnote', 17) - - # Insert an inline pattern before ImageReferencePattern - FOOTNOTE_RE = r'\[\^([^\]]*)\]' # blah blah [^1] blah - md.inlinePatterns.register(FootnoteInlineProcessor(FOOTNOTE_RE, self), 'footnote', 175) - # Insert a tree-processor that would actually add the footnote div - # This must be before all other treeprocessors (i.e., inline and - # codehilite) so they can run on the the contents of the div. - md.treeprocessors.register(FootnoteTreeprocessor(self), 'footnote', 50) - - # Insert a tree-processor that will run after inline is done. - # In this tree-processor we want to check our duplicate footnote tracker - # And add additional backrefs to the footnote pointing back to the - # duplicated references. - md.treeprocessors.register(FootnotePostTreeprocessor(self), 'footnote-duplicate', 15) - - # Insert a postprocessor after amp_substitute processor - md.postprocessors.register(FootnotePostprocessor(self), 'footnote', 25) - - def reset(self): - """ Clear footnotes on reset, and prepare for distinct document. """ - self.footnotes = OrderedDict() - self.unique_prefix += 1 - self.found_refs = {} - self.used_refs = set() - - def unique_ref(self, reference, found=False): - """ Get a unique reference if there are duplicates. """ - if not found: - return reference - - original_ref = reference - while reference in self.used_refs: - ref, rest = reference.split(self.get_separator(), 1) - m = RE_REF_ID.match(ref) - if m: - reference = '%s%d%s%s' % (m.group(1), int(m.group(2))+1, self.get_separator(), rest) - else: - reference = '%s%d%s%s' % (ref, 2, self.get_separator(), rest) - - self.used_refs.add(reference) - if original_ref in self.found_refs: - self.found_refs[original_ref] += 1 - else: - self.found_refs[original_ref] = 1 - return reference - - def findFootnotesPlaceholder(self, root): - """ Return ElementTree Element that contains Footnote placeholder. """ - def finder(element): - for child in element: - if child.text: - if child.text.find(self.getConfig("PLACE_MARKER")) > -1: - return child, element, True - if child.tail: - if child.tail.find(self.getConfig("PLACE_MARKER")) > -1: - return child, element, False - child_res = finder(child) - if child_res is not None: - return child_res - return None - - res = finder(root) - return res - - def setFootnote(self, id, text): - """ Store a footnote for later retrieval. """ - self.footnotes[id] = text - - def get_separator(self): - """ Get the footnote separator. """ - return self.getConfig("SEPARATOR") - - def makeFootnoteId(self, id): - """ Return footnote link id. """ - if self.getConfig("UNIQUE_IDS"): - return 'fn%s%d-%s' % (self.get_separator(), self.unique_prefix, id) - else: - return 'fn{}{}'.format(self.get_separator(), id) - - def makeFootnoteRefId(self, id, found=False): - """ Return footnote back-link id. """ - if self.getConfig("UNIQUE_IDS"): - return self.unique_ref('fnref%s%d-%s' % (self.get_separator(), self.unique_prefix, id), found) - else: - return self.unique_ref('fnref{}{}'.format(self.get_separator(), id), found) - - def makeFootnotesDiv(self, root): - """ Return div of footnotes as et Element. """ - - if not list(self.footnotes.keys()): - return None - - div = etree.Element("div") - div.set('class', 'footnote') - etree.SubElement(div, "hr") - ol = etree.SubElement(div, "ol") - surrogate_parent = etree.Element("div") - - for index, id in enumerate(self.footnotes.keys(), start=1): - li = etree.SubElement(ol, "li") - li.set("id", self.makeFootnoteId(id)) - # Parse footnote with surrogate parent as li cannot be used. - # List block handlers have special logic to deal with li. - # When we are done parsing, we will copy everything over to li. - self.parser.parseChunk(surrogate_parent, self.footnotes[id]) - for el in list(surrogate_parent): - li.append(el) - surrogate_parent.remove(el) - backlink = etree.Element("a") - backlink.set("href", "#" + self.makeFootnoteRefId(id)) - backlink.set("class", "footnote-backref") - backlink.set( - "title", - self.getConfig("BACKLINK_TITLE") % (index) - ) - backlink.text = FN_BACKLINK_TEXT - - if len(li): - node = li[-1] - if node.tag == "p": - node.text = node.text + NBSP_PLACEHOLDER - node.append(backlink) - else: - p = etree.SubElement(li, "p") - p.append(backlink) - return div - - -class FootnoteBlockProcessor(BlockProcessor): - """ Find all footnote references and store for later use. """ - - RE = re.compile(r'^[ ]{0,3}\[\^([^\]]*)\]:[ ]*(.*)$', re.MULTILINE) - - def __init__(self, footnotes): - super().__init__(footnotes.parser) - self.footnotes = footnotes - - def test(self, parent, block): - return True - - def run(self, parent, blocks): - """ Find, set, and remove footnote definitions. """ - block = blocks.pop(0) - m = self.RE.search(block) - if m: - id = m.group(1) - fn_blocks = [m.group(2)] - - # Handle rest of block - therest = block[m.end():].lstrip('\n') - m2 = self.RE.search(therest) - if m2: - # Another footnote exists in the rest of this block. - # Any content before match is continuation of this footnote, which may be lazily indented. - before = therest[:m2.start()].rstrip('\n') - fn_blocks[0] = '\n'.join([fn_blocks[0], self.detab(before)]).lstrip('\n') - # Add back to blocks everything from beginning of match forward for next iteration. - blocks.insert(0, therest[m2.start():]) - else: - # All remaining lines of block are continuation of this footnote, which may be lazily indented. - fn_blocks[0] = '\n'.join([fn_blocks[0], self.detab(therest)]).strip('\n') - - # Check for child elements in remaining blocks. - fn_blocks.extend(self.detectTabbed(blocks)) - - footnote = "\n\n".join(fn_blocks) - self.footnotes.setFootnote(id, footnote.rstrip()) - - if block[:m.start()].strip(): - # Add any content before match back to blocks as separate block - blocks.insert(0, block[:m.start()].rstrip('\n')) - return True - # No match. Restore block. - blocks.insert(0, block) - return False - - def detectTabbed(self, blocks): - """ Find indented text and remove indent before further proccesing. - - Returns: a list of blocks with indentation removed. - """ - fn_blocks = [] - while blocks: - if blocks[0].startswith(' '*4): - block = blocks.pop(0) - # Check for new footnotes within this block and split at new footnote. - m = self.RE.search(block) - if m: - # Another footnote exists in this block. - # Any content before match is continuation of this footnote, which may be lazily indented. - before = block[:m.start()].rstrip('\n') - fn_blocks.append(self.detab(before)) - # Add back to blocks everything from beginning of match forward for next iteration. - blocks.insert(0, block[m.start():]) - # End of this footnote. - break - else: - # Entire block is part of this footnote. - fn_blocks.append(self.detab(block)) - else: - # End of this footnote. - break - return fn_blocks - - def detab(self, block): - """ Remove one level of indent from a block. - - Preserve lazily indented blocks by only removing indent from indented lines. - """ - lines = block.split('\n') - for i, line in enumerate(lines): - if line.startswith(' '*4): - lines[i] = line[4:] - return '\n'.join(lines) - - -class FootnoteInlineProcessor(InlineProcessor): - """ InlinePattern for footnote markers in a document's body text. """ - - def __init__(self, pattern, footnotes): - super().__init__(pattern) - self.footnotes = footnotes - - def handleMatch(self, m, data): - id = m.group(1) - if id in self.footnotes.footnotes.keys(): - sup = etree.Element("sup") - a = etree.SubElement(sup, "a") - sup.set('id', self.footnotes.makeFootnoteRefId(id, found=True)) - a.set('href', '#' + self.footnotes.makeFootnoteId(id)) - a.set('class', 'footnote-ref') - a.text = str(list(self.footnotes.footnotes.keys()).index(id) + 1) - return sup, m.start(0), m.end(0) - else: - return None, None, None - - -class FootnotePostTreeprocessor(Treeprocessor): - """ Amend footnote div with duplicates. """ - - def __init__(self, footnotes): - self.footnotes = footnotes - - def add_duplicates(self, li, duplicates): - """ Adjust current li and add the duplicates: fnref2, fnref3, etc. """ - for link in li.iter('a'): - # Find the link that needs to be duplicated. - if link.attrib.get('class', '') == 'footnote-backref': - ref, rest = link.attrib['href'].split(self.footnotes.get_separator(), 1) - # Duplicate link the number of times we need to - # and point the to the appropriate references. - links = [] - for index in range(2, duplicates + 1): - sib_link = copy.deepcopy(link) - sib_link.attrib['href'] = '%s%d%s%s' % (ref, index, self.footnotes.get_separator(), rest) - links.append(sib_link) - self.offset += 1 - # Add all the new duplicate links. - el = list(li)[-1] - for link in links: - el.append(link) - break - - def get_num_duplicates(self, li): - """ Get the number of duplicate refs of the footnote. """ - fn, rest = li.attrib.get('id', '').split(self.footnotes.get_separator(), 1) - link_id = '{}ref{}{}'.format(fn, self.footnotes.get_separator(), rest) - return self.footnotes.found_refs.get(link_id, 0) - - def handle_duplicates(self, parent): - """ Find duplicate footnotes and format and add the duplicates. """ - for li in list(parent): - # Check number of duplicates footnotes and insert - # additional links if needed. - count = self.get_num_duplicates(li) - if count > 1: - self.add_duplicates(li, count) - - def run(self, root): - """ Crawl the footnote div and add missing duplicate footnotes. """ - self.offset = 0 - for div in root.iter('div'): - if div.attrib.get('class', '') == 'footnote': - # Footnotes should be under the first ordered list under - # the footnote div. So once we find it, quit. - for ol in div.iter('ol'): - self.handle_duplicates(ol) - break - - -class FootnoteTreeprocessor(Treeprocessor): - """ Build and append footnote div to end of document. """ - - def __init__(self, footnotes): - self.footnotes = footnotes - - def run(self, root): - footnotesDiv = self.footnotes.makeFootnotesDiv(root) - if footnotesDiv is not None: - result = self.footnotes.findFootnotesPlaceholder(root) - if result: - child, parent, isText = result - ind = list(parent).index(child) - if isText: - parent.remove(child) - parent.insert(ind, footnotesDiv) - else: - parent.insert(ind + 1, footnotesDiv) - child.tail = None - else: - root.append(footnotesDiv) - - -class FootnotePostprocessor(Postprocessor): - """ Replace placeholders with html entities. """ - def __init__(self, footnotes): - self.footnotes = footnotes - - def run(self, text): - text = text.replace( - FN_BACKLINK_TEXT, self.footnotes.getConfig("BACKLINK_TEXT") - ) - return text.replace(NBSP_PLACEHOLDER, " ") - - -def makeExtension(**kwargs): # pragma: no cover - """ Return an instance of the FootnoteExtension """ - return FootnoteExtension(**kwargs) diff --git a/Source/Libs/markdown/extensions/legacy_attrs.py b/Source/Libs/markdown/extensions/legacy_attrs.py deleted file mode 100644 index 445aba1..0000000 --- a/Source/Libs/markdown/extensions/legacy_attrs.py +++ /dev/null @@ -1,67 +0,0 @@ -""" -Python Markdown - -A Python implementation of John Gruber's Markdown. - -Documentation: https://python-markdown.github.io/ -GitHub: https://github.com/Python-Markdown/markdown/ -PyPI: https://pypi.org/project/Markdown/ - -Started by Manfred Stienstra (http://www.dwerg.net/). -Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org). -Currently maintained by Waylan Limberg (https://github.com/waylan), -Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser). - -Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later) -Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b) -Copyright 2004 Manfred Stienstra (the original version) - -License: BSD (see LICENSE.md for details). - -Legacy Attributes Extension -=========================== - -An extension to Python Markdown which implements legacy attributes. - -Prior to Python-Markdown version 3.0, the Markdown class had an `enable_attributes` -keyword which was on by default and provided for attributes to be defined for elements -using the format `{@key=value}`. This extension is provided as a replacement for -backward compatibility. New documents should be authored using attr_lists. However, -numerious documents exist which have been using the old attribute format for many -years. This extension can be used to continue to render those documents correctly. -""" - -import re -from markdown.treeprocessors import Treeprocessor, isString -from markdown.extensions import Extension - - -ATTR_RE = re.compile(r'\{@([^\}]*)=([^\}]*)}') # {@id=123} - - -class LegacyAttrs(Treeprocessor): - def run(self, doc): - """Find and set values of attributes ({@key=value}). """ - for el in doc.iter(): - alt = el.get('alt', None) - if alt is not None: - el.set('alt', self.handleAttributes(el, alt)) - if el.text and isString(el.text): - el.text = self.handleAttributes(el, el.text) - if el.tail and isString(el.tail): - el.tail = self.handleAttributes(el, el.tail) - - def handleAttributes(self, el, txt): - """ Set attributes and return text without definitions. """ - def attributeCallback(match): - el.set(match.group(1), match.group(2).replace('\n', ' ')) - return ATTR_RE.sub(attributeCallback, txt) - - -class LegacyAttrExtension(Extension): - def extendMarkdown(self, md): - md.treeprocessors.register(LegacyAttrs(md), 'legacyattrs', 15) - - -def makeExtension(**kwargs): # pragma: no cover - return LegacyAttrExtension(**kwargs) diff --git a/Source/Libs/markdown/extensions/legacy_em.py b/Source/Libs/markdown/extensions/legacy_em.py deleted file mode 100644 index 360988b..0000000 --- a/Source/Libs/markdown/extensions/legacy_em.py +++ /dev/null @@ -1,49 +0,0 @@ -''' -Legacy Em Extension for Python-Markdown -======================================= - -This extension provides legacy behavior for _connected_words_. - -Copyright 2015-2018 The Python Markdown Project - -License: [BSD](https://opensource.org/licenses/bsd-license.php) - -''' - -from . import Extension -from ..inlinepatterns import UnderscoreProcessor, EmStrongItem, EM_STRONG2_RE, STRONG_EM2_RE -import re - -# _emphasis_ -EMPHASIS_RE = r'(_)([^_]+)\1' - -# __strong__ -STRONG_RE = r'(_{2})(.+?)\1' - -# __strong_em___ -STRONG_EM_RE = r'(_)\1(?!\1)([^_]+?)\1(?!\1)(.+?)\1{3}' - - -class LegacyUnderscoreProcessor(UnderscoreProcessor): - """Emphasis processor for handling strong and em matches inside underscores.""" - - PATTERNS = [ - EmStrongItem(re.compile(EM_STRONG2_RE, re.DOTALL | re.UNICODE), 'double', 'strong,em'), - EmStrongItem(re.compile(STRONG_EM2_RE, re.DOTALL | re.UNICODE), 'double', 'em,strong'), - EmStrongItem(re.compile(STRONG_EM_RE, re.DOTALL | re.UNICODE), 'double2', 'strong,em'), - EmStrongItem(re.compile(STRONG_RE, re.DOTALL | re.UNICODE), 'single', 'strong'), - EmStrongItem(re.compile(EMPHASIS_RE, re.DOTALL | re.UNICODE), 'single', 'em') - ] - - -class LegacyEmExtension(Extension): - """ Add legacy_em extension to Markdown class.""" - - def extendMarkdown(self, md): - """ Modify inline patterns. """ - md.inlinePatterns.register(LegacyUnderscoreProcessor(r'_'), 'em_strong2', 50) - - -def makeExtension(**kwargs): # pragma: no cover - """ Return an instance of the LegacyEmExtension """ - return LegacyEmExtension(**kwargs) diff --git a/Source/Libs/markdown/extensions/md_in_html.py b/Source/Libs/markdown/extensions/md_in_html.py deleted file mode 100644 index ec7dcba..0000000 --- a/Source/Libs/markdown/extensions/md_in_html.py +++ /dev/null @@ -1,364 +0,0 @@ -""" -Python-Markdown Markdown in HTML Extension -=============================== - -An implementation of [PHP Markdown Extra](http://michelf.com/projects/php-markdown/extra/)'s -parsing of Markdown syntax in raw HTML. - -See <https://Python-Markdown.github.io/extensions/raw_html> -for documentation. - -Copyright The Python Markdown Project - -License: [BSD](https://opensource.org/licenses/bsd-license.php) - -""" - -from . import Extension -from ..blockprocessors import BlockProcessor -from ..preprocessors import Preprocessor -from ..postprocessors import RawHtmlPostprocessor -from .. import util -from ..htmlparser import HTMLExtractor, blank_line_re -import xml.etree.ElementTree as etree - - -class HTMLExtractorExtra(HTMLExtractor): - """ - Override HTMLExtractor and create etree Elements for any elements which should have content parsed as Markdown. - """ - - def __init__(self, md, *args, **kwargs): - # All block-level tags. - self.block_level_tags = set(md.block_level_elements.copy()) - # Block-level tags in which the content only gets span level parsing - self.span_tags = set( - ['address', 'dd', 'dt', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'legend', 'li', 'p', 'summary', 'td', 'th'] - ) - # Block-level tags which never get their content parsed. - self.raw_tags = set(['canvas', 'math', 'option', 'pre', 'script', 'style', 'textarea']) - - super().__init__(md, *args, **kwargs) - - # Block-level tags in which the content gets parsed as blocks - self.block_tags = set(self.block_level_tags) - (self.span_tags | self.raw_tags | self.empty_tags) - self.span_and_blocks_tags = self.block_tags | self.span_tags - - def reset(self): - """Reset this instance. Loses all unprocessed data.""" - self.mdstack = [] # When markdown=1, stack contains a list of tags - self.treebuilder = etree.TreeBuilder() - self.mdstate = [] # one of 'block', 'span', 'off', or None - super().reset() - - def close(self): - """Handle any buffered data.""" - super().close() - # Handle any unclosed tags. - if self.mdstack: - # Close the outermost parent. handle_endtag will close all unclosed children. - self.handle_endtag(self.mdstack[0]) - - def get_element(self): - """ Return element from treebuilder and reset treebuilder for later use. """ - element = self.treebuilder.close() - self.treebuilder = etree.TreeBuilder() - return element - - def get_state(self, tag, attrs): - """ Return state from tag and `markdown` attr. One of 'block', 'span', or 'off'. """ - md_attr = attrs.get('markdown', '0') - if md_attr == 'markdown': - # `<tag markdown>` is the same as `<tag markdown='1'>`. - md_attr = '1' - parent_state = self.mdstate[-1] if self.mdstate else None - if parent_state == 'off' or (parent_state == 'span' and md_attr != '0'): - # Only use the parent state if it is more restrictive than the markdown attribute. - md_attr = parent_state - if ((md_attr == '1' and tag in self.block_tags) or - (md_attr == 'block' and tag in self.span_and_blocks_tags)): - return 'block' - elif ((md_attr == '1' and tag in self.span_tags) or - (md_attr == 'span' and tag in self.span_and_blocks_tags)): - return 'span' - elif tag in self.block_level_tags: - return 'off' - else: # pragma: no cover - return None - - def handle_starttag(self, tag, attrs): - # Handle tags that should always be empty and do not specify a closing tag - if tag in self.empty_tags and (self.at_line_start() or self.intail): - attrs = {key: value if value is not None else key for key, value in attrs} - if "markdown" in attrs: - attrs.pop('markdown') - element = etree.Element(tag, attrs) - data = etree.tostring(element, encoding='unicode', method='html') - else: - data = self.get_starttag_text() - self.handle_empty_tag(data, True) - return - - if tag in self.block_level_tags and (self.at_line_start() or self.intail): - # Valueless attr (ex: `<tag checked>`) results in `[('checked', None)]`. - # Convert to `{'checked': 'checked'}`. - attrs = {key: value if value is not None else key for key, value in attrs} - state = self.get_state(tag, attrs) - if self.inraw or (state in [None, 'off'] and not self.mdstack): - # fall back to default behavior - attrs.pop('markdown', None) - super().handle_starttag(tag, attrs) - else: - if 'p' in self.mdstack and tag in self.block_level_tags: - # Close unclosed 'p' tag - self.handle_endtag('p') - self.mdstate.append(state) - self.mdstack.append(tag) - attrs['markdown'] = state - self.treebuilder.start(tag, attrs) - else: - # Span level tag - if self.inraw: - super().handle_starttag(tag, attrs) - else: - text = self.get_starttag_text() - if self.mdstate and self.mdstate[-1] == "off": - self.handle_data(self.md.htmlStash.store(text)) - else: - self.handle_data(text) - if tag in self.CDATA_CONTENT_ELEMENTS: - # This is presumably a standalone tag in a code span (see #1036). - self.clear_cdata_mode() - - def handle_endtag(self, tag): - if tag in self.block_level_tags: - if self.inraw: - super().handle_endtag(tag) - elif tag in self.mdstack: - # Close element and any unclosed children - while self.mdstack: - item = self.mdstack.pop() - self.mdstate.pop() - self.treebuilder.end(item) - if item == tag: - break - if not self.mdstack: - # Last item in stack is closed. Stash it - element = self.get_element() - # Get last entry to see if it ends in newlines - # If it is an element, assume there is no newlines - item = self.cleandoc[-1] if self.cleandoc else '' - # If we only have one newline before block element, add another - if not item.endswith('\n\n') and item.endswith('\n'): - self.cleandoc.append('\n') - self.cleandoc.append(self.md.htmlStash.store(element)) - self.cleandoc.append('\n\n') - self.state = [] - # Check if element has a tail - if not blank_line_re.match( - self.rawdata[self.line_offset + self.offset + len(self.get_endtag_text(tag)):]): - # More content exists after endtag. - self.intail = True - else: - # Treat orphan closing tag as a span level tag. - text = self.get_endtag_text(tag) - if self.mdstate and self.mdstate[-1] == "off": - self.handle_data(self.md.htmlStash.store(text)) - else: - self.handle_data(text) - else: - # Span level tag - if self.inraw: - super().handle_endtag(tag) - else: - text = self.get_endtag_text(tag) - if self.mdstate and self.mdstate[-1] == "off": - self.handle_data(self.md.htmlStash.store(text)) - else: - self.handle_data(text) - - def handle_startendtag(self, tag, attrs): - if tag in self.empty_tags: - attrs = {key: value if value is not None else key for key, value in attrs} - if "markdown" in attrs: - attrs.pop('markdown') - element = etree.Element(tag, attrs) - data = etree.tostring(element, encoding='unicode', method='html') - else: - data = self.get_starttag_text() - else: - data = self.get_starttag_text() - self.handle_empty_tag(data, is_block=self.md.is_block_level(tag)) - - def handle_data(self, data): - if self.intail and '\n' in data: - self.intail = False - if self.inraw or not self.mdstack: - super().handle_data(data) - else: - self.treebuilder.data(data) - - def handle_empty_tag(self, data, is_block): - if self.inraw or not self.mdstack: - super().handle_empty_tag(data, is_block) - else: - if self.at_line_start() and is_block: - self.handle_data('\n' + self.md.htmlStash.store(data) + '\n\n') - else: - self.handle_data(self.md.htmlStash.store(data)) - - def parse_pi(self, i): - if self.at_line_start() or self.intail or self.mdstack: - # The same override exists in HTMLExtractor without the check - # for mdstack. Therefore, use HTMLExtractor's parent instead. - return super(HTMLExtractor, self).parse_pi(i) - # This is not the beginning of a raw block so treat as plain data - # and avoid consuming any tags which may follow (see #1066). - self.handle_data('<?') - return i + 2 - - def parse_html_declaration(self, i): - if self.at_line_start() or self.intail or self.mdstack: - # The same override exists in HTMLExtractor without the check - # for mdstack. Therefore, use HTMLExtractor's parent instead. - return super(HTMLExtractor, self).parse_html_declaration(i) - # This is not the beginning of a raw block so treat as plain data - # and avoid consuming any tags which may follow (see #1066). - self.handle_data('<!') - return i + 2 - - -class HtmlBlockPreprocessor(Preprocessor): - """Remove html blocks from the text and store them for later retrieval.""" - - def run(self, lines): - source = '\n'.join(lines) - parser = HTMLExtractorExtra(self.md) - parser.feed(source) - parser.close() - return ''.join(parser.cleandoc).split('\n') - - -class MarkdownInHtmlProcessor(BlockProcessor): - """Process Markdown Inside HTML Blocks which have been stored in the HtmlStash.""" - - def test(self, parent, block): - # ALways return True. `run` will return `False` it not a valid match. - return True - - def parse_element_content(self, element): - """ - Recursively parse the text content of an etree Element as Markdown. - - Any block level elements generated from the Markdown will be inserted as children of the element in place - of the text content. All `markdown` attributes are removed. For any elements in which Markdown parsing has - been disabled, the text content of it and its chidlren are wrapped in an `AtomicString`. - """ - - md_attr = element.attrib.pop('markdown', 'off') - - if md_attr == 'block': - # Parse content as block level - # The order in which the different parts are parsed (text, children, tails) is important here as the - # order of elements needs to be preserved. We can't be inserting items at a later point in the current - # iteration as we don't want to do raw processing on elements created from parsing Markdown text (for - # example). Therefore, the order of operations is children, tails, text. - - # Recursively parse existing children from raw HTML - for child in list(element): - self.parse_element_content(child) - - # Parse Markdown text in tail of children. Do this separate to avoid raw HTML parsing. - # Save the position of each item to be inserted later in reverse. - tails = [] - for pos, child in enumerate(element): - if child.tail: - block = child.tail.rstrip('\n') - child.tail = '' - # Use a dummy placeholder element. - dummy = etree.Element('div') - self.parser.parseBlocks(dummy, block.split('\n\n')) - children = list(dummy) - children.reverse() - tails.append((pos + 1, children)) - - # Insert the elements created from the tails in reverse. - tails.reverse() - for pos, tail in tails: - for item in tail: - element.insert(pos, item) - - # Parse Markdown text content. Do this last to avoid raw HTML parsing. - if element.text: - block = element.text.rstrip('\n') - element.text = '' - # Use a dummy placeholder element as the content needs to get inserted before existing children. - dummy = etree.Element('div') - self.parser.parseBlocks(dummy, block.split('\n\n')) - children = list(dummy) - children.reverse() - for child in children: - element.insert(0, child) - - elif md_attr == 'span': - # Span level parsing will be handled by inlineprocessors. - # Walk children here to remove any `markdown` attributes. - for child in list(element): - self.parse_element_content(child) - - else: - # Disable inline parsing for everything else - if element.text is None: - element.text = '' - element.text = util.AtomicString(element.text) - for child in list(element): - self.parse_element_content(child) - if child.tail: - child.tail = util.AtomicString(child.tail) - - def run(self, parent, blocks): - m = util.HTML_PLACEHOLDER_RE.match(blocks[0]) - if m: - index = int(m.group(1)) - element = self.parser.md.htmlStash.rawHtmlBlocks[index] - if isinstance(element, etree.Element): - # We have a matched element. Process it. - blocks.pop(0) - self.parse_element_content(element) - parent.append(element) - # Cleanup stash. Replace element with empty string to avoid confusing postprocessor. - self.parser.md.htmlStash.rawHtmlBlocks.pop(index) - self.parser.md.htmlStash.rawHtmlBlocks.insert(index, '') - # Confirm the match to the blockparser. - return True - # No match found. - return False - - -class MarkdownInHTMLPostprocessor(RawHtmlPostprocessor): - def stash_to_string(self, text): - """ Override default to handle any etree elements still in the stash. """ - if isinstance(text, etree.Element): - return self.md.serializer(text) - else: - return str(text) - - -class MarkdownInHtmlExtension(Extension): - """Add Markdown parsing in HTML to Markdown class.""" - - def extendMarkdown(self, md): - """ Register extension instances. """ - - # Replace raw HTML preprocessor - md.preprocessors.register(HtmlBlockPreprocessor(md), 'html_block', 20) - # Add blockprocessor which handles the placeholders for etree elements - md.parser.blockprocessors.register( - MarkdownInHtmlProcessor(md.parser), 'markdown_block', 105 - ) - # Replace raw HTML postprocessor - md.postprocessors.register(MarkdownInHTMLPostprocessor(md), 'raw_html', 30) - - -def makeExtension(**kwargs): # pragma: no cover - return MarkdownInHtmlExtension(**kwargs) diff --git a/Source/Libs/markdown/extensions/meta.py b/Source/Libs/markdown/extensions/meta.py deleted file mode 100644 index 10dee11..0000000 --- a/Source/Libs/markdown/extensions/meta.py +++ /dev/null @@ -1,79 +0,0 @@ -""" -Meta Data Extension for Python-Markdown -======================================= - -This extension adds Meta Data handling to markdown. - -See <https://Python-Markdown.github.io/extensions/meta_data> -for documentation. - -Original code Copyright 2007-2008 [Waylan Limberg](http://achinghead.com). - -All changes Copyright 2008-2014 The Python Markdown Project - -License: [BSD](https://opensource.org/licenses/bsd-license.php) - -""" - -from . import Extension -from ..preprocessors import Preprocessor -import re -import logging - -log = logging.getLogger('MARKDOWN') - -# Global Vars -META_RE = re.compile(r'^[ ]{0,3}(?P<key>[A-Za-z0-9_-]+):\s*(?P<value>.*)') -META_MORE_RE = re.compile(r'^[ ]{4,}(?P<value>.*)') -BEGIN_RE = re.compile(r'^-{3}(\s.*)?') -END_RE = re.compile(r'^(-{3}|\.{3})(\s.*)?') - - -class MetaExtension (Extension): - """ Meta-Data extension for Python-Markdown. """ - - def extendMarkdown(self, md): - """ Add MetaPreprocessor to Markdown instance. """ - md.registerExtension(self) - self.md = md - md.preprocessors.register(MetaPreprocessor(md), 'meta', 27) - - def reset(self): - self.md.Meta = {} - - -class MetaPreprocessor(Preprocessor): - """ Get Meta-Data. """ - - def run(self, lines): - """ Parse Meta-Data and store in Markdown.Meta. """ - meta = {} - key = None - if lines and BEGIN_RE.match(lines[0]): - lines.pop(0) - while lines: - line = lines.pop(0) - m1 = META_RE.match(line) - if line.strip() == '' or END_RE.match(line): - break # blank line or end of YAML header - done - if m1: - key = m1.group('key').lower().strip() - value = m1.group('value').strip() - try: - meta[key].append(value) - except KeyError: - meta[key] = [value] - else: - m2 = META_MORE_RE.match(line) - if m2 and key: - # Add another line to existing key - meta[key].append(m2.group('value').strip()) - else: - lines.insert(0, line) - break # no meta data - done - self.md.Meta = meta - return lines - - -def makeExtension(**kwargs): # pragma: no cover - return MetaExtension(**kwargs) diff --git a/Source/Libs/markdown/extensions/nl2br.py b/Source/Libs/markdown/extensions/nl2br.py deleted file mode 100644 index 6c7491b..0000000 --- a/Source/Libs/markdown/extensions/nl2br.py +++ /dev/null @@ -1,33 +0,0 @@ -""" -NL2BR Extension -=============== - -A Python-Markdown extension to treat newlines as hard breaks; like -GitHub-flavored Markdown does. - -See <https://Python-Markdown.github.io/extensions/nl2br> -for documentation. - -Oringinal code Copyright 2011 [Brian Neal](https://deathofagremmie.com/) - -All changes Copyright 2011-2014 The Python Markdown Project - -License: [BSD](https://opensource.org/licenses/bsd-license.php) - -""" - -from . import Extension -from ..inlinepatterns import SubstituteTagInlineProcessor - -BR_RE = r'\n' - - -class Nl2BrExtension(Extension): - - def extendMarkdown(self, md): - br_tag = SubstituteTagInlineProcessor(BR_RE, 'br') - md.inlinePatterns.register(br_tag, 'nl', 5) - - -def makeExtension(**kwargs): # pragma: no cover - return Nl2BrExtension(**kwargs) diff --git a/Source/Libs/markdown/extensions/sane_lists.py b/Source/Libs/markdown/extensions/sane_lists.py deleted file mode 100644 index e27eb18..0000000 --- a/Source/Libs/markdown/extensions/sane_lists.py +++ /dev/null @@ -1,54 +0,0 @@ -""" -Sane List Extension for Python-Markdown -======================================= - -Modify the behavior of Lists in Python-Markdown to act in a sane manor. - -See <https://Python-Markdown.github.io/extensions/sane_lists> -for documentation. - -Original code Copyright 2011 [Waylan Limberg](http://achinghead.com) - -All changes Copyright 2011-2014 The Python Markdown Project - -License: [BSD](https://opensource.org/licenses/bsd-license.php) - -""" - -from . import Extension -from ..blockprocessors import OListProcessor, UListProcessor -import re - - -class SaneOListProcessor(OListProcessor): - - SIBLING_TAGS = ['ol'] - LAZY_OL = False - - def __init__(self, parser): - super().__init__(parser) - self.CHILD_RE = re.compile(r'^[ ]{0,%d}((\d+\.))[ ]+(.*)' % - (self.tab_length - 1)) - - -class SaneUListProcessor(UListProcessor): - - SIBLING_TAGS = ['ul'] - - def __init__(self, parser): - super().__init__(parser) - self.CHILD_RE = re.compile(r'^[ ]{0,%d}(([*+-]))[ ]+(.*)' % - (self.tab_length - 1)) - - -class SaneListExtension(Extension): - """ Add sane lists to Markdown. """ - - def extendMarkdown(self, md): - """ Override existing Processors. """ - md.parser.blockprocessors.register(SaneOListProcessor(md.parser), 'olist', 40) - md.parser.blockprocessors.register(SaneUListProcessor(md.parser), 'ulist', 30) - - -def makeExtension(**kwargs): # pragma: no cover - return SaneListExtension(**kwargs) diff --git a/Source/Libs/markdown/extensions/smarty.py b/Source/Libs/markdown/extensions/smarty.py deleted file mode 100644 index 894805f..0000000 --- a/Source/Libs/markdown/extensions/smarty.py +++ /dev/null @@ -1,263 +0,0 @@ -''' -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. - -''' - - -from . import Extension -from ..inlinepatterns import HtmlInlineProcessor, HTML_RE -from ..treeprocessors import InlineProcessor -from ..util import Registry, deprecated - - -# 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|s\b)" % closeClass - -# All remaining quotes should be opening ones -remainingSingleQuotesRegex = r"'" -remainingDoubleQuotesRegex = r'"' - -HTML_STRICT_RE = HTML_RE + r'(?!\>)' - - -class SubstituteTextPattern(HtmlInlineProcessor): - def __init__(self, pattern, replace, md): - """ Replaces matches with some text. """ - HtmlInlineProcessor.__init__(self, pattern) - self.replace = replace - self.md = md - - @property - @deprecated("Use 'md' instead.") - def markdown(self): - # TODO: remove this later - return self.md - - def handleMatch(self, m, data): - 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): - 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'], - } - super().__init__(**kwargs) - self.substitutions = dict(substitutions) - self.substitutions.update(self.getConfig('substitutions', default={})) - - def _addPatterns(self, md, patterns, serie, priority): - 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): - 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): - ellipsesPattern = SubstituteTextPattern( - r'(?<!\.)\.{3}(?!\.)', (self.substitutions['ellipsis'],), md - ) - self.inlinePatterns.register(ellipsesPattern, 'smarty-ellipses', 10) - - def educateAngledQuotes(self, md): - 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): - 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() - 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) diff --git a/Source/Libs/markdown/extensions/tables.py b/Source/Libs/markdown/extensions/tables.py deleted file mode 100644 index 0a9d084..0000000 --- a/Source/Libs/markdown/extensions/tables.py +++ /dev/null @@ -1,223 +0,0 @@ -""" -Tables Extension for Python-Markdown -==================================== - -Added parsing of tables to Python-Markdown. - -See <https://Python-Markdown.github.io/extensions/tables> -for documentation. - -Original code Copyright 2009 [Waylan Limberg](http://achinghead.com) - -All changes Copyright 2008-2014 The Python Markdown Project - -License: [BSD](https://opensource.org/licenses/bsd-license.php) - -""" - -from . import Extension -from ..blockprocessors import BlockProcessor -import xml.etree.ElementTree as etree -import re -PIPE_NONE = 0 -PIPE_LEFT = 1 -PIPE_RIGHT = 2 - - -class TableProcessor(BlockProcessor): - """ Process Tables. """ - - RE_CODE_PIPES = re.compile(r'(?:(\\\\)|(\\`+)|(`+)|(\\\|)|(\|))') - RE_END_BORDER = re.compile(r'(?<!\\)(?:\\\\)*\|$') - - def __init__(self, parser): - self.border = False - self.separator = '' - super().__init__(parser) - - def test(self, parent, block): - """ - Ensure first two rows (column header and separator row) are valid table rows. - - Keep border check and separator row do avoid repeating the work. - """ - is_table = False - rows = [row.strip(' ') for row in block.split('\n')] - if len(rows) > 1: - header0 = rows[0] - self.border = PIPE_NONE - if header0.startswith('|'): - self.border |= PIPE_LEFT - if self.RE_END_BORDER.search(header0) is not None: - self.border |= PIPE_RIGHT - row = self._split_row(header0) - row0_len = len(row) - is_table = row0_len > 1 - - # Each row in a single column table needs at least one pipe. - if not is_table and row0_len == 1 and self.border: - for index in range(1, len(rows)): - is_table = rows[index].startswith('|') - if not is_table: - is_table = self.RE_END_BORDER.search(rows[index]) is not None - if not is_table: - break - - if is_table: - row = self._split_row(rows[1]) - is_table = (len(row) == row0_len) and set(''.join(row)) <= set('|:- ') - if is_table: - self.separator = row - - return is_table - - def run(self, parent, blocks): - """ Parse a table block and build table. """ - block = blocks.pop(0).split('\n') - header = block[0].strip(' ') - rows = [] if len(block) < 3 else block[2:] - - # Get alignment of columns - align = [] - for c in self.separator: - c = c.strip(' ') - if c.startswith(':') and c.endswith(':'): - align.append('center') - elif c.startswith(':'): - align.append('left') - elif c.endswith(':'): - align.append('right') - else: - align.append(None) - - # Build table - table = etree.SubElement(parent, 'table') - thead = etree.SubElement(table, 'thead') - self._build_row(header, thead, align) - tbody = etree.SubElement(table, 'tbody') - if len(rows) == 0: - # Handle empty table - self._build_empty_row(tbody, align) - else: - for row in rows: - self._build_row(row.strip(' '), tbody, align) - - def _build_empty_row(self, parent, align): - """Build an empty row.""" - tr = etree.SubElement(parent, 'tr') - count = len(align) - while count: - etree.SubElement(tr, 'td') - count -= 1 - - def _build_row(self, row, parent, align): - """ Given a row of text, build table cells. """ - tr = etree.SubElement(parent, 'tr') - tag = 'td' - if parent.tag == 'thead': - tag = 'th' - cells = self._split_row(row) - # We use align here rather than cells to ensure every row - # contains the same number of columns. - for i, a in enumerate(align): - c = etree.SubElement(tr, tag) - try: - c.text = cells[i].strip(' ') - except IndexError: # pragma: no cover - c.text = "" - if a: - c.set('align', a) - - def _split_row(self, row): - """ split a row of text into list of cells. """ - if self.border: - if row.startswith('|'): - row = row[1:] - row = self.RE_END_BORDER.sub('', row) - return self._split(row) - - def _split(self, row): - """ split a row of text with some code into a list of cells. """ - elements = [] - pipes = [] - tics = [] - tic_points = [] - tic_region = [] - good_pipes = [] - - # Parse row - # Throw out \\, and \| - for m in self.RE_CODE_PIPES.finditer(row): - # Store ` data (len, start_pos, end_pos) - if m.group(2): - # \`+ - # Store length of each tic group: subtract \ - tics.append(len(m.group(2)) - 1) - # Store start of group, end of group, and escape length - tic_points.append((m.start(2), m.end(2) - 1, 1)) - elif m.group(3): - # `+ - # Store length of each tic group - tics.append(len(m.group(3))) - # Store start of group, end of group, and escape length - tic_points.append((m.start(3), m.end(3) - 1, 0)) - # Store pipe location - elif m.group(5): - pipes.append(m.start(5)) - - # Pair up tics according to size if possible - # Subtract the escape length *only* from the opening. - # Walk through tic list and see if tic has a close. - # Store the tic region (start of region, end of region). - pos = 0 - tic_len = len(tics) - while pos < tic_len: - try: - tic_size = tics[pos] - tic_points[pos][2] - if tic_size == 0: - raise ValueError - index = tics[pos + 1:].index(tic_size) + 1 - tic_region.append((tic_points[pos][0], tic_points[pos + index][1])) - pos += index + 1 - except ValueError: - pos += 1 - - # Resolve pipes. Check if they are within a tic pair region. - # Walk through pipes comparing them to each region. - # - If pipe position is less that a region, it isn't in a region - # - If it is within a region, we don't want it, so throw it out - # - If we didn't throw it out, it must be a table pipe - for pipe in pipes: - throw_out = False - for region in tic_region: - if pipe < region[0]: - # Pipe is not in a region - break - elif region[0] <= pipe <= region[1]: - # Pipe is within a code region. Throw it out. - throw_out = True - break - if not throw_out: - good_pipes.append(pipe) - - # Split row according to table delimiters. - pos = 0 - for pipe in good_pipes: - elements.append(row[pos:pipe]) - pos = pipe + 1 - elements.append(row[pos:]) - return elements - - -class TableExtension(Extension): - """ Add tables to Markdown. """ - - def extendMarkdown(self, md): - """ Add an instance of TableProcessor to BlockParser. """ - if '|' not in md.ESCAPED_CHARS: - md.ESCAPED_CHARS.append('|') - md.parser.blockprocessors.register(TableProcessor(md.parser), 'table', 75) - - -def makeExtension(**kwargs): # pragma: no cover - return TableExtension(**kwargs) diff --git a/Source/Libs/markdown/extensions/toc.py b/Source/Libs/markdown/extensions/toc.py deleted file mode 100644 index 57d2e3b..0000000 --- a/Source/Libs/markdown/extensions/toc.py +++ /dev/null @@ -1,380 +0,0 @@ -""" -Table of Contents Extension for Python-Markdown -=============================================== - -See <https://Python-Markdown.github.io/extensions/toc> -for documentation. - -Oringinal code Copyright 2008 [Jack Miller](https://codezen.org/) - -All changes Copyright 2008-2014 The Python Markdown Project - -License: [BSD](https://opensource.org/licenses/bsd-license.php) - -""" - -from . import Extension -from ..treeprocessors import Treeprocessor -from ..util import code_escape, parseBoolValue, AMP_SUBSTITUTE, HTML_PLACEHOLDER_RE, AtomicString -from ..postprocessors import UnescapePostprocessor -import re -import html -import unicodedata -import xml.etree.ElementTree as etree - - -def slugify(value, separator, unicode=False): - """ Slugify a string, to make it URL friendly. """ - if not unicode: - # Replace Extended Latin characters with ASCII, i.e. žlutý → zluty - value = unicodedata.normalize('NFKD', value) - value = value.encode('ascii', 'ignore').decode('ascii') - value = re.sub(r'[^\w\s-]', '', value).strip().lower() - return re.sub(r'[{}\s]+'.format(separator), separator, value) - - -def slugify_unicode(value, separator): - """ Slugify a string, to make it URL friendly while preserving Unicode characters. """ - return slugify(value, separator, unicode=True) - - -IDCOUNT_RE = re.compile(r'^(.*)_([0-9]+)$') - - -def unique(id, ids): - """ Ensure id is unique in set of ids. Append '_1', '_2'... if not """ - while id in ids or not id: - m = IDCOUNT_RE.match(id) - if m: - id = '%s_%d' % (m.group(1), int(m.group(2))+1) - else: - id = '%s_%d' % (id, 1) - ids.add(id) - return id - - -def get_name(el): - """Get title name.""" - - text = [] - for c in el.itertext(): - if isinstance(c, AtomicString): - text.append(html.unescape(c)) - else: - text.append(c) - return ''.join(text).strip() - - -def stashedHTML2text(text, md, strip_entities=True): - """ Extract raw HTML from stash, reduce to plain text and swap with placeholder. """ - def _html_sub(m): - """ Substitute raw html with plain text. """ - try: - raw = md.htmlStash.rawHtmlBlocks[int(m.group(1))] - except (IndexError, TypeError): # pragma: no cover - return m.group(0) - # Strip out tags and/or entities - leaving text - res = re.sub(r'(<[^>]+>)', '', raw) - if strip_entities: - res = re.sub(r'(&[\#a-zA-Z0-9]+;)', '', res) - return res - - return HTML_PLACEHOLDER_RE.sub(_html_sub, text) - - -def unescape(text): - """ Unescape escaped text. """ - c = UnescapePostprocessor() - return c.run(text) - - -def nest_toc_tokens(toc_list): - """Given an unsorted list with errors and skips, return a nested one. - [{'level': 1}, {'level': 2}] - => - [{'level': 1, 'children': [{'level': 2, 'children': []}]}] - - A wrong list is also converted: - [{'level': 2}, {'level': 1}] - => - [{'level': 2, 'children': []}, {'level': 1, 'children': []}] - """ - - ordered_list = [] - if len(toc_list): - # Initialize everything by processing the first entry - last = toc_list.pop(0) - last['children'] = [] - levels = [last['level']] - ordered_list.append(last) - parents = [] - - # Walk the rest nesting the entries properly - while toc_list: - t = toc_list.pop(0) - current_level = t['level'] - t['children'] = [] - - # Reduce depth if current level < last item's level - if current_level < levels[-1]: - # Pop last level since we know we are less than it - levels.pop() - - # Pop parents and levels we are less than or equal to - to_pop = 0 - for p in reversed(parents): - if current_level <= p['level']: - to_pop += 1 - else: # pragma: no cover - break - if to_pop: - levels = levels[:-to_pop] - parents = parents[:-to_pop] - - # Note current level as last - levels.append(current_level) - - # Level is the same, so append to - # the current parent (if available) - if current_level == levels[-1]: - (parents[-1]['children'] if parents - else ordered_list).append(t) - - # Current level is > last item's level, - # So make last item a parent and append current as child - else: - last['children'].append(t) - parents.append(last) - levels.append(current_level) - last = t - - return ordered_list - - -class TocTreeprocessor(Treeprocessor): - def __init__(self, md, config): - super().__init__(md) - - self.marker = config["marker"] - self.title = config["title"] - self.base_level = int(config["baselevel"]) - 1 - self.slugify = config["slugify"] - self.sep = config["separator"] - self.use_anchors = parseBoolValue(config["anchorlink"]) - self.anchorlink_class = config["anchorlink_class"] - self.use_permalinks = parseBoolValue(config["permalink"], False) - if self.use_permalinks is None: - self.use_permalinks = config["permalink"] - self.permalink_class = config["permalink_class"] - self.permalink_title = config["permalink_title"] - self.header_rgx = re.compile("[Hh][123456]") - if isinstance(config["toc_depth"], str) and '-' in config["toc_depth"]: - self.toc_top, self.toc_bottom = [int(x) for x in config["toc_depth"].split('-')] - else: - self.toc_top = 1 - self.toc_bottom = int(config["toc_depth"]) - - def iterparent(self, node): - ''' Iterator wrapper to get allowed parent and child all at once. ''' - - # We do not allow the marker inside a header as that - # would causes an enless loop of placing a new TOC - # inside previously generated TOC. - for child in node: - if not self.header_rgx.match(child.tag) and child.tag not in ['pre', 'code']: - yield node, child - yield from self.iterparent(child) - - def replace_marker(self, root, elem): - ''' Replace marker with elem. ''' - for (p, c) in self.iterparent(root): - text = ''.join(c.itertext()).strip() - if not text: - continue - - # To keep the output from screwing up the - # validation by putting a <div> inside of a <p> - # we actually replace the <p> in its entirety. - - # The <p> element may contain more than a single text content - # (nl2br can introduce a <br>). In this situation, c.text returns - # the very first content, ignore children contents or tail content. - # len(c) == 0 is here to ensure there is only text in the <p>. - if c.text and c.text.strip() == self.marker and len(c) == 0: - for i in range(len(p)): - if p[i] == c: - p[i] = elem - break - - def set_level(self, elem): - ''' Adjust header level according to base level. ''' - level = int(elem.tag[-1]) + self.base_level - if level > 6: - level = 6 - elem.tag = 'h%d' % level - - def add_anchor(self, c, elem_id): # @ReservedAssignment - anchor = etree.Element("a") - anchor.text = c.text - anchor.attrib["href"] = "#" + elem_id - anchor.attrib["class"] = self.anchorlink_class - c.text = "" - for elem in c: - anchor.append(elem) - while len(c): - c.remove(c[0]) - c.append(anchor) - - def add_permalink(self, c, elem_id): - permalink = etree.Element("a") - permalink.text = ("%spara;" % AMP_SUBSTITUTE - if self.use_permalinks is True - else self.use_permalinks) - permalink.attrib["href"] = "#" + elem_id - permalink.attrib["class"] = self.permalink_class - if self.permalink_title: - permalink.attrib["title"] = self.permalink_title - c.append(permalink) - - def build_toc_div(self, toc_list): - """ Return a string div given a toc list. """ - div = etree.Element("div") - div.attrib["class"] = "toc" - - # Add title to the div - if self.title: - header = etree.SubElement(div, "span") - header.attrib["class"] = "toctitle" - header.text = self.title - - def build_etree_ul(toc_list, parent): - ul = etree.SubElement(parent, "ul") - for item in toc_list: - # List item link, to be inserted into the toc div - li = etree.SubElement(ul, "li") - link = etree.SubElement(li, "a") - link.text = item.get('name', '') - link.attrib["href"] = '#' + item.get('id', '') - if item['children']: - build_etree_ul(item['children'], li) - return ul - - build_etree_ul(toc_list, div) - - if 'prettify' in self.md.treeprocessors: - self.md.treeprocessors['prettify'].run(div) - - return div - - def run(self, doc): - # Get a list of id attributes - used_ids = set() - for el in doc.iter(): - if "id" in el.attrib: - used_ids.add(el.attrib["id"]) - - toc_tokens = [] - for el in doc.iter(): - if isinstance(el.tag, str) and self.header_rgx.match(el.tag): - self.set_level(el) - text = get_name(el) - - # Do not override pre-existing ids - if "id" not in el.attrib: - innertext = unescape(stashedHTML2text(text, self.md)) - el.attrib["id"] = unique(self.slugify(innertext, self.sep), used_ids) - - if int(el.tag[-1]) >= self.toc_top and int(el.tag[-1]) <= self.toc_bottom: - toc_tokens.append({ - 'level': int(el.tag[-1]), - 'id': el.attrib["id"], - 'name': unescape(stashedHTML2text( - code_escape(el.attrib.get('data-toc-label', text)), - self.md, strip_entities=False - )) - }) - - # Remove the data-toc-label attribute as it is no longer needed - if 'data-toc-label' in el.attrib: - del el.attrib['data-toc-label'] - - if self.use_anchors: - self.add_anchor(el, el.attrib["id"]) - if self.use_permalinks not in [False, None]: - self.add_permalink(el, el.attrib["id"]) - - toc_tokens = nest_toc_tokens(toc_tokens) - div = self.build_toc_div(toc_tokens) - if self.marker: - self.replace_marker(doc, div) - - # serialize and attach to markdown instance. - toc = self.md.serializer(div) - for pp in self.md.postprocessors: - toc = pp.run(toc) - self.md.toc_tokens = toc_tokens - self.md.toc = toc - - -class TocExtension(Extension): - - TreeProcessorClass = TocTreeprocessor - - def __init__(self, **kwargs): - self.config = { - "marker": ['[TOC]', - 'Text to find and replace with Table of Contents - ' - 'Set to an empty string to disable. Defaults to "[TOC]"'], - "title": ["", - "Title to insert into TOC <div> - " - "Defaults to an empty string"], - "anchorlink": [False, - "True if header should be a self link - " - "Defaults to False"], - "anchorlink_class": ['toclink', - 'CSS class(es) used for the link. ' - 'Defaults to "toclink"'], - "permalink": [0, - "True or link text if a Sphinx-style permalink should " - "be added - Defaults to False"], - "permalink_class": ['headerlink', - 'CSS class(es) used for the link. ' - 'Defaults to "headerlink"'], - "permalink_title": ["Permanent link", - "Title attribute of the permalink - " - "Defaults to 'Permanent link'"], - "baselevel": ['1', 'Base level for headers.'], - "slugify": [slugify, - "Function to generate anchors based on header text - " - "Defaults to the headerid ext's slugify function."], - 'separator': ['-', 'Word separator. Defaults to "-".'], - "toc_depth": [6, - 'Define the range of section levels to include in' - 'the Table of Contents. A single integer (b) defines' - 'the bottom section level (<h1>..<hb>) only.' - 'A string consisting of two digits separated by a hyphen' - 'in between ("2-5"), define the top (t) and the' - 'bottom (b) (<ht>..<hb>). Defaults to `6` (bottom).'], - } - - super().__init__(**kwargs) - - def extendMarkdown(self, md): - md.registerExtension(self) - self.md = md - self.reset() - tocext = self.TreeProcessorClass(md, self.getConfigs()) - # Headerid ext is set to '>prettify'. With this set to '_end', - # it should always come after headerid ext (and honor ids assigned - # by the header id extension) if both are used. Same goes for - # attr_list extension. This must come last because we don't want - # to redefine ids after toc is created. But we do want toc prettified. - md.treeprocessors.register(tocext, 'toc', 5) - - def reset(self): - self.md.toc = '' - self.md.toc_tokens = [] - - -def makeExtension(**kwargs): # pragma: no cover - return TocExtension(**kwargs) diff --git a/Source/Libs/markdown/extensions/wikilinks.py b/Source/Libs/markdown/extensions/wikilinks.py deleted file mode 100644 index cddee7a..0000000 --- a/Source/Libs/markdown/extensions/wikilinks.py +++ /dev/null @@ -1,87 +0,0 @@ -''' -WikiLinks Extension for Python-Markdown -====================================== - -Converts [[WikiLinks]] to relative links. - -See <https://Python-Markdown.github.io/extensions/wikilinks> -for documentation. - -Original code Copyright [Waylan Limberg](http://achinghead.com/). - -All changes Copyright The Python Markdown Project - -License: [BSD](https://opensource.org/licenses/bsd-license.php) - -''' - -from . import Extension -from ..inlinepatterns import InlineProcessor -import xml.etree.ElementTree as etree -import re - - -def build_url(label, base, end): - """ Build a url from the label, a base, and an end. """ - clean_label = re.sub(r'([ ]+_)|(_[ ]+)|([ ]+)', '_', label) - return '{}{}{}'.format(base, clean_label, end) - - -class WikiLinkExtension(Extension): - - def __init__(self, **kwargs): - self.config = { - 'base_url': ['/', 'String to append to beginning or URL.'], - 'end_url': ['/', 'String to append to end of URL.'], - 'html_class': ['wikilink', 'CSS hook. Leave blank for none.'], - 'build_url': [build_url, 'Callable formats URL from label.'], - } - - super().__init__(**kwargs) - - def extendMarkdown(self, md): - self.md = md - - # append to end of inline patterns - WIKILINK_RE = r'\[\[([\w0-9_ -]+)\]\]' - wikilinkPattern = WikiLinksInlineProcessor(WIKILINK_RE, self.getConfigs()) - wikilinkPattern.md = md - md.inlinePatterns.register(wikilinkPattern, 'wikilink', 75) - - -class WikiLinksInlineProcessor(InlineProcessor): - def __init__(self, pattern, config): - super().__init__(pattern) - self.config = config - - def handleMatch(self, m, data): - if m.group(1).strip(): - base_url, end_url, html_class = self._getMeta() - label = m.group(1).strip() - url = self.config['build_url'](label, base_url, end_url) - a = etree.Element('a') - a.text = label - a.set('href', url) - if html_class: - a.set('class', html_class) - else: - a = '' - return a, m.start(0), m.end(0) - - def _getMeta(self): - """ Return meta data or config data. """ - base_url = self.config['base_url'] - end_url = self.config['end_url'] - html_class = self.config['html_class'] - if hasattr(self.md, 'Meta'): - if 'wiki_base_url' in self.md.Meta: - base_url = self.md.Meta['wiki_base_url'][0] - if 'wiki_end_url' in self.md.Meta: - end_url = self.md.Meta['wiki_end_url'][0] - if 'wiki_html_class' in self.md.Meta: - html_class = self.md.Meta['wiki_html_class'][0] - return base_url, end_url, html_class - - -def makeExtension(**kwargs): # pragma: no cover - return WikiLinkExtension(**kwargs) diff --git a/Source/Libs/markdown/htmlparser.py b/Source/Libs/markdown/htmlparser.py deleted file mode 100644 index 7ca858e..0000000 --- a/Source/Libs/markdown/htmlparser.py +++ /dev/null @@ -1,323 +0,0 @@ -""" -Python Markdown - -A Python implementation of John Gruber's Markdown. - -Documentation: https://python-markdown.github.io/ -GitHub: https://github.com/Python-Markdown/markdown/ -PyPI: https://pypi.org/project/Markdown/ - -Started by Manfred Stienstra (http://www.dwerg.net/). -Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org). -Currently maintained by Waylan Limberg (https://github.com/waylan), -Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser). - -Copyright 2007-2020 The Python Markdown Project (v. 1.7 and later) -Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b) -Copyright 2004 Manfred Stienstra (the original version) - -License: BSD (see LICENSE.md for details). -""" - -import re -import importlib -import sys - - -# Import a copy of the html.parser lib as `htmlparser` so we can monkeypatch it. -# Users can still do `from html import parser` and get the default behavior. -spec = importlib.util.find_spec('html.parser') -htmlparser = importlib.util.module_from_spec(spec) -spec.loader.exec_module(htmlparser) -sys.modules['htmlparser'] = htmlparser - -# Monkeypatch HTMLParser to only accept `?>` to close Processing Instructions. -htmlparser.piclose = re.compile(r'\?>') -# Monkeypatch HTMLParser to only recognize entity references with a closing semicolon. -htmlparser.entityref = re.compile(r'&([a-zA-Z][-.a-zA-Z0-9]*);') -# Monkeypatch HTMLParser to no longer support partial entities. We are always feeding a complete block, -# so the 'incomplete' functionality is unnecessary. As the entityref regex is run right before incomplete, -# and the two regex are the same, then incomplete will simply never match and we avoid the logic within. -htmlparser.incomplete = htmlparser.entityref -# Monkeypatch HTMLParser to not accept a backtick in a tag name, attribute name, or bare value. -htmlparser.locatestarttagend_tolerant = re.compile(r""" - <[a-zA-Z][^`\t\n\r\f />\x00]* # tag name <= added backtick here - (?:[\s/]* # optional whitespace before attribute name - (?:(?<=['"\s/])[^`\s/>][^\s/=>]* # attribute name <= added backtick here - (?:\s*=+\s* # value indicator - (?:'[^']*' # LITA-enclosed value - |"[^"]*" # LIT-enclosed value - |(?!['"])[^`>\s]* # bare value <= added backtick here - ) - (?:\s*,)* # possibly followed by a comma - )?(?:\s|/(?!>))* - )* - )? - \s* # trailing whitespace -""", re.VERBOSE) - -# Match a blank line at the start of a block of text (two newlines). -# The newlines may be preceded by additional whitespace. -blank_line_re = re.compile(r'^([ ]*\n){2}') - - -class HTMLExtractor(htmlparser.HTMLParser): - """ - Extract raw HTML from text. - - The raw HTML is stored in the `htmlStash` of the Markdown instance passed - to `md` and the remaining text is stored in `cleandoc` as a list of strings. - """ - - def __init__(self, md, *args, **kwargs): - if 'convert_charrefs' not in kwargs: - kwargs['convert_charrefs'] = False - - # Block tags that should contain no content (self closing) - self.empty_tags = set(['hr']) - - # This calls self.reset - super().__init__(*args, **kwargs) - self.md = md - - def reset(self): - """Reset this instance. Loses all unprocessed data.""" - self.inraw = False - self.intail = False - self.stack = [] # When inraw==True, stack contains a list of tags - self._cache = [] - self.cleandoc = [] - super().reset() - - def close(self): - """Handle any buffered data.""" - super().close() - if len(self.rawdata): - # Temp fix for https://bugs.python.org/issue41989 - # TODO: remove this when the bug is fixed in all supported Python versions. - if self.convert_charrefs and not self.cdata_elem: # pragma: no cover - self.handle_data(htmlparser.unescape(self.rawdata)) - else: - self.handle_data(self.rawdata) - # Handle any unclosed tags. - if len(self._cache): - self.cleandoc.append(self.md.htmlStash.store(''.join(self._cache))) - self._cache = [] - - @property - def line_offset(self): - """Returns char index in self.rawdata for the start of the current line. """ - if self.lineno > 1 and '\n' in self.rawdata: - m = re.match(r'([^\n]*\n){{{}}}'.format(self.lineno-1), self.rawdata) - if m: - return m.end() - else: # pragma: no cover - # Value of self.lineno must exceed total number of lines. - # Find index of beginning of last line. - return self.rawdata.rfind('\n') - return 0 - - def at_line_start(self): - """ - Returns True if current position is at start of line. - - Allows for up to three blank spaces at start of line. - """ - if self.offset == 0: - return True - if self.offset > 3: - return False - # Confirm up to first 3 chars are whitespace - return self.rawdata[self.line_offset:self.line_offset + self.offset].strip() == '' - - def get_endtag_text(self, tag): - """ - Returns the text of the end tag. - - If it fails to extract the actual text from the raw data, it builds a closing tag with `tag`. - """ - # Attempt to extract actual tag from raw source text - start = self.line_offset + self.offset - m = htmlparser.endendtag.search(self.rawdata, start) - if m: - return self.rawdata[start:m.end()] - else: # pragma: no cover - # Failed to extract from raw data. Assume well formed and lowercase. - return '</{}>'.format(tag) - - def handle_starttag(self, tag, attrs): - # Handle tags that should always be empty and do not specify a closing tag - if tag in self.empty_tags: - self.handle_startendtag(tag, attrs) - return - - if self.md.is_block_level(tag) and (self.intail or (self.at_line_start() and not self.inraw)): - # Started a new raw block. Prepare stack. - self.inraw = True - self.cleandoc.append('\n') - - text = self.get_starttag_text() - if self.inraw: - self.stack.append(tag) - self._cache.append(text) - else: - self.cleandoc.append(text) - if tag in self.CDATA_CONTENT_ELEMENTS: - # This is presumably a standalone tag in a code span (see #1036). - self.clear_cdata_mode() - - def handle_endtag(self, tag): - text = self.get_endtag_text(tag) - - if self.inraw: - self._cache.append(text) - if tag in self.stack: - # Remove tag from stack - while self.stack: - if self.stack.pop() == tag: - break - if len(self.stack) == 0: - # End of raw block. - if blank_line_re.match(self.rawdata[self.line_offset + self.offset + len(text):]): - # Preserve blank line and end of raw block. - self._cache.append('\n') - else: - # More content exists after endtag. - self.intail = True - # Reset stack. - self.inraw = False - self.cleandoc.append(self.md.htmlStash.store(''.join(self._cache))) - # Insert blank line between this and next line. - self.cleandoc.append('\n\n') - self._cache = [] - else: - self.cleandoc.append(text) - - def handle_data(self, data): - if self.intail and '\n' in data: - self.intail = False - if self.inraw: - self._cache.append(data) - else: - self.cleandoc.append(data) - - def handle_empty_tag(self, data, is_block): - """ Handle empty tags (`<data>`). """ - if self.inraw or self.intail: - # Append this to the existing raw block - self._cache.append(data) - elif self.at_line_start() and is_block: - # Handle this as a standalone raw block - if blank_line_re.match(self.rawdata[self.line_offset + self.offset + len(data):]): - # Preserve blank line after tag in raw block. - data += '\n' - else: - # More content exists after tag. - self.intail = True - item = self.cleandoc[-1] if self.cleandoc else '' - # If we only have one newline before block element, add another - if not item.endswith('\n\n') and item.endswith('\n'): - self.cleandoc.append('\n') - self.cleandoc.append(self.md.htmlStash.store(data)) - # Insert blank line between this and next line. - self.cleandoc.append('\n\n') - else: - self.cleandoc.append(data) - - def handle_startendtag(self, tag, attrs): - self.handle_empty_tag(self.get_starttag_text(), is_block=self.md.is_block_level(tag)) - - def handle_charref(self, name): - self.handle_empty_tag('&#{};'.format(name), is_block=False) - - def handle_entityref(self, name): - self.handle_empty_tag('&{};'.format(name), is_block=False) - - def handle_comment(self, data): - self.handle_empty_tag('<!--{}-->'.format(data), is_block=True) - - def handle_decl(self, data): - self.handle_empty_tag('<!{}>'.format(data), is_block=True) - - def handle_pi(self, data): - self.handle_empty_tag('<?{}?>'.format(data), is_block=True) - - def unknown_decl(self, data): - end = ']]>' if data.startswith('CDATA[') else ']>' - self.handle_empty_tag('<![{}{}'.format(data, end), is_block=True) - - def parse_pi(self, i): - if self.at_line_start() or self.intail: - return super().parse_pi(i) - # This is not the beginning of a raw block so treat as plain data - # and avoid consuming any tags which may follow (see #1066). - self.handle_data('<?') - return i + 2 - - def parse_html_declaration(self, i): - if self.at_line_start() or self.intail: - return super().parse_html_declaration(i) - # This is not the beginning of a raw block so treat as plain data - # and avoid consuming any tags which may follow (see #1066). - self.handle_data('<!') - return i + 2 - - # The rest has been copied from base class in standard lib to address #1036. - # As __startag_text is private, all references to it must be in this subclass. - # The last few lines of parse_starttag are reversed so that handle_starttag - # can override cdata_mode in certain situations (in a code span). - __starttag_text = None - - def get_starttag_text(self): - """Return full source of start tag: '<...>'.""" - return self.__starttag_text - - def parse_starttag(self, i): # pragma: no cover - self.__starttag_text = None - endpos = self.check_for_whole_start_tag(i) - if endpos < 0: - return endpos - rawdata = self.rawdata - self.__starttag_text = rawdata[i:endpos] - - # Now parse the data between i+1 and j into a tag and attrs - attrs = [] - match = htmlparser.tagfind_tolerant.match(rawdata, i+1) - assert match, 'unexpected call to parse_starttag()' - k = match.end() - self.lasttag = tag = match.group(1).lower() - while k < endpos: - m = htmlparser.attrfind_tolerant.match(rawdata, k) - if not m: - break - attrname, rest, attrvalue = m.group(1, 2, 3) - if not rest: - attrvalue = None - elif attrvalue[:1] == '\'' == attrvalue[-1:] or \ - attrvalue[:1] == '"' == attrvalue[-1:]: # noqa: E127 - attrvalue = attrvalue[1:-1] - if attrvalue: - attrvalue = htmlparser.unescape(attrvalue) - attrs.append((attrname.lower(), attrvalue)) - k = m.end() - - end = rawdata[k:endpos].strip() - if end not in (">", "/>"): - lineno, offset = self.getpos() - if "\n" in self.__starttag_text: - lineno = lineno + self.__starttag_text.count("\n") - offset = len(self.__starttag_text) \ - - self.__starttag_text.rfind("\n") # noqa: E127 - else: - offset = offset + len(self.__starttag_text) - self.handle_data(rawdata[i:endpos]) - return endpos - if end.endswith('/>'): - # XHTML-style empty tag: <span attr="value" /> - self.handle_startendtag(tag, attrs) - else: - # *** set cdata_mode first so we can override it in handle_starttag (see #1036) *** - if tag in self.CDATA_CONTENT_ELEMENTS: - self.set_cdata_mode(tag) - self.handle_starttag(tag, attrs) - return endpos diff --git a/Source/Libs/markdown/inlinepatterns.py b/Source/Libs/markdown/inlinepatterns.py deleted file mode 100644 index eb74f49..0000000 --- a/Source/Libs/markdown/inlinepatterns.py +++ /dev/null @@ -1,892 +0,0 @@ -""" -Python Markdown - -A Python implementation of John Gruber's Markdown. - -Documentation: https://python-markdown.github.io/ -GitHub: https://github.com/Python-Markdown/markdown/ -PyPI: https://pypi.org/project/Markdown/ - -Started by Manfred Stienstra (http://www.dwerg.net/). -Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org). -Currently maintained by Waylan Limberg (https://github.com/waylan), -Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser). - -Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later) -Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b) -Copyright 2004 Manfred Stienstra (the original version) - -License: BSD (see LICENSE.md for details). - -INLINE PATTERNS -============================================================================= - -Inline patterns such as *emphasis* are handled by means of auxiliary -objects, one per pattern. Pattern objects must be instances of classes -that extend markdown.Pattern. Each pattern object uses a single regular -expression and needs support the following methods: - - pattern.getCompiledRegExp() # returns a regular expression - - pattern.handleMatch(m) # takes a match object and returns - # an ElementTree element or just plain text - -All of python markdown's built-in patterns subclass from Pattern, -but you can add additional patterns that don't. - -Also note that all the regular expressions used by inline must -capture the whole block. For this reason, they all start with -'^(.*)' and end with '(.*)!'. In case with built-in expression -Pattern takes care of adding the "^(.*)" and "(.*)!". - -Finally, the order in which regular expressions are applied is very -important - e.g. if we first replace http://.../ links with <a> tags -and _then_ try to replace inline html, we would end up with a mess. -So, we apply the expressions in the following order: - -* escape and backticks have to go before everything else, so - that we can preempt any markdown patterns by escaping them. - -* then we handle auto-links (must be done before inline html) - -* then we handle inline HTML. At this point we will simply - replace all inline HTML strings with a placeholder and add - the actual HTML to a hash. - -* then inline images (must be done before links) - -* then bracketed links, first regular then reference-style - -* finally we apply strong and emphasis -""" - -from . import util -from collections import namedtuple -import re -import xml.etree.ElementTree as etree -try: # pragma: no cover - from html import entities -except ImportError: # pragma: no cover - import htmlentitydefs as entities - - -def build_inlinepatterns(md, **kwargs): - """ Build the default set of inline patterns for Markdown. """ - inlinePatterns = util.Registry() - inlinePatterns.register(BacktickInlineProcessor(BACKTICK_RE), 'backtick', 190) - inlinePatterns.register(EscapeInlineProcessor(ESCAPE_RE, md), 'escape', 180) - inlinePatterns.register(ReferenceInlineProcessor(REFERENCE_RE, md), 'reference', 170) - inlinePatterns.register(LinkInlineProcessor(LINK_RE, md), 'link', 160) - inlinePatterns.register(ImageInlineProcessor(IMAGE_LINK_RE, md), 'image_link', 150) - inlinePatterns.register( - ImageReferenceInlineProcessor(IMAGE_REFERENCE_RE, md), 'image_reference', 140 - ) - inlinePatterns.register( - ShortReferenceInlineProcessor(REFERENCE_RE, md), 'short_reference', 130 - ) - inlinePatterns.register( - ShortImageReferenceInlineProcessor(IMAGE_REFERENCE_RE, md), 'short_image_ref', 125 - ) - inlinePatterns.register(AutolinkInlineProcessor(AUTOLINK_RE, md), 'autolink', 120) - inlinePatterns.register(AutomailInlineProcessor(AUTOMAIL_RE, md), 'automail', 110) - inlinePatterns.register(SubstituteTagInlineProcessor(LINE_BREAK_RE, 'br'), 'linebreak', 100) - inlinePatterns.register(HtmlInlineProcessor(HTML_RE, md), 'html', 90) - inlinePatterns.register(HtmlInlineProcessor(ENTITY_RE, md), 'entity', 80) - inlinePatterns.register(SimpleTextInlineProcessor(NOT_STRONG_RE), 'not_strong', 70) - inlinePatterns.register(AsteriskProcessor(r'\*'), 'em_strong', 60) - inlinePatterns.register(UnderscoreProcessor(r'_'), 'em_strong2', 50) - return inlinePatterns - - -""" -The actual regular expressions for patterns ------------------------------------------------------------------------------ -""" - -NOIMG = r'(?<!\!)' - -# `e=f()` or ``e=f("`")`` -BACKTICK_RE = r'(?:(?<!\\)((?:\\{2})+)(?=`+)|(?<!\\)(`+)(.+?)(?<!`)\2(?!`))' - -# \< -ESCAPE_RE = r'\\(.)' - -# *emphasis* -EMPHASIS_RE = r'(\*)([^\*]+)\1' - -# **strong** -STRONG_RE = r'(\*{2})(.+?)\1' - -# __smart__strong__ -SMART_STRONG_RE = r'(?<!\w)(_{2})(?!_)(.+?)(?<!_)\1(?!\w)' - -# _smart_emphasis_ -SMART_EMPHASIS_RE = r'(?<!\w)(_)(?!_)(.+?)(?<!_)\1(?!\w)' - -# __strong _em__ -SMART_STRONG_EM_RE = r'(?<!\w)(\_)\1(?!\1)(.+?)(?<!\w)\1(?!\1)(.+?)\1{3}(?!\w)' - -# ***strongem*** or ***em*strong** -EM_STRONG_RE = r'(\*)\1{2}(.+?)\1(.*?)\1{2}' - -# ___strongem___ or ___em_strong__ -EM_STRONG2_RE = r'(_)\1{2}(.+?)\1(.*?)\1{2}' - -# ***strong**em* -STRONG_EM_RE = r'(\*)\1{2}(.+?)\1{2}(.*?)\1' - -# ___strong__em_ -STRONG_EM2_RE = r'(_)\1{2}(.+?)\1{2}(.*?)\1' - -# **strong*em*** -STRONG_EM3_RE = r'(\*)\1(?!\1)([^*]+?)\1(?!\1)(.+?)\1{3}' - -# [text](url) or [text](<url>) or [text](url "title") -LINK_RE = NOIMG + r'\[' - -# ![alttxt](http://x.com/) or ![alttxt](<http://x.com/>) -IMAGE_LINK_RE = r'\!\[' - -# [Google][3] -REFERENCE_RE = LINK_RE - -# ![alt text][2] -IMAGE_REFERENCE_RE = IMAGE_LINK_RE - -# stand-alone * or _ -NOT_STRONG_RE = r'((^|\s)(\*|_)(\s|$))' - -# <http://www.123.com> -AUTOLINK_RE = r'<((?:[Ff]|[Hh][Tt])[Tt][Pp][Ss]?://[^<>]*)>' - -# <me@example.com> -AUTOMAIL_RE = r'<([^<> !]+@[^@<> ]+)>' - -# <...> -HTML_RE = r'(<(\/?[a-zA-Z][^<>@ ]*( [^<>]*)?|!--(?:(?!<!--|-->).)*--)>)' - -# "&" (decimal) or "&" (hex) or "&" (named) -ENTITY_RE = r'(&(?:\#[0-9]+|\#x[0-9a-fA-F]+|[a-zA-Z0-9]+);)' - -# two spaces at end of line -LINE_BREAK_RE = r' \n' - - -def dequote(string): - """Remove quotes from around a string.""" - if ((string.startswith('"') and string.endswith('"')) or - (string.startswith("'") and string.endswith("'"))): - return string[1:-1] - else: - return string - - -class EmStrongItem(namedtuple('EmStrongItem', ['pattern', 'builder', 'tags'])): - """Emphasis/strong pattern item.""" - - -""" -The pattern classes ------------------------------------------------------------------------------ -""" - - -class Pattern: # pragma: no cover - """Base class that inline patterns subclass. """ - - ANCESTOR_EXCLUDES = tuple() - - def __init__(self, pattern, md=None): - """ - Create an instant of an inline pattern. - - Keyword arguments: - - * pattern: A regular expression that matches a pattern - - """ - self.pattern = pattern - self.compiled_re = re.compile(r"^(.*?)%s(.*)$" % pattern, - re.DOTALL | re.UNICODE) - - self.md = md - - @property - @util.deprecated("Use 'md' instead.") - def markdown(self): - # TODO: remove this later - return self.md - - def getCompiledRegExp(self): - """ Return a compiled regular expression. """ - return self.compiled_re - - def handleMatch(self, m): - """Return a ElementTree element from the given match. - - Subclasses should override this method. - - Keyword arguments: - - * m: A re match object containing a match of the pattern. - - """ - pass # pragma: no cover - - def type(self): - """ Return class name, to define pattern type """ - return self.__class__.__name__ - - def unescape(self, text): - """ Return unescaped text given text with an inline placeholder. """ - try: - stash = self.md.treeprocessors['inline'].stashed_nodes - except KeyError: # pragma: no cover - return text - - def get_stash(m): - id = m.group(1) - if id in stash: - value = stash.get(id) - if isinstance(value, str): - return value - else: - # An etree Element - return text content only - return ''.join(value.itertext()) - return util.INLINE_PLACEHOLDER_RE.sub(get_stash, text) - - -class InlineProcessor(Pattern): - """ - Base class that inline patterns subclass. - - This is the newer style inline processor that uses a more - efficient and flexible search approach. - """ - - def __init__(self, pattern, md=None): - """ - Create an instant of an inline pattern. - - Keyword arguments: - - * pattern: A regular expression that matches a pattern - - """ - self.pattern = pattern - self.compiled_re = re.compile(pattern, re.DOTALL | re.UNICODE) - - # Api for Markdown to pass safe_mode into instance - self.safe_mode = False - self.md = md - - def handleMatch(self, m, data): - """Return a ElementTree element from the given match and the - start and end index of the matched text. - - If `start` and/or `end` are returned as `None`, it will be - assumed that the processor did not find a valid region of text. - - Subclasses should override this method. - - Keyword arguments: - - * m: A re match object containing a match of the pattern. - * data: The buffer current under analysis - - Returns: - - * el: The ElementTree element, text or None. - * start: The start of the region that has been matched or None. - * end: The end of the region that has been matched or None. - - """ - pass # pragma: no cover - - -class SimpleTextPattern(Pattern): # pragma: no cover - """ Return a simple text of group(2) of a Pattern. """ - def handleMatch(self, m): - return m.group(2) - - -class SimpleTextInlineProcessor(InlineProcessor): - """ Return a simple text of group(1) of a Pattern. """ - def handleMatch(self, m, data): - return m.group(1), m.start(0), m.end(0) - - -class EscapeInlineProcessor(InlineProcessor): - """ Return an escaped character. """ - - def handleMatch(self, m, data): - char = m.group(1) - if char in self.md.ESCAPED_CHARS: - return '{}{}{}'.format(util.STX, ord(char), util.ETX), m.start(0), m.end(0) - else: - return None, m.start(0), m.end(0) - - -class SimpleTagPattern(Pattern): # pragma: no cover - """ - Return element of type `tag` with a text attribute of group(3) - of a Pattern. - - """ - def __init__(self, pattern, tag): - Pattern.__init__(self, pattern) - self.tag = tag - - def handleMatch(self, m): - el = etree.Element(self.tag) - el.text = m.group(3) - return el - - -class SimpleTagInlineProcessor(InlineProcessor): - """ - Return element of type `tag` with a text attribute of group(2) - of a Pattern. - - """ - def __init__(self, pattern, tag): - InlineProcessor.__init__(self, pattern) - self.tag = tag - - def handleMatch(self, m, data): # pragma: no cover - el = etree.Element(self.tag) - el.text = m.group(2) - return el, m.start(0), m.end(0) - - -class SubstituteTagPattern(SimpleTagPattern): # pragma: no cover - """ Return an element of type `tag` with no children. """ - def handleMatch(self, m): - return etree.Element(self.tag) - - -class SubstituteTagInlineProcessor(SimpleTagInlineProcessor): - """ Return an element of type `tag` with no children. """ - def handleMatch(self, m, data): - return etree.Element(self.tag), m.start(0), m.end(0) - - -class BacktickInlineProcessor(InlineProcessor): - """ Return a `<code>` element containing the matching text. """ - def __init__(self, pattern): - InlineProcessor.__init__(self, pattern) - self.ESCAPED_BSLASH = '{}{}{}'.format(util.STX, ord('\\'), util.ETX) - self.tag = 'code' - - def handleMatch(self, m, data): - if m.group(3): - el = etree.Element(self.tag) - el.text = util.AtomicString(util.code_escape(m.group(3).strip())) - return el, m.start(0), m.end(0) - else: - return m.group(1).replace('\\\\', self.ESCAPED_BSLASH), m.start(0), m.end(0) - - -class DoubleTagPattern(SimpleTagPattern): # pragma: no cover - """Return a ElementTree element nested in tag2 nested in tag1. - - Useful for strong emphasis etc. - - """ - def handleMatch(self, m): - tag1, tag2 = self.tag.split(",") - el1 = etree.Element(tag1) - el2 = etree.SubElement(el1, tag2) - el2.text = m.group(3) - if len(m.groups()) == 5: - el2.tail = m.group(4) - return el1 - - -class DoubleTagInlineProcessor(SimpleTagInlineProcessor): - """Return a ElementTree element nested in tag2 nested in tag1. - - Useful for strong emphasis etc. - - """ - def handleMatch(self, m, data): # pragma: no cover - tag1, tag2 = self.tag.split(",") - el1 = etree.Element(tag1) - el2 = etree.SubElement(el1, tag2) - el2.text = m.group(2) - if len(m.groups()) == 3: - el2.tail = m.group(3) - return el1, m.start(0), m.end(0) - - -class HtmlInlineProcessor(InlineProcessor): - """ Store raw inline html and return a placeholder. """ - def handleMatch(self, m, data): - rawhtml = self.unescape(m.group(1)) - place_holder = self.md.htmlStash.store(rawhtml) - return place_holder, m.start(0), m.end(0) - - def unescape(self, text): - """ Return unescaped text given text with an inline placeholder. """ - try: - stash = self.md.treeprocessors['inline'].stashed_nodes - except KeyError: # pragma: no cover - return text - - def get_stash(m): - id = m.group(1) - value = stash.get(id) - if value is not None: - try: - return self.md.serializer(value) - except Exception: - return r'\%s' % value - - return util.INLINE_PLACEHOLDER_RE.sub(get_stash, text) - - -class AsteriskProcessor(InlineProcessor): - """Emphasis processor for handling strong and em matches inside asterisks.""" - - PATTERNS = [ - EmStrongItem(re.compile(EM_STRONG_RE, re.DOTALL | re.UNICODE), 'double', 'strong,em'), - EmStrongItem(re.compile(STRONG_EM_RE, re.DOTALL | re.UNICODE), 'double', 'em,strong'), - EmStrongItem(re.compile(STRONG_EM3_RE, re.DOTALL | re.UNICODE), 'double2', 'strong,em'), - EmStrongItem(re.compile(STRONG_RE, re.DOTALL | re.UNICODE), 'single', 'strong'), - EmStrongItem(re.compile(EMPHASIS_RE, re.DOTALL | re.UNICODE), 'single', 'em') - ] - - def build_single(self, m, tag, idx): - """Return single tag.""" - el1 = etree.Element(tag) - text = m.group(2) - self.parse_sub_patterns(text, el1, None, idx) - return el1 - - def build_double(self, m, tags, idx): - """Return double tag.""" - - tag1, tag2 = tags.split(",") - el1 = etree.Element(tag1) - el2 = etree.Element(tag2) - text = m.group(2) - self.parse_sub_patterns(text, el2, None, idx) - el1.append(el2) - if len(m.groups()) == 3: - text = m.group(3) - self.parse_sub_patterns(text, el1, el2, idx) - return el1 - - def build_double2(self, m, tags, idx): - """Return double tags (variant 2): `<strong>text <em>text</em></strong>`.""" - - tag1, tag2 = tags.split(",") - el1 = etree.Element(tag1) - el2 = etree.Element(tag2) - text = m.group(2) - self.parse_sub_patterns(text, el1, None, idx) - text = m.group(3) - el1.append(el2) - self.parse_sub_patterns(text, el2, None, idx) - return el1 - - def parse_sub_patterns(self, data, parent, last, idx): - """ - Parses sub patterns. - - `data` (`str`): - text to evaluate. - - `parent` (`etree.Element`): - Parent to attach text and sub elements to. - - `last` (`etree.Element`): - Last appended child to parent. Can also be None if parent has no children. - - `idx` (`int`): - Current pattern index that was used to evaluate the parent. - - """ - - offset = 0 - pos = 0 - - length = len(data) - while pos < length: - # Find the start of potential emphasis or strong tokens - if self.compiled_re.match(data, pos): - matched = False - # See if the we can match an emphasis/strong pattern - for index, item in enumerate(self.PATTERNS): - # Only evaluate patterns that are after what was used on the parent - if index <= idx: - continue - m = item.pattern.match(data, pos) - if m: - # Append child nodes to parent - # Text nodes should be appended to the last - # child if present, and if not, it should - # be added as the parent's text node. - text = data[offset:m.start(0)] - if text: - if last is not None: - last.tail = text - else: - parent.text = text - el = self.build_element(m, item.builder, item.tags, index) - parent.append(el) - last = el - # Move our position past the matched hunk - offset = pos = m.end(0) - matched = True - if not matched: - # We matched nothing, move on to the next character - pos += 1 - else: - # Increment position as no potential emphasis start was found. - pos += 1 - - # Append any leftover text as a text node. - text = data[offset:] - if text: - if last is not None: - last.tail = text - else: - parent.text = text - - def build_element(self, m, builder, tags, index): - """Element builder.""" - - if builder == 'double2': - return self.build_double2(m, tags, index) - elif builder == 'double': - return self.build_double(m, tags, index) - else: - return self.build_single(m, tags, index) - - def handleMatch(self, m, data): - """Parse patterns.""" - - el = None - start = None - end = None - - for index, item in enumerate(self.PATTERNS): - m1 = item.pattern.match(data, m.start(0)) - if m1: - start = m1.start(0) - end = m1.end(0) - el = self.build_element(m1, item.builder, item.tags, index) - break - return el, start, end - - -class UnderscoreProcessor(AsteriskProcessor): - """Emphasis processor for handling strong and em matches inside underscores.""" - - PATTERNS = [ - EmStrongItem(re.compile(EM_STRONG2_RE, re.DOTALL | re.UNICODE), 'double', 'strong,em'), - EmStrongItem(re.compile(STRONG_EM2_RE, re.DOTALL | re.UNICODE), 'double', 'em,strong'), - EmStrongItem(re.compile(SMART_STRONG_EM_RE, re.DOTALL | re.UNICODE), 'double2', 'strong,em'), - EmStrongItem(re.compile(SMART_STRONG_RE, re.DOTALL | re.UNICODE), 'single', 'strong'), - EmStrongItem(re.compile(SMART_EMPHASIS_RE, re.DOTALL | re.UNICODE), 'single', 'em') - ] - - -class LinkInlineProcessor(InlineProcessor): - """ Return a link element from the given match. """ - RE_LINK = re.compile(r'''\(\s*(?:(<[^<>]*>)\s*(?:('[^']*'|"[^"]*")\s*)?\))?''', re.DOTALL | re.UNICODE) - RE_TITLE_CLEAN = re.compile(r'\s') - - def handleMatch(self, m, data): - text, index, handled = self.getText(data, m.end(0)) - - if not handled: - return None, None, None - - href, title, index, handled = self.getLink(data, index) - if not handled: - return None, None, None - - el = etree.Element("a") - el.text = text - - el.set("href", href) - - if title is not None: - el.set("title", title) - - return el, m.start(0), index - - def getLink(self, data, index): - """Parse data between `()` of `[Text]()` allowing recursive `()`. """ - - href = '' - title = None - handled = False - - m = self.RE_LINK.match(data, pos=index) - if m and m.group(1): - # Matches [Text](<link> "title") - href = m.group(1)[1:-1].strip() - if m.group(2): - title = m.group(2)[1:-1] - index = m.end(0) - handled = True - elif m: - # Track bracket nesting and index in string - bracket_count = 1 - backtrack_count = 1 - start_index = m.end() - index = start_index - last_bracket = -1 - - # Primary (first found) quote tracking. - quote = None - start_quote = -1 - exit_quote = -1 - ignore_matches = False - - # Secondary (second found) quote tracking. - alt_quote = None - start_alt_quote = -1 - exit_alt_quote = -1 - - # Track last character - last = '' - - for pos in range(index, len(data)): - c = data[pos] - if c == '(': - # Count nested ( - # Don't increment the bracket count if we are sure we're in a title. - if not ignore_matches: - bracket_count += 1 - elif backtrack_count > 0: - backtrack_count -= 1 - elif c == ')': - # Match nested ) to ( - # Don't decrement if we are sure we are in a title that is unclosed. - if ((exit_quote != -1 and quote == last) or (exit_alt_quote != -1 and alt_quote == last)): - bracket_count = 0 - elif not ignore_matches: - bracket_count -= 1 - elif backtrack_count > 0: - backtrack_count -= 1 - # We've found our backup end location if the title doesn't resolve. - if backtrack_count == 0: - last_bracket = index + 1 - - elif c in ("'", '"'): - # Quote has started - if not quote: - # We'll assume we are now in a title. - # Brackets are quoted, so no need to match them (except for the final one). - ignore_matches = True - backtrack_count = bracket_count - bracket_count = 1 - start_quote = index + 1 - quote = c - # Secondary quote (in case the first doesn't resolve): [text](link'"title") - elif c != quote and not alt_quote: - start_alt_quote = index + 1 - alt_quote = c - # Update primary quote match - elif c == quote: - exit_quote = index + 1 - # Update secondary quote match - elif alt_quote and c == alt_quote: - exit_alt_quote = index + 1 - - index += 1 - - # Link is closed, so let's break out of the loop - if bracket_count == 0: - # Get the title if we closed a title string right before link closed - if exit_quote >= 0 and quote == last: - href = data[start_index:start_quote - 1] - title = ''.join(data[start_quote:exit_quote - 1]) - elif exit_alt_quote >= 0 and alt_quote == last: - href = data[start_index:start_alt_quote - 1] - title = ''.join(data[start_alt_quote:exit_alt_quote - 1]) - else: - href = data[start_index:index - 1] - break - - if c != ' ': - last = c - - # We have a scenario: [test](link"notitle) - # When we enter a string, we stop tracking bracket resolution in the main counter, - # but we do keep a backup counter up until we discover where we might resolve all brackets - # if the title string fails to resolve. - if bracket_count != 0 and backtrack_count == 0: - href = data[start_index:last_bracket - 1] - index = last_bracket - bracket_count = 0 - - handled = bracket_count == 0 - - if title is not None: - title = self.RE_TITLE_CLEAN.sub(' ', dequote(self.unescape(title.strip()))) - - href = self.unescape(href).strip() - - return href, title, index, handled - - def getText(self, data, index): - """Parse the content between `[]` of the start of an image or link - resolving nested square brackets. - - """ - bracket_count = 1 - text = [] - for pos in range(index, len(data)): - c = data[pos] - if c == ']': - bracket_count -= 1 - elif c == '[': - bracket_count += 1 - index += 1 - if bracket_count == 0: - break - text.append(c) - return ''.join(text), index, bracket_count == 0 - - -class ImageInlineProcessor(LinkInlineProcessor): - """ Return a img element from the given match. """ - - def handleMatch(self, m, data): - text, index, handled = self.getText(data, m.end(0)) - if not handled: - return None, None, None - - src, title, index, handled = self.getLink(data, index) - if not handled: - return None, None, None - - el = etree.Element("img") - - el.set("src", src) - - if title is not None: - el.set("title", title) - - el.set('alt', self.unescape(text)) - return el, m.start(0), index - - -class ReferenceInlineProcessor(LinkInlineProcessor): - """ Match to a stored reference and return link element. """ - NEWLINE_CLEANUP_RE = re.compile(r'\s+', re.MULTILINE) - - RE_LINK = re.compile(r'\s?\[([^\]]*)\]', re.DOTALL | re.UNICODE) - - def handleMatch(self, m, data): - text, index, handled = self.getText(data, m.end(0)) - if not handled: - return None, None, None - - id, end, handled = self.evalId(data, index, text) - if not handled: - return None, None, None - - # Clean up linebreaks in id - id = self.NEWLINE_CLEANUP_RE.sub(' ', id) - if id not in self.md.references: # ignore undefined refs - return None, m.start(0), end - - href, title = self.md.references[id] - - return self.makeTag(href, title, text), m.start(0), end - - def evalId(self, data, index, text): - """ - Evaluate the id portion of [ref][id]. - - If [ref][] use [ref]. - """ - m = self.RE_LINK.match(data, pos=index) - if not m: - return None, index, False - else: - id = m.group(1).lower() - end = m.end(0) - if not id: - id = text.lower() - return id, end, True - - def makeTag(self, href, title, text): - el = etree.Element('a') - - el.set('href', href) - if title: - el.set('title', title) - - el.text = text - return el - - -class ShortReferenceInlineProcessor(ReferenceInlineProcessor): - """Short form of reference: [google]. """ - def evalId(self, data, index, text): - """Evaluate the id from of [ref] """ - - return text.lower(), index, True - - -class ImageReferenceInlineProcessor(ReferenceInlineProcessor): - """ Match to a stored reference and return img element. """ - def makeTag(self, href, title, text): - el = etree.Element("img") - el.set("src", href) - if title: - el.set("title", title) - el.set("alt", self.unescape(text)) - return el - - -class ShortImageReferenceInlineProcessor(ImageReferenceInlineProcessor): - """ Short form of inage reference: ![ref]. """ - def evalId(self, data, index, text): - """Evaluate the id from of [ref] """ - - return text.lower(), index, True - - -class AutolinkInlineProcessor(InlineProcessor): - """ Return a link Element given an autolink (`<http://example/com>`). """ - def handleMatch(self, m, data): - el = etree.Element("a") - el.set('href', self.unescape(m.group(1))) - el.text = util.AtomicString(m.group(1)) - return el, m.start(0), m.end(0) - - -class AutomailInlineProcessor(InlineProcessor): - """ - Return a mailto link Element given an automail link (`<foo@example.com>`). - """ - def handleMatch(self, m, data): - el = etree.Element('a') - email = self.unescape(m.group(1)) - if email.startswith("mailto:"): - email = email[len("mailto:"):] - - def codepoint2name(code): - """Return entity definition by code, or the code if not defined.""" - entity = entities.codepoint2name.get(code) - if entity: - return "{}{};".format(util.AMP_SUBSTITUTE, entity) - else: - return "%s#%d;" % (util.AMP_SUBSTITUTE, code) - - letters = [codepoint2name(ord(letter)) for letter in email] - el.text = util.AtomicString(''.join(letters)) - - mailto = "mailto:" + email - mailto = "".join([util.AMP_SUBSTITUTE + '#%d;' % - ord(letter) for letter in mailto]) - el.set('href', mailto) - return el, m.start(0), m.end(0) diff --git a/Source/Libs/markdown/pep562.py b/Source/Libs/markdown/pep562.py deleted file mode 100644 index b130d3b..0000000 --- a/Source/Libs/markdown/pep562.py +++ /dev/null @@ -1,245 +0,0 @@ -""" -Backport of PEP 562. - -https://pypi.org/search/?q=pep562 - -Licensed under MIT -Copyright (c) 2018 Isaac Muse <isaacmuse@gmail.com> - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the Software without restriction, including without limitation -the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, -and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF -CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -IN THE SOFTWARE. -""" -import sys -from collections import namedtuple -import re - -__all__ = ('Pep562',) - -RE_VER = re.compile( - r'''(?x) - (?P<major>\d+)(?:\.(?P<minor>\d+))?(?:\.(?P<micro>\d+))? - (?:(?P<type>a|b|rc)(?P<pre>\d+))? - (?:\.post(?P<post>\d+))? - (?:\.dev(?P<dev>\d+))? - ''' -) - -REL_MAP = { - ".dev": "", - ".dev-alpha": "a", - ".dev-beta": "b", - ".dev-candidate": "rc", - "alpha": "a", - "beta": "b", - "candidate": "rc", - "final": "" -} - -DEV_STATUS = { - ".dev": "2 - Pre-Alpha", - ".dev-alpha": "2 - Pre-Alpha", - ".dev-beta": "2 - Pre-Alpha", - ".dev-candidate": "2 - Pre-Alpha", - "alpha": "3 - Alpha", - "beta": "4 - Beta", - "candidate": "4 - Beta", - "final": "5 - Production/Stable" -} - -PRE_REL_MAP = {"a": 'alpha', "b": 'beta', "rc": 'candidate'} - - -class Version(namedtuple("Version", ["major", "minor", "micro", "release", "pre", "post", "dev"])): - """ - Get the version (PEP 440). - - A biased approach to the PEP 440 semantic version. - - Provides a tuple structure which is sorted for comparisons `v1 > v2` etc. - (major, minor, micro, release type, pre-release build, post-release build, development release build) - Release types are named in is such a way they are comparable with ease. - Accessors to check if a development, pre-release, or post-release build. Also provides accessor to get - development status for setup files. - - How it works (currently): - - - You must specify a release type as either `final`, `alpha`, `beta`, or `candidate`. - - To define a development release, you can use either `.dev`, `.dev-alpha`, `.dev-beta`, or `.dev-candidate`. - The dot is used to ensure all development specifiers are sorted before `alpha`. - You can specify a `dev` number for development builds, but do not have to as implicit development releases - are allowed. - - You must specify a `pre` value greater than zero if using a prerelease as this project (not PEP 440) does not - allow implicit prereleases. - - You can optionally set `post` to a value greater than zero to make the build a post release. While post releases - are technically allowed in prereleases, it is strongly discouraged, so we are rejecting them. It should be - noted that we do not allow `post0` even though PEP 440 does not restrict this. This project specifically - does not allow implicit post releases. - - It should be noted that we do not support epochs `1!` or local versions `+some-custom.version-1`. - - Acceptable version releases: - - ``` - Version(1, 0, 0, "final") 1.0 - Version(1, 2, 0, "final") 1.2 - Version(1, 2, 3, "final") 1.2.3 - Version(1, 2, 0, ".dev-alpha", pre=4) 1.2a4 - Version(1, 2, 0, ".dev-beta", pre=4) 1.2b4 - Version(1, 2, 0, ".dev-candidate", pre=4) 1.2rc4 - Version(1, 2, 0, "final", post=1) 1.2.post1 - Version(1, 2, 3, ".dev") 1.2.3.dev0 - Version(1, 2, 3, ".dev", dev=1) 1.2.3.dev1 - ``` - - """ - - def __new__(cls, major, minor, micro, release="final", pre=0, post=0, dev=0): - """Validate version info.""" - - # Ensure all parts are positive integers. - for value in (major, minor, micro, pre, post): - if not (isinstance(value, int) and value >= 0): - raise ValueError("All version parts except 'release' should be integers.") - - if release not in REL_MAP: - raise ValueError("'{}' is not a valid release type.".format(release)) - - # Ensure valid pre-release (we do not allow implicit pre-releases). - if ".dev-candidate" < release < "final": - if pre == 0: - raise ValueError("Implicit pre-releases not allowed.") - elif dev: - raise ValueError("Version is not a development release.") - elif post: - raise ValueError("Post-releases are not allowed with pre-releases.") - - # Ensure valid development or development/pre release - elif release < "alpha": - if release > ".dev" and pre == 0: - raise ValueError("Implicit pre-release not allowed.") - elif post: - raise ValueError("Post-releases are not allowed with pre-releases.") - - # Ensure a valid normal release - else: - if pre: - raise ValueError("Version is not a pre-release.") - elif dev: - raise ValueError("Version is not a development release.") - - return super().__new__(cls, major, minor, micro, release, pre, post, dev) - - def _is_pre(self): - """Is prerelease.""" - - return self.pre > 0 - - def _is_dev(self): - """Is development.""" - - return bool(self.release < "alpha") - - def _is_post(self): - """Is post.""" - - return self.post > 0 - - def _get_dev_status(self): # pragma: no cover - """Get development status string.""" - - return DEV_STATUS[self.release] - - def _get_canonical(self): - """Get the canonical output string.""" - - # Assemble major, minor, micro version and append `pre`, `post`, or `dev` if needed.. - if self.micro == 0: - ver = "{}.{}".format(self.major, self.minor) - else: - ver = "{}.{}.{}".format(self.major, self.minor, self.micro) - if self._is_pre(): - ver += '{}{}'.format(REL_MAP[self.release], self.pre) - if self._is_post(): - ver += ".post{}".format(self.post) - if self._is_dev(): - ver += ".dev{}".format(self.dev) - - return ver - - -def parse_version(ver, pre=False): - """Parse version into a comparable Version tuple.""" - - m = RE_VER.match(ver) - - # Handle major, minor, micro - major = int(m.group('major')) - minor = int(m.group('minor')) if m.group('minor') else 0 - micro = int(m.group('micro')) if m.group('micro') else 0 - - # Handle pre releases - if m.group('type'): - release = PRE_REL_MAP[m.group('type')] - pre = int(m.group('pre')) - else: - release = "final" - pre = 0 - - # Handle development releases - dev = m.group('dev') if m.group('dev') else 0 - if m.group('dev'): - dev = int(m.group('dev')) - release = '.dev-' + release if pre else '.dev' - else: - dev = 0 - - # Handle post - post = int(m.group('post')) if m.group('post') else 0 - - return Version(major, minor, micro, release, pre, post, dev) - - -class Pep562: - """ - Backport of PEP 562 <https://pypi.org/search/?q=pep562>. - - Wraps the module in a class that exposes the mechanics to override `__dir__` and `__getattr__`. - The given module will be searched for overrides of `__dir__` and `__getattr__` and use them when needed. - """ - - def __init__(self, name): - """Acquire `__getattr__` and `__dir__`, but only replace module for versions less than Python 3.7.""" - - self._module = sys.modules[name] - self._get_attr = getattr(self._module, '__getattr__', None) - self._get_dir = getattr(self._module, '__dir__', None) - sys.modules[name] = self - - def __dir__(self): - """Return the overridden `dir` if one was provided, else apply `dir` to the module.""" - - return self._get_dir() if self._get_dir else dir(self._module) - - def __getattr__(self, name): - """Attempt to retrieve the attribute from the module, and if missing, use the overridden function if present.""" - - try: - return getattr(self._module, name) - except AttributeError: - if self._get_attr: - return self._get_attr(name) - raise - - -__version_info__ = Version(1, 0, 0, "final") -__version__ = __version_info__._get_canonical() diff --git a/Source/Libs/markdown/postprocessors.py b/Source/Libs/markdown/postprocessors.py deleted file mode 100644 index f4fb924..0000000 --- a/Source/Libs/markdown/postprocessors.py +++ /dev/null @@ -1,134 +0,0 @@ -""" -Python Markdown - -A Python implementation of John Gruber's Markdown. - -Documentation: https://python-markdown.github.io/ -GitHub: https://github.com/Python-Markdown/markdown/ -PyPI: https://pypi.org/project/Markdown/ - -Started by Manfred Stienstra (http://www.dwerg.net/). -Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org). -Currently maintained by Waylan Limberg (https://github.com/waylan), -Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser). - -Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later) -Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b) -Copyright 2004 Manfred Stienstra (the original version) - -License: BSD (see LICENSE.md for details). - -POST-PROCESSORS -============================================================================= - -Markdown also allows post-processors, which are similar to preprocessors in -that they need to implement a "run" method. However, they are run after core -processing. - -""" - -from collections import OrderedDict -from . import util -import re - - -def build_postprocessors(md, **kwargs): - """ Build the default postprocessors for Markdown. """ - postprocessors = util.Registry() - postprocessors.register(RawHtmlPostprocessor(md), 'raw_html', 30) - postprocessors.register(AndSubstitutePostprocessor(), 'amp_substitute', 20) - postprocessors.register(UnescapePostprocessor(), 'unescape', 10) - return postprocessors - - -class Postprocessor(util.Processor): - """ - Postprocessors are run after the ElementTree it converted back into text. - - Each Postprocessor implements a "run" method that takes a pointer to a - text string, modifies it as necessary and returns a text string. - - Postprocessors must extend markdown.Postprocessor. - - """ - - def run(self, text): - """ - Subclasses of Postprocessor should implement a `run` method, which - takes the html document as a single text string and returns a - (possibly modified) string. - - """ - pass # pragma: no cover - - -class RawHtmlPostprocessor(Postprocessor): - """ Restore raw html to the document. """ - - BLOCK_LEVEL_REGEX = re.compile(r'^\<\/?([^ >]+)') - - def run(self, text): - """ Iterate over html stash and restore html. """ - replacements = OrderedDict() - for i in range(self.md.htmlStash.html_counter): - html = self.stash_to_string(self.md.htmlStash.rawHtmlBlocks[i]) - if self.isblocklevel(html): - replacements["<p>{}</p>".format( - self.md.htmlStash.get_placeholder(i))] = html - replacements[self.md.htmlStash.get_placeholder(i)] = html - - def substitute_match(m): - key = m.group(0) - - if key not in replacements: - if key[3:-4] in replacements: - return f'<p>{ replacements[key[3:-4]] }</p>' - else: - return key - - return replacements[key] - - if replacements: - base_placeholder = util.HTML_PLACEHOLDER % r'([0-9]+)' - pattern = re.compile(f'<p>{ base_placeholder }</p>|{ base_placeholder }') - processed_text = pattern.sub(substitute_match, text) - else: - return text - - if processed_text == text: - return processed_text - else: - return self.run(processed_text) - - def isblocklevel(self, html): - m = self.BLOCK_LEVEL_REGEX.match(html) - if m: - if m.group(1)[0] in ('!', '?', '@', '%'): - # Comment, php etc... - return True - return self.md.is_block_level(m.group(1)) - return False - - def stash_to_string(self, text): - """ Convert a stashed object to a string. """ - return str(text) - - -class AndSubstitutePostprocessor(Postprocessor): - """ Restore valid entities """ - - def run(self, text): - text = text.replace(util.AMP_SUBSTITUTE, "&") - return text - - -class UnescapePostprocessor(Postprocessor): - """ Restore escaped chars """ - - RE = re.compile(r'{}(\d+){}'.format(util.STX, util.ETX)) - - def unescape(self, m): - return chr(int(m.group(1))) - - def run(self, text): - return self.RE.sub(self.unescape, text) diff --git a/Source/Libs/markdown/preprocessors.py b/Source/Libs/markdown/preprocessors.py deleted file mode 100644 index e1023c5..0000000 --- a/Source/Libs/markdown/preprocessors.py +++ /dev/null @@ -1,82 +0,0 @@ -""" -Python Markdown - -A Python implementation of John Gruber's Markdown. - -Documentation: https://python-markdown.github.io/ -GitHub: https://github.com/Python-Markdown/markdown/ -PyPI: https://pypi.org/project/Markdown/ - -Started by Manfred Stienstra (http://www.dwerg.net/). -Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org). -Currently maintained by Waylan Limberg (https://github.com/waylan), -Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser). - -Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later) -Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b) -Copyright 2004 Manfred Stienstra (the original version) - -License: BSD (see LICENSE.md for details). - -PRE-PROCESSORS -============================================================================= - -Preprocessors work on source text before we start doing anything too -complicated. -""" - -from . import util -from .htmlparser import HTMLExtractor -import re - - -def build_preprocessors(md, **kwargs): - """ Build the default set of preprocessors used by Markdown. """ - preprocessors = util.Registry() - preprocessors.register(NormalizeWhitespace(md), 'normalize_whitespace', 30) - preprocessors.register(HtmlBlockPreprocessor(md), 'html_block', 20) - return preprocessors - - -class Preprocessor(util.Processor): - """ - Preprocessors are run after the text is broken into lines. - - Each preprocessor implements a "run" method that takes a pointer to a - list of lines of the document, modifies it as necessary and returns - either the same pointer or a pointer to a new list. - - Preprocessors must extend markdown.Preprocessor. - - """ - def run(self, lines): - """ - Each subclass of Preprocessor should override the `run` method, which - takes the document as a list of strings split by newlines and returns - the (possibly modified) list of lines. - - """ - pass # pragma: no cover - - -class NormalizeWhitespace(Preprocessor): - """ Normalize whitespace for consistent parsing. """ - - def run(self, lines): - source = '\n'.join(lines) - source = source.replace(util.STX, "").replace(util.ETX, "") - source = source.replace("\r\n", "\n").replace("\r", "\n") + "\n\n" - source = source.expandtabs(self.md.tab_length) - source = re.sub(r'(?<=\n) +\n', '\n', source) - return source.split('\n') - - -class HtmlBlockPreprocessor(Preprocessor): - """Remove html blocks from the text and store them for later retrieval.""" - - def run(self, lines): - source = '\n'.join(lines) - parser = HTMLExtractor(self.md) - parser.feed(source) - parser.close() - return ''.join(parser.cleandoc).split('\n') diff --git a/Source/Libs/markdown/serializers.py b/Source/Libs/markdown/serializers.py deleted file mode 100644 index 59bab18..0000000 --- a/Source/Libs/markdown/serializers.py +++ /dev/null @@ -1,189 +0,0 @@ -# markdown/searializers.py -# -# Add x/html serialization to Elementree -# Taken from ElementTree 1.3 preview with slight modifications -# -# Copyright (c) 1999-2007 by Fredrik Lundh. All rights reserved. -# -# fredrik@pythonware.com -# https://www.pythonware.com/ -# -# -------------------------------------------------------------------- -# The ElementTree toolkit is -# -# Copyright (c) 1999-2007 by Fredrik Lundh -# -# By obtaining, using, and/or copying this software and/or its -# associated documentation, you agree that you have read, understood, -# and will comply with the following terms and conditions: -# -# Permission to use, copy, modify, and distribute this software and -# its associated documentation for any purpose and without fee is -# hereby granted, provided that the above copyright notice appears in -# all copies, and that both that copyright notice and this permission -# notice appear in supporting documentation, and that the name of -# Secret Labs AB or the author not be used in advertising or publicity -# pertaining to distribution of the software without specific, written -# prior permission. -# -# SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD -# TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT- -# ABILITY AND FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR -# BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY -# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, -# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS -# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE -# OF THIS SOFTWARE. -# -------------------------------------------------------------------- - - -from xml.etree.ElementTree import ProcessingInstruction -from xml.etree.ElementTree import Comment, ElementTree, QName -import re - -__all__ = ['to_html_string', 'to_xhtml_string'] - -HTML_EMPTY = ("area", "base", "basefont", "br", "col", "frame", "hr", - "img", "input", "isindex", "link", "meta", "param") -RE_AMP = re.compile(r'&(?!(?:\#[0-9]+|\#x[0-9a-f]+|[0-9a-z]+);)', re.I) - -try: - HTML_EMPTY = set(HTML_EMPTY) -except NameError: # pragma: no cover - pass - - -def _raise_serialization_error(text): # pragma: no cover - raise TypeError( - "cannot serialize {!r} (type {})".format(text, type(text).__name__) - ) - - -def _escape_cdata(text): - # escape character data - try: - # it's worth avoiding do-nothing calls for strings that are - # shorter than 500 character, or so. assume that's, by far, - # the most common case in most applications. - if "&" in text: - # Only replace & when not part of an entity - text = RE_AMP.sub('&', text) - if "<" in text: - text = text.replace("<", "<") - if ">" in text: - text = text.replace(">", ">") - return text - except (TypeError, AttributeError): # pragma: no cover - _raise_serialization_error(text) - - -def _escape_attrib(text): - # escape attribute value - try: - if "&" in text: - # Only replace & when not part of an entity - text = RE_AMP.sub('&', text) - if "<" in text: - text = text.replace("<", "<") - if ">" in text: - text = text.replace(">", ">") - if "\"" in text: - text = text.replace("\"", """) - if "\n" in text: - text = text.replace("\n", " ") - return text - except (TypeError, AttributeError): # pragma: no cover - _raise_serialization_error(text) - - -def _escape_attrib_html(text): - # escape attribute value - try: - if "&" in text: - # Only replace & when not part of an entity - text = RE_AMP.sub('&', text) - if "<" in text: - text = text.replace("<", "<") - if ">" in text: - text = text.replace(">", ">") - if "\"" in text: - text = text.replace("\"", """) - return text - except (TypeError, AttributeError): # pragma: no cover - _raise_serialization_error(text) - - -def _serialize_html(write, elem, format): - tag = elem.tag - text = elem.text - if tag is Comment: - write("<!--%s-->" % _escape_cdata(text)) - elif tag is ProcessingInstruction: - write("<?%s?>" % _escape_cdata(text)) - elif tag is None: - if text: - write(_escape_cdata(text)) - for e in elem: - _serialize_html(write, e, format) - else: - namespace_uri = None - if isinstance(tag, QName): - # QNAME objects store their data as a string: `{uri}tag` - if tag.text[:1] == "{": - namespace_uri, tag = tag.text[1:].split("}", 1) - else: - raise ValueError('QName objects must define a tag.') - write("<" + tag) - items = elem.items() - if items: - items = sorted(items) # lexical order - for k, v in items: - if isinstance(k, QName): - # Assume a text only QName - k = k.text - if isinstance(v, QName): - # Assume a text only QName - v = v.text - else: - v = _escape_attrib_html(v) - if k == v and format == 'html': - # handle boolean attributes - write(" %s" % v) - else: - write(' {}="{}"'.format(k, v)) - if namespace_uri: - write(' xmlns="%s"' % (_escape_attrib(namespace_uri))) - if format == "xhtml" and tag.lower() in HTML_EMPTY: - write(" />") - else: - write(">") - if text: - if tag.lower() in ["script", "style"]: - write(text) - else: - write(_escape_cdata(text)) - for e in elem: - _serialize_html(write, e, format) - if tag.lower() not in HTML_EMPTY: - write("</" + tag + ">") - if elem.tail: - write(_escape_cdata(elem.tail)) - - -def _write_html(root, format="html"): - assert root is not None - data = [] - write = data.append - _serialize_html(write, root, format) - return "".join(data) - - -# -------------------------------------------------------------------- -# public functions - -def to_html_string(element): - return _write_html(ElementTree(element).getroot(), format="html") - - -def to_xhtml_string(element): - return _write_html(ElementTree(element).getroot(), format="xhtml") diff --git a/Source/Libs/markdown/test_tools.py b/Source/Libs/markdown/test_tools.py deleted file mode 100644 index 2ce0e74..0000000 --- a/Source/Libs/markdown/test_tools.py +++ /dev/null @@ -1,220 +0,0 @@ -""" -Python Markdown - -A Python implementation of John Gruber's Markdown. - -Documentation: https://python-markdown.github.io/ -GitHub: https://github.com/Python-Markdown/markdown/ -PyPI: https://pypi.org/project/Markdown/ - -Started by Manfred Stienstra (http://www.dwerg.net/). -Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org). -Currently maintained by Waylan Limberg (https://github.com/waylan), -Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser). - -Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later) -Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b) -Copyright 2004 Manfred Stienstra (the original version) - -License: BSD (see LICENSE.md for details). -""" - -import os -import sys -import unittest -import textwrap -from . import markdown, Markdown, util - -try: - import tidylib -except ImportError: - tidylib = None - -__all__ = ['TestCase', 'LegacyTestCase', 'Kwargs'] - - -class TestCase(unittest.TestCase): - """ - A unittest.TestCase subclass with helpers for testing Markdown output. - - Define `default_kwargs` as a dict of keywords to pass to Markdown for each - test. The defaults can be overridden on individual tests. - - The `assertMarkdownRenders` method accepts the source text, the expected - output, and any keywords to pass to Markdown. The `default_kwargs` are used - except where overridden by `kwargs`. The output and expected output are passed - to `TestCase.assertMultiLineEqual`. An AssertionError is raised with a diff - if the actual output does not equal the expected output. - - The `dedent` method is available to dedent triple-quoted strings if - necessary. - - In all other respects, behaves as unittest.TestCase. - """ - - default_kwargs = {} - - def assertMarkdownRenders(self, source, expected, expected_attrs=None, **kwargs): - """ - Test that source Markdown text renders to expected output with given keywords. - - `expected_attrs` accepts a dict. Each key should be the name of an attribute - on the `Markdown` instance and the value should be the expected value after - the source text is parsed by Markdown. After the expected output is tested, - the expected value for each attribute is compared against the actual - attribute of the `Markdown` instance using `TestCase.assertEqual`. - """ - - expected_attrs = expected_attrs or {} - kws = self.default_kwargs.copy() - kws.update(kwargs) - md = Markdown(**kws) - output = md.convert(source) - self.assertMultiLineEqual(output, expected) - for key, value in expected_attrs.items(): - self.assertEqual(getattr(md, key), value) - - def dedent(self, text): - """ - Dedent text. - """ - - # TODO: If/when actual output ends with a newline, then use: - # return textwrap.dedent(text.strip('/n')) - return textwrap.dedent(text).strip() - - -class recursionlimit: - """ - A context manager which temporarily modifies the Python recursion limit. - - The testing framework, coverage, etc. may add an arbitrary number of levels to the depth. To maintain consistency - in the tests, the current stack depth is determined when called, then added to the provided limit. - - Example usage: - - with recursionlimit(20): - # test code here - - See https://stackoverflow.com/a/50120316/866026 - """ - - def __init__(self, limit): - self.limit = util._get_stack_depth() + limit - self.old_limit = sys.getrecursionlimit() - - def __enter__(self): - sys.setrecursionlimit(self.limit) - - def __exit__(self, type, value, tb): - sys.setrecursionlimit(self.old_limit) - - -######################### -# Legacy Test Framework # -######################### - - -class Kwargs(dict): - """ A dict like class for holding keyword arguments. """ - pass - - -def _normalize_whitespace(text): - """ Normalize whitespace for a string of html using tidylib. """ - output, errors = tidylib.tidy_fragment(text, options={ - 'drop_empty_paras': 0, - 'fix_backslash': 0, - 'fix_bad_comments': 0, - 'fix_uri': 0, - 'join_styles': 0, - 'lower_literals': 0, - 'merge_divs': 0, - 'output_xhtml': 1, - 'quote_ampersand': 0, - 'newline': 'LF' - }) - return output - - -class LegacyTestMeta(type): - def __new__(cls, name, bases, dct): - - def generate_test(infile, outfile, normalize, kwargs): - def test(self): - with open(infile, encoding="utf-8") as f: - input = f.read() - with open(outfile, encoding="utf-8") as f: - # Normalize line endings - # (on Windows, git may have altered line endings). - expected = f.read().replace("\r\n", "\n") - output = markdown(input, **kwargs) - if tidylib and normalize: - try: - expected = _normalize_whitespace(expected) - output = _normalize_whitespace(output) - except OSError: - self.skipTest("Tidylib's c library not available.") - elif normalize: - self.skipTest('Tidylib not available.') - self.assertMultiLineEqual(output, expected) - return test - - location = dct.get('location', '') - exclude = dct.get('exclude', []) - normalize = dct.get('normalize', False) - input_ext = dct.get('input_ext', '.txt') - output_ext = dct.get('output_ext', '.html') - kwargs = dct.get('default_kwargs', Kwargs()) - - if os.path.isdir(location): - for file in os.listdir(location): - infile = os.path.join(location, file) - if os.path.isfile(infile): - tname, ext = os.path.splitext(file) - if ext == input_ext: - outfile = os.path.join(location, tname + output_ext) - tname = tname.replace(' ', '_').replace('-', '_') - kws = kwargs.copy() - if tname in dct: - kws.update(dct[tname]) - test_name = 'test_%s' % tname - if tname not in exclude: - dct[test_name] = generate_test(infile, outfile, normalize, kws) - else: - dct[test_name] = unittest.skip('Excluded')(lambda: None) - - return type.__new__(cls, name, bases, dct) - - -class LegacyTestCase(unittest.TestCase, metaclass=LegacyTestMeta): - """ - A `unittest.TestCase` subclass for running Markdown's legacy file-based tests. - - A subclass should define various properties which point to a directory of - text-based test files and define various behaviors/defaults for those tests. - The following properties are supported: - - location: A path to the directory of test files. An absolute path is preferred. - exclude: A list of tests to exclude. Each test name should comprise the filename - without an extension. - normalize: A boolean value indicating if the HTML should be normalized. - Default: `False`. - input_ext: A string containing the file extension of input files. Default: `.txt`. - ouput_ext: A string containing the file extension of expected output files. - Default: `html`. - default_kwargs: A `Kwargs` instance which stores the default set of keyword - arguments for all test files in the directory. - - In addition, properties can be defined for each individual set of test files within - the directory. The property should be given the name of the file without the file - extension. Any spaces and dashes in the filename should be replaced with - underscores. The value of the property should be a `Kwargs` instance which - contains the keyword arguments that should be passed to `Markdown` for that - test file. The keyword arguments will "update" the `default_kwargs`. - - When the class instance is created, it will walk the given directory and create - a separate unitttest for each set of test files using the naming scheme: - `test_filename`. One unittest will be run for each set of input and output files. - """ - pass diff --git a/Source/Libs/markdown/treeprocessors.py b/Source/Libs/markdown/treeprocessors.py deleted file mode 100644 index a475fde..0000000 --- a/Source/Libs/markdown/treeprocessors.py +++ /dev/null @@ -1,436 +0,0 @@ -""" -Python Markdown - -A Python implementation of John Gruber's Markdown. - -Documentation: https://python-markdown.github.io/ -GitHub: https://github.com/Python-Markdown/markdown/ -PyPI: https://pypi.org/project/Markdown/ - -Started by Manfred Stienstra (http://www.dwerg.net/). -Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org). -Currently maintained by Waylan Limberg (https://github.com/waylan), -Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser). - -Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later) -Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b) -Copyright 2004 Manfred Stienstra (the original version) - -License: BSD (see LICENSE.md for details). -""" - -import xml.etree.ElementTree as etree -from . import util -from . import inlinepatterns - - -def build_treeprocessors(md, **kwargs): - """ Build the default treeprocessors for Markdown. """ - treeprocessors = util.Registry() - treeprocessors.register(InlineProcessor(md), 'inline', 20) - treeprocessors.register(PrettifyTreeprocessor(md), 'prettify', 10) - return treeprocessors - - -def isString(s): - """ Check if it's string """ - if not isinstance(s, util.AtomicString): - return isinstance(s, str) - return False - - -class Treeprocessor(util.Processor): - """ - Treeprocessors are run on the ElementTree object before serialization. - - Each Treeprocessor implements a "run" method that takes a pointer to an - ElementTree, modifies it as necessary and returns an ElementTree - object. - - Treeprocessors must extend markdown.Treeprocessor. - - """ - def run(self, root): - """ - Subclasses of Treeprocessor should implement a `run` method, which - takes a root ElementTree. This method can return another ElementTree - object, and the existing root ElementTree will be replaced, or it can - modify the current tree and return None. - """ - pass # pragma: no cover - - -class InlineProcessor(Treeprocessor): - """ - A Treeprocessor that traverses a tree, applying inline patterns. - """ - - def __init__(self, md): - self.__placeholder_prefix = util.INLINE_PLACEHOLDER_PREFIX - self.__placeholder_suffix = util.ETX - self.__placeholder_length = 4 + len(self.__placeholder_prefix) \ - + len(self.__placeholder_suffix) - self.__placeholder_re = util.INLINE_PLACEHOLDER_RE - self.md = md - self.inlinePatterns = md.inlinePatterns - self.ancestors = [] - - @property - @util.deprecated("Use 'md' instead.") - def markdown(self): - # TODO: remove this later - return self.md - - def __makePlaceholder(self, type): - """ Generate a placeholder """ - id = "%04d" % len(self.stashed_nodes) - hash = util.INLINE_PLACEHOLDER % id - return hash, id - - def __findPlaceholder(self, data, index): - """ - Extract id from data string, start from index - - Keyword arguments: - - * data: string - * index: index, from which we start search - - Returns: placeholder id and string index, after the found placeholder. - - """ - m = self.__placeholder_re.search(data, index) - if m: - return m.group(1), m.end() - else: - return None, index + 1 - - def __stashNode(self, node, type): - """ Add node to stash """ - placeholder, id = self.__makePlaceholder(type) - self.stashed_nodes[id] = node - return placeholder - - def __handleInline(self, data, patternIndex=0): - """ - Process string with inline patterns and replace it - with placeholders - - Keyword arguments: - - * data: A line of Markdown text - * patternIndex: The index of the inlinePattern to start with - - Returns: String with placeholders. - - """ - if not isinstance(data, util.AtomicString): - startIndex = 0 - count = len(self.inlinePatterns) - while patternIndex < count: - data, matched, startIndex = self.__applyPattern( - self.inlinePatterns[patternIndex], data, patternIndex, startIndex - ) - if not matched: - patternIndex += 1 - return data - - def __processElementText(self, node, subnode, isText=True): - """ - Process placeholders in Element.text or Element.tail - of Elements popped from self.stashed_nodes. - - Keywords arguments: - - * node: parent node - * subnode: processing node - * isText: bool variable, True - it's text, False - it's tail - - Returns: None - - """ - if isText: - text = subnode.text - subnode.text = None - else: - text = subnode.tail - subnode.tail = None - - childResult = self.__processPlaceholders(text, subnode, isText) - - if not isText and node is not subnode: - pos = list(node).index(subnode) + 1 - else: - pos = 0 - - childResult.reverse() - for newChild in childResult: - node.insert(pos, newChild[0]) - - def __processPlaceholders(self, data, parent, isText=True): - """ - Process string with placeholders and generate ElementTree tree. - - Keyword arguments: - - * data: string with placeholders instead of ElementTree elements. - * parent: Element, which contains processing inline data - - Returns: list with ElementTree elements with applied inline patterns. - - """ - def linkText(text): - if text: - if result: - if result[-1][0].tail: - result[-1][0].tail += text - else: - result[-1][0].tail = text - elif not isText: - if parent.tail: - parent.tail += text - else: - parent.tail = text - else: - if parent.text: - parent.text += text - else: - parent.text = text - result = [] - strartIndex = 0 - while data: - index = data.find(self.__placeholder_prefix, strartIndex) - if index != -1: - id, phEndIndex = self.__findPlaceholder(data, index) - - if id in self.stashed_nodes: - node = self.stashed_nodes.get(id) - - if index > 0: - text = data[strartIndex:index] - linkText(text) - - if not isString(node): # it's Element - for child in [node] + list(node): - if child.tail: - if child.tail.strip(): - self.__processElementText( - node, child, False - ) - if child.text: - if child.text.strip(): - self.__processElementText(child, child) - else: # it's just a string - linkText(node) - strartIndex = phEndIndex - continue - - strartIndex = phEndIndex - result.append((node, self.ancestors[:])) - - else: # wrong placeholder - end = index + len(self.__placeholder_prefix) - linkText(data[strartIndex:end]) - strartIndex = end - else: - text = data[strartIndex:] - if isinstance(data, util.AtomicString): - # We don't want to loose the AtomicString - text = util.AtomicString(text) - linkText(text) - data = "" - - return result - - def __applyPattern(self, pattern, data, patternIndex, startIndex=0): - """ - Check if the line fits the pattern, create the necessary - elements, add it to stashed_nodes. - - Keyword arguments: - - * data: the text to be processed - * pattern: the pattern to be checked - * patternIndex: index of current pattern - * startIndex: string index, from which we start searching - - Returns: String with placeholders instead of ElementTree elements. - - """ - new_style = isinstance(pattern, inlinepatterns.InlineProcessor) - - for exclude in pattern.ANCESTOR_EXCLUDES: - if exclude.lower() in self.ancestors: - return data, False, 0 - - if new_style: - match = None - # Since handleMatch may reject our first match, - # we iterate over the buffer looking for matches - # until we can't find any more. - for match in pattern.getCompiledRegExp().finditer(data, startIndex): - node, start, end = pattern.handleMatch(match, data) - if start is None or end is None: - startIndex += match.end(0) - match = None - continue - break - else: # pragma: no cover - match = pattern.getCompiledRegExp().match(data[startIndex:]) - leftData = data[:startIndex] - - if not match: - return data, False, 0 - - if not new_style: # pragma: no cover - node = pattern.handleMatch(match) - start = match.start(0) - end = match.end(0) - - if node is None: - return data, True, end - - if not isString(node): - if not isinstance(node.text, util.AtomicString): - # We need to process current node too - for child in [node] + list(node): - if not isString(node): - if child.text: - self.ancestors.append(child.tag.lower()) - child.text = self.__handleInline( - child.text, patternIndex + 1 - ) - self.ancestors.pop() - if child.tail: - child.tail = self.__handleInline( - child.tail, patternIndex - ) - - placeholder = self.__stashNode(node, pattern.type()) - - if new_style: - return "{}{}{}".format(data[:start], - placeholder, data[end:]), True, 0 - else: # pragma: no cover - return "{}{}{}{}".format(leftData, - match.group(1), - placeholder, match.groups()[-1]), True, 0 - - def __build_ancestors(self, parent, parents): - """Build the ancestor list.""" - ancestors = [] - while parent is not None: - if parent is not None: - ancestors.append(parent.tag.lower()) - parent = self.parent_map.get(parent) - ancestors.reverse() - parents.extend(ancestors) - - def run(self, tree, ancestors=None): - """Apply inline patterns to a parsed Markdown tree. - - Iterate over ElementTree, find elements with inline tag, apply inline - patterns and append newly created Elements to tree. If you don't - want to process your data with inline patterns, instead of normal - string, use subclass AtomicString: - - node.text = markdown.AtomicString("This will not be processed.") - - Arguments: - - * tree: ElementTree object, representing Markdown tree. - * ancestors: List of parent tag names that precede the tree node (if needed). - - Returns: ElementTree object with applied inline patterns. - - """ - self.stashed_nodes = {} - - # Ensure a valid parent list, but copy passed in lists - # to ensure we don't have the user accidentally change it on us. - tree_parents = [] if ancestors is None else ancestors[:] - - self.parent_map = {c: p for p in tree.iter() for c in p} - stack = [(tree, tree_parents)] - - while stack: - currElement, parents = stack.pop() - - self.ancestors = parents - self.__build_ancestors(currElement, self.ancestors) - - insertQueue = [] - for child in currElement: - if child.text and not isinstance( - child.text, util.AtomicString - ): - self.ancestors.append(child.tag.lower()) - text = child.text - child.text = None - lst = self.__processPlaceholders( - self.__handleInline(text), child - ) - for item in lst: - self.parent_map[item[0]] = child - stack += lst - insertQueue.append((child, lst)) - self.ancestors.pop() - if child.tail: - tail = self.__handleInline(child.tail) - dumby = etree.Element('d') - child.tail = None - tailResult = self.__processPlaceholders(tail, dumby, False) - if dumby.tail: - child.tail = dumby.tail - pos = list(currElement).index(child) + 1 - tailResult.reverse() - for newChild in tailResult: - self.parent_map[newChild[0]] = currElement - currElement.insert(pos, newChild[0]) - if len(child): - self.parent_map[child] = currElement - stack.append((child, self.ancestors[:])) - - for element, lst in insertQueue: - for i, obj in enumerate(lst): - newChild = obj[0] - element.insert(i, newChild) - return tree - - -class PrettifyTreeprocessor(Treeprocessor): - """ Add linebreaks to the html document. """ - - def _prettifyETree(self, elem): - """ Recursively add linebreaks to ElementTree children. """ - - i = "\n" - if self.md.is_block_level(elem.tag) and elem.tag not in ['code', 'pre']: - if (not elem.text or not elem.text.strip()) \ - and len(elem) and self.md.is_block_level(elem[0].tag): - elem.text = i - for e in elem: - if self.md.is_block_level(e.tag): - self._prettifyETree(e) - if not elem.tail or not elem.tail.strip(): - elem.tail = i - if not elem.tail or not elem.tail.strip(): - elem.tail = i - - def run(self, root): - """ Add linebreaks to ElementTree root object. """ - - self._prettifyETree(root) - # Do <br />'s separately as they are often in the middle of - # inline content and missed by _prettifyETree. - brs = root.iter('br') - for br in brs: - if not br.tail or not br.tail.strip(): - br.tail = '\n' - else: - br.tail = '\n%s' % br.tail - # Clean up extra empty lines at end of code blocks. - pres = root.iter('pre') - for pre in pres: - if len(pre) and pre[0].tag == 'code': - pre[0].text = util.AtomicString(pre[0].text.rstrip() + '\n') diff --git a/Source/Libs/markdown/util.py b/Source/Libs/markdown/util.py deleted file mode 100644 index 46cfe68..0000000 --- a/Source/Libs/markdown/util.py +++ /dev/null @@ -1,485 +0,0 @@ -""" -Python Markdown - -A Python implementation of John Gruber's Markdown. - -Documentation: https://python-markdown.github.io/ -GitHub: https://github.com/Python-Markdown/markdown/ -PyPI: https://pypi.org/project/Markdown/ - -Started by Manfred Stienstra (http://www.dwerg.net/). -Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org). -Currently maintained by Waylan Limberg (https://github.com/waylan), -Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser). - -Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later) -Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b) -Copyright 2004 Manfred Stienstra (the original version) - -License: BSD (see LICENSE.md for details). -""" - -import re -import sys -import warnings -import xml.etree.ElementTree -from collections import namedtuple -from functools import wraps -from itertools import count - -from .pep562 import Pep562 - -if sys.version_info >= (3, 10): - from importlib import metadata -else: - # <PY310 use backport - import importlib_metadata as metadata - -PY37 = (3, 7) <= sys.version_info - - -# TODO: Remove deprecated variables in a future release. -__deprecated__ = { - 'etree': ('xml.etree.ElementTree', xml.etree.ElementTree), - 'string_type': ('str', str), - 'text_type': ('str', str), - 'int2str': ('chr', chr), - 'iterrange': ('range', range) -} - - -""" -Constants you might want to modify ------------------------------------------------------------------------------ -""" - - -BLOCK_LEVEL_ELEMENTS = [ - # Elements which are invalid to wrap in a `<p>` tag. - # See https://w3c.github.io/html/grouping-content.html#the-p-element - 'address', 'article', 'aside', 'blockquote', 'details', 'div', 'dl', - 'fieldset', 'figcaption', 'figure', 'footer', 'form', 'h1', 'h2', 'h3', - 'h4', 'h5', 'h6', 'header', 'hgroup', 'hr', 'main', 'menu', 'nav', 'ol', - 'p', 'pre', 'section', 'table', 'ul', - # Other elements which Markdown should not be mucking up the contents of. - 'canvas', 'colgroup', 'dd', 'body', 'dt', 'group', 'iframe', 'li', 'legend', - 'math', 'map', 'noscript', 'output', 'object', 'option', 'progress', 'script', - 'style', 'tbody', 'td', 'textarea', 'tfoot', 'th', 'thead', 'tr', 'video' -] - -# Placeholders -STX = '\u0002' # Use STX ("Start of text") for start-of-placeholder -ETX = '\u0003' # Use ETX ("End of text") for end-of-placeholder -INLINE_PLACEHOLDER_PREFIX = STX+"klzzwxh:" -INLINE_PLACEHOLDER = INLINE_PLACEHOLDER_PREFIX + "%s" + ETX -INLINE_PLACEHOLDER_RE = re.compile(INLINE_PLACEHOLDER % r'([0-9]+)') -AMP_SUBSTITUTE = STX+"amp"+ETX -HTML_PLACEHOLDER = STX + "wzxhzdk:%s" + ETX -HTML_PLACEHOLDER_RE = re.compile(HTML_PLACEHOLDER % r'([0-9]+)') -TAG_PLACEHOLDER = STX + "hzzhzkh:%s" + ETX - - -""" -Constants you probably do not need to change ------------------------------------------------------------------------------ -""" - -# Only load extension entry_points once. -INSTALLED_EXTENSIONS = metadata.entry_points(group='markdown.extensions') -RTL_BIDI_RANGES = ( - ('\u0590', '\u07FF'), - # Hebrew (0590-05FF), Arabic (0600-06FF), - # Syriac (0700-074F), Arabic supplement (0750-077F), - # Thaana (0780-07BF), Nko (07C0-07FF). - ('\u2D30', '\u2D7F') # Tifinagh -) - - -""" -AUXILIARY GLOBAL FUNCTIONS -============================================================================= -""" - - -def deprecated(message, stacklevel=2): - """ - Raise a DeprecationWarning when wrapped function/method is called. - - Usage: - @deprecated("This method will be removed in version X; use Y instead.") - def some_method()" - pass - """ - def wrapper(func): - @wraps(func) - def deprecated_func(*args, **kwargs): - warnings.warn( - f"'{func.__name__}' is deprecated. {message}", - category=DeprecationWarning, - stacklevel=stacklevel - ) - return func(*args, **kwargs) - return deprecated_func - return wrapper - - -@deprecated("Use 'Markdown.is_block_level' instead.") -def isBlockLevel(tag): - """Check if the tag is a block level HTML tag.""" - if isinstance(tag, str): - return tag.lower().rstrip('/') in BLOCK_LEVEL_ELEMENTS - # Some ElementTree tags are not strings, so return False. - return False - - -def parseBoolValue(value, fail_on_errors=True, preserve_none=False): - """Parses a string representing bool value. If parsing was successful, - returns True or False. If preserve_none=True, returns True, False, - or None. If parsing was not successful, raises ValueError, or, if - fail_on_errors=False, returns None.""" - if not isinstance(value, str): - if preserve_none and value is None: - return value - return bool(value) - elif preserve_none and value.lower() == 'none': - return None - elif value.lower() in ('true', 'yes', 'y', 'on', '1'): - return True - elif value.lower() in ('false', 'no', 'n', 'off', '0', 'none'): - return False - elif fail_on_errors: - raise ValueError('Cannot parse bool value: %r' % value) - - -def code_escape(text): - """Escape code.""" - if "&" in text: - text = text.replace("&", "&") - if "<" in text: - text = text.replace("<", "<") - if ">" in text: - text = text.replace(">", ">") - return text - - -def _get_stack_depth(size=2): - """Get current stack depth, performantly. - """ - frame = sys._getframe(size) - - for size in count(size): - frame = frame.f_back - if not frame: - return size - - -def nearing_recursion_limit(): - """Return true if current stack depth is within 100 of maximum limit.""" - return sys.getrecursionlimit() - _get_stack_depth() < 100 - - -""" -MISC AUXILIARY CLASSES -============================================================================= -""" - - -class AtomicString(str): - """A string which should not be further processed.""" - pass - - -class Processor: - def __init__(self, md=None): - self.md = md - - @property - @deprecated("Use 'md' instead.") - def markdown(self): - # TODO: remove this later - return self.md - - -class HtmlStash: - """ - This class is used for stashing HTML objects that we extract - in the beginning and replace with place-holders. - """ - - def __init__(self): - """ Create a HtmlStash. """ - self.html_counter = 0 # for counting inline html segments - self.rawHtmlBlocks = [] - self.tag_counter = 0 - self.tag_data = [] # list of dictionaries in the order tags appear - - def store(self, html): - """ - Saves an HTML segment for later reinsertion. Returns a - placeholder string that needs to be inserted into the - document. - - Keyword arguments: - - * html: an html segment - - Returns : a placeholder string - - """ - self.rawHtmlBlocks.append(html) - placeholder = self.get_placeholder(self.html_counter) - self.html_counter += 1 - return placeholder - - def reset(self): - self.html_counter = 0 - self.rawHtmlBlocks = [] - - def get_placeholder(self, key): - return HTML_PLACEHOLDER % key - - def store_tag(self, tag, attrs, left_index, right_index): - """Store tag data and return a placeholder.""" - self.tag_data.append({'tag': tag, 'attrs': attrs, - 'left_index': left_index, - 'right_index': right_index}) - placeholder = TAG_PLACEHOLDER % str(self.tag_counter) - self.tag_counter += 1 # equal to the tag's index in self.tag_data - return placeholder - - -# Used internally by `Registry` for each item in its sorted list. -# Provides an easier to read API when editing the code later. -# For example, `item.name` is more clear than `item[0]`. -_PriorityItem = namedtuple('PriorityItem', ['name', 'priority']) - - -class Registry: - """ - A priority sorted registry. - - A `Registry` instance provides two public methods to alter the data of the - registry: `register` and `deregister`. Use `register` to add items and - `deregister` to remove items. See each method for specifics. - - When registering an item, a "name" and a "priority" must be provided. All - items are automatically sorted by "priority" from highest to lowest. The - "name" is used to remove ("deregister") and get items. - - A `Registry` instance it like a list (which maintains order) when reading - data. You may iterate over the items, get an item and get a count (length) - of all items. You may also check that the registry contains an item. - - When getting an item you may use either the index of the item or the - string-based "name". For example: - - registry = Registry() - registry.register(SomeItem(), 'itemname', 20) - # Get the item by index - item = registry[0] - # Get the item by name - item = registry['itemname'] - - When checking that the registry contains an item, you may use either the - string-based "name", or a reference to the actual item. For example: - - someitem = SomeItem() - registry.register(someitem, 'itemname', 20) - # Contains the name - assert 'itemname' in registry - # Contains the item instance - assert someitem in registry - - The method `get_index_for_name` is also available to obtain the index of - an item using that item's assigned "name". - """ - - def __init__(self): - self._data = {} - self._priority = [] - self._is_sorted = False - - def __contains__(self, item): - if isinstance(item, str): - # Check if an item exists by this name. - return item in self._data.keys() - # Check if this instance exists. - return item in self._data.values() - - def __iter__(self): - self._sort() - return iter([self._data[k] for k, p in self._priority]) - - def __getitem__(self, key): - self._sort() - if isinstance(key, slice): - data = Registry() - for k, p in self._priority[key]: - data.register(self._data[k], k, p) - return data - if isinstance(key, int): - return self._data[self._priority[key].name] - return self._data[key] - - def __len__(self): - return len(self._priority) - - def __repr__(self): - return '<{}({})>'.format(self.__class__.__name__, list(self)) - - def get_index_for_name(self, name): - """ - Return the index of the given name. - """ - if name in self: - self._sort() - return self._priority.index( - [x for x in self._priority if x.name == name][0] - ) - raise ValueError('No item named "{}" exists.'.format(name)) - - def register(self, item, name, priority): - """ - Add an item to the registry with the given name and priority. - - Parameters: - - * `item`: The item being registered. - * `name`: A string used to reference the item. - * `priority`: An integer or float used to sort against all items. - - If an item is registered with a "name" which already exists, the - existing item is replaced with the new item. Treat carefully as the - old item is lost with no way to recover it. The new item will be - sorted according to its priority and will **not** retain the position - of the old item. - """ - if name in self: - # Remove existing item of same name first - self.deregister(name) - self._is_sorted = False - self._data[name] = item - self._priority.append(_PriorityItem(name, priority)) - - def deregister(self, name, strict=True): - """ - Remove an item from the registry. - - Set `strict=False` to fail silently. - """ - try: - index = self.get_index_for_name(name) - del self._priority[index] - del self._data[name] - except ValueError: - if strict: - raise - - def _sort(self): - """ - Sort the registry by priority from highest to lowest. - - This method is called internally and should never be explicitly called. - """ - if not self._is_sorted: - self._priority.sort(key=lambda item: item.priority, reverse=True) - self._is_sorted = True - - # Deprecated Methods which provide a smooth transition from OrderedDict - - def __setitem__(self, key, value): - """ Register item with priority 5 less than lowest existing priority. """ - if isinstance(key, str): - warnings.warn( - 'Using setitem to register a processor or pattern is deprecated. ' - 'Use the `register` method instead.', - DeprecationWarning, - stacklevel=2, - ) - if key in self: - # Key already exists, replace without altering priority - self._data[key] = value - return - if len(self) == 0: - # This is the first item. Set priority to 50. - priority = 50 - else: - self._sort() - priority = self._priority[-1].priority - 5 - self.register(value, key, priority) - else: - raise TypeError - - def __delitem__(self, key): - """ Deregister an item by name. """ - if key in self: - self.deregister(key) - warnings.warn( - 'Using del to remove a processor or pattern is deprecated. ' - 'Use the `deregister` method instead.', - DeprecationWarning, - stacklevel=2, - ) - else: - raise KeyError('Cannot delete key {}, not registered.'.format(key)) - - def add(self, key, value, location): - """ Register a key by location. """ - if len(self) == 0: - # This is the first item. Set priority to 50. - priority = 50 - elif location == '_begin': - self._sort() - # Set priority 5 greater than highest existing priority - priority = self._priority[0].priority + 5 - elif location == '_end': - self._sort() - # Set priority 5 less than lowest existing priority - priority = self._priority[-1].priority - 5 - elif location.startswith('<') or location.startswith('>'): - # Set priority halfway between existing priorities. - i = self.get_index_for_name(location[1:]) - if location.startswith('<'): - after = self._priority[i].priority - if i > 0: - before = self._priority[i-1].priority - else: - # Location is first item` - before = after + 10 - else: - # location.startswith('>') - before = self._priority[i].priority - if i < len(self) - 1: - after = self._priority[i+1].priority - else: - # location is last item - after = before - 10 - priority = before - ((before - after) / 2) - else: - raise ValueError('Not a valid location: "%s". Location key ' - 'must start with a ">" or "<".' % location) - self.register(value, key, priority) - warnings.warn( - 'Using the add method to register a processor or pattern is deprecated. ' - 'Use the `register` method instead.', - DeprecationWarning, - stacklevel=2, - ) - - -def __getattr__(name): - """Get attribute.""" - - deprecated = __deprecated__.get(name) - if deprecated: - warnings.warn( - "'{}' is deprecated. Use '{}' instead.".format(name, deprecated[0]), - category=DeprecationWarning, - stacklevel=(3 if PY37 else 4) - ) - return deprecated[1] - raise AttributeError("module '{}' has no attribute '{}'".format(__name__, name)) - - -if not PY37: - Pep562(__name__)