Something went wrong. Try again.
Terminal markdown pager with syntax highlighting
Something went wrong. Try again.
8.0 kB · 255 lines
TypeScript
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256import { APP_NAME } from "./constants.js";
export interface CliOptions { help: boolean; version: boolean; initConfig: boolean; light: boolean; dark: boolean; lineNumbers: boolean; width: number; depth: number; completion: string | null; source: string | null;}
const VERSION = "0.1.0";
export function parseArgs(args: string[]): CliOptions { const options: CliOptions = { help: false, version: false, initConfig: false, light: false, dark: false, lineNumbers: false, width: 100, depth: 1, completion: null, source: null, };
for (let i = 0; i < args.length; i++) { const arg = args[i];
if (arg === "-h" || arg === "--help") { options.help = true; } else if (arg === "-v" || arg === "--version") { options.version = true; } else if (arg === "--init-config" || arg === "--config") { options.initConfig = true; } else if (arg === "--light") { options.light = true; } else if (arg === "--dark") { options.dark = true; } else if (arg === "-l" || arg === "--line-numbers") { options.lineNumbers = true; } else if (arg === "-w" || arg === "--width") { const next = args[i + 1]; if (next !== undefined && !next.startsWith("-")) { options.width = parseInt(next, 10); i++; } } else if (arg?.startsWith("--width=")) { options.width = parseInt(arg.slice(8), 10); } else if (arg === "-d" || arg === "--depth") { const next = args[i + 1]; if (next !== undefined && !next.startsWith("-")) { const n = parseInt(next, 10); options.depth = Number.isNaN(n) || n < 1 ? 1 : n; i++; } } else if (arg?.startsWith("--depth=")) { const n = parseInt(arg.slice(8), 10); options.depth = Number.isNaN(n) || n < 1 ? 1 : n; } else if (arg === "--completion") { const next = args[i + 1]; if (next !== undefined && !next.startsWith("-")) { options.completion = next; i++; } } else if (arg?.startsWith("--completion=")) { options.completion = arg.slice(13); } else if (arg && !arg.startsWith("-")) { options.source = arg; } }
return options;}
export function printVersion(): void { console.log(`${APP_NAME} version ${VERSION}`);}
export function printHelp(): void { const dim = (s: string) => `\x1b[2m${s}\x1b[0m`; const bold = (s: string) => `\x1b[1m${s}\x1b[0m`;
console.log(` Render markdown on the CLI, with pizzazz!
${bold("Usage:")} ${APP_NAME} [SOURCE|DIR] [flags] ${APP_NAME} [command]
With no arguments and a TTY, opens a directory browser at the current directory. With a file, renders it in the pager. With a directory, opens the browser there.
${bold("Available Commands:")} completion Generate the autocompletion script for the specified shell config Create the ${APP_NAME} config file
${bold("Flags:")} -h, --help help for ${APP_NAME} -d, --depth uint directory browser recursion depth ${dim("(default: 1, includes direct subdirectories)")} -l, --line-numbers show line numbers --light force light mode --dark force dark mode -v, --version version for ${APP_NAME} -w, --width uint word-wrap at width ${dim("(default: 100, 0 to disable)")}
${bold("Navigation (in browser):")} j, Down Move down k, Up Move up f, PgDn Page down b, PgUp Page up g, Home Go to top G, End Go to bottom / Filter files Enter Open selected file ? Show keyboard shortcuts q, Esc, Ctrl+C Quit
${bold("Navigation (in pager):")} j, Down Scroll down k, Up Scroll up d Scroll down half page u Scroll up half page f, Space, PgDn Scroll down one page b, PgUp Scroll up one page g, Home Go to top G, End Go to bottom / Search in document n, N Next/previous search match e Edit file in $EDITOR ? Show keyboard shortcuts q, Esc, Ctrl+C Quit (back to browser if opened from one)
Use "${APP_NAME} [command] --help" for more information about a command.`);}
export function generateBashCompletion(): string { return `# ${APP_NAME} bash completion# Add this to your .bashrc or .bash_profile:# source <(${APP_NAME} --completion bash)
_${APP_NAME}_completions() { local cur prev opts COMPREPLY=() cur="\${COMP_WORDS[COMP_CWORD]}" prev="\${COMP_WORDS[COMP_CWORD-1]}" opts="-h --help -v --version -l --line-numbers --light --dark -w --width -d --depth --completion --init-config"
case "\${prev}" in --completion) COMPREPLY=( $(compgen -W "bash zsh fish" -- "\${cur}") ) return 0 ;; -w|--width|-d|--depth) return 0 ;; esac
if [[ "\${cur}" == -* ]]; then COMPREPLY=( $(compgen -W "\${opts}" -- "\${cur}") ) return 0 fi
# Complete markdown files and directories local IFS=$'\\n' local files=( $(compgen -f -- "\${cur}") ) local dirs=( $(compgen -d -- "\${cur}") )
COMPREPLY=() for f in "\${files[@]}"; do COMPREPLY+=( "$f" ) done for d in "\${dirs[@]}"; do COMPREPLY+=( "$d/" ) done return 0}
complete -o filenames -F _${APP_NAME}_completions ${APP_NAME}`;}
export function generateZshCompletion(): string { return `#compdef ${APP_NAME}# ${APP_NAME} zsh completion# Add this to your .zshrc:# source <(${APP_NAME} --completion zsh)# Or save to a file in your $fpath
_${APP_NAME}() { _arguments -s \\ '-h[Show help message]' \\ '--help[Show help message]' \\ '-v[Show version]' \\ '--version[Show version]' \\ '-l[Show line numbers]' \\ '--line-numbers[Show line numbers]' \\ '--light[Force light mode]' \\ '--dark[Force dark mode]' \\ '-w[Word-wrap at width]:width:' \\ '--width[Word-wrap at width]:width:' \\ '-d[Browser recursion depth]:depth:' \\ '--depth[Browser recursion depth]:depth:' \\ '--completion[Generate completion script]:shell:(bash zsh fish)' \\ '--init-config[Create default config file]' \\ '*:markdown file or directory:_files -g "*(/) *.md *.markdown *.mdx"'}
# Register the completion functioncompdef _${APP_NAME} ${APP_NAME}`;}
export function generateFishCompletion(): string { return `# ${APP_NAME} fish completion# Add this to your fish config:# ${APP_NAME} --completion fish | source# Or save to ~/.config/fish/completions/${APP_NAME}.fish
complete -c ${APP_NAME} -s h -l help -d 'Show help message'complete -c ${APP_NAME} -s v -l version -d 'Show version'complete -c ${APP_NAME} -s l -l line-numbers -d 'Show line numbers'complete -c ${APP_NAME} -l light -d 'Force light mode'complete -c ${APP_NAME} -l dark -d 'Force dark mode'complete -c ${APP_NAME} -s w -l width -d 'Word-wrap at width' -xcomplete -c ${APP_NAME} -s d -l depth -d 'Browser recursion depth' -xcomplete -c ${APP_NAME} -l completion -d 'Generate completion script' -xa 'bash zsh fish'complete -c ${APP_NAME} -l init-config -d 'Create default config file'complete -c ${APP_NAME} -f -a '(__fish_complete_suffix .md .markdown .mdx)'`;}
export function printCompletion(shell: string): boolean { switch (shell.toLowerCase()) { case "bash": console.log(generateBashCompletion()); return true; case "zsh": console.log(generateZshCompletion()); return true; case "fish": console.log(generateFishCompletion()); return true; default: console.error(`Unknown shell: ${shell}`); console.error("Supported shells: bash, zsh, fish"); return false; }}