From f707db04a094850cfbb069224489610f9ad333cc Mon Sep 17 00:00:00 2001 From: Bastien Guerry Date: Thu, 25 Jul 2013 13:20:35 +0200 Subject: New file elisp.html.markdown for Emacs Lisp in 15 minutes. --- elisp.html.markdown | 309 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 309 insertions(+) create mode 100644 elisp.html.markdown diff --git a/elisp.html.markdown b/elisp.html.markdown new file mode 100644 index 00000000..80cdc7eb --- /dev/null +++ b/elisp.html.markdown @@ -0,0 +1,309 @@ +--- +language: elisp +contributors: + - ["Bastien Guerry", "http://bzg.fr"] +filename: elisp.el +--- + +```elisp +;; This gives an introduction to Emacs Lisp in 15 minutes. +;; +;; First make sure you read this text by Peter Norvig: +;; http://norvig.com/21-days.html +;; +;; Then install GNU Emacs 24.3: +;; +;; Debian: apt-get install emacs (or see your distro instructions) +;; MacOSX: http://emacsformacosx.com/emacs-builds/Emacs-24.3-universal-10.6.8.dmg +;; Windows: http://ftp.gnu.org/gnu/windows/emacs/emacs-24.3-bin-i386.zip +;; +;; More general information can be found at: +;; http://www.gnu.org/software/emacs/#Obtaining +;; +;; Fire up Emacs. +;; +;; Look at the gray line at the bottom of the window: +;; +;; "*scratch*" is the name of the editing space you are now in. +;: This editing space is called a "buffer". +;; +;; The scratch buffer is the default buffer when opening Emacs. +;; You are never editing files: you are editing buffers that you +;; can save to a file. +;; +;; "Lisp interaction" refers to a set of commands available here. +;; +;; This set of command is loaded on top of core Emacs commands. +;; For exemple, `save-buffer' is a core Emacs command to save a +;; buffer to its associated file, and `eval-defun' is a command +;; loaded from the `lisp-interaction-mode' you are now in. +;; +;; Semi-columns start comments anywhere on a line. +;; +;; Elisp programs are made of symbolic expressions ("sexps"): +(+ 2 2) + +;; This symbolic expression reads as "Add 3 to 3". + +;; Sexps are enclosed into parentheses, possibly nested: +(+ 2 (+ 1 1)) + +;; A symbolic expression contains atoms or other symbolic +;; expressions. In the above examples, 1 and 2 are atoms, +;; (+ 2 (+ 1 1)) and (+ 1 1) are symbolic expressions. + +;; From `lisp-interaction-mode' you can evaluate sexps. +;; Put the cursor right after the closing parenthesis then +;; hit the control and the j keys ("C-j" for short). + +(+ 3 (+ 1 2)) +;; ^ cursor here +;; `C-j' => 6 + +;; `C-j' inserts the result of the evaluation in the buffer. + +;; `C-xC-e' displays the same result in Emacs bottom line, +;: called the "minibuffer". We will generally use `C-xC-e', +;; as we don't want to clutter the buffer with useless text. + +;; `setq' stores a value into a variable: +(setq my-name "Bastien") +;; `C-xC-e' => "Bastien" (displayed in the mini-buffer) + +;; `insert' will insert "Hello!" where the cursor is: +(insert "Hello!") +;; `C-xC-e' => "Hello!" + +;; We used `insert' with only one argument "Hello!", but +;; we can pass more arguments -- here we use two: + +(insert "Hello" " world!") +;; `C-xC-e' => "Hello world!" + +;; You can use variables instead of strings: +(insert "Hello, I am " my-name) +;; `C-xC-e' => "Hello, I am Bastien" + +;; You can combine sexps into functions: +(defun hello () (insert "Hello, I am " my-name)) +;; `C-xC-e' => hello + +;; You can evaluate functions: +(hello) +;; `C-xC-e' => Hello, I am Bastien + +;; The empty parentheses in the function's definition means that +;; it does not accept arguments. But always using `my-name' is +;; boring, let's tell the function to accept one argument (here +;; the argument is called "name"): + +(defun hello (name) (insert "Hello " name)) +;; `C-xC-e' => hello + +;; Now let's call the function with the string "you" as the value +;; for its unique argument: +(hello "you") +;; `C-xC-e' => "Hello you" + +;; Yeah! + +;; Take a breath. + +;; Now switch to a new buffer named "*test*" in another window: + +(switch-to-buffer-other-window "*test*") +;; `C-xC-e' +;: => [screen has two windows and cursor is in the *test* buffer] + +;; Use the mouse to go back to the window where you code. + +;; You can combine several sexps with `progn': +(progn + (switch-to-buffer-other-window "*test*") + (hello "you")) +;; `C-xC-e' +;: => [The screen has two windows and cursor is in the *test* buffer] + +;; Now if you don't mind, I'll stop asking you to hit `C-xC-e': do it +;; for every sexp that follows. + +;; It's often useful to erase the buffer: +(progn + (switch-to-buffer-other-window "*test*") + (erase-buffer) + (hello "you")) + +;; Or to go back to the other window: +(progn + (switch-to-buffer-other-window "*test*") + (erase-buffer) + (hello "you") + (other-window 1)) + +;; You can bind a value to a local variable with `let': +(let ((local-name "you")) + (switch-to-buffer-other-window "*test*") + (erase-buffer) + (hello local-name) + (other-window 1)) + +;; No need to use `progn' in that case, since `let' also combines +;; several sexps. + +;; Let's format a string: +(format "Hello %s!\n" "visitor") + +;; %s is a place-holder for a string, replaced by "Alice". +;; \n is the newline character. + +;; Let's refine our function by using format: +(defun hello (name) + (insert (format "Hello %s!\n" name))) + +(hello "you") + +;; Let's create another function which uses `let': +(defun greeting (name) + (let ((your-name "Bastien")) + (insert (format "Hello %s!\n\nI'am %s." + name ; the argument of the function + your-name ; the let-bound variable "Roger" + )))) + +;; And evaluate it: +(greeting "you") + +;; Some function are interactive: +(read-from-minibuffer "Enter your name: ") + +;; Evaluating this function returns what you entered at the prompt. + +;; Let's make our `greeting' function prompts for your name: +(defun greeting (from-name) + (let ((your-name (read-from-minibuffer "Enter your name: "))) + (insert (format "Hello!\n\I am %s and you are %s." + from-name ; the argument of the function + your-name ; the let-bound var, entered at prompt + )))) + +(greeting "Bastien") + +;; Let complete it by displaying the results in the other window: +(defun greeting (from-name) + (let ((your-name (read-from-minibuffer "Enter your name: "))) + (switch-to-buffer-other-window "*test*") + (erase-buffer) + (insert (format "Hello %s!\n\nI'am %s." your-name from-name)) + (other-window 1))) + +;; Now test it: +(greeting "Bastien") + +;; Take a breath. + +;; Let's store a list of names: +(setq list-of-names '("Sarah" "Chloe")) + +;; Get the first element of this list with `car': +(car list-of-names) + +;; Get a list of all but the first element with `cdr': +(cdr list-of-names) + +;; Add an element to the beginning of a list with `push': +(push "Stephanie" list-of-names) + +;; NOTE: `car' and `cdr' don't modify the list, but `push' does. + +;; Let's call `hello' for each element in `list-of-names': +(mapcar 'hello list-of-names) + +;; Refine `greeting' to say hello to everyone in `list-of-names': +(defun greeting () + (switch-to-buffer-other-window "*test*") + (erase-buffer) + (mapcar 'hello list-of-names) + (other-window 1)) + +(greeting) + +;; Remember the `hello' function we defined above? It takes one +;; argument, a name. `mapcar' calls `hello', successively using each +;; element of `list-of-names' as the argument for `hello'. + +;; Now let's arrange a bit what we have in the displayed buffer: + +(defun replace-hello-by-bonjour () + (switch-to-buffer-other-window "*test*") + (goto-char (point-min)) + (while (search-forward "Hello") + (replace-match "Bonjour")) + (other-window 1)) + +;; (goto-char (point-min)) goes to the beginning of the buffer. +;; (search-forward "Hello") searches for the string "Hello". +;; (while x y) evaluates the y sexp(s) while x returns something. +;; If x returns `nil' (nothing), we exit the while loop. + +(replace-hello-by-bonjour) + +;; You should see all occurrences of "Hello" in the *test* buffer +;; replaced by "Bonjour". + +;; You should also get an error: "Search failed: Hello". +;; You need to tell search-forward whether it should stop searching +;; at some point in the buffer, and whether it should silently fail +;; when nothing is found: + +;; (search-forward "Hello" nil t) does it. + +;; The `nil' argument says: the search is not bound to a position. + +;; The `t' argument says: silently fail when nothing is found. + +;; We do it here, in a new function that also says "Hello first": + +(defun hello-to-bonjour () + (switch-to-buffer-other-window "*test*") + (erase-buffer) + ;; Say hello to names in `list-of-names' + (mapcar 'hello list-of-names) + (goto-char (point-min)) + ;; Replace "Hello" by "Bonjour" + (while (search-forward "Hello" nil t) + (replace-match "Bonjour")) + (other-window 1)) + +(hello-to-bonjour) + +;; Let's colorize the names: + +(defun boldify-names () + (switch-to-buffer-other-window "*test*") + (goto-char (point-min)) + (while (re-search-forward "Bonjour \\(.+\\)!" nil t) + (add-text-properties (match-beginning 1) + (match-end 1) + (list 'face 'bold))) + (other-window 1)) + +;; This functions introduces `re-search-forward': instead of +;; searching for the string "Bonjour", you search for a pattern, +;; using a "regular expression" (abbreviated in the prefix "re-"). + +;; The regular expression is "Bonjour \\(.+\\)!" and it reads: +;; the string "Bonjour ", and +;; a group of | this is the \\( ... \\) construct +;; any character | this is the . +;; possibly repeated | this is the + +;; and the "!" string. + +;; Ready? Test it! + +(boldify-names) + +;; `add-text-properties' adds... text properties, like a face. + +;; OK, we are done. Happy hacking! + +``` -- cgit v1.2.3 From a446da2bd672a814d15b7b0da5d6b197d14305e4 Mon Sep 17 00:00:00 2001 From: Bastien Guerry Date: Thu, 25 Jul 2013 14:22:56 +0200 Subject: Fix typo. --- elisp.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/elisp.html.markdown b/elisp.html.markdown index 80cdc7eb..d5ba376d 100644 --- a/elisp.html.markdown +++ b/elisp.html.markdown @@ -43,7 +43,7 @@ filename: elisp.el ;; Elisp programs are made of symbolic expressions ("sexps"): (+ 2 2) -;; This symbolic expression reads as "Add 3 to 3". +;; This symbolic expression reads as "Add 2 to 2". ;; Sexps are enclosed into parentheses, possibly nested: (+ 2 (+ 1 1)) -- cgit v1.2.3 From a3ba320e170134227568a0f004874475be33773a Mon Sep 17 00:00:00 2001 From: Bastien Guerry Date: Fri, 26 Jul 2013 11:59:51 +0200 Subject: elisp.html.markdown: Bump to v0.2. --- elisp.html.markdown | 79 +++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 59 insertions(+), 20 deletions(-) diff --git a/elisp.html.markdown b/elisp.html.markdown index d5ba376d..87368bc7 100644 --- a/elisp.html.markdown +++ b/elisp.html.markdown @@ -2,11 +2,11 @@ language: elisp contributors: - ["Bastien Guerry", "http://bzg.fr"] -filename: elisp.el +filename: learn-emacs-lisp.el --- ```elisp -;; This gives an introduction to Emacs Lisp in 15 minutes. +;; This gives an introduction to Emacs Lisp in 15 minutes (v0.2) ;; ;; First make sure you read this text by Peter Norvig: ;; http://norvig.com/21-days.html @@ -19,10 +19,20 @@ filename: elisp.el ;; ;; More general information can be found at: ;; http://www.gnu.org/software/emacs/#Obtaining + +;; Important warning: +;; +;; Going through this tutorial won't damage your computer unless +;; you get so angry that you throw it on the floor. In that case, +;; I hereby decline any responsability. Have fun! + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; Fire up Emacs. ;; -;; Look at the gray line at the bottom of the window: +;; Hit the `q' key to dismiss the welcome message. +;; +;; Now look at the gray line at the bottom of the window: ;; ;; "*scratch*" is the name of the editing space you are now in. ;: This editing space is called a "buffer". @@ -33,12 +43,14 @@ filename: elisp.el ;; ;; "Lisp interaction" refers to a set of commands available here. ;; -;; This set of command is loaded on top of core Emacs commands. -;; For exemple, `save-buffer' is a core Emacs command to save a -;; buffer to its associated file, and `eval-defun' is a command -;; loaded from the `lisp-interaction-mode' you are now in. +;; Emacs has a built-in set of commands available in every buffer, +;; and several subsets of commands available when you activate a +;; specific mode. Here we use the `lisp-interaction-mode', which +;; comes with commands to evaluate and navigate within Elisp code. + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; -;; Semi-columns start comments anywhere on a line. +;; Semi-colons start comments anywhere on a line. ;; ;; Elisp programs are made of symbolic expressions ("sexps"): (+ 2 2) @@ -54,7 +66,7 @@ filename: elisp.el ;; From `lisp-interaction-mode' you can evaluate sexps. ;; Put the cursor right after the closing parenthesis then -;; hit the control and the j keys ("C-j" for short). +;; hold down the control and hit the j keys ("C-j" for short). (+ 3 (+ 1 2)) ;; ^ cursor here @@ -109,13 +121,17 @@ filename: elisp.el ;; Take a breath. +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; Now switch to a new buffer named "*test*" in another window: (switch-to-buffer-other-window "*test*") ;; `C-xC-e' ;: => [screen has two windows and cursor is in the *test* buffer] -;; Use the mouse to go back to the window where you code. +;; Mouse over the top window and left-click to go back. Or you can +;; use `C-xo' (i.e. hold down control-x and hit j) to go to the other +;; window interactively. ;; You can combine several sexps with `progn': (progn @@ -127,11 +143,13 @@ filename: elisp.el ;; Now if you don't mind, I'll stop asking you to hit `C-xC-e': do it ;; for every sexp that follows. +;; Always go back to the *scratch* buffer with the mouse or `C-xo'. + ;; It's often useful to erase the buffer: (progn (switch-to-buffer-other-window "*test*") (erase-buffer) - (hello "you")) + (hello "there")) ;; Or to go back to the other window: (progn @@ -166,9 +184,9 @@ filename: elisp.el (defun greeting (name) (let ((your-name "Bastien")) (insert (format "Hello %s!\n\nI'am %s." - name ; the argument of the function - your-name ; the let-bound variable "Roger" - )))) + name ; the argument of the function + your-name ; the let-bound variable "Bastien" + )))) ;; And evaluate it: (greeting "you") @@ -182,9 +200,9 @@ filename: elisp.el (defun greeting (from-name) (let ((your-name (read-from-minibuffer "Enter your name: "))) (insert (format "Hello!\n\I am %s and you are %s." - from-name ; the argument of the function - your-name ; the let-bound var, entered at prompt - )))) + from-name ; the argument of the function + your-name ; the let-bound var, entered at prompt + )))) (greeting "Bastien") @@ -201,8 +219,10 @@ filename: elisp.el ;; Take a breath. +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; Let's store a list of names: -(setq list-of-names '("Sarah" "Chloe")) +(setq list-of-names '("Sarah" "Chloe" "Mathilde")) ;; Get the first element of this list with `car': (car list-of-names) @@ -214,6 +234,8 @@ filename: elisp.el (push "Stephanie" list-of-names) ;; NOTE: `car' and `cdr' don't modify the list, but `push' does. +;; This is an important difference: some functions don't have any +;; side-effects (like `car') while others have (like `push'). ;; Let's call `hello' for each element in `list-of-names': (mapcar 'hello list-of-names) @@ -283,8 +305,8 @@ filename: elisp.el (goto-char (point-min)) (while (re-search-forward "Bonjour \\(.+\\)!" nil t) (add-text-properties (match-beginning 1) - (match-end 1) - (list 'face 'bold))) + (match-end 1) + (list 'face 'bold))) (other-window 1)) ;; This functions introduces `re-search-forward': instead of @@ -306,4 +328,21 @@ filename: elisp.el ;; OK, we are done. Happy hacking! +;; If you want to know more about a variable or a function: +;; +;; C-h v a-variable RET +;; C-h f a-function RET +;; +;; To read the Emacs Lisp manual with Emacs: +;; +;; C-h i m elisp RET +;; +;; To read an online introduction to Emacs Lisp: +;; https://www.gnu.org/software/emacs/manual/html_node/eintr/index.html + +;; Thanks to these people for their feedback and suggestions: +;; - Wes Hardaker +;; - notbob +;; - Kevin Montuori +;; - Arne Babenhauserheide ``` -- cgit v1.2.3 From e06a5d7e0acd8e5c92190da67930118220680346 Mon Sep 17 00:00:00 2001 From: Bastien Guerry Date: Fri, 26 Jul 2013 14:54:49 +0200 Subject: elisp.html.markdown: Fix typos. Thanks to Alan Schmitt for reporting them. --- elisp.html.markdown | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/elisp.html.markdown b/elisp.html.markdown index 87368bc7..b1bd2c47 100644 --- a/elisp.html.markdown +++ b/elisp.html.markdown @@ -6,7 +6,7 @@ filename: learn-emacs-lisp.el --- ```elisp -;; This gives an introduction to Emacs Lisp in 15 minutes (v0.2) +;; This gives an introduction to Emacs Lisp in 15 minutes (v0.2a) ;; ;; First make sure you read this text by Peter Norvig: ;; http://norvig.com/21-days.html @@ -183,7 +183,7 @@ filename: learn-emacs-lisp.el ;; Let's create another function which uses `let': (defun greeting (name) (let ((your-name "Bastien")) - (insert (format "Hello %s!\n\nI'am %s." + (insert (format "Hello %s!\n\nI am %s." name ; the argument of the function your-name ; the let-bound variable "Bastien" )))) @@ -211,7 +211,7 @@ filename: learn-emacs-lisp.el (let ((your-name (read-from-minibuffer "Enter your name: "))) (switch-to-buffer-other-window "*test*") (erase-buffer) - (insert (format "Hello %s!\n\nI'am %s." your-name from-name)) + (insert (format "Hello %s!\n\nI am %s." your-name from-name)) (other-window 1))) ;; Now test it: @@ -273,17 +273,17 @@ filename: learn-emacs-lisp.el ;; replaced by "Bonjour". ;; You should also get an error: "Search failed: Hello". -;; You need to tell search-forward whether it should stop searching -;; at some point in the buffer, and whether it should silently fail -;; when nothing is found: +;; +;; To avoid this error, you need to tell `search-forward' whether it +;; should stop searching at some point in the buffer, and whether it +;; should silently fail when nothing is found: -;; (search-forward "Hello" nil t) does it. +;; (search-forward "Hello" nil t) does the trick: ;; The `nil' argument says: the search is not bound to a position. - ;; The `t' argument says: silently fail when nothing is found. -;; We do it here, in a new function that also says "Hello first": +;; We use this sexp in the function below, which don't throw an error: (defun hello-to-bonjour () (switch-to-buffer-other-window "*test*") @@ -345,4 +345,5 @@ filename: learn-emacs-lisp.el ;; - notbob ;; - Kevin Montuori ;; - Arne Babenhauserheide +;; - Alan Schmitt ``` -- cgit v1.2.3 From bfca9d4999312cb587953eb6755ac4f5933a23e0 Mon Sep 17 00:00:00 2001 From: LinXitoW Date: Sun, 28 Jul 2013 17:33:48 +0200 Subject: fix hotkey typo o in c-xo instead of j --- elisp.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/elisp.html.markdown b/elisp.html.markdown index b1bd2c47..bd23817d 100644 --- a/elisp.html.markdown +++ b/elisp.html.markdown @@ -130,7 +130,7 @@ filename: learn-emacs-lisp.el ;: => [screen has two windows and cursor is in the *test* buffer] ;; Mouse over the top window and left-click to go back. Or you can -;; use `C-xo' (i.e. hold down control-x and hit j) to go to the other +;; use `C-xo' (i.e. hold down control-x and hit o) to go to the other ;; window interactively. ;; You can combine several sexps with `progn': -- cgit v1.2.3 From a55974ba591ab735af0b1af3400536adbe95f6c7 Mon Sep 17 00:00:00 2001 From: Bastien Guerry Date: Sun, 28 Jul 2013 23:52:43 +0200 Subject: Fix more typos. --- elisp.html.markdown | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/elisp.html.markdown b/elisp.html.markdown index b1bd2c47..c99466b6 100644 --- a/elisp.html.markdown +++ b/elisp.html.markdown @@ -130,7 +130,7 @@ filename: learn-emacs-lisp.el ;: => [screen has two windows and cursor is in the *test* buffer] ;; Mouse over the top window and left-click to go back. Or you can -;; use `C-xo' (i.e. hold down control-x and hit j) to go to the other +;; use `C-xo' (i.e. hold down control-x and hit o) to go to the other ;; window interactively. ;; You can combine several sexps with `progn': @@ -196,17 +196,17 @@ filename: learn-emacs-lisp.el ;; Evaluating this function returns what you entered at the prompt. -;; Let's make our `greeting' function prompts for your name: +;; Let's make our `greeting' function prompt for your name: (defun greeting (from-name) (let ((your-name (read-from-minibuffer "Enter your name: "))) - (insert (format "Hello!\n\I am %s and you are %s." + (insert (format "Hello!\n\nI am %s and you are %s." from-name ; the argument of the function your-name ; the let-bound var, entered at prompt )))) (greeting "Bastien") -;; Let complete it by displaying the results in the other window: +;; Let's complete it by displaying the results in the other window: (defun greeting (from-name) (let ((your-name (read-from-minibuffer "Enter your name: "))) (switch-to-buffer-other-window "*test*") @@ -283,7 +283,7 @@ filename: learn-emacs-lisp.el ;; The `nil' argument says: the search is not bound to a position. ;; The `t' argument says: silently fail when nothing is found. -;; We use this sexp in the function below, which don't throw an error: +;; We use this sexp in the function below, which doesn't throw an error: (defun hello-to-bonjour () (switch-to-buffer-other-window "*test*") -- cgit v1.2.3