From 2d21f978c10c5ee80e41cb1d4281ab0bb31be9e6 Mon Sep 17 00:00:00 2001 From: SqrtMinusOne Date: Thu, 17 Mar 2022 21:31:07 +0300 Subject: [PATCH] feat: add searches to settings --- elfeed-summary.el | 144 +++++++++++++++++++++++++++++++++------------- 1 file changed, 105 insertions(+), 39 deletions(-) diff --git a/elfeed-summary.el b/elfeed-summary.el index 666c0cd..1d7b76d 100644 --- a/elfeed-summary.el +++ b/elfeed-summary.el @@ -27,10 +27,13 @@ ;; TODO ;;; Code: +(require 'cl-lib) (require 'elfeed) (require 'elfeed-db) -(require 'widget) +(require 'elfeed-search) +(require 'magit-section) (require 'seq) +(require 'widget) (define-widget 'elfeed-summary-query 'lazy "A query to extract a subset of elfeed feeds." @@ -61,56 +64,76 @@ (const :tag "OR" or) (repeat elfeed-summary-query)))) -(define-widget 'elfeed-summary-group 'lazy - "A group of `elfeed-summary-query'" +(define-widget 'elfeed-summary-setting-elements 'lazy + "Type widget for `elfeed-summary-settings'" :offset 4 - :tag "Group" + :tag "Settings list" :type '(repeat (choice (cons :tag "Group" (const group) - (list :tag "Group params" - (cons - (const :tag "Title" :title) - (string :tag "Title")) - (cons - (const :tag "Sort function" :sort-fn) - (choice - function - (const :tag "None" nil))) - (cons - (const :tag "Elements" :elements) - elfeed-summary-group))) - elfeed-summary-query))) + (repeat :tag "Group params" + (choice + (cons + (const :tag "Title" :title) + (string :tag "Title")) + (cons + (const :tag "Sort function" :sort-fn) + (choice + function + (const :tag "None" nil))) + (cons + (const :tag "Elements" :elements) + elfeed-summary-setting-elements)))) + (cons :tag "Query" + (const query) + elfeed-summary-query) + (cons :tag "Search" + (const search) + (repeat :tag "Search params" + (choice + (cons :tag "Filter" + (const :tag "Filter" :filter) + (string :tag "Filter string")) + (cons :tag "Title" + (const :tag "Title" :title) + (string :tag "Filter title"))))) + (const :tag "Misc feeds" :misc)))) (defgroup elfeed-summary () "Feed summary inteface for elfeed." :group 'elfeed) -(defcustom elfeed-summary-settings '((group (:title . "All feeds") - (:sort-fn . string-lessp) - (:elements :all))) +(defcustom elfeed-summary-settings + '((group (:title . "All feeds") + (:sort-fn . string-lessp) + (:elements (query . :all))) + (group (:title . "Searches") + (:elements (search + (:filter . "@7-days-ago +unread") + (:title . "Unread entries this week")) + (search + (:filter . "@6-months-ago emacs") + (:title . "Something about Emacs"))))) "Elfeed summary buffer settings. This is a list of these possible items: -- group -- query +- Group `(group . )' + Groups are used to group elements under collapsible sections. +- Query `(query . )' + Query extracts a subset of elfeed feeds based on the given criteria. + Each found feed will be represented as a line. +- Search `(search . )' + Elfeed search, as defined by `elfeed-search-set-filter'. - a few special forms -Groups are used to group queries under collapsible sections. - -A group is a cons cell like (group . ), where params are an -alist with the following attributes: +`' is an alist with the following keys: - `:title' (mandatory) - `:elements' (mandatory) - also a list of groups and queries - `:sort-fn' - function used to sort titles of feeds, found by queries in `:elements'. E.g. `string-greaterp' for alphabetical order. -Query is a form that extract a subset of elfeed feeds based on -some criteria. In the summary buffer, each feed found by the -query will be represented as a line. - -Query can be: +`' can be: - A symbol of a tag. A feed will be matched if it has that tag. - `:all'. Will match anything. @@ -125,22 +148,27 @@ Query can be: Match if any of the conditions 1, 2, ..., n match. - `(not )' -Feed tags are taken from `elfeed-feeds'. +Feed tags for query are taken from `elfeed-feeds'. Query examples: - `(emacs lisp)' Return all feeds that have either \"emacs\" or \"lisp\" tags. - `(and emacs lisp)' Return all feeds that have both \"emacs\" and \"lisp\" tags. -- `(and (title . \"Emacs\") (not planets)) +- `(and (title . \"Emacs\") (not planets))' Return all feeds that have \"Emacs\" in their title and don't have the \"planets\" tag. +`` is an alist with the following keys: +- `:filter' (mandatory) filter string, as defined by + `elfeed-search-set-filter' +- `:title' (mandatory) title + Available special forms: - `:misc' - print out feeds, not found by any query above. - `:unread' - a special feed of all unread entries." :group 'elfeed-summary - :type 'elfeed-summary-group) + :type 'elfeed-summary-setting-elements) (defcustom elfeed-summary-look-back (* 60 60 24 180) "TODO" @@ -157,6 +185,8 @@ Available special forms: "Default face for the elfeed-summary group." :group 'elfeed-summary) +;;; Logic + (cl-defun elfeed-summary--match-tag (query &key tags title url author title-meta) "Check if attributes of elfeed feed match QUERY. @@ -261,7 +291,8 @@ QUERY is described in `elfeed-summary-settings'." if (and (listp param) (eq (car param) 'group)) append (elfeed-summary--extract-feeds (cdr (assoc :elements (cdr param)))) - else append (elfeed-summary--get-feeds param))) + else if (and (listp param) (eq (car param) 'query)) + append (elfeed-summary--get-feeds (cdr param)))) (defun elfeed-summary--build-tree-feed (feed unread-count total-count) (let* ((unread (or (gethash (elfeed-feed-id feed) unread-count) 0)) @@ -284,6 +315,33 @@ QUERY is described in `elfeed-summary-settings'." (unread . ,unread) (total . ,unread)))) +(defun elfeed-summary--build-search (search) + "TODO + +Implented the same way as `elfeed-search--update-list'." + (let* ((filter (elfeed-search-parse-filter (alist-get :filter search))) + (head (list nil)) + (tail head) + (count 0)) + (if elfeed-search-compile-filter + ;; Force lexical bindings regardless of the current + ;; buffer-local value. Lexical scope uses the faster + ;; stack-ref opcode instead of the traditional varref opcode. + (let ((lexical-binding t) + (func (byte-compile (elfeed-search-compile-filter filter)))) + (with-elfeed-db-visit (entry feed) + (when (funcall func entry feed count) + (setf (cdr tail) (list entry) + tail (cdr tail) + count (1+ count))))) + (with-elfeed-db-visit (entry feed) + (when (elfeed-search-filter filter entry feed count) + (setf (cdr tail) (list entry) + tail (cdr tail) + count (1+ count))))) + `((search . ,search) + (count . ,count)))) + (defun elfeed-summary--build-tree (params unread-count total-count misc-feeds) (cl-loop for param in params if (and (listp param) (eq (car param) 'group)) @@ -291,16 +349,19 @@ QUERY is described in `elfeed-summary-settings'." (children . ,(elfeed-summary--build-tree (cdr (assoc :elements (cdr param))) unread-count total-count misc-feeds))) + else if (and (listp param) (eq (car param) 'search)) + collect (elfeed-summary--build-search param) + else if (and (listp param) (eq (car param) 'query)) + 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 (eq param :misc) append (cl-loop for feed in misc-feeds collect (elfeed-summary--build-tree-feed feed unread-count total-count)) else if (eq param :unread) collect (elfeed-summary--build-tree-unread unread-count) - else - append (cl-loop for feed in (elfeed-summary--get-feeds param) - collect (elfeed-summary--build-tree-feed - feed unread-count total-count)))) + else do (error "Can't parse: %s" (prin1-to-string param)))) (defun elfeed-summary--get-data () (let* ((feeds (elfeed-summary--extract-feeds @@ -328,5 +389,10 @@ QUERY is described in `elfeed-summary-settings'." (elfeed-summary--build-tree elfeed-summary-settings unread-count total-count misc-feeds))) +;;; View + +(defun elfeed-summary--render (tree) + "TODO") + (provide 'elfeed-summary) ;;; elfeed-summary.el ends here