]> code.delx.au - gnu-emacs/blob - lisp/tutorial.el
(browse-url): Set DISPLAY to the one of the
[gnu-emacs] / lisp / tutorial.el
1 ;;; tutorial.el --- tutorial for Emacs
2
3 ;; Copyright (C) 2006 Free Software Foundation, Inc.
4
5 ;; Maintainer: FSF
6 ;; Keywords: help, internal
7
8 ;; This file is part of GNU Emacs.
9
10 ;; GNU Emacs is free software; you can redistribute it and/or modify
11 ;; it under the terms of the GNU General Public License as published by
12 ;; the Free Software Foundation; either version 2, or (at your option)
13 ;; any later version.
14
15 ;; GNU Emacs is distributed in the hope that it will be useful,
16 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 ;; GNU General Public License for more details.
19
20 ;; You should have received a copy of the GNU General Public License
21 ;; along with GNU Emacs; see the file COPYING. If not, write to the
22 ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
23 ;; Boston, MA 02110-1301, USA.
24
25 ;;; Commentary:
26
27 ;; Code for running the Emacs tutorial.
28
29 ;;; History:
30
31 ;; File was created 2006-09.
32
33 ;;; Code:
34
35 (require 'help-mode) ;; for function help-buffer
36 (eval-when-compile (require 'cl))
37
38 (defface tutorial-warning-face
39 '((((class color) (min-colors 88) (background light))
40 (:foreground "Red1" :weight bold))
41 (((class color) (min-colors 88) (background dark))
42 (:foreground "Pink" :weight bold))
43 (((class color) (min-colors 16) (background light))
44 (:foreground "Red1" :weight bold))
45 (((class color) (min-colors 16) (background dark))
46 (:foreground "Pink" :weight bold))
47 (((class color) (min-colors 8)) (:foreground "red"))
48 (t (:inverse-video t :weight bold)))
49 "Face used to highlight warnings in the tutorial."
50 :group 'font-lock-faces)
51
52 (defvar tutorial--point-before-chkeys 0
53 "Point before display of key changes.")
54 (make-variable-buffer-local 'tutorial--point-before-chkeys)
55
56 (defvar tutorial--point-after-chkeys 0
57 "Point after display of key changes.")
58 (make-variable-buffer-local 'tutorial--point-after-chkeys)
59
60 (defvar tutorial--lang nil
61 "Tutorial language.")
62 (make-variable-buffer-local 'tutorial--lang)
63
64 (defun tutorial--describe-nonstandard-key (value)
65 "Give more information about a changed key binding.
66 This is used in `help-with-tutorial'. The information includes
67 the key sequence that no longer has a default binding, the
68 default binding and the current binding. It also tells in what
69 keymap the new binding has been done and how to access the
70 function in the default binding from the keyboard.
71
72 For `cua-mode' key bindings that try to combine CUA key bindings
73 with default Emacs bindings information about this is shown.
74
75 VALUE should have either of these formats:
76
77 \(cua-mode)
78 \(current-binding KEY-FUN DEF-FUN KEY WHERE)
79
80 Where
81 KEY is a key sequence whose standard binding has been changed
82 KEY-FUN is the actual binding for KEY
83 DEF-FUN is the standard binding of KEY
84 WHERE is a text describing the key sequences to which DEF-FUN is
85 bound now (or, if it is remapped, a key sequence
86 for the function it is remapped to)"
87 (with-output-to-temp-buffer (help-buffer)
88 (help-setup-xref (list #'tutorial--describe-nonstandard-key value)
89 (interactive-p))
90 (with-current-buffer (help-buffer)
91 (insert
92 "Your Emacs customizations override the default binding for this key:"
93 "\n\n")
94 (let ((inhibit-read-only t))
95 (cond
96 ((eq (car value) 'cua-mode)
97 (insert
98 "CUA mode is enabled.
99
100 When CUA mode is enabled, you can use C-z, C-x, C-c, and C-v to
101 undo, cut, copy, and paste in addition to the normal Emacs
102 bindings. The C-x and C-c keys only do cut and copy when the
103 region is active, so in most cases, they do not conflict with the
104 normal function of these prefix keys.
105
106 If you really need to perform a command which starts with one of
107 the prefix keys even when the region is active, you have three
108 options:
109 - press the prefix key twice very quickly (within 0.2 seconds),
110 - press the prefix key and the following key within 0.2 seconds, or
111 - use the SHIFT key with the prefix key, i.e. C-S-x or C-S-c."))
112 ((eq (car value) 'current-binding)
113 (let ((cb (nth 1 value))
114 (db (nth 2 value))
115 (key (nth 3 value))
116 (where (nth 4 value))
117 map
118 (maps (current-active-maps))
119 mapsym)
120 ;; Look at the currently active keymaps and try to find
121 ;; first the keymap where the current binding occurs:
122 (while maps
123 (let* ((m (car maps))
124 (mb (lookup-key m key t)))
125 (setq maps (cdr maps))
126 (when (eq mb cb)
127 (setq map m)
128 (setq maps nil))))
129 ;; Now, if a keymap was found we must found the symbol
130 ;; name for it to display to the user. This can not
131 ;; always be found since all keymaps does not have a
132 ;; symbol pointing to them, but here they should have
133 ;; that:
134 (when map
135 (mapatoms (lambda (s)
136 (and
137 ;; If not already found
138 (not mapsym)
139 ;; and if s is a keymap
140 (and (boundp s)
141 (keymapp (symbol-value s)))
142 ;; and not the local symbol map
143 (not (eq s 'map))
144 ;; and the value of s is map
145 (eq map (symbol-value s))
146 ;; then save this value in mapsym
147 (setq mapsym s)))))
148 (insert "The default Emacs binding for the key "
149 (key-description key)
150 " is the command `")
151 (insert (format "%s" db))
152 (insert "'. "
153 "However, your customizations have rebound it to the command `")
154 (insert (format "%s" cb))
155 (insert "'.")
156 (when mapsym
157 (insert " (For the more advanced user:"
158 " This binding is in the keymap `"
159 (format "%s" mapsym)
160 "'.)"))
161 (if (string= where "")
162 (unless (keymapp db)
163 (insert "\n\nYou can use M-x "
164 (format "%s" db)
165 " RET instead."))
166 (insert "\n\nWith you current key bindings"
167 " you can use the key "
168 where
169 " to get the function `"
170 (format "%s" db)
171 "'."))
172 )
173 (fill-region (point-min) (point)))))
174 (print-help-return-message))))
175
176 (defun tutorial--sort-keys (left right)
177 "Sort predicate for use with `tutorial--default-keys'.
178 This is a predicate function to `sort'.
179
180 The sorting is for presentation purpose only and is done on the
181 key sequence.
182
183 LEFT and RIGHT are the elements to compare."
184 (let ((x (append (cadr left) nil))
185 (y (append (cadr right) nil)))
186 ;; Skip the front part of the key sequences if they are equal:
187 (while (and x y
188 (listp x) (listp y)
189 (equal (car x) (car y)))
190 (setq x (cdr x))
191 (setq y (cdr y)))
192 ;; Try to make a comparision that is useful for presentation (this
193 ;; could be made nicer perhaps):
194 (let ((cx (car x))
195 (cy (car y)))
196 ;;(message "x=%s, y=%s;;;; cx=%s, cy=%s" x y cx cy)
197 (cond
198 ;; Lists? Then call this again
199 ((and cx cy
200 (listp cx)
201 (listp cy))
202 (tutorial--sort-keys cx cy))
203 ;; Are both numbers? Then just compare them
204 ((and (wholenump cx)
205 (wholenump cy))
206 (> cx cy))
207 ;; Is one of them a number? Let that be bigger then.
208 ((wholenump cx)
209 t)
210 ((wholenump cy)
211 nil)
212 ;; Are both symbols? Compare the names then.
213 ((and (symbolp cx)
214 (symbolp cy))
215 (string< (symbol-name cy)
216 (symbol-name cx)))
217 ))))
218
219 (defconst tutorial--default-keys
220 (let* (
221 ;; On window system suspend Emacs is replaced in the
222 ;; default keymap so honor this here.
223 (suspend-emacs (if window-system
224 'iconify-or-deiconify-frame
225 'suspend-emacs))
226 (default-keys
227 `(
228 ;; These are not mentioned but are basic:
229 (ESC-prefix [27])
230 (Control-X-prefix [?\C-x])
231 (mode-specific-command-prefix [?\C-c])
232
233 (save-buffers-kill-emacs [?\C-x ?\C-c])
234
235
236 ;; * SUMMARY
237 (scroll-up [?\C-v])
238 (scroll-down [?\M-v])
239 (recenter [?\C-l])
240
241
242 ;; * BASIC CURSOR CONTROL
243 (forward-char [?\C-f])
244 (backward-char [?\C-b])
245
246 (forward-word [?\M-f])
247 (backward-word [?\M-b])
248
249 (next-line [?\C-n])
250 (previous-line [?\C-p])
251
252 (move-beginning-of-line [?\C-a])
253 (move-end-of-line [?\C-e])
254
255 (backward-sentence [?\M-a])
256 (forward-sentence [?\M-e])
257
258 (newline "\r")
259
260 (beginning-of-buffer [?\M-<])
261 (end-of-buffer [?\M->])
262
263 (universal-argument [?\C-u])
264
265
266 ;; * WHEN EMACS IS HUNG
267 (keyboard-quit [?\C-g])
268
269
270 ;; * DISABLED COMMANDS
271 (downcase-region [?\C-x ?\C-l])
272
273
274 ;; * WINDOWS
275 (delete-other-windows [?\C-x ?1])
276 ;; C-u 0 C-l
277 ;; Type CONTROL-h k CONTROL-f.
278
279
280 ;; * INSERTING AND DELETING
281 ;; C-u 8 * to insert ********.
282
283 (delete-backward-char [backspace])
284 (delete-backward-char "\d")
285 (delete-char [?\C-d])
286
287 (backward-kill-word [(meta backspace)])
288 (kill-word [?\M-d])
289
290 (kill-line [?\C-k])
291 (kill-sentence [?\M-k])
292
293 (set-mark-command [?\C-@])
294 (set-mark-command [?\C- ])
295 (kill-region [?\C-w])
296 (yank [?\C-y])
297 (yank-pop [?\M-y])
298
299
300 ;; * UNDO
301 (advertised-undo [?\C-x ?u])
302 (advertised-undo [?\C-x ?u])
303
304
305 ;; * FILES
306 (find-file [?\C-x ?\C-f])
307 (save-buffer [?\C-x ?\C-s])
308
309
310 ;; * BUFFERS
311 (list-buffers [?\C-x ?\C-b])
312 (switch-to-buffer [?\C-x ?b])
313 (save-some-buffers [?\C-x ?s])
314
315
316 ;; * EXTENDING THE COMMAND SET
317 ;; C-x Character eXtend. Followed by one character.
318 (execute-extended-command [?\M-x])
319
320 ;; C-x C-f Find file
321 ;; C-x C-s Save file
322 ;; C-x s Save some buffers
323 ;; C-x C-b List buffers
324 ;; C-x b Switch buffer
325 ;; C-x C-c Quit Emacs
326 ;; C-x 1 Delete all but one window
327 ;; C-x u Undo
328
329
330 ;; * MODE LINE
331 (describe-mode [?\C-h ?m])
332
333 (set-fill-column [?\C-x ?f])
334 (fill-paragraph [?\M-q])
335
336
337 ;; * SEARCHING
338 (isearch-forward [?\C-s])
339 (isearch-backward [?\C-r])
340
341
342 ;; * MULTIPLE WINDOWS
343 (split-window-vertically [?\C-x ?2])
344 (scroll-other-window [?\C-\M-v])
345 (other-window [?\C-x ?o])
346 (find-file-other-window [?\C-x ?4 ?\C-f])
347
348
349 ;; * RECURSIVE EDITING LEVELS
350 (keyboard-escape-quit [27 27 27])
351
352
353 ;; * GETTING MORE HELP
354 ;; The most basic HELP feature is C-h c
355 (describe-key-briefly [?\C-h ?c])
356 (describe-key [?\C-h ?k])
357
358
359 ;; * MORE FEATURES
360 ;; F10
361
362
363 ;; * CONCLUSION
364 ;;(iconify-or-deiconify-frame [?\C-z])
365 (,suspend-emacs [?\C-z])
366 )))
367 (sort default-keys 'tutorial--sort-keys))
368 "Default Emacs key bindings that the tutorial depends on.")
369
370 (defun tutorial--detailed-help (button)
371 "Give detailed help about changed keys."
372 (with-output-to-temp-buffer (help-buffer)
373 (help-setup-xref (list #'tutorial--detailed-help button)
374 (interactive-p))
375 (with-current-buffer (help-buffer)
376 (let* ((tutorial-buffer (button-get button 'tutorial-buffer))
377 ;;(tutorial-arg (button-get button 'tutorial-arg))
378 (explain-key-desc (button-get button 'explain-key-desc))
379 (changed-keys (with-current-buffer tutorial-buffer
380 (save-excursion
381 (goto-char (point-min))
382 (tutorial--find-changed-keys
383 tutorial--default-keys)))))
384 (when changed-keys
385 (insert
386 "The following key bindings used in the tutorial had been changed
387 from Emacs default in the " (buffer-name tutorial-buffer) " buffer:\n\n" )
388 (let ((frm " %-9s %-27s %-11s %s\n"))
389 (insert (format frm "Key" "Standard Binding" "Is Now On" "Remark")))
390 (dolist (tk changed-keys)
391 (let* ((def-fun (nth 1 tk))
392 (key (nth 0 tk))
393 (def-fun-txt (nth 2 tk))
394 (where (nth 3 tk))
395 (remark (nth 4 tk))
396 (rem-fun (command-remapping def-fun))
397 (key-txt (key-description key))
398 (key-fun (with-current-buffer tutorial-buffer (key-binding key)))
399 tot-len)
400 (unless (eq def-fun key-fun)
401 ;; Insert key binding description:
402 (when (string= key-txt explain-key-desc)
403 (put-text-property 0 (length key-txt)
404 'face 'tutorial-warning-face key-txt))
405 (insert " " key-txt " ")
406 (setq tot-len (length key-txt))
407 (when (> 9 tot-len)
408 (insert (make-string (- 9 tot-len) ? ))
409 (setq tot-len 9))
410 ;; Insert a link describing the old binding:
411 (insert-button def-fun-txt
412 'value def-fun
413 'action
414 (lambda(button) (interactive)
415 (describe-function
416 (button-get button 'value)))
417 'follow-link t)
418 (setq tot-len (+ tot-len (length def-fun-txt)))
419 (when (> 36 tot-len)
420 (insert (make-string (- 36 tot-len) ? )))
421 (when (listp where)
422 (setq where "list"))
423 ;; Tell where the old binding is now:
424 (insert (format " %-11s "
425 (if (string= "" where)
426 (format "M-x %s" def-fun-txt)
427 where)))
428 ;; Insert a link with more information, for example
429 ;; current binding and keymap or information about
430 ;; cua-mode replacements:
431 (insert-button (car remark)
432 'action
433 (lambda(b) (interactive)
434 (let ((value (button-get b 'value)))
435 (tutorial--describe-nonstandard-key value)))
436 'value (cdr remark)
437 'follow-link t)
438 (insert "\n")))))
439
440 (insert "
441 It is legitimate to change key bindings, but changed bindings do not
442 correspond to what the tutorial says. (See also " )
443 (insert-button "Key Binding Conventions"
444 'action
445 (lambda(button) (interactive)
446 (info
447 "(elisp) Key Binding Conventions")
448 (message "Type C-x 0 to close the new window"))
449 'follow-link t)
450 (insert ".)\n\n")
451 (print-help-return-message)))))
452
453 (defun tutorial--find-changed-keys (default-keys)
454 "Find the key bindings that have changed.
455 Check if the default Emacs key bindings that the tutorial depends
456 on have been changed.
457
458 Return a list with the keys that have been changed. The element
459 of this list have the following format:
460
461 \(list KEY DEF-FUN DEF-FUN-TXT WHERE REMARK)
462
463 Where
464 KEY is a key sequence whose standard binding has been changed
465 DEF-FUN is the standard binding of KEY
466 DEF-FUN-TXT is a short descriptive text for DEF-FUN
467 WHERE is a text describing the key sequences to which DEF-FUN is
468 bound now (or, if it is remapped, a key sequence
469 for the function it is remapped to)
470 REMARK is a list with info about rebinding. It has either of these
471 formats:
472
473 \(TEXT cua-mode)
474 \(TEXT current-binding KEY-FUN DEF-FUN KEY WHERE)
475
476 Here TEXT is a link text to show to the user. The
477 rest of the list is used to show information when
478 the user clicks the link.
479
480 KEY-FUN is the actual binding for KEY."
481 (let (changed-keys remark)
482 ;; (default-keys tutorial--default-keys))
483 (dolist (kdf default-keys)
484 ;; The variables below corresponds to those with the same names
485 ;; described in the doc string.
486 (let* ((key (nth 1 kdf))
487 (def-fun (nth 0 kdf))
488 (def-fun-txt (format "%s" def-fun))
489 (rem-fun (command-remapping def-fun))
490 (key-fun (if (eq def-fun 'ESC-prefix)
491 (lookup-key global-map [27])
492 (key-binding key)))
493 (where (where-is-internal (if rem-fun rem-fun def-fun))))
494 (if where
495 (progn
496 (setq where (key-description (car where)))
497 (when (and (< 10 (length where))
498 (string= (substring where 0 (length "<menu-bar>"))
499 "<menu-bar>"))
500 (setq where "the menus")))
501 (setq where ""))
502 (setq remark nil)
503 (unless
504 (cond ((eq key-fun def-fun)
505 ;; No rebinding, return t
506 t)
507 ((eq key-fun (command-remapping def-fun))
508 ;; Just a remapping, return t
509 t)
510 ;; cua-mode specials:
511 ((and cua-mode
512 (or (and
513 (equal key [?\C-v])
514 (eq key-fun 'cua-paste))
515 (and
516 (equal key [?\C-z])
517 (eq key-fun 'undo))))
518 (setq remark (list "cua-mode, more info" 'cua-mode))
519 nil)
520 ((and cua-mode
521 (or
522 (and (eq def-fun 'ESC-prefix)
523 (equal key-fun
524 `(keymap
525 (118 . cua-repeat-replace-region))))
526 (and (eq def-fun 'mode-specific-command-prefix)
527 (equal key-fun
528 '(keymap
529 (timeout . copy-region-as-kill))))
530 (and (eq def-fun 'Control-X-prefix)
531 (equal key-fun
532 '(keymap (timeout . kill-region))))))
533 (setq remark (list "cua-mode replacement" 'cua-mode))
534 (cond
535 ((eq def-fun 'mode-specific-command-prefix)
536 (setq def-fun-txt "\"C-c prefix\""))
537 ((eq def-fun 'Control-X-prefix)
538 (setq def-fun-txt "\"C-x prefix\""))
539 ((eq def-fun 'ESC-prefix)
540 (setq def-fun-txt "\"ESC prefix\"")))
541 (setq where "Same key")
542 nil)
543 ;; viper-mode specials:
544 ((and (boundp 'viper-mode-string)
545 (boundp 'viper-current-state)
546 (eq viper-current-state 'vi-state)
547 (or (and (eq def-fun 'isearch-forward)
548 (eq key-fun 'viper-isearch-forward))
549 (and (eq def-fun 'isearch-backward)
550 (eq key-fun 'viper-isearch-backward))))
551 ;; These bindings works as the default bindings,
552 ;; return t
553 t)
554 ((when normal-erase-is-backspace
555 (or (and (equal key [C-delete])
556 (equal key-fun 'kill-word))
557 (and (equal key [C-backspace])
558 (equal key-fun 'backward-kill-word))))
559 ;; This is the strange handling of C-delete and
560 ;; C-backspace, return t
561 t)
562 (t
563 ;; This key has indeed been rebound. Put information
564 ;; in `remark' and return nil
565 (setq remark
566 (list "more info" 'current-binding
567 key-fun def-fun key where))
568 nil))
569 (add-to-list 'changed-keys
570 (list key def-fun def-fun-txt where remark)))))
571 changed-keys))
572
573 (defvar tutorial--tab-map
574 (let ((map (make-sparse-keymap)))
575 (define-key map [tab] 'forward-button)
576 (define-key map [(shift tab)] 'backward-button)
577 (define-key map [(meta tab)] 'backward-button)
578 map)
579 "Keymap that allows tabbing between buttons.")
580
581 (defun tutorial--display-changes (changed-keys)
582 "Display changes to some default key bindings.
583 If some of the default key bindings that the tutorial depends on
584 have been changed then display the changes in the tutorial buffer
585 with some explanatory links.
586
587 CHANGED-KEYS should be a list in the format returned by
588 `tutorial--find-changed-keys'."
589 (when (or changed-keys
590 (boundp 'viper-mode-string))
591 ;; Need the custom button face for viper buttons:
592 (when (boundp 'viper-mode-string)
593 (require 'cus-edit))
594 (let ((start (point))
595 end
596 (head (get-lang-string tutorial--lang 'tut-chgdhead))
597 (head2 (get-lang-string tutorial--lang 'tut-chgdhead2)))
598 (when (and head head2)
599 (goto-char tutorial--point-before-chkeys)
600 (insert head)
601 (insert-button head2
602 'tutorial-buffer
603 (current-buffer)
604 ;;'tutorial-arg arg
605 'action
606 'tutorial--detailed-help
607 'follow-link t
608 'face 'link)
609 (insert "]\n\n" )
610 (when changed-keys
611 (dolist (tk changed-keys)
612 (let* ((def-fun (nth 1 tk))
613 (key (nth 0 tk))
614 (def-fun-txt (nth 2 tk))
615 (where (nth 3 tk))
616 (remark (nth 4 tk))
617 (rem-fun (command-remapping def-fun))
618 (key-txt (key-description key))
619 (key-fun (key-binding key))
620 tot-len)
621 (unless (eq def-fun key-fun)
622 ;; Mark the key in the tutorial text
623 (unless (string= "Same key" where)
624 (let ((here (point))
625 (case-fold-search nil)
626 (key-desc (key-description key)))
627 (cond ((string= "ESC" key-desc)
628 (setq key-desc "<ESC>"))
629 ((string= "RET" key-desc)
630 (setq key-desc "<Return>"))
631 ((string= "DEL" key-desc)
632 (setq key-desc "<Delback>")))
633 (while (re-search-forward
634 (concat "[[:space:]]\\("
635 (regexp-quote key-desc)
636 "\\)[[:space:]]") nil t)
637 (put-text-property (match-beginning 1)
638 (match-end 1)
639 'tutorial-remark 'only-colored)
640 (put-text-property (match-beginning 1)
641 (match-end 1)
642 'face 'tutorial-warning-face)
643 (forward-line)
644 (let ((s (get-lang-string tutorial--lang 'tut-chgdkey))
645 (s2 (get-lang-string tutorial--lang 'tut-chgdkey2))
646 (start (point))
647 end)
648 (when (and s s2)
649 (setq s (format s key-desc where s2))
650 (insert s)
651 (insert-button s2
652 'tutorial-buffer
653 (current-buffer)
654 ;;'tutorial-arg arg
655 'action
656 'tutorial--detailed-help
657 'explain-key-desc key-desc
658 'follow-link t
659 'face 'link)
660 (insert "] **")
661 (insert "\n")
662 (setq end (point))
663 (put-text-property start end 'local-map tutorial--tab-map)
664 ;; Add a property so we can remove the remark:
665 (put-text-property start end 'tutorial-remark t)
666 (put-text-property start end
667 'face 'tutorial-warning-face)
668 (put-text-property start end 'read-only t))))
669 (goto-char here)))))))
670
671
672 (setq end (point))
673 ;; Make the area with information about change key
674 ;; bindings stand out:
675 (put-text-property start end 'tutorial-remark t)
676 (put-text-property start end
677 'face 'tutorial-warning-face)
678 ;; Make it possible to use Tab/S-Tab between fields in
679 ;; this area:
680 (put-text-property start end 'local-map tutorial--tab-map)
681 (setq tutorial--point-after-chkeys (point-marker))
682 ;; Make this area read-only:
683 (put-text-property start end 'read-only t)))))
684
685 (defun tutorial--saved-dir ()
686 "Directory where to save tutorials."
687 (expand-file-name ".emacstut" "~/"))
688
689 (defun tutorial--saved-file ()
690 "File name in which to save tutorials."
691 (let ((file-name tutorial--lang)
692 (ext (file-name-extension tutorial--lang)))
693 (when (or (not ext)
694 (string= ext ""))
695 (setq file-name (concat file-name ".tut")))
696 (expand-file-name file-name (tutorial--saved-dir))))
697
698 (defun tutorial--remove-remarks()
699 "Remove the remark lines that was added to the tutorial buffer."
700 (save-excursion
701 (goto-char (point-min))
702 (let (prop-start
703 prop-end
704 prop-val)
705 ;; Catch the case when we already are on a remark line
706 (while (if (get-text-property (point) 'tutorial-remark)
707 (setq prop-start (point))
708 (setq prop-start (next-single-property-change (point) 'tutorial-remark)))
709 (setq prop-end (next-single-property-change prop-start 'tutorial-remark))
710 (setq prop-val (get-text-property prop-start 'tutorial-remark))
711 (unless prop-end
712 (setq prop-end (point-max)))
713 (goto-char prop-end)
714 (if (eq prop-val 'only-colored)
715 (put-text-property prop-start prop-end 'face '(:background nil))
716 (let ((orig-text (get-text-property prop-start 'tutorial-orig)))
717 (delete-region prop-start prop-end)
718 (when orig-text (insert orig-text))))))))
719
720 (defun tutorial--save-tutorial ()
721 "Save the tutorial buffer.
722 This saves the part of the tutorial before and after the area
723 showing changed keys. It also saves the point position and the
724 position where the display of changed bindings was inserted."
725 ;; This runs in a hook so protect it:
726 (condition-case err
727 (tutorial--save-tutorial-to (tutorial--saved-file))
728 (error (message "Error saving tutorial state: %s" (error-message-string err))
729 (sit-for 4))))
730
731 (defun tutorial--save-tutorial-to (saved-file)
732 "Save the tutorial buffer to SAVED-FILE.
733 See `tutorial--save-tutorial' for more information."
734 ;; Anything to save?
735 (when (or (buffer-modified-p)
736 (< 1 (point)))
737 (let ((tutorial-dir (tutorial--saved-dir))
738 save-err)
739 ;; The tutorial is saved in a subdirectory in the user home
740 ;; directory. Create this subdirectory first.
741 (unless (file-directory-p tutorial-dir)
742 (condition-case err
743 (make-directory tutorial-dir nil)
744 (error (setq save-err t)
745 (warn "Could not create directory %s: %s" tutorial-dir
746 (error-message-string err)))))
747 ;; Make sure we have that directory.
748 (if (file-directory-p tutorial-dir)
749 (let ((tut-point (if (= 0 tutorial--point-after-chkeys)
750 ;; No info about changed keys is
751 ;; displayed.
752 (point)
753 (if (< (point) tutorial--point-after-chkeys)
754 (- (point))
755 (- (point) tutorial--point-after-chkeys))))
756 (old-point (point))
757 ;; Use a special undo list so that we easily can undo
758 ;; the changes we make to the tutorial buffer. This is
759 ;; currently not needed since we now delete the buffer
760 ;; after saving, but kept for possible future use of
761 ;; this function.
762 buffer-undo-list
763 (inhibit-read-only t))
764 ;; Delete the area displaying info about changed keys.
765 ;; (when (< 0 tutorial--point-after-chkeys)
766 ;; (delete-region tutorial--point-before-chkeys
767 ;; tutorial--point-after-chkeys))
768 ;; Delete the remarks:
769 (tutorial--remove-remarks)
770 ;; Put the value of point first in the buffer so it will
771 ;; be saved with the tutorial.
772 (goto-char (point-min))
773 (insert (number-to-string tut-point)
774 "\n"
775 (number-to-string (marker-position
776 tutorial--point-before-chkeys))
777 "\n")
778 (condition-case err
779 (write-region nil nil saved-file)
780 (error (setq save-err t)
781 (warn "Could not save tutorial to %s: %s"
782 saved-file
783 (error-message-string err))))
784 ;; An error is raised here?? Is this a bug?
785 (condition-case err
786 (undo-only)
787 (error nil))
788 ;; Restore point
789 (goto-char old-point)
790 (if save-err
791 (message "Could not save tutorial state.")
792 (message "Saved tutorial state.")))
793 (message "Can't save tutorial: %s is not a directory"
794 tutorial-dir)))))
795
796
797 ;;;###autoload
798 (defun help-with-tutorial (&optional arg dont-ask-for-revert)
799 "Select the Emacs learn-by-doing tutorial.
800 If there is a tutorial version written in the language
801 of the selected language environment, that version is used.
802 If there's no tutorial in that language, `TUTORIAL' is selected.
803 With ARG, you are asked to choose which language.
804 If DONT-ASK-FOR-REVERT is non-nil the buffer is reverted without
805 any question when restarting the tutorial.
806
807 If any of the standard Emacs key bindings that are used in the
808 tutorial have been changed then an explanatory note about this is
809 shown in the beginning of the tutorial buffer.
810
811 When the tutorial buffer is killed the content and the point
812 position in the buffer is saved so that the tutorial may be
813 resumed later."
814 (interactive "P")
815 (if (boundp 'viper-current-state)
816 (let ((prompt1
817 "You can not run the Emacs tutorial directly because you have \
818 enabled Viper.")
819 (prompt2 "\nThere is however a Viper tutorial you can run instead.
820 Run the Viper tutorial? "))
821 (if (fboundp 'viper-tutorial)
822 (if (y-or-n-p (concat prompt1 prompt2))
823 (progn (message "")
824 (funcall 'viper-tutorial 0))
825 (message "Tutorial aborted by user"))
826 (message prompt1)))
827 (let* ((lang (if arg
828 (let ((minibuffer-setup-hook minibuffer-setup-hook))
829 (add-hook 'minibuffer-setup-hook
830 'minibuffer-completion-help)
831 (read-language-name 'tutorial "Language: " "English"))
832 (if (get-language-info current-language-environment 'tutorial)
833 current-language-environment
834 "English")))
835 (filename (get-language-info lang 'tutorial))
836 ;; Choose a buffer name including the language so that
837 ;; several languages can be tested simultaneously:
838 (tut-buf-name (concat "TUTORIAL (" lang ")"))
839 (old-tut-buf (get-buffer tut-buf-name))
840 (old-tut-win (when old-tut-buf (get-buffer-window old-tut-buf t)))
841 (old-tut-is-ok (when old-tut-buf
842 (not (buffer-modified-p old-tut-buf))))
843 old-tut-file
844 (old-tut-point 1))
845 (setq tutorial--point-after-chkeys (point-min))
846 ;; Try to display the tutorial buffer before asking to revert it.
847 ;; If the tutorial buffer is shown in some window make sure it is
848 ;; selected and displayed:
849 (if old-tut-win
850 (raise-frame
851 (window-frame
852 (select-window (get-buffer-window old-tut-buf t))))
853 ;; Else, is there an old tutorial buffer? Then display it:
854 (when old-tut-buf
855 (switch-to-buffer old-tut-buf)))
856 ;; Use whole frame for tutorial
857 (delete-other-windows)
858 ;; If the tutorial buffer has been changed then ask if it should
859 ;; be reverted:
860 (when (and old-tut-buf
861 (not old-tut-is-ok))
862 (setq old-tut-is-ok
863 (if dont-ask-for-revert
864 nil
865 (not (y-or-n-p
866 "You have changed the Tutorial buffer. Revert it? ")))))
867 ;; (Re)build the tutorial buffer if it is not ok
868 (unless old-tut-is-ok
869 (switch-to-buffer (get-buffer-create tut-buf-name))
870 (unless old-tut-buf (text-mode))
871 (unless lang (error "Variable lang is nil"))
872 (setq tutorial--lang lang)
873 (setq old-tut-file (file-exists-p (tutorial--saved-file)))
874 (let ((inhibit-read-only t))
875 (erase-buffer))
876 (message "Preparing tutorial ...") (sit-for 0)
877
878 ;; Do not associate the tutorial buffer with a file. Instead use
879 ;; a hook to save it when the buffer is killed.
880 (setq buffer-auto-save-file-name nil)
881 (add-hook 'kill-buffer-hook 'tutorial--save-tutorial nil t)
882
883 ;; Insert the tutorial. First offer to resume last tutorial
884 ;; editing session.
885 (when dont-ask-for-revert
886 (setq old-tut-file nil))
887 (when old-tut-file
888 (setq old-tut-file
889 (y-or-n-p "Resume your last saved tutorial? ")))
890 (if old-tut-file
891 (progn
892 (insert-file-contents (tutorial--saved-file))
893 (goto-char (point-min))
894 (setq old-tut-point
895 (string-to-number
896 (buffer-substring-no-properties
897 (line-beginning-position) (line-end-position))))
898 (forward-line)
899 (setq tutorial--point-before-chkeys
900 (string-to-number
901 (buffer-substring-no-properties
902 (line-beginning-position) (line-end-position))))
903 (forward-line)
904 (delete-region (point-min) (point))
905 (goto-char tutorial--point-before-chkeys)
906 (setq tutorial--point-before-chkeys (point-marker)))
907 (insert-file-contents (expand-file-name filename data-directory))
908 (forward-line)
909 (setq tutorial--point-before-chkeys (point-marker)))
910
911
912 ;; Check if there are key bindings that may disturb the
913 ;; tutorial. If so tell the user.
914 (let ((changed-keys (tutorial--find-changed-keys tutorial--default-keys)))
915 (when changed-keys
916 (tutorial--display-changes changed-keys)))
917
918
919 ;; Clear message:
920 (unless dont-ask-for-revert
921 (message "") (sit-for 0))
922
923
924 (if old-tut-file
925 ;; Just move to old point in saved tutorial.
926 (let ((old-point
927 (if (> 0 old-tut-point)
928 (- old-tut-point)
929 (+ old-tut-point tutorial--point-after-chkeys))))
930 (when (< old-point 1)
931 (setq old-point 1))
932 (goto-char old-point))
933 (goto-char (point-min))
934 (search-forward "\n<<")
935 (beginning-of-line)
936 ;; Convert the <<...>> line to the proper [...] line,
937 ;; or just delete the <<...>> line if a [...] line follows.
938 (cond ((save-excursion
939 (forward-line 1)
940 (looking-at "\\["))
941 (delete-region (point) (progn (forward-line 1) (point))))
942 ((looking-at "<<Blank lines inserted.*>>")
943 (replace-match "[Middle of page left blank for didactic purposes. Text continues below]"))
944 (t
945 (looking-at "<<")
946 (replace-match "[")
947 (search-forward ">>")
948 (replace-match "]")))
949 (beginning-of-line)
950 (let ((n (- (window-height (selected-window))
951 (count-lines (point-min) (point))
952 6)))
953 (if (< n 8)
954 (progn
955 ;; For a short gap, we don't need the [...] line,
956 ;; so delete it.
957 (delete-region (point) (progn (end-of-line) (point)))
958 (newline n))
959 ;; Some people get confused by the large gap.
960 (newline (/ n 2))
961
962 ;; Skip the [...] line (don't delete it).
963 (forward-line 1)
964 (newline (- n (/ n 2)))))
965 (goto-char (point-min)))
966 (setq buffer-undo-list nil)
967 (set-buffer-modified-p nil)))))
968
969
970 ;; Below is some attempt to handle language specific strings. These
971 ;; are currently only used in the tutorial.
972
973 (defconst lang-strings
974 '(
975 ("English" .
976 (
977 (tut-chgdkey . "** The key %s has been rebound, but you can use %s instead [")
978 (tut-chgdkey2 . "More information")
979 (tut-chgdhead . "
980 NOTICE: The main purpose of the Emacs tutorial is to teach you
981 the most important standard Emacs commands (key bindings).
982 However, your Emacs has been customized by changing some of
983 these basic editing commands, so it doesn't correspond to the
984 tutorial. We have inserted colored notices where the altered
985 commands have been introduced. [")
986 (tut-chgdhead2 . "Details")
987 )
988 )
989 )
990 "Language specific strings for Emacs.
991 This is an association list with the keys equal to the strings
992 that can be returned by `read-language-name'. The elements in
993 the list are themselves association lists with keys that are
994 string ids and values that are the language specific strings.
995
996 See `get-lang-string' for more information.")
997
998 (defun get-lang-string(lang stringid &optional no-eng-fallback)
999 "Get a language specific string for Emacs.
1000 In certain places Emacs can replace a string showed to the user with a language specific string.
1001 This function retrieves such strings.
1002
1003 LANG is the language specification. It should be one of those
1004 strings that can be returned by `read-language-name'. STRINGID
1005 is a symbol that specifies the string to retrieve.
1006
1007 If no string is found for STRINGID in the choosen language then
1008 the English string is returned unless NO-ENG-FALLBACK is non-nil.
1009
1010 See `lang-strings' for more information.
1011
1012 Currently this feature is only used in `help-with-tutorial'."
1013 (let ((my-lang-strings (assoc lang lang-strings))
1014 (found-string))
1015 (when my-lang-strings
1016 (let ((entry (assoc stringid (cdr my-lang-strings))))
1017 (when entry
1018 (setq found-string (cdr entry)))))
1019 ;; Fallback to English strings
1020 (unless (or found-string
1021 no-eng-fallback)
1022 (setq found-string (get-lang-string "English" stringid t)))
1023 found-string))
1024
1025 ;;(get-lang-string "English" 'tut-chgdkey)
1026
1027 (provide 'tutorial)
1028
1029 ;; arch-tag: c8e80aef-c3bb-4ffb-8af6-22171bf0c100
1030 ;;; tutorial.el ends here