summaryrefslogtreecommitdiff
path: root/lisp/emacs-lisp/checkdoc.el
diff options
context:
space:
mode:
authorEric M. Ludlam <zappo@gnu.org>1998-07-03 15:15:27 +0000
committerEric M. Ludlam <zappo@gnu.org>1998-07-03 15:15:27 +0000
commitbca0d6075a6fc298526a7677bceaedcf180f0572 (patch)
treebf54c728576813aff3c71db2390fdad04ce746e1 /lisp/emacs-lisp/checkdoc.el
parent20b5aff947261deef6c6805d79d8e7e7365cc89b (diff)
downloademacs-bca0d6075a6fc298526a7677bceaedcf180f0572.tar.gz
emacs-bca0d6075a6fc298526a7677bceaedcf180f0572.tar.bz2
emacs-bca0d6075a6fc298526a7677bceaedcf180f0572.zip
(checkdoc): Updated commentary.
(checkdoc-autofix-flag): Updated doc. (checkdoc-force-docstrings-flag): Updated doc. (checkdoc-force-history-flag): New flag. (checkdoc-triple-semi-comment-check-flag): Fixed name. (checkdoc-spellcheck-documentation-flag): Fixed doc. (checkdoc-ispell-lisp-words): Update default value. (checkdoc-generate-compile-warnings-flag, checkdoc-proper-noun-list, checkdoc-proper-noun-regexp, checkdoc-symbol-words): New variables. (princ-list): Function created if it isn't bound. (checkdoc-interactive): parts removed to `checkdoc-interactive-loop'. (checkdoc,checkdoc-message-interactive): New function. (checkdoc-interactive-loop): was in `checkdoc-interactive', then added better keybindings, and better autofixing behavior, Cursor now sits next to the error, forcing scrolling if needed, and using a better centering algorithm, and much better error navigation after choosing "f"ix. (checkdoc-next-error): Added parameter ENABLE-FIX. (checkdoc-next-message-error,checkdoc-recursive-edit): New functions. (checkdoc-start): was `checkdoc', uses new note taking system. (checkdoc-current-buffer, checkdoc-continue, checkdoc-comments): Updated to use new note taking system. (checkdoc-rogue-spaces, checkdoc-rogue-space-check-engine): Added INTERACT parameter, uses new warnings functions. (checkdoc-message-text, checkdoc-defun): Updated to use new note taking system. (checkdoc-ispell-current-buffer, checkdoc-ispell-interactive): fix doc. (checkdoc-ispell-message-text, checkdoc-ispell-start): New function. (checkdoc-create-error, checkdoc-error-text, checkdoc-error-start, checkdoc-error-end, checkdoc-error-unfixable): New functions. (checkdoc-minor-keymap): Updated keybinds to new interactive functions, completely re-arranged the minor-mode menu. (checkdoc-this-string-valid): Moved no doc-string warning here, and added autofix if a comment already exists there. (checkdoc-this-string-valid-engine): fix doc, robusted doc finder. All previously returned errors now call `checkdoc-create-error'. Moved no doc string warning out. Update allowed punctuation at end of first line. Fixed up sentence joining. Verb checking flag now only checks the first line of a function. Added more safe conditions to ambiguous symbols. Moved symbol quoting to end. Added autofix for variables that should end in `-flag'. Replaced use of `y-or-n-p' with `checkdoc-y-or-n-p'. Reading checkdoc-param comment no longer depends on list syntax. Fixed various error string spelling & format. (checkdoc-in-sample-code-p): List starting with all caps word is now condsidered sample code. (checkdoc-in-example-string-p, checkdoc-proper-noun-region-engine, checkdoc-sentencespace-region-engine): New functions. (checkdoc-ispell-docstring-engine): Disable spell checking during if user never wants interaction. We don't have a non-interactive spell checking method yet. (checkdoc-file-comments-engine): Now set up to check all possible conditions even after encountering an error. Added auto-fixes for history and commentary. All previously returned errors now call `checkdoc-create-error'. Message spelling and format. (checkdoc-message-text-search): Moved parts to `checkdoc-message-text-next-string'. (checkdoc-message-text-next-string): New function (checkdoc-message-text-engine): All previously returned errors now call `checkdoc-create-error'. Can find/skip 'format' call after the call we are checking. Added sentence/propernoun scans. `y-or-n-p' checks and fixes are now more robust. (checkdoc-y-or-n-p): New function. (checkdoc-autofix-ask-replace): Update doc. Protect match-data. Correctly handle `checkdoc-autofix-flag' of 'never. New behavior with `checkdoc-autofix-flag' of 'automatic-then-never. Better overlay handling. (checkdoc-output-font-lock-keywords): Updated to new output format. (checkdoc-pending-errors): New variable. (checkdoc-find-error): Updated to new output format. (checkdoc-start-section, checkdoc-error): Improved the output. (checkdoc-show-diagnostics): Smarter show algorithm.
Diffstat (limited to 'lisp/emacs-lisp/checkdoc.el')
-rw-r--r--lisp/emacs-lisp/checkdoc.el1910
1 files changed, 1358 insertions, 552 deletions
diff --git a/lisp/emacs-lisp/checkdoc.el b/lisp/emacs-lisp/checkdoc.el
index 5be801c2f3c..3432d680441 100644
--- a/lisp/emacs-lisp/checkdoc.el
+++ b/lisp/emacs-lisp/checkdoc.el
@@ -3,7 +3,7 @@
;;; Copyright (C) 1997, 1998 Free Software Foundation
;; Author: Eric M. Ludlam <zappo@gnu.org>
-;; Version: 0.5.1
+;; Version: 0.6.1
;; Keywords: docs, maint, lisp
;; This file is part of GNU Emacs.
@@ -32,8 +32,9 @@
;; checks needed to make sure these styles are remembered.
;;
;; There are two ways to use checkdoc:
-;; 1) Periodically use `checkdoc'. `checkdoc-current-buffer' and
-;; `checkdoc-defun' to check your documentation.
+;; 1) Periodically use `checkdoc' or `checkdoc-current-buffer'.
+;; `checkdoc' is a more interactive version of
+;; `checkdoc-current-buffer'
;; 2) Use `checkdoc-minor-mode' to automatically check your
;; documentation whenever you evaluate Lisp code with C-M-x
;; or [menu-bar emacs-lisp eval-buffer]. Additional key-bindings
@@ -42,6 +43,34 @@
;; (add-hook 'emacs-lisp-mode-hook
;; '(lambda () (checkdoc-minor-mode 1)))
;;
+;; Using `checkdoc':
+;;
+;; The commands `checkdoc' and `checkdoc-ispell' are the top-level
+;; entry points to all of the different checks that are available. It
+;; breaks examination of your Lisp file into four sections (comments,
+;; documentation, messages, and spacing) and indicates its current
+;; state in a status buffer.
+;;
+;; The Comments check examines your headers, footers, and
+;; various tags (such as "Code:") to make sure that your code is ready
+;; for easy integration into existing systems.
+;;
+;; The Documentation check deals with documentation strings
+;; and their elements that help make Emacs easier to use.
+;;
+;; The Messages check ensures that the strings displayed in the
+;; minibuffer by some commands (such as `error' and `y-or-n-p')
+;; are consistent with the Emacs environment.
+;;
+;; The Spacing check cleans up white-space at the end of lines.
+;;
+;; The interface while working with documentation and messages is
+;; slightly different when being run in the interactive mode. The
+;; interface offers several options, including the ability to skip to
+;; the next error, or back up to previous errors. Auto-fixing is
+;; turned off at this stage, but you can use the `f' or `F' key to fix
+;; a given error (if the fix is available.)
+;;
;; Auto-fixing:
;;
;; There are four classifications of style errors in terms of how
@@ -57,7 +86,7 @@
;; variable `checkdoc-autofix-flag' controls how these types of errors
;; are fixed.
;;
-;; Spell checking doc strings:
+;; Spell checking text:
;;
;; The variable `checkdoc-spellcheck-documentation-flag' can be set
;; to customize how spell checking is to be done. Since spell
@@ -74,7 +103,7 @@
;; running. Use `ispell-kill-ispell' to make checkdoc restart it with
;; these words enabled.
;;
-;; Checking parameters
+;; Checking parameters:
;;
;; You might not always want a function to have its parameters listed
;; in order. When this is the case, put the following comment just in
@@ -87,9 +116,9 @@
;; skip looking for it by putting the following comment just in front
;; of the documentation string: "; checkdoc-params: (args go here)"
;;
-;; Checking message strings
+;; Checking message strings:
;;
-;; The text that follows the `error', and `y-or-n-p' commands is
+;; The text that follows the `error' and `y-or-n-p' commands is
;; also checked. The documentation for `error' clearly states some
;; simple style rules to follow which checkdoc will auto-fix for you.
;; `y-or-n-p' also states that it should end in a space. I added that
@@ -102,11 +131,36 @@
;; Return a string which is the error you wish to report. The cursor
;; position should be preserved.
;;
-;; This file requires lisp-mnt (Lisp maintenance routines) for the
-;; comment checkers.
+;; Error errors:
+;;
+;; Checkdoc does not always flag errors correctly. There are a
+;; couple ways you can coax your file into passing all of checkdoc's
+;; tests through buffer local variables.
+;;
+;; The variable `checkdoc-verb-check-experimental-flag' can be used
+;; to turn off the check for verb-voice in case you use words that are
+;; not semantically verbs, but are still in the incomplete list.
+;;
+;; The variable `checkdoc-symbol-words' can be a list of words that
+;; happen to also be symbols. This is not a problem for one-word
+;; symbols, but if you use a hyphenated word that is also a symbol,
+;; then you may need this.
+;;
+;; The symbol `checkdoc-force-docstrings-flag' can be set to nil if
+;; you have many undocumented functions you don't wish to document.
+;;
+;; See the above section "Checking Parameters" for details about
+;; parameter checking.
+;;
+;; Dependencies:
+;;
+;; This file requires lisp-mnt (Lisp maintenance routines) for the
+;; comment checkers.
+;;
+;; Requires custom for Emacs v20.
;;; TO DO:
-;; Hook into the byte compiler on a defun/defver level to generate
+;; Hook into the byte compiler on a defun/defvar level to generate
;; warnings in the byte-compiler's warning/error buffer.
;; Better ways to override more typical `eval' functions. Advice
;; might be good but hard to turn on/off as a minor mode.
@@ -117,7 +171,7 @@
;; not specifically docstring related. Would this even be useful?
;;; Code:
-(defvar checkdoc-version "0.5.1"
+(defvar checkdoc-version "0.6.1"
"Release version of checkdoc you are currently running.")
;; From custom web page for compatibility between versions of custom:
@@ -140,7 +194,7 @@
If this value is the symbol `query', then the user is queried before
any change is made. If the value is `automatic', then all changes are
made without asking unless the change is very-complex. If the value
-is `semiautomatic', or any other value, then simple fixes are made
+is `semiautomatic' or any other value, then simple fixes are made
without asking, and complex changes are made by asking the user first.
The value `never' is the same as nil, never ask or change anything."
:group 'checkdoc
@@ -159,12 +213,27 @@ interaction. See `checkdoc-autofix-flag' for auto-fixing details."
(defcustom checkdoc-force-docstrings-flag t
"*Non-nil means that all checkable definitions should have documentation.
Style guide dictates that interactive functions MUST have documentation,
-and that its good but not required practice to make non user visible items
+and that it's good but not required practice to make non user visible items
have doc strings."
:group 'checkdoc
:type 'boolean)
-(defcustom checkdoc-tripple-semi-comment-check-flag t
+(defcustom checkdoc-force-history-flag t
+ "*Non-nil means that files should have a History section or ChangeLog file.
+This helps document the evolution of, and recent changes to, the package."
+ :group 'checkdoc
+ :type 'boolean)
+
+(defcustom checkdoc-permit-comma-termination-flag nil
+ "*Non-nil means the first line of a docstring may end with a comma.
+Ordinarily, a full sentence is required. This may be misleading when
+there is a substantial caveat to the one-line description -- the comma
+should be used when the first part could stand alone as a sentence, but
+it indicates that a modifying clause follows."
+ :group 'checkdoc
+ :type 'boolean)
+
+(defcustom checkdoc-triple-semi-comment-check-flag t
"*Non-nil means to check for multiple adjacent occurrences of ;;; comments.
According to the style of Emacs code in the Lisp libraries, a block
comment can look like this:
@@ -178,14 +247,14 @@ is ignored regardless of its location in the code."
:type 'boolean)
(defcustom checkdoc-spellcheck-documentation-flag nil
- "*Non-nil means run Ispell on doc strings based on value.
+ "*Non-nil means run Ispell on text based on value.
This is automatically set to nil if Ispell does not exist on your
system. Possible values are:
nil - Don't spell-check during basic style checks.
defun - Spell-check when style checking a single defun
- buffer - Spell-check only when style checking the whole buffer
- interactive - Spell-check only during `checkdoc-interactive'
+ buffer - Spell-check when style checking the whole buffer
+ interactive - Spell-check during any interactive check.
t - Always spell-check"
:group 'checkdoc
:type '(choice (const nil)
@@ -195,7 +264,7 @@ system. Possible values are:
(const t)))
(defvar checkdoc-ispell-lisp-words
- '("alist" "etags" "iff" "keymap" "paren" "regexp" "sexp" "emacs" "xemacs")
+ '("alist" "emacs" "etags" "iff" "keymap" "paren" "regexp" "sexp" "xemacs")
"List of words that are correct when spell-checking Lisp documentation.")
(defcustom checkdoc-max-keyref-before-warn 10
@@ -244,6 +313,31 @@ variable `checkdoc-common-verbs-wrong-voice' if you wish to add your own."
:group 'checkdoc
:type 'boolean)
+(defvar checkdoc-generate-compile-warnings-flag nil
+ "Non-nil means generage warnings in a buffer for browsing.
+Do not set this by hand, use a function like `checkdoc-current-buffer'
+with a universal argument.")
+
+(defcustom checkdoc-symbol-words nil
+ "A list of symbols which also happen to make good words.
+These symbol-words are ignored when unquoted symbols are searched for.
+This should be set in an Emacs Lisp file's local variables."
+ :group 'checkdoc
+ :type '(repeat (symbol :tag "Word")))
+
+(defvar checkdoc-proper-noun-list
+ '("ispell" "xemacs" "emacs" "lisp")
+ "List of words (not capitalized) which should be capitalized.")
+
+(defvar checkdoc-proper-noun-regexp
+ (let ((expr "\\<\\(")
+ (l checkdoc-proper-noun-list))
+ (while l
+ (setq expr (concat expr (car l) (if (cdr l) "\\|" ""))
+ l (cdr l)))
+ (concat expr "\\)\\>"))
+ "Regular expression derived from `checkdoc-proper-noun-regexp'.")
+
(defvar checkdoc-common-verbs-regexp nil
"Regular expression derived from `checkdoc-common-verbs-regexp'.")
@@ -338,7 +432,7 @@ be re-created.")
(if checkdoc-syntax-table
nil
(setq checkdoc-syntax-table (copy-syntax-table emacs-lisp-mode-syntax-table))
- ;; When dealing with syntax in doc strings, make sure that - are encompased
+ ;; When dealing with syntax in doc strings, make sure that - are encompassed
;; in words so we can use cheap \\> to get the end of a symbol, not the
;; end of a word in a conglomerate.
(modify-syntax-entry ?- "w" checkdoc-syntax-table)
@@ -366,7 +460,13 @@ be re-created.")
(defalias 'checkdoc-call-eval-buffer 'eval-current-buffer)
)
-;; Emacs 20s have MULE characters which dont equate to numbers.
+;; Emacs 20 has this handy function.
+(if (not (fboundp 'princ-list))
+ (defun princ-list (&rest args)
+ "Call `princ' on ARGS."
+ (mapcar 'princ args)))
+
+;; Emacs 20s have MULE characters which don't equate to numbers.
(if (fboundp 'char=)
(defalias 'checkdoc-char= 'char=)
(defalias 'checkdoc-char= '=))
@@ -390,43 +490,109 @@ be re-created.")
;;; User level commands
;;
;;;###autoload
-(defun checkdoc-eval-current-buffer ()
- "Evaluate and check documentation for the current buffer.
-Evaluation is done first because good documentation for something that
-doesn't work is just not useful. Comments, doc strings, and rogue
-spacing are all verified."
+(defun checkdoc ()
+ "Interactivly check the entire buffer for style errors.
+The current status of the ckeck will be displayed in a buffer which
+the users will view as each check is completed."
(interactive)
- (checkdoc-call-eval-buffer nil)
- (checkdoc-current-buffer t))
+ (let ((status (list "Checking..." "-" "-" "-"))
+ (checkdoc-spellcheck-documentation-flag
+ (member checkdoc-spellcheck-documentation-flag
+ '(buffer interactive t)))
+ ;; if the user set autofix to never, then that breaks the
+ ;; obviously requested asking implied by using this function.
+ ;; Set it to paranoia level.
+ (checkdoc-autofix-flag (if (or (not checkdoc-autofix-flag)
+ (eq checkdoc-autofix-flag 'never))
+ 'query
+ checkdoc-autofix-flag))
+ tmp)
+ (checkdoc-display-status-buffer status)
+ ;; check the comments
+ (if (not buffer-file-name)
+ (setcar status "Not checked")
+ (if (checkdoc-file-comments-engine)
+ (setcar status "Errors")
+ (setcar status "Ok")))
+ (setcar (cdr status) "Checking...")
+ (checkdoc-display-status-buffer status)
+ ;; Check the documentation
+ (setq tmp (checkdoc-interactive nil t))
+ (if tmp
+ (setcar (cdr status) (format "%d Errors" (length tmp)))
+ (setcar (cdr status) "Ok"))
+ (setcar (cdr (cdr status)) "Checking...")
+ (checkdoc-display-status-buffer status)
+ ;; Check the message text
+ (if (setq tmp (checkdoc-message-interactive nil t))
+ (setcar (cdr (cdr status)) (format "%d Errors" (length tmp)))
+ (setcar (cdr (cdr status)) "Ok"))
+ (setcar (cdr (cdr (cdr status))) "Checking...")
+ (checkdoc-display-status-buffer status)
+ ;; Rogue spacing
+ (if (condition-case nil
+ (checkdoc-rogue-spaces nil t)
+ (error t))
+ (setcar (cdr (cdr (cdr status))) "Errors")
+ (setcar (cdr (cdr (cdr status))) "Ok"))
+ (checkdoc-display-status-buffer status)))
+
+(defun checkdoc-display-status-buffer (check)
+ "Display and update the status buffer for the current checkdoc mode.
+CHECK is a vector stating the current status of each test as an
+element is the status of that level of teset."
+ (with-output-to-temp-buffer " *Checkdoc Status*"
+ (princ-list
+ "Buffer comments and tags: " (nth 0 check) "\n"
+ "Documentation style: " (nth 1 check) "\n"
+ "Message/Query text style: " (nth 2 check) "\n"
+ "Unwanted Spaces: " (nth 3 check)
+ ))
+ (shrink-window-if-larger-than-buffer
+ (get-buffer-window " *Checkdoc Status*"))
+ (message nil)
+ (sit-for 0))
;;;###autoload
-(defun checkdoc-current-buffer (&optional take-notes)
- "Check current buffer for document, comment, error style, and rogue spaces.
-With a prefix argument (in Lisp, the argument TAKE-NOTES),
-store all errors found in a warnings buffer,
-otherwise stop after the first error."
+(defun checkdoc-interactive (&optional start-here showstatus)
+ "Interactively check the current buffer for doc string errors.
+Prefix argument START-HERE will start the checking from the current
+point, otherwise the check starts at the beginning of the current
+buffer. Allows navigation forward and backwards through document
+errors. Does not check for comment or space warnings.
+Optional argument SHOWSTATUS indicates that we should update the
+checkdoc status window instead of the usual behavior."
(interactive "P")
- (if (interactive-p) (message "Checking buffer for style..."))
- ;; Assign a flag to spellcheck flag
(let ((checkdoc-spellcheck-documentation-flag
- (memq checkdoc-spellcheck-documentation-flag '(buffer t))))
- ;; every test is responsible for returning the cursor.
- (or (and buffer-file-name ;; only check comments in a file
- (checkdoc-comments take-notes))
- (checkdoc take-notes)
- (checkdoc-message-text take-notes)
- (checkdoc-rogue-spaces take-notes)
- (not (interactive-p))
- (message "Checking buffer for style...Done."))))
+ (member checkdoc-spellcheck-documentation-flag
+ '(interactive t))))
+ (checkdoc-interactive-loop start-here showstatus 'checkdoc-next-error)))
;;;###autoload
-(defun checkdoc-interactive (&optional start-here)
- "Interactively check the current buffers for errors.
+(defun checkdoc-message-interactive (&optional start-here showstatus)
+ "Interactively check the current buffer for message string errors.
Prefix argument START-HERE will start the checking from the current
point, otherwise the check starts at the beginning of the current
buffer. Allows navigation forward and backwards through document
-errors. Does not check for comment or space warnings."
+errors. Does not check for comment or space warnings.
+Optional argument SHOWSTATUS indicates that we should update the
+checkdoc status window instead of the usual behavior."
(interactive "P")
+ (let ((checkdoc-spellcheck-documentation-flag
+ (member checkdoc-spellcheck-documentation-flag
+ '(interactive t))))
+ (checkdoc-interactive-loop start-here showstatus
+ 'checkdoc-next-message-error)))
+
+(defun checkdoc-interactive-loop (start-here showstatus findfunc)
+ "Interactivly loop over all errors that can be found by a given method.
+Searching starts at START-HERE. SHOWSTATUS expresses the verbosity
+of the search, and wether ending the search will auto-exit this function.
+FINDFUNC is a symbol representing a function that will position the
+cursor, and return error message text to present the the user. It is
+assumed that the cursor will stop just before a major sexp, which will
+be highlighted to present the user with feedback as to the offending
+style."
;; Determine where to start the test
(let* ((begin (prog1 (point)
(if (not start-here) (goto-char (point-min)))))
@@ -435,18 +601,24 @@ errors. Does not check for comment or space warnings."
(member checkdoc-spellcheck-documentation-flag
'(buffer interactive t)))
;; Fetch the error list
- (err-list (list (checkdoc-next-error))))
- (if (not (car err-list)) (setq err-list nil))
- ;; Include whatever function point is in for good measure.
- (beginning-of-defun)
- (while err-list
- (goto-char (cdr (car err-list)))
- ;; The cursor should be just in front of the offending doc string
- (let ((cdo (save-excursion
- (checkdoc-make-overlay (point)
- (progn (forward-sexp 1)
- (point)))))
- c)
+ (err-list (list (funcall findfunc nil)))
+ (cdo nil)
+ (returnme nil)
+ c)
+ (save-window-excursion
+ (if (not (car err-list)) (setq err-list nil))
+ ;; Include whatever function point is in for good measure.
+ (beginning-of-defun)
+ (while err-list
+ (goto-char (cdr (car err-list)))
+ ;; The cursor should be just in front of the offending doc string
+ (if (stringp (car (car err-list)))
+ (setq cdo (save-excursion (checkdoc-make-overlay
+ (point) (progn (forward-sexp 1)
+ (point)))))
+ (setq cdo (checkdoc-make-overlay
+ (checkdoc-error-start (car (car err-list)))
+ (checkdoc-error-end (car (car err-list))))))
(unwind-protect
(progn
(checkdoc-overlay-put cdo 'face 'highlight)
@@ -455,74 +627,240 @@ errors. Does not check for comment or space warnings."
(if (not (pos-visible-in-window-p
(save-excursion (forward-sexp 1) (point))
(selected-window)))
- (recenter))
- (message "%s(? e n p q)" (car (car err-list)))
- (setq c (checkdoc-read-event))
+ (if (looking-at "\"")
+ (let ((l (count-lines (point)
+ (save-excursion
+ (forward-sexp 1) (point)))))
+ (if (> l (window-height))
+ (recenter 1)
+ (recenter (/ (- (window-height) l) 2))))
+ (recenter)))
+ (message "%s (C-h,%se,n,p,q)" (checkdoc-error-text
+ (car (car err-list)))
+ (if (checkdoc-error-unfixable (car (car err-list)))
+ "" "f,"))
+ (save-excursion
+ (goto-char (checkdoc-error-start (car (car err-list))))
+ (if (not (pos-visible-in-window-p))
+ (recenter (- (window-height) 2)))
+ (setq c (checkdoc-read-event)))1
(if (not (integerp c)) (setq c ??))
- (cond ((or (checkdoc-char= c ?n) (checkdoc-char= c ?\ ))
- (let ((ne (checkdoc-next-error)))
- (if (not ne)
- (progn
- (message "No More Stylistic Errors.")
- (sit-for 2))
- (setq err-list (cons ne err-list)))))
- ((or (checkdoc-char= c ?p) (checkdoc-char= c ?\C-?))
- (if (/= (length err-list) 1)
- (progn
- (setq err-list (cdr err-list))
- ;; This will just re-ask fixup questions if
- ;; it was skipped the last time.
- (checkdoc-next-error))
- (message "No Previous Errors.")
- (sit-for 2)))
- ((checkdoc-char= c ?e)
- (message "Edit the docstring, and press C-M-c to exit.")
- (recursive-edit)
- (checkdoc-delete-overlay cdo)
- (setq err-list (cdr err-list)) ;back up the error found.
- (beginning-of-defun)
- (let ((ne (checkdoc-next-error)))
- (if (not ne)
- (progn
- (message "No More Stylistic Errors.")
- (sit-for 2))
- (setq err-list (cons ne err-list)))))
- ((checkdoc-char= c ?q)
- (setq err-list nil
- begin (point)))
- (t
- (message "[E]dit [SPC|n] next error [DEL|p] prev error\
- [q]uit [?] help: ")
- (sit-for 5))))
- (checkdoc-delete-overlay cdo))))
+ (cond
+ ;; Exit condition
+ ((checkdoc-char= c ?\C-g) (signal 'quit nil))
+ ;; Request an auto-fix
+ ((or (checkdoc-char= c ?y) (checkdoc-char= c ?f))
+ (checkdoc-delete-overlay cdo)
+ (setq cdo nil)
+ (goto-char (cdr (car err-list)))
+ ;; `automatic-then-never' tells the autofix function
+ ;; to only allow one fix to be automatic. The autofix
+ ;; function will than set the flag to 'never, allowing
+ ;; the checker to return a different error.
+ (let ((checkdoc-autofix-flag 'automatic-then-never)
+ (fixed nil))
+ (funcall findfunc t)
+ (setq fixed (not (eq checkdoc-autofix-flag
+ 'automatic-then-never)))
+ (if (not fixed)
+ (progn
+ (message "A Fix was not available.")
+ (sit-for 2))
+ (setq err-list (cdr err-list))))
+ (beginning-of-defun)
+ (let ((pe (car err-list))
+ (ne (funcall findfunc nil)))
+ (if ne
+ (setq err-list (cons ne err-list))
+ (cond ((not err-list)
+ (message "No More Stylistic Errors.")
+ (sit-for 2))
+ (t
+ (message
+ "No Additional style errors. Continuing...")
+ (sit-for 2))))))
+ ;; Move to the next error (if available)
+ ((or (checkdoc-char= c ?n) (checkdoc-char= c ?\ ))
+ (let ((ne (funcall findfunc nil)))
+ (if (not ne)
+ (if showstatus
+ (setq returnme err-list
+ err-list nil)
+ (if (not err-list)
+ (message "No More Stylistic Errors.")
+ (message "No Additional style errors. Continuing..."))
+ (sit-for 2))
+ (setq err-list (cons ne err-list)))))
+ ;; Go backwards in the list of errors
+ ((or (checkdoc-char= c ?p) (checkdoc-char= c ?\C-?))
+ (if (/= (length err-list) 1)
+ (progn
+ (setq err-list (cdr err-list))
+ (goto-char (cdr (car err-list)))
+ (beginning-of-defun))
+ (message "No Previous Errors.")
+ (sit-for 2)))
+ ;; Edit the buffer recursively.
+ ((checkdoc-char= c ?e)
+ (checkdoc-recursive-edit
+ (checkdoc-error-text (car (car err-list))))
+ (checkdoc-delete-overlay cdo)
+ (setq err-list (cdr err-list)) ;back up the error found.
+ (beginning-of-defun)
+ (let ((ne (funcall findfunc nil)))
+ (if (not ne)
+ (if showstatus
+ (setq returnme err-list
+ err-list nil)
+ (message "No More Stylistic Errors.")
+ (sit-for 2))
+ (setq err-list (cons ne err-list)))))
+ ;; Quit checkdoc
+ ((checkdoc-char= c ?q)
+ (setq returnme err-list
+ err-list nil
+ begin (point)))
+ ;; Goofy s tuff
+ (t
+ (if (get-buffer-window "*Checkdoc Help*")
+ (progn
+ (delete-window (get-buffer-window "*Checkdoc Help*"))
+ (kill-buffer "*Checkdoc Help*"))
+ (with-output-to-temp-buffer "*Checkdoc Help*"
+ (princ-list
+ "Checkdoc Keyboard Summary:\n"
+ (if (checkdoc-error-unfixable (car (car err-list)))
+ ""
+ (concat
+ "f, y - auto Fix this warning without asking (if\
+ available.)\n"
+ " Very complex operations will still query.\n")
+ )
+ "e - Enter recursive Edit. Press C-M-c to exit.\n"
+ "SPC, n - skip to the Next error.\n"
+ "DEL, p - skip to the Previous error.\n"
+ "q - Quit checkdoc.\n"
+ "C-h - Toggle this help buffer."))
+ (shrink-window-if-larger-than-buffer
+ (get-buffer-window "*Checkdoc Help*"))))))
+ (if cdo (checkdoc-delete-overlay cdo)))))
(goto-char begin)
- (message "Checkdoc: Done.")))
+ (if (get-buffer "*Checkdoc Help*") (kill-buffer "*Checkdoc Help*"))
+ (message "Checkdoc: Done.")
+ returnme))
-(defun checkdoc-next-error ()
+(defun checkdoc-next-error (enable-fix)
"Find and return the next checkdoc error list, or nil.
+Only documentation strings are checked.
Add error vector is of the form (WARNING . POSITION) where WARNING
is the warning text, and POSITION is the point in the buffer where the
error was found. We can use points and not markers because we promise
-not to edit the buffer before point without re-executing this check."
- (let ((msg nil) (p (point)))
- (condition-case nil
- (while (and (not msg) (checkdoc-next-docstring))
- (message "Searching for doc string error...%d%%"
- (/ (* 100 (point)) (point-max)))
- (if (setq msg (checkdoc-this-string-valid))
- (setq msg (cons msg (point)))))
- ;; Quit.. restore position, Other errors, leave alone
- (quit (goto-char p)))
- msg))
+not to edit the buffer before point without re-executing this check.
+Argument ENABLE-FIX will enable auto-fixing while looking for the next
+error. This argument assumes that the cursor is already positioned to
+perform the fix."
+ (if enable-fix
+ (checkdoc-this-string-valid)
+ (let ((msg nil) (p (point))
+ (checkdoc-autofix-flag nil))
+ (condition-case nil
+ (while (and (not msg) (checkdoc-next-docstring))
+ (message "Searching for doc string error...%d%%"
+ (/ (* 100 (point)) (point-max)))
+ (if (setq msg (checkdoc-this-string-valid))
+ (setq msg (cons msg (point)))))
+ ;; Quit.. restore position, Other errors, leave alone
+ (quit (goto-char p)))
+ msg)))
+
+(defun checkdoc-next-message-error (enable-fix)
+ "Find and return the next checkdoc mesasge related error list, or nil.
+Only text for error and `y-or-n-p' strings are checked. See
+`checkdoc-next-error' for details on the return value.
+Argument ENABLE-FIX turns on the auto-fix feature. This argument
+assumes that the cursor is already positioned to perform the fix."
+ (if enable-fix
+ (checkdoc-message-text-engine)
+ (let ((msg nil) (p (point)) (type nil)
+ (checkdoc-autofix-flag nil))
+ (condition-case nil
+ (while (and (not msg)
+ (setq type
+ (checkdoc-message-text-next-string (point-max))))
+ (message "Searching for message string error...%d%%"
+ (/ (* 100 (point)) (point-max)))
+ (if (setq msg (checkdoc-message-text-engine type))
+ (setq msg (cons msg (point)))))
+ ;; Quit.. restore position, Other errors, leave alone
+ (quit (goto-char p)))
+ msg)))
+
+(defun checkdoc-recursive-edit (msg)
+ "Enter recursive edit to permit a user to fix some error checkdoc has found.
+MSG is the error that was found, which is displayed in a help buffer."
+ (with-output-to-temp-buffer "*Checkdoc Help*"
+ (princ-list
+ "Error message:\n " msg
+ "\n\nEdit to fix this problem, and press C-M-c to continue."))
+ (shrink-window-if-larger-than-buffer
+ (get-buffer-window "*Checkdoc Help*"))
+ (message "When you're done editing press C-M-c to continue.")
+ (unwind-protect
+ (recursive-edit)
+ (if (get-buffer-window "*Checkdoc Help*")
+ (progn
+ (delete-window (get-buffer-window "*Checkdoc Help*"))
+ (kill-buffer "*Checkdoc Help*")))))
+
+;;;###autoload
+(defun checkdoc-eval-current-buffer ()
+ "Evaluate and check documentation for the current buffer.
+Evaluation is done first because good documentation for something that
+doesn't work is just not useful. Comments, doc strings, and rogue
+spacing are all verified."
+ (interactive)
+ (checkdoc-call-eval-buffer nil)
+ (checkdoc-current-buffer t))
;;;###autoload
-(defun checkdoc (&optional take-notes)
- "Use `checkdoc-continue' starting at the beginning of the current buffer.
+(defun checkdoc-current-buffer (&optional take-notes)
+ "Check current buffer for document, comment, error style, and rogue spaces.
+With a prefix argument (in Lisp, the argument TAKE-NOTES),
+store all errors found in a warnings buffer,
+otherwise stop after the first error."
+ (interactive "P")
+ (if (interactive-p) (message "Checking buffer for style..."))
+ ;; Assign a flag to spellcheck flag
+ (let ((checkdoc-spellcheck-documentation-flag
+ (memq checkdoc-spellcheck-documentation-flag '(buffer t)))
+ (checkdoc-autofix-flag (if take-notes 'never
+ checkdoc-autofix-flag))
+ (checkdoc-generate-compile-warnings-flag
+ (or take-notes checkdoc-generate-compile-warnings-flag)))
+ (if take-notes
+ (checkdoc-start-section "checkdoc-current-buffer"))
+ ;; every test is responsible for returning the cursor.
+ (or (and buffer-file-name ;; only check comments in a file
+ (checkdoc-comments))
+ (checkdoc-start)
+ (checkdoc-message-text)
+ (checkdoc-rogue-spaces)
+ (not (interactive-p))
+ (if take-notes (checkdoc-show-diagnostics))
+ (message "Checking buffer for style...Done."))))
+
+;;;###autoload
+(defun checkdoc-start (&optional take-notes)
+ "Start scanning the current buffer for documentation string style errors.
+Only documentation strings are checked.
+Use `checkdoc-continue' to continue checking if an error cannot be fixed.
Prefix argument TAKE-NOTES means to collect all the warning messages into
a separate buffer."
(interactive "P")
(let ((p (point)))
(goto-char (point-min))
+ (if (and take-notes (interactive-p))
+ (checkdoc-start-section "checkdoc-start"))
(checkdoc-continue take-notes)
;; Go back since we can't be here without success above.
(goto-char p)
@@ -530,37 +868,35 @@ a separate buffer."
;;;###autoload
(defun checkdoc-continue (&optional take-notes)
- "Find the next docstring in the current buffer which is stylisticly poor.
+ "Find the next doc string in the current buffer which has a style error.
Prefix argument TAKE-NOTES means to continue through the whole buffer and
-save warnings in a separate buffer."
+save warnings in a separate buffer. Second optional argument START-POINT
+is the starting location. If this is nil, `point-min' is used instead."
(interactive "P")
(let ((wrong nil) (msg nil) (errors nil)
;; Assign a flag to spellcheck flag
(checkdoc-spellcheck-documentation-flag
(member checkdoc-spellcheck-documentation-flag
- '(buffer t))))
+ '(buffer t)))
+ (checkdoc-autofix-flag (if take-notes 'never
+ checkdoc-autofix-flag))
+ (checkdoc-generate-compile-warnings-flag
+ (or take-notes checkdoc-generate-compile-warnings-flag)))
(save-excursion
;; If we are taking notes, encompass the whole buffer, otherwise
;; the user is navigating down through the buffer.
- (if take-notes (checkdoc-start-section "checkdoc"))
(while (and (not wrong) (checkdoc-next-docstring))
- ;; OK, lets look at the doc string.
+ ;; OK, let's look at the doc string.
(setq msg (checkdoc-this-string-valid))
- (if msg
- ;; Oops
- (if take-notes
- (progn
- (checkdoc-error (point) msg)
- (setq errors t))
- (setq wrong (point))))))
+ (if msg (setq wrong (point)))))
(if wrong
(progn
(goto-char wrong)
- (error msg)))
- (if (and take-notes errors)
- (checkdoc-show-diagnostics)
- (if (interactive-p)
- (message "No style warnings.")))))
+ (if (not take-notes)
+ (error (checkdoc-error-text msg)))))
+ (checkdoc-show-diagnostics)
+ (if (interactive-p)
+ (message "No style warnings."))))
(defun checkdoc-next-docstring ()
"Move to the next doc string after point, and return t.
@@ -586,36 +922,57 @@ if there is one."
(let* ((checkdoc-spellcheck-documentation-flag
(member checkdoc-spellcheck-documentation-flag
'(buffer t)))
- (e (checkdoc-file-comments-engine)))
- (if e
- (if take-notes
- (checkdoc-error nil e)
- (error e)))
- (if (and e take-notes)
- (checkdoc-show-diagnostics))
+ (checkdoc-autofix-flag (if take-notes 'never checkdoc-autofix-flag))
+ ;; This is just irritating when taking notes.
+ (checkdoc-triple-semi-comment-check-flag
+ (if take-notes nil checkdoc-triple-semi-comment-check-flag))
+ (e (checkdoc-file-comments-engine))
+ (checkdoc-generate-compile-warnings-flag
+ (or take-notes checkdoc-generate-compile-warnings-flag)))
+ (if e (error (checkdoc-error-text e)))
+ (checkdoc-show-diagnostics)
e))
;;;###autoload
-(defun checkdoc-rogue-spaces (&optional take-notes)
+(defun checkdoc-rogue-spaces (&optional take-notes interact)
"Find extra spaces at the end of lines in the current file.
Prefix argument TAKE-NOTES non-nil means to save warnings in a
separate buffer. Otherwise print a message. This returns the error
-if there is one."
+if there is one.
+Optional argument INTERACT permits more interactive fixing."
(interactive "P")
(if take-notes (checkdoc-start-section "checkdoc-rogue-spaces"))
- (let ((e (checkdoc-rogue-space-check-engine)))
- (if e
- (if take-notes
- (checkdoc-error nil e)
- (message e)))
- (if (and e take-notes)
- (checkdoc-show-diagnostics))
+ (let* ((checkdoc-autofix-flag (if take-notes 'never checkdoc-autofix-flag))
+ (e (checkdoc-rogue-space-check-engine nil nil interact))
+ (checkdoc-generate-compile-warnings-flag
+ (or take-notes checkdoc-generate-compile-warnings-flag)))
(if (not (interactive-p))
e
- (if e (message e) (message "Space Check: done.")))))
-
+ (if e
+ (message (checkdoc-error-text e))
+ (checkdoc-show-diagnostics)
+ (message "Space Check: done.")))))
;;;###autoload
+(defun checkdoc-message-text (&optional take-notes)
+ "Scan the buffer for occurrences of the error function, and verify text.
+Optional argument TAKE-NOTES causes all errors to be logged."
+ (interactive "P")
+ (if take-notes (checkdoc-start-section "checkdoc-message-text"))
+ (let* ((p (point)) e
+ (checkdoc-autofix-flag (if take-notes 'never checkdoc-autofix-flag))
+ (checkdoc-generate-compile-warnings-flag
+ (or take-notes checkdoc-generate-compile-warnings-flag)))
+ (setq e (checkdoc-message-text-search))
+ (if (not (interactive-p))
+ e
+ (if e
+ (error (checkdoc-error-text e))
+ (checkdoc-show-diagnostics)))
+ (goto-char p))
+ (if (interactive-p) (message "Checking interactive message text...done.")))
+
+;;;###autoload
(defun checkdoc-eval-defun ()
"Evaluate the current form with `eval-defun' and check its documentation.
Evaluation is done first so the form will be read before the
@@ -638,7 +995,7 @@ space at the end of each line."
(if (not (looking-at checkdoc-defun-regexp))
;; I found this more annoying than useful.
;;(if (not no-error)
- ;; (message "Cannot check this sexp's docstring."))
+ ;; (message "Cannot check this sexp's doc string."))
nil
;; search drops us after the identifier. The next sexp is either
;; the argument list or the value of the variable. skip it.
@@ -651,19 +1008,34 @@ space at the end of each line."
(beg (save-excursion (beginning-of-defun) (point)))
(end (save-excursion (end-of-defun) (point)))
(msg (checkdoc-this-string-valid)))
- (if msg (if no-error (message msg) (error msg))
+ (if msg (if no-error
+ (message (checkdoc-error-text msg))
+ (error (checkdoc-error-text msg)))
(setq msg (checkdoc-message-text-search beg end))
- (if msg (if no-error (message msg) (error msg))
+ (if msg (if no-error
+ (message (checkdoc-error-text msg))
+ (error (checkdoc-error-text msg)))
(setq msg (checkdoc-rogue-space-check-engine beg end))
- (if msg (if no-error (message msg) (error msg)))))
+ (if msg (if no-error
+ (message (checkdoc-error-text msg))
+ (error (checkdoc-error-text msg))))))
(if (interactive-p) (message "Checkdoc: done."))))))
;;; Ispell interface for forcing a spell check
;;
;;;###autoload
+(defun checkdoc-ispell (&optional take-notes)
+ "Check the style and spelling of everything interactively.
+Calls `checkdoc' with spell-checking turned on.
+Prefix argument TAKE-NOTES is the same as for `checkdoc'"
+ (interactive)
+ (let ((checkdoc-spellcheck-documentation-flag t))
+ (call-interactively 'checkdoc nil current-prefix-arg)))
+
+;;;###autoload
(defun checkdoc-ispell-current-buffer (&optional take-notes)
- "Check the style and spelling of the current buffer interactively.
+ "Check the style and spelling of the current buffer.
Calls `checkdoc-current-buffer' with spell-checking turned on.
Prefix argument TAKE-NOTES is the same as for `checkdoc-current-buffer'"
(interactive)
@@ -674,19 +1046,37 @@ Prefix argument TAKE-NOTES is the same as for `checkdoc-current-buffer'"
(defun checkdoc-ispell-interactive (&optional take-notes)
"Check the style and spelling of the current buffer interactively.
Calls `checkdoc-interactive' with spell-checking turned on.
-Prefix argument TAKE-NOTES is the same as for `checkdoc-interacitve'"
+Prefix argument TAKE-NOTES is the same as for `checkdoc-interactive'"
(interactive)
(let ((checkdoc-spellcheck-documentation-flag t))
(call-interactively 'checkdoc-interactive nil current-prefix-arg)))
;;;###autoload
-(defun checkdoc-ispell (&optional take-notes)
+(defun checkdoc-ispell-message-interactive (&optional take-notes)
+ "Check the style and spelling of message text interactively.
+Calls `checkdoc-message-interactive' with spell-checking turned on.
+Prefix argument TAKE-NOTES is the same as for `checkdoc-message-interactive'"
+ (interactive)
+ (let ((checkdoc-spellcheck-documentation-flag t))
+ (call-interactively 'checkdoc-message-interactive nil current-prefix-arg)))
+
+;;;###autoload
+(defun checkdoc-ispell-message-text (&optional take-notes)
+ "Check the style and spelling of message text interactively.
+Calls `checkdoc-message-text' with spell-checking turned on.
+Prefix argument TAKE-NOTES is the same as for `checkdoc-message-text'"
+ (interactive)
+ (let ((checkdoc-spellcheck-documentation-flag t))
+ (call-interactively 'checkdoc-message-text nil current-prefix-arg)))
+
+;;;###autoload
+(defun checkdoc-ispell-start (&optional take-notes)
"Check the style and spelling of the current buffer.
-Calls `checkdoc' with spell-checking turned on.
-Prefix argument TAKE-NOTES is the same as for `checkdoc'"
+Calls `checkdoc-start' with spell-checking turned on.
+Prefix argument TAKE-NOTES is the same as for `checkdoc-start'"
(interactive)
(let ((checkdoc-spellcheck-documentation-flag t))
- (call-interactively 'checkdoc nil current-prefix-arg)))
+ (call-interactively 'checkdoc-start nil current-prefix-arg)))
;;;###autoload
(defun checkdoc-ispell-continue (&optional take-notes)
@@ -715,6 +1105,45 @@ Prefix argument TAKE-NOTES is the same as for `checkdoc-defun'"
(let ((checkdoc-spellcheck-documentation-flag t))
(call-interactively 'checkdoc-defun nil current-prefix-arg)))
+;;; Error Management
+;;
+;; Errors returned from checkdoc functions can have various
+;; features and behaviors, so we need some ways of specifying
+;; them, and making them easier to use in the wacked-out interfaces
+;; people are requesting
+(defun checkdoc-create-error (text start end &optional unfixable)
+ "Used to create the return error text returned from all engines.
+TEXT is the descriptive text of the error. START and END define the region
+it is sensible to highlight when describing the problem.
+Optional argument UNFIXABLE means that the error has no auto-fix available.
+
+A list of the form (TEXT START END UNFIXABLE) is returned if we are not
+generating a buffered list of errors."
+ (if checkdoc-generate-compile-warnings-flag
+ (progn (checkdoc-error start text)
+ nil)
+ (list text start end unfixable)))
+
+(defun checkdoc-error-text (err)
+ "Return the text specified in the checkdoc ERR."
+ ;; string-p part is for backwards compatibility
+ (if (stringp err) err (car err)))
+
+(defun checkdoc-error-start (err)
+ "Return the start point specified in the checkdoc ERR."
+ ;; string-p part is for backwards compatibility
+ (if (stringp err) nil (nth 1 err)))
+
+(defun checkdoc-error-end (err)
+ "Return the end point specified in the checkdoc ERR."
+ ;; string-p part is for backwards compatibility
+ (if (stringp err) nil (nth 2 err)))
+
+(defun checkdoc-error-unfixable (err)
+ "Return the t if we cannot autofix the error specified in the checkdoc ERR."
+ ;; string-p part is for backwards compatibility
+ (if (stringp err) nil (nth 3 err)))
+
;;; Minor Mode specification
;;
(defvar checkdoc-minor-mode nil
@@ -728,21 +1157,24 @@ Prefix argument TAKE-NOTES is the same as for `checkdoc-defun'"
(pmap (make-sparse-keymap)))
;; Override some bindings
(define-key map "\C-\M-x" 'checkdoc-eval-defun)
+ (define-key map "\C-x`" 'checkdoc-continue)
(if (not (string-match "XEmacs" emacs-version))
(define-key map [menu-bar emacs-lisp eval-buffer]
'checkdoc-eval-current-buffer))
+ ;; Add some new bindings under C-c ?
(define-key pmap "x" 'checkdoc-defun)
(define-key pmap "X" 'checkdoc-ispell-defun)
(define-key pmap "`" 'checkdoc-continue)
(define-key pmap "~" 'checkdoc-ispell-continue)
+ (define-key pmap "s" 'checkdoc-start)
+ (define-key pmap "S" 'checkdoc-ispell-start)
(define-key pmap "d" 'checkdoc)
(define-key pmap "D" 'checkdoc-ispell)
- (define-key pmap "i" 'checkdoc-interactive)
- (define-key pmap "I" 'checkdoc-ispell-interactive)
(define-key pmap "b" 'checkdoc-current-buffer)
(define-key pmap "B" 'checkdoc-ispell-current-buffer)
(define-key pmap "e" 'checkdoc-eval-current-buffer)
(define-key pmap "m" 'checkdoc-message-text)
+ (define-key pmap "M" 'checkdoc-ispell-message-text)
(define-key pmap "c" 'checkdoc-comments)
(define-key pmap "C" 'checkdoc-ispell-comments)
(define-key pmap " " 'checkdoc-rogue-spaces)
@@ -758,23 +1190,31 @@ Prefix argument TAKE-NOTES is the same as for `checkdoc-defun'"
(easy-menu-define
checkdoc-minor-menu checkdoc-minor-keymap "Checkdoc Minor Mode Menu"
'("CheckDoc"
- ["First Style Error" checkdoc t]
- ["First Style or Spelling Error " checkdoc-ispell t]
- ["Next Style Error" checkdoc-continue t]
- ["Next Style or Spelling Error" checkdoc-ispell-continue t]
- ["Interactive Style Check" checkdoc-interactive t]
- ["Interactive Style and Spelling Check" checkdoc-ispell-interactive t]
- ["Check Defun" checkdoc-defun t]
- ["Check and Spell Defun" checkdoc-ispell-defun t]
- ["Check and Evaluate Defun" checkdoc-eval-defun t]
+ ["Interactive Buffer Style Check" checkdoc t]
+ ["Interactive Buffer Style and Spelling Check" checkdoc-ispell t]
["Check Buffer" checkdoc-current-buffer t]
["Check and Spell Buffer" checkdoc-ispell-current-buffer t]
- ["Check and Evaluate Buffer" checkdoc-eval-current-buffer t]
+ "---"
+ ["Interactive Style Check" checkdoc-interactive t]
+ ["Interactive Style and Spelling Check" checkdoc-ispell-interactive t]
+ ["Find First Style Error" checkdoc-start t]
+ ["Find First Style or Spelling Error" checkdoc-ispell-start t]
+ ["Next Style Error" checkdoc-continue t]
+ ["Next Style or Spelling Error" checkdoc-ispell-continue t]
+ ["Interactive Message Text Style Check" checkdoc-message-interactive t]
+ ["Interactive Message Text Style and Spelling Check"
+ checkdoc-ispell-message-interactive t]
+ ["Check Message Text" checkdoc-message-text t]
+ ["Check and Spell Message Text" checkdoc-ispell-message-text t]
["Check Comment Style" checkdoc-comments buffer-file-name]
["Check Comment Style and Spelling" checkdoc-ispell-comments
buffer-file-name]
- ["Check message text" checkdoc-message-text t]
["Check for Rogue Spaces" checkdoc-rogue-spaces t]
+ "---"
+ ["Check Defun" checkdoc-defun t]
+ ["Check and Spell Defun" checkdoc-ispell-defun t]
+ ["Check and Evaluate Defun" checkdoc-eval-defun t]
+ ["Check and Evaluate Buffer" checkdoc-eval-current-buffer t]
)))
;; XEmacs requires some weird stuff to add this menu in a minor mode.
;; What is it?
@@ -853,25 +1293,88 @@ See the style guide in the Emacs Lisp manual for more details."
(beginning-of-line)
(skip-chars-forward " \n\t"))
- (if (not (looking-at "[ \t\n]*\""))
- nil
- (let ((old-syntax-table (syntax-table)))
- (unwind-protect
- (progn
- (set-syntax-table checkdoc-syntax-table)
- (checkdoc-this-string-valid-engine))
- (set-syntax-table old-syntax-table)))))
-
-(defun checkdoc-this-string-valid-engine ()
- "Return a message string if the current doc string is invalid.
+ (let ((fp (checkdoc-defun-info))
+ (err nil))
+ (setq
+ err
+ ;; * Every command, function, or variable intended for users to know
+ ;; about should have a documentation string.
+ ;;
+ ;; * An internal variable or subroutine of a Lisp program might as well
+ ;; have a documentation string. In earlier Emacs versions, you could
+ ;; save space by using a comment instead of a documentation string,
+ ;; but that is no longer the case.
+ (if (and (not (nth 1 fp)) ; not a variable
+ (or (nth 2 fp) ; is interactive
+ checkdoc-force-docstrings-flag) ;or we always complain
+ (not (checkdoc-char= (following-char) ?\"))) ; no doc string
+ ;; Sometimes old code has comments where the documentation should
+ ;; be. Lets see if we can find the comment, and offer to turn it
+ ;; into documentation for them.
+ (let ((have-comment nil))
+ (condition-case nil
+ (progn
+ (forward-sexp -1)
+ (forward-sexp 1)
+ (skip-chars-forward "\n \t")
+ (setq have-comment (looking-at comment-start)))
+ (error nil))
+ (if have-comment
+ (if (or (eq checkdoc-autofix-flag
+ 'automatic-then-never)
+ (checkdoc-y-or-n-p
+ "Convert comment to documentation? "))
+ (save-excursion
+ ;; Our point is at the beginning of the comment!
+ ;; Insert a quote, then remove the comment chars.
+ (insert "\"")
+ (while (looking-at comment-start)
+ (while (looking-at comment-start)
+ (delete-char 1))
+ (if (looking-at "[ \t]+")
+ (delete-region (match-beginning 0) (match-end 0)))
+ (forward-line 1)
+ (beginning-of-line)
+ (skip-chars-forward " \t")
+ (if (looking-at comment-start)
+ (progn
+ (beginning-of-line)
+ (zap-to-char 1 ?\;))))
+ (beginning-of-line)
+ (forward-char -1)
+ (insert "\"")
+ (if (eq checkdoc-autofix-flag 'automatic-then-never)
+ (setq checkdoc-autofix-flag 'never)))
+ (checkdoc-create-error
+ "You should convert this comment to documentation"
+ (point) (save-excursion (end-of-line) (point))))
+ (checkdoc-create-error
+ (if (nth 2 fp)
+ "All interactive functions should have documentation"
+ "All variables and subroutines might as well have a \
+documentation string")
+ (point) (+ (point) 1) t)))))
+ (if (and (not err) (looking-at "\""))
+ (let ((old-syntax-table (syntax-table)))
+ (unwind-protect
+ (progn
+ (set-syntax-table checkdoc-syntax-table)
+ (checkdoc-this-string-valid-engine fp))
+ (set-syntax-table old-syntax-table)))
+ err)))
+
+(defun checkdoc-this-string-valid-engine (fp)
+ "Return an error list or string if the current doc string is invalid.
Depends on `checkdoc-this-string-valid' to reset the syntax table so that
-regexp short cuts work."
+regexp short cuts work. FP is the function defun information."
(let ((case-fold-search nil)
;; Use a marker so if an early check modifies the text,
;; we won't accidentally loose our place. This could cause
;; end-of doc string whitespace to also delete the " char.
- (e (save-excursion (forward-sexp 1) (point-marker)))
- (fp (checkdoc-defun-info)))
+ (s (point))
+ (e (if (looking-at "\"")
+ (save-excursion (forward-sexp 1) (point-marker))
+ (point))))
(or
;; * *Do not* indent subsequent lines of a documentation string so that
;; the text is lined up in the source code with the text of the first
@@ -888,7 +1391,10 @@ regexp short cuts work."
"Remove this whitespace? "
"")
nil
- "Second line should not have indentation")))
+ (checkdoc-create-error
+ "Second line should not have indentation"
+ (match-beginning 1)
+ (match-end 1)))))
;; * Do not start or end a documentation string with whitespace.
(let (start end)
(if (or (if (looking-at "\"\\([ \t\n]+\\)")
@@ -903,22 +1409,9 @@ regexp short cuts work."
(if (checkdoc-autofix-ask-replace
start end "Remove this whitespace? " "")
nil
- "Documentation strings should not start or end with whitespace")))
- ;; * Every command, function, or variable intended for users to know
- ;; about should have a documentation string.
- ;;
- ;; * An internal variable or subroutine of a Lisp program might as well
- ;; have a documentation string. In earlier Emacs versions, you could
- ;; save space by using a comment instead of a documentation string,
- ;; but that is no longer the case.
- (if (and (not (nth 1 fp)) ; not a variable
- (or (nth 2 fp) ; is interactive
- checkdoc-force-docstrings-flag) ;or we always complain
- (not (checkdoc-char= (following-char) ?\"))) ; no doc string
- (if (nth 2 fp)
- "All interactive functions should have documentation"
- "All variables and subroutines might as well have a \
-documentation string"))
+ (checkdoc-create-error
+ "Documentation strings should not start or end with whitespace"
+ start end))))
;; * The first line of the documentation string should consist of one
;; or two complete sentences that stand on their own as a summary.
;; `M-x apropos' displays just the first line, and if it doesn't
@@ -942,12 +1435,16 @@ documentation string"))
(point) (1+ (point)) "Add period to sentence? "
".\"" t)
nil
- "First sentence should end with punctuation.")))
+ (checkdoc-create-error
+ "First sentence should end with punctuation"
+ (point) (1+ (point))))))
((looking-at "[\\!;:.)]")
;; These are ok
nil)
+ ((and checkdoc-permit-comma-termination-flag (looking-at ","))
+ nil)
(t
- ;; If it is not a complete sentence, lets see if we can
+ ;; If it is not a complete sentence, let's see if we can
;; predict a clever way to make it one.
(let ((msg "First line is not a complete sentence")
(e (point)))
@@ -973,16 +1470,17 @@ may require more formatting")
;; with a space.
(delete-char 1) (insert " ")
(setq msg nil))))
- ;; Lets see if there is enough room to draw the next
+ ;; Let's see if there is enough room to draw the next
;; line's sentence up here. I often get hit w/
;; auto-fill moving my words around.
(let ((numc (progn (end-of-line) (- 80 (current-column))))
(p (point)))
(forward-line 1)
(beginning-of-line)
- (if (and (re-search-forward "[.!:\"][ \n\"]" (save-excursion
- (end-of-line)
- (point))
+ (if (and (re-search-forward "[.!:\"]\\([ \t\n]+\\|\"\\)"
+ (save-excursion
+ (end-of-line)
+ (point))
t)
(< (current-column) numc))
(if (checkdoc-autofix-ask-replace
@@ -991,10 +1489,16 @@ may require more formatting")
" " t)
(progn
;; They said yes. We have more fill work to do...
- (delete-char 1)
+ (goto-char (match-beginning 1))
+ (delete-region (point) (match-end 1))
(insert "\n")
(setq msg nil))))))
- msg))))
+ (if msg
+ (checkdoc-create-error msg s (save-excursion
+ (goto-char s)
+ (end-of-line)
+ (point)))
+ nil) ))))
;; Continuation of above. Make sure our sentence is capitalized.
(save-excursion
(skip-chars-forward "\"\\*")
@@ -1004,54 +1508,10 @@ may require more formatting")
"Capitalize your sentence? " (upcase (match-string 0))
t)
nil
- "First line should be capitalized")
+ (checkdoc-create-error
+ "First line should be capitalized"
+ (match-beginning 0) (match-end 0)))
nil))
- ;; * For consistency, phrase the verb in the first sentence of a
- ;; documentation string as an infinitive with "to" omitted. For
- ;; instance, use "Return the cons of A and B." in preference to
- ;; "Returns the cons of A and B." Usually it looks good to do
- ;; likewise for the rest of the first paragraph. Subsequent
- ;; paragraphs usually look better if they have proper subjects.
- ;;
- ;; For our purposes, just check to first sentence. A more robust
- ;; grammar checker would be preferred for the rest of the
- ;; documentation string.
- (and checkdoc-verb-check-experimental-flag
- (save-excursion
- ;; Maybe rebuild the monster-regex
- (checkdoc-create-common-verbs-regexp)
- (let ((lim (save-excursion
- (end-of-line)
- ;; check string-continuation
- (if (checkdoc-char= (preceding-char) ?\\)
- (progn (forward-line 1)
- (end-of-line)))
- (point)))
- (rs nil) replace original (case-fold-search t))
- (while (and (not rs)
- (re-search-forward checkdoc-common-verbs-regexp
- lim t))
- (setq original (buffer-substring-no-properties
- (match-beginning 1) (match-end 1))
- rs (assoc (downcase original)
- checkdoc-common-verbs-wrong-voice))
- (if (not rs) (error "Verb voice alist corrupted"))
- (setq replace (let ((case-fold-search nil))
- (save-match-data
- (if (string-match "^[A-Z]" original)
- (capitalize (cdr rs))
- (cdr rs)))))
- (if (checkdoc-autofix-ask-replace
- (match-beginning 1) (match-end 1)
- (format "Wrong voice for verb `%s'. Replace with `%s'? "
- original replace)
- replace t)
- (setq rs nil)))
- (if rs
- ;; there was a match, but no replace
- (format
- "Incorrect voice in sentence. Use `%s' instead of `%s'"
- replace original)))))
;; * Don't write key sequences directly in documentation strings.
;; Instead, use the `\\[...]' construct to stand for them.
(save-excursion
@@ -1062,10 +1522,12 @@ mouse-[0-3]\\)\\)\\>"))
(while (and (not f) (setq m (re-search-forward re e t)))
(setq f (not (checkdoc-in-sample-code-p start e))))
(if m
- (concat
- "Keycode " (match-string 1)
- " embedded in doc string. Use \\\\<keymap> & \\\\[function] "
- "instead"))))
+ (checkdoc-create-error
+ (concat
+ "Keycode " (match-string 1)
+ " embedded in doc string. Use \\\\<keymap> & \\\\[function] "
+ "instead")
+ (match-beginning 1) (match-end 1) t))))
;; It is not practical to use `\\[...]' very many times, because
;; display of the documentation string will become slow. So use this
;; to describe the most important commands in your major mode, and
@@ -1073,26 +1535,31 @@ mouse-[0-3]\\)\\)\\>"))
(save-excursion
(if (re-search-forward "\\\\\\\\\\[\\w+" e t
(1+ checkdoc-max-keyref-before-warn))
- "Too many occurrences of \\[function]. Use \\{keymap} instead"))
+ (checkdoc-create-error
+ "Too many occurrences of \\[function]. Use \\{keymap} instead"
+ s (marker-position e))))
;; Ambiguous quoted symbol. When a symbol is both bound and fbound,
;; and is referred to in documentation, it should be prefixed with
;; something to disambiguate it. This check must be before the
;; 80 column check because it will probably break that.
(save-excursion
(let ((case-fold-search t)
- (ret nil))
- (while (and
- (re-search-forward
- "\\(\\<\\(variable\\|option\\|function\\|command\\|symbol\\)\
-\\s-+\\)?`\\(\\sw\\(\\sw\\|\\s_\\)+\\)'" e t)
- (not ret))
- (let ((sym (intern-soft (match-string 3)))
- (mb (match-beginning 3)))
- (if (and sym (boundp sym) (fboundp sym) (not (match-string 1)))
+ (ret nil) mb me)
+ (while (and (re-search-forward "`\\(\\sw\\(\\sw\\|\\s_\\)+\\)'" e t)
+ (not ret))
+ (let* ((ms1 (match-string 1))
+ (sym (intern-soft ms1)))
+ (setq mb (match-beginning 1)
+ me (match-end 1))
+ (if (and sym (boundp sym) (fboundp sym)
+ (save-excursion
+ (goto-char mb)
+ (forward-word -1)
+ (not (looking-at
+ "variable\\|option\\|function\\|command\\|symbol"))))
(if (checkdoc-autofix-ask-replace
- mb (match-end 3) "Prefix this ambiguous symbol? "
- (match-string 3) t)
- ;; We didn't actuall replace anything. Here we find
+ mb me "Prefix this ambiguous symbol? " ms1 t)
+ ;; We didn't actually replace anything. Here we find
;; out what special word form they wish to use as
;; a prefix.
(let ((disambiguate
@@ -1105,74 +1572,41 @@ mouse-[0-3]\\)\\)\\>"))
(insert disambiguate " ")
(forward-word 1))
(setq ret
- (format "Disambiguate %s by preceeding w/ \
-function,command,variable,option or symbol." (match-string 3)))))))
- ret))
+ (format "Disambiguate %s by preceding w/ \
+function,command,variable,option or symbol." ms1))))))
+ (if ret
+ (checkdoc-create-error ret mb me)
+ nil)))
;; * Format the documentation string so that it fits in an
;; Emacs window on an 80-column screen. It is a good idea
;; for most lines to be no wider than 60 characters. The
;; first line can be wider if necessary to fit the
;; information that ought to be there.
(save-excursion
- (let ((start (point)))
+ (let ((start (point))
+ (eol nil))
(while (and (< (point) e)
- (or (progn (end-of-line) (< (current-column) 80))
+ (or (progn (end-of-line) (setq eol (point))
+ (< (current-column) 80))
(progn (beginning-of-line)
(re-search-forward "\\\\\\\\[[<{]"
- (save-excursion
- (end-of-line)
- (point)) t))
+ eol t))
(checkdoc-in-sample-code-p start e)))
(forward-line 1))
(end-of-line)
(if (and (< (point) e) (> (current-column) 80))
- "Some lines are over 80 columns wide")))
- ;;* When a documentation string refers to a Lisp symbol, write it as
- ;; it would be printed (which usually means in lower case), with
- ;; single-quotes around it. For example: `lambda'. There are two
- ;; exceptions: write t and nil without single-quotes. (In this
- ;; manual, we normally do use single-quotes for those symbols.)
- (save-excursion
- (let ((found nil) (start (point)) (msg nil) (ms nil))
- (while (and (not msg)
- (re-search-forward
- "[^([`':]\\(\\w\+[:-]\\(\\w\\|\\s_\\)+\\)[^]']"
- e t))
- (setq ms (match-string 1))
- (save-match-data
- ;; A . is a \s_ char, so we must remove periods from
- ;; sentences more carefully.
- (if (string-match "\\.$" ms)
- (setq ms (substring ms 0 (1- (length ms))))))
- (if (and (not (checkdoc-in-sample-code-p start e))
- (setq found (intern-soft ms))
- (or (boundp found) (fboundp found)))
- (progn
- (setq msg (format "Add quotes around Lisp symbol `%s'? "
- ms))
- (if (checkdoc-autofix-ask-replace
- (match-beginning 1) (+ (match-beginning 1)
- (length ms))
- msg (concat "`" ms "'") t)
- (setq msg nil)))))
- msg))
- ;; t and nil case
- (save-excursion
- (if (re-search-forward "\\(`\\(t\\|nil\\)'\\)" e t)
- (if (checkdoc-autofix-ask-replace
- (match-beginning 1) (match-end 1)
- (format "%s should not appear in quotes. Remove? "
- (match-string 2))
- (match-string 2) t)
- nil
- "Symbols t and nil should not appear in `quotes'")))
+ (checkdoc-create-error
+ "Some lines are over 80 columns wide"
+ s (save-excursion (goto-char s) (end-of-line) (point)) ))))
;; Here we deviate to tests based on a variable or function.
+ ;; We must do this before checking for symbols in quotes because there
+ ;; is a chance that just such a symbol might really be an argument.
(cond ((eq (nth 1 fp) t)
;; This is if we are in a variable
(or
;; * The documentation string for a variable that is a
- ;; yes-or-no flag should start with words such as "Non-nil
- ;; means...", to make it clear that all non-`nil' values are
+ ;; yes-or-no flag should start with words such as Non-nil
+ ;; means..., to make it clear that all non-`nil' values are
;; equivalent and indicate explicitly what `nil' and non-`nil'
;; mean.
;; * If a user option variable records a true-or-false
@@ -1181,11 +1615,24 @@ function,command,variable,option or symbol." (match-string 3)))))))
;; If the variable has -flag in the name, make sure
(if (and (string-match "-flag$" (car fp))
(not (looking-at "\"\\*?Non-nil\\s-+means\\s-+")))
- "Flag variable doc strings should start: Non-nil means")
+ (checkdoc-create-error
+ "Flag variable doc strings should start: Non-nil means"
+ s (marker-position e) t))
;; If the doc string starts with "Non-nil means"
(if (and (looking-at "\"\\*?Non-nil\\s-+means\\s-+")
(not (string-match "-flag$" (car fp))))
- "Flag variables should end in `-flag'")
+ (if (checkdoc-y-or-n-p
+ (format
+ "Rename to %s and Query-Replace all occurances? "
+ (concat (car fp) "-flag")))
+ (progn
+ (beginning-of-defun)
+ (query-replace-regexp
+ (concat "\\<" (regexp-quote (car fp)) "\\>")
+ (concat (car fp) "-flag")))
+ (checkdoc-create-error
+ "Flag variables should end in `-flag'" s
+ (marker-position e))))
;; Done with variables
))
(t
@@ -1221,7 +1668,7 @@ function,command,variable,option or symbol." (match-string 3)))))))
e t)))
(if (not found)
(let ((case-fold-search t))
- ;; If the symbol was not found, lets see if we
+ ;; If the symbol was not found, let's see if we
;; can find it with a different capitalization
;; and see if the user wants to capitalize it.
(if (save-excursion
@@ -1243,9 +1690,10 @@ function,command,variable,option or symbol." (match-string 3)))))))
(if (not found)
;; It wasn't found at all! Offer to attach this new symbol
;; to the end of the documentation string.
- (if (y-or-n-p
- (format "Add %s documentation to end of doc string?"
- (upcase (car args))))
+ (if (checkdoc-y-or-n-p
+ (format
+ "Add %s documentation to end of doc string? "
+ (upcase (car args))))
;; Now do some magic and invent a doc string.
(save-excursion
(goto-char e) (forward-char -1)
@@ -1258,17 +1706,124 @@ function,command,variable,option or symbol." (match-string 3)))))))
(looking-at "[.?!]")))
(insert "."))
nil)
- (format
- "Argument `%s' should appear as `%s' in the doc string"
- (car args) (upcase (car args))))
+ (checkdoc-create-error
+ (format
+ "Argument `%s' should appear (as `%s') in the doc string"
+ (car args) (upcase (car args)))
+ s (marker-position e)))
(if (or (and order (eq order 'yes))
(and (not order) checkdoc-arguments-in-order-flag))
(if (< found last-pos)
- "Arguments occur in the doc string out of order"))))
+ (checkdoc-create-error
+ "Arguments occur in the doc string out of order"
+ s (marker-position e) t)))))
+ ;; * For consistency, phrase the verb in the first sentence of a
+ ;; documentation string for functions as an infinitive with
+ ;; "to" omitted. For instance, use `Return the cons of A and
+ ;; B.' in preference to `Returns the cons of A and B.'
+ ;; Usually it looks good to do likewise for the rest of the
+ ;; first paragraph. Subsequent paragraphs usually look better
+ ;; if they have proper subjects.
+ ;;
+ ;; This is the least important of the above tests. Make sure
+ ;; it occurs last.
+ (and checkdoc-verb-check-experimental-flag
+ (save-excursion
+ ;; Maybe rebuild the monster-regex
+ (checkdoc-create-common-verbs-regexp)
+ (let ((lim (save-excursion
+ (end-of-line)
+ ;; check string-continuation
+ (if (checkdoc-char= (preceding-char) ?\\)
+ (progn (forward-line 1)
+ (end-of-line)))
+ (point)))
+ (rs nil) replace original (case-fold-search t))
+ (while (and (not rs)
+ (re-search-forward
+ checkdoc-common-verbs-regexp
+ lim t))
+ (setq original (buffer-substring-no-properties
+ (match-beginning 1) (match-end 1))
+ rs (assoc (downcase original)
+ checkdoc-common-verbs-wrong-voice))
+ (if (not rs) (error "Verb voice alist corrupted"))
+ (setq replace (let ((case-fold-search nil))
+ (save-match-data
+ (if (string-match "^[A-Z]" original)
+ (capitalize (cdr rs))
+ (cdr rs)))))
+ (if (checkdoc-autofix-ask-replace
+ (match-beginning 1) (match-end 1)
+ (format "Use the infinitive for `%s'. \
+Replace with `%s'? " original replace)
+ replace t)
+ (setq rs nil)))
+ (if rs
+ ;; there was a match, but no replace
+ (checkdoc-create-error
+ (format
+ "Infinitive `%s' should be replaced with `%s'"
+ original replace)
+ (match-beginning 1) (match-end 1))))))
;; Done with functions
)))
- ;; Make sure the doc string has correctly spelled english words
- ;; in it. This functions is extracted due to its complexity,
+ ;;* When a documentation string refers to a Lisp symbol, write it as
+ ;; it would be printed (which usually means in lower case), with
+ ;; single-quotes around it. For example: `lambda'. There are two
+ ;; exceptions: write t and nil without single-quotes. (In this
+ ;; manual, we normally do use single-quotes for those symbols.)
+ (save-excursion
+ (let ((found nil) (start (point)) (msg nil) (ms nil))
+ (while (and (not msg)
+ (re-search-forward
+ "[^([`':a-zA-Z]\\(\\w+[:-]\\(\\w\\|\\s_\\)+\\)[^]']"
+ e t))
+ (setq ms (match-string 1))
+ (save-match-data
+ ;; A . is a \s_ char, so we must remove periods from
+ ;; sentences more carefully.
+ (if (string-match "\\.$" ms)
+ (setq ms (substring ms 0 (1- (length ms))))))
+ (if (and (not (checkdoc-in-sample-code-p start e))
+ (not (checkdoc-in-example-string-p start e))
+ (not (member ms checkdoc-symbol-words))
+ (setq found (intern-soft ms))
+ (or (boundp found) (fboundp found)))
+ (progn
+ (setq msg (format "Add quotes around Lisp symbol `%s'? "
+ ms))
+ (if (checkdoc-autofix-ask-replace
+ (match-beginning 1) (+ (match-beginning 1)
+ (length ms))
+ msg (concat "`" ms "'") t)
+ (setq msg nil)
+ (setq msg
+ (format "Lisp symbol `%s' should appear in quotes"
+ ms))))))
+ (if msg
+ (checkdoc-create-error msg (match-beginning 1)
+ (+ (match-beginning 1)
+ (length ms)))
+ nil)))
+ ;; t and nil case
+ (save-excursion
+ (if (re-search-forward "\\(`\\(t\\|nil\\)'\\)" e t)
+ (if (checkdoc-autofix-ask-replace
+ (match-beginning 1) (match-end 1)
+ (format "%s should not appear in quotes. Remove? "
+ (match-string 2))
+ (match-string 2) t)
+ nil
+ (checkdoc-create-error
+ "Symbols t and nil should not appear in `quotes'"
+ (match-beginning 1) (match-end 1)))))
+ ;; Here is some basic sentence formatting
+ (checkdoc-sentencespace-region-engine (point) e)
+ ;; Here are common proper nouns that should always appear capitalized.
+ (checkdoc-proper-noun-region-engine (point) e)
+ ;; Make sure the doc string has correctly spelled English words
+ ;; in it. This function is extracted due to its complexity,
;; and reliance on the Ispell program.
(checkdoc-ispell-docstring-engine e)
;; User supplied checks
@@ -1337,14 +1892,20 @@ from the comment."
t)
(let ((sl nil))
(goto-char (match-end 0))
- (setq lst (read (current-buffer)))
+ (condition-case nil
+ (setq lst (read (current-buffer)))
+ (error (setq lst nil))) ; error in text
+ (if (not (listp lst)) ; not a list of args
+ (setq lst (list lst)))
+ (if (and lst (not (symbolp (car lst)))) ;weird arg
+ (setq lst nil))
(while lst
(setq sl (cons (symbol-name (car lst)) sl)
lst (cdr lst)))
(setq sl1 (append sl1 sl))))
sl1)
ret)))
- ;; Read the list of paramters, but do not put the symbols in
+ ;; Read the list of parameters, but do not put the symbols in
;; the standard obarray.
(setq lst (read bss)))
;; This is because read will intern nil if it doesn't into the
@@ -1359,9 +1920,9 @@ from the comment."
(defun checkdoc-in-sample-code-p (start limit)
"Return non-nil if the current point is in a code fragment.
A code fragment is identified by an open parenthesis followed by a
-symbol which is a valid function, or a parenthesis that is quoted with the '
-character. Only the region from START to LIMIT is is allowed while
-searching for the bounding parenthesis."
+symbol which is a valid function or a word in all CAPS, or a parenthesis
+that is quoted with the ' character. Only the region from START to LIMIT
+is is allowed while searching for the bounding parenthesis."
(save-match-data
(save-restriction
(narrow-to-region start limit)
@@ -1377,8 +1938,107 @@ searching for the bounding parenthesis."
;; the name, then it is probably supposed to be bound
;; but isn't yet.
(or (fboundp (intern-soft ms))
+ (let ((case-fold-search nil))
+ (string-match "^[A-Z-]+$" ms))
(string-match "\\w[-:_]+\\w" ms))))))))))
+(defun checkdoc-in-example-string-p (start limit)
+ "Return non-nil if the current point is in an \"example string\".
+This string is identified by the characters \\\" surrounding the text.
+The text checked is between START and LIMIT."
+ (save-match-data
+ (save-excursion
+ (let ((p (point))
+ (c 0))
+ (goto-char start)
+ (while (and (< (point) p) (re-search-forward "\\\\\"" limit t))
+ (setq c (1+ c)))
+ (and (< 0 c) (= (% c 2) 0))))))
+
+(defun checkdoc-proper-noun-region-engine (begin end)
+ "Check all text between BEGIN and END for lower case proper nouns.
+These are Emacs centric proper nouns which should be capitalized for
+consistency. Return an error list if any are not fixed, but
+internally skip over no answers.
+If the offending word is in a piece of quoted text, then it is skipped."
+ (save-excursion
+ (let ((case-fold-search nil)
+ (errtxt nil) bb be
+ (old-syntax-table (syntax-table)))
+ (unwind-protect
+ (progn
+ (set-syntax-table checkdoc-syntax-table)
+ (goto-char begin)
+ (while (re-search-forward checkdoc-proper-noun-regexp end t)
+ (let ((text (match-string 1))
+ (b (match-beginning 1))
+ (e (match-end 1)))
+ (if (and (not (save-excursion
+ (goto-char b)
+ (forward-char -1)
+ (looking-at "`\\|\"\\|\\.\\|\\\\")))
+ (not (checkdoc-in-example-string-p begin end)))
+ (if (checkdoc-autofix-ask-replace
+ b e (format "Text %s should be capitalized. Fix? "
+ text)
+ (capitalize text) t)
+ nil
+ (if errtxt
+ ;; If there is already an error, then generate
+ ;; the warning output if applicable
+ (if checkdoc-generate-compile-warnings-flag
+ (checkdoc-create-error
+ (format
+ "Name %s should appear capitalized as %s"
+ text (capitalize text))
+ b e))
+ (setq errtxt
+ (format
+ "Name %s should appear capitalized as %s"
+ text (capitalize text))
+ bb b be e)))))))
+ (set-syntax-table old-syntax-table))
+ (if errtxt (checkdoc-create-error errtxt bb be)))))
+
+(defun checkdoc-sentencespace-region-engine (begin end)
+ "Make sure all sentences have double spaces between BEGIN and END."
+ (save-excursion
+ (let ((case-fold-search nil)
+ (errtxt nil) bb be
+ (old-syntax-table (syntax-table)))
+ (unwind-protect
+ (progn
+ (set-syntax-table checkdoc-syntax-table)
+ (goto-char begin)
+ (while (re-search-forward "[^.0-9]\\(\\. \\)[^ \n]" end t)
+ (let ((b (match-beginning 1))
+ (e (match-end 1)))
+ (if (and (not (checkdoc-in-sample-code-p begin end))
+ (not (checkdoc-in-example-string-p begin end))
+ (not (save-excursion
+ (goto-char (match-beginning 1))
+ (forward-sexp -1)
+ ;; piece of an abbreviation
+ (looking-at "\\([a-z]\\|[ie]\\.?g\\)\\.")
+ )))
+ (if (checkdoc-autofix-ask-replace
+ b e "There should be two spaces after a period. Fix? "
+ ". ")
+ nil
+ (if errtxt
+ ;; If there is already an error, then generate
+ ;; the warning output if applicable
+ (if checkdoc-generate-compile-warnings-flag
+ (checkdoc-create-error
+ "There should be two spaces after a period"
+ b e))
+ (setq errtxt
+ "There should be two spaces after a period"
+ bb b be e)))))))
+ (set-syntax-table old-syntax-table))
+ (if errtxt (checkdoc-create-error errtxt bb be)))))
+
+
;;; Ispell engine
;;
(eval-when-compile (require 'ispell))
@@ -1407,7 +2067,11 @@ nil."
"Run the Ispell tools on the doc string between point and END.
Since Ispell isn't Lisp-smart, we must pre-process the doc string
before using the Ispell engine on it."
- (if (not checkdoc-spellcheck-documentation-flag)
+ (if (or (not checkdoc-spellcheck-documentation-flag)
+ ;; If the user wants no questions or fixing, then we must
+ ;; disable spell checking as not useful.
+ (not checkdoc-autofix-flag)
+ (eq checkdoc-autofix-flag 'never))
nil
(checkdoc-ispell-init)
(save-excursion
@@ -1423,12 +2087,12 @@ before using the Ispell engine on it."
(point)))
sym (intern-soft word))
(if (and sym (or (boundp sym) (fboundp sym)))
- ;; This is probably repetative in most cases, but not always.
+ ;; This is probably repetitive in most cases, but not always.
nil
;; Find out how we spell-check this word.
(if (or
;; All caps w/ option th, or s tacked on the end
- ;; for pluralization or nuberthness.
+ ;; for pluralization or numberthness.
(string-match "^[A-Z][A-Z]+\\(s\\|th\\)?$" word)
(looking-at "}") ; a keymap expression
)
@@ -1448,41 +2112,51 @@ before using the Ispell engine on it."
;;; Rogue space checking engine
;;
-(defun checkdoc-rogue-space-check-engine (&optional start end)
- "Return a message string if there is a line with white space at the end.
+(defun checkdoc-rogue-space-check-engine (&optional start end interact)
+ "Return a message list if there is a line with white space at the end.
If `checkdoc-autofix-flag' permits, delete that whitespace instead.
If optional arguments START and END are non nil, bound the check to
-this region."
+this region.
+Optional argument INTERACT may permit the user to fix problems on the fly."
(let ((p (point))
- (msg nil))
+ (msg nil) s e (f nil))
(if (not start) (setq start (point-min)))
;; If end is nil, it means end of buffer to search anyway
(or
- ;; Checkfor and error if `? ' or `?\ ' is used at the end of a line.
+ ;; Check for an error if `? ' or `?\ ' is used at the end of a line.
;; (It's dangerous)
(progn
(goto-char start)
- (if (re-search-forward "\\?\\\\?[ \t][ \t]*$" end t)
- (setq msg
- "Don't use `? ' at the end of a line. \
-Some editors & news agents may remove it")))
- ;; Check for, and pottentially remove whitespace appearing at the
+ (while (and (not msg) (re-search-forward "\\?\\\\?[ \t][ \t]*$" end t))
+ (setq msg
+ "Don't use `? ' at the end of a line. \
+News agents may remove it"
+ s (match-beginning 0) e (match-end 0) f t)
+ ;; If interactive is passed down, give them a chance to fix things.
+ (if (and interact (y-or-n-p (concat msg ". Fix? ")))
+ (progn
+ (checkdoc-recursive-edit msg)
+ (setq msg nil)
+ (goto-char s)
+ (beginning-of-line)))))
+ ;; Check for, and potentially remove whitespace appearing at the
;; end of different lines.
(progn
(goto-char start)
;; There is no documentation in the Emacs Lisp manual about this check,
;; it is intended to help clean up messy code and reduce the file size.
- (while (and (not msg) (re-search-forward "[^ \t\n]\\([ \t]+\\)$" end t))
+ (while (and (not msg) (re-search-forward "[^ \t\n;]\\([ \t]+\\)$" end t))
;; This is not a complex activity
(if (checkdoc-autofix-ask-replace
(match-beginning 1) (match-end 1)
"White space at end of line. Remove? " "")
nil
- (setq msg "White space found at end of line")))))
+ (setq msg "White space found at end of line"
+ s (match-beginning 1) e (match-end 1))))))
;; Return an error and leave the cursor at that spot, or restore
;; the cursor.
(if msg
- msg
+ (checkdoc-create-error msg s e f)
(goto-char p)
nil)))
@@ -1490,131 +2164,205 @@ Some editors & news agents may remove it")))
;;
(eval-when-compile
;; We must load this to:
- ;; a) get symbols for comple and
+ ;; a) get symbols for compile and
;; b) determine if we have lm-history symbol which doesn't always exist
(require 'lisp-mnt))
(defun checkdoc-file-comments-engine ()
- "Return a message string if this file does not match the Emacs standard.
+ "Return a message list if this file does not match the Emacs standard.
This checks for style only, such as the first line, Commentary:,
Code:, and others referenced in the style guide."
(if (featurep 'lisp-mnt)
nil
(require 'lisp-mnt)
- ;; Old Xemacs don't have `lm-commentary-mark'
+ ;; Old XEmacs don't have `lm-commentary-mark'
(if (and (not (fboundp 'lm-commentary-mark)) (boundp 'lm-commentary))
(defalias 'lm-commentary-mark 'lm-commentary)))
(save-excursion
(let* ((f1 (file-name-nondirectory (buffer-file-name)))
(fn (file-name-sans-extension f1))
- (fe (substring f1 (length fn))))
+ (fe (substring f1 (length fn)))
+ (err nil))
(goto-char (point-min))
- (or
+ ;; This file has been set up where ERR is a variable. Each check is
+ ;; asked, and the function will make sure that if the user does not
+ ;; auto-fix some error, that we still move on to the next auto-fix,
+ ;; AND we remember the past errors.
+ (setq
+ err
;; Lisp Maintenance checks first
;; Was: (lm-verify) -> not flexible enough for some people
;; * Summary at the beginning of the file:
(if (not (lm-summary))
;; This certifies as very complex so always ask unless
;; it's set to never
- (if (and checkdoc-autofix-flag
- (not (eq checkdoc-autofix-flag 'never))
- (y-or-n-p "There is no first line summary! Add one? "))
+ (if (checkdoc-y-or-n-p "There is no first line summary! Add one? ")
(progn
(goto-char (point-min))
(insert ";;; " fn fe " --- " (read-string "Summary: ") "\n"))
- "The first line should be of the form: \";;; package --- Summary\"")
- nil)
- ;; * Commentary Section
- (if (not (lm-commentary-mark))
- "You should have a section marked \";;; Commentary:\""
- nil)
- ;; * History section. Say nothing if there is a file ChangeLog
- (if (or (file-exists-p "ChangeLog")
- (let ((fn 'lm-history-mark)) ;bestill byte-compiler
- (and (fboundp fn) (funcall fn))))
- nil
- "You should have a section marked \";;; History:\" or use a ChangeLog")
- ;; * Code section
- (if (not (lm-code-mark))
- (let ((cont t))
- (goto-char (point-min))
- (while (and cont (re-search-forward "^(" nil t))
- (setq cont (looking-at "require\\s-+")))
- (if (and (not cont)
- checkdoc-autofix-flag
- (not (eq checkdoc-autofix-flag 'never))
- (y-or-n-p "There is no ;;; Code: marker. Insert one? "))
- (progn (beginning-of-line)
- (insert ";;; Code:\n")
- nil)
- "You should have a section marked \";;; Code:\""))
- nil)
- ;; * A footer. Not compartamentalized from lm-verify: too bad.
- ;; The following is partially clipped from lm-verify
- (save-excursion
- (goto-char (point-max))
- (if (not (re-search-backward
- (concat "^;;;[ \t]+" fn "\\(" (regexp-quote fe)
- "\\)?[ \t]+ends here[ \t]*$"
- "\\|^;;;[ \t]+ End of file[ \t]+"
- fn "\\(" (regexp-quote fe) "\\)?")
- nil t))
- (if (and checkdoc-autofix-flag
- (not (eq checkdoc-autofix-flag 'never))
- (y-or-n-p "No identifiable footer! Add one? "))
- (progn
- (goto-char (point-max))
- (insert "\n(provide '" fn ")\n;;; " fn fe " ends here\n"))
- (format "The footer should be (provide '%s)\\n;;; %s%s ends here"
- fn fn fe))))
- ;; Ok, now lets look for multiple occurances of ;;;, and offer
- ;; to remove the extra ";" if applicable. This pre-supposes
- ;; that the user has semiautomatic fixing on to be useful.
-
- ;; In the info node (elisp)Library Headers a header is three ;
- ;; (the header) followed by text of only two ;
- ;; In (elisp)Comment Tips, however it says this:
- ;; * Another use for triple-semicolon comments is for commenting out
- ;; lines within a function. We use triple-semicolons for this
- ;; precisely so that they remain at the left margin.
- (let ((msg nil))
- (goto-char (point-min))
- (while (and checkdoc-tripple-semi-comment-check-flag
- (not msg) (re-search-forward "^;;;[^;]" nil t))
- ;; We found a triple, lets check all following lines.
- (if (not (bolp)) (progn (beginning-of-line) (forward-line 1)))
- (let ((complex-replace t))
- (while (looking-at ";;\\(;\\)[^;]")
- (if (and (checkdoc-outside-major-sexp) ;in code is ok.
- (checkdoc-autofix-ask-replace
- (match-beginning 1) (match-end 1)
- "Multiple occurances of ;;; found. Use ;; instead? "
- "" complex-replace))
- ;; Learn that, yea, the user did want to do this a
- ;; whole bunch of times.
- (setq complex-replace nil))
- (beginning-of-line)
- (forward-line 1)))))
- ;; Lets spellcheck the commentary section. This is the only
- ;; section that is easy to pick out, and it is also the most
- ;; visible section (with the finder)
- (save-excursion
- (goto-char (lm-commentary-mark))
- ;; Spellcheck between the commentary, and the first
- ;; non-comment line. We could use lm-commentary, but that
- ;; returns a string, and Ispell wants to talk to a buffer.
- ;; Since the comments talk about Lisp, use the specialized
- ;; spell-checker we also used for doc strings.
- (checkdoc-ispell-docstring-engine (save-excursion
- (re-search-forward "^[^;]" nil t)
- (point))))
+ (checkdoc-create-error
+ "The first line should be of the form: \";;; package --- Summary\""
+ (point-min) (save-excursion (goto-char (point-min)) (end-of-line)
+ (point))))
+ nil))
+ (setq
+ err
+ (or
+ ;; * Commentary Section
+ (if (not (lm-commentary-mark))
+ (progn
+ (goto-char (point-min))
+ (cond
+ ((re-search-forward
+ "write\\s-+to\\s-+the\\s-+Free Software Foundation, Inc."
+ nil t)
+ (re-search-forward "^;;\\s-*\n\\|^\n" nil t))
+ ((or (re-search-forward "^;;; History" nil t)
+ (re-search-forward "^;;; Code" nil t)
+ (re-search-forward "^(require" nil t)
+ (re-search-forward "^("))
+ (beginning-of-line)))
+ (if (checkdoc-y-or-n-p
+ "You should have a \";;; Commentary:\", add one? ")
+ (insert "\n;;; Commentary:\n;; \n\n")
+ (checkdoc-create-error
+ "You should have a section marked \";;; Commentary:\""
+ nil nil t)))
+ nil)
+ err))
+ (setq
+ err
+ (or
+ ;; * History section. Say nothing if there is a file ChangeLog
+ (if (or (not checkdoc-force-history-flag)
+ (file-exists-p "ChangeLog")
+ (file-exists-p "../ChangeLog")
+ (let ((fn 'lm-history-mark)) ;bestill byte-compiler
+ (and (fboundp fn) (funcall fn))))
+ nil
+ (progn
+ (goto-char (or (lm-commentary-mark) (point-min)))
+ (cond
+ ((re-search-forward
+ "write\\s-+to\\s-+the\\s-+Free Software Foundation, Inc."
+ nil t)
+ (re-search-forward "^;;\\s-*\n\\|^\n" nil t))
+ ((or (re-search-forward "^;;; Code" nil t)
+ (re-search-forward "^(require" nil t)
+ (re-search-forward "^("))
+ (beginning-of-line)))
+ (if (checkdoc-y-or-n-p
+ "You should have a \";;; History:\", add one? ")
+ (insert "\n;;; History:\n;; \n\n")
+ (checkdoc-create-error
+ "You should have a section marked \";;; History:\" or use a ChangeLog"
+ (point) nil))))
+ err))
+ (setq
+ err
+ (or
+ ;; * Code section
+ (if (not (lm-code-mark))
+ (let ((cont t))
+ (goto-char (point-min))
+ (while (and cont (re-search-forward "^(" nil t))
+ (setq cont (looking-at "require\\s-+")))
+ (if (and (not cont)
+ (checkdoc-y-or-n-p
+ "There is no ;;; Code: marker. Insert one? "))
+ (progn (beginning-of-line)
+ (insert ";;; Code:\n")
+ nil)
+ (checkdoc-create-error
+ "You should have a section marked \";;; Code:\""
+ (point) nil)))
+ nil)
+ err))
+ (setq
+ err
+ (or
+ ;; * A footer. Not compartmentalized from lm-verify: too bad.
+ ;; The following is partially clipped from lm-verify
+ (save-excursion
+ (goto-char (point-max))
+ (if (not (re-search-backward
+ (concat "^;;;[ \t]+" fn "\\(" (regexp-quote fe)
+ "\\)?[ \t]+ends here[ \t]*$"
+ "\\|^;;;[ \t]+ End of file[ \t]+"
+ fn "\\(" (regexp-quote fe) "\\)?")
+ nil t))
+ (if (checkdoc-y-or-n-p "No identifiable footer! Add one? ")
+ (progn
+ (goto-char (point-max))
+ (insert "\n(provide '" fn ")\n\n;;; " fn fe " ends here\n"))
+ (checkdoc-create-error
+ (format "The footer should be: (provide '%s)\\n;;; %s%s ends here"
+ fn fn fe)
+ (1- (point-max)) (point-max)))))
+ err))
+ ;; The below checks will not return errors if the user says NO
+
+ ;; Ok, now let's look for multiple occurrences of ;;;, and offer
+ ;; to remove the extra ";" if applicable. This pre-supposes
+ ;; that the user has semiautomatic fixing on to be useful.
+
+ ;; In the info node (elisp)Library Headers a header is three ;
+ ;; (the header) followed by text of only two ;
+ ;; In (elisp)Comment Tips, however it says this:
+ ;; * Another use for triple-semicolon comments is for commenting out
+ ;; lines within a function. We use triple-semicolons for this
+ ;; precisely so that they remain at the left margin.
+ (let ((msg nil))
+ (goto-char (point-min))
+ (while (and checkdoc-triple-semi-comment-check-flag
+ (not msg) (re-search-forward "^;;;[^;]" nil t))
+ ;; We found a triple, let's check all following lines.
+ (if (not (bolp)) (progn (beginning-of-line) (forward-line 1)))
+ (let ((complex-replace t)
+ (dont-replace nil))
+ (while (looking-at ";;\\(;\\)[^;#]")
+ (if (and (not dont-replace)
+ (checkdoc-outside-major-sexp) ;in code is ok.
+ (checkdoc-autofix-ask-replace
+ (match-beginning 1) (match-end 1)
+ "Multiple occurrences of ;;; found. Use ;; instead? "
+ "" complex-replace))
+ ;; Learn that, yea, the user did want to do this a
+ ;; whole bunch of times.
+ (setq complex-replace nil)
+ ;; In this case, skip all this crap
+ (setq dont-replace t))
+ (beginning-of-line)
+ (forward-line 1)))))
+
+ ;; Let's spellcheck the commentary section. This is the only
+ ;; section that is easy to pick out, and it is also the most
+ ;; visible section (with the finder).
+ (let ((cm (lm-commentary-mark)))
+ (if cm
+ (save-excursion
+ (goto-char (lm-commentary-mark))
+ ;; Spellcheck between the commentary, and the first
+ ;; non-comment line. We could use lm-commentary, but that
+ ;; returns a string, and Ispell wants to talk to a buffer.
+ ;; Since the comments talk about Lisp, use the specialized
+ ;; spell-checker we also used for doc strings.
+ (let ((e (save-excursion (re-search-forward "^[^;]" nil t)
+ (point))))
+ (checkdoc-sentencespace-region-engine (point) e)
+ (checkdoc-proper-noun-region-engine (point) e)
+ (checkdoc-ispell-docstring-engine e)))))
;;; test comment out code
;;; (foo 1 3)
;;; (bar 5 7)
- ;; Generic Full-file checks (should be comment related)
- (checkdoc-run-hooks 'checkdoc-comment-style-hooks)
- ;; Done with full file comment checks
- ))))
+ (setq
+ err
+ (or
+ ;; Generic Full-file checks (should be comment related)
+ (checkdoc-run-hooks 'checkdoc-comment-style-hooks)
+ err))
+ ;; Done with full file comment checks
+ err)))
(defun checkdoc-outside-major-sexp ()
"Return t if point is outside the bounds of a valid sexp."
@@ -1626,56 +2374,60 @@ Code:, and others referenced in the style guide."
;;; `error' and `message' text verifier.
;;
-(defun checkdoc-message-text (&optional take-notes)
- "Scan the buffer for occurrences of the error function, and verify text.
-Optional argument TAKE-NOTES causes all errors to be logged."
- (interactive "P")
- (if take-notes (checkdoc-start-section "checkdoc-message-text"))
- (let ((p (point))
- (e (checkdoc-message-text-search)))
- (if e (if take-notes (checkdoc-error (point) e) (error e)))
- (if (and take-notes e) (checkdoc-show-diagnostics))
- (goto-char p))
- (if (interactive-p) (message "Checking error message text...done.")))
-
(defun checkdoc-message-text-search (&optional beg end)
- "Search between BEG and END for an error with `error'.
+ "Search between BEG and END for a style error with message text.
Optional arguments BEG and END represent the boundary of the check.
The default boundary is the entire buffer."
- (let ((e nil))
+ (let ((e nil)
+ (type nil))
(if (not (or beg end)) (setq beg (point-min) end (point-max)))
(goto-char beg)
- (while (and (not e) (re-search-forward "(\\s-*error[ \t\n]" end t))
- (if (looking-at "\"")
- (setq e (checkdoc-message-text-engine 'error))))
- (goto-char beg)
- (while (and (not e) (re-search-forward
- "\\<y-or-n-p\\(-with-timeout\\)?[ \t\n]" end t))
- ;; Format is common as a first arg..
- (if (looking-at "(format[ \t\n]") (goto-char (match-end 0)))
- (if (looking-at "\"")
- (setq e (checkdoc-message-text-engine 'y-or-n-p))))
- (goto-char beg)
- ;; this is cheating for checkdoc only.
- (while (and (not e) (re-search-forward
- "(checkdoc-autofix-ask-replace[ \t\n]"
- end t))
- (forward-sexp 2)
- (skip-chars-forward " \t\n")
- (if (looking-at "(format[ \t\n]") (goto-char (match-end 0)))
- (if (looking-at "\"")
- (setq e (checkdoc-message-text-engine 'y-or-n-p))))
- ;; Is it worth adding checks for read commands too? That would
- ;; require fixing up `interactive' which could be unpleasant.
- ;; Most people get that right by accident anyway.
+ (while (setq type (checkdoc-message-text-next-string end))
+ (setq e (checkdoc-message-text-engine type)))
e))
-(defun checkdoc-message-text-engine (type)
+(defun checkdoc-message-text-next-string (end)
+ "Move cursor to the next checkable message string after point.
+Return the message classification.
+Argument END is the maximum bounds to search in."
+ (let ((return nil))
+ (while (and (not return)
+ (re-search-forward
+ "(\\s-*\\(\\(\\w\\|\\s_\\)*error\\|\
+\\(\\w\\|\\s_\\)*y-or-n-p\\(-with-timeout\\)?\
+\\|checkdoc-autofix-ask-replace\\)[ \t\n]+" end t))
+ (let* ((fn (match-string 1))
+ (type (cond ((string-match "error" fn)
+ 'error)
+ (t 'y-or-n-p))))
+ (if (string-match "checkdoc-autofix-ask-replace" fn)
+ (progn (forward-sexp 2)
+ (skip-chars-forward " \t\n")))
+ (if (and (eq type 'y-or-n-p)
+ (looking-at "(format[ \t\n]+"))
+ (goto-char (match-end 0)))
+ (skip-chars-forward " \t\n")
+ (if (not (looking-at "\""))
+ nil
+ (setq return type))))
+ return))
+
+(defun checkdoc-message-text-engine (&optional type)
"Return or fix errors found in strings passed to a message display function.
-According to the documentation for the function `error', the error string
-should not end with a period, and should start with a capitol letter.
+According to the documentation for the function `error', the error list
+should not end with a period, and should start with a capital letter.
The function `y-or-n-p' has similar constraints.
Argument TYPE specifies the type of question, such as `error or `y-or-n-p."
+ ;; If type is nil, then attempt to derive it.
+ (if (not type)
+ (save-excursion
+ (up-list -1)
+ (if (looking-at "(format")
+ (up-list -1))
+ (setq type
+ (cond ((looking-at "(error")
+ 'error)
+ (t 'y-or-n-p)))))
(let ((case-fold-search nil))
(or
;; From the documentation of the symbol `error':
@@ -1689,8 +2441,19 @@ Argument TYPE specifies the type of question, such as `error or `y-or-n-p."
"Capitalize your message text? "
(capitalize (match-string 0))
t)))
- "Messages should start with a capitol letter"
+ (checkdoc-create-error
+ "Messages should start with a capital letter"
+ (match-beginning 0) (match-end 0))
nil)
+ ;; In general, sentences should have two spaces after the period.
+ (checkdoc-sentencespace-region-engine (point)
+ (save-excursion (forward-sexp 1)
+ (point)))
+ ;; Look for proper nouns in this region too.
+ (checkdoc-proper-noun-region-engine (point)
+ (save-excursion (forward-sexp 1)
+ (point)))
+ ;; Here are message type specific questions.
(if (and (eq type 'error)
(save-excursion (forward-sexp 1)
(forward-char -2)
@@ -1700,50 +2463,72 @@ Argument TYPE specifies the type of question, such as `error or `y-or-n-p."
"Remove period from error? "
""
t)))
- "Error messages should *not* end with a period"
+ (checkdoc-create-error
+ "Error messages should *not* end with a period"
+ (match-beginning 0) (match-end 0))
nil)
;; `y-or-n-p' documentation explicitly says:
;; It should end in a space; `y-or-n-p' adds `(y or n) ' to it.
;; I added the ? requirement. Without it, it is unclear that we
;; ask a question and it appears to be an undocumented style.
- (if (and (eq type 'y-or-n-p)
- (save-excursion (forward-sexp 1)
- (forward-char -3)
- (not (looking-at "\\? ")))
- (if (save-excursion (forward-sexp 1)
- (forward-char -2)
- (looking-at "\\?"))
- ;; If we see a ?, then replace with "? ".
- (if (checkdoc-autofix-ask-replace
- (match-beginning 0) (match-end 0)
- "y-or-n-p text should endwith \"? \". Fix? "
- "? " t)
- nil
- "y-or-n-p text should endwith \"? \".")
- (if (save-excursion (forward-sexp 1)
- (forward-char -2)
- (looking-at " "))
- (if (checkdoc-autofix-ask-replace
- (match-beginning 0) (match-end 0)
- "y-or-n-p text should endwith \"? \". Fix? "
- "? " t)
- nil
- "y-or-n-p text should endwith \"? \".")
- (if (and ;; if this isn't true, we have a problem.
- (save-excursion (forward-sexp 1)
- (forward-char -1)
- (looking-at "\""))
- (checkdoc-autofix-ask-replace
- (match-beginning 0) (match-end 0)
- "y-or-n-p text should endwith \"? \". Fix? "
- "? \"" t))
- nil
- "y-or-n-p text should endwith \"? \"."))))
- nil)
+ (if (eq type 'y-or-n-p)
+ (if (not (save-excursion (forward-sexp 1)
+ (forward-char -3)
+ (not (looking-at "\\? "))))
+ nil
+ (if (save-excursion (forward-sexp 1)
+ (forward-char -2)
+ (looking-at "\\?"))
+ ;; If we see a ?, then replace with "? ".
+ (if (checkdoc-autofix-ask-replace
+ (match-beginning 0) (match-end 0)
+ "y-or-n-p text should end with \"? \". Fix? "
+ "? " t)
+ nil
+ (checkdoc-create-error
+ "y-or-n-p text should end with \"? \""
+ (match-beginning 0) (match-end 0)))
+ (if (save-excursion (forward-sexp 1)
+ (forward-char -2)
+ (looking-at " "))
+ (if (checkdoc-autofix-ask-replace
+ (match-beginning 0) (match-end 0)
+ "y-or-n-p text should end with \"? \". Fix? "
+ "? " t)
+ nil
+ (checkdoc-create-error
+ "y-or-n-p text should end with \"? \""
+ (match-beginning 0) (match-end 0)))
+ (if (and ;; if this isn't true, we have a problem.
+ (save-excursion (forward-sexp 1)
+ (forward-char -1)
+ (looking-at "\""))
+ (checkdoc-autofix-ask-replace
+ (match-beginning 0) (match-end 0)
+ "y-or-n-p text should end with \"? \". Fix? "
+ "? \"" t))
+ nil
+ (checkdoc-create-error
+ "y-or-n-p text should end with \"? \""
+ (match-beginning 0) (match-end 0)))))))
+ ;; Now, let's just run the spell checker on this guy.
+ (checkdoc-ispell-docstring-engine (save-excursion (forward-sexp 1)
+ (point)))
)))
;;; Auto-fix helper functions
;;
+(defun checkdoc-y-or-n-p (question)
+ "Like `y-or-n-p', but pays attention to `checkdoc-autofix-flag'.
+Argument QUESTION is the prompt passed to `y-or-n-p'."
+ (prog1
+ (if (or (not checkdoc-autofix-flag)
+ (eq checkdoc-autofix-flag 'never))
+ nil
+ (y-or-n-p question))
+ (if (eq checkdoc-autofix-flag 'automatic-then-never)
+ (setq checkdoc-autofix-flag 'never))))
+
(defun checkdoc-autofix-ask-replace (start end question replacewith
&optional complex)
"Highlight between START and END and queries the user with QUESTION.
@@ -1756,14 +2541,19 @@ If a section is auto-replaced without asking the user, this function
will pause near the fixed code so the user will briefly see what
happened.
-This function returns non-nil if the text was replaced."
- (if checkdoc-autofix-flag
+This function returns non-nil if the text was replaced.
+
+This function will not modify `match-data'."
+ (if (and checkdoc-autofix-flag
+ (not (eq checkdoc-autofix-flag 'never)))
(let ((o (checkdoc-make-overlay start end))
- (ret nil))
+ (ret nil)
+ (md (match-data)))
(unwind-protect
(progn
(checkdoc-overlay-put o 'face 'highlight)
(if (or (eq checkdoc-autofix-flag 'automatic)
+ (eq checkdoc-autofix-flag 'automatic-then-never)
(and (eq checkdoc-autofix-flag 'semiautomatic)
(not complex))
(and (or (eq checkdoc-autofix-flag 'query) complex)
@@ -1771,28 +2561,37 @@ This function returns non-nil if the text was replaced."
(save-excursion
(goto-char start)
;; On the off chance this is automatic, display
- ;; the question anyway so the user knows whats
+ ;; the question anyway so the user knows what's
;; going on.
(if checkdoc-bouncy-flag (message "%s -> done" question))
(delete-region start end)
(insert replacewith)
(if checkdoc-bouncy-flag (sit-for 0))
(setq ret t)))
- (checkdoc-delete-overlay o))
- (checkdoc-delete-overlay o))
+ (checkdoc-delete-overlay o)
+ (set-match-data md))
+ (checkdoc-delete-overlay o)
+ (set-match-data md))
+ (if (eq checkdoc-autofix-flag 'automatic-then-never)
+ (setq checkdoc-autofix-flag 'never))
ret)))
;;; Warning management
;;
(defvar checkdoc-output-font-lock-keywords
- '(("\\(\\w+\\.el\\):" 1 font-lock-function-name-face)
- ("style check: \\(\\w+\\)" 1 font-lock-comment-face)
- ("^\\([0-9]+\\):" 1 font-lock-constant-face))
+ '(("\\(\\w+\\.el\\): \\(\\w+\\)"
+ (1 font-lock-function-name-face)
+ (2 font-lock-comment-face))
+ ("^\\(\\w+\\.el\\):" 1 font-lock-function-name-face)
+ (":\\([0-9]+\\):" 1 font-lock-constant-face))
"Keywords used to highlight a checkdoc diagnostic buffer.")
(defvar checkdoc-output-mode-map nil
"Keymap used in `checkdoc-output-mode'.")
+(defvar checkdoc-pending-errors nil
+ "Non-nil when there are errors that have not been displayed yet.")
+
(if checkdoc-output-mode-map
nil
(setq checkdoc-output-mode-map (make-sparse-keymap))
@@ -1830,11 +2629,9 @@ This function returns non-nil if the text was replaced."
"In a checkdoc diagnostic buffer, find the error under point."
(interactive)
(beginning-of-line)
- (if (looking-at "[0-9]+")
- (let ((l (string-to-int (match-string 0)))
- (f (save-excursion
- (re-search-backward " \\(\\(\\w+\\|\\s_\\)+\\.el\\):")
- (match-string 1))))
+ (if (looking-at "\\(\\(\\w+\\|\\s_\\)+\\.el\\):\\([0-9]+\\):")
+ (let ((l (string-to-int (match-string 3)))
+ (f (match-string 1)))
(if (not (get-buffer f))
(error "Can't find buffer %s" f))
(switch-to-buffer-other-window (get-buffer f))
@@ -1845,14 +2642,17 @@ This function returns non-nil if the text was replaced."
Create the header so that the string CHECK-TYPE is displayed as the
function called to create the messages."
(checkdoc-output-to-error-buffer
- "\n\n*** " (current-time-string) " "
- (file-name-nondirectory (buffer-file-name)) ": style check: " check-type
+ "\n\n\C-l\n*** "
+ (file-name-nondirectory (buffer-file-name)) ": " check-type
" V " checkdoc-version))
(defun checkdoc-error (point msg)
"Store POINT and MSG as errors in the checkdoc diagnostic buffer."
+ (setq checkdoc-pending-errors t)
(checkdoc-output-to-error-buffer
- "\n" (int-to-string (count-lines (point-min) (or point 1))) ": "
+ "\n"
+ (file-name-nondirectory (buffer-file-name)) ":"
+ (int-to-string (count-lines (point-min) (or point 1))) ": "
msg))
(defun checkdoc-output-to-error-buffer (&rest text)
@@ -1864,11 +2664,17 @@ function called to create the messages."
(defun checkdoc-show-diagnostics ()
"Display the checkdoc diagnostic buffer in a temporary window."
- (let ((b (get-buffer checkdoc-diagnostic-buffer)))
- (if b (progn (pop-to-buffer b)
- (beginning-of-line)))
- (other-window -1)
- (shrink-window-if-larger-than-buffer)))
+ (if checkdoc-pending-errors
+ (let ((b (get-buffer checkdoc-diagnostic-buffer)))
+ (if b (progn (pop-to-buffer b)
+ (goto-char (point-max))
+ (re-search-backward "\C-l" nil t)
+ (beginning-of-line)
+ (forward-line 1)
+ (recenter 0)))
+ (other-window -1)
+ (setq checkdoc-pending-errors nil)
+ nil)))
(defgroup checkdoc nil
"Support for doc string checking in Emacs Lisp."