From e59a3ce998a8b91a3b151dd0c9c2855d8f2c450e Mon Sep 17 00:00:00 2001 From: erin kirby Date: Mon, 19 Apr 2021 13:22:31 -0400 Subject: [PATCH 1/8] Adds Scoped Code Tabs extension --- docdown/scoped_code_tabs.py | 95 +++++++++++++++++++ docs/docdown.rst | 8 ++ docs/extensions/scoped_code_tabs.rst | 109 ++++++++++++++++++++++ requirements_dev.txt | 3 +- setup.py | 2 + tests/test_scoped_code_tabs.py | 133 +++++++++++++++++++++++++++ 6 files changed, 349 insertions(+), 1 deletion(-) create mode 100644 docdown/scoped_code_tabs.py create mode 100644 docs/extensions/scoped_code_tabs.rst create mode 100644 tests/test_scoped_code_tabs.py diff --git a/docdown/scoped_code_tabs.py b/docdown/scoped_code_tabs.py new file mode 100644 index 0000000..48b9fa0 --- /dev/null +++ b/docdown/scoped_code_tabs.py @@ -0,0 +1,95 @@ +# -*- coding: utf-8 -*- + +""" +scoped_code_tabs +---------------------------------- + +docdown.scoped_code_tabs Markdown extension module +""" +import re + +from markdown.preprocessors import Preprocessor +from markdown_fenced_code_tabs import CodeTabsExtension + + +class ScopedCodeTabsPreprocessor(Preprocessor): + RE_FENCE_START = r'^ *\|\~\s*$' # start line, e.g., ` |~ ` + RE_FENCE_END = r'^\s*\~\|\s*$' # last non-blank line, e.g, '~|\n \n\n' + + def __init__(self, md, code_tabs_preprocessor): + self.code_tabs_preprocessor = code_tabs_preprocessor + super(ScopedCodeTabsPreprocessor, self).__init__(md) + + def run(self, lines): + new_lines = [] + fenced_code_tab = [] + starting_line = None + in_tab = False + + for line in lines: + if re.search(self.RE_FENCE_START, line): + # Start block pattern, save line in case of no end fence + in_tab = True + starting_line = line + elif re.search(self.RE_FENCE_END, line): + # End of code block, run through fenced code tabs pre-processor and reset code tab list + new_lines += self.code_tabs_preprocessor.run(fenced_code_tab) + fenced_code_tab = [] + in_tab = False + elif in_tab: + # Still in tab -- append to tab list + fenced_code_tab.append(line) + else: + # Not in a fenced code tab, and not starting/ending one -- pass as usual + new_lines.append(line) + + # Non-terminated code tab block, append matching starting fence and remaining lines without processing + if fenced_code_tab: + new_lines += [starting_line] + fenced_code_tab + return new_lines + + +class ScopedCodeTabExtension(CodeTabsExtension): + + def __init__(self, **kwargs): + """ + A Markdown extension that serves to scope where Fenced Code Tabs are rendered by way of |~ ... ~| fences. + + Example: + + ## A set of code tabs in Python and Java + |~ + ```python + def main(): + print("This would be passed through markdown_fenced_code_tabs") + ``` + + ```java + public static void main(String[] args) { + System.out.println("This would be passed through markdown_fenced_code_tabs"); + } + ``` + ~| + + ## A regular, non-tabbed code block in Bash + ```bash + codeblockinfo() { + echo("This would NOT be passed through markdown_fenced_code tabs"); + } + ``` + """ + super(ScopedCodeTabExtension, self).__init__(**kwargs) + + def extendMarkdown(self, md, md_globals): + super(ScopedCodeTabExtension, self).extendMarkdown(md, md_globals) + md.registerExtension(self) + + md.preprocessors.add('scoped_code_tabs', + ScopedCodeTabsPreprocessor(md, + code_tabs_preprocessor=md.preprocessors['fenced_code_block']), + ">normalize_whitespace") + del md.preprocessors['fenced_code_block'] + + +def makeExtension(*args, **kwargs): + return ScopedCodeTabExtension(*args, **kwargs) diff --git a/docs/docdown.rst b/docs/docdown.rst index 10806d9..2ee0d48 100644 --- a/docs/docdown.rst +++ b/docs/docdown.rst @@ -59,6 +59,14 @@ docdown.platform_section module :undoc-members: :show-inheritance: +docdown.scoped_code_tabs module +------------------------------- + +.. automodule:: docdown.scoped_code_tabs + :members: + :undoc-members: + :show-inheritance: + docdown.sequence module ----------------------- diff --git a/docs/extensions/scoped_code_tabs.rst b/docs/extensions/scoped_code_tabs.rst new file mode 100644 index 0000000..ebd137a --- /dev/null +++ b/docs/extensions/scoped_code_tabs.rst @@ -0,0 +1,109 @@ +###################### +Scoped Code Tabs +###################### + +Scoped Code Tabs allows for the explicit annotation of when and where to tabulate a set of code blocks versus rendering them +separately. + +A scoped code tab is delimited by an opening ``|~`` fence and closing ``~|`` fence. The code blocks within the fences +are defined as typical code blocks, using backticks, with the opening backtick fence specifying the code language contained +within the block. + +The configuration for rendering the tabs is as directly defined by the `markdown_fenced_code_tabs`_ extension. + + +============= +Dependencies +============= +The ``docdown.scoped_code_tabs`` extension requires the third-party extension `markdown_fenced_code_tabs`_ in order to process +the tabulated fenced code blocks. + +============== +Configuration +============== + +single_block_as_tab + Whether a single code block should still be rendered as a code tab. Default: ``False`` +active_class + The CSS class to apply to the active tab. Default: ``active`` +template + Which template to use to render code tabs. One of: [``default``, ``bootstrap3``, ``bootstrap4``]. Default: ``default`` + Please see the *-template.html files in the `markdown_fenced_code_tabs`_ extension. + + +======= +Usage +======= +In documents +------------- + +.. code-block:: md + + ### Hello World Examples + |~ + ```bash + helloWorld() { + greeting=${1:-World} + echo(`Hello ${greeting}`) + } + ``` + ~| + + ```python + def hello_world(greeting: str = "World") -> None: + print(f"Hello {greeting}") + ``` + +Python +-------------- + +.. code-block:: python + + config = { + 'docdown.scoped_code_tabs': { + 'single_block_as_tab': True, + 'template': 'bootstrap4', + 'active_class': 'tab-active' + } + } + + text = """\ + ### Hello World Examples + |~ + ```bash + helloWorld() { + greeting=${1:-World} + echo(`Hello ${greeting}`) + } + ``` + ~| + ```python + def hello_world(greeting: str = "World") -> None: + print(f"Hello {greeting}") + ``` + """ + + html = markdown.markdown( + text, + extensions=['docdown.scoped_code_tabs'], + extension_configs=config, + output_format='html5') + +======= +Output +======= +Note the extra classes and divs for tabulation around the ``|~`` ``~|`` code block. + +.. code-block:: html + +

