diff --git a/README.md b/README.md
index 4db53ef..feff48e 100644
--- a/README.md
+++ b/README.md
@@ -191,6 +191,32 @@ content to include.
- #
**recursive** (_true_): When this option is disabled, included files are not
processed for recursive includes. Possible values are `true` and `false`.
+- #
+ **order** (_'alpha-path'_): Define the order in which multiple files are included
+ when using globs. Possible values are:
+ - A combination of an optional order type and an optional order by separated
+ by a hyphen (`-`), and optionally prefixed by a hyphen (`-`) to indicate
+ ascending order. If an order type or an order by is not specified, the defaults
+ are used. It follows the form:
+ `[-]-` where:
+ - **Order type**:
+ - `'alpha'` (default): Alphabetical order.
+ - `'natural'`: Natural order, so that e.g. `file2.md` comes before `file10.md`.
+ - **Order by**:
+ - `'path'` (default): Order by full file path.
+ - `'name'`: Order by file name only.
+ - `'extension'`: Order by file extension.
+ - A combination of an optional prefix hyphen to denote ascending order and
+ one of the following values in the form `[-]` where `` is one of:
+ - `'size'`: Order by file size.
+ - `'mtime'`: Order by file modification time.
+ - `'ctime'`: Order by file creation time (or the last metadata change time
+ on Unix systems).
+ - `'atime'`: Order by file last access time.
+ - `'random'`: Random order.
+ - `'system'`: Order provided by the operating system. This is the same as not
+ specifying any order and relying on the default order of the filesystem. This
+ may be different between operating systems, so use it with care.
- #
**encoding** (_'utf-8'_): Specify the encoding of the included file.
If not defined `'utf-8'` will be used.
@@ -249,6 +275,7 @@ content to include.
{%
include-markdown "**"
exclude="./{index,LICENSE}.md"
+ order="name"
%}
```
@@ -256,6 +283,13 @@ content to include.
{% include-markdown '/escap\'ed/single-quotes/in/file\'/name.md' %}
```
+```jinja
+{%
+ include-markdown "**"
+ order="-natural-extension"
+%}
+```
+
#### **`include`**
@@ -282,6 +316,32 @@ Includes the content of a file or a group of files.
- #
**recursive** (_true_): When this option is disabled, included files are not
processed for recursive includes. Possible values are `true` and `false`.
+- #
+ **order** (_'alpha-path'_): Define the order in which multiple files are included
+ when using globs. Possible values are:
+ - A combination of an optional order type and an optional order by separated
+ by a hyphen (`-`), and optionally prefixed by a hyphen (`-`) to indicate
+ ascending order. If an order type or an order by is not specified, the defaults
+ are used. It follows the form:
+ `[-]-` where:
+ - **Order type**:
+ - `'alpha'` (default): Alphabetical order.
+ - `'natural'`: Natural order, so that e.g. `file2.md` comes before `file10.md`.
+ - **Order by**:
+ - `'path'` (default): Order by full file path.
+ - `'name'`: Order by file name only.
+ - `'extension'`: Order by file extension.
+ - A combination of an optional prefix hyphen to denote ascending order and
+ one of the following values in the form `[-]` where `` is one of:
+ - `'size'`: Order by file size.
+ - `'mtime'`: Order by file modification time.
+ - `'ctime'`: Order by file creation time (or the last metadata change time
+ on Unix systems).
+ - `'atime'`: Order by file last access time.
+ - `'random'`: Random order.
+ - `'system'`: Order provided by the operating system. This is the same as not
+ specifying any order and relying on the default order of the filesystem. This
+ may be different between operating systems, so use it with care.
- #
**encoding** (_'utf-8'_): Specify the encoding of the included file.
If not defined `'utf-8'` will be used.
@@ -306,6 +366,7 @@ Includes the content of a file or a group of files.
{%
include '**'
exclude='./*.md'
+ order='random'
%}
```
diff --git a/locale/es/README.md b/locale/es/README.md
index d142858..c216fd8 100644
--- a/locale/es/README.md
+++ b/locale/es/README.md
@@ -177,6 +177,35 @@ se encuentran en el contenido a incluir se eliminan. Los valores posibles son
**recursive** (*true*): Cuando esta opción está deshabilitada, los archivos
incluidos no son procesados para incluir de forma recursiva. Los valores
posibles son `true` y `false`.
+- # **order**
+(*'alpha-path'*): Define el orden en el que múltiples archivos son incluidos
+al usar globs. Los valores posibles son:
+ - Una combinación de un tipo de orden opcional y un sujeto de ordenación opcional
+separados por un guion (`-`), y opcionalmente precedidos por un guion (`-`)
+para indicar orden ascendente. Si no se especifica un tipo de orden o un sujeto
+de ordenación, se usan los valores por defecto. Sigue la forma:
+`[-]-` donde:
+ - **Tipo de orden**:
+ - `'alpha'` (por defecto): Orden alfabético.
+ - `'natural'`: Orden natural, de modo que por ejemplo `file2.md` va antes
+`file10.md`.
+ - **Sujeto de ordenación**:
+ - `'path'` (por defecto): Ordena por la ruta completa del archivo.
+ - `'name'`: Ordena sólo por el nombre del archivo.
+ - `'extension'`: Ordena por la extensión del archivo.
+ - Una combinación de un guion opcional al principio para denotar orden ascendente
+y uno de los siguientes valores en la forma `[-]` donde `` es
+uno de los siguientes:
+ - `'size'`: Ordena por el tamaño del archivo.
+ - `'mtime'`: Ordena por la hora de modificación del archivo.
+ - `'ctime'`: Ordena por la hora de creación del archivo (o la última hora de
+cambio de metadatos en sistemas Unix).
+ - `'atime'`: Ordena por la última hora de acceso al archivo.
+ - `'random'`: Orden aleatorio.
+ - `'system'`: Orden proporcionado por el sistema operativo. Esto es lo mismo que
+no especificar ningún orden y confiar en el orden por defecto del sistema de
+archivos. Esto puede ser diferente entre sistemas operativos, así que úsalo con
+precaución.
- #
**encoding** (*'utf-8'*): Especifica la codificación del archivo incluído. Si
no se define, se usará `'utf-8'`.
@@ -237,6 +266,7 @@ especificado. Sólo soporta la sintaxis de encabezado de caracteres de hash
{%
include-markdown "**"
exclude="./{index,LICENSE}.md"
+ order="name"
%}
```
@@ -244,6 +274,13 @@ especificado. Sólo soporta la sintaxis de encabezado de caracteres de hash
{% include-markdown '/escap\'ed/single-quotes/in/file\'/name.md' %}
```
+```jinja
+{%
+ include-markdown "**"
+ order="-natural-extension"
+%}
+```
+
#### **`include`**
Incluye el contenido de un archivo o un grupo de archivos.
@@ -271,6 +308,35 @@ Los valores posibles son `true` y `false`.
(*true*): Cuando esta opción está deshabilitada, los archivos incluidos no son
procesados para incluir de forma recursiva. Los valores posibles son `true` y
`false`.
+- # **order** (*'alpha-path'*):
+Define el orden en el que múltiples archivos son incluidos al usar globs. Los
+posibles valores son:
+ - Una combinación de un tipo de orden opcional y un sujeto de ordenación opcional
+separados por un guion (`-`), y opcionalmente precedidos por un guion (`-`)
+para indicar orden ascendente. Si no se especifica un tipo de orden o un sujeto
+de ordenación, se usan los valores por defecto. Sigue la forma:
+`[-]-` donde:
+ - **Tipo de orden**:
+ - `'alpha'` (por defecto): Orden alfabético.
+ - `'natural'`: Orden natural, de modo que por ejemplo `file2.md` va antes
+`file10.md`.
+ - **Sujeto de ordenación**:
+ - `'path'` (por defecto): Ordena por la ruta completa del archivo.
+ - `'name'`: Ordena sólo por el nombre del archivo.
+ - `'extension'`: Ordena por la extensión del archivo.
+ - Una combinación de un guion opcional al principio para denotar orden ascendente
+y uno de los siguientes valores en la forma `[-]` donde `` es
+uno de los siguientes:
+ - `'size'`: Ordena por el tamaño del archivo.
+ - `'mtime'`: Ordena por la hora de modificación del archivo.
+ - `'ctime'`: Ordena por la hora de creación del archivo (o la última hora de
+cambio de metadatos en sistemas Unix).
+ - `'atime'`: Ordena por la última hora de acceso al archivo.
+ - `'random'`: Orden aleatorio.
+ - `'system'`: Orden proporcionado por el sistema operativo. Esto es lo mismo que
+no especificar ningún orden y confiar en el orden por defecto del sistema de
+archivos. Esto puede ser diferente entre sistemas operativos, así que úsalo con
+precaución.
- # **encoding**
(*'utf-8'*): Especifica la codificación del archivo incluído. Si no se define,
se usará `'utf-8'`.
@@ -295,6 +361,7 @@ se usará `'utf-8'`.
{%
include '**'
exclude='./*.md'
+ order='random'
%}
```
diff --git a/locale/es/README.md.po b/locale/es/README.md.po
index d13ddde..1168523 100644
--- a/locale/es/README.md.po
+++ b/locale/es/README.md.po
@@ -471,3 +471,95 @@ msgid ""
msgstr ""
"Se agregará un archivo *.gitignore* al directorio de caché si no existe para"
" evitar confirmar los archivos de caché."
+
+msgid ""
+"# "
+"**order** (*'alpha-path'*): Define the order in which multiple files are "
+"included when using globs. Possible values are:"
+msgstr ""
+"# "
+"**order** (*'alpha-path'*): Define el orden en el que múltiples archivos son"
+" incluidos al usar globs. Los valores posibles son:"
+
+msgid "**Order type**:"
+msgstr "**Tipo de orden**:"
+
+msgid "`'alpha'` (default): Alphabetical order."
+msgstr "`'alpha'` (por defecto): Orden alfabético."
+
+msgid ""
+"`'natural'`: Natural order, so that e.g. `file2.md` comes before "
+"`file10.md`."
+msgstr ""
+"`'natural'`: Orden natural, de modo que por ejemplo `file2.md` va antes "
+"`file10.md`."
+
+msgid "**Order by**:"
+msgstr "**Sujeto de ordenación**:"
+
+msgid "`'path'` (default): Order by full file path."
+msgstr "`'path'` (por defecto): Ordena por la ruta completa del archivo."
+
+msgid "`'name'`: Order by file name only."
+msgstr "`'name'`: Ordena sólo por el nombre del archivo."
+
+msgid ""
+"A combination of an optional prefix hyphen to denote ascending order and one"
+" of the following values in the form `[-]` where `` is one of:"
+msgstr ""
+"Una combinación de un guion opcional al principio para denotar orden "
+"ascendente y uno de los siguientes valores en la forma `[-]` donde "
+"`` es uno de los siguientes:"
+
+msgid "`'size'`: Order by file size."
+msgstr "`'size'`: Ordena por el tamaño del archivo."
+
+msgid "`'mtime'`: Order by file modification time."
+msgstr "`'mtime'`: Ordena por la hora de modificación del archivo."
+
+msgid ""
+"`'ctime'`: Order by file creation time (or the last metadata change time on "
+"Unix systems)."
+msgstr ""
+"`'ctime'`: Ordena por la hora de creación del archivo (o la última hora de "
+"cambio de metadatos en sistemas Unix)."
+
+msgid "`'atime'`: Order by file last access time."
+msgstr "`'atime'`: Ordena por la última hora de acceso al archivo."
+
+msgid "`'random'`: Random order."
+msgstr "`'random'`: Orden aleatorio."
+
+msgid ""
+"`'system'`: Order provided by the operating system. This is the same as not "
+"specifying any order and relying on the default order of the filesystem. "
+"This may be different between operating systems, so use it with care."
+msgstr ""
+"`'system'`: Orden proporcionado por el sistema operativo. Esto es lo mismo "
+"que no especificar ningún orden y confiar en el orden por defecto del "
+"sistema de archivos. Esto puede ser diferente entre sistemas operativos, así"
+" que úsalo con precaución."
+
+msgid ""
+"# **order** (*'alpha-"
+"path'*): Define the order in which multiple files are included when using "
+"globs. Possible values are:"
+msgstr ""
+"# **order** (*'alpha-"
+"path'*): Define el orden en el que múltiples archivos son incluidos al usar "
+"globs. Los posibles valores son:"
+
+msgid "`'extension'`: Order by file extension."
+msgstr "`'extension'`: Ordena por la extensión del archivo."
+
+msgid ""
+"A combination of an optional order type and an optional order by separated "
+"by a hyphen (`-`), and optionally prefixed by a hyphen (`-`) to indicate "
+"ascending order. If an order type or an order by is not specified, the "
+"defaults are used. It follows the form: `[-]-` where:"
+msgstr ""
+"Una combinación de un tipo de orden opcional y un sujeto de ordenación "
+"opcional separados por un guion (`-`), y opcionalmente precedidos por un "
+"guion (`-`) para indicar orden ascendente. Si no se especifica un tipo de "
+"orden o un sujeto de ordenación, se usan los valores por defecto. Sigue la "
+"forma: `[-]-` donde:"
diff --git a/locale/fr/README.md b/locale/fr/README.md
index 1e611d8..534ff49 100644
--- a/locale/fr/README.md
+++ b/locale/fr/README.md
@@ -178,6 +178,35 @@ trouvées dans le contenu à inclure sont supprimées. Les valeurs possibles son
**recursive** (*true*): Lorsque cette option est désactivée, les fichiers
inclus ne sont pas traités pour des inclusions récursives. Les valeurs possibles
sont `true` et `false`.
+- # **order**
+(*'alpha-path'*): Définit l'ordre dans lequel plusieurs fichiers sont inclus
+lors de l'utilisation de globs. Les possibles valeurs sont:
+ - Une combinaison d'un type de commande optionnel et d'un sujet de commande
+optionnel séparés par un trait d'union (`-`), et éventuellement précédés par
+un trait d'union (`-`) pour indiquer l'ordre ascendant. Si un type d'ordre ou un
+sujet d'ordre n'est pas spécifié, les valeurs par défaut sont utilisées. Il suit
+la forme: `[-]-` où:
+ - **Type d'ordre**:
+ - `'alpha'` (par défaut): Ordre alphabétique.
+ - `'natural'`: Ordre naturel, de sorte que par exemple `file2.md` vient avant
+`file10.md`.
+ - **Sujet de l'ordre**:
+ - `'path'` (par défaut): Ordre par chemin de fichier complet.
+ - `'name'`: Ordre par nom de fichier uniquement.
+ - `'extension'`: Ordre par extension de fichier.
+ - Une combinaison d'un trait d'union préfixe optionnel pour indiquer l'ordre
+ascendant et l'une des valeurs suivantes sous la forme `[-]` où
+`` est l'une de:
+ - `'size'`: Ordre par taille de fichier.
+ - `'mtime'`: Ordre par heure de modification du fichier.
+ - `'ctime'`: Ordre par heure de création du fichier (ou la dernière heure de
+changement de métadonnées sur les systèmes Unix).
+ - `'atime'`: Ordre par dernière heure d'accès au fichier.
+ - `'random'`: Ordre aléatoire.
+ - `'system'`: Ordre fourni par le système d'exploitation. C'est la même chose que
+de ne spécifier aucun ordre et de se fier à l'ordre par défaut du système de
+fichiers. Cela peut être différent entre les systèmes d'exploitation, alors
+utilisez-le avec précaution.
- #
**encoding** (*'utf-8'*): Spécifiez l'encodage du fichier inclus. S'il n'est
pas défini, `'utf-8'` sera utilisé.
@@ -238,6 +267,7 @@ négatives pour supprimer les caractères `#` de tête.
{%
include-markdown "**"
exclude="./{index,LICENSE}.md"
+ order="name"
%}
```
@@ -245,6 +275,13 @@ négatives pour supprimer les caractères `#` de tête.
{% include-markdown '/escap\'ed/single-quotes/in/file\'/name.md' %}
```
+```jinja
+{%
+ include-markdown "**"
+ order="-natural-extension"
+%}
+```
+
#### **`include`**
Inclus le contenu d'un fichier ou d'un groupe de fichiers.
@@ -271,6 +308,35 @@ valeurs possibles sont `true` et `false`.
(*true*): Lorsque cette option est désactivée, les fichiers inclus ne sont pas
traités pour des inclusions récursives. Les valeurs possibles sont `true` et
`false`.
+- # **order** (*'alpha-path'*):
+Définit l'ordre dans lequel plusieurs fichiers sont inclus lors de
+l'utilisation de globs. Les possibles valeurs sont:
+ - Une combinaison d'un type de commande optionnel et d'un sujet de commande
+optionnel séparés par un trait d'union (`-`), et éventuellement précédés par
+un trait d'union (`-`) pour indiquer l'ordre ascendant. Si un type d'ordre ou un
+sujet d'ordre n'est pas spécifié, les valeurs par défaut sont utilisées. Il suit
+la forme: `[-]-` où:
+ - **Type d'ordre**:
+ - `'alpha'` (par défaut): Ordre alphabétique.
+ - `'natural'`: Ordre naturel, de sorte que par exemple `file2.md` vient avant
+`file10.md`.
+ - **Sujet de l'ordre**:
+ - `'path'` (par défaut): Ordre par chemin de fichier complet.
+ - `'name'`: Ordre par nom de fichier uniquement.
+ - `'extension'`: Ordre par extension de fichier.
+ - Une combinaison d'un trait d'union préfixe optionnel pour indiquer l'ordre
+ascendant et l'une des valeurs suivantes sous la forme `[-]` où
+`` est l'une de:
+ - `'size'`: Ordre par taille de fichier.
+ - `'mtime'`: Ordre par heure de modification du fichier.
+ - `'ctime'`: Ordre par heure de création du fichier (ou la dernière heure de
+changement de métadonnées sur les systèmes Unix).
+ - `'atime'`: Ordre par dernière heure d'accès au fichier.
+ - `'random'`: Ordre aléatoire.
+ - `'system'`: Ordre fourni par le système d'exploitation. C'est la même chose que
+de ne spécifier aucun ordre et de se fier à l'ordre par défaut du système de
+fichiers. Cela peut être différent entre les systèmes d'exploitation, alors
+utilisez-le avec précaution.
- # **encoding**
(*'utf-8'*): Spécifiez l'encodage du fichier inclus. S'il n'est pas défini,
`'utf-8'` sera utilisé.
@@ -295,6 +361,7 @@ traités pour des inclusions récursives. Les valeurs possibles sont `true` et
{%
include '**'
exclude='./*.md'
+ order='random'
%}
```
diff --git a/locale/fr/README.md.po b/locale/fr/README.md.po
index 1b2f8e6..27a73f7 100644
--- a/locale/fr/README.md.po
+++ b/locale/fr/README.md.po
@@ -470,3 +470,95 @@ msgid ""
msgstr ""
"Un fichier *.gitignore* sera ajouté au répertoire de cache s'il n'existe pas"
" pour éviter de valider les fichiers de cache."
+
+msgid ""
+"# "
+"**order** (*'alpha-path'*): Define the order in which multiple files are "
+"included when using globs. Possible values are:"
+msgstr ""
+"# "
+"**order** (*'alpha-path'*): Définit l'ordre dans lequel plusieurs fichiers "
+"sont inclus lors de l'utilisation de globs. Les possibles valeurs sont:"
+
+msgid "**Order type**:"
+msgstr "**Type d'ordre**:"
+
+msgid "`'alpha'` (default): Alphabetical order."
+msgstr "`'alpha'` (par défaut): Ordre alphabétique."
+
+msgid ""
+"`'natural'`: Natural order, so that e.g. `file2.md` comes before "
+"`file10.md`."
+msgstr ""
+"`'natural'`: Ordre naturel, de sorte que par exemple `file2.md` vient avant "
+"`file10.md`."
+
+msgid "**Order by**:"
+msgstr "**Sujet de l'ordre**:"
+
+msgid "`'path'` (default): Order by full file path."
+msgstr "`'path'` (par défaut): Ordre par chemin de fichier complet."
+
+msgid "`'name'`: Order by file name only."
+msgstr "`'name'`: Ordre par nom de fichier uniquement."
+
+msgid ""
+"A combination of an optional prefix hyphen to denote ascending order and one"
+" of the following values in the form `[-]` where `` is one of:"
+msgstr ""
+"Une combinaison d'un trait d'union préfixe optionnel pour indiquer l'ordre "
+"ascendant et l'une des valeurs suivantes sous la forme `[-]` où "
+"`` est l'une de:"
+
+msgid "`'size'`: Order by file size."
+msgstr "`'size'`: Ordre par taille de fichier."
+
+msgid "`'mtime'`: Order by file modification time."
+msgstr "`'mtime'`: Ordre par heure de modification du fichier."
+
+msgid ""
+"`'ctime'`: Order by file creation time (or the last metadata change time on "
+"Unix systems)."
+msgstr ""
+"`'ctime'`: Ordre par heure de création du fichier (ou la dernière heure de "
+"changement de métadonnées sur les systèmes Unix)."
+
+msgid "`'atime'`: Order by file last access time."
+msgstr "`'atime'`: Ordre par dernière heure d'accès au fichier."
+
+msgid "`'random'`: Random order."
+msgstr "`'random'`: Ordre aléatoire."
+
+msgid ""
+"`'system'`: Order provided by the operating system. This is the same as not "
+"specifying any order and relying on the default order of the filesystem. "
+"This may be different between operating systems, so use it with care."
+msgstr ""
+"`'system'`: Ordre fourni par le système d'exploitation. C'est la même chose "
+"que de ne spécifier aucun ordre et de se fier à l'ordre par défaut du "
+"système de fichiers. Cela peut être différent entre les systèmes "
+"d'exploitation, alors utilisez-le avec précaution."
+
+msgid ""
+"# **order** (*'alpha-"
+"path'*): Define the order in which multiple files are included when using "
+"globs. Possible values are:"
+msgstr ""
+"# **order** (*'alpha-"
+"path'*): Définit l'ordre dans lequel plusieurs fichiers sont inclus lors de "
+"l'utilisation de globs. Les possibles valeurs sont:"
+
+msgid "`'extension'`: Order by file extension."
+msgstr "`'extension'`: Ordre par extension de fichier."
+
+msgid ""
+"A combination of an optional order type and an optional order by separated "
+"by a hyphen (`-`), and optionally prefixed by a hyphen (`-`) to indicate "
+"ascending order. If an order type or an order by is not specified, the "
+"defaults are used. It follows the form: `[-]-` where:"
+msgstr ""
+"Une combinaison d'un type de commande optionnel et d'un sujet de commande "
+"optionnel séparés par un trait d'union (`-`), et éventuellement précédés par"
+" un trait d'union (`-`) pour indiquer l'ordre ascendant. Si un type d'ordre "
+"ou un sujet d'ordre n'est pas spécifié, les valeurs par défaut sont "
+"utilisées. Il suit la forme: `[-]-` où:"
diff --git a/pyproject.toml b/pyproject.toml
index 001c794..b8dc17c 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "mkdocs-include-markdown-plugin"
-version = "7.1.8"
+version = "7.2.0"
description = "Mkdocs Markdown includer plugin."
readme = "README.md"
license = "Apache-2.0"
@@ -151,7 +151,7 @@ select = [
"COM",
"SLOT",
]
-ignore = ["G004"]
+ignore = ["G004", "E731"]
[tool.ruff.lint.pydocstyle]
convention = "google"
diff --git a/schema.json b/schema.json
index d9d9852..07f7b1b 100644
--- a/schema.json
+++ b/schema.json
@@ -28,6 +28,12 @@
"type": "string",
"default": "utf-8"
},
+ "order": {
+ "markdownDescription": "https://github.com/mondeja/mkdocs-include-markdown-plugin#include_encoding",
+ "type": "string",
+ "default": "alpha-path",
+ "pattern": "^-?(?:(?:alpha|natural)(?:-(?:path|name|extension))?|system|random|size|mtime|ctime|atime)?$"
+ },
"preserve_includer_indent": {
"markdownDescription": "https://github.com/mondeja/mkdocs-include-markdown-plugin#include_preserve-includer-indent",
"type": "boolean",
diff --git a/src/mkdocs_include_markdown_plugin/config.py b/src/mkdocs_include_markdown_plugin/config.py
index 047d1d1..ae1a8d6 100644
--- a/src/mkdocs_include_markdown_plugin/config.py
+++ b/src/mkdocs_include_markdown_plugin/config.py
@@ -20,6 +20,7 @@ class PluginConfig(Config): # noqa: D101
comments = MkType(bool, default=False)
rewrite_relative_urls = MkType(bool, default=True)
heading_offset = MkType(int, default=0)
+ order = MkType(str, default='alpha-path')
start = Optional(MkType(str))
end = Optional(MkType(str))
exclude = ListOfItems(MkType(str), default=[])
diff --git a/src/mkdocs_include_markdown_plugin/directive.py b/src/mkdocs_include_markdown_plugin/directive.py
index f86c7fd..a77505e 100644
--- a/src/mkdocs_include_markdown_plugin/directive.py
+++ b/src/mkdocs_include_markdown_plugin/directive.py
@@ -13,8 +13,15 @@
from mkdocs.exceptions import PluginError
from wcmatch import glob
-from mkdocs_include_markdown_plugin import process
from mkdocs_include_markdown_plugin.logger import logger
+from mkdocs_include_markdown_plugin.process import (
+ file_lineno_message,
+ filter_paths,
+ is_absolute_path,
+ is_relative_path,
+ is_url,
+ sort_paths,
+)
@dataclass
@@ -28,6 +35,7 @@ class DirectiveBoolArgument: # noqa: D101
from typing import Callable, Literal, TypedDict
DirectiveBoolArgumentsDict = dict[str, DirectiveBoolArgument]
+ OrderOption = tuple[bool, str, str]
DefaultValues = TypedDict(
'DefaultValues', {
@@ -41,6 +49,7 @@ class DirectiveBoolArgument: # noqa: D101
'recursive': bool,
'start': str | None,
'end': str | None,
+ 'order': str,
},
)
@@ -92,10 +101,12 @@ def str_arg(arg: str) -> re.Pattern[str]:
ARGUMENT_REGEXES = {
+ # str
'start': functools.partial(str_arg, 'start'),
'end': functools.partial(str_arg, 'end'),
'exclude': functools.partial(str_arg, 'exclude'),
'encoding': functools.partial(str_arg, 'encoding'),
+ 'order': functools.partial(str_arg, 'order'),
# bool
'comments': functools.partial(arg, 'comments'),
@@ -178,7 +189,7 @@ def warn_invalid_directive_arguments(
)
for maybe_arg in _maybe_arguments_iter(arguments_string):
if maybe_arg not in valid_args:
- location = process.file_lineno_message(
+ location = file_lineno_message(
page_src_path, docs_dir, directive_lineno(),
)
logger.warning(
@@ -283,12 +294,13 @@ def resolve_file_paths_to_include( # noqa: PLR0912
includer_page_src_path: str | None,
docs_dir: str,
ignore_paths: list[str],
+ order: str,
) -> tuple[list[str], bool]:
"""Resolve the file paths to include for a directive."""
- if process.is_url(include_string):
+ if is_url(include_string):
return [include_string], True
- if process.is_absolute_path(include_string):
+ if is_absolute_path(include_string):
if os.name == 'nt': # pragma: no cover
# Windows
fpath = os.path.normpath(include_string)
@@ -299,21 +311,25 @@ def resolve_file_paths_to_include( # noqa: PLR0912
if not is_file:
return [], False
- return process.filter_paths(
- [fpath], ignore_paths,
- ), False
+ paths = filter_paths([fpath], ignore_paths)
+ is_url_ = False
+ return sort_paths(paths, parse_order_option(order)), is_url_
try:
is_file = stat.S_ISREG(os.stat(include_string).st_mode)
except (FileNotFoundError, OSError):
is_file = False
- return process.filter_paths(
+ paths = filter_paths(
[include_string] if is_file else glob.iglob(
include_string, flags=GLOB_FLAGS,
),
- ignore_paths), False
+ ignore_paths,
+ )
+ is_url_ = False
+ sort_paths(paths, parse_order_option(order))
+ return paths, is_url_
- if process.is_relative_path(include_string):
+ if is_relative_path(include_string):
if includer_page_src_path is None: # pragma: no cover
raise PluginError(
'Relative paths are not allowed when the includer page'
@@ -338,7 +354,10 @@ def resolve_file_paths_to_include( # noqa: PLR0912
root_dir=root_dir,
):
paths.append(os.path.join(root_dir, fp))
- return process.filter_paths(paths, ignore_paths), False
+ paths = filter_paths(paths, ignore_paths)
+ is_url_ = False
+ sort_paths(paths, parse_order_option(order))
+ return paths, is_url_
# relative to docs_dir
paths = []
@@ -357,7 +376,10 @@ def resolve_file_paths_to_include( # noqa: PLR0912
root_dir=root_dir,
):
paths.append(os.path.join(root_dir, fp))
- return process.filter_paths(paths, ignore_paths), False
+ paths = filter_paths(paths, ignore_paths)
+ is_url_ = False
+ sort_paths(paths, parse_order_option(order))
+ return paths, is_url_
def resolve_file_paths_to_exclude(
@@ -366,10 +388,10 @@ def resolve_file_paths_to_exclude(
docs_dir: str,
) -> list[str]:
"""Resolve the file paths to exclude for a directive."""
- if process.is_absolute_path(exclude_string):
+ if is_absolute_path(exclude_string):
return glob.glob(exclude_string, flags=GLOB_FLAGS)
- if process.is_relative_path(exclude_string):
+ if is_relative_path(exclude_string):
if includer_page_src_path is None: # pragma: no cover
raise PluginError(
'Relative paths are not allowed when the includer page'
@@ -394,3 +416,58 @@ def resolve_file_paths_to_exclude(
flags=GLOB_FLAGS,
root_dir=docs_dir,
)
+
+
+def validate_order_option(
+ order: str,
+ page_src_path: str | None,
+ docs_dir: str,
+ directive_lineno: Callable[[], int],
+ directive: str,
+) -> None:
+ """Validate the 'order' option."""
+ regex = get_order_option_regex()
+ match = regex.match(order)
+ if not match:
+ location = file_lineno_message(
+ page_src_path, docs_dir, directive_lineno(),
+ )
+ raise PluginError(
+ f"Invalid value '{order}' for the 'order' argument in"
+ f" '{directive}' directive at {location}. The argument"
+ " 'order' must be a string that matches the regex"
+ f" '{regex.pattern}'.",
+ )
+
+
+@functools.cache
+def get_order_option_regex() -> re.Pattern[str]:
+ """Return the compiled regex to validate the 'order' option."""
+ return re.compile(
+ r'^-?'
+ r'(?:'
+ r'(?:alpha|natural)?(?:-?(?:path|name|extension))?'
+ r'|system|random|size|mtime|ctime|atime'
+ r')?$',
+ )
+
+
+def parse_order_option(order: str) -> OrderOption:
+ """Parse the 'order' option into a tuple."""
+ ascending = False
+ order_type = 'alpha'
+ order_by = 'path'
+ if order.startswith('-'):
+ ascending = True
+ order = order[1:]
+ order_split = order.split('-', 1)
+ if len(order_split) == 2: # noqa: PLR2004
+ order_type, order_by = order_split
+ elif order_split[0] in (
+ 'alpha', 'random', 'natural', 'system',
+ 'size', 'mtime', 'ctime', 'atime',
+ ):
+ order_type = order_split[0]
+ elif order_split[0] in ('name', 'path', 'extension'):
+ order_by = order_split[0]
+ return ascending, order_type, order_by
diff --git a/src/mkdocs_include_markdown_plugin/event.py b/src/mkdocs_include_markdown_plugin/event.py
index e621ad6..79204fb 100644
--- a/src/mkdocs_include_markdown_plugin/event.py
+++ b/src/mkdocs_include_markdown_plugin/event.py
@@ -24,6 +24,7 @@
parse_string_argument,
resolve_file_paths_to_exclude,
resolve_file_paths_to_include,
+ validate_order_option,
warn_invalid_directive_arguments,
)
from mkdocs_include_markdown_plugin.files_watcher import FilesWatcher
@@ -141,13 +142,42 @@ def found_include_tag( # noqa: PLR0912, PLR0915
):
ignore_paths.append(path)
+ order = defaults['order']
+ if 'order' in used_arguments:
+ order_match = ARGUMENT_REGEXES['order']().search(
+ arguments_string,
+ )
+ order_ = parse_string_argument(order_match)
+ if order_ is None:
+ location = process.file_lineno_message(
+ page_src_path, docs_dir, directive_lineno(),
+ )
+ raise PluginError(
+ "Invalid empty 'order' argument in 'include'"
+ f' directive at {location}',
+ )
+ validate_order_option(
+ order_, page_src_path, docs_dir, directive_lineno, 'include',
+ )
+ order = order_
+
file_paths_to_include, is_url = resolve_file_paths_to_include(
filename,
page_src_path,
docs_dir,
ignore_paths,
+ order,
)
+ if is_url and 'order' in used_arguments: # pragma: no cover
+ location = process.file_lineno_message(
+ page_src_path, docs_dir, directive_lineno(),
+ )
+ logger.warning(
+ f"Ignoring 'order' argument of 'include' directive"
+ f" at {location} because the included path is a URL",
+ )
+
if not file_paths_to_include:
location = process.file_lineno_message(
page_src_path, docs_dir, directive_lineno(),
@@ -353,13 +383,46 @@ def found_include_markdown_tag( # noqa: PLR0912, PLR0915
):
ignore_paths.append(path)
+ order = defaults['order']
+ if 'order' in used_arguments:
+ order_match = ARGUMENT_REGEXES['order']().search(
+ arguments_string,
+ )
+ order_ = parse_string_argument(order_match)
+ if order_ is None:
+ location = process.file_lineno_message(
+ page_src_path, docs_dir, directive_lineno(),
+ )
+ raise PluginError(
+ "Invalid empty 'order' argument in 'include-markdown'"
+ f' directive at {location}',
+ )
+ validate_order_option(
+ order_,
+ page_src_path,
+ docs_dir,
+ directive_lineno,
+ 'include-markdown',
+ )
+ order = order_
+
file_paths_to_include, is_url = resolve_file_paths_to_include(
filename,
page_src_path,
docs_dir,
ignore_paths,
+ order,
)
+ if is_url and 'order' in used_arguments: # pragma: no cover
+ location = process.file_lineno_message(
+ page_src_path, docs_dir, directive_lineno(),
+ )
+ logger.warning(
+ f"Ignoring 'order' argument of 'include-markdown' directive"
+ f" at {location} because the included path is a URL",
+ )
+
if not file_paths_to_include:
location = process.file_lineno_message(
page_src_path, docs_dir, directive_lineno(),
@@ -645,6 +708,7 @@ def on_page_markdown(
'recursive': config.recursive,
'start': config.start,
'end': config.end,
+ 'order': config.order,
},
Settings(
exclude=config.exclude,
diff --git a/src/mkdocs_include_markdown_plugin/plugin.py b/src/mkdocs_include_markdown_plugin/plugin.py
index 948d06d..1e8bcda 100644
--- a/src/mkdocs_include_markdown_plugin/plugin.py
+++ b/src/mkdocs_include_markdown_plugin/plugin.py
@@ -11,7 +11,6 @@
if TYPE_CHECKING: # pragma: no cover
-
from mkdocs.config.defaults import MkDocsConfig
from mkdocs.livereload import LiveReloadServer
from mkdocs.structure.files import Files
@@ -19,6 +18,9 @@
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 (
+ get_order_option_regex,
+)
from mkdocs_include_markdown_plugin.event import (
on_page_markdown as _on_page_markdown,
)
@@ -50,6 +52,15 @@ def on_config(self, config: MkDocsConfig) -> MkDocsConfig:
' "include-markdown".',
)
+ if self.config.order != 'alpha-path':
+ regex = get_order_option_regex()
+ if not regex.match(self.config.order):
+ raise PluginError(
+ f"Invalid value '{self.config.order}' for the 'order'"
+ ' global setting. Order must be a string'
+ f" that matches the regex '{regex.pattern}'.",
+ )
+
return config
@cached_property
diff --git a/src/mkdocs_include_markdown_plugin/process.py b/src/mkdocs_include_markdown_plugin/process.py
index cfa09aa..df502cc 100644
--- a/src/mkdocs_include_markdown_plugin/process.py
+++ b/src/mkdocs_include_markdown_plugin/process.py
@@ -15,6 +15,7 @@
from typing import Any
from mkdocs_include_markdown_plugin.cache import Cache
+ from mkdocs_include_markdown_plugin.directive import OrderOption
# Markdown regular expressions. Taken from the original Markdown.pl by John
@@ -494,12 +495,12 @@ def filter_paths(
Args:
filepaths (list): Set of source paths to filter.
- ignore_paths (list): Paths that must not be included in the response.
+ ignore_paths (list): Paths that are ignored.
Returns:
list: Non filtered paths ordered alphabetically.
"""
- response = []
+ result = []
for filepath in filepaths:
# ignore by filepath
if filepath in ignore_paths:
@@ -514,11 +515,60 @@ def filter_paths(
# ignore if is a directory
try:
if not stat.S_ISDIR(os.stat(filepath).st_mode):
- response.append(filepath)
+ result.append(filepath)
except (FileNotFoundError, OSError): # pragma: no cover
continue
- response.sort()
- return response
+ return result
+
+
+def natural_sort_key(s: str) -> list[Any]:
+ """Key function for natural sorting of strings."""
+ return [int(text) if text.isdigit() else text.lower()
+ for text in re.split(r'(\d+)', s)]
+
+
+def sort_paths(paths: list[str], order: OrderOption) -> list[str]:
+ """Sort a list of paths in-place according to an order option."""
+ ascending, order_type, order_by = order
+
+ if order_type == 'random':
+ import random # noqa: PLC0415
+
+ random.shuffle(paths)
+ return paths
+
+ key = None
+ if order_type == 'alpha':
+ if order_by == 'name':
+ def key(p: str) -> str:
+ return os.path.basename(p)
+ elif order_by == 'extension':
+ def key(p: str) -> str:
+ return os.path.splitext(p)[1]
+ elif order_type == 'natural':
+ if order_by == 'extension':
+ def key(p: str) -> str:
+ return natural_sort_key(os.path.splitext(p)[1]) # type: ignore
+ elif order_by == 'name':
+ def key(p: str) -> str:
+ return natural_sort_key(os.path.basename(p)) # type: ignore
+ else:
+ key = natural_sort_key # type: ignore
+ elif order_type == 'size':
+ def key(p: str) -> int: # type: ignore
+ return os.path.getsize(p)
+ ascending = not ascending # larger files first
+ elif order_type == 'mtime':
+ def key(p: str) -> float: # type: ignore
+ return os.path.getmtime(p)
+ elif order_type == 'ctime':
+ def key(p: str) -> float: # type: ignore
+ return os.path.getctime(p)
+ elif order_type == 'atime':
+ def key(p: str) -> float: # type: ignore
+ return os.path.getatime(p)
+ paths.sort(key=key, reverse=ascending)
+ return paths
def _is_valid_url_scheme_char(c: str) -> bool:
diff --git a/tests/test_integration/test_cache_integration.py b/tests/test_integration/test_cache_integration.py
index 9e4754e..1ac8122 100644
--- a/tests/test_integration/test_cache_integration.py
+++ b/tests/test_integration/test_cache_integration.py
@@ -1,5 +1,4 @@
import os
-from dataclasses import dataclass
import pytest
from mkdocs.exceptions import PluginError
@@ -13,7 +12,7 @@
is_platformdirs_installed,
)
from mkdocs_include_markdown_plugin.event import on_page_markdown
-from testing_helpers import parametrize_directives
+from testing_helpers import FakeConfig, parametrize_directives
@pytest.mark.parametrize(
@@ -82,21 +81,13 @@ def run():
def test_cache_setting_when_not_available_raises_error(monkeypatch):
- @dataclass
- class FakeConfig:
- cache: int
- cache_dir: str
- directives: dict[str, str]
-
monkeypatch.setattr(
mkdocs_include_markdown_plugin.cache,
'is_platformdirs_installed',
lambda: False,
)
plugin = IncludeMarkdownPlugin()
- plugin.config = FakeConfig(
- cache=600, cache_dir='', directives={'__default': ''},
- )
+ plugin.config = FakeConfig(cache=600, cache_dir='')
with pytest.raises(PluginError) as exc:
plugin.on_config({})
assert (
@@ -106,19 +97,11 @@ class FakeConfig:
def test_cache_setting_available_with_cache_dir(monkeypatch):
- @dataclass
- class FakeConfig:
- cache: int
- cache_dir: str
- directives: dict[str, str]
-
monkeypatch.setattr(
mkdocs_include_markdown_plugin.cache,
'is_platformdirs_installed',
lambda: False,
)
plugin = IncludeMarkdownPlugin()
- plugin.config = FakeConfig(
- cache=600, cache_dir='foo', directives={'__default': ''},
- )
+ plugin.config = FakeConfig(cache=600, cache_dir='foo')
plugin.on_config({})
diff --git a/tests/test_integration/test_order_setting.py b/tests/test_integration/test_order_setting.py
new file mode 100644
index 0000000..7564768
--- /dev/null
+++ b/tests/test_integration/test_order_setting.py
@@ -0,0 +1,24 @@
+import pytest
+from mkdocs.exceptions import PluginError
+
+from mkdocs_include_markdown_plugin.directive import get_order_option_regex
+from mkdocs_include_markdown_plugin.plugin import IncludeMarkdownPlugin
+from testing_helpers import FakeConfig
+
+
+def test_invalid_order_setting():
+ plugin = IncludeMarkdownPlugin()
+ plugin.config = FakeConfig(order='invalid-order')
+ with pytest.raises(PluginError) as exc:
+ plugin.on_config({})
+ regex = get_order_option_regex()
+ assert (
+ "Invalid value 'invalid-order' for the 'order' global setting."
+ f" Order must be a string that matches the regex '{regex.pattern}'."
+ ) in str(exc.value)
+
+
+def test_valid_order_setting():
+ plugin = IncludeMarkdownPlugin()
+ plugin.config = FakeConfig(order='alpha-name')
+ assert plugin.on_config({}) is not None
diff --git a/tests/test_unit/test_arguments.py b/tests/test_unit/test_arguments.py
index 821f1d6..fa8a7bd 100644
--- a/tests/test_unit/test_arguments.py
+++ b/tests/test_unit/test_arguments.py
@@ -164,17 +164,19 @@ def test_exclude_double_quote_escapes(
@unix_only
@parametrize_directives
-def test_invalid_exclude_argument(directive, page, tmp_path, caplog, plugin):
- drectory_to_include = tmp_path / 'exclude_double_quote_escapes'
- drectory_to_include.mkdir()
+def test_invalid_empty_exclude_argument(
+ directive, page, tmp_path, caplog, plugin,
+):
+ directory_to_include = tmp_path / 'exclude_double_quote_escapes'
+ directory_to_include.mkdir()
- page_to_include_filepath = drectory_to_include / 'included.md'
+ page_to_include_filepath = directory_to_include / 'included.md'
page_to_include_filepath.write_text('Content that should be included\n')
- page_to_exclude_filepath = drectory_to_include / 'igno"re"d.md'
+ page_to_exclude_filepath = directory_to_include / 'igno"re"d.md'
page_to_exclude_filepath.write_text('Content that should be excluded\n')
- includer_glob = os.path.join(str(drectory_to_include), '*.md')
+ includer_glob = os.path.join(str(directory_to_include), '*.md')
includer_file_content = f'''{{%
{directive} "{includer_glob}"
@@ -197,7 +199,9 @@ def test_invalid_exclude_argument(directive, page, tmp_path, caplog, plugin):
@parametrize_directives
-def test_empty_encoding_argument(directive, page, tmp_path, plugin, caplog):
+def test_invalid_empty_encoding_argument(
+ directive, page, tmp_path, plugin, caplog,
+):
page_to_include_filepath = tmp_path / 'included.md'
page_to_include_filepath.write_text('Content to include')
diff --git a/tests/test_unit/test_config.py b/tests/test_unit/test_config.py
index 68ccb00..5e272a9 100644
--- a/tests/test_unit/test_config.py
+++ b/tests/test_unit/test_config.py
@@ -227,7 +227,7 @@ def test_config_options(
),
indirect=['plugin'],
)
-def test_config_encoding_option(
+def test_config_encoding_option_on_unix(
includer_schema,
content_to_include,
expected_result,
diff --git a/tests/test_unit/test_order.py b/tests/test_unit/test_order.py
new file mode 100644
index 0000000..a6215c6
--- /dev/null
+++ b/tests/test_unit/test_order.py
@@ -0,0 +1,638 @@
+import os
+import time
+
+import pytest
+from mkdocs.exceptions import PluginError
+
+from mkdocs_include_markdown_plugin.directive import get_order_option_regex
+from mkdocs_include_markdown_plugin.event import on_page_markdown
+from testing_helpers import parametrize_directives, unix_only, windows_only
+
+
+@parametrize_directives
+def test_default_order(directive, page, tmp_path, plugin):
+ page_to_include_file = tmp_path / 'hincluded.md'
+ page_to_include_file.write_text('hincluded.md\n')
+ page_to_include_file = tmp_path / 'included.md'
+ page_to_include_file.write_text('included.md\n')
+
+ assert on_page_markdown(
+ f'''{{% {directive} "*.md" %}}''',
+ page(tmp_path / 'includer.md'),
+ tmp_path,
+ plugin,
+ ) == 'hincluded.md\nincluded.md\n'
+
+
+@parametrize_directives
+def test_default_reverse_order(directive, page, tmp_path, plugin):
+ page_to_include_file = tmp_path / 'hincluded.md'
+ page_to_include_file.write_text('hincluded.md\n')
+ page_to_include_file = tmp_path / 'included.md'
+ page_to_include_file.write_text('included.md\n')
+
+ assert on_page_markdown(
+ f'''{{%
+{directive} "*.md"
+order='-'
+%}}''',
+ page(tmp_path / 'includer.md'),
+ tmp_path,
+ plugin,
+ ) == 'included.md\nhincluded.md\n'
+
+
+@parametrize_directives
+def test_natural_order(directive, page, tmp_path, plugin):
+ page_to_include_file = tmp_path / 'file1.md'
+ page_to_include_file.write_text('file1.md\n')
+ page_to_include_file = tmp_path / 'file10.md'
+ page_to_include_file.write_text('file10.md\n')
+ page_to_include_file = tmp_path / 'file2.md'
+ page_to_include_file.write_text('file2.md\n')
+
+ assert on_page_markdown(
+ f'''{{%
+{directive} "*.md"
+order='natural'
+%}}''',
+ page(tmp_path / 'includer.md'),
+ tmp_path,
+ plugin,
+ ) == 'file1.md\nfile2.md\nfile10.md\n'
+
+
+@parametrize_directives
+def test_natural_reverse_order(directive, page, tmp_path, plugin):
+ page_to_include_file = tmp_path / 'file1.md'
+ page_to_include_file.write_text('file1.md\n')
+ page_to_include_file = tmp_path / 'file10.md'
+ page_to_include_file.write_text('file10.md\n')
+ page_to_include_file = tmp_path / 'file2.md'
+ page_to_include_file.write_text('file2.md\n')
+
+ assert on_page_markdown(
+ f'''{{%
+{directive} "*.md"
+order='-natural'
+%}}''',
+ page(tmp_path / 'includer.md'),
+ tmp_path,
+ plugin,
+ ) == 'file10.md\nfile2.md\nfile1.md\n'
+
+
+@parametrize_directives
+@pytest.mark.parametrize('order_value', ('alpha', 'path'))
+def test_alpha_order(directive, order_value, page, tmp_path, plugin):
+ f1 = tmp_path / 'a.md'
+ f1.write_text('a.md\n')
+ f2 = tmp_path / 'c.md'
+ f2.write_text('c.md\n')
+ f3 = tmp_path / 'b.md'
+ f3.write_text('b.md\n')
+
+ assert on_page_markdown(
+ f'''{{%
+{directive} "*.md"
+order='{order_value}'
+%}}''',
+ page(tmp_path / 'includer.md'),
+ tmp_path,
+ plugin,
+ ) == 'a.md\nb.md\nc.md\n'
+
+
+@parametrize_directives
+def test_alpha_reverse_order(directive, page, tmp_path, plugin):
+ f1 = tmp_path / 'a.md'
+ f1.write_text('a.md\n')
+ f2 = tmp_path / 'c.md'
+ f2.write_text('c.md\n')
+ f3 = tmp_path / 'b.md'
+ f3.write_text('b.md\n')
+
+ assert on_page_markdown(
+ f'''{{%
+{directive} "*.md"
+order='-alpha'
+%}}''',
+ page(tmp_path / 'includer.md'),
+ tmp_path,
+ plugin,
+ ) == 'c.md\nb.md\na.md\n'
+
+
+@parametrize_directives
+def test_random_order(directive, page, tmp_path, plugin):
+ f1 = tmp_path / 'a.md'
+ f1.write_text('a.md\n')
+ f2 = tmp_path / 'b.md'
+ f2.write_text('b.md\n')
+ f3 = tmp_path / 'c.md'
+ f3.write_text('c.md\n')
+
+ result = on_page_markdown(
+ f'''{{%
+{directive} "*.md"
+order='random'
+%}}''',
+ page(tmp_path / 'includer.md'),
+ tmp_path,
+ plugin,
+ ).splitlines()
+
+ assert set(result) == {'a.md', 'b.md', 'c.md'}
+
+
+@parametrize_directives
+def test_system_order(directive, page, tmp_path, plugin):
+ f1 = tmp_path / 'a.md'
+ f1.write_text('a.md\n')
+ f2 = tmp_path / 'b.md'
+ f2.write_text('b.md\n')
+
+ result = on_page_markdown(
+ f'''{{%
+{directive} "*.md"
+order='system'
+%}}''',
+ page(tmp_path / 'includer.md'),
+ tmp_path,
+ plugin,
+ ).splitlines()
+
+ assert set(result) == {'a.md', 'b.md'}
+
+
+@parametrize_directives
+def test_size_order(directive, page, tmp_path, plugin):
+ f1 = tmp_path / 'small.md'
+ small_content = 'a' * 10
+ f1.write_text(small_content)
+ f2 = tmp_path / 'large.md'
+ large_content = 'b' * 100
+ f2.write_text(large_content)
+
+ assert on_page_markdown(
+ f'''{{%
+{directive} "*.md"
+order='size'
+%}}''',
+ page(tmp_path / 'includer.md'),
+ tmp_path,
+ plugin,
+ ) == large_content + small_content
+
+
+@parametrize_directives
+def test_size_reverse_order(directive, page, tmp_path, plugin):
+ f1 = tmp_path / 'small.md'
+ small_content = 'a' * 10
+ f1.write_text(small_content)
+ f2 = tmp_path / 'large.md'
+ large_content = 'b' * 100
+ f2.write_text(large_content)
+
+ assert on_page_markdown(
+ f'''{{%
+{directive} "*.md"
+order='-size'
+%}}''',
+ page(tmp_path / 'includer.md'),
+ tmp_path,
+ plugin,
+ ) == small_content + large_content
+
+
+@parametrize_directives
+def test_mtime_order(directive, page, tmp_path, plugin):
+ f1 = tmp_path / 'older.md'
+ f1.write_text('older.md\n')
+ f2 = tmp_path / 'newer.md'
+ f2.write_text('newer.md\n')
+ now = time.time()
+ os.utime(f1, (now - 10, now - 10))
+ os.utime(f2, (now, now))
+ assert on_page_markdown(
+ f'''{{%
+{directive} "*.md"
+order='mtime'
+%}}''',
+ page(tmp_path / 'includer.md'),
+ tmp_path,
+ plugin,
+ ) == 'older.md\nnewer.md\n'
+
+
+@parametrize_directives
+def test_mtime_reverse_order(directive, page, tmp_path, plugin):
+ f1 = tmp_path / 'older.md'
+ f1.write_text('older.md\n')
+ f2 = tmp_path / 'newer.md'
+ f2.write_text('newer.md\n')
+ now = time.time()
+ os.utime(f1, (now - 10, now - 10))
+ os.utime(f2, (now, now))
+ assert on_page_markdown(
+ f'''{{%
+{directive} "*.md"
+order='-mtime'
+%}}''',
+ page(tmp_path / 'includer.md'),
+ tmp_path,
+ plugin,
+ ) == 'newer.md\nolder.md\n'
+
+
+@unix_only
+@parametrize_directives
+def test_atime_order(directive, page, tmp_path, plugin):
+ f1 = tmp_path / 'older.md'
+ f1.write_text('older.md\n')
+ f2 = tmp_path / 'newer.md'
+ f2.write_text('newer.md\n')
+ os.utime(f1, (time.time() - 10, time.time() - 10))
+
+ assert on_page_markdown(
+ f'''{{%
+{directive} "*.md"
+order='atime'
+%}}''',
+ page(tmp_path / 'includer.md'),
+ tmp_path,
+ plugin,
+ ) == 'older.md\nnewer.md\n'
+
+
+@unix_only
+@parametrize_directives
+def test_atime_reverse_order(directive, page, tmp_path, plugin):
+ f1 = tmp_path / 'older.md'
+ f1.write_text('older.md\n')
+ f2 = tmp_path / 'newer.md'
+ f2.write_text('newer.md\n')
+ os.utime(f1, (time.time() - 10, time.time() - 10))
+
+ assert on_page_markdown(
+ f'''{{%
+{directive} "*.md"
+order='-atime'
+%}}''',
+ page(tmp_path / 'includer.md'),
+ tmp_path,
+ plugin,
+ ) == 'newer.md\nolder.md\n'
+
+
+@parametrize_directives
+def test_alpha_order_by_path(directive, page, tmp_path, plugin):
+ f1 = tmp_path / 'sub/a.md'
+ f1.parent.mkdir(parents=True)
+ f1.write_text('sub/a.md\n')
+ f2 = tmp_path / 'b.md'
+ f2.write_text('b.md\n')
+
+ assert on_page_markdown(
+ f'''{{%
+{directive} "./**/*.md"
+order='alpha-path'
+%}}''',
+ page(tmp_path / 'includer.md'),
+ tmp_path,
+ plugin,
+ ) == 'b.md\nsub/a.md\n'
+
+
+@parametrize_directives
+@pytest.mark.parametrize('order_value', ('alpha-extension', 'extension'))
+def test_alpha_order_by_extension(
+ directive, order_value, page, tmp_path, plugin,
+):
+ f1 = tmp_path / 'file2.md'
+ f1.write_text('file2.md\n')
+ f2 = tmp_path / 'file1.txt'
+ f2.write_text('file1.txt\n')
+
+ assert on_page_markdown(
+ f'''{{%
+{directive} "*"
+order='{order_value}'
+%}}''',
+ page(tmp_path / 'includer.md'),
+ tmp_path,
+ plugin,
+ ) == 'file2.md\nfile1.txt\n'
+
+
+@parametrize_directives
+def test_natural_order_by_path(directive, page, tmp_path, plugin):
+ f1 = tmp_path / 'sub/file2.md'
+ f1.parent.mkdir(parents=True)
+ f1.write_text('sub/file2.md\n')
+ f2 = tmp_path / 'file10.md'
+ f2.write_text('file10.md\n')
+ f3 = tmp_path / 'file1.md'
+ f3.write_text('file1.md\n')
+
+ assert on_page_markdown(
+ f'''{{%
+{directive} "./**/*.md"
+order='natural-path'
+%}}''',
+ page(tmp_path / 'includer.md'),
+ tmp_path,
+ plugin,
+ ) == 'file1.md\nfile10.md\nsub/file2.md\n'
+
+
+@parametrize_directives
+def test_natural_order_by_name(directive, page, tmp_path, plugin):
+ f1 = tmp_path / 'sub/file2.md'
+ f1.parent.mkdir(parents=True)
+ f1.write_text('sub/file2.md\n')
+ f2 = tmp_path / 'file10.md'
+ f2.write_text('file10.md\n')
+ f3 = tmp_path / 'file1.md'
+ f3.write_text('file1.md\n')
+
+ assert on_page_markdown(
+ f'''{{%
+{directive} "./**/*.md"
+order='natural-name'
+%}}''',
+ page(tmp_path / 'includer.md'),
+ tmp_path,
+ plugin,
+ ) == 'file1.md\nsub/file2.md\nfile10.md\n'
+
+
+@parametrize_directives
+def test_natural_order_by_extension(directive, page, tmp_path, plugin):
+ f1 = tmp_path / 'file2.md'
+ f1.write_text('file2.md\n')
+ f2 = tmp_path / 'file10.txt'
+ f2.write_text('file10.txt\n')
+ f3 = tmp_path / 'file1.md'
+ f3.write_text('file1.md\n')
+
+ assert on_page_markdown(
+ f'''{{%
+{directive} "*"
+order='natural-extension'
+%}}''',
+ page(tmp_path / 'includer.md'),
+ tmp_path,
+ plugin,
+ ) == 'file1.md\nfile2.md\nfile10.txt\n'
+
+
+@parametrize_directives
+@pytest.mark.parametrize('order_value', ('alpha-name', 'name'))
+def test_alpha_order_by_name(directive, order_value, page, tmp_path, plugin):
+ f1 = tmp_path / 'a.md'
+ f1.write_text('a.md\n')
+ f2 = tmp_path / 'c.md'
+ f2.write_text('c.md\n')
+ f3 = tmp_path / 'b.md'
+ f3.write_text('b.md\n')
+
+ assert on_page_markdown(
+ f'''{{%
+{directive} "*.md"
+order='{order_value}'
+%}}''',
+ page(tmp_path / 'includer.md'),
+ tmp_path,
+ plugin,
+ ) == 'a.md\nb.md\nc.md\n'
+
+
+@parametrize_directives
+def test_alpha_order_by_name_reverse(directive, page, tmp_path, plugin):
+ f1 = tmp_path / 'a.md'
+ f1.write_text('a.md\n')
+ f2 = tmp_path / 'c.md'
+ f2.write_text('c.md\n')
+ f3 = tmp_path / 'b.md'
+ f3.write_text('b.md\n')
+
+ assert on_page_markdown(
+ f'''{{%
+{directive} "*.md"
+order='-alpha-name'
+%}}''',
+ page(tmp_path / 'includer.md'),
+ tmp_path,
+ plugin,
+ ) == 'c.md\nb.md\na.md\n'
+
+
+@parametrize_directives
+def test_alpha_order_by_path_reverse(directive, page, tmp_path, plugin):
+ f1 = tmp_path / 'sub/a.md'
+ f1.parent.mkdir(parents=True)
+ f1.write_text('sub/a.md\n')
+ f2 = tmp_path / 'b.md'
+ f2.write_text('b.md\n')
+
+ assert on_page_markdown(
+ f'''{{%
+{directive} "./**/*.md"
+order='-alpha-path'
+%}}''',
+ page(tmp_path / 'includer.md'),
+ tmp_path,
+ plugin,
+ ) == 'sub/a.md\nb.md\n'
+
+
+@parametrize_directives
+def test_alpha_order_by_extension_reverse(directive, page, tmp_path, plugin):
+ f1 = tmp_path / 'file2.md'
+ f1.write_text('file2.md\n')
+ f2 = tmp_path / 'file1.txt'
+ f2.write_text('file1.txt\n')
+
+ assert on_page_markdown(
+ f'''{{%
+{directive} "*"
+order='-alpha-extension'
+%}}''',
+ page(tmp_path / 'includer.md'),
+ tmp_path,
+ plugin,
+ ) == 'file1.txt\nfile2.md\n'
+
+
+@parametrize_directives
+def test_natural_order_by_name_reverse(directive, page, tmp_path, plugin):
+ f1 = tmp_path / 'sub/file2.md'
+ f1.parent.mkdir(parents=True)
+ f1.write_text('sub/file2.md\n')
+ f2 = tmp_path / 'file10.md'
+ f2.write_text('file10.md\n')
+ f3 = tmp_path / 'file1.md'
+ f3.write_text('file1.md\n')
+
+ assert on_page_markdown(
+ f'''{{%
+{directive} "./**/*.md"
+order='-natural-name'
+%}}''',
+ page(tmp_path / 'includer.md'),
+ tmp_path,
+ plugin,
+ ) == 'file10.md\nsub/file2.md\nfile1.md\n'
+
+
+@parametrize_directives
+def test_natural_order_by_path_reverse(directive, page, tmp_path, plugin):
+ f1 = tmp_path / 'sub/file2.md'
+ f1.parent.mkdir(parents=True)
+ f1.write_text('sub/file2.md\n')
+ f2 = tmp_path / 'file10.md'
+ f2.write_text('file10.md\n')
+ f3 = tmp_path / 'file1.md'
+ f3.write_text('file1.md\n')
+
+ assert on_page_markdown(
+ f'''{{%
+{directive} "./**/*.md"
+order='-natural-path'
+%}}''',
+ page(tmp_path / 'includer.md'),
+ tmp_path,
+ plugin,
+ ) == 'sub/file2.md\nfile10.md\nfile1.md\n'
+
+
+@parametrize_directives
+def test_natural_order_by_extension_reverse(directive, page, tmp_path, plugin):
+ f1 = tmp_path / 'file2.md'
+ f1.write_text('file2.md\n')
+ f2 = tmp_path / 'file10.txt'
+ f2.write_text('file10.txt\n')
+ f3 = tmp_path / 'file1.md'
+ f3.write_text('file1.md\n')
+
+ assert on_page_markdown(
+ f'''{{%
+{directive} "*"
+order='-natural-extension'
+%}}''',
+ page(tmp_path / 'includer.md'),
+ tmp_path,
+ plugin,
+ ) == 'file10.txt\nfile1.md\nfile2.md\n'
+
+
+@parametrize_directives
+@windows_only
+def test_ctime_order(directive, page, tmp_path, plugin):
+ f1 = tmp_path / 'file2.md'
+ f1.write_text('file2.md\n')
+ time.sleep(1)
+ f2 = tmp_path / 'file1.md'
+ f2.write_text('file1.md\n')
+
+ assert on_page_markdown(
+ f'''{{%
+{directive} "*.md"
+order='ctime'
+%}}''',
+ page(tmp_path / 'includer.md'),
+ tmp_path,
+ plugin,
+ ) == 'file2.md\nfile1.md\n'
+
+
+@windows_only
+@parametrize_directives
+def test_ctime_reverse_order(directive, page, tmp_path, plugin):
+ f1 = tmp_path / 'file2.md'
+ f1.write_text('file2.md\n')
+ time.sleep(1)
+ f2 = tmp_path / 'file1.md'
+ f2.write_text('file1.md\n')
+
+ assert on_page_markdown(
+ f'''{{%
+{directive} "*.md"
+order='-ctime'
+%}}''',
+ page(tmp_path / 'includer.md'),
+ tmp_path,
+ plugin,
+ ) == 'file1.md\nfile2.md\n'
+
+
+@unix_only
+@parametrize_directives
+def test_invalid_empty_order_argument(
+ directive, page, tmp_path, caplog, plugin,
+):
+ directory_to_include = tmp_path / 'empty_order_argument'
+ directory_to_include.mkdir()
+
+ page_to_include_filepath = directory_to_include / 'included.md'
+ page_to_include_filepath.write_text('Content that should be included\n')
+
+ page_to_exclude_filepath = directory_to_include / 'igno"re"d.md'
+ page_to_exclude_filepath.write_text('Content that should be excluded\n')
+
+ includer_glob = os.path.join(str(directory_to_include), '*.md')
+
+ includer_file_content = f'''{{%
+ {directive} "{includer_glob}"
+ order=
+%}}'''
+
+ with pytest.raises(PluginError) as exc:
+ on_page_markdown(
+ includer_file_content,
+ page(tmp_path / 'includer.md'),
+ tmp_path,
+ plugin,
+ )
+
+ assert len(caplog.records) == 0
+ assert str(exc.value) == (
+ f"Invalid empty 'order' argument in '{directive}' directive"
+ ' at includer.md:1'
+ )
+
+
+@parametrize_directives
+def test_invalid_order_argument(directive, page, tmp_path, caplog, plugin):
+ directory_to_include = tmp_path / 'invalid_order_argument'
+ directory_to_include.mkdir()
+
+ page_to_include_filepath = directory_to_include / 'included.md'
+ page_to_include_filepath.write_text('Content that should be included\n')
+
+ page_to_exclude_filepath = directory_to_include / 'ignored.md'
+ page_to_exclude_filepath.write_text('Content that should be excluded\n')
+
+ includer_glob = os.path.join(str(directory_to_include), '*.md')
+
+ includer_file_content = f'''{{%
+ {directive} "{includer_glob}"
+ order='invalid-order'
+%}}'''
+
+ with pytest.raises(PluginError) as exc:
+ on_page_markdown(
+ includer_file_content,
+ page(tmp_path / 'includer.md'),
+ tmp_path,
+ plugin,
+ )
+
+ assert len(caplog.records) == 0
+ regex = get_order_option_regex()
+ assert str(exc.value) == (
+ f"Invalid value 'invalid-order' for the 'order' argument in"
+ f" '{directive}' directive at includer.md:1. The argument"
+ f" 'order' must be a string that matches the regex '{regex.pattern}'."
+ )
diff --git a/tests/testing_helpers.py b/tests/testing_helpers.py
index de0a666..bc8ab04 100644
--- a/tests/testing_helpers.py
+++ b/tests/testing_helpers.py
@@ -1,8 +1,11 @@
import os
import sys
+from dataclasses import dataclass, field
import pytest
+from mkdocs_include_markdown_plugin.config import PluginConfig
+
parametrize_directives = pytest.mark.parametrize(
'directive',
@@ -15,4 +18,19 @@
reason='Test only supported on Unix systems',
)
+windows_only = pytest.mark.skipif(
+ not sys.platform.startswith('win'),
+ reason='Test only supported on Windows systems',
+)
+
rootdir = os.path.join(os.path.dirname(__file__), '..')
+
+
+@dataclass
+class FakeConfig:
+ cache: int = PluginConfig.cache.default
+ cache_dir: str = PluginConfig.cache_dir.default
+ directives: dict[str, str] = field(
+ default_factory=lambda: PluginConfig.directives.default,
+ )
+ order: str = PluginConfig.order.default