changelog shortlog graph tags branches changeset files revisions annotate raw help

Mercurial > core / emacs/default.el

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