User:Accraze/Tools/Doom Emacs/Configuration
Doom Emacs is a major part of my workflow on WMF projects. This is a guide and literate config for my setup. The auto-generated code and org files for these configs are stored in my dotfiles repo on wmf gitlab: https://gitlab.wikimedia.org/accraze/dotfiles/-/tree/main/.doom.d
Init
This file is used to load specific modules built for Doom Emacs. This does a bunch of heavy lifting in configuring different modes in emacs, you can also add flags to customize the module installations.
;;; init.el -*- lexical-binding: t; -*-
;;
;; DO NOT EDIT THIS FILE DIRECTLY
;; This is a file generated from a literate programing source file.
;; You should make any changes there and regenerate it from Emacs org-mode
;; using org-babel-tangle (C-c C-v t)
;;
;; This file controls what Doom modules are enabled and what order they load in.
;; Press 'K' on a module to view its documentation, and 'gd' to browse its directory.
;;
(doom! :input
;;chinese
;;japanese
;;layout ; auie,ctsrnm is the superior home row
:completion
(company +childrame) ; the ultimate code completion backend
;;helm ; the *other* search engine for love and life
;;ido ; the other *other* search engine...
(ivy +prescient -childframe
-fuzzy +icons) ; a search engine for love and life
:ui
deft ; notational velocity for Emacs
doom ; what makes DOOM look the way it does
doom-dashboard ; a nifty splash screen for Emacs
doom-quit ; DOOM quit-message prompts when you quit Emacs
;;(emoji +unicode) ; 🙂
;;fill-column ; a `fill-column' indicator
hl-todo ; highlight TODO/FIXME/NOTE/DEPRECATED/HACK/REVIEW
;;hydra
;;indent-guides ; highlighted indent columns
;;ligatures ; ligatures and symbols to make your code pretty again
;;minimap ; show a map of the code on the side
modeline ; snazzy, Atom-inspired modeline, plus API
;;nav-flash ; blink cursor line after big motions
neotree ; a project drawer, like NERDTree for vim
ophints ; highlight the region an operation acts on
(popup +defaults) ; tame sudden yet inevitable temporary windows
;;tabs ; a tab bar for Emacs
treemacs ; a project drawer, like neotree but cooler
;;unicode ; extended unicode support for various languages
vc-gutter ; vcs diff in the fringe
vi-tilde-fringe ; fringe tildes to mark beyond EOB
;;window-select ; visually switch windows
workspaces ; tab emulation, persistence & separate workspaces
;;zen ; distraction-free coding or writing
:editor
(evil +everywhere); come to the dark side, we have cookies
file-templates ; auto-snippets for empty files
fold ; (nigh) universal code folding
(format +onsave) ; automated prettiness
;;god ; run Emacs commands without modifier keys
;;lispy ; vim for lisp, for people who don't like vim
multiple-cursors ; editing in many places at once
;;objed ; text object editing for the innocent
;;parinfer ; turn lisp into python, sort of
;;rotate-text ; cycle region at point between text candidates
snippets ; my elves. They type so I don't have to
;;word-wrap ; soft wrapping with language-aware indent
:emacs
dired ; making dired pretty [functional]
electric ; smarter, keyword-based electric-indent
;;ibuffer ; interactive buffer management
undo ; persistent, smarter undo for your inevitable mistakes
vc ; version-control and Emacs, sitting in a tree
:term
;;eshell ; the elisp shell that works everywhere
;;shell ; simple shell REPL for Emacs
;;term ; basic terminal emulator for Emacs
vterm ; the best terminal emulation in Emacs
:checkers
syntax ; tasing you for every semicolon you forget
;;spell ; tasing you for misspelling mispelling
;;grammar ; tasing grammar mistake every you make
:tools
;;ansible
;;debugger ; FIXME stepping through code, to help you add bugs
;;direnv
;;docker
;;editorconfig ; let someone else argue about tabs vs spaces
;;ein ; tame Jupyter notebooks with emacs
(eval +overlay) ; run code, run (also, repls)
;;gist ; interacting with github gists
lookup ; navigate your code and its documentation
;;lsp
(magit +forge) ; a git porcelain for Emacs
;;make ; run make tasks from Emacs
;;pass ; password manager for nerds
;;pdf ; pdf enhancements
;;prodigy ; FIXME managing external services & code builders
;;rgb ; creating color strings
;;taskrunner ; taskrunner for all your projects
;;terraform ; infrastructure as code
;;tmux ; an API for interacting with tmux
;;upload ; map local to remote projects via ssh/ftp
:os
(:if IS-MAC macos) ; improve compatibility with macOS
;;tty ; improve the terminal Emacs experience
:lang
;;agda ; types of types of types of types...
;;cc ; C/C++/Obj-C madness
;;clojure ; java with a lisp
common-lisp ; if you've seen one lisp, you've seen them all
;;coq ; proofs-as-programs
;;crystal ; ruby at the speed of c
;;csharp ; unity, .NET, and mono shenanigans
;;data ; config/data formats
;;(dart +flutter) ; paint ui and not much else
;;elixir ; erlang done right
;;elm ; care for a cup of TEA?
emacs-lisp ; drown in parentheses
;;erlang ; an elegant language for a more civilized age
;;ess ; emacs speaks statistics
;;faust ; dsp, but you get to keep your soul
;;fsharp ; ML stands for Microsoft's Language
;;fstar ; (dependent) types and (monadic) effects and Z3
;;gdscript ; the language you waited for
;;(go +lsp) ; the hipster dialect
;;(haskell +dante) ; a language that's lazier than I am
;;hy ; readability of scheme w/ speed of python
;;idris ; a language you can depend on
json ; At least it ain't XML
;;(java +meghanada) ; the poster child for carpal tunnel syndrome
;;javascript ; all(hope(abandon(ye(who(enter(here))))))
;;julia ; a better, faster MATLAB
;;kotlin ; a better, slicker Java(Script)
;;latex ; writing papers in Emacs has never been so fun
;;lean
;;factor
;;ledger ; an accounting system in Emacs
;;lua ; one-based indices? one-based indices
markdown ; writing docs for people to ignore
;;nim ; python + lisp at the speed of c
;;nix ; I hereby declare "nix geht mehr!"
;;ocaml ; an objective camel
(org
+journal
+roam) ; organize your plain life in plain text
;;php ; perl's insecure younger brother
;;plantuml ; diagrams for confusing people more
;;purescript ; javascript, but functional
python ; beautiful is better than ugly
;;qt ; the 'cutest' gui framework ever
;;racket ; a DSL for DSLs
;;raku ; the artist formerly known as perl6
;;rest ; Emacs as a REST client
;;rst ; ReST in peace
;;(ruby +rails) ; 1.step {|i| p "Ruby is #{i.even? ? 'love' : 'life'}"}
;;rust ; Fe2O3.unwrap().unwrap().unwrap().unwrap()
;;scala ; java, but good
;;scheme ; a fully conniving family of lisps
sh ; she sells {ba,z,fi}sh shells on the C xor
;;sml
;;solidity ; do you need a blockchain? No.
;;swift ; who asked for emoji variables?
;;terra ; Earth and Moon in alignment for performance.
;;web ; the tubes
yaml ; JSON, but readable
:email
;;(mu4e +gmail)
;;notmuch
;;(wanderlust +gmail)
:app
;;calendar
;;irc ; how neckbeards socialize
(rss +org) ; emacs as an RSS reader
;;twitter ; twitter client https://twitter.com/vnought
:config
literate
(default +bindings +smartparens)
)
Packages
All packages are installed from MELPA / ELPA or emacsmirror, otherwise noted using the :recipe syntax.
- PROPERTIES:
;; -*- no-byte-compile: t; -*-
;;; $DOOMDIR/packages.el
;; DO NOT EDIT THIS FILE DIRECTLY
;; This is a file generated from a literate programing source file.
;; You should make any changes there and regenerate it from Emacs org-mode
;; using org-babel-tangle (C-c C-v t)
(package! magit-section)
(package! org-pomodoro)
(package! org-fancy-priorities)
(package! org-ref)
(package! py-autopep8)
(package! py-isort)
(package! kubel)
(package! visual-fill-column)
(package! org-chef)
(package! ox-mediawiki)
(package! elpher)
(package! gitconfig-mode
:recipe (:host github :repo "magit/git-modes"
:files ("gitconfig-mode.el")))
(package! gitignore-mode
:recipe (:host github :repo "magit/git-modes"
:files ("gitignore-mode.el")))
(package! sly-quicklisp)
Config
This is how I configure my Doom Emacs setup for WMF projects. First let’s add the header:
;;; $DOOMDIR/config.el -*- lexical-binding: t; -*-
;;
;; DO NOT EDIT THIS FILE DIRECTLY
;; This is a file generated from a literate programing source file.
;; You should make any changes there and regenerate it from Emacs org-mode
;; using org-babel-tangle (C-c C-v t)
;;
Basics
Identity
Let’s add some basic identity info that will get used for stuff like file creation, email, gpg, etc. Also going to set our auth-sources file path for storing credentials.
(setq user-full-name "Andy Craze"
user-mail-address "acraze@wikimedia.org")
(setq auth-sources '("~/.authinfo"))
Theme
Next let’s configure our theme.
;; There are two ways to load a theme. Both assume the theme is installed and
;; available. You can either set `doom-theme' or manually load a theme with the
;; `load-theme' function. This is the default:
(setq doom-theme 'doom-gruvbox)
Fonts
;; Doom exposes five (optional) variables for controlling fonts in Doom. Here
;; are the three important ones:
;;
;; + `doom-font'
;; + `doom-variable-pitch-font'
;; + `doom-big-font' -- used for `doom-big-font-mode'; use this for
;; presentations or streaming.
;;
;; They all accept either a font-spec, font string ("Input Mono-12"), or xlfd
;; font string. You generally only need these two:
;; (setq doom-font (font-spec :family "monospace" :size 12 :weight 'semi-light)
;; doom-variable-pitch-font (font-spec :family "sans" :size 13))
;;
;; This determines the style of line numbers in effect. If set to `nil', line
;; numbers are disabled. For relative line numbers, set this to `relative'.
(setq display-line-numbers-type t)
Apps
Deft
Deft mode is great for serching through your notes.
(setq deft-directory "~/org"
deft-extensions '("org" "txt")
deft-recursive t)
Magit
Magit makes using git fun again.
(use-package magit-section
:ensure t ; make sure it is installed
:demand t ; make sure it is loaded
)
(require 'git-commit)
Org
Org-mode was the app that got me first interested in using Emacs. I had heard for years and finally decided to give it a try after watching a demo of someone’s GTD implementation at EmacsConf a few years back.
;; If you use `org' and don't want your org files in the default location below,
;; change `org-directory'. It must be set before org loads!
(setq org-directory "~/org/")
(setq org-log-done 'time
org-log-into-drawer t
org-log-state-notes-insert-after-drawers nil)
(setq org-agenda-directory "~/org/")
(after! org
(setq org-agenda-directory "~/org/")
(setq org-capture-templates
`(("i" "inbox" entry (file ,(concat org-agenda-directory "inbox.org"))
"* TODO %?")
("e" "email" entry (file+headline ,(concat org-agenda-directory "emails.org") "Emails")
"* TODO [#A] Reply: %a :@home:@school:" :immediate-finish t)
("l" "link" entry (file ,(concat org-agenda-directory "inbox.org"))
"* TODO %(org-cliplink-capture)" :immediate-finish t)
("c" "Cookbook" entry (file "~/org/cookbook.org")
"%(org-chef-get-recipe-from-url)"
:empty-lines 1 :immediate-finish t)
("m" "Manual Cookbook" entry (file "~/org/cookbook.org")
"* %^{Recipe title: }\n :PROPERTIES:\n :source-url:\n :servings:\n :prep-time:\n :cook-time:\n :ready-in:\n :END:\n** Ingredients\n %?\n** Directions\n\n" :immediate-finish t)
("p" "org-protocol-capture" entry (file ,(concat org-agenda-directory "inbox.org"))
"* TODO [[%:link][%:description]]\n\n %i" :immediate-finish t))))`
(setq org-todo-keywords
'((sequence "TODO(t)" "NEXT(n)" "|" "DONE(d)")
(sequence "WAITING(w@/!)" "HOLD(h@/!)" "|" "CANCELLED(c@/!)")))
;; (add-to-list 'org-agenda-custom-commands, 'org-agenda-todo-view)
(setq org-refile-targets '((org-agenda-files :maxlevel . 3)))
(setq accraze/org-agenda-directory (file-truename "~/org/"))
org-agenda
I use org-agenda to keep track of all tasks and events in my GTD system.
(use-package! org-agenda
:init
(map! "<f1>" #'accraze/switch-to-agenda)
(setq org-agenda-block-separator nil
org-agenda-start-with-log-mode t)
(defun accraze/switch-to-agenda ()
(interactive)
(org-agenda nil " "))
:config
(defun accraze/is-project-p ()
"Any task with a todo keyword subtask"
(save-restriction
(widen)
(let ((has-subtask)
(subtree-end (save-excursion (org-end-of-subtree t)))
(is-a-task (member (nth 2 (org-heading-components)) org-todo-keywords-1)))
(save-excursion
(forward-line 1)
(while (and (not has-subtask)
(< (point) subtree-end)
(re-search-forward "^\*+ " subtree-end t))
(when (member (org-get-todo-state) org-todo-keywords-1)
(setq has-subtask t))))
(and is-a-task has-subtask))))
(defun accraze/skip-projects ()
"Skip trees that are projects"
(save-restriction
(widen)
(let ((next-headline (save-excursion (or (outline-next-heading) (point-max)))))
(cond
((org-is-habit-p)
next-headline)
((accraze/is-project-p)
next-headline)
(t
nil)))))
(setq org-columns-default-format "%40ITEM(Task) %Effort(EE){:} %CLOCKSUM(Time Spent) %SCHEDULED(Scheduled) %DEADLINE(Deadline)")
(setq org-agenda-custom-commands `((" " "Agenda"
((agenda ""
((org-agenda-span 'week)
(org-deadline-warning-days 365)))
(todo "TODO"
((org-agenda-overriding-header "Inbox")
(org-agenda-files '(,(expand-file-name "inbox.org" accraze/org-agenda-directory)))))
(todo "TODO"
((org-agenda-overriding-header "Emails")
(org-agenda-files '(,(expand-file-name "emails.org" accraze/org-agenda-directory)))))
(todo "NEXT"
((org-agenda-overriding-header "In Progress")
(org-agenda-files '(,(expand-file-name "projects.org" accraze/org-agenda-directory)))))
(todo "TODO"
((org-agenda-overriding-header "Active Projects")
(org-agenda-skip-function #'accraze/skip-projects)
(org-agenda-files '(,(expand-file-name "projects.org" accraze/org-agenda-directory)))))
(todo "TODO"
((org-agenda-overriding-header "One-off Tasks")
(org-agenda-files '(,(expand-file-name "next.org" accraze/org-agenda-directory)))
(org-agenda-skip-function '(org-agenda-skip-entry-if 'deadline 'scheduled)))))))))
(use-package org-fancy-priorities
:hook(org-mode . org-fancy-priorities-mode)
:config
(setq org-fancy-priorities-list '("⚡" "⬆" "⬇" "☕")))
(after! org
(setq org-roam-directory "~/org/roam")
(require 'org-roam-protocol)
(require 'org-habit)
)
(setq reftex-default-bibliography '("~/org/roam/biblio.bib"))
;; see org-ref for use of these variables
(setq org-ref-bibliography-notes "~/org/roam/bibliography/notes.org"
org-ref-default-bibliography '("~/org/roam/biblio.bib")
org-ref-pdf-directory "~/org/roam/pdfs/")
(use-package! org-archive
:after org
:config
(setq org-archive-location "~/org/archive.org::datetree/"))
(setq org-journal-date-prefix "#+TITLE: "
org-journal-time-prefix "* "
org-journal-date-format "%a, %Y-%m-%d"
org-journal-file-format "%Y-%m-%d.org.gpg"
)
ox-mediawiki
I often need to export org docs to mediawiki format, which I use ox-mediawiki:
(use-package ox-mediawiki)
Python
Much of my current work is done using Python, here are some slight tweaks I’ve used to improve developer experience.
;; (require 'py-autopep8)
;; (add-hook 'python-mode-hook 'py-autopep8-enable-on-save)
(require 'py-isort)
(add-hook 'before-save-hook 'py-isort-before-save)
SSH / Tramp
You can connect to remote machines via ssh using tramp:
(after! tramp
(setq tramp-inline-compress-start-size 1000)
(setq tramp-copy-size-limit 10000)
(setq vc-handled-backends '(Git))
(setq tramp-verbose 1)
(setq tramp-default-method "scp")
(setq tramp-use-ssh-controlmaster-options nil)
(setq projectile--mode-line "Projectile")
(setq remote-file-name-inhibit-cache nil)
(setq vc-ignore-dir-regexp
(format "%s\\|%s"
vc-ignore-dir-regexp
tramp-file-name-regexp))
(setq tramp-verbose 1))
Elfeed (RSS reader)
(setq rmh-elfeed-org-files (list "~/org/elfeed.org"))
(map! :map elfeed-search-mode-map
:after elfeed-search
[remap kill-this-buffer] "q"
[remap kill-buffer] "q"
:n doom-leader-key nil
:n "q" #'+rss/quit
:n "e" #'elfeed-update
:n "r" #'elfeed-search-untag-all-unread
:n "u" #'elfeed-search-tag-all-unread
:n "s" #'elfeed-search-live-filter
:n "RET" #'elfeed-search-show-entry
:n "p" #'elfeed-show-pdf
:n "+" #'elfeed-search-tag-all
:n "-" #'elfeed-search-untag-all
:n "S" #'elfeed-search-set-filter
:n "b" #'elfeed-search-browse-url
:n "y" #'elfeed-search-yank)
(map! :map elfeed-show-mode-map
:after elfeed-show
[remap kill-this-buffer] "q"
[remap kill-buffer] "q"
:n doom-leader-key nil
:nm "q" #'+rss/delete-pane
:nm "o" #'ace-link-elfeed
:nm "RET" #'org-ref-elfeed-add
:nm "n" #'elfeed-show-next
:nm "N" #'elfeed-show-prev
:nm "p" #'elfeed-show-pdf
:nm "+" #'elfeed-show-tag
:nm "-" #'elfeed-show-untag
:nm "s" #'elfeed-show-new-live-search
:nm "y" #'elfeed-show-yank)
;; RSS usability enhancements
(after! elfeed-search
(set-evil-initial-state! 'elfeed-search-mode 'normal))
(after! elfeed-show-mode
(set-evil-initial-state! 'elfeed-show-mode 'normal))
(after! evil-snipe
(push 'elfeed-show-mode evil-snipe-disabled-modes)
(push 'elfeed-search-mode evil-snipe-disabled-modes))
;; RSS visual enhancements
(after! elfeed
(elfeed-org)
(use-package! elfeed-link)
(setq elfeed-search-filter "@1-week-ago +unread"
elfeed-search-print-entry-function '+rss/elfeed-search-print-entry
elfeed-search-title-min-width 80
elfeed-show-entry-switch #'pop-to-buffer
elfeed-show-entry-delete #'+rss/delete-pane
elfeed-show-refresh-function #'+rss/elfeed-show-refresh--better-style
shr-max-image-proportion 0.6
visual-fill-column-mode 1)
(add-hook! 'elfeed-show-mode-hook (hide-mode-line-mode 1))
(add-hook! 'elfeed-search-update-hook #'hide-mode-line-mode)
(defface elfeed-show-title-face '((t (:weight ultrabold :slant italic :height 1.5)))
"title face in elfeed show buffer"
:group 'elfeed)
(defface elfeed-show-author-face `((t (:weight light)))
"title face in elfeed show buffer"
:group 'elfeed)
(set-face-attribute 'elfeed-search-title-face nil
:foreground 'nil
:weight 'light)
(defadvice! +rss-elfeed-wrap-h-nicer ()
"Enhances an elfeed entry's readability by wrapping it to a width of
`fill-column' and centering it with `visual-fill-column-mode'."
:override #'+rss-elfeed-wrap-h
(setq-local truncate-lines nil
shr-width 120
visual-fill-column-center-text t
default-text-properties '(line-height 1.1))
(let ((inhibit-read-only t)
(inhibit-modification-hooks t))
(visual-fill-column-mode)
;; (setq-local shr-current-font '(:family "Merriweather" :height 1.2))
(set-buffer-modified-p nil)))
(defun +rss/elfeed-search-print-entry (entry)
"Print ENTRY to the buffer."
(let* ((elfeed-goodies/tag-column-width 40)
(elfeed-goodies/feed-source-column-width 30)
(title (or (elfeed-meta entry :title) (elfeed-entry-title entry) ""))
(title-faces (elfeed-search--faces (elfeed-entry-tags entry)))
(feed (elfeed-entry-feed entry))
(feed-title
(when feed
(or (elfeed-meta feed :title) (elfeed-feed-title feed))))
(tags (mapcar #'symbol-name (elfeed-entry-tags entry)))
(tags-str (concat (mapconcat 'identity tags ",")))
(title-width (- (window-width) elfeed-goodies/feed-source-column-width
elfeed-goodies/tag-column-width 4))
(tag-column (elfeed-format-column
tags-str (elfeed-clamp (length tags-str)
elfeed-goodies/tag-column-width
elfeed-goodies/tag-column-width)
:left))
(feed-column (elfeed-format-column
feed-title (elfeed-clamp elfeed-goodies/feed-source-column-width
elfeed-goodies/feed-source-column-width
elfeed-goodies/feed-source-column-width)
:left)))
(insert (propertize feed-column 'face 'elfeed-search-feed-face) " ")
(insert (propertize tag-column 'face 'elfeed-search-tag-face) " ")
(insert (propertize title 'face title-faces 'kbd-help title))
(setq-local line-spacing 0.2)))
(defun +rss/elfeed-show-refresh--better-style ()
"Update the buffer to match the selected entry, using a mail-style."
(interactive)
(let* ((inhibit-read-only t)
(title (elfeed-entry-title elfeed-show-entry))
(date (seconds-to-time (elfeed-entry-date elfeed-show-entry)))
(author (elfeed-meta elfeed-show-entry :author))
(link (elfeed-entry-link elfeed-show-entry))
(tags (elfeed-entry-tags elfeed-show-entry))
(tagsstr (mapconcat #'symbol-name tags ", "))
(nicedate (format-time-string "%a, %e %b %Y %T %Z" date))
(content (elfeed-deref (elfeed-entry-content elfeed-show-entry)))
(type (elfeed-entry-content-type elfeed-show-entry))
(feed (elfeed-entry-feed elfeed-show-entry))
(feed-title (elfeed-feed-title feed))
(base (and feed (elfeed-compute-base (elfeed-feed-url feed)))))
(erase-buffer)
(insert "\n")
(insert (format "%s\n\n" (propertize title 'face 'elfeed-show-title-face)))
(insert (format "%s\t" (propertize feed-title 'face 'elfeed-search-feed-face)))
(when (and author elfeed-show-entry-author)
(insert (format "%s\n" (propertize author 'face 'elfeed-show-author-face))))
(insert (format "%s\n\n" (propertize nicedate 'face 'elfeed-log-date-face)))
(when tags
(insert (format "%s\n"
(propertize tagsstr 'face 'elfeed-search-tag-face))))
;; (insert (propertize "Link: " 'face 'message-header-name))
;; (elfeed-insert-link link link)
;; (insert "\n")
(cl-loop for enclosure in (elfeed-entry-enclosures elfeed-show-entry)
do (insert (propertize "Enclosure: " 'face 'message-header-name))
do (elfeed-insert-link (car enclosure))
do (insert "\n"))
(insert "\n")
(if content
(if (eq type 'html)
(elfeed-insert-html content base)
(insert content))
(insert (propertize "(empty)\n" 'face 'italic)))
(goto-char (point-min))))
)
;; RSS functionality enhancements
(after! elfeed-show
(require 'url)
(defvar elfeed-pdf-dir
(expand-file-name "pdfs/"
(file-name-directory (directory-file-name elfeed-enclosure-default-dir))))
(defvar elfeed-link-pdfs
'(("https://www.jstatsoft.org/index.php/jss/article/view/v0\\([^/]+\\)" . "https://www.jstatsoft.org/index.php/jss/article/view/v0\\1/v\\1.pdf")
("http://arxiv.org/abs/\\([^/]+\\)" . "https://arxiv.org/pdf/\\1.pdf"))
"List of alists of the form (REGEX-FOR-LINK . FORM-FOR-PDF)")
(defun elfeed-show-pdf (entry)
(interactive
(list (or elfeed-show-entry (elfeed-search-selected :ignore-region))))
(let ((link (elfeed-entry-link entry))
(feed-name (plist-get (elfeed-feed-meta (elfeed-entry-feed entry)) :title))
(title (elfeed-entry-title entry))
(file-view-function
(lambda (f)
(when elfeed-show-entry
(elfeed-kill-buffer))
(pop-to-buffer (find-file-noselect f))))
pdf)
(let ((file (expand-file-name
(concat (subst-char-in-string ?/ ?, title) ".pdf")
(expand-file-name (subst-char-in-string ?/ ?, feed-name)
elfeed-pdf-dir))))
(if (file-exists-p file)
(funcall file-view-function file)
(dolist (link-pdf elfeed-link-pdfs)
(when (and (string-match-p (car link-pdf) link)
(not pdf))
(setq pdf (replace-regexp-in-string (car link-pdf) (cdr link-pdf) link))))
(if (not pdf)
(message "No associated PDF for entry")
(message "Fetching %s" pdf)
(unless (file-exists-p (file-name-directory file))
(make-directory (file-name-directory file) t))
(url-copy-file pdf file)
(funcall file-view-function file))))))
)
(use-package org-chef
:ensure t)
Terminal (vterm)
Terminal emulation has been somewhat hit-or-miss for me so far. In my experience, vterm has provided the best experience, with some tweaks to make it a bit more snappy.
(use-package vterm
:commands vterm
:config
(setq vterm-max-scrollback 10000)
(setq vterm-timer-delay 0.01)
)
(use-package ox-gemini
:ensure t)
emms
I sometimes use emacs as a multimedia player, mostly to play random audio files I have laying around.
(emms-default-players)
(emms-mode-line 1)
(emms-playing-time 1)
(setq emms-source-file-default-directory "~/Music/"
emms-playlist-buffer-name "*Music*"
emms-info-asynchronously t
emms-source-file-directory-tree-function 'emms-source-file-directory-tree-find)
(map! :leader
(:prefix ("a" . "EMMS audio player")
:desc "Go to emms playlist" "a" #'emms-playlist-mode-go
:desc "Emms pause track" "x" #'emms-pause
:desc "Emms stop track" "s" #'emms-stop
:desc "Emms play previous track" "p" #'emms-previous
:desc "Emms play next track" "n" #'emms-next))
(require 'emms-librefm-scrobbler)