3656 lines
		
	
	
		
			140 KiB
		
	
	
	
		
			EmacsLisp
		
	
	
	
	
	
			
		
		
	
	
			3656 lines
		
	
	
		
			140 KiB
		
	
	
	
		
			EmacsLisp
		
	
	
	
	
	
;;; init.el --- Configuration entry point -*- lexical-binding: t -*-
 | 
						|
;;; Commentary:
 | 
						|
;;; Code:
 | 
						|
(require 'cl-lib)
 | 
						|
(require 'xdg)
 | 
						|
 | 
						|
;; Some other config files
 | 
						|
(cl-eval-when (compile load eval)
 | 
						|
  (add-to-list 'load-path (expand-file-name "elisp" user-emacs-directory))
 | 
						|
  (add-to-list 'load-path (expand-file-name "third-party" user-emacs-directory)))
 | 
						|
 | 
						|
;; Set package dir to follow no-littering conventions
 | 
						|
(setq package-user-dir (expand-file-name "var/elpa"
 | 
						|
                                         user-emacs-directory))
 | 
						|
 | 
						|
;; Use melpa
 | 
						|
(require 'package)
 | 
						|
(add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t)
 | 
						|
(package-initialize)
 | 
						|
 | 
						|
;; Ensure use-package is installed
 | 
						|
(unless (package-installed-p 'use-package)
 | 
						|
  (package-refresh-contents)
 | 
						|
  (package-install 'use-package))
 | 
						|
 | 
						|
;; use-package
 | 
						|
(eval-when-compile
 | 
						|
  (require 'use-package)
 | 
						|
  (setq use-package-always-ensure t
 | 
						|
        package-user-dir (expand-file-name "var/elpa"
 | 
						|
                                           user-emacs-directory)))
 | 
						|
 | 
						|
;; no-littering
 | 
						|
(use-package no-littering
 | 
						|
  :defer nil
 | 
						|
  :init
 | 
						|
  (no-littering-theme-backups)
 | 
						|
  (setq custom-file (no-littering-expand-etc-file-name "custom.el")))
 | 
						|
 | 
						|
;; load things saved with custom
 | 
						|
(load custom-file t t)
 | 
						|
 | 
						|
;; Load the local system's configuration
 | 
						|
(load (expand-file-name "local-init" user-emacs-directory) t t)
 | 
						|
 | 
						|
;; diminish
 | 
						|
(use-package diminish
 | 
						|
  :config
 | 
						|
  (diminish 'visual-line-mode)
 | 
						|
  (diminish 'abbrev-mode))
 | 
						|
 | 
						|
;; Private config loading
 | 
						|
(require 'private nil t)
 | 
						|
(defun my/get-private (key)
 | 
						|
  "Get the private config variable KEY from the private configuration file."
 | 
						|
  (alist-get key my/private-config))
 | 
						|
 | 
						|
;; basic stuff
 | 
						|
(use-package emacs
 | 
						|
  :hook (;;(emacs-lisp-mode . my/-emacs-lisp-mode-setup-evil-lookup)
 | 
						|
         ;;(prog-mode . electric-pair-local-mode)
 | 
						|
         ((text-mode tex-mode prog-mode) . auto-fill-mode)
 | 
						|
         ((text-mode tex-mode prog-mode) . my/-enable-show-trailing-whitespace)
 | 
						|
         ((tex-mode prog-mode) . kill-ring-deindent-mode))
 | 
						|
  :init
 | 
						|
  (with-eval-after-load 'find-func
 | 
						|
    (when (and (file-directory-p "~/src/emacs/src/"))
 | 
						|
      (setq find-function-C-source-directory "~/src/emacs/src/")))
 | 
						|
 | 
						|
  (defun my/-enable-show-trailing-whitespace ()
 | 
						|
    (setq-local show-trailing-whitespace t))
 | 
						|
  ;; (defun my/-emacs-lisp-mode-setup-evil-lookup ()
 | 
						|
  ;;   (setq-local evil-lookup-func
 | 
						|
  ;;               #'my/describe-symbol-at-point))
 | 
						|
  (defun my/describe-symbol-at-point ()
 | 
						|
    "Calls `describe-symbol' on the return value of `symbol-at-point'."
 | 
						|
    (interactive)
 | 
						|
    (let ((form (symbol-at-point)))
 | 
						|
      (if (consp form)
 | 
						|
          (describe-symbol (cadr form))
 | 
						|
        (describe-symbol form))))
 | 
						|
 | 
						|
  ;; Trusted buffer stuff
 | 
						|
  (defun my/temp-trust-buffer ()
 | 
						|
    "Set the current buffers local value of `trusted-content' to \\=:all."
 | 
						|
    (interactive)
 | 
						|
    (setq-local trusted-content :all)
 | 
						|
    (cond
 | 
						|
     ((and (buffer-modified-p) (y-or-n-p "Save and reload buffer?"))
 | 
						|
      (save-buffer)
 | 
						|
      (revert-buffer-quick))
 | 
						|
     ((y-or-n-p "Revert buffer?")
 | 
						|
      (revert-buffer-quick))))
 | 
						|
  (put 'trusted-content 'permanent-local t)
 | 
						|
  (defun my/-trusted-content-segment ()
 | 
						|
    (when (and (derived-mode-p 'prog-mode)
 | 
						|
               (not buffer-read-only))
 | 
						|
      (cond
 | 
						|
       ((and (local-variable-p 'trusted-content)
 | 
						|
             (equal trusted-content :all)
 | 
						|
             buffer-file-name)
 | 
						|
        (propertize "[Temp. Trusted]" 'face 'warning))
 | 
						|
       ((not (trusted-content-p))
 | 
						|
        (propertize "[Untrusted]" 'face 'error)))))
 | 
						|
  (add-to-list 'mode-line-misc-info
 | 
						|
               '(:eval (my/-trusted-content-segment)))
 | 
						|
  (defun my/-fix-trusted-content-p-for-remote (oldfun &rest args)
 | 
						|
    (let ((source (or buffer-file-truename default-directory)))
 | 
						|
      (if (or (not source) (eq trusted-content :all))
 | 
						|
          (apply oldfun (ensure-list args))
 | 
						|
        (let* ((method (file-remote-p source 'method))
 | 
						|
               (host (file-remote-p source 'host))
 | 
						|
               (trusted-content (cl-remove-if-not
 | 
						|
                                 (llama and (equal method (file-remote-p % 'method))
 | 
						|
                                        (equal host (file-remote-p % 'host)))
 | 
						|
                                 trusted-content)))
 | 
						|
          (apply oldfun args)))))
 | 
						|
  (advice-add 'trusted-content-p :around
 | 
						|
              #'my/-fix-trusted-content-p-for-remote)
 | 
						|
 | 
						|
  ;; Increase responsiveness
 | 
						|
  (setq gc-cons-threshold 80000000
 | 
						|
        inhibit-compacting-font-caches t
 | 
						|
        read-process-output-max (* 1024 1024)) ;; 1mb
 | 
						|
  (global-so-long-mode 1)
 | 
						|
 | 
						|
  ;; Terminal mouse support
 | 
						|
  (xterm-mouse-mode 1)
 | 
						|
 | 
						|
  ;; Make cursor more visible
 | 
						|
  (global-hl-line-mode 1)
 | 
						|
  (blink-cursor-mode -1)
 | 
						|
 | 
						|
  ;; Enable all disabled stuff
 | 
						|
  (setq disabled-command-function nil)
 | 
						|
 | 
						|
  ;; Stop some annoying stuff
 | 
						|
  (setq extended-command-suggest-shorter nil
 | 
						|
        suggest-key-bindings nil)
 | 
						|
 | 
						|
  ;; Better scrolling
 | 
						|
  (setq mouse-scroll-delay 0
 | 
						|
        scroll-conservatively 10
 | 
						|
        scroll-margin 2
 | 
						|
        scroll-preserve-screen-position t)
 | 
						|
 | 
						|
  ;; Make show paren instant
 | 
						|
  (setq show-paren-delay 0)
 | 
						|
  (show-paren-mode 1)
 | 
						|
 | 
						|
  ;; Display line numbers
 | 
						|
  (global-display-line-numbers-mode 1)
 | 
						|
 | 
						|
  ;; Allow the frame to be any size
 | 
						|
  (setq frame-resize-pixelwise t)
 | 
						|
 | 
						|
  ;; Don't use a gtk file picker
 | 
						|
  (setq use-file-dialog nil)
 | 
						|
 | 
						|
  ;; Make yes-or-no-p less verbose (and not use windows)
 | 
						|
  (setq use-dialog-box nil
 | 
						|
        use-short-answers t)
 | 
						|
 | 
						|
  ;; Disable startup screen
 | 
						|
  (setq inhibit-startup-screen t
 | 
						|
        server-client-instructions nil)
 | 
						|
 | 
						|
  ;; show column numbers
 | 
						|
  (column-number-mode 1)
 | 
						|
 | 
						|
  ;; Disable the menu and tool bars
 | 
						|
  (menu-bar-mode -1)
 | 
						|
  (tool-bar-mode -1)
 | 
						|
 | 
						|
  ;; No scroll bars
 | 
						|
  (scroll-bar-mode -1)
 | 
						|
 | 
						|
  ;; Visual line mode
 | 
						|
  (global-visual-line-mode 1)
 | 
						|
 | 
						|
  ;; Better line wrapping
 | 
						|
  (global-visual-wrap-prefix-mode 1)
 | 
						|
 | 
						|
  ;; Make some commands easier to enter multiple times
 | 
						|
  (repeat-mode 1)
 | 
						|
 | 
						|
  ;; Easier buffer navigation
 | 
						|
  (keymap-global-set "C-c <" #'previous-buffer)
 | 
						|
  (keymap-global-set "C-c >" #'next-buffer)
 | 
						|
  (keymap-global-set "C-c k" #'previous-buffer)
 | 
						|
  (keymap-global-set "C-c j" #'next-buffer)
 | 
						|
 | 
						|
  ;; Seems useful...
 | 
						|
  (keymap-global-set "C-c u" #'browse-url)
 | 
						|
 | 
						|
  ;; Set fonts
 | 
						|
  (add-to-list 'default-frame-alist '(font . "FiraCode Nerd Font Mono-12"))
 | 
						|
  (add-hook 'server-after-make-frame-hook
 | 
						|
            (lambda ()
 | 
						|
              (set-fontset-font t 'japanese-jisx0208 "IPAGothic")))
 | 
						|
 | 
						|
  ;; Enable color in compilation buffers
 | 
						|
  (add-hook 'compilation-filter-hook 'ansi-color-compilation-filter)
 | 
						|
 | 
						|
  ;; Some settings for programming
 | 
						|
  (setq-default indent-tabs-mode nil
 | 
						|
                tab-width 4
 | 
						|
                fill-column 80
 | 
						|
                comment-multi-line t
 | 
						|
                comment-empty-lines 'eol)
 | 
						|
  (add-to-list 'auto-mode-alist '("\\.[cC][nN][fF]\\'" . conf-mode))
 | 
						|
  (keymap-set emacs-lisp-mode-map "C-c C-r" #'eval-region)
 | 
						|
  (defun my/-fix-emacs-lisp-mode-system-files ()
 | 
						|
    (when (string-prefix-p lisp-directory buffer-file-name)
 | 
						|
      ;; system Emacs files use tab characters and look weird without this.
 | 
						|
      (setq-local tab-width 8)))
 | 
						|
  (add-hook 'emacs-lisp-mode-hook #'my/-fix-emacs-lisp-mode-system-files)
 | 
						|
 | 
						|
  ;; Tree sitter download locations
 | 
						|
  (setq treesit-language-source-alist
 | 
						|
        '((c "https://github.com/tree-sitter/tree-sitter-c")
 | 
						|
          (cpp "https://github.com/tree-sitter/tree-sitter-cpp")
 | 
						|
          (java "https://github.com/tree-sitter/tree-sitter-java")
 | 
						|
          ;; (glsl "https://github.com/tree-sitter-grammars/tree-sitter-glsl")
 | 
						|
          (python "https://github.com/tree-sitter/tree-sitter-python")
 | 
						|
          (rust "https://github.com/tree-sitter/tree-sitter-rust")
 | 
						|
          (json "https://github.com/tree-sitter/tree-sitter-json")
 | 
						|
          (yaml "https://github.com/ikatyang/tree-sitter-yaml")
 | 
						|
          (css "https://github.com/tree-sitter/tree-sitter-css")
 | 
						|
          (go "https://github.com/tree-sitter/tree-sitter-go")
 | 
						|
          (gomod "https://github.com/camdencheek/tree-sitter-go-mod")
 | 
						|
          (javascript "https://github.com/tree-sitter/tree-sitter-javascript")
 | 
						|
          (typescript "https://github.com/tree-sitter/tree-sitter-typescript"
 | 
						|
                      nil "typescript/src")
 | 
						|
          (tsx "https://github.com/tree-sitter/tree-sitter-typescript"
 | 
						|
               nil "tsx/src")
 | 
						|
          (bash "https://github.com/tree-sitter/tree-sitter-bash")
 | 
						|
          (cmake "https://github.com/uyha/tree-sitter-cmake")
 | 
						|
          (blueprint "https://github.com/huanie/tree-sitter-blueprint")
 | 
						|
          (kdl "https://github.com/tree-sitter-grammars/tree-sitter-kdl")))
 | 
						|
  ;; Tree sitter major mode conversions
 | 
						|
  (dolist (ent '((c-mode . c-ts-mode)
 | 
						|
                 (c++-mode . c++-ts-mode)
 | 
						|
                 (c-or-c++-mode . c-or-c++-ts-mode)
 | 
						|
                 (python-mode . python-ts-mode)
 | 
						|
                 (java-mode . java-ts-mode)
 | 
						|
                 (rust-mode . rust-ts-mode)
 | 
						|
                 (json-mode . json-ts-mode)
 | 
						|
                 (jsonc-mode . json-ts-mode)
 | 
						|
                 (yaml-mode . yaml-ts-mode)
 | 
						|
                 (css-mode . css-ts-mode)
 | 
						|
                 (javascript-mode . js-ts-mode)
 | 
						|
                 (cmake-mode . cmake-ts-mode)))
 | 
						|
    (add-to-list 'major-mode-remap-alist ent))
 | 
						|
  (defun my/treesit-compile-all (force)
 | 
						|
    "Compile all the modules defined in `treesit-language-source-alist'.
 | 
						|
If FORCE, recompile all modules, even ones that are already compiled.
 | 
						|
Interactively, force the recompile if called with a prefix."
 | 
						|
    (interactive "P")
 | 
						|
    (let ((did-build nil))
 | 
						|
      (dolist (lang treesit-language-source-alist)
 | 
						|
        (when (or force (not (treesit-language-available-p (car lang))))
 | 
						|
          (treesit-install-language-grammar (car lang))
 | 
						|
          (setq did-build t)))
 | 
						|
      (unless did-build
 | 
						|
        (message "All defined parsers installed!")))))
 | 
						|
 | 
						|
(use-package auth-source
 | 
						|
  :ensure nil
 | 
						|
  :custom
 | 
						|
  (auth-sources '("~/.authinfo.gpg")))
 | 
						|
 | 
						|
(setopt remote-file-name-access-timeout 10)
 | 
						|
(use-package tramp
 | 
						|
  :ensure nil
 | 
						|
  :custom
 | 
						|
  (tramp-file-name-with-method "doas")
 | 
						|
  :config
 | 
						|
  ;; (connection-local-set-profile-variables
 | 
						|
  ;;  'direct-async
 | 
						|
  ;;  '((tramp-direct-async-process . t)))
 | 
						|
  (connection-local-set-profile-variables
 | 
						|
   'error-only
 | 
						|
   '((tramp-verbose . 1)))
 | 
						|
  (connection-local-set-profile-variables
 | 
						|
   'enable-dir-locals
 | 
						|
   '((enable-remote-dir-locals . t)))
 | 
						|
  (connection-local-set-profile-variables
 | 
						|
   'apheleia-remote-local
 | 
						|
   '((apheleia-remote-algorithm . local)))
 | 
						|
  (dolist (method '("podman" "docker" "ssh" "sshx" "sudo" "su" "doas"
 | 
						|
                    "sudoedit" "run0" "kubernetes" "dockercp" "podmancp"
 | 
						|
                    "distrobox" "toolbox" "flatpak" "apptainer" "nspawn"))
 | 
						|
    (let ((inhibit-message t)
 | 
						|
          (message-log-max nil))
 | 
						|
      (tramp-enable-method (intern method)))
 | 
						|
    (connection-local-set-profiles
 | 
						|
     `(:method ,method)
 | 
						|
     ;; 'direct-async
 | 
						|
     'error-only 'enable-dir-locals
 | 
						|
     'apheleia-remote-local)))
 | 
						|
 | 
						|
(use-package midnight
 | 
						|
  :ensure nil
 | 
						|
  :config
 | 
						|
  (add-to-list 'clean-buffer-list-kill-never-buffer-names
 | 
						|
               "*mu4e-main*")
 | 
						|
  (add-to-list 'clean-buffer-list-kill-never-buffer-names
 | 
						|
               "*Async-native-compile-log*")
 | 
						|
  (add-to-list 'clean-buffer-list-kill-never-buffer-names
 | 
						|
               "*dashboard*")
 | 
						|
  (add-to-list 'clean-buffer-list-kill-never-buffer-names
 | 
						|
               "*elfeed-search*")
 | 
						|
  (midnight-mode 1))
 | 
						|
 | 
						|
(defvar my/kill-some-buffers-exclude-names
 | 
						|
  '("*mu4e-main*" "*Async-native-compile-log*" "*dashboard*" "*elfeed-search*"
 | 
						|
    "*Messages*" "*scratch*")
 | 
						|
  "List of literal buffer names that `my/kill-some-buffers' should not kill.")
 | 
						|
 | 
						|
(defun my/kill-some-buffers-excluded-buffer-p (buffer)
 | 
						|
  "Return non-nil if BUFFER should be excluded from `my/kill-some-buffers'."
 | 
						|
  (cl-find (buffer-name buffer) my/kill-some-buffers-exclude-names
 | 
						|
           :test 'equal))
 | 
						|
 | 
						|
(defun my/buffer-visible-p (buffer)
 | 
						|
  "Return non-nil if BUFFER is visible.
 | 
						|
BUFFER can be a string or a buffer."
 | 
						|
  (cond
 | 
						|
   ((stringp buffer)
 | 
						|
    (not (string-prefix-p " " buffer)))
 | 
						|
   ((bufferp buffer)
 | 
						|
    (and (stringp (buffer-name buffer))
 | 
						|
         (my/buffer-visible-p (buffer-name buffer))))
 | 
						|
   (t
 | 
						|
    (signal 'wrong-type-argument `((or bufferp stringp) ,buffer)))))
 | 
						|
 | 
						|
(defvar my/kill-some-buffers-default-pred 'my/buffer-visible-p
 | 
						|
  "Default predicate for `my/kill-some-buffers'.")
 | 
						|
 | 
						|
(defun my/kill-some-buffers-prompt-for (buffer)
 | 
						|
  "Generate a prompt for BUFFER."
 | 
						|
  (let* ((process (get-buffer-process buffer))
 | 
						|
         (process-p (and (process-live-p process)
 | 
						|
                         (not (process-query-on-exit-flag process))))
 | 
						|
         (modified-p (and (buffer-file-name buffer)
 | 
						|
                          (buffer-modified-p buffer))))
 | 
						|
    (format "Buffer \"%s\" %s.  Kill? "
 | 
						|
            (buffer-name buffer)
 | 
						|
            (cond
 | 
						|
             ((and process-p modified-p)
 | 
						|
              "HAS BEEN EDITED AND HAS A LIVE PROCESS")
 | 
						|
             (modified-p
 | 
						|
              "HAS BEEN EDITED")
 | 
						|
             (process-p
 | 
						|
              "HAS A LIVE PROCESS")
 | 
						|
             (t "is unmodified")))))
 | 
						|
 | 
						|
(cl-defun my/kill-some-buffers (&optional auto-unmod pred)
 | 
						|
  "Improved version of `kill-some-buffers'.
 | 
						|
Ask the user weather to kill each visible buffer whose name is not in
 | 
						|
`my/kill-some-buffers-exclude-names'.
 | 
						|
 | 
						|
When AUTO-UNMOD is non-nil, as it is with a prefix argument, automatically kill
 | 
						|
unmodified buffers, and then ask about the rest.
 | 
						|
 | 
						|
When PRED is non-nil, it is a function that will be run in each buffer (not just
 | 
						|
visible ones).  If it returns t, that buffer will be considered for killing.  If
 | 
						|
PRED is nil, the value of `my/kill-some-buffers-default-pred' is used."
 | 
						|
  (interactive "P")
 | 
						|
  ;; we already ask, no need to do it again
 | 
						|
  (let ((kill-buffer-query-functions nil)
 | 
						|
        (all-action (when auto-unmod 'unmod))
 | 
						|
        (had-valid-buffer)
 | 
						|
        (ask-again-buffers)
 | 
						|
        (to-kill))
 | 
						|
    (cl-flet ((ask-about (buffer allow-unmod)
 | 
						|
                (unless all-action
 | 
						|
                  (read-answer
 | 
						|
                   (my/kill-some-buffers-prompt-for buffer)
 | 
						|
                   `(("yes"    ?y "save and kill this buffer")
 | 
						|
                     ("no"     ?n "skip this buffer")
 | 
						|
                     ("all"    ?! "save and kill all remaining buffers")
 | 
						|
                     ("nosave" ?l "kill this buffer without saving")
 | 
						|
                     ,@(when allow-unmod
 | 
						|
                         '(("unmod"  ?a
 | 
						|
                            "kill unmodified buffers, ask about the rest")))
 | 
						|
                     ("quit"   ?q "exit")))))
 | 
						|
              (act-on (ans buffer allow-unmod)
 | 
						|
                (when (equal ans "all")
 | 
						|
                  (setq all-action 'all))
 | 
						|
                (when (and allow-unmod
 | 
						|
                           (equal ans "unmod"))
 | 
						|
                  (setq all-action 'unmod))
 | 
						|
                (cond
 | 
						|
                 ((and (eq all-action 'unmod)
 | 
						|
                       (buffer-file-name buffer)
 | 
						|
                       (buffer-modified-p buffer))
 | 
						|
                  (push buffer ask-again-buffers))
 | 
						|
                 ((or (eq all-action 'all)
 | 
						|
                      (eq all-action 'unmod)
 | 
						|
                      (equal ans "yes"))
 | 
						|
                  (when (buffer-file-name buffer)
 | 
						|
                    (with-current-buffer buffer
 | 
						|
                      (save-buffer)))
 | 
						|
                  (push buffer to-kill))
 | 
						|
                 ((equal ans "nosave")
 | 
						|
                  (with-current-buffer buffer
 | 
						|
                    (set-buffer-modified-p nil))
 | 
						|
                  (push buffer to-kill))
 | 
						|
                 ;; Skip buffer
 | 
						|
                 ;; ((equal ans "no"))
 | 
						|
                 ((equal ans "quit")
 | 
						|
                  (cl-return-from my/kill-some-buffers)))))
 | 
						|
      (dolist (buffer (buffer-list))
 | 
						|
        (when (and (not (my/kill-some-buffers-excluded-buffer-p buffer))
 | 
						|
                   (funcall (or pred my/kill-some-buffers-default-pred) buffer))
 | 
						|
          (setq had-valid-buffer t)
 | 
						|
          (act-on (ask-about buffer t) buffer t)))
 | 
						|
      (unless had-valid-buffer
 | 
						|
        (message "Nothing to do..."))
 | 
						|
      (setq all-action nil)
 | 
						|
      (dolist (buffer ask-again-buffers)
 | 
						|
        (act-on (ask-about buffer nil) buffer nil))
 | 
						|
      ;; Do this last so that tty frames don't auto-close half way through
 | 
						|
      (mapc 'kill-buffer to-kill))))
 | 
						|
(keymap-global-set "C-x K" 'my/kill-some-buffers)
 | 
						|
 | 
						|
(use-package tab-bar
 | 
						|
  :ensure nil
 | 
						|
  :init
 | 
						|
  (setq tab-bar-show 1
 | 
						|
        tab-bar-tab-hints t
 | 
						|
        icon-preference '(symbol text image emoji))
 | 
						|
  (tab-bar-mode 1))
 | 
						|
 | 
						|
;; jinx (better flyspell)
 | 
						|
(use-package jinx
 | 
						|
  :hook (emacs-startup . global-jinx-mode)
 | 
						|
  :config
 | 
						|
  (evil-define-key 'normal 'global
 | 
						|
    "z=" #'jinx-correct)
 | 
						|
  (defun my/jinx-visit-dictionary (language &optional other-window)
 | 
						|
    "Visit the dictionary file for LANGUAGE in another window.
 | 
						|
With OTHER-WINDOW, visit the file in another window.  Interactively, use the
 | 
						|
current buffer's language, prompting if there is more than one.  OTHER-WINDOW is
 | 
						|
t with a prefix argument."
 | 
						|
    (interactive (list
 | 
						|
                  (let ((langs (split-string jinx-languages " ")))
 | 
						|
                    (if (length= langs 1)
 | 
						|
                        (car langs)
 | 
						|
                      (completing-read "Language: " langs nil t)))
 | 
						|
                  current-prefix-arg))
 | 
						|
    (let* ((config-dir (expand-file-name "enchant" (xdg-config-home)))
 | 
						|
           (dict-path (expand-file-name (concat language ".dic") config-dir)))
 | 
						|
      (if other-window
 | 
						|
          (find-file-other-window dict-path)
 | 
						|
        (find-file dict-path)))))
 | 
						|
 | 
						|
;; recentf
 | 
						|
(use-package recentf
 | 
						|
  :init
 | 
						|
  (setq recentf-exclude `("^/tmp/.*"
 | 
						|
                          "^~/.mail/[^/]/Drafts/.*"
 | 
						|
                          ,(format "^%svar/dape-breakpoints" user-emacs-directory)
 | 
						|
                          ,(format "^%svar/elpa/.*" user-emacs-directory)
 | 
						|
                          ,(format "^%svar/elfeed/.*" user-emacs-directory)
 | 
						|
                          ,(format "^%svar/gnus/.*" user-emacs-directory)
 | 
						|
                          ,(format "^%svar/ellama-sessions/.*" user-emacs-directory)
 | 
						|
                          ,(format "^%setc/gnus/.*" user-emacs-directory)
 | 
						|
                          ,(format "^%svar/bookmark-default.el" user-emacs-directory)))
 | 
						|
  :bind ("C-c r" . recentf)
 | 
						|
  :config
 | 
						|
  (recentf-mode 1))
 | 
						|
 | 
						|
;; bookmarks
 | 
						|
(use-package bookmark
 | 
						|
  :ensure nil
 | 
						|
  :bind ("C-c B" . my/bookmark-find-file)
 | 
						|
  :config
 | 
						|
  (defun my/bookmark-find-file (&optional name)
 | 
						|
    "Run `find-file' in or on bookmark NAME.
 | 
						|
If NAME points to a directory, run `find-file' with `default-directory' in that
 | 
						|
directory. Otherwise, run `find-file' on that file."
 | 
						|
    (interactive (list (bookmark-completing-read
 | 
						|
                        "Find file in" bookmark-current-bookmark)))
 | 
						|
    (unless name
 | 
						|
      (error "No bookmark specified"))
 | 
						|
    (bookmark-maybe-historicize-string name)
 | 
						|
    (when-let ((file (bookmark-get-filename name)))
 | 
						|
      (if (file-directory-p file)
 | 
						|
          (let ((default-directory (file-name-as-directory file)))
 | 
						|
            (call-interactively 'find-file))
 | 
						|
        (find-file file)))))
 | 
						|
 | 
						|
;; kitty keyboard protocol
 | 
						|
(use-package kkp
 | 
						|
  :defer nil
 | 
						|
  :config
 | 
						|
  (global-kkp-mode 1)
 | 
						|
  (defun my/quoted-insert (arg)
 | 
						|
    "Insert the next character using read-key, not read-char."
 | 
						|
    (interactive "*p")
 | 
						|
    ;; Source: https://github.com/benjaminor/kkp/issues/11
 | 
						|
    (let ((char (read-key)))
 | 
						|
      ;; Ensure char is treated as a character code for insertion
 | 
						|
      (unless (characterp char)
 | 
						|
        (user-error "%s is not a valid character"
 | 
						|
                    (key-description (vector char))))
 | 
						|
      (when (numberp char)
 | 
						|
        (while (> arg 0)
 | 
						|
          (insert-and-inherit char)
 | 
						|
          (setq arg (1- arg))))))
 | 
						|
  (keymap-global-set "C-q" #'my/quoted-insert)
 | 
						|
  (defun my/kkp-disable-around-advice (oldfun &rest args)
 | 
						|
    "Run OLDFUN with ARGS with kkp disabled."
 | 
						|
    (let ((status (kkp--this-terminal-has-active-kkp-p)))
 | 
						|
      (unwind-protect
 | 
						|
          (progn
 | 
						|
            (when status (kkp-disable-in-terminal))
 | 
						|
            (apply oldfun args))
 | 
						|
        (when status (kkp-enable-in-terminal))
 | 
						|
        ;; consume the response from the terminal. If this is not here and this
 | 
						|
        ;; function is set as advice for `map-y-or-n-p' called from
 | 
						|
        ;; `save-buffers-kill-terminal', a bunch of extra characters will be
 | 
						|
        ;; printed for the shell after Emacs exits because Emacs will die before
 | 
						|
        ;; it can read the terminal's response to `kkp-enable-in-terminal'
 | 
						|
        (while-no-input
 | 
						|
          (sleep-for 0.1)))))
 | 
						|
  (advice-add #'map-y-or-n-p :around #'my/kkp-disable-around-advice))
 | 
						|
 | 
						|
;; some eww (status bar) stuff
 | 
						|
(defun my/cmdline-for-pid (pid)
 | 
						|
  "Return the command line arguments passed to PID.
 | 
						|
PID can be a string or a number."
 | 
						|
  (butlast (string-split
 | 
						|
            (with-temp-buffer
 | 
						|
              (insert-file-contents-literally
 | 
						|
               (format "/proc/%s/cmdline" pid))
 | 
						|
              (buffer-substring-no-properties (point-min)
 | 
						|
                                              (point-max)))
 | 
						|
            "\0")))
 | 
						|
(defun my/eww-current-config-dir ()
 | 
						|
  "Return the configuration directory for a currently running eww process."
 | 
						|
  ;; This probably only works on Linux
 | 
						|
  (catch 'found
 | 
						|
    (dolist (subdir (directory-files "/proc"))
 | 
						|
      (when (string-match-p (rx bos (+ num) eos) subdir)
 | 
						|
        (ignore-error permission-denied
 | 
						|
          (let* ((attrs (file-attributes (format "/proc/%s/exe" subdir)))
 | 
						|
                 (type (file-attribute-type attrs)))
 | 
						|
            (when (and (stringp type)
 | 
						|
                       (string-match-p (rx (or bos "/") "eww") type))
 | 
						|
              (cl-maplist (lambda (tail)
 | 
						|
                            (when (equal (car tail) "-c")
 | 
						|
                              (throw 'found (cl-second tail))))
 | 
						|
                          (my/cmdline-for-pid subdir)))))))))
 | 
						|
(defun my/eww-update-variables (&rest vars)
 | 
						|
  "Update the key value pairs in VARS.
 | 
						|
Each key should be either a symbol or a string.  Each value will have its
 | 
						|
printed representation (via `princ') set as the new value for the key."
 | 
						|
  (let* ((mappings (map-apply
 | 
						|
                    #'(lambda (key val)
 | 
						|
                        (when (symbolp key) (setq key (symbol-name key)))
 | 
						|
                        (when (cl-find ?= key)
 | 
						|
                          (error "Key cannot contain an equal sign (=): %s" key))
 | 
						|
                        (format "%s=%s" key val))
 | 
						|
                    vars))
 | 
						|
         (args (cons "update" mappings))
 | 
						|
         (cfg-dir (my/eww-current-config-dir)))
 | 
						|
    (when cfg-dir
 | 
						|
      (setq args (nconc (list "-c" cfg-dir) args)))
 | 
						|
    (apply 'call-process "eww" nil 0 nil args)))
 | 
						|
(defun my/eww-poll-variables (&rest vars)
 | 
						|
  "Poll each variable in VARS, which is a lists of strings or symbols."
 | 
						|
  (let* ((args (cons "poll" (mapcar #'(lambda (elt) (format "%s" elt)) vars)))
 | 
						|
         (cfg-dir (my/eww-current-config-dir)))
 | 
						|
    (when cfg-dir
 | 
						|
      (setq args (nconc (list "-c" cfg-dir) args)))
 | 
						|
    (apply 'call-process "eww" nil 0 nil args)))
 | 
						|
 | 
						|
;; mozc
 | 
						|
(require 'mozc nil t)
 | 
						|
(setq default-input-method "japanese-mozc")
 | 
						|
(defun my/-set-eww-fcitx-state (enabled)
 | 
						|
  "Set the fcitx state for eww to ENABLED."
 | 
						|
  (my/eww-update-variables "fcitx5-state" (if enabled 2 1)))
 | 
						|
(defun my/-update-waybar-fcitx-state ()
 | 
						|
  "Update the waybar fcitx5 state."
 | 
						|
  (call-process "pkill" nil 0 nil "-RTMIN+1" "waybar"))
 | 
						|
(defun my/global-toggle-mozc (&optional no-eww)
 | 
						|
  "Toggle mozc for all buffers.
 | 
						|
With NO-EWW, don't update eww's state."
 | 
						|
  (interactive)
 | 
						|
  (let ((default-input-method "japanese-mozc")
 | 
						|
        (default-directory "~"))
 | 
						|
    (toggle-input-method nil t)
 | 
						|
    (let ((activate (or (bound-and-true-p evil-input-method)
 | 
						|
                        current-input-method)))
 | 
						|
      (dolist (buffer (buffer-list))
 | 
						|
        (with-current-buffer buffer
 | 
						|
          (if activate
 | 
						|
              (activate-input-method activate)
 | 
						|
            (deactivate-input-method)
 | 
						|
            (mozc-mode -1)
 | 
						|
            (when (boundp 'evil-input-method)
 | 
						|
              (setq-local evil-input-method nil)))))
 | 
						|
      (unless no-eww
 | 
						|
        (my/-set-eww-fcitx-state activate)
 | 
						|
        (my/-update-waybar-fcitx-state)))
 | 
						|
    (force-mode-line-update t)))
 | 
						|
(keymap-global-set "C-\\" #'my/global-toggle-mozc)
 | 
						|
(defun my/mozc-active-in-buffer-p (&optional buffer)
 | 
						|
  "Return non-nil if mozc is active in BUFFER."
 | 
						|
  (unless buffer (setq buffer (current-buffer)))
 | 
						|
  (with-current-buffer buffer
 | 
						|
    (or (equal (or (bound-and-true-p evil-input-method) current-input-method)
 | 
						|
               "japanese-mozc")
 | 
						|
        (bound-and-true-p mozc-mode))))
 | 
						|
(defun my/-fcitx-enabled-p ()
 | 
						|
  "Return non-nil if fcitx is enabled."
 | 
						|
  (ignore-errors
 | 
						|
    (= 2 (dbus-call-method :session "org.fcitx.Fcitx5" "/controller"
 | 
						|
                           "org.fcitx.Fcitx.Controller1" "State"))))
 | 
						|
(defun my/-set-fcitx-enabled (enabled)
 | 
						|
  "If ENABLED is non-nil, enabled fcitx, otherwise disabled it."
 | 
						|
  (dbus-call-method :session "org.fcitx.Fcitx5" "/controller"
 | 
						|
                    "org.fcitx.Fcitx.Controller1"
 | 
						|
                    (if enabled "Activate" "Deactivate")))
 | 
						|
(defun my/-normalize-mozc-state ()
 | 
						|
  "Normalize the mozc state between Emacs and fcitx."
 | 
						|
  (if (cl-some 'frame-focus-state (frame-list))
 | 
						|
      (progn
 | 
						|
        (when (my/-fcitx-enabled-p)
 | 
						|
          (my/-set-fcitx-enabled nil)
 | 
						|
          (when (not (my/mozc-active-in-buffer-p))
 | 
						|
            (my/global-toggle-mozc t))))
 | 
						|
    (when (cl-some 'my/mozc-active-in-buffer-p (buffer-list))
 | 
						|
      (my/global-toggle-mozc t)
 | 
						|
      (my/-set-fcitx-enabled t)
 | 
						|
      (my/-set-eww-fcitx-state t))))
 | 
						|
(add-function :after after-focus-change-function #'my/-normalize-mozc-state)
 | 
						|
;; fix the helper process failing if mozc is built with debug flags
 | 
						|
;; (with-eval-after-load 'mozc
 | 
						|
;;   (defun my/-fixed-mozc-helper-process-filter (proc output-string)
 | 
						|
;;     ;; NOTE this is just a modified version of the origial in mozc.el
 | 
						|
;;     ;; If proc is no longer active, just throw away the data.
 | 
						|
;;     (when (eq proc mozc-helper-process)
 | 
						|
;;       (with-temp-buffer
 | 
						|
;;         (insert (concat mozc-helper-process-string-buf output-string))
 | 
						|
;;         (goto-char (point-min))
 | 
						|
;;         (unwind-protect
 | 
						|
;;             (while-let ((pos (scan-sexps (point) 1)))
 | 
						|
;;               (let* ((msg (buffer-substring-no-properties (point) pos)))
 | 
						|
;;                 (push msg mozc-helper-process-message-queue)
 | 
						|
;;                 (goto-char pos)))
 | 
						|
;;           (setq mozc-helper-process-string-buf (buffer-substring-no-properties
 | 
						|
;;                                                 (point) (point-max)))))))
 | 
						|
;;   (advice-add 'mozc-helper-process-filter :override
 | 
						|
;;               #'my/-fixed-mozc-helper-process-filter))
 | 
						|
 | 
						|
;; migemo
 | 
						|
(use-package migemo
 | 
						|
  :config
 | 
						|
  (setq migemo-dictionary "/usr/share/migemo/utf-8/migemo-dict"
 | 
						|
        migemo-coding-system 'utf-8-unix
 | 
						|
        migemo-regex-dictionary nil
 | 
						|
        migemo-user-dictionary nil
 | 
						|
        migemo-isearch-enable-p t)
 | 
						|
  (keymap-global-set "C-c w" 'isearch-forward)
 | 
						|
  (keymap-global-set "C-c W" 'isearch-backward)
 | 
						|
  (migemo-init))
 | 
						|
 | 
						|
;; undo-tree
 | 
						|
(use-package undo-tree
 | 
						|
  :defer nil
 | 
						|
  :hook (undo-tree-visualizer-mode . my/-undo-tree-visualizer-mode-setup)
 | 
						|
  :config
 | 
						|
  (defun my/-undo-tree-visualizer-mode-setup ()
 | 
						|
    (visual-line-mode -1)
 | 
						|
    (setq truncate-lines t))
 | 
						|
  (global-undo-tree-mode))
 | 
						|
 | 
						|
;; evil
 | 
						|
(use-package evil
 | 
						|
  :init
 | 
						|
  (setq evil-want-integration t
 | 
						|
        evil-want-C-d-scroll nil
 | 
						|
        evil-want-keybinding nil
 | 
						|
        evil-undo-system 'undo-tree
 | 
						|
        evil-search-module 'isearch
 | 
						|
        evil-respect-visual-line-mode t)
 | 
						|
  :config
 | 
						|
  (evil-mode 1)
 | 
						|
  (evil-define-key '(normal visual motion) proced-mode-map
 | 
						|
    "u" #'proced-unmark)
 | 
						|
  (evil-define-key '(normal visual motion) dired-mode-map
 | 
						|
    "u" #'dired-unmark)
 | 
						|
  (evil-define-key '(normal visual motion) profiler-report-mode-map
 | 
						|
    (kbd "TAB") #'profiler-report-toggle-entry)
 | 
						|
  (eldoc-add-command 'evil-insert
 | 
						|
                     'evil-append
 | 
						|
                     'evil-insert-line
 | 
						|
                     'evil-append-line))
 | 
						|
(use-package evil-collection
 | 
						|
  :after evil
 | 
						|
  :diminish evil-collection-unimpaired-mode
 | 
						|
  :config
 | 
						|
  (evil-collection-init))
 | 
						|
(use-package evil-surround
 | 
						|
  :after evil
 | 
						|
  :config
 | 
						|
  (evil-define-key 'operator evil-surround-mode-map
 | 
						|
    "z" #'evil-surround-edit
 | 
						|
    "Z" #'evil-Surround-edit)
 | 
						|
  (evil-define-key 'visual evil-surround-mode-map
 | 
						|
    "gz" #'evil-surround-region
 | 
						|
    "gZ" #'evil-Surround-region)
 | 
						|
  (global-evil-surround-mode 1))
 | 
						|
(use-package evil-terminal-cursor-changer
 | 
						|
  :after evil
 | 
						|
  :config
 | 
						|
  (evil-terminal-cursor-changer-activate))
 | 
						|
(use-package evil-numbers
 | 
						|
  :after evil
 | 
						|
  :bind (("C-c =" . evil-numbers/inc-at-pt)
 | 
						|
         ("C-c +" . evil-numbers/inc-at-pt)
 | 
						|
         ("C-c -" . evil-numbers/dec-at-pt)
 | 
						|
         ("C-c C-=" . evil-numbers/inc-at-pt-incremental)
 | 
						|
         ("C-c C-+" . evil-numbers/inc-at-pt-incremental)
 | 
						|
         ("C-c C--" . evil-numbers/dec-at-pt-incremental)))
 | 
						|
(use-package evil-cleverparens
 | 
						|
  :hook ((prog-mode . my/-enable-evil-cleverparens)
 | 
						|
         (evil-cleverparens-mode . paredit-mode))
 | 
						|
  :bind (:map paredit-mode-map
 | 
						|
         ("C-<return>" . paredit-RET)
 | 
						|
         ("C-RET" . paredit-RET))
 | 
						|
  :custom
 | 
						|
  (evil-cleverparens-use-s-and-S nil)
 | 
						|
  (evil-cleverparens-complete-parens-in-yanked-region t)
 | 
						|
  :config
 | 
						|
  (eldoc-add-command 'paredit-RET
 | 
						|
                     'paredit-open-round
 | 
						|
                     'paredit-open-angled
 | 
						|
                     'paredit-open-bracket
 | 
						|
                     'paredit-open-angled
 | 
						|
                     'paredit-open-parenthesis
 | 
						|
                     'delete-indentation
 | 
						|
                     'evil-cp-insert
 | 
						|
                     'evil-cp-append
 | 
						|
                     'evil-cp-insert-at-beginning-of-form
 | 
						|
                     'evil-cp-insert-at-end-of-form)
 | 
						|
  (keymap-unset evil-cleverparens-mode-map "<normal-state> M-o" t)
 | 
						|
  (defun my/-enable-evil-cleverparens ()
 | 
						|
    (if (derived-mode-p 'lisp-data-mode)
 | 
						|
        (evil-cleverparens-mode 1)
 | 
						|
      (electric-pair-local-mode 1)))
 | 
						|
  (cl-defun my/range-inside-thing-p (thing beg end &optional no-edge)
 | 
						|
    "Return non-nil if BEG and END fall inside the bounds of THING.
 | 
						|
With NO-EDGE, return nil if beg or end fall on the edge of the range."
 | 
						|
    (save-excursion
 | 
						|
      ;; this fixes that fact that `thing-at-point-bounds-of-string-at-point'
 | 
						|
      ;; errors if called at the end of the buffer
 | 
						|
      (condition-case nil
 | 
						|
          (let ((sb (progn (goto-char beg) (bounds-of-thing-at-point thing)))
 | 
						|
                (eb (progn (goto-char end) (bounds-of-thing-at-point thing))))
 | 
						|
            (and sb eb (equal sb eb)
 | 
						|
                 (or (not no-edge)
 | 
						|
                     (and (/= beg (car sb))
 | 
						|
                          (< beg (cdr sb))
 | 
						|
                          (/= end (car sb))
 | 
						|
                          (< end (cdr sb))))))
 | 
						|
        ;; if the error happens, we aren't in a string
 | 
						|
        (wrong-type-argument nil))))
 | 
						|
  (defun my/-evil-cp-region-ok-p-no-string (oldfun beg end)
 | 
						|
    (or
 | 
						|
     (and (sp-point-in-comment beg)
 | 
						|
          (sp-point-in-comment end))
 | 
						|
     (and (sp-point-in-string beg)
 | 
						|
          (sp-point-in-string end)
 | 
						|
          (my/range-inside-thing-p 'string beg end t))
 | 
						|
     (funcall oldfun beg end)))
 | 
						|
  (defun my/column-num-at-pos (pos)
 | 
						|
    "Return the column number at POS."
 | 
						|
    (save-excursion
 | 
						|
      (goto-char pos)
 | 
						|
      (current-column)))
 | 
						|
  (defun my/-evil-cp-block-ok-p-no-string (oldfun beg end)
 | 
						|
    (when (> beg end) (cl-rotatef beg end))
 | 
						|
    (or
 | 
						|
     (save-excursion
 | 
						|
       (goto-char beg)
 | 
						|
       (let ((start-off (current-column))
 | 
						|
             (end-off (my/column-num-at-pos end)))
 | 
						|
         (cl-block nil
 | 
						|
           (dotimes (_ (count-lines beg end) t)
 | 
						|
             (let ((bol (pos-bol)))
 | 
						|
               (unless (sp-region-ok-p (+ bol start-off)
 | 
						|
                                       (+ bol end-off))
 | 
						|
                 (cl-return))
 | 
						|
               (forward-line))))))
 | 
						|
     (funcall oldfun beg end)))
 | 
						|
  (advice-add 'sp-region-ok-p :around 'my/-evil-cp-region-ok-p-no-string)
 | 
						|
  (advice-add 'evil-cp--balanced-block-p :around 'my/-evil-cp-block-ok-p-no-string))
 | 
						|
 | 
						|
;; better lisp editing
 | 
						|
(use-package adjust-parens
 | 
						|
  :hook (lisp-data-mode . adjust-parens-mode)
 | 
						|
  :config
 | 
						|
  (defun my/lisp-indent-adjust-parens ()
 | 
						|
    "Like `lisp-indent-adjust-parens', but got to first char on line first.
 | 
						|
Also, this works even if the region is active (it indents every line in the
 | 
						|
region)."
 | 
						|
    (interactive)
 | 
						|
    (save-mark-and-excursion
 | 
						|
      (let ((end (mark t))
 | 
						|
            (line-count 1)
 | 
						|
            (indent-cols))
 | 
						|
        (when (and (region-active-p) end)
 | 
						|
          (setq mark-active nil
 | 
						|
                line-count (count-lines (point) end))
 | 
						|
          (when (> (point) end)
 | 
						|
            (let ((start (point)))
 | 
						|
              (goto-char end)
 | 
						|
              (setq end start))))
 | 
						|
        ;; find the indentation column of each line
 | 
						|
        (save-excursion
 | 
						|
          (dotimes (_ line-count)
 | 
						|
            (back-to-indentation)
 | 
						|
            (push (- (point) (pos-bol)) indent-cols)
 | 
						|
            (forward-line))
 | 
						|
          (cl-callf nreverse indent-cols))
 | 
						|
        (cl-loop repeat line-count
 | 
						|
                 for indent-col in indent-cols
 | 
						|
                 for bol = (pos-bol)
 | 
						|
                 do (back-to-indentation)
 | 
						|
                 ;; skip this line if the indentation has changed
 | 
						|
                 when (= (- (point) bol) indent-col) do
 | 
						|
                 (lisp-indent-adjust-parens)
 | 
						|
                 ;; if the indent failed, stop
 | 
						|
                 (when (= (- (point) bol) indent-col)
 | 
						|
                   (cl-return))
 | 
						|
                 do (forward-line)))))
 | 
						|
  (defun my/lisp-dedent-adjust-parens ()
 | 
						|
    "Like `lisp-dedent-adjust-parens', but got to first char on line first.
 | 
						|
Also, this works even if the region is active (it just jumps to the first line
 | 
						|
in the region and indents once)."
 | 
						|
    (interactive)
 | 
						|
    (save-mark-and-excursion
 | 
						|
      (let ((end (mark t)))
 | 
						|
        (when (and (region-active-p) end)
 | 
						|
          (setq mark-active nil)
 | 
						|
          (when (> (point) end)
 | 
						|
            (goto-char end))))
 | 
						|
      (back-to-indentation)
 | 
						|
      (lisp-dedent-adjust-parens)))
 | 
						|
  (eldoc-add-command 'my/lisp-indent-adjust-parens
 | 
						|
                     'my/lisp-dedent-adjust-parens
 | 
						|
                     'lisp-indent-adjust-parens
 | 
						|
                     'lisp-dedent-adjust-parens)
 | 
						|
  (evil-define-key '(normal visual) adjust-parens-mode-map
 | 
						|
    (kbd "<tab>") #'my/lisp-indent-adjust-parens
 | 
						|
    (kbd "<backtab>") #'my/lisp-dedent-adjust-parens
 | 
						|
    (kbd "C-c C-i") #'my/lisp-indent-adjust-parens
 | 
						|
    (kbd "C-c S-<tab>") #'my/lisp-dedent-adjust-parens))
 | 
						|
 | 
						|
;; for when the files are just too large
 | 
						|
(use-package vlf
 | 
						|
  :demand t
 | 
						|
  :config
 | 
						|
  (require 'vlf-setup))
 | 
						|
 | 
						|
;; allow copy from terminal
 | 
						|
(use-package xclip
 | 
						|
  :config
 | 
						|
  (setq xclip-method 'wl-copy
 | 
						|
        xclip-program (symbol-name xclip-method))
 | 
						|
  (xclip-mode 1)
 | 
						|
  (defun my/-xclip-detect-wl-paste-error (oldfun type)
 | 
						|
    (if (eq xclip-method 'wl-copy)
 | 
						|
        ;; Direct from `xclip-get-selection'
 | 
						|
        (when (and (getenv "WAYLAND_DISPLAY")
 | 
						|
                   (memq type '(clipboard CLIPBOARD primary PRIMARY)))
 | 
						|
          (let* ((exit-code 0)
 | 
						|
                 (output
 | 
						|
                  (with-output-to-string
 | 
						|
                    (setq exit-code
 | 
						|
                          (apply #'call-process (replace-regexp-in-string
 | 
						|
                                                 "\\(.*\\)copy" "\\1paste"
 | 
						|
                                                 xclip-program 'fixedcase)
 | 
						|
                                 nil standard-output nil
 | 
						|
                                 "-n" (if (memq type '(primary PRIMARY))
 | 
						|
                                          '("-p")))))))
 | 
						|
            (if (zerop exit-code)
 | 
						|
                output
 | 
						|
              "")))
 | 
						|
      (funcall oldfun type)))
 | 
						|
  (advice-add 'xclip-get-selection :around 'my/-xclip-detect-wl-paste-error))
 | 
						|
 | 
						|
;; Set the WAYLAND_DISPLAY environment variable
 | 
						|
(require 'xdg)
 | 
						|
(defun my/detect-wayland-display ()
 | 
						|
  "Try to set the WAYLAND_DISPLAY environment variable.
 | 
						|
This attempts to detect a running Wayland session and set the WAYLAND_DISPLAY
 | 
						|
environment variable accordingly."
 | 
						|
  (let ((found '(nil . nil)))
 | 
						|
    (dolist (entry (directory-files-and-attributes
 | 
						|
                    (xdg-runtime-dir) nil nil 'nosort)
 | 
						|
                   (cdr found))
 | 
						|
      (cl-destructuring-bind (name . attrs) entry
 | 
						|
        (when-let (((string-match (rx bos "wayland-" (group (+ (any "0-9"))) eos)
 | 
						|
                                  name))
 | 
						|
                   (id (string-to-number (match-string 1 name)))
 | 
						|
                   ((or (not (car found)) (< id (car found))))
 | 
						|
 | 
						|
                   ;; socket
 | 
						|
                   ((string-prefix-p "s" (file-attribute-modes attrs))))
 | 
						|
          (setq found (cons id name)))))))
 | 
						|
(unless (getenv "WAYLAND_DISPLAY")
 | 
						|
  (setenv "WAYLAND_DISPLAY" (my/detect-wayland-display)))
 | 
						|
 | 
						|
;; which-key
 | 
						|
(use-package which-key
 | 
						|
  :diminish which-key-mode
 | 
						|
  :config
 | 
						|
  (which-key-mode 1))
 | 
						|
 | 
						|
;; avy
 | 
						|
(use-package avy
 | 
						|
  :bind (("C-c C-j" . avy-resume)
 | 
						|
         ("M-s s" . evil-avy-goto-char-2)
 | 
						|
         ("M-s S" . evil-avy-goto-line))
 | 
						|
  :init
 | 
						|
  (define-minor-mode my/evil-avy-mode
 | 
						|
    "A minor mode for binding avy commands to s and S in evil's normal and
 | 
						|
visual states."
 | 
						|
    :keymap (make-sparse-keymap))
 | 
						|
  (evil-define-key '(normal visual operator motion) my/evil-avy-mode-map
 | 
						|
    "s" #'evil-avy-goto-char-2
 | 
						|
    "S" #'evil-avy-goto-line)
 | 
						|
  (define-globalized-minor-mode my/evil-avy-global-mode my/evil-avy-mode
 | 
						|
    (lambda () (my/evil-avy-mode 1))
 | 
						|
    :predicate '((not magit-mode dired-mode
 | 
						|
                      proced-mode mu4e-main-mode
 | 
						|
                      mu4e-view-mode mu4e-headers-mode
 | 
						|
                      ibuffer-mode calc-mode calc-trail-mode
 | 
						|
                      gnus-group-mode) t))
 | 
						|
  (my/evil-avy-global-mode 1)
 | 
						|
  :config
 | 
						|
  (avy-setup-default))
 | 
						|
 | 
						|
;; better `replace-regexp'
 | 
						|
(use-package visual-regexp
 | 
						|
  :bind (("C-c q" . vr/replace)
 | 
						|
         ("C-M-%" . vr/query-replace))
 | 
						|
  :init
 | 
						|
  (let ((val minibuffer-regexp-prompts))
 | 
						|
    (cl-pushnew "Replace" val :test 'equal)
 | 
						|
    (setopt minibuffer-regexp-prompts val)))
 | 
						|
 | 
						|
;; better `align-regexp'
 | 
						|
(use-package ialign
 | 
						|
  :defer t
 | 
						|
  :custom
 | 
						|
  (ialign-initial-repeat t))
 | 
						|
 | 
						|
;; ace-window
 | 
						|
(use-package ace-window
 | 
						|
  :diminish ace-window-mode
 | 
						|
  :bind ("M-o" . ace-window)
 | 
						|
  :init
 | 
						|
  (setq aw-scope 'frame
 | 
						|
        aw-minibuffer-flag t))
 | 
						|
 | 
						|
;; savehist
 | 
						|
(use-package savehist
 | 
						|
  :config
 | 
						|
  (savehist-mode 1))
 | 
						|
 | 
						|
;; vertico
 | 
						|
(use-package vertico
 | 
						|
  :bind (:map vertico-map
 | 
						|
         ("C-RET" . vertico-exit-input)
 | 
						|
         ("C-<return>" . vertico-exit-input)
 | 
						|
         ("C-S-k" . kill-line)
 | 
						|
         ("C-k" . vertico-previous)
 | 
						|
         ("C-j" . vertico-next)
 | 
						|
         ("RET" . vertico-directory-enter)
 | 
						|
         ("DEL" . vertico-directory-delete-char)
 | 
						|
         ("M-DEL" . vertico-directory-delete-word))
 | 
						|
  :hook (minibuffer-setup . cursor-intangible-mode)
 | 
						|
  :init
 | 
						|
  (defun my/crm-indicator (args)
 | 
						|
    (cons (format "[CRM%s] %s"
 | 
						|
                  (replace-regexp-in-string
 | 
						|
                   "\\`\\[.*?]\\*\\|\\[.*?]\\*\\'" ""
 | 
						|
                   crm-separator)
 | 
						|
                  (car args))
 | 
						|
          (cdr args)))
 | 
						|
  (advice-add #'completing-read-multiple :filter-args #'my/crm-indicator)
 | 
						|
  (setq vertico-cycle t
 | 
						|
        enable-recursive-minibuffers t
 | 
						|
        ;; read-extended-command-predicate #'command-completion-default-include-p
 | 
						|
        read-extended-command-predicate nil
 | 
						|
        minibuffer-prompt-properties '(read-only t ;; noindent 3
 | 
						|
                                                 cursor-intangible t
 | 
						|
                                                 face minibuffer-prompt))
 | 
						|
  (vertico-mode 1)
 | 
						|
  ;; for jinx
 | 
						|
  (require 'vertico-multiform)
 | 
						|
  (add-to-list 'vertico-multiform-categories
 | 
						|
               '(jinx grid (vertico-grid-annotate . 20)))
 | 
						|
  (vertico-multiform-mode 1))
 | 
						|
 | 
						|
;; orderless
 | 
						|
(use-package orderless
 | 
						|
  :autoload orderless-define-completion-style
 | 
						|
  :hook (text-mode . my/-setup-text-mode-completion-styles)
 | 
						|
  :init
 | 
						|
  (defun my/-setup-text-mode-completion-styles ()
 | 
						|
    (setq-local completion-styles '(basic)))
 | 
						|
  (orderless-define-completion-style my/orderless-with-initialism
 | 
						|
    (orderless-matching-styles '(orderless-initialism orderless-regexp)))
 | 
						|
  (setq orderless-matching-styles '(orderless-regexp)
 | 
						|
        completion-styles '(orderless basic)
 | 
						|
        completion-category-defaults nil
 | 
						|
        completion-category-overrides '((file
 | 
						|
                                         (styles basic partial-completion))
 | 
						|
                                        (command
 | 
						|
                                         (styles my/orderless-with-initialism
 | 
						|
                                                 basic)))))
 | 
						|
 | 
						|
;; marginalia
 | 
						|
(use-package marginalia
 | 
						|
  :bind (:map minibuffer-local-map
 | 
						|
         ("M-a" . marginalia-cycle))
 | 
						|
  :init
 | 
						|
  (marginalia-mode 1))
 | 
						|
 | 
						|
;; embark
 | 
						|
(use-package embark
 | 
						|
  :bind (("C-," . embark-act)
 | 
						|
         ("C-;" . embark-dwim)
 | 
						|
         :map help-map
 | 
						|
         ("B" . embark-bindings)
 | 
						|
         :map embark-symbol-map
 | 
						|
         ("h" . helpful-symbol)
 | 
						|
         :map embark-become-file+buffer-map
 | 
						|
         ("b" . consult-buffer)
 | 
						|
         ("B" . switch-to-buffer))
 | 
						|
  :init
 | 
						|
  (setq embark-quit-after-action nil
 | 
						|
        embark-indicators '(embark-minimal-indicator
 | 
						|
                            embark-isearch-highlight-indicator
 | 
						|
                            embark-highlight-indicator))
 | 
						|
  :config
 | 
						|
  (defvar-keymap my/embark-string-map
 | 
						|
    :doc "Keymap for Embark string actions."
 | 
						|
    :parent embark-expression-map
 | 
						|
    "R" 'repunctuate-sentences)
 | 
						|
  (defun my/embark-target-string ()
 | 
						|
    "Target the string at point."
 | 
						|
    (if-let (((not (eobp))) ; prevent next line from causing errors
 | 
						|
             (bounds (bounds-of-thing-at-point 'string)))
 | 
						|
        (append (list 'string (buffer-substring-no-properties (car bounds)
 | 
						|
                                                              (cdr bounds)))
 | 
						|
                bounds)))
 | 
						|
  (add-to-list 'embark-around-action-hooks
 | 
						|
               '(repunctuate-sentences embark--mark-target))
 | 
						|
  (add-to-list 'embark-keymap-alist
 | 
						|
               '(string my/embark-string-map))
 | 
						|
  (add-to-list 'embark-target-finders 'my/embark-target-string)
 | 
						|
  (add-to-list 'display-buffer-alist
 | 
						|
               '("\\`\\*Embark Collect \\(Live\\|Completions\\)\\*"
 | 
						|
                 nil
 | 
						|
                 (window-parameters (mode-line-format . none))))
 | 
						|
  (evil-define-key '(normal motion) org-mode-map
 | 
						|
    (kbd "C-,") #'embark-act))
 | 
						|
 | 
						|
;; consult
 | 
						|
(use-package consult
 | 
						|
  :bind (("C-s" . consult-line)
 | 
						|
         ("C-x b" . consult-buffer)
 | 
						|
         ("C-S-s" . consult-ripgrep)
 | 
						|
         ("C-x C-S-f" . consult-fd)
 | 
						|
         ("C-x c k" . consult-keep-lines)
 | 
						|
         ("C-x c f" . consult-focus-lines)
 | 
						|
         ("C-x c r" . consult-recent-file)
 | 
						|
         ("C-x c b" . consult-bookmark)
 | 
						|
         ("C-x c d" . consult-fd)
 | 
						|
         ("C-x c h" . consult-history)
 | 
						|
         ("C-c p" . consult-fd)
 | 
						|
         ("C-x c g" . consult-ripgrep)
 | 
						|
         ("C-c P" . consult-ripgrep)
 | 
						|
         ("C-x c y" . consult-yank-from-kill-ring)
 | 
						|
         ("M-g i" . consult-imenu)
 | 
						|
         ("M-g I" . consult-imenu-multi)
 | 
						|
         ("M-g r" . consult-imenu-multi)
 | 
						|
         :map help-map
 | 
						|
         ("TAB". consult-info)
 | 
						|
         ("C-S-m" . consult-man))
 | 
						|
  :hook (minibuffer-setup . my/consult-setup-minibuffer-completion)
 | 
						|
  :init
 | 
						|
  (defun my/consult-setup-minibuffer-completion ()
 | 
						|
    (setq-local completion-in-region-function #'consult-completion-in-region))
 | 
						|
  (evil-declare-motion #'consult-line))
 | 
						|
(use-package consult-eglot
 | 
						|
  :commands consult-eglot-symbols)
 | 
						|
 | 
						|
;; wgrep
 | 
						|
(use-package wgrep)
 | 
						|
 | 
						|
;; integration for embark and consult
 | 
						|
(use-package embark-consult
 | 
						|
  :hook (embark-collect-mode . consult-preview-at-point-mode))
 | 
						|
 | 
						|
(use-package completion-preview
 | 
						|
  :ensure nil
 | 
						|
  :defer nil
 | 
						|
  :bind (:map completion-preview-active-mode-map
 | 
						|
         ("M-i" . completion-preview-insert)
 | 
						|
         ("M-S-i" . completion-preview-complete)
 | 
						|
         ("M-p" . completion-preview-prev-candidate)
 | 
						|
         ("M-n" . completion-preview-next-candidate))
 | 
						|
  :config
 | 
						|
  (add-to-list
 | 
						|
   'emulation-mode-map-alists
 | 
						|
   `((completion-preview-active-mode . ,completion-preview-active-mode-map)))
 | 
						|
  (global-completion-preview-mode 1))
 | 
						|
 | 
						|
;; corfu (autocomplete)
 | 
						|
(use-package corfu
 | 
						|
  :bind (("M-<tab>" . completion-at-point)
 | 
						|
         :map corfu-map
 | 
						|
         ("C-j" . corfu-next)
 | 
						|
         ("C-k" . corfu-previous)
 | 
						|
         ("M-SPC" . corfu-insert-separator)
 | 
						|
         ("M-m" . my/corfu-move-to-minibuffer))
 | 
						|
  :init
 | 
						|
  (defun my/corfu-move-to-minibuffer ()
 | 
						|
    (interactive)
 | 
						|
    (when completion-in-region--data
 | 
						|
      (let ((completion-extra-properties corfu--extra)
 | 
						|
            (completion-cycle-threshold completion-cycling))
 | 
						|
        (apply #'consult-completion-in-region completion-in-region--data))))
 | 
						|
  (setq corfu-cycle t
 | 
						|
        corfu-auto nil
 | 
						|
        corfu-on-exact-match nil
 | 
						|
        corfu-popupinfo-delay '(1.0 . 0.5)
 | 
						|
        completion-cycle-threshold nil
 | 
						|
        global-corfu-minibuffer
 | 
						|
        ;; only enable corfu in the minibuffer in graphical frames
 | 
						|
        (lambda ()
 | 
						|
          (and (display-graphic-p)
 | 
						|
               (not (eq (current-local-map)
 | 
						|
                        read-passwd-map)))))
 | 
						|
  (global-corfu-mode 1)
 | 
						|
  (corfu-popupinfo-mode 1)
 | 
						|
  :config
 | 
						|
  (add-to-list 'corfu-continue-commands #'my/corfu-move-to-minibuffer)
 | 
						|
  (defun my/help-buffer-exists-p ()
 | 
						|
    "Return if the buffer that `help-buffer' would, or nil if it doesn't exist."
 | 
						|
    (or (and help-xref-following (derived-mode-p 'help-mode))
 | 
						|
        (get-buffer "*Help*")))
 | 
						|
  (defun my/-corfu-popupinfo-close-help-buffer (oldfun &rest args)
 | 
						|
    (if (derived-mode-p 'emacs-lisp-mode)
 | 
						|
        (let ((help-buf (my/help-buffer-exists-p)))
 | 
						|
          (prog1
 | 
						|
              (apply oldfun args)
 | 
						|
            (when-let (((not help-buf))
 | 
						|
                       (buf (help-buffer)))
 | 
						|
              ;; Ensure that, even if `help-buffer' returns nil in the future, we
 | 
						|
              ;; don't kill the current buffer
 | 
						|
              (kill-buffer buf))))
 | 
						|
      (apply oldfun args)))
 | 
						|
  (advice-add 'corfu-popupinfo--get-documentation :around
 | 
						|
              'my/-corfu-popupinfo-close-help-buffer))
 | 
						|
(use-package corfu-terminal
 | 
						|
  :init
 | 
						|
  (corfu-terminal-mode 1)
 | 
						|
  :config
 | 
						|
  (require 'corfu-terminal-popupinfo)
 | 
						|
  (corfu-terminal-popupinfo-mode 1))
 | 
						|
 | 
						|
(use-package dabbrev
 | 
						|
  :ensure nil
 | 
						|
  :config
 | 
						|
  (add-to-list 'dabbrev-ignored-buffer-regexps "\\` ")
 | 
						|
  (add-to-list 'dabbrev-ignored-buffer-modes 'doc-view-mode)
 | 
						|
  (add-to-list 'dabbrev-ignored-buffer-modes 'pdf-view-mode)
 | 
						|
  (add-to-list 'dabbrev-ignored-buffer-modes 'tags-table-mode))
 | 
						|
 | 
						|
;; cape (a bunch of capfs!)
 | 
						|
(use-package cape
 | 
						|
  :bind (([remap dabbrev-expand] . cape-dabbrev)
 | 
						|
         ("C-c P" . cape-line)
 | 
						|
         ("C-c f" . cape-file))
 | 
						|
  :hook (text-mode . my/-cape-setup-text-mode)
 | 
						|
  :init
 | 
						|
  (defun my/-cape-setup-text-mode ()
 | 
						|
    ;; Only run this if we are not in `TeX-mode'
 | 
						|
    (unless (bound-and-true-p TeX-mode-p)
 | 
						|
      (setq-local completion-at-point-functions
 | 
						|
                  (append completion-at-point-functions (list 'cape-dict
 | 
						|
                                                              'cape-dabbrev))
 | 
						|
                  corfu-auto nil))))
 | 
						|
 | 
						|
;; xref
 | 
						|
(use-package xref
 | 
						|
  :init
 | 
						|
  (evil-define-key '(normal motion) 'global
 | 
						|
    "gr" #'xref-find-references)
 | 
						|
  (setq xref-show-xrefs-function #'consult-xref
 | 
						|
        xref-show-definitions-function #'consult-xref))
 | 
						|
 | 
						|
;; popup.el
 | 
						|
(use-package popup)
 | 
						|
 | 
						|
;; posframe
 | 
						|
(use-package posframe
 | 
						|
  :init
 | 
						|
  (defun my/posframe-tip (name msg)
 | 
						|
    "Like `popup-tip', but with a posframe.
 | 
						|
NAME should be the buffer name to pass to `posframe-show'. MSG is the message to
 | 
						|
display."
 | 
						|
    (unwind-protect
 | 
						|
        (progn
 | 
						|
          (posframe-show name
 | 
						|
                         :string msg
 | 
						|
                         :position (point)
 | 
						|
                         :max-width 80
 | 
						|
                         :border-width 2
 | 
						|
                         :border-color "white")
 | 
						|
          (clear-this-command-keys)
 | 
						|
          (push (read-event) unread-command-events)
 | 
						|
          (posframe-hide name))
 | 
						|
      (posframe-hide name))))
 | 
						|
 | 
						|
(defun my/floating-tooltip (name msg)
 | 
						|
  "If `display-graphic-p', call `my/posframe-tip', otherwise `popup-tip'.
 | 
						|
MSG is the message to show in the popup.  NAME is the name of the buffer to pass
 | 
						|
to `posframe-show' if the display is graphical."
 | 
						|
  (if (display-graphic-p)
 | 
						|
      (my/posframe-tip name msg)
 | 
						|
    (popup-tip msg)))
 | 
						|
 | 
						|
;; flymake
 | 
						|
(use-package flymake
 | 
						|
  :config
 | 
						|
  (require 'consult-flymake)
 | 
						|
  :custom
 | 
						|
  (flymake-indicator-type 'margins))
 | 
						|
 | 
						|
;; flycheck
 | 
						|
(use-package flycheck
 | 
						|
  :hook ((sh-mode emacs-lisp-mode) . my/flycheck-if-trusted)
 | 
						|
  :custom
 | 
						|
  (flycheck-indication-mode 'left-margin)
 | 
						|
  :init
 | 
						|
  (setq flycheck-display-errors-function nil)
 | 
						|
  (defun my/flycheck-if-trusted ()
 | 
						|
    (when (trusted-content-p)
 | 
						|
      (flycheck-mode))))
 | 
						|
(use-package consult-flycheck)
 | 
						|
 | 
						|
(defun my/sly-notes-at-point (&optional pos buffer)
 | 
						|
  "Return the sly notes at POS in BUFFER.
 | 
						|
If BUFFER is nil, the current buffer is used."
 | 
						|
  (with-current-buffer (or buffer (current-buffer))
 | 
						|
    (unless pos
 | 
						|
      (setq pos (point)))
 | 
						|
    (cl-loop for overlay in (overlays-at pos)
 | 
						|
             for note = (overlay-get overlay 'sly-note)
 | 
						|
             when note
 | 
						|
             collect note)))
 | 
						|
 | 
						|
(defun my/diagnostic-at-point ()
 | 
						|
  "Show the diagnostics under point."
 | 
						|
  (interactive)
 | 
						|
  (let ((message))
 | 
						|
    (when-let (((bound-and-true-p flymake-mode))
 | 
						|
               (diag (get-char-property (point) 'flymake-diagnostic)))
 | 
						|
      (cl-callf nconc message (string-split (flymake--diag-text diag) "\n" t)))
 | 
						|
    (when (bound-and-true-p flycheck-mode)
 | 
						|
      (cl-callf nconc message
 | 
						|
        (mapcar 'flycheck-error-message (flycheck-overlay-errors-at (point)))))
 | 
						|
    ;; sly (lazy-loaded)
 | 
						|
    (when (featurep 'sly)
 | 
						|
      (cl-callf nconc message (mapcar (lambda (note)
 | 
						|
                                        (plist-get note :message))
 | 
						|
                                      (my/sly-notes-at-point))))
 | 
						|
    ;; jinx
 | 
						|
    (when-let (((bound-and-true-p jinx-mode))
 | 
						|
               (jinx-msg (jinx--get-overlays (point) (1+ (point)))))
 | 
						|
      (push "misspelled word" message))
 | 
						|
    (when message
 | 
						|
      (my/floating-tooltip " *my-diagnostic-posframe*"
 | 
						|
                           (mapconcat (lambda (msg)
 | 
						|
                                        (concat "•" msg))
 | 
						|
                                      message "\n")))))
 | 
						|
 | 
						|
(defconst my/consult-flymake-flycheck-narrow
 | 
						|
  '((?e . "Error")
 | 
						|
    (?w . "Warning")
 | 
						|
    (?i . "Info")
 | 
						|
    (?n . "Info")))
 | 
						|
 | 
						|
(defun my/-consult-replace-flymake-error-level (candidates)
 | 
						|
  "Return CANDIDATES with the flymake error level note replaced with info."
 | 
						|
  (cl-loop for cand in candidates
 | 
						|
           collect
 | 
						|
           (cl-loop
 | 
						|
            with start = nil
 | 
						|
            for i below (length cand)
 | 
						|
            for props = (text-properties-at i cand)
 | 
						|
            for face = (plist-get props 'face)
 | 
						|
            when (eq face 'compilation-info) do
 | 
						|
            (setq start (or start i))
 | 
						|
            else when start do
 | 
						|
            (setf (substring cand start i)
 | 
						|
                  (propertize (string-pad "info" (- i start))
 | 
						|
                              'face (flycheck-error-level-error-list-face
 | 
						|
                                     'info)))
 | 
						|
            (cl-return cand)
 | 
						|
            finally return cand)))
 | 
						|
 | 
						|
(defun my/consult-flymake-flycheck-candidates (&optional project)
 | 
						|
  "Return combined candidate list for flymake and flycheck.
 | 
						|
With PROJECT, return the candiadeets for that project."
 | 
						|
  (let ((had-errors))
 | 
						|
    (prog1
 | 
						|
        (seq-uniq
 | 
						|
         (append
 | 
						|
          (when-let (((bound-and-true-p flymake-mode))
 | 
						|
                     (diags (if project (flymake--project-diagnostics
 | 
						|
                                         project)
 | 
						|
                              (flymake-diagnostics))))
 | 
						|
            (setq had-errors t)
 | 
						|
            (my/-consult-replace-flymake-error-level
 | 
						|
             (consult-flymake--candidates diags)))
 | 
						|
          (when (boundp 'flycheck-mode)
 | 
						|
            (if project
 | 
						|
                (cl-loop for buf in (project-buffers project)
 | 
						|
                         append
 | 
						|
                         (with-current-buffer buf
 | 
						|
                           (when (and flycheck-mode flycheck-current-errors)
 | 
						|
                             (setq had-errors t)
 | 
						|
                             (consult-flycheck--candidates))))
 | 
						|
              (when (and flycheck-mode flycheck-current-errors)
 | 
						|
                (setq had-errors t)
 | 
						|
                (consult-flycheck--candidates))))))
 | 
						|
      (unless had-errors
 | 
						|
        (user-error "No errors (Flymake: %s | Flycheck: %s)"
 | 
						|
                    (cond
 | 
						|
                     ((not (bound-and-true-p flymake-mode))
 | 
						|
                      "not running")
 | 
						|
                     ((seq-difference (flymake-running-backends)
 | 
						|
                                      (flymake-reporting-backends))
 | 
						|
                      "running")
 | 
						|
                     (t "finished"))
 | 
						|
                    (if (boundp 'flycheck-last-status-change)
 | 
						|
                        flycheck-last-status-change
 | 
						|
                      "not running"))))))
 | 
						|
 | 
						|
(defun my/consult-flymake-flycheck (&optional project)
 | 
						|
  "Jump to flymake or flycheck error.
 | 
						|
With PROJECT, give diagnostics for all buffers in the current project."
 | 
						|
  (interactive "P")
 | 
						|
  (consult--read
 | 
						|
   (consult--with-increased-gc
 | 
						|
     (my/consult-flymake-flycheck-candidates
 | 
						|
      (and project (project-current))))
 | 
						|
   :prompt "Error: "
 | 
						|
   :category 'flymake-flycheck-error
 | 
						|
   :history t
 | 
						|
   :require-match t
 | 
						|
   :sort nil
 | 
						|
   :narrow (consult--type-narrow my/consult-flymake-flycheck-narrow)
 | 
						|
   :group (consult--type-group my/consult-flymake-flycheck-narrow)
 | 
						|
   :lookup #'consult--lookup-candidate
 | 
						|
   :state (consult--jump-state)))
 | 
						|
(with-eval-after-load 'flymake
 | 
						|
  (keymap-set flymake-mode-map "C-c e" 'my/diagnostic-at-point)
 | 
						|
  (keymap-set flymake-mode-map "C-c E" 'my/consult-flymake-flycheck))
 | 
						|
(with-eval-after-load 'flycheck
 | 
						|
  (keymap-set flycheck-mode-map "C-c e" 'my/diagnostic-at-point)
 | 
						|
  (keymap-set flycheck-mode-map "C-c E" 'my/consult-flymake-flycheck))
 | 
						|
(with-eval-after-load 'jinx
 | 
						|
  (keymap-set jinx-mode-map "C-c e" 'my/diagnostic-at-point))
 | 
						|
 | 
						|
;; eldoc
 | 
						|
(use-package eldoc
 | 
						|
  :diminish eldoc-mode
 | 
						|
  :init
 | 
						|
  (setq-default eldoc-echo-area-use-multiline-p nil))
 | 
						|
 | 
						|
;; eglot
 | 
						|
(use-package eglot
 | 
						|
  :demand t
 | 
						|
  :pin gnu ;; try to force Elpa version to fix warnings
 | 
						|
  :hook ((eglot-managed-mode . my/-eglot-setup))
 | 
						|
  :init
 | 
						|
  ;; (defun my/eglot-in-text-mode-only ()
 | 
						|
  ;;   (when (eq major-mode 'text-mode)
 | 
						|
  ;;     (eglot-ensure)))
 | 
						|
  (defun my/eglot-if-trusted ()
 | 
						|
    (when (trusted-content-p)
 | 
						|
      (eglot-ensure)))
 | 
						|
  (defvar my/-eglot-documentation-buffer nil
 | 
						|
    "Buffer for showing documentation for `my/eglot-documentation-at-point'.")
 | 
						|
  (define-derived-mode my/eglot-documentation-mode special-mode "Eglot-Doc"
 | 
						|
    "Major mode for eglot documentation buffers."
 | 
						|
    :interactive nil
 | 
						|
    (face-remap-add-relative 'nobreak-space 'default))
 | 
						|
  (defun my/eglot-documentation-at-point ()
 | 
						|
    "Show documentation for a symbol at point."
 | 
						|
    (interactive)
 | 
						|
    (if-let (server (eglot-current-server))
 | 
						|
        (progn
 | 
						|
          (unless (buffer-live-p my/-eglot-documentation-buffer)
 | 
						|
            (setq my/-eglot-documentation-buffer
 | 
						|
                  (get-buffer-create "*eglot documentation*")))
 | 
						|
          (eglot-hover-eldoc-function
 | 
						|
           (lambda (info _ _)
 | 
						|
             (if-let (((not (seq-empty-p info)))
 | 
						|
                      (buff (current-buffer)))
 | 
						|
                 (with-current-buffer my/-eglot-documentation-buffer
 | 
						|
                   (let ((inhibit-read-only t))
 | 
						|
                     (unless (derived-mode-p 'my/eglot-documentation-mode)
 | 
						|
                       (my/eglot-documentation-mode))
 | 
						|
                     (erase-buffer)
 | 
						|
                     (insert info)
 | 
						|
                     (goto-char (point-min)))
 | 
						|
                   (when (not (get-buffer-window my/-eglot-documentation-buffer nil))
 | 
						|
                     (switch-to-buffer-other-window my/-eglot-documentation-buffer t)
 | 
						|
                     (switch-to-buffer-other-window buff t)))))))))
 | 
						|
  (defun my/-eglot-cleanup-doc-buffer (_server &optional _interactive _timeout
 | 
						|
                                               preserve-buffers)
 | 
						|
    (when (and (not preserve-buffers)
 | 
						|
               (buffer-live-p my/-eglot-documentation-buffer)
 | 
						|
               (cl-every (lambda (buffer)
 | 
						|
                           (with-current-buffer buffer
 | 
						|
                             (let ((server (eglot-current-server)))
 | 
						|
                               (or (not (eglot-lsp-server-p server))
 | 
						|
                                   (eglot--shutdown-requested server)))))
 | 
						|
                         (buffer-list)))
 | 
						|
      (kill-buffer my/-eglot-documentation-buffer)))
 | 
						|
  (advice-add 'eglot-shutdown :after 'my/-eglot-cleanup-doc-buffer)
 | 
						|
  (defun my/-eglot-setup ()
 | 
						|
    "Setup eldoc variables for `eglot-managed-mode-hook'."
 | 
						|
    (setq-local evil-lookup-func #'my/eglot-documentation-at-point)
 | 
						|
    (evil-define-key '(normal motion) 'local
 | 
						|
      "K" #'evil-lookup
 | 
						|
      "gR" #'eglot-rename
 | 
						|
      "gA" #'eglot-code-actions
 | 
						|
      "gs" #'consult-eglot-symbols)
 | 
						|
    (eglot-inlay-hints-mode -1))
 | 
						|
  (setq eglot-autoshutdown t
 | 
						|
        eglot-ignored-server-capabilities '(:documentOnTypeFormattingProvider))
 | 
						|
  :config
 | 
						|
  ;; Fix directory local variables in remote buffers
 | 
						|
  (defun my/-eglot-fix-dir-locals-in-remote-dirs (oldfun server &optional path)
 | 
						|
    (if (not (file-remote-p (or path default-directory)))
 | 
						|
        (funcall oldfun server path)
 | 
						|
      (cl-letf (((default-value 'enable-remote-dir-locals)
 | 
						|
                 (let ((default-directory (if path (if (file-directory-p path)
 | 
						|
                                                       (file-name-as-directory path)
 | 
						|
                                                     (file-name-directory path))
 | 
						|
                                            default-directory)))
 | 
						|
                   (connection-local-value enable-remote-dir-locals))))
 | 
						|
        (funcall oldfun server path))))
 | 
						|
  (advice-add 'eglot--workspace-configuration-plist :around
 | 
						|
              #'my/-eglot-fix-dir-locals-in-remote-dirs)
 | 
						|
 | 
						|
  (add-to-list 'eglot-server-programs
 | 
						|
               (cons '(c-mode c-ts-mode c++-mode c++-ts-mode objc-mode)
 | 
						|
                     '("clangd" "--all-scopes-completion" "--background-index"
 | 
						|
                       "--clang-tidy" "--completion-style=detailed"
 | 
						|
                       "--header-insertion=never" "--pch-storage=memory"
 | 
						|
                       "--function-arg-placeholders"
 | 
						|
                       "--compile-commands-dir=build")))
 | 
						|
  ;; this should come after the above
 | 
						|
  (add-to-list 'eglot-server-programs
 | 
						|
               (cons '(arduino-ts-mode)
 | 
						|
                     '("arduino-language-server"))))
 | 
						|
(use-package eglot-inactive-regions
 | 
						|
  :custom
 | 
						|
  (eglot-inactive-regions-style 'darken-foreground)
 | 
						|
  (eglot-inactive-regions-opacity 0.4)
 | 
						|
  :config
 | 
						|
  (eglot-inactive-regions-mode 1))
 | 
						|
 | 
						|
;; LTeX (languagetool)
 | 
						|
(require 'ltex-eglot)
 | 
						|
 | 
						|
;; apheleia (code formatter)
 | 
						|
(use-package apheleia
 | 
						|
  :defer nil
 | 
						|
  :bind ("C-c o" . apheleia-format-buffer)
 | 
						|
  :custom
 | 
						|
  (apheleia-formatters-respect-fill-column t)
 | 
						|
  :init
 | 
						|
  (add-to-list 'auto-mode-alist `(,(rx "/.clang-format" eos) . yaml-ts-mode))
 | 
						|
  (defun my/apheleia-disable-in-current-buffer ()
 | 
						|
    (setq-local apheleia-inhibit t))
 | 
						|
  :config
 | 
						|
  (with-eval-after-load 'apheleia
 | 
						|
    (setf (alist-get 'java-mode apheleia-mode-alist) 'clang-format
 | 
						|
          (alist-get 'java-ts-mode apheleia-mode-alist) 'clang-format))
 | 
						|
  (apheleia-global-mode +1))
 | 
						|
 | 
						|
;; awk
 | 
						|
(with-eval-after-load 'cc-mode
 | 
						|
  (add-hook 'awk-mode-hook #'my/apheleia-disable-in-current-buffer))
 | 
						|
 | 
						|
;; gud
 | 
						|
(use-package gud
 | 
						|
  :demand t
 | 
						|
  :ensure nil
 | 
						|
  :after (project evil)
 | 
						|
  :bind (:map project-prefix-map
 | 
						|
         ("U" . my/project-gdb))
 | 
						|
  :custom
 | 
						|
  (gud-highlight-current-line t)
 | 
						|
  :config
 | 
						|
  (setq gdb-debuginfod-enable-setting t)
 | 
						|
  (defvar my/project-gdb-command nil
 | 
						|
    "Command to use in `my/project-gdb'.")
 | 
						|
  (put 'my/project-gdb-command 'safe-local-variable (lambda (val)
 | 
						|
                                                      (stringp val)))
 | 
						|
  (defun my/project-gdb (project command-line)
 | 
						|
    "Run gdb in the project root"
 | 
						|
    (interactive (let* ((project (project-current t))
 | 
						|
                        (default-directory (project-root project)))
 | 
						|
                   (list project (gud-query-cmdline 'gdb))))
 | 
						|
    (let ((default-directory (project-root project)))
 | 
						|
      (gdb command-line)))
 | 
						|
  (evil-set-initial-state 'gdb-locals-mode 'motion)
 | 
						|
  (evil-collection-inhibit-insert-state 'gdb-locals-mode-map)
 | 
						|
  (evil-define-key '(normal motion visual) gdb-locals-mode-map
 | 
						|
    (kbd "TAB") (keymap-lookup gdb-locals-mode-map "TAB")
 | 
						|
    (kbd "RET") #'gdb-edit-locals-value
 | 
						|
    (kbd "<mouse-1>") #'gdb-edit-locals-value
 | 
						|
    "q" #'kill-current-buffer)
 | 
						|
  (evil-set-initial-state 'gdb-registers-mode 'motion)
 | 
						|
  (evil-collection-inhibit-insert-state 'gdb-registers-mode-map)
 | 
						|
  (evil-define-key '(normal motion visual) gdb-registers-mode-map
 | 
						|
    (kbd "TAB") (keymap-lookup gdb-registers-mode-map "TAB")
 | 
						|
    (kbd "RET") #'gdb-edit-register-value
 | 
						|
    (kbd "<mouse-1>") #'gdb-edit-register-value
 | 
						|
    "q" #'kill-current-buffer
 | 
						|
    (kbd "C-c f") #'gdb-registers-toggle-filter
 | 
						|
    (kbd "C-c F") (lambda ()
 | 
						|
                    "Customize the filter for the registers buffer."
 | 
						|
                    (interactive)
 | 
						|
                    (customize-option-other-window
 | 
						|
                     'gdb-registers-filter-pattern-list)))
 | 
						|
  (evil-set-initial-state 'gdb-frames-mode 'motion)
 | 
						|
  (evil-collection-inhibit-insert-state 'gdb-frames-mode-map)
 | 
						|
  (evil-define-key '(normal motion visual) gdb-frames-mode-map
 | 
						|
    "q" #'kill-current-buffer
 | 
						|
    (kbd "RET") #'gdb-select-frame)
 | 
						|
  (evil-set-initial-state 'gdb-breakpoints-mode 'motion)
 | 
						|
  (evil-collection-inhibit-insert-state 'gdb-breakpoints-mode-map)
 | 
						|
  (evil-define-key '(normal motion visual) gdb-breakpoints-mode-map
 | 
						|
    (kbd "TAB") (keymap-lookup gdb-breakpoints-mode-map "TAB")
 | 
						|
    "q" #'gdb-delete-frame-or-window
 | 
						|
    "D" #'gdb-delete-breakpoint
 | 
						|
    (kbd "RET") #'gdb-goto-breakpoint
 | 
						|
    (kbd "<mouse-1>") #'gdb-goto-breakpoint
 | 
						|
    (kbd "SPC") #'gdb-toggle-breakpoint)
 | 
						|
  (evil-set-initial-state 'gdb-threads-mode 'motion)
 | 
						|
  (evil-collection-inhibit-insert-state 'gdb-threads-mode-map)
 | 
						|
  (evil-define-key '(normal motion visual) gdb-threads-mode-map
 | 
						|
    (kbd "TAB") (keymap-lookup gdb-threads-mode-map "TAB")
 | 
						|
    "q" #'gdb-delete-frame-or-window
 | 
						|
    "D" #'gdb-frame-disassembly-for-thread
 | 
						|
    (kbd "C-c f") #'gdb-display-stack-for-thread
 | 
						|
    (kbd "C-c i") #'gdb-interrupt-thread
 | 
						|
    (kbd "C-c l") #'gdb-display-locals-for-thread
 | 
						|
    (kbd "C-c r") #'gdb-display-registers-for-thread
 | 
						|
    (kbd "C-c c") #'gdb-continue-thread
 | 
						|
    (kbd "C-c d") #'gdb-display-disassembly-for-thread
 | 
						|
    (kbd "C-c s") #'gdb-step-thread
 | 
						|
    (kbd "C-c F") #'gdb-frame-stack-for-thread
 | 
						|
    (kbd "C-c L") #'gdb-frame-locals-for-thread
 | 
						|
    (kbd "C-c R") #'gdb-frame-registers-for-thread
 | 
						|
    (kbd "RET") #'gdb-select-thread
 | 
						|
    (kbd "<mouse-2>") #'gdb-select-thread))
 | 
						|
 | 
						|
;; dape
 | 
						|
(use-package dape
 | 
						|
  :hook ((after-init . dape-breakpoint-load)
 | 
						|
         (kill-emacs . dape-breakpoint-save)
 | 
						|
         (dape-start . save-some-buffers)
 | 
						|
         (dape-display-source . pulse-momentary-highlight-one-line))
 | 
						|
  :init
 | 
						|
  (setopt dape-default-breakpoints-file (no-littering-expand-var-file-name
 | 
						|
                                         "dape-breakpoints"))
 | 
						|
  :config
 | 
						|
  (evil-define-key 'motion dape-info-parent-mode-map
 | 
						|
    (kbd "TAB") #'dape--info-buffer-tab)
 | 
						|
  (setopt dape-buffer-window-arrangement 'right)
 | 
						|
  (dape-breakpoint-global-mode 1))
 | 
						|
 | 
						|
;; dumb-jump
 | 
						|
(use-package dumb-jump
 | 
						|
  :init
 | 
						|
  (add-hook 'xref-backend-functions #'dumb-jump-xref-activate))
 | 
						|
 | 
						|
;; yasnippet
 | 
						|
(use-package yasnippet
 | 
						|
  :demand t
 | 
						|
  :bind ("C-c s" . yas-expand)
 | 
						|
  :config
 | 
						|
  (yas-global-mode 1))
 | 
						|
 | 
						|
;; project.el
 | 
						|
(use-package project
 | 
						|
  :defer nil
 | 
						|
  :bind (([remap project-compile] . my/project-compile-or-default)
 | 
						|
         :map project-prefix-map
 | 
						|
         ("s" . my/project-eshell)
 | 
						|
         ("u" . my/project-run))
 | 
						|
  :init
 | 
						|
  (setq uniquify-dirname-transform #'project-uniquify-dirname-transform)
 | 
						|
  (defvar eshell-buffer-name)
 | 
						|
  (defun my/project-eshell (prompt &optional arg)
 | 
						|
    "Switch to or create an eshell buffer in the current projects root."
 | 
						|
    (interactive (list t current-prefix-arg))
 | 
						|
    (if-let ((proj (project-current prompt))
 | 
						|
             (default-directory (project-root proj))
 | 
						|
             (eshell-buffer-name
 | 
						|
              (concat "*eshell for project " default-directory "*")))
 | 
						|
        (eshell arg)))
 | 
						|
  (defun my/project-eshell-or-default (&optional arg)
 | 
						|
    "Open an eshell for the current project, otherwise, open a normal eshell."
 | 
						|
    (interactive "P")
 | 
						|
    (unless (my/project-eshell nil arg)
 | 
						|
      (eshell arg)))
 | 
						|
  (defun my/project-compile-or-default ()
 | 
						|
    "If in a project, run `project-compile', otherwise run `compile'."
 | 
						|
    (interactive)
 | 
						|
    (if (project-current)
 | 
						|
        (call-interactively 'project-compile)
 | 
						|
      (call-interactively 'compile)))
 | 
						|
  (defvar my/project-run-command nil
 | 
						|
    "Command to run with `my/project-run'.")
 | 
						|
  (put 'my/project-run-command 'safe-local-variable #'stringp)
 | 
						|
  (defvar my/project-run-dir nil
 | 
						|
    "Directory to run project in with `my/project-run'.")
 | 
						|
  (put 'my/project-run-dir 'safe-local-variable #'stringp)
 | 
						|
  (defvar my/-project-run-history '()
 | 
						|
    "Commands previously run with `my/project-run'")
 | 
						|
  (defvar my/project-root-marker ".project-root"
 | 
						|
    "Marker file to look for in non-vc backed projects.")
 | 
						|
  (defun my/project-get-root-dir ()
 | 
						|
    "Get the root dir for the current project"
 | 
						|
    (let* ((proj (project-current nil))
 | 
						|
           (default-directory (if proj
 | 
						|
                                  (project-root proj)
 | 
						|
                                default-directory)))
 | 
						|
      (if my/project-run-dir
 | 
						|
          (expand-file-name my/project-run-dir)
 | 
						|
        default-directory)))
 | 
						|
  (defvar my/compile-use-comint t
 | 
						|
    "Weather or not to use comint by default for compile buffers.")
 | 
						|
  (defun my/-compile-use-comint-by-default (args)
 | 
						|
    (if my/compile-use-comint
 | 
						|
        (list (car args) t)
 | 
						|
      args))
 | 
						|
  (advice-add 'compile :filter-args 'my/-compile-use-comint-by-default)
 | 
						|
  (defun my/project-run (command comint)
 | 
						|
    "Like `project-compile', but for running a project.
 | 
						|
COMMAND and COMINT are like `compile'."
 | 
						|
    (interactive
 | 
						|
     (list
 | 
						|
      (let ((default-directory (my/project-get-root-dir)))
 | 
						|
        (read-shell-command "Run Command: "
 | 
						|
                            (or (car my/-project-run-history)
 | 
						|
                                my/project-run-command)
 | 
						|
                            (if (and my/project-run-command
 | 
						|
                                     (equal my/project-run-command
 | 
						|
                                            (car-safe my/-project-run-history)))
 | 
						|
                                '(my/-project-run-history . 1)
 | 
						|
                              'my/-project-run-history)))
 | 
						|
      (consp current-prefix-arg)))
 | 
						|
    (let* ((default-directory (my/project-get-root-dir))
 | 
						|
           (compilation-buffer-name-function (lambda (_)
 | 
						|
                                               (progn "*run project*")))
 | 
						|
           (compilation-directory default-directory)
 | 
						|
           (compile-history nil)
 | 
						|
           (compile-command nil))
 | 
						|
      (compile command comint)
 | 
						|
      (when (not my/project-run-command)
 | 
						|
        (setq my/project-run-command command))))
 | 
						|
  :config
 | 
						|
  (defun my/project-try-dotfile (dir)
 | 
						|
    (if-let (root (locate-dominating-file dir my/project-root-marker))
 | 
						|
        (list 'vc nil root)))
 | 
						|
  (add-hook 'project-find-functions #'my/project-try-dotfile))
 | 
						|
 | 
						|
;; comint
 | 
						|
(use-package comint
 | 
						|
  :ensure nil
 | 
						|
  :after evil
 | 
						|
  :config
 | 
						|
  (evil-set-initial-state 'comint-mode 'normal))
 | 
						|
 | 
						|
;; editorconfig
 | 
						|
(use-package editorconfig
 | 
						|
  :demand t
 | 
						|
  :ensure nil
 | 
						|
  :init
 | 
						|
  (editorconfig-mode 1))
 | 
						|
 | 
						|
;; nxml
 | 
						|
(use-package nxml-mode
 | 
						|
  :ensure nil
 | 
						|
  :hook (nxml-mode . my/-nxml-setup)
 | 
						|
  :init
 | 
						|
  (defun my/-nxml-setup ()
 | 
						|
    "Setup `nxml-mode'."
 | 
						|
    (sgml-electric-tag-pair-mode 1)
 | 
						|
    (setq-local completion-at-point-functions
 | 
						|
                '(rng-completion-at-point cape-file)))
 | 
						|
  (add-to-list 'auto-mode-alist
 | 
						|
               `(,(concat
 | 
						|
                   (regexp-opt '("gschema" "gresource" "ui")) "\\'")
 | 
						|
                 . nxml-mode)))
 | 
						|
 | 
						|
;; devdocs
 | 
						|
(use-package devdocs
 | 
						|
  :bind (("C-h D" . devdocs-lookup)))
 | 
						|
 | 
						|
;; woman
 | 
						|
(use-package woman
 | 
						|
  :ensure nil
 | 
						|
  :bind (:map help-map
 | 
						|
         ("C-m" . woman))
 | 
						|
  :config
 | 
						|
  (cl-callf2 cl-delete-if-not
 | 
						|
      #'(lambda (elt)
 | 
						|
          (file-directory-p (if (consp elt)
 | 
						|
                                (cdr elt)
 | 
						|
                              elt)))
 | 
						|
      woman-manpath))
 | 
						|
 | 
						|
;; Bibtex (built in)
 | 
						|
(require 'bibtex)
 | 
						|
;; Better URL highlighting and matching
 | 
						|
(dolist (field '("howpublished" "url"))
 | 
						|
  (add-to-list 'bibtex-generate-url-list
 | 
						|
               `((,field . ,(rx "http" (? "s") "://" (+ (not "}"))))
 | 
						|
                 "%s"
 | 
						|
                 (,field
 | 
						|
                  ,(rx (? (or "\\url{" "{")) (group "http" (? "s") "://"
 | 
						|
                                                    (+ (not "}")))
 | 
						|
                       (? "}"))
 | 
						|
                  ,(lambda (field)
 | 
						|
                     (match-string 1 field))))))
 | 
						|
(defun my/bibtex-in-entry-p (&optional exclude-braces)
 | 
						|
  "Return t is point is inside a BibTeX entry.
 | 
						|
When EXCLUDE-BRACES is non-nil, don't count the first and last brace of the
 | 
						|
entry as in the entry.  That is, if the point is on the first { or last } of the
 | 
						|
entry, return nil."
 | 
						|
  (cl-destructuring-bind (depth &rest r) (syntax-ppss)
 | 
						|
    (if (zerop depth)
 | 
						|
        (and (not exclude-braces) (eql (char-after) ?\{))
 | 
						|
      (or (not exclude-braces) (not (eql (char-after) ?\}))))))
 | 
						|
(defvar my/bibtex-indent-width 4
 | 
						|
  "Width to indent for `my/bibtex-calculate-indentation'.")
 | 
						|
(defun my/bibtex-calculate-indentation ()
 | 
						|
  "Calculate the column to indent to on the current line."
 | 
						|
  (save-excursion
 | 
						|
    (back-to-indentation)
 | 
						|
    (if (my/bibtex-in-entry-p t)
 | 
						|
        my/bibtex-indent-width
 | 
						|
      0)))
 | 
						|
(defun my/bibtex-empty-line-p ()
 | 
						|
  "Return t if the current line is only blank characters."
 | 
						|
  (save-excursion
 | 
						|
    (beginning-of-line)
 | 
						|
    (looking-at (rx (* blank) eol))))
 | 
						|
(defun my/bibtex-indent-line ()
 | 
						|
  "Indent the current line."
 | 
						|
  (interactive)
 | 
						|
  (save-excursion
 | 
						|
    (beginning-of-line)
 | 
						|
    (when (looking-at (rx (+ blank)))
 | 
						|
      (delete-region (point) (match-end 0)))
 | 
						|
    (indent-to (my/bibtex-calculate-indentation)))
 | 
						|
  (when (looking-at (rx (+ blank) eol))
 | 
						|
    (end-of-line)))
 | 
						|
(defun my/bibtex-indent-or-find-text ()
 | 
						|
  "Either indent the current line or jump to the current fields text.
 | 
						|
If the current line is only whitespace call `my/bibtex-calculate-indentation',
 | 
						|
otherwise, call `bibtex-find-text'."
 | 
						|
  (interactive)
 | 
						|
  (if (my/bibtex-empty-line-p)
 | 
						|
      (my/bibtex-indent-line)
 | 
						|
    (bibtex-find-text)))
 | 
						|
(defun my/bibtex-indent-or-find-text-and-insert ()
 | 
						|
  "Like `my/bibtex-indent-or-find-text', but enter insert mode after."
 | 
						|
  (interactive)
 | 
						|
  (my/bibtex-indent-or-find-text)
 | 
						|
  (if (my/bibtex-empty-line-p)
 | 
						|
      (evil-append 1)
 | 
						|
    (evil-insert 1)))
 | 
						|
(defun my/-bibtex-setup-indent ()
 | 
						|
  "Set up `bibtex-mode' indentation stuff."
 | 
						|
  (setq-local indent-line-function 'my/bibtex-indent-line
 | 
						|
              electric-indent-chars '(?\n ?\{ ?\} ?,)))
 | 
						|
(defun my/-bibtex-fix-fill-prefix ()
 | 
						|
  "`bivtex-mode' has a bad habbit of messing up `fill-prefix'."
 | 
						|
  (when (eq major-mode 'bibtex-mode)
 | 
						|
    (setq-local fill-prefix nil)))
 | 
						|
(advice-add 'bibtex-mode :after 'my/-bibtex-fix-fill-prefix)
 | 
						|
(add-hook 'bibtex-mode-hook 'my/-bibtex-setup-indent)
 | 
						|
(keymap-set bibtex-mode-map "RET" 'newline-and-indent)
 | 
						|
(keymap-set bibtex-mode-map "TAB" 'my/bibtex-indent-or-find-text)
 | 
						|
(evil-define-key 'normal bibtex-mode-map
 | 
						|
  (kbd "TAB") 'my/bibtex-indent-or-find-text-and-insert)
 | 
						|
 | 
						|
;; Latex help (from elisp file)
 | 
						|
(require 'latex-help)
 | 
						|
 | 
						|
;; AUCTeX
 | 
						|
(use-package auctex
 | 
						|
  :hook ((LaTeX-mode . turn-on-reftex)
 | 
						|
         (LaTeX-mode . LaTeX-math-mode)
 | 
						|
         (LaTeX-mode . my/-setup-LaTeX-mode)
 | 
						|
         (LaTeX-mode . my/flycheck-if-trusted)
 | 
						|
         (TeX-mode . kill-ring-deindent-mode))
 | 
						|
  :bind (:map TeX-mode-map
 | 
						|
         ("C-c ?" . latex-help))
 | 
						|
  :init
 | 
						|
  (add-to-list 'major-mode-remap-alist '(plain-tex-mode . plain-TeX-mode))
 | 
						|
  (add-to-list 'major-mode-remap-alist '(latex-mode . LaTeX-mode))
 | 
						|
  (add-to-list 'major-mode-remap-alist '(ams-tex-mode . AmSTeX-mode))
 | 
						|
  (add-to-list 'major-mode-remap-alist '(context-mode . ConTeXt-mode))
 | 
						|
  (add-to-list 'major-mode-remap-alist '(texinfo-mode . Texinfo-mode))
 | 
						|
  (add-to-list 'major-mode-remap-alist '(doctex-mode . docTeX-mode))
 | 
						|
  (add-to-list 'auto-mode-alist '("/\\.latexmkrc\\'" . perl-mode))
 | 
						|
  (add-to-list 'auto-mode-alist '("\\.[tT]e[xX]\\'" . LaTeX-mode))
 | 
						|
  :config
 | 
						|
  (defun my/-auctex-texdoc-setup-env (oldfun &rest args)
 | 
						|
    (let ((process-environment process-environment)
 | 
						|
          (emacs-cmd (concat "emacsclient" (and (not (display-graphic-p)) " -nw"))))
 | 
						|
      (setenv "PDFVIEWER_texdoc" "evince")
 | 
						|
      (setenv "MDVIEWER_texdoc" emacs-cmd)
 | 
						|
      (setenv "PAGER_texdoc" emacs-cmd)
 | 
						|
      (apply oldfun args)))
 | 
						|
  (advice-add 'TeX-documentation-texdoc :around 'my/-auctex-texdoc-setup-env)
 | 
						|
  (defun my/-setup-LaTeX-mode ()
 | 
						|
    (setq evil-lookup-func 'latex-help-at-point))
 | 
						|
  (setq TeX-auto-save t
 | 
						|
        TeX-parse-self t
 | 
						|
        reftex-plug-into-AUCTeX t)
 | 
						|
  (evil-define-operator my/evil-LaTeX-fill (beg end)
 | 
						|
    "Like `evil-fill', but using auctex."
 | 
						|
    ;; The code here came straight from `evil-fill'
 | 
						|
    :move-point nil
 | 
						|
    :type line
 | 
						|
    (save-excursion
 | 
						|
      (ignore-errors (LaTeX-fill-region beg end))))
 | 
						|
  (evil-define-operator my/evil-LaTeX-fill-and-move (beg end)
 | 
						|
    "Like `evil-fill-and-move', but using auctex."
 | 
						|
    ;; The code here came straight from `evil-fill-and-move'
 | 
						|
    :move-point nil
 | 
						|
    :type line
 | 
						|
    (let ((marker (make-marker)))
 | 
						|
      (move-marker marker (1- end))
 | 
						|
      (ignore-errors
 | 
						|
        (LaTeX-fill-region beg end)
 | 
						|
        (goto-char marker)
 | 
						|
        (evil-first-non-blank))))
 | 
						|
  (evil-define-key 'normal TeX-mode-map
 | 
						|
    "gq" 'my/evil-LaTeX-fill-and-move
 | 
						|
    "gw" 'my/evil-LaTeX-fill)
 | 
						|
  (setq-default TeX-master nil)
 | 
						|
  (require 'tex)
 | 
						|
  (TeX-global-PDF-mode 1))
 | 
						|
 | 
						|
;; blueprint
 | 
						|
(use-package blueprint-ts-mode
 | 
						|
  :hook (blueprint-ts-mode . my/eglot-if-trusted)
 | 
						|
  :after eglot)
 | 
						|
 | 
						|
;; python-ts-mode
 | 
						|
(use-package python
 | 
						|
  :ensure nil
 | 
						|
  :hook ((python-ts-mode . my/eglot-if-trusted)
 | 
						|
         (python-ts-mode . my/-setup-python-ts-mode))
 | 
						|
  :init
 | 
						|
  (setq python-shell-interpreter "ipython"
 | 
						|
        python-shell-interpreter-args "-i --simple-prompt")
 | 
						|
  :config
 | 
						|
  (defun my/-setup-python-ts-mode ()
 | 
						|
    (setq-local fill-column 79)))
 | 
						|
;; python virtual environments
 | 
						|
(use-package pyvenv)
 | 
						|
(use-package pyenv-mode)
 | 
						|
 | 
						|
;; java-ts-mode
 | 
						|
(use-package java-ts-mode
 | 
						|
  :hook ((java-ts-mode . my/eglot-if-trusted)
 | 
						|
         (java-ts-mode . my/-setup-java-ts-mode))
 | 
						|
  :config
 | 
						|
  (defun my/-setup-java-ts-mode ()
 | 
						|
    (let ((rules (car treesit-simple-indent-rules)))
 | 
						|
      (setcdr rules
 | 
						|
              (cons '((and (parent-is "array_initializer")
 | 
						|
                           (node-is "array_initializer"))
 | 
						|
                      parent-bol java-ts-mode-indent-offset)
 | 
						|
                    (nthcdr 1 rules))))))
 | 
						|
 | 
						|
;; c-ts-mode
 | 
						|
(setopt ff-ignore-include t)
 | 
						|
(defun my/find-other-file (&optional in-other-window event)
 | 
						|
  "Like `ff-find-other-file', but respect configuration variables.
 | 
						|
IN-OTHER-WINDOW and EVENT are the same as the original command."
 | 
						|
  (interactive (list current-prefix-arg last-nonmenu-event))
 | 
						|
  (ff-find-other-file in-other-window ff-ignore-include event))
 | 
						|
(defun my/find-other-file-other-window (event)
 | 
						|
  "Like `ff-find-other-file-other-window', but respect config variables.
 | 
						|
EVENT is the same as the original command."
 | 
						|
  (interactive (list last-nonmenu-event))
 | 
						|
  (my/find-other-file t event))
 | 
						|
(keymap-global-set "<remap> <ff-find-other-file>" #'my/find-other-file)
 | 
						|
(keymap-global-set "<remap> <ff-find-other-file-other-window>"
 | 
						|
                   #'my/find-other-file-other-window)
 | 
						|
(use-package c-ts-mode
 | 
						|
  :after evil
 | 
						|
  :hook ((c-ts-mode c++-ts-mode) . my/eglot-if-trusted)
 | 
						|
  :init
 | 
						|
  (defun my/-c-ts-in-doc-comment-p (&optional include-end)
 | 
						|
    (when-let ((node (treesit-thing-at-point "comment" 'toplevel))
 | 
						|
               (start (treesit-node-start node))
 | 
						|
               (end (treesit-node-end node)))
 | 
						|
      (and (> (point) (+ start 2))
 | 
						|
           (eql (char-after (+ start 1)) ?*)
 | 
						|
           (memql (char-after (+ start 2)) '(?* ?!))
 | 
						|
           (or include-end
 | 
						|
               (<= (point) (- end 2))))))
 | 
						|
  (defun my/c-ts-newline (&optional arg)
 | 
						|
    "Insert ARG newlines as with `newline'.
 | 
						|
If inside a comment and in multi-line comment mode, act as
 | 
						|
`default-indent-new-line'."
 | 
						|
    (interactive "*P")
 | 
						|
    (when (and arg (< (prefix-numeric-value arg) 0))
 | 
						|
      (user-error "Count cannot be negative"))
 | 
						|
    (if (my/-c-ts-in-doc-comment-p)
 | 
						|
        (dotimes (_ (prefix-numeric-value arg))
 | 
						|
          (default-indent-new-line))
 | 
						|
      (newline arg t)))
 | 
						|
  (setq-default c-ts-mode-indent-offset 4)
 | 
						|
  :config
 | 
						|
  (dolist (sym '(c-ts-mode-map c++-ts-mode-map))
 | 
						|
    (keymap-set (symbol-value sym) "<remap> <newline>" #'my/c-ts-newline))
 | 
						|
  (evil-define-key 'normal 'c-ts-mode-map
 | 
						|
    "go" #'my/find-other-file
 | 
						|
    "gO" #'my/find-other-file-other-window)
 | 
						|
  (evil-define-key 'normal 'c++-ts-mode-map
 | 
						|
    "go" #'my/find-other-file
 | 
						|
    "gO" #'my/find-other-file-other-window)
 | 
						|
  (evil-define-key 'normal 'objc-mode-map
 | 
						|
    "go" #'my/find-other-file
 | 
						|
    "gO" #'my/find-other-file-other-window)
 | 
						|
  (defun -arduino-ts-mode-set-modeline (oldfun)
 | 
						|
    "Override the modeline set by c++-ts-mode.
 | 
						|
This is :around advice, so OLDFUN is the real function
 | 
						|
`c-ts-mode-set-modeline'."
 | 
						|
    (if (not (eq major-mode 'arduino-ts-mode))
 | 
						|
        (funcall oldfun)
 | 
						|
      (setq mode-name (concat "Arduino C++" (string-trim-right comment-start)))
 | 
						|
      (force-mode-line-update)))
 | 
						|
  (advice-add 'c-ts-mode-set-modeline :around #'-arduino-ts-mode-set-modeline))
 | 
						|
(define-derived-mode arduino-ts-mode c++-ts-mode "Arduino C")
 | 
						|
(add-to-list 'auto-mode-alist '("\\.ino\\'" . arduino-ts-mode))
 | 
						|
 | 
						|
;; GLSL
 | 
						|
(use-package glsl-mode)
 | 
						|
 | 
						|
;; php-mode
 | 
						|
(use-package php-ts-mode
 | 
						|
  :ensure nil
 | 
						|
  :hook (php-mode . my/eglot-if-trusted))
 | 
						|
 | 
						|
;; web-mode
 | 
						|
(use-package web-mode
 | 
						|
  :hook (web-mode . my/eglot-if-trusted)
 | 
						|
  :init
 | 
						|
  (add-to-list 'eglot-server-programs
 | 
						|
               '(web-mode . ("vscode-html-language-server" "--stdio"))))
 | 
						|
 | 
						|
;; JavaScript
 | 
						|
(use-package js
 | 
						|
  :ensure nil
 | 
						|
  :hook (js-ts-mode . my/eglot-if-trusted))
 | 
						|
(use-package js-comint
 | 
						|
  :bind (:map js-ts-mode-map
 | 
						|
         ("C-x C-e" . js-send-last-sexp)
 | 
						|
         ("C-c C-b" . js-send-buffer)
 | 
						|
         ("C-c C-r" . js-send-region)
 | 
						|
         ("C-M-x" . my/js-send-defun))
 | 
						|
  :hook (js-comint-mode . my/-setup-js-comint-mode)
 | 
						|
  :config
 | 
						|
  (defun my/-setup-js-comint-mode ()
 | 
						|
    (setq-local comint-highlight-input nil))
 | 
						|
  (defun my/js-send-defun ()
 | 
						|
    "Send the defun under point to the inferior JavaScript process."
 | 
						|
    (interactive)
 | 
						|
    (if-let ((code (thing-at-point 'defun)))
 | 
						|
        (js-comint-send-string code)
 | 
						|
      (user-error "No defun under point"))))
 | 
						|
 | 
						|
;; TypeScript
 | 
						|
(use-package typescript-ts-mode
 | 
						|
  :ensure nil
 | 
						|
  :hook (typescript-ts-mode . my/eglot-if-trusted)
 | 
						|
  :init
 | 
						|
  (add-to-list 'auto-mode-alist `(,(rx ".ts" eos) . typescript-ts-mode)))
 | 
						|
 | 
						|
;; Polymode
 | 
						|
(use-package polymode
 | 
						|
  :config
 | 
						|
  (define-hostmode my/poly-web-hostmode
 | 
						|
    :mode 'web-mode)
 | 
						|
  (define-innermode my/poly-php-innermode
 | 
						|
    :mode 'php-ts-mode
 | 
						|
    :head-matcher (regexp-quote "<?php")
 | 
						|
    :tail-matcher (regexp-quote "?>")
 | 
						|
    :head-mode 'body
 | 
						|
    :tail-mode 'body)
 | 
						|
  (define-polymode my/poly-web-mode
 | 
						|
    :hostmode 'my/poly-web-hostmode
 | 
						|
    :innermodes '(my/poly-php-innermode))
 | 
						|
  (add-to-list 'auto-mode-alist '("\\.php\\|\\.phtml\\'" . my/poly-web-mode)))
 | 
						|
 | 
						|
;; shell-mode
 | 
						|
(use-package sh-script
 | 
						|
  :ensure nil
 | 
						|
  :hook (sh-mode . my/-setup-sh-mode)
 | 
						|
  :init
 | 
						|
  (defun my/-setup-sh-mode ()
 | 
						|
    (add-hook 'completion-at-point-functions #'cape-file nil t)))
 | 
						|
 | 
						|
;; go mode
 | 
						|
(use-package go-mode
 | 
						|
  :defer nil
 | 
						|
  :hook (go-mode . my/eglot-if-trusted))
 | 
						|
(use-package go-ts-mode
 | 
						|
  :ensure nil
 | 
						|
  :hook (go-ts-mode . my/eglot-if-trusted))
 | 
						|
 | 
						|
;; rust
 | 
						|
(use-package rust-mode)
 | 
						|
(use-package rust-ts-mode
 | 
						|
  :ensure nil
 | 
						|
  :hook (rust-ts-mode . my/eglot-if-trusted))
 | 
						|
 | 
						|
;; zig
 | 
						|
(use-package zig-mode
 | 
						|
  :hook (zig-mode . my/eglot-if-trusted))
 | 
						|
 | 
						|
;; lua
 | 
						|
(use-package lua-mode
 | 
						|
  :hook (lua-mode . my/eglot-if-trusted))
 | 
						|
 | 
						|
;; markdown
 | 
						|
(use-package markdown-mode
 | 
						|
  :hook (markdown-mode . auto-fill-mode))
 | 
						|
 | 
						|
;; groovy
 | 
						|
(use-package groovy-mode)
 | 
						|
 | 
						|
;; cmake
 | 
						|
(require 'cmake-mode)
 | 
						|
(require 'cmake-ts-mode)
 | 
						|
(with-eval-after-load 'cmake-mode
 | 
						|
  (add-hook 'cmake-ts-mode-hook #'my/eglot-if-trusted)
 | 
						|
  (setq cmake-ts-mode-indent-offset tab-width))
 | 
						|
 | 
						|
;; kdl
 | 
						|
(require 'kdl-ts-mode)
 | 
						|
(with-eval-after-load 'kdl-ts-mode
 | 
						|
  (setq kdl-ts-mode-indent-offset 4))
 | 
						|
 | 
						|
;; json
 | 
						|
(use-package json-mode)
 | 
						|
(use-package json-ts-mode
 | 
						|
  :hook (json-ts-mode . my/eglot-if-trusted)
 | 
						|
  :custom
 | 
						|
  (json-ts-mode-indent-offset 4))
 | 
						|
 | 
						|
;; csv
 | 
						|
(use-package csv-mode)
 | 
						|
 | 
						|
;; firejail
 | 
						|
(require 'firejail-mode)
 | 
						|
 | 
						|
;; yaml
 | 
						|
(use-package yaml-ts-mode
 | 
						|
  :hook (;; (yaml-ts-mode . my/eglot-if-trusted)
 | 
						|
         (yaml-ts-mode . my/-setup-yaml-ts-mode))
 | 
						|
  :init
 | 
						|
  (add-to-list 'auto-mode-alist `("\\.clangd\\'" . yaml-ts-mode))
 | 
						|
  (defun my/-setup-yaml-ts-mode ()
 | 
						|
    (setq indent-line-function #'yaml-indent-line)))
 | 
						|
(use-package yaml-mode)
 | 
						|
 | 
						|
;; yuck (config language for eww)
 | 
						|
(use-package yuck-mode)
 | 
						|
 | 
						|
;; Some Elisp indentation stuff
 | 
						|
;; Source: https://github.com/magit/emacsql
 | 
						|
;; emacsql.el line 394
 | 
						|
(defun my/lisp-inside-plist-p ()
 | 
						|
  "Return t if point is inside a plist."
 | 
						|
  (save-excursion
 | 
						|
    (let ((start (point)))
 | 
						|
      (beginning-of-defun)
 | 
						|
      (when-let ((sexp (nth 1 (parse-partial-sexp (point) start))))
 | 
						|
        (goto-char sexp)
 | 
						|
        (looking-at (rx "(" (* (syntax whitespace)) ":"))))))
 | 
						|
 | 
						|
(defun my/-calculate-indent-fix-plists (oldfun &rest args)
 | 
						|
  "This function is meant to advise `calculate-lisp-indent'.
 | 
						|
It calls OLDFUN with ARGS in such an environment as to prevent the default
 | 
						|
indentation of plists."
 | 
						|
  (if (and (eq major-mode 'emacs-lisp-mode)
 | 
						|
           (save-excursion
 | 
						|
             (beginning-of-line)
 | 
						|
             (my/lisp-inside-plist-p)))
 | 
						|
      (let ((lisp-indent-offset 1))
 | 
						|
        (apply oldfun args))
 | 
						|
    (apply oldfun args)))
 | 
						|
 | 
						|
(advice-add 'calculate-lisp-indent :around
 | 
						|
            'my/-calculate-indent-fix-plists)
 | 
						|
 | 
						|
(defvar my/max-lisp-noindent-comment-search-lines 30
 | 
						|
  "Max lines to search for the noindent comment.")
 | 
						|
 | 
						|
(defun my/-calculate-lisp-indent-noindent-comment (oldfun &rest args)
 | 
						|
  "This function is meant to advise `calculate-lisp-indent'.
 | 
						|
It calls OLDFUN with ARGS, unless the line ends with the comment
 | 
						|
  ; noindent [LINES]
 | 
						|
In this case, it just returns the current amount of indentation.  LINES is the
 | 
						|
number of lines that this comment affects.  This is limited by
 | 
						|
`my/max-lisp-noindent-comment-search-lines'.
 | 
						|
 | 
						|
This only works if its on the first or second form in a block.  I think this is
 | 
						|
because the indentation code only checks those and then assumes the same
 | 
						|
indentation for every following line in the same block.  This is probably OK as
 | 
						|
I can't imagine too many instances where you need to randomly change the indent
 | 
						|
midway through a block, and in those cases you can just stick this on the first
 | 
						|
line in the block and manually deal with indentation."
 | 
						|
  (if (and (save-excursion
 | 
						|
             (end-of-line)
 | 
						|
             (re-search-backward
 | 
						|
              (rx (+ ";") (syntax whitespace) "noindent"
 | 
						|
                  (? (syntax whitespace) (group (+ num)))
 | 
						|
                  line-end)
 | 
						|
              (pos-bol (- my/max-lisp-noindent-comment-search-lines))
 | 
						|
              t))
 | 
						|
           (save-excursion
 | 
						|
             ;; if we are on a blank line, move forward a line
 | 
						|
             (when (= (pos-bol) (pos-eol))
 | 
						|
               (beginning-of-line 2))
 | 
						|
             (<= (count-lines (match-beginning 0) (pos-eol))
 | 
						|
                 (if-let ((match (match-string 1)))
 | 
						|
                     (string-to-number match)
 | 
						|
                   1))))
 | 
						|
      (save-excursion
 | 
						|
        (beginning-of-line)
 | 
						|
        (looking-at (rx (* blank)))
 | 
						|
        (length (match-string 0)))
 | 
						|
    (apply oldfun args)))
 | 
						|
 | 
						|
(advice-add 'calculate-lisp-indent :around
 | 
						|
            'my/-calculate-lisp-indent-noindent-comment)
 | 
						|
 | 
						|
;; common lisp
 | 
						|
(use-package lisp-mode
 | 
						|
  :ensure nil
 | 
						|
  :hook (lisp-mode . my/-format-only-when-sly-running)
 | 
						|
  :init
 | 
						|
  (defun my/-apheleia-sly-skip-function ()
 | 
						|
    (or (not (featurep 'sly))
 | 
						|
        (not sly-mode)
 | 
						|
        (not (sly-connected-p))))
 | 
						|
  (defun my/-format-only-when-sly-running ()
 | 
						|
    "Make apheleia-mode format lisp buffers only when sly is running."
 | 
						|
    (add-to-list 'apheleia-skip-functions #'my/-apheleia-sly-skip-function)))
 | 
						|
;; sly
 | 
						|
(use-package sly
 | 
						|
  ;; :hook (lisp-mode . my/-lisp-mode-autoconnect-sly)
 | 
						|
  :bind (:map sly-mode-map
 | 
						|
         ("C-c e" . my/diagnostic-at-point))
 | 
						|
  :autoload sly-connected-p
 | 
						|
  :init
 | 
						|
  (defun my/-lisp-mode-autoconnect-sly ()
 | 
						|
    (unless (sly-connected-p)
 | 
						|
      (sly)))
 | 
						|
  (setq inferior-lisp-program "/usr/bin/sbcl")
 | 
						|
  (defun my/-sly-fix-special-buffers ()
 | 
						|
    (when (string-match-p (rx bos "*" (* any) "*" eos) (buffer-name))
 | 
						|
      (setq-local show-trailing-whitespace nil)))
 | 
						|
  (add-hook 'lisp-mode-hook 'my/-sly-fix-special-buffers)
 | 
						|
  :config
 | 
						|
  (evil-define-key 'insert sly-mrepl-mode-map
 | 
						|
    (kbd ",") 'self-insert-command)
 | 
						|
  (evil-define-key nil sly-mrepl-mode-map
 | 
						|
    (kbd "C-c ,") 'sly-mrepl-shortcut)
 | 
						|
  (sly-symbol-completion-mode -1)
 | 
						|
  (setq common-lisp-hyperspec-root
 | 
						|
        (concat "file://" (expand-file-name "~/src/clhs/HyperSpec/")))
 | 
						|
  (defun my/-hyperspec-loopup-in-eww (oldfun &rest r)
 | 
						|
    (let ((browse-url-browser-function #'eww-browse-url))
 | 
						|
      (apply oldfun r)))
 | 
						|
  (advice-add 'common-lisp-hyperspec :around #'my/-hyperspec-loopup-in-eww)
 | 
						|
 | 
						|
  (defvar-local my/-sly-fontification-buffer nil
 | 
						|
    "The fontification buffer for the current sly buffer.")
 | 
						|
  (defun my/-sly-get-fontification-buffer ()
 | 
						|
    "Return the sly fontification buffer."
 | 
						|
    (if (buffer-live-p my/-sly-fontification-buffer)
 | 
						|
        my/-sly-fontification-buffer
 | 
						|
      (let ((buffer (generate-new-buffer
 | 
						|
                     (format " %s-fontification-buffer" (buffer-name)))))
 | 
						|
        (with-current-buffer buffer
 | 
						|
          (unless (derived-mode-p 'c++-mode)
 | 
						|
            (let ((delayed-mode-hooks nil))
 | 
						|
              (delay-mode-hooks
 | 
						|
                (lisp-mode)
 | 
						|
                (rainbow-delimiters-mode 1))))
 | 
						|
          (let ((inhibit-message t))
 | 
						|
            (indent-tabs-mode -1))
 | 
						|
          (unless font-lock-mode
 | 
						|
            (font-lock-mode 1)))
 | 
						|
        (setq-local my/-sly-fontification-buffer buffer))))
 | 
						|
  (defmacro my/-sly-with-font-lock-buffer (&rest body)
 | 
						|
    "Execute BODY in the sly indirect buffer.
 | 
						|
Note that this erases the buffer before doing anything."
 | 
						|
    `(with-current-buffer (my/-sly-get-fontification-buffer)
 | 
						|
       (erase-buffer)
 | 
						|
       ,@body))
 | 
						|
  (defun my/-sly-fontify-current-input ()
 | 
						|
    "Function called from `post-command-hook' to fontify the current input."
 | 
						|
    (let ((deactivate-mark nil))
 | 
						|
      (when-let ((proc (get-buffer-process (current-buffer)))
 | 
						|
                 (start (process-mark proc))
 | 
						|
                 (end (point-max))
 | 
						|
                 (input (buffer-substring-no-properties start end))
 | 
						|
                 (fontified (my/-sly-with-font-lock-buffer
 | 
						|
                             (insert input)
 | 
						|
                             (font-lock-ensure)
 | 
						|
                             (buffer-string)))
 | 
						|
                 (len (length fontified))
 | 
						|
                 (i 0))
 | 
						|
        ;; mostly from:
 | 
						|
        ;; `python-shell-font-lock-post-command-hook'
 | 
						|
        (while (not (= i len))
 | 
						|
          (let* ((props (text-properties-at i fontified))
 | 
						|
                 (change-i (or (next-property-change i fontified)
 | 
						|
                               len)))
 | 
						|
            (when-let ((face (plist-get props 'face)))
 | 
						|
              (setf (plist-get props 'face) nil
 | 
						|
                    (plist-get props 'font-lock-face) face))
 | 
						|
            (set-text-properties (+ start i) (+ start change-i) props)
 | 
						|
            (setq i change-i))))))
 | 
						|
  (defun my/-sly-cleanup-fontification-buffer ()
 | 
						|
    (when (buffer-live-p my/-sly-fontification-buffer)
 | 
						|
      (kill-buffer my/-sly-fontification-buffer)))
 | 
						|
  (defun my/-sly-mrepl-enable-fontification ()
 | 
						|
    (setq-local comint-highlight-input nil)
 | 
						|
    (add-hook 'post-command-hook #'my/-sly-fontify-current-input
 | 
						|
              nil t)
 | 
						|
    (add-hook 'kill-buffer-hook #'my/-sly-cleanup-fontification-buffer
 | 
						|
              nil t))
 | 
						|
  (add-hook 'sly-mrepl-mode-hook #'my/-sly-mrepl-enable-fontification))
 | 
						|
 | 
						|
;; inferior cc
 | 
						|
(require 'inferior-cc)
 | 
						|
(with-eval-after-load 'c-ts-mode
 | 
						|
  (keymap-set c-ts-base-mode-map "C-x C-e" #'inferior-cc-eval-expression)
 | 
						|
  (keymap-set c-ts-base-mode-map "C-c C-r" #'inferior-cc-eval-region)
 | 
						|
  (keymap-set c-ts-base-mode-map "C-c C-b" #'inferior-cc-eval-buffer)
 | 
						|
  (keymap-set c-ts-base-mode-map "C-M-x" #'inferior-cc-eval-defun))
 | 
						|
(with-eval-after-load 'java-ts-mode
 | 
						|
  (keymap-set java-ts-mode-map "C-x C-e" #'inferior-cc-eval-expression)
 | 
						|
  (keymap-set java-ts-mode-map "C-c C-r" #'inferior-cc-eval-region)
 | 
						|
  (keymap-set java-ts-mode-map "C-c C-b" #'inferior-cc-eval-buffer)
 | 
						|
  (keymap-set java-ts-mode-map "C-M-x" #'inferior-cc-eval-defun))
 | 
						|
 | 
						|
;; jupyter
 | 
						|
(use-package jupyter
 | 
						|
  :hook (jupyter-repl-mode . my/-setup-jupyter-mode)
 | 
						|
  :init
 | 
						|
  (defun my/-jupyter-dont-use-ts-modes (retval)
 | 
						|
    "Prevent `jupyter-kernel-language-mode-properties' from selecting TS modes."
 | 
						|
    (cl-destructuring-bind (mode syntax-table) retval
 | 
						|
      (if-let (((string-suffix-p "-ts-mode" (symbol-name mode)))
 | 
						|
               (non-ts (car (rassq mode major-mode-remap-alist)))
 | 
						|
               ((not (string-suffix-p "-ts-mode" (symbol-name non-ts)))))
 | 
						|
          (list non-ts
 | 
						|
                (if-let ((table-sym (intern-soft (format "%s-syntax-table"
 | 
						|
                                                         non-ts))))
 | 
						|
                    (symbol-value table-sym)
 | 
						|
                  syntax-table))
 | 
						|
        retval)))
 | 
						|
  (advice-add 'jupyter-kernel-language-mode-properties
 | 
						|
              :filter-return #'my/-jupyter-dont-use-ts-modes)
 | 
						|
  :config
 | 
						|
  ;; fix some bugs
 | 
						|
  (defun my/-fix-jupyter-org--set-src-block-cache (oldfun)
 | 
						|
    (unless jupyter-org--src-block-cache
 | 
						|
      (setq jupyter-org--src-block-cache
 | 
						|
            (list 'invalid nil (make-marker)
 | 
						|
                  (let ((end (make-marker)))
 | 
						|
                    ;; Move the end marker when text is inserted
 | 
						|
                    (set-marker-insertion-type end t)
 | 
						|
                    end))))
 | 
						|
    (funcall oldfun))
 | 
						|
  (advice-add 'jupyter-org--set-src-block-cache :around
 | 
						|
              #'my/-fix-jupyter-org--set-src-block-cache)
 | 
						|
 | 
						|
  (face-spec-set 'jupyter-repl-traceback
 | 
						|
                 '((default . (:background unspecified)))
 | 
						|
                 'face-override-spec)
 | 
						|
  (defun company-doc-buffer (&optional string)
 | 
						|
    "Emulate company's `company-doc-buffer'."
 | 
						|
    (with-current-buffer (get-buffer-create "*company-documentation*")
 | 
						|
      (erase-buffer)
 | 
						|
      (fundamental-mode)
 | 
						|
      (when string
 | 
						|
        (save-excursion
 | 
						|
          (insert string)
 | 
						|
          (visual-line-mode)))
 | 
						|
      (current-buffer)))
 | 
						|
  (defun my/-jupyter-kick-use-back-to-cell ()
 | 
						|
    "Kick the point out of the invisible read only area at the start of cells."
 | 
						|
    (let ((props (text-properties-at (point))))
 | 
						|
      (when (and (plist-get props 'invisible)
 | 
						|
                 (plist-get props 'read-only))
 | 
						|
        (forward-char))))
 | 
						|
  (defun my/-setup-jupyter-mode ()
 | 
						|
    "Setup `jupyter-repl-mode'."
 | 
						|
    (display-line-numbers-mode -1)
 | 
						|
    (add-hook 'post-command-hook #'my/-jupyter-kick-use-back-to-cell
 | 
						|
              nil t))
 | 
						|
  (cl-defmethod jupyter-indent-line (&context (jupyter-lang c++))
 | 
						|
    (let ((res (syntax-ppss (pos-bol))))
 | 
						|
      ;; no paren depth
 | 
						|
      (if (zerop (cl-first res))
 | 
						|
          (save-excursion
 | 
						|
            (back-to-indentation)
 | 
						|
            (delete-region (pos-bol) (point)))
 | 
						|
        (indent-for-tab-command)))))
 | 
						|
 | 
						|
;; C/C++ and jupyter
 | 
						|
(defvar my/jupyter-extra-language-associations
 | 
						|
  '(("c" . "c++")))
 | 
						|
 | 
						|
(defun my/-find-jupyter-buffer-for-lang (lang)
 | 
						|
  "Find a Jupyter buffer supporint LANG."
 | 
						|
  (let ((res (cl-find-if (lambda (buf)
 | 
						|
                           (with-current-buffer buf
 | 
						|
                             (and (derived-mode-p 'jupyter-repl-mode)
 | 
						|
                                  jupyter-current-client
 | 
						|
                                  (cl-equalp lang
 | 
						|
                                             (symbol-name
 | 
						|
                                              (jupyter-kernel-language
 | 
						|
                                               jupyter-current-client))))))
 | 
						|
                         (buffer-list))))
 | 
						|
    (when-let (((not res))
 | 
						|
               (real (alist-get lang my/jupyter-extra-language-associations nil
 | 
						|
                                nil #'cl-equalp)))
 | 
						|
      (setq res (my/-find-jupyter-buffer-for-lang real)))
 | 
						|
    res))
 | 
						|
 | 
						|
(defun my/-jupyter-buffer-for-major-mode (&optional mode)
 | 
						|
  "Return a Jupyter buffer that can evaluate the code MODE is editing.
 | 
						|
MODE defaults to `major-mode'."
 | 
						|
  (when-let ((name (symbol-name (or mode major-mode)))
 | 
						|
             ((string-match (rx bos (group (+? any)) (? "-ts") "-mode" eos)
 | 
						|
                            name)))
 | 
						|
    (my/-find-jupyter-buffer-for-lang (match-string 1 name))))
 | 
						|
 | 
						|
(defun my/-jupyter-find-proper-buffer ()
 | 
						|
  "Find the buffer for `my/jupyter-eval-in-proper-buffer'."
 | 
						|
  (if (and (derived-mode-p 'jupyter-repl-mode)
 | 
						|
           jupyter-current-client)
 | 
						|
      (current-buffer)
 | 
						|
    (my/-jupyter-buffer-for-major-mode major-mode)))
 | 
						|
 | 
						|
(defun my/jupyter-eval-in-proper-buffer (code &optional no-error)
 | 
						|
  "Eval CODE a buffer suitable for `major-mode'.
 | 
						|
If NO-ERROR is non-nil, signal an error if a buffer fails to be found.  If the
 | 
						|
current buffer is a Jupyter buffer, just use that."
 | 
						|
  (interactive (list (if-let ((buffer (my/-jupyter-find-proper-buffer)))
 | 
						|
                         (with-current-buffer buffer
 | 
						|
                           (jupyter-read-expression))
 | 
						|
                       (user-error "No Jupyter buffer found for mode: %s"
 | 
						|
                                   major-mode))))
 | 
						|
  (if-let ((buffer (my/-jupyter-find-proper-buffer)))
 | 
						|
      (with-current-buffer buffer
 | 
						|
        (let ((jupyter-repl-echo-eval-p t))
 | 
						|
          (jupyter-eval-string code)))
 | 
						|
    (unless no-error (user-error "No Jupyter buffer found for mode: %s"
 | 
						|
                                 major-mode))))
 | 
						|
 | 
						|
(defun my/jupyter-eval-defun ()
 | 
						|
  "Eval the defun under point by sending it to a Jupyter repl."
 | 
						|
  (interactive)
 | 
						|
  (if-let ((code (thing-at-point 'defun)))
 | 
						|
      (progn
 | 
						|
        (my/jupyter-eval-in-proper-buffer code)
 | 
						|
        (message "Evaluated defun"))
 | 
						|
    (user-error "Nothing to evaluate under point")))
 | 
						|
 | 
						|
(defun my/jupyter-eval-buffer ()
 | 
						|
  "Eval the current buffer by sending it to a Jupyter repl."
 | 
						|
  (interactive)
 | 
						|
  (my/jupyter-eval-in-proper-buffer (buffer-substring-no-properties
 | 
						|
                                     (point-min) (point-max)))
 | 
						|
  (message "Evaluated buffer"))
 | 
						|
 | 
						|
(defun my/rust-jupyter-eval-region (start end)
 | 
						|
  "Send the current buffer between START and END to a Jupyter repl."
 | 
						|
  (interactive "r")
 | 
						|
  (let ((code (buffer-substring-no-properties start end)))
 | 
						|
    (my/jupyter-eval-in-proper-buffer code)
 | 
						|
    (message "Evaluated region")))
 | 
						|
 | 
						|
(defun my/rust-ts-jupyter-eval-expression ()
 | 
						|
  "Eval the expression under point by sending it to a Jupyter repl."
 | 
						|
  (interactive nil rust-ts-mode)
 | 
						|
  (save-excursion
 | 
						|
    (let ((start (point)))
 | 
						|
      (back-to-indentation)
 | 
						|
      (unless (> (point) start)
 | 
						|
        (goto-char start)))
 | 
						|
    (if-let ((thing (treesit-thing-at-point "_" 'nested))
 | 
						|
             (code (treesit-node-text thing)))
 | 
						|
        (progn
 | 
						|
          (my/jupyter-eval-in-proper-buffer code)
 | 
						|
          (message "Evaluated: %s" code))
 | 
						|
      (user-error "Nothing to evaluate under point"))))
 | 
						|
 | 
						|
(with-eval-after-load 'rust-ts-mode
 | 
						|
  (keymap-set rust-ts-mode-map
 | 
						|
              "C-M-x" #'my/jupyter-eval-defun)
 | 
						|
  (keymap-set rust-ts-mode-map
 | 
						|
              "C-x C-e" #'my/rust-ts-jupyter-eval-expression)
 | 
						|
  (keymap-set rust-ts-mode-map
 | 
						|
              "C-c C-r" #'my/rust-jupyter-eval-region)
 | 
						|
  (keymap-set rust-ts-mode-map
 | 
						|
              "C-c C-b" #'my/jupyter-eval-buffer))
 | 
						|
 | 
						|
;; pdf-tools
 | 
						|
(use-package pdf-tools
 | 
						|
  :hook (pdf-view-mode . my/setup-pdf-view-mode)
 | 
						|
  :init
 | 
						|
  (setq pdf-misc-print-program-executable "lp")
 | 
						|
  (defun my/setup-pdf-view-mode ()
 | 
						|
    (display-line-numbers-mode -1)
 | 
						|
    (evil-define-key '(motion normal visual) 'local
 | 
						|
      (kbd "C-s") #'isearch-forward
 | 
						|
      (kbd "C-r") #'isearch-backward)
 | 
						|
    (setq-local cursor-type nil))
 | 
						|
  (pdf-tools-install))
 | 
						|
 | 
						|
;; doc view
 | 
						|
(use-package doc-view
 | 
						|
  :ensure nil
 | 
						|
  :hook (doc-view-mode . my/-setup-doc-view-mode)
 | 
						|
  :init
 | 
						|
  (defun my/-setup-doc-view-mode ()
 | 
						|
    (display-line-numbers-mode -1)
 | 
						|
    (evil-define-key '(motion normal visual) 'local
 | 
						|
      (kbd "C-s") #'isearch-forward
 | 
						|
      (kbd "C-r") #'isearch-backward)))
 | 
						|
 | 
						|
;; calc
 | 
						|
(use-package calc
 | 
						|
  :ensure nil
 | 
						|
  :bind (("C-c m" . quick-calc)
 | 
						|
         :map calc-mode-map
 | 
						|
         ("M-<tab>" . calc-roll-up)
 | 
						|
         ("M-TAB" . calc-roll-up))
 | 
						|
  :hook ((calc-mode calc-trail-mode) . my/setup-calc-calc-trail-mode)
 | 
						|
  :init
 | 
						|
  (defun my/setup-calc-calc-trail-mode ()
 | 
						|
    (setq-local doom-modeline-percent-position '()
 | 
						|
                truncate-partial-width-windows nil)
 | 
						|
    (visual-line-mode -1)
 | 
						|
    (display-line-numbers-mode -1)
 | 
						|
    (toggle-truncate-lines 1))
 | 
						|
  (setq mode-line-right-align-edge 'right-margin)
 | 
						|
  :config
 | 
						|
  (defun my/-window-dedicated-modeline-segment ()
 | 
						|
    (let ((dedicated (window-dedicated-p)))
 | 
						|
      (cond
 | 
						|
       ((eq dedicated t) "[SD]")
 | 
						|
       (dedicated "[D]"))))
 | 
						|
  (add-to-list 'mode-line-misc-info
 | 
						|
               '(:eval (my/-window-dedicated-modeline-segment)))
 | 
						|
  (evil-define-key '(normal visual motion) calc-edit-mode-map
 | 
						|
    (kbd "RET") 'calc-edit-return
 | 
						|
    (kbd "<return>") 'calc-edit-return)
 | 
						|
  (defun my/-calc-float-mode-string ()
 | 
						|
    (cl-destructuring-bind (mode prec) calc-float-format
 | 
						|
      (concat
 | 
						|
       (upcase-initials (symbol-name mode))
 | 
						|
       (unless (zerop prec)
 | 
						|
         (concat ": " (number-to-string prec))))))
 | 
						|
  (doom-modeline-def-segment calc
 | 
						|
    "Display calculator icons and info."
 | 
						|
    (concat
 | 
						|
     (doom-modeline-spc)
 | 
						|
     (when-let ((icon (doom-modeline-icon 'faicon "nf-fa-calculator" "🖩" "")))
 | 
						|
       (concat
 | 
						|
        (doom-modeline-display-icon icon)
 | 
						|
        (doom-modeline-vspc)))
 | 
						|
     (doom-modeline--buffer-simple-name)
 | 
						|
     (when (eq major-mode 'calc-mode)
 | 
						|
       (concat
 | 
						|
        (doom-modeline-spc)
 | 
						|
        (number-to-string calc-internal-prec)
 | 
						|
        (doom-modeline-spc)
 | 
						|
        (upcase-initials (symbol-name calc-angle-mode))
 | 
						|
        (doom-modeline-spc)
 | 
						|
        (my/-calc-float-mode-string)
 | 
						|
        (when calc-prefer-frac
 | 
						|
          (concat
 | 
						|
           (doom-modeline-spc)
 | 
						|
           "Frac"))
 | 
						|
        (cond
 | 
						|
         (calc-algebraic-mode
 | 
						|
          (concat
 | 
						|
           (doom-modeline-spc)
 | 
						|
           "Alg"))
 | 
						|
         (calc-incomplete-algebraic-mode
 | 
						|
          (concat
 | 
						|
           (doom-modeline-spc)
 | 
						|
           "IAlg"))))))))
 | 
						|
 | 
						|
;; sage (for when calc is not enough)
 | 
						|
(use-package sage-shell-mode
 | 
						|
  :demand
 | 
						|
  :bind ("C-c g" . my/run-sage)
 | 
						|
  :hook (sage-shell-mode . my/-setup-sage-shell-mode)
 | 
						|
  :init
 | 
						|
  (defun my/-setup-sage-shell-mode ()
 | 
						|
    (setq-local comint-dynamic-complete-functions
 | 
						|
                '(comint-c-a-p-replace-by-expanded-history)))
 | 
						|
  :config
 | 
						|
  (defun my/run-sage (p)
 | 
						|
    "Like `sage-shell:run-sage', but does not ask anything without a prefix
 | 
						|
argument."
 | 
						|
    (interactive "P")
 | 
						|
    (let ((sage-shell:ask-command-options p))
 | 
						|
      (funcall-interactively #'sage-shell:run-sage
 | 
						|
                             (sage-shell:read-command)))))
 | 
						|
 | 
						|
;; fricas (because I like calculators)
 | 
						|
(add-to-list 'load-path "/usr/lib/fricas/emacs")
 | 
						|
(use-package fricas
 | 
						|
  :ensure nil
 | 
						|
  :custom
 | 
						|
  (fricas-run-command "fricas -nosman")
 | 
						|
  :init
 | 
						|
  ;; Fix `fricas-mode' messing up `completion-at-point-functions'
 | 
						|
  (advice-add #'fricas-mode :around
 | 
						|
              #'(lambda (oldfun &rest r)
 | 
						|
                  (let ((temp-capfs))
 | 
						|
                    (let ((completion-at-point-functions '(t)))
 | 
						|
                      (apply oldfun r)
 | 
						|
                      (setq temp-capfs completion-at-point-functions))
 | 
						|
                    (setq-local completion-at-point-functions temp-capfs)))
 | 
						|
              '((name . "my/-fricas-fix-capfs")))
 | 
						|
  :config
 | 
						|
  (face-spec-set 'fricas-type-time '((t (:foreground unspecified
 | 
						|
                                         :background unspecified
 | 
						|
                                         :inherit font-lock-type-face))))
 | 
						|
  (face-spec-set 'fricas-message '((t (:foreground unspecified
 | 
						|
                                       :background unspecified
 | 
						|
                                       :inherit error))))
 | 
						|
  (face-spec-set 'fricas-undefined '((t (:foreground unspecified
 | 
						|
                                         :background unspecified
 | 
						|
                                         :inherit nerd-icons-lblue))))
 | 
						|
  (face-spec-set 'fricas-algebra '((t (:foreground unspecified
 | 
						|
                                       :background unspecified
 | 
						|
                                       :weight bold
 | 
						|
                                       :inherit fricas-prompt))))
 | 
						|
  (face-spec-set 'fricas-TeX '((t (:foreground "black"
 | 
						|
                                   :background "white"
 | 
						|
                                   :inherit fricas-prompt)))))
 | 
						|
 | 
						|
;; gnuplot (mostly for org-plot)
 | 
						|
(use-package gnuplot)
 | 
						|
 | 
						|
(defun my/dir-container-p (&optional dir)
 | 
						|
  "Return non-nil if DIR is a remote directory that is a container.
 | 
						|
Actually, return the method name."
 | 
						|
  (car (member (file-remote-p default-directory 'method)
 | 
						|
               '("docker" "podman" "kubernetes" "dockercp" "podmancp"
 | 
						|
                 "toolbox" "distrobox" "flatpak" "apptainer" "nspawn"))))
 | 
						|
(defun my/dir-distrobox-p (&optional dir)
 | 
						|
  "Return non-nil if DIR is a remote directory that is a distrobox container."
 | 
						|
  (let ((method (my/dir-container-p dir)))
 | 
						|
    (or (equal method "distrobox")
 | 
						|
        (and method
 | 
						|
             (let ((default-directory (or dir default-directory)))
 | 
						|
               (executable-find "distrobox-host-exec" t))))))
 | 
						|
(defun my/dir-sudo-p (&optional dir)
 | 
						|
  "Return non-nil if DIR is a remote directory that is sudo, doas, etc.."
 | 
						|
  (member (file-remote-p (or dir default-directory) 'method)
 | 
						|
          '("sudo" "doas" "su" "sudoedit")))
 | 
						|
(defun my/dir-really-remote-p (&optional dir)
 | 
						|
  "Return non-nil if DIR is a remote directory that is really remote."
 | 
						|
  (and (file-remote-p (or dir default-directory))
 | 
						|
       (not (my/dir-distrobox-p dir))
 | 
						|
       (not (my/dir-sudo-p dir))))
 | 
						|
 | 
						|
;; eat
 | 
						|
(use-package eat
 | 
						|
  :bind (("C-c V" . my/project-eat-or-default)
 | 
						|
         :map eat-mode-map
 | 
						|
         ("M-o" . ace-window)
 | 
						|
         :map eat-semi-char-mode-map
 | 
						|
         ("M-o" . ace-window)
 | 
						|
         :map eat-eshell-emacs-mode-map
 | 
						|
         ("M-o" . ace-window)
 | 
						|
         :map eat-eshell-semi-char-mode-map
 | 
						|
         ("M-o" . ace-window))
 | 
						|
  :hook
 | 
						|
  (eat-mode . my/-setup-eat-mode)
 | 
						|
  :init
 | 
						|
  (evil-define-key 'insert eat-semi-char-mode-map
 | 
						|
    (kbd "<escape>") #'eat-self-input
 | 
						|
    (kbd "C-S-n") #'evil-normal-state
 | 
						|
    (kbd "C-y") #'eat-yank
 | 
						|
    (kbd "C-u") #'universal-argument
 | 
						|
    (kbd "C-w") evil-window-map)
 | 
						|
  :config
 | 
						|
  (defun my/-setup-eat-mode ()
 | 
						|
    (visual-wrap-prefix-mode -1)
 | 
						|
    (visual-line-mode -1))
 | 
						|
 | 
						|
  ;; The below makes sure that the first time the ESC key is pressed, it does
 | 
						|
  ;; what it is supposed to
 | 
						|
  (add-hook 'eat--semi-char-mode-hook #'evil-normalize-keymaps)
 | 
						|
  (defun my/-evil-disable-cursor-in-eat-buffer (oldfun &rest r)
 | 
						|
    "Disable `evil--sw-refresh-cursor' in `eat-mode' buffers."
 | 
						|
    (when (or (not (derived-mode-p 'eat-mode))
 | 
						|
              (not (eq evil-state 'insert)))
 | 
						|
      (apply oldfun r)))
 | 
						|
  (advice-add 'evil--sw-refresh-cursor :around
 | 
						|
              #'my/-evil-disable-cursor-in-eat-buffer)
 | 
						|
  (defun my/-eat-update-cursor-on-tty (&rest r)
 | 
						|
    (etcc--evil-set-cursor))
 | 
						|
  (advice-add 'eat--set-cursor :after
 | 
						|
              #'my/-eat-update-cursor-on-tty)
 | 
						|
  (defun my/-eat-disable-evil-in-char-mode ()
 | 
						|
    (if eat--char-mode
 | 
						|
        (evil-local-mode -1)
 | 
						|
      (evil-local-mode 1)))
 | 
						|
  (add-hook 'eat--char-mode-hook #'my/-eat-disable-evil-in-char-mode)
 | 
						|
  ;; Evil fixes done
 | 
						|
 | 
						|
  (defun my/-eat-choose-good-term ()
 | 
						|
    (if (my/dir-really-remote-p)
 | 
						|
        "xterm-256color"
 | 
						|
      (eat-term-get-suitable-term-name)))
 | 
						|
  (setq eat-term-name #'my/-eat-choose-good-term)
 | 
						|
  (defun my/-eat-shell-for-cwd ()
 | 
						|
    "Return a good shell for CWD, or nil if the default shell should be used."
 | 
						|
    (cond
 | 
						|
     ((my/dir-container-p) "/bin/sh") ;; idk why zsh dosen't work
 | 
						|
     ((my/dir-really-remote-p) "/bin/sh")))
 | 
						|
  (defun my/project-eat (&optional arg prompt)
 | 
						|
    "Switch to or create a eat buffer in the current projects root."
 | 
						|
    (interactive (list current-prefix-arg t))
 | 
						|
    (if-let ((proj (project-current prompt))
 | 
						|
             (default-directory (project-root proj)))
 | 
						|
        (let ((eat-buffer-name (format "*eat for project %s*" default-directory)))
 | 
						|
          (eat (my/-eat-shell-for-cwd) arg))))
 | 
						|
  (defun my/project-eat-or-default (&optional arg)
 | 
						|
    "Open an eat for the current project, otherwise, open a normal eat."
 | 
						|
    (interactive "P")
 | 
						|
    (unless (my/project-eat arg)
 | 
						|
      (eat (my/-eat-shell-for-cwd) arg))))
 | 
						|
 | 
						|
;; eshell stuff
 | 
						|
(use-package eshell
 | 
						|
  :ensure nil
 | 
						|
  :defer nil
 | 
						|
  :hook ((eshell-load . eat-eshell-visual-command-mode)
 | 
						|
         (eshell-mode . eat-eshell-mode)
 | 
						|
         (eshell-mode . my/-eshell-mode-setup)
 | 
						|
         (eshell-directory-change . my/-eshell-maybe-setup-remote))
 | 
						|
  :bind (:map eshell-mode-map
 | 
						|
         ("TAB" . completion-at-point)
 | 
						|
         ("<tab>" . completion-at-point))
 | 
						|
  :custom
 | 
						|
  (eshell-history-append t)
 | 
						|
  :init
 | 
						|
  (defun my/-eshell-filter-alias-list ()
 | 
						|
    (cl-remove-if-not (lambda (elt)
 | 
						|
                        (or (string-match-p
 | 
						|
                             (rx bos
 | 
						|
                                 (or "clear" "find-file"
 | 
						|
                                     "ls" "la" "git"
 | 
						|
                                     (and "eshell/" (+ (not " "))))
 | 
						|
                                 (or " " eos))
 | 
						|
                             (cl-second elt))))
 | 
						|
                      eshell-command-aliases-list))
 | 
						|
 | 
						|
  (defun eshell/-captive-cd (&optional dir &rest _)
 | 
						|
    (cond
 | 
						|
     ((not dir)
 | 
						|
      (eshell/-captive-cd "~"))
 | 
						|
     ((or (not (file-remote-p default-directory))
 | 
						|
          (file-remote-p dir))
 | 
						|
      (eshell/cd dir))
 | 
						|
     ((file-name-absolute-p dir)
 | 
						|
      (eshell/cd (concat (file-remote-p default-directory) dir)))
 | 
						|
     (t
 | 
						|
      (eshell/cd dir))))
 | 
						|
 | 
						|
  (defvar-local my/-eshell-last-remote-system nil)
 | 
						|
  (defun my/-eshell-maybe-setup-remote (&optional force)
 | 
						|
    (when (or force (not (equal my/-eshell-last-remote-system
 | 
						|
                                (file-remote-p default-directory))))
 | 
						|
      (kill-local-variable 'eshell-syntax-highlighting-highlight-in-remote-dirs)
 | 
						|
      (if (my/dir-really-remote-p)
 | 
						|
          (setq-local eshell-command-aliases-list (my/-eshell-filter-alias-list))
 | 
						|
        (setq-local eshell-command-aliases-list
 | 
						|
                    (default-toplevel-value 'eshell-command-aliases-list)))
 | 
						|
      (setq-local eshell-command-aliases-list
 | 
						|
                  (copy-tree eshell-command-aliases-list))
 | 
						|
      (when (file-remote-p default-directory)
 | 
						|
        (add-to-list 'eshell-command-aliases-list '("cd" "-captive-cd $1") t))
 | 
						|
      (when (or (my/dir-distrobox-p)
 | 
						|
                (my/dir-sudo-p))
 | 
						|
        (setq-local eshell-syntax-highlighting-highlight-in-remote-dirs t)
 | 
						|
        (setf (alist-get "pwd" eshell-command-aliases-list nil nil 'equal)
 | 
						|
              '("(directory-file-name (file-remote-p default-directory 'localname))")))
 | 
						|
      (when (my/dir-distrobox-p)
 | 
						|
        (unless (executable-find "eza" t)
 | 
						|
          (if (executable-find "exa" t)
 | 
						|
              (setf (alist-get "ls" eshell-command-aliases-list nil nil 'equal)
 | 
						|
                    '("exa -F $*"))
 | 
						|
            (setf (alist-get "ls" eshell-command-aliases-list nil t 'equal)
 | 
						|
                  nil)))
 | 
						|
        (unless (executable-find "trash-put" t)
 | 
						|
          (setf (alist-get "tp" eshell-command-aliases-list nil t 'equal) nil
 | 
						|
                (alist-get "trr" eshell-command-aliases-list nil t 'equal) nil
 | 
						|
                (alist-get "tre" eshell-command-aliases-list nil t 'equal) nil
 | 
						|
                (alist-get "trm" eshell-command-aliases-list nil t 'equal) nil
 | 
						|
                (alist-get "rm" eshell-command-aliases-list nil t 'equal) nil))
 | 
						|
        (setf (alist-get "ldg" eshell-command-aliases-list nil t 'equal) nil)))
 | 
						|
    (setq-local my/-eshell-last-remote-system
 | 
						|
                (file-remote-p default-directory)))
 | 
						|
 | 
						|
  (defun my/-eshell-mode-setup ()
 | 
						|
    "Setup function run from `eshell-mode-hook'"
 | 
						|
    (setq-local corfu-auto nil)
 | 
						|
    (my/-eshell-maybe-setup-remote t)
 | 
						|
    (with-editor-export-editor))
 | 
						|
  (setq-default eshell-command-aliases-list
 | 
						|
                '(("clear" "clear t")
 | 
						|
                  ("e" "find-file $1")
 | 
						|
                  ("n" "find-file $1")
 | 
						|
                  ("emacs" "find-file $1")
 | 
						|
                  ("nvim" "find-file $1")
 | 
						|
                  ("ls" "eza --git -F $*")
 | 
						|
                  ("la" "ls -a $*")
 | 
						|
                  ("l" "ls -l $*")
 | 
						|
                  ("ll" "la -l $*")
 | 
						|
                  ("gt" "git status $*")
 | 
						|
                  ("gp" "git push $*")
 | 
						|
                  ("gu" "git pull $*")
 | 
						|
                  ("gf" "git fetch $*")
 | 
						|
                  ("ga" "git add $*")
 | 
						|
                  ("gcm" "git commit -m ${string-join $* \" \"}")
 | 
						|
                  ("ldg" "ledger -f \"$HOME/docs/finance/finances.ledger\" $*")
 | 
						|
                  ("tp" "trash-put $*")
 | 
						|
                  ("trr" "trash-restore $*")
 | 
						|
                  ("tre" "trash-empty $*")
 | 
						|
                  ("trm" "trash-rm $*")
 | 
						|
                  ("rm" "echo 'rm: I''m unsafe! Don''t use me.'; false")
 | 
						|
                  ("\\rm" "eshell/rm")))
 | 
						|
 | 
						|
  (defvar my/eshell-bm-auto-ls t
 | 
						|
    "Weather or not to run ls after `eshell/bm'")
 | 
						|
  (defun eshell/bm (&optional name)
 | 
						|
    "Change to directory of bookmark NAME.
 | 
						|
If no name is given, list all bookmarks instead."
 | 
						|
    (if name
 | 
						|
        (progn
 | 
						|
          (string-match (rx bos (group (* (not "/"))) (* "/") (group (* any)))
 | 
						|
                        name)
 | 
						|
          (let* ((bm-name (match-string 1 name))
 | 
						|
                 (after-path (match-string 2 name))
 | 
						|
                 (bm-path (bookmark-get-filename bm-name))
 | 
						|
                 (full-path (expand-file-name after-path bm-path)))
 | 
						|
            (when (my/dir-distrobox-p)
 | 
						|
              (setq full-path (concat (file-remote-p default-directory)
 | 
						|
                                      full-path)))
 | 
						|
            (if (not (file-directory-p full-path))
 | 
						|
                (progn
 | 
						|
                  (find-file full-path)
 | 
						|
                  (goto-char (bookmark-get-position bm-name)))
 | 
						|
              (eshell/cd full-path)
 | 
						|
              (when my/eshell-bm-auto-ls
 | 
						|
                (eshell/ls)))))
 | 
						|
      (bookmark-maybe-load-default-file)
 | 
						|
      (eshell-print
 | 
						|
       (mapconcat (lambda (record)
 | 
						|
                    (let ((name (bookmark-name-from-full-record record))
 | 
						|
                          (file (bookmark-get-filename record)))
 | 
						|
                      (format "%s => %s"
 | 
						|
                              (propertize name 'face '(:foreground "deep sky blue"
 | 
						|
                                                       :weight bold))
 | 
						|
                              (if (file-directory-p file)
 | 
						|
                                  (file-name-as-directory file)
 | 
						|
                                (directory-file-name file)))))
 | 
						|
                  bookmark-alist
 | 
						|
                  "\n"))))
 | 
						|
  (defun pcomplete/bm ()
 | 
						|
    "Completions for `bm'."
 | 
						|
    (let ((arg (pcomplete-arg)))
 | 
						|
      (if (not (cl-find ?/ arg))
 | 
						|
          (pcomplete-here (mapcar (##concat % "/") (bookmark-all-names)))
 | 
						|
        (when (string-match (rx bos (group (+ (not "/"))) (+ "/") (group (* any)))
 | 
						|
                            arg)
 | 
						|
          (let ((bm-name (match-string 1 arg))
 | 
						|
                (after-path (match-string 2 arg)))
 | 
						|
            (when-let ((base (ignore-errors (bookmark-get-filename bm-name)))
 | 
						|
                       ((file-directory-p base))
 | 
						|
                       (abs-path (expand-file-name after-path base))
 | 
						|
                       (dir-path (if (string-empty-p after-path)
 | 
						|
                                     abs-path
 | 
						|
                                   (file-name-directory abs-path)))
 | 
						|
                       (path-end (if (string-empty-p after-path)
 | 
						|
                                     ""
 | 
						|
                                   (file-name-nondirectory abs-path))))
 | 
						|
              (pcomplete-here
 | 
						|
               (mapcan (lambda (entry)
 | 
						|
                         (unless (member (car entry) '(".." "."))
 | 
						|
                           (if (eq t (file-attribute-type (cdr entry)))
 | 
						|
                               (list (concat (car entry) "/"))
 | 
						|
                             (list (car entry)))))
 | 
						|
                       (directory-files-and-attributes dir-path))
 | 
						|
               path-end))))))))
 | 
						|
(use-package esh-help
 | 
						|
  :hook (eshell-mode . my/-setup-eshell-help-func)
 | 
						|
  :init
 | 
						|
  (defun my/-setup-eshell-help-func ()
 | 
						|
    (eldoc-mode 1)
 | 
						|
    (setq-local evil-lookup-func #'esh-help-run-help))
 | 
						|
  (setup-esh-help-eldoc))
 | 
						|
(use-package eshell-syntax-highlighting
 | 
						|
  :init
 | 
						|
  (eshell-syntax-highlighting-global-mode 1))
 | 
						|
(use-package eshell-starship
 | 
						|
  :ensure nil
 | 
						|
  :demand t
 | 
						|
  :hook (eshell-prompt-mode . eshell-starship-prompt-mode)
 | 
						|
  :config
 | 
						|
  (eshell-starship-setup-evil-keybindings)
 | 
						|
  (set-face-attribute 'eshell-starship-icon-face nil
 | 
						|
                      :family "FiraCode Nerd Font"))
 | 
						|
 | 
						|
(defvar my/eshell-or-eat-hook nil
 | 
						|
  "Hook to determine weather `my/open-shell-dwin' uses `eshell' or `eat'.")
 | 
						|
(defvar my/always-use-eat nil
 | 
						|
  "Make `my/open-shell-dwim' always use eat.")
 | 
						|
(put 'my/always-use-eat 'safe-local-variable 'booleanp)
 | 
						|
(defun my/open-shell-dwim (&optional arg)
 | 
						|
  "Open either an `eshell' or `eat' terminal based on `my/eshell-or-eat-hook'.
 | 
						|
ARG is the same as for either of the above functions."
 | 
						|
  (interactive "P")
 | 
						|
  (if (or my/always-use-eat
 | 
						|
          (run-hook-with-args-until-success 'my/eshell-or-eat-hook))
 | 
						|
      (my/project-eat-or-default arg)
 | 
						|
    (my/project-eshell-or-default arg)))
 | 
						|
(keymap-global-set "C-c v" #'my/open-shell-dwim)
 | 
						|
 | 
						|
;; proced
 | 
						|
(use-package proced
 | 
						|
  :bind ("C-x j" . proced)
 | 
						|
  :init
 | 
						|
  (evil-define-key '(motion visual normal) proced-mode-map
 | 
						|
    "u" 'proced-unmark)
 | 
						|
  (setq proced-auto-update-flag t
 | 
						|
        proced-auto-update-interval 1)
 | 
						|
  (defun my/-setup-proced-mode ()
 | 
						|
    (visual-line-mode -1)
 | 
						|
    (setq-local truncate-lines t))
 | 
						|
  (add-hook 'proced-mode-hook 'my/-setup-proced-mode))
 | 
						|
 | 
						|
;; dired
 | 
						|
(use-package dired
 | 
						|
  :ensure nil
 | 
						|
  :custom
 | 
						|
  (dired-listing-switches
 | 
						|
   "-l --almost-all --human-readable --group-directories-first --no-group")
 | 
						|
  (dired-hide-details-hide-symlink-targets nil)
 | 
						|
  :init
 | 
						|
  (setq-default dired-kill-when-opening-new-dired-buffer t)
 | 
						|
  (setq delete-by-moving-to-trash t
 | 
						|
        dired-recursive-copies 'always
 | 
						|
        dired-recursive-deletes 'always
 | 
						|
        dired-dwim-target t
 | 
						|
        dired-create-destination-dirs 'ask
 | 
						|
        dired-create-destination-dirs-on-trailing-dirsep t
 | 
						|
        dired-isearch-filenames 'dwim
 | 
						|
        dired-do-revert-buffer (lambda (dir)
 | 
						|
                                 (not (file-remote-p dir)))
 | 
						|
        dired-clean-up-buffers-too t
 | 
						|
        dired-clean-confirm-killing-deleted-buffers t)
 | 
						|
  (evil-define-key '(normal visual motion) dired-mode-map
 | 
						|
    "u" #'dired-unmark
 | 
						|
    "U" #'dired-unmark-all-marks))
 | 
						|
 | 
						|
;; dirvish
 | 
						|
(use-package dirvish
 | 
						|
  :defer nil
 | 
						|
  :bind (("C-c b" . dirvish-quick-access)
 | 
						|
         :map dirvish-mode-map
 | 
						|
         ("<mouse-1>" . dirvish-subtree-toggle-or-open)
 | 
						|
         ("<mouse-2>" . dired-mouse-find-file-other-window)
 | 
						|
         ("<mouse-3>" . dired-mouse-find-file))
 | 
						|
  :hook (((dirvish-directory-view-mode dired-mode dirvish-mode) .
 | 
						|
          my/-setup-dirvish-lines)
 | 
						|
         ;; ((dirvish-directory-view-mode dired-mode dirvish-mode) .
 | 
						|
         ;;  auto-revert-mode)
 | 
						|
         ((dirvish-mode dired-mode) . my/-setup-dirvish-mouse))
 | 
						|
  :custom
 | 
						|
  (dirvish-subtree-always-show-state t)
 | 
						|
  (dirvish-reuse-session t)
 | 
						|
  (dirvish-quick-access-function 'dired)
 | 
						|
  :init
 | 
						|
  (defun my/-setup-dirvish-lines ()
 | 
						|
    (setq-local truncate-lines t))
 | 
						|
  (defun my/-setup-dirvish-mouse ()
 | 
						|
    (setq-local mouse-1-click-follows-link nil
 | 
						|
                mouse-1-click-in-non-selected-windows nil))
 | 
						|
  (defvar my/-dirvish-base-quick-access-entries
 | 
						|
    `(("h" "~/" "Home")
 | 
						|
      ("d" "~/downloads/" "Downloads")
 | 
						|
      ("e" ,user-emacs-directory "Emacs user directory")
 | 
						|
      ("z" "~/.config/zsh/" "Zsh user directory")
 | 
						|
      ("o" "~/docs/" "Documents")
 | 
						|
      ("w." "~/workspace/" "Workspace")))
 | 
						|
  (defun my/-dirvish-build-quick-access-entries (bookmarks)
 | 
						|
    ;; NOTE called from a variable watcher for `bookmark-alist' and so must
 | 
						|
    ;; never set that variable
 | 
						|
    (let (out)
 | 
						|
      (dolist (bme bookmarks
 | 
						|
                   (append my/-dirvish-base-quick-access-entries
 | 
						|
                           (sort out :key 'car)))
 | 
						|
        (let ((name (car bme)))
 | 
						|
          (let-alist (cdr bme)
 | 
						|
            (when (and (file-directory-p .filename)
 | 
						|
                       (string-match (rx bos (group (any "a-z" "A-Z")) "ws" eos)
 | 
						|
                                     name))
 | 
						|
              (setf (alist-get (concat "w" (match-string 1 name)) out
 | 
						|
                               nil nil 'equal)
 | 
						|
                    (list .filename
 | 
						|
                          (concat
 | 
						|
                           (capitalize
 | 
						|
                            (car (last (string-split .filename "/" t))))
 | 
						|
                           " Workspace")))))))))
 | 
						|
  :config
 | 
						|
  (require 'dirvish-extras)
 | 
						|
  (defun my/-dirvish-bookmark-alist-watcher (_sym newval oper where)
 | 
						|
    (when (and (not where) (memq oper '(set makunbound defvaralias)))
 | 
						|
      (setopt dirvish-quick-access-entries
 | 
						|
              (my/-dirvish-build-quick-access-entries newval))))
 | 
						|
  (add-variable-watcher 'bookmark-alist #'my/-dirvish-bookmark-alist-watcher)
 | 
						|
  (defvar-local my/-dirvish-uid-name-cache nil
 | 
						|
    "Cons of path and a hash table mapping user ids to their names.")
 | 
						|
  (dirvish-define-attribute file-owner-mode
 | 
						|
    "The file's owner and mode."
 | 
						|
    :index 2
 | 
						|
    :when (and (dirvish-prop :root) dired-hide-details-mode
 | 
						|
               (> win-width 60))
 | 
						|
    (let ((root (dirvish-prop :root))
 | 
						|
          (uid (file-attribute-user-id f-attrs)))
 | 
						|
      (unless (or (dirvish-prop :remote) (stringp uid))
 | 
						|
        (unless (and (equal root (car my/-dirvish-uid-name-cache))
 | 
						|
                     (hash-table-p (cdr my/-dirvish-uid-name-cache)))
 | 
						|
          (setq my/-dirvish-uid-name-cache
 | 
						|
                (cons root (make-hash-table :test 'equal))))
 | 
						|
        (if-let ((name (gethash uid (cdr my/-dirvish-uid-name-cache))))
 | 
						|
            (setq uid name)
 | 
						|
          (let* ((new-attrs (file-attributes f-name 'string))
 | 
						|
                 (new-name (file-attribute-user-id new-attrs)))
 | 
						|
            (puthash uid new-name
 | 
						|
                     (cdr my/-dirvish-uid-name-cache))
 | 
						|
            (setq uid new-name))))
 | 
						|
      (cons 'right (propertize (format " %s %s" uid (file-attribute-modes f-attrs))
 | 
						|
                               'face (or hl-face 'dirvish-file-time)))))
 | 
						|
  (let ((cur-val dirvish-ui-setup-items))
 | 
						|
    (cl-pushnew '("o" file-owner-mode "File owner and mode")
 | 
						|
                cur-val :test 'equal)
 | 
						|
    (setopt dirvish-ui-setup-items cur-val))
 | 
						|
  (add-to-list 'dirvish--libraries '(dirvish file-owner-mode))
 | 
						|
  (setopt dirvish-attributes
 | 
						|
          '(vc-state subtree-state nerd-icons file-size file-owner-mode))
 | 
						|
  (evil-define-key 'normal dirvish-mode-map
 | 
						|
    (kbd "q") #'dirvish-quit
 | 
						|
    (kbd "a") #'dirvish-quick-access
 | 
						|
    (kbd "f") #'dirvish-file-info-menu
 | 
						|
    (kbd "y") #'dirvish-yank-menu
 | 
						|
    (kbd "N") #'dirvish-narrow
 | 
						|
    (kbd "^") #'dirvish-history-last
 | 
						|
    (kbd "h") #'dirvish-history-jump
 | 
						|
    (kbd "s") #'dirvish-quicksort
 | 
						|
    (kbd "o") #'dirvish-quicksort
 | 
						|
    (kbd "v") #'dirvish-vc-menu
 | 
						|
    (kbd "TAB") #'dirvish-subtree-toggle
 | 
						|
    (kbd "M-f") #'dirvish-history-go-forward
 | 
						|
    (kbd "M-b") #'dirvish-history-go-backward
 | 
						|
    (kbd "M-l") #'dirvish-history-go-forward
 | 
						|
    (kbd "M-h") #'dirvish-history-go-backward
 | 
						|
    (kbd "M-l") #'dirvish-ls-switches-menu
 | 
						|
    (kbd "M-m") #'dirvish-mark-menu
 | 
						|
    (kbd "M-t") #'dirvish-layout-toggle
 | 
						|
    (kbd "M-s") #'dirvish-setup-menu
 | 
						|
    (kbd "M-e") #'dirvish-emerge-menu
 | 
						|
    (kbd "M-j") #'dirvish-fd-jump
 | 
						|
    (kbd "/") #'dirvish-fd
 | 
						|
    (kbd "?") #'dirvish-dispatch)
 | 
						|
  (dirvish-override-dired-mode)
 | 
						|
  (dirvish-define-preview eza (file)
 | 
						|
    "Use `eza' to generate directory preview."
 | 
						|
    :require ("eza")
 | 
						|
    (when (file-directory-p file)
 | 
						|
      `(shell . ("eza" "-la" "--color=always" "--icons"
 | 
						|
                 "--group-directories-first" ,file))))
 | 
						|
  (add-to-list 'dirvish-preview-dispatchers 'eza))
 | 
						|
 | 
						|
;; trashed
 | 
						|
(use-package trashed
 | 
						|
  :bind ("C-c h" . trashed))
 | 
						|
 | 
						|
;; ibuffer
 | 
						|
(use-package ibuffer
 | 
						|
  :bind ("C-x C-b" . ibuffer))
 | 
						|
 | 
						|
;; magit
 | 
						|
(use-package magit
 | 
						|
  :init
 | 
						|
  (defvar-keymap my/magit-personal-prefix-map
 | 
						|
    :doc "Keymap for some useful Magit commands."
 | 
						|
    "s" #'magit-stage
 | 
						|
    "d" #'magit-diff-dwim
 | 
						|
    "D" #'magit-diff
 | 
						|
    "b" #'magit-blame)
 | 
						|
  (keymap-global-set "C-c i" my/magit-personal-prefix-map)
 | 
						|
  (evil-define-key '(normal visual motion) magit-mode-map
 | 
						|
    "s" #'magit-stage
 | 
						|
    "S" #'magit-stage-modified))
 | 
						|
;; (use-package forge
 | 
						|
;;   :custom
 | 
						|
;;   (forge-add-default-bindings nil)
 | 
						|
;;   :config
 | 
						|
;;   (add-to-list 'forge-alist '("git.zander.im" "git.zander.im/api/v1"
 | 
						|
;;                               "git.zander.im" forge-gitea-repository)))
 | 
						|
 | 
						|
;; org-mode
 | 
						|
(use-package org
 | 
						|
  :pin gnu
 | 
						|
  :bind (("C-c c" . org-capture)
 | 
						|
         ("C-c a" . org-agenda)
 | 
						|
         ("C-c l" . org-store-link)
 | 
						|
         :map org-mode-map
 | 
						|
         ("C-c t" . org-table-create))
 | 
						|
  :hook (org-mode . org-table-header-line-mode)
 | 
						|
  :init
 | 
						|
  (font-lock-add-keywords 'org-mode
 | 
						|
                          `((,(rx bol (* " ") (group "-") " ")
 | 
						|
                             (0 (prog1 nil
 | 
						|
                                  (compose-region (match-beginning 1)
 | 
						|
                                                  (match-end 1) "•"))))))
 | 
						|
  (defun my/-check-org-babel-eval-no-confirm (&rest args)
 | 
						|
    (not (trusted-content-p)))
 | 
						|
  (setq org-directory "~/org"
 | 
						|
        org-agenda-files '("~/org/")
 | 
						|
        org-confirm-babel-evaluate 'my/-check-org-babel-eval-no-confirm
 | 
						|
        org-log-into-drawer t
 | 
						|
        org-log-done 'time
 | 
						|
        org-log-redeadline 'time
 | 
						|
        org-log-reschedule 'time
 | 
						|
        org-preview-latex-default-process 'dvisvgm
 | 
						|
        org-highlight-latex-and-related '(native entities)
 | 
						|
        org-startup-with-inline-images t
 | 
						|
        org-adapt-indentation t
 | 
						|
        org-hide-leading-stars t
 | 
						|
        org-html-with-latex 'dvisvgm
 | 
						|
        org-preview-latex-process-alist
 | 
						|
        '((dvisvgm
 | 
						|
           :image-input-type "dvi"
 | 
						|
           :image-output-type "svg"
 | 
						|
           :image-size-adjust (1.7 . 1.5)
 | 
						|
           :latex-compiler ("pdflatex -interaction nonstopmode -output-format=dvi -output-directory=%o %f")
 | 
						|
           :image-converter ("dvisvgm %o%b.dvi --no-fonts --exact-bbox --scale=%S --output=%O"))))
 | 
						|
  (defun my/-org-allow-in-derived-mode (oldfun &rest r)
 | 
						|
    "Allow OLDFUN to run, even if `major-mode' is only derived from `org-mode'.
 | 
						|
R is rest of the arguments to OLDFUN."
 | 
						|
    (let ((major-mode (if (derived-mode-p 'org-mode)
 | 
						|
                          'org-mode
 | 
						|
                        major-mode)))
 | 
						|
      (apply oldfun r)))
 | 
						|
  (advice-add 'org-element-at-point :around 'my/-org-allow-in-derived-mode)
 | 
						|
  (advice-add 'org-table-header-line-mode :around
 | 
						|
              'my/-org-allow-in-derived-mode)
 | 
						|
  :config
 | 
						|
  ;; org-babel stuff
 | 
						|
  (org-babel-do-load-languages
 | 
						|
   'org-babel-load-languages
 | 
						|
   '((emacs-lisp . t)
 | 
						|
     (python . t)
 | 
						|
     (jupyter . t)
 | 
						|
     (shell . t)))
 | 
						|
  (setq org-babel-default-header-args:jupyter-python
 | 
						|
        '((:kernel . "python") (:session . "org-babel-python") (:async . "yes")))
 | 
						|
  (org-babel-jupyter-override-src-block "python"))
 | 
						|
(use-package evil-org
 | 
						|
  :after org
 | 
						|
  :hook (org-mode . evil-org-mode)
 | 
						|
  :init
 | 
						|
  (require 'evil-org-agenda)
 | 
						|
  (evil-org-agenda-set-keys))
 | 
						|
 | 
						|
;; ledger
 | 
						|
(use-package ledger-mode)
 | 
						|
(use-package flycheck-ledger
 | 
						|
  :hook (ledger-mode . my/flycheck-if-trusted))
 | 
						|
 | 
						|
;; khard contacts
 | 
						|
(require 'khard)
 | 
						|
 | 
						|
;; This is also in khard (see above), it's just also here so that if I remove
 | 
						|
;; that file ever, other things will not break.
 | 
						|
(defun my/message-in-header-p (name &optional testfn)
 | 
						|
  "If in field NAME, return the start of the header, otherwise, return nil.
 | 
						|
The name is compared with the field name using TESTFN (defaults to `equal')."
 | 
						|
  (save-excursion
 | 
						|
    (when (and (message-point-in-header-p)
 | 
						|
               (message-beginning-of-header t))
 | 
						|
      (beginning-of-line)
 | 
						|
      (when (and (looking-at (rx bol (group (+? any)) ":" (? " ")))
 | 
						|
                 (funcall (or testfn 'equal) (match-string 1) name))
 | 
						|
        (match-end 0)))))
 | 
						|
 | 
						|
;; mu4e
 | 
						|
(use-package mu4e
 | 
						|
  :ensure nil
 | 
						|
  :defer nil
 | 
						|
  :hook ((mu4e-index-updated . my/-mu4e-enable-index-messages)
 | 
						|
         (mu4e-main-mode . my/-mu4e-setup-main-mode)
 | 
						|
         (mu4e-view-mode . my/-mu4e-setup-view-mode)
 | 
						|
         (mu4e-compose-mode . my/-mu4e-setup-compose-mode))
 | 
						|
  :bind (("C-x C-m" . mu4e)
 | 
						|
         :map message-mode-map
 | 
						|
         ("C-c k" . khard-insert-email-contact))
 | 
						|
  :init
 | 
						|
  (require 'mu4e)
 | 
						|
  (evil-define-key '(normal motion) mu4e-main-mode-map "q" #'bury-buffer)
 | 
						|
  (evil-define-key '(normal motion) mu4e-view-mode-map "gy" #'mu4e-view-save-url)
 | 
						|
  (defun my/-mu4e-setup-view-mode ()
 | 
						|
    (setq-local global-hl-line-mode nil))
 | 
						|
  (defun my/-mu4e-setup-main-mode ()
 | 
						|
    (setq-local default-directory "~/"))
 | 
						|
  (defun my/-mu4e-enable-index-messages ()
 | 
						|
    (setq mu4e-hide-index-messages nil))
 | 
						|
  (defun my/mu4e-update-mail-and-index-silent ()
 | 
						|
    "Run `mu4e-update-mail-and-index' without any messages in the background."
 | 
						|
    (setq mu4e-hide-index-messages t)
 | 
						|
    (mu4e-update-mail-and-index t))
 | 
						|
  (defun my/-mu4e-enable-autocomplete-in-header ()
 | 
						|
    ;; corfu auto must be t (not the integer returned by
 | 
						|
    ;; `my/message-in-header-p')
 | 
						|
    (setq-local corfu-auto (and (not (window-minibuffer-p))
 | 
						|
                                (my/message-in-header-p "To")
 | 
						|
                                t)))
 | 
						|
  (defun my/-mu4e-setup-compose-mode ()
 | 
						|
    (add-hook 'post-command-hook 'my/-mu4e-enable-autocomplete-in-header
 | 
						|
              nil t)
 | 
						|
    (add-to-list
 | 
						|
     (make-local-variable 'completion-at-point-functions)
 | 
						|
     (cape-capf-super #'mu4e-complete-contact #'khard-message-mode-capf)))
 | 
						|
  (defun my/-mu4e-fix-cycle-threshold ()
 | 
						|
    (setq-local completion-cycle-threshold nil))
 | 
						|
  (advice-add 'mu4e--compose-setup-completion :after
 | 
						|
              'my/-mu4e-fix-cycle-threshold)
 | 
						|
  (defvar my/mu4e-inbox-query
 | 
						|
    (concat "maildir:/protonmail/Inbox or maildir:/ucsc-gmail/Inbox"))
 | 
						|
  (defvar my/mu4e-interesting-mail-query
 | 
						|
    (concat "flag:unread AND NOT flag:trashed AND NOT "
 | 
						|
            "maildir:/protonmail/Trash AND NOT maildir:/protonmail/Spam AND NOT "
 | 
						|
            "maildir:/ucsc-gmail/[Gmail]/Trash AND NOT "
 | 
						|
            "maildir:/ucsc-gmail/[Gmail]/Spam")
 | 
						|
    "Flag for mail which will appear as \"unread\" and will be notified.")
 | 
						|
  (setq message-kill-buffer-on-exit t
 | 
						|
        message-confirm-send t
 | 
						|
        sendmail-program "/usr/bin/msmtp"
 | 
						|
        message-sendmail-f-is-evil t
 | 
						|
        send-mail-function 'smtpmail-send-it
 | 
						|
        message-send-mail-function 'message-send-mail-with-sendmail
 | 
						|
        mu4e-trash-without-flag t
 | 
						|
        mu4e-change-filenames-when-moving t
 | 
						|
        mu4e-context-policy 'pick-first
 | 
						|
        mu4e-attachment-dir "~/downloads/"
 | 
						|
        mu4e-last-update-buffer " *mu4e-last-update*"
 | 
						|
        mu4e-index-update-error-warning nil
 | 
						|
        mu4e-get-mail-command "mbsync -a"
 | 
						|
        mu4e-completing-read-function #'completing-read-default
 | 
						|
        mu4e-compose-context-policy 'ask-if-none
 | 
						|
        mu4e-bookmarks `((:name "Inbox"
 | 
						|
                          :query ,my/mu4e-inbox-query
 | 
						|
                          :key ?i)
 | 
						|
                         (:name "Unread"
 | 
						|
                          :query ,my/mu4e-interesting-mail-query
 | 
						|
                          :key ?u))
 | 
						|
        mu4e-contexts
 | 
						|
        (list (make-mu4e-context
 | 
						|
               :name "Personal"
 | 
						|
               :match-func (lambda (msg)
 | 
						|
                             (when msg
 | 
						|
                               (string-match-p "^/protonmail/"
 | 
						|
                                               (mu4e-message-field msg
 | 
						|
                                                                   :maildir))))
 | 
						|
               :vars `((user-mail-address . ,(my/get-private 'mu4e-email))
 | 
						|
                       (user-full-name . ,(my/get-private 'mu4e-name))
 | 
						|
                       (message-sendmail-extra-arguments . ("-a" "protonmail"))
 | 
						|
                       (message-signature . nil)
 | 
						|
                       (mu4e-refile-folder . "/protonmail/Archive")
 | 
						|
                       (mu4e-sent-folder . "/protonmail/Sent")
 | 
						|
                       (mu4e-drafts-folder . "/protonmail/Drafts")
 | 
						|
                       (mu4e-trash-folder . "/protonmail/Trash")))
 | 
						|
              (make-mu4e-context
 | 
						|
               :name "School"
 | 
						|
               :match-func (lambda (msg)
 | 
						|
                             (when msg
 | 
						|
                               (string-match-p "^/ucsc-gmail/"
 | 
						|
                                               (mu4e-message-field msg
 | 
						|
                                                                   :maildir))))
 | 
						|
               :vars `((user-mail-address . ,(my/get-private 'mu4e-email-school))
 | 
						|
                       (user-full-name . ,(my/get-private 'mu4e-name))
 | 
						|
                       (message-sendmail-extra-arguments . ("-a" "ucsc-gmail"))
 | 
						|
                       (message-signature . nil)
 | 
						|
                       (mu4e-refile-folder . "/protonmail/[Gmail]/All Mail")
 | 
						|
                       (mu4e-sent-folder . "/ucsc-gmail/[Gmail]/Sent Mail")
 | 
						|
                       (mu4e-drafts-folder . "/ucsc-gmail/[Gmail]/Drafts")
 | 
						|
                       (mu4e-trash-folder . "/ucsc-gmail/[Gmail]/Trash"))))))
 | 
						|
(use-package mu4e-alert
 | 
						|
  :after mu4e
 | 
						|
  :hook (after-init . mu4e-alert-enable-notifications)
 | 
						|
  :init
 | 
						|
  (setq mu4e-alert-set-window-urgency nil
 | 
						|
        mu4e-alert-interesting-mail-query my/mu4e-interesting-mail-query)
 | 
						|
  :config
 | 
						|
  (mu4e-alert-set-default-style 'libnotify))
 | 
						|
(mu4e t)
 | 
						|
(mu4e-context-switch nil "Personal")
 | 
						|
;; refresh the eww message count
 | 
						|
(defun my/-mu4e-eww-refresh-unread-count ()
 | 
						|
  "Refresh the eww unread message count.
 | 
						|
This will also update waybar."
 | 
						|
  (my/eww-poll-variables "mu4e")
 | 
						|
  (call-process "pkill" nil 0 nil "-RTMIN+2" "waybar"))
 | 
						|
(add-hook 'mu4e-message-changed-hook #'my/-mu4e-eww-refresh-unread-count)
 | 
						|
 | 
						|
;; mu4e compose HTML messages
 | 
						|
(use-package org-mime)
 | 
						|
(require 'org-mu4e-compose)
 | 
						|
(setq ;; mail-user-agent 'org-mu4e-user-agent
 | 
						|
 org-mime-org-html-with-latex-default 'dvisvgm
 | 
						|
 org-mime-export-options '(:with-latex dvisvgm :with-footnotes t))
 | 
						|
;; (evil-define-key '(normal visual) org-mu4e-compose-mode-map
 | 
						|
;;   "G" #'mu4e-compose-goto-bottom
 | 
						|
;;   "gg" #'mu4e-compose-goto-top)
 | 
						|
;; (evil-define-key 'normal org-mu4e-compose-mode-map
 | 
						|
;;   "ZZ" #'message-send-and-exit
 | 
						|
;;   "ZD" #'message-dont-send
 | 
						|
;;   "ZQ" #'message-kill-buffer
 | 
						|
;;   "ZF" #'mml-attach-file)
 | 
						|
;; (evil-define-key 'normal mu4e-view-mode-map
 | 
						|
;;   "R" 'org-mu4e-compose-reply
 | 
						|
;;   "cr" 'org-mu4e-compose-reply)
 | 
						|
;; (evil-define-key 'normal mu4e-headers-mode-map
 | 
						|
;;   "R" 'org-mu4e-compose-reply
 | 
						|
;;   "cr" 'org-mu4e-compose-reply)
 | 
						|
;; (defun my/-setup-org-mu4e-compose-mode ()
 | 
						|
;;   "Setup up stuff in `org-mu4e-compose' buffers."
 | 
						|
;;   (setq-local ltex-eglot-variable-save-method 'file)
 | 
						|
;;   ;; this should come last so it can pick up the above
 | 
						|
;;   ;; (my/eglot-if-trusted)
 | 
						|
;;   )
 | 
						|
;; (add-hook 'org-mu4e-compose-mode-hook #'my/-setup-org-mu4e-compose-mode)
 | 
						|
 | 
						|
;; elfeed
 | 
						|
(use-package elfeed
 | 
						|
  :bind (("C-c d" . elfeed))
 | 
						|
  :custom
 | 
						|
  (elfeed-feeds
 | 
						|
   '(("https://archlinux.org/feeds/news/" linux arch)
 | 
						|
     ("https://9to5linux.com/feed/atom" linux news)))
 | 
						|
  :config
 | 
						|
  (setq elfeed-log-buffer-name " *elfeed-log*")
 | 
						|
  (evil-define-key '(normal motion) elfeed-search-mode-map
 | 
						|
    "r" #'elfeed-search-fetch)
 | 
						|
  (elfeed-db-load))
 | 
						|
 | 
						|
;; helpful
 | 
						|
(use-package helpful
 | 
						|
  :hook ((emacs-lisp-mode . my/-helpful-setup-emacs-lisp-mode)
 | 
						|
         (helpful-mode . my/-setup-helpful-mode))
 | 
						|
  :bind (:map help-map
 | 
						|
         ("f" . helpful-callable)
 | 
						|
         ("v" . helpful-variable)
 | 
						|
         ("k" . helpful-key)
 | 
						|
         ("O" . helpful-symbol)
 | 
						|
         ("x" . helpful-command)
 | 
						|
         ("F" . helpful-function)
 | 
						|
         :map helpful-mode-map
 | 
						|
         ("<mouse-8>" . my/helpful-history-back)
 | 
						|
         ("<mouse-9>" . my/helpful-history-forward)
 | 
						|
         ("<normal-state><" . my/helpful-history-back)
 | 
						|
         ("<normal-state>>" . my/helpful-history-forward))
 | 
						|
  :init
 | 
						|
  (defun my/-helpful-setup-emacs-lisp-mode ()
 | 
						|
    (setq-local evil-lookup-func #'helpful-at-point))
 | 
						|
  (defun my/-setup-helpful-mode ()
 | 
						|
    (setq-local evil-lookup-func #'helpful-at-point
 | 
						|
                tab-width 8))
 | 
						|
  (defvar my/helpful-symbol-history-size 50
 | 
						|
    "Max size of `my/helpful-symbol-history'.")
 | 
						|
  (defvar my/helpful-symbol-history '()
 | 
						|
    "History of helpful symbols.")
 | 
						|
  (defvar my/-helpful-inhibit-history nil
 | 
						|
    "If non-nil, don't add symbols to `my/helpful-symbol-history'.")
 | 
						|
  (defvar my/-helpful-last-entry nil
 | 
						|
    "Last entry looked up with helpful.")
 | 
						|
  (defun my/helpful-history-back (count)
 | 
						|
    "Go back COUNT symbols in `my/helpful-symbol-history'.  If called
 | 
						|
interactively, COUNT defaults to 1."
 | 
						|
    (interactive "p")
 | 
						|
    (my/helpful-history-forward (- count)))
 | 
						|
  (defun my/helpful-history-forward (count)
 | 
						|
    "Move COUNT symbols in `my/helpful-symbol-history'.  If COUNT is negative,
 | 
						|
move back.  If COUNT is larger than the history, go to the newest entry. Go to
 | 
						|
the oldest entry if -COUNT is larger than the history."
 | 
						|
    (interactive "p")
 | 
						|
    (when helpful--sym
 | 
						|
      (let* ((hist-len (length my/helpful-symbol-history))
 | 
						|
             (current-pos (seq-position my/helpful-symbol-history
 | 
						|
                                        (cons helpful--sym
 | 
						|
                                              helpful--callable-p)
 | 
						|
                                        'equal))
 | 
						|
             (new-pos (- current-pos count)))
 | 
						|
        (cond
 | 
						|
         ;; if already at the newest element, signal an error
 | 
						|
         ((and (> count 0) (= current-pos 0))
 | 
						|
          (message "%s" "No newer symbol!"))
 | 
						|
         ;; if already at the oldest element, signal an error
 | 
						|
         ((and (< count 0) (= (1+ current-pos) hist-len))
 | 
						|
          (message "%s" "No older symbol!"))
 | 
						|
         (t
 | 
						|
          (let ((my/-helpful-inhibit-history t)
 | 
						|
                (entry (cond
 | 
						|
                        ((<= new-pos 0)
 | 
						|
                         (seq-first my/helpful-symbol-history))
 | 
						|
                        ((>= new-pos hist-len)
 | 
						|
                         (car (last my/helpful-symbol-history)))
 | 
						|
                        (t
 | 
						|
                         (nth new-pos my/helpful-symbol-history)))))
 | 
						|
            (if (cdr entry)
 | 
						|
                (helpful-callable (car entry))
 | 
						|
              (helpful-variable (car entry)))))))))
 | 
						|
  (defun my/-helpful-switch-buffer-function (helpful-buf)
 | 
						|
    "Like `pop-to-buffer', but kill previous helpful buffers and save the new
 | 
						|
buffers `helpful--sym' to `my/helpful-symbol-history'."
 | 
						|
    (cl-loop with window = nil
 | 
						|
             for buf in (buffer-list)
 | 
						|
             when (and
 | 
						|
                   (not (eq buf helpful-buf))
 | 
						|
                   (eq (buffer-local-value 'major-mode buf) 'helpful-mode))
 | 
						|
             do
 | 
						|
             (when-let (cur-window (get-buffer-window buf nil))
 | 
						|
               (setq window cur-window))
 | 
						|
             (kill-buffer buf)
 | 
						|
             finally
 | 
						|
             (let ((entry (cons (buffer-local-value 'helpful--sym helpful-buf)
 | 
						|
                                (buffer-local-value 'helpful--callable-p
 | 
						|
                                                    helpful-buf))))
 | 
						|
               (unless my/-helpful-inhibit-history
 | 
						|
                 (when-let (from-current-hist
 | 
						|
                            (member my/-helpful-last-entry
 | 
						|
                                    my/helpful-symbol-history))
 | 
						|
                   (setq my/helpful-symbol-history from-current-hist))
 | 
						|
                 (cl-pushnew entry my/helpful-symbol-history :test 'equal)
 | 
						|
                 (setq my/helpful-symbol-history
 | 
						|
                       (seq-take my/helpful-symbol-history
 | 
						|
                                 my/helpful-symbol-history-size)))
 | 
						|
               (setq my/-helpful-last-entry entry))
 | 
						|
             (if window
 | 
						|
                 (window--display-buffer helpful-buf window 'reuse)
 | 
						|
               (pop-to-buffer helpful-buf))))
 | 
						|
  (setq helpful-switch-buffer-function 'my/-helpful-switch-buffer-function
 | 
						|
        helpful-max-buffers 2))
 | 
						|
 | 
						|
;; useful for debugging
 | 
						|
(defun my/describe-symbol-plist (symbol)
 | 
						|
  "Descrive the plist of SYMBOL in a buffer."
 | 
						|
  (interactive (list (intern (completing-read
 | 
						|
                              "Symbol: "
 | 
						|
                              (let ((syms))
 | 
						|
                                (mapatoms (##push (symbol-name %) syms))
 | 
						|
                                syms)
 | 
						|
                              nil t))))
 | 
						|
  (with-current-buffer (get-buffer-create "*describe-symbol-plist*")
 | 
						|
    (unless (derived-mode-p 'special-mode)
 | 
						|
      (special-mode))
 | 
						|
    (let ((inhibit-read-only t)
 | 
						|
          (keys)
 | 
						|
          (values))
 | 
						|
      (map-do (lambda (k v)
 | 
						|
                (push k keys)
 | 
						|
                (push v values))
 | 
						|
              (symbol-plist symbol))
 | 
						|
      (setq keys (nreverse keys)
 | 
						|
            values (nreverse values))
 | 
						|
      (erase-buffer)
 | 
						|
      (insert (propertize "Plist of "
 | 
						|
                          'face 'shortdoc-heading))
 | 
						|
      (insert (propertize (format "%S" symbol)
 | 
						|
                          'face '((:weight normal) shortdoc-heading)))
 | 
						|
      (insert "\n\n")
 | 
						|
      (with-temp-buffer
 | 
						|
        (let ((delayed-mode-hooks nil))
 | 
						|
          (delay-mode-hooks
 | 
						|
            (lisp-mode))
 | 
						|
          (font-lock-mode)
 | 
						|
          (show-paren-mode)
 | 
						|
          (when (fboundp 'rainbow-delimiters-mode)
 | 
						|
            (rainbow-delimiters-mode)))
 | 
						|
        (let ((pp-max-width fill-column)
 | 
						|
              (pp-use-max-width t))
 | 
						|
          (setq values (mapcar (lambda (val)
 | 
						|
                                 (erase-buffer)
 | 
						|
                                 (pp val (current-buffer))
 | 
						|
                                 (font-lock-ensure)
 | 
						|
                                 (buffer-string))
 | 
						|
                               values))))
 | 
						|
      (goto-char (point-max))
 | 
						|
      (cl-loop for key in keys
 | 
						|
               for value in values
 | 
						|
               do
 | 
						|
               (insert (propertize (prin1-to-string key)
 | 
						|
                                   'face 'bold))
 | 
						|
               (insert "\n")
 | 
						|
               (insert value)
 | 
						|
               (insert "\n"))
 | 
						|
      (delete-char -1))
 | 
						|
    (pop-to-buffer (current-buffer))))
 | 
						|
 | 
						|
(defun my/greyify-color (color percent &optional frame)
 | 
						|
  "Make COLOR closer to black by PERCENT on FRAME.
 | 
						|
Color can be any color which can be passed to `color-values'."
 | 
						|
  (cl-destructuring-bind (&optional r g b)
 | 
						|
      (color-name-to-rgb color frame)
 | 
						|
    (when (and r g b)
 | 
						|
      (let ((scale (- 1.0 (/ percent 100.0))))
 | 
						|
        (color-rgb-to-hex (* r scale)
 | 
						|
                          (* g scale)
 | 
						|
                          (* b scale))))))
 | 
						|
 | 
						|
;; rainbow-delimiters
 | 
						|
(use-package rainbow-delimiters
 | 
						|
  :hook (prog-mode . rainbow-delimiters-mode)
 | 
						|
  :config
 | 
						|
  ;; generate dark version of the rainbow delimiters faces
 | 
						|
  (defun my/-rainbow-delimiters-recalc-dark-faces (&optional frame)
 | 
						|
    (unless frame (setq frame (selected-frame)))
 | 
						|
    (dotimes (i 9)
 | 
						|
      (when-let ((old-face (intern-soft
 | 
						|
                            (format "rainbow-delimiters-depth-%d-face"
 | 
						|
                                    (1+ i))))
 | 
						|
                 (new-face
 | 
						|
                  (intern
 | 
						|
                   (format "my/rainbow-delimiters-depth-%d-dark-face"
 | 
						|
                           (1+ i))))
 | 
						|
                 (old-color (face-attribute old-face :foreground frame))
 | 
						|
                 (new-color (my/greyify-color old-color 50 frame)))
 | 
						|
        (set-face-attribute new-face frame :foreground new-color))))
 | 
						|
  (add-hook 'after-make-frame-functions
 | 
						|
            #'my/-rainbow-delimiters-recalc-dark-faces)
 | 
						|
  (add-hook 'server-after-make-frame-hook
 | 
						|
            #'my/-rainbow-delimiters-recalc-dark-faces)
 | 
						|
  (defun my/rainbow-delimiters-parinfer-pick-face (depth match loc)
 | 
						|
    "Version of `rainbow-delimiters-default-pick-face' that colors closing
 | 
						|
parenthesis darker than opening ones. This function defers to
 | 
						|
`rainbow-delimiters-default-pick-face' and just changes the output if it returns
 | 
						|
one of the normal rainbow-delimiters-depth-N-face faces."
 | 
						|
    (save-match-data
 | 
						|
      (let* ((base-face (rainbow-delimiters-default-pick-face depth match loc))
 | 
						|
             (base-name (symbol-name base-face)))
 | 
						|
        (if (and evil-cleverparens-mode
 | 
						|
                 (eq ?\) (char-syntax
 | 
						|
                          (elt (buffer-substring-no-properties loc (1+ loc)) 0)))
 | 
						|
                 (string-match (rx string-start "rainbow-delimiters-depth-"
 | 
						|
                                   (group (+ num))
 | 
						|
                                   "-face" string-end)
 | 
						|
                               base-name))
 | 
						|
            (or (intern-soft (format "my/rainbow-delimiters-depth-%s-dark-face"
 | 
						|
                                     (match-string 1 base-name)))
 | 
						|
                base-face)
 | 
						|
          base-face))))
 | 
						|
  (setopt rainbow-delimiters-pick-face-function
 | 
						|
          'my/rainbow-delimiters-parinfer-pick-face))
 | 
						|
 | 
						|
;; make regexp look nicer
 | 
						|
(use-package easy-escape
 | 
						|
  :hook ((emacs-lisp-mode reb-mode) . easy-escape-minor-mode)
 | 
						|
  :config
 | 
						|
  (face-spec-set 'easy-escape-face
 | 
						|
                 '((t (:foreground unspecified
 | 
						|
                       :weight bold
 | 
						|
                       :inherit 'font-lock-regexp-grouping-backslash)))))
 | 
						|
 | 
						|
;; auto-highlight-symbol
 | 
						|
(use-package auto-highlight-symbol
 | 
						|
  :hook (lisp-data-mode . auto-highlight-symbol-mode)
 | 
						|
  :init
 | 
						|
  (setq ahs-face 'bold
 | 
						|
        ahs-face-unfocused 'bold
 | 
						|
        ahs-definition-face 'bold
 | 
						|
        ahs-definition-face-unfocused 'bold
 | 
						|
        ahs-plugin-default-face 'bold
 | 
						|
        ahs-plugin-default-face-unfocused 'bold)
 | 
						|
  :config
 | 
						|
  (keymap-unset auto-highlight-symbol-mode-map "C-x C-a" t))
 | 
						|
 | 
						|
;; Theme (doom-themes)
 | 
						|
(use-package doom-themes
 | 
						|
  :config
 | 
						|
  (load-theme 'doom-molokai t)
 | 
						|
  (doom-themes-org-config))
 | 
						|
 | 
						|
;; solaire-mode
 | 
						|
(use-package solaire-mode
 | 
						|
  :config
 | 
						|
  (solaire-global-mode 1))
 | 
						|
 | 
						|
;; Highlight todos
 | 
						|
(use-package hl-todo
 | 
						|
  :hook (prog-mode . hl-todo-mode))
 | 
						|
(use-package magit-todos
 | 
						|
  :after (hl-todo magit)
 | 
						|
  :config
 | 
						|
  (magit-todos-mode 1))
 | 
						|
 | 
						|
;; icons
 | 
						|
(use-package nerd-icons)
 | 
						|
(use-package nerd-icons-completion
 | 
						|
  :config
 | 
						|
  (nerd-icons-completion-mode))
 | 
						|
(use-package nerd-icons-dired
 | 
						|
  :hook (dired-mode . my/-maybe-enable-nerd-icons-dired)
 | 
						|
  :init
 | 
						|
  (defun my/-maybe-enable-nerd-icons-dired ()
 | 
						|
    (unless (bound-and-true-p dirvish-override-dired-mode)
 | 
						|
      (nerd-icons-dired-mode))))
 | 
						|
(use-package kind-icon
 | 
						|
  :after corfu
 | 
						|
  :init
 | 
						|
  (setq kind-icon-default-face 'corfu-default
 | 
						|
        kind-icon-default-style
 | 
						|
        '(:padding -1 :stroke 0 :margin 0 :radius 0 :height 0.5 :scale 1))
 | 
						|
  :config
 | 
						|
  (add-to-list 'corfu-margin-formatters #'kind-icon-margin-formatter))
 | 
						|
 | 
						|
;; modeline (doom-modeline)
 | 
						|
(use-package doom-modeline
 | 
						|
  :init
 | 
						|
  (setq doom-modeline-support-imenu t)
 | 
						|
  (doom-modeline-mode 1))
 | 
						|
 | 
						|
;; dashboard.el
 | 
						|
(use-package dashboard
 | 
						|
  :config
 | 
						|
  (defvar-local my/-dashboard-did-fix-image nil
 | 
						|
    "Weather or not the dashboard image has been fixed in this buffer.")
 | 
						|
  (defun my/-dashboard-fix-image ()
 | 
						|
    (unless my/-dashboard-did-fix-image
 | 
						|
      (dashboard-refresh-buffer)
 | 
						|
      (setq my/-dashboard-did-fix-image t)))
 | 
						|
  (defun my/-dashboard-setup-function ()
 | 
						|
    (add-hook 'window-configuration-change-hook 'my/-dashboard-fix-image nil t)
 | 
						|
    (setq-local display-line-numbers nil))
 | 
						|
  (add-hook 'dashboard-mode-hook 'my/-dashboard-setup-function)
 | 
						|
  (set-face-background 'dashboard-banner-logo-title nil)
 | 
						|
  (dashboard-setup-startup-hook)
 | 
						|
  (setq initial-buffer-choice (lambda () (get-buffer-create "*dashboard*"))
 | 
						|
        dashboard-force-refresh t
 | 
						|
        dashboard-display-icons-p t
 | 
						|
        dashboard-icon-type 'nerd-icons
 | 
						|
        dashboard-set-file-icons t
 | 
						|
        dashboard-projects-backend 'project-el
 | 
						|
        dashboard-items '((recents . 5)
 | 
						|
                          (projects . 5)
 | 
						|
                          (bookmarks . 5))))
 | 
						|
 | 
						|
;; world clocks
 | 
						|
(setq zoneinfo-style-world-list
 | 
						|
      '(("America/Los_Angeles" "California")
 | 
						|
        ("Asia/Tokyo" "Tokyo")
 | 
						|
        ("America/New_York" "New York")
 | 
						|
        ("Europe/London" "London")
 | 
						|
        ("Europe/Paris" "Paris")
 | 
						|
        ("Asia/Calcutta" "Bangalore")))
 | 
						|
 | 
						|
;; dictionaries
 | 
						|
(use-package dictionary
 | 
						|
  :defer t
 | 
						|
  :ensure nil
 | 
						|
  :custom
 | 
						|
  (dictionary-read-word-function . #'dictionary-completing-read-word)
 | 
						|
  (dictionary-read-dictionary-function . #'dictionary-completing-read-dictionary))
 | 
						|
 | 
						|
;; prettify ^L characters
 | 
						|
(use-package form-feed
 | 
						|
  :custom-face
 | 
						|
  (form-feed-line ((((type graphic)) :strike-through "#555556")
 | 
						|
                   (((type tty)) :inherit font-lock-comment-face :underline t)))
 | 
						|
  :config
 | 
						|
  (global-form-feed-mode 1))
 | 
						|
 | 
						|
;; fun!
 | 
						|
(use-package mines
 | 
						|
  :config
 | 
						|
  (evil-set-initial-state 'mines-mode 'emacs))
 | 
						|
 | 
						|
;;; init.el ends here
 |