diff --git a/README.md b/README.md index 4dd9696..e350393 100644 --- a/README.md +++ b/README.md @@ -290,6 +290,24 @@ With the following result: For more details refer to the [Rest Filter Patterns](#rest-filter-patterns) section below. +By default, remaining items keep their hierarchical structure. You may add `flat` to flatten all the matching pages: + +```yaml +nav: + - page1.md + - Rest: + - ... | flat | **/introduction.md + - ... | flat +``` + +- Page 1 +- Rest + - Introduction + - Introduction + - Page 2 + - Page 3 + - Page 4 +
## Rest Filter Patterns diff --git a/mkdocs_awesome_pages_plugin/meta.py b/mkdocs_awesome_pages_plugin/meta.py index e2b0fa1..9899160 100644 --- a/mkdocs_awesome_pages_plugin/meta.py +++ b/mkdocs_awesome_pages_plugin/meta.py @@ -49,18 +49,21 @@ class RestType(Enum): class MetaNavRestItem(MetaNavItem): - _REGEX = r'^\.{3}\s*\|\s*(?:(regex|glob)=)?(.*)' + _REGEX = r'^\.{3}\s*(?:\|\s*(flat)\s*)?\s*(?:\|\s*(?:(regex|glob)=)?(.*))?' def __init__(self, value: str): super().__init__(value) match = re.search(self._REGEX, value) - if match: - self.type = RestType.GLOB if match.group(1) is None else RestType(match.group(1)) - self.pattern = match.group(2) + if match.group(2) is not None: + self.type = RestType(match.group(2)) + elif match.group(3) is not None: + self.type = RestType.GLOB else: self.type = RestType.ALL - self.pattern = None + + self.pattern = match.group(3) + self.flat = match.group(1) is not None def matches(self, path: Optional[str]) -> bool: if self.type == RestType.GLOB: @@ -72,7 +75,7 @@ def matches(self, path: Optional[str]) -> bool: @staticmethod def is_rest(item: Any) -> bool: - return isinstance(item, str) and (item == '...' or re.search(MetaNavRestItem._REGEX, item)) + return isinstance(item, str) and re.search(MetaNavRestItem._REGEX, item) class RestItemList(collections.abc.Iterable): diff --git a/mkdocs_awesome_pages_plugin/plugin.py b/mkdocs_awesome_pages_plugin/plugin.py index dad3d16..1e48c02 100644 --- a/mkdocs_awesome_pages_plugin/plugin.py +++ b/mkdocs_awesome_pages_plugin/plugin.py @@ -84,7 +84,10 @@ def _generate_rest_blocks(self, items: List[NavigationItem], exclude_files: List child_result = self._generate_rest_blocks(item.children, exclude_files) for rest_item, children in child_result.items(): if children: - result[rest_item].append(Section(item.title, children)) + if rest_item.flat: + result[rest_item].extend(children) + else: + result[rest_item].append(Section(item.title, children)) return result def _insert_rest(self, items): diff --git a/mkdocs_awesome_pages_plugin/tests/e2e/test_mkdocs_nav.py b/mkdocs_awesome_pages_plugin/tests/e2e/test_mkdocs_nav.py index 0b96b9d..793bb26 100644 --- a/mkdocs_awesome_pages_plugin/tests/e2e/test_mkdocs_nav.py +++ b/mkdocs_awesome_pages_plugin/tests/e2e/test_mkdocs_nav.py @@ -642,6 +642,38 @@ def test_meta_collapse_global(self): ('2', '/a/2') ]) + def test_flat(self): + navigation = self.mkdocs(self.createConfig(mkdocs_nav=[ + '... | flat', + {'C': [ + 'a/2.md', + 'a/1.md', + 'b/3.md' + ]} + ]), [ + ('a', [ + '1.md', + '2.md', + '3.md' + ]), + ('b', [ + '1.md', + '2.md', + '3.md' + ]) + ]) + + self.assertEqual(navigation, [ + ('3', '/a/3'), + ('1', '/b/1'), + ('2', '/b/2'), + ('C', [ + ('2', '/a/2'), + ('1', '/a/1'), + ('3', '/b/3') + ]) + ]) + class TestMkdocsNavRestGlob(E2ETestCase): @@ -853,6 +885,47 @@ def test_duplicate_pattern(self): '3.md' ]) + def test_flat(self): + navigation = self.mkdocs(self.createConfig(mkdocs_nav=[ + '... | flat | **/1.md', + {'2': [ + '... | flat | a/**/2.md' + ]}, + {'Rest': [ + '... | flat' + ]} + ]), [ + ('a', [ + '1.md', + '2.md', + '3.md', + ('aa', [ + '1.md', + '2.md' + ]) + ]), + ('b', [ + '1.md', + '2.md', + '3.md' + ]) + ], dummy_pages=False) + + self.assertEqual(navigation, [ + ('1', '/a/1'), + ('1', '/a/aa/1'), + ('1', '/b/1'), + ('2', [ + ('2', '/a/2'), + ('2', '/a/aa/2') + ]), + ('Rest', [ + ('3', '/a/3'), + ('2', '/b/2'), + ('3', '/b/3') + ]) + ]) + class TestMkdocsNavRestRegex(E2ETestCase): @@ -1063,3 +1136,44 @@ def test_duplicate_pattern(self): '2.md', '3.md' ]) + + def test_flat(self): + navigation = self.mkdocs(self.createConfig(mkdocs_nav=[ + r'... | flat | regex=^.*/1\.md$', + {'2': [ + r'... | flat | regex=^a/(.*/|)2\.md$' + ]}, + {'Rest': [ + '... | flat' + ]} + ]), [ + ('a', [ + '1.md', + '2.md', + '3.md', + ('aa', [ + '1.md', + '2.md' + ]) + ]), + ('b', [ + '1.md', + '2.md', + '3.md' + ]) + ], dummy_pages=False) + + self.assertEqual(navigation, [ + ('1', '/a/1'), + ('1', '/a/aa/1'), + ('1', '/b/1'), + ('2', [ + ('2', '/a/2'), + ('2', '/a/aa/2') + ]), + ('Rest', [ + ('3', '/a/3'), + ('2', '/b/2'), + ('3', '/b/3') + ]) + ]) diff --git a/mkdocs_awesome_pages_plugin/tests/navigation/test_meta.py b/mkdocs_awesome_pages_plugin/tests/navigation/test_meta.py index cae1434..de4a21e 100644 --- a/mkdocs_awesome_pages_plugin/tests/navigation/test_meta.py +++ b/mkdocs_awesome_pages_plugin/tests/navigation/test_meta.py @@ -180,69 +180,172 @@ def test_all(self): self.assertIsNone(item.pattern) self.assertEqual(item.type, RestType.ALL) + self.assertFalse(item.flat) + + def test_all_flat(self): + item = MetaNavRestItem('...|flat') + + self.assertIsNone(item.pattern) + self.assertEqual(item.type, RestType.ALL) + self.assertTrue(item.flat) + + def test_all_flat_spaces(self): + item = MetaNavRestItem('... | flat') + + self.assertIsNone(item.pattern) + self.assertEqual(item.type, RestType.ALL) + self.assertTrue(item.flat) def test_glob_implicit(self): item = MetaNavRestItem('...|foo') self.assertEqual(item.pattern, 'foo') self.assertEqual(item.type, RestType.GLOB) + self.assertFalse(item.flat) def test_glob_implicit_spaces(self): item = MetaNavRestItem('... | foo') self.assertEqual(item.pattern, 'foo') self.assertEqual(item.type, RestType.GLOB) + self.assertFalse(item.flat) def test_glob_implicit_trailing_space(self): item = MetaNavRestItem('... | foo ') self.assertEqual(item.pattern, 'foo ') self.assertEqual(item.type, RestType.GLOB) + self.assertFalse(item.flat) + + def test_glob_implicit_flat(self): + item = MetaNavRestItem('...|flat|foo') + + self.assertEqual(item.pattern, 'foo') + self.assertEqual(item.type, RestType.GLOB) + self.assertTrue(item.flat) + + def test_glob_implicit_flat_spaces(self): + item = MetaNavRestItem('... | flat | foo') + + self.assertEqual(item.pattern, 'foo') + self.assertEqual(item.type, RestType.GLOB) + self.assertTrue(item.flat) + + def test_glob_implicit_flat_trailing_space(self): + item = MetaNavRestItem('... | flat | foo ') + + self.assertEqual(item.pattern, 'foo ') + self.assertEqual(item.type, RestType.GLOB) + self.assertTrue(item.flat) def test_glob_explicit(self): item = MetaNavRestItem('...|glob=foo') self.assertEqual(item.pattern, 'foo') self.assertEqual(item.type, RestType.GLOB) + self.assertFalse(item.flat) def test_glob_explicit_spaces(self): item = MetaNavRestItem('... | glob=foo') self.assertEqual(item.pattern, 'foo') self.assertEqual(item.type, RestType.GLOB) + self.assertFalse(item.flat) def test_glob_explicit_trailing_space(self): item = MetaNavRestItem('... | glob=foo ') self.assertEqual(item.pattern, 'foo ') self.assertEqual(item.type, RestType.GLOB) + self.assertFalse(item.flat) def test_glob_explicit_leading_space(self): item = MetaNavRestItem('... | glob= foo') self.assertEqual(item.pattern, ' foo') self.assertEqual(item.type, RestType.GLOB) + self.assertFalse(item.flat) + + def test_glob_explicit_flat(self): + item = MetaNavRestItem('...|flat|glob=foo') + + self.assertEqual(item.pattern, 'foo') + self.assertEqual(item.type, RestType.GLOB) + self.assertTrue(item.flat) + + def test_glob_explicit_flat_spaces(self): + item = MetaNavRestItem('... | flat | glob=foo') + + self.assertEqual(item.pattern, 'foo') + self.assertEqual(item.type, RestType.GLOB) + self.assertTrue(item.flat) + + def test_glob_explicit_flat_trailing_space(self): + item = MetaNavRestItem('... | flat | glob=foo ') + + self.assertEqual(item.pattern, 'foo ') + self.assertEqual(item.type, RestType.GLOB) + self.assertTrue(item.flat) + + def test_glob_explicit_flat_leading_space(self): + item = MetaNavRestItem('... | flat | glob= foo') + + self.assertEqual(item.pattern, ' foo') + self.assertEqual(item.type, RestType.GLOB) + self.assertTrue(item.flat) def test_regex(self): item = MetaNavRestItem('...|regex=foo') self.assertEqual(item.pattern, 'foo') self.assertEqual(item.type, RestType.REGEX) + self.assertFalse(item.flat) def test_regex_spaces(self): item = MetaNavRestItem('... | regex=foo') self.assertEqual(item.pattern, 'foo') self.assertEqual(item.type, RestType.REGEX) + self.assertFalse(item.flat) def test_regex_trailing_space(self): item = MetaNavRestItem('... | regex=foo ') self.assertEqual(item.pattern, 'foo ') self.assertEqual(item.type, RestType.REGEX) + self.assertFalse(item.flat) def test_regex_leading_space(self): item = MetaNavRestItem('... | regex= foo') self.assertEqual(item.pattern, ' foo') self.assertEqual(item.type, RestType.REGEX) + self.assertFalse(item.flat) + + def test_regex_flat(self): + item = MetaNavRestItem('...|flat|regex=foo') + + self.assertEqual(item.pattern, 'foo') + self.assertEqual(item.type, RestType.REGEX) + self.assertTrue(item.flat) + + def test_regex_flat_spaces(self): + item = MetaNavRestItem('... | flat | regex=foo') + + self.assertEqual(item.pattern, 'foo') + self.assertEqual(item.type, RestType.REGEX) + self.assertTrue(item.flat) + + def test_regex_flat_trailing_space(self): + item = MetaNavRestItem('... | flat | regex=foo ') + + self.assertEqual(item.pattern, 'foo ') + self.assertEqual(item.type, RestType.REGEX) + self.assertTrue(item.flat) + + def test_regex_flat_leading_space(self): + item = MetaNavRestItem('... | flat | regex= foo') + + self.assertEqual(item.pattern, ' foo') + self.assertEqual(item.type, RestType.REGEX) + self.assertTrue(item.flat)