;;; config.el -*- lexical-binding: t; -*- ;; DOC: https://git.v0.io/hlissner/doom-emacs/src/commit/a680a4c96d2b57fcb75635bd1a918e1235e72890/docs/api.org ;; DOC: https://github.com/daviwil/emacs-from-scratch ;; Line numbers are pretty slow all around. The performance boost of ;; disabling them outweighs the utility of always keeping them on. (setq display-line-numbers-type nil) ;; Focus new window after splitting (setq evil-split-window-below t evil-vsplit-window-right t ) ;; Undo (setq undo-tree-auto-save-history t undo-tree-history-directory-alist '(("." . "~/.cache/emacs/undo")) ) ;; Fonts (setq doom-font-increment 1 doom-font-size 9 ;doom-font-name "DejaVu Sans Mono" doom-font-name "Hack" ;doom-font-name "Unifont" doom-font (font-spec :family doom-font-name :size doom-font-size :antialias t) doom-unicode-font (font-spec :family "Symbola" :antialias t) doom-variable-pitch-font (font-spec :family doom-font-name :antialias t) ivy-posframe-font (font-spec :family doom-font-name :antialias t) ) (setq x-underline-at-descent-line t) ;; Dashboard ;;(setq +doom-dashboard-banner-file (expand-file-name "banner.png" doom-private-dir)) (defun bury-messages-and-scratch () (mapcar #'bury-buffer '("*Messages*" "*scratch*"))) (add-hook 'emacs-startup-hook #'bury-messages-and-scratch) ;; Formatting (setq haskell-stylish-on-save nil) (setq lsp-haskell-formatting-provider "fourmolu") (setq +format-on-save-enabled-modes '(not emacs-lisp-mode ; works well enough without it sql-mode ; sqlformat is broken tex-mode ; latexindent is broken latex-mode ; latexindent is broken bibtex-mode ; is broken ess-r-mode ; styler takes forever ) ) ;; Theming (setq doom-theme 'doom-vibrant) (custom-theme-set-faces! '(doom-vibrant) '(default :background "black" :foreground "white") '(hl-line :background "black") '(cursor :background "DarkOrange" :foreground "black") ;; Haskell colors '(font-lock-string-face :foreground "magenta") '(highlight-numbers-number :foreground "DeepSkyBlue" :bold t) '(font-lock-comment-face :foreground "cyan3") '(font-lock-doc-face :foreground "cyan1") ;'(haskell-constructor-face :foreground "#c4451d") ;'(haskell-keyword-face :foreground "#af005f") ;'(haskell-keyword-face :foreground "RoyalBlue") ;'(haskell-keyword-face :foreground "#44385f") ;'(haskell-keyword-face :foreground "#906238" :bold t) ;'(haskell-keyword-face :foreground "green4" :bold t) '(haskell-keyword-face :foreground "LightGreen" :bold nil) ;'(haskell-pragma-face :foreground "#2aa198") '(haskell-pragma-face :foreground "#8f4e8b") ;'(haskell-type-face :foreground "#9e358f") '(haskell-operator-face :foreground "yellow" :bold nil) ;'(haskell-type-face :foreground "SaddleBrown") ;'(haskell-constructor-face :foreground "darkgoldenrod4") '(haskell-definition-face :foreground "white" :bold nil) '(swiper-line-face :background "grey15") '(swiper-match-face-1 :foreground "grey50" :background "black") ; what is between matches '(swiper-match-face-2 :foreground "black" :background "yellow2") '(swiper-match-face-3 :foreground "black" :background "yellow3") '(swiper-match-face-4 :foreground "black" :background "yellow4") '(swiper-background-match-face-2 :foreground "black" :background "yellow2") '(swiper-background-match-face-3 :foreground "black" :background "yellow3") '(swiper-background-match-face-4 :foreground "black" :background "yellow4") '(evil-ex-lazy-highlight :foreground "black" :background "yellow2") '(region :foreground "black" :background "yellow") ; selected area '(doom-modeline-buffer-modified :foreground "orange") '(git-gutter-fr:added :foreground "green4" :background "black") '(git-gutter-fr:deleted :foreground "red4" :background "black") '(git-gutter-fr:modified :foreground "orange4" :background "black") '(flycheck-fringe-error :foreground "red") '(flycheck-fringe-warning :foreground "orange") '(flycheck-fringe-info :foreground "purple") '(flycheck-info :underline '(:color "SkyBlue4" :style wave)) '(flycheck-warning :underline '(:color "orange" :style wave)) '(flycheck-error :underline '(:color "red" :style wave)) '(warning :foreground "orange") '(error :foreground "red") '(ivy-minibuffer-match-face-1 :foreground "grey50" :background "black") ; what is between matches '(ivy-minibuffer-match-face-2 :foreground "black" :background "yellow2") '(ivy-minibuffer-match-face-3 :foreground "black" :background "yellow3") '(ivy-minibuffer-match-face-4 :foreground "black" :background "yellow4") ; Violet ;'(haskell-operator-face :foreground "yellow3") ;'(haskell-type-face :foreground "#5a4e82") ;'(haskell-constructor-face :foreground "#8a4f88") ;'(haskell-constructor-face :foreground "#8a4c87") '(haskell-constructor-face :foreground "white" :bold t) ; Almost the same as haskell-constructor-face ; because that pattern does not match on type-contexts correctly '(haskell-type-face :foreground "white" :bold t) '(lsp-face-highlight-read :background "green10") '(lsp-face-highlight-write :background "green10") '(mode-line :background "gray20") '(mode-line-inactive :background "gray5") '(solaire-default-face :background "gray10") '(solaire-mode-line-face :background "gray20") '(solaire-mode-line-inactive-face :background "gray5") '(doom-modeline-project-dir :foreground "DeepSkyBlue" :bold t) ;'(magit-diff-context-highlight :foreground "white" :background "black") '(magit-diff-added-highlight :foreground "green") '(magit-diff-removed-highlight :foreground "red") ;;'(font-lock-comment-face ((t (:foreground "cyan" :italic t :slant oblique)))) ;;'(font-lock-comment-delimiter-face ((t (:foreground "cyan")))) ;;'(fringe ((t (:foreground "red" :background "black")))) ;;'(whitespace-indentation ((t (:foreground "gray50" :bold t)))) ;;'(whitespace-space ((t (:foreground "black" :bold t)))) ;;'(whitespace-tab ((t (:foreground "gray50" :bold t)))) ;;'(whitespace-trailing ((t (:foreground "red" :bold t)))) ;;'(font-lock-builtin-face ((t (:foreground "yellow" :weight bold)))) ;;'(font-lock-keyword-face ((t (:foreground "yellow" :weight bold)))) ;;'(font-lock-string-face ((t (:foreground "magenta")))) ;;'(font-lock-variable-name-face ((t (:foreground "SpringGreen3" :weight bold)))) ;;'(nix-antiquote-face ((t (:foreground "blue")))) ;;'(mode-line ((t (:background "midnight blue" :box (:line-width 1 :color "blue") :height 1.0)))) ;;'(mode-line-inactive ((t (:background "grey30" :foreground "white" :box (:line-width 1 :color "grey30") :weight light :height 1.0)))) ;;'(mode-line ((t (:family "Noto Sans" :height 0.9)))) ;;'(mode-line-inactive ((t (:family "Noto Sans" :height 0.9)))) ;;'(solaire-mode-line-inactive-face ((t (:background "gray10")))) ) ;; polymode (use-package! polymode) (define-hostmode poly-haskell-hostmode :mode 'haskell-mode) ;; Support org tables in Haskell files (defun pm--org-table-tail-matcher (ahead) (when (re-search-forward "^[^|]" nil t ahead) (cons (match-beginning 0) (match-end 0)))) (define-innermode poly-haskell-org-table-innermode :mode 'org-mode :head-matcher "^|.*" :tail-matcher #'pm--org-table-tail-matcher :head-mode 'body :tail-mode 'host) (define-polymode poly-haskell-mode :hostmode 'poly-haskell-hostmode :innermodes '(poly-haskell-org-table-innermode)) ;; Language Server Protocol (setq ;+lsp-prompt-to-install-server 'quiet ;lsp-enable-file-watchers nil ;lsp-enable-indentation nil ;lsp-enable-on-type-formatting nil ;lsp-enable-symbol-highlighting nil lsp-ui-doc-enable nil ; slow ;lsp-ui-sideline-enable nil ; not anymore useful than flycheck ;lsp-ui-peek-enable t ;lsp-ui-peek-always-show nil ;lsp-ui-flycheck-enable t ;lsp-ui-flycheck-live-reporting t ;; Disable help mouse-overs for mode-line segments (i.e. :help-echo text). ;; They're generally unhelpful and only add confusing visual clutter. mode-line-default-help-echo nil show-help-function nil global-prettify-symbols-mode nil prettify-symbols-mode -1 lsp-treemacs-errors-position-params `((side . right)) lsp-log-io nil ; Only for debugging ) ;; The modeline is not useful to me in the popup window. ;; It looks much nicer to hide it. (remove-hook 'emacs-everywhere-init-hooks #'hide-mode-line-mode) ;;(remove-hook 'window-setup-hook #'doom-init-theme-h) ;;(add-hook 'after-init-hook #'doom-init-theme-h 'append) ;;(delq! t custom-theme-load-path) (setq auto-save-default t ; Nobody likes to loose work, I certainly don't confirm-kill-emacs nil make-backup-files nil password-cache-expiry nil ; I can trust my computers ... can't I? scroll-margin 10 ;; scroll-preserve-screen-position 'always ; Don't have `point' jump around truncate-string-ellipsis "…" ; Unicode ellispis are nicer than "...", and also save /precious/ space kill-whole-line t ; Make Ctrl-K remove the whole line, instead of just emptying it undo-limit 80000000 ; Raise undo-limit to 80Mb x-stretch-cursor nil ; Disorienting ) ;; Debugging ;(setq debug-on-quit t) ;; Haskell (setq haskell-process-log nil haskell-interactive-popup-errors nil ) (add-hook 'haskell-mode-hook (lambda () (set (make-local-variable 'company-backends) (append '((company-capf company-dabbrev-code)) company-backends)))) (define-hostmode poly-haskell-hostmode :mode 'haskell-mode) ;; Layout (setq split-height-threshold nil split-width-threshold 160 ) (after! flycheck ; flycheck-error-list-buffer (set-popup-rule! (rx bos "*Flycheck errors*" eos) :side 'right :size 0.45) (add-hook 'flycheck-error-list-mode-hook (lambda () (setq tabulated-list-sort-key (list "Level")))) ) ;(add-hook 'flycheck-error-list-after-refresh-hook ; (lambda () ; (with-selected-window (flycheck-get-error-list-window t) ; (fit-window-to-buffer (flycheck-get-error-list-window t) 30)))) ;; Completing (add-hook 'after-init-hook 'global-company-mode) ;(setq company-idle-delay nil) ;(use-package! ivy ; :defer t ; :bind ; (:map ivy-minibuffer-map ; ("RET" . 'ivy-alt-done) ; ("TAB" . 'ivy-alt-done)) ; :init ; (setq ivy-extra-directories nil ; ) ; ) ;(add-hook! 'org-mode-hook #'mixed-pitch-mode) ;(add-hook! 'org-mode-hook #'solaire-mode) ;(setq mixed-pitch-variable-pitch-cursor nil) ;(add-hook! org-mode :append ; #'visual-line-mode ; #'variable-pitch-mode) (setq doom-modeline-enable-word-count nil) (map! "C-x b" #'counsel-buffer-or-recentf "C-x C-b" #'counsel-switch-buffer) (defun recentf-track-visited-file (_prev _curr) (and buffer-file-name (recentf-add-file buffer-file-name))) (after! recentf (add-hook 'switch-buffer-functions #'recentf-track-visited-file)) ;; Org ; DOC: https://rgoswami.me/posts/org-note-workflow/ ; ToStudy: https://github.com/abo-abo/orca ; ToStudy: https://github.com/weirdNox/org-noter ; ToStudy: https://github.com/bastibe/org-journal ; ToStudy: https://orgmode.org/manual/Org-Crypt.html ; DOC: http://www.wouterspekkink.org/academia/writing/tool/doom-emacs/2021/02/27/writing-academic-papers-with-org-mode.html ; DOC: https://jonathanabennett.github.io/blog/2019/05/29/writing-academic-papers-with-org-mode/ ; ToStudy: https://github.com/org-roam/org-roam-ui ; ToStudy: https://blog.tecosaur.com/tmio/2021-07-31-citations.html ; ToStudy: https://github.com/jkitchin/org-ref-cite (setq org-directory "~/documents/notes") (setq org-noter-notes-search-path '("~/notes/path/")) (after! org (setq org-log-done t org-log-into-drawer t org-special-ctrl-a/e t org-special-ctrl-k t ;org-hide-emphasis-markers t ) (add-to-list 'org-capture-templates '("e" "English" table-line ; key, name, type (file "english/vocabulary.org") ; target "|%^{English}|%^{Français}|%^{Registre|Courant|Soutenu|Familier}|" :kill-buffer t)) (add-to-list 'org-capture-templates '("x" "Expense" plain (file "compta.org") "\n%(org-read-date) * send %^{Send to} %^{For why}\nExpense:Gifts %^{Amount}\n %^{Currency|EUR|USD|JPY}\n Assets:%^{Account||Personal|Home}")) ;; Firefox and Chrome ;(add-to-list 'org-capture-templates ; `("P" "Protocol" entry ; key, name, type ; (file+headline +org-capture-notes-file "Inbox") ; target ; "* %^{Title}\nSource: %u, %c\n #+BEGIN_QUOTE\n%i\n#+END_QUOTE\n\n\n%?" ; :prepend t ; properties ; :kill-buffer t)) ;(add-to-list 'org-capture-templates ; `("L" "Protocol Link" entry ; (file+headline +org-capture-notes-file "Inbox") ; "* %? [[%:link][%(transform-square-brackets-to-round-ones \"%:description\")]]\n" ; :prepend t ; :kill-buffer t)) ; ) ) (map! :after counsel :map org-mode-map "C-c l l h" #'counsel-org-link) (after! counsel (setq counsel-outline-display-style 'title)) ;(after! org-id ; ;; Do not create ID if a CUSTOM_ID exists ; (setq org-id-link-to-org-use-id 'create-if-interactive-and-no-custom-id) ; ) ; ;(after! org-agenda ; ;; (setq org-agenda-prefix-format ; ;; '((agenda . " %i %-12:c%?-12t% s") ; ;; ;; Indent todo items by level to show nesting ; ;; (todo . " %i %-12:c%l") ; ;; (tags . " %i %-12:c") ; ;; (search . " %i %-12:c"))) ; (setq org-agenda-include-diary t) ; ) ; ;(use-package! org-roam ; :defer t ; :hook ((after-init . org-roam-mode)) ; :custom ; (setq org-roam-directory "~/documents/notes/org-roam") ; ; ;; Let's set up some org-roam capture templates ; (setq org-roam-capture-templates ; '(("d" "default" plain (function org-roam--capture-get-point) ; "%?" ; :file-name "%<%Y-%m-%d-%H%M%S>-${slug}" ; :head "#+title: ${title}\n" ; :unnarrowed t))) ; ; ;; And now we set necessary variables for org-roam-dailies ; (setq org-roam-dailies-capture-templates ; '(("d" "default" entry ; #'org-roam-capture--get-point ; "* %?" ; :file-name "daily/%<%Y-%m-%d>" ; :head "#+title: %<%Y-%m-%d>\n\n"))) ; ) ; ;(use-package! org-ref ; :after org-roam ; :config ; (setq reftex-default-bibliography bibliography-files ; org-ref-default-bibliography bibliography-files ; org-ref-bibliography-notes "~/documents/notes/org-roam/bibnotes.org" ;; Is this even needed? ; org-ref-notes-directory "~/documents/notes/org-roam" ; org-ref-notes-function 'orb-edit-notes ; ;org-ref-get-pdf-filename-function 'org-ref-get-pdf-filename-helm-bibtex) ; ) ; ;(use-package! bibtex-completion ; :defer t ; :config ; (setq bibtex-completion-notes-path "~/documents/notes/org-roam/" ; bibtex-completion-pdf-field "file" ; bibtex-completion-bibliography bibliography-files ; ) ;) ; ;(defvar orb-title-format "${author-or-editor-abbrev} (${date}). ${title}." ; "Format of the title to use for `orb-templates'.") ; ;(use-package! org-roam-bibtex ; :after org-roam ; :hook (org-roam-mode . org-roam-bibtex-mode) ; :bind (:map org-mode-map ; (("C-c n a" . orb-note-actions))) ; :config ; (setq orb-preformat-keywords ; '(("citekey" . "=key=") "title" "date" "author-or-editor-abbrev") ; orb-templates ; `(("r" "ref" plain ; (function org-roam-capture--get-point) ; "" ; :file-name "${citekey}" ; :head ,(s-join "\n" ; (list ; (concat "#+TITLE: " orb-title-format) ; "#+ROAM_KEY: ${ref}" ; "" ; "- tags :: " ; "" ; "* Notes" ; "")) ; :unnarrowed t) ; ("n" "ref + noter" plain ; (function org-roam-capture--get-point) ; "" ; :file-name "${citekey}" ; :head ,(s-join "\n" ; (list ; (concat "#+TITLE: " orb-title-format) ; "#+ROAM_KEY: ${ref}" ; "" ; "- tags :: " ; "" ; "* Annotations :noter:" ; ":PROPERTIES:" ; ":NOTER_DOCUMENT: %(orb-process-file-field \"${citekey}\")" ; ":NOTER_PAGE:" ; ":END:" ; "")))))) ; ;(map! :after pdf-tools ; :map pdf-view-mode-map ; :gn "q" (lambda () ; (interactive) ; (if (bound-and-true-p org-noter-doc-mode) ; (org-noter-kill-session) ; (kill-current-buffer))) ; ) ; ;(use-package! org-noter ; :after (:any org pdf-view) ; :config ; (setq org-noter-notes-search-path '("~/documents/notes/org-roam") ; org-noter-always-create-frame nil ; ) ; ) ;(setq lsp-lens-mode t) (setq doom-modeline-height 1) (setq doom-modeline-lsp t) ;(setq doom-modeline-minor-modes t) ;(setq lsp-modeline-diagnostics-enable t) ;; Indenting (setq-default tab-width 2) (setq indent-tabs-mode nil) ;; Searching (global-superword-mode t) ; https://emacs.stackexchange.com/questions/9583/how-to-treat-underscore-as-part-of-the-word (modify-syntax-entry ?_ "w") (defun swiper-all/search-word-at-point () (interactive) (swiper-all (format "%s" (thing-at-point 'word)))) ; Highlight without jumping to the next match ; See: https://emacs.stackexchange.com/questions/52411/evil-star-visualstar-without-moving-the-cursor (defun search-word-at-point () (interactive) (evil-search (progn (evil-ex-search-word-forward) (evil-ex-search-previous) ))) (defun search-unbounded-word-at-point () (interactive) (evil-search (progn (evil-ex-search-unbounded-word-forward) (evil-ex-search-unbounded-word-previous) ))) (setq ivy-case-fold-search-default nil ; case-sensitive search by default ) (after! evil ;(general-define-key "C-s" '(counsel-grep-or-swiper :which-key "search")) ;(general-define-key :keymaps '(swiper-map) "C-u" '(lambda () (interactive) (kill-line 0))) ;(general-nvmap "/" '(counsel-grep-or-swiper :which-key "search in buffer")) (evil-define-key 'normal global-map (kbd "/") 'swiper (kbd "C-f") 'swiper-all (kbd "C-/") 'swiper-all/search-word-at-point (kbd "*") 'search-word-at-point (kbd "g*") 'search-unbounded-word-at-point ) ) ;; Edition (after! evil (setq evil-want-fine-undo t ) (customize-set-variable 'evil-want-Y-yank-to-eol nil) (evil-define-key 'normal global-map (kbd "K") 'evil-join) (evil-define-key 'insert global-map (kbd "C-u") '(lambda () (interactive) (kill-line 0))) (evil-define-key 'normal global-map (kbd "SPC g d") 'git-gutter:popup-hunk) ) (setq +vc-gutter-default-style nil ) (customize-set-variable 'git-gutter:update-interval 2) ;(customize-set-variable 'git-gutter:window-width 2) (after! git-gutter-fringe (require 'fringe-helper) ; Narrower vertical bar, matching the one in the "+" of git-gutter-fr:added (fringe-helper-define 'git-gutter-fr:modified nil "...XX..." "...XX..." ".XXXXXX." ".XXXXXX." "...XX..." "...XX..." ".XXXXXX." ".XXXXXX.") ; Narrower horizontql bar, matching the one in the "+" of git-gutter-fr:added (fringe-helper-define 'git-gutter-fr:deleted nil "........" "........" "........" ".XXXXXX." ".XXXXXX." "........" "........" "........") ) ;; Folding (defun set-selective-display/at-point (&optional level) "Fold text indented same of more than the cursor. If level is set, set the indent level to LEVEL. If 'selective-display' is already set to LEVEL, calling it again will unset 'selective-display' by setting it to 0." (interactive "P") (if (eq selective-display (1+ (current-column))) (set-selective-display 0) (set-selective-display (or level (1+ (current-column)))))) (after! evil (evil-define-key 'normal global-map (kbd "C-d") 'set-selective-display/at-point) ) ;; Navigation ;; DOC: https://github.com/noctuid/evil-guide (after! evil (setq evil-cross-lines t evil-move-cursor-back nil evil-search-wrap nil evil-want-C-i-jump nil ) ;; Switching buffers (evil-define-key 'motion 'global (kbd "") 'evil-prev-buffer ; Does not work without the angles ('<' and '>') (kbd "C-k") 'evil-next-buffer ) ;(define-key evil-normal-state-map [tab] 'evil-prev-buffer) ;; Jumping (evil-define-key 'motion 'global (kbd "C-u") 'better-jumper-jump-backward (kbd "C-o") 'better-jumper-jump-forward ) ;; Moving with ijkl (instead of hjkl) (evil-define-key '(motion normal visual) 'global (kbd "i") 'evil-previous-visual-line (kbd "j") 'evil-backward-char (kbd "k") 'evil-next-visual-line (kbd "h") 'evil-insert ) ;; Switching windows with SPC-w + ijkl (instead of hjkl) (define-key evil-window-map "i" 'evil-window-up) (define-key evil-window-map "I" 'evil-window-move-very-top) (define-key evil-window-map "j" 'evil-window-left) (define-key evil-window-map "J" 'evil-window-move-far-left) (define-key evil-window-map "k" 'evil-window-down) (define-key evil-window-map "K" 'evil-window-move-very-bottom) ;(define-key evil-emacs-state-map "\C-w" 'evil-window-map) ;;(evil-define-key 'window global-map "l" 'evil-window-right) ;;(evil-define-key 'window global-map "L" 'evil-window-move-far-right) ;; ijkl in Dired ;;(evil-define-key 'normal dired-mode-map "i" 'evil-previous-line) ;; Errors (evil-define-key 'motion 'global (kbd "C-j") 'previous-error (kbd "C-l") 'next-error ) ;(bind-key "C-j" #'previous-error) ;(bind-key "C-l" #'next-error) ) (after! auto-yasnippet (evil-define-key '(motion normal) 'global (kbd "C-") 'evil-switch-to-windows-last-buffer ) ) (after! (evil magit evil-collection-magit) (evil-define-key '(motion normal) magit-mode-map (kbd "C-i") 'evil-prev-buffer (kbd "i") 'evil-previous-visual-line (kbd "k") 'evil-next-visual-line (kbd "C-k") 'evil-next-buffer (kbd "j") 'magit-section-backward (kbd "l") 'magit-section-forward ) ) (after! (evil magit git-rebase) (evil-define-key '(motion normal) git-rebase-mode-map (kbd "C-i") 'evil-prev-buffer (kbd "i") 'evil-previous-visual-line (kbd "k") 'evil-next-visual-line (kbd "C-k") 'evil-next-buffer (kbd "j") 'evil-backward-char ) ) ;;(eval-after-load 'evil ; to run after evil-integration.el ;; '(progn ;; (define-key Buffer-menu-mode-map "\C-i" 'evil-prev-buffer) ;; (define-key Buffer-menu-mode-map "\C-k" 'evil-next-buffer) ;; (define-key messages-buffer-mode-map "\C-i" 'evil-prev-buffer) ;; (define-key messages-buffer-mode-map "\C-k" 'evil-next-buffer) ;; (define-key completion-list-mode-map "\C-i" 'evil-prev-buffer) ;; (define-key completion-list-mode-map "\C-k" 'evil-next-buffer) ;; ;(define-key custom-theme-mode-map "\C-i" 'evil-prev-buffer) ;; ;(define-key custom-theme-mode-map "\C-k" 'evil-next-buffer) ;; ) ;; ) ;;(with-eval-after-load 'evil-maps ;; (define-key evil-motion-state-map (kbd "TAB") nil)) ;; Feed reader ; See also: Managing ArXiv RSS Feeds in Emacs https://cundy.me/post/elfeed/ (setq elfeed-enable-web-interface t url-queue-timeout 30 ) ;(global-set-key (kbd "C-x w") 'elfeed) (defun elfeed-mark-all-as-read () (interactive) (mark-whole-buffer) (elfeed-search-untag-all-unread)) ; Functions to support syncing .elfeed between machines ; makes sure elfeed reads index from disk before launching (defun elfeed-load-db-and-open () "Wrapper to load the elfeed db from disk before opening" (interactive) (elfeed-db-load) (elfeed) (elfeed-search-update--force)) ; Write to disk when quiting (defun elfeed-save-db-and-bury () "Wrapper to save the elfeed db to disk before burying buffer" (interactive) (elfeed-db-save) (quit-window)) (defun elfeed-toggle-star () "Toggle star" (interactive) (elfeed-search-tag-all '*) ) (after! (evil elfeed evil-collection-elfeed) (evil-define-key 'motion elfeed-search-mode-map "m" #'elfeed-toggle-star) ) (defun elfeed-org-update-incremental () "Automatically update the feeds from feeds.org when updated" (setq my-elfeed-org-last (or (and (boundp 'elfeed-feeds) elfeed-feeds) nil)) (elfeed) (setq my-elfeed-org-current elfeed-feeds) (let ((elfeed-feeds (-difference my-elfeed-org-current my-elfeed-org-last))) ;; You don't need the line below if you don't want to see a message about what feeds are being updated (message "%s" elfeed-feeds) (mapc #'elfeed-update-feed (elfeed--shuffle (elfeed-feed-list)))) (setq elfeed-feeds my-elfeed-org-current))