RSS
阅读 RSS 使用的是 elfeed,下面两个插件搭配起来使用,体验会更流程:
- elfeed-org 使用 org 文件来定义 feeds 列表,它可以很方便进行打标签,重命名
- elfeed-dashboard 把定义的 feeds 列表按标签展示
(use-package elfeed
:custom ((elfeed-use-curl t)
(elfeed-db-directory "~/Downloads/elfeed/")
(elfeed-curl-timeout 20)
(elfeed-curl-extra-arguments `("-x" ,socks-proxy)))
:bind (:map elfeed-show-mode-map
("8" . my/elfeed-toggle-star)
("9" . my/elfeed-show-images)
("g" . elfeed-show-refresh)
:map elfeed-search-mode-map
("SPC" . scroll-up-command)
("DEL" . scroll-down-command)
("f" . my/elfeed-search-by-title)
("b" . my/elfeed-browse)
("8" . my/elfeed-toggle-star))
:init
(progn
(defun my/elfeed-browse ()
(interactive)
(let ((entries (elfeed-search-selected)))
(elfeed-untag entries 'unread)
(mapc #'elfeed-search-update-entry entries))
(elfeed-search-browse-url))
(defun my/elfeed-add-title-tag (entry)
(when-let* ((feed (elfeed-entry-feed entry))
(title (cl-destructuring-bind (&key title &allow-other-keys)
(elfeed-feed-meta feed)
title)))
(elfeed-tag entry (intern title))))
(add-hook 'elfeed-new-entry-hook 'my/elfeed-add-title-tag 1)
(defun my/elfeed-hook ()
;; (variable-pitch-mode)
;; (setq-local shr-inhibit-images nil)
(setq-local line-spacing 0.5)
(setq-local shr-width 85))
(defun my/elfeed-search-print-entry (entry)
(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 (seq-filter
(lambda (tag) (not (string-equal feed-title tag)))
(mapcar #'symbol-name (elfeed-entry-tags entry))))
(tags-str (mapconcat
(lambda (s) (propertize s 'face 'elfeed-search-tag-face))
tags ","))
(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)))
(insert (propertize date 'face 'elfeed-search-date-face) " ")
(insert (propertize title-column 'face title-faces 'kbd-help title) " ")
(when feed-title
(insert (propertize feed-title 'face 'elfeed-search-feed-face) " "))
(when tags
(insert "(" tags-str ")"))))
(setq elfeed-search-print-entry-function 'my/elfeed-search-print-entry))
:hook ((elfeed-search-mode elfeed-show-mode) . my/elfeed-hook)
:config
(progn
(setq elfeed-search-filter "@1-months-ago +unread #100")
(evil-add-hjkl-bindings elfeed-show-mode-map)
(evil-add-hjkl-bindings elfeed-search-mode-map)
(defun my/elfeed-clear-queue ()
"Sometime elfeed update will end with pending task, which popup at minibuffer, this fix the annoying message"
(interactive)
(setq elfeed-curl-queue-active 0))
(defun my/elfeed-show-images ()
(interactive)
(let ((shr-inhibit-images nil))
(elfeed-show-refresh)))
;;functions to support syncing .elfeed between machines
;;makes sure elfeed reads index from disk before launching
(defun my/elfeed-open-db-and-load ()
"Wrapper to load the elfeed db from disk before opening"
(interactive)
(elfeed-db-load)
(elfeed)
(elfeed-search-update--force))
;;write to disk when quiting
(defun my/elfeed-close-db-and-save ()
"Wrapper to save the elfeed db to disk before burying buffer"
(interactive)
(elfeed-db-save)
;; (quit-window)
)
(defun my/elfeed-toggle-star ()
(interactive)
(let* ((entry (or elfeed-show-entry
(first (elfeed-search-selected))))
(tag (intern "starred"))
(taggged (elfeed-tagged-p tag entry)))
(if taggged
(elfeed-untag entry tag)
(elfeed-tag entry tag))
(message "Starred: %s" (not taggged))))
(defun my/elfeed-search-star ()
(interactive)
(let ((tag (intern "starred"))
(entries (elfeed-search-selected)))
(cl-loop for entry in entries do (elfeed-tag entry tag))
(mapc #'elfeed-search-update-entry entries)
(unless (use-region-p) (forward-line))))
(defun my/elfeed-search-unstar ()
"Remove starred tag from all selected entries."
(interactive)
(let ((tag (intern "starred"))
(entries (elfeed-search-selected)))
(cl-loop for entry in entries do (elfeed-untag entry tag))
(mapc #'elfeed-search-update-entry entries)
(unless (use-region-p) (forward-line))))
(setq my/elfeed-export-dir (expand-file-name "~/Sync/"))
(defun my/elfeed-export (dir)
(interactive (list (read-directory-name "Export dir: " my/elfeed-export-dir)))
(require 'f)
(let* ((sf (elfeed-search-parse-filter "+starred"))
(uf (elfeed-search-parse-filter "-unread"))
(starred-entries '())
(read-entries '())
(hash-table (make-hash-table))
(output (expand-file-name (format-time-string "elfeed-%Y-%m-%d.el" (current-time))
dir)))
(with-elfeed-db-visit (entry feed)
(let ((title-and-link (cons (elfeed-entry-title entry)
(elfeed-entry-link entry))))
(when (elfeed-search-filter sf entry feed)
(add-to-list 'starred-entries title-and-link))
(when (elfeed-search-filter uf entry feed)
(add-to-list 'read-entries title-and-link))))
(puthash :starred starred-entries hash-table)
(puthash :read read-entries hash-table)
(f-write-text (prin1-to-string hash-table) 'utf-8 output)
(message "Export to %s. starred: %d, read: %d" output (length starred-entries) (length read-entries))))
(defun my/elfeed-import (f)
(interactive (list (read-file-name "Backup file: " my/elfeed-export-dir)))
(require 'f)
(let* ((hash-table (read (f-read-text f)))
(starred-entries (thread-last (gethash :starred hash-table)
(mapcar 'cdr)))
(read-entries (thread-last (gethash :read hash-table)
(mapcar 'cdr))))
(with-elfeed-db-visit (entry feed)
(let* ((link (elfeed-entry-link entry)))
(when (member link starred-entries)
(elfeed-tag entry (intern "starred")))
(when (member link read-entries)
(elfeed-untag entry (intern "unread")))))
(message "Import starred: %d, read: %d" (length starred-entries) (length read-entries))))
(defun my/elfeed-search-by-title (title)
(interactive (list (or (when-let* ((entry (car (elfeed-search-selected)))
(feed (elfeed-entry-feed entry)))
(or (cl-destructuring-bind (&key title &allow-other-keys)
(elfeed-feed-meta feed)
title)
(elfeed-feed-title feed)))
(read-from-minibuffer "Feed Title: "))))
(unwind-protect
(let ((elfeed-search-filter-active :live))
(setq elfeed-search-filter (concat "+" title)))
(elfeed-search-update :force)))
(custom-set-faces
'(elfeed-search-unread-title-face ((((class color) (background light)) (:foreground "#000" :weight normal :strike-through nil))
(((class color) (background dark)) (:foreground "#fff" :weight normal :strike-through nil))))
'(elfeed-search-title-face ((((class color) (background light)) (:foreground "grey" :strike-through t))
(((class color) (background dark)) (:foreground "grey" :strike-through t)))))
;; face for starred articles
(defface elfeed-search-starred-title-face
'((t :foreground "#f77" :strike-through nil))
"Marks a starred Elfeed entry.")
(push '(starred elfeed-search-starred-title-face) elfeed-search-face-alist)))
(use-package elfeed-dashboard
:ensure nil
:commands (elfeed-dashboard)
:bind (:map elfeed-dashboard-mode-map
("/" . my/feed-choose-by-tag)
:map elfeed-search-mode-map
("/" . my/feed-choose-by-tag))
:config
(setq elfeed-dashboard-file (no-littering-expand-etc-file-name "elfeed-dashboard.org"))
;; Disable this update, it cost too much cpu when there are many feeds
;; Update feed counts on elfeed-quit
;; (advice-add 'elfeed-search-quit-window :after #'elfeed-dashboard-update-links)
(defun my/feed-choose-by-tag ()
(interactive)
(ivy-read "Tag: " (elfeed-db-get-all-tags)
:action (lambda (tag)
(elfeed-dashboard-query (format "+unread +%s" tag))))))
(use-package elfeed-org
:ensure nil
:custom ((rmh-elfeed-org-files `(,(no-littering-expand-etc-file-name "elfeed-feeds.org"))))
:hook (elfeed-dashboard-mode . elfeed-org)
:init
(progn
(defun my/reload-org-feeds ()
(interactive)
(rmh-elfeed-org-process rmh-elfeed-org-files rmh-elfeed-org-tree-id))
(advice-add 'elfeed-dashboard-update :before #'my/reload-org-feeds)))下面阐述 elfeed 核心配置:
elfeed-curl-extra-arguments可以配置 curl 的参数,比较常用的是配置代理- elfeed 默认使用 shr 展示,在加载图片是会卡住,可通过设置
shr-inhibit-images为nil来禁用图片,之后通过自定义函数my/show-feed-images来开启图片 - 为了方便记录重要文章,定义
my/elfeed-toggle-star函数来实现「标星」效果,并可以使用+starred来搜索 - 默认 elfeed 列表行间距太小,可以通过
(setq-local line-spacing 0.3)调整 elfeed 的数据库存放在本地,可以把这个目录防止云盘的同步目录里。我这里实现了两个函数,来对指定的 tag 进行导出/导入,这样相当于只备份元数据,更轻量些。
- 导出时主要有两个 tag,一个是
+starred,表示加星的文章;另一个是-unread,表示已读的。
- 导出时主要有两个 tag,一个是
查询语法
| 查询语句 | 含义 |
|---|---|
@6-months-ago +unread | 6 个月内的未读文章 |
-unread +youtube #10 | 已读的与 youtube 相关的前 10 篇文章 |
+emacs =http://example.org/feed | 指定 feed 内,与 emacs 有关的文章 |
常用快捷键
elfeed-search
s在 minibuffer 重新输入搜索词yyank 当前行文章的 URL,并去掉 unread 标签b调用外部浏览器打开所选行文章g刷新当前 feed 列表G重新抓取 feed 记录
elfeed-show
TAB切换到下一个超链接处g刷新b调用外部浏览器打开文章