From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!.POSTED.blaine.gmane.org!not-for-mail From: Damien Cassou Newsgroups: gmane.emacs.bugs Subject: bug#38583: [PATCH] 27.0.50; Add unattended spell-checking to checkdoc Date: Mon, 13 Jan 2020 06:19:56 +0100 Message-ID: <87o8v8q6f7.fsf@cassou.me> References: <87immlw7zo.fsf@cassou.me> <83blsc8ujl.fsf@gnu.org> <871rt8gfy0.fsf@cassou.me> <83mubw6lpb.fsf@gnu.org> <87o8vu7zqp.fsf@cassou.me> <83d0c9q2t3.fsf@gnu.org> <87blrt7rso.fsf@cassou.me> <874kx28b7m.fsf@cassou.me> <83sgkm6uxj.fsf@gnu.org> Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" Injection-Info: blaine.gmane.org; posting-host="blaine.gmane.org:195.159.176.226"; logging-data="6147"; mail-complaints-to="usenet@blaine.gmane.org" Cc: alex.branham@gmail.com, eggert@cs.ucla.edu, k.stevens@ieee.org, 38583@debbugs.gnu.org, zappo@gnu.org, larsi@gnus.org To: Eli Zaretskii Original-X-From: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Mon Jan 13 06:22:20 2020 Return-path: Envelope-to: geb-bug-gnu-emacs@m.gmane-mx.org Original-Received: from lists.gnu.org ([209.51.188.17]) by blaine.gmane.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.89) (envelope-from ) id 1iqsA8-000zWg-TK for geb-bug-gnu-emacs@m.gmane-mx.org; Mon, 13 Jan 2020 06:21:17 +0100 Original-Received: from localhost ([::1]:45822 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1iqsA7-0008Ge-9O for geb-bug-gnu-emacs@m.gmane-mx.org; Mon, 13 Jan 2020 00:21:15 -0500 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]:47741) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1iqs9x-0008GM-EK for bug-gnu-emacs@gnu.org; Mon, 13 Jan 2020 00:21:07 -0500 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1iqs9u-0004Hx-Cd for bug-gnu-emacs@gnu.org; Mon, 13 Jan 2020 00:21:04 -0500 Original-Received: from debbugs.gnu.org ([209.51.188.43]:52549) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1iqs9u-0004HV-7x for bug-gnu-emacs@gnu.org; Mon, 13 Jan 2020 00:21:02 -0500 Original-Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1iqs9u-0004OG-3I for bug-gnu-emacs@gnu.org; Mon, 13 Jan 2020 00:21:02 -0500 X-Loop: help-debbugs@gnu.org Resent-From: Damien Cassou Original-Sender: "Debbugs-submit" Resent-CC: bug-gnu-emacs@gnu.org Resent-Date: Mon, 13 Jan 2020 05:21:02 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 38583 X-GNU-PR-Package: emacs X-GNU-PR-Keywords: patch X-Debbugs-Original-Cc: alex.branham@gmail.com, eggert@cs.ucla.edu, k.stevens@ieee.org, bug-gnu-emacs@gnu.org, larsi@gnus.org, zappo@gnu.org Original-Received: via spool by submit@debbugs.gnu.org id=B.157889281716796 (code B ref -1); Mon, 13 Jan 2020 05:21:02 +0000 Original-Received: (at submit) by debbugs.gnu.org; 13 Jan 2020 05:20:17 +0000 Original-Received: from localhost ([127.0.0.1]:58522 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1iqs9A-0004Mp-E8 for submit@debbugs.gnu.org; Mon, 13 Jan 2020 00:20:17 -0500 Original-Received: from lists.gnu.org ([209.51.188.17]:42957) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1iqs98-0004Mh-Aq for submit@debbugs.gnu.org; Mon, 13 Jan 2020 00:20:15 -0500 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]:47467) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1iqs95-00087Z-Va for bug-gnu-emacs@gnu.org; Mon, 13 Jan 2020 00:20:14 -0500 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1iqs93-0003CB-7o for bug-gnu-emacs@gnu.org; Mon, 13 Jan 2020 00:20:11 -0500 Original-Received: from mail.choca.pics ([80.67.172.235]:51718) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1iqs92-00032j-RB; Mon, 13 Jan 2020 00:20:09 -0500 Original-Received: from localhost (localhost.localdomain [IPv6:::1]) by mail.choca.pics (Postfix) with ESMTP id D1C0B181A5046; Mon, 13 Jan 2020 06:19:59 +0100 (CET) Original-Received: from mail.choca.pics ([IPv6:::1]) by localhost (mail.choca.pics [IPv6:::1]) (amavisd-new, port 10032) with ESMTP id UDBjRRHPQhR3; Mon, 13 Jan 2020 06:19:58 +0100 (CET) Original-Received: from localhost (localhost.localdomain [IPv6:::1]) by mail.choca.pics (Postfix) with ESMTP id 857AC181A5042; Mon, 13 Jan 2020 06:19:58 +0100 (CET) X-Virus-Scanned: amavisd-new at choca.pics Original-Received: from mail.choca.pics ([IPv6:::1]) by localhost (mail.choca.pics [IPv6:::1]) (amavisd-new, port 10026) with ESMTP id PIkx3C42fJJ5; Mon, 13 Jan 2020 06:19:58 +0100 (CET) Original-Received: from luz4 (57.139.120.78.rev.sfr.net [78.120.139.57]) by mail.choca.pics (Postfix) with ESMTPSA id 191101819AC60; Mon, 13 Jan 2020 06:19:58 +0100 (CET) In-Reply-To: <83sgkm6uxj.fsf@gnu.org> X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 209.51.188.43 X-BeenThere: bug-gnu-emacs@gnu.org List-Id: "Bug reports for GNU Emacs, the Swiss army knife of text editors" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Original-Sender: "bug-gnu-emacs" Xref: news.gmane.org gmane.emacs.bugs:174520 Archived-At: --=-=-= Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable Eli Zaretskii writes: > Sorry, it fell through the cracks. no problem. > However, I was about to push it now, but compiling the modified > version generates warnings: [=E2=80=A6] > > Could you please augment the patch so that these warnings are avoided? fixed > Also, please mention the bug number in the commit log message. fixed --=20 Damien Cassou "Success is the ability to go from one failure to another without losing enthusiasm." --Winston Churchill --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=0001-Add-unattended-spell-checking-to-checkdoc.patch >From 46df39d2f7ddfe79dd55d8342ea20a2dc17ac31c Mon Sep 17 00:00:00 2001 From: Damien Cassou Date: Fri, 27 Dec 2019 15:35:52 +0100 Subject: [PATCH] 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. --- lisp/emacs-lisp/checkdoc.el | 118 +++++++++++++++++++++--------------- 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 93b9ffbe38..cbad6f0554 100644 --- a/lisp/emacs-lisp/checkdoc.el +++ b/lisp/emacs-lisp/checkdoc.el @@ -849,7 +849,7 @@ checkdoc-current-buffer ;; every test is responsible for returning the cursor. (or (and buffer-file-name ;; only check comments in a file (checkdoc-comments)) - (checkdoc-start) + (checkdoc-start take-notes) (checkdoc-message-text) (checkdoc-rogue-spaces) (when checkdoc-package-keywords-flag @@ -902,7 +902,7 @@ checkdoc-continue ;; 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 @@ checkdoc-create-common-verbs-regexp ;;; 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 @@ checkdoc-this-string-valid (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 @@ checkdoc-this-string-valid-engine ;; 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 @@ checkdoc-sentencespace-region-engine ;; (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 @@ checkdoc-ispell-init (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 ;; diff --git a/lisp/textmodes/ispell.el b/lisp/textmodes/ispell.el index 53a4543308..c06f3915fa 100644 --- a/lisp/textmodes/ispell.el +++ b/lisp/textmodes/ispell.el @@ -1951,18 +1951,7 @@ ispell-word (or quietly (message "Checking spelling of %s..." (funcall ispell-format-word-function word))) - (ispell-send-string "%\n") ; put in verbose mode - (ispell-send-string (concat "^" word "\n")) - ;; wait until ispell has processed word - (while (progn - (ispell-accept-output) - (not (string= "" (car ispell-filter))))) - ;;(ispell-send-string "!\n") ;back to terse mode. - (setq ispell-filter (cdr ispell-filter)) ; remove extra \n - (if (and ispell-filter (listp ispell-filter)) - (if (> (length ispell-filter) 1) - (error "Ispell and its process have different character maps") - (setq poss (ispell-parse-output (car ispell-filter))))) + (setq poss (ispell--run-on-word word)) (cond ((eq poss t) (or quietly (message "%s is correct" @@ -2024,6 +2013,43 @@ ispell-word (goto-char cursor-location) ; return to original location replace)))) +(defun ispell--run-on-word (word) + "Run ispell on WORD." + (ispell-send-string "%\n") ; put in verbose mode + (ispell-send-string (concat "^" word "\n")) + ;; wait until ispell has processed word + (while (progn + (ispell-accept-output) + (not (string= "" (car ispell-filter))))) + (setq ispell-filter (cdr ispell-filter)) + (when (and ispell-filter (listp ispell-filter)) + (if (> (length ispell-filter) 1) + (error "Ispell and its processs have different character maps: %s" ispell-filter) + (ispell-parse-output (car ispell-filter))))) + +(defun ispell-error-checking-word (word) + "Return a string describing that checking for WORD failed." + (format "Error checking word %s using %s with %s dictionary" + (funcall ispell-format-word-function word) + (file-name-nondirectory ispell-program-name) + (or ispell-current-dictionary "default"))) + +(defun ispell-correct-p (&optional following) + "Return t if the word at point is correct. Nil otherwise. + +If optional argument FOLLOWING is non-nil then the following +word (rather than preceding) is checked when the cursor is not +over a word." + (save-excursion + ;; reset ispell-filter so it only contains the result of + ;; spell-checking the current-word: + (setq ispell-filter nil) + (let* ((word-and-boundaries (ispell-get-word following)) + (word (car word-and-boundaries)) + (poss (ispell--run-on-word word))) + (unless poss (error (ispell-error-checking-word word))) + (or (eq poss t) + (stringp poss))))) (defun ispell-get-word (following &optional extra-otherchars) "Return the word for spell-checking according to ispell syntax. -- 2.24.1 --=-=-=--