Hello World Examples

+

helloWorld() {
+    greeting=${1:-World}
+    echo(`Hello ${greeting}`)
+    }
+    

+

python + def hello_world(greeting: str = "World") -> None: + print(f"Hello {greeting}")

+ +.. _`markdown_fenced_code_tabs`: https://github.com/yacir/markdown-fenced-code-tabs diff --git a/requirements_dev.txt b/requirements_dev.txt index 09c5b14..3875cc6 100644 --- a/requirements_dev.txt +++ b/requirements_dev.txt @@ -8,7 +8,8 @@ coverage==4.1 Sphinx==1.4.8 twine==1.13.0 -Markdown==2.6.6 +Markdown>=2.6.6 +markdown-fenced-code-tabs==1.0.5 unicodecsv==0.14.1 # note_block templating diff --git a/setup.py b/setup.py index 21417ff..2902778 100644 --- a/setup.py +++ b/setup.py @@ -11,7 +11,9 @@ requirements = [ # TODO: put package requirements here + 'Markdown >= 2.6.6', 'unicodecsv >= 0.14.1', + 'markdown-fenced-code-tabs >= 1.0.5', ] test_requirements = [ diff --git a/tests/test_scoped_code_tabs.py b/tests/test_scoped_code_tabs.py new file mode 100644 index 0000000..6b93f2c --- /dev/null +++ b/tests/test_scoped_code_tabs.py @@ -0,0 +1,133 @@ +# flake8: noqa E501 +import unittest + +import markdown + + +class ScopedCodeTabsExtensionTest(unittest.TestCase): + """ + Integration test with markdown for :class:`docdown.scoped_code_tabs.ScopedCodeTabsExtension` + """ + MARKDOWN_EXTENSIONS = ['docdown.scoped_code_tabs'] + + def test_mixed_code_blocks(self): + """ + Tests mixed code blocks to ensure extension is only run on those with |~ ... ~| fences + """ + text = """\ +### A set of code tabs in Python and Java +[comment]: # (This should render as two code tabs) +|~ +```python +def main(): + print("This would be passed through markdown_fenced_code_tabs") +``` + +```java +public static void main(String[] args) { + System.out.println("This would be passed through markdown_fenced_code_tabs"); +} +``` +~| + +### A regular, non-tabbed code block in Bash +[comment]: # (This should render as two, non-tabbed code blocks) +```bash +codeblockinfo() { + echo("This would NOT be passed through markdown_fenced_code_tabs"); +} +``` + +```clojure +(defn code-block-info [] + (println "This should also render as a normal code block")) +(hello-world) +``` + +#### (White-space fences are OK) +[comment]: # (This should render as two more fenced code tabs even with whitespace around the fences) + |~ +```html + + + +

