nex renderer
This commit is contained in:
parent
e708dce8f7
commit
67a727ba24
2 changed files with 232 additions and 0 deletions
117
examples/markdown.nex
Normal file
117
examples/markdown.nex
Normal file
|
|
@ -0,0 +1,117 @@
|
||||||
|
|
||||||
|
|
||||||
|
h1 Heading 8-)
|
||||||
|
==============
|
||||||
|
|
||||||
|
|
||||||
|
h2 Heading
|
||||||
|
----------
|
||||||
|
|
||||||
|
|
||||||
|
h3 Heading
|
||||||
|
----------
|
||||||
|
|
||||||
|
|
||||||
|
h4 Heading
|
||||||
|
----------
|
||||||
|
|
||||||
|
|
||||||
|
h5 Heading
|
||||||
|
----------
|
||||||
|
|
||||||
|
|
||||||
|
h6 Heading
|
||||||
|
----------
|
||||||
|
|
||||||
|
|
||||||
|
Horizontal Rules
|
||||||
|
----------------
|
||||||
|
|
||||||
|
----------------------------------------------------------------------------
|
||||||
|
----------------------------------------------------------------------------
|
||||||
|
----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
Emphasis
|
||||||
|
--------
|
||||||
|
|
||||||
|
This is bold text This is bold text This is italic text This is italic text
|
||||||
|
Strikethrough
|
||||||
|
|
||||||
|
|
||||||
|
Blockquotes
|
||||||
|
-----------
|
||||||
|
|
||||||
|
| Blockquotes can also be nested...
|
||||||
|
| | by using additional greater-than signs right next to each other...
|
||||||
|
| | | or with spaces between arrows.
|
||||||
|
|
||||||
|
|
||||||
|
Lists
|
||||||
|
-----
|
||||||
|
|
||||||
|
|
||||||
|
Unordered
|
||||||
|
---------
|
||||||
|
|
||||||
|
+ Create a list by starting a line with +, -, or *
|
||||||
|
- Marker character change forces new list start:
|
||||||
|
* Ac tristique libero volutpat at
|
||||||
|
+ Facilisis in pretium nisl aliquet
|
||||||
|
- Nulla volutpat aliquam velit
|
||||||
|
+ Very easy
|
||||||
|
|
||||||
|
|
||||||
|
Ordered
|
||||||
|
-------
|
||||||
|
|
||||||
|
1. Lorem ipsum dolor sit amet
|
||||||
|
2. Consectetur adipiscing elit
|
||||||
|
3. Integer molestie lorem at massa
|
||||||
|
|
||||||
|
|
||||||
|
Code
|
||||||
|
----
|
||||||
|
|
||||||
|
Inline code
|
||||||
|
|
||||||
|
|
||||||
|
Indented code
|
||||||
|
|
||||||
|
|
||||||
|
01 | // Some comments
|
||||||
|
02 | line 1 of code
|
||||||
|
03 | line 2 of code
|
||||||
|
04 | line 3 of code
|
||||||
|
|
||||||
|
Block code "fences"
|
||||||
|
|
||||||
|
|
||||||
|
01 | Sample text here...
|
||||||
|
|
||||||
|
Syntax highlighting
|
||||||
|
|
||||||
|
|
||||||
|
01 | var foo = function (bar) {
|
||||||
|
02 | return bar++;
|
||||||
|
03 | };
|
||||||
|
04 |
|
||||||
|
05 | console.log(foo(5));
|
||||||
|
|
||||||
|
|
||||||
|
Links
|
||||||
|
-----
|
||||||
|
|
||||||
|
link text (http://dev.nodeca.com)
|
||||||
|
|
||||||
|
|
||||||
|
link with title (http://nodeca.github.io/pica/demo/ "title text!")
|
||||||
|
|
||||||
|
|
||||||
|
Images
|
||||||
|
------
|
||||||
|
|
||||||
|
Minion (https://octodex.github.com/images/minion.png) Stormtroopocat
|
||||||
|
(https://octodex.github.com/images/stormtroopocat.jpg "The Stormtroopocat")
|
||||||
|
|
||||||
|
|
||||||
115
src/md2txt/renderers/nex.py
Normal file
115
src/md2txt/renderers/nex.py
Normal file
|
|
@ -0,0 +1,115 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from functools import partial
|
||||||
|
from typing import Any, List
|
||||||
|
|
||||||
|
from ..models import BlockStyle, FrontMatter, HeadingPayload
|
||||||
|
from ..plugins import register_renderer
|
||||||
|
from .text import (
|
||||||
|
BOLD_RE,
|
||||||
|
CODE_STASH_RE,
|
||||||
|
IMAGE_RE,
|
||||||
|
ITALIC_RE,
|
||||||
|
LINK_RE,
|
||||||
|
STRIKETHROUGH_RE,
|
||||||
|
TextRenderer,
|
||||||
|
UNDERLINE_EM_RE,
|
||||||
|
UNDERLINE_STRONG_RE,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class NexRenderer(TextRenderer):
|
||||||
|
"""Render Markdown events as plain text for the Nex protocol.
|
||||||
|
|
||||||
|
Nex has no hypertext markup for hand-authored documents at all (its one
|
||||||
|
special syntax, `=> URL`, is spec'd for server-auto-generated directory
|
||||||
|
listings, not document bodies) -- so this renderer's whole job is to
|
||||||
|
flatten Markdown down to plain, readable text. It reuses TextRenderer's
|
||||||
|
wrapping/margin/hyphenation/ASCII-art/include machinery unchanged (same
|
||||||
|
approach as AmaRenderer/MicronRenderer) and only overrides headings (no
|
||||||
|
FIGlet -- a plain setext-style underline instead, which is a readability
|
||||||
|
convention borrowed from Markdown itself, not a Nex spec requirement)
|
||||||
|
and inline formatting (emphasis markers are stripped rather than
|
||||||
|
translated, and links/images are inlined as "label (url)" plain prose
|
||||||
|
rather than footnoted or turned into `=> ` lines).
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, frontmatter: FrontMatter, *, width: int = 80, **_: Any) -> None:
|
||||||
|
super().__init__(width=width, frontmatter=frontmatter)
|
||||||
|
# Links are inlined by `_replace_link` below, not footnoted.
|
||||||
|
self.links.clear()
|
||||||
|
self.link_indices.clear()
|
||||||
|
|
||||||
|
def _render_heading(self, payload: HeadingPayload, style: BlockStyle) -> None:
|
||||||
|
self._ensure_header_spacing()
|
||||||
|
processed_text = self._process_inline(payload.text)
|
||||||
|
underline_char = "=" if payload.level == 1 else "-"
|
||||||
|
self._emit_render(
|
||||||
|
partial(self._render_underlined_heading, processed_text, underline_char),
|
||||||
|
style,
|
||||||
|
stylable=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
def _render_underlined_heading(self, text: str, underline_char: str, style: BlockStyle) -> List[str]:
|
||||||
|
wrapped = self._wrap_text(text, style=style)
|
||||||
|
output: List[str] = []
|
||||||
|
for line in wrapped:
|
||||||
|
line = line.rstrip()
|
||||||
|
if not line.strip():
|
||||||
|
output.append("")
|
||||||
|
continue
|
||||||
|
leading = len(line) - len(line.lstrip(" "))
|
||||||
|
underline = " " * leading + underline_char * len(line.lstrip(" "))
|
||||||
|
output.append(line)
|
||||||
|
output.append(underline)
|
||||||
|
output.append("")
|
||||||
|
return output
|
||||||
|
|
||||||
|
# Inline -------------------------------------------------------------
|
||||||
|
|
||||||
|
def _process_inline(self, text: str) -> str:
|
||||||
|
code_segments: List[str] = []
|
||||||
|
|
||||||
|
def stash_code(match) -> str:
|
||||||
|
code_segments.append(match.group(0)[1:-1])
|
||||||
|
return f"\u0000CODE{len(code_segments) - 1}\u0000"
|
||||||
|
|
||||||
|
text = CODE_STASH_RE.sub(stash_code, text)
|
||||||
|
text = STRIKETHROUGH_RE.sub(lambda m: m.group(1), text)
|
||||||
|
text = BOLD_RE.sub(lambda m: m.group(1), text)
|
||||||
|
text = UNDERLINE_STRONG_RE.sub(lambda m: m.group(1), text)
|
||||||
|
text = ITALIC_RE.sub(lambda m: m.group(1), text)
|
||||||
|
text = UNDERLINE_EM_RE.sub(lambda m: m.group(1), text)
|
||||||
|
text = LINK_RE.sub(self._replace_link, text)
|
||||||
|
text = IMAGE_RE.sub(self._replace_image, text)
|
||||||
|
|
||||||
|
for idx, code in enumerate(code_segments):
|
||||||
|
text = text.replace(f"\u0000CODE{idx}\u0000", code)
|
||||||
|
return text
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _replace_link(match) -> str:
|
||||||
|
label = match.group(1).strip()
|
||||||
|
url = match.group(2).strip()
|
||||||
|
if not label or label == url:
|
||||||
|
return url
|
||||||
|
return f"{label} ({url})"
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _replace_image(match) -> str:
|
||||||
|
alt = match.group(1).strip()
|
||||||
|
url = match.group(2).strip()
|
||||||
|
if not alt:
|
||||||
|
return url
|
||||||
|
return f"{alt} ({url})"
|
||||||
|
|
||||||
|
|
||||||
|
def _nex_renderer_factory(*, frontmatter: FrontMatter, **options: Any) -> NexRenderer:
|
||||||
|
width = int(options.get("width", 80))
|
||||||
|
return NexRenderer(frontmatter, width=width)
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
register_renderer("nex", _nex_renderer_factory)
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
Loading…
Add table
Add a link
Reference in a new issue