Repository: mondeja/mkdocs-include-markdown-plugin
Pull request #241 (2024-11-21 04:21:07)

Allow to customize directive names


Patch:
diff --git a/README.md b/README.md
index 726c1cb..0182ab2 100644
--- a/README.md
+++ b/README.md
@@ -109,6 +109,20 @@ You can include it in the installation of the plugin adding the `cache` extra:
 mkdocs-include-markdown-plugin[cache]
 ```
 
+<!-- mdpo-disable-next-line -->
+
+#### `directives`
+
+Customize the names of the directives.
+
+```yaml
+plugins:
+  - include-markdown:
+      directives:
+        include-markdown: include-md
+        include: replace
+```
+
 ### Reference
 
 This plugin provides two directives, one to include Markdown files and another
diff --git a/locale/es/README.md b/locale/es/README.md
index b167809..d46a887 100644
--- a/locale/es/README.md
+++ b/locale/es/README.md
@@ -96,6 +96,18 @@ Puedes incluirla en la instalación del plugin añadiendo el extra `cache`:
 mkdocs-include-markdown-plugin[cache]
 ```
 
+#### `directives`
+
+Personaliza los nombres de las directivas.
+
+```yaml
+plugins:
+  - include-markdown:
+      directives:
+        include-markdown: include-md
+        include: replace
+```
+
 ### Referencia
 
 Este plugin provee dos directivas, una para incluir archivos Markdown y otra para
diff --git a/locale/es/README.md.po b/locale/es/README.md.po
index df818d5..16bcc87 100644
--- a/locale/es/README.md.po
+++ b/locale/es/README.md.po
@@ -451,3 +451,6 @@ msgid ""
 "[downloads-link]: https://pepy.tech/project/mkdocs-include-markdown-plugin"
 msgstr ""
 "[downloads-link]: https://pepy.tech/project/mkdocs-include-markdown-plugin"
