comparison lisp/emacs-lisp/lisp-mnt.el @ 14501:6ac4623cdc87

(lm-header-prefix): New variable. (lm-comment-column): defvar moved. (lm-history-header, lm-commentary-header): New variables. (lm-get-header-re, lm-get-package-name): New functions. (lm-code-mark): defun --> defsubst. (lm-history-mark, lm-commentary-mark): New functions. (lm-header): Now accepts RCS ident style headers. (lm-summary): Now accepts unix `what' commands prefix @(#). (lm-crack-address, lm-authors, lm-maintainer, lm-insert-at-column): (lm-creation-date, lm-last-modified-date, lm-version): (lm-keywords, lm-adapted-by): Comments made into doc strings. (lm-commentary): Added more tolerant 'cond' case. Now uses functions lm-commentary-mark, lm-... to get points. (lm-verify): Made interactive. Added more check points in the last prog1. (lm-synopsis): Made interactive.
author Richard M. Stallman <rms@gnu.org>
date Tue, 06 Feb 1996 19:07:41 +0000
parents 0ebde55bb21d
children fd6e27938b72
comparison
equal deleted inserted replaced
14500:20946c9d3806 14501:6ac4623cdc87
3 ;; Copyright (C) 1992, 1994 Free Software Foundation, Inc. 3 ;; Copyright (C) 1992, 1994 Free Software Foundation, Inc.
4 4
5 ;; Author: Eric S. Raymond <esr@snark.thyrsus.com> 5 ;; Author: Eric S. Raymond <esr@snark.thyrsus.com>
6 ;; Maintainer: Eric S. Raymond <esr@snark.thyrsus.com> 6 ;; Maintainer: Eric S. Raymond <esr@snark.thyrsus.com>
7 ;; Created: 14 Jul 1992 7 ;; Created: 14 Jul 1992
8 ;; Version: $Id: lisp-mnt.el,v 1.13 1996/01/25 00:55:13 kwzh Exp rms $ 8 ;; Version: $Id: lisp-mnt.el,v 1.14 1996/02/04 21:30:40 rms Exp rms $
9 ;; Keywords: docs 9 ;; Keywords: docs
10 ;; X-Bogus-Bureaucratic-Cruft: Gruad will get you if you don't watch out! 10 ;; X-Bogus-Bureaucratic-Cruft: Gruad will get you if you don't watch out!
11 11
12 ;; This file is part of GNU Emacs. 12 ;; This file is part of GNU Emacs.
13 13
20 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 20 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
21 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 ;; GNU General Public License for more details. 22 ;; GNU General Public License for more details.
23 23
24 ;; You should have received a copy of the GNU General Public License 24 ;; You should have received a copy of the GNU General Public License
25 ;; along with GNU Emacs; see the file COPYING. If not, write to the 25 ;; along with GNU Emacs; see the file COPYING. If not, write to
26 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, 26 ;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
27 ;; Boston, MA 02111-1307, USA.
28 27
29 ;;; Commentary: 28 ;;; Commentary:
30 29
31 ;; This minor mode adds some services to Emacs-Lisp editing mode. 30 ;; This minor mode adds some services to Emacs-Lisp editing mode.
32 ;; 31 ;;
115 ;;; Code: 114 ;;; Code:
116 115
117 (require 'picture) ; provides move-to-column-force 116 (require 'picture) ; provides move-to-column-force
118 (require 'emacsbug) 117 (require 'emacsbug)
119 118
119 ;;; Variables:
120
121 (defconst lm-header-prefix "^;;*[ \t]+\\(@\(#\)\\)?[ \t]*\\([\$]\\)?"
122 "Prefix that is ignored before the tag.
123 Eg. you can write the 1st line synopsis string and headers like this
124 in your lisp package:
125
126 ;; @(#) package.el -- pacakge description
127 ;;
128 ;; @(#) $Maintainer: Person Foo Bar $
129
130 The @(#) construct is used by unix what(1) and
131 then $identifier: doc string $ is used by GNU ident(1)")
132
133 (defconst lm-comment-column 16
134 "Column used for placing formatted output.")
135
136 (defconst lm-commentary-header "Commentary\\|Documentation"
137 "Regexp which matches start of documentation section.")
138
139 (defconst lm-history-header "Change Log\\|History"
140 "Regexp which matches the start of code log section.")
141
142 ;;; Functions:
143
120 ;; These functions all parse the headers of the current buffer 144 ;; These functions all parse the headers of the current buffer
121 145
122 (defun lm-section-mark (hd &optional after) 146 (defsubst lm-get-header-re (header &optional mode)
123 ;; Return the buffer location of a given section start marker 147 "Returns regexp for matching HEADER. If called with optional MODE and
148 with value 'section, return section regexp instead."
149 (cond
150 ((eq mode 'section)
151 (concat "^;;;;* " header ":[ \t]*$"))
152 (t
153 (concat lm-header-prefix header ":[ \t]*"))))
154
155 (defsubst lm-get-package-name ()
156 "Returns package name by looking at the first line."
157 (save-excursion
158 (goto-char (point-min))
159 (if (and (looking-at (concat lm-header-prefix))
160 (progn (goto-char (match-end 0))
161 (looking-at "\\([^\t ]+\\)")
162 (match-end 1)))
163 (buffer-substring (match-beginning 1) (match-end 1))
164 )))
165
166 (defun lm-section-mark (header &optional after)
167 "Return the buffer location of a given section start marker.
168 The HEADER is section mark string to find and AFTER is non-nil
169 returns location of next line."
124 (save-excursion 170 (save-excursion
125 (let ((case-fold-search t)) 171 (let ((case-fold-search t))
126 (goto-char (point-min)) 172 (goto-char (point-min))
127 (if (re-search-forward (concat "^;;;;* " hd ":[ \t]*$") nil t) 173 (if (re-search-forward (lm-get-header-re header 'section) nil t)
128 (progn 174 (progn
129 (beginning-of-line) 175 (beginning-of-line)
130 (if after (forward-line 1)) 176 (if after (forward-line 1))
131 (point)) 177 (point))
132 nil)))) 178 nil))))
133 179
134 (defun lm-code-mark () 180 (defsubst lm-code-mark ()
135 ;; Return the buffer location of the code start marker 181 "Return the buffer location of the 'Code' start marker."
136 (lm-section-mark "Code")) 182 (lm-section-mark "Code"))
137 183
138 (defun lm-header (hd) 184 (defsubst lm-commentary-mark ()
139 ;; Return the contents of a named header 185 "Return the buffer location of the 'Commentary' start marker."
186 (lm-section-mark lm-commentary-header))
187
188 (defsubst lm-history-mark ()
189 "Return the buffer location of the 'history' start marker."
190 (lm-section-mark lm-history-header))
191
192 (defun lm-header (header)
193 "Return the contents of a named HEADER."
140 (goto-char (point-min)) 194 (goto-char (point-min))
141 (let ((case-fold-search t)) 195 (let ((case-fold-search t))
142 (if (re-search-forward 196 (if (and (re-search-forward (lm-get-header-re header) (lm-code-mark) t)
143 (concat "^;; " hd ": \\(.*\\)") (lm-code-mark) t) 197 ;; RCS ident likes format "$identifier: data$"
198 (looking-at "\\([^$\n]+\\)")
199 (match-end 1))
144 (buffer-substring (match-beginning 1) (match-end 1)) 200 (buffer-substring (match-beginning 1) (match-end 1))
145 nil))) 201 nil)))
146 202
147 (defun lm-header-multiline (hd) 203 (defun lm-header-multiline (header)
148 ;; Return the contents of a named header, with possible continuation lines. 204 "Return the contents of a named HEADER, with possible continuation lines.
149 ;; Note -- the returned value is a list of strings, one per line. 205 The returned value is a list of strings, one per line."
150 (save-excursion 206 (save-excursion
151 (goto-char (point-min)) 207 (goto-char (point-min))
152 (let ((res (save-excursion (lm-header hd)))) 208 (let ((res (lm-header header)))
153 (if res 209 (cond
154 (progn 210 (res
155 (forward-line 1) 211 (setq res (list res))
156 (setq res (list res)) 212 (forward-line 1)
157 (while (looking-at "^;;\t\\(.*\\)") 213
158 (setq res (cons (buffer-substring 214 (while (and (looking-at (concat lm-header-prefix "[\t ]+"))
159 (match-beginning 1) 215 (progn
160 (match-end 1)) 216 (goto-char (match-end 0))
161 res)) 217 (looking-at "\\(.*\\)"))
162 (forward-line 1)) 218 (match-end 1))
163 )) 219 (setq res (cons (buffer-substring
164 res))) 220 (match-beginning 1)
221 (match-end 1))
222 res))
223 (forward-line 1))
224 ))
225 res
226 )))
165 227
166 ;; These give us smart access to the header fields and commentary 228 ;; These give us smart access to the header fields and commentary
167 229
168 (defun lm-summary (&optional file) 230 (defun lm-summary (&optional file)
169 ;; Return the buffer's or FILE's one-line summary. 231 "Return the buffer's or optional FILE's one-line summary."
170 (save-excursion 232 (save-excursion
171 (if file 233 (if file
172 (find-file file)) 234 (find-file file))
173 (goto-char (point-min)) 235 (goto-char (point-min))
174 (prog1 236 (prog1
175 (if (looking-at "^;;; [^ ]+ --- \\(.*\\)") 237 (if (and
238 (looking-at lm-header-prefix)
239 (progn (goto-char (match-end 0))
240 (looking-at "[^ ]+[ \t]+--+[ \t]+\\(.*\\)")))
176 (buffer-substring (match-beginning 1) (match-end 1))) 241 (buffer-substring (match-beginning 1) (match-end 1)))
177 (if file 242 (if file
178 (kill-buffer (current-buffer))) 243 (kill-buffer (current-buffer)))
179 ))) 244 )))
180 245
181
182 (defun lm-crack-address (x) 246 (defun lm-crack-address (x)
183 ;; Given a string containing a human and email address, parse it 247 "Cracks email address from string.
184 ;; into a cons pair (name . address). 248 Given a string 'x' containing a human and email address, parse it
249 into a cons pair (NAME . ADDRESS)."
185 (cond ((string-match "\\(.+\\) [(<]\\(\\S-+@\\S-+\\)[>)]" x) 250 (cond ((string-match "\\(.+\\) [(<]\\(\\S-+@\\S-+\\)[>)]" x)
186 (cons (substring x (match-beginning 1) (match-end 1)) 251 (cons (substring x (match-beginning 1) (match-end 1))
187 (substring x (match-beginning 2) (match-end 2)))) 252 (substring x (match-beginning 2) (match-end 2))))
188 ((string-match "\\(\\S-+@\\S-+\\) [(<]\\(.*\\)[>)]" x) 253 ((string-match "\\(\\S-+@\\S-+\\) [(<]\\(.*\\)[>)]" x)
189 (cons (substring x (match-beginning 2) (match-end 2)) 254 (cons (substring x (match-beginning 2) (match-end 2))
192 (cons nil x)) 257 (cons nil x))
193 (t 258 (t
194 (cons x nil)))) 259 (cons x nil))))
195 260
196 (defun lm-authors (&optional file) 261 (defun lm-authors (&optional file)
197 ;; Return the buffer's or FILE's author list. Each element of the 262 "Return the buffer's or optional FILE's author list. Each element of the
198 ;; list is a cons; the car is a name-aming-humans, the cdr an email 263 list is a cons; the car is a name-aming-humans, the cdr an email
199 ;; address. 264 address."
200 (save-excursion 265 (save-excursion
201 (if file 266 (if file
202 (find-file file)) 267 (find-file file))
203 (let ((authorlist (lm-header-multiline "author"))) 268 (let ((authorlist (lm-header-multiline "author")))
204 (prog1 269 (prog1
206 (if file 271 (if file
207 (kill-buffer (current-buffer))) 272 (kill-buffer (current-buffer)))
208 )))) 273 ))))
209 274
210 (defun lm-maintainer (&optional file) 275 (defun lm-maintainer (&optional file)
211 ;; Get a package's bug-report & maintenance address. Parse it out of FILE, 276 "Seearch for 'maintainer'. Get a package's bug-report & maintenance address.
212 ;; or the current buffer if FILE is nil. 277 Parse it out of FILE, or the current buffer if FILE is nil.
213 ;; The return value is a (name . address) cons. 278 The return value is a (NAME . ADDRESS) cons."
214 (save-excursion 279 (save-excursion
215 (if file 280 (if file
216 (find-file file)) 281 (find-file file))
217 (prog1 282 (prog1
218 (let ((maint (lm-header "maintainer"))) 283 (let ((maint (lm-header "maintainer")))
222 (if file 287 (if file
223 (kill-buffer (current-buffer))) 288 (kill-buffer (current-buffer)))
224 ))) 289 )))
225 290
226 (defun lm-creation-date (&optional file) 291 (defun lm-creation-date (&optional file)
227 ;; Return a package's creation date, if any. Parse it out of FILE, 292 "Seearch for 'created'. Return a package's creation date, if any.
228 ;; or the current buffer if FILE is nil. 293 Parse it out of FILE, or the current buffer if FILE is nil."
229 (save-excursion 294 (save-excursion
230 (if file 295 (if file
231 (find-file file)) 296 (find-file file))
232 (prog1 297 (prog1
233 (lm-header "created") 298 (lm-header "created")
235 (kill-buffer (current-buffer))) 300 (kill-buffer (current-buffer)))
236 ))) 301 )))
237 302
238 303
239 (defun lm-last-modified-date (&optional file) 304 (defun lm-last-modified-date (&optional file)
240 ;; Return a package's last-modified date, if you can find one. 305 "Return a package's last-modified date, if it has one."
241 (save-excursion 306 (save-excursion
242 (if file 307 (if file
243 (find-file file)) 308 (find-file file))
244 (prog1 309 (prog1
245 (if (progn 310 (if (progn
258 (if file 323 (if file
259 (kill-buffer (current-buffer))) 324 (kill-buffer (current-buffer)))
260 ))) 325 )))
261 326
262 (defun lm-version (&optional file) 327 (defun lm-version (&optional file)
263 ;; Return the package's version field. 328 "Search for RCS identifier '$Id'. Return the package's version field.
264 ;; If none, look for an RCS or SCCS header to crack it out of. 329 If none, look for an RCS or SCCS header to crack it out of."
265 (save-excursion 330 (save-excursion
266 (if file 331 (if file
267 (find-file file)) 332 (find-file file))
268 (prog1 333 (prog1
269 (or 334 (or
288 (if file 353 (if file
289 (kill-buffer (current-buffer))) 354 (kill-buffer (current-buffer)))
290 ))) 355 )))
291 356
292 (defun lm-keywords (&optional file) 357 (defun lm-keywords (&optional file)
293 ;; Return the header containing the package's topic keywords. 358 "Search for 'keywords'. Return the header containing the package's
294 ;; Parse them out of FILE, or the current buffer if FILE is nil. 359 topic keywords. Parse them out of FILE, or the current buffer if FILE is nil."
295 (save-excursion 360 (save-excursion
296 (if file 361 (if file
297 (find-file file)) 362 (find-file file))
298 (prog1 363 (prog1
299 (let ((keywords (lm-header "keywords"))) 364 (let ((keywords (lm-header "keywords")))
301 (if file 366 (if file
302 (kill-buffer (current-buffer))) 367 (kill-buffer (current-buffer)))
303 ))) 368 )))
304 369
305 (defun lm-adapted-by (&optional file) 370 (defun lm-adapted-by (&optional file)
306 ;; Return the name or code of the person who cleaned up this package 371 "Search for 'adapted-by'. Return the name or code of the person who
307 ;; for distribution. Parse it out of FILE, or the current buffer if 372 cleaned up this package for distribution. Parse it out of FILE, or
308 ;; FILE is nil. 373 the current buffer if FILE is nil."
309 (save-excursion 374 (save-excursion
310 (if file 375 (if file
311 (find-file file)) 376 (find-file file))
312 (prog1 377 (prog1
313 (lm-header "adapted-by") 378 (lm-header "adapted-by")
314 (if file 379 (if file
315 (kill-buffer (current-buffer))) 380 (kill-buffer (current-buffer)))
316 ))) 381 )))
317 382
318 (defun lm-commentary (&optional file) 383 (defun lm-commentary (&optional file)
319 ;; Return the commentary region of a file, as a string. 384 "Return the commentary region of a file, as a string.
320 (save-excursion 385 The area is started with tag 'Commentary' and eded with tag
321 (if file 386 'Change Log' or 'History'."
322 (find-file file)) 387 (save-excursion
323 (prog1 388 (if file
324 (let ((commentary (lm-section-mark "Commentary" t)) 389 (find-file file))
325 (change-log (lm-section-mark "Change Log")) 390 (prog1
326 (code (lm-section-mark "Code"))) 391 (let ((commentary (lm-commentary-mark))
327 (and commentary 392 (change-log (lm-history-mark))
328 (if change-log 393 (code (lm-code-mark))
329 (buffer-substring commentary change-log) 394 )
330 (buffer-substring commentary code))) 395 (cond
331 ) 396 ((and commentary change-log)
397 (buffer-substring commentary change-log))
398 ((and commentary code)
399 (buffer-substring commentary code))
400 (t
401 nil)))
332 (if file 402 (if file
333 (kill-buffer (current-buffer))) 403 (kill-buffer (current-buffer)))
334 ))) 404 )))
335 405
336 ;;; Verification and synopses 406 ;;; Verification and synopses
337 407
338 (defun lm-insert-at-column (col &rest pieces) 408 (defun lm-insert-at-column (col &rest strings)
339 (if (> (current-column) col) (insert "\n")) 409 "Insert list of STRINGS, at column COL."
340 (move-to-column-force col) 410 (if (> (current-column) col) (insert "\n"))
341 (apply 'insert pieces)) 411 (move-to-column-force col)
342 412 (apply 'insert strings))
343 (defconst lm-comment-column 16) 413
344 414 (defun lm-verify (&optional file showok &optional verb)
345 (defun lm-verify (&optional file showok)
346 "Check that the current buffer (or FILE if given) is in proper format. 415 "Check that the current buffer (or FILE if given) is in proper format.
347 If FILE is a directory, recurse on its files and generate a report into 416 If FILE is a directory, recurse on its files and generate a report into
348 a temporary buffer." 417 a temporary buffer."
349 (if (and file (file-directory-p file)) 418 (interactive)
350 (progn 419 (let* ((verb (or verb (interactive-p)))
351 (switch-to-buffer (get-buffer-create "*lm-verify*")) 420 ret
352 (erase-buffer) 421 name
353 (mapcar 422 )
354 '(lambda (f) 423 (if verb
355 (if (string-match ".*\\.el$" f) 424 (setq ret "Ok.")) ;init value
356 (let ((status (lm-verify f))) 425
357 (if status 426 (if (and file (file-directory-p file))
358 (progn 427 (setq
359 (insert f ":") 428 ret
360 (lm-insert-at-column lm-comment-column status "\n")) 429 (progn
361 (and showok 430 (switch-to-buffer (get-buffer-create "*lm-verify*"))
431 (erase-buffer)
432 (mapcar
433 '(lambda (f)
434 (if (string-match ".*\\.el$" f)
435 (let ((status (lm-verify f)))
436 (if status
362 (progn 437 (progn
363 (insert f ":") 438 (insert f ":")
364 (lm-insert-at-column lm-comment-column "OK\n"))))))) 439 (lm-insert-at-column lm-comment-column status "\n"))
365 (directory-files file)) 440 (and showok
366 ) 441 (progn
367 (save-excursion 442 (insert f ":")
368 (if file 443 (lm-insert-at-column lm-comment-column "OK\n")))))))
369 (find-file file)) 444 (directory-files file))
370 (prog1 445 ))
371 (cond 446 (save-excursion
372 ((not (lm-summary)) 447 (if file
373 "Can't find a package summary") 448 (find-file file))
374 ((not (lm-code-mark)) 449 (setq name (lm-get-package-name))
375 "Can't find a code section marker") 450
376 ((progn 451 (setq
377 (goto-char (point-max)) 452 ret
378 (forward-line -1) 453 (prog1
379 (looking-at (concat ";;; " file "ends here"))) 454 (cond
380 "Can't find a footer line") 455 ((null name)
381 ) 456 "Can't find a package NAME")
382 (if file 457
383 (kill-buffer (current-buffer))) 458 ((not (lm-authors))
384 )))) 459 "Author: tag missing.")
460
461 ((not (lm-maintainer))
462 "Maintainer: tag missing.")
463
464 ((not (lm-summary))
465 "Can't find a one-line 'Summary' description")
466
467 ((not (lm-keywords))
468 "Keywords: tag missing.")
469
470 ((not (lm-commentary-mark))
471 "Can't find a 'Commentary' section marker.")
472
473 ((not (lm-history-mark))
474 "Can't find a 'History' section marker.")
475
476 ((not (lm-code-mark))
477 "Can't find a 'Code' section marker")
478
479 ((progn
480 (goto-char (point-max))
481 (not
482 (re-search-backward
483 (concat "^;;;[ \t]+" name "[ \t]+ends here[ \t]*$"
484 "\\|^;;;[ \t]+ End of file[ \t]+" name)
485 nil t
486 )))
487 (format "Can't find a footer line for [%s]" name))
488 (t
489 ret))
490 (if file
491 (kill-buffer (current-buffer)))
492 ))))
493 (if verb
494 (message ret))
495 ret
496 ))
385 497
386 (defun lm-synopsis (&optional file showall) 498 (defun lm-synopsis (&optional file showall)
387 "Generate a synopsis listing for the buffer or the given FILE if given. 499 "Generate a synopsis listing for the buffer or the given FILE if given.
388 If FILE is a directory, recurse on its files and generate a report into 500 If FILE is a directory, recurse on its files and generate a report into
389 a temporary buffer. If SHOWALL is on, also generate a line for files 501 a temporary buffer. If SHOWALL is on, also generate a line for files
390 which do not include a recognizable synopsis." 502 which do not include a recognizable synopsis."
391 (interactive "fSynopsis for (file or dir): ") 503 (interactive
504 (list
505 (read-file-name "Synopsis for (file or dir): ")))
506
392 (if (and file (file-directory-p file)) 507 (if (and file (file-directory-p file))
393 (progn 508 (progn
394 (switch-to-buffer (get-buffer-create "*lm-verify*")) 509 (switch-to-buffer (get-buffer-create "*lm-verify*"))
395 (erase-buffer) 510 (erase-buffer)
396 (mapcar 511 (mapcar
418 533
419 (defun lm-report-bug (topic) 534 (defun lm-report-bug (topic)
420 "Report a bug in the package currently being visited to its maintainer. 535 "Report a bug in the package currently being visited to its maintainer.
421 Prompts for bug subject. Leaves you in a mail buffer." 536 Prompts for bug subject. Leaves you in a mail buffer."
422 (interactive "sBug Subject: ") 537 (interactive "sBug Subject: ")
423 (let ((package (buffer-name)) 538 (let ((package (lm-get-package-name))
424 (addr (lm-maintainer)) 539 (addr (lm-maintainer))
425 (version (lm-version))) 540 (version (lm-version)))
426 (mail nil 541 (mail nil
427 (if addr 542 (if addr
428 (concat (car addr) " <" (cdr addr) ">") 543 (concat (car addr) " <" (cdr addr) ">")
429 bug-gnu-emacs) 544 bug-gnu-emacs)
430 topic) 545 topic)
431 (goto-char (point-max)) 546 (goto-char (point-max))
432 (insert "\nIn " 547 (insert "\nIn "
433 package 548 package
434 (if version (concat " version " version) "") 549 (if version (concat " version " version) "")
435 "\n\n") 550 "\n\n")
436 (message "%s" 551 (message
437 (substitute-command-keys "Type \\[mail-send] to send bug report.")))) 552 (substitute-command-keys "Type \\[mail-send] to send bug report."))))
438 553
439 (provide 'lisp-mnt) 554 (provide 'lisp-mnt)
440 555
441 ;;; lisp-mnt.el ends here 556 ;;; lisp-mnt.el ends here
557