Hello {{ greeting|default:"World" }}!

+ + +``` + +```python +def hello_world(name: str = None): + greeting = name or 'World' + return f'Hello {greeting}!' +``` + ~| +""" + expected_output = """\ +

A set of code tabs in Python and Java

+
def main():
+    print("This would be passed through markdown_fenced_code_tabs")
+
public static void main(String[] args) {
+    System.out.println("This would be passed through markdown_fenced_code_tabs");
+}
+
+ +

A regular, non-tabbed code block in Bash

+

bash +codeblockinfo() { + echo("This would NOT be passed through markdown_fenced_code_tabs"); +}

+

clojure +(defn code-block-info [] + (println "This should also render as a normal code block")) +(hello-world)

+

(White-space fences are OK)

+
<html>
+<head></head>
+<body>
+    <p>Hello {{ greeting|default:"World" }}!</p>
+</body>
+</html>
+
def hello_world(name: str = None):
+    greeting = name or 'World'
+    return f'Hello {greeting}!'
+
""" + + html = markdown.markdown( + text, + extensions=['docdown.scoped_code_tabs'], + output_format='html5') + + self.maxDiff = len(html) * 2 + self.assertEqual(html, expected_output) + + def test_custom_config_values(self): + config = { + 'docdown.scoped_code_tabs': { + 'single_block_as_tab': True, + 'template': 'bootstrap4' + } + } + + text = """\ +|~ +```python +def hello_world(greeting: str = 'World'): + return f'Hello {greeting}!' +``` + ~| +""" + + expected_output = """\ +

def hello_world(greeting: str = 'World'):
+    return f'Hello {greeting}!'
+

""" + + html = markdown.markdown( + text, + extensions=['docdown.scoped_code_tabs'], + extension_configs=config, + output_format='html5') + + self.maxDiff = len(html) * 2 + self.assertEqual(html, expected_output) From 17eb6e9f3e96337a175434c10f0ca5018445d6e9 Mon Sep 17 00:00:00 2001 From: erin kirby Date: Mon, 19 Apr 2021 13:22:56 -0400 Subject: [PATCH 2/8] Fixes small typo in platform section test documentation --- tests/test_platform_section_extension.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_platform_section_extension.py b/tests/test_platform_section_extension.py index 5bca1e3..912c653 100644 --- a/tests/test_platform_section_extension.py +++ b/tests/test_platform_section_extension.py @@ -1,10 +1,10 @@ # -*- coding: utf-8 -*- """ -test_note_blocks_extension +test_platform_section_extension ---------------------------------- -Tests for `docdown.note_blocks` module. +Tests for `docdown.platform_section` module. """ from __future__ import absolute_import, print_function, unicode_literals From f86002d303d3ea84c9bb82103f02d687fd19d37f Mon Sep 17 00:00:00 2001 From: erin kirby Date: Mon, 19 Apr 2021 13:25:00 -0400 Subject: [PATCH 3/8] Adds Erin to list of contributors --- Authors.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/Authors.rst b/Authors.rst index 9610662..7052407 100644 --- a/Authors.rst +++ b/Authors.rst @@ -12,3 +12,4 @@ Contributors * Dave MacNamara * Justin Michalicek +* Erin Kirby From b78930b93537e93cccb4fdcdbee635f01db1145b Mon Sep 17 00:00:00 2001 From: erin kirby Date: Mon, 19 Apr 2021 13:26:45 -0400 Subject: [PATCH 4/8] Syncs CONTRIBUTING guide and setup docs to match the Python versions listed for TravisCI and tox's envlist --- CONTRIBUTING.rst | 2 +- setup.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 993a6dc..dabaf36 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -101,7 +101,7 @@ Before you submit a pull request, check that it meets these guidelines: 2. If the pull request adds functionality, the docs should be updated. Put your new functionality into a function with a docstring, and add the feature to the list in README.rst. -3. The pull request should work for Python 2.7, 3.2, 3.3, 3.4 and 3.5, and for PyPy. Check +3. The pull request should work for Python 2.7, 3.4, 3.5, 3.6 and 3.7, and for PyPy. Check https://travis-ci.org/livio/DocDown-Python/pull_requests and make sure that the tests pass for all supported Python versions. diff --git a/setup.py b/setup.py index 2902778..3f8a984 100644 --- a/setup.py +++ b/setup.py @@ -44,9 +44,10 @@ "Programming Language :: Python :: 2", 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.7', ], test_suite='tests', tests_require=test_requirements From 82e405c2e5e3b414daa588edc3d927d35dfdd5d8 Mon Sep 17 00:00:00 2001 From: erin kirby Date: Mon, 19 Apr 2021 16:00:07 -0400 Subject: [PATCH 5/8] =?UTF-8?q?Bump=20version:=200.2.7=20=E2=86=92=200.3.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- HISTORY.rst | 5 +++++ docdown/__init__.py | 2 +- setup.cfg | 2 +- setup.py | 2 +- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index 7c254a3..fbeae9e 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -2,6 +2,11 @@ History ======= +0.3.0 (2021-04-19) +------------------ + +* Add Scoped Code Tabs Markdown Extension + 0.2.7 (2019-11-20) ------------------ diff --git a/docdown/__init__.py b/docdown/__init__.py index de04dd3..fdc671e 100644 --- a/docdown/__init__.py +++ b/docdown/__init__.py @@ -2,4 +2,4 @@ __author__ = """Jason Emerick""" __email__ = 'jason@mobelux.com' -__version__ = '0.2.7' +__version__ = '0.3.0' diff --git a/setup.cfg b/setup.cfg index e9fb5e3..c5a8362 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 0.2.7 +current_version = 0.3.0 commit = True tag = True diff --git a/setup.py b/setup.py index 3f8a984..817a852 100644 --- a/setup.py +++ b/setup.py @@ -22,7 +22,7 @@ setup( name='docdown', - version='0.2.7', + version='0.3.0', description="DocDown is a Markdown extension for source code documentation.", long_description=readme + '\n\n' + history, author="Jason Emerick, Justin Michalicek", From 826d92de3e1c4e2b4b2cfb69bb67b56c267a47f4 Mon Sep 17 00:00:00 2001 From: erin kirby Date: Tue, 20 Apr 2021 11:20:47 -0400 Subject: [PATCH 6/8] Hard cap on Markdown version of below 3.0 --- requirements_dev.txt | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements_dev.txt b/requirements_dev.txt index 3875cc6..2cf07e5 100644 --- a/requirements_dev.txt +++ b/requirements_dev.txt @@ -8,7 +8,7 @@ coverage==4.1 Sphinx==1.4.8 twine==1.13.0 -Markdown>=2.6.6 +Markdown<3.0.0 markdown-fenced-code-tabs==1.0.5 unicodecsv==0.14.1 diff --git a/setup.py b/setup.py index 817a852..96cbb4a 100644 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ requirements = [ # TODO: put package requirements here - 'Markdown >= 2.6.6', + 'Markdown < 3.0.0', 'unicodecsv >= 0.14.1', 'markdown-fenced-code-tabs >= 1.0.5', ] From 2ccd8e244f8cb2f775c23cd91debe858efff8b68 Mon Sep 17 00:00:00 2001 From: erin kirby Date: Tue, 20 Apr 2021 11:26:27 -0400 Subject: [PATCH 7/8] Comment out watchdog as a requirement since unused and not compatible with py34 --- requirements_dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements_dev.txt b/requirements_dev.txt index 2cf07e5..4a53d9d 100644 --- a/requirements_dev.txt +++ b/requirements_dev.txt @@ -1,7 +1,7 @@ pip==9.0.1 bumpversion==0.5.3 wheel==0.29.0 -watchdog==0.8.3 +#watchdog==0.8.3 flake8==2.6.0 tox==2.5.0 coverage==4.1 From 637ac3f77f4f03901b967939ab3ebfb8f324dea4 Mon Sep 17 00:00:00 2001 From: erin kirby Date: Tue, 20 Apr 2021 11:35:29 -0400 Subject: [PATCH 8/8] =?UTF-8?q?Bump=20version:=200.3.0=20=E2=86=92=200.3.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- HISTORY.rst | 5 +++++ docdown/__init__.py | 2 +- setup.cfg | 2 +- setup.py | 2 +- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index fbeae9e..2ca04ed 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -2,6 +2,11 @@ History ======= +0.3.1 (2021-04-20) +------------------ + +* Place a hard cap below 3.0 on Markdown to address compatibility issues + 0.3.0 (2021-04-19) ------------------ diff --git a/docdown/__init__.py b/docdown/__init__.py index fdc671e..18c5518 100644 --- a/docdown/__init__.py +++ b/docdown/__init__.py @@ -2,4 +2,4 @@ __author__ = """Jason Emerick""" __email__ = 'jason@mobelux.com' -__version__ = '0.3.0' +__version__ = '0.3.1' diff --git a/setup.cfg b/setup.cfg index c5a8362..151e76f 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 0.3.0 +current_version = 0.3.1 commit = True tag = True diff --git a/setup.py b/setup.py index 96cbb4a..0550dfc 100644 --- a/setup.py +++ b/setup.py @@ -22,7 +22,7 @@ setup( name='docdown', - version='0.3.0', + version='0.3.1', description="DocDown is a Markdown extension for source code documentation.", long_description=readme + '\n\n' + history, author="Jason Emerick, Justin Michalicek",