Skip to content

Commit 5106e13

Browse files
authored
✨NEW: Add containers plugin (#27)
1 parent 00338f9 commit 5106e13

File tree

12 files changed

+708
-1
lines changed

12 files changed

+708
-1
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,5 +138,6 @@ __pycache__/
138138
.ropeproject/
139139
*.egg-info/
140140
.vscode/
141+
.DS_Store
141142

142143
docs/api/

docs/plugins.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,16 @@ Other plugins are then available *via* the `markdown_it.extensions` package:
3232
---
3333
```
3434

35+
- [containers](https://github.com/markdown-it/markdown-it-container) is a plugin for creating block-level custom containers:
36+
37+
```md
38+
::::: name
39+
:::: name
40+
*markdown*
41+
::::
42+
:::::
43+
```
44+
3545
- [texmath](https://github.com/goessner/markdown-it-texmath) parses TeX math equations set inside opening and closing delimiters:
3646

3747
```md
@@ -56,7 +66,6 @@ There are also many other plugins which could easily be ported (and hopefully wi
5666
- [definition list](https://github.com/markdown-it/markdown-it-deflist)
5767
- [abbreviation](https://github.com/markdown-it/markdown-it-abbr)
5868
- [emoji](https://github.com/markdown-it/markdown-it-emoji)
59-
- [custom container](https://github.com/markdown-it/markdown-it-container)
6069
- [insert](https://github.com/markdown-it/markdown-it-ins)
6170
- [mark](https://github.com/markdown-it/markdown-it-mark)
6271
- ... and [others](https://www.npmjs.org/browse/keyword/markdown-it-plugin)
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
Copyright (c) 2015 Vitaly Puzrin, Alex Kocharin.
2+
3+
Permission is hereby granted, free of charge, to any person
4+
obtaining a copy of this software and associated documentation
5+
files (the "Software"), to deal in the Software without
6+
restriction, including without limitation the rights to use,
7+
copy, modify, merge, publish, distribute, sublicense, and/or sell
8+
copies of the Software, and to permit persons to whom the
9+
Software is furnished to do so, subject to the following
10+
conditions:
11+
12+
The above copyright notice and this permission notice shall be
13+
included in all copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17+
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19+
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20+
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22+
OTHER DEALINGS IN THE SOFTWARE.
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
# markdown-it-container
2+
3+
[![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)
4+
[![NPM version](https://img.shields.io/npm/v/markdown-it-container.svg?style=flat)](https://www.npmjs.org/package/markdown-it-container)
5+
[![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)
6+
7+
> Plugin for creating block-level custom containers for [markdown-it](https://github.com/markdown-it/markdown-it) markdown parser.
8+
9+
__v2.+ requires `markdown-it` v5.+, see changelog.__
10+
11+
With this plugin you can create block containers like:
12+
13+
```
14+
::: warning
15+
*here be dragons*
16+
:::
17+
```
18+
19+
.... and specify how they should be rendered. If no renderer defined, `<div>` with
20+
container name class will be created:
21+
22+
```html
23+
<div class="warning">
24+
<em>here be dragons</em>
25+
</div>
26+
```
27+
28+
Markup is the same as for [fenced code blocks](http://spec.commonmark.org/0.18/#fenced-code-blocks).
29+
Difference is, that marker use another character and content is rendered as markdown markup.
30+
31+
32+
## Installation
33+
34+
node.js, browser:
35+
36+
```bash
37+
$ npm install markdown-it-container --save
38+
$ bower install markdown-it-container --save
39+
```
40+
41+
42+
## API
43+
44+
```js
45+
var md = require('markdown-it')()
46+
.use(require('markdown-it-container'), name [, options]);
47+
```
48+
49+
Params:
50+
51+
- __name__ - container name (mandatory)
52+
- __options:__
53+
- __validate__ - optional, function to validate tail after opening marker, should
54+
return `true` on success.
55+
- __render__ - optional, renderer function for opening/closing tokens.
56+
- __marker__ - optional (`:`), character to use in delimiter.
57+
58+
59+
## Example
60+
61+
```js
62+
var md = require('markdown-it')();
63+
64+
md.use(require('markdown-it-container'), 'spoiler', {
65+
66+
validate: function(params) {
67+
return params.trim().match(/^spoiler\s+(.*)$/);
68+
},
69+
70+
render: function (tokens, idx) {
71+
var m = tokens[idx].info.trim().match(/^spoiler\s+(.*)$/);
72+
73+
if (tokens[idx].nesting === 1) {
74+
// opening tag
75+
return '<details><summary>' + md.utils.escapeHtml(m[1]) + '</summary>\n';
76+
77+
} else {
78+
// closing tag
79+
return '</details>\n';
80+
}
81+
}
82+
});
83+
84+
console.log(md.render('::: spoiler click me\n*content*\n:::\n'));
85+
86+
// Output:
87+
//
88+
// <details><summary>click me</summary>
89+
// <p><em>content</em></p>
90+
// </details>
91+
```
92+
93+
## License
94+
95+
[MIT](https://github.com/markdown-it/markdown-it-container/blob/master/LICENSE)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from .index import container_plugin # noqa F401
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
"""Process block-level custom containers."""
2+
from math import floor
3+
4+
from markdown_it import MarkdownIt
5+
from markdown_it.common.utils import charCodeAt
6+
from markdown_it.rules_block import StateBlock
7+
8+
9+
def container_plugin(md: MarkdownIt, name, **options):
10+
"""Second param may be useful,
11+
if you decide to increase minimal allowed marker length
12+
"""
13+
14+
def validateDefault(params: str, *args):
15+
return params.strip().split(" ", 2)[0] == name
16+
17+
def renderDefault(self, tokens, idx, _options, env):
18+
# add a class to the opening tag
19+
if tokens[idx].nesting == 1:
20+
tokens[idx].attrJoin("class", name)
21+
22+
return self.renderToken(tokens, idx, _options, env)
23+
24+
min_markers = 3
25+
marker_str = options.get("marker", ":")
26+
marker_char = charCodeAt(marker_str, 0)
27+
marker_len = len(marker_str)
28+
validate = options.get("validate", validateDefault)
29+
render = options.get("render", renderDefault)
30+
31+
def container_func(state: StateBlock, startLine: int, endLine: int, silent: bool):
32+
33+
auto_closed = False
34+
start = state.bMarks[startLine] + state.tShift[startLine]
35+
maximum = state.eMarks[startLine]
36+
37+
# Check out the first character quickly,
38+
# this should filter out most of non-containers
39+
if marker_char != charCodeAt(state.src, start):
40+
return False
41+
42+
# Check out the rest of the marker string
43+
pos = start + 1
44+
while pos <= maximum:
45+
if marker_str[(pos - start) % marker_len] != state.src[pos]:
46+
break
47+
pos += 1
48+
49+
marker_count = floor((pos - start) / marker_len)
50+
if marker_count < min_markers:
51+
return False
52+
pos -= (pos - start) % marker_len
53+
54+
markup = state.src[start:pos]
55+
params = state.src[pos:maximum]
56+
if not validate(params, markup):
57+
return False
58+
59+
# Since start is found, we can report success here in validation mode
60+
if silent:
61+
return True
62+
63+
# Search for the end of the block
64+
nextLine = startLine
65+
66+
while True:
67+
nextLine += 1
68+
if nextLine >= endLine:
69+
# unclosed block should be autoclosed by end of document.
70+
# also block seems to be autoclosed by end of parent
71+
break
72+
73+
start = state.bMarks[nextLine] + state.tShift[nextLine]
74+
maximum = state.eMarks[nextLine]
75+
76+
if start < maximum and state.sCount[nextLine] < state.blkIndent:
77+
# non-empty line with negative indent should stop the list:
78+
# - ```
79+
# test
80+
break
81+
82+
if marker_char != charCodeAt(state.src, start):
83+
continue
84+
85+
if state.sCount[nextLine] - state.blkIndent >= 4:
86+
# closing fence should be indented less than 4 spaces
87+
continue
88+
89+
pos = start + 1
90+
while pos <= maximum:
91+
if marker_str[(pos - start) % marker_len] != state.src[pos]:
92+
break
93+
pos += 1
94+
95+
# closing code fence must be at least as long as the opening one
96+
if floor((pos - start) / marker_len) < marker_count:
97+
continue
98+
99+
# make sure tail has spaces only
100+
pos -= (pos - start) % marker_len
101+
pos = state.skipSpaces(pos)
102+
103+
if pos < maximum:
104+
continue
105+
106+
# found!
107+
auto_closed = True
108+
break
109+
110+
old_parent = state.parentType
111+
old_line_max = state.lineMax
112+
state.parentType = "container"
113+
114+
# this will prevent lazy continuations from ever going past our end marker
115+
state.lineMax = nextLine
116+
117+
token = state.push(f"container_{name}_open", "div", 1)
118+
token.markup = markup
119+
token.block = True
120+
token.info = params
121+
token.map = [startLine, nextLine]
122+
123+
state.md.block.tokenize(state, startLine + 1, nextLine)
124+
125+
token = state.push(f"container_{name}_close", "div", -1)
126+
token.markup = state.src[start:pos]
127+
token.block = True
128+
129+
state.parentType = old_parent
130+
state.lineMax = old_line_max
131+
state.line = nextLine + (1 if auto_closed else 0)
132+
133+
return True
134+
135+
md.block.ruler.before(
136+
"fence",
137+
"container_" + name,
138+
container_func,
139+
{"alt": ["paragraph", "reference", "blockquote", "list"]},
140+
)
141+
md.add_render_rule(f"container_{name}_open", render)
142+
md.add_render_rule(f"container_{name}_close", render)
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
- package: markdown-it-container
2+
commit: adb3defde3a1c56015895b47ce4c6591b8b1e3a2
3+
date: Jun 2, 2020
4+
version: 3.0.0
5+
changes:

tests/test_container/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)