cmd_man.v

3.2 KB · raw

1//! `koh man` — the manual, built into the binary.2//!3//! The page lives in `docs/koh3-man.txt` and is embedded at build time, so a4//! `koh` copied to another machine still carries its own documentation. With a5//! command name it prints just that command's part; otherwise the whole page,6//! through the built-in pager on a terminal.78module cli910import os11import src.fmt12import src.view1314const manual = $embed_file('../../docs/koh3-man.txt').to_string()1516pub fn cmd_man(raw []string) int {17	args := parse('man', raw, Options{}) or { return usage_error(err.msg()) }18	mut text := manual19	if args.positional.len > 0 {20		name := args.positional_at(0)21		text = section_of(name) or {22			eprintln('  ${fmt.red(fmt.glyph_fail)} the manual has no "${name}" — try "koh man" for the whole page')23			return 224		}25	}26	page(text)27	return 028}2930// section_of returns the part of the COMMANDS section that documents one31// command: from its synopsis to the next command's.32fn section_of(name string) ?string {33	lines := manual.split_into_lines()34	mut start := -135	for i, line in lines {36		if line.starts_with('    koh ') && line.contains(' ${name}') {37			start = i38			break39		}40	}41	if start < 0 {42		return none43	}44	mut end := lines.len45	for i := start + 1; i < lines.len; i++ {46		trimmed := lines[i].trim_space()47		if trimmed.starts_with('koh ') && lines[i].starts_with('    ') {48			end = i49			break50		}51		if lines[i].starts_with('OPTIONS') || lines[i].starts_with('ENVIRONMENT') {52			end = i53			break54		}55	}56	return lines[start..end].join('\n')57}5859// page shows the manual in Koh 2's colours, through the built-in pager when it60// is longer than the terminal, and plainly when the output is going somewhere61// else.62fn page(text string) {63	lines := text.trim_right('\n').split_into_lines().map(manual_line(it))64	rows, _ := view.terminal_size()65	if os.is_atty(1) != 0 && os.is_atty(0) != 0 && lines.len >= rows {66		view.page_manual(lines)67		return68	}69	for line in lines {70		println(line.text)71	}72}7374// manual_line colours one line as Koh 2's `colorManLine` does: headings blue75// behind a ◆, command lines amber, lead-ins ending in a colon orange, and body76// text light with its `--flags` picked out.77fn manual_line(line string) view.ManualLine {78	trimmed := line.trim_left(' ')79	if line != '' && !line.starts_with(' ') && line == line.to_upper() {80		return view.ManualLine{81			text:    if fmt.colour_enabled() { fmt.blue('◆ ${line}') } else { line }82			section: line83		}84	}85	if trimmed.starts_with('koh ') {86		return view.ManualLine{87			text: fmt.gold(line)88		}89	}90	if trimmed != line && trimmed.ends_with(':') {91		return view.ManualLine{92			text: fmt.warning(line)93		}94	}95	return view.ManualLine{96		text: line[..line.len - trimmed.len] + body_text(trimmed)97	}98}99100// body_text paints body text light, with each `--flag` in it amber.101fn body_text(text string) string {102	mut out := ''103	mut rest := text104	for {105		at := rest.index('--') or { break }106		mut end := at + 2107		for end < rest.len && rest[end] !in [` `, `,`, `)`, `.`, `\``] {108			end++109		}110		if end == at + 2 {111			out += fmt.light(rest[..end])112		} else {113			out += fmt.light(rest[..at]) + fmt.gold(rest[at..end])114		}115		rest = rest[end..]116	}117	return out + fmt.light(rest)118}