#+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 \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}. \end{abstract} # * Test # #+BEGIN_SRC emacs-lisp # (org-babel-load-file "~/.emacs.d/blendoit/test.org") # #+END_SRC # * Blending Linux and Windows # - The GNU Emacs cabal is attempting to create a complete OS out of a text # editor. # - Microsoft has a notorious /embrace, extend, extinguish/ approach when it # comes to rival technologies. # - Both are simultaneously possible. * 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. #+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 ** 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 ** Refresh configuration Add an ~after-save-hook~ that tangles and loads this literary configuration. # #+NAME: emacs-client # #+BEGIN_SRC emacs-lisp # (defun my/tangle-and-load-blendoit-init() # (org-babel-load-file "~/.emacs.d/blendoit/blendoit-init.org") # ) # (add-hook 'after-save-hook 'my/tangle-and-load-blendoit-init) # (my/tangle-and-load-blendoit-init) # #+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 . #+NAME: custom-file-location #+BEGIN_SRC emacs-lisp (setq custom-file "~/.emacs.d/init-custom.el") (load custom-file) #+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 ** Customization shortcuts We begin by defining a user shortcut to this very file: #+NAME: shortcut-config #+BEGIN_SRC emacs-lisp (defun find-init-blendoit () "Jump to this very file." (interactive) (find-file "~/.emacs.d/blendoit/blendoit-init.org")) (global-set-key (kbd "C-c c") 'find-init-blendoit) #+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 ** Secrets #+INCLUDE: ./secrets.org #+BEGIN_SRC emacs-lisp (setq user-full-name "Marius Peter" user-mail-address "blendoit@gmail.com") #+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 * 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-b") 'ibuffer-sidebar-toggle-sidebar) (global-set-key (kbd "C-r") 'counsel-recentf) ; (global-set-key (kbd "C-n") 'make-frame) ; 7aram! #+END_SRC *** Graphical file open Open file with ~C-o~. #+BEGIN_SRC emacs-lisp (global-set-key (kbd "C-o") 'menu-find-file-existing) #+END_SRC We trick Emacs into opening file dialog with ~C-o~ instead of prompting for a file in minibuffer. #+BEGIN_SRC emacs-lisp (defadvice find-file-read-args (around find-file-read-args-always-use-dialog-box act) "Simulate invoking menu item as if by the mouse; see `use-dialog-box'." (let ((last-nonmenu-event nil)) ad-do-it)) #+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)) 2) (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. #+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. ** Package archives List of package archives. #+NAME: packages #+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 ** ~use-package~ 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) #+END_SRC ** TODO Convenient package update One-function rollup of upgradeable package tagging, download and lazy install. ** ~ivy~ Auto completion. #+BEGIN_SRC emacs-lisp (use-package ivy :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) :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 ** ~evil-mode~ Forgive me, for I have sinned. #+BEGIN_SRC emacs-lisp (use-package evil) ;; (evil-mode 1) #+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 "~/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 t) (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) #+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 =CLOSED= items appear green in LaTeX. Very stylish, much flair! #+BEGIN_SRC emacs-lisp (setq org-latex-inactive-timestamp-format "\\textcolor{ForestGreen!60}{\\textit{%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")))) (add-to-list 'org-latex-packages-alist '("table" "xcolor" t ("pdflatex"))) (add-to-list 'org-latex-packages-alist '("AUTO" "babel" t ("pdflatex"))) (add-to-list 'org-latex-packages-alist '("AUTO" "polyglossia" t ("xelatex" "lualatex"))) #+END_SRC *** Export This creates a shorter binding for the most common Org export: Org \rightarrow LaTeX \rightarrow PDF. #+BEGIN_SRC emacs-lisp (defun blendoit-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") 'blendoit-org-quick-export) #+END_SRC ** ~undo-tree~ #+BEGIN_SRC emacs-lisp (global-undo-tree-mode) #+END_SRC ** ~dumb-jump~ #+BEGIN_SRC emacs-lisp (use-package dumb-jump) (add-hook 'xref-backend-functions #'dumb-jump-xref-activate) #+END_SRC ** ~gnuplot~ #+BEGIN_SRC emacs-lisp (use-package gnuplot) #+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 ** ibuffer-sidebar #+BEGIN_SRC emacs-lisp ;; (use-package ibuffer-sidebar) ;; (ibuffer-sidebar-show-sidebar) ; :bind ("mouse-1" . ibuffer-mouse-visit-buffer) ; :bind ("mouse-3" . ibuffer-mouse-toggle-mark)) ; (add-hook 'ibuffer-sidebar-mode-hook ; (lambda () ; (local-unset-key (quote mouse-1)) ; (local-unset-key (quote mouse-2)) ; (local-set-key (quote mouse-1) (quote ibuffer-mouse-visit-buffer)) ; (local-set-key (quote mouse-2) (quote ibuffer-mouse-toggle-mark)))) #+END_SRC ** TODO Sidebar Get inspiration from ~ibuffer-sidebar~ and create a better sidebar. #+BEGIN_SRC emacs-lisp ;; (load-file) #+END_SRC ** ~which-key~ #+BEGIN_SRC emacs-lisp (use-package which-key :init (which-key-mode) ;; :config ;; (setq which-key-idle-delay 1000) ;; (setq which-key-idle-secondary-delay 0.05) ;; (setq which-key-show-early-on-C-h t) ) #+END_SRC ** ~company~ #+NAME: company #+BEGIN_SRC emacs-lisp ; (add-hook 'after-init-hook 'global-company-mode) #+END_SRC ** ~flycheck~ #+NAME: flycheck #+BEGIN_SRC emacs-lisp (use-package flycheck :init (global-flycheck-mode)) #+END_SRC ** ~csv-mode~ #+BEGIN_SRC emacs-lisp (use-package csv-mode) #+END_SRC ** ~json-mode~ Oí, Jason! Not needed in 27.1? #+BEGIN_SRC emacs-lisp ; (use-package json-mode) #+END_SRC ** ~magit~ #+BEGIN_SRC emacs-lisp (use-package magit :bind ("C-c g" . magit-status)) #+END_SRC ** ~pdf-tools~ #+BEGIN_SRC emacs-lisp (use-package pdf-tools) ;; (pdf-tools-install) #+END_SRC ** ~dashboard~ We replace the standard welcome screen with our own. #+BEGIN_SRC emacs-lisp (setq inhibit-startup-message t) (use-package dashboard :config (dashboard-setup-startup-hook) (setq dashboard-startup-banner "~/.emacs.d/blendoit/img/Safran_logo.svg") (setq dashboard-items '((recents . 5) (projects . 5))) (setq dashboard-banner-logo-title "A modern professional text editor.")) #+END_SRC ** ~rainbow-mode~ This highlights hexadecimal numbers which look like colors, in that same color. #+BEGIN_SRC emacs-lisp (use-package rainbow-mode :ensure t :init (add-hook 'prog-mode-hook 'rainbow-mode)) #+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) :init (projectile-mode 1) (setq projectile-completion-system 'ivy)) #+END_SRC ** ~all-the-icons~ #+BEGIN_SRC emacs-lisp (use-package all-the-icons) #+END_SRC ** Better parentheses #+BEGIN_SRC emacs-lisp (show-paren-mode 1) (setq show-paren-delay 0) (use-package rainbow-delimiters :config (add-hook 'prog-mode-hook #'rainbow-delimiters-mode)) (electric-pair-mode) #+END_SRC * Cosmetics ** 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. *** Default cursor 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 box)) (setq-default mixed-pitch-variable-pitch-cursor (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 *** ~mixed-pitch~ Fixed-pitch and variable-pitch faces will be used intelligently in all hooked modes. ~mixed-pitch~ is good at automatically determining which faces should be monospaced and proportional; I prefer to define each face in my custom theme. #+BEGIN_SRC emacs-lisp ;; (use-package mixed-pitch ;; :hook ((org-mode . mixed-pitch-mode) ;; (Info-mode . mixed-pitch-mode))) (add-hook 'text-mode-hook 'variable-pitch-mode) (add-hook 'info-mode-hook 'variable-pitch-mode) #+END_SRC ** Initial frame 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 ** Theme My custom themes. #+BEGIN_SRC emacs-lisp (setq custom-theme-directory "~/.emacs.d/blendoit/themes/") (load-theme 'blendoit-light) ; (load-theme 'blendoit-dark) #+END_SRC *** ~blendoit-light~ A highly legible and unambiguous theme. The default face is a black foreground on a white background, this matches with MS Word. - Red :: TODO; cursor; negative; danger - Green :: DONE; positive - Blue :: document structure - Purple :: ~org-mode~ elements *** ~blendoit-dark~ Same principle * 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 ** 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 (setq menu-bar-mode t) #+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.