def test_while_no_nl():
    compiler, state = _compiler_state({
        'scopeName': 'test',
        'patterns': [{
            'begin': '> ',
            'while': '> ',
            'contentName': 'while',
            'patterns': [
                {'match': r'\Ga', 'name': 'ga'},
                {'match': 'a', 'name': 'noga'},
            ],
        }],
    })

    state, regions1 = highlight_line(compiler, state, '> aa\n', True)
    state, regions2 = highlight_line(compiler, state, '> aa\n', False)
    state, regions3 = highlight_line(compiler, state, 'after\n', False)

    assert regions1 == (
        Region(0, 2, ('test',)),
        Region(2, 3, ('test', 'while', 'ga')),
        Region(3, 4, ('test', 'while', 'noga')),
        Region(4, 5, ('test', 'while')),
    )
    assert regions2 == (
        Region(0, 2, ('test', 'while')),
        Region(2, 3, ('test', 'while', 'ga')),
        Region(3, 4, ('test', 'while', 'noga')),
        Region(4, 5, ('test', 'while')),
    )
    assert regions3 == (
        Region(0, 6, ('test',)),
    )
def test_captures_implies_begin_while_captures():
    compiler, state = _compiler_state({
        'scopeName': 'test',
        'patterns': [
            {
                'begin': '(>) ',
                'while': '(>) ',
                'captures': {'1': {'name': 'block'}},
            },
        ],
    })

    state, regions1 = highlight_line(compiler, state, '> x\n', True)
    state, regions2 = highlight_line(compiler, state, '> x\n', False)

    assert regions1 == (
        Region(0, 1, ('test', 'block')),
        Region(1, 2, ('test',)),
        Region(2, 4, ('test',)),
    )

    assert regions2 == (
        Region(0, 1, ('test', 'block')),
        Region(1, 2, ('test',)),
        Region(2, 4, ('test',)),
    )
def test_include_other_grammar():
    compiler, state = _compiler_state(
        {
            'scopeName': 'test',
            'patterns': [
                {
                    'begin': '<',
                    'end': '>',
                    'name': 'angle',
                    'patterns': [{'include': 'other.grammar'}],
                },
                {
                    'begin': '`',
                    'end': '`',
                    'name': 'tick',
                    'patterns': [{'include': 'other.grammar#backtick'}],
                },
            ],
        },
        {
            'scopeName': 'other.grammar',
            'patterns': [
                {'match': 'a', 'name': 'roota'},
                {'match': '.', 'name': 'rootother'},
            ],
            'repository': {
                'backtick': {
                    'patterns': [
                        {'match': 'a', 'name': 'ticka'},
                        {'match': '.', 'name': 'tickother'},
                    ],
                },
            },
        },
    )

    state, regions1 = highlight_line(compiler, state, '<az>\n', True)
    state, regions2 = highlight_line(compiler, state, '`az`\n', False)

    assert regions1 == (
        Region(0, 1, ('test', 'angle')),
        Region(1, 2, ('test', 'angle', 'roota')),
        Region(2, 3, ('test', 'angle', 'rootother')),
        Region(3, 4, ('test', 'angle')),
        Region(4, 5, ('test',)),
    )

    assert regions2 == (
        Region(0, 1, ('test', 'tick')),
        Region(1, 2, ('test', 'tick', 'ticka')),
        Region(2, 3, ('test', 'tick', 'tickother')),
        Region(3, 4, ('test', 'tick')),
        Region(4, 5, ('test',)),
    )
def test_backslash_a():
    grammar = {
        'scopeName': 'test',
        'patterns': [{'name': 'aaa', 'match': r'\Aa+'}],
    }
    compiler, state = _compiler_state(grammar)

    state, (region_0,) = highlight_line(compiler, state, 'aaa', True)
    state, (region_1,) = highlight_line(compiler, state, 'aaa', False)

    # \A should only match at the beginning of the file
    assert region_0 == Region(0, 3, ('test', 'aaa'))
    assert region_1 == Region(0, 3, ('test',))
def test_backslash_g_captures_nl():
    compiler, state = _compiler_state(BEGIN_END_NL)

    state, regions1 = highlight_line(compiler, state, 'x\n', True)
    state, regions2 = highlight_line(compiler, state, 'aax\n', False)

    assert regions1 == (
        Region(0, 2, ('test',)),
    )
    assert regions2 == (
        Region(0, 1, ('test', 'ga')),
        Region(1, 2, ('test', 'noga')),
        Region(2, 3, ('test',)),
        Region(3, 4, ('test',)),
    )
