mkdocs2confluence 0.7.9__tar.gz → 0.7.10__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {mkdocs2confluence-0.7.9/src/mkdocs2confluence.egg-info → mkdocs2confluence-0.7.10}/PKG-INFO +3 -3
- {mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/README.md +2 -2
- {mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/pyproject.toml +1 -1
- {mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10/src/mkdocs2confluence.egg-info}/PKG-INFO +3 -3
- {mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/src/mkdocs_to_confluence/emitter/xhtml.py +15 -0
- {mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/src/mkdocs_to_confluence/ir/nodes.py +16 -0
- {mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/src/mkdocs_to_confluence/transforms/abbrevs.py +47 -26
- {mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/tests/test_abbrevs.py +49 -46
- {mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/LICENSE +0 -0
- {mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/setup.cfg +0 -0
- {mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/src/mkdocs2confluence.egg-info/SOURCES.txt +0 -0
- {mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/src/mkdocs2confluence.egg-info/dependency_links.txt +0 -0
- {mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/src/mkdocs2confluence.egg-info/entry_points.txt +0 -0
- {mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/src/mkdocs2confluence.egg-info/requires.txt +0 -0
- {mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/src/mkdocs2confluence.egg-info/top_level.txt +0 -0
- {mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/src/mkdocs_to_confluence/__init__.py +0 -0
- {mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/src/mkdocs_to_confluence/cli.py +0 -0
- {mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/src/mkdocs_to_confluence/emitter/__init__.py +0 -0
- {mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/src/mkdocs_to_confluence/ir/__init__.py +0 -0
- {mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/src/mkdocs_to_confluence/ir/document.py +0 -0
- {mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/src/mkdocs_to_confluence/ir/treeutil.py +0 -0
- {mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/src/mkdocs_to_confluence/loader/__init__.py +0 -0
- {mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/src/mkdocs_to_confluence/loader/config.py +0 -0
- {mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/src/mkdocs_to_confluence/loader/extra_css.py +0 -0
- {mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/src/mkdocs_to_confluence/loader/nav.py +0 -0
- {mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/src/mkdocs_to_confluence/loader/page.py +0 -0
- {mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/src/mkdocs_to_confluence/parser/__init__.py +0 -0
- {mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/src/mkdocs_to_confluence/parser/markdown.py +0 -0
- {mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/src/mkdocs_to_confluence/pdf/__init__.py +0 -0
- {mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/src/mkdocs_to_confluence/pdf/generator.py +0 -0
- {mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/src/mkdocs_to_confluence/pdf/render.py +0 -0
- {mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/src/mkdocs_to_confluence/preprocess/__init__.py +0 -0
- {mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/src/mkdocs_to_confluence/preprocess/abbrevs.py +0 -0
- {mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/src/mkdocs_to_confluence/preprocess/fence.py +0 -0
- {mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/src/mkdocs_to_confluence/preprocess/frontmatter.py +0 -0
- {mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/src/mkdocs_to_confluence/preprocess/icons.py +0 -0
- {mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/src/mkdocs_to_confluence/preprocess/includes.py +0 -0
- {mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/src/mkdocs_to_confluence/preprocess/linkdefs.py +0 -0
- {mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/src/mkdocs_to_confluence/preview/__init__.py +0 -0
- {mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/src/mkdocs_to_confluence/preview/render.py +0 -0
- {mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/src/mkdocs_to_confluence/preview/server.py +0 -0
- {mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/src/mkdocs_to_confluence/publisher/__init__.py +0 -0
- {mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/src/mkdocs_to_confluence/publisher/client.py +0 -0
- {mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/src/mkdocs_to_confluence/publisher/pipeline.py +0 -0
- {mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/src/mkdocs_to_confluence/transforms/__init__.py +0 -0
- {mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/src/mkdocs_to_confluence/transforms/assets.py +0 -0
- {mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/src/mkdocs_to_confluence/transforms/editlink.py +0 -0
- {mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/src/mkdocs_to_confluence/transforms/images.py +0 -0
- {mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/src/mkdocs_to_confluence/transforms/internallinks.py +0 -0
- {mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/src/mkdocs_to_confluence/transforms/mermaid.py +0 -0
- {mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/tests/test_cli.py +0 -0
- {mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/tests/test_editlink.py +0 -0
- {mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/tests/test_emitter.py +0 -0
- {mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/tests/test_extra_css.py +0 -0
- {mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/tests/test_frontmatter.py +0 -0
- {mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/tests/test_icons.py +0 -0
- {mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/tests/test_images.py +0 -0
- {mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/tests/test_internallinks.py +0 -0
- {mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/tests/test_ir.py +0 -0
- {mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/tests/test_linkdefs.py +0 -0
- {mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/tests/test_loader.py +0 -0
- {mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/tests/test_mermaid.py +0 -0
- {mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/tests/test_page_loader.py +0 -0
- {mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/tests/test_parser.py +0 -0
- {mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/tests/test_pdf.py +0 -0
- {mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/tests/test_preprocess.py +0 -0
- {mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/tests/test_preview.py +0 -0
- {mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/tests/test_publish_client.py +0 -0
- {mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/tests/test_publish_config.py +0 -0
- {mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/tests/test_publish_pipeline.py +0 -0
- {mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/tests/test_server.py +0 -0
- {mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/tests/test_treeutil.py +0 -0
{mkdocs2confluence-0.7.9/src/mkdocs2confluence.egg-info → mkdocs2confluence-0.7.10}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mkdocs2confluence
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.10
|
|
4
4
|
Summary: Publish MkDocs Material pages to Confluence Cloud — admonitions, Mermaid diagrams, tabs, page properties and more
|
|
5
5
|
Author: Anders Hybertz
|
|
6
6
|
License: GPL-3.0-or-later
|
|
@@ -355,7 +355,7 @@ If `repo_url` + `edit_uri` are set in `mkdocs.yml`, an **Edit Source** row links
|
|
|
355
355
|
|
|
356
356
|
### Abbreviation expansion
|
|
357
357
|
|
|
358
|
-
MkDocs abbreviation definitions (`*[ABBR]: Full term`) are
|
|
358
|
+
MkDocs abbreviation definitions (`*[ABBR]: Full term`) are rendered as inline Confluence **footnotes**. The **first occurrence** of each abbreviation in body text is annotated with a footnote macro — Confluence renders it as a superscript number and collects all definitions at the bottom of the page. Subsequent occurrences are left as plain text. Abbreviations that only appear in headings or other non-expandable contexts are collected into an auto-appended **Glossary** section as a fallback.
|
|
359
359
|
|
|
360
360
|
---
|
|
361
361
|
|
|
@@ -364,7 +364,7 @@ MkDocs abbreviation definitions (`*[ABBR]: Full term`) are expanded inline — C
|
|
|
364
364
|
| Feature | Behaviour |
|
|
365
365
|
|---|---|
|
|
366
366
|
| **Admonition styling** | `tip`, `info`, `warning`, `note` use Confluence's fixed native macro colours. `danger`, `error`, `bug` use a custom red `panel` macro with 🚨 prefix. All other types are mapped to the nearest native macro. |
|
|
367
|
-
| **Abbreviation tooltips** | No native tooltip support. First occurrence
|
|
367
|
+
| **Abbreviation tooltips** | No native tooltip support. First occurrence annotated with an inline Confluence footnote; definition collected at the bottom of the page. |
|
|
368
368
|
| **Page ordering** | Confluence sorts child pages alphabetically; the v2 REST API has no write endpoint for ordering. |
|
|
369
369
|
| **Code language aliases** | Short aliases (`py`, `js`, `yml`, `ts`, `sh`) are passed through as-is; Confluence requires full language names for syntax highlighting. |
|
|
370
370
|
| **Unrecognised blocks** | Preserved as a visible `warning` macro — no content is silently lost. |
|
|
@@ -315,7 +315,7 @@ If `repo_url` + `edit_uri` are set in `mkdocs.yml`, an **Edit Source** row links
|
|
|
315
315
|
|
|
316
316
|
### Abbreviation expansion
|
|
317
317
|
|
|
318
|
-
MkDocs abbreviation definitions (`*[ABBR]: Full term`) are
|
|
318
|
+
MkDocs abbreviation definitions (`*[ABBR]: Full term`) are rendered as inline Confluence **footnotes**. The **first occurrence** of each abbreviation in body text is annotated with a footnote macro — Confluence renders it as a superscript number and collects all definitions at the bottom of the page. Subsequent occurrences are left as plain text. Abbreviations that only appear in headings or other non-expandable contexts are collected into an auto-appended **Glossary** section as a fallback.
|
|
319
319
|
|
|
320
320
|
---
|
|
321
321
|
|
|
@@ -324,7 +324,7 @@ MkDocs abbreviation definitions (`*[ABBR]: Full term`) are expanded inline — C
|
|
|
324
324
|
| Feature | Behaviour |
|
|
325
325
|
|---|---|
|
|
326
326
|
| **Admonition styling** | `tip`, `info`, `warning`, `note` use Confluence's fixed native macro colours. `danger`, `error`, `bug` use a custom red `panel` macro with 🚨 prefix. All other types are mapped to the nearest native macro. |
|
|
327
|
-
| **Abbreviation tooltips** | No native tooltip support. First occurrence
|
|
327
|
+
| **Abbreviation tooltips** | No native tooltip support. First occurrence annotated with an inline Confluence footnote; definition collected at the bottom of the page. |
|
|
328
328
|
| **Page ordering** | Confluence sorts child pages alphabetically; the v2 REST API has no write endpoint for ordering. |
|
|
329
329
|
| **Code language aliases** | Short aliases (`py`, `js`, `yml`, `ts`, `sh`) are passed through as-is; Confluence requires full language names for syntax highlighting. |
|
|
330
330
|
| **Unrecognised blocks** | Preserved as a visible `warning` macro — no content is silently lost. |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "mkdocs2confluence"
|
|
3
|
-
version = "0.7.
|
|
3
|
+
version = "0.7.10"
|
|
4
4
|
description = "Publish MkDocs Material pages to Confluence Cloud — admonitions, Mermaid diagrams, tabs, page properties and more"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = { text = "GPL-3.0-or-later" }
|
{mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10/src/mkdocs2confluence.egg-info}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mkdocs2confluence
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.10
|
|
4
4
|
Summary: Publish MkDocs Material pages to Confluence Cloud — admonitions, Mermaid diagrams, tabs, page properties and more
|
|
5
5
|
Author: Anders Hybertz
|
|
6
6
|
License: GPL-3.0-or-later
|
|
@@ -355,7 +355,7 @@ If `repo_url` + `edit_uri` are set in `mkdocs.yml`, an **Edit Source** row links
|
|
|
355
355
|
|
|
356
356
|
### Abbreviation expansion
|
|
357
357
|
|
|
358
|
-
MkDocs abbreviation definitions (`*[ABBR]: Full term`) are
|
|
358
|
+
MkDocs abbreviation definitions (`*[ABBR]: Full term`) are rendered as inline Confluence **footnotes**. The **first occurrence** of each abbreviation in body text is annotated with a footnote macro — Confluence renders it as a superscript number and collects all definitions at the bottom of the page. Subsequent occurrences are left as plain text. Abbreviations that only appear in headings or other non-expandable contexts are collected into an auto-appended **Glossary** section as a fallback.
|
|
359
359
|
|
|
360
360
|
---
|
|
361
361
|
|
|
@@ -364,7 +364,7 @@ MkDocs abbreviation definitions (`*[ABBR]: Full term`) are expanded inline — C
|
|
|
364
364
|
| Feature | Behaviour |
|
|
365
365
|
|---|---|
|
|
366
366
|
| **Admonition styling** | `tip`, `info`, `warning`, `note` use Confluence's fixed native macro colours. `danger`, `error`, `bug` use a custom red `panel` macro with 🚨 prefix. All other types are mapped to the nearest native macro. |
|
|
367
|
-
| **Abbreviation tooltips** | No native tooltip support. First occurrence
|
|
367
|
+
| **Abbreviation tooltips** | No native tooltip support. First occurrence annotated with an inline Confluence footnote; definition collected at the bottom of the page. |
|
|
368
368
|
| **Page ordering** | Confluence sorts child pages alphabetically; the v2 REST API has no write endpoint for ordering. |
|
|
369
369
|
| **Code language aliases** | Short aliases (`py`, `js`, `yml`, `ts`, `sh`) are passed through as-is; Confluence requires full language names for syntax highlighting. |
|
|
370
370
|
| **Unrecognised blocks** | Preserved as a visible `warning` macro — no content is silently lost. |
|
{mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/src/mkdocs_to_confluence/emitter/xhtml.py
RENAMED
|
@@ -22,6 +22,7 @@ from typing import Sequence
|
|
|
22
22
|
from urllib.parse import urlparse
|
|
23
23
|
|
|
24
24
|
from mkdocs_to_confluence.ir.nodes import (
|
|
25
|
+
AbbrevFootnoteNode,
|
|
25
26
|
Admonition,
|
|
26
27
|
BlockQuote,
|
|
27
28
|
BoldNode,
|
|
@@ -555,6 +556,18 @@ def _emit_footnote_ref(node: FootnoteRef) -> str:
|
|
|
555
556
|
)
|
|
556
557
|
|
|
557
558
|
|
|
559
|
+
def _emit_abbrev_footnote(node: AbbrevFootnoteNode) -> str:
|
|
560
|
+
"""Abbreviation term + inline Confluence footnote macro with its definition."""
|
|
561
|
+
term = html.escape(node.abbr)
|
|
562
|
+
defn = html.escape(node.definition)
|
|
563
|
+
return (
|
|
564
|
+
f"{term}"
|
|
565
|
+
f'<ac:structured-macro ac:name="footnote" ac:schema-version="1">'
|
|
566
|
+
f"<ac:rich-text-body><p>{defn}</p></ac:rich-text-body>"
|
|
567
|
+
f"</ac:structured-macro>"
|
|
568
|
+
)
|
|
569
|
+
|
|
570
|
+
|
|
558
571
|
def _emit_footnote_block(node: FootnoteBlock) -> str:
|
|
559
572
|
"""Footnotes section: heading + ordered list with anchor targets."""
|
|
560
573
|
items: list[str] = []
|
|
@@ -613,6 +626,8 @@ def _emit_inline(node: IRNode) -> str:
|
|
|
613
626
|
return _emit_image(node)
|
|
614
627
|
if isinstance(node, FootnoteRef):
|
|
615
628
|
return _emit_footnote_ref(node)
|
|
629
|
+
if isinstance(node, AbbrevFootnoteNode):
|
|
630
|
+
return _emit_abbrev_footnote(node)
|
|
616
631
|
if isinstance(node, LineBreakNode):
|
|
617
632
|
return "<br />"
|
|
618
633
|
if isinstance(node, InlineHtmlNode):
|
|
@@ -428,6 +428,22 @@ class FrontMatter(IRNode):
|
|
|
428
428
|
site_url: str | None = None
|
|
429
429
|
|
|
430
430
|
|
|
431
|
+
# ── Abbreviation footnotes ────────────────────────────────────────────────────
|
|
432
|
+
|
|
433
|
+
|
|
434
|
+
@dataclass(frozen=True)
|
|
435
|
+
class AbbrevFootnoteNode(IRNode):
|
|
436
|
+
"""An abbreviation annotated with an inline Confluence footnote.
|
|
437
|
+
|
|
438
|
+
The emitter outputs the abbreviated term immediately followed by a
|
|
439
|
+
``footnote`` macro containing the full definition. Confluence collects
|
|
440
|
+
all footnote macros and renders their bodies at the bottom of the page.
|
|
441
|
+
"""
|
|
442
|
+
|
|
443
|
+
abbr: str
|
|
444
|
+
definition: str
|
|
445
|
+
|
|
446
|
+
|
|
431
447
|
# ── Footnotes ────────────────────────────────────────────────────────────────
|
|
432
448
|
|
|
433
449
|
|
{mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/src/mkdocs_to_confluence/transforms/abbrevs.py
RENAMED
|
@@ -3,17 +3,19 @@
|
|
|
3
3
|
Collects ``*[ABBR]: definition`` pairs (extracted by
|
|
4
4
|
:mod:`mkdocs_to_confluence.preprocess.abbrevs`) and walks the IR tree to:
|
|
5
5
|
|
|
6
|
-
1. **
|
|
7
|
-
paragraphs, list items, table body cells, blockquotes — by
|
|
8
|
-
``
|
|
6
|
+
1. **Annotate** the first occurrence of each abbreviation in *safe* body nodes —
|
|
7
|
+
paragraphs, list items, table body cells, blockquotes — by inserting an
|
|
8
|
+
inline Confluence ``footnote`` macro immediately after the term. Confluence
|
|
9
|
+
renders this as a superscript number and collects all definitions at the
|
|
10
|
+
bottom of the page.
|
|
9
11
|
|
|
10
12
|
2. **Skip** structural/title nodes where expansion would look odd:
|
|
11
13
|
section headings, table header cells, admonition/panel titles, code spans,
|
|
12
14
|
and link text.
|
|
13
15
|
|
|
14
16
|
3. **Append a Glossary section** at the end of the page for any abbreviation
|
|
15
|
-
that was detected in the page text but could not be
|
|
16
|
-
|
|
17
|
+
that was detected in the page text but could not be footnoted because it
|
|
18
|
+
only appeared in skipped contexts (headings, table headers, etc.).
|
|
17
19
|
|
|
18
20
|
Entry point
|
|
19
21
|
-----------
|
|
@@ -27,6 +29,7 @@ import re
|
|
|
27
29
|
from dataclasses import replace
|
|
28
30
|
|
|
29
31
|
from mkdocs_to_confluence.ir.nodes import (
|
|
32
|
+
AbbrevFootnoteNode,
|
|
30
33
|
Admonition,
|
|
31
34
|
BlockQuote,
|
|
32
35
|
BoldNode,
|
|
@@ -64,21 +67,31 @@ class _State:
|
|
|
64
67
|
for abbr in abbrevs
|
|
65
68
|
}
|
|
66
69
|
|
|
67
|
-
def
|
|
68
|
-
"""
|
|
70
|
+
def expand_to_nodes(self, text: str) -> tuple[IRNode, ...]:
|
|
71
|
+
"""Split *text* around the first unexpanded abbreviation.
|
|
69
72
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
as plain text.
|
|
73
|
+
Returns a mix of :class:`TextNode` and :class:`AbbrevFootnoteNode`.
|
|
74
|
+
Each abbreviation is footnoted at most once per page.
|
|
73
75
|
"""
|
|
74
|
-
|
|
76
|
+
best: tuple[int, int, str] | None = None
|
|
77
|
+
for abbr in self.abbrevs:
|
|
75
78
|
if abbr in self.expanded:
|
|
76
79
|
continue
|
|
77
|
-
|
|
78
|
-
if
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
80
|
+
m = self._patterns[abbr].search(text)
|
|
81
|
+
if m and (best is None or m.start() < best[0]):
|
|
82
|
+
best = (m.start(), m.end(), abbr)
|
|
83
|
+
|
|
84
|
+
if best is None:
|
|
85
|
+
return (TextNode(text),) if text else ()
|
|
86
|
+
|
|
87
|
+
start, end, abbr = best
|
|
88
|
+
self.expanded.add(abbr)
|
|
89
|
+
nodes: list[IRNode] = []
|
|
90
|
+
if text[:start]:
|
|
91
|
+
nodes.append(TextNode(text[:start]))
|
|
92
|
+
nodes.append(AbbrevFootnoteNode(abbr=abbr, definition=self.abbrevs[abbr]))
|
|
93
|
+
nodes.extend(self.expand_to_nodes(text[end:]))
|
|
94
|
+
return tuple(nodes)
|
|
82
95
|
|
|
83
96
|
|
|
84
97
|
# ── Block-level transform ─────────────────────────────────────────────────────
|
|
@@ -151,22 +164,25 @@ def _transform_table_cell(cell: TableCell, state: _State) -> TableCell:
|
|
|
151
164
|
|
|
152
165
|
|
|
153
166
|
def _inline(nodes: tuple[IRNode, ...], state: _State, safe: bool) -> tuple[IRNode, ...]:
|
|
154
|
-
|
|
167
|
+
result: list[IRNode] = []
|
|
168
|
+
for n in nodes:
|
|
169
|
+
result.extend(_transform_inline(n, state, safe))
|
|
170
|
+
return tuple(result)
|
|
155
171
|
|
|
156
172
|
|
|
157
|
-
def _transform_inline(node: IRNode, state: _State, safe: bool) -> IRNode:
|
|
173
|
+
def _transform_inline(node: IRNode, state: _State, safe: bool) -> tuple[IRNode, ...]:
|
|
158
174
|
if isinstance(node, TextNode):
|
|
159
|
-
return
|
|
175
|
+
return state.expand_to_nodes(node.text) if safe else (node,)
|
|
160
176
|
|
|
161
177
|
if isinstance(node, (BoldNode, ItalicNode, StrikethroughNode)):
|
|
162
|
-
return replace(node, children=_inline(node.children, state, safe))
|
|
178
|
+
return (replace(node, children=_inline(node.children, state, safe)),)
|
|
163
179
|
|
|
164
180
|
if isinstance(node, LinkNode):
|
|
165
181
|
# Expanding inside link text could break the anchor label — skip.
|
|
166
|
-
return replace(node, children=_inline(node.children, state, safe=False))
|
|
182
|
+
return (replace(node, children=_inline(node.children, state, safe=False)),)
|
|
167
183
|
|
|
168
184
|
# CodeInlineNode, ImageNode — never expand.
|
|
169
|
-
return node
|
|
185
|
+
return (node,)
|
|
170
186
|
|
|
171
187
|
|
|
172
188
|
# ── Glossary builder ──────────────────────────────────────────────────────────
|
|
@@ -182,7 +198,7 @@ def _find_mentioned(text: str, abbrevs: dict[str, str]) -> set[str]:
|
|
|
182
198
|
|
|
183
199
|
|
|
184
200
|
def _build_glossary_section(terms: dict[str, str]) -> tuple[IRNode, ...]:
|
|
185
|
-
"""Return an HR + h6 ``Section`` listing abbreviations that could not be
|
|
201
|
+
"""Return an HR + h6 ``Section`` listing abbreviations that could not be footnoted."""
|
|
186
202
|
items = tuple(
|
|
187
203
|
ListItem(children=(Paragraph(children=(TextNode(f"{abbr} — {defn}"),)),))
|
|
188
204
|
for abbr, defn in sorted(terms.items())
|
|
@@ -217,9 +233,11 @@ def apply_abbreviations(
|
|
|
217
233
|
abbreviations need a glossary entry.
|
|
218
234
|
|
|
219
235
|
Returns:
|
|
220
|
-
Modified node tuple.
|
|
221
|
-
|
|
222
|
-
|
|
236
|
+
Modified node tuple. Abbreviations in body text are replaced with an
|
|
237
|
+
:class:`~mkdocs_to_confluence.ir.nodes.AbbrevFootnoteNode` on first
|
|
238
|
+
occurrence. A ``Glossary`` section is appended only for abbreviations
|
|
239
|
+
that were detected in *page_text* but never footnoted (e.g. they only
|
|
240
|
+
appeared in headings or table headers).
|
|
223
241
|
"""
|
|
224
242
|
if not abbrevs:
|
|
225
243
|
return nodes
|
|
@@ -228,12 +246,15 @@ def apply_abbreviations(
|
|
|
228
246
|
transformed = tuple(_transform_block(n, state) for n in nodes)
|
|
229
247
|
|
|
230
248
|
mentioned = _find_mentioned(page_text, abbrevs)
|
|
249
|
+
# Only add a glossary entry for abbreviations that were never footnoted.
|
|
231
250
|
glossary_needed = {
|
|
232
251
|
abbr: abbrevs[abbr]
|
|
233
252
|
for abbr in mentioned
|
|
253
|
+
if abbr not in state.expanded
|
|
234
254
|
}
|
|
235
255
|
|
|
236
256
|
if glossary_needed:
|
|
237
257
|
transformed = transformed + _build_glossary_section(glossary_needed)
|
|
238
258
|
|
|
239
259
|
return transformed
|
|
260
|
+
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
from mkdocs_to_confluence.ir.nodes import (
|
|
6
|
+
AbbrevFootnoteNode,
|
|
6
7
|
Admonition,
|
|
7
8
|
BoldNode,
|
|
8
9
|
BulletList,
|
|
@@ -81,26 +82,36 @@ def _para(text: str) -> Paragraph:
|
|
|
81
82
|
return Paragraph(children=(TextNode(text),))
|
|
82
83
|
|
|
83
84
|
|
|
84
|
-
def
|
|
85
|
+
def test_expands_first_occurrence_as_footnote():
|
|
85
86
|
abbrevs = {"IAM": "Identity and Access Management"}
|
|
86
87
|
nodes = (_para("The IAM platform handles IAM requests."),)
|
|
87
88
|
result = apply_abbreviations(nodes, abbrevs, page_text="The IAM platform handles IAM requests.")
|
|
88
89
|
para = result[0]
|
|
89
90
|
assert isinstance(para, Paragraph)
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
assert
|
|
94
|
-
assert text
|
|
91
|
+
children = para.children
|
|
92
|
+
# Should be: TextNode("The ") + AbbrevFootnoteNode + TextNode(" platform handles IAM requests.")
|
|
93
|
+
assert len(children) == 3
|
|
94
|
+
assert isinstance(children[0], TextNode)
|
|
95
|
+
assert children[0].text == "The "
|
|
96
|
+
assert isinstance(children[1], AbbrevFootnoteNode)
|
|
97
|
+
assert children[1].abbr == "IAM"
|
|
98
|
+
assert children[1].definition == "Identity and Access Management"
|
|
99
|
+
# Second occurrence left as plain text
|
|
100
|
+
assert isinstance(children[2], TextNode)
|
|
101
|
+
assert "IAM requests." in children[2].text
|
|
102
|
+
assert "Identity and Access Management" not in children[2].text
|
|
95
103
|
|
|
96
104
|
|
|
97
105
|
def test_expands_multiple_different_abbrevs():
|
|
98
106
|
abbrevs = {"IAM": "Identity and Access Management", "RBAC": "Role-Based Access Control"}
|
|
99
107
|
nodes = (_para("Use IAM and RBAC for access control."),)
|
|
100
108
|
result = apply_abbreviations(nodes, abbrevs, page_text="Use IAM and RBAC for access control.")
|
|
101
|
-
|
|
102
|
-
assert
|
|
103
|
-
|
|
109
|
+
para = result[0]
|
|
110
|
+
assert isinstance(para, Paragraph)
|
|
111
|
+
footnotes = [c for c in para.children if isinstance(c, AbbrevFootnoteNode)]
|
|
112
|
+
abbrs = {fn.abbr for fn in footnotes}
|
|
113
|
+
assert "IAM" in abbrs
|
|
114
|
+
assert "RBAC" in abbrs
|
|
104
115
|
|
|
105
116
|
|
|
106
117
|
def test_no_expand_when_no_abbrevs():
|
|
@@ -131,14 +142,13 @@ def test_glossary_appended_for_heading_only_abbrev():
|
|
|
131
142
|
children=(),
|
|
132
143
|
)
|
|
133
144
|
result = apply_abbreviations((section,), abbrevs, page_text="IAM Platform")
|
|
134
|
-
#
|
|
145
|
+
# IAM only in heading (unsafe) → no footnote → glossary fallback appended
|
|
135
146
|
assert len(result) == 3
|
|
136
147
|
assert isinstance(result[1], HorizontalRule)
|
|
137
148
|
glossary = result[2]
|
|
138
149
|
assert isinstance(glossary, Section)
|
|
139
150
|
assert glossary.anchor == "glossary"
|
|
140
151
|
assert glossary.level == 6
|
|
141
|
-
# Glossary should list the term
|
|
142
152
|
bullet_list = glossary.children[0]
|
|
143
153
|
assert isinstance(bullet_list, BulletList)
|
|
144
154
|
item_text = bullet_list.items[0].children[0].children[0].text # type: ignore[union-attr]
|
|
@@ -146,16 +156,13 @@ def test_glossary_appended_for_heading_only_abbrev():
|
|
|
146
156
|
assert "Identity and Access Management" in item_text
|
|
147
157
|
|
|
148
158
|
|
|
149
|
-
def
|
|
159
|
+
def test_no_glossary_when_abbrev_footnoted_inline():
|
|
150
160
|
abbrevs = {"IAM": "Identity and Access Management"}
|
|
151
161
|
nodes = (_para("The IAM platform."),)
|
|
152
162
|
result = apply_abbreviations(nodes, abbrevs, page_text="The IAM platform.")
|
|
153
|
-
#
|
|
154
|
-
assert len(result) ==
|
|
155
|
-
assert isinstance(result[
|
|
156
|
-
glossary = result[-1]
|
|
157
|
-
assert isinstance(glossary, Section)
|
|
158
|
-
assert glossary.anchor == "glossary"
|
|
163
|
+
# Footnoted inline → no glossary needed
|
|
164
|
+
assert len(result) == 1
|
|
165
|
+
assert isinstance(result[0], Paragraph)
|
|
159
166
|
|
|
160
167
|
|
|
161
168
|
def test_no_expand_in_table_header_cell():
|
|
@@ -173,9 +180,9 @@ def test_no_expand_in_table_header_cell():
|
|
|
173
180
|
header_text = result[0].header.cells[0].children[0].text # type: ignore[union-attr]
|
|
174
181
|
assert header_text == "API Endpoint"
|
|
175
182
|
|
|
176
|
-
# Body cell: first occurrence
|
|
177
|
-
|
|
178
|
-
assert "API
|
|
183
|
+
# Body cell: first occurrence footnoted
|
|
184
|
+
body_children = result[0].rows[0].cells[0].children # type: ignore[union-attr]
|
|
185
|
+
assert any(isinstance(c, AbbrevFootnoteNode) and c.abbr == "API" for c in body_children)
|
|
179
186
|
|
|
180
187
|
|
|
181
188
|
def test_no_expand_in_admonition_title():
|
|
@@ -188,9 +195,9 @@ def test_no_expand_in_admonition_title():
|
|
|
188
195
|
result = apply_abbreviations((admonition,), abbrevs, page_text="TLS Configuration Use TLS for encryption.")
|
|
189
196
|
# Title is str, unchanged
|
|
190
197
|
assert result[0].title == "TLS Configuration" # type: ignore[union-attr]
|
|
191
|
-
# Body paragraph:
|
|
192
|
-
|
|
193
|
-
assert "TLS
|
|
198
|
+
# Body paragraph: TLS footnoted
|
|
199
|
+
body_children = result[0].children[0].children # type: ignore[union-attr]
|
|
200
|
+
assert any(isinstance(c, AbbrevFootnoteNode) and c.abbr == "TLS" for c in body_children)
|
|
194
201
|
|
|
195
202
|
|
|
196
203
|
def test_no_expand_in_code_block():
|
|
@@ -218,42 +225,37 @@ def test_expands_inside_bold():
|
|
|
218
225
|
bold = BoldNode(children=(TextNode("CI pipeline"),))
|
|
219
226
|
para = Paragraph(children=(bold,))
|
|
220
227
|
result = apply_abbreviations((para,), abbrevs, page_text="CI pipeline")
|
|
221
|
-
|
|
222
|
-
assert
|
|
228
|
+
bold_children = result[0].children[0].children # type: ignore[union-attr]
|
|
229
|
+
assert any(isinstance(c, AbbrevFootnoteNode) and c.abbr == "CI" for c in bold_children)
|
|
223
230
|
|
|
224
231
|
|
|
225
232
|
def test_word_boundary_not_partial_match():
|
|
226
233
|
abbrevs = {"API": "Application Programming Interface"}
|
|
227
234
|
nodes = (_para("The RAPID response via API."),)
|
|
228
235
|
result = apply_abbreviations(nodes, abbrevs, page_text="The RAPID response via API.")
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
236
|
+
para = result[0]
|
|
237
|
+
assert isinstance(para, Paragraph)
|
|
238
|
+
# RAPID should be untouched; API should be footnoted
|
|
239
|
+
all_text = "".join(c.text for c in para.children if isinstance(c, TextNode))
|
|
240
|
+
assert "RAPID" in all_text
|
|
241
|
+
footnotes = [c for c in para.children if isinstance(c, AbbrevFootnoteNode)]
|
|
242
|
+
assert any(fn.abbr == "API" for fn in footnotes)
|
|
233
243
|
|
|
234
244
|
|
|
235
|
-
def
|
|
236
|
-
# Abbreviations expanded inline should
|
|
237
|
-
# so readers who jump directly to the bottom have a full reference.
|
|
245
|
+
def test_footnoted_abbrevs_not_in_glossary():
|
|
246
|
+
# Abbreviations expanded inline via footnote should NOT also appear in a glossary.
|
|
238
247
|
abbrevs = {"API": "Application Programming Interface", "IAM": "Identity and Access Management"}
|
|
239
248
|
nodes = (_para("Use the API and IAM to authenticate."),)
|
|
240
249
|
result = apply_abbreviations(
|
|
241
250
|
nodes, abbrevs,
|
|
242
251
|
page_text="Use the API and IAM to authenticate."
|
|
243
252
|
)
|
|
244
|
-
# Both
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
assert
|
|
248
|
-
|
|
249
|
-
assert
|
|
250
|
-
assert isinstance(result[1], HorizontalRule)
|
|
251
|
-
glossary = result[-1]
|
|
252
|
-
assert isinstance(glossary, Section)
|
|
253
|
-
items = glossary.children[0].items # type: ignore[union-attr]
|
|
254
|
-
labels = " ".join(item.children[0].children[0].text for item in items) # type: ignore[union-attr]
|
|
255
|
-
assert "API" in labels
|
|
256
|
-
assert "IAM" in labels
|
|
253
|
+
# Both footnoted inline → no glossary
|
|
254
|
+
assert len(result) == 1
|
|
255
|
+
para = result[0]
|
|
256
|
+
assert isinstance(para, Paragraph)
|
|
257
|
+
footnotes = [c for c in para.children if isinstance(c, AbbrevFootnoteNode)]
|
|
258
|
+
assert {fn.abbr for fn in footnotes} == {"API", "IAM"}
|
|
257
259
|
|
|
258
260
|
|
|
259
261
|
def test_abbrev_not_in_text_produces_no_glossary():
|
|
@@ -281,3 +283,4 @@ def test_glossary_entries_sorted_alphabetically():
|
|
|
281
283
|
items = glossary.children[0].items # type: ignore[union-attr]
|
|
282
284
|
labels = [item.children[0].children[0].text for item in items] # type: ignore[union-attr]
|
|
283
285
|
assert labels == sorted(labels)
|
|
286
|
+
|
|
File without changes
|
|
File without changes
|
{mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/src/mkdocs2confluence.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
{mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/src/mkdocs2confluence.egg-info/entry_points.txt
RENAMED
|
File without changes
|
{mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/src/mkdocs2confluence.egg-info/requires.txt
RENAMED
|
File without changes
|
{mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/src/mkdocs2confluence.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/src/mkdocs_to_confluence/emitter/__init__.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/src/mkdocs_to_confluence/ir/__init__.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/src/mkdocs_to_confluence/ir/document.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/src/mkdocs_to_confluence/ir/treeutil.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/src/mkdocs_to_confluence/loader/__init__.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/src/mkdocs_to_confluence/loader/config.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/src/mkdocs_to_confluence/loader/extra_css.py
RENAMED
|
File without changes
|
|
File without changes
|
{mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/src/mkdocs_to_confluence/loader/page.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/src/mkdocs_to_confluence/parser/__init__.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/src/mkdocs_to_confluence/parser/markdown.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/src/mkdocs_to_confluence/pdf/__init__.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/src/mkdocs_to_confluence/pdf/generator.py
RENAMED
|
File without changes
|
|
File without changes
|
{mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/src/mkdocs_to_confluence/preprocess/__init__.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/src/mkdocs_to_confluence/preprocess/abbrevs.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/src/mkdocs_to_confluence/preprocess/fence.py
RENAMED
|
File without changes
|
|
File without changes
|
{mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/src/mkdocs_to_confluence/preprocess/icons.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/src/mkdocs_to_confluence/preprocess/includes.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/src/mkdocs_to_confluence/preprocess/linkdefs.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/src/mkdocs_to_confluence/preview/__init__.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/src/mkdocs_to_confluence/preview/render.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/src/mkdocs_to_confluence/preview/server.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/src/mkdocs_to_confluence/publisher/__init__.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/src/mkdocs_to_confluence/publisher/client.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/src/mkdocs_to_confluence/publisher/pipeline.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/src/mkdocs_to_confluence/transforms/__init__.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/src/mkdocs_to_confluence/transforms/assets.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/src/mkdocs_to_confluence/transforms/editlink.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/src/mkdocs_to_confluence/transforms/images.py
RENAMED
|
File without changes
|
|
File without changes
|
{mkdocs2confluence-0.7.9 → mkdocs2confluence-0.7.10}/src/mkdocs_to_confluence/transforms/mermaid.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|