;; -*- lexical-binding: t; eval: (outline-minor-mode); outline-regexp: "^(" -*- (setq use-package-expand-minimally t) (use-package use-package :demand t :config (setq use-package-always-defer t) (unless (eq (car use-package-keywords) :if) (push :if use-package-keywords))) (use-package cl-lib :demand t) (use-package use-package-ensure :config (define-advice use-package-ensure-elpa (:after (name args _state &optional _no-refresh) select-package) (dolist (ensure args) (let ((package (or (and (eq ensure t) (use-package-as-symbol name)) ensure))) (when (consp package) (setq package (car package))) (when package (cl-pushnew package package-selected-packages)))))) (use-package package :demand t :config (setq package-archives '(("gnu" . "https://elpa.gnu.org/packages/") ("nongnu" . "https://elpa.nongnu.org/nongnu/") ("melpa" . "https://melpa.org/packages/") ("melpa-stable" . "https://stable.melpa.org/packages/")) package-archive-priorities '(("gnu" . 10) ("nongnu" . 9) ("melpa-stable" . 5) ("melpa" . 1)) package-menu-hide-low-priority t) (when (file-directory-p "/run/current-system") (cl-pushnew "/run/current-system/sw/share/emacs/site-lisp/elpa" package-directory-list :test #'string=))) (use-package anaphora :demand t :ensure t) (progn ;; groups (defvar group--enabled-groups '()) (defvar group--all-groups nil) (defun enable-group (group) (push group group--enabled-groups)) (defmacro enable-groups (&rest groups) (declare (indent 0)) `(progn ,@(mapcar (lambda (g) `(enable-group ',g)) groups))) (defun enable-all-groups () (setq group--all-groups t)) (defun group-enabled-p (group) (or (memq group group--enabled-groups) group--all-groups)) (defmacro if-group (groups then &rest else) (declare (indent 2)) (let ((groups (if (listp groups) groups (list groups)))) `(if (and ,@(mapcar (lambda (group) `(group-enabled-p ',group)) groups)) ,then ,@else))) (defmacro when-group (groups &rest body) (declare (indent 1)) `(if-group ,groups (progn ,@body))) (defmacro unless-group (groups &rest body) (declare (indent 1)) `(if-group ,groups nil ,@body)) (defvar group-file (locate-user-emacs-file "groups.el")) (if (file-exists-p group-file) (load group-file) (enable-all-groups))) (progn ;; host attributes (defun has-attribute (attr) (let ((attr (if (stringp attr) attr (thread-last (symbol-name attr) upcase (string-replace "-" "_"))))) (string= (getenv attr) "1"))) (defmacro if-attribute (attr then &rest else) (declare (indent 2)) `(if (has-attribute ',attr) ,then ,@else)) (defmacro when-attribute (attr &rest body) (declare (indent 1)) `(if-attribute ,attr (progn ,@body))) (defmacro unless-attribute (attr &rest body) (declare (indent 1)) `(if-attribute ,attr nil ,@body))) (use-package custom :config (setq custom-safe-themes t custom-file (locate-user-emacs-file ".custom.el"))) (use-package faces :config (add-to-list 'face-remapping-alist '(tab-bar-tab-inactive mode-line-inactive)) (custom-set-faces '(default ((t (:font "monospace 7")))) '(variable-pitch ((t (:font "sans-serif 8")))) '(font-lock-comment-face ((t (:slant italic)))))) (progn ;; transparency (defvar default-alpha 90) (set-frame-parameter nil 'alpha-background default-alpha) (add-to-list 'default-frame-alist `(alpha-background . ,default-alpha)) (defun toggle-transparency (&optional frame) (interactive) (let* ((current-alpha (frame-parameter nil 'alpha-background))) (set-frame-parameter frame 'alpha-background (if (= current-alpha default-alpha) 100 default-alpha)))) (defun disable-transparency (&optional frame) (interactive) (set-frame-parameter frame 'alpha-background '(100 . 100))) (defun change-transparency (f &optional frame) (let ((trans (frame-parameter frame 'alpha-background))) (set-frame-parameter nil 'alpha-background (funcall f trans)))) (defun lower-transparency (arg) (interactive "P") (change-transparency (lambda (x) (- x (or arg 5))))) (defun raise-transparency (arg) (interactive "P") (change-transparency (lambda (x) (+ x (or arg 5)))))) (progn ;; useful-functions (defun spawn-shell-cmd (name buffer command) (declare (ignore name)) (call-process-shell-command (concat "setsid -f " command) nil buffer)) (cl-defun clear-message-with-delay (&optional (delay 0.5)) (interactive) (run-with-timer delay nil clear-message-function)) (defmacro with-new-frame (&rest body) `(progn (select-frame (make-frame)) ,@body)) (defun eval-in-new-frame (form &optional lexical) (with-new-frame (eval form lexical))) (defun greet (name) "Greet NAME in a frendly, non intimidating fassion." (interactive "sWhat's your name? ") (message "Hello, %s, I am the Emacs Lisp interpreter." name)) (defun advice-clear (sym) (advice-mapc (lambda (f &rest _) (advice-remove sym f)) sym)) (defun frame-menu-toolbars-on () "Show the menu and toolbars in current frame." (interactive) (modify-frame-parameters nil '((tool-bar-lines . 1) (menu-bar-lines . 1) (vertical-scroll-bars) (right-fringe . 1) (left-fringe . 1)))) (defun frame-menu-toolbars-off () "Hide the menu and toolbars in current frame." (interactive) (modify-frame-parameters nil '((tool-bar-lines . 0) (menu-bar-lines . 0) (vertical-scroll-bars) (right-fringe . 0) (left-fringe . 0)))) (defun make-scratch-buffer (name) (interactive "sName: ") (find-file (concat "~/tmp/" name ".scratch.org"))) (defun revert-all-buffers (&optional arg) (interactive) (let ((pattern (or arg ".*")) (c-buf (current-buffer))) (dolist (buf (buffer-list)) (when (and (string-match-p pattern (buffer-name buf)) (buffer-file-name buf)) (with-current-buffer buf (revert-buffer 'ignore-auto 'noconfirm 'preserve-modes)))))) (defun do-split-window () (interactive) (funcall split-window-preferred-function)) (defun go-next-window (arg) (interactive "p") (cond ((cl-plusp arg) (select-window (next-window)) (go-next-window (1- arg))) ((cl-minusp arg) (select-window (previous-window)) (go-next-window (1+ arg))))) (defun go-previous-window (arg) (interactive "p") (go-next-window (* arg -1))) (defun go-other-buffer () (interactive) (switch-to-buffer (other-buffer (current-buffer)) nil t)) (defun kill-buffer-and-maybe-window () (interactive) (if (not (one-window-p)) (kill-buffer-and-window) (kill-buffer))) (defun display-buffer-in-side-window-nearest (buffer alist) (let* ((side (if (> (+ (window-left-column) (current-column)) (/ (frame-width) 2)) 'right 'left)) (alist (cons `(side . ,side) alist))) (display-buffer-in-side-window buffer alist))) (defun pidof (name) (require 'proced) (cl-find name (proced-filter (proced-process-attributes) (alist-get 'user proced-filter-alist)) :key (lambda (x) (alist-get 'args (cdr x))) :test #'string-match-p)) (defun string-match-substrings (regexp string &optional start) (declare (side-effect-free t)) (save-match-data (when (string-match regexp string start) (mapcar (lambda (x) (if (and (car x) (cadr x)) (substring string (car x) (cadr x)) "")) (seq-partition (match-data) 2))))) (defun write-string (string filename &optional append mustbenew) (with-temp-buffer (insert string) (write-region (point-min) (point-max) filename append nil nil mustbenew))) (defun file-string (filename &optional beg end) (with-temp-buffer (insert-file-contents filename nil beg end) (buffer-string))) (defun add-hooks (hook &rest functions) (declare (indent 1)) (mapc (lambda (x) ;; A closure is a list. Ugh. (let* ((x (ensure-list x)) (x (if (functionp x) (list x) x))) (apply #'add-hook hook x))) functions)) (defun keymap-modify (keymap &rest definitions) (declare (indent 1)) (apply #'define-keymap :keymap keymap definitions)) (defun keymap-global-modify (&rest definitions) (declare (indent 0)) (apply #'define-keymap :keymap global-map definitions)) (defun insert-file-name (&optional absolute) (interactive "P") (let ((f (read-file-name "File name: "))) (insert (if absolute f (file-relative-name f default-directory))))) (keymap-global-set "C-x M-f" 'insert-file-name) (defun enable-ts-mode (lang &optional from to) (let ((from (or from (intern (format "%s-mode" lang)))) (to (or to (intern (format "%s-ts-mode" lang))))) (when (treesit-ready-p lang 'quiet) (cl-pushnew (cons from to) major-mode-remap-alist :test #'equal)))) (defun fixup-typing-stats (&optional arg) "Keyboard macro." (interactive "p") (kmacro-exec-ring-item '([left left left left left left left left left left left left left left left left left left left left left left left left left left left left left left left left left left left left 44 right right right right right right right right right right right right right right right 44 right right right right right right right right right right right 40 right right right right right right right right right right 41 down] 2 "%d") arg)) (defun fixup-writing-station (&optional arg) "Keyboard macro." (interactive "p") (kmacro-exec-ring-item '([C-up right 60 115 101 99 116 105 111 110 32 105 100 61 5 62 6 60 104 50 62 5 60 47 104 50 62 13 60 112 62 6 67108896 C-down 15 60 47 115 101 99 116 105 111 110 62 1 2 24 24 134217765 91 13 60 98 62 13 33 21 67108896 24 24 24 24 134217765 93 13 60 47 98 62 13 33 21 67108896 24 24 24 24 134217765 17 10 13 60 98 114 62 17 10 13 33 C-down 14] 0 "%d") arg)) (defun writing-station-preproc (&optional arg) "Keyboard macro." (interactive "p") (kmacro-exec-ring-item '([?\C-r ?_ right ?\C- end ?\M-w home right ?\M-c ?: ?\C-s ?\C-y ?\M-b ?\[ ?\M-f ?\] ?\C-r ?S backspace ?_ right backspace]) arg)) (defun zdict-open () (interactive) (cond ((file-exists-p "/etc/dictionaries-common/words") (view-file "/etc/dictionaries-common/words")) ((file-exists-p "/usr/share/dict/words") (view-file "/usr/share/dict/words")) (t (message "zdict: cannot open dictionary"))) (rename-buffer "zlastcompletion") (bury-buffer))) (progn ;; pair-inserters (defvar-keymap insert-pair-map) (defmacro define-pair-inserter (name delim1 &optional delim2) (let ((name (intern (format "insert-%s" name)))) `(progn (defun ,name (arg) (interactive "P") (insert-pair (or arg 1) ,delim1 ,(or delim2 delim1))) ,@(mapcar (lambda (pfx) `(keymap-set insert-pair-map ,(format "%s%c" pfx delim1) ',name)) '("" "M-"))))) (define-pair-inserter parens ?\( ?\)) (define-pair-inserter brackets ?\[ ?\]) (define-pair-inserter braces ?\{ ?\}) (define-pair-inserter angle-brackets ?< ?>) (define-pair-inserter double-quotes ?\") (define-pair-inserter single-quotes ?\') (define-pair-inserter back-quotes ?\`) (keymap-global-set "M-(" insert-pair-map)) (progn ;; hyper-key (define-prefix-command 'h-x-prefix) (keymap-global-modify "H-x" 'h-x-prefix "H-" 'backward-sexp "H-" 'forward-sexp "H-" 'backward-up-list "H-" 'down-list "H-f" 'insert-file-name)) (progn ;; alt-key (setq x-meta-keysym 'meta) (setq x-alt-keysym 'alt) (when-group alt-key (cl-psetq x-alt-keysym x-meta-keysym x-meta-keysym x-alt-keysym))) ;; (progn ;; swap-ctrl ;; (setq x-ctrl-keysym 'ctrl) ;; (cl-psetq ;; x-alt-keysym x-ctrl-keysym ;; x-ctrl-keysym x-alt-keysym)) (progn ;; ham (defvar ham-callsign-history nil) (defun ham-lookup-callsign (call) (interactive (list (cond ((and (not current-prefix-arg) (word-at-point)) (word-at-point)) ((region-active-p) (buffer-substring-no-properties (region-beginning) (region-end))) (t (read-string "Callsign: " nil 'ham-callsign-history))))) (browse-url (format "https://www.ae7q.com/query/data/CallHistory.php?CALL=%s" (upcase call)))) (define-prefix-command 'ham-prefix) (keymap-global-set "H-x H-h" 'ham-prefix) (keymap-global-set "H-x H-h c" 'ham-lookup-callsign)) (use-package emacs :load-path "lisp/" :config (setq enable-recursive-minibuffers t) (minibuffer-depth-indicate-mode) (setq scroll-conservatively 999 scroll-preserve-screen-position t next-screen-context-lines 0 auto-revert-avoid-polling t disabled-command-function nil ediff-window-setup-function 'ediff-setup-windows-plain ediff-split-window-function 'split-window-sensibly diff-font-lock-prettify t set-mark-command-repeat-pop t fortune-dir "/usr/share/games/fortunes" fortune-file (expand-file-name "linuxcookie" fortune-dir) initial-scratch-message nil save-interprogram-paste-before-kill t visible-bell t what-cursor-show-names t windmove-create-window t tab-always-indent 'complete goto-line-history-local t repeat-keep-prefix t desktop-load-locked-desktop 'check-pid completion-styles '(basic flex partial-completion initials) completion-ignore-case t read-file-name-completion-ignore-case t read-buffer-completion-ignore-case t bookmark-completion-ignore-case t window-sides-slots '(4 4 4 4) display-buffer-alist (cl-flet ((match-modes (&rest modes) (lambda (buf _) (with-current-buffer buf (not (seq-every-p #'null (mapcar #'derived-mode-p modes))))))) `((,(rx bol (| "*Help*" "*Man" "*info" "*Shortdoc")) (display-buffer-reuse-mode-window (lambda (buffer alist) (if (> (frame-width) 255) (display-buffer-in-side-window-nearest buffer alist) (display-buffer-in-side-window buffer alist)))) (side . left) (slot . -4) (window-width . 81) (mode help-mode Man-mode Info-mode)) (,(rx bol (| "*compilation*" "*Compile-Log*" "*grep*" "*Occur*" "*Locate*") eol) display-buffer-in-side-window-nearest (window-width . 0.33)) (,(rx (| "*Backtrace*" "*Warnings*" "*Messages*")) display-buffer-in-side-window (side . bottom) (slot . 4) (window-height . 0.33)) (,(match-modes 'magit-mode) display-buffer-in-side-window-nearest (window-width . 100)) (,(rx bol "*sly-mrepl") display-buffer-same-window) (,(rx bol "*Dired-sidebar*" eol) display-buffer-in-side-window (side . left) (slot . 4) (window-width . 30)) (,(rx bol (or "*Org Agenda*" ) eol) display-buffer-reuse-mode-window (mode org-agenda-mode)) (,(rx bol (or "*Org Select*" "*Org Attach*" " *Agenda Commands*") eol) display-buffer-in-side-window (side . bottom) (slot . 4) (window-height . fit-window-to-buffer)) (,(rx bol "*Org QL View: ") display-buffer-in-side-window-nearest (slot . 1)))) display-buffer-base-action '(display-buffer-same-window)) (setq-default fill-column 78) (cl-pushnew "/run/current-system/sw/lib" treesit-extra-load-path :test #'equal) (keymap-global-set "C-x M-s" 'window-toggle-side-windows) (keymap-global-set " " 'ibuffer) (keymap-global-modify "M-s M-g" 'rgrep "M-s M-l" 'locate "M-s M-f" 'find-dired) (keymap-global-modify "C-(" 'backward-paragraph "C-)" 'forward-paragraph) (add-hook 'before-save-hook 'delete-trailing-whitespace) (modify-all-frames-parameters '((tool-bar-lines . 0) (menu-bar-lines . 0) (vertical-scroll-bars) (right-fringe . 0) (left-fringe . 0) (undecorated . t))) (blink-cursor-mode 0) (global-hi-lock-mode) (minibuffer-electric-default-mode) (column-number-mode) (show-paren-mode) (electric-pair-mode) (repeat-mode) (url-handler-mode) (size-indication-mode) (when (fboundp #'editorconfig-mode) (editorconfig-mode)) (zdict-open) (switch-to-buffer "*scratch*")) (use-package project :config (define-advice project-try-vc (:around (oldf &rest args) ignore-home) (require 'tramp) (let ((pr (apply oldf args))) (cond ((not pr) nil) ((file-equal-p (project-root pr) (tramp-handle-expand-file-name (concat (or (file-remote-p default-directory) "") "~/"))) nil) (t pr)))) (setq project-vc-extra-root-markers '(".projectile" ".project"))) (use-package diminish :ensure t) (use-package meow :config (apply #'meow-leader-define-key (mapcar (lambda (n) (cons (number-to-string n) 'meow-digit-argument)) (number-sequence 0 9))) (apply #'meow-normal-define-key (mapcar (lambda (x) (cons (car x) (cadr x))) (seq-partition `(,@(apply #'append (mapcar (lambda (n) `(,(number-to-string n) ,(intern (format "meow-expand-%d" n)))) (number-sequence 0 9))) "-" negative-argument ,@(apply #'append (apply #'append (mapcar (lambda (x) (list `(,(car x) ,(intern (format "meow-%s" (cadr x)))) `(,(upcase (car x)) ,(intern (format "meow-%s-expand" (cadr x)))))) '(("n" next) ("p" prev) ("b" left) ("f" right))))) "a" meow-append "A" meow-open-below "c" meow-change "d" meow-delete "D" meow-backward-delete "e" meow-next-word "E" meow-next-symbol "g" meow-cancel-selection "G" meow-grab "h" meow-mark-word "H" meow-mark-symbol "i" meow-insert "I" meow-open-above "j" meow-find "J" meow-join "k" meow-kill "m" meow-back-word "M" meow-back-symbol "o" meow-block "O" meow-to-block "q" meow-quit "r" meow-replace "R" meow-swap-grab "s" meow-search "t" meow-till "u" meow-undo "U" meow-undo-in-selection "v" meow-visit "w" meow-save "x" meow-delete "X" meow-backward-delete "y" meow-yank "Y" meow-sync-grab "z" meow-pop-selection ";" meow-reverse "l" meow-line "\"" meow-goto-line "," meow-inner-of-thing "." meow-bounds-of-thing "[" meow-beginning-of-thing "]" meow-end-of-thing "<" beginning-of-buffer ">" end-of-buffer "{" backward-paragraph "}" forward-paragraph "'" avy-goto-char) 2)))) (use-package server :demand t :config (unless (server-running-p server-name) (server-start)) (setenv "EDITOR" "emacsclient")) (use-package el-patch :demand t :ensure t :config (unless (eq (car use-package-keywords) :if) (push :if use-package-keywords))) (use-package window :config/el-patch (defun split-window-sensibly (&optional window) "Split WINDOW in a way suitable for `display-buffer'. WINDOW defaults to the currently selected window. If `split-height-threshold' specifies an integer, WINDOW is at least `split-height-threshold' lines tall and can be split vertically, split WINDOW into two windows one above the other and return the lower window. Otherwise, if `split-width-threshold' specifies an integer, WINDOW is at least `split-width-threshold' columns wide and can be split horizontally, split WINDOW into two windows side by side and return the window on the right. If this can't be done either and WINDOW is the only window on its frame, try to split WINDOW vertically disregarding any value specified by `split-height-threshold'. If that succeeds, return the lower window. Return nil otherwise. By default `display-buffer' routines call this function to split the largest or least recently used window. To change the default customize the option `split-window-preferred-function'. You can enforce this function to not split WINDOW horizontally, by setting (or binding) the variable `split-width-threshold' to nil. If, in addition, you set `split-height-threshold' to zero, chances increase that this function does split WINDOW vertically. In order to not split WINDOW vertically, set (or bind) the variable `split-height-threshold' to nil. Additionally, you can set `split-width-threshold' to zero to make a horizontal split more likely to occur. Have a look at the function `window-splittable-p' if you want to know how `split-window-sensibly' determines whether WINDOW can be split." (let ((window (or window (selected-window)))) (or (and (window-splittable-p window (el-patch-add t)) ;; Split window horizontally. (with-selected-window window ((el-patch-swap split-window-below split-window-right)))) (and (window-splittable-p window (el-patch-remove t)) ;; Split window vertically. (with-selected-window window ((el-patch-swap split-window-right split-window-below)))) (and ;; If WINDOW is the only usable window on its frame (it is ;; the only one or, not being the only one, all the other ;; ones are dedicated) and is not the minibuffer window, try ;; to split it vertically disregarding the value of ;; `split-height-threshold'. (let ((frame (window-frame window))) (or (eq window (frame-root-window frame)) (catch 'done (walk-window-tree (lambda (w) (unless (or (eq w window) (window-dedicated-p w)) (throw 'done nil))) frame nil 'nomini) t))) (not (window-minibuffer-p window)) (let ((split-height-threshold 0)) (when (window-splittable-p window) (with-selected-window window (split-window-below))))))))) (use-package info :init (defun my/view-org-as-info () (interactive) (let* ((base (file-name-concat temporary-file-directory (make-temp-name "org-info"))) (texi (file-name-with-extension base "texi")) (info (file-name-with-extension base "info")) (title (if (buffer-file-name) (file-name-nondirectory (buffer-file-name)) (buffer-name)))) (org-export-to-file 'texinfo texi nil nil nil nil `(:title ,title) #'org-texinfo-compile) (info info (format "*info for %s*" (buffer-name (current-buffer)))) (delete-file texi) (delete-file info))) (defun my/view-md-as-info () (interactive) (let ((b (get-buffer-create (concat (buffer-name) "(org)")))) (call-process-region nil nil "pandoc" nil b nil "-f" "markdown" "-t" "org") (with-current-buffer b (my/view-org-as-info)) (kill-buffer b))) (defun my/view-file-as-info () (interactive) (let* ((ff (mapcar (lambda (x) (intern (format "my/view-%s-as-info" x))) `(,@(when (buffer-file-name) `(,(file-name-extension (buffer-file-name)))) ,@(when-let ((ext (cdr (last (split-string (buffer-name) "\\."))))) `(,ext)) ,major-mode))) (f (seq-find #'functionp ff))) (if f (funcall f) (user-error "Don't know how to view file as info")))) :config (custom-set-faces '(Info-quoted ((t (:inherit font-lock-string-face)))) '(info-title-4 ((t (:inherit variable-pitch))))) (add-hook 'Info-mode-hook 'variable-pitch-mode)) (use-package shr :config (setq shr-width 80 shr-fill-text nil) (when (display-graphic-p) (setq shr-bullet "• "))) (use-package eww-lnum :ensure t) (use-package eww :config (keymap-set eww-mode-map "f" 'eww-lnum-follow)) (use-package elpher :ensure t :config (custom-set-faces '(elpher-gemini-preformatted ((t (:inherit (fixed-pitch)))))) (add-hook 'elpher-mode-hook 'variable-pitch-mode)) (use-package browse-url :config (setq browse-url-firefox-program (or (executable-find "firefox-esr") (executable-find "firefox")) browse-url-browser-function #'browse-url-firefox)) (use-package browser-hist :ensure t :config (setq browser-hist-default-browser 'firefox browser-hist-db-paths '((firefox . "$HOME/.mozilla/firefox/main/places.sqlite")))) (when-group org ;; org packages (use-package org-present :ensure t) (use-package org-real :ensure t)) (use-package org :if (group-enabled-p 'org) :init (defun my/org-procrastinate () (interactive) (org-with-point-at (org-find-exact-headline-in-buffer "Procrastination" (find-file-noselect (expand-file-name "_.org" org-directory))) (org-clock-in))) (defun my/org-clock-nag-procrastination () (require 'org-clock) (when (and (org-clocking-p) (equal org-clock-heading "Procrastination")) (org-notify "What are you doing? What should you be doing?" t))) (run-at-time t 150 #'my/org-clock-nag-procrastination) (defun my/consult-org-heading () (interactive) (let ((files (org-agenda-files)) (consult-after-jump-hook nil)) (consult-org-heading nil (cond ((and buffer-file-name (derived-mode-p 'org-mode)) (if (member buffer-file-name files) files (cons buffer-file-name files))) ((derived-mode-p 'org-mode) nil) (t 'agenda))))) (define-prefix-command 'org-prefix) (define-prefix-command 'org-clock-prefix) (keymap-global-modify "H-o" 'org-prefix "C-x M-o" 'org-prefix) (keymap-modify org-prefix "a" 'org-agenda "c" 'org-capture "b" 'org-switchb "p" 'my/org-procrastinate "o" 'my/consult-org-heading "l" 'org-clock-prefix) (keymap-modify org-clock-prefix "i" 'org-clock-in "o" 'org-clock-out "x" 'org-clock-in-last "e" 'org-clock-modify-effort-estimate "j" 'org-clock-goto "q" 'org-clock-cancel) :config (require 'xdg) (setq org-todo-keywords '((sequence "TODO(t)" "PROG(p)" "DONE(d)") (type "WAIT(w)" "|") (type "PROJ(j)" "|")) org-priority-highest 1 org-priority-default 3 org-priority-lowest 5 org-priority-start-cycle-with-default nil org-use-fast-todo-selection 'expert org-log-into-drawer t org-refile-use-outline-path t org-refile-targets '((nil . (:maxlevel . 9))) org-modules '(org-habit org-tempo ol-info ol-man ol-gnus ox-beamer) org-startup-folded t org-hide-leading-stars t org-startup-indented t org-src-window-setup 'current-window org-ellipsis "…" org-hide-emphasis-markers t org-pretty-entities t org-tags-column 0 org-catch-invisible-edits 'show org-image-actual-width 500 org-display-remote-inline-images 'cache org-preview-latex-default-process 'dvisvgm org-preview-latex-image-directory (file-name-concat (or (xdg-cache-home) user-emacs-directory) "ltximg/") org-show-notification-handler "notify-send" org-timestamp-custom-formats '("%B %d, %Y" . "%B %d, %Y at %H:%M") org-enforce-todo-dependencies t org-enforce-todo-checkbox-dependencies t org-clock-out-remove-zero-time-clocks t org-tags-exclude-from-inheritance '("ATTACH")) (setf (plist-get org-format-latex-options :background) "Transparent") (add-hook 'org-mode-hook 'variable-pitch-mode) (custom-set-faces '(org-code ((t (:inherit (font-lock-function-name-face fixed-pitch))))) '(org-block ((t (:inherit fixed-pitch)))) '(org-meta-line ((t (:inherit fixed-pitch)))) '(org-indent ((t (:inherit (org-hide fixed-pitch))))) '(org-ellipsis ((t (:underline nil :foreground "#454d4f")))) '(org-checkbox ((t (:inherit fixed-pitch)))) '(org-table ((t (:inherit fixed-pitch))))) (keymap-modify org-mode-map "C-c b" 'org-switchb "C-'" nil) (define-advice org-get-category (:around (oldf &optional pos _) use-parent) (or (save-excursion (when (org-up-heading-safe) (org-no-properties (org-sort-remove-invisible (org-get-heading t t t t))))) (funcall oldf pos _))) (defun revert-org-buffers () (interactive) (save-some-buffers nil (lambda () (eq major-mode 'org-mode))) (revert-all-buffers "\\.org$"))) (use-package org-superstar :if (group-enabled-p 'org) :ensure t :init (add-hook 'org-mode-hook 'org-superstar-mode) :config (setq org-superstar-special-todo-items t org-superstar-todo-bullet-alist '(("TODO" . (?☐ ?*)) ("PROG" . (?☒ ?*)) ("DONE" . (?☑ ?*)) ("PROJ" . (?☐ ?*)) ("WAIT" . (?⌚ ?*))) org-superstar-headline-bullets-list '((?◉ ?*) (?○ ?*) (?● ?*) (?◈ ?*) (?◇ ?*)) org-superstar-item-bullet-alist '((?* . ?⦿) (?+ . ?•) (?- . ?⁃)))) (use-package calfw :ensure t) (use-package calfw-org :ensure t :commands cfw:open-org-calendar) (use-package org-capture :if (group-enabled-p 'org) :config (defun my/org-agenda-capture-day () (with-current-buffer org-agenda-buffer (format-time-string "<%Y-%m-%d %a>" (org-time-from-absolute (org-get-at-bol 'day))))) (defun my/org-capture-date-tag () (format-time-string "date%%%Y_%m_%d")) (setq org-capture-templates (cl-flet ((tmpl (pre &optional post) (format "* %s\n%s" pre (if post post ""))) (loc (loc) (cdr (assoc loc '((calendar . (file+olp "_.org" "Calendar")) (todos . (file+olp "_.org" "TODOs")))))) (linked ( unkey undesc ankey andesc type loc tmpl &optional antmpl) (list (list unkey undesc type loc tmpl) (list ankey andesc type loc (or antmpl (concat tmpl "\n%a")))))) `(,@(linked "a" "Add appointment" "A" "Add linked appointment" 'entry (loc 'calendar) (tmpl "%^{Name}" "%^{at}t")) ("c" "Add appointment for this day (calfw)" entry ,(loc 'calendar) ,(tmpl "%^{Name}" "%(cfw:org-capture-day)")) ("p" "Add appointment for this day (agenda)" entry ,(loc 'calendar) ,(tmpl "%^{Name}" "%(my/org-agenda-capture-day)")) ,@(linked "t" "Add TODO" "T" "Add linked TODO" 'entry (loc 'todos) (tmpl "TODO %^{Name} :@review:")))) org-capture-templates-contexts '(("p" ((in-mode . "org-agenda-mode"))) ("c" ((in-mode . "cfw:calendar-mode"))) ("p" "c" ((in-mode . "cfw:calendar-mode")))))) (use-package org-urgency :if (group-enabled-p 'org) :ensure t :vc (:url "https://ba.ln.ea.cx/src/marsironpi/emacs/org-urgency") :config (setq org-urgency-functions (org-urgency-list '((tag? "@review" 1000) (state? "PROG" 2) (state? "WAIT" -10) (priority 5) (near-deadline 4) (timestamped-today? 110) (random 2))))) (use-package org-margin :if (group-enabled-p 'org) :ensure t :vc (:url "https://ba.ln.ea.cx/src/marsironpi/emacs/org-margin") :init (with-eval-after-load 'org (keymap-set org-mode-map "C-c m" 'org-margin-org-mode)) (with-eval-after-load 'pdf-tools (keymap-set pdf-view-mode-map "C-c m" 'org-margin-doc-mode))) (use-package ol-file-paged :if (group-enabled-p 'org) :ensure t :vc (:url "https://ba.ln.ea.cx/src/marsironpi/emacs/ol-file-paged")) (use-package org-agenda :if (group-enabled-p 'org) :config (defvar my/org-today nil) (define-advice org-today (:filter-return (val) my/org-today) (if my/org-today my/org-today val)) (defun my/org-visit-day (&optional day) (interactive (list (time-to-days (encode-time (org-parse-time-string (org-read-date)))))) (let ((today (let ((my/org-today nil)) (org-today)))) (setq my/org-today (unless (equal day today) day)))) (defun my/org-agenda-visit-day (&optional arg) (interactive "P" org-agenda-mode) (call-interactively #'my/org-visit-day) (org-agenda-redo-all arg)) (defun my/org-agenda-include-tag () (interactive nil org-agenda-mode) (org-agenda-filter-by-tag '(16))) (defun my/org-agenda-skip-if-sub-todos () (let* ((end (save-excursion (org-end-of-subtree t) )) (end (max end (org-entry-end-position)))) (save-match-data (when (save-excursion (goto-char (1- (org-entry-end-position))) (re-search-forward (rx-to-string `(and "* " (or ,@org-not-done-keywords))) end 'noerror)) (org-entry-end-position))))) (defun my/org-agenda-skip-if-only-timestamped () (when (and (org-entry-is-todo-p) (not (or (org-get-scheduled-time (point)) (org-get-deadline-time (point))))) (org-entry-end-position))) (setq org-agenda-files (list (file-name-concat org-directory "_.org")) org-agenda-include-diary t org-agenda-time-grid nil org-agenda-persistent-marks t org-agenda-window-setup 'current-window org-agenda-prefix-format '((agenda . " %-50:c%?-12t% s") (todo . " %-50:c") (tags . " %-50:c") (search . " %-50:c")) org-agenda-span 31 org-agenda-start-on-weekday nil org-agenda-skip-deadline-if-done t org-agenda-skip-scheduled-if-done t org-agenda-skip-timestamp-if-done t org-agenda-cmp-user-defined #'org-urgency-compare org-agenda-sorting-strategy '((agenda . (user-defined-down)) (todo . (user-defined-down)) (search . (category-keep))) org-agenda-custom-commands '(("n" "Agenda and TODO list" ((alltodo "") (agenda "" ((org-agenda-skip-function #'my/org-agenda-skip-if-only-timestamped) (org-deadline-warning-days 0)))) ((org-agenda-skip-function #'my/org-agenda-skip-if-sub-todos) (org-agenda-max-entries 7) (org-agenda-span 15))) ("L" "Shortened TODO list" ((alltodo "")) ((org-agenda-skip-function #'my/org-agenda-skip-if-sub-todos) (org-agenda-max-entries 7))) ("l" "TODO list" ((alltodo "")) ((org-agenda-skip-function #'my/org-agenda-skip-if-sub-todos)))) org-agenda-exporter-settings '((htmlize-hyperlink-style (format " body { background-image: url(\"img\"); background-size: cover; background-repeat: no-repeat; background-position: center; height: 100vh; } pre { background-color: %scc; padding: 1em; padding-bottom: 5em; margin: 5em; margin-top: 20vh; overflow: hidden; border-radius: 0.7em; } span { background-color: initial !important } " (face-background 'default) (face-background 'default))) (org-agenda-tags-column -100))) (add-hook 'org-agenda-mode-hook (lambda () (when (not text-scale-mode) (text-scale-set 1)))) (keymap-modify org-agenda-mode-map "M-u" 'rename-uniquely "\\" 'my/org-agenda-include-tag "`" 'my/org-agenda-visit-day) :config/el-patch (defun org-agenda-filter-make-matcher (filter type &optional expand) "Create the form that tests a line for agenda filter. Optional argument EXPAND can be used for the TYPE tag and will expand the tags in the FILTER if any of the tags in FILTER are grouptags." (let ((multi-pos-cats (and (eq type 'category) (string-match-p "\\+.*\\+" (mapconcat (lambda (cat) (substring cat 0 1)) filter "")))) f f1) (cond ;; Tag filter ((eq type 'tag) (setq filter (delete-dups (append (assoc-default 'tag org-agenda-filters-preset) filter))) (dolist (x filter) (let ((op (string-to-char x))) (if expand (setq x (org-agenda-filter-expand-tags (list x) t)) (setq x (list x))) (setq f1 (org-agenda-filter-make-matcher-tag-exp x op)) (push f1 f)))) ;; Category filter ((eq type 'category) (setq filter (delete-dups (append (assoc-default 'category org-agenda-filters-preset) filter))) (dolist (x filter) (if (equal "-" (substring x 0 1)) (setq f1 (list 'not (list 'equal (substring x 1) 'cat))) (setq f1 (list 'equal (substring x 1) 'cat))) (push f1 f))) ;; Regexp filter ((eq type 'regexp) (setq filter (delete-dups (append (assoc-default 'regexp org-agenda-filters-preset) filter))) (dolist (x filter) (if (equal "-" (substring x 0 1)) (setq f1 (list 'not (list 'string-match (substring x 1) 'txt))) (setq f1 (list 'string-match (substring x 1) 'txt))) (push f1 f))) ;; Effort filter ((eq type 'effort) (setq filter (delete-dups (append (assoc-default 'effort org-agenda-filters-preset) filter))) (dolist (x filter) (push (org-agenda-filter-effort-form x) f)))) (cons (if (el-patch-swap multi-pos-cats (or multi-pos-cats (eq type 'tag))) 'or 'and) (nreverse f))))) (use-package org-clock :config (define-advice org-clock-select-task (:around (oldf &rest args) use-consult) (require 'consult) (if consult-mode (save-window-excursion (save-excursion (my/consult-org-heading))) (apply oldf args)))) (use-package org-refile :config (define-advice org-refile-get-location (:around (oldf &rest args) use-consult) (require 'consult) (if consult-mode (org-with-point-at (my/consult-org-heading) (list (or (elt (org-heading-components) 4) "") buffer-file-name nil (save-restriction (widen) (org-narrow-to-subtree) (point-max) (point)))) (apply oldf args)))) (use-package holidays :config (setq calendar-christian-all-holidays-flag t calendar-holidays (append holiday-general-holidays holiday-local-holidays holiday-other-holidays holiday-christian-holidays holiday-solar-holidays))) (use-package org-habit :config (setq org-habit-graph-column 70) (custom-set-faces '(org-habit-clear-face ((t (:background "blue")))) '(org-habit-clear-future-face ((t (:background "midnightblue")))) '(org-habit-ready-face ((t (:background "forestgreen")))) '(org-habit-ready-future-face ((t (:background "darkgreen")))) '(org-habit-alert-face ((t (:background "gold")))) '(org-habit-alert-future-face ((t (:background "darkgoldenrod")))) '(org-habit-overdue-face ((t (:background "firebrick")))) '(org-habit-overdue-future-face ((t (:background "darkred")))))) (use-package org-num :if (group-enabled-p 'org) :config (defun my/org-num-format (nums) (concat (number-to-string (car (last nums))) " ")) (setq org-num-skip-unnumbered t org-num-format-function #'my/org-num-format)) (use-package ol :if (group-enabled-p 'org) :init (keymap-modify org-prefix "H-o" 'org-open-at-point-global "H-s" 'org-store-link "H-l" 'org-insert-link-global)) (use-package org-ctags :if (group-enabled-p 'org) :init (setq org-ctags-open-link-functions nil)) (use-package citeproc :if (group-enabled-p 'org) :ensure t) (use-package oc :if (group-enabled-p 'org) :config (mapc #'require '(oc-csl oc-natbib))) (use-package ob :if (group-enabled-p '(programming org)) :config (mapc #'require '(ob-ruby ob-js ob-C ob-shell ob-lilypond ob-lisp ob-haskell ob-plantuml ob-lisp ob-scheme)) (setq org-plantuml-exec-mode 'plantuml org-babel-lisp-eval-fn #'sly-eval) :config/el-patch (defun org-babel-confirm-evaluate (info) "Confirm evaluation of the code block INFO. This query can also be suppressed by setting the value of `org-confirm-babel-evaluate' to nil, in which case all future interactive code block evaluations will proceed without any confirmation from the user. Note disabling confirmation may result in accidental evaluation of potentially harmful code. The variable `org-babel-confirm-evaluate-answer-no' is used by the async export process, which requires a non-interactive environment, to override this check." (let* ((evalp (org-babel-check-confirm-evaluate info)) (lang (nth 0 info)) (name (nth 4 info)) (name-string (if name (format " (%s) " name) " "))) (pcase evalp (`nil nil) (`t t) (`query (or (and (not (bound-and-true-p org-babel-confirm-evaluate-answer-no)) ((el-patch-swap yes-or-no-p y-or-n-p) (format "Evaluate this %s code block%son your system? " lang name-string))) (progn (message "Evaluation of this %s code block%sis aborted." lang name-string) nil))) (x (error "Unexpected value `%s' from `org-babel-check-confirm-evaluate'" x)))))) (use-package ox :if (group-enabled-p 'org) :config (require 'ox-html) (org-export-define-derived-backend 'html-simple 'html :translate-alist ;; markup '((bold . org-html-simple-bold) (code . org-html-simple-code) (italic . org-html-simple-italic) (subscript . org-html-simple-subscript) (superscript . org-html-simple-superscript) (strike-through . org-html-simple-strike-through) (underline . org-html-simple-underline) (verbatim . org-html-simple-verbatim) ;; misc (horizontal-rule . org-html-simple-horizontal-rule) (line-break . org-html-simple-line-break) (timestamp . org-html-simple-timestamp) ;; blocks (example-block . org-html-simple-example-block) (item . org-html-simple-item) (paragraph . org-html-simple-paragraph) (plain-list . org-html-simple-plain-list) (quote-block . org-html-simple-quote-block) (special-block . org-html-simple-special-block) (src-block . org-html-simple-src-block) (verse-block . org-html-simple-verse-block) ;; table (table . org-html-simple-table) (table-row . org-html-simple-table-row) (cell . org-html-simple-table-cell) ;; structural (headline . org-html-simple-headline) (inner-template . (lambda (a &rest _) a)) (plain-text . org-html-simple-plain-text) (section . org-html-simple-section) (template . org-html-simple-template)) :options-alist '((:html-simple-title-suffix "HTML_TITLE_SUFFIX" nil nil) (:html-simple-preamble "HTML_SIMPLE_PREAMBLE" nil nil) (:html-simple-postamble "HTML_SIMPLE_POSTAMBLE" nil nil)) :menu-entry '(?H "Export to simple HTML" ((?H "As HTML buffer" org-html-simple-export-as-html) (?h "As HTML file" org-html-simple-export-to-html) (?o "As HTML file and open" (lambda (a s v b) (if a (org-html-simple-export-to-html t s v b) (org-open-file (org-html-simple-export-to-html nil s v b)))))))) (cl-macrolet ((defsimple (name &rest body) `(defun ,(intern (format "org-html-simple-%s" name)) (el contents info) ,@body)) (deftag (name tag form &key nl) (let ((nl (if nl "\n" ""))) `(defsimple ,name (let ((val (org-html-encode-plain-text (or (org-element-property :value el) ""))) (cnt contents)) (format ,(format "<%s>%s%%s" tag nl tag) ,form)))))) ;; markup (deftag bold "strong" cnt) (deftag code "code" val) (deftag italic "em" cnt) (deftag subscript "sub" cnt) (deftag superscript "sup" cnt) (deftag strike-through "s" cnt) (deftag underline "u" cnt) (deftag verbatim "var" val) ;; misc (defsimple line-break "
\n") (defsimple horizontal-rule "
\n") (defun org-html-simple--format-timestamp (el) (cl-flet ((p (prop) (org-element-property prop el))) (format "%04d-%02d-%02d%s" (p :year-start) (p :month-start) (p :day-start) (if (p :hour-start) (format "T%02d:%02d" (p :hour-start) (p :minute-start)) "")))) (defsimple timestamp (let* ((org-display-custom-times t) (value (org-html-simple--format-timestamp el)) (text (replace-regexp-in-string (rx (or "<" ">" "[" "]")) "" (org-timestamp-translate el)))) (format "" value text))) ;; blocks (deftag example "samp" (thread-last cnt (replace-regexp-in-string "\n" "
\n") (replace-regexp-in-string "
\n\\'" "")) :nl t) (defsimple item (let* ((list (org-export-get-parent el)) (type (cl-case (org-element-property :type list) (descriptive "dt") (otherwise "li"))) (checkbox (if (org-element-property :checkbox el) (format " " (if (eq (org-element-property :checkbox el) 'on) " checked" "")) "")) (text (if (string= type "dt") (concat (org-export-data (org-element-property :tag el) info) "\n
" contents) contents))) (format "<%s>%s%s" type checkbox (or text "")))) (defsimple paragraph (cond ((eq (org-element-type (org-export-get-parent el)) 'item) contents) ((org-html-standalone-image-p el info) (format "
\n%s
\n%s\n
\n
\n" contents (org-export-data (org-export-get-caption el) info))) (t (format "

\n%s" contents)))) (defsimple plain-list (let ((type (cl-case (org-element-property :type el) (descriptive "dl") (ordered "ol") (unordered "ul") (otherwise "ul")))) (format "<%s>\n%s" type contents type))) (deftag quote-block "blockquote" cnt :nl t) (defsimple special-block (let ((tag (org-element-property :type el))) (format "<%s>\n%s" tag (or contents "") tag))) (deftag src-block "pre" val :nl t) (deftag verse-block "blockquote" (thread-last cnt (replace-regexp-in-string "\n" "
\n") (replace-regexp-in-string "
\n\\'" "\n")) :nl t) ;; table (defsimple cell (let ((row (org-export-get-parent el)) (table (org-export-get-parent-table el))) (cond ((and (org-export-table-has-header-p el info) (= 1 (org-export-table-row-group row info))) (concat "" contents)) (t (concat "" contents))))) (defsimple table-row (when (eq (org-element-property :type el) 'standard) (let ((starts-group (org-export-table-row-starts-rowgroup-p el info)) (group (org-export-table-row-group el info))) (concat (when starts-group (if (= group 1) "\n\n" "\n")) "" contents "\n")))) (defsimple table (let ((caption (when (org-export-get-caption el) (org-export-data (org-export-get-caption el) info))) (colgroups (mapconcat (lambda (cell) (let ((alignment (org-export-table-cell-alignment cell info))) (concat ;; Begin a colgroup? (when (org-export-table-cell-starts-colgroup-p cell info) "\n\n") ;; Add a column. Also specify its alignment. (format "" alignment) ;; End a colgroup? (when (org-export-table-cell-ends-colgroup-p cell info) "\n")))) (org-html-table-first-row-data-cells el info) "\n"))) (concat "" (when (and caption (not (string-empty-p (org-trim caption)))) (format "\n\n" caption)) colgroups "\n" contents "
%s
"))) ;; structural (defsimple headline (let* ((level (org-export-get-relative-level el info)) (pre-newline (not (and (= level 1) (org-export-first-sibling-p el info)))) (type (number-to-string (min 6 level))) (todo-keyword (org-element-property :todo-keyword el)) (todo (and todo-keyword (concat "" todo-keyword " "))) (priority-level (org-element-property :priority el)) (priority (and priority-level (concat "" priority-level " "))) (title (org-export-data (org-element-property :title el) info))) (format "%s%s%s%s\n%s" (if pre-newline "\n" "") type (or todo "") (or priority "") (or title "") type (or contents "")))) (defsimple section (or contents "")) (defun org-html-simple-plain-text (contents info) (org-html-encode-plain-text contents)) (defun org-html-simple-template (contents info) (concat "\n" "" (org-export-data (plist-get info :title) info) (plist-get info :html-simple-title-suffix) "\n\n" (let ((h (plist-get info :html-simple-preamble))) (if (functionp h) (funcall h contents info) h)) contents (let ((h (plist-get info :html-simple-postamble))) (if (functionp h) (funcall h contents info) h))))) (defun org-html-simple-export-as-html (&optional async subtreep visible-only body-only ext-plist) (interactive) (org-export-to-buffer 'html-simple "*Org HTML-Simple Export*" async subtreep visible-only body-only ext-plist ;; FIXME: Find out why `set-auto-mode' doesn't work (lambda () (mhtml-mode)))) (defun org-html-simple-export-to-html (&optional async subtreep visible-only body-only ext-plist) (interactive) (let* ((extension (concat (when (> (length org-html-extension) 0) ".") (or (plist-get ext-plist :html-extension) org-html-extension "html"))) (file (org-export-output-file-name extension subtreep)) (org-export-coding-system org-html-coding-system)) (org-export-to-file 'html-simple file async subtreep visible-only body-only ext-plist))) (defun org-html-simple-publish-to-html (plist filename pub-dir) (require 'ox-publish) (org-publish-org-to 'html-simple filename (concat (when (> (length org-html-extension) 0) ".") (or (plist-get plist :html-extension) org-html-extension "html")) plist pub-dir))) (use-package ox-publish :if (group-enabled-p 'org) :config (defun my/org-html-site-preamble (c info) (format "

%s

%s
" (org-export-data (plist-get info :title) info) (if-let ((title (plist-get info :subtitle))) (format "\n

%s

\n" title) ""))) (defun my/org-html-site-postamble (c info) (let* ((file (plist-get info :input-file)) (date (org-timestamp-from-time (org-publish-find-date file info)))) (if (equal (file-name-nondirectory file) (plist-get info :sitemap-filename)) "
\n
\n" (format " " (plist-get info :postamble-time-prefix) (org-html-simple-timestamp date nil nil))))) (defun my/org-publish-sitemap-entry (entry _style project) (format "[[file:%s][%s]]" (file-name-sans-extension entry) (org-publish-find-title entry project))) (defun my/org-publish-blog-post-entry (entry _style project) (format "[[file:%s][%s - %s]]" (file-name-sans-extension entry) (replace-regexp-in-string "[a-z]+\\'" "" (file-name-base entry)) (org-publish-find-title entry project))) (defun my/org-publish-org-project (name &rest opts) (cons name (org-combine-plists '( :recursive t :publishing-function org-html-simple-publish-to-html :html-simple-preamble my/org-html-site-preamble :html-simple-postamble my/org-html-site-postamble :html-simple-title-suffix " - Home on the Web" :auto-sitemap t :sitemap-filename "_.org" :sitemap-style list :sitemap-sort-files chronologically :sitemap-format-entry my/org-publish-sitemap-entry) opts))) (setq org-publish-project-alist `(,(my/org-publish-org-project "www/log" :base-directory "~/www/simon/src/log" :publishing-directory "~/www/simon/www/log" :sitemap-title "Blog" :sitemap-format-entry #'my/org-publish-blog-post-entry :postamble-time-prefix "Published on") ,(my/org-publish-org-project "www/doc" :base-directory "~/www/simon/src/doc" :publishing-directory "~/www/simon/www/doc" :sitemap-title "Articles" :postamble-time-prefix "Last updated on") ,(my/org-publish-org-project "k12/11/wri/a" :base-directory "~/k12/11/wri/a" :publishing-directory "~/www/simon/www/k12/11/wri/a" :sitemap-title "Writings" :html-simple-title-suffix " - Simon's K12" :postamble-time-prefix "Last updated on") ("k12/css" :base-directory "~/k12" :publishing-directory "~/www/simon/www/k12" :base-extension "css" :recursive t :publishing-function org-publish-attachment) ("k12" :components ("k12/11/wri/a" "k12/css")) ,(my/org-publish-org-project "www/misc" :base-directory "~/www/simon/src" :publishing-directory "~/www/simon/www" :html-simple-postamble "\n" :auto-sitemap nil) ("www/rest" :base-directory "~/www/simon/src" :publishing-directory "~/www/simon/www" :recursive t :publishing-function org-publish-attachment :base-extension any :exclude ,(rx (or ".org" "~") string-end)) ("www" :components ("www/rest" "www/misc" "www/log" "www/doc"))) org-publish-use-timestamps-flag nil)) (use-package ox-latex :if (group-enabled-p 'org) :config (setf (alist-get "leaflet" org-latex-classes nil nil #'equal) '("\\documentclass[11pt,notumble]{leaflet}" ("\\section{%s}" . "\\section*{%s}") ("\\subsection{%s}" . "\\subsection*{%s}") ("\\subsubsection{%s}" . "\\subsubsection*{%s}")) (alist-get "letter" org-latex-classes nil nil #'equal) '("\\documentclass[11pt]{letter}" ("\\section{%s}" . "\\section*{%s}") ("\\subsection{%s}" . "\\subsection*{%s}") ("\\subsubsection{%s}" . "\\subsubsection*{%s}") ("\\paragraph{%s}" . "\\paragraph*{%s}") ("\\subparagraph{%s}" . "\\subparagraph*{%s}")))) (use-package gnuplot :ensure t :if (group-enabled-p 'org)) (use-package outline :init (add-to-list 'desktop-locals-to-save 'outline-regexp) :config (setq outline-minor-mode-cycle t) (defun outline-guess-regexp () (interactive) (setq-local outline-regexp (concat "[ \t]*\\(" (regexp-quote comment-start) "\\)+ *[*]+"))) (defun my/outline-minor-mode-hook () (when (derived-mode-p 'prog-mode) (outline-guess-regexp))) (add-hook 'outline-minor-mode-hook 'my/outline-minor-mode-hook)) (use-package tex-mode :config (setq tex-indent-basic 0 LaTeX-indent-level 0)) (when-group tex (use-package auctex :ensure t) (use-package cdlatex :ensure t :config (setq cdlatex-takeover-subsuperscript nil)) (with-eval-after-load 'org (keymap-set org-mode-map "C-c d" 'org-cdlatex-mode) (keymap-modify org-cdlatex-mode-map "_" nil "^" nil)) ;; from https://karthinks.com/software/scaling-latex-previews-in-emacs (defun my/text-scale-adjust-latex-previews () "Adjust the size of latex preview fragments when changing the buffer's text scale." (pcase major-mode ('latex-mode (dolist (ov (overlays-in (point-min) (point-max))) (if (eq (overlay-get ov 'category) 'preview-overlay) (my/text-scale--resize-fragment ov)))) ('org-mode (dolist (ov (overlays-in (point-min) (point-max))) (if (eq (overlay-get ov 'org-overlay-type) 'org-latex-overlay) (my/text-scale--resize-fragment ov)))))) (defun my/text-scale--resize-fragment (ov) (overlay-put ov 'display (cons 'image (plist-put (cdr (overlay-get ov 'display)) :scale (+ 1.0 (* 0.25 text-scale-mode-amount)))))) (add-hook 'text-scale-mode-hook 'my/text-scale-adjust-latex-previews)) (use-package auth-source-pass :init (auth-source-pass-enable)) (use-package pinentry :init (when (fboundp 'pinentry-start) (pinentry-start t))) (use-package mm-decode :if (executable-find "w3m") :config (setq mm-text-html-renderer 'gnus-w3m)) (use-package sendmail :if (group-enabled-p 'mail) :init (setq mail-default-directory "~/mail" send-mail-function 'sendmail-send-it sendmail-program (executable-find "msmtp") mail-specify-envelope-from t mail-envelope-from 'header)) (use-package message :if (group-enabled-p 'mail) :init (setq message-directory mail-default-directory message-alternative-emails (rx (or "simonparri@ganzeria.com" "simon@zoar.cx" "marsironpi@ba.ln.ea.cx" "2025sparri@sullivan.k12.il.us"))) :config (add-hooks 'message-mode-hook 'electric-indent-mode)) (use-package gnus :if (group-enabled-p 'mail) :init (setq gnus-directory "~/.news/") :config (setq gnus-select-method '(nnnil "") gnus-secondary-select-methods (cl-flet ((maildir (name) `(nnmaildir ,(format "%s" name) (directory ,(format "~/mail/%s" name)) (gnus-search-engine gnus-search-mu)))) `(,@(mapcar #'maildir '(ganzeria ba.ln.ea.cx sullivan-schools)) (nntp "news.gwene.org") (nntp "news.eternal-september.org" (nntp-authinfo-user "MarsIronPI") (nntp-authinfo-force t)))) gnus-search-mu-remove-prefix (expand-file-name "~/mail") gnus-plugged nil gnus-message-archive-method nil gnus-activate-level 3 gnus-summary-line-format "%U%O %6i %4t %(%16&user-date; %-30,30f %B%S%)\n" gnus-summary-dummy-line-format gnus-summary-line-format gnus-user-date-format-alist '((t . "%Y-%m-%d %H:%M")) gnus-sum-thread-tree-false-root "" gnus-sum-thread-tree-indent " " gnus-sum-thread-tree-leaf-with-other "├► " gnus-sum-thread-tree-root "" gnus-sum-thread-tree-single-leaf "╰► " gnus-sum-thread-tree-vertical "│" gnus-summary-thread-gathering-function 'gnus-gather-threads-by-references gnus-summary-make-false-root 'dummy gnus-thread-sort-functions '(gnus-thread-sort-by-date gnus-thread-sort-by-score) gnus-refer-thread-use-search t gnus-simplify-subject-functions '(gnus-simplify-subject-re gnus-simplify-whitespace) nntp-connection-timeout 10 gnus-agent-auto-agentize-methods '(nntp) gnus-agent-queue-mail nil gnus-score-thread-simplify t gnus-use-adaptive-scoring t gnus-decay-scores "\\.ADAPT\\'" gnus-score-decay-constant 1 gnus-default-adaptive-score-alist '((gnus-killed-mark (subject -10)) (gnus-catchup-mark (subject -2)))) (add-hooks 'gnus-group-mode-hook 'gnus-topic-mode) (add-hooks 'gnus-article-mode-hook 'variable-pitch-mode)) (use-package message :if (executable-find "mu") :if (group-enabled-p 'mail) :config ;; based on `org-contacts-message-complete-function' (defun mu-cfind-completion-function () (when (mail-abbrev-in-expansion-header-p) (let ((beg (save-match-data (save-excursion (re-search-backward "[\n:,][ \t]*") (goto-char (match-end 0)) (point)))) (end (point))) (list beg end (completion-table-dynamic (lambda (string) (let ((jsonstr (thread-last (shell-quote-argument string) (format "mu cfind --format=json %s 2>/dev/null") shell-command-to-string))) (when (not (string-empty-p jsonstr)) (thread-last (json-parse-string jsonstr) (mapcar (lambda (h) (gethash "display" h)))))))) :exclusive 'no)))) (add-hook 'message-mode-hook (lambda () (add-hook 'completion-at-point-functions 'mu-cfind-completion-function nil t)))) (use-package jabber :ensure t :init (keymap-set ctl-x-map "C-j" #'dired-jump) (keymap-global-set "H-c" jabber-global-keymap) :config (defun my/jabber-jabber-activity-show-p (jid) (and (jabber-activity-show-p-default jid) (not (member jid (mapcar #'car *jabber-active-groupchats*))))) (setq jabber-activity-show-p #'my/jabber-jabber-activity-show-p jabber-alert-muc-hooks '(jabber-muc-scroll) jabber-muc-decorate-presence-patterns '(("^.+ has left the chatroom$" . nil) ("^.+ enters the room (.+)$" . nil) ("." . jabber-muc-presence-dim)) jabber-account-list '(("marsironpi@ba.ln.ea.cx")))) (use-package proced :config (setq-default proced-auto-update-flag t)) (use-package piper :vc (:url "https://gitlab.com/howardabrams/emacs-piper")) (use-package dired-subtree :ensure t) (use-package dired :config (require 'em-ls) (eshell-ls-enable-in-dired) (setq eshell-ls-use-in-dired t dired-dwim-target t dired-mark-region t) (defun dired-do-async-command-silent (command) "Run a shell command COMMAND like dired-do-async-shell-command, but without an output buffer. Only works on a single file." (interactive `(,(read-shell-command (format "& on %s: " (car (dired-get-marked-files t)))))) (let ((cmd (concat command " \"" (car (dired-get-marked-files)) "\""))) (spawn-shell-cmd cmd nil cmd))) (keymap-modify dired-mode-map "C-c &" 'dired-do-async-command-silent "TAB" 'dired-subtree-cycle) (defvar dired-id3--hist () "History for dired-id3") (defun dired-id3 (file) (interactive (list (or (car (dired-get-marked-files t)) (expand-file-name (read-file-name "MP3 File: "))))) (if (executable-find "mid3v2") (let ((command "mid3v2")) (mapc (lambda (lst) (let* ((name (car lst)) (option (cadr lst)) (value (read-string (concat name ": ") nil dired-id3--hist))) (if (not (string= value "")) (setq command (concat command " " option " " (shell-quote-argument value)))))) '(("Title" "-t") ("Artist" "-a") ("Album" "-A") ("Comment" "-c") ("Genre" "-g") ("Year" "-y") ("Track" "-T"))) (shell-command (concat command " " file))) (message "Unable to run. Install mid3v2 and try again.")))) (progn ;; dired-sidebar (defvar dired-sidebar-name "*Dired-sidebar*") (defun dired-sidebar (&optional dir) (interactive (list (when current-prefix-arg (dired-read-dir-and-switches "")))) (aif (get-buffer-window dired-sidebar-name) (quit-window nil it) (awhen (get-buffer dired-sidebar-name) (kill-buffer it)) (let ((buf (dired-noselect (or dir default-directory)))) (with-current-buffer buf (rename-buffer dired-sidebar-name) (dired-hide-details-mode) (display-buffer buf))))) (keymap-global-set "C-x M-d" 'dired-sidebar)) (use-package image :config (setq image-transform-smoothing t)) (use-package image-dired :init (defvar dired-wallpapers-directory "~/img/wallpapers/") (defun dired-wallpapers () (interactive) (require 'image-dired) (awhen (get-buffer "*wallpapers*") (kill-buffer it)) (awhen (get-file-buffer dired-wallpapers-directory) (with-current-buffer it (revert-buffer nil t))) (let ((image-dired-show-all-from-dir-max-files most-positive-fixnum) (display-buffer-overriding-action '(display-buffer-no-window (allow-no-window . t))) (image-dired-thumbnail-buffer "*wallpapers*")) (with-current-buffer (image-dired dired-wallpapers-directory) (let ((display-buffer-overriding-action '(nil))) (display-buffer (current-buffer)))))) :config (awhen (executable-find "xdg-open") (setq image-dired-external-viewer it)) (defun my/image-dired-display-thumbnail-original-image (&optional arg) (interactive "P") (if-let ((file (image-dired-file-name-at-point))) (progn (find-file file) (if arg (image-transform-original) (image-transform-fit-both))) (if (not (string-equal major-mode "image-dired-thumbnail-mode")) (user-error "Not in image-dired-thumbnail-mode") (when (not file) (user-error "No original file name found"))))) (keymap-modify image-dired-thumbnail-mode-map "n" 'image-dired-next-line "p" 'image-dired-previous-line "f" 'image-dired-forward-image "b" 'image-dired-backward-image " " 'my/image-dired-display-thumbnail-original-image)) (use-package wallpaper :config (defvar my/wallpaper-light nil) (defun my/set-wallpaper (file) (call-process-shell-command (concat "mywal -s " (when my/wallpaper-light "-l ") "-i " (expand-file-name file)))) (setq wallpaper-set-function #'my/set-wallpaper) (define-advice wallpaper-set (:around (oldf file) pass-prefix-arg) (let ((my/wallpaper-light (equal current-prefix-arg '(4)))) (funcall oldf file)))) (use-package wgrep :ensure t :config (setq wgrep-auto-save-buffer t)) (use-package calendar :config (setq calendar-latitude 39.598 calendar-longitude -88.610 calendar-location-name "Sullivan, IL") (calendar-set-date-style 'iso)) (use-package calc :config (setq calc-make-windows-dedicated t calc-symbolic-mode t calc-angle-mode 'rad calc-language 'big calc-prefer-frac t)) (use-package tab-bar :config (setq tab-bar-tab-hints t tab-bar-new-tab-to 'rightmost tab-bar-auto-width-max nil tab-bar-close-button-show nil tab-bar-format '(tab-bar-format-tabs tab-bar-separator)) (keymap-global-set "C-x t M" 'tab-bar-move-tab-backward) (tab-bar-history-mode)) (use-package hide-mode-line :ensure t) (use-package hippie-exp :config (setq hippie-expand-try-functions-list '(try-expand-dabbrev-visible try-expand-dabbrev try-expand-dabbrev-all-buffers try-complete-file-name-partially try-complete-file-name try-expand-dabbrev-from-kill try-expand-whole-kill try-expand-line try-expand-line-all-buffers try-expand-all-abbrevs try-complete-lisp-symbol-partially try-complete-lisp-symbol)) ;; (keymap-global-set " " hippie-expand) ) (use-package special :config (keymap-modify special-mode-map "o" 'other-window "O" (lambda () (interactive) (other-window -1)) "'" 'avy-goto-char)) (use-package conf-space :mode "\\.htaccess\\'") (use-package visual-fill :ensure t) (use-package visual-fill-column :ensure t :config (advice-add 'text-scale-adjust :after #'visual-fill-column-adjust)) (use-package adaptive-wrap :ensure t :config (define-advice adaptive-wrap-prefix-function (:after (beg end) org-indent) (when org-indent-mode (org-indent-refresh-maybe beg end t)))) (use-package text-mode :config (add-hooks 'text-mode-hook 'visual-line-mode 'visual-fill-column-mode 'adaptive-wrap-prefix-mode 'flyspell-mode 'electric-quote-local-mode)) (use-package ispell :init (defun dict-lang-it () "Change the current ispell dictionary to italiano." (interactive) (ispell-change-dictionary "italiano")) (defun dict-lang-enus () "Change the ispell dictionary to en_us." (interactive) (ispell-change-dictionary "en_US"))) (use-package flyspell :config (setq flyspell-check-changes t)) (use-package vertico-posframe :ensure t :config (keymap-unset vertico-multiform-map "M-p") (keymap-set vertico-multiform-map "M-P" #'vertico-multiform-posframe)) (use-package vertico :demand t :ensure t :config (mapc #'require '(vertico-repeat vertico-directory vertico-multiform vertico-posframe)) (setq vertico-count 5 vertico-cycle t vertico-posframe-poshandler #'posframe-poshandler-frame-bottom-center vertico-multiform-commands 'nil) (keymap-modify vertico-map "RET" 'vertico-directory-enter "DEL" 'vertico-directory-delete-char "M-g" 'vertico-multiform-grid "C-c b" 'embark-become "H-b" 'embark-become) (keymap-global-set "H-r" 'vertico-repeat) (add-hook 'rfn-eshadow-update-overlay-hook 'vertico-directory-tidy) (add-hook 'minibuffer-setup-hook 'vertico-repeat-save) (vertico-mode) (vertico-multiform-mode) (when (display-graphic-p) (vertico-posframe-mode))) (use-package consult :demand t :ensure t :config (setq consult-buffer-sources (delq 'consult--source-bookmark consult-buffer-sources) consult-preview-excluded-buffers '(major-mode . exwm-mode) xref-show-xrefs-function 'consult-xref xref-show-definitions-function 'consult-xref) (defvar-keymap consult-mode-map " " 'consult-buffer " " 'consult-buffer-other-window " " 'consult-buffer-other-window " " 'consult-yank-pop " " 'consult-imenu " " 'consult-locate " " 'consult-find " " 'consult-keep-lines " " 'consult-goto-line " " 'consult-complex-command " " 'consult-history " " 'consult-history "C-x r C-c" 'consult-register "C-x r C-f" 'consult-register-load "C-x r C-t" 'consult-register-store "C-x M-@" 'consult-global-mark "H-d c" 'consult-compile-error "H-d f" 'consult-flymake) (if (executable-find "rg") (keymap-set consult-mode-map " " 'consult-ripgrep) (keymap-set consult-mode-map " " 'consult-grep)) (keymap-global-set "M-g M-i" 'consult-imenu-multi) (define-minor-mode consult-mode "Enable consult keybindings." :lighter "" :keymap consult-mode-map :interactive t :global t) (consult-mode)) (use-package consult-dir :ensure t :init (keymap-global-set "H-j" 'consult-dir) (keymap-global-set "H-J" 'consult-dir-jump-file)) (use-package marginalia :demand t :ensure t :config (setq marginalia-align 'right marginalia-field-width 100) (marginalia-mode)) (use-package embark :demand t :ensure t :config (setq embark-prompter 'embark-keymap-prompter embark-quit-after-action nil embark-verbose-indicator-display-action '(display-buffer-in-side-window (side . bottom) (window-height . fit-window-to-buffer))) (defun my/embark-act-and-quit (&optional arg) (interactive "P") (let ((embark-quit-after-action t)) (embark-act arg))) (keymap-global-modify "H-e" 'embark-act "H-a" 'my/embark-act-and-quit "H-t" 'embark-export)) (use-package embark-consult :demand t :ensure t :config (add-hook 'embark-after-export-hook 'consult-preview-at-point-mode)) (use-package company :config (define-advice company-mode (:around (&rest _) disable-company) nil)) (use-package cape :ensure t) (use-package corfu :ensure t :demand t :config (defun corfu-enable-in-minibuffer () "Enable Corfu in the minibuffer if `completion-at-point' is bound." (when (where-is-internal #'completion-at-point (list (current-local-map))) (corfu-mode))) (add-hook 'minibuffer-setup-hook 'corfu-enable-in-minibuffer) (keymap-set corfu-map "M-'" 'corfu-quick-jump) (global-corfu-mode)) (use-package face-remap :config (diminish 'buffer-face-mode)) (use-package pcmpl-args :ensure t) (use-package eshell :init (defun eshell-at-home (&optional arg) (interactive "P") (let ((default-directory "~/")) (eshell arg))) (setq eshell-banner-message "" eshell-show-lisp-completions t eshell-hist-ignoredups t eshell-status-in-mode-line t eshell-default-target-is-dot t eshell-visual-options nil eshell-visual-subcommands nil eshell-visual-commands nil eshell-history-size 5000 eshell-history-append t eshell-modules-list '( eshell-alias eshell-banner eshell-basic eshell-cmpl eshell-dirs eshell-elecslash eshell-extpipe eshell-glob eshell-hist eshell-ls eshell-pred eshell-prompt eshell-script eshell-term eshell-unix)) (setenv "PAGER" "cat") (setenv "EDITOR" "emacsclient") :config (defun eshell-get-last-output () (buffer-substring (eshell-beginning-of-output) (eshell-end-of-output))) (defvar-local eshell-output-history []) (defun eshell-save-output () (setq eshell-output-history (vconcat eshell-output-history `[,(eshell-get-last-output)]))) (defun eshell-get-output-from-buffer (n &optional buffer) (let ((buffer (or (and buffer (get-buffer buffer)) (get-buffer (concat "*eshell*" (if (equal buffer "0") "" (format "<%s>" buffer)))) (current-buffer)))) (with-current-buffer buffer (elt eshell-output-history n)))) (defun my/eshell-face (str) (propertize str 'font-lock-face 'eshell-prompt)) (defun my/vc-dirty-p (&optional dir) (let ((dir (if (and (not dir) (project-current)) (project-root (project-current)) dir))) (when dir (thread-last (directory-files dir) (mapcar #'vc-state) (seq-some (lambda (x) (not (member x '(nil up-to-date))))))))) (defun my/eshell-vcs-status (&optional dir) (if-let* ((dir (or dir (eshell/pwd))) (prj (project-current nil dir)) (dir (project-root prj)) (vc (vc-responsible-backend dir))) (let* ((dirty (if (my/vc-dirty-p dir) "*" "")) (vc-name (if (eq vc 'Git) "" (downcase (symbol-name vc)))) (vc-branch (when (eq vc 'Git) (let ((default-directory dir)) (require 'magit) (or (magit-get-current-tag) (magit-get-current-branch))))) (vc-branch (aif vc-branch (concat (propertize "on " 'font-lock-face 'shadow) (my/eshell-face it)) ""))) (format "(%s%s%s)" (my/eshell-face vc-name) vc-branch (my/eshell-face dirty))) "")) (defvar-local eshell-prompt-number 0) (defun my/eshell-prompt () (prog1 (concat (propertize (let ((vc (my/eshell-vcs-status))) (format "%s %s %s%s%s" (propertize (format "%s" eshell-prompt-number) 'font-lock-face 'shadow) (my/eshell-face (abbreviate-file-name (eshell/pwd))) vc (if (equal vc "") "" " ") (my/eshell-face (if (= (user-uid) 0) "#" "$")))) 'read-only t) " ") (cl-incf eshell-prompt-number))) (setq eshell-prompt-function #'my/eshell-prompt eshell-highlight-prompt nil) (define-advice eshell-next-prompt (:around (oldf &rest args) assume-highlighted) (let ((eshell-highlight-prompt t)) (apply oldf args))) (define-advice eshell-fix-bad-commands (:around (&rest ignore) disable) nil) (defun eshell-read-aliases-list () (setq eshell-command-aliases-list '(("la" "ls -a $*") ("ll" "ls -l $*") ("lla" "ls -al $*") ("l" "ls $*") ("rtv" "rtv --enable-media $*") ("dquilt" "quilt --quiltrc=~/.quiltrc-dpkg $*") ("gaac" "git annex autocommit -a $*") ("gaag" "git annex get --auto $*") ("gasy" "git annex sync $*") ("gaas" "git annex autosync $*") ("gage" "git annex get $*") ("ip-u" "ip-update basil") ("-" "cd -") ("e" "find-file $1")))) (eshell-read-aliases-list) (defun eshell-write-aliases-list () nil) (defalias 'eshell/date nil) (define-advice eshell-ls-decorated-name (:filter-return (val) add-link) (require 'tramp) (if (eq insert-func #'eshell-buffered-print) (let ((fname (tramp-handle-expand-file-name val))) (propertize val 'button t 'follow-link t 'category t 'keymap button-map 'action (lambda (&rest _) (find-file fname)))) val)) (defun eshell/from (&rest args) (eshell-eval-using-options "from" args `((?h "help" nil nil "output this help screen") (?b "buffer" t buffer "use a different Eshell buffer") :usage "[OPTIONS] N Output the command output from the Nth command from the desired Eshell buffer.") (cl-destructuring-bind (n) args (setq n (string-to-number n)) (cl-assert (numberp n) nil "from: argument must be a number: %s" (car args)) (eshell-get-output-from-buffer n buffer)))) (put 'eshell/from 'eshell-no-numeric-conversions t) (defun eshell/ssh (&rest args) (throw 'eshell-replace-command (if (or eshell-in-pipeline-p (not (= (length args) 1))) (eshell-parse-command "*ssh" args) (eshell-parse-command "cd" (list (concat "/ssh:" (car args) ":~/")))))) (put 'eshell/ssh 'eshell-no-numeric-conversions t) (defun eshell/mpc (&rest args) (if args (throw 'eshell-replace-command (eshell-parse-command "*mpc" args)) (progn (mpc) nil))) (add-to-list 'eshell-complex-commands "ssh") (add-to-list 'eshell-complex-commands "mpc") (defun my/eshell-remove-trailing-space (beg end) (replace-regexp-in-region " +\\'" "" beg end)) (defun my/eshell-clear () (interactive) (let ((eshell-buffer-maximum-lines 0)) (save-excursion (eshell-truncate-buffer)))) (defun my/eshell-cd () (interactive) (eshell/cd (read-directory-name "Change directory: " nil nil t)) (let (beg end) (save-excursion (eshell-bol) (setq beg (point)) (end-of-line) (setq end (point))) (let ((cur (buffer-substring beg end))) (delete-region beg end) (eshell-send-input) (insert cur)))) (defun my/eshell-mode-hook () (add-hook 'eshell-expand-input-functions 'my/eshell-remove-trailing-space nil t) (add-hook 'eshell-post-command-hook 'eshell-save-output 99 t)) (add-hook 'eshell-mode-hook 'my/eshell-mode-hook) :config/el-patch (defun eshell (&optional arg) "Create an interactive Eshell buffer. Start a new Eshell session, or switch to an already active session. Return the buffer selected (or created). With a nonnumeric prefix arg, create a new session. With a numeric prefix arg (as in `\\[universal-argument] 42 \\[eshell]'), switch to the session with that number, or create it if it doesn't already exist. The buffer name used for Eshell sessions is determined by the value of `eshell-buffer-name', which see. Eshell is a shell-like command interpreter. For more information on Eshell, see Info node `(eshell)Top'." (interactive "P") (cl-assert eshell-buffer-name) (let ((buf (cond ((numberp arg) (get-buffer-create (format "%s<%d>" eshell-buffer-name arg))) (arg ((el-patch-swap generate-new-buffer get-buffer-create) eshell-buffer-name)) (t ((el-patch-swap get-buffer-create generate-new-buffer) eshell-buffer-name))))) (cl-assert (and buf (buffer-live-p buf))) (with-suppressed-warnings ((obsolete display-comint-buffer-action)) (pop-to-buffer buf display-comint-buffer-action)) (unless (derived-mode-p 'eshell-mode) (eshell-mode)) buf))) (use-package em-ls :config (setq eshell-ls-date-format "%Y-%m-%d %H:%M") :config/el-patch (defun eshell-ls-file (fileinfo &optional size-width copy-fileinfo) "Output FILEINFO in long format. FILEINFO may be a string, or a cons cell whose car is the filename and whose cdr is the list of file attributes." (if (not (cdr fileinfo)) (funcall error-func (format "%s: No such file or directory\n" (car fileinfo))) (setq fileinfo (eshell-ls-annotate (if copy-fileinfo (cons (car fileinfo) (cdr fileinfo)) fileinfo))) (let ((file (car fileinfo)) (attrs (cdr fileinfo))) (if (not (eq listing-style 'long-listing)) (if show-size (funcall insert-func (eshell-ls-size-string attrs size-width) " " file "\n") (funcall insert-func file "\n")) (let ((line (concat (if show-size (concat (eshell-ls-size-string attrs size-width) " ")) (format (if numeric-uid-gid "%s%4d %-8s %-8s " "%s%4d %-14s %-8s ") (or (file-attribute-modes attrs) "??????????") (or (file-attribute-link-number attrs) 0) (or (let ((user (file-attribute-user-id attrs))) (and (stringp user) (eshell-substring user 14))) (file-attribute-user-id attrs) "") (or (let ((group (file-attribute-group-id attrs))) (and (stringp group) (eshell-substring group 8))) (file-attribute-group-id attrs) "")) (let* ((str (eshell-ls-printable-size (file-attribute-size attrs))) (len (length str))) ;; Let file sizes shorter than 9 align neatly. (if (< len (or size-width 8)) (concat (make-string (- (or size-width 8) len) ? ) str) str)) " " (format-time-string (el-patch-swap (concat eshell-ls-date-format " " (if (= (decoded-time-year (decode-time)) (decoded-time-year (decode-time (nth (cond ;; What /is/ this stupid thing? ((eq sort-method 'by-atime) 4) ;; Who ever asked for /this/ ((eq sort-method 'by-ctime) 6) ;; insane, broken behavior? (t 5)) attrs)))) "%H:%M" " %Y")) eshell-ls-date-format) (nth (cond ((eq sort-method 'by-atime) 4) ((eq sort-method 'by-ctime) 6) (t 5)) attrs)) " "))) (funcall insert-func line file "\n")))))) (defun eshell-ls--insert-directory (orig-fun file switches &optional wildcard full-directory-p) "Insert directory listing for FILE, formatted according to SWITCHES. Leaves point after the inserted text. SWITCHES may be a string of options, or a list of strings. Optional third arg WILDCARD means treat FILE as shell wildcard. Optional fourth arg FULL-DIRECTORY-P means file is a directory and switches do not contain `d', so that a full listing is expected. This version of the function uses `eshell/ls'. If any of the switches passed are not recognized, the operating system's version will be used instead." (el-patch-add (defvar insert-func)) (if (not eshell-ls-use-in-dired) (funcall orig-fun file switches wildcard full-directory-p) (let ((handler (find-file-name-handler file 'insert-directory))) (if handler (funcall handler 'insert-directory file switches wildcard full-directory-p) (if (stringp switches) (setq switches (split-string switches))) (let (eshell-current-handles eshell-current-subjob-p font-lock-mode) ;; use the fancy highlighting in `eshell-ls' rather than font-lock (when eshell-ls-use-colors (el-patch-remove (font-lock-mode -1)) (setq font-lock-defaults nil)) (require 'em-glob) (let* ((insert-func 'insert) (error-func 'insert) (eshell-error-if-no-glob t) (target ; Expand the shell wildcards if any. (if (and (atom file) (string-match "[[?*]" file) (not (file-exists-p file))) (mapcar #'file-relative-name (eshell-extended-glob file)) (file-relative-name file))) (switches (append eshell-ls-dired-initial-args (and (or (consp dired-directory) wildcard) (list "-d")) switches))) (eshell-do-ls (nconc switches (list target)))))))))) (use-package esh-mode :config (keymap-modify eshell-mode-map "C-l" 'my/eshell-clear "C-c d" 'my/eshell-cd "C-c C-f" 'insert-file-name)) (use-package esh-var :config (setf (alist-get "$" eshell-variable-aliases-list nil nil #'equal) ;; Don't ask, it works. '((lambda (indices) (let ((v eshell-output-history)) (cond (indices (eshell-apply-indices v indices)) (t (elt v (1- (length v)))))))))) (use-package fish-completion :if (executable-find "fish") :ensure t :config (setq fish-completion-fallback-on-bash-p t) (add-hook 'eshell-mode 'fish-completion-mode)) (use-package bash-completion :disabled t :if (and (executable-find "bash") (not (executable-find "fish"))) :ensure t :init (defun my/eshell-add-bash-completion-function () (add-hook 'completion-at-point-functions 'bash-completion-dynamic-complete-nocomint nil t)) (unless (package-installed-p 'fish-completion) (add-hook 'eshell-mode-hook 'my/eshell-add-bash-completion-function))) (use-package eat :ensure t :init (with-eval-after-load 'eshell (eat-eshell-mode)) :config (defun my/eat-kill-after-exit (proc) (kill-buffer (process-buffer proc))) (add-hook 'eat-exit-hook 'my/eat-kill-after-exit)) (use-package pdf-view-reader :if (locate-library "pdf-view") :ensure t :vc (:url "https://ba.ln.ea.cx/src/marsironpi/emacs/pdf-view-reader")) (use-package pdf-tools :if (locate-library "pdf-view") :autoload pdf-view-mode :init (add-to-list 'auto-mode-alist '("\\.pdf\\'" . pdf-view-mode)) (pdf-view-desktop-setup) :config (keymap-modify pdf-view-mode-map "M-g M-g" #'pdf-view-goto-page "M-g g" #'pdf-view-goto-page "d" #'pdf-view-book "o" #'pdf-view-offset-current-page "O" #'pdf-view-offset-set)) (use-package nov :if (group-enabled-p 'media) :ensure t :init (add-to-list 'auto-mode-alist '("\\.epub3?\\'" . nov-mode)) :config (setq nov-save-place-file nil) (add-hooks 'nov-mode-hook 'visual-line-mode 'visual-fill-column-mode)) (progn ;; pulseaudio (defun pulseaudio--list-type (type) (thread-last (shell-command-to-string (concat "pactl list short " (symbol-name type))) (string-chop-newline) (string-lines) (mapcar #'string-chop-newline) (mapcar (lambda (s) (cadr (string-match-substrings "^[0-9]+\t\\(.+?\\)\t.*$" s)))))) (defun pulseaudio--sinks () (pulseaudio--list-type 'sinks)) (defun pulseaudio--sources () (pulseaudio--list-type 'sources)) (defvar pulseaudio--sink-history ()) (defun pulseaudio--read-sink (&optional prompt) (completing-read (or prompt "Sink: ") (pulseaudio--sinks) nil t nil 'pulseaudio--sink-history)) (defun pulseaudio-set-default-sink (sink) (interactive (list (pulseaudio--read-sink))) (call-process-shell-command (concat "pactl set-default-sink " (shell-quote-argument sink)))) (defun pulseaudio-add-remote-sink (host) (interactive "sHost: ") (call-process-shell-command (concat "pactl load-module module-tunnel-sink server=" (shell-quote-argument host)))) (defun pulseaudio-remove-remote-sink () (interactive) (call-process-shell-command "pactl unload-module module-tunnel-sink")) (defun pulseaudio-mute (&optional arg) (interactive) (let ((state (cl-case arg (mute "1") (unmute "0") (otherwise "toggle")))) (call-process-shell-command (concat "pactl set-sink-mute @DEFAULT_SINK@ " state)))) (defun pulseaudio-change-sink (amt &optional sink) (interactive "P") (call-process-shell-command (concat "pactl set-sink-volume @DEFAULT_SINK@ " (format "%s%s%%" (if (cl-plusp amt) "+" "") amt)))) (defvar pulseaudio-default-amt 5) (defun pulseaudio-sink-raise (&optional amt) (interactive "P") (pulseaudio-change-sink (or amt pulseaudio-default-amt))) (defun pulseaudio-sink-lower (&optional amt) (interactive "P") (pulseaudio-change-sink (- (or amt pulseaudio-default-amt))))) (use-package empv :if (group-enabled-p 'music) :commands (empv-play-media-at-point) :ensure t :init (with-eval-after-load 'dired (keymap-set dired-mode-map "e" 'empv-play-media-at-point)) :config (setq empv-playlist-dir (xdg-user-dir "MUSIC") empv-base-directory empv-playlist-dir empv-use-consult-if-possible nil empv-invidious-instance "https://yewtu.be/api/v1" empv-mpv-args (cl-delete "--no-video" empv-mpv-args :test #'equal)) (defvar empv-mode-line-format (concat " " (propertize "%t" 'face 'bold) " " (propertize "[%T/%D]" 'face 'italic))) (defvar empv-mode-line-string) (defvar empv-mode-line-timer) (defun empv-mode-line-update () (cl-flet ((time-str (sec total) (if (> (or total 0) (* 60 60)) (format-seconds "%.2h:%.2m:%.2s" (or sec 0)) (format-seconds "%.2m:%.2s" (or sec 0))))) (empv--let-properties '(time-pos duration metadata path) (if .path (let ((time-pos (time-str .time-pos .duration)) (duration (time-str .duration .duration)) (progress (when (and .time-pos .duration) (thread-last (/ .time-pos .duration) (* 100) (format "%d%%")))) (path .path)) (let-alist .metadata (setq empv-mode-line-string (format-spec empv-mode-line-format `((?t . ,(or (and .TITLE (string-limit .TITLE 50)) (string-limit path 50 t))) (?a . ,(or .ARTIST "")) (?A . ,(or .ALBUM "")) (?G . ,(or .GENRE "")) (?p . ,(or progress "")) (?T . ,(or time-pos "")) (?D . ,(or duration "")))))) (force-mode-line-update)) (setq empv-mode-line-string ""))))) (define-minor-mode empv-mode-line-mode "" :global t (if empv-mode-line-mode (progn (add-to-list 'global-mode-string 'empv-mode-line-string t) (setq empv-mode-line-timer (run-at-time t 2 #'empv-mode-line-update))) (delq 'empv-mode-line-string global-mode-string) (cancel-timer empv-mode-line-timer))) (empv-mode-line-mode) :config/el-patch (defun empv-toggle-video () "Toggle the video display. You can press \"_\" to hide it again when you are focused on MPV." (interactive) (empv--cmd 'cycle (el-patch-swap 'video 'force-window)))) (use-package yeetube :config (require 'xdg) (setq yeetube-results-limit 30 yeetube-download-directory (xdg-user-dir "DOWNLOAD"))) (use-package goggles :diminish :demand t :ensure t :config (define-globalized-minor-mode global-goggles-mode goggles-mode goggles-mode) (global-goggles-mode)) (use-package avy :ensure t :init (keymap-global-modify "C-'" 'avy-goto-char "H-'" 'avy-goto-char) :config ;; Much from https://karthinks.com/software/avy-can-do-anything/ (defun avy-action-embark (pt) (unwind-protect (save-excursion (goto-char pt) (embark-act)) (select-window (cdr (ring-ref avy-ring 0)))) t) (defun avy-action-mark-to-char (pt) (activate-mark) (goto-char pt)) (defun avy-action-recenter (pt) (save-excursion (goto-char pt) (recenter nil t)) t) (defun avy-action-recenter-to-top (pt) (save-excursion (goto-char pt) (recenter 0 t)) t) (defun avy-action-recenter-to-bottom (pt) (save-excursion (goto-char pt) (recenter -1 t)) t) (setf (alist-get ?e avy-dispatch-alist) 'avy-action-embark (alist-get ? avy-dispatch-alist) 'avy-action-mark-to-char (alist-get ? avy-dispatch-alist) 'avy-action-recenter (alist-get ?r avy-dispatch-alist) 'avy-action-recenter-to-top (alist-get ?R avy-dispatch-alist) 'avy-action-recenter-to-bottom)) (use-package ace-window :ensure t :init (keymap-global-set "C-x o" 'ace-window) :config (setq aw-keys (seq-remove (lambda (x) (memq x '( ?x ?m ?M ?c ?j ?n ?u ?e ?F ?v ?b ?o ?T))) (number-sequence ?a ?z)) aw-scope 'frame) (add-to-list 'aw-ignored-buffers "*Embark Actions*")) (use-package golden-ratio :ensure t :config (setq golden-ratio-extra-commands '(windmove-left windmove-right windmove-down windmove-up ace-window go-next-window go-previous-window)) :config/el-patch (defun golden-ratio (&optional arg) "Resizes current window to the golden-ratio's size specs." (interactive "p") (unless (or (el-patch-remove (and (not golden-ratio-mode) (null arg))) (window-minibuffer-p) (one-window-p) (golden-ratio-exclude-major-mode-p) (member (buffer-name) golden-ratio-exclude-buffer-names) (and golden-ratio-exclude-buffer-regexp (loop for r in golden-ratio-exclude-buffer-regexp thereis (string-match r (buffer-name)))) (and golden-ratio-inhibit-functions (loop for fun in golden-ratio-inhibit-functions thereis (funcall fun)))) (let ((dims (golden-ratio--dimensions)) (golden-ratio-mode nil)) ;; Always disable `golden-ratio-mode' to avoid ;; infinite loop in `balance-windows'. (let (window-configuration-change-hook) (balance-windows) (golden-ratio--resize-window dims) (when golden-ratio-recenter (scroll-right) (recenter))) (run-hooks 'window-configuration-change-hook))))) (use-package isearch :config (setq isearch-allow-motion t) (keymap-modify isearch-mode-map " " 'avy-isearch " " 'isearch-query-replace " " 'isearch-query-replace-regexp)) (use-package anzu :demand t :ensure t :diminish :init (global-anzu-mode) (defvar-keymap anzu-replace-mode-map " " 'anzu-query-replace " " 'anzu-query-replace-regexp " " 'anzu-isearch-query-replace " " 'anzu-isearch-query-replace-regexp) (define-minor-mode anzu-replace-mode "Rebind query-replace and friends to anzu equivalents." :lighter "" :keymap anzu-replace-mode-map :interactive t :global t) (anzu-replace-mode)) (use-package expand-region :ensure t :init (keymap-global-set "C-=" 'er/expand-region)) (use-package hydra :ensure t) (progn ;; hydra-modes (defhydra hydra-modes (global-map "H-m") "Modes" ("s" electric-pair-mode "Electric Pair Mode") ("f" flymake-mode "Flymake") ("o" outline-minor-mode "Outline") ("(" puni-mode "Puni") ("v" view-mode "View Mode") ("F" which-function-mode "Which-Function Mode") ("m" minimap-mode "Minimap") ("V" variable-pitch-mode "Variable Pitch Mode") ("M" hide-mode-line-mode "Toggle Mode Line") ("T" toggle-transparency "Toggle transparency") ("W" global-subword-mode "Subword mode") ("d" eldoc-mode "ElDoc mode") ("w" visual-fill-column-mode "Visual Fill Column") ("$" global-show-trailing-whitespace-mode "Show trailing whitespace"))) (use-package multiple-cursors :ensure t :init (keymap-global-set "C-;" 'mc/mark-all-dwim) (defhydra hydra-multiple-cursors (global-map "H-;") ("l" mc/edit-lines "Beginning of lines") ("L" mc/edit-ends-of-lines "Ends of lines") ("n" mc/mark-next-like-this "Mark next") ("N" mc/unmark-next-like-this "Unmark next") ("p" mc/mark-previous-like-this "Mark previous") ("P" mc/unmark-previous-like-this "Unmark previous") ("m" mc/mark-more-like-this-extended "Mark more") ("a" mc/mark-all-dwim "Mark all (dwim)") ("A" mc/insert-letters "Insert letters") ("!" mc/insert-numbers "Insert numbers") ("|" mc/vertical-align "Align cursors")) :config (mapc (lambda (h) (let ((cmd (intern (format "hydra-multiple-cursors/%s" (cadr h))))) (cl-pushnew cmd mc/cmds-to-run-once))) hydra-multiple-cursors/heads)) (use-package prog-mode :config (defun my/beginning-point-toplevel-sexp () (save-excursion (condition-case err (progn (while (progn (backward-up-list) t))) (scan-error (point))))) (defun my/ending-point-toplevel-sexp () (save-excursion (goto-char (my/beginning-point-toplevel-sexp)) (if (bound-and-true-p puni-mode) (puni-forward-sexp) (forward-sexp)) (point))) (defun my/indent-toplevel-sexp () (interactive) (indent-region (my/beginning-point-toplevel-sexp) (my/ending-point-toplevel-sexp))) (when (version< emacs-version "30.0") (keymap-set prog-mode-map "M-q" 'my/indent-toplevel-sexp)) (define-advice insert-pair (:after (&rest _) indent-sexp-when-prog) (when (derived-mode-p 'prog-mode) (if (version< emacs-version "30.0") (my/indent-toplevel-sexp) (prog-fill-reindent-defun)))) (add-hooks 'prog-mode-hook (lambda () (indent-tabs-mode -1)) 'hs-minor-mode)) (use-package puni :if (group-enabled-p 'programming) :ensure t :demand t :init (add-hook 'prog-mode-hook 'puni-mode) :config (setf (alist-get 'puni-mode minor-mode-alist) '(" Puni")) (keymap-modify puni-mode-map "M-s" 'puni-splice "M-?" 'puni-convolute "M-r" 'puni-raise "M-R" 'move-to-window-line-top-bottom "M-J" 'puni-split "M-" 'puni-barf-forward "C-M-" 'puni-slurp-backward "M-" 'puni-slurp-forward "C-M-" 'puni-barf-backward "M-S" search-map "M-(" nil "M-)" nil) (mapc (lambda (sym) (advice-add sym :after #'my/indent-toplevel-sexp)) '(puni-splice puni-raise puni-convolute puni-kill-line))) (use-package hideshow :config (keymap-modify hs-minor-mode-map "H-i i" 'hs-toggle-hiding "H-i s" 'hs-show-all "H-i h" 'hs-hide-all "H-i l" 'hs-hide-level)) (use-package sgml-mode :config (setq sgml-basic-offset 0)) (use-package css-mode :init (enable-ts-mode 'css) :config (setq css-indent-offset 2)) (require 'sgml-mode) (use-package mhtml-mode :init (enable-ts-mode 'html) :config (require 'ruby-mode) (defconst mhtml--ruby-submode (mhtml--construct-submode 'ruby-mode :name "Ruby" :end-tag "" :syntax-table ruby-mode-syntax-table :propertize #'ruby-syntax-propertize :keymap ruby-mode-map)) :config/el-patch (defvar mhtml--syntax-propertize (syntax-propertize-rules ("" (0 (ignore (goto-char (match-end 0)) ;; Don't apply in a comment. (unless (syntax-ppss-context (syntax-ppss)) (mhtml--syntax-propertize-submode mhtml--css-submode end))))) (el-patch-add ("