diff options
Diffstat (limited to 'attic/elisp')
| -rw-r--r-- | attic/elisp/jao-notmuch-gnus.el | 226 | ||||
| -rw-r--r-- | attic/elisp/misc.el | 299 |
2 files changed, 520 insertions, 5 deletions
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/elisp/misc.el b/attic/elisp/misc.el index 2f1bcb0..77089ff 100644 --- a/attic/elisp/misc.el +++ b/attic/elisp/misc.el @@ -813,11 +813,6 @@ :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 @@ -955,3 +950,297 @@ 'notmuch-tree-match-author-face 'notmuch-tree-no-match-author-face))) (propertize auth 'face face))) + +;; winttr + +(defun jao-weather (&optional wide) + (interactive "P") + (if (not wide) + (message "%s" + (jao-shell-string "curl -s" + "https://wttr.in/?format=%l++%m++%C+%c+%t+%w++%p")) + (jao-afio-goto-scratch) + (if-let ((b (get-buffer "*wttr*"))) + (progn (pop-to-buffer b) + (term-send-string (get-buffer-process nil) "clear;curl wttr.in\n")) + (jao-exec-in-term "curl wttr.in" "*wttr*")))) +(global-set-key (kbd "<f5>") #'jao-weather) + +;; so-long +(setq large-file-warning-threshold (* 200 1024 1024)) + +(use-package so-long + :ensure t + :diminish) +(global-so-long-mode 1) + +;;;; code reviews +(use-package code-review + :disabled t + :ensure t + :after forge + :bind (:map magit-status-mode-map + ("C-c C-r" . code-review-forge-pr-at-point))) + +;;;; jenkins +(use-package jenkins + :ensure t + :init + ;; one also needs jenkins-api-token, jenkins-username and jenkins-url + ;; optionally: jenkins-colwidth-id, jenkins-colwidth-last-status + (setq jenkins-colwidth-name 35) + :config + (defun jao-jenkins-first-job (&rest _) + (interactive) + (goto-char (point-min)) + (when (re-search-forward "^- Job" nil t) + (goto-char (match-beginning 0)))) + (add-hook 'jenkins-job-view-mode-hook #'jao-jenkins-first-job) + (advice-add 'jenkins-job-render :after #'jao-jenkins-first-job) + + (defun jenkins-refresh-console-output () + (interactive) + (let ((n (buffer-name))) + (when (string-match "\\*jenkins-console-\\([^-]+\\)-\\(.+\\)\\*$" n) + (jenkins-get-console-output (match-string 1 n) (match-string 2 n)) + (goto-char (point-max))))) + + :bind (:map jenkins-job-view-mode-map + (("n" . next-line) + ("p" . previous-line) + ("f" . jao-jenkins-first-job) + ("RET" . jenkins--show-console-output-from-job-screen)) + :map jenkins-console-output-mode-map + (("n" . next-line) + ("p" . previous-line) + ("g" . jenkins-refresh-console-output)))) + +;;; doric themes +(use-package doric-themes + :if (jao-is-darwin) + :ensure t + :demand t + :config + ;; These are the default values. + (setq doric-themes-to-toggle '(doric-light doric-marble)) + (setq doric-themes-to-rotate doric-themes-collection) + + (doric-themes-select 'doric-marble) + + (set-face-attribute 'default nil :family "Triplicate T4c" :height 120) + ;; (set-face-attribute 'default nil :family "0xProto" :height 110) + ;; (set-face-attribute 'default nil :family "Rec Mono Casual" :height 120) + ;; (set-face-attribute 'default nil :family "Rec Mono Linear" :height 120) + ;; (set-face-attribute 'default nil :family "Rec Mono Duotone" :height 120) + ;; (set-face-attribute 'default nil :family "Victor Mono" :height 120) + ;; (set-face-attribute 'variable-pitch nil :family "Aporetic Sans" :height 1.0) + ;; (set-face-attribute 'fixed-pitch nil :family "Aporetic Sans Mono" :height 1.0) + + :bind + (("<f5>" . doric-themes-toggle) + ("C-<f5>" . doric-themes-select) + ("M-<f5>" . doric-themes-rotate))) + +;;; gnuplot +(use-package gnuplot + :disabled t + :ensure t + :commands (gnuplot-mode gnuplot-make-buffer) + :init (add-to-list 'auto-mode-alist '("\\.gp$" . gnuplot-mode))) + +;;; rdrview +;; https://jiewawa.me/2024/04/another-way-of-integrating-mozilla-readability-in-emacs-eww/ +(define-minor-mode eww-rdrview-mode + "Toggle whether to use `rdrview' to make eww buffers more readable." + :lighter " R" + (if eww-rdrview-mode + (progn + (setq eww-retrieve-command '("rdrview" "-T" "title,sitename,body" "-H")) + (add-hook 'eww-after-render-hook #'eww-rdrview-update-title)) + (progn + (setq eww-retrieve-command nil) + (remove-hook 'eww-after-render-hook #'eww-rdrview-update-title)))) + +(defun eww-rdrview-update-title () + "Change title key in `eww-data' with first line of buffer. +It should be the title of the web page as returned by `rdrview'" + (save-excursion + (goto-char (point-min)) + (plist-put eww-data :title (string-trim (thing-at-point 'line t)))) + (eww--after-page-change)) + +(defun eww-rdrview-toggle-and-reload () + "Toggle `eww-rdrview-mode' and reload page in current eww buffer." + (interactive) + (if eww-rdrview-mode (eww-rdrview-mode -1) + (eww-rdrview-mode 1)) + (eww-reload)) + +(defun jao-eww-readable (rdrview) + (interactive "P" eww-mode) + (if rdrview + (eww-rdrview-toggle-and-reload) + (eww-readable))) + +;;; spotify +(jao-load-path "espotify") + +(use-package espotify + :demand t + :init (setq espotify-service-name "mopidy")) + +(use-package consult-spotify :demand t) + +(defalias 'jao-streaming-album #'consult-spotify-album) +(defalias 'jao-streaming-track #'consult-spotify-track) +(defalias 'jao-streaming-artist #'consult-spotify-artist) +(defalias 'jao-streaming-playlist #'consult-spotify-playlist) + +(jao-def-exec-in-term "ncmpcpp" "ncmpcpp" (jao-afio-goto-scratch)) + +;;; spt +(use-package jao-spt + :demand t + :config + (defun jao-spt-setup-aliases () + (setq espotify-play-uri-function #'jao-spt-play-uri) + (defalias 'jao-streaming-list #'jao-term-spt) + (defalias 'jao-streaming-lyrics #'jao-spt-show-lyrics) + (defalias 'jao-streaming-toggle #'jao-spt-toggle) + (defalias 'jao-streaming-next #'jao-spt-next) + (defalias 'jao-streaming-prev #'jao-spt-previous) + (defalias 'jao-streaming-current #'jao-spt-echo-current) + (defalias 'jao-streaming-seek #'jao-spt-seek) + (defalias 'jao-streaming-seek-back #'jao-spt-seek-back) + (defalias 'jao-streaming-volume #'jao-spt-vol) + (defalias 'jao-streaming-volume-down #'jao-spt-vol-down) + (defalias 'jao-streaming-like #'jao-spt-like) + (defalias 'jao-streaming-dislike #'jao-spt-dislike) + (defalias 'jao-streaming-toggle-shuffle #'jao-spt-toggle-shuffle))) + +(jao-def-exec-in-term "spt" "spt" (jao-afio-goto-scratch)) + +(defvar jao-spt-on t) + +(defun jao-streaming-toggle-player () + (interactive) + (if jao-spt-on + (progn (setq jao-mpris-player "playerctld") + (require 'jao-mpris) + (jao-mpris-setup-aliases)) + (jao-spt-setup-aliases) + (setq jao-mpris-player "spt")) + (setq jao-spt-on (not jao-spt-on)) + (message "%s activated " jao-mpris-player)) + +(jao-streaming-toggle-player) + +;;; mpd + mopidy +(use-package jao-mpc + :demand t + :commands jao-mpc-setup) + +(defvar jao-mopidy-port 6669) +(defvar jao-mpc-last-port jao-mpc-port) + +(defun jao-mpc-toggle-port () + (interactive) + (setq jao-mpc-port + (if (equal jao-mpc-port jao-mopidy-port) 6600 jao-mopidy-port) + jao-mpc-last-port jao-mpc-port)) + +(defsubst jao-mpc-mopidy-p () (equal jao-mpc-last-port jao-mopidy-port)) + +(jao-mpc-setup jao-mopidy-port 70) + +(defun jao-mpc-pport (&optional mop) + (cond ((or mop (jao-mpc-playing-p jao-mopidy-port)) jao-mopidy-port) + ((jao-mpc-playing-p) 6600) + (t jao-mpc-last-port))) + +(defmacro jao-defun-play (name &optional mpc-name) + (let ((arg (gensym))) + `(defun ,(intern (format "jao-player-%s" name)) (&optional ,arg) + (interactive "P") + (,(intern (format "jao-mpc-%s" (or mpc-name name))) + (setq jao-mpc-last-port (jao-mpc-pport ,arg)))))) + +(jao-defun-play toggle) +(jao-defun-play next) +(jao-defun-play previous) +(jao-defun-play stop) +(jao-defun-play echo echo-current-times) +(jao-defun-play list show-playlist) +(jao-defun-play info lyrics-track-data) +(jao-defun-play browse show-albums) +(jao-defun-play select-album) + +(defun jao-player-seek (delta) (jao-mpc-seek delta (jao-mpc-pport))) + +(defalias 'jao-player-connect 'jao-mpc-connect) +(defalias 'jao-player-play 'jao-mpc-play) + +;;; org-modern +(use-package org-modern + :ensure t + :init + (setq org-modern-fold-stars + '(("▶" . "▼") ("▷" . "▽") ("▶" . "▼") ("▹" . "▿") ("▸" . "▾"))) + + (define-derived-mode jao-org-inbox-mode org-mode + "Org inbox" + (org-indent-mode) + (org-modern-mode)) + + (add-to-list 'auto-mode-alist '("inbox\\.org\\'" . jao-org-inbox-mode)) + (add-hook 'org-agenda-finalize-hook #'org-modern-agenda)) + +;;; Gnus notify + +(defun jao-gnus--notify-strs () + (let* ((all (jao-gnus--unread-counts)) + (counts (cdr all)) + (labels (seq-keep (lambda (args) + (apply 'jao-gnus--unread-label counts args)) + jao-gnus-tracked-groups))) + (jao-when-darwin (jao-gnus--xbar-echo labels)) + labels)) + +(defvar jao-gnus-tracked-groups + (let ((feeds (thread-first + (directory-files mail-source-directory nil "feeds\\.[^e]") + (seq-difference + '("feeds.trove" "feeds.emacs" "feeds.emacs-devel"))))) + `(("nnml:jao\\.bigml" "B" jao-themes-f00) + ("nnml:jao\\.\\(inbox\\|trove\\)" "I" jao-themes-f01) + ("nnml:jao.write" "W" jao-themes-warning) + ("nnml:jao.[^ithwb]" "J" jao-themes-dimm) + ("nnml:jao.hacking" "H" jao-themes-dimm) + ;; (,(format "^nnml:%s" (regexp-opt feeds)) "F" jao-themes-dimm) + ;; ("feeds\\.emacs" "E" jao-themes-dimm) + ("nnml:feeds\\." "F" jao-themes-dimm) + ("nnml:local" "l" jao-themes-dimm) + ("nnrss:.*" "R" jao-themes-dimm) + ("^\\(gwene\\|gmane\\)\\." "N" jao-themes-dimm)))) + +(defun jao-gnus--unread-counts () + (seq-reduce (lambda (r g) + (let ((n (gnus-group-unread (car g)))) + (if (and (numberp n) (> n 0)) + (cons (+ n (car r)) + (cons (cons (car g) n) (cdr r))) + r))) + gnus-newsrc-alist + '(0))) + +(defun jao-gnus-unread-count () + (seq-reduce (lambda (c g) (+ c (or (gnus-group-unread (car g)) 0))) + gnus-newsrc-alist + 0)) + +(defun jao-gnus--unread-label (counts rx label face) + (let ((n (seq-reduce (lambda (n c) + (if (string-match-p rx (car c)) (+ n (cdr c)) n)) + counts + 0))) + (when (> n 0) `(:propertize ,(format "%s%d " label n) face ,face)))) |
