Skip to content

itscaleb/emacs.d

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Emacs Configuration

My init.el file uses org-babel-load-file to load the Emacs Lisp source code blocks in this README. In other words, this README that you’re looking at now is my Emacs configuration.

If the variables I’m setting aren’t clear what they’re used for, use \C-h v and type it in to read the docstring. I’ll make sure to mention the interesting parts though.

Customize

I want my customization information going to a file other than the emacs config file. I don’t like the constant churn in my main config file when I make a change through customize.

(setf custom-file "~/.emacs.d/emacs-custom.el")
(load custom-file t)

Packages

After struggling with managing packages through my Emacs configuration, I decided to manage Emacs packages through Nix. Here is my nix expression that handles it all.

A nice benefit of this approach (in addition to all the benefits of nix) is that every package I choose is installed when I use nix to install Emacs, so I can remove all the code managing packages and simplify my Emacs configuration.

Emacs

Global Settings

Gotta turn off tabs, also enable auto-revert-mode so changes to the files outside of emacs update the buffers. Plus some other various stuff.

(global-hl-line-mode t)
(global-auto-revert-mode t)
(global-prettify-symbols-mode t)
(dumb-jump-mode)
(setf indent-tabs-mode nil
      initial-buffer-choice (lambda ()
                              (org-agenda nil "a")
                              (delete-other-windows))
      vc-follow-symlinks t
      confirm-kill-emacs 'y-or-n-p
      backup-directory-alist '(("." . "~/.emacs.d/backup"))
      abbrev-file-name "~/org/abbrev_defs")
