def test_pathcompleter_completes_directories_with_only_directories():
    # setup: create a test dir with 10 files
    test_dir = tempfile.mkdtemp()
    write_test_files(test_dir)

    # create a sub directory there
    os.mkdir(os.path.join(test_dir, 'subdir'))

    if not test_dir.endswith(os.path.sep):
        test_dir += os.path.sep

    with chdir(test_dir):
        completer = PathCompleter(only_directories=True)
        doc_text = ''
        doc = Document(doc_text, len(doc_text))
        event = CompleteEvent()
        completions = list(completer.get_completions(doc, event))
        result = [c.text for c in completions]
        assert ['subdir'] == result

    # check that there is no completion when passing a file
    with chdir(test_dir):
        completer = PathCompleter(only_directories=True)
        doc_text = '1'
        doc = Document(doc_text, len(doc_text))
        event = CompleteEvent()
        completions = list(completer.get_completions(doc, event))
        assert [] == completions

    # cleanup
    shutil.rmtree(test_dir)
def test_word_completer_ignore_case():
    completer = WordCompleter(['abc', 'def', 'aaa'], ignore_case=True)
    completions = completer.get_completions(Document('a'), CompleteEvent())
    assert [c.text for c in completions] == ['abc', 'aaa']

    completions = completer.get_completions(Document('A'), CompleteEvent())
    assert [c.text for c in completions] == ['abc', 'aaa']
Example #3
0
    def _get_positions_to_highlight(
            self, document: Document) -> List[Tuple[int, int]]:
        """
        Return a list of (row, col) tuples that need to be highlighted.
        """
        pos: Optional[int]

        # Try for the character under the cursor.
        if document.current_char and document.current_char in self.chars:
            pos = document.find_matching_bracket_position(
                start_pos=document.cursor_position - self.max_cursor_distance,
                end_pos=document.cursor_position + self.max_cursor_distance)

        # Try for the character before the cursor.
        elif (document.char_before_cursor
              and document.char_before_cursor in self._closing_braces
              and document.char_before_cursor in self.chars):
            document = Document(document.text, document.cursor_position - 1)

            pos = document.find_matching_bracket_position(
                start_pos=document.cursor_position - self.max_cursor_distance,
                end_pos=document.cursor_position + self.max_cursor_distance)
        else:
            pos = None

        # Return a list of (row, col) tuples that need to be highlighted.
        if pos:
            pos += document.cursor_position  # pos is relative.
            row, col = document.translate_index_to_position(pos)
            return [(row, col),
                    (document.cursor_position_row,
                     document.cursor_position_col)]
        else:
            return []
def test_pathcompleter_respects_completions_under_min_input_len():
    # setup: create a test dir with 10 files
    test_dir = tempfile.mkdtemp()
    write_test_files(test_dir)

    # min len:1 and no text
    with chdir(test_dir):
        completer = PathCompleter(min_input_len=1)
        doc_text = ''
        doc = Document(doc_text, len(doc_text))
        event = CompleteEvent()
        completions = list(completer.get_completions(doc, event))
        assert [] == completions

    # min len:1 and text of len 1
    with chdir(test_dir):
        completer = PathCompleter(min_input_len=1)
        doc_text = '1'
        doc = Document(doc_text, len(doc_text))
        event = CompleteEvent()
        completions = list(completer.get_completions(doc, event))
        result = [c.text for c in completions]
        assert [''] == result

    # min len:0 and text of len 2
    with chdir(test_dir):
        completer = PathCompleter(min_input_len=0)
        doc_text = '1'
        doc = Document(doc_text, len(doc_text))
        event = CompleteEvent()
        completions = list(completer.get_completions(doc, event))
        result = [c.text for c in completions]
        assert [''] == result

    # create 10 files with a 2 char long name
    for i in range(10):
        with open(os.path.join(test_dir, str(i) * 2), 'wb') as out:
            out.write(b'')

    # min len:1 and text of len 1
    with chdir(test_dir):
        completer = PathCompleter(min_input_len=1)
        doc_text = '2'
        doc = Document(doc_text, len(doc_text))
        event = CompleteEvent()
        completions = list(completer.get_completions(doc, event))
        result = sorted(c.text for c in completions)
        assert ['', '2'] == result

    # min len:2 and text of len 1
    with chdir(test_dir):
        completer = PathCompleter(min_input_len=2)
        doc_text = '2'
        doc = Document(doc_text, len(doc_text))
        event = CompleteEvent()
        completions = list(completer.get_completions(doc, event))
        assert [] == completions

    # cleanup
    shutil.rmtree(test_dir)
