2018-06-21 21:14:00 +02:00
|
|
|
;;; ui/vc-gutter/config.el -*- lexical-binding: t; -*-
|
|
|
|
|
2022-08-08 17:46:31 +02:00
|
|
|
;; TODO Implement me
|
2018-06-21 21:14:00 +02:00
|
|
|
(defvar +vc-gutter-in-margin nil
|
|
|
|
"If non-nil, use the margin for diffs instead of the fringe.")
|
|
|
|
|
|
|
|
(defvar +vc-gutter-in-remote-files nil
|
|
|
|
"If non-nil, enable the vc gutter in remote files (e.g. open through TRAMP).")
|
|
|
|
|
|
|
|
|
2022-08-08 17:36:23 +02:00
|
|
|
;;
|
|
|
|
;;; Default styles
|
|
|
|
|
2022-08-12 20:29:19 +02:00
|
|
|
(when (modulep! +pretty)
|
2022-08-08 17:36:23 +02:00
|
|
|
;; UI: make the fringe small enough that the diff bars aren't too domineering,
|
|
|
|
;; while leaving enough room for other indicators.
|
|
|
|
(if (fboundp 'fringe-mode) (fringe-mode '8))
|
|
|
|
;; UI: the gutter looks less cramped with some space between it and buffer.
|
|
|
|
(setq-default fringes-outside-margins t)
|
|
|
|
|
|
|
|
;; STYLE: Redefine fringe bitmaps to take up only half the horizontal space in
|
|
|
|
;; the fringe. This way we avoid overbearingly large diff bars without having
|
|
|
|
;; to shrink the fringe and sacrifice precious space for other fringe
|
|
|
|
;; indicators (like flycheck or flyspell).
|
|
|
|
;; TODO Extract these into a package with faces that themes can target.
|
2022-08-12 20:29:19 +02:00
|
|
|
(if (not (modulep! +diff-hl))
|
2022-08-08 17:46:31 +02:00
|
|
|
(after! git-gutter-fringe
|
|
|
|
(define-fringe-bitmap 'git-gutter-fr:added [224]
|
|
|
|
nil nil '(center repeated))
|
|
|
|
(define-fringe-bitmap 'git-gutter-fr:modified [224]
|
|
|
|
nil nil '(center repeated))
|
|
|
|
(define-fringe-bitmap 'git-gutter-fr:deleted [128 192 224 240]
|
|
|
|
nil nil 'bottom))
|
|
|
|
(defadvice! +vc-gutter-define-thin-bitmaps-a (&rest args)
|
|
|
|
:override #'diff-hl-define-bitmaps
|
|
|
|
(define-fringe-bitmap 'diff-hl-bmp-middle [224] nil nil '(center repeated))
|
|
|
|
(define-fringe-bitmap 'diff-hl-bmp-delete [240 224 192 128] nil nil 'top))
|
|
|
|
(defun +vc-gutter-type-face-fn (type _pos)
|
|
|
|
(intern (format "diff-hl-%s" type)))
|
|
|
|
(defun +vc-gutter-type-at-pos-fn (type _pos)
|
|
|
|
(if (eq type 'delete)
|
|
|
|
'diff-hl-bmp-delete
|
|
|
|
'diff-hl-bmp-middle))
|
2022-08-09 14:58:12 +02:00
|
|
|
(advice-add #'diff-hl-fringe-bmp-from-pos :override #'+vc-gutter-type-at-pos-fn)
|
|
|
|
(advice-add #'diff-hl-fringe-bmp-from-type :override #'+vc-gutter-type-at-pos-fn)
|
|
|
|
(setq diff-hl-draw-borders nil)
|
|
|
|
(add-hook! 'diff-hl-mode-hook
|
|
|
|
(defun +vc-gutter-fix-diff-hl-faces-h ()
|
|
|
|
(mapc (doom-rpartial #'set-face-background nil)
|
|
|
|
'(diff-hl-insert
|
|
|
|
diff-hl-delete
|
|
|
|
diff-hl-change)))))
|
2022-08-08 17:36:23 +02:00
|
|
|
|
|
|
|
;; FIX: To minimize overlap between flycheck indicators and git-gutter/diff-hl
|
|
|
|
;; indicators in the left fringe.
|
|
|
|
(after! flycheck
|
|
|
|
;; let diff have left fringe, flycheck can have right fringe
|
|
|
|
(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)))
|
|
|
|
|
|
|
|
|
2018-06-21 21:14:00 +02:00
|
|
|
;;
|
2022-08-08 17:46:31 +02:00
|
|
|
;;; git-gutter
|
2018-06-21 21:14:00 +02:00
|
|
|
|
2019-07-23 12:44:03 +02:00
|
|
|
(use-package! git-gutter
|
2022-08-12 20:29:19 +02:00
|
|
|
:unless (modulep! +diff-hl)
|
2019-07-22 19:11:12 +02:00
|
|
|
:commands git-gutter:revert-hunk git-gutter:stage-hunk
|
2018-06-21 21:14:00 +02:00
|
|
|
:init
|
2019-07-26 19:57:13 +02:00
|
|
|
(add-hook! 'find-file-hook
|
2019-07-18 15:27:20 +02:00
|
|
|
(defun +vc-gutter-init-maybe-h ()
|
|
|
|
"Enable `git-gutter-mode' in the current buffer.
|
2019-05-06 02:14:14 -04:00
|
|
|
If the buffer doesn't represent an existing file, `git-gutter-mode's activation
|
2019-06-14 11:08:59 +02:00
|
|
|
is deferred until the file is saved. Respects `git-gutter:disabled-modes'."
|
2020-02-25 12:49:52 -05:00
|
|
|
(let ((file-name (buffer-file-name (buffer-base-buffer))))
|
2021-02-26 21:25:16 -05:00
|
|
|
(cond
|
|
|
|
((and (file-remote-p (or file-name default-directory))
|
|
|
|
(not +vc-gutter-in-remote-files)))
|
2022-08-10 14:06:52 +02:00
|
|
|
;; UX: If not a valid file, wait until it is written/saved to activate
|
2021-02-26 21:25:16 -05:00
|
|
|
;; git-gutter.
|
|
|
|
((not (and file-name (vc-backend file-name)))
|
|
|
|
(add-hook 'after-save-hook #'+vc-gutter-init-maybe-h nil 'local))
|
2022-08-10 14:06:52 +02:00
|
|
|
;; UX: Allow git-gutter or git-gutter-fringe to activate based on the
|
|
|
|
;; type of frame we're in. This allows git-gutter to work for silly
|
|
|
|
;; geese who open both tty and gui frames from the daemon.
|
2021-02-26 21:25:16 -05:00
|
|
|
((if (and (display-graphic-p)
|
|
|
|
(require 'git-gutter-fringe nil t))
|
|
|
|
(setq-local git-gutter:init-function #'git-gutter-fr:init
|
|
|
|
git-gutter:view-diff-function #'git-gutter-fr:view-diff-infos
|
|
|
|
git-gutter:clear-function #'git-gutter-fr:clear
|
|
|
|
git-gutter:window-width -1)
|
|
|
|
(setq-local git-gutter:init-function 'nil
|
|
|
|
git-gutter:view-diff-function #'git-gutter:view-diff-infos
|
|
|
|
git-gutter:clear-function #'git-gutter:clear-diff-infos
|
|
|
|
git-gutter:window-width 1))
|
2021-03-18 01:05:39 -04:00
|
|
|
(unless (memq major-mode git-gutter:disabled-modes)
|
|
|
|
(git-gutter-mode +1)
|
|
|
|
(remove-hook 'after-save-hook #'+vc-gutter-init-maybe-h 'local)))))))
|
2019-07-18 15:27:20 +02:00
|
|
|
|
2022-08-10 14:06:52 +02:00
|
|
|
;; UX: Disable in Org mode, as per syl20bnr/spacemacs#10555 and
|
2020-04-16 21:55:59 -04:00
|
|
|
;; syohex/emacs-git-gutter#24. Apparently, the mode-enabling function for
|
|
|
|
;; global minor modes gets called for new buffers while they are still in
|
|
|
|
;; `fundamental-mode', before a major mode has been assigned. I don't know why
|
|
|
|
;; this is the case, but adding `fundamental-mode' here fixes the issue.
|
2019-07-23 20:43:35 +02:00
|
|
|
(setq git-gutter:disabled-modes '(fundamental-mode image-mode pdf-view-mode))
|
2018-06-21 21:14:00 +02:00
|
|
|
:config
|
2020-04-16 21:55:59 -04:00
|
|
|
(set-popup-rule! "^\\*git-gutter" :select nil :size '+popup-shrink-to-fit)
|
|
|
|
|
2022-08-10 14:06:52 +02:00
|
|
|
;; PERF: Only enable the backends that are available, so it doesn't have to
|
|
|
|
;; check when opening each buffer.
|
2020-04-16 21:55:59 -04:00
|
|
|
(setq git-gutter:handled-backends
|
|
|
|
(cons 'git (cl-remove-if-not #'executable-find (list 'hg 'svn 'bzr)
|
|
|
|
:key #'symbol-name)))
|
2018-06-21 21:14:00 +02:00
|
|
|
|
2022-08-10 14:06:52 +02:00
|
|
|
;; UX: update git-gutter on focus (in case I was using git externally)
|
2018-06-21 21:14:00 +02:00
|
|
|
(add-hook 'focus-in-hook #'git-gutter:update-all-windows)
|
|
|
|
|
2019-07-26 19:57:13 +02:00
|
|
|
(add-hook! '(doom-escape-hook doom-switch-window-hook) :append
|
2019-07-18 15:27:20 +02:00
|
|
|
(defun +vc-gutter-update-h (&rest _)
|
|
|
|
"Refresh git-gutter on ESC. Return nil to prevent shadowing other
|
2018-06-21 21:14:00 +02:00
|
|
|
`doom-escape-hook' hooks."
|
2021-03-05 20:07:26 -05:00
|
|
|
(ignore (or (memq this-command '(git-gutter:stage-hunk
|
2021-02-26 21:25:16 -05:00
|
|
|
git-gutter:revert-hunk))
|
2021-03-05 20:07:26 -05:00
|
|
|
inhibit-redisplay
|
|
|
|
(if git-gutter-mode
|
|
|
|
(git-gutter)
|
|
|
|
(+vc-gutter-init-maybe-h))))))
|
2022-08-10 14:06:52 +02:00
|
|
|
;; UX: update git-gutter when using magit commands
|
2019-07-18 15:27:20 +02:00
|
|
|
(advice-add #'magit-stage-file :after #'+vc-gutter-update-h)
|
2019-09-27 15:15:30 -04:00
|
|
|
(advice-add #'magit-unstage-file :after #'+vc-gutter-update-h)
|
|
|
|
|
|
|
|
(defadvice! +vc-gutter--fix-linearity-of-hunks-a (diffinfos is-reverse)
|
2022-08-10 14:06:52 +02:00
|
|
|
"FIX: `git-gutter:next-hunk' and `git-gutter:previous-hunk' sometimes
|
2019-09-27 15:15:30 -04:00
|
|
|
jumping to random hunks."
|
|
|
|
:override #'git-gutter:search-near-diff-index
|
2021-03-05 20:07:26 -05:00
|
|
|
(cl-position-if (let ((lineno (line-number-at-pos))
|
|
|
|
(fn (if is-reverse #'> #'<)))
|
|
|
|
(lambda (line) (funcall fn lineno line)))
|
2019-09-27 15:15:30 -04:00
|
|
|
diffinfos
|
|
|
|
:key #'git-gutter-hunk-start-line
|
|
|
|
:from-end is-reverse)))
|
2018-06-21 21:14:00 +02:00
|
|
|
|
2018-10-03 19:01:06 -04:00
|
|
|
|
2022-08-08 17:46:31 +02:00
|
|
|
;;
|
|
|
|
;;; diff-hl
|
|
|
|
|
|
|
|
(use-package! diff-hl
|
2022-08-12 20:29:19 +02:00
|
|
|
:when (modulep! +diff-hl)
|
2022-08-09 14:57:00 +02:00
|
|
|
:hook (find-file . diff-hl-mode)
|
|
|
|
:hook (vc-dir-mode . diff-hl-dir-mode)
|
2022-08-09 14:59:17 +02:00
|
|
|
:hook (dired-mode . diff-hl-dired-mode)
|
2022-08-08 17:46:31 +02:00
|
|
|
:hook (diff-hl-mode . diff-hl-flydiff-mode)
|
|
|
|
:config
|
|
|
|
(set-popup-rule! "^\\*diff-hl" :select nil :size '+popup-shrink-to-fit)
|
|
|
|
|
|
|
|
;; PERF: reduce load on remote
|
|
|
|
(defvaralias 'diff-hl-disable-on-remote '+vc-gutter-in-remote-files)
|
|
|
|
;; 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
|
|
|
|
|
|
|
|
;; 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))
|
|
|
|
;; UX: Refresh git-gutter on ESC or refocusing the Emacs frame.
|
|
|
|
(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
|
|
|
|
;; reverting 2 lines or 20. This fix resizes the popup to match its contents.
|
|
|
|
(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 ()
|
|
|
|
(if (not diff-hl-flydiff-mode)
|
|
|
|
(remove-hook 'evil-insert-state-exit-hook #'diff-hl-flydiff-update)
|
|
|
|
(add-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,
|
|
|
|
;; often far from the target hunk.
|
|
|
|
(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)
|
|
|
|
(goto-char pt)))))
|