Files
emacs-config/elfeed-config.el
2025-09-05 15:29:31 +02:00

394 lines
13 KiB
EmacsLisp

;;; elfeed-config.el --- Elfeed RSS reader configuration -*- lexical-binding: t -*-
;;; Commentary:
;; Configuration for Elfeed RSS reader with custom faces and filtering functions
;;; Code:
;; Install required packages if not already installed
(use-package elfeed
:ensure t
:bind (("C-x w" . elfeed))
:config
;; Set default search filter to show entries from last 2 weeks
(setq elfeed-search-filter "@2-weeks-ago +unread")
;; Store database in .emacs.d
(setq elfeed-db-directory (expand-file-name "elfeed" user-emacs-directory))
;; Refresh feeds on startup
(setq elfeed-search-remain-on-entry t)
;; Set update interval
(setq elfeed-search-title-max-width 100)
(setq elfeed-search-title-min-width 30)
;; Sorting configuration
(setq elfeed-sort-order 'descending)
(setq elfeed-search-clipboard-type 'CLIPBOARD)
(setq elfeed-search-date-format '("%F %R" 16 :left))
;; Face customization for different tags
(defface elfeed-face-tag-news
'((t :foreground "#8B4513"))
"Face for news-tagged entries")
(defface elfeed-face-tag-tech
'((t :foreground "#4682B4"))
"Face for tech-tagged entries")
(defface elfeed-face-tag-security
'((t :foreground "#DC143C"))
"Face for security-tagged entries")
(defface elfeed-face-tag-programming
'((t :foreground "#228B22"))
"Face for programming-tagged entries")
(defface elfeed-face-tag-opensource
'((t :foreground "#FF8C00"))
"Face for opensource-tagged entries")
;; Apply faces to tags
(setq elfeed-search-face-alist
'((news elfeed-face-tag-news)
(tech elfeed-face-tag-tech)
(security elfeed-face-tag-security)
(programming elfeed-face-tag-programming)
(opensource elfeed-face-tag-opensource))))
(use-package elfeed-org
:ensure t
:after elfeed
:config
;; Load feeds from org file
(setq rmh-elfeed-org-files (list (expand-file-name "elfeed.org" user-emacs-directory)))
(elfeed-org))
;; Optional: Web browser for opening links
(setq browse-url-browser-function 'browse-url-firefox)
;; Keybindings for elfeed
(with-eval-after-load 'elfeed
(define-key elfeed-search-mode-map (kbd "j") 'next-line)
(define-key elfeed-search-mode-map (kbd "k") 'previous-line)
(define-key elfeed-search-mode-map (kbd "m") 'elfeed-search-toggle-all-star)
(define-key elfeed-search-mode-map (kbd "u") 'elfeed-search-toggle-all-unread)
(define-key elfeed-search-mode-map (kbd "U") 'elfeed-update)
(define-key elfeed-search-mode-map (kbd "f") 'elfeed-search-live-filter))
;; Update feeds every hour
(run-at-time 0 (* 60 60) 'elfeed-update)
;; Sorting functions
(defun elfeed-sort-by-date-ascending ()
"Sort elfeed entries by date ascending."
(interactive)
(setq elfeed-sort-order 'ascending)
(elfeed-search-update :force))
(defun elfeed-sort-by-date-descending ()
"Sort elfeed entries by date descending."
(interactive)
(setq elfeed-sort-order 'descending)
(elfeed-search-update :force))
(defun elfeed-sort-by-title ()
"Sort elfeed entries by title."
(interactive)
(setf elfeed-search-sort-function
(lambda (a b)
(string< (elfeed-entry-title a)
(elfeed-entry-title b))))
(elfeed-search-update :force))
(defun elfeed-sort-by-feed ()
"Sort elfeed entries by feed source."
(interactive)
(setf elfeed-search-sort-function
(lambda (a b)
(string< (elfeed-feed-title (elfeed-entry-feed a))
(elfeed-feed-title (elfeed-entry-feed b)))))
(elfeed-search-update :force))
;; Helper function to show only specific categories
(defun elfeed-show-news ()
"Show only news entries."
(interactive)
(elfeed-search-set-filter "@2-weeks-ago +unread +news"))
(defun elfeed-show-tech ()
"Show only tech entries."
(interactive)
(elfeed-search-set-filter "@2-weeks-ago +unread +tech"))
(defun elfeed-show-security ()
"Show only security entries."
(interactive)
(elfeed-search-set-filter "@2-weeks-ago +unread +security"))
(defun elfeed-show-programming ()
"Show only programming entries."
(interactive)
(elfeed-search-set-filter "@2-weeks-ago +unread +programming"))
(defun elfeed-show-opensource ()
"Show only open source entries."
(interactive)
(elfeed-search-set-filter "@2-weeks-ago +unread +opensource"))
;; Location-based filters
(defun elfeed-show-munich ()
"Show only Munich/Bavaria news."
(interactive)
(elfeed-search-set-filter "@2-weeks-ago +unread +munich"))
(defun elfeed-show-bavaria ()
"Show only Bavaria news."
(interactive)
(elfeed-search-set-filter "@2-weeks-ago +unread +bavaria"))
(defun elfeed-show-germany ()
"Show only German news."
(interactive)
(elfeed-search-set-filter "@2-weeks-ago +unread +germany"))
(defun elfeed-show-europe ()
"Show only European/EU news."
(interactive)
(elfeed-search-set-filter "@2-weeks-ago +unread +eu"))
(defun elfeed-show-us ()
"Show only US news."
(interactive)
(elfeed-search-set-filter "@2-weeks-ago +unread +us"))
(defun elfeed-show-world ()
"Show only world news."
(interactive)
(elfeed-search-set-filter "@2-weeks-ago +unread +world"))
;; Language filters
(defun elfeed-show-german-language ()
"Show only German language feeds."
(interactive)
(elfeed-search-set-filter "@2-weeks-ago +unread +de"))
;; Programming language filters
(defun elfeed-show-cpp ()
"Show only C++ entries."
(interactive)
(elfeed-search-set-filter "@2-weeks-ago +unread +cpp"))
(defun elfeed-show-python ()
"Show only Python entries."
(interactive)
(elfeed-search-set-filter "@2-weeks-ago +unread +python"))
(defun elfeed-show-qt ()
"Show only Qt entries."
(interactive)
(elfeed-search-set-filter "@2-weeks-ago +unread +qt"))
;; Combined filters
(defun elfeed-show-today ()
"Show entries from today only."
(interactive)
(elfeed-search-set-filter "@1-day-ago +unread"))
(defun elfeed-show-starred ()
"Show starred entries."
(interactive)
(elfeed-search-set-filter "+star"))
(defun elfeed-show-all ()
"Show all unread entries."
(interactive)
(elfeed-search-set-filter "@2-weeks-ago +unread"))
(defun elfeed-mark-all-read ()
"Mark all entries as read."
(interactive)
(mark-whole-buffer)
(elfeed-search-untag-all-unread))
;; Advanced filter prompt
(defun elfeed-filter-by-source ()
"Filter by specific feed source."
(interactive)
(let* ((feeds (elfeed-feed-list))
(titles (mapcar (lambda (url)
(or (plist-get (elfeed-db-get-feed url) :title)
url))
feeds))
(selected (completing-read "Select feed: " titles)))
(elfeed-search-set-filter
(format "@2-weeks-ago +unread =%s" selected))))
;; Custom search function
(defun elfeed-search-custom ()
"Prompt for a custom search filter."
(interactive)
(let ((filter (read-string "Enter filter: " elfeed-search-filter)))
(elfeed-search-set-filter filter)))
;; Article reading functions
(defun elfeed-show-entry-in-eww ()
"Open the current elfeed entry in eww browser."
(interactive)
(let ((entry (elfeed-search-selected :single)))
(when entry
(eww (elfeed-entry-link entry))
(add-hook 'eww-after-render-hook 'eww-readable nil t))))
(defun elfeed-search-eww-open (&optional use-generic-p)
"Open the current elfeed entry in eww.
If USE-GENERIC-P is non-nil, use eww-readable after loading."
(interactive "P")
(let ((entries (elfeed-search-selected)))
(cl-loop for entry in entries
do (elfeed-untag entry 'unread)
when (elfeed-entry-link entry)
do (eww (elfeed-entry-link entry)))
(when use-generic-p
(add-hook 'eww-after-render-hook 'eww-readable nil t))
(unless (use-region-p) (forward-line))
(elfeed-search-update-entry)))
(defun elfeed-show-eww-open (&optional use-generic-p)
"Open the current elfeed show entry in eww.
If USE-GENERIC-P is non-nil, use eww-readable after loading."
(interactive "P")
(let ((link (elfeed-entry-link elfeed-show-entry)))
(when link
(eww link)
(when use-generic-p
(add-hook 'eww-after-render-hook 'eww-readable nil t)))))
;; Fetch and display article content in elfeed-show buffer
(defun elfeed-show-refresh-mail-style ()
"Refresh the current elfeed entry, fetching full content and displaying it."
(interactive)
(let ((link (elfeed-entry-link elfeed-show-entry)))
(when link
(message "Fetching full article content...")
(url-retrieve
link
(lambda (status)
(if (plist-get status :error)
(message "Error fetching article: %s" (plist-get status :error))
(let ((html (buffer-string))
(inhibit-read-only t))
(with-current-buffer (get-buffer "*elfeed-entry*")
(erase-buffer)
(insert (format "Title: %s\n" (elfeed-entry-title elfeed-show-entry)))
(insert (format "Feed: %s\n" (elfeed-feed-title (elfeed-entry-feed elfeed-show-entry))))
(insert (format "Date: %s\n" (format-time-string "%Y-%m-%d %H:%M" (elfeed-entry-date elfeed-show-entry))))
(insert (format "Link: %s\n\n" link))
(insert "--- Full Article ---\n\n")
(let ((shr-width (- (window-width) 5))
(shr-max-image-proportion 0.7))
(shr-render-region (point) (point-max)))
(goto-char (point-min))))))))))
;; Enhanced readable mode for elfeed
(defun elfeed-show-readable ()
"Make the current elfeed entry more readable by extracting main content."
(interactive)
(let ((inhibit-read-only t))
(save-excursion
(goto-char (point-min))
(when (search-forward "\n\n" nil t)
(let ((shr-width (min 80 (- (window-width) 5)))
(shr-max-image-proportion 0.6)
(shr-use-fonts nil))
(shr-render-region (point) (point-max)))))
(text-scale-increase 1)
(olivetti-mode 1)))
;; Toggle between summary and full article
(defvar-local elfeed-show-full-article-p nil
"Whether the full article is currently displayed.")
(defun elfeed-show-toggle-full-article ()
"Toggle between entry summary and full article content."
(interactive)
(if elfeed-show-full-article-p
(progn
(elfeed-show-refresh)
(setq-local elfeed-show-full-article-p nil)
(message "Showing summary"))
(let ((link (elfeed-entry-link elfeed-show-entry)))
(when link
(message "Fetching full article...")
(url-retrieve
link
(lambda (status)
(if (plist-get status :error)
(message "Error fetching article: %s" (plist-get status :error))
(let ((html (buffer-substring (point) (point-max)))
(inhibit-read-only t))
(with-current-buffer (get-buffer "*elfeed-entry*")
(let ((pos (point)))
(erase-buffer)
(elfeed-show-refresh)
(goto-char (point-max))
(insert "\n\n--- Full Article ---\n\n")
(let ((start (point)))
(insert html)
(shr-render-region start (point-max))
(goto-char pos))
(setq-local elfeed-show-full-article-p t)
(message "Showing full article")))))))))))
;; Open in external browser as fallback
(defun elfeed-open-in-browser ()
"Open current entry in external browser."
(interactive)
(let ((entry (if (eq major-mode 'elfeed-show-mode)
elfeed-show-entry
(elfeed-search-selected :single))))
(when entry
(browse-url (elfeed-entry-link entry)))))
;; Bind keys for article viewing
(with-eval-after-load 'elfeed
;; In search mode
(define-key elfeed-search-mode-map (kbd "E") 'elfeed-search-eww-open)
(define-key elfeed-search-mode-map (kbd "B") 'elfeed-open-in-browser)
;; In show mode
(define-key elfeed-show-mode-map (kbd "E") 'elfeed-show-eww-open)
(define-key elfeed-show-mode-map (kbd "R") 'elfeed-show-readable)
(define-key elfeed-show-mode-map (kbd "F") 'elfeed-show-toggle-full-article)
(define-key elfeed-show-mode-map (kbd "B") 'elfeed-open-in-browser))
;; Disable line numbers in elfeed buffers
(add-hook 'elfeed-show-mode-hook
(lambda ()
(display-line-numbers-mode -1)
(setq-local display-line-numbers nil)))
(add-hook 'elfeed-search-mode-hook
(lambda ()
(display-line-numbers-mode -1)
(setq-local display-line-numbers nil)))
;; Disable line numbers in EWW
(add-hook 'eww-mode-hook
(lambda ()
(display-line-numbers-mode -1)
(setq-local display-line-numbers nil)))
;; Additional function to force disable line numbers in elfeed
(defun elfeed-disable-line-numbers ()
"Forcefully disable line numbers in elfeed buffers."
(interactive)
(when (or (eq major-mode 'elfeed-search-mode)
(eq major-mode 'elfeed-show-mode))
(display-line-numbers-mode -1)
(setq-local display-line-numbers nil)
(message "Line numbers disabled in elfeed")))
(provide 'elfeed-config)
;;; elfeed-config.el ends here