diff --git a/docs/features/admonition/callout.md b/docs/features/admonition/callout.md
index 544086f..28adb45 100644
--- a/docs/features/admonition/callout.md
+++ b/docs/features/admonition/callout.md
@@ -70,6 +70,57 @@ See [Demo](demo) for more examples
> [!faq]- Are callouts foldable?
> Yes! In a foldable callout, the contents are hidden when the callout is collapsed.
```
+
+## Nested
+
+### obsidian callout
+
+```tabs
+---tab obsidian markdown
+~~~
+> [!note] some note
+> some content before nested notes
+>
+> > [!note] nested note with no content
+>
+> > [!note] another nested note with some another content
+> > some another content
+>
+> some content after nested notes
+~~~
+---tab obsidian rendered
+![[images/callout_3.png]]
+```
+
+### mkdocs-material admonition
+
+```tabs
+---tab mkdocs-material markdown
+~~~
+!!! note "some note"
+
+ some content before nested notes
+
+ !!! note "nested note with no content"
+
+ !!! note "another nested note with some another content"
+
+ some another content
+
+ some content after nested notes
+~~~
+---tab mkdocs-material rendered
+> [!note] some note
+> some content before nested notes
+>
+> > [!note] nested note with no content
+>
+> > [!note] another nested note with some another content
+> > some another content
+>
+> some content after nested notes
+```
+
## 💡 Notes
common types that `obsidian callout` and `mkdocs-material admonition` support are
@@ -96,16 +147,12 @@ common types that `obsidian callout`, `mkdocs-material admonition` and even `Git
## Implementation details and Warning
> [!warning] implementation limitation
-> 1. Nested callout or admonition is not suppoerted
->
-> 2. Unlike actual obsidian callout, It requires more precise syntax.
-> there sholud be only zero or one space before and after first `>` character
-> and no space before the rest of `>` characters and one space after it.
+> Unlike actual obsidian callout, It requires more precise syntax.
+> there sholud be only zero or one space before and after first `>` character
+> and no space before the rest of `>` characters and one space after it.
>
> recommended format is as below
> ```text
> > [!info]
> > copy me
> ```
-
-
diff --git a/obsidian_support/conversion/abstract_conversion.py b/obsidian_support/conversion/abstract_conversion.py
index ec7e02a..6da77d9 100644
--- a/obsidian_support/conversion/abstract_conversion.py
+++ b/obsidian_support/conversion/abstract_conversion.py
@@ -33,14 +33,14 @@ def obsidian_regex_groups(self):
return list(self.obsidian_regex_pattern.groupindex.keys())
@abstractmethod
- def convert(self, syntax_groups: SyntaxGroup, page: Page) -> str:
+ def convert(self, syntax_groups: SyntaxGroup, page: Page, depth: int) -> str:
pass
"""
A template method that applies conversion for every regex matches
"""
- def markdown_convert(self, markdown: str, page: Page) -> str:
+ def markdown_convert(self, markdown: str, page: Page, depth: int = 0) -> str:
converted_markdown = ""
index = 0
excluded_indices = get_exclude_indices(markdown)
@@ -56,7 +56,7 @@ def markdown_convert(self, markdown: str, page: Page) -> str:
syntax_groups = list(map(lambda group: obsidian_syntax.group(group), self.obsidian_regex_groups))
- mkdocs_syntax = self.convert(syntax_groups, page)
+ mkdocs_syntax = self.convert(syntax_groups, page, depth)
converted_markdown += markdown[index:start]
converted_markdown += mkdocs_syntax
index = end + 1
diff --git a/obsidian_support/conversion/admonition/admonition_backquotes.py b/obsidian_support/conversion/admonition/admonition_backquotes.py
index 0f79fc2..45e14c9 100644
--- a/obsidian_support/conversion/admonition/admonition_backquotes.py
+++ b/obsidian_support/conversion/admonition/admonition_backquotes.py
@@ -46,7 +46,7 @@ def obsidian_regex_pattern(self):
""", flags=re.VERBOSE)
@override
- def convert(self, syntax_groups: SyntaxGroup, page: Page) -> str:
+ def convert(self, syntax_groups: SyntaxGroup, page: Page, depth: int) -> str:
return self._create_admonition(*syntax_groups)
def _create_admonition(self, place, ad_type: str, title: str, collapse: str, contents: str) -> str:
diff --git a/obsidian_support/conversion/admonition/admonition_callout.py b/obsidian_support/conversion/admonition/admonition_callout.py
index 0156224..4c8defe 100644
--- a/obsidian_support/conversion/admonition/admonition_callout.py
+++ b/obsidian_support/conversion/admonition/admonition_callout.py
@@ -39,10 +39,11 @@ def obsidian_regex_pattern(self):
""", flags=re.VERBOSE)
@override
- def convert(self, syntax_groups: SyntaxGroup, page: Page) -> str:
- return self._create_admonition(*syntax_groups)
+ def convert(self, syntax_groups: SyntaxGroup, page: Page, depth: int) -> str:
+ return self._create_admonition(page, depth, *syntax_groups)
- def _create_admonition(self, place, ad_type: str, collapse: str, title: str, contents: str) -> str:
+ def _create_admonition(self, page: Page, depth: int, place: str, ad_type: str, collapse: str, title: str,
+ contents: str) -> str:
contents = contents.replace("\n> ", "\n ")
contents = contents.replace("\n > ", "\n ")
contents = contents.replace("\n>", "\n ")
@@ -60,5 +61,18 @@ def _create_admonition(self, place, ad_type: str, collapse: str, title: str, con
else:
collapse = "!!! "
- admonition = place + collapse + ad_type + title + "\n" + contents
+ de_indented_contents = self._de_indent(contents)
+ contents = self.markdown_convert(de_indented_contents, page, depth)
+ re_indented_contents = self._indent(depth + 1, contents)
+
+ admonition = place + collapse + ad_type + title + "\n" + re_indented_contents
return admonition
+
+ def _de_indent(self, contents: str) -> str:
+ contents = contents.replace("\n ", "\n")
+ return contents
+
+ def _indent(self, depth: int, contents: str) -> str:
+ indent = " " * 4 * depth
+ contents = contents.replace("\n", "\n" + indent)
+ return contents
diff --git a/obsidian_support/conversion/comment/comment.py b/obsidian_support/conversion/comment/comment.py
index f9995ed..2c5e901 100644
--- a/obsidian_support/conversion/comment/comment.py
+++ b/obsidian_support/conversion/comment/comment.py
@@ -24,7 +24,7 @@ def obsidian_regex_pattern(self):
return re.compile(r"%%(?P[\S\s]*?)%%")
@override
- def convert(self, syntax_groups: SyntaxGroup, page: Page) -> str:
+ def convert(self, syntax_groups: SyntaxGroup, page: Page, depth: int) -> str:
return self._convert_comment(*syntax_groups)
def _convert_comment(self, comment):
diff --git a/obsidian_support/conversion/image_link/image_internal_link.py b/obsidian_support/conversion/image_link/image_internal_link.py
index 1e0e04a..90f5d34 100644
--- a/obsidian_support/conversion/image_link/image_internal_link.py
+++ b/obsidian_support/conversion/image_link/image_internal_link.py
@@ -25,7 +25,7 @@ def obsidian_regex_pattern(self):
return re.compile(r"!\[\[(?P[^|^\]]+)(?P|.+)?]]")
@override
- def convert(self, syntax_groups: SyntaxGroup, page: Page) -> str:
+ def convert(self, syntax_groups: SyntaxGroup, page: Page, depth: int) -> str:
return self._convert_image_internal_link(*syntax_groups)
def _convert_image_internal_link(self, image_path: str, tags: str) -> str:
diff --git a/obsidian_support/conversion/image_link/image_web_link.py b/obsidian_support/conversion/image_link/image_web_link.py
index babdbee..a789089 100644
--- a/obsidian_support/conversion/image_link/image_web_link.py
+++ b/obsidian_support/conversion/image_link/image_web_link.py
@@ -27,7 +27,7 @@ def obsidian_regex_pattern(self):
return re.compile(r"!\[(?P(?!\\).*)]\((?Phttps?://.*)\)")
@override
- def convert(self, syntax_groups: SyntaxGroup, page: Page) -> str:
+ def convert(self, syntax_groups: SyntaxGroup, page: Page, depth: int) -> str:
return self._convert_image_link(*syntax_groups)
def _convert_image_link(self, tags: str, image_path: str) -> str:
diff --git a/obsidian_support/conversion/pdf/pdf.py b/obsidian_support/conversion/pdf/pdf.py
index 7026d70..bd300da 100644
--- a/obsidian_support/conversion/pdf/pdf.py
+++ b/obsidian_support/conversion/pdf/pdf.py
@@ -21,7 +21,7 @@ def obsidian_regex_pattern(self):
return re.compile(r"!\[\[(?P[^|^\]]+\.pdf)(?P#height=\d+)?]]")
@override
- def convert(self, syntax_groups: SyntaxGroup, page: Page) -> str:
+ def convert(self, syntax_groups: SyntaxGroup, page: Page, depth: int) -> str:
base_path = page.canonical_url[:-len(page.url)]
return self._convert_tags(base_path, *syntax_groups)
diff --git a/obsidian_support/conversion/tabs/tabs_backquotes.py b/obsidian_support/conversion/tabs/tabs_backquotes.py
index 44cd4fa..bb7e7c2 100644
--- a/obsidian_support/conversion/tabs/tabs_backquotes.py
+++ b/obsidian_support/conversion/tabs/tabs_backquotes.py
@@ -20,7 +20,7 @@ def obsidian_regex_pattern(self):
""", flags=re.VERBOSE)
@override
- def convert(self, syntax_groups: SyntaxGroup, page: Page) -> str:
+ def convert(self, syntax_groups: SyntaxGroup, page: Page, depth: int) -> str:
return self._create_content_tabs(*syntax_groups)
def _create_content_tabs(self, place, tabs) -> str:
diff --git a/obsidian_support/conversion/tabs/tabs_tilde_block.py b/obsidian_support/conversion/tabs/tabs_tilde_block.py
index 074b18b..d434a10 100644
--- a/obsidian_support/conversion/tabs/tabs_tilde_block.py
+++ b/obsidian_support/conversion/tabs/tabs_tilde_block.py
@@ -20,7 +20,7 @@ def obsidian_regex_pattern(self):
""", flags=re.VERBOSE)
@override
- def convert(self, syntax_groups: SyntaxGroup, page: Page) -> str:
+ def convert(self, syntax_groups: SyntaxGroup, page: Page, depth: int) -> str:
return self._create_content_tabs(*syntax_groups)
def _create_content_tabs(self, place, tabs) -> str:
diff --git a/obsidian_support/conversion/tags/tags.py b/obsidian_support/conversion/tags/tags.py
index b77f608..e3bd856 100644
--- a/obsidian_support/conversion/tags/tags.py
+++ b/obsidian_support/conversion/tags/tags.py
@@ -20,7 +20,7 @@ def obsidian_regex_pattern(self):
return re.compile(r"(?[\w\-_\/]+)(?![^\[\(]*[\]\)])")
@override
- def convert(self, syntax_groups: SyntaxGroup, page: Page) -> str:
+ def convert(self, syntax_groups: SyntaxGroup, page: Page, depth: int) -> str:
return self._convert_tags(*syntax_groups)
def _convert_tags(self, tags: str) -> str:
diff --git a/test/conversion/admonition/test_admonition_callout.py b/test/conversion/admonition/test_admonition_callout.py
index 17635d4..1be24f2 100644
--- a/test/conversion/admonition/test_admonition_callout.py
+++ b/test/conversion/admonition/test_admonition_callout.py
@@ -5,7 +5,7 @@
from obsidian_support.conversion.admonition.admonition_callout import AdmonitionCalloutConversion
-def test_admonition_backquotes_conversion():
+def test_admonition_backquotes_conversion_1():
# given
conversion = AdmonitionCalloutConversion()
markdown = cleandoc("""
@@ -22,3 +22,38 @@ def test_admonition_backquotes_conversion():
some content
"""))
+
+
+def test_admonition_backquotes_conversion_2():
+ # given
+ conversion = AdmonitionCalloutConversion()
+ markdown = cleandoc("""
+ > [!note] some note
+ > some content before nested notes
+ >
+ > > [!note] nested note with no content
+ >
+ > > [!note] another nested note with some another content
+ > > some another content
+ >
+ > some content after nested notes
+ """)
+
+ # when
+ converted = conversion.markdown_convert(markdown, None)
+
+ # then
+ assert_that(converted).is_equal_to(cleandoc("""
+ !!! note "some note"
+
+ some content before nested notes
+
+ !!! note "nested note with no content"
+
+
+ !!! note "another nested note with some another content"
+
+ some another content
+
+ some content after nested notes
+ """))