branch: elpa/package-lint
commit 84ba63ca9668f6a1ffd8fa7e66792037a19e5d81
Author: Steve Purcell <[email protected]>
Commit: Steve Purcell <[email protected]>
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."