mkdocs-include-markdown-plugin 7.1.8__py3-none-any.whl → 7.2.1__py3-none-any.whl

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.
@@ -20,6 +20,7 @@ class PluginConfig(Config): # noqa: D101
20
20
  comments = MkType(bool, default=False)
21
21
  rewrite_relative_urls = MkType(bool, default=True)
22
22
  heading_offset = MkType(int, default=0)
23
+ order = MkType(str, default='alpha-path')
23
24
  start = Optional(MkType(str))
24
25
  end = Optional(MkType(str))
25
26
  exclude = ListOfItems(MkType(str), default=[])
@@ -13,8 +13,15 @@ from typing import TYPE_CHECKING
13
13
  from mkdocs.exceptions import PluginError
14
14
  from wcmatch import glob
15
15
 
16
- from mkdocs_include_markdown_plugin import process
17
16
  from mkdocs_include_markdown_plugin.logger import logger
17
+ from mkdocs_include_markdown_plugin.process import (
18
+ file_lineno_message,
19
+ filter_paths,
20
+ is_absolute_path,
21
+ is_relative_path,
22
+ is_url,
23
+ sort_paths,
24
+ )
18
25
 
19
26
 
20
27
  @dataclass
@@ -28,6 +35,7 @@ if TYPE_CHECKING: # pragma: no cover
28
35
  from typing import Callable, Literal, TypedDict
29
36
 
30
37
  DirectiveBoolArgumentsDict = dict[str, DirectiveBoolArgument]
38
+ OrderOption = tuple[bool, str, str]
31
39
 
32
40
  DefaultValues = TypedDict(
33
41
  'DefaultValues', {
@@ -41,6 +49,7 @@ if TYPE_CHECKING: # pragma: no cover
41
49
  'recursive': bool,
42
50
  'start': str | None,
43
51
  'end': str | None,
52
+ 'order': str,
44
53
  },
45
54
  )
46
55
 
@@ -92,10 +101,12 @@ def str_arg(arg: str) -> re.Pattern[str]:
92
101
 
93
102
 
