1 前言

目标: 在Eshell中像在bash/zsh中使用fzf那般搜索历史命令

2 fzf

我的主力Shell 是Eshell, 但是平时我也会用Zsh, 而fzf 是一个非常好用的命令行工具,用了fzf搜索历史命令:

Figure 1: fzf

Figure 1: fzf

3 Eshell

我日常的操作基本都是在 Eshell 上面进行的,不过 Eshell 是没办法直接像 Bash 那样调用 fzf来查找命令历史的,所以我希望把这个功能迁移到到Eshell 上面来。

我在 Emacs 使用的补全框架是 Ivy/Counsel,它有一个 counsel-esh-history的命令可以使用 Ivy 来搜索命令,但是没办法使用用户已经输入的内容来过滤命令,所以我就在自己折腾了一个

counsel-esh-history 命令。效果如下:

Figure 2: 感觉很不错嘛 :)

Figure 2: 感觉很不错嘛 :)

4 源代码

得益于 Ivy强大的内置函数, 功能实现起来相当便利,完整代码如下:

(defun samray/esh-history ()
  "Interactive search eshell history."
  (interactive)
  (require 'em-hist)
  (save-excursion
    (let* ((start-pos (eshell-bol))
	   (end-pos (point-at-eol))
	   (input (buffer-substring-no-properties start-pos end-pos)))
      (let* ((command (ivy-read "Command: "
				(delete-dups
				 (when (> (ring-size eshell-history-ring) 0)
				   (ring-elements eshell-history-ring)))
				:preselect input
				:action #'ivy-completion-in-region-action))
	     (cursor-move (length command)))
	(kill-region (+ start-pos cursor-move) (+ end-pos cursor-move))
	)))
  ;; move cursor to eol
  (end-of-line)
  )

代码不是很复杂, 主要功能是获取用户输入的命令, 然后把所有的历史命令读取出来,最后使用ivy-read内置的ivy-completion-in-region-action功能, 用用户的输入的命令与历史命令进行匹配, 由用户选择最终的命令.

ivy-read是Emacs内置completing-read的函数的强化, 关于ivy-read具体用法可以参考文档ivy-read.

5 总结

最后, 我也顺便把代码分享到 Emacs社区, 而 manateelazycat也把这段代码的功能加入到aweshell, Oh yeah !