(quietly-read-abbrev-file nil)
(add-to-list 'exec-path "~/.nix-profile/bin")
(add-to-list 'load-path "~/.emacs.d/lisp")
(setq tab-always-indent 'complete)
(add-to-list 'Info-directory-list (expand-file-name "~/org/info"))
(require 'server)
(require 'org-man)
(pdf-tools-install)
(unless (server-running-p)
  (server-start))

Input Method Stuff

This sets up japanese input within emacs. You probably don’t need this if you have it set up through your OS?

(setf default-input-method "japanese")

Global Key Bindings

(global-set-key (kbd "C-x g") 'magit-status)
(global-set-key (kbd "C-x M-g") 'magit-dispatch-popup)
(global-set-key "\C-cl" 'org-store-link)
(global-set-key "\C-ca" 'org-agenda)
(global-set-key "\C-cc" 'org-capture)
(global-set-key "\C-cb" 'org-iswitchb)
(global-set-key "\C-cj" (lambda () (interactive) (org-clock-jump-to-current-clock)))
(global-set-key "\C-s" 'swiper)
(global-set-key (kbd "C-S-c C-S-c") 'mc/edit-lines)
(global-set-key (kbd "C->") 'mc/mark-next-like-this)
(global-set-key (kbd "C-<") 'mc/mark-previous-like-this)
(global-set-key (kbd "C-c C-<") 'mc/mark-all-like-this)

Making Things Look Nice

(require 'spaceline-config)
(setf powerline-default-separator 'wave)
(when (eq system-type 'darwin) ; fix for spaceline on mac
  (setf ns-use-srgb-colorspace nil))
(spaceline-spacemacs-theme)
(load-theme 'zenburn t)
(show-paren-mode t)
(menu-bar-mode -1)
(tool-bar-mode -1)
(scroll-bar-mode -1)
(fringe-mode 1)

Turn the clock on in the mode-line. I tend to never leave Emacs, so I’d lose track of time otherwise! By default it also shows the load-average, but I’m not really worried about that so I turned it off.

(setf display-time-default-load-average nil)
(display-time-mode)

Adding My User Info

 (setf user-full-name "Caleb Gossler"
	user-mail-address "[email protected]"
	calendar-latitude 47.4
	calendar-longitude -122.3
	calendar-location-name "Seattle, WA")

Ispell Personal Dictionary

This file is where I store words I want added to my dictionary so they aren’t marked as misspelled.

(setf ispell-personal-dictionary "~/org/dictionary")

Calendar Settings

(setf diary-file "~/org/diary"
      appt-message-warning-time 15
      diary-number-of-entries 3
      appt-display-diary nil)
(appt-activate 1)

LilyPond

(autoload 'LilyPond-mode "lilypond-mode")
(add-to-list 'auto-mode-alist '("\\.ly$" . LilyPond-mode))
(add-hook 'LilyPond-mode-hook (lambda () (turn-on-font-lock)))

Auth Sources

Auth sources let you store credentials for services you use in emacs. This way you don’t need to keep passwords in plain text in your config.

(setf auth-sources
      '((:source "~/org/authinfo.gpg")))

Org-mode

Some Basics

I’m trying out org-drill for creating and studying flash cards. It looks like if I add it to the org-modules, I need to require cl before it gets loaded.

The org-drill-all command scans for flashcards throughout all my notes and starts an org-drill session.

(defun org-drill-all ()
  (interactive)
  (org-drill
   (directory-files-recursively "~/org/notes/" "\.org$")))

This lets me ignore headers when exporting, without ignoring the contents under it.

(require 'ox-extra)
(ox-extras-activate '(ignore-headlines))

Now that’s loaded so we can start setting some options.

(add-hook 'org-mode-hook 'visual-line-mode)
(add-hook 'org-mode-hook 'flyspell-mode)
(add-hook 'org-mode-hook 'org-display-inline-images)
(add-hook 'org-babel-after-execute-hook (lambda ()
					    (when org-inline-image-overlays
					      (org-redisplay-inline-images))))

(require 'cl)
(require 'org-drill)
(require 'org-habit)
(load "auctex.el")
(setf org-agenda-files '("~/org/agenda")
      org-agenda-include-diary t
      org-refile-targets '((org-agenda-files :maxlevel . 2))
      org-startup-indented t
      org-agenda-span 'day
      org-agenda-todo-ignore-scheduled 'future
      org-log-into-drawer t
      org-clock-idle-time 10
      org-return-follows-link t
      org-special-ctrl-a/e t
      org-pretty-entities t
      org-pretty-entities-include-sub-superscripts t
      org-agenda-skip-scheduled-if-deadline-is-shown t
      org-drill-learn-fraction 0.3
      org-drill-add-random-noise-to-intervals-p t
      org-drill-leech-method 'warn
      org-log-done 'time
      org-latex-create-formula-image-program 'dvipng)
(plist-put org-format-latex-options :scale 2)

Configure Text Searching of my Notes

If you use org-agenda-text-search-extra-files, You can use the agenda view search facility to do text searching in org files other than your agenda files. This enables evernote-like search features in Emacs!! I set it to include all org files (recursively) in my ~/org/notes/ directory.

Note: \C-a s brings up the search prompt in the minibuffer.

(setf org-agenda-text-search-extra-files (directory-files-recursively "~/org/notes/" "\.org$"))

Todo Keywords

Setting up keyword workflows.

(setf org-todo-keywords
      '((sequence "TODO(t)" "IN PROGRESS(i)" "ON HOLD(h)" "IN REVIEW(r)" "FOLLOW-UP(f)" "BLOCKED(b)" "RELEASE(e)" "|" "DONE(d!)" "CANCELLED(c!)")))

Clocking

(setf org-clock-persist 'history)
(org-clock-persistence-insinuate)

Org-babel

I have a file of babel functions that I want available in every org file.

(org-babel-lob-ingest "~/org/babel/library.org")

This enables execution of various code blocks

(setf org-ditaa-jar-path "/run/current-system/sw/lib/ditaa.jar"
      org-confirm-babel-evaluate nil)
(org-babel-do-load-languages
 'org-babel-load-languages
 '((ditaa . t)
   (sql . t)
   (dot . t)
   (racket . t)
   (shell . t)
   (python . t)
   (lilypond . t)
   (latex . t)
   (gnuplot . t)))
(setq org-babel-racket-command "racket")

Org-capture

If any capture doesn’t specify a target, fall back to inbox.org.

(setf org-default-notes-file "~/org/agenda/inbox.org")

Templates

(setf org-capture-templates
      '(("c" "Clock into new")
        ("ct" "Task" entry (file+headline "~/org/agenda/inbox.org" "Inbox")
         "* TODO %?\nSCHEDULED: %T Created: %U\n  Context: %a\n  %i" :clock-in t :clock-keep t)
        ("cm" "Meeting" entry (file+datetree "~/org/agenda/meetings.org")
         "* %?\n** Details\n  + *Agenda*:\n  + *Attendees*:\n" :clock-in t :clock-keep t)
        ("t" "New Task" entry (file+headline "~/org/agenda/inbox.org" "Inbox")
         "* TODO %?\n  Created: %U\n  Context: %a\n  %i")
        ("j" "New Journal Entry" entry (file+datetree "~/org/agenda/journal.org.gpg")
         "* %? %^g\n\nPosted At: %U" :empty-lines-after 1 :kill-buffer t)
        ("l" "New Log Entry" entry (file+datetree "~/org/agenda/log.org.gpg")
         "* %? \nPosted At: %U" :empty-lines-after 1 :kill-buffer t)
        ("r" "Random" entry (file "~/org/agenda/random.org.gpg")
         "* %?\nCreated At: %U" :empty-lines-after 1 :kill-buffer t)
        ("n" "Add Note to Clocked in Entry" item (clock)
         "+ %i%? (%<%r>)")
        ("b" "Add Checkbox to Clocked in Entry" item (clock)
         "+ [ ] %? (%<%r>)" :prepend t)
        ("a" "Add Task to Clocked in Entry" entry (clock)
         "* TODO %?" :prepend t)))
(define-key global-map "\C-cn"
  (lambda () (interactive) (org-capture nil "n")))

Capturing from outside Emacs

This is a helper function that lets emacsclient start a new frame and select a capture template. There’s also an advice function to close the frame after the capture has been finalized.

The emacs client command is emacsclient -e "(start-capture \"t\")"

 (defun start-capture (template-key)
   "Start capture with the template assigned to TEMPLATE-KEY"
   (make-frame '((alpha . 80)(height . 10)
		  (top . -1)(left . -10)(autoraise . t)
		  (title . "Capture")(name . "captureframe")
		  (minibuffer . nil)))
   (select-frame-by-name "captureframe")
   (org-capture nil template-key)
   (delete-other-windows))

 (defadvice org-capture-finalize (after delete-capture-frame activate)
   "Advise capture-finalize to close the frame if it is the capture frame"
   (if (equal "captureframe" (frame-parameter nil 'name))
	(delete-frame)))

Custom Agenda Commands

(setf org-agenda-custom-commands
      '(("a" "Combined Agenda"
         ((agenda)
          (tags-todo "+inbox" ((org-agenda-overriding-header "Inbox:")))))
        ("t" "Study"
         ((agenda)
          (todo "READING" ((org-agenda-overriding-header "Currently Reading")))
          (todo "PAUSED" ((org-agenda-overriding-header "On Hold")))
          (todo "TO-WATCH" ((org-agenda-overriding-header "Videos to Watch")))
          (todo "RESEARCH"))
         ((org-agenda-category-filter-preset '("+study"))))
        ("w" "Work"
         ((agenda)
          (todo "BLOCKED" ((org-agenda-overriding-header "Blocked:")))
          (tags-todo "+inbox" ((org-agenda-overriding-header "Inbox:")))
          (todo "REVIEW" ((org-agenda-overriding-header "Code Reviews:")))
          (todo "WAITING" ((org-agenda-overriding-header "Waiting:")))
          (todo "IN-PROGRESS" ((org-agenda-overriding-header "In Progress:")))
          (todo "RESEARCH" ((org-agenda-overriding-header "Research:")))
          (todo "READING" ((org-agenda-overriding-header "Reading:")))
          (todo "TO-READ" ((org-agenda-overriding-header "To Read:")))
          (todo "TODO" ((org-agenda-overriding-header "Backlog:"))))
         ((org-agenda-category-filter-preset '("+work"))))))

Org-Publish

(setq org-publish-project-alist
           '(("homepage"
              :base-directory "~/org/website/"
              :publishing-directory "~/website"
              :publishing-function org-html-publish-to-html)
             ("notes"
              :base-directory "~/org/website/notes"
              :publishing-directory "~/website/notes"
              :publishing-function org-html-publish-to-html
              :makeindex t
              :auto-sitemap t
              :sitemap-filename "index.org"
              :sitemap-title "My Notes"
              :sitemap-format-entry my-org-publish-sitemap
              :sitemap-style list
              :recursive t)))

Dired

Dired renders a buffer that it builds off of a call to ls. Here we can customize the switches passed to it. dired-dwim-target tells dired to try to guess a default target directory for file operations. This means if there is a Dired buffer displayed in the next window, use that as the target. Convinient when doing operations between directories.

(setf dired-listing-switches "-lh"
      delete-by-moving-to-trash t
      dired-dwim-target t)

By default, dired shows permissions, user and group, file size, and dates. I prefer a cleaner view, so this enables dired-hide-details-mode. You can toggle it on and off with ( when the extra details are needed.

(add-hook 'dired-mode-hook 'dired-hide-details-mode)

Dired-X

Dired-X adds some nice features, one of them is doing file operations async. A must have for doing large/over-the-network file operations

(add-hook 'dired-load-hook
          (lambda ()
            (load "dired-x")
            (dired-async-mode)))

Emacs Multimedia System (EMMS)

It’s very convenient to be able to control music from within Emacs. I use it mostly to play streams.

 (require 'emms-setup)
 (require 'emms-streams)
 (require 'emms-stream-info)
 (setq emms-directory "~/org/emms"
	emms-stream-default-action "play"
	emms-stream-info-backend 'mplayer
	emms-stream-bookmarks-file "~/org/emms/streams"
	emms-mode-line-format " 𝄞 ")

 (emms-minimalistic)
 (emms-default-players)
 (emms-mode-line-enable)

 (advice-add 'emms-stream-info-mplayer-backend
	      :override
	      (lambda (url)
		"The original function isn't working, using this temporarily until I figure it out."
		(condition-case excep
		    (call-process "mplayer" nil t nil
				  "-msglevel" "decaudio=-1:cache=-1:statusline=-1:cplayer=-1" "-cache" "180"
				  "-endpos" "0" "-vo" "null" "-ao" "null" "-playlist"
				  url)
		  (file-error
		   (error "Could not find the mplayer backend binary")))))

EasyPG

Use gpg2 instead of gpg

(setf epg-gpg-program "gpg2")

Helm

(setf ivy-use-virtual-buffers t)
(setf magit-completing-read-function 'ivy-completing-read)
(setf ivy-count-format "(%d/%d) ")
(ivy-mode 1)

Jabber

I use this to connect to google hangouts/talk. It doesn’t offer all the features but It’s nice being able to chat in emacs.

(setf jabber-alert-presence-hooks nil
      jabber-show-resources nil
      jabber-auto-reconnect t
      jabber-history-enabled t
      jabber-roster-show-title nil
      jabber-roster-line-format " %c %-25n %u %-8s  %S"
      jabber-alert-message-wave "~/.emacs.d/data/sound.wav"
      ; jabber-message-alert-same-buffer nil
      jabber-account-list '(("[email protected]"))
      jabber-alert-message-hooks '(jabber-message-notifications
                                   jabber-message-echo
                                   jabber-message-scroll
                                   jabber-message-wave))

Email

After a few tries, I’ve settled on using mu4e for reading email. mu4e leverages mu, which is a set of tools to index and search emails stored in Maildir format.

In order for mu to index that email, it has to exist in a Maildir somewhere on your system. I’m using mbsync for that. mbsync synchronizes IMAP4 and Maildir mailboxes. It propogates new mail, deletions, etc. both ways. In addition to mbsync, offlineimap seems to be another popular choice.

I have this setup for sending/receiving email for two accounts, my work and personal. With mu4e, you can do this with mu4e “contexts.”

Setting it all up is not as bad as it sounds. And in my opinion it offers a better experience than any other email client I’ve used, and it’s all within Emacs!

Basic Email Config

For sending mail, I’m using Message, the Emacs message composition mode. After this is set up, you can use Message mode (\C-x m) to send outgoing email.

One complication with my setup is that I want to be able to send mail through two SMTP accounts. mu4e makes this easy to do with mu4e contexts. There are some variables you need to set to your SMTP server’s info, and when you switch contexts in mu4e it will adjust those variables for you.

Here I’m setting some variables that are consistent across both my SMTP accounts. Below in the mu4e-contexts setup, you’ll see where I set the individual values for smtpmail-smtp-server on both contexts.

 (setf send-mail-function 'smtpmail-send-it
	smtpmail-stream-type 'starttls
	smtpmail-smtp-service 587
	message-kill-buffer-on-exit t)

For authentication, the first time you send mail, Emacs will prompt you for your username and password. By default, it will save it in your authinfo file so you probably want your authinfo encrypted (which is dead simple using EasyPG, a built-in Emacs package).

Or, you can add it manually yourself by appending a line similar to the following to your authinfo (adding your username and password):

machine smtp.gmail.com login <your username>@gmail.com port 587 password <your password>

For gmail, you should have two factor authentication turned on, and generate an app specific password.

Mu4e Config

Most of these variables are self-explanitory. Some make mu4e work better with gmail, and are explained in the mu4e FAQ. The missing part here is my mbsync configuration file. I’ll see if I can get that included here at some point.

(require 'mu4e)
(require 'org-mu4e)
(add-hook 'mu4e-compose-mode-hook 'auto-fill-mode)

(global-set-key (kbd "C-c m") 'mu4e)

(setf mu4e-maildir "~/.mail"
      mu4e-view-show-images t
      mu4e-update-interval 300
      mu4e-view-show-addresses t
      mu4e-hide-index-messages t
      mu4e-decryption-policy 'ask
      mu4e-compose-format-flowed t
      mu4e-context-policy 'ask-if-none
      mu4e-get-mail-command "mbsync -a"
      mu4e-change-filenames-when-moving t

      mu4e-compose-context-policy 'ask-if-none
      mu4e-maildir-shortcuts '(("/personal/inbox" . ?i)
                               ("/work/inbox" . ?w))
      mu4e-user-mail-address-list '("[email protected]"
                                    "[email protected]"
                                    "[email protected]")
      mu4e-headers-fields '((:human-date	. 15)
                            (:flags		. 6)
                            (:from		. 30)
                            (:thread-subject	. nil)))
(setf mu4e-contexts
      `( ,(make-mu4e-context
           :name "Personal"
           :match-func (lambda (msg)
                         (when msg
                           (mu4e-message-contact-field-matches
                            msg :to "gmail\.com")))
           :vars '( ( user-mail-address      . "[email protected]"  )
                    ( user-full-name         . "Caleb Gossler" )
                    ( smtpmail-smtp-server   . "smtp.gmail.com")
                    ( mu4e-compose-signature . "Caleb Gossler\nPGP: 94EE 36DD")
                    ( mu4e-trash-folder      . "/personal/trash")
                    ( mu4e-sent-folder       . "/personal/sent")
                    ( mu4e-drafts-folder     . "/personal/drafts")
                    ( mu4e-refile-folder     . "/personal/archive")
                    ( mu4e-sent-messages-behavior . delete) ;gmail handles sent messages
                    ( mu4e-headers-skip-duplicates . t)))
         ,(make-mu4e-context
          :name "Work"
          :match-func (lambda (msg)
                        (when msg
                          (mu4e-message-contact-field-matches
                           msg :to "motivity\.net")))
          :vars '( ( user-mail-address		. "[email protected]" )
                   ( user-full-name		. "Caleb Gossler" )
                   ( smtpmail-smtp-server	. "smtp.gmail.com")
                   ( mu4e-compose-signature	. "Caleb Gossler\nSoftware Engineer")
                   ( mu4e-trash-folder		. "/work/trash")
                   ( mu4e-sent-folder		. "/work/sent")
                   ( mu4e-drafts-folder	. "/work/drafts")
                   ( mu4e-refile-folder	. "/work/archive")
                   ( mu4e-sent-messages-behavior . delete)
                   ( mu4e-headers-skip-duplicates . t)))))

Get a desktop notification on update.

(add-hook 'mu4e-index-updated-hook
	    (defun new-mail-notification ()
	      (require 'notifications)
	      (notifications-notify :title "Email Updated")))

This hook seems to help when sending email, preventing newlines from appearing in paragraphs.

(add-hook 'mu4e-compose-mode-hook (lambda ()
				      (setf use-hard-newlines nil)))

BBDB

(autoload 'bbdb-insinuate-mu4e "bbdb-mu4e")
(bbdb-initialize 'message 'mu4e)
(setf bbdb-file "~/org/bbdb.gpg")
(setq bbdb-mail-user-agent (quote message-user-agent))
(setq mu4e-view-mode-hook (quote (bbdb-mua-auto-update visual-line-mode)))
(setq mu4e-compose-complete-addresses nil)
(setq bbdb-mua-pop-up t)
(setq bbdb-mua-pop-up-window-size 5)

Racket

(add-hook 'racket-mode-hook
          (lambda ()
            (define-key racket-mode-map (kbd "C-c r") 'racket-run)))
(add-hook 'racket-mode-hook      #'racket-unicode-input-method-enable)
(add-hook 'racket-repl-mode-hook #'racket-unicode-input-method-enable)

IRC

This function has ERC connect to every IRC network entry in your authinfo file.

Each line should look something like: machine irc.freenode.net login mynick port irc password mypass

(defun irc-connect-all ()
  (interactive)
  (require 'auth-source)
  (let ((auth (auth-source-search :port "irc" :max 10 :requires '(user secret host))))
    (dolist (login auth)
      (let ((pass (funcall (plist-get login :secret)))
            (nick (plist-get login :user))
            (host (plist-get login :host)))
        (erc :server host :nick nick :password pass)))))

ERC Settings

 (setf erc-hide-list '("JOIN" "PART" "QUIT")
	erc-rename-buffers t
	erc-kill-server-buffer-on-quit t
	erc-modules '(autojoin button completion
		      fill irccontrols list log
		      match menu move-to-prompt
		      netsplit networks noncommands
		      notify notifications readonly
		      ring stamp spelling track)
	erc-log-mode t
	erc-log-insert-log-on-open t
	erc-log-write-after-insert t
	erc-log-channels-directory "~/org/irc-logs")

RSS

 (elfeed-org)
 (elfeed-goodies/setup)
 (setf elfeed-db-directory "~/org/elfeed-db"
	elfeed-goodies/entry-pane-position 'bottom
	rmh-elfeed-org-files '("~/org/rss.org"))

Some Extra Commands

  • Sometimes I export an org document to UTF plain text, but need to paste it into an email. This is helpful to get rid of “fill”
(defun quit ()
  "This will quit emacs and kill emacs server"
  (interactive)
  (save-some-buffers)
  (kill-emacs))

(defun unfill-paragraph ()
  (interactive)
  (let ((fill-column (point-max)))
    (fill-paragraph nil)))

(defun unfill-region (start end)
  (interactive "r")
  (let ((fill-column (point-max)))
    (fill-region start end nil)))
  • Let’s play zork!
(defun zork ()
  "Starts a game of Zork."
  (interactive)
  (require 'malyon)
  (malyon "~/.emacs.d/games/zork1.z5"))

(defun spider ()
  "Starts a game of 'Spider and Web'."
  (interactive)
  (require 'malyon)
  (malyon "~/.emacs.d/games/spider.z5"))

Hooks

(add-hook 'after-init-hook 'global-company-mode)
(add-hook 'message-mode-hook 'turn-on-orgtbl)
(add-hook 'message-mode-hook 'turn-on-orgstruct++)
(add-hook 'sql-interactive-mode-hook (lambda ()
                                       (toggle-truncate-lines t)))

Yasnippet

(setq yas-snippet-dirs
      '("~/.emacs.d/snippets"))

(yas-global-mode 1)

Eshell

Stolen from here

(defun eshell-here ()
  "Opens up a new shell in the directory associated with the
current buffer's file. The eshell is renamed to match that
directory to make multiple eshell windows easier."
  (interactive)
  (let* ((parent (if (buffer-file-name)
                     (file-name-directory (buffer-file-name))
                   default-directory))
         (height (/ (window-total-height) 3))
         (name   (car (last (split-string parent "/" t)))))
    (split-window-vertically (- height))
    (other-window 1)
    (eshell "new")
    (rename-buffer (concat "*eshell: " name "*"))

    ;;(insert (concat "ls"))
    (eshell-send-input)))

IBuffer

(global-set-key (kbd "C-x C-b") 'ibuffer)
(setq ibuffer-saved-filter-groups
      '(("default"
         ("Dired" (mode . dired-mode))
         ("Magit" (name . "*magit"))
         ("Mail" (or (name . "*mu4e*")
                     (mode . mu4e-compose-mode)))
         ("ERC" (mode . erc-mode))
         ("Elfeed" (name . "*elfeed"))
         ("Help" (or
                  (name . "*Help*")
                  (name . "*Apropos*")
                  (name . "*info*")))
         ("Org" (or (mode . org-mode)
                    (mode . org-agenda-mode))))))
(add-hook 'ibuffer-mode-hook
          (lambda ()
            (ibuffer-auto-mode 1)
            (ibuffer-switch-to-saved-filter-groups "default")))

Epubs

(add-to-list 'auto-mode-alist '("\\.epub\\'" . nov-mode))
(setq nov-text-width 100)

Releases

No releases published

Packages

No packages published