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