A GNU Emacs Configuration for Academic Research in the Humanities
Table of Contents
- Front matter
- Contents of init.el
- Contents of settings.el
- License
- General quality of life improvements
- Editing improvements
- Writing and research
- Org-mode
- Essential org-mode settings
- Useful org-mode customizations
- Clock table settings
- Capture template model
- Agenda settings
- Refile model
- helm-org
- Beamer export settings
- LaTeX export: fix hyperlink style
- LaTeX export: using XeLaTeX
- htmlize
- Fixing the theming of code blocks in htmlize export
- Exporting from org or Markdown to different formats using Pandoc
- Hooks
- org-habit
- org-agenda-delete-empty-blocks
- Programming
- Encoding
- Appearance and display
- Miscellaneous key bindings
- Backup
Front matter
How this document was generated
This page displays the contents of an org-mode
file called settings.org
,
which I have written in a literate style using the functionality of org-babel
.
When my Emacs starts up, a minimal init.el bootstraps the straight.el
package
manager and then interprets settings.org
line-by-line, ignoring the prose and
looking for Lisp code that I have marked for execution (the parts formatted here
with a dark background). I have exported the contents of the configuration file
in HTML format using built-in org-mode functionality, a couple of minor
customizations, and the “readtheorg” theme.
Dependencies
My configuration assumes that the following are installed:
- Recursive search through file contents in a directory: silversearcher-ag (ripgrep would also work)
- General document exporting and importing: LaTeX + XeTeX; Pandoc
- Version control: Git
- Fonts: Linux Libertine O; Consolas
- Backups: backup-each-save.el
- Extra functionality:
- For recording Emacs in action: gif-screencast.el (has its own dependencies) and keycast.el
- scribe-mode.el (my own minor mode for transcribing medieval manuscripts written in Old French)
Contents of init.el
For package management within Emacs, I use straight.el
instead of the built-in
package.el
. For me, the key feature of straight.el
is that it allows one to
reproduce one’s configuration exactly, right down to the specific installed
versions of any given package. Anecdotally, it also seems to install packages
better than package.el
(I used to have packages fail to install from
MELPA all the time).
Best of all, because straight.el
is backward-compatible with the syntax of
use-package
, no major changes need to be made if you want to go back to
package.el
for whatever reason (and all the helpful use-package
code you’ll
find in online examples can be incorporated without difficulty).
(defvar bootstrap-version) (let ((bootstrap-file (expand-file-name "straight/repos/straight.el/bootstrap.el" user-emacs-directory)) (bootstrap-version 5)) (unless (file-exists-p bootstrap-file) (with-current-buffer (url-retrieve-synchronously "https://raw.githubusercontent.com/raxod502/straight.el/develop/install.el" 'silent 'inhibit-cookies) (goto-char (point-max)) (eval-print-last-sexp))) (load bootstrap-file nil 'nomessage)) (straight-use-package 'use-package) (setq straight-use-package-by-default t) (straight-use-package 'org) (org-babel-load-file (expand-file-name "settings.org" user-emacs-directory)) (put 'downcase-region 'disabled nil)
Contents of settings.el
License
My Emacs configuration is released under the terms of GNU General Public License:
;; Copyright (C) 2020 ;; Joseph R. Johnson ;; This program is free software: you can redistribute it and/or modify it under ;; the terms of the GNU General Public License as published by the Free Software ;; Foundation, either version 3 of the License, or (at your option) any later ;; version. ;; This program is distributed in the hope that it will be useful, but WITHOUT ;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS ;; FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ;; details. ;; You should have received a copy of the GNU General Public License along with ;; this program. If not, see <https://www.gnu.org/licenses/>.
General quality of life improvements
Declaring commonly used file paths
Declaring the path to my root working directory:
(setq dropbox-path "/home/joe/Dropbox/")
Next, some common org-mode
files for personal organization. My approach is to
keep four different files to manage all of my life data that is not tied to a
specific reference (like a book or article that I read) through four org-mode
files: planner.org
, which has all of my tasks and calendar data;
notebook.org
, where I write down things I’ve learned and don’t want to forget;
meetings.org
, where I take notes on any in-person meetings; and scratch.org
,
a place to jot down ideas that don’t yet have a definite place. I store all of
these in a folder called core-org-files
within my Dropbox.
(setq planner-path (concat dropbox-path "core-org-files/planner.org") notebook-path (concat dropbox-path "core-org-files/notebook.org") meetings-path (concat dropbox-path "core-org-files/meetings.org") scratch-path (concat dropbox-path "core-org-files/scratch.org"))
Finally, the path to my BibTeX library for academic references:
(setq bib-path (concat dropbox-path "references/bibtex/MyLibrary.bib"))
Disabling Customize
Next, let’s make sure nothing gets in the way of our manually programmed changes to Emacs. Emacs includes built-in functionality called Customize which was created in the goal of offering users a graphical user interface modifying their settings (thus avoiding the need to deal with Emacs Lisp code directly). The problem is that while Customize can be greatly useful for tweaking the values of certain variables or changing the program’s theming settings, it cannot help you with the intensive kinds of changes that you will want to make in order to create a workflow for academic research. I have learned through hard experience that doing some tweaking in the Customize interface and some tweaking in direct Emacs Lisp can produce conflicts and inconsistencies.
Somewhat frustratingly, the simple act of never using Customize is not enough to
prevent it from doing things: sometimes Emacs automatically invokes it in the
background. This next setting relegates any such activity to a separate file
that will not be loaded unless you explicitly wish to do so, for example with
the command (load custom-file)
:
(setq custom-file "~/.emacs.d/custom.el")
General improvements from better-defaults.el:
Save bookmarks on exit:
(add-hook 'kill-emacs-hook 'bookmark-save) ;; or '(lambda () (bookmark-save))
Add externally copied text to kill ring no matter what:
(setq save-interprogram-paste-before-kill t)
Tell apropos
to give us more info:
(setq apropos-do-all t)
Do not allow middle-click pasting to move the point:
(setq mouse-yank-at-point t)
Always use spaces instead of tab characters:
(setq-default indent-tabs-mode nil)
Add a newline to the end of files on save:
(setq require-final-newline t)
Always load newer bytecode:
(setq load-prefer-newer t)
Do not let ediff make a new Emacs frame:
(setq ediff-window-setup-function 'ediff-setup-windows-plain)
helm
(use-package helm :diminish (helm-mode . "") :bind (("<f1>" . helm-find-files) ("<f4>" . helm-buffers-list) ("<f5>" . helm-show-kill-ring) ("<f6>" . helm-bookmarks) ("M-x" . helm-M-x)) :config (helm-mode 1) (require 'helm-config) (setq-default helm-M-x-fuzzy-match t))
goto-match-beginning
Found on EmacsWiki.
(add-hook 'isearch-mode-end-hook 'goto-match-beginning) (defun goto-match-beginning () (when isearch-forward (goto-char isearch-other-end)))
Loading Emacs Lisp
It is useful to have a convenient folder in one’s Emacs directory for loading
custom Lisp files (after doing this, you can just call require
to load the
code):
(add-to-list 'load-path "~/.emacs.d/lisp/")
I found it annoying to open up init.el and run load-file
every time I wanted
to see the results of a configuration change. A quicker alternative:
(defun refresh-emacs () (interactive) (load "~/.emacs.d/init.el"))
Diminish
diminish
is a package that removes the distracting clutter of minor modes from
the Emacs modeline. Since use-package
has syntax for this, I tend to prefer
using diminish
that way whenever possible. This section is for modes that have
no corresponding use-package
declaration.
(use-package diminish) (diminish 'auto-revert-mode) (eval-after-load 'org-indent '(diminish 'org-indent-mode)) (diminish 'visual-line-mode)
which-key
(use-package which-key) (which-key-mode) (which-key-setup-minibuffer)
Output and message settings
By default, Emacs will truncate messages that exceed a certain length. This can be a big problem, especially when debugging a program. This setting changes this behavior:
(setq eval-expression-print-length nil)
This next line tells Emacs not to warn me when killing a process like a shell on quit (tip found on EmacsRedux):
(setq confirm-kill-processes nil)
Editing improvements
Manuscript transcription minor mode
(require 'scribe-mode)
Inserting guillemets
(defun insert-guillemets () (interactive) (insert "«»") (backward-char)) (global-set-key (kbd "C-c g") 'insert-guillemets)
Inserting cedillas
I’ve had persistent issues getting Emacs to type “รง” characters correctly using the US-International keyboard layout. This is the best solution I’ve been able to come up with so far.
(defun insert-c-cedilla () (interactive) (insert "ç")) (global-set-key (kbd "C-c c") 'insert-c-cedilla)
Inserting org-beamer column properties
(defun insert-beamer-column () (interactive) (insert ":PROPERTIES:\n:BEAMER_COL: 0.\n:BEAMER_env: block\n:END:") (backward-char 25))
Inserting org-beamer LaTeX image limits
(defun limit-latex-image-size () (interactive) (insert "#+ATTR_LATEX: :width cm") (backward-char 2))
move-line up, move-line-down
I love how org-mode
lets you move elements up or down with M-up
/ M-down
,
and it’d be nice to have that functionality in other contexts, for example when
programming. This is the best solution I’ve found so far.
(defun move-line-up () "Move up the current line." (interactive) (transpose-lines 1) (forward-line -2) (indent-according-to-mode)) (defun move-line-down () "Move down the current line." (interactive) (forward-line 1) (transpose-lines 1) (forward-line -1) (indent-according-to-mode)) (global-set-key [(meta up)] 'move-line-up) (global-set-key [(meta down)] 'move-line-down)
Custom function: my/tei-add-line-tags
I do a lot of transcribing of medieval manuscripts in TEI (or pseudo-TEI)
format, so the <l>
tag is a constant must. This function encloses <l></l>
around
a highlighted region.
I wrote this at a time when I probably couldn’t have come up with the entire thing off the top of my head; I wish I remembered to note where I adapted the model from. It was probably something like Xah’s Ergo Emacs (his whole guide to Emacs is amazing, and highly recommended).
My implementation is a little buggy/inconsistent - I need to fix it.
(defun my/tei-add-line-tags () (interactive) (region-beginning) (forward-char (current-indentation)) (while (< (point) (region-end)) (insert "<l>") (move-end-of-line 1) (insert "</l>") (back-to-indentation) (next-line))) (global-set-key (kbd "C-c t") 'my/tei-add-line-tags)
Unfill-paragraph, unfill-region
Solution found on StackOverflow.
(defun unfill-paragraph () (interactive) (let ((fill-column (point-max))) (fill-paragraph nil))) (defun unfill-region () (interactive) (let ((fill-column (point-max))) (fill-region (region-beginning) (region-end) nil))) (global-set-key (kbd "C-M-q") 'unfill-paragraph)
undo-tree
(use-package undo-tree :diminish (undo-tree-mode . "") :config (global-undo-tree-mode))
Spell check
(setq ispell-dictionary "francais") (defun spellcheck-start () (interactive) (flyspell-mode) (flyspell-buffer))
Writing and research
helm-bibtex
(use-package helm-bibtex :after helm :bind (("C-c r" . helm-bibtex)) :config (autoload 'helm-bibtex "helm-bibtex" "" t) (setq bibtex-completion-bibliography `(,bib-path)) (setq reftex-default-bibliography `(,bib-path)) (setq bibtex-completion-notes-path (concat dropbox-path "references/org-note-files")) (setq bibtex-completion-find-additional-pdfs t) (setq bibtex-completion-pdf-extension '(".pdf" ".djvu")) (setq bibtex-completion-pdf-field "File") ;; For Zotero's BibTeX format (setq bibtex-completion-format-citation-functions '((org-mode . bibtex-completion-format-citation-pandoc-citeproc) (default . bibtex-completion-format-citation-pandoc-citeproc))))
helm-ag
(use-package helm-ag :after helm :bind (("C-c s" . helm-do-ag)))
pdf-tools
(use-package pdf-tools :config (pdf-tools-install) (setq-default pdf-view-display-size 'fit-page) (define-key pdf-view-mode-map (kbd "C-s") 'isearch-forward) (setq pdf-view-resize-factor 1.1) (setq pdf-annot-activate-created-annotations t) (setq pdf-view-midnight-colors '("#3F3F3F" . "#DCDCCC")) ;; for Zenburn theme (add-hook 'pdf-tools-enabled-hook 'pdf-view-midnight-minor-mode) (define-key pdf-view-mode-map (kbd "h") 'pdf-annot-add-highlight-markup-annotation) (define-key pdf-view-mode-map (kbd "t") 'pdf-annot-add-text-annotation) (define-key pdf-view-mode-map (kbd "D") 'pdf-annot-delete))
Solution for auto-starting midnight mode found here; PDF annotating keys from Pragmatic Emacs.
markdown-mode
No frills, just needed for syntax highlighting:
(use-package markdown-mode)
writeroom-mode
I keep thinking I’ll use this, but haven’t needed it much yet.
(use-package writeroom-mode :bind (("<f9>" . writeroom-mode)) :config (setq writeroom-width 96))
Word wrap and spacing settings
When first beginning to use Emacs, I disliked the default lack of word-wrap (for writers, this means that one paragraph of prose is interpreted by the program as one giant line, and if you maximize or full-screen the editor, the text appears as one long unreadable thread). One of my first moves was to enable word-wrap globally like so:
(global-visual-line-mode 1)
However, I now believe that there is a better way for writers using Emacs to
handle this situation: leave this setting out and use the default
fill-paragraph
functionality (M-q
), with a sensible fill-column
setting (I
understand that 80 is traditional).
(setq-default fill-column 80)
This last setting lets Emacs know that a sentence ends after a single (rather than double) space. I learned to double-space after full stops, but am gradually adjusting.
(setq sentence-end-double-space nil)
Org-mode
Essential org-mode settings
In my opinion, new users should be particularly careful to modify
org-catch-invisible-edits
from its default. While getting to know org-mode
with the default setting, I lost some data by deleting a big chunk of text that
was folded out of view.
(require 'org) (setq org-agenda-files `(,planner-path)) (setq org-archive-location (concat dropbox-path "archive/org-archive-files/%s_archive::datetree/* Archived items")) (setq org-todo-keywords '((sequence "TODO" "IN-PROGRESS" "|" "DONE" "CANCELED") (sequence "WATCH" "|" "DONE") (sequence "PROJECT" "|" "DONE") (sequence "WAITING" "TODO" "IN-PROGRESS" "|" "DONE" "CANCELED") (sequence "RAINYDAY" "TODO" "IN-PROGRESS" "|" "DONE" "CANCELED") (sequence "HOLIDAY" "|" "DONE"))) (setq org-catch-invisible-edits 'smart)
Useful org-mode customizations
;; (setq org-list-demote-modify-bullet t) ;; (setq org-list-allow-alphabetical t) (setq org-use-property-inheritance t) (setq org-startup-indented t) (setq org-hide-leading-stars t) (setq org-src-preserve-indentation t) (setq org-agenda-skip-deadline-if-done t) (setq org-src-tab-acts-natively t) (setq org-agenda-skip-deadline-prewarning-if-scheduled nil) (setq org-lowest-priority ?D) (setq org-startup-with-inline-images nil) (setq org-agenda-use-time-grid nil) (setq org-tags-column 80) (setq org-confirm-elisp-link-function nil) (setq org-cycle-separator-lines 1) (setq org-agenda-window-setup 'current-window) (setq org-todo-window-setup 'current-window) (setq org-log-into-drawer t) (setq org-agenda-skip-scheduled-if-done t) (setq org-agenda-prefix-format "%t %s") ;; (setq org-agenda-hide-tags-regexp "research\\|teaching\\|classes\\|life") (setq org-agenda-hide-tags-regexp nil) (setq org-blank-before-new-entry '((heading . nil) (plain-list-item . nil)))
Clock table settings
(setq org-clock-persist 'history) ;; on these two, see https://orgmode.org/manual/Clocking-Work-Time.html (org-clock-persistence-insinuate)
Capture template model
Details like active projects have been anonymized for online publication. When
writing, I like to use two org files: one ’scaffolding’ file that contains all
ideas/tasks/related info, and another file that contains the essay itself. I
then use org-refile
to pull tasks out of the scaffolding file and into my planner,
where they will be able to appear in the agenda views.
(global-set-key (kbd "<f10>") 'org-capture) (setq org-capture-templates '(("r" "Research") ("r1" "Project 1") ("r1t" "Project 1 task" entry (file+headline planner-path "Project 1") "* TODO %?" :prepend t :kill-buffer t) ("r1s" "Project 1 scaffolding" entry (file+headline (lambda () (concat dropbox-path "path/to/project_1")) "Inbox") "* %?" :prepend t :kill-buffer t) ("r2" "General/other research task" entry (file+headline planner-path "Other research") "* TODO %?" :prepend t :kill-buffer t) ("t" "Teaching task") ("t1" "Class task" entry (file+headline planner-path "Classes") "* TODO %?" :prepend t :kill-buffer t) ("t2" "Advising task" entry (file+headline planner-path "Advising") "* TODO %?" :prepend t :kill-buffer t) ("t3" "Grading task" entry (file+headline planner-path "Grading") "* TODO %?" :prepend t :kill-buffer t) ("t4" "Other teaching" entry (file+headline planner-path "Other teaching") "* TODO %?" :prepend t :kill-buffer t) ("a" "Administrative task" entry (file+headline planner-path "Administrative/other work") "* TODO %?" :prepend t :kill-buffer t) ("l" "Life task") ("c" "Calendar data") ("c1" "General calendar data" entry (file+headline planner-path "General calendar") "* %?" :prepend t :kill-buffer t) ("c2" "University calendar data" entry (file+headline planner-path "University calendar") "* %?" :prepend t :kill-buffer t) ("c3" "Birthdays and anniversaries" entry (file+headline planner-path "Birthdays and anniversaries") "* %?" :prepend t :kill-buffer t) ("m" "Meeting" entry (file+datetree meetings-path) "* %?" :prepend t :kill-buffer t)))
Agenda settings
(global-set-key (kbd "C-c a") 'org-agenda) (setq org-agenda-custom-commands '(("w" "Waiting" ((todo "WAITING"))) ("r" "Rainy day ideas" ((todo "RAINYDAY"))) ("p" "Pending archive" ((todo "DONE" ((org-agenda-overriding-header "Completed and pending archive:"))) (todo "CANCELED" ((org-agenda-overriding-header "Canceled and pending archive:"))))) ("c" "Week at a glance" ((agenda "" ((org-agenda-start-day "1d") (org-agenda-span 'week) (org-agenda-start-on-weekday 'nil) (org-deadline-warning-days 12) (org-agenda-overriding-header "Week at a glance:"))))) ("f" "Full daily view" ((todo "PROJECT" ((org-agenda-overriding-header "Projects currently open:"))) (agenda "" ((org-agenda-skip-function '(org-agenda-skip-entry-if 'todo 'done)) (org-deadline-warning-days 12) (org-agenda-start-day "1d") (org-agenda-span 1))) (tags-todo "research" ((org-agenda-skip-function '(or (org-agenda-skip-entry-if 'scheduled) (org-agenda-skip-entry-if 'todo '("WAITING" "HOLIDAY" "WATCH" "RAINYDAY" "PROJECT")))) (org-agenda-overriding-header "Open research tasks:"))) (tags-todo "teaching" ((org-agenda-skip-function '(or (org-agenda-skip-entry-if 'scheduled) (org-agenda-skip-entry-if 'todo '("WAITING" "HOLIDAY" "WATCH" "RAINYDAY" "PROJECT")))) (org-agenda-overriding-header "Open teaching tasks:"))) (tags-todo "-research-teaching" ((org-agenda-skip-function '(or (org-agenda-skip-entry-if 'scheduled) (org-agenda-skip-entry-if 'todo '("WAITING" "HOLIDAY" "WATCH" "RAINYDAY" "PROJECT")))) (org-agenda-overriding-header "Other open tasks:"))) (todo "WATCH" ((org-agenda-overriding-header "Things to keep an eye on:"))) )) ))
Refile model
Contents anonymized for publication.
(setq org-refile-targets '(("/path/to/project-1.org" :maxlevel . 9) ("/path/to/project-2.org" :maxlevel . 9) ("/path/to/project-3.org" :maxlevel . 9) ("/path/to/project-4.org" :maxlevel . 9) (nil :maxlevel . 9)))
helm-org
This allows helm to read a list of tags in an org file. The standard
configuration I found online did not allow tags to be assigned with C-c C-c
. I
found a fix here.
(use-package helm-org :after helm :config (add-to-list 'helm-completing-read-handlers-alist '(org-capture . helm-org-completing-read-tags)) (add-to-list 'helm-completing-read-handlers-alist '(org-set-tags-command . helm-org-completing-read-tags)))
Beamer export settings
I’m not entirely sure this is necessary, but I haven’t gotten around to experimenting with it.
(require 'ox-latex) (add-to-list 'org-latex-classes '("beamer" "\\documentclass\[presentation\]\{beamer\}" ("\\section\{%s\}" . "\\section*\{%s\}") ("\\subsection\{%s\}" . "\\subsection*\{%s\}") ("\\subsubsection\{%s\}" . "\\subsubsection*\{%s\}")))
LaTeX export: fix hyperlink style
Solutions found here:
(customize-set-value 'org-latex-with-hyperref nil)
(add-to-list 'org-latex-default-packages-alist "\\PassOptionsToPackage{hyphens}{url}")
LaTeX export: using XeLaTeX
Enable this if you want XeLaTeX to be used for exporting via ox (not usually necessary, since Pandoc covers this use case):
(setq org-latex-compiler "xelatex")
htmlize
(use-package htmlize)
Fixing the theming of code blocks in htmlize export
Solution found here.
(defun eos/org-inline-css-hook (exporter) "Insert custom inline css to automatically set the background of code to whatever theme I'm using's background" (when (eq exporter 'html) (let* ((my-pre-bg (face-background 'default)) (my-pre-fg (face-foreground 'default))) (setq org-html-head-extra (concat org-html-head-extra (format "<style type=\"text/css\">\n pre.src {background-color: %s; color: %s;}</style>\n" my-pre-bg my-pre-fg)))))) (add-hook 'org-export-before-processing-hook #'eos/org-inline-css-hook)
Exporting from org or Markdown to different formats using Pandoc
I’ve adapted the following function from the one posted by the John Kitchin,
adding add two new features: (1) the ability to export timestamped files rather
than overwriting existing exports; (2) the ability to automatically export to
both docx
and pdf
in an orderly fashion, each kind in separate directories.
Note: For this to work as expected, you will need to have your desired
.csl
file and the path to your.bib
file specified in the header of your org file.
(defun ox-export-via-pandoc () "Export the current org file as a docx via markdown." (interactive) (let* ((current-file (file-name-nondirectory buffer-file-name)) (basename (file-name-sans-extension current-file)) (docx-file (concat basename "_" (format-time-string "%Y-%m-%d-%Hh%Mm%Ss") ".docx")) (pdf-file (concat basename "_" (format-time-string "%Y-%m-%d-%Hh%Mm%Ss") ".pdf"))) (unless (file-directory-p "docx-exports") (shell-command "mkdir docx-exports")) (shell-command (format "pandoc -s --filter pandoc-citeproc %s -o docx-exports/%s" current-file docx-file)) (unless (file-directory-p "pdf-exports") (shell-command "mkdir pdf-exports")) (shell-command (format "pandoc -s --variable mainfont=\"Linux Libertine O\" --pdf-engine=xelatex --filter pandoc-citeproc %s -o pdf-exports/%s" current-file pdf-file)) )) (global-set-key (kbd "C-c p") 'ox-export-via-pandoc)
And this, for ODT:
(defun ox-export-odt-via-pandoc () "Export the current org file as a odt via markdown." (interactive) (let* ((current-file (file-name-nondirectory buffer-file-name)) (basename (file-name-sans-extension current-file)) (odt-file (concat basename "_" (format-time-string "%Y-%m-%d-%Hh%Mm%Ss") ".odt")) (pdf-file (concat basename "_" (format-time-string "%Y-%m-%d-%Hh%Mm%Ss") ".pdf"))) (unless (file-directory-p "odt-exports") (shell-command "mkdir odt-exports")) (shell-command (format "pandoc -s --filter pandoc-citeproc %s -o odt-exports/%s" current-file odt-file)) (unless (file-directory-p "pdf-exports") (shell-command "mkdir pdf-exports")) (shell-command (format "pandoc -s --variable mainfont=\"Linux Libertine O\" --pdf-engine=xelatex --filter pandoc-citeproc %s -o pdf-exports/%s" current-file pdf-file)) )) (global-set-key (kbd "C-c o") 'ox-export-odt-via-pandoc)
Hooks
;; (add-hook 'org-agenda-mode-hook (lambda () (hl-line-mode 1))) ;; good for dracula theme (add-hook 'org-capture-mode-hook 'delete-other-windows)
org-habit
(require 'org-habit) (setq org-habit-graph-column 50) ;; 80 for dracula theme (setq org-habit-following-days 6) (setq org-habit-preceding-days 30)
org-agenda-delete-empty-blocks
Code found in a discussion on r/Emacs. Say you have a section of your agenda view for a certain TODO keyword (like WATCH or PENDING), but you currently have no such tasks in any of your tracked files – this will prevent the section from showing up empty and cluttering the agenda view.
(defun org-agenda-delete-empty-blocks () "Remove empty agenda blocks. A block is identified as empty if there are fewer than 2 non-empty lines in the block (excluding the line with `org-agenda-block-separator' characters)." (when org-agenda-compact-blocks (user-error "Cannot delete empty compact blocks")) (setq buffer-read-only nil) (save-excursion (goto-char (point-min)) (let* ((blank-line-re "^\\s-*$") (content-line-count (if (looking-at-p blank-line-re) 0 1)) (start-pos (point)) (block-re (format "%c\\{10,\\}" org-agenda-block-separator))) (while (and (not (eobp)) (forward-line)) (cond ((looking-at-p block-re) (when (< content-line-count 2) (delete-region start-pos (1+ (point-at-bol)))) (setq start-pos (point)) (forward-line) (setq content-line-count (if (looking-at-p blank-line-re) 0 1))) ((not (looking-at-p blank-line-re)) (setq content-line-count (1+ content-line-count))))) (when (< content-line-count 2) (delete-region start-pos (point-max))) (goto-char (point-min)) ;; The above strategy can leave a separator line at the beginning ;; of the buffer. (when (looking-at-p block-re) (delete-region (point) (1+ (point-at-eol)))))) (setq buffer-read-only t)) (add-hook 'org-agenda-finalize-hook #'org-agenda-delete-empty-blocks)
Programming
aggressive-indent
This package keeps your Lisp code properly spaced at all times, without needing to press a special key or enter a command.
(use-package aggressive-indent :init (add-hook 'emacs-lisp-mode-hook #'aggressive-indent-mode))
Encoding
The following settings mandate the use of UTF-8 encoding in all contexts. In my experience, failing to do this can create confusion when working across multiple operating systems (diacritics getting messed up, etc.).
(prefer-coding-system 'utf-8) (setq-default buffer-file-coding-system 'utf-8) (setq-default default-buffer-file-coding-system 'utf-8) (setq-default coding-system-for-read 'utf-8) (setq-default coding-system-for-write 'utf-8) (setq-default locale-coding-system 'utf-8) (set-terminal-coding-system 'utf-8) (set-language-environment 'utf-8) (set-keyboard-coding-system 'utf-8) (set-default-coding-systems 'utf-8)
Appearance and display
General
(show-paren-mode 1) (setq show-paren-delay 0) (blink-cursor-mode 0) (tool-bar-mode 0) (menu-bar-mode 0) (scroll-bar-mode 0) (column-number-mode t) (horizontal-scroll-bar-mode 0) (setq inhibit-splash-screen t) (setq split-width-threshold 80) (setq initial-scratch-message "") (setq inhibit-startup-message t) (setq visible-bell t) (setq scroll-conservatively 1000) (tooltip-mode -1)
Frame size
Solution found here; hook added to make it work with Emacsclient when it opens a new frame.
(add-hook 'before-make-frame-hook #'(lambda () (add-to-list 'default-frame-alist '(left . 0)) (add-to-list 'default-frame-alist '(top . 0)) (add-to-list 'default-frame-alist '(height . 40)) ;; (add-to-list 'default-frame-alist '(width . 122)) ;; good for dracula theme (add-to-list 'default-frame-alist '(width . 112)))) ;; good for Zenburn theme
Zenburn theme
(use-package zenburn-theme :config (load-theme 'zenburn t) (setq org-todo-keyword-faces '(("WAITING" . "gray") ("WATCH" . "#88C087") ("PROJECT" . "#88C087"))) (set-face-attribute 'org-todo nil :bold nil :foreground "#CC9393") (set-face-attribute 'org-agenda-date-today nil :bold nil :foreground "#FFFFFF") (set-face-attribute 'org-warning nil :bold nil :foreground "#CC9393") (set-face-attribute 'font-lock-keyword-face nil :bold nil :foreground "#F0DFAF") (set-face-attribute 'org-tag nil :bold nil) (font-lock-add-keywords 'org-mode '(("\\[@.*?\\]" . font-lock-keyword-face))) ;;add syntax highlighting for Pandoc citations (font-lock-add-keywords 'org-mode '(("\\[-@.*?\\]" . font-lock-keyword-face))))
gif-screencast.el
Requires installed packages: scrot, imagemagick, gifsicle
(require 'gif-screencast) (global-set-key (kbd "<f11>") 'gif-screencast-start-or-stop)
keycast.el
(require 'keycast)
Miscellaneous key bindings
Navigation
For improving the ergonomics of common navigation operations within Emacs (making new windows, switching between windows, etc.):
(global-set-key (kbd "M-o") 'other-window) (global-set-key (kbd "M-5") 'delete-other-windows) (global-set-key (kbd "M-4") 'split-window-below) (global-set-key (kbd "M-3") 'split-window-right) (global-set-key (kbd "M-0") 'delete-window) (global-set-key (kbd "C-<left>") 'previous-buffer) (global-set-key (kbd "C-<right>") 'next-buffer) (setq next-line-add-newlines t)
Saving
Likewise, I prefer for these extremely common saving-related functions to depend on a single keystroke:
(global-set-key [f2] 'save-buffer) (global-set-key [f3] 'save-some-buffers) (global-set-key [f12] 'save-buffers-kill-terminal)
Quick access
I keep a file called scratch.org
for random notes or drafting ideas that don’t
really have a conventional place in my workflow and bind it to F8
:
(global-set-key [f8] (lambda() (interactive)(find-file scratch-path)))
I found vscode’s key binding for opening a shell within the editor extremely convenient:
(global-set-key (kbd "C-`") 'shell)
Store links for linking purposes:
(global-set-key (kbd "C-c l") 'org-store-link)
Backup
magit
The magit
package provides an incredibly fast an intuitive interface for using
the Git version control system within Emacs.
(use-package magit :bind (("<f7>" . magit-status)))
Here’s an example of how to use magit
in a basic case, as configured: press
F7
to call it up in a file under version control, then use n
and p
to
navigate to any files whose changes you wish to stage, staging them with s
.
Press c
twice to indicate that you wish to make a commit, enter your commit
message, and then press C-c C-c
to make the commit.
backup-each-save
To install it, download the file backup-each-save.el and place it in your
~/.emacs.d/lisp
folder after adding that folder to your load-path). Then,
using a solution for filtering from EmacsWiki:
(require 'backup-each-save) (add-hook 'after-save-hook 'backup-each-save) (defun backup-each-save-filter (filename) (let ((ignored-filenames '("^/tmp" "semantic.cache$" "\\.emacs-places$" "\\.recentf$" ".newsrc\\(\\.eld\\)?")) (matched-ignored-filename nil)) (mapc (lambda (x) (when (string-match x filename) (setq matched-ignored-filename t))) ignored-filenames) (not matched-ignored-filename))) (setq backup-each-save-filter-function 'backup-each-save-filter)
Emacs’s built-in backup
Settings for built-in Emacs backup functionality, augmented by snippets of code suggested by Pragmatic Emacs:
(setq auto-save-interval 20 backup-by-copying t kept-new-versions 10 kept-old-versions 0 delete-old-versions t version-control t vc-make-backup-files t backup-directory-alist '(("." . "~/.emacs.d/backups")) auto-save-file-name-transforms '((".*" "~/.emacs.d/auto-save-list/" t)))