2018-06-21 21:14:00 +02:00
|
|
|
;;; ui/vc-gutter/config.el -*- lexical-binding: t; -*-
|
|
|
|
|
2022-08-08 17:36:23 +02:00
|
|
|
;;
|
|
|
|
;;; Default styles
|
|
|
|
|
2024-07-10 03:18:46 -04:00
|
|
|
;; STYLE: Redefine fringe bitmaps to be sleeker by making them solid bars (with
|
|
|
|
;; no border) that only take up half the horizontal space in the fringe. This
|
|
|
|
;; approach lets us avoid robbing fringe space from other packages/modes that
|
|
|
|
;; may need benefit from it (like magit, flycheck, or flyspell).
|
2022-08-12 20:29:19 +02:00
|
|
|
(when (modulep! +pretty)
|
2022-08-08 17:36:23 +02:00
|
|
|
(if (fboundp 'fringe-mode) (fringe-mode '8))
|
|
|
|
(setq-default fringes-outside-margins t)
|
|
|
|
|
2024-07-10 03:18:46 -04:00
|
|
|
(defadvice! +vc-gutter-define-thin-bitmaps-a (&rest _)
|
|
|
|
:after #'diff-hl-define-bitmaps
|
|
|
|
(let* ((scale (if (and (boundp 'text-scale-mode-amount)
|
|
|
|
(numberp text-scale-mode-amount))
|
|
|
|
(expt text-scale-mode-step text-scale-mode-amount)
|
|
|
|
1))
|
|
|
|
(spacing (or (and (display-graphic-p) (default-value 'line-spacing)) 0))
|
|
|
|
(h (+ (ceiling (* (frame-char-height) scale))
|
|
|
|
(if (floatp spacing)
|
|
|
|
(truncate (* (frame-char-height) spacing))
|
|
|
|
spacing)))
|
|
|
|
(w (min (frame-parameter nil (intern (format "%s-fringe" diff-hl-side)))
|
|
|
|
16))
|
|
|
|
(_ (if (zerop w) (setq w 16))))
|
|
|
|
(define-fringe-bitmap 'diff-hl-bmp-middle
|
|
|
|
(make-vector
|
|
|
|
h (string-to-number (let ((half-w (1- (/ w 2))))
|
|
|
|
(concat (make-string half-w ?1)
|
|
|
|
(make-string (- w half-w) ?0)))
|
|
|
|
2))
|
|
|
|
nil nil 'center)))
|
2024-06-22 17:13:20 -04:00
|
|
|
(defun +vc-gutter-type-at-pos-fn (type _pos)
|
|
|
|
(if (eq type 'delete)
|
|
|
|
'diff-hl-bmp-delete
|
|
|
|
'diff-hl-bmp-middle))
|
2024-07-10 03:19:19 -04:00
|
|
|
(setq diff-hl-fringe-bmp-function #'+vc-gutter-type-at-pos-fn)
|
2024-06-22 17:13:20 -04:00
|
|
|
(setq diff-hl-draw-borders nil)
|
2024-07-10 03:19:19 -04:00
|
|
|
|
2024-06-22 17:13:20 -04:00
|
|
|
(add-hook! 'diff-hl-mode-hook
|
2024-07-10 03:19:19 -04:00
|
|
|
(defun +vc-gutter-make-diff-hl-faces-transparent-h ()
|
2024-06-22 17:13:20 -04:00
|
|
|
(mapc (doom-rpartial #'set-face-background nil)
|
|
|
|
'(diff-hl-insert
|
|
|
|
diff-hl-delete
|
|
|
|
diff-hl-change))))
|
|
|
|
|
|
|
|
;; FIX: To minimize overlap between flycheck indicators and diff-hl indicators
|
|
|
|
;; in the left fringe.
|
2022-08-08 17:36:23 +02:00
|
|
|
(after! flycheck
|
2022-09-06 23:41:28 +02:00
|
|
|
;; Let diff-hl have left fringe, flycheck can have right fringe
|
2022-08-08 17:36:23 +02:00
|
|
|
(setq flycheck-indication-mode 'right-fringe)
|
|
|
|
;; A non-descript, left-pointing arrow
|
|
|
|
(define-fringe-bitmap 'flycheck-fringe-bitmap-double-arrow
|
|
|
|
[16 48 112 240 112 48 16] nil nil 'center)))
|
|
|
|
|
|
|
|
|
2022-08-08 17:46:31 +02:00
|
|
|
;;
|
|
|
|
;;; diff-hl
|
|
|
|
|
|
|
|
(use-package! diff-hl
|
2024-07-25 19:40:33 -04:00
|
|
|
:hook (doom-first-file . global-diff-hl-mode)
|
|
|
|
:hook (vc-dir-mode . turn-on-diff-hl-mode)
|
2022-08-08 17:46:31 +02:00
|
|
|
:hook (diff-hl-mode . diff-hl-flydiff-mode)
|
2023-02-18 14:00:59 +08:00
|
|
|
:commands diff-hl-stage-current-hunk diff-hl-revert-hunk diff-hl-next-hunk diff-hl-previous-hunk
|
2024-07-25 19:41:51 -04:00
|
|
|
:init
|
|
|
|
(add-hook! 'dired-mode-hook
|
|
|
|
(defun +vc-gutter-enable-maybe-h ()
|
|
|
|
"Conditionally enable `diff-hl-dired-mode' in dired buffers.
|
|
|
|
Respects `diff-hl-disable-on-remote'."
|
2024-08-29 01:12:29 -04:00
|
|
|
;; Neither `diff-hl-dired-mode' or `diff-hl-dired-mode-unless-remote'
|
|
|
|
;; respect `diff-hl-disable-on-remote', so...
|
|
|
|
(unless (and (bound-and-true-p diff-hl-disable-on-remote)
|
2024-07-25 19:41:51 -04:00
|
|
|
(file-remote-p default-directory))
|
|
|
|
(diff-hl-dired-mode +1))))
|
|
|
|
|
2024-08-28 15:08:46 -04:00
|
|
|
;; HACK: diff-hl won't be visible in TTY frames, but there's no simple way to
|
|
|
|
;; use the fringe in GUI Emacs *and* use the margin in the terminal *AND*
|
|
|
|
;; support daemon users, so we need more than a static `display-graphic-p'
|
|
|
|
;; check at startup.
|
|
|
|
(if (not (daemonp))
|
|
|
|
(unless (display-graphic-p)
|
|
|
|
(add-hook 'global-diff-hl-mode-hook #'diff-hl-margin-mode))
|
|
|
|
(when (modulep! :os tty)
|
|
|
|
(put 'diff-hl-mode 'last t)
|
|
|
|
(add-hook! 'doom-switch-window-hook
|
|
|
|
(defun +vc-gutter-use-margins-in-tty-h ()
|
|
|
|
(when (bound-and-true-p global-diff-hl-mode)
|
|
|
|
(let ((graphic? (display-graphic-p)))
|
|
|
|
(unless (eq (get 'diff-hl-mode 'last) graphic?)
|
|
|
|
(diff-hl-margin-mode (if graphic? -1 +1))
|
|
|
|
(put 'diff-hl-mode 'last graphic?))))))))
|
|
|
|
|
2022-08-08 17:46:31 +02:00
|
|
|
:config
|
|
|
|
(set-popup-rule! "^\\*diff-hl" :select nil :size '+popup-shrink-to-fit)
|
|
|
|
|
2024-08-21 05:01:45 -04:00
|
|
|
(setq diff-hl-global-modes '(not image-mode pdf-view-mode))
|
2022-08-08 17:46:31 +02:00
|
|
|
;; PERF: A slightly faster algorithm for diffing.
|
|
|
|
(setq vc-git-diff-switches '("--histogram"))
|
|
|
|
;; PERF: Slightly more conservative delay before updating the diff
|
|
|
|
(setq diff-hl-flydiff-delay 0.5) ; default: 0.3
|
2024-07-08 22:16:05 -04:00
|
|
|
;; PERF: don't block Emacs when updating vc gutter
|
|
|
|
(setq diff-hl-update-async t)
|
2022-08-08 17:46:31 +02:00
|
|
|
;; UX: get realtime feedback in diffs after staging/unstaging hunks.
|
|
|
|
(setq diff-hl-show-staged-changes nil)
|
|
|
|
|
|
|
|
;; UX: Update diffs when it makes sense too, without being too slow
|
2022-08-12 20:29:19 +02:00
|
|
|
(when (modulep! :editor evil)
|
2022-08-08 17:46:31 +02:00
|
|
|
(map! :after diff-hl-show-hunk
|
|
|
|
:map diff-hl-show-hunk-map
|
|
|
|
:n "p" #'diff-hl-show-hunk-previous
|
|
|
|
:n "n" #'diff-hl-show-hunk-next
|
|
|
|
:n "c" #'diff-hl-show-hunk-copy-original-text
|
|
|
|
:n "r" #'diff-hl-show-hunk-revert-hunk
|
|
|
|
:n "[" #'diff-hl-show-hunk-previous
|
|
|
|
:n "]" #'diff-hl-show-hunk-next
|
|
|
|
:n "{" #'diff-hl-show-hunk-previous
|
|
|
|
:n "}" #'diff-hl-show-hunk-next
|
|
|
|
:n "S" #'diff-hl-show-hunk-stage-hunk))
|
2024-06-22 17:13:20 -04:00
|
|
|
;; UX: Refresh gutter on ESC or refocusing the Emacs frame.
|
2022-08-08 17:46:31 +02:00
|
|
|
(add-hook! '(doom-escape-hook doom-switch-window-hook) :append
|
|
|
|
(defun +vc-gutter-update-h (&rest _)
|
|
|
|
"Return nil to prevent shadowing other `doom-escape-hook' hooks."
|
|
|
|
(ignore (or inhibit-redisplay
|
|
|
|
(and (or (bound-and-true-p diff-hl-mode)
|
|
|
|
(bound-and-true-p diff-hl-dir-mode))
|
|
|
|
(diff-hl-update-once))))))
|
|
|
|
;; UX: Update diff-hl when magit alters git state.
|
2022-08-12 20:29:19 +02:00
|
|
|
(when (modulep! :tools magit)
|
2022-08-08 17:46:31 +02:00
|
|
|
(add-hook 'magit-pre-refresh-hook #'diff-hl-magit-pre-refresh)
|
2022-08-09 14:57:36 +02:00
|
|
|
(add-hook 'magit-post-refresh-hook #'diff-hl-magit-post-refresh))
|
2022-08-08 17:46:31 +02:00
|
|
|
|
2022-08-09 17:54:18 +02:00
|
|
|
;; FIX: The revert popup consumes 50% of the frame, whether or not you're
|
2022-09-06 23:41:28 +02:00
|
|
|
;; reverting 2 lines or 20. This resizes the popup to match its contents.
|
2022-08-09 17:54:18 +02:00
|
|
|
(defadvice! +vc-gutter--shrink-popup-a (fn &rest args)
|
|
|
|
:around #'diff-hl-revert-hunk-1
|
|
|
|
(letf! ((refine-mode diff-auto-refine-mode)
|
|
|
|
(diff-auto-refine-mode t)
|
|
|
|
(defun diff-refine-hunk ()
|
|
|
|
(when refine-mode
|
|
|
|
(funcall diff-refine-hunk))
|
|
|
|
(shrink-window-if-larger-than-buffer)))
|
|
|
|
(apply fn args)))
|
|
|
|
|
2022-08-08 17:46:31 +02:00
|
|
|
;; UX: Don't delete the current hunk's indicators while we're editing
|
2022-08-12 20:29:19 +02:00
|
|
|
(when (modulep! :editor evil)
|
2022-08-09 17:54:43 +02:00
|
|
|
(add-hook! 'diff-hl-flydiff-mode-hook
|
|
|
|
(defun +vc-gutter-init-flydiff-mode-h ()
|
2024-07-10 03:19:19 -04:00
|
|
|
(if diff-hl-flydiff-mode
|
|
|
|
(add-hook 'evil-insert-state-exit-hook #'diff-hl-flydiff-update)
|
|
|
|
(remove-hook 'evil-insert-state-exit-hook #'diff-hl-flydiff-update)))))
|
2022-08-08 17:46:31 +02:00
|
|
|
|
|
|
|
;; FIX: Reverting a hunk causes the cursor to be moved to an unexpected place,
|
2022-09-06 23:41:28 +02:00
|
|
|
;; often far from the target hunk.
|
2022-08-08 17:46:31 +02:00
|
|
|
(defadvice! +vc-gutter--save-excursion-a (fn &rest args)
|
|
|
|
"Suppresses unexpected cursor movement by `diff-hl-revert-hunk'."
|
|
|
|
:around #'diff-hl-revert-hunk
|
|
|
|
(let ((pt (point)))
|
|
|
|
(prog1 (apply fn args)
|
2024-08-21 04:33:06 -04:00
|
|
|
(goto-char pt))))
|
|
|
|
|
|
|
|
;; FIX: `global-diff-hl-mode' enables `diff-hl-mode' *everywhere*, which calls
|
|
|
|
;; `diff-hl-update'. If `diff-hl-update-async' is non-nil, this means a new
|
|
|
|
;; thread is spawned for *every* buffer, whether they're visible or not. Not
|
|
|
|
;; only can this slow a lot down, but `kill-buffer' will silently refuse to
|
|
|
|
;; kill buffers with a thread associated with it. Chaos ensues (see #7991
|
|
|
|
;; and #7954).
|
|
|
|
;; REVIEW: Report this upstream.
|
|
|
|
(defun +vc-gutter--kill-thread (&optional block?)
|
|
|
|
(when-let ((th +vc-gutter--diff-hl-thread))
|
|
|
|
(when (thread-live-p th)
|
|
|
|
(thread-signal th 'quit nil)
|
|
|
|
(when block?
|
|
|
|
(condition-case _
|
|
|
|
(thread-join th)
|
|
|
|
((quit error) nil))))))
|
|
|
|
|
|
|
|
(defvar-local +vc-gutter--diff-hl-thread nil)
|
|
|
|
(defadvice! +vc-gutter--debounce-threads-a (&rest _)
|
|
|
|
:override #'diff-hl-update
|
|
|
|
(unless (or inhibit-redisplay
|
|
|
|
non-essential
|
|
|
|
delay-mode-hooks
|
|
|
|
(null (buffer-file-name (buffer-base-buffer)))
|
|
|
|
(null (get-buffer-window (current-buffer))))
|
|
|
|
(if (and diff-hl-update-async
|
|
|
|
(not
|
|
|
|
(run-hook-with-args-until-success 'diff-hl-async-inhibit-functions
|
|
|
|
default-directory)))
|
|
|
|
(progn
|
|
|
|
(+vc-gutter--kill-thread)
|
|
|
|
(setq +vc-gutter--diff-hl-thread
|
|
|
|
(make-thread (lambda ()
|
|
|
|
(unwind-protect
|
|
|
|
(diff-hl--update-safe)
|
|
|
|
(setq +vc-gutter--diff-hl-thread nil)))
|
|
|
|
"diff-hl--update-safe")))
|
|
|
|
(diff-hl--update))
|
|
|
|
t))
|
|
|
|
|
|
|
|
(defadvice! +vc-gutter--only-tick-on-success-a (&rest _)
|
|
|
|
:override #'diff-hl-update-once
|
|
|
|
(unless (equal diff-hl--modified-tick (buffer-chars-modified-tick))
|
|
|
|
(when (diff-hl-update)
|
|
|
|
(setq diff-hl--modified-tick (buffer-chars-modified-tick)))))
|
|
|
|
|
|
|
|
;; HACK: This advice won't work in *all* cases (it's a C function, and any
|
|
|
|
;; calls to it from C won't trigger advice), but the thread issues above are
|
|
|
|
;; triggered from Elisp's buffer API (from what I can tell).
|
2024-08-21 12:57:28 -04:00
|
|
|
(defadvice! +vc-gutter--kill-diff-hl-thread-a (&optional buf)
|
2024-08-21 04:33:06 -04:00
|
|
|
:before #'kill-buffer
|
2024-08-22 16:04:22 -04:00
|
|
|
(when-let ((buf (ignore-errors (window-normalize-buffer buf))))
|
|
|
|
(with-current-buffer buf
|
2024-08-28 15:08:46 -04:00
|
|
|
(+vc-gutter--kill-thread t)))))
|