diff options
| author | Damien Cassou | 2019-12-27 15:35:52 +0100 |
|---|---|---|
| committer | Eli Zaretskii | 2020-01-16 20:41:48 +0200 |
| commit | 25adbc4a5ecc3e16625c0171607e3153bbdf7ab1 (patch) | |
| tree | afcf3388aaa4a4e33fa3da39ff7dead458bfd7ee | |
| parent | 1b1aaf37dc745570ec5202c8cc596591f4afa38d (diff) | |
| download | emacs-25adbc4a5ecc3e16625c0171607e3153bbdf7ab1.tar.gz emacs-25adbc4a5ecc3e16625c0171607e3153bbdf7ab1.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.
| -rw-r--r-- | lisp/emacs-lisp/checkdoc.el | 118 | ||||
| -rw-r--r-- | lisp/textmodes/ispell.el | 50 |
2 files changed, 106 insertions, 62 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." | |||
| 849 | ;; every test is responsible for returning the cursor. | 849 | ;; every test is responsible for returning the cursor. |
| 850 | (or (and buffer-file-name ;; only check comments in a file | 850 | (or (and buffer-file-name ;; only check comments in a file |
| 851 | (checkdoc-comments)) | 851 | (checkdoc-comments)) |
| 852 | (checkdoc-start) | 852 | (checkdoc-start take-notes) |
| 853 | (checkdoc-message-text) | 853 | (checkdoc-message-text) |
| 854 | (checkdoc-rogue-spaces) | 854 | (checkdoc-rogue-spaces) |
| 855 | (when checkdoc-package-keywords-flag | 855 | (when checkdoc-package-keywords-flag |
| @@ -902,7 +902,7 @@ buffer and save warnings in a separate buffer." | |||
| 902 | ;; the user is navigating down through the buffer. | 902 | ;; the user is navigating down through the buffer. |
| 903 | (while (and (not wrong) (checkdoc-next-docstring)) | 903 | (while (and (not wrong) (checkdoc-next-docstring)) |
| 904 | ;; OK, let's look at the doc string. | 904 | ;; OK, let's look at the doc string. |
| 905 | (setq msg (checkdoc-this-string-valid)) | 905 | (setq msg (checkdoc-this-string-valid take-notes)) |
| 906 | (if msg (setq wrong (point))))) | 906 | (if msg (setq wrong (point))))) |
| 907 | (if wrong | 907 | (if wrong |
| 908 | (progn | 908 | (progn |
| @@ -1284,12 +1284,15 @@ checking of documentation strings. | |||
| 1284 | 1284 | ||
| 1285 | ;;; Checking engines | 1285 | ;;; Checking engines |
| 1286 | ;; | 1286 | ;; |
| 1287 | (defun checkdoc-this-string-valid () | 1287 | (defun checkdoc-this-string-valid (&optional take-notes) |
| 1288 | "Return a message string if the current doc string is invalid. | 1288 | "Return a message string if the current doc string is invalid. |
| 1289 | Check for style only, such as the first line always being a complete | 1289 | Check for style only, such as the first line always being a complete |
| 1290 | sentence, whitespace restrictions, and making sure there are no | 1290 | sentence, whitespace restrictions, and making sure there are no |
| 1291 | hard-coded key-codes such as C-[char] or mouse-[number] in the comment. | 1291 | hard-coded key-codes such as C-[char] or mouse-[number] in the comment. |
| 1292 | See the style guide in the Emacs Lisp manual for more details." | 1292 | See the style guide in the Emacs Lisp manual for more details. |
| 1293 | |||
| 1294 | With a non-nil TAKE-NOTES, store all errors found in a warnings | ||
| 1295 | buffer, otherwise stop after the first error." | ||
| 1293 | 1296 | ||
| 1294 | ;; Jump over comments between the last object and the doc string | 1297 | ;; Jump over comments between the last object and the doc string |
| 1295 | (while (looking-at "[ \t\n]*;") | 1298 | (while (looking-at "[ \t\n]*;") |
| @@ -1366,13 +1369,16 @@ documentation string") | |||
| 1366 | (point) (+ (point) 1) t))))) | 1369 | (point) (+ (point) 1) t))))) |
| 1367 | (if (and (not err) (= (following-char) ?\")) | 1370 | (if (and (not err) (= (following-char) ?\")) |
| 1368 | (with-syntax-table checkdoc-syntax-table | 1371 | (with-syntax-table checkdoc-syntax-table |
| 1369 | (checkdoc-this-string-valid-engine fp)) | 1372 | (checkdoc-this-string-valid-engine fp take-notes)) |
| 1370 | err))) | 1373 | err))) |
| 1371 | 1374 | ||
| 1372 | (defun checkdoc-this-string-valid-engine (fp) | 1375 | (defun checkdoc-this-string-valid-engine (fp &optional take-notes) |
| 1373 | "Return an error list or string if the current doc string is invalid. | 1376 | "Return an error list or string if the current doc string is invalid. |
| 1374 | Depends on `checkdoc-this-string-valid' to reset the syntax table so that | 1377 | Depends on `checkdoc-this-string-valid' to reset the syntax table so that |
| 1375 | regexp short cuts work. FP is the function defun information." | 1378 | regexp short cuts work. FP is the function defun information. |
| 1379 | |||
| 1380 | With a non-nil TAKE-NOTES, store all errors found in a warnings | ||
| 1381 | buffer, otherwise stop after the first error." | ||
| 1376 | (let ((case-fold-search nil) | 1382 | (let ((case-fold-search nil) |
| 1377 | ;; Use a marker so if an early check modifies the text, | 1383 | ;; Use a marker so if an early check modifies the text, |
| 1378 | ;; we won't accidentally lose our place. This could cause | 1384 | ;; we won't accidentally lose our place. This could cause |
| @@ -1864,7 +1870,7 @@ Replace with \"%s\"? " original replace) | |||
| 1864 | ;; Make sure the doc string has correctly spelled English words | 1870 | ;; Make sure the doc string has correctly spelled English words |
| 1865 | ;; in it. This function is extracted due to its complexity, | 1871 | ;; in it. This function is extracted due to its complexity, |
| 1866 | ;; and reliance on the Ispell program. | 1872 | ;; and reliance on the Ispell program. |
| 1867 | (checkdoc-ispell-docstring-engine e) | 1873 | (checkdoc-ispell-docstring-engine e take-notes) |
| 1868 | ;; User supplied checks | 1874 | ;; User supplied checks |
| 1869 | (save-excursion (checkdoc-run-hooks 'checkdoc-style-functions fp e)) | 1875 | (save-excursion (checkdoc-run-hooks 'checkdoc-style-functions fp e)) |
| 1870 | ;; Done! | 1876 | ;; Done! |
| @@ -2090,6 +2096,10 @@ If the offending word is in a piece of quoted text, then it is skipped." | |||
| 2090 | ;; | 2096 | ;; |
| 2091 | (defvar ispell-process) | 2097 | (defvar ispell-process) |
| 2092 | (declare-function ispell-buffer-local-words "ispell" ()) | 2098 | (declare-function ispell-buffer-local-words "ispell" ()) |
| 2099 | (declare-function ispell-correct-p "ispell" ()) | ||
| 2100 | (declare-function ispell-set-spellchecker-params "ispell" ()) | ||
| 2101 | (declare-function ispell-accept-buffer-local-defs "ispell" ()) | ||
| 2102 | (declare-function ispell-error-checking-word "ispell" (word)) | ||
| 2093 | 2103 | ||
| 2094 | (defun checkdoc-ispell-init () | 2104 | (defun checkdoc-ispell-init () |
| 2095 | "Initialize Ispell process (default version) with Lisp words. | 2105 | "Initialize Ispell process (default version) with Lisp words. |
| @@ -2100,58 +2110,66 @@ nil." | |||
| 2100 | (unless ispell-process | 2110 | (unless ispell-process |
| 2101 | (condition-case nil | 2111 | (condition-case nil |
| 2102 | (progn | 2112 | (progn |
| 2103 | (ispell-buffer-local-words) | 2113 | (ispell-set-spellchecker-params) ; Initialize variables and dicts alists |
| 2114 | (ispell-accept-buffer-local-defs) ; use the correct dictionary | ||
| 2104 | ;; This code copied in part from ispell.el Emacs 19.34 | 2115 | ;; This code copied in part from ispell.el Emacs 19.34 |
| 2105 | (dolist (w checkdoc-ispell-lisp-words) | 2116 | (dolist (w checkdoc-ispell-lisp-words) |
| 2106 | (process-send-string ispell-process (concat "@" w "\n")))) | 2117 | (process-send-string ispell-process (concat "@" w "\n")))) |
| 2107 | (error (setq checkdoc-spellcheck-documentation-flag nil))))) | 2118 | (error (setq checkdoc-spellcheck-documentation-flag nil))))) |
| 2108 | 2119 | ||
| 2109 | (defun checkdoc-ispell-docstring-engine (end) | 2120 | (defun checkdoc-ispell-docstring-engine (end &optional take-notes) |
| 2110 | "Run the Ispell tools on the doc string between point and END. | 2121 | "Run the Ispell tools on the doc string between point and END. |
| 2111 | Since Ispell isn't Lisp-smart, we must pre-process the doc string | 2122 | Since Ispell isn't Lisp-smart, we must pre-process the doc string |
| 2112 | before using the Ispell engine on it." | 2123 | before using the Ispell engine on it. |
| 2113 | (if (or (not checkdoc-spellcheck-documentation-flag) | 2124 | |
| 2114 | ;; If the user wants no questions or fixing, then we must | 2125 | With a non-nil TAKE-NOTES, store all errors found in a warnings |
| 2115 | ;; disable spell checking as not useful. | 2126 | buffer, otherwise stop after the first error." |
| 2116 | (not checkdoc-autofix-flag) | 2127 | (when (and checkdoc-spellcheck-documentation-flag |
| 2117 | (eq checkdoc-autofix-flag 'never)) | 2128 | ;; If the user wants no questions or fixing, then we must |
| 2118 | nil | 2129 | ;; disable spell checking as not useful. |
| 2130 | (or take-notes | ||
| 2131 | (and checkdoc-autofix-flag | ||
| 2132 | (not (eq checkdoc-autofix-flag 'never))))) | ||
| 2119 | (checkdoc-ispell-init) | 2133 | (checkdoc-ispell-init) |
| 2134 | (unless checkdoc-spellcheck-documentation-flag | ||
| 2135 | ;; this happens when (checkdoc-ispell-init) can't start `ispell-program-name' | ||
| 2136 | (user-error "No spellchecker installed: check the variable `ispell-program-name'.")) | ||
| 2120 | (save-excursion | 2137 | (save-excursion |
| 2121 | (skip-chars-forward "^a-zA-Z") | 2138 | (skip-chars-forward "^a-zA-Z") |
| 2122 | (let ((word nil) (sym nil) (case-fold-search nil) (err nil)) | 2139 | (let (word sym case-fold-search err word-beginning word-end) |
| 2123 | (while (and (not err) (< (point) end)) | 2140 | (while (and (not err) (< (point) end)) |
| 2124 | (if (save-excursion (forward-char -1) (looking-at "[('`]")) | 2141 | (if (save-excursion (forward-char -1) (looking-at "[('`]")) |
| 2125 | ;; Skip lists describing meta-syntax, or bound variables | 2142 | ;; Skip lists describing meta-syntax, or bound variables |
| 2126 | (forward-sexp 1) | 2143 | (forward-sexp 1) |
| 2127 | (setq word (buffer-substring-no-properties | 2144 | (setq word-beginning (point) |
| 2128 | (point) (progn | 2145 | word-end (progn |
| 2129 | (skip-chars-forward "a-zA-Z-") | 2146 | (skip-chars-forward "a-zA-Z-") |
| 2130 | (point))) | 2147 | (point)) |
| 2131 | sym (intern-soft word)) | 2148 | word (buffer-substring-no-properties word-beginning word-end) |
| 2132 | (if (and sym (or (boundp sym) (fboundp sym))) | 2149 | sym (intern-soft word)) |
| 2133 | ;; This is probably repetitive in most cases, but not always. | 2150 | (unless (and sym (or (boundp sym) (fboundp sym))) |
| 2134 | nil | 2151 | ;; Find out how we spell-check this word. |
| 2135 | ;; Find out how we spell-check this word. | 2152 | (unless (or |
| 2136 | (if (or | 2153 | ;; All caps w/ option th, or s tacked on the end |
| 2137 | ;; All caps w/ option th, or s tacked on the end | 2154 | ;; for pluralization or number. |
| 2138 | ;; for pluralization or number. | 2155 | (string-match "^[A-Z][A-Z]+\\(s\\|th\\)?$" word) |
| 2139 | (string-match "^[A-Z][A-Z]+\\(s\\|th\\)?$" word) | 2156 | (looking-at "}") ; a keymap expression |
| 2140 | (looking-at "}") ; a keymap expression | 2157 | ) |
| 2141 | ) | 2158 | (save-excursion |
| 2142 | nil | 2159 | (let ((lk last-input-event)) |
| 2143 | (save-excursion | 2160 | (if take-notes |
| 2144 | (if (not (eq checkdoc-autofix-flag 'never)) | 2161 | (progn |
| 2145 | (let ((lk last-input-event)) | 2162 | (unless (ispell-correct-p) |
| 2146 | (ispell-word nil t) | 2163 | (checkdoc-create-error |
| 2147 | (if (not (equal last-input-event lk)) | 2164 | (ispell-error-checking-word word) |
| 2148 | (progn | 2165 | word-beginning word-end))) |
| 2149 | (sit-for 0) | 2166 | (ispell-word nil t)) |
| 2150 | (message "Continuing...")))) | 2167 | (if (not (equal last-input-event lk)) |
| 2151 | ;; Nothing here. | 2168 | (progn |
| 2152 | ))))) | 2169 | (sit-for 0) |
| 2153 | (skip-chars-forward "^a-zA-Z")) | 2170 | (message "Continuing...")))))))) |
| 2154 | err)))) | 2171 | (skip-chars-forward "^a-zA-Z")) |
| 2172 | err)))) | ||
| 2155 | 2173 | ||
| 2156 | ;;; Rogue space checking engine | 2174 | ;;; Rogue space checking engine |
| 2157 | ;; | 2175 | ;; |
diff --git a/lisp/textmodes/ispell.el b/lisp/textmodes/ispell.el index 53a45433085..c06f3915faa 100644 --- a/lisp/textmodes/ispell.el +++ b/lisp/textmodes/ispell.el | |||
| @@ -1951,18 +1951,7 @@ quit spell session exited." | |||
| 1951 | (or quietly | 1951 | (or quietly |
| 1952 | (message "Checking spelling of %s..." | 1952 | (message "Checking spelling of %s..." |
| 1953 | (funcall ispell-format-word-function word))) | 1953 | (funcall ispell-format-word-function word))) |
| 1954 | (ispell-send-string "%\n") ; put in verbose mode | 1954 | (setq poss (ispell--run-on-word word)) |
| 1955 | (ispell-send-string (concat "^" word "\n")) | ||
| 1956 | ;; wait until ispell has processed word | ||
| 1957 | (while (progn | ||
| 1958 | (ispell-accept-output) | ||
| 1959 | (not (string= "" (car ispell-filter))))) | ||
| 1960 | ;;(ispell-send-string "!\n") ;back to terse mode. | ||
| 1961 | (setq ispell-filter (cdr ispell-filter)) ; remove extra \n | ||
| 1962 | (if (and ispell-filter (listp ispell-filter)) | ||
| 1963 | (if (> (length ispell-filter) 1) | ||
| 1964 | (error "Ispell and its process have different character maps") | ||
| 1965 | (setq poss (ispell-parse-output (car ispell-filter))))) | ||
| 1966 | (cond ((eq poss t) | 1955 | (cond ((eq poss t) |
| 1967 | (or quietly | 1956 | (or quietly |
| 1968 | (message "%s is correct" | 1957 | (message "%s is correct" |
| @@ -2024,6 +2013,43 @@ quit spell session exited." | |||
| 2024 | (goto-char cursor-location) ; return to original location | 2013 | (goto-char cursor-location) ; return to original location |
| 2025 | replace)))) | 2014 | replace)))) |
| 2026 | 2015 | ||
| 2016 | (defun ispell--run-on-word (word) | ||
| 2017 | "Run ispell on WORD." | ||
| 2018 | (ispell-send-string "%\n") ; put in verbose mode | ||
| 2019 | (ispell-send-string (concat "^" word "\n")) | ||
| 2020 | ;; wait until ispell has processed word | ||
| 2021 | (while (progn | ||
| 2022 | (ispell-accept-output) | ||
| 2023 | (not (string= "" (car ispell-filter))))) | ||
| 2024 | (setq ispell-filter (cdr ispell-filter)) | ||
| 2025 | (when (and ispell-filter (listp ispell-filter)) | ||
| 2026 | (if (> (length ispell-filter) 1) | ||
| 2027 | (error "Ispell and its processs have different character maps: %s" ispell-filter) | ||
| 2028 | (ispell-parse-output (car ispell-filter))))) | ||
| 2029 | |||
| 2030 | (defun ispell-error-checking-word (word) | ||
| 2031 | "Return a string describing that checking for WORD failed." | ||
| 2032 | (format "Error checking word %s using %s with %s dictionary" | ||
| 2033 | (funcall ispell-format-word-function word) | ||
| 2034 | (file-name-nondirectory ispell-program-name) | ||
| 2035 | (or ispell-current-dictionary "default"))) | ||
| 2036 | |||
| 2037 | (defun ispell-correct-p (&optional following) | ||
| 2038 | "Return t if the word at point is correct. Nil otherwise. | ||
| 2039 | |||
| 2040 | If optional argument FOLLOWING is non-nil then the following | ||
| 2041 | word (rather than preceding) is checked when the cursor is not | ||
| 2042 | over a word." | ||
| 2043 | (save-excursion | ||
| 2044 | ;; reset ispell-filter so it only contains the result of | ||
| 2045 | ;; spell-checking the current-word: | ||
| 2046 | (setq ispell-filter nil) | ||
| 2047 | (let* ((word-and-boundaries (ispell-get-word following)) | ||
| 2048 | (word (car word-and-boundaries)) | ||
| 2049 | (poss (ispell--run-on-word word))) | ||
| 2050 | (unless poss (error (ispell-error-checking-word word))) | ||
| 2051 | (or (eq poss t) | ||
| 2052 | (stringp poss))))) | ||
| 2027 | 2053 | ||
| 2028 | (defun ispell-get-word (following &optional extra-otherchars) | 2054 | (defun ispell-get-word (following &optional extra-otherchars) |
| 2029 | "Return the word for spell-checking according to ispell syntax. | 2055 | "Return the word for spell-checking according to ispell syntax. |