aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/python/prompt-toolkit/py3/prompt_toolkit/layout/menus.py
diff options
context:
space:
mode:
authormonster <monster@ydb.tech>2022-07-07 14:41:37 +0300
committermonster <monster@ydb.tech>2022-07-07 14:41:37 +0300
commit06e5c21a835c0e923506c4ff27929f34e00761c2 (patch)
tree75efcbc6854ef9bd476eb8bf00cc5c900da436a2 /contrib/python/prompt-toolkit/py3/prompt_toolkit/layout/menus.py
parent03f024c4412e3aa613bb543cf1660176320ba8f4 (diff)
downloadydb-06e5c21a835c0e923506c4ff27929f34e00761c2.tar.gz
fix ya.make
Diffstat (limited to 'contrib/python/prompt-toolkit/py3/prompt_toolkit/layout/menus.py')
-rw-r--r--contrib/python/prompt-toolkit/py3/prompt_toolkit/layout/menus.py723
1 files changed, 0 insertions, 723 deletions
diff --git a/contrib/python/prompt-toolkit/py3/prompt_toolkit/layout/menus.py b/contrib/python/prompt-toolkit/py3/prompt_toolkit/layout/menus.py
deleted file mode 100644
index 24d6e46af0..0000000000
--- a/contrib/python/prompt-toolkit/py3/prompt_toolkit/layout/menus.py
+++ /dev/null
@@ -1,723 +0,0 @@
-import math
-from itertools import zip_longest
-from typing import (
- TYPE_CHECKING,
- Callable,
- Dict,
- Iterable,
- List,
- Optional,
- Sequence,
- Tuple,
- TypeVar,
- Union,
- cast,
-)
-
-from prompt_toolkit.application.current import get_app
-from prompt_toolkit.buffer import CompletionState
-from prompt_toolkit.completion import Completion
-from prompt_toolkit.data_structures import Point
-from prompt_toolkit.filters import (
- Condition,
- FilterOrBool,
- has_completions,
- is_done,
- to_filter,
-)
-from prompt_toolkit.formatted_text import (
- StyleAndTextTuples,
- fragment_list_width,
- to_formatted_text,
-)
-from prompt_toolkit.key_binding.key_processor import KeyPressEvent
-from prompt_toolkit.layout.utils import explode_text_fragments
-from prompt_toolkit.mouse_events import MouseEvent, MouseEventType
-from prompt_toolkit.utils import get_cwidth
-
-from .containers import ConditionalContainer, HSplit, ScrollOffsets, Window
-from .controls import GetLinePrefixCallable, UIContent, UIControl
-from .dimension import Dimension
-from .margins import ScrollbarMargin
-
-if TYPE_CHECKING:
- from prompt_toolkit.key_binding.key_bindings import (
- KeyBindings,
- NotImplementedOrNone,
- )
-
-
-__all__ = [
- "CompletionsMenu",
- "MultiColumnCompletionsMenu",
-]
-
-E = KeyPressEvent
-
-
-class CompletionsMenuControl(UIControl):
- """
- Helper for drawing the complete menu to the screen.
-
- :param scroll_offset: Number (integer) representing the preferred amount of
- completions to be displayed before and after the current one. When this
- is a very high number, the current completion will be shown in the
- middle most of the time.
- """
-
- # Preferred minimum size of the menu control.
- # The CompletionsMenu class defines a width of 8, and there is a scrollbar
- # of 1.)
- MIN_WIDTH = 7
-
- def has_focus(self) -> bool:
- return False
-
- def preferred_width(self, max_available_width: int) -> Optional[int]:
- complete_state = get_app().current_buffer.complete_state
- if complete_state:
- menu_width = self._get_menu_width(500, complete_state)
- menu_meta_width = self._get_menu_meta_width(500, complete_state)
-
- return menu_width + menu_meta_width
- else:
- return 0
-
- def preferred_height(
- self,
- width: int,
- max_available_height: int,
- wrap_lines: bool,
- get_line_prefix: Optional[GetLinePrefixCallable],
- ) -> Optional[int]:
-
- complete_state = get_app().current_buffer.complete_state
- if complete_state:
- return len(complete_state.completions)
- else:
- return 0
-
- def create_content(self, width: int, height: int) -> UIContent:
- """
- Create a UIContent object for this control.
- """
- complete_state = get_app().current_buffer.complete_state
- if complete_state:
- completions = complete_state.completions
- index = complete_state.complete_index # Can be None!
-
- # Calculate width of completions menu.
- menu_width = self._get_menu_width(width, complete_state)
- menu_meta_width = self._get_menu_meta_width(
- width - menu_width, complete_state
- )
- show_meta = self._show_meta(complete_state)
-
- def get_line(i: int) -> StyleAndTextTuples:
- c = completions[i]
- is_current_completion = i == index
- result = _get_menu_item_fragments(
- c, is_current_completion, menu_width, space_after=True
- )
-
- if show_meta:
- result += self._get_menu_item_meta_fragments(
- c, is_current_completion, menu_meta_width
- )
- return result
-
- return UIContent(
- get_line=get_line,
- cursor_position=Point(x=0, y=index or 0),
- line_count=len(completions),
- )
-
- return UIContent()
-
- def _show_meta(self, complete_state: CompletionState) -> bool:
- """
- Return ``True`` if we need to show a column with meta information.
- """
- return any(c.display_meta_text for c in complete_state.completions)
-
- def _get_menu_width(self, max_width: int, complete_state: CompletionState) -> int:
- """
- Return the width of the main column.
- """
- return min(
- max_width,
- max(
- self.MIN_WIDTH,
- max(get_cwidth(c.display_text) for c in complete_state.completions) + 2,
- ),
- )
-
- def _get_menu_meta_width(
- self, max_width: int, complete_state: CompletionState
- ) -> int:
- """
- Return the width of the meta column.
- """
-
- def meta_width(completion: Completion) -> int:
- return get_cwidth(completion.display_meta_text)
-
- if self._show_meta(complete_state):
- return min(
- max_width, max(meta_width(c) for c in complete_state.completions) + 2
- )
- else:
- return 0
-
- def _get_menu_item_meta_fragments(
- self, completion: Completion, is_current_completion: bool, width: int
- ) -> StyleAndTextTuples:
-
- if is_current_completion:
- style_str = "class:completion-menu.meta.completion.current"
- else:
- style_str = "class:completion-menu.meta.completion"
-
- text, tw = _trim_formatted_text(completion.display_meta, width - 2)
- padding = " " * (width - 1 - tw)
-
- return to_formatted_text(
- cast(StyleAndTextTuples, []) + [("", " ")] + text + [("", padding)],
- style=style_str,
- )
-
- def mouse_handler(self, mouse_event: MouseEvent) -> "NotImplementedOrNone":
- """
- Handle mouse events: clicking and scrolling.
- """
- b = get_app().current_buffer
-
- if mouse_event.event_type == MouseEventType.MOUSE_UP:
- # Select completion.
- b.go_to_completion(mouse_event.position.y)
- b.complete_state = None
-
- elif mouse_event.event_type == MouseEventType.SCROLL_DOWN:
- # Scroll up.
- b.complete_next(count=3, disable_wrap_around=True)
-
- elif mouse_event.event_type == MouseEventType.SCROLL_UP:
- # Scroll down.
- b.complete_previous(count=3, disable_wrap_around=True)
-
- return None
-
-
-def _get_menu_item_fragments(
- completion: Completion,
- is_current_completion: bool,
- width: int,
- space_after: bool = False,
-) -> StyleAndTextTuples:
- """
- Get the style/text tuples for a menu item, styled and trimmed to the given
- width.
- """
- if is_current_completion:
- style_str = "class:completion-menu.completion.current {} {}".format(
- completion.style,
- completion.selected_style,
- )
- else:
- style_str = "class:completion-menu.completion " + completion.style
-
- text, tw = _trim_formatted_text(
- completion.display, (width - 2 if space_after else width - 1)
- )
-
- padding = " " * (width - 1 - tw)
-
- return to_formatted_text(
- cast(StyleAndTextTuples, []) + [("", " ")] + text + [("", padding)],
- style=style_str,
- )
-
-
-def _trim_formatted_text(
- formatted_text: StyleAndTextTuples, max_width: int
-) -> Tuple[StyleAndTextTuples, int]:
- """
- Trim the text to `max_width`, append dots when the text is too long.
- Returns (text, width) tuple.
- """
- width = fragment_list_width(formatted_text)
-
- # When the text is too wide, trim it.
- if width > max_width:
- result = [] # Text fragments.
- remaining_width = max_width - 3
-
- for style_and_ch in explode_text_fragments(formatted_text):
- ch_width = get_cwidth(style_and_ch[1])
-
- if ch_width <= remaining_width:
- result.append(style_and_ch)
- remaining_width -= ch_width
- else:
- break
-
- result.append(("", "..."))
-
- return result, max_width - remaining_width
- else:
- return formatted_text, width
-
-
-class CompletionsMenu(ConditionalContainer):
- # NOTE: We use a pretty big z_index by default. Menus are supposed to be
- # above anything else. We also want to make sure that the content is
- # visible at the point where we draw this menu.
- def __init__(
- self,
- max_height: Optional[int] = None,
- scroll_offset: Union[int, Callable[[], int]] = 0,
- extra_filter: FilterOrBool = True,
- display_arrows: FilterOrBool = False,
- z_index: int = 10**8,
- ) -> None:
-
- extra_filter = to_filter(extra_filter)
- display_arrows = to_filter(display_arrows)
-
- super().__init__(
- content=Window(
- content=CompletionsMenuControl(),
- width=Dimension(min=8),
- height=Dimension(min=1, max=max_height),
- scroll_offsets=ScrollOffsets(top=scroll_offset, bottom=scroll_offset),
- right_margins=[ScrollbarMargin(display_arrows=display_arrows)],
- dont_extend_width=True,
- style="class:completion-menu",
- z_index=z_index,
- ),
- # Show when there are completions but not at the point we are
- # returning the input.
- filter=has_completions & ~is_done & extra_filter,
- )
-
-
-class MultiColumnCompletionMenuControl(UIControl):
- """
- Completion menu that displays all the completions in several columns.
- When there are more completions than space for them to be displayed, an
- arrow is shown on the left or right side.
-
- `min_rows` indicates how many rows will be available in any possible case.
- When this is larger than one, it will try to use less columns and more
- rows until this value is reached.
- Be careful passing in a too big value, if less than the given amount of
- rows are available, more columns would have been required, but
- `preferred_width` doesn't know about that and reports a too small value.
- This results in less completions displayed and additional scrolling.
- (It's a limitation of how the layout engine currently works: first the
- widths are calculated, then the heights.)
-
- :param suggested_max_column_width: The suggested max width of a column.
- The column can still be bigger than this, but if there is place for two
- columns of this width, we will display two columns. This to avoid that
- if there is one very wide completion, that it doesn't significantly
- reduce the amount of columns.
- """
-
- _required_margin = 3 # One extra padding on the right + space for arrows.
-
- def __init__(self, min_rows: int = 3, suggested_max_column_width: int = 30) -> None:
- assert min_rows >= 1
-
- self.min_rows = min_rows
- self.suggested_max_column_width = suggested_max_column_width
- self.scroll = 0
-
- # Info of last rendering.
- self._rendered_rows = 0
- self._rendered_columns = 0
- self._total_columns = 0
- self._render_pos_to_completion: Dict[Tuple[int, int], Completion] = {}
- self._render_left_arrow = False
- self._render_right_arrow = False
- self._render_width = 0
-
- def reset(self) -> None:
- self.scroll = 0
-
- def has_focus(self) -> bool:
- return False
-
- def preferred_width(self, max_available_width: int) -> Optional[int]:
- """
- Preferred width: prefer to use at least min_rows, but otherwise as much
- as possible horizontally.
- """
- complete_state = get_app().current_buffer.complete_state
- if complete_state is None:
- return 0
-
- column_width = self._get_column_width(complete_state)
- result = int(
- column_width
- * math.ceil(len(complete_state.completions) / float(self.min_rows))
- )
-
- # When the desired width is still more than the maximum available,
- # reduce by removing columns until we are less than the available
- # width.
- while (
- result > column_width
- and result > max_available_width - self._required_margin
- ):
- result -= column_width
- return result + self._required_margin
-
- def preferred_height(
- self,
- width: int,
- max_available_height: int,
- wrap_lines: bool,
- get_line_prefix: Optional[GetLinePrefixCallable],
- ) -> Optional[int]:
- """
- Preferred height: as much as needed in order to display all the completions.
- """
- complete_state = get_app().current_buffer.complete_state
- if complete_state is None:
- return 0
-
- column_width = self._get_column_width(complete_state)
- column_count = max(1, (width - self._required_margin) // column_width)
-
- return int(math.ceil(len(complete_state.completions) / float(column_count)))
-
- def create_content(self, width: int, height: int) -> UIContent:
- """
- Create a UIContent object for this menu.
- """
- complete_state = get_app().current_buffer.complete_state
- if complete_state is None:
- return UIContent()
-
- column_width = self._get_column_width(complete_state)
- self._render_pos_to_completion = {}
-
- _T = TypeVar("_T")
-
- def grouper(
- n: int, iterable: Iterable[_T], fillvalue: Optional[_T] = None
- ) -> Iterable[Sequence[Optional[_T]]]:
- "grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx"
- args = [iter(iterable)] * n
- return zip_longest(fillvalue=fillvalue, *args)
-
- def is_current_completion(completion: Completion) -> bool:
- "Returns True when this completion is the currently selected one."
- return (
- complete_state is not None
- and complete_state.complete_index is not None
- and c == complete_state.current_completion
- )
-
- # Space required outside of the regular columns, for displaying the
- # left and right arrow.
- HORIZONTAL_MARGIN_REQUIRED = 3
-
- # There should be at least one column, but it cannot be wider than
- # the available width.
- column_width = min(width - HORIZONTAL_MARGIN_REQUIRED, column_width)
-
- # However, when the columns tend to be very wide, because there are
- # some very wide entries, shrink it anyway.
- if column_width > self.suggested_max_column_width:
- # `column_width` can still be bigger that `suggested_max_column_width`,
- # but if there is place for two columns, we divide by two.
- column_width //= column_width // self.suggested_max_column_width
-
- visible_columns = max(1, (width - self._required_margin) // column_width)
-
- columns_ = list(grouper(height, complete_state.completions))
- rows_ = list(zip(*columns_))
-
- # Make sure the current completion is always visible: update scroll offset.
- selected_column = (complete_state.complete_index or 0) // height
- self.scroll = min(
- selected_column, max(self.scroll, selected_column - visible_columns + 1)
- )
-
- render_left_arrow = self.scroll > 0
- render_right_arrow = self.scroll < len(rows_[0]) - visible_columns
-
- # Write completions to screen.
- fragments_for_line = []
-
- for row_index, row in enumerate(rows_):
- fragments: StyleAndTextTuples = []
- middle_row = row_index == len(rows_) // 2
-
- # Draw left arrow if we have hidden completions on the left.
- if render_left_arrow:
- fragments.append(("class:scrollbar", "<" if middle_row else " "))
- elif render_right_arrow:
- # Reserve one column empty space. (If there is a right
- # arrow right now, there can be a left arrow as well.)
- fragments.append(("", " "))
-
- # Draw row content.
- for column_index, c in enumerate(row[self.scroll :][:visible_columns]):
- if c is not None:
- fragments += _get_menu_item_fragments(
- c, is_current_completion(c), column_width, space_after=False
- )
-
- # Remember render position for mouse click handler.
- for x in range(column_width):
- self._render_pos_to_completion[
- (column_index * column_width + x, row_index)
- ] = c
- else:
- fragments.append(("class:completion", " " * column_width))
-
- # Draw trailing padding for this row.
- # (_get_menu_item_fragments only returns padding on the left.)
- if render_left_arrow or render_right_arrow:
- fragments.append(("class:completion", " "))
-
- # Draw right arrow if we have hidden completions on the right.
- if render_right_arrow:
- fragments.append(("class:scrollbar", ">" if middle_row else " "))
- elif render_left_arrow:
- fragments.append(("class:completion", " "))
-
- # Add line.
- fragments_for_line.append(
- to_formatted_text(fragments, style="class:completion-menu")
- )
-
- self._rendered_rows = height
- self._rendered_columns = visible_columns
- self._total_columns = len(columns_)
- self._render_left_arrow = render_left_arrow
- self._render_right_arrow = render_right_arrow
- self._render_width = (
- column_width * visible_columns + render_left_arrow + render_right_arrow + 1
- )
-
- def get_line(i: int) -> StyleAndTextTuples:
- return fragments_for_line[i]
-
- return UIContent(get_line=get_line, line_count=len(rows_))
-
- def _get_column_width(self, complete_state: CompletionState) -> int:
- """
- Return the width of each column.
- """
- return max(get_cwidth(c.display_text) for c in complete_state.completions) + 1
-
- def mouse_handler(self, mouse_event: MouseEvent) -> "NotImplementedOrNone":
- """
- Handle scroll and click events.
- """
- b = get_app().current_buffer
-
- def scroll_left() -> None:
- b.complete_previous(count=self._rendered_rows, disable_wrap_around=True)
- self.scroll = max(0, self.scroll - 1)
-
- def scroll_right() -> None:
- b.complete_next(count=self._rendered_rows, disable_wrap_around=True)
- self.scroll = min(
- self._total_columns - self._rendered_columns, self.scroll + 1
- )
-
- if mouse_event.event_type == MouseEventType.SCROLL_DOWN:
- scroll_right()
-
- elif mouse_event.event_type == MouseEventType.SCROLL_UP:
- scroll_left()
-
- elif mouse_event.event_type == MouseEventType.MOUSE_UP:
- x = mouse_event.position.x
- y = mouse_event.position.y
-
- # Mouse click on left arrow.
- if x == 0:
- if self._render_left_arrow:
- scroll_left()
-
- # Mouse click on right arrow.
- elif x == self._render_width - 1:
- if self._render_right_arrow:
- scroll_right()
-
- # Mouse click on completion.
- else:
- completion = self._render_pos_to_completion.get((x, y))
- if completion:
- b.apply_completion(completion)
-
- return None
-
- def get_key_bindings(self) -> "KeyBindings":
- """
- Expose key bindings that handle the left/right arrow keys when the menu
- is displayed.
- """
- from prompt_toolkit.key_binding.key_bindings import KeyBindings
-
- kb = KeyBindings()
-
- @Condition
- def filter() -> bool:
- "Only handle key bindings if this menu is visible."
- app = get_app()
- complete_state = app.current_buffer.complete_state
-
- # There need to be completions, and one needs to be selected.
- if complete_state is None or complete_state.complete_index is None:
- return False
-
- # This menu needs to be visible.
- return any(window.content == self for window in app.layout.visible_windows)
-
- def move(right: bool = False) -> None:
- buff = get_app().current_buffer
- complete_state = buff.complete_state
-
- if complete_state is not None and complete_state.complete_index is not None:
- # Calculate new complete index.
- new_index = complete_state.complete_index
- if right:
- new_index += self._rendered_rows
- else:
- new_index -= self._rendered_rows
-
- if 0 <= new_index < len(complete_state.completions):
- buff.go_to_completion(new_index)
-
- # NOTE: the is_global is required because the completion menu will
- # never be focussed.
-
- @kb.add("left", is_global=True, filter=filter)
- def _left(event: E) -> None:
- move()
-
- @kb.add("right", is_global=True, filter=filter)
- def _right(event: E) -> None:
- move(True)
-
- return kb
-
-
-class MultiColumnCompletionsMenu(HSplit):
- """
- Container that displays the completions in several columns.
- When `show_meta` (a :class:`~prompt_toolkit.filters.Filter`) evaluates
- to True, it shows the meta information at the bottom.
- """
-
- def __init__(
- self,
- min_rows: int = 3,
- suggested_max_column_width: int = 30,
- show_meta: FilterOrBool = True,
- extra_filter: FilterOrBool = True,
- z_index: int = 10**8,
- ) -> None:
-
- show_meta = to_filter(show_meta)
- extra_filter = to_filter(extra_filter)
-
- # Display filter: show when there are completions but not at the point
- # we are returning the input.
- full_filter = has_completions & ~is_done & extra_filter
-
- @Condition
- def any_completion_has_meta() -> bool:
- complete_state = get_app().current_buffer.complete_state
- return complete_state is not None and any(
- c.display_meta for c in complete_state.completions
- )
-
- # Create child windows.
- # NOTE: We don't set style='class:completion-menu' to the
- # `MultiColumnCompletionMenuControl`, because this is used in a
- # Float that is made transparent, and the size of the control
- # doesn't always correspond exactly with the size of the
- # generated content.
- completions_window = ConditionalContainer(
- content=Window(
- content=MultiColumnCompletionMenuControl(
- min_rows=min_rows,
- suggested_max_column_width=suggested_max_column_width,
- ),
- width=Dimension(min=8),
- height=Dimension(min=1),
- ),
- filter=full_filter,
- )
-
- meta_window = ConditionalContainer(
- content=Window(content=_SelectedCompletionMetaControl()),
- filter=show_meta & full_filter & any_completion_has_meta,
- )
-
- # Initialise split.
- super().__init__([completions_window, meta_window], z_index=z_index)
-
-
-class _SelectedCompletionMetaControl(UIControl):
- """
- Control that shows the meta information of the selected completion.
- """
-
- def preferred_width(self, max_available_width: int) -> Optional[int]:
- """
- Report the width of the longest meta text as the preferred width of this control.
-
- It could be that we use less width, but this way, we're sure that the
- layout doesn't change when we select another completion (E.g. that
- completions are suddenly shown in more or fewer columns.)
- """
- app = get_app()
- if app.current_buffer.complete_state:
- state = app.current_buffer.complete_state
- return 2 + max(get_cwidth(c.display_meta_text) for c in state.completions)
- else:
- return 0
-
- def preferred_height(
- self,
- width: int,
- max_available_height: int,
- wrap_lines: bool,
- get_line_prefix: Optional[GetLinePrefixCallable],
- ) -> Optional[int]:
- return 1
-
- def create_content(self, width: int, height: int) -> UIContent:
- fragments = self._get_text_fragments()
-
- def get_line(i: int) -> StyleAndTextTuples:
- return fragments
-
- return UIContent(get_line=get_line, line_count=1 if fragments else 0)
-
- def _get_text_fragments(self) -> StyleAndTextTuples:
- style = "class:completion-menu.multi-column-meta"
- state = get_app().current_buffer.complete_state
-
- if (
- state
- and state.current_completion
- and state.current_completion.display_meta_text
- ):
- return to_formatted_text(
- cast(StyleAndTextTuples, [("", " ")])
- + state.current_completion.display_meta
- + [("", " ")],
- style=style,
- )
-
- return []