1 ;;; viper-tut.el --- Viper tutorial
3 ;; Author: Lennart Borgman
4 ;; Created: Fri Sep 08 2006
5 (defconst viper-tut:version "0.2") ;;Version: 0.2
8 ;; Compatibility: Emacs 22
10 ;; Features that might be required by this library:
12 ;; `button', `cus-edit', `cus-face', `cus-load', `cus-start',
13 ;; `help-mode', `tutorial', `view', `wid-edit'.
15 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
21 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
26 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
28 ;; This program is free software; you can redistribute it and/or modify
29 ;; it under the terms of the GNU General Public License as published by
30 ;; the Free Software Foundation; either version 2, or (at your option)
33 ;; This program is distributed in the hope that it will be useful,
34 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
35 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
36 ;; GNU General Public License for more details.
38 ;; You should have received a copy of the GNU General Public License
39 ;; along with this program; see the file COPYING. If not, write to the
40 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
41 ;; Boston, MA 02111-1307, USA.
43 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
47 (eval-when-compile (require 'mumamo))
48 (eval-when-compile (require 'ourcomments-util))
52 (defface viper-tut-header-top
53 '((t (:foreground "black" :background "goldenrod3")))
57 (defface viper-tut-header
58 '((t (:foreground "black" :background "goldenrod2" :height 1.8)))
62 (defvar tutorial--tab-map
63 (let ((map (make-sparse-keymap)))
64 (define-key map [tab] 'forward-button)
65 (define-key map [(shift tab)] 'backward-button)
66 (define-key map [(meta tab)] 'backward-button)
68 "Keymap that allows tabbing between buttons.")
70 (defconst viper-tut--emacs-part 6)
72 (defconst viper-tut--default-keys
75 ;; ^D Move DOWN one half-screen
76 ;;(viper-scroll-up [(control ?d)])
77 (viper-scroll-up [?\C-d])
79 ;; ^U Move UP one half-screen
80 ;;(viper-scroll-down [(control ?u)])
81 (viper-scroll-down [?\C-u])
83 ;; h Move left one character
84 (viper-backward-char [?h])
86 ;; j Move down one line
87 (viper-next-line [?j])
90 (viper-previous-line [?k])
92 ;; l Move right one character
93 (viper-forward-char [?l])
96 (viper-command-argument [?d])
98 ;; x X-OUT one character
99 (viper-delete-char [?x])
101 ;; u UNDO last change
104 ;; :q!<RETURN> QUIT without saving changes
107 ;; ZZ Exit and save any changes
108 (viper-save-kill-buffer [?Z ?Z])
110 ;; o OPEN a line for inserting text
111 (viper-open-line [?o])
113 ;; i INSERT starting at the cursor
116 ;; ESC ESCAPE from insert mode
117 ;;(viper-intercept-ESC-key [(escape)])
118 ;(viper-intercept-ESC-key [27])
119 (viper-intercept-ESC-key [escape])
122 ;; viper-intercept-ESC-key
123 ;; viper-intercept-ESC-key
125 ;; (more info current-binding (keymap (118 . cua-repeat-replace-region)) viper-intercept-ESC-key [27] <escape>)))
128 ;;;;;;;;;;;;;; Part 2
129 ;; w Move to the beginning of the next WORD
130 (viper-forward-word [?w])
131 ;; e Move to the END of the next word
132 (viper-end-of-word [?e])
133 ;; b Move BACK to the beginning to the previous word
134 (viper-backward-word [?b])
136 ;; $ Move to the end of the line
137 (viper-goto-eol [?$])
139 ;; ^ Move to the first non-white character on the line
140 (viper-bol-and-skip-white [?^])
142 ;; 0 Move to the first column on the line (column zero)
143 (viper-beginning-of-line [?0])
144 ;; #| Move to an exact column on the line (column #) e.g. 5| 12|
145 (viper-goto-col [?|])
147 ;; f char FIND the next occurrence of char on the line
148 (viper-find-char-forward [?f])
149 ;; t char Move 'TIL the next occurrence of char on the line
150 (viper-goto-char-forward [?t])
152 ;; F char FIND the previous occurrence of char on the line
153 (viper-find-char-backward [?F])
154 ;; T char Move 'TIL the previous occurrence of char on the line
155 (viper-goto-char-backward [?T])
157 ;; ; Repeat the last f, t, F, or T
158 (viper-repeat-find [?\;])
159 ;; , Reverse the last f, t, F, or T
160 (viper-repeat-find-opposite [?,])
162 ;; % Show matching () or {} or []
163 (viper-exec-mapped-kbd-macro [?%])
165 ;; H Move to the HIGHEST position in the window
166 (viper-window-top [?H])
167 ;; M Move to the MIDDLE position in the window
168 (viper-window-middle [?M])
169 ;; L Move to the LOWEST position in the window
170 (viper-window-bottom [?L])
172 ;; m char MARK this location and name it char
173 (viper-mark-point [?m])
174 ;; ' char (quote character) return to line named char
175 ;; '' (quote quote) return from last movement
176 (viper-goto-mark-and-skip-white [?'])
178 ;; G GO to the last line in the file
179 ;; #G GO to line #. (e.g., 3G , 5G , 175G )
180 (viper-goto-line [?G])
182 ;; { (left brace) Move to the beginning of a paragraph
183 ;; } (right brace) Move to the end of a paragraph
184 (viper-backward-paragraph [?{])
185 (viper-forward-paragraph [?}])
187 ;; ( (left paren) Move to the beginning of a sentence
188 ;; ) (right paren) Move to the beginning of the next sentence
189 (viper-backward-sentence [?\(])
190 (viper-forward-sentence [?\)])
192 ;; [[ Move to the beginning of a section
193 ;; ]] Move to the end of a section
194 (viper-brac-function [?\[])
195 (viper-ket-function [?\]])
197 ;; /string Find string looking forward
198 (viper-exec-mapped-kbd-macro [?/])
199 ;; ?string Find string looking backward
200 (viper-search-backward [??])
202 ;; n Repeat last / or ? command
203 ;; N Reverse last / or ? command
204 (viper-search-next [?n])
205 (viper-search-Next [?N])
208 ;;;;;;;;;;;;;; Part 3
210 ;; #movement repeat movement # times
211 (viper-digit-argument [?1])
212 (viper-digit-argument [?2])
213 (viper-digit-argument [?3])
214 (viper-digit-argument [?4])
215 (viper-digit-argument [?5])
216 (viper-digit-argument [?6])
217 (viper-digit-argument [?7])
218 (viper-digit-argument [?8])
219 (viper-digit-argument [?9])
221 ;; dmovement DELETE to where "movement" command specifies
222 ;; d#movement DELETE to where the #movement command specifies
223 ;; d runs the command viper-command-argument
225 ;; ymovement YANK to where "movement" command specifies
226 ;; y#movement YANK to where the #movement command specifies
227 (viper-command-argument [?y])
229 ;; P (upper p) PUT the contents of the buffer before the cursor
230 ;; p (lower p) PUT the contents of the buffer after the cursor
231 (viper-put-back [?p])
232 (viper-Put-back [?P])
234 ;; "#P (upper p) PUT contents of buffer # before the cursor
235 ;; "#p (lower p) PUT contents of buffer # after the cursor
237 ;; "aDELETE DELETE text into buffer a
238 ;; "aYANK YANK text into buffer a
239 ;; "aPUT PUT text from named buffer a
240 (viper-command-argument [?\"])
242 ;; :w<RETURN> WRITE contents of the file (without quitting)
244 ;; :e filename<RETURN> Begin EDITing the file called "filename"
248 ;;;;;;;;;;;;;; Part 4
251 ;; o OPEN a line below the cursor
252 ;; O OPEN a line above the cursor
253 (viper-open-line [?o])
254 (viper-Open-line [?O])
256 ;; i INSERT starting before the cursor
257 ;; I INSERT at the beginning of the line
261 ;; a APPEND starting after the cursor
262 ;; A APPEND at the end of the line
266 ;; ESC ESCAPE from insert mode
267 (viper-intercept-ESC-key [(escape)])
270 (viper-join-lines [?J])
272 ;; #s SUBSTITUTE for # characters
273 ;; #S SUBSTITUTE for # whole lines
274 (viper-substitute [?s])
275 (viper-substitute-line [?S])
277 ;; r REPLACE character (NO need to press ESC)
278 ;; R enter over-type mode
279 (viper-replace-char [?r])
280 (viper-overwrite [?R])
282 ;; cmovement CHANGE to where the movement commands specifies
283 (viper-command-argument [?c])
286 ;;;;;;;;;;;;;; Part 5
288 ;; ~ (tilde) Convert case of current character
289 (viper-toggle-case [?~])
290 ;; U (upper u) UNDO all changes made to the current line
294 ;; . (dot) repeat last change
297 ;; ^F Move FORWARD one full-screen
298 ;; ^B Move BACKWARD one full-screen
299 ;;(viper-scroll-screen [(control ?f)])
300 (viper-scroll-screen [?\C-f])
301 ;;(viper-scroll-screen-back [(control ?b)])
302 (viper-scroll-screen-back [?\C-b])
304 ;; ^E Move the window down one line without moving cursor
305 ;; ^Y Move the window up one line without moving cursor
306 ;;(viper-scroll-up-one [(control ?e)])
307 (viper-scroll-up-one [?\C-e])
308 ;;(viper-scroll-down-one [(control ?y)])
309 (viper-scroll-down-one [?\C-y])
311 ;; z<RETURN> Position the current line to top of window
312 ;; z. Position the current line to middle of window
313 ;; z- Position the current line to bottom of window
314 (viper-line-to-top "z\C-m")
315 (viper-line-to-middle [?z ?.])
316 (viper-line-to-bottom [?z ?-])
318 ;; ^G Show status of current file
319 ;;(viper-info-on-file [(control ?c)(control ?g)])
320 (viper-info-on-file [?\C-c ?\C-g])
322 ;;(recenter [(control ?l)])
323 (recenter-top-bottom [?\C-l])
325 ;; !}fmt Format the paragraph, joining and filling lines to
326 ;; !}sort Sort lines of a paragraph alphabetically
327 (viper-command-argument [?!])
329 ;; >movement Shift right to where the movement command specifies
330 ;; <movement Shift left to where the movement command specifies
331 (viper-command-argument [?>])
332 (viper-command-argument [?<])
336 (defun viper-tut--detailed-help (button)
337 "Give detailed help about changed keys."
338 (with-output-to-temp-buffer (help-buffer)
339 (help-setup-xref (list #'viper-tut--detailed-help button)
341 (with-current-buffer (help-buffer)
342 (let* ((tutorial-buffer (button-get button 'tutorial-buffer))
343 ;;(tutorial-arg (button-get button 'tutorial-arg))
344 (explain-key-desc (button-get button 'explain-key-desc))
345 (part (button-get button 'part))
346 (changed-keys (with-current-buffer tutorial-buffer
347 (let ((tutorial--lang "English"))
348 (tutorial--find-changed-keys
349 (if (= part viper-tut--emacs-part)
350 tutorial--default-keys
351 viper-tut--default-keys))))))
354 "The following key bindings used in the tutorial had been changed\n"
355 (if (= part viper-tut--emacs-part)
356 "from Emacs default in the "
357 "from Viper default in the ")
358 (buffer-name tutorial-buffer) " buffer:\n\n" )
359 (let ((frm " %-9s %-27s %-11s %s\n"))
360 (insert (format frm "Key" "Standard Binding" "Is Now On" "Remark")))
361 (dolist (tk changed-keys)
362 (let* ((def-fun (nth 1 tk))
364 (def-fun-txt (nth 2 tk))
367 (rem-fun (command-remapping def-fun))
368 (key-txt (key-description key))
369 (key-fun (with-current-buffer tutorial-buffer (key-binding key)))
371 (unless (eq def-fun key-fun)
372 ;; Insert key binding description:
373 (when (string= key-txt explain-key-desc)
374 (put-text-property 0 (length key-txt) 'face '(:background "yellow") key-txt))
375 (insert " " key-txt " ")
376 (setq tot-len (length key-txt))
378 (insert (make-string (- 9 tot-len) ? ))
380 ;; Insert a link describing the old binding:
381 (insert-button def-fun-txt
382 'help-echo (format "Describe function '%s" def-fun-txt)
383 'action `(lambda(button) (interactive)
384 (describe-function ',def-fun))
386 (setq tot-len (+ tot-len (length def-fun-txt)))
388 (insert (make-string (- 36 tot-len) ? )))
391 ;; Tell where the old binding is now:
392 (insert (format " %-11s " where))
393 ;; Insert a link with more information, for example
394 ;; current binding and keymap or information about
395 ;; cua-mode replacements:
396 (insert-button (car remark)
397 'help-echo "Give more information about the changed key binding"
398 'action `(lambda(b) (interactive)
399 (let ((value ,(cdr remark)))
401 (tutorial--describe-nonstandard-key value)))
408 It is legitimate to change key bindings, but changed bindings do not
409 correspond to what the tutorial says.
411 (insert-button "Key Binding Conventions"
413 (lambda(button) (interactive)
415 "(elisp) Key Binding Conventions")
416 (message "Type C-x 0 to close the new window"))
419 (with-no-warnings (print-help-return-message))))))
422 (defvar viper-tut--part nil
423 "Viper tutorial part.")
424 (make-variable-buffer-local 'viper-tut--part)
426 (defun viper-tut--saved-file ()
427 "File name in which to save tutorials."
429 (file-name-nondirectory (viper-tut--file viper-tut--part)))
430 (ext (file-name-extension file-name)))
433 (setq file-name (concat file-name ".tut")))
434 (expand-file-name file-name (tutorial--saved-dir))))
436 (defun viper-tut--save-tutorial ()
437 "Save the tutorial buffer.
438 This saves the part of the tutorial before and after the area
439 showing changed keys. It also saves point position and the
440 position where the display of changed bindings was inserted.
442 Do not save anything if not `viper-mode' is enabled in the
444 ;; This runs in a hook so protect it:
446 (when (boundp 'viper-mode-string)
447 (tutorial--save-tutorial-to (viper-tut--saved-file)))
448 (error (warn "Error saving tutorial state: %s" (error-message-string err)))))
451 (defvar viper-tut--parts
453 (0 "0intro" "Introduction")
454 (1 "1basics" "Basic Editing")
455 (2 "2moving" "Moving Efficiently")
456 (3 "3cutpaste" "Cutting and Pasting")
457 (4 "4inserting" "Inserting Techniques")
458 (5 "5tricks" "Tricks and Timesavers")
459 (6 "(no file)" "Emacs tutorial for Viper Users")
462 (defcustom viper-tut-directory
463 (let* ((this-file (if load-file-name
466 (this-dir (file-name-directory this-file)))
467 (file-name-as-directory
468 (expand-file-name "../etc/viper-tut" this-dir)))
469 "Directory where the Viper tutorial files lives."
473 (defun viper-tut--file(part)
474 "Get file name for part."
477 (when (= part (nth 0 rec))
479 (if (= part viper-tut--emacs-part)
480 (let ((tf (expand-file-name (get-language-info "English" 'tutorial) tutorial-directory)))
481 (unless (file-exists-p tf)
482 (error "Can't find the English tutorial file for Emacs: %S" tf))
484 (expand-file-name (nth 1 rec) viper-tut-directory)))))
488 (defun viper-tut-viper-is-on ()
489 ;;(message "viper-tut-viper-is-on, vms=%s, cb=%s" (boundp 'viper-mode-string) (current-buffer))
490 ;;(boundp 'viper-mode-string)
491 (boundp 'viper-current-state))
493 (defun viper-tut--display-changes (changed-keys part)
494 "Display changes to some default Viper key bindings.
495 If some of the default key bindings that the Viper tutorial
496 depends on have been changed then display the changes in the
497 tutorial buffer with some explanatory links.
499 CHANGED-KEYS should be a list in the format returned by
500 `tutorial--find-changed-keys'."
501 (when (or changed-keys
502 (viper-tut-viper-is-on))
503 ;; Need the custom button face for viper buttons:
504 ;;(when (and (boundp 'viper-mode) viper-mode) (require 'cus-edit))
505 (goto-char tutorial--point-before-chkeys)
506 (let* ((start (point))
509 (if (viper-tut-viper-is-on)
510 (if (= part viper-tut--emacs-part)
512 NOTICE: This part of the Viper tutorial runs the Emacs tutorial.
513 Several keybindings are changed from Emacs default (either
514 because of Viper or some other customization) and doesn't
515 correspond to the tutorial.
517 We have inserted colored notices where the altered commands have
518 been introduced. If you change Viper state (vi state, insert
519 state, etc) these notices will be changed to reflect the new
522 NOTICE: The main purpose of the Viper tutorial is to teach you
523 the most important vi commands (key bindings). However, your
524 Emacs has been customized by changing some of these basic Viper
525 editing commands, so it doesn't correspond to the tutorial. We
526 have inserted colored notices where the altered commands have
529 NOTICE: You have currently not turned on Viper. Nothing in this
530 tutorial \(the Viper Tutorial\) will work unless you do that. ["
532 (head2 (if (viper-tut-viper-is-on)
533 (get-lang-string tutorial--lang 'tut-chgdhead2)
534 "More information")))
535 (when (and head head2)
543 (if (viper-tut-viper-is-on)
544 'viper-tut--detailed-help
547 'echo "Click for more information"
548 'face '(:inherit link :background "yellow"))
551 (dolist (tk changed-keys)
552 (let* ((def-fun (nth 1 tk))
554 (def-fun-txt (nth 2 tk))
557 (rem-fun (command-remapping def-fun))
558 (key-txt (key-description key))
559 (key-fun (key-binding key))
561 (unless (eq def-fun key-fun)
562 ;; Mark the key in the tutorial text
563 (unless (string= "Same key" where)
564 (let* ((here (point))
565 (key-desc (key-description key))
566 (vi-char (= 1 (length key-desc)))
569 (when (string= "RET" key-desc)
570 (setq key-desc "Return"))
571 (when (string= "DEL" key-desc)
572 (setq key-desc "Delback"))
573 (while (if (not vi-char)
574 (unless hit ;; Only tell once
577 (concat "[^[:alpha:]]\\("
578 (regexp-quote key-desc)
579 "\\)[^[:alpha:]]") nil t))
581 (next-single-property-change
584 (put-text-property (match-beginning 0)
586 'tutorial-remark nil) ;;'only-colored)
587 (put-text-property (match-beginning 0)
589 'face '(:background "yellow"))
590 (goto-char (1+ vi-char-pos))
591 (setq hit (string= key-desc (char-to-string (char-before))))
593 (put-text-property vi-char-pos (1+ vi-char-pos)
594 'face '(:background "yellow"))))
597 (let ((s (get-lang-string tutorial--lang 'tut-chgdkey))
598 (s2 (get-lang-string tutorial--lang 'tut-chgdkey2))
601 ;; key-desc " has been rebound, but you can use " where " instead ["))
603 (when (or (not where) (= 0 (length where)))
604 (setq where (concat "`M-x " def-fun-txt "'")))
605 (setq s (format s key-desc where s2))
613 'viper-tut--detailed-help
614 'explain-key-desc key-desc
616 'face '(:inherit link :background "yellow"))
620 (put-text-property start end 'local-map tutorial--tab-map)
621 (put-text-property start end 'tutorial-remark t)
622 (put-text-property start end
623 'face '(:background "yellow" :foreground "#c00"))
624 (put-text-property start end 'read-only t)))))
625 (goto-char here)))))))
629 ;; Make the area with information about change key
630 ;; bindings stand out:
631 (put-text-property start end
633 ;; The default warning face does not
634 ;;look good in this situation. Instead
635 ;;try something that could be
636 ;;recognized from warnings in normal
638 ;; 'font-lock-warning-face
639 (list :background "yellow" :foreground "#c00"))
640 ;; Make it possible to use Tab/S-Tab between fields in
642 (put-text-property start end 'local-map tutorial--tab-map)
643 (put-text-property start end 'tutorial-remark t)
644 (setq tutorial--point-after-chkeys (point-marker))
645 ;; Make this area read-only:
646 (put-text-property start end 'read-only t)))))
648 (defun viper-tut--at-change-state()
651 (let ((inhibit-read-only t)
653 ;; Delete the remarks:
654 ;;(tutorial--remove-remarks)
656 ;;(viper-tut--add-remarks)
660 (error (message "error in viper-tut--at-change-state: %s" (error-message-string err)))))
664 (defun viper-tutorial(part &optional dont-ask-for-revert)
665 "Run a tutorial for Viper.
667 A simple classic tutorial in 5 parts that have been used by many
668 people starting to learn vi keys. You may learn enough to start
669 using `viper-mode' in Emacs.
671 Some people find that vi keys helps against repetetive strain
674 `http://www.emacswiki.org/emacs/RepeatedStrainInjury'.
676 Note: There might be a few clashes between vi key binding and
677 Emacs standard key bindings. You will be notified about those in
678 the tutorial. Even more, if your own key bindings comes in
679 between you will be notified about that too."
681 ;; (condition-case nil
682 ;; (widget-choose "The following viper tutorials are available"
683 ;; (mapcar (lambda(rec)
684 ;; (cons (nth 2 rec) (nth 0 rec)))
685 ;; viper-tut--parts))
689 (if (not (boundp 'viper-current-state))
692 You can not run the Viper tutorial in this Emacs because you
693 have not enabled Viper.
695 Do you want to run the Viper tutorial in a new Emacs? "))
696 (if (y-or-n-p prompt)
697 (let ((ret (funcall 'emacs--no-desktop
701 " (setq viper-mode t)"
703 " (require 'viper-tut)"
704 " (call-interactively 'viper-tutorial))"))))
705 (message "Starting Viper tutorial in a new Emacs"))
706 (message "Viper tutorial aborted by user")))
708 (let* ((filename (viper-tut--file part))
709 ;; Choose a buffer name including the language so that
710 ;; several languages can be tested simultaneously:
711 (tut-buf-name "Viper TUTORIAL")
712 (old-tut-buf (get-buffer tut-buf-name))
713 (old-tut-part (when old-tut-buf
714 (with-current-buffer old-tut-buf
716 (old-tut-win (when old-tut-buf (get-buffer-window old-tut-buf t)))
717 (old-tut-is-ok (when old-tut-buf
719 (= part old-tut-part)
720 (not (buffer-modified-p old-tut-buf)))))
723 (unless (file-exists-p filename) (error "Can't fine %s" filename))
724 (setq tutorial--point-after-chkeys (point-min))
725 ;; Try to display the tutorial buffer before asking to revert it.
726 ;; If the tutorial buffer is shown in some window make sure it is
727 ;; selected and displayed:
731 (select-window (get-buffer-window old-tut-buf t))))
732 ;; Else, is there an old tutorial buffer? Then display it:
734 (switch-to-buffer old-tut-buf)))
735 ;; Use whole frame for tutorial
736 ;;(delete-other-windows)
737 ;; If the tutorial buffer has been changed then ask if it should
739 (when (and old-tut-buf
741 (= part old-tut-part))
743 (if dont-ask-for-revert
746 "You have changed the Tutorial buffer. Revert it? ")))))
747 ;; (Re)build the tutorial buffer if it is not ok
748 (unless old-tut-is-ok
749 (switch-to-buffer (get-buffer-create tut-buf-name))
750 (unless old-tut-buf (text-mode))
751 (setq viper-tut--part part)
752 (setq old-tut-file (file-exists-p (viper-tut--saved-file)))
753 (when (= part 0) (setq old-tut-file nil)) ;; You do not edit in the intro
754 (setq buffer-read-only nil)
755 (let ((inhibit-read-only t)) ;; For the text property
757 (message "Preparing Viper tutorial ...") (sit-for 0)
759 ;; Do not associate the tutorial buffer with a file. Instead use
760 ;; a hook to save it when the buffer is killed.
761 (setq buffer-auto-save-file-name nil)
762 (add-hook 'kill-buffer-hook 'viper-tut--save-tutorial nil t)
764 ;; Insert the tutorial. First offer to resume last tutorial
766 (when dont-ask-for-revert
767 (setq old-tut-file nil))
772 "Resume your last saved Viper tutorial part %s? "
776 (insert-file-contents (viper-tut--saved-file))
777 (goto-char (point-min))
780 (buffer-substring-no-properties
781 (line-beginning-position) (line-end-position))))
783 (setq tutorial--point-before-chkeys
785 (buffer-substring-no-properties
786 (line-beginning-position) (line-end-position))))
788 (delete-region (point-min) (point))
789 (goto-char tutorial--point-before-chkeys)
790 (setq tutorial--point-before-chkeys (point-marker)))
791 ;;(insert-file-contents (expand-file-name filename data-directory))
792 (insert-file-contents filename)
793 (viper-tut--replace-links)
795 (goto-char (point-min))
796 (while (re-search-forward "'\\([][+a-zA-Z~<>!;,:.'\"%/?(){}$^0|-]\\)'" nil t)
797 (let ((matched-char (match-string 1))
798 (inhibit-read-only t))
799 (put-text-property 0 1 'vi-char t matched-char)
800 (put-text-property 0 1 'face '(:foreground "blue") matched-char)
801 (replace-match matched-char))))
803 (setq tutorial--point-before-chkeys (point-marker)))
805 (viper-tut--add-remarks)
807 (goto-char (point-min))
809 ;; Just move to old point in saved tutorial.
811 (if (> 0 old-tut-point)
813 (+ old-tut-point tutorial--point-after-chkeys))))
814 (when (< old-point 1)
816 (goto-char old-point)))
818 (viper-tut-fix-header-and-footer)
821 (message "") (sit-for 0)
823 (setq buffer-undo-list nil)
824 (set-buffer-modified-p nil))
825 (setq buffer-read-only (= 0 part)))))
827 ;;(tutorial--find-changed-keys '((scroll-up [?\C-v])))
828 (defun viper-tut--add-remarks()
829 ;; Check if there are key bindings that may disturb the
830 ;; tutorial. If so tell the user.
831 (let* ((tutorial--lang "English")
833 (if (= viper-tut--part viper-tut--emacs-part)
834 (tutorial--find-changed-keys tutorial--default-keys)
835 (tutorial--find-changed-keys viper-tut--default-keys))))
836 (viper-tut--display-changes changed-keys viper-tut--part))
838 (if (= viper-tut--part viper-tut--emacs-part)
840 (add-hook 'viper-vi-state-hook 'viper-tut--at-change-state nil t)
841 (add-hook 'viper-insert-state-hook 'viper-tut--at-change-state nil t)
842 (add-hook 'viper-replace-state-hook 'viper-tut--at-change-state nil t)
843 (add-hook 'viper-emacs-state-hook 'viper-tut--at-change-state nil t)
845 (remove-hook 'viper-vi-state-hook 'viper-tut--at-change-state t)
846 (remove-hook 'viper-insert-statehook 'viper-tut--at-change-state t)
847 (remove-hook 'viper-replace-state-hook 'viper-tut--at-change-state t)
848 (remove-hook 'viper-emacs-state-hook 'viper-tut--at-change-state t)
851 (defun viper-tut-fix-header-and-footer ()
853 (goto-char (point-min))
854 (add-text-properties (point) (1+ (line-end-position))
855 '( read-only t face viper-tut-header))
856 (goto-char (point-min))
857 (viper-tut--insert-goto-row nil)
858 (goto-char (point-max))
859 (viper-tut--insert-goto-row t)))
861 (defun viper-tut--insert-goto-row(last)
862 (let ((start (point))
864 (insert " Go to part: ")
865 (dolist (rec viper-tut--parts)
866 (let ((n (nth 0 rec))
869 (if (= n viper-tut--part)
870 (insert (format "%s" n))
871 (insert-button (format "%s" n)
872 'help-echo (concat "Go to part: " title)
876 (viper-tutorial ,n t))))
879 (insert-button "Exit Tutorial"
880 'help-echo "Exit tutorial and close tutorial buffer"
884 (kill-buffer (current-buffer))))
885 (unless last (insert "\n"))
887 (put-text-property start end 'local-map tutorial--tab-map)
888 (put-text-property start end 'tutorial-remark t)
889 (put-text-property start end
890 'face 'viper-tut-header-top)
891 (put-text-property start end 'read-only t)))
893 (defun viper-tut--replace-links()
894 "Replace markers for links with actual links."
895 (let ((re-links (regexp-opt '("VIPER-MANUAL"
907 (case-fold-search nil)
908 (inhibit-read-only t))
910 (goto-char (point-min))
911 (while (re-search-forward re-links nil t)
912 (let ((matched (match-string 0))
918 ((string= matched "VIPER-TOGGLE-KEY")
919 (insert-button "viper-toggle-key"
921 (lambda(button) (interactive)
922 (describe-variable 'viper-toggle-key))
924 ((string= matched "CUA-MODE")
925 (insert-button "cua-mode"
927 (lambda(button) (interactive)
928 (describe-function 'cua-mode))
930 ((string= matched "ISEARCH-FORWARD")
931 (insert-button "isearch-forward"
933 (lambda(button) (interactive)
934 (describe-function 'isearch-forward))
936 ((string= matched "KILL-BUFFER")
937 (insert-button "kill-buffer"
939 (lambda(button) (interactive)
940 (describe-function 'kill-buffer))
942 ((string= matched "UNIVERSAL-ARGUMENT")
943 (insert-button "universal-argument"
945 (lambda(button) (interactive)
946 (describe-function 'universal-argument))
948 ((string= matched "DIGIT-ARGUMENT")
949 (insert-button "digit-argument"
951 (lambda(button) (interactive)
952 (describe-function 'digit-argument))
954 ((string= matched "* EMACS-NOTICE:")
955 (insert "* Emacs NOTICE:")
958 (not (looking-at "^$"))))
959 (put-text-property start (point)
962 :foreground "#999999"))
963 (put-text-property start (point) 'read-only t)
965 ((string= matched "SEARCH-COMMANDS")
966 (insert-button "search commands"
968 (lambda(button) (interactive)
969 (info-other-window "(emacs) Search")
970 (message "Type C-x 0 to close the new window"))
972 ((string= matched "KEYBOARD-MACROS")
973 (insert-button "keyboard macros"
975 (lambda(button) (interactive)
976 (info-other-window "(emacs) Keyboard Macros")
977 (message "Type C-x 0 to close the new window"))
979 ((string= matched "VIPER-MANUAL")
980 (insert-button "Viper manual"
982 (lambda(button) (interactive)
983 (info-other-window "(viper)")
984 (message "Type C-x 0 to close the new window"))
986 ((string= matched "R-AND-R")
987 (insert-button "r and R"
989 (lambda(button) (interactive)
990 (info-other-window "(viper) Basics")
991 (message "Type C-x 0 to close the new window"))
993 ((string= matched "README-FILE")
994 (insert-button "README file"
996 (lambda(button) (interactive)
997 (find-file-other-window (expand-file-name "README" viper-tut-directory))
998 (message "Type C-x 0 to close the new window"))
1001 (error "Unmatched text: %s" matched)))
1002 (put-text-property start (point) 'tutorial-remark t)
1003 (put-text-property start (point) 'tutorial-orig matched)
1004 (put-text-property start (point) 'local-map tutorial--tab-map)
1005 (put-text-property start (point) 'read-only t))))))
1007 (provide 'viper-tut)
1008 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1009 ;;; viper-tut.el ends here