Skip to content

Commit

Permalink
Merge pull request #1 from josh-heyer/josh/python-markdown-compat
Browse files Browse the repository at this point in the history
Basic Python-Markdown compatibility
  • Loading branch information
josh-heyer committed Feb 4, 2021
2 parents 9a98d22 + 3fa8b64 commit 454357c
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 8 deletions.
18 changes: 17 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,19 @@
## How this differs from Elvis Wolcott's original

The primary goal is reasonable compatibility with content written against Python-Markdown's Admonitions plugin: https://squidfunk.github.io/mkdocs-material/reference/admonitions/

That means:

- Allows indented, rather than fenced, admonitions (both are supported, and can both be used in the same document if desired)
- An optional space between the tag and the keyword
- Case-insensitive keyword matching
- Quoted titles (poorly-supported right now - just strips containing quotes, if any)
- Empty titles ("" results in no title at all, vs. a default title)

Original README follows:

---

[![Travis (.com)](https://img.shields.io/travis/com/elviswolcott/remark-admonitions?logo=travis)](https://travis-ci.com/elviswolcott/remark-admonitions)
[![npm](https://img.shields.io/npm/v/remark-admonitions?label=remark-admonitions&logo=npm)](https://www.npmjs.com/package/remark-admonitions)
# remark-admonitions
Expand Down Expand Up @@ -156,4 +172,4 @@ The classic theme (`styles/classic.css`) replicates the look of `remarkable-admo

Syntax and classic theme based on [`remarkable-admonitions`](https://github.com/favoloso/remarkable-admonitions).

The SVG icons included are from [GitHub Octicons](https://octicons.github.com).
The SVG icons included are from [GitHub Octicons](https://octicons.github.com).
70 changes: 63 additions & 7 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -143,36 +143,92 @@ module.exports = function attacher(options) {
.map(escapeRegExp)
.join("|");
const tag = escapeRegExp(config.tag);
const regex = new RegExp(`${tag}(${keywords})(?: *(.*))?\n`);
const regex = new RegExp(`${tag} ?(${keywords})(?: *(.*))?\n`, "i");
const escapeTag = new RegExp(escapeRegExp(`\\${config.tag}`), "g");

// the tokenizer is called on blocks to determine if there is an admonition present and create tags for it
function blockTokenizer(eat, value, silent) {

// stop if no match or match does not start at beginning of line
const match = regex.exec(value);
if (!match || match.index !== 0) return false;
// if silent return the match
if (silent) return true;

const now = eat.now();
const [opening, keyword, title] = match;
let [opening, keyword, title] = match;
const food = [];
const content = [];
let content = [];
const indentSize = match.index + value.indexOf(keyword);

keyword = keyword.toLowerCase();
if (title)
{
title = title.trim();
title = title ? title.replace(/^"|"$/g, '') : undefined;
}

// consume lines until a closing tag
let idx = 0;
let idx = 0, empties = 0, indented, endIndent = false;
while ((idx = value.indexOf(NEWLINE)) !== -1) {
// grab this line and eat it
const next = value.indexOf(NEWLINE, idx + 1);
const line =
next !== -1 ? value.slice(idx + 1, next) : value.slice(idx + 1);

let indent = 0, empty = false;
if (indented !== false)
{
let pos = 0;
for (let c of line)
{
if (c === ' ') indent += 1;
else if (c === '\t') indent += 4 - (indent % 4);
else break;
++pos;
}
empty = pos >= line.length;
if (!indented && !empty)
indented = indent >= indentSize;

empties = empty ? empties+1 : 0;
}

if (indented && indent < indentSize && !empty)
{
while(empties--)
{
value = food.pop() + '\n' + value;
content.pop();
}
endIndent = true;
}

// the closing tag is NOT part of the content, but avoid matching a following admonition
const endTag = line.startsWith(config.tag),
startsNew = regex.test(line + "\n");

if (endTag && startsNew) break;

if (endIndent && !endTag) break;

food.push(line);
value = value.slice(idx + 1);
// the closing tag is NOT part of the content
if (line.startsWith(config.tag)) break;

// disambiguate an indented code block in a fenced admonition
if (endTag)
{
indented = false;
break;
}

content.push(line);
}

// unindent
if (indented)
content = content.map(l => l.slice(indentSize));

// consume the processed tag and replace escape sequences
const contentString = content.join(NEWLINE).replace(escapeTag, config.tag);
const add = eat(opening + food.join(NEWLINE));
Expand All @@ -187,7 +243,7 @@ module.exports = function attacher(options) {
exit();
// parse the title in inline mode
const titleNodes = this.tokenizeInline(
title || formatKeyword(keyword),
title === "" ? "" : title || formatKeyword(keyword),
now
);
// create the nodes for the icon
Expand Down

0 comments on commit 454357c

Please sign in to comment.