mirror of
https://github.com/SqrtMinusOne/elfeed-summary.git
synced 2025-12-10 17:43:03 +03:00
Compare commits
26 commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 76b4b93838 | |||
|
|
c82d0dff9b | ||
| 7e308adaa3 | |||
| 5ca7a36eca | |||
| 92b5d026e0 | |||
| efa30b88d7 | |||
| 129b893da1 | |||
| 6bad19a7d4 | |||
| 96ffc25862 | |||
| d3caffb44a | |||
| 063f60a9a7 | |||
|
|
7a25708b8a | ||
| ccbaf85d9e | |||
| b583c2f3ea | |||
| 49dd44be53 | |||
| 38099f3dd3 | |||
| d51ca5be1d | |||
| 7193b2f269 | |||
| d302c10be3 | |||
| ebf851fa2d | |||
| 121a3df868 | |||
| fa917339d2 | |||
| 125e0f059d | |||
| 5006ef6432 | |||
|
|
1930de3924 | ||
| 012f6fee58 |
4 changed files with 664 additions and 53 deletions
11
.github/workflows/melpazoid.yml
vendored
11
.github/workflows/melpazoid.yml
vendored
|
|
@ -10,15 +10,16 @@ jobs:
|
|||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python 3.6
|
||||
uses: actions/setup-python@v1
|
||||
with: { python-version: 3.6 }
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up Python 3.10
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.10'
|
||||
- name: Install
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
sudo apt-get install emacs && emacs --version
|
||||
git clone https://github.com/riscy/melpazoid.git ~/melpazoid
|
||||
git clone https://github.com/SqrtMinusOne/melpazoid.git ~/melpazoid
|
||||
pip install ~/melpazoid
|
||||
- name: Run
|
||||
env:
|
||||
|
|
|
|||
5
.gitignore
vendored
5
.gitignore
vendored
|
|
@ -1,3 +1,6 @@
|
|||
# Autoloads
|
||||
*autoloads*
|
||||
|
||||
# Compiled
|
||||
*.elc
|
||||
|
||||
|
|
@ -8,4 +11,4 @@
|
|||
*~
|
||||
|
||||
# Undo-tree save-files
|
||||
*.~undo-tree
|
||||
*.~undo-tree
|
||||
|
|
|
|||
137
README.org
137
README.org
|
|
@ -24,6 +24,8 @@ The tree consists of:
|
|||
- searches;
|
||||
- groups, that can include other groups, feeds, and searches.
|
||||
|
||||
Groups can also be generated automatically.
|
||||
|
||||
Available keybindings in the summary mode:
|
||||
|
||||
| Keybinding | Command | Description |
|
||||
|
|
@ -49,6 +51,10 @@ This is a list of these possible items:
|
|||
Query extracts a subset of elfeed feeds based on the given criteria. Each found feed will be represented as a line.
|
||||
- Search =(search . <search-params>)=
|
||||
Elfeed search, as defined by =elfeed-search-set-filter=.
|
||||
- Tags tree =(auto-tags . <auto-tags-params>)=
|
||||
A tree generated automatically from the available tags.
|
||||
- Tag groups =(tag-groups . <tag-group-params>)=
|
||||
Insert one tag as one group.
|
||||
- a few special forms
|
||||
|
||||
=<group-params>= is an alist with the following keys:
|
||||
|
|
@ -89,6 +95,21 @@ Query examples:
|
|||
- =:title= (mandatory) title.
|
||||
- =:tags= - list of tags to get the face of the entry.
|
||||
|
||||
=<auto-tags-params>= is an alist with the following keys:
|
||||
- =:max-level= - maximum level of the tree (default 2)
|
||||
- =:source= - which feeds to use to build the tree.
|
||||
Can be =:misc= (default) or =(query . <query-params>)=.
|
||||
- =:original-order= - do not try to build a more concise tree by
|
||||
putting the most frequent tags closer to the root of the tree.
|
||||
- =:faces= - list of faces for groups.
|
||||
|
||||
=<tag-group-params>= is an alist with the following keys:
|
||||
- =:source= - which feeds to use to build the tree.
|
||||
Can be =:misc= (default) or =(query . <query-params>)=.
|
||||
- =:repeat-feeds= - allow feeds to repeat. Otherwise, each feed is
|
||||
assigned to group with the least amount of members.
|
||||
- =:face= - face for groups.
|
||||
|
||||
Available special forms:
|
||||
- =:misc= - print out feeds, not found by any query above.
|
||||
|
||||
|
|
@ -150,11 +171,106 @@ Here is an excerpt from my configuration that was used to produce this screensho
|
|||
(:title . "Ungrouped")
|
||||
(:elements :misc))))))
|
||||
#+end_src
|
||||
** Automatic generation of groups
|
||||
*** =auto-tags=
|
||||
As described in the [[*Tree configuration][tree configuration]] section, there are two ways to avoid defining all the relevant groups manually, =auto-tags= and =tag-groups=. Both use tags that are defined in =elfeed-feeds=.
|
||||
|
||||
=auto-tags= tries to build the most concise tree from these tags. E.g. if we have feeds:
|
||||
#+begin_example
|
||||
feed1 tag1 tag2
|
||||
feed2 tag1 tag2
|
||||
feed3 tag1 tag3
|
||||
feed4 tag1 tag3
|
||||
#+end_example
|
||||
|
||||
It will create the following tree:
|
||||
- tag1
|
||||
- tag2
|
||||
- feed1
|
||||
- feed2
|
||||
- tag3
|
||||
- feed3
|
||||
- feed4
|
||||
|
||||
The tree is truncated by =:max-level=, which is 2 by default.
|
||||
|
||||
If tags don't form this kind of hierarchy in =elfeed-feeds=, the algorithm will still try to build the most "optimal" tree, where the most frequent tags are on the top.
|
||||
|
||||
To avoid that you can set =(:original-order . t)=, in which case each feed will be placed at the path =tag1 tag2 ... tagN feed=, where the order of tags is the same as in =elfeed-feeds=. By the way, this allows reproducing the hierarchy of [[https://github.com/remyhonig/elfeed-org][elfeed-org]], e.g. this structure:
|
||||
#+begin_example
|
||||
,* tag1 :tag1:
|
||||
,** feed1
|
||||
,** feed2 :tag2:
|
||||
,** feed3 :tag2:
|
||||
,* tag3 :tag3:
|
||||
,** feed4 :tag2:
|
||||
,** feed5 :tag2:
|
||||
,** feed6 :tag2:
|
||||
#+end_example
|
||||
|
||||
Will be converted to this:
|
||||
- tag1
|
||||
- feed1
|
||||
- tag2
|
||||
- feed2
|
||||
- feed3
|
||||
- tag3
|
||||
- tag2
|
||||
- feed4
|
||||
- feed5
|
||||
- feed6
|
||||
|
||||
Whereas without =:original-order= the structure will be:
|
||||
- tag1
|
||||
- feed1
|
||||
- tag2
|
||||
- tag1
|
||||
- feed2
|
||||
- feed3
|
||||
- tag3
|
||||
- feed4
|
||||
- feed5
|
||||
- feed6
|
||||
*** =tag-groups=
|
||||
The second option is =tag-groups=, which creates a group for each tag.
|
||||
|
||||
By default, each feed is assigned to its less frequent tag. This can be turned off by setting =(:repeat-feeds . t)=.
|
||||
|
||||
E.g., the elfeed-org setup from the section above will be converted to this structure:
|
||||
- tag1
|
||||
- feed1
|
||||
- feed2
|
||||
- feed3
|
||||
- tag3
|
||||
- feed4
|
||||
- feed5
|
||||
- feed6
|
||||
|
||||
And with =:repeat-feeds=:
|
||||
- tag1
|
||||
- feed1
|
||||
- feed2
|
||||
- feed3
|
||||
- tag2
|
||||
- feed2
|
||||
- feed3
|
||||
- feed4
|
||||
- feed5
|
||||
- feed6
|
||||
- tag3
|
||||
- feed4
|
||||
- feed5
|
||||
- feed6
|
||||
*** Common options
|
||||
Both =auto-tags= and =tag-groups= allow setting the =:search= parameter.
|
||||
|
||||
The default value is =(:search . :misc)=, i.e. use feeds that weren't found by other queries.
|
||||
|
||||
Passing =(:search . (query . <query-params>))= is another option.
|
||||
** Faces
|
||||
Faces for groups by default use the =elfeed-summary-group-faces= variable, which serves as a list of faces for each level of the tree. Individual group faces can be overridden with the =:face= attribute.
|
||||
Group faces by default use the =elfeed-summary-group-faces= variable, which serves as a list of faces for each level of the tree. Individual group faces can be overridden with the =:face= attribute.
|
||||
|
||||
Faces for feeds by default reuse [[https://github.com/skeeto/elfeed#custom-tag-faces][the existing elfeed mechanism]]. The tags for feeds are taken from the =elfeed-feeds= variable; if a feed has at least one unread entry, the unread tag is added to the list. This can be overridden by setting the =elfeed-summary-feed-face-fn= variable.
|
||||
Feed faces by default reuse [[https://github.com/skeeto/elfeed#custom-tag-faces][the existing elfeed mechanism]]. The tags for feeds are taken from the =elfeed-feeds= variable; if a feed has at least one unread entry, the unread tag is added to the list. This can be overridden by setting the =elfeed-summary-feed-face-fn= variable.
|
||||
|
||||
Searches are mostly the same as feeds, but tags for the search are taken from the =:tags= attribute. This also can be overridden with =elfeed-summary-search-face-fn= variable.
|
||||
** Opening =elfeed-search= in other window
|
||||
|
|
@ -166,10 +282,23 @@ If you set:
|
|||
Then =RET= and =M-RET= in the =elfeed-summary= buffer will open the search buffer in other window.
|
||||
|
||||
=elfeed-summary-width= regulates the width of the remaining summary window in this case. It is useful because the data in the search buffer is generally wider than in the summary buffer. The variable can also be set to =nil= to disable this behavior.
|
||||
** Skipping feeds
|
||||
[[https://tt-rss.org/][tt-rss]] has a feature to disable updating a particular feed but keep it in the feed list. I also want that for elfeed.
|
||||
|
||||
To use that, set =elfeed-summary-skip-sync-tag= to some value:
|
||||
#+begin_src emacs-lisp
|
||||
(setq elfeed-summary-skip-sync-tag 'skip)
|
||||
#+end_src
|
||||
|
||||
And tag the feeds you want to skip with this tag. Then, running =M-x elfeed-summary-update= will skip them. This won't affect =M-x elfeed-update= unless you:
|
||||
#+begin_src emacs-lisp
|
||||
(advice-add #'elfeed-update :override #'elfeed-summary-update)
|
||||
#+end_src
|
||||
|
||||
Also watch out if you use [[https://github.com/remyhonig/elfeed-org][elfeed-org]] and want to use the =ignore= tag, because this package omits feeds with this tag altogether (configurable by =rmh-elfeed-org-ignore-tag=).
|
||||
|
||||
** Other options
|
||||
Also take a look at =M-x customize-group elfeed-summary= for the rest of available options.
|
||||
|
||||
Setting a unique URL for every feed resolves the problem.
|
||||
* Ideas and alternatives
|
||||
The default interface of elfeed is just a list of all entries. Naturally, it gets hard to navigate when there are a lot of sources with varying frequencies of posts.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
;;; elfeed-summary.el --- Feed summary interface for elfeed -*- lexical-binding: t -*-
|
||||
|
||||
;; Copyright (C) 2022 Korytov Pavel
|
||||
;; Copyright (C) 2022, 2023 Korytov Pavel
|
||||
|
||||
;; Author: Korytov Pavel <thexcloud@gmail.com>
|
||||
;; Maintainer: Korytov Pavel <thexcloud@gmail.com>
|
||||
;; Version: 0.1.0
|
||||
;; Version: 0.1.1
|
||||
;; Package-Requires: ((emacs "27.1") (magit-section "3.3.0") (elfeed "3.4.1"))
|
||||
;; Homepage: https://github.com/SqrtMinusOne/elfeed-summary.el
|
||||
;; Published-At: 2022-03-26
|
||||
|
||||
;; This file is NOT part of GNU Emacs.
|
||||
|
||||
|
|
@ -25,8 +26,8 @@
|
|||
|
||||
;;; Commentary:
|
||||
;; The package provides a tree-based feed summary interface for
|
||||
;; elfeed. The tree can include individual feeds, searches, and
|
||||
;; groups. It mainly serves as an easier "jumping point" for elfeed,
|
||||
;; elfeed. The tree can include individual feeds, searches, and
|
||||
;; groups. It mainly serves as an easier "jumping point" for elfeed,
|
||||
;; so searching a subset of the elfeed database is one action away.
|
||||
;;
|
||||
;; `elfeed-summary' pops up the summary buffer. The buffer shows
|
||||
|
|
@ -122,7 +123,47 @@
|
|||
(string :tag "Filter title"))
|
||||
(cons :tag "Tags"
|
||||
(const :tag "Tags" :tags)
|
||||
(repeat symbol)))))
|
||||
(repeat symbol))
|
||||
(cons (const :tag "Add the default filter string" :add-default)
|
||||
(boolean :tag "Add the default filter string")))))
|
||||
(cons :tag "Generate a tree from tags"
|
||||
(const auto-tags)
|
||||
(repeat :tag "Tree generation parameters"
|
||||
(choice
|
||||
(cons :tag "Source"
|
||||
(const :tag "Source" :source)
|
||||
(choice
|
||||
(cons :tag "Query"
|
||||
(const query)
|
||||
elfeed-summary-query)
|
||||
(const :tag "Misc feeds" :misc)))
|
||||
(cons :tag "Maximum tree level"
|
||||
(const :tag "Maximum tree level" :max-level)
|
||||
(number :tag "Value"))
|
||||
(cons :tag "Original tag order"
|
||||
(const :tag "Original tag order" :original-order)
|
||||
(boolean :tag "Original tag order"))
|
||||
(cons :tag "Faces"
|
||||
(const :tag "Faces" :faces)
|
||||
(repeat
|
||||
(face :tag "Face"))))))
|
||||
(cons :tag "Insert each tag as group"
|
||||
(const tag-groups)
|
||||
(repeat :tag "Tag group parameters"
|
||||
(choice
|
||||
(cons :tag "Source"
|
||||
(const :tag "Source" :source)
|
||||
(choice
|
||||
(cons :tag "Query"
|
||||
(const query)
|
||||
elfeed-summary-query)
|
||||
(const :tag "Misc feeds" :misc)))
|
||||
(cons :tag "Allow feeds to repeat"
|
||||
(const :tag "Allow feeds to repeat" :repeat-feeds)
|
||||
(boolean :tag "Allow feeds to repeat"))
|
||||
(cons :tag "Face"
|
||||
(const :tag "Face" :faces)
|
||||
(face :tag "Face")))))
|
||||
(const :tag "Misc feeds" :misc))))
|
||||
|
||||
(defgroup elfeed-summary ()
|
||||
|
|
@ -149,11 +190,15 @@ This is a list of these possible items:
|
|||
Each found feed will be represented as a line.
|
||||
- Search `(search . <search-params>)'
|
||||
Elfeed search, as defined by `elfeed-search-set-filter'.
|
||||
- Tags tree `(auto-tags . <auto-tags-params>)'
|
||||
A tree generated automatically from the available tags.
|
||||
- Tag groups `(tag-groups . <tag-group-params>)'
|
||||
Insert one tag as one group.
|
||||
- a few special forms
|
||||
|
||||
`<group-params>' is an alist with the following keys:
|
||||
- `:title' (mandatory)
|
||||
- `:elements' (mandatory) - elements of the group. The structure is
|
||||
- `:elements' (mandatory) - elements of the group. The structure is
|
||||
the same as in the root definition.
|
||||
- `:face' - group face. The default face is `elfeed-summary-group-face'.
|
||||
- `:hide' - if non-nil, the group is collapsed by default.
|
||||
|
|
@ -172,8 +217,6 @@ This is a list of these possible items:
|
|||
- `(or <q-1> <q-2> ... <q-n>)' or `(<q-1> <q-2> ... <q-n>)'
|
||||
Match if any of the conditions 1, 2, ..., n match.
|
||||
- `(not <query>)'
|
||||
|
||||
Feed tags for the query are determined by the `elfeed-feeds'
|
||||
variable.
|
||||
|
||||
Query examples:
|
||||
|
|
@ -190,6 +233,23 @@ Query examples:
|
|||
`elfeed-search-set-filter'
|
||||
- `:title' (mandatory) title.
|
||||
- `:tags' - list of tags to get the face of the entry.
|
||||
- `:add-default' - if t, prepend the filter with
|
||||
`elfeed-summary-default-filter'.
|
||||
|
||||
`<auto-tags-params>' is an alist with the following keys:
|
||||
- `:max-level' - maximum level of the tree (default 2)
|
||||
- `:source' - which feeds to use to build the tree.
|
||||
Can be `:misc' (default) or `(query . <query-params>)'.
|
||||
- `:original-order' - do not try to build a more concise tree by
|
||||
putting the most frequent tags closer to the root of the tree.
|
||||
- `:faces' - list of faces for groups.
|
||||
|
||||
`<tag-group-params>' is an alist with the following keys:
|
||||
- `:source' - which feeds to use to build the tree.
|
||||
Can be `:misc' (default) or `(query . <query-params>)'.
|
||||
- `:repeat-feeds' - allow feeds to repeat. Otherwise, each feed is
|
||||
assigned to group with the least amount of members.
|
||||
- `:face' - face for groups.
|
||||
|
||||
Available special forms:
|
||||
- `:misc' - print out feeds, not found by any query above.
|
||||
|
|
@ -229,6 +289,17 @@ Probably should be one of `elfeed-initial-tags'."
|
|||
:group 'elfeed-summary
|
||||
:type 'symbol)
|
||||
|
||||
(defcustom elfeed-summary-skip-sync-tag nil
|
||||
"Do not sync feeds with this tag.
|
||||
|
||||
Feeds are tagged in `elfeed-feeds'. Watch out if you're using
|
||||
elfeed-org, because `rmh-elfeed-org-ignore-tag' is set to \"ignore\"
|
||||
by default, which seems to remove the feed from `elfeed-feeds'
|
||||
altogether. This options keeps the feed there, just makes
|
||||
`elfeed-summary-update' to skip in sync."
|
||||
:group 'elfeed-summary
|
||||
:type 'symbol)
|
||||
|
||||
(defcustom elfeed-summary-feed-face-fn #'elfeed-summary--feed-face-fn
|
||||
"Function to get the face of the feed entry.
|
||||
|
||||
|
|
@ -266,6 +337,18 @@ ordering."
|
|||
:group 'elfeed-summary
|
||||
:type 'function)
|
||||
|
||||
(defcustom elfeed-summary-auto-tags-group-title-fn
|
||||
#'elfeed-summary--auto-tags-group-title
|
||||
"Function to get the title of an autogenerated group.
|
||||
|
||||
Accepts the only parameter, which is a tree node created by
|
||||
`elfeed-summary--arrange-sequences-in-tree'.
|
||||
|
||||
See `elfeed-summary--auto-tags-group-title' for the default
|
||||
implementation."
|
||||
:group 'elfeed-summary
|
||||
:type 'function)
|
||||
|
||||
(defcustom elfeed-summary-refresh-on-each-update nil
|
||||
"Whether to refresh the elfeed summary buffer after each update.
|
||||
|
||||
|
|
@ -315,6 +398,14 @@ Useful only if `elfeed-summary-other-window' is set to t."
|
|||
"Face for the number of entries of an unread feed or search."
|
||||
:group 'elfeed-summary)
|
||||
|
||||
(defface elfeed-summary-button-face
|
||||
'((t (:inherit bold)))
|
||||
"Face for buttons in the elfeed summary buffer.
|
||||
|
||||
This has to be distinct from the built-it `widget-button' face because
|
||||
some themes override the foreground there, which shadows faces in the
|
||||
button text."
|
||||
:group 'elfeed-summary)
|
||||
|
||||
;;; Logic
|
||||
(cl-defun elfeed-summary--match-tag (query &key tags title url author title-meta)
|
||||
|
|
@ -479,11 +570,17 @@ SEARCH is a `<search-params>' form as described in
|
|||
`elfeed-summary-settings'.
|
||||
|
||||
Implented the same way as `elfeed-search--update-list'."
|
||||
(let* ((filter (elfeed-search-parse-filter (alist-get :filter search)))
|
||||
(let* ((filter-str
|
||||
(concat
|
||||
(when (alist-get :add-default search)
|
||||
elfeed-summary-default-filter)
|
||||
(alist-get :filter search)))
|
||||
(filter (elfeed-search-parse-filter filter-str))
|
||||
(head (list nil))
|
||||
(tail head)
|
||||
(unread 0)
|
||||
(total 0))
|
||||
(total 0)
|
||||
unread-ids)
|
||||
(if elfeed-search-compile-filter
|
||||
;; Force lexical bindings regardless of the current
|
||||
;; buffer-local value. Lexical scope uses the faster
|
||||
|
|
@ -496,20 +593,279 @@ Implented the same way as `elfeed-search--update-list'."
|
|||
tail (cdr tail)
|
||||
total (1+ total))
|
||||
(when (member elfeed-summary-unread-tag (elfeed-entry-tags entry))
|
||||
(setq unread (1+ unread))))))
|
||||
(setq unread (1+ unread))
|
||||
(push (elfeed-entry-id entry) unread-ids) ))))
|
||||
(with-elfeed-db-visit (entry feed)
|
||||
(when (elfeed-search-filter filter entry feed total)
|
||||
(setf (cdr tail) (list entry)
|
||||
tail (cdr tail)
|
||||
total (1+ total))
|
||||
(when (member elfeed-summary-unread-tag (elfeed-entry-tags entry))
|
||||
(setq unread (1+ unread))))))
|
||||
(setq unread (1+ unread))
|
||||
(push (elfeed-entry-id entry) unread-ids)))))
|
||||
`(search . ((params . ,(cdr search))
|
||||
(faces . ,(funcall elfeed-summary-search-face-fn
|
||||
(cdr search) unread total))
|
||||
(unread . ,unread)
|
||||
(unread-ids . ,unread-ids)
|
||||
(total . ,total)))))
|
||||
|
||||
(defun elfeed-summary--get-tags-ordered ()
|
||||
"Return the list of elfeed tags, properly ordered.
|
||||
|
||||
The tags are ordered (1) by their most frequent position in
|
||||
`elfeed-feeds' and (2) alphabetically."
|
||||
(let* ((tags-order
|
||||
;; list of (tag . ((<position-1> . <freq-1>) (<position-2> . <freq-2>) ...))
|
||||
(cl-loop with tags-order = '()
|
||||
for feed in elfeed-feeds
|
||||
do (cl-loop for tag in (cdr feed)
|
||||
for i from 0
|
||||
unless (alist-get tag tags-order)
|
||||
do (push (list tag) tags-order)
|
||||
do (cl-incf (alist-get
|
||||
i (alist-get tag tags-order) 0)))
|
||||
finally return tags-order))
|
||||
;; list of (tag . <most-frequent-position>)
|
||||
(tags-most-freq-order
|
||||
(cl-loop for (tag . order) in tags-order collect
|
||||
(cons
|
||||
tag
|
||||
(car
|
||||
(cl-reduce
|
||||
(lambda (acc value)
|
||||
(if (> (cdr value) (cdr acc))
|
||||
value
|
||||
acc))
|
||||
order
|
||||
:initial-value '(-1 . -1)))))))
|
||||
(mapcar
|
||||
#'car
|
||||
(seq-sort
|
||||
(lambda (datum1 datum2)
|
||||
(if (not (= (cdr datum1) (cdr datum2)))
|
||||
(< (cdr datum1) (cdr datum2))
|
||||
(string-lessp (symbol-name (car datum1))
|
||||
(symbol-name (car datum2)))))
|
||||
tags-most-freq-order))))
|
||||
|
||||
(defun elfeed-summary--build-tree-auto-tags-reorder-tags (feeds)
|
||||
"Reorder tags in FEEDS.
|
||||
|
||||
FEEDS is a list of (<feed> . <tags>), where <feed> is an instance of
|
||||
`elfeed-feed' and <tags> is a list of tag symbols."
|
||||
(let* ((all-tags (elfeed-summary--get-tags-ordered))
|
||||
(tag-priority (make-hash-table)))
|
||||
(cl-loop for tag in all-tags
|
||||
for i from 0
|
||||
do (puthash tag i tag-priority))
|
||||
(cl-loop for (feed . tags) in feeds
|
||||
collect
|
||||
(cons feed
|
||||
(seq-sort-by (lambda (tag) (gethash tag tag-priority))
|
||||
#'> tags)))))
|
||||
|
||||
(defun elfeed-summary--compare-sequences (sequence1 sequence2)
|
||||
"Compare SEQUENCE1 and SEQUENCE2.
|
||||
|
||||
Both are lists of symbols."
|
||||
(cond
|
||||
((null sequence1) t)
|
||||
((null sequence2) nil)
|
||||
(t (let ((item1 (symbol-name (car sequence1)))
|
||||
(item2 (symbol-name (car sequence2))))
|
||||
(if (string-equal item1 item2)
|
||||
(elfeed-summary--compare-sequences (cdr sequence1)
|
||||
(cdr sequence2))
|
||||
(string-lessp item1 item2))))))
|
||||
|
||||
(defun elfeed-summary--arrange-sequences-in-tree (sequences)
|
||||
"Arrange SEQUENCES in a tree structure.
|
||||
|
||||
Each element of SEQUENCES is a list of symbols.
|
||||
|
||||
The resulting structure is an alist of tree nodes with the following keys:
|
||||
- `value' - the current node symbol
|
||||
- `children' - child nodes
|
||||
- `sequences' - sequences at this node
|
||||
|
||||
The root of the tree has the value of nil."
|
||||
(let ((ordered-sequences
|
||||
(seq-reverse
|
||||
(seq-sort #'elfeed-summary--compare-sequences sequences)))
|
||||
(tree `(,nil . ((value . ,nil) (children . ,nil) (sequences . ,nil))))
|
||||
current-tree-pos
|
||||
(processed-sequences (make-hash-table :test #'equal)))
|
||||
(dolist (sequence ordered-sequences)
|
||||
(unless (gethash sequence processed-sequences)
|
||||
(setq current-tree-pos tree)
|
||||
(dolist (value sequence)
|
||||
(if-let ((value-in-tree (alist-get value (alist-get 'children current-tree-pos))))
|
||||
(setq current-tree-pos value-in-tree)
|
||||
(setq current-tree-pos
|
||||
(setf
|
||||
(alist-get value (alist-get 'children current-tree-pos))
|
||||
`((value . ,value) (children . ,nil) (sequences . ,nil))))))
|
||||
(push sequence (alist-get 'sequences current-tree-pos))
|
||||
(puthash sequence t processed-sequences)))
|
||||
tree))
|
||||
|
||||
(defun elfeed-summary--auto-tags-group-title (child-tree)
|
||||
"Default function to get the name of an auto-tags group.
|
||||
|
||||
CHILD-TREE is a structure as defined in
|
||||
`elfeed-summary--arrange-sequences-in-tree', with tag lists as
|
||||
sequences."
|
||||
(symbol-name (alist-get 'value child-tree)))
|
||||
|
||||
(defun elfeed-summary--build-tree-auto-tags-recursive
|
||||
(param tree feeds-by-tag-sequence unread-count total-count &optional level)
|
||||
"Recursively create the auto-tags tree.
|
||||
|
||||
PARAM is an `<auto-tags-params>' form as described in
|
||||
`elfeed-summary-settings'. TREE is the result of applying
|
||||
`elfeed-summary--arrange-sequences-in-tree' onto the list of tags of
|
||||
all feeds.
|
||||
|
||||
FEEDS-BY-TAG-SEQUENCE is a hashmap with lists of tags as keys and
|
||||
instances of `elfeed-feed' as values. This is used to figure out
|
||||
feeds in a particular TREE node.
|
||||
|
||||
UNREAD-COUNT and TOTAL-COUNT are hashmaps with feed ids as keys and
|
||||
corresponding numbers of entries as values.
|
||||
|
||||
LEVEL is the current level of recursion, which is 0 by default."
|
||||
(unless level
|
||||
(setq level 0))
|
||||
(let ((max-level (or (alist-get :max-level (cdr param)) 2))
|
||||
(face (when-let (faces (alist-get :faces (cdr param)))
|
||||
(nth (% level (length faces)) faces))))
|
||||
(append
|
||||
;; Just append all the feeds at the current level
|
||||
(cl-loop for sequence in (alist-get 'sequences tree) append
|
||||
(cl-loop for feed in (seq-sort
|
||||
#'elfeed-summary--feed-sort-fn
|
||||
(gethash sequence feeds-by-tag-sequence))
|
||||
collect (elfeed-summary--build-tree-feed
|
||||
feed unread-count total-count)))
|
||||
;; Go deeper if we can
|
||||
(when (< level max-level)
|
||||
(cl-loop
|
||||
for datum in (alist-get 'children tree)
|
||||
for child-tree = (cdr datum) collect
|
||||
`(group . ((params . ((:title
|
||||
. ,(funcall elfeed-summary-auto-tags-group-title-fn
|
||||
child-tree))))
|
||||
(face . ,face)
|
||||
(children . ,(elfeed-summary--build-tree-auto-tags-recursive
|
||||
param child-tree feeds-by-tag-sequence
|
||||
unread-count total-count (1+ level)))))))
|
||||
;; If we can't go deeper, this will just append all the feeds to
|
||||
;; the current level anyway
|
||||
(when (>= level max-level)
|
||||
(cl-loop for datum in (alist-get 'children tree) append
|
||||
(elfeed-summary--build-tree-auto-tags-recursive
|
||||
param (cdr datum) feeds-by-tag-sequence
|
||||
unread-count total-count (1+ level)))))))
|
||||
|
||||
(defun elfeed-summary--build-tree-get-feeds (param misc-feeds)
|
||||
"Get feeds for PARAM.
|
||||
|
||||
PARAM is an alist with the optional `:source' key. The value can be
|
||||
either `(query . <query-params>)' or `:misc' (default).
|
||||
|
||||
MISC-FEEDS is the list of feeds used for `:misc'.
|
||||
|
||||
The result is a list of items like (`<feed>' tag1 tag2 ...), where
|
||||
`<feed>' is an instance of `elfeed-feed'."
|
||||
(let* ((source (alist-get :source (cdr param))))
|
||||
(mapcar
|
||||
(lambda (feed)
|
||||
(cons feed (alist-get (elfeed-feed-id feed) elfeed-feeds
|
||||
nil nil #'equal)))
|
||||
(cond ((or (eq source :misc) (null source))
|
||||
misc-feeds)
|
||||
((and (listp source) (eq (car source) 'query))
|
||||
(elfeed-summary--get-feeds (cdr source)))
|
||||
(t (error "Invalid source: %s" source))))))
|
||||
|
||||
(defun elfeed-summary--build-tree-auto-tags (param unread-count total-count misc-feeds)
|
||||
"Create the auto-tags tree.
|
||||
|
||||
PARAM is a cons cell `(auto-tags . <auto-tags-params>)', where
|
||||
`<auto-tags-params>' is described in `elfeed-summary-settings'.
|
||||
|
||||
UNREAD-COUNT and TOTAL-COUNT are hashmaps with feed ids as keys and
|
||||
corresponding numbers of entries as values.
|
||||
|
||||
MISC-FEEDS is a list of feeds that were not used in PARAMS."
|
||||
(let ((feeds (elfeed-summary--build-tree-get-feeds param misc-feeds))
|
||||
(reorder-tags (not (alist-get :original-order (cdr param)))))
|
||||
(when reorder-tags
|
||||
(setq feeds (elfeed-summary--build-tree-auto-tags-reorder-tags feeds)))
|
||||
(let ((tree (elfeed-summary--arrange-sequences-in-tree
|
||||
(mapcar #'cdr feeds)))
|
||||
(feeds-by-tag-sequence (make-hash-table :test #'equal)))
|
||||
(cl-loop
|
||||
for (feed . sequence) in feeds
|
||||
do (puthash sequence (cons feed (gethash sequence feeds-by-tag-sequence))
|
||||
feeds-by-tag-sequence))
|
||||
(elfeed-summary--build-tree-auto-tags-recursive
|
||||
param tree feeds-by-tag-sequence unread-count total-count))))
|
||||
|
||||
(defun elfeed-summary--build-tree-tag-groups (param unread-count total-count misc-feeds)
|
||||
"Create the tag-groups tree.
|
||||
|
||||
PARAM is a cell of `(tag-groups . <tag-group-params>)', with the
|
||||
`<tag-group-params>' form as defined in `elfeed-summary-settings'.
|
||||
|
||||
UNREAD-COUNT and TOTAL-COUNT are hashmaps with feed ids as keys and
|
||||
corresponding numbers of entries as values.
|
||||
|
||||
MISC-FEEDS is a list of feeds that were not used in PARAMS."
|
||||
(let ((feeds (elfeed-summary--build-tree-get-feeds param misc-feeds))
|
||||
(repeat-feeds (alist-get :repeat-feeds (cdr param)))
|
||||
(face (alist-get :face (cdr param)))
|
||||
(groups (make-hash-table)))
|
||||
(if (not repeat-feeds)
|
||||
(let ((tag-freqs (make-hash-table)))
|
||||
(cl-loop for feed in feeds do
|
||||
(cl-loop for tag in (cdr feed) do
|
||||
(puthash
|
||||
tag (1+ (gethash tag tag-freqs 0))
|
||||
tag-freqs)))
|
||||
(cl-loop for feed in feeds
|
||||
for min-freq-tag = (cl-reduce
|
||||
(lambda (acc tag)
|
||||
(let ((freq (gethash tag tag-freqs)))
|
||||
(if (or (null (cdr acc)) (< freq (cdr acc)))
|
||||
(cons tag freq)
|
||||
acc)))
|
||||
(cdr feed)
|
||||
:initial-value '(nil . nil))
|
||||
when min-freq-tag do
|
||||
(puthash (car min-freq-tag)
|
||||
(cons (car feed) (gethash min-freq-tag groups))
|
||||
groups)))
|
||||
(cl-loop for feed in feeds do
|
||||
(cl-loop for tag in (cdr feed) do
|
||||
(puthash tag (cons (car feed) (gethash tag groups)) groups))))
|
||||
(let ((groups-list (seq-sort-by
|
||||
(lambda (f) (symbol-name (car f)))
|
||||
#'string-lessp
|
||||
(cl-loop for tag being the hash-keys of groups
|
||||
using (hash-values feeds)
|
||||
collect (cons tag feeds)))))
|
||||
(cl-loop for (tag . feeds) in groups-list
|
||||
collect `(group
|
||||
. ((params . ((:title . ,(symbol-name tag))))
|
||||
(face . ,face)
|
||||
(children . ,(mapcar
|
||||
(lambda (feed) (elfeed-summary--build-tree-feed
|
||||
feed unread-count total-count))
|
||||
(seq-sort
|
||||
#'elfeed-summary--feed-sort-fn feeds)))))))))
|
||||
|
||||
(defun elfeed-summary--build-tree (params unread-count total-count misc-feeds)
|
||||
"Recursively create the summary details tree.
|
||||
|
||||
|
|
@ -534,8 +890,15 @@ The resulting form is described in `elfeed-summary--get-data'."
|
|||
append (cl-loop for feed in (elfeed-summary--get-feeds (cdr param))
|
||||
collect (elfeed-summary--build-tree-feed
|
||||
feed unread-count total-count))
|
||||
else if (and (listp param) (eq (car param) 'auto-tags))
|
||||
append (elfeed-summary--build-tree-auto-tags
|
||||
param unread-count total-count misc-feeds)
|
||||
else if (and (listp param) (eq (car param) 'tag-groups))
|
||||
append (elfeed-summary--build-tree-tag-groups
|
||||
param unread-count total-count misc-feeds)
|
||||
else if (eq param :misc)
|
||||
append (cl-loop for feed in misc-feeds
|
||||
append (cl-loop for feed in (seq-sort #'elfeed-summary--feed-sort-fn
|
||||
misc-feeds)
|
||||
collect (elfeed-summary--build-tree-feed
|
||||
feed unread-count total-count))
|
||||
else do (error "Can't parse: %s" (prin1-to-string param))))
|
||||
|
|
@ -549,7 +912,13 @@ PARAMS is a form as described in `elfeed-summary-settings'."
|
|||
append (elfeed-summary--extract-feeds
|
||||
(cdr (assoc :elements (cdr param))))
|
||||
else if (and (listp param) (eq (car param) 'query))
|
||||
append (elfeed-summary--get-feeds (cdr param))))
|
||||
append (elfeed-summary--get-feeds (cdr param))
|
||||
else if (and (listp param)
|
||||
(or (eq (car param) 'auto-tags) (eq (car param) 'tag-groups))
|
||||
(eq (car-safe (alist-get :source (cdr param)))
|
||||
'query))
|
||||
append (elfeed-summary--get-feeds
|
||||
(cdr (alist-get :source (cdr param))))))
|
||||
|
||||
(defun elfeed-summary--ensure ()
|
||||
"Ensure that elfeed database is loaded and feeds are set up."
|
||||
|
|
@ -589,6 +958,7 @@ The return value is a list of alists of the following elements:
|
|||
`elfeed-summary-settings'.
|
||||
- `faces' - list of faces for the search entry.
|
||||
- `unread' - number of unread entries in the search results.
|
||||
- `unread-ids' - ids of unread entries for marking them as read.
|
||||
- `total' - total number of entries in the search results."
|
||||
(let* ((feeds (elfeed-summary--extract-feeds
|
||||
elfeed-summary-settings))
|
||||
|
|
@ -608,7 +978,7 @@ The return value is a list of alists of the following elements:
|
|||
(puthash (elfeed-feed-id feed)
|
||||
(1+ (or (gethash (elfeed-feed-id feed) unread-count) 0))
|
||||
unread-count))
|
||||
(when (> (- (time-convert nil 'integer)
|
||||
(when (> (- (float-time)
|
||||
elfeed-summary-look-back)
|
||||
(elfeed-entry-date entry))
|
||||
(elfeed-db-return)))
|
||||
|
|
@ -634,6 +1004,20 @@ The return value is a list of alists of the following elements:
|
|||
(defvar elfeed-summary--search-mark-read nil
|
||||
"If t, mark the feed as read instead of switching to it.")
|
||||
|
||||
(defun elfeed-summary--magit-section-toggle-workaround (section)
|
||||
"`magit-section-toggle' with a workaround for invisible lines.
|
||||
|
||||
SECTION is an instance of `magit-section'.
|
||||
|
||||
No idea what I'm doing wrong, but this seems to help."
|
||||
(interactive (list (save-excursion
|
||||
(let ((lines (count-lines (point-min) (point-max))))
|
||||
(while (and (invisible-p (point))
|
||||
(< (line-number-at-pos) lines))
|
||||
(forward-line 1)))
|
||||
(magit-current-section))))
|
||||
(magit-section-toggle section))
|
||||
|
||||
(defvar elfeed-summary-mode-map
|
||||
(let ((map (make-sparse-keymap)))
|
||||
(set-keymap-parent map magit-section-mode-map)
|
||||
|
|
@ -642,13 +1026,16 @@ The return value is a list of alists of the following elements:
|
|||
(define-key map (kbd "q") #'elfeed-summary-quit-window)
|
||||
(define-key map (kbd "r") #'elfeed-summary--refresh)
|
||||
(define-key map (kbd "R") #'elfeed-summary-update)
|
||||
(define-key map (kbd "p") #'elfeed-summary-update-at-point)
|
||||
(define-key map (kbd "u") #'elfeed-summary-toggle-only-unread)
|
||||
(define-key map (kbd "U") #'elfeed-summary--action-mark-read)
|
||||
(define-key map (kbd "<tab>") #'elfeed-summary--magit-section-toggle-workaround)
|
||||
(when (fboundp #'evil-define-key*)
|
||||
(evil-define-key* 'normal map
|
||||
(kbd "<tab>") #'magit-section-toggle
|
||||
(kbd "<tab>") #'elfeed-summary--magit-section-toggle-workaround
|
||||
"r" #'elfeed-summary--refresh
|
||||
"R" #'elfeed-summary-update
|
||||
"p" #'elfeed-summary-update-at-point
|
||||
"u" #'elfeed-summary-toggle-only-unread
|
||||
(kbd "RET") #'elfeed-summary--action
|
||||
"M-RET" #'elfeed-summary--action-show-read
|
||||
|
|
@ -671,10 +1058,10 @@ The return value is a list of alists of the following elements:
|
|||
|
||||
If there's a widget at the point, pass the press event to the widget.
|
||||
That should result in the call to
|
||||
`elfeed-summary--search-feed-notify'. Otherwise, if there's a group
|
||||
`elfeed-summary--feed-notify'. Otherwise, if there's a group
|
||||
section, run the corresponding action for the group.
|
||||
|
||||
The behavior of both `elfeed-summary--search-feed-notify' and
|
||||
The behavior of both `elfeed-summary--feed-notify' and
|
||||
`elfeed-summary--open-section' is modified by lexically scoped
|
||||
variables `elfeed-summary--search-show-read' and
|
||||
`elfeed-summary--search-mark-read'.
|
||||
|
|
@ -730,8 +1117,8 @@ If `elfeed-summary-other-window' is t, open elfeed in other window."
|
|||
(enlarge-window (- elfeed-summary-width
|
||||
(window-width))
|
||||
t))))
|
||||
(switch-to-buffer (elfeed-search-buffer)))
|
||||
(unless (eq major-mode 'elfeed-search-mode)
|
||||
(display-buffer (elfeed-search-buffer)))
|
||||
(with-current-buffer (elfeed-search-buffer)
|
||||
(elfeed-search-mode)))
|
||||
|
||||
(defun elfeed-summary--goto-feed (feed show-read)
|
||||
|
|
@ -747,13 +1134,12 @@ items."
|
|||
show-read)
|
||||
(format "+%s " elfeed-summary-unread-tag))
|
||||
"="
|
||||
(rx-to-string (elfeed-feed-id feed) t)
|
||||
)))
|
||||
(rx-to-string (elfeed-feed-id feed) t))))
|
||||
|
||||
(defun elfeed-summary--search-feed-notify (widget &rest _)
|
||||
"A function to run in `:notify' in a feed widget button.
|
||||
(defun elfeed-summary--feed-notify (widget &rest _)
|
||||
"The function to run in `:notify' in a feed widget button.
|
||||
|
||||
WIDGET is an instance of the pressed widget."
|
||||
WIDGET is the instance of the pressed widget."
|
||||
(cond
|
||||
(elfeed-summary--search-mark-read
|
||||
(elfeed-summary--mark-read (list (widget-get widget :feed))))
|
||||
|
|
@ -790,10 +1176,7 @@ SECTION is an instance of `elfeed-summary-group-section'."
|
|||
(format "+%s " elfeed-summary-unread-tag))
|
||||
(mapconcat
|
||||
(lambda (feed)
|
||||
(format "=%s" (replace-regexp-in-string
|
||||
(rx "?" (* not-newline) eos)
|
||||
""
|
||||
(elfeed-feed-id feed))))
|
||||
(format "=%s" (rx-to-string (elfeed-feed-id feed) t)))
|
||||
feeds
|
||||
" ")))))))
|
||||
|
||||
|
|
@ -820,12 +1203,49 @@ descent."
|
|||
'elfeed-summary-count-face))
|
||||
(propertize title 'face (alist-get 'faces data)))))
|
||||
(widget-create 'push-button
|
||||
:notify #'elfeed-summary--search-feed-notify
|
||||
:notify #'elfeed-summary--feed-notify
|
||||
:feed feed
|
||||
:only-read (= 0 (alist-get 'unread data))
|
||||
:button-face 'elfeed-summary-button-face
|
||||
text)
|
||||
(insert "\n")))
|
||||
|
||||
(defun elfeed-summary--mark-read-ids (ids)
|
||||
"Mark elfeed entries with IDS as read."
|
||||
(when (or (not elfeed-summary-confirm-mark-read)
|
||||
(y-or-n-p "Mark all entries in feed as read? "))
|
||||
(let ((ids-hash (make-hash-table)))
|
||||
(dolist (id ids)
|
||||
(puthash id t ids-hash))
|
||||
(with-elfeed-db-visit (entry feed)
|
||||
;; XXX to shut up the byte compiler
|
||||
(ignore feed)
|
||||
(when (and
|
||||
(gethash (elfeed-entry-id entry) ids-hash nil)
|
||||
(member elfeed-summary-unread-tag (elfeed-entry-tags entry)))
|
||||
(setf (elfeed-entry-tags entry)
|
||||
(seq-filter (lambda (tag) (not (eq elfeed-summary-unread-tag tag)))
|
||||
(elfeed-entry-tags entry))))))
|
||||
(elfeed-summary--refresh)))
|
||||
|
||||
(defun elfeed-summary--search-notify (widget &rest _)
|
||||
"The function to run in `:notify' in a search widget button.
|
||||
|
||||
WIDGET is the instance of the pressed widget."
|
||||
(cond
|
||||
(elfeed-summary--search-mark-read
|
||||
(elfeed-summary--mark-read-ids
|
||||
(widget-get widget :unread-ids)))
|
||||
(t (elfeed-summary--open-elfeed)
|
||||
(elfeed-search-set-filter
|
||||
(concat
|
||||
(when (widget-get widget :add-default)
|
||||
elfeed-summary-default-filter)
|
||||
(widget-get widget :filter)
|
||||
(unless (or elfeed-summary--search-show-read
|
||||
(widget-get widget :only-read))
|
||||
(format " +%s " elfeed-summary-unread-tag)))))))
|
||||
|
||||
(defun elfeed-summary--render-search (data _level)
|
||||
"Render a search item for the elfeed summary buffer.
|
||||
|
||||
|
|
@ -850,11 +1270,11 @@ descent."
|
|||
'face
|
||||
(alist-get 'faces data)))))
|
||||
(widget-create 'push-button
|
||||
:notify (lambda (widget &rest _)
|
||||
(elfeed-summary--open-elfeed)
|
||||
(elfeed-search-set-filter
|
||||
(widget-get widget :filter)))
|
||||
:notify #'elfeed-summary--search-notify
|
||||
:filter (alist-get :filter search-data)
|
||||
:only-read (= 0 (alist-get 'unread data))
|
||||
:add-default (alist-get :add-default search-data)
|
||||
:unread-ids (alist-get 'unread-ids data)
|
||||
text)
|
||||
(widget-insert "\n")))
|
||||
|
||||
|
|
@ -1055,11 +1475,32 @@ summary buffer."
|
|||
(with-current-buffer buffer
|
||||
(elfeed-summary--refresh))))
|
||||
|
||||
(defun elfeed-summary-update ()
|
||||
"Update all the feeds in `elfeed-feeds' and the summary buffer."
|
||||
(interactive)
|
||||
(elfeed-log 'info "Elfeed update: %s"
|
||||
(format-time-string "%B %e %Y %H:%M:%S %Z"))
|
||||
(defun elfeed-summary--feed-list ()
|
||||
"Return a flat list version of `elfeed-feeds'.
|
||||
|
||||
This is a modification of `elfeed-feed-list' that takes
|
||||
`elfeed-summary-skip-sync-tag' in account. The return value is a list
|
||||
of string."
|
||||
;; Validate elfeed-feeds and fail early rather than asynchronously later.
|
||||
(dolist (feed elfeed-feeds)
|
||||
(unless (cl-typecase feed
|
||||
(list (and (stringp (car feed))
|
||||
(cl-every #'symbolp (cdr feed))))
|
||||
(string t))
|
||||
;; Chris, package-lint doesn't like your code :P
|
||||
(error "`elfeed-feeds' malformed, bad entry: %S" feed)))
|
||||
(cl-loop for feed in elfeed-feeds
|
||||
when (and (listp feed)
|
||||
(not (memq elfeed-summary-skip-sync-tag
|
||||
(cdr feed))))
|
||||
collect (car feed)
|
||||
else if (not (listp feed)) collect feed))
|
||||
|
||||
(defvar elfeed-summary--update-start-time nil
|
||||
"Time when the current elfeed update started.")
|
||||
|
||||
(defun elfeed-summary--update (feeds)
|
||||
"Update elfeed FEEDS."
|
||||
;; XXX Here's a remarkably dirty solution. This command is meant to
|
||||
;; refresh the elfeed-summary buffer after all the feeds have been
|
||||
;; updated. But elfeed doesn't seem to provide anything to hook
|
||||
|
|
@ -1076,8 +1517,10 @@ summary buffer."
|
|||
(not (or (and (listp hook) (eq (car hook) 'closure))
|
||||
(byte-code-function-p hook))))
|
||||
elfeed-update-hooks))
|
||||
(setq elfeed-summary--update-start-time (time-convert nil 'integer))
|
||||
(let* ((elfeed--inhibit-update-init-hooks t)
|
||||
(remaining-feeds (elfeed-feed-list))
|
||||
(remaining-feeds (seq-copy feeds))
|
||||
(feed-count (length remaining-feeds))
|
||||
(elfeed-update-closure
|
||||
(lambda (url)
|
||||
(message (if (> (elfeed-queue-count-total) 0)
|
||||
|
|
@ -1085,7 +1528,12 @@ summary buffer."
|
|||
(in-process (elfeed-queue-count-active)))
|
||||
(format "%d jobs pending, %d active..."
|
||||
(- total in-process) in-process))
|
||||
"Elfeed update completed"))
|
||||
(format "Elfeed update completed: %s feeds in %s"
|
||||
feed-count
|
||||
(format-seconds
|
||||
"%M, %S" (time-subtract
|
||||
(time-convert nil 'integer)
|
||||
elfeed-summary--update-start-time)))))
|
||||
(setq remaining-feeds
|
||||
(seq-remove
|
||||
(lambda (url-1)
|
||||
|
|
@ -1101,10 +1549,40 @@ summary buffer."
|
|||
elfeed-summary-refresh-on-each-update)
|
||||
(elfeed-summary--refresh-if-exists)))))
|
||||
(add-hook 'elfeed-update-hooks elfeed-update-closure)
|
||||
(mapc #'elfeed-update-feed (elfeed--shuffle (elfeed-feed-list)))
|
||||
(mapc #'elfeed-update-feed (elfeed--shuffle feeds))
|
||||
(run-hooks 'elfeed-update-init-hooks)
|
||||
(elfeed-db-save)))
|
||||
|
||||
(defun elfeed-summary-update ()
|
||||
"Update all feeds in `elfeed-feeds' and the summary buffer."
|
||||
(interactive)
|
||||
(elfeed-log 'info "Elfeed update: %s"
|
||||
(format-time-string "%B %e %Y %T %Z"))
|
||||
(elfeed-summary--update (elfeed-summary--feed-list)))
|
||||
|
||||
(defun elfeed-summary-update-at-point ()
|
||||
"Update `elfeed-summary' feeds at point."
|
||||
(interactive)
|
||||
(let ((feeds (or
|
||||
(when-let (feed (widget-get (get-char-property (point) 'button) :feed))
|
||||
(list (elfeed-feed-url feed)))
|
||||
(when-let (section (magit-current-section))
|
||||
(when (slot-boundp section 'group)
|
||||
(mapcar #'elfeed-feed-url
|
||||
(elfeed-summary--group-extract-feeds
|
||||
(oref section group)))))))
|
||||
(ignore-feeds
|
||||
(cl-loop for feed in elfeed-feeds
|
||||
when (and (listp feed) (memq elfeed-summary-skip-sync-tag
|
||||
(cdr feed)))
|
||||
collect (car feed))))
|
||||
(unless feeds
|
||||
(user-error "No feeds at point"))
|
||||
(setq feeds (seq-difference feeds ignore-feeds))
|
||||
(unless feeds
|
||||
(user-error "All feeds at point are ignored"))
|
||||
(elfeed-summary--update feeds)))
|
||||
|
||||
(defvar elfeed-summary--setup nil
|
||||
"Whether elfeed summary was set up.")
|
||||
|
||||
|
|
@ -1154,7 +1632,7 @@ options."
|
|||
(with-current-buffer buffer
|
||||
(elfeed-summary--render
|
||||
(elfeed-summary--get-data)))
|
||||
(switch-to-buffer buffer)
|
||||
(display-buffer buffer)
|
||||
(goto-char (point-min))))
|
||||
|
||||
(provide 'elfeed-summary)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue