diff options
Diffstat (limited to 'attic')
-rw-r--r-- | attic/counsel.org | 337 | ||||
-rw-r--r-- | attic/elisp/jao-custom-modus.el | 159 | ||||
-rw-r--r-- | attic/elisp/jao-doc-view-imenu.el | 74 | ||||
-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.el | 222 | ||||
-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.el | 226 | ||||
-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.el | 131 | ||||
-rw-r--r-- | attic/elisp/misc.el | 951 | ||||
-rw-r--r-- | attic/elisp/nnnm.el (renamed from attic/net/nnnm.el) | 0 | ||||
-rw-r--r-- | attic/misc.org | 22 | ||||
-rw-r--r-- | attic/net/jao-proton-utils.el | 131 | ||||
-rw-r--r-- | attic/net/w3m.org | 191 |
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 |