Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions md2cf/confluence_renderer.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,10 @@ def append(self, child):
self.children.append(child)


class ConfluenceRenderer(mistune.Renderer):
def __init__(self, strip_header=False, remove_text_newlines=False, **kwargs):
class ConfluenceRenderer(mistune.HTMLRenderer):
def __init__(self, strip_header=False,
remove_text_newlines=False,
**kwargs):
super().__init__(**kwargs)
self.strip_header = strip_header
self.remove_text_newlines = remove_text_newlines
Expand All @@ -61,14 +63,14 @@ def reinit(self):
self.attachments = list()
self.title = None

def header(self, text, level, raw=None):
def heading(self, text, level, raw=None):
if self.title is None and level == 1:
self.title = text
# Don't duplicate page title as a header
if self.strip_header:
return ""

return super(ConfluenceRenderer, self).header(text, level, raw=raw)
return super(ConfluenceRenderer, self).heading(text, level, raw=raw)

def structured_macro(self, name):
return ConfluenceTag("structured-macro", attrib={"name": name})
Expand All @@ -87,7 +89,7 @@ def text(self, text):
if self.remove_text_newlines:
text = text.replace("\n", " ")

return super().text(text)
return text

def block_code(self, code, lang=None):
root_element = self.structured_macro("code")
Expand Down
13 changes: 10 additions & 3 deletions md2cf/document.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,9 @@ def get_pages_from_directory(


def get_page_data_from_file_path(
file_path: Path, strip_header: bool = False, remove_text_newlines: bool = False
file_path: Path,
strip_header: bool = False,
remove_text_newlines: bool = False,
) -> Page:
if not isinstance(file_path, Path):
file_path = Path(file_path)
Expand Down Expand Up @@ -234,17 +236,22 @@ def parse_page(
markdown_lines: List[str],
strip_header: bool = False,
remove_text_newlines: bool = False,
escape: bool = False,
allow_harmful_protocols: bool = False
) -> Page:
renderer = ConfluenceRenderer(
use_xhtml=True,
strip_header=strip_header,
remove_text_newlines=remove_text_newlines,
escape=escape,
allow_harmful_protocols=allow_harmful_protocols
)
confluence_mistune = mistune.Markdown(renderer=renderer)
confluence_content = confluence_mistune("".join(markdown_lines))

page = Page(
title=renderer.title, body=confluence_content, attachments=renderer.attachments
title=renderer.title,
body=confluence_content,
attachments=renderer.attachments
)

return page
Expand Down
6 changes: 3 additions & 3 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@
],
keywords="markdown confluence",
install_requires=[
"mistune==0.8.4",
"mistune==3.0.1",
"tortilla==0.5.0",
"PyYAML==6.0",
"gitignore_parser==0.0.8",
"PyYAML==6.0.1",
"gitignore_parser==0.1.6",
],
python_requires=">=3.6",
entry_points={"console_scripts": ["md2cf=md2cf.__main__:main"]},
Expand Down
69 changes: 31 additions & 38 deletions tests/functional/result.xml
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
<h1>Markdown: Syntax</h1>
<ul>
<li><a href="#markdown-syntax">Markdown: Syntax</a><ul>
<li><a href="#overview">Overview</a><ul>
<li><a href="#philosophy">Philosophy</a></li>
<li><a href="#html">Inline HTML</a></li>
<li><a href="#autoescape">Automatic Escaping for Special Characters</a></li>
</ul>
</li>
<li><a href="#block">Block Elements</a><ul>
<li><a href="#p">Paragraphs and Line Breaks</a></li>
<li><a href="#header">Headers</a></li>
<li><a href="#blockquote">Blockquotes</a></li>
<li><a href="#list">Lists</a></li>
<li><a href="#precode">Code Blocks</a></li>
<li><a href="#hr">Horizontal Rules</a></li>
<li><a href="#block-elements">Block Elements</a><ul>
<li><a href="#paragraphs-and-line-breaks">Paragraphs and Line Breaks</a></li>
<li><a href="#headers">Headers</a></li>
<li><a href="#blockquotes">Blockquotes</a></li>
<li><a href="#lists">Lists</a></li>
<li><a href="#code-blocks">Code Blocks</a></li>
</ul>
</li>
<li><a href="#span">Span Elements</a><ul>
<li><a href="#link">Links</a></li>
<li><a href="#em">Emphasis</a></li>
<li><a href="#span-elements">Span Elements</a><ul>
<li><a href="#links">Links</a></li>
<li><a href="#emphasis">Emphasis</a></li>
<li><a href="#code">Code</a></li>
</ul>
</li>
</ul>
</li>
</ul>
<p><strong>Note:</strong> This document is itself written using Markdown; you
can <a href="/projects/markdown/syntax.text">see the source for it by adding '.text' to the URL</a>.</p>
<hr />
Expand Down Expand Up @@ -60,30 +60,37 @@ determines the header level.)</p>
familiar with quoting passages of text in an email message, then you
know how to create a blockquote in Markdown. It looks best if you hard
wrap the text and put a <code>&gt;</code> before every line:</p>
<blockquote><p>This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet,
<blockquote>
<p>This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet,
consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus.
Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus.</p>
<p>Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse
id sem consectetuer libero luctus adipiscing.</p>
</blockquote>
<p>Markdown allows you to be lazy and only put the <code>&gt;</code> before the first
line of a hard-wrapped paragraph:</p>
<blockquote><p>This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet,
<blockquote>
<p>This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet,
consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus.
Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus.</p>
</blockquote>
<blockquote>
<p>Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse
id sem consectetuer libero luctus adipiscing.</p>
</blockquote>
<p>Blockquotes can be nested (i.e. a blockquote-in-a-blockquote) by
adding additional levels of <code>&gt;</code>:</p>
<blockquote><p>This is the first level of quoting.</p>
<blockquote><p>This is nested blockquote.</p>
<blockquote>
<p>This is the first level of quoting.</p>
<blockquote>
<p>This is nested blockquote.</p>
</blockquote>
<p>Back to the first level.</p>
</blockquote>
<p>Blockquotes can contain other Markdown elements, including headers, lists,
and code blocks:</p>
<blockquote><h2>This is a header.</h2>
<blockquote>
<h2>This is a header.</h2>
<ol>
<li>This is the first list item.</li>
<li>This is the second list item.</li>
Expand Down Expand Up @@ -132,16 +139,6 @@ Markdown produces from the above list is:</p>
<li>McHale</li>
<li>Parish</li>
</ol>
<p>or even:</p>
<ol>
<li>Bird</li>
<li>McHale</li>
<li>Parish</li>
</ol>
<p>you'd get the exact same HTML output. The point is, if you want to,
you can use ordinal numbers in your ordered Markdown lists, so that
the numbers in your source match the numbers in your published HTML.
But if you want to be lazy, you don't have to.</p>
<p>To make lists look nice, you can wrap items with hanging indents:</p>
<ul>
<li>Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
Expand Down Expand Up @@ -188,7 +185,8 @@ sit amet, consectetuer adipiscing elit.</p>
delimiters need to be indented:</p>
<ul>
<li><p>A list item with a blockquote:</p>
<blockquote><p>This is a blockquote
<blockquote>
<p>This is a blockquote
inside a list item.</p>
</blockquote>
</li>
Expand All @@ -211,17 +209,13 @@ in both <code>&lt;pre&gt;</code> and <code>&lt;code&gt;</code> tags.</p>
block by at least 4 spaces or 1 tab.</p>
<p>This is a normal paragraph:</p>
<ac:structured-macro ac:name="code"><ac:parameter ac:name="linenumbers">true</ac:parameter>
<ac:plain-text-body><![CDATA[This is a code block.

]]></ac:plain-text-body>
<ac:plain-text-body><![CDATA[This is a code block.]]></ac:plain-text-body>
</ac:structured-macro>
<p>Here is an example of AppleScript:</p>
<ac:structured-macro ac:name="code"><ac:parameter ac:name="linenumbers">true</ac:parameter>
<ac:plain-text-body><![CDATA[tell application "Foo"
beep
end tell

]]></ac:plain-text-body>
end tell]]></ac:plain-text-body>
</ac:structured-macro>
<p>A code block continues until it reaches a line that is not indented
(or the end of the article).</p>
Expand All @@ -233,17 +227,16 @@ ampersands and angle brackets. For example, this:</p>
<ac:structured-macro ac:name="code"><ac:parameter ac:name="linenumbers">true</ac:parameter>
<ac:plain-text-body><![CDATA[<div class="footer">
&copy; 2004 Foo Corporation
</div>

]]></ac:plain-text-body>
</div>]]></ac:plain-text-body>
</ac:structured-macro>
<p>Regular Markdown syntax is not processed within code blocks. E.g.,
asterisks are just literal asterisks within a code block. This means
it's also easy to use Markdown to write about Markdown's own syntax.</p>
<ac:structured-macro ac:name="code"><ac:parameter ac:name="linenumbers">true</ac:parameter>
<ac:plain-text-body><![CDATA[tell application "Foo"
beep
end tell]]></ac:plain-text-body>
end tell
]]></ac:plain-text-body>
</ac:structured-macro>
<h2>Span Elements</h2>
<h3>Links</h3>
Expand Down
39 changes: 13 additions & 26 deletions tests/functional/test.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,18 @@
# Markdown: Syntax

