1
0
mirror of https://github.com/httpie/cli.git synced 2026-04-26 20:02:11 +02:00
Files
httpie-cli/extras/scripts/generate_man_pages.py
T

157 lines
4.4 KiB
Python
Raw Normal View History

2022-04-14 17:43:10 +03:00
import re
from contextlib import contextmanager
from pathlib import Path
from typing import Optional, Iterator, Iterable
import httpie
from httpie.cli.definition import options as core_options
from httpie.cli.options import ParserSpec
from httpie.manager.cli import options as manager_options
from httpie.output.ui.rich_help import OptionsHighlighter, to_usage
from httpie.output.ui.rich_utils import render_as_string
from httpie.utils import split
# Escape certain characters so they are rendered properly on
# all terminals.
ESCAPE_MAP = {
"'": "\\'",
'~': '\\~',
'': "\\'",
'\\': '\\\\',
}
ESCAPE_MAP = {ord(key): value for key, value in ESCAPE_MAP.items()}
EXTRAS_DIR = Path(__file__).parent.parent
MAN_PAGE_PATH = EXTRAS_DIR / 'man'
OPTION_HIGHLIGHT_RE = re.compile(
OptionsHighlighter.highlights[0]
)
class ManPageBuilder:
def __init__(self):
self.source = []
def title_line(
self,
full_name: str,
program_name: str,
program_version: str,
last_edit_date: str,
) -> None:
self.source.append(
f'.TH {program_name} 1 "{last_edit_date}" '
f'"{full_name} {program_version}" "{full_name} Manual"'
)
def set_name(self, program_name: str) -> None:
with self.section('NAME'):
self.write(program_name)
def write(self, text: str, *, bold: bool = False) -> None:
if bold:
text = '.B ' + text
self.source.append(text)
def separate(self) -> None:
self.source.append('.PP')
def add_options(self, options: Iterable[str], *, metavar: Optional[str] = None) -> None:
text = ", ".join(map(self.boldify, options))
if metavar:
text += f' {self.underline(metavar)}'
self.write(f'.IP "{text}"')
def build(self) -> str:
return '\n'.join(self.source)
@contextmanager
def section(self, section_name: str) -> Iterator[None]:
self.write(f'.SH {section_name}')
self.in_section = True
yield
self.in_section = False
def underline(self, text: str) -> str:
return r'\fI\,{}\/\fR'.format(text)
def boldify(self, text: str) -> str:
return r'\fB\,{}\/\fR'.format(text)
def _escape_and_dedent(text: str) -> str:
lines = []
for should_act, line in enumerate(text.splitlines()):
# Only dedent after the first line.
if should_act:
if line.startswith(' '):
line = line[4:]
lines.append(line)
return '\n'.join(lines).translate(ESCAPE_MAP)
def to_man_page(program_name: str, spec: ParserSpec) -> str:
builder = ManPageBuilder()
builder.title_line(
full_name='HTTPie',
program_name=program_name,
program_version=httpie.__version__,
last_edit_date=httpie.__date__,
)
builder.set_name(program_name)
with builder.section('SYNOPSIS'):
builder.write(render_as_string(to_usage(spec, program_name=program_name)))
with builder.section('DESCRIPTION'):
builder.write(spec.description)
for index, group in enumerate(spec.groups, 1):
with builder.section(group.name):
if group.description:
builder.write(group.description)
for argument in group.arguments:
if argument.is_hidden:
continue
raw_arg = argument.serialize(isolation_mode=True)
metavar = raw_arg.get('metavar')
if raw_arg.get('is_positional'):
# In case of positional arguments, metavar is always equal
# to the list of options (e.g `METHOD`).
metavar = None
builder.add_options(raw_arg['options'], metavar=metavar)
description = _escape_and_dedent(raw_arg.get('description', ''))
description = OPTION_HIGHLIGHT_RE.sub(
lambda match: builder.boldify(match['option']),
description
)
builder.write('\n' + description + '\n')
builder.separate()
return builder.build()
def main() -> None:
for program_name, spec in [
('http', core_options),
('https', core_options),
('httpie', manager_options),
]:
with open((MAN_PAGE_PATH / program_name).with_suffix('.1'), 'w') as stream:
stream.write(to_man_page(program_name, spec))
if __name__ == '__main__':
main()