changelog shortlog graph tags branches changeset files revisions annotate raw help

Mercurial > core / emacs/default.el

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