branch: elpa/package-lint commit 84ba63ca9668f6a1ffd8fa7e66792037a19e5d81 Author: Steve Purcell <st...@sanityinc.com> Commit: Steve Purcell <st...@sanityinc.com>
Support multi-line headers, and report errors at the header value loc, not eol --- package-lint-test.el | 6 ++-- package-lint.el | 99 ++++++++++++++++++++++++++++------------------------ 2 files changed, 56 insertions(+), 49 deletions(-) diff --git a/package-lint-test.el b/package-lint-test.el index daf631e79a..d01c3b18a9 100644 --- a/package-lint-test.el +++ b/package-lint-test.el @@ -153,7 +153,7 @@ headers and provide form." (ert-deftest package-lint-test-warn-no-standard-keyword () (should (equal - '((6 3 warning "You should include standard keywords: see the variable `finder-known-keywords'.")) + '((6 13 warning "You should include standard keywords: see the variable `finder-known-keywords'.")) (package-lint-test--run ";; Keywords: foo")))) (ert-deftest package-lint-test-no-warning-if-at-least-one-standard-keyword () @@ -342,11 +342,11 @@ Alternatively, depend on (emacs \"24.3\") or greater, in which cl-lib is bundled (ert-deftest package-lint-test-warning-cl-lib-not-needed () (should (member - '(6 52 warning "An explicit dependency on cl-lib <= 1.0 is not needed on Emacs >= 24.3.") + '(6 21 warning "An explicit dependency on cl-lib <= 1.0 is not needed on Emacs >= 24.3.") (package-lint-test--run ";; Package-Requires: ((emacs \"25.1\") (cl-lib \"1.0\"))"))) (should (member - '(6 52 warning "An explicit dependency on cl-lib <= 1.0 is not needed on Emacs >= 24.3.") + '(6 21 warning "An explicit dependency on cl-lib <= 1.0 is not needed on Emacs >= 24.3.") (package-lint-test--run ";; Package-Requires: ((emacs \"24.3\") (cl-lib \"0.5\"))")))) (ert-deftest package-lint-test-warning-cl () diff --git a/package-lint.el b/package-lint.el index 23a96a5dca..1d87188b51 100644 --- a/package-lint.el +++ b/package-lint.el @@ -392,7 +392,7 @@ Instead it should use `user-emacs-directory' or `locate-user-emacs-file'." (defun package-lint--check-keywords-list () "Verify that package keywords are listed in `finder-known-keywords'." (when (package-lint--goto-header "Keywords") - (let ((err-pos (match-beginning 2))) + (let ((err-pos (point))) (let ((keywords (lm-keywords-list))) (unless (cl-some (lambda (keyword) (assoc (intern keyword) finder-known-keywords)) keywords) (package-lint--error-at-point @@ -403,18 +403,15 @@ Instead it should use `user-emacs-directory' or `locate-user-emacs-file'." (defun package-lint--check-url-header () "Verify that the package has an HTTPS or HTTP Homepage/URL header." (if (package-lint--goto-header "\\(?:URL\\|Homepage\\)") - (let ((url (match-string 3)) - (url-start (match-beginning 3))) - (when (string-match-p "^<.*>$" url) - (setq url (substring url 1 -1) - url-start (1+ url-start)) - (backward-char 1)) - (unless (and (equal (thing-at-point 'url) url) - (string-match-p "^https?://" url)) - (package-lint--error-at-point - 'error - "Package URLs should be a single HTTPS or HTTP URL." - url-start))) + (progn + (when (looking-at "<.*>$") + (forward-char 1)) + (let ((url (thing-at-point 'url))) + (unless (and url (string-match-p "^https?://" url)) + (package-lint--error-at-point + 'error + "Package URLs should be a single HTTPS or HTTP URL." + (point))))) (package-lint--error-at-bob 'error "Package should have a Homepage or URL header."))) @@ -423,27 +420,27 @@ Instead it should use `user-emacs-directory' or `locate-user-emacs-file'." "Check the contents of the \"Package-Requires\" header. Return a list of well-formed dependencies, same as `package-lint--check-well-formed-dependencies'." - (when (package-lint--goto-header "Package-Requires") - (let ((position (match-beginning 3)) - (deps (match-string 3))) - (condition-case err - (pcase-let ((`(,parsed-deps . ,parse-end-pos) (read-from-string deps))) - (unless (= parse-end-pos (length deps)) - (package-lint--error-at-bol - 'error - "More than one expression provided.")) - (let ((deps (package-lint--check-well-formed-dependencies position parsed-deps))) - (package-lint--check-emacs-version deps) - (package-lint--check-packages-installable deps) - (package-lint--check-deps-use-non-snapshot-version deps) - (package-lint--check-deps-do-not-use-zero-versions deps) - (package-lint--check-cl-lib-version deps) - deps)) - (error - (package-lint--error-at-bol - 'error - (format "Couldn't parse \"Package-Requires\" header: %s" (error-message-string err))) - nil))))) + (let ((deps (package-lint--goto-header "Package-Requires"))) + (when deps + (let ((position (point))) + (condition-case err + (pcase-let ((`(,parsed-deps . ,parse-end-pos) (read-from-string deps))) + (unless (= parse-end-pos (length deps)) + (package-lint--error-at-bol + 'error + "More than one expression provided.")) + (let ((deps (package-lint--check-well-formed-dependencies position parsed-deps))) + (package-lint--check-emacs-version deps) + (package-lint--check-packages-installable deps) + (package-lint--check-deps-use-non-snapshot-version deps) + (package-lint--check-deps-do-not-use-zero-versions deps) + (package-lint--check-cl-lib-version deps) + deps)) + (error + (package-lint--error-at-bol + 'error + (format "Couldn't parse \"Package-Requires\" header: %s" (error-message-string err))) + nil)))))) (defun package-lint--check-well-formed-dependencies (position parsed-deps) "Check that dependencies listed at POSITION are well-formed. @@ -848,7 +845,7 @@ Alternatively, depend on (emacs \"24.3\") or greater, in which cl-lib is bundled (package-lint--error-at-point 'warning (format "\"%s\" is not a valid version. MELPA will handle this, but other archives will not." version) - (match-beginning 3))) + (point))) (package-lint--error-at-bob 'warning "\"Version:\" or \"Package-Version:\" header is missing. MELPA will handle this, but other archives will not.")))) @@ -1153,18 +1150,28 @@ Lines consisting only of whitespace or empty comments are considered empty." (lambda (v1 v2) (not (version-list-< v1 v2))))) (aref descriptors 0)))) -(defun package-lint--goto-header (header-name) +(defun package-lint--goto-header (header-name &optional multiline) "Move to the first occurrence of HEADER-NAME in the file. -If the return value is non-nil, then point will be at the end of -the file, and the second and third match groups will contain the name and -value of the header with any leading or trailing whitespace removed." - (let ((initial-point (point))) - (goto-char (point-min)) - (let ((case-fold-search t)) - (if (re-search-forward (concat (lm-get-header-re header-name) "\\(.*?\\) *$") nil t) - (match-string-no-properties 3) - (goto-char initial-point) - nil)))) +If the return value is non-nil, then point will be at the +beginning of the header's value, and the second and third match +groups will contain the name and value of the header with any +leading or trailing whitespace removed. + +If MULTILINE is non-nil, allow the header value to span lines, and return +them as a list of strings." + (goto-char (point-min)) + (when (re-search-forward (concat (lm-get-header-re header-name) "\\(.*?\\) *$") (lm-code-mark) t) + (let ((start-pos (match-beginning 3)) + (val (match-string-no-properties 3))) + (when multiline + (setq val (list val)) + (forward-line 1) + (while (looking-at "^;+\\(\t\\|[\t\s]\\{2,\\}\\)\\(.+\\)") + (push (match-string-no-properties 2) val) + (forward-line 1)) + (nreverse val)) + (goto-char start-pos) + val))) (defun package-lint--update-or-insert-version (version) "Ensure current buffer has a \"Version: VERSION\" header."