Merge pull request #47701 from kshitijk4poor/salvage/cli-completer-keystroke-latency

fix(cli): keep typing responsive by running completion off the UI event loop
This commit is contained in:
kshitij 2026-06-17 12:42:50 +05:30 committed by GitHub
commit 9901141d64
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 61 additions and 1 deletions

9
cli.py
View file

@ -12024,6 +12024,7 @@ class HermesCLI(CLIAgentSetupMixin, CLICommandsMixin):
# Create the input area with multiline (Alt+Enter), autocomplete, and paste handling
from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
from prompt_toolkit.completion import ThreadedCompleter
_completer = SlashCommandCompleter(
@ -12039,7 +12040,13 @@ class HermesCLI(CLIAgentSetupMixin, CLICommandsMixin):
wrap_lines=True,
read_only=Condition(lambda: bool(cli_ref._command_running)),
history=FileHistory(str(self._history_file)),
completer=_completer,
# complete_while_typing fires the completer on every keystroke. The
# completer does blocking work — fuzzy @-file indexing shells out to
# rg/fd (up to a 2s timeout) and path completion hits os.listdir/stat
# — so running it inline would stall the render loop on each key (very
# noticeable on WSL2/slow filesystems). ThreadedCompleter moves it off
# the UI event loop, keeping typing responsive.
completer=ThreadedCompleter(_completer),
complete_while_typing=True,
auto_suggest=SlashCommandAutoSuggest(
history_suggest=AutoSuggestFromHistory(),

View file

@ -1280,6 +1280,10 @@ class SlashCommandCompleter(Completer):
current word doesn't look like a path. A word is path-like when
it starts with ``./``, ``../``, ``~/``, ``/``, or contains a
``/`` separator (e.g. ``src/main.py``).
Tokens containing a ``://`` scheme separator (e.g. URLs like
``https://example.com/x``) are excluded even though they contain a
``/`` they are never useful local-path completions.
"""
if not text:
return None
@ -1291,6 +1295,12 @@ class SlashCommandCompleter(Completer):
word = text[i + 1:]
if not word:
return None
# URLs contain "/" but are not local paths. Treating them as paths fires
# os.listdir on every keystroke while typing/pasting a link (e.g. an
# https:// URL becomes a listdir of "https:") — pure latency, never a
# useful completion. Skip any token with a scheme separator.
if "://" in word:
return None
# Only trigger path completion for path-like tokens
if word.startswith(("./", "../", "~/", "/")) or "/" in word:
return word

View file

@ -59,6 +59,32 @@ class TestExtractPathWord:
def test_just_tilde_slash(self):
assert SlashCommandCompleter._extract_path_word("~/") == "~/"
def test_url_is_not_treated_as_path(self):
# A URL contains "/" so the bare slash heuristic would otherwise return
# it as a path word, firing os.listdir("https:") on every keystroke.
assert SlashCommandCompleter._extract_path_word("see https://paste.rs/abc") is None
def test_http_url_is_not_treated_as_path(self):
assert SlashCommandCompleter._extract_path_word("ref http://example.com/x") is None
def test_scheme_alone_is_enough_to_reject(self):
# The "://" scheme separator is the signal, even before any path part
# has been typed.
assert SlashCommandCompleter._extract_path_word("ssh://host") is None
def test_path_word_with_colon_but_no_scheme_still_resolves(self):
# Only the "://" scheme separator should reject; a bare colon inside a
# real path token must not regress path detection.
assert (
SlashCommandCompleter._extract_path_word("open ./a:b/c.py") == "./a:b/c.py"
)
def test_ordinary_path_unaffected_by_url_guard(self):
assert (
SlashCommandCompleter._extract_path_word("edit src/pkg/mod.py")
== "src/pkg/mod.py"
)
class TestPathCompletions:
def test_lists_current_directory(self, tmp_path):
@ -155,6 +181,23 @@ class TestIntegration:
completions = list(completer.get_completions(doc, event))
assert completions == []
def test_url_does_not_touch_filesystem(self, completer, monkeypatch):
# Regression for laggy typing: a URL token contains "/", so before the
# scheme guard it reached _path_completions and called os.listdir on
# every keystroke. Assert no completions AND that the filesystem is
# never touched while a URL is under the cursor.
import hermes_cli.commands as commands_mod
def _fail(*_args, **_kwargs):
raise AssertionError("os.listdir must not run for a URL token")
monkeypatch.setattr(commands_mod.os, "listdir", _fail)
text = "open https://paste.rs/abc"
doc = Document(text, cursor_position=len(text))
event = MagicMock()
assert list(completer.get_completions(doc, event)) == []
def test_absolute_path_triggers_completion(self, completer):
doc = Document("check /etc/hos", cursor_position=14)
event = MagicMock()