feature/version-control => ui/vc-gutter, emacs/vc

Reorganize vcs functionality. Moves the custom fringe bitmaps into :ui
vc-gutter.
This commit is contained in:
Henrik Lissner 2018-06-21 21:14:00 +02:00
parent e05d7cfee0
commit 60779c9aed
No known key found for this signature in database
GPG key ID: 5F6C0EA160557395
11 changed files with 237 additions and 152 deletions

View file

@ -13,7 +13,6 @@
spellcheck ; tasing you for misspelling mispelling
(syntax-checker ; tasing you for every semicolon you forget
+childframe) ; use childframes for error popups (Emacs 26+ only)
version-control ; remember, remember that commit in November
workspaces ; tab emulation, persistence & separate workspaces
:completion
@ -42,6 +41,7 @@
;pretty-code ; replace bits of code with pretty symbols
;tabbar ; FIXME an (incomplete) tab bar for Emacs
;unicode ; extended unicode support for various languages
vc-gutter ; vcs diff in the fringe
vi-tilde-fringe ; fringe tildes to mark beyond EOB
window-select ; visually switch windows
@ -55,6 +55,7 @@
;eshell ; a consistent, cross-platform shell (WIP)
imenu ; an imenu sidebar and searchable code index
;term ; terminals in Emacs
vc ; version-control and Emacs, sitting in a tree
:tools
editorconfig ; let someone else argue about tabs vs spaces

View file

@ -597,10 +597,10 @@
:desc "Magit file delete" :n "x" #'magit-file-delete
:desc "List gists" :n "G" #'+gist:list
:desc "Initialize repo" :n "i" #'magit-init
:desc "Browse issues tracker" :n "I" #'+vcs/git-browse-issues
:desc "Browse issues tracker" :n "I" #'+vc/git-browse-issues
:desc "Magit buffer log" :n "l" #'magit-log-buffer-file
:desc "List repositories" :n "L" #'magit-list-repositories
:desc "Browse remote" :n "o" #'+vcs/git-browse
:desc "Browse remote" :n "o" #'+vc/git-browse
:desc "Magit push popup" :n "p" #'magit-push-popup
:desc "Magit pull popup" :n "P" #'magit-pull-popup
:desc "Git revert hunk" :n "r" #'git-gutter:revert-hunk

View file

