Current section
Files
Jump to
Current section
Files
lib/cmdc_tui/dialogs/slash_autocomplete.ex
defmodule CmdcTui.Dialogs.SlashAutocomplete do
@moduledoc """
Textarea 内嵌 Slash 命令自动补全 overlay。
当 Textarea 输入内容以 `/` 开头时,在输入框上方叠加此 Popup。
使用 ExRatatui.Widgets.SlashCommands 解析/匹配/渲染。
"""
alias ExRatatui.Widgets.SlashCommands
alias ExRatatui.Layout.Rect
alias CmdcTui.Commands
@doc "根据当前 Textarea 内容更新 slash_autocomplete 状态。"
@spec check_slash(CmdcTui.State.t()) :: CmdcTui.State.t()
def check_slash(state) do
if is_nil(state.textarea) do
%{state | slash_autocomplete: false, slash_matches: [], slash_selected: 0}
else
value = ExRatatui.textarea_get_value(state.textarea)
case SlashCommands.parse(value) do
{:command, prefix} ->
matches = Commands.match(prefix)
%{
state
| slash_autocomplete: length(matches) > 0,
slash_matches: matches,
slash_selected: 0
}
:no_command ->
%{state | slash_autocomplete: false, slash_matches: [], slash_selected: 0}
end
end
end
@doc "渲染 slash 自动补全 Popup(叠加在 input_area 上方)。"
@spec render(CmdcTui.State.t(), Rect.t()) :: [{ExRatatui.widget(), Rect.t()}]
def render(%{slash_autocomplete: false}, _area), do: []
def render(%{slash_matches: []}, _area), do: []
def render(state, area) do
SlashCommands.render_autocomplete(state.slash_matches,
area: area,
selected: state.slash_selected,
percent_width: 45,
percent_height: 35,
highlight_style: %ExRatatui.Style{fg: :black, bg: :cyan, modifiers: [:bold]},
style: %ExRatatui.Style{fg: :white}
)
end
@doc "选择上一个 slash 匹配项。"
@spec select_prev(CmdcTui.State.t()) :: CmdcTui.State.t()
def select_prev(state) do
%{state | slash_selected: max(0, state.slash_selected - 1)}
end
@doc "选择下一个 slash 匹配项。"
@spec select_next(CmdcTui.State.t()) :: CmdcTui.State.t()
def select_next(state) do
max_idx = max(0, length(state.slash_matches) - 1)
%{state | slash_selected: min(state.slash_selected + 1, max_idx)}
end
@doc "执行当前选中的 slash 命令,清空 textarea,关闭补全。"
@spec execute_selected(CmdcTui.State.t()) :: {CmdcTui.State.t(), :ok | :quit}
def execute_selected(state) do
case Enum.at(state.slash_matches, state.slash_selected) do
nil ->
{close(state), :ok}
cmd ->
state = close(state)
if state.textarea do
ExRatatui.textarea_set_value(state.textarea, "")
end
case Commands.execute(cmd.name, state) do
{:ok, new_state} -> {new_state, :ok}
{:error, _reason} -> {state, :ok}
:quit -> {state, :quit}
end
end
end
defp close(state) do
%{state | slash_autocomplete: false, slash_matches: [], slash_selected: 0}
end
end