Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions src/prompt_toolkit/buffer.py
Original file line number Diff line number Diff line change
Expand Up @@ -535,6 +535,15 @@ def _text_changed(self) -> None:
if self.validator and self.validate_while_typing():
get_app().create_background_task(self._async_validator())

# Auto suggestion.
# (This also happens on all change events, also when deleting text.
# Suggestions have just been cleared above, so ask the auto suggester
# for a new one for the current text. When the text was changed by
# `insert_text`, this can schedule the suggester a second time, but
# that is harmless: it is guarded to run only one at a time.)
if self.auto_suggest:
get_app().create_background_task(self._async_suggester())

def _cursor_position_changed(self) -> None:
# Remove any complete state.
# (Input validation should only be undone when the cursor position
Expand Down
50 changes: 50 additions & 0 deletions tests/test_buffer.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
from __future__ import annotations

import asyncio

import pytest

from prompt_toolkit.application import Application
from prompt_toolkit.application.current import set_app
from prompt_toolkit.auto_suggest import AutoSuggest, Suggestion
from prompt_toolkit.buffer import Buffer
from prompt_toolkit.document import Document
from prompt_toolkit.input import DummyInput
from prompt_toolkit.output import DummyOutput


@pytest.fixture
Expand Down Expand Up @@ -110,3 +118,45 @@ def test_swap_characters_before_cursor(_buffer):
_buffer.swap_characters_before_cursor()

assert _buffer.text == "hello wrold"


class _AlwaysSuggest(AutoSuggest):
"""Auto suggester that records the documents it was called with."""

def __init__(self) -> None:
self.documents: list[Document] = []

def get_suggestion(self, buffer: Buffer, document: Document) -> Suggestion:
self.documents.append(document)
return Suggestion(" suggestion")


def test_auto_suggest_after_delete():
"""
A suggestion should be generated after deleting text as well, not only
after inserting text.
"""

async def run() -> None:
suggester = _AlwaysSuggest()
buff = Buffer(auto_suggest=suggester)
app = Application(input=DummyInput(), output=DummyOutput())

with set_app(app):
# Typing generates a suggestion.
buff.insert_text("hello")
await asyncio.sleep(0.1)
assert buff.suggestion is not None
assert buff.suggestion.text == " suggestion"
assert [d.text for d in suggester.documents] == ["hello"]

# Deleting a character clears the suggestion, but a new one
# should be generated for the remaining text.
buff.delete_before_cursor()
await asyncio.sleep(0.1)
assert buff.text == "hell"
assert buff.suggestion is not None
assert buff.suggestion.text == " suggestion"
assert suggester.documents[-1].text == "hell"

asyncio.run(run())
Loading