* corfu
  #+begin_src emacs-lisp
    (use-package corfu
      :ensure t
      :demand t
      :init (setq corfu-echo-documentation 0.25
                  corfu-cycle t
                  corfu-count 15
                  corfu-quit-no-match t
                  corfu-auto t
                  corfu-commit-predicate nil
                  corfu-preview-current nil
                  corfu-preselect-first t
                  corfu-min-width 20
                  corfu-max-width 100)
      :config

      ;; show eldoc string immediately after accepted completion too
      (with-eval-after-load "eldoc"
        (eldoc-add-command-completions "corfu-"))

      (defun jao-corfu-enable-no-auto ()
        (setq-local corfu-auto nil)
        (corfu-mode 1))

      (defmacro jao-corfu-no-auto (mode)
        (let ((mode-name (intern (format "%s-mode" mode)))
              (hook-name (intern (format "%s-mode-hook" mode))))
          `(with-eval-after-load ',mode
             (add-to-list 'corfu-excluded-modes ',mode-name)
             (add-hook ',hook-name #'jao-corfu-enable-no-auto))))

      (jao-corfu-no-auto eshell)
      ;; (add-to-list 'corfu-excluded-modes 'notmuch-message-mode)

      (defun jao-corfu--active-p ()
        (and (>= corfu--index 0) (/= corfu--index corfu--preselect)))

      (defun jao-corfu-quit-or-insert ()
        (interactive)
        (if (jao-corfu--active-p) (corfu-insert) (corfu-quit)))

      (defun jao-corfu-quit-or-previous ()
        (interactive)
        (if (jao-corfu--active-p)
            (corfu-previous)
          (corfu-quit)
          (previous-line)))

      :bind (:map corfu-map
                  ("C-<return>" . corfu-insert)
                  ("\r" . jao-corfu-quit-or-insert)
                  ("C-p" . jao-corfu-quit-or-previous)))

    (defun corfu-in-minibuffer ()
      (when (not (bound-and-true-p vertico--input)) (corfu-mode 1)))

    (when (display-graphic-p)
      (add-hook 'minibuffer-setup-hook #'corfu-in-minibuffer 1)
      (global-corfu-mode 1))

  #+end_src
* erc
*** package
  #+begin_src emacs-lisp
    (use-package erc
      :init
      (setq erc-modules
            '(autojoin
              button
              dcc
              fill
              irccontrols
              match
              move-to-prompt
              netsplit
              networks
              noncommands
              notify
              pcomplete
              ring
              services
              stamp
              track
              truncate))

      (setq erc-auto-query 'bury
            erc-autojoin-channels-alist `(("libera.chat" ,@jao-libera-channels))
            erc-away-nickname "jao"
            erc-button-buttonize-nicks t
            erc-common-server-suffixes '(("libera.chat$" . "lb"))
            erc-current-nick-highlight-type 'nick-or-keyword
            erc-email-userid (car jao-mails)
            erc-fill-column 84
            erc-fill-prefix "     "
            erc-format-nick-function 'erc-format-@nick
            erc-header-line-face-method t
            erc-header-line-format nil ;; "%l %o"
            erc-header-line-uses-tabbar-p nil
            erc-hide-list '("JOIN" "PART" "QUIT")
            erc-hide-timestamps nil
            erc-input-line-position -1
            erc-insert-timestamp-function 'erc-insert-timestamp-right
            erc-join-buffer 'bury
            erc-kill-buffer-on-part t
            erc-kill-queries-on-quit t
            erc-log-channels-directory nil
            erc-mode-line-away-status-format "(a)"
            erc-mode-line-format "%t"
            erc-nick "jao"
            erc-notice-highlight-type 'all
            erc-notice-prefix "- "
            erc-notify-signoff-hook 'erc-notify-signoff
            erc-notify-signon-hook 'erc-notify-signon
            erc-pcomplete-nick-postfix ","
            erc-rename-buffers t
            erc-server-send-ping-timeout 60
            erc-prompt ":"
            erc-prompt-for-nickserv-password nil
            erc-use-auth-source-for-nickserv-password t
            erc-prompt-for-password nil
            erc-public-away-p t
            erc-server "irc.libera.chat"
            erc-server-coding-system '(utf-8 . undecided)
            erc-server-reconnect-attempts 10
            erc-server-reconnect-timeout 10
            erc-timestamp-format "%H:%M"
            erc-timestamp-only-if-changed-flag t
            erc-timestamp-right-column 84
            erc-user-full-name "https://jao.io"
            erc-user-mode "+i"
            erc-whowas-on-nosuchnick t)

      :config

      (define-minor-mode ncm-erc-mode "" nil
        (:eval (format " [%s]" (hash-table-count erc-channel-users))))

      (add-hook 'erc-mode-hook (lambda () (ncm-erc-mode 1)))
      (add-hook 'erc-mode-hook (lambda () (auto-fill-mode -1))))
    #+end_src
*** no angles
   #+begin_src emacs-lisp
    (defun jao-erc--no-angles (old-func &rest args)
      (let ((msg (apply old-func args)))
        (replace-regexp-in-string "^<\\([^>]+\\)>" "(\\1)" msg)))

    (with-eval-after-load "erc"
      (modify-syntax-entry ?\( "." erc-mode-syntax-table)
      (modify-syntax-entry ?\) "." erc-mode-syntax-table)
      (advice-add 'erc-format-privmessage :around #'jao-erc--no-angles)
      (advice-add 'erc-format-my-nick :around #'jao-erc--no-angles))
    #+end_src
*** tracking
    #+begin_src emacs-lisp
     (defun jao-erc-track-shorten (names)
       (let ((names (erc-track-shorten-names names)))
         (mapcar (lambda (n) (string-remove-prefix "#" n)) names)))

     (setq erc-track-exclude-server-buffer t
           erc-track-exclude-types '("NICK" "JOIN" "PART" "QUIT" "MODE" "KICK")
           erc-track-remove-disconnected-buffers t
           erc-track-shorten-aggressively t ;; 'max
           erc-track-shorten-function #'jao-erc-track-shorten
           erc-track-switch-direction 'importance
           erc-track-visibility nil ;; t all, nil only selected frame
           erc-track-position-in-mode-line nil
           erc-track-enable-keybindings nil ;; 'ask
           erc-track-faces-priority-list '(erc-error-face
                                           erc-current-nick-face
                                           erc-pal-face
                                           erc-direct-msg-face
                                           erc-nick-msg-face
                                           erc-default-face
                                           erc-action-face
                                           erc-notice-face))
     (defun jao-track-erc-buffers ()
       (dolist (e erc-modified-channels-alist)
         (tracking-add-buffer (car e) (list (cddr e)))))

     (with-eval-after-load "erc-track"
       (require 'tracking nil t)
       (add-hook 'exwm-workspace-switch-hook #'erc-modified-channels-update)
       (add-hook 'erc-track-list-changed-hook #'jao-track-erc-buffers))

     (jao-shorten-modes 'erc-mode)
     (jao-tracking-faces 'erc-error-face
                         'erc-pal-face
                         'erc-current-nick-face
                         'erc-direct-msg-face)
    #+end_src
*** commands (/recover &co.)
    #+begin_src emacs-lisp
     (defun erc-cmd-RECOVER (&rest ignore)
       "Recover nick"
       (let ((fn (jao--get-user/password "freenode")))
         (erc-cmd-MSG (format "nickserv IDENTIFY %s %s" (car fn) (cadr fn)))
         (erc-cmd-MSG (format "nickserv GHOST %s" (car fn)))
         (erc-cmd-MSG (format "nickserv RELEASE %s" (car fn)))
         (erc-cmd-NICK (car fn))))
    #+end_src
*** startup
    #+begin_src emacs-lisp
    (defun jao-erc (&optional yes)
      (interactive "P")
      ;; (when (or yes (y-or-n-p "Connect to bitlbee using ERC? "))
      ;;   (erc-select :server "localhost"))
      (when (or yes (y-or-n-p "Connect to libera using ERC? "))
        (erc-select :server "irc.libera.chat")))
    #+end_src
* dtache
  #+begin_src emacs-lisp
    (use-package dtache
      :ensure t
      :hook (after-init . dtache-setup))

    (use-package dtache-eshell
      :after dtache
      :hook (eshell-mode . dtache-eshell-mode))

    (use-package dtache-consult
      :after (consult dtache)
      :bind ([remap dtache-open-session] . dtache-consult-session))
  #+end_src
* cdlatex
  #+begin_src emacs-lisp
    (use-package cdlatex
      :ensure t
      :hook ((org-mode . org-cdlatex-mode))
      :diminish ((cdlatex-mode . " £")
                 (org-cdlatex-mode . " £")))
  #+end_src
* maps
  #+begin_src emacs-lisp
    (use-package osm
      :ensure t
      :init
      (with-eval-after-load 'org (require 'osm-ol))
      :config
      (transient-define-prefix jao-transient-osm ()
        ["Open Street Maps"
         ("s" "search" osm-search)
         ("g" "goto" osm-goto)
         ("b" "jump to bookmark" osm-bookmark-jump)
         ("t" "server" osm-server)])
      :bind ("C-c M" . #'jao-transient-osm))
  #+end_src