def test_word_completer_pattern():
    # With a pattern which support '.'
    completer = WordCompleter(
        ['abc', 'a.b.c', 'a.b', 'xyz'],
        pattern=re.compile(r'^([a-zA-Z0-9_.]+|[^a-zA-Z0-9_.\s]+)'))
    completions = completer.get_completions(Document('a.'), CompleteEvent())
    assert [c.text for c in completions] == ['a.b.c', 'a.b']

    # Without pattern
    completer = WordCompleter(['abc', 'a.b.c', 'a.b', 'xyz'])
    completions = completer.get_completions(Document('a.'), CompleteEvent())
    assert [c.text for c in completions] == []
def test_word_completer_sentence():
    # With sentence=True
    completer = WordCompleter(
        ['hello world', 'www', 'hello www', 'hello there'], sentence=True)
    completions = completer.get_completions(Document('hello w'),
                                            CompleteEvent())
    assert [c.text for c in completions] == ['hello world', 'hello www']

    # With sentence=False
    completer = WordCompleter(
        ['hello world', 'www', 'hello www', 'hello there'], sentence=False)
    completions = completer.get_completions(Document('hello w'),
                                            CompleteEvent())
    assert [c.text for c in completions] == ['www']
def test_cursor_position(document):
    assert document.cursor_position_row == 1
    assert document.cursor_position_col == 3

    d = Document('', 0)
    assert d.cursor_position_row == 0
    assert d.cursor_position_col == 0
Example #8
0
    def validate(self, document: Document) -> None:
        # Parse input document.
        # We use `match`, not `match_prefix`, because for validation, we want
        # the actual, unambiguous interpretation of the input.
        m = self.compiled_grammar.match(document.text)

        if m:
            for v in m.variables():
                validator = self.validators.get(v.varname)

                if validator:
                    # Unescape text.
                    unwrapped_text = self.compiled_grammar.unescape(
                        v.varname, v.value)

                    # Create a document, for the completions API (text/cursor_position)
                    inner_document = Document(unwrapped_text,
                                              len(unwrapped_text))

                    try:
                        validator.validate(inner_document)
                    except ValidationError as e:
                        raise ValidationError(cursor_position=v.start +
                                              e.cursor_position,
                                              message=e.message)
        else:
            raise ValidationError(cursor_position=len(document.text),
                                  message='Invalid command')
Example #9
0
    def _get_completions_for_match(
            self, match: Match,
            complete_event: CompleteEvent) -> Iterable[Completion]:
        """
        Yield all the possible completions for this input string.
        (The completer assumes that the cursor position was at the end of the
        input string.)
        """
        for match_variable in match.end_nodes():
            varname = match_variable.varname
            start = match_variable.start

            completer = self.completers.get(varname)

            if completer:
                text = match_variable.value

                # Unwrap text.
                unwrapped_text = self.compiled_grammar.unescape(varname, text)

                # Create a document, for the completions API (text/cursor_position)
                document = Document(unwrapped_text, len(unwrapped_text))

                # Call completer
                for completion in completer.get_completions(document, complete_event):
                    new_text = unwrapped_text[:len(text) + completion.start_position] + completion.text

                    # Wrap again.
                    yield Completion(
                        text=self.compiled_grammar.escape(varname, new_text),
                        start_position=start - len(match.string),
                        display=completion.display,
                        display_meta=completion.display_meta)
    def get_completions(self, document: Document,
                        complete_event: CompleteEvent) -> Iterable[Completion]:
        # Get list of words.
        words = self.words
        if callable(words):
            words = words()

        # Get word/text before cursor.
        if self.sentence:
            word_before_cursor = document.text_before_cursor
        else:
            word_before_cursor = document.get_word_before_cursor(
                WORD=self.WORD, pattern=self.pattern)

        if self.ignore_case:
            word_before_cursor = word_before_cursor.lower()

        def word_matches(word: str) -> bool:
            """ True when the word before the cursor matches. """
            if self.ignore_case:
                word = word.lower()

            if self.match_middle:
                return word_before_cursor in word
            else:
                return word.startswith(word_before_cursor)

        for a in words:
            if word_matches(a):
                display_meta = self.meta_dict.get(a, '')
                yield Completion(a,
                                 -len(word_before_cursor),
                                 display_meta=display_meta)
