;;; +key.el --- minor mode for keymaps -*- lexical-binding: t; -*-

;;; Commentary:

;; Much of the code here was cribbed from https://emacs.stackexchange.com/a/358,
;; which in turn was cribbed in part from
;; https://github.com/kaushalmodi/.emacs.d/blob/master/elisp/modi-mode.el,
;; https://github.com/jwiegley/use-package/blob/master/bind-key.el and
;; elsewhere.

;; The basic idea is to have a minor-mode for my personal key customizations,
;; especially a "leader key" set up à la vim.  In Emacs, I use `C-z' for this
;; leader key, because of its easy location and relative uselessness by default.

;;; Code:

(require 'easy-mmode)
(require 'setup nil t)

;; I need to define this map before the proper mode map.
(defvar +key-leader-map (let ((map (make-sparse-keymap))
                               (c-z (global-key-binding "\C-z")))
                           (define-key map "\C-z" c-z)
                           map)
  "A leader keymap under the \"C-z\" bind.")

;; http://xahlee.info/emacs/emacs/emacs_menu_app_keys.html and
(defvar +key-menu-map (let ((map (make-sparse-keymap)))
                        (define-key map (kbd "<menu>")
                          #'execute-extended-command)
                        map)
  "Custom bindings behind the menu key.")

(defvar +key-mode-map (let ((map (make-sparse-keymap)))
                        (define-key map "\C-z" +key-leader-map)
                        (define-key map (kbd "<menu>") +key-menu-map)
                        map)
  "Keymap for `+key-mode'.")

(defun turn-off-+key-mode ()
  "Turn off `+key-mode'."
  (+key-mode -1))

;;;###autoload
(define-minor-mode +key-mode
  "A minor mode with keybindings that will override every other mode."
  :init-value t
  :lighter " +"
  (if +key-mode
      (progn                            ; Enable
        (add-to-list 'emulation-mode-map-alists
                     `((+key-mode . ,+key-mode-map)))
        ;; Disable in minibuffer
        (add-hook 'minibuffer-setup-hook #'turn-off-+key-mode)
        ;; compat Linux-Windows
        (define-key key-translation-map (kbd "<apps>") (kbd "<menu>"))
        ;; curse you, thinkpad keyboard!!!
        (define-key key-translation-map (kbd "<print>") (kbd "<menu>"))
        )
    ;; Disable
    (setq emulation-mode-map-alists
          (assoc-delete-all '+key-mode emulation-mode-map-alists
                            (lambda (a b)
                              (equal (car a) b))))
    (remove-hook 'minibuffer-setup-hook #'turn-off-+key-mode)
    (define-key key-translation-map (kbd "<print>") nil)
    (define-key key-translation-map (kbd "<apps>") nil)))

;;;###autoload
(defun +key-setup ()
  "Ensure `+key-mode' happens after init."
  (if after-init-time
      (+key-global-mode)
    (add-hook 'after-init-hook #'+key-global-mode)))

;;;###autoload
(define-globalized-minor-mode +key-global-mode +key-mode +key-mode)

;; Extras for `setup'
(with-eval-after-load 'setup
  (setup-define :+key
    (lambda (key command)
      `(define-key +key-mode-map ,key ,command))
    :documentation "Bind KEY to COMMAND in `+key-mode-map'."
    :debug '(form sexp)
    :ensure '(kbd nil)
    :repeatable t)

  (setup-define :+leader
    (lambda (key command)
      `(define-key +key-leader-map ,key ,command))
    :documentation "Bind KEY to COMMAND in `+key-leader-map'."
    :debug '(form sexp)
    :ensure '(kbd nil)
    :repeatable t)

  (setup-define :+menu
    (lambda (key command)
      `(define-key +key-menu-map ,key ,command))
    :documentation "Bind KEY to COMMAND in `+key-leader-map'."
    :debug '(form sexp)
    :ensure '(kbd nil)
    :repeatable t))

(provide '+key)
;;; +key.el ends here
