随笔 - 52,  文章 - 0,  评论 - 7,  阅读 - 9836

实现输入时的命令提示功能

随着命令越来越多,我自己也记不清有什么命令、有什么参数、有什么注意事项了。

所以就有了命令提示的需求,接下来我们来实现它。

设计

与一般的命令提示不同,我无法做到详细到参数提示,而只能做到命令格式提示、命令说明提示。但这样也足够了,至少我现在的体验十分不错。

提示分以下几种情况:

  • 输入;
  • 输入命令时
  • 输入命令参数时

我的设计是:

  • 当输入;时,提示所有可用的命令,包括自定义别名。
  • 当输入命令时,模糊匹配命令,并反馈已匹配的命令。
  • 当输入命令参数时,命令已明确,所以可以提示命令格式(帮助文档),及可能存在的配置文件信息、需要补充的信息等。

在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()

至此,所有代码就完成了。

不过,最后还需要规范一下命令的帮助文档,以便命令提示不会过于混乱。

  • <>必须
  • []可选
  • ...多参
  • |互斥
posted on   落寞的雪  阅读(10)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 百万级群聊的设计实践
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 全网最简单!3分钟用满血DeepSeek R1开发一款AI智能客服,零代码轻松接入微信、公众号、小程
· .NET 10 首个预览版发布,跨平台开发与性能全面提升
· 《HelloGitHub》第 107 期

< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5
点击右上角即可分享
微信分享提示