;;; editor/evil/+everywhere.el -*- lexical-binding: t; -*-

;; We load evil-collection ourselves for these reasons:
;;
;; 1. To truly lazy load it. Some of its modules, like
;;    evil-collection-{elisp-mode,buff-menu} are loaded immediately, because
;;    Emacs loads their packages immediately, which pulls in all of
;;    evil-collection (and other packages with it, sometimes).
;; 2. This ensures a predictable load order, versus lazy loading using :defer or
;;    :after-call. This means users can use (after! org ...) and be sure that
;;    their changes will override evil-collection's.
;; 3. Ideally, we'd do away with evil-collection entirely. It changes too often,
;;    introduces breaking bugs too frequently, and I don't agree with all their
;;    design choices. Regardless, it does mork than it causes trouble, so it may
;;    be here to stay.
;; 4. Adds `+evil-collection-disabled-list', to make it easier for users to
;;    disable modules, and to reduce the effort required to maintain our copy of
;;    `evil-collection-list' (now I can just copy it from time to time).

(defvar +evil-collection-disabled-list
  '(anaconda-mode
    buff-menu
    comint
    company
    custom
    eldoc
    elisp-mode
    ert
    free-keys
    help
    helm
    image
    kotlin-mode
    occur
    package-menu
    ruby-mode
    simple
    slime)
  "A list of `evil-collection' modules to ignore. See the definition of this
variable for an explanation of the defaults (in comments). See
`evil-collection-mode-list' for a list of available options.")

(defvar evil-collection-setup-minibuffer nil)

;; We do this ourselves, and better.
(defvar evil-collection-want-unimpaired-p nil)

;; This has to be defined here since evil-collection doesn't autoload its own.
;; It must be updated whenever evil-collection updates theirs. Here's an easy
;; way to update it:
;;
;; (with-current-buffer
;;     (url-retrieve-synchronously "https://raw.githubusercontent.com/emacs-evil/evil-collection/master/evil-collection.el" t t)
;;   (goto-char (point-min))
;;   (when (re-search-forward "^(defvar evil-collection--supported-modes\n[^(]+")
;;     (let ((list (sexp-at-point)))
;;       ;; Fixes
;;       (when (assq 'pdf list)
;;         (setf (alist-get 'pdf list) '(pdf-tools)))
;;       (kill-new (prin1-to-string list)))))

(defvar evil-collection-mode-list
  `(2048-game
    ag
    alchemist
    anaconda-mode
    apropos
    arc-mode
    bookmark
    (buff-menu "buff-menu")
    calc
    calendar
    cider
    cmake-mode
    comint
    company
    compile
    (custom cus-edit)
    cus-theme
    daemons
    deadgrep
    debbugs
    debug
    diff-mode
    dired
    disk-usage
    doc-view
    docker
    ebib
    edbi
    edebug
    ediff
    eglot
    elfeed
    elisp-mode
    elisp-refs
    elisp-slime-nav
    emms
    epa
    ert
    eshell
    eval-sexp-fu
    evil-mc
    eww
    flycheck
    flymake
    free-keys
    geiser
    ggtags
    git-timemachine
    go-mode
    grep
    guix
    hackernews
    helm
    help
    helpful
    hg-histedit
    hungry-delete
    ibuffer
    image
    image-dired
    image+
    imenu-list
    indium
    info
    ivy
    js2-mode
    leetcode
    log-edit
    log-view
    lsp-ui-imenu
    lua-mode
    kotlin-mode
    macrostep
    man
    magit
    magit-todos
    ,@(if evil-collection-setup-minibuffer '(minibuffer))
    monky
    mu4e
    mu4e-conversation
    neotree
    notmuch
    nov
    (occur replace)
    omnisharp
    outline
    p4
    (package-menu package)
    pass
    (pdf pdf-tools)
    popup
    proced
    process-menu
    prodigy
    profiler
    python
    quickrun
    racer
    realgud
    reftex
    restclient
    rjsx-mode
    robe
    rtags
    ruby-mode
    simple
    slime
    sly
    tablist
    (term term ansi-term multi-term)
    tetris
    tide
    transmission
    typescript-mode
    vc-annotate
    vc-dir
    vc-git
    vdiff
    view
    vlf
    vterm
    w3m
    wdired
    wgrep
    which-key
    woman
    xref
    youtube-dl
    (ztree ztree-diff)))

(defun +evil-collection-init (module &optional disabled-list)
  "Initialize evil-collection-MODULE.

Unlike `evil-collection-init', this respects `+evil-collection-disabled-list',
and complains if a module is loaded too early (during startup)."
  (unless (memq (or (car-safe module) module) disabled-list)
    (doom-log "Initialized evil-collection-%s %s"
              (or (car-safe module) module)
              (if doom-init-time "" "(too early!)"))
    (with-demoted-errors "evil-collection error: %s"
      (evil-collection-init (list module)))))


;;
;;; Bootstrap

;; These modes belong to packages that Emacs always loads at startup, causing
;; evil-collection to load immediately. We avoid this by loading them after
;; evil-collection has first loaded...
(with-eval-after-load 'evil-collection
  (mapc #'+evil-collection-init '(comint custom help)))

;; ...or on first invokation of their associated major/minor modes.
(add-transient-hook! 'Buffer-menu-mode
  (+evil-collection-init '(buff-menu "buff-menu")))
(add-transient-hook! 'image-mode
  (+evil-collection-init 'image))
(add-transient-hook! 'emacs-lisp-mode
  (+evil-collection-init 'elisp-mode))
(add-transient-hook! 'occur-mode
  (+evil-collection-init 'replace))

(evil-define-key* 'normal process-menu-mode-map
  "q" #'kill-current-buffer
  "d" #'process-menu-delete-process)

;; Don't overwrite the leader keys
(setq evil-collection-key-blacklist
      (list doom-leader-key doom-localleader-key
            doom-leader-alt-key doom-localleader-alt-key))

;; Load the rest
(dolist (mode evil-collection-mode-list)
  (dolist (req (or (cdr-safe mode) (list mode)))
    (with-eval-after-load req
      (+evil-collection-init mode +evil-collection-disabled-list))))
