Skip to content

Commit 92cc433

Browse files
Improve code completion performance (changes to Buffer).
Don't invalidate the UI for every completion that we consume.
1 parent 92f188a commit 92cc433

File tree

1 file changed

+35
-8
lines changed

1 file changed

+35
-8
lines changed

src/prompt_toolkit/buffer.py

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
get_common_complete_suffix,
4343
)
4444
from .document import Document
45+
from .eventloop import aclosing
4546
from .filters import FilterOrBool, to_filter
4647
from .history import History, InMemoryHistory
4748
from .search import SearchDirection, SearchState
@@ -1736,15 +1737,41 @@ def proceed() -> bool:
17361737
while generating completions."""
17371738
return self.complete_state == complete_state
17381739

1739-
async for completion in self.completer.get_completions_async(
1740-
document, complete_event
1741-
):
1742-
complete_state.completions.append(completion)
1743-
self.on_completions_changed.fire()
1740+
refresh_needed = asyncio.Event()
1741+
1742+
async def refresh_while_loading() -> None:
1743+
"""Background loop to refresh the UI at most 3 times a second
1744+
while the completion are loading. Calling
1745+
`on_completions_changed.fire()` for every completion that we
1746+
receive is too expensive when there are many completions. (We
1747+
could tune `Application.max_render_postpone_time` and
1748+
`Application.min_redraw_interval`, but having this here is a
1749+
better approach.)
1750+
"""
1751+
while True:
1752+
self.on_completions_changed.fire()
1753+
refresh_needed.clear()
1754+
await asyncio.sleep(0.3)
1755+
await refresh_needed.wait()
17441756

1745-
# If the input text changes, abort.
1746-
if not proceed():
1747-
break
1757+
refresh_task = asyncio.create_task(refresh_while_loading())
1758+
try:
1759+
# Load.
1760+
async with aclosing(
1761+
self.completer.get_completions_async(document, complete_event)
1762+
) as async_generator:
1763+
async for completion in async_generator:
1764+
complete_state.completions.append(completion)
1765+
refresh_needed.set()
1766+
1767+
# If the input text changes, abort.
1768+
if not proceed():
1769+
break
1770+
finally:
1771+
refresh_task.cancel()
1772+
1773+
# Refresh one final time after we got everything.
1774+
self.on_completions_changed.fire()
17481775

17491776
completions = complete_state.completions
17501777

0 commit comments

Comments
 (0)