Emacs Lisp

Личный сайт Go-разработчика из Казани

1;; This gives an introduction to Emacs Lisp in 15 minutes (v0.2d) 2;; 3;; First make sure you read this text by Peter Norvig: 4;; http://norvig.com/21-days.html 5;; 6;; Then install latest version of GNU Emacs: 7;; 8;; Debian: apt-get install emacs (or see your distro instructions) 9;; OSX: https://emacsformacosx.com/ 10;; Windows: https://ftp.gnu.org/gnu/emacs/windows/ 11;; 12;; More general information can be found at: 13;; http://www.gnu.org/software/emacs/#Obtaining 14 15;; Important warning: 16;; 17;; Going through this tutorial won't damage your computer unless 18;; you get so angry that you throw it on the floor. In that case, 19;; I hereby decline any responsibility. Have fun! 20 21;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 22;; 23;; Fire up Emacs. 24;; 25;; Hit the `q' key to dismiss the welcome message. 26;; 27;; Now look at the gray line at the bottom of the window: 28;; 29;; "*scratch*" is the name of the editing space you are now in. 30;; This editing space is called a "buffer". 31;; 32;; The scratch buffer is the default buffer when opening Emacs. 33;; You are never editing files: you are editing buffers that you 34;; can save to a file. 35;; 36;; "Lisp interaction" refers to a set of commands available here. 37;; 38;; Emacs has a built-in set of commands available in every buffer, 39;; and several subsets of commands available when you activate a 40;; specific mode. Here we use the `lisp-interaction-mode', which 41;; comes with commands to evaluate and navigate within Elisp code. 42 43;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 44;; 45;; Semi-colons start comments anywhere on a line. 46;; 47;; Elisp programs are made of symbolic expressions ("sexps"): 48(+ 2 2) 49 50;; This symbolic expression reads as "Add 2 to 2". 51 52;; Sexps are enclosed into parentheses, possibly nested: 53(+ 2 (+ 1 1)) 54 55;; A symbolic expression contains atoms or other symbolic 56;; expressions. In the above examples, 1 and 2 are atoms, 57;; (+ 2 (+ 1 1)) and (+ 1 1) are symbolic expressions. 58 59;; From `lisp-interaction-mode' you can evaluate sexps. 60;; Put the cursor right after the closing parenthesis then 61;; hold down the control and hit the j keys ("C-j" for short). 62 63(+ 3 (+ 1 2)) 64;; ^ cursor here 65;; `C-j' => 6 66 67;; `C-j' inserts the result of the evaluation in the buffer. 68 69;; `C-xC-e' displays the same result in Emacs bottom line, 70;; called the "echo area". We will generally use `C-xC-e', 71;; as we don't want to clutter the buffer with useless text. 72 73;; `setq' stores a value into a variable: 74(setq my-name "Bastien") 75;; `C-xC-e' => "Bastien" (displayed in the echo area) 76 77;; `insert' will insert "Hello!" where the cursor is: 78(insert "Hello!") 79;; `C-xC-e' => "Hello!" 80 81;; We used `insert' with only one argument "Hello!", but 82;; we can pass more arguments -- here we use two: 83 84(insert "Hello" " world!") 85;; `C-xC-e' => "Hello world!" 86 87;; You can use variables instead of strings: 88(insert "Hello, I am " my-name) 89;; `C-xC-e' => "Hello, I am Bastien" 90 91;; You can combine sexps into functions: 92(defun hello () (insert "Hello, I am " my-name)) 93;; `C-xC-e' => hello 94 95;; You can evaluate functions: 96(hello) 97;; `C-xC-e' => Hello, I am Bastien 98 99;; The empty parentheses in the function's definition means that 100;; it does not accept arguments. But always using `my-name' is 101;; boring, let's tell the function to accept one argument (here 102;; the argument is called "name"): 103 104(defun hello (name) (insert "Hello " name)) 105;; `C-xC-e' => hello 106 107;; Now let's call the function with the string "you" as the value 108;; for its unique argument: 109(hello "you") 110;; `C-xC-e' => "Hello you" 111 112;; Yeah! 113 114;; Take a breath. 115 116;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 117;; 118;; Now switch to a new buffer named "*test*" in another window: 119 120(switch-to-buffer-other-window "*test*") 121;; `C-xC-e' 122;; => [screen has two windows and cursor is in the *test* buffer] 123 124;; Mouse over the top window and left-click to go back. Or you can 125;; use `C-xo' (i.e. hold down control-x and hit o) to go to the other 126;; window interactively. 127 128;; You can combine several sexps with `progn': 129(progn 130 (switch-to-buffer-other-window "*test*") 131 (hello "you")) 132;; `C-xC-e' 133;; => [The screen has two windows and cursor is in the *test* buffer] 134 135;; Now if you don't mind, I'll stop asking you to hit `C-xC-e': do it 136;; for every sexp that follows. 137 138;; Always go back to the *scratch* buffer with the mouse or `C-xo'. 139 140;; It's often useful to erase the buffer: 141(progn 142 (switch-to-buffer-other-window "*test*") 143 (erase-buffer) 144 (hello "there")) 145 146;; Or to go back to the other window: 147(progn 148 (switch-to-buffer-other-window "*test*") 149 (erase-buffer) 150 (hello "you") 151 (other-window 1)) 152 153;; You can bind a value to a local variable with `let': 154(let ((local-name "you")) 155 (switch-to-buffer-other-window "*test*") 156 (erase-buffer) 157 (hello local-name) 158 (other-window 1)) 159 160;; No need to use `progn' in that case, since `let' also combines 161;; several sexps. 162 163;; Let's format a string: 164(format "Hello %s!\n" "visitor") 165 166;; %s is a place-holder for a string, replaced by "visitor". 167;; \n is the newline character. 168 169;; Let's refine our function by using format: 170(defun hello (name) 171 (insert (format "Hello %s!\n" name))) 172 173(hello "you") 174 175;; Let's create another function which uses `let': 176(defun greeting (name) 177 (let ((your-name "Bastien")) 178 (insert (format "Hello %s!\n\nI am %s." 179 name ; the argument of the function 180 your-name ; the let-bound variable "Bastien" 181 )))) 182 183;; And evaluate it: 184(greeting "you") 185 186;; Some functions are interactive: 187(read-from-minibuffer "Enter your name: ") 188 189;; Evaluating this function returns what you entered at the prompt. 190 191;; Let's make our `greeting' function prompt for your name: 192(defun greeting (from-name) 193 (let ((your-name (read-from-minibuffer "Enter your name: "))) 194 (insert (format "Hello!\n\nI am %s and you are %s." 195 from-name ; the argument of the function 196 your-name ; the let-bound var, entered at prompt 197 )))) 198 199(greeting "Bastien") 200 201;; Let's complete it by displaying the results in the other window: 202(defun greeting (from-name) 203 (let ((your-name (read-from-minibuffer "Enter your name: "))) 204 (switch-to-buffer-other-window "*test*") 205 (erase-buffer) 206 (insert (format "Hello %s!\n\nI am %s." your-name from-name)) 207 (other-window 1))) 208 209;; Now test it: 210(greeting "Bastien") 211 212;; Take a breath. 213 214;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 215;; 216;; Let's store a list of names: 217;; If you want to create a literal list of data, use ' to stop it from 218;; being evaluated - literally, "quote" the data. 219(setq list-of-names '("Sarah" "Chloe" "Mathilde")) 220 221;; Get the first element of this list with `car': 222(car list-of-names) 223 224;; Get a list of all but the first element with `cdr': 225(cdr list-of-names) 226 227;; Add an element to the beginning of a list with `push': 228(push "Stephanie" list-of-names) 229 230;; NOTE: `car' and `cdr' don't modify the list, but `push' does. 231;; This is an important difference: some functions don't have any 232;; side-effects (like `car') while others have (like `push'). 233 234;; Let's call `hello' for each element in `list-of-names': 235(mapcar 'hello list-of-names) 236 237;; Refine `greeting' to say hello to everyone in `list-of-names': 238(defun greeting () 239 (switch-to-buffer-other-window "*test*") 240 (erase-buffer) 241 (mapcar 'hello list-of-names) 242 (other-window 1)) 243 244(greeting) 245 246;; Remember the `hello' function we defined above? It takes one 247;; argument, a name. `mapcar' calls `hello', successively using each 248;; element of `list-of-names' as the argument for `hello'. 249 250;; Now let's arrange a bit what we have in the displayed buffer: 251 252(defun replace-hello-by-bonjour () 253 (switch-to-buffer-other-window "*test*") 254 (goto-char (point-min)) 255 (while (search-forward "Hello") 256 (replace-match "Bonjour")) 257 (other-window 1)) 258 259;; (goto-char (point-min)) goes to the beginning of the buffer. 260;; (search-forward "Hello") searches for the string "Hello". 261;; (while x y) evaluates the y sexp(s) while x returns something. 262;; If x returns `nil' (nothing), we exit the while loop. 263 264(replace-hello-by-bonjour) 265 266;; You should see all occurrences of "Hello" in the *test* buffer 267;; replaced by "Bonjour". 268 269;; You should also get an error: "Search failed: Hello". 270;; 271;; To avoid this error, you need to tell `search-forward' whether it 272;; should stop searching at some point in the buffer, and whether it 273;; should silently fail when nothing is found: 274 275;; (search-forward "Hello" nil t) does the trick: 276 277;; The `nil' argument says: the search is not bound to a position. 278;; The `'t' argument says: silently fail when nothing is found. 279 280;; We use this sexp in the function below, which doesn't throw an error: 281 282(defun hello-to-bonjour () 283 (switch-to-buffer-other-window "*test*") 284 (erase-buffer) 285 ;; Say hello to names in `list-of-names' 286 (mapcar 'hello list-of-names) 287 (goto-char (point-min)) 288 ;; Replace "Hello" by "Bonjour" 289 (while (search-forward "Hello" nil t) 290 (replace-match "Bonjour")) 291 (other-window 1)) 292 293(hello-to-bonjour) 294 295;; Let's boldify the names: 296 297(defun boldify-names () 298 (switch-to-buffer-other-window "*test*") 299 (goto-char (point-min)) 300 (while (re-search-forward "Bonjour \\(.+\\)!" nil t) 301 (add-text-properties (match-beginning 1) 302 (match-end 1) 303 (list 'face 'bold))) 304 (other-window 1)) 305 306;; This functions introduces `re-search-forward': instead of 307;; searching for the string "Bonjour", you search for a pattern, 308;; using a "regular expression" (abbreviated in the prefix "re-"). 309 310;; The regular expression is "Bonjour \\(.+\\)!" and it reads: 311;; the string "Bonjour ", and 312;; a group of | this is the \\( ... \\) construct 313;; any character | this is the . 314;; possibly repeated | this is the + 315;; and the "!" string. 316 317;; Ready? Test it! 318 319(boldify-names) 320 321;; `add-text-properties' adds... text properties, like a face. 322 323;; OK, we are done. Happy hacking! 324 325;; If you want to know more about a variable or a function: 326;; 327;; C-h v a-variable RET 328;; C-h f a-function RET 329;; 330;; To read the Emacs Lisp manual with Emacs: 331;; 332;; C-h i m elisp RET 333;; 334;; To read an online introduction to Emacs Lisp: 335;; https://www.gnu.org/software/emacs/manual/html_node/eintr/index.html

Further Reading