init docforge lib
This commit is contained in:
210
docforge/model/nav.py
Normal file
210
docforge/model/nav.py
Normal file
@@ -0,0 +1,210 @@
|
||||
"""Navigation structure for doc-forge documentation.
|
||||
|
||||
Navigation provides a hierarchical structure for organizing documentation
|
||||
modules. It is derived automatically from the project structure rather
|
||||
than being manually authored, ensuring consistency between the
|
||||
documentation model and the navigation.
|
||||
|
||||
The navigation is used by renderers to generate table of contents,
|
||||
sidebars, and other navigation elements.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Dict, List, Optional
|
||||
|
||||
|
||||
class NavEntry:
|
||||
"""Single navigation entry linking to a module.
|
||||
|
||||
A NavEntry represents one item in the documentation navigation,
|
||||
typically corresponding to a module. It contains a display title
|
||||
and the module path it links to.
|
||||
|
||||
Attributes:
|
||||
title: The display title for this navigation entry
|
||||
module: The import path of the module this entry links to
|
||||
"""
|
||||
|
||||
def __init__(self, title: str, module: str) -> None:
|
||||
"""Initialize a NavEntry.
|
||||
|
||||
Args:
|
||||
title: The display title for this entry
|
||||
module: The import path of the linked module
|
||||
|
||||
Raises:
|
||||
ValueError: If title or module is empty
|
||||
"""
|
||||
if not title:
|
||||
raise ValueError("NavEntry title cannot be empty")
|
||||
if not module:
|
||||
raise ValueError("NavEntry module cannot be empty")
|
||||
|
||||
self.title: str = title
|
||||
self.module: str = module
|
||||
|
||||
def __repr__(self) -> str:
|
||||
"""Return a string representation of the NavEntry.
|
||||
|
||||
Returns:
|
||||
String representation showing title and module
|
||||
"""
|
||||
return f"NavEntry(title='{self.title}', module='{self.module}')"
|
||||
|
||||
|
||||
class Navigation:
|
||||
"""Navigation structure derived from project modules.
|
||||
|
||||
Navigation provides an organized hierarchy for browsing documentation.
|
||||
It is automatically generated from the project's module structure,
|
||||
ensuring that the navigation always reflects the actual available
|
||||
documentation.
|
||||
|
||||
The navigation can be customized by:
|
||||
- Changing the order of entries
|
||||
- Grouping related modules
|
||||
- Providing custom titles
|
||||
|
||||
Attributes:
|
||||
entries: List of navigation entries in order
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
"""Initialize an empty Navigation."""
|
||||
self.entries: List[NavEntry] = []
|
||||
|
||||
def add_entry(self, entry: NavEntry) -> None:
|
||||
"""Add a navigation entry.
|
||||
|
||||
Args:
|
||||
entry: The navigation entry to add
|
||||
"""
|
||||
self.entries.append(entry)
|
||||
|
||||
def add_entry_by_module(self, module: str, title: Optional[str] = None) -> None:
|
||||
"""Add a navigation entry for a module.
|
||||
|
||||
This is a convenience method that creates a NavEntry from a module
|
||||
path. If no title is provided, the module name is used as the title.
|
||||
|
||||
Args:
|
||||
module: The import path of the module
|
||||
title: Optional custom title (defaults to module name)
|
||||
"""
|
||||
if title is None:
|
||||
# Use the last part of the module path as the title
|
||||
title = module.split('.')[-1].replace('_', ' ').title()
|
||||
|
||||
entry = NavEntry(title, module)
|
||||
self.add_entry(entry)
|
||||
|
||||
def get_entry(self, title: str) -> Optional[NavEntry]:
|
||||
"""Get a navigation entry by title.
|
||||
|
||||
Args:
|
||||
title: The title of the entry to find
|
||||
|
||||
Returns:
|
||||
The NavEntry if found, None otherwise
|
||||
"""
|
||||
for entry in self.entries:
|
||||
if entry.title == title:
|
||||
return entry
|
||||
return None
|
||||
|
||||
def get_entry_by_module(self, module: str) -> Optional[NavEntry]:
|
||||
"""Get a navigation entry by module path.
|
||||
|
||||
Args:
|
||||
module: The module path to search for
|
||||
|
||||
Returns:
|
||||
The NavEntry if found, None otherwise
|
||||
"""
|
||||
for entry in self.entries:
|
||||
if entry.module == module:
|
||||
return entry
|
||||
return None
|
||||
|
||||
def remove_entry(self, title: str) -> bool:
|
||||
"""Remove a navigation entry by title.
|
||||
|
||||
Args:
|
||||
title: The title of the entry to remove
|
||||
|
||||
Returns:
|
||||
True if entry was removed, False if not found
|
||||
"""
|
||||
for i, entry in enumerate(self.entries):
|
||||
if entry.title == title:
|
||||
del self.entries[i]
|
||||
return True
|
||||
return False
|
||||
|
||||
def remove_entry_by_module(self, module: str) -> bool:
|
||||
"""Remove a navigation entry by module path.
|
||||
|
||||
Args:
|
||||
module: The module path of the entry to remove
|
||||
|
||||
Returns:
|
||||
True if entry was removed, False if not found
|
||||
"""
|
||||
for i, entry in enumerate(self.entries):
|
||||
if entry.module == module:
|
||||
del self.entries[i]
|
||||
return True
|
||||
return False
|
||||
|
||||
def reorder_entries(self, titles: List[str]) -> None:
|
||||
"""Reorder entries based on provided title order.
|
||||
|
||||
Entries not mentioned in the titles list will maintain their
|
||||
relative order and be placed after the specified entries.
|
||||
|
||||
Args:
|
||||
titles: List of titles in the desired order
|
||||
"""
|
||||
# Create a mapping of title to entry for quick lookup
|
||||
entry_map = {entry.title: entry for entry in self.entries}
|
||||
|
||||
# Build new ordered list
|
||||
ordered_entries = []
|
||||
remaining_entries = list(self.entries)
|
||||
|
||||
# Add entries in specified order
|
||||
for title in titles:
|
||||
if title in entry_map:
|
||||
ordered_entries.append(entry_map[title])
|
||||
# Remove from remaining entries
|
||||
remaining_entries = [e for e in remaining_entries if e.title != title]
|
||||
|
||||
# Add remaining entries in their original order
|
||||
ordered_entries.extend(remaining_entries)
|
||||
|
||||
self.entries = ordered_entries
|
||||
|
||||
def is_empty(self) -> bool:
|
||||
"""Check if navigation has no entries.
|
||||
|
||||
Returns:
|
||||
True if navigation has no entries, False otherwise
|
||||
"""
|
||||
return len(self.entries) == 0
|
||||
|
||||
def get_module_list(self) -> List[str]:
|
||||
"""Get list of all module paths in navigation.
|
||||
|
||||
Returns:
|
||||
List of module paths in navigation order
|
||||
"""
|
||||
return [entry.module for entry in self.entries]
|
||||
|
||||
def __repr__(self) -> str:
|
||||
"""Return a string representation of the Navigation.
|
||||
|
||||
Returns:
|
||||
String representation showing entry count
|
||||
"""
|
||||
return f"Navigation(entries={len(self.entries)})"
|
||||
Reference in New Issue
Block a user