实现输入时的命令提示功能
随着命令越来越多,我自己也记不清有什么命令、有什么参数、有什么注意事项了。
所以就有了命令提示的需求,接下来我们来实现它。

设计
与一般的命令提示不同,我无法做到详细到参数提示,而只能做到命令格式提示、命令说明提示。但这样也足够了,至少我现在的体验十分不错。
提示分以下几种情况:
- 输入
;时 - 输入命令时
- 输入命令参数时
我的设计是:
- 当输入
;时,提示所有可用的命令,包括自定义别名。 - 当输入命令时,模糊匹配命令,并反馈已匹配的命令。
- 当输入命令参数时,命令已明确,所以可以提示命令格式(帮助文档),及可能存在的配置文件信息、需要补充的信息等。
在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()
至此,所有代码就完成了。
不过,最后还需要规范一下命令的帮助文档,以便命令提示不会过于混乱。
<>必须[]可选...多参|互斥
浙公网安备 33010602011771号