Skip to content
Merged
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
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## Unreleased

### Added

- `--enable-line-numbers` CLI flag to opt in to line numbers for Confluence code blocks

### Fixed

- Pass title prefix through rendering pipeline so Confluence anchors use the correct prefixed page title
Expand All @@ -15,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Changed

- **BREAKING (pre-1.0):** Confluence code block line numbers are now **off by default**. Existing users will lose line numbers on their next upload unless they pass `--enable-line-numbers`.
- Use stdlib `html.parser.HTMLParser` for robust HTML-to-XHTML conversion instead of regex
- Use dynamic default branch detection in publish-release script

Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,10 @@ In file.md:

By default, relative links that point to non-existent files (or files that are not being uploaded in the current batch) will result in an error. To ignore these errors and keep the links as they are, use the `--ignore-relative-link-errors` flag.

## Code blocks

By default, line numbers are disabled in Confluence code blocks for cleaner output. To render line numbers alongside your code, pass the `--enable-line-numbers` flag.

## Directory arguments

### Uploading Folders Recursively
Expand Down
9 changes: 9 additions & 0 deletions mdfluence/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,12 @@ def get_parser():
help="disable conversion of :shortcode: emoji to Unicode characters",
)

parser.add_argument(
"--enable-line-numbers",
action="store_true",
help="enable line numbers in Confluence code blocks",
)

parser.add_argument(
"--render-diagrams",
action="store_true",
Expand Down Expand Up @@ -710,6 +716,7 @@ def collect_pages_to_upload(args):
mmdc_path=args.mmdc_path,
plantuml_path=args.plantuml_path,
title_prefix=args.prefix,
enable_line_numbers=args.enable_line_numbers,
)
)

Expand Down Expand Up @@ -743,6 +750,7 @@ def collect_pages_to_upload(args):
mmdc_path=args.mmdc_path,
plantuml_path=args.plantuml_path,
title_prefix=args.prefix,
enable_line_numbers=args.enable_line_numbers,
)
else:
try:
Expand All @@ -761,6 +769,7 @@ def collect_pages_to_upload(args):
mmdc_path=args.mmdc_path,
plantuml_path=args.plantuml_path,
title_prefix=args.prefix,
enable_line_numbers=args.enable_line_numbers,
)
)
except FileNotFoundError:
Expand Down
5 changes: 4 additions & 1 deletion mdfluence/confluence_renderer.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,10 +134,12 @@ def __init__(
render_diagrams=False,
mmdc_path=None,
plantuml_path=None,
enable_line_numbers=False,
):
super().__init__(escape=False)
self.strip_header = strip_header
self.remove_text_newlines = remove_text_newlines
self.enable_line_numbers = enable_line_numbers
self.attachments = list()
self.title = None
self.enable_relative_links = enable_relative_links
Expand Down Expand Up @@ -274,7 +276,8 @@ def block_code(self, code, info=None):
if info is not None:
lang_parameter = self.parameter(name="language", value=info)
root_element.append(lang_parameter)
root_element.append(self.parameter(name="linenumbers", value="true"))
linenumbers_value = "true" if self.enable_line_numbers else "false"
root_element.append(self.parameter(name="linenumbers", value=linenumbers_value))
root_element.append(self.plain_text_body(code))
return root_element.render()

