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

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

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

设计

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

提示分以下几种情况:

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

我的设计是:

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

在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 2025-02-06 22:27  落寞的雪  阅读(43)  评论(0)    收藏  举报