# -*- mode: org; -*- #+TITLE: Smart Documents #+AUTHOR: Marius Peter #+DATE: <2021-02-09 Tue> #+EMAIL: smart-documents@tuta.io #+OPTIONS: html-style:nil #+MACRO: SD /Smart Document/ #+MACRO: SDs /Smart Documents/ # LaTeX setup #+SETUPFILE: ~/.emacs.d/resources/templates/documents/gnu-default.setup # Title page #+INCLUDE: ~/.emacs.d/resources/templates/documents/title-default.org #+LATEX_HEADER_EXTRA: \newfontfamily\garamond{EB Garamond} #+LATEX_HEADER_EXTRA: \newfontfamily\publicsans{Public Sans} #+LATEX: \begin{abstract} The idea of {{{SDs}}} 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 {{{SD}}} itself! #+LATEX: \end{abstract} * COMMENT All TODOs ** TODO Harmonize ~compagnon~ themes ** TODO Soft-coded paths ** TODO Normalize `sd-path-' names =sd-paths= alist? ** TODO Project vision/values/core objectives ** TODO button as master {{{sd}}} entry point ** TODO Turn ~left-fringe~ into ~org-agenda~ buffer drawer ** WAITING Display ~*Messages*~ buffer on startup ** TODO Integrate site publishing recipes ** TODO ~transient-*-file~ paths Put them somewhere it makes sense. * Introduction 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 press ~f1~ at any time to access Emacs built-in help. ** TODO User details One advantage of working with {{{SDs}}} 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." (interactive) (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 the following locations: - =~/.emacs.el= - =~/.emacs.d/init.el= - =~/.config/emacs/init.el=. 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 no (profiler-start) #+END_SRC ** Usual paths to files & directories This section defines the paths that structure the overall {{{SD}}} logic. - File :: A collection of information recognized by a computer---to us, a normal file appears as programs (source code or /executables/) or data (an image, a video, a document...). It is usually stored in a computer's memory or on a storage device (hard drive, USB drive...) - Path :: A description of a file's location from the user's perspective. A path is program searches for a file or executable program. - Directory :: Synonymous with /folder/. From the computer's---and Emacs'---perspective, a file which contains other files. These files may themselves be directories. *** Meta files---files about 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 [[help:user-emacs-directory][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. A meta files should follow the following best practices: - Be located at [[help:sd-path-meta][sd-path-meta]] :: This ensures a tidy [[help:user-emacs-directory][user-emacs-directory]]. - Be explicit :: Meta filenames should not begin with a period: they would be hidden by default on GNU/Linux systems. Novices must see all files by default. #+BEGIN_SRC emacs-lisp :tangle yes (defcustom sd-path-meta (concat user-emacs-directory "meta/") "Directory containing files about files.") #+END_SRC **** Recently visited files #+BEGIN_SRC emacs-lisp :tangle yes (setq recentf-save-file (concat sd-path-meta "recentf")) #+END_SRC **** File bookmarks #+BEGIN_SRC emacs-lisp :tangle yes (setq bookmark-default-file (concat sd-path-meta "bookmarks")) #+END_SRC **** Projects' bookmarks #+BEGIN_SRC emacs-lisp :tangle yes (setq projectile-known-projects-file (concat sd-path-meta "projectile-bookmarks.eld")) #+END_SRC **** Org id locations #+begin_quote We track IDs through files, so that links work globally. The file defined at [[help:org-id-locations-file][org-id-locations-file]] maintains a hash table for IDs and writes this table to disk when exiting Emacs. Because of this, it works best if you use a single Emacs process, not many. Paraphrased from [[help:org-id-track-globally][the Emacs help interface]]. #+end_quote #+BEGIN_SRC emacs-lisp :tangle yes (setq org-id-locations-file (concat sd-path-meta "org-id-locations")) ;; The leading period is removed because no files are hidden in the ;; metafiles' directory. #+END_SRC **** Location in previously visited file #+BEGIN_SRC emacs-lisp :tangle yes (setq save-place-file (concat sd-path-meta "places")) #+END_SRC **** Auto save file lists #+BEGIN_SRC emacs-lisp :tangle yes (setq auto-save-list-file-prefix (concat sd-path-meta "auto-save-list/.saves-")) #+END_SRC *** Resources All third-party resources are saved at the following location. #+BEGIN_SRC emacs-lisp :tangle yes (defcustom sd-path-resources (concat user-emacs-directory "resources/") "Directory containing the third-party resources. Resources may be any data that is not auto-generated during Emacs startup.") #+END_SRC **** Packages #+BEGIN_SRC emacs-lisp :tangle yes (setq package-user-dir (concat sd-path-resources "elpa/")) #+END_SRC **** Themes #+BEGIN_SRC emacs-lisp :tangle yes (setq custom-theme-directory (concat sd-path-resources "themes/")) #+END_SRC **** Snippets This path, specifically, is required to be in list form. #+BEGIN_SRC emacs-lisp :tangle yes (setq yas-snippet-dirs (list (concat sd-path-resources "snippets/"))) #+END_SRC **** Templates #+BEGIN_SRC emacs-lisp :tangle yes (setq sd-path-templates (concat sd-path-resources "templates/")) #+END_SRC **** Emojis #+BEGIN_SRC emacs-lisp :tangle yes (setq emojify-emojis-dir (concat sd-path-resources "emojis/")) #+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 sd-path-resources "custom.el")) (load custom-file) #+END_SRC ** Backups Backups are very important! #+BEGIN_SRC emacs-lisp :tangle yes (setq backup-directory-alist `((".*" . "~/.cache/emacs/")) auto-save-file-name-transforms `((".*" ,"~/.cache/emacs/" 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 ** Undo history We save undo history in a designated directory, so as not to pollute our file hierarchy. #+BEGIN_SRC emacs-lisp :tangle yes (setq undo-tree-history-directory-alist '(("." . "~/.cache/emacs/"))) #+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 . 80)) (add-to-list 'initial-frame-alist '(height . 24)) #+END_SRC We also set the dimensions of subsequent frames: #+BEGIN_SRC emacs-lisp :tangle yes (add-to-list 'default-frame-alist '(width . 80)) (add-to-list 'default-frame-alist '(height . 24)) #+END_SRC Transparency. : (set-frame-parameter (selected-frame) 'alpha '( . )) : (set-frame-parameter (selected-frame) 'alpha ) #+BEGIN_SRC emacs-lisp :tangle yes (set-frame-parameter (selected-frame) 'alpha '(90 . 70)) (add-to-list 'default-frame-alist '(alpha . (90 . 70))) #+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 (let ((secrets (concat user-emacs-directory "secrets.org"))) (when (file-exists-p secrets) (org-babel-load-file secrets))) #+END_SRC * Keyboard shortcuts 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. ** CUA mode 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 ** 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 [[This very file]]) #+BEGIN_SRC emacs-lisp :tangle yes (global-set-key (kbd "C-c c") 'sd-find-literate-config) #+END_SRC *** Open the Org diary #+BEGIN_SRC emacs-lisp :tangle yes (global-set-key (kbd "C-c d") 'sd-find-org-diary) #+END_SRC *** Capture Org content #+BEGIN_SRC emacs-lisp :tangle yes (global-set-key (kbd "C-c k") 'org-capture) #+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 (kbd "C-c a") 'org-agenda) #+END_SRC *** Open the diary # SSH headache with lws VPS #+BEGIN_SRC emacs-lisp :tangle yes (global-set-key [f9] '(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 no (global-set-key [f8] '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") 'sd-delete-window-or-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") 'sd-delete-frame-or-kill-emacs) #+END_SRC *** Remap =C-z= when using a graphical interface By default, =C-z= suspends the editor. This is extremely handy when the editor is started with the =-nw= option (no window, i.e. launched in a terminal), because it returns control to the terminal command line without quitting Emacs---it simply places the Emacs process in the background. The user may then use the Linux job management tools to return inside the Emacs process. However, when using a graphical display, we have no need for suspending the frame, so we remap =C-z= to the much more sensible ~undo~ behaviour. #+BEGIN_SRC emacs-lisp :tangle yes (when (display-graphic-p) (global-set-key (kbd "C-z") 'undo)) #+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 ** TODO COMMENT Navigation # [2021-09-13 Mon] # # Kinda don't like this... This is some more text. And then some. And # then some. =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 (add-hook 'org-mode '(lambda () (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 (local-unset-key (kbd "M-h")) (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 ** Accessing customization *** 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") 'sd-quick-export) #+END_SRC **** TODO Presentation #+BEGIN_SRC emacs-lisp :tangle yes #+END_SRC *** Clean up buffer Clean up buffer in every mode. #+BEGIN_SRC emacs-lisp :tangle yes (global-set-key [f12] 'sd-beautify-buffer) #+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 ** COMMENT ~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") (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 *** Completion #+BEGIN_SRC emacs-lisp :tangle yes (use-package company) (add-hook 'after-init-hook 'global-company-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 *** Delete all consecutive whitespaces #+NAME: hungry-delete #+BEGIN_SRC emacs-lisp :tangle yes (use-package hungry-delete :config (progn (global-hungry-delete-mode) (add-to-list 'hungry-delete-except-modes ;; Otherwise minibuffer paths can't be backspaced. 'minibuffer-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 And he will be called Wonderful *Counselor*, Mighty God, Everlasting Father, Prince of Peace. #+BEGIN_SRC emacs-lisp :tangle yes (use-package counsel :bind ("M-x" . counsel-M-x) :config (counsel-mode t)) #+END_SRC **** Searching for items in current buffer #+BEGIN_SRC emacs-lisp :tangle yes (use-package swiper :bind (("C-f" . swiper))) #+END_SRC *** COMMENT Web browsing Thanks Xah![fn::[[http://ergoemacs.org/emacs/emacs_set_default_browser.html]]] We use a browser depending on the url. #+BEGIN_SRC emacs-lisp :tangle yes (setq browse-url-browser-function '(("wikipedia\\.org" . browse-url-firefox) ("github\\.com" . browse-url-chromium) ("thefreedictionary\\.com" . eww-browse-url) ("." . browse-url-default-browser))) #+END_SRC *** IRC Emacs ships with an IRC client called ~erc~. #+BEGIN_SRC emacs-lisp :tangle yes (use-package erc :custom (erc-autojoin-channels-alist '(("freenode.net" "#linux" "#archlinux" "#emacs" "#bitcoin" "#latex" "#org-mode" "#python"))) (erc-autojoin-timing 'ident) ; Autojoin after NickServ identification. (erc-fill-function 'erc-fill-static) (erc-fill-static-center 16) ;; (erc-hide-list '("JOIN" "PART" "QUIT")) (erc-lurker-hide-list '("JOIN" "PART" "QUIT")) (erc-lurker-threshold-time (* 3600 4)) ; Four hours (erc-prompt-for-nickserv-password nil) (erc-server-reconnect-attempts 5) (erc-server-reconnect-timeout 3) :config (add-to-list 'erc-modules 'spelling) (erc-services-mode 1) (erc-update-modules)) #+END_SRC *** TODO Telegram Yeah, a Telegram client exists for Emacs. #+BEGIN_SRC emacs-lisp :tangle no (use-package telega :load-path "~/telega.el/telega.el" :commands (telega) :defer t) #+END_SRC *** COMMENT Drawings #+BEGIN_SRC ditaa :file resources/images/ditaa.png +-----------+ +-----------------+ | c06F | | c06F | | Create a | | Open the source | | source |--->| block and | | block for | | start drawing! | | ditaa | | | +-----------+ +-----------------+ #+END_SRC #+RESULTS: [[file:resources/images/ditaa.png]] *** TODO UML diagrams # Implement automatically downloading this kind of executable! #+BEGIN_SRC emacs-lisp :tangle yes ;; (require 'plantuml-mode) (use-package plantuml-mode) (setq plantuml-default-exec-mode 'jar plantuml-jar-path (concat sd-path-resources "executables/plantuml.jar") org-plantuml-jar-path (concat sd-path-resources "executables/plantuml.jar")) #+END_SRC #+BEGIN_SRC plantuml :file resources/images/uml.png @startuml doob.png !theme cerulean-outline title Example diagram with Plantuml package "Package 1" as pkg1 { node node1 node node2 } package "Package 2" as pkg2 { component "Component 1" as comp1 component "Component 2" as comp2 interface "Interface" as int } pkg1 .. pkg2 comp1 --> int comp2 --> int @enduml #+END_SRC #+ATTR_LATEX: :width 0.8\textwidth #+RESULTS: [[file:resources/images/uml.png]] ** 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 *** OCaml #+BEGIN_SRC emacs-lisp :tangle yes (use-package tuareg) #+END_SRC *** Haskell #+BEGIN_SRC emacs-lisp :tangle yes (use-package haskell-mode) #+END_SRC *** Lua # If I have to deal with luaotfload one more time, I swear... #+BEGIN_SRC emacs-lisp :tangle yes (use-package lua-mode) #+END_SRC *** Web languages Encompasses HTML, CSS, Javascript, Jinja(2), as well as many other web-related markup languages. #+BEGIN_SRC emacs-lisp :tangle yes (use-package web-mode) (use-package nginx-mode) #+END_SRC ** File formats These aren't tied to a particular language per se. *** =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 no (use-package pdf-tools) (unless (string-equal system-type "windows-nt") (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 #+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 that 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 *** Minor modes in mode line We hide minor modes in the mode line. #+BEGIN_SRC emacs-lisp :tangle yes (use-package rich-minority) (rich-minority-mode 1) (setf rm-whitelist "projectile") #+END_SRC *** Emojis Emojis are a symbol of modernity, and their tasteful use enables communicating with people from around the world---we're all for that! B-) \smiley #+BEGIN_SRC emacs-lisp :tangle yes (when (string-equal system-type "gnu/linux") (use-package emojify :hook (after-init . global-emojify-mode))) #+END_SRC * ~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 a word 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 (This block was evaluated on Microsoft Windows.) #+RESULTS[cf982044956d8f3ec89e7a9da80976b1b19db423]: org-meta-info : (("CATEGORY" . "smart-documents") : ("BLOCKED" . "") : ("FILE" . "c:/Users/blend/AppData/Roaming/.emacs.d/smart-documents.org") : ("PRIORITY" . "A") : ("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) #+END_SRC *** Pretty LaTeX symbols We display LaTeX entities as UTF8 symbols \rArr this is a slick idea to further make Emacs look like the exported PDF. Using symbols in tables is discouraged? #+BEGIN_SRC emacs-lisp :tangle yes (setq org-pretty-entities t) #+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 org-num-skip-commented t org-num-skip-unnumbered t) #+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]]). *** TODO 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 # This is a terrible idea :-( but leaving the option to the # reader. Long live ISO-8601! # https://orgmode.org/manual/Custom-time-format.html 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 *** COMMENT Sequence of TODOs #+begin_src emacs-lisp :tangle yes (setq org-todo-keywords '((sequence "TODO" ; Vanilla sequence "|" "DONE") (sequence "APPLY" ; Job applications "FOLLOW UP" "|" "REJECTED" "STOP" "OFFER") (sequence "STUCK" ; Project mgmt "WAITING" "|" "N/A" "COMPLETED"))) #+end_src #+begin_src emacs-lisp :tangle yes (setq org-todo-keyword-faces '(("STUCK" . (:height 1.6 :background "red" :foreground "white" :weight bold)) ("WAITING" . (:height 1.6 :background "yellow")) ("N/A" . (:height 1.6 :background "LightSteelBlue3" :foreground "white")) ("COMPLETED" . (:height 1.6 :background "green" :foreground "white")))) #+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)] *** COMMENT Open agenda in separate frame # Meh We open the agenda in a separate frame. #+BEGIN_SRC emacs-lisp :tangle yes (setq org-agenda-window-setup 'other-frame) #+END_SRC *** Diary file The diary file can be included in all agenda views. #+BEGIN_SRC emacs-lisp :tangle yes (setq org-agenda-diary-file "~/org/PERSONAL/diary/diary.org") #+END_SRC *** List of agenda files If the agenda file does not already exist, create it at the expected location. #+BEGIN_SRC emacs-lisp :tangle yes (unless (file-exists-p (concat sd-path-meta "org-agenda-files")) (with-temp-buffer (write-file (concat sd-path-meta "org-agenda-files")))) #+END_SRC The list of agenda files is saved at the following location. #+BEGIN_SRC emacs-lisp :tangle yes (setq org-agenda-files (concat sd-path-meta "org-agenda-files")) #+END_SRC ** Org Capture #+BEGIN_SRC emacs-lisp :tangle yes (setq org-default-notes-file (concat sd-path-resources "org/default-notes.org")) #+END_SRC ** Programming a {{{SD}}} 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 (org-babel-do-load-languages 'org-babel-load-languages '((shell . t) (python . t) (ditaa . t) (plantuml . t) (emacs-lisp . t) (awk . t) ;; (ledger . t) ;; Deprecated in Emacs 28.1? (latex . t) (C . t) (gnuplot . t) (ocaml . t))) #+END_SRC ** Exporting {{{SDs}}} *** 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 it easy to distinguish time stamps from body text, and make them align nicely in definition lists, which I prefer when logging events: - [2021-10-03 Sun] :: Did something - [2021-10-04 Mon] :: Did something else - [2021-10-05 Tue] :: Did yet another thing #+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")) ("" "fontspec" t ; Fonts for LuaLaTeX ("lualatex")) ("" "booktabs" t ; Publication quality tables ("pdflatex" "lualatex")) ("" "wasysym" t ; Emojis and other symbols ("pdflatex" "lualatex")) ("" "lettrine" t ("pdflatex" "lualatex")) ("table,svgnames" "xcolor" t ; svgnames opens up ~150 new color keywords ("pdflatex" "lualatex")) ("skip=0.5\\baselineskip" "caption" t ; Increase space between floats and captions ("pdflatex" "lualatex")))) #+END_SRC **** COMMENT Colored source blocks in PDF export # Too distracting. Focus on fonts. 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 export 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 **** Extra LaTeX class This /letter/ template completes the other default LaTeX classes. #+BEGIN_SRC emacs-lisp :tangle yes (require 'ox-publish) (add-to-list 'org-latex-classes '("letter" "\\documentclass[11pt]{letter}" ("\\section{%s}" . "\\section*{%s}") ("\\subsection*{%s}" . "\\subsection*{%s}") ("\\subsubsection*{%s}" . "\\subsubsection*{%s}"))) (add-to-list 'org-latex-classes '("book-blendoit" "\\documentclass[11pt]{book}" ("\\chapter{%s}" . "\\chapter*{%s}") ("\\section{%s}" . "\\section*{%s}") ("\\subsection*{%s}" . "\\subsection*{%s}") ("\\subsubsection*{%s}" . "\\subsubsection*{%s}"))) #+END_SRC **** COMMENT Table of contents # Commented out on <2021-09-21 Tue>. 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! **** COMMENT AUCTEX #+BEGIN_SRC emacs-lisp :tangle yes (use-package tex :defer t :ensure auctex :ensure auctex-latexmk) (auctex-latexmk-setup) #+END_SRC **** Groff export #+BEGIN_SRC emacs-lisp :tangle no (require 'ox-groff) #+END_SRC ** TODO 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 as 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 than the keybinding definitions for two reasons: 1. To a new user, keybindings are more relevant than the 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 to that name will resolve to the earliest heading in the document, i.e. the keybinding subsection, and not the subsection describing the ``one-click workflow''. ** Opening files First off, we identify files that we'd like to jump to conveniently. *** This very file We begin by defining a function to open this very file. #+BEGIN_SRC emacs-lisp :tangle yes (defun sd-find-literate-config () "Visit this very file." (interactive) (find-file sd-literate-config)) #+END_SRC *** Org diary file #+BEGIN_SRC emacs-lisp :tangle yes (defun sd-find-org-diary() "Visit the `org-agenda-diary-file'." (interactive) (find-file org-agenda-diary-file)) #+END_SRC ** TODO Export to PDF This series of ~quick-export~ functions have one objective: harmonize the export of Emacs buffers to PDF. Org mode does this by design; we describe additional exports for other modes, most notably Nroff mode and Ledger mode. *** From Org mode 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 sd-quick-export--org () "Org mode async export to PDF and open. This basically reimplements `C-c C-e C-a l o'." (org-open-file (org-latex-export-to-pdf))) #+END_SRC *** From a Ledger report #+BEGIN_SRC emacs-lisp :tangle yes (defun sd-quick-export--ledger-report () "Quick export for `ledger-mode' report buffers." (let ((old-buffer (current-buffer))) (with-output-to-temp-buffer "**SD Export**" (print "#+SETUPFILE: ~/.emacs.d/resources/templates/documents/default.org") (newline) (insert-buffer-substring old-buffer) (forward-line 10) (org-table-convert-region (point) (goto-char (point-max))) (setq more-lines-p t) (while more-lines-p (move-end-of-line 1) (newline) (setq more-lines-p (= 0 (forward-line 1)))) (org-open-file (org-latex-export-to-pdf))))) #+END_SRC *** From Nroff mode #+BEGIN_SRC emacs-lisp :tangle yes (defun sd-quick-export--nroff (macros) "Export Nroff/Groff buffer to PDF, with specified macro set." (let* ((file-exported-name (concat (file-name-sans-extension buffer-file-name) (format "-%s.pdf" macros))) (command-export (format "groff -%s -Tps %s | ps2pdf - > %s" macros (buffer-file-name) file-exported-name))) (shell-command command-export) (org-open-file file-exported-name))) #+END_SRC *** Quick export #+BEGIN_SRC emacs-lisp :tangle yes (defun sd-quick-export () "Quickly prettify and export current buffer to PDF." (interactive) (cond ((eq major-mode 'org-mode) (sd-quick-export--org)) ((eq major-mode 'nroff-mode) (sd-quick-export--nroff (read-string "Macro set used (ms, me, mm...): "))) ((eq major-mode 'emacs-lisp-mode) (message "No quick-export implemented for emacs-lisp-mode yet.")) ((eq major-mode 'ledger-report-mode) (sd-quick-export--ledger-report)) (t (message (format "No sd-quick-export backend for %s." major-mode))))) #+END_SRC ** Operate on whole buffer *** Fix indentation #+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 *** Beautify **** All types of buffers #+BEGIN_SRC emacs-lisp :tangle yes (defun sd-beautify-buffer () "Clean up buffer in the most general sense. This means performing the following actions: 1) indenting the buffer according to the major mode in force, 2) deleting trailing whitespaces. As well as a couple other things." (interactive) (sd-indent-buffer) (delete-trailing-whitespace) (cond ((string-equal major-mode "org-mode") (sd-org-fix-headlines-spacing)) ((string-equal major-mode "python-mode") (or (shell-command (concat "~/.local/bin/black " buffer-file-name)) (message "Could not find black Python formatter."))))) #+END_SRC **** TODO COMMENT For Org mode, specifically #+BEGIN_SRC emacs-lisp :tangle yes (defun sd-org-fix-headlines-spacing () "Insert the proper amount of newlines between Org headlines." (save-excursion (progn (goto-char (point-min)) (flush-lines "^[[:space:]]*$") (while (re-search-forward "^*" nil t) (beginning-of-line) (newline 2) (next-line) (newline)))) #+END_SRC ** Smart quitting :PROPERTIES: :test: t :END: *** Window #+BEGIN_SRC emacs-lisp :tangle yes (defun sd-delete-window-or-previous-buffer () "Delete window; if sole window, previous buffer." (interactive) (if (> (length (window-list)) 1) (delete-window) (previous-buffer))) #+END_SRC *** Frame #+BEGIN_SRC emacs-lisp :tangle yes (defun sd-delete-frame-or-kill-emacs () (interactive) "Delete frame; if sole frame, kill Emacs." (if (> (length (frame-list)) 1) (delete-frame) (save-buffers-kill-terminal))) #+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 100) (setq recentf-max-saved-items 100) (run-at-time nil (* 5 60) 'recentf-save-list) #+END_SRC *** Reload changed files silently #+BEGIN_SRC emacs-lisp :tangle yes (global-auto-revert-mode) #+END_SRC ** Frame *** Header & mode lines # Top of the buffer is more intuitive for buffer info, bottom is more # intuitive for buffer action. **** TODO Icons :PROPERTIES: :sd-unpack-path: sd-icons.el :END: We start by defining some icons we wish to include in our user interface. Emacs allows the usage of GIF images---this paves the way for UI elements which may be animated. #+BEGIN_SRC emacs-lisp :tangle yes (defcustom sd-icon-loading (create-image (concat user-emacs-directory "resources/images/icons/ellipsis.gif") 'gif nil :scale 0.4) "The GIF representing \"loading\". Not animated by default." :type 'sexp :version "27.1" :group 'sd) (defun sd-icon-loading () "Insert an animated blue ellipsis." (insert-image sd-icon-loading) (image-animate sd-icon-loading 0 t)) #+END_SRC **** Header line In Org mode, the document header line will be the title of the document we are working on currently. We start by defining keybindings for our header line buttons for navigating through open windows. #+BEGIN_SRC emacs-lisp :tangle yes (defvar sd-header-line-previous-buffer-keymap (let ((map (make-sparse-keymap))) (define-key map [header-line mouse-1] 'previous-buffer) map) "Keymap for what is displayed in the header line, with a single window.") (defvar sd-header-line-kill-buffer-keymap (let ((map (make-sparse-keymap))) (define-key map [header-line mouse-1] 'kill-buffer-and-window) map) "Keymap for closing current window.") (defvar sd-header-line-maximize-window-keymap (let ((map (make-sparse-keymap))) (define-key map [header-line mouse-1] 'delete-other-windows) map) "Keymap for maximizing the current window.") (defvar sd-header-line-minimize-window-keymap (let ((map (make-sparse-keymap))) (define-key map [header-line mouse-1] 'delete-window) map) "Keymap for minimizing the current window.") #+END_SRC Now, we describe the actual format of the header line. #+BEGIN_SRC emacs-lisp :tangle yes (use-package all-the-icons) (setq-default header-line-format '(:eval (list (if (eq (length (window-list)) 1) (propertize " ↤ " 'face 'org-meta-line 'mouse-face 'highlight 'keymap sd-header-line-previous-buffer-keymap 'help-echo "Return to previous window.") (list (propertize " ❌ " 'face 'org-meta-line 'mouse-face 'org-todo 'keymap sd-header-line-kill-buffer-keymap 'help-echo "Close this window.") (propertize " ⇱" 'face 'org-meta-line 'mouse-face 'highlight 'keymap sd-header-line-maximize-window-keymap 'help-echo "Maximize this window.") (propertize "⇲ " 'face 'org-meta-line 'mouse-face 'highlight 'keymap sd-header-line-minimize-window-keymap 'help-echo "Minimize this window."))) mode-line-buffer-identification))) (image-animate sd-icon-loading 0 t) #+END_SRC **** TODO COMMENT Mode line This interpretation of the ideal mode line is the result of carefully studying the default ~mode-line~, as well as studying various customizations online. #+BEGIN_SRC emacs-lisp :tangle yes (defvar sd-mode-line-lock-buffer-keymap (let ((map (make-sparse-keymap))) (define-key map [mode-line mouse-1] 'read-only-mode) map) "Keymap for locking/unlocking the current buffer.") #+END_SRC #+BEGIN_SRC emacs-lisp :tangle yes (setq-default mode-line-format (list mode-line-front-space '(:eval (if buffer-read-only (propertize "🔒" 'keymap sd-mode-line-lock-buffer-keymap 'help-echo "C-x C-q: unlock buffer.") (propertize "🔓" 'keymap sd-mode-line-lock-buffer-keymap 'help-echo "C-x C-q: lock buffer."))) '(:eval (if (buffer-modified-p) (propertize " 🖉 " 'help-echo "Buffer is modified.") (propertize " ✓ " 'help-echo "Buffer is saved."))) mode-line-modes " " mode-line-end-spaces)) #+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 We leave the default ~fill-column~ unchanged, so as to minimally disrupt a user's existing documents. We automatically break lines longer than =fill-column=. #+BEGIN_SRC emacs-lisp :tangle yes (add-hook 'org-mode-hook 'turn-on-auto-fill) #+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 when viewed on GNU/Linux systems. #+BEGIN_SRC emacs-lisp :tangle yes (when (string-equal system-type "gnu/linux") (add-hook 'org-mode-hook (lambda () "Beautify Org symbols." (push '("[ ]" . "○") ; Unchecked item prettify-symbols-alist) (push '("[X]" . "◉" ) ; Checked item prettify-symbols-alist) (push '("[-]" . "◎" ) ; Partially checked item prettify-symbols-alist) (push '("-" . "⁃" ) ; Plain list dash prettify-symbols-alist) (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 # This is just another comment. 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 no (load-theme 'sd-compagnon-dark) #+END_SRC #+BEGIN_SRC emacs-lisp :tangle no (load-theme 'molokai) #+END_SRC #+BEGIN_SRC emacs-lisp :tangle yes (load-theme 'wombat) #+END_SRC #+BEGIN_SRC emacs-lisp :tangle no (load-theme 'sd-light) #+END_SRC #+BEGIN_SRC emacs-lisp :tangle no (load-theme 'sd-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 [[https://www.interaction-design.org/literature/topics/affordances][affordance]] 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 | ~sd-light~ | ~sd-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 | Code syntax highlighting | /same/ | **** Red **** Green **** Blue **** Purple Purple is Emacs' main logo color. Since we use Emacs for coding a lot, code syntax highlighting to could be in the pink/purple shades. This is also a nod to Spacemacs' dark theme.2 hour *** TODO 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 no (setq-default cursor-type 'box) #+END_SRC *** TODO Fonts Here are some fonts I discovered and enjoyed since I began learning Emacs. **** Serif - Crimson Pro[fn::] :: ~variable-pitch~, default body text font - Inspired by Garamond - Linux Libertine[fn::] :: ~variable-pitch~, default body text font - Inspired by Garamond **** Sans serif - 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 - Jost[fn::https://indestructibletype.com/Jost.html] :: ~org-document-title~ and ~org-level-1~ - Ultra-modern - Tasteful amount of geometric inspiration - Open Sans[fn::https://www.opensans.com/] :: ~variable-pitch~ - Ooh geometric Bauhaus influences, look at me - Tall leading height is =harmonious= - 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 **** Monospace - Hack[fn::https://sourcefoundry.org/hack/] :: ~default~ and ~fixed-pitch~, default code font - Legible, modern monospace font - Strict, sharp, uncompromising - 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! - Courier Prime[fn::https://quoteunquoteapps.com/courierprime/index.php] :: monospace in print **** COMMENT 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 no (if (< screen-width 1920) (default-font) else) #+END_SRC ** TODO /Wealthy/ document theme #+NAME: claude-garamont #+CAPTION[Claude Garamont, an icon of font design]: Claude Garamont, an icon of font design. World-renowned for his elegant typefaces, which inspired many generations of typographers. #+ATTR_LATEX: :width 0.4\textwidth [[~/.emacs.d/resources/images/smart-documents/ClaudeGaramond.jpeg]] #+LATEX: \garamond \lettrine{G}{ood} golly, nobody wishes for a /pedestrian/ theme! Let your entourage know that you're rocking an editor fit for a king with this finely crafted `wealthy' theme. Selecting it shall enable the following fancitudes: 1. The default font shall be sublimed in the form of /EB Garamond/ 2. Bullets will be tastefully replaced with pointing fingers 3. Heading stars will be replaced with Black Queen chess pieces #+BEGIN_QUOTE \lettrine{C}{laude} Garamont (c. 1510--1561), known commonly as *Claude Garamond*, was a French type designer, publisher and punch-cutter based in Paris. Garamond worked as an engraver of punches, the masters used to stamp matrices, the moulds used to cast metal type. He worked in the tradition now called old-style serif design, which produced letters with a relatively organic structure resembling handwriting with a pen but with a slightly more structured and upright design. Considered one of the leading type designers of all time, he is recognised to this day for the elegance of his typefaces. Many old-style serif typefaces are collectively known as Garamond, named after the designer. From [[https://en.wikipedia.org/wiki/Claude_Garamond]] #+END_QUOTE #+LATEX: \publicsans *** Symbol substitution #+BEGIN_SRC emacs-lisp :tangle yes (defun sd-wealthy () "Beautify symbols for our wealthy theme." (push '("-" . "☞" ) prettify-symbols-alist) ; unnumbered bullets (push '("*" . "♛" ) prettify-symbols-alist) ; headings (prettify-symbols-mode)) #+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 no (profiler-stop) #+END_SRC ** Profiling---report #+BEGIN_SRC emacs-lisp :tangle no (profiler-report) #+END_SRC * Conclusion In this configuration file, we described a series of customization steps taken to make Emacs more palatable to modern word processors users. * COMMENT Local files variables :PROPERTIES: :UNNUMBERED: t :END: If the following variable is set to nil, ~org-babel~ will not ask to confirm the evaluation of source code blocks during export or tangling of this very file.