實現輸入時的命令提示功能
隨著命令越來越多,我自己也記不清有什么命令、有什么參數、有什么注意事項了。
所以就有了命令提示的需求,接下來我們來實現它。

設計
與一般的命令提示不同,我無法做到詳細到參數提示,而只能做到命令格式提示、命令說明提示。但這樣也足夠了,至少我現在的體驗十分不錯。
提示分以下幾種情況:
- 輸入
;時 - 輸入命令時
- 輸入命令參數時
我的設計是:
- 當輸入
;時,提示所有可用的命令,包括自定義別名。 - 當輸入命令時,模糊匹配命令,并反饋已匹配的命令。
- 當輸入命令參數時,命令已明確,所以可以提示命令格式(幫助文檔),及可能存在的配置文件信息、需要補充的信息等。
在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號