def test_completer():
    class completer1(Completer):
        def get_completions(self, document, complete_event):
            yield Completion('before-%s-after' % document.text,
                             -len(document.text))
            yield Completion('before-%s-after-B' % document.text,
                             -len(document.text))

    class completer2(Completer):
        def get_completions(self, document, complete_event):
            yield Completion('before2-%s-after2' % document.text,
                             -len(document.text))
            yield Completion('before2-%s-after2-B' % document.text,
                             -len(document.text))

    # Create grammar.  "var1" + "whitespace" + "var2"
    g = compile(r'(?P<var1>[a-z]*) \s+ (?P<var2>[a-z]*)')

    # Test 'get_completions()'
    completer = GrammarCompleter(g, {
        'var1': completer1(),
        'var2': completer2()
    })
    completions = list(
        completer.get_completions(Document('abc def', len('abc def')),
                                  CompleteEvent()))

    assert len(completions) == 2
    assert completions[0].text == 'before2-def-after2'
    assert completions[0].start_position == -3
    assert completions[1].text == 'before2-def-after2-B'
    assert completions[1].start_position == -3
def test_pathcompleter_can_expanduser():
    completer = PathCompleter(expanduser=True)
    doc_text = '~'
    doc = Document(doc_text, len(doc_text))
    event = CompleteEvent()
    completions = list(completer.get_completions(doc, event))
    assert len(completions) > 0
def test_pathcompleter_does_not_expanduser_by_default():
    completer = PathCompleter()
    doc_text = '~'
    doc = Document(doc_text, len(doc_text))
    event = CompleteEvent()
    completions = list(completer.get_completions(doc, event))
    assert [] == completions
def test_pathcompleter_completes_in_current_directory():
    completer = PathCompleter()
    doc_text = ''
    doc = Document(doc_text, len(doc_text))
    event = CompleteEvent()
    completions = list(completer.get_completions(doc, event))
    assert len(completions) > 0
    def _get_fuzzy_completions(
            self, document: Document,
            complete_event: CompleteEvent) -> Iterable[Completion]:

        word_before_cursor = document.get_word_before_cursor(
            pattern=re.compile(self._get_pattern()))

        # Get completions
        document2 = Document(text=document.text[:document.cursor_position -
                                                len(word_before_cursor)],
                             cursor_position=document.cursor_position -
                             len(word_before_cursor))

        completions = list(
            self.completer.get_completions(document2, complete_event))

        fuzzy_matches: List[_FuzzyMatch] = []

        pat = '.*?'.join(map(re.escape, word_before_cursor))
        pat = '(?=({0}))'.format(
            pat)  # lookahead regex to manage overlapping matches
        regex = re.compile(pat, re.IGNORECASE)
        for compl in completions:
            matches = list(regex.finditer(compl.text))
            if matches:
                # Prefer the match, closest to the left, then shortest.
                best = min(matches, key=lambda m: (m.start(), len(m.group(1))))
                fuzzy_matches.append(
                    _FuzzyMatch(len(best.group(1)), best.start(), compl))

        def sort_key(fuzzy_match: '_FuzzyMatch') -> Tuple[int, int]:
            " Sort by start position, then by the length of the match. "
            return fuzzy_match.start_pos, fuzzy_match.match_length

        fuzzy_matches = sorted(fuzzy_matches, key=sort_key)

        for match in fuzzy_matches:
            # Include these completions, but set the correct `display`
            # attribute and `start_position`.
            yield Completion(match.completion.text,
                             start_position=match.completion.start_position -
                             len(word_before_cursor),
                             display_meta=match.completion.display_meta,
                             display=self._get_display(match,
                                                       word_before_cursor),
                             style=match.completion.style)
def test_word_completer_dynamic_word_list():
    called = [0]

    def get_words():
        called[0] += 1
        return ['abc', 'def', 'aaa']

    completer = WordCompleter(get_words)

    # Dynamic list on empty input.
    completions = completer.get_completions(Document(''), CompleteEvent())
    assert [c.text for c in completions] == ['abc', 'def', 'aaa']
    assert called[0] == 1

    # Static list on non-empty input.
    completions = completer.get_completions(Document('a'), CompleteEvent())
    assert [c.text for c in completions] == ['abc', 'aaa']
    assert called[0] == 2
    def accept(buff):
        # Evaluate "calculator" expression.
        try:
            output = '\n\nIn:  {}\nOut: {}'.format(
                input_field.text,
                eval(input_field.text))  # Don't do 'eval' in real code!
        except BaseException as e:
            output = '\n\n{}'.format(e)
        new_text = output_field.text + output

        # Add text to output buffer.
        output_field.buffer.document = Document(text=new_text,
                                                cursor_position=len(new_text))
