summaryrefslogtreecommitdiff
path: root/common/.emacs.d
diff options
context:
space:
mode:
Diffstat (limited to 'common/.emacs.d')
-rw-r--r--common/.emacs.d/init.el4240
1 files changed, 4240 insertions, 0 deletions
diff --git a/common/.emacs.d/init.el b/common/.emacs.d/init.el
new file mode 100644
index 0000000..eb18086
--- /dev/null
+++ b/common/.emacs.d/init.el
@@ -0,0 +1,4240 @@
+;; -*- 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))
+
+(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 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-<left>" 'backward-sexp
+ "H-<right>" 'forward-sexp
+ "H-<up>" 'backward-up-list
+ "H-<down>" '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)
+
+ (keymap-global-set "C-x M-s" 'window-toggle-side-windows)
+
+ (keymap-global-set "<remap> <list-buffers>" '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)))
+
+ (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-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</%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 "<br>\n")
+
+ (defsimple horizontal-rule "<hr>\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 "<time datetime=%s>%s</time>"
+ value text)))
+
+ ;; blocks
+ (deftag example "samp"
+ (thread-last cnt
+ (replace-regexp-in-string "\n" "<br>\n")
+ (replace-regexp-in-string "<br>\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 "<input type=checkbox%s disabled> "
+ (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<dd>" 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 "<figure>\n%s<figcaption>\n%s\n</figcaption>\n</figure>\n"
+ contents
+ (org-export-data (org-export-get-caption el) info)))
+ (t (format "<p>\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</%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</%s>" tag (or contents "") tag)))
+
+ (deftag src-block "pre" val :nl t)
+
+ (deftag verse-block "blockquote"
+ (thread-last cnt
+ (replace-regexp-in-string "\n" "<br>\n")
+ (replace-regexp-in-string "<br>\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 "<th>" contents))
+ (t (concat "<td>" 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<thead>\n"
+ "<tbody>\n"))
+ "<tr>" 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<colgroup>\n")
+ ;; Add a column. Also specify its alignment.
+ (format "<col align=%s>" alignment)
+ ;; End a colgroup?
+ (when (org-export-table-cell-ends-colgroup-p
+ cell info)
+ "\n</colgroup>"))))
+ (org-html-table-first-row-data-cells el info) "\n")))
+ (concat
+ "<table>"
+ (when (and
+ caption
+ (not (string-empty-p (org-trim caption))))
+ (format "\n<caption>%s</caption>\n" caption))
+ colgroups
+ "\n"
+ contents
+ "</table>")))
+
+ ;; 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 "<span class=\"" (if (not (string= todo-keyword "TODO"))
+ "todo " "")
+ (downcase todo-keyword) "\">"
+ todo-keyword "</span> ")))
+ (priority-level
+ (org-element-property :priority el))
+ (priority
+ (and priority-level (concat "<span class=priority>" priority-level "</span> ")))
+ (title
+ (org-export-data (org-element-property :title el) info)))
+ (format "%s<h%s>%s%s%s</h%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 "<!doctype html>\n"
+ "<title>"
+ (org-export-data (plist-get info :title) info)
+ (plist-get info :html-simple-title-suffix)
+ "</title>\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
+ "<link rel=stylesheet href=-y/_.css>
+<meta name=viewport content=\"initial-scale=1\">
+
+<main>
+<h1>%s</h1>%s
+<article>
+"
+ (org-export-data (plist-get info :title) info)
+ (if-let ((title (plist-get info :subtitle)))
+ (format "\n<h2>%s</h2>\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))
+ "</article>\n</main>\n"
+ (format
+ "</article>
+</main>
+
+<footer>
+<address>
+<a href=/~simon>Simon Parri</a>
+</address>
+<p>
+%s %s
+</footer>
+"
+ (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 "</main>\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)
+ (thread-last
+ (shell-quote-argument string)
+ (format "mu cfind --format=json %s")
+ shell-command-to-string
+ json-parse-string
+ (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
+ "C-c l" 'bongo-dired-library-mode
+ "C-c m" 'bongo-dired-play-line
+ "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
+ "<remap> <image-dired-display-this>"
+ '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))
+ (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 "<remap> <dabbrev-expand>" 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
+ "<remap> <switch-to-buffer>" 'consult-buffer
+ "<remap> <switch-to-buffer-other-window>" 'consult-buffer-other-window
+ "<remap> <switch-to-buffer-other-frame>" 'consult-buffer-other-window
+
+ "<remap> <yank-pop>" 'consult-yank-pop
+
+ "<remap> <imenu>" 'consult-imenu
+ "<remap> <locate>" 'consult-locate
+ "<remap> <find-dired>" 'consult-find
+
+ "<remap> <keep-lines>" 'consult-keep-lines
+
+ "<remap> <goto-line>" 'consult-goto-line
+
+ "<remap> <repeat-complex-command>" 'consult-complex-command
+
+ "<remap> <eshell-list-history>" 'consult-history
+ "<remap> <comint-dynamic-list-input-ring>" '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 "<remap> <rgrep>" 'consult-ripgrep)
+ (keymap-set consult-mode-map "<remap> <rgrep>" '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-tools
+ :if (locate-library "pdf-view")
+ :autoload pdf-view-mode
+ :load-path ("~/lab/emacs/pdf-view-reader")
+ :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-mode
+ "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 bongo
+ :ensure t
+ :commands (bongo-dired-library-mode
+ bongo-dired-play-line
+ bongo-pause/resume
+ bongo-stop
+ bongo-next
+ bongo-previous)
+ :config
+ (require 'xdg)
+
+ (setq
+ bongo-mpv-extra-arguments ()
+ bongo-default-directory
+ (concat (or (xdg-user-dir "MUSIC")
+ (expand-file-name "~/mus"))
+ "/")
+ bongo-field-separator " — "
+ bongo-display-track-icons nil
+ bongo-insert-album-covers t
+ bongo-mark-played-tracks t
+ bongo-prefer-library-buffers nil
+ bongo-custom-backend-matchers
+ '((mpv . ((local-file "file:" "http:" "https:" "ftp:") "opus" "m4a"))
+ (mpv . ("https:" . t))))
+
+ (defun my/bongo-switch-buffers (&optional prompt)
+ (interactive "P")
+ (let ((dir (if prompt
+ (read-directory-name
+ "Library directory: "
+ bongo-default-directory)
+ bongo-default-directory)))
+ (with-current-buffer (dired-noselect dir)
+ (bongo-dired-library-mode)
+ (display-buffer (current-buffer)))))
+
+ (keymap-modify bongo-playlist-mode-map
+ "<remap> <bongo-switch-buffers>" 'my/bongo-switch-buffers)
+
+ :config/el-patch
+ (defun bongo-compose-remote-option (socket-file)
+ "Get the command line argument for starting mpv's remote interface at SOCKET-FILE."
+ (when (equal bongo-mpv-remote-option 'unknown)
+ (setq bongo-mpv-remote-option (bongo--mpv-get-remote-option)))
+ (el-patch-swap
+ (list bongo-mpv-remote-option socket-file)
+ (list (format "%s=%s" bongo-mpv-remote-option socket-file)))))
+
+(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
+ "<remap> <avy-goto-char>" 'avy-isearch
+ "<remap> <query-replace>" 'isearch-query-replace
+ "<remap> <query-replace-regexp>" 'isearch-query-replace-regexp))
+
+(use-package anzu
+ :demand t
+ :ensure t
+ :diminish
+ :init
+ (global-anzu-mode)
+ (defvar-keymap anzu-replace-mode-map
+ "<remap> <query-replace>" 'anzu-query-replace
+ "<remap> <query-replace-regexp>" 'anzu-query-replace-regexp
+ "<remap> <isearch-query-replace>" 'anzu-isearch-query-replace
+ "<remap> <isearch-query-replace-regexp>" '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-<left>" 'puni-barf-forward
+ "C-M-<left>" 'puni-slurp-backward
+ "M-<right>" 'puni-slurp-forward
+ "C-M-<right>" '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
+ :config
+ (setq css-indent-offset 2))
+
+(require 'sgml-mode)
+(use-package mhtml-mode
+ :config
+ (require 'ruby-mode)
+ (defconst mhtml--ruby-submode
+ (mhtml--construct-submode 'ruby-mode
+ :name "Ruby"
+ :end-tag "</script>"
+ :syntax-table ruby-mode-syntax-table
+ :propertize #'ruby-syntax-propertize
+ :keymap ruby-mode-map))
+ :config/el-patch
+ (defvar mhtml--syntax-propertize
+ (syntax-propertize-rules
+ ("<style.*?>"
+ (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
+ ("<script type=\"?text/html\"?>"
+ (0 nil)))
+ (el-patch-add
+ ("<script type=\"?text/ruby\"?>"
+ (0 (ignore
+ (goto-char (match-end 0))
+ ;; Don't apply in a comment.
+ (unless (syntax-ppss-context (syntax-ppss))
+ (mhtml--syntax-propertize-submode mhtml--ruby-submode end))))))
+ ("<script.*?>"
+ (0 (ignore
+ (goto-char (match-end 0))
+ ;; Don't apply in a comment.
+ (unless (syntax-ppss-context (syntax-ppss))
+ (mhtml--syntax-propertize-submode mhtml--js-submode end)))))
+ sgml-syntax-propertize-rules)))
+
+(when-group programming
+ (use-package yaml-mode
+ :ensure t)
+ (use-package geiser
+ :config
+ (setq
+ geiser-active-implementations '(racket guile)))
+ (use-package geiser-guile
+ :ensure t)
+ (use-package geiser-racket
+ :ensure t)
+ (use-package lua-mode
+ :ensure t
+ :config
+ (add-hook
+ 'lua-mode-hook
+ (lambda ()
+ (setq-local tab-width 3)
+ (indent-tabs-mode 1))))
+ (use-package fennel-mode
+ :ensure t)
+ (use-package coffee-mode
+ :ensure t)
+ (use-package brainfuck-mode
+ :ensure t)
+ (use-package raku-mode
+ :ensure t)
+ (use-package haskell-mode
+ :ensure t)
+ (use-package rainbow-delimiters
+ :ensure t)
+ (use-package rainbow-mode
+ :ensure t)
+ (use-package udev-mode
+ :ensure t)
+ (use-package nix-mode
+ :ensure t))
+
+(use-package osm ;; maximap
+ :if (group-enabled-p 'osm)
+ :if (executable-find "curl")
+ :ensure t
+ :config
+ (require 'calendar))
+
+(use-package impatient-mode
+ :if (group-enabled-p 'programming)
+ :ensure t
+ :config
+ (add-to-list 'imp-default-user-filters '(mhtml-mode . nil)))
+
+(use-package js
+ :mode (".mjs" . js-mode)
+ :config
+ (setq js-indent-level 2))
+
+(use-package cc-mode
+ :config
+ (keymap-set c-mode-map "<F5>" 'recompile)
+ (keymap-set c++-mode-map "<F5>" 'recompile))
+
+(progn ;; tabfs
+ (defvar tabfs-directory (expand-file-name "~/.vimfx/tabfs/fs/mnt"))
+
+ (defun tabfs-exists-p ()
+ (ignore-errors
+ (and (file-exists-p tabfs-directory)
+ (not (null (directory-files tabfs-directory nil "^[^.]"))))))
+
+ (defun tabfs-ensure ()
+ (unless (tabfs-exists-p)
+ (user-error "TabFS not running in \"%s\"" tabfs-directory)))
+
+ (defun tabfs--filename->representation (filename)
+ (string-join
+ (thread-last
+ (string-match-substrings "^\\(.*\\)\\.\\([0-9]+\\)$" filename)
+ cdr
+ reverse
+ (funcall (lambda (tb)
+ (list (car tb)
+ (string-replace "_" " " (cadr tb))))))
+ ": "))
+
+ (defun tabfs--representation->filename (representation)
+ (string-join
+ (thread-last
+ (string-match-substrings "^\\([0-9]+\\): \\(.*\\)$" representation)
+ cdr
+ reverse
+ (funcall (lambda (tb)
+ (list (car tb)
+ (string-replace " " "_" (cadr tb))))))
+ "."))
+
+ (cl-defmacro tabfs--with-filename ((var representation) &body body)
+ (declare (indent 1))
+ `(let ((,var (tabfs--representation->filename ,representation)))
+ ,@body))
+
+ (defun tabfs--directory (filename)
+ (expand-file-name
+ filename
+ (expand-file-name
+ "tabs/by-title"
+ tabfs-directory)))
+ (defun tabfs--tab-window (filename)
+ (expand-file-name "window" (tabfs--directory filename)))
+
+ (defun tabfs-list-tabs ()
+ (tabfs-ensure)
+ (thread-last
+ (expand-file-name "tabs/by-title" tabfs-directory)
+ (funcall
+ (lambda (x) (directory-files x nil "^[^.]")))
+ (mapcar #'tabfs--filename->representation)))
+
+ (defun tabfs-read-tab ()
+ (tabfs-ensure)
+ (let ((tabs (tabfs-list-tabs)))
+ (completing-read "Tab: "
+ tabs
+ nil t nil
+ 'tabfs-tab-history
+ (car tabs))))
+
+ (defun tabfs-switch-tab (tab)
+ (interactive (list (tabfs-read-tab)))
+ (tabfs-ensure)
+ (tabfs--with-filename (f tab)
+ (let* ((t-active (expand-file-name "active" (tabfs--directory f)))
+ (w-active (expand-file-name "focused" (tabfs--tab-window f))))
+ (mapc (lambda (f) (write-string "true" f)) (list t-active w-active)))))
+
+ (defun tabfs-close-tab (tab)
+ (interactive (list (tabfs-read-tab)))
+ (tabfs-ensure)
+ (tabfs--with-filename (f tab)
+ (delete-file (tabfs--directory f))))
+
+ (defun tabfs-current-tab ()
+ (tabfs-ensure)
+ (let ((lastpath
+ (thread-first
+ "tabs/last-focused"
+ (expand-file-name tabfs-directory)
+ file-truename)))
+ (thread-last
+ (expand-file-name "tabs/by-title" tabfs-directory)
+ (funcall
+ (lambda (x) (directory-files x t)))
+ (seq-find
+ (lambda (f)
+ (equal (file-truename f) lastpath)))
+ file-name-nondirectory
+ tabfs--filename->representation)))
+
+ (defvar tabfs-host-history nil)
+ (defun tabfs-send-to-host (host tab)
+ "Sends TAB to HOST via SSH. Assumes HOST has TabFS set up
+exactly like localhost."
+ (interactive
+ (list (read-from-minibuffer "Host: "
+ nil nil nil
+ 'tabfs-host-history
+ (car tabfs-host-history)
+ t)
+ (tabfs-current-tab)))
+ (tabfs-ensure)
+ (let ((url
+ (tabfs--with-filename (dir tab)
+ (thread-last
+ (expand-file-name "tabs/by-title" tabfs-directory)
+ (expand-file-name dir)
+ (expand-file-name "url.txt")
+ file-string))))
+ (with-temp-file
+ (format "/ssh:%s:%s" host
+ (expand-file-name "tabs/create" tabfs-directory))
+ (insert url))))
+
+ (defvar consult--source-tabfs
+ '(:name "Tab"
+ :narrow ?t
+ :category buffer
+ :face consult-buffer
+ :history tabfs-tab-history
+ :items #'tabfs-list-tabs
+ :enabled #'tabfs-exists-p
+ :action #'tabfs-switch-tab)))
+
+(with-eval-after-load 'ol ;; ol-tabfs
+ (defvar org-tabfs-browser-regexp "Mozilla Firefox$")
+ (defun org-tabfs-store-link ()
+ (when (and (tabfs-exists-p)
+ (string-match-p org-tabfs-browser-regexp (buffer-name)))
+ (let* ((cur (tabfs--directory
+ (tabfs--representation->filename
+ (tabfs-current-tab))))
+ (rx (rx bol (or "http" "https" "file")))
+ (url (string-chop-newline
+ (file-string
+ (expand-file-name "url.txt" cur))))
+ (type (car (string-match-substrings rx url)))
+ (title (string-chop-newline
+ (file-string
+ (expand-file-name "title.txt" cur)))))
+ (when type
+ (org-link-store-props
+ :type type
+ :description title
+ :link (replace-regexp-in-string "^file://" "" url))))))
+ (org-link-set-parameters "tabfs" :store #'org-tabfs-store-link))
+
+(use-package flymake-diagnostic-at-point
+ :ensure t)
+
+(use-package flymake
+ :init
+ (defmacro flymake-define-generic-backend (name command &rest body)
+ "Define NAME as a backend for Flymake. COMMAND is the command
+to be invoked. It should be an qutoed list in the form required
+by `make-process'. The new backend will throw an error if the
+first element of COMMAND is not found by `executable-find'. BODY
+is called to search for errors and should return a list of
+flymake diagnostic objects. If any element of COMMAND is not a
+string, it is evaluated at runtime, and its result is used
+instead."
+ (declare (indent 2))
+ (let ((proc (intern (concat (symbol-name name) "--proc"))))
+ `(progn
+ (defvar-local ,proc nil)
+ (defun ,name (report-fn &rest _args)
+ (unless (executable-find (car ,command))
+ (error (concat "Cannot find " (car ,command))))
+ (when (process-live-p ,proc) (kill-process ,proc))
+ (let ((source (current-buffer))
+ (report-fn report-fn))
+ (save-restriction
+ (widen)
+ (setq ,proc (make-process :name ,(symbol-name name)
+ :noquery t :connection-type 'pipe
+ :buffer (generate-new-buffer
+ ,(concat " *" (symbol-name name) "*"))
+ :command (mapcar (lambda (x) (if (not (stringp x)) (eval x) x)) ,command)
+ :sentinel
+ (lambda (proc _event)
+ (when (eq (process-status proc) 'exit)
+ (unwind-protect
+ (if (with-current-buffer source
+ (eq proc ,proc))
+ (with-current-buffer (process-buffer proc)
+ (goto-char (point-min))
+ (funcall report-fn (progn ,@body)))
+ (flymake-log :warning "Cancelling obsolete check %s" proc))
+ (kill-buffer (process-buffer proc)))))))
+ (process-send-region ,proc (point-min) (point-max))
+ (process-send-eof ,proc))))
+ (defun ,(intern (concat (symbol-name name) "-setup-backend")) ()
+ (add-hook 'flymake-diagnostic-functions ',name nil t)))))
+
+ (when-group writing
+ (flymake-define-generic-backend proselint-flymake
+ '("proselint" "-j" "-")
+ (require 'json)
+ (let ((errors
+ (cdr (assoc 'errors
+ (last (assoc 'data
+ (json-read)))))))
+ (mapcar (lambda (e)
+ (flymake-make-diagnostic
+ source
+ (cdr (assoc 'start e))
+ (+ (cdr (assoc 'start e))
+ (cdr (assoc 'extent e)))
+ :warning
+ (cdr (assoc 'message e))))
+ errors)))
+
+ (add-hook 'text-mode-hook 'proselint-flymake-setup-backend))
+
+ (when-group programming
+ (flymake-define-generic-backend gcc-flymake
+ '("gcc" "-pedantic" "-Wall" "-o" (null-device) "-S" "-x" (cl-case major-mode
+ ('c-mode "c")
+ ('c++-mode "c++")) "-")
+ (cl-loop while (search-forward-regexp
+ "^<stdin>:\\([0-9]+\\):\\([0-9]+\\): \\(note\\|warning\\|error\\): \\(.+\\)$" nil t)
+ for row = (match-string 1)
+ for col = (match-string 2)
+ for type = (cond ((string= (match-string 3) "note") :note)
+ ((string= (match-string 3) "warning") :warning)
+ ((string= (match-string 3) "error") :error))
+ for msg = (match-string 4)
+ for (beg . end) = (flymake-diag-region
+ source
+ (string-to-number row)
+ (string-to-number col))
+ collect (flymake-make-diagnostic
+ source
+ beg end
+ type
+ msg)))
+
+ (add-hook 'c-mode-hook 'gcc-flymake-setup-backend)
+ (add-hook 'c++-mode-hook 'gcc-flymake-setup-backend))
+
+ :config
+ (setq
+ flymake-no-changes-timeout nil
+ flymake-diagnostic-at-point-error-prefix ""
+ flymake-diagnostic-at-point-display-diagnostic-function
+ 'flymake-diagnostic-at-point-display-minibuffer)
+
+ (require 'flymake-diagnostic-at-point)
+ (add-hook 'flymake-mode-hook 'flymake-diagnostic-at-point-mode)
+
+ (keymap-modify flymake-mode-map
+ "M-n" 'flymake-goto-next-error
+ "M-p" 'flymake-goto-prev-error))
+
+(use-package dumb-jump
+ :if (group-enabled-p 'programming)
+ :ensure t
+ :init
+ (add-hook 'xref-backend-functions 'dumb-jump-xref-activate))
+
+(use-package git-annex
+ :demand t
+ :ensure t
+ :config
+ (custom-set-faces
+ '(git-annex-dired-annexed-available ((t (:inherit default))))
+ '(git-annex-dired-annexed-unavailable ((t (:inherit error)))))
+ :config
+ (setq git-annex-commit nil))
+
+(use-package vc
+ :init
+ (setq vc-follow-symlinks t))
+
+(use-package cperl-mode
+ :init
+ (add-to-list 'major-mode-remap-alist '(perl-mode . cperl-mode))
+ :config
+ (setq cperl-lazy-help-time 1)
+ (add-hook 'cperl-mode-hook 'cperl-lazy-install))
+
+(use-package ruby-mode
+ :mode "\\.irbrc\\'")
+
+(use-package inf-ruby
+ :if (group-enabled-p 'programming)
+ :ensure t
+ :config
+ (setf
+ (alist-get "ruby" inf-ruby-implementations
+ nil nil #'equal)
+ "irb --inf-ruby-mode -r irb/completion")
+
+ (when (executable-find "pry")
+ (setq inf-ruby-default-implementation "pry")))
+
+(use-package macrostep
+ :ensure t)
+(use-package elisp-mode
+ :if (group-enabled-p 'programming)
+ :config
+ (keymap-set emacs-lisp-mode-map "C-c C-m" 'macrostep-expand))
+
+(use-package lisp-mode
+ :interpreter "cl"
+ :config
+ (setq inferior-lisp-program "sbcl"))
+
+(when-group programming ;; sly
+ (use-package sly-asdf
+ :ensure t)
+ (use-package sly-quicklisp
+ :ensure t)
+ (use-package sly-named-readtables
+ :ensure t)
+ (use-package sly-repl-ansi-color
+ :ensure t)
+ (use-package sly-macrostep
+ :ensure t))
+
+(use-package sly
+ :if (group-enabled-p 'programming)
+ :ensure t
+ :config
+ (setq
+ sly-enable-evaluate-in-emacs t
+ sly-symbol-completion-mode nil)
+
+ (put 'define-interface
+ 'sly-common-lisp-indent-function
+ '(4 (&whole 4 &rest 1)
+ &rest (&whole 2 &lambda &body)))
+ (put 'extend-interface
+ 'sly-common-lisp-indent-function
+ '(4 (&whole 4 &rest 1)
+ &rest (&whole 2 &lambda &body)))
+ (put 'implement-interface
+ 'sly-common-lisp-indent-function
+ '(4 4 2
+ &rest (&whole 2 &lambda &body)))
+ (put 'define-implementing-class
+ 'sly-common-lisp-indent-function
+ '(6 (&whole 4 &rest 1)
+ (&whole 2 &rest 1)
+ &rest (&whole 2 0 &lambda &body)))
+ (put 'defmethods
+ 'sly-common-lisp-indent-function
+ '(4 4 &rest (&whole 2 &lambda &body)))
+ (put 'define-package
+ 'sly-common-lisp-indent-function
+ '(as defpackage))
+
+ (defun my/sly-maybe-run-tests ()
+ (interactive nil sly-mode)
+ (unless sly-mode
+ (user-error "Not in sly-mode buffer"))
+ (when-let* ((_ (sly-connected-p))
+ (cur (sly-current-package))
+ (test (if (string-suffix-p "/test" cur)
+ cur
+ (concat cur "/test")))
+ (fun "TEST-INTERACTIVELY")
+ (test (sly-eval `(cl:let ((p (slynk::guess-package ,test)))
+ (cl:when p (cl:package-name p)))))
+ (fun (sly-eval `(cl:find-symbol ,fun ,test))))
+ (sly-interactive-eval (format "(%s)" fun))))
+
+ (add-hook 'sly-mode-hook
+ (lambda ()
+ (add-hook 'after-save-hook
+ #'my/sly-maybe-run-tests
+ nil t)))
+
+ (add-hook 'sly-mrepl-mode-hook
+ #'puni-mode))
+
+(progn ;; wesnoth-mode
+ (when-let ((wesnoth-dir
+ (seq-find
+ #'file-exists-p
+ '("/usr/share/games/wesnoth/1.14/data/tools/emacs_mode/"
+ "/usr/share/wesnoth/data/tools/emacs_mode/"))))
+ (add-to-list 'load-path wesnoth-dir)
+ (use-package wesnoth-mode
+ :mode "\\.cfg")))
+
+(use-package chess
+ :if (group-enabled-p 'games)
+ :ensure t
+ :config
+ (setq chess-images-separate-frame nil))
+
+(progn ;; ol-chess
+ (when-group (org games)
+ (with-eval-after-load 'ol
+ (defun org-chess-open (path _)
+ (require 'chess)
+ (if-let ((pos (chess-fen-to-pos path)))
+ (let ((chess-starting-position pos)
+ (chess-default-engine 'none))
+ (call-interactively #'chess))
+ (user-error "Failed to parse FEN: %S" path)))
+ (org-link-set-parameters "chess" :follow #'org-chess-open))))
+
+(use-package kana
+ :if (group-enabled-p 'games)
+ :ensure t
+ :config
+ (setq kana-loop-speed 3.0))
+
+(use-package transmission
+ :if (group-enabled-p 'torrents)
+ :ensure t)
+
+(use-package magit
+ :ensure t
+ :init
+ (keymap-global-set "H-g" 'magit-status)
+ :config
+ (setq magit-display-buffer-function #'display-buffer))
+
+(use-package magit-annex
+ :ensure t)
+
+(use-package winner
+ :demand t
+ :config
+ (when-group exwm
+ (setq winner-dont-bind-my-keys t))
+ (winner-mode))
+
+(progn ;; term
+ (defun spawn-term (&optional dir)
+ (interactive)
+ (let ((default-directory
+ (or dir (expand-file-name "~/"))))
+ (eat nil t)))
+
+ (defun spawn-term-here ()
+ (interactive)
+ (spawn-term default-directory)))
+
+(use-package exwm
+ :if (group-enabled-p 'exwm)
+ :ensure t
+ :init
+ (defun exwm-run (it)
+ (interactive (list (read-shell-command "Run: ")))
+ (let* ((is-program (eq (string-to-char it) ?!))
+ (sym (read it))
+ (str (if is-program
+ (coerce (rest (coerce it 'list)) 'string)
+ it)))
+ (cond
+ ((and (not is-program)
+ (commandp sym))
+ (call-interactively sym))
+ (t (spawn-shell-cmd str nil str)))))
+
+ (defun exwm-raise (regex)
+ (let ((tab
+ (seq-find (lambda (t)
+ (string-match-p regex (alist-get 'name t)))
+ (tab-bar--tabs-recent)))
+ (buffer
+ (seq-find (lambda (b)
+ (string-match-p regex (buffer-name b)))
+ (buffer-list))))
+ (cond
+ (tab (tab-bar-select-tab-by-name (alist-get 'name tab)))
+ (buffer
+ (if (get-buffer-window buffer)
+ (select-window (get-buffer-window buffer))
+ (exwm-workspace-switch-to-buffer buffer))))))
+
+ (cl-defun run-raise-remove (command regex &optional
+ (run #'exwm-run)
+ (raise #'exwm-raise)
+ (remove #'bury-buffer))
+ (if (string-match-p regex (buffer-name))
+ (funcall remove)
+ (or
+ (funcall raise regex)
+ (funcall run command))))
+
+ (defalias 'run-or-raise-or-remove 'run-raise-remove)
+ (defmacro define-run-raise-remove (name command regex)
+ `(defun ,name ()
+ (interactive)
+ (run-raise-remove ,command ,regex)))
+ (defalias 'define-rrr 'define-run-raise-remove)
+
+ (define-rrr browser browse-url-firefox-program ".*[Ff]irefox.*")
+ (define-rrr process-viewer "proced" "\\*Proced\\*")
+ (define-rrr agenda "org-agenda-list" "\\*Org Agenda\\*")
+ (define-rrr torrents "transmission" "^\\*transmission\\*$")
+ (define-rrr networks "nmtui" "nmtui")
+ (define-rrr bluetooth "bluetuith" "bluetuith")
+ (define-rrr mixer "pulsemixer" "pulsemixer")
+ (define-rrr music "bongo" "^\\*Bongo.*\\*$")
+ (define-rrr wallpapers "dired-wallpapers" "\\*wallpapers\\*")
+ (defalias 'walls 'wallpapers)
+
+ (defun email ()
+ (interactive)
+ (run-raise-remove (lambda ()
+ (tab-bar-new-tab)
+ (gnus))
+ "\\*\\(Group\\|Summary\\|Article\\).*\\*"
+ 'funcall
+ (lambda (_)
+ (awhen (seq-find
+ (lambda (tb)
+ (string-match-p "\\*Group\\*"
+ (alist-get 'name tb)))
+ (tab-bar--tabs-recent))
+ (tab-bar-switch-to-tab (alist-get 'name it))
+ t))
+ 'tab-bar-close-tab))
+
+ (defmacro define-term-command (name command)
+ `(defun ,name ()
+ (interactive)
+ (with-current-buffer
+ (eat ,command t)
+ (rename-buffer ,(symbol-name name) t))))
+
+ (define-term-command nmtui "nmtui")
+ (define-term-command bluetuith "bluetuith")
+ (define-term-command pulsemixer "pulsemixer")
+
+ (defun show-wallpaper ()
+ (interactive)
+ (if-group theme
+ (spawn-shell-cmd "wallpaper" nil
+ "feh --fullscreen --zoom fill $(cat ~/.cache/wal/wal)")
+ (user-error "Need pywal set up to show wallpaper")))
+
+ (defun start-screensaver ()
+ (interactive)
+ (start-process-shell-command "xss" nil "xss"))
+
+ (defvar barrier-host "basil")
+ (defun barrier-on ()
+ (interactive)
+ (spawn-shell-cmd
+ "barrier" nil
+ (if-attribute is-main
+ (format "barriers --disable-crypto -n %s -c ~/.barrier.conf" (system-name))
+ (format "barrierc -n %s %s" (system-name) barrier-host))))
+ (defun barrier-off ()
+ (interactive)
+ (spawn-shell-cmd
+ "barrier-stop" nil
+ (if-attribute is-main
+ "killall barriers"
+ "killall barrierc")))
+
+ (defun keymap-exwm-set (key command)
+ "Like `keymap-global-set' but makes KEY global in EXWM as well."
+ (keymap-global-set key command)
+ (let ((i (kbd key)))
+ (cl-pushnew (if (sequencep i) (aref i 0) i)
+ exwm-input-prefix-keys))))
+
+(use-package exwm-edit
+ :ensure t)
+
+(use-package exwm
+ :if (group-enabled-p 'exwm)
+ :if (display-graphic-p)
+ :config
+ (mapc
+ #'require
+ '(exwm-systemtray
+ exwm-randr
+ exwm-edit))
+
+ (add-hook 'exwm-update-class-hook
+ (lambda ()
+ (if (and (not exwm-title) exwm-class-name)
+ (exwm-workspace-rename-buffer exwm-class-name))))
+ (add-hook 'exwm-update-title-hook
+ (lambda ()
+ (if exwm-title
+ (exwm-workspace-rename-buffer exwm-title))))
+
+ (add-hooks 'after-init-hook
+ 'exwm-enable 'delete-other-frames)
+
+ (defun exwm-workspace-next (&optional arg)
+ (interactive "p")
+ (exwm-workspace-switch
+ (+ exwm-workspace-current-index (or arg 1))))
+ (defun exwm-workspace-previous (&optional arg)
+ (interactive "p")
+ (exwm-workspace-next (- (or arg 1))))
+
+ :config
+ (when-group mail
+ (setq
+ display-time-mail-function
+ (lambda ()
+ (with-temp-buffer
+ (cd "~/")
+ (not
+ (string=
+ (shell-command-to-string "mu find flag:unread")
+ "no matches for search expression
+"))))
+ display-time-use-mail-icon t))
+
+ (setq display-time-interval 10)
+
+ (display-time-mode)
+ (when-attribute has-battery
+ (display-battery-mode)
+ (setq battery-load-low 20
+ battery-mode-line-format "[%b%p%%][%t]"
+ battery-echo-area-format "%B from %L\tUsing %rW\tAt %p%%, %t remaining"))
+
+ (exwm-systemtray-mode)
+ (exwm-randr-mode)
+ (cl-pushnew ?\H-o exwm-input-prefix-keys)
+ (cl-pushnew ?\H-c exwm-input-prefix-keys)
+ (cl-pushnew ?\M-! exwm-input-prefix-keys))
+
+(use-package exwm-randr
+ :if (group-enabled-p 'exwm)
+ :if (display-graphic-p)
+ :config
+ (setq
+ exwm-randr-workspace-monitor-plist
+ '(
+ 0 "LVDS-1"
+ 1 "VGA-1"
+ 2 "HDMI-1"
+ 3 "DP-1"
+ 4 "SVIDEO-1"))
+
+ (defun exwm-randr-workspace-add (monitor)
+ (interactive
+ (list (completing-read
+ "Monitor: "
+ (mapcar #'cadr (seq-partition exwm-randr-workspace-monitor-plist 2))
+ nil t nil nil)))
+ (exwm-workspace-add
+ (car (cl-find monitor
+ (seq-partition exwm-randr-workspace-monitor-plist 2)
+ :key #'cadr :test #'string=)))))
+
+(use-package exwm
+ :if (group-enabled-p 'exwm)
+ :if (display-graphic-p)
+ :demand t
+ :config
+ (keymap-global-unset "C-z")
+
+ (setq
+ exwm-input-global-keys
+ `((,(kbd "s-<f1>") . exwm-reset)))
+
+ (let ((dirs '("left" "right" "up" "down")))
+ (mapc (lambda (dir)
+ (keymap-exwm-set
+ (format "s-<%s>" dir)
+ (intern (format "windmove-%s" dir))))
+ dirs)
+
+ (mapc (lambda (dir)
+ (keymap-exwm-set
+ (format "S-s-<%s>" dir)
+ (intern (format "windmove-swap-states-%s" dir))))
+ dirs))
+
+ (mapc (lambda (n)
+ (keymap-exwm-set
+ (format "s-%s" n)
+ (lambda ()
+ (interactive)
+ (tab-bar-select-tab n))))
+ (number-sequence 1 9))
+
+ (keymap-exwm-set "s-0" 'tab-bar-close-tab)
+ (seq-mapn (lambda (n c)
+ (keymap-exwm-set
+ (format "s-%s" c)
+ (lambda ()
+ (interactive)
+ (tab-bar-new-tab-to n))))
+ (number-sequence 1 9)
+ '("!" "@" "#" "$" "%" "^" "&" "*" "("))
+ (keymap-exwm-set "s-)" 'tab-bar-new-tab)
+
+ (define-prefix-command 's-x-prefix)
+ (define-prefix-command 'barrier-prefix)
+ (mapc
+ (lambda (x)
+ (keymap-exwm-set (car x) (cadr x)))
+ '(("s-n" go-next-window)
+ ("s-p" go-previous-window)
+
+ ("s-u" winner-undo)
+ ("s-r" winner-redo)
+
+ ("s-j" switch-to-buffer)
+ ("s-J" go-other-buffer)
+ ("s-<tab>" ace-window)
+
+ ("s-t" tab-bar-new-tab)
+ ("s-T" tab-bar-close-tab)
+ ("s-SPC" tab-bar-switch-to-tab)
+ ("s-b" toggle-frame-tab-bar)
+
+ ("s-c" delete-window)
+ ("s-C" kill-buffer-and-maybe-window)
+ ("s-k" kill-buffer)
+ ("s-K" kill-current-buffer)
+ ("s-o" delete-other-windows)
+ ("s-O" same-window-prefix)
+
+ ("s-d" do-split-window)
+ ("s-=" balance-windows)
+ ("s-+" golden-ratio)
+ ("s-l" golden-ratio-mode)
+
+ ("s-<return>" eshell-at-home)
+ ("S-s-<return>" eshell)
+
+ ("s-s" start-screensaver)
+ ("<XF86ScreenSaver>" start-screensaver)
+ ("s-S" show-wallpaper)
+
+ ("s-," pulseaudio-sink-lower)
+ ("<XF86AudioLowerVolume>" pulseaudio-sink-lower)
+ ("s-." pulseaudio-sink-raise)
+ ("<XF86AudioRaiseVolume>" pulseaudio-sink-raise)
+ ("s-/" pulseaudio-mute)
+ ("<XF86AudioMute>" pulseaudio-mute)
+
+ ("s-'" bongo-pause/resume)
+ ("<XF86AudioPlay>" bongo-pause/resume)
+ ("s-\"" bongo-stop)
+ ("<XF86AudioStop>" bongo-stop)
+ ("s->" bongo-next)
+ ("<XF86AudioNext>" bongo-next)
+ ("s-<" bongo-previous)
+ ("<XF86AudioPrev>" bongo-previous)
+
+ ("s-a" lower-transparency)
+ ("s-A" raise-transparency)
+ ("C-s-a" toggle-transparency)
+
+ ("s-x" s-x-prefix)
+ ("s-x x" exwm-run)
+ ("s-x s-x" exwm-run)
+ ("<XF86Launch1>" exwm-run)
+ ("s-x p" process-viewer)
+ ("s-x e" email)
+ ("s-x c" jabber-global-keymap)
+ ("s-x t" torrents)
+ ;; ("s-x v" empv-toggle-video)
+ ("s-x w" networks)
+ ("s-x W" bluetooth)
+ ("s-x m" mixer)
+ ("s-x M" music)
+ ("s-x a" agenda)
+ ("s-x s" my/magit-annex-autosync)
+ ("s-x RET" spawn-term)
+ ("s-x S-<return>" spawn-term-here)
+ ("s-x B" barrier-prefix)
+ ("s-x B 1" barrier-on)
+ ("s-x B 0" barrier-off)
+
+ ("s-h" exwm-workspace-switch)))
+
+ (define-keymap
+ :keymap exwm-mode-map
+ "C-q" 'exwm-input-send-next-key
+ "<f11>" 'exwm-layout-set-fullscreen
+ "s-<f11>" 'exwm-floating-toggle-floating
+ "s-<f2>" 'exwm-input-release-keyboard)
+
+ (setq
+ exwm-input-simulation-keys
+ (mapcar
+ (lambda (a) (cons (kbd (car a)) (kbd (cadr a))))
+ '(("C-c C-c" "C-c")
+ ("C-x C-x" "C-x")))))
+
+(use-package ewal
+ :if (group-enabled-p 'wal)
+ :ensure t
+ :init
+ (defun reload-theme ()
+ (interactive)
+ (load-theme 'ewal-doom-one))
+
+ (if (eq (window-system) 'x)
+ (reload-theme)))
+(use-package doom-themes
+ :ensure (doom-themes :pin "melpa"))
+(use-package ewal-doom-themes
+ :if (group-enabled-p 'wal)
+ :ensure t)
+
+(use-package keyfreq
+ :ensure t
+ :init
+ (keyfreq-mode)
+ (keyfreq-autosave-mode)
+ (setq keyfreq-file (locate-user-emacs-file "keyfreq")))
+
+(progn ;; local-init
+ (let ((f (locate-user-emacs-file "local-init.el")))
+ (when (file-exists-p f)
+ (load-file f))))
+
+(desktop-save-mode) ;; needs to run last