diff options
author | Damien Cassou <damien@cassou.me> | 2019-12-27 15:35:52 +0100 |
---|---|---|
committer | Eli Zaretskii <eliz@gnu.org> | 2020-01-17 11:29:06 +0200 |
commit | 6338f69102551cb8bfba36000fb73935aefa5b7b (patch) | |
tree | 7bf8a4733ed2b2a2efe687dbc6385107acba9360 /lisp/emacs-lisp/checkdoc.el | |
parent | 4453acbdc987130e9967ef7afe90359ad7a48987 (diff) | |
download | emacs-6338f69102551cb8bfba36000fb73935aefa5b7b.tar.gz emacs-6338f69102551cb8bfba36000fb73935aefa5b7b.tar.bz2 emacs-6338f69102551cb8bfba36000fb73935aefa5b7b.zip |
Add unattended spell-checking to checkdoc
This commit makes checkdoc capable of spell-checking even when the
user isn't using it interactively. When TAKE-NOTES is non-nil,
checkdoc will run spell-checking (with ispell) and report spelling
mistakes.
Fixes: (bug#38583).
* lisp/textmodes/ispell.el (ispell-word): Extract part of it to
`ispell--run-on-word`.
(ispell--run-on-word): New function, extracted from `ispell-word`.
(ispell-error-checking-word): New function.
(ispell-correct-p): New function. Use `ispell--run-on-word` and
`ispell-error-checking-word`.
* lisp/emacs-lisp/checkdoc.el (checkdoc-current-buffer): Pass
TAKE-NOTES to `checkdoc-start`.
(checkdoc-continue): Pass TAKE-NOTES to `checkdoc-this-string-valid`.
(checkdoc-this-string-valid): Add optional argument TAKE-NOTES and
pass it to `checkdoc-this-string-valid-engine`.
(checkdoc-this-string-valid-engine): Add optional argument TAKE-NOTES
and pass it to `checkdoc-ispell-docstring-engine`.
(checkdoc-ispell-init): Call `ispell-set-spellchecker-params` and
`ispell-accept-buffer-local-defs`. These calls are required to
properly use ispell. The problem went unnoticed until now because
checkdoc was only using ispell through the high-level command
`ispell-word` which takes care of all the initialization for the user.
(checkdoc-ispell-docstring-engine): Add optional argument TAKE-NOTES
to force reporting of spell-checking errors. Throw error
when (checkdoc-ispell-init) fails configuring ispell. Replace a
few (if cond nil body) with (unless cond body). Replace (let ((var
nil))) with (let (var)). Replace (if (not (eq checkdoc-autofix-flag
'never)) body) with just body because `checkdoc-autofix-flag` is
checked at the beginning of the function.
(cherry picked from commit 25adbc4a5ecc3e16625c0171607e3153bbdf7ab1)
Diffstat (limited to 'lisp/emacs-lisp/checkdoc.el')
-rw-r--r-- | lisp/emacs-lisp/checkdoc.el | 118 |
1 files changed, 68 insertions, 50 deletions
diff --git a/lisp/emacs-lisp/checkdoc.el b/lisp/emacs-lisp/checkdoc.el index 93b9ffbe38b..cbad6f05541 100644 --- a/lisp/emacs-lisp/checkdoc.el +++ b/lisp/emacs-lisp/checkdoc.el @@ -849,7 +849,7 @@ otherwise stop after the first error." ;; every test is responsible for returning the cursor. (or (and buffer-file-name ;; only check comments in a file (checkdoc-comments)) - (checkdoc-start) + (checkdoc-start take-notes) (checkdoc-message-text) (checkdoc-rogue-spaces) (when checkdoc-package-keywords-flag @@ -902,7 +902,7 @@ buffer and save warnings in a separate buffer." ;; the user is navigating down through the buffer. (while (and (not wrong) (checkdoc-next-docstring)) ;; OK, let's look at the doc string. - (setq msg (checkdoc-this-string-valid)) + (setq msg (checkdoc-this-string-valid take-notes)) (if msg (setq wrong (point))))) (if wrong (progn @@ -1284,12 +1284,15 @@ checking of documentation strings. ;;; Checking engines ;; -(defun checkdoc-this-string-valid () +(defun checkdoc-this-string-valid (&optional take-notes) "Return a message string if the current doc string is invalid. Check for style only, such as the first line always being a complete sentence, whitespace restrictions, and making sure there are no hard-coded key-codes such as C-[char] or mouse-[number] in the comment. -See the style guide in the Emacs Lisp manual for more details." +See the style guide in the Emacs Lisp manual for more details. + +With a non-nil TAKE-NOTES, store all errors found in a warnings +buffer, otherwise stop after the first error." ;; Jump over comments between the last object and the doc string (while (looking-at "[ \t\n]*;") @@ -1366,13 +1369,16 @@ documentation string") (point) (+ (point) 1) t))))) (if (and (not err) (= (following-char) ?\")) (with-syntax-table checkdoc-syntax-table - (checkdoc-this-string-valid-engine fp)) + (checkdoc-this-string-valid-engine fp take-notes)) err))) -(defun checkdoc-this-string-valid-engine (fp) +(defun checkdoc-this-string-valid-engine (fp &optional take-notes) "Return an error list or string if the current doc string is invalid. Depends on `checkdoc-this-string-valid' to reset the syntax table so that -regexp short cuts work. FP is the function defun information." +regexp short cuts work. FP is the function defun information. + +With a non-nil TAKE-NOTES, store all errors found in a warnings +buffer, otherwise stop after the first error." (let ((case-fold-search nil) ;; Use a marker so if an early check modifies the text, ;; we won't accidentally lose our place. This could cause @@ -1864,7 +1870,7 @@ Replace with \"%s\"? " original replace) ;; Make sure the doc string has correctly spelled English words ;; in it. This function is extracted due to its complexity, ;; and reliance on the Ispell program. - (checkdoc-ispell-docstring-engine e) + (checkdoc-ispell-docstring-engine e take-notes) ;; User supplied checks (save-excursion (checkdoc-run-hooks 'checkdoc-style-functions fp e)) ;; Done! @@ -2090,6 +2096,10 @@ If the offending word is in a piece of quoted text, then it is skipped." ;; (defvar ispell-process) (declare-function ispell-buffer-local-words "ispell" ()) +(declare-function ispell-correct-p "ispell" ()) +(declare-function ispell-set-spellchecker-params "ispell" ()) +(declare-function ispell-accept-buffer-local-defs "ispell" ()) +(declare-function ispell-error-checking-word "ispell" (word)) (defun checkdoc-ispell-init () "Initialize Ispell process (default version) with Lisp words. @@ -2100,58 +2110,66 @@ nil." (unless ispell-process (condition-case nil (progn - (ispell-buffer-local-words) + (ispell-set-spellchecker-params) ; Initialize variables and dicts alists + (ispell-accept-buffer-local-defs) ; use the correct dictionary ;; This code copied in part from ispell.el Emacs 19.34 (dolist (w checkdoc-ispell-lisp-words) (process-send-string ispell-process (concat "@" w "\n")))) (error (setq checkdoc-spellcheck-documentation-flag nil))))) -(defun checkdoc-ispell-docstring-engine (end) +(defun checkdoc-ispell-docstring-engine (end &optional take-notes) "Run the Ispell tools on the doc string between point and END. Since Ispell isn't Lisp-smart, we must pre-process the doc string -before using the Ispell engine on it." - (if (or (not checkdoc-spellcheck-documentation-flag) - ;; If the user wants no questions or fixing, then we must - ;; disable spell checking as not useful. - (not checkdoc-autofix-flag) - (eq checkdoc-autofix-flag 'never)) - nil +before using the Ispell engine on it. + +With a non-nil TAKE-NOTES, store all errors found in a warnings +buffer, otherwise stop after the first error." + (when (and checkdoc-spellcheck-documentation-flag + ;; If the user wants no questions or fixing, then we must + ;; disable spell checking as not useful. + (or take-notes + (and checkdoc-autofix-flag + (not (eq checkdoc-autofix-flag 'never))))) (checkdoc-ispell-init) + (unless checkdoc-spellcheck-documentation-flag + ;; this happens when (checkdoc-ispell-init) can't start `ispell-program-name' + (user-error "No spellchecker installed: check the variable `ispell-program-name'.")) (save-excursion (skip-chars-forward "^a-zA-Z") - (let ((word nil) (sym nil) (case-fold-search nil) (err nil)) - (while (and (not err) (< (point) end)) - (if (save-excursion (forward-char -1) (looking-at "[('`]")) - ;; Skip lists describing meta-syntax, or bound variables - (forward-sexp 1) - (setq word (buffer-substring-no-properties - (point) (progn - (skip-chars-forward "a-zA-Z-") - (point))) - sym (intern-soft word)) - (if (and sym (or (boundp sym) (fboundp sym))) - ;; This is probably repetitive in most cases, but not always. - nil - ;; Find out how we spell-check this word. - (if (or - ;; All caps w/ option th, or s tacked on the end - ;; for pluralization or number. - (string-match "^[A-Z][A-Z]+\\(s\\|th\\)?$" word) - (looking-at "}") ; a keymap expression - ) - nil - (save-excursion - (if (not (eq checkdoc-autofix-flag 'never)) - (let ((lk last-input-event)) - (ispell-word nil t) - (if (not (equal last-input-event lk)) - (progn - (sit-for 0) - (message "Continuing...")))) - ;; Nothing here. - ))))) - (skip-chars-forward "^a-zA-Z")) - err)))) + (let (word sym case-fold-search err word-beginning word-end) + (while (and (not err) (< (point) end)) + (if (save-excursion (forward-char -1) (looking-at "[('`]")) + ;; Skip lists describing meta-syntax, or bound variables + (forward-sexp 1) + (setq word-beginning (point) + word-end (progn + (skip-chars-forward "a-zA-Z-") + (point)) + word (buffer-substring-no-properties word-beginning word-end) + sym (intern-soft word)) + (unless (and sym (or (boundp sym) (fboundp sym))) + ;; Find out how we spell-check this word. + (unless (or + ;; All caps w/ option th, or s tacked on the end + ;; for pluralization or number. + (string-match "^[A-Z][A-Z]+\\(s\\|th\\)?$" word) + (looking-at "}") ; a keymap expression + ) + (save-excursion + (let ((lk last-input-event)) + (if take-notes + (progn + (unless (ispell-correct-p) + (checkdoc-create-error + (ispell-error-checking-word word) + word-beginning word-end))) + (ispell-word nil t)) + (if (not (equal last-input-event lk)) + (progn + (sit-for 0) + (message "Continuing...")))))))) + (skip-chars-forward "^a-zA-Z")) + err)))) ;;; Rogue space checking engine ;; |