def test_word_completer_static_word_list():
    completer = WordCompleter(['abc', 'def', 'aaa'])

    # Static list on empty input.
    completions = completer.get_completions(Document(''), CompleteEvent())
    assert [c.text for c in completions] == ['abc', 'def', 'aaa']

    # Static list on non-empty input.
    completions = completer.get_completions(Document('a'), CompleteEvent())
    assert [c.text for c in completions] == ['abc', 'aaa']

    completions = completer.get_completions(Document('A'), CompleteEvent())
    assert [c.text for c in completions] == []

    # Multiple words ending with space. (Accept all options)
    completions = completer.get_completions(Document('test '), CompleteEvent())
    assert [c.text for c in completions] == ['abc', 'def', 'aaa']

    # Multiple words. (Check last only.)
    completions = completer.get_completions(Document('test a'),
                                            CompleteEvent())
    assert [c.text for c in completions] == ['abc', 'aaa']
def test_fuzzy_completer():
    collection = [
        'migrations.py', 'django_migrations.py', 'django_admin_log.py',
        'api_user.doc', 'user_group.doc', 'users.txt', 'accounts.txt',
        '123.py', 'test123test.py'
    ]
    completer = FuzzyWordCompleter(collection)
    completions = completer.get_completions(Document('txt'), CompleteEvent())
    assert [c.text for c in completions] == ['users.txt', 'accounts.txt']

    completions = completer.get_completions(Document('djmi'), CompleteEvent())
    assert [c.text for c in completions
            ] == ['django_migrations.py', 'django_admin_log.py']

    completions = completer.get_completions(Document('mi'), CompleteEvent())
    assert [c.text for c in completions] == [
        'migrations.py', 'django_migrations.py', 'django_admin_log.py'
    ]

    completions = completer.get_completions(Document('user'), CompleteEvent())
    assert [c.text for c in completions
            ] == ['user_group.doc', 'users.txt', 'api_user.doc']

    completions = completer.get_completions(Document('123'), CompleteEvent())
    assert [c.text for c in completions] == ['123.py', 'test123test.py']

    completions = completer.get_completions(Document('miGr'), CompleteEvent())
    assert [c.text for c in completions] == [
        'migrations.py',
        'django_migrations.py',
    ]

    # Multiple words ending with space. (Accept all options)
    completions = completer.get_completions(Document('test '), CompleteEvent())
    assert [c.text for c in completions] == collection

    # Multiple words. (Check last only.)
    completions = completer.get_completions(Document('test txt'),
                                            CompleteEvent())
    assert [c.text for c in completions] == ['users.txt', 'accounts.txt']
def test_pathcompleter_completes_files_in_current_directory():
    # setup: create a test dir with 10 files
    test_dir = tempfile.mkdtemp()
    write_test_files(test_dir)

    expected = sorted([str(i) for i in range(10)])

    if not test_dir.endswith(os.path.sep):
        test_dir += os.path.sep

    with chdir(test_dir):
        completer = PathCompleter()
        # this should complete on the cwd
        doc_text = ''
        doc = Document(doc_text, len(doc_text))
        event = CompleteEvent()
        completions = list(completer.get_completions(doc, event))
        result = sorted(c.text for c in completions)
        assert expected == result

    # cleanup
    shutil.rmtree(test_dir)
def test_pathcompleter_completes_files_in_absolute_directory():
    # setup: create a test dir with 10 files
    test_dir = tempfile.mkdtemp()
    write_test_files(test_dir)

    expected = sorted([str(i) for i in range(10)])

    test_dir = os.path.abspath(test_dir)
    if not test_dir.endswith(os.path.sep):
        test_dir += os.path.sep

    completer = PathCompleter()
    # force unicode
    doc_text = text_type(test_dir)
    doc = Document(doc_text, len(doc_text))
    event = CompleteEvent()
    completions = list(completer.get_completions(doc, event))
    result = sorted([c.text for c in completions])
    assert expected == result

    # cleanup
    shutil.rmtree(test_dir)
