feat(v0.4.3): TUI 命令 Tab 补全

CommandSuggester 实现 Textual Suggester API:
- 根据已注册命令列表提供前缀匹配补全
- 前缀无匹配时 fallback 到包含匹配
- 按长度排序,优先最短匹配
- 在 Input placeholder 提示 'Tab 补全'

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
qinglong
2026-06-13 11:06:31 +08:00
parent 2d0a37ba19
commit 37d1110f2b
+39 -1
View File
@@ -9,6 +9,8 @@ from textual.app import App
from textual.containers import Container, ScrollableContainer, Horizontal from textual.containers import Container, ScrollableContainer, Horizontal
from textual.widgets import Static, Input, Header, Footer, LoadingIndicator from textual.widgets import Static, Input, Header, Footer, LoadingIndicator
from textual.reactive import reactive from textual.reactive import reactive
from textual.suggester import Suggester
from textual.suggestion import Suggestion
from typing import List, Dict from typing import List, Dict
import asyncio import asyncio
from datetime import datetime from datetime import datetime
@@ -493,6 +495,37 @@ class PluginStatusPanel(Static):
pass pass
class CommandSuggester(Suggester):
"""命令补全 — 根据已注册命令提供 Tab 补全建议"""
def __init__(self, command_service=None):
self._cmd_svc = command_service
def set_command_service(self, svc):
self._cmd_svc = svc
async def get_suggestion(self, value: str) -> Suggestion | None:
"""根据当前输入返回匹配的命令建议"""
if not value or not self._cmd_svc:
return None
value_lower = value.lower().strip()
commands = getattr(self._cmd_svc, "commands", {})
# 优先前缀匹配
candidates = sorted(
[n for n in commands if n.startswith(value_lower)],
key=len,
)
if not candidates:
candidates = sorted(
[n for n in commands if value_lower in n],
key=len,
)
if candidates:
cmd = candidates[0]
return Suggestion(cmd[len(value_lower):])
return None
class TUIFramework(App): class TUIFramework(App):
"""TUI框架应用""" """TUI框架应用"""
def __init__(self, config, log_service, command_service, plugin_service=None): def __init__(self, config, log_service, command_service, plugin_service=None):
@@ -623,7 +656,12 @@ class TUIFramework(App):
self.message_display, self.message_display,
id="message-area" id="message-area"
) )
self.command_input = Input(placeholder="输入指令...", id="command-input") self.command_suggester = CommandSuggester(self.command_service)
self.command_input = Input(
placeholder="输入指令... Tab 补全",
suggester=self.command_suggester,
id="command-input",
)
yield Container( yield Container(
self.command_input, self.command_input,
id="input-area" id="input-area"