//! `koh man` — the manual, built into the binary. //! //! The page lives in `docs/koh3-man.txt` and is embedded at build time, so a //! `koh` copied to another machine still carries its own documentation. With a //! command name it prints just that command's part; otherwise the whole page, //! through the built-in pager on a terminal. module cli import os import src.fmt import src.view const manual = $embed_file('../../docs/koh3-man.txt').to_string() pub fn cmd_man(raw []string) int { args := parse('man', raw, Options{}) or { return usage_error(err.msg()) } mut text := manual if args.positional.len > 0 { name := args.positional_at(0) text = section_of(name) or { eprintln(' ${fmt.red(fmt.glyph_fail)} the manual has no "${name}" — try "koh man" for the whole page') return 2 } } page(text) return 0 } // section_of returns the part of the COMMANDS section that documents one // command: from its synopsis to the next command's. fn section_of(name string) ?string { lines := manual.split_into_lines() mut start := -1 for i, line in lines { if line.starts_with(' koh ') && line.contains(' ${name}') { start = i break } } if start < 0 { return none } mut end := lines.len for i := start + 1; i < lines.len; i++ { trimmed := lines[i].trim_space() if trimmed.starts_with('koh ') && lines[i].starts_with(' ') { end = i break } if lines[i].starts_with('OPTIONS') || lines[i].starts_with('ENVIRONMENT') { end = i break } } return lines[start..end].join('\n') } // page shows the manual in Koh 2's colours, through the built-in pager when it // is longer than the terminal, and plainly when the output is going somewhere // else. fn page(text string) { lines := text.trim_right('\n').split_into_lines().map(manual_line(it)) rows, _ := view.terminal_size() if os.is_atty(1) != 0 && os.is_atty(0) != 0 && lines.len >= rows { view.page_manual(lines) return } for line in lines { println(line.text) } } // manual_line colours one line as Koh 2's `colorManLine` does: headings blue // behind a ◆, command lines amber, lead-ins ending in a colon orange, and body // text light with its `--flags` picked out. fn manual_line(line string) view.ManualLine { trimmed := line.trim_left(' ') if line != '' && !line.starts_with(' ') && line == line.to_upper() { return view.ManualLine{ text: if fmt.colour_enabled() { fmt.blue('◆ ${line}') } else { line } section: line } } if trimmed.starts_with('koh ') { return view.ManualLine{ text: fmt.gold(line) } } if trimmed != line && trimmed.ends_with(':') { return view.ManualLine{ text: fmt.warning(line) } } return view.ManualLine{ text: line[..line.len - trimmed.len] + body_text(trimmed) } } // body_text paints body text light, with each `--flag` in it amber. fn body_text(text string) string { mut out := '' mut rest := text for { at := rest.index('--') or { break } mut end := at + 2 for end < rest.len && rest[end] !in [` `, `,`, `)`, `.`, `\``] { end++ } if end == at + 2 { out += fmt.light(rest[..end]) } else { out += fmt.light(rest[..at]) + fmt.gold(rest[at..end]) } rest = rest[end..] } return out + fmt.light(rest) }