在 Eshell 中快速跳转到常用目录

发布于 2023-03-26 11:24:22 字数 4154 浏览 116 评论 0

我在之前的文章中提到一种把目录加为书签以实现快速跳转的方法,这个功能十分好用,然而由于我用 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-namenil.

不过这个问题也很好解决,用 let 临时指定 buffer-file-namedefaullt-directory 的指就好了(不得不说,动态作用域还是有好处的).

unmark 函数

unmark 函数接受一个书签名做参数,然后删掉指定的书签:

(defalias 'eshell/unmark 'bookmark-delete)

这就很简单了,直接使用 bookmark 库中的 bookmark-delete 函数就行了. 所以我这里只是用 defaliasbookmark-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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

‖放下

暂无简介

文章
评论
26 人气
更多

推荐作者

櫻之舞

文章 0 评论 0

弥枳

文章 0 评论 0

m2429

文章 0 评论 0

野却迷人

文章 0 评论 0

我怀念的。

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文