changelog shortlog graph tags branches changeset files revisions annotate raw help

Mercurial > core / emacs/default.el

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