More tweaks of skeleton documentation wrt \n behavior at bol/eol.
[bpt/emacs.git] / lisp / emacs-lisp / checkdoc.el
CommitLineData
e8af40ee 1;;; checkdoc.el --- check documentation strings for style requirements
5b531322 2
ba318903 3;; Copyright (C) 1997-1998, 2001-2014 Free Software Foundation, Inc.
04f3f5a2 4
0a0a3dee 5;; Author: Eric M. Ludlam <zappo@gnu.org>
84f473b0 6;; Version: 0.6.2
5b531322 7;; Keywords: docs, maint, lisp
04f3f5a2 8
5b531322 9;; This file is part of GNU Emacs.
04f3f5a2 10
d6cba7ae 11;; GNU Emacs is free software: you can redistribute it and/or modify
5b531322 12;; it under the terms of the GNU General Public License as published by
d6cba7ae
GM
13;; the Free Software Foundation, either version 3 of the License, or
14;; (at your option) any later version.
04f3f5a2 15
5b531322
KH
16;; GNU Emacs is distributed in the hope that it will be useful,
17;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19;; GNU General Public License for more details.
04f3f5a2 20
5b531322 21;; You should have received a copy of the GNU General Public License
d6cba7ae 22;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
5b531322
KH
23
24;;; Commentary:
25;;
5fecb21a 26;; The Emacs Lisp manual has a nice chapter on how to write
5b531322
KH
27;; documentation strings. Many stylistic suggestions are fairly
28;; deterministic and easy to check for syntactically, but also easy
29;; to forget. The main checkdoc engine will perform the stylistic
30;; checks needed to make sure these styles are remembered.
31;;
32;; There are two ways to use checkdoc:
bca0d607
EL
33;; 1) Periodically use `checkdoc' or `checkdoc-current-buffer'.
34;; `checkdoc' is a more interactive version of
35;; `checkdoc-current-buffer'
5b531322 36;; 2) Use `checkdoc-minor-mode' to automatically check your
5fecb21a 37;; documentation whenever you evaluate Lisp code with C-M-x
5b531322
KH
38;; or [menu-bar emacs-lisp eval-buffer]. Additional key-bindings
39;; are also provided under C-c ? KEY
40;; (require 'checkdoc)
4f91a816 41;; (add-hook 'emacs-lisp-mode-hook 'checkdoc-minor-mode)
5b531322 42;;
bca0d607
EL
43;; Using `checkdoc':
44;;
45;; The commands `checkdoc' and `checkdoc-ispell' are the top-level
46;; entry points to all of the different checks that are available. It
47;; breaks examination of your Lisp file into four sections (comments,
48;; documentation, messages, and spacing) and indicates its current
49;; state in a status buffer.
50;;
51;; The Comments check examines your headers, footers, and
52;; various tags (such as "Code:") to make sure that your code is ready
53;; for easy integration into existing systems.
54;;
55;; The Documentation check deals with documentation strings
56;; and their elements that help make Emacs easier to use.
57;;
58;; The Messages check ensures that the strings displayed in the
59;; minibuffer by some commands (such as `error' and `y-or-n-p')
60;; are consistent with the Emacs environment.
61;;
62;; The Spacing check cleans up white-space at the end of lines.
63;;
64;; The interface while working with documentation and messages is
65;; slightly different when being run in the interactive mode. The
66;; interface offers several options, including the ability to skip to
67;; the next error, or back up to previous errors. Auto-fixing is
68;; turned off at this stage, but you can use the `f' or `F' key to fix
69;; a given error (if the fix is available.)
70;;
5b531322
KH
71;; Auto-fixing:
72;;
73;; There are four classifications of style errors in terms of how
74;; easy they are to fix. They are simple, complex, really complex,
75;; and impossible. (Impossible really means that checkdoc does not
76;; have a fixing routine yet.) Typically white-space errors are
77;; classified as simple, and are auto-fixed by default. Typographic
78;; changes are considered complex, and the user is asked if they want
79;; the problem fixed before checkdoc makes the change. These changes
80;; can be done without asking if `checkdoc-autofix-flag' is properly
81;; set. Potentially redundant changes are considered really complex,
82;; and the user is always asked before a change is inserted. The
83;; variable `checkdoc-autofix-flag' controls how these types of errors
84;; are fixed.
85;;
bca0d607 86;; Spell checking text:
5b531322
KH
87;;
88;; The variable `checkdoc-spellcheck-documentation-flag' can be set
89;; to customize how spell checking is to be done. Since spell
90;; checking can be quite slow, you can optimize how best you want your
84003f38 91;; checking done. The default is `defun', which spell checks each time
5b531322
KH
92;; `checkdoc-defun' or `checkdoc-eval-defun' is used. Setting to nil
93;; prevents spell checking during normal usage.
94;; Setting this variable to nil does not mean you cannot take
95;; advantage of the spell checking. You can instead use the
96;; interactive functions `checkdoc-ispell-*' to check the spelling of
97;; your documentation.
5fecb21a
RS
98;; There is a list of Lisp-specific words which checkdoc will
99;; install into Ispell on the fly, but only if Ispell is not already
5b531322
KH
100;; running. Use `ispell-kill-ispell' to make checkdoc restart it with
101;; these words enabled.
102;;
bca0d607 103;; Checking parameters:
0a0a3dee 104;;
6deb1543 105;; You might not always want a function to have its parameters listed
0a0a3dee
EL
106;; in order. When this is the case, put the following comment just in
107;; front of the documentation string: "; checkdoc-order: nil" This
108;; overrides the value of `checkdoc-arguments-in-order-flag'.
109;;
110;; If you specifically wish to avoid mentioning a parameter of a
111;; function in the doc string (such as a hidden parameter, or a
112;; parameter which is very obvious like events), you can have checkdoc
113;; skip looking for it by putting the following comment just in front
114;; of the documentation string: "; checkdoc-params: (args go here)"
115;;
bca0d607 116;; Checking message strings:
a4370a77 117;;
bca0d607 118;; The text that follows the `error' and `y-or-n-p' commands is
a4370a77
EL
119;; also checked. The documentation for `error' clearly states some
120;; simple style rules to follow which checkdoc will auto-fix for you.
121;; `y-or-n-p' also states that it should end in a space. I added that
122;; it should end in "? " since that is almost always used.
123;;
5b531322
KH
124;; Adding your own checks:
125;;
126;; You can experiment with adding your own checks by setting the
7b1bf173 127;; hooks `checkdoc-style-functions' and `checkdoc-comment-style-functions'.
5b531322
KH
128;; Return a string which is the error you wish to report. The cursor
129;; position should be preserved.
130;;
bca0d607
EL
131;; Error errors:
132;;
133;; Checkdoc does not always flag errors correctly. There are a
134;; couple ways you can coax your file into passing all of checkdoc's
135;; tests through buffer local variables.
136;;
137;; The variable `checkdoc-verb-check-experimental-flag' can be used
138;; to turn off the check for verb-voice in case you use words that are
139;; not semantically verbs, but are still in the incomplete list.
140;;
141;; The variable `checkdoc-symbol-words' can be a list of words that
142;; happen to also be symbols. This is not a problem for one-word
143;; symbols, but if you use a hyphenated word that is also a symbol,
144;; then you may need this.
145;;
146;; The symbol `checkdoc-force-docstrings-flag' can be set to nil if
147;; you have many undocumented functions you don't wish to document.
148;;
149;; See the above section "Checking Parameters" for details about
150;; parameter checking.
151;;
152;; Dependencies:
153;;
154;; This file requires lisp-mnt (Lisp maintenance routines) for the
155;; comment checkers.
156;;
157;; Requires custom for Emacs v20.
5b531322
KH
158
159;;; TO DO:
bca0d607 160;; Hook into the byte compiler on a defun/defvar level to generate
5b531322
KH
161;; warnings in the byte-compiler's warning/error buffer.
162;; Better ways to override more typical `eval' functions. Advice
163;; might be good but hard to turn on/off as a minor mode.
164;;
165;;; Maybe Do:
166;; Code sweep checks for "forbidden functions", proper use of hooks,
167;; proper keybindings, and other items from the manual that are
168;; not specifically docstring related. Would this even be useful?
169
170;;; Code:
bca0d607 171(defvar checkdoc-version "0.6.1"
5b531322
KH
172 "Release version of checkdoc you are currently running.")
173
849f465a
KR
174(require 'help-mode) ;; for help-xref-info-regexp
175(require 'thingatpt) ;; for handy thing-at-point-looking-at
176
6d74f782
JB
177(defvar compilation-error-regexp-alist)
178(defvar compilation-mode-font-lock-keywords)
179
76667462
SM
180(defgroup checkdoc nil
181 "Support for doc string checking in Emacs Lisp."
182 :prefix "checkdoc"
183 :group 'lisp
184 :version "20.3")
185
b92317dc 186(defcustom checkdoc-minor-mode-string " CDoc"
cb711556 187 "String to display in mode line when Checkdoc mode is enabled; nil for none."
b92317dc 188 :type '(choice string (const :tag "None" nil))
b92317dc
GM
189 :version "23.1")
190
5b531322 191(defcustom checkdoc-autofix-flag 'semiautomatic
76667462 192 "Non-nil means attempt auto-fixing of doc strings.
5fecb21a
RS
193If this value is the symbol `query', then the user is queried before
194any change is made. If the value is `automatic', then all changes are
5b531322 195made without asking unless the change is very-complex. If the value
bca0d607 196is `semiautomatic' or any other value, then simple fixes are made
5b531322 197without asking, and complex changes are made by asking the user first.
5fecb21a 198The value `never' is the same as nil, never ask or change anything."
5b531322 199 :type '(choice (const automatic)
1398b795
SM
200 (const query)
201 (const never)
202 (other :tag "semiautomatic" semiautomatic)))
5b531322
KH
203
204(defcustom checkdoc-bouncy-flag t
76667462 205 "Non-nil means to \"bounce\" to auto-fix locations.
5b531322
KH
206Setting this to nil will silently make fixes that require no user
207interaction. See `checkdoc-autofix-flag' for auto-fixing details."
5b531322
KH
208 :type 'boolean)
209
210(defcustom checkdoc-force-docstrings-flag t
76667462 211 "Non-nil means that all checkable definitions should have documentation.
5b531322 212Style guide dictates that interactive functions MUST have documentation,
bca0d607 213and that it's good but not required practice to make non user visible items
5fecb21a 214have doc strings."
5b531322 215 :type 'boolean)
b0429158 216;;;###autoload(put 'checkdoc-force-docstrings-flag 'safe-local-variable #'booleanp)
5b531322 217
5fe443de 218(defcustom checkdoc-force-history-flag nil
76667462 219 "Non-nil means that files should have a History section or ChangeLog file.
bca0d607 220This helps document the evolution of, and recent changes to, the package."
bca0d607 221 :type 'boolean)
b0429158 222;;;###autoload(put 'checkdoc-force-history-flag 'safe-local-variable #'booleanp)
bca0d607
EL
223
224(defcustom checkdoc-permit-comma-termination-flag nil
76667462 225 "Non-nil means the first line of a docstring may end with a comma.
bca0d607
EL
226Ordinarily, a full sentence is required. This may be misleading when
227there is a substantial caveat to the one-line description -- the comma
228should be used when the first part could stand alone as a sentence, but
229it indicates that a modifying clause follows."
bca0d607 230 :type 'boolean)
b0429158 231;;;###autoload(put 'checkdoc-permit-comma-termination-flag 'safe-local-variable #'booleanp)
bca0d607 232
5b531322 233(defcustom checkdoc-spellcheck-documentation-flag nil
76667462 234 "Non-nil means run Ispell on text based on value.
5fecb21a 235This is automatically set to nil if Ispell does not exist on your
5b531322
KH
236system. Possible values are:
237
5fecb21a
RS
238 nil - Don't spell-check during basic style checks.
239 defun - Spell-check when style checking a single defun
bca0d607
EL
240 buffer - Spell-check when style checking the whole buffer
241 interactive - Spell-check during any interactive check.
5fecb21a 242 t - Always spell-check"
5b531322 243 :type '(choice (const nil)
1398b795
SM
244 (const defun)
245 (const buffer)
246 (const interactive)
247 (const t)))
b0429158 248;;;###autoload(put 'checkdoc-spellcheck-documentation-flag 'safe-local-variable #'booleanp)
5b531322
KH
249
250(defvar checkdoc-ispell-lisp-words
314125ec 251 '("alist" "emacs" "etags" "keymap" "paren" "regexp" "sexp" "xemacs")
5fecb21a 252 "List of words that are correct when spell-checking Lisp documentation.")
b0429158 253;;;###autoload(put 'checkdoc-ispell-list-words 'safe-local-variable #'checkdoc-list-of-strings-p)
5b531322
KH
254
255(defcustom checkdoc-max-keyref-before-warn 10
76667462 256 "The number of \\ [command-to-keystroke] tokens allowed in a doc string.
5b531322
KH
257Any more than this and a warning is generated suggesting that the construct
258\\ {keymap} be used instead."
5b531322
KH
259 :type 'integer)
260
261(defcustom checkdoc-arguments-in-order-flag t
76667462 262 "Non-nil means warn if arguments appear out of order.
5b531322
KH
263Setting this to nil will mean only checking that all the arguments
264appear in the proper form in the documentation, not that they are in
265the same order as they appear in the argument list. No mention is
266made in the style guide relating to order."
5b531322 267 :type 'boolean)
b0429158 268;;;###autoload(put 'checkdoc-arguments-in-order-flag 'safe-local-variable #'booleanp)
5b531322 269
d1069532
SM
270(define-obsolete-variable-alias 'checkdoc-style-hooks
271 'checkdoc-style-functions "24.3")
272(defvar checkdoc-style-functions nil
273 "Hook run after the standard style check is completed.
274All functions must return nil or a string representing the error found.
5b531322
KH
275Useful for adding new user implemented commands.
276
277Each hook is called with two parameters, (DEFUNINFO ENDPOINT).
278DEFUNINFO is the return value of `checkdoc-defun-info'. ENDPOINT is the
279location of end of the documentation string.")
280
d1069532 281(define-obsolete-variable-alias 'checkdoc-comment-style-hooks
76673f9c 282 'checkdoc-comment-style-functions "24.3")
d1069532
SM
283(defvar checkdoc-comment-style-functions nil
284 "Hook run after the standard comment style check is completed.
5b531322
KH
285Must return nil if no errors are found, or a string describing the
286problem discovered. This is useful for adding additional checks.")
287
288(defvar checkdoc-diagnostic-buffer "*Style Warnings*"
0a0a3dee 289 "Name of warning message buffer.")
5b531322
KH
290
291(defvar checkdoc-defun-regexp
292 "^(def\\(un\\|var\\|custom\\|macro\\|const\\|subst\\|advice\\)\
293\\s-+\\(\\(\\sw\\|\\s_\\)+\\)[ \t\n]+"
294 "Regular expression used to identify a defun.
295A search leaves the cursor in front of the parameter list.")
296
297(defcustom checkdoc-verb-check-experimental-flag t
76667462 298 "Non-nil means to attempt to check the voice of the doc string.
5b531322 299This check keys off some words which are commonly misused. See the
5fecb21a 300variable `checkdoc-common-verbs-wrong-voice' if you wish to add your own."
5b531322 301 :type 'boolean)
b0429158 302;;;###autoload(put 'checkdoc-verb-check-experimental-flag 'safe-local-variable #'booleanp)
5b531322 303
bca0d607 304(defvar checkdoc-generate-compile-warnings-flag nil
79796334 305 "Non-nil means generate warnings in a buffer for browsing.
bca0d607
EL
306Do not set this by hand, use a function like `checkdoc-current-buffer'
307with a universal argument.")
308
309(defcustom checkdoc-symbol-words nil
9b89e3ee
GM
310 "A list of symbol names (strings) which also happen to make good words.
311These words are ignored when unquoted symbols are searched for.
bca0d607 312This should be set in an Emacs Lisp file's local variables."
bca0d607 313 :type '(repeat (symbol :tag "Word")))
b0429158 314;;;###autoload(put 'checkdoc-symbol-words 'safe-local-variable #'checkdoc-list-of-strings-p)
9b89e3ee
GM
315
316;;;###autoload
317(defun checkdoc-list-of-strings-p (obj)
318 ;; this is a function so it might be shared by checkdoc-proper-noun-list
319 ;; and/or checkdoc-ispell-lisp-words in the future
320 (and (listp obj)
b0429158 321 (not (memq nil (mapcar #'stringp obj)))))
bca0d607
EL
322
323(defvar checkdoc-proper-noun-list
324 '("ispell" "xemacs" "emacs" "lisp")
325 "List of words (not capitalized) which should be capitalized.")
326
327(defvar checkdoc-proper-noun-regexp
8bf7ed70
KR
328 ;; "[.!?]" is for noun at end of a sentence, since those chars
329 ;; are symbol syntax in emacs-lisp-mode and so don't match \\_>.
330 ;; The \" allows it to be the last sentence in a docstring too.
f69c67b6
KR
331 (concat "\\_<"
332 (regexp-opt checkdoc-proper-noun-list t)
333 "\\(\\_>\\|[.!?][ \t\n\"]\\)")
bca0d607 334 "Regular expression derived from `checkdoc-proper-noun-regexp'.")
b0429158 335;;;###autoload(put 'checkdoc-proper-noun-regexp 'safe-local-variable 'stringp)
bca0d607 336
5b531322
KH
337(defvar checkdoc-common-verbs-regexp nil
338 "Regular expression derived from `checkdoc-common-verbs-regexp'.")
b0429158 339;;;###autoload(put 'checkdoc-common-verbs-regexp 'safe-local-variable 'stringp)
5b531322
KH
340
341(defvar checkdoc-common-verbs-wrong-voice
342 '(("adds" . "add")
343 ("allows" . "allow")
344 ("appends" . "append")
6deb1543
KH
345 ("applies" . "apply")
346 ("arranges" . "arrange")
5b531322
KH
347 ("brings" . "bring")
348 ("calls" . "call")
349 ("catches" . "catch")
350 ("changes" . "change")
351 ("checks" . "check")
352 ("contains" . "contain")
1bd6facc 353 ("converts" . "convert")
5b531322
KH
354 ("creates" . "create")
355 ("destroys" . "destroy")
356 ("disables" . "disable")
357 ("executes" . "execute")
6deb1543 358 ("evals" . "evaluate")
5b531322
KH
359 ("evaluates" . "evaluate")
360 ("finds" . "find")
361 ("forces" . "force")
362 ("gathers" . "gather")
363 ("generates" . "generate")
364 ("goes" . "go")
365 ("guesses" . "guess")
366 ("highlights" . "highlight")
367 ("holds" . "hold")
368 ("ignores" . "ignore")
369 ("indents" . "indent")
370 ("initializes" . "initialize")
371 ("inserts" . "insert")
372 ("installs" . "install")
373 ("investigates" . "investigate")
374 ("keeps" . "keep")
375 ("kills" . "kill")
376 ("leaves" . "leave")
377 ("lets" . "let")
378 ("loads" . "load")
379 ("looks" . "look")
380 ("makes" . "make")
381 ("marks" . "mark")
382 ("matches" . "match")
995e028a 383 ("moves" . "move")
5b531322
KH
384 ("notifies" . "notify")
385 ("offers" . "offer")
386 ("parses" . "parse")
387 ("performs" . "perform")
388 ("prepares" . "prepare")
389 ("prepends" . "prepend")
390 ("reads" . "read")
391 ("raises" . "raise")
392 ("removes" . "remove")
393 ("replaces" . "replace")
394 ("resets" . "reset")
395 ("restores" . "restore")
396 ("returns" . "return")
397 ("runs" . "run")
398 ("saves" . "save")
399 ("says" . "say")
400 ("searches" . "search")
401 ("selects" . "select")
402 ("sets" . "set")
403 ("sex" . "s*x")
404 ("shows" . "show")
405 ("signifies" . "signify")
406 ("sorts" . "sort")
407 ("starts" . "start")
408 ("stores" . "store")
409 ("switches" . "switch")
410 ("tells" . "tell")
411 ("tests" . "test")
412 ("toggles" . "toggle")
6deb1543 413 ("tries" . "try")
5b531322
KH
414 ("turns" . "turn")
415 ("undoes" . "undo")
416 ("unloads" . "unload")
417 ("unmarks" . "unmark")
418 ("updates" . "update")
419 ("uses" . "use")
420 ("yanks" . "yank")
421 )
422 "Alist of common words in the wrong voice and what should be used instead.
423Set `checkdoc-verb-check-experimental-flag' to nil to avoid this costly
424and experimental check. Do not modify this list without setting
425the value of `checkdoc-common-verbs-regexp' to nil which cause it to
426be re-created.")
427
1398b795
SM
428(defvar checkdoc-syntax-table
429 (let ((st (make-syntax-table emacs-lisp-mode-syntax-table)))
430 ;; When dealing with syntax in doc strings, make sure that - are
431 ;; encompassed in words so we can use cheap \\> to get the end of a symbol,
432 ;; not the end of a word in a conglomerate.
cca982d0 433 (modify-syntax-entry ?- "w" st)
1398b795 434 st)
5b531322
KH
435 "Syntax table used by checkdoc in document strings.")
436
5b531322
KH
437;;; Compatibility
438;;
b372cfa9 439(defalias 'checkdoc-make-overlay
b0429158 440 (if (featurep 'xemacs) #'make-extent #'make-overlay))
b372cfa9 441(defalias 'checkdoc-overlay-put
b0429158 442 (if (featurep 'xemacs) #'set-extent-property #'overlay-put))
b372cfa9 443(defalias 'checkdoc-delete-overlay
b0429158 444 (if (featurep 'xemacs) #'delete-extent #'delete-overlay))
b372cfa9 445(defalias 'checkdoc-overlay-start
b0429158 446 (if (featurep 'xemacs) #'extent-start #'overlay-start))
b372cfa9 447(defalias 'checkdoc-overlay-end
b0429158 448 (if (featurep 'xemacs) #'extent-end #'overlay-end))
b372cfa9 449(defalias 'checkdoc-mode-line-update
b0429158 450 (if (featurep 'xemacs) #'redraw-modeline #'force-mode-line-update))
b372cfa9 451(defalias 'checkdoc-char=
b0429158 452 (if (featurep 'xemacs) #'char= #'=))
5b531322
KH
453
454;;; User level commands
455;;
456;;;###autoload
bca0d607 457(defun checkdoc ()
79796334
RS
458 "Interactively check the entire buffer for style errors.
459The current status of the check will be displayed in a buffer which
bca0d607 460the users will view as each check is completed."
5b531322 461 (interactive)
bca0d607
EL
462 (let ((status (list "Checking..." "-" "-" "-"))
463 (checkdoc-spellcheck-documentation-flag
84003f38
RS
464 (car (memq checkdoc-spellcheck-documentation-flag
465 '(buffer interactive t))))
bca0d607
EL
466 ;; if the user set autofix to never, then that breaks the
467 ;; obviously requested asking implied by using this function.
468 ;; Set it to paranoia level.
469 (checkdoc-autofix-flag (if (or (not checkdoc-autofix-flag)
470 (eq checkdoc-autofix-flag 'never))
471 'query
472 checkdoc-autofix-flag))
473 tmp)
474 (checkdoc-display-status-buffer status)
475 ;; check the comments
476 (if (not buffer-file-name)
477 (setcar status "Not checked")
478 (if (checkdoc-file-comments-engine)
479 (setcar status "Errors")
480 (setcar status "Ok")))
481 (setcar (cdr status) "Checking...")
482 (checkdoc-display-status-buffer status)
483 ;; Check the documentation
484 (setq tmp (checkdoc-interactive nil t))
485 (if tmp
486 (setcar (cdr status) (format "%d Errors" (length tmp)))
487 (setcar (cdr status) "Ok"))
488 (setcar (cdr (cdr status)) "Checking...")
489 (checkdoc-display-status-buffer status)
490 ;; Check the message text
491 (if (setq tmp (checkdoc-message-interactive nil t))
492 (setcar (cdr (cdr status)) (format "%d Errors" (length tmp)))
493 (setcar (cdr (cdr status)) "Ok"))
494 (setcar (cdr (cdr (cdr status))) "Checking...")
495 (checkdoc-display-status-buffer status)
496 ;; Rogue spacing
497 (if (condition-case nil
498 (checkdoc-rogue-spaces nil t)
499 (error t))
500 (setcar (cdr (cdr (cdr status))) "Errors")
501 (setcar (cdr (cdr (cdr status))) "Ok"))
502 (checkdoc-display-status-buffer status)))
503
504(defun checkdoc-display-status-buffer (check)
505 "Display and update the status buffer for the current checkdoc mode.
b08b261e
JB
506CHECK is a list of four strings stating the current status of each
507test; the nth string describes the status of the nth test."
eb83be8a 508 (let (temp-buffer-setup-hook)
5fe443de 509 (with-output-to-temp-buffer "*Checkdoc Status*"
1398b795
SM
510 (mapc #'princ
511 (list "Buffer comments and tags: " (nth 0 check)
512 "\nDocumentation style: " (nth 1 check)
513 "\nMessage/Query text style: " (nth 2 check)
514 "\nUnwanted Spaces: " (nth 3 check)))))
bca0d607 515 (shrink-window-if-larger-than-buffer
5fe443de 516 (get-buffer-window "*Checkdoc Status*"))
bca0d607
EL
517 (message nil)
518 (sit-for 0))
5b531322
KH
519
520;;;###autoload
bca0d607
EL
521(defun checkdoc-interactive (&optional start-here showstatus)
522 "Interactively check the current buffer for doc string errors.
523Prefix argument START-HERE will start the checking from the current
524point, otherwise the check starts at the beginning of the current
525buffer. Allows navigation forward and backwards through document
526errors. Does not check for comment or space warnings.
527Optional argument SHOWSTATUS indicates that we should update the
528checkdoc status window instead of the usual behavior."
5b531322 529 (interactive "P")
5b531322 530 (let ((checkdoc-spellcheck-documentation-flag
84003f38
RS
531 (car (memq checkdoc-spellcheck-documentation-flag
532 '(interactive t)))))
b08b261e
JB
533 (prog1
534 ;; Due to a design flaw, this will never spell check
535 ;; docstrings.
536 (checkdoc-interactive-loop start-here showstatus
b0429158 537 #'checkdoc-next-error)
b08b261e
JB
538 ;; This is a workaround to perform spell checking.
539 (checkdoc-interactive-ispell-loop start-here))))
5b531322
KH
540
541;;;###autoload
bca0d607
EL
542(defun checkdoc-message-interactive (&optional start-here showstatus)
543 "Interactively check the current buffer for message string errors.
5b531322
KH
544Prefix argument START-HERE will start the checking from the current
545point, otherwise the check starts at the beginning of the current
546buffer. Allows navigation forward and backwards through document
bca0d607
EL
547errors. Does not check for comment or space warnings.
548Optional argument SHOWSTATUS indicates that we should update the
549checkdoc status window instead of the usual behavior."
5b531322 550 (interactive "P")
bca0d607 551 (let ((checkdoc-spellcheck-documentation-flag
84003f38
RS
552 (car (memq checkdoc-spellcheck-documentation-flag
553 '(interactive t)))))
b08b261e
JB
554 (prog1
555 ;; Due to a design flaw, this will never spell check messages.
556 (checkdoc-interactive-loop start-here showstatus
b0429158 557 #'checkdoc-next-message-error)
b08b261e
JB
558 ;; This is a workaround to perform spell checking.
559 (checkdoc-message-interactive-ispell-loop start-here))))
bca0d607
EL
560
561(defun checkdoc-interactive-loop (start-here showstatus findfunc)
79796334 562 "Interactively loop over all errors that can be found by a given method.
b08b261e
JB
563
564If START-HERE is nil, searching starts at the beginning of the current
565buffer, otherwise searching starts at START-HERE. SHOWSTATUS
566expresses the verbosity of the search, and whether ending the search
567will auto-exit this function.
568
bca0d607 569FINDFUNC is a symbol representing a function that will position the
110c171f 570cursor, and return error message text to present to the user. It is
bca0d607
EL
571assumed that the cursor will stop just before a major sexp, which will
572be highlighted to present the user with feedback as to the offending
573style."
5b531322
KH
574 ;; Determine where to start the test
575 (let* ((begin (prog1 (point)
576 (if (not start-here) (goto-char (point-min)))))
577 ;; Assign a flag to spellcheck flag
578 (checkdoc-spellcheck-documentation-flag
84003f38
RS
579 (car (memq checkdoc-spellcheck-documentation-flag
580 '(buffer interactive t))))
5b531322 581 ;; Fetch the error list
bca0d607
EL
582 (err-list (list (funcall findfunc nil)))
583 (cdo nil)
584 (returnme nil)
585 c)
586 (save-window-excursion
587 (if (not (car err-list)) (setq err-list nil))
588 ;; Include whatever function point is in for good measure.
589 (beginning-of-defun)
590 (while err-list
591 (goto-char (cdr (car err-list)))
592 ;; The cursor should be just in front of the offending doc string
593 (if (stringp (car (car err-list)))
594 (setq cdo (save-excursion (checkdoc-make-overlay
595 (point) (progn (forward-sexp 1)
596 (point)))))
597 (setq cdo (checkdoc-make-overlay
598 (checkdoc-error-start (car (car err-list)))
599 (checkdoc-error-end (car (car err-list))))))
5b531322
KH
600 (unwind-protect
601 (progn
602 (checkdoc-overlay-put cdo 'face 'highlight)
5fecb21a 603 ;; Make sure the whole doc string is visible if possible.
5b531322 604 (sit-for 0)
d8693181
DL
605 (if (and (looking-at "\"")
606 (not (pos-visible-in-window-p
607 (save-excursion (forward-sexp 1) (point))
608 (selected-window))))
609 (let ((l (count-lines (point)
610 (save-excursion
611 (forward-sexp 1) (point)))))
612 (if (> l (window-height))
613 (recenter 1)
614 (recenter (/ (- (window-height) l) 2))))
615 (recenter))
bca0d607 616 (message "%s (C-h,%se,n,p,q)" (checkdoc-error-text
1398b795 617 (car (car err-list)))
bca0d607
EL
618 (if (checkdoc-error-unfixable (car (car err-list)))
619 "" "f,"))
620 (save-excursion
621 (goto-char (checkdoc-error-start (car (car err-list))))
622 (if (not (pos-visible-in-window-p))
623 (recenter (- (window-height) 2)))
b372cfa9 624 (setq c (read-event)))
5b531322 625 (if (not (integerp c)) (setq c ??))
bca0d607
EL
626 (cond
627 ;; Exit condition
628 ((checkdoc-char= c ?\C-g) (signal 'quit nil))
629 ;; Request an auto-fix
630 ((or (checkdoc-char= c ?y) (checkdoc-char= c ?f))
631 (checkdoc-delete-overlay cdo)
632 (setq cdo nil)
633 (goto-char (cdr (car err-list)))
634 ;; `automatic-then-never' tells the autofix function
635 ;; to only allow one fix to be automatic. The autofix
b0429158 636 ;; function will then set the flag to `never', allowing
bca0d607
EL
637 ;; the checker to return a different error.
638 (let ((checkdoc-autofix-flag 'automatic-then-never)
639 (fixed nil))
640 (funcall findfunc t)
641 (setq fixed (not (eq checkdoc-autofix-flag
642 'automatic-then-never)))
643 (if (not fixed)
644 (progn
645 (message "A Fix was not available.")
646 (sit-for 2))
647 (setq err-list (cdr err-list))))
648 (beginning-of-defun)
96ef1feb 649 (let ((ne (funcall findfunc nil)))
bca0d607
EL
650 (if ne
651 (setq err-list (cons ne err-list))
652 (cond ((not err-list)
653 (message "No More Stylistic Errors.")
654 (sit-for 2))
655 (t
656 (message
657 "No Additional style errors. Continuing...")
658 (sit-for 2))))))
659 ;; Move to the next error (if available)
6d74f782 660 ((or (checkdoc-char= c ?n) (checkdoc-char= c ?\s))
bca0d607
EL
661 (let ((ne (funcall findfunc nil)))
662 (if (not ne)
663 (if showstatus
664 (setq returnme err-list
665 err-list nil)
666 (if (not err-list)
667 (message "No More Stylistic Errors.")
668 (message "No Additional style errors. Continuing..."))
669 (sit-for 2))
670 (setq err-list (cons ne err-list)))))
671 ;; Go backwards in the list of errors
672 ((or (checkdoc-char= c ?p) (checkdoc-char= c ?\C-?))
673 (if (/= (length err-list) 1)
674 (progn
675 (setq err-list (cdr err-list))
676 (goto-char (cdr (car err-list)))
677 (beginning-of-defun))
678 (message "No Previous Errors.")
679 (sit-for 2)))
680 ;; Edit the buffer recursively.
681 ((checkdoc-char= c ?e)
682 (checkdoc-recursive-edit
683 (checkdoc-error-text (car (car err-list))))
684 (checkdoc-delete-overlay cdo)
685 (setq err-list (cdr err-list)) ;back up the error found.
686 (beginning-of-defun)
687 (let ((ne (funcall findfunc nil)))
688 (if (not ne)
689 (if showstatus
690 (setq returnme err-list
691 err-list nil)
692 (message "No More Stylistic Errors.")
693 (sit-for 2))
694 (setq err-list (cons ne err-list)))))
695 ;; Quit checkdoc
696 ((checkdoc-char= c ?q)
697 (setq returnme err-list
698 err-list nil
699 begin (point)))
b08b261e 700 ;; Goofy stuff
bca0d607
EL
701 (t
702 (if (get-buffer-window "*Checkdoc Help*")
703 (progn
704 (delete-window (get-buffer-window "*Checkdoc Help*"))
705 (kill-buffer "*Checkdoc Help*"))
706 (with-output-to-temp-buffer "*Checkdoc Help*"
1398b795
SM
707 (with-current-buffer standard-output
708 (insert
709 "Checkdoc Keyboard Summary:\n"
710 (if (checkdoc-error-unfixable (car (car err-list)))
711 ""
712 (concat
713 "f, y - auto Fix this warning without asking (if\
bca0d607 714 available.)\n"
1398b795
SM
715 " Very complex operations will still query.\n")
716 )
717 "e - Enter recursive Edit. Press C-M-c to exit.\n"
718 "SPC, n - skip to the Next error.\n"
719 "DEL, p - skip to the Previous error.\n"
720 "q - Quit checkdoc.\n"
721 "C-h - Toggle this help buffer.")))
bca0d607
EL
722 (shrink-window-if-larger-than-buffer
723 (get-buffer-window "*Checkdoc Help*"))))))
724 (if cdo (checkdoc-delete-overlay cdo)))))
5b531322 725 (goto-char begin)
bca0d607
EL
726 (if (get-buffer "*Checkdoc Help*") (kill-buffer "*Checkdoc Help*"))
727 (message "Checkdoc: Done.")
728 returnme))
5b531322 729
b08b261e
JB
730(defun checkdoc-interactive-ispell-loop (start-here)
731 "Interactively spell check doc strings in the current buffer.
732If START-HERE is nil, searching starts at the beginning of the current
733buffer, otherwise searching starts at START-HERE."
734 (when checkdoc-spellcheck-documentation-flag
735 (save-excursion
736 ;; Move point to where we need to start.
737 (if start-here
738 ;; Include whatever function point is in for good measure.
739 (beginning-of-defun)
740 (goto-char (point-min)))
741 ;; Loop over docstrings.
742 (while (checkdoc-next-docstring)
743 (message "Searching for doc string spell error...%d%%"
744 (/ (* 100 (point)) (point-max)))
745 (if (looking-at "\"")
746 (checkdoc-ispell-docstring-engine
747 (save-excursion (forward-sexp 1) (point-marker)))))
748 (message "Checkdoc: Done."))))
749
750(defun checkdoc-message-interactive-ispell-loop (start-here)
751 "Interactively spell check messages in the current buffer.
752If START-HERE is nil, searching starts at the beginning of the current
753buffer, otherwise searching starts at START-HERE."
754 (when checkdoc-spellcheck-documentation-flag
755 (save-excursion
756 ;; Move point to where we need to start.
757 (if start-here
758 ;; Include whatever function point is in for good measure.
759 (beginning-of-defun)
760 (goto-char (point-min)))
761 ;; Loop over message strings.
762 (while (checkdoc-message-text-next-string (point-max))
763 (message "Searching for message string spell error...%d%%"
764 (/ (* 100 (point)) (point-max)))
765 (if (looking-at "\"")
766 (checkdoc-ispell-docstring-engine
767 (save-excursion (forward-sexp 1) (point-marker)))))
768 (message "Checkdoc: Done."))))
769
770
bca0d607 771(defun checkdoc-next-error (enable-fix)
5b531322 772 "Find and return the next checkdoc error list, or nil.
bca0d607 773Only documentation strings are checked.
b08b261e
JB
774An error list is of the form (WARNING . POSITION) where WARNING is the
775warning text, and POSITION is the point in the buffer where the error
776was found. We can use points and not markers because we promise not
777to edit the buffer before point without re-executing this check.
bca0d607
EL
778Argument ENABLE-FIX will enable auto-fixing while looking for the next
779error. This argument assumes that the cursor is already positioned to
780perform the fix."
781 (if enable-fix
782 (checkdoc-this-string-valid)
783 (let ((msg nil) (p (point))
784 (checkdoc-autofix-flag nil))
785 (condition-case nil
786 (while (and (not msg) (checkdoc-next-docstring))
787 (message "Searching for doc string error...%d%%"
788 (/ (* 100 (point)) (point-max)))
789 (if (setq msg (checkdoc-this-string-valid))
790 (setq msg (cons msg (point)))))
791 ;; Quit.. restore position, Other errors, leave alone
792 (quit (goto-char p)))
793 msg)))
794
795(defun checkdoc-next-message-error (enable-fix)
c13599b6 796 "Find and return the next checkdoc message related error list, or nil.
bca0d607
EL
797Only text for error and `y-or-n-p' strings are checked. See
798`checkdoc-next-error' for details on the return value.
799Argument ENABLE-FIX turns on the auto-fix feature. This argument
800assumes that the cursor is already positioned to perform the fix."
801 (if enable-fix
802 (checkdoc-message-text-engine)
803 (let ((msg nil) (p (point)) (type nil)
804 (checkdoc-autofix-flag nil))
805 (condition-case nil
806 (while (and (not msg)
807 (setq type
808 (checkdoc-message-text-next-string (point-max))))
809 (message "Searching for message string error...%d%%"
810 (/ (* 100 (point)) (point-max)))
811 (if (setq msg (checkdoc-message-text-engine type))
812 (setq msg (cons msg (point)))))
813 ;; Quit.. restore position, Other errors, leave alone
814 (quit (goto-char p)))
815 msg)))
816
817(defun checkdoc-recursive-edit (msg)
818 "Enter recursive edit to permit a user to fix some error checkdoc has found.
819MSG is the error that was found, which is displayed in a help buffer."
820 (with-output-to-temp-buffer "*Checkdoc Help*"
1398b795
SM
821 (mapc #'princ
822 (list "Error message:\n " msg
823 "\n\nEdit to fix this problem, and press C-M-c to continue.")))
bca0d607
EL
824 (shrink-window-if-larger-than-buffer
825 (get-buffer-window "*Checkdoc Help*"))
826 (message "When you're done editing press C-M-c to continue.")
827 (unwind-protect
828 (recursive-edit)
829 (if (get-buffer-window "*Checkdoc Help*")
830 (progn
831 (delete-window (get-buffer-window "*Checkdoc Help*"))
832 (kill-buffer "*Checkdoc Help*")))))
833
834;;;###autoload
835(defun checkdoc-eval-current-buffer ()
836 "Evaluate and check documentation for the current buffer.
837Evaluation is done first because good documentation for something that
838doesn't work is just not useful. Comments, doc strings, and rogue
839spacing are all verified."
840 (interactive)
b372cfa9 841 (eval-buffer nil)
bca0d607 842 (checkdoc-current-buffer t))
5b531322
KH
843
844;;;###autoload
bca0d607
EL
845(defun checkdoc-current-buffer (&optional take-notes)
846 "Check current buffer for document, comment, error style, and rogue spaces.
847With a prefix argument (in Lisp, the argument TAKE-NOTES),
848store all errors found in a warnings buffer,
849otherwise stop after the first error."
850 (interactive "P")
32226619
JB
851 (if (called-interactively-p 'interactive)
852 (message "Checking buffer for style..."))
bca0d607
EL
853 ;; Assign a flag to spellcheck flag
854 (let ((checkdoc-spellcheck-documentation-flag
84003f38
RS
855 (car (memq checkdoc-spellcheck-documentation-flag
856 '(buffer t))))
bca0d607
EL
857 (checkdoc-autofix-flag (if take-notes 'never
858 checkdoc-autofix-flag))
859 (checkdoc-generate-compile-warnings-flag
860 (or take-notes checkdoc-generate-compile-warnings-flag)))
861 (if take-notes
862 (checkdoc-start-section "checkdoc-current-buffer"))
863 ;; every test is responsible for returning the cursor.
864 (or (and buffer-file-name ;; only check comments in a file
865 (checkdoc-comments))
866 (checkdoc-start)
867 (checkdoc-message-text)
868 (checkdoc-rogue-spaces)
32226619 869 (not (called-interactively-p 'interactive))
bca0d607
EL
870 (if take-notes (checkdoc-show-diagnostics))
871 (message "Checking buffer for style...Done."))))
872
873;;;###autoload
874(defun checkdoc-start (&optional take-notes)
875 "Start scanning the current buffer for documentation string style errors.
876Only documentation strings are checked.
877Use `checkdoc-continue' to continue checking if an error cannot be fixed.
5b531322
KH
878Prefix argument TAKE-NOTES means to collect all the warning messages into
879a separate buffer."
880 (interactive "P")
881 (let ((p (point)))
882 (goto-char (point-min))
32226619 883 (if (and take-notes (called-interactively-p 'interactive))
bca0d607 884 (checkdoc-start-section "checkdoc-start"))
5b531322
KH
885 (checkdoc-continue take-notes)
886 ;; Go back since we can't be here without success above.
887 (goto-char p)
888 nil))
889
890;;;###autoload
891(defun checkdoc-continue (&optional take-notes)
bca0d607 892 "Find the next doc string in the current buffer which has a style error.
5b531322 893Prefix argument TAKE-NOTES means to continue through the whole buffer and
bca0d607
EL
894save warnings in a separate buffer. Second optional argument START-POINT
895is the starting location. If this is nil, `point-min' is used instead."
5b531322 896 (interactive "P")
96ef1feb 897 (let ((wrong nil) (msg nil)
5b531322
KH
898 ;; Assign a flag to spellcheck flag
899 (checkdoc-spellcheck-documentation-flag
84003f38
RS
900 (car (memq checkdoc-spellcheck-documentation-flag
901 '(buffer t))))
bca0d607
EL
902 (checkdoc-autofix-flag (if take-notes 'never
903 checkdoc-autofix-flag))
904 (checkdoc-generate-compile-warnings-flag
905 (or take-notes checkdoc-generate-compile-warnings-flag)))
5b531322
KH
906 (save-excursion
907 ;; If we are taking notes, encompass the whole buffer, otherwise
908 ;; the user is navigating down through the buffer.
5b531322 909 (while (and (not wrong) (checkdoc-next-docstring))
bca0d607 910 ;; OK, let's look at the doc string.
04f3f5a2 911 (setq msg (checkdoc-this-string-valid))
bca0d607 912 (if msg (setq wrong (point)))))
5b531322
KH
913 (if wrong
914 (progn
915 (goto-char wrong)
bca0d607 916 (if (not take-notes)
71873e2b 917 (user-error "%s" (checkdoc-error-text msg)))))
bca0d607 918 (checkdoc-show-diagnostics)
32226619 919 (if (called-interactively-p 'interactive)
bca0d607 920 (message "No style warnings."))))
5b531322
KH
921
922(defun checkdoc-next-docstring ()
5fecb21a
RS
923 "Move to the next doc string after point, and return t.
924Return nil if there are no more doc strings."
5b531322
KH
925 (if (not (re-search-forward checkdoc-defun-regexp nil t))
926 nil
927 ;; search drops us after the identifier. The next sexp is either
928 ;; the argument list or the value of the variable. skip it.
929 (forward-sexp 1)
930 (skip-chars-forward " \n\t")
931 t))
932
23b809c2 933;;;###autoload
5b531322 934(defun checkdoc-comments (&optional take-notes)
5fecb21a 935 "Find missing comment sections in the current Emacs Lisp file.
5b531322
KH
936Prefix argument TAKE-NOTES non-nil means to save warnings in a
937separate buffer. Otherwise print a message. This returns the error
938if there is one."
939 (interactive "P")
940 (if take-notes (checkdoc-start-section "checkdoc-comments"))
941 (if (not buffer-file-name)
1398b795 942 (error "Can only check comments for a file buffer"))
5b531322 943 (let* ((checkdoc-spellcheck-documentation-flag
84003f38
RS
944 (car (memq checkdoc-spellcheck-documentation-flag
945 '(buffer t))))
bca0d607 946 (checkdoc-autofix-flag (if take-notes 'never checkdoc-autofix-flag))
bca0d607 947 (e (checkdoc-file-comments-engine))
1398b795
SM
948 (checkdoc-generate-compile-warnings-flag
949 (or take-notes checkdoc-generate-compile-warnings-flag)))
71873e2b 950 (if e (user-error "%s" (checkdoc-error-text e)))
bca0d607 951 (checkdoc-show-diagnostics)
5b531322
KH
952 e))
953
954;;;###autoload
bca0d607 955(defun checkdoc-rogue-spaces (&optional take-notes interact)
5b531322
KH
956 "Find extra spaces at the end of lines in the current file.
957Prefix argument TAKE-NOTES non-nil means to save warnings in a
958separate buffer. Otherwise print a message. This returns the error
bca0d607
EL
959if there is one.
960Optional argument INTERACT permits more interactive fixing."
5b531322
KH
961 (interactive "P")
962 (if take-notes (checkdoc-start-section "checkdoc-rogue-spaces"))
bca0d607
EL
963 (let* ((checkdoc-autofix-flag (if take-notes 'never checkdoc-autofix-flag))
964 (e (checkdoc-rogue-space-check-engine nil nil interact))
1398b795
SM
965 (checkdoc-generate-compile-warnings-flag
966 (or take-notes checkdoc-generate-compile-warnings-flag)))
32226619 967 (if (not (called-interactively-p 'interactive))
5b531322 968 e
bca0d607 969 (if e
274f1353 970 (message "%s" (checkdoc-error-text e))
bca0d607
EL
971 (checkdoc-show-diagnostics)
972 (message "Space Check: done.")))))
5b531322 973
bca0d607
EL
974;;;###autoload
975(defun checkdoc-message-text (&optional take-notes)
976 "Scan the buffer for occurrences of the error function, and verify text.
977Optional argument TAKE-NOTES causes all errors to be logged."
978 (interactive "P")
979 (if take-notes (checkdoc-start-section "checkdoc-message-text"))
980 (let* ((p (point)) e
981 (checkdoc-autofix-flag (if take-notes 'never checkdoc-autofix-flag))
982 (checkdoc-generate-compile-warnings-flag
983 (or take-notes checkdoc-generate-compile-warnings-flag)))
984 (setq e (checkdoc-message-text-search))
32226619 985 (if (not (called-interactively-p 'interactive))
bca0d607
EL
986 e
987 (if e
71873e2b 988 (user-error "%s" (checkdoc-error-text e))
bca0d607
EL
989 (checkdoc-show-diagnostics)))
990 (goto-char p))
32226619
JB
991 (if (called-interactively-p 'interactive)
992 (message "Checking interactive message text...done.")))
a1506d29 993
5b531322
KH
994;;;###autoload
995(defun checkdoc-eval-defun ()
6deb1543 996 "Evaluate the current form with `eval-defun' and check its documentation.
5b531322
KH
997Evaluation is done first so the form will be read before the
998documentation is checked. If there is a documentation error, then the display
999of what was evaluated will be overwritten by the diagnostic message."
1000 (interactive)
b0429158 1001 (call-interactively #'eval-defun)
5b531322
KH
1002 (checkdoc-defun))
1003
1004;;;###autoload
1005(defun checkdoc-defun (&optional no-error)
5fecb21a
RS
1006 "Examine the doc string of the function or variable under point.
1007Call `error' if the doc string has problems. If NO-ERROR is
5b531322 1008non-nil, then do not call error, but call `message' instead.
5fecb21a 1009If the doc string passes the test, then check the function for rogue white
5b531322
KH
1010space at the end of each line."
1011 (interactive)
1012 (save-excursion
1013 (beginning-of-defun)
1014 (if (not (looking-at checkdoc-defun-regexp))
1015 ;; I found this more annoying than useful.
1016 ;;(if (not no-error)
bca0d607 1017 ;; (message "Cannot check this sexp's doc string."))
5b531322
KH
1018 nil
1019 ;; search drops us after the identifier. The next sexp is either
1020 ;; the argument list or the value of the variable. skip it.
1021 (goto-char (match-end 0))
1022 (forward-sexp 1)
1023 (skip-chars-forward " \n\t")
1024 (let* ((checkdoc-spellcheck-documentation-flag
84003f38
RS
1025 (car (memq checkdoc-spellcheck-documentation-flag
1026 '(defun t))))
a4370a77 1027 (beg (save-excursion (beginning-of-defun) (point)))
71873e2b
SM
1028 (end (save-excursion (end-of-defun) (point))))
1029 (dolist (fun (list #'checkdoc-this-string-valid
1030 (lambda () (checkdoc-message-text-search beg end))
1031 (lambda () (checkdoc-rogue-space-check-engine beg end))))
1032 (let ((msg (funcall fun)))
1033 (if msg (if no-error
1034 (message "%s" (checkdoc-error-text msg))
1035 (user-error "%s" (checkdoc-error-text msg))))))
32226619
JB
1036 (if (called-interactively-p 'interactive)
1037 (message "Checkdoc: done."))))))
5b531322
KH
1038
1039;;; Ispell interface for forcing a spell check
1040;;
1041
bca0d607 1042;;;###autoload
b0429158 1043(defun checkdoc-ispell ()
bca0d607
EL
1044 "Check the style and spelling of everything interactively.
1045Calls `checkdoc' with spell-checking turned on.
b0429158 1046Prefix argument is the same as for `checkdoc'"
bca0d607
EL
1047 (interactive)
1048 (let ((checkdoc-spellcheck-documentation-flag t))
b0429158 1049 (call-interactively #'checkdoc nil current-prefix-arg)))
bca0d607 1050
5b531322 1051;;;###autoload
b0429158 1052(defun checkdoc-ispell-current-buffer ()
bca0d607 1053 "Check the style and spelling of the current buffer.
5b531322 1054Calls `checkdoc-current-buffer' with spell-checking turned on.
b0429158 1055Prefix argument is the same as for `checkdoc-current-buffer'"
5b531322
KH
1056 (interactive)
1057 (let ((checkdoc-spellcheck-documentation-flag t))
b0429158 1058 (call-interactively #'checkdoc-current-buffer nil current-prefix-arg)))
5b531322
KH
1059
1060;;;###autoload
b0429158 1061(defun checkdoc-ispell-interactive ()
5b531322
KH
1062 "Check the style and spelling of the current buffer interactively.
1063Calls `checkdoc-interactive' with spell-checking turned on.
b0429158 1064Prefix argument is the same as for `checkdoc-interactive'"
5b531322
KH
1065 (interactive)
1066 (let ((checkdoc-spellcheck-documentation-flag t))
b0429158 1067 (call-interactively #'checkdoc-interactive nil current-prefix-arg)))
5b531322
KH
1068
1069;;;###autoload
b0429158 1070(defun checkdoc-ispell-message-interactive ()
bca0d607
EL
1071 "Check the style and spelling of message text interactively.
1072Calls `checkdoc-message-interactive' with spell-checking turned on.
b0429158 1073Prefix argument is the same as for `checkdoc-message-interactive'"
bca0d607
EL
1074 (interactive)
1075 (let ((checkdoc-spellcheck-documentation-flag t))
b0429158
SM
1076 (call-interactively #'checkdoc-message-interactive
1077 nil current-prefix-arg)))
bca0d607
EL
1078
1079;;;###autoload
b0429158 1080(defun checkdoc-ispell-message-text ()
bca0d607
EL
1081 "Check the style and spelling of message text interactively.
1082Calls `checkdoc-message-text' with spell-checking turned on.
b0429158 1083Prefix argument is the same as for `checkdoc-message-text'"
bca0d607
EL
1084 (interactive)
1085 (let ((checkdoc-spellcheck-documentation-flag t))
b0429158 1086 (call-interactively #'checkdoc-message-text nil current-prefix-arg)))
bca0d607
EL
1087
1088;;;###autoload
b0429158 1089(defun checkdoc-ispell-start ()
5b531322 1090 "Check the style and spelling of the current buffer.
bca0d607 1091Calls `checkdoc-start' with spell-checking turned on.
b0429158 1092Prefix argument is the same as for `checkdoc-start'"
5b531322
KH
1093 (interactive)
1094 (let ((checkdoc-spellcheck-documentation-flag t))
b0429158 1095 (call-interactively #'checkdoc-start nil current-prefix-arg)))
5b531322
KH
1096
1097;;;###autoload
b0429158 1098(defun checkdoc-ispell-continue ()
5b531322
KH
1099 "Check the style and spelling of the current buffer after point.
1100Calls `checkdoc-continue' with spell-checking turned on.
b0429158 1101Prefix argument is the same as for `checkdoc-continue'"
5b531322
KH
1102 (interactive)
1103 (let ((checkdoc-spellcheck-documentation-flag t))
b0429158 1104 (call-interactively #'checkdoc-continue nil current-prefix-arg)))
5b531322
KH
1105
1106;;;###autoload
b0429158 1107(defun checkdoc-ispell-comments ()
5b531322
KH
1108 "Check the style and spelling of the current buffer's comments.
1109Calls `checkdoc-comments' with spell-checking turned on.
b0429158 1110Prefix argument is the same as for `checkdoc-comments'"
5b531322
KH
1111 (interactive)
1112 (let ((checkdoc-spellcheck-documentation-flag t))
b0429158 1113 (call-interactively #'checkdoc-comments nil current-prefix-arg)))
5b531322
KH
1114
1115;;;###autoload
b0429158 1116(defun checkdoc-ispell-defun ()
5fecb21a 1117 "Check the style and spelling of the current defun with Ispell.
5b531322 1118Calls `checkdoc-defun' with spell-checking turned on.
b0429158 1119Prefix argument is the same as for `checkdoc-defun'"
5b531322
KH
1120 (interactive)
1121 (let ((checkdoc-spellcheck-documentation-flag t))
b0429158 1122 (call-interactively #'checkdoc-defun nil current-prefix-arg)))
5b531322 1123
bca0d607
EL
1124;;; Error Management
1125;;
1126;; Errors returned from checkdoc functions can have various
1127;; features and behaviors, so we need some ways of specifying
1128;; them, and making them easier to use in the wacked-out interfaces
1129;; people are requesting
1130(defun checkdoc-create-error (text start end &optional unfixable)
1131 "Used to create the return error text returned from all engines.
1132TEXT is the descriptive text of the error. START and END define the region
1133it is sensible to highlight when describing the problem.
1134Optional argument UNFIXABLE means that the error has no auto-fix available.
1135
1136A list of the form (TEXT START END UNFIXABLE) is returned if we are not
1137generating a buffered list of errors."
1138 (if checkdoc-generate-compile-warnings-flag
1139 (progn (checkdoc-error start text)
1140 nil)
1141 (list text start end unfixable)))
1142
1143(defun checkdoc-error-text (err)
1144 "Return the text specified in the checkdoc ERR."
1145 ;; string-p part is for backwards compatibility
1146 (if (stringp err) err (car err)))
1147
1148(defun checkdoc-error-start (err)
1149 "Return the start point specified in the checkdoc ERR."
1150 ;; string-p part is for backwards compatibility
1151 (if (stringp err) nil (nth 1 err)))
1152
1153(defun checkdoc-error-end (err)
1154 "Return the end point specified in the checkdoc ERR."
1155 ;; string-p part is for backwards compatibility
1156 (if (stringp err) nil (nth 2 err)))
1157
1158(defun checkdoc-error-unfixable (err)
1159 "Return the t if we cannot autofix the error specified in the checkdoc ERR."
1160 ;; string-p part is for backwards compatibility
1161 (if (stringp err) nil (nth 3 err)))
1162
5b531322
KH
1163;;; Minor Mode specification
1164;;
5b531322 1165
82bc80bf 1166(defvar checkdoc-minor-mode-map
5b531322
KH
1167 (let ((map (make-sparse-keymap))
1168 (pmap (make-sparse-keymap)))
1169 ;; Override some bindings
1170 (define-key map "\C-\M-x" 'checkdoc-eval-defun)
bca0d607 1171 (define-key map "\C-x`" 'checkdoc-continue)
f8246027 1172 (if (not (featurep 'xemacs))
5b531322
KH
1173 (define-key map [menu-bar emacs-lisp eval-buffer]
1174 'checkdoc-eval-current-buffer))
bca0d607 1175 ;; Add some new bindings under C-c ?
5b531322
KH
1176 (define-key pmap "x" 'checkdoc-defun)
1177 (define-key pmap "X" 'checkdoc-ispell-defun)
1178 (define-key pmap "`" 'checkdoc-continue)
1179 (define-key pmap "~" 'checkdoc-ispell-continue)
bca0d607
EL
1180 (define-key pmap "s" 'checkdoc-start)
1181 (define-key pmap "S" 'checkdoc-ispell-start)
5b531322
KH
1182 (define-key pmap "d" 'checkdoc)
1183 (define-key pmap "D" 'checkdoc-ispell)
5b531322
KH
1184 (define-key pmap "b" 'checkdoc-current-buffer)
1185 (define-key pmap "B" 'checkdoc-ispell-current-buffer)
1186 (define-key pmap "e" 'checkdoc-eval-current-buffer)
a4370a77 1187 (define-key pmap "m" 'checkdoc-message-text)
bca0d607 1188 (define-key pmap "M" 'checkdoc-ispell-message-text)
5b531322
KH
1189 (define-key pmap "c" 'checkdoc-comments)
1190 (define-key pmap "C" 'checkdoc-ispell-comments)
1191 (define-key pmap " " 'checkdoc-rogue-spaces)
1192
1193 ;; bind our submap into map
1194 (define-key map "\C-c?" pmap)
1195 map)
1196 "Keymap used to override evaluation key-bindings for documentation checking.")
1197
1198;; Add in a menubar with easy-menu
1199
82bc80bf 1200(easy-menu-define
1398b795
SM
1201 nil checkdoc-minor-mode-map "Checkdoc Minor Mode Menu"
1202 '("CheckDoc"
1203 ["Interactive Buffer Style Check" checkdoc t]
1204 ["Interactive Buffer Style and Spelling Check" checkdoc-ispell t]
1205 ["Check Buffer" checkdoc-current-buffer t]
1206 ["Check and Spell Buffer" checkdoc-ispell-current-buffer t]
1207 "---"
1208 ["Interactive Style Check" checkdoc-interactive t]
1209 ["Interactive Style and Spelling Check" checkdoc-ispell-interactive t]
1210 ["Find First Style Error" checkdoc-start t]
1211 ["Find First Style or Spelling Error" checkdoc-ispell-start t]
1212 ["Next Style Error" checkdoc-continue t]
1213 ["Next Style or Spelling Error" checkdoc-ispell-continue t]
1214 ["Interactive Message Text Style Check" checkdoc-message-interactive t]
1215 ["Interactive Message Text Style and Spelling Check"
1216 checkdoc-ispell-message-interactive t]
1217 ["Check Message Text" checkdoc-message-text t]
1218 ["Check and Spell Message Text" checkdoc-ispell-message-text t]
1219 ["Check Comment Style" checkdoc-comments buffer-file-name]
1220 ["Check Comment Style and Spelling" checkdoc-ispell-comments
1221 buffer-file-name]
1222 ["Check for Rogue Spaces" checkdoc-rogue-spaces t]
1223 "---"
1224 ["Check Defun" checkdoc-defun t]
1225 ["Check and Spell Defun" checkdoc-ispell-defun t]
1226 ["Check and Evaluate Defun" checkdoc-eval-defun t]
1227 ["Check and Evaluate Buffer" checkdoc-eval-current-buffer t]
1228 ))
5b531322
KH
1229;; XEmacs requires some weird stuff to add this menu in a minor mode.
1230;; What is it?
1231
5b531322 1232;;;###autoload
b7f30cf5 1233(define-minor-mode checkdoc-minor-mode
ac6c8639
CY
1234 "Toggle automatic docstring checking (Checkdoc minor mode).
1235With a prefix argument ARG, enable Checkdoc minor mode if ARG is
1236positive, and disable it otherwise. If called from Lisp, enable
1237the mode if ARG is omitted or nil.
5b531322 1238
5fecb21a 1239In Checkdoc minor mode, the usual bindings for `eval-defun' which is
07051573 1240bound to \\<checkdoc-minor-mode-map>\\[checkdoc-eval-defun] and `checkdoc-eval-current-buffer' are overridden to include
5b531322
KH
1241checking of documentation strings.
1242
82bc80bf 1243\\{checkdoc-minor-mode-map}"
b92317dc 1244 nil checkdoc-minor-mode-string nil
90620d3d 1245 :group 'checkdoc)
5b531322
KH
1246
1247;;; Subst utils
1248;;
1249(defsubst checkdoc-run-hooks (hookvar &rest args)
1250 "Run hooks in HOOKVAR with ARGS."
1251 (if (fboundp 'run-hook-with-args-until-success)
b0429158 1252 (apply #'run-hook-with-args-until-success hookvar args)
5b531322 1253 ;; This method was similar to above. We ignore the warning
5fecb21a 1254 ;; since we will use the above for future Emacs versions
b0429158 1255 (apply #'run-hook-with-args hookvar args)))
5b531322
KH
1256
1257(defsubst checkdoc-create-common-verbs-regexp ()
1258 "Rebuild the contents of `checkdoc-common-verbs-regexp'."
1259 (or checkdoc-common-verbs-regexp
1260 (setq checkdoc-common-verbs-regexp
1261 (concat "\\<\\("
1262 (mapconcat (lambda (e) (concat (car e)))
1263 checkdoc-common-verbs-wrong-voice "\\|")
1264 "\\)\\>"))))
1265
1266;; Profiler says this is not yet faster than just calling assoc
1267;;(defun checkdoc-word-in-alist-vector (word vector)
1268;; "Check to see if WORD is in the car of an element of VECTOR.
1269;;VECTOR must be sorted. The CDR should be a replacement. Since the
1270;;word list is getting bigger, it is time for a quick bisecting search."
1271;; (let ((max (length vector)) (min 0) i
1272;; (found nil) (fw nil))
1273;; (setq i (/ max 2))
1274;; (while (and (not found) (/= min max))
1275;; (setq fw (car (aref vector i)))
1276;; (cond ((string= word fw) (setq found (cdr (aref vector i))))
1277;; ((string< word fw) (setq max i))
1278;; (t (setq min i)))
1279;; (setq i (/ (+ max min) 2))
1280;; )
1281;; found))
1282
1283;;; Checking engines
1284;;
1285(defun checkdoc-this-string-valid ()
5fecb21a 1286 "Return a message string if the current doc string is invalid.
5b531322
KH
1287Check for style only, such as the first line always being a complete
1288sentence, whitespace restrictions, and making sure there are no
1289hard-coded key-codes such as C-[char] or mouse-[number] in the comment.
1290See the style guide in the Emacs Lisp manual for more details."
1291
5fecb21a 1292 ;; Jump over comments between the last object and the doc string
5b531322
KH
1293 (while (looking-at "[ \t\n]*;")
1294 (forward-line 1)
1295 (beginning-of-line)
1296 (skip-chars-forward " \n\t"))
1297
bca0d607
EL
1298 (let ((fp (checkdoc-defun-info))
1299 (err nil))
1300 (setq
1301 err
1302 ;; * Every command, function, or variable intended for users to know
1303 ;; about should have a documentation string.
1304 ;;
1305 ;; * An internal variable or subroutine of a Lisp program might as well
1306 ;; have a documentation string. In earlier Emacs versions, you could
1307 ;; save space by using a comment instead of a documentation string,
1308 ;; but that is no longer the case.
1309 (if (and (not (nth 1 fp)) ; not a variable
1310 (or (nth 2 fp) ; is interactive
1311 checkdoc-force-docstrings-flag) ;or we always complain
1312 (not (checkdoc-char= (following-char) ?\"))) ; no doc string
1313 ;; Sometimes old code has comments where the documentation should
84f473b0 1314 ;; be. Let's see if we can find the comment, and offer to turn it
bca0d607 1315 ;; into documentation for them.
eb83be8a
DL
1316 (let ((have-comment nil)
1317 (comment-start ";")) ; in case it's not default
bca0d607
EL
1318 (condition-case nil
1319 (progn
1320 (forward-sexp -1)
1321 (forward-sexp 1)
1322 (skip-chars-forward "\n \t")
1323 (setq have-comment (looking-at comment-start)))
1324 (error nil))
1325 (if have-comment
1326 (if (or (eq checkdoc-autofix-flag
1327 'automatic-then-never)
1328 (checkdoc-y-or-n-p
1329 "Convert comment to documentation? "))
1330 (save-excursion
1331 ;; Our point is at the beginning of the comment!
1332 ;; Insert a quote, then remove the comment chars.
1333 (insert "\"")
84f473b0 1334 (let ((docstring-start-point (point)))
bca0d607 1335 (while (looking-at comment-start)
84f473b0
EL
1336 (while (looking-at comment-start)
1337 (delete-char 1))
1338 (if (looking-at "[ \t]+")
1339 (delete-region (match-beginning 0) (match-end 0)))
1340 (forward-line 1)
1341 (beginning-of-line)
1342 (skip-chars-forward " \t")
1343 (if (looking-at comment-start)
1344 (progn
1345 (beginning-of-line)
1346 (zap-to-char 1 ?\;))))
bca0d607 1347 (beginning-of-line)
84f473b0
EL
1348 (forward-char -1)
1349 (insert "\"")
1350 (forward-char -1)
1351 ;; quote any double-quote characters in the comment.
1352 (while (search-backward "\"" docstring-start-point t)
1353 (insert "\\"))
1354 (if (eq checkdoc-autofix-flag 'automatic-then-never)
1355 (setq checkdoc-autofix-flag 'never))))
bca0d607
EL
1356 (checkdoc-create-error
1357 "You should convert this comment to documentation"
5ed619e0 1358 (point) (line-end-position)))
bca0d607
EL
1359 (checkdoc-create-error
1360 (if (nth 2 fp)
1361 "All interactive functions should have documentation"
1362 "All variables and subroutines might as well have a \
1363documentation string")
1364 (point) (+ (point) 1) t)))))
1365 (if (and (not err) (looking-at "\""))
cca982d0
SM
1366 (with-syntax-table checkdoc-syntax-table
1367 (checkdoc-this-string-valid-engine fp))
bca0d607
EL
1368 err)))
1369
1370(defun checkdoc-this-string-valid-engine (fp)
1371 "Return an error list or string if the current doc string is invalid.
5b531322 1372Depends on `checkdoc-this-string-valid' to reset the syntax table so that
bca0d607 1373regexp short cuts work. FP is the function defun information."
5b531322
KH
1374 (let ((case-fold-search nil)
1375 ;; Use a marker so if an early check modifies the text,
537b04b9 1376 ;; we won't accidentally lose our place. This could cause
5fecb21a 1377 ;; end-of doc string whitespace to also delete the " char.
bca0d607
EL
1378 (s (point))
1379 (e (if (looking-at "\"")
1380 (save-excursion (forward-sexp 1) (point-marker))
1381 (point))))
5b531322
KH
1382 (or
1383 ;; * *Do not* indent subsequent lines of a documentation string so that
1384 ;; the text is lined up in the source code with the text of the first
1385 ;; line. This looks nice in the source code, but looks bizarre when
1386 ;; users view the documentation. Remember that the indentation
1387 ;; before the starting double-quote is not part of the string!
1388 (save-excursion
1389 (forward-line 1)
1390 (beginning-of-line)
1391 (if (and (< (point) e)
1392 (looking-at "\\([ \t]+\\)[^ \t\n]"))
1393 (if (checkdoc-autofix-ask-replace (match-beginning 1)
1394 (match-end 1)
a4370a77 1395 "Remove this whitespace? "
5b531322
KH
1396 "")
1397 nil
bca0d607
EL
1398 (checkdoc-create-error
1399 "Second line should not have indentation"
1400 (match-beginning 1)
1401 (match-end 1)))))
9c07782e
GM
1402 ;; * Check for '(' in column 0.
1403 (save-excursion
1404 (when (re-search-forward "^(" e t)
1405 (if (checkdoc-autofix-ask-replace (match-beginning 0)
1406 (match-end 0)
1407 "Escape this '('? "
1408 "\\(")
1409 nil
1410 (checkdoc-create-error
1411 "Open parenthesis in column 0 should be escaped"
1412 (match-beginning 0) (match-end 0)))))
5b531322
KH
1413 ;; * Do not start or end a documentation string with whitespace.
1414 (let (start end)
1415 (if (or (if (looking-at "\"\\([ \t\n]+\\)")
1416 (setq start (match-beginning 1)
1417 end (match-end 1)))
1418 (save-excursion
1419 (forward-sexp 1)
1420 (forward-char -1)
1421 (if (/= (skip-chars-backward " \t\n") 0)
1422 (setq start (point)
1423 end (1- e)))))
1424 (if (checkdoc-autofix-ask-replace
a4370a77 1425 start end "Remove this whitespace? " "")
5b531322 1426 nil
bca0d607
EL
1427 (checkdoc-create-error
1428 "Documentation strings should not start or end with whitespace"
1429 start end))))
5b531322
KH
1430 ;; * The first line of the documentation string should consist of one
1431 ;; or two complete sentences that stand on their own as a summary.
1432 ;; `M-x apropos' displays just the first line, and if it doesn't
1433 ;; stand on its own, the result looks bad. In particular, start the
1434 ;; first line with a capital letter and end with a period.
1435 (save-excursion
1436 (end-of-line)
1437 (skip-chars-backward " \t\n")
1438 (if (> (point) e) (goto-char e)) ;of the form (defun n () "doc" nil)
1439 (forward-char -1)
1440 (cond
1441 ((and (checkdoc-char= (following-char) ?\")
1442 ;; A backslashed double quote at the end of a sentence
1443 (not (checkdoc-char= (preceding-char) ?\\)))
1444 ;; We might have to add a period in this case
1445 (forward-char -1)
84f473b0 1446 (if (looking-at "[.!?]")
5b531322
KH
1447 nil
1448 (forward-char 1)
1449 (if (checkdoc-autofix-ask-replace
a4370a77 1450 (point) (1+ (point)) "Add period to sentence? "
5b531322
KH
1451 ".\"" t)
1452 nil
bca0d607
EL
1453 (checkdoc-create-error
1454 "First sentence should end with punctuation"
1455 (point) (1+ (point))))))
84f473b0 1456 ((looking-at "[\\!?;:.)]")
5b531322
KH
1457 ;; These are ok
1458 nil)
bca0d607
EL
1459 ((and checkdoc-permit-comma-termination-flag (looking-at ","))
1460 nil)
5b531322 1461 (t
bca0d607 1462 ;; If it is not a complete sentence, let's see if we can
5b531322
KH
1463 ;; predict a clever way to make it one.
1464 (let ((msg "First line is not a complete sentence")
1465 (e (point)))
1466 (beginning-of-line)
1467 (if (re-search-forward "\\. +" e t)
1468 ;; Here we have found a complete sentence, but no break.
1469 (if (checkdoc-autofix-ask-replace
1470 (1+ (match-beginning 0)) (match-end 0)
a4370a77 1471 "First line not a complete sentence. Add RET here? "
5b531322
KH
1472 "\n" t)
1473 (let (l1 l2)
76fc02b6 1474 (end-of-line 2)
5b531322
KH
1475 (setq l1 (current-column)
1476 l2 (save-excursion
76fc02b6 1477 (end-of-line 2)
5b531322
KH
1478 (current-column)))
1479 (if (> (+ l1 l2 1) 80)
5fecb21a 1480 (setq msg "Incomplete auto-fix; doc string \
a4370a77 1481may require more formatting")
5b531322
KH
1482 ;; We can merge these lines! Replace this CR
1483 ;; with a space.
1484 (delete-char 1) (insert " ")
1485 (setq msg nil))))
bca0d607 1486 ;; Let's see if there is enough room to draw the next
5b531322
KH
1487 ;; line's sentence up here. I often get hit w/
1488 ;; auto-fill moving my words around.
1489 (let ((numc (progn (end-of-line) (- 80 (current-column))))
1490 (p (point)))
1491 (forward-line 1)
1492 (beginning-of-line)
84f473b0 1493 (if (and (re-search-forward "[.!?:\"]\\([ \t\n]+\\|\"\\)"
76fc02b6 1494 (line-end-position) t)
5b531322
KH
1495 (< (current-column) numc))
1496 (if (checkdoc-autofix-ask-replace
1497 p (1+ p)
5fecb21a 1498 "1st line not a complete sentence. Join these lines? "
5b531322
KH
1499 " " t)
1500 (progn
1501 ;; They said yes. We have more fill work to do...
bca0d607
EL
1502 (goto-char (match-beginning 1))
1503 (delete-region (point) (match-end 1))
5b531322
KH
1504 (insert "\n")
1505 (setq msg nil))))))
bca0d607
EL
1506 (if msg
1507 (checkdoc-create-error msg s (save-excursion
1508 (goto-char s)
76fc02b6 1509 (line-end-position))))))))
5b531322
KH
1510 ;; Continuation of above. Make sure our sentence is capitalized.
1511 (save-excursion
1512 (skip-chars-forward "\"\\*")
1513 (if (looking-at "[a-z]")
1514 (if (checkdoc-autofix-ask-replace
1515 (match-beginning 0) (match-end 0)
a4370a77 1516 "Capitalize your sentence? " (upcase (match-string 0))
5b531322
KH
1517 t)
1518 nil
bca0d607
EL
1519 (checkdoc-create-error
1520 "First line should be capitalized"
1521 (match-beginning 0) (match-end 0)))
5b531322 1522 nil))
5b531322
KH
1523 ;; * Don't write key sequences directly in documentation strings.
1524 ;; Instead, use the `\\[...]' construct to stand for them.
1525 (save-excursion
1526 (let ((f nil) (m nil) (start (point))
04f3f5a2 1527 (re "[^`A-Za-z0-9_]\\([CMA]-[a-zA-Z]\\|\\(\\([CMA]-\\)?\
5b531322
KH
1528mouse-[0-3]\\)\\)\\>"))
1529 ;; Find the first key sequence not in a sample
1530 (while (and (not f) (setq m (re-search-forward re e t)))
1531 (setq f (not (checkdoc-in-sample-code-p start e))))
1532 (if m
bca0d607
EL
1533 (checkdoc-create-error
1534 (concat
1535 "Keycode " (match-string 1)
1536 " embedded in doc string. Use \\\\<keymap> & \\\\[function] "
1537 "instead")
1538 (match-beginning 1) (match-end 1) t))))
5b531322
KH
1539 ;; It is not practical to use `\\[...]' very many times, because
1540 ;; display of the documentation string will become slow. So use this
1541 ;; to describe the most important commands in your major mode, and
1542 ;; then use `\\{...}' to display the rest of the mode's keymap.
1543 (save-excursion
25d91d94
SM
1544 (if (and (re-search-forward "\\\\\\\\\\[\\w+" e t
1545 (1+ checkdoc-max-keyref-before-warn))
1546 (not (re-search-forward "\\\\\\\\{\\w+}" e t)))
bca0d607
EL
1547 (checkdoc-create-error
1548 "Too many occurrences of \\[function]. Use \\{keymap} instead"
1549 s (marker-position e))))
5fecb21a 1550 ;; Ambiguous quoted symbol. When a symbol is both bound and fbound,
a4370a77
EL
1551 ;; and is referred to in documentation, it should be prefixed with
1552 ;; something to disambiguate it. This check must be before the
1553 ;; 80 column check because it will probably break that.
1554 (save-excursion
1555 (let ((case-fold-search t)
bca0d607
EL
1556 (ret nil) mb me)
1557 (while (and (re-search-forward "`\\(\\sw\\(\\sw\\|\\s_\\)+\\)'" e t)
1558 (not ret))
1559 (let* ((ms1 (match-string 1))
1560 (sym (intern-soft ms1)))
1561 (setq mb (match-beginning 1)
1562 me (match-end 1))
1563 (if (and sym (boundp sym) (fboundp sym)
1564 (save-excursion
1565 (goto-char mb)
1566 (forward-word -1)
1567 (not (looking-at
1568 "variable\\|option\\|function\\|command\\|symbol"))))
a4370a77 1569 (if (checkdoc-autofix-ask-replace
bca0d607
EL
1570 mb me "Prefix this ambiguous symbol? " ms1 t)
1571 ;; We didn't actually replace anything. Here we find
a4370a77
EL
1572 ;; out what special word form they wish to use as
1573 ;; a prefix.
1574 (let ((disambiguate
1575 (completing-read
5b76833f 1576 "Disambiguating Keyword (default variable): "
a4370a77
EL
1577 '(("function") ("command") ("variable")
1578 ("option") ("symbol"))
1579 nil t nil nil "variable")))
1580 (goto-char (1- mb))
1581 (insert disambiguate " ")
1582 (forward-word 1))
1583 (setq ret
bca0d607
EL
1584 (format "Disambiguate %s by preceding w/ \
1585function,command,variable,option or symbol." ms1))))))
1586 (if ret
1587 (checkdoc-create-error ret mb me)
1588 nil)))
5b531322
KH
1589 ;; * Format the documentation string so that it fits in an
1590 ;; Emacs window on an 80-column screen. It is a good idea
1591 ;; for most lines to be no wider than 60 characters. The
1592 ;; first line can be wider if necessary to fit the
1593 ;; information that ought to be there.
1594 (save-excursion
bca0d607
EL
1595 (let ((start (point))
1596 (eol nil))
5b531322 1597 (while (and (< (point) e)
bca0d607
EL
1598 (or (progn (end-of-line) (setq eol (point))
1599 (< (current-column) 80))
5b531322
KH
1600 (progn (beginning-of-line)
1601 (re-search-forward "\\\\\\\\[[<{]"
bca0d607 1602 eol t))
5b531322
KH
1603 (checkdoc-in-sample-code-p start e)))
1604 (forward-line 1))
1605 (end-of-line)
1606 (if (and (< (point) e) (> (current-column) 80))
bca0d607
EL
1607 (checkdoc-create-error
1608 "Some lines are over 80 columns wide"
76fc02b6 1609 s (save-excursion (goto-char s) (line-end-position))))))
5b531322 1610 ;; Here we deviate to tests based on a variable or function.
bca0d607
EL
1611 ;; We must do this before checking for symbols in quotes because there
1612 ;; is a chance that just such a symbol might really be an argument.
5b531322
KH
1613 (cond ((eq (nth 1 fp) t)
1614 ;; This is if we are in a variable
1615 (or
1616 ;; * The documentation string for a variable that is a
bca0d607
EL
1617 ;; yes-or-no flag should start with words such as Non-nil
1618 ;; means..., to make it clear that all non-`nil' values are
5b531322
KH
1619 ;; equivalent and indicate explicitly what `nil' and non-`nil'
1620 ;; mean.
1621 ;; * If a user option variable records a true-or-false
1622 ;; condition, give it a name that ends in `-flag'.
1623
1624 ;; If the variable has -flag in the name, make sure
1625 (if (and (string-match "-flag$" (car fp))
1626 (not (looking-at "\"\\*?Non-nil\\s-+means\\s-+")))
bca0d607 1627 (checkdoc-create-error
995e028a 1628 "Flag variable doc strings should usually start: Non-nil means"
bca0d607 1629 s (marker-position e) t))
145b6377
SM
1630 ;; Don't rename variable to "foo-flag". This is unnecessary
1631 ;; and such names often end up inconvenient when the variable
1632 ;; is later expanded to non-boolean values. --Stef
5fecb21a 1633 ;; If the doc string starts with "Non-nil means"
145b6377
SM
1634 ;; (if (and (looking-at "\"\\*?Non-nil\\s-+means\\s-+")
1635 ;; (not (string-match "-flag$" (car fp))))
1636 ;; (let ((newname
1637 ;; (if (string-match "-p$" (car fp))
1638 ;; (concat (substring (car fp) 0 -2) "-flag")
1639 ;; (concat (car fp) "-flag"))))
1640 ;; (if (checkdoc-y-or-n-p
1641 ;; (format
1642 ;; "Rename to %s and Query-Replace all occurrences? "
1643 ;; newname))
1644 ;; (progn
1645 ;; (beginning-of-defun)
1646 ;; (query-replace-regexp
1647 ;; (concat "\\<" (regexp-quote (car fp)) "\\>")
1648 ;; newname))
1649 ;; (checkdoc-create-error
1650 ;; "Flag variable names should normally end in `-flag'" s
1651 ;; (marker-position e)))))
5b531322
KH
1652 ;; Done with variables
1653 ))
1654 (t
1655 ;; This if we are in a function definition
1656 (or
1657 ;; * When a function's documentation string mentions the value
1658 ;; of an argument of the function, use the argument name in
1659 ;; capital letters as if it were a name for that value. Thus,
1660 ;; the documentation string of the function `/' refers to its
1661 ;; second argument as `DIVISOR', because the actual argument
1662 ;; name is `divisor'.
1663
1664 ;; Addendum: Make sure they appear in the doc in the same
1665 ;; order that they are found in the arg list.
1666 (let ((args (cdr (cdr (cdr (cdr fp)))))
1667 (last-pos 0)
1668 (found 1)
1669 (order (and (nth 3 fp) (car (nth 3 fp))))
a4370a77
EL
1670 (nocheck (append '("&optional" "&rest") (nth 3 fp)))
1671 (inopts nil))
5b531322
KH
1672 (while (and args found (> found last-pos))
1673 (if (member (car args) nocheck)
a4370a77
EL
1674 (setq args (cdr args)
1675 inopts t)
5b531322
KH
1676 (setq last-pos found
1677 found (save-excursion
1678 (re-search-forward
1679 (concat "\\<" (upcase (car args))
1680 ;; Require whitespace OR
1681 ;; ITEMth<space> OR
1682 ;; ITEMs<space>
2e898692 1683 "\\(\\>\\|th\\>\\|s\\>\\|[.,;:]\\)")
5b531322
KH
1684 e t)))
1685 (if (not found)
1686 (let ((case-fold-search t))
bca0d607 1687 ;; If the symbol was not found, let's see if we
5b531322
KH
1688 ;; can find it with a different capitalization
1689 ;; and see if the user wants to capitalize it.
1690 (if (save-excursion
1691 (re-search-forward
84f473b0
EL
1692 (concat "\\<\\(" (car args)
1693 ;; Require whitespace OR
1694 ;; ITEMth<space> OR
1695 ;; ITEMs<space>
1696 "\\)\\(\\>\\|th\\>\\|s\\>\\)")
1697 e t))
5b531322
KH
1698 (if (checkdoc-autofix-ask-replace
1699 (match-beginning 1) (match-end 1)
1700 (format
995e028a 1701 "If this is the argument `%s', it should appear as %s. Fix? "
5b531322
KH
1702 (car args) (upcase (car args)))
1703 (upcase (car args)) t)
1704 (setq found (match-beginning 1))))))
1705 (if found (setq args (cdr args)))))
1706 (if (not found)
a4370a77
EL
1707 ;; It wasn't found at all! Offer to attach this new symbol
1708 ;; to the end of the documentation string.
bca0d607
EL
1709 (if (checkdoc-y-or-n-p
1710 (format
1711 "Add %s documentation to end of doc string? "
1712 (upcase (car args))))
5fecb21a 1713 ;; Now do some magic and invent a doc string.
a4370a77
EL
1714 (save-excursion
1715 (goto-char e) (forward-char -1)
1716 (insert "\n"
1717 (if inopts "Optional a" "A")
1718 "rgument " (upcase (car args))
1719 " ")
1720 (insert (read-string "Describe: "))
1721 (if (not (save-excursion (forward-char -1)
1722 (looking-at "[.?!]")))
1723 (insert "."))
1724 nil)
bca0d607
EL
1725 (checkdoc-create-error
1726 (format
995e028a 1727 "Argument `%s' should appear (as %s) in the doc string"
bca0d607
EL
1728 (car args) (upcase (car args)))
1729 s (marker-position e)))
5b531322
KH
1730 (if (or (and order (eq order 'yes))
1731 (and (not order) checkdoc-arguments-in-order-flag))
1732 (if (< found last-pos)
bca0d607
EL
1733 (checkdoc-create-error
1734 "Arguments occur in the doc string out of order"
1735 s (marker-position e) t)))))
1736 ;; * For consistency, phrase the verb in the first sentence of a
995e028a
RS
1737 ;; documentation string for functions as an imperative.
1738 ;; For instance, use `Return the cons of A and
bca0d607
EL
1739 ;; B.' in preference to `Returns the cons of A and B.'
1740 ;; Usually it looks good to do likewise for the rest of the
1741 ;; first paragraph. Subsequent paragraphs usually look better
1742 ;; if they have proper subjects.
1743 ;;
1744 ;; This is the least important of the above tests. Make sure
1745 ;; it occurs last.
1746 (and checkdoc-verb-check-experimental-flag
1747 (save-excursion
b08b261e 1748 ;; Maybe rebuild the monster-regexp
bca0d607
EL
1749 (checkdoc-create-common-verbs-regexp)
1750 (let ((lim (save-excursion
1751 (end-of-line)
1752 ;; check string-continuation
1753 (if (checkdoc-char= (preceding-char) ?\\)
76fc02b6
GM
1754 (line-end-position 2)
1755 (point))))
bca0d607
EL
1756 (rs nil) replace original (case-fold-search t))
1757 (while (and (not rs)
1758 (re-search-forward
1759 checkdoc-common-verbs-regexp
1760 lim t))
1761 (setq original (buffer-substring-no-properties
1762 (match-beginning 1) (match-end 1))
1763 rs (assoc (downcase original)
1764 checkdoc-common-verbs-wrong-voice))
1765 (if (not rs) (error "Verb voice alist corrupted"))
1766 (setq replace (let ((case-fold-search nil))
66e9f105
JB
1767 (if (string-match-p "^[A-Z]" original)
1768 (capitalize (cdr rs))
1769 (cdr rs))))
bca0d607
EL
1770 (if (checkdoc-autofix-ask-replace
1771 (match-beginning 1) (match-end 1)
995e028a
RS
1772 (format "Use the imperative for \"%s\". \
1773Replace with \"%s\"? " original replace)
bca0d607
EL
1774 replace t)
1775 (setq rs nil)))
1776 (if rs
1777 ;; there was a match, but no replace
1778 (checkdoc-create-error
1779 (format
995e028a 1780 "Probably \"%s\" should be imperative \"%s\""
bca0d607
EL
1781 original replace)
1782 (match-beginning 1) (match-end 1))))))
5b531322
KH
1783 ;; Done with functions
1784 )))
bca0d607
EL
1785 ;;* When a documentation string refers to a Lisp symbol, write it as
1786 ;; it would be printed (which usually means in lower case), with
1787 ;; single-quotes around it. For example: `lambda'. There are two
1788 ;; exceptions: write t and nil without single-quotes. (In this
1789 ;; manual, we normally do use single-quotes for those symbols.)
1790 (save-excursion
1791 (let ((found nil) (start (point)) (msg nil) (ms nil))
1792 (while (and (not msg)
1793 (re-search-forward
b762841f 1794 ;; Ignore manual page references like
26a27884
JA
1795 ;; git-config(1).
1796 "[^-([`':a-zA-Z]\\(\\w+[:-]\\(\\w\\|\\s_\\)+\\)[^](']"
bca0d607
EL
1797 e t))
1798 (setq ms (match-string 1))
66e9f105
JB
1799 ;; A . is a \s_ char, so we must remove periods from
1800 ;; sentences more carefully.
1801 (when (string-match-p "\\.$" ms)
1802 (setq ms (substring ms 0 (1- (length ms)))))
bca0d607
EL
1803 (if (and (not (checkdoc-in-sample-code-p start e))
1804 (not (checkdoc-in-example-string-p start e))
1805 (not (member ms checkdoc-symbol-words))
1806 (setq found (intern-soft ms))
1807 (or (boundp found) (fboundp found)))
1808 (progn
1809 (setq msg (format "Add quotes around Lisp symbol `%s'? "
1810 ms))
1811 (if (checkdoc-autofix-ask-replace
1812 (match-beginning 1) (+ (match-beginning 1)
1813 (length ms))
1814 msg (concat "`" ms "'") t)
1815 (setq msg nil)
1816 (setq msg
1817 (format "Lisp symbol `%s' should appear in quotes"
1818 ms))))))
1819 (if msg
1820 (checkdoc-create-error msg (match-beginning 1)
1821 (+ (match-beginning 1)
1822 (length ms)))
1823 nil)))
1824 ;; t and nil case
1825 (save-excursion
1826 (if (re-search-forward "\\(`\\(t\\|nil\\)'\\)" e t)
1827 (if (checkdoc-autofix-ask-replace
1828 (match-beginning 1) (match-end 1)
1829 (format "%s should not appear in quotes. Remove? "
1830 (match-string 2))
1831 (match-string 2) t)
1832 nil
1833 (checkdoc-create-error
995e028a 1834 "Symbols t and nil should not appear in `...' quotes"
bca0d607
EL
1835 (match-beginning 1) (match-end 1)))))
1836 ;; Here is some basic sentence formatting
1837 (checkdoc-sentencespace-region-engine (point) e)
1838 ;; Here are common proper nouns that should always appear capitalized.
1839 (checkdoc-proper-noun-region-engine (point) e)
1840 ;; Make sure the doc string has correctly spelled English words
1841 ;; in it. This function is extracted due to its complexity,
5fecb21a 1842 ;; and reliance on the Ispell program.
5b531322
KH
1843 (checkdoc-ispell-docstring-engine e)
1844 ;; User supplied checks
d1069532 1845 (save-excursion (checkdoc-run-hooks 'checkdoc-style-functions fp e))
5b531322
KH
1846 ;; Done!
1847 )))
1848
1849(defun checkdoc-defun-info nil
1850 "Return a list of details about the current sexp.
1851It is a list of the form:
5fecb21a 1852 (NAME VARIABLE INTERACTIVE NODOCPARAMS PARAMETERS ...)
5b531322
KH
1853where NAME is the name, VARIABLE is t if this is a `defvar',
1854INTERACTIVE is nil if this is not an interactive function, otherwise
1855it is the position of the `interactive' call, and PARAMETERS is a
1856string which is the name of each variable in the function's argument
1857list. The NODOCPARAMS is a sublist of parameters specified by a checkdoc
1858comment for a given defun. If the first element is not a string, then
1859the token checkdoc-order: <TOKEN> exists, and TOKEN is a symbol read
1860from the comment."
1861 (save-excursion
1862 (beginning-of-defun)
1863 (let ((defun (looking-at "(def\\(un\\|macro\\|subst\\|advice\\)"))
1864 (is-advice (looking-at "(defadvice"))
1865 (lst nil)
1866 (ret nil)
1867 (oo (make-vector 3 0))) ;substitute obarray for `read'
1868 (forward-char 1)
1869 (forward-sexp 1)
1870 (skip-chars-forward " \n\t")
1871 (setq ret
1872 (list (buffer-substring-no-properties
1873 (point) (progn (forward-sexp 1) (point)))))
1874 (if (not defun)
1875 (setq ret (cons t ret))
1876 ;; The variable spot
1877 (setq ret (cons nil ret))
1878 ;; Interactive
1879 (save-excursion
1880 (setq ret (cons
82bc80bf 1881 (re-search-forward "^\\s-*(interactive"
5b531322
KH
1882 (save-excursion (end-of-defun) (point))
1883 t)
1884 ret)))
1885 (skip-chars-forward " \t\n")
1886 (let ((bss (buffer-substring (point) (save-excursion (forward-sexp 1)
1887 (point))))
1888 ;; Overload th main obarray so read doesn't intern the
1889 ;; local symbols of the function we are checking.
1890 ;; Without this we end up cluttering the symbol space w/
1891 ;; useless symbols.
1892 (obarray oo))
1893 ;; Ok, check for checkdoc parameter comment here
1894 (save-excursion
1895 (setq ret
1896 (cons
1897 (let ((sl1 nil))
1898 (if (re-search-forward ";\\s-+checkdoc-order:\\s-+"
1899 (save-excursion (end-of-defun)
1900 (point))
1901 t)
1902 (setq sl1 (list (cond ((looking-at "nil") 'no)
1903 ((looking-at "t") 'yes)))))
1904 (if (re-search-forward ";\\s-+checkdoc-params:\\s-+"
1905 (save-excursion (end-of-defun)
1906 (point))
1907 t)
1908 (let ((sl nil))
1909 (goto-char (match-end 0))
bca0d607
EL
1910 (condition-case nil
1911 (setq lst (read (current-buffer)))
1912 (error (setq lst nil))) ; error in text
1913 (if (not (listp lst)) ; not a list of args
1914 (setq lst (list lst)))
1915 (if (and lst (not (symbolp (car lst)))) ;weird arg
1916 (setq lst nil))
5b531322
KH
1917 (while lst
1918 (setq sl (cons (symbol-name (car lst)) sl)
1919 lst (cdr lst)))
1920 (setq sl1 (append sl1 sl))))
1921 sl1)
1922 ret)))
bca0d607 1923 ;; Read the list of parameters, but do not put the symbols in
5b531322
KH
1924 ;; the standard obarray.
1925 (setq lst (read bss)))
1926 ;; This is because read will intern nil if it doesn't into the
1927 ;; new obarray.
1928 (if (not (listp lst)) (setq lst nil))
1929 (if is-advice nil
1930 (while lst
1931 (setq ret (cons (symbol-name (car lst)) ret)
1932 lst (cdr lst)))))
1933 (nreverse ret))))
1934
1935(defun checkdoc-in-sample-code-p (start limit)
5fecb21a 1936 "Return non-nil if the current point is in a code fragment.
5b531322 1937A code fragment is identified by an open parenthesis followed by a
bca0d607
EL
1938symbol which is a valid function or a word in all CAPS, or a parenthesis
1939that is quoted with the ' character. Only the region from START to LIMIT
5a5fa834 1940is allowed while searching for the bounding parenthesis."
5b531322
KH
1941 (save-match-data
1942 (save-restriction
1943 (narrow-to-region start limit)
1944 (save-excursion
1945 (and (condition-case nil (progn (up-list 1) t) (error nil))
1946 (condition-case nil (progn (forward-list -1) t) (error nil))
1947 (or (save-excursion (forward-char -1) (looking-at "'("))
1948 (and (looking-at "(\\(\\(\\w\\|[-:_]\\)+\\)[ \t\n;]")
1949 (let ((ms (buffer-substring-no-properties
1950 (match-beginning 1) (match-end 1))))
1951 ;; if this string is function bound, we are in
1952 ;; sample code. If it has a - or : character in
1953 ;; the name, then it is probably supposed to be bound
1954 ;; but isn't yet.
1955 (or (fboundp (intern-soft ms))
bca0d607
EL
1956 (let ((case-fold-search nil))
1957 (string-match "^[A-Z-]+$" ms))
5b531322
KH
1958 (string-match "\\w[-:_]+\\w" ms))))))))))
1959
bca0d607
EL
1960(defun checkdoc-in-example-string-p (start limit)
1961 "Return non-nil if the current point is in an \"example string\".
1962This string is identified by the characters \\\" surrounding the text.
1963The text checked is between START and LIMIT."
1964 (save-match-data
1965 (save-excursion
1966 (let ((p (point))
1967 (c 0))
1968 (goto-char start)
1969 (while (and (< (point) p) (re-search-forward "\\\\\"" limit t))
1970 (setq c (1+ c)))
1971 (and (< 0 c) (= (% c 2) 0))))))
1972
1973(defun checkdoc-proper-noun-region-engine (begin end)
1974 "Check all text between BEGIN and END for lower case proper nouns.
1975These are Emacs centric proper nouns which should be capitalized for
1976consistency. Return an error list if any are not fixed, but
1977internally skip over no answers.
1978If the offending word is in a piece of quoted text, then it is skipped."
1979 (save-excursion
1980 (let ((case-fold-search nil)
cca982d0
SM
1981 (errtxt nil) bb be)
1982 (with-syntax-table checkdoc-syntax-table
1983 (goto-char begin)
1984 (while (re-search-forward checkdoc-proper-noun-regexp end t)
1985 (let ((text (match-string 1))
1986 (b (match-beginning 1))
1987 (e (match-end 1)))
1988 (if (and (not (save-excursion
1989 (goto-char b)
1990 (forward-char -1)
1991 (looking-at "`\\|\"\\|\\.\\|\\\\")))
1992 ;; surrounded by /, as in a URL or filename: /emacs/
1993 (not (and (= ?/ (char-after e))
1994 (= ?/ (char-before b))))
1995 (not (checkdoc-in-example-string-p begin end))
1996 ;; info or url links left alone
1997 (not (thing-at-point-looking-at
1998 help-xref-info-regexp))
1999 (not (thing-at-point-looking-at
2000 help-xref-url-regexp)))
2001 (if (checkdoc-autofix-ask-replace
2002 b e (format "Text %s should be capitalized. Fix? "
2003 text)
2004 (capitalize text) t)
2005 nil
2006 (if errtxt
2007 ;; If there is already an error, then generate
2008 ;; the warning output if applicable
2009 (if checkdoc-generate-compile-warnings-flag
2010 (checkdoc-create-error
2011 (format
2012 "Name %s should appear capitalized as %s"
2013 text (capitalize text))
2014 b e))
2015 (setq errtxt
2016 (format
2017 "Name %s should appear capitalized as %s"
2018 text (capitalize text))
2019 bb b be e)))))))
bca0d607
EL
2020 (if errtxt (checkdoc-create-error errtxt bb be)))))
2021
2022(defun checkdoc-sentencespace-region-engine (begin end)
2023 "Make sure all sentences have double spaces between BEGIN and END."
84f473b0
EL
2024 (if sentence-end-double-space
2025 (save-excursion
2026 (let ((case-fold-search nil)
cca982d0
SM
2027 (errtxt nil) bb be)
2028 (with-syntax-table checkdoc-syntax-table
2029 (goto-char begin)
2030 (while (re-search-forward "[^ .0-9]\\(\\. \\)[^ \n]" end t)
2031 (let ((b (match-beginning 1))
2032 (e (match-end 1)))
2033 (unless (or (checkdoc-in-sample-code-p begin end)
2034 (checkdoc-in-example-string-p begin end)
2035 (save-excursion
2036 (goto-char b)
2037 (condition-case nil
2038 (progn
2039 (forward-sexp -1)
2040 ;; piece of an abbreviation
2041 ;; FIXME etc
2042 (looking-at
1dcf791f 2043 "\\([a-zA-Z]\\|[iI]\\.?e\\|[eE]\\.?g\\)\\."))
cca982d0
SM
2044 (error t))))
2045 (if (checkdoc-autofix-ask-replace
2046 b e
2047 "There should be two spaces after a period. Fix? "
2048 ". ")
2049 nil
2050 (if errtxt
2051 ;; If there is already an error, then generate
2052 ;; the warning output if applicable
2053 (if checkdoc-generate-compile-warnings-flag
2054 (checkdoc-create-error
2055 "There should be two spaces after a period"
2056 b e))
2057 (setq errtxt
2058 "There should be two spaces after a period"
2059 bb b be e)))))))
84f473b0 2060 (if errtxt (checkdoc-create-error errtxt bb be))))))
bca0d607 2061
5b531322
KH
2062;;; Ispell engine
2063;;
e2aec513
GM
2064(defvar ispell-process)
2065(declare-function ispell-buffer-local-words "ispell" ())
5b531322
KH
2066
2067(defun checkdoc-ispell-init ()
5fecb21a 2068 "Initialize Ispell process (default version) with Lisp words.
5b531322
KH
2069The words used are from `checkdoc-ispell-lisp-words'. If `ispell'
2070cannot be loaded, then set `checkdoc-spellcheck-documentation-flag' to
2071nil."
2072 (require 'ispell)
e2aec513
GM
2073 (unless ispell-process
2074 (condition-case nil
2075 (progn
2076 (ispell-buffer-local-words)
2077 ;; This code copied in part from ispell.el Emacs 19.34
2078 (dolist (w checkdoc-ispell-lisp-words)
2079 (process-send-string ispell-process (concat "@" w "\n"))))
2080 (error (setq checkdoc-spellcheck-documentation-flag nil)))))
5b531322
KH
2081
2082(defun checkdoc-ispell-docstring-engine (end)
5fecb21a
RS
2083 "Run the Ispell tools on the doc string between point and END.
2084Since Ispell isn't Lisp-smart, we must pre-process the doc string
2085before using the Ispell engine on it."
bca0d607
EL
2086 (if (or (not checkdoc-spellcheck-documentation-flag)
2087 ;; If the user wants no questions or fixing, then we must
2088 ;; disable spell checking as not useful.
b08b261e 2089 (not checkdoc-autofix-flag)
bca0d607 2090 (eq checkdoc-autofix-flag 'never))
5b531322
KH
2091 nil
2092 (checkdoc-ispell-init)
2093 (save-excursion
2094 (skip-chars-forward "^a-zA-Z")
2095 (let ((word nil) (sym nil) (case-fold-search nil) (err nil))
2096 (while (and (not err) (< (point) end))
2097 (if (save-excursion (forward-char -1) (looking-at "[('`]"))
2098 ;; Skip lists describing meta-syntax, or bound variables
2099 (forward-sexp 1)
2100 (setq word (buffer-substring-no-properties
2101 (point) (progn
2102 (skip-chars-forward "a-zA-Z-")
2103 (point)))
2104 sym (intern-soft word))
2105 (if (and sym (or (boundp sym) (fboundp sym)))
bca0d607 2106 ;; This is probably repetitive in most cases, but not always.
5b531322
KH
2107 nil
2108 ;; Find out how we spell-check this word.
2109 (if (or
0a0a3dee 2110 ;; All caps w/ option th, or s tacked on the end
e1dbe924 2111 ;; for pluralization or number.
0a0a3dee 2112 (string-match "^[A-Z][A-Z]+\\(s\\|th\\)?$" word)
5b531322
KH
2113 (looking-at "}") ; a keymap expression
2114 )
2115 nil
2116 (save-excursion
2117 (if (not (eq checkdoc-autofix-flag 'never))
2118 (let ((lk last-input-event))
2119 (ispell-word nil t)
2120 (if (not (equal last-input-event lk))
2121 (progn
2122 (sit-for 0)
2123 (message "Continuing..."))))
2124 ;; Nothing here.
2125 )))))
2126 (skip-chars-forward "^a-zA-Z"))
2127 err))))
2128
2129;;; Rogue space checking engine
2130;;
bca0d607
EL
2131(defun checkdoc-rogue-space-check-engine (&optional start end interact)
2132 "Return a message list if there is a line with white space at the end.
5b531322 2133If `checkdoc-autofix-flag' permits, delete that whitespace instead.
c67c14f3 2134If optional arguments START and END are non-nil, bound the check to
bca0d607
EL
2135this region.
2136Optional argument INTERACT may permit the user to fix problems on the fly."
5b531322 2137 (let ((p (point))
bca0d607 2138 (msg nil) s e (f nil))
5b531322
KH
2139 (if (not start) (setq start (point-min)))
2140 ;; If end is nil, it means end of buffer to search anyway
2141 (or
bca0d607 2142 ;; Check for an error if `? ' or `?\ ' is used at the end of a line.
5b531322
KH
2143 ;; (It's dangerous)
2144 (progn
2145 (goto-char start)
bca0d607
EL
2146 (while (and (not msg) (re-search-forward "\\?\\\\?[ \t][ \t]*$" end t))
2147 (setq msg
2148 "Don't use `? ' at the end of a line. \
2149News agents may remove it"
2150 s (match-beginning 0) e (match-end 0) f t)
2151 ;; If interactive is passed down, give them a chance to fix things.
2152 (if (and interact (y-or-n-p (concat msg ". Fix? ")))
2153 (progn
2154 (checkdoc-recursive-edit msg)
2155 (setq msg nil)
2156 (goto-char s)
2157 (beginning-of-line)))))
2158 ;; Check for, and potentially remove whitespace appearing at the
5b531322
KH
2159 ;; end of different lines.
2160 (progn
2161 (goto-char start)
5fecb21a 2162 ;; There is no documentation in the Emacs Lisp manual about this check,
5b531322 2163 ;; it is intended to help clean up messy code and reduce the file size.
bca0d607 2164 (while (and (not msg) (re-search-forward "[^ \t\n;]\\([ \t]+\\)$" end t))
5b531322
KH
2165 ;; This is not a complex activity
2166 (if (checkdoc-autofix-ask-replace
2167 (match-beginning 1) (match-end 1)
5fecb21a 2168 "White space at end of line. Remove? " "")
5b531322 2169 nil
bca0d607
EL
2170 (setq msg "White space found at end of line"
2171 s (match-beginning 1) e (match-end 1))))))
5b531322
KH
2172 ;; Return an error and leave the cursor at that spot, or restore
2173 ;; the cursor.
2174 (if msg
bca0d607 2175 (checkdoc-create-error msg s e f)
5b531322
KH
2176 (goto-char p)
2177 nil)))
2178
2179;;; Comment checking engine
2180;;
e8f2a5d5
GM
2181(defvar generate-autoload-cookie)
2182
e2aec513
GM
2183(eval-when-compile (require 'lisp-mnt)) ; expand silly defsubsts
2184(declare-function lm-summary "lisp-mnt" (&optional file))
2185(declare-function lm-section-start "lisp-mnt" (header &optional after))
2186(declare-function lm-section-end "lisp-mnt" (header))
2187
5b531322 2188(defun checkdoc-file-comments-engine ()
bca0d607 2189 "Return a message list if this file does not match the Emacs standard.
5b531322
KH
2190This checks for style only, such as the first line, Commentary:,
2191Code:, and others referenced in the style guide."
2192 (if (featurep 'lisp-mnt)
2193 nil
2194 (require 'lisp-mnt)
bca0d607 2195 ;; Old XEmacs don't have `lm-commentary-mark'
b0429158
SM
2196 (if (and (not (fboundp 'lm-commentary-mark)) (fboundp 'lm-commentary))
2197 (defalias 'lm-commentary-mark #'lm-commentary)))
5b531322
KH
2198 (save-excursion
2199 (let* ((f1 (file-name-nondirectory (buffer-file-name)))
2200 (fn (file-name-sans-extension f1))
bca0d607
EL
2201 (fe (substring f1 (length fn)))
2202 (err nil))
5b531322 2203 (goto-char (point-min))
bca0d607
EL
2204 ;; This file has been set up where ERR is a variable. Each check is
2205 ;; asked, and the function will make sure that if the user does not
2206 ;; auto-fix some error, that we still move on to the next auto-fix,
2207 ;; AND we remember the past errors.
2208 (setq
2209 err
5b531322
KH
2210 ;; Lisp Maintenance checks first
2211 ;; Was: (lm-verify) -> not flexible enough for some people
2212 ;; * Summary at the beginning of the file:
2213 (if (not (lm-summary))
2214 ;; This certifies as very complex so always ask unless
2215 ;; it's set to never
bca0d607 2216 (if (checkdoc-y-or-n-p "There is no first line summary! Add one? ")
5b531322
KH
2217 (progn
2218 (goto-char (point-min))
2219 (insert ";;; " fn fe " --- " (read-string "Summary: ") "\n"))
bca0d607
EL
2220 (checkdoc-create-error
2221 "The first line should be of the form: \";;; package --- Summary\""
76fc02b6
GM
2222 (point-min) (save-excursion (goto-char (point-min))
2223 (line-end-position))))
bca0d607
EL
2224 nil))
2225 (setq
2226 err
2227 (or
2228 ;; * Commentary Section
2229 (if (not (lm-commentary-mark))
2230 (progn
2231 (goto-char (point-min))
2232 (cond
2233 ((re-search-forward
2234 "write\\s-+to\\s-+the\\s-+Free Software Foundation, Inc."
2235 nil t)
2236 (re-search-forward "^;;\\s-*\n\\|^\n" nil t))
2237 ((or (re-search-forward "^;;; History" nil t)
2238 (re-search-forward "^;;; Code" nil t)
2239 (re-search-forward "^(require" nil t)
c9d557f5 2240 (re-search-forward "^(" nil t))
81e213dc
CY
2241 (beginning-of-line))
2242 (t (re-search-forward ";;; .* --- .*\n")))
bca0d607
EL
2243 (if (checkdoc-y-or-n-p
2244 "You should have a \";;; Commentary:\", add one? ")
2245 (insert "\n;;; Commentary:\n;; \n\n")
2246 (checkdoc-create-error
2247 "You should have a section marked \";;; Commentary:\""
2248 nil nil t)))
2249 nil)
2250 err))
2251 (setq
2252 err
2253 (or
2254 ;; * History section. Say nothing if there is a file ChangeLog
2255 (if (or (not checkdoc-force-history-flag)
2256 (file-exists-p "ChangeLog")
2257 (file-exists-p "../ChangeLog")
b0429158 2258 (and (fboundp 'lm-history-mark) (funcall #'lm-history-mark)))
bca0d607
EL
2259 nil
2260 (progn
2261 (goto-char (or (lm-commentary-mark) (point-min)))
2262 (cond
2263 ((re-search-forward
2264 "write\\s-+to\\s-+the\\s-+Free Software Foundation, Inc."
2265 nil t)
2266 (re-search-forward "^;;\\s-*\n\\|^\n" nil t))
2267 ((or (re-search-forward "^;;; Code" nil t)
2268 (re-search-forward "^(require" nil t)
c9d557f5 2269 (re-search-forward "^(" nil t))
bca0d607
EL
2270 (beginning-of-line)))
2271 (if (checkdoc-y-or-n-p
2272 "You should have a \";;; History:\", add one? ")
2273 (insert "\n;;; History:\n;; \n\n")
2274 (checkdoc-create-error
2275 "You should have a section marked \";;; History:\" or use a ChangeLog"
2276 (point) nil))))
2277 err))
2278 (setq
2279 err
2280 (or
2281 ;; * Code section
2282 (if (not (lm-code-mark))
68634374
KR
2283 (let ((cont t)
2284 pos)
bca0d607 2285 (goto-char (point-min))
68634374
KR
2286 ;; match ";;;###autoload" cookie to keep it with the form
2287 (require 'autoload)
2288 (while (and cont (re-search-forward
2289 (concat "^\\("
2290 (regexp-quote generate-autoload-cookie)
2291 "\n\\)?"
2292 "(")
2293 nil t))
2294 (setq pos (match-beginning 0)
2295 cont (looking-at "require\\s-+")))
bca0d607
EL
2296 (if (and (not cont)
2297 (checkdoc-y-or-n-p
2298 "There is no ;;; Code: marker. Insert one? "))
68634374
KR
2299 (progn (goto-char pos)
2300 (insert ";;; Code:\n\n")
bca0d607
EL
2301 nil)
2302 (checkdoc-create-error
2303 "You should have a section marked \";;; Code:\""
2304 (point) nil)))
2305 nil)
2306 err))
2307 (setq
2308 err
2309 (or
2310 ;; * A footer. Not compartmentalized from lm-verify: too bad.
2311 ;; The following is partially clipped from lm-verify
2312 (save-excursion
2313 (goto-char (point-max))
2314 (if (not (re-search-backward
65a30f58 2315 (concat "^;;;[ \t]+" (regexp-quote fn) "\\(" (regexp-quote fe)
bca0d607
EL
2316 "\\)?[ \t]+ends here[ \t]*$"
2317 "\\|^;;;[ \t]+ End of file[ \t]+"
65a30f58 2318 (regexp-quote fn) "\\(" (regexp-quote fe) "\\)?")
bca0d607
EL
2319 nil t))
2320 (if (checkdoc-y-or-n-p "No identifiable footer! Add one? ")
2321 (progn
2322 (goto-char (point-max))
2323 (insert "\n(provide '" fn ")\n\n;;; " fn fe " ends here\n"))
2324 (checkdoc-create-error
2325 (format "The footer should be: (provide '%s)\\n;;; %s%s ends here"
2326 fn fn fe)
2327 (1- (point-max)) (point-max)))))
2328 err))
2329 ;; The below checks will not return errors if the user says NO
bca0d607
EL
2330
2331 ;; Let's spellcheck the commentary section. This is the only
2332 ;; section that is easy to pick out, and it is also the most
2333 ;; visible section (with the finder).
2334 (let ((cm (lm-commentary-mark)))
d547e25f
LK
2335 (when cm
2336 (save-excursion
2337 (goto-char cm)
2338 (let ((e (copy-marker (lm-commentary-end))))
2339 ;; Since the comments talk about Lisp, use the
2340 ;; specialized spell-checker we also used for doc
2341 ;; strings.
2342 (checkdoc-sentencespace-region-engine (point) e)
2343 (checkdoc-proper-noun-region-engine (point) e)
2344 (checkdoc-ispell-docstring-engine e)))))
bca0d607
EL
2345 (setq
2346 err
2347 (or
2348 ;; Generic Full-file checks (should be comment related)
d1069532 2349 (checkdoc-run-hooks 'checkdoc-comment-style-functions)
bca0d607
EL
2350 err))
2351 ;; Done with full file comment checks
2352 err)))
5b531322
KH
2353
2354(defun checkdoc-outside-major-sexp ()
2355 "Return t if point is outside the bounds of a valid sexp."
2356 (save-match-data
2357 (save-excursion
2358 (let ((p (point)))
2359 (or (progn (beginning-of-defun) (bobp))
2360 (progn (end-of-defun) (< (point) p)))))))
2361
a4370a77
EL
2362;;; `error' and `message' text verifier.
2363;;
a4370a77 2364(defun checkdoc-message-text-search (&optional beg end)
bca0d607 2365 "Search between BEG and END for a style error with message text.
a4370a77
EL
2366Optional arguments BEG and END represent the boundary of the check.
2367The default boundary is the entire buffer."
bca0d607
EL
2368 (let ((e nil)
2369 (type nil))
a4370a77
EL
2370 (if (not (or beg end)) (setq beg (point-min) end (point-max)))
2371 (goto-char beg)
bca0d607
EL
2372 (while (setq type (checkdoc-message-text-next-string end))
2373 (setq e (checkdoc-message-text-engine type)))
a4370a77 2374 e))
a1506d29 2375
bca0d607
EL
2376(defun checkdoc-message-text-next-string (end)
2377 "Move cursor to the next checkable message string after point.
2378Return the message classification.
2379Argument END is the maximum bounds to search in."
2380 (let ((return nil))
2381 (while (and (not return)
2382 (re-search-forward
2383 "(\\s-*\\(\\(\\w\\|\\s_\\)*error\\|\
2384\\(\\w\\|\\s_\\)*y-or-n-p\\(-with-timeout\\)?\
2385\\|checkdoc-autofix-ask-replace\\)[ \t\n]+" end t))
2386 (let* ((fn (match-string 1))
2387 (type (cond ((string-match "error" fn)
2388 'error)
2389 (t 'y-or-n-p))))
2390 (if (string-match "checkdoc-autofix-ask-replace" fn)
2391 (progn (forward-sexp 2)
2392 (skip-chars-forward " \t\n")))
2393 (if (and (eq type 'y-or-n-p)
2394 (looking-at "(format[ \t\n]+"))
2395 (goto-char (match-end 0)))
2396 (skip-chars-forward " \t\n")
2397 (if (not (looking-at "\""))
2398 nil
2399 (setq return type))))
2400 return))
2401
2402(defun checkdoc-message-text-engine (&optional type)
a4370a77 2403 "Return or fix errors found in strings passed to a message display function.
bca0d607
EL
2404According to the documentation for the function `error', the error list
2405should not end with a period, and should start with a capital letter.
a4370a77
EL
2406The function `y-or-n-p' has similar constraints.
2407Argument TYPE specifies the type of question, such as `error or `y-or-n-p."
bca0d607
EL
2408 ;; If type is nil, then attempt to derive it.
2409 (if (not type)
2410 (save-excursion
2411 (up-list -1)
2412 (if (looking-at "(format")
2413 (up-list -1))
2414 (setq type
2415 (cond ((looking-at "(error")
2416 'error)
2417 (t 'y-or-n-p)))))
a4370a77
EL
2418 (let ((case-fold-search nil))
2419 (or
2420 ;; From the documentation of the symbol `error':
2421 ;; In Emacs, the convention is that error messages start with a capital
2422 ;; letter but *do not* end with a period. Please follow this convention
2423 ;; for the sake of consistency.
2424 (if (and (save-excursion (forward-char 1)
2425 (looking-at "[a-z]\\w+"))
2426 (not (checkdoc-autofix-ask-replace
2427 (match-beginning 0) (match-end 0)
2428 "Capitalize your message text? "
2429 (capitalize (match-string 0))
2430 t)))
bca0d607
EL
2431 (checkdoc-create-error
2432 "Messages should start with a capital letter"
2433 (match-beginning 0) (match-end 0))
a4370a77 2434 nil)
bca0d607
EL
2435 ;; In general, sentences should have two spaces after the period.
2436 (checkdoc-sentencespace-region-engine (point)
2437 (save-excursion (forward-sexp 1)
2438 (point)))
2439 ;; Look for proper nouns in this region too.
2440 (checkdoc-proper-noun-region-engine (point)
2441 (save-excursion (forward-sexp 1)
2442 (point)))
2443 ;; Here are message type specific questions.
a4370a77
EL
2444 (if (and (eq type 'error)
2445 (save-excursion (forward-sexp 1)
2446 (forward-char -2)
2447 (looking-at "\\."))
2448 (not (checkdoc-autofix-ask-replace (match-beginning 0)
2449 (match-end 0)
2450 "Remove period from error? "
2451 ""
2452 t)))
bca0d607
EL
2453 (checkdoc-create-error
2454 "Error messages should *not* end with a period"
2455 (match-beginning 0) (match-end 0))
a4370a77
EL
2456 nil)
2457 ;; `y-or-n-p' documentation explicitly says:
2458 ;; It should end in a space; `y-or-n-p' adds `(y or n) ' to it.
2459 ;; I added the ? requirement. Without it, it is unclear that we
2460 ;; ask a question and it appears to be an undocumented style.
bca0d607
EL
2461 (if (eq type 'y-or-n-p)
2462 (if (not (save-excursion (forward-sexp 1)
2463 (forward-char -3)
2464 (not (looking-at "\\? "))))
2465 nil
2466 (if (save-excursion (forward-sexp 1)
2467 (forward-char -2)
2468 (looking-at "\\?"))
2469 ;; If we see a ?, then replace with "? ".
2470 (if (checkdoc-autofix-ask-replace
2471 (match-beginning 0) (match-end 0)
995e028a 2472 "`y-or-n-p' argument should end with \"? \". Fix? "
bca0d607
EL
2473 "? " t)
2474 nil
2475 (checkdoc-create-error
995e028a 2476 "`y-or-n-p' argument should end with \"? \""
bca0d607
EL
2477 (match-beginning 0) (match-end 0)))
2478 (if (save-excursion (forward-sexp 1)
2479 (forward-char -2)
2480 (looking-at " "))
2481 (if (checkdoc-autofix-ask-replace
2482 (match-beginning 0) (match-end 0)
995e028a 2483 "`y-or-n-p' argument should end with \"? \". Fix? "
bca0d607
EL
2484 "? " t)
2485 nil
2486 (checkdoc-create-error
995e028a 2487 "`y-or-n-p' argument should end with \"? \""
bca0d607
EL
2488 (match-beginning 0) (match-end 0)))
2489 (if (and ;; if this isn't true, we have a problem.
2490 (save-excursion (forward-sexp 1)
2491 (forward-char -1)
2492 (looking-at "\""))
2493 (checkdoc-autofix-ask-replace
2494 (match-beginning 0) (match-end 0)
995e028a 2495 "`y-or-n-p' argument should end with \"? \". Fix? "
bca0d607
EL
2496 "? \"" t))
2497 nil
2498 (checkdoc-create-error
995e028a 2499 "`y-or-n-p' argument should end with \"? \""
bca0d607
EL
2500 (match-beginning 0) (match-end 0)))))))
2501 ;; Now, let's just run the spell checker on this guy.
2502 (checkdoc-ispell-docstring-engine (save-excursion (forward-sexp 1)
2503 (point)))
a4370a77
EL
2504 )))
2505
5b531322
KH
2506;;; Auto-fix helper functions
2507;;
bca0d607
EL
2508(defun checkdoc-y-or-n-p (question)
2509 "Like `y-or-n-p', but pays attention to `checkdoc-autofix-flag'.
2510Argument QUESTION is the prompt passed to `y-or-n-p'."
2511 (prog1
2512 (if (or (not checkdoc-autofix-flag)
2513 (eq checkdoc-autofix-flag 'never))
2514 nil
2515 (y-or-n-p question))
2516 (if (eq checkdoc-autofix-flag 'automatic-then-never)
2517 (setq checkdoc-autofix-flag 'never))))
2518
5b531322
KH
2519(defun checkdoc-autofix-ask-replace (start end question replacewith
2520 &optional complex)
2521 "Highlight between START and END and queries the user with QUESTION.
2522If the user says yes, or if `checkdoc-autofix-flag' permits, replace
2523the region marked by START and END with REPLACEWITH. If optional flag
2524COMPLEX is non-nil, then we may ask the user a question. See the
2525documentation for `checkdoc-autofix-flag' for details.
2526
2527If a section is auto-replaced without asking the user, this function
2528will pause near the fixed code so the user will briefly see what
2529happened.
2530
bca0d607
EL
2531This function returns non-nil if the text was replaced.
2532
2533This function will not modify `match-data'."
2534 (if (and checkdoc-autofix-flag
2535 (not (eq checkdoc-autofix-flag 'never)))
5b531322 2536 (let ((o (checkdoc-make-overlay start end))
bca0d607
EL
2537 (ret nil)
2538 (md (match-data)))
5b531322
KH
2539 (unwind-protect
2540 (progn
2541 (checkdoc-overlay-put o 'face 'highlight)
2542 (if (or (eq checkdoc-autofix-flag 'automatic)
bca0d607 2543 (eq checkdoc-autofix-flag 'automatic-then-never)
5b531322
KH
2544 (and (eq checkdoc-autofix-flag 'semiautomatic)
2545 (not complex))
2546 (and (or (eq checkdoc-autofix-flag 'query) complex)
2547 (y-or-n-p question)))
2548 (save-excursion
2549 (goto-char start)
2550 ;; On the off chance this is automatic, display
bca0d607 2551 ;; the question anyway so the user knows what's
5b531322
KH
2552 ;; going on.
2553 (if checkdoc-bouncy-flag (message "%s -> done" question))
2554 (delete-region start end)
2555 (insert replacewith)
2556 (if checkdoc-bouncy-flag (sit-for 0))
2557 (setq ret t)))
bca0d607
EL
2558 (checkdoc-delete-overlay o)
2559 (set-match-data md))
2560 (checkdoc-delete-overlay o)
2561 (set-match-data md))
2562 (if (eq checkdoc-autofix-flag 'automatic-then-never)
2563 (setq checkdoc-autofix-flag 'never))
5b531322
KH
2564 ret)))
2565
2566;;; Warning management
2567;;
2568(defvar checkdoc-output-font-lock-keywords
f578d9f8 2569 '(("^\\*\\*\\* \\(.+\\.el\\): \\([^ \n]+\\)"
bca0d607 2570 (1 font-lock-function-name-face)
f578d9f8 2571 (2 font-lock-comment-face)))
5b531322
KH
2572 "Keywords used to highlight a checkdoc diagnostic buffer.")
2573
f578d9f8
SM
2574(defvar checkdoc-output-error-regex-alist
2575 '(("^\\(.+\\.el\\):\\([0-9]+\\): " 1 2)))
5b531322 2576
bca0d607
EL
2577(defvar checkdoc-pending-errors nil
2578 "Non-nil when there are errors that have not been displayed yet.")
2579
f578d9f8
SM
2580(define-derived-mode checkdoc-output-mode compilation-mode "Checkdoc"
2581 "Set up the major mode for the buffer containing the list of errors."
b0429158
SM
2582 (setq-local compilation-error-regexp-alist
2583 checkdoc-output-error-regex-alist)
2584 (setq-local compilation-mode-font-lock-keywords
2585 checkdoc-output-font-lock-keywords))
5b531322 2586
84f473b0
EL
2587(defun checkdoc-buffer-label ()
2588 "The name to use for a checkdoc buffer in the error list."
2589 (if (buffer-file-name)
780b142e 2590 (file-relative-name (buffer-file-name))
84f473b0
EL
2591 (concat "#<buffer "(buffer-name) ">")))
2592
5b531322
KH
2593(defun checkdoc-start-section (check-type)
2594 "Initialize the checkdoc diagnostic buffer for a pass.
2595Create the header so that the string CHECK-TYPE is displayed as the
2596function called to create the messages."
780b142e
SM
2597 (let ((dir default-directory)
2598 (label (checkdoc-buffer-label)))
2599 (with-current-buffer (get-buffer-create checkdoc-diagnostic-buffer)
2600 (checkdoc-output-mode)
2601 (setq default-directory dir)
2602 (goto-char (point-max))
b260aab7
SM
2603 (let ((inhibit-read-only t))
2604 (insert "\n\n\C-l\n*** " label ": "
2605 check-type " V " checkdoc-version)))))
5b531322
KH
2606
2607(defun checkdoc-error (point msg)
2608 "Store POINT and MSG as errors in the checkdoc diagnostic buffer."
bca0d607 2609 (setq checkdoc-pending-errors t)
780b142e
SM
2610 (let ((text (list "\n" (checkdoc-buffer-label) ":"
2611 (int-to-string
2612 (count-lines (point-min) (or point (point-min))))
2613 ": " msg)))
2614 (with-current-buffer (get-buffer checkdoc-diagnostic-buffer)
2615 (goto-char (point-max))
b260aab7 2616 (let ((inhibit-read-only t))
b0429158 2617 (apply #'insert text)))))
5b531322
KH
2618
2619(defun checkdoc-show-diagnostics ()
2620 "Display the checkdoc diagnostic buffer in a temporary window."
bca0d607
EL
2621 (if checkdoc-pending-errors
2622 (let ((b (get-buffer checkdoc-diagnostic-buffer)))
2623 (if b (progn (pop-to-buffer b)
2624 (goto-char (point-max))
2625 (re-search-backward "\C-l" nil t)
2626 (beginning-of-line)
2627 (forward-line 1)
2628 (recenter 0)))
2629 (other-window -1)
2630 (setq checkdoc-pending-errors nil)
2631 nil)))
5b531322 2632
1398b795 2633(custom-add-option 'emacs-lisp-mode-hook 'checkdoc-minor-mode)
5b531322
KH
2634
2635(provide 'checkdoc)
5fecb21a 2636
5b531322 2637;;; checkdoc.el ends here