Currently in the mode-line the filename %f, is absolute.

I would like to have the path relative to the version-control root, without having to find this every redraw.

How could this be cached, allowing for changes to the buffers filename to be refreshed?


after-set-visited-file-name-hook is a reliable way to detect changes to the filename.

This clears the cached value when a new filename if set.

;; Cache the vc-root relative name of the buffer.
(defvar-local buffer-file-name--vc-root-relative nil
  "Use to cache the VC relative path.

- nil: Unset.
- t: No VC root, use regular filename.
- Otherwise this is the VC relative path.")
(defun buffer-file-name-vc-root-relative-fn (filepath)
  ;; Lazy initialize.
  (when (eq buffer-file-name--vc-root-relative nil)
    (setq buffer-file-name--vc-root-relative
            (let ((base-vc-backend (ignore-errors (vc-responsible-backend filepath))))
              (when base-vc-backend
                (vc-call-backend base-vc-backend 'root filepath)))))
        (if base-path
          ;; Account for possibility of filepath containing a '%'.
            (replace-regexp-in-string "%" "%%" (file-relative-name filepath base-path)))
          ;; No base-path, set to 't', initialized with no base (return nil).
  (unless (eq buffer-file-name--vc-root-relative t)

(add-hook 'after-set-visited-file-name-hook
  (lambda () (setq buffer-file-name--vc-root-relative nil)))

Then in the mode-line, this can be used to show the filename.

(or (buffer-file-name-vc-root-relative-fn filepath) "%f")
|improve this answer|||||

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.