home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 2: Applications / Linux Cubed Series 2 - Applications.iso / editors / emacs / xemacs / xemacs-1.006 / xemacs-1 / lib / xemacs-19.13 / lisp / modes / lisp-mnt.el < prev    next >
Encoding:
Text File  |  1995-04-26  |  14.5 KB  |  452 lines

  1. ;;; lisp-mnt.el --- minor mode for Emacs Lisp maintainers
  2.  
  3. ;; Copyright (C) 1992, 1994, 1995 Free Software Foundation, Inc.
  4.  
  5. ;; Author: Eric S. Raymond <esr@snark.thyrsus.com>
  6. ;; Maintainer: Eric S. Raymond <esr@snark.thyrsus.com>
  7. ;; Created: 14 Jul 1992
  8. ;; Version: $Id: lisp-mnt.el,v 1.10 1995/03/16 04:37:48 rms Exp $
  9. ;; Keywords: docs
  10. ;; X-Modified-by: Bob Weiner <weiner@mot.com>, 4/14/95, to support InfoDock
  11. ;;     headers.
  12. ;; X-Bogus-Bureaucratic-Cruft: Gruad will get you if you don't watch out!
  13.  
  14. ;; This file is part of XEmacs.
  15.  
  16. ;; XEmacs is free software; you can redistribute it and/or modify it
  17. ;; under the terms of the GNU General Public License as published by
  18. ;; the Free Software Foundation; either version 2, or (at your option)
  19. ;; any later version.
  20.  
  21. ;; XEmacs is distributed in the hope that it will be useful, but
  22. ;; WITHOUT ANY WARRANTY; without even the implied warranty of
  23. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  24. ;; General Public License for more details.
  25.  
  26. ;; You should have received a copy of the GNU General Public License
  27. ;; along with XEmacs; see the file COPYING.  If not, write to the Free
  28. ;; Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  29.  
  30. ;;; Synched up with: FSF 19.29.
  31.  
  32. ;;; Commentary:
  33.  
  34. ;; This minor mode adds some services to Emacs-Lisp editing mode.
  35. ;;
  36. ;; First, it knows about the header conventions for library packages.
  37. ;; One entry point supports generating synopses from a library directory.
  38. ;; Another can be used to check for missing headers in library files.
  39. ;; 
  40. ;; Another entry point automatically addresses bug mail to a package's
  41. ;; maintainer or author.
  42.  
  43. ;; This file can be loaded by your lisp-mode-hook.  Have it (require 'lisp-mnt)
  44.  
  45. ;; This file is an example of the header conventions.  Note the following
  46. ;; features:
  47. ;; 
  48. ;;    * Header line --- makes it possible to extract a one-line summary of
  49. ;; the package's uses automatically for use in library synopses, KWIC
  50. ;; indexes and the like.
  51. ;; 
  52. ;;    Format is three semicolons, followed by the filename, followed by
  53. ;; three dashes, followed by the summary.  All fields space-separated.
  54. ;; 
  55. ;;    * Author line --- contains the name and net address of at least
  56. ;; the principal author.
  57. ;; 
  58. ;;    If there are multiple authors, they should be listed on continuation
  59. ;; lines led by ;;<TAB>, like this:
  60. ;; 
  61. ;; ;; Author: Ashwin Ram <Ram-Ashwin@cs.yale.edu>
  62. ;; ;;    Dave Sill <de5@ornl.gov>
  63. ;; ;;    David Lawrence <tale@pawl.rpi.edu>
  64. ;; ;;    Noah Friedman <friedman@ai.mit.edu>
  65. ;; ;;    Joe Wells <jbw@maverick.uswest.com>
  66. ;; ;;    Dave Brennan <brennan@hal.com>
  67. ;; ;;    Eric Raymond <esr@snark.thyrsus.com>
  68. ;; 
  69. ;; This field may have some special values; notably "FSF", meaning
  70. ;; "Free Software Foundation".
  71. ;; 
  72. ;;    * Maintainer line --- should be a single name/address as in the Author
  73. ;; line, or an address only, or the string "FSF".  If there is no maintainer
  74. ;; line, the person(s) in the Author field are presumed to be it.  The example
  75. ;; in this file is mildly bogus because the maintainer line is redundant.
  76. ;;    The idea behind these two fields is to be able to write a lisp function
  77. ;; that does "send mail to the author" without having to mine the name out by
  78. ;; hand. Please be careful about surrounding the network address with <> if
  79. ;; there's also a name in the field.
  80. ;; 
  81. ;;    * Created line --- optional, gives the original creation date of the
  82. ;; file.  For historical interest, basically.
  83. ;; 
  84. ;;    * Version line --- intended to give the reader a clue if they're looking
  85. ;; at a different version of the file than the one they're accustomed to.  This
  86. ;; may be an RCS or SCCS header.
  87. ;; 
  88. ;;    * Adapted-By line --- this is for FSF's internal use.  The person named
  89. ;; in this field was the one responsible for installing and adapting the
  90. ;; package for the distribution.  (This file doesn't have one because the
  91. ;; author *is* one of the maintainers.)
  92. ;; 
  93. ;;    * Keywords line --- used by the finder code (now under construction)
  94. ;; for finding Emacs Lisp code related to a topic.
  95. ;;
  96. ;;    * X-Bogus-Bureaucratic-Cruft line --- this is a joke and an example
  97. ;; of a comment header.  Headers starting with `X-' should never be used
  98. ;; for any real purpose; this is the way to safely add random headers
  99. ;; without invoking the wrath of any program.
  100. ;;
  101. ;;    * Commentary line --- enables lisp code to find the developer's and
  102. ;; maintainers' explanations of the package internals.
  103. ;; 
  104. ;;    * Change log line --- optional, exists to terminate the commentary
  105. ;; section and start a change-log part, if one exists.
  106. ;; 
  107. ;;    * Code line --- exists so Lisp can know where commentary and/or
  108. ;; change-log sections end.
  109. ;; 
  110. ;;    * Footer line --- marks end-of-file so it can be distinguished from
  111. ;; an expanded formfeed or the results of truncation.
  112.  
  113. ;;; Change Log:
  114.  
  115. ;; Tue Jul 14 23:44:17 1992    ESR
  116. ;;    * Created.
  117.  
  118. ;;; Code:
  119.  
  120. (require 'picture)        ; provides move-to-column-force
  121. (require 'emacsbug)
  122.  
  123. ;; These functions all parse the headers of the current buffer
  124.  
  125. (defun lm-section-mark (hd &optional after)
  126.   ;; Return the buffer location of a given section start marker
  127.   (save-excursion
  128.     (let ((case-fold-search t))
  129.       (goto-char (point-min))
  130.       (if (re-search-forward (concat "^;;;* " hd "[:.][ \t\n]*") nil t)
  131.       (if after
  132.           (point)
  133.         (match-beginning 0))))))
  134.  
  135. (defun lm-code-mark ()
  136.   ;; Return the buffer location of the code start marker
  137.   (or (lm-section-mark "Code")
  138.       (save-excursion
  139.     (goto-char (point-min))
  140.     (if (re-search-forward "^\(" nil t)
  141.         (match-beginning 0)))))
  142.  
  143. (defun lm-header (hd)
  144.   ;; Return the contents of a named header
  145.     (goto-char (point-min))
  146.     (let ((case-fold-search t))
  147.       (if (re-search-forward
  148.        (concat "^;; " hd ":[ \t]+\\(.*\\)") (lm-code-mark) t)
  149.       (buffer-substring (match-beginning 1) (match-end 1))
  150.     nil)))
  151.  
  152. (defun lm-header-multiline (hd)
  153.   ;; Return the contents of a named header, with possible continuation lines.
  154.   ;; Note -- the returned value is a list of strings, one per line.
  155.   (save-excursion
  156.     (goto-char (point-min))
  157.     (let ((res (save-excursion (lm-header hd))))
  158.       (if res
  159.       (progn
  160.         (forward-line 1)
  161.         (setq res (list res))
  162.         (while (looking-at "^;;\t\\(.*\\)")
  163.           (setq res (cons (buffer-substring
  164.                    (match-beginning 1)
  165.                    (match-end 1))
  166.                   res))
  167.           (forward-line 1))))
  168.       res)))
  169.  
  170. ;; These give us smart access to the header fields and commentary
  171.  
  172. (defun lm-summary (&optional file)
  173.   ;; Return the buffer's or FILE's one-line summary.
  174.   (save-excursion
  175.     (if file
  176.     (find-file file))
  177.     (goto-char (point-min))
  178.     (prog1
  179.     (if (or (looking-at "^;;; [^ ]+ ---[ \t]+\\(.*\\)")
  180.         (re-search-forward "^;; SUMMARY:[ \t]+\\(.*\\)" nil t))
  181.         (buffer-substring (match-beginning 1) (match-end 1)))
  182.       (if file
  183.       (kill-buffer (current-buffer)))
  184.       )))
  185.  
  186.  
  187. (defun lm-crack-address (x)
  188.   ;; Given a string containing a human and email address, parse it
  189.   ;; into a cons pair (name . address).
  190.   (cond ((string-match "\\(.+\\) [(<]\\(\\S-+@\\S-+\\)[>)]" x)
  191.      (cons (substring x (match-beginning 1) (match-end 1))
  192.            (substring x (match-beginning 2) (match-end 2))))
  193.     ((string-match "\\(\\S-+@\\S-+\\) [(<]\\(.*\\)[>)]" x)
  194.      (cons (substring x (match-beginning 2) (match-end 2))
  195.            (substring x (match-beginning 1) (match-end 1))))
  196.     ((string-match "\\S-+@\\S-+" x)
  197.      (cons nil x))
  198.     (t
  199.      (cons x nil))))
  200.  
  201. (defun lm-authors (&optional file)
  202.   ;; Return the buffer's or FILE's author list.  Each element of the
  203.   ;; list is a cons; the car is a name-aming-humans, the cdr an email
  204.   ;; address.
  205.   (save-excursion
  206.     (if file
  207.     (find-file file))
  208.     (let* ((authorlist (lm-header-multiline "author"))
  209.        (email-list (lm-header-multiline "E-MAIL"))
  210.        (authors authorlist))
  211.       (prog1
  212.       (if (null email-list)
  213.           (mapcar 'lm-crack-address authorlist)
  214.         (while (and email-list authors)
  215.           (setcar authors (cons (car authors) (car email-list)))
  216.           (setq email-list (cdr email-list)
  217.             authors (cdr authors)))
  218.         authorlist)
  219.     (if file
  220.         (kill-buffer (current-buffer)))))))
  221.  
  222. (defun lm-maintainer (&optional file)
  223.   ;; Get a package's bug-report & maintenance address.  Parse it out of FILE,
  224.   ;; or the current buffer if FILE is nil.
  225.   ;; The return value is a (name . address) cons.
  226.   (save-excursion
  227.     (if file
  228.     (find-file file))
  229.     (prog1
  230.     (let ((maint (lm-header "maintainer")))
  231.       (if maint
  232.           (lm-crack-address maint)
  233.         (car (lm-authors))))
  234.       (if file
  235.       (kill-buffer (current-buffer))))))
  236.  
  237. (defun lm-creation-date (&optional file)
  238.   ;; Return a package's creation date, if any.  Parse it out of FILE,
  239.   ;; or the current buffer if FILE is nil.
  240.   (save-excursion
  241.     (if file
  242.     (find-file file))
  243.     (prog1
  244.     (or (lm-header "created")
  245.         (let ((date-and-time (lm-header "ORIG-DATE")))
  246.           (if date-and-time
  247.           (substring date-and-time 0
  248.                  (string-match " " date-and-time)))))
  249.       (if file
  250.       (kill-buffer (current-buffer))))))
  251.  
  252. (defun lm-last-modified-date (&optional file)
  253.   ;; Return a package's last-modified date, if you can find one.
  254.   (save-excursion 
  255.     (if file
  256.     (find-file file))
  257.     (prog1
  258.     (if (progn
  259.           (goto-char (point-min))
  260.           (re-search-forward
  261.            "\\$Id: [^ ]+ [^ ]+ \\([^/]+\\)/\\([^/]+\\)/\\([^ ]+\\) "
  262.            (lm-code-mark) t))
  263.         (format "%s %s %s"
  264.             (buffer-substring (match-beginning 3) (match-end 3))
  265.             (nth (string-to-int 
  266.               (buffer-substring (match-beginning 2) (match-end 2)))
  267.              '("" "Jan" "Feb" "Mar" "Apr" "May" "Jun"
  268.                "Jul" "Aug" "Sep" "Oct" "Nov" "Dec"))
  269.             (buffer-substring (match-beginning 1) (match-end 1)))
  270.       (let ((date-and-time (lm-header "LAST-MOD")))
  271.         (if date-and-time
  272.         (substring date-and-time 0
  273.                (string-match " " date-and-time)))))
  274.       (if file
  275.       (kill-buffer (current-buffer))))))
  276.  
  277. (defun lm-version (&optional file)
  278.   ;; Return the package's version field.
  279.   ;; If none, look for an RCS or SCCS header to crack it out of.
  280.   (save-excursion 
  281.     (if file
  282.     (find-file file))
  283.     (prog1
  284.     (or
  285.      (lm-header "version")
  286.      (let ((header-max (lm-code-mark)))
  287.        (goto-char (point-min))
  288.        (cond
  289.         ;; Look for an RCS header
  290.         ((re-search-forward "\\$Id: [^ ]+ \\([^ ]+\\) " header-max t)
  291.          (buffer-substring (match-beginning 1) (match-end 1)))
  292.  
  293.         ;; Look for an SCCS header
  294.         ((re-search-forward 
  295.           (concat
  296.            (regexp-quote "@(#)")
  297.            (regexp-quote (file-name-nondirectory (buffer-file-name)))
  298.            "\t\\([012345679.]*\\)")
  299.           header-max t)
  300.          (buffer-substring (match-beginning 1) (match-end 1)))
  301.  
  302.         (t nil))))
  303.       (if file
  304.       (kill-buffer (current-buffer))))))
  305.  
  306. (defun lm-keywords (&optional file)
  307.   ;; Return the header containing the package's topic keywords.
  308.   ;; Parse them out of FILE, or the current buffer if FILE is nil.
  309.   (save-excursion
  310.     (if file
  311.     (find-file file))
  312.     (prog1
  313.     (let ((keywords (lm-header "keywords")))
  314.       (and keywords (downcase keywords)))
  315.       (if file
  316.       (kill-buffer (current-buffer))))))
  317.  
  318. (defun lm-adapted-by (&optional file)
  319.   ;; Return the name or code of the person who cleaned up this package
  320.   ;; for distribution.  Parse it out of FILE, or the current buffer if
  321.   ;; FILE is nil.
  322.   (save-excursion
  323.     (if file
  324.     (find-file file))
  325.     (prog1
  326.     (lm-header "adapted-by")
  327.       (if file
  328.       (kill-buffer (current-buffer))))))
  329.  
  330. (defun lm-commentary (&optional file)
  331.   ;; Return the commentary region of a file, as a string.
  332.   (save-excursion
  333.     (if file
  334.     (find-file file))
  335.     (prog1
  336.     (let ((commentary (lm-section-mark "Commentary" t))
  337.           (end (lm-section-mark "Change Log")))
  338.       (if commentary
  339.           (if end
  340.           (buffer-substring commentary end)
  341.         (setq end (lm-section-mark "Code"))
  342.         (buffer-substring commentary end))
  343.         (setq commentary (lm-section-mark "DESCRIPTION" t)
  344.           end (lm-section-mark "DESCRIP-END"))
  345.         (and commentary end (buffer-substring commentary end))))
  346.       (if file
  347.       (kill-buffer (current-buffer))))))
  348.  
  349. ;;; Verification and synopses
  350.  
  351. (defun insert-at-column (col &rest pieces)
  352.    (if (> (current-column) col) (insert "\n"))
  353.    (move-to-column-force col)
  354.    (apply 'insert pieces))
  355.  
  356. (defconst lm-comment-column 16)
  357.  
  358. (defun lm-verify (&optional file showok)
  359.   "Check that the current buffer (or FILE if given) is in proper format.
  360. If FILE is a directory, recurse on its files and generate a report into
  361. a temporary buffer."
  362.   (if (and file (file-directory-p file))
  363.       (progn
  364.     (switch-to-buffer (get-buffer-create "*lm-verify*"))
  365.     (erase-buffer)
  366.     (mapcar
  367.      (function
  368.       (lambda (f)
  369.         (if (string-match ".*\\.el$" f)
  370.         (let ((status (lm-verify f)))
  371.           (if status
  372.               (progn
  373.             (insert f ":")
  374.             (insert-at-column lm-comment-column status "\n"))
  375.             (and showok
  376.              (progn
  377.                (insert f ":")
  378.                (insert-at-column lm-comment-column "OK\n")))))))
  379.       (directory-files file))))
  380.     (save-excursion
  381.       (if file
  382.       (find-file file))
  383.       (prog1
  384.       (cond
  385.        ((not (lm-summary))
  386.         "Can't find a package summary")
  387.        ((not (lm-code-mark))
  388.         "Can't find a code section marker")
  389.        ((progn
  390.           (goto-char (point-max))
  391.           (forward-line -1)
  392.           (looking-at (concat ";;; " file "ends here")))
  393.         "Can't find a footer line")
  394.        )
  395.     (if file
  396.         (kill-buffer (current-buffer)))))))
  397.  
  398. (defun lm-synopsis (&optional file showall)
  399.   "Generate a synopsis listing for the buffer or the given FILE if given.
  400. If FILE is a directory, recurse on its files and generate a report into
  401. a temporary buffer.  If SHOWALL is on, also generate a line for files
  402. which do not include a recognizable synopsis."
  403.   (if (and file (file-directory-p file))
  404.       (progn
  405.     (switch-to-buffer (get-buffer-create "*lm-verify*"))
  406.     (erase-buffer)
  407.     (mapcar
  408.      (function
  409.       (lambda (f)
  410.         (if (string-match ".*\\.el$" f)
  411.         (let ((syn (lm-synopsis f)))
  412.           (if syn
  413.               (progn
  414.             (insert f ":")
  415.             (insert-at-column lm-comment-column syn "\n"))
  416.             (and showall
  417.              (progn
  418.                (insert f ":")
  419.                (insert-at-column lm-comment-column "NA\n"))))))))
  420.      (directory-files file)))
  421.     (save-excursion
  422.       (if file
  423.       (find-file file))
  424.       (prog1
  425.       (lm-summary)
  426.     (if file
  427.         (kill-buffer (current-buffer)))))))
  428.  
  429. (defun lm-report-bug (topic)
  430.   "Report a bug in the package currently being visited to its maintainer.
  431. Prompts for bug subject.  Leaves you in a mail buffer."
  432.   (interactive "sBug Subject: ")
  433.   (let ((package (buffer-name))
  434.     (addr (lm-maintainer))
  435.     (version (lm-version)))
  436.     (mail nil
  437.       (if addr
  438.           (concat (car addr) " <" (cdr addr) ">")
  439.         bug-gnu-emacs)
  440.       topic)
  441.     (goto-char (point-max))
  442.     (insert "\nIn "
  443.         package
  444.         (if version (concat " version " version) "")
  445.         "\n\n")
  446.     (message
  447.      (substitute-command-keys "Type \\[mail-send] to send bug report."))))
  448.  
  449. (provide 'lisp-mnt)
  450.  
  451. ;;; lisp-mnt.el ends here
  452.