From c0142ddccb882e89d5131b1fbe37674495085af0 Mon Sep 17 00:00:00 2001 From: Carles Sala Date: Thu, 29 Dec 2022 17:49:22 +0100 Subject: [PATCH 1/7] Add configuration description --- README.md | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 7eba538..3797d7e 100644 --- a/README.md +++ b/README.md @@ -4,17 +4,7 @@ [![codecov.io][cov-badge]][cov-link] [![PyPI version][pypi-badge]][pypi-link] -An [mdformat][mdformat] plugin to render *thematic breaks* using three dashes - -``` ---- -``` - -instead of 70 underscores - -``` -________________________________________________________________________________ -``` +An [mdformat][mdformat] plugin to make thematic breaks rendering style configurable. ## Install @@ -24,6 +14,8 @@ Install with: pip install mdformat-simple-breaks ``` +After the installation, the plugin will be automatically used when running `mdformat` as usual. + ## Usage as a [pre-commit](https://pre-commit.com) hook Add the following to your `.pre-commit-config.yaml`: @@ -37,6 +29,18 @@ Add the following to your `.pre-commit-config.yaml`: - mdformat-simple-breaks ``` +## Configuration + +By default this plugin will render the thematic breaks as a line made of three dashes `---`, +but this can be modified by adding the following two lins in the [.mdformat.toml configuration +file][mdformat-toml]: + +``` +thematic_breaks_character = "-" # possible values: {"-", "_", "*"} +thematic_breaks_length = 3 # possible values: INTEGER between 3 and 80. +``` + + ## Plugin rationale The [CommonMark specification][commonmark-spec] states that *thematic breaks*, which are to be @@ -68,3 +72,4 @@ using the common three dash style when preferred. [commonmark-spec]: https://spec.commonmark.org/0.30/#thematic-breaks [style-change]: https://github.com/executablebooks/mdformat/issues/69 [mdformat-thematic-breaks]: https://mdformat.readthedocs.io/en/stable/users/style.html#thematic-breaks +[mdformat-toml]: https://mdformat.readthedocs.io/en/stable/users/configuration_file.html From 327da9bc2f422b52950ffb3f3eaf85646ecc0782 Mon Sep 17 00:00:00 2001 From: Carles Sala Date: Fri, 30 Dec 2022 15:17:30 +0100 Subject: [PATCH 2/7] Add character and length options --- mdformat_simple_breaks/__init__.py | 2 +- mdformat_simple_breaks/plugin.py | 61 +++++++++++++++++++++++++++++- 2 files changed, 60 insertions(+), 3 deletions(-) diff --git a/mdformat_simple_breaks/__init__.py b/mdformat_simple_breaks/__init__.py index 1c98271..0bf7c88 100644 --- a/mdformat_simple_breaks/__init__.py +++ b/mdformat_simple_breaks/__init__.py @@ -2,4 +2,4 @@ __version__ = "0.0.1" -from .plugin import RENDERERS, update_mdit # noqa: F401 +from .plugin import RENDERERS, add_cli_options, update_mdit # noqa: F401 diff --git a/mdformat_simple_breaks/plugin.py b/mdformat_simple_breaks/plugin.py index beb96ee..1049b2c 100644 --- a/mdformat_simple_breaks/plugin.py +++ b/mdformat_simple_breaks/plugin.py @@ -1,19 +1,76 @@ """Main plugin module.""" +import argparse +from pathlib import Path from typing import Mapping from markdown_it import MarkdownIt +from mdformat import _conf from mdformat.renderer import RenderContext, RenderTreeNode from mdformat.renderer.typing import Render +_conf_validate_values = _conf._validate_values + + +def _validate_values(opts: Mapping, conf_path: Path) -> None: + _conf_validate_values(opts, conf_path) + wrap = opts.get("wrap") + if not isinstance(wrap, int): + wrap = 70 + + character = opts.get("thematic_breaks_character") + if character not in (None, "-", "_", "*"): + raise _conf.InvalidConfError( + f"Invalid 'thematic_breaks_character' value in {conf_path}: {character}" + ) + + length = opts.get("thematic_breaks_length") + if length is not None and not (isinstance(length, int) and 3 <= length <= wrap): + raise _conf.InvalidConfError( + f"Invalid 'thematic_breaks_length' value in {conf_path}: {length}" + ) + + +def _monkey_patch_mdformat_on_import() -> None: + """Monkey patch the mdformat._conf to add the plugin opts.`""" + _conf.DEFAULT_OPTS["thematic_breaks_character"] = "-" + _conf.DEFAULT_OPTS["thematic_breaks_length"] = 3 + _conf._validate_values = _validate_values + + +def thematic_break_length(value: str) -> int: + value = int(value) + if value < 3: + raise ValueError("thematic_break_length must be an integer greater than 2") + + return value + + +def add_cli_options(parser: argparse.ArgumentParser) -> None: + parser.add_argument( + "--thematic-breaks-character", + choices=("-", "_", "*"), + help="Character to use for rendering thematic breaks (default: -)", + ) + parser.add_argument( + "--thematic-breaks-length", + type=thematic_break_length, + help="Length of the rendered thematic breaks (default: 3)", + ) + def update_mdit(mdit: MarkdownIt) -> None: - """Update the parser, e.g. by adding a plugin: `mdit.use(myplugin)`""" + """No-op update_mdit for mdformat-simple-breaks plugin.""" pass def hr(node: RenderTreeNode, context: RenderContext) -> str: - return "---" + character = context.options["mdformat"]["thematic_breaks_character"] + length = context.options["mdformat"]["thematic_breaks_length"] + return character * length RENDERERS: Mapping[str, Render] = {"hr": hr} + + +_monkey_patch_mdformat_on_import() From 7e2f272af15a4bad5ea2c75031393f5c0670887a Mon Sep 17 00:00:00 2001 From: Carles Sala Date: Fri, 30 Dec 2022 15:29:41 +0100 Subject: [PATCH 3/7] Update README --- README.md | 44 ++++++++++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 3797d7e..9da8723 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,9 @@ # mdformat-simple-breaks -[![Build Status][ci-badge]][ci-link] -[![codecov.io][cov-badge]][cov-link] +[![Build Status][ci-badge]][ci-link] [![codecov.io][cov-badge]][cov-link] [![PyPI version][pypi-badge]][pypi-link] -An [mdformat][mdformat] plugin to make thematic breaks rendering style configurable. +An [mdformat] plugin to make thematic breaks rendering style configurable. ## Install @@ -31,16 +30,26 @@ Add the following to your `.pre-commit-config.yaml`: ## Configuration -By default this plugin will render the thematic breaks as a line made of three dashes `---`, -but this can be modified by adding the following two lins in the [.mdformat.toml configuration -file][mdformat-toml]: +### CLI Arguments + +By default this plugin will render the thematic breaks as a line made of three dashes `---`, but +this can be modified by using the following command line options when running `mdformat`: + +- `--thematic-breaks-character`: Character to use when rendering the thematic breaks. Possible + values are `-` (default), `_` and `*`. +- `--thematic-breaks-length`: Number of times that the character will be repeated to render the + thematic break. Only integer values greater than 2 are accepted. Default is 3. + +### `.mdformat.toml` + +If an [`.mdformat.toml`][mdformat-toml] file is used to pass options to `mdformat`, the following two lines can be +added to configure the `mdformat-simple-breaks` plugin: ``` thematic_breaks_character = "-" # possible values: {"-", "_", "*"} -thematic_breaks_length = 3 # possible values: INTEGER between 3 and 80. +thematic_breaks_length = 3 # possible values: INTEGER greater than 2 ``` - ## Plugin rationale The [CommonMark specification][commonmark-spec] states that *thematic breaks*, which are to be @@ -54,22 +63,21 @@ As a result, most of the Markdown guides and cheat sheets show a line made of th as an example of a *thematic break*, and therefore this likely to be the format which Markdown writers are most used to type. -On the other hand, plain [mdformat][mdformat] renders these *thematic breaks* [as a line of 70 +On the other hand, plain [mdformat] renders these *thematic breaks* [as a line of 70 consecutive underscore characters][mdformat-thematic-breaks]. This is an [explicit style decision ][style-change] that is not going to be reverted and for which no configuration will be added. -As a result, this plugin has been created to offer the option to render the *thematic breaks* -using the common three dash style when preferred. - +As a result, this plugin has been created to offer the option to render the *thematic breaks* using +any style preferred by the user. [ci-badge]: https://github.com/csala/mdformat-simple-breaks/workflows/CI/badge.svg?branch=master [ci-link]: https://github.com/csala/mdformat/actions?query=workflow%3ACI+branch%3Amaster+event%3Apush -[cov-badge]: https://codecov.io/gh/csala/mdformat-simple-breaks/branch/master/graph/badge.svg -[cov-link]: https://codecov.io/gh/csala/mdformat-simple-breaks -[pypi-badge]: https://img.shields.io/pypi/v/mdformat-simple-breaks.svg -[pypi-link]: https://pypi.org/project/mdformat-simple-breaks -[mdformat]: https://github.com/executablebooks/mdformat [commonmark-spec]: https://spec.commonmark.org/0.30/#thematic-breaks -[style-change]: https://github.com/executablebooks/mdformat/issues/69 +[cov-badge]: https://codecov.io/gh/csala/mdformat-thematic-breaks/branch/master/graph/badge.svg +[cov-link]: https://codecov.io/gh/csala/mdformat-thematic-breaks +[mdformat]: https://github.com/executablebooks/mdformat [mdformat-thematic-breaks]: https://mdformat.readthedocs.io/en/stable/users/style.html#thematic-breaks [mdformat-toml]: https://mdformat.readthedocs.io/en/stable/users/configuration_file.html +[pypi-badge]: https://img.shields.io/pypi/v/mdformat-thematic-breaks.svg +[pypi-link]: https://pypi.org/project/mdformat-thematic-breaks +[style-change]: https://github.com/executablebooks/mdformat/issues/69 From 75d06b1540e32782526c03f7779e8d9b13b5195b Mon Sep 17 00:00:00 2001 From: Carles Sala Date: Fri, 30 Dec 2022 15:31:21 +0100 Subject: [PATCH 4/7] Add .mdformat.toml --- .mdformat.toml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .mdformat.toml diff --git a/.mdformat.toml b/.mdformat.toml new file mode 100644 index 0000000..878d087 --- /dev/null +++ b/.mdformat.toml @@ -0,0 +1,5 @@ +wrap = 99 # possible values: {"keep", "no", INTEGER} +number = true # possible values: {false, true} +end_of_line = "lf" # possible values: {"lf", "crlf", "keep"} +thematic_breaks_character = "-" # possible values: {"-", "_", "*"} +thematic_breaks_length = 3 # possible values: 3 <= INTEGER From 4f1f1b140e5ea7dda2655d83ec99481191832073 Mon Sep 17 00:00:00 2001 From: Carles Sala Date: Fri, 30 Dec 2022 15:52:47 +0100 Subject: [PATCH 5/7] Format DEVELOPMENT.md guide --- DEVELOPMENT.md | 62 +++++++++++++++++++++++++++----------------------- 1 file changed, 34 insertions(+), 28 deletions(-) diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index b61e024..4493a2b 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -2,7 +2,7 @@ ## Development Setup -This package utilises [flit][flit] as the build engine, and [tox][tox] for test automation. +This package utilises [flit] as the build engine, and [tox] for test automation. To install these development dependencies: @@ -52,44 +52,46 @@ pip install flit flit publish ``` -or trigger the GitHub Action job, by creating a release with a tag equal to the version, e.g. `v0.0.1`. +or trigger the GitHub Action job, by creating a release with a tag equal to the version, e.g. +`v0.0.1`. -Note, this requires generating an API key on PyPi and adding it to the repository `Settings/Secrets`, under the name `PYPI_KEY`. +Note, this requires generating an API key on PyPi and adding it to the repository +`Settings/Secrets`, under the name `PYPI_KEY`. # Plugin Architecture -The plugin simply overwrites the [`hr`][hr-code] function from the [mdformat][mdformat] library +The plugin simply overwrites the [`hr`][hr-code] function from the \[mdformat\]\[mdformat\] library and returns a fixed `---` string instead of the 70 `_` one. # General Coding Guidelines -This is part of the initial development guides included in the [mdformat-plugin][mdformat-plugin] -repo and [mdformat contributing guide][mdformat-contributing]. Copied over here for reference. +This is part of the initial development guides included in the [mdformat-plugin] repo and +[mdformat contributing guide][mdformat-contributing]. Copied over here for reference. ## Required changes for a new plugin -This demonstration is setup with a plugin named `plugin`. -There are a number of locations to change. -At a top level for a plugin `foo` at least the following changes are required +This demonstration is setup with a plugin named `plugin`. There are a number of locations to +change. At a top level for a plugin `foo` at least the following changes are required - Global find and replace `mdformat_plugin` to `mdformat_foo` including folder names. - Global find and replace `mdformat-plugin` to `mdformat-foo` including folder names. -- `tests/test_fixtures.py`: `output = mdformat.text(text, extensions={"plugin"})` becomes `output = mdformat.text(text, extensions={"foo"})` -- `pyproject.toml` in addition to the global find and replace: `plugin = "mdformat_plugin"` becomes `foo = "mdformat_foo"` +- `tests/test_fixtures.py`: `output = mdformat.text(text, extensions={"plugin"})` becomes + `output = mdformat.text(text, extensions={"foo"})` +- `pyproject.toml` in addition to the global find and replace: `plugin = "mdformat_plugin"` becomes + `foo = "mdformat_foo"` Do not forget to update authorship / maintainers in `pyproject.toml` as well. ## Developing code formatter plugins -Mdformat code formatter plugins need to define a formatter function that is of type `Callable[[str, str], str]`. -The input arguments are the code block's unformatted code and info string, in that order. -The return value should be formatted code. +Mdformat code formatter plugins need to define a formatter function that is of type +`Callable[[str, str], str]`. The input arguments are the code block's unformatted code and info +string, in that order. The return value should be formatted code. -This function needs to be exposed via entry point distribution metadata. -The entry point's group must be "mdformat.codeformatter", -name must be name of the coding language it formats (as it appears in Markdown code block info strings), e.g. "python", -and value has to point to the formatter function within the plugin package, -e.g. "my_package.some_module:format_python" +This function needs to be exposed via entry point distribution metadata. The entry point's group +must be "mdformat.codeformatter", name must be name of the coding language it formats (as it +appears in Markdown code block info strings), e.g. "python", and value has to point to the +formatter function within the plugin package, e.g. "my_package.some_module:format_python" If using `setup.py` for packaging, the entry point configuration would have to be similar to: @@ -104,7 +106,8 @@ setuptools.setup( ) ``` -If using Poetry for packaging, the entry point configuration in `pyproject.toml` would need to be like: +If using Poetry for packaging, the entry point configuration in `pyproject.toml` would need to be +like: ```toml # other config here... @@ -117,9 +120,11 @@ which formats Python code blocks with Black. ## Developing parser extension plugins -The easiest way to get started on a plugin, is to use the template repository. +The easiest way to get started on a plugin, is to use the + template repository. -Mdformat parser extension plugins need to adhere to the `mdformat.plugins.ParserExtensionInterface`: +Mdformat parser extension plugins need to adhere to the +`mdformat.plugins.ParserExtensionInterface`: ```python from collections.abc import Mapping @@ -137,8 +142,8 @@ def update_mdit(mdit: MarkdownIt) -> None: RENDERERS: Mapping[str, Render] ``` -This interface needs to be exposed via entry point distribution metadata. -The entry point's group must be "mdformat.parser_extension". +This interface needs to be exposed via entry point distribution metadata. The entry point's group +must be "mdformat.parser_extension". If using `setup.py` for packaging, the entry point configuration would have to be similar to: @@ -153,7 +158,8 @@ setuptools.setup( ) ``` -If using Poetry or Flit for packaging, the entry point configuration in `pyproject.toml` would need to be like: +If using Poetry or Flit for packaging, the entry point configuration in `pyproject.toml` would need +to be like: ```toml # other config here... @@ -164,8 +170,8 @@ If using Poetry or Flit for packaging, the entry point configuration in `pyproje "myextension" = "my_package:ext_module_or_class" ``` -[mdformat-plugin]: https://github.com/executablebooks/mdformat-plugin [flit]: https://flit.readthedocs.io -[tox]: https://tox.readthedocs.io -[mdformat-contributing]: https://github.com/executablebooks/mdformat/blob/master/docs/contributors/contributing.md [hr-code]: https://github.com/executablebooks/mdformat/blob/5d9b573ce33bae219087984dd148894c774f41d4/src/mdformat/renderer/_context.py#L55 +[mdformat-contributing]: https://github.com/executablebooks/mdformat/blob/master/docs/contributors/contributing.md +[mdformat-plugin]: https://github.com/executablebooks/mdformat-plugin +[tox]: https://tox.readthedocs.io From 41d0466f0ce374acfd9a6e8f68c5db24cc5c85df Mon Sep 17 00:00:00 2001 From: Carles Sala Date: Fri, 30 Dec 2022 16:00:02 +0100 Subject: [PATCH 6/7] Remove DEVELOPMENt.md guide --- DEVELOPMENT.md | 177 ------------------------------------------------- 1 file changed, 177 deletions(-) delete mode 100644 DEVELOPMENT.md diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md deleted file mode 100644 index 4493a2b..0000000 --- a/DEVELOPMENT.md +++ /dev/null @@ -1,177 +0,0 @@ -# Development Guide - -## Development Setup - -This package utilises [flit] as the build engine, and [tox] for test automation. - -To install these development dependencies: - -```bash -pip install tox -``` - -To run the tests: - -```bash -tox -``` - -and with test coverage: - -```bash -tox -e py37-cov -``` - -The easiest way to write tests, is to edit tests/fixtures.md - -To run the code formatting and style checks: - -```bash -tox -e py37-pre-commit -``` - -or directly - -```bash -pip install pre-commit -pre-commit run --all -``` - -To run the pre-commit hook test: - -```bash -tox -e py37-hook -``` - -## Publish to PyPi - -Either use flit directly: - -```bash -pip install flit -flit publish -``` - -or trigger the GitHub Action job, by creating a release with a tag equal to the version, e.g. -`v0.0.1`. - -Note, this requires generating an API key on PyPi and adding it to the repository -`Settings/Secrets`, under the name `PYPI_KEY`. - -# Plugin Architecture - -The plugin simply overwrites the [`hr`][hr-code] function from the \[mdformat\]\[mdformat\] library -and returns a fixed `---` string instead of the 70 `_` one. - -# General Coding Guidelines - -This is part of the initial development guides included in the [mdformat-plugin] repo and -[mdformat contributing guide][mdformat-contributing]. Copied over here for reference. - -## Required changes for a new plugin - -This demonstration is setup with a plugin named `plugin`. There are a number of locations to -change. At a top level for a plugin `foo` at least the following changes are required - -- Global find and replace `mdformat_plugin` to `mdformat_foo` including folder names. -- Global find and replace `mdformat-plugin` to `mdformat-foo` including folder names. -- `tests/test_fixtures.py`: `output = mdformat.text(text, extensions={"plugin"})` becomes - `output = mdformat.text(text, extensions={"foo"})` -- `pyproject.toml` in addition to the global find and replace: `plugin = "mdformat_plugin"` becomes - `foo = "mdformat_foo"` - -Do not forget to update authorship / maintainers in `pyproject.toml` as well. - -## Developing code formatter plugins - -Mdformat code formatter plugins need to define a formatter function that is of type -`Callable[[str, str], str]`. The input arguments are the code block's unformatted code and info -string, in that order. The return value should be formatted code. - -This function needs to be exposed via entry point distribution metadata. The entry point's group -must be "mdformat.codeformatter", name must be name of the coding language it formats (as it -appears in Markdown code block info strings), e.g. "python", and value has to point to the -formatter function within the plugin package, e.g. "my_package.some_module:format_python" - -If using `setup.py` for packaging, the entry point configuration would have to be similar to: - -```python -import setuptools - -setuptools.setup( - # other arguments here... - entry_points={ - "mdformat.codeformatter": ["python = my_package.some_module:format_python"] - } -) -``` - -If using Poetry for packaging, the entry point configuration in `pyproject.toml` would need to be -like: - -```toml -# other config here... -[tool.poetry.plugins."mdformat.codeformatter"] -"python" = "my_package.some_module:format_python" -``` - -For a real-world example plugin, see [mdformat-black](https://github.com/hukkin/mdformat-black), -which formats Python code blocks with Black. - -## Developing parser extension plugins - -The easiest way to get started on a plugin, is to use the - template repository. - -Mdformat parser extension plugins need to adhere to the -`mdformat.plugins.ParserExtensionInterface`: - -```python -from collections.abc import Mapping -from markdown_it import MarkdownIt -from mdformat.renderer.typing import Render - - -def update_mdit(mdit: MarkdownIt) -> None: - """Update the parser, e.g. by adding a plugin: `mdit.use(myplugin)`""" - - -# A mapping from `RenderTreeNode.type` value to a `Render` function that can -# render the given `RenderTreeNode` type. These functions override the default -# `Render` funcs defined in `mdformat.renderer.DEFAULT_RENDERERS`. -RENDERERS: Mapping[str, Render] -``` - -This interface needs to be exposed via entry point distribution metadata. The entry point's group -must be "mdformat.parser_extension". - -If using `setup.py` for packaging, the entry point configuration would have to be similar to: - -```python -import setuptools - -setuptools.setup( - # other arguments here... - entry_points={ - "mdformat.parser_extension": ["myextension = my_package:ext_module_or_class"] - } -) -``` - -If using Poetry or Flit for packaging, the entry point configuration in `pyproject.toml` would need -to be like: - -```toml -# other config here... -[tool.poetry.plugins."mdformat.parser_extension"] -"myextension" = "my_package:ext_module_or_class" -# or -[tool.flit.plugins."mdformat.parser_extension"] -"myextension" = "my_package:ext_module_or_class" -``` - -[flit]: https://flit.readthedocs.io -[hr-code]: https://github.com/executablebooks/mdformat/blob/5d9b573ce33bae219087984dd148894c774f41d4/src/mdformat/renderer/_context.py#L55 -[mdformat-contributing]: https://github.com/executablebooks/mdformat/blob/master/docs/contributors/contributing.md -[mdformat-plugin]: https://github.com/executablebooks/mdformat-plugin -[tox]: https://tox.readthedocs.io From 1b515f6f0358026a66d3615168911fbc700c00a3 Mon Sep 17 00:00:00 2001 From: Carles Sala Date: Fri, 30 Dec 2022 16:05:05 +0100 Subject: [PATCH 7/7] Set default option values --- mdformat_simple_breaks/plugin.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mdformat_simple_breaks/plugin.py b/mdformat_simple_breaks/plugin.py index 1049b2c..4ddec61 100644 --- a/mdformat_simple_breaks/plugin.py +++ b/mdformat_simple_breaks/plugin.py @@ -65,8 +65,9 @@ def update_mdit(mdit: MarkdownIt) -> None: def hr(node: RenderTreeNode, context: RenderContext) -> str: - character = context.options["mdformat"]["thematic_breaks_character"] - length = context.options["mdformat"]["thematic_breaks_length"] + options = context.options["mdformat"] + character = options.get("thematic_breaks_character", "-") + length = options.get("thematic_breaks_length", 3) return character * length