1from __future__ import annotations 2 3import argparse 4from typing import Sequence 5 6from babi.highlight import Compiler 7from babi.highlight import Grammars 8from babi.highlight import highlight_line 9from babi.theme import Style 10from babi.theme import Theme 11from babi.user_data import prefix_data 12from babi.user_data import xdg_config 13 14 15def print_styled(s: str, style: Style) -> None: 16 color_s = '' 17 undo_s = '' 18 if style.fg is not None: 19 color_s += '\x1b[38;2;{r};{g};{b}m'.format(**style.fg._asdict()) 20 undo_s += '\x1b[39m' 21 if style.bg is not None: 22 color_s += '\x1b[48;2;{r};{g};{b}m'.format(**style.bg._asdict()) 23 undo_s += '\x1b[49m' 24 if style.b: 25 color_s += '\x1b[1m' 26 undo_s += '\x1b[22m' 27 if style.i: 28 color_s += '\x1b[3m' 29 undo_s += '\x1b[23m' 30 if style.u: 31 color_s += '\x1b[4m' 32 undo_s += '\x1b[24m' 33 print(f'{color_s}{s}{undo_s}', end='', flush=True) 34 35 36def _highlight_output(theme: Theme, compiler: Compiler, filename: str) -> int: 37 state = compiler.root_state 38 39 if theme.default.bg is not None: 40 print('\x1b[48;2;{r};{g};{b}m'.format(**theme.default.bg._asdict())) 41 with open(filename, encoding='UTF-8') as f: 42 for line_idx, line in enumerate(f): 43 first_line = line_idx == 0 44 state, regions = highlight_line(compiler, state, line, first_line) 45 for start, end, scope in regions: 46 print_styled(line[start:end], theme.select(scope)) 47 print('\x1b[m', end='') 48 return 0 49 50 51def main(argv: Sequence[str] | None = None) -> int: 52 parser = argparse.ArgumentParser() 53 parser.add_argument('--theme', default=xdg_config('theme.json')) 54 parser.add_argument('--grammar-dir', default=prefix_data('grammar_v1')) 55 parser.add_argument('filename') 56 args = parser.parse_args(argv) 57 58 with open(args.filename, encoding='UTF-8') as f: 59 first_line = next(f, '') 60 61 theme = Theme.from_filename(args.theme) 62 63 grammars = Grammars(args.grammar_dir) 64 compiler = grammars.compiler_for_file(args.filename, first_line) 65 66 return _highlight_output(theme, compiler, args.filename) 67 68 69if __name__ == '__main__': 70 exit(main()) 71