def test_complex_captures():
    compiler, state = _compiler_state({
        'scopeName': 'test',
        'patterns': [
            {
                'match': '(<).([^>]+)(>)',
                'captures': {
                    '1': {'name': 'lbracket'},
                    '2': {
                        'patterns': [
                            {'match': 'a', 'name': 'a'},
                            {'match': 'z', 'name': 'z'},
                        ],
                    },
                    '3': {'name': 'rbracket'},
                },
            },
        ],
    })

    state, regions = highlight_line(compiler, state, '<qabz>', first_line=True)
    assert regions == (
        Region(0, 1, ('test', 'lbracket')),
        Region(1, 2, ('test',)),
        Region(2, 3, ('test', 'a')),
        Region(3, 4, ('test',)),
        Region(4, 5, ('test', 'z')),
        Region(5, 6, ('test', 'rbracket')),
    )
def test_include_base():
    compiler, state = _compiler_state(
        {
            'scopeName': 'test',
            'patterns': [
                {
                    'begin': '<',
                    'end': '>',
                    'name': 'bracket',
                    # $base from root grammar includes itself
                    'patterns': [{'include': '$base'}],
                },
                {'include': 'other.grammar'},
                {'match': 'z', 'name': 'testz'},
            ],
        },
        {
            'scopeName': 'other.grammar',
            'patterns': [
                {
                    'begin': '`',
                    'end': '`',
                    'name': 'tick',
                    # $base from included grammar includes the root
                    'patterns': [{'include': '$base'}],
                },
            ],
        },
    )

    state, regions1 = highlight_line(compiler, state, '<z>\n', True)
    state, regions2 = highlight_line(compiler, state, '`z`\n', False)

    assert regions1 == (
        Region(0, 1, ('test', 'bracket')),
        Region(1, 2, ('test', 'bracket', 'testz')),
        Region(2, 3, ('test', 'bracket')),
        Region(3, 4, ('test',)),
    )

    assert regions2 == (
        Region(0, 1, ('test', 'tick')),
        Region(1, 2, ('test', 'tick', 'testz')),
        Region(2, 3, ('test', 'tick')),
        Region(3, 4, ('test',)),
    )
def test_backslash_g_inline():
    compiler, state = _compiler_state(BEGIN_END_NO_NL)

    _, regions = highlight_line(compiler, state, 'xaax', True)
    assert regions == (
        Region(0, 1, ('test',)),
        Region(1, 2, ('test', 'ga')),
        Region(2, 3, ('test', 'noga')),
        Region(3, 4, ('test',)),
    )
def test_captures_ignores_invalid_out_of_bounds():
    compiler, state = _compiler_state({
        'scopeName': 'test',
        'patterns': [{'match': '.', 'captures': {'1': {'name': 'oob'}}}],
    })

    state, regions = highlight_line(compiler, state, 'x', first_line=True)

    assert regions == (
        Region(0, 1, ('test',)),
    )
def test_end_before_other_match():
    compiler, state = _compiler_state(BEGIN_END_NO_NL)

    state, regions = highlight_line(compiler, state, 'xazzx', True)

    assert regions == (
        Region(0, 1, ('test',)),
        Region(1, 2, ('test', 'ga')),
        Region(2, 4, ('test',)),
        Region(4, 5, ('test',)),
    )
def test_captures_ignores_empty():
    compiler, state = _compiler_state({
        'scopeName': 'test',
        'patterns': [{
            'match': '(.*) hi',
            'captures': {'1': {'name': 'before'}},
        }],
    })

    state, regions1 = highlight_line(compiler, state, ' hi\n', True)
    state, regions2 = highlight_line(compiler, state, 'o hi\n', False)

    assert regions1 == (
        Region(0, 3, ('test',)),
        Region(3, 4, ('test',)),
    )
    assert regions2 == (
        Region(0, 1, ('test', 'before')),
        Region(1, 4, ('test',)),
        Region(4, 5, ('test',)),
    )
