├── README.org └── demo ├── Makefile └── emacs.org /README.org: -------------------------------------------------------------------------------- 1 | #+TITLE: Literal Emacs configuration tutorial 2 | #+AUTHOR: Pierre Lecocq 3 | #+EMAIL: pierre.lecocq@gmail.com 4 | #+STARTUP: content 5 | 6 | * Introduction 7 | 8 | ** The goal 9 | 10 | In our world, there are two main ways to manage your Emacs configuration: 11 | 12 | - one and only one configuration file (for the minimalists) 13 | - several files that separate configuration tasks and goals (for the arrangement maniacs) 14 | 15 | And there are two main ways to write your Emacs configuration: 16 | 17 | - write pure emacs lisp code 18 | - write prose that embed emacs lisp code that is extracted afterwards by a "generator". This is called "[[https://en.wikipedia.org/wiki/Literate_programming][literate programming]]" 19 | 20 | We will see here how to generate an Emacs configuration organized in several files using the literate programming approch. 21 | 22 | ** The tools 23 | 24 | In order to reach our goal, we will use: 25 | 26 | - [[https://www.gnu.org/software/emacs/][Emacs]] (thanks, genious) 27 | - [[http://orgmode.org/][org-mode]] to extract emacs lisp code from a literal file. *Org mode* is an amazing tool "for keeping notes, maintaining TODO lists, planning projects, and authoring documents with a fast and effective plain-text system". 28 | - a [[https://www.gnu.org/software/make/][Makefile]] to generate the final emacs lisp code (but you can do it with a shell script or even by hand in your terminal) 29 | 30 | ** Disclaimer 31 | 32 | This is just a *proof of concept*. 33 | 34 | It is *not* a document about Emacs configuration but just a way to write, generate, organize and deal with your Emacs preferences. 35 | 36 | If you want to learn more about the editor's configuration itself, please refer to the [[https://github.com/pierre-lecocq/emacs4developers][Emacs for developers]] tutorial. 37 | 38 | ** The workflow 39 | 40 | Here is the menu: 41 | 42 | 1. Write a =.org= file with code blocks and literal comments 43 | 2. Generate some emacs lisp code thanks to the =Makefile= (one command line. Period.) 44 | 3. Load the newly generated configuration in Emacs 45 | 46 | The input is (in the =demo= folder provided with this repository): 47 | 48 | - one =emacs.org= file 49 | - one =Makefile= 50 | 51 | The output is: 52 | 53 | - one =emacs.el= file (generated from the org file) 54 | - several emacs lisp files (also generated from the org file) 55 | - =lisp/common.el= that setup all the common configuration variables 56 | - =lisp/packages.el= that installs every external packages you need 57 | - =lisp/hooks.el= that gather all hooks 58 | - one =lisp/vendor= folder where the Emacs internal package manager will download and store the packages (can be tweaked from the Emacs configuration itself) 59 | 60 | Of course, this nomenclature is an example and a dummy one and you are free to change and adapt it to your own needs and wills. I encourage you to do so and experiment by yourself. Let's hack this tutorial! 61 | 62 | * The org file 63 | 64 | ** The general idea 65 | 66 | The idea is to write a =emacs.org= file, with comments (regular text) and code (that will engender the Emacs final configuration). 67 | 68 | The prose (or "comments", a.ka. "regular text") is written like in any regular text file. No need to encapsulate it in any kind of tag. I let you take a look at the [[http://orgmode.org/manual/Markup.html][org-mode syntax documentation]] if you do not know it already. 69 | 70 | The code blocks is the interesting parts. It will, after the "generator" pass, produce some emacs lisp code that IS (/will be/) your Emacs configuration. 71 | They are surrended by tags that indicate to the "generator" what kind of code it is and what is their final location. 72 | 73 | To sum up: 74 | 75 | 1. we write a regular org file with all text we want 76 | 2. we embed some code blocks and tell in which file the code will be located 77 | 3. we use the "generator" against this =.org= file to produce the final Emacs configuration 78 | 79 | Easy as pie. 80 | 81 | ** The file content 82 | 83 | =Emacs= has a very powerful and large range of configuration variables/functions. It all depends on your usage of the editor and on your patience to read the documentation. 84 | 85 | Let's imagine you want to put all your common configuration in a single file that will be executed in a first place, before everything else in your configuration chain. 86 | 87 | For this, we need to write some code blocks whose code will be generated in the =lisp/common.el= file. For this, let's see an example: 88 | 89 | #+begin_src emacs-lisp 90 | > #+begin_src emacs-lisp :tangle lisp/common.el 91 | > (setq debug-on-error t) 92 | > (setq user-full-name "My name" 93 | > user-mail-address "my.name@mail.com") 94 | > #+end_src 95 | #+end_src 96 | 97 | /Note: remove the '>' characters in the begining of each lines/ 98 | 99 | First, let's focus on the =begin_src= tag line. 100 | 101 | - it begins by =begin_src= and ends by =end_src= that tells the "generator" that the text between is code 102 | - it provides the language type to generate. Here, we want =emacs-lisp= 103 | - and, with the =:tangle= argument (=lisp/common.el= here, but it can be whatever you want), it tells where the final code should be placed 104 | 105 | Of course, we can multiply the code blocks to the infinite. The idea is to separate the code blocks by literal comments in order to tidy them AND to take advantage of the power of the outline concept of =org-mode.= 106 | 107 | 108 | *PLEASE NOW REFER TO THE WHOLE (BUT SIMPLE) EXAMPLE GIVEN WITH THIS TUTORIAL HERE*: [[./demo/emacs.org][emacs.org]] 109 | 110 | And look at its [[https://raw.githubusercontent.com/pierre-lecocq/literal-emacs/master/demo/emacs.org][raw content]] to see how it is written. 111 | 112 | * The Makefile 113 | 114 | The =Makefile= will help us, with one command to generate and execute the emacs lisp code. 115 | 116 | The key is to write a rule that will call Emacs with some code in argument: 117 | 118 | - Require the =org-mode= package 119 | - Use the function =org-babel-load-file= to parse the file, extract the code blocks content and execute it 120 | 121 | Here is the command line if you want to run it by yourself (in the =demo= folder of this repository, for example): 122 | 123 | #+begin_src sh 124 | emacs --batch --eval "(require 'org)" --eval "(org-babel-load-file \"emacs.org\")" 125 | #+end_src 126 | 127 | 128 | *PLEASE NOW REFER TO THE SAMPLE MAKEFILE GIVEN WITH THIS TUTORIAL HERE*: [[./demo/Makefile][Makefile]] 129 | 130 | 131 | Here are the simple command you can run: 132 | 133 | - =make= to generate the emacs lisp code 134 | - =make test= to test the generated emacs lisp configuration 135 | - =make clean= to clean up everything and be ready to begin from zero 136 | 137 | Note that if you change something in your =emacs.org= file, you must run =make= again. And you should NEVER edit the =emacs.el= generated file. 138 | 139 | * Testing 140 | 141 | In order to test this proof of concept or your own literal configuration attempt, let's follow these very easy steps: 142 | 143 | 1. Type =make= to generate the =emacs.el= final file and all the =lisp/*.el= files 144 | 2. Type =emacs -Q -l emacs.el= to try it out. The =-Q= option anihilate all other configuration and the =-l= option make this precise =emacs.el= file the main configuration file 145 | 146 | You should have a brand new configured Emacs running now. 147 | 148 | Of course the sample [[https://github.com/pierre-lecocq/literal-emacs/blob/master/demo/Makefile][Makefile]] provides a =make test= rule to automate this. 149 | 150 | * Adopting 151 | 152 | Once you have tweaked and adpated everything to your needs, you may want to make this =emacs.el= your default configuration file. 153 | 154 | Here is a very easy workaround to achieve this: create a =~/.emacs= file and set its content to =(load-file "/path/to/the/dir/that/contains/the/generated/emacs.el")= 155 | 156 | Then, when you launch Emacs, it will load the new generated =emacs.el= file! 157 | 158 | * Conclusion 159 | 160 | As you can see, the /method/ is here, but all the /work/ belongs to you. 161 | 162 | You MUST write and try your own Emacs configuration since it MUST fit YOUR needs. 163 | 164 | Writing in literate programming is a demarche that requires some time but can bring you a lot benefits in term of organization and readability, especially with the Emacs users gift that is =org-mode=. 165 | 166 | If you want to see a real-world example that is used every day, you can see my own Emacs configuration repository [[https://github.com/pierre-lecocq/emacs.d/tree/literal][here]]. It might differ a little bit from this tutorial in term of shape but the idea is exactly the same. 167 | -------------------------------------------------------------------------------- /demo/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # File: Makefile 3 | # Time-stamp: <2015-10-07 08:19:16> 4 | # Copyright (C) 2015 Pierre Lecocq 5 | # Description: Makefile used to generate a lisp file from an org file 6 | # 7 | 8 | # Set configuration folder path 9 | SRC_DIR=$(shell pwd) 10 | 11 | # Source file (the org file) 12 | SRC_FILE=$(SRC_DIR)/emacs.org 13 | 14 | # Destination file (the emacs lisp file) 15 | DEST_FILE=$(SRC_DIR)/emacs.el 16 | 17 | # Destination directory where the lisp code will be generated 18 | DEST_DIR=$(SRC_DIR)/lisp 19 | 20 | # I love poneys 21 | .PHONY: test build clean love 22 | 23 | # Main rule 24 | all: build 25 | 26 | # Generate lisp and compile it 27 | build: 28 | mkdir -p $(DEST_DIR); \ 29 | emacs --batch \ 30 | --eval "(require 'org)" \ 31 | --eval "(org-babel-load-file \"$(SRC_FILE)\")" 32 | 33 | # Testing is doubting 34 | test: 35 | emacs -Q -l $(DEST_FILE) 36 | 37 | # Housework 38 | clean: 39 | rm -rf $(DEST_FILE) $(DEST_DIR) 40 | 41 | # Because "make love" is important 42 | love: clean build 43 | -------------------------------------------------------------------------------- /demo/emacs.org: -------------------------------------------------------------------------------- 1 | * Common 2 | 3 | Generic variables setup 4 | 5 | #+begin_src emacs-lisp :tangle lisp/common.el 6 | (setq debug-on-error t) 7 | 8 | (setq user-full-name "My name" 9 | user-mail-address "my.name@mail.com") 10 | #+end_src 11 | 12 | Require some internal libs 13 | 14 | #+begin_src emacs-lisp :tangle lisp/common.el 15 | (require 'linum) 16 | (require 'paren) 17 | #+end_src 18 | 19 | Activate some internal features 20 | 21 | #+begin_src emacs-lisp :tangle lisp/common.el 22 | (auto-compression-mode 1) 23 | (column-number-mode 1) 24 | (global-auto-revert-mode 1) 25 | (global-font-lock-mode 1) 26 | (global-hl-line-mode 1) 27 | (line-number-mode 1) 28 | (show-paren-mode 1) 29 | (transient-mark-mode 1) 30 | (which-function-mode 1) 31 | #+end_src 32 | 33 | No need backup. Let's be brave. 34 | 35 | #+begin_src emacs-lisp :tangle lisp/common.el 36 | (setq backup-inhibited t 37 | make-backup-files nil 38 | auto-save-default nil) 39 | #+end_src 40 | 41 | Startup messgage and scratch buffer setup 42 | 43 | #+begin_src emacs-lisp :tangle lisp/common.el 44 | (setq initial-scratch-message (format ";; Scratch buffer - started on %s\n\n" (current-time-string)) 45 | inhibit-startup-message t 46 | inhibit-splash-screen t) 47 | #+end_src 48 | 49 | Uniquify the buffer's name 50 | 51 | #+begin_src emacs-lisp :tangle lisp/common.el 52 | (setq uniquify-buffer-name-style 'forward uniquify-separator "/") 53 | #+end_src 54 | 55 | * Packages 56 | 57 | ** Package manager 58 | Setup the package manager 59 | 60 | #+begin_src emacs-lisp :tangle lisp/packages.el 61 | (require 'package) 62 | 63 | (setq package-user-dir (expand-file-name "./lisp/vendor")) 64 | 65 | (setq package-archives 66 | '(("melpa" . "http://melpa.org/packages/") 67 | ("gnu" . "http://elpa.gnu.org/packages/") 68 | ("marmalade" . "http://marmalade-repo.org/packages/") 69 | ("org" . "http://orgmode.org/elpa/"))) 70 | 71 | (package-initialize) 72 | 73 | (when (not package-archive-contents) 74 | (package-refresh-contents)) 75 | #+end_src 76 | 77 | ** External packages 78 | 79 | Install and configure =anzu= 80 | 81 | #+begin_src emacs-lisp :tangle lisp/packages.el 82 | (unless (package-installed-p 'anzu) 83 | (package-install 'anzu)) 84 | (global-anzu-mode +1) 85 | (set-face-attribute 'anzu-mode-line nil :foreground "yellow") 86 | #+end_src 87 | 88 | Install and configure =autopair= 89 | 90 | #+begin_src emacs-lisp :tangle lisp/packages.el 91 | (unless (package-installed-p 'autopair) 92 | (package-install 'autopair)) 93 | (autopair-global-mode t) 94 | #+end_src 95 | 96 | Install and configure =darkmin-theme= 97 | 98 | #+begin_src emacs-lisp :tangle lisp/packages.el 99 | (unless (package-installed-p 'darkmine-theme) 100 | (package-install 'darkmine-theme)) 101 | (load-theme 'darkmine t) 102 | #+end_src 103 | 104 | Install and configure =symon-mode= 105 | 106 | #+begin_src emacs-lisp :tangle lisp/packages.el 107 | (unless (package-installed-p 'symon) 108 | (package-install 'symon)) 109 | (setq symon-delay 5) 110 | (symon-mode t) 111 | #+end_src 112 | 113 | * Hooks 114 | 115 | Set the prog-mode hook 116 | 117 | #+begin_src emacs-lisp :tangle lisp/hooks.el 118 | (defun hook-prog-mode () 119 | "Hook for Prog mode." 120 | (local-set-key (kbd "C-c ") 'hs-show-block) 121 | (local-set-key (kbd "C-c ") 'hs-hide-block) 122 | (local-set-key (kbd "C-c ") 'hs-hide-all) 123 | (local-set-key (kbd "C-c ") 'hs-show-all) 124 | (hs-minor-mode t) 125 | (rainbow-delimiters-mode)) 126 | 127 | (add-hook 'prog-mode-hook #'hook-prog-mode) 128 | #+end_src 129 | 130 | Set the text-mode hook 131 | 132 | #+begin_src emacs-lisp :tangle lisp/hooks.el 133 | (defun hook-text-mode () 134 | "Hook for Text mode." 135 | (linum-mode 1) 136 | (make-local-variable 'linum-format) 137 | (setq linum-format " %d ")) 138 | 139 | (add-hook 'text-mode-hook #'hook-text-mode) 140 | #+end_src 141 | 142 | * Bootstrap 143 | 144 | We create a bootstrap file to load all the lisp files that were generated by the code blocks above 145 | 146 | #+begin_src emacs-lisp :tangle emacs.el 147 | (load-file "lisp/common.el") 148 | (load-file "lisp/packages.el") 149 | (load-file "lisp/hooks.el") 150 | #+end_src 151 | --------------------------------------------------------------------------------