Skip to content

Commit

Permalink
Add explicit flag to remove files not listed in navigation file
Browse files Browse the repository at this point in the history
Signed-off-by: Evan Flynn <[email protected]>
  • Loading branch information
evan-flynn-apexai committed Mar 29, 2023
1 parent a83351c commit d2bafac
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 42 deletions.
7 changes: 7 additions & 0 deletions docs/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,5 +136,12 @@ You can change the indentation just for the extension, but that will not affect
- mdx_truly_sane_lists
```

#### `explicit`

*boolean, default `false`*

If enabled, will remove any files from the build not explicitly listed in the navigation file
(e.g. `SUMMARY.md`).

[mkdocs-nav]: https://www.mkdocs.org/user-guide/writing-your-docs/#configure-pages-and-navigation
[docs_dir]: https://www.mkdocs.org/user-guide/configuration/#docs_dir
4 changes: 4 additions & 0 deletions mkdocs_literate_nav/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ def __init__(
self.implicit_index = implicit_index
self._markdown_config = markdown_config or {}
self.seen_items: set[str] = set()
self.files_in_nav: set[str] = set()
self._warn = functools.lru_cache()(log.warning)

def markdown_to_nav(self, roots: tuple[str, ...] = (".",)) -> Nav:
Expand Down Expand Up @@ -80,6 +81,7 @@ def _list_element_to_nav(
if first_item is not None:
if isinstance(first_item, str):
self.seen_items.add(first_item)
self.files_in_nav.add(first_item)
result.append(first_item)
for item in section:
assert item.tag == "li"
Expand Down Expand Up @@ -117,6 +119,8 @@ def _list_element_to_nav(

assert out_item is not None
if type(out_item) in (str, list, DirectoryWildcard) and out_title is not None:
if isinstance(out_item, str):
self.files_in_nav.add(out_item)
result.append({out_title: out_item})
else:
result.append(out_item)
Expand Down
101 changes: 59 additions & 42 deletions mkdocs_literate_nav/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ class _PluginConfig:
implicit_index = mkdocs.config.config_options.Type(bool, default=False)
markdown_extensions = mkdocs.config.config_options.MarkdownExtensions()
tab_length = mkdocs.config.config_options.Type(int, default=4)
explicit = mkdocs.config.config_options.Type(bool, default=False)


class LiterateNavPlugin(mkdocs.plugins.BasePlugin):
Expand All @@ -42,7 +43,7 @@ class LiterateNavPlugin(mkdocs.plugins.BasePlugin):

@event_priority(-100) # Run last
def on_files(self, files: mkdocs.structure.files.Files, config: mkdocs.config.Config) -> None:
config["nav"] = resolve_directories_in_nav(
config["nav"] = self._resolve_directories_in_nav(
config["nav"],
files,
nav_file_name=self.config["nav_file"],
Expand All @@ -53,60 +54,76 @@ def on_files(self, files: mkdocs.structure.files.Files, config: mkdocs.config.Co
tab_length=self.config["tab_length"],
),
)
self._files = files

if self.config["explicit"]:

def is_file_in_nav_file(f) -> bool:
if (
str(f.src_uri) in self.nav_parser.files_in_nav
or f not in files.documentation_pages()
):
return True
log.warning(f"File excluded from navigation file: {f.src_uri}")
return False

self._files = [f for f in files if is_file_in_nav_file(f)]
else:
self._files = files
return mkdocs.structure.files.Files(self._files)

def on_nav(
self,
nav: mkdocs.structure.nav.Navigation,
config: mkdocs.config.Config,
files: mkdocs.structure.files.Files,
) -> None:
if files != getattr(self, "_files", None):
if files != getattr(self, "_files", None) and not self.config["explicit"]:
log.warning(
"The literate-nav plugin created the nav based on files that were subsequently modified by another MkDocs plugin! "
"Re-order `plugins` in mkdocs.yml so that 'literate-nav' appears later."
)


def resolve_directories_in_nav(
nav_data,
files: mkdocs.structure.files.Files,
nav_file_name: str,
implicit_index: bool,
markdown_config: dict | None = None,
):
"""Walk through a standard MkDocs nav config and replace `directory/` references.
Directories, if found, are resolved by the rules of literate nav insertion:
If it has a literate nav file, that is used. Otherwise an implicit nav is generated.
"""

def get_nav_for_dir(path: str) -> tuple[str, str] | None:
file = files.get_file_from_path(os.path.join(path, nav_file_name))
if not file:
return None
log.debug(f"Navigation for {path!r} based on {file.src_path!r}.")

# https://github.com/mkdocs/mkdocs/blob/ff0b726056/mkdocs/structure/nav.py#L113
# Prevent the warning in case the user doesn't also end up including this page in
# the final nav, maybe they want it only for the purpose of feeding to this plugin.
mkdocs.structure.pages.Page(None, file, {})

# https://github.com/mkdocs/mkdocs/blob/fa5aa4a26e/mkdocs/structure/pages.py#L120
with open(file.abs_src_path, encoding="utf-8-sig") as f:
return nav_file_name, f.read()

globber = MkDocsGlobber(files)
nav_parser = parser.NavParser(
get_nav_for_dir, globber, implicit_index=implicit_index, markdown_config=markdown_config
)

result = None
if not nav_data or get_nav_for_dir("."):
result = nav_parser.markdown_to_nav()
if not result:
result = nav_parser.resolve_yaml_nav(nav_data)
return result or []
def _resolve_directories_in_nav(
self,
nav_data,
files: mkdocs.structure.files.Files,
nav_file_name: str,
implicit_index: bool,
markdown_config: dict | None = None,
):
"""Walk through a standard MkDocs nav config and replace `directory/` references.
Directories, if found, are resolved by the rules of literate nav insertion:
If it has a literate nav file, that is used. Otherwise an implicit nav is generated.
"""

def get_nav_for_dir(path: str) -> tuple[str, str] | None:
file = files.get_file_from_path(os.path.join(path, nav_file_name))
if not file:
return None
log.debug(f"Navigation for {path!r} based on {file.src_path!r}.")

# https://github.com/mkdocs/mkdocs/blob/ff0b726056/mkdocs/structure/nav.py#L113
# Prevent the warning in case the user doesn't also end up including this page in
# the final nav, maybe they want it only for the purpose of feeding to this plugin.
mkdocs.structure.pages.Page(None, file, {})

# https://github.com/mkdocs/mkdocs/blob/fa5aa4a26e/mkdocs/structure/pages.py#L120
with open(file.abs_src_path, encoding="utf-8-sig") as f:
return nav_file_name, f.read()

globber = MkDocsGlobber(files)
self.nav_parser = parser.NavParser(
get_nav_for_dir, globber, implicit_index=implicit_index, markdown_config=markdown_config
)

result = None
if not nav_data or get_nav_for_dir("."):
result = self.nav_parser.markdown_to_nav()
if not result:
result = self.nav_parser.resolve_yaml_nav(nav_data)
return result or []


class MkDocsGlobber:
Expand Down

0 comments on commit d2bafac

Please sign in to comment.