#+PROPERTY: header-args:emacs-lisp :tangle "~/.emacs.d/config-min.el" :comments both

* setup ssh keys
#+begin_src emacs-lisp
  (use-package keychain-environment)
  (require 'ssh-key-management)
  (keychain-refresh-environment)
  (rb/ssh-add)
#+end_src
* misc emacs settings
#+begin_src emacs-lisp
  (setq garbage-collection-messages t)

  (setq backup-directory-alist `(("." . ,(concat user-emacs-directory "backups"))))

  (setq xref-history-storage #'xref-window-local-history)

  (setq use-dialog-box nil)

  ;; Don't fricken suspend emacs
  (when (eq #'suspend-frame
            (key-binding (kbd "C-z")))
    (global-set-key (kbd "C-z") nil))

  ;; Minibuffer reading
  (setq switch-window-input-style 'minibuffer)

  ;; Help window select stuff
  (setq help-window-select t)

  ;; I don't care about backup files, stop bothering me
  (setq delete-old-versions t)

  ;; Stop creating lock files
  (setq create-lockfiles nil)

  ;; ;; Undo tree is useful
  (use-package undo-tree)
  (setq undo-tree-history-directory-alist '(("." . "~/.emacs.d/undo"))
        undo-tree-limit 8000000
        undo-tree-auto-save-history nil)
  (global-undo-tree-mode t)
  (add-to-list 'undo-tree-incompatible-major-modes 'vterm-mode)
  (add-to-list 'undo-tree-incompatible-major-modes 'compilation-mode)

  ;; Try out vundo instead?
  ;; (use-package vundo
  ;;   :commands (vundo))

  ;; Beacon-mode
  (use-package beacon)
  (beacon-mode 1)
  (setq beacon-blink-delay 0.1)
  (setq beacon-color "#006400")

  ;; Use my fork of chinese-etzy
  (load-file (ef "lisp/my-etzy.el"))

  ;; If I ever decide to turn on display-lines-mode, use relative line
  ;; numbering
  (setq display-line-numbers-type 'relative)

  ;; Make sure shell and rgrep works in windows
  (when (eq 'windows-nt system-type)
    (setq-default explicit-shell-file-name "bash")
    (setq shell-file-name "bash"))

  ;; Show the column number!
  (column-number-mode t)

  (global-set-key (kbd "C-x C-p") #'pp-macroexpand-last-sexp)
#+end_src

* switching keyboards
#+begin_src emacs-lisp
  (require 'keyboard-toggle)
  (exwm-global-set-key (kbd "s-a") #'my/toggle-keyboard)
#+end_src
* minibuffers
#+begin_src emacs-lisp
  ;; Allow minibuffer-ception
  (setq enable-recursive-minibuffers t)

  ;; I like ido for finding files
  ;; (require 'ido)
  ;; (ido-mode 'file)

  (use-package vertico
    :demand t
    :config
    (require 'vertico-jumper)
    (vertico-mode)
    (vertico-multiform-mode)
    (setq vertico-count 8)

    (use-package consult
    ;;   :bind (
    ;;          ("C-x b" . consult-buffer)                ;; orig. switch-to-buffer
    ;;          ("C-x 4 b" . consult-buffer-other-window) ;; orig. switch-to-buffer-other-window
    ;;          ("C-x 5 b" . consult-buffer-other-frame)  ;; orig. switch-to-buffer-other-frame
    ;;          ("C-x t b" . consult-buffer-other-tab)    ;; orig. switch-to-buffer-other-tab
    ;;          )
      )

    (setq completion-in-region-function
          (lambda (&rest args)
            (apply (if vertico-mode
                       #'consult-completion-in-region
                     #'completion--in-region)
                   args)))

    (use-package affe
      :config
      (defun my/affe-find (&optional dir initial)
        "Fuzzy find in DIR with optional INITIAL input."
        (interactive "P")
        (pcase-let* ((`(,prompt ,paths ,dir) (consult--directory-prompt "Fuzzy find" dir))
                     (default-directory dir))
          (consult--read
           (thread-first (consult--async-sink)
                         (consult--async-refresh-timer 0.05)
                         (consult--async-map (lambda (x) (string-remove-prefix "./" x)))
                         (affe--async (affe--command affe-find-command paths))
                         (consult--async-split #'consult--split-nil))
           :prompt prompt
           :sort nil
           :require-match t
           :history '(:input affe--find-history)
           :initial initial
           :category 'file
           :add-history (thing-at-point 'filename)
           :state (lambda (action cand)
                    (when (and cand (eq action 'return))
                      (find-file (expand-file-name cand dir)))))))

      (advice-add #'affe-find
                  :override
                  #'my/affe-find)

      (with-eval-after-load 'projectile
        (define-key projectile-command-map (kbd "F") #'affe-find)
        (define-key projectile-command-map (kbd "s G") #'affe-grep))

      (with-eval-after-load 'llvm-shared
        (defun affe-quick-prompt-dirs (orig &optional dir initial)
          (message "%s" dir)
          (cl-flet ((dir-plus-subdirs
                      (dir)
                      (cons dir
                            (->>
                             (directory-files "/scratch/benson/tools4/cgt" t "[^.]")
                             (remove-if #'(lambda (dir) (not (file-directory-p dir))))
                             (mapcar #'(lambda (x) (file-name-as-directory x)))))))
            (-->
             (not (equal '(16) dir))
             (if it default-directory
               (completing-read "Quick select directory?"
                                `(,(expand-file-name "lib/Target/Argo/"
                                                     (lls/conf-get 'build-release-dir))
                                  ,(expand-file-name "lib/Target/Argo/"
                                                     (lls/conf-get 'build-debug-dir))
                                  ,(expand-file-name "llvm/lib/Target/Argo/"
                                                     (lls/get-llvm-root-dir))
                                  ,(expand-file-name "llvm_cgt/argo/"
                                                     (lls/conf-aux-get 'tools-directory))
                                  ,@(dir-plus-subdirs
                                     (expand-file-name "cgt/"
                                                       (lls/conf-aux-get 'tools-directory))))))
             (let ((default-directory it))
               (funcall orig dir initial)))))

        (advice-add #'affe-find
                    :around
                    #'affe-quick-prompt-dirs)))

    (setq vertico-multiform-commands
          '((find-file flat (vertico-cycle . t))))

    ;; (setq vertico-multiform-categories
    ;;       '((file flat (vertico-cycle . t))))

    (use-package embark)
    (define-key *root-map* (kbd "C-o") #'embark-act)
    (define-key embark-buffer-map (kbd "e") #'embark-export)

    (advice-add #'vertico--format-candidate
                :around
                #'my/highlight-active-mode)

    (defun my/is-mode-enabled (str)
      (let ((symbol (intern str)))
        (when (fboundp symbol)
          (if (not (boundp symbol))
              (eq major-mode symbol)
            (eval symbol)))))

    (defface vertico-current-mode-enabled `((t (:foreground "cyan" :extend t :inherit vertico-current))) nil)

    (defun my/highlight-active-mode (orig cand prefix suffix index _start)
      (let ((res (funcall orig cand prefix suffix index _start)))
        (cond ((not (my/is-mode-enabled cand))
               res)
              ((= index vertico--index)
               (propertize res 'face 'vertico-current-mode-enabled))
              (t (propertize res 'face font-lock-keyword-face))))))

  (use-package vertico-directory
    :after vertico
    :ensure nil
    ;; More convenient directory navigation commands
    :bind (:map vertico-map
                ("RET" . vertico-directory-enter)
                ("DEL" . vertico-directory-delete-char)
                ("M-DEL" . vertico-directory-delete-word)
                ("C-s" . vertico-next))
    ;; Tidy shadowed file names
    :hook (rfn-eshadow-update-overlay . vertico-directory-tidy)
    :config
    (defun my/should-backwards-tramp ()
      (and (not (eq (char-before) ?:))
           (not (eq (char-before) ?@))
           (save-excursion
             (re-search-backward "@" nil t))
           (save-excursion
             (re-search-backward "/" nil t))
           (< (save-excursion
                (re-search-backward "/" nil t)
                (point))
              (save-excursion
                (re-search-backward "@" nil t)
                (point)))))

    (defun my/vertico-directory-up (&optional n)
      (interactive "p")
      (when (and (> (point) (minibuffer-prompt-end))
                 (or (eq (char-before) ?/)
                     (my/should-backwards-tramp))
                 (eq 'file (vertico--metadata-get 'category)))
        (let ((path (buffer-substring (minibuffer-prompt-end) (point))) found)
          (when (string-match-p "\\`~[^/]*/\\'" path)
            (delete-minibuffer-contents)
            (insert (expand-file-name path)))
          (dotimes (_ n found)
            (save-excursion
              (let ((end (point)))
                (goto-char (1- end))
                (when (re-search-backward (rx (or "/" "@" ":")) (minibuffer-prompt-end) t)
                  (delete-region (1+ (point)) end)
                  (setq found t))))))))

    (advice-add #'vertico-directory-up
                :override
                #'my/vertico-directory-up))

  ;; Optionally use the `orderless' completion style. See
  ;; `+orderless-dispatch' in the Consult wiki for an advanced Orderless style
  ;; dispatcher. Additionally enable `partial-completion' for file path
  ;; expansion. `partial-completion' is important for wildcard support.
  ;; Multiple files can be opened at once with `find-file' if you enter a
  ;; wildcard. You may also give the `initials' completion style a try.
  (use-package orderless
    :init
    ;; Configure a custom style dispatcher (see the Consult wiki)
    ;; (setq orderless-style-dispatchers '(+orderless-dispatch)
    ;;       orderless-component-separator #'orderless-escapable-split-on-space)
    (setq completion-styles '(orderless basic)
          completion-category-defaults nil
          completion-category-overrides '((file (styles basic partial-completion)))))

  ;; Persist history over Emacs restarts. Vertico sorts by history position.
  (use-package savehist
    :init
    (savehist-mode))

  (use-package marginalia
    :after vertico
    :custom
    (marginalia-annotators '(marginalia-annotators-heavy marginalia-annotators-light nil))
    (marginalia-align 'left)
    (marginalia-align-offset (if my/puppet-p 20 100))
    :init
    (marginalia-mode))

  ;; (defun basic-remote-try-completion (string table pred point)
  ;;   (and (vertico--remote-p string)
  ;;        (completion-basic-try-completion string table pred point)))
  ;; (defun basic-remote-all-completions (string table pred point)
  ;;   (and (vertico--remote-p string)
  ;;        (completion-basic-all-completions string table pred point)))
  ;; (add-to-list
  ;;  'completion-styles-alist
  ;;  '(basic-remote basic-remote-try-completion basic-remote-all-completions nil))
  ;; (setq completion-styles '(orderless basic)
  ;;       completion-category-overrides '((file (styles basic-remote partial-completion))))
#+end_src
* navigation
#+begin_src emacs-lisp
  ;; Scrollkeeper is helpful for scrolling up and down
  (defvar window-third-height nil)

  (defun update-window-third-height (win &rest args)
    ;; (message "Updating window height for %s" (window-buffer win))
    (setq window-third-height
          (max 1 (/ (window-height win) 3))))

  (advice-add #'select-window
              :after
              'update-window-third-height)

  (defun scroll-up-third ()
    (interactive)
    ;; (pulse-momentary-highlight-one-line (point))
    (scroll-up (or window-third-height 10)))

  (defun scroll-down-third ()
    (interactive)
    ;; (pulse-momentary-highlight-one-line (point))
    (scroll-down (or window-third-height 10)))

  (global-set-key (kbd "C-v") 'scroll-up-third)
  (global-set-key (kbd "M-v") 'scroll-down-third)
  ;; (use-package scrollkeeper)
  ;; (global-set-key (kbd "C-v") 'scrollkeeper-down)
  ;; (global-set-key (kbd "M-v") 'scrollkeeper-up)

  ;; Word navigation
  (global-set-key (kbd "M-f") 'forward-to-word)
  (global-set-key (kbd "M-F") 'forward-word)

  ;; Goto-char
  (require 'brumlow-goto-char)
  (global-set-key (kbd "M-m") #'jump-to-char)
#+end_src
* window manipulation
#+begin_src emacs-lisp
  ;; The prefix
  (define-prefix-command '*window-map*)
  (define-key *root-map* (kbd "w") '*window-map*)

  ;; Side-window stuff
  (use-package resize-window)
  (require 'side-window-split)

  (defun side-window-exwm-hide-window (buffer alist)
    (when-let (window (and (with-current-buffer buffer
                             (eq major-mode 'exwm-mode))
                           (get-buffer-window buffer)))
      (with-selected-window window
        (previous-buffer))))

  (advice-add #'display-buffer-in-side-window
              :before
              #'side-window-exwm-hide-window)

  (setq window-sides-vertical t)
  (define-key *window-map* (kbd "j") 'side-bottom-window)
  (define-key *window-map* (kbd "k") 'side-top-window)
  (define-key *window-map* (kbd "h") 'side-left-window)
  (define-key *window-map* (kbd "l") 'side-right-window)
  (define-key *window-map* (kbd "d") 'side-window-delete-all)
  (define-key *window-map* (kbd "r") 'resize-window)

  (global-set-key (kbd "C-x 4 B") #'my/display-buffer-in-side-window)
  (global-set-key (kbd "C-x 4 F") #'my/find-file-side-window)
  (global-set-key (kbd "C-x 4 )") #'side-window-delete-all)

  ;; Dedicated window
  (defun my/toggle-dedicated-window ()
    (interactive)
    (let ((win (selected-window)))
      (set-window-dedicated-p win (not (window-dedicated-p win)))))
#+end_src
* dired
#+begin_src emacs-lisp
  ;; I like dired+'s formatting for listing files
  (use-package dired+
    :ensure nil
    :quelpa (dired+ :fetcher "github" :repo "emacsmirror/dired-plus" :branch "master"))
  (require 'dired+)
  (setq diredp-hide-details-initially-flag nil)
  (setq diredp-hide-details-propagate-flag nil)

  (defun dired-mark-pop-up (buffer-or-name op-symbol files function &rest args)
    "Return FUNCTION's result on ARGS after showing which files are marked.
  Displays the file names in a window showing a buffer named
  BUFFER-OR-NAME; the default name being \" *Marked Files*\".  The
  window is not shown if there is just one file, `dired-no-confirm'
  is t, or OP-SYMBOL is a member of the list in `dired-no-confirm'.

  By default, Dired shrinks the display buffer to fit the marked files.
  To disable this, use the Customization interface to add a new rule
  to `display-buffer-alist' where condition regexp is \"^ \\*Marked Files\\*$\",
  action argument symbol is `window-height' and its value is nil.

  FILES is the list of marked files.  It can also be (t FILENAME)
  in the case of one marked file, to distinguish that from using
  just the current file.

  FUNCTION should not manipulate files, just read input (an
  argument or confirmation)."
    (if (or (eq dired-no-confirm t)
            (memq op-symbol dired-no-confirm)
            ;; If FILES defaulted to the current line's file.
            (= (length files) 1))
        (apply function args)
      (let ((buffer (get-buffer-create (or buffer-or-name " *Marked Files*")))
            ;; Mark *Marked Files* window as softly-dedicated, to prevent
            ;; other buffers e.g. *Completions* from reusing it (bug#17554).
            (display-buffer-mark-dedicated 'soft))
        (with-current-buffer-window
            buffer
            `(display-buffer-below-selected
              (window-height . fit-window-to-buffer)
              (preserve-size . (nil . t))
              (body-function
               . ,#'(lambda (_window)
                      ;; Handle (t FILE) just like (FILE), here.  That value is
                      ;; used (only in some cases), to mean just one file that was
                      ;; marked, rather than the current line file.
                      (dired-format-columns-of-files
                       (if (eq (car files) t) (cdr files) files))
                      (remove-text-properties (point-min) (point-max)
                                              '(mouse-face nil help-echo nil))
                      (setq tab-line-exclude nil))))
            #'(lambda (window _value)
                (with-selected-window window
                  (unwind-protect
                      (apply function args)
                    (when (window-live-p window)
                      (quit-restore-window window 'kill)))))))))

  ;; This hook is neat, I get to see how far down the file I
  ;; am. However, it's way too slow. Causes doom-modeline to lock up in
  ;; redisplay. Disabling for now.
  (remove-hook 'dired-after-readin-hook 'diredp-nb-marked-in-mode-name)
  (remove-hook 'dired-mode-hook         'diredp-nb-marked-in-mode-name)

  ;; dired configuration
  (setq dired-dwim-target t)
  (setq dired-listing-switches "-alh  --group-directories-first --sort=extension")

  (when (string-match-p ".*NATIVE_COMP.*" system-configuration-features)
    (require 'dired-native-compile)
    (define-key dired-mode-map (kbd "B") #'dired-do-native-compile))

  ;; diredx lets me hide stuff I don't want to see
  (require 'dired-x)
  (add-hook 'dired-mode-hook (lambda () (dired-omit-mode)))
  (setq dired-omit-files (concat dired-omit-files "\\|^\\..+$"))

  ;; Useful for traversing folders
  (use-package dired-subtree)

  (define-key dired-mode-map (kbd "<tab>") 'dired-subtree-insert)
  (define-key dired-mode-map (kbd "<backtab>") 'dired-subtree-remove)
#+end_src
* emacs lisp
#+begin_src emacs-lisp
  ;; These are the programming facilities I like the most for a minimal
  ;; setup for emacs-lisp programming

  ;; Don't leave any whitespace on the end of lines in a file.
  (use-package ws-butler)
  (ws-butler-global-mode t)

  ;; Errors
  (use-package flycheck)
  (add-to-list 'display-buffer-alist
               `(,(rx bos "*Flycheck errors*" eos)
                 (display-buffer-reuse-window
                  display-buffer-in-side-window)
                 (side            . bottom)
                 (reusable-frames . visible)
                 (window-height   . 0.10)))

  ;; Autocompletion
  (use-package corfu
    :after orderless
    :custom
    (corfu-quit-at-boundary t)
    (corfu-quit-no-match t)
    (corfu-cycle t)
    (corfu-auto t)
    :init
    (add-hook 'prog-mode-hook
              'corfu-mode)
    :config
    (unless window-system
      (use-package corfu-terminal)
      (corfu-terminal-mode 1))
    (when my-ec/enable-exwm
      (require 'corfu-hack)))

  ;; Magit
  (use-package magit)
  (use-package magit-popup)
  ;; (use-package magit-todos
  ;;   :config
  ;;   (require 'hl-todo)
  ;;   (add-to-list 'hl-todo-keyword-faces
  ;; 	       '("TODO(pestctrl)" . "#FFFFFF"))
  ;;   (add-to-list 'magit-todos-keywords-list
  ;; 	       "TODO(pestctrl)")
  ;;   (magit-todos-mode 1))
  ;; Todo: Figure out why transient side-window stuff wrecks my
  ;; side-window stuff
  (setq transient-display-buffer-action
        '(display-buffer-in-side-window
          (side . left)
          (dedicated . t)
          (inhibit-same-window . t)))
  (global-set-key (kbd "C-x g") 'magit-status)
  (global-set-key (kbd "C-x M-g") 'magit-dispatch)
  (require 'magit-overrides)

  ;; Magit uses ediff
  (with-eval-after-load 'ediff
    (setq ediff-window-setup-function 'ediff-setup-windows-plain
          ediff-split-window-function 'split-window-horizontally)

    (defun ediff-clear-up-windows (&rest optional)
      (let ((tab-name (alist-get 'name (tab-bar--current-tab))))
        (unless (string-match-p "-ediff$" tab-name)
          (switch-or-create-tab (concat tab-name "-ediff"))))
      (when (window-parameter (selected-window) 'window-side)
        (window-toggle-side-windows))
      (let ((ignore-window-parameters t))
        (delete-other-windows)))

    (advice-add #'ediff-setup
                :before
                #'ediff-clear-up-windows)

    (require 'ediff-transition)

    (advice-add #'ediff-quit
                :after
                #'tab-bar-close-tab)

    (defun ediff-copy-both-to-C ()
      (interactive)
      (ediff-copy-diff ediff-current-difference nil 'C nil
                       (concat
                        (ediff-get-region-contents ediff-current-difference 'A ediff-control-buffer)
                        (ediff-get-region-contents ediff-current-difference 'B ediff-control-buffer))))
    (defun add-d-to-ediff-mode-map () (define-key ediff-mode-map "d" 'ediff-copy-both-to-C))
    (add-hook 'ediff-keymap-setup-hook 'add-d-to-ediff-mode-map)
    (set-face-attribute 'ediff-even-diff-A nil :background "midnight blue")
    (set-face-attribute 'ediff-even-diff-Ancestor nil :background "midnight blue")
    (set-face-attribute 'ediff-even-diff-B nil :background "midnight blue")
    (set-face-attribute 'ediff-even-diff-C nil :background "midnight blue")
    (set-face-attribute 'ediff-odd-diff-A nil :background "midnight blue")
    (set-face-attribute 'ediff-odd-diff-Ancestor nil :background "midnight blue")
    (set-face-attribute 'ediff-odd-diff-B nil :background "midnight blue")
    (set-face-attribute 'ediff-odd-diff-C nil :background "midnight blue")

    ;; (set-face-attribute 'ediff-odd-diff-A nil :background "gray30")
    ;; (set-face-attribute 'ediff-odd-diff-B nil :background "gray30")
    ;; (set-face-attribute 'ediff-even-diff-A nil :background "#5c370f")
    ;; (set-face-attribute 'ediff-even-diff-B nil :background "#5c370f")
    ;; ;; (set-face-attribute 'ediff-current-diff-A nil :background "")
    ;; (set-face-attribute 'ediff-current-diff-B nil :background "dark green")
    )

  (with-eval-after-load 'diff
    (set-face-attribute 'diff-header nil :background "gray20")
    (set-face-attribute 'diff-file-header nil :background "gray20")
    (set-face-attribute 'diff-function nil :background "midnight blue")
    (set-face-attribute 'diff-added nil :background "#104010")
    (set-face-attribute 'diff-refine-added nil :background "#308030"))

  ;; Paredit
  (use-package paredit
    :bind (:map paredit-mode-map
                ("M-?" . nil))
    :hook ((emacs-lisp-mode . paredit-mode)
           (lisp-mode . paredit-mode)))

  ;; Paren highlighting
  (show-paren-mode t)

  ;; Rainbow parens
  (use-package rainbow-delimiters)
  (add-hook 'prog-mode-hook #'rainbow-delimiters-mode)

  ;; Which function
  (which-function-mode 1)

  ;; Macroexpander
  (use-package macrostep)

  (define-key macrostep-keymap (kbd "C-c C-c") nil)

  (define-key macrostep-keymap (kbd "DEL") nil)
  (define-key macrostep-keymap (kbd "c") nil)
  (define-key macrostep-keymap (kbd "u") nil)
  (define-key macrostep-keymap (kbd "C-c q") #'macrostep-collapse)
  ;; (define-key macrostep-keymap (kbd "q") #'macrostep-collapse)

  (define-key macrostep-keymap (kbd "RET") nil)
  (define-key macrostep-keymap (kbd "e") nil)
  (define-key emacs-lisp-mode-map (kbd "C-c e") #'macrostep-expand)


  (define-key macrostep-keymap (kbd "n") nil)
  (define-key macrostep-keymap (kbd "C-c C-n") #'macrostep-next-macro)

  (define-key macrostep-keymap (kbd "p") nil)
  (define-key macrostep-keymap (kbd "C-c C-p") #'macrostep-prev-macro)

  ;; Auto highlighting of symbols
  (use-package auto-highlight-symbol)
  (add-hook 'prog-mode-hook
            'auto-highlight-symbol-mode)

  ;; wgrep
  (use-package wgrep)

  ;; Use cursors, sooo good
  (use-package multiple-cursors)

  (define-prefix-command '*multiple-cursors-map*)
  (define-key *multiple-cursors-map* (kbd "a") 'mc/mark-all-like-this)
  (define-key *multiple-cursors-map* (kbd "A") 'mc/vertical-align)
  (define-key *multiple-cursors-map* (kbd "SPC") 'mc/vertical-align-with-space)
  (define-key *multiple-cursors-map* (kbd "n") 'mc/insert-numbers)

  (defhydra mc-interactive (*multiple-cursors-map* "i")
    "For those looping commands"
    ("n" mc/mark-next-like-this)
    ("p" mc/mark-previous-like-this)
    ("s" mc/skip-to-next-like-this)
    ("S" mc/skip-to-previous-like-this)
    ("q" nil))

  (global-set-key (kbd "C-c m") '*multiple-cursors-map*)

  ;; Space and tab configuration
  (setq default-tab-width 4)
  (setq-default indent-tabs-mode nil)
  (setq-default tab-width 4)

  ;; If I have to switch to viewing tabs
  (defun my/TABS (num)
    (interactive "p")
    (setq tab-width (if (= num 1)
                        8
                      num)))

  ;; Eval buffer, slime-ism
  (define-key emacs-lisp-mode-map (kbd "C-c C-k") #'eval-buffer)

  ;; Make scratch buffers out of nowhere!
  (require 'cl)
  (defun scratch-buffer ()
    (interactive)
    (let ((count 0))
      (while (get-buffer (format "*scratch%d*" count))
        (cl-incf count))
      (let ((buff (get-buffer-create (format "*scratch%d*" count))))
        (with-current-buffer buff
          (lisp-interaction-mode)
          (insert (substitute-command-keys initial-scratch-message)))
        (display-buffer-same-window buff nil))))

  ;; Eval and replace
  (defun my/eval-and-replace ()
    "Replace the preceding sexp with its value."
    (interactive)
    (backward-kill-sexp)
    (condition-case nil
        (prin1 (eval (read (current-kill 0)))
               (current-buffer))
      (error (message "Invalid expression")
             (insert (current-kill 0)))))

  (define-key emacs-lisp-mode-map (kbd "C-c C-e") 'my/eval-and-replace)

  ;; Use cider's eval expression
  (use-package cider)
  (autoload 'cider--make-result-overlay "cider-overlays")

  (defun endless/eval-overlay (value point)
    (let ((comment-start ";;"))
      (cider--make-result-overlay (format "%S" value)
        :where point
        :duration 'command))
    value)

  (advice-add 'eval-region :around
              (lambda (f beg end &rest r)
                (endless/eval-overlay
                 (apply f beg end r)
                 end)))

  (advice-add 'eval-last-sexp :filter-return
              (lambda (r)
                (endless/eval-overlay r (point))))

  (advice-add 'eval-defun :filter-return
              (lambda (r)
                (endless/eval-overlay
                 r
                 (save-excursion
                   (end-of-defun)
                   (point)))))

  ;; expand-region
  (use-package expand-region
    :commands er/expand-region
    :bind (("M-E" . #'er/expand-region)))

  ;; Banner comments
  (unless my-ec/at-ti
    (use-package banner-comment
      :commands banner-comment
      :bind (("C-c b" . #'banner-comment))))

  (add-hook 'lisp-mode-hook
            (lambda () (setq comment-start ";; ")))

  (add-hook 'emacs-lisp-mode-hook
            (lambda () (setq comment-start ";; ")))

  ;; re-builder
  (require 're-builder)
  (setq reb-re-syntax 'rx)
#+end_src
* ibuffer
#+begin_src emacs-lisp
  (global-set-key (kbd "C-x C-b") 'ibuffer)

  (setq ibuffer-show-empty-filter-groups nil)

  (add-hook 'ibuffer-mode-hook
            #'(lambda ()
               (ibuffer-switch-to-saved-filter-groups "default")
               (ibuffer-do-sort-by-custom)
               ;; (ibuffer-auto-mode)
               ))

  (require 'ibuf-ext)

  (define-key ibuffer-mode-map my/keymap-key nil)

  (with-eval-after-load "ibuf-ext"
    (define-ibuffer-filter directory-name
        "Filter files in the agenda folder"
      (:description "agenda")
      (and (buffer-file-name buf)
           (string-match qualifier
                         (buffer-file-name buf))))

    (define-ibuffer-sorter custom
      "My custom ibuffer sorter."
      (:description "custom")
      (cl-labels ((exwm-name-or-directory (buffer)
                    (with-current-buffer buffer
                      (if (eq major-mode 'exwm-mode)
                          (downcase (buffer-name))
                        (concat
                         (symbol-name major-mode)
                         " - "
                         (or (ibuffer-buffer-file-name)
                             ""))))))
        (not
         (string-greaterp (downcase (or (exwm-name-or-directory (car a)) ""))
                          (downcase (or (exwm-name-or-directory (car b)) "")))))))

  (add-to-list 'ibuffer-never-show-predicates
               #'(lambda (buf)
                  (with-current-buffer buf
                    (eq major-mode 'helm-major-mode))))

  (setq ibuffer-saved-filter-groups
        '(("default"
           ("X-Windows"       (mode . exwm-mode))
           ("Terminals"       (or (mode . vterm-mode)
                                  (mode . term-mode)))
           ("emacs-config"    (and (or (filename . ".emacs.d")
                                       (filename . "emacs-config"))
                                   (not (mode . magit-status-mode))
                                   (not (mode . magit-log-mode))
                                   (not (mode . magit-diff-mode))
                                   (not (mode . magit-process-mode))))
           ("code-aux"        (or (mode . slime-repl-mode)
                                  (mode . slime-mode)
                                  (mode . magit-status-mode)
                                  (mode . magit-status-mode)
                                  (mode . magit-log-mode)
                                  (mode . magit-diff-mode)
                                  (mode . magit-process-mode)
                                  (mode . ein:notebooklist-mode)
                                  (mode . cider-repl-mode)
                                  (mode . comint-mode)
                                  (mode . makefile-gmake-mode)
                                  (mode . conf-space-mode)
                                  (mode . sh-mode)))
           ("code"            (and (predicate . (not (file-remote-p default-directory)))
                                   (or (mode . perl-mode)
                                       (mode . asm-mode)
                                       (mode . php-mode)
                                       (mode . clojure-mode)
                                       (mode . csharp-mode)
                                       (mode . c++-mode)
                                       (mode . c-mode)
                                       (mode . scala-mode)
                                       (mode . emacs-lisp-mode)
                                       (mode . java-mode)
                                       (mode . js-mode)
                                       (mode . python-mode)
                                       (mode . ng2-ts-mode)
                                       (mode . lisp-mode)
                                       (mode . ein:notebook-multilang-mode)
                                       (mode . llvm-mode))))
           ("web"             (or (mode . web-mode)
                                  (mode . mhtml-mode)
                                  (mode . js2-mode)
                                  (mode . css-mode)))
           ("Org Mode"        (and (mode . org-mode)
                                   (not (directory-name . "agenda"))))
           ("Shell"           (or (mode . shell-mode)
                                  (mode . compilation-mode)))
           ("text"            (filename . "\\.txt"))
           ("pdfs"            (or (mode . doc-view-mode)
                                  (mode . pdf-view-mode)))
           ("Agenda Buffers"  (mode . org-agenda-mode))
           ("Agenda Files"    (mode . org-mode))
           ("folders"         (and (mode . dired-mode)
                                   (predicate . (not (file-remote-p default-directory)))))
           ("tramp"           (predicate . (file-remote-p default-directory)))
           ("Help"            (or (name . "\*Help\*")
                                  (name . "\*Apropos\*")
                                  (name . "\*info\*"))))))

  (defun ibuffer-find-file-with-ido ()
    "Like `find-file', but default to the directory of the buffer at point."
    (interactive)
    (let ((completing-read-function #'ido-completing-read)
          (default-directory (let ((buf (ibuffer-current-buffer)))
                               (if (buffer-live-p buf)
                                   (with-current-buffer buf
                                     default-directory)
                                 default-directory))))
      (call-interactively #'ido-find-file)))

  ;; (define-key ibuffer-mode-map (kbd "C-x C-f") #'ibuffer-find-file-with-ido)
#+end_src
* useful tools
** org-mode
 #+begin_src emacs-lisp
   (require 'org)

   (setq org-src-window-setup 'current-window)
   (setq org-use-speed-commands t)
 #+end_src
*** Indent look
#+begin_src emacs-lisp
  (setq org-startup-indented t)

  (defun my/org-indent-prefixes ()
    "Compute prefix strings for regular text and headlines."
    (setq org-indent--heading-line-prefixes
          (make-vector org-indent--deepest-level nil))
    (setq org-indent--inlinetask-line-prefixes
          (make-vector org-indent--deepest-level nil))
    (setq org-indent--text-line-prefixes
          (make-vector org-indent--deepest-level nil))
    (dotimes (n org-indent--deepest-level)
      (let ((indentation (if (<= n 1) 0
                           (* (1- org-indent-indentation-per-level)
                              (1- n)))))
        ;; Headlines line prefixes.
        (let ((heading-prefix ""))
          (aset org-indent--heading-line-prefixes
                n
                (org-add-props heading-prefix nil 'face 'org-indent))
          ;; Inline tasks line prefixes
          (aset org-indent--inlinetask-line-prefixes
                n
                (cond ((<= n 1) "")
                      ((bound-and-true-p org-inlinetask-show-first-star)
                       (concat org-indent-inlinetask-first-star
                               (substring heading-prefix 1)))
                      (t (org-add-props heading-prefix nil 'face 'org-indent)))))
        ;; Text line prefixes.
        (aset org-indent--text-line-prefixes
              n
              (org-add-props
                  (concat (make-string (if (< n 2) n
                                         (1+ indentation)) ?\s)
                          (and (> n 0)
                               (char-to-string org-indent-boundary-char)))
                  nil 'face 'org-indent)))))


  (advice-add #'org-indent--compute-prefixes
              :override
              #'my/org-indent-prefixes)
#+end_src
** terminal
#+begin_src emacs-lisp
  (if (eq system-type 'windows-nt)
      (define-key *root-map* "c" #'shell)
    (quelpa '(vterm))
    (require 'multi-vterm-tabs)
    (use-package vterm
      :ensure nil
      :commands vterm vterm-kill
      :bind (("C-x 4 t" . #'find-vterm-other-window)
             :map *root-map*
             ("c" . #'multi-vterm-tab))
      :config
      (add-to-list 'vterm-tramp-shells
                   '("ssh" "/bin/bash"))

      (add-to-list 'vterm-tramp-shells
                   '("sudo" "/bin/bash"))

      (setq ansi-color-names-vector
            ["black" "red3" "green3" "yellow3" "DodgerBlue2" "magenta3" "cyan3" "gray90"])

      (set-face-attribute 'term-bold        nil :weight 'bold)
      (set-face-attribute 'vterm-color-blue nil :foreground "DodgerBlue2")

      (define-key vterm-mode-map my/keymap-key nil)

      (defun vterm/delete-to-side (arg)
        (interactive "P")
        (if arg
            (vterm-send "C-u")
          (vterm-send "C-k")))

      (define-key vterm-mode-map (kbd "C-k") #'vterm/delete-to-side)

      (setq vterm-kill-buffer-on-exit t)))
#+end_src
** wgrep
#+begin_src emacs-lisp
#+end_src
** Query replace rx
#+begin_src emacs-lisp
  (defun my/query-replace-rx (&rest _)
    "Call `query-replace-regexp', reading regexp in `rx' syntax.
    Automatically wraps in parens and adds `seq' to the beginning of
    the form."
    (interactive)
    (cl-letf (((symbol-function #'query-replace-read-from) (lambda (&rest _)
                                                             (--> (read-string "rx form: ")
                                                                  (concat "'(seq " it ")")
                                                                  (read it)
                                                                  (cadr it)
                                                                  (rx-to-string it)))))
      (call-interactively #'query-replace-regexp)))
#+end_src
** helm info is pretty slick
#+begin_src emacs-lisp
  (use-package helm)
  ;; (require 'helm-info)

  ;; (defun helm-info-emacs-stuff ()
  ;;   "Helm for Emacs, Elisp, and
  ;;   CL-library info pages."
  ;;   (interactive)
  ;;   (helm :sources
  ;;         '(helm-source-info-emacs helm-source-info-elisp helm-source-info-cl)))

  ;; (global-set-key (kbd "C-c h") #'helm-info-emacs-stuff)
#+end_src
** helpful
#+begin_src emacs-lisp
  (use-package helpful)
  (global-set-key (kbd "C-h f") #'helpful-function)
  (global-set-key (kbd "C-h v") #'helpful-variable)
  (global-set-key (kbd "C-h k") #'helpful-key)
  (global-set-key (kbd "C-h o") #'helpful-symbol)
  (setq helpful-switch-buffer-function #'pop-to-buffer)
#+end_src
** olivetti
#+begin_src emacs-lisp
  (use-package olivetti
    :commands (olivetti-mode)
    :hook ((prog-mode . olivetti-mode)
           (org-mode . olivetti-mode)
           (dired-mode . olivetti-mode)
           (org-agenda . olivetti-mode)
           (Info-mode . olivetti-mode)
           (message-mode . olivetti-mode)
           (markdown-mode . olivetti-mode))
    :config
    (add-to-list 'window-persistent-parameters
                 '(spilt-window . t))

    (advice-add 'window-toggle-side-windows
                :before
                'olivetti-reset-all-windows)

    (setq-default olivetti-body-width 140)

    (with-eval-after-load 'mu4e
      (add-hook 'mu4e-view-mode-hook
                'olivetti-mode)))
#+end_src
** w3m
#+begin_src emacs-lisp
  ;; Remove when Emacs 27 releases
  (when (executable-find "w3m")
    (setq w3m-use-tabs nil)
    (use-package w3m)

    (defun dired-browse-with-w3m (arg)
      (interactive "P")
      (let ((browse-url-browser-function (if arg
                                             (symbol-function browse-url-browser-function)
                                           #'w3m-browse-url)))
        (browse-url-of-dired-file)))

    (define-key dired-mode-map (kbd "W") 'dired-browse-with-w3m)

    (global-set-key (kbd "C-c g")
                    (lambda ()
                      (interactive)
                      (w3m-goto-url "https://google.com"))))
#+end_src
** ztree, for directory diffing
#+begin_src emacs-lisp
  (use-package ztree)
#+end_src
* time zones
#+begin_src emacs-lisp
  (when my/puppet-p
    (setenv "TZ" "CST6CDT"))
#+end_src
* opening links
#+begin_src emacs-lisp
  (if (not my-ec/is-wsl)
      (setq browse-url-browser-function 'browse-url-firefox)
    (setq browse-url-generic-program  "/mnt/c/Windows/System32/cmd.exe"
          browse-url-generic-args     '("/c" "start")
          browse-url-browser-function 'browse-url-generic
          search-web-default-browser 'browse-url-generic))
#+end_src
* Terminal compatibility
#+begin_src emacs-lisp
  (global-set-key (kbd "M-[ emacs-C-SPC") #'set-mark-command)
  (global-set-key (kbd "M-[ emacs-C-/") #'undo)
#+end_src
