# -*- mode: org; -*- #+TITLE: Smart Documents #+AUTHOR: Marius Peter #+DATE: <2020-11-04 Wed> #+EMAIL: blendoit@gmail.com #+STARTUP: showall #+SETUPFILE: ~/.emacs.d/templates/documents/default.org #+INCLUDE: ~/.emacs.d/templates/documents/default.org_title #+LATEX: \begin{abstract} The idea of /Smart Documents/ came to me as I was reflecting on how to improve the document creation process in my workplace. The GNU Emacs editor had captured my imagination and I wanted to create an accessible and highly productive text editor to benefit my organization. In this paper, I'll lay out my vision for the /Smart Document/, a file containing both text destined to the reader, and code describing how to update, validate, and present this text; then, I'll weave my personal GNU Emacs customizations with a tutorial. This paper is a /Smart Document/ itself! #+LATEX: \end{abstract} * Introduction GNU Emacs is most often used as a text editor. It would be unfair to say it is just that, because Emacs is capable of so much more. The utmost level of customization is afforded by enabling the user to rewrite /any/ part of the source code and observe the editor's modified behavior 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 /modes/ have been written to interact with hundreds of file formats, including =.txt=, =.pdf=, =.jpg=, =.csv=, and =.zip= just to name a few. This paper itself was written in /Org mode/, a collection of functions enabling the harmonious mixing of code and comments in view of publication: this is the endgame of /literate programming/, and the basis of my vision for /Smart Documents/. The following sections were laid out very deliberately. When we start Emacs, the source code blocks contained in this document are evaluated sequentially---our editing environment is constructed in real time as we execute the blocks in order. For instance, we only begin loading packages once we ensured ~use-package~ is working properly.[fn::For more information on the detailed steps Emacs takes upon starting, refer to [[https://www.gnu.org/software/emacs/manual/html_node/elisp/Startup-Summary.html]].] Customizing Emacs goes far, far beyond rewriting sections of this document---feel free to experiment and discover. Here are three commands that will help you understand all the symbols in this file, if you are browsing this paper within Emacs itself: - ~C-h f~ :: describe function - ~C-h v~ :: describe variable - ~C-h k~ :: describe key You can always press ~f1~ to access Emacs built-in help. * First-time setup The following code blocks are normally evaluated once---upon starting Emacs for the first time. ** TODO Unpacking our literate configuration :PROPERTIES: :sd-unpack-path: sd.el :END: #+BEGIN_SRC emacs-lisp (defvar sd-packed-p t "Boolean to track literate configuration packed/unpacked status.") (defvar sd-unpack-sections (org-property-values "sd-unpack-path") "List of target sections in `my/literate-config' to be unpacked.") (defun sd-unpack-sections () "Unpack literate configuration into `emacs-user-directory'." (interactive) (mapcar 'sd-unpack sd-unpack-sections) ) (defun sd-unpack-section (&optional section) "Unpack SECTION into `user-emacs-directory'. If nil, unpack section under point. Make go through list of headings and unpack first matching SECTION." (interactive) (if (not section) (insert (concat "\nThe contents of this Section was automatically moved to\n=" user-emacs-directory (org-entry-get nil "sd-unpack-path") "=.\n" "Use `sd-pack-section' to copy the contents back into this section.")))) (defun sd-pack-section () "Pack SECTION into `my/literate-config'." (interactive) (message "foobar!!!")) (global-set-key (kbd "C-t") 'sd-pack-section) (sd-unpack "init.el") #+END_SRC ** TODO User details One advantage of working with /Smart Documents/ is that they can automatically be populated with our details in the header, footer, or other appropriate element. #+NAME: user-details-get #+BEGIN_SRC emacs-lisp (setq user-full-name "Marius Peter") (defun my/user-details-get () "Get user details." (setq user-full-name (read-string "Enter full user name:")) (setq user-mail-address (read-string "Enter user e-mail address:")) (message "Successfully captured user details.")) #+END_SRC #+NAME: user-details #+BEGIN_SRC emacs-lisp (defun my/tokenize-user-details () "Tokenize user details." (cons 'user-full-name user-full-name)) (unless (file-exists-p (concat user-emacs-directory "meta/user-details")) (setq user-details '(user-full-name user-mail-address)) (append-to-file "Foobar\n" nil "~/.emacs.d/meta/foobar")) #+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 :tangle yes (when (string-equal system-type "windows-nt") (add-to-list 'exec-path "C:/Users/marius.peter/PortableGit/bin/")) #+END_SRC * Early setup ** The first file to load :PROPERTIES: :sd-unpack-path: init.el :END: The contents of this Section was automatically moved to =~/.emacs.d/init.el=. Use `sd-pack-section' to copy the contents back into this section. This is the very first user-editable file loaded by Emacs.[fn::This feature became available in version 27.1.] In it, we disable GUI elements that would otherwise be loaded and displayed once Emacs is ready to accept user input. It can be found here: [[file:early-init.el]] ** The second file to load :PROPERTIES: :sd-unpack-path: early-init.el :END: #+BEGIN_QUOTE Traditionally, file =~/.emacs= is used as the init file, although Emacs also looks at =~/.emacs.el=, =~/.emacs.d/init.el=, =~/.config/emacs/init.el=, or other locations. From the GNU website[fn::[[https://www.gnu.org/software/emacs/manual/html_node/emacs/Init-File.html]]] #+END_QUOTE This file can be found here: [[file:init.el]] If no file is found, Emacs then loads in its purely vanilla state. ** 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 :tangle yes ; (profiler-start) #+END_SRC ** Jumping to this file We begin by defining a function to open this very file. #+NAME: shortcut-config #+BEGIN_SRC emacs-lisp :tangle yes (defun my/find-literate-config () "Jump to this very file." (interactive) (find-file (concat my/literate-config ".org"))) #+END_SRC ** Speeding up the next startup #+BEGIN_SRC emacs-lisp :tangle yes (defun byte-compile-literate-config () "Byte compile our literate configuration file." (delete-file (concat my/literate-config ".elc")) (delete-file (concat my/literate-config ".el")) (org-babel-tangle-file (concat my/literate-config ".org")) (byte-compile-file (concat my/literate-config ".el"))) (add-hook 'kill-emacs-hook 'byte-compile-literate-config) #+END_SRC ** Meta-files In this section, we'll be tidying up the =.emacs.d/= directory---by default, many Emacs packages create files useful for themselves in our ~user-emacs-directory~. This leads to undesirable clutter. Certain packages create files that log recently visited files ([[Recently visited files]]); log location of known projects ([[Projects' bookmarks]]); log location in recently visited files ([[Location in previously visited file]]) The commonality between all these files is that they tend to reference... other files. Thus, I decided to refer to them as meta-files. First, let's designate a folder to collect our meta-files together: #+BEGIN_SRC emacs-lisp :tangle yes (setq my/meta-files-location (concat user-emacs-directory "meta/")) #+END_SRC *** Recently visited files #+BEGIN_SRC emacs-lisp :tangle yes (setq recentf-save-file (concat my/meta-files-location "recentf")) #+END_SRC *** Projects' bookmarks #+BEGIN_SRC emacs-lisp :tangle yes (setq projectile-known-projects-file (concat my/meta-files-location "projectile-bookmarks.eld")) #+END_SRC *** Location in previously visited file #+BEGIN_SRC emacs-lisp :tangle yes (setq save-place-file (concat my/meta-files-location "places")) #+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 :tangle yes (setq custom-file (concat user-emacs-directory "custom.el")) (load custom-file) #+END_SRC ** Backups Backups are very important! #+BEGIN_SRC emacs-lisp :tangle yes (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 initial frame: #+BEGIN_SRC emacs-lisp :tangle yes (add-to-list 'initial-frame-alist '(width . 100)) (add-to-list 'initial-frame-alist '(height . 50)) #+END_SRC We also set the dimensions of subsequent frames: #+BEGIN_SRC emacs-lisp :tangle yes (add-to-list 'default-frame-alist '(width . 50)) (add-to-list 'default-frame-alist '(height . 30)) #+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 :tangle yes (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 The code contained in the =secrets.org= file is loaded by Emacs, but not rendered in this PDF for the sake of privacy. It contains individually identifying information such as names and e-mail addresses, which are used to populate Org templates (Section [[~org-mode~]]). You need to create this =secrets.org= file, as it is ignored by =git= by default. #+BEGIN_SRC emacs-lisp :tangle yes (org-babel-load-file "~/.emacs.d/secrets.org") #+END_SRC * Keyboard shortcuts The following bindings strive to further enhance CUA mode.[fn::Common User Access. This is a term coined by IBM which has influenced user navigation cues on all modern desktop OSes. From IBM's CUA, we get the =Ctrl-c= and =Ctrl-v= keyboard shortcuts.] #+BEGIN_SRC emacs-lisp :tangle yes (cua-mode) #+END_SRC What follows are the most useful keybindings, as well as the keybindings to the functions we defined ourselves. It doesn't matter if we haven't defined the functions themselves yet; Emacs will accept a keybinding for any symbol and does not check if the symbol's function definition exists, until the keybinding is pressed. ** Files *** Save a file #+BEGIN_SRC emacs-lisp :tangle yes (global-set-key (kbd "C-s") 'save-buffer) #+END_SRC *** Open a file #+BEGIN_SRC emacs-lisp :tangle yes (global-set-key (kbd "C-o") 'find-file) #+END_SRC *** List open files #+BEGIN_SRC emacs-lisp :tangle yes (global-set-key (kbd "C-b") 'ivy-switch-buffer) #+END_SRC *** Open this very file (Function defined in Section [[Jumping to this file]]) #+BEGIN_SRC emacs-lisp :tangle yes (global-set-key (kbd "C-c c") 'my/find-literate-config) #+END_SRC *** Open a recently visited file #+BEGIN_SRC emacs-lisp :tangle yes (global-set-key (kbd "C-r") 'counsel-recentf) #+END_SRC *** Locate a file #+BEGIN_SRC emacs-lisp :tangle yes (global-set-key (kbd "C-c l") 'counsel-locate) #+END_SRC *** Open the agenda #+BEGIN_SRC emacs-lisp :tangle yes (global-set-key [f5] 'org-agenda-list) #+END_SRC *** Open the diary #+BEGIN_SRC emacs-lisp :tangle yes (global-set-key [f6] '(lambda () "Load `org-agenda-diary-file'." (interactive) (find-file org-agenda-diary-file))) #+END_SRC *** Open Org mode document properties #+BEGIN_SRC emacs-lisp :tangle yes (global-set-key [f9] 'sd-document-properties) #+END_SRC ** Windows *** Close window and quit The following bindings lead to more natural window & frame exit behaviors. #+NAME: close-window-or-previous-buffer #+BEGIN_SRC emacs-lisp :tangle yes (global-set-key (kbd "C-w") '(lambda () "Delete window; if sole window, previous buffer." (interactive) (if (> (length (window-list)) 1) (delete-window) (previous-buffer)))) #+END_SRC ** Frame *** Make new frame #+BEGIN_SRC emacs-lisp :tangle yes (global-set-key (kbd "C-n") 'make-frame) #+END_SRC *** Make only frame #+BEGIN_SRC emacs-lisp :tangle yes (global-set-key (kbd "C-`") 'delete-other-windows) #+END_SRC *** Delete frame or kill Emacs #+NAME: delete-frame-or-kill-emacs #+BEGIN_SRC emacs-lisp :tangle yes (global-set-key (kbd "C-q") '(lambda () (interactive) "delete frame; if sole frame, kill Emacs." (if (> (length (frame-list)) 1) (delete-frame) (kill-emacs)))) #+END_SRC *** Open sidebar #+BEGIN_SRC emacs-lisp :tangle yes (global-set-key (kbd " ") 'sd-sidebar) #+END_SRC ** Text display *** 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 :tangle yes (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 ** Navigation =Alt= (=Meta=) is the privileged key for motion in a buffer. It is followed by an optional numerical argument, and a movement command. You may navigate in a buffer by keeping =Alt= pressed, optionally inputting a number from the keypad or number row, then pressing any of the following movement keys: =j=, =k=, =h=, and =l=. You will move in that direction in the amount of the numerical argument. #+NAME: keybinding-navigation #+CAPTION[Navigation keybindings]: Navigation keybindings. #+ATTR_LATEX: :booktabs t | | *Backwards* | *Forwards* | |-----------+-------------+------------| | Character | =M-h= | =M-l= | | Line | =M-k= | =M-j= | | Word | =M-f= | =M-b= | | Paragraph | =M-a= | =M-e= | We prevent Org mode from overriding preferred navigation keys. #+BEGIN_SRC emacs-lisp :tangle yes (local-unset-key (kbd "M-j")) (local-unset-key (kbd "M-k")) (local-unset-key (kbd "M-l")) (local-unset-key (kbd "M-h")) #+END_SRC *** Move down one line #+BEGIN_SRC emacs-lisp :tangle yes (global-set-key (kbd "M-j") 'next-line) #+END_SRC *** Move up one line #+BEGIN_SRC emacs-lisp :tangle yes (global-set-key (kbd "M-k") 'previous-line) #+END_SRC *** Move left one character #+BEGIN_SRC emacs-lisp :tangle yes (global-set-key (kbd "M-h") 'left-char) #+END_SRC *** Move right one character #+BEGIN_SRC emacs-lisp :tangle yes (global-set-key (kbd "M-l") 'right-char) #+END_SRC ** Customizing the editor *** Customize a variable #+BEGIN_SRC emacs-lisp :tangle yes (global-set-key (kbd "C-c v") 'customize-variable) #+END_SRC *** Customize a face #+BEGIN_SRC emacs-lisp :tangle yes (global-set-key (kbd "C-c f") 'customize-face) #+END_SRC ** One-click workflows A major advantage of the Emacs document production system: arbitrarily complicated functions can be assigned to very simple keybindings. This means we can automate workflows up to a pretty absurd level. *** Export to PDF PDF is probably the most prevalent file format for sharing static documents. **** Document #+BEGIN_SRC emacs-lisp :tangle yes (global-set-key (kbd "C-p") 'my/org-quick-export) #+END_SRC **** TODO Presentation #+BEGIN_SRC emacs-lisp :tangle yes #+END_SRC *** Indent buffer Indent buffer in every mode. #+BEGIN_SRC emacs-lisp :tangle yes (global-set-key [f12] 'sd-indent-buffer) #+END_SRC *** Beautify Org mode buffer Not only indent, but also clean up superfluous newlines. #+BEGIN_SRC emacs-lisp :tangle yes (local-set-key [f12] 'sd-org-beautify) #+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 :tangle yes (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 :tangle yes #+END_SRC *** ~use-package~ We ensure =use-package= is installed, as well as all packages described in this configuration file. #+BEGIN_SRC emacs-lisp :tangle yes (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 ** ~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.[fn::For more information on =vi= keybindings, visit [[https://hea-www.harvard.edu/~fine/Tech/vi.html]].] #+BEGIN_SRC emacs-lisp :tangle yes (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. *** Syntax checking We require a package to highlight syntax errors and warnings. The ~flycheck~ package ensures we are aware of all our code's syntactical shortcomings. #+NAME: flycheck #+BEGIN_SRC emacs-lisp :tangle yes (use-package flycheck) (global-flycheck-mode) #+END_SRC *** Spelling #+NAME: flyspell #+BEGIN_SRC emacs-lisp :tangle yes (use-package flyspell) (add-hook 'text-mode-hook 'flyspell-mode) #+END_SRC *** Insert template from keyword Thanks to ~yasnippet~, we can type certain keywords, then press =TAB=, to automatically insert a predefined text snippet. We can then navigate through the snippet by using == (next field) and == (previous field).[fn::== is synonymous with pressing shift-tab.] For instance: typing =src= then pressing =TAB= will expand the keyword to the following text: : #+BEGIN_SRC emacs-lisp :tangle yes : : #+END_SRC We notice that emacs-lisp is highlighted---this is the first modifiable field. Many clever programming tricks can be performed with ~yasnippet~ to save us a ton of time with boilerplate text! #+NAME: yasnippet #+BEGIN_SRC emacs-lisp :tangle yes (use-package yasnippet) (yas-global-mode 1) #+END_SRC *** Complete anything interactively #+NAME: company #+BEGIN_SRC emacs-lisp ; (add-hook 'after-init-hook 'global-company-mode) #+END_SRC *** Delete all consecutive whitespaces #+NAME: company #+BEGIN_SRC emacs-lisp :tangle yes (use-package hungry-delete) (global-hungry-delete-mode) #+END_SRC ** Utilities *** Versioning of files Wonderful Git porcelain for Emacs. Enables the administration of a Git repository in a pain-free way. #+BEGIN_SRC emacs-lisp :tangle yes (use-package magit :bind ("C-c g" . magit-status)) #+END_SRC *** Navigate between projects This enables us to better manage our =.git= projects. #+BEGIN_SRC emacs-lisp :tangle yes (use-package projectile :bind ("C-c p" . 'projectile-command-map) :init (projectile-mode 1) (setq projectile-completion-system 'ivy)) #+END_SRC *** Display keyboard shortcuts on screen #+BEGIN_SRC emacs-lisp :tangle yes (use-package which-key :init (which-key-mode)) #+END_SRC *** Jump to symbol's definition ~dumb-jump~ is a reliable symbol definition finder. It uses different matching algorithms and heuristics to provide a very educated guess on the location of a symbol's definition. #+BEGIN_SRC emacs-lisp :tangle yes (use-package dumb-jump) (add-hook 'xref-backend-functions #'dumb-jump-xref-activate) #+END_SRC *** Graphical representation of file history #+BEGIN_SRC emacs-lisp :tangle yes (use-package undo-tree) (global-undo-tree-mode) #+END_SRC *** Auto-completion framework #+BEGIN_SRC emacs-lisp :tangle yes (use-package ivy :config (setq ivy-use-virtual-buffers t ivy-count-format "%d/%d " enable-recursive-minibuffers t)) (ivy-mode t) #+END_SRC **** Smartly suggesting interactive search matches Wonderful counsellor! #+BEGIN_SRC emacs-lisp :tangle yes (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 **** Searching for items #+BEGIN_SRC emacs-lisp :tangle yes (use-package swiper :bind (("C-f" . counsel-grep-or-swiper))) #+END_SRC ** Coding languages *** TODO Emacs Lisp *** Python Python is included by default on most Linux distributions. #+BEGIN_SRC emacs-lisp :tangle yes (use-package py-yapf) (add-hook 'python-mode-hook 'py-yapf-enable-on-save) #+END_SRC ** File formats *** =csv= and Excel #+BEGIN_SRC emacs-lisp :tangle yes (use-package csv-mode) #+END_SRC *** Interacting with PDFs Org mode shines particularly when exporting to PDF---Org files can reliably be shared and exported to PDF in a reproducible fashion. #+BEGIN_SRC emacs-lisp :tangle yes (use-package pdf-tools) ;; (pdf-tools-install) #+END_SRC *** Accounting Ledger is a creation of John Wiegley's. It enables double-entry accounting in a simple plaintext format, and reliable verification of account balances through time.[fn::For more information, visit https://www.ledger-cli.org/.] #+BEGIN_SRC emacs-lisp :tangle yes (use-package ledger-mode :bind ("C-c r" . ledger-report) ("C-c C" . ledger-mode-clean-buffer)) #+END_SRC These reports can be generated within Emacs. It is quite useful to pipe their output to an automated ``smart document''. #+BEGIN_SRC emacs-lisp :tangle yes (setq ledger-reports '(("bal" "%(binary) -f %(ledger-file) bal") ("bal-USD" "%(binary) -f %(ledger-file) bal --exchange USD") ("reg" "%(binary) -f %(ledger-file) reg") ("net-worth" "%(binary) -f %(ledger-file) bal ^Assets ^Liabilities --exchange USD") ("net-income" "%(binary) -f %(ledger-file) bal ^Income ^Expenses --exchange USD --depth 2 --invert") ("payee" "%(binary) -f %(ledger-file) reg @%(payee)") ("account" "%(binary) -f %(ledger-file) reg %(account)") ("budget" "%(binary) -f %(ledger-file) budget --exchange USD"))) #+END_SRC *** Plotting & charting #+BEGIN_SRC emacs-lisp :tangle yes (use-package gnuplot) #+END_SRC ** Cosmetics *** Start page We replace the standard welcome screen with our own. #+BEGIN_SRC emacs-lisp :tangle yes (setq inhibit-startup-message t) (use-package dashboard :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 *** Sidebar :PROPERTIES: :sd-unpack-path: sd-sidebar.el :END: Get inspiration from ~ibuffer-sidebar~ and create a better sidebar. #+BEGIN_SRC emacs-lisp :tangle yes (defun sd-sidebar () (interactive) (message "Foobius Peter.")) #+END_SRC *** Better parentheses #+BEGIN_SRC emacs-lisp :tangle yes (use-package rainbow-delimiters :config (add-hook 'prog-mode-hook #'rainbow-delimiters-mode)) (show-paren-mode 1) #+END_SRC *** Highlight ``color keywords'' in their color This highlights hexadecimal numbers which look like colors, in that same color. #+BEGIN_SRC emacs-lisp :tangle yes (use-package rainbow-mode :init (add-hook 'prog-mode-hook 'rainbow-mode)) #+END_SRC *** +UTF-8 bullet points in =Org mode=+ This section was removed, as it is more explicit to display the headline character for what it is: a collection of at least one asterisk. * ~org-mode~ Org mode is so significant that this section of the paper deserves its own introduction. ** Introduction Phew, after all this initialization, I can finally introduce Org mode! I am so *excited*. Org mode replaces aword processor, a presentation creator, and a spreadsheet editor. 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! Check out how much information Org mode keeps concerning the most recent header: #+NAME: org-meta-info #+BEGIN_SRC emacs-lisp :tangle no :results pp :exports both :cache yes (save-excursion (org-previous-visible-heading 1) (org-entry-properties)) #+END_SRC #+RESULTS[cf982044956d8f3ec89e7a9da80976b1b19db423]: org-meta-info : (("CATEGORY" . "smart-documents") : ("BLOCKED" . "") : ("FILE" . "/home/blendux/.emacs.d/smart-documents.org") : ("PRIORITY" . "B") : ("ITEM" . "Introduction")) ** Basic customization *** Base folder Org base directory is in user home on GNU/Linux, or in =AppData= in MS Windows. #+NAME: org-directory #+BEGIN_SRC emacs-lisp :tangle yes (setq org-directory (concat user-emacs-directory "~/org")) #+END_SRC *** Prevent/warn on invisible edits #+BEGIN_SRC emacs-lisp :tangle yes (setq org-catch-invisible-edits t) #+END_SRC ** Org cosmetics First, we ensure the display of 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!] We then set values for many other Org-related cosmetic symbols. #+BEGIN_SRC emacs-lisp :tangle yes (setq org-hide-emphasis-markers nil org-startup-indented t org-src-preserve-indentation nil org-edit-src-content-indentation 2 org-ellipsis "  ") ; folding symbol #+END_SRC *** Dynamic numbering of headlines We enable the dynamic numbering of headlines in an Org buffer. We also set the numbering face to ~org-special-keyword~, which specifies a ~:background white~ attribute. This is necessary because otherwise, the background of the numbering may be overridden by the ~TODO~ face attribute ~:background coral~. #+BEGIN_SRC emacs-lisp :tangle yes (add-hook 'org-mode-hook 'org-num-mode) (setq org-num-face 'org-special-keyword) #+END_SRC By default, we hide Org document properties such as =#+TITLE=, =#+AUTHOR=, and =#+DATE=, because those keywords are defined when the document template is populated. We can nevertheless always access those properties and edit them manually, with a simple keyboard shortcut (cf. Section [[Open Org mode document properties]]). *** Document properties #+BEGIN_SRC emacs-lisp :tangle yes (defun org-property-value (property) "Return the value of a given Org document property." (interactive) (save-excursion (goto-char (point-min)) (re-search-forward (concat "^[[:space:]]*#\\+" property ":[[:space:]]*\\(.*?\\)[[:space:]]*$") nil t) (nth 3 (car (cdr (org-element-at-point)))))) #+END_SRC #+BEGIN_SRC emacs-lisp :tangle no (defun sd-document-properties () "Open separate buffer to edit Org mode properties." (interactive) (let ((title (car (org-property-value "TITLE"))) (date (org-property-value "DATE"))) (with-output-to-temp-buffer "Smart Document Properties" (print title) (print date)))) #+END_SRC #+BEGIN_SRC emacs-lisp :tangle yes (add-hook 'org-src-mode-hook '(lambda () "Disable flycheck for `emacs-lisp-mode'." (setq-local flycheck-disabled-checkers '(emacs-lisp-checkdoc)))) #+END_SRC *** Timestamps More literary timestamps are exported to LaTeX using the following custom format: #+BEGIN_SRC emacs-lisp :tangle yes (setq org-time-stamp-custom-formats '("%d %b. %Y (%a)" . "%d %b. %Y (%a), at %H:%M")) #+END_SRC ** Programming a Smart Documents The following languages can be used inside =SRC= blocks, in view of being executed by the Org Babel backend upon document export. #+BEGIN_SRC emacs-lisp :tangle yes (setq org-babel-load-languages '((shell . t) (python . t) (plantuml . t) (emacs-lisp . t) (awk . t) (ledger . t) (gnuplot . t) (latex . t))) (org-babel-do-load-languages 'org-babel-load-languages '((C . t) (shell . t) (gnuplot . 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)] We open the agenda in a separate window. #+BEGIN_SRC emacs-lisp :tangle yes (setq org-agenda-window-setup 'other-frame) #+END_SRC ** LaTeX export We'll be compiling our documents with LuaTeX. This will afford us some future-proofing, since it was designated as the successor to pdfTeX by the latter's creators. First, we define the command executed when an Org file is exported to LaTeX. We'll use =latexmk=, the Perl script which automagically runs binaries related to LaTeX in the correct order and the right amount of times. Options and why we need them: - ~-shell-excape~ :: required by minted to color source blocks - ~-pdflatex=lualatex~ :: we use lualatex to generate our PDF - ~-interaction=nonstopmode~ :: go as far as possible without prompting user for input #+BEGIN_SRC emacs-lisp :tangle yes (setq org-latex-pdf-process '("latexmk -pdf -f \ -pdflatex=lualatex -shell-escape \ -interaction=nonstopmode -outdir=%o %f")) #+END_SRC *** Exporting timestamps We customize the format for org time stamps to make them appear monospaced in our exported LaTeX documents. This makes them visually distinguishable from body text. #+BEGIN_SRC emacs-lisp :tangle yes (setq org-latex-active-timestamp-format "\\texttt{%s}") (setq org-latex-inactive-timestamp-format "\\texttt{%s}") #+END_SRC *** LaTeX packages The following packages are loaded for every time we export to LaTeX. #+BEGIN_SRC emacs-lisp :tangle yes (setq org-latex-packages-alist '(("AUTO" "babel" t ("pdflatex")) ("AUTO" "polyglossia" t ; Babel replacement for LuaLaTeX ("xelatex" "lualatex")) ("" "booktabs" t ; Publication quality tables in LaTeX ("pdflatex")) ("table,svgnames" "xcolor" t ; svgnames opens up ~150 color keywords ("pdflatex")) ("skip=0.5\\baselineskip" "caption" t ; Increase space between floats and captions ("pdflatex" "lualatex")))) #+END_SRC *** Colored source blocks in PDF export Little bonus for GNU/Linux users: syntax highlighting for source code blocks in LaTeX exports. #+BEGIN_SRC emacs-lisp :tangle yes (when (string-equal system-type "gnu/linux") (add-to-list 'org-latex-packages-alist '("AUTO" "minted" t ("pdflatex" "lualatex"))) (setq org-latex-listings 'minted) (setq org-latex-minted-options '(("style" "friendly") ("breaklines" "true") ("breakanywhere" "true")))) #+END_SRC *** Cleaning directory after PDF compilation Now, we set the files to be deleted when a LaTeX \rightarrow PDF compilation occurs. We only care about two files, in the end: the Org mode file for edition, and the PDF for distribution. #+BEGIN_SRC emacs-lisp :tangle yes (setq org-latex-logfiles-extensions '("aux" "bcf" "blg" "fdb_latexmk" "fls" "figlist" "idx" "log" "nav" "out" "ptc" "run.xml" "snm" "toc" "vrb" "xdv" "tex" "lot" "lof")) #+END_SRC *** Chronological diary entries By default, Org agenda inserts diary entries as the first under the selected date. It is preferable to insert entries in the order that they were recorded, i.e. chronologically. #+BEGIN_SRC emacs-lisp :tangle yes (setq org-agenda-insert-diary-strategy 'date-tree-last) #+END_SRC What follows is an additional document class structures that can be exported in LaTeX. #+BEGIN_SRC emacs-lisp :tangle yes ;; (add-to-list 'org-latex-classes ;; '("book-blendoit" ;; "\\documentclass[12pt]{book}" ;; ("\\chapter{%s}" . "\\chapter*{%s}") ;; ("\\section{%s}" . "\\section*{%s}") ;; ("\\subsection*{%s}" . "\\subsection*{%s}") ;; ("\\subsubsection*{%s}" . "\\subsubsection*{%s}"))) #+END_SRC *** Table of contents By default, body text can immediately follow the table of contents. It is however cleaner to separate table of contents with the rest of the work. #+BEGIN_SRC emacs-lisp :tangle yes (setq org-latex-toc-command "\\tableofcontents\\clearpage") #+END_SRC The following makes =TODO= items appear red and =CLOSED= items appear green in Org's LaTeX exports. Very stylish, much flair! ** Org links This is a mind-bending capacity of Org mode: we can assign arbitrary functions to be executed when a user follows an Org link. Org links appear like hyperlinks both in buffers and PDF exports---e.g. the following link to this very section, Section [[Org links]]---but their in-buffer behavior can be arbitrarily assigned. #+BEGIN_SRC emacs-lisp :tangle no (org-add-link-type "tag" 'endless/follow-tag-link) (defun endless/follow-tag-link (tag) "Display a list of TODO headlines with tag TAG. With prefix argument, also display headlines without a TODO keyword." (org-tags-view (null current-prefix-arg) tag)) [[tag:work+phonenumber-boss][Optional Description]] #+END_SRC * One-click workflows In this section, we'll implement useful one-click workflows. It comes later keybinding definitions for two reasons: 1. To a new user, keybindings are more important than the precise implementation of the bound function---it is more important to know how to drive a car than how a car works. 2. If the following subsections share the same name as the keybinding subsection (Section [[Keyboard shortcuts]]), the links will resolve to the earliest heading in the document, i.e. the keybinding subsection and not the subsection describing the `one-click workflow'. ** TODO Export to PDF This reimplements the most common Org mode export: Org \rightarrow LaTeX \rightarrow PDF. The binding is defined in Section [[Export to PDF]]. #+BEGIN_SRC emacs-lisp :tangle yes (defun my/org-quick-export () "Org async export to PDF and open. This basically reimplements `C-c C-e C-a l o'." (interactive) (org-open-file (org-latex-export-to-pdf))) #+END_SRC ** Beautify buffer Binding defined in Section [[Indent buffer]]. #+BEGIN_SRC emacs-lisp :tangle yes (defun sd-indent-buffer () "Indent entire buffer." (interactive) (save-excursion (indent-region (point-min) (point-max) nil))) #+END_SRC #+BEGIN_SRC emacs-lisp :tangle yes (defun sd-org-beautify () "Beautify Org mode buffer." (interactive) (when (eq major-mode 'org-mode) (sd-indent-buffer))) #+END_SRC * Editing preferences These customizations enhance editor usability. They also encompass cosmetic changes not brought about a specific package. ** Editor *** Coding standards This is just a better default. Don't @ me. #+BEGIN_SRC emacs-lisp :tangle yes (setq c-default-style "linux" c-basic-offset 4) #+END_SRC *** Recent files The keybinding for opening a recently visited file is described in paragraph [[Open a recently visited file]]. #+BEGIN_SRC emacs-lisp :tangle yes (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 ** Frame *** Header & mode lines # Top of the buffer is more intuitive for buffer info, bottom is more intuitive # for buffer action. **** TODO Header line In Org mode, the document header line will be the title of the document we are working on currently. #+BEGIN_SRC emacs-lisp :tangle yes (add-hook 'org-mode-hook (lambda () "Set the header line to show #+TITLE and section name." (setq header-line-format '(:eval (list " " (org-property-value "TITLE")))))) #+END_SRC **** Mode line #+NAME: mode-line-format #+BEGIN_SRC emacs-lisp :tangle yes (setq-default mode-line-format (list '(:eval (list " " (if buffer-read-only "🔒" "🔓") (propertize " %b " 'help-echo (buffer-file-name) ) (if (buffer-modified-p) "🖉" "✓"))))) #+END_SRC ** Window ** Buffer *** Save cursor location Save cursor location in visited buffer after closing it or Emacs. #+BEGIN_SRC emacs-lisp :tangle yes (save-place-mode 1) #+END_SRC *** Column filling A line of text is considered ``filled'' when it reaches 79 characters in length. #+BEGIN_SRC emacs-lisp :tangle yes (setq-default fill-column 79) (add-hook 'org-mode-hook 'turn-on-auto-fill) ; Automatically break lines longer than ; =fill-column=. #+END_SRC *** Right and left margins We set reasonable margins on either side of our buffer. #+BEGIN_SRC emacs-lisp :tangle yes (setq-default left-margin-width 6 right-margin-width 6) #+END_SRC ** Text *** Beautiful symbols We want the Emacs Lisp keyword =lambda= to be rendered as \lambda within the editor. This is mostly for a subjective ``cool'' factor. #+BEGIN_SRC emacs-lisp :tangle yes (global-prettify-symbols-mode 1) #+END_SRC *** Org mode sugar Let's pimp out the appearance of our text in Org mode. First, we prettify checkbox lists. #+BEGIN_SRC emacs-lisp :tangle yes (when (string-equal system-type "gnu/linux") (add-hook 'org-mode-hook (lambda () "Beautify Org symbols." (push '("[ ]" . "○") prettify-symbols-alist) ; Unchecked item (push '("[X]" . "◉" ) prettify-symbols-alist) ; Checked item (push '("[-]" . "◎" ) prettify-symbols-alist) ; Partially checked item (push '("-" . "⁃" ) prettify-symbols-alist) ; DONE headings (prettify-symbols-mode)))) #+END_SRC - [ ] This first item is unticked - [-] This second item is partially completed - [X] This first sub-item is ticked - [ ] This sub-item is not ticked - [ ] This third item is ticked *** Electric modes Electricity is a very important technology. In Emacs jargon, ``electric'' modes tend to automate behaviors or present some elegant simplification to a workflow.[fn::More information can be found at [[https://www.emacswiki.org/emacs/Electricity]].] #+BEGIN_SRC emacs-lisp :tangle yes (electric-pair-mode) ; Certain character pairs are automatically completed. (electric-indent-mode) ; Newlines are always intelligently indented. #+END_SRC ** Minibuffer We replace the longer ~yes-or-no-p~ questions with more convenient ~y-or-n-p~. #+BEGIN_SRC emacs-lisp :tangle yes (defalias 'yes-or-no-p 'y-or-n-p) #+END_SRC Disable minibuffer scroll bar. #+BEGIN_SRC emacs-lisp :tangle yes (set-window-scroll-bars (minibuffer-window) nil nil) #+END_SRC * Themes Without a carefully designed theme, our editor would become unusable. Thus, we describe two themes that were developed purposefully and iteratively. #+BEGIN_SRC emacs-lisp :tangle yes (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 classic 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 | interactive 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 :tangle yes (setq-default cursor-type 'bar) #+END_SRC *** Fonts **** Currently used /chad fonts/ - Hack[fn::https://sourcefoundry.org/hack/] :: ~default~ and ~fixed-pitch~, default code font - Legible, modern monospace font - Strict, sharp, uncompromising - Public Sans[fn::https://public-sans.digital.gov/] :: ~variable-pitch~, default body text font - Very modern yet neutral - Designed for the U.S. government - Exceptional color on screen - Hermit[fn::https://pcaro.es/p/hermit/] :: ~org-block~, anything Org/meta in general - Slightly wider than Hack - More opinionated shapes - Very legible parentheses, very useful for Emacs Lisp! - Jost[fn::https://indestructibletype.com/Jost.html] :: ~org-document-title~ and ~org-level-1~ - Ultra-modern - Tasteful amount of geometric inspiration **** Previously used /virgin fonts/ - +Liberation Sans+[fn::https://en.wikipedia.org/wiki/Liberation_fonts] :: +~variable-pitch~+ - Metrically compatible with /Arial/ (ugh) - Unoffensive, unambitious forms - Pretty angular letters, it's like you're trying to read squares - +Open Sans+[fn::https://www.opensans.com/] :: +~variable-pitch~+ - Ooh geometric Bauhaus influences, look at me - Tall leading height is =h a r m o n i o u s= **** Using proportional fonts when needed We use ~variable-pitch-mode~ for appropriate modes. #+BEGIN_SRC emacs-lisp :tangle yes (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 1920\times1080. #+BEGIN_SRC emacs-lisp :tangle yes #+END_SRC ** TODO ~minimal~ * Late setup At this point, our editor is almost ready to run. Phew! All that's left to do is to interrupt our profiling activities, and smartly store the result of our profiling. ** Profiling --- stop #+BEGIN_SRC emacs-lisp :tangle yes ;; (profiler-stop) #+END_SRC ** Profiling --- report #+BEGIN_SRC emacs-lisp :tangle yes ;; (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.