* [Overview](#overview)
* [Philosophy](#philosophy)
* [Inline HTML](#html)
* [Automatic Escaping for Special Characters](#autoescape)
* [Block Elements](#block)
* [Paragraphs and Line Breaks](#p)
* [Headers](#header)
* [Blockquotes](#blockquote)
* [Lists](#list)
* [Code Blocks](#precode)
* [Horizontal Rules](#hr)
* [Span Elements](#span)
* [Links](#link)
* [Emphasis](#em)
* [Code](#code)
- [Markdown: Syntax](#markdown-syntax)
- [Overview](#overview)
- [Philosophy](#philosophy)
- [Block Elements](#block-elements)
- [Paragraphs and Line Breaks](#paragraphs-and-line-breaks)
- [Headers](#headers)
- [Blockquotes](#blockquotes)
- [Lists](#lists)
- [Code Blocks](#code-blocks)
- [Span Elements](#span-elements)
- [Links](#links)
- [Emphasis](#emphasis)
- [Code](#code)


**Note:** This document is itself written using Markdown; you
Expand Down Expand Up @@ -154,17 +152,6 @@ If you instead wrote the list in Markdown like this:
1. McHale
1. Parish

or even:

3. Bird
1. McHale
8. Parish

you'd get the exact same HTML output. The point is, if you want to,
you can use ordinal numbers in your ordered Markdown lists, so that
the numbers in your source match the numbers in your published HTML.
But if you want to be lazy, you don't have to.

To make lists look nice, you can wrap items with hanging indents:

* Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
Expand Down
16 changes: 8 additions & 8 deletions tests/unit/test_renderer.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ def test_tag_render_with_child_and_text():

def test_renderer_reinit():
renderer = ConfluenceRenderer()
renderer.header("this is a title", 1)
renderer.heading("this is a title", 1)
assert renderer.title is not None

renderer.reinit()
Expand Down Expand Up @@ -138,7 +138,7 @@ def test_renderer_header_sets_title():
test_header = "this is a header"
renderer = ConfluenceRenderer()

renderer.header(test_header, 1)
renderer.heading(test_header, 1)

assert renderer.title == test_header

Expand All @@ -147,7 +147,7 @@ def test_renderer_strips_header():
test_header = "this is a header"
renderer = ConfluenceRenderer(strip_header=True)

result = renderer.header(test_header, 1)
result = renderer.heading(test_header, 1)

assert result == ""

Expand All @@ -156,7 +156,7 @@ def test_renderer_header_lower_level_does_not_set_title():
test_header = "this is a header"
renderer = ConfluenceRenderer()

renderer.header(test_header, 2)
renderer.heading(test_header, 2)

assert renderer.title is None

Expand All @@ -166,8 +166,8 @@ def test_renderer_header_later_level_sets_title():
test_header = "this is a header"
renderer = ConfluenceRenderer()

renderer.header(test_lower_header, 2)
renderer.header(test_header, 1)
renderer.heading(test_lower_header, 2)
renderer.heading(test_header, 1)

assert renderer.title is test_header

Expand All @@ -177,8 +177,8 @@ def test_renderer_header_only_sets_first_title():
test_second_header = "this is another header"
renderer = ConfluenceRenderer()

renderer.header(test_header, 1)
renderer.header(test_second_header, 1)
renderer.heading(test_header, 1)
renderer.heading(test_second_header, 1)

assert renderer.title is test_header

Expand Down