@ -70,8 +70,8 @@ command from the current directory instead of the project root."
;; GIT
(ex! "gist" #'+gist:send) ; send current buffer/region to gist
(ex! "gistl" #'+gist:list) ; list gists by user
(ex! "gbrowse" #'+vcs/git-browse) ; show file in github/gitlab
(ex! "gissues" #'+vcs/git-browse-issues) ; show github issues
(ex! "gbrowse" #'+vc/git-browse) ; show file in github/gitlab
(ex! "gissues" #'+vc/git-browse-issues) ; show github issues
(ex! "git" #'magit-status) ; open magit status window
(ex! "gstage" #'magit-stage)
(ex! "gunstage" #'magit-unstage)

View file

@ -1,7 +1,7 @@
;;; feature/version-control/autoload.el -*- lexical-binding: t; -*-
;;; emacs/vc/autoload.el -*- lexical-binding: t; -*-
;;;###autoload
(defun +vcs-root ()
(defun +vc-git-root-url ()
"Return the root git repo URL for the current file."
(require 'git-link)
(let* ((remote (git-link--select-remote))
@ -13,7 +13,7 @@
(defvar git-link-open-in-browser)
;;;###autoload
(defun +vcs/git-browse ()
(defun +vc/git-browse ()
"Open the website for the current version controlled file. Fallback to
repository root."
(interactive)
@ -24,19 +24,19 @@ repository root."
(git-link (git-link--select-remote) beg end))))
;;;###autoload
(defun +vcs/git-browse-issues ()
(defun +vc/git-browse-issues ()
"Open the issues page for current repo."
(interactive)
(browse-url (format "%s/issues" (+vcs-root))))
(browse-url (format "%s/issues" (+vc-git-root-url))))
;;;###autoload
(defun +vcs/git-browse-pulls ()
(defun +vc/git-browse-pulls ()
"Open the pull requests page for current repo."
(interactive)
(browse-url (format "%s/pulls" (+vcs-root))))
(browse-url (format "%s/pulls" (+vc-git-root-url))))
;;;###autoload
(defun +vcs*update-header-line (revision)
(defun +vc*update-header-line (revision)
"Show revision details in the header-line, instead of the minibuffer.
Sometimes I forget `git-timemachine' is enabled in a buffer. Putting revision
@ -50,14 +50,3 @@ info in the `header-line-format' is a good indication."
(propertize author 'face 'git-timemachine-minibuffer-author-face)
(propertize sha-or-subject 'face 'git-timemachine-minibuffer-detail-face)
date-full date-relative))))
;;;###autoload
(defun +vcs|enable-smerge-mode-maybe ()
"Auto-enable `smerge-mode' when merge conflict is detected."
(save-excursion
(goto-char (point-min))
(when (re-search-forward "^<<<<<<< " nil :noerror)
(smerge-mode 1)
(when (and (featurep 'hydra)
+vcs-auto-hydra-smerge)
(+hydra-smerge/body)))))

View file

@ -1,26 +1,63 @@
;;; feature/version-control/config.el -*- lexical-binding: t; -*-
;;; emacs/vc/config.el -*- lexical-binding: t; -*-
(load! "+git")
;; TODO (load! "+hg")
;;
(setq vc-make-backup-files nil)
(defvar +vcs-auto-hydra-smerge t
(defvar +vc-auto-hydra-smerge t
"When entering `smerge-mode' automatically open associated hydra.")
;;
;; Plugins
;;
;; `git-timemachine'
(after! git-timemachine
;; Sometimes I forget `git-timemachine' is enabled in a buffer, so instead of
;; showing revision details in the minibuffer, show them in
;; `header-line-format', which has better visibility.
(setq git-timemachine-show-minibuffer-details t)
(advice-add #'git-timemachine--show-minibuffer-details :override #'+vc*update-header-line)
(after! evil
;; Force evil to rehash keybindings for the current state
(add-hook 'git-timemachine-mode-hook #'evil-normalize-keymaps)))
;; `git-commit-mode'
;; see https://chris.beams.io/posts/git-commit/
(setq git-commit-fill-column 72
git-commit-summary-max-length 50
git-commit-style-convention-checks '(overlong-summary-line non-empty-second-line))
(when (featurep! :feature evil)
(add-hook 'git-commit-mode-hook #'evil-insert-state))
;;
;; `vc'
;;
;; `vc-hooks'
(setq vc-make-backup-files nil)
;; `vc-annotate'
(after! vc-annotate
(set-popup-rules!
'(("^\\vc-d" :select) ; *vc-diff*
'(("^\\vc-d" :select nil) ; *vc-diff*
("^\\vc-c" :select t))) ; *vc-change-log*
(set-evil-initial-state!
'(vc-annotate-mode vc-git-log-view-mode)
'normal))
(def-package! smerge-mode
:hook (find-file . +vcs|enable-smerge-mode-maybe)
:config
;; `smerge-mode'
(defun +vcs|enable-smerge-mode-maybe ()
"Auto-enable `smerge-mode' when merge conflict is detected."
(save-excursion
(goto-char (point-min))
(when (re-search-forward "^<<<<<<< " nil :noerror)
(smerge-mode 1)
(when (and (featurep 'hydra) +vc-auto-hydra-smerge)
(+hydra-smerge/body)))))
(add-hook 'find-file-hook #'+vcs|enable-smerge-mode-maybe)
(after! smerge-mode ; built-in
(unless EMACS26+
(with-no-warnings
(defalias #'smerge-keep-upper #'smerge-keep-mine)
@ -65,3 +102,4 @@
("r" smerge-resolve)
("R" smerge-kill-current)
("q" nil :color blue)))

View file

@ -0,0 +1,7 @@
;; -*- no-byte-compile: t; -*-
;;; emacs/vc/packages.el
(package! git-link)
(package! git-timemachine)
(package! gitconfig-mode)
(package! gitignore-mode)

View file

@ -1,78 +0,0 @@
;;; feature/version-control/+git.el -*- lexical-binding: t; -*-
;; see https://chris.beams.io/posts/git-commit/
(setq git-commit-fill-column 72
git-commit-summary-max-length 50
git-commit-style-convention-checks '(overlong-summary-line non-empty-second-line))
(when (featurep! :feature evil)
(add-hook 'git-commit-mode-hook #'evil-insert-state))
(def-package! git-gutter-fringe
:defer t
:init
(defun +version-control|git-gutter-maybe ()
"Enable `git-gutter-mode' in non-remote buffers."
(when (and (buffer-file-name)
(not (file-remote-p (buffer-file-name))))
(require 'git-gutter-fringe)
(git-gutter-mode +1)))
(add-hook! (text-mode prog-mode conf-mode) #'+version-control|git-gutter-maybe)
:config
(set-popup-rule! "^\\*git-gutter" :select nil)
;; Update git-gutter on focus (in case I was using git externally)
(add-hook 'focus-in-hook #'git-gutter:update-all-windows)
(defun +version-control|update-git-gutter (&rest _)
"Refresh git-gutter on ESC. Return nil to prevent shadowing other
`doom-escape-hook' hooks."
(when git-gutter-mode
(ignore (git-gutter))))
(add-hook 'doom-escape-hook #'+version-control|update-git-gutter t)
;; update git-gutter when using these commands
(advice-add #'magit-stage :after #'+version-control|update-git-gutter)
(advice-add #'magit-unstage :after #'+version-control|update-git-gutter)
(advice-add #'magit-stage-file :after #'+version-control|update-git-gutter)
(advice-add #'magit-unstage-file :after #'+version-control|update-git-gutter)
(defhydra +version-control@git-gutter
(:body-pre (git-gutter-mode 1) :hint nil)
"
Movement Hunk Actions Misc. gg: +%-4s(car (git-gutter:statistic))/ -%-3s(cdr (git-gutter:statistic))
^_g_^ [_s_] stage [_R_] set start Rev
^_k_^ [_r_] revert
^ ^ [_m_] mark
^ ^ [_p_] popup
^_j_^ [_q_] quit
^_G_^ [_Q_] Quit and disable"
("j" (progn (git-gutter:next-hunk 1) (recenter)))
("k" (progn (git-gutter:previous-hunk 1) (recenter)))
("g" (progn (goto-char (point-min)) (git-gutter:next-hunk 1)))
("G" (progn (goto-char (point-min)) (git-gutter:previous-hunk 1)))
("s" git-gutter:stage-hunk)
("r" git-gutter:revert-hunk)
("m" git-gutter:mark-hunk)
("p" git-gutter:popup-hunk)
("R" git-gutter:set-start-revision)
("q" nil :color blue)
("Q" (git-gutter-mode -1) :color blue)))
(def-package! git-timemachine
:defer t
:config
;; Sometimes I forget `git-timemachine' is enabled in a buffer, so instead of
;; showing revision details in the minibuffer, show them in
;; `header-line-format', which has better visibility.
(setq git-timemachine-show-minibuffer-details t)
(advice-add #'git-timemachine--show-minibuffer-details :override #'+vcs*update-header-line)
(after! evil
;; Force evil to rehash keybindings for the current state
(add-hook 'git-timemachine-mode-hook #'evil-force-normal-state)))

View file

@ -1,15 +0,0 @@
;; -*- no-byte-compile: t; -*-
;;; feature/version-control/packages.el
;;; config.el
;; n/a
;;; +git
(unless (featurep! -git)
(package! git-gutter-fringe)
(package! git-link)
(package! git-timemachine)
(package! gitconfig-mode)
(package! gitignore-mode))
;;; TODO +hg

View file

@ -81,26 +81,3 @@
ov 'display (propertize " [...] " 'face '+doom-folded-face))))
(setq hs-set-up-overlay #'+doom-set-up-overlay))
;; NOTE Adjust these bitmaps if you change `doom-fringe-size'
(after! flycheck
;; because git-gutter is in the left 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))
;; subtle diff indicators in the fringe
(after! git-gutter-fringe
;; places the git gutter outside the margins.
(setq-default fringes-outside-margins t)
;; thin fringe bitmaps
(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))
;; standardize default fringe width
(if (fboundp 'fringe-mode) (fringe-mode '4))

View file

@ -0,0 +1,161 @@
;;; ui/vc-gutter/config.el -*- lexical-binding: t; -*-
(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).")
(defvar +vc-gutter-diff-unsaved-buffer nil
"If non-nil, `diff-hl-flydiff-mode' will be activated. This allows on-the-fly
diffing, even for unsaved buffers.")
(defvar +vc-gutter-default-style t
"If non-nil, enable the default look of the vc gutter. This means subtle thin
bitmaps on the left, an arrow bitmap for flycheck, and flycheck indicators moved
to the right fringe.")
;;
;; Plugins
;;
(def-package! git-gutter-fringe
:defer t
:init
(defun +version-control|git-gutter-maybe ()
"Enable `git-gutter-mode' in non-remote buffers."
(when (and buffer-file-name
(vc-backend buffer-file-name)
(or +vc-gutter-in-remote-files
(not (file-remote-p buffer-file-name))))
(require 'git-gutter-fringe)
(git-gutter-mode +1)))
(add-hook! (text-mode prog-mode conf-mode after-save)
#'+version-control|git-gutter-maybe)
;; standardize default fringe width
(if (fboundp 'fringe-mode) (fringe-mode '4))
:config
(set-popup-rule! "^\\*git-gutter" :select nil)
;; Update git-gutter on focus (in case I was using git externally)
(add-hook 'focus-in-hook #'git-gutter:update-all-windows)
(defun +version-control|update-git-gutter (&rest _)
"Refresh git-gutter on ESC. Return nil to prevent shadowing other
`doom-escape-hook' hooks."
(when git-gutter-mode
(ignore (git-gutter))))
(add-hook 'doom-escape-hook #'+version-control|update-git-gutter t)
;; update git-gutter when using these commands
(add-hook 'magit-post-refresh-hook #'+version-control|update-git-gutter)
(defhydra +version-control@git-gutter
(:body-pre (git-gutter-mode 1) :hint nil)
"
Movement Hunk Actions Misc. gg: +%-4s(car (git-gutter:statistic))/ -%-3s(cdr (git-gutter:statistic))
^_g_^ [_s_] stage [_R_] set start Rev
^_k_^ [_r_] revert
^ ^ [_m_] mark
^ ^ [_p_] popup
^_j_^ [_q_] quit
^_G_^ [_Q_] Quit and disable"
("j" (progn (git-gutter:next-hunk 1) (recenter)))
("k" (progn (git-gutter:previous-hunk 1) (recenter)))
("g" (progn (goto-char (point-min)) (git-gutter:next-hunk 1)))
("G" (progn (goto-char (point-min)) (git-gutter:previous-hunk 1)))
("s" git-gutter:stage-hunk)
("r" git-gutter:revert-hunk)
("m" git-gutter:mark-hunk)
("p" git-gutter:popup-hunk)
("R" git-gutter:set-start-revision)
("q" nil :color blue)
("Q" (git-gutter-mode -1) :color blue))
;; subtle diff indicators in the fringe
(when +vc-gutter-default-style
;; places the git gutter outside the margins.
(setq-default fringes-outside-margins t)
;; thin fringe bitmaps
(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)
;; let diff have left fringe, flycheck can have right fringe
(after! flycheck
(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))))
;; (def-package! diff-hl
;; :defer t
;; :init
;; (defun +vc-gutter|init ()
;; "Start `diff-hl-mode' if in a file-visiting and tracked buffer."
;; (when (and buffer-file-name
;; (vc-state buffer-file-name)
;; (or +vc-gutter-in-remote-files
;; (not (file-remote-p buffer-file-name))))
;; (diff-hl-mode +1)))
;; (add-hook! (text-mode prog-mode conf-mode after-save)
;; #'+vc-gutter|init)
;; ;; standardize fringe size
;; (if (fboundp 'fringe-mode) (fringe-mode '4))
;; :config
;; (setq vc-git-diff-switches '("--histogram"))
;; ;; Update diffs when it makes sense too, without being too slow
;; (if (not +vc-gutter-diff-unsaved-buffer)
;; (add-hook! '(doom-escape-hook focus-in-hook) #'diff-hl-update)
;; (diff-hl-flydiff-mode +1)
;; (add-hook! '(doom-escape-hook focus-in-hook) #'diff-hl-flydiff-update)
;; (when (featurep! :feature evil)
;; (when diff-hl-flydiff-timer
;; (cancel-timer diff-hl-flydiff-timer))
;; (add-hook 'evil-insert-state-exit-hook #'diff-hl-flydiff-update)))
;; ;; Don't delete the current hunk's indicators while we're editing
;; (advice-remove #'diff-hl-overlay-modified #'ignore)
;; ;; Update diff-hl when magit refreshes
;; (add-hook 'magit-post-refresh-hook 'diff-hl-magit-post-refresh)
;; ;; update git-gutter when using these commands
;; (advice-add #'magit-stage :after #'+version-control|update-git-gutter)
;; (advice-add #'magit-unstage :after #'+version-control|update-git-gutter)
;; (advice-add #'magit-stage-file :after #'+version-control|update-git-gutter)
;; (advice-add #'magit-unstage-file :after #'+version-control|update-git-gutter)
;; ;; Draw me like one of your French editors
;; (setq-default fringes-outside-margins t)
;; (cond ((or +vc-gutter-in-margin (not (display-graphic-p)))
;; (diff-hl-margin-mode)
;; (setq diff-hl-margin-symbols-alist
;; '((insert . "❙") (delete . "^") (change . "❙")
;; (unknown . "❙") (ignored . "❙"))))
;; (t
;; ;; Because diff-hl is in the left fringe
;; (setq flycheck-indication-mode 'right-fringe)
;; (defun +vc-gutter|setup-fringe-bitmaps ()
;; "Define thin fringe bitmaps for maximum sexiness."
;; (define-fringe-bitmap 'diff-hl-bmp-top [224] nil nil '(center repeated))
;; (define-fringe-bitmap 'diff-hl-bmp-middle [224] nil nil '(center repeated))
;; (define-fringe-bitmap 'diff-hl-bmp-bottom [224] nil nil '(center repeated))
;; (define-fringe-bitmap 'diff-hl-bmp-insert [224] nil nil '(center repeated))
;; (define-fringe-bitmap 'diff-hl-bmp-single [224] nil nil '(center repeated))
;; (define-fringe-bitmap 'diff-hl-bmp-delete [240 224 192 128] nil nil 'top))
;; (defun +vc-gutter-type-at-pos (type _pos)
;; "Return the bitmap for `diff-hl' to use for change at point."
;; (pcase type
;; (`unknown 'question-mark)
;; (`delete 'diff-hl-bmp-delete)
;; (`change 'diff-hl-bmp-middle)
;; (`ignored 'diff-hl-bmp-i)
;; (x (intern (format "diff-hl-bmp-%s" x)))))
;; ;; Tweak the fringe bitmaps so we get long, elegant bars
;; (setq diff-hl-fringe-bmp-function #'+vc-gutter-type-at-pos
;; diff-hl-draw-borders nil)
;; (add-hook 'diff-hl-mode-hook #'+vc-gutter|setup-fringe-bitmaps))))

View file

@ -0,0 +1,5 @@
;; -*- no-byte-compile: t; -*-
;;; ui/vc-gutter/packages.el
(package! git-gutter-fringe)
;; (package! diff-hl)