diff --git a/src/__init__.py b/src/__init__.py new file mode 100644 index 0000000..bda6776 --- /dev/null +++ b/src/__init__.py @@ -0,0 +1,2 @@ +# this initializes the submodule __init__ +from src.markdown import * diff --git a/src/markdown/__init__.py b/src/markdown/__init__.py new file mode 100644 index 0000000..e187b3d --- /dev/null +++ b/src/markdown/__init__.py @@ -0,0 +1,11 @@ +# https://docs.python.org/3/tutorial/modules.html +# you need to define __all__ to be able to let from x import * work... +# there is no out-of-the-box dynamic solution +# see http://stackoverflow.com/questions/1057431/loading-all-modules-in-a-folder-in-python + +from os.path import dirname, basename, isfile +import glob + +modules = glob.glob(dirname(__file__)+"/*.py") +__all__ = [ basename(f)[:-3] for f in modules if isfile(f) and not f.endswith('__init__.py')] +print('loaded markdown converters: ' + ', '.join(__all__)) \ No newline at end of file diff --git a/src/markdown/code.py b/src/markdown/code.py index 6b0c5e6..38bd66c 100644 --- a/src/markdown/code.py +++ b/src/markdown/code.py @@ -1,6 +1,8 @@ from abc import ABC from re import compile +from src.markdown_converter import MarkdownConverter + class BaseMarkdownCode(ABC): markdown = "```" @@ -24,10 +26,12 @@ class BaseMarkdownCode(ABC): result = result.replace(match[0], BaseMarkdownCode.markdown + language) return result.replace('', BaseMarkdownCode.markdown) +@MarkdownConverter.Register class MarkdownFile(BaseMarkdownCode): def __init__(self): super().__init__('file') +@MarkdownConverter.Register class MarkdownCode(BaseMarkdownCode): def __init__(self): super().__init__('code') diff --git a/src/markdown/headers.py b/src/markdown/headers.py index 5f51a5f..5eb8f55 100644 --- a/src/markdown/headers.py +++ b/src/markdown/headers.py @@ -1,7 +1,10 @@ -from collections import OrderedDict from re import compile -class MarkdownHeader: +from src.markdown_converter import MarkdownConverter + + +@MarkdownConverter.Register +class MarkdownHeader(): pattern = compile('(=+)(.*?)(=+)') head = "=" config = { diff --git a/src/markdown/links.py b/src/markdown/links.py index e2dfb06..2310950 100644 --- a/src/markdown/links.py +++ b/src/markdown/links.py @@ -1,8 +1,11 @@ -import os -from os import walk import re +from os import walk -class MarkdownLinks: +from src.markdown_converter import MarkdownConverter + + +@MarkdownConverter.Register +class MarkdownLinks(): # see http://pythex.org/ pattern = re.compile('(\[\[)(.*?)(\]\])') diff --git a/src/markdown/lists.py b/src/markdown/lists.py index d0fc87f..edae4fe 100644 --- a/src/markdown/lists.py +++ b/src/markdown/lists.py @@ -1,6 +1,10 @@ import re -class MarkdownOrderedList: +from src.markdown_converter import MarkdownConverter + + +@MarkdownConverter.Register +class MarkdownOrderedList(): pattern = re.compile('(^-\s)(.*)', re.MULTILINE) def convert(self, text): diff --git a/src/markdown/simplestyle.py b/src/markdown/simplestyle.py index 503433d..17215c6 100644 --- a/src/markdown/simplestyle.py +++ b/src/markdown/simplestyle.py @@ -1,6 +1,9 @@ from abc import ABC from re import compile +from src.markdown_converter import MarkdownConverter + + class NopStyle(ABC): def convert(self, text): return text @@ -29,27 +32,33 @@ class SimpleStyleBetweenTags(ABC): result = result.replace(orig_header, new_header) return result +@MarkdownConverter.Register class MarkdownLineBreak(SimpleReplacementStyle): def __init__(self): super().__init__('
', '\\') # inline html is supported with Hugo, don't need the tags. -class MarkdownInlineHtml: +@MarkdownConverter.Register +class MarkdownInlineHtml(): def convert(self, text): return text.replace('', '').replace('', '') # bold in Doku is bold in MD +@MarkdownConverter.Register class MarkdownBold(NopStyle): pass +@MarkdownConverter.Register class MarkdownItalic(SimpleStyleBetweenTags): def __init__(self): super().__init__('*', '//') +@MarkdownConverter.Register class MarkdownStrikeThrough(SimpleStyleBetweenTags): def __init__(self): super().__init__('~~', '', '') +@MarkdownConverter.Register class MarkdownInlineCode(SimpleStyleBetweenTags): def __init__(self): super().__init__('`', "''", "''") \ No newline at end of file diff --git a/src/markdown_converter.py b/src/markdown_converter.py index e40b30d..2ec3a25 100644 --- a/src/markdown_converter.py +++ b/src/markdown_converter.py @@ -1,33 +1,19 @@ -from functools import reduce - -from src.markdown.links import MarkdownLinks - -from src.markdown.headers import MarkdownHeader -from src.markdown.simplestyle import MarkdownItalic, MarkdownBold, MarkdownStrikeThrough - +from pathlib import Path class MarkdownConverter: + converters = [] + + @classmethod + def Register(cls, converter_class): + cls.converters.append(converter_class()) + return converter_class def __init__(self, file): self.file = file - self.converters = ( - # TODO auto-discover these. How do I do that, without interfaces? - MarkdownHeader(), - MarkdownLinks(), - MarkdownItalic(), - MarkdownBold(), - MarkdownStrikeThrough() - ) def convert(self): - converted = [] - with open(self.file, 'r') as file: - for line in file: - converted.append(self.convert_line(line)) - - return "\n".join(converted) - - def convert_line(self, line): - convertfns = map(lambda converter: converter.convert, self.converters) - massconvert = reduce(lambda red1, red2: lambda text: red1(red2(line)), convertfns, lambda text: text) - return massconvert(line) + text = Path(self.file).read_text() + # TODO solve this functional-style instead of mutating text + for converter in MarkdownConverter.converters: + text = converter.convert(text) + return text \ No newline at end of file diff --git a/test/expected_markdown_output.txt b/test/expected_markdown_output.txt index de57dd9..d2dcb86 100644 --- a/test/expected_markdown_output.txt +++ b/test/expected_markdown_output.txt @@ -1,7 +1,7 @@ -# header 1 +# header 1 -##### header 5 +##### header 5 [hi]({{< relref "hello world" >}}) this is a test! -{{< wp Dogs >}} are cool, look it up. [sublink]({{< relref "link/sub" >}} example. +{{< wp Dogs >}} are cool, look it up. [sublink]({{< relref "link/sub" >}}) example.