New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feature] more status in the mode line #111

Closed
groks opened this Issue Nov 23, 2015 · 8 comments

Comments

Projects
None yet
3 participants
@groks
Contributor

groks commented Nov 23, 2015

The first line at the top of the search buffer currently shows the last updated date of the db. I'd like to know:

  • current search filter
  • how many entries, how many unread, from how many feeds
  • db last update

There's also the issue of where to put it.

  • could leave it as is
  • could add a real emacs header to the buffer
  • could add to the mode line

Perhaps one day elfeed's search mode might derive from tabulated-list-mode, like the package list view built-in to Emacs, and the header will become column headers? So here's a quick sketch of how to add the data to the mode line:

(defface my-elfeed-mode-line-search-filter-face
  '((t :inherit mode-line-buffer-id))
  "Face for showing the current Elfeed search filter."
  :group 'elfeed)

(defface my-elfeed-mode-line-db-update-face
  '((t :slant oblique))
  "Face for showing the date and time the database was last updated."
  :group 'elfeed)

(defvar my-elfeed-mode-line-format
  '(:eval (if (equal major-mode 'elfeed-search-mode)
              (concat (propertize (format "\"%s\"" elfeed-search-filter) 'face 'my-elfeed-mode-line-search-filter-face)
                      " "
                      (my-elfeed-entry-count)
                      " "
                      (propertize
                       (format "updated: %s "
                               (format-time-string "%a %H:%M"
                                                   (seconds-to-time (elfeed-db-last-update))))
                       'face 'my-elfeed-mode-line-db-update-face))
            ""))
  "A mode-line format to show *elfeed-search* status in the mode-line.")