def test_pathcompleter_can_apply_file_filter():
    # setup: create a test dir with 10 files
    test_dir = tempfile.mkdtemp()
    write_test_files(test_dir)

    # add a .csv file
    with open(os.path.join(test_dir, 'my.csv'), 'wb') as out:
        out.write(b'')

    file_filter = lambda f: f and f.endswith('.csv')

    with chdir(test_dir):
        completer = PathCompleter(file_filter=file_filter)
        doc_text = ''
        doc = Document(doc_text, len(doc_text))
        event = CompleteEvent()
        completions = list(completer.get_completions(doc, event))
        result = [c.text for c in completions]
        assert ['my.csv'] == result

    # cleanup
    shutil.rmtree(test_dir)
Example #23
0
    def _get_text_fragments(self, text: str) -> StyleAndTextTuples:
        m = self.compiled_grammar.match_prefix(text)

        if m:
            characters: StyleAndTextTuples = [(self.default_style, c)
                                              for c in text]

            for v in m.variables():
                # If we have a `Lexer` instance for this part of the input.
                # Tokenize recursively and apply tokens.
                lexer = self.lexers.get(v.varname)

                if lexer:
                    document = Document(text[v.start:v.stop])
                    lexer_tokens_for_line = lexer.lex_document(document)
                    text_fragments: StyleAndTextTuples = []
                    for i in range(len(document.lines)):
                        text_fragments.extend(lexer_tokens_for_line(i))
                        text_fragments.append(('', '\n'))
                    if text_fragments:
                        text_fragments.pop()

                    i = v.start
                    for t, s, *_ in text_fragments:
                        for c in s:
                            if characters[i][0] == self.default_style:
                                characters[i] = (t, characters[i][1])
                            i += 1

            # Highlight trailing input.
            trailing_input = m.trailing_input()
            if trailing_input:
                for i in range(trailing_input.start, trailing_input.stop):
                    characters[i] = ('class:trailing-input', characters[i][1])

            return characters
        else:
            return [('', text)]
def test_pathcompleter_get_paths_constrains_path():
    # setup: create a test dir with 10 files
    test_dir = tempfile.mkdtemp()
    write_test_files(test_dir)

    # add a subdir with 10 other files with different names
    subdir = os.path.join(test_dir, 'subdir')
    os.mkdir(subdir)
    write_test_files(subdir, 'abcdefghij')

    get_paths = lambda: ['subdir']

    with chdir(test_dir):
        completer = PathCompleter(get_paths=get_paths)
        doc_text = ''
        doc = Document(doc_text, len(doc_text))
        event = CompleteEvent()
        completions = list(completer.get_completions(doc, event))
        result = [c.text for c in completions]
        expected = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']
        assert expected == result

    # cleanup
    shutil.rmtree(test_dir)
def insert_comment(event: E) -> None:
    """
    Without numeric argument, comment all lines.
    With numeric argument, uncomment all lines.
    In any case accept the input.
    """
    buff = event.current_buffer

    # Transform all lines.
    if event.arg != 1:

        def change(line: str) -> str:
            return line[1:] if line.startswith('#') else line
    else:

        def change(line: str) -> str:
            return '#' + line

    buff.document = Document(text='\n'.join(map(change,
                                                buff.text.splitlines())),
                             cursor_position=0)

    # Accept input.
    buff.validate_and_handle()
Example #26
0
 def text(self, value: str) -> None:
     self.buffer.set_document(Document(value, 0), bypass_readonly=True)
