diff options
Diffstat (limited to 'mdit_py_plugins/container')
-rw-r--r-- | mdit_py_plugins/container/LICENSE | 22 | ||||
-rw-r--r-- | mdit_py_plugins/container/README.md | 95 | ||||
-rw-r--r-- | mdit_py_plugins/container/__init__.py | 1 | ||||
-rw-r--r-- | mdit_py_plugins/container/index.py | 174 | ||||
-rw-r--r-- | mdit_py_plugins/container/port.yaml | 5 |
5 files changed, 297 insertions, 0 deletions
diff --git a/mdit_py_plugins/container/LICENSE b/mdit_py_plugins/container/LICENSE new file mode 100644 index 0000000..e6c3230 --- /dev/null +++ b/mdit_py_plugins/container/LICENSE @@ -0,0 +1,22 @@ +Copyright (c) 2015 Vitaly Puzrin, Alex Kocharin. + +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. diff --git a/mdit_py_plugins/container/README.md b/mdit_py_plugins/container/README.md new file mode 100644 index 0000000..03868d7 --- /dev/null +++ b/mdit_py_plugins/container/README.md @@ -0,0 +1,95 @@ +# markdown-it-container + +[![Build Status](https://img.shields.io/travis/markdown-it/markdown-it-container/master.svg?style=flat)](https://travis-ci.org/markdown-it/markdown-it-container) +[![NPM version](https://img.shields.io/npm/v/markdown-it-container.svg?style=flat)](https://www.npmjs.org/package/markdown-it-container) +[![Coverage Status](https://img.shields.io/coveralls/markdown-it/markdown-it-container/master.svg?style=flat)](https://coveralls.io/r/markdown-it/markdown-it-container?branch=master) + +> Plugin for creating block-level custom containers for [markdown-it](https://github.com/markdown-it/markdown-it) markdown parser. + +__v2.+ requires `markdown-it` v5.+, see changelog.__ + +With this plugin you can create block containers like: + +``` +::: warning +*here be dragons* +::: +``` + +.... and specify how they should be rendered. If no renderer defined, `<div>` with +container name class will be created: + +```html +<div class="warning"> +<em>here be dragons</em> +</div> +``` + +Markup is the same as for [fenced code blocks](http://spec.commonmark.org/0.18/#fenced-code-blocks). +Difference is, that marker use another character and content is rendered as markdown markup. + + +## Installation + +node.js, browser: + +```bash +$ npm install markdown-it-container --save +$ bower install markdown-it-container --save +``` + + +## API + +```js +var md = require('markdown-it')() + .use(require('markdown-it-container'), name [, options]); +``` + +Params: + +- __name__ - container name (mandatory) +- __options:__ + - __validate__ - optional, function to validate tail after opening marker, should + return `true` on success. + - __render__ - optional, renderer function for opening/closing tokens. + - __marker__ - optional (`:`), character to use in delimiter. + + +## Example + +```js +var md = require('markdown-it')(); + +md.use(require('markdown-it-container'), 'spoiler', { + + validate: function(params) { + return params.trim().match(/^spoiler\s+(.*)$/); + }, + + render: function (tokens, idx) { + var m = tokens[idx].info.trim().match(/^spoiler\s+(.*)$/); + + if (tokens[idx].nesting === 1) { + // opening tag + return '<details><summary>' + md.utils.escapeHtml(m[1]) + '</summary>\n'; + + } else { + // closing tag + return '</details>\n'; + } + } +}); + +console.log(md.render('::: spoiler click me\n*content*\n:::\n')); + +// Output: +// +// <details><summary>click me</summary> +// <p><em>content</em></p> +// </details> +``` + +## License + +[MIT](https://github.com/markdown-it/markdown-it-container/blob/master/LICENSE) diff --git a/mdit_py_plugins/container/__init__.py b/mdit_py_plugins/container/__init__.py new file mode 100644 index 0000000..7a89c81 --- /dev/null +++ b/mdit_py_plugins/container/__init__.py @@ -0,0 +1 @@ +from .index import container_plugin # noqa F401 diff --git a/mdit_py_plugins/container/index.py b/mdit_py_plugins/container/index.py new file mode 100644 index 0000000..b6edd43 --- /dev/null +++ b/mdit_py_plugins/container/index.py @@ -0,0 +1,174 @@ +"""Process block-level custom containers.""" +from math import floor +from typing import Callable, Optional + +from markdown_it import MarkdownIt +from markdown_it.common.utils import charCodeAt +from markdown_it.rules_block import StateBlock + + +def container_plugin( + md: MarkdownIt, + name: str, + marker: str = ":", + validate: Optional[Callable[[str, str], bool]] = None, + render=None, +): + """Plugin ported from + `markdown-it-container <https://github.com/markdown-it/markdown-it-container>`__. + + It is a plugin for creating block-level custom containers: + + .. code-block:: md + + :::: name + ::: name + *markdown* + ::: + :::: + + :param name: the name of the container to parse + :param marker: the marker character to use + :param validate: func(marker, param) -> bool, default matches against the name + :param render: render func + + """ + + def validateDefault(params: str, *args): + return params.strip().split(" ", 2)[0] == name + + def renderDefault(self, tokens, idx, _options, env): + # add a class to the opening tag + if tokens[idx].nesting == 1: + tokens[idx].attrJoin("class", name) + + return self.renderToken(tokens, idx, _options, env) + + min_markers = 3 + marker_str = marker + marker_char = charCodeAt(marker_str, 0) + marker_len = len(marker_str) + validate = validate or validateDefault + render = render or renderDefault + + def container_func(state: StateBlock, startLine: int, endLine: int, silent: bool): + + auto_closed = False + start = state.bMarks[startLine] + state.tShift[startLine] + maximum = state.eMarks[startLine] + + # Check out the first character quickly, + # this should filter out most of non-containers + if marker_char != state.srcCharCode[start]: + return False + + # Check out the rest of the marker string + pos = start + 1 + while pos <= maximum: + try: + character = state.src[pos] + except IndexError: + break + if marker_str[(pos - start) % marker_len] != character: + break + pos += 1 + + marker_count = floor((pos - start) / marker_len) + if marker_count < min_markers: + return False + pos -= (pos - start) % marker_len + + markup = state.src[start:pos] + params = state.src[pos:maximum] + assert validate is not None + if not validate(params, markup): + return False + + # Since start is found, we can report success here in validation mode + if silent: + return True + + # Search for the end of the block + nextLine = startLine + + while True: + nextLine += 1 + if nextLine >= endLine: + # unclosed block should be autoclosed by end of document. + # also block seems to be autoclosed by end of parent + break + + start = state.bMarks[nextLine] + state.tShift[nextLine] + maximum = state.eMarks[nextLine] + + if start < maximum and state.sCount[nextLine] < state.blkIndent: + # non-empty line with negative indent should stop the list: + # - ``` + # test + break + + if marker_char != state.srcCharCode[start]: + continue + + if state.sCount[nextLine] - state.blkIndent >= 4: + # closing fence should be indented less than 4 spaces + continue + + pos = start + 1 + while pos <= maximum: + try: + character = state.src[pos] + except IndexError: + break + if marker_str[(pos - start) % marker_len] != character: + break + pos += 1 + + # closing code fence must be at least as long as the opening one + if floor((pos - start) / marker_len) < marker_count: + continue + + # make sure tail has spaces only + pos -= (pos - start) % marker_len + pos = state.skipSpaces(pos) + + if pos < maximum: + continue + + # found! + auto_closed = True + break + + old_parent = state.parentType + old_line_max = state.lineMax + state.parentType = "container" + + # this will prevent lazy continuations from ever going past our end marker + state.lineMax = nextLine + + token = state.push(f"container_{name}_open", "div", 1) + token.markup = markup + token.block = True + token.info = params + token.map = [startLine, nextLine] + + state.md.block.tokenize(state, startLine + 1, nextLine) + + token = state.push(f"container_{name}_close", "div", -1) + token.markup = state.src[start:pos] + token.block = True + + state.parentType = old_parent + state.lineMax = old_line_max + state.line = nextLine + (1 if auto_closed else 0) + + return True + + md.block.ruler.before( + "fence", + "container_" + name, + container_func, + {"alt": ["paragraph", "reference", "blockquote", "list"]}, + ) + md.add_render_rule(f"container_{name}_open", render) + md.add_render_rule(f"container_{name}_close", render) diff --git a/mdit_py_plugins/container/port.yaml b/mdit_py_plugins/container/port.yaml new file mode 100644 index 0000000..e47c118 --- /dev/null +++ b/mdit_py_plugins/container/port.yaml @@ -0,0 +1,5 @@ +- package: markdown-it-container + commit: adb3defde3a1c56015895b47ce4c6591b8b1e3a2 + date: Jun 2, 2020 + version: 3.0.0 + changes: |