User Interface suggestions
Digging elfeed. Coming from feedly (via Web client and the superior (in most ways) GReader app on Android), previously "Google Reader" (sniff)).
Below are a few personal prefs you might appreciate:
I think elfeed would be better to display date as the last column, and for it to be expressed as aging, in relative time, (i.e. 2d for 2 days ago, 3h for 3 hours ago, etc).
When I read/scan feeds/titles I want know when the feed I am reading before I know the Title of the entry. For this reason, I would prefer to see the feed title before the article Title. In fact, I don't need to see it repeating (at least if sorted by title). My eyes dance right and left reading the current layout. My preferred layout, which GREADER provides, is to have the feed source appear once only, in a different face than the items for all the article from the feed. It takes a little more vertical space, but makes scanning MUCH faster.
Also, I would by default not display the unread tag
So, in summary, my ideal layout would be something like:
Feed1 info .................................................... ......... # unread items | # read items | # tagged .. title1 ................................................................ 2d
.. title2 ................................................................ 3d Feed2 info ............................................................... # unread items | # read items | # tagged .. title3 ................................................................ 1h kittenz .. title4 ................................................................ 1d kittenz
etc
I think this layout allows improved top-down left->right scanning.
If having sections or reorganizing columns like this is infeasible , simply suppressing repeated feed names would reduce much visual clutter and allow the eyes to dance much less.
Thanks for elfeed. Much appreciated!
Thanks for sharing your ideas. However, I prefer the listing the way it currently works. You can get some control over the listing with elfeed-search-print-entry-function, but this only works on a line by line basis and won't allow you to group.
Fortunately you could realistically write your own custom front-end to Elfeed. In fact, elfeed-search has nothing special going for it other than being launched with "M-x elfeed". It's practically a separate package. It uses hooks to get notifications about database changes, and you could do exactly the same thing with your own front-end. The filter stuff from elfeed-search should be re-usable, too.
@malcook You could customize this function, which inserts separators where the date changes:
(cl-defun ap/elfeed-search-add-separators (&key (min-group-size 2))
"Insert overlay spacers where the current date changes.
If no group has at least MIN-GROUP-SIZE items, no spacers will be
inserted. "
;; TODO: Use column-specific functions so that, e.g. date column could be grouped by month/year
(cl-labels ((count-date-items (date)
(cl-loop for entry in elfeed-search-entries
when (equal date (elfeed-search-format-date (elfeed-entry-date entry)))
count it))
(insert-date (date &key count)
(ov (line-beginning-position) (line-beginning-position)
'before-string (propertize (format "\n%s (%s)\n" date count)
'face 'elfeed-search-date-face)
'type 'date-separator))
(entry-date (offset)
(when-let ((entry (nth offset elfeed-search-entries)))
(elfeed-search-format-date (elfeed-entry-date entry)))))
(ov-clear)
(save-excursion
(goto-char (point-min))
(cl-loop with largest-group-size = 1
with offset = (- 1 elfeed-search--offset) ; 1 is first line
with prev-data = (entry-date offset)
initially do (insert-date prev-data
:count (count-date-items prev-data))
while (not (eobp))
do (progn
(forward-line 1)
(incf offset))
for current-data = (entry-date offset)
if (not (equal current-data prev-data))
do (progn
(insert-date current-data
:count (count-date-items current-data))
(setq prev-data current-data))
else do (incf largest-group-size)
finally do (when (< largest-group-size min-group-size)
(ov-clear))))))