94
103
  ARGUMENT_REGEXES = {
104
+ # str
95
105
  'start': functools.partial(str_arg, 'start'),
96
106
  'end': functools.partial(str_arg, 'end'),
97
107
  'exclude': functools.partial(str_arg, 'exclude'),
98
108
  'encoding': functools.partial(str_arg, 'encoding'),
109
+ 'order': functools.partial(str_arg, 'order'),
99
110
 
100
111
  # bool
101
112
  'comments': functools.partial(arg, 'comments'),
@@ -129,7 +140,7 @@ def _maybe_arguments_iter(arguments_string: str) -> Iterable[str]:
129
140
  inside_string = False
130
141
  escaping = False
131
142
  opening_argument = False # whether we are at the beginning of an argument
132
- current_value = ''
143
+ current_value: list[str] = []
133
144
 
134
145
  for c in arguments_string:
135
146
  if inside_string:
@@ -142,24 +153,25 @@ def _maybe_arguments_iter(arguments_string: str) -> Iterable[str]:
142
153
  else:
143
154
  escaping = False
144
155
  elif c == '=':
156
+ current_value_str = ''.join(current_value)
145
157
  new_current_value = ''
146
- for ch in reversed(current_value):
158
+ for ch in reversed(current_value_str):
147
159
  if ch in string.whitespace:
148
- current_value = new_current_value[::-1]
160
+ current_value_str = new_current_value[::-1]
149
161
  break
150
162
  new_current_value += ch
151
- yield current_value
152
- current_value = ''
163
+ yield current_value_str
164
+ current_value = []
153
165
  opening_argument = True
154
166
  elif opening_argument:
155
167
  opening_argument = False
156
168
  if c in ('"', "'"):
157
169
  current_string_opening = c
158
170
  inside_string = True
159
- current_value += c
160
- current_value += c
171
+ current_value.append(c)
172
+ current_value.append(c)
161
173
  else:
162
- current_value += c
174
+ current_value.append(c)
163
175
 
164
176
 
165
177
  def warn_invalid_directive_arguments(
@@ -178,7 +190,7 @@ def warn_invalid_directive_arguments(
178
190
  )
179
191
  for maybe_arg in _maybe_arguments_iter(arguments_string):
180
192
  if maybe_arg not in valid_args:
181
- location = process.file_lineno_message(
193
+ location = file_lineno_message(
182
194
  page_src_path, docs_dir, directive_lineno(),
183
195
  )
184
196
  logger.warning(
@@ -283,12 +295,13 @@ def resolve_file_paths_to_include( # noqa: PLR0912
283
295
  includer_page_src_path: str | None,
284
296
  docs_dir: str,
285
297
  ignore_paths: list[str],
298
+ order: str,
286
299
  ) -> tuple[list[str], bool]:
287
300
  """Resolve the file paths to include for a directive."""
288
- if process.is_url(include_string):
301
+ if is_url(include_string):
289
302
  return [include_string], True
290
303
 
291
- if process.is_absolute_path(include_string):
304
+ if is_absolute_path(include_string):
292
305
  if os.name == 'nt': # pragma: no cover
293
306
  # Windows
294
307
  fpath = os.path.normpath(include_string)
@@ -299,21 +312,25 @@ def resolve_file_paths_to_include( # noqa: PLR0912
299
312
  if not is_file:
300
313
  return [], False
301
314
 
302
- return process.filter_paths(
303
- [fpath], ignore_paths,
304
- ), False
315
+ paths = filter_paths([fpath], ignore_paths)
316
+ is_url_ = False
317
+ return sort_paths(paths, parse_order_option(order)), is_url_
305
318
 
306
319
  try:
307
320
  is_file = stat.S_ISREG(os.stat(include_string).st_mode)
308
321
  except (FileNotFoundError, OSError):
309
322
  is_file = False
310
- return process.filter_paths(
323
+ paths = filter_paths(
311
324
  [include_string] if is_file else glob.iglob(
312
325
  include_string, flags=GLOB_FLAGS,
313
326
  ),
314
- ignore_paths), False
327
+ ignore_paths,
328
+ )
329
+ is_url_ = False
330
+ sort_paths(paths, parse_order_option(order))
331
+ return paths, is_url_
315
332
 
316
- if process.is_relative_path(include_string):
333
+ if is_relative_path(include_string):
317
334
  if includer_page_src_path is None: # pragma: no cover
318
335
  raise PluginError(
319
336
  'Relative paths are not allowed when the includer page'
@@ -338,7 +355,10 @@ def resolve_file_paths_to_include( # noqa: PLR0912
338
355
  root_dir=root_dir,
339
356
  ):
340
357
  paths.append(os.path.join(root_dir, fp))
341
- return process.filter_paths(paths, ignore_paths), False
358
+ paths = filter_paths(paths, ignore_paths)
359
+ is_url_ = False
360
+ sort_paths(paths, parse_order_option(order))
361
+ return paths, is_url_
342
362
 
343
363
  # relative to docs_dir
344
364
  paths = []
@@ -357,7 +377,10 @@ def resolve_file_paths_to_include( # noqa: PLR0912
357
377
  root_dir=root_dir,
358
378
  ):
359
379
  paths.append(os.path.join(root_dir, fp))
360
- return process.filter_paths(paths, ignore_paths), False
380
+ paths = filter_paths(paths, ignore_paths)
381
+ is_url_ = False
382
+ sort_paths(paths, parse_order_option(order))
383
+ return paths, is_url_
361
384
 
362
385
 
363
386
  def resolve_file_paths_to_exclude(
@@ -366,10 +389,10 @@ def resolve_file_paths_to_exclude(
366
389
  docs_dir: str,
367
390
  ) -> list[str]:
368
391
  """Resolve the file paths to exclude for a directive."""
369
- if process.is_absolute_path(exclude_string):
392
+ if is_absolute_path(exclude_string):
370
393
  return glob.glob(exclude_string, flags=GLOB_FLAGS)
371
394
 
372
- if process.is_relative_path(exclude_string):
395
+ if is_relative_path(exclude_string):
373
396
  if includer_page_src_path is None: # pragma: no cover
374
397
  raise PluginError(
375
398
  'Relative paths are not allowed when the includer page'
@@ -394,3 +417,58 @@ def resolve_file_paths_to_exclude(
394
417
  flags=GLOB_FLAGS,
395
418
  root_dir=docs_dir,
396
419
  )
420
+
421
+
422
+ def validate_order_option(
423
+ order: str,
424
+ page_src_path: str | None,
425
+ docs_dir: str,
426
+ directive_lineno: Callable[[], int],
427
+ directive: str,
428
+ ) -> None:
429
+ """Validate the 'order' option."""
430
+ regex = get_order_option_regex()
431
+ match = regex.match(order)
432
+ if not match:
433
+ location = file_lineno_message(
434
+ page_src_path, docs_dir, directive_lineno(),
435
+ )
436
+ raise PluginError(
437
+ f"Invalid value '{order}' for the 'order' argument in"
438
+ f" '{directive}' directive at {location}. The argument"
439
+ " 'order' must be a string that matches the regex"
440
+ f" '{regex.pattern}'.",
441
+ )
442
+
443
+
444
+ @functools.cache
445
+ def get_order_option_regex() -> re.Pattern[str]:
446
+ """Return the compiled regex to validate the 'order' option."""
447
+ return re.compile(
448
+ r'^-?'
449
+ r'(?:'
450
+ r'(?:alpha|natural)?(?:-?(?:path|name|extension))?'
451
+ r'|system|random|size|mtime|ctime|atime'
452
+ r')?$',
453
+ )
454
+
455
+
456
+ def parse_order_option(order: str) -> OrderOption:
457
+ """Parse the 'order' option into a tuple."""
458
+ ascending = False
459
+ order_type = 'alpha'
460
+ order_by = 'path'
461
+ if order.startswith('-'):
462
+ ascending = True
463
+ order = order[1:]
464
+ order_split = order.split('-', 1)
465
+ if len(order_split) == 2: # noqa: PLR2004
466
+ order_type, order_by = order_split
467
+ elif order_split[0] in (
468
+ 'alpha', 'random', 'natural', 'system',
469
+ 'size', 'mtime', 'ctime', 'atime',
470
+ ):
471
+ order_type = order_split[0]
472
+ elif order_split[0] in ('name', 'path', 'extension'):
473
+ order_by = order_split[0]
474
+ return ascending, order_type, order_by
@@ -24,6 +24,7 @@ from mkdocs_include_markdown_plugin.directive import (
24
24
  parse_string_argument,
25
25
  resolve_file_paths_to_exclude,
26
26
  resolve_file_paths_to_include,
27
+ validate_order_option,
27
28
  warn_invalid_directive_arguments,
28
29
  )
29
30
  from mkdocs_include_markdown_plugin.files_watcher import FilesWatcher
@@ -141,13 +142,42 @@ def get_file_content( # noqa: PLR0913, PLR0915
141
142
  ):
142
143
  ignore_paths.append(path)
143
144
 
145
+ order = defaults['order']
146
+ if 'order' in used_arguments:
147
+ order_match = ARGUMENT_REGEXES['order']().search(
148
+ arguments_string,
149
+ )
150
+ order_ = parse_string_argument(order_match)
151
+ if order_ is None:
152
+ location = process.file_lineno_message(
153
+ page_src_path, docs_dir, directive_lineno(),
154
+ )
155
+ raise PluginError(
156
+ "Invalid empty 'order' argument in 'include'"
157
+ f' directive at {location}',
158
+ )
159
+ validate_order_option(
160
+ order_, page_src_path, docs_dir, directive_lineno, 'include',
161
+ )
162
+ order = order_
163
+
144
164
  file_paths_to_include, is_url = resolve_file_paths_to_include(
145
165
  filename,
146
166
  page_src_path,
147
167
  docs_dir,
148
168
  ignore_paths,
169
+ order,
149
170
  )
150
171
 
172
+ if is_url and 'order' in used_arguments: # pragma: no cover
173
+ location = process.file_lineno_message(
174
+ page_src_path, docs_dir, directive_lineno(),
175
+ )
176
+ logger.warning(
177
+ f"Ignoring 'order' argument of 'include' directive"
178
+ f" at {location} because the included path is a URL",
179
+ )
180
+
151
181
  if not file_paths_to_include:
152
182
  location = process.file_lineno_message(
153
183
  page_src_path, docs_dir, directive_lineno(),
@@ -353,13 +383,46 @@ def get_file_content( # noqa: PLR0913, PLR0915
353
383
  ):
354
384
  ignore_paths.append(path)
355
385
 
386
+ order = defaults['order']
387
+ if 'order' in used_arguments:
388
+ order_match = ARGUMENT_REGEXES['order']().search(
389
+ arguments_string,
390
+ )
391
+ order_ = parse_string_argument(order_match)
392
+ if order_ is None:
393
+ location = process.file_lineno_message(
394
+ page_src_path, docs_dir, directive_lineno(),
395
+ )
396
+ raise PluginError(
397
+ "Invalid empty 'order' argument in 'include-markdown'"
398
+ f' directive at {location}',
399
+ )
400
+ validate_order_option(
401
+ order_,
402
+ page_src_path,
403
+ docs_dir,
404
+ directive_lineno,
405
+ 'include-markdown',
406
+ )
407
+ order = order_
408
+
356
409
  file_paths_to_include, is_url = resolve_file_paths_to_include(
357
410
  filename,
358
411
  page_src_path,
359
412
  docs_dir,
360
413
  ignore_paths,
414
+ order,
361
415
  )
362
416
 
417
+ if is_url and 'order' in used_arguments: # pragma: no cover
418
+ location = process.file_lineno_message(
419
+ page_src_path, docs_dir, directive_lineno(),
420
+ )
421
+ logger.warning(
422
+ f"Ignoring 'order' argument of 'include-markdown' directive"
423
+ f" at {location} because the included path is a URL",
424
+ )
425
+
363
426
  if not file_paths_to_include:
364
427
  location = process.file_lineno_message(
365
428
  page_src_path, docs_dir, directive_lineno(),
@@ -482,7 +545,7 @@ def get_file_content( # noqa: PLR0913, PLR0915
482
545
  # but they have been specified, so the warning(s) must be raised
483
546
  expected_but_any_found = [start is not None, end is not None]
484
547
 
485
- text_to_include = ''
548
+ text_to_include_parts = []
486
549
  for file_path in file_paths_to_include:
487
550
  if process.is_url(filename):
488
551
  new_text_to_include = process.read_url(
@@ -558,11 +621,10 @@ def get_file_content( # noqa: PLR0913, PLR0915
558
621
  new_text_to_include
559
622
  ):
560
623
  lines = new_text_to_include.splitlines(keepends=True)
561
- new_text_to_include = lines[0]
624
+ indented_lines = [lines[0]]
562
625
  for i in range(1, len(lines)):
563
- new_text_to_include += (
564
- filled_includer_indent + lines[i]
565
- )
626
+ indented_lines.append(filled_includer_indent + lines[i])
627
+ new_text_to_include = ''.join(indented_lines)
566
628
 
567
629
  if offset:
568
630
  new_text_to_include = process.increase_headings_offset(
@@ -570,7 +632,9 @@ def get_file_content( # noqa: PLR0913, PLR0915
570
632
  offset=offset + cumulative_heading_offset,
571
633
  )
572
634
 
573
- text_to_include += new_text_to_include
635
+ text_to_include_parts.append(new_text_to_include)
636
+
637
+ text_to_include = ''.join(text_to_include_parts)
574
638
 
575
639
  # warn if expected start or ends haven't been found in included content
576
640
  for i, delimiter_name in enumerate(['start', 'end']):
@@ -645,6 +709,7 @@ def on_page_markdown(
645
709
  'recursive': config.recursive,
646
710
  'start': config.start,
647
711
  'end': config.end,
712
+ 'order': config.order,
648
713
  },
649
714
  Settings(
650
715
  exclude=config.exclude,
@@ -11,7 +11,6 @@ from mkdocs.plugins import BasePlugin, event_priority
11
11
 
12
12
 
13
13
  if TYPE_CHECKING: # pragma: no cover
14
-
15
14
  from mkdocs.config.defaults import MkDocsConfig
16
15
  from mkdocs.livereload import LiveReloadServer
17
16
  from mkdocs.structure.files import Files
@@ -19,6 +18,9 @@ if TYPE_CHECKING: # pragma: no cover
19
18
 
20
19
  from mkdocs_include_markdown_plugin.cache import Cache, initialize_cache
21
20
  from mkdocs_include_markdown_plugin.config import PluginConfig
21
+ from mkdocs_include_markdown_plugin.directive import (
22
+ get_order_option_regex,
23
+ )
22
24
  from mkdocs_include_markdown_plugin.event import (
23
25
  on_page_markdown as _on_page_markdown,
24
26
  )
@@ -50,6 +52,15 @@ class IncludeMarkdownPlugin(BasePlugin[PluginConfig]):
50
52
  ' "include-markdown".',
51
53
  )
52
54
 
55
+ if self.config.order != 'alpha-path':
56
+ regex = get_order_option_regex()
57
+ if not regex.match(self.config.order):
58
+ raise PluginError(
59
+ f"Invalid value '{self.config.order}' for the 'order'"
60
+ ' global setting. Order must be a string'
61
+ f" that matches the regex '{regex.pattern}'.",
62
+ )
63
+
53
64
  return config
54
65
 
55
66
  @cached_property
@@ -15,6 +15,7 @@ if TYPE_CHECKING: # pragma: no cover
15
15
  from typing import Any
16
16
 
17
17
  from mkdocs_include_markdown_plugin.cache import Cache
18
+ from mkdocs_include_markdown_plugin.directive import OrderOption
18
19
 
19
20
 
20
21
  # Markdown regular expressions. Taken from the original Markdown.pl by John
@@ -173,10 +174,12 @@ def transform_p_by_p_skipping_codeblocks( # noqa: PLR0912, PLR0915
173
174
  _maybe_icodeblock_lines: list[str] = []
174
175
  _previous_line_was_empty = False
175
176
 
176
- lines, current_paragraph = ([], '')
177
+ lines = []
178
+ current_paragraph_lines: list[str] = []
177
179
 
178
180
  def process_current_paragraph() -> None:
179
- lines.extend(func(current_paragraph).splitlines(keepends=True))
181
+ lines.extend(func(''.join(current_paragraph_lines),
182
+ ).splitlines(keepends=True))
180
183
 
181
184
  # The next implementation takes into account that indented code
182
185
  # blocks must be surrounded by newlines as per the CommonMark
@@ -190,25 +193,24 @@ def transform_p_by_p_skipping_codeblocks( # noqa: PLR0912, PLR0915
190
193
  if lstripped_line.startswith(('```', '~~~')):
191
194
  _current_fcodeblock_delimiter = lstripped_line[:3]
192
195
  process_current_paragraph()
193
- current_paragraph = ''
196
+ current_paragraph_lines = []
194
197
  lines.append(line)
195
198
  elif line.startswith(' '):
196
199
  if not lstripped_line or _maybe_icodeblock_lines:
197
200
  # maybe enter indented codeblock
198
201
  _maybe_icodeblock_lines.append(line)
199
202
  else:
200
- current_paragraph += line
203
+ current_paragraph_lines.append(line)
201
204
  elif _maybe_icodeblock_lines:
202
205
  process_current_paragraph()
203
- current_paragraph = ''
206
+ current_paragraph_lines = []
204
207
  if not _previous_line_was_empty:
205
208
  # wasn't an indented code block
206
- for line_ in _maybe_icodeblock_lines:
207
- current_paragraph += line_
209
+ current_paragraph_lines.extend(_maybe_icodeblock_lines)
208
210
  _maybe_icodeblock_lines = []
209
- current_paragraph += line
211
+ current_paragraph_lines.append(line)
210
212
  process_current_paragraph()
211
- current_paragraph = ''
213
+ current_paragraph_lines = []
212
214
  else:
213
215
  # exit indented codeblock
214
216
  for line_ in _maybe_icodeblock_lines:
@@ -216,7 +218,7 @@ def transform_p_by_p_skipping_codeblocks( # noqa: PLR0912, PLR0915
216
218
  _maybe_icodeblock_lines = []
217
219
  lines.append(line)
218
220
  else:
219
- current_paragraph += line
221
+ current_paragraph_lines.append(line)
220
222
  _previous_line_was_empty = not lstripped_line
221
223
  else:
222
224
  lines.append(line)
@@ -229,14 +231,13 @@ def transform_p_by_p_skipping_codeblocks( # noqa: PLR0912, PLR0915
229
231
  if not _previous_line_was_empty:
230
232
  # at EOF
231
233
  process_current_paragraph()
232
- current_paragraph = ''
233
- for line_ in _maybe_icodeblock_lines:
234
- current_paragraph += line_
234
+ current_paragraph_lines = []
235
+ current_paragraph_lines.extend(_maybe_icodeblock_lines)
235
236
  process_current_paragraph()
236
- current_paragraph = ''
237
+ current_paragraph_lines = []
237
238
  else:
238
239
  process_current_paragraph()
239
- current_paragraph = ''
240
+ current_paragraph_lines = []
240
241
  for line_ in _maybe_icodeblock_lines:
241
242
  lines.append(line_)
242
243
  else:
@@ -494,12 +495,12 @@ def filter_paths(
494
495
 
495
496
  Args:
496
497
  filepaths (list): Set of source paths to filter.
497
- ignore_paths (list): Paths that must not be included in the response.
498
+ ignore_paths (list): Paths that are ignored.
498
499
 
499
500
  Returns:
500
501
  list: Non filtered paths ordered alphabetically.
501
502
  """
502
- response = []
503
+ result = []
503
504
  for filepath in filepaths:
504
505
  # ignore by filepath
505
506
  if filepath in ignore_paths:
@@ -514,11 +515,60 @@ def filter_paths(
514
515
  # ignore if is a directory
515
516
  try:
516
517
  if not stat.S_ISDIR(os.stat(filepath).st_mode):
517
- response.append(filepath)
518
+ result.append(filepath)
518
519
  except (FileNotFoundError, OSError): # pragma: no cover
519
520
  continue
520
- response.sort()
521
- return response
521
+ return result
522
+
523
+
524
+ def natural_sort_key(s: str) -> list[Any]:
525
+ """Key function for natural sorting of strings."""
526
+ return [int(text) if text.isdigit() else text.lower()
527
+ for text in re.split(r'(\d+)', s)]
528
+
529
+
530
+ def sort_paths(paths: list[str], order: OrderOption) -> list[str]:
531
+ """Sort a list of paths in-place according to an order option."""
532
+ ascending, order_type, order_by = order
533
+
534
+ if order_type == 'random':
535
+ import random # noqa: PLC0415
536
+
537
+ random.shuffle(paths)
538
+ return paths
539
+
540
+ key = None
541
+ if order_type == 'alpha':
542
+ if order_by == 'name':
543
+ def key(p: str) -> str:
544
+ return os.path.basename(p)
545
+ elif order_by == 'extension':
546
+ def key(p: str) -> str:
547
+ return os.path.splitext(p)[1]
548
+ elif order_type == 'natural':
549
+ if order_by == 'extension':
550
+ def key(p: str) -> str:
551
+ return natural_sort_key(os.path.splitext(p)[1]) # type: ignore
552
+ elif order_by == 'name':
553
+ def key(p: str) -> str:
554
+ return natural_sort_key(os.path.basename(p)) # type: ignore
555
+ else:
556
+ key = natural_sort_key # type: ignore
557
+ elif order_type == 'size':
558
+ def key(p: str) -> int: # type: ignore
559
+ return os.path.getsize(p)
560
+ ascending = not ascending # larger files first
561
+ elif order_type == 'mtime':
562
+ def key(p: str) -> float: # type: ignore
563
+ return os.path.getmtime(p)
564
+ elif order_type == 'ctime':
565
+ def key(p: str) -> float: # type: ignore
566
+ return os.path.getctime(p)
567
+ elif order_type == 'atime':
568
+ def key(p: str) -> float: # type: ignore
569
+ return os.path.getatime(p)
570
+ paths.sort(key=key, reverse=ascending)
571
+ return paths
522
572
 
523
573
 
524
574
  def _is_valid_url_scheme_char(c: str) -> bool:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mkdocs-include-markdown-plugin
3
- Version: 7.1.8
3
+ Version: 7.2.1
4
4
  Summary: Mkdocs Markdown includer plugin.
5
5
  Project-URL: Source, https://github.com/mondeja/mkdocs-include-markdown-plugin
6
6
  Project-URL: Documentation, https://github.com/mondeja/mkdocs-include-markdown-plugin#documentation
@@ -181,6 +181,8 @@ plugins:
181
181
  This plugin provides two directives, one to include Markdown files and another
182
182
  to include files of any type.
183
183
 
184
+ #### Common arguments
185
+
184
186
  Paths of included files can be either:
185
187
 
186
188
  - URLs to include remote content.
@@ -199,36 +201,63 @@ single `'` quotes, which can be escaped prepending them a `\` character as
199
201
  The arguments **start** and **end** may contain usual (Python-style) escape
200
202
  sequences like `\n` to match against newlines.
201
203
 
202
- <!-- mdpo-disable-next-line -->
203
-
204
- #### **`include-markdown`**
205
-
206
- Includes Markdown files content, optionally using two delimiters to filter the
207
- content to include.
208
-
209
- - <a name="include-markdown_start" href="#include-markdown_start">#</a>
204
+ - <a name="include_start" href="#include_start">#</a>
210
205
  **start**: Delimiter that marks the beginning of the content to include.
211
- - <a name="include-markdown_end" href="#include-markdown_end">#</a>
206
+ - <a name="include_end" href="#include_end">#</a>
212
207
  **end**: Delimiter that marks the end of the content to include.
213
- - <a name="include-markdown_preserve-includer-indent" href="#include-markdown_preserve-includer-indent">#</a>
208
+ - <a name="include_preserve-includer-indent" href="#include_preserve-includer-indent">#</a>
214
209
  **preserve-includer-indent** (_true_): When this option is enabled (default),
215
210
  every line of the content to include is indented with the same number of
216
211
  spaces used to indent the includer `{% %}` template. Possible values are
217
212
  `true` and `false`.
218
- - <a name="include-markdown_dedent" href="#include-markdown_dedent">#</a>
213
+ - <a name="include_dedent" href="#include_dedent">#</a>
219
214
  **dedent** (_false_): If enabled, the included content will be dedented.
220
- - <a name="include-markdown_exclude" href="#include-markdown_exclude">#</a>
215
+ - <a name="include_exclude" href="#include_exclude">#</a>
221
216
  **exclude**: Specify with a glob which files should be ignored. Only useful
222
217
  when passing globs to include multiple files.
223
- - <a name="include-markdown_trailing-newlines" href="#include-markdown_trailing-newlines">#</a>
218
+ - <a name="include_trailing-newlines" href="#include_trailing-newlines">#</a>
224
219
  **trailing-newlines** (_true_): When this option is disabled, the trailing newlines
225
220
  found in the content to include are stripped. Possible values are `true` and `false`.
226
- - <a name="include-markdown_recursive" href="#include-markdown_recursive">#</a>
221
+ - <a name="include_recursive" href="#include_recursive">#</a>
227
222
  **recursive** (_true_): When this option is disabled, included files are not
228
223
  processed for recursive includes. Possible values are `true` and `false`.
229
- - <a name="include-markdown_encoding" href="#include-markdown_encoding">#</a>
224
+ - <a name="include_order" href="#include_order">#</a>
225
+ **order** (_'alpha-path'_): Define the order in which multiple files are included
226
+ when using globs. Possible values are:
227
+ - A combination of an optional order type and an optional order by separated
228
+ by a hyphen (`-`), and optionally prefixed by a hyphen (`-`) to indicate
229
+ ascending order. If an order type or an order by is not specified, the defaults
230
+ are used. It follows the form:
231
+ `[-]<order_type>-<order_by>` where:
232
+ - **Order type**:
233
+ - `'alpha'` (default): Alphabetical order.
234
+ - `'natural'`: Natural order, so that e.g. `file2.md` comes before `file10.md`.
235
+ - **Order by**:
236
+ - `'path'` (default): Order by full file path.
237
+ - `'name'`: Order by file name only.
238
+ - `'extension'`: Order by file extension.
239
+ - A combination of an optional prefix hyphen to denote ascending order and one
240
+ of the following values in the form `[-]<value>` where `<value>` is one of:
241
+ - `'size'`: Order by file size.
242
+ - `'mtime'`: Order by file modification time.
243
+ - `'ctime'`: Order by file creation time (or the last metadata change time
244
+ on Unix systems).
245
+ - `'atime'`: Order by file last access time.
246
+ - `'system'`: Order provided by the operating system. This is the same as not
247
+ specifying any order and relying on the default order of the filesystem.
248
+ This may be different between operating systems, so use it with care.
249
+ - `'random'`: Random order.
250
+ - <a name="include_encoding" href="#include_encoding">#</a>
230
251
  **encoding** (_'utf-8'_): Specify the encoding of the included file.
231
252
  If not defined `'utf-8'` will be used.
253
+
254
+ <!-- mdpo-disable-next-line -->
255
+
256
+ #### **`include-markdown`**
257
+
258
+ Includes Markdown files content, optionally using two delimiters to filter the
259
+ content to include.
260
+
232
261
  - <a name="include-markdown_rewrite-relative-urls" href="#include-markdown_rewrite-relative-urls">#</a>
233
262
  **rewrite-relative-urls** (_true_): When this option is enabled (default),
234
263
  Markdown links and images in the content that are specified by a relative URL
@@ -284,6 +313,7 @@ content to include.
284
313
  {%
285
314
  include-markdown "**"
286
315
  exclude="./{index,LICENSE}.md"
316
+ order="name"
287
317
  %}
288
318
  ```
289
319
 
@@ -291,36 +321,19 @@ content to include.
291
321
  {% include-markdown '/escap\'ed/single-quotes/in/file\'/name.md' %}
292
322
  ```
293
323
 
324
+ ```jinja
325
+ {%
326
+ include-markdown "**"
327
+ order="-natural-extension"
328
+ %}
329
+ ```
330
+
294
331
  <!-- mdpo-disable-next-line -->
295
332
 
296
333
  #### **`include`**
297
334
 
298
335
  Includes the content of a file or a group of files.
299
336
 
300
- - <a name="include_start" href="#include_start">#</a>
301
- **start**: Delimiter that marks the beginning of the content to include.
302
- - <a name="include_end" href="#include_end">#</a>
303
- **end**: Delimiter that marks the end of the content to include.
304
- - <a name="include_preserve-includer-indent" href="#include_preserve-includer-indent">#</a>
305
- **preserve-includer-indent** (_true_): When this option is enabled (default),
306
- every line of the content to include is indented with the same number of
307
- spaces used to indent the includer `{% %}` template. Possible values are
308
- `true` and `false`.
309
- - <a name="include_dedent" href="#include_dedent">#</a>
310
- **dedent** (_false_): If enabled, the included content will be dedented.
311
- - <a name="include_exclude" href="#include_exclude">#</a>
312
- **exclude**: Specify with a glob which files should be ignored. Only useful
313
- when passing globs to include multiple files.
314
- - <a name="include_trailing-newlines" href="#include_trailing-newlines">#</a>
315
- **trailing-newlines** (_true_): When this option is disabled, the trailing newlines
316
- found in the content to include are stripped. Possible values are `true` and `false`.
317
- - <a name="include_recursive" href="#include_recursive">#</a>
318
- **recursive** (_true_): When this option is disabled, included files are not
319
- processed for recursive includes. Possible values are `true` and `false`.
320
- - <a name="include_encoding" href="#include_encoding">#</a>
321
- **encoding** (_'utf-8'_): Specify the encoding of the included file.
322
- If not defined `'utf-8'` will be used.
323
-
324
337
  ##### Examples
325
338
 
326
339
  ```jinja
@@ -341,6 +354,7 @@ Includes the content of a file or a group of files.
341
354
  {%
342
355
  include '**'
343
356
  exclude='./*.md'
357
+ order='random'
344
358
  %}
345
359
  ```
346
360
 
@@ -0,0 +1,16 @@
1
+ mkdocs_include_markdown_plugin/__init__.py,sha256=ByOde15RQSHlZTmkepPSzIt0GzUIv_-GKORFDnxnOmc,193
2
+ mkdocs_include_markdown_plugin/cache.py,sha256=iKZXwehr2Mus0NRcsKFRC6xMC4B5aG4pP-Yb1ooCQo4,3836
3
+ mkdocs_include_markdown_plugin/config.py,sha256=AuJWYB_sAbRYpecXy6HHugQR09kKjlibcYX6kkWmBVc,1137
4
+ mkdocs_include_markdown_plugin/directive.py,sha256=K8L2ozzeCzSJ1N3owPAdETWEIM80kJCU2pygpYjg1F4,14752
5
+ mkdocs_include_markdown_plugin/event.py,sha256=uzns7ARTwL8vH1VTqp6eSdZb8S9IQrxFIGUDk5VAH3o,26427
6
+ mkdocs_include_markdown_plugin/files_watcher.py,sha256=hu-63u8syl00mXP59vP_iqD7T1R9iU9JjiZOHDV5fas,298
7
+ mkdocs_include_markdown_plugin/logger.py,sha256=nl63G4IJSt5mEYHbObuh7BmmowSv-aa1l_v9I257570,230
8
+ mkdocs_include_markdown_plugin/placeholders.py,sha256=LBxFagmDDJ8le4AYrLJbt7WqYBu-kpJP6QqGWH7tDrs,1132
9
+ mkdocs_include_markdown_plugin/plugin.py,sha256=t7eIgB3Z6XmcPOzlTqE15ajhY3oYPNKOZLb6D1_hx7I,4334
10
+ mkdocs_include_markdown_plugin/process.py,sha256=u7D1Lgk1kb9pR5hAo7-_9n4HE1DvC6jVzrGwaJie74k,22674
11
+ mkdocs_include_markdown_plugin/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
+ mkdocs_include_markdown_plugin-7.2.1.dist-info/METADATA,sha256=GiQX7WtSJYA0eNUAwGdDNhfQT2iOgx1QY5HTz9nEwJM,12697
13
+ mkdocs_include_markdown_plugin-7.2.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
14
+ mkdocs_include_markdown_plugin-7.2.1.dist-info/entry_points.txt,sha256=WEL5m7sw5ZMBwmdOs5S_9mGoZfLCaUDPkDKw7CUk16Q,96
15
+ mkdocs_include_markdown_plugin-7.2.1.dist-info/licenses/LICENSE,sha256=Fyr5Goos1Rh671K_-S1AC7jTp1ng4xQdFjv-qbmCWLc,11365
16
+ mkdocs_include_markdown_plugin-7.2.1.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.27.0
2
+ Generator: hatchling 1.28.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -187,7 +187,7 @@
187
187
  same "printed page" as the copyright notice for easier
188
188
  identification within third-party archives.
189
189
 
190
- Copyright 2017-2025 Joe Rickerby and contributors
190
+ Copyright 2017-2026 Joe Rickerby and contributors
191
191
 
192
192
  Licensed under the Apache License, Version 2.0 (the "License");
193
193
  you may not use this file except in compliance with the License.
@@ -1,16 +0,0 @@
1
- mkdocs_include_markdown_plugin/__init__.py,sha256=ByOde15RQSHlZTmkepPSzIt0GzUIv_-GKORFDnxnOmc,193
2
- mkdocs_include_markdown_plugin/cache.py,sha256=iKZXwehr2Mus0NRcsKFRC6xMC4B5aG4pP-Yb1ooCQo4,3836
3
- mkdocs_include_markdown_plugin/config.py,sha256=VX95U8-9gFzPUIBSTA8TSL4xOD49qTMSs8dhFiilGns,1091
4
- mkdocs_include_markdown_plugin/directive.py,sha256=A6pgzbvXiuLgsPs46W0RSEh1N0sxOjmQLwvHBk_CNm0,12425
5
- mkdocs_include_markdown_plugin/event.py,sha256=KyGoX7jxw8psHd3nWz4iIiZpeN1cPuc7NkiYSCKIRyc,23934
6
- mkdocs_include_markdown_plugin/files_watcher.py,sha256=hu-63u8syl00mXP59vP_iqD7T1R9iU9JjiZOHDV5fas,298
7
- mkdocs_include_markdown_plugin/logger.py,sha256=nl63G4IJSt5mEYHbObuh7BmmowSv-aa1l_v9I257570,230
8
- mkdocs_include_markdown_plugin/placeholders.py,sha256=LBxFagmDDJ8le4AYrLJbt7WqYBu-kpJP6QqGWH7tDrs,1132
9
- mkdocs_include_markdown_plugin/plugin.py,sha256=0muHIhmklC7v-QKWsAcAtZKhhZQ2olH6b28b3_3e4gI,3850
10
- mkdocs_include_markdown_plugin/process.py,sha256=FQ3f4h6uNKelllh_Wk95Zl8xGrfD6Cc0jCP-9s7Bmf4,20803
11
- mkdocs_include_markdown_plugin/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
- mkdocs_include_markdown_plugin-7.1.8.dist-info/METADATA,sha256=pisnflwETGk3GBV-a7mgWms8XXWFRtFemtLuO63PQq0,12770
13
- mkdocs_include_markdown_plugin-7.1.8.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
14
- mkdocs_include_markdown_plugin-7.1.8.dist-info/entry_points.txt,sha256=WEL5m7sw5ZMBwmdOs5S_9mGoZfLCaUDPkDKw7CUk16Q,96
15
- mkdocs_include_markdown_plugin-7.1.8.dist-info/licenses/LICENSE,sha256=qm1MNzU_lIANFB-1cBpD9hqdD_Htv3_Xr6hobt0IHq8,11365
16
- mkdocs_include_markdown_plugin-7.1.8.dist-info/RECORD,,