changelog shortlog graph tags branches changeset files revisions annotate raw help

Mercurial > core / emacs/default.el

changeset 690: 90417ae14b21
parent: 2b7d5a8d63ac
child: 38e9c3be2392
author: Richard Westhaver <ellis@rwest.io>
date: Tue, 01 Oct 2024 23:34:01 -0400
permissions: -rw-r--r--
description: added io/fast, moved obj/music -> aud/music
1 ;;; default.el --- default config -*- lexical-binding: t -*-
2 
3 ;;; Code:
4 ;;; Settings
5 (require 'util)
6 (put 'upcase-region 'disabled nil)
7 (put 'list-threads 'disabled nil)
8 (put 'list-timers 'disabled nil)
9 (setq show-paren-context-when-offscreen 'overlay)
10 (setopt
11  org-safe-remote-resources '("\\`https://cdn\\.compiler\\.company/org/clean\\.theme\\'")
12  ;; tabs = bad (unless in makefile..)
13  indent-tabs-mode nil
14  make-backup-files nil
15  auto-save-list-file-prefix (expand-file-name "auto-save/." user-emacs-directory)
16  tramp-auto-save-directory (expand-file-name "auto-save/tramp/" user-emacs-directory)
17  dired-free-space nil
18  mml-attach-file-at-the-end t
19  dired-mouse-drag-files t
20  confirm-kill-emacs nil
21  confirm-kill-processes nil
22  use-short-answers t
23  display-time-format "%Y-%m-%d %H:%M"
24  ring-bell-function 'ignore
25  completion-ignore-case t
26  ;; NOTE 2023-11-04: you need to add the following lines to ~/.gnupg/gpg-agent.conf:
27  ;; allow-emacs-pinentry
28  ;; allow-loopback-pinentry
29  epg-pinentry-mode 'loopback
30  shr-use-colors nil
31  shr-use-fonts nil
32  shr-max-image-proportion 0.6
33  shr-image-animate nil
34  shr-discard-aria-hidden t
35  bookmark-default-file (expand-file-name "bookmarks" user-emacs-directory)
36  set-mark-command-repeat-pop t
37  tempo-interactive t
38  emms-directory (expand-file-name "emms" user-emacs-directory)
39  gnus-cache-directory (expand-file-name "gnus" user-emacs-directory)
40  url-cache-directory (expand-file-name "url" user-emacs-directory)
41  tab-always-indent 'complete
42  shr-cookie-policy nil
43  ;; NOTE 2023-11-04: EXPERIMENTAL
44  ediff-floating-control-frame t
45  register-use-preview nil
46  shr-use-xwidgets-for-media t
47  which-key-mode t
48  view-read-only t)
49 
50 ;;; Treesitter
51 
52 ;;(add-to-list 'treesit-extra-load-path "/usr/local/lib/")
53 
54 ;; (let ((grammar-dir "/usr/local/share/tree-sitter/"))
55 ;; (when (file-exists-p grammar-dir)
56 ;; (setq treesit-extra-load-path
57 ;; (append
58 ;; (flatten
59 ;; (mapcar
60 ;; (lambda (f)
61 ;; (unless (or (string= "." f) (string= ".." f))
62 ;; (concat grammar-dir f)))
63 ;; (directory-files "/usr/local/share/tree-sitter")))
64 ;; treesit-extra-load-path))))
65 
66 ;;; Variables
67 (defvar user-emacs-lib-directory (expand-file-name (join-paths user-emacs-directory "lib")))
68 (defvar user-custom-file (expand-file-name (format "%s.el" user-login-name) user-emacs-directory))
69 (defvar user-home-directory (expand-file-name "~"))
70 (defvar user-lab-directory (expand-file-name "lab" user-home-directory))
71 (defvar user-stash-directory (expand-file-name ".stash" user-home-directory))
72 (defvar user-store-directory (expand-file-name ".store" user-home-directory))
73 (defvar user-mail-directory (expand-file-name "mail" user-home-directory))
74 (defvar user-org-stash-directory (expand-file-name "org" user-stash-directory))
75 (defvar default-theme 'leuven-dark)
76 (defvar company-source-directory (join-paths user-home-directory "comp"))
77 (defvar company-org-directory (join-paths company-source-directory "org"))
78 (defvar company-domain "compiler.company")
79 (defvar company-name "The Compiler Company, LLC")
80 (defvar company-vc-domain "vc.compiler.company")
81 (defvar company-home "the.compiler.company")
82 (defvar company-cdn-url "https://cdn.compiler.company")
83 
84 (add-to-load-path user-emacs-lib-directory)
85 
86 ;;; Theme
87 (defun load-default-theme (&optional theme)
88  (interactive)
89  (when theme (setq default-theme theme))
90  (load-theme default-theme))
91 
92 ;;; Packages
93 (with-eval-after-load 'package
94  (setq package-archives
95  '(("gnu" . "https://elpa.gnu.org/packages/")
96  ("nongnu" . "https://elpa.nongnu.org/nongnu/")
97  ("melpa" . "https://melpa.org/packages/")))
98  (setopt
99  use-package-always-ensure t
100  use-package-expand-minimally t)
101  (add-packages
102  ;; eglot-x ;; LSP extensions
103  org-web-tools ;; web parsing
104  citeproc ;; citations
105  htmlize ;; html export
106  ;; all-the-icons all-the-icons-dired all-the-icons-ibuffer ;; icons
107  nerd-icons nerd-icons-dired nerd-icons-ibuffer nerd-icons-corfu nerd-icons-completion
108  hide-mode-line) ;; ui
109  ;; bbdb
110  (package-install-selected-packages t))
111 
112 ;;; Env
113 (require 'exec-path-from-shell)
114 (exec-path-from-shell-copy-envs (list "SSH_AGENT_PID"
115  "SSH_AUTH_SOCK"
116  "PATH"
117  "CARGO_HOME"
118  "CC"
119  "LD"
120  "LD_LIBRARY_PATH"
121  "RUSTUP_HOME"
122  "QUICKLISP_HOME"
123  "DEV" "DEV_ID" "DEV_HOME"
124  "WORKER" "WORKER_ID" "WORKER_HOME"
125  "SBCL_HOME"
126  "STASH"
127  "STORE"
128  "LISP_HOME"))
129 
130 (add-to-list 'exec-path (expand-file-name "~/.cargo/bin/"))
131 (add-to-list 'exec-path (expand-file-name "~/.local/bin/"))
132 (add-to-list 'exec-path "/bin/")
133 (add-to-list 'exec-path "/usr/local/sbin/")
134 (add-to-list 'exec-path "/usr/local/bin/")
135 (add-to-list 'exec-path "/usr/local/share/lisp/bin/")
136 
137 ;;; Completions
138 (use-package cape :ensure t)
139 (use-package orderless
140  :ensure t
141  :init
142  (setq completion-styles '(orderless partial-completion basic)
143  completion-category-overrides '((file (styles basic partial-completion))
144  (eglot (styles orderless))
145  (eglot-capf (styles orderless)))))
146 
147 (use-package corfu
148  :ensure t
149  :config
150  (global-corfu-mode)
151  (corfu-popupinfo-mode)
152  (corfu-echo-mode)
153  (dolist (c (list (cons "SPC" " ")
154  (cons "." ".")
155  (cons "," ",")
156  (cons ":" ":")
157  (cons ")" ")")
158  (cons "}" "}")
159  (cons "]" "]")))
160  (define-key corfu-map (kbd (car c)) `(lambda ()
161  (interactive)
162  (corfu-insert)
163  (insert ,(cdr c)))))
164  ;; (add-to-list 'completion-at-point-functions #'cape-dabbrev t)
165  ;; (add-to-list 'completion-at-point-functions #'cape-abbrev t)
166  ;; (add-to-list 'completion-at-point-functions #'cape-file)
167  (defun corfu-move-to-minibuffer ()
168  (interactive)
169  (pcase completion-in-region--data
170  (`(,beg ,end ,table ,pred ,extras)
171  (let ((completion-extra-properties extras)
172  completion-cycle-threshold completion-cycling)
173  (consult-completion-in-region beg end table pred)))))
174  (keymap-set corfu-map "M-m" #'corfu-move-to-minibuffer)
175  (add-to-list 'corfu-continue-commands #'corfu-move-to-minibuffer)
176  (unless (display-graphic-p)
177  (use-package corfu-terminal :ensure t :config (corfu-terminal-mode 1))))
178 
179 (use-package kind-icon
180  :ensure t
181  :after corfu
182  ;:custom
183  ; (kind-icon-blend-background t)
184  ; (kind-icon-default-face 'corfu-default) ; only needed with blend-background
185  :config
186  (add-to-list 'corfu-margin-formatters #'kind-icon-margin-formatter))
187 
188 
189 (use-package vertico
190  :ensure t
191  :config (vertico-mode)
192  (keymap-set vertico-map "M-q" #'vertico-quick-insert)
193  (keymap-set vertico-map "C-q" #'vertico-quick-exit))
194 
195 (use-package marginalia :ensure t
196  :config (marginalia-mode))
197 (use-package embark
198  :ensure t)
199 (use-package embark-consult :ensure t)
200 (use-package consult :ensure t)
201 
202 ;;; Desktop
203 (setopt desktop-dirname (expand-file-name "sessions" user-emacs-directory))
204 
205 ;;; Multisession
206 (setq multisession-storage 'sqlite)
207 
208 ;;; Kill Ring
209 (kill-ring-deindent-mode)
210 
211 ;;; VC
212 ;; use rhg, fallback to hg. see hgrc
213 (if (file-exists-p "~/.local/bin/rhg")
214  (setq hg-binary "~/.local/bin/rhg"))
215 
216 ;;; Dired
217 ;;; Projects
218 (setopt project-list-file (expand-file-name "projects" user-emacs-directory)
219  project-mode-line t
220  project-file-history-behavior 'relativize)
221 
222 ;;; Tabs
223 (add-hook 'tab-bar-mode-hook #'tab-bar-history-mode)
224 
225 ;;; Lisp
226 (use-package company :ensure t)
227 
228 (defvar slime-toggle nil)
229 (defun slime-toggle ()
230  "toggle between lisp file and slime-repl"
231  (interactive)
232  (cond
233  ((eq major-mode 'slime-repl-mode)
234  (setq slime-toggle (pop-to-buffer (or slime-toggle (read-buffer "lisp buffer: ")))))
235  ((not (eq major-mode 'slime-repl-mode))
236  (if (slime-connected-p)
237  (progn
238  (setq slime-toggle (current-buffer))
239  (slime-switch-to-output-buffer))
240  (setq slime-toggle (current-buffer))
241  (slime)))))
242 
243 (use-package slime
244  :ensure t
245  :config
246  (require 'slime-company "slime-company")
247  (require 'slime-cape "slime-cape")
248  (require 'slime-repl-ansi-color "slime-repl-ansi-color")
249  (setq slime-contribs '(slime-fancy
250  slime-quicklisp
251  slime-hyperdoc
252  ;; slime-listener-hooks
253  ;; slime-enclosing-context
254  ;; slime-media
255  ;; slime-mrepl
256  ;; slime-company
257  slime-sbcl-exts
258  slime-cape ;; ext
259  slime-repl-ansi-color
260  ;; slime-cl-indent
261  ;; slime-snapshot
262  slime-sprof
263  slime-tramp
264  ;; slime-typeout-frame
265  slime-xref-browser
266  slime-repl-ansi-color
267  ;; slime-highlight-edits
268  slime-asdf))
269  (put 'make-instance 'common-lisp-indent-function 1)
270  (put 'reinitialize-instance 'common-lisp-indent-function 1)
271  (slime-setup slime-contribs)
272  ;; X11-only (mcclim requires clx)
273  (defun clouseau-inspect (string)
274  "Inspect a lisp value with Clouseau. make sure to load clouseau
275 with a custom core or in your init file before using this
276 function: '(ql:quickload :clouseau)'."
277  (interactive
278  (list (slime-read-from-minibuffer
279  "Inspect value (evaluated): "
280  (slime-sexp-at-point))))
281  (let ((inspector 'cl-user::*clouseau-inspector*))
282  (slime-eval-async
283  `(cl:progn
284  (cl:defvar ,inspector nil)
285  ;; (Re)start the inspector if necessary.
286  (cl:unless (cl:and (clim:application-frame-p ,inspector)
287  (clim-internals::frame-process ,inspector))
288  (cl:setf ,inspector (cl:nth-value 1 (clouseau:inspect nil :new-process t))))
289  ;; Tell the inspector to visualize the correct datum.
290  (cl:setf (clouseau:root-object ,inspector :run-hook-p t)
291  (cl:eval (cl:read-from-string ,string)))
292  ;; Return nothing.
293  (cl:values)))))
294 
295  (define-common-lisp-style "core" "Core Common Lisp Indentation Style"
296  (:inherit "sbcl")
297  (:indentation
298  (defpkg (as defpackage))
299  (define-package (as defpackage))))
300 
301  ;; lisp font-lock defaults: https://www.n16f.net/blog/custom-font-lock-configuration-in-emacs/
302  ;; (defface cl-character-face
303  ;; '((default :inherit font-lock-constant-face))
304  ;; "The face used to highlight Common Lisp character literals.")
305 
306  ;; (defface cl-standard-function-face
307  ;; '((default :inherit font-lock-keyword-face))
308  ;; "The face used to highlight standard Common Lisp function symbols.")
309 
310  ;; (defface cl-standard-value-face
311  ;; '((default :inherit font-lock-variable-name-face))
312  ;; "The face used to highlight standard Common Lisp value symbols.")
313 
314  ;; (defvar cl-font-lock-keywords
315  ;; (let* ((character-re (concat "#\\\\" lisp-mode-symbol-regexp "\\_>"))
316  ;; (function-re (concat "(" (regexp-opt cl-function-names t) "\\_>"))
317  ;; (value-re (regexp-opt cl-value-names 'symbols)))
318  ;; `((,character-re . 'cl-character-face)
319  ;; (,function-re
320  ;; (1 'cl-standard-function-face))
321  ;; (,value-re . 'cl-standard-value-face))))
322 
323  (setq common-lisp-style-default "core")
324  ;; (define-key slime-prefix-map (kbd "i") 'clouseau-inspect)
325  (setq slime-threads-update-interval 1)
326  ;; (add-hook 'slime-mode-hook 'slime-cape-maybe-enable)
327  ;; (add-hook 'slime-repl-mode-hook 'slime-cape-maybe-enable)
328  )
329 
330 (use-package lisp-mode
331  :ensure nil
332  :after slime
333  :custom
334  inferior-lisp-program "sbcl --dynamic-space-size=8G"
335  scheme-program-name "gsi"
336  guile-program "guile"
337  cmulisp-program "lisp"
338  scsh-program "scsh")
339 
340 ;;; Eglot
341 ;; (with-eval-after-load 'eglot
342 ;; (unless (package-installed-p 'eglot-x)
343 ;; (package-vc-install '(eglot-x :url "https://vc.compiler.company/packy/eglot-x")))
344 ;; (require 'eglot-x)
345 ;; (with-eval-after-load 'eglot-x
346 ;; (add-to-list 'eglot-server-programs
347 ;; '((rust-ts-mode rust-mode) .
348 ;; ("rust-analyzer" :initializationOptions (:check (:command "clippy")))))
349 ;; (eglot-x-setup)))
350 
351 ;;; Rust
352 (add-hook 'rust-mode-hook 'eglot-ensure)
353 
354 (setq rust-rustfmt-switches nil
355  rust-indent-offset 2)
356 
357 ;;; Python
358 (setq python-indent-offset 2)
359 (add-hook 'python-mode-hook 'eglot-ensure)
360 
361 ;;; Javascript
362 (setq js-indent-level 2)
363 
364 ;;; Bash
365 (setq sh-basic-offset 2)
366 
367 ;;; Graphviz
368 ;; (use-package graphviz-dot-mode
369 ;; :ensure t
370 ;; :config
371 ;; (setq graphviz-dot-indent-width 2))
372 
373 ;;; Comments
374 (defcustom prog-comment-keywords
375  '("TODO" "REVIEW" "FIX" "HACK" "RESEARCH")
376  "List of strings with comment keywords."
377  :group 'default
378  :type 'list)
379 
380 (defcustom prog-comment-timestamp-format-concise "%F"
381  "Specifier for date in `prog-comment-timestamp-keyword'.
382 Refer to the doc string of `format-time-string' for the available
383 options."
384  :group 'default
385  :type 'string)
386 
387 (defcustom prog-comment-timestamp-format-verbose "%F %T %z"
388  "Like `prog-comment-timestamp-format-concise', but longer."
389  :group 'default
390  :type 'string)
391 
392 ;;;###autoload
393 (defun prog-comment-dwim (arg)
394  "Flexible, do-what-I-mean commenting.
395 
396 If region is active and ARG is either a numeric argument greater
397 than one or a universal prefix (\\[universal-argument]), then
398 apply `comment-kill' on all comments in the region.
399 
400 If the region is active and no ARG is supplied, or is equal to a
401 numeric prefix of 1, then toggle the comment status of the region.
402 
403 Else toggle the comment status of the line at point. With a
404 numeric prefix ARG, do so for ARGth lines (negative prefix
405 operates on the lines before point)."
406  (interactive "p")
407  (cond
408  ((and (> arg 1) (use-region-p))
409  (let* ((beg (region-beginning))
410  (end (region-end))
411  (num (count-lines beg end)))
412  (save-excursion
413  (goto-char beg)
414  (comment-kill num))))
415  ((use-region-p)
416  (comment-or-uncomment-region (region-beginning) (region-end)))
417  (t
418  (save-excursion (comment-line (or arg 1))))))
419 
420 (defvar prog-comment--keyword-hist '()
421  "Input history of selected comment keywords.")
422 
423 (defun prog-comment--keyword-prompt (keywords)
424  "Prompt for candidate among KEYWORDS."
425  (let ((def (car prog-comment--keyword-hist)))
426  (completing-read
427  (format "Select keyword [%s]: " def)
428  keywords nil nil nil 'prog-comment--keyword-hist def)))
429 
430 
431 ;;;###autoload
432 (defun prog-comment-timestamp-keyword (keyword &optional verbose)
433  "Add timestamped comment with KEYWORD.
434 
435 When called interactively, the list of possible keywords is that
436 of `prog-comment-keywords', though it is possible to
437 input arbitrary text.
438 
439 If point is at the beginning of the line or if line is empty (no
440 characters at all or just indentation), the comment is started
441 there in accordance with `comment-style'. Any existing text
442 after the point will be pushed to a new line and will not be
443 turned into a comment.
444 
445 If point is anywhere else on the line, the comment is indented
446 with `comment-indent'.
447 
448 The comment is always formatted as 'DELIMITER KEYWORD DATE:',
449 with the date format being controlled by the variable
450 `prog-comment-timestamp-format-concise'.
451 
452 With optional VERBOSE argument (such as a prefix argument
453 `\\[universal-argument]'), use an alternative date format, as
454 specified by `prog-comment-timestamp-format-verbose'."
455  (interactive
456  (list
457  (prog-comment--keyword-prompt prog-comment-keywords)
458  current-prefix-arg))
459  (let* ((date (if verbose
460  comment-timestamp-format-verbose
461  prog-comment-timestamp-format-concise))
462  (string (format "%s %s: " keyword (format-time-string date)))
463  (beg (point)))
464  (cond
465  ((or (eq beg (pos-bol))
466  (default-line-regexp-p 'empty))
467  (let* ((maybe-newline (unless (default-line-regexp-p 'empty 1) "\n")))
468  ;; NOTE 2021-07-24: we use this `insert' instead of
469  ;; `comment-region' because of a yet-to-be-determined bug that
470  ;; traps `undo' to the two states between the insertion of the
471  ;; string and its transformation into a comment.
472  (insert
473  (concat comment-start
474  ;; NOTE 2021-07-24: See function `comment-add' for
475  ;; why we need this.
476  (make-string
477  (comment-add nil)
478  (string-to-char comment-start))
479  comment-padding
480  string
481  comment-end))
482  (indent-region beg (point))
483  (when maybe-newline
484  (save-excursion (insert maybe-newline)))))
485  (t
486  (comment-indent t)
487  (insert (concat " " string))))))
488 
489 (setq hexl-bits 8)
490 (setq tab-width 4)
491 
492 ;;; Keyboard Macros
493 (defun toggle-macro-recording ()
494  (interactive)
495  (if defining-kbd-macro
496  (end-kbd-macro)
497  (start-kbd-macro nil)))
498 
499 (defun play-macro-if-not-playing ()
500  (interactive)
501  (if defining-kbd-macro
502  (end-kbd-macro)
503  (call-last-kbd-macro)))
504 
505 ;;; Registers
506 ;; - additional register vtypes: buffer
507 (defun decrement-register (number register)
508  "Subtract NUMBER from the contents of register REGISTER.
509 Interactively, NUMBER is the prefix arg."
510  (interactive "p\ncDecrement register: ")
511  (increment-register (- number) register))
512 
513 (defun copy-register (a b)
514  "Copy register A to B."
515  (interactive
516  (list (register-read-with-preview "From register: ")
517  (register-read-with-preview "To register: ")))
518  (set-register b (get-register a)))
519 
520 (defun buffer-to-register (register &optional delete)
521  "Put current buffer in register - this would also work for
522  just buffers, as switch-to-buffer can use both, but it
523  facilitates for easier saving/restoring of registers."
524  (interactive "cPut current buffername in register: \nP.")
525  (set-register register (cons 'buffer (buffer-name (current-buffer)))))
526 
527 (defun file-to-register (register &optional delete)
528  "This is better than put-buffer-in-register for file-buffers, because a closed
529  file can be opened again, but does not work for no-file-buffers."
530  (interactive "cPut the filename of current buffer in register: \nP")
531  (set-register register (cons 'file (buffer-file-name (current-buffer)))))
532 
533 (defun file-query-to-register (register &optional delete)
534  (interactive
535  (list
536  (register-read-with-preview "File query to register: ")))
537  (set-register register (list 'file-query (buffer-file-name (current-buffer)) (point))))
538 
539 ;; additional register-val handlers
540 ;; (cl-defmethod register-val-jump-to :around ((val cons) delete)
541 ;; (cond
542 ;; (t (cl-call-next-method val delete))))
543 
544 ;;; Outlines
545 (defun outline-hook (&optional rx)
546  "Enable `outline-minor-mode' and set `outline-regexp'."
547  (when rx (setq-local outline-regexp rx))
548  (outline-minor-mode 1))
549 
550 (setq outline-minor-mode-use-buttons nil)
551 
552 (defun add-outline-hook (mode &optional rx)
553  (let ((sym (symb mode "-hook")))
554  (add-hook sym (lambda () (outline-hook rx)))))
555 
556 (defmacro outline-hooks (&rest pairs)
557  `(mapc (lambda (x) (add-outline-hook (car x) (cadr x))) ',pairs))
558 
559 (outline-hooks (asm-mode ";;;+")
560  (nasm-mode ";;;+")
561  (rust-mode "\\(//!\\|////+\\)")
562  (sh-mode "###+")
563  (sh-script-mode "###+")
564  (makefile-mode "###+")
565  (conf-mode "###+")
566  (common-lisp-mode)
567  (emacs-lisp-mode)
568  (lisp-data-mode)
569  (org-mode)
570  (css-mode)
571  (html-mode)
572  (skel-mode))
573 
574 ;;; Scratch
575 (defcustom default-scratch-buffer-mode 'lisp-interaction-mode
576  "Default major mode for new scratch buffers"
577  :group 'default
578  :type 'symbol)
579 
580 ;; Adapted from the `scratch.el' package by Ian Eure.
581 (defun default-scratch-list-modes ()
582  "List known major modes."
583  (cl-loop for sym the symbols of obarray
584  for name = (symbol-name sym)
585  when (and (functionp sym)
586  (not (member sym minor-mode-list))
587  (string-match "-mode$" name)
588  (not (string-match "--" name)))
589  collect name))
590 
591 (defun default-scratch-buffer-setup (region &optional mode)
592  "Add contents to `scratch' buffer and name it accordingly.
593 
594 REGION is added to the contents to the new buffer.
595 
596 Use the current buffer's major mode by default. With optional
597 MODE use that major mode instead."
598  (let* ((major (or mode major-mode))
599  (string (format "Scratch buffer for: %s\n\n" major))
600  (text (concat string region))
601  (buf (format "*Scratch for %s*" major)))
602  (with-current-buffer (get-buffer-create buf)
603  (funcall major)
604  (save-excursion
605  (insert text)
606  (goto-char (point-min))
607  (comment-region (pos-bol) (pos-eol)))
608  (vertical-motion 2))
609  (pop-to-buffer buf)))
610 
611 ;;;###autoload
612 (defun default-scratch-buffer (&optional arg)
613  "Produce a bespoke scratch buffer matching current major mode.
614 
615 With optional ARG as a prefix argument (\\[universal-argument]),
616 use `default-scratch-buffer-mode'.
617 
618 With ARG as a double prefix argument, prompt for a major mode
619 with completion.
620 
621 If region is active, copy its contents to the new scratch
622 buffer."
623  (interactive "P")
624  (let* ((default-mode default-scratch-buffer-mode)
625  (modes (default-scratch-list-modes))
626  (region (with-current-buffer (current-buffer)
627  (if (region-active-p)
628  (buffer-substring-no-properties
629  (region-beginning)
630  (region-end))
631  "")))
632  (m))
633  (pcase (prefix-numeric-value arg)
634  (16 (progn
635  (setq m (intern (completing-read "Select major mode: " modes nil t)))
636  (default-scratch-buffer-setup region m)))
637  (4 (default-scratch-buffer-setup region default-mode))
638  (_ (default-scratch-buffer-setup region)))))
639 
640 ;;;###autoload
641 (defun scratch-new ()
642  "create a new scratch buffer. (could be *scratch* - *scratchN*)"
643  (interactive)
644  (let ((n 0)
645  bufname)
646  (while (progn
647  (setq bufname
648  (concat "*scratch"
649  (if (= n 0) "" (int-to-string n))
650  "*"))
651  (setq n (1+ n))
652  (get-buffer bufname)))
653  (switch-to-buffer (get-buffer-create bufname))
654  (insert initial-scratch-message)
655  (lisp-interaction-mode)))
656 
657 ;;; Shell
658 (defun set-no-process-query-on-exit ()
659  (let ((proc (get-buffer-process (current-buffer))))
660  (when (processp proc)
661  (set-process-query-on-exit-flag proc nil))))
662 
663 (add-hook 'shell-mode-hook 'set-no-process-query-on-exit)
664 (add-hook 'term-exec-hook 'set-no-process-query-on-exit)
665 
666 ;;; Eshell
667 (defun eshell-new()
668  "Open a new instance of eshell."
669  (interactive)
670  (eshell 'Z))
671 
672 (setq eshell-highlight-prompt t
673  eshell-hist-ignoredups t
674  eshell-save-history-on-exit t
675  eshell-prefer-lisp-functions nil
676  eshell-destroy-buffer-when-process-dies t)
677 
678 (add-hook 'eshell-mode-hook
679  (lambda ()
680  (eshell/alias "d" "dired $1")
681  (eshell/alias "ff" "find-file $1")
682  (eshell/alias "hgfe" "hg-fast-export.sh")))
683 
684 (defun eshell/clear ()
685  "Clear the eshell buffer."
686  (let ((inhibit-read-only t))
687  (erase-buffer)
688  (eshell-send-input)))
689 
690 (defun eshell-quit-or-delete-char (arg)
691  (interactive "p")
692  (if (and (eolp) (looking-back eshell-prompt-regexp))
693  (progn
694  (eshell-life-is-too-much) ; Why not? (eshell/exit)
695  (ignore-errors
696  (delete-window)))
697  (delete-forward-char arg)))
698 
699 (add-hook 'eshell-mode-hook
700  (lambda ()
701  (bind-keys :map eshell-mode-map
702  ("C-d" . eshell-quit-or-delete-char))))
703 
704 (defun eshell-next-prompt (n)
705  "Move to end of Nth next prompt in the buffer. See `eshell-prompt-regexp'."
706  (interactive "p")
707  (re-search-forward eshell-prompt-regexp nil t n)
708  (when eshell-highlight-prompt
709  (while (not (get-text-property (line-beginning-position) 'read-only) )
710  (re-search-forward eshell-prompt-regexp nil t n)))
711  (eshell-skip-prompt))
712 
713 (defun eshell-previous-prompt (n)
714  "Move to end of Nth previous prompt in the buffer. See `eshell-prompt-regexp'."
715  (interactive "p")
716  (backward-char)
717  (eshell-next-prompt (- n)))
718 
719 (defun eshell-insert-history ()
720  "Displays the eshell history to select and insert back into your eshell."
721  (interactive)
722  (insert (ido-completing-read "Eshell history: "
723  (delete-dups
724  (ring-elements eshell-history-ring)))))
725 
726 ;;; Eww
727 (setopt
728  browse-url-browser-function 'eww
729  eww-auto-rename-buffer 'title
730  eww-search-prefix "https://google.com/search?q=")
731 
732 ;; ref: https://github.com/oantolin/emacs-config/blob/master/my-lisp/shr-heading.el
733 (defun shr-heading-next (&optional arg)
734  "Move forward by ARG headings (any h1-h4).
735 If ARG is negative move backwards, ARG defaults to 1."
736  (interactive "p")
737  (unless arg (setq arg 1))
738  (catch 'return
739  (dotimes (_ (abs arg))
740  (when (> arg 0) (end-of-line))
741  (if-let ((match
742  (funcall (if (> arg 0)
743  #'text-property-search-forward
744  #'text-property-search-backward)
745  'face '(shr-h1 shr-h2 shr-h3 shr-h4)
746  (lambda (tags face)
747  (cl-loop for x in (if (consp face) face (list face))
748  thereis (memq x tags))))))
749  (goto-char
750  (if (> arg 0) (prop-match-beginning match) (prop-match-end match)))
751  (throw 'return nil))
752  (when (< arg 0) (beginning-of-line)))
753  (beginning-of-line)
754  (point)))
755 
756 (defun shr-heading-previous (&optional arg)
757  "Move backward by ARG headings (any h1-h4).
758 If ARG is negative move forwards instead, ARG defaults to 1."
759  (interactive "p")
760  (shr-heading-next (- (or arg 1))))
761 
762 (defun shr-heading--line-at-point ()
763  "Return the current line."
764  (buffer-substring (line-beginning-position) (line-end-position)))
765 
766 (defun shr-heading-setup-imenu ()
767  "Setup imenu for h1-h4 headings in eww buffer.
768 Add this function to appropriate major mode hooks such as
769 `eww-mode-hook' or `elfeed-show-mode-hook'."
770  (setq-local
771  imenu-prev-index-position-function #'shr-heading-previous
772  imenu-extract-index-name-function #'shr-heading--line-at-point))
773 
774 (defvar shr-heading-map
775  (let ((map (make-sparse-keymap)))
776  (define-key map "n" #'shr-heading-next)
777  (define-key map "\C-n" #'shr-heading-next)
778  (define-key map "p" #'shr-heading-previous)
779  (define-key map "\C-p" #'shr-heading-previous)
780  map))
781 
782 (add-hook 'eww-mode-hook 'shr-heading-setup-imenu)
783 (add-hook 'eww-mode-hook (lambda () (define-key eww-mode-map "i" shr-heading-map)))
784 
785 ;;; Tramp
786 (setopt tramp-default-method "ssh"
787  tramp-default-user user-login-name
788  tramp-default-host "localhost")
789 
790 ;;; Imenu
791 ;; (use-package imenu-list :ensure t)
792 
793 ;;; Org
794 (require 'org)
795 (require 'org-agenda)
796 (require 'org-id)
797 (require 'org-protocol)
798 
799 (setq org-html-htmlize-output-type 'css
800  org-html-head-include-default-style nil
801  ;; cc default
802  org-ascii-text-width 80)
803 
804 (org-crypt-use-before-save-magic)
805 
806 (setq org-structure-template-alist
807  '(("s" . "src")
808  ("e" . "src emacs-lisp")
809  ("x" . "src shell")
810  ("l" . "src lisp")
811  ("h" . "export html")
812  ("p" . "src python")
813  ("r" . "src rust")
814  ("E" . "example")
815  ("q" . "quote")
816  ("c" . "center")
817  ("C" . "comment")
818  ("v" . "verse")))
819 
820 ;; org-sbx [[https://list.orgmode.org/d429d29b-42fa-7d7b-6f3a-9fe692fd6dc7@grinta.net/T/]]
821 (defun %org-sbx (name header args)
822  (let* ((args (mapconcat
823  (lambda (x)
824  (format "%s=%S" (symbol-name (car x)) (cadr x)))
825  args ", "))
826  (ctx (list 'babel-call (list :call name
827  :name name
828  :inside-header header
829  :arguments args
830  :end-header ":results silent")))
831  (info (org-babel-lob-get-info ctx)))
832  (when info (org-babel-execute-src-block nil info))))
833 
834 (defmacro org-sbx (name &rest args)
835  (let* ((header (if (stringp (car args)) (car args) nil))
836  (args (if (stringp (car args)) (cdr args) args)))
837  (unless (stringp name)
838  (setq name (symbol-name name)))
839  (let ((result (%org-sbx name header args)))
840  (org-trim (if (stringp result) result (format "%S" result))))))
841 
842 (defun org-babel-execute-region (beg end &optional arg)
843  (interactive "r")
844  (narrow-to-region beg end)
845  (org-babel-execute-buffer arg)
846  (widen))
847 
848 (defun org-schedule-effort ()
849  (interactive)
850  (save-excursion
851  (org-back-to-heading t)
852  (let* ((element (org-element-at-point))
853  (effort (org-element-property :EFFORT element))
854  (scheduled (org-element-property :scheduled element))
855  (ts-year-start (org-element-property :year-start scheduled))
856  (ts-month-start (org-element-property :month-start scheduled))
857  (ts-day-start (org-element-property :day-start scheduled))
858  (ts-hour-start (org-element-property :hour-start scheduled))
859  (ts-minute-start (org-element-property :minute-start scheduled)) )
860  (org-schedule nil (concat
861  (format "%s" ts-year-start)
862  "-"
863  (if (< ts-month-start 10)
864  (concat "0" (format "%s" ts-month-start))
865  (format "%s" ts-month-start))
866  "-"
867  (if (< ts-day-start 10)
868  (concat "0" (format "%s" ts-day-start))
869  (format "%s" ts-day-start))
870  " "
871  (if (< ts-hour-start 10)
872  (concat "0" (format "%s" ts-hour-start))
873  (format "%s" ts-hour-start))
874  ":"
875  (if (< ts-minute-start 10)
876  (concat "0" (format "%s" ts-minute-start))
877  (format "%s" ts-minute-start))
878  "+"
879  effort)) )))
880 
881 (setopt org-preview-latex-image-directory "~/.emacs.d/.cache/ltximg"
882  org-latex-image-default-width "8cm"
883  org-refile-use-cache t
884  org-refile-allow-creating-parent-nodes 'confirm
885 
886  org-refile-targets '((nil :maxlevel . 3)
887  (org-agenda-files :maxlevel . 3))
888  ;; org-agenda-files (list "inbox.org")
889  org-agenda-include-diary t
890  org-agenda-include-inactive-timestamps t
891  org-confirm-babel-evaluate nil
892  org-src-fontify-natively t
893  org-src-tabs-act-natively t
894  org-footnote-section nil
895  org-log-into-drawer t
896  org-log-refile 'time
897  org-log-redeadline 'time
898  org-log-states-order-reversed nil
899  org-clock-persist 'history)
900 
901 (add-hook 'after-init-hook #'org-clock-persistence-insinuate)
902 
903 ;; archive
904 (defun extract-org-directory-titles-as-list (&optional dir)
905  (interactive "D")
906  (print
907  (delete nil
908  (let ((case-fold-search t))
909  (mapcar (lambda (f)
910  (when (string-match "org$" f)
911  (with-temp-buffer
912  (insert-file-contents-literally
913  (concat (file-name-as-directory dir) f))
914  (while (and (not (looking-at-p "#\\+TITLE:"))
915  (not (eobp)))
916  (forward-line))
917  (when (not (eobp))
918  (cons f (substring (thing-at-point 'line) 9 -1))))))
919  (directory-files dir))))))
920 
921 (defun insert-directory-org-file-titles (&optional dir)
922  (interactive "D")
923  (let ((files-titles (extract-org-directory-titles-as-list dir)))
924  (dolist (ft files-titles)
925  (insert (concat "[[file:" (car ft)"][" (cdr ft) "]]\n")))))
926 
927 (defun insert-directory-org-files (&optional dir)
928  (interactive "D")
929  (let ((files (directory-files dir)))
930  (dolist (f files)
931  (insert (concat "[[file:" f "][" (file-name-base f) "]]\n")))))
932 
933 (defun include-directory-org-files (&optional dir)
934  (interactive "D")
935  (let ((files (directory-files dir)))
936  (dolist (f files)
937  (insert (concat "#+INCLUDE: " f "\n")))))
938 
939 (defun org-todo-at-date (date)
940  "create a todo entry for a given date."
941  (interactive (list (org-time-string-to-time (org-read-date))))
942  (cl-flet ((org-current-effective-time (&rest r) date)
943  (org-today (&rest r) (time-to-days date)))
944  (cond ((eq major-mode 'org-mode) (org-todo))
945  ((eq major-mode 'org-agenda-mode) (org-agenda-todo)))))
946 
947 (defun org-agenda-show-week-all (&optional arg ) (interactive "P") (org-agenda arg "n"))
948 
949 (defun org-ask-location ()
950  "prompt for a location."
951  (let* ((org-refile-targets '((nil :maxlevel . 9)))
952  (hd (condition-case nil
953  (car (org-refile-get-location))
954  (error (car org-refile-history)))))
955  (goto-char (point-min))
956  (outline-next-heading)
957  (if (re-search-forward
958  (format org-complex-heading-regexp-format (regexp-quote hd))
959  nil t)
960  (goto-char (line-beginning-position))
961  (goto-char (point-max))
962  (or (bolp) (insert "\n"))
963  (insert "* " hd "\n")))
964  (end-of-line))
965 
966 (defun org-capture-fileref-snippet (f type headers func-name)
967  (let* ((code-snippet
968  (buffer-substring-no-properties (mark) (- (point) 1)))
969  (file-name (buffer-file-name))
970  (file-base (file-name-nondirectory file-name))
971  (line-number (line-number-at-pos (region-beginning)))
972  (initial-txt (if (null func-name)
973  (format "From [[file:%s::%s][%s]]:"
974  file-name line-number file-base)
975  (format "From ~%s~ (in [[file:%s::%s][%s]]):"
976  func-name file-name line-number
977  file-base))))
978  (format "
979  %s
980  #+BEGIN_%s %s
981  %s
982  #+END_%s" initial-txt type headers code-snippet type)))
983 
984 (defun org-capture-clip-snippet (f)
985  "Given a file, F, this captures the currently selected text
986  within an Org EXAMPLE block and a backlink to the file."
987  (with-current-buffer (find-buffer-visiting f)
988  (org-capture-fileref-snippet f "EXAMPLE" "" nil)))
989 
990 (defun org-capture-code-snippet (f)
991  "Given a file, F, this captures the currently selected text
992  within an Org SRC block with a language based on the current mode
993  and a backlink to the function and the file."
994  (with-current-buffer (find-buffer-visiting f)
995  (let ((org-src-mode (replace-regexp-in-string "-mode" "" (format "%s" major-mode)))
996  (func-name (which-function)))
997  (org-capture-fileref-snippet f "SRC" org-src-mode func-name))))
998 
999 (defun region-to-clocked-task (start end)
1000  "Copies the selected text to the currently clocked in org-mode task."
1001  (interactive "r")
1002  (org-capture-string (buffer-substring-no-properties start end) "3"))
1003 
1004 (setq org-global-properties
1005  '(quote (("EFFORT_ALL" . "0:15 0:30 0:45 1:00 2:00 3:00 4:00 5:00 6:00 0:00")
1006  ("STYLE_ALL" . "habit"))))
1007 
1008 (defun org-mode-ask-effort ()
1009  "Ask for an effort estimate when clocking in."
1010  (unless (org-entry-get (point) "Effort")
1011  (let ((effort
1012  (completing-read
1013  "Effort: "
1014  (org-entry-get-multivalued-property (point) "Effort"))))
1015  (unless (equal effort "")
1016  (org-set-property "Effort" effort)))))
1017 
1018 (add-hook 'org-clock-in-prepare-hook
1019  'org-mode-ask-effort)
1020 
1021 ;;;###autoload
1022 (defun org-adjust-tags-column-reset-tags ()
1023  "In org-mode buffers it will reset tag position according to
1024 `org-tags-column'."
1025  (when (and
1026  (not (string= (buffer-name) "*Remember*"))
1027  (eql major-mode 'org-mode))
1028  (let ((b-m-p (buffer-modified-p)))
1029  (condition-case nil
1030  (save-excursion
1031  (goto-char (point-min))
1032  (command-execute 'outline-next-visible-heading)
1033  ;; disable (message) that org-set-tags generates
1034  (cl-flet ((message (&rest ignored) nil))
1035  (org-set-tags 1 t))
1036  (set-buffer-modified-p b-m-p))
1037  (error nil)))))
1038 
1039 ;; TODO 2024-08-05: infer logbook column-titles/props
1040 (defun column-display-value-transformer (column-title value)
1041  "Modifies the value to display in column view."
1042  (let ((title (upcase column-title)))
1043  (when (and (member title '("UPDATED" "NOTE")))
1044  (org-back-to-heading)
1045  (re-search-forward
1046  "Note taken on \\[\\(.*\\)\\] \\\\\\\\\\\n +\\(.*\\) *$"
1047  (org-entry-end-position) t))
1048  (if (equal column-title "UPDATED")
1049  (match-string-no-properties 1)
1050  (match-string-no-properties 2))))
1051 
1052 (setq org-columns-modify-value-for-display-function
1053  #'column-display-value-transformer)
1054 
1055 ;;;###autoload
1056 (defun org-align-all-tables ()
1057  "align all tables in current buffer"
1058  (interactive)
1059  (org-table-map-tables 'org-table-align 'quietly))
1060 
1061 (defun org-remove-redundant-tags ()
1062  "Remove redundant tags of headlines in current buffer.
1063 
1064 A tag is considered redundant if it is local to a headline and
1065 inherited by a parent headline."
1066  (interactive)
1067  (when (eq major-mode 'org-mode)
1068  (save-excursion
1069  (org-map-entries
1070  (lambda ()
1071  (let ((alltags (split-string (or (org-entry-get (point) "ALLTAGS") "") ":"))
1072  local inherited tag)
1073  (dolist (tag alltags)
1074  (if (get-text-property 0 'inherited tag)
1075  (push tag inherited) (push tag local)))
1076  (dolist (tag local)
1077  (if (member tag inherited) (org-toggle-tag tag 'off)))))
1078  t nil))))
1079 
1080 ;;;; Agenda
1081 (cl-pushnew '("i" "Work in progress tasks" ((todo "WIP") (agenda))) org-agenda-custom-commands)
1082 
1083 (defvar org-agenda-overriding-header)
1084 (defvar org-agenda-sorting-strategy)
1085 (defvar org-agenda-restrict)
1086 (defvar org-agenda-restrict-begin)
1087 (defvar org-agenda-restrict-end)
1088 
1089 ;;;###autoload
1090 (defun org-agenda-reschedule-to-today ()
1091  (interactive)
1092  (cl-flet ((org-read-date (&rest rest) (current-time)))
1093  (call-interactively 'org-agenda-schedule)))
1094 
1095 ;; Patch org-mode to use vertical splitting
1096 (defadvice org-prepare-agenda (after org-fix-split)
1097  (toggle-window-split))
1098 (ad-activate 'org-prepare-agenda)
1099 
1100 (add-hook 'org-agenda-mode-hook (lambda () (hl-line-mode 1)))
1101 
1102 (defun org-agenda-log-mode-colorize-block ()
1103  "Set different line spacing based on clock time duration."
1104  (save-excursion
1105  (let* ((colors (cl-case (alist-get 'background-mode (frame-parameters))
1106  (light
1107  (list "#F6B1C3" "#FFFF9D" "#BEEB9F" "#ADD5F7"))
1108  (dark
1109  (list "#aa557f" "DarkGreen" "DarkSlateGray" "DarkSlateBlue"))))
1110  pos
1111  duration)
1112  (nconc colors colors)
1113  (goto-char (point-min))
1114  (while (setq pos (next-single-property-change (point) 'duration))
1115  (goto-char pos)
1116  (when (and (not (equal pos (pos-bol)))
1117  (setq duration (org-get-at-bol 'duration)))
1118  ;; larger duration bar height
1119  (let ((line-height (if (< duration 15) 1.0 (+ 0.5 (/ duration 30))))
1120  (ov (make-overlay (pos-bol) (1+ (pos-eol)))))
1121  (overlay-put ov 'face `(:background ,(car colors) :foreground "black"))
1122  (setq colors (cdr colors))
1123  (overlay-put ov 'line-height line-height)
1124  (overlay-put ov 'line-spacing (1- line-height))))))))
1125 
1126 (add-hook 'org-agenda-finalize-hook #'org-agenda-log-mode-colorize-block)
1127 
1128 ;;;###autoload
1129 (defun org-agenda-current-subtree-or-region (only-todos)
1130  "Display an agenda view for the current subtree or region.
1131  With prefix, display only TODO-keyword items."
1132  (interactive "P")
1133  (let ((starting-point (point))
1134  header)
1135  (with-current-buffer (or (buffer-base-buffer (current-buffer))
1136  (current-buffer))
1137  (if (use-region-p)
1138  (progn
1139  (setq header "Region")
1140  (put 'org-agenda-files 'org-restrict (list (buffer-file-name (current-buffer))))
1141  (setq org-agenda-restrict (current-buffer))
1142  (move-marker org-agenda-restrict-begin (region-beginning))
1143  (move-marker org-agenda-restrict-end
1144  (save-excursion
1145  ;; If point is at beginning of line, include
1146  ;; heading on that line by moving forward 1.
1147  (goto-char (1+ (region-end)))
1148  (org-end-of-subtree))))
1149  ;; No region; restrict to subtree.
1150  (save-excursion
1151  (save-restriction
1152  ;; In case the command was called from an indirect buffer, set point
1153  ;; in the base buffer to the same position while setting restriction.
1154  (widen)
1155  (goto-char starting-point)
1156  (setq header "Subtree")
1157  (org-agenda-set-restriction-lock))))
1158  ;; NOTE: Unlike other agenda commands, binding `org-agenda-sorting-strategy'
1159  ;; around `org-search-view' seems to have no effect.
1160  (let ((org-agenda-sorting-strategy '(priority-down timestamp-up))
1161  (org-agenda-overriding-header header))
1162  (org-search-view (if only-todos t nil) "*"))
1163  (org-agenda-remove-restriction-lock t)
1164  (message nil))))
1165 
1166 (defun org-export-translate-to-lang (term-translations &optional lang)
1167  "Adds desired translations to `org-export-dictionary'.
1168  TERM-TRANSLATIONS is alist consisted of term you want to translate
1169  and its corresponding translation, first as :default then as :html and
1170  :utf-8. LANG is language you want to translate to."
1171  (dolist (term-translation term-translations)
1172  (let* ((term (car term-translation))
1173  (translation-default (nth 1 term-translation))
1174  (translation-html (nth 2 term-translation))
1175  (translation-utf-8 (nth 3 term-translation))
1176  (term-list (assoc term org-export-dictionary))
1177  (term-langs (cdr term-list)))
1178  (setcdr term-list (append term-langs
1179  (list
1180  (list lang
1181  :default translation-default
1182  :html translation-html
1183  :utf-8 translation-utf-8)))))))
1184 
1185 ;;; Dictionary
1186 (setq dictionary-server "compiler.company"
1187  switch-to-buffer-obey-display-actions t)
1188 
1189 ;;; Ispell
1190 ;; requires aspell and a hunspell dictionary (hunspell-en_us)
1191 (setq-default ispell-program-name "aspell")
1192 (add-hook 'mail-send-hook #'ispell-message)
1193 
1194 ;;; Skel
1195 (require 'sk)
1196 (require 'skt)
1197 
1198 (provide 'default)
1199 ;; default.el ends here