class RollingLinesWindow: def __init__(self, limit: int, *, lexer: 'Optional[Lexer]' = None, wrap_lines: bool = False, dont_extend_height: bool = False, last_line_return: bool = False): self.last_line_return = last_line_return self.buffer = Buffer(read_only=True) self.string_buffer = RollingLinesBuffer(limit) self.window = Window(content=BufferControl(buffer=self.buffer, lexer=lexer, focusable=True), wrap_lines=wrap_lines, dont_extend_height=dont_extend_height) def append(self, *lines: str): if self.string_buffer.append(*lines): cursor_pos = None new_text = self.string_buffer.get() if self.last_line_return: new_text += "\n" if self.buffer.cursor_position < len(self.buffer.text): cursor_pos = self.buffer.cursor_position self.buffer.set_document(Document(text=new_text, cursor_position=cursor_pos), bypass_readonly=True) def __pt_container__(self): return self.window
def test_undo_capitalize(self): buffer = Buffer() text = u'Selec' buffer.set_document(Document(text, len(text))) self.capitalizer.apply_capitalization(buffer) self.assertEqual(u'Selec', buffer.text) text = buffer.text + 't' buffer.set_document(Document(text, len(text))) self.capitalizer.apply_capitalization(buffer) self.assertEqual(u'SELECT', buffer.text) text = buffer.text + 'i' buffer.set_document(Document(text, len(text))) self.capitalizer.apply_capitalization(buffer) self.assertEqual(u'Selecti', buffer.text) text = buffer.text + 'on' buffer.set_document(Document(text, len(text))) self.capitalizer.apply_capitalization(buffer) self.assertEqual(u'Selection', buffer.text)
def test_undo_capitalize(self): buffer = Buffer() text = 'Selec' buffer.set_document(Document(text, len(text))) self.capitalizer.apply_capitalization(buffer) self.assertEqual('Selec', buffer.text) text = buffer.text + 't' buffer.set_document(Document(text, len(text))) self.capitalizer.apply_capitalization(buffer) self.assertEqual('SELECT', buffer.text) text = buffer.text + 'i' buffer.set_document(Document(text, len(text))) self.capitalizer.apply_capitalization(buffer) self.assertEqual('Selecti', buffer.text) text = buffer.text + 'on' buffer.set_document(Document(text, len(text))) self.capitalizer.apply_capitalization(buffer) self.assertEqual('Selection', buffer.text)
class Terminal(object): """ Terminal widget for use in a prompt_toolkit layout. """ def __init__(self, command=['/bin/bash'], before_exec_func=None, bell_func=None, style='', width=None, height=None, done_callback=None): self.terminal_control = _TerminalControl( command=command, before_exec_func=before_exec_func, bell_func=bell_func, done_callback=done_callback) self.terminal_window = _Window(terminal_control=self.terminal_control, content=self.terminal_control, wrap_lines=False) # Key bindigns for copy buffer. kb = KeyBindings() @kb.add('c-c') def _(event): self.exit_copy_mode() @kb.add('space') def _(event): " Reset selection. " event.current_buffer.start_selection() @kb.add('enter', filter=has_selection) def _(event): " Reset selection. " data = event.current_buffer.copy_selection() event.app.clipboard.set_data(data) self.search_toolbar = SearchToolbar( forward_search_prompt='Search down: ', backward_search_prompt='Search up: ') self.copy_buffer = Buffer(read_only=True) self.copy_buffer_control = BufferControl( buffer=self.copy_buffer, search_buffer_control=self.search_toolbar.control, include_default_input_processors=False, input_processors=[ _UseStyledTextProcessor(self), HighlightSelectionProcessor(), HighlightSearchProcessor(), HighlightIncrementalSearchProcessor(), ], preview_search= True, # XXX: not sure why we need twice preview_search. key_bindings=kb) self.copy_window = Window(content=self.copy_buffer_control, wrap_lines=False) self.is_copying = False self.styled_copy_lines = [ ] # List of lists of (style, text) tuples, for each line. @Condition def is_copying(): return self.is_copying self.container = FloatContainer( content=HSplit( [ # Either show terminal window or copy buffer. VSplit([ # XXX: this nested VSplit should not have been necessary, # but the ConditionalContainer which width can become # zero will collapse the other elements. ConditionalContainer(self.terminal_window, filter=~is_copying), ConditionalContainer(self.copy_window, filter=is_copying), ]), ConditionalContainer(self.search_toolbar, filter=is_copying), ], style=style, width=width, height=height), floats=[ Float(top=0, right=0, height=1, content=ConditionalContainer(Window( content=FormattedTextControl( text=self._copy_position_formatted_text), style='class:copy-mode-cursor-position'), filter=is_copying)) ]) def _copy_position_formatted_text(self): """ Return the cursor position text to be displayed in copy mode. """ render_info = self.copy_window.render_info if render_info: return '[%s/%s]' % (render_info.cursor_position.y + 1, render_info.content_height) else: return '[0/0]' def enter_copy_mode(self): # Suspend process. self.terminal_control.process.suspend() # Copy content into copy buffer. data_buffer = self.terminal_control.process.screen.pt_screen.data_buffer text = [] styled_lines = [] if data_buffer: for line_index in range(min(data_buffer), max(data_buffer) + 1): line = data_buffer[line_index] styled_line = [] if line: for column_index in range(0, max(line) + 1): char = line[column_index] text.append(char.char) styled_line.append((char.style, char.char)) text.append('\n') styled_lines.append(styled_line) text.pop() # Drop last line ending. text = ''.join(text) self.copy_buffer.set_document(Document(text=text, cursor_position=len(text)), bypass_readonly=True) self.styled_lines = styled_lines # Enter copy mode. self.is_copying = True get_app().layout.focus(self.copy_window) def exit_copy_mode(self): # Resume process. self.terminal_control.process.resume() # focus terminal again. self.is_copying = False get_app().layout.focus(self.terminal_window) def __pt_container__(self): return self.container @property def process(self): return self.terminal_control.process
class TextArea: """ A simple input field. This is a higher level abstraction on top of several other classes with sane defaults. This widget does have the most common options, but it does not intend to cover every single use case. For more configurations options, you can always build a text area manually, using a :class:`~prompt_toolkit.buffer.Buffer`, :class:`~prompt_toolkit.layout.BufferControl` and :class:`~prompt_toolkit.layout.Window`. Buffer attributes: :param text: The initial text. :param multiline: If True, allow multiline input. :param completer: :class:`~prompt_toolkit.completion.Completer` instance for auto completion. :param complete_while_typing: Boolean. :param accept_handler: Called when `Enter` is pressed (This should be a callable that takes a buffer as input). :param history: :class:`~prompt_toolkit.history.History` instance. :param auto_suggest: :class:`~prompt_toolkit.auto_suggest.AutoSuggest` instance for input suggestions. BufferControl attributes: :param password: When `True`, display using asterisks. :param focusable: When `True`, allow this widget to receive the focus. :param focus_on_click: When `True`, focus after mouse click. :param input_processors: `None` or a list of :class:`~prompt_toolkit.layout.Processor` objects. Window attributes: :param lexer: :class:`~prompt_toolkit.lexers.Lexer` instance for syntax highlighting. :param wrap_lines: When `True`, don't scroll horizontally, but wrap lines. :param width: Window width. (:class:`~prompt_toolkit.layout.Dimension` object.) :param height: Window height. (:class:`~prompt_toolkit.layout.Dimension` object.) :param scrollbar: When `True`, display a scroll bar. :param style: A style string. :param dont_extend_width: When `True`, don't take up more width then the preferred width reported by the control. :param dont_extend_height: When `True`, don't take up more width then the preferred height reported by the control. :param get_line_prefix: None or a callable that returns formatted text to be inserted before a line. It takes a line number (int) and a wrap_count and returns formatted text. This can be used for implementation of line continuations, things like Vim "breakindent" and so on. Other attributes: :param search_field: An optional `SearchToolbar` object. """ 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) @property def text(self) -> str: """ The `Buffer` text. """ return self.buffer.text @text.setter def text(self, value: str) -> None: self.buffer.set_document(Document(value, 0), bypass_readonly=True) @property def document(self) -> Document: """ The `Buffer` document (text + cursor position). """ return self.buffer.document @document.setter def document(self, value: Document) -> None: self.buffer.document = value @property def accept_handler(self) -> Optional[BufferAcceptHandler]: """ The accept handler. Called when the user accepts the input. """ return self.buffer.accept_handler @accept_handler.setter def accept_handler(self, value: BufferAcceptHandler) -> None: self.buffer.accept_handler = value def __pt_container__(self) -> Container: return self.window
def test_capitalize(self): buffer = Buffer() text = u'selec' buffer.set_document(Document(text, len(text))) self.capitalizer.apply_capitalization(buffer) self.assertEqual(u'selec', buffer.text) text = u'select' buffer.set_document(Document(text, len(text))) self.capitalizer.apply_capitalization(buffer) self.assertEqual(u'SELECT', buffer.text) text = u'CREATE TABLE "select' buffer.set_document(Document(text, len(text))) self.capitalizer.apply_capitalization(buffer) self.assertEqual(u'CREATE TABLE "select', buffer.text) text = u'CREATE TABLE \'select\'' buffer.set_document(Document(text, len(text))) self.capitalizer.apply_capitalization(buffer) self.assertEqual(u'CREATE TABLE \'select\'', buffer.text) text = u'create table test (x int)' buffer.set_document(Document(text, len(text))) self.capitalizer.apply_capitalization(buffer) self.assertEqual(u'CREATE TABLE test (x INT)', buffer.text) text = u'create table test (a boolean, b string, c integer)' buffer.set_document(Document(text, len(text))) self.capitalizer.apply_capitalization(buffer) self.assertEqual(u'CREATE TABLE test (a BOOLEAN, b STRING, c INTEGER)', buffer.text) text = u'create table test\n(a boolean, b string, c integer)' buffer.set_document(Document(text, len(text))) self.capitalizer.apply_capitalization(buffer) self.assertEqual( u'CREATE TABLE test\n(a BOOLEAN, b STRING, c INTEGER)', buffer.text) text = u'\select dynamic' buffer.set_document(Document(text, len(text))) self.capitalizer.apply_capitalization(buffer) self.assertEqual(u'\select dynamic', buffer.text)
class InputField: """Single line of user-editable text""" def __init__(self, text='', width=None, extend_width=True, read_only=False, on_accepted=None, on_changed=None, style=''): self._activity_indicator = utils.ActivityIndicator( callback=self.set_text) self.read_only = read_only self.on_accepted = on_accepted self.buffer = Buffer( multiline=False, accept_handler=self._accept_handler, on_text_changed=on_changed, read_only=Condition(lambda: self.read_only), ) self.container = Window( content=BufferControl(self.buffer), always_hide_cursor=Condition(lambda: self.read_only), width=width, dont_extend_height=True, dont_extend_width=not extend_width, style=style, ) if text: self.set_text(text, ignore_callback=True) def _accept_handler(self, buffer): if self.on_accepted: self.on_accepted(buffer) # Do not clear input field on enter return True @property def text(self): return self.buffer.text @text.setter def text(self, text): self.set_text(text) def set_text(self, text, ignore_callback=False, preserve_cursor_position=True): if preserve_cursor_position: curpos = self.buffer.cursor_position if ignore_callback: handlers = self.buffer.on_text_changed._handlers self.buffer.on_text_changed._handlers = [] self.buffer.set_document(Document(text), bypass_readonly=True) if ignore_callback: self.buffer.on_text_changed._handlers = handlers if preserve_cursor_position: self.buffer.cursor_position = curpos @property def read_only(self): return self._read_only @read_only.setter def read_only(self, read_only): self._read_only = bool(read_only) @property def is_loading(self): """Whether an activity indicator is displayed""" return self._activity_indicator.active @is_loading.setter def is_loading(self, is_loading): self._activity_indicator.active = is_loading def __pt_container__(self): return self.container
class Pane(object): """ One pane, containing one process and a search buffer for going into copy mode or displaying the help. """ _pane_counter = 1000 # Start at 1000, to be sure to not confuse this with pane indexes. def __init__(self, process): assert isinstance(process, Process) self.process = process self.chosen_name = None # Displayed the clock instead of this pane content. self.clock_mode = False # Give unique ID. Pane._pane_counter += 1 self.pane_id = Pane._pane_counter # Prompt_toolkit buffer, for displaying scrollable text. # (In copy mode, or help mode.) # Note: Because the scroll_buffer can only contain text, we also use the # copy_token_list, that contains a token list with color information. self.scroll_buffer = Buffer(read_only=True) self.copy_token_list = [] self.display_scroll_buffer = False self.scroll_buffer_title = '' # Search buffer, for use in copy mode. (Each pane gets its own search buffer.) self.search_buffer = Buffer() self.is_searching = False self.search_state = SearchState(ignore_case=False) @property def name(self): """ The name for the window as displayed in the title bar and status bar. """ # Name, explicitely set for the pane. if self.chosen_name: return self.chosen_name else: # Name from the process running inside the pane. name = self.process.get_name() if name: return os.path.basename(name) return '' def enter_copy_mode(self): """ Suspend the process, and copy the screen content to the `scroll_buffer`. That way the user can search through the history and copy/paste. """ document, token_list = self.process.create_copy_document() self._enter_scroll_buffer('Copy', document, token_list) def display_text(self, text, title=''): """ Display the given text in the scroll buffer. """ self._enter_scroll_buffer( title, document=Document(text, 0), token_list=[(Token, text)]) def _enter_scroll_buffer(self, title, document, token_list): # Suspend child process. self.process.suspend() self.scroll_buffer.set_document(document, bypass_readonly=True) self.copy_token_list = token_list self.display_scroll_buffer = True self.scroll_buffer_title = title # Reset search state. self.search_state = SearchState(ignore_case=False) def exit_scroll_buffer(self): """ Exit scroll buffer. (Exits help or copy mode.) """ self.process.resume() self.display_scroll_buffer = False
class Pane(object): """ One pane, containing one process and a search buffer for going into copy mode or displaying the help. """ _pane_counter = 1000 # Start at 1000, to be sure to not confuse this with pane indexes. def __init__(self, process): assert isinstance(process, Process) self.process = process self.chosen_name = None # Displayed the clock instead of this pane content. self.clock_mode = False # Give unique ID. Pane._pane_counter += 1 self.pane_id = Pane._pane_counter # Prompt_toolkit buffer, for displaying scrollable text. # (In copy mode, or help mode.) # Note: Because the scroll_buffer can only contain text, we also use the # copy_token_list, that contains a token list with color information. self.scroll_buffer = Buffer(read_only=True) self.copy_token_list = [] self.display_scroll_buffer = False self.scroll_buffer_title = '' # Search buffer, for use in copy mode. (Each pane gets its own search buffer.) self.search_buffer = Buffer() self.is_searching = False self.search_state = SearchState(ignore_case=False) @property def name(self): """ The name for the window as displayed in the title bar and status bar. """ # Name, explicitely set for the pane. if self.chosen_name: return self.chosen_name else: # Name from the process running inside the pane. name = self.process.get_name() if name: return os.path.basename(name) return '' def enter_copy_mode(self): """ Suspend the process, and copy the screen content to the `scroll_buffer`. That way the user can search through the history and copy/paste. """ document, token_list = self.process.create_copy_document() self._enter_scroll_buffer('Copy', document, token_list) def display_text(self, text, title=''): """ Display the given text in the scroll buffer. """ self._enter_scroll_buffer(title, document=Document(text, 0), token_list=[(Token, text)]) def _enter_scroll_buffer(self, title, document, token_list): # Suspend child process. self.process.suspend() self.scroll_buffer.set_document(document, bypass_readonly=True) self.copy_token_list = token_list self.display_scroll_buffer = True self.scroll_buffer_title = title # Reset search state. self.search_state = SearchState(ignore_case=False) def exit_scroll_buffer(self): """ Exit scroll buffer. (Exits help or copy mode.) """ self.process.resume() self.display_scroll_buffer = False
def test_capitalize(self): buffer = Buffer() text = 'selec' buffer.set_document(Document(text, len(text))) self.capitalizer.apply_capitalization(buffer) self.assertEqual('selec', buffer.text) text = 'select' buffer.set_document(Document(text, len(text))) self.capitalizer.apply_capitalization(buffer) self.assertEqual('SELECT', buffer.text) text = 'CREATE TABLE "select' buffer.set_document(Document(text, len(text))) self.capitalizer.apply_capitalization(buffer) self.assertEqual('CREATE TABLE "select', buffer.text) text = 'CREATE TABLE \'select\'' buffer.set_document(Document(text, len(text))) self.capitalizer.apply_capitalization(buffer) self.assertEqual('CREATE TABLE \'select\'', buffer.text) text = 'create table test (x int)' buffer.set_document(Document(text, len(text))) self.capitalizer.apply_capitalization(buffer) self.assertEqual('CREATE TABLE test (x INT)', buffer.text) text = 'create table test (a boolean, b string, c integer)' buffer.set_document(Document(text, len(text))) self.capitalizer.apply_capitalization(buffer) self.assertEqual('CREATE TABLE test (a BOOLEAN, b STRING, c INTEGER)', buffer.text) text = 'create table test\n(a boolean, b string, c integer)' buffer.set_document(Document(text, len(text))) self.capitalizer.apply_capitalization(buffer) self.assertEqual('CREATE TABLE test\n(a BOOLEAN, b STRING, c INTEGER)', buffer.text) text = '\\select dynamic' buffer.set_document(Document(text, len(text))) self.capitalizer.apply_capitalization(buffer) self.assertEqual('\\select dynamic', buffer.text)
class TextArea(object): """ A simple input field. This contains a ``prompt_toolkit`` :class:`~prompt_toolkit.buffer.Buffer` object that hold the text data structure for the edited buffer, the :class:`~prompt_toolkit.layout.BufferControl`, which applies a :class:`~prompt_toolkit.lexers.Lexer` to the text and turns it into a :class:`~prompt_toolkit.layout.UIControl`, and finally, this :class:`~prompt_toolkit.layout.UIControl` is wrapped in a :class:`~prompt_toolkit.layout.Window` object (just like any :class:`~prompt_toolkit.layout.UIControl`), which is responsible for the scrolling. This widget does have some options, but it does not intend to cover every single use case. For more configurations options, you can always build a text area manually, using a :class:`~prompt_toolkit.buffer.Buffer`, :class:`~prompt_toolkit.layout.BufferControl` and :class:`~prompt_toolkit.layout.Window`. :param text: The initial text. :param multiline: If True, allow multiline input. :param lexer: :class:`~prompt_toolkit.lexers.Lexer` instance for syntax highlighting. :param completer: :class:`~prompt_toolkit.completion.Completer` instance for auto completion. :param focusable: When `True`, allow this widget to receive the focus. :param wrap_lines: When `True`, don't scroll horizontally, but wrap lines. :param width: Window width. (:class:`~prompt_toolkit.layout.Dimension` object.) :param height: Window height. (:class:`~prompt_toolkit.layout.Dimension` object.) :param password: When `True`, display using asterisks. :param accept_handler: Called when `Enter` is pressed. :param scrollbar: When `True`, display a scroll bar. :param search_field: An optional `SearchToolbar` object. :param style: A style string. :param dont_extend_height: :param dont_extend_width: """ def __init__(self, text='', multiline=True, password=False, lexer=None, completer=None, accept_handler=None, focusable=True, wrap_lines=True, read_only=False, width=None, height=None, dont_extend_height=False, dont_extend_width=False, line_numbers=False, scrollbar=False, style='', search_field=None, preview_search=True, prompt=''): assert isinstance(text, six.text_type) assert search_field is None or isinstance(search_field, SearchToolbar) if search_field is None: search_control = None elif isinstance(search_field, SearchToolbar): search_control = search_field.control self.buffer = Buffer(document=Document(text, 0), multiline=multiline, read_only=read_only, completer=completer, complete_while_typing=True, accept_handler=(lambda buff: accept_handler(buff)) if accept_handler else None) self.control = BufferControl( buffer=self.buffer, lexer=lexer, input_processors=[ ConditionalProcessor(processor=PasswordProcessor(), filter=to_filter(password)), BeforeInput(prompt, style='class:text-area.prompt'), ], search_buffer_control=search_control, preview_search=preview_search, focusable=focusable) if multiline: if scrollbar: right_margins = [ScrollbarMargin(display_arrows=True)] else: right_margins = [] if line_numbers: left_margins = [NumberedMargin()] else: left_margins = [] else: wrap_lines = False # Never wrap for single line input. 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=wrap_lines, left_margins=left_margins, right_margins=right_margins) @property def text(self): return self.buffer.text @text.setter def text(self, value): self.buffer.set_document(Document(value, 0), bypass_readonly=True) @property def document(self): return self.buffer.document @document.setter def document(self, value): self.buffer.document = value def __pt_container__(self): return self.window
class TextArea(object): """ A simple input field. This is a higher level abstraction on top of several other classes with sane defaults. This widget does have the most common options, but it does not intend to cover every single use case. For more configurations options, you can always build a text area manually, using a :class:`~prompt_toolkit.buffer.Buffer`, :class:`~prompt_toolkit.layout.BufferControl` and :class:`~prompt_toolkit.layout.Window`. Buffer attributes: :param text: The initial text. :param multiline: If True, allow multiline input. :param completer: :class:`~prompt_toolkit.completion.Completer` instance for auto completion. :param complete_while_typing: Boolean. :param accept_handler: Called when `Enter` is pressed (This should be a callable that takes a buffer as input). :param history: :class:`~prompt_toolkit.history.History` instance. :param auto_suggest: :class:`~prompt_toolkit.auto_suggest.AutoSuggest` instance for input suggestions. BufferControl attributes: :param password: When `True`, display using asterisks. :param focusable: When `True`, allow this widget to receive the focus. :param focus_on_click: When `True`, focus after mouse click. :param input_processors: `None` or a list of :class:`~prompt_toolkit.layout.Processor` objects. Window attributes: :param lexer: :class:`~prompt_toolkit.lexers.Lexer` instance for syntax highlighting. :param wrap_lines: When `True`, don't scroll horizontally, but wrap lines. :param width: Window width. (:class:`~prompt_toolkit.layout.Dimension` object.) :param height: Window height. (:class:`~prompt_toolkit.layout.Dimension` object.) :param scrollbar: When `True`, display a scroll bar. :param style: A style string. :param dont_extend_width: When `True`, don't take up more width then the preferred width reported by the control. :param dont_extend_height: When `True`, don't take up more width then the preferred height reported by the control. :param get_line_prefix: None or a callable that returns formatted text to be inserted before a line. It takes a line number (int) and a wrap_count and returns formatted text. This can be used for implementation of line continuations, things like Vim "breakindent" and so on. Other attributes: :param search_field: An optional `SearchToolbar` object. """ def __init__(self, text='', multiline=True, password=False, lexer=None, auto_suggest=None, completer=None, complete_while_typing=True, accept_handler=None, history=None, focusable=True, focus_on_click=False, wrap_lines=True, read_only=False, width=None, height=None, dont_extend_height=False, dont_extend_width=False, line_numbers=False, get_line_prefix=None, scrollbar=False, style='', search_field=None, preview_search=True, prompt='', input_processors=None): # assert isinstance(text, six.text_type) if search_field is None: search_control = None 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) @property def text(self): """ The `Buffer` text. """ return self.buffer.text @text.setter def text(self, value): self.buffer.set_document(Document(value, 0), bypass_readonly=True) @property def document(self): """ The `Buffer` document (text + cursor position). """ return self.buffer.document @document.setter def document(self, value): self.buffer.document = value @property def accept_handler(self): """ The accept handler. Called when the user accepts the input. """ return self.buffer.accept_handler @accept_handler.setter def accept_handler(self, value): self.buffer.accept_handler = value def __pt_container__(self): return self.window
class HoundCli(object): """ Abstracts CSVHound CLI """ def __init__(self): # create left and right buffers in main area self.left_buffer = Buffer(read_only=True) self.right_buffer = Buffer() # sample text to get us going test_text = 'test\none\ntwo\nthree' # this is how to explicity write new content to a read-only buffer self.left_buffer.set_document(Document(test_text), bypass_readonly=True) # create windows; you need a Windor and a BufferControl to display Buffer # content self.left_window = Window(BufferControl(buffer=self.left_buffer)) self.right_window = Window(BufferControl(buffer=self.right_buffer)) self.command_window = Window(height=1, style='class:line') self.command_area = HSplit([ self.command_window, Window(height=1, char='\u2500', style='class:line') ]) self.main_body = VSplit([ self.left_window, # A vertical line in the middle. We explicitly specify the width, to make # sure that the layout engine will not try to divide the whole width by # three for all these windows. Window(width=1, char='\u2502', style='class:line'), # Display the Result buffer on the right. self.right_window, ]) self.title_area = Window( height=1, content=FormattedTextControl(self.get_titlebar_text), align=WindowAlign.LEFT, # style='class:reverse') style='bg:DarkBlue Gold') self.message_area = Window( height=1, content=DummyControl(), align=WindowAlign.LEFT, # style='class:reverse') style='bg:DarkBlue Gold') self.status_area = Window(height=1, content=DummyControl(), align=WindowAlign.LEFT, style='bg:DarkBlue Gold') # style='class:reverse') self.float_content = HSplit([ self.title_area, self.message_area, Window(height=1, char='\u2500', style='class:line'), self.command_area, self.main_body, self.status_area ]) self.root_container = FloatContainer(content=self.float_content, floats=[ Float(xcursor=True, ycursor=True, content=CompletionsMenu( max_height=16, scroll_offset=1)) ]) self.kb = KeyBindings() self.kb.add('c-n')(focus_next) self.kb.add('c-p')(focus_previous) @self.kb.add('c-c', eager=True) @self.kb.add('c-q', eager=True) def _(event): """ Pressing Ctrl-Q or Ctrl-C will exit the user interface. Setting a return value means: quit the event loop that drives the user interface and return this value from the `Application.run()` call. Note that Ctrl-Q does not work on all terminals. Sometimes it requires executing `stty -ixon`. """ event.app.exit() @self.kb.add('o') def _(event): """ attempt to insert a file open command prompt """ self.cmd_buffer_open = self.create_cmd_buffer("open") self.cmd_open_control = self.create_cmd_control( self.cmd_buffer_open, prompt='[Open file:] ') self.command_area.children[0].content = self.cmd_open_control app = get_app() app.layout.focus(self.command_area) self.application = None self._current_table = None # define initial titlebar text def get_titlebar_text(self): return [ ('class:title', ' CSV Hound'), ('class:title', ' (Press [Ctrl-Q] to quit.)'), ] def get_status_text(self, message): return [ ('Gold', ' ' + message), # ('class:red', ' '+message), ] # accept_handler for open command def do_open(self, _): logger.debug('attempting to open file [' + _.text + ']...') if (csvhound.core.file_exists(_.text)): model = csvhound.core.BaseHound() self._current_table = model.get_table_from_file(_.text) output = "Rows: " + str(len(self._current_table.rows)) output += "\nColumns: " + str(len(self._current_table.columns)) output += "\n---------------------------------------------------" output += '\n' output += '\nTABLE COLUMNS:\n\n' # rows = model.describe_table() details = model.describe_table() for detail in details: # print(detail) output += detail[0] + ' [' + detail[1] + ']\n' self.left_buffer.set_document(Document(output), bypass_readonly=True) # hide previous command buffer control self.status_area.content = FormattedTextControl( self.get_status_text('file open: ' + _.text)) else: self.status_area.content = FormattedTextControl( self.get_status_text('file not found: ' + _.text)) self.command_area.children[0].content = DummyControl() app = get_app() app.layout.focus(self.left_buffer) def do_inspect(self, _): self.right_buffer.text = "INSPECT:\n" + _.text # hide previous command buffer control self.command_area.children[0].content = DummyControl() self.status_area.content = FormattedTextControl( self.get_status_text('inspect file: ' + _.text)) app = get_app() app.layout.focus(self.left_buffer) # create a command buffer for a given command, with named accept_handler def create_cmd_buffer(self, command): completer = PathCompleter() # if command == 'open' or command == 'inspect': # completer=PathCompleter() # else: # completer=None accept_handler = getattr(self, 'do_' + command) return Buffer(completer=completer, name='cmd_buffer_' + command, accept_handler=accept_handler, multiline=False) # create command buffer control with given command buffer def create_cmd_control(self, buffer, prompt='>>>> '): return BufferControl(buffer, input_processors=[ BeforeInput(prompt, style='class:text-area.prompt') ]) def create_application(self): self.application = Application( layout=Layout(self.root_container), key_bindings=self.kb, # Let's add mouse support! mouse_support=True, # Using an alternate screen buffer means as much as: "run full screen". # It switches the terminal to an alternate screen. full_screen=True) def run_application(self): self.application.run()
class FormattedTextArea: """Just like text area, but it accepts formatted content.""" def __init__( self, text="", focusable=False, wrap_lines=True, width=None, height=None, scrollbar=False, dont_extend_height=True, dont_extend_width=False, read_only=True, ): self.read_only = read_only formatted_text = to_formatted_text(text) plain_text = fragment_list_to_text(formatted_text) self.buffer = Buffer( document=Document(plain_text, 0), read_only=Condition(lambda: self.read_only), ) self.control = FormattedBufferControl( buffer=self.buffer, formatted_text=formatted_text, input_processors=[ FormatTextProcessor(), HighlightSelectionProcessor() ], include_default_input_processors=False, focusable=focusable, focus_on_click=focusable, ) self.scrollbar = scrollbar right_margins = [ ConditionalMargin( ScrollbarMargin(display_arrows=True), filter=Condition(lambda: self.scrollbar), ), ] self.window = Window( content=self.control, width=width, height=height, wrap_lines=wrap_lines, right_margins=right_margins, dont_extend_height=dont_extend_height, dont_extend_width=dont_extend_width, ) @property def document(self): return self.buffer.document @document.setter def document(self, value): self.buffer.set_document(value, bypass_readonly=True) @property def text(self): return self.buffer.text @text.setter def text(self, text): formatted_text = to_formatted_text(text) self.control.update_formatted_text(formatted_text) plain_text = fragment_list_to_text(formatted_text) self.document = Document(plain_text, 0) def __pt_container__(self): return self.window