summaryrefslogtreecommitdiffstats
path: root/sphinx/builders/manpage.py
blob: 2d35d2047b92dca6a83abcc38fe7484fc13f70ee (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
"""Manual pages builder."""

from __future__ import annotations

import warnings
from os import path
from typing import TYPE_CHECKING, Any

from docutils.frontend import OptionParser
from docutils.io import FileOutput

from sphinx import addnodes
from sphinx.builders import Builder
from sphinx.locale import __
from sphinx.util import logging
from sphinx.util.console import darkgreen  # type: ignore[attr-defined]
from sphinx.util.display import progress_message
from sphinx.util.nodes import inline_all_toctrees
from sphinx.util.osutil import ensuredir, make_filename_from_project
from sphinx.writers.manpage import ManualPageTranslator, ManualPageWriter

if TYPE_CHECKING:
    from sphinx.application import Sphinx
    from sphinx.config import Config

logger = logging.getLogger(__name__)


class ManualPageBuilder(Builder):
    """
    Builds groff output in manual page format.
    """
    name = 'man'
    format = 'man'
    epilog = __('The manual pages are in %(outdir)s.')

    default_translator_class = ManualPageTranslator
    supported_image_types: list[str] = []

    def init(self) -> None:
        if not self.config.man_pages:
            logger.warning(__('no "man_pages" config value found; no manual pages '
                              'will be written'))

    def get_outdated_docs(self) -> str | list[str]:
        return 'all manpages'  # for now

    def get_target_uri(self, docname: str, typ: str | None = None) -> str:
        return ''

    @progress_message(__('writing'))
    def write(self, *ignored: Any) -> None:
        docwriter = ManualPageWriter(self)
        with warnings.catch_warnings():
            warnings.filterwarnings('ignore', category=DeprecationWarning)
            # DeprecationWarning: The frontend.OptionParser class will be replaced
            # by a subclass of argparse.ArgumentParser in Docutils 0.21 or later.
            docsettings: Any = OptionParser(
                defaults=self.env.settings,
                components=(docwriter,),
                read_config_files=True).get_default_values()

        for info in self.config.man_pages:
            docname, name, description, authors, section = info
            if docname not in self.env.all_docs:
                logger.warning(__('"man_pages" config value references unknown '
                                  'document %s'), docname)
                continue
            if isinstance(authors, str):
                if authors:
                    authors = [authors]
                else:
                    authors = []

            docsettings.title = name
            docsettings.subtitle = description
            docsettings.authors = authors
            docsettings.section = section

            if self.config.man_make_section_directory:
                dirname = 'man%s' % section
                ensuredir(path.join(self.outdir, dirname))
                targetname = f'{dirname}/{name}.{section}'
            else:
                targetname = f'{name}.{section}'

            logger.info(darkgreen(targetname) + ' { ', nonl=True)
            destination = FileOutput(
                destination_path=path.join(self.outdir, targetname),
                encoding='utf-8')

            tree = self.env.get_doctree(docname)
            docnames: set[str] = set()
            largetree = inline_all_toctrees(self, docnames, docname, tree,
                                            darkgreen, [docname])
            largetree.settings = docsettings
            logger.info('} ', nonl=True)
            self.env.resolve_references(largetree, docname, self)
            # remove pending_xref nodes
            for pendingnode in largetree.findall(addnodes.pending_xref):
                pendingnode.replace_self(pendingnode.children)

            docwriter.write(largetree, destination)

    def finish(self) -> None:
        pass


def default_man_pages(config: Config) -> list[tuple[str, str, str, list[str], int]]:
    """ Better default man_pages settings. """
    filename = make_filename_from_project(config.project)
    return [(config.root_doc, filename, f'{config.project} {config.release}',
             [config.author], 1)]


def setup(app: Sphinx) -> dict[str, Any]:
    app.add_builder(ManualPageBuilder)

    app.add_config_value('man_pages', default_man_pages, False)
    app.add_config_value('man_show_urls', False, False)
    app.add_config_value('man_make_section_directory', False, False)

    return {
        'version': 'builtin',
        'parallel_read_safe': True,
        'parallel_write_safe': True,
    }