Skip to content

Commit 643a66d

Browse files
Improve code completion performance (MultiColumnCompletionMenuControl).
Add cache in `MultiColumnCompletionMenuControl._get_column_width` to improve the performance while navigating a large amount of completions.
1 parent 92cc433 commit 643a66d

File tree

1 file changed

+28
-2
lines changed

1 file changed

+28
-2
lines changed

src/prompt_toolkit/layout/menus.py

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
Union,
1414
cast,
1515
)
16+
from weakref import WeakKeyDictionary, WeakValueDictionary
1617

1718
from prompt_toolkit.application.current import get_app
1819
from prompt_toolkit.buffer import CompletionState
@@ -333,6 +334,16 @@ def __init__(self, min_rows: int = 3, suggested_max_column_width: int = 30) -> N
333334
self.suggested_max_column_width = suggested_max_column_width
334335
self.scroll = 0
335336

337+
# Cache for column width computations. This computation is not cheap,
338+
# so we don't want to do it over and over again while the user
339+
# navigates through the completions.
340+
# (map `completion_state` to `(completion_count, width)`. We remember
341+
# the count, because a completer can add new completions to the
342+
# `CompletionState` while loading.)
343+
self._column_width_for_completion_state: "WeakKeyDictionary[CompletionState, Tuple[int, int]]" = (
344+
WeakKeyDictionary()
345+
)
346+
336347
# Info of last rendering.
337348
self._rendered_rows = 0
338349
self._rendered_columns = 0
@@ -509,11 +520,26 @@ def get_line(i: int) -> StyleAndTextTuples:
509520

510521
return UIContent(get_line=get_line, line_count=len(rows_))
511522

512-
def _get_column_width(self, complete_state: CompletionState) -> int:
523+
def _get_column_width(self, completion_state: CompletionState) -> int:
513524
"""
514525
Return the width of each column.
515526
"""
516-
return max(get_cwidth(c.display_text) for c in complete_state.completions) + 1
527+
try:
528+
count, width = self._column_width_for_completion_state[completion_state]
529+
if count != len(completion_state.completions):
530+
# Number of completions changed, recompute.
531+
raise KeyError
532+
return width
533+
except KeyError:
534+
result = (
535+
max(get_cwidth(c.display_text) for c in completion_state.completions)
536+
+ 1
537+
)
538+
self._column_width_for_completion_state[completion_state] = (
539+
len(completion_state.completions),
540+
result,
541+
)
542+
return result
517543

518544
def mouse_handler(self, mouse_event: MouseEvent) -> "NotImplementedOrNone":
519545
"""

0 commit comments

Comments
 (0)