summaryrefslogtreecommitdiffhomepage
path: root/attic
diff options
context:
space:
mode:
Diffstat (limited to 'attic')
-rw-r--r--attic/counsel.org337
-rw-r--r--attic/elisp/jao-custom-modus.el159
-rw-r--r--attic/elisp/jao-doc-view-imenu.el74
-rw-r--r--attic/elisp/jao-emms-info-track.el (renamed from attic/media/jao-emms-info-track.el)0
-rw-r--r--attic/elisp/jao-emms-lyrics.el (renamed from attic/media/jao-emms-lyrics.el)0
-rw-r--r--attic/elisp/jao-emms-random-album.el (renamed from attic/media/jao-emms-random-album.el)0
-rw-r--r--attic/elisp/jao-emms.el (renamed from attic/media/jao-emms.el)0
-rw-r--r--attic/elisp/jao-frm.el222
-rw-r--r--attic/elisp/jao-maildir.el (renamed from attic/net/jao-maildir.el)0
-rw-r--r--attic/elisp/jao-mpdn.el (renamed from attic/media/jao-mpdn.el)4
-rw-r--r--attic/elisp/jao-notmuch-gnus.el226
-rw-r--r--attic/elisp/jao-notmuch-move.el (renamed from attic/net/jao-notmuch-move.el)0
-rw-r--r--attic/elisp/jao-notmuch-tree-fold.el (renamed from attic/net/jao-notmuch-tree-fold.el)0
-rw-r--r--attic/elisp/jao-recoll.el131
-rw-r--r--attic/elisp/misc.el951
-rw-r--r--attic/elisp/nnnm.el (renamed from attic/net/nnnm.el)0
-rw-r--r--attic/misc.org22
-rw-r--r--attic/net/jao-proton-utils.el131
-rw-r--r--attic/net/w3m.org191
19 files changed, 1765 insertions, 683 deletions
diff --git a/attic/counsel.org b/attic/counsel.org
deleted file mode 100644
index f6814ae..0000000
--- a/attic/counsel.org
+++ /dev/null
@@ -1,337 +0,0 @@
-#+title: Completion configuration using ivy, counsel and friends
-
-* selectrum
- #+begin_src emacs-lisp :load no
- (use-package selectrum
- :ensure t
- :init
- (defun jao-selectrum--ord-refine (&rest args)
- (let ((completion-styles '(orderless)))
- (apply #'selectrum-refine-candidates-using-completions-styles args)))
-
- (defun jao-selectrum-orderless ()
- (interactive)
- (setq selectrum-refine-candidates-function #'jao-selectrum--ord-refine)
- (setq selectrum-highlight-candidates-function #'orderless-highlight-matches)
- (setq orderless-skip-highlighting (lambda () selectrum-is-active)))
-
-
- :config
- ;; https://github.com/raxod502/selectrum/wiki/Ido,-icomplete(fido)-emulation
- (defun selectrum-fido-backward-updir ()
- "Delete char before or go up directory, like `ido-mode'."
- (interactive)
- (if (and (eq (char-before) ?/)
- (eq (selectrum--get-meta 'category) 'file))
- (save-excursion
- (goto-char (1- (point)))
- (when (search-backward "/" (point-min) t)
- (delete-region (1+ (point)) (point-max))))
- (call-interactively 'backward-delete-char)))
-
- (defun selectrum-fido-delete-char ()
- "Delete char or maybe call `dired', like `ido-mode'."
- (interactive)
- (let ((end (point-max)))
- (if (or (< (point) end) (not (eq (selectrum--get-meta 'category) 'file)))
- (call-interactively 'delete-char)
- (dired (file-name-directory (minibuffer-contents)))
- (exit-minibuffer))))
-
- (defun selectrum-fido-ret ()
- "Exit minibuffer or enter directory, like `ido-mode'."
- (interactive)
- (let* ((dir (and (eq (selectrum--get-meta 'category) 'file)
- (file-name-directory (minibuffer-contents))))
- (current (selectrum-get-current-candidate))
- (probe (and dir current
- (expand-file-name (directory-file-name current) dir))))
- (if (and probe (file-directory-p probe) (not (string= current "./")))
- (selectrum-insert-current-candidate)
- (selectrum-select-current-candidate))))
-
- ;; (define-key selectrum-minibuffer-map (kbd "RET") 'selectrum-fido-ret)
- (define-key selectrum-minibuffer-map (kbd "DEL") 'selectrum-fido-backward-updir)
- (define-key selectrum-minibuffer-map (kbd "C-d") 'selectrum-fido-delete-char)
-
- :custom ((selectrum-complete-in-buffer t)
- ;; (selectrum-display-action '(display-buffer-at-bottom))
- (selectrum-extend-current-candidate-highlight t)
- (selectrum-fix-vertical-window-height nil)
- (selectrum-max-window-height 20)
- (selectrum-show-indices nil)
- (selectrum-count-style 'current/matches))
- :bind (("C-R" . selectrum-repeat)))
- #+end_src
-* ivy
- #+begin_src emacs-lisp
- (use-package ivy
- :ensure t
- :demand t
- :custom
- ((ivy-count-format "(%d/%d) ")
- (ivy-do-completion-in-region t)
- (ivy-height 20)
- (ivy-re-builders-alist '((counsel-ag . ivy--regex)
- (counsel-rg . ivy--regex)
- (counsel-yank-pop . ivy--regex)
- (swiper . ivy--regex)
- (swiper-isearch . ivy--regex)
- (t . ivy--regex-fuzzy)))
- (ivy-use-virtual-buffers t)
- (ivy-virtual-abbreviate 'abbreviate)
- (ivy-wrap t))
-
- :config
- ;; used by ivy--regex-fuzzy to order results
- (use-package flx :ensure t)
-
- ;; Try C-o in the minibuffer
- (use-package ivy-hydra
- :after ivy
- :ensure t
- :init (setq ivy-read-action-function #'ivy-hydra-read-action))
-
- (add-to-list 'ivy-initial-inputs-alist
- '(gnus-summary-move-article . ""))
-
- :bind (("C-R" . ivy-resume)
- ("C-x b" . ivy-switch-buffer)
- ("C-c v" . ivy-push-view)
- ("C-c V" . ivy-pop-view))
- :diminish)
- #+end_src
-* counsel
- #+begin_src emacs-lisp
- (use-package counsel
- :ensure t
- :custom ((counsel-describe-function-function 'helpful-callable)
- (counsel-describe-variable-function 'helpful-variable)
- (counsel-find-file-at-point t)
- (counsel-linux-app-format-function
- #'counsel-linux-app-format-function-name-pretty)
- (counsel-mode-override-describe-bindings nil)
- (counsel-recentf-include-xdg-list t))
- :config
- :bind (("C-s" . swiper-isearch)
- ("C-S-s" . isearch-forward)
- ("M-x" . counsel-M-x)
- ("C-x f" . counsel-find-file)
- ("C-c k" . counsel-ag)
- ("C-c K" . counsel-rg)
- ("C-c l" . counsel-locate)
- ("C-c b" . counsel-git)
- ("C-c i" . counsel-imenu)
- ("C-c G" . counsel-search)
- ("s-r" . counsel-linux-app))
- :diminish)
- #+end_src
-* counsel add-ons
-*** notmuch
- #+begin_src emacs-lisp
- (use-package counsel-notmuch
- :ensure t
- :config (with-eval-after-load "gnus-group"
- (define-key gnus-group-mode-map "Gg" 'counsel-notmuch)))
- #+end_src
-*** recoll
- #+begin_src emacs-lisp
- (require 'jao-recoll)
- (defvar jao-counsel-recoll--history nil)
- (defun jao-counsel-recoll--function (str)
- (let ((xs (counsel-recoll-function str)))
- (cl-remove-if-not (lambda (x) (string-prefix-p "file://" x)) xs)))
-
- (defun jao-counsel-recoll (&optional initial-input)
- (interactive)
- (counsel-require-program "recoll")
- (ivy-read "recoll: " 'jao-counsel-recoll--function
- :initial-input initial-input
- :dynamic-collection t
- :history 'jao-counsel-recoll--history
- :action (lambda (x)
- (when (string-match "file://\\(.*\\)\\'" x)
- (let ((file-name (match-string 1 x)))
- (if (string-match "pdf$" x)
- (jao-open-doc file-name)
- (find-file file-name)))))
- :unwind #'counsel-delete-process
- :caller 'jao-counsel-recoll))
-
- (defun jao-counsel-recoll--recoll (_s) (jao-recoll ivy-text))
-
- (ivy-set-actions 'jao-counsel-recoll
- '(("x" jao-counsel-recoll--recoll "List in buffer")))
-
- (global-set-key (kbd "C-c R") #'jao-counsel-recoll)
- #+end_src
-* ivy rich
- #+begin_src emacs-lisp
- (use-package ivy-rich
- :after (ivy counsel)
- :ensure t
- :custom ((ivy-rich-path-style 'relative)
- (ivy-rich-parse-remote-buffer nil)
- (ivy-rich-parse-remote-file-path nil))
- :config
- (ivy-rich-modify-columns
- 'ivy-switch-buffer
- '((ivy-rich-candidate (:width 80))
- (ivy-rich-switch-buffer-indicators (:face jao-themes-f00))
- (ivy-rich-switch-buffer-project (:width 15))
- (ivy-rich-switch-buffer-major-mode (:width 15 :face jao-themes-f12)))))
- #+end_src
-* cmap
- #+begin_src emacs-lisp
- (jao-load-path "cmap")
- (use-package cmap
- :demand t
- :bind (("C-;" . cmap-cmap)
- ("C-'" . cmap-default)))
- #+end_src
-*** prompter
- #+begin_src emacs-lisp
- (defun jao-cmap--hide-help ()
- (when-let ((w (get-buffer-window (help-buffer))))
- (with-selected-window w (kill-buffer-and-window))))
-
- (defun jao-cmap--prompter (keymap)
- (let ((display-buffer-alist '(("*Help*"
- (display-buffer-at-bottom)
- (window-parameters (mode-line-format . none))
- (window-height . fit-window-to-buffer)))))
- (let ((inhibit-message t))
- (describe-keymap keymap))))
-
- (defun jao-cmap--prompter-done ()
- (save-current-buffer (jao-cmap--hide-help)))
-
- (setq cmap-prompter #'jao-cmap--prompter)
- (setq cmap-prompter-done #'jao-cmap--prompter-done)
- #+end_src
-*** minibuffer actions
- #+begin_src emacs-lisp
- (defun jao-cmap--completion-metadata ()
- (completion-metadata
- (buffer-substring-no-properties (field-beginning) (point))
- minibuffer-completion-table
- minibuffer-completion-predicate))
-
- (defun jao-cmap--completion-category ()
- (completion-metadata-get (jao-cmap--completion-metadata) 'category))
-
- (defmacro cmap-define-keymap (v d &rest b)
- `(defvar ,v (cmap-keymap ,@b) ,d))
-
- (cmap-define-keymap jao-cmap-buffer-map
- "Keymap for buffer actions."
- ("k" . kill-buffer)
- ("b" . switch-to-buffer)
- ("o" . switch-to-buffer-other-window)
- ("z" . bury-buffer)
- ("q" . kill-buffer-and-window)
- ("=" . ediff-buffers))
-
- ;; (cmap-define-keymap espotify-item-keymap
- ;; "Actions for Spotify search results"
- ;; ("a" espotify--play-album)
- ;; ("h" espotify--show-info))
-
- (defvar jao-cmap--smaps
- '((command . cmap-command-map)
- ;; (espotify-search-item . espotify-item-keymap)
- (function . cmap-function-map)
- (variable . cmap-variable-map)
- (face . cmap-face-map)
- (buffer . jao-cmap-buffer-map)
- (consult-buffer . jao-cmap-buffer-map)))
-
- (defun jao-cmap-target-minibuffer-candidate ()
- (when (minibuffer-window-active-p (selected-window))
- (let ((cand (ivy-state-current ivy-last))
- (cat (jao-cmap--completion-category)))
- (when-let (m (alist-get cat jao-cmap--smaps))
- (cons m cand)))))
-
- (add-to-list 'cmap-targets #'jao-cmap-target-minibuffer-candidate)
- #+end_src
-*** url / video actions
- #+begin_src emacs-lisp
- (defvar jao-cmap-video-url-rx
- (format "^https?://\\(?:www\\.\\)?%s/.+"
- (regexp-opt '("youtu.be"
- "youtube.com"
- "blip.tv"
- "vimeo.com"
- "infoq.com")
- t))
- "A regular expression matching URLs that point to video streams")
-
- (defun jao-cmap--play-video (player url)
- (interactive "sURL: ")
- (let ((cmd (format "%s %s" player (shell-quote-argument url))))
- (start-process-shell-command player nil cmd)))
-
- (defun jao-cmap-mpv (&optional url)
- "Play video stream with mpv"
- (interactive "sURL: ")
- (jao-cmap--play-video "mpv" url))
-
- (defun jao-cmap-vlc (&optional url)
- "Play video stream with vlc"
- (interactive "sURL: ")
- (jao-cmap--play-video "vlc" url))
-
- (defun jao-cmap-target-w3m-url ()
- (when-let (url (or (thing-at-point-url-at-point)
- (w3m-anchor)
- w3m-current-url))
- (cons 'cmap-url-map url)))
-
- (defun jao-cmap-kill (&optional x)
- "Save to kill ring"
- (interactive "s")
- (kill-new x))
-
- (defun jao-cmap-url (url)
- "Browse URL, externally if we're already in emacs-w3m"
- (if (derived-mode-p 'w3m-mode)
- (jao-browse-with-external-browser url)
- (browse-url url)))
-
- (define-key cmap-url-map [return] #'jao-cmap-url)
- (define-key cmap-url-map "f" #'browse-url-firefox)
- (define-key cmap-url-map "w" #'jao-cmap-kill)
-
- (defun jao-cmap-target-video-url ()
- (when-let (url (jao-cmap-target-w3m-url))
- (when (string-match-p jao-cmap-video-url-rx (cdr url))
- (cons 'jao-cmap-video-url-map (cdr url)))))
-
- (cmap-define-keymap jao-cmap-video-url-map
- "Actions on URLs pointing to remote video streams."
- ("v" . jao-cmap-vlc)
- ([return] . jao-cmap-mpv))
-
- (add-to-list 'cmap-targets #'jao-cmap-target-w3m-url)
- (add-to-list 'cmap-targets #'jao-cmap-target-video-url)
- #+end_src
-* hooks
- #+begin_src emacs-lisp
- (with-eval-after-load "exwm"
- (add-to-list 'exwm-input-global-keys '([?\s-r] . counsel-linux-app)))
-
- (with-eval-after-load "espotify"
- (require 'ivy-spotify)
- (defalias 'jao-spotify-album #'ivy-spotify-album)
- (defalias 'jao-spotify-track #'ivy-spotify-track)
- (defalias 'jao-spotify-artist #'ivy-spotify-artist)
- (defalias 'jao-spotify-playlist #'ivy-spotify-playlist))
- #+end_src
-* startup
- #+begin_src emacs-lisp
- (ivy-mode 1)
- (counsel-mode 1)
- (ivy-rich-mode 1)
- (ivy-rich-project-root-cache-mode 1)
- #+end_src
diff --git a/attic/elisp/jao-custom-modus.el b/attic/elisp/jao-custom-modus.el
new file mode 100644
index 0000000..9b2cd8e
--- /dev/null
+++ b/attic/elisp/jao-custom-modus.el
@@ -0,0 +1,159 @@
+;;; jao-custom-themes.el --- color themes based on modus-themes -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2022 jao
+
+;; Author: jao <mail@jao.io>
+;; Keywords: faces, faces
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; Color themes based on modus
+
+;;; Code:
+
+(use-package modus-themes
+ :ensure t
+ :demand t)
+
+;;;; Customization
+(setq modus-themes-italic-constructs t
+ modus-themes-bold-constructs t
+ modus-themes-mixed-fonts nil
+ modus-themes-subtle-line-numbers t
+ modus-themes-intense-mouseovers nil
+ modus-themes-deuteranopia nil
+ modus-themes-tabs-accented t
+ modus-themes-variable-pitch-ui nil
+ modus-themes-inhibit-reload nil
+
+ modus-themes-fringes nil ; {nil,'subtle,'intense}
+
+ ;; Options for `modus-themes-lang-checkers' are either nil (the
+ ;; default), or a list of properties that may include any of those
+ ;; symbols: `straight-underline', `text-also', `background',
+ ;; `intense' OR `faint'.
+ modus-themes-lang-checkers nil
+
+ ;; Options for `modus-themes-mode-line' are either nil, or a list
+ ;; that can combine any of `3d' OR `moody', `borderless',
+ ;; `accented', a natural number for extra padding (or a cons cell
+ ;; of padding and NATNUM), and a floating point for the height of
+ ;; the text relative to the base font size (or a cons cell of
+ ;; height and FLOAT)
+ modus-themes-mode-line '(accented borderless)
+
+ ;; Options for `modus-themes-markup' are either nil, or a list
+ ;; that can combine any of `bold', `italic', `background',
+ ;; `intense'.
+ modus-themes-markup '(background)
+
+ ;; Options for `modus-themes-syntax' are either nil (the default),
+ ;; or a list of properties that may include any of those symbols:
+ ;; `faint', `yellow-comments', `green-strings', `alt-syntax'
+ modus-themes-syntax '(faint alt-syntax)
+
+ ;; Options for `modus-themes-hl-line' are either nil (the default),
+ ;; or a list of properties that may include any of those symbols:
+ ;; `accented', `underline', `intense'
+ modus-themes-hl-line nil
+
+ ;; Options for `modus-themes-paren-match' are either nil (the
+ ;; default), or a list of properties that may include any of those
+ ;; symbols: `bold', `intense', `underline'
+ modus-themes-paren-match '(bold)
+
+ ;; Options for `modus-themes-links' are either nil (the default),
+ ;; or a list of properties that may include any of those symbols:
+ ;; `neutral-underline' OR `no-underline', `faint' OR `no-color',
+ ;; `bold', `italic', `background'
+ modus-themes-links '(neutral-underline)
+
+ ;; Options for `modus-themes-box-buttons' are either nil (the
+ ;; default), or a list that can combine any of `flat',
+ ;; `accented', `faint', `variable-pitch', `underline',
+ ;; `all-buttons', the symbol of any font weight as listed in
+ ;; `modus-themes-weights', and a floating point number
+ ;; (e.g. 0.9) for the height of the button's text.
+ modus-themes-box-buttons '(flat)
+
+ ;; Options for `modus-themes-prompts' are either nil (the
+ ;; default), or a list of properties that may include any of those
+ ;; symbols: `background', `bold', `gray', `intense', `italic'
+ modus-themes-prompts nil
+
+ ;; The `modus-themes-completions' is an alist that reads three
+ ;; keys: `matches', `selection', `popup'. Each accepts a nil
+ ;; value (or empty list) or a list of properties that can include
+ ;; any of the following (for WEIGHT read further below):
+ ;;
+ ;; `matches' - `background', `intense', `underline', `italic', WEIGHT
+ ;; `selection' - `accented', `intense', `underline', `italic', `text-also', WEIGHT
+ ;; `popup' - same as `selected'
+ ;; `t' - applies to any key not explicitly referenced (check docs)
+ ;;
+ ;; WEIGHT is a symbol such as `semibold', `light', or anything
+ ;; covered in `modus-themes-weights'. Bold is used in the absence
+ ;; of an explicit WEIGHT.
+ modus-themes-completions
+ '((matches . (regular))
+ (selection . (regular accented))
+ (popup . (regular accented)))
+
+ modus-themes-mail-citations '(faint) ; {nil,'intense,'faint,'monochrome}
+
+ ;; Options for `modus-themes-region' are either nil (the default),
+ ;; or a list of properties that may include any of those symbols:
+ ;; `no-extend', `bg-only', `accented'
+ modus-themes-region nil
+
+ ;; Options for `modus-themes-diffs': nil, 'desaturated, 'bg-only
+ modus-themes-diffs 'desaturated
+
+ modus-themes-org-blocks nil ; {nil,'gray-background,'tinted-background}
+
+ modus-themes-org-agenda ; this is an alist: read the manual or its doc string
+ '((header-block . (light 1.0))
+ (header-date . (underline-today grayscale workaholic 1.0))
+ (event . (accented italic varied))
+ (scheduled . rainbow)
+ (habit . simplified))
+
+ ;; The `modus-themes-headings' is an alist with lots of possible
+ ;; combinations, include per-heading-level tweaks: read the
+ ;; manual or its doc string
+ modus-themes-headings
+ '((0 . (light))
+ (1 . (rainbow light))
+ (2 . (rainbow light))
+ (3 . (rainbow regular))
+ (4 . (rainbow regular))
+ (5 . (rainbow))
+ (t . (semibold))))
+
+;;;; Loading themes
+(modus-themes-load-themes)
+
+(defun jao-colors-scheme-dark-p ()
+ (equal "dark" (getenv "JAO_COLOR_SCHEME")))
+
+(if (jao-colors-scheme-dark-p)
+ (modus-themes-load-vivendi)
+ (modus-themes-load-operandi))
+
+;; (jao-mode-line-adjust-faces)
+
+(provide 'jao-custom-themes)
+;;; jao-custom-themes.el ends here
diff --git a/attic/elisp/jao-doc-view-imenu.el b/attic/elisp/jao-doc-view-imenu.el
new file mode 100644
index 0000000..8b27c38
--- /dev/null
+++ b/attic/elisp/jao-doc-view-imenu.el
@@ -0,0 +1,74 @@
+;; jao-doc-view-imenu.el --- old docview/imenu -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2022 jao
+
+;; Author: jao <mail@jao.io>
+;; Keywords:
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; Old code that made its way into emacs 29. It defines imenu entries for
+;; docview.
+
+;;; Code:
+
+(defvar jao-pdf--outline-rx
+ "[^\t]+\\(\t+\\)\"\\(.+\\)\"\t#\\(?:page=\\)?\\([0-9]+\\)")
+
+(defun jao-pdf-outline (&optional file-name)
+ "Return an alist describing the given FILE-NAME (or current if nil).
+The result is cached as a local buffer variable."
+ (let* ((outline nil)
+ (fn (or file-name (buffer-file-name)))
+ (fn (shell-quote-argument (expand-file-name fn))))
+ (with-temp-buffer
+ (insert (shell-command-to-string (format "mutool show %s outline" fn)))
+ (goto-char (point-min))
+ (while (re-search-forward jao-pdf--outline-rx nil t)
+ (push `((level . ,(length (match-string 1)))
+ (title . ,(match-string 2))
+ (page . ,(string-to-number (match-string 3))))
+ outline)))
+ (setq jao-pdf--outline (nreverse outline))))
+
+(defun jao-pdf-imenu--index (items act)
+ (let ((level (alist-get 'level (car items)))
+ (index nil))
+ (while (and (car items) (<= level (alist-get 'level (car items))))
+ (let-alist (car items)
+ (let ((title (format "%s%s (%s)" "" .title .page)))
+ (if (> .level level)
+ (let ((sub (jao-pdf-imenu--index items act))
+ (fst (car index)))
+ (setq index (cdr index))
+ (push (cons (car fst) (cons fst (car sub))) index)
+ (setq items (cdr sub)))
+ (push `(,title 0 ,act ,.page) index)
+ (setq items (cdr items))))))
+ (cons (nreverse index) items)))
+
+(defun jao-pdf-imenu-index (&optional goto-page-fn file-name)
+ "Create an imenu index using `jao-pdf-outline'."
+ (let* ((goto (or goto-page-fn 'doc-view-goto-page))
+ (act (lambda (_name _pos page) (funcall goto page)))
+ (items (jao-pdf-outline file-name)))
+ (car (jao-pdf-imenu--index items act))))
+
+(defun jao-pdf-set-up-imenu ()
+ (setq-local imenu-create-index-function #'jao-pdf-imenu-index
+ imenu-submenus-on-top nil
+ imenu-sort-function nil)
+ (imenu-add-to-menubar "Outline"))
diff --git a/attic/media/jao-emms-info-track.el b/attic/elisp/jao-emms-info-track.el
index cf93625..cf93625 100644
--- a/attic/media/jao-emms-info-track.el
+++ b/attic/elisp/jao-emms-info-track.el
diff --git a/attic/media/jao-emms-lyrics.el b/attic/elisp/jao-emms-lyrics.el
index 0ea52e0..0ea52e0 100644
--- a/attic/media/jao-emms-lyrics.el
+++ b/attic/elisp/jao-emms-lyrics.el
diff --git a/attic/media/jao-emms-random-album.el b/attic/elisp/jao-emms-random-album.el
index 72e056b..72e056b 100644
--- a/attic/media/jao-emms-random-album.el
+++ b/attic/elisp/jao-emms-random-album.el
diff --git a/attic/media/jao-emms.el b/attic/elisp/jao-emms.el
index 53b3513..53b3513 100644
--- a/attic/media/jao-emms.el
+++ b/attic/elisp/jao-emms.el
diff --git a/attic/elisp/jao-frm.el b/attic/elisp/jao-frm.el
new file mode 100644
index 0000000..2658687
--- /dev/null
+++ b/attic/elisp/jao-frm.el
@@ -0,0 +1,222 @@
+;;; jao-frm.el --- use frm to show mailbox
+
+;; Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2019, 2020
+
+;; Author: Jose Antonio Ortega Ruiz <jao@gnu.org>
+;; Keywords: mail
+
+;; This file is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 3, or (at your option)
+;; any later version.
+
+;; This file is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs; see the file COPYING. If not, write to
+;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+;; Boston, MA 02111-1307, USA.
+
+;;; Commentary:
+
+;; Little hack to see the contents of your mailbox using GNU mailutils'
+;; `frm' program.
+;;
+;; Just put (require 'jao-frm) in your .emacs, and M-x jao-frm will pop up a
+;; new window with your mailbox contents (from and subject) as
+;; printed by frm. In this buffer, use `n' and `p' to move, `q' to close
+;; the window. `g' will call Gnus.
+;;
+
+;;; Code:
+
+;;;; Customisation:
+
+(defgroup jao-frm nil
+ "Frm-base mailbox checker"
+ :group 'mail
+ :prefix "jao-frm-")
+
+(defcustom jao-frm-exec-path "frm"
+ "frm executable path"
+ :group 'jao-frm
+ :type 'file)
+
+(defcustom jao-frm-mail-command 'gnus
+ "Emacs function to invoke when `g' is pressed on an frm buffer."
+ :group 'jao-frm
+ :type 'symbol)
+
+(defcustom jao-frm-mailboxes nil
+ "List of mailboxes to check, or directory containing them."
+ :group 'jao-frm
+ :type '(choice directory (repeat file)))
+
+(defface jao-frm-mailno-face '((t (:foreground "dark slate grey")))
+ "Face for the mail number."
+ :group 'jao-frm)
+
+(defface jao-frm-from-face '((t (:foreground "slate grey")))
+ "Face for From: header."
+ :group 'jao-frm)
+
+(defface jao-frm-subject-face '((t (:foreground "slate blue")))
+ "Face for Subject: header."
+ :group 'jao-frm)
+
+(defface jao-frm-mailbox-face '((t (:bold t :weight bold)))
+ "Face for mailbox name."
+ :group 'jao-frm)
+
+;;;; Mode:
+
+(defvar jao-frm-mode-map
+ (let ((map (make-keymap)))
+ (suppress-keymap map)
+ (define-key map [?q] 'jao-frm-delete-window)
+ (define-key map [?n] 'next-line)
+ (define-key map [?p] 'previous-line)
+ (define-key map [?r] 'jao-frm)
+ (define-key map [?g] (lambda ()
+ (interactive)
+ (funcall jao-frm-mail-command)))
+ (define-key map [(control k)] 'jao-frm-delete-message)
+ map))
+
+(setq jao-frm-font-lock-keywords
+ '(("^[^ :]+:" . 'jao-frm-mailbox-face)
+ ("^\\([ 0-9]+\\):\t+\\([^\t]+\\)\t+\\([^\n]+$\\)"
+ (1 'jao-frm-mailno-face)
+ (2 'jao-frm-from-face)
+ (3 'jao-frm-subject-face))))
+
+(defvar jao-frm-mode-syntax-table
+ (let ((st (make-syntax-table)))
+ st))
+
+(defun jao-frm-mode ()
+ "Major mode for displaying frm output."
+ (interactive)
+ (kill-all-local-variables)
+ (buffer-disable-undo)
+ (use-local-map jao-frm-mode-map)
+ (set (make-local-variable 'font-lock-defaults)
+ '(jao-frm-font-lock-keywords))
+ (set (make-local-variable 'truncate-lines) t)
+ (set (make-local-variable 'kill-whole-line) t)
+ (set (make-local-variable 'next-line-add-newlines) nil)
+ (setq major-mode 'jao-frm-mode)
+ (setq mode-name "frm")
+ (read-only-mode 1)
+ (goto-char 1))
+
+;;;; Mode commands:
+(defvar jao-frm-last-config nil)
+
+(defun jao-frm-delete-window ()
+ "Delete frm window and restore last win config"
+ (interactive)
+ (if (and (consp jao-frm-last-config)
+ (window-configuration-p (car jao-frm-last-config)))
+ (progn
+ (set-window-configuration (car jao-frm-last-config))
+ (goto-char (cadr jao-frm-last-config))
+ (setq jao-frm-last-config nil))
+ (bury-buffer)))
+
+(defun jao-frm-delete-message ()
+ "Delete message at point"
+ (interactive)
+ (when (eq (current-buffer) (get-buffer "*frm*"))
+ (beginning-of-line)
+ (when (search-forward-regexp "^ +\\([0-9]+\\):" nil t)
+ (let ((mn (string-to-number (match-string 1))))
+ (when (y-or-n-p (format "Delete message number %d? " mn))
+ (read-only-mode -1)
+ (shell-command (format "echo 'd %d'|mail" mn) t)
+ (jao-frm)
+ (when (= (point-max) (point-min))
+ (jao-frm-delete-window)
+ (message "Mailbox is empty")))))))
+
+;;;; Activate frm:
+(defun jao-frm-mbox-mails (mbox)
+ (let ((no (ignore-errors
+ (substring
+ (shell-command-to-string (format "frm -s n %s|wc -l" mbox))
+ 0 -1))))
+ (if (stringp no) (string-to-number no) 0)))
+
+(defun jao-frm-mail-number ()
+ (let ((no 0))
+ (dolist (b (jao-frm-mboxes) no) (setq no (+ no (jao-frm-mbox-mails b))))))
+
+(defun jao-frm-default-count-formatter (m n)
+ (format "%s: %s" (file-name-sans-extension (file-name-nondirectory m)) n))
+
+(defun jao-frm-mail-counts (fmt)
+ (let ((fmt (or fmt 'jao-frm-default-count-formatter)))
+ (remove nil
+ (mapcar (lambda (m)
+ (let ((n (jao-frm-mbox-mails m)))
+ (unless (zerop n) (funcall fmt m n))))
+ (jao-frm-mboxes)))))
+
+(defun jao-frm-display-mailbox (mbox)
+ (when (not (zerop (jao-frm-mbox-mails mbox)))
+ (insert (or (file-name-nondirectory mbox) mbox) ":\n\n")
+ (apply 'call-process
+ `(,jao-frm-exec-path nil ,(current-buffer) nil
+ "-s" "n" "-n" "-t" ,@(and mbox (list mbox))))
+ (newline 2)))
+
+(defun jao-frm-mboxes ()
+ (cond ((null jao-frm-mailboxes) (list (getenv "MAIL")))
+ ((listp jao-frm-mailboxes) jao-frm-mailboxes)
+ ((stringp jao-frm-mailboxes)
+ (if (file-directory-p jao-frm-mailboxes)
+ (directory-files jao-frm-mailboxes t "^[^.]")
+ (list jao-frm-mailboxes)))
+ (t (error "Error in mbox specification. Check `jao-frm-mailboxes'"))))
+
+;;;###autoload
+(defun jao-frm ()
+ "Run frm."
+ (interactive)
+ (let ((fbuff (get-buffer-create "*frm*"))
+ (inhibit-read-only t))
+ (if (not (eq fbuff (current-buffer)))
+ (setq jao-frm-last-config
+ (list (current-window-configuration) (point-marker))))
+ (with-current-buffer fbuff
+ (delete-region (point-min) (point-max))
+ (mapc 'jao-frm-display-mailbox (jao-frm-mboxes))
+ (unless (eq major-mode 'jao-frm-mode)
+ (jao-frm-mode))
+ (goto-char (point-min))
+ (if (= (point-min) (point-max))
+ (message "Mailbox is empty.")
+ (pop-to-buffer fbuff))
+ (when (and (boundp 'display-time-mode) display-time-mode)
+ (display-time-update)))))
+
+;;;###autoload
+(defun jao-frm-show-mail-numbers (&optional fmt)
+ (interactive)
+ (let ((counts (jao-frm-mail-counts fmt)))
+ (message (if counts (mapconcat 'identity counts ", ") "No mail"))))
+
+;;;###autoload
+(defun jao-frm-mail-string ()
+ (let ((counts (jao-frm-mail-counts
+ (lambda (m n)
+ (let ((m (substring (file-name-nondirectory m) 0 1)))
+ (format "%s%s" (capitalize m) n))))))
+ (mapconcat 'identity counts " ")))
+
+(provide 'jao-frm)
+
+;;; jao-frm.el ends here
diff --git a/attic/net/jao-maildir.el b/attic/elisp/jao-maildir.el
index 18a1725..18a1725 100644
--- a/attic/net/jao-maildir.el
+++ b/attic/elisp/jao-maildir.el
diff --git a/attic/media/jao-mpdn.el b/attic/elisp/jao-mpdn.el
index d707767..2e02d59 100644
--- a/attic/media/jao-mpdn.el
+++ b/attic/elisp/jao-mpdn.el
@@ -1,6 +1,6 @@
;;; jao-mpdn.el --- Notifications using elmpd -*- lexical-binding: t; -*-
-;; Copyright (C) 2021 jao
+;; Copyright (C) 2021, 2022 jao
;; Author: jao <mail@jao.io>
;; Keywords: convenience
@@ -116,7 +116,7 @@
(setq jao-mpdn--current (jao-mpdn--parse-retort txt))
(jao-mpdn--update-minibuffer)
(cond (next (funcall next))
- ((and (null jao-mpdn--current) jao-random-album-p)
+ ((and (null jao-mpdn--current) jao-random-album-active)
(jao-random-album-next)))))))
(jao-mpdn--send "currentsong" cb)))
diff --git a/attic/elisp/jao-notmuch-gnus.el b/attic/elisp/jao-notmuch-gnus.el
new file mode 100644
index 0000000..1576964
--- /dev/null
+++ b/attic/elisp/jao-notmuch-gnus.el
@@ -0,0 +1,226 @@
+;;; jao-notmuch-gnus.el --- notmuch-gnus interoperability -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2022 jao
+
+;; Author: jao <mail@jao.io>
+;; Keywords: mail
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; Helper functions to work in Gnus with mail indexed by notmuch.
+
+;;; Code:
+
+(require 'gnus)
+(require 'ol-gnus)
+(require 'notmuch-show)
+
+;;; Tagging in notmuch from Gnus buffers
+
+(defun jao-notmuch-gnus--notmuch-id (id)
+ (when id (if (string-match "<\\(.+\\)>" id) (match-string 1 id) id)))
+
+(defun jao-notmuch-gnus-message-id (&optional no-show)
+ "Find the id of currently selected message in Gnus or notmuch."
+ (when (and (not no-show) (derived-mode-p 'gnus-summary-mode))
+ (save-window-excursion (gnus-summary-show-article)))
+ (cond (gnus-original-article-buffer
+ (with-current-buffer gnus-original-article-buffer
+ (jao-notmuch-gnus--notmuch-id (message-field-value "message-id"))))
+ ((derived-mode-p 'notmuch-show-mode 'notmuch-tree-mode)
+ (notmuch-show-get-message-id))))
+
+(defun jao-notmuch-gnus-message-tags (id)
+ "Ask notmuch for the tags of a message with the given ID."
+ (let ((cmd (format "notmuch search --output=tags 'id:%s'" id)))
+ (split-string (shell-command-to-string cmd))))
+
+(defun jao-notmuch-gnus-tag-message (&optional id tags no-log)
+ "Interactively add or remove tags to the current message."
+ (interactive)
+ (let* ((id (or id (jao-notmuch-gnus-message-id)))
+ (current (unless tags (jao-notmuch-gnus-message-tags id)))
+ (prompt (format "Change tags %s" (string-join current "/")))
+ (tags (or tags (notmuch-read-tag-changes current prompt))))
+ (notmuch-tag (concat "id:" id) tags)
+ (unless no-log
+ (message "%s -> %s" current (jao-notmuch-gnus-message-tags id)))))
+
+(defun jao-notmuch-gnus-show-tags ()
+ "Display in the echo area the tags of the current message."
+ (interactive)
+ (when-let (id (jao-notmuch-gnus-message-id))
+ (message "%s" (string-join (jao-notmuch-gnus-message-tags id) " "))))
+
+(defun jao-notmuch-gnus-toggle-tags (tags &optional id current)
+ "Toggle the given TAGS list for the current Gnus message."
+ (let* ((id (or id (jao-notmuch-gnus-message-id)))
+ (current (or current (jao-notmuch-gnus-message-tags id)))
+ (tags (mapcar (lambda (x)
+ (concat (if (member x current) "-" "+") x))
+ tags)))
+ (notmuch-tag (concat "id:" id) tags)
+ (message "New tags: %s" (jao-notmuch-gnus-message-tags id))))
+
+(defun jao-notmuch-gnus-tag-mark ()
+ "Remove the new tag for an article when it's marked as seen by Gnus."
+ (when-let (id (jao-notmuch-gnus-message-id t))
+ (jao-notmuch-gnus-tag-message id '("-new") t)))
+
+(add-hook 'gnus-mark-article-hook #'jao-notmuch-gnus-tag-mark)
+
+(defun jao-notmuch-gnus--group-tags (group)
+ (when (string-match ".+:\\(.+\\)" group)
+ (split-string (match-string 1 group) "\\.")))
+
+(defun jao-notmuch-gnus-tag-on-move (op headers from to _d)
+ (when-let* ((to-tags (when to (jao-notmuch-gnus--group-tags to)))
+ (id (jao-notmuch-gnus--notmuch-id (mail-header-id headers))))
+ (if (eq op 'delete)
+ (let ((cur (seq-difference (jao-notmuch-gnus--group-tags from) to-tags)))
+ (jao-notmuch-gnus-toggle-tags (append cur to-tags) id cur))
+ (notmuch-tag (concat "id:" id)
+ (mapcar (lambda (x) (concat "+" x)) to-tags)))))
+
+(defun jao-notmuch-gnus-auto-tag ()
+ (add-hook 'gnus-summary-article-move-hook #'jao-notmuch-gnus-tag-on-move)
+ (add-hook 'gnus-summary-article-expire-hook #'jao-notmuch-gnus-tag-on-move))
+
+;;; Gnus search using notmuch
+
+(add-to-list 'gnus-search-expandable-keys "list")
+
+(cl-defmethod gnus-search-transform-expression ((engine gnus-search-notmuch)
+ (expr (head list)))
+ (format "List:%s" (gnus-search-transform-expression engine (cdr expr))))
+
+
+;;; Displaying search results in Gnus
+
+(defvar jao-notmuch-gnus-server "nnml"
+ "Name of the target Gnus server, e.g. nnml+mail.")
+
+(defvar jao-notmuch-gnus-mail-directory (expand-file-name "~/.emacs.d/gnus/Mail")
+ "Directory where Gnus stores its mail.")
+
+(defvar jao-notmuch-gnus-leafnode-directory (expand-file-name "~/var/news")
+ "Directory where leafnode stores its messages as seen by notmuch.")
+
+(defun jao-notmuch-gnus-file-to-group (file &optional maildir newsdir)
+ "Compute the Gnus group name from the given file name.
+Example:
+
+ IN: /home/jao/var/mail/jao/foo/cur/1259184569.M4818P3384.localhost,W=6921:2,S
+ OUT: nnml:jao.foo
+
+ IN: /home/jao/.emacs.d/gnus/Mail/jao.trove/32, /home/jao/.emacs.d/gnus/Mail/
+ OUT: nnml:jao.trove
+
+ IN: /home/jao/var/mail/gmane/foo/bar/100
+ OUT: nntp:gmane.foo.bar
+
+ IN: /home/jao/var/mail/bigml/cur/1259176906.M17483P24679.localhost,W=2488:2,S
+ OUT:nnimap:bigml/inbox"
+ (let* ((maildir (or maildir jao-notmuch-gnus-mail-directory))
+ (newsdir (or newsdir jao-notmuch-gnus-leafnode-directory))
+ (g (directory-file-name (file-name-directory file)))
+ (g (replace-regexp-in-string (file-name-as-directory maildir) "" g))
+ (g (replace-regexp-in-string (file-name-as-directory newsdir) "" g))
+ (nntp (string-match-p "^\\(gmane\\|gwene\\)/" g))
+ (g (cond (nntp (concat "nntp:" g))
+ ((file-name-directory g)
+ (replace-regexp-in-string "^\\([^/]+\\)/"
+ (concat jao-notmuch-gnus-server
+ ":\\1/")
+ (file-name-directory g) t))
+ (t (concat jao-notmuch-gnus-server ":" g))))
+ (g (replace-regexp-in-string "/" "." g))
+ (g (replace-regexp-in-string "[/.]$" "" g)))
+ (cond ((string-match ":$" g) (concat g "inbox"))
+ (nntp g)
+ (t (replace-regexp-in-string ":\\." ":" g)))))
+
+(defun jao-notmuch-gnus-id-to-file (id)
+ (when id
+ (let ((cmd (format "notmuch search --output=files %s" id)))
+ (string-trim (shell-command-to-string cmd)))))
+
+(defun jao-notmuch-gnus-goto-message (&optional msg-id filename)
+ "Open a summary buffer containing the current notmuch article."
+ (interactive)
+ (let* ((filename (or filename
+ (jao-notmuch-gnus-id-to-file msg-id)
+ (notmuch-show-get-filename)))
+ (group (when filename (jao-notmuch-gnus-file-to-group filename)))
+ (msg-id (or msg-id (notmuch-show-get-message-id)))
+ (msg-id (when msg-id (replace-regexp-in-string "^id:" "" msg-id))))
+ (if (and group msg-id)
+ (org-gnus-follow-link group msg-id)
+ (message "Couldn't get relevant infos for switching to Gnus."))))
+
+(defun jao-notmuch-gnus-engine (prefix config)
+ (let ((prefix (file-name-as-directory (expand-file-name prefix "~")))
+ (config (expand-file-name config gnus-home-directory)))
+ `(gnus-search-engine gnus-search-notmuch
+ (remove-prefix ,prefix)
+ (config-file ,config))))
+
+;;; Org links
+(defun jao-notmuch-gnus--fname (id)
+ (let ((cmd (format "notmuch search --output=files id:%s" id)))
+ (car (split-string (shell-command-to-string cmd)))))
+
+(defun jao-notmuch-gnus-org-follow (id)
+ (when-let* ((fname (jao-notmuch-gnus--fname id))
+ (group (jao-notmuch-gnus-file-to-group fname)))
+ (org-gnus-follow-link group id)))
+
+(defun jao-notmuch-gnus-org-store ()
+ (when-let (d (or (when (derived-mode-p 'notmuch-show-mode 'notmuch-tree-mode)
+ (cons (notmuch-show-get-message-id)
+ (notmuch-show-get-subject)))
+ (when (derived-mode-p 'gnus-summary-mode 'gnus-article-mode)
+ (cons (jao-notmuch-gnus-message-id)
+ (gnus-summary-article-subject)))))
+ (org-link-store-props :type "mail"
+ :link (concat "mail:" (car d))
+ :description (concat "Mail: " (cdr d)))))
+
+(org-link-set-parameters "mail"
+ :follow #'jao-notmuch-gnus-org-follow
+ :store #'jao-notmuch-gnus-org-store)
+
+(org-link-set-parameters "gnus" :store #'ignore)
+(org-link-set-parameters "notmuch" :store #'ignore)
+
+;;; consult-notmuch
+
+(with-eval-after-load "consult-notmuch"
+ (defun jao-notmuch-gnus--open-candidate (candidate)
+ "Open a notmuch-search completion candidate email in Gnus."
+ (message "candidate: %S" candidate)
+ (jao-notmuch-gnus-goto-message (consult-notmuch--thread-id candidate)))
+
+ (defun jao-gnus-consult-notmuch ()
+ "Run a consult-notmuch query that opens candidates in Gnus."
+ (interactive)
+ (jao-notmuch-gnus--open-candidate (consult-notmuch--search)))
+
+ (consult-customize jao-gnus-consult-notmuch :preview-key 'any))
+
+;;; .
+(provide 'jao-notmuch-gnus)
+;;; jao-notmuch-gnus.el ends here
diff --git a/attic/net/jao-notmuch-move.el b/attic/elisp/jao-notmuch-move.el
index eb7ea4c..eb7ea4c 100644
--- a/attic/net/jao-notmuch-move.el
+++ b/attic/elisp/jao-notmuch-move.el
diff --git a/attic/net/jao-notmuch-tree-fold.el b/attic/elisp/jao-notmuch-tree-fold.el
index ef528df..ef528df 100644
--- a/attic/net/jao-notmuch-tree-fold.el
+++ b/attic/elisp/jao-notmuch-tree-fold.el
diff --git a/attic/elisp/jao-recoll.el b/attic/elisp/jao-recoll.el
new file mode 100644
index 0000000..b23106f
--- /dev/null
+++ b/attic/elisp/jao-recoll.el
@@ -0,0 +1,131 @@
+;;; jao-recoll.el -- Displaying recoll queries -*- lexical-binding: t; -*-
+
+;; Copyright (c) 2017, 2020, 2021, 2022 Jose Antonio Ortega Ruiz
+
+;; This file is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This file is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+;; Author: Jose Antonio Ortega Ruiz <jao@gnu.org>
+;; Start date: Wed Nov 01, 2017 18:14
+
+
+;;; Comentary:
+
+;; A simple interactive command to perform recoll queries and display
+;; its results using org markup.
+
+;;; Code:
+
+(require 'org)
+
+(define-derived-mode recoll-mode org-mode "Recoll"
+ "Simple mode for showing recoll query results"
+ (read-only-mode 1))
+
+(defvar jao-recoll--file-regexp
+ "\\(\\w+/.+\\)\t+\\[\\([^]]+\\)\\]\t+\\[\\([^\t]+\\)\\].+")
+
+(defvar jao-recoll-flags "-A -p 5 -n 100")
+
+(defvar jao-recoll-single-buffer t)
+(defvar-local jao-recoll--last-query nil)
+(defvar-local jao-recoll--last-full-query nil)
+
+(defun jao-recoll-show-query ()
+ (interactive)
+ (message (concat jao-recoll--last-query "\n"
+ jao-recoll--last-full-query)))
+
+(defun jao-recoll-requery ()
+ (interactive)
+ (jao-recoll jao-recoll--last-query))
+
+(defun jao-recoll--buffer (q)
+ (get-buffer-create (if jao-recoll-single-buffer
+ "*Recoll*"
+ (format "*Recoll: '%s'*" q))))
+
+(defun jao-recoll--format-snippets (lnk)
+ (when (looking-at-p "SNIPPETS")
+ (let ((kill-whole-line t))
+ (kill-line)
+ (while (and (not (eobp)) (not (looking-at-p "/SNIPPETS")))
+ (cond ((looking-at "^\\([1-9][0-9]*\\) : ")
+ (replace-match (format " - [[%s::\\1][\\1]] : " lnk)))
+ ((looking-at "^0 : \\(.[^\n]+\\)")
+ (let ((desc (match-string 1)))
+ (replace-match " - ")
+ (insert (org-make-link-string lnk desc))))
+ (t (insert " - ")))
+ (forward-line 1))
+ (unless (eobp) (kill-line)))))
+
+(defun jao-recoll--org-link (uri desc mime)
+ (cond ((string= mime "application/pdf")
+ (concat "doc:" (file-name-nondirectory uri)))
+ ((string= mime "message/rfc822") (concat "message:" (substring uri 7)))
+ ((string= mime "text/x-orgmode-sub") (concat uri "::*" desc))
+ (t uri)))
+
+;;;###autoload
+(defun jao-recoll (&optional prefix-query)
+ "Performs a query using recoll and shows the results using org markup."
+ (interactive)
+ (let* ((query (read-string "Recoll query: " prefix-query))
+ (cmd (format "recoll %s -t %s"
+ jao-recoll-flags (shell-quote-argument query)))
+ (inhibit-read-only t))
+ (with-current-buffer (jao-recoll--buffer query)
+ (recoll-mode)
+ (delete-region (point-min) (point-max))
+ (shell-command cmd t)
+ (setq jao-recoll--last-query query)
+ (goto-char (point-min))
+ (when (looking-at-p "Recoll query:")
+ (setq jao-recoll--last-full-query
+ (string-trim (thing-at-point 'line)))
+ (let ((kill-whole-line nil)) (kill-line))
+ (insert query)
+ (forward-line 2))
+ (open-line 1)
+ (while (search-forward-regexp jao-recoll--file-regexp nil t)
+ (let* ((mime (match-string 1))
+ (ref (match-string 2))
+ (desc (match-string 3))
+ (start (match-beginning 0))
+ (end (match-end 0))
+ (lnk (jao-recoll--org-link ref desc mime))
+ (desc (if (string= mime "text/x-orgmode-sub")
+ (org-link-display-format
+ (concat (file-name-nondirectory ref) " :: " desc))
+ desc)))
+ (delete-region start end)
+ (insert "* " (org-make-link-string lnk desc) " (" mime ")")
+ (forward-line)
+ (jao-recoll--format-snippets lnk)))
+ (pop-to-buffer (current-buffer))
+ (goto-char (point-min))
+ (org-next-visible-heading 1)
+ (org-overview)
+ (jao-recoll-show-query))))
+
+(define-key recoll-mode-map [?n] 'org-next-link)
+(define-key recoll-mode-map [?p] 'org-previous-link)
+(define-key recoll-mode-map [?q] 'bury-buffer)
+(define-key recoll-mode-map [?r] 'jao-recoll-requery)
+(define-key recoll-mode-map [?g] 'jao-recoll-requery)
+(define-key recoll-mode-map [?w] 'jao-recoll-show-query)
+
+;;; .
+(provide 'jao-recoll)
+;;; jao-recoll.el ends here
diff --git a/attic/elisp/misc.el b/attic/elisp/misc.el
new file mode 100644
index 0000000..6484310
--- /dev/null
+++ b/attic/elisp/misc.el
@@ -0,0 +1,951 @@
+;; -*- lexical-binding: t; -*-
+
+;;; programming languages
+;;;; Erlang
+(use-package erlang
+ :ensure t
+ :custom ((inferior-erlang-machine-options '("shell"))
+ (inferior-erlang-machine "rebar3")
+ (inferior-erlang-shell-type nil)
+ (erlang-indent-level 4))
+
+ ;; :bind (:map erlang-mode-map (("C-c C-z" . jao-vterm-repl-pop-to-repl)))
+
+ :init
+ ;; (require 'jao-vterm-repl)
+ ;; (add-to-list 'auto-mode-alist '("^rebar\\.config\\`" . erlang-mode))
+ ;; (jao-vterm-repl-register "rebar.config" "rebar3 shell" "^[0-9]+> ")
+
+ :config
+ ;; (defun jao-erlang-current-module ()
+ ;; (when (save-excursion (goto-char (point-min))
+ ;; (re-search-forward "^-module(\\([^)]+\\))" nil t))
+ ;; (match-string-no-properties 1)))
+
+ ;; (defun jao-erlang-compile (arg)
+ ;; (interactive "P")
+ ;; (save-some-buffers)
+ ;; (when-let ((mname (jao-erlang-current-module)))
+ ;; (with-current-buffer (jao-vterm-repl)
+ ;; (vterm-send-string (format "c(%s).\n" mname))
+ ;; (sit-for 0)
+ ;; (setq compilation-last-buffer (current-buffer))
+ ;; (when arg (jao-vterm-repl-pop-to-repl)))))
+
+ ;; (setq erlang-shell-function #'jao-vterm-repl
+ ;; erlang-shell-display-function #'jao-vterm-repl-pop-to-repl
+ ;; erlang-compile-function #'jao-erlang-compile)
+ )
+;;;; Idris
+(use-package idris-mode
+ :ensure t
+ :custom ((idris-interpreter-path "idris2")
+ (idris-pretty-printer-width 80)
+ (idris-repl-history-file "~/.emacs.d/cache/idris-history.eld")
+ (idris-stay-in-current-window-on-compiler-error t)))
+(jao-define-attached-buffer "^\\*idris.*")
+
+;;;; Racket
+(use-package racket-mode
+ :ensure t
+ :init (setq racket-show-functions '(racket-show-echo-area)
+ racket-documentation-search-location 'local)
+ :config
+ (jao-define-attached-buffer "\\`\\*Racket REPL")
+ (jao-define-attached-buffer "\\`\\*Racket Describe" 0.5)
+ (add-hook 'racket-mode-hook #'paredit-mode)
+ (require 'racket-xp)
+ (add-hook 'racket-mode-hook #'racket-xp-mode)
+ :bind (:map racket-xp-mode-map (("C-c C-S-d" . racket-xp-documentation)
+ ("C-c C-d" . racket-xp-describe))))
+
+;;; smart scan
+(use-package smartscan
+ :ensure t
+ :commands smartscan-mode
+ :init (add-hook 'prog-mode-hook #'smartscan-mode)
+ :diminish)
+
+;;; easy escape
+(use-package easy-escape
+ :ensure t
+ :config
+ (set-face-attribute 'easy-escape-face nil :underline t)
+ (set-face-attribute 'easy-escape-delimiter-face nil :underline t)
+ :hook (emacs-lisp-mode . easy-escape-minor-mode)
+ :diminish (easy-escape-minor-mode . "^"))
+
+;;; vterm
+(use-package vterm
+ :ensure t
+ :demand t
+ :commands (vterm vterm-mode)
+ :init (setq vterm-kill-buffer-on-exit t
+ vterm-copy-exclude-prompt t
+ jao-use-vterm t)
+ :config
+
+ (defun jao-vterm-send-C-c () (interactive) (vterm-send-key "c" nil nil t))
+
+ (jao-define-attached-buffer "\\*vterm\\*" 0.5)
+
+ :bind (:map vterm-mode-map ("C-c C-c" . jao-vterm-send-C-c)))
+
+(defun jao-exec-in-vterm (cmd bname)
+ (if (string-blank-p (or cmd ""))
+ (vterm)
+ (let ((vterm-shell cmd)
+ (vterm-kill-buffer-on-exit t)
+ (buff (generate-new-buffer bname)))
+ (switch-to-buffer buff)
+ (vterm-mode))))
+;;; ace window
+(use-package ace-window
+ :ensure t
+ :demand t
+ :init (setq aw-keys '(?a ?s ?d ?f ?g ?h ?j ?k ?l)
+ aw-char-position 'top-left
+ aw-ignore-current nil
+ aw-dispatch-when-more-than 2
+ aw-leading-char-style 'path
+ aw-display-mode-overlay t
+ aw-scope 'frame)
+ :config
+
+ (defun jao-ace-consult-buffer-other-window (w)
+ (interactive)
+ (aw-switch-to-window w)
+ (consult-buffer))
+
+ (setf (alist-get ?b aw-dispatch-alist)
+ '(jao-ace-consult-buffer-other-window "Consult buffer"))
+
+ (setf (alist-get ?B aw-dispatch-alist)
+ (alist-get ?u aw-dispatch-alist))
+
+
+ :bind (("M-o" . ace-window)
+ ("M-O" . ace-swap-window)
+ ("C-x 4 t" . ace-swap-window)))
+
+;;; switch window
+(use-package switch-window
+ :ensure t
+ :custom ((switch-window-minibuffer-shortcut ?z)
+ (switch-window-background t)
+ (switch-window-shortcut-style 'qwerty)
+ (switch-window-shortcut-appearance 'text)
+ (switch-window-timeout 7)
+ (switch-window-threshold 2))
+ :config
+ (defun jao-switch-window--then (prompt cmd)
+ (let ((f `(lambda ()
+ (let ((default-directory ,default-directory))
+ (call-interactively ',cmd)))))
+ (switch-window--then prompt f f)))
+
+ (defun jao-switch-window-then-dired ()
+ (interactive)
+ (jao-switch-window--then "Find directory" 'dired))
+
+ (defun jao-switch-window-then-find-file ()
+ (interactive)
+ (jao-switch-window--then "Find file" 'find-file))
+
+ (defun jao-switch-window-then-consult-buffer ()
+ (interactive)
+ (jao-switch-window--then "Switch to buffer" 'consult-buffer))
+
+ :bind (("s-o" . switch-window)
+ ("C-x 4 d" . jao-switch-window-then-dired)
+ ("C-x 4 f" . jao-switch-window-then-find-file)
+ ("C-x 4 b" . jao-switch-window-then-consult-buffer)))
+
+;;; git helpers
+(use-package dired-git-info
+ :ensure t
+ :bind (:map dired-mode-map (")" . dired-git-info-mode)))
+
+(use-package gist :ensure t)
+
+;;; json
+(use-package json-mode :ensure t)
+(use-package json-navigator :ensure t)
+
+;;; deft
+(use-package deft
+ :ensure t
+ :after jao-org-notes
+ :commands deft
+ :init (setq deft-extensions '("org" "md")
+ deft-directory jao-org-notes-dir
+ deft-use-filename-as-title nil
+ deft-use-filter-string-for-name t
+ deft-file-naming-rules '((noslash . "-")
+ (nospace . "-")
+ (case-fn . downcase))
+ deft-org-mode-title-prefix t
+ deft-recursive t
+ deft-recursive-ignore-dir-regexp (regexp-opt '("." ".." "attic"))
+ deft-strip-summary-regexp
+ (concat "\\([\n\t]"
+ "\\|^#\\+\\(title\\|created\\|date\\|author\\):.*$"
+ "\\|^#\\+\\(file\\)?tags: *\\)"))
+ :config (setq deft-strip-title-regexp
+ (concat "\\(^#\\+title: *\\)\\|" deft-strip-title-regexp))
+ :bind (("<f9>" . deft)))
+
+;;; detached
+(use-package detached
+ :ensure t
+ :init
+ (detached-init)
+ :config
+ (transient-define-prefix jao-transient-detached ()
+ ["Detached sessions"
+ ("v" "view session output" detached-view-session)
+ ("a" "attach to a session" detached-attach-session)
+ ("=" "diff a session with another session" detached-diff-session)
+ ("c" "open the session output in compilation mode" detached-compile-session)
+ ("r" "rerun a session" detached-rerun-session)
+ ("i" "insert the session's command at point" detached-insert-session-command)
+ ("w" "copy the session's shell command" detached-copy-session-command)
+ ("W" "copy the session's output" detached-copy-session)
+ ("k" "kill an active session" detached-kill-session)])
+
+ :bind (;; Replace `async-shell-command' with `detached-shell-command'
+ ([remap async-shell-command] . detached-shell-command)
+ ;; Replace `compile' with `detached-compile'
+ ([remap compile] . detached-compile)
+ ([remap recompile] . detached-compile-recompile)
+ ;; Replace built in completion of sessions with `consult'
+ ([remap detached-open-session] . detached-consult-session)
+ ("s-d" . jao-transient-detached))
+ :custom ((detached-show-output-on-attach t)
+ (detached-terminal-data-command system-type)))
+
+(defun jao-detached-exec (command)
+ (if (fboundp 'detached-create-session)
+ (detached-create-session command)
+ (jao-shell-exec command)))
+
+;;; time display
+(setq display-time-world-list
+ '(("Europe/Paris" "Barcelona")
+ ("America/Los_Angeles" "Los Angeles")
+ ("America/New_York" "New York")
+ ("Europe/London" "London")
+ ("Asia/Calcutta" "Bangalore")
+ ("Asia/Tokyo" "Tokyo")))
+
+(defun jao-time--pdt-hour ()
+ (jao-time-at-zone "%H" "America/Los_Angeles"))
+
+(defun jao-time--chicago-hour ()
+ (jao-time-at-zone "%H" "America/Chicago"))
+
+(defun jao-time-at-zone (format zone)
+ (set-time-zone-rule zone)
+ (prog1 (format-time-string format)
+ (set-time-zone-rule nil)))
+
+(defun jao-time-echo-la-time ()
+ (interactive)
+ (message (jao-time-at-zone "LA %H:%M" "America/Los_Angeles")))
+
+(defun jao-time-echo-times ()
+ (interactive)
+ (let ((msg (format "%s (%s)"
+ (format-time-string "%a, %e %B - %H:%M")
+ (jao-time-at-zone "%H:%M" "America/Los_Angeles"))))
+ (jao-notify msg "" (jao-data-file "clock-world-icon.png"))))
+
+(defun jao-time-to-epoch (&optional s)
+ "Transform a time string to an epoch integer in milliseconds."
+ (interactive)
+ (let ((s (or s (read-string "Time string: " (thing-at-point 'string)))))
+ (message "%s = %s"
+ s
+ (round (* 1000 (time-to-seconds (parse-time-string s)))))))
+
+(defun jao-epoch-to-time (&optional v)
+ "Transform an epoch, given in milliseconds, to a time string."
+ (interactive)
+ (let ((v (or v (read-number "Milliseconds: " (thing-at-point 'number)))))
+ (message "%s = %s" v
+ (format-time-string "%Y-%m-%d %H:%M:%S"
+ (seconds-to-time (/ v 1000.0))))))
+;;; mu4e
+(jao-load-path "mu4e")
+(use-package mu4e
+ :init
+ (setq mu4e-attachment-dir (expand-file-name "~/var/download/attachments")
+ mu4e-change-filenames-when-moving nil
+ mu4e-completing-read-function 'completing-read
+ mu4e-display-update-status-in-modeline nil
+ mu4e-get-mail-command "true" ;; "run-mb.sh || [ $? -eq 1 ]"
+ mu4e-headers-show-threads t
+ mu4e-headers-sort-direction 'ascending
+ mu4e-headers-visible-columns 100
+ mu4e-headers-visible-lines 12
+ mu4e-hide-index-messages t
+ mu4e-index-cleanup t ;; don't do a full cleanup check
+ mu4e-index-lazy-check t ;; don't consider up-to-date dirs
+ mu4e-maildir "~/var/mail"
+ mu4e-split-view 'horizontal ;; 'vertical
+ mu4e-update-interval 300
+ mu4e-use-fancy-chars nil
+ mu4e-user-mail-address-list jao-mails
+ mu4e-view-show-addresses t
+ mu4e-view-show-images t
+ mu4e-maildir-shortcuts '((:maildir "/jao/inbox" :key ?j)
+ (:maildir "/bigml/inbox" :key ?b))
+ jao-mu4e-uninteresting-mail-query
+ (concat
+ "flag:unread AND NOT flag:trashed"
+ " AND NOT (maildir:/bigml/inbox OR maildir:/bigml/bugs OR"
+ " maildir:/bigml/support OR maildir:/jao/inbox)")
+ jao-mu4e-interesting-mail-query
+ (concat
+ "flag:unread AND NOT flag:trashed"
+ " AND (maildir:/bigml/inbox OR maildir:/bigml/bugs OR"
+ " maildir:/bigml/support OR maildir:/jao/inbox)")
+ mu4e-bookmarks
+ `((:name "Inbox" :query ,jao-mu4e-interesting-mail-query :key ?i)
+ (:name "Other messages"
+ :query ,jao-mu4e-uninteresting-mail-query
+ :key 117)
+ (:name "Today's messages" :query "date:today..now"
+ :key 116)
+ (:name "Last 7 days" :query "date:7d..now" :hide-unread t
+ :key 119)
+ (:name "Messages with PDFs"
+ :query "mime:application/pdf OR mime:x-application/pdf"
+ :key 112)))
+
+ :config
+ (defun jao-mu4e--maildir (msg)
+ (when msg
+ (let ((md (mu4e-message-field msg :maildir)))
+ (when (string-match "/\\([^/]+\\)/.*" md)
+ (match-string 1 md)))))
+
+ (defun jao-mu4e--refile-folder (name)
+ (lambda (msg)
+ (let ((md (jao-mu4e--maildir msg)))
+ (if (string= md name)
+ (concat "/jao/" name)
+ (format "/%s/%s" md name)))))
+
+ (setq mu4e-sent-folder (jao-mu4e--refile-folder "sent"))
+ (setq mu4e-drafts-folder (jao-mu4e--refile-folder "drafts"))
+ (setq mu4e-trash-folder (jao-mu4e--refile-folder "trash"))
+ (setq mu4e-refile-folder (jao-mu4e--refile-folder "trove"))
+
+ (setq mu4e-contexts nil)
+
+ (setq mu4e-view-show-images t)
+ (when (fboundp 'imagemagick-register-types)
+ (imagemagick-register-types))
+
+ (define-key mu4e-view-mode-map [remap mu4e-view-verify-msg-popup]
+ 'epa-mail-verify)
+
+ ;; View html message in browser (type aV)
+ (add-to-list 'mu4e-view-actions
+ '("ViewInBrowser" . mu4e-action-view-in-browser) t))
+
+;;; twtxt
+(use-package twtxt
+ :ensure t
+ :init (setq twtxt-file (expand-file-name "~/doc/jao.io/twtxt")
+ twtxt-following
+ '(("yarn" "https://twtxt.net/user/news/twtxt.txt"))))
+;;; corfu bits
+(defun jao-corfu-enable-no-auto ()
+ (setq-local corfu-auto nil)
+ (corfu-mode 1))
+
+(defmacro jao-corfu-no-auto (mode)
+ (let ((mode-name (intern (format "%s-mode" mode)))
+ (hook-name (intern (format "%s-mode-hook" mode))))
+ `(with-eval-after-load ',mode
+ (add-to-list 'corfu-excluded-modes ',mode-name)
+ (add-hook ',hook-name #'jao-corfu-enable-no-auto))))
+
+(jao-corfu-no-auto eshell)
+
+;;; gnus bits
+
+(jao-transient-major-mode gnus-group
+ ["Search"
+ ("zc" "consult search" consult-notmuch)
+ ("zf" "consult folder search" jao-consult-notmuch-folder)
+ ("g" "gnus search" gnus-group-read-ephemeral-search-group)])
+
+(defun jao-gnus-restart-servers ()
+ (interactive)
+ (message "Restarting all servers...")
+ (gnus-group-enter-server-mode)
+ (gnus-server-close-all-servers)
+ (gnus-server-open-all-servers)
+ (gnus-server-exit)
+ (message "Restarting all servers... done"))
+
+;;;; startup and kill
+;; close gnus when closing emacs, but ask when exiting
+(setq gnus-interactive-exit t)
+
+(defun jao-gnus-started-hook ()
+ (add-hook 'before-kill-emacs-hook 'gnus-group-exit))
+
+(add-hook 'gnus-started-hook 'jao-gnus-started-hook)
+
+(defun jao-gnus-after-exiting-hook ()
+ (remove-hook 'before-kill-emacs-hook 'gnus-group-exit))
+
+(add-hook 'gnus-after-exiting-gnus-hook 'jao-gnus-after-exiting-hook)
+
+;; define a wrapper around the save-buffers-kill-emacs
+;; to run the new hook before:
+(advice-add 'save-buffers-kill-emacs :before (lambda ()
+ (run-hooks 'before-kill-emacs-hook)))
+
+(defadvice save-buffers-kill-emacs
+ (before my-save-buffers-kill-emacs activate)
+ "Install hook when emacs exits before emacs asks to save this and that."
+ (run-hooks 'before-kill-emacs-hook))
+
+(advice-remove 'ad-Advice-save-buffers-kill-emacs 'save-buffers-kill-emacs)
+
+;;;; delayed expiry
+(defvar jao-gnus--expire-every 50)
+(defvar jao-gnus--get-count (1+ jao-gnus--expire-every))
+
+(defun jao-gnus-get-new-news (&optional arg)
+ (interactive "p")
+ (when (and jao-gnus--expire-every
+ (> jao-gnus--get-count jao-gnus--expire-every))
+ (when jao-gnus-use-pm-imap (gnus-group-catchup "nnimap:pm/spam" t))
+ (gnus-group-expire-all-groups)
+ (setq jao-gnus--get-count 0))
+ (setq jao-gnus--get-count (1+ jao-gnus--get-count))
+ (gnus-group-get-new-news (max (if (= 1 jao-gnus--get-count) 4 3)
+ (or arg 0))))
+
+(define-key gnus-group-mode-map "g" 'jao-gnus-get-new-news)
+(define-key gnus-group-mode-map "\C-x\C-s" #'gnus-group-save-newsrc)
+
+(defun jao-gnus--first-group ()
+ (when (derived-mode-p 'gnus-group-mode)
+ (gnus-group-first-unread-group)))
+
+(with-eval-after-load "jao-afio"
+ (add-hook 'jao-afio-switch-hook #'jao-gnus--first-group))
+
+;;;; remove HTML from From contents (arxiv with r2e)
+(require 'shr)
+(defvar jao-gnus--from-rx
+ (concat "From: \\\"?\\( " jao-gnus--news-rx "\\)"))
+
+(defun jao-gnus-remove-anchors ()
+ (save-excursion
+ (goto-char (point-min))
+ (cond ((re-search-forward jao-gnus--from-rx nil t)
+ (replace-match "" nil nil nil 1))
+ ((re-search-forward "[gq].+ updates on arXiv.org: " nil t)
+ (replace-match "")
+ (let ((begin (point)))
+ (when (re-search-forward "^\\(To\\|Subject\\):" nil t)
+ (beginning-of-line)
+ (let ((shr-width 10000))
+ (shr-render-region begin (1- (point))))))))))
+
+(add-hook 'gnus-part-display-hook 'jao-gnus-remove-anchors)
+
+;;;; find message id
+(defun jao-gnus-file-message-id (filename)
+ (with-temp-buffer
+ (insert-file filename)
+ (goto-char (point-min))
+ (when (re-search-forward "^[Mm]essage-[Ii][Dd]: <?\\([^><]+\\)>?" nil t)
+ (match-string 1))))
+;;; old volume controls
+(defun jao-player-volume-delta (raise)
+ (jao-player-vol-delta (if raise 5 -5))
+ (sit-for 0.05)
+ (jao-player-show-volume))
+
+(defun jao-player-volume-raise ()
+ (interactive)
+ (jao-player-volume-delta t))
+
+(defun jao-player-volume-lower ()
+ (interactive)
+ (jao-player-volume-delta nil))
+
+(defun jao-player-show-volume ()
+ (interactive)
+ (jao-notify "Volume" (format "%s%%" (jao-player-volume))))
+
+;;; corfu
+(use-package corfu
+ :ensure t
+ :init (setq corfu-echo-documentation 0.25
+ corfu-cycle t
+ corfu-count 15
+ corfu-quit-no-match t
+ corfu-auto t
+ corfu-commit-predicate nil
+ corfu-preview-current nil
+ corfu-preselect-first t
+ corfu-min-width 20
+ corfu-max-width 100)
+ :config
+
+ ;; show eldoc string immediately after accepted completion too
+ (with-eval-after-load "eldoc"
+ (eldoc-add-command-completions "corfu-"))
+
+ (defun jao-corfu-no-auto () (setq-local corfu-auto nil) (corfu-mode))
+
+ (add-hook 'eshell-mode-hook #'jao-corfu-no-auto)
+
+ (defun jao-corfu--active-p ()
+ (and (>= corfu--index 0) (/= corfu--index corfu--preselect)))
+
+ (defun jao-corfu-quit-or-insert ()
+ (interactive)
+ (if (jao-corfu--active-p) (corfu-insert) (corfu-quit)))
+
+ (defun jao-corfu-quit-or-previous ()
+ (interactive)
+ (if (jao-corfu--active-p)
+ (corfu-previous)
+ (corfu-quit)
+ (previous-line)))
+
+ :bind (:map corfu-map
+ ("C-<return>" . corfu-insert)
+ ("\r" . jao-corfu-quit-or-insert)
+ ("C-p" . jao-corfu-quit-or-previous)))
+
+(defun corfu-in-minibuffer ()
+ (when (not (bound-and-true-p vertico--input))
+ (setq-local corfu-echo-documentation nil)
+ (corfu-mode 1)))
+
+(defun jao-corfu-maybe-enable ()
+ (when (and (not jao-wayland-enabled) (display-graphic-p))
+ (add-hook 'minibuffer-setup-hook #'corfu-in-minibuffer 1)
+ (global-corfu-mode 1)))
+
+(add-hook 'after-init-hook #'jao-corfu-maybe-enable)
+
+;;; company
+(use-package company
+ :ensure t
+ :custom ((company-backends '(company-capf
+ company-bbdb
+ company-files
+ company-dabbrev
+ company-keywords))
+ (company-global-modes '(not slack-message-buffer-mode
+ circe-channel-mode
+ telega-chat-mode))
+ (company-format-margin-function nil) ;; #'company-text-icons-margin
+ (company-idle-delay 0.2)
+ (company-lighter "")
+ (company-lighter-base "")
+ (company-show-numbers nil)
+ (company-selection-wrap-around t)
+ (company-tooltip-limit 15)
+ (company-tooltip-align-annotations t)
+ (company-tooltip-offset-display 'lines)) ;; 'scrollbar
+
+ :config
+ (defun jao-complete-at-point ()
+ "Complete using company unless we're in the minibuffer."
+ (interactive)
+ (if (or (not company-mode) (window-minibuffer-p))
+ (completion-at-point)
+ (company-manual-begin)))
+
+ (defun jao-company-use-in-tab ()
+ (global-set-key [remap completion-at-point] #'jao-complete-at-point)
+ (global-set-key [remap completion-symbol] #'jao-complete-at-point)
+ (global-set-key (kbd "M-TAB") #'jao-complete-at-point))
+
+ (jao-company-use-in-tab)
+
+ :bind (:map company-active-map
+
+ ("<tab>" . company-complete-common-or-cycle)
+ ("TAB" . company-complete-common-or-cycle)
+
+ ("C-h" . company-show-doc-buffer)
+ ("M-." . company-show-location)
+ ("C-<return>" . company-complete-selection)
+ ([remap return] . company-abort)
+ ("RET" . company-abort)
+
+ :filter (or (not (derived-mode-p 'eshell-mode))
+ (company-explicit-action-p))
+ ("<return>" . company-complete-selection)
+ ("RET" . company-complete-selection))
+ :diminish)
+
+(unless (display-graphic-p) (global-company-mode 1))
+
+
+;;; eldoc for magit status/log buffers
+(defun jao-magit-eldoc-for-commit (_callback)
+ (when-let ((commit (magit-commit-at-point)))
+ (with-temp-buffer
+ (magit-git-insert "show"
+ "--format=format:%an <%ae>, %ar"
+ (format "--stat=%d" (window-width))
+ commit)
+ (goto-char (point-min))
+ (put-text-property (point-min) (line-end-position) 'face 'bold)
+ (buffer-string))))
+
+(defun jao-magit-eldoc-setup ()
+ (add-hook 'eldoc-documentation-functions
+ #'jao-magit-eldoc-for-commit nil t)
+ (eldoc-mode 1))
+
+(add-hook 'magit-log-mode-hook #'jao-magit-eldoc-setup)
+(add-hook 'magit-status-mode-hook #'jao-magit-eldoc-setup)
+
+(with-eval-after-load "eldoc"
+ (eldoc-add-command 'magit-next-line)
+ (eldoc-add-command 'magit-previous-line)
+ (eldoc-add-command 'magit-section-forward)
+ (eldoc-add-command 'magit-section-backward))
+
+;;; outline mode for notmuch tree view
+
+(defun jao-notmuch-tree--msg-prefix (msg)
+ (insert (propertize (if (plist-get msg :first) "> " " ") 'display " ")))
+
+(defun jao-notmuch-tree--mode-setup ()
+ (setq-local outline-regexp "^> \\|^En")
+ (outline-minor-mode t))
+
+(defun jao-notmuch-tree-hide-others (&optional and-show)
+ (interactive)
+ (outline-hide-body)
+ (outline-show-entry)
+ (when and-show (notmuch-tree-show-message nil)))
+
+(defsubst jao-notmuch-tree--message-open ()
+ (and (buffer-live-p notmuch-tree-message-buffer)
+ (get-buffer-window notmuch-tree-message-buffer)))
+
+(defsubst jao-notmuch--get-prop (prop &optional props)
+ (or (and props (plist-get props prop))
+ (notmuch-tree-get-prop prop)
+ (notmuch-show-get-prop prop)))
+
+(defun jao-notmuch--looking-at-match-p ()
+ (and (jao-notmuch--get-prop :match)
+ (equal (jao-notmuch--get-prop :orig-tags)
+ (jao-notmuch--get-prop :tags))))
+
+(defun jao-notmuch-tree--next (prev thread no-exit &optional ignore-new)
+ (let ((line-move-ignore-invisible nil))
+ (cond ((and (not ignore-new)
+ (jao-notmuch--looking-at-match-p)
+ (not (jao-notmuch-tree--message-open))))
+ (thread
+ (notmuch-tree-next-thread prev)
+ (unless (or (not (notmuch-tree-get-message-properties))
+ (jao-notmuch--looking-at-match-p))
+ (notmuch-tree-matching-message prev (not no-exit))))
+ (t (notmuch-tree-matching-message prev (not no-exit)))))
+ (when (notmuch-tree-get-message-id)
+ (jao-notmuch-tree-hide-others t))
+ (when prev (forward-char 2)))
+
+(defvar jao-notmuch-tree--prefix-map
+ (let ((m (make-keymap "Thread operations")))
+ (define-key m (kbd "TAB") #'outline-cycle)
+ (define-key m (kbd "t") #'outline-toggle-children)
+ (define-key m (kbd "s") #'outline-show-entry)
+ (define-key m (kbd "S") #'outline-show-all)
+ (define-key m (kbd "h") #'outline-hide-entry)
+ (define-key m (kbd "H") #'outline-hide-body)
+ (define-key m (kbd "o") #'jao-notmuch-tree-hide-others)
+ (define-key m (kbd "n") #'outline-hide-other)
+ m))
+
+(defun jao-notmuch-tree-outline-setup (&optional prefix)
+ (define-key notmuch-tree-mode-map (kbd (or prefix "T"))
+ jao-notmuch-tree--prefix-map)
+ (define-key notmuch-tree-mode-map (kbd "TAB") #'outline-cycle)
+ (define-key notmuch-tree-mode-map (kbd "M-TAB") #'outline-cycle-buffer)
+ (add-hook 'notmuch-tree-mode-hook #'jao-notmuch-tree--mode-setup)
+ (advice-add 'notmuch-tree-insert-msg :before #'jao-notmuch-tree--msg-prefix))
+
+(defun jao-notmuch-tree-next (thread &optional no-exit)
+ "Next message or thread in forest, taking care of thread visibility."
+ (interactive "P")
+ (jao-notmuch-tree--next nil thread no-exit))
+
+(defun jao-notmuch-tree-next-thread (&optional exit)
+ "Next thread in forest, taking care of thread visibility."
+ (interactive "P")
+ (jao-notmuch-tree--next nil t exit))
+
+(defun jao-notmuch-tree-previous (thread)
+ "Previous message or thread in forest, taking care of thread visibility."
+ (interactive "P")
+ (jao-notmuch-tree--next t thread t))
+
+(defun jao-notmuch-tree-previous-thread (&optional exit)
+ "Previous thread in forest, taking care of thread visibility."
+ (interactive "P")
+ (jao-notmuch-tree--next t t exit))
+
+
+;;; elpher/gemini
+(use-package elpher :ensure t)
+(defun jao-elpher--browse (url &rest _) (elpher-go url))
+(add-to-list 'browse-url-handlers
+ '("^\\(gemini\\|gopher\\)://.*" . jao-elpher--browse))
+
+;;; fontsets
+(defun jao--set-fontsets (_f)
+ (when (and (display-graphic-p) (fboundp 'set-fontset-font))
+ (set-fontset-font t 64257 "Quivira" nil)
+ (set-fontset-font t 'egyptian "Noto Sans Egyptian Hieroglyphs" nil)
+ (set-fontset-font t 'hangul "NanumGothicCoding" nil)
+ (set-fontset-font t 'unicode (face-attribute 'default :family) nil)
+ (set-fontset-font t 'unicode-bmp (face-attribute 'default :family) nil)
+ (set-fontset-font t 'symbol "Symbola-10" nil)
+ (set-fontset-font t 'greek "GFS Didot" nil)
+ (set-fontset-font t 'mathematical "FreeSerif" nil)
+ (set-fontset-font t 'emoji "Noto Color Emoji" nil)
+ ;; boxes
+ (set-fontset-font t '(9472 . 9599) "Source Code Pro" nil)
+ ;; variation selector-16
+ (set-fontset-font t 65039 "BabelStone Modern-1" nil)))
+
+(jao--set-fontsets nil)
+(add-to-list 'after-make-frame-functions 'jao--set-fontsets)
+
+;;; eshell history completion to allow !$
+;; This is done by advising eshell-history-reference to expand !$
+;; into !!:$ which works...
+(defadvice jao-eshell-history-reference (before ben-fix-eshell-history)
+ "Fixes eshell history to allow !$ as abbreviation for !!:$"
+ (when (string= (ad-get-arg 0) "!$") (ad-set-arg 0 "!!:$")))
+(ad-activate 'jao-eshell-history-reference)
+(add-hook 'eshell-expand-input-functions #'eshell-expand-history-references)
+;;; enwc
+(use-package enwc
+ :ensure t
+ :custom ((enwc-default-backend 'nm)
+ (enwc-wired-device "wlp164s0")
+ (enwc-wireless-device "wlp164s0")
+ (enwc-display-mode-line nil)))
+
+
+;;; tidal/mpc
+(defconst jao-mpc--search-cmd
+ "-f '%%album%% - %%artist%% :::%%file%%' search %s '%s'|grep :::tidal:album")
+
+(defun jao-mpc--search-albums (query)
+ (let* ((cmd (format jao-mpc--search-cmd "any" query))
+ (str (jao-mpc--cmd cmd))
+ (res (split-string str "\n" t)))
+ (message "%s" str)
+ (mapcar (lambda (s) (split-string s ":::" t " ")) res)))
+
+(defun jao-mpc-select-tidal-album (&optional query port)
+ (interactive "sSearch terms: ")
+ (let* ((jao-mpc-port (or port jao-mpc-port))
+ (resa (jao-mpc--search-albums query)))
+ (if (null resa)
+ (user-error "No results")
+ (when-let* ((a (completing-read "Play album: " resa nil t))
+ (s (car (alist-get a resa nil nil 'string=))))
+ (jao-mpc--add-and-play s port t)))))
+;;; dogears
+(use-package dogears
+ :ensure t
+ :enabled nil
+ :bind (:map global-map
+ ("M-g d" . dogears-go)
+ ("M-g M-b" . dogears-back)
+ ("M-g M-f" . dogears-forward)
+ ("M-g M-d" . dogears-list)
+ ("M-g M-D" . dogears-sidebar)))
+
+(dogears-mode)
+;;; pulsar
+(use-package pulsar
+ :ensure t
+ :demand t
+ :diminish
+ :custom ((pulsar-pulse t)
+ (pulsar-delay 0.1)
+ (pulsar-iterations 10)
+ (pulsar-face 'pulsar-yellow)
+ (pulsar-highlight-face 'jao-themes--hilite))
+ :config
+ (dolist (f '(jao-prev-window
+ jao-tracking-next-buffer
+ smartscan-symbol-go-forward
+ smartscan-symbol-go-backward))
+ (add-to-list 'pulsar-pulse-functions f))
+
+ :hook ((jao-afio-switch . pulsar-pulse-line)
+ (consult-after-jump . pulsar-reveal-entry)
+ (imenu-after-jump . pulsar-reveal-entry)
+ (next-error . pulsar-pulse-line)))
+
+(pulsar-global-mode 1)
+;;;; mouse
+(use-package disable-mouse
+ :ensure t
+ :diminish ((disable-mouse-global-mode . "")))
+
+(global-disable-mouse-mode)
+;;; tmr
+(use-package tmr
+ :ensure t
+ :init
+ (setq tmr-sound-file "/usr/share/sounds/freedesktop/stereo/message.oga"))
+;;; pdf-tools
+(use-package pdf-tools
+ :ensure t
+ :demand t
+ :init
+ (add-hook 'after-init-hook
+ (lambda ()
+ (setq pdf-view-midnight-colors
+ (cons (frame-parameter nil 'foreground-color)
+ (frame-parameter nil 'background-color)))))
+
+ :hook ((pdf-view-mode . jao-doc-session-mark))
+
+ :config (pdf-tools-install)
+
+ :diminish ((pdf-view-midnight-minor-mode . ""))
+
+ :bind (:map pdf-view-mode-map
+ (("C-c C-d" . pdf-view-midnight-minor-mode)
+ ("j" . pdf-view-next-line-or-next-page)
+ ("J" . pdf-view-scroll-up-or-next-page)
+ ("k" . pdf-view-previous-line-or-previous-page)
+ ("K" . pdf-view-scroll-down-or-previous-page))))
+;;; slack
+(eval-and-compile
+ (defvar jao-slack-dir (expand-file-name "emacs-slack" jao-local-lisp-dir)))
+
+(use-package slack
+ :commands (slack-start)
+ :vc t
+ :load-path jao-slack-dir
+ :init
+ (setq slack-alert-icon (jao-data-file "slack.svg")
+ slack-buffer-emojify nil
+ slack-buffer-create-on-notify t
+ slack-display-team-name t
+ slack-typing-visibility 'buffer ;; 'never, 'buffer, 'frame
+ slack-thread-also-send-to-room t
+ slack-profile-image-file-directory "/tmp/slack-imgs/"
+ slack-image-file-directory "/tmp/slack-imgs/"
+ slack-file-dir "~/var/download/slack/"
+ slack-prefer-current-team t
+ slack-message-tracking-faces '(warning)
+ slack-log-level 'warn
+ slack-message-custom-notifier (lambda (_msg room _team) room))
+ :bind (:map slack-mode-map (("@" . slack-message-embed-mention)
+ ("#" . slack-message-embed-channel))
+ :map slack-message-buffer-mode-map
+ (("C-c C-e" . slack-message-edit)
+ ("C-c C-a" . slack-file-upload)))
+ :hook ((slack-file-info-buffer-mode . view-mode))
+ :config
+
+ (defun my-slack-nobreak-mrkdwn ()
+ "Return non-nil (don't break line) if point is in markdown code face."
+ (seq-find (lambda (ov)
+ (eq 'slack-mrkdwn-code-block-face (overlay-get ov 'face)))
+ (overlays-at (point))))
+ (add-hook 'slack-message-buffer-mode-hook
+ (lambda ()
+ (add-hook 'fill-nobreak-predicate #'my-slack-nobreak-mrkdwn
+ nil 'local)))
+
+ (dolist (f (list slack-file-dir slack-image-file-directory))
+ (when (not (file-exists-p f)) (make-directory f)))
+
+ (jao-shorten-modes 'slack-message-buffer-mode
+ 'slack-thread-message-buffer-mode)
+ (jao-tracking-faces 'warning)
+ (jao-tracking-cleaner "logstash-\\([^-]+\\)-\\(.+\\)" "\\2-\\1")
+ (jao-tracking-cleaner
+ "^\\*Slack - .*? : \\(MPIM: \\)?\\([^ ]+\\)\\( \\(T\\)\\)?.*" "\\2\\4")
+ (jao-define-attached-buffer "\\*Slack .+ Edit Message [0-9].+" 20))
+
+;;; snippets
+(defun jao-org-notes-open-tags ()
+ "Search for a note file, matching all tags with completion."
+ (let* ((tags (jao-org-notes--read-tags))
+ (fn (lambda ()
+ (prog1 (jao-org-notes--find-tag (car tags))
+ (setq tags (cdr tags)))))
+ (res (funcall fn)))
+ (while (and res tags) (setq res (seq-intersection res (funcall fn))))
+ (unless res (user-error "No notes found"))
+ (when-let (f (completing-read "Select file: " (mapcar #'car res)))
+ (find-file (cadr (assoc f res))))))
+
+(defun jao-sway-run-or-focus-tidal ()
+ (interactive)
+ (if (jao-shell-running-p "tidal-hifi")
+ (jao-swaymsg "[app_id=tidal-hifi] scratchpad show")
+ (let ((c
+ "tidal-hifi --enable-features=UseOzonePlatform --ozone-platform=wayland &"))
+ (start-process-shell-command "tidal-hifi" nil c))
+ (jao-sway-run-or-focus-tidal)))
+
+;;
+
+(defun jao-afio--set-mode-line ()
+ (when (and window-system (fboundp 'jao-mode-line-hide-inactive))
+ (if (string= "docs" (jao-afio-frame-name))
+ (jao-mode-line-show-inactive nil)
+ (jao-mode-line-hide-inactive nil))))
+
+(unless jao-modeline-in-minibuffer
+ (add-hook 'jao-afio-switch-hook #'jao-afio--set-mode-line))
+
+;;
+
+(defun jao-word-definition-lookup ()
+ "Look up the word under cursor in a browser."
+ (interactive)
+ (require 'thingatpt)
+ (browse-url
+ (concat "http://www.wordnik.com/words/"
+ ;; "http://www.answers.com/main/ntquery?s="
+ (thing-at-point 'word))))
+
+;;
+
+(defun jao-notmuch-format-author (width msg)
+ (let* ((headers (plist-get msg :headers))
+ (auth (notmuch-tree-clean-address (plist-get headers :From)))
+ (awidth (string-width auth))
+ (auth (if (> awidth width)
+ (substring auth 0 width)
+ (concat auth (make-string (- width awidth) 32))))
+ (face (if (plist-get msg :match)
+ 'notmuch-tree-match-author-face
+ 'notmuch-tree-no-match-author-face)))
+ (propertize auth 'face face)))
diff --git a/attic/net/nnnm.el b/attic/elisp/nnnm.el
index 552e95c..552e95c 100644
--- a/attic/net/nnnm.el
+++ b/attic/elisp/nnnm.el
diff --git a/attic/misc.org b/attic/misc.org
deleted file mode 100644
index e84cad0..0000000
--- a/attic/misc.org
+++ /dev/null
@@ -1,22 +0,0 @@
-* dtache
- #+begin_src emacs-lisp
- (use-package dtache
- :ensure t
- :hook (after-init . dtache-setup))
-
- (use-package dtache-eshell
- :after dtache
- :hook (eshell-mode . dtache-eshell-mode))
-
- (use-package dtache-consult
- :after (consult dtache)
- :bind ([remap dtache-open-session] . dtache-consult-session))
- #+end_src
-* signel
- #+begin_src emacs-lisp :tangle no
- (jao-load-org "lib/net/signel.org")
- (with-eval-after-load "tracking"
- (jao-tracking-faces 'signel-notification)
- (jao-shorten-modes 'signel-chat-mode))
- (setq signel-report-deliveries t)
- #+end_src
diff --git a/attic/net/jao-proton-utils.el b/attic/net/jao-proton-utils.el
deleted file mode 100644
index 012a2ff..0000000
--- a/attic/net/jao-proton-utils.el
+++ /dev/null
@@ -1,131 +0,0 @@
-;; jao-proton-utils.el -- simple interaction with Proton mail and vpn
-
-;; Copyright (c) 2018, 2019, 2020 Jose Antonio Ortega Ruiz
-
-;; This file is free software; you can redistribute it and/or modify
-;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation; either version 3, or (at your option)
-;; any later version.
-
-;; This file is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-;; GNU General Public License for more details.
-
-;; You should have received a copy of the GNU General Public License
-;; along with GNU Emacs; see the file COPYING. If not, write to
-;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-;; Boston, MA 02111-1307, USA.
-
-;; Author: Jose Antonio Ortega Ruiz <mail@jao.io>
-;; Start date: Fri Dec 21, 2018 23:56
-
-;;; Comentary:
-
-;; This is a very simple comint-derived mode to run the CLI version
-;; of PM's Bridge within the comfort of emacs.
-
-;;; Code:
-
-(define-derived-mode proton-bridge-mode comint-mode "proton-bridge"
- "A very simple comint-based mode to run ProtonMail's bridge"
- (setq comint-prompt-read-only t)
- (setq comint-prompt-regexp "^>>> "))
-
-;;;###autoload
-(defun run-proton-bridge ()
- "Run or switch to an existing bridge process, using its CLI"
- (interactive)
- (pop-to-buffer (make-comint "proton-bridge" "protonmail-bridge" nil "-c"))
- (unless (eq major-mode 'proton-bridge-mode)
- (proton-bridge-mode)))
-
-(defvar proton-vpn-mode-map)
-
-(defvar jao-proton-vpn-font-lock-keywords '("\\[.+\\]"))
-
-;;;###autoload
-(defun proton-vpn-mode ()
- "A very simple mode to show the output of ProtonVPN commands"
- (interactive)
- (kill-all-local-variables)
- (buffer-disable-undo)
- (use-local-map proton-vpn-mode-map)
- (setq-local font-lock-defaults '(jao-proton-vpn-font-lock-keywords))
- (setq-local truncate-lines t)
- (setq-local next-line-add-newlines nil)
- (setq major-mode 'proton-vpn-mode)
- (setq mode-name "proton-vpn")
- (read-only-mode 1))
-
-(defvar jao-proton-vpn--buffer "*pvpn*")
-
-(defun jao-proton-vpn--do (things)
- (let ((b (pop-to-buffer (get-buffer-create jao-proton-vpn--buffer))))
- (let ((inhibit-read-only t)
- (cmd (format "protonvpn-cli %s" things)))
- (delete-region (point-min) (point-max))
- (message "Running: %s ...." cmd)
- (shell-command cmd b)
- (message ""))
- (proton-vpn-mode)))
-
-;;;###autoload
-(defun proton-vpn-status ()
- (interactive)
- (jao-proton-vpn--do "s"))
-
-(defun proton-vpn--get-status ()
- (or (when-let ((b (get-buffer jao-proton-vpn--buffer)))
- (with-current-buffer b
- (goto-char (point-min))
- (if (re-search-forward "^Status: *\\(.+\\)$" nil t)
- (match-string-no-properties 1)
- (when (re-search-forward "^Connected!$")
- "Connected"))))
- "Disconnected"))
-
-;;;###autoload
-(defun proton-vpn-connect (cc)
- (interactive "P")
- (let ((cc (when cc (read-string "Country code: "))))
- (jao-proton-vpn--do (if cc (format "c --cc %s" cc) "c --sc"))
- (proton-vpn-status)))
-
-(defun proton-vpn-reconnect ()
- (interactive)
- (jao-proton-vpn--do "r"))
-
-(setenv "PVPN_WAIT" "300")
-
-;;;###autoload
-(defun proton-vpn-maybe-reconnect ()
- (interactive)
- (when (string= "Connected" (proton-vpn--get-status))
- (jao-proton-vpn--do "d")
- (sit-for 5)
- (jao-proton-vpn--do "r")))
-
-;;;###autoload
-(defun proton-vpn-disconnect ()
- (interactive)
- (jao-proton-vpn--do "d"))
-
-(setq proton-vpn-mode-map
- (let ((map (make-keymap)))
- (suppress-keymap map)
- (define-key map [?q] 'bury-buffer)
- (define-key map [?n] 'next-line)
- (define-key map [?p] 'previous-line)
- (define-key map [?g] 'proton-vpn-status)
- (define-key map [?r] 'proton-vpn-reconnect)
- (define-key map [?d] (lambda ()
- (interactive)
- (when (y-or-n-p "Disconnect?")
- (proton-vpn-disconnect))))
- (define-key map [?c] 'proton-vpn-connect)
- map))
-
-
-(provide 'jao-proton-utils)
-;;; jao-proton.el ends here
diff --git a/attic/net/w3m.org b/attic/net/w3m.org
deleted file mode 100644
index 3689c8e..0000000
--- a/attic/net/w3m.org
+++ /dev/null
@@ -1,191 +0,0 @@
-#+property: header-args :lexical t :tangle yes :comments no :results silent
-#+title: Customizations for emacs-w3m
-#+auto_tangle: t
-
-* browse-url and afio
- #+begin_src emacs-lisp
- (defun jao-w3m-find-url (url)
- (let* ((url (w3m-canonicalize-url url))
- (fn `(lambda (b)
- (with-current-buffer b
- (string= ,url (w3m-canonicalize-url w3m-current-url))))))
- (when-let (b (seq-find fn (w3m-list-buffers)))
- (pop-to-buffer b))))
-
- (defun jao-w3m-browse-url (url &rest r)
- (jao-afio--goto-www)
- (select-window (frame-first-window))
- (or (jao-w3m-find-url url)
- (w3m-goto-url-new-session url)))
-
- (defun jao-w3m-download (arg)
- (interactive "P")
- (jao-download (w3m-anchor) arg))
-
- (setq jao-afio-use-w3m t)
- (setq jao-browse-url-function 'jao-w3m-browse-url)
- #+end_src
-* Org integration
- #+begin_src emacs-lisp
- (defun jao-w3m-get-link ()
- (let ((wb (w3m-alive-p)))
- (when wb
- (let ((url (with-current-buffer wb w3m-current-url))
- (title (w3m-buffer-title wb)))
- (cons url title)))))
-
- (defun jao-insert-w3m-link ()
- (interactive)
- (let ((link (jao-w3m-get-link)))
- (when link (insert "[[" (car link) "][" (cdr link) "]]"))))
-
- (with-eval-after-load "org"
- (require 'ol-w3m nil t)
- (define-key org-mode-map "\C-cW" 'jao-insert-w3m-link))
- #+end_src
-* notmuch integration
- #+begin_src emacs-lisp
- (defvar-local jao-notmuch--showing-images nil)
-
- (defun jao-notmuch--setup-w3m-images (&optional activate)
- (when (eq mm-text-html-renderer 'w3m)
- (setq-local w3m-ignored-image-url-regexp
- (unless jao-notmuch--showing-images
- notmuch-show-text/html-blocked-images))
- (when activate
- (setq-local scroll-margin 0)
- (w3m-toggle-inline-images (if jao-notmuch--showing-images t 'turnoff)))))
-
- (defun jao-notmuch--w3m-toggle-images ()
- (save-window-excursion
- (when (or (derived-mode-p 'notmuch-show-mode)
- (jao-notmuch-goto-message-buffer nil t))
- (goto-char (point-min))
- (when (re-search-forward "^\\[ text/html " nil t)
- (when (looking-at-p "(hidden)")
- (notmuch-show-toggle-part-invisibility))
- (forward-line 1)
- (setq jao-notmuch--showing-images (not jao-notmuch--showing-images))
- (jao-notmuch--setup-w3m-images t)))))
-
- (add-hook 'notmuch-show-mode-hook #'jao-notmuch--setup-w3m-images)
- #+end_src
-* Capture page
- #+begin_src emacs-lisp
- (defun jao-w3m-capture-page ()
- (interactive)
- (let* ((title (w3m-current-title))
- (url w3m-current-url)
- (html (y-or-n-p "Save as HTML (y) or PS (n)? "))
- (basename (concat (read-string "File name: ")
- (if html ".html" ".ps")))
- (name (expand-file-name basename jao-sink-dir)))
- (if html
- (progn
- (w3m-view-source)
- (write-region (point-min) (point-max) name nil nil nil t)
- (w3m-view-source))
- (progn
- (split-window-horizontally 85)
- (w3m-redisplay-this-page)
- (ps-print-buffer name)
- (delete-other-windows)
- (w3m-redisplay-this-page)))
- (kill-new (format "[[doc:%s][%s]] ([[%s][original]])"
- basename title url))))
- #+end_src
-* Consult narrowing
- #+begin_src emacs-lisp
- (with-eval-after-load "w3m-util"
- (with-eval-after-load "consult"
- (defvar jao-consult-w3m-buffer-history nil)
- (defun jao-www--item (b)
- (with-current-buffer b
- (propertize (or w3m-current-title (buffer-name))
- 'buffer b
- 'url (or w3m-current-url (buffer-name)))))
- (defvar jao-consult-w3m-source
- (list :name "www buffer"
- :category 'www-buffer
- :hidden t
- :narrow (cons ?w "www")
- :annotate (lambda (b) (when b (get-text-property 0 'url b)))
- :history 'jao-consult-w3m-buffer-history
- :items (lambda ()
- (seq-map #'jao-www--item
- (seq-filter #'jao-www--buffer-p (buffer-list))))
- :action (lambda (b)
- (jao-afio--goto-www)
- (switch-to-buffer (get-text-property 0 'buffer b)))))
- (jao-consult-add-buffer-source 'jao-consult-w3m-source "Web" ?w)))
- #+end_src
-* Package
- #+begin_src emacs-lisp
- (use-package w3m
- :ensure t
- :custom ((w3m-key-binding 'info)
- (w3m-display-mode 'dual-pane))
- :init
- (setq w3m-add-user-agent nil
- w3m-confirm-leaving-secure-page nil
- w3m-cookie-accept-bad-cookies t
- w3m-cookie-accept-domains '(".github.com"
- ".librarything.com"
- ".goodreads.com"
- ".sr.ht"
- ".gnu.org"
- ".codeberg.org"
- "codeberg.org"
- ".bookshop.org"
- ".reddit.com")
- w3m-cookie-reject-domains '(".")
- w3m-default-save-directory "~/var/download"
- w3m-do-cleanup-temp-files nil
- w3m-external-view-temp-directory "/tmp"
- w3m-fill-column 110
- w3m-goto-article-function 'jao-w3m-browse-url
- w3m-form-input-textarea-buffer-lines 40
- w3m-history-minimize-in-new-session t
- w3m-history-reuse-history-elements nil
- w3m-image-no-idle-timer t
- w3m-make-new-session t
- w3m-profile-directory "~/.w3m"
- w3m-redisplay-pages-automatically-p nil
- w3m-resize-images t
- w3m-safe-url-regexp nil
- w3m-search-default-engine "duckduckgo" ; "google-en"
- w3m-select-buffer-horizontal-window nil
- w3m-select-buffer-window-ratio '(20 . 40)
- w3m-session-load-last-sessions t
- w3m-session-load-crashed-sessions 'ask
- w3m-show-graphic-icons-in-header-line nil
- w3m-show-graphic-icons-in-mode-line nil
- w3m-use-tab nil
- w3m-use-tab-line nil
- w3m-use-title-buffer-name t
- w3m-use-cookies t
- w3m-use-filter nil
- w3m-use-favicon nil
- w3m-use-header-line nil
- w3m-use-refresh nil
- w3m-use-symbol t)
-
- :config
- :bind (:map w3m-mode-map
- (("+" . w3m-zoom-in-image)
- ("-" . w3m-zoom-out-image)
- ("C-c C-@" . tracking-next-buffer)
- ("C-c C-SPC" . tracking-next-buffer)
- ("C-c C-b" . nil)
- ("C-c c" . jao-w3m-capture-page)
- ("b" . w3m-view-previous-page)
- ("B" . w3m-view-next-page)
- ("c" . w3m-print-this-url)
- ("d" . jao-w3m-download)
- ("D" . w3m-download)
- ("f" . w3m-lnum-follow)
- ("v" . jao-view-video)
- ("w" . org-w3m-copy-for-org-mode)
- ("x" . jao-rss-subscribe)
- ("y" . w3m-print-current-url))))
- #+end_src