changelog shortlog graph tags branches changeset files revisions annotate raw help

Mercurial > core / emacs/default.el

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