From a1976450877b49fef09a6c5e181152784b9a0af9 Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Thu, 26 Mar 2020 20:26:57 -0700 Subject: [PATCH] merge the textmate demo into babi --- babi/textmate_demo.py | 69 ++++++++++++++++++++++++++++++ setup.cfg | 1 + tests/textmate_demo_test.py | 84 +++++++++++++++++++++++++++++++++++++ 3 files changed, 154 insertions(+) create mode 100644 babi/textmate_demo.py create mode 100644 tests/textmate_demo_test.py diff --git a/babi/textmate_demo.py b/babi/textmate_demo.py new file mode 100644 index 0000000..c4886ad --- /dev/null +++ b/babi/textmate_demo.py @@ -0,0 +1,69 @@ +import argparse +from typing import Optional +from typing import Sequence + +from babi.highlight import Compiler +from babi.highlight import Grammars +from babi.highlight import highlight_line +from babi.theme import Style +from babi.theme import Theme +from babi.user_data import prefix_data +from babi.user_data import xdg_config + + +def print_styled(s: str, style: Style) -> None: + color_s = '' + undo_s = '' + if style.fg is not None: + color_s += '\x1b[38;2;{r};{g};{b}m'.format(**style.fg._asdict()) + undo_s += '\x1b[39m' + if style.bg is not None: + color_s += '\x1b[48;2;{r};{g};{b}m'.format(**style.bg._asdict()) + undo_s += '\x1b[49m' + if style.b: + color_s += '\x1b[1m' + undo_s += '\x1b[22m' + if style.i: + color_s += '\x1b[3m' + undo_s += '\x1b[23m' + if style.u: + color_s += '\x1b[4m' + undo_s += '\x1b[24m' + print(f'{color_s}{s}{undo_s}', end='', flush=True) + + +def _highlight_output(theme: Theme, compiler: Compiler, filename: str) -> int: + state = compiler.root_state + + if theme.default.bg is not None: + print('\x1b[48;2;{r};{g};{b}m'.format(**theme.default.bg._asdict())) + with open(filename) as f: + for line_idx, line in enumerate(f): + first_line = line_idx == 0 + state, regions = highlight_line(compiler, state, line, first_line) + for start, end, scope in regions: + print_styled(line[start:end], theme.select(scope)) + print('\x1b[m', end='') + return 0 + + +def main(argv: Optional[Sequence[str]] = None) -> int: + parser = argparse.ArgumentParser() + parser.add_argument('--theme', default=xdg_config('theme.json')) + parser.add_argument('--grammar-dir', default=prefix_data('grammar_v1')) + parser.add_argument('filename') + args = parser.parse_args(argv) + + with open(args.filename) as f: + first_line = next(f, '') + + theme = Theme.from_filename(args.theme) + + grammars = Grammars(args.grammar_dir) + compiler = grammars.compiler_for_file(args.filename, first_line) + + return _highlight_output(theme, compiler, args.filename) + + +if __name__ == '__main__': + exit(main()) diff --git a/setup.cfg b/setup.cfg index 8331968..6015c70 100644 --- a/setup.cfg +++ b/setup.cfg @@ -32,6 +32,7 @@ python_requires = >=3.6.1 [options.entry_points] console_scripts = babi = babi.main:main + babi-textmate-demo = babi.textmate_demo:main [options.packages.find] exclude = diff --git a/tests/textmate_demo_test.py b/tests/textmate_demo_test.py new file mode 100644 index 0000000..93a753b --- /dev/null +++ b/tests/textmate_demo_test.py @@ -0,0 +1,84 @@ +import json + +import pytest + +from babi.textmate_demo import main + +THEME = { + 'colors': {'foreground': '#ffffff', 'background': '#000000'}, + 'tokenColors': [ + {'scope': 'bold', 'settings': {'fontStyle': 'bold'}}, + {'scope': 'italic', 'settings': {'fontStyle': 'italic'}}, + {'scope': 'underline', 'settings': {'fontStyle': 'underline'}}, + {'scope': 'comment', 'settings': {'foreground': '#1e77d3'}}, + ], +} + +GRAMMAR = { + 'scopeName': 'source.demo', + 'fileTypes': ['demo'], + 'patterns': [ + {'match': r'\*[^*]*\*', 'name': 'bold'}, + {'match': '/[^/]*/', 'name': 'italic'}, + {'match': '_[^_]*_', 'name': 'underline'}, + {'match': '#.*', 'name': 'comment'}, + ], +} + + +@pytest.fixture +def theme_grammars(tmpdir): + theme = tmpdir.join('config/theme.json').ensure() + theme.write(json.dumps(THEME)) + grammars = tmpdir.join('grammar_v1').ensure_dir() + grammars.join('source.demo.json').write(json.dumps(GRAMMAR)) + return theme, grammars + + +def test_basic(theme_grammars, tmpdir, capsys): + theme, grammars = theme_grammars + + f = tmpdir.join('f.demo') + f.write('*bold*/italic/_underline_# comment\n') + + assert not main(( + '--theme', str(theme), '--grammar-dir', str(grammars), + str(f), + )) + + out, _ = capsys.readouterr() + + assert out == ( + '\x1b[48;2;0;0;0m\n' + '\x1b[38;2;255;255;255m\x1b[48;2;0;0;0m\x1b[1m' + '*bold*' + '\x1b[39m\x1b[49m\x1b[22m' + '\x1b[38;2;255;255;255m\x1b[48;2;0;0;0m\x1b[3m' + '/italic/' + '\x1b[39m\x1b[49m\x1b[23m' + '\x1b[38;2;255;255;255m\x1b[48;2;0;0;0m\x1b[4m' + '_underline_' + '\x1b[39m\x1b[49m\x1b[24m' + '\x1b[38;2;30;119;211m\x1b[48;2;0;0;0m' + '# comment' + '\x1b[39m\x1b[49m\x1b' + '[38;2;255;255;255m\x1b[48;2;0;0;0m\n\x1b[39m\x1b[49m' + '\x1b[m' + ) + + +def test_basic_with_blank_theme(theme_grammars, tmpdir, capsys): + theme, grammars = theme_grammars + theme.write('{}') + + f = tmpdir.join('f.demo') + f.write('*bold*/italic/_underline_# comment\n') + + assert not main(( + '--theme', str(theme), '--grammar-dir', str(grammars), + str(f), + )) + + out, _ = capsys.readouterr() + + assert out == '*bold*/italic/_underline_# comment\n\x1b[m'