#+TITLE: My Literate GNU Emacs Config #+AUTHOR: Marius Peter #+DATE: <2020-07-23 Thu> #+EMAIL: blendoit@gmail.com #+STARTUP: showall customtime #+SETUPFILE: ~/.emacs.d/templates/documents/general.org #+INCLUDE: ~/.emacs.d/templates/documents/general.org_title #+LATEX: \begin{abstract} GNU Emacs is most often used as a text editor. The utmost level of customisation is afforded by enabling the user to rewrite \textit{any} part of the source code and observe the editor's modified behaviour in real time. Since its inception in 1984, GNU Emacs has grown to be much more than a full-featured, high-productivity text editor---new \textit{modes} have been written to interact with hundreds of file formats, including \texttt{.txt}, \texttt{.pdf}, \texttt{.jpg}, \texttt{.csv}, and \texttt{.zip} just to name a few. This configuration file itself was written in \textit{Org mode}, a collection of functions enabling the harmonious mixing of code and comments in view of publication: this is the endgame of \textit{literate programming}. #+LATEX: \end{abstract} * Introduction The following sections were laid out very deliberately. When we start Emacs, Emacs Lisp source blocks contained in this document are evaluated sequentially. This means that our editing environment is bootstrapped at every Emacs startup! For instance, we only begin loading packages once we ensured ~use-package~ is working properly. Customizing Emacs goes far, far beyond this document---feel free to experiment and discover. - ~C-h f~ describe function - ~C-h v~ describe variable - ~C-h k~ describe key These three commands will attempt to describe the element currently under our cursor, however you can start typing to search for another symbol. * TODO First-time setup Spacemacs-like dialog for default settings. #+NAME: first-setup # #+BEGIN_SRC emacs-lisp # ;; Prompt enterprise or personal install. Create file in .emacs.d/ on Linux, # ;; AppData/ on Windows. Ask user for details and preferred bindings. # # ; Check if .emacs.d exists # # ; If it does, warn user # # ; Copy init-bootstrap.el from USB to where operating systems expects init.el # # ;; (defun blendoit/first-time-setup-windows-nt () # ;; "Execute the first-time setup on MS Windows. # ;; If no `.emacs.d/' config exists on local system, copy # ;; init-bootstrap.el to `~.emacs.d/'." # ;; (interactive) # ;; (find-file "~/.emacs.d/blendoit/blendoit-init.org")) # # ;; (cond ((string-equal system-type "windows-nt")blendoit/first-time-setup-windows-nt) # ;; ((string-equal system-type "gnu/linux") blendoit/first-time-setup-linux)) # #+END_SRC ** File system paths In this subsection, we tell Emacs about relevant paths to resources. On my MS Windows machine, I add the path to Portable Git.[fn::Download from https://git-scm.com/download/win] #+BEGIN_SRC emacs-lisp (if (string-equal system-type "windows-nt") (add-to-list 'exec-path "C:/Users/marius.peter/PortableGit/bin/")) #+END_SRC * Early setup ** Garbage collection First, we increase the RAM threshold beyond which the garbage collector is activated. #+NAME: garbage-collection #+BEGIN_SRC emacs-lisp (setq gc-cons-threshold 100000000) #+END_SRC ** Profiling --- start We start the profiler now , and will interrupt it in Section [[Profiling --- stop]]. We will then present profiling report in Section [[Profiling --- report]]. #+NAME: profiler-start #+BEGIN_SRC emacs-lisp ; (profiler-start) #+END_SRC ** Emacs client Makes opening emacs faster for following instances. #+NAME: emacs-client #+BEGIN_SRC emacs-lisp ; (setq initial-buffer-choice (lambda () (get-buffer "*dashboard*"))) #+END_SRC ** Custom file Load settings created automatically by GNU Emacs Custom. (For example, any clickable option/toggle is saved here.) Useful for fooling around with M-x customize-group . user-emacs-directory #+NAME: custom-file-location #+BEGIN_SRC emacs-lisp (setq custom-file (concat user-emacs-directory "init-custom.el")) (load custom-file) #+END_SRC ** Customization shortcuts We begin by defining a user shortcut to this very file. We load this as early as possible, this facilitates debugging. #+NAME: shortcut-config #+BEGIN_SRC emacs-lisp (defun my/find-literate-config () "Jump to this very file." (interactive) (find-file my/literate-config)) (global-set-key (kbd "C-c c") 'my/find-literate-config) #+END_SRC Now, different shortcuts for other customization actions: #+NAME: shortcuts-customization #+BEGIN_SRC emacs-lisp (global-set-key (kbd "C-c v") 'customize-variable) (global-set-key (kbd "C-c f") 'customize-face) #+END_SRC ** Backups Backups are so important that they should be described right after the shortcut to this file. #+BEGIN_SRC emacs-lisp (setq backup-directory-alist `((".*" . ,temporary-file-directory)) auto-save-file-name-transforms `((".*" ,temporary-file-directory t)) backup-by-copying t ; Don't delink hardlinks version-control t ; Use version numbers on backups delete-old-versions t ; Automatically delete excess backups kept-new-versions 20 ; how many of the newest versions to keep kept-old-versions 5 ; and how many of the old ) #+END_SRC ** Initial and default frames We set the dimensions of the inital and default frames. #+BEGIN_SRC emacs-lisp (add-to-list 'default-frame-alist '(width . 100)) (add-to-list 'default-frame-alist '(height . 50)) (add-to-list 'initial-frame-alist '(width . 100)) (add-to-list 'initial-frame-alist '(height . 50)) #+END_SRC *** GNU/Linux These settings affect the first and subsequent frames spawned by Emacs in GNU/Linux. Frame transparency increases when focus is lost. #+BEGIN_SRC emacs-lisp (when (and (display-graphic-p) (string-equal system-type "gnu/linux")) (set-frame-parameter (selected-frame) 'alpha '(90 . 50)) (add-to-list 'default-frame-alist '(alpha . (90 . 50)))) #+END_SRC ** Secrets #+INCLUDE: ./secrets.org #+BEGIN_SRC emacs-lisp (setq user-full-name "Marius Peter" user-mail-address "blendoit@gmail.com") #+END_SRC * Global key bindings The following bindings strive to further enhance CUA[fn::Common User Access.] mode. ** Navigation #+BEGIN_SRC emacs-lisp (global-set-key (kbd "C-`") 'delete-other-windows) (global-set-key (kbd "C-s") 'save-buffer) (global-set-key (kbd "C-r") 'counsel-recentf) #+END_SRC *** ~find-file~ Open file with ~C-o~. #+BEGIN_SRC emacs-lisp (global-set-key (kbd "C-o") 'find-file) #+END_SRC *** ~counsel-locate~ Locate file. #+BEGIN_SRC emacs-lisp (global-set-key (kbd "C-c l") 'counsel-locate) #+END_SRC *** Exit behaviours The following bindings lead to more natural exit behaviors. #+BEGIN_SRC emacs-lisp (defun delete-window-or-previous-buffer () "Delete window; if sole window, previous buffer." (interactive) (if (> (length (window-list)) 1) (delete-window) (previous-buffer))) (global-set-key (kbd "C-w") 'delete-window-or-previous-buffer) (global-set-key (kbd "C-q") 'save-buffers-kill-terminal) #+END_SRC ** Mouse zoom The typical binding on both GNU/Linux and MS Windows is adequate here: ~C-=~ to zoom in, ~C--~ to zoom out. It seems that starting with Emacs 27.1, Control + mousewheel works. #+BEGIN_SRC emacs-lisp (global-set-key (kbd "C--") 'text-scale-decrease) (global-set-key (kbd "C-=") 'text-scale-increase) (global-set-key (kbd "C-+") 'text-scale-increase) #+END_SRC * Packages Packages are collections of =.el= files providing added functionality to Emacs. ** Meta How do we bootstrap packages? First, let's figure out: 1. Where we get our packages from 2. How we upgrade packages 3. How we ensure our required packages are installed *** Package archives List of package archives. #+NAME: package-archives #+BEGIN_SRC emacs-lisp (require 'package) (add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t) (add-to-list 'package-archives '("org" . "https://orgmode.org/elpa/") t) (package-initialize) #+END_SRC *** TODO Convenient package update One-function rollup of upgradeable package tagging, download and lazy install. #+BEGIN_SRC emacs-lisp #+END_SRC *** ~use-package~ We ensure =use-package= is installed, as well as all packages described in this configuration file. #+BEGIN_SRC emacs-lisp (unless (package-installed-p 'use-package) (package-refresh-contents) (package-install 'use-package) (eval-when-compile (require 'use-package))) (setq use-package-always-ensure t) (require 'use-package) (require 'bind-key) #+END_SRC *** ~delight~ With this package, we suppress the mention of certain minor modes in the mode line/powerline. We include it in Meta (cf. [[Meta]]) because it concerns all other packages. #+BEGIN_SRC emacs-lisp (use-package delight) (delight '((org-indent-mode) (bufface-mode))) #+END_SRC ** ~org-mode~ Phew, I can finally introduce Org mode! I am so *excited*. Org mode replaces aword processor, a presentation creator, and a spreadsheet editor. IMHO, the spreadsheet ability captures more than 80% use cases wherein one wishes to include a table in a text document destined for physical publication. (It is clear that Excel spreadsheets are /not/ destined for physical publication---simply attempt to print an Excel spreadsheet with the default settings.) In my opinion, Org mode matches all /useful/ features of the Microsoft Office suite 1-to-1. What follows are customizations designed to make Org mode behave more like Microsoft Word. The end goal is, once again, to draw as many new users to Emacs as possible! *** Basic customization Org base directory is in user home on GNU/Linux, or in =AppData= in MS Windows. #+NAME: org-basic #+BEGIN_SRC emacs-lisp (setq org-directory (concat user-emacs-directory "~/org")) #+END_SRC First, we hide markup symbols for *bold*, /italic/, _underlined_ and +strikethrough+ text, and ensure our document appears indented upon loading:[fn::It /appears/ indented, but the underlying plaintext file does not contain tab characters!] For the time being, I will in fact display emphasis markers, because hiding them corrupts tables. #+NAME: org-basic #+BEGIN_SRC emacs-lisp (setq org-hide-emphasis-markers nil) (setq org-startup-indented t) #+END_SRC Then, we customize Org headings to emulate WYSIWYG[fn::What You See Is What You Get (input and output are identical), as opposed to What You See Is What You Mean (the input contains instructions that can modify the output).] behavior normally found in Word: *** Invisible edits #+BEGIN_SRC emacs-lisp (setq org-catch-invisible-edits t) #+END_SRC *** Agenda The agenda displays a chronological list of headings across all agenda files for which the heading or body contain a matching =org-time-stamp=.[fn::An =org-time-stamp= can be inserted with ~C-c .~ (period)] #+BEGIN_SRC emacs-lisp (global-set-key (kbd "C-c a") 'org-agenda-list) (defun my/find-diary-file () "Load `org-agenda-diary-file'." (interactive) (find-file org-agenda-diary-file)) (global-set-key (kbd "C-c d") 'my/find-diary-file) #+END_SRC *** Timestamps More literary timestamps are exported to LaTeX using the following custom format: #+BEGIN_SRC emacs-lisp (setq org-time-stamp-custom-formats '("%d %b, %Y (%a)" . "%d %b, %Y (%a), at %H:%M")) #+END_SRC *** LaTeX export The following makes =TODO= items appear red and =CLOSED= items appear green in Org's LaTeX exports. Very stylish, much flair! #+BEGIN_SRC emacs-lisp (setq org-latex-active-timestamp-format "\\textcolor{SteelBlue}{\\texttt{%s}}") (setq org-latex-inactive-timestamp-format "\\textcolor{ForestGreen}{\\texttt{%s}}") #+END_SRC *** Publish In the following /alist/ (association list), we describe the projects publishable via =org-publish=. We separate the publishing of =.org= files and attachments, because an online tutorial recommended we do so. #+BEGIN_SRC emacs-lisp (require 'ox-publish) (setq org-publish-project-alist '( ("Safran-VIP-html" :base-directory "~/org/WORK/Safran/programs/B787/VIP/doc/org/" :base-extension "org" :publishing-directory "~/org/WORK/Safran/programs/B787/VIP/doc/wiki/" :recursive t :publishing-function org-html-publish-to-html :auto-preamble t :auto-sitemap t :sitemap-title "" ) ("Safran-VIP-static" :base-directory "~/org/WORK/Safran/programs/B787/VIP/doc/org/" :base-extension "css\\|js\\|png\\|jpg\\|gif\\|pdf\\|mp3\\|mp4\\|ogg\\|swf" :publishing-directory "~/org/WORK/Safran/programs/B787/VIP/doc/wiki/" :recursive t :publishing-function org-publish-attachment ) ("Safran-VIP-all" :components ("Safran-VIP-html" "Safran-VIP-static")) ("Safran-MA700-html" :base-directory "~/org/WORK/Safran/programs/MA700/doc/org/" :base-extension "org" :publishing-directory "~/org/WORK/Safran/programs/MA700/doc/wiki/" :recursive t :publishing-function org-html-publish-to-html :auto-preamble t :auto-sitemap t :sitemap-title "" ) ("Safran-MA700-static" :base-directory "~/org/WORK/Safran/programs/MA700/doc/org/" :base-extension "css\\|js\\|png\\|jpg\\|gif\\|pdf\\|mp3\\|mp4\\|ogg\\|swf" :publishing-directory "~/org/WORK/Safran/programs/MA700/doc/wiki/" :recursive t :publishing-function org-publish-attachment ) ("Safran-MA700-all" :components ("Safran-MA700-html" "Safran-MA700-static")))) #+END_SRC *** Export This creates a shorter binding for the most common Org export: Org \rightarrow LaTeX \rightarrow PDF. #+BEGIN_SRC emacs-lisp (defun my/org-quick-export () "Org export to PDF and open. This basically reimplements `C-c C-e l o'." (interactive) (org-open-file (org-latex-export-to-pdf))) (global-set-key (kbd "C-c e") 'my/org-quick-export) #+END_SRC ** TODO ~evil-mode~ Forgive me, for I have sinned. This is the 2^{nd} most significant customization after ~org-mode~. Enabling ~evil-mode~ completely changes editing keys. For more information on =vi= keybindings, visit [[https://hea-www.harvard.edu/~fine/Tech/vi.html]]. #+BEGIN_SRC emacs-lisp (use-package evil) ; (setq evil-toggle-key "C-c d") ; devil... ; (evil-mode 1) #+END_SRC ** Spelling, completion, and snippets The following customizations open the doors to vastly increased typing speed and accuracy. *** ~flycheck~ Syntax highlighting for Emacs. #+NAME: flycheck #+BEGIN_SRC emacs-lisp (use-package flycheck :delight) (global-flycheck-mode) #+END_SRC *** TODO ~flyspell~ #+NAME: flyspell #+BEGIN_SRC emacs-lisp (use-package flyspell :delight) (add-hook 'text-mode-hook 'flyspell-mode) #+END_SRC *** ~yasnippet~ #+NAME: yasnippet #+BEGIN_SRC emacs-lisp (use-package yasnippet :delight) (yas-global-mode 1) #+END_SRC *** ~company~ #+NAME: company #+BEGIN_SRC emacs-lisp ; (add-hook 'after-init-hook 'global-company-mode) #+END_SRC ** Utilities *** ~magit~ Wonderful Git porcelain for Emacs. Enables the administration of a Git repository in a pain-free way. #+BEGIN_SRC emacs-lisp (use-package magit :bind ("C-c g" . magit-status)) #+END_SRC *** ~projectile~ This enables us to better manage our =.git= projects. #+BEGIN_SRC emacs-lisp (use-package projectile :bind ("C-c p" . 'projectile-command-map) :delight :init (projectile-mode 1) (setq projectile-completion-system 'ivy)) #+END_SRC *** ~which-key~ #+BEGIN_SRC emacs-lisp (use-package which-key :init (which-key-mode) :delight) #+END_SRC *** ~dumb-jump~ #+BEGIN_SRC emacs-lisp (use-package dumb-jump) (add-hook 'xref-backend-functions #'dumb-jump-xref-activate) #+END_SRC *** ~undo-tree~ #+BEGIN_SRC emacs-lisp (use-package undo-tree :delight) (global-undo-tree-mode) #+END_SRC *** ~ivy~ Auto completion. #+BEGIN_SRC emacs-lisp (use-package ivy :delight :config (setq ivy-use-virtual-buffers t ivy-count-format "%d/%d " enable-recursive-minibuffers t)) (ivy-mode t) #+END_SRC **** ~counsel~ Wonderful counsellor! #+BEGIN_SRC emacs-lisp (use-package counsel :bind ("M-x" . counsel-M-x) :delight :config (counsel-mode t)) (global-set-key (kbd "C-f") 'counsel-grep-or-swiper) #+END_SRC **** ~swiper~ #+BEGIN_SRC emacs-lisp (use-package swiper :bind (("C-f" . counsel-grep-or-swiper))) #+END_SRC ** File formats *** ~csv-mode~ #+BEGIN_SRC emacs-lisp (use-package csv-mode) #+END_SRC *** ~pdf-tools~ #+BEGIN_SRC emacs-lisp (use-package pdf-tools) ;; (pdf-tools-install) #+END_SRC *** ~ledger~ #+BEGIN_SRC emacs-lisp (use-package ledger-mode :bind ("C-c r" . ledger-report) ("C-c C" . ledger-mode-clean-buffer)) #+END_SRC *** ~gnuplot~ #+BEGIN_SRC emacs-lisp (use-package gnuplot) #+END_SRC ** Cosmetics *** ~dashboard~ We replace the standard welcome screen with our own. #+BEGIN_SRC emacs-lisp (setq inhibit-startup-message t) (use-package dashboard :delight :config (dashboard-setup-startup-hook) (setq dashboard-startup-banner (concat user-emacs-directory "img/Safran_logo.svg")) (setq dashboard-items '((recents . 5) (projects . 5))) (setq dashboard-banner-logo-title "A modern professional text editor.")) #+END_SRC *** ~powerline~ #+NAME: powerline #+BEGIN_SRC emacs-lisp (use-package powerline) (powerline-default-theme) #+END_SRC *** TODO Sidebar Get inspiration from ~ibuffer-sidebar~ and create a better sidebar. #+BEGIN_SRC emacs-lisp ;; (load-file) #+END_SRC *** Better parentheses #+BEGIN_SRC emacs-lisp (use-package rainbow-delimiters :config (add-hook 'prog-mode-hook #'rainbow-delimiters-mode)) (electric-pair-mode) #+END_SRC *** ~all-the-icons~ #+BEGIN_SRC emacs-lisp (use-package all-the-icons) #+END_SRC *** ~rainbow-mode~ This highlights hexadecimal numbers which look like colors, in that same color. #+BEGIN_SRC emacs-lisp (use-package rainbow-mode :init (add-hook 'prog-mode-hook 'rainbow-mode) :delight) #+END_SRC * Themes We load my custom theme. #+BEGIN_SRC emacs-lisp (setq custom-theme-directory (concat user-emacs-directory "themes/")) (load-theme 'blendoit-light) ; (load-theme 'blendoit-dark) #+END_SRC ** My light and dark themes A highly legible unambiguous and thoughtful theme. *** Colors The default face is a black foreground on a white background, this matches MS Word. We are striving for a simple, intuitive color scheme. Most of the visual cues derived from color are identical in both light and dark themes (Table [[theme-color-1]]). #+NAME: theme-color-1 #+CAPTION[Light and dark themes' colors]: Light and dark themes' colors. #+ATTR_LATEX: :booktabs t | Color | ~blendoit-light~ | ~blendoit-dark~ | |---------------------------------+---------------------------------+--------------------| | Black | default text | default background | | Lighter shades | lesser headers | /n/a/ | | White | default background | default text | | Darker shades | /n/a/ | lesser headers | | \color{Red} Red | negative | /same/ | | \color{Tomato} Tomato | timestamp `TODO' | /same/ | | \color{Green} Green | positive | /same/ | | \color{ForestGreen} ForestGreen | timestamp `DONE' | /same/ | | \color{Blue} Blue | interactable content; links | /same/ | | \color{SteelBlue} SteelBlue | anything Org mode; anchor color | /same/ | | \color{DeepSkyBlue} DeepSkyBlue | ~highlight~ | /same/ | | \color{DodgerBlue} DodgerBlue | ~isearch~ | /same/ | | \color{Purple} Purple | | | *** Cursors In order to imitate other modern text editors, we resort to a blinking bar cursor. We choose red, the most captivating color, because the cursor is arguably the region on our screen: 1. most often looked at; 2. most often searched when lost. In files containing only ~fixed-pitch~ fonts (i.e. files containing only code), the cursor becomes a high-visibility box. In files containing a mix of ~variable-pitch~ and ~fixed-pitch~ fonts, the cursor is a more MS Word-like bar. #+BEGIN_SRC emacs-lisp (setq-default cursor-type (quote bar)) #+END_SRC *** Faces - ~default~: Hack - Legible, modern monospace font - Strict, sharp, uncompromising - ~fixed-pitch~: Hack - ~variable-pitch~: Liberation Sans - Libre alternative to Arial - ~org-block~: Hermit - Slightly wider than Hack - More opinionated shapes - Very legible parentheses **** ~variable-pitch-mode~ We use ~variable-pitch-mode~ for appropriate modes. #+BEGIN_SRC emacs-lisp (add-hook 'org-mode-hook 'variable-pitch-mode) (add-hook 'info-mode-hook 'variable-pitch-mode) #+END_SRC **** TODO Default font size Make default font size larger on displays of which the resolution is greater than =1920x1080=. #+BEGIN_SRC emacs-lisp #+END_SRC ** TODO ~minimal~ * Editing preferences These customizations enhance editor usability. A line of text is considered `filled' when it reaches 79 characters in length. #+BEGIN_SRC emacs-lisp (setq-default fill-column 79) #+END_SRC We replace the longer ~yes-or-no-p~ questions with more convenient ~y-or-n-p~. #+BEGIN_SRC emacs-lisp (defalias 'yes-or-no-p 'y-or-n-p) #+END_SRC Disable minibuffer scroll bar. #+BEGIN_SRC emacs-lisp (set-window-scroll-bars (minibuffer-window) nil nil) #+END_SRC Save cursor location in visited buffer after closing it or Emacs. #+BEGIN_SRC emacs-lisp (save-place-mode 1) #+END_SRC Saving any file in ~user-emacs-directory~ (by default on Emacs, =~/emacs.d=) shall byte-recompile the entire =.emacs/= directory, for increased speed. #+BEGIN_SRC emacs-lisp ;; (defun my/byte-compile-user-config () ;; "Byte-compile dotfiles if current file is in `user-emacs-directory'. ;; Also tangles `my-literate-config'." ;; (interactive) ;; (if (string-equal buffer-file-name my/literate-config) ;; (org-babel-tangle)) ;; (if (string-prefix-p user-emacs-directory default-directory) ;; (byte-recompile-directory (concat user-emacs-directory "blendoit/") 0))) ;; (add-hook 'after-save-hook my/byte-compile-user-config) #+END_SRC ** Clean up menus Originally, I wished to inhibit certain entries in the GUI menus. Not worth the effort at this time. #+BEGIN_SRC emacs-lisp (menu-bar-mode -1) (tool-bar-mode -1) #+END_SRC ** Coding standards This is just a better default. Don't @ me. #+BEGIN_SRC emacs-lisp (setq c-default-style "linux" c-basic-offset 4) #+END_SRC ** Dividers This ensures users can resize windows using the GUI. #+BEGIN_SRC emacs-lisp (menu-bar-bottom-and-right-window-divider) #+END_SRC ** Tabs #+BEGIN_SRC emacs-lisp #+END_SRC ** ~auto-fill~ Automatically break lines longer than =fill-column=. #+BEGIN_SRC emacs-lisp (add-hook 'org-mode-hook 'turn-on-auto-fill) #+END_SRC ** Recent files #+BEGIN_SRC emacs-lisp (recentf-mode 1) (setq recentf-max-menu-items 25) (setq recentf-max-saved-items 25) (run-at-time nil (* 5 60) 'recentf-save-list) #+END_SRC ** ~pop-up-frames~ #+BEGIN_SRC emacs-lisp ; (setq pop-up-frames (quote graphic-only)) #+END_SRC * Late setup ** Profiling --- stop #+BEGIN_SRC emacs-lisp ;; (profiler-stop) #+END_SRC ** Profiling --- report #+BEGIN_SRC emacs-lisp ;; (profiler-report) #+END_SRC * Conclusion In this configuration file, we described a series of customization steps taken to make Emacs more palatable to modern IDE users.