Expand Down
8 changes: 8 additions & 0 deletions mdfluence/document.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ def get_pages_from_directory(
mmdc_path: str | None = None,
plantuml_path: str | None = None,
title_prefix: str | None = None,
enable_line_numbers: bool = False,
) -> List[Page]:
"""
Collect a list of markdown files recursively under the file_path directory.
Expand Down Expand Up @@ -251,6 +252,7 @@ def get_pages_from_directory(
mmdc_path=mmdc_path,
plantuml_path=plantuml_path,
title_prefix=title_prefix,
enable_line_numbers=enable_line_numbers,
)
processed_page.parent_title = parent_page_title
processed_pages.append(processed_page)
Expand All @@ -275,6 +277,7 @@ def get_page_data_from_file_path(
mmdc_path: str | None = None,
plantuml_path: str | None = None,
title_prefix: str | None = None,
enable_line_numbers: bool = False,
) -> Page:
if not isinstance(file_path, Path):
file_path = Path(file_path)
Expand All @@ -299,6 +302,7 @@ def get_page_data_from_file_path(
mmdc_path=mmdc_path,
plantuml_path=plantuml_path,
title_prefix=title_prefix,
enable_line_numbers=enable_line_numbers,
)

if not page.title:
Expand All @@ -320,6 +324,7 @@ def get_page_data_from_lines(
mmdc_path: str | None = None,
plantuml_path: str | None = None,
title_prefix: str | None = None,
enable_line_numbers: bool = False,
) -> Page:
frontmatter = get_document_frontmatter(markdown_lines)
if "frontmatter_end_line" in frontmatter:
Expand All @@ -339,6 +344,7 @@ def get_page_data_from_lines(
mmdc_path=mmdc_path,
plantuml_path=plantuml_path,
title_prefix=title_prefix,
enable_line_numbers=enable_line_numbers,
)

if "title" in frontmatter:
Expand Down Expand Up @@ -366,6 +372,7 @@ def parse_page(
mmdc_path: str | None = None,
plantuml_path: str | None = None,
title_prefix: str | None = None,
enable_line_numbers: bool = False,
) -> Page:
markdown_text = "".join(markdown_lines)

Expand All @@ -385,6 +392,7 @@ def parse_page(
render_diagrams=render_diagrams,
mmdc_path=mmdc_path,
plantuml_path=plantuml_path,
enable_line_numbers=enable_line_numbers,
)
confluence_mistune = mistune.Markdown(renderer=renderer)
for plugin in [
Expand Down
12 changes: 6 additions & 6 deletions test_package/functional/result.xml
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ and code blocks:</p>
<li>This is the second list item.</li>
</ol>
<p>Here's some example code:</p>
<ac:structured-macro ac:name="code"><ac:parameter ac:name="linenumbers">true</ac:parameter>
<ac:structured-macro ac:name="code"><ac:parameter ac:name="linenumbers">false</ac:parameter>
<ac:plain-text-body><![CDATA[return shell_exec("echo $input | $markdown_script");]]></ac:plain-text-body>
</ac:structured-macro>
</blockquote>
Expand Down Expand Up @@ -205,7 +205,7 @@ inside a list item.</p>
to be indented <em>twice</em> -- 8 spaces or two tabs:</p>
<ul>
<li><p>A list item with a code block:</p>
<ac:structured-macro ac:name="code"><ac:parameter ac:name="linenumbers">true</ac:parameter>
<ac:structured-macro ac:name="code"><ac:parameter ac:name="linenumbers">false</ac:parameter>
<ac:plain-text-body><![CDATA[<code goes here>]]></ac:plain-text-body>
</ac:structured-macro>
</li>
Expand All @@ -218,11 +218,11 @@ in both <code>&lt;pre&gt;</code> and <code>&lt;code&gt;</code> tags.</p>
<p>To produce a code block in Markdown, simply indent every line of the
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:structured-macro ac:name="code"><ac:parameter ac:name="linenumbers">false</ac:parameter>
<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:structured-macro ac:name="code"><ac:parameter ac:name="linenumbers">false</ac:parameter>
<ac:plain-text-body><![CDATA[tell application "Foo"
beep
end tell]]></ac:plain-text-body>
Expand All @@ -234,15 +234,15 @@ are automatically converted into HTML entities. This makes it very
easy to include example HTML source code using Markdown -- just paste
it and indent it, and Markdown will handle the hassle of encoding the
ampersands and angle brackets. For example, this:</p>
<ac:structured-macro ac:name="code"><ac:parameter ac:name="linenumbers">true</ac:parameter>
<ac:structured-macro ac:name="code"><ac:parameter ac:name="linenumbers">false</ac:parameter>
<ac:plain-text-body><![CDATA[<div class="footer">
&copy; 2004 Foo Corporation
</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:structured-macro ac:name="code"><ac:parameter ac:name="linenumbers">false</ac:parameter>
<ac:plain-text-body><![CDATA[tell application "Foo"
beep
end tell
Expand Down
13 changes: 13 additions & 0 deletions test_package/functional/test_full_rendering.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,16 @@ def test_full_document(script_loc):

assert page.body == result_data
assert page.title == "Markdown: Syntax"


def test_full_document_with_line_numbers(script_loc):
markdown_path = script_loc.join("test.md")

page = document.get_page_data_from_file_path(
markdown_path, enable_line_numbers=True
)

# All code blocks should have linenumbers set to true
assert '<ac:parameter ac:name="linenumbers">true</ac:parameter>' in page.body
assert '<ac:parameter ac:name="linenumbers">false</ac:parameter>' not in page.body
assert page.title == "Markdown: Syntax"
34 changes: 32 additions & 2 deletions test_package/unit/test_renderer.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ def test_renderer_block_code():
test_code = "this is a piece of code"
test_markup = (
'<ac:structured-macro ac:name="code">'
'<ac:parameter ac:name="linenumbers">true</ac:parameter>\n'
'<ac:parameter ac:name="linenumbers">false</ac:parameter>\n'
"<ac:plain-text-body><![CDATA[this is a piece of code]]></ac:plain-text-body>\n"
"</ac:structured-macro>\n"
)
Expand All @@ -148,7 +148,7 @@ def test_renderer_block_code_with_language():
test_markup = (
'<ac:structured-macro ac:name="code">'
'<ac:parameter ac:name="language">whitespace</ac:parameter>\n'
'<ac:parameter ac:name="linenumbers">true</ac:parameter>\n'
'<ac:parameter ac:name="linenumbers">false</ac:parameter>\n'
"<ac:plain-text-body><![CDATA[this is a piece of code]]></ac:plain-text-body>\n"
"</ac:structured-macro>\n"
)
Expand All @@ -158,6 +158,36 @@ def test_renderer_block_code_with_language():
assert renderer.block_code(test_code, info=test_language) == test_markup


def test_renderer_block_code_with_line_numbers():
test_code = "this is a piece of code"
test_markup = (
'<ac:structured-macro ac:name="code">'
'<ac:parameter ac:name="linenumbers">true</ac:parameter>\n'
"<ac:plain-text-body><![CDATA[this is a piece of code]]></ac:plain-text-body>\n"
"</ac:structured-macro>\n"
)

renderer = ConfluenceRenderer(enable_line_numbers=True)

assert renderer.block_code(test_code) == test_markup


def test_renderer_block_code_with_language_and_line_numbers():
test_code = "this is a piece of code"
test_language = "python"
test_markup = (
'<ac:structured-macro ac:name="code">'
'<ac:parameter ac:name="language">python</ac:parameter>\n'
'<ac:parameter ac:name="linenumbers">true</ac:parameter>\n'
"<ac:plain-text-body><![CDATA[this is a piece of code]]></ac:plain-text-body>\n"
"</ac:structured-macro>\n"
)

renderer = ConfluenceRenderer(enable_line_numbers=True)

assert renderer.block_code(test_code, info=test_language) == test_markup


def test_renderer_header_sets_title():
test_header = "this is a header"
renderer = ConfluenceRenderer()
Expand Down
Loading