My elfeed configuration
This commit is contained in:
parent
68b36fc922
commit
a8f7732067
196
gists.org
196
gists.org
|
@ -943,6 +943,202 @@ Which covers my (potential) GPG/SSH usage within Emacs. Now, anytime a I perform
|
|||
(display-buffer (current-buffer))))
|
||||
#+end_src
|
||||
|
||||
* elfeed configuration :emacs:
|
||||
|
||||
#+begin_src elisp :tangle gists/elfeed.el
|
||||
;; For constructing relative date strings from a timestamp.
|
||||
;; Clone from https://github.com/rougier/relative-date
|
||||
(use-package relative-date
|
||||
:ensure nil
|
||||
:load-path "lisp/relative-date"
|
||||
:config
|
||||
(defun bram85-fix-relative-date-in-future (f &rest args)
|
||||
"Fix future relative dates, as they are returned with a +-
|
||||
prefix given the `relative-dates-formats' below. Because the
|
||||
internal data is negative, it shows up as such when replacing
|
||||
placeholders."
|
||||
(replace-regexp-in-string "\\`+-" "+" (apply f args)))
|
||||
|
||||
(advice-add 'relative-date :around #'bram85-fix-relative-date-in-future)
|
||||
|
||||
(setq relative-date-formats
|
||||
(let* ((seconds 1)
|
||||
(minutes (* 60 seconds))
|
||||
(hours (* 60 minutes))
|
||||
(days (* 24 hours))
|
||||
(months (* 30 days))
|
||||
(years (* 365 days)))
|
||||
`((, (* 3 minutes) . "now") ;; Less than 3 minutes (past)
|
||||
(,(- (* 3 minutes)) . "soon") ;; Less than 3 minutes (future)
|
||||
(, (* 1 hours) . "%(M)m") ;; Less than 1 hour
|
||||
(,(- (* 1 hours)) . "+%(M)m") ;; Less than 1 hour in the future
|
||||
(, (* 24 hours) . "%(H)h") ;; Less than 24 hours
|
||||
(,(- (* 24 hours)) . "+%(H)h") ;; Less than 24 hours in the future
|
||||
(, (* 7 days) . "%(d)d") ;; Less than 7 days
|
||||
(,(- (* 7 days)) . "+%(d)d") ;; Less than 7 days in the future
|
||||
(, (* 1 months) . "%(w)w") ;; Less than 30 days
|
||||
(,(- (* 1 months)) . "+%(w)w") ;; Less than 30 days in the future
|
||||
(, (* 365 days) . "%(m)mo") ;; Less than a year
|
||||
(,(- (* 365 days)) . "+%(m)mo") ;; Less than a year in the future
|
||||
(, (* 1000 years) . "%(y)y") ;; Less than 1000 years
|
||||
(,(- (* 100 years)) . "+%(y)y") ;; Less than 100 years in the future (to fit in 4 chars)
|
||||
))))
|
||||
|
||||
(use-package elfeed
|
||||
:after relative-date
|
||||
:init
|
||||
(defun bram85-elfeed-show-eww-open (&optional use-generic-p)
|
||||
"open with eww"
|
||||
(interactive "P")
|
||||
(let ((browse-url-browser-function #'eww-browse-url))
|
||||
(elfeed-show-visit use-generic-p)))
|
||||
|
||||
(defun bram85-elfeed-search-eww-open (&optional use-generic-p)
|
||||
"open with eww"
|
||||
(interactive "P")
|
||||
(let ((browse-url-browser-function #'eww-browse-url))
|
||||
(elfeed-search-browse-url use-generic-p)))
|
||||
|
||||
;; The following functions is for easy navigation, taken from
|
||||
;; Karthinks blog: https://karthinks.com/software/lazy-elfeed/
|
||||
(defun bram85-elfeed-scroll-up-command (&optional arg)
|
||||
"Scroll up or go to next feed item in Elfeed"
|
||||
(interactive "^P")
|
||||
(let ((scroll-error-top-bottom nil))
|
||||
(condition-case-unless-debug nil
|
||||
(scroll-up-command arg)
|
||||
(error (elfeed-show-next)))))
|
||||
|
||||
(defun bram85-elfeed-scroll-down-command (&optional arg)
|
||||
"Scroll down or go to prev feed item in Elfeed"
|
||||
(interactive "^P")
|
||||
(let ((scroll-error-top-bottom nil))
|
||||
(condition-case-unless-debug nil
|
||||
(scroll-down-command arg)
|
||||
(error (elfeed-show-prev)))))
|
||||
:bind
|
||||
(("C-x w" . elfeed)
|
||||
:map elfeed-show-mode-map
|
||||
("w" . elfeed-show-visit)
|
||||
("W" . bram85-elfeed-show-eww-open)
|
||||
("SPC" . bram85-elfeed-scroll-up-command)
|
||||
("S-SPC" . bram85-elfeed-scroll-down-command)
|
||||
:map elfeed-search-mode-map
|
||||
("w" . elfeed-search-browse-url)
|
||||
("W" . bram85-elfeed-search-eww-open))
|
||||
:config
|
||||
(setq-default elfeed-search-filter "@3-days-ago +unread")
|
||||
(setq elfeed-feeds '(
|
||||
;; ...
|
||||
))
|
||||
|
||||
;; enable relative dates which are max 4 chars wide
|
||||
(setq elfeed-search-date-format '("rel" 4 :right))
|
||||
|
||||
(defvar bram85-elfeed-hidden-tags
|
||||
'(unread
|
||||
highvol
|
||||
lowvol
|
||||
linkonly)
|
||||
"List of tags which do not get printed.")
|
||||
|
||||
(defun bram85-elfeed-tags (entry)
|
||||
"Return a list of tags as strings for the given ENTRY, where
|
||||
hidden tags are filtered out."
|
||||
(mapcar #'symbol-name
|
||||
(seq-difference (elfeed-entry-tags entry) bram85-elfeed-hidden-tags)))
|
||||
|
||||
(defun bram85-elfeed-search-print-entry (entry)
|
||||
"Print elfeed ENTRY to the buffer."
|
||||
(let ((format-time-string-orig (symbol-function #'format-time-string)))
|
||||
;; Make sure that elfeed-search-format-date calls relative-date
|
||||
;; instead of format-time-string, if the format is "rel".
|
||||
(cl-letf (((symbol-function #'format-time-string)
|
||||
(lambda (fmt &optional time zone)
|
||||
(if (string= fmt "rel")
|
||||
(relative-date time)
|
||||
;; For all other formats, call the builtin function.
|
||||
(funcall format-time-string-orig fmt time zone)))))
|
||||
(let* ((date (elfeed-search-format-date (elfeed-entry-date entry)))
|
||||
(title (or (elfeed-meta entry :title) (elfeed-entry-title entry) ""))
|
||||
(title-faces (elfeed-search--faces (elfeed-entry-tags entry)))
|
||||
(feed (elfeed-entry-feed entry))
|
||||
(feed-title
|
||||
(when feed
|
||||
(or (elfeed-meta feed :title) (elfeed-feed-title feed))))
|
||||
(tags (bram85-elfeed-tags entry))
|
||||
(tags-str (propertize (concat ":" (string-join tags ":") ":")
|
||||
'face 'elfeed-search-tag-face))
|
||||
(title-width (- (window-width) 10 elfeed-search-trailing-width))
|
||||
(title-column (elfeed-format-column
|
||||
title (elfeed-clamp
|
||||
elfeed-search-title-min-width
|
||||
title-width
|
||||
elfeed-search-title-max-width)
|
||||
:left)))
|
||||
;; relative date
|
||||
(insert (propertize date 'face 'elfeed-search-date-face) " ")
|
||||
;; title
|
||||
(insert (propertize title-column 'face title-faces 'kbd-help title) " ")
|
||||
;; feed title
|
||||
(when feed-title
|
||||
(insert (propertize feed-title 'face 'elfeed-search-feed-face) " "))
|
||||
;; tags
|
||||
(when tags
|
||||
(insert tags-str))))))
|
||||
|
||||
;; install the print function
|
||||
(setq elfeed-search-print-entry-function #'bram85-elfeed-search-print-entry)
|
||||
(setq elfeed-search-trailing-width 20)
|
||||
|
||||
;; record read date when an entry is opened
|
||||
(eval-when-compile
|
||||
(require 'elfeed))
|
||||
|
||||
(defun bram85-elfeed--mark-read (entry read-date)
|
||||
"Set the READ-DATE for the given ENTRY."
|
||||
(unless (elfeed-tagged-p 'unread entry)
|
||||
(setf (elfeed-meta entry :read-date) read-date)))
|
||||
|
||||
(defun bram85-elfeed-mark-read (entries tags)
|
||||
"Mark the ENTRIES as read and record the read date with some delay.
|
||||
|
||||
It is considered marked read if the 'unread' tag is part of
|
||||
TAGS to be removed from ENTRIES."
|
||||
(when (member 'unread tags)
|
||||
(let* ((date (format-time-string "%FT%T%z"))
|
||||
(entry-unread-p (lambda (e) (elfeed-tagged-p 'unread e)))
|
||||
(filtered-entries (seq-filter entry-unread-p entries)))
|
||||
(dolist (entry filtered-entries)
|
||||
(run-with-timer 5 nil #'bram85-elfeed--mark-read entry date)))))
|
||||
|
||||
(defun bram85-elfeed-mark-as-unread (entries tags)
|
||||
"Reset the read date when ENTRIES are marked as read again.
|
||||
|
||||
It is considered marked unread if the 'unread' tag is part of
|
||||
TAGS to be added to ENTRIES."
|
||||
(when (member 'unread tags)
|
||||
(dolist (entry entries)
|
||||
(setf (elfeed-meta entry :read-date) nil))))
|
||||
|
||||
(add-hook 'elfeed-untag-hooks 'bram85-elfeed-mark-read)
|
||||
(add-hook 'elfeed-tag-hooks 'bram85-elfeed-mark-as-unread)
|
||||
|
||||
;; Fix bug in Elfeed where the history variable
|
||||
;; elfeed-search-filter-history remains unused.
|
||||
(defun bram85-elfeed-advice-elfeed-search-live-filter (f &rest args)
|
||||
(let ((read-from-minibuffer-orig (symbol-function #'read-from-minibuffer)))
|
||||
(cl-letf (((symbol-function #'read-from-minibuffer)
|
||||
(lambda (prompt init-value)
|
||||
(funcall read-from-minibuffer-orig prompt init-value nil nil 'elfeed-search-filter-history))))
|
||||
(apply f args))))
|
||||
|
||||
(advice-add #'elfeed-search-live-filter :around #'bram85-elfeed-advice-elfeed-search-live-filter)
|
||||
(add-hook 'elfeed-update-hooks (lambda (_) (when (zerop (elfeed-queue-count-total)) (elfeed-db-save))))
|
||||
|
||||
;; update elfeed every two hours, only when idle
|
||||
(run-with-timer nil (* 2 60 60) (lambda () (run-with-idle-timer 120 nil 'elfeed-update))))
|
||||
#+end_src
|
||||
|
||||
* Meta
|
||||
** License
|
||||
|
|
192
gists/elfeed.el
Normal file
192
gists/elfeed.el
Normal file
|
@ -0,0 +1,192 @@
|
|||
;; For constructing relative date strings from a timestamp.
|
||||
;; Clone from https://github.com/rougier/relative-date
|
||||
(use-package relative-date
|
||||
:ensure nil
|
||||
:load-path "lisp/relative-date"
|
||||
:config
|
||||
(defun bram85-fix-relative-date-in-future (f &rest args)
|
||||
"Fix future relative dates, as they are returned with a +-
|
||||
prefix given the `relative-dates-formats' below. Because the
|
||||
internal data is negative, it shows up as such when replacing
|
||||
placeholders."
|
||||
(replace-regexp-in-string "\\`+-" "+" (apply f args)))
|
||||
|
||||
(advice-add 'relative-date :around #'bram85-fix-relative-date-in-future)
|
||||
|
||||
(setq relative-date-formats
|
||||
(let* ((seconds 1)
|
||||
(minutes (* 60 seconds))
|
||||
(hours (* 60 minutes))
|
||||
(days (* 24 hours))
|
||||
(months (* 30 days))
|
||||
(years (* 365 days)))
|
||||
`((, (* 3 minutes) . "now") ;; Less than 3 minutes (past)
|
||||
(,(- (* 3 minutes)) . "soon") ;; Less than 3 minutes (future)
|
||||
(, (* 1 hours) . "%(M)m") ;; Less than 1 hour
|
||||
(,(- (* 1 hours)) . "+%(M)m") ;; Less than 1 hour in the future
|
||||
(, (* 24 hours) . "%(H)h") ;; Less than 24 hours
|
||||
(,(- (* 24 hours)) . "+%(H)h") ;; Less than 24 hours in the future
|
||||
(, (* 7 days) . "%(d)d") ;; Less than 7 days
|
||||
(,(- (* 7 days)) . "+%(d)d") ;; Less than 7 days in the future
|
||||
(, (* 1 months) . "%(w)w") ;; Less than 30 days
|
||||
(,(- (* 1 months)) . "+%(w)w") ;; Less than 30 days in the future
|
||||
(, (* 365 days) . "%(m)mo") ;; Less than a year
|
||||
(,(- (* 365 days)) . "+%(m)mo") ;; Less than a year in the future
|
||||
(, (* 1000 years) . "%(y)y") ;; Less than 1000 years
|
||||
(,(- (* 100 years)) . "+%(y)y") ;; Less than 100 years in the future (to fit in 4 chars)
|
||||
))))
|
||||
|
||||
(use-package elfeed
|
||||
:after relative-date
|
||||
:init
|
||||
(defun bram85-elfeed-show-eww-open (&optional use-generic-p)
|
||||
"open with eww"
|
||||
(interactive "P")
|
||||
(let ((browse-url-browser-function #'eww-browse-url))
|
||||
(elfeed-show-visit use-generic-p)))
|
||||
|
||||
(defun bram85-elfeed-search-eww-open (&optional use-generic-p)
|
||||
"open with eww"
|
||||
(interactive "P")
|
||||
(let ((browse-url-browser-function #'eww-browse-url))
|
||||
(elfeed-search-browse-url use-generic-p)))
|
||||
|
||||
;; The following functions is for easy navigation, taken from
|
||||
;; Karthinks blog: https://karthinks.com/software/lazy-elfeed/
|
||||
(defun bram85-elfeed-scroll-up-command (&optional arg)
|
||||
"Scroll up or go to next feed item in Elfeed"
|
||||
(interactive "^P")
|
||||
(let ((scroll-error-top-bottom nil))
|
||||
(condition-case-unless-debug nil
|
||||
(scroll-up-command arg)
|
||||
(error (elfeed-show-next)))))
|
||||
|
||||
(defun bram85-elfeed-scroll-down-command (&optional arg)
|
||||
"Scroll down or go to prev feed item in Elfeed"
|
||||
(interactive "^P")
|
||||
(let ((scroll-error-top-bottom nil))
|
||||
(condition-case-unless-debug nil
|
||||
(scroll-down-command arg)
|
||||
(error (elfeed-show-prev)))))
|
||||
:bind
|
||||
(("C-x w" . elfeed)
|
||||
:map elfeed-show-mode-map
|
||||
("w" . elfeed-show-visit)
|
||||
("W" . bram85-elfeed-show-eww-open)
|
||||
("SPC" . bram85-elfeed-scroll-up-command)
|
||||
("S-SPC" . bram85-elfeed-scroll-down-command)
|
||||
:map elfeed-search-mode-map
|
||||
("w" . elfeed-search-browse-url)
|
||||
("W" . bram85-elfeed-search-eww-open))
|
||||
:config
|
||||
(setq-default elfeed-search-filter "@3-days-ago +unread")
|
||||
(setq elfeed-feeds '(
|
||||
;; ...
|
||||
))
|
||||
|
||||
;; enable relative dates which are max 4 chars wide
|
||||
(setq elfeed-search-date-format '("rel" 4 :right))
|
||||
|
||||
(defvar bram85-elfeed-hidden-tags
|
||||
'(unread
|
||||
highvol
|
||||
lowvol
|
||||
linkonly)
|
||||
"List of tags which do not get printed.")
|
||||
|
||||
(defun bram85-elfeed-tags (entry)
|
||||
"Return a list of tags as strings for the given ENTRY, where
|
||||
hidden tags are filtered out."
|
||||
(mapcar #'symbol-name
|
||||
(seq-difference (elfeed-entry-tags entry) bram85-elfeed-hidden-tags)))
|
||||
|
||||
(defun bram85-elfeed-search-print-entry (entry)
|
||||
"Print elfeed ENTRY to the buffer."
|
||||
(let ((format-time-string-orig (symbol-function #'format-time-string)))
|
||||
;; Make sure that elfeed-search-format-date calls relative-date
|
||||
;; instead of format-time-string, if the format is "rel".
|
||||
(cl-letf (((symbol-function #'format-time-string)
|
||||
(lambda (fmt &optional time zone)
|
||||
(if (string= fmt "rel")
|
||||
(relative-date time)
|
||||
;; For all other formats, call the builtin function.
|
||||
(funcall format-time-string-orig fmt time zone)))))
|
||||
(let* ((date (elfeed-search-format-date (elfeed-entry-date entry)))
|
||||
(title (or (elfeed-meta entry :title) (elfeed-entry-title entry) ""))
|
||||
(title-faces (elfeed-search--faces (elfeed-entry-tags entry)))
|
||||
(feed (elfeed-entry-feed entry))
|
||||
(feed-title
|
||||
(when feed
|
||||
(or (elfeed-meta feed :title) (elfeed-feed-title feed))))
|
||||
(tags (bram85-elfeed-tags entry))
|
||||
(tags-str (propertize (concat ":" (string-join tags ":") ":")
|
||||
'face 'elfeed-search-tag-face))
|
||||
(title-width (- (window-width) 10 elfeed-search-trailing-width))
|
||||
(title-column (elfeed-format-column
|
||||
title (elfeed-clamp
|
||||
elfeed-search-title-min-width
|
||||
title-width
|
||||
elfeed-search-title-max-width)
|
||||
:left)))
|
||||
;; relative date
|
||||
(insert (propertize date 'face 'elfeed-search-date-face) " ")
|
||||
;; title
|
||||
(insert (propertize title-column 'face title-faces 'kbd-help title) " ")
|
||||
;; feed title
|
||||
(when feed-title
|
||||
(insert (propertize feed-title 'face 'elfeed-search-feed-face) " "))
|
||||
;; tags
|
||||
(when tags
|
||||
(insert tags-str))))))
|
||||
|
||||
;; install the print function
|
||||
(setq elfeed-search-print-entry-function #'bram85-elfeed-search-print-entry)
|
||||
(setq elfeed-search-trailing-width 20)
|
||||
|
||||
;; record read date when an entry is opened
|
||||
(eval-when-compile
|
||||
(require 'elfeed))
|
||||
|
||||
(defun bram85-elfeed--mark-read (entry read-date)
|
||||
"Set the READ-DATE for the given ENTRY."
|
||||
(unless (elfeed-tagged-p 'unread entry)
|
||||
(setf (elfeed-meta entry :read-date) read-date)))
|
||||
|
||||
(defun bram85-elfeed-mark-read (entries tags)
|
||||
"Mark the ENTRIES as read and record the read date with some delay.
|
||||
|
||||
It is considered marked read if the 'unread' tag is part of
|
||||
TAGS to be removed from ENTRIES."
|
||||
(when (member 'unread tags)
|
||||
(let* ((date (format-time-string "%FT%T%z"))
|
||||
(entry-unread-p (lambda (e) (elfeed-tagged-p 'unread e)))
|
||||
(filtered-entries (seq-filter entry-unread-p entries)))
|
||||
(dolist (entry filtered-entries)
|
||||
(run-with-timer 5 nil #'bram85-elfeed--mark-read entry date)))))
|
||||
|
||||
(defun bram85-elfeed-mark-as-unread (entries tags)
|
||||
"Reset the read date when ENTRIES are marked as read again.
|
||||
|
||||
It is considered marked unread if the 'unread' tag is part of
|
||||
TAGS to be added to ENTRIES."
|
||||
(when (member 'unread tags)
|
||||
(dolist (entry entries)
|
||||
(setf (elfeed-meta entry :read-date) nil))))
|
||||
|
||||
(add-hook 'elfeed-untag-hooks 'bram85-elfeed-mark-read)
|
||||
(add-hook 'elfeed-tag-hooks 'bram85-elfeed-mark-as-unread)
|
||||
|
||||
;; Fix bug in Elfeed where the history variable
|
||||
;; elfeed-search-filter-history remains unused.
|
||||
(defun bram85-elfeed-advice-elfeed-search-live-filter (f &rest args)
|
||||
(let ((read-from-minibuffer-orig (symbol-function #'read-from-minibuffer)))
|
||||
(cl-letf (((symbol-function #'read-from-minibuffer)
|
||||
(lambda (prompt init-value)
|
||||
(funcall read-from-minibuffer-orig prompt init-value nil nil 'elfeed-search-filter-history))))
|
||||
(apply f args))))
|
||||
|
||||
(advice-add #'elfeed-search-live-filter :around #'bram85-elfeed-advice-elfeed-search-live-filter)
|
||||
(add-hook 'elfeed-update-hooks (lambda (_) (when (zerop (elfeed-queue-count-total)) (elfeed-db-save))))
|
||||
|
||||
;; update elfeed every two hours, only when idle
|
||||
(run-with-timer nil (* 2 60 60) (lambda () (run-with-idle-timer 120 nil 'elfeed-update))))
|
Loading…
Reference in a new issue