@alphapapa I really like your setup. Could you be more explicit about how you get elfeed to look like this? How do you add your function to elfeed?
@timmli I use elfeed-goodies, so I replace the drawing function like this:
(advice-add #'elfeed-goodies/entry-line-draw :override #'ap/elfeed-goodies/entry-line-draw)
@alphapapa Sorry, I still don't get it. Where does ap/elfeed-goodies/entry-line-draw come from?
Oh, that's in my config:
(defun ap/elfeed-goodies/entry-line-draw (entry)
"My version of this function. Prints ENTRY to the buffer."
(cl-flet ((add-faces (str &rest faces)
(dolist (face faces str)
(add-face-text-property 0 (length str)
face 'append str)))
(tags (entry)
(seq-difference (--map (substring-no-properties (symbol-name it))
(elfeed-entry-tags entry))
'("unread" "starred"))))
(let* ( ;; Choose color and faces first
;; See https://www.reddit.com/r/emacs/comments/7a976a/face_applied_to_result_of_symbolname_becomes/
(site (pocket-reader--url-domain (elfeed-entry-link entry)))
(hash (rainbow-identifiers--hash-function site))
(site-face (rainbow-identifiers-cie-l*a*b*-choose-face hash))
(title-faces (elfeed-search--faces (elfeed-entry-tags entry)))
;; Feed
(feed (elfeed-entry-feed entry))
(feed-title (when feed
(or (elfeed-meta feed :title) (elfeed-feed-title feed))))
(feed-width elfeed-goodies/feed-source-column-width)
(feed-column (elfeed-format-column feed-title feed-width :left))
(feed-column (apply #'add-faces feed-column site-face title-faces))
;; Tags before title (so title can use the width of the tags column for this item)
(tags (tags entry))
(tags-str (s-join "," tags))
;; Use raw tag list to check for starred
(starred-p (member 'starred (elfeed-entry-tags entry)))
(tags-width (min (length tags-str)
elfeed-goodies/tag-column-width))
(tag-column (elfeed-format-column tags-str tags-width :right))
(tag-column (apply #'add-faces tag-column site-face title-faces))
;; Title
(title (or (elfeed-meta entry :title) (elfeed-entry-title entry) ""))
(title-width (- (window-width) feed-width tags-width 4))
(title-column (elfeed-format-column (truncate-string-to-width title title-width nil nil 'ellipsis) title-width :left))
(title-column (apply #'add-faces title-column site-face title-faces)))
(insert feed-column " ")
(insert (if starred-p
(propertize "*"
'face 'pocket-reader-favorite-star)
" ")
" ")
(insert (propertize title-column 'kbd-help title) " ")
(insert tag-column))))
@alphapapa Thanks! One more question: How am I supposed to include your function ap/elfeed-search-add-separators there?
Just add it to elfeed-search-update-hook, like:
(add-hook 'elfeed-search-update-hook #'ap/elfeed-search-add-separators)
Just add it to
elfeed-search-update-hook, like:(add-hook 'elfeed-search-update-hook #'ap/elfeed-search-add-separators)
hy i stumbled upon this and try to use it but its not working as shown in screenshot only first date is shown and after that all feeed come under same date
i have this in doom emacs config
(after! elfeed-goodies
(add-hook 'elfeed-search-update-hook #'ap/elfeed-search-add-separators)
(cl-defun ap/elfeed-search-add-separators (&key (min-group-size 2))
"Insert overlay spacers where the current date changes.
If no group has at least MIN-GROUP-SIZE items, no spacers will be
inserted. "
;; TODO: Use column-specific functions so that, e.g. date column could be grouped by month/year
(cl-labels ((count-date-items (date)
(cl-loop for entry in elfeed-search-entries
when (equal date (elfeed-search-format-date (elfeed-entry-date entry)))
count it))
(insert-date (date &key count)
(ov (line-beginning-position) (line-beginning-position)
'before-string (propertize (format "\n%s (%s)\n" date count)
'face 'elfeed-search-date-face)
'type 'date-separator))
(entry-date (offset)
(when-let ((entry (nth offset elfeed-search-entries)))
(elfeed-search-format-date (elfeed-entry-date entry)))))
(ov-clear)
(save-excursion
(goto-char (point-min))
(cl-loop with largest-group-size = 1
with offset = (- 1 elfeed-search--offset) ; 1 is first line
with prev-data = (entry-date offset)
initially do (insert-date prev-data
:count (count-date-items prev-data))
while (not (eobp))
do (progn
(forward-line 1)
(incf offset))
for current-data = (entry-date offset)
if (not (equal current-data prev-data))
do (progn
(insert-date current-data
:count (count-date-items current-data))
(setq prev-data current-data))
else do (incf largest-group-size)
finally do (when (< largest-group-size min-group-size)
(ov-clear))))))
)
i found the issue i just have to use cl-incf instead of incf