Add a new BSD-ish licence.
[skel] / skel.el.in
CommitLineData
7fb0878b 1;;; -*-emacs-lisp-*-
2;;;
4000b4fd 3;;; $Id: skel.el.in,v 1.3 1999/08/20 07:40:51 mdw Exp $
7fb0878b 4;;;
5;;; Filling in skeletons
6;;;
7;;; (c) 1998 Mark Wooding
8;;;
9
10;;;----- Licensing notice ---------------------------------------------------
11;;;
12;;; This program is free software; you can redistribute it and/or modify
13;;; it under the terms of the GNU General Public License as published by
14;;; the Free Software Foundation; either version 2 of the License, or
15;;; (at your option) any later version.
16;;;
17;;; This program is distributed in the hope that it will be useful,
18;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20;;; GNU General Public License for more details.
21;;;
22;;; You should have received a copy of the GNU General Public License
23;;; along with this program; if not, write to the Free Software Foundation,
24;;; Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25
26;;;----- Revision history ---------------------------------------------------
27;;;
28;;; $Log: skel.el.in,v $
4000b4fd 29;;; Revision 1.3 1999/08/20 07:40:51 mdw
30;;; Slight twiddles to the Lisp code.
31;;;
4193db31 32;;; Revision 1.2 1999/05/05 19:30:38 mdw
33;;; Misc tidying.
34;;;
35;;; Revision 1.1.1.1 1999/04/05 13:44:59 mdw
36;;; Initial import.
7fb0878b 37;;;
38
39;;;----- Variables (largely tweakable) --------------------------------------
40
41(defvar skel-directory-list '(".skel" "")
42 "*List of directory names which contain skeleton files.")
43
44(defvar skel-skeleton-path '("@skeldir@" "~/skel" "~/src/skel")
45 "*List of directories to search for skeleton data anyway.")
46
47(defvar skel-skelrc '(".skelrc" "skelrc")
48 "*File containing skeleton substitution data, looked up using the standard
49skeleton search mathod.")
50
51(defvar skel-alist '()
52 "Alist of values to substitute into a skeleton. It is filled in by skelrc
53files and user interaction.
54
55The alist's keys are symbols, interned from the placeholder strings in the
56skeleton file. The values are forms to be evaluated. In the simplest case,
57the form is a string added as a result of user interaction; however, it could
58just as easily be a function call or something similarly complicated.")
59
60;;;----- Finding skeleton files ---------------------------------------------
61
62(defun skel-do-join (acc jfun ll s)
63 "Recursive guts of skel-join."
64 (if ll
65 (if (car ll)
66 (skel-do-join (skel-do-join acc jfun (cdr ll)
67 (funcall jfun s (car (car ll))))
68 jfun (cons (cdr (car ll)) (cdr ll)) s)
69 acc)
70 (cons s acc)))
71
72(defun skel-join (jfun base &rest ll)
73 "Return a list built from joining elements from the given lists in order,
74left to right. JFUN is a function of two arguments which can join items
75together. BASE is the initial item."
76 (nreverse (skel-do-join nil jfun ll base)))
77
78(defun skel-do-parents (dir acc)
79 "Tail recursive guts of skel-parents"
80 (setq acc (cons dir acc))
81 (setq dir (substring dir 0 (string-match "/[^/]*/?$" dir)))
82 (if (string= dir "")
83 (cons "/" acc)
84 (skel-do-parents dir acc)))
85
86(defun skel-parents (dir)
87 "Returns a list of DIR, DIR's parent directory, etc., all the way up to the
88root."
89 (setq dir (expand-file-name dir))
90 (nreverse (skel-do-parents dir nil)))
91
92(defun skel-do-find (l all acc)
93 (if l
94 (let ((n (car l)))
95 (if (and (file-readable-p n) (file-regular-p n))
96 (if all
97 (skel-do-find (cdr l) all (cons (abbreviate-file-name n) acc))
98 (abbreviate-file-name n))
99 (skel-do-find (cdr l) all acc)))
100 acc))
101
102(defun skel-find-skeleton (name &optional all acc)
103 "Searches for skeleton files. NAME is the name of the file to find, or
104a list of possible names.
105
106If ALL is nil, or omitted, return only the first matching filename
107encountered. Otherwise, return a list of all matching names, most `global'
108first. ACC is a base list to which the matching filenames are prepended."
109
110 ;; --- Build one big list of all the possible names ---
111
4000b4fd 112 (let ((l (skel-join #'(lambda (x y) (if (string= y "")
113 x
114 (expand-file-name y x)))
7fb0878b 115 nil
116 (append (skel-parents default-directory)
117 skel-skeleton-path)
118 skel-directory-list
4193db31 119 (if (consp name) name (cons name nil)))))
7fb0878b 120
121 ;; --- Now filter out any which aren't interesting ---
122
123 (skel-do-find l all acc)))
124
125;;;----- Processing file skeletons ------------------------------------------
126
127(defun skel-include (file)
128 "Includes the skeleton rc FILE."
129 (let ((rc (skel-find-skeleton file t)))
130 (while rc
131 (load (car rc) nil t t)
132 (setq rc (cdr rc)))))
133
134(defun skel-lookup (name)
135
136 "Reads the value of symbol NAME in skel-alist. If there is no currrent
137value, the user is prompted for one."
138
139 ;; --- Resolve NAME into a symbol ---
140
141 (if (stringp name)
142 (setq name (intern name)))
143
144 ;; --- Look up the value ---
145 ;;
146 ;; Add it to the list if we've not seen it before. Protect ourselves
147 ;; against functions which do regexp matching.
148
149 (let ((pair (assq name skel-alist))
150 value)
151 (if pair
152 (save-match-data (setq value (eval (cdr pair))))
153 (setq value (read-string (format "Value for %s: " name)))
154 (setq skel-alist (cons (cons name value) skel-alist)))
155 value))
156
157(defun skel-do-fill-in ()
158 "Does the actual donkey-work of filling in a file. For each fill-in area
159in the current buffer, the function looks to see if the item in question has
160been entered into ALIST: if so, it is replaced automatically; otherwise the
161user is promted to enter a string to substitute into the buffer at this
162point."
163 (if (re-search-forward "\\[\\[\\([^]]*\\)\\]\\]" nil t)
164 (progn
165 (replace-match (skel-lookup (match-string 1)) t t nil)
166 (goto-char (match-beginning 0))
167 (skel-do-fill-in))))
168
169;;;----- Creating new files from skeletons ----------------------------------
170
171(defun skel-do-create-file (name switch &optional skel)
172
173 "Does the main work of creating a file based on a skeleton. The SWITCH
174argument is called to display the buffer."
175
176 ;; --- Some local variables ---
177 ;;
178 ;; This is a little bit of a hack, but do I look like someone who cares?
179
4193db31 180 (let (ext)
7fb0878b 181
182 ;; --- Find out if the file's there already ---
183
184 (if (file-exists-p name)
185 (or (yes-or-no-p
186 (format "File %s already exists. Overwrite? " name))
187 (error "Aborted!")))
188
189 ;; --- Fiddle with the filename ---
190
191 (cond ((stringp skel) (let ((extind (string-match "\.[^.]*$" skel)))
192 (setq ext (and extind (substring skel extind)))))
193 (skel (progn
194 (setq ext (read-string "Extension: "))
195 (or (string= ext "") (setq ext (concat "." ext)))))
196 (t (let ((extind (string-match "\.[^.]*$" name)))
197 (setq ext (and extind (substring name extind))))))
198 (setq skel (concat "skeleton" (or ext "")))
199
200 ;; --- Find the skeleton filename ---
201
202 (setq skel (or (skel-find-skeleton skel)
203 (skel-find-skeleton "skeleton")
204 (error "Couldn't find skeleton file %s" skel)))
205
206 ;; --- Visit the file and destroy its contents ---
207
208 (funcall switch (find-file-noselect name))
209 (kill-region (point-min) (point-max))
210 (insert-file skel)
211
212 ;; --- Mangle the skeleton data in the file ---
213
214 (make-local-variable 'skel-alist)
215 (setq skel-alist '())
216
217 ;; --- Read the default values to insert ---
218
219 (let ((rc (append
220 (skel-find-skeleton skel-skelrc t)
221 (and ext
222 (skel-find-skeleton
223 (if (listp skel-skelrc)
4000b4fd 224 (mapcar #'(lambda (x) (concat x ext)) skel-skelrc)
7fb0878b 225 (concat skel-skelrc ext))
226 t)))))
227 (while rc
228 (load (car rc) nil t t)
229 (setq rc (cdr rc))))
230
231 ;; --- Now do substitution ---
232
233 (skel-do-fill-in)
234 (not-modified)))
235
236;;;----- User commands ------------------------------------------------------
237
238(defun skel-create-file (name &optional skel)
239 "Creates a new file called NAME and visits it. If SKEL is non-`nil', it is
240the name of a skeleton file to insert and substitute. Otherwise the skeleton
241file's name is derived from NAME by taking NAME's extension and appending it
242to `skel'."
243 (interactive "FSkeleton create file: \nP")
244 (skel-do-create-file name 'switch-to-buffer skel))
245
246(defun skel-create-file-other-window (name &optional skel)
247 "Like skel-create-file, but in another window."
248 (interactive "FSkeleton create file in other window: \nP")
249 (skel-do-create-file name 'switch-to-buffer-other-window skel))
250
251(defun skel-create-file-other-frame (name &optional skel)
252 "Like skel-create-file, but in another frame."
253 (interactive "FSkeleton create file in other frame: \nP")
254 (skel-do-create-file name 'switch-to-buffer-other-frame skel))
255
256;;;----- Is that all there is? ----------------------------------------------
257
258(provide 'skel)