changelog shortlog graph tags branches changeset files revisions annotate raw help

Mercurial > core / emacs/default.el

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