2
2
3
3
from __future__ import annotations
4
4
5
+ import fnmatch
5
6
import subprocess
6
7
import tempfile
8
+ from collections import defaultdict
7
9
from datetime import date
8
10
from importlib import metadata
9
11
from pathlib import Path
10
12
from shutil import which
11
13
from typing import TYPE_CHECKING
12
14
15
+ from mkdocs .config .defaults import MkDocsConfig
16
+ from mkdocs .exceptions import PluginError
13
17
from mkdocs .plugins import BasePlugin
14
18
15
19
from mkdocs_manpage .config import PluginConfig
20
24
from typing import Any
21
25
22
26
from mkdocs .config .defaults import MkDocsConfig
27
+ from mkdocs .structure .files import Files
23
28
from mkdocs .structure .pages import Page
24
29
25
30
@@ -32,6 +37,19 @@ def _log_pandoc_output(output: str) -> None:
32
37
logger .debug (f"pandoc: { line .strip ()} " )
33
38
34
39
40
+ section_headers = {
41
+ "1" : "User Commands" ,
42
+ "2" : "System Calls Manual" ,
43
+ "3" : "Library Functions Manual" ,
44
+ "4" : "Kernel Interfaces Manual" ,
45
+ "5" : "File Formats Manual" ,
46
+ "6" : "Games Manual" ,
47
+ "7" : "Miscellaneous Information Manual" ,
48
+ "8" : "System Administration" ,
49
+ "9" : "Kernel Routines" ,
50
+ }
51
+
52
+
35
53
class MkdocsManpagePlugin (BasePlugin [PluginConfig ]):
36
54
"""The MkDocs plugin to generate manpages.
37
55
@@ -47,7 +65,16 @@ class MkdocsManpagePlugin(BasePlugin[PluginConfig]):
47
65
mkdocs_config : MkDocsConfig
48
66
49
67
def __init__ (self ) -> None : # noqa: D107
50
- self .pages : dict [str , str ] = {}
68
+ self .html_pages : dict [str , dict [str , str ]] = defaultdict (dict )
69
+
70
+ def _expand_inputs (self , inputs : list [str ], page_uris : list [str ]) -> list [str ]:
71
+ expanded : list [str ] = []
72
+ for input_file in inputs :
73
+ if "*" in input_file :
74
+ expanded .extend (fnmatch .filter (page_uris , input_file ))
75
+ else :
76
+ expanded .append (input_file )
77
+ return expanded
51
78
52
79
def on_config (self , config : MkDocsConfig ) -> MkDocsConfig | None :
53
80
"""Save the global MkDocs configuration.
@@ -65,6 +92,23 @@ def on_config(self, config: MkDocsConfig) -> MkDocsConfig | None:
65
92
self .mkdocs_config = config
66
93
return config
67
94
95
+ def on_files (self , files : Files , * , config : MkDocsConfig ) -> Files | None : # noqa: ARG002
96
+ """Expand inputs for manual pages.
97
+
98
+ Hook for the [`on_files` event](https://www.mkdocs.org/user-guide/plugins/#on_files).
99
+ In this hook we expand inputs for each manual pages (glob patterns using `*`).
100
+
101
+ Parameters:
102
+ files: The collection of MkDocs files.
103
+ config: The MkDocs configuration.
104
+
105
+ Returns:
106
+ Modified collection or none.
107
+ """
108
+ for manpage in self .config .pages :
109
+ manpage ["inputs" ] = self ._expand_inputs (manpage ["inputs" ], page_uris = list (files .src_uris .keys ()))
110
+ return files
111
+
68
112
def on_page_content (self , html : str , * , page : Page , ** kwargs : Any ) -> str | None : # noqa: ARG002
69
113
"""Record pages contents.
70
114
@@ -77,9 +121,10 @@ def on_page_content(self, html: str, *, page: Page, **kwargs: Any) -> str | None
77
121
"""
78
122
if not self .config .enabled :
79
123
return None
80
- if page .file .src_uri in self .config .pages or not self .config .pages :
81
- logger .debug (f"Adding page { page .file .src_uri } to manpage" )
82
- self .pages [page .file .src_uri ] = html
124
+ for manpage in self .config .pages :
125
+ if page .file .src_uri in manpage ["inputs" ]:
126
+ logger .debug (f"Adding page { page .file .src_uri } to manpage { manpage ['output' ]} " )
127
+ self .html_pages [manpage ["output" ]][page .file .src_uri ] = html
83
128
return html
84
129
85
130
def on_post_build (self , config : MkDocsConfig , ** kwargs : Any ) -> None : # noqa: ARG002
@@ -97,51 +142,52 @@ def on_post_build(self, config: MkDocsConfig, **kwargs: Any) -> None: # noqa: A
97
142
if pandoc is None :
98
143
logger .debug ("Could not find pandoc executable, trying to call 'pandoc' directly" )
99
144
pandoc = "pandoc"
100
- pages = []
101
- if self .config .pages :
102
- for page in self .config .pages :
103
- try :
104
- pages .append (self .pages [page ])
105
- except KeyError :
106
- logger .error (f"No page with path { page } " ) # noqa: TRY400
107
- else :
108
- pages = list (self .pages .values ())
109
- html = "\n \n " .join (pages )
110
-
111
- if self .config .preprocess :
112
- html = preprocess (html , self .config .preprocess )
113
-
114
- output_file = Path (config .site_dir , "manpage.1" )
115
- with tempfile .NamedTemporaryFile ("w" , prefix = "mkdocs_manpage_" , suffix = ".1.html" ) as temp_file :
116
- temp_file .write (html )
117
- pandoc_variables = [
118
- f"title:{ self .mkdocs_config .site_name } " ,
119
- "section:1" ,
120
- f"date:{ date .today ().strftime ('%Y-%m-%d' )} " , # noqa: DTZ011
121
- f"footer:mkdocs-manpage v{ metadata .version ('mkdocs-manpage' )} " ,
122
- "header:User Commands" ,
123
- ]
124
- pandoc_options = [
125
- "--verbose" ,
126
- "--standalone" ,
127
- "--wrap=none" ,
128
- ]
129
- pandoc_command = [
130
- pandoc ,
131
- * pandoc_options ,
132
- * [f"-V{ var } " for var in pandoc_variables ],
133
- "--to" ,
134
- "man" ,
135
- temp_file .name ,
136
- "-o" ,
137
- str (output_file ),
138
- ]
139
- pandoc_process = subprocess .run (
140
- pandoc_command , # noqa: S603
141
- stdout = subprocess .PIPE ,
142
- stderr = subprocess .STDOUT ,
143
- text = True ,
144
- check = False ,
145
- )
146
- _log_pandoc_output (pandoc_process .stdout )
147
- logger .info (f"Generated manpage at { output_file } " )
145
+
146
+ for page in self .config .pages :
147
+ try :
148
+ html = "\n \n " .join (self .html_pages [page ["output" ]][input_page ] for input_page in page ["inputs" ])
149
+ except KeyError as error :
150
+ raise PluginError (str (error )) from error
151
+
152
+ if self .config .get ("preprocess" ):
153
+ html = preprocess (html , self .config ["preprocess" ], page ["output" ])
154
+
155
+ output_file = Path (config .config_file_path ).parent .joinpath (page ["output" ])
156
+ output_file .parent .mkdir (parents = True , exist_ok = True )
157
+ section = output_file .suffix [1 :]
158
+ section_header = page .get ("header" , section_headers .get (section , section_headers ["1" ]))
159
+ title = page .get ("title" , self .mkdocs_config .site_name )
160
+
161
+ with tempfile .NamedTemporaryFile ("w" , prefix = "mkdocs_manpage_" , suffix = ".1.html" ) as temp_file :
162
+ temp_file .write (html )
163
+ pandoc_variables = [
164
+ f"title:{ title } " ,
165
+ f"section:{ section } " ,
166
+ f"date:{ date .today ().strftime ('%Y-%m-%d' )} " , # noqa: DTZ011
167
+ f"footer:mkdocs-manpage v{ metadata .version ('mkdocs-manpage' )} " ,
168
+ f"header:{ section_header } " ,
169
+ ]
170
+ pandoc_options = [
171
+ "--verbose" ,
172
+ "--standalone" ,
173
+ "--wrap=none" ,
174
+ ]
175
+ pandoc_command = [
176
+ pandoc ,
177
+ * pandoc_options ,
178
+ * [f"-V{ var } " for var in pandoc_variables ],
179
+ "--to" ,
180
+ "man" ,
181
+ temp_file .name ,
182
+ "-o" ,
183
+ str (output_file ),
184
+ ]
185
+ pandoc_process = subprocess .run (
186
+ pandoc_command , # noqa: S603
187
+ stdout = subprocess .PIPE ,
188
+ stderr = subprocess .STDOUT ,
189
+ text = True ,
190
+ check = False ,
191
+ )
192
+ _log_pandoc_output (pandoc_process .stdout )
193
+ logger .info (f"Generated manpage { output_file } " )
0 commit comments