parent
30435e5197
commit
5dd8171fb3
7 changed files with 171 additions and 77 deletions
@ -0,0 +1,108 @@ |
||||
from pathlib import Path |
||||
from json import loads |
||||
import re |
||||
|
||||
from hotdoc.core.exceptions import HotdocSourceException |
||||
from hotdoc.core.extension import Extension |
||||
from hotdoc.core.tree import Page |
||||
from hotdoc.core.project import Project |
||||
from hotdoc.run_hotdoc import Application |
||||
from hotdoc.core.formatter import Formatter |
||||
from hotdoc.utils.loggable import Logger, warn, info |
||||
|
||||
import typing as T |
||||
|
||||
if T.TYPE_CHECKING: |
||||
import argparse |
||||
|
||||
Logger.register_warning_code('unknown-refman-link', HotdocSourceException, 'refman-links') |
||||
|
||||
class RefmanLinksExtension(Extension): |
||||
extension_name = 'refman-links' |
||||
argument_prefix = 'refman' |
||||
|
||||
def __init__(self, app: Application, project: Project): |
||||
self.project: Project |
||||
super().__init__(app, project) |
||||
self._data_file: T.Optional[Path] = None |
||||
self._data: T.Dict[str, str] = {} |
||||
|
||||
@staticmethod |
||||
def add_arguments(parser: 'argparse.ArgumentParser'): |
||||
group = parser.add_argument_group( |
||||
'Refman links', |
||||
'Custom Meson extension', |
||||
) |
||||
|
||||
# Add Arguments with `group.add_argument(...)` |
||||
group.add_argument( |
||||
f'--refman-data-file', |
||||
help="JSON file with the mappings to replace", |
||||
default=None, |
||||
) |
||||
|
||||
def parse_config(self, config: T.Dict[str, T.Any]) -> None: |
||||
super().parse_config(config) |
||||
self._data_file = config.get('refman_data_file') |
||||
|
||||
def _formatting_page_cb(self, formatter: Formatter, page: Page) -> None: |
||||
''' Replace Meson refman tags |
||||
|
||||
Links of the form [[function]] are automatically replaced |
||||
with valid links to the correct URL. To reference objects / types use the |
||||
[[@object]] syntax. |
||||
''' |
||||
link_regex = re.compile(r'\[\[#?@?([ \n\t]*[a-zA-Z0-9_]+[ \n\t]*\.)*[ \n\t]*[a-zA-Z0-9_]+[ \n\t]*\]\]', re.MULTILINE) |
||||
for m in link_regex.finditer(page.formatted_contents): |
||||
i = m.group() |
||||
obj_id: str = i[2:-2] |
||||
obj_id = re.sub(r'[ \n\t]', '', obj_id) # Remove whitespaces |
||||
|
||||
# Marked as inside a code block? |
||||
in_code_block = False |
||||
if obj_id.startswith('#'): |
||||
in_code_block = True |
||||
obj_id = obj_id[1:] |
||||
|
||||
if obj_id not in self._data: |
||||
warn('unknown-refman-link', f'{Path(page.name).name}: Unknown Meson refman link: "{obj_id}"') |
||||
continue |
||||
|
||||
# Just replaces [[!file.id]] paths with the page file (no fancy HTML) |
||||
if obj_id.startswith('!'): |
||||
page.formatted_contents = page.formatted_contents.replace(i, self._data[obj_id]) |
||||
continue |
||||
|
||||
# Fancy links for functions and methods |
||||
text = obj_id |
||||
if text.startswith('@'): |
||||
text = text[1:] |
||||
else: |
||||
text = text + '()' |
||||
if not in_code_block: |
||||
text = f'<code>{text}</code>' |
||||
link = f'<a href="{self._data[obj_id]}"><ins>{text}</ins></a>' |
||||
page.formatted_contents = page.formatted_contents.replace(i, link) |
||||
|
||||
def setup(self) -> None: |
||||
super().setup() |
||||
|
||||
if not self._data_file: |
||||
info('Meson refman extension DISABLED') |
||||
return |
||||
|
||||
raw = Path(self._data_file).read_text(encoding='utf-8') |
||||
self._data = loads(raw) |
||||
|
||||
# Register formater |
||||
for ext in self.project.extensions.values(): |
||||
ext = T.cast(Extension, ext) |
||||
ext.formatter.formatting_page_signal.connect(self._formatting_page_cb) |
||||
info('Meson refman extension LOADED') |
||||
|
||||
@staticmethod |
||||
def get_dependencies() -> T.List[T.Type[Extension]]: |
||||
return [] # In case this extension has dependencies on other extensions |
||||
|
||||
def get_extension_classes() -> T.List[T.Type[Extension]]: |
||||
return [RefmanLinksExtension] |
Loading…
Reference in new issue