774 lines
28 KiB
Python
774 lines
28 KiB
Python
"""
|
|
Creation of the `Layout` instance for the Python input/REPL.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import platform
|
|
import sys
|
|
from enum import Enum
|
|
from inspect import _ParameterKind as ParameterKind
|
|
from typing import TYPE_CHECKING, Any
|
|
|
|
from prompt_toolkit.application import get_app
|
|
from prompt_toolkit.enums import DEFAULT_BUFFER, SEARCH_BUFFER
|
|
from prompt_toolkit.filters import (
|
|
Condition,
|
|
has_focus,
|
|
is_done,
|
|
renderer_height_is_known,
|
|
)
|
|
from prompt_toolkit.formatted_text import fragment_list_width, to_formatted_text
|
|
from prompt_toolkit.formatted_text.base import StyleAndTextTuples
|
|
from prompt_toolkit.key_binding.vi_state import InputMode
|
|
from prompt_toolkit.layout.containers import (
|
|
AnyContainer,
|
|
ConditionalContainer,
|
|
Container,
|
|
Float,
|
|
FloatContainer,
|
|
HSplit,
|
|
ScrollOffsets,
|
|
VSplit,
|
|
Window,
|
|
)
|
|
from prompt_toolkit.layout.controls import BufferControl, FormattedTextControl
|
|
from prompt_toolkit.layout.dimension import AnyDimension, Dimension
|
|
from prompt_toolkit.layout.layout import Layout
|
|
from prompt_toolkit.layout.margins import PromptMargin
|
|
from prompt_toolkit.layout.menus import CompletionsMenu, MultiColumnCompletionsMenu
|
|
from prompt_toolkit.layout.processors import (
|
|
AppendAutoSuggestion,
|
|
ConditionalProcessor,
|
|
DisplayMultipleCursors,
|
|
HighlightIncrementalSearchProcessor,
|
|
HighlightMatchingBracketProcessor,
|
|
HighlightSelectionProcessor,
|
|
Processor,
|
|
TabsProcessor,
|
|
)
|
|
from prompt_toolkit.lexers import Lexer, SimpleLexer
|
|
from prompt_toolkit.mouse_events import MouseEvent
|
|
from prompt_toolkit.selection import SelectionType
|
|
from prompt_toolkit.widgets.toolbars import (
|
|
ArgToolbar,
|
|
CompletionsToolbar,
|
|
SearchToolbar,
|
|
SystemToolbar,
|
|
ValidationToolbar,
|
|
)
|
|
|
|
from .filters import HasSignature, ShowDocstring, ShowSidebar, ShowSignature
|
|
from .prompt_style import PromptStyle
|
|
from .utils import if_mousedown
|
|
|
|
if TYPE_CHECKING:
|
|
from .python_input import OptionCategory, PythonInput
|
|
|
|
__all__ = ["PtPythonLayout", "CompletionVisualisation"]
|
|
|
|
|
|
class CompletionVisualisation(Enum):
|
|
"Visualisation method for the completions."
|
|
|
|
NONE = "none"
|
|
POP_UP = "pop-up"
|
|
MULTI_COLUMN = "multi-column"
|
|
TOOLBAR = "toolbar"
|
|
|
|
|
|
def show_completions_toolbar(python_input: PythonInput) -> Condition:
|
|
return Condition(
|
|
lambda: python_input.completion_visualisation == CompletionVisualisation.TOOLBAR
|
|
)
|
|
|
|
|
|
def show_completions_menu(python_input: PythonInput) -> Condition:
|
|
return Condition(
|
|
lambda: python_input.completion_visualisation == CompletionVisualisation.POP_UP
|
|
)
|
|
|
|
|
|
def show_multi_column_completions_menu(python_input: PythonInput) -> Condition:
|
|
return Condition(
|
|
lambda: python_input.completion_visualisation
|
|
== CompletionVisualisation.MULTI_COLUMN
|
|
)
|
|
|
|
|
|
def python_sidebar(python_input: PythonInput) -> Window:
|
|
"""
|
|
Create the `Layout` for the sidebar with the configurable options.
|
|
"""
|
|
|
|
def get_text_fragments() -> StyleAndTextTuples:
|
|
tokens: StyleAndTextTuples = []
|
|
|
|
def append_category(category: OptionCategory[Any]) -> None:
|
|
tokens.extend(
|
|
[
|
|
("class:sidebar", " "),
|
|
("class:sidebar.title", " %-36s" % category.title),
|
|
("class:sidebar", "\n"),
|
|
]
|
|
)
|
|
|
|
def append(index: int, label: str, status: str) -> None:
|
|
selected = index == python_input.selected_option_index
|
|
|
|
@if_mousedown
|
|
def select_item(mouse_event: MouseEvent) -> None:
|
|
python_input.selected_option_index = index
|
|
|
|
@if_mousedown
|
|
def goto_next(mouse_event: MouseEvent) -> None:
|
|
"Select item and go to next value."
|
|
python_input.selected_option_index = index
|
|
option = python_input.selected_option
|
|
option.activate_next()
|
|
|
|
sel = ",selected" if selected else ""
|
|
|
|
tokens.append(("class:sidebar" + sel, " >" if selected else " "))
|
|
tokens.append(("class:sidebar.label" + sel, "%-24s" % label, select_item))
|
|
tokens.append(("class:sidebar.status" + sel, " ", select_item))
|
|
tokens.append(("class:sidebar.status" + sel, f"{status}", goto_next))
|
|
|
|
if selected:
|
|
tokens.append(("[SetCursorPosition]", ""))
|
|
|
|
tokens.append(
|
|
("class:sidebar.status" + sel, " " * (13 - len(status)), goto_next)
|
|
)
|
|
tokens.append(("class:sidebar", "<" if selected else ""))
|
|
tokens.append(("class:sidebar", "\n"))
|
|
|
|
i = 0
|
|
for category in python_input.options:
|
|
append_category(category)
|
|
|
|
for option in category.options:
|
|
append(i, option.title, str(option.get_current_value()))
|
|
i += 1
|
|
|
|
tokens.pop() # Remove last newline.
|
|
|
|
return tokens
|
|
|
|
class Control(FormattedTextControl):
|
|
def move_cursor_down(self) -> None:
|
|
python_input.selected_option_index += 1
|
|
|
|
def move_cursor_up(self) -> None:
|
|
python_input.selected_option_index -= 1
|
|
|
|
return Window(
|
|
Control(get_text_fragments),
|
|
style="class:sidebar",
|
|
width=Dimension.exact(43),
|
|
height=Dimension(min=3),
|
|
scroll_offsets=ScrollOffsets(top=1, bottom=1),
|
|
)
|
|
|
|
|
|
def python_sidebar_navigation(python_input: PythonInput) -> Window:
|
|
"""
|
|
Create the `Layout` showing the navigation information for the sidebar.
|
|
"""
|
|
|
|
def get_text_fragments() -> StyleAndTextTuples:
|
|
# Show navigation info.
|
|
return [
|
|
("class:sidebar", " "),
|
|
("class:sidebar.key", "[Arrows]"),
|
|
("class:sidebar", " "),
|
|
("class:sidebar.description", "Navigate"),
|
|
("class:sidebar", " "),
|
|
("class:sidebar.key", "[Enter]"),
|
|
("class:sidebar", " "),
|
|
("class:sidebar.description", "Hide menu"),
|
|
]
|
|
|
|
return Window(
|
|
FormattedTextControl(get_text_fragments),
|
|
style="class:sidebar",
|
|
width=Dimension.exact(43),
|
|
height=Dimension.exact(1),
|
|
)
|
|
|
|
|
|
def python_sidebar_help(python_input: PythonInput) -> Container:
|
|
"""
|
|
Create the `Layout` for the help text for the current item in the sidebar.
|
|
"""
|
|
token = "class:sidebar.helptext"
|
|
|
|
def get_current_description() -> str:
|
|
"""
|
|
Return the description of the selected option.
|
|
"""
|
|
i = 0
|
|
for category in python_input.options:
|
|
for option in category.options:
|
|
if i == python_input.selected_option_index:
|
|
return option.description
|
|
i += 1
|
|
return ""
|
|
|
|
def get_help_text() -> StyleAndTextTuples:
|
|
return [(token, get_current_description())]
|
|
|
|
return ConditionalContainer(
|
|
content=Window(
|
|
FormattedTextControl(get_help_text),
|
|
style=token,
|
|
height=Dimension(min=3),
|
|
wrap_lines=True,
|
|
),
|
|
filter=ShowSidebar(python_input)
|
|
& Condition(lambda: python_input.show_sidebar_help)
|
|
& ~is_done,
|
|
)
|
|
|
|
|
|
def signature_toolbar(python_input: PythonInput) -> Container:
|
|
"""
|
|
Return the `Layout` for the signature.
|
|
"""
|
|
|
|
def get_text_fragments() -> StyleAndTextTuples:
|
|
result: StyleAndTextTuples = []
|
|
append = result.append
|
|
Signature = "class:signature-toolbar"
|
|
|
|
if python_input.signatures:
|
|
sig = python_input.signatures[0] # Always take the first one.
|
|
|
|
append((Signature, " "))
|
|
try:
|
|
append((Signature, sig.name))
|
|
except IndexError:
|
|
# Workaround for #37: https://github.com/jonathanslenders/python-prompt-toolkit/issues/37
|
|
# See also: https://github.com/davidhalter/jedi/issues/490
|
|
return []
|
|
|
|
append((Signature + ",operator", "("))
|
|
|
|
got_positional_only = False
|
|
got_keyword_only = False
|
|
|
|
for i, p in enumerate(sig.parameters):
|
|
# Detect transition between positional-only and not positional-only.
|
|
if p.kind == ParameterKind.POSITIONAL_ONLY:
|
|
got_positional_only = True
|
|
if got_positional_only and p.kind != ParameterKind.POSITIONAL_ONLY:
|
|
got_positional_only = False
|
|
append((Signature, "/"))
|
|
append((Signature + ",operator", ", "))
|
|
|
|
if not got_keyword_only and p.kind == ParameterKind.KEYWORD_ONLY:
|
|
got_keyword_only = True
|
|
append((Signature, "*"))
|
|
append((Signature + ",operator", ", "))
|
|
|
|
sig_index = getattr(sig, "index", 0)
|
|
|
|
if i == sig_index:
|
|
# Note: we use `_Param.description` instead of
|
|
# `_Param.name`, that way we also get the '*' before args.
|
|
append((Signature + ",current-name", p.description))
|
|
else:
|
|
append((Signature, p.description))
|
|
|
|
if p.default:
|
|
# NOTE: For the jedi-based completion, the default is
|
|
# currently still part of the name.
|
|
append((Signature, f"={p.default}"))
|
|
|
|
append((Signature + ",operator", ", "))
|
|
|
|
if sig.parameters:
|
|
# Pop last comma
|
|
result.pop()
|
|
|
|
append((Signature + ",operator", ")"))
|
|
append((Signature, " "))
|
|
return result
|
|
|
|
return ConditionalContainer(
|
|
content=Window(
|
|
FormattedTextControl(get_text_fragments), height=Dimension.exact(1)
|
|
),
|
|
# Show only when there is a signature
|
|
filter=HasSignature(python_input)
|
|
&
|
|
# Signature needs to be shown.
|
|
ShowSignature(python_input)
|
|
&
|
|
# And no sidebar is visible.
|
|
~ShowSidebar(python_input)
|
|
&
|
|
# Not done yet.
|
|
~is_done,
|
|
)
|
|
|
|
|
|
class PythonPromptMargin(PromptMargin):
|
|
"""
|
|
Create margin that displays the prompt.
|
|
It shows something like "In [1]:".
|
|
"""
|
|
|
|
def __init__(self, python_input: PythonInput) -> None:
|
|
self.python_input = python_input
|
|
|
|
def get_prompt_style() -> PromptStyle:
|
|
return python_input.all_prompt_styles[python_input.prompt_style]
|
|
|
|
def get_prompt() -> StyleAndTextTuples:
|
|
return to_formatted_text(get_prompt_style().in_prompt())
|
|
|
|
def get_continuation(
|
|
width: int, line_number: int, is_soft_wrap: bool
|
|
) -> StyleAndTextTuples:
|
|
if python_input.show_line_numbers and not is_soft_wrap:
|
|
text = ("%i " % (line_number + 1)).rjust(width)
|
|
return [("class:line-number", text)]
|
|
else:
|
|
return to_formatted_text(get_prompt_style().in2_prompt(width))
|
|
|
|
super().__init__(get_prompt, get_continuation)
|
|
|
|
|
|
def status_bar(python_input: PythonInput) -> Container:
|
|
"""
|
|
Create the `Layout` for the status bar.
|
|
"""
|
|
TB = "class:status-toolbar"
|
|
|
|
@if_mousedown
|
|
def toggle_paste_mode(mouse_event: MouseEvent) -> None:
|
|
python_input.paste_mode = not python_input.paste_mode
|
|
|
|
@if_mousedown
|
|
def enter_history(mouse_event: MouseEvent) -> None:
|
|
python_input.enter_history()
|
|
|
|
def get_text_fragments() -> StyleAndTextTuples:
|
|
python_buffer = python_input.default_buffer
|
|
|
|
result: StyleAndTextTuples = []
|
|
append = result.append
|
|
|
|
append((TB, " "))
|
|
result.extend(get_inputmode_fragments(python_input))
|
|
append((TB, " "))
|
|
|
|
# Position in history.
|
|
append(
|
|
(
|
|
TB,
|
|
"%i/%i "
|
|
% (python_buffer.working_index + 1, len(python_buffer._working_lines)),
|
|
)
|
|
)
|
|
|
|
# Shortcuts.
|
|
app = get_app()
|
|
if (
|
|
not python_input.vi_mode
|
|
and app.current_buffer == python_input.search_buffer
|
|
):
|
|
append((TB, "[Ctrl-G] Cancel search [Enter] Go to this position."))
|
|
elif bool(app.current_buffer.selection_state) and not python_input.vi_mode:
|
|
# Emacs cut/copy keys.
|
|
append((TB, "[Ctrl-W] Cut [Meta-W] Copy [Ctrl-Y] Paste [Ctrl-G] Cancel"))
|
|
else:
|
|
result.extend(
|
|
[
|
|
(TB + " class:status-toolbar.key", "[F3]", enter_history),
|
|
(TB, " History ", enter_history),
|
|
(TB + " class:status-toolbar.key", "[F6]", toggle_paste_mode),
|
|
(TB, " ", toggle_paste_mode),
|
|
]
|
|
)
|
|
|
|
if python_input.paste_mode:
|
|
append(
|
|
(TB + " class:paste-mode-on", "Paste mode (on)", toggle_paste_mode)
|
|
)
|
|
else:
|
|
append((TB, "Paste mode", toggle_paste_mode))
|
|
|
|
return result
|
|
|
|
return ConditionalContainer(
|
|
content=Window(content=FormattedTextControl(get_text_fragments), style=TB),
|
|
filter=~is_done
|
|
& renderer_height_is_known
|
|
& Condition(
|
|
lambda: python_input.show_status_bar
|
|
and not python_input.show_exit_confirmation
|
|
),
|
|
)
|
|
|
|
|
|
def get_inputmode_fragments(python_input: PythonInput) -> StyleAndTextTuples:
|
|
"""
|
|
Return current input mode as a list of (token, text) tuples for use in a
|
|
toolbar.
|
|
"""
|
|
app = get_app()
|
|
|
|
@if_mousedown
|
|
def toggle_vi_mode(mouse_event: MouseEvent) -> None:
|
|
python_input.vi_mode = not python_input.vi_mode
|
|
|
|
token = "class:status-toolbar"
|
|
input_mode_t = "class:status-toolbar.input-mode"
|
|
|
|
mode = app.vi_state.input_mode
|
|
result: StyleAndTextTuples = []
|
|
append = result.append
|
|
|
|
if python_input.title:
|
|
result.extend(to_formatted_text(python_input.title))
|
|
|
|
append((input_mode_t, "[F4] ", toggle_vi_mode))
|
|
|
|
# InputMode
|
|
if python_input.vi_mode:
|
|
recording_register = app.vi_state.recording_register
|
|
if recording_register:
|
|
append((token, " "))
|
|
append((token + " class:record", f"RECORD({recording_register})"))
|
|
append((token, " - "))
|
|
|
|
if app.current_buffer.selection_state is not None:
|
|
if app.current_buffer.selection_state.type == SelectionType.LINES:
|
|
append((input_mode_t, "Vi (VISUAL LINE)", toggle_vi_mode))
|
|
elif app.current_buffer.selection_state.type == SelectionType.CHARACTERS:
|
|
append((input_mode_t, "Vi (VISUAL)", toggle_vi_mode))
|
|
append((token, " "))
|
|
elif app.current_buffer.selection_state.type == SelectionType.BLOCK:
|
|
append((input_mode_t, "Vi (VISUAL BLOCK)", toggle_vi_mode))
|
|
append((token, " "))
|
|
elif mode in (InputMode.INSERT, "vi-insert-multiple"):
|
|
append((input_mode_t, "Vi (INSERT)", toggle_vi_mode))
|
|
append((token, " "))
|
|
elif mode == InputMode.NAVIGATION:
|
|
append((input_mode_t, "Vi (NAV)", toggle_vi_mode))
|
|
append((token, " "))
|
|
elif mode == InputMode.REPLACE:
|
|
append((input_mode_t, "Vi (REPLACE)", toggle_vi_mode))
|
|
append((token, " "))
|
|
else:
|
|
if app.emacs_state.is_recording:
|
|
append((token, " "))
|
|
append((token + " class:record", "RECORD"))
|
|
append((token, " - "))
|
|
|
|
append((input_mode_t, "Emacs", toggle_vi_mode))
|
|
append((token, " "))
|
|
|
|
return result
|
|
|
|
|
|
def show_sidebar_button_info(python_input: PythonInput) -> Container:
|
|
"""
|
|
Create `Layout` for the information in the right-bottom corner.
|
|
(The right part of the status bar.)
|
|
"""
|
|
|
|
@if_mousedown
|
|
def toggle_sidebar(mouse_event: MouseEvent) -> None:
|
|
"Click handler for the menu."
|
|
python_input.show_sidebar = not python_input.show_sidebar
|
|
|
|
version = sys.version_info
|
|
tokens: StyleAndTextTuples = [
|
|
("class:status-toolbar.key", "[F2]", toggle_sidebar),
|
|
("class:status-toolbar", " Menu", toggle_sidebar),
|
|
("class:status-toolbar", " - "),
|
|
(
|
|
"class:status-toolbar.python-version",
|
|
"%s %i.%i.%i"
|
|
% (platform.python_implementation(), version[0], version[1], version[2]),
|
|
),
|
|
("class:status-toolbar", " "),
|
|
]
|
|
width = fragment_list_width(tokens)
|
|
|
|
def get_text_fragments() -> StyleAndTextTuples:
|
|
# Python version
|
|
return tokens
|
|
|
|
return ConditionalContainer(
|
|
content=Window(
|
|
FormattedTextControl(get_text_fragments),
|
|
style="class:status-toolbar",
|
|
height=Dimension.exact(1),
|
|
width=Dimension.exact(width),
|
|
),
|
|
filter=~is_done
|
|
& renderer_height_is_known
|
|
& Condition(
|
|
lambda: python_input.show_status_bar
|
|
and not python_input.show_exit_confirmation
|
|
),
|
|
)
|
|
|
|
|
|
def create_exit_confirmation(
|
|
python_input: PythonInput, style: str = "class:exit-confirmation"
|
|
) -> Container:
|
|
"""
|
|
Create `Layout` for the exit message.
|
|
"""
|
|
|
|
def get_text_fragments() -> StyleAndTextTuples:
|
|
# Show "Do you really want to exit?"
|
|
return [
|
|
(style, f"\n {python_input.exit_message} ([y]/n) "),
|
|
("[SetCursorPosition]", ""),
|
|
(style, " \n"),
|
|
]
|
|
|
|
visible = ~is_done & Condition(lambda: python_input.show_exit_confirmation)
|
|
|
|
return ConditionalContainer(
|
|
content=Window(
|
|
FormattedTextControl(get_text_fragments, focusable=True), style=style
|
|
),
|
|
filter=visible,
|
|
)
|
|
|
|
|
|
def meta_enter_message(python_input: PythonInput) -> Container:
|
|
"""
|
|
Create the `Layout` for the 'Meta+Enter` message.
|
|
"""
|
|
|
|
def get_text_fragments() -> StyleAndTextTuples:
|
|
return [("class:accept-message", " [Meta+Enter] Execute ")]
|
|
|
|
@Condition
|
|
def extra_condition() -> bool:
|
|
"Only show when..."
|
|
b = python_input.default_buffer
|
|
|
|
return (
|
|
python_input.show_meta_enter_message
|
|
and (
|
|
not b.document.is_cursor_at_the_end
|
|
or python_input.accept_input_on_enter is None
|
|
)
|
|
and "\n" in b.text
|
|
)
|
|
|
|
visible = ~is_done & has_focus(DEFAULT_BUFFER) & extra_condition
|
|
|
|
return ConditionalContainer(
|
|
content=Window(FormattedTextControl(get_text_fragments)), filter=visible
|
|
)
|
|
|
|
|
|
class PtPythonLayout:
|
|
def __init__(
|
|
self,
|
|
python_input: PythonInput,
|
|
lexer: Lexer,
|
|
extra_body: AnyContainer | None = None,
|
|
extra_toolbars: list[AnyContainer] | None = None,
|
|
extra_buffer_processors: list[Processor] | None = None,
|
|
input_buffer_height: AnyDimension | None = None,
|
|
) -> None:
|
|
D = Dimension
|
|
extra_body_list: list[AnyContainer] = [extra_body] if extra_body else []
|
|
extra_toolbars = extra_toolbars or []
|
|
|
|
input_buffer_height = input_buffer_height or D(min=6)
|
|
|
|
search_toolbar = SearchToolbar(python_input.search_buffer)
|
|
|
|
def create_python_input_window() -> Window:
|
|
def menu_position() -> int | None:
|
|
"""
|
|
When there is no autocompletion menu to be shown, and we have a
|
|
signature, set the pop-up position at `bracket_start`.
|
|
"""
|
|
b = python_input.default_buffer
|
|
|
|
if python_input.signatures:
|
|
row, col = python_input.signatures[0].bracket_start
|
|
index = b.document.translate_row_col_to_index(row - 1, col)
|
|
return index
|
|
return None
|
|
|
|
return Window(
|
|
BufferControl(
|
|
buffer=python_input.default_buffer,
|
|
search_buffer_control=search_toolbar.control,
|
|
lexer=lexer,
|
|
include_default_input_processors=False,
|
|
input_processors=[
|
|
ConditionalProcessor(
|
|
processor=HighlightIncrementalSearchProcessor(),
|
|
filter=has_focus(SEARCH_BUFFER)
|
|
| has_focus(search_toolbar.control),
|
|
),
|
|
HighlightSelectionProcessor(),
|
|
DisplayMultipleCursors(),
|
|
TabsProcessor(),
|
|
# Show matching parentheses, but only while editing.
|
|
ConditionalProcessor(
|
|
processor=HighlightMatchingBracketProcessor(chars="[](){}"),
|
|
filter=has_focus(DEFAULT_BUFFER)
|
|
& ~is_done
|
|
& Condition(
|
|
lambda: python_input.highlight_matching_parenthesis
|
|
),
|
|
),
|
|
ConditionalProcessor(
|
|
processor=AppendAutoSuggestion(), filter=~is_done
|
|
),
|
|
]
|
|
+ (extra_buffer_processors or []),
|
|
menu_position=menu_position,
|
|
# Make sure that we always see the result of an reverse-i-search:
|
|
preview_search=True,
|
|
),
|
|
left_margins=[PythonPromptMargin(python_input)],
|
|
# Scroll offsets. The 1 at the bottom is important to make sure
|
|
# the cursor is never below the "Press [Meta+Enter]" message
|
|
# which is a float.
|
|
scroll_offsets=ScrollOffsets(bottom=1, left=4, right=4),
|
|
# As long as we're editing, prefer a minimal height of 6.
|
|
height=(
|
|
lambda: (
|
|
None
|
|
if get_app().is_done or python_input.show_exit_confirmation
|
|
else input_buffer_height
|
|
)
|
|
),
|
|
wrap_lines=Condition(lambda: python_input.wrap_lines),
|
|
)
|
|
|
|
sidebar = python_sidebar(python_input)
|
|
self.exit_confirmation = create_exit_confirmation(python_input)
|
|
|
|
self.root_container = HSplit(
|
|
[
|
|
VSplit(
|
|
[
|
|
HSplit(
|
|
[
|
|
FloatContainer(
|
|
content=HSplit(
|
|
[create_python_input_window()] + extra_body_list
|
|
),
|
|
floats=[
|
|
Float(
|
|
xcursor=True,
|
|
ycursor=True,
|
|
content=HSplit(
|
|
[
|
|
signature_toolbar(python_input),
|
|
ConditionalContainer(
|
|
content=CompletionsMenu(
|
|
scroll_offset=(
|
|
lambda: python_input.completion_menu_scroll_offset
|
|
),
|
|
max_height=12,
|
|
),
|
|
filter=show_completions_menu(
|
|
python_input
|
|
),
|
|
),
|
|
ConditionalContainer(
|
|
content=MultiColumnCompletionsMenu(),
|
|
filter=show_multi_column_completions_menu(
|
|
python_input
|
|
),
|
|
),
|
|
]
|
|
),
|
|
),
|
|
Float(
|
|
left=2,
|
|
bottom=1,
|
|
content=self.exit_confirmation,
|
|
),
|
|
Float(
|
|
bottom=0,
|
|
right=0,
|
|
height=1,
|
|
content=meta_enter_message(python_input),
|
|
hide_when_covering_content=True,
|
|
),
|
|
Float(
|
|
bottom=1,
|
|
left=1,
|
|
right=0,
|
|
content=python_sidebar_help(python_input),
|
|
),
|
|
],
|
|
),
|
|
ArgToolbar(),
|
|
search_toolbar,
|
|
SystemToolbar(),
|
|
ValidationToolbar(),
|
|
ConditionalContainer(
|
|
content=CompletionsToolbar(),
|
|
filter=show_completions_toolbar(python_input)
|
|
& ~is_done,
|
|
),
|
|
# Docstring region.
|
|
ConditionalContainer(
|
|
content=Window(
|
|
height=D.exact(1),
|
|
char="\u2500",
|
|
style="class:separator",
|
|
),
|
|
filter=HasSignature(python_input)
|
|
& ShowDocstring(python_input)
|
|
& ~is_done,
|
|
),
|
|
ConditionalContainer(
|
|
content=Window(
|
|
BufferControl(
|
|
buffer=python_input.docstring_buffer,
|
|
lexer=SimpleLexer(style="class:docstring"),
|
|
# lexer=PythonLexer,
|
|
),
|
|
height=D(max=12),
|
|
),
|
|
filter=HasSignature(python_input)
|
|
& ShowDocstring(python_input)
|
|
& ~is_done,
|
|
),
|
|
]
|
|
),
|
|
ConditionalContainer(
|
|
content=HSplit(
|
|
[
|
|
sidebar,
|
|
Window(style="class:sidebar,separator", height=1),
|
|
python_sidebar_navigation(python_input),
|
|
]
|
|
),
|
|
filter=ShowSidebar(python_input) & ~is_done,
|
|
),
|
|
]
|
|
)
|
|
]
|
|
+ extra_toolbars
|
|
+ [
|
|
VSplit(
|
|
[status_bar(python_input), show_sidebar_button_info(python_input)]
|
|
)
|
|
]
|
|
)
|
|
|
|
self.layout = Layout(self.root_container)
|
|
self.sidebar = sidebar
|