X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/1fb87f1f1aa0947ec7b572a0ec1677c18aefc9f0..0e963201d03d9229bb8ac4323291d2b0119526ed:/lisp/emacs-lisp/checkdoc.el diff --git a/lisp/emacs-lisp/checkdoc.el b/lisp/emacs-lisp/checkdoc.el index 288e25e606..ecf6f8203a 100644 --- a/lisp/emacs-lisp/checkdoc.el +++ b/lisp/emacs-lisp/checkdoc.el @@ -1,6 +1,6 @@ -;;; checkdoc.el --- check documentation strings for style requirements +;;; checkdoc.el --- check documentation strings for style requirements -*- lexical-binding:t -*- -;; Copyright (C) 1997-1998, 2001-2015 Free Software Foundation, Inc. +;; Copyright (C) 1997-1998, 2001-2016 Free Software Foundation, Inc. ;; Author: Eric M. Ludlam ;; Version: 0.6.2 @@ -267,6 +267,12 @@ made in the style guide relating to order." :type 'boolean) ;;;###autoload(put 'checkdoc-arguments-in-order-flag 'safe-local-variable #'booleanp) +(defcustom checkdoc-package-keywords-flag nil + "Non-nil means warn if this file's package keywords are not recognized. +Currently, all recognized keywords must be on `finder-known-keywords'." + :version "25.1" + :type 'boolean) + (define-obsolete-variable-alias 'checkdoc-style-hooks 'checkdoc-style-functions "24.3") (defvar checkdoc-style-functions nil @@ -315,6 +321,7 @@ This should be set in an Emacs Lisp file's local variables." ;;;###autoload (defun checkdoc-list-of-strings-p (obj) + "Return t when OBJ is a list of strings." ;; this is a function so it might be shared by checkdoc-proper-noun-list ;; and/or checkdoc-ispell-lisp-words in the future (and (listp obj) @@ -741,7 +748,7 @@ buffer, otherwise searching starts at START-HERE." ;; Loop over docstrings. (while (checkdoc-next-docstring) (message "Searching for doc string spell error...%d%%" - (/ (* 100 (point)) (point-max))) + (floor (* 100.0 (point)) (point-max))) (if (looking-at "\"") (checkdoc-ispell-docstring-engine (save-excursion (forward-sexp 1) (point-marker))))) @@ -761,7 +768,7 @@ buffer, otherwise searching starts at START-HERE." ;; Loop over message strings. (while (checkdoc-message-text-next-string (point-max)) (message "Searching for message string spell error...%d%%" - (/ (* 100 (point)) (point-max))) + (floor (* 100.0 (point)) (point-max))) (if (looking-at "\"") (checkdoc-ispell-docstring-engine (save-excursion (forward-sexp 1) (point-marker))))) @@ -785,7 +792,7 @@ perform the fix." (condition-case nil (while (and (not msg) (checkdoc-next-docstring)) (message "Searching for doc string error...%d%%" - (/ (* 100 (point)) (point-max))) + (floor (* 100.0 (point)) (point-max))) (if (setq msg (checkdoc-this-string-valid)) (setq msg (cons msg (point))))) ;; Quit.. restore position, Other errors, leave alone @@ -807,7 +814,7 @@ assumes that the cursor is already positioned to perform the fix." (setq type (checkdoc-message-text-next-string (point-max)))) (message "Searching for message string error...%d%%" - (/ (* 100 (point)) (point-max))) + (floor (* 100.0 (point)) (point-max))) (if (setq msg (checkdoc-message-text-engine type)) (setq msg (cons msg (point))))) ;; Quit.. restore position, Other errors, leave alone @@ -866,10 +873,19 @@ otherwise stop after the first error." (checkdoc-start) (checkdoc-message-text) (checkdoc-rogue-spaces) + (when checkdoc-package-keywords-flag + (checkdoc-package-keywords)) (not (called-interactively-p 'interactive)) (if take-notes (checkdoc-show-diagnostics)) (message "Checking buffer for style...Done.")))) +;;;###autoload +(defun checkdoc-file (file) + "Check FILE for document, comment, error style, and rogue spaces." + (with-current-buffer (find-file-noselect file) + (let ((checkdoc-diagnostic-buffer "*warn*")) + (checkdoc-current-buffer t)))) + ;;;###autoload (defun checkdoc-start (&optional take-notes) "Start scanning the current buffer for documentation string style errors. @@ -1404,7 +1420,7 @@ regexp short cuts work. FP is the function defun information." (when (re-search-forward "^(" e t) (if (checkdoc-autofix-ask-replace (match-beginning 0) (match-end 0) - "Escape this '('? " + (format-message "Escape this `('? ") "\\(") nil (checkdoc-create-error @@ -1524,7 +1540,7 @@ may require more formatting") ;; Instead, use the `\\[...]' construct to stand for them. (save-excursion (let ((f nil) (m nil) (start (point)) - (re "[^`A-Za-z0-9_]\\([CMA]-[a-zA-Z]\\|\\(\\([CMA]-\\)?\ + (re "[^`‘A-Za-z0-9_]\\([CMA]-[a-zA-Z]\\|\\(\\([CMA]-\\)?\ mouse-[0-3]\\)\\)\\>")) ;; Find the first key sequence not in a sample (while (and (not f) (setq m (re-search-forward re e t))) @@ -1554,7 +1570,8 @@ mouse-[0-3]\\)\\)\\>")) (save-excursion (let ((case-fold-search t) (ret nil) mb me) - (while (and (re-search-forward "`\\(\\sw\\(\\sw\\|\\s_\\)+\\)'" e t) + (while (and (re-search-forward + "[`‘]\\(\\sw\\(\\sw\\|\\s_\\)+\\)['’]" e t) (not ret)) (let* ((ms1 (match-string 1)) (sym (intern-soft ms1))) @@ -1615,8 +1632,8 @@ function,command,variable,option or symbol." ms1)))))) (or ;; * The documentation string for a variable that is a ;; yes-or-no flag should start with words such as Non-nil - ;; means..., to make it clear that all non-`nil' values are - ;; equivalent and indicate explicitly what `nil' and non-`nil' + ;; means..., to make it clear that all non-nil values are + ;; equivalent and indicate explicitly what nil and non-nil ;; mean. ;; * If a user option variable records a true-or-false ;; condition, give it a name that ends in `-flag'. @@ -1663,14 +1680,15 @@ function,command,variable,option or symbol." ms1)))))) ;; Addendum: Make sure they appear in the doc in the same ;; order that they are found in the arg list. - (let ((args (cdr (cdr (cdr (cdr fp))))) + (let ((args (nthcdr 4 fp)) (last-pos 0) (found 1) (order (and (nth 3 fp) (car (nth 3 fp)))) (nocheck (append '("&optional" "&rest") (nth 3 fp))) (inopts nil)) (while (and args found (> found last-pos)) - (if (member (car args) nocheck) + (if (or (member (car args) nocheck) + (string-match "\\`_" (car args))) (setq args (cdr args) inopts t) (setq last-pos found @@ -1697,7 +1715,7 @@ function,command,variable,option or symbol." ms1)))))) e t)) (if (checkdoc-autofix-ask-replace (match-beginning 1) (match-end 1) - (format + (format-message "If this is the argument `%s', it should appear as %s. Fix? " (car args) (upcase (car args))) (upcase (car args)) t) @@ -1723,7 +1741,7 @@ function,command,variable,option or symbol." ms1)))))) (insert ".")) nil) (checkdoc-create-error - (format + (format-message "Argument `%s' should appear (as %s) in the doc string" (car args) (upcase (car args))) s (marker-position e))) @@ -1784,16 +1802,17 @@ Replace with \"%s\"? " original replace) ))) ;;* When a documentation string refers to a Lisp symbol, write it as ;; it would be printed (which usually means in lower case), with - ;; single-quotes around it. For example: `lambda'. There are two - ;; exceptions: write t and nil without single-quotes. (In this - ;; manual, we normally do use single-quotes for those symbols.) + ;; single-quotes around it. For example: ‘lambda’. There are two + ;; exceptions: write t and nil without single-quotes. (For + ;; compatibility with an older Emacs style, quoting with ` and ' + ;; also works, e.g., `lambda' is treated like ‘lambda’.) (save-excursion (let ((found nil) (start (point)) (msg nil) (ms nil)) (while (and (not msg) (re-search-forward ;; Ignore manual page references like ;; git-config(1). - "[^-([`':a-zA-Z]\\(\\w+[:-]\\(\\w\\|\\s_\\)+\\)[^](']" + "[^-([`'‘’:a-zA-Z]\\(\\w+[:-]\\(\\w\\|\\s_\\)+\\)[^]('’]" e t)) (setq ms (match-string 1)) ;; A . is a \s_ char, so we must remove periods from @@ -1806,16 +1825,16 @@ Replace with \"%s\"? " original replace) (setq found (intern-soft ms)) (or (boundp found) (fboundp found))) (progn - (setq msg (format "Add quotes around Lisp symbol `%s'? " - ms)) + (setq msg (format-message + "Add quotes around Lisp symbol `%s'? " ms)) (if (checkdoc-autofix-ask-replace (match-beginning 1) (+ (match-beginning 1) (length ms)) - msg (concat "`" ms "'") t) + msg (format-message "`%s'" ms) t) (setq msg nil) (setq msg - (format "Lisp symbol `%s' should appear in quotes" - ms)))))) + (format-message + "Lisp symbol `%s' should appear in quotes" ms)))))) (if msg (checkdoc-create-error msg (match-beginning 1) (+ (match-beginning 1) @@ -1823,7 +1842,7 @@ Replace with \"%s\"? " original replace) nil))) ;; t and nil case (save-excursion - (if (re-search-forward "\\(`\\(t\\|nil\\)'\\)" e t) + (if (re-search-forward "\\([`‘]\\(t\\|nil\\)['’]\\)" e t) (if (checkdoc-autofix-ask-replace (match-beginning 1) (match-end 1) (format "%s should not appear in quotes. Remove? " @@ -1831,7 +1850,7 @@ Replace with \"%s\"? " original replace) (match-string 2) t) nil (checkdoc-create-error - "Symbols t and nil should not appear in `...' quotes" + "Symbols t and nil should not appear in single quotes" (match-beginning 1) (match-end 1))))) ;; Here is some basic sentence formatting (checkdoc-sentencespace-region-engine (point) e) @@ -1936,7 +1955,7 @@ from the comment." "Return non-nil if the current point is in a code fragment. A code fragment is identified by an open parenthesis followed by a symbol which is a valid function or a word in all CAPS, or a parenthesis -that is quoted with the ' character. Only the region from START to LIMIT +that is quoted with the \\=' character. Only the region from START to LIMIT is allowed while searching for the bounding parenthesis." (save-match-data (save-restriction @@ -1988,7 +2007,7 @@ If the offending word is in a piece of quoted text, then it is skipped." (if (and (not (save-excursion (goto-char b) (forward-char -1) - (looking-at "`\\|\"\\|\\.\\|\\\\"))) + (looking-at "[`\".‘]\\|\\\\"))) ;; surrounded by /, as in a URL or filename: /emacs/ (not (and (= ?/ (char-after e)) (= ?/ (char-before b)))) @@ -2404,7 +2423,7 @@ Argument END is the maximum bounds to search in." According to the documentation for the function `error', the error list should not end with a period, and should start with a capital letter. The function `y-or-n-p' has similar constraints. -Argument TYPE specifies the type of question, such as `error or `y-or-n-p." +Argument TYPE specifies the type of question, such as `error' or `y-or-n-p'." ;; If type is nil, then attempt to derive it. (if (not type) (save-excursion @@ -2469,7 +2488,8 @@ Argument TYPE specifies the type of question, such as `error or `y-or-n-p." ;; If we see a ?, then replace with "? ". (if (checkdoc-autofix-ask-replace (match-beginning 0) (match-end 0) - "`y-or-n-p' argument should end with \"? \". Fix? " + (format-message + "`y-or-n-p' argument should end with \"? \". Fix? ") "? " t) nil (checkdoc-create-error @@ -2480,7 +2500,8 @@ Argument TYPE specifies the type of question, such as `error or `y-or-n-p." (looking-at " ")) (if (checkdoc-autofix-ask-replace (match-beginning 0) (match-end 0) - "`y-or-n-p' argument should end with \"? \". Fix? " + (format-message + "`y-or-n-p' argument should end with \"? \". Fix? ") "? " t) nil (checkdoc-create-error @@ -2492,7 +2513,8 @@ Argument TYPE specifies the type of question, such as `error or `y-or-n-p." (looking-at "\"")) (checkdoc-autofix-ask-replace (match-beginning 0) (match-end 0) - "`y-or-n-p' argument should end with \"? \". Fix? " + (format-message + "`y-or-n-p' argument should end with \"? \". Fix? ") "? \"" t)) nil (checkdoc-create-error @@ -2608,13 +2630,16 @@ function called to create the messages." "Store POINT and MSG as errors in the checkdoc diagnostic buffer." (setq checkdoc-pending-errors t) (let ((text (list "\n" (checkdoc-buffer-label) ":" - (int-to-string - (count-lines (point-min) (or point (point-min)))) - ": " msg))) - (with-current-buffer (get-buffer checkdoc-diagnostic-buffer) - (goto-char (point-max)) - (let ((inhibit-read-only t)) - (apply #'insert text))))) + (int-to-string + (count-lines (point-min) (or point (point-min)))) + ": " msg))) + (if (string= checkdoc-diagnostic-buffer "*warn*") + (warn (apply #'concat text)) + (with-current-buffer (get-buffer checkdoc-diagnostic-buffer) + (let ((inhibit-read-only t) + (pt (point-max))) + (goto-char pt) + (apply #'insert text)))))) (defun checkdoc-show-diagnostics () "Display the checkdoc diagnostic buffer in a temporary window." @@ -2631,6 +2656,39 @@ function called to create the messages." (setq checkdoc-pending-errors nil) nil))) +(defun checkdoc-get-keywords () + "Return a list of package keywords for the current file." + (save-excursion + (goto-char (point-min)) + (when (re-search-forward "^;; Keywords: \\(.*\\)$" nil t) + (split-string (match-string-no-properties 1) ", " t)))) + +(defvar finder-known-keywords) + +;;;###autoload +(defun checkdoc-package-keywords () + "Find package keywords that aren't in `finder-known-keywords'." + (interactive) + (require 'finder) + (let ((unrecognized-keys + (cl-remove-if + (lambda (x) (assoc (intern-soft x) finder-known-keywords)) + (checkdoc-get-keywords)))) + (if unrecognized-keys + (let* ((checkdoc-autofix-flag 'never) + (checkdoc-generate-compile-warnings-flag t)) + (save-excursion + (goto-char (point-min)) + (re-search-forward "^;; Keywords: \\(.*\\)$" nil t) + (checkdoc-start-section "checkdoc-package-keywords") + (checkdoc-create-error + (concat "Unrecognized keywords: " + (mapconcat #'identity unrecognized-keys ", ")) + (match-beginning 1) (match-end 1))) + (checkdoc-show-diagnostics)) + (when (called-interactively-p 'any) + (message "No Package Keyword Errors."))))) + (custom-add-option 'emacs-lisp-mode-hook 'checkdoc-minor-mode) (provide 'checkdoc)