changelog shortlog graph tags branches changeset files revisions annotate raw help

Mercurial > core / emacs/default.el

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