实现输入时的命令提示功能
随着命令越来越多,我自己也记不清有什么命令、有什么参数、有什么注意事项了。
所以就有了命令提示的需求,接下来我们来实现它。
设计
与一般的命令提示不同,我无法做到详细到参数提示,而只能做到命令格式提示、命令说明提示。但这样也足够了,至少我现在的体验十分不错。
提示分以下几种情况:
- 输入
;
时 - 输入命令时
- 输入命令参数时
我的设计是:
- 当输入
;
时,提示所有可用的命令,包括自定义别名。 - 当输入命令时,模糊匹配命令,并反馈已匹配的命令。
- 当输入命令参数时,命令已明确,所以可以提示命令格式(帮助文档),及可能存在的配置文件信息、需要补充的信息等。
在ui中的表现:
显示命令提示时,关闭所有历史;反之依然。
实现
逻辑方面比较简单,复杂的是ui的高度控制
逻辑
首先,我们需要扩展处理器管理器(handleMgr.ahk),在注册命令时保存命令的帮助文档:
static h := Map(), mans := Map() static Register(which, handler) { if not handler() is BaseHandle throw TypeError('无效的处理器:' handler.Prototype.__Class) if Mgr.h.Has(which) throw Error('注册重复的命令:' which) Mgr.h.Set(which, handler), Mgr.mans.Set(which, handler.Echo()) return this }
这样就可以用于匹配及输入提示了,具体的:
; 匹配 ks := [] Mgr.mans.keys.foreach(v => InStr(v, _k) && ks.Push(v)) ; 输出 r := Mgr.mans.Get(ks[1])
然后,扩展main.ahk(主ui脚本)。
同样(历史记录功能),需要增加一个变量sh
来记录提示text
控件的高度,并与ui高度h
和历史记录高度hh
进行比较,即可得知应该如何修改ui高度。
听起来可能很绕,实际也确实很绕,所以不建议深究。
对main.ahk脚本的扩展代码如下:
- 增加提示控件及绑定事件
this.edit.OnEvent('change', (g, *) => this._Suggestion(g.value)) this.st := this.AddText('x20 y' this.hh + 2 ' w300 Hidden Backgroundd2f1d9') this.st.OnEvent('ContextMenu', (v, *) => this.OnCopy(v))
- 实现
_Suggestion
_Suggestion(cmd) { if this.loading ; 如果已输入阻塞命令,则返回 return this.st.Visible := true if cmd = ';' ; 情况一 return _showSgAndFit(Mgr.mans.Keys.Join(',')) if cmd.beginWith(';') cmd := cmd.substring(2) if !(cmd := LTrim(cmd)) ; 清除提示 return this._CloseSuggestion() ks := [], _k := (i := InStr(cmd, A_Space)) ? SubStr(cmd, 1, i - 1) : cmd if i { ; 情况三,命令已确定,可以输出更多信息 if Mgr.mans.Has(_k) { return _showSgAndFit(Mgr.mans.Get(_k) '`n--CONF`n' Mgr.h.Get(_k).Conf()) } else if alias.Has(_k) { return _showSgAndFit(alias.Get(_k) '`n--`nALIAS') } else if this.buildInCMD.Has(_k) return _showSgAndFit(_k '-buildin' '`n--`nBUILDIN') } ; 支持注册命令、别名、内置命令 Mgr.mans.keys.foreach(v => InStr(v, _k) && ks.Push(v)) alias.data.keys.foreach(v => InStr(v, _k) && ks.Push(v)) this.buildInCMD.keys.foreach(v => InStr(v, _k) && ks.Push(v)) if ks.Length = 1 { ; 最佳匹配,输出命令帮助文档 r := Mgr.mans.Get(ks[1], alias.Get(ks[1], ks[1] '-buildin')) } else if !ks.Length { ; 无匹配 r := 'nil' } else r := ks.Join(',') ; 情况二,所有可能的命令 return _showSgAndFit(r) _showSgAndFit(str) { ; 用于调整ui高度,不用深究 this.st.Text := slice(str, MeowTool.maxLen + 3), this.st.raw := str this.st.Move(, , , (this.st.Text.Count('`n') + 1) * 20), this.st.GetPos(, , , &h) if h = this.sh return if this.sh = 0 { ; 第一次 this.l.foreach(v => (v.Visible := false)) this.sh := h, ch := this.h, diff := this.hh - (this.sh + 57) if diff > 0 { ; 缩小 loop diff this.Move(, , , ch - A_Index) } else if diff < 0 { loop -diff this.Move(, , , ch + A_Index) } this.h -= diff } else { if h > this.sh { ; 变高 ch := this.h, vh := h - this.sh loop vh this.Move(, , , ch + A_Index) this.h += vh } else if h < this.sh { ch := this.h, vh := this.sh - h loop vh this.Move(, , , ch - A_Index) this.h -= vh } } this.extra.Move(, , , this.h - 22), this.sh := h } }
- 关闭提示
_CloseSuggestion() { ; 同样是调整高度 if !this.sh return this.st.Text := '', this.st.Visible := false this.l.foreach(v => (v.Visible := true)) if this.l.Length { ch := this.h, diff := this.hh - (this.sh + 57) if diff > 0 { loop diff this.Move(, , , ch + A_Index) } else if diff < 0 { loop -diff this.Move(, , , ch - A_Index) } this.h += diff this.extra.Move(, , , this.h - 20) } else { loop this.h - 55 this.Move(, , , this.h - A_Index) this.hh := 52, this.h := 65 this.extra.Move(, , , 30) } this.sh := 0 }
- 适配原先代码
修改Handle
方法,在添加历史前关闭提示
this._CloseSuggestion(), this.AddHistory(cmd, true), this._Loading()
至此,所有代码就完成了。
不过,最后还需要规范一下命令的帮助文档,以便命令提示不会过于混乱。
<>
必须[]
可选...
多参|
互斥
合集:
ahk-meow-tool
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 百万级群聊的设计实践
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 全网最简单!3分钟用满血DeepSeek R1开发一款AI智能客服,零代码轻松接入微信、公众号、小程
· .NET 10 首个预览版发布,跨平台开发与性能全面提升
· 《HelloGitHub》第 107 期