Coverage for src/lilbee/cli/tui/widgets/suggester.py: 100%
44 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-09-04 17:08 +0000
« prev ^ index » next coverage.py v7.15.2, created at 2026-09-04 17:08 +0000
1"""Tab completion for the chat input via Textual's Suggester API."""
3from __future__ import annotations
5from textual.suggester import Suggester
7from lilbee.cli.tui.command_registry import completion_names
8from lilbee.cli.tui.widgets.autocomplete import (
9 _fetch_document_names,
10 _model_options,
11 _setting_options,
12 _theme_options,
13)
15_SLASH_COMMANDS = completion_names()
18class SlashSuggester(Suggester):
19 """Context-aware suggestions for the chat input.
20 Suggests slash command names when input starts with '/'.
21 Suggests argument values for commands that take them.
22 """
24 async def get_suggestion(self, value: str) -> str | None:
25 if not value:
26 return None
28 if value.startswith("/") and " " not in value:
29 return self._suggest_command(value)
31 if " " in value:
32 return self._suggest_argument(value)
34 return None
36 def _suggest_command(self, prefix: str) -> str | None:
37 for cmd in _SLASH_COMMANDS:
38 if cmd.startswith(prefix) and cmd != prefix:
39 return cmd
40 return None
42 def _suggest_argument(self, value: str) -> str | None:
43 cmd, _, partial = value.partition(" ")
44 cmd = cmd.lower()
46 if cmd == "/model":
47 return self._suggest_from_list(value, partial, self._get_model_names())
48 if cmd == "/set":
49 return self._suggest_from_list(value, partial, self._get_setting_names())
50 if cmd == "/delete":
51 return self._suggest_from_list(value, partial, self._get_document_names())
52 if cmd == "/theme":
53 return self._suggest_from_list(value, partial, self._get_theme_names())
54 return None
56 def _suggest_from_list(self, full: str, partial: str, options: list[str]) -> str | None:
57 for opt in options:
58 if opt.startswith(partial) and opt != partial:
59 return full[: len(full) - len(partial)] + opt
60 return None
62 # Option sources are shared with the completion overlay (autocomplete);
63 # these stay as methods so callers and tests can override per-suggester.
64 def _get_model_names(self) -> list[str]:
65 return _model_options()
67 def _get_setting_names(self) -> list[str]:
68 return _setting_options()
70 def _get_document_names(self) -> list[str]:
71 return _fetch_document_names()
73 def _get_theme_names(self) -> list[str]:
74 return _theme_options()