(defun my-elfeed-entry-count ()
  "Count the number of entries and feeds being currently displayed."
  (let ((n-entries 0)
        (n-entries-unread 0)
        (n-feeds 0)
        (feeds (make-hash-table :test 'equal)))
    (cl-loop for entry in elfeed-search-entries
             for feed = (elfeed-entry-feed entry)
             for url = (elfeed-feed-url feed)
             do (progn
                  (puthash url nil feeds)
                  (setq n-entries (1+  n-entries))
                  (when (memq 'unread (elfeed-entry-tags entry))
                    (setq n-entries-unread (1+ n-entries-unread)))))
    (maphash (lambda (_key _value)
               (setq n-feeds (1+ n-feeds))) feeds)
    (format "%d/%d:%d" n-entries-unread n-entries n-feeds)))

(my-eval-after-load elfeed
  (add-to-list 'mode-line-misc-info my-elfeed-mode-line-format :append))

It works well in conjunction with the toggles in #109 - you can see how the number of feeds goes up/down as you toggle unread/date.

@skeeto

This comment has been minimized.

Show comment
Hide comment
@skeeto

skeeto Nov 25, 2015

Owner

I really like the idea of putting the current search filter in there.
Sometimes I forget what filter I'm using. # unread isn't a bad idea
either. Just need to figured out how to fit it all.

And, holy crap, I didn't know about header-line-format! I should have
been using this all along. I'm playing around with it now and will
probably be switching to it.

I'm not a fan of putting important information in the modeline. It's
already overcrowded.

Owner

skeeto commented Nov 25, 2015

I really like the idea of putting the current search filter in there.
Sometimes I forget what filter I'm using. # unread isn't a bad idea
either. Just need to figured out how to fit it all.

And, holy crap, I didn't know about header-line-format! I should have
been using this all along. I'm playing around with it now and will
probably be switching to it.

I'm not a fan of putting important information in the modeline. It's
already overcrowded.

skeeto added a commit that referenced this issue Nov 25, 2015

Switch to an actual Emacs header (#111).
This replaces Elfeed's pseudo-header with a real Emacs header via
header-line-format.
@skeeto

This comment has been minimized.

Show comment
Hide comment
@skeeto

skeeto Nov 25, 2015

Owner

Can you give 986294f a spin? It's off in a feature branch just in case
there's some major breakage I missed.

Owner

skeeto commented Nov 25, 2015

Can you give 986294f a spin? It's off in a feature branch just in case
there's some major breakage I missed.

@groks

This comment has been minimized.

Show comment
Hide comment
@groks

groks Nov 27, 2015

Contributor

Looks good!

I like the way you've moved the search filter to the right so that the header doesn't jump around as as you modify the filter.

Some suggestions:

Maybe more info could be added about the db status. Currently:

Last update 2015-11-26 15:45:35 +0000

...new:

100:9/10 downloaded 2015-11-26 15:45

One feed has failed to update. A tool-tip could explain: 100 entries download from 9 of 10 feeds, 1 feed failed, and clicking this area would update just those 10 feeds, which in this branch would be all feeds but in #109 a subset. This last batch of feeds updated could also be re-run with C-u C-u G. No more problem with feeds missing due to having all entries filtered out. (Maybe re-run just the feeds with errors with C-u C-u C-u G or is that a step too far...?)

Maybe the 90/100:10 status count and search filter should be fontified the same way. There's really two concepts: the database and the sub-set on display. Colouring the two halves would emphasise that. A tool-tip could explain: filtering 90 unread of 100 entries from 10 feeds.

Contributor

groks commented Nov 27, 2015

Looks good!

I like the way you've moved the search filter to the right so that the header doesn't jump around as as you modify the filter.

Some suggestions:

Maybe more info could be added about the db status. Currently:

Last update 2015-11-26 15:45:35 +0000

...new:

100:9/10 downloaded 2015-11-26 15:45

One feed has failed to update. A tool-tip could explain: 100 entries download from 9 of 10 feeds, 1 feed failed, and clicking this area would update just those 10 feeds, which in this branch would be all feeds but in #109 a subset. This last batch of feeds updated could also be re-run with C-u C-u G. No more problem with feeds missing due to having all entries filtered out. (Maybe re-run just the feeds with errors with C-u C-u C-u G or is that a step too far...?)

Maybe the 90/100:10 status count and search filter should be fontified the same way. There's really two concepts: the database and the sub-set on display. Colouring the two halves would emphasise that. A tool-tip could explain: filtering 90 unread of 100 entries from 10 feeds.

@algernon

This comment has been minimized.

Show comment
Hide comment
@algernon

algernon Nov 27, 2015

Contributor

It would be really nice if we could override the header function. Something along these lines:

(defvar elfeed-search-header-function #'elfeed-search--header
  "Function that returns the string to be used for the Elfeed header.")

And then use (funcall elfeed-search-header-function) wherever you're calling elfeed-search--header directly. This would allow users to change what appears there, the order, alignment, add tooltips, and so on.

I'll pull up a branch with this change, and possibly with some alternative header examples.

Contributor

algernon commented Nov 27, 2015

It would be really nice if we could override the header function. Something along these lines:

(defvar elfeed-search-header-function #'elfeed-search--header
  "Function that returns the string to be used for the Elfeed header.")

And then use (funcall elfeed-search-header-function) wherever you're calling elfeed-search--header directly. This would allow users to change what appears there, the order, alignment, add tooltips, and so on.

I'll pull up a branch with this change, and possibly with some alternative header examples.

algernon added a commit to algernon/elfeed that referenced this issue Nov 27, 2015

Switch to an actual Emacs header (#111).
This replaces Elfeed's pseudo-header with a real Emacs header via
header-line-format.

algernon added a commit to algernon/elfeed that referenced this issue Nov 27, 2015

elfeed-search: Make the header function customisable
See #111.

Signed-off-by: Gergely Nagy <algernon@cloudera.com>

algernon added a commit to algernon/elfeed that referenced this issue Nov 27, 2015

elfeed-search: Make the header function customisable
See #111.

Signed-off-by: Gergely Nagy <algernon@cloudera.com>
@algernon

This comment has been minimized.

Show comment
Hide comment
@algernon

algernon Nov 27, 2015

Contributor

Still playing with it, but once the function is easy to override (see e22607a), it's fairly easy to bend it to one's will.

Contributor

algernon commented Nov 27, 2015

Still playing with it, but once the function is easy to override (see e22607a), it's fairly easy to bend it to one's will.

@algernon

This comment has been minimized.

Show comment
Hide comment
@algernon

algernon Nov 27, 2015

Contributor

Managed to come up with this so far:

Screenshot

Code used:

(defun elfeed-search--stats ()
  "Count the number of entries and feeds being currently displayed."
  (if (and elfeed-search-filter-active elfeed-search-filter-overflowing)
      (list 0 0 0)
    (cl-loop with feeds = (make-hash-table :test 'equal)
             for entry in elfeed-search-entries
             for feed = (elfeed-entry-feed entry)
             for url = (elfeed-feed-url feed)
             count entry into entry-count
             count (elfeed-tagged-p 'unread entry) into unread-count
             do (puthash url t feeds)
             finally
             (cl-return
              (list unread-count entry-count (hash-table-count feeds))))))

(defun my-elfeed-search--header ()
  "Returns the string to be used as the Elfeed header."
  (let* ((separator-left (intern (format "powerline-%s-%s"
                                         (powerline-current-separator)
                                         (car powerline-default-separator-dir))))
         (separator-right (intern (format "powerline-%s-%s"
                                          (powerline-current-separator)
                                          (cdr powerline-default-separator-dir)))))
    (cond
     ((zerop (elfeed-db-last-update))
      (elfeed-search--intro-header))
     (url-queue
      (let* ((total (length url-queue))
             (in-process (cl-count-if #'url-queue-buffer url-queue))
             (center (list (funcall separator-left 'mode-line 'powerline-active2)
                           (powerline-raw (format " %d feeds pending, %d in process ... " total in-process)
                                          'powerline-active2)
                           (funcall separator-right 'powerline-active2 'mode-line))))
        (concat (powerline-fill-center nil (/ (powerline-width center) 2.0))
                (powerline-render center))))
     ((let* ((db-time (seconds-to-time (elfeed-db-last-update)))
             (update (format-time-string "%Y-%m-%d %H:%M:%S %z" db-time))
             (stats (elfeed-search--stats))
             (search-filter (cond
                             (elfeed-search-filter-active
                              "")
                             (elfeed-search-filter
                              elfeed-search-filter)
                             ("")))
             (lhs (list
                   (powerline-raw (concat search-filter " ") 'powerline-active1 'l)
                   (funcall separator-right 'powerline-active1 'mode-line)))
             (center (list
                      (funcall separator-left 'mode-line 'powerline-active2)
                      (destructuring-bind (unread entry-count feed-count) stats
                        (propertize (format " %d/%d:%d " unread entry-count feed-count)
                                    'face 'powerline-active2
                                    'help-echo (format "%d unread entries\n%d total entries\n%d feeds"
                                                       unread entry-count feed-count)))
                      (funcall separator-right 'powerline-active2 'mode-line)))
             (rhs (list
                   (funcall separator-left 'mode-line 'powerline-active1)
                   (powerline-raw (concat " " update) 'powerline-active1 'r))))

        (concat (powerline-render lhs)
                (powerline-fill-center nil (/ (powerline-width center) 2.0))
                (powerline-render center)
                (powerline-fill nil (powerline-width rhs))
                (powerline-render rhs)))))))

(setq elfeed-search-header-function #'my-elfeed-search--header)
Contributor

algernon commented Nov 27, 2015

Managed to come up with this so far:

Screenshot

Code used:

(defun elfeed-search--stats ()
  "Count the number of entries and feeds being currently displayed."
  (if (and elfeed-search-filter-active elfeed-search-filter-overflowing)
      (list 0 0 0)
    (cl-loop with feeds = (make-hash-table :test 'equal)
             for entry in elfeed-search-entries
             for feed = (elfeed-entry-feed entry)
             for url = (elfeed-feed-url feed)
             count entry into entry-count
             count (elfeed-tagged-p 'unread entry) into unread-count
             do (puthash url t feeds)
             finally
             (cl-return
              (list unread-count entry-count (hash-table-count feeds))))))

(defun my-elfeed-search--header ()
  "Returns the string to be used as the Elfeed header."
  (let* ((separator-left (intern (format "powerline-%s-%s"
                                         (powerline-current-separator)
                                         (car powerline-default-separator-dir))))
         (separator-right (intern (format "powerline-%s-%s"
                                          (powerline-current-separator)
                                          (cdr powerline-default-separator-dir)))))
    (cond
     ((zerop (elfeed-db-last-update))
      (elfeed-search--intro-header))
     (url-queue
      (let* ((total (length url-queue))
             (in-process (cl-count-if #'url-queue-buffer url-queue))
             (center (list (funcall separator-left 'mode-line 'powerline-active2)
                           (powerline-raw (format " %d feeds pending, %d in process ... " total in-process)
                                          'powerline-active2)
                           (funcall separator-right 'powerline-active2 'mode-line))))
        (concat (powerline-fill-center nil (/ (powerline-width center) 2.0))
                (powerline-render center))))
     ((let* ((db-time (seconds-to-time (elfeed-db-last-update)))
             (update (format-time-string "%Y-%m-%d %H:%M:%S %z" db-time))
             (stats (elfeed-search--stats))
             (search-filter (cond
                             (elfeed-search-filter-active
                              "")
                             (elfeed-search-filter
                              elfeed-search-filter)
                             ("")))
             (lhs (list
                   (powerline-raw (concat search-filter " ") 'powerline-active1 'l)
                   (funcall separator-right 'powerline-active1 'mode-line)))
             (center (list
                      (funcall separator-left 'mode-line 'powerline-active2)
                      (destructuring-bind (unread entry-count feed-count) stats
                        (propertize (format " %d/%d:%d " unread entry-count feed-count)
                                    'face 'powerline-active2
                                    'help-echo (format "%d unread entries\n%d total entries\n%d feeds"
                                                       unread entry-count feed-count)))
                      (funcall separator-right 'powerline-active2 'mode-line)))
             (rhs (list
                   (funcall separator-left 'mode-line 'powerline-active1)
                   (powerline-raw (concat " " update) 'powerline-active1 'r))))

        (concat (powerline-render lhs)
                (powerline-fill-center nil (/ (powerline-width center) 2.0))
                (powerline-render center)
                (powerline-fill nil (powerline-width rhs))
                (powerline-render rhs)))))))

(setq elfeed-search-header-function #'my-elfeed-search--header)

skeeto added a commit that referenced this issue Nov 30, 2015

elfeed-search: Make the header function customisable
See #111.

Signed-off-by: Gergely Nagy <algernon@cloudera.com>
@skeeto

This comment has been minimized.

Show comment
Hide comment
@skeeto

skeeto Nov 30, 2015

Owner

@algernon
That's really slick! I cherry-picked your customization function so anyone can make a fancy header.

@groks
Good point about leaving off the seconds and time zone. Those aren't really important enough. In the old header, on Windows it would be really long ("Eastern Time Zone"), so I was only thinking about making it shorter. Eliminating it didn't occur to me.

In order to display the failure information in the header Elfeed actually needs to capture it first (#113). Right now it's just dumped in the message buffer. I'm going to merge what we've got so far into master.

Owner

skeeto commented Nov 30, 2015

@algernon
That's really slick! I cherry-picked your customization function so anyone can make a fancy header.

@groks
Good point about leaving off the seconds and time zone. Those aren't really important enough. In the old header, on Windows it would be really long ("Eastern Time Zone"), so I was only thinking about making it shorter. Eliminating it didn't occur to me.

In order to display the failure information in the header Elfeed actually needs to capture it first (#113). Right now it's just dumped in the message buffer. I'm going to merge what we've got so far into master.

@skeeto

This comment has been minimized.

Show comment
Hide comment
@skeeto

skeeto May 8, 2018

Owner

Closing since this is old and has basically been addressed by all the mentioned commits.

Owner

skeeto commented May 8, 2018

Closing since this is old and has basically been addressed by all the mentioned commits.

@skeeto skeeto closed this May 8, 2018

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment