diff --git a/init.example.el b/init.example.el index d46e5193e..cd34691e3 100644 --- a/init.example.el +++ b/init.example.el @@ -34,6 +34,7 @@ jump ; helping you get around snippets ; my elves. They type so I don't have to file-templates ; auto-snippets for empty files + hydra ; keybindings that stick around spellcheck ; tasing you for misspelling mispelling syntax-checker ; tasing you for every semicolon you forget version-control ; remember, remember that commit in November diff --git a/modules/feature/hydra/autoload/evil.el b/modules/feature/hydra/autoload/evil.el new file mode 100644 index 000000000..92aa9cdef --- /dev/null +++ b/modules/feature/hydra/autoload/evil.el @@ -0,0 +1,2 @@ +;;; feature/hydra/autoload/evil.el -*- lexical-binding: t; -*- + diff --git a/modules/feature/hydra/autoload/hydras.el b/modules/feature/hydra/autoload/hydras.el new file mode 100644 index 000000000..002f91420 --- /dev/null +++ b/modules/feature/hydra/autoload/hydras.el @@ -0,0 +1,2 @@ +;;; feature/hydra/autoload/hydras.el -*- lexical-binding: t; -*- + diff --git a/modules/feature/hydra/config.el b/modules/feature/hydra/config.el new file mode 100644 index 000000000..51dc55260 --- /dev/null +++ b/modules/feature/hydra/config.el @@ -0,0 +1,94 @@ +;;; feature/hydra/config.el -*- lexical-binding: t; -*- + +(def-package! hydra + :commands (+hydra-zoom/body +hydra-window/body) + :config + (defhydra +hydra-zoom (:hint t :color red) + "zoom" + ("j" text-scale-increase "in") + ("k" text-scale-decrease "out") + ("0" (text-scale-set 0) "reset")) + + (defhydra +hydra-window (:hint nil) + " + Split: _v_ert _s_:horz + Delete: _c_lose _o_nly + Switch Window: _h_:left _j_:down _k_:up _l_:right + Buffers: _p_revious _n_ext _b_:select _f_ind-file + Resize: _H_:splitter left _J_:splitter down _K_:splitter up _L_:splitter right + Move: _a_:up _z_:down _i_menu" + + + ("z" scroll-up-line) + ("a" scroll-down-line) + ("i" idomenu) + + ("h" windmove-left) + ("j" windmove-down) + ("k" windmove-up) + ("l" windmove-right) + + ("p" previous-buffer) + ("n" next-buffer) + ("b" ido-switch-buffer) + ("f" ido-find-file) + + ("s" split-window-below) + ("v" split-window-right) + + ("c" delete-window) + ("o" delete-other-windows) + + ("H" hydra-move-splitter-left) + ("J" hydra-move-splitter-down) + ("K" hydra-move-splitter-up) + ("L" hydra-move-splitter-right) + + ("q" nil))) + + +(when (featurep! :completion ivy) + (def-package! ivy-hydra + :config + (define-key ivy-mode-map (kbd "C-o") + (defhydra coo-ivy (:hint nil :color pink) + " + Move ^^^^^^^^^^ | Call ^^^^ | Cancel^^ | Options^^ | Action _w_/_s_/_a_: %s(ivy-action-name) +----------^^^^^^^^^^-+--------------^^^^-+-------^^-+--------^^-+--------------------------------- + _g_ ^ ^ _k_ ^ ^ _u_ | _f_orward _o_ccur | _i_nsert | _c_alling: %-7s(if ivy-calling \"on\" \"off\") _C_ase-fold: %-10`ivy-case-fold-search + ^↨^ _h_ ^+^ _l_ ^↕^ | _RET_ done ^^ | _q_uit | _m_atcher: %-7s(ivy--matcher-desc) _t_runcate: %-11`truncate-lines + _G_ ^ ^ _j_ ^ ^ _d_ | _TAB_ alt-done ^^ | ^ ^ | _<_/_>_: shrink/grow +" + ;; arrows + ("j" ivy-next-line) + ("k" ivy-previous-line) + ("l" ivy-alt-done) + ("h" ivy-backward-delete-char) + ("g" ivy-beginning-of-buffer) + ("G" ivy-end-of-buffer) + ("d" ivy-scroll-up-command) + ("u" ivy-scroll-down-command) + ("e" ivy-scroll-down-command) + ;; actions + ("q" keyboard-escape-quit :exit t) + ("C-g" keyboard-escape-quit :exit t) + ("" keyboard-escape-quit :exit t) + ("C-o" nil) + ("i" nil) + ("TAB" ivy-alt-done :exit nil) + ("C-j" ivy-alt-done :exit nil) + ;; ("d" ivy-done :exit t) + ("RET" ivy-done :exit t) + ("C-m" ivy-done :exit t) + ("f" ivy-call) + ("c" ivy-toggle-calling) + ("m" ivy-toggle-fuzzy) + (">" ivy-minibuffer-grow) + ("<" ivy-minibuffer-shrink) + ("w" ivy-prev-action) + ("s" ivy-next-action) + ("a" ivy-read-action) + ("t" (setq truncate-lines (not truncate-lines))) + ("C" ivy-toggle-case-fold) + + ("o" ivy-occur :exit t))))) diff --git a/modules/feature/hydra/packages.el b/modules/feature/hydra/packages.el new file mode 100644 index 000000000..08cbb525d --- /dev/null +++ b/modules/feature/hydra/packages.el @@ -0,0 +1,7 @@ +;; -*- no-byte-compile: t; -*- +;;; feature/hydra/packages.el + +(package! hydra) +(when (featurep! :completion ivy) + (package! ivy-hydra)) + diff --git a/modules/feature/version-control/+git.el b/modules/feature/version-control/+git.el index 4a8cb7da2..e0e9ebc68 100644 --- a/modules/feature/version-control/+git.el +++ b/modules/feature/version-control/+git.el @@ -31,7 +31,38 @@ (when git-gutter-mode (git-gutter) nil)) - (add-hook '+evil-esc-hook #'+version-control|update-git-gutter t))) + (add-hook '+evil-esc-hook #'+version-control|update-git-gutter t)) + + + (when (featurep! :feature hydra) + (require 'hydra) + (defhydra +hydra-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 diff --git a/modules/feature/version-control/autoload.el b/modules/feature/version-control/autoload.el index c0a8e97bf..4383233f7 100644 --- a/modules/feature/version-control/autoload.el +++ b/modules/feature/version-control/autoload.el @@ -38,6 +38,15 @@ repository root." (+vcs*update-header-line) (setq-local header-line-format nil))) +;;;###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 +vcs-auto-hydra-smerge (+hydra-smerge/body))))) + ;;;###autoload (defun +vcs*update-header-line (&rest _) "Show revision details in the header-line, instead of the minibuffer. diff --git a/modules/feature/version-control/config.el b/modules/feature/version-control/config.el index 95d45108e..98db7e1b3 100644 --- a/modules/feature/version-control/config.el +++ b/modules/feature/version-control/config.el @@ -2,6 +2,9 @@ (setq vc-make-backup-files nil) +(defvar +vcs-auto-hydra-smerge t + "When entering `smerge-mode' automatically open associated hydra.") + (load! +git) ;; (load! +hg) @@ -13,3 +16,54 @@ (set! :evil-state 'vc-annotate-mode 'normal) (set! :evil-state 'vc-git-log-view-mode 'normal)) + +(def-package! smerge-mode + :init + (add-hook 'find-file-hook #'+vcs|enable-smerge-mode-maybe) + :config + (when (featurep! :feature hydra) + (require 'hydra) + + (when (version< emacs-version "26") + (defalias 'smerge-keep-upper 'smerge-keep-mine) + (defalias 'smerge-keep-lower 'smerge-keep-other) + (defalias 'smerge-diff-base-upper 'smerge-diff-base-mine) + (defalias 'smerge-diff-upper-lower 'smerge-diff-mine-other) + (defalias 'smerge-diff-base-lower 'smerge-diff-base-other)) + + (defhydra +hydra-smerge (:hint nil + :pre (smerge-mode 1) + ;; Disable `smerge-mode' when quitting hydra if + ;; no merge conflicts remain. + :post (smerge-auto-leave)) + " + ╭────────┐ + Movement Keep Diff Other │ smerge │ + ╭─────────────────────────────────────────────────┴────────╯ + ^_g_^ [_b_] base [_<_] upper/base [_C_] Combine + ^_C-k_^ [_u_] upper [_=_] upper/lower [_r_] resolve + ^_k_ ↑^ [_l_] lower [_>_] base/lower [_R_] remove + ^_j_ ↓^ [_a_] all [_H_] hightlight + ^_C-j_^ [_RET_] current [_E_] ediff ╭────────── + ^_G_^ │ [_q_] quit" + ("g" (progn (goto-char (point-min)) (smerge-next))) + ("G" (progn (goto-char (point-max)) (smerge-prev))) + ("C-j" smerge-next) + ("C-k" smerge-prev) + ("j" next-line) + ("k" previous-line) + ("b" smerge-keep-base) + ("u" smerge-keep-upper) + ("l" smerge-keep-lower) + ("a" smerge-keep-all) + ("RET" smerge-keep-current) + ("\C-m" smerge-keep-current) + ("<" smerge-diff-base-upper) + ("=" smerge-diff-upper-lower) + (">" smerge-diff-base-lower) + ("H" smerge-refine) + ("E" smerge-ediff) + ("C" smerge-combine-with-next) + ("r" smerge-resolve) + ("R" smerge-kill-current) + ("q" nil :color blue))))