Example #27
0
    def __init__(self,
                 text: str = '',
                 multiline: FilterOrBool = True,
                 password: FilterOrBool = False,
                 lexer: Optional[Lexer] = None,
                 auto_suggest: Optional[AutoSuggest] = None,
                 completer: Optional[Completer] = None,
                 complete_while_typing: FilterOrBool = True,
                 accept_handler: Optional[BufferAcceptHandler] = None,
                 history: Optional[History] = None,
                 focusable: FilterOrBool = True,
                 focus_on_click: FilterOrBool = False,
                 wrap_lines: FilterOrBool = True,
                 read_only: FilterOrBool = False,
                 width: AnyDimension = None,
                 height: AnyDimension = None,
                 dont_extend_height: FilterOrBool = False,
                 dont_extend_width: FilterOrBool = False,
                 line_numbers: bool = False,
                 get_line_prefix: Optional[GetLinePrefixCallable] = None,
                 scrollbar: bool = False,
                 style: str = '',
                 search_field: Optional[SearchToolbar] = None,
                 preview_search: FilterOrBool = True,
                 prompt: AnyFormattedText = '',
                 input_processors: Optional[List[Processor]] = None) -> None:

        if search_field is None:
            search_control = None
        elif isinstance(search_field, SearchToolbar):
            search_control = search_field.control

        if input_processors is None:
            input_processors = []

        # Writeable attributes.
        self.completer = completer
        self.complete_while_typing = complete_while_typing
        self.lexer = lexer
        self.auto_suggest = auto_suggest
        self.read_only = read_only
        self.wrap_lines = wrap_lines

        self.buffer = Buffer(
            document=Document(text, 0),
            multiline=multiline,
            read_only=Condition(lambda: is_true(self.read_only)),
            completer=DynamicCompleter(lambda: self.completer),
            complete_while_typing=Condition(
                lambda: is_true(self.complete_while_typing)),
            auto_suggest=DynamicAutoSuggest(lambda: self.auto_suggest),
            accept_handler=accept_handler,
            history=history)

        self.control = BufferControl(
            buffer=self.buffer,
            lexer=DynamicLexer(lambda: self.lexer),
            input_processors=[
                ConditionalProcessor(AppendAutoSuggestion(),
                                     has_focus(self.buffer) & ~is_done),
                ConditionalProcessor(processor=PasswordProcessor(),
                                     filter=to_filter(password)),
                BeforeInput(prompt, style='class:text-area.prompt'),
            ] + input_processors,
            search_buffer_control=search_control,
            preview_search=preview_search,
            focusable=focusable,
            focus_on_click=focus_on_click)

        if multiline:
            if scrollbar:
                right_margins = [ScrollbarMargin(display_arrows=True)]
            else:
                right_margins = []
            if line_numbers:
                left_margins = [NumberedMargin()]
            else:
                left_margins = []
        else:
            height = D.exact(1)
            left_margins = []
            right_margins = []

        style = 'class:text-area ' + style

        self.window = Window(
            height=height,
            width=width,
            dont_extend_height=dont_extend_height,
            dont_extend_width=dont_extend_width,
            content=self.control,
            style=style,
            wrap_lines=Condition(lambda: is_true(self.wrap_lines)),
            left_margins=left_margins,
            right_margins=right_margins,
            get_line_prefix=get_line_prefix)
def document():
    return Document('line 1\n' + 'line 2\n' + 'line 3\n' + 'line 4\n',
                    len('line 1\n' + 'lin'))
def test_word_completer_match_middle():
    completer = WordCompleter(['abc', 'def', 'abca'], match_middle=True)
    completions = completer.get_completions(Document('bc'), CompleteEvent())
    assert [c.text for c in completions] == ['abc', 'abca']
Example #30
0
    "[q] Quit [a] Focus left top [b] Right top [c] Left bottom [d] Right bottom."
)

LIPSUM = """Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Maecenas quis interdum enim. Nam viverra, mauris et blandit malesuada, ante est
bibendum mauris, ac dignissim dui tellus quis ligula. Aenean condimentum leo at
dignissim placerat. In vel dictum ex, vulputate accumsan mi. Donec ut quam
placerat massa tempor elementum. Sed tristique mauris ac suscipit euismod. Ut
tempus vehicula augue non venenatis. Mauris aliquam velit turpis, nec congue
risus aliquam sit amet. Pellentesque blandit scelerisque felis, faucibus
consequat ante. Curabitur tempor tortor a imperdiet tincidunt. Nam sed justo
sit amet odio bibendum congue. Quisque varius ligula nec ligula gravida, sed
convallis augue faucibus. Nunc ornare pharetra bibendum. Praesent blandit ex
quis sodales maximus. """

left_top = Window(BufferControl(Buffer(document=Document(LIPSUM))))
left_bottom = Window(BufferControl(Buffer(document=Document(LIPSUM))))
right_top = Window(BufferControl(Buffer(document=Document(LIPSUM))))
right_bottom = Window(BufferControl(Buffer(document=Document(LIPSUM))))

body = HSplit([
    Window(FormattedTextControl(top_text), height=2, style='reverse'),
    Window(height=1, char='-'),  # Horizontal line in the middle.
    VSplit([left_top, Window(width=1, char='|'), right_top]),
    Window(height=1, char='-'),  # Horizontal line in the middle.
    VSplit([left_bottom, Window(width=1, char='|'), right_bottom]),
])

# 2. Key bindings
kb = KeyBindings()