From 6766c45bfd09d6cd2dee1e1400a80b137f6aaaa5 Mon Sep 17 00:00:00 2001 From: Nicolas Goaziou Date: Tue, 30 Nov 2021 15:39:17 +0100 Subject: [PATCH] org-lint: Add checkers for citations and related * lisp/org-lint.el (org-lint--checkers): Register new checkers. (org-lint-non-existent-bibliography): (org-lint-missing-print-bibliography): (org-lint-invalid-cite-export-declaration): (org-lint-incomplete-citation): New functions. * testing/lisp/test-org-lint.el (test-org-lint/non-existent-bibliography): (test-org-lint/missing-print-bibliography): (test-org-lint/invalid-cite-export-declaration): (test-org-lint/incomplete-citation): New tests. --- lisp/org-lint.el | 73 ++++++++++++++++++++++++++++++++++- testing/lisp/test-org-lint.el | 50 ++++++++++++++++++++++++ 2 files changed, 122 insertions(+), 1 deletion(-) diff --git a/lisp/org-lint.el b/lisp/org-lint.el index da5e6ae79..a35aed814 100644 --- a/lisp/org-lint.el +++ b/lisp/org-lint.el @@ -101,12 +101,17 @@ ;; - obsolete QUOTE section ;; - obsolete "file+application" link ;; - spurious colons in tags +;; - non-existent bibliography file +;; - missing "print_bibliography" keyword +;; - invalid "cite_export" value +;; - incomplete citation object ;;; Code: (require 'cl-lib) (require 'ob) +(require 'oc) (require 'ol) (require 'org-attach) (require 'org-macro) @@ -296,7 +301,24 @@ (make-org-lint-checker :name 'spurious-colons :description "Report spurious colons in tags" - :categories '(tags))) + :categories '(tags)) + (make-org-lint-checker + :name 'non-existent-bibliography + :description "Report invalid bibliography file" + :categories '(cite)) + (make-org-lint-checker + :name 'missing-print-bibliography + :description "Report missing \"print_bibliography\" keyword" + :categories '(cite)) + (make-org-lint-checker + :name 'invalid-cite-export-declaration + :description "Report invalid value for \"cite_export\" keyword" + :categories '(cite)) + (make-org-lint-checker + :name 'incomplete-citation + :description "Report incomplete citation object" + :categories '(cite) + :trust 'low)) "List of all available checkers.") (defun org-lint--collect-duplicates @@ -1121,6 +1143,55 @@ Use \"export %s\" instead" (list (org-element-property :begin h) "Tags contain a spurious colon"))))) +(defun org-lint-non-existent-bibliography (ast) + (org-element-map ast 'keyword + (lambda (k) + (when (equal "BIBLIOGRAPHY" (org-element-property :key k)) + (let ((file (org-strip-quotes (org-element-property :value k)))) + (and (not (file-remote-p file)) + (not (file-exists-p file)) + (list (org-element-property :begin k) + (format "Non-existent bibliography %S" file)))))))) + +(defun org-lint-missing-print-bibliography (ast) + (and (org-element-map ast 'citation #'identity nil t) + (not (org-element-map ast 'keyword + (lambda (k) + (equal "PRINT_BIBLIOGRAPHY" (org-element-property :key k))) + nil t)) + (list + (list (point-max) "Possibly missing \"PRINT_BIBLIOGRAPHY\" keyword")))) + +(defun org-lint-invalid-cite-export-declaration (ast) + (org-element-map ast 'keyword + (lambda (k) + (when (equal "CITE_EXPORT" (org-element-property :key k)) + (let ((value (org-element-property :value k)) + (source (org-element-property :begin k))) + (if (equal value "") + (list source "Missing export processor name") + (condition-case _ + (pcase (org-cite-read-processor-declaration value) + (`(,(and (pred symbolp) name) + ,(pred string-or-null-p) + ,(pred string-or-null-p)) + (unless (org-cite-get-processor name) + (list source "Unknown cite export processor %S" name))) + (_ + (list source "Invalid cite export processor declaration"))) + (error + (list source "Invalid cite export processor declaration"))))))))) + +(defun org-lint-incomplete-citation (ast) + (org-element-map ast 'plain-text + (lambda (text) + (and (string-match-p org-element-citation-prefix-re text) + ;; XXX: The code below signals the error at the beginning + ;; of the paragraph containing the faulty object. It is + ;; not very accurate but may be enough for now. + (list (org-element-property :contents-begin + (org-element-property :parent text)) + "Possibly incomplete citation markup"))))) ;;; Reports UI diff --git a/testing/lisp/test-org-lint.el b/testing/lisp/test-org-lint.el index e57993c0a..decef6e43 100644 --- a/testing/lisp/test-org-lint.el +++ b/testing/lisp/test-org-lint.el @@ -573,6 +573,56 @@ SCHEDULED: <2012-03-29 thu.>" (org-test-with-temp-text "* H :tag::" (org-lint '(spurious-colons))))) +(ert-deftest test-org-lint/non-existent-bibliography () + "Test `org-lint-non-existent-bibliography' checker." + (should + (org-test-with-temp-text "#+bibliography: Idonotexist.bib" + (org-lint '(non-existent-bibliography))))) + +(ert-deftest test-org-lint/missing-print-bibliography () + "Test `org-lint-missing-print-bibliography' checker." + (should + (org-test-with-temp-text "[cite:@foo]" + (org-lint '(missing-print-bibliography)))) + (should-not + (org-test-with-temp-text "[cite:@foo]\n#+print_bibliography:" + (org-lint '(missing-print-bibliography)))) + (should-not + (org-test-with-temp-text "" + (org-lint '(missing-print-bibliography))))) + +(ert-deftest test-org-lint/invalid-cite-export-declaration () + "Test `org-lint-invalid-cite-export-declaration' checker." + (should + (org-test-with-temp-text "#+cite_export: " + (org-lint '(invalid-cite-export-declaration)))) + (should + (org-test-with-temp-text "#+cite_export: 2" + (org-lint '(invalid-cite-export-declaration)))) + (should + (org-test-with-temp-text "#+cite_export: basic bar baz qux" + (org-lint '(invalid-cite-export-declaration)))) + (should + (org-test-with-temp-text "#+cite_export: basic \"bar" + (org-lint '(invalid-cite-export-declaration)))) + (should + (org-test-with-temp-text "#+cite_export: unknown" + (org-lint '(invalid-cite-export-declaration)))) + (should-not + (org-test-with-temp-text "#+cite_export: basic" + (org-lint '(invalid-cite-export-declaration))))) + +(ert-deftest test-org-lint/incomplete-citation () + "Test `org-lint-incomplete-citation' checker." + (should + (org-test-with-temp-text "[cite:foo]" + (org-lint '(incomplete-citation)))) + (should + (org-test-with-temp-text "[cite:@foo" + (org-lint '(incomplete-citation)))) + (should-not + (org-test-with-temp-text "[cite:@foo]" + (org-lint '(incomplete-citation))))) (provide 'test-org-lint) ;;; test-org-lint.el ends here