Ejemplo n.º 12
0
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 test_begin_end_substitute_special_chars():
    compiler, state = _compiler_state({
        'scopeName': 'test',
        'patterns': [{'begin': r'(\*)', 'end': r'\1', 'name': 'italic'}],
    })

    state, regions = highlight_line(compiler, state, '*italic*', True)

    assert regions == (
        Region(0, 1, ('test', 'italic')),
        Region(1, 7, ('test', 'italic')),
        Region(7, 8, ('test', 'italic')),
    )
def test_captures_implies_begin_end_captures():
    compiler, state = _compiler_state({
        'scopeName': 'test',
        'patterns': [
            {
                'begin': '(""")',
                'end': '(""")',
                'captures': {'1': {'name': 'quote'}},
            },
        ],
    })

    state, regions = highlight_line(compiler, state, '"""x"""', True)

    assert regions == (
        Region(0, 3, ('test', 'quote')),
        Region(3, 4, ('test',)),
        Region(4, 7, ('test', 'quote')),
    )
def test_include_repository_rule():
    compiler, state = _compiler_state({
        'scopeName': 'test',
        'patterns': [{'include': '#impl'}],
        'repository': {
            'impl': {
                'patterns': [
                    {'match': 'a', 'name': 'a'},
                    {'match': '.', 'name': 'other'},
                ],
            },
        },
    })

    state, regions = highlight_line(compiler, state, 'az', first_line=True)

    assert regions == (
        Region(0, 1, ('test', 'a')),
        Region(1, 2, ('test', 'other')),
    )
def test_captures_multiple_applied_to_same_capture():
    compiler, state = _compiler_state({
        'scopeName': 'test',
        'patterns': [
            {
                'match': '((a)) ((b) c) (d (e)) ((f) )',
                'name': 'matched',
                'captures': {
                    '1': {'name': 'g1'},
                    '2': {'name': 'g2'},
                    '3': {'name': 'g3'},
                    '4': {'name': 'g4'},
                    '5': {'name': 'g5'},
                    '6': {'name': 'g6'},
                    '7': {
                        'patterns': [
                            {'match': 'f', 'name': 'g7f'},
                            {'match': ' ', 'name': 'g7space'},
                        ],
                    },
                    # this one has to backtrack some
                    '8': {'name': 'g8'},
                },
            },
        ],
    })

    state, regions = highlight_line(compiler, state, 'a b c d e f ', True)

    assert regions == (
        Region(0, 1, ('test', 'matched', 'g1', 'g2')),
        Region(1, 2, ('test', 'matched')),
        Region(2, 3, ('test', 'matched', 'g3', 'g4')),
        Region(3, 5, ('test', 'matched', 'g3')),
        Region(5, 6, ('test', 'matched')),
        Region(6, 8, ('test', 'matched', 'g5')),
        Region(8, 9, ('test', 'matched', 'g5', 'g6')),
        Region(9, 10, ('test', 'matched')),
        Region(10, 11, ('test', 'matched', 'g7f', 'g8')),
        Region(11, 12, ('test', 'matched', 'g7space')),
    )
def test_include_self():
    compiler, state = _compiler_state({
        'scopeName': 'test',
        'patterns': [
            {
                'begin': '<',
                'end': '>',
                'contentName': 'bracketed',
                'patterns': [{'include': '$self'}],
            },
            {'match': '.', 'name': 'content'},
        ],
    })

    state, regions = highlight_line(compiler, state, '<<_>>', first_line=True)
    assert regions == (
        Region(0, 1, ('test',)),
        Region(1, 2, ('test', 'bracketed')),
        Region(2, 3, ('test', 'bracketed', 'bracketed', 'content')),
        Region(3, 4, ('test', 'bracketed', 'bracketed')),
        Region(4, 5, ('test', 'bracketed')),
    )
def test_rule_with_begin_and_no_end():
    compiler, state = _compiler_state({
        'scopeName': 'test',
        'patterns': [
            {
                'begin': '!', 'end': '!', 'name': 'bang',
                'patterns': [{'begin': '--', 'name': 'invalid'}],
            },
        ],
    })

    state, regions = highlight_line(compiler, state, '!x! !--!', True)

    assert regions == (
        Region(0, 1, ('test', 'bang')),
        Region(1, 2, ('test', 'bang')),
        Region(2, 3, ('test', 'bang')),
        Region(3, 4, ('test',)),
        Region(4, 5, ('test', 'bang')),
        Region(5, 7, ('test', 'bang', 'invalid')),
        Region(7, 8, ('test', 'bang', 'invalid')),
    )