1 ;;; assistant.el --- guiding users through Emacs setup
2 ;; Copyright (C) 2004 Free Software Foundation, Inc.
4 ;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
7 ;; This file is part of GNU Emacs.
9 ;; GNU Emacs is free software; you can redistribute it and/or modify
10 ;; it under the terms of the GNU General Public License as published by
11 ;; the Free Software Foundation; either version 2, or (at your option)
14 ;; GNU Emacs is distributed in the hope that it will be useful,
15 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
16 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 ;; GNU General Public License for more details.
19 ;; You should have received a copy of the GNU General Public License
20 ;; along with GNU Emacs; see the file COPYING. If not, write to the
21 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22 ;; Boston, MA 02111-1307, USA.
31 (defvar assistant-readers
32 '(("variable" assistant-variable-reader)
33 ("validate" assistant-sexp-reader)
34 ("result" assistant-list-reader)
35 ("next" assistant-list-reader)))
37 ;;; Internal variables
39 (defvar assistant-data nil)
40 (defvar assistant-current-node nil)
42 (defun assistant-parse-buffer ()
43 (let (results command value)
44 (goto-char (point-min))
45 (while (search-forward "@" nil t)
46 (if (not (looking-at "[^ \t\n]+"))
48 (setq command (downcase (match-string 0)))
49 (goto-char (match-end 0)))
51 (if (looking-at "[ \t]*\n")
55 (unless (re-search-forward (concat "^@end " command) nil t)
56 (error "No @end %s found" command))
59 (buffer-substring start (point))
61 (skip-chars-forward " \t")
63 (buffer-substring (point) (line-end-position))
65 (push (list command (assistant-reader command value))
67 (assistant-segment (nreverse results))))
69 ;; Segment the raw assistant data into a list of nodes.
70 (defun assistant-segment (list)
75 (when (and (equal (car elem) "node")
77 (push (list "save" nil) node)
78 (push (nreverse node) ast)
82 (push (list "save" nil) node)
83 (push (nreverse node) ast))
84 (cons title (nreverse ast))))
86 (defun assistant-reader (command value)
87 (let ((formatter (cadr (assoc command assistant-readers))))
90 (funcall formatter value))))
92 (defun assistant-list-reader (value)
93 (car (read-from-string (concat "(" value ")"))))
95 (defun assistant-variable-reader (value)
96 (let ((section (car (read-from-string (concat "(" value ")")))))
97 (append section (list (nth 2 section)))))
99 (defun assistant-sexp-reader (value)
100 (if (zerop (length value))
102 (car (read-from-string value))))
104 (defun assistant-buffer-name (title)
105 (format "*Assistant %s*" title))
107 (defun assistant-get (ast command)
108 (cadr (assoc command ast)))
110 (defun assistant-set (ast command value)
111 (let ((elem (assoc command ast)))
113 (setcar (nthcdr 3 elem) value))))
115 (defun assistant-get-list (ast command)
118 (when (equal (car elem) command)
122 (defun assistant (file)
123 "Assist setting up Emacs based on FILE."
124 (interactive "fAssistant file name: ")
127 (insert-file-contents file)
128 (assistant-parse-buffer))))
129 (pop-to-buffer (assistant-buffer-name (assistant-get ast "title")))
130 (assistant-render ast)))
132 (defun assistant-render (ast)
133 (let ((first-node (assistant-get (nth 1 ast) "node")))
134 (set (make-local-variable 'assistant-data) ast)
135 (set (make-local-variable 'assistant-current-node) first-node)
136 (set (make-local-variable 'assistant-previous-node) nil)
137 (assistant-render-node first-node)))
139 (defun assistant-find-node (node-name)
140 (let ((ast (cdr assistant-data)))
142 (not (string= node-name (assistant-get (car ast) "node"))))
146 (defun assistant-previous-node-text (node)
147 (format "[ << Go back to %s ] " node))
149 (defun assistant-next-node-text (node)
151 (format "[ Proceed to %s >> ]" node)
154 (defun assistant-render-node (node-name)
155 (let ((node (assistant-find-node node-name)))
156 (setq assistant-current-node node-name)
158 (insert (cadar assistant-data) "\n\n")
159 (insert node-name "\n\n")
160 (insert (assistant-get node "text") "\n\n")
161 (when assistant-previous-node
162 (assistant-node-button 'previous assistant-previous-node))
163 (assistant-node-button 'next (assistant-find-next-node))
166 (defun assistant-node-button (type node)
167 (let ((text (if (eq type 'next)
168 (assistant-next-node-text node)
169 (assistant-previous-node-text node))))
174 :notify (lambda (widget &rest ignore)
175 (let* ((node (widget-get widget ':assistant-node))
176 (type (widget-get widget ':assistant-type)))
177 (when (eq type 'next)
178 (assistant-validate node))
181 (assistant-render-node node))))
183 (use-local-map widget-keymap)))
185 (defun assistant-validate (node-name)
186 (let* ((node (assistant-find-node node-name))
187 (validation (assistant-get node "validate"))
190 (when (setq result (assistant-eval validation node))
191 (unless (y-or-n-p (format "Error: %s. Continue? " result))
192 (error "%s" result))))
193 (assistant-set node "save" t)))
195 (defun assistant-find-next-node ()
196 (let* ((node (assistant-find-node node-name))
197 (nexts (assistant-get-list node "next"))
199 (while (and (setq elem (pop nexts))
201 (when (assistant-eval (car elem) node)
202 (setq next (cadr elem))))
205 (defun assistant-eval (form node)
206 (let ((bindings nil))
207 (dolist (variable (assistant-get-list node "variable"))
208 (push (list (car variable) (nth 3 variable))
214 (defun assistant-finish ()
217 (dolist (node (cdr assistant-data))
218 (when (assistant-get node "save")
219 (setq result (assistant-get node "result"))
220 (push (list (car result)
221 (assistant-eval (cadr result) node))
224 (nreverse results))))