【分享】让 eshell 等应用在 modeline 上显示当前目录的版本控制信息

现在已经有一个 eshell-prompt-extras 可以用于 eshell 显示当前路径的版本控制软件(例如 Git、SVN、Hg 等)的信息

但是我希望能够将信息显示在 modeline 里,于是写了一个东西:

;;; Cache VCS status
(defvar-local +modeline-eshell-vcs nil)

(defsubst +modeline-vcs-get-direcroty-info (dir)
  (when-let* ((backend (vc-responsible-backend dir 'noerror))
              (rev (vc-working-revision dir backend))
              (disp-rev (condition-case nil
                            (or (vc-call-backend backend '-symbolic-ref default-directory)
                                (and rev (substring rev 0 7)))
                          (error nil)))
         (def-ml (vc-default-mode-line-string backend dir)))
    (concat " "
            (substring def-ml 0
                       (if (eq backend 'Hg) 3 4))
            disp-rev)))

(defun +modeline-update-eshell-vcs-info (&rest _)
  "Update icon of vcs state in mode-line."
  (setq +modeline-eshell-vcs
        (when (eq major-mode 'eshell-mode)
          (+modeline-vcs-get-direcroty-info default-directory))))

(add-hook 'eshell-post-command-hook #'+modeline-update-eshell-vcs-info)
(add-hook 'eshell-mode-hook #'+modeline-update-eshell-vcs-info)

感觉这段代码应该不止适用于 eshell,其他的所有模式都可用,只要把 add-hook 替换成对应的 hooks 就能给 dired 之类的 major mode 使用了

效果:

好像展示 repo 的修改状态还有点问题,这个等我有时间再修(


加了个强制刷新

(defsubst +modeline-vcs-get-direcroty-info (dir)
  (when-let ((backend (vc-responsible-backend dir 'noerror)))
    (vc-state-refresh default-directory backend)
    (when-let ((rev (vc-working-revision dir backend))
               (disp-rev (condition-case nil
                             (or (vc-call-backend backend '-symbolic-ref default-directory)
                                 (and rev (substring rev 0 7)))
                           (error nil)))
               (def-ml (vc-default-mode-line-string backend dir)))
      (concat " "
              (substring def-ml 0
                         (if (eq backend 'Hg) 3 4))
              disp-rev))))

不过仓库顶层的状态还是有问题,可能需要细看 vc-mode 的代码了

又修了一下,应该已经解决了


;;; Cache VCS status
(defvar-local +modeline-eshell-vcs nil)

(defun +vc-git-state-dir (dir)
  "Git-specific version of `vc-state' (for directories)."
  (let* ((args
          `("status" "--porcelain" "-z"
            ;; Just to be explicit, it's the default anyway.
            "--untracked-files"
            "--"))
        (status (apply #'vc-git--run-command-string dir args)))
    (if (null status)
        ;; If status is nil, there was an error calling git, likely because
        ;; the file is not in a git repo.
        'unregistered
      ;; If this code is adapted to parse 'git status' for a directory,
      ;; note that a renamed file takes up two null values and needs to be
      ;; treated slightly more carefully.
      (vc-git--git-status-to-vc-state
       (mapcar (lambda (s)
                 (substring s 0 2))
               (split-string status "\0" t))))))

(defsubst +modeline-vcs-get-direcroty-info (dir)
  (when-let ((backend (vc-responsible-backend dir 'noerror)))
    (vc-file-clearprops dir)
    (vc-state-refresh default-directory 'Git)
    (when-let ((rev (vc-working-revision dir backend))
               (disp-rev (condition-case nil
                             (or (vc-call-backend backend '-symbolic-ref default-directory)
                                 (and rev (substring rev 0 7)))
                           (error nil)))
               (def-ml (cl-letf (((symbol-function #'vc-git-state) #'+vc-git-state-dir))
                         (vc-default-mode-line-string backend dir))))
      (concat " "
              (substring def-ml 0
                         (if (eq backend 'Hg) 3 4))
              disp-rev))))

(defun +modeline-update-eshell-vcs-info (&rest _)
  "Update icon of vcs state in mode-line."
  (setq +modeline-eshell-vcs
        (when (eq major-mode 'eshell-mode)
          (+modeline-vcs-get-direcroty-info default-directory))))
(add-hook 'eshell-post-command-hook #'+modeline-update-eshell-vcs-info)
(add-hook 'eshell-mode-hook #'+modeline-update-eshell-vcs-info)
2 个赞