在 Eshell 中快速跳转到常用目录
我在之前的文章中提到一种把目录加为书签以实现快速跳转的方法,这个功能十分好用,然而由于我用 Eshell 比较多,所以就像把这个功能移植到 Eshell 中,如果只是把之前的 shell 脚本转换成 elisp 来做,其实蛮简单的,也已经有人这么做了:Bookmarking directories in Eshell
但实际上 Emacs 本身就已经有了一套书签系统了(bookmark.el),把 Eshell 与这套书签系统对接起来才是像是 Emacs 的风格吧!
marks 函数
该函数用于列出所有的书签及其指向的目录:
(require 'bookmark) (defun eshell/marks () (bookmark-maybe-load-default-file) (let* ((bookmarks (bookmark-maybe-sort-alist)) (directory-bookmarks (remove-if-not (lambda (bookmark) (file-directory-p (bookmark-get-filename bookmark))) bookmarks)) (show-bookmark-fn (lambda (bookmark) (let ((name (bookmark-name-from-full-record bookmark)) (filename (bookmark-get-filename bookmark))) (format "%s -> %s" name filename))))) (mapconcat show-bookmark-fn directory-bookmarks "\n")))
首先是加载 bookmark 库,然而第二句的 (bookmark-maybe-load-default-file)
是让 bookmark 加载之前已经保存的书签信息,在然后我们通过函数 (bookmark-maybe-sort-alist)
来获取书签列表,并只保留那些指向是目录的书签。
最后我们以 "书签名 -> 指向目录" 的格式输出书签信息就行了。
这里有几点需要注意一下:
- 在eshell中执行的内部命令,其实是调用名为
eshell/命令
的函数,比如在eshell下执行marks
,那么就会调用这里定义的eshell/marks
函数了 (bookmark-maybe-load-default-file)
这一句是必不可少的,若没有这句话,那么在执行任何bookmark命令之前,书签列表都是为空的.- eshell会将函数返回的最后结果作为是命令的输出(不是用message来输出的),所以最后我们使用了
mapconcat
来将字符串列表拼接成一个完整的字符串.
jump 函数
jump 函数接一个书签名作为参数,跳转到其指向的目录中:
(defun eshell/jump (bookmark-name) (bookmark-maybe-load-default-file) (let ((filename (bookmark-get-filename bookmark-name))) (if (file-directory-p filename) (eshell/cd filename) (error "%s is not a directory" bookmark-name))))
第一步依然是用 (bookmark-maybe-load-default-file)
加载书签信息.
然后使用 bookmark-get-filename
函数可以根据传入的书签名称找到所指向的路径.
最后判断一下指向的路径是否是目录,如果是目录就调用 eshell/cd
进入所指向的目录,否则就提示错误.
mark 函数
mark 函数接受一个书签名,并将当前目录加为书签:
(defun eshell/mark (&optional bookmark-name no-overwrite) (let ((buffer-file-name default-directory)) (bookmark-set bookmark-name no-overwrite)))
mark 函数这里其实做了点讨巧的事情,bookmark库本身提供了一个 bookmark-set
函数用于添加书签(问我怎么找到的?用 C-h k C-x r m 查出来的),但是很可惜,若你在 eshell 中执行 bookmark-set 这个命令会发现出错了,错误提示为 Buffer not visiting a file or directory.
因为 bookmark 是用 buffer-file-name
作为书签的指向内容的,而 eshell buffer 中的 buffer-file-name
为 nil
.
不过这个问题也很好解决,用 let
临时指定 buffer-file-name
为 defaullt-directory
的指就好了(不得不说,动态作用域还是有好处的).
unmark 函数
unmark 函数接受一个书签名做参数,然后删掉指定的书签:
(defalias 'eshell/unmark 'bookmark-delete)
这就很简单了,直接使用 bookmark 库中的 bookmark-delete
函数就行了. 所以我这里只是用 defalias
为 bookmark-delete
定义个别名就OK了.
补全
最后是为 jump 函数添加补全功能,eshell 使用 pcomplete 这个库来进行补全,至于怎么来写补全函数以后找时间再写另一片文章。
(defun pcmpl-bookmark-names (&optional name) "Return a list of directory bookmark names" (bookmark-maybe-load-default-file) (let* ((name (or name "")) (bookmarks (bookmark-maybe-sort-alist)) (directory-bookmarks (cl-remove-if-not (lambda (bookmark) (file-directory-p (bookmark-get-filename bookmark))) bookmarks)) (bookmark-names (mapcar #'bookmark-name-from-full-record directory-bookmarks))) (cl-remove-if-not (lambda (bookmark-name) (string-prefix-p name bookmark-name)) bookmark-names))) (defun pcomplete/jump () "completion for `jump'" (while (pcomplete-here (pcmpl-bookmark-names (pcomplete-arg 'last)))))
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论