changelog shortlog graph tags branches changeset files revisions annotate raw help

Mercurial > core / emacs/default.el

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