394 lines
13 KiB
EmacsLisp
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 |