+
+msgid "Customize the names of the directives."
+msgstr "Personaliza los nombres de las directivas."
diff --git a/locale/fr/README.md b/locale/fr/README.md
index 2671139..3768766 100644
--- a/locale/fr/README.md
+++ b/locale/fr/README.md
@@ -96,6 +96,18 @@ supplément `cache`:
 mkdocs-include-markdown-plugin[cache]
 ```
 
+#### `directives`
+
+Personnaliser les noms des directives.
+
+```yaml
+plugins:
+  - include-markdown:
+      directives:
+        include-markdown: include-md
+        include: replace
+```
+
 ### Référence
 
 Ce plugin fournit deux directives, une pour inclure des fichiers Markdown et une
diff --git a/locale/fr/README.md.po b/locale/fr/README.md.po
index 17cbe55..be54f39 100644
--- a/locale/fr/README.md.po
+++ b/locale/fr/README.md.po
@@ -450,3 +450,6 @@ msgid ""
 "[downloads-link]: https://pepy.tech/project/mkdocs-include-markdown-plugin"
 msgstr ""
 "[downloads-link]: https://pepy.tech/project/mkdocs-include-markdown-plugin"
+
+msgid "Customize the names of the directives."
+msgstr "Personnaliser les noms des directives."
diff --git a/pyproject.toml b/pyproject.toml
index 581d709..2293a2d 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
 [project]
 name = "mkdocs-include-markdown-plugin"
-version = "7.0.1"
+version = "7.1.0"
 description = "Mkdocs Markdown includer plugin."
 readme = "README.md"
 license = "Apache-2.0"
diff --git a/src/mkdocs_include_markdown_plugin/config.py b/src/mkdocs_include_markdown_plugin/config.py
index da8563b..7792b65 100644
--- a/src/mkdocs_include_markdown_plugin/config.py
+++ b/src/mkdocs_include_markdown_plugin/config.py
@@ -25,3 +25,11 @@ class PluginConfig(Config):  # noqa: D101
     exclude = ListOfItems(MkType(str), default=[])
     cache = MkType(int, default=0)
     recursive = MkType(bool, default=True)
+    directives = MkType(
+        dict,
+        default={
+            '__default': '',
+            'include': 'include',
+            'include-markdown': 'include-markdown',
+        },
+    )
diff --git a/src/mkdocs_include_markdown_plugin/directive.py b/src/mkdocs_include_markdown_plugin/directive.py
index 0e57356..2587ea9 100644
--- a/src/mkdocs_include_markdown_plugin/directive.py
+++ b/src/mkdocs_include_markdown_plugin/directive.py
@@ -2,6 +2,7 @@
 
 from __future__ import annotations
 
+import functools
 import os
 import re
 import stat
@@ -74,11 +75,13 @@ TRUE_FALSE_BOOL_STR = {
 }
 
 
+@functools.lru_cache
 def arg(arg: str) -> re.Pattern[str]:
     """Return a compiled regexp to match a boolean argument."""
     return re.compile(rf'{arg}=([{RE_ESCAPED_PUNCTUATION}\w]*)')
 
 
+@functools.lru_cache
 def str_arg(arg: str) -> re.Pattern[str]:
     """Return a compiled regexp to match a string argument."""
     return re.compile(
@@ -88,21 +91,23 @@ def str_arg(arg: str) -> re.Pattern[str]:
 
 
 ARGUMENT_REGEXES = {
-    'start': str_arg('start'),
-    'end': str_arg('end'),
-    'exclude': str_arg('exclude'),
-    'encoding': str_arg('encoding'),
+    'start': functools.partial(str_arg, 'start'),
+    'end': functools.partial(str_arg, 'end'),
+    'exclude': functools.partial(str_arg, 'exclude'),
+    'encoding': functools.partial(str_arg, 'encoding'),
 
     # bool
-    'comments': arg('comments'),
-    'preserve-includer-indent': arg('preserve-includer-indent'),
-    'dedent': arg('dedent'),
-    'trailing-newlines': arg('trailing-newlines'),
-    'rewrite-relative-urls': arg('rewrite-relative-urls'),
-    'recursive': arg('recursive'),
+    'comments': functools.partial(arg, 'comments'),
+    'preserve-includer-indent': functools.partial(
+        arg, 'preserve-includer-indent',
+    ),
+    'dedent': functools.partial(arg, 'dedent'),
+    'trailing-newlines': functools.partial(arg, 'trailing-newlines'),
+    'rewrite-relative-urls': functools.partial(arg, 'rewrite-relative-urls'),
+    'recursive': functools.partial(arg, 'recursive'),
 
     # int
-    'heading-offset': arg('heading-offset'),
+    'heading-offset': functools.partial(arg, 'heading-offset'),
 }
 
 INCLUDE_DIRECTIVE_ARGS = {
@@ -111,8 +116,6 @@ INCLUDE_DIRECTIVE_ARGS = {
     )
 }
 
-INCLUDE_MARKDOWN_DIRECTIVE_ARGS = set(ARGUMENT_REGEXES)
-
 WARN_INVALID_DIRECTIVE_ARGS_REGEX = re.compile(
     rf'[\w-]*=[{RE_ESCAPED_PUNCTUATION}\w]*',
 )
@@ -128,12 +131,11 @@ def warn_invalid_directive_arguments(
     """Warns about the invalid arguments passed to a directive."""
     valid_args = (
         INCLUDE_DIRECTIVE_ARGS if directive == 'include'
-        else INCLUDE_MARKDOWN_DIRECTIVE_ARGS
+        else set(ARGUMENT_REGEXES)
     )
-    for arg_match in WARN_INVALID_DIRECTIVE_ARGS_REGEX.finditer(
+    for arg_value in WARN_INVALID_DIRECTIVE_ARGS_REGEX.findall(
         arguments_string,
     ):
-        arg_value = arg_match.group()
         if arg_value.split('=', 1)[0] not in valid_args:
             location = process.file_lineno_message(
                 page_src_path, docs_dir, directive_lineno(),
@@ -173,7 +175,7 @@ def parse_string_argument(match: re.Match[str]) -> str | None:
 
 
 def create_include_tag(
-        opening_tag: str, closing_tag: str, tag: str = 'include',
+        opening_tag: str, closing_tag: str, tag: str,
 ) -> re.Pattern[str]:
     """Create a regex pattern to match an inclusion tag directive.
 
@@ -200,7 +202,7 @@ def parse_bool_options(
     for option_name in option_names:
         bool_options[option_name] = DirectiveBoolArgument(
             value=defaults[option_name],  # type: ignore
-            regex=ARGUMENT_REGEXES[option_name],
+            regex=ARGUMENT_REGEXES[option_name](),
         )
 
     for arg_name, arg in bool_options.items():
diff --git a/src/mkdocs_include_markdown_plugin/event.py b/src/mkdocs_include_markdown_plugin/event.py
index f63fd26..58eb317 100644
--- a/src/mkdocs_include_markdown_plugin/event.py
+++ b/src/mkdocs_include_markdown_plugin/event.py
@@ -18,6 +18,7 @@ from mkdocs_include_markdown_plugin.cache import Cache
 from mkdocs_include_markdown_plugin.directive import (
     ARGUMENT_REGEXES,
     GLOB_FLAGS,
+    create_include_tag,
     parse_bool_options,
     parse_filename_argument,
     parse_string_argument,
@@ -133,7 +134,7 @@ def get_file_content(  # noqa: PLR0913, PLR0915
             docs_dir,
         )
 
-        exclude_match = ARGUMENT_REGEXES['exclude'].search(arguments_string)
+        exclude_match = ARGUMENT_REGEXES['exclude']().search(arguments_string)
         ignore_paths = [*settings_ignore_paths]
         if exclude_match is not None:
             exclude_string = parse_string_argument(exclude_match)
@@ -186,7 +187,7 @@ def get_file_content(  # noqa: PLR0913, PLR0915
                 f' Possible values are true or false.',
             )
 
-        start_match = ARGUMENT_REGEXES['start'].search(arguments_string)
+        start_match = ARGUMENT_REGEXES['start']().search(arguments_string)
         if start_match:
             start = parse_string_argument(start_match)
             if start is None:
@@ -200,7 +201,7 @@ def get_file_content(  # noqa: PLR0913, PLR0915
         else:
             start = defaults['start']
 
-        end_match = ARGUMENT_REGEXES['end'].search(arguments_string)
+        end_match = ARGUMENT_REGEXES['end']().search(arguments_string)
         if end_match:
             end = parse_string_argument(end_match)
             if end is None:
@@ -214,7 +215,8 @@ def get_file_content(  # noqa: PLR0913, PLR0915
         else:
             end = defaults['end']
 
-        encoding_match = ARGUMENT_REGEXES['encoding'].search(arguments_string)
+        encoding_match = ARGUMENT_REGEXES['encoding']().search(
+            arguments_string)
         if encoding_match:
             encoding = parse_string_argument(encoding_match)
             if encoding is None:
@@ -344,7 +346,7 @@ def get_file_content(  # noqa: PLR0913, PLR0915
             docs_dir,
         )
 
-        exclude_match = ARGUMENT_REGEXES['exclude'].search(arguments_string)
+        exclude_match = ARGUMENT_REGEXES['exclude']().search(arguments_string)
         ignore_paths = [*settings_ignore_paths]
         if exclude_match is not None:
             exclude_string = parse_string_argument(exclude_match)
@@ -400,7 +402,7 @@ def get_file_content(  # noqa: PLR0913, PLR0915
             )
 
         # start and end arguments
-        start_match = ARGUMENT_REGEXES['start'].search(arguments_string)
+        start_match = ARGUMENT_REGEXES['start']().search(arguments_string)
         if start_match:
             start = parse_string_argument(start_match)
             if start is None:
@@ -414,7 +416,7 @@ def get_file_content(  # noqa: PLR0913, PLR0915
         else:
             start = defaults['start']
 
-        end_match = ARGUMENT_REGEXES['end'].search(arguments_string)
+        end_match = ARGUMENT_REGEXES['end']().search(arguments_string)
         if end_match:
             end = parse_string_argument(end_match)
             if end is None:
@@ -428,7 +430,8 @@ def get_file_content(  # noqa: PLR0913, PLR0915
         else:
             end = defaults['end']
 
-        encoding_match = ARGUMENT_REGEXES['encoding'].search(arguments_string)
+        encoding_match = ARGUMENT_REGEXES['encoding']().search(
+            arguments_string)
         if encoding_match:
             encoding = parse_string_argument(encoding_match)
             if encoding is None:
@@ -443,7 +446,7 @@ def get_file_content(  # noqa: PLR0913, PLR0915
             encoding = defaults['encoding']
 
         # heading offset
-        offset_match = ARGUMENT_REGEXES['heading-offset'].search(
+        offset_match = ARGUMENT_REGEXES['heading-offset']().search(
             arguments_string,
         )
         if offset_match:
@@ -633,8 +636,16 @@ def on_page_markdown(
         page.file.abs_src_path,
         docs_dir,
         {
-            'include': plugin._include_tag(),
-            'include-markdown': plugin._include_markdown_tag(),
+            'include': create_include_tag(
+                config.opening_tag,
+                config.closing_tag,
+                config.directives.get('include', 'include'),
+            ),
+            'include-markdown': create_include_tag(
+                config.opening_tag,
+                config.closing_tag,
+                config.directives.get('include-markdown', 'include-markdown'),
+            ),
         },
         {
             'encoding': config.encoding,
diff --git a/src/mkdocs_include_markdown_plugin/plugin.py b/src/mkdocs_include_markdown_plugin/plugin.py
index 9c8dd5c..602cc0c 100644
--- a/src/mkdocs_include_markdown_plugin/plugin.py
+++ b/src/mkdocs_include_markdown_plugin/plugin.py
@@ -11,7 +11,6 @@ from mkdocs.plugins import BasePlugin, event_priority
 
 
 if TYPE_CHECKING:  # pragma: no cover
-    import re
 
     from mkdocs.config.defaults import MkDocsConfig
     from mkdocs.livereload import LiveReloadServer
@@ -20,7 +19,6 @@ if TYPE_CHECKING:  # pragma: no cover
 
 from mkdocs_include_markdown_plugin.cache import Cache, initialize_cache
 from mkdocs_include_markdown_plugin.config import PluginConfig
-from mkdocs_include_markdown_plugin.directive import create_include_tag
 from mkdocs_include_markdown_plugin.event import (
     on_page_markdown as _on_page_markdown,
 )
@@ -44,20 +42,18 @@ class IncludeMarkdownPlugin(BasePlugin[PluginConfig]):
             cache.clean()
             self._cache = cache
 
-        return config
-
-    def _include_tag(self) -> re.Pattern[str]:
-        return create_include_tag(
-            self.config.opening_tag,
-            self.config.closing_tag,
-        )
+        if '__default' not in self.config.directives:  # pragma: no cover
+            for directive in self.config.directives:
+                if directive not in {
+                    'include', 'include-markdown',
+                }:
+                    raise PluginError(
+                        f"Invalid directive name '{directive}' at 'directives'"
+                        ' global setting. Valid values are "include" and'
+                        ' "include-markdown".',
+                    )
 
-    def _include_markdown_tag(self) -> re.Pattern[str]:
-        return create_include_tag(
-            self.config.opening_tag,
-            self.config.closing_tag,
-            tag='include-markdown',
-        )
+        return config
 
     @cached_property
     def _files_watcher(self) -> FilesWatcher:


Test patch:
diff --git a/tests/test_unit/test_config.py b/tests/test_unit/test_config.py
index 324e86a..a661226 100644
--- a/tests/test_unit/test_config.py
+++ b/tests/test_unit/test_config.py
@@ -152,6 +152,36 @@ def _run_test(
             {},
             id='custom-comments',
         ),
+
+        # directives
+        pytest.param(
+            '{% foo "{filepath}" %}bar\n',
+            'baz\n',
+            'baz\nbar\n',
+            {'comments': False, 'directives': {'include-markdown': 'foo'}},
+            id='custom-include-markdown-directive',
+        ),
+        pytest.param(
+            '{% my-include "{filepath}" %}bar\n',
+            'baz\n',
+            'baz\nbar\n',
+            {'comments': False, 'directives': {'include': 'my-include'}},
+            id='custom-include-directive',
+        ),
+        pytest.param(
+            '{% foo "{filepath}" %}bar\n{% include-markdown "{filepath}" %}',
+            'baz\n',
+            '{% foo "{filepath}" %}bar\nbaz\n',
+            {'comments': False, 'directives': {'non-existent': 'foo'}},
+            id='default-include-markdown-directive',
+        ),
+        pytest.param(
+            '{% foo "{filepath}" %}bar\n{% include "{filepath}" %}',
+            'baz\n',
+            '{% foo "{filepath}" %}bar\nbaz\n',
+            {'comments': False, 'directives': {'non-existent': 'foo'}},
+            id='default-include-directive',
+        ),
     ),
     indirect=['plugin'],
 )
diff --git a/tests/test_unit/test_exclude.py b/tests/test_unit/test_exclude.py
index f0681d5..edea155 100644
--- a/tests/test_unit/test_exclude.py
+++ b/tests/test_unit/test_exclude.py
@@ -1,4 +1,4 @@
-"""Tests for mkdocs-include-markdown-plugin `exclude` directives argument."""
+"""Tests for mkdocs-include-markdown-plugin `exclude` setting."""
 
 import functools
 import os