;;; evil-collection-ediff.el --- Evil bindings for ediff -*- lexical-binding: t -*-
;; Copyright (C) 2015 Justin Burkett
;; Author: Justin Burkett <justin@burkett.cc>
;; Maintainer: James Nguyen <james@jojojames.com>
;; Pierre Neidhardt <ambrevar@gmail.com>
;; URL: https://github.com/emacs-evil/evil-collection
;; Homepage: https://github.com/emacs-evil/evil-collection
;; Version: 0.0.1
;; Package-Requires: ((emacs "25.1") (evil "1.2.3"))
;; Keywords: evil, ediff, tools
;; This file is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published
;; by the Free Software Foundation; either version 3, or (at your
;; option) any later version.
;;
;; This file is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;;
;; For a full copy of the GNU General Public License
;; see <http://www.gnu.org/licenses/>.
;;; Commentary:
;; Make ediff a little evil. This configures ediff to be friendlier to users
;; of vim-like keybindings. Consult the help buffer (=?=) for more info.
;; Here's a table describing the bindings
;; | Command | Original Binding | Evil-ediff |
;; |-----------------------------+------------------+-------------|
;; | ediff-next-difference | n,SPC | C-j,n,SPC |
;; | ediff-previous-difference | p,DEL | C-k,N,p,DEL |
;; | ediff-jump-to-difference | j | d |
;; | jump to first difference | 1j | gg (or 1d) |
;; | jump to last difference | N/A | G |
;; | copy region A to region B | a | a,l |
;; | copy region B to region A | b | b,h |
;; | scroll down 1 line | C-u 1 v | j |
;; | scroll up 1 line | C-u 1 V | k |
;; | scroll down half page | v,C-v | C-d,v,C-v |
;; | scroll up half page | V,M-v | C-u,V,M-v |
;; | scroll left | > | zh |
;; | scroll right | < | zl |
;; | toggle highlighting | h | H |
;; | ediff-suspend | z | C-z |
;; Not implemented yet
;; | restore old diff | ra,rb,rc | u |
;;; Code:
(require 'evil-collection)
(require 'ediff nil t)
(defconst evil-collection-ediff-maps '(ediff-mode-map))
(defvar evil-collection-ediff-initial-state-backup (evil-initial-state 'ediff-mode))
(defvar evil-collection-ediff-long-help-message-compare2-backup ediff-long-help-message-compare2)
(defvar evil-collection-ediff-long-help-message-compare3-backup ediff-long-help-message-compare3)
(defvar evil-collection-ediff-long-help-message-narrow2-backup ediff-long-help-message-narrow2)
(defvar evil-collection-ediff-long-help-message-word-backup ediff-long-help-message-word-mode)
(defvar evil-collection-ediff-long-help-message-merge-backup ediff-long-help-message-merge)
(defvar evil-collection-ediff-long-help-message-head-backup ediff-long-help-message-head)
(defvar evil-collection-ediff-long-help-message-tail-backup ediff-long-help-message-tail)
(defvar evil-collection-ediff-help-changed nil)
(defun evil-collection-ediff-adjust-help ()
"Adjust long help messages to reflect evil-ediff bindings."
(unless evil-collection-ediff-help-changed
(dolist (msg '(ediff-long-help-message-compare2
ediff-long-help-message-compare3
ediff-long-help-message-narrow2
ediff-long-help-message-word-mode
ediff-long-help-message-merge
ediff-long-help-message-head
ediff-long-help-message-tail))
(dolist (chng '( ;;("^" . " ")
("p,DEL -previous diff " . "k,N,p -previous diff ")
("n,SPC -next diff " . " j,n -next diff ")
(" j -jump to diff " . " d -jump to diff ")
(" h -highlighting " . " H -highlighting ")
(" v/V -scroll up/dn " . "C-u/d -scroll up/dn ")
(" </> -scroll lt/rt " . "zh/zl -scroll lt/rt ")
(" z/q -suspend/quit" . "C-z/q -suspend/quit")))
(setf (symbol-value msg)
(replace-regexp-in-string (car chng) (cdr chng) (symbol-value msg))))))
(setq evil-collection-ediff-help-changed t))
(defun evil-collection-ediff-scroll-left (&optional arg)
"Scroll left."
(interactive "P")
(let ((last-command-event ?>))
(ediff-scroll-horizontally arg)))
(defun evil-collection-ediff-scroll-right (&optional arg)
"Scroll right."
(interactive "P")
(let ((last-command-event ?<))
(ediff-scroll-horizontally arg)))
(defun evil-collection-ediff-scroll-up (&optional arg)
"Scroll up by half of a page."
(interactive "P")
(let ((last-command-event ?V))
(ediff-scroll-vertically arg)))
(defun evil-collection-ediff-scroll-down (&optional arg)
"Scroll down by half of a page."
(interactive "P")
(let ((last-command-event ?v))
(ediff-scroll-vertically arg)))
(defun evil-collection-ediff-scroll-down-1 ()
"Scroll down by a line."
(interactive)
(let ((last-command-event ?v))
(ediff-scroll-vertically 1)))
(defun evil-collection-ediff-scroll-up-1 ()
"Scroll down by a line."
(interactive)
(let ((last-command-event ?V))
(ediff-scroll-vertically 1)))
(defun evil-collection-ediff-first-difference ()
"Jump to first difference."
(interactive)
(ediff-jump-to-difference 1))
(defun evil-collection-ediff-last-difference ()
"Jump to last difference."
(interactive)
(ediff-jump-to-difference ediff-number-of-differences))
;; (defun evil-collection-ediff-restore-diff ()
;; "Restore the copy of current region."
;; (interactive)
;; (ediff-restore-diff nil ?a)
;; (ediff-restore-diff nil ?b))
(defvar evil-collection-ediff-bindings
'(("d" . ediff-jump-to-difference)
("H" . ediff-toggle-hilit)
("\C-e" . evil-collection-ediff-scroll-down-1)
("\C-y" . evil-collection-ediff-scroll-up-1)
("j" . ediff-next-difference)
("k" . ediff-previous-difference)
("N" . ediff-previous-difference)
("gg" . evil-collection-ediff-first-difference)
("G" . evil-collection-ediff-last-difference)
("\C-d" . evil-collection-ediff-scroll-down)
("\C-u" . evil-collection-ediff-scroll-up)
("\C-z" . ediff-suspend)
("z" . nil)
("zl" . evil-collection-ediff-scroll-right)
("zh" . evil-collection-ediff-scroll-left)
;; Not working yet
;; ("u" . evil-collection-ediff-restore-diff)
)
"A list of bindings changed/added in evil-ediff.")
(defun evil-collection-ediff-startup-hook ()
"Place evil-ediff bindings in `ediff-mode-map'."
(evil-make-overriding-map ediff-mode-map 'normal)
(dolist (entry evil-collection-ediff-bindings)
(define-key ediff-mode-map (car entry) (cdr entry)))
(unless (or ediff-3way-comparison-job
(eq ediff-split-window-function 'split-window-vertically))
(define-key ediff-mode-map "l" 'ediff-copy-A-to-B)
(define-key ediff-mode-map "h" 'ediff-copy-B-to-A))
(evil-normalize-keymaps)
nil)
(defun evil-collection-ediff-setup ()
"Initialize evil-ediff."
(interactive)
(evil-set-initial-state 'ediff-mode 'normal)
(add-hook 'ediff-startup-hook 'evil-collection-ediff-startup-hook)
(evil-collection-ediff-adjust-help))
(defun evil-collection-ediff-revert ()
"Revert changes made by evil-ediff."
(interactive)
(evil-set-initial-state 'ediff-mode evil-collection-ediff-initial-state-backup)
(unless evil-collection-ediff-help-changed
(dolist (msg
'((ediff-long-help-message-compare2 . ediff-long-help-message-compare2-backup)
(ediff-long-help-message-compare3 . ediff-long-help-message-compare3-backup)
(ediff-long-help-message-narrow2 . ediff-long-help-message-narrow2-backup)
(ediff-long-help-message-word-mode . ediff-long-help-message-word-mode-backup)
(ediff-long-help-message-merge . ediff-long-help-message-merge-backup)
(ediff-long-help-message-head . ediff-long-help-message-head-backup)
(ediff-long-help-message-tail . ediff-long-help-message-tail-backup)))
(setf (symbol-value (car msg)) (symbol-value (cdr msg)))))
(setq evil-collection-ediff-help-changed nil)
(remove-hook 'ediff-startup-hook 'evil-collection-ediff-startup-hook))
(provide 'evil-collection-ediff)
;;; evil-collection-ediff.el ends here