我觉着,命令行工具都应该有transient这样的界面

感觉transient这种方式非常合理:

  • 如果你记住了参数用的顺手,它一闪而过,不会影响你,几乎不会有任何中间的阻碍,甚至相当于它不存在。你只要连续按你记住的命令就可以了。
  • 如果你不熟悉参数,或者生僻的没记住的参数选项。它可以在输入命令的中间状态停下来,给你提示,然后继续。这要比man或者搜一下命令方便。

可惜它只限于emacs里。如果有一个通用的此类组件可以被其他命令行gui工具使用。我感觉会很有用。就拿magit和常规的git工具来对比。加入git也有类似transient这种库可以调用,那git这么多复杂的子命令,参数,都可以有一个无障碍界面来展示和提供选择。那几乎就是emacs之外的magit了。

再比如其他的命令行工具比如find grep…mysql客户端,redis客户端…

有没有可能作一个这样的库,lib之类的东西?各位觉得呢?

3 个赞

Emacs 因为有 keymap。

如果想要 find 也有提示,可以用 fish:

$ find . -[TAB]
-a  -and                          (Result is only true if both previous and next action are true)
-amin                                        (File last accessed specified number of minutes ago)
-anewer                                 (File last accessed more recently than file was modified)
-atime                                          (File last accessed specified number of days ago)
…and 67 more rows

但它不是凭空来的,而是有人做了先期的工作:

$ cat /opt/local/share/fish/completions/find.fish
# Completions for the 'find' command

# Switches for how to handle symlinks

complete -c find -s P -d "Never follow symlinks"
complete -c find -s L -o follow -d "Follow symlinks"
complete -c find -s H -d "Don't follow symlinks (except for command line arguments)"
...

你是想要类似这样的吗?没有transient那样简洁,但是shell下通用

像git find这些shell参数提示,有点接近,还是不如transient这种方式方便顺手。

当然有这样的工具

我也这么觉得,尤其是 docker.el 这样把 transient 和 tablist 组合在一起用的,简直爽到不行 期待哪位大神复刻一个 Rust / Golang 的库出来

docker.el这个我没用过。

其实这种工具也不需要修改原有的命令行工具。只需要一个图形的包裹程序,包一下类似find git docker现有的命令行工具即可。和magit一样,magit就相当于包了一下git。

有一个通用的cli的图形,可以定制参数,界面。用的时候调用界面接收选项和参数输入,最后调用一下现有的命令行工具。

rg.el 也是这套路,用起来不错。

命令行下最类似的是这个:zsh-navigation-tools,还是不如transient方便。

命令行工具调出界面接收参数,如果不输出最终命令,让我按回车确认执行,我会感觉很不放心。

这只是个习惯问题。

另外命令行工具也不能只有界面。因为需要在自动化脚本之类的环境下执行。所以,只需要对一些复杂的命令 git之类加一个包装即可。原有的命令一点都不需要动。喜欢直接用git的可以直接用git,find,grep。图方便的,可以用ui-git,ui-find,ui-grep。

有点类似,但是它好像更接近工作流。是把一系列工具集成在一起。更偏上一层。

不仅仅是习惯的问题。

用 transient 界面肯定有他的好处,以 magit 为例,打开界面就能看到选项分类罗列整齐, 通常每个选项对应一个按键,大大减少了用户输入。甚至流程都规划好了,这点尤其对新手用户友好,避免出现选项搭配错误,根据提示一步步走就可以。有了界面还可以对输入进行约束,防止用户发呆。

而对我来说,magit 就像迷宫。每到一个界面前,都要仔细阅读。magit 整理好的选项,更像是打乱的牌。一通操作下来,付出的精神消耗并不小。

例如操作 git worktree,在命令行直接打字就可以了,加上补全也不慢。但是用 magit 我要在屏幕上一条一条慢慢找,在想到底是按 w 键还是 t 键,结果都不是,而是出乎我意料之外的 Z 键。

直接用命令行有一种没有窗户纸的畅快,虽然打字比较麻烦,还好用补全可以缓解,而且补全项是可以搜索过滤的,这比阅读整个屏幕轻松很多:

image

transient 界面上应该也可以实现 vi 风格的搜索,但是过滤就涉及到界面重排,比较麻烦。

所以我的方式只是额外加了一层包装。如果有人喜欢直接用原有命令行,或者在程序脚本中用命令。就不需要这层包装,直接用原有的命令。有git也有ui-git。在ui下面也不需要找。这个记熟了和直接记熟了命令行参数是一样的。记住了cmd -opt 和记住了ui-cmd o 这个序列是一样的。界面可以不用看直接连续按下去。

怕就怕 -opt 到了界面不是 o,取单个字母很容易冲突。

不就是helm吗?

读下来,我觉得(n)vim中也特别需要这么一套机制来拯救它的cmdline使用体验。也真巧了,一个星期前我不得不自己简单地搓了个类似python argparse的实现,加上param+flag的补全定义。

如此看来这块是(n)vim的盲区,即cmdline的popup menu不能显示针对补全项的详细信息;它只能显示补全--foo, 但不能额外显示关于foo的额外信息。

你可以试试

虽然没有很好的补全体验,但你可以提前把命令写下来,用的时候还算方便,当然可能还是存在一些命令不能正常用的情况,但满足了绝大部分使用场景

(容我跑个题) 谢谢推荐!不过补全这块是我最需要的功能,所以这个插件并不很适合我。

这是我目前的定义,还挺啰嗦的。
  do --:Rg
    local function root_default()
      local project = require("infra.project")
      return project.git_root() or project.working_root()
    end
    local sort_comp = cmds.FlagComp.constant("sort", { "none", "path", "modified", "accessed", "created" })
    -- see: rg --type-list
    local type_comp = cmds.FlagComp.constant("type", { "c", "go", "h", "lua", "py", "sh", "systemd", "vim", "zig" })
    local function is_extra_flag(flag) return flag ~= "root" and flag ~= "pattern" end

    local spell = cmds.Spell("Rg", function(args)
      local extra = {}
      local iter = fn.filtern(is_extra_flag, fn.items(args))
      for key, val in iter do
        if val == true then table.insert(extra, string.format("--%s", key)) end
        table.insert(extra, string.format("--%s=%s", key, val))
      end

      require("grep").rg(args.root, args.pattern, extra)
    end)

    -- stylua: ignore
    do
      spell:add_flag("root",          "string", false, root_default, common_root_comp)
      spell:add_flag("fixed-strings", "true",   false)
      spell:add_flag("hidden",        "true",   false)
      spell:add_flag("max-depth",     "number", false)
      spell:add_flag("multiline",     "true",   false)
      spell:add_flag("no-ignore",     "true",   false)
      spell:add_flag("sort",          "string", false, nil,          sort_comp)
      spell:add_flag("sortr",         "string", false, nil,          sort_comp)
      spell:add_flag("type",          "string", false, nil,          type_comp)
    end

    spell:add_arg("pattern", "string", true)
    cmds.cast(spell)
  end

其实这个插件和下面这个插件合起来用,就非常类似于emacs中marginalia、savehist、vertico、orderless这种包的配合了。当然也仅供参考哈。我认为无论对于命令做成whichkey也好或者还是命令搜索补全类型也好,最终能用到就好。

transient 还有的一大优点就是可以拓展补全,对于 cmdline tool 的某些 option 可以 用elisp completing-read 来提前编写 presets。写 elisp 比写 bash 爽多了。我练手写了一个简单的生成和更新 git repo changelog 的封装工具, 预置了 config 和 template 可供选择,很方便的。

2 个赞