├── .emacs ├── .emacs.d ├── colors.el ├── common.el ├── cpp-modes.el ├── custom.el ├── git-config.el ├── global-keys.el ├── graphic-display.el ├── hacks.el ├── local.el ├── major-modes.el ├── minor-modes.el ├── org-modes.el ├── prog.el ├── python-modes.el ├── site-lisp │ ├── boost-sml.el │ ├── c-change-brackets.el │ ├── c-transpose-args.el │ ├── cpp-auto-include.el │ ├── cyclekey.el │ ├── lorikeet.png │ ├── tspew.el │ ├── utilities.el │ ├── welcome-dashboard.el │ ├── zone-words-emotions-dictionary.el │ └── zone-words.el └── terminal.el ├── .gitignore └── README.md /.emacs: -------------------------------------------------------------------------------- 1 | ;; -*- lexical-binding: t; -*- 2 | ;;------------------------------------------------------------------------------ 3 | ;; debugging 4 | (setq debug-on-error nil) 5 | ;; don't resize the frame on font changes etc 6 | (setq frame-inhibit-implied-resize t) 7 | 8 | ;; this file's true directory 9 | (setq dotfile-dir (file-name-directory 10 | (file-chase-links 11 | (or load-file-name 12 | (buffer-file-name))))) 13 | 14 | ;; my stuff is in .emacs.d 15 | (add-to-list 'load-path (concat dotfile-dir ".emacs.d/")) 16 | ;; 3rd party stuff is in site-lisp 17 | (add-to-list 'load-path (concat dotfile-dir ".emacs.d/site-lisp/")) 18 | ;; packages 19 | (setq package-user-dir (concat dotfile-dir ".emacs.d/packages/")) 20 | 21 | ;;------------------------------------------------------------------------------ 22 | ;; apply custom variables 23 | (setq custom-file (concat dotfile-dir ".emacs.d/custom.el")) 24 | (load custom-file) 25 | 26 | ;;------------------------------------------------------------------------------ 27 | ;; package setup 28 | (setq package-archives '(("gnu" . "http://elpa.gnu.org/packages/") 29 | ("stable-melpa" . "http://stable.melpa.org/packages/") 30 | ("melpa" . "http://melpa.org/packages/") 31 | ("org-contrib" . "http://elpa.nongnu.org/nongnu/") 32 | ("cselpa" . "https://elpa.thecybershadow.net/packages/"))) 33 | 34 | (package-initialize) 35 | (setq package-enable-at-startup nil) 36 | 37 | (unless package-archive-contents 38 | (message "Refreshing package archives...") 39 | (package-refresh-contents)) 40 | 41 | (unless (package-installed-p 'use-package) 42 | (message "`use-package' not found. Installing...") 43 | (package-install 'use-package)) 44 | 45 | (setq use-package-enable-imenu-support t) 46 | (require 'use-package) 47 | (setq use-package-minimum-reported-time 0 48 | use-package-verbose t 49 | use-package-compute-statistics t) 50 | 51 | (setq personal-keybindings nil) 52 | 53 | ;;------------------------------------------------------------------------------ 54 | ;; Setup GC 55 | (use-package gcmh 56 | :ensure t 57 | :config 58 | (gcmh-mode 1)) 59 | 60 | ;;------------------------------------------------------------------------------ 61 | ;; Startup profiling 62 | (use-package esup 63 | :ensure t 64 | :defer t) 65 | 66 | ;;------------------------------------------------------------------------------ 67 | ;; Common settings 68 | (load "common.el") 69 | 70 | ;;------------------------------------------------------------------------------ 71 | ;; Graphic or terminal mode? 72 | (if (display-graphic-p) 73 | (load "graphic-display.el") 74 | (load "terminal.el")) 75 | 76 | ;;------------------------------------------------------------------------------ 77 | ;; Minor modes 78 | (load "minor-modes.el") 79 | 80 | ;;------------------------------------------------------------------------------ 81 | ;; Colors 82 | (load "colors.el") 83 | 84 | ;;------------------------------------------------------------------------------ 85 | ;; Global key bindings 86 | (load "global-keys.el") 87 | 88 | ;;------------------------------------------------------------------------------ 89 | ;; Major modes 90 | (load "major-modes.el") 91 | 92 | ;;------------------------------------------------------------------------------ 93 | ;; General programming 94 | (load "prog.el") 95 | 96 | ;;------------------------------------------------------------------------------ 97 | ;; C++ 98 | (load "cpp-modes.el") 99 | 100 | ;;------------------------------------------------------------------------------ 101 | ;; Python 102 | (load "python-modes.el") 103 | 104 | ;;------------------------------------------------------------------------------ 105 | ;; Git 106 | (load "git-config.el") 107 | 108 | ;;------------------------------------------------------------------------------ 109 | ;; Org-mode stuff 110 | (load "org-modes.el") 111 | 112 | ;;------------------------------------------------------------------------------ 113 | ;; Byte-compile elisp on save 114 | (defun byte-compile-current-buffer () 115 | "`byte-compile' current buffer if it's emacs-lisp-mode and compiled file exists." 116 | (interactive) 117 | (when (and (eq major-mode 'emacs-lisp-mode) 118 | (file-exists-p (byte-compile-dest-file buffer-file-name))) 119 | (byte-compile-file buffer-file-name))) 120 | 121 | (add-hook 'after-save-hook 'byte-compile-current-buffer) 122 | 123 | ;;------------------------------------------------------------------------------ 124 | ;; My stuff 125 | (load "utilities.el") 126 | 127 | (bind-key "C-x C-S-e" 'eval-and-replace) 128 | (bind-key "C-c C-w" 'toggle-window-split) 129 | (bind-key "C-c C-d" 'insert-current-date) 130 | (bind-key "C-c C-t" 'insert-current-time) 131 | (bind-key "C-c C-u" 'insert-uuid) 132 | (bind-key "C-c C-f" 'my-search-forward-1) 133 | (bind-key "C-c C-b" 'my-search-backward-1) 134 | 135 | ;;------------------------------------------------------------------------------ 136 | ;; Graphic window settings 137 | (when (display-graphic-p) 138 | (setq default-frame-height (frame-height)) 139 | (setq default-frame-alist 140 | (append 141 | `((width . ,column-wrap-hard) 142 | (height . ,default-frame-height)) 143 | default-frame-alist)) 144 | 145 | (size-frame-default)) 146 | 147 | ;;------------------------------------------------------------------------------ 148 | ;; Start server 149 | (require 'server) 150 | (unless (server-running-p) 151 | (server-start)) 152 | 153 | (put 'narrow-to-region 'disabled nil) 154 | 155 | ;;------------------------------------------------------------------------------ 156 | ;; Hacks & late-bound overrides 157 | (load "hacks.el") 158 | 159 | ;------------------------------------------------------------------------------ 160 | ;; apply local site-specific changes 161 | (let ((local-file (concat dotfile-dir ".emacs.d/local.el"))) 162 | (load local-file)) 163 | 164 | ;------------------------------------------------------------------------------ 165 | ;; welcome dashboard 166 | (when (display-graphic-p) 167 | (use-package welcome-dashboard 168 | :ensure nil ;; when using local file and not straight nor use-package 169 | :config 170 | (setq welcome-dashboard-latitude 39.9237 171 | welcome-dashboard-longitude -104.9201 172 | welcome-dashboard-image-file (concat dotfile-dir ".emacs.d/site-lisp/lorikeet.png") 173 | welcome-dashboard-title (concat "Welcome " user-full-name ", have a great day!")) 174 | (welcome-dashboard-create-welcome-hook))) 175 | 176 | ;; Local Variables: 177 | ;; byte-compile-warnings: (not free-vars) 178 | ;; End: 179 | -------------------------------------------------------------------------------- /.emacs.d/colors.el: -------------------------------------------------------------------------------- 1 | ;; -*- lexical-binding: t; -*- 2 | ;;------------------------------------------------------------------------------ 3 | ;; Disable themes completely before applying a new one 4 | (defadvice load-theme (before theme-dont-propagate activate) 5 | "Disable all theme effects before enabling new ones." 6 | (mapc #'disable-theme custom-enabled-themes)) 7 | 8 | ;;------------------------------------------------------------------------------ 9 | ;; Nice theme 10 | (defun load-theme-cyberpunk () 11 | (interactive) 12 | (load-theme 'cyberpunk t)) 13 | 14 | (use-package cyberpunk-theme 15 | :ensure t 16 | :bind (("C-c t c" . load-theme-cyberpunk))) 17 | (load-theme-cyberpunk) 18 | 19 | ;;------------------------------------------------------------------------------ 20 | ;; Highlight FIXME/TODO/etc 21 | (use-package hl-todo 22 | :ensure t 23 | :hook (prog-mode . hl-todo-mode)) 24 | 25 | ;;------------------------------------------------------------------------------ 26 | ;; Special types of comments 27 | (defface font-lock-comment-strike 28 | '((t (:strike-through t))) 29 | "For strike-through comments") 30 | 31 | (defface font-lock-comment-important 32 | '((t (:foreground "#00ff00"))) 33 | "For important") 34 | 35 | (defface font-lock-comment-todo 36 | '((t (:foreground "#ff0000"))) 37 | "For todo comments") 38 | 39 | (defun add-custom-keyw() 40 | "adds a few special keywords" 41 | (font-lock-add-keywords 42 | nil 43 | '(("\\s<+x[[:space:]]*\\(.*?\\)[[:space:]]*\\s>" 1 'font-lock-comment-strike prepend) 44 | ("\\s<+t[[:space:]]*\\(.*?\\)[[:space:]]*\\s>" 1 'font-lock-comment-todo prepend) 45 | ("\\s<+i[[:space:]]*\\(.*?\\)[[:space:]]*\\s>" 1 'font-lock-comment-important prepend)))) 46 | 47 | (add-hook 'prog-mode-hook #'add-custom-keyw) 48 | 49 | (defun add-custom-keyw-cpp() 50 | "adds a few special keywords" 51 | (font-lock-add-keywords 52 | nil 53 | '(("//+x[[:space:]]*\\(.*?\\)[[:space:]]*\\s>" 1 'font-lock-comment-strike prepend) 54 | ("//+t[[:space:]]*\\(.*?\\)[[:space:]]*\\s>" 1 'font-lock-comment-todo prepend) 55 | ("//+i[[:space:]]*\\(.*?\\)[[:space:]]*\\s>" 1 'font-lock-comment-important prepend)))) 56 | 57 | (add-hook 'c++-mode-hook #'add-custom-keyw-cpp) 58 | 59 | ;;------------------------------------------------------------------------------ 60 | ;; Diff mode colors 61 | (eval-after-load 'diff-mode 62 | '(progn 63 | (set-face-foreground 'diff-added "green4") 64 | (set-face-foreground 'diff-removed "red3"))) 65 | 66 | ;;------------------------------------------------------------------------------ 67 | ;; modus themes 68 | (defun load-theme-modus-operandi () 69 | (interactive) 70 | (load-theme 'modus-operandi t)) 71 | (defun load-theme-modus-vivendi () 72 | (interactive) 73 | (load-theme 'modus-vivendi t)) 74 | 75 | (use-package modus-themes 76 | :ensure t 77 | :config 78 | (setq modus-themes-common-palette-overrides 79 | '((border-mode-line-active bg-mode-line-active) 80 | (border-mode-line-inactive bg-mode-line-inactive) 81 | (fringe bg-inactive) 82 | (bg-region bg-active) 83 | (fg-region unspecified)) 84 | modus-themes-bold-constructs t 85 | modus-themes-italic-constructs t 86 | modus-themes-prompts '(bold intense) 87 | modus-themes-completions '((matches . (extrabold)) 88 | (selection . (semibold text-also))) 89 | modus-themes-org-blocks 'tinted-background) 90 | :bind (("C-c t l" . load-theme-modus-operandi) 91 | ("C-c t d" . load-theme-modus-vivendi) 92 | ("C-c t o" . load-theme-modus-operandi) 93 | ("C-c t v" . load-theme-modus-vivendi) 94 | ("C-c t m" . modus-themes-select))) 95 | 96 | ;;------------------------------------------------------------------------------ 97 | ;; ef themes 98 | (use-package ef-themes 99 | :ensure t 100 | :config 101 | (setq ef-themes-to-toggle '(ef-summer ef-winter)) 102 | :bind (("C-c t e" . ef-themes-select))) 103 | -------------------------------------------------------------------------------- /.emacs.d/common.el: -------------------------------------------------------------------------------- 1 | ;; -*- lexical-binding: t; -*- 2 | ;;------------------------------------------------------------------------------ 3 | ;; Hide minor modes 4 | (use-package diminish 5 | :ensure t) 6 | 7 | ;;------------------------------------------------------------------------------ 8 | ;; UTF8 defaults 9 | (set-language-environment 'utf-8) 10 | (prefer-coding-system 'utf-8) 11 | (define-coding-system-alias 'UTF-8 'utf-8) 12 | (set-default-coding-systems 'utf-8) 13 | (set-terminal-coding-system 'utf-8) 14 | (set-keyboard-coding-system 'utf-8) 15 | (set-selection-coding-system 'utf-8) 16 | (setq buffer-file-coding-system 'utf-8 17 | default-file-name-coding-system 'utf-8 18 | locale-coding-system 'utf-8) 19 | (when (display-graphic-p) 20 | (setq x-select-request-type '(UTF8_STRING COMPOUND_TEXT TEXT STRING))) 21 | 22 | ;;------------------------------------------------------------------------------ 23 | ;; Clean up display 24 | (tool-bar-mode -1) 25 | (menu-bar-mode -1) 26 | (display-battery-mode -1) 27 | (scroll-bar-mode -1) 28 | (setq inhibit-startup-screen t 29 | initial-scratch-message "" 30 | visible-bell 1 31 | x-stretch-cursor t) 32 | 33 | ;; Display defaults 34 | (setq column-wrap-soft 80 35 | column-wrap-hard 100) 36 | (setq sentence-end-double-space nil) 37 | (setq-default indent-tabs-mode nil) 38 | (setq tab-width 2) 39 | 40 | ;; Keep whitespace clean 41 | (setq require-final-newline t) 42 | (use-package ws-butler 43 | :ensure t 44 | :config 45 | (ws-butler-global-mode) 46 | :diminish 47 | ws-butler-mode) 48 | 49 | ;;------------------------------------------------------------------------------ 50 | ;; Copy/paste stuff 51 | (setq select-enable-clipboard t 52 | select-enable-primary t 53 | save-interprogram-paste-before-kill t 54 | mouse-yank-at-point t) 55 | 56 | ;;------------------------------------------------------------------------------ 57 | ;; Autosaves/backups 58 | (make-directory "~/.emacs.d/backups" t) 59 | (make-directory "~/.emacs.d/autosaves" t) 60 | (setq backup-directory-alist 61 | `((".*" . , "~/.emacs.d/backups/"))) 62 | (setq auto-save-file-name-transforms 63 | `((".*" ,"~/.emacs.d/autosaves/" t))) 64 | (setq create-lockfiles nil) 65 | 66 | ;; Delete backups older than one month 67 | (defun delete-backups () 68 | (interactive) 69 | (message "Deleting old backup files...") 70 | (let ((month (* 60 60 24 31)) 71 | (current (float-time (current-time)))) 72 | (dolist (file (directory-files temporary-file-directory t)) 73 | (when (and (backup-file-name-p file) 74 | (> (- current (float-time (nth 5 (file-attributes file)))) 75 | month)) 76 | (message file) 77 | (delete-file file))))) 78 | (delete-backups) 79 | 80 | ;;------------------------------------------------------------------------------ 81 | ;; More usable defaults 82 | (defalias 'list-buffers 'ibuffer) 83 | (defalias 'yes-or-no-p 'y-or-n-p) 84 | (setq load-prefer-newer t 85 | apropos-do-all t) 86 | (diminish 'abbrev-mode) 87 | 88 | ;;------------------------------------------------------------------------------ 89 | ;; Smooth scrolling 90 | (setq scroll-margin 1 91 | scroll-step 1 92 | scroll-conservatively 10000 93 | scroll-preserve-screen-position 1) 94 | (setq mouse-wheel-follow-mouse t 95 | mouse-wheel-scroll-amount '(1 ((shift) . 1))) 96 | 97 | ;;------------------------------------------------------------------------------ 98 | ;; Prevent prompt on opening large TAGS file 99 | (setq large-file-warning-threshold 100000000) 100 | 101 | ;;------------------------------------------------------------------------------ 102 | ;; vertico 103 | (use-package vertico 104 | :ensure t 105 | :config 106 | (vertico-mode)) 107 | 108 | (use-package orderless 109 | :ensure t 110 | :config 111 | (setq orderless-matching-styles '(orderless-literal orderless-regexp orderless-flex) 112 | completion-styles '(orderless basic) 113 | completion-category-overrides '((file (styles basic partial-completion)))) 114 | :after vertico) 115 | 116 | ;;------------------------------------------------------------------------------ 117 | ;; Undo 118 | (use-package vundo 119 | :ensure t 120 | :bind (("C-x _" . vundo))) 121 | 122 | (use-package undo-tree 123 | :ensure t 124 | :config 125 | (global-undo-tree-mode 1) 126 | (setq undo-tree-visualizer-timestamps t 127 | undo-tree-visualizer-diff t 128 | undo-tree-history-directory-alist '(("." . "~/.emacs.d/undo")) 129 | undo-tree-auto-save-history nil) 130 | :diminish undo-tree-mode 131 | :bind (("C-z" . undo-tree-undo) 132 | ("C-x u" . undo-tree-visualize))) 133 | 134 | ;;------------------------------------------------------------------------------ 135 | ;; Google-this 136 | (use-package google-this 137 | :ensure t 138 | :commands (google-this-parse-and-search-string) 139 | :config 140 | (setq google-this-browse-url-function 'eww-browse-url) 141 | :bind 142 | (("C-c " . duckduckgo-this-cpp-reference))) 143 | 144 | (defun duckduckgo-this-lucky-search-url () 145 | "Return the url for a feeling-ducky duckduckgo search." 146 | (format "https://duckduckgo.com/?q=\\%%s")) 147 | 148 | (defun duckduckgo-this-cpp-reference () 149 | "Visit the most probable cppreference.com page for this word." 150 | (interactive) 151 | (google-this-parse-and-search-string 152 | (concat (thing-at-point 'symbol) " site:cppreference.com") 153 | nil (duckduckgo-this-lucky-search-url))) 154 | 155 | ;;------------------------------------------------------------------------------ 156 | ;; Avy 157 | (use-package avy 158 | :ensure t 159 | :bind (("C-c SPC" . avy-goto-word-or-subword-1) 160 | ("C-," . avy-pop-mark) 161 | ("C-j" . avy-goto-char-timer))) 162 | 163 | (use-package ace-window 164 | :ensure t 165 | :bind ("C-c w" . ace-window)) 166 | 167 | ;;------------------------------------------------------------------------------ 168 | ;; smart-scan: use M-n and M-p to jump to next/prev thing at point 169 | ;; remove M-' from smartscan keymap: it's used by surround 170 | (defvar smartscan-map 171 | (let ((m (make-sparse-keymap))) 172 | (define-key m (kbd "M-n") 'smartscan-symbol-go-forward) 173 | (define-key m (kbd "M-p") 'smartscan-symbol-go-backward) 174 | m) 175 | "Keymap for `smartscan'.") 176 | 177 | (use-package smartscan 178 | :ensure t 179 | :config (global-smartscan-mode t)) 180 | 181 | ;;------------------------------------------------------------------------------ 182 | ;; use surround for changing delimiters 183 | (use-package surround 184 | :ensure t 185 | :bind-keymap ("M-'" . surround-keymap) 186 | :bind (:map surround-keymap 187 | ("M-'" . smartscan-symbol-replace))) 188 | 189 | ;;------------------------------------------------------------------------------ 190 | ;; visual-regexp-steroids: better regexp searching 191 | (use-package visual-regexp-steroids 192 | :ensure t 193 | :bind (("C-c r" . vr/replace) 194 | ("C-c q" . vr/query-replace) 195 | ("C-r" . vr/isearch-backward) 196 | ("C-s" . vr/isearch-forward))) 197 | 198 | ;;------------------------------------------------------------------------------ 199 | ;; neotree: tree interface for opening files 200 | 201 | ;; When opening a file, or a directory with dired, hide the neotree window. Just 202 | ;; using neo-enter-hook doesn't quite do it, because neotree routes all 203 | ;; functionality (eg refresh, toggle hidden, etc) through neo-buffer--execute. 204 | 205 | (use-package neotree 206 | :ensure t 207 | :config 208 | (setq neo-smart-open t 209 | neo-hidden-regexp-list '("\\.pyc$" "~$" "^#.*#$" "^\\.#\\..*$" "\\.elc$") 210 | my/neotree-opening-file nil 211 | my/neotree-entering-dired nil) 212 | (setq neo-theme (if (display-graphic-p) 'icons 'arrow)) 213 | (defun neo-hide-on-enter (type path arg) 214 | (if (or (and (eq my/neotree-opening-file t) 215 | (equal type 'file)) 216 | (and (eq my/neotree-entering-dired t) 217 | (equal type 'directory))) 218 | (neotree-hide)) 219 | (setq my/neotree-opening-file nil 220 | my/neotree-entering-dired nil)) 221 | (defun my/before-neobuffer-execute (arg0 &optional file-fn dir-fn &rest args) 222 | (when (eq dir-fn 'neo-open-dired) 223 | (setq my/neotree-entering-dired t)) 224 | (when (or (eq file-fn 'neo-open-file) 225 | (eq file-fn 'neo-open-file-vertical-split) 226 | (eq file-fn 'neo-open-file-horizontal-split)) 227 | (setq my/neotree-opening-file t))) 228 | (advice-add 'neo-buffer--execute :before #'my/before-neobuffer-execute) 229 | (add-hook 'neo-enter-hook #'neo-hide-on-enter) 230 | :bind (("" . neotree-toggle))) 231 | 232 | ;;------------------------------------------------------------------------------ 233 | ;; make shell files executable on save 234 | (add-hook 'after-save-hook 'executable-make-buffer-file-executable-if-script-p) 235 | 236 | ;;------------------------------------------------------------------------------ 237 | ;; cycle CamelCase, snake_case, kebab-case etc 238 | (use-package string-inflection 239 | :ensure t 240 | :bind (("C-c -" . string-inflection-all-cycle) 241 | ("C-c _" . string-inflection-toggle))) 242 | 243 | ;;------------------------------------------------------------------------------ 244 | ;; better zap-to-char 245 | (use-package zzz-to-char 246 | :ensure t 247 | :bind (("M-z" . zzz-up-to-char))) 248 | 249 | ;;------------------------------------------------------------------------------ 250 | ;; browse kill-ring 251 | (use-package browse-kill-ring 252 | :ensure t 253 | :bind (("C-M-y" . browse-kill-ring))) 254 | 255 | ;;------------------------------------------------------------------------------ 256 | ;; symbol-overlay 257 | (use-package symbol-overlay 258 | :ensure t 259 | :bind (("" . symbol-overlay-put))) 260 | 261 | ;;------------------------------------------------------------------------------ 262 | ;; simple modeline functionality 263 | (defun my-truncate-buffer-name (buf-name) 264 | (let ((len (length buf-name))) 265 | (cond ((> len 30) 266 | (concat "..." 267 | (substring buf-name (- len 27) len))) 268 | (t buf-name)))) 269 | 270 | (setq auto-revert-check-vc-info t) 271 | 272 | ;;------------------------------------------------------------------------------ 273 | ;; keep track of the cursor 274 | (use-package beacon 275 | :ensure t 276 | :config 277 | (beacon-mode 1)) 278 | 279 | ;;------------------------------------------------------------------------------ 280 | ;; consult extras 281 | (use-package consult 282 | :ensure t 283 | ;; Replace bindings. Lazily loaded due by `use-package'. 284 | :bind (;; C-c bindings (mode-specific-map) 285 | ;;("C-c h" . consult-history) 286 | ("M-g i" . consult-imenu) 287 | ("C-c m" . consult-mode-command) 288 | ("C-c k" . consult-kmacro) 289 | ;; C-x bindings (ctl-x-map) 290 | ("C-x M-:" . consult-complex-command) ;; orig. repeat-complex-command 291 | ("C-x b" . consult-buffer) ;; orig. switch-to-buffer 292 | ("C-x 4 b" . consult-buffer-other-window) ;; orig. switch-to-buffer-other-window 293 | ("C-x 5 b" . consult-buffer-other-frame) ;; orig. switch-to-buffer-other-frame 294 | ("C-x r b" . consult-bookmark) ;; orig. bookmark-jump 295 | ("C-x p b" . consult-project-buffer) ;; orig. project-switch-to-buffer 296 | ;; Custom M-# bindings for fast register access 297 | ;;("M-#" . consult-register-load) 298 | ;;("M-'" . consult-register-store) ;; orig. abbrev-prefix-mark (unrelated) 299 | ;;("C-M-#" . consult-register) 300 | ;; Other custom bindings 301 | ("M-y" . consult-yank-pop) ;; orig. yank-pop 302 | (" a" . consult-apropos) ;; orig. apropos-command 303 | ;; M-g bindings (goto-map) 304 | ("M-g e" . consult-compile-error) 305 | ("M-g f" . consult-flycheck) ;; Alternative: consult-flymake 306 | ("C-o" . consult-goto-line) ;; orig. (custom) goto-line 307 | ("M-g g" . consult-goto-line) ;; orig. goto-line 308 | ("M-g M-g" . consult-goto-line) ;; orig. goto-line 309 | ("M-g o" . consult-outline) ;; Alternative: consult-org-heading 310 | ("M-g m" . consult-mark) 311 | ("M-g k" . consult-global-mark) 312 | ("M-m" . consult-imenu) ;; orig. back-to-indentation 313 | ("M-g i" . consult-imenu) 314 | ("M-g I" . consult-imenu-multi) 315 | ;; M-s bindings (search-map) 316 | ("M-s d" . consult-find) 317 | ("M-s D" . consult-locate) 318 | ("M-s g" . consult-grep) 319 | ("M-s G" . consult-git-grep) 320 | ("M-s r" . consult-ripgrep) 321 | ("M-s l" . consult-line) 322 | ("M-s L" . consult-line-multi) 323 | ("M-s m" . consult-multi-occur) 324 | ("M-s k" . consult-keep-lines) 325 | ("M-s u" . consult-focus-lines) 326 | ;; Isearch integration 327 | ("M-s e" . consult-isearch-history) 328 | :map isearch-mode-map 329 | ("M-e" . consult-isearch-history) ;; orig. isearch-edit-string 330 | ("M-s e" . consult-isearch-history) ;; orig. isearch-edit-string 331 | ("M-s l" . consult-line) ;; needed by consult-line to detect isearch 332 | ("M-s L" . consult-line-multi) ;; needed by consult-line to detect isearch 333 | ;; Minibuffer history 334 | :map minibuffer-local-map 335 | ("M-s" . consult-history) ;; orig. next-matching-history-element 336 | ("M-r" . consult-history)) ;; orig. previous-matching-history-element 337 | 338 | ;; Enable automatic preview at point in the *Completions* buffer. This is 339 | ;; relevant when you use the default completion UI. 340 | :hook (completion-list-mode . consult-preview-at-point-mode) 341 | 342 | ;; The :init configuration is always executed (Not lazy) 343 | :init 344 | 345 | ;; Optionally configure the register formatting. This improves the register 346 | ;; preview for `consult-register', `consult-register-load', 347 | ;; `consult-register-store' and the Emacs built-ins. 348 | (setq register-preview-delay 0.5 349 | register-preview-function #'consult-register-format) 350 | 351 | ;; Optionally tweak the register preview window. 352 | ;; This adds thin lines, sorting and hides the mode line of the window. 353 | (advice-add #'register-preview :override #'consult-register-window) 354 | 355 | ;; Use Consult to select xref locations with preview 356 | (setq xref-show-xrefs-function #'consult-xref 357 | xref-show-definitions-function #'consult-xref) 358 | 359 | ;; Configure other variables and modes in the :config section, 360 | ;; after lazily loading the package. 361 | :config 362 | 363 | ;; Optionally configure preview. The default value 364 | ;; is 'any, such that any key triggers the preview. 365 | ;; (setq consult-preview-key 'any) 366 | ;; (setq consult-preview-key (kbd "M-.")) 367 | ;; (setq consult-preview-key (list (kbd "") (kbd ""))) 368 | ;; For some commands and buffer sources it is useful to configure the 369 | ;; :preview-key on a per-command basis using the `consult-customize' macro. 370 | (consult-customize 371 | consult-theme :preview-key '(:debounce 0.2 any) 372 | consult-ripgrep consult-git-grep consult-grep 373 | consult-bookmark consult-recent-file consult-xref 374 | consult--source-bookmark consult--source-file-register 375 | consult--source-recent-file consult--source-project-recent-file 376 | ;; :preview-key (kbd "M-.") 377 | :preview-key '(:debounce 0.4 any)) 378 | 379 | ;; Optionally configure the narrowing key. 380 | ;; Both < and C-+ work reasonably well. 381 | (setq consult-narrow-key "<") ;; (kbd "C-+") 382 | 383 | ;; Optionally make narrowing help available in the minibuffer. 384 | ;; You may want to use `embark-prefix-help-command' or which-key instead. 385 | ;; (define-key consult-narrow-map (vconcat consult-narrow-key "?") #'consult-narrow-help) 386 | 387 | ;; By default `consult-project-function' uses `project-root' from project.el. 388 | ;; Optionally configure a different project root function. 389 | ;; There are multiple reasonable alternatives to chose from. 390 | ;;;; 1. project.el (the default) 391 | ;; (setq consult-project-function #'consult--default-project--function) 392 | ;;;; 2. projectile.el (projectile-project-root) 393 | (autoload 'projectile-project-root "projectile") 394 | (setq consult-project-function (lambda (_) (projectile-project-root))) 395 | ;;;; 3. vc.el (vc-root-dir) 396 | ;; (setq consult-project-function (lambda (_) (vc-root-dir))) 397 | ;;;; 4. locate-dominating-file 398 | ;; (setq consult-project-function (lambda (_) (locate-dominating-file "." ".git"))) 399 | ) 400 | 401 | (use-package consult-flycheck 402 | :ensure t 403 | :after consult) 404 | 405 | (use-package consult-dir 406 | :ensure t 407 | :after consult 408 | :bind (("C-x C-d" . consult-dir) 409 | :map minibuffer-local-completion-map 410 | ("C-x C-d" . consult-dir) 411 | ("C-x C-j" . consult-dir-jump-file))) 412 | 413 | ;;------------------------------------------------------------------------------ 414 | ;; extras in the minibuffer 415 | (use-package marginalia 416 | :ensure t 417 | ;; Either bind `marginalia-cycle' globally or only in the minibuffer 418 | :bind (;;("M-a" . marginalia-cycle) 419 | :map minibuffer-local-map 420 | ("M-a" . marginalia-cycle)) 421 | 422 | ;; The :init configuration is always executed (Not lazy!) 423 | :init 424 | 425 | ;; Must be in the :init section of use-package such that the mode gets 426 | ;; enabled right away. Note that this forces loading the package. 427 | (marginalia-mode)) 428 | 429 | ;;------------------------------------------------------------------------------ 430 | ;; corfu for completion 431 | (use-package corfu 432 | :ensure t 433 | ;; Optional customizations 434 | ;; :custom 435 | ;; (corfu-cycle t) ;; Enable cycling for `corfu-next/previous' 436 | ;; (corfu-auto t) ;; Enable auto completion 437 | ;; (corfu-separator ?\s) ;; Orderless field separator 438 | ;; (corfu-quit-at-boundary nil) ;; Never quit at completion boundary 439 | ;; (corfu-quit-no-match nil) ;; Never quit, even if there is no match 440 | ;; (corfu-preview-current nil) ;; Disable current candidate preview 441 | ;; (corfu-preselect 'prompt) ;; Preselect the prompt 442 | ;; (corfu-on-exact-match nil) ;; Configure handling of exact matches 443 | ;; (corfu-scroll-margin 5) ;; Use scroll margin 444 | 445 | ;; Enable Corfu only for certain modes. 446 | ;; :hook ((prog-mode . corfu-mode) 447 | ;; (shell-mode . corfu-mode) 448 | ;; (eshell-mode . corfu-mode)) 449 | 450 | ;; Recommended: Enable Corfu globally. 451 | ;; This is recommended since Dabbrev can be used globally (M-/). 452 | ;; See also `corfu-excluded-modes'. 453 | :init 454 | (global-corfu-mode)) 455 | 456 | (defun corfu-move-to-minibuffer () 457 | (interactive) 458 | (let ((completion-extra-properties corfu--extra) 459 | completion-cycle-threshold completion-cycling) 460 | (apply #'consult-completion-in-region completion-in-region--data))) 461 | (define-key corfu-map "\M-m" #'corfu-move-to-minibuffer) 462 | 463 | (defun corfu-enable-always-in-minibuffer () 464 | "Enable Corfu in the minibuffer if Vertico/Mct are not active." 465 | (unless (or (bound-and-true-p mct--active) 466 | (bound-and-true-p vertico--input) 467 | (eq (current-local-map) read-passwd-map)) 468 | ;; (setq-local corfu-auto nil) ;; Enable/disable auto completion 469 | (setq-local corfu-echo-delay nil ;; Disable automatic echo and popup 470 | corfu-popupinfo-delay nil) 471 | (corfu-mode 1))) 472 | (add-hook 'minibuffer-setup-hook #'corfu-enable-always-in-minibuffer 1) 473 | 474 | (use-package corfu-terminal 475 | :ensure t 476 | :init 477 | (unless (display-graphic-p) 478 | (corfu-terminal-mode +1)) 479 | :after corfu) 480 | 481 | (use-package kind-icon 482 | :ensure t 483 | :after corfu 484 | :custom 485 | (kind-icon-default-face 'corfu-default) ; to compute blended backgrounds correctly 486 | :config 487 | (add-to-list 'corfu-margin-formatters #'kind-icon-margin-formatter)) 488 | 489 | ;;------------------------------------------------------------------------------ 490 | ;; powerthesaurus 491 | (use-package powerthesaurus 492 | :ensure t 493 | :bind 494 | (("C-c " . powerthesaurus-lookup-dwim))) 495 | 496 | ;;------------------------------------------------------------------------------ 497 | ;; embark 498 | (use-package embark 499 | :ensure t 500 | :bind 501 | (("C-." . embark-act) 502 | ("M-." . embark-dwim) 503 | ("C-h B" . embark-bindings)) 504 | :init 505 | ;; Optionally replace the key help with a completing-read interface 506 | (setq prefix-help-command #'embark-prefix-help-command) 507 | ;; Show the Embark target at point via Eldoc. You may adjust the Eldoc 508 | ;; strategy, if you want to see the documentation from multiple providers. 509 | (add-hook 'eldoc-documentation-functions #'embark-eldoc-first-target) 510 | ;; (setq eldoc-documentation-strategy #'eldoc-documentation-compose-eagerly) 511 | :config 512 | ;; Hide the mode line of the Embark live/completions buffers 513 | (add-to-list 'display-buffer-alist 514 | '("\\`\\*Embark Collect \\(Live\\|Completions\\)\\*" 515 | nil 516 | (window-parameters (mode-line-format . none))))) 517 | 518 | ;; Consult users will also want the embark-consult package. 519 | (use-package embark-consult 520 | :ensure t 521 | :hook 522 | (embark-collect-mode . consult-preview-at-point-mode)) 523 | 524 | ;;------------------------------------------------------------------------------ 525 | ;; time and world clock 526 | (use-package time 527 | :ensure t 528 | :custom 529 | (world-clock-time-format "%a %e %b %T %Z") 530 | (display-time-day-and-date t) 531 | (display-time-default-load-average nil) 532 | (zoneinfo-style-world-list 533 | '(("America/Los_Angeles" "Folsom") 534 | ("America/Phoenix" "Chandler") 535 | ("America/Denver" "Denver") 536 | ("Asia/Kolkata" "Bangalore") 537 | ("Asia/Singapore" "Penang") 538 | ("Australia/Brisbane" "Brisbane")))) 539 | 540 | ;;------------------------------------------------------------------------------ 541 | ;; increment numbers in region 542 | (defun replace-numbers-in-region-with-increments () 543 | "Replace numbers in a given region with numbers incrementing from zero." 544 | (interactive) 545 | (replace-regexp "[0-9]+" 546 | '((lambda (x n) (number-to-string n)) . 0) 547 | nil (region-beginning) (region-end))) 548 | (bind-key "C-S-#" 'replace-numbers-in-region-with-increments) 549 | 550 | ;;------------------------------------------------------------------------------ 551 | ;; fuzzy find 552 | (use-package fzf 553 | :ensure t 554 | :bind 555 | (("C-c z f" . fzf-find-file) 556 | ("C-c z g" . fzf-grep) 557 | ("C-c z z" . fzf)) 558 | :config 559 | (setq fzf/args "-x --color bw --print-query --margin=1,0 --no-hscroll" 560 | fzf/executable "fzf" 561 | fzf/git-grep-args "-i --line-number %s" 562 | ;; command used for `fzf-grep-*` functions 563 | ;; example usage for ripgrep: 564 | fzf/grep-command "rg --no-heading -nH" 565 | ;; fzf/grep-command "grep -nrH" 566 | ;; If nil, the fzf buffer will appear at the top of the window 567 | fzf/position-bottom t 568 | fzf/window-height 15)) 569 | 570 | ;;------------------------------------------------------------------------------ 571 | ;; cycle accented characters at point 572 | (use-package cyclekey 573 | :load-path (lambda () (concat dotfile-dir ".emacs.d/site-lisp/")) 574 | :ensure nil 575 | :init 576 | (setq cyclekey-languages '("French" "German" "Spanish") 577 | cyclekey-save-languages nil) 578 | :config 579 | (cyclekey-init) 580 | :bind ("M-o" . cyclekey-cycle)) 581 | 582 | ;;------------------------------------------------------------------------------ 583 | ;; Prettier modeline 584 | (use-package doom-modeline 585 | :ensure t 586 | :init (doom-modeline-mode 1) 587 | (setq doom-modeline-minor-modes t)) 588 | 589 | ;;------------------------------------------------------------------------------ 590 | ;; minions: minor modes in modeline 591 | (use-package minions 592 | :ensure t 593 | :config (minions-mode 1)) 594 | 595 | ;;------------------------------------------------------------------------------ 596 | ;; Writable grep buffer 597 | (use-package wgrep 598 | :ensure t) 599 | 600 | ;;------------------------------------------------------------------------------ 601 | ;; Lookup words 602 | (setopt dictionary-server "dict.org") 603 | (bind-key "C-c " 'dictionary-lookup-definition) 604 | 605 | ;;------------------------------------------------------------------------------ 606 | ;; Bind extra help keys 607 | (use-package help 608 | :bind 609 | (:map help-map 610 | ("=" . describe-char) 611 | ("j" . describe-face) 612 | ("-" . describe-keymap))) 613 | -------------------------------------------------------------------------------- /.emacs.d/cpp-modes.el: -------------------------------------------------------------------------------- 1 | ;; -*- lexical-binding: t; -*- 2 | ;;------------------------------------------------------------------------------ 3 | ;; C++ mode 4 | (setq-default c-basic-offset 2) 5 | 6 | ;;------------------------------------------------------------------------------ 7 | ;; LLVM root directory 8 | (defun find-file-recursive (directory filename) 9 | (if (and directory (file-directory-p directory)) 10 | (let ((found-files 11 | (directory-files-recursively directory 12 | (concat "^" filename "$")))) 13 | (if found-files 14 | (car found-files) 15 | nil)) 16 | nil)) 17 | 18 | (defun apply-macro (macro arg-list) 19 | (eval 20 | `(,macro ,@(cl-loop for arg in arg-list 21 | collect `(quote ,arg))))) 22 | 23 | (defun find-file-first-dir (directories filename) 24 | (apply-macro 'or (append (mapcar (lambda (dir) (find-file-recursive dir filename)) 25 | directories) 26 | (list (executable-find filename))))) 27 | 28 | (defcustom my-cpp-llvm-bin-marker "clangd" 29 | "The file to use to detect the llvm bin directory." 30 | :group 'my-cpp-config 31 | :type 'string 32 | :safe 'stringp) 33 | 34 | (defcustom my-cpp-llvm-roots 35 | '("/usr/lib/llvm-19/" "/usr/lib/llvm-18/" "/usr/lib/llvm-17/" "/usr/lib/llvm-16/") 36 | "The paths to search for llvm binaries." 37 | :group 'my-cpp-config 38 | :type 'list 39 | :safe 'listp) 40 | 41 | (defun find-llvm-root () 42 | (let ((llvm-bin (find-file-first-dir 43 | my-cpp-llvm-roots my-cpp-llvm-bin-marker))) 44 | (when llvm-bin 45 | (string-remove-suffix "bin/" 46 | (file-name-directory (file-truename llvm-bin)))))) 47 | 48 | (defun find-exe (root filename) 49 | (or (find-file-recursive root filename) 50 | (executable-find filename))) 51 | 52 | ;;------------------------------------------------------------------------------ 53 | ;; syntax highlighting 54 | (unless (use-treesit-for 'cpp) 55 | (use-package modern-cpp-font-lock 56 | :ensure t 57 | :hook (c++-mode . modern-c++-font-lock-mode))) 58 | 59 | ;;------------------------------------------------------------------------------ 60 | ;; clang-format 61 | (eval-after-load 'format-all 62 | '(progn 63 | (puthash 'clang-format (find-exe (find-llvm-root) "clang-format") 64 | format-all--executable-table) 65 | (puthash "C++" '(clang-format) format-all--language-table))) 66 | 67 | ;; clang-format files are YAML 68 | (add-to-list 'auto-mode-alist '("\\.clang-format\\'" . yaml-mode)) 69 | 70 | ;;------------------------------------------------------------------------------ 71 | ;; Auto insertion of headers 72 | (autoload 'cpp-auto-include/namespace-qualify-file "cpp-auto-include" 73 | "Explicitly qualify uses of the standard library with their namespace(s)." t) 74 | (autoload 'cpp-auto-include/ensure-includes-for-file "cpp-auto-include" 75 | "Auto-insert #include line(s) required for the current buffer." t) 76 | (autoload 'cpp-auto-include/ensure-includes-for-current-line "cpp-auto-include" 77 | "Auto-insert #include line(s) required for the current line." t) 78 | (eval-after-load 'cc-mode 79 | '(bind-keys :map c++-mode-map 80 | ("C-c n" . cpp-auto-include/namespace-qualify-file) 81 | ("C-c i" . cpp-auto-include/ensure-includes-for-file) 82 | ("C-c o" . cpp-auto-include/ensure-includes-for-current-line))) 83 | (eval-after-load 'c-ts-mode 84 | '(bind-keys :map c++-ts-mode-map 85 | ("C-c n" . cpp-auto-include/namespace-qualify-file) 86 | ("C-c i" . cpp-auto-include/ensure-includes-for-file) 87 | ("C-c o" . cpp-auto-include/ensure-includes-for-current-line))) 88 | 89 | ;;------------------------------------------------------------------------------ 90 | ;; Transposing arguments 91 | (autoload 'c-transpose-args "c-transpose-args" 92 | "Transpose function arguments." t) 93 | (autoload 'c-transpose-args-forward "c-transpose-args" 94 | "Transpose function arguments forward." t) 95 | (autoload 'c-transpose-args-backward "c-transpose-args" 96 | "Transpose function arguments backward." t) 97 | (eval-after-load 'cc-mode 98 | '(bind-keys :map c++-mode-map 99 | ("C-M-t" . c-transpose-args))) 100 | (eval-after-load 'c-ts-mode 101 | '(bind-keys :map c++-ts-mode-map 102 | ("C-M-t" . c-transpose-args))) 103 | 104 | ;;------------------------------------------------------------------------------ 105 | ;; Toggling include quote styles 106 | (autoload 'c-toggle-include-quotes "c-change-brackets" 107 | "Toggle between angled includes and quoted includes." t) 108 | (eval-after-load 'cc-mode 109 | '(bind-keys :map c++-mode-map 110 | ("C-c q" . c-toggle-include-quotes))) 111 | (eval-after-load 'c-ts-mode 112 | '(bind-keys :map c++-ts-mode-map 113 | ("C-c q" . c-toggle-include-quotes))) 114 | 115 | ;;------------------------------------------------------------------------------ 116 | ;; Align Boost.SML tables 117 | (autoload 'find-and-align-boost-sml "boost-sml" 118 | "Find and align Boost.SML tables." t) 119 | 120 | (eval-after-load 'cc-mode 121 | '(bind-keys :map c++-mode-map 122 | ("C-" . align) 123 | ("C-]" . find-and-align-boost-sml))) 124 | (eval-after-load 'c-ts-mode 125 | '(bind-keys :map c++-ts-mode-map 126 | ("C-" . align) 127 | ("C-]" . find-and-align-boost-sml))) 128 | 129 | ;;------------------------------------------------------------------------------ 130 | ;; lsp + clangd 131 | (use-package flycheck-clang-tidy 132 | :ensure t 133 | :config 134 | (setq flycheck-clang-tidy-executable (find-exe (find-llvm-root) "clang-tidy")) 135 | :defer) 136 | 137 | (use-package lsp-mode 138 | :ensure t 139 | :custom 140 | (lsp-completion-provider :none) 141 | :init 142 | (defun my/lsp-mode-setup-completion () 143 | (setf (alist-get 'styles (alist-get 'lsp-capf completion-category-defaults)) 144 | '(orderless))) 145 | (defun my/config-lsp-mode () 146 | ;; Start lsp mode etc unless we're in a temp buffer 147 | ;; (don't do it when exporting org-mode blocks) 148 | (unless (string-match-p (regexp-quote "*temp*") (buffer-name)) 149 | (require 'lsp-clangd) 150 | (lsp) 151 | (require 'lsp-diagnostics) 152 | (lsp-diagnostics-flycheck-enable) 153 | (flycheck-clang-tidy-setup) 154 | (flycheck-add-next-checker 'lsp 'c/c++-clang-tidy) 155 | (bind-keys :map flycheck-mode-map 156 | ("M-" . flycheck-next-error) 157 | ("M-" . flycheck-previous-error)))) 158 | :config 159 | (setq lsp-enable-indentation nil 160 | lsp-auto-guess-root t 161 | lsp-clangd-binary-path (find-exe (find-llvm-root) "clangd") 162 | lsp-prefer-flymake nil 163 | lsp-headerline-breadcrumb-enable t) 164 | :hook ((lsp-mode . lsp-enable-which-key-integration) 165 | (c++-mode . my/config-lsp-mode) 166 | (c++-ts-mode . my/config-lsp-mode) 167 | (lsp-completion-mode . my/lsp-mode-setup-completion))) 168 | 169 | (use-package lsp-ui 170 | :ensure t 171 | :config 172 | (setq lsp-ui-sideline-enable t 173 | lsp-ui-sideline-show-symbol t 174 | lsp-ui-sideline-show-hover t 175 | lsp-ui-sideline-show-code-actions t 176 | lsp-ui-sideline-update-mode 'point 177 | lsp-ui-doc-header t 178 | lsp-ui-doc-include-signature t 179 | lsp-ui-sideline-ignore-duplicate t 180 | lsp-ui-flycheck-enable t 181 | lsp-ui-imenu-enable t) 182 | (define-key lsp-ui-mode-map [remap xref-find-definitions] #'lsp-ui-peek-find-definitions) 183 | (define-key lsp-ui-mode-map [remap xref-find-references] #'lsp-ui-peek-find-references) 184 | (define-key lsp-ui-mode-map (kbd "C-") #'lsp-ui-sideline-apply-code-actions) 185 | (define-key lsp-ui-mode-map (kbd "C-c m") #'lsp-ui-imenu) 186 | (defun my/config-lsp-ui-mode () 187 | (lsp-enable-imenu) 188 | (lsp-ui-mode)) 189 | :hook (lsp-mode . my/config-lsp-ui-mode)) 190 | 191 | ;;------------------------------------------------------------------------------ 192 | ;; building & error navigation 193 | (setq compilation-scroll-output t) 194 | 195 | ;; Remove compilation window on success 196 | (setq compilation-finish-functions 197 | (lambda (buf str) 198 | (if (null (string-match ".*exited abnormally.*" str)) 199 | ;;no errors, make the compilation window go away in a few seconds 200 | (progn 201 | (run-at-time 202 | "1 sec" nil 'delete-windows-on 203 | (get-buffer-create "*compilation*")) 204 | (message "No compilation errors!"))))) 205 | 206 | (eval-after-load 'cc-mode 207 | '(bind-keys :map c++-mode-map 208 | ("M-k" . projectile-compile-project))) 209 | (eval-after-load 'c-ts-mode 210 | '(bind-keys :map c++-ts-mode-map 211 | ("M-k" . projectile-compile-project))) 212 | 213 | ;; make compilation buffers support ANSI colours 214 | (defun colorize-compilation-buffer () 215 | (require 'ansi-color) 216 | (ansi-color-apply-on-region compilation-filter-start (point))) 217 | (add-hook 'compilation-filter-hook 'colorize-compilation-buffer) 218 | 219 | ;;------------------------------------------------------------------------------ 220 | ;; Debugging 221 | 222 | (setq gdb-many-windows t 223 | gdb-show-main t) 224 | 225 | (defun gdb-run-or-cont (arg) 226 | "Run or continue program with numeric argument ARG." 227 | (interactive "p") 228 | (when (boundp 'gdb-thread-number) 229 | (if (eq gdb-thread-number nil) 230 | (gud-run arg) 231 | (gud-cont arg)))) 232 | 233 | (use-package gud 234 | :bind (("C-x C-a " . gdb-run-or-cont))) 235 | 236 | ;;------------------------------------------------------------------------------ 237 | ;; Manage template spew 238 | (require 'tspew) 239 | (add-hook 'compilation-mode-hook 'tspew-mode) 240 | -------------------------------------------------------------------------------- /.emacs.d/custom.el: -------------------------------------------------------------------------------- 1 | ;; -*- lexical-binding: t; -*- 2 | (custom-set-variables 3 | ;; custom-set-variables was added by Custom. 4 | ;; If you edit it by hand, you could mess it up, so be careful. 5 | ;; Your init file should contain only one such instance. 6 | ;; If there is more than one, they won't work right. 7 | '(ansi-color-faces-vector 8 | [default default default italic underline success warning error]) 9 | '(bm-cycle-all-buffers t) 10 | '(bm-marker 'bm-marker-right t) 11 | '(case-fold-search t) 12 | '(comint-buffer-maximum-size 20000) 13 | '(comint-completion-addsuffix t) 14 | '(comint-get-old-input (lambda nil "") t) 15 | '(comint-input-ignoredups t) 16 | '(comint-input-ring-size 5000) 17 | '(comint-move-point-for-output nil) 18 | '(comint-prompt-read-only nil) 19 | '(comint-scroll-show-maximum-output t) 20 | '(comint-scroll-to-bottom-on-input t) 21 | '(custom-safe-themes 22 | '("b66970f42d765a40fdb2b6b86dd2ab6289bed518cf4d8973919e5f24f0ca537b" 23 | "eb50f36ed5141c3f702f59baa1968494dc8e9bd22ed99d2aaa536c613c8782db" 24 | "4320a92406c5015e8cba1e581a88f058765f7400cf5d885a3aa9b7b9fc448fa7" 25 | "3c83b3676d796422704082049fc38b6966bcad960f896669dfc21a7a37a748fa" 26 | "c280a47cb66453f297553efcd0152666bdaab65ef4e8dfc498973f81ec1b60bb" 27 | "01c3b475bc176a1eb66ad7f2513523f59fb131c58132b975b254624198c8fd41" 28 | "756597b162f1be60a12dbd52bab71d40d6a2845a3e3c2584c6573ee9c332a66e" default)) 29 | '(ecb-layout-name "left3") 30 | '(ecb-options-version "2.40") 31 | '(ecb-tip-of-the-day nil) 32 | '(elpy-modules 33 | '(elpy-module-eldoc elpy-module-pyvenv elpy-module-highlight-indentation 34 | elpy-module-yasnippet elpy-module-sane-defaults)) 35 | '(highlight-symbol-idle-delay 0) 36 | '(highlight-symbol-on-navigation-p t) 37 | '(lsp-ui-doc-position 'bottom) 38 | '(magit-todos-insert-after '(bottom) nil nil "Changed by setter of obsolete option `magit-todos-insert-at'") 39 | '(org-export-with-sub-superscripts '{}) 40 | '(package-selected-packages nil) 41 | '(protect-buffer-bury-p nil) 42 | '(safe-local-variable-values 43 | '((eval when (require 'rainbow-mode nil t) (rainbow-mode 1)))) 44 | '(vc-annotate-background "#2f2f2f") 45 | '(vc-annotate-color-map 46 | '((20 . "#dc322f") (40 . "#D01A4E") (60 . "#cb4b16") (80 . "#b58900") 47 | (100 . "#b58900") (120 . "#b58900") (140 . "#5f127b") (160 . "#5f127b") 48 | (180 . "#859900") (200 . "#859900") (220 . "#859900") (240 . "#859900") 49 | (260 . "#2aa198") (280 . "#268bd2") (300 . "#268bd2") (320 . "#268bd2") 50 | (340 . "#94BFF3") (360 . "#d33682"))) 51 | '(vc-annotate-very-old-color "#d33682") 52 | '(vc-follow-symlinks nil)) 53 | (custom-set-faces 54 | ;; custom-set-faces was added by Custom. 55 | ;; If you edit it by hand, you could mess it up, so be careful. 56 | ;; Your init file should contain only one such instance. 57 | ;; If there is more than one, they won't work right. 58 | '(fixed-pitch ((t (:family "Inconsolata")))) 59 | '(org-document-title ((t (:family "Inconsolata" :height 1.0))))) 60 | -------------------------------------------------------------------------------- /.emacs.d/git-config.el: -------------------------------------------------------------------------------- 1 | ;; -*- lexical-binding: t; -*- 2 | ;;------------------------------------------------------------------------------ 3 | ;; Git interactions 4 | 5 | ;; magit 6 | (use-package magit 7 | :ensure t 8 | :bind 9 | (("C-c g" . magit-status) 10 | ("C-c B" . magit-blame))) 11 | 12 | (use-package git-modes 13 | :ensure t) 14 | 15 | ;; git time machine 16 | (use-package git-timemachine 17 | :ensure t 18 | :bind 19 | (("C-c h" . git-timemachine-toggle))) 20 | 21 | ;; git messenger 22 | (use-package git-messenger 23 | :ensure t 24 | :bind 25 | (("C-c b" . git-messenger:popup-message)) 26 | :config 27 | (bind-key "m" 'git-messenger:copy-message git-messenger-map) 28 | ;; Enable magit-commit-mode after typing 's', 'S', 'd' 29 | (add-hook 'git-messenger:popup-buffer-hook #'magit-commit-mode)) 30 | 31 | ;; on-the-fly diff highlighting 32 | (use-package diff-hl 33 | :ensure t 34 | :config 35 | (setq diff-hl-side 'right) 36 | (diff-hl-flydiff-mode) 37 | :hook 38 | (prog-mode . diff-hl-mode)) 39 | 40 | ;; prevent annoying reverts during conflict editing 41 | (setq auto-revert-vc-info nil) 42 | 43 | ;; get url links 44 | (use-package git-link 45 | :ensure t 46 | :bind 47 | (("C-c u" . git-link))) 48 | 49 | ;; display TODOs 50 | (use-package magit-todos 51 | :ensure t 52 | :config (magit-todos-mode 1)) 53 | -------------------------------------------------------------------------------- /.emacs.d/global-keys.el: -------------------------------------------------------------------------------- 1 | ;; -*- lexical-binding: t; -*- 2 | ;; (bind-key "C-o" 'goto-line) ;; replaced by consult 3 | (bind-key "M-r" 'replace-string) 4 | (bind-key "M-k" 'compile) 5 | (bind-key "M-SPC" 'cycle-spacing) 6 | 7 | ;; old-style searching 8 | (bind-key "C-M-s" 'isearch-forward) 9 | (bind-key "C-M-r" 'isearch-backward) 10 | 11 | ;; Action of home key 12 | (defun beginning-of-line-or-indentation () 13 | "move to beginning of line, or indentation" 14 | (interactive) 15 | (when this-command-keys-shift-translated 16 | (unless mark-active (push-mark nil t t))) 17 | (if (bolp) 18 | (back-to-indentation) 19 | (beginning-of-line)) 20 | (when this-command-keys-shift-translated 21 | (setq transient-mark-mode (cons 'only transient-mark-mode)))) 22 | (bind-key "" 'beginning-of-line-or-indentation) 23 | 24 | ;; Turn off insert 25 | (defun do-nothing () (interactive)) 26 | (bind-key "" 'do-nothing) 27 | (bind-key "" 'do-nothing) 28 | 29 | ;; Kill-ring menu 30 | (defun popup-kill-ring-menu () 31 | "Show the kill ring in a popup menu." 32 | (interactive) 33 | (popup-menu 'yank-menu)) 34 | (bind-key "C-c y" 'popup-kill-ring-menu) 35 | 36 | ;; Cycle buffers/windows with F5-F8 37 | (bind-key "" 'next-multiframe-window) 38 | (bind-key "" 'previous-multiframe-window) 39 | (bind-key "" 'previous-buffer) 40 | (bind-key "" 'next-buffer) 41 | 42 | ;; Moving windows 43 | (bind-key "C-c " 'windmove-left) 44 | (bind-key "C-c " 'windmove-right) 45 | (bind-key "C-c " 'windmove-up) 46 | (bind-key "C-c " 'windmove-down) 47 | 48 | ;; Use hippie-expand instead of dabbrev-expand 49 | (bind-key "M-/" 'hippie-expand) 50 | 51 | ;; Don't ask for which buffer to kill 52 | (bind-key "C-x k" 'kill-current-buffer) 53 | 54 | ;; quick-calc on C-= 55 | (bind-key "C-=" 'quick-calc) 56 | 57 | ;; comments 58 | (defun my/comment-dwim () 59 | "Comment region if active, else comment line. 60 | 61 | This avoids the excess region commenting of `comment-line' while also avoiding the weird single-line 62 | behavior of `comment-dwim'." 63 | (interactive) 64 | (save-excursion 65 | (if (use-region-p) 66 | (call-interactively #'comment-or-uncomment-region) 67 | (call-interactively #'comment-line)))) 68 | (bind-key "M-;" 'my/comment-dwim) 69 | 70 | ;; wrap selection in delimiters 71 | (defvar insert-pair-map 72 | (let ((map (make-sparse-keymap))) 73 | (define-key map [t] #'insert-pair) 74 | map)) 75 | (bind-key "C-(" insert-pair-map) 76 | 77 | ;; C-a my attention key in tmux, so remap emacs' C-a to C-b 78 | ;; C-b is normally bound to backward-char 79 | (bind-key "C-b" 'move-beginning-of-line) 80 | 81 | (when (> emacs-major-version 28) 82 | (bind-key "C-c d" 'duplicate-dwim)) 83 | 84 | ;; prevent accidental suspend 85 | (global-unset-key (kbd "C-x C-z")) 86 | -------------------------------------------------------------------------------- /.emacs.d/graphic-display.el: -------------------------------------------------------------------------------- 1 | ;; -*- lexical-binding: t; -*- 2 | ;;------------------------------------------------------------------------------ 3 | ;; smooth scrolling 4 | (setq pixel-scroll-mode t) 5 | 6 | ;;------------------------------------------------------------------------------ 7 | ;; font: Inconsolata 8 | (set-frame-font "Berkeley Mono-11") 9 | 10 | (custom-set-faces 11 | '(fixed-pitch ((t (:family "Berkeley Mono"))))) 12 | (custom-set-faces 13 | '(org-document-title ((t (:family "Berkeley Mono" :height 1.0))))) 14 | 15 | ;;------------------------------------------------------------------------------ 16 | (use-package dash 17 | :ensure t) 18 | 19 | ;;------------------------------------------------------------------------------ 20 | ;; Sizing/docking 21 | (setq frame-resize-pixelwise t) 22 | 23 | (defun monitor-width (monitor) 24 | (nth 3 (assq 'geometry monitor))) 25 | 26 | (defun frame-max-height (&optional frame) 27 | (interactive) 28 | (set-frame-parameter frame 'fullscreen 'fullheight)) 29 | 30 | (defun dock-frame-left (&optional frame monitor) 31 | (interactive) 32 | (setq frame (or frame (selected-frame))) 33 | (setq monitor (or monitor (frame-monitor-attributes frame))) 34 | (let* ((monitor-list (-take-while 35 | (lambda (x) (not (equal monitor x))) 36 | (display-monitor-attributes-list))) 37 | (widths (mapcar #'monitor-width monitor-list)) 38 | (x (apply '+ widths))) 39 | (set-frame-parameter frame 'left x))) 40 | 41 | (defun dock-frame-right (&optional frame monitor) 42 | (interactive) 43 | (setq frame (or frame (selected-frame))) 44 | (setq monitor (or monitor (frame-monitor-attributes frame))) 45 | (let* ((monitor-list (-take-while 46 | (lambda (x) (not (equal monitor x))) 47 | (display-monitor-attributes-list))) 48 | (widths (mapcar #'monitor-width monitor-list)) 49 | (x (+ (apply '+ widths) (monitor-width monitor)))) 50 | (set-frame-parameter frame 'left (- x (frame-pixel-width frame))))) 51 | 52 | (defun size-frame-default () 53 | (set-frame-parameter nil 'width column-wrap-hard) 54 | (frame-max-height)) 55 | 56 | (bind-key "C-S-" 'frame-max-height) 57 | (bind-key "C-" 'dock-frame-left) 58 | (bind-key "C-" 'dock-frame-right) 59 | 60 | ;;------------------------------------------------------------------------------ 61 | ;; Frame opacity 62 | (defun sanityinc/adjust-opacity (frame incr) 63 | (let* ((oldalpha (or (frame-parameter frame 'alpha) 100)) 64 | (newalpha (+ incr oldalpha))) 65 | (when (and (<= frame-alpha-lower-limit newalpha) (>= 100 newalpha)) 66 | (modify-frame-parameters frame (list (cons 'alpha newalpha)))))) 67 | 68 | (bind-key "M-C-8" (lambda () (interactive) (sanityinc/adjust-opacity nil -2))) 69 | (bind-key "M-C-9" (lambda () (interactive) (sanityinc/adjust-opacity nil 2))) 70 | (bind-key "M-C-0" (lambda () (interactive) (modify-frame-parameters nil `((alpha . 100))))) 71 | 72 | ;;------------------------------------------------------------------------------ 73 | ;; All the icons! 74 | (use-package all-the-icons 75 | :ensure t 76 | :config 77 | (unless (file-exists-p 78 | (concat (getenv "HOME") "/.local/share/fonts/all-the-icons.ttf")) 79 | (all-the-icons-install-fonts t))) 80 | 81 | (use-package all-the-icons-dired 82 | :ensure t 83 | :hook dired-mode 84 | :after all-the-icons) 85 | 86 | ;;------------------------------------------------------------------------------ 87 | ;; Display file info 88 | (use-package file-info 89 | :ensure t 90 | :bind ("C-c " . 'file-info-show) 91 | :config 92 | (setq hydra-hint-display-type 'posframe) 93 | (setq hydra-posframe-show-params `(:poshandler posframe-poshandler-frame-center 94 | :internal-border-width 2 95 | :internal-border-color "#61AFEF" 96 | :left-fringe 16 97 | :right-fringe 16))) 98 | 99 | ;;------------------------------------------------------------------------------ 100 | ;; Ultrascroll 101 | (package-vc-install 102 | '(ultra-scroll 103 | :vc-backend Git 104 | :url "https://github.com/jdtsmith/ultra-scroll")) 105 | 106 | (use-package ultra-scroll 107 | ;:load-path "~/code/emacs/ultra-scroll" ; if you git clone'd instead of package-vc-install 108 | :init 109 | (setq scroll-conservatively 101 ; important! 110 | scroll-margin 0) 111 | :config 112 | (ultra-scroll-mode 1)) 113 | -------------------------------------------------------------------------------- /.emacs.d/hacks.el: -------------------------------------------------------------------------------- 1 | ;; -*- lexical-binding: t; -*- 2 | ;;------------------------------------------------------------------------------ 3 | ;; Org-mode customization: allow left-alignment of numeric fields 4 | 5 | (defcustom org-table-numeric-field-alignment "r" 6 | "The default alignment for fields containing numbers." 7 | :group 'org-table-settings 8 | :type 'string) 9 | 10 | (defun my-org-table-align () 11 | "Align the table at point by aligning all vertical bars." 12 | (interactive) 13 | (let ((beg (org-table-begin)) 14 | (end (copy-marker (org-table-end)))) 15 | (org-table-save-field 16 | ;; Make sure invisible characters in the table are at the right 17 | ;; place since column widths take them into account. 18 | (font-lock-ensure beg end) 19 | (move-marker org-table-aligned-begin-marker beg) 20 | (move-marker org-table-aligned-end-marker end) 21 | (goto-char beg) 22 | (org-table-with-shrunk-columns 23 | (let* ((indent (progn (looking-at "[ \t]*") (match-string 0))) 24 | ;; Table's rows as lists of fields. Rules are replaced 25 | ;; by nil. Trailing spaces are removed. 26 | (fields (mapcar 27 | (lambda (l) 28 | (and (not (string-match-p org-table-hline-regexp l)) 29 | (org-split-string l "[ \t]*|[ \t]*"))) 30 | (split-string (buffer-substring beg end) "\n" t))) 31 | ;; Compute number of columns. If the table contains no 32 | ;; field, create a default table and bail out. 33 | (columns-number 34 | (if fields (apply #'max (mapcar #'length fields)) 35 | (kill-region beg end) 36 | (org-table-create org-table-default-size) 37 | (user-error "Empty table - created default table"))) 38 | (widths nil) 39 | (alignments nil)) 40 | ;; Compute alignment and width for each column. 41 | (dotimes (i columns-number) 42 | (let* ((max-width 1) 43 | (fixed-align? nil) 44 | (numbers 0) 45 | (non-empty 0)) 46 | (dolist (row fields) 47 | (let ((cell (or (nth i row) ""))) 48 | (setq max-width (max max-width (org-string-width cell))) 49 | (cond (fixed-align? nil) 50 | ((equal cell "") nil) 51 | ((string-match "\\`<\\([lrc]\\)[0-9]*>\\'" cell) 52 | (setq fixed-align? (match-string 1 cell))) 53 | (t 54 | (cl-incf non-empty) 55 | (when (string-match-p org-table-number-regexp cell) 56 | (cl-incf numbers)))))) 57 | (push max-width widths) 58 | (push (cond 59 | (fixed-align?) 60 | ((>= numbers (* org-table-number-fraction non-empty)) 61 | org-table-numeric-field-alignment) ;; normally always "r" 62 | (t "l")) 63 | alignments))) 64 | (setq widths (nreverse widths)) 65 | (setq alignments (nreverse alignments)) 66 | ;; Store alignment of this table, for later editing of single 67 | ;; fields. 68 | (setq org-table-last-alignment alignments) 69 | (setq org-table-last-column-widths widths) 70 | ;; Build new table rows. Only replace rows that actually 71 | ;; changed. 72 | (dolist (row fields) 73 | (let ((previous (buffer-substring (point) (line-end-position))) 74 | (new 75 | (format "%s|%s|" 76 | indent 77 | (if (null row) ;horizontal rule 78 | (mapconcat (lambda (w) (make-string (+ 2 w) ?-)) 79 | widths 80 | "+") 81 | (let ((cells ;add missing fields 82 | (append row 83 | (make-list (- columns-number 84 | (length row)) 85 | "")))) 86 | (mapconcat #'identity 87 | (cl-mapcar #'org-table--align-field 88 | cells 89 | widths 90 | alignments) 91 | "|")))))) 92 | (if (equal new previous) 93 | (forward-line) 94 | (insert new "\n") 95 | (delete-region (point) (line-beginning-position 2))))) 96 | (set-marker end nil) 97 | (when org-table-overlay-coordinates (org-table-overlay-coordinates)) 98 | (setq org-table-may-need-update nil)))))) 99 | 100 | (advice-add #'org-table-align :override #'my-org-table-align) 101 | 102 | (defun my-clang-tidy-error-to-url-slug (args) 103 | (list (replace-regexp-in-string "^\\([^-]*\\)-" "\\1/" (car args)))) 104 | 105 | (advice-add #'lsp-cpp-flycheck-clang-tidy--show-documentation 106 | :filter-args #'my-clang-tidy-error-to-url-slug) 107 | 108 | (advice-add #'flycheck-clang-tidy--show-documentation 109 | :filter-args #'my-clang-tidy-error-to-url-slug) 110 | 111 | ;;------------------------------------------------------------------------------ 112 | ;; workaround for broken bm package 113 | ;; https://github.com/joodland/bm/issues/45 114 | 115 | (defun my/bm-lists (&optional direction predicate) 116 | "Return a pair of lists giving all the bookmarks of the current buffer. 117 | The car has all the bookmarks before the overlay center; 118 | the cdr has all the bookmarks after the overlay center. 119 | A bookmark implementation of `overlay-lists'. 120 | 121 | If optional argument DIRECTION is provided, only return bookmarks 122 | in the specified direction. 123 | 124 | If optional argument PREDICATE is provided, it is used as a 125 | selection criteria for filtering the lists." 126 | (if (null predicate) 127 | (setq predicate 'bm-bookmarkp)) 128 | 129 | (overlay-recenter (point)) 130 | (cond ((equal 'forward direction) 131 | (cons nil (remq nil (mapcar predicate (overlays-in (point) (point-max)))))) 132 | ((equal 'backward direction) 133 | (cons (remq nil (mapcar predicate (overlays-in (point-min) (point)))) nil)) 134 | (t 135 | (cons 136 | (remq nil (mapcar predicate (overlays-in (point-min) (point)))) 137 | (remq nil (mapcar predicate (overlays-in (point) (point-max)))))))) 138 | 139 | (advice-add #'bm-lists 140 | :override #'my/bm-lists) 141 | 142 | ;;------------------------------------------------------------------------------ 143 | ;; workaround for broken clipetty with local tmux 144 | (defun my/clipetty--emit (string) 145 | "Emit STRING, optionally wrapped in a DCS, to an appropriate tty." 146 | (let ((tmux (getenv "TMUX" (selected-frame))) 147 | (term (getenv "TERM" (selected-frame))) 148 | (ssh-tty (or (getenv "SSH_TTY" (selected-frame)) (terminal-name)))) 149 | (if (<= (length string) clipetty--max-cut) 150 | (write-region 151 | (clipetty--dcs-wrap string tmux term ssh-tty) 152 | nil 153 | (clipetty--tty ssh-tty tmux) 154 | t 155 | 0) 156 | (message "Selection too long to send to terminal %d" (length string)) 157 | (sit-for 1)))) 158 | 159 | (advice-add #'clipetty--emit 160 | :override #'my/clipetty--emit) 161 | 162 | ;; c++-ts-mode operators are not complete 163 | ;; added: <=> 164 | (defvar c-ts-mode--operators 165 | '("=" "-" "*" "/" "+" "%" "~" "|" "&" "^" "<<" ">>" "->" 166 | "." "<" "<=" ">=" ">" "==" "!=" "!" "&&" "||" "-=" 167 | "+=" "*=" "/=" "%=" "|=" "&=" "^=" ">>=" "<<=" "--" "++" "<=>") 168 | "C/C++ operators for tree-sitter font-locking.") 169 | -------------------------------------------------------------------------------- /.emacs.d/local.el: -------------------------------------------------------------------------------- 1 | ;; -*- lexical-binding: t; -*- 2 | (defun files-in-below-directory (directory) 3 | "List the .el files in DIRECTORY and in its sub-directories." 4 | ;; Although the function will be used non-interactively, 5 | ;; it will be easier to test if we make it interactive. 6 | ;; The directory will have a name such as 7 | ;; "/usr/local/share/emacs/22.1.1/lisp/" 8 | (interactive "directory name: ") 9 | (let (el-files-list 10 | (current-directory-list 11 | (directory-files-and-attributes directory t))) 12 | ;; while we are in the current directory 13 | (while current-directory-list 14 | (cond 15 | ;; check to see whether filename ends in `.el' 16 | ;; and if so, append its name to a list. 17 | ((equal ".el" (substring (car (car current-directory-list)) -3)) 18 | (setq el-files-list 19 | (cons (car (car current-directory-list)) el-files-list))) 20 | ;; check whether filename is that of a directory 21 | ((eq t (car (cdr (car current-directory-list)))) 22 | ;; decide whether to skip or recurse 23 | (if 24 | (equal "." 25 | (substring (car (car current-directory-list)) -1)) 26 | ;; then do nothing since filename is that of 27 | ;; current directory or parent, "." or ".." 28 | () 29 | ;; else descend into the directory and repeat the process 30 | (setq el-files-list 31 | (append 32 | (files-in-below-directory 33 | (car (car current-directory-list))) 34 | el-files-list))))) 35 | ;; move to the next filename in the list; this also 36 | ;; shortens the list so the while loop eventually comes to an end 37 | (setq current-directory-list (cdr current-directory-list))) 38 | ;; return the filenames 39 | el-files-list)) 40 | 41 | (let ((local-dir (concat dotfile-dir ".emacs.d/local/"))) 42 | (when (file-directory-p local-dir) 43 | (mapc 'load (files-in-below-directory local-dir)))) 44 | -------------------------------------------------------------------------------- /.emacs.d/major-modes.el: -------------------------------------------------------------------------------- 1 | ;; -*- lexical-binding: t; -*- 2 | ;;------------------------------------------------------------------------------ 3 | ;; markdown mode 4 | (use-package markdown-mode 5 | :ensure t 6 | :mode ("\\.md\\'" . markdown-mode)) 7 | 8 | ;; Shells 9 | (setq ls-lisp-use-insert-directory-program t) 10 | (setq insert-directory-program "/bin/ls") 11 | 12 | ;; For editing command-line in bash: 13 | ;; When $EDITOR="emacsclient -nw", C-x C-e on the command line 14 | ;; allows editing the line in emacs 15 | (add-to-list 'auto-mode-alist '("\\`/tmp/bash-fc\.[a-z0-9A-Z]+\\'" . sh-mode)) 16 | 17 | ;;------------------------------------------------------------------------------ 18 | ;; CMake 19 | (use-package cmake-mode 20 | :ensure t 21 | :mode (("\\.cmake\\'" . cmake-mode) 22 | ("^CMakeLists.txt$" . cmake-mode))) 23 | 24 | (use-package cmake-font-lock 25 | :ensure t 26 | :hook (cmake-mode . cmake-font-lock-activate)) 27 | 28 | ;;------------------------------------------------------------------------------ 29 | ;; Cucumber/Gherkin 30 | (use-package feature-mode 31 | :ensure t 32 | :mode ("\\.feature$" . feature-mode)) 33 | 34 | ;;------------------------------------------------------------------------------ 35 | ;; Yaml 36 | (use-package yaml-mode 37 | :ensure t 38 | :mode ("\\.yml$" . yaml-mode)) 39 | 40 | ;;------------------------------------------------------------------------------ 41 | ;; JSON 42 | (use-package json-mode 43 | :ensure t 44 | :mode ("\\.json$" . json-mode)) 45 | 46 | ;;------------------------------------------------------------------------------ 47 | ;; Dired 48 | (use-package dired 49 | :bind 50 | (("C-x j" . dired-jump)) 51 | (:map dired-mode-map 52 | ("M-" . jjgr-dired-up-directory) 53 | ("" . jjgr-dired-find-file) 54 | ("g" . dired-git-info-mode) 55 | ("r" . revert-buffer) 56 | ("b" . jjgr-dired-up-directory) 57 | ([remap dired-summary] . which-key-show-major-mode)) 58 | :custom 59 | (dired-find-subdir t "Reuse buffers for opened directories") 60 | :config 61 | (setq dired-dwim-target t) 62 | (defun jjgr-dired-up-directory (&optional other-window) 63 | "Run Dired on parent directory of current directory, reusing buffer." 64 | (interactive "P") 65 | (let* ((dir (dired-current-directory)) 66 | (orig (current-buffer)) 67 | (up (file-name-directory (directory-file-name dir)))) 68 | (or (dired-goto-file (directory-file-name dir)) 69 | ;; Only try dired-goto-subdir if buffer has more than one dir. 70 | (and (cdr dired-subdir-alist) 71 | (dired-goto-subdir up)) 72 | (progn 73 | (kill-buffer orig) 74 | (dired up) 75 | (dired-goto-file dir))))) 76 | (defun jjgr-dired-find-file (&optional prefix) 77 | "Open file with either operating system defaults or within Emacs." 78 | (interactive "P") 79 | (if prefix 80 | (org-open-file (dired-get-file-for-visit) 'system) 81 | (dired-find-file))) 82 | (defun my/dired-config () 83 | (diredfl-mode) 84 | (hl-line-mode) 85 | (set-face-background hl-line-face "gray13")) 86 | :hook (dired-mode . my/dired-config)) 87 | 88 | (use-package diredfl 89 | :ensure t 90 | :defer) 91 | 92 | (use-package dired-git-info 93 | :ensure t 94 | :after dired) 95 | 96 | ;;------------------------------------------------------------------------------ 97 | ;; Dirvish 98 | (if (display-graphic-p) 99 | (use-package dirvish 100 | :ensure t 101 | :after dired 102 | :config 103 | (dirvish-override-dired-mode) 104 | :bind 105 | (("C-x j" . dirvish-dwim)))) 106 | 107 | ;;------------------------------------------------------------------------------ 108 | ;; AsciiDoc 109 | (use-package adoc-mode 110 | :ensure t) 111 | 112 | ;;------------------------------------------------------------------------------ 113 | ;; Graphviz dotfiles 114 | (use-package graphviz-dot-mode 115 | :ensure t 116 | :config 117 | (setq graphviz-dot-indent-width 2)) 118 | -------------------------------------------------------------------------------- /.emacs.d/minor-modes.el: -------------------------------------------------------------------------------- 1 | ;; -*- lexical-binding: t; -*- 2 | ;;------------------------------------------------------------------------------ 3 | ;; Show matching parens 4 | (show-paren-mode 1) 5 | (setq show-paren-delay 0) 6 | 7 | ;;------------------------------------------------------------------------------ 8 | ;; Syntactic close 9 | (use-package syntactic-close 10 | :ensure t 11 | :bind ("M-]" . syntactic-close)) 12 | 13 | ;;------------------------------------------------------------------------------ 14 | ;; Show column/line numbers 15 | (column-number-mode) 16 | (add-hook 'prog-mode-hook 'display-line-numbers-mode) 17 | 18 | ;;------------------------------------------------------------------------------ 19 | ;; Auto-revert buffers 20 | (global-auto-revert-mode 1) 21 | (setq global-auto-revert-non-file-buffers t) 22 | (diminish 'auto-revert-mode) 23 | 24 | ;;------------------------------------------------------------------------------ 25 | ;; Rainbow delimiters 26 | (use-package rainbow-delimiters 27 | :ensure t 28 | :hook (prog-mode . rainbow-delimiters-mode)) 29 | 30 | ;;------------------------------------------------------------------------------ 31 | ;; Rainbow hex colors & color names 32 | ;; use custom regexp for #colors to avoid clash with e.g. #define 33 | (use-package rainbow-mode 34 | :ensure t 35 | :config 36 | (setq rainbow-hexadecimal-colors-font-lock-keywords 37 | '(("[^&]\\(#\\(?:[0-9a-fA-F]\\{3\\}\\)+\\{1,4\\}\\)\\b" 38 | (1 (rainbow-colorize-itself 1))) 39 | ("^\\(#\\(?:[0-9a-fA-F]\\{3\\}\\)+\\{1,4\\}\\)\\b" 40 | (0 (rainbow-colorize-itself))) 41 | ("[Rr][Gg][Bb]:[0-9a-fA-F]\\{1,4\\}/[0-9a-fA-F]\\{1,4\\}/[0-9a-fA-F]\\{1,4\\}" 42 | (0 (rainbow-colorize-itself))) 43 | ("[Rr][Gg][Bb][Ii]:[0-9.]+/[0-9.]+/[0-9.]+" 44 | (0 (rainbow-colorize-itself))) 45 | ("\\(?:[Cc][Ii][Ee]\\(?:[Xx][Yy][Zz]\\|[Uu][Vv][Yy]\\|[Xx][Yy][Yy]\\|[Ll][Aa][Bb]\\|[Ll][Uu][Vv]\\)\\|[Tt][Ee][Kk][Hh][Vv][Cc]\\):[+-]?[0-9.]+\\(?:[Ee][+-]?[0-9]+\\)?/[+-]?[0-9.]+\\(?:[Ee][+-]?[0-9]+\\)?/[+-]?[0-9.]+\\(?:[Ee][+-]?[0-9]+\\)?" 46 | (0 (rainbow-colorize-itself))))) 47 | :hook 48 | (prog-mode . rainbow-mode) 49 | (css-mode . rainbow-mode) 50 | :diminish rainbow-mode) 51 | 52 | ;;------------------------------------------------------------------------------ 53 | ;; Highlight numbers, quoted things, escape sequences 54 | (use-package highlight-numbers 55 | :ensure t 56 | :hook (prog-mode . highlight-numbers-mode) 57 | :diminish highlight-numbers-mode) 58 | 59 | (use-package highlight-quoted 60 | :ensure t 61 | :hook (prog-mode . highlight-quoted-mode) 62 | :diminish highlight-quoted-mode) 63 | 64 | (use-package highlight-escape-sequences 65 | :ensure t 66 | :hook (prog-mode . hes-mode) 67 | :diminish hes-mode) 68 | 69 | ;;------------------------------------------------------------------------------ 70 | ;; doc hints 71 | (use-package eldoc 72 | :ensure t 73 | :hook 74 | (emacs-lisp-mode lisp-interaction-mode c++-mode c++-ts-mode) 75 | :diminish eldoc-mode) 76 | 77 | ;;------------------------------------------------------------------------------ 78 | ;; smart tabs 79 | (use-package smart-tab 80 | :ensure t 81 | :config 82 | (global-smart-tab-mode 1) 83 | (setq smart-tab-completion-functions-alist nil 84 | smart-tab-user-provided-completion-function #'completion-at-point) 85 | :diminish smart-tab-mode) 86 | 87 | ;;------------------------------------------------------------------------------ 88 | ;; which-key to give help on keys 89 | (use-package which-key 90 | :ensure t 91 | :config 92 | (which-key-mode) 93 | :diminish which-key-mode) 94 | 95 | ;;------------------------------------------------------------------------------ 96 | ;; which-key embark niceties 97 | ;; see https://github.com/oantolin/embark/wiki/Additional-Configuration#use-which-key-like-a-key-menu-prompt 98 | (defun embark-which-key-indicator () 99 | "An embark indicator that displays keymaps using which-key. 100 | The which-key help message will show the type and value of the 101 | current target followed by an ellipsis if there are further 102 | targets." 103 | (lambda (&optional keymap targets prefix) 104 | (if (null keymap) 105 | (which-key--hide-popup-ignore-command) 106 | (which-key--show-keymap 107 | (if (eq (plist-get (car targets) :type) 'embark-become) 108 | "Become" 109 | (format "Act on %s '%s'%s" 110 | (plist-get (car targets) :type) 111 | (embark--truncate-target (plist-get (car targets) :target)) 112 | (if (cdr targets) "…" ""))) 113 | (if prefix 114 | (pcase (lookup-key keymap prefix 'accept-default) 115 | ((and (pred keymapp) km) km) 116 | (_ (key-binding prefix 'accept-default))) 117 | keymap) 118 | nil nil t (lambda (binding) 119 | (not (string-suffix-p "-argument" (cdr binding)))))))) 120 | 121 | (setq embark-indicators 122 | '(embark-which-key-indicator 123 | embark-highlight-indicator 124 | embark-isearch-highlight-indicator)) 125 | 126 | (defun embark-hide-which-key-indicator (fn &rest args) 127 | "Hide the which-key indicator immediately when using the completing-read prompter." 128 | (which-key--hide-popup-ignore-command) 129 | (let ((embark-indicators 130 | (remq #'embark-which-key-indicator embark-indicators))) 131 | (apply fn args))) 132 | 133 | (advice-add #'embark-completing-read-prompter 134 | :around #'embark-hide-which-key-indicator) 135 | 136 | ;;------------------------------------------------------------------------------ 137 | ;; expand-region 138 | (use-package expand-region 139 | :ensure t 140 | :bind (("C-{" . er/expand-region) 141 | ("C-}" . er/contract-region)) 142 | :config 143 | (require 'the-org-mode-expansions) 144 | (setq expand-region-smart-cursor t 145 | expand-region-fast-keys-enabled nil)) 146 | (delete-selection-mode 1) 147 | 148 | ;;------------------------------------------------------------------------------ 149 | ;; region-state: show lines/characters selected 150 | (use-package region-state 151 | :ensure t 152 | :config 153 | (region-state-mode) 154 | :diminish region-state-mode) 155 | 156 | ;;------------------------------------------------------------------------------ 157 | ;; prettify symbols in modes that support it 158 | ;; unprettify the symbol at point 159 | (global-prettify-symbols-mode) 160 | (setq prettify-symbols-unprettify-at-point 'right-edge) 161 | 162 | ;;------------------------------------------------------------------------------ 163 | ;; Bookmarks 164 | (use-package bm 165 | :ensure t 166 | :bind (("C-" . bm-toggle) 167 | ("" . bm-next) 168 | ("S-" . bm-previous))) 169 | 170 | ;;------------------------------------------------------------------------------ 171 | ;; Mark the fill column 172 | (setq-default fill-column column-wrap-soft 173 | truncate-lines t) 174 | 175 | (setq-default whitespace-line-column column-wrap-soft 176 | whitespace-style '(face lines-tail)) 177 | 178 | (unless (> emacs-major-version 27) 179 | (add-hook 'prog-mode-hook 180 | (lambda () 181 | (let ((my-post-fill-column-fg 182 | (if (display-graphic-p) "gray20" "yellow"))) 183 | (unless (string-match-p (regexp-quote "*temp*") (buffer-name)) 184 | (whitespace-mode) 185 | (set-face-attribute 'whitespace-line nil 186 | :foreground my-post-fill-column-fg)))))) 187 | 188 | (when (> emacs-major-version 27) 189 | (add-hook 'prog-mode-hook #'display-fill-column-indicator-mode)) 190 | 191 | ;;------------------------------------------------------------------------------ 192 | ;; Projectile 193 | (use-package projectile 194 | :ensure t 195 | :config 196 | (setq projectile-enable-caching t 197 | projectile-use-git-grep t) 198 | (define-key projectile-mode-map (kbd "C-c p") 'projectile-command-map) 199 | :bind (("C-x f" . projectile-find-file) 200 | ("C-c #" . projectile-find-file-dwim) 201 | ("C-x C-h" . projectile-find-other-file)) 202 | :hook 203 | (prog-mode . projectile-mode) 204 | :diminish projectile-mode) 205 | 206 | ;;------------------------------------------------------------------------------ 207 | ;; RIPGrep 208 | (use-package ripgrep 209 | :ensure t 210 | :config 211 | (setq ripgrep-arguments '("--hidden")) 212 | :bind (("C-x g" . projectile-ripgrep))) 213 | 214 | ;;------------------------------------------------------------------------------ 215 | ;; Flycheck 216 | (use-package flycheck 217 | :ensure t 218 | :hook sh-mode) 219 | 220 | (use-package flycheck-pos-tip 221 | :ensure t 222 | :hook (flycheck-mode . flycheck-pos-tip-mode)) 223 | 224 | ;;------------------------------------------------------------------------------ 225 | ;; Emojify 226 | (use-package emojify 227 | :ensure t 228 | :init (global-emojify-mode) 229 | :bind (("C-c e" . emojify-insert-emoji)) 230 | :diminish emojify-mode) 231 | 232 | ;;------------------------------------------------------------------------------ 233 | ;; filladapt 234 | (use-package filladapt 235 | :ensure t 236 | :diminish filladapt-mode 237 | :config 238 | (setq-default filladapt-mode t)) 239 | 240 | ;;------------------------------------------------------------------------------ 241 | ;; grugru (rotate text at point) 242 | (use-package grugru 243 | :ensure t 244 | :config 245 | (grugru-highlight-mode) 246 | (grugru-define-global 'symbol (grugru-metagenerator-keep-case '("yes" "no"))) 247 | (grugru-define-global 'symbol (grugru-metagenerator-keep-case '("true" "false"))) 248 | (grugru-define-global 'symbol (grugru-metagenerator-keep-case '("width" "height"))) 249 | (grugru-define-global 'symbol (grugru-metagenerator-keep-case '("left" "right"))) 250 | (grugru-define-global 'symbol (grugru-metagenerator-keep-case '("top" "bottom"))) 251 | (grugru-define-global 'symbol (grugru-metagenerator-keep-case '("north" "south" "east" "west"))) 252 | (grugru-define-on-major-mode '(c++-mode) 'symbol '("public" "protected" "private")) 253 | (grugru-define-on-major-mode '(c++-mode) 'symbol '("class" "struct")) 254 | (grugru-define-on-major-mode '(c++-mode) 'symbol '("static_cast" "dynamic_cast" "reinterpret_cast" "const_cast" "bit_cast")) 255 | (when (fboundp 'c++-ts-mode) 256 | (grugru-define-on-major-mode '(c++-ts-mode) 'symbol '("public" "protected" "private")) 257 | (grugru-define-on-major-mode '(c++-ts-mode) 'symbol '("class" "struct")) 258 | (grugru-define-on-major-mode '(c++-ts-mode) 'symbol '("static_cast" "dynamic_cast" "reinterpret_cast" "const_cast" "bit_cast"))) 259 | :bind 260 | (("C-c /" . grugru-forward) 261 | ("C-c C-/" . grugru-backward)) 262 | :diminish grugru-highlight-mode) 263 | 264 | ;;------------------------------------------------------------------------------ 265 | ;; yasnippet 266 | (use-package yasnippet 267 | :ensure t 268 | :diminish yas-minor-mode 269 | :config 270 | (yas-reload-all) 271 | (add-hook 'prog-mode-hook #'yas-minor-mode)) 272 | 273 | ;;------------------------------------------------------------------------------ 274 | ;; format-all 275 | (use-package format-all 276 | :ensure t 277 | :init 278 | (defun my/format-all-buffer-setup () 279 | (format-all-mode) 280 | (let ((language (format-all--language-id-buffer))) 281 | (format-all--set-chain language 282 | (format-all--get-default-chain language)))) 283 | :hook ((c++-mode c++-ts-mode cmake-mode) . my/format-all-buffer-setup) 284 | :bind (("C-c f" . format-all-buffer))) 285 | 286 | ;;------------------------------------------------------------------------------ 287 | ;; jinx for spellchecking 288 | (when (locate-file "enchant-2" exec-path) 289 | (use-package jinx 290 | :ensure t 291 | :hook (emacs-startup . global-jinx-mode) 292 | :bind ([remap ispell-word] . jinx-correct) 293 | :config (setq jinx-languages "en_GB"))) 294 | 295 | ;;------------------------------------------------------------------------------ 296 | ;; writegood-mode to avoid lazy writing 297 | (use-package writegood-mode 298 | :ensure t 299 | :bind (("C-c C-r m" . writegood-mode) 300 | ("C-c C-r l" . writegood-grade-level) 301 | ("C-c C-r e" . writegood-reading-ease))) 302 | 303 | ;;------------------------------------------------------------------------------ 304 | ;; zone out 305 | (use-package zone-rainbow 306 | :ensure t 307 | :config 308 | (setq zone-programs '[zone-rainbow])) 309 | 310 | (when (locate-file "wn" exec-path) 311 | (require 'zone-words) 312 | (setq zone-programs (vconcat [zone-words] zone-programs)) 313 | (zone-when-idle 120)) 314 | -------------------------------------------------------------------------------- /.emacs.d/org-modes.el: -------------------------------------------------------------------------------- 1 | ;; -*- lexical-binding: t; -*- 2 | ;;------------------------------------------------------------------------------ 3 | ;; Org-mode 4 | (use-package org 5 | :ensure t 6 | :commands (org-mode) 7 | :mode ("\\.org$" . org-mode) 8 | :bind (:map org-mode-map 9 | ("C-M-t" . my-org-table-transpose-cells)) 10 | :config 11 | (setq org-log-done t 12 | org-support-shift-select t 13 | org-startup-indented t 14 | org-src-fontify-natively t 15 | org-export-allow-bind-keywords t 16 | org-latex-listings 'minted 17 | org-reveal-note-key-char nil 18 | org-directory (concat (getenv "HOME") "/.org_notes")) 19 | (unless (file-exists-p org-directory) 20 | (make-directory org-directory)) 21 | (setq org-default-notes-file (concat org-directory "/notes.org")) 22 | (setq org-latex-pdf-process 23 | '("pdflatex -shell-escape -interaction nonstopmode -output-directory %o %f" 24 | "pdflatex -shell-escape -interaction nonstopmode -output-directory %o %f" 25 | "pdflatex -shell-escape -interaction nonstopmode -output-directory %o %f")) 26 | (setq org-latex-minted-options 27 | '(("frame" "none") 28 | ("fontsize" "\\scriptsize") 29 | ("linenos" "") 30 | )) 31 | (setq org-beamer-outline-frame-title "Contents") 32 | (setq org-todo-keywords '((sequence "☛ TODO(t)" "|" "✔ DONE(d)") 33 | (sequence "⚑ WAITING(w)" "|") 34 | (sequence "|" "✘ CANCELLED(c)"))) 35 | (font-lock-add-keywords 'org-mode 36 | '(("^ +\\([-*]\\) " 37 | (0 (prog1 () (compose-region (match-beginning 1) 38 | (match-end 1) "•")))))) 39 | (org-babel-do-load-languages 40 | 'org-babel-load-languages 41 | '((C . t) 42 | (js . t) 43 | (haskell . t) 44 | (emacs-lisp . t))) 45 | (add-to-list 'org-structure-template-alist '("n" . "notes"))) 46 | 47 | ;; better header bullets 48 | (use-package org-bullets 49 | :ensure t 50 | :config 51 | (setq org-bullets-bullet-list 52 | '("◉" "✸" "✿" "◎" "►" "◇")) 53 | :hook (org-mode . org-bullets-mode)) 54 | 55 | (use-package org-re-reveal 56 | :ensure t 57 | :defer) 58 | 59 | (use-package htmlize 60 | :ensure t 61 | :defer) 62 | 63 | (use-package org-roam 64 | :ensure t 65 | :config 66 | (setq org-roam-directory org-directory 67 | org-roam-v2-ack t 68 | org-roam-completion-everywhere t) 69 | (org-roam-db-autosync-mode) 70 | (org-roam-setup) 71 | :bind (("C-c n f" . org-roam-node-find) 72 | ("C-c n r" . org-roam-node-random) 73 | (:map org-mode-map 74 | (("C-c n i" . org-roam-node-insert) 75 | ("C-c n o" . org-id-get-create) 76 | ("C-c n t" . org-roam-tag-add) 77 | ("C-c n a" . org-roam-alias-add) 78 | ("C-c n l" . org-roam-buffer-toggle))))) 79 | 80 | (defvar modi/htmlize-initial-flyspell-state nil 81 | "Variable to store the state of `flyspell-mode' when `htmlize-buffer' is called.") 82 | 83 | (defun modi/htmlize-before-hook-fn () 84 | (when (fboundp 'flyspell-mode) 85 | (setq modi/htmlize-initial-flyspell-state flyspell-mode) 86 | (when flyspell-mode 87 | (flyspell-mode -1)))) 88 | (add-hook 'htmlize-before-hook #'modi/htmlize-before-hook-fn) 89 | 90 | (defun modi/htmlize-after-hook-fn () 91 | (when (fboundp 'flyspell-mode) 92 | (when modi/htmlize-initial-flyspell-state 93 | (flyspell-mode 1)))) 94 | (add-hook 'htmlize-after-hook #'modi/htmlize-after-hook-fn) 95 | 96 | (bind-key "C-c l" 'org-store-link) 97 | (bind-key "C-c c" 'org-capture) 98 | (bind-key "C-c a" 'org-agenda) 99 | 100 | (add-hook 'org-mode-hook 101 | (lambda () 102 | (add-to-list 'org-latex-packages-alist '("" "minted" nil)) 103 | (bind-key "M-Q" 'toggle-truncate-lines org-mode-map) 104 | (require 'ox-latex) 105 | (require 'ox-beamer) 106 | (require 'org-re-reveal) 107 | (require 'htmlize) 108 | (add-to-list 'org-beamer-environments-extra 109 | '("onlyenv" "O" "\\begin{onlyenv}%a" "\\end{onlyenv}")))) 110 | 111 | (setq initial-major-mode 'org-mode) 112 | 113 | ;; swap org-mode cells 114 | (defun md-org-table-swap-cells (row col nextrow nextcol) 115 | (interactive) 116 | (let ((curfield (org-table-get row col)) 117 | (nextfield (org-table-get nextrow nextcol))) 118 | (org-table-analyze) 119 | (org-table-put row col nextfield) 120 | (org-table-put nextrow nextcol curfield) 121 | (org-table-align) 122 | (org-table-goto-field (format "@%s$%s" nextrow nextcol)) 123 | (message "md-org-table-swap-cells %s:%s <-> %s:%s" 124 | (format "@%s$%s" row col) curfield (format "@%s$%s" nextrow nextcol) nextfield))) 125 | 126 | (defun md-org-table-swap-cell-right () 127 | (interactive) 128 | (if (org-at-table-p) 129 | (let* ((col (org-table-current-column)) 130 | (row (org-table-current-dline)) 131 | (nextrow row) 132 | (nextcol (+ col 1))) 133 | (md-org-table-swap-cells row col nextrow nextcol) 134 | (md-update-todo-status)) 135 | (org-shiftright))) 136 | 137 | (defun md-org-table-swap-cell-left () 138 | (interactive) 139 | (if (org-at-table-p) 140 | (let* ((col (org-table-current-column)) 141 | (row (org-table-current-dline)) 142 | (nextrow row) 143 | (nextcol (- col 1))) 144 | (md-org-table-swap-cells row col nextrow nextcol) 145 | (md-update-todo-status)) 146 | (org-shiftleft))) 147 | 148 | (defun my-org-table-transpose-cells (prefix) 149 | "Transpose argument at point with the argument before it. 150 | With prefix arg ARG, transpose with the argument after it." 151 | (interactive "P") 152 | (cond ((not prefix) (md-org-table-swap-cell-left)) 153 | (t (md-org-table-swap-cell-right)))) 154 | 155 | ;; render tables with latex 156 | ;; https://old.reddit.com/r/emacs/comments/d3a8or/pretty_org_tables_in_the_buffer_chapter_2_it/ 157 | 158 | (defun my-render-org-table-at-point () 159 | (interactive) 160 | (save-excursion 161 | (beginning-of-line) 162 | (if (overlays-at (point)) 163 | ;; this is a rough solution, because there can 164 | ;; be other overlays at point 165 | (delete-overlay (car (overlays-at (point)))) 166 | 167 | (let* ((element-type (org-element-type (org-element-at-point)))) 168 | (if (and (not (eq element-type 'table)) 169 | (not (eq element-type 'table-row))) 170 | (error "not at an org table") 171 | 172 | (while (not (eq 'table (org-element-type (org-element-at-point)))) 173 | (forward-line -1)) 174 | 175 | (my-render-org-table (org-element-at-point))))))) 176 | 177 | 178 | (defun my-render-org-table (table) 179 | (interactive) 180 | (let* ((begin (org-element-property :begin table)) 181 | (end (let ((pos (org-element-property :end table))) 182 | (goto-char pos) 183 | (beginning-of-line) 184 | ;; skip possible space after table 185 | (while (not (looking-at " *[|#]")) 186 | (setq pos (point)) 187 | (forward-line -1)) 188 | pos)) 189 | (tabletxt (buffer-substring-no-properties begin end)) 190 | (img (with-temp-buffer 191 | (insert tabletxt) 192 | (mark-whole-buffer) 193 | (org-latex-convert-region-to-latex) 194 | (org-preview-latex-fragment) 195 | (goto-char (point-min)) 196 | (overlay-get (car (overlays-at (point))) 'display))) 197 | (overlay (make-overlay begin end))) 198 | (overlay-put overlay 'display img) 199 | (forward-line -1))) 200 | 201 | 202 | (defun my-render-org-tables-in-buffer () 203 | (save-excursion 204 | (org-element-map (org-element-parse-buffer) 'table 'my-render-org-table))) 205 | -------------------------------------------------------------------------------- /.emacs.d/prog.el: -------------------------------------------------------------------------------- 1 | ;; -*- lexical-binding: t; -*- 2 | ;;------------------------------------------------------------------------------ 3 | ;; tree-sitter grammar repositories 4 | (setq treesit-language-source-alist 5 | '((bash "https://github.com/tree-sitter/tree-sitter-bash") 6 | (c "https://github.com/tree-sitter/tree-sitter-c") 7 | (cmake "https://github.com/uyha/tree-sitter-cmake") 8 | (cpp "https://github.com/tree-sitter/tree-sitter-cpp") 9 | (css "https://github.com/tree-sitter/tree-sitter-css") 10 | (elisp "https://github.com/Wilfred/tree-sitter-elisp") 11 | (go "https://github.com/tree-sitter/tree-sitter-go") 12 | (html "https://github.com/tree-sitter/tree-sitter-html") 13 | (javascript "https://github.com/tree-sitter/tree-sitter-javascript" "master" "src") 14 | (json "https://github.com/tree-sitter/tree-sitter-json") 15 | (make "https://github.com/alemuller/tree-sitter-make") 16 | (markdown "https://github.com/ikatyang/tree-sitter-markdown") 17 | (python "https://github.com/tree-sitter/tree-sitter-python") 18 | (toml "https://github.com/tree-sitter/tree-sitter-toml") 19 | (tsx "https://github.com/tree-sitter/tree-sitter-typescript" "master" "tsx/src") 20 | (typescript "https://github.com/tree-sitter/tree-sitter-typescript" "master" "typescript/src") 21 | (yaml "https://github.com/ikatyang/tree-sitter-yaml"))) 22 | 23 | ;;------------------------------------------------------------------------------ 24 | ;; install languages for tree-sitter 25 | (setq my/treesit-languages '(c cpp python)) 26 | 27 | (when (fboundp 'treesit-language-available-p) 28 | (setq major-mode-remap-alist 29 | '((c-mode . c-ts-mode) 30 | (c++-mode . c++-ts-mode) 31 | (c-or-c++-mode . c-or-c++-ts-mode) 32 | (python-mode . python-ts-mode))) 33 | (mapc (lambda (lang) 34 | (unless (treesit-language-available-p lang) 35 | (treesit-install-language-grammar lang))) 36 | my/treesit-languages)) 37 | 38 | (defun use-treesit-for (lang) 39 | (and (fboundp 'treesit-language-available-p) 40 | (treesit-language-available-p lang) 41 | (member lang my/treesit-languages))) 42 | -------------------------------------------------------------------------------- /.emacs.d/python-modes.el: -------------------------------------------------------------------------------- 1 | ;; -*- lexical-binding: t; -*- 2 | ;;------------------------------------------------------------------------------ 3 | ;; toggle strings between f-strings and otherwise 4 | (defun toggle-string-to-fstring () 5 | "Toggle between string and fstring at point" 6 | (interactive) 7 | (when (nth 3 (syntax-ppss)) 8 | (save-excursion 9 | (goto-char (nth 8 (syntax-ppss))) 10 | (if (eq (char-before) ?f) 11 | (delete-char -1) 12 | (insert "f"))))) 13 | 14 | ;;------------------------------------------------------------------------------ 15 | ;; manage python imports 16 | (unless (> emacs-major-version 28) 17 | (use-package py-isort 18 | :ensure t 19 | :commands (py-isort-before-save))) 20 | 21 | ;;------------------------------------------------------------------------------ 22 | ;; elpy 23 | (unless (> emacs-major-version 28) 24 | (use-package elpy 25 | :ensure t 26 | :init 27 | (elpy-enable) 28 | :config 29 | (defun my/elpy-mode () 30 | (add-hook 'before-save-hook 'py-isort-before-save nil t) 31 | (add-hook 'before-save-hook 'elpy-black-fix-code nil t) 32 | (setq python-shell-interpreter "python3" 33 | python-indent-offset 4 34 | elpy-rpc-python-command "python3" 35 | elpy-rpc-timeout 10) 36 | (setq flycheck-python-flake8-executable "python3" 37 | flycheck-python-pycompile-executable "python3" 38 | flycheck-python-pylint-executable "python3") 39 | (flycheck-mode) 40 | (flycheck-select-checker 'python-flake8) 41 | (bind-keys :map flycheck-mode-map 42 | ("M-" . flycheck-next-error) 43 | ("M-" . flycheck-previous-error)) 44 | (elpy-mode)) 45 | :bind (:map elpy-mode-map 46 | ("M-k" . elpy-check) 47 | ("C-c f" . elpy-black-fix-code) 48 | ("C-c s" . toggle-string-to-fstring) 49 | ("M-" . nil) 50 | ("M-" . nil)) 51 | :hook ((python-mode . my/elpy-mode) 52 | (python-ts-mode . my/elpy-mode)))) 53 | 54 | ;;------------------------------------------------------------------------------ 55 | ;; eglot 56 | (when (> emacs-major-version 28) 57 | (use-package eglot 58 | :ensure t 59 | :config 60 | (defun my/eglot-organize-imports () (interactive) 61 | (eglot-code-actions nil nil "source.organizeImports" t)) 62 | (defun my/python-eglot-config () 63 | (eglot-ensure) 64 | (add-hook 'before-save-hook 'eglot-format-buffer nil t) 65 | (add-hook 'before-save-hook 'my/eglot-organize-imports nil t)) 66 | (fset #'jsonrpc--log-event #'ignore) 67 | :bind (:map eglot-mode-map 68 | ("C-c f" . eglot-format) 69 | ("C-c i" . my/eglot-organize-imports) 70 | ("C-c r" . eglot-rename) 71 | ("C-c s" . toggle-string-to-fstring) 72 | ("C-" . eglot-code-actions) 73 | ("M-" . flymake-goto-prev-error) 74 | ("M-" . flymake-goto-next-error)) 75 | :hook ((python-mode . my/python-eglot-config) 76 | (python-ts-mode . my/python-eglot-config)))) 77 | -------------------------------------------------------------------------------- /.emacs.d/site-lisp/boost-sml.el: -------------------------------------------------------------------------------- 1 | ;; -*- lexical-binding: t; -*- 2 | (defcustom boost-sml-table-start "make_transition_table" 3 | "Marker for the beginning of a Boost SML table." 4 | :group 'boost-sml 5 | :type 'string 6 | :safe 'stringp) 7 | 8 | (defcustom boost-sml-align-guards-beyond-events nil 9 | "When true, align guards at the end of all events." 10 | :group 'boost-sml 11 | :type 'boolean 12 | :safe 'booleanp) 13 | 14 | (defcustom boost-sml-align-guard-closes nil 15 | "When true, align the closing ] of guards. This only takes effect 16 | boost-sml-align-guards-beyond-events is true (so guard opens are also aligned)." 17 | :group 'boost-sml 18 | :type 'boolean 19 | :safe 'booleanp) 20 | 21 | (defcustom boost-sml-spaces-in-guards 0 22 | "Put N spaces inside [ and ] for guards." 23 | :group 'boost-sml 24 | :type 'integer 25 | :safe 'integerp) 26 | 27 | (defcustom boost-sml-align-actions-beyond-guards nil 28 | "When true, align actions at the end of all guards." 29 | :group 'boost-sml 30 | :type 'boolean 31 | :safe 'booleanp) 32 | 33 | (defcustom boost-sml-spaces-after-default-star 0 34 | "The number of spaces to put after the default state marker (*)." 35 | :group 'boost-sml 36 | :type 'integer 37 | :safe 'integerp) 38 | 39 | (defcustom boost-sml-insert-clang-format-guards t 40 | "When true, insert clang-format guard comments to prevent 41 | clang-format from meddling with the formatting." 42 | :group 'boost-sml 43 | :type 'boolean 44 | :safe 'booleanp) 45 | 46 | (defcustom boost-sml-align-eol-commas nil 47 | "When true, align the commas at the end of lines." 48 | :group 'boost-sml 49 | :type 'boolean 50 | :safe 'booleanp) 51 | 52 | (defcustom boost-sml-spaces-in-action-parens 0 53 | "Put N spaces inside ( and ) around actions." 54 | :group 'boost-sml 55 | :type 'integer 56 | :safe 'integerp) 57 | 58 | (defcustom boost-sml-align-parens-around-actions nil 59 | "When true, align the parens placed around actions." 60 | :group 'boost-sml 61 | :type 'boolean 62 | :safe 'booleanp) 63 | 64 | (defcustom boost-sml-consistent-parens-around-actions nil 65 | "When true, put parens around all actions if any have parens." 66 | :group 'boost-sml 67 | :type 'boolean 68 | :safe 'booleanp) 69 | 70 | (require 'cl-lib) 71 | (require 'rx) 72 | (require 'subr-x) 73 | 74 | (defun boost-sml/whitespace-p (c) 75 | (string-match-p "[[:space:]]" (char-to-string c))) 76 | 77 | (defun boost-sml/skip-whitespace () 78 | (while (boost-sml/whitespace-p (char-after)) (forward-char))) 79 | 80 | (defun boost-sml/skip-to (sep) 81 | (while (not (looking-at sep)) 82 | (cond ((eolp) (forward-char)) 83 | ((looking-at "[[:space:]]") (boost-sml/skip-whitespace)) 84 | (t (forward-sexp))))) 85 | 86 | (defun boost-sml/next-token () 87 | (let ((start (point))) 88 | (boost-sml/skip-whitespace) 89 | (cond ((eolp) t) 90 | ((looking-at (rx "/*")) (forward-comment 1)) 91 | ((looking-at "//") (end-of-line)) 92 | ((looking-at (rx (or "{" "<" "(" "[" "\"" "'"))) (forward-sexp)) 93 | (t (forward-same-syntax))) 94 | (string-trim (buffer-substring-no-properties start (point))))) 95 | 96 | (defun boost-sml/default-state-token () 97 | (let ((start (point))) 98 | (boost-sml/skip-whitespace) 99 | (when (looking-at (rx "*")) (forward-char)) 100 | (buffer-substring-no-properties start (point)))) 101 | 102 | (defun boost-sml/guard-token () 103 | (boost-sml/skip-whitespace) 104 | (if (looking-at (rx "[")) (boost-sml/next-token) "")) 105 | 106 | (defun boost-sml/action-separator-token () 107 | (boost-sml/skip-whitespace) 108 | (if (looking-at (rx "/")) (boost-sml/next-token) "")) 109 | 110 | (defun boost-sml/transition-separator-token () 111 | (boost-sml/skip-whitespace) 112 | (if (looking-at (rx "=")) (boost-sml/next-token) "")) 113 | 114 | (defun boost-sml/eol-token () 115 | (boost-sml/skip-whitespace) 116 | (if (looking-at (rx ",")) (boost-sml/next-token) "")) 117 | 118 | (defun boost-sml/collect-until-including (pred) 119 | (let ((start (point)) 120 | (next (boost-sml/next-token))) 121 | (while (not (funcall pred next)) 122 | (setq next (boost-sml/next-token))) 123 | (let ((s (buffer-substring-no-properties start (point)))) 124 | (list (string-trim-right (string-remove-suffix next (string-trim s))) 125 | next)))) 126 | 127 | (defun boost-sml/collect-until-excluding (pred) 128 | (let ((start (point)) 129 | (next (boost-sml/next-token))) 130 | (while (not (funcall pred next)) 131 | (setq next (boost-sml/next-token))) 132 | (backward-char (length next)) 133 | (let ((s (buffer-substring-no-properties start (point)))) 134 | (list (string-trim s))))) 135 | 136 | (defun boost-sml/is-event-separator (s) 137 | (string-equal s "+")) 138 | 139 | (defun boost-sml/is-guard (s) 140 | (and (> (length s) 0) (string-equal (substring s 0 1) "["))) 141 | 142 | (defun boost-sml/is-action-separator (s) 143 | (string-equal s "/")) 144 | 145 | (defun boost-sml/is-transition-separator (s) 146 | (string-equal s "=")) 147 | 148 | (defun boost-sml/is-eol (s) 149 | (or (= 0 (length s)) 150 | (string-equal s ",") 151 | (string-prefix-p ")" s))) 152 | 153 | ;; a line is tokenized into: 154 | ;; 0: default state star, if any 155 | ;; 1: state 156 | ;; 2: event separator, if any (+) 157 | ;; 3: event 158 | ;; 4: guard, if any 159 | ;; 5: action separator, if any (/) 160 | ;; 6: action, if any 161 | ;; 7: transition separator, if any (=) 162 | ;; 8: new state, if any 163 | ;; 9: line terminator, if any (,) 164 | 165 | (defun boost-sml/tokenize-line () 166 | (beginning-of-line) 167 | (setq l (list (boost-sml/default-state-token))) 168 | (nconc l (boost-sml/collect-until-including 169 | (lambda (tok) (or (boost-sml/is-event-separator tok) 170 | (boost-sml/is-eol tok))))) 171 | (nconc l (boost-sml/collect-until-excluding 172 | (lambda (tok) (or (boost-sml/is-guard tok) 173 | (boost-sml/is-action-separator tok) 174 | (boost-sml/is-transition-separator tok) 175 | (boost-sml/is-eol tok))))) 176 | (nconc l (list (boost-sml/guard-token))) 177 | (nconc l (list (boost-sml/action-separator-token))) 178 | (nconc l (boost-sml/collect-until-excluding 179 | (lambda (tok) (or (boost-sml/is-transition-separator tok) 180 | (boost-sml/is-eol tok))))) 181 | (nconc l (list (boost-sml/transition-separator-token))) 182 | (nconc l (boost-sml/collect-until-excluding 183 | (lambda (tok) (boost-sml/is-eol tok)))) 184 | (nconc l (list (boost-sml/eol-token))) 185 | l) 186 | 187 | (defun boost-sml/next-line-valid () 188 | (let ((end-of-table (rx (and (* space) ")")))) 189 | (when (not (looking-at end-of-table)) 190 | (forward-line)) 191 | (not (looking-at end-of-table)))) 192 | 193 | (defun boost-sml/transform-nth (lists n f) 194 | (mapc (lambda (l) (setf (nth n l) (funcall f (nth n l)))) lists)) 195 | 196 | (defun boost-sml/reduce-nth (lists n f :initial-value init) 197 | (let ((nths (mapcar (lambda (l) (nth n l)) lists))) 198 | (cl-reduce f nths :initial-value init))) 199 | 200 | (defun boost-sml/max-length-at-index (lists n) 201 | (boost-sml/reduce-nth lists n (lambda (acc s) (max acc (length s))) :initial-value 0)) 202 | 203 | (defun boost-sml/right-pad-string (s desired-len left-pad right-pad) 204 | (let ((unpadded (if (< (length s) desired-len) 205 | (concat s (make-string (- desired-len (length s)) ?\s)) 206 | s))) 207 | (concat (make-string left-pad ?\s) 208 | unpadded 209 | (make-string right-pad ?\s)))) 210 | 211 | (cl-defun boost-sml/left-align-at (lines n &optional (left-pad 0) (right-pad 0)) 212 | (let ((len (boost-sml/max-length-at-index lines n))) 213 | (when (> len 0) 214 | (boost-sml/transform-nth 215 | lines n 216 | (lambda (s) (boost-sml/right-pad-string s len left-pad right-pad)))))) 217 | 218 | (defun boost-sml/align-default-stars (lines) 219 | (boost-sml/left-align-at lines 0 0 boost-sml-spaces-after-default-star)) 220 | 221 | (defun boost-sml/align-states (lines) 222 | (boost-sml/left-align-at lines 1)) 223 | 224 | (defun boost-sml/align-event-separators (lines) 225 | (boost-sml/left-align-at lines 2 1 1)) 226 | 227 | (defun boost-sml/align-events (lines) 228 | (boost-sml/left-align-at lines 3)) 229 | 230 | (defun boost-sml/align-guards (lines) 231 | (boost-sml/left-align-at lines 4 1)) 232 | 233 | (defun boost-sml/event-and-guard (line) 234 | (concat (nth 3 line) (nth 4 line))) 235 | 236 | (defun boost-sml/align-events-with-guards (lines) 237 | (let* ((event-guards (mapcar #'boost-sml/event-and-guard lines)) 238 | (max-len (reduce #'max (mapcar #'length event-guards) :initial-value 0))) 239 | (mapc (lambda (l) 240 | (let ((pad-len (- max-len (length (boost-sml/event-and-guard l))))) 241 | (setf (nth 4 l) 242 | (boost-sml/right-pad-string (nth 4 l) (length (nth 4 l)) 1 pad-len)))) 243 | lines))) 244 | 245 | (defun boost-sml/align-action-separators (lines) 246 | (boost-sml/left-align-at lines 5 1 1)) 247 | 248 | (defun boost-sml/align-actions (lines) 249 | (boost-sml/left-align-at lines 6)) 250 | 251 | (defun boost-sml/align-transition-separators (lines) 252 | (boost-sml/left-align-at lines 7 1 1)) 253 | 254 | (defun boost-sml/align-new-states (lines) 255 | (boost-sml/left-align-at lines 8)) 256 | 257 | (defun boost-sml/assemble-line (line) 258 | (string-trim-right (apply #'concat line))) 259 | 260 | (defun boost-sml/fix-line-terminator (line) 261 | (let ((l (boost-sml/assemble-line line))) 262 | (when (and (> (length l) 0) 263 | (not (string-prefix-p "//" (string-trim l)))) 264 | (setf (nth 9 line) ",")))) 265 | 266 | (defun boost-sml/fix-line-terminators (lines) 267 | (mapc #'boost-sml/fix-line-terminator lines) 268 | (setf (nth 9 (car (last lines))) "")) 269 | 270 | (defun boost-sml/put-spaces-in-guard (line) 271 | (let ((guard (nth 4 line))) 272 | (setf (nth 4 line) 273 | (if (> (length guard) 0) 274 | (let ((bare-guard (string-trim (substring guard 1 (1- (length guard))))) 275 | (spaces (make-string boost-sml-spaces-in-guards ?\s))) 276 | (concat "[" spaces bare-guard spaces "]")) 277 | guard)))) 278 | 279 | (defun boost-sml/put-spaces-in-guards (lines) 280 | (mapc #'boost-sml/put-spaces-in-guard lines)) 281 | 282 | (defun boost-sml/expand-guard (len line) 283 | (let ((guard (nth 4 line))) 284 | (setf (nth 4 line) 285 | (if (> (length guard) 0) 286 | (let ((untermed-guard (substring guard 0 (1- (length guard)))) 287 | (spaces (make-string (- len (length guard)) ?\s))) 288 | (concat untermed-guard spaces "]")) 289 | guard)))) 290 | 291 | (defun boost-sml/align-guard-closes (lines) 292 | (let ((max-len (reduce #'max (mapcar (lambda (l) (length (nth 4 l))) lines) :initial-value 0))) 293 | (mapc (lambda (l) (boost-sml/expand-guard max-len l)) lines))) 294 | 295 | (defun boost-sml/put-parens-around-action (line) 296 | (let ((action (nth 6 line))) 297 | (setf (nth 6 line) 298 | (if (> (length action) 0) 299 | (let ((bare-action 300 | (if (string-prefix-p "(" action) 301 | (string-trim (substring action 1 (1- (length action)))) 302 | action)) 303 | (spaces (make-string boost-sml-spaces-in-action-parens ?\s))) 304 | (concat "(" spaces bare-action spaces ")")) 305 | action)))) 306 | 307 | (defun boost-sml/put-parens-around-actions (lines) 308 | (when (boost-sml/reduce-nth 309 | lines 6 310 | (lambda (acc action) (or acc (string-prefix-p "(" action))) 311 | :initial-value t) 312 | (mapc #'boost-sml/put-parens-around-action lines))) 313 | 314 | (defun boost-sml/expand-action (len line) 315 | (let ((action (nth 6 line))) 316 | (setf (nth 6 line) 317 | (if (string-prefix-p "(" action) 318 | (let ((untermed-action (substring action 0 (1- (length action)))) 319 | (spaces (make-string (- len (length action)) ?\s))) 320 | (concat untermed-action spaces ")")) 321 | action)))) 322 | 323 | (defun boost-sml/align-action-closes (lines) 324 | (let ((max-len (reduce #'max (mapcar (lambda (l) (length (nth 6 l))) lines) :initial-value 0))) 325 | (mapc (lambda (l) (boost-sml/expand-action max-len l)) lines))) 326 | 327 | (defun boost-sml/put-spaces-in-action (line) 328 | (let ((action (nth 6 line))) 329 | (setf (nth 6 line) 330 | (if (string-prefix-p "(" action) 331 | (let ((bare-action (string-trim (substring action 1 (1- (length action))))) 332 | (spaces (make-string boost-sml-spaces-in-action-parens ?\s))) 333 | (concat "(" spaces bare-action spaces ")")) 334 | action)))) 335 | 336 | (defun boost-sml/put-spaces-in-actions (lines) 337 | (mapc #'boost-sml/put-spaces-in-action lines)) 338 | 339 | (defun boost-sml/align-lines (lines) 340 | (boost-sml/put-spaces-in-guards lines) 341 | (when (and boost-sml-align-guard-closes boost-sml-align-guards-beyond-events) 342 | (boost-sml/align-guard-closes lines)) 343 | 344 | (boost-sml/put-spaces-in-actions lines) 345 | (when boost-sml-consistent-parens-around-actions 346 | (boost-sml/put-parens-around-actions lines)) 347 | (when boost-sml-align-parens-around-actions 348 | (boost-sml/align-action-closes lines)) 349 | 350 | (boost-sml/align-default-stars lines) 351 | (boost-sml/align-states lines) 352 | (boost-sml/align-event-separators lines) 353 | 354 | (if boost-sml-align-guards-beyond-events 355 | (progn (boost-sml/align-events lines) 356 | (boost-sml/align-guards lines)) 357 | (boost-sml/align-events-with-guards lines)) 358 | 359 | (boost-sml/align-action-separators lines) 360 | (boost-sml/align-actions lines) 361 | (boost-sml/align-transition-separators lines) 362 | (when boost-sml-align-eol-commas 363 | (boost-sml/align-new-states lines)) 364 | (boost-sml/fix-line-terminators lines)) 365 | 366 | (defun boost-sml/replace-next-buffer-line-with-line (line) 367 | (forward-line) 368 | (when (not (eolp)) (kill-line)) 369 | (insert (boost-sml/assemble-line line))) 370 | 371 | (defun boost-sml/rest-of-current-line () 372 | (if (bolp) 373 | "" 374 | (let ((start (point))) 375 | (end-of-line) 376 | (buffer-substring-no-properties start (point))))) 377 | 378 | (defun boost-sml/align-table-at (start) 379 | (goto-char start) 380 | (let ((lines (cl-loop while (boost-sml/next-line-valid) 381 | collecting (boost-sml/tokenize-line)))) 382 | (let ((trailing-part (boost-sml/rest-of-current-line))) 383 | (boost-sml/align-lines lines) 384 | (goto-char start) 385 | (mapc #'boost-sml/replace-next-buffer-line-with-line lines) 386 | (insert trailing-part)))) 387 | 388 | (defun boost-sml/insert-and-indent (s) 389 | (beginning-of-line) 390 | (insert (concat s "\n")) 391 | (forward-line -1) 392 | (let ((start (point))) 393 | (end-of-line) 394 | (indent-region start (point)))) 395 | 396 | (defun boost-sml/insert-clang-format-guards (start) 397 | (goto-char start) 398 | (forward-line -1) 399 | (when (not (looking-at "[[:space:]]*// clang-format off")) 400 | (forward-line) 401 | (boost-sml/insert-and-indent "// clang-format off")) 402 | (search-forward boost-sml-table-start nil t) 403 | (boost-sml/skip-to "(") 404 | (forward-sexp) 405 | (forward-line) 406 | (when (not (looking-at "[[:space:]]*// clang-format on")) 407 | (boost-sml/insert-and-indent "// clang-format on"))) 408 | 409 | ;;;###autoload 410 | (defun find-and-align-boost-sml () 411 | (interactive) 412 | (save-excursion 413 | (ignore-errors 414 | (goto-char (point-min)) 415 | (while (search-forward boost-sml-table-start nil t) 416 | (boost-sml/skip-to "(") 417 | (let ((start (point))) 418 | (forward-sexp) 419 | (indent-region start (point)) 420 | (boost-sml/align-table-at start) 421 | (when boost-sml-insert-clang-format-guards 422 | (boost-sml/insert-clang-format-guards start))))))) 423 | 424 | (provide 'boost-sml) 425 | -------------------------------------------------------------------------------- /.emacs.d/site-lisp/c-change-brackets.el: -------------------------------------------------------------------------------- 1 | ;; -*- lexical-binding: t; -*- 2 | (defun c-change-to-angle-include () 3 | (when (re-search-forward "\"" nil t) 4 | (replace-match ">") 5 | (re-search-backward "\"" nil t) 6 | (replace-match "<"))) 7 | 8 | (defun c-change-to-quote-include () 9 | (when (re-search-forward ">" nil t) 10 | (replace-match "\"") 11 | (re-search-backward "<" nil t) 12 | (replace-match "\""))) 13 | 14 | (defun my/ts-toggle-quotes (node) 15 | (let* ((b (treesit-node-start node)) 16 | (e (treesit-node-end node)) 17 | (s (buffer-substring b e)) 18 | (q (substring s 0 1)) 19 | (replace-left (if (string= q "<") "\"" "<")) 20 | (replace-right (if (string= q "<") "\"" ">"))) 21 | (when (or (string= q "<") (string= q "\"")) 22 | (goto-char b) 23 | (delete-forward-char 1) 24 | (insert replace-left) 25 | (goto-char e) 26 | (delete-backward-char 1) 27 | (insert replace-right)))) 28 | 29 | ;;;###autoload 30 | (defun c-toggle-include-quotes () 31 | "Change quotes around a string. 32 | 33 | Does the change only if the point is inside quotes that are 34 | syntactically correct under the current major mode." 35 | (interactive) 36 | (save-excursion 37 | (if (derived-mode-p 'c++-ts-mode) 38 | (let ((node (treesit-node-at (point)))) 39 | (if (string= (treesit-node-type node) "system_lib_string") 40 | (my/ts-toggle-quotes node) 41 | (my/ts-toggle-quotes (treesit-node-parent node)))) 42 | (cond ((nth 3 (syntax-ppss)) 43 | (c-change-to-angle-include)) 44 | ((nth 1 (syntax-ppss)) 45 | (goto-char (nth 1 (syntax-ppss))) 46 | (when (looking-at "<") 47 | (c-change-to-quote-include))))))) 48 | 49 | (provide 'c-toggle-include-quotes) 50 | -------------------------------------------------------------------------------- /.emacs.d/site-lisp/c-transpose-args.el: -------------------------------------------------------------------------------- 1 | ;; -*- lexical-binding: t; -*- 2 | ;;------------------------------------------------------------------------------ 3 | ;; Transpose function args 4 | (defun c-forward-to-argsep () 5 | "Move to the end of the current c function argument. 6 | Returns point." 7 | (interactive) 8 | (while 9 | (progn (comment-forward most-positive-fixnum) 10 | (looking-at "[^,)>}]")) 11 | (forward-sexp)) 12 | (point)) 13 | 14 | (defun c-backward-to-argsep () 15 | "Move to the beginning of the current c function argument. 16 | Returns point." 17 | (interactive) 18 | (let ((pt (point)) cur) 19 | (up-list -1) 20 | (forward-char) 21 | (while 22 | (progn 23 | (setq cur (point)) 24 | (> pt (c-forward-to-argsep))) 25 | (forward-char)) 26 | (goto-char cur))) 27 | 28 | (defun c-transpose-args-direction (is_forward) 29 | "Transpose two arguments of a c-function. 30 | The first arg is the one with point in it." 31 | (interactive) 32 | (let* 33 | (;; only different to pt when not 'is_forward' 34 | (pt-original (point)) 35 | (pt 36 | (progn 37 | (when (not is_forward) 38 | (goto-char (- (c-backward-to-argsep) 1)) 39 | (unless (looking-at ",") 40 | (goto-char pt-original) 41 | (user-error "Argument separator not found"))) 42 | (point))) 43 | (b (c-backward-to-argsep)) 44 | (sep 45 | (progn (goto-char pt) 46 | (c-forward-to-argsep))) 47 | (e 48 | (progn 49 | (unless (looking-at ",") 50 | (goto-char pt-original) 51 | (user-error "Argument separator not found")) 52 | (forward-char) 53 | (c-forward-to-argsep))) 54 | (ws-first 55 | (buffer-substring-no-properties 56 | (goto-char b) 57 | (progn (skip-chars-forward "[[:space:]\n]") 58 | (point)))) 59 | (first (buffer-substring-no-properties (point) sep)) 60 | (ws-second 61 | (buffer-substring-no-properties 62 | (goto-char (1+ sep)) 63 | (progn (skip-chars-forward "[[:space:]\n]") 64 | (point)))) 65 | (second (buffer-substring-no-properties (point) e))) 66 | (delete-region b e) 67 | (insert ws-first second "," ws-second first) 68 | 69 | ;; Correct the cursor location to be on the same character. 70 | (if is_forward 71 | (goto-char 72 | (+ 73 | ;; word start. 74 | (- (point) (length first)) 75 | ;; Apply initial offset within the word. 76 | (- pt b (length ws-first)))) 77 | (goto-char 78 | (+ 79 | b (length ws-first) 80 | ;; Apply initial offset within the word. 81 | (- pt-original (+ pt 1 (length ws-second)))))))) 82 | 83 | ;;;###autoload 84 | (defun c-transpose-args-forward () (interactive) (c-transpose-args-direction t)) 85 | ;;;###autoload 86 | (defun c-transpose-args-backward () (interactive) (c-transpose-args-direction nil)) 87 | 88 | (defun my/ts-transpose-nodes (n1 n2 arg) 89 | (when (and n1 n2) 90 | (let* ((b1 (treesit-node-start n1)) 91 | (e1 (treesit-node-end n1)) 92 | (b2 (treesit-node-start n2)) 93 | (e2 (treesit-node-end n2)) 94 | (s1 (buffer-substring b1 e1)) 95 | (s2 (buffer-substring b2 e2))) 96 | (delete-region b2 e2) 97 | (goto-char b2) 98 | (insert s1) 99 | (delete-region b1 e1) 100 | (goto-char b1) 101 | (insert s2) 102 | (goto-char (if arg (1- b1) e2))))) 103 | 104 | (defun my/cpp-ts-transpose-args (arg) 105 | (let* ((parent-is-arg-list 106 | (lambda (node) (let ((parent-type (treesit-node-type (treesit-node-parent node)))) 107 | (or (string-equal parent-type "argument_list") 108 | (string-equal parent-type "parameter_list") 109 | (string-equal parent-type "template_argument_list") 110 | (string-equal parent-type "template_parameter_list") 111 | (string-equal parent-type "initializer_list"))))) 112 | (leaf-node (treesit-node-at (point))) 113 | (arg-node (or (treesit-parent-until leaf-node parent-is-arg-list) 114 | leaf-node))) 115 | (cond ((string-equal (treesit-node-text arg-node) "(") nil) 116 | ((string-equal (treesit-node-text arg-node) ")") nil) 117 | ((string-equal (treesit-node-text arg-node) "{") nil) 118 | ((string-equal (treesit-node-text arg-node) "}") nil) 119 | ((string-equal (treesit-node-text arg-node) "<") nil) 120 | ((string-equal (treesit-node-text arg-node) ">") nil) 121 | ((string-equal (treesit-node-text arg-node) "[") nil) 122 | ((string-equal (treesit-node-text arg-node) "]") nil) 123 | ((string-equal (treesit-node-text arg-node) ",") 124 | (my/ts-transpose-nodes (treesit-node-prev-sibling arg-node) 125 | (treesit-node-next-sibling arg-node) 126 | arg)) 127 | (t (my/ts-transpose-nodes (treesit-node-prev-sibling (treesit-node-prev-sibling arg-node)) 128 | arg-node arg))))) 129 | 130 | ;;;###autoload 131 | (defun c-transpose-args (arg) 132 | "Transpose argument around point, leaving point after both. 133 | With prefix arg, leave point before both." 134 | (interactive "*P") 135 | (if (derived-mode-p 'c++-ts-mode) 136 | (my/cpp-ts-transpose-args arg) 137 | (cond ((not arg) (c-transpose-args-backward)) 138 | (t (c-transpose-args-forward))))) 139 | 140 | (provide 'c-transpose-args) 141 | -------------------------------------------------------------------------------- /.emacs.d/site-lisp/cyclekey.el: -------------------------------------------------------------------------------- 1 | ;;; cyclekey.el --- Quickly cycle through diacritics at point -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 2023 Shankar Rao 4 | 5 | ;; Author: Shankar Rao 6 | ;; URL: https://github.com/shankar2k/cyclekey 7 | ;; Version: 0.2 8 | ;; Package-Requires: ((emacs "26.1")) 9 | ;; Keywords: convenience, wp, i18n, diacritic, accent 10 | 11 | ;; This file is not part of GNU Emacs. 12 | 13 | ;;; Commentary: 14 | 15 | ;; This package provides the function cyclekey-cycle, which for the character 16 | ;; at point cycles through relevant diacritics and accents. 17 | ;; 18 | ;; See documentation on https://github.com/shankar2k/cyclekey/. 19 | 20 | ;;; License: 21 | 22 | ;; This program is free software; you can redistribute it and/or modify 23 | ;; it under the terms of the GNU General Public License as published by 24 | ;; the Free Software Foundation, either version 3 of the License, or 25 | ;; (at your option) any later version. 26 | 27 | ;; This program is distributed in the hope that it will be useful, 28 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 29 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 30 | ;; GNU General Public License for more details. 31 | 32 | ;; You should have received a copy of the GNU General Public License 33 | ;; along with this program. If not, see . 34 | 35 | ;;; History: 36 | 37 | ;; Version 0.2 (2023-12-08): 38 | 39 | ;; - Added punctuation support 40 | 41 | ;; Version 0.1 (2023-12-02): 42 | 43 | ;; - Initial version 44 | 45 | ;;; Code: 46 | 47 | ;;;; Requirements 48 | 49 | ;;;; Customization 50 | 51 | (defcustom cyclekey-languages 52 | nil 53 | "List of languages that Cyclekey uses to generate character cycles." 54 | :type '(repeat string)) 55 | 56 | (defcustom cyclekey-marks-alist 57 | (purecopy 58 | '(("Afrikaans" "aáä" "eéèêë" "iíîï" "oóôö" "uúûü" "yý" "AÁÄ" "EÉÈÊË" 59 | "IÍÎÏ" "OÓÔÖ" "UÚÛÜ" "YÝ") 60 | ("Albanian" "cç" "eë" "CÇ" "EË" "\"„“") 61 | ("Bosnian" "cčć" "dđ" "sš" "zž" "CČĆ" "DĐ" "SŠ" "ZŽ" "\"„”") 62 | ("Catalan" "aà" "cç" "eéè" "iíï" "oóò" "uúü" "AÀ" "CÇ" "EÉÈ" "IÍÏ" 63 | "OÓÒ" "UÚÜ" "\"«»") 64 | ("Croatian" "cčć" "dđ" "sš" "zž" "CČĆ" "DĐ" "SŠ" "ZŽ" "\"„“" "'‚‘") 65 | ("Czech" "aá" "cč" "dď" "eéĕ" "ií" "nň" "oó" "rř" "sš" "tť" "uúů" "yý" 66 | "zž" "AÁ" "CČ" "DĎ" "EÉĚ" "IÍ" "NŇ" "OÓ" "RŘ" "SŠ" "TŤ" "UÚŮ" "YÝ" 67 | "ZŽ" "\"„”" "'‚‘") 68 | ("Danish" "aæå" "oø" "AÆÅ" "OØ" "\"»«" "'›‹") 69 | ("Dutch" "aáä" "eéë" "iíï" "oóö" "uúü" "AÁÄ" "EÉË" "IÍÏ" "OÓÖ" "UÚÜ") 70 | ("Estonian" "aä" "oõö" "sš" "uü" "zž" "AÄ" "OÕÖ" "SŠ" "UÜ" "ZŽ" "\"„”") 71 | ("French" "aàáâæ" "cç" "eéèêë" "iîï" "oôœ" "uùûü" "yÿ" "AÀÁÆ" "CÇ" 72 | "EÉÈÊË" "IÎÏ" "OÔŒ" "UÙÛÜ" "YŸ" "\"«»") 73 | ("German" "aä" "oö" "sß" "uü" "AÄ" "OÖ" "Sß" "UÜ" "\"„“»«" "'›‹‚‘") 74 | ("Hungarian" "aá" "eé" "ií" "oóöő" "uúüű" "AÁ" "EÉ" "IÍ" "OÓÖŐ" "UÚÜŰ" 75 | "\"„»«") 76 | ("Icelandic" "aáæ" "dđ" "eé" "ií" "oóö" "uú" "tþ" "yý" "AÄ" "DÐ" "EÉ" 77 | "IÍ" "OÓÖ" "UÚ" "TÞ" "YÝ" "\"„“«»" "'‚‘") 78 | ("Irish" "aá" "eé" "iıí" "oó" "uú" "AÁ" "EÉ" "IÍ" "OÓ" "UÚ" "&⁊") 79 | ("Italian" "aà" "eéè" "iì" "oóò" "uù" "AÀ" "EÉÈ" "IÌ" "OÓÒ" "UÙ" "\"«»") 80 | ("Hawaiian" "aā" "eē" "iī" "oō" "uū" "AĀ" "EĒ" "IĪ" "OŌ" "UŪ") 81 | ("Latvian" "aā" "cč" "eē" "gģ" "iī" "kķ" "lļ" "nņ" "sš" "uū" "zž" "AĀ" 82 | "CČ" "EĒ" "GĢ" "IĪ" "KĶ" "LĻ" "NŅ" "SŠ" "UŪ" "ZŽ" "\"„“") 83 | ("Lithuanian" "aą" "cč" "eęė" "iį" "sš" "uųū" "zž" "AĄ" "CČ" "EĘĖ" "IĮ" 84 | "SŠ" "UŲŪ" "ZŽ" "\"„”" "'‚‘") 85 | ("Luxembourgish" "aä" "eëé" "AÄ" "EËÉ" "\"„“«»") 86 | ("Maltese" "cċ" "gġ" "hħ" "zż" "CĊ" "GĠ" "HĦ" "ZŻ") 87 | ("Montenegrin" "cčć" "dđ" "sšś" "zžź" "CČĆ" "DĐ" "SŠŚ" "ZŽŹ") 88 | ("Norwegian" "aæå" "eéèê" "oóòôø" "uù" "AÆÅ" "EÉÈÊ" "OÓÒÔØ" "UÙ" "\"« »") 89 | ("Polish" "aą" "cć" "eę" "lł" "nń" "oó" "sś" "zźż" "AĄ" "CĆ" "EĘ" "LŁ" 90 | "NŃ" "OÓ" "SŚ" "ZŹŻ" "\"„”«»" "'‚’") 91 | ("Portuguese" "aáâãà" "cç" "eéê" "ií" "oóôõ" "uú" "AÁÂÃÀ" "CÇ" "EÉÊ" 92 | "IÍ" "OÓÔÕ" "UÚ" "\"«»") 93 | ("Romanian" "aăâ" "iî" "sș" "tț" "AĂÂ" "IÎ" "SȘ" "TȚ" "\"„”«»") 94 | ("Scottish" "aà" "eè" "iì" "oò" "uù" "AÀ" "EÈ" "IÌ" "OÒ" "UÙ") 95 | ("Serbian" "cčć" "dđ" "sš" "zž" "CČĆ" "DĐ" "SŠ" "ZŽ" "\"„") 96 | ("Slovak" "aáä" "cč" "dď" "eé" "ií" "lĺľ" "nň" "oóô" "rŕ" "sš" "tť" 97 | "uú" "yý" "zž" "AÁÄ" "CČ" "DĎ" "EÉ" "IÍ" "LĹĽ" "NŇ" "OÓÔ" "RŔ" "SŠ" 98 | "TŤ" "UÚ" "YÝ" "ZŽ" "\"„”" "'‚‘") 99 | ("Slovenian" "cč" "sš" "zž" "CČ" "SŠ" "ZŽ" "\"„”" "'‚‘") 100 | ("Spanish" "aá" "eé" "ií" "nñ" "oó" "uúü" "yý" "AÁ" "EÉ" "IÍ" "NÑ" "OÓ" 101 | "UÚÜ" "YÝ" "?¿" "!¡" "\"«»") 102 | ("Swedish" "aåä" "oö" "AÅÄ" "OÖ" "\"”") 103 | ("Turkish" "cç" "gğ" "iı" "oö" "sş" "uü" "CÇ" "GĢ" "Iİ" "OÖ" "SŞ" "UÜ") 104 | ("Turkmen" "aä" "cç" "nň" "oö" "sş" "uü" "yý" "zž" "AÄ" "CÇ" "NŇ" "OÖ" 105 | "SŞ" "UÜ" "YÝ" "ZŽ") 106 | ("Welsh" "aâàáä" "eêèéë" "iîìíï" "oôòóö" "uûùúü" "wŵẁẃẅ" "yŷỳýÿ" "AÂÀÁÄ" 107 | "EÊÈÉË" "IÎÌÍÏ" "OÔÒÓÖ" "UÛÙÚÜ" "WŴẀẂẄ" "YŶỲÝŸ"))) 108 | "Alist mapping languages to lists of character cycles. 109 | 110 | Each key is a string corresponding to a known language that uses 111 | a latin alphabet. 112 | 113 | Each value is a list of character cycles, where each character 114 | cycle is a string with the first character an English letter or 115 | punctuation mark, and all subsequent characters are variants 116 | marked with various diacritics." 117 | :type '(alist :key-type string :value-type (repeat string))) 118 | 119 | (defcustom cyclekey-save-languages t 120 | "When true, save ``cyclekey-languages'' for future sessions 121 | whenever it is modified by either ``cyclekey-add-language'' or 122 | ``cyclekey-remove-language''." 123 | :type 'boolean) 124 | 125 | ;;;; Variables 126 | 127 | (defvar cyclekey-forward-map (make-hash-table) 128 | "Hash table mapping each character to the next diacritic in the cycle.") 129 | 130 | (defvar cyclekey-backward-map (make-hash-table) 131 | "Hash table mapping each character to the previous diacritic in the cycle.") 132 | 133 | (defvar cyclekey-full-map (make-hash-table) 134 | "Hash table mapping each base character to the cycle of all its marks.") 135 | 136 | ;;;; Functions 137 | 138 | (defun cyclekey-init () 139 | "Initialize the forward and backwards Cyclekey maps. 140 | 141 | This should be called whenever ``cyclekey-languages'' or 142 | ``cyclekey-marks-alist'' are modified." 143 | (clrhash cyclekey-forward-map) 144 | (clrhash cyclekey-backward-map) 145 | (clrhash cyclekey-full-map) 146 | (dolist (lang cyclekey-languages) 147 | (dolist (cycle (alist-get lang cyclekey-marks-alist nil nil #'equal)) 148 | (let ((first (aref cycle 0))) 149 | (seq-doseq (next (substring cycle 1)) 150 | (unless (gethash next cyclekey-forward-map) 151 | (let ((last (gethash first cyclekey-backward-map first))) 152 | (puthash next first cyclekey-forward-map) 153 | (puthash last next cyclekey-forward-map) 154 | (puthash next last cyclekey-backward-map) 155 | (puthash first next cyclekey-backward-map))))))) 156 | (maphash #'cyclekey--make-full-cycle cyclekey-forward-map)) 157 | 158 | (defun cyclekey--make-full-cycle (key val) 159 | "Add cycle of marks for KEY to ``cyclekey-full-map''. 160 | 161 | VAL, the character KEY maps forward to, is included to enable use 162 | with ``maphash''." 163 | (let ((next val) 164 | (cycle nil)) 165 | (while (/= next key) 166 | (setq cycle (concat cycle (list next)) 167 | next (gethash next cyclekey-forward-map))) 168 | (puthash key cycle cyclekey-full-map))) 169 | 170 | (defun cyclekey--is-english-punctuation (ch) 171 | "Return t if CH is an English punctuation character." 172 | (and (<= ?! ch ?~) ; is printable ASCII <= 127 173 | (not (<= ?0 ch ?9)) ; is not digit 174 | (not (<= ?A ch ?Z)) ; is not capital letter 175 | (not (<= ?a ch ?z)))) ; is not lowercase letter 176 | 177 | ;;;; Commands 178 | 179 | ;;;###autoload 180 | (defun cyclekey-cycle (backward?) 181 | "Cycle the character at point to the next diacritic in the Cyclekey map. 182 | 183 | This command does nothing if the character at point is not 184 | present in the Cyclekey map. If ``backward?'' is non-nil, then 185 | cycle the character at point to the previous diacritic in the 186 | Cyclekey map." 187 | (interactive "P") 188 | (when-let ((ch (gethash (preceding-char) 189 | (if backward? 190 | cyclekey-backward-map 191 | cyclekey-forward-map)))) 192 | (delete-char -1) 193 | (insert ch))) 194 | 195 | ;;;###autoload 196 | (defun cyclekey-help () 197 | "Show how ``cyclekey-cycle'' cycles through marks for various characters." 198 | (interactive) 199 | (with-output-to-temp-buffer "*Cyclekey Help*" 200 | (princ "Cyclekey Help\n=============\n\n") 201 | (princ "Cyclekey Languages\n------------------\n") 202 | (princ (string-join cyclekey-languages ", ")) 203 | (princ "\n\nCyclekey cycling\n----------------\n") 204 | (maphash (lambda (ch cycle) 205 | (when (and cycle (or (<= ?a ch ?z) 206 | (cyclekey--is-english-punctuation ch))) 207 | (princ 208 | (format " %c ---> %-19s%s\n" ch cycle 209 | (if-let* ((upch (and (<= ?a ch ?z) (upcase ch))) 210 | (upcyc (gethash upch cyclekey-full-map))) 211 | (format "%c --> %s" upch upcyc) 212 | ""))))) 213 | cyclekey-full-map)) 214 | (with-current-buffer "*Cyclekey Help*" 215 | (setq truncate-lines t))) 216 | 217 | ;;;###autoload 218 | (defun cyclekey-add-language (lang) 219 | "Add marks from LANG to the character cycles in ``cyclekey-cycle''." 220 | (interactive (list (completing-read "Language to add to Cyclekey: " 221 | cyclekey-marks-alist))) 222 | (unless (member lang cyclekey-languages) 223 | (setq cyclekey-languages (append cyclekey-languages (list lang))) 224 | (when cyclekey-save-languages 225 | (customize-save-variable 'cyclekey-languages cyclekey-languages)) 226 | (cyclekey-init) 227 | (when (called-interactively-p 'any) 228 | (message "Marks from %s added to Cyclekey." lang)))) 229 | 230 | ;;;###autoload 231 | (defun cyclekey-remove-language (lang) 232 | "Remove marks from LANG from the character cycles in ``cyclekey-cycle''." 233 | (interactive (list (completing-read "Language to remove from Cyclekey: " 234 | cyclekey-languages))) 235 | (setq cyclekey-languages (remove lang cyclekey-languages)) 236 | (when cyclekey-save-languages 237 | (customize-save-variable 'cyclekey-languages cyclekey-languages)) 238 | (cyclekey-init) 239 | (when (called-interactively-p 'any) 240 | (message "Marks from %s removed from Cyclekey." lang))) 241 | 242 | 243 | ;;;; Footer 244 | 245 | (provide 'cyclekey) 246 | 247 | ;;; cyclekey.el ends here 248 | -------------------------------------------------------------------------------- /.emacs.d/site-lisp/lorikeet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elbeno/dotemacs/0d56390aaa544aa38df31471eebf57eb4a5d15d5/.emacs.d/site-lisp/lorikeet.png -------------------------------------------------------------------------------- /.emacs.d/site-lisp/tspew.el: -------------------------------------------------------------------------------- 1 | ;;; tspew.el --- Clean and format "template spew" errors from gcc and Clang -*- lexical-binding: t; -*- 2 | 3 | ;; Author: Jeff Trull 4 | 5 | ;; This program is free software: you can redistribute it and/or modify 6 | ;; it under the terms of the GNU General Public License as published by 7 | ;; the Free Software Foundation, either version 3 of the License, or 8 | ;; (at your option) any later version. 9 | 10 | ;; This program is distributed in the hope that it will be useful, 11 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | ;; GNU General Public License for more details. 14 | 15 | ;; You should have received a copy of the GNU General Public License 16 | ;; along with this program. If not, see . 17 | 18 | (require 'compile) 19 | (require 'cc-mode) 20 | (require 'cl-lib) 21 | 22 | (defgroup tspew nil 23 | "Display C++ compilation results more cleanly. 24 | Suggested usage: (add-hook 'compilation-mode-hook 'tspew-mode) 25 | " 26 | :group 'compilation :group 'programming) 27 | 28 | ;; the beginnings of tspew-mode 29 | ;; first, a syntax table for error messages 30 | (defvar tspew-syntax-table (make-syntax-table) 31 | "Syntax table for lexing compiler errors" ) 32 | ;; modify to suit our needs 33 | ;; left and right angle brackets are a kind of parentheses in type names 34 | (modify-syntax-entry ?< "(>" tspew-syntax-table) 35 | (modify-syntax-entry ?> ")<" tspew-syntax-table) 36 | 37 | ;; colon is a "symbol constituent" - usable in identifiers 38 | (modify-syntax-entry ?: "_" tspew-syntax-table) 39 | 40 | ;; now we can use (with-symbol-table tspew-syntax-table (movement-fn)) 41 | 42 | ;; we need a grammar for several reasons: 43 | ;; 1) to resolve the fact that angle brackets may appear in operator overloads 44 | ;; 2) for iterating over template parameters I want "XXX" to be a single item 45 | ;; e.g. > should be an sexp of length 2, not 3, 46 | ;; with elements "int" and "std::allocator" 47 | ;; - although maybe this is OK? We have to stop before emitting "<" anyway 48 | 49 | (defvar tspew-indent-level c-basic-offset 50 | "Indentation amount for types in error messages") 51 | 52 | (defvar tspew-default-fill-width 100 53 | "Default maximum width of error type display, in columns 54 | If the compilation window is visible, its width will be used instead") 55 | 56 | ;; captured initial column width 57 | (defvar tspew--fill-width nil 58 | "Max width in columns for current run") 59 | 60 | (defvar tspew-quoted-region-regexp 61 | ;; in gcc, types and function signatures are enclosed by Unicode left and right single quotes 62 | ;; in clang it's ASCII single quotes 63 | (let ((quote-start-regexp "\\(\u2018\\|'\\)") 64 | (quote-end-regexp "\\(\u2019\\|'\\)") 65 | ;; some surprising things can be in type names 66 | (char-lit-regexp "\\('[^']'\\)") 67 | (allowed-char-regexp "[][[:alnum:]:()<>,&_ =+/*%^.;{}-]")) 68 | (concat quote-start-regexp "\\(" char-lit-regexp "\\|" allowed-char-regexp "\\)+" quote-end-regexp)) 69 | "Regexp for identifying type and function names (typically quoted) 70 | within an error message") 71 | 72 | ;; remember where we are in the buffer 73 | ;; the compilation filter may give us partial lines, so we have to keep track of how far 74 | ;; we've come 75 | (defvar-local tspew--parse-start nil 76 | "Starting point for incremental error parsing." ) 77 | 78 | ;; define key shortcuts for expand/contract of formatted expressions 79 | (defvar-keymap tspew-mode-map 80 | :doc "Keymap for the tspew compilation minor mode" 81 | "C-c +" #'tspew-increase-detail 82 | "C-c -" #'tspew-decrease-detail 83 | ) 84 | 85 | (add-to-list 'minor-mode-map-alist `(tspew-mode . ,tspew-mode-map)) 86 | 87 | (defun tspew--remove-overlays () 88 | (let ((overlays (seq-filter (lambda (ov) (overlay-get ov 'is-tspew)) 89 | (overlays-in (point-min) (point-max))))) 90 | (dolist (ov overlays) 91 | (delete-overlay ov))) 92 | ) 93 | 94 | (defun tspew--parse-initialize (_proc) 95 | "Reset compilation output processing" 96 | 97 | (let ((win (get-buffer-window))) 98 | (setq-local tspew--fill-width 99 | (if win (window-body-width win) tspew-default-fill-width))) 100 | 101 | (tspew--remove-overlays) 102 | (setq tspew--parse-start nil) 103 | (set (make-local-variable 'parse-sexp-lookup-properties) t) ;; so we can special-case character syntax 104 | ) 105 | 106 | ;; create a compilation filter hook to incrementally parse errors 107 | (defun tspew--compilation-filter () 108 | "Transform error messages into something prettier." 109 | ;; Parse from tspew--parse-start to point, or as close as you can get, 110 | ;; updating tspew--parse-start past the last newline we got. 111 | ;; Be sure to use "markers" when necessary, as positions are strictly 112 | ;; buffer offsets and are not "stable" in the iterator sense 113 | (if (not tspew--parse-start) 114 | (setq-local tspew--parse-start compilation-filter-start)) 115 | 116 | ;; ensure things like "operator()" are considered a single symbol, 117 | ;; not a symbol followed by parens. The same is true of anonymous classes 118 | ;; and lambdas, both of which have printed representations containing parens. 119 | (tspew--mark-special-case-symbols tspew--parse-start (point)) 120 | 121 | (while (and (< tspew--parse-start (point)) 122 | (> (count-lines tspew--parse-start (point)) 1)) 123 | ;; we have at least one newline in our working region 124 | (save-excursion 125 | (goto-char tspew--parse-start) 126 | (forward-line) 127 | (let ((line-end-marker (point-marker))) 128 | ;; process a single line 129 | (tspew--handle-line tspew--parse-start line-end-marker) 130 | (setq-local tspew--parse-start (marker-position line-end-marker)))))) 131 | 132 | ;; TSpew is a minor mode for compilation buffers, not source code 133 | ;; To use it you need to enable it after a compilation buffer is created, 134 | ;; and they are not created until compilation begins. So you must tell 135 | ;; compilation-mode to do it for you using compilation-mode-hook. 136 | ;; For example: 137 | ;; (add-hook 'compilation-mode-hook 'tspew-mode) 138 | ;; will enable tspew for all compiles. You may prefer to restrict it to 139 | ;; certain projects instead by writing your own hook. 140 | 141 | (define-minor-mode tspew-mode 142 | "Toggle tspew (Template Spew) mode" 143 | :init-value nil 144 | :lighter "TSpew" 145 | (if tspew-mode 146 | (progn 147 | (add-hook 'compilation-start-hook 'tspew--parse-initialize nil t) 148 | (add-hook 'compilation-filter-hook 'tspew--compilation-filter nil t)) 149 | ;; if we are being toggled off, remove hooks 150 | (remove-hook 'compilation-start-hook 'tspew--parse-initialize) 151 | (remove-hook 'compilation-filter-hook 'tspew--compilation-filter) 152 | ;; overlays too 153 | (tspew--remove-overlays) 154 | (kill-local-variable 'tspew--parse-start))) 155 | 156 | 157 | ;; NEW (as of 8/4/2023) plan: 158 | ;; Don't bother with start points 159 | ;; We have functions for each production in the grammar that return an endpoint (found!) or nil. 160 | ;; We make functions like "optional" and "alternative" that wrap them. 161 | ;; Each parser only handles internal whitespace. We AND them implicitly. 162 | ;; These low-level parsers will not interact with the fill/indent mechanism, so no printer parameter 163 | ;; We will gather higher-level objects ("chunks") and then submit them to that facility 164 | 165 | ;; point updated only on successful parse 166 | 167 | ;; A lightweight parser formalism 168 | ;; A Parser returns t and updates point if successful and returns nil otherwise 169 | 170 | ;; 171 | ;; parser combinators 172 | ;; 173 | 174 | ;; some combinators for use in defining higher-level structures 175 | ;; these accept parsers and make new parsers from them 176 | 177 | (defun tspew--parser-optional (p) 178 | "Create a parser that optionally parses its argument (i.e. ignores any failure)" 179 | (lambda () (or (funcall p) t))) 180 | 181 | (defmacro tspew--parser-alternative (&rest parsers) 182 | "Create a parser that attempts to parse one of several input parsers, 183 | passing if any do" 184 | `(lambda () 185 | (let ((start (point))) 186 | ;; this whole thing is a macro because of this - "or" is not a function, so we cannot "apply" it. 187 | ;; instead we build the expression through a macro 188 | (if (or ,@(mapcar (lambda (p) (list 'funcall p)) parsers)) 189 | t 190 | (goto-char start) 191 | nil)))) 192 | 193 | (defmacro tspew--parser-sequential (&rest parsers) 194 | "Create a parser that attempts to parse a series of input parsers in sequence, 195 | passing if all do" 196 | `(lambda () 197 | (let ((start (point))) 198 | (if (and ,@(mapcar (lambda (p) (list 'funcall p)) parsers)) 199 | t 200 | (goto-char start) 201 | nil)))) 202 | 203 | (defun tspew--parser-multiple (p) 204 | "Create a parser that parses one or more of the input parser, greedily. 205 | To parse zero or more, combine with tspew--parser-optional" 206 | (lambda () 207 | (and (funcall p) ;; at least one 208 | (progn 209 | (while (funcall p)) ;; run until one fails 210 | t)))) ;; and return true 211 | 212 | (defun tspew--parser-alternating (p1 p2) 213 | "Create a parser that parses p1 and p2 alternately, 214 | passing if at least one parser matches: 215 | p1 [p2 [p1 [p2 [p1 ...]]]]" 216 | (lambda () 217 | (and (funcall p1) 218 | (progn 219 | (while (and (funcall p2) (funcall p1))) 220 | t)))) 221 | 222 | ;; syntactic sugar for combinators - a grammar constructor 223 | ;; implementation first 224 | (defun tspew--parser-grammar-expand (grammar) 225 | "Recursive function implementation of tspew--parser-grammar. 226 | You can call this to see the exact form produced by the grammar, pre-expansion" 227 | (if (not grammar) 228 | '(lambda () t) ;; pass and consume no input 229 | (cond 230 | ((stringp grammar) 231 | `(tspew--parser-keyword ,grammar)) ;; interpret as keyword 232 | ((not (listp grammar)) 233 | grammar) ;; unknown, just pass through (future extension?) 234 | ((or (equal (car grammar) 'function) ;; the name of a function (parser, most likely) 235 | (and (symbolp (car grammar)) ;; a parser generator 236 | (string-prefix-p "tspew--parser-" (symbol-name (car grammar))))) 237 | grammar) ;; existing parser - just pass it through 238 | (t (cl-case (car grammar) 239 | (- (cl-assert (equal (length grammar) 2)) 240 | `(tspew--parser-optional ,(tspew--parser-grammar-expand (cadr grammar)))) 241 | (| `(tspew--parser-alternative 242 | ,@(mapcar (lambda (p) (tspew--parser-grammar-expand p)) (cdr grammar)))) 243 | (+ (cl-assert (equal (length grammar) 2)) 244 | `(tspew--parser-multiple ,(tspew--parser-grammar-expand (cadr grammar)))) 245 | (* (cl-assert (equal (length grammar) 2)) 246 | `(tspew--parser-optional 247 | (tspew--parser-multiple ,(tspew--parser-grammar-expand (cadr grammar))))) 248 | (<> (cl-assert (equal (length grammar) 3)) 249 | `(tspew--parser-alternating ,(tspew--parser-grammar-expand (cadr grammar)) 250 | ,(tspew--parser-grammar-expand (caddr grammar)))) 251 | (t ;; it's a list, so it's the default - a sequential parser 252 | `(tspew--parser-sequential ,@(mapcar (lambda (p) (tspew--parser-grammar-expand p)) grammar))))) 253 | ))) 254 | 255 | ;; then the user interface 256 | (defmacro tspew--parser-grammar (grammar) 257 | "Create a parser from combinators using shorthand. 258 | An sexp that starts with: 259 | - becomes optional 260 | | becomes alternative 261 | + becomes multiple (at least one) 262 | * becomes optional multiple (zero or more) 263 | <> becomes alternating (at least the first, then second first second...) 264 | Without one of those initial symbols, the inputs are considered to be 265 | a sequential parser." 266 | (tspew--parser-grammar-expand grammar)) 267 | 268 | ;; low-level (leaf) parsers 269 | 270 | (defun tspew--parse-symbol () 271 | "Parse a symbol (a string of characters with word or \"symbol constituent\" syntax)" 272 | (let ((start (point))) 273 | ;; skip forward past word and symbol constituents 274 | ;; forward-symbol skips initial whitespace also, which I don't want 275 | (or (> (skip-syntax-forward "w_") 0) 276 | (progn 277 | (goto-char start) 278 | nil)))) 279 | 280 | (defun tspew--parse-cv () 281 | "Parse the const or volatile keywords" 282 | (and (or (looking-at-p "const\\s ") (looking-at "volatile\\s ")) 283 | (progn 284 | (skip-syntax-forward "w") 285 | (skip-syntax-forward " ") 286 | t))) 287 | 288 | ;; mandatory whitespace 289 | (defun tspew--parse-whitespace () 290 | "Parse one or more whitespace characters" 291 | (let ((start (point))) 292 | (or (> (skip-syntax-forward " ") 0) 293 | (progn 294 | (goto-char start) 295 | nil)))) 296 | 297 | (defun tspew--parse-template-preamble () 298 | "Parse the initial template 299 | in function template specializations" 300 | (and (looking-at-p "template<") 301 | (progn 302 | (forward-word 1) 303 | (forward-sexp 1) 304 | (skip-syntax-forward " ") 305 | t))) 306 | 307 | (defun tspew--parse-with-clause () 308 | "Parse a type elaboration for function template instantiations 309 | of the form \"[with X = Y; Q = R; ...]\"" 310 | (and (looking-at-p "\\[with ") 311 | (progn 312 | (forward-sexp) 313 | t))) 314 | 315 | (defun tspew--parse-ref-modifier () 316 | "Parse a pointer, ref, or rvalue ref" 317 | (and (looking-at-p "\\*\\|&\\|&&") 318 | (progn 319 | (skip-syntax-forward "_") 320 | t))) 321 | 322 | (defun tspew--parse-sequence () 323 | "Parse a curly braced sequence (i.e. of types or integers)" 324 | (and (equal (char-after) ?{) 325 | (progn 326 | (forward-sexp) 327 | t))) 328 | 329 | ;; 330 | ;; parser generators (take a param, return a parser) 331 | ;; 332 | 333 | ;; here we will use "parser" in the name to indicate that result is a parser, 334 | ;; so you have to funcall to use it. You can also use the result in a parser 335 | ;; combinator (see below) 336 | 337 | ;; parenthesized expression using the given start character 338 | (defun tspew--parser-paren-expr (parenc) 339 | "Parse a balanced parenthesis expression starting with 340 | the given opening character" 341 | (lambda () 342 | (and (equal (char-after) parenc) 343 | (progn 344 | (forward-sexp) ;; this could theoretically fail but again, this is compiler output... 345 | t)))) 346 | 347 | ;; a specific string 348 | (defun tspew--parser-keyword (kwd) 349 | "Create a parser for a pre-selected keyword. 350 | It requires - and consumes - trailing whitespace" 351 | (lambda () 352 | (and (looking-at-p (concat kwd "\\s ")) ;; trailing whitespace required 353 | (progn 354 | (forward-char (length kwd)) 355 | (skip-syntax-forward " ") 356 | t)))) 357 | 358 | (defun tspew--parser-memfn-qual () 359 | "Parse a member function qualifier, consuming trailing whitespace" 360 | (tspew--parser-alternative 361 | (tspew--parser-keyword "const") 362 | (tspew--parser-keyword "volatile") 363 | (tspew--parser-keyword "&&") 364 | (tspew--parser-keyword "&"))) 365 | 366 | ;; 367 | ;; parser utilities 368 | ;; 369 | 370 | ;; composed, higher-level parsers 371 | 372 | (defun tspew--parse-func-name () 373 | "Parse a function name (the bit between the return type 374 | and the open paren of the arguments)" 375 | ;; for the moment, assume we can use a "type" which is similar, possibly identical 376 | (tspew--parse-type)) 377 | 378 | (defun tspew--parse-param-list () 379 | "Parse a comma-separated function parameter list as seen 380 | in compiler error messages" 381 | (forward-sexp) 382 | ) 383 | 384 | (defun tspew--parser-builtin-int-type () 385 | "Parse a builtin C++ integral type (int/char with modifiers), 386 | with trailing whitespace" 387 | (tspew--parser-grammar 388 | (| ( (- "unsigned") "char") 389 | ( (* (| "long" "short" "unsigned")) "int")))) 390 | 391 | (defun tspew--parse-type () 392 | "Parse a type as found in compiler error messages" 393 | ;; either a parenthesized decltype expression, OR 394 | ;; cv qualifier, followed by symbol, followed optionally 395 | ;; by a parenthesized expression (angle brackets), followed 396 | ;; optionally by a symbol (member types, pointer/ref indicators, etc.) 397 | ;; type := decltype '(' expr ')' | [ cv ] symbol [ sexp [ symbol ] ] ] [ & | && | * ] 398 | ;; e.g. const std::vector::iterator 399 | ;; ^- cv ^-symbol ^- sexp ^-symbol 400 | (funcall (tspew--parser-grammar 401 | (| 402 | ;; decltype expression 403 | ( (- "constexpr") "decltype" (tspew--parser-paren-expr ?\()) 404 | 405 | ;; normal types 406 | ( 407 | ;; first, we can have const/constexpr/volatile 408 | (* (| "constexpr" #'tspew--parse-cv)) 409 | ;; then one of two things: 410 | (| (tspew--parser-builtin-int-type) ;; a builtin int of some kind 411 | ( (- (| "typename" "auto" "struct")) ;; a user-defined type (or float or double, which look like types) 412 | #'tspew--parse-symbol 413 | (- ( (tspew--parser-paren-expr ?<) 414 | (- #'tspew--parse-symbol))))) 415 | ;; either of the above can have reference modifiers 416 | (- ( (- #'tspew--parse-whitespace) #'tspew--parse-ref-modifier))))))) 417 | 418 | (defun tspew--parse-function () 419 | "Parse a function signature, as found in compiler error messages" 420 | ;; func := [ constexpr ] [ static ] type func-name param-list [memfn-qual] [ with-clause ] 421 | (funcall (tspew--parser-grammar 422 | ( (- #'tspew--parse-template-preamble) 423 | ;; BOZO actually not sure which of these keywords will appear first 424 | (- "constexpr") 425 | (- "static") 426 | ;; return type is optional because it could be a constructor 427 | (- ( #'tspew--parse-type #'tspew--parse-whitespace)) 428 | #'tspew--parse-func-name 429 | (| 430 | ;; gcc, and clang sometimes 431 | ( (tspew--parser-paren-expr ?\() 432 | ;; we can have child classes with function call operators here, 433 | ;; which themselves can have child classes, and so on 434 | ;; gcc seems to format them like types but clang puts the arg lists in parens 435 | (- (<> #'tspew--parse-type (tspew--parser-paren-expr ?\())) 436 | (- ( #'tspew--parse-whitespace (- (tspew--parser-memfn-qual)) (- #'tspew--parse-with-clause)))) 437 | 438 | ;; clang's special function template specialization format (no "with" clause, no param list) 439 | ;; ::fname vs 440 | ;; ::fname(T, U...) [with T = X, U = Y...] in gcc 441 | (tspew--parser-paren-expr ?<)))))) 442 | 443 | ;; here I try to implement a two-part pretty-printing system (that is, 444 | ;; both indentation and "fill") as described in a paper by Rose and Welsh 445 | ;; (1981), which is paywalled, but there is a nice description of it and 446 | ;; related work in "the PretzelBook", see 447 | ;; http://www.literateprogramming.com/pretzelbook.pdf 448 | ;; There were apparently many similar approaches in the late 70s 449 | 450 | ;; The scanner is implemented in the literature (such as it is) as a parser 451 | ;; that supplies different things to the printer depending on the place 452 | ;; you are in the AST. Having only an ad-hoc scanner, I have ad-hoc code for this. 453 | 454 | ;; the "scanner" (front end) part of the system 455 | (defun tspew--scan (printer) 456 | "Scan tokens, supplying length information to the back end" 457 | (with-syntax-table tspew-syntax-table 458 | ;; tokenize 459 | (let* ((start (point)) 460 | (syntax (char-syntax (char-after))) 461 | (tok-range 462 | ;; "consume" (by moving point) and return next token 463 | (cl-case syntax 464 | (?. 465 | ;; punctuation is passed straight through along with trailing whitespace 466 | (skip-syntax-forward ".") 467 | ;; skip trailing whitespace 468 | (skip-syntax-forward " ") 469 | (cons start (point))) 470 | (?\( 471 | ;; supply just the open "paren" (of whatever type) 472 | (forward-char) 473 | (cons start (point))) 474 | (?\) 475 | (forward-char) 476 | ;; closing "paren" may be followed by whitespace 477 | ;; consume it *if* followed immediately by another closing paren 478 | (when (and (not (eobp)) (equal (char-syntax (char-after)) ?\s)) 479 | (skip-syntax-forward " ") 480 | (when (not (equal (char-syntax (char-after)) ?\))) 481 | ;; NOT another close paren. supply whitespace as next token. 482 | (skip-syntax-backward " "))) 483 | (cons start (+ start 1))) 484 | (?\s 485 | ;; whitespace not following punctuation or closing paren 486 | ;; preserve for readability 487 | (skip-syntax-forward " ") 488 | (cons start (point))) 489 | (t 490 | ;; grab the next sexp 491 | (forward-sexp) 492 | (cons start (point))))) 493 | (tok (buffer-substring (car tok-range) (cdr tok-range)))) 494 | 495 | ;; send token to indent/fill engine 496 | (funcall printer tok-range) 497 | 498 | ;; optionally send some control information 499 | ;; we send three kinds: 500 | ;; "internal break" - a spot to put a newline in between sequential elements, if needed 501 | ;; "enter hierarchy" - a parenthesized expression begins of specified length 502 | ;; "exit hierarchy" - a parenthesized expression ends 503 | 504 | (cond 505 | ((string-match-p "^[,;]\s" tok) 506 | (funcall printer 'intbrk)) ;; optional newline between elements 507 | 508 | ((equal (char-syntax (string-to-char tok)) ?\() 509 | ;; we just entered a parenthesized expression 510 | (funcall printer 511 | (cons 512 | 'enter ;; beginning of new hierarchy level 513 | (tspew--visible-distance (point) 514 | (save-excursion 515 | (backward-char) ;; start at open "paren" 516 | (forward-sexp) ;; skip over balanced parens 517 | (point)))))) 518 | 519 | ((equal (char-syntax (string-to-char tok)) ?\)) 520 | (funcall printer 'exit)))))) ;; exit hierarchy level 521 | 522 | ;; the "printer" (back end) 523 | 524 | (defun tspew--printer (initial-indent) 525 | "Return a closure to \"print\" tokens while maintaining appropriate indentation" 526 | (let 527 | ((indentation-stack `((no-break . ,initial-indent))) ;; current indent level info 528 | ;; Each element is a dotted pair of: 529 | ;; 1) the current indentation level in columns 530 | ;; 2) whether we are splitting the elements of this level one per line 531 | (space-remaining (- tspew--fill-width initial-indent)) ;; tracking horizontal space 532 | (prev-tok-end nil) ;; tracking "insertion point" 533 | (format-instructions '())) ;; accumulated changes 534 | 535 | (lambda (cmd) 536 | 537 | ;; the printer maintains the current indentation level and decides when it's 538 | ;; necessary to start putting out sequence elements on separate lines. 539 | ;; It maintains a stack of ('brksym . indent) pairs giving for each level 540 | ;; what the amount of indentation is and whether we are currently breaking 541 | ;; up the sequence with newlines 542 | (cl-typecase cmd 543 | (cons 544 | (if (equal (car cmd) 'enter) 545 | ;; an "enter" - push mode for this level 546 | (let ((len (cdr cmd)) 547 | (indentation (cdar indentation-stack))) 548 | (if (or (< len space-remaining) 549 | (equal len 1)) ;; trivial (empty) parens 550 | ;; there is room enough to print the rest of this sexp 551 | ;; don't require line breaks 552 | (push (cons 'no-break indentation) indentation-stack) 553 | ;; we must switch to a new line to maximize available space 554 | (setq indentation (+ indentation tspew-indent-level)) 555 | ;; new space remaining: whatever is left after indentation 556 | (setq space-remaining (- tspew--fill-width indentation)) 557 | ;; require elements at this level to break/indent 558 | (push (cons 'break indentation) indentation-stack) 559 | ;; output line break and indent 560 | (push (cons prev-tok-end indentation) format-instructions))) 561 | 562 | ;; not an "enter" command, but a plain token 563 | ;; represented as a range in the buffer 564 | (cl-assert (and (integerp (car cmd)) (integerp (cdr cmd)))) 565 | (setq prev-tok-end (cdr cmd)) 566 | (setq space-remaining (- space-remaining 567 | (tspew--visible-distance (car cmd) (cdr cmd)))))) 568 | 569 | (symbol 570 | (cl-case cmd 571 | 572 | (result format-instructions) ;; return result 573 | 574 | (exit 575 | (pop indentation-stack)) ;; restore previous indentation 576 | 577 | (intbrk 578 | (when (equal (caar indentation-stack) 'break) 579 | ;; we have a sequence element and previously decided to split one per line 580 | ;; break and indent to current level (for a new sequence element) 581 | (setq space-remaining (- tspew--fill-width (cdar indentation-stack))) 582 | (push (cons prev-tok-end (cdar indentation-stack)) format-instructions))))))))) 583 | 584 | (defun tspew--format-region (start end &optional initial-indent) 585 | "Fill and indent region containing text. 586 | This is the primary engine for the formatting algorithm" 587 | 588 | (let ((printer (tspew--printer (or initial-indent 0)))) 589 | 590 | ;; send one token at a time, inserting indentation and line breaks as required 591 | (save-excursion 592 | (goto-char start) 593 | (while (not (equal (point) end)) 594 | (cl-assert (<= (point) end)) 595 | (tspew--scan printer))) 596 | 597 | (funcall printer 'result))) 598 | 599 | (defun tspew--format-with-clause (start end) 600 | "Fill and indent region containing a with clause" 601 | 602 | ;; the semicolon-separated list inside the with clause looks OK when formatted using the type code 603 | (save-excursion 604 | (let* ((start (+ start 6)) ;; "[with " 605 | (end (- end 1)) ;; directly before "]" 606 | (result (list (cons start 0))) ;; a single newline after "[with " 607 | (parse-rhs 608 | (tspew--parser-alternative #'tspew--parse-sequence #'tspew--parse-type)) 609 | (parse-with-stmt-tparam 610 | (lambda () 611 | (when (looking-at "\\.\\.\\.") (forward-char 3)) ;; skip ellipses, which looks like punctuation 612 | (tspew--parse-type))) 613 | (tparam 614 | (progn 615 | (goto-char start) 616 | (funcall (tspew--parser-builtin-int-type)) ;; this might be an integral NTTP 617 | (funcall parse-with-stmt-tparam) ;; skip name 618 | (buffer-substring start (point))))) 619 | 620 | ;; do first X = Y pair 621 | (forward-char 3) ;; skip " = " 622 | (setq result 623 | (append result 624 | (tspew--format-region 625 | (point) 626 | (progn (funcall parse-rhs) (point)) 627 | (+ (length tparam) 3)))) 628 | (while (not (equal (point) end)) 629 | (cl-assert (equal (char-after) ?\;)) 630 | (forward-char) 631 | (push (cons (+ (point) 1) 0) result) ;; a newline after every "; " 632 | 633 | (skip-syntax-forward " ") 634 | (setq tparam 635 | (buffer-substring (point) 636 | (progn 637 | (funcall (tspew--parser-builtin-int-type)) 638 | (funcall parse-with-stmt-tparam) 639 | (point)))) 640 | 641 | (forward-char 3) ;; " = " 642 | (setq result 643 | (append result 644 | (tspew--format-region 645 | (point) 646 | (progn (funcall parse-rhs) (point)) 647 | (+ (length tparam) 3))))) 648 | (forward-char) 649 | result))) ;; skip trailing right bracket 650 | 651 | (defun tspew--format-function-region (start end) 652 | "Fill and indent region containing a function" 653 | 654 | ;; Detect (via existing parsers) the different chunks of a function 655 | ;; then dispatch formatters (such as format-region) as appropriate 656 | (save-excursion 657 | (goto-char start) 658 | (append 659 | 660 | ;; template if present 661 | (if (looking-at-p "template<") 662 | (let ((result (tspew--format-template-preamble (point) (progn (tspew--parse-template-preamble) (point))))) 663 | (skip-syntax-forward " ") 664 | result) 665 | '()) 666 | 667 | ;; constexpr and/or static (both optional) 668 | (if (looking-at-p "constexpr ") 669 | (progn 670 | (forward-word) 671 | (skip-syntax-forward " ") 672 | '()) 673 | '()) 674 | 675 | (if (looking-at-p "static ") 676 | (progn 677 | (forward-word) 678 | (skip-syntax-forward " ") 679 | '()) 680 | '()) 681 | 682 | ;; return type 683 | ;; might be absent if function is a constructor 684 | ;; you can distinguish it from the function name because it is followed by whitespace, not '(' 685 | ;; this may also happen when the function is an "auto" template 686 | (let ((tstart (point)) 687 | (tend (progn (tspew--parse-type) (point)))) 688 | (if (equal (char-after) ?\() ;; must be constructor name 689 | (progn 690 | (goto-char tstart) 691 | '()) 692 | (skip-syntax-forward " ") ;; consume whitespace 693 | (append 694 | (tspew--format-region tstart tend) 695 | (list (cons (point) 0))))) ;; newline between return type and function name 696 | 697 | ;; even the function name can require formatting 698 | (append 699 | (tspew--format-region (point) (progn (tspew--parse-func-name) (point))) 700 | (list (cons (point) 0))) 701 | 702 | ;; at this point we could end with a clang-style function template specialization 703 | (if-let ((fun-spl-end (save-excursion (and (funcall (tspew--parser-paren-expr ?<)) (point))))) 704 | (tspew--format-region (point) fun-spl-end) 705 | 706 | ;; otherwise it's the gcc possibilities: parameters, member function qualifiers, with clause 707 | (append 708 | (tspew--format-region (point) (progn (tspew--parse-param-list) (point))) 709 | 710 | ;; the param list may be followed by (no whitespace) "::" and a type, also requiring formatting 711 | ;; and then optionally another param list, and optionally another type, and... 712 | (if (looking-at "::") 713 | (let ((result '())) 714 | (while (looking-at "::") 715 | (setq result 716 | (append result 717 | (list (cons (point) 0)) 718 | (tspew--format-region (point) (progn (tspew--parse-type) (point))))) 719 | (if (equal (char-after) ?\() 720 | (setq result 721 | (append result 722 | (list (cons (point) 0)) 723 | (tspew--format-region (point) (progn (funcall (tspew--parser-paren-expr ?\()) (point))))))) 724 | result) 725 | '()) 726 | 727 | ;; skip trailing space and memfn qual, if present 728 | (if (< (point) end) 729 | (progn 730 | (skip-syntax-forward " ") 731 | (funcall (tspew--parser-memfn-qual)) 732 | '()) 733 | '()) 734 | 735 | (if (< (point) end) 736 | (let ((wc-start (progn (skip-syntax-forward " ") (point)))) 737 | (if (tspew--parse-with-clause) 738 | (append 739 | ;; newline before with clause, if present 740 | (list (cons wc-start 0)) 741 | (tspew--format-with-clause wc-start (point))) 742 | '())) 743 | '())))))) 744 | 745 | ;; newlines in between these: 746 | ;; 1) output static if present 747 | ;; 2) format return type 748 | ;; 3) if func-name has template args, format individually 749 | ;; otherwise, format it as a unit with the param list 750 | ;; 4) if with-clause present, format it 751 | 752 | (defun tspew--format-quoted-expr (tstart tend) 753 | "Split up and indent a quoted region within an error message as necessary 754 | to meet line width requirements" 755 | ;; At the moment we handle types or function names (as in "required from" lines) 756 | ;; We check to see which one we have. Types are simple. For functions, we break them up into chunks 757 | ;; separated by whitespace, like "return type" "function parameter list" or "with clause" 758 | ;; and format those separately using the indent/fill algorithm 759 | (with-restriction tstart tend 760 | (with-syntax-table tspew-syntax-table 761 | (save-excursion 762 | (let ((result (list (cons tstart 0)))) ;; initial newline 763 | (goto-char tstart) 764 | (cond 765 | ((tspew--parse-function) 766 | (when (not (equal (point) tend)) 767 | (message "found a function: |%s| but it does not fill the quoted expression |%s|" 768 | (buffer-substring tstart (point)) 769 | (buffer-substring tstart tend))) 770 | (append result (tspew--format-function-region tstart (point)))) 771 | 772 | ((tspew--parse-type) 773 | (when (not (equal (point) tend)) 774 | (message "found a type: |%s| but it does not fill the quoted expression |%s|" 775 | (buffer-substring tstart (point)) 776 | (buffer-substring tstart tend))) 777 | (append result (tspew--format-region tstart (point)))) 778 | 779 | (t 780 | (message (format "Found a quoted expression I don't understand: |%s|" 781 | (buffer-substring tstart tend))) 782 | nil))))))) 783 | 784 | (defun tspew--format-template-preamble (tstart tend) 785 | "Format a function template preamble e.g. template" 786 | 787 | (save-excursion 788 | (goto-char tstart) 789 | (forward-word) ;; skip "template" 790 | (cl-assert (equal (char-after) ?<)) 791 | (append 792 | (tspew--format-region (point) tend) 793 | (list (cons tend 0))))) ;; terminal newline 794 | 795 | (defun tspew--mark-special-case-symbols (start end) 796 | "Mark various tricky elements so they are considered \"symbol constituents\" 797 | This includes operator overloads, lambdas, and anonymous classes" 798 | (let ((opr-regexp "operator\\(<<\\|<\\|>>\\|>\\|()\\|\\[]\\)") 799 | (anon-class-regexp "(anonymous class)\\|{anonymous}") 800 | (lambda-clang-regexp "(lambda at [[:alnum:]_/.-]+:[0-9]+:[0-9]+)") 801 | (lambda-gcc-regexp "")) 802 | (save-excursion 803 | (goto-char start) 804 | (while (re-search-forward 805 | (concat "\\(" opr-regexp "\\)\\|\\(" anon-class-regexp "\\)\\|\\(" lambda-clang-regexp "\\)\\|\\(" lambda-gcc-regexp "\\)") 806 | end t) 807 | (with-silent-modifications 808 | (put-text-property (match-beginning 0) (match-end 0) 'syntax-table (string-to-syntax "_"))))))) 809 | 810 | ;; contents can be functions, function specializations, maybe other things? 811 | (defun tspew--handle-quoted-expr (tstart tend) 812 | "Fill and indent a single quoted expression (type or function) 813 | within an error message" 814 | ;; create an overlay covering the expression 815 | (let ((instructions (tspew--format-quoted-expr tstart tend))) 816 | 817 | (when (equal 0 (length instructions)) 818 | (message "no instructions produced for region |%s|" (buffer-substring tstart tend))) 819 | (dolist (instr instructions) 820 | (let* ((istart (car instr)) 821 | (indentation (cdr instr)) 822 | (ov (make-overlay istart istart))) 823 | ;; display indented and filled types in place of the original 824 | (overlay-put ov 'before-string (concat "\n" (make-string indentation ?\s))) 825 | ;; remember overlay 826 | ;; [JET] I initially kept a list of overlays and used that, but compilation-mode 827 | ;; calls kill-all-local-variables, which deletes the buffer-local value 828 | ;; of my list. So instead, we use properties: 829 | (overlay-put ov 'is-tspew t))))) 830 | 831 | (defun tspew--handle-line (lstart lend) 832 | "Process a single line of error output" 833 | ;; lstart is a position, lend is a marker 834 | ;; is this an error message with a type? 835 | (let* ((err-regexp (cadr (assoc 'gnu compilation-error-regexp-alist-alist)))) 836 | (save-excursion 837 | (goto-char lstart) 838 | (if (and (looking-at-p err-regexp) ;; error line 839 | ;; ignore static asserts (too complex! plus, what would we do with them?) 840 | (not (save-excursion (re-search-forward "static_assert\\|static assertion" lend t))) 841 | ;; the line is too long 842 | (>= (- (line-end-position) (line-beginning-position)) tspew--fill-width)) 843 | ;; while there is still a match remaining in the line: 844 | (while (re-search-forward tspew-quoted-region-regexp lend t) 845 | (let ((tstart (+ (match-beginning 0) 1)) 846 | (tend (- (match-end 0) 1))) 847 | ;; process this region 848 | (tspew--handle-quoted-expr tstart tend) 849 | ;; mark region with depths within parentheses (or angle brackets) 850 | (with-syntax-table tspew-syntax-table 851 | (tspew--mark-depths tstart tend)) 852 | ;; advance past matched text 853 | (goto-char (+ tend 1)))))))) 854 | 855 | ;; 856 | ;; depth-based folding support 857 | ;; 858 | 859 | (defun tspew--mark-depths (start end) 860 | "Mark regions of text inside parentheses/angle brackets 861 | with their depths, as an overlay property" 862 | (save-excursion 863 | (goto-char start) 864 | (let ((pos-stack nil) 865 | (max-depth 0)) 866 | (while (not (equal (point) end)) 867 | (cl-case (char-syntax (char-after)) 868 | (?\( 869 | (push (+ (point) 1) pos-stack)) 870 | (?\) 871 | (let ((ov (make-overlay (car pos-stack) (point)))) 872 | (overlay-put ov 'tspew-depth (length pos-stack)) 873 | (setq max-depth (max (length pos-stack) max-depth)) 874 | (overlay-put ov 'is-tspew t) 875 | (pop pos-stack))) 876 | (t nil)) 877 | (forward-char 1)) 878 | 879 | ;; create an overlay recording the maximum depth encountered 880 | (let ((ov (make-overlay start end))) 881 | (overlay-put ov 'tspew-max-depth (+ max-depth 1)) 882 | (overlay-put ov 'is-tspew t))))) 883 | 884 | (defun tspew--fold-to-depth (start end level) 885 | "Hide text regions with depth >= level. 886 | When level is nil, all regions are made visible" 887 | (dolist 888 | (ov (overlays-in start end)) 889 | (when-let ((depth (overlay-get ov 'tspew-depth))) 890 | ;; regions with depth > N are contained within regions of depth N 891 | ;; therefore we need only hide those exactly at "level" 892 | (if (and level (equal depth level)) 893 | (progn 894 | (overlay-put ov 'invisible t) 895 | (overlay-put ov 'before-string "...")) 896 | (overlay-put ov 'invisible nil) 897 | (overlay-put ov 'before-string nil))))) 898 | 899 | (defun tspew-decrease-detail () 900 | "Hide (or \"fold\") the lowest levels of hierarchy in a tspew-formatted region. 901 | Each time you use this command one additional level is hidden." 902 | (interactive) 903 | 904 | (if-let* ((ov (seq-find (lambda (o) (overlay-get o 'tspew-max-depth)) 905 | (overlays-in (point) (+ (point) 1)))) 906 | (max-depth (overlay-get ov 'tspew-max-depth))) 907 | (progn 908 | (if-let ((depth (overlay-get ov 'tspew-current-depth))) 909 | (overlay-put ov 'tspew-current-depth (max (- depth 1) 1)) 910 | ;; create a tspew-current-depth property from max-depth 911 | (overlay-put ov 'tspew-current-depth (max (- (overlay-get ov 'tspew-max-depth) 1) 0))) 912 | (message "hiding depth %d and higher" (overlay-get ov 'tspew-current-depth)) 913 | (tspew-fold (overlay-get ov 'tspew-current-depth))) 914 | (error "no formatted region found"))) 915 | 916 | (defun tspew-increase-detail () 917 | "Expose more of the lower levels of hierarchy in a tspew-formatted region. 918 | Each time you use this command one additional level is revealed." 919 | (interactive) 920 | 921 | (if-let* ((ov (seq-find (lambda (o) (overlay-get o 'tspew-max-depth)) 922 | (overlays-in (point) (+ (point) 1)))) 923 | (max-depth (overlay-get ov 'tspew-max-depth))) 924 | (progn 925 | (if-let ((depth (overlay-get ov 'tspew-current-depth))) 926 | (overlay-put ov 'tspew-current-depth 927 | (min (+ depth 1) (overlay-get ov 'tspew-max-depth))) 928 | (overlay-put ov 'tspew-current-depth (overlay-get ov 'tspew-max-depth))) 929 | (message "hiding depth %d and higher" (overlay-get ov 'tspew-current-depth)) 930 | (tspew-fold (overlay-get ov 'tspew-current-depth))) 931 | (error "no formatted region found"))) 932 | 933 | (defun tspew--quoted-range-at (pos) 934 | "Return the range, including quotes, within which pos is found. 935 | Returns nil if pos is not within a quoted range." 936 | (save-excursion 937 | (beginning-of-line) 938 | (let ((end (save-excursion (end-of-line) (point))) 939 | (rstart nil) 940 | (rend nil)) 941 | (while (re-search-forward tspew-quoted-region-regexp end t) 942 | (when (and (>= pos (match-beginning 0)) (< pos (match-end 0))) 943 | (setq rstart (match-beginning 0)) 944 | (setq rend (match-end 0)))) 945 | (if (and rstart rend) (list rstart rend) nil)))) 946 | 947 | (defun tspew--visible-distance (start end) 948 | "Return the number of visible characters in the buffer between the 949 | given positions, taking into account overlays with invisible and 950 | before-string properties" 951 | (cl-assert (> end start)) 952 | ;; thinking about the algorithm 953 | ;; we start in an unknown place. If at the start of an overlay with before-string, add its length 954 | ;; if in an invisible overlay, note start point 955 | ;; go to next overlay transition 956 | ;; if now visible (or at end), subtract distance from prev pos/start point and mark us as visible again 957 | ;; if we were visible previously, take no action 958 | 959 | (with-restriction start end 960 | (save-excursion 961 | (goto-char start) 962 | (let* ( 963 | ;; get before-string property, if any, of an invisible overlay that starts here 964 | ;; we count only the invisible ones, and only at the start points, to avoid double-counting 965 | ;; if it's visible, the before-string is visually before this point 966 | ;; if it's not the start point, we've taken it into account already 967 | (before-string-at-point 968 | (lambda () 969 | (cl-reduce (lambda (x y) (or x y)) ;; reduce with or - but it's a macro so we do this 970 | (mapcar (lambda (ov) 971 | (and (equal (overlay-start ov) (point)) 972 | (overlay-get ov 'invisible) 973 | (overlay-get ov 'before-string))) 974 | (overlays-in (point) (+ (point) 1))) 975 | :initial-value nil))) 976 | (before (funcall before-string-at-point)) 977 | (prev-invisible (get-char-property start 'invisible)) 978 | (prev-pos start) 979 | (distance (if before (+ (- end start) (length before)) (- end start)))) 980 | (goto-char (next-overlay-change start)) 981 | (while (< (point) end) 982 | (when prev-invisible (setq distance (- distance (- (point) prev-pos)))) 983 | (setq prev-invisible (get-char-property (point) 'invisible)) 984 | (setq prev-pos (point)) 985 | (if-let ((before (funcall before-string-at-point))) 986 | (setq distance (+ distance (length before)))) 987 | (goto-char (next-overlay-change (point)))) 988 | (if prev-invisible (- distance (- (point) prev-pos)) distance))))) 989 | 990 | (defun tspew-fold (&optional level) 991 | "Fold the quoted region containing point to the requested level. 992 | Text at the designated level, or deeper, will be replaced with ellipses. 993 | The value nil will unfold all levels." 994 | (interactive "P") 995 | ;; find the quoted region containing point 996 | (if-let* ((range (tspew--quoted-range-at (point))) 997 | (start (car range)) 998 | (end (cadr range))) 999 | (progn 1000 | (tspew--fold-to-depth start end (and level (prefix-numeric-value level))) 1001 | ;; remove indentation overlays from the region in preparation for reformatting 1002 | (dolist (ov (overlays-in start end)) 1003 | (when (and (overlay-get ov 'is-tspew) 1004 | (not (overlay-get ov 'tspew-depth)) 1005 | (not (overlay-get ov 'tspew-max-depth))) 1006 | (delete-overlay ov))) 1007 | ;; and now perform formatting again keeping in mind the folded expressions 1008 | (tspew--handle-quoted-expr (+ start 1) (- end 1))) 1009 | (error "Not inside a quoted region"))) 1010 | 1011 | ;; BOZO should this be tspew-mode? 1012 | (provide 'tspew) 1013 | ;;; tspew.el ends here 1014 | -------------------------------------------------------------------------------- /.emacs.d/site-lisp/utilities.el: -------------------------------------------------------------------------------- 1 | ;; -*- lexical-binding: t; -*- 2 | ;;------------------------------------------------------------------------------ 3 | ;; Get rid of all buffers, going back to just *scratch* 4 | (defun nuke-all-buffers () 5 | "kill all buffers, leaving *scratch* only" 6 | (interactive) 7 | (mapc (lambda (x) (kill-buffer x)) 8 | (buffer-list)) 9 | (delete-other-windows)) 10 | 11 | ;;------------------------------------------------------------------------------ 12 | ;; Evaluate s-exp at point and replace it with its value 13 | ;; cf. eval-last-sexp bound to C-x C-e 14 | (defun eval-and-replace () 15 | "Replace the preceding sexp with its value." 16 | (interactive) 17 | (backward-kill-sexp) 18 | (condition-case nil 19 | (prin1 (eval (read (current-kill 0))) 20 | (current-buffer)) 21 | (error (message "Invalid expression") 22 | (insert (current-kill 0))))) 23 | 24 | ;;------------------------------------------------------------------------------ 25 | ;; Toggle horizontal/vertical window split 26 | (defun toggle-window-split () 27 | (interactive) 28 | (if (= (count-windows) 2) 29 | (let* ((this-win-buffer (window-buffer)) 30 | (next-win-buffer (window-buffer (next-window))) 31 | (this-win-edges (window-edges (selected-window))) 32 | (next-win-edges (window-edges (next-window))) 33 | (this-win-2nd (not (and (<= (car this-win-edges) 34 | (car next-win-edges)) 35 | (<= (cadr this-win-edges) 36 | (cadr next-win-edges))))) 37 | (splitter 38 | (if (= (car this-win-edges) 39 | (car (window-edges (next-window)))) 40 | 'split-window-horizontally 41 | 'split-window-vertically))) 42 | (delete-other-windows) 43 | (let ((first-win (selected-window))) 44 | (funcall splitter) 45 | (if this-win-2nd (other-window 1)) 46 | (set-window-buffer (selected-window) this-win-buffer) 47 | (set-window-buffer (next-window) next-win-buffer) 48 | (select-window first-win) 49 | (if this-win-2nd (other-window 1)))))) 50 | 51 | ;;------------------------------------------------------------------------------ 52 | ;; which minor modes are active? 53 | (defun which-active-modes () 54 | "Give a message of which minor modes are enabled in the current buffer." 55 | (interactive) 56 | (let ((active-modes)) 57 | (mapc (lambda (mode) (condition-case nil 58 | (if (and (symbolp mode) (symbol-value mode)) 59 | (add-to-list 'active-modes mode)) 60 | (error nil) )) 61 | minor-mode-list) 62 | (message "Active modes are %s" active-modes))) 63 | 64 | ;;------------------------------------------------------------------------------ 65 | ;; Insert current time/date 66 | (defun insert-current-time (prefix) 67 | "Insert the current date. With prefix-argument, use 24h format. 68 | With two prefix arguments, write out an ISO 8601 date and 69 | time." 70 | (interactive "P") 71 | (let ((format (cond 72 | ((not prefix) "%I:%M:%S %p") 73 | ((equal prefix '(4)) "%T") 74 | ((equal prefix '(16)) "%FT%T%z")))) 75 | (insert (format-time-string format)))) 76 | 77 | (defun insert-current-date (prefix) 78 | "Insert the current date. With prefix-argument, use ISO 8601 79 | format. With two prefix arguments, write out the day and month 80 | name." 81 | (interactive "P") 82 | (let ((format (cond 83 | ((not prefix) "%x") 84 | ((equal prefix '(4)) "%F") 85 | ((equal prefix '(16)) "%A, %d %B %Y")))) 86 | (insert (format-time-string format)))) 87 | 88 | ;;------------------------------------------------------------------------------ 89 | ;; Insert generated UUIDs 90 | (random t) 91 | 92 | (defun random-ms-uuid () 93 | (format "%04x%04x-%04x-4%s-%s-%06x%06x" 94 | (random (expt 16 4)) 95 | (random (expt 16 4)) 96 | (random (expt 16 4)) 97 | (substring (format "%04x" (random (expt 16 4))) 1) 98 | (concat 99 | (let ((n (random 4))) 100 | (substring "89ab" n (1+ n))) 101 | (substring (format "%04x" (random (expt 16 4))) 1)) 102 | (random (expt 16 6)) 103 | (random (expt 16 6)))) 104 | 105 | (defun random-xcode-uuid () 106 | (format "%04X%04X%04X%04X%04X%04X" 107 | (random (expt 16 4)) 108 | (random (expt 16 4)) 109 | (random (expt 16 4)) 110 | (random (expt 16 4)) 111 | (random (expt 16 4)) 112 | (random (expt 16 4)))) 113 | 114 | (defun insert-uuid (prefix) 115 | "Insert a random universally unique identifier (UUID). A UUID 116 | is a 128-bit (16 byte) number formatted in a certain way. 117 | 118 | Example of a UUID: 1df63142-a513-X850-Y1a3-535fc3520c3d 119 | Where X is 4 and Y is one of {8,9,a,b}. 120 | 121 | With a prefix argument, insert a random UUID suitable for use in 122 | XCode projects. An XCode UUID is a 96-bit (12 byte) number 123 | formatted as a hex string. 124 | 125 | Example of an XCode UUID: a513b85041a3535fc3520c3d." 126 | (interactive "P") 127 | (insert 128 | (cond 129 | ((not prefix) (random-ms-uuid)) 130 | ((equal prefix '(4)) (random-xcode-uuid))))) 131 | 132 | ;;------------------------------------------------------------------------------ 133 | ;; copy various things to kill ring 134 | (defun gk-copy-buffer-file-name () 135 | "Push the buffer's file name to the ‘kill-ring’." 136 | (interactive) 137 | (if-let* ((fil (buffer-file-name))) 138 | (with-temp-buffer 139 | (insert fil) 140 | (clipboard-kill-ring-save (point-min) (point-max)) 141 | (message fil)) 142 | (error "Buffer not visiting a file."))) 143 | 144 | (defun gk-copy-last-message () 145 | "Copy-as-kill the last echoed message." 146 | (interactive) 147 | (with-current-buffer (messages-buffer) 148 | (save-excursion 149 | (goto-char (point-max)) 150 | (forward-line -1) 151 | (clipboard-kill-ring-save 152 | (line-beginning-position) (line-end-position))))) 153 | 154 | ;;------------------------------------------------------------------------------ 155 | ;; get the paths of all the submodules in the current git repo 156 | (defun my-git-submodule-paths () 157 | "Get a list of all the git submodule paths in the current 158 | (projectile-project-root)." 159 | (interactive) 160 | (let* ((root (projectile-project-root))) 161 | (split-string (shell-command-to-string 162 | (concat "cd " root " && " 163 | "git submodule --quiet foreach 'echo $path'"))))) 164 | 165 | ;; advise projectile-ripgrep: don't search in submodules 166 | (defun my-projectile-ripgrep (func &rest args) 167 | (let ((saved projectile-globally-ignored-directories)) 168 | (setq projectile-globally-ignored-directories 169 | (append projectile-globally-ignored-directories (my-git-submodule-paths))) 170 | (apply func args) 171 | (setq projectile-globally-ignored-directories saved))) 172 | 173 | (advice-add #'projectile-ripgrep :around #'my-projectile-ripgrep) 174 | 175 | ;; forward to next indicated character 176 | (defun my-search-forward-1 (char &optional count) 177 | "Search forward for CHAR COUNT times in current line." 178 | (interactive 179 | (list (read-char "1> ") 180 | current-prefix-arg)) 181 | (forward-char) 182 | (unwind-protect 183 | (search-forward (char-to-string char) (line-end-position) nil (or count 1)) 184 | (backward-char) 185 | (point))) 186 | 187 | ;; back to previous indicated character 188 | (defun my-search-backward-1 (char &optional count) 189 | "Search backward for CHAR COUNT times in current line." 190 | (interactive 191 | (list (read-char "1> ") 192 | current-prefix-arg)) 193 | (backward-char) 194 | (unwind-protect 195 | (search-backward (char-to-string char) (line-beginning-position) nil 196 | (or count 1)) 197 | (forward-char) 198 | (point))) 199 | -------------------------------------------------------------------------------- /.emacs.d/site-lisp/welcome-dashboard.el: -------------------------------------------------------------------------------- 1 | ;;; welcome-dashboard.el --- Simple welcome-dashboard screen -*- lexical-binding: t -*- 2 | 3 | ;; Welcome-dashboard screen 4 | 5 | ;; Authod: Mikael Konradsson 6 | ;; Maintainer: Mikael Konradsson 7 | ;; Created: 2023 8 | ;; Package-Version: 0.1 9 | ;; Package-Requires: ((emacs "27.1") (compat "29.1.4.1")) 10 | ;; Homepage: https://github.com/konrad1977/welcome-dashboard 11 | 12 | ;;; Commentary: 13 | 14 | ;;; Minimalistic dashboard for Emacs. 15 | 16 | ;; code: 17 | 18 | (require 'all-the-icons) 19 | (require 'async) 20 | (require 'json) 21 | (require 'recentf) 22 | (require 'url) 23 | (require 'vc) 24 | 25 | ;;; Code: 26 | 27 | (defvar welcome-dashboard-mode nil) 28 | (defvar welcome-dashboard-recentfiles '() 29 | "Recent list.") 30 | 31 | (defvar welcome-dashboard-todos '() 32 | "Todos.") 33 | 34 | (defvar welcome-dashboard-last-project-name nil) 35 | 36 | (defvar welcome-dashboard-temperature nil) 37 | (defvar welcome-dashboard-weatherdescription nil) 38 | (defvar welcome-dashboard-weathericon nil) 39 | 40 | (defcustom welcome-dashboard-title "Quick access [C-number to open file]" 41 | "Welcome-dashboard title." 42 | :group 'welcome-dashboard 43 | :type '(string)) 44 | 45 | (defcustom welcome-dashboard-use-fahrenheit nil 46 | "Show weather temperature in fahrenheit." 47 | :group 'welcome-dashboard 48 | :type '(boolean)) 49 | 50 | (defcustom welcome-dashboard-min-left-padding 10 51 | "Minimum left padding when resizing window." 52 | :group 'welcome-dashboard 53 | :type '(natnum)) 54 | 55 | (defcustom welcome-dashboard-path-max-length 72 56 | "Latitude for weather information." 57 | :group 'welcome-dashboard 58 | :type '(natnum)) 59 | 60 | (defcustom welcome-dashboard-latitude nil 61 | "Latitude for weather information." 62 | :group 'welcome-dashboard 63 | :type '(float)) 64 | 65 | (defcustom welcome-dashboard-longitude nil 66 | "Longitude for weather information in welcome-dashboard package." 67 | :group 'welcome-dashboard 68 | :type '(float)) 69 | 70 | (defcustom welcome-dashboard-image-file "" 71 | "Image file in welcome-dashboard package." 72 | :group 'welcome-dashboard 73 | :type '(file)) 74 | 75 | (defcustom welcome-dashboard-image-width 200 76 | "Image width for weather information." 77 | :group 'welcome-dashboard 78 | :type '(natnum)) 79 | 80 | (defcustom welcome-dashboard-image-height 200 81 | "Image width for weather information." 82 | :group 'welcome-dashboard 83 | :type '(natnum)) 84 | 85 | (defgroup welcome-dashboard nil 86 | "Welcome-dashboard group." 87 | :group 'applications) 88 | 89 | (defconst welcome-dashboard-buffer "*welcome*" 90 | "Welcome-dashboard buffer name.") 91 | 92 | (defvar welcome-dashboard-mode-map 93 | (let ((map (make-sparse-keymap))) 94 | (define-key map (kbd "RET") 'welcome-dashboard--open-recent-file) 95 | (define-key map (kbd "") 'welcome-dashboard--open-recent-file) 96 | (define-key map (kbd "o") 'welcome-dashboard--open-recent-file) 97 | 98 | ;; Add shortcuts for file indexes 99 | (dolist (i (number-sequence 1 9)) 100 | (define-key map (kbd (concat "C-" (number-to-string i))) 101 | `(lambda () 102 | (interactive) 103 | (welcome-dashboard--open-recent-file-at-index ,i))) 104 | (define-key map (kbd (concat "C-x " (number-to-string i))) 105 | `(lambda () 106 | (interactive) 107 | (welcome-dashboard--open-todos-at-index ,i)))) 108 | 109 | map) 110 | "Keymap for `welcome-dashboard-mode'.") 111 | 112 | (define-derived-mode welcome-dashboard-mode fundamental-mode "Welcome-dashboard" 113 | "Major mode for the welcome-dashboard screen." 114 | :group 'welcome-dashboard 115 | :syntax-table nil 116 | :abbrev-table nil 117 | (buffer-disable-undo) 118 | (setq-local display-line-numbers nil) 119 | (setq-local truncate-lines t) 120 | (setq-local mode-line-format nil) 121 | (setq-local global-hl-line-mode nil) 122 | (use-local-map welcome-dashboard-mode-map)) 123 | 124 | (defface welcome-dashboard-title-face 125 | '((t :foreground "#87AAF8" :height 1.2 :weight thin)) 126 | "Title face." 127 | :group 'welcome-dashboard) 128 | 129 | (defface welcome-dashboard-subtitle-face 130 | '((t :foreground "#9399b2" :weight semi-bold)) 131 | "Subtitle face." 132 | :group 'welcome-dashboard) 133 | 134 | (defface welcome-dashboard-info-face 135 | '((t :foreground "#F66D86" :height 0.9 :bold t :italic t)) 136 | "Face added to code-usage display." 137 | :group 'welcome-dashboard) 138 | 139 | (defface welcome-dashboard-text-info-face 140 | '((t :foreground "#ADB5D0" :height 0.9 :bold nil)) 141 | "Face added to code-usage display." 142 | :group 'welcome-dashboard) 143 | 144 | (defface welcome-dashboard-path-face 145 | '((t :foreground "#63677D" :height 0.9 :weight thin :bold nil :italic nil)) 146 | "Face for the file path." 147 | :group 'welcome-dashboard) 148 | 149 | (defface welcome-dashboard-filename-face 150 | '((t :weight semi-bold)) 151 | "Face for the file name." 152 | :group 'welcome-dashboard) 153 | 154 | (defface welcome-dashboard-time-face 155 | '((t :foreground "#a6adc8" :height 0.9 :weight thin)) 156 | "Face for time." 157 | :group 'welcome-dashboard) 158 | 159 | (defface welcome-dashboard-weather-description-face 160 | '((t :foreground "#f9e2af" :height 0.9 :weight thin :bold nil :italic nil)) 161 | "Face for weather description." 162 | :group 'welcome-dashboard) 163 | 164 | (defface welcome-dashboard-startup-time-face 165 | '((t :foreground "#C2A4F8" :height 0.9 :weight thin :bold nil :italic nil)) 166 | "Face for startup time." 167 | :group 'welcome-dashboard) 168 | 169 | (defface welcome-dashboard-shortcut-face 170 | '((t :foreground "#f9e2af" :height 0.9 :bold t)) 171 | "Face for recent files shortcuts." 172 | :group 'welcome-dashboard) 173 | 174 | (defface welcome-dashboard-shortcut-todo-face 175 | '((t :foreground "#abd47c" :height 0.9 :bold t)) 176 | "Face for todo shortcuts." 177 | :group 'welcome-dashboard) 178 | 179 | (defface welcome-dashboard-todo-type-face 180 | '((t :foreground "#585b70" :height 0.9 :bold t)) 181 | "Face for todo shortcuts." 182 | :group 'welcome-dashboard) 183 | 184 | (defface welcome-dashboard-weather-icon-face 185 | '((t :height 0.9)) 186 | "Face for weather icon." 187 | :group 'welcome-dashboard) 188 | 189 | (defface welcome-dashboard-weather-temperature-face 190 | '((t :foreground "#f38ba8" :height 0.9 :weight thin :bold nil :italic nil)) 191 | "Face for temperature." 192 | :group 'welcome-dashboard) 193 | 194 | (defface welcome-dashboard-project-face 195 | '((t :foreground "#f38ba8" :bold t)) 196 | "Face for project name." 197 | :group 'welcome-dashboard) 198 | 199 | (defun welcome-dashboard--weather-icon-from-code (code) 200 | "Maps a weather (as CODE) to a corresponding string." 201 | (pcase code 202 | (`0 "wi-day-sunny") 203 | ((or `1 `2 `3) "wi-day-cloudy") 204 | ((or `45 `48) "wi-day-fog") 205 | ((or `51 `53 `55) "wi-sprinkle") 206 | ((or `56 `57) "wi-snow") 207 | ((or `61 `63 `65) "wi-day-rain") 208 | ((or `66 `67) "wi-day-rain-mix") 209 | ((or `71 `73 `75) "wi-snow") 210 | (`77 "wi-snow") 211 | ((or `80 `81 `82) "wi-rain") 212 | ((or `85 `86) "wi-rain-mix") 213 | ((or `95 `96 `99) "wi-thunderstorm") 214 | (_ "Unknown"))) 215 | 216 | (defun welcome-dashboard--weather-code-to-string (code) 217 | "Maps a weather (as CODE) to a corresponding string." 218 | (pcase code 219 | (`0 "Clear sky") 220 | ((or `1 `2 `3) "Partly cloudy") 221 | ((or `45 `48) "Fog") 222 | ((or `51 `53 `55) "Drizzle") 223 | ((or `56 `57) "Freezing drizzle") 224 | ((or `61 `63 `65) "Rain") 225 | ((or `66 `67) "Freezing rain") 226 | ((or `71 `73 `75) "Snowfall") 227 | (`77 "Snow grains") 228 | ((or `80 `81 `82) "Rain showers") 229 | ((or `85 `86) "Snow showers") 230 | ((or `95 `96 `99) "Thunderstorm") 231 | (_ "Unknown"))) 232 | 233 | (defun welcome-dashboard--insert-centered (text) 234 | "Insert TEXT at the center of the current line." 235 | (let ((width (window-width))) 236 | (insert (make-string (/ (- width (length text)) 2) ?\ )) 237 | (insert text))) 238 | 239 | (defun welcome-dashboard--open-recent-file () 240 | "Open the recent file on the current line." 241 | (interactive) 242 | (let* ((line-start (line-beginning-position)) 243 | (line-end (line-end-position)) 244 | (prop-pos (next-single-property-change line-start 'path nil line-end))) 245 | (when prop-pos 246 | (let ((file (get-text-property prop-pos 'path))) 247 | (if (file-exists-p file) 248 | (find-file file) 249 | (error "File %s does not exist" file)))))) 250 | 251 | (defun welcome-dashboard--open-recent-file-at-index (index) 252 | "Open the recent file at the given INDEX in the list." 253 | (interactive "nIndex: ") 254 | (let ((files welcome-dashboard-recentfiles)) 255 | (when (<= 1 index (length files)) 256 | (find-file (nth (1- index) files))))) 257 | 258 | (defun welcome-dashboard--open-todos-at-index (index) 259 | "Open the todo at INDEX." 260 | (interactive "nIndex: ") 261 | (let ((todos welcome-dashboard-todos)) 262 | (when (<= 1 index (length todos)) 263 | (let* ((todo (nth (1- index) todos)) 264 | (file (nth 0 todo)) 265 | (line (string-to-number (nth 1 todo))) 266 | (column (string-to-number (nth 2 todo)))) 267 | (find-file file) 268 | (goto-char (point-min)) 269 | (forward-line (1- line)) 270 | (forward-char (1- column)))))) 271 | 272 | (defun welcome-dashboard--truncate-path-in-middle (path n) 273 | "Truncate the middle of PATH to length N by removing characters. 274 | And adding an ellipsis." 275 | (if (<= (length path) n) 276 | path 277 | (let* ((left (/ (- n 3) 2)) 278 | (right (- n left 3)) 279 | (head (substring path 0 (+ left 1))) 280 | (tail (substring path (- (length path) right))) 281 | (ellipsis "...")) 282 | (concat head ellipsis tail)))) 283 | 284 | (defun welcome-dashboard--insert-recent-files () 285 | "Insert the first x recent files with icons in the welcome-dashboard buffer." 286 | (recentf-mode) 287 | (insert "\n") 288 | (let* ((files welcome-dashboard-recentfiles) 289 | (left-margin (welcome-dashboard--calculate-padding-left))) 290 | (dolist (file files) 291 | (let* ((index (cl-position file files :test #'equal)) 292 | (full-path (file-truename file)) 293 | (shortcut (format "%d" (+ index +1))) 294 | (file-name (file-name-nondirectory file)) 295 | (file-dir (file-name-directory file)) 296 | (title (format "%s %s%s" 297 | (propertize (all-the-icons-icon-for-file file :v-adjust -0.05) 'face '(:family "all-the-icons" :height 1.0)) 298 | (propertize (welcome-dashboard--truncate-path-in-middle file-dir welcome-dashboard-path-max-length) 'face 'welcome-dashboard-path-face) 299 | (propertize file-name 'face 'welcome-dashboard-filename-face))) 300 | (title-with-path (propertize title 'path full-path)) 301 | (title-with-path-and-shortcut (concat title-with-path (propertize (format " [%s]" shortcut) 'face 'welcome-dashboard-shortcut-face)))) 302 | (insert (format "%s%s\n" (make-string left-margin ?\s) title-with-path-and-shortcut)))))) 303 | 304 | (defun welcome-dashboard--insert-todos () 305 | "Insert todos." 306 | (when (> (length welcome-dashboard-todos) 0) 307 | (welcome-dashboard--insert-text 308 | (format "%s %s %s" 309 | (propertize "You got work todo in" 'face 'welcome-dashboard-subtitle-face) 310 | (propertize welcome-dashboard-last-project-name 'face 'welcome-dashboard-project-face) 311 | (propertize "project" 'face 'welcome-dashboard-subtitle-face))) 312 | (dolist (todo welcome-dashboard-todos) 313 | (let* ((index (cl-position todo welcome-dashboard-todos :test #'equal)) 314 | (shortcut (format "%d" (+ index +1))) 315 | (path (nth 0 todo)) 316 | (type (nth 3 todo)) 317 | (text (nth 4 todo)) 318 | (title (format "%s %s %s %s" 319 | (propertize (all-the-icons-octicon "alert") 320 | 'face `(:family ,(all-the-icons-octicon-family) :height 1.0) 321 | 'display '(raise 0)) 322 | (propertize type 'face 'welcome-dashboard-todo-type-face) 323 | (propertize (string-trim-left (welcome-dashboard--truncate-text-right text)) 'face 'welcome-dashboard-filename-face) 324 | (propertize (format "[%s]" shortcut) 'face 'welcome-dashboard-shortcut-todo-face)))) 325 | (welcome-dashboard--insert-text 326 | (propertize title 'path path)))))) 327 | 328 | (defun welcome-dashboard--calculate-padding-left () 329 | "Calculate padding for left side." 330 | (if-let* ((files welcome-dashboard-recentfiles) 331 | (max-length (apply 'max (mapcar (lambda (path) (length (welcome-dashboard--truncate-path-in-middle path welcome-dashboard-path-max-length))) files))) 332 | (filenames (mapcar (lambda (path) (file-name-nondirectory path)) files)) 333 | (max-filename-length (/ (apply 'max (mapcar 'length filenames)) 2)) 334 | (left-margin (max (+ welcome-dashboard-min-left-padding max-filename-length) (/ (- (window-width) max-length) 2)))) 335 | (- left-margin max-filename-length) 336 | welcome-dashboard-min-left-padding)) 337 | 338 | (defun welcome-dashboard--insert-text (text) 339 | "Insert (as TEXT)." 340 | (let ((left-margin (welcome-dashboard--calculate-padding-left))) 341 | (insert (format "%s%s\n" (make-string left-margin ?\s) text)))) 342 | 343 | (defun welcome-dashboard--redisplay-buffer-on-resize (&rest _) 344 | "Resize current buffer." 345 | (when (equal (buffer-name) welcome-dashboard-buffer) 346 | (welcome-dashboard--refresh-screen))) 347 | 348 | (defun welcome-dashboard--fetch-weather-data () 349 | "Fetch weather data from API." 350 | (let ((url-request-method "GET") 351 | (url-request-extra-headers '(("Content-Type" . "application/json"))) 352 | (url (format "https://api.open-meteo.com/v1/forecast?latitude=%s&longitude=%s¤t_weather=true" welcome-dashboard-latitude welcome-dashboard-longitude))) 353 | (url-retrieve url 354 | (lambda (_) 355 | (goto-char (point-min)) 356 | (re-search-forward "^$") 357 | (let* ((json-data (buffer-substring-no-properties (point) (point-max))) 358 | (json-obj (json-read-from-string json-data)) 359 | (current-weather (cdr (assoc 'current_weather json-obj))) 360 | (temp (cdr (assoc 'temperature current-weather))) 361 | (weather-code (cdr (assoc 'weathercode current-weather))) 362 | (weather-icon (all-the-icons-icon-for-weather 363 | (welcome-dashboard--weather-icon-from-code weather-code)))) 364 | (setq welcome-dashboard-weathericon weather-icon) 365 | (if welcome-dashboard-use-fahrenheit 366 | (setq welcome-dashboard-temperature (format "%.1f" (+ (* temp 1.8) 32))) 367 | (setq welcome-dashboard-temperature (format "%.1f" temp))) 368 | (setq welcome-dashboard-weatherdescription (format "%s" (welcome-dashboard--weather-code-to-string weather-code)))) 369 | (welcome-dashboard--refresh-screen)) 370 | nil 371 | t))) 372 | 373 | ;;;###autoload 374 | (defun welcome-dashboard-create-welcome-hook () 375 | "Setup welcome-dashboard screen." 376 | (when (< (length command-line-args) 2) 377 | (add-hook 'switch-to-buffer 'welcome-dashboard--redisplay-buffer-on-resize) 378 | (add-hook 'window-size-change-functions 'welcome-dashboard--redisplay-buffer-on-resize) 379 | (add-hook 'emacs-startup-hook (lambda () 380 | (welcome-dashboard--refresh-screen) 381 | (welcome-dashboard--fetch-todos) 382 | (when (welcome-dashboard--show-weather-info) 383 | (welcome-dashboard--fetch-weather-data)))))) 384 | (defun welcome-dashboard--truncate-text-right (text) 385 | "Truncate TEXT at the right to a maximum of 100 characters." 386 | (if (> (length text) 70) 387 | (concat (substring text 0 67) "...") 388 | text)) 389 | 390 | 391 | 392 | (defun welcome-dashboard--insert-startup-time () 393 | "Insert startup time." 394 | (welcome-dashboard--insert-text (format "%s %s %s %s" 395 | (propertize (all-the-icons-octicon "clock") 396 | 'face `(:family ,(all-the-icons-octicon-family) :height 1.0) 397 | 'display '(raise 0)) 398 | (propertize "Startup time:" 'face 'welcome-dashboard-text-info-face) 399 | (propertize (emacs-init-time "%.2f") 'face 'welcome-dashboard-startup-time-face) 400 | (propertize "seconds" 'face 'welcome-dashboard-text-info-face)))) 401 | 402 | 403 | (defun welcome-dashboard--insert-package-info (packages) 404 | "Insert package info as (PACKAGES)." 405 | (welcome-dashboard--insert-text (format "%s %s %s" 406 | (propertize (all-the-icons-octicon "package") 407 | 'face `(:family ,(all-the-icons-octicon-family) :height 1.0) 408 | 'display '(raise -0.1)) 409 | (propertize packages 'face 'welcome-dashboard-info-face 'display '(raise -0.1)) 410 | (propertize "packages loaded" 'face 'welcome-dashboard-text-info-face 'display '(raise -0.1))))) 411 | 412 | (defun welcome-dashboard--temperature-symbol () 413 | "Get the correct type of temperature symbol." 414 | (if welcome-dashboard-use-fahrenheit 415 | "℉" 416 | "℃")) 417 | 418 | (defun welcome-dashboard--show-weather-info () 419 | "Check if we latitude and longitude and then show weather info." 420 | (let ((latitude welcome-dashboard-latitude) 421 | (longitude welcome-dashboard-longitude)) 422 | (if (and (floatp latitude) (floatp longitude)) 423 | t 424 | nil))) 425 | 426 | (defun welcome-dashboard--insert-weather-info () 427 | "Insert weather info." 428 | (when (welcome-dashboard--show-weather-info) 429 | (if welcome-dashboard-weatherdescription 430 | (welcome-dashboard--insert-text (format "%s %s, %s%s" 431 | (propertize welcome-dashboard-weathericon 'face '(:family "Weather icons" :height 1.0) 'display '(raise 0)) 432 | (propertize welcome-dashboard-weatherdescription 'face 'welcome-dashboard-weather-description-face) 433 | (propertize welcome-dashboard-temperature 'face 'welcome-dashboard-weather-temperature-face) 434 | (propertize (welcome-dashboard--temperature-symbol) 'face 'welcome-dashboard-text-info-face))) 435 | (welcome-dashboard--insert-text (propertize "Loading weather data..." 'face 'welcome-dashboard-weather-temperature-face))))) 436 | 437 | (defun welcome-dashboard--parse-todo-result (result) 438 | "Parse the RESULT and create a list of todo items." 439 | (let ((regex "\\(.+?\\):\\([0-9]+\\):\\([0-9]+\\):\s?\\W+\\(.*\\):\\(.*\\)$")) 440 | (save-match-data 441 | (let (matches todos) 442 | (while (string-match regex result) 443 | (setq matches (list (match-string 1 result) 444 | (match-string 2 result) 445 | (match-string 3 result) 446 | (match-string 4 result) 447 | (match-string 5 result))) 448 | (setq result (substring result (match-end 0))) 449 | (push matches todos)) 450 | (nreverse todos))))) 451 | 452 | (cl-defun welcome-dashboard--async-command-to-string (&key command &key callback) 453 | "Async shell command to JSON run async (as COMMAND) 454 | and parse it json and call (as CALLBACK)." 455 | (async-start 456 | `(lambda () 457 | (shell-command-to-string ,command)) 458 | `(lambda (result) 459 | (funcall ,callback result)))) 460 | 461 | (defun welcome-dashboard--last-root () 462 | "Get the version control root directory of the most recent file." 463 | (when (> (length welcome-dashboard-recentfiles) 0) 464 | (let ((file (car welcome-dashboard-recentfiles))) 465 | (vc-find-root file ".git")))) 466 | 467 | (defun welcome-dashboard--fetch-todos () 468 | "Fetch todos." 469 | (when (and (executable-find "rg") (welcome-dashboard--last-root)) 470 | (let* ((root (welcome-dashboard--last-root)) 471 | (projectname (file-name-nondirectory (directory-file-name root))) 472 | (command (format "rg -e \"(TODO|FIX|FIXME|PERF|HACK|NOTE):\s+\" --color=never --no-heading --with-filename --line-number --column --sort path %s" root))) 473 | (setq welcome-dashboard-last-project-name projectname) 474 | (welcome-dashboard--async-command-to-string 475 | :command command 476 | :callback '(lambda (result) 477 | (setq welcome-dashboard-todos (seq-take (welcome-dashboard--parse-todo-result result) 9)) 478 | (welcome-dashboard--refresh-screen)))))) 479 | 480 | ;; TODO: Help me do something 481 | 482 | (defun welcome-dashboard--package-length () 483 | "Get the number of installed packages." 484 | (cond 485 | ((bound-and-true-p package-alist) 486 | (length package-activated-list)) 487 | ((boundp 'straight--profile-cache) 488 | (hash-table-count straight--profile-cache)) 489 | ((boundp 'elpaca--queued) 490 | (length elpaca--queued)) 491 | (t 0))) 492 | 493 | (defun welcome-dashboard--refresh-screen () 494 | "Show the welcome-dashboard screen." 495 | (setq welcome-dashboard-recentfiles (seq-take recentf-list 9)) 496 | (with-current-buffer (get-buffer-create welcome-dashboard-buffer) 497 | (let* ((buffer-read-only) 498 | (image (create-image welcome-dashboard-image-file 'png nil :width welcome-dashboard-image-width :height welcome-dashboard-image-height)) 499 | (size (image-size image)) 500 | (width (car size)) 501 | (left-margin (max welcome-dashboard-min-left-padding (floor (/ (- (window-width) width) 2)))) 502 | (packages (format "%d" (welcome-dashboard--package-length)))) 503 | (erase-buffer) 504 | (goto-char (point-min)) 505 | (let ((inhibit-read-only t)) 506 | (insert "\n") 507 | (welcome-dashboard--insert-text (propertize welcome-dashboard-title 'face 'welcome-dashboard-title-face)) 508 | (welcome-dashboard--insert-recent-files) 509 | (setq cursor-type nil) 510 | 511 | (insert "\n") 512 | (welcome-dashboard--insert-todos) 513 | ;; (welcome-dashboard--insert-text (make-string 60 ?-)) 514 | 515 | (insert "\n") 516 | (welcome-dashboard--insert-startup-time) 517 | (welcome-dashboard--insert-package-info packages) 518 | (welcome-dashboard--insert-weather-info) 519 | 520 | (insert "\n\n") 521 | (insert (make-string left-margin ?\ )) 522 | (insert-image image) 523 | (insert "\n\n") 524 | (welcome-dashboard--insert-centered (propertize (format-time-string "%A, %B %d %H:%M") 'face 'welcome-dashboard-time-face)) 525 | 526 | (switch-to-buffer welcome-dashboard-buffer) 527 | (read-only-mode +1) 528 | (welcome-dashboard-mode) 529 | (goto-char (point-min)) 530 | (forward-line 3))))) 531 | 532 | (provide 'welcome-dashboard) 533 | ;;; welcome-dashboard.el ends here 534 | -------------------------------------------------------------------------------- /.emacs.d/site-lisp/zone-words-emotions-dictionary.el: -------------------------------------------------------------------------------- 1 | ;; -*- lexical-binding: t; -*- 2 | ;;; zone-words-emotions-dictionary.el --- zone-words dictionary. 3 | 4 | ;;; Commentary: 5 | ;; zone-words dictionary. 6 | 7 | 8 | ;;; Code: 9 | 10 | ;; From http://www.psychpage.com/learning/library/assess/feelings.html 11 | (defvar zone-words-emotions-dictionary--unpleasant-feelings '("irritated" "lousy" "upset" "incapable" "enraged" "disappointed" "doubtful" "alone" "hostile" "discouraged" "uncertain" "paralyzed" "insulting" "ashamed" "indecisive" "fatigued" "sore" "powerless" "perplexed" "useless" "annoyed" "diminished" "embarrassed" "inferior" "upset" "guilty" "hesitant" "vulnerable" "hateful" "dissatisfied" "shy" "empty" "unpleasant" "miserable" "stupefied" "forced" "offensive" "detestable" "disillusioned" "hesitant" "bitter" "repugnant" "unbelieving" "despair" "aggressive" "despicable" "skeptical" "frustrated" "resentful" "disgusting" "distrustful" "distressed" "inflamed" "abominable" "misgiving" "woeful" "provoked" "terrible" "lost" "pathetic" "incensed" "in despair" "unsure" "tragic" "infuriated" "sulky" "uneasy" "cross" "bad" "pessimistic" "dominated" "worked up" "tense" "boiling" "fuming" "indignant" "insensitive" "fearful" "crushed" "tearful" "dull" "terrified" "tormented" "sorrowful" "nonchalant" "suspicious" "deprived" "pained" "neutral" "anxious" "pained" "grief" "reserved" "alarmed" "tortured" "anguish" "weary" "panic" "dejected" "desolate" "bored" "nervous" "rejected" "desperate" "preoccupied" "scared" "injured" "pessimistic" "cold" "worried" "offended" "unhappy" "disinterested" "frightened" "afflicted" "lonely" "lifeless" "timid" "aching" "grieved" "shaky" "victimized" "mournful" "restless" "heartbroken" "dismayed" "doubtful" "agonized" "threatened" "appalled" "cowardly" "humiliated" "quaking" "wronged" "menaced" "alienated" "wary")) 12 | 13 | ;; From http://www.psychpage.com/learning/library/assess/feelings.html 14 | (defvar zone-words-emotions-dictionary--pleasant-feelings '("understanding" "great" "playful" "calm" "confident" "gay" "courageous" "peaceful" "reliable" "joyous" "energetic" "easy" "lucky" "liberated" "comfortable" "amazed" "fortunate" "optimistic" "pleased" "free" "delighted" "provocative" "encouraged" "sympathetic" "overjoyed" "impulsive" "clever" "interested" "gleeful" "free" "surprised" "satisfied" "thankful" "frisky" "content" "receptive" "important" "animated" "quiet" "accepting" "festive" "spirited" "certain" "kind" "ecstatic" "thrilled" "relaxed" "satisfied" "wonderful" "serene" "glad" "cheerful" "bright" "sunny" "blessed" "merry" "reassured" "elated" "jubilant" "loving" "concerned" "eager" "impulsive" "considerate" "affected" "keen" "free" "affectionate" "fascinated" "earnest" "sure" "sensitive" "intrigued" "intent" "certain" "tender" "absorbed" "anxious" "rebellious" "devoted" "inquisitive" "inspired" "unique" "attracted" "nosy" "determined" "dynamic" "passionate" "snoopy" "excited" "tenacious" "admiration" "engrossed" "enthusiastic" "hardy" "warm" "curious" "bold" "secure" "touched" "brave" "sympathy" "daring" "close" "challenged" "loved" "optimistic" "comforted" "re-enforced" "confident" "hopeful")) 15 | 16 | (defun zone-words-emotions-dictionary-lookup-emotion () 17 | (zone-words-emotions-dictionary--random-item (zone-words-emotions-dictionary--random-item 18 | (list 19 | zone-words-emotions-dictionary--pleasant-feelings 20 | zone-words-emotions-dictionary--unpleasant-feelings)))) 21 | 22 | (defun zone-words-emotions-dictionary--random-item (items) 23 | (nth (random (length items)) 24 | items)) 25 | 26 | (provide 'zone-words-emotions-dictionary) 27 | 28 | ;;; zone-words-emotions-dictionary.el ends here 29 | -------------------------------------------------------------------------------- /.emacs.d/site-lisp/zone-words.el: -------------------------------------------------------------------------------- 1 | ;; -*- lexical-binding: t; -*- 2 | ;;; zone-words.el --- Discover words to describe emotions while you zone out. 3 | 4 | ;;; Commentary: 5 | ;; Discover words to describe emotions while you zone out. 6 | 7 | 8 | ;;; Code: 9 | 10 | (require 'zone) 11 | (require 'zone-words-emotions-dictionary) 12 | 13 | (defvar zone-words-top-margin 2) 14 | (defvar zone-words-left-margin 5) 15 | (defvar zone-words-word-definition-margin 1) 16 | (defvar zone-words-word-pause 10) 17 | (defvar zone-words--word-lookup-func nil) 18 | (defvar zone-words-word-word-wrap 75) 19 | 20 | (defface zone-words--word-face 21 | '((((class color) (background light)) 22 | :foreground "DarkGrey" :weight bold :height 2.0 :inherit variable-pitch) 23 | (((class color) (background dark)) 24 | :foreground "white" :weight bold :height 2.5 :inherit variable-pitch) 25 | (t :height 1.5 :weight bold :inherit variable-pitch)) 26 | "Face for title" :group 'zone-words) 27 | 28 | (defun zone-words-preview () 29 | "Previews `zone-words'." 30 | (interactive) 31 | (let ((zone-programs [zone-words])) 32 | (zone))) 33 | 34 | (defun zone-words () 35 | "Display words to describe your emotions while you zone out." 36 | (delete-other-windows) 37 | (setq mode-line-format nil) 38 | (setq-local cursor-type nil) 39 | (zone-fill-out-screen (window-width) (window-height)) 40 | (while (not (input-pending-p)) 41 | (delete-region (point-min) (point-max)) 42 | (goto-char (point-min)) 43 | (zone-words--insert-vertical-space zone-words-top-margin) 44 | (let ((term (zone-words--word-lookup))) 45 | (insert (zone-words-word--position-text (car term))) 46 | (add-text-properties (line-beginning-position) (point) 47 | (list 'face 'zone-words--word-face)) 48 | (zone-words--insert-vertical-space zone-words-word-definition-margin) 49 | (insert (zone-words-word--position-text (cdr term)))) 50 | (zone-park/sit-for (point-min) zone-words-word-pause))) 51 | 52 | (defun zone-words-word--position-text (text) 53 | "Indent and wrap TEXT using `zone-words-left-margin' and `zone-words-word-word-wrap'." 54 | (zone-words-word--indent-text zone-words-left-margin 55 | (zone-words-word--word-wrap 56 | text zone-words-word-word-wrap))) 57 | 58 | (defun zone-words-word--indent-text (len text) 59 | "Indent by LEN characters, given TEXT." 60 | (replace-regexp-in-string "^" 61 | (zone-words-word--string-repeat " " len) 62 | text)) 63 | 64 | ;; From s.el. 65 | (defun zone-words-word--word-wrap (s len) 66 | "If S is longer than LEN, wrap the words with newlines." 67 | (with-temp-buffer 68 | (insert s) 69 | (let ((fill-column len)) 70 | (fill-region (point-min) (point-max))) 71 | (buffer-substring-no-properties (point-min) (point-max)))) 72 | 73 | ;; From s.el. 74 | (defun zone-words-word--string-repeat (s num) 75 | "Make a string of S repeated NUM times." 76 | (let (ss) 77 | (while (> num 0) 78 | (setq ss (cons s ss)) 79 | (setq num (1- num))) 80 | (apply 'concat ss))) 81 | 82 | (defun zone-words--word-lookup () 83 | "Look up a term and return a cons with term and definition." 84 | (if (functionp zone-words--word-lookup-func) 85 | (funcall zone-words--word-lookup-func) 86 | (let* ((word (zone-words-emotions-dictionary-lookup-emotion)) 87 | (definition (if (locate-file "wn" exec-path) 88 | (shell-command-to-string (format "wn %s -over" word)) 89 | "\n\nFor the definition, you need wordnet installed on your machine.\n\nInstall with:\n\nbrew install wordnet (Mac OS)\n\napt-get install wordnet (Linux)") 90 | )) 91 | (cons word definition)))) 92 | 93 | (defun zone-words--insert-vertical-space (n) 94 | "Insert vertical space (ie. N new lines)." 95 | (when (< n 1) 96 | (error "Argument must be positive")) 97 | (while (> n 0) 98 | (insert "\n") 99 | (setq n (- n 1)))) 100 | 101 | (provide 'zone-words) 102 | 103 | ;;; zone-words.el ends here 104 | -------------------------------------------------------------------------------- /.emacs.d/terminal.el: -------------------------------------------------------------------------------- 1 | ;; -*- lexical-binding: t; -*- 2 | ;;------------------------------------------------------------------------------ 3 | ;; Fix terminal keys 4 | (use-package term-keys 5 | :ensure t 6 | :config 7 | (term-keys-mode t) 8 | :diminish term-keys-mode) 9 | 10 | ;;------------------------------------------------------------------------------ 11 | ;; Use system clipboard 12 | (use-package clipetty 13 | :ensure t 14 | :hook (after-init . global-clipetty-mode)) 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.elc 2 | .emacs.d/packages 3 | .emacs.d/local 4 | .emacs.d/snippets 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # .emacs configuration 2 | 3 | --------------------------------------------------------------------------------