emacs-orgmode@gnu.org archives
 help / color / mirror / code / Atom feed
* [RFC] Org linting library
@ 2015-04-19 13:31 Nicolas Goaziou
  2015-04-19 14:23 ` Rasmus
                   ` (7 more replies)
  0 siblings, 8 replies; 53+ messages in thread
From: Nicolas Goaziou @ 2015-04-19 13:31 UTC (permalink / raw)
  To: Org Mode List

[-- Attachment #1: Type: text/plain, Size: 2677 bytes --]

Hello,

The following library implements linting for Org syntax. The sole public
function is `org-lint', which see.

Internally, the library defines a new structure: `org-lint-checker',
with the following slots:

  - NAME: Unique check identifier, as a symbol. The check is done
    calling the function `org-lint-NAME' with one mandatory argument,
    the parse tree describing the current Org buffer. Such function
    calls are wrapped within a `save-excursion' and point is always at
    `point-min'. Its return value has to be an alist (POSITION MESSAGE)
    when POSITION refer to the buffer position of the error, as an
    integer, and MESSAGE is a strings describing the error.

  - DESCRIPTION: Summary about the check, as a string.

  - CATEGORIES: Categories relative to the check, as a list of symbol.
    They are used for filtering when calling `org-lint'. Checkers not
    explicitly associated to a category are collected in the `default'
    one.

  - TRUST: The trust level one can have in the check. It is either `low'
    or `high', depending on the heuristics implemented and the nature of
    the check. This has an indicative value only and is displayed along
    reports.

All checks have to be listed in `org-lint--checkers'.

Results are displayed in a special "*Org Lint*" buffer with a dedicated
major mode, derived from `tabulated-list-mode'. In addition to the usual
key-bindings inherited from it, "C-j" displays problematic line reported
under point and "RET" jumps to it.

Checks currently implemented are:

  - duplicates CUSTOM_ID properties
  - duplicate NAME values
  - duplicate targets
  - duplicate footnote definitions
  - orphaned affiliated keywords
  - obsolete affiliated keywords
  - missing language in src blocks
  - NAME values with a colon
  - wrong header arguments in src blocks
  - misuse of CATEGORY keyword
  - "coderef" links with unknown destination
  - "custom-id" links with unknown destination
  - "fuzzy" links with unknown destination
  - "id" links with unknown destination
  - links to non-existent local files
  - special properties in properties drawer
  - obsolete syntax for PROPERTIES drawers
  - missing definition for footnote references
  - missing reference for footnote definitions
  - non-footnote definitions in footnote section
  - probable invalid keywords
  - invalid blocks
  - probable incomplete drawers
  - obsolete QUOTE section

Since it relies on lexical binding, `pcase' and `string-prefix-p', it
cannot be added to Org 8.3, but can make it into Org 8.4, if deemed
useful enough.

Feedback welcome.


Regards,

-- 
Nicolas Goaziou                                                0x80A93738

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Implement-Org-linting.patch --]
[-- Type: text/x-diff, Size: 24656 bytes --]

From 8324f1422953caab5566950f989e95fd4325a0b0 Mon Sep 17 00:00:00 2001
From: Nicolas Goaziou <mail@nicolasgoaziou.fr>
Date: Wed, 15 Apr 2015 22:24:15 +0200
Subject: [PATCH] Implement Org linting

* lisp/org-lint.el: New file.
---
 lisp/org-lint.el | 681 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 681 insertions(+)
 create mode 100644 lisp/org-lint.el

diff --git a/lisp/org-lint.el b/lisp/org-lint.el
new file mode 100644
index 0000000..009a503
--- /dev/null
+++ b/lisp/org-lint.el
@@ -0,0 +1,681 @@
+;;; org-lint.el --- Linting for Org documents        -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2015  Free Software Foundation
+
+;; Author: Nicolas Goaziou <mail@nicolasgoaziou.fr>
+;; Keywords: outlines, hypermedia, calendar, wp
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; This library implements linting for Org syntax.  The sole public
+;; function is `org-lint', which see.
+
+;; Internally, the library defines a new structure:
+;; `org-lint-checker', with the following slots:
+
+;;   - NAME: Unique check identifier, as a symbol.  The check is done
+;;     calling the function `org-lint-NAME' with one mandatory
+;;     argument, the parse tree describing the current Org buffer.
+;;     Such function calls are wrapped within a `save-excursion' and
+;;     point is always at `point-min'.  Its return value has to be an
+;;     alist (POSITION MESSAGE) when POSITION refer to the buffer
+;;     position of the error, as an integer, and MESSAGE is a strings
+;;     describing the error.
+
+;;   - DESCRIPTION: Summary about the check, as a string.
+
+;;   - CATEGORIES: Categories relative to the check, as a list of
+;;     symbol.  They are used for filtering when calling `org-lint'.
+;;     Checkers not explicitly associated to a category are collected
+;;     in the `default' one.
+
+;;   - TRUST: The trust level one can have in the check.  It is either
+;;     `low' or `high', depending on the heuristics implemented and
+;;     the nature of the check.  This has an indicative value only and
+;;     is displayed along reports.
+
+;; All checks have to be listed in `org-lint--checkers'.
+
+;; Results are displayed in a special "*Org Lint*" buffer with
+;; a dedicated major mode, derived from `tabulated-list-mode'.  In
+;; addition to the usual key-bindings inherited from it, "C-j"
+;; displays problematic line reported under point and "RET" jumps to
+;; it.
+
+;; Checks currently implemented are:
+
+;;   - duplicates CUSTOM_ID properties
+;;   - duplicate NAME values
+;;   - duplicate targets
+;;   - duplicate footnote definitions
+;;   - orphaned affiliated keywords
+;;   - obsolete affiliated keywords
+;;   - missing language in src blocks
+;;   - NAME values with a colon
+;;   - wrong header arguments in src blocks
+;;   - misuse of CATEGORY keyword
+;;   - "coderef" links with unknown destination
+;;   - "custom-id" links with unknown destination
+;;   - "fuzzy" links with unknown destination
+;;   - "id" links with unknown destination
+;;   - links to non-existent local files
+;;   - special properties in properties drawer
+;;   - obsolete syntax for PROPERTIES drawers
+;;   - missing definition for footnote references
+;;   - missing reference for footnote definitions
+;;   - non-footnote definitions in footnote section
+;;   - probable invalid keywords
+;;   - invalid blocks
+;;   - probable incomplete drawers
+;;   - obsolete QUOTE section
+
+\f
+;;; Code:
+
+(require 'cl-lib)
+(require 'org-element)
+(require 'ox)
+(require 'ob)
+
+\f
+;;; Checkers
+
+(cl-defstruct (org-lint-checker (:copier nil))
+  (name 'missing-checker-name)
+  (description "")
+  (categories '(default))
+  (trust 'high))			; `low' or `high'
+
+(defun org-lint-missing-checker-name (_)
+  (error
+   "`A checker has no `:name' property.  Please verify `org-lint--checkers'"))
+
+(defconst org-lint--checkers
+  (list
+   (make-org-lint-checker
+    :name 'duplicate-custom-id
+    :description "Report duplicates CUSTOM_ID properties"
+    :categories '(link))
+   (make-org-lint-checker
+    :name 'duplicate-name
+    :description "Report duplicate NAME values"
+    :categories '(babel link))
+   (make-org-lint-checker
+    :name 'duplicate-target
+    :description "Report duplicate targets"
+    :categories '(link))
+   (make-org-lint-checker
+    :name 'duplicate-footnote-definition
+    :description "Report duplicate footnote definitions"
+    :categories '(footnote))
+   (make-org-lint-checker
+    :name 'orphaned-affiliated-keywords
+    :description "Report orphaned affiliated keywords"
+    :trust 'low)
+   (make-org-lint-checker
+    :name 'obsolete-affiliated-keywords
+    :description "Report obsolete affiliated keywords"
+    :categories '(obsolete))
+   (make-org-lint-checker
+    :name 'missing-language-in-src-block
+    :description "Report missing language in src blocks"
+    :categories '(babel))
+   (make-org-lint-checker
+    :name 'colon-in-name
+    :description "Report NAME values with a colon"
+    :categories '(babel))
+   (make-org-lint-checker
+    :name 'wrong-header-argument
+    :description "Report wrong header arguments in src blocks"
+    :categories '(babel))
+   (make-org-lint-checker
+    :name 'deprecated-category-setup
+    :description "Report misuse of CATEGORY keyword"
+    :categories '(obsolete))
+   (make-org-lint-checker
+    :name 'invalid-coderef-link
+    :description "Report \"coderef\" links with unknown destination"
+    :categories '(link))
+   (make-org-lint-checker
+    :name 'invalid-custom-id-link
+    :description "Report \"custom-id\" links with unknown destination"
+    :categories '(link))
+   (make-org-lint-checker
+    :name 'invalid-fuzzy-link
+    :description "Report \"fuzzy\" links with unknown destination"
+    :categories '(link))
+   (make-org-lint-checker
+    :name 'invalid-id-link
+    :description "Report \"id\" links with unknown destination"
+    :categories '(link))
+   (make-org-lint-checker
+    :name 'link-to-local-file
+    :description "Report links to non-existent local files"
+    :categories '(link)
+    :trust 'low)
+   (make-org-lint-checker
+    :name 'special-property-in-properties-drawer
+    :description "Report special properties in properties drawers"
+    :categories '(properties))
+   (make-org-lint-checker
+    :name 'obsolete-properties-drawer
+    :description "Report obsolete syntax for properties drawers"
+    :categories '(obsolete properties)
+    :trust 'low)
+   (make-org-lint-checker
+    :name 'undefined-footnote-reference
+    :description "Report missing definition for footnote references"
+    :categories '(footnote))
+   (make-org-lint-checker
+    :name 'unreferenced-footnote-definition
+    :description "Report missing reference for footnote definitions"
+    :categories '(footnote))
+   (make-org-lint-checker
+    :name 'extraneous-element-in-footnote-section
+    :description "Report non-footnote definitions in footnote section"
+    :trust 'high
+    :categories '(footnote))
+   (make-org-lint-checker
+    :name 'invalid-keyword-syntax
+    :description "Report probable invalid keywords"
+    :trust 'low)
+   (make-org-lint-checker
+    :name 'corrupted-block
+    :description "Report invalid blocks"
+    :trust 'low)
+   (make-org-lint-checker
+    :name 'incomplete-drawer
+    :description "Report probable incomplete drawers"
+    :trust 'low)
+   (make-org-lint-checker
+    :name 'quote-section
+    :description "Report obsolete QUOTE section"
+    :categories '(obsolete)
+    :trust 'low))
+  "List of all available checkers.")
+
+(defun org-lint-duplicate-custom-id (ast)
+  (org-element-map ast '(headline inlinetask)
+    (let (ids)
+      (lambda (e)
+	(let ((custom-id (org-element-property :CUSTOM_ID e)))
+	  (cond
+	   ((not custom-id) nil)
+	   ((member custom-id ids)
+	    (list (org-element-map e 'node-property
+		    (lambda (p)
+		      (and (eq (compare-strings
+				"CUSTOM_ID" nil nil
+				(org-element-property :key p) nil nil
+				t)
+			       t)
+			   (org-element-property :begin p)))
+		    nil t)
+		  (format "Duplicate CUSTOM_ID property \"%s\"" custom-id)))
+	   (t (push custom-id ids) nil)))))))
+
+(defun org-lint-duplicate-name (ast)
+  (org-element-map ast org-element-all-elements
+    (let (names)
+      (lambda (e)
+	(let ((name (org-element-property :name e)))
+	  (cond
+	   ((not name) nil)
+	   ((member name names)
+	    (list (progn (goto-char (org-element-property :begin e))
+			 (let ((case-fold-search t))
+			   (re-search-forward
+			    (concat "^[ \t]*#\\+NAME: +" (regexp-quote name)))
+			   (match-beginning 0)))
+		  (format "Duplicate NAME \"%s\"" name)))
+	   (t (push name names) nil)))))))
+
+(defun org-lint-duplicate-target (ast)
+  (org-element-map ast 'target
+    (let (targets)
+      (lambda (o)
+	(let ((target (org-split-string (org-element-property :value o))))
+	  (if (member target targets)
+	      (list (org-element-property :begin o)
+		    (format "Duplicate target \"%s\""
+			    (org-element-property :value o)))
+	    (push target targets)
+	    nil))))))
+
+(defun org-lint-duplicate-footnote-definition (ast)
+  (org-element-map ast 'footnote-definition
+    (let (labels)
+      (lambda (f)
+	(let ((label (org-element-property :label f)))
+	  (cond ((not label) nil)
+		((member label labels)
+		 (list (org-element-property :post-affiliated f)
+		       (format "Duplicate footnote definition \"%s\"" label)))
+		(t (push label labels) nil)))))))
+
+(defun org-lint-orphaned-affiliated-keywords (ast)
+  (org-element-map ast 'keyword
+    (lambda (k)
+      (let ((key (org-element-property :key k)))
+	(and (member key org-element-affiliated-keywords)
+	     (list (org-element-property :post-affiliated k)
+		   (format "Orphaned affiliated keyword: \"%s\"" key)))))))
+
+(defun org-lint-obsolete-affiliated-keywords (_)
+  (let ((regexp (format "^[ \t]*#\\+%s:"
+			(regexp-opt '("DATA" "LABEL" "RESNAME" "SOURCE"
+				      "SRCNAME" "TBLNAME" "RESULT" "HEADERS")
+				    t)))
+	reports)
+    (while (re-search-forward regexp nil t)
+      (let ((key (upcase (org-match-string-no-properties 1))))
+	(when (< (point)
+		 (org-element-property :post-affiliated (org-element-at-point)))
+	  (push
+	   (list (line-beginning-position)
+		 (format
+		  "Obsolete affiliated keyword: \"%s\".  Use \"%s\" instead"
+		  key
+		  (pcase key
+		    ("HEADERS" "HEADER")
+		    ("RESULT" "RESULTS")
+		    (_ "NAME"))))
+	   reports))))
+    reports))
+
+(defun org-lint-missing-language-in-src-block (ast)
+  (org-element-map ast 'src-block
+    (lambda (b)
+      (unless (org-element-property :language b)
+	(list (org-element-property :post-affiliated b)
+	      "Missing language in source block")))))
+
+(defun org-lint-deprecated-category-setup (ast)
+  (org-element-map ast 'keyword
+    (let (category-flag)
+      (lambda (k)
+	(cond
+	 ((not (string= (org-element-property :key k) "CATEGORY")) nil)
+	 (category-flag
+	  (list (org-element-property :post-affiliated k)
+		"Spurious CATEGORY keyword.  Set :CATEGORY: property instead"))
+	 (t (setf category-flag t) nil))))))
+
+(defun org-lint-invalid-coderef-link (ast)
+  (let ((info (list :parse-tree ast)))
+    (org-element-map ast 'link
+      (lambda (link)
+	(let ((ref (org-element-property :path link)))
+	  (and (equal (org-element-property :type link) "coderef")
+	       (not (ignore-errors (org-export-resolve-coderef ref info)))
+	       (list (org-element-property :begin link)
+		     (format "Unknown coderef \"%s\"" ref))))))))
+
+(defun org-lint-invalid-custom-id-link (ast)
+  (let ((info (list :parse-tree ast)))
+    (org-element-map ast 'link
+      (lambda (link)
+	(and (equal (org-element-property :type link) "custom-id")
+	     (not (ignore-errors (org-export-resolve-id-link link info)))
+	     (list (org-element-property :begin link)
+		   (format "Unknown custom ID \"%s\""
+			   (org-element-property :path link))))))))
+
+(defun org-lint-invalid-fuzzy-link (ast)
+  (let ((info (list :parse-tree ast)))
+    (org-element-map ast 'link
+      (lambda (link)
+	(and (equal (org-element-property :type link) "fuzzy")
+	     (not (ignore-errors (org-export-resolve-fuzzy-link link info)))
+	     (list (org-element-property :begin link)
+		   (format "Unknown fuzzy location \"%s\""
+			   (let ((path (org-element-property :path link)))
+			     (if (string-prefix-p "*" path)
+				 (substring path 1)
+			       path)))))))))
+
+(defun org-lint-invalid-id-link (ast)
+  (org-element-map ast 'link
+    (lambda (link)
+      (let ((id (org-element-property :path link)))
+	(and (equal (org-element-property :type link) "id")
+	     (not (org-id-find id))
+	     (list (org-element-property :begin link)
+		   (format "Unknown ID \"%s\"" id)))))))
+
+(defun org-lint-special-property-in-properties-drawer (ast)
+  (org-element-map ast 'node-property
+    (lambda (p)
+      (let ((key (org-element-property :key p)))
+	(and (member-ignore-case key org-special-properties)
+	     (list (org-element-property :begin p)
+		   (format
+		    "Special property \"%s\" found in a properties drawer"
+		    key)))))))
+
+(defun org-lint-obsolete-properties-drawer (ast)
+  (org-element-map ast 'drawer
+    (lambda (d)
+      (when (equal (org-element-property :drawer-name d) "PROPERTIES")
+	(let ((section (org-element-lineage d '(section))))
+	  (unless (org-element-map section 'property-drawer #'identity nil t)
+	    (list (org-element-property :post-affiliated d)
+		  (if (save-excursion
+			(goto-char (org-element-property :post-affiliated d))
+			(forward-line -1)
+			(or (org-at-heading-p) (org-at-planning-p)))
+		      "Incorrect contents for PROPERTIES drawer"
+		    "Incorrect location for PROPERTIES drawer"))))))))
+
+(defun org-lint-link-to-local-file (ast)
+  (org-element-map ast 'link
+    (lambda (l)
+      (when (equal (org-element-property :type l) "file")
+	(let ((file (org-element-property :path l)))
+	  (and (not (file-remote-p file))
+	       (not (file-exists-p file))
+	       (list (org-element-property :begin l)
+		     (format "Link to non-existent local file \"%s\""
+			     file))))))))
+
+(defun org-lint-undefined-footnote-reference (ast)
+  (let ((definitions (org-element-map ast 'footnote-definition
+		       (lambda (f) (org-element-property :label f)))))
+    (org-element-map ast 'footnote-reference
+      (lambda (f)
+	(let ((label (org-element-property :label f)))
+	  (and label
+	       (not (member label definitions))
+	       (list (org-element-property :begin f)
+		     (format "Missing definition for footnote [%s]"
+			     label))))))))
+
+(defun org-lint-unreferenced-footnote-definition (ast)
+  (let ((references (org-element-map ast 'footnote-reference
+		      (lambda (f) (org-element-property :label f)))))
+    (org-element-map ast 'footnote-definition
+      (lambda (f)
+	(let ((label (org-element-property :label f)))
+	  (and label
+	       (not (member label references))
+	       (list (org-element-property :post-affiliated f)
+		     (format "No reference for footnote definition [%s]"
+			     label))))))))
+
+(defun org-lint-colon-in-name (ast)
+  (org-element-map ast org-element-all-elements
+    (lambda (e)
+      (let ((name (org-element-property :name e)))
+	(and name
+	     (org-string-match-p ":" name)
+	     (list (progn
+		     (goto-char (org-element-property :begin e))
+		     (let ((case-fold-search t))
+		       (re-search-forward
+			(concat "^[ \t]*#\\+NAME: +" (regexp-quote name)))
+		       (match-beginning 0)))
+		   (format
+		    "Name \"%s\" contains a colon; Babel cannot use it as input"
+		    name)))))))
+
+(defun org-lint-incomplete-drawer (_)
+  (let (reports)
+    (while (re-search-forward org-drawer-regexp nil t)
+      (let ((name (org-trim (org-match-string-no-properties 0)))
+	    (element (org-element-at-point)))
+	(pcase (org-element-type element)
+	  (`(drawer property-drawer)
+	   (goto-char (org-element-property :end element))
+	   nil)
+	  (`paragraph
+	   (push (list (line-beginning-position)
+		       (format "Possible incomplete drawer \"%s\"" name))
+		 reports))
+	  (_ nil))))
+    reports))
+
+(defun org-lint-corrupted-block (_)
+  (let ((case-fold-search t)
+	(regexp "^[ \t]*#\\+\\(BEGIN\\|END\\)\\(?::\\|_[^[:space:]]*\\)?[ \t]*")
+	reports)
+    (while (re-search-forward regexp nil t)
+      (let ((name (org-trim (buffer-substring-no-properties
+			     (line-beginning-position) (line-end-position)))))
+	(cond
+	 ((and (string-prefix-p "END" (match-string 1) t)
+	       (not (eolp)))
+	  (push (list (line-beginning-position)
+		      (format "Invalid block closing line \"%s\"" name))
+		reports))
+	 ((eq (org-element-type (org-element-at-point)) 'paragraph)
+	  (push (list (line-beginning-position)
+		      (format "Possible incomplete block \"%s\""
+			      name))
+		reports)))))
+    reports))
+
+(defun org-lint-invalid-keyword-syntax (_)
+  (let ((regexp "^[ \t]*\\(#\\+[^[:space:]:]*\\)\\(?:[[:space:]]\\|$\\)")
+	reports)
+    (while (re-search-forward regexp nil t)
+      (let ((name (org-match-string-no-properties 1)))
+	(unless (or (string-prefix-p "#+BEGIN" name t)
+		    (string-prefix-p "#+END" name t))
+	  (push (list (match-beginning 0)
+		      (format "Possible missing colon in keyword \"%s\"" name))
+		reports))))
+    reports))
+
+(defun org-lint-extraneous-element-in-footnote-section (ast)
+  (org-element-map ast 'headline
+    (lambda (h)
+      (and (org-element-property :footnote-section-p h)
+	   (org-element-map (org-element-contents h)
+	       (org-remove-if
+		(lambda (e)
+		  (memq e '(comment comment-block footnote-definition
+				    property-drawer section)))
+		org-element-all-elements)
+	     (lambda (e)
+	       (not (and (eq (org-element-type e) 'headline)
+			 (org-element-property :commentedp e))))
+	     nil t '(footnote-definition property-drawer))
+	   (list (org-element-property :begin h)
+		 "Extraneous elements in footnote section")))))
+
+(defun org-lint-quote-section (ast)
+  (org-element-map ast '(headline inlinetask)
+    (lambda (h)
+      (let ((title (org-element-property :raw-value h)))
+	(and (or (string-prefix-p "QUOTE " title)
+		 (string-prefix-p (concat org-comment-string " QUOTE ") title))
+	     (list (org-element-property :begin h)
+		   "Deprecated QUOTE section"))))))
+
+(defun org-lint-wrong-header-argument (ast)
+  (let (reports)
+    (org-element-map ast 'src-block
+      (lambda (b)
+	(let* ((language (org-element-property :language b))
+	       (allowed
+		(mapcar #'symbol-name
+			(append
+			 org-babel-header-arg-names
+			 (let ((v (intern (concat "org-babel-header-args:"
+						  language))))
+			   (and (boundp v)
+				(mapcar #'car (symbol-value v))))))))
+	  (dolist (header (mapcar
+			   (lambda (e) (substring (symbol-name (car e)) 1))
+			   (org-babel-parse-header-arguments
+			    (org-element-property :parameters b))))
+	    (unless (member header allowed)
+	      (push (list (org-element-property :post-affiliated b)
+			  (format "Unknown header argument \"%s\"" header))
+		    reports))))))
+    reports))
+
+\f
+;;; Reports UI
+
+(defvar org-lint--report-mode-map
+  (let ((map (make-sparse-keymap)))
+    (set-keymap-parent map tabulated-list-mode-map)
+    (define-key map (kbd "RET") 'org-lint--jump-to-source)
+    (define-key map (kbd "C-j") 'org-lint--show-source)
+    map)
+  "Local keymap for `org-lint--report-mode' buffers.")
+
+(define-derived-mode org-lint--report-mode tabulated-list-mode "OrgLint"
+  "Major mode used to display reports emitted during linting.
+\\<package-menu-mode-map>"
+  (setf tabulated-list-format
+	`[("Line" 6
+	   (lambda (a b)
+	     (< (string-to-number (aref (cadr a) 0))
+		(string-to-number (aref (cadr b) 0))))
+	   :right-align t)
+	  ("Trust" 5 t)
+	  ("Warning" 0 nil)])
+  (tabulated-list-init-header))
+
+(defun org-lint--generate-reports (checkers)
+  "Run all CHECKERS in current buffer.
+Return an alist (ID [LINE TRUST DESCRIPTION]), suitable for
+`tabulated-list-printer'."
+  (save-excursion
+    (goto-char (point-min))
+    (let ((ast (org-element-parse-buffer))
+	  (id 0)
+	  (last-line 1)
+	  (last-pos 1))
+      ;; Insert unique ID for each report.  Replace buffer positions
+      ;; with line numbers.
+      (mapcar
+       (lambda (report)
+	 (list
+	  (incf id)
+	  (apply #'vector
+		 (cons
+		  (progn
+		    (goto-char (car report))
+		    (beginning-of-line)
+		    (prog1 (number-to-string
+			    (incf last-line (count-lines last-pos (point))))
+		      (setf last-pos (point))))
+		  (cdr report)))))
+       ;; Insert trust level in generated reports.  Also sort them by
+       ;; buffer position in order to optimize lines computation.
+       (sort (cl-mapcan
+	      (lambda (c)
+		(let ((trust (symbol-name (org-lint-checker-trust c))))
+		  (mapcar
+		   (lambda (report) (list (car report) trust (nth 1 report)))
+		   (save-excursion
+		     (funcall (intern
+			       (concat "org-lint-"
+				       (symbol-name (org-lint-checker-name c))))
+			      ast)))))
+	      checkers)
+	     #'car-less-than-car)))))
+
+(defvar org-lint--source-buffer nil
+  "Source buffer associated to current report buffer.")
+(make-variable-buffer-local 'org-lint--source-buffer)
+
+(defun org-lint--display-reports (source checkers)
+  "Display linting reports for buffer SOURCE.
+CHECKERS is the list of checkers used."
+  (let ((buffer (get-buffer-create "*Org Lint*")))
+    (with-current-buffer buffer
+      (org-lint--report-mode)
+      (setf org-lint--source-buffer source)
+      (setf tabulated-list-entries
+	    (lambda ()
+	      (with-current-buffer source
+		(org-lint--generate-reports checkers))))
+      (tabulated-list-print))
+    (pop-to-buffer buffer)))
+
+(defun org-lint--jump-to-source ()
+  "Move to source line that generated the report at point."
+  (interactive)
+  (let ((l (string-to-number (aref (tabulated-list-get-entry) 0))))
+    (switch-to-buffer-other-window org-lint--source-buffer)
+    (org-goto-line l)
+    (org-show-set-visibility 'local)
+    (recenter)))
+
+(defun org-lint--show-source ()
+  "Show source line that generated the report at point."
+  (interactive)
+  (let ((buffer (current-buffer))
+	(l (string-to-number (aref (tabulated-list-get-entry) 0))))
+    (switch-to-buffer-other-window org-lint--source-buffer)
+    (org-goto-line l)
+    (org-show-set-visibility 'local)
+    (recenter)
+    (switch-to-buffer-other-window buffer)))
+
+\f
+;;; Public function
+
+;;;###autoload
+(defun org-lint (&optional arg)
+  "Check current Org buffer for syntax mistakes.
+
+By default, run all checkers.  With a single prefix ARG \
+\\[universal-argument],
+select one category of checkers only.  With a double prefix
+\\[universal-argument] \\[universal-argument], select one precise \
+checker by its name.
+
+ARG can also be a list of checker names, as symbols, to run."
+  (interactive "P")
+  (unless (derived-mode-p 'org-mode) (user-error "Not in an Org buffer"))
+  (message "Org linting process starting...")
+  (org-lint--display-reports
+   (current-buffer)
+   (pcase arg
+     (`nil org-lint--checkers)
+     (`(4)
+      (let ((category
+	     (completing-read
+	      "Checker category: "
+	      (mapcar #'org-lint-checker-categories org-lint--checkers)
+	      nil t)))
+	(org-remove-if-not
+	 (lambda (c) (assoc-string (org-lint-checker-categories c) category))
+	 org-lint--checkers)))
+     (`(16)
+      (list
+       (let ((name (completing-read
+		    "Checker name: "
+		    (mapcar #'org-lint-checker-name org-lint--checkers)
+		    nil t)))
+	 (catch 'exit
+	   (dolist (c org-lint--checkers)
+	     (when (string= (org-lint-checker-name c) name)
+	       (throw 'exit c)))))))
+     ((pred consp)
+      (org-remove-if-not (lambda (c) (memq (org-lint-checker-name c) arg))
+			 org-lint--checkers))
+     (_ (user-error "Invalid argument `%S' for `org-lint'" arg))))
+  (message "Org linting process completed"))
+
+
+(provide 'org-lint)
+;;; org-lint.el ends here
-- 
2.3.5


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* Re: [RFC] Org linting library
  2015-04-19 13:31 [RFC] Org linting library Nicolas Goaziou
@ 2015-04-19 14:23 ` Rasmus
  2015-04-19 16:24   ` Nicolas Goaziou
  2015-04-19 19:32 ` Marco Wahl
                   ` (6 subsequent siblings)
  7 siblings, 1 reply; 53+ messages in thread
From: Rasmus @ 2015-04-19 14:23 UTC (permalink / raw)
  To: emacs-orgmode

Hi,

Nicolas Goaziou <mail@nicolasgoaziou.fr> writes:

> Since it relies on lexical binding, `pcase' and `string-prefix-p', it
> cannot be added to Org 8.3, but can make it into Org 8.4, if deemed
> useful enough.

It looks like a great tool.  Especially given the fact that the org format
still seems to be developing.

Anyway, some very premature notes:

One venue that may worth pursuing is better error messages during export,
when links do not exist.  E.g. after a link fails to resolve, an org-lint
buffer is opened to ease tracking down the error.

Perhaps, org-lint should also check that INCLUDE keywords correctly
resolves, e.g. whether #section exits in file.org:

#+INCLUDE: "file.org::#section" 

Perhaps that would induce nasty recursions, though...

Thanks,
Rasmus

-- 
Enough with the bla bla!

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [RFC] Org linting library
  2015-04-19 14:23 ` Rasmus
@ 2015-04-19 16:24   ` Nicolas Goaziou
  0 siblings, 0 replies; 53+ messages in thread
From: Nicolas Goaziou @ 2015-04-19 16:24 UTC (permalink / raw)
  To: Rasmus; +Cc: emacs-orgmode

[-- Attachment #1: Type: text/plain, Size: 1018 bytes --]

Rasmus <rasmus@gmx.us> writes:

> It looks like a great tool.  Especially given the fact that the org format
> still seems to be developing.

Indeed. We can use it as another channel for syntax deprecation.

> Anyway, some very premature notes:
>
> One venue that may worth pursuing is better error messages during export,
> when links do not exist.  E.g. after a link fails to resolve, an org-lint
> buffer is opened to ease tracking down the error.

I think the error message during export is as good as the one provided
by `org-lint', minus the line and the jump.

> Perhaps, org-lint should also check that INCLUDE keywords correctly
> resolves, e.g. whether #section exits in file.org:
>
> #+INCLUDE: "file.org::#section"

Good idea. I implemented it along with a check for SETUPFILE keywords.
Updated file attached.

> Perhaps that would induce nasty recursions, though...

`org-lint' only checks current document, it doesn't enter INCLUDE files,
so there's no danger here.

Thanks for the feedback.


Regards,


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Implement-Org-linting.patch --]
[-- Type: text/x-diff, Size: 27156 bytes --]

From 1b182320599a435de7ab85f64ba984d1e5ef4502 Mon Sep 17 00:00:00 2001
From: Nicolas Goaziou <mail@nicolasgoaziou.fr>
Date: Wed, 15 Apr 2015 22:24:15 +0200
Subject: [PATCH] Implement Org linting

* lisp/org-lint.el: New file.
---
 lisp/org-lint.el | 748 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 748 insertions(+)
 create mode 100644 lisp/org-lint.el

diff --git a/lisp/org-lint.el b/lisp/org-lint.el
new file mode 100644
index 0000000..a514b8b
--- /dev/null
+++ b/lisp/org-lint.el
@@ -0,0 +1,748 @@
+;;; org-lint.el --- Linting for Org documents        -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2015  Free Software Foundation
+
+;; Author: Nicolas Goaziou <mail@nicolasgoaziou.fr>
+;; Keywords: outlines, hypermedia, calendar, wp
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; This library implements linting for Org syntax.  The sole public
+;; function is `org-lint', which see.
+
+;; Internally, the library defines a new structure:
+;; `org-lint-checker', with the following slots:
+
+;;   - NAME: Unique check identifier, as a symbol.  The check is done
+;;     calling the function `org-lint-NAME' with one mandatory
+;;     argument, the parse tree describing the current Org buffer.
+;;     Such function calls are wrapped within a `save-excursion' and
+;;     point is always at `point-min'.  Its return value has to be an
+;;     alist (POSITION MESSAGE) when POSITION refer to the buffer
+;;     position of the error, as an integer, and MESSAGE is a strings
+;;     describing the error.
+
+;;   - DESCRIPTION: Summary about the check, as a string.
+
+;;   - CATEGORIES: Categories relative to the check, as a list of
+;;     symbol.  They are used for filtering when calling `org-lint'.
+;;     Checkers not explicitly associated to a category are collected
+;;     in the `default' one.
+
+;;   - TRUST: The trust level one can have in the check.  It is either
+;;     `low' or `high', depending on the heuristics implemented and
+;;     the nature of the check.  This has an indicative value only and
+;;     is displayed along reports.
+
+;; All checks have to be listed in `org-lint--checkers'.
+
+;; Results are displayed in a special "*Org Lint*" buffer with
+;; a dedicated major mode, derived from `tabulated-list-mode'.  In
+;; addition to the usual key-bindings inherited from it, "C-j"
+;; displays problematic line reported under point and "RET" jumps to
+;; it.
+
+;; Checks currently implemented are:
+
+;;   - duplicates CUSTOM_ID properties
+;;   - duplicate NAME values
+;;   - duplicate targets
+;;   - duplicate footnote definitions
+;;   - orphaned affiliated keywords
+;;   - obsolete affiliated keywords
+;;   - missing language in src blocks
+;;   - NAME values with a colon
+;;   - wrong header arguments in src blocks
+;;   - misuse of CATEGORY keyword
+;;   - "coderef" links with unknown destination
+;;   - "custom-id" links with unknown destination
+;;   - "fuzzy" links with unknown destination
+;;   - "id" links with unknown destination
+;;   - links to non-existent local files
+;;   - SETUPFILE keywords with non-existent file parameter
+;;   - INCLUDE keywords with wrong link parameter
+;;   - special properties in properties drawer
+;;   - obsolete syntax for PROPERTIES drawers
+;;   - missing definition for footnote references
+;;   - missing reference for footnote definitions
+;;   - non-footnote definitions in footnote section
+;;   - probable invalid keywords
+;;   - invalid blocks
+;;   - probable incomplete drawers
+;;   - obsolete QUOTE section
+
+\f
+;;; Code:
+
+(require 'cl-lib)
+(require 'org-element)
+(require 'ox)
+(require 'ob)
+
+\f
+;;; Checkers
+
+(cl-defstruct (org-lint-checker (:copier nil))
+  (name 'missing-checker-name)
+  (description "")
+  (categories '(default))
+  (trust 'high))			; `low' or `high'
+
+(defun org-lint-missing-checker-name (_)
+  (error
+   "`A checker has no `:name' property.  Please verify `org-lint--checkers'"))
+
+(defconst org-lint--checkers
+  (list
+   (make-org-lint-checker
+    :name 'duplicate-custom-id
+    :description "Report duplicates CUSTOM_ID properties"
+    :categories '(link))
+   (make-org-lint-checker
+    :name 'duplicate-name
+    :description "Report duplicate NAME values"
+    :categories '(babel link))
+   (make-org-lint-checker
+    :name 'duplicate-target
+    :description "Report duplicate targets"
+    :categories '(link))
+   (make-org-lint-checker
+    :name 'duplicate-footnote-definition
+    :description "Report duplicate footnote definitions"
+    :categories '(footnote))
+   (make-org-lint-checker
+    :name 'orphaned-affiliated-keywords
+    :description "Report orphaned affiliated keywords"
+    :trust 'low)
+   (make-org-lint-checker
+    :name 'obsolete-affiliated-keywords
+    :description "Report obsolete affiliated keywords"
+    :categories '(obsolete))
+   (make-org-lint-checker
+    :name 'missing-language-in-src-block
+    :description "Report missing language in src blocks"
+    :categories '(babel))
+   (make-org-lint-checker
+    :name 'colon-in-name
+    :description "Report NAME values with a colon"
+    :categories '(babel))
+   (make-org-lint-checker
+    :name 'wrong-header-argument
+    :description "Report wrong header arguments in src blocks"
+    :categories '(babel))
+   (make-org-lint-checker
+    :name 'deprecated-category-setup
+    :description "Report misuse of CATEGORY keyword"
+    :categories '(obsolete))
+   (make-org-lint-checker
+    :name 'invalid-coderef-link
+    :description "Report \"coderef\" links with unknown destination"
+    :categories '(link))
+   (make-org-lint-checker
+    :name 'invalid-custom-id-link
+    :description "Report \"custom-id\" links with unknown destination"
+    :categories '(link))
+   (make-org-lint-checker
+    :name 'invalid-fuzzy-link
+    :description "Report \"fuzzy\" links with unknown destination"
+    :categories '(link))
+   (make-org-lint-checker
+    :name 'invalid-id-link
+    :description "Report \"id\" links with unknown destination"
+    :categories '(link))
+   (make-org-lint-checker
+    :name 'link-to-local-file
+    :description "Report links to non-existent local files"
+    :categories '(link)
+    :trust 'low)
+   (make-org-lint-checker
+    :name 'non-existent-setupfile-parameter
+    :description "Report SETUPFILE keywords with non-existent file parameter"
+    :trust 'low)
+   (make-org-lint-checker
+    :name 'wrong-include-link-parameter
+    :description "Report INCLUDE keywords with misleading link parameter"
+    :trust 'low)
+   (make-org-lint-checker
+    :name 'special-property-in-properties-drawer
+    :description "Report special properties in properties drawers"
+    :categories '(properties))
+   (make-org-lint-checker
+    :name 'obsolete-properties-drawer
+    :description "Report obsolete syntax for properties drawers"
+    :categories '(obsolete properties)
+    :trust 'low)
+   (make-org-lint-checker
+    :name 'undefined-footnote-reference
+    :description "Report missing definition for footnote references"
+    :categories '(footnote))
+   (make-org-lint-checker
+    :name 'unreferenced-footnote-definition
+    :description "Report missing reference for footnote definitions"
+    :categories '(footnote))
+   (make-org-lint-checker
+    :name 'extraneous-element-in-footnote-section
+    :description "Report non-footnote definitions in footnote section"
+    :trust 'high
+    :categories '(footnote))
+   (make-org-lint-checker
+    :name 'invalid-keyword-syntax
+    :description "Report probable invalid keywords"
+    :trust 'low)
+   (make-org-lint-checker
+    :name 'corrupted-block
+    :description "Report invalid blocks"
+    :trust 'low)
+   (make-org-lint-checker
+    :name 'incomplete-drawer
+    :description "Report probable incomplete drawers"
+    :trust 'low)
+   (make-org-lint-checker
+    :name 'quote-section
+    :description "Report obsolete QUOTE section"
+    :categories '(obsolete)
+    :trust 'low))
+  "List of all available checkers.")
+
+(defun org-lint-duplicate-custom-id (ast)
+  (org-element-map ast '(headline inlinetask)
+    (let (ids)
+      (lambda (e)
+	(let ((custom-id (org-element-property :CUSTOM_ID e)))
+	  (cond
+	   ((not custom-id) nil)
+	   ((member custom-id ids)
+	    (list (org-element-map e 'node-property
+		    (lambda (p)
+		      (and (eq (compare-strings
+				"CUSTOM_ID" nil nil
+				(org-element-property :key p) nil nil
+				t)
+			       t)
+			   (org-element-property :begin p)))
+		    nil t)
+		  (format "Duplicate CUSTOM_ID property \"%s\"" custom-id)))
+	   (t (push custom-id ids) nil)))))))
+
+(defun org-lint-duplicate-name (ast)
+  (org-element-map ast org-element-all-elements
+    (let (names)
+      (lambda (e)
+	(let ((name (org-element-property :name e)))
+	  (cond
+	   ((not name) nil)
+	   ((member name names)
+	    (list (progn (goto-char (org-element-property :begin e))
+			 (let ((case-fold-search t))
+			   (re-search-forward
+			    (concat "^[ \t]*#\\+NAME: +" (regexp-quote name)))
+			   (match-beginning 0)))
+		  (format "Duplicate NAME \"%s\"" name)))
+	   (t (push name names) nil)))))))
+
+(defun org-lint-duplicate-target (ast)
+  (org-element-map ast 'target
+    (let (targets)
+      (lambda (o)
+	(let ((target (org-split-string (org-element-property :value o))))
+	  (if (member target targets)
+	      (list (org-element-property :begin o)
+		    (format "Duplicate target \"%s\""
+			    (org-element-property :value o)))
+	    (push target targets)
+	    nil))))))
+
+(defun org-lint-duplicate-footnote-definition (ast)
+  (org-element-map ast 'footnote-definition
+    (let (labels)
+      (lambda (f)
+	(let ((label (org-element-property :label f)))
+	  (cond ((not label) nil)
+		((member label labels)
+		 (list (org-element-property :post-affiliated f)
+		       (format "Duplicate footnote definition \"%s\"" label)))
+		(t (push label labels) nil)))))))
+
+(defun org-lint-orphaned-affiliated-keywords (ast)
+  (org-element-map ast 'keyword
+    (lambda (k)
+      (let ((key (org-element-property :key k)))
+	(and (member key org-element-affiliated-keywords)
+	     (list (org-element-property :post-affiliated k)
+		   (format "Orphaned affiliated keyword: \"%s\"" key)))))))
+
+(defun org-lint-obsolete-affiliated-keywords (_)
+  (let ((regexp (format "^[ \t]*#\\+%s:"
+			(regexp-opt '("DATA" "LABEL" "RESNAME" "SOURCE"
+				      "SRCNAME" "TBLNAME" "RESULT" "HEADERS")
+				    t)))
+	reports)
+    (while (re-search-forward regexp nil t)
+      (let ((key (upcase (org-match-string-no-properties 1))))
+	(when (< (point)
+		 (org-element-property :post-affiliated (org-element-at-point)))
+	  (push
+	   (list (line-beginning-position)
+		 (format
+		  "Obsolete affiliated keyword: \"%s\".  Use \"%s\" instead"
+		  key
+		  (pcase key
+		    ("HEADERS" "HEADER")
+		    ("RESULT" "RESULTS")
+		    (_ "NAME"))))
+	   reports))))
+    reports))
+
+(defun org-lint-missing-language-in-src-block (ast)
+  (org-element-map ast 'src-block
+    (lambda (b)
+      (unless (org-element-property :language b)
+	(list (org-element-property :post-affiliated b)
+	      "Missing language in source block")))))
+
+(defun org-lint-deprecated-category-setup (ast)
+  (org-element-map ast 'keyword
+    (let (category-flag)
+      (lambda (k)
+	(cond
+	 ((not (string= (org-element-property :key k) "CATEGORY")) nil)
+	 (category-flag
+	  (list (org-element-property :post-affiliated k)
+		"Spurious CATEGORY keyword.  Set :CATEGORY: property instead"))
+	 (t (setf category-flag t) nil))))))
+
+(defun org-lint-invalid-coderef-link (ast)
+  (let ((info (list :parse-tree ast)))
+    (org-element-map ast 'link
+      (lambda (link)
+	(let ((ref (org-element-property :path link)))
+	  (and (equal (org-element-property :type link) "coderef")
+	       (not (ignore-errors (org-export-resolve-coderef ref info)))
+	       (list (org-element-property :begin link)
+		     (format "Unknown coderef \"%s\"" ref))))))))
+
+(defun org-lint-invalid-custom-id-link (ast)
+  (let ((info (list :parse-tree ast)))
+    (org-element-map ast 'link
+      (lambda (link)
+	(and (equal (org-element-property :type link) "custom-id")
+	     (not (ignore-errors (org-export-resolve-id-link link info)))
+	     (list (org-element-property :begin link)
+		   (format "Unknown custom ID \"%s\""
+			   (org-element-property :path link))))))))
+
+(defun org-lint-invalid-fuzzy-link (ast)
+  (let ((info (list :parse-tree ast)))
+    (org-element-map ast 'link
+      (lambda (link)
+	(and (equal (org-element-property :type link) "fuzzy")
+	     (not (ignore-errors (org-export-resolve-fuzzy-link link info)))
+	     (list (org-element-property :begin link)
+		   (format "Unknown fuzzy location \"%s\""
+			   (let ((path (org-element-property :path link)))
+			     (if (string-prefix-p "*" path)
+				 (substring path 1)
+			       path)))))))))
+
+(defun org-lint-invalid-id-link (ast)
+  (org-element-map ast 'link
+    (lambda (link)
+      (let ((id (org-element-property :path link)))
+	(and (equal (org-element-property :type link) "id")
+	     (not (org-id-find id))
+	     (list (org-element-property :begin link)
+		   (format "Unknown ID \"%s\"" id)))))))
+
+(defun org-lint-special-property-in-properties-drawer (ast)
+  (org-element-map ast 'node-property
+    (lambda (p)
+      (let ((key (org-element-property :key p)))
+	(and (member-ignore-case key org-special-properties)
+	     (list (org-element-property :begin p)
+		   (format
+		    "Special property \"%s\" found in a properties drawer"
+		    key)))))))
+
+(defun org-lint-obsolete-properties-drawer (ast)
+  (org-element-map ast 'drawer
+    (lambda (d)
+      (when (equal (org-element-property :drawer-name d) "PROPERTIES")
+	(let ((section (org-element-lineage d '(section))))
+	  (unless (org-element-map section 'property-drawer #'identity nil t)
+	    (list (org-element-property :post-affiliated d)
+		  (if (save-excursion
+			(goto-char (org-element-property :post-affiliated d))
+			(forward-line -1)
+			(or (org-at-heading-p) (org-at-planning-p)))
+		      "Incorrect contents for PROPERTIES drawer"
+		    "Incorrect location for PROPERTIES drawer"))))))))
+
+(defun org-lint-link-to-local-file (ast)
+  (org-element-map ast 'link
+    (lambda (l)
+      (when (equal (org-element-property :type l) "file")
+	(let ((file (org-element-property :path l)))
+	  (and (not (file-remote-p file))
+	       (not (file-exists-p file))
+	       (list (org-element-property :begin l)
+		     (format "Link to non-existent local file \"%s\""
+			     file))))))))
+
+(defun org-lint-non-existent-setupfile-parameter (ast)
+  (org-element-map ast 'keyword
+    (lambda (k)
+      (when (equal (org-element-property :key k) "SETUPFILE")
+	(let ((file (org-remove-double-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 setup file \"%s\"" file))))))))
+
+(defun org-lint-wrong-include-link-parameter (ast)
+  (let (reports)
+    (org-element-map ast 'keyword
+      (lambda (k)
+	(when (equal (org-element-property :key k) "INCLUDE")
+	  (let* ((value (org-element-property :value k))
+		 (path
+		  (and (string-match "^\\(\".+\"\\|\\S-+\\)[ \t]*" value)
+		       (save-match-data
+			 (org-remove-double-quotes (match-string 1 value))))))
+	    (if (not path)
+		(push (list (org-element-property :post-affiliated k)
+			    "Missing location argument in INCLUDE keyword")
+		      reports)
+	      (let* ((file (org-string-nw-p
+			    (if (string-match "::\\(.*\\)\\'" path)
+				(substring path 0 (match-beginning 0))
+			      path)))
+		     (search (and (not (equal file path))
+				  (org-string-nw-p (match-string 1 path)))))
+		(if (and file
+			 (not (file-remote-p file))
+			 (not (file-exists-p file)))
+		    (push (list (org-element-property :post-affiliated k)
+				"Non-existent file argument in INCLUDE keyword")
+			  reports)
+		  (let* ((visiting (if file (find-buffer-visiting file)
+				     (current-buffer)))
+			 (buffer (or visiting (find-file-noselect file))))
+		    (unwind-protect
+			(with-current-buffer buffer
+			  (when (and search
+				     (not
+				      (ignore-errors
+					(let ((org-link-search-inhibit-query t))
+					  (org-link-search search nil nil t)))))
+			    (push
+			     (list
+			      (org-element-property :post-affiliated k)
+			      (format
+			       "Invalid search part \"%s\" in INCLUDE keyword"
+			       search))
+			     reports)))
+		      (unless visiting (kill-buffer buffer)))))))))))
+    reports))
+
+(defun org-lint-undefined-footnote-reference (ast)
+  (let ((definitions (org-element-map ast 'footnote-definition
+		       (lambda (f) (org-element-property :label f)))))
+    (org-element-map ast 'footnote-reference
+      (lambda (f)
+	(let ((label (org-element-property :label f)))
+	  (and label
+	       (not (member label definitions))
+	       (list (org-element-property :begin f)
+		     (format "Missing definition for footnote [%s]"
+			     label))))))))
+
+(defun org-lint-unreferenced-footnote-definition (ast)
+  (let ((references (org-element-map ast 'footnote-reference
+		      (lambda (f) (org-element-property :label f)))))
+    (org-element-map ast 'footnote-definition
+      (lambda (f)
+	(let ((label (org-element-property :label f)))
+	  (and label
+	       (not (member label references))
+	       (list (org-element-property :post-affiliated f)
+		     (format "No reference for footnote definition [%s]"
+			     label))))))))
+
+(defun org-lint-colon-in-name (ast)
+  (org-element-map ast org-element-all-elements
+    (lambda (e)
+      (let ((name (org-element-property :name e)))
+	(and name
+	     (org-string-match-p ":" name)
+	     (list (progn
+		     (goto-char (org-element-property :begin e))
+		     (let ((case-fold-search t))
+		       (re-search-forward
+			(concat "^[ \t]*#\\+NAME: +" (regexp-quote name)))
+		       (match-beginning 0)))
+		   (format
+		    "Name \"%s\" contains a colon; Babel cannot use it as input"
+		    name)))))))
+
+(defun org-lint-incomplete-drawer (_)
+  (let (reports)
+    (while (re-search-forward org-drawer-regexp nil t)
+      (let ((name (org-trim (org-match-string-no-properties 0)))
+	    (element (org-element-at-point)))
+	(pcase (org-element-type element)
+	  (`(drawer property-drawer)
+	   (goto-char (org-element-property :end element))
+	   nil)
+	  (`paragraph
+	   (push (list (line-beginning-position)
+		       (format "Possible incomplete drawer \"%s\"" name))
+		 reports))
+	  (_ nil))))
+    reports))
+
+(defun org-lint-corrupted-block (_)
+  (let ((case-fold-search t)
+	(regexp "^[ \t]*#\\+\\(BEGIN\\|END\\)\\(?::\\|_[^[:space:]]*\\)?[ \t]*")
+	reports)
+    (while (re-search-forward regexp nil t)
+      (let ((name (org-trim (buffer-substring-no-properties
+			     (line-beginning-position) (line-end-position)))))
+	(cond
+	 ((and (string-prefix-p "END" (match-string 1) t)
+	       (not (eolp)))
+	  (push (list (line-beginning-position)
+		      (format "Invalid block closing line \"%s\"" name))
+		reports))
+	 ((eq (org-element-type (org-element-at-point)) 'paragraph)
+	  (push (list (line-beginning-position)
+		      (format "Possible incomplete block \"%s\""
+			      name))
+		reports)))))
+    reports))
+
+(defun org-lint-invalid-keyword-syntax (_)
+  (let ((regexp "^[ \t]*\\(#\\+[^[:space:]:]*\\)\\(?:[[:space:]]\\|$\\)")
+	reports)
+    (while (re-search-forward regexp nil t)
+      (let ((name (org-match-string-no-properties 1)))
+	(unless (or (string-prefix-p "#+BEGIN" name t)
+		    (string-prefix-p "#+END" name t))
+	  (push (list (match-beginning 0)
+		      (format "Possible missing colon in keyword \"%s\"" name))
+		reports))))
+    reports))
+
+(defun org-lint-extraneous-element-in-footnote-section (ast)
+  (org-element-map ast 'headline
+    (lambda (h)
+      (and (org-element-property :footnote-section-p h)
+	   (org-element-map (org-element-contents h)
+	       (org-remove-if
+		(lambda (e)
+		  (memq e '(comment comment-block footnote-definition
+				    property-drawer section)))
+		org-element-all-elements)
+	     (lambda (e)
+	       (not (and (eq (org-element-type e) 'headline)
+			 (org-element-property :commentedp e))))
+	     nil t '(footnote-definition property-drawer))
+	   (list (org-element-property :begin h)
+		 "Extraneous elements in footnote section")))))
+
+(defun org-lint-quote-section (ast)
+  (org-element-map ast '(headline inlinetask)
+    (lambda (h)
+      (let ((title (org-element-property :raw-value h)))
+	(and (or (string-prefix-p "QUOTE " title)
+		 (string-prefix-p (concat org-comment-string " QUOTE ") title))
+	     (list (org-element-property :begin h)
+		   "Deprecated QUOTE section"))))))
+
+(defun org-lint-wrong-header-argument (ast)
+  (let (reports)
+    (org-element-map ast 'src-block
+      (lambda (b)
+	(let* ((language (org-element-property :language b))
+	       (allowed
+		(mapcar #'symbol-name
+			(append
+			 org-babel-header-arg-names
+			 (let ((v (intern (concat "org-babel-header-args:"
+						  language))))
+			   (and (boundp v)
+				(mapcar #'car (symbol-value v))))))))
+	  (dolist (header (mapcar
+			   (lambda (e) (substring (symbol-name (car e)) 1))
+			   (org-babel-parse-header-arguments
+			    (org-element-property :parameters b))))
+	    (unless (member header allowed)
+	      (push (list (org-element-property :post-affiliated b)
+			  (format "Unknown header argument \"%s\"" header))
+		    reports))))))
+    reports))
+
+\f
+;;; Reports UI
+
+(defvar org-lint--report-mode-map
+  (let ((map (make-sparse-keymap)))
+    (set-keymap-parent map tabulated-list-mode-map)
+    (define-key map (kbd "RET") 'org-lint--jump-to-source)
+    (define-key map (kbd "C-j") 'org-lint--show-source)
+    map)
+  "Local keymap for `org-lint--report-mode' buffers.")
+
+(define-derived-mode org-lint--report-mode tabulated-list-mode "OrgLint"
+  "Major mode used to display reports emitted during linting.
+\\<package-menu-mode-map>"
+  (setf tabulated-list-format
+	`[("Line" 6
+	   (lambda (a b)
+	     (< (string-to-number (aref (cadr a) 0))
+		(string-to-number (aref (cadr b) 0))))
+	   :right-align t)
+	  ("Trust" 5 t)
+	  ("Warning" 0 nil)])
+  (tabulated-list-init-header))
+
+(defun org-lint--generate-reports (checkers)
+  "Run all CHECKERS in current buffer.
+Return an alist (ID [LINE TRUST DESCRIPTION]), suitable for
+`tabulated-list-printer'."
+  (save-excursion
+    (goto-char (point-min))
+    (let ((ast (org-element-parse-buffer))
+	  (id 0)
+	  (last-line 1)
+	  (last-pos 1))
+      ;; Insert unique ID for each report.  Replace buffer positions
+      ;; with line numbers.
+      (mapcar
+       (lambda (report)
+	 (list
+	  (incf id)
+	  (apply #'vector
+		 (cons
+		  (progn
+		    (goto-char (car report))
+		    (beginning-of-line)
+		    (prog1 (number-to-string
+			    (incf last-line (count-lines last-pos (point))))
+		      (setf last-pos (point))))
+		  (cdr report)))))
+       ;; Insert trust level in generated reports.  Also sort them by
+       ;; buffer position in order to optimize lines computation.
+       (sort (cl-mapcan
+	      (lambda (c)
+		(let ((trust (symbol-name (org-lint-checker-trust c))))
+		  (mapcar
+		   (lambda (report) (list (car report) trust (nth 1 report)))
+		   (save-excursion
+		     (funcall (intern
+			       (concat "org-lint-"
+				       (symbol-name (org-lint-checker-name c))))
+			      ast)))))
+	      checkers)
+	     #'car-less-than-car)))))
+
+(defvar org-lint--source-buffer nil
+  "Source buffer associated to current report buffer.")
+(make-variable-buffer-local 'org-lint--source-buffer)
+
+(defun org-lint--display-reports (source checkers)
+  "Display linting reports for buffer SOURCE.
+CHECKERS is the list of checkers used."
+  (let ((buffer (get-buffer-create "*Org Lint*")))
+    (with-current-buffer buffer
+      (org-lint--report-mode)
+      (setf org-lint--source-buffer source)
+      (setf tabulated-list-entries
+	    (lambda ()
+	      (with-current-buffer source
+		(org-lint--generate-reports checkers))))
+      (tabulated-list-print))
+    (pop-to-buffer buffer)))
+
+(defun org-lint--jump-to-source ()
+  "Move to source line that generated the report at point."
+  (interactive)
+  (let ((l (string-to-number (aref (tabulated-list-get-entry) 0))))
+    (switch-to-buffer-other-window org-lint--source-buffer)
+    (org-goto-line l)
+    (org-show-set-visibility 'local)
+    (recenter)))
+
+(defun org-lint--show-source ()
+  "Show source line that generated the report at point."
+  (interactive)
+  (let ((buffer (current-buffer))
+	(l (string-to-number (aref (tabulated-list-get-entry) 0))))
+    (switch-to-buffer-other-window org-lint--source-buffer)
+    (org-goto-line l)
+    (org-show-set-visibility 'local)
+    (recenter)
+    (switch-to-buffer-other-window buffer)))
+
+\f
+;;; Public function
+
+;;;###autoload
+(defun org-lint (&optional arg)
+  "Check current Org buffer for syntax mistakes.
+
+By default, run all checkers.  With a single prefix ARG \
+\\[universal-argument],
+select one category of checkers only.  With a double prefix
+\\[universal-argument] \\[universal-argument], select one precise \
+checker by its name.
+
+ARG can also be a list of checker names, as symbols, to run."
+  (interactive "P")
+  (unless (derived-mode-p 'org-mode) (user-error "Not in an Org buffer"))
+  (message "Org linting process starting...")
+  (org-lint--display-reports
+   (current-buffer)
+   (pcase arg
+     (`nil org-lint--checkers)
+     (`(4)
+      (let ((category
+	     (completing-read
+	      "Checker category: "
+	      (mapcar #'org-lint-checker-categories org-lint--checkers)
+	      nil t)))
+	(org-remove-if-not
+	 (lambda (c) (assoc-string (org-lint-checker-categories c) category))
+	 org-lint--checkers)))
+     (`(16)
+      (list
+       (let ((name (completing-read
+		    "Checker name: "
+		    (mapcar #'org-lint-checker-name org-lint--checkers)
+		    nil t)))
+	 (catch 'exit
+	   (dolist (c org-lint--checkers)
+	     (when (string= (org-lint-checker-name c) name)
+	       (throw 'exit c)))))))
+     ((pred consp)
+      (org-remove-if-not (lambda (c) (memq (org-lint-checker-name c) arg))
+			 org-lint--checkers))
+     (_ (user-error "Invalid argument `%S' for `org-lint'" arg))))
+  (message "Org linting process completed"))
+
+
+(provide 'org-lint)
+;;; org-lint.el ends here
-- 
2.3.5


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* Re: [RFC] Org linting library
  2015-04-19 13:31 [RFC] Org linting library Nicolas Goaziou
  2015-04-19 14:23 ` Rasmus
@ 2015-04-19 19:32 ` Marco Wahl
  2015-04-20 10:42   ` Nicolas Goaziou
  2015-04-20  2:15 ` Charles C. Berry
                   ` (5 subsequent siblings)
  7 siblings, 1 reply; 53+ messages in thread
From: Marco Wahl @ 2015-04-19 19:32 UTC (permalink / raw)
  To: emacs-orgmode

Hi!

Nicolas Goaziou <mail@nicolasgoaziou.fr> writes:

> The following library implements linting for Org syntax. The sole public
> function is `org-lint', which see.

Great, thanks a lot.  Already found some issues with some org-files.

> Results are displayed in a special "*Org Lint*" buffer with a dedicated
> major mode, derived from `tabulated-list-mode'. In addition to the usual
> key-bindings inherited from it, "C-j" displays problematic line reported
> under point and "RET" jumps to it.

Cool.

> Feedback welcome.

I think it's too much that org-lint tries to follow ssh links.  This
slowed down the org-linting considerably in my case.

#v+
2278 low   Link to non-existent local file "///ssh:root@127.0.0.1#2222:/etc/httpd/conf/httpd.conf"
#v-


My 2ct,  Marco
-- 
http://www.wahlzone.de
GPG: 0x49010A040A3AE6F2

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [RFC] Org linting library
  2015-04-19 13:31 [RFC] Org linting library Nicolas Goaziou
  2015-04-19 14:23 ` Rasmus
  2015-04-19 19:32 ` Marco Wahl
@ 2015-04-20  2:15 ` Charles C. Berry
  2015-04-20  9:12   ` Nicolas Goaziou
  2015-04-22 19:27 ` Nicolas Goaziou
                   ` (4 subsequent siblings)
  7 siblings, 1 reply; 53+ messages in thread
From: Charles C. Berry @ 2015-04-20  2:15 UTC (permalink / raw)
  To: Nicolas Goaziou; +Cc: Org Mode List

On Sun, 19 Apr 2015, Nicolas Goaziou wrote:

> Hello,
>
> The following library implements linting for Org syntax. The sole public
> function is `org-lint', which see.
>

Nice!

Comments below.

> Internally, the library defines a new structure: `org-lint-checker',
> with the following slots:
>

[snip]

>
> Checks currently implemented are:
>
>  - duplicates CUSTOM_ID properties
>  - duplicate NAME values
>  - duplicate targets
>  - duplicate footnote definitions
>  - orphaned affiliated keywords

This generates an orphaned keyword warning, but seems innocuous and is 
what babel renders.

#+NAME: abc
#+BEGIN_SRC emacs-lisp
nil
#+END_SRC

#+RESULTS: abc


>  - obsolete affiliated keywords
>  - missing language in src blocks
>  - NAME values with a colon
>  - wrong header arguments in src blocks

IFF the header args are in the #+BEGIN_SRC line.

#+HEADER lines and header-arg properties are not screened AFAICS.

[snip]

Chuck

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [RFC] Org linting library
  2015-04-20  2:15 ` Charles C. Berry
@ 2015-04-20  9:12   ` Nicolas Goaziou
  0 siblings, 0 replies; 53+ messages in thread
From: Nicolas Goaziou @ 2015-04-20  9:12 UTC (permalink / raw)
  To: Charles C. Berry; +Cc: Org Mode List

[-- Attachment #1: Type: text/plain, Size: 672 bytes --]

"Charles C. Berry" <ccberry@ucsd.edu> writes:

>>  - orphaned affiliated keywords
>
> This generates an orphaned keyword warning, but seems innocuous and is 
> what babel renders.
>
> #+NAME: abc
> #+BEGIN_SRC emacs-lisp
> nil #+END_SRC
>
> #+RESULTS: abc

Indeed, it is innocuous. It is meant to catch, e.g.,

  #+CAPTION: my table

  | table with a spurious newline above |

This check has a "low" trust level so false positive are OK.

>>  - wrong header arguments in src blocks
>
> IFF the header args are in the #+BEGIN_SRC line.
>
> #+HEADER lines and header-arg properties are not screened AFAICS.

Indeed. Here's an update.

Thank you for the feedback.


Regards,

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Implement-Org-linting.patch --]
[-- Type: text/x-diff, Size: 27973 bytes --]

From 1b6b392f6e8e1bba68e07a36e7f9afc792e699a2 Mon Sep 17 00:00:00 2001
From: Nicolas Goaziou <mail@nicolasgoaziou.fr>
Date: Wed, 15 Apr 2015 22:24:15 +0200
Subject: [PATCH] Implement Org linting

* lisp/org-lint.el: New file.
---
 lisp/org-lint.el | 768 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 768 insertions(+)
 create mode 100644 lisp/org-lint.el

diff --git a/lisp/org-lint.el b/lisp/org-lint.el
new file mode 100644
index 0000000..c818ff1
--- /dev/null
+++ b/lisp/org-lint.el
@@ -0,0 +1,768 @@
+;;; org-lint.el --- Linting for Org documents        -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2015  Free Software Foundation
+
+;; Author: Nicolas Goaziou <mail@nicolasgoaziou.fr>
+;; Keywords: outlines, hypermedia, calendar, wp
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; This library implements linting for Org syntax.  The sole public
+;; function is `org-lint', which see.
+
+;; Internally, the library defines a new structure:
+;; `org-lint-checker', with the following slots:
+
+;;   - NAME: Unique check identifier, as a symbol.  The check is done
+;;     calling the function `org-lint-NAME' with one mandatory
+;;     argument, the parse tree describing the current Org buffer.
+;;     Such function calls are wrapped within a `save-excursion' and
+;;     point is always at `point-min'.  Its return value has to be an
+;;     alist (POSITION MESSAGE) when POSITION refer to the buffer
+;;     position of the error, as an integer, and MESSAGE is a strings
+;;     describing the error.
+
+;;   - DESCRIPTION: Summary about the check, as a string.
+
+;;   - CATEGORIES: Categories relative to the check, as a list of
+;;     symbol.  They are used for filtering when calling `org-lint'.
+;;     Checkers not explicitly associated to a category are collected
+;;     in the `default' one.
+
+;;   - TRUST: The trust level one can have in the check.  It is either
+;;     `low' or `high', depending on the heuristics implemented and
+;;     the nature of the check.  This has an indicative value only and
+;;     is displayed along reports.
+
+;; All checks have to be listed in `org-lint--checkers'.
+
+;; Results are displayed in a special "*Org Lint*" buffer with
+;; a dedicated major mode, derived from `tabulated-list-mode'.  In
+;; addition to the usual key-bindings inherited from it, "C-j"
+;; displays problematic line reported under point and "RET" jumps to
+;; it.
+
+;; Checks currently implemented are:
+
+;;   - duplicates CUSTOM_ID properties
+;;   - duplicate NAME values
+;;   - duplicate targets
+;;   - duplicate footnote definitions
+;;   - orphaned affiliated keywords
+;;   - obsolete affiliated keywords
+;;   - missing language in src blocks
+;;   - NAME values with a colon
+;;   - wrong header arguments in src blocks
+;;   - misuse of CATEGORY keyword
+;;   - "coderef" links with unknown destination
+;;   - "custom-id" links with unknown destination
+;;   - "fuzzy" links with unknown destination
+;;   - "id" links with unknown destination
+;;   - links to non-existent local files
+;;   - SETUPFILE keywords with non-existent file parameter
+;;   - INCLUDE keywords with wrong link parameter
+;;   - special properties in properties drawer
+;;   - obsolete syntax for PROPERTIES drawers
+;;   - missing definition for footnote references
+;;   - missing reference for footnote definitions
+;;   - non-footnote definitions in footnote section
+;;   - probable invalid keywords
+;;   - invalid blocks
+;;   - probable incomplete drawers
+;;   - obsolete QUOTE section
+
+\f
+;;; Code:
+
+(require 'cl-lib)
+(require 'org-element)
+(require 'ox)
+(require 'ob)
+
+\f
+;;; Checkers
+
+(cl-defstruct (org-lint-checker (:copier nil))
+  (name 'missing-checker-name)
+  (description "")
+  (categories '(default))
+  (trust 'high))			; `low' or `high'
+
+(defun org-lint-missing-checker-name (_)
+  (error
+   "`A checker has no `:name' property.  Please verify `org-lint--checkers'"))
+
+(defconst org-lint--checkers
+  (list
+   (make-org-lint-checker
+    :name 'duplicate-custom-id
+    :description "Report duplicates CUSTOM_ID properties"
+    :categories '(link))
+   (make-org-lint-checker
+    :name 'duplicate-name
+    :description "Report duplicate NAME values"
+    :categories '(babel link))
+   (make-org-lint-checker
+    :name 'duplicate-target
+    :description "Report duplicate targets"
+    :categories '(link))
+   (make-org-lint-checker
+    :name 'duplicate-footnote-definition
+    :description "Report duplicate footnote definitions"
+    :categories '(footnote))
+   (make-org-lint-checker
+    :name 'orphaned-affiliated-keywords
+    :description "Report orphaned affiliated keywords"
+    :trust 'low)
+   (make-org-lint-checker
+    :name 'obsolete-affiliated-keywords
+    :description "Report obsolete affiliated keywords"
+    :categories '(obsolete))
+   (make-org-lint-checker
+    :name 'missing-language-in-src-block
+    :description "Report missing language in src blocks"
+    :categories '(babel))
+   (make-org-lint-checker
+    :name 'colon-in-name
+    :description "Report NAME values with a colon"
+    :categories '(babel))
+   (make-org-lint-checker
+    :name 'wrong-header-argument
+    :description "Report wrong headers in src blocks, keywords and properties"
+    :categories '(babel))
+   (make-org-lint-checker
+    :name 'deprecated-category-setup
+    :description "Report misuse of CATEGORY keyword"
+    :categories '(obsolete))
+   (make-org-lint-checker
+    :name 'invalid-coderef-link
+    :description "Report \"coderef\" links with unknown destination"
+    :categories '(link))
+   (make-org-lint-checker
+    :name 'invalid-custom-id-link
+    :description "Report \"custom-id\" links with unknown destination"
+    :categories '(link))
+   (make-org-lint-checker
+    :name 'invalid-fuzzy-link
+    :description "Report \"fuzzy\" links with unknown destination"
+    :categories '(link))
+   (make-org-lint-checker
+    :name 'invalid-id-link
+    :description "Report \"id\" links with unknown destination"
+    :categories '(link))
+   (make-org-lint-checker
+    :name 'link-to-local-file
+    :description "Report links to non-existent local files"
+    :categories '(link)
+    :trust 'low)
+   (make-org-lint-checker
+    :name 'non-existent-setupfile-parameter
+    :description "Report SETUPFILE keywords with non-existent file parameter"
+    :trust 'low)
+   (make-org-lint-checker
+    :name 'wrong-include-link-parameter
+    :description "Report INCLUDE keywords with misleading link parameter"
+    :trust 'low)
+   (make-org-lint-checker
+    :name 'special-property-in-properties-drawer
+    :description "Report special properties in properties drawers"
+    :categories '(properties))
+   (make-org-lint-checker
+    :name 'obsolete-properties-drawer
+    :description "Report obsolete syntax for properties drawers"
+    :categories '(obsolete properties)
+    :trust 'low)
+   (make-org-lint-checker
+    :name 'undefined-footnote-reference
+    :description "Report missing definition for footnote references"
+    :categories '(footnote))
+   (make-org-lint-checker
+    :name 'unreferenced-footnote-definition
+    :description "Report missing reference for footnote definitions"
+    :categories '(footnote))
+   (make-org-lint-checker
+    :name 'extraneous-element-in-footnote-section
+    :description "Report non-footnote definitions in footnote section"
+    :trust 'high
+    :categories '(footnote))
+   (make-org-lint-checker
+    :name 'invalid-keyword-syntax
+    :description "Report probable invalid keywords"
+    :trust 'low)
+   (make-org-lint-checker
+    :name 'corrupted-block
+    :description "Report invalid blocks"
+    :trust 'low)
+   (make-org-lint-checker
+    :name 'incomplete-drawer
+    :description "Report probable incomplete drawers"
+    :trust 'low)
+   (make-org-lint-checker
+    :name 'quote-section
+    :description "Report obsolete QUOTE section"
+    :categories '(obsolete)
+    :trust 'low))
+  "List of all available checkers.")
+
+(defun org-lint-duplicate-custom-id (ast)
+  (org-element-map ast '(headline inlinetask)
+    (let (ids)
+      (lambda (e)
+	(let ((custom-id (org-element-property :CUSTOM_ID e)))
+	  (cond
+	   ((not custom-id) nil)
+	   ((member custom-id ids)
+	    (list (org-element-map e 'node-property
+		    (lambda (p)
+		      (and (eq (compare-strings
+				"CUSTOM_ID" nil nil
+				(org-element-property :key p) nil nil
+				t)
+			       t)
+			   (org-element-property :begin p)))
+		    nil t)
+		  (format "Duplicate CUSTOM_ID property \"%s\"" custom-id)))
+	   (t (push custom-id ids) nil)))))))
+
+(defun org-lint-duplicate-name (ast)
+  (org-element-map ast org-element-all-elements
+    (let (names)
+      (lambda (e)
+	(let ((name (org-element-property :name e)))
+	  (cond
+	   ((not name) nil)
+	   ((member name names)
+	    (list (progn (goto-char (org-element-property :begin e))
+			 (let ((case-fold-search t))
+			   (re-search-forward
+			    (concat "^[ \t]*#\\+NAME: +" (regexp-quote name)))
+			   (match-beginning 0)))
+		  (format "Duplicate NAME \"%s\"" name)))
+	   (t (push name names) nil)))))))
+
+(defun org-lint-duplicate-target (ast)
+  (org-element-map ast 'target
+    (let (targets)
+      (lambda (o)
+	(let ((target (org-split-string (org-element-property :value o))))
+	  (if (member target targets)
+	      (list (org-element-property :begin o)
+		    (format "Duplicate target \"%s\""
+			    (org-element-property :value o)))
+	    (push target targets)
+	    nil))))))
+
+(defun org-lint-duplicate-footnote-definition (ast)
+  (org-element-map ast 'footnote-definition
+    (let (labels)
+      (lambda (f)
+	(let ((label (org-element-property :label f)))
+	  (cond ((not label) nil)
+		((member label labels)
+		 (list (org-element-property :post-affiliated f)
+		       (format "Duplicate footnote definition \"%s\"" label)))
+		(t (push label labels) nil)))))))
+
+(defun org-lint-orphaned-affiliated-keywords (ast)
+  (org-element-map ast 'keyword
+    (lambda (k)
+      (let ((key (org-element-property :key k)))
+	(and (member key org-element-affiliated-keywords)
+	     (list (org-element-property :post-affiliated k)
+		   (format "Orphaned affiliated keyword: \"%s\"" key)))))))
+
+(defun org-lint-obsolete-affiliated-keywords (_)
+  (let ((regexp (format "^[ \t]*#\\+%s:"
+			(regexp-opt '("DATA" "LABEL" "RESNAME" "SOURCE"
+				      "SRCNAME" "TBLNAME" "RESULT" "HEADERS")
+				    t)))
+	reports)
+    (while (re-search-forward regexp nil t)
+      (let ((key (upcase (org-match-string-no-properties 1))))
+	(when (< (point)
+		 (org-element-property :post-affiliated (org-element-at-point)))
+	  (push
+	   (list (line-beginning-position)
+		 (format
+		  "Obsolete affiliated keyword: \"%s\".  Use \"%s\" instead"
+		  key
+		  (pcase key
+		    ("HEADERS" "HEADER")
+		    ("RESULT" "RESULTS")
+		    (_ "NAME"))))
+	   reports))))
+    reports))
+
+(defun org-lint-missing-language-in-src-block (ast)
+  (org-element-map ast 'src-block
+    (lambda (b)
+      (unless (org-element-property :language b)
+	(list (org-element-property :post-affiliated b)
+	      "Missing language in source block")))))
+
+(defun org-lint-deprecated-category-setup (ast)
+  (org-element-map ast 'keyword
+    (let (category-flag)
+      (lambda (k)
+	(cond
+	 ((not (string= (org-element-property :key k) "CATEGORY")) nil)
+	 (category-flag
+	  (list (org-element-property :post-affiliated k)
+		"Spurious CATEGORY keyword.  Set :CATEGORY: property instead"))
+	 (t (setf category-flag t) nil))))))
+
+(defun org-lint-invalid-coderef-link (ast)
+  (let ((info (list :parse-tree ast)))
+    (org-element-map ast 'link
+      (lambda (link)
+	(let ((ref (org-element-property :path link)))
+	  (and (equal (org-element-property :type link) "coderef")
+	       (not (ignore-errors (org-export-resolve-coderef ref info)))
+	       (list (org-element-property :begin link)
+		     (format "Unknown coderef \"%s\"" ref))))))))
+
+(defun org-lint-invalid-custom-id-link (ast)
+  (let ((info (list :parse-tree ast)))
+    (org-element-map ast 'link
+      (lambda (link)
+	(and (equal (org-element-property :type link) "custom-id")
+	     (not (ignore-errors (org-export-resolve-id-link link info)))
+	     (list (org-element-property :begin link)
+		   (format "Unknown custom ID \"%s\""
+			   (org-element-property :path link))))))))
+
+(defun org-lint-invalid-fuzzy-link (ast)
+  (let ((info (list :parse-tree ast)))
+    (org-element-map ast 'link
+      (lambda (link)
+	(and (equal (org-element-property :type link) "fuzzy")
+	     (not (ignore-errors (org-export-resolve-fuzzy-link link info)))
+	     (list (org-element-property :begin link)
+		   (format "Unknown fuzzy location \"%s\""
+			   (let ((path (org-element-property :path link)))
+			     (if (string-prefix-p "*" path)
+				 (substring path 1)
+			       path)))))))))
+
+(defun org-lint-invalid-id-link (ast)
+  (org-element-map ast 'link
+    (lambda (link)
+      (let ((id (org-element-property :path link)))
+	(and (equal (org-element-property :type link) "id")
+	     (not (org-id-find id))
+	     (list (org-element-property :begin link)
+		   (format "Unknown ID \"%s\"" id)))))))
+
+(defun org-lint-special-property-in-properties-drawer (ast)
+  (org-element-map ast 'node-property
+    (lambda (p)
+      (let ((key (org-element-property :key p)))
+	(and (member-ignore-case key org-special-properties)
+	     (list (org-element-property :begin p)
+		   (format
+		    "Special property \"%s\" found in a properties drawer"
+		    key)))))))
+
+(defun org-lint-obsolete-properties-drawer (ast)
+  (org-element-map ast 'drawer
+    (lambda (d)
+      (when (equal (org-element-property :drawer-name d) "PROPERTIES")
+	(let ((section (org-element-lineage d '(section))))
+	  (unless (org-element-map section 'property-drawer #'identity nil t)
+	    (list (org-element-property :post-affiliated d)
+		  (if (save-excursion
+			(goto-char (org-element-property :post-affiliated d))
+			(forward-line -1)
+			(or (org-at-heading-p) (org-at-planning-p)))
+		      "Incorrect contents for PROPERTIES drawer"
+		    "Incorrect location for PROPERTIES drawer"))))))))
+
+(defun org-lint-link-to-local-file (ast)
+  (org-element-map ast 'link
+    (lambda (l)
+      (when (equal (org-element-property :type l) "file")
+	(let ((file (org-element-property :path l)))
+	  (and (not (file-remote-p file))
+	       (not (file-exists-p file))
+	       (list (org-element-property :begin l)
+		     (format "Link to non-existent local file \"%s\""
+			     file))))))))
+
+(defun org-lint-non-existent-setupfile-parameter (ast)
+  (org-element-map ast 'keyword
+    (lambda (k)
+      (when (equal (org-element-property :key k) "SETUPFILE")
+	(let ((file (org-remove-double-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 setup file \"%s\"" file))))))))
+
+(defun org-lint-wrong-include-link-parameter (ast)
+  (org-element-map ast 'keyword
+    (lambda (k)
+      (when (equal (org-element-property :key k) "INCLUDE")
+	(let* ((value (org-element-property :value k))
+	       (path
+		(and (string-match "^\\(\".+\"\\|\\S-+\\)[ \t]*" value)
+		     (save-match-data
+		       (org-remove-double-quotes (match-string 1 value))))))
+	  (if (not path)
+	      (list (org-element-property :post-affiliated k)
+		    "Missing location argument in INCLUDE keyword")
+	    (let* ((file (org-string-nw-p
+			  (if (string-match "::\\(.*\\)\\'" path)
+			      (substring path 0 (match-beginning 0))
+			    path)))
+		   (search (and (not (equal file path))
+				(org-string-nw-p (match-string 1 path)))))
+	      (if (and file
+		       (not (file-remote-p file))
+		       (not (file-exists-p file)))
+		  (list (org-element-property :post-affiliated k)
+			"Non-existent file argument in INCLUDE keyword")
+		(let* ((visiting (if file (find-buffer-visiting file)
+				   (current-buffer)))
+		       (buffer (or visiting (find-file-noselect file))))
+		  (unwind-protect
+		      (with-current-buffer buffer
+			(when (and search
+				   (not
+				    (ignore-errors
+				      (let ((org-link-search-inhibit-query t))
+					(org-link-search search nil nil t)))))
+			  (list
+			   (org-element-property :post-affiliated k)
+			   (format
+			    "Invalid search part \"%s\" in INCLUDE keyword"
+			    search))))
+		    (unless visiting (kill-buffer buffer))))))))))))
+
+(defun org-lint-undefined-footnote-reference (ast)
+  (let ((definitions (org-element-map ast 'footnote-definition
+		       (lambda (f) (org-element-property :label f)))))
+    (org-element-map ast 'footnote-reference
+      (lambda (f)
+	(let ((label (org-element-property :label f)))
+	  (and label
+	       (not (member label definitions))
+	       (list (org-element-property :begin f)
+		     (format "Missing definition for footnote [%s]"
+			     label))))))))
+
+(defun org-lint-unreferenced-footnote-definition (ast)
+  (let ((references (org-element-map ast 'footnote-reference
+		      (lambda (f) (org-element-property :label f)))))
+    (org-element-map ast 'footnote-definition
+      (lambda (f)
+	(let ((label (org-element-property :label f)))
+	  (and label
+	       (not (member label references))
+	       (list (org-element-property :post-affiliated f)
+		     (format "No reference for footnote definition [%s]"
+			     label))))))))
+
+(defun org-lint-colon-in-name (ast)
+  (org-element-map ast org-element-all-elements
+    (lambda (e)
+      (let ((name (org-element-property :name e)))
+	(and name
+	     (org-string-match-p ":" name)
+	     (list (progn
+		     (goto-char (org-element-property :begin e))
+		     (let ((case-fold-search t))
+		       (re-search-forward
+			(concat "^[ \t]*#\\+NAME: +" (regexp-quote name)))
+		       (match-beginning 0)))
+		   (format
+		    "Name \"%s\" contains a colon; Babel cannot use it as input"
+		    name)))))))
+
+(defun org-lint-incomplete-drawer (_)
+  (let (reports)
+    (while (re-search-forward org-drawer-regexp nil t)
+      (let ((name (org-trim (org-match-string-no-properties 0)))
+	    (element (org-element-at-point)))
+	(pcase (org-element-type element)
+	  (`(drawer property-drawer)
+	   (goto-char (org-element-property :end element))
+	   nil)
+	  (`paragraph
+	   (push (list (line-beginning-position)
+		       (format "Possible incomplete drawer \"%s\"" name))
+		 reports))
+	  (_ nil))))
+    reports))
+
+(defun org-lint-corrupted-block (_)
+  (let ((case-fold-search t)
+	(regexp "^[ \t]*#\\+\\(BEGIN\\|END\\)\\(?::\\|_[^[:space:]]*\\)?[ \t]*")
+	reports)
+    (while (re-search-forward regexp nil t)
+      (let ((name (org-trim (buffer-substring-no-properties
+			     (line-beginning-position) (line-end-position)))))
+	(cond
+	 ((and (string-prefix-p "END" (match-string 1) t)
+	       (not (eolp)))
+	  (push (list (line-beginning-position)
+		      (format "Invalid block closing line \"%s\"" name))
+		reports))
+	 ((eq (org-element-type (org-element-at-point)) 'paragraph)
+	  (push (list (line-beginning-position)
+		      (format "Possible incomplete block \"%s\""
+			      name))
+		reports)))))
+    reports))
+
+(defun org-lint-invalid-keyword-syntax (_)
+  (let ((regexp "^[ \t]*\\(#\\+[^[:space:]:]*\\)\\(?:[[:space:]]\\|$\\)")
+	reports)
+    (while (re-search-forward regexp nil t)
+      (let ((name (org-match-string-no-properties 1)))
+	(unless (or (string-prefix-p "#+BEGIN" name t)
+		    (string-prefix-p "#+END" name t))
+	  (push (list (match-beginning 0)
+		      (format "Possible missing colon in keyword \"%s\"" name))
+		reports))))
+    reports))
+
+(defun org-lint-extraneous-element-in-footnote-section (ast)
+  (org-element-map ast 'headline
+    (lambda (h)
+      (and (org-element-property :footnote-section-p h)
+	   (org-element-map (org-element-contents h)
+	       (org-remove-if
+		(lambda (e)
+		  (memq e '(comment comment-block footnote-definition
+				    property-drawer section)))
+		org-element-all-elements)
+	     (lambda (e)
+	       (not (and (eq (org-element-type e) 'headline)
+			 (org-element-property :commentedp e))))
+	     nil t '(footnote-definition property-drawer))
+	   (list (org-element-property :begin h)
+		 "Extraneous elements in footnote section")))))
+
+(defun org-lint-quote-section (ast)
+  (org-element-map ast '(headline inlinetask)
+    (lambda (h)
+      (let ((title (org-element-property :raw-value h)))
+	(and (or (string-prefix-p "QUOTE " title)
+		 (string-prefix-p (concat org-comment-string " QUOTE ") title))
+	     (list (org-element-property :begin h)
+		   "Deprecated QUOTE section"))))))
+
+(defun org-lint-wrong-header-argument (ast)
+  (let* ((reports)
+	 (verify
+	  (lambda (datum lang headers)
+	    (let ((allowed
+		   (append
+		    org-babel-header-arg-names
+		    (and lang
+			 (let ((v (intern
+				   (concat "org-babel-header-args:" lang))))
+			   (and (boundp v)
+				(mapcar #'car (symbol-value v))))))))
+	      (dolist (header headers)
+		(let ((h (substring (symbol-name (car header)) 1)))
+		  (unless (assoc-string h allowed)
+		    (push (list (org-element-property :post-affiliated datum)
+				(format "Unknown header argument \"%s\"" h))
+			  reports))))))))
+    (org-element-map ast '(keyword node-property src-block)
+      (lambda (datum)
+	(pcase (org-element-type datum)
+	  (`keyword
+	   (when (string= (org-element-property :key datum) "PROPERTY")
+	     (let ((value (org-element-property :value datum)))
+	       (when (string-match "\\`header-args\\(?::\\(\\S-+\\)\\)? *"
+				   value)
+		 (funcall verify
+			  datum
+			  (match-string 1 value)
+			  (org-babel-parse-header-arguments
+			   (substring value (match-end 0))))))))
+	  (`node-property
+	   (let ((key (org-element-property :key datum)))
+	     (when (let ((case-fold-search t))
+		     (string-match "\\`HEADER-ARGS\\(?::\\(\\S-+\\)\\)?" key))
+	       (funcall verify
+			datum
+			(match-string 1 key)
+			(org-babel-parse-header-arguments
+			 (org-element-property :value datum))))))
+	  (`src-block
+	   (funcall verify
+		    datum
+		    (org-element-property :language datum)
+		    (cl-mapcan #'org-babel-parse-header-arguments
+			       (cons (org-element-property :parameters datum)
+				     (org-element-property :header datum))))))))
+    reports))
+
+\f
+;;; Reports UI
+
+(defvar org-lint--report-mode-map
+  (let ((map (make-sparse-keymap)))
+    (set-keymap-parent map tabulated-list-mode-map)
+    (define-key map (kbd "RET") 'org-lint--jump-to-source)
+    (define-key map (kbd "C-j") 'org-lint--show-source)
+    map)
+  "Local keymap for `org-lint--report-mode' buffers.")
+
+(define-derived-mode org-lint--report-mode tabulated-list-mode "OrgLint"
+  "Major mode used to display reports emitted during linting.
+\\<package-menu-mode-map>"
+  (setf tabulated-list-format
+	`[("Line" 6
+	   (lambda (a b)
+	     (< (string-to-number (aref (cadr a) 0))
+		(string-to-number (aref (cadr b) 0))))
+	   :right-align t)
+	  ("Trust" 5 t)
+	  ("Warning" 0 nil)])
+  (tabulated-list-init-header))
+
+(defun org-lint--generate-reports (checkers)
+  "Run all CHECKERS in current buffer.
+Return an alist (ID [LINE TRUST DESCRIPTION]), suitable for
+`tabulated-list-printer'."
+  (save-excursion
+    (goto-char (point-min))
+    (let ((ast (org-element-parse-buffer))
+	  (id 0)
+	  (last-line 1)
+	  (last-pos 1))
+      ;; Insert unique ID for each report.  Replace buffer positions
+      ;; with line numbers.
+      (mapcar
+       (lambda (report)
+	 (list
+	  (incf id)
+	  (apply #'vector
+		 (cons
+		  (progn
+		    (goto-char (car report))
+		    (beginning-of-line)
+		    (prog1 (number-to-string
+			    (incf last-line (count-lines last-pos (point))))
+		      (setf last-pos (point))))
+		  (cdr report)))))
+       ;; Insert trust level in generated reports.  Also sort them by
+       ;; buffer position in order to optimize lines computation.
+       (sort (cl-mapcan
+	      (lambda (c)
+		(let ((trust (symbol-name (org-lint-checker-trust c))))
+		  (mapcar
+		   (lambda (report) (list (car report) trust (nth 1 report)))
+		   (save-excursion
+		     (funcall (intern
+			       (concat "org-lint-"
+				       (symbol-name (org-lint-checker-name c))))
+			      ast)))))
+	      checkers)
+	     #'car-less-than-car)))))
+
+(defvar org-lint--source-buffer nil
+  "Source buffer associated to current report buffer.")
+(make-variable-buffer-local 'org-lint--source-buffer)
+
+(defun org-lint--display-reports (source checkers)
+  "Display linting reports for buffer SOURCE.
+CHECKERS is the list of checkers used."
+  (let ((buffer (get-buffer-create "*Org Lint*")))
+    (with-current-buffer buffer
+      (org-lint--report-mode)
+      (setf org-lint--source-buffer source)
+      (setf tabulated-list-entries
+	    (lambda ()
+	      (with-current-buffer source
+		(org-lint--generate-reports checkers))))
+      (tabulated-list-print))
+    (pop-to-buffer buffer)))
+
+(defun org-lint--jump-to-source ()
+  "Move to source line that generated the report at point."
+  (interactive)
+  (let ((l (string-to-number (aref (tabulated-list-get-entry) 0))))
+    (switch-to-buffer-other-window org-lint--source-buffer)
+    (org-goto-line l)
+    (org-show-set-visibility 'local)
+    (recenter)))
+
+(defun org-lint--show-source ()
+  "Show source line that generated the report at point."
+  (interactive)
+  (let ((buffer (current-buffer))
+	(l (string-to-number (aref (tabulated-list-get-entry) 0))))
+    (switch-to-buffer-other-window org-lint--source-buffer)
+    (org-goto-line l)
+    (org-show-set-visibility 'local)
+    (recenter)
+    (switch-to-buffer-other-window buffer)))
+
+\f
+;;; Public function
+
+;;;###autoload
+(defun org-lint (&optional arg)
+  "Check current Org buffer for syntax mistakes.
+
+By default, run all checkers.  With a single prefix ARG \
+\\[universal-argument],
+select one category of checkers only.  With a double prefix
+\\[universal-argument] \\[universal-argument], select one precise \
+checker by its name.
+
+ARG can also be a list of checker names, as symbols, to run."
+  (interactive "P")
+  (unless (derived-mode-p 'org-mode) (user-error "Not in an Org buffer"))
+  (message "Org linting process starting...")
+  (org-lint--display-reports
+   (current-buffer)
+   (pcase arg
+     (`nil org-lint--checkers)
+     (`(4)
+      (let ((category
+	     (completing-read
+	      "Checker category: "
+	      (mapcar #'org-lint-checker-categories org-lint--checkers)
+	      nil t)))
+	(org-remove-if-not
+	 (lambda (c) (assoc-string (org-lint-checker-categories c) category))
+	 org-lint--checkers)))
+     (`(16)
+      (list
+       (let ((name (completing-read
+		    "Checker name: "
+		    (mapcar #'org-lint-checker-name org-lint--checkers)
+		    nil t)))
+	 (catch 'exit
+	   (dolist (c org-lint--checkers)
+	     (when (string= (org-lint-checker-name c) name)
+	       (throw 'exit c)))))))
+     ((pred consp)
+      (org-remove-if-not (lambda (c) (memq (org-lint-checker-name c) arg))
+			 org-lint--checkers))
+     (_ (user-error "Invalid argument `%S' for `org-lint'" arg))))
+  (message "Org linting process completed"))
+
+
+(provide 'org-lint)
+;;; org-lint.el ends here
-- 
2.3.5


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* Re: [RFC] Org linting library
  2015-04-19 19:32 ` Marco Wahl
@ 2015-04-20 10:42   ` Nicolas Goaziou
  0 siblings, 0 replies; 53+ messages in thread
From: Nicolas Goaziou @ 2015-04-20 10:42 UTC (permalink / raw)
  To: Marco Wahl; +Cc: emacs-orgmode

Marco Wahl <marcowahlsoft@gmail.com> writes:

> I think it's too much that org-lint tries to follow ssh links.

It doesn't follow remote paths.

> 2278 low   Link to non-existent local file "///ssh:root@127.0.0.1#2222:/etc/httpd/conf/httpd.conf"
                                  ^^^^^       ^^^

There's a bug in the parser, which doesn't know how to handle
"/ssh:root@127.0.0.1#2222:/etc/httpd/conf/httpd.conf".

It prefixes it with "//", turning it into
"///ssh:root@127.0.0.1#2222:/etc/httpd/conf/httpd.conf". Then `org-lint'
thinks it's a local file.

The problem should be fixed in master, so you need to update Org.

Thanks for the feedback.

Regards,

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [RFC] Org linting library
  2015-04-19 13:31 [RFC] Org linting library Nicolas Goaziou
                   ` (2 preceding siblings ...)
  2015-04-20  2:15 ` Charles C. Berry
@ 2015-04-22 19:27 ` Nicolas Goaziou
  2015-04-22 19:31   ` Sebastien Vauban
                     ` (2 more replies)
  2015-04-27 15:22 ` Doug Lewan
                   ` (3 subsequent siblings)
  7 siblings, 3 replies; 53+ messages in thread
From: Nicolas Goaziou @ 2015-04-22 19:27 UTC (permalink / raw)
  To: Org Mode List

[-- Attachment #1: Type: text/plain, Size: 190 bytes --]

Here's another update. I added a few more tests.

If there's no more bug report or feedback, I'll simply put it in
a "wip-lint" branch until Org 8.4 starts its development cycle.

Regards,


[-- Attachment #2: org-lint.el --]
[-- Type: application/emacs-lisp, Size: 33790 bytes --]

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [RFC] Org linting library
  2015-04-22 19:27 ` Nicolas Goaziou
@ 2015-04-22 19:31   ` Sebastien Vauban
  2015-04-22 19:40     ` Nicolas Goaziou
  2015-04-23 10:47   ` Eric Abrahamsen
  2015-04-23 16:25   ` Sebastien Vauban
  2 siblings, 1 reply; 53+ messages in thread
From: Sebastien Vauban @ 2015-04-22 19:31 UTC (permalink / raw)
  To: emacs-orgmode-mXXj517/zsQ

Hello Nicolas,

Nicolas Goaziou wrote:
> Here's another update. I added a few more tests.
>
> If there's no more bug report or feedback, I'll simply put it in
> a "wip-lint" branch until Org 8.4 starts its development cycle.

I'd like to enforce org-lint on all my Org docs, either in a find-file
hook or before export, for example.

Can't you put it in master as well?

Best regards,
  Seb

-- 
Sebastien Vauban

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [RFC] Org linting library
  2015-04-22 19:31   ` Sebastien Vauban
@ 2015-04-22 19:40     ` Nicolas Goaziou
  0 siblings, 0 replies; 53+ messages in thread
From: Nicolas Goaziou @ 2015-04-22 19:40 UTC (permalink / raw)
  To: Sebastien Vauban; +Cc: public-emacs-orgmode-mXXj517/zsQ



Sebastien Vauban <sva-news-D0wtAvR13HarG/iDocfnWg@public.gmane.org>
writes:

> I'd like to enforce org-lint on all my Org docs, either in a find-file
> hook or before export, for example.
>
> Can't you put it in master as well?

No, I can't. 

As explained in the original post, "org-lint.el" requires at least Emacs
24.3 (lexical binding, pcase, tabulated-list-mode...) whereas Org 8.3 is
expected to be Emacs 23 compatible.


Regards,

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [RFC] Org linting library
  2015-04-22 19:27 ` Nicolas Goaziou
  2015-04-22 19:31   ` Sebastien Vauban
@ 2015-04-23 10:47   ` Eric Abrahamsen
  2015-04-23 16:25   ` Sebastien Vauban
  2 siblings, 0 replies; 53+ messages in thread
From: Eric Abrahamsen @ 2015-04-23 10:47 UTC (permalink / raw)
  To: emacs-orgmode

Nicolas Goaziou <mail@nicolasgoaziou.fr> writes:

> Here's another update. I added a few more tests.
>
> If there's no more bug report or feedback, I'll simply put it in
> a "wip-lint" branch until Org 8.4 starts its development cycle.

This is handy! No bugs to report.

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [RFC] Org linting library
  2015-04-22 19:27 ` Nicolas Goaziou
  2015-04-22 19:31   ` Sebastien Vauban
  2015-04-23 10:47   ` Eric Abrahamsen
@ 2015-04-23 16:25   ` Sebastien Vauban
  2015-04-26 12:50     ` Nicolas Goaziou
  2 siblings, 1 reply; 53+ messages in thread
From: Sebastien Vauban @ 2015-04-23 16:25 UTC (permalink / raw)
  To: emacs-orgmode-mXXj517/zsQ

Nicolas Goaziou wrote:
> Here's another update. I added a few more tests.
>
> If there's no more bug report or feedback, I'll simply put it in
> a "wip-lint" branch until Org 8.4 starts its development cycle.

Could `org-lint' return a success/fail indication, so that we could
write something along in an export hook:

--8<---------------cut here---------------start------------->8---
(if (org-lint)
    ... continue with export ...
  ... stop with message or display the lint buffer ...)
--8<---------------cut here---------------end--------------->8---

As well, could the buffer be hidden (or killed) if it's empty?

Best regards,
  Seb

-- 
Sebastien Vauban

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [RFC] Org linting library
  2015-04-23 16:25   ` Sebastien Vauban
@ 2015-04-26 12:50     ` Nicolas Goaziou
  0 siblings, 0 replies; 53+ messages in thread
From: Nicolas Goaziou @ 2015-04-26 12:50 UTC (permalink / raw)
  To: Sebastien Vauban; +Cc: public-emacs-orgmode-mXXj517/zsQ



Sebastien Vauban <sva-news-D0wtAvR13HarG/iDocfnWg@public.gmane.org>
writes:

> Could `org-lint' return a success/fail indication, so that we could
> write something along in an export hook:
>
> (if (org-lint)
>     ... continue with export ...
>   ... stop with message or display the lint buffer ...)
>
> As well, could the buffer be hidden (or killed) if it's empty?

When called non-interactively `org-lint' returns the reports, as an
alist or nil, so it can be used as a predicate.

Regards,

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [RFC] Org linting library
  2015-04-19 13:31 [RFC] Org linting library Nicolas Goaziou
                   ` (3 preceding siblings ...)
  2015-04-22 19:27 ` Nicolas Goaziou
@ 2015-04-27 15:22 ` Doug Lewan
  2015-05-19 11:54 ` Andreas Leha
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 53+ messages in thread
From: Doug Lewan @ 2015-04-27 15:22 UTC (permalink / raw)
  To: Nicolas Goaziou, Org Mode List


> -----Original Message-----
> On
> Behalf Of Nicolas Goaziou
> Sent: Sunday, 2015 April 19 09:32
> To: Org Mode List
> Subject: [O] [RFC] Org linting library
> 
> Hello,
> 
> The following library implements linting for Org syntax. The sole
> public
> function is `org-lint', which see.
> Nicolas Goaziou
> 0x80A93738
[Doug Lewan] 

Very cool. Thank you.


-- 
,Doug
Douglas Lewan
Shubert Ticketing
(201) 489-8600 ext 224 or ext 4335

The human brain is the most complex thing known to man, according to the human brain.

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [RFC] Org linting library
  2015-04-19 13:31 [RFC] Org linting library Nicolas Goaziou
                   ` (4 preceding siblings ...)
  2015-04-27 15:22 ` Doug Lewan
@ 2015-05-19 11:54 ` Andreas Leha
  2015-05-19 13:39   ` Nicolas Goaziou
  2015-05-19 13:32 ` Rainer M Krug
  2015-06-05 10:51 ` Rainer M Krug
  7 siblings, 1 reply; 53+ messages in thread
From: Andreas Leha @ 2015-05-19 11:54 UTC (permalink / raw)
  To: emacs-orgmode

Nicolas Goaziou <mail@nicolasgoaziou.fr> writes:
> Hello,
>
> The following library implements linting for Org syntax. The sole public
> function is `org-lint', which see.
>
> Internally, the library defines a new structure: `org-lint-checker',
> with the following slots:
>
>   - NAME: Unique check identifier, as a symbol. The check is done
>     calling the function `org-lint-NAME' with one mandatory argument,
>     the parse tree describing the current Org buffer. Such function
>     calls are wrapped within a `save-excursion' and point is always at
>     `point-min'. Its return value has to be an alist (POSITION MESSAGE)
>     when POSITION refer to the buffer position of the error, as an
>     integer, and MESSAGE is a strings describing the error.
>
>   - DESCRIPTION: Summary about the check, as a string.
>
>   - CATEGORIES: Categories relative to the check, as a list of symbol.
>     They are used for filtering when calling `org-lint'. Checkers not
>     explicitly associated to a category are collected in the `default'
>     one.
>
>   - TRUST: The trust level one can have in the check. It is either `low'
>     or `high', depending on the heuristics implemented and the nature of
>     the check. This has an indicative value only and is displayed along
>     reports.
>
> All checks have to be listed in `org-lint--checkers'.
>
> Results are displayed in a special "*Org Lint*" buffer with a dedicated
> major mode, derived from `tabulated-list-mode'. In addition to the usual
> key-bindings inherited from it, "C-j" displays problematic line reported
> under point and "RET" jumps to it.
>
> Checks currently implemented are:
>
>   - duplicates CUSTOM_ID properties
>   - duplicate NAME values
>   - duplicate targets
>   - duplicate footnote definitions
>   - orphaned affiliated keywords
>   - obsolete affiliated keywords
>   - missing language in src blocks
>   - NAME values with a colon
>   - wrong header arguments in src blocks
>   - misuse of CATEGORY keyword
>   - "coderef" links with unknown destination
>   - "custom-id" links with unknown destination
>   - "fuzzy" links with unknown destination
>   - "id" links with unknown destination
>   - links to non-existent local files
>   - special properties in properties drawer
>   - obsolete syntax for PROPERTIES drawers
>   - missing definition for footnote references
>   - missing reference for footnote definitions
>   - non-footnote definitions in footnote section
>   - probable invalid keywords
>   - invalid blocks
>   - probable incomplete drawers
>   - obsolete QUOTE section
>
> Since it relies on lexical binding, `pcase' and `string-prefix-p', it
> cannot be added to Org 8.3, but can make it into Org 8.4, if deemed
> useful enough.
>
> Feedback welcome.
>
>
> Regards,

Hi Nicolas,

I have finally made some time to test this.  I get some
unexpected warnings:

   79 low   Unknown value "myfile.R" for header ":tangle"
  192 low   Unknown value "none" for header ":results"
22985 low   Unknown value "never-export" for header ":eval"

Thanks for this very useful tool!

Thanks,
Andreas

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [RFC] Org linting library
  2015-04-19 13:31 [RFC] Org linting library Nicolas Goaziou
                   ` (5 preceding siblings ...)
  2015-05-19 11:54 ` Andreas Leha
@ 2015-05-19 13:32 ` Rainer M Krug
  2015-05-19 13:43   ` Andreas Leha
  2015-05-19 13:47   ` Nicolas Goaziou
  2015-06-05 10:51 ` Rainer M Krug
  7 siblings, 2 replies; 53+ messages in thread
From: Rainer M Krug @ 2015-05-19 13:32 UTC (permalink / raw)
  To: Org Mode List

[-- Attachment #1: Type: text/plain, Size: 3477 bytes --]

Nicolas Goaziou <mail@nicolasgoaziou.fr> writes:

> Hello,
>
> The following library implements linting for Org syntax. The sole public
> function is `org-lint', which see.
>
> Internally, the library defines a new structure: `org-lint-checker',
> with the following slots:
>
>   - NAME: Unique check identifier, as a symbol. The check is done
>     calling the function `org-lint-NAME' with one mandatory argument,
>     the parse tree describing the current Org buffer. Such function
>     calls are wrapped within a `save-excursion' and point is always at
>     `point-min'. Its return value has to be an alist (POSITION MESSAGE)
>     when POSITION refer to the buffer position of the error, as an
>     integer, and MESSAGE is a strings describing the error.
>
>   - DESCRIPTION: Summary about the check, as a string.
>
>   - CATEGORIES: Categories relative to the check, as a list of symbol.
>     They are used for filtering when calling `org-lint'. Checkers not
>     explicitly associated to a category are collected in the `default'
>     one.
>
>   - TRUST: The trust level one can have in the check. It is either `low'
>     or `high', depending on the heuristics implemented and the nature of
>     the check. This has an indicative value only and is displayed along
>     reports.
>
> All checks have to be listed in `org-lint--checkers'.
>
> Results are displayed in a special "*Org Lint*" buffer with a dedicated
> major mode, derived from `tabulated-list-mode'. In addition to the usual
> key-bindings inherited from it, "C-j" displays problematic line reported
> under point and "RET" jumps to it.
>
> Checks currently implemented are:
>
>   - duplicates CUSTOM_ID properties
>   - duplicate NAME values
>   - duplicate targets
>   - duplicate footnote definitions
>   - orphaned affiliated keywords
>   - obsolete affiliated keywords
>   - missing language in src blocks
>   - NAME values with a colon
>   - wrong header arguments in src blocks
>   - misuse of CATEGORY keyword
>   - "coderef" links with unknown destination
>   - "custom-id" links with unknown destination
>   - "fuzzy" links with unknown destination
>   - "id" links with unknown destination
>   - links to non-existent local files
>   - special properties in properties drawer
>   - obsolete syntax for PROPERTIES drawers
>   - missing definition for footnote references
>   - missing reference for footnote definitions
>   - non-footnote definitions in footnote section
>   - probable invalid keywords
>   - invalid blocks
>   - probable incomplete drawers
>   - obsolete QUOTE section
>
> Since it relies on lexical binding, `pcase' and `string-prefix-p', it
> cannot be added to Org 8.3, but can make it into Org 8.4, if deemed
> useful enough.
>

This sounds very interesting and I would like to try it out. I
understand that it can't be put into master, but could it be put into a
branch?

This would make testing a bit easier.

Thanks,

Rainer

> Feedback welcome.
>
>
> Regards,

-- 
Rainer M. Krug, PhD (Conservation Ecology, SUN), MSc (Conservation Biology, UCT), Dipl. Phys. (Germany)

Centre of Excellence for Invasion Biology
Stellenbosch University
South Africa

Tel :       +33 - (0)9 53 10 27 44
Cell:       +33 - (0)6 85 62 59 98
Fax :       +33 - (0)9 58 10 27 44

Fax (D):    +49 - (0)3 21 21 25 22 44

email:      Rainer@krugs.de

Skype:      RMkrug

PGP: 0x0F52F982

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 494 bytes --]

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [RFC] Org linting library
  2015-05-19 11:54 ` Andreas Leha
@ 2015-05-19 13:39   ` Nicolas Goaziou
  2015-05-19 14:54     ` Andreas Leha
  0 siblings, 1 reply; 53+ messages in thread
From: Nicolas Goaziou @ 2015-05-19 13:39 UTC (permalink / raw)
  To: Andreas Leha; +Cc: emacs-orgmode

Hello,

Andreas Leha <andreas.leha@med.uni-goettingen.de> writes:

> I have finally made some time to test this. I get some
> unexpected warnings:
>
>    79 low   Unknown value "myfile.R" for header ":tangle"
>   192 low   Unknown value "none" for header ":results"

Make sure you rebased the wip-lint branch on top of master.

> 22985 low   Unknown value "never-export" for header ":eval"

I added it to allowed value. You need to update Org.

> Thanks for this very useful tool!

Thanks for the feedback.


Regards,

-- 
Nicolas Goaziou

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [RFC] Org linting library
  2015-05-19 13:32 ` Rainer M Krug
@ 2015-05-19 13:43   ` Andreas Leha
  2015-05-20 15:01     ` Rainer M Krug
  2015-05-19 13:47   ` Nicolas Goaziou
  1 sibling, 1 reply; 53+ messages in thread
From: Andreas Leha @ 2015-05-19 13:43 UTC (permalink / raw)
  To: emacs-orgmode

Hi Rainer,

Rainer M Krug <Rainer@krugs.de> writes:
> Nicolas Goaziou <mail@nicolasgoaziou.fr> writes:
>
>> Hello,
>>
>> The following library implements linting for Org syntax. The sole public
>> function is `org-lint', which see.
>>
>> Internally, the library defines a new structure: `org-lint-checker',
>> with the following slots:
>>
>>   - NAME: Unique check identifier, as a symbol. The check is done
>>     calling the function `org-lint-NAME' with one mandatory argument,
>>     the parse tree describing the current Org buffer. Such function
>>     calls are wrapped within a `save-excursion' and point is always at
>>     `point-min'. Its return value has to be an alist (POSITION MESSAGE)
>>     when POSITION refer to the buffer position of the error, as an
>>     integer, and MESSAGE is a strings describing the error.
>>
>>   - DESCRIPTION: Summary about the check, as a string.
>>
>>   - CATEGORIES: Categories relative to the check, as a list of symbol.
>>     They are used for filtering when calling `org-lint'. Checkers not
>>     explicitly associated to a category are collected in the `default'
>>     one.
>>
>>   - TRUST: The trust level one can have in the check. It is either `low'
>>     or `high', depending on the heuristics implemented and the nature of
>>     the check. This has an indicative value only and is displayed along
>>     reports.
>>
>> All checks have to be listed in `org-lint--checkers'.
>>
>> Results are displayed in a special "*Org Lint*" buffer with a dedicated
>> major mode, derived from `tabulated-list-mode'. In addition to the usual
>> key-bindings inherited from it, "C-j" displays problematic line reported
>> under point and "RET" jumps to it.
>>
>> Checks currently implemented are:
>>
>>   - duplicates CUSTOM_ID properties
>>   - duplicate NAME values
>>   - duplicate targets
>>   - duplicate footnote definitions
>>   - orphaned affiliated keywords
>>   - obsolete affiliated keywords
>>   - missing language in src blocks
>>   - NAME values with a colon
>>   - wrong header arguments in src blocks
>>   - misuse of CATEGORY keyword
>>   - "coderef" links with unknown destination
>>   - "custom-id" links with unknown destination
>>   - "fuzzy" links with unknown destination
>>   - "id" links with unknown destination
>>   - links to non-existent local files
>>   - special properties in properties drawer
>>   - obsolete syntax for PROPERTIES drawers
>>   - missing definition for footnote references
>>   - missing reference for footnote definitions
>>   - non-footnote definitions in footnote section
>>   - probable invalid keywords
>>   - invalid blocks
>>   - probable incomplete drawers
>>   - obsolete QUOTE section
>>
>> Since it relies on lexical binding, `pcase' and `string-prefix-p', it
>> cannot be added to Org 8.3, but can make it into Org 8.4, if deemed
>> useful enough.
>>
>
> This sounds very interesting and I would like to try it out. I
> understand that it can't be put into master, but could it be put into a
> branch?
>
> This would make testing a bit easier.
>

It is.  The branch is called `wip-lint'.

Regards,
Andreas

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [RFC] Org linting library
  2015-05-19 13:32 ` Rainer M Krug
  2015-05-19 13:43   ` Andreas Leha
@ 2015-05-19 13:47   ` Nicolas Goaziou
  1 sibling, 0 replies; 53+ messages in thread
From: Nicolas Goaziou @ 2015-05-19 13:47 UTC (permalink / raw)
  To: Rainer M Krug; +Cc: Org Mode List

Rainer M Krug <Rainer@krugs.de> writes:

> This sounds very interesting and I would like to try it out. I
> understand that it can't be put into master, but could it be put into a
> branch?

It already lives in "wip-lint" branch.

Regards,

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [RFC] Org linting library
  2015-05-19 13:39   ` Nicolas Goaziou
@ 2015-05-19 14:54     ` Andreas Leha
  2015-05-19 19:09       ` Nicolas Goaziou
  0 siblings, 1 reply; 53+ messages in thread
From: Andreas Leha @ 2015-05-19 14:54 UTC (permalink / raw)
  To: emacs-orgmode

Hi Nicolas,

Nicolas Goaziou <mail@nicolasgoaziou.fr> writes:
>> I have finally made some time to test this. I get some
>> unexpected warnings:
>>
>>    79 low   Unknown value "myfile.R" for header ":tangle"
>>   192 low   Unknown value "none" for header ":results"
>
> Make sure you rebased the wip-lint branch on top of master.
>
>> 22985 low   Unknown value "never-export" for header ":eval"
>
> I added it to allowed value. You need to update Org.
>

I just pulled and rebased again.  The "never-export" issue is
gone, indeed. The others are still there.  What am I missing
here?

Thanks,
Andreas

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [RFC] Org linting library
  2015-05-19 14:54     ` Andreas Leha
@ 2015-05-19 19:09       ` Nicolas Goaziou
  2015-05-19 20:02         ` Andreas Leha
  0 siblings, 1 reply; 53+ messages in thread
From: Nicolas Goaziou @ 2015-05-19 19:09 UTC (permalink / raw)
  To: Andreas Leha; +Cc: emacs-orgmode

Andreas Leha <andreas.leha@med.uni-goettingen.de> writes:

> I just pulled and rebased again.  The "never-export" issue is
> gone, indeed. The others are still there.  What am I missing
> here?

Nothing. I overlooked a typo. It should be fixed in wip-lint.

Thank you.

Regards,

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [RFC] Org linting library
  2015-05-19 19:09       ` Nicolas Goaziou
@ 2015-05-19 20:02         ` Andreas Leha
  2015-05-19 21:03           ` Nicolas Goaziou
  0 siblings, 1 reply; 53+ messages in thread
From: Andreas Leha @ 2015-05-19 20:02 UTC (permalink / raw)
  To: emacs-orgmode

Hi Nicolas,

Nicolas Goaziou <mail@nicolasgoaziou.fr> writes:
> Andreas Leha <andreas.leha@med.uni-goettingen.de> writes:
>
>> I just pulled and rebased again.  The "never-export" issue is
>> gone, indeed. The others are still there.  What am I missing
>> here?
>
> Nothing. I overlooked a typo. It should be fixed in wip-lint.

Thanks.  Now the :tangle header argument does not produce a
warning.

But I still get

--8<---------------cut here---------------start------------->8---
191 low Unknown value "none" for header ":results"
--8<---------------cut here---------------end--------------->8---


Ahh, and one more thing: The :tangle property in my test case was set
buffer wide through

--8<---------------cut here---------------start------------->8---
#+PROPERTY: header-args:R :tangle "myfile.R"
--8<---------------cut here---------------end--------------->8---

But still, it produced a warning for each code block.  It would
be nicer if that warning was issued only once, IMO.  But that is
very minor.

Regards,
Andreas

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [RFC] Org linting library
  2015-05-19 20:02         ` Andreas Leha
@ 2015-05-19 21:03           ` Nicolas Goaziou
  2015-05-19 21:10             ` Andreas Leha
  0 siblings, 1 reply; 53+ messages in thread
From: Nicolas Goaziou @ 2015-05-19 21:03 UTC (permalink / raw)
  To: Andreas Leha; +Cc: emacs-orgmode

Andreas Leha <andreas.leha@med.uni-goettingen.de> writes:

> But I still get
>
> 191 low Unknown value "none" for header ":results"

Could you show an ECM? I cannot reproduce it.

Regards,

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [RFC] Org linting library
  2015-05-19 21:03           ` Nicolas Goaziou
@ 2015-05-19 21:10             ` Andreas Leha
  2015-05-19 21:26               ` Nicolas Goaziou
  0 siblings, 1 reply; 53+ messages in thread
From: Andreas Leha @ 2015-05-19 21:10 UTC (permalink / raw)
  To: emacs-orgmode

Hi Nicolas,

Nicolas Goaziou <mail@nicolasgoaziou.fr> writes:
> Andreas Leha <andreas.leha@med.uni-goettingen.de> writes:
>
>> But I still get
>>
>> 191 low Unknown value "none" for header ":results"
>
> Could you show an ECM? I cannot reproduce it.
>

Sure:

--8<---------------cut here---------------start------------->8---
* Test code block names

#+name: test
#+begin_src R :results none
1:10
#+end_src
--8<---------------cut here---------------end--------------->8---

Tested with "emacs -Q" with the same result:

--8<---------------cut here---------------start------------->8---
     4 low   Unknown value "none" for header ":results"
--8<---------------cut here---------------end--------------->8---

Regards,
Andreas

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [RFC] Org linting library
  2015-05-19 21:10             ` Andreas Leha
@ 2015-05-19 21:26               ` Nicolas Goaziou
  2015-05-19 21:35                 ` Andreas Leha
  0 siblings, 1 reply; 53+ messages in thread
From: Nicolas Goaziou @ 2015-05-19 21:26 UTC (permalink / raw)
  To: Andreas Leha; +Cc: emacs-orgmode

Andreas Leha <andreas.leha@med.uni-goettingen.de> writes:

> * Test code block names
>
> #+name: test
> #+begin_src R :results none
> 1:10
> #+end_src

OK. It was a R-specific bug. It should now be fixed.

Thank you.

Regards,

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [RFC] Org linting library
  2015-05-19 21:26               ` Nicolas Goaziou
@ 2015-05-19 21:35                 ` Andreas Leha
  0 siblings, 0 replies; 53+ messages in thread
From: Andreas Leha @ 2015-05-19 21:35 UTC (permalink / raw)
  To: emacs-orgmode

Hi Nicolas,

Nicolas Goaziou <mail@nicolasgoaziou.fr> writes:
> Andreas Leha <andreas.leha@med.uni-goettingen.de> writes:
>
>> * Test code block names
>>
>> #+name: test
>> #+begin_src R :results none
>> 1:10
>> #+end_src
>
> OK. It was a R-specific bug. It should now be fixed.
>

Thank you!  Fix confirmed.  The last warning is gone.

Thanks,
Andreas

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [RFC] Org linting library
  2015-05-19 13:43   ` Andreas Leha
@ 2015-05-20 15:01     ` Rainer M Krug
  2015-05-20 15:08       ` Rainer M Krug
  0 siblings, 1 reply; 53+ messages in thread
From: Rainer M Krug @ 2015-05-20 15:01 UTC (permalink / raw)
  To: Andreas Leha; +Cc: emacs-orgmode

[-- Attachment #1: Type: text/plain, Size: 4277 bytes --]

Andreas Leha <andreas.leha@med.uni-goettingen.de> writes:

> Hi Rainer,
>
> Rainer M Krug <Rainer@krugs.de> writes:
>> Nicolas Goaziou <mail@nicolasgoaziou.fr> writes:
>>
>>> Hello,
>>>
>>> The following library implements linting for Org syntax. The sole public
>>> function is `org-lint', which see.
>>>
>>> Internally, the library defines a new structure: `org-lint-checker',
>>> with the following slots:
>>>
>>>   - NAME: Unique check identifier, as a symbol. The check is done
>>>     calling the function `org-lint-NAME' with one mandatory argument,
>>>     the parse tree describing the current Org buffer. Such function
>>>     calls are wrapped within a `save-excursion' and point is always at
>>>     `point-min'. Its return value has to be an alist (POSITION MESSAGE)
>>>     when POSITION refer to the buffer position of the error, as an
>>>     integer, and MESSAGE is a strings describing the error.
>>>
>>>   - DESCRIPTION: Summary about the check, as a string.
>>>
>>>   - CATEGORIES: Categories relative to the check, as a list of symbol.
>>>     They are used for filtering when calling `org-lint'. Checkers not
>>>     explicitly associated to a category are collected in the `default'
>>>     one.
>>>
>>>   - TRUST: The trust level one can have in the check. It is either `low'
>>>     or `high', depending on the heuristics implemented and the nature of
>>>     the check. This has an indicative value only and is displayed along
>>>     reports.
>>>
>>> All checks have to be listed in `org-lint--checkers'.
>>>
>>> Results are displayed in a special "*Org Lint*" buffer with a dedicated
>>> major mode, derived from `tabulated-list-mode'. In addition to the usual
>>> key-bindings inherited from it, "C-j" displays problematic line reported
>>> under point and "RET" jumps to it.
>>>
>>> Checks currently implemented are:
>>>
>>>   - duplicates CUSTOM_ID properties
>>>   - duplicate NAME values
>>>   - duplicate targets
>>>   - duplicate footnote definitions
>>>   - orphaned affiliated keywords
>>>   - obsolete affiliated keywords
>>>   - missing language in src blocks
>>>   - NAME values with a colon
>>>   - wrong header arguments in src blocks
>>>   - misuse of CATEGORY keyword
>>>   - "coderef" links with unknown destination
>>>   - "custom-id" links with unknown destination
>>>   - "fuzzy" links with unknown destination
>>>   - "id" links with unknown destination
>>>   - links to non-existent local files
>>>   - special properties in properties drawer
>>>   - obsolete syntax for PROPERTIES drawers
>>>   - missing definition for footnote references
>>>   - missing reference for footnote definitions
>>>   - non-footnote definitions in footnote section
>>>   - probable invalid keywords
>>>   - invalid blocks
>>>   - probable incomplete drawers
>>>   - obsolete QUOTE section
>>>
>>> Since it relies on lexical binding, `pcase' and `string-prefix-p', it
>>> cannot be added to Org 8.3, but can make it into Org 8.4, if deemed
>>> useful enough.
>>>
>>
>> This sounds very interesting and I would like to try it out. I
>> understand that it can't be put into master, but could it be put into a
>> branch?
>>
>> This would make testing a bit easier.
>>
>
> It is.  The branch is called `wip-lint'.

Thanks (also to Nicolas) - I found it. Just expected the branch to be
tracked automatically.

This is really brilliant!

But I now get a message in one .org file:

,----
| Org linting process starting...
| Search failed: "^[ 	]*#\\+NAME: +tab:sensVar"
`----

and no results.

Works in other .org files.

This one is rather long (11570 lines) and many code blocks.

Just let me know how I can trace down where this is coming from and what
the message tells me.

Thanks,

Rainer

>
> Regards,
> Andreas
>
>

-- 
Rainer M. Krug, PhD (Conservation Ecology, SUN), MSc (Conservation Biology, UCT), Dipl. Phys. (Germany)

Centre of Excellence for Invasion Biology
Stellenbosch University
South Africa

Tel :       +33 - (0)9 53 10 27 44
Cell:       +33 - (0)6 85 62 59 98
Fax :       +33 - (0)9 58 10 27 44

Fax (D):    +49 - (0)3 21 21 25 22 44

email:      Rainer@krugs.de

Skype:      RMkrug

PGP: 0x0F52F982

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 494 bytes --]

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [RFC] Org linting library
  2015-05-20 15:01     ` Rainer M Krug
@ 2015-05-20 15:08       ` Rainer M Krug
  2015-05-20 15:24         ` Rainer M Krug
  0 siblings, 1 reply; 53+ messages in thread
From: Rainer M Krug @ 2015-05-20 15:08 UTC (permalink / raw)
  To: Andreas Leha; +Cc: emacs-orgmode

[-- Attachment #1: Type: text/plain, Size: 4598 bytes --]

Rainer M Krug <Rainer@krugs.de> writes:

> Andreas Leha <andreas.leha@med.uni-goettingen.de> writes:
>
>> Hi Rainer,
>>
>> Rainer M Krug <Rainer@krugs.de> writes:
>>> Nicolas Goaziou <mail@nicolasgoaziou.fr> writes:
>>>
>>>> Hello,
>>>>
>>>> The following library implements linting for Org syntax. The sole public
>>>> function is `org-lint', which see.
>>>>
>>>> Internally, the library defines a new structure: `org-lint-checker',
>>>> with the following slots:
>>>>
>>>>   - NAME: Unique check identifier, as a symbol. The check is done
>>>>     calling the function `org-lint-NAME' with one mandatory argument,
>>>>     the parse tree describing the current Org buffer. Such function
>>>>     calls are wrapped within a `save-excursion' and point is always at
>>>>     `point-min'. Its return value has to be an alist (POSITION MESSAGE)
>>>>     when POSITION refer to the buffer position of the error, as an
>>>>     integer, and MESSAGE is a strings describing the error.
>>>>
>>>>   - DESCRIPTION: Summary about the check, as a string.
>>>>
>>>>   - CATEGORIES: Categories relative to the check, as a list of symbol.
>>>>     They are used for filtering when calling `org-lint'. Checkers not
>>>>     explicitly associated to a category are collected in the `default'
>>>>     one.
>>>>
>>>>   - TRUST: The trust level one can have in the check. It is either `low'
>>>>     or `high', depending on the heuristics implemented and the nature of
>>>>     the check. This has an indicative value only and is displayed along
>>>>     reports.
>>>>
>>>> All checks have to be listed in `org-lint--checkers'.
>>>>
>>>> Results are displayed in a special "*Org Lint*" buffer with a dedicated
>>>> major mode, derived from `tabulated-list-mode'. In addition to the usual
>>>> key-bindings inherited from it, "C-j" displays problematic line reported
>>>> under point and "RET" jumps to it.
>>>>
>>>> Checks currently implemented are:
>>>>
>>>>   - duplicates CUSTOM_ID properties
>>>>   - duplicate NAME values
>>>>   - duplicate targets
>>>>   - duplicate footnote definitions
>>>>   - orphaned affiliated keywords
>>>>   - obsolete affiliated keywords
>>>>   - missing language in src blocks
>>>>   - NAME values with a colon
>>>>   - wrong header arguments in src blocks
>>>>   - misuse of CATEGORY keyword
>>>>   - "coderef" links with unknown destination
>>>>   - "custom-id" links with unknown destination
>>>>   - "fuzzy" links with unknown destination
>>>>   - "id" links with unknown destination
>>>>   - links to non-existent local files
>>>>   - special properties in properties drawer
>>>>   - obsolete syntax for PROPERTIES drawers
>>>>   - missing definition for footnote references
>>>>   - missing reference for footnote definitions
>>>>   - non-footnote definitions in footnote section
>>>>   - probable invalid keywords
>>>>   - invalid blocks
>>>>   - probable incomplete drawers
>>>>   - obsolete QUOTE section
>>>>
>>>> Since it relies on lexical binding, `pcase' and `string-prefix-p', it
>>>> cannot be added to Org 8.3, but can make it into Org 8.4, if deemed
>>>> useful enough.
>>>>
>>>
>>> This sounds very interesting and I would like to try it out. I
>>> understand that it can't be put into master, but could it be put into a
>>> branch?
>>>
>>> This would make testing a bit easier.
>>>
>>
>> It is.  The branch is called `wip-lint'.
>
> Thanks (also to Nicolas) - I found it. Just expected the branch to be
> tracked automatically.
>
> This is really brilliant!
>
> But I now get a message in one .org file:
>
> ,----
> | Org linting process starting...
> | Search failed: "^[ 	]*#\\+NAME: +tab:sensVar"
> `----
>
> and no results.
>
> Works in other .org files.
>
> This one is rather long (11570 lines) and many code blocks.
>
> Just let me know how I can trace down where this is coming from and what
> the message tells me.

It seems that the error comes from the fact that ~#+LABEL: sensVar~ was
defined twice.

Renaming these results in working linting.

Rainer

>
> Thanks,
>
> Rainer
>
>>
>> Regards,
>> Andreas
>>
>>

-- 
Rainer M. Krug, PhD (Conservation Ecology, SUN), MSc (Conservation Biology, UCT), Dipl. Phys. (Germany)

Centre of Excellence for Invasion Biology
Stellenbosch University
South Africa

Tel :       +33 - (0)9 53 10 27 44
Cell:       +33 - (0)6 85 62 59 98
Fax :       +33 - (0)9 58 10 27 44

Fax (D):    +49 - (0)3 21 21 25 22 44

email:      Rainer@krugs.de

Skype:      RMkrug

PGP: 0x0F52F982

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 494 bytes --]

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [RFC] Org linting library
  2015-05-20 15:08       ` Rainer M Krug
@ 2015-05-20 15:24         ` Rainer M Krug
  2015-05-20 20:42           ` Andreas Leha
  2015-05-20 21:46           ` Nicolas Goaziou
  0 siblings, 2 replies; 53+ messages in thread
From: Rainer M Krug @ 2015-05-20 15:24 UTC (permalink / raw)
  To: Nicolas Goaziou; +Cc: emacs-orgmode

[-- Attachment #1: Type: text/plain, Size: 5644 bytes --]

Rainer M Krug <Rainer@krugs.de> writes:

> Rainer M Krug <Rainer@krugs.de> writes:
>
>> Andreas Leha <andreas.leha@med.uni-goettingen.de> writes:
>>
>>> Hi Rainer,
>>>
>>> Rainer M Krug <Rainer@krugs.de> writes:
>>>> Nicolas Goaziou <mail@nicolasgoaziou.fr> writes:
>>>>
>>>>> Hello,
>>>>>
>>>>> The following library implements linting for Org syntax. The sole public
>>>>> function is `org-lint', which see.
>>>>>
>>>>> Internally, the library defines a new structure: `org-lint-checker',
>>>>> with the following slots:
>>>>>
>>>>>   - NAME: Unique check identifier, as a symbol. The check is done
>>>>>     calling the function `org-lint-NAME' with one mandatory argument,
>>>>>     the parse tree describing the current Org buffer. Such function
>>>>>     calls are wrapped within a `save-excursion' and point is always at
>>>>>     `point-min'. Its return value has to be an alist (POSITION MESSAGE)
>>>>>     when POSITION refer to the buffer position of the error, as an
>>>>>     integer, and MESSAGE is a strings describing the error.
>>>>>
>>>>>   - DESCRIPTION: Summary about the check, as a string.
>>>>>
>>>>>   - CATEGORIES: Categories relative to the check, as a list of symbol.
>>>>>     They are used for filtering when calling `org-lint'. Checkers not
>>>>>     explicitly associated to a category are collected in the `default'
>>>>>     one.
>>>>>
>>>>>   - TRUST: The trust level one can have in the check. It is either `low'
>>>>>     or `high', depending on the heuristics implemented and the nature of
>>>>>     the check. This has an indicative value only and is displayed along
>>>>>     reports.
>>>>>
>>>>> All checks have to be listed in `org-lint--checkers'.
>>>>>
>>>>> Results are displayed in a special "*Org Lint*" buffer with a dedicated
>>>>> major mode, derived from `tabulated-list-mode'. In addition to the usual
>>>>> key-bindings inherited from it, "C-j" displays problematic line reported
>>>>> under point and "RET" jumps to it.
>>>>>
>>>>> Checks currently implemented are:
>>>>>
>>>>>   - duplicates CUSTOM_ID properties
>>>>>   - duplicate NAME values
>>>>>   - duplicate targets
>>>>>   - duplicate footnote definitions
>>>>>   - orphaned affiliated keywords
>>>>>   - obsolete affiliated keywords
>>>>>   - missing language in src blocks
>>>>>   - NAME values with a colon
>>>>>   - wrong header arguments in src blocks
>>>>>   - misuse of CATEGORY keyword
>>>>>   - "coderef" links with unknown destination
>>>>>   - "custom-id" links with unknown destination
>>>>>   - "fuzzy" links with unknown destination
>>>>>   - "id" links with unknown destination
>>>>>   - links to non-existent local files
>>>>>   - special properties in properties drawer
>>>>>   - obsolete syntax for PROPERTIES drawers
>>>>>   - missing definition for footnote references
>>>>>   - missing reference for footnote definitions
>>>>>   - non-footnote definitions in footnote section
>>>>>   - probable invalid keywords
>>>>>   - invalid blocks
>>>>>   - probable incomplete drawers
>>>>>   - obsolete QUOTE section
>>>>>
>>>>> Since it relies on lexical binding, `pcase' and `string-prefix-p', it
>>>>> cannot be added to Org 8.3, but can make it into Org 8.4, if deemed
>>>>> useful enough.
>>>>>
>>>>
>>>> This sounds very interesting and I would like to try it out. I
>>>> understand that it can't be put into master, but could it be put into a
>>>> branch?
>>>>
>>>> This would make testing a bit easier.
>>>>
>>>
>>> It is.  The branch is called `wip-lint'.
>>
>> Thanks (also to Nicolas) - I found it. Just expected the branch to be
>> tracked automatically.
>>
>> This is really brilliant!
>>
>> But I now get a message in one .org file:
>>
>> ,----
>> | Org linting process starting...
>> | Search failed: "^[ 	]*#\\+NAME: +tab:sensVar"
>> `----
>>
>> and no results.
>>
>> Works in other .org files.
>>
>> This one is rather long (11570 lines) and many code blocks.
>>
>> Just let me know how I can trace down where this is coming from and what
>> the message tells me.
>
> It seems that the error comes from the fact that ~#+LABEL: sensVar~ was
> defined twice.
>
> Renaming these results in working linting.

OK - please ignore this last comment.

There is an example where I get the error:

--8<---------------cut here---------------start------------->8---
* Fitting the kernel to the data
The parameter which will be fitted can be found in Table [[tab:fitInitial]]

#+CAPTION: Variables used for the initial fit of the wind profile using the function and the initial values.
#+LABEL: tab:fitInitial
| variable           | initial value | remark                                           |
|--------------------+---------------+--------------------------------------------------|
--8<---------------cut here---------------end--------------->8---

The cause is the link [[tab:initial]] If I remove everything below and
including the line #+CAPTION the linting works.

,----
|      2 high  Unknown fuzzy location "tab:fitInitial"
`----

Cheers,

Rainer



>
> Rainer
>
>>
>> Thanks,
>>
>> Rainer
>>
>>>
>>> Regards,
>>> Andreas
>>>
>>>

-- 
Rainer M. Krug, PhD (Conservation Ecology, SUN), MSc (Conservation Biology, UCT), Dipl. Phys. (Germany)

Centre of Excellence for Invasion Biology
Stellenbosch University
South Africa

Tel :       +33 - (0)9 53 10 27 44
Cell:       +33 - (0)6 85 62 59 98
Fax :       +33 - (0)9 58 10 27 44

Fax (D):    +49 - (0)3 21 21 25 22 44

email:      Rainer@krugs.de

Skype:      RMkrug

PGP: 0x0F52F982

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 494 bytes --]

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [RFC] Org linting library
  2015-05-20 15:24         ` Rainer M Krug
@ 2015-05-20 20:42           ` Andreas Leha
  2015-05-20 21:15             ` Rainer M Krug
  2015-05-20 21:46           ` Nicolas Goaziou
  1 sibling, 1 reply; 53+ messages in thread
From: Andreas Leha @ 2015-05-20 20:42 UTC (permalink / raw)
  To: emacs-orgmode

Hi Rainer,

Rainer M Krug <Rainer@krugs.de> writes:
> Rainer M Krug <Rainer@krugs.de> writes:
>
>> Rainer M Krug <Rainer@krugs.de> writes:
>>
>>> Andreas Leha <andreas.leha@med.uni-goettingen.de> writes:
>>>
>>>> Hi Rainer,
>>>>
>>>> Rainer M Krug <Rainer@krugs.de> writes:
>>>>> Nicolas Goaziou <mail@nicolasgoaziou.fr> writes:
>>>>>
>>>>>> Hello,
>>>>>>
>>>>>> The following library implements linting for Org syntax. The sole public
>>>>>> function is `org-lint', which see.
>>>>>>
>>>>>> Internally, the library defines a new structure: `org-lint-checker',
>>>>>> with the following slots:
>>>>>>
>>>>>>   - NAME: Unique check identifier, as a symbol. The check is done
>>>>>>     calling the function `org-lint-NAME' with one mandatory argument,
>>>>>>     the parse tree describing the current Org buffer. Such function
>>>>>>     calls are wrapped within a `save-excursion' and point is always at
>>>>>>     `point-min'. Its return value has to be an alist (POSITION MESSAGE)
>>>>>>     when POSITION refer to the buffer position of the error, as an
>>>>>>     integer, and MESSAGE is a strings describing the error.
>>>>>>
>>>>>>   - DESCRIPTION: Summary about the check, as a string.
>>>>>>
>>>>>>   - CATEGORIES: Categories relative to the check, as a list of symbol.
>>>>>>     They are used for filtering when calling `org-lint'. Checkers not
>>>>>>     explicitly associated to a category are collected in the `default'
>>>>>>     one.
>>>>>>
>>>>>>   - TRUST: The trust level one can have in the check. It is either `low'
>>>>>>     or `high', depending on the heuristics implemented and the nature of
>>>>>>     the check. This has an indicative value only and is displayed along
>>>>>>     reports.
>>>>>>
>>>>>> All checks have to be listed in `org-lint--checkers'.
>>>>>>
>>>>>> Results are displayed in a special "*Org Lint*" buffer with a dedicated
>>>>>> major mode, derived from `tabulated-list-mode'. In addition to the usual
>>>>>> key-bindings inherited from it, "C-j" displays problematic line reported
>>>>>> under point and "RET" jumps to it.
>>>>>>
>>>>>> Checks currently implemented are:
>>>>>>
>>>>>>   - duplicates CUSTOM_ID properties
>>>>>>   - duplicate NAME values
>>>>>>   - duplicate targets
>>>>>>   - duplicate footnote definitions
>>>>>>   - orphaned affiliated keywords
>>>>>>   - obsolete affiliated keywords
>>>>>>   - missing language in src blocks
>>>>>>   - NAME values with a colon
>>>>>>   - wrong header arguments in src blocks
>>>>>>   - misuse of CATEGORY keyword
>>>>>>   - "coderef" links with unknown destination
>>>>>>   - "custom-id" links with unknown destination
>>>>>>   - "fuzzy" links with unknown destination
>>>>>>   - "id" links with unknown destination
>>>>>>   - links to non-existent local files
>>>>>>   - special properties in properties drawer
>>>>>>   - obsolete syntax for PROPERTIES drawers
>>>>>>   - missing definition for footnote references
>>>>>>   - missing reference for footnote definitions
>>>>>>   - non-footnote definitions in footnote section
>>>>>>   - probable invalid keywords
>>>>>>   - invalid blocks
>>>>>>   - probable incomplete drawers
>>>>>>   - obsolete QUOTE section
>>>>>>
>>>>>> Since it relies on lexical binding, `pcase' and `string-prefix-p', it
>>>>>> cannot be added to Org 8.3, but can make it into Org 8.4, if deemed
>>>>>> useful enough.
>>>>>>
>>>>>
>>>>> This sounds very interesting and I would like to try it out. I
>>>>> understand that it can't be put into master, but could it be put into a
>>>>> branch?
>>>>>
>>>>> This would make testing a bit easier.
>>>>>
>>>>
>>>> It is.  The branch is called `wip-lint'.
>>>
>>> Thanks (also to Nicolas) - I found it. Just expected the branch to be
>>> tracked automatically.
>>>
>>> This is really brilliant!
>>>
>>> But I now get a message in one .org file:
>>>
>>> ,----
>>> | Org linting process starting...
>>> | Search failed: "^[ 	]*#\\+NAME: +tab:sensVar"
>>> `----
>>>
>>> and no results.
>>>
>>> Works in other .org files.
>>>
>>> This one is rather long (11570 lines) and many code blocks.
>>>
>>> Just let me know how I can trace down where this is coming from and what
>>> the message tells me.
>>
>> It seems that the error comes from the fact that ~#+LABEL: sensVar~ was
>> defined twice.
>>
>> Renaming these results in working linting.
>
> OK - please ignore this last comment.
>
> There is an example where I get the error:
>
> * Fitting the kernel to the data
> The parameter which will be fitted can be found in Table [[tab:fitInitial]]
>
> #+CAPTION: Variables used for the initial fit of the wind profile using the function and the initial values.
> #+LABEL: tab:fitInitial
> | variable           | initial value | remark                                           |
> |--------------------+---------------+--------------------------------------------------|
>
> The cause is the link [[tab:initial]] If I remove everything below and
> including the line #+CAPTION the linting works.
>
> ,----
> |      2 high  Unknown fuzzy location "tab:fitInitial"
> `----
>

Untested - but should that not be
#+NAME: tab:fitInitial
rather than #+LABEL?

Cheers,
Andreas

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [RFC] Org linting library
  2015-05-20 20:42           ` Andreas Leha
@ 2015-05-20 21:15             ` Rainer M Krug
  0 siblings, 0 replies; 53+ messages in thread
From: Rainer M Krug @ 2015-05-20 21:15 UTC (permalink / raw)
  To: Andreas Leha; +Cc: emacs-orgmode@gnu.org



Envoyé de mon iPhone

> Le 20 mai 2015 à 22:42, Andreas Leha <andreas.leha@med.uni-goettingen.de> a écrit :
> 
> Hi Rainer,
> 
> Rainer M Krug <Rainer@krugs.de> writes:
>> Rainer M Krug <Rainer@krugs.de> writes:
>> 
>>> Rainer M Krug <Rainer@krugs.de> writes:
>>> 
>>>> Andreas Leha <andreas.leha@med.uni-goettingen.de> writes:
>>>> 
>>>>> Hi Rainer,
>>>>> 
>>>>> Rainer M Krug <Rainer@krugs.de> writes:
>>>>>> Nicolas Goaziou <mail@nicolasgoaziou.fr> writes:
>>>>>> 
>>>>>>> Hello,
>>>>>>> 
>>>>>>> The following library implements linting for Org syntax. The sole public
>>>>>>> function is `org-lint', which see.
>>>>>>> 
>>>>>>> Internally, the library defines a new structure: `org-lint-checker',
>>>>>>> with the following slots:
>>>>>>> 
>>>>>>>  - NAME: Unique check identifier, as a symbol. The check is done
>>>>>>>    calling the function `org-lint-NAME' with one mandatory argument,
>>>>>>>    the parse tree describing the current Org buffer. Such function
>>>>>>>    calls are wrapped within a `save-excursion' and point is always at
>>>>>>>    `point-min'. Its return value has to be an alist (POSITION MESSAGE)
>>>>>>>    when POSITION refer to the buffer position of the error, as an
>>>>>>>    integer, and MESSAGE is a strings describing the error.
>>>>>>> 
>>>>>>>  - DESCRIPTION: Summary about the check, as a string.
>>>>>>> 
>>>>>>>  - CATEGORIES: Categories relative to the check, as a list of symbol.
>>>>>>>    They are used for filtering when calling `org-lint'. Checkers not
>>>>>>>    explicitly associated to a category are collected in the `default'
>>>>>>>    one.
>>>>>>> 
>>>>>>>  - TRUST: The trust level one can have in the check. It is either `low'
>>>>>>>    or `high', depending on the heuristics implemented and the nature of
>>>>>>>    the check. This has an indicative value only and is displayed along
>>>>>>>    reports.
>>>>>>> 
>>>>>>> All checks have to be listed in `org-lint--checkers'.
>>>>>>> 
>>>>>>> Results are displayed in a special "*Org Lint*" buffer with a dedicated
>>>>>>> major mode, derived from `tabulated-list-mode'. In addition to the usual
>>>>>>> key-bindings inherited from it, "C-j" displays problematic line reported
>>>>>>> under point and "RET" jumps to it.
>>>>>>> 
>>>>>>> Checks currently implemented are:
>>>>>>> 
>>>>>>>  - duplicates CUSTOM_ID properties
>>>>>>>  - duplicate NAME values
>>>>>>>  - duplicate targets
>>>>>>>  - duplicate footnote definitions
>>>>>>>  - orphaned affiliated keywords
>>>>>>>  - obsolete affiliated keywords
>>>>>>>  - missing language in src blocks
>>>>>>>  - NAME values with a colon
>>>>>>>  - wrong header arguments in src blocks
>>>>>>>  - misuse of CATEGORY keyword
>>>>>>>  - "coderef" links with unknown destination
>>>>>>>  - "custom-id" links with unknown destination
>>>>>>>  - "fuzzy" links with unknown destination
>>>>>>>  - "id" links with unknown destination
>>>>>>>  - links to non-existent local files
>>>>>>>  - special properties in properties drawer
>>>>>>>  - obsolete syntax for PROPERTIES drawers
>>>>>>>  - missing definition for footnote references
>>>>>>>  - missing reference for footnote definitions
>>>>>>>  - non-footnote definitions in footnote section
>>>>>>>  - probable invalid keywords
>>>>>>>  - invalid blocks
>>>>>>>  - probable incomplete drawers
>>>>>>>  - obsolete QUOTE section
>>>>>>> 
>>>>>>> Since it relies on lexical binding, `pcase' and `string-prefix-p', it
>>>>>>> cannot be added to Org 8.3, but can make it into Org 8.4, if deemed
>>>>>>> useful enough.
>>>>>> 
>>>>>> This sounds very interesting and I would like to try it out. I
>>>>>> understand that it can't be put into master, but could it be put into a
>>>>>> branch?
>>>>>> 
>>>>>> This would make testing a bit easier.
>>>>> 
>>>>> It is.  The branch is called `wip-lint'.
>>>> 
>>>> Thanks (also to Nicolas) - I found it. Just expected the branch to be
>>>> tracked automatically.
>>>> 
>>>> This is really brilliant!
>>>> 
>>>> But I now get a message in one .org file:
>>>> 
>>>> ,----
>>>> | Org linting process starting...
>>>> | Search failed: "^[    ]*#\\+NAME: +tab:sensVar"
>>>> `----
>>>> 
>>>> and no results.
>>>> 
>>>> Works in other .org files.
>>>> 
>>>> This one is rather long (11570 lines) and many code blocks.
>>>> 
>>>> Just let me know how I can trace down where this is coming from and what
>>>> the message tells me.
>>> 
>>> It seems that the error comes from the fact that ~#+LABEL: sensVar~ was
>>> defined twice.
>>> 
>>> Renaming these results in working linting.
>> 
>> OK - please ignore this last comment.
>> 
>> There is an example where I get the error:
>> 
>> * Fitting the kernel to the data
>> The parameter which will be fitted can be found in Table [[tab:fitInitial]]
>> 
>> #+CAPTION: Variables used for the initial fit of the wind profile using the function and the initial values.
>> #+LABEL: tab:fitInitial
>> | variable           | initial value | remark                                           |
>> |--------------------+---------------+--------------------------------------------------|
>> 
>> The cause is the link [[tab:initial]] If I remove everything below and
>> including the line #+CAPTION the linting works.
>> 
>> ,----
>> |      2 high  Unknown fuzzy location "tab:fitInitial"
>> `----
> 
> Untested - but should that not be
> #+NAME: tab:fitInitial
> rather than #+LABEL?
> 

Yes - that would be correct. But the linting should tell me that instead of failing with this message 

Cheers,

Rainer

> Cheers,
> Andreas
> 
> 

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [RFC] Org linting library
  2015-05-20 15:24         ` Rainer M Krug
  2015-05-20 20:42           ` Andreas Leha
@ 2015-05-20 21:46           ` Nicolas Goaziou
  2015-05-21  8:40             ` Rainer M Krug
  1 sibling, 1 reply; 53+ messages in thread
From: Nicolas Goaziou @ 2015-05-20 21:46 UTC (permalink / raw)
  To: Rainer M Krug; +Cc: emacs-orgmode

Rainer M Krug <Rainer@krugs.de> writes:

> There is an example where I get the error:
>
> * Fitting the kernel to the data
> The parameter which will be fitted can be found in Table [[tab:fitInitial]]
>
> #+CAPTION: Variables used for the initial fit of the wind profile using the function and the initial values.
> #+LABEL: tab:fitInitial
> | variable           | initial value | remark                                           |
> |--------------------+---------------+--------------------------------------------------|
>
> The cause is the link [[tab:initial]] If I remove everything below and
> including the line #+CAPTION the linting works.

Fixed. Thank you.


Regards,

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [RFC] Org linting library
  2015-05-20 21:46           ` Nicolas Goaziou
@ 2015-05-21  8:40             ` Rainer M Krug
  2015-05-21 16:24               ` Nicolas Goaziou
  0 siblings, 1 reply; 53+ messages in thread
From: Rainer M Krug @ 2015-05-21  8:40 UTC (permalink / raw)
  To: emacs-orgmode

[-- Attachment #1: Type: text/plain, Size: 2269 bytes --]

Nicolas Goaziou <mail@nicolasgoaziou.fr> writes:

> Rainer M Krug <Rainer@krugs.de> writes:
>
>> There is an example where I get the error:
>>
>> * Fitting the kernel to the data
>> The parameter which will be fitted can be found in Table [[tab:fitInitial]]
>>
>> #+CAPTION: Variables used for the initial fit of the wind profile using the function and the initial values.
>> #+LABEL: tab:fitInitial
>> | variable           | initial value | remark                                           |
>> |--------------------+---------------+--------------------------------------------------|
>>
>> The cause is the link [[tab:initial]] If I remove everything below and
>> including the line #+CAPTION the linting works.
>
> Fixed. Thank you.
>

Thanks.

Two more questions:

I get the following warnings:

,----
|     10 low   Unknown OPTIONS item "@"
|     11 low   Unknown OPTIONS item "skip"
|     11 low   Unknown OPTIONS item "LaTeX"
|     11 low   Unknown OPTIONS item "TeX"
`----

based on the following lines:

,----
| #+OPTIONS:   H:3 num:t toc:t \n:nil @:t ::t |:t ^:nil -:t f:t *:t <:t
| #+OPTIONS:   TeX:t LaTeX:t skip:nil d:nil todo:t pri:nil tags:not-in-toc
`----

The file is not that old - but have these options changed?


and I get

,----
|   1821 low   Orphaned affiliated keyword: "RESULTS"
`----

caused by an orphaned

,----
| #+RESULTS:
`----

But these are created by evaluating a code block which produced no
results - so they are a valid part of the document?

And an error:

I get, in the same document I got the other errors, the following error:

,----
| Org linting process starting...
| org-split-string: Wrong type argument: stringp, 292
`----

The backtrace is huge - how can I produce a usable backtrace?

Hope this helps,

Rainer


>
> Regards,

-- 
Rainer M. Krug, PhD (Conservation Ecology, SUN), MSc (Conservation Biology, UCT), Dipl. Phys. (Germany)

Centre of Excellence for Invasion Biology
Stellenbosch University
South Africa

Tel :       +33 - (0)9 53 10 27 44
Cell:       +33 - (0)6 85 62 59 98
Fax :       +33 - (0)9 58 10 27 44

Fax (D):    +49 - (0)3 21 21 25 22 44

email:      Rainer@krugs.de

Skype:      RMkrug

PGP: 0x0F52F982

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 494 bytes --]

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [RFC] Org linting library
  2015-05-21  8:40             ` Rainer M Krug
@ 2015-05-21 16:24               ` Nicolas Goaziou
  2015-05-21 18:18                 ` Rainer M Krug
                                   ` (2 more replies)
  0 siblings, 3 replies; 53+ messages in thread
From: Nicolas Goaziou @ 2015-05-21 16:24 UTC (permalink / raw)
  To: Rainer M Krug; +Cc: emacs-orgmode

Rainer M Krug <Rainer@krugs.de> writes:

> Two more questions:
>
> I get the following warnings:
>
> ,----
> |     10 low   Unknown OPTIONS item "@"
> |     11 low   Unknown OPTIONS item "skip"
> |     11 low   Unknown OPTIONS item "LaTeX"
> |     11 low   Unknown OPTIONS item "TeX"
> `----
>
> based on the following lines:
>
> ,----
> | #+OPTIONS:   H:3 num:t toc:t \n:nil @:t ::t |:t ^:nil -:t f:t *:t <:t
> | #+OPTIONS:   TeX:t LaTeX:t skip:nil d:nil todo:t pri:nil tags:not-in-toc
> `----
>
> The file is not that old - but have these options changed?

These options do not do anything. "@" and "skip" have no equivalent.
"TeX" and "LaTeX" are now "tex". See (info "(org) Export settings").

> and I get
>
> ,----
> |   1821 low   Orphaned affiliated keyword: "RESULTS"
> `----
>
> caused by an orphaned
>
> ,----
> | #+RESULTS:
> `----
>
> But these are created by evaluating a code block which produced no
> results - so they are a valid part of the document?

Correct. Note however the "low" trust on this check. It's just
a heads-up. Anyway I remove such reports for RESULTS in wip-lint.

> And an error:
>
> I get, in the same document I got the other errors, the following error:
>
> ,----
> | Org linting process starting...
> | org-split-string: Wrong type argument: stringp, 292
> `----
>
> The backtrace is huge - how can I produce a usable backtrace?

Usually, only the last top-level function call is useful. 

It would be better to provide an ECM, tho. The error comes from Babel
header check. Does "292" ring a bell in a #+HEADER: line?


Regards,

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [RFC] Org linting library
  2015-05-21 16:24               ` Nicolas Goaziou
@ 2015-05-21 18:18                 ` Rainer M Krug
  2015-05-21 18:23                 ` Rainer M Krug
  2015-05-21 18:30                 ` Rainer M Krug
  2 siblings, 0 replies; 53+ messages in thread
From: Rainer M Krug @ 2015-05-21 18:18 UTC (permalink / raw)
  To: emacs-orgmode

[-- Attachment #1: Type: text/plain, Size: 2420 bytes --]

Nicolas Goaziou <mail@nicolasgoaziou.fr> writes:

> Rainer M Krug <Rainer@krugs.de> writes:
>
>> Two more questions:
>>
>> I get the following warnings:
>>
>> ,----
>> |     10 low   Unknown OPTIONS item "@"
>> |     11 low   Unknown OPTIONS item "skip"
>> |     11 low   Unknown OPTIONS item "LaTeX"
>> |     11 low   Unknown OPTIONS item "TeX"
>> `----
>>
>> based on the following lines:
>>
>> ,----
>> | #+OPTIONS:   H:3 num:t toc:t \n:nil @:t ::t |:t ^:nil -:t f:t *:t <:t
>> | #+OPTIONS:   TeX:t LaTeX:t skip:nil d:nil todo:t pri:nil tags:not-in-toc
>> `----
>>
>> The file is not that old - but have these options changed?
>
> These options do not do anything. "@" and "skip" have no equivalent.
> "TeX" and "LaTeX" are now "tex". See (info "(org) Export settings").
>
>> and I get
>>
>> ,----
>> |   1821 low   Orphaned affiliated keyword: "RESULTS"
>> `----
>>
>> caused by an orphaned
>>
>> ,----
>> | #+RESULTS:
>> `----
>>
>> But these are created by evaluating a code block which produced no
>> results - so they are a valid part of the document?
>
> Correct. Note however the "low" trust on this check. It's just
> a heads-up. Anyway I remove such reports for RESULTS in wip-lint.
>
>> And an error:
>>
>> I get, in the same document I got the other errors, the following error:
>>
>> ,----
>> | Org linting process starting...
>> | org-split-string: Wrong type argument: stringp, 292
>> `----
>>
>> The backtrace is huge - how can I produce a usable backtrace?
>
> Usually, only the last top-level function call is useful. 
>
> It would be better to provide an ECM, tho. The error comes from Babel
> header check. Does "292" ring a bell in a #+HEADER: line?

Hi

here is an ECM:

,----
| * Matlab code
| 
| #+begin_src octave
| 
| #+end_src
`----

Actually, whatever I put there instead of octave, I get the same error.

This is very strange. What is the problem here?

Cheers,

Rainer

>
>
> Regards,

-- 
Rainer M. Krug, PhD (Conservation Ecology, SUN), MSc (Conservation Biology, UCT), Dipl. Phys. (Germany)

Centre of Excellence for Invasion Biology
Stellenbosch University
South Africa

Tel :       +33 - (0)9 53 10 27 44
Cell:       +33 - (0)6 85 62 59 98
Fax :       +33 - (0)9 58 10 27 44

Fax (D):    +49 - (0)3 21 21 25 22 44

email:      Rainer@krugs.de

Skype:      RMkrug

PGP: 0x0F52F982

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 494 bytes --]

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [RFC] Org linting library
  2015-05-21 16:24               ` Nicolas Goaziou
  2015-05-21 18:18                 ` Rainer M Krug
@ 2015-05-21 18:23                 ` Rainer M Krug
  2015-05-21 18:30                 ` Rainer M Krug
  2 siblings, 0 replies; 53+ messages in thread
From: Rainer M Krug @ 2015-05-21 18:23 UTC (permalink / raw)
  To: emacs-orgmode

[-- Attachment #1: Type: text/plain, Size: 2358 bytes --]

Nicolas Goaziou <mail@nicolasgoaziou.fr> writes:

> Rainer M Krug <Rainer@krugs.de> writes:
>
>> Two more questions:
>>
>> I get the following warnings:
>>
>> ,----
>> |     10 low   Unknown OPTIONS item "@"
>> |     11 low   Unknown OPTIONS item "skip"
>> |     11 low   Unknown OPTIONS item "LaTeX"
>> |     11 low   Unknown OPTIONS item "TeX"
>> `----
>>
>> based on the following lines:
>>
>> ,----
>> | #+OPTIONS:   H:3 num:t toc:t \n:nil @:t ::t |:t ^:nil -:t f:t *:t <:t
>> | #+OPTIONS:   TeX:t LaTeX:t skip:nil d:nil todo:t pri:nil tags:not-in-toc
>> `----
>>
>> The file is not that old - but have these options changed?
>
> These options do not do anything. "@" and "skip" have no equivalent.
> "TeX" and "LaTeX" are now "tex". See (info "(org) Export settings").
>
>> and I get
>>
>> ,----
>> |   1821 low   Orphaned affiliated keyword: "RESULTS"
>> `----
>>
>> caused by an orphaned
>>
>> ,----
>> | #+RESULTS:
>> `----
>>
>> But these are created by evaluating a code block which produced no
>> results - so they are a valid part of the document?
>
> Correct. Note however the "low" trust on this check. It's just
> a heads-up. Anyway I remove such reports for RESULTS in wip-lint.
>
>> And an error:
>>
>> I get, in the same document I got the other errors, the following error:
>>
>> ,----
>> | Org linting process starting...
>> | org-split-string: Wrong type argument: stringp, 292
>> `----
>>
>> The backtrace is huge - how can I produce a usable backtrace?
>
> Usually, only the last top-level function call is useful. 
>
> It would be better to provide an ECM, tho. The error comes from Babel
> header check. Does "292" ring a bell in a #+HEADER: line?

Addition to my last email: when I close emacs and open it again and try
to linter the ECM, it works - is it possible that there is some caching
which is not cleaned up?

Rainer


>
>
> Regards,

-- 
Rainer M. Krug, PhD (Conservation Ecology, SUN), MSc (Conservation Biology, UCT), Dipl. Phys. (Germany)

Centre of Excellence for Invasion Biology
Stellenbosch University
South Africa

Tel :       +33 - (0)9 53 10 27 44
Cell:       +33 - (0)6 85 62 59 98
Fax :       +33 - (0)9 58 10 27 44

Fax (D):    +49 - (0)3 21 21 25 22 44

email:      Rainer@krugs.de

Skype:      RMkrug

PGP: 0x0F52F982

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 494 bytes --]

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [RFC] Org linting library
  2015-05-21 16:24               ` Nicolas Goaziou
  2015-05-21 18:18                 ` Rainer M Krug
  2015-05-21 18:23                 ` Rainer M Krug
@ 2015-05-21 18:30                 ` Rainer M Krug
  2015-05-21 21:53                   ` Nicolas Goaziou
  2 siblings, 1 reply; 53+ messages in thread
From: Rainer M Krug @ 2015-05-21 18:30 UTC (permalink / raw)
  To: emacs-orgmode

[-- Attachment #1: Type: text/plain, Size: 2574 bytes --]

Nicolas Goaziou <mail@nicolasgoaziou.fr> writes:

> Rainer M Krug <Rainer@krugs.de> writes:
>
>> Two more questions:
>>
>> I get the following warnings:
>>
>> ,----
>> |     10 low   Unknown OPTIONS item "@"
>> |     11 low   Unknown OPTIONS item "skip"
>> |     11 low   Unknown OPTIONS item "LaTeX"
>> |     11 low   Unknown OPTIONS item "TeX"
>> `----
>>
>> based on the following lines:
>>
>> ,----
>> | #+OPTIONS:   H:3 num:t toc:t \n:nil @:t ::t |:t ^:nil -:t f:t *:t <:t
>> | #+OPTIONS:   TeX:t LaTeX:t skip:nil d:nil todo:t pri:nil tags:not-in-toc
>> `----
>>
>> The file is not that old - but have these options changed?
>
> These options do not do anything. "@" and "skip" have no equivalent.
> "TeX" and "LaTeX" are now "tex". See (info "(org) Export settings").
>
>> and I get
>>
>> ,----
>> |   1821 low   Orphaned affiliated keyword: "RESULTS"
>> `----
>>
>> caused by an orphaned
>>
>> ,----
>> | #+RESULTS:
>> `----
>>
>> But these are created by evaluating a code block which produced no
>> results - so they are a valid part of the document?
>
> Correct. Note however the "low" trust on this check. It's just
> a heads-up. Anyway I remove such reports for RESULTS in wip-lint.
>
>> And an error:
>>
>> I get, in the same document I got the other errors, the following error:
>>
>> ,----
>> | Org linting process starting...
>> | org-split-string: Wrong type argument: stringp, 292
>> `----
>>
>> The backtrace is huge - how can I produce a usable backtrace?
>
> Usually, only the last top-level function call is useful. 
>
> It would be better to provide an ECM, tho. The error comes from Babel
> header check. Does "292" ring a bell in a #+HEADER: line?

Well - now it does. The following produces the error:

--8<---------------cut here---------------start------------->8---
#+PROPERTY: header-args  :tangle-mode (identity #o444)

* Some code

#+begin_src R :results nons

#+end_src
--8<---------------cut here---------------end--------------->8---

The problem is the (identity #o444) which is causing the problem.

Here it is,

Rainer

>
>
> Regards,

-- 
Rainer M. Krug, PhD (Conservation Ecology, SUN), MSc (Conservation Biology, UCT), Dipl. Phys. (Germany)

Centre of Excellence for Invasion Biology
Stellenbosch University
South Africa

Tel :       +33 - (0)9 53 10 27 44
Cell:       +33 - (0)6 85 62 59 98
Fax :       +33 - (0)9 58 10 27 44

Fax (D):    +49 - (0)3 21 21 25 22 44

email:      Rainer@krugs.de

Skype:      RMkrug

PGP: 0x0F52F982

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 494 bytes --]

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [RFC] Org linting library
  2015-05-21 18:30                 ` Rainer M Krug
@ 2015-05-21 21:53                   ` Nicolas Goaziou
  2015-05-22  8:10                     ` Rainer M Krug
  0 siblings, 1 reply; 53+ messages in thread
From: Nicolas Goaziou @ 2015-05-21 21:53 UTC (permalink / raw)
  To: Rainer M Krug; +Cc: emacs-orgmode

Rainer M Krug <Rainer@krugs.de> writes:

> Well - now it does. The following produces the error:
>
> #+PROPERTY: header-args  :tangle-mode (identity #o444)
>
> * Some code
>
> #+begin_src R :results nons
>
> #+end_src
>
> The problem is the (identity #o444) which is causing the problem.

Fixed. Thank you.

Regards,

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [RFC] Org linting library
  2015-05-21 21:53                   ` Nicolas Goaziou
@ 2015-05-22  8:10                     ` Rainer M Krug
  2015-05-22 19:08                       ` Nicolas Goaziou
  0 siblings, 1 reply; 53+ messages in thread
From: Rainer M Krug @ 2015-05-22  8:10 UTC (permalink / raw)
  To: emacs-orgmode

[-- Attachment #1: Type: text/plain, Size: 5355 bytes --]

Nicolas Goaziou <mail@nicolasgoaziou.fr> writes:

> Rainer M Krug <Rainer@krugs.de> writes:
>
>> Well - now it does. The following produces the error:
>>
>> #+PROPERTY: header-args  :tangle-mode (identity #o444)
>>
>> * Some code
>>
>> #+begin_src R :results nons
>>
>> #+end_src
>>
>> The problem is the (identity #o444) which is causing the problem.
>
> Fixed. Thank you.

Thanks - working.

Now there re a few more lints giving wrong warnings:

1) not identifying header argument with +
,----
| #+PROPERTY: header-args+ :tangle no
`----

results in

,----
| 87 high  Unknown header argument ""
`----

2) not knowing header argument "file-ext" from R
,----
| #+begin_src R  :exports results :file-ext pdf :results graphics :width 8 :height 8
| plotSensMainEffAll(files)
| #+end_src
`----

results in

,----
|   1691 high  Unknown header argument "file-ext"
`----

3) I am not to sure about this one, but in LaTeX labels of figures are
with the colon. Initially, I had #+LABEL: instead of #+NAME: but changed
it as one should use #+NAME instead of #+LABEL, but now I get the following:

,----
| #+CAPTION: The caption
| #+NAME: fig:sensDefault
| #+RESULTS: fig_sensDefault
| [[file:./output/fig_sensDefault.pdf]]
`----

results in

,----
|   1686 high  Name "fig:sensDefault" contains a colon; Babel cannot use it as input
`----

I agree with the fact that Babel can not use it as input, but I do not
want to use it as input, only as a label in LaTeX for the figure?

According to the manual this should work:

http://orgmode.org/manual/Images-and-tables.html:

,----
|      #+CAPTION: This is the caption for the next figure link (or table)
|      #+NAME:   fig:SED-HR4049
|      [[./img/a.jpg]]
`----

So maybe check if this is part of a construct with a CAPTION or if it
really is used as an input somewhere?

4) references where :FILE is a normal PROPERTY: (or isn't it?)

,----
| ** Koivusalo2002 - Snow processes in a forest clearing and in a coniferous forest
| :PROPERTIES:
| :TITLE:    Snow processes in a forest clearing and in a coniferous forest
| :BTYPE:    article
| :CUSTOM_ID: Koivusalo2002
| :AUTHOR:   Koivusalo, H. and Kokkonen, T.
| :DOI:      10.1016/S0022-1694(02)00031-8
| :FILE:     file:./Literature/Koivusalo_2002.pdf
| :ISSN:     00221694
| :JOURNAL:  Journal of Hydrology
| :KEYWORDS: energy,forest,mathematical models,melt,snow,uxes
| :MENDELEY-GROUPS: Energy Balance,bibliography
| :MONTH:    may
| :NUMBER:   1-4
| :PAGES:    145--164
| :URL:      http://linkinghub.elsevier.com/retrieve/pii/S0022169402000318
| :VOLUME:   262
| :YEAR:     2002
| :END:
| [[file:Literature/Koivusalo2002.pdf]]
`----

results in

,----
|  11221 high  Special property "FILE" found in a properties drawer
`----

That is all I can see at the moment.


Here is the ECM:

--8<---------------cut here---------------start------------->8---
#+PROPERTY: header-args  :tangle-mode (identity #o444)
#+PROPERTY: header-args  :tangle-mode (identity #o444)
#+PROPERTY: header-args+ :tangle no

* file-ext


#+begin_src R  :exports results :file-ext pdf :results graphics :width 8 :height 8
cat(23)
#+end_src

* And the : in NAME

#+CAPTION: The caption
#+NAME: fig:sensDefault
#+RESULTS: fig_sensDefault
[[file:./output/fig_sensDefault.pdf]]

* References

** Mahat2013 - Testing above- and below-canopy representations of turbulent fluxes in an energy balance snowmelt model
:PROPERTIES:
:TITLE:    Testing above- and below-canopy representations of turbulent fluxes in an energy balance snowmelt model
:BTYPE:    article
:CUSTOM_ID: Mahat2013
:AUTHOR:   Mahat, Vinod and Tarboton, David G. and Molotch, Noah P.
:DOI:      10.1002/wrcr.20073
:FILE:     :Users/rainerkrug/Documents/Mendeley/Files/Mahat, Tarboton, Molotch/Mahat, Tarboton, Molotch\_2013\_Testing above- and below-canopy representations of turbulent fluxes in an energy balance snowmelt model.pdf:pdf
:ISSN:     00431397
:JOURNAL:  Water Resources Research
:MONTH:    feb
:NUMBER:   2
:PAGES:    1107--1122
:URL:      http://doi.wiley.com/10.1002/wrcr.20073
:VOLUME:   49
:YEAR:     2013
:END:
[[file:Literature/Mahat2013.pdf]]
--8<---------------cut here---------------end--------------->8---

which results in
,----
|      3 high  Unknown header argument ""
|      8 high  Unknown header argument "file-ext"
|     15 high  Name "fig:sensDefault" contains a colon; Babel cannot use it as input
|     17 low   Link to non-existent local file "./output/fig_sensDefault.pdf"
|     28 high  Special property "FILE" found in a properties drawer
|     38 low   Link to non-existent local file "Literature/Mahat2013.pdf"
`----

The "Link to non-existent ..." are obvious.

I am really happy with org-lint - thanks a lot. This makes working with
org files much easier.

Thanks,

Rainer

>
> Regards,

-- 
Rainer M. Krug, PhD (Conservation Ecology, SUN), MSc (Conservation Biology, UCT), Dipl. Phys. (Germany)

Centre of Excellence for Invasion Biology
Stellenbosch University
South Africa

Tel :       +33 - (0)9 53 10 27 44
Cell:       +33 - (0)6 85 62 59 98
Fax :       +33 - (0)9 58 10 27 44

Fax (D):    +49 - (0)3 21 21 25 22 44

email:      Rainer@krugs.de

Skype:      RMkrug

PGP: 0x0F52F982

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 494 bytes --]

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [RFC] Org linting library
  2015-05-22  8:10                     ` Rainer M Krug
@ 2015-05-22 19:08                       ` Nicolas Goaziou
  2015-05-23 12:00                         ` Rainer M Krug
  0 siblings, 1 reply; 53+ messages in thread
From: Nicolas Goaziou @ 2015-05-22 19:08 UTC (permalink / raw)
  To: Rainer M Krug; +Cc: emacs-orgmode

Rainer M Krug <Rainer@krugs.de> writes:

> 1) not identifying header argument with +
> ,----
> | #+PROPERTY: header-args+ :tangle no
> `----
>
> results in
>
> ,----
> | 87 high  Unknown header argument ""
> `----

This should be fixed.

> 2) not knowing header argument "file-ext" from R
> ,----
> | #+begin_src R  :exports results :file-ext pdf :results graphics :width 8 :height 8
> | plotSensMainEffAll(files)
> | #+end_src
> `----
>
> results in
>
> ,----
> |   1691 high  Unknown header argument "file-ext"
> `----

Fixed too.

> 3) I am not to sure about this one, but in LaTeX labels of figures are
> with the colon. Initially, I had #+LABEL: instead of #+NAME: but changed
> it as one should use #+NAME instead of #+LABEL, but now I get the following:
>
> ,----
> | #+CAPTION: The caption
> | #+NAME: fig:sensDefault
> | #+RESULTS: fig_sensDefault
> | [[file:./output/fig_sensDefault.pdf]]
> `----
>
> results in
>
> ,----
> |   1686 high  Name "fig:sensDefault" contains a colon; Babel cannot use it as input
> `----
>
> I agree with the fact that Babel can not use it as input, but I do not
> want to use it as input, only as a label in LaTeX for the figure?

Then you can ignore safely this report.

> According to the manual this should work:
>
> http://orgmode.org/manual/Images-and-tables.html:
>
> ,----
> |      #+CAPTION: This is the caption for the next figure link (or table)
> |      #+NAME:   fig:SED-HR4049
> |      [[./img/a.jpg]]
> `----
>
> So maybe check if this is part of a construct with a CAPTION or if it
> really is used as an input somewhere?

It is quite complicated to check if it is an input somewhere, e.g., it
could be used as an input in another document.

> 4) references where :FILE is a normal PROPERTY: (or isn't it?)
>
> ,----
> | ** Koivusalo2002 - Snow processes in a forest clearing and in a coniferous forest
> | :PROPERTIES:
> | :TITLE:    Snow processes in a forest clearing and in a coniferous forest
> | :BTYPE:    article
> | :CUSTOM_ID: Koivusalo2002
> | :AUTHOR:   Koivusalo, H. and Kokkonen, T.
> | :DOI:      10.1016/S0022-1694(02)00031-8
> | :FILE:     file:./Literature/Koivusalo_2002.pdf
> | :ISSN:     00221694
> | :JOURNAL:  Journal of Hydrology
> | :KEYWORDS: energy,forest,mathematical models,melt,snow,uxes
> | :MENDELEY-GROUPS: Energy Balance,bibliography
> | :MONTH:    may
> | :NUMBER:   1-4
> | :PAGES:    145--164
> | :URL:      http://linkinghub.elsevier.com/retrieve/pii/S0022169402000318
> | :VOLUME:   262
> | :YEAR:     2002
> | :END:
> | [[file:Literature/Koivusalo2002.pdf]]
> `----
>
> results in
>
> ,----
> |  11221 high  Special property "FILE" found in a properties drawer
> `----

"FILE" is a special property, i.e., it shouldn't be set in a property
drawer. See (info "(org) Special properties").

> I am really happy with org-lint - thanks a lot. This makes working with
> org files much easier.

Thanks for all the feedback.


Regards,

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [RFC] Org linting library
  2015-05-22 19:08                       ` Nicolas Goaziou
@ 2015-05-23 12:00                         ` Rainer M Krug
  2015-05-24 15:19                           ` Nicolas Goaziou
  0 siblings, 1 reply; 53+ messages in thread
From: Rainer M Krug @ 2015-05-23 12:00 UTC (permalink / raw)
  To: emacs-orgmode

[-- Attachment #1: Type: text/plain, Size: 4332 bytes --]

Nicolas Goaziou <mail@nicolasgoaziou.fr> writes:

> Rainer M Krug <Rainer@krugs.de> writes:
>
>> 1) not identifying header argument with +
>> ,----
>> | #+PROPERTY: header-args+ :tangle no
>> `----
>>
>> results in
>>
>> ,----
>> | 87 high  Unknown header argument ""
>> `----
>
> This should be fixed.

Thanks.

>
>> 2) not knowing header argument "file-ext" from R
>> ,----
>> | #+begin_src R  :exports results :file-ext pdf :results graphics :width 8 :height 8
>> | plotSensMainEffAll(files)
>> | #+end_src
>> `----
>>
>> results in
>>
>> ,----
>> |   1691 high  Unknown header argument "file-ext"
>> `----
>
> Fixed too.

Perfect - thanks.

>
>> 3) I am not to sure about this one, but in LaTeX labels of figures are
>> with the colon. Initially, I had #+LABEL: instead of #+NAME: but changed
>> it as one should use #+NAME instead of #+LABEL, but now I get the following:
>>
>> ,----
>> | #+CAPTION: The caption
>> | #+NAME: fig:sensDefault
>> | #+RESULTS: fig_sensDefault
>> | [[file:./output/fig_sensDefault.pdf]]
>> `----
>>
>> results in
>>
>> ,----
>> |   1686 high  Name "fig:sensDefault" contains a colon; Babel cannot use it as input
>> `----
>>
>> I agree with the fact that Babel can not use it as input, but I do not
>> want to use it as input, only as a label in LaTeX for the figure?
>
> Then you can ignore safely this report.

Concerning ignoring: it would be nice to dynamically exclude certain
types of messages - i.e. hiding them from the results of the
linting. I don't think this is possible at
the moment?

I have literally about 100 names with a colon - so these messages are
overshadowing the really important ones.

>
>> According to the manual this should work:
>>
>> http://orgmode.org/manual/Images-and-tables.html:
>>
>> ,----
>> |      #+CAPTION: This is the caption for the next figure link (or table)
>> |      #+NAME:   fig:SED-HR4049
>> |      [[./img/a.jpg]]
>> `----
>>
>> So maybe check if this is part of a construct with a CAPTION or if it
>> really is used as an input somewhere?
>
> It is quite complicated to check if it is an input somewhere, e.g., it
> could be used as an input in another document.

True - haven't considered that.

>
>> 4) references where :FILE is a normal PROPERTY: (or isn't it?)
>>
>> ,----
>> | ** Koivusalo2002 - Snow processes in a forest clearing and in a coniferous forest
>> | :PROPERTIES:
>> | :TITLE:    Snow processes in a forest clearing and in a coniferous forest
>> | :BTYPE:    article
>> | :CUSTOM_ID: Koivusalo2002
>> | :AUTHOR:   Koivusalo, H. and Kokkonen, T.
>> | :DOI:      10.1016/S0022-1694(02)00031-8
>> | :FILE:     file:./Literature/Koivusalo_2002.pdf
>> | :ISSN:     00221694
>> | :JOURNAL:  Journal of Hydrology
>> | :KEYWORDS: energy,forest,mathematical models,melt,snow,uxes
>> | :MENDELEY-GROUPS: Energy Balance,bibliography
>> | :MONTH:    may
>> | :NUMBER:   1-4
>> | :PAGES:    145--164
>> | :URL:      http://linkinghub.elsevier.com/retrieve/pii/S0022169402000318
>> | :VOLUME:   262
>> | :YEAR:     2002
>> | :END:
>> | [[file:Literature/Koivusalo2002.pdf]]
>> `----
>>
>> results in
>>
>> ,----
>> |  11221 high  Special property "FILE" found in a properties drawer
>> `----
>
> "FILE" is a special property, i.e., it shouldn't be set in a property
> drawer. See (info "(org) Special properties").

OK - I'll throw it put then.

>
>> I am really happy with org-lint - thanks a lot. This makes working with
>> org files much easier.
>
> Thanks for all the feedback.

Pleasure - I found several undetected problems in my org file by using
the linting library.

Another question: When duplicate names are detected, would it be
possible to specify both (or more) line numbers in the warning? THis
would make finding them much easier.

Cheers,

Rainer

>
>
> Regards,

-- 
Rainer M. Krug, PhD (Conservation Ecology, SUN), MSc (Conservation Biology, UCT), Dipl. Phys. (Germany)

Centre of Excellence for Invasion Biology
Stellenbosch University
South Africa

Tel :       +33 - (0)9 53 10 27 44
Cell:       +33 - (0)6 85 62 59 98
Fax :       +33 - (0)9 58 10 27 44

Fax (D):    +49 - (0)3 21 21 25 22 44

email:      Rainer@krugs.de

Skype:      RMkrug

PGP: 0x0F52F982

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 494 bytes --]

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [RFC] Org linting library
  2015-05-23 12:00                         ` Rainer M Krug
@ 2015-05-24 15:19                           ` Nicolas Goaziou
  2015-05-24 15:52                             ` Rainer M Krug
  0 siblings, 1 reply; 53+ messages in thread
From: Nicolas Goaziou @ 2015-05-24 15:19 UTC (permalink / raw)
  To: Rainer M Krug; +Cc: emacs-orgmode

Rainer M Krug <Rainer@krugs.de> writes:

> Concerning ignoring: it would be nice to dynamically exclude certain
> types of messages - i.e. hiding them from the results of the
> linting. I don't think this is possible at
> the moment?
>
> I have literally about 100 names with a colon - so these messages are
> overshadowing the really important ones.

Done. Now, "h" hides reports from checker and point. "i" ignores them
altogether, which means they do not appear on subsequent refreshes.

> Another question: When duplicate names are detected, would it be
> possible to specify both (or more) line numbers in the warning? THis
> would make finding them much easier.

Done.

Regards,

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [RFC] Org linting library
  2015-05-24 15:19                           ` Nicolas Goaziou
@ 2015-05-24 15:52                             ` Rainer M Krug
  2015-05-27  9:31                               ` Andreas Leha
  0 siblings, 1 reply; 53+ messages in thread
From: Rainer M Krug @ 2015-05-24 15:52 UTC (permalink / raw)
  To: emacs-orgmode

[-- Attachment #1: Type: text/plain, Size: 1313 bytes --]

Nicolas Goaziou <mail@nicolasgoaziou.fr> writes:

> Rainer M Krug <Rainer@krugs.de> writes:
>
>> Concerning ignoring: it would be nice to dynamically exclude certain
>> types of messages - i.e. hiding them from the results of the
>> linting. I don't think this is possible at
>> the moment?
>>
>> I have literally about 100 names with a colon - so these messages are
>> overshadowing the really important ones.
>
> Done. Now, "h" hides reports from checker and point. "i" ignores them
> altogether, which means they do not appear on subsequent refreshes.

Sounds perfect - am really looking forward to trying it out on Tuesday.

>
>> Another question: When duplicate names are detected, would it be
>> possible to specify both (or more) line numbers in the warning? THis
>> would make finding them much easier.
>
> Done.

Perfect.

Cheers,

Rainer

>
> Regards,

-- 
Rainer M. Krug, PhD (Conservation Ecology, SUN), MSc (Conservation Biology, UCT), Dipl. Phys. (Germany)

Centre of Excellence for Invasion Biology
Stellenbosch University
South Africa

Tel :       +33 - (0)9 53 10 27 44
Cell:       +33 - (0)6 85 62 59 98
Fax :       +33 - (0)9 58 10 27 44

Fax (D):    +49 - (0)3 21 21 25 22 44

email:      Rainer@krugs.de

Skype:      RMkrug

PGP: 0x0F52F982

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 494 bytes --]

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [RFC] Org linting library
  2015-05-24 15:52                             ` Rainer M Krug
@ 2015-05-27  9:31                               ` Andreas Leha
  2015-05-27 10:49                                 ` Nicolas Goaziou
  0 siblings, 1 reply; 53+ messages in thread
From: Andreas Leha @ 2015-05-27  9:31 UTC (permalink / raw)
  To: emacs-orgmode

Hi Nicolas,

some more spurious warnings I get:


1. Captions with short version:

,----
| 1105 low   Possible missing colon in keyword "#+caption[Data"
`----

2. header arguments from ob-latex:

,----
|    451 high  Unknown header argument "packages"
|    451 high  Unknown header argument "imoutoptions"
|    451 high  Unknown header argument "iminoptions"
|    451 high  Unknown header argument "imagemagick"
|    451 high  Unknown header argument "fit"
`----


Best,
Andreas

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [RFC] Org linting library
  2015-05-27  9:31                               ` Andreas Leha
@ 2015-05-27 10:49                                 ` Nicolas Goaziou
  0 siblings, 0 replies; 53+ messages in thread
From: Nicolas Goaziou @ 2015-05-27 10:49 UTC (permalink / raw)
  To: Andreas Leha; +Cc: emacs-orgmode

Hello,

Andreas Leha <andreas.leha@med.uni-goettingen.de> writes:

> some more spurious warnings I get:
>
>
> 1. Captions with short version:
>
> ,----
> | 1105 low   Possible missing colon in keyword "#+caption[Data"
> `----
>
> 2. header arguments from ob-latex:
>
> ,----
> |    451 high  Unknown header argument "packages"
> |    451 high  Unknown header argument "imoutoptions"
> |    451 high  Unknown header argument "iminoptions"
> |    451 high  Unknown header argument "imagemagick"
> |    451 high  Unknown header argument "fit"
> `----

Fixed. Thank you.


Regards,

-- 
Nicolas Goaziou

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [RFC] Org linting library
  2015-04-19 13:31 [RFC] Org linting library Nicolas Goaziou
                   ` (6 preceding siblings ...)
  2015-05-19 13:32 ` Rainer M Krug
@ 2015-06-05 10:51 ` Rainer M Krug
  2015-06-05 21:40   ` Nicolas Goaziou
  7 siblings, 1 reply; 53+ messages in thread
From: Rainer M Krug @ 2015-06-05 10:51 UTC (permalink / raw)
  To: Org Mode List

[-- Attachment #1: Type: text/plain, Size: 3703 bytes --]

One more observation:

The link

,----
| [[file:__BOX__/Aerodynamic%20resistances.docx][file:./__BOX__/Aerodynamic resistances.docx]]
`----

causes the following warnings:

,----
|   9094 low   Link to non-existent local file "__BOX__/Aerodynamic%20resistances.docx"
|   9094 low   Link to non-existent local file "./__BOX__/Aerodynamic"
`----

although the file exists and the link works in org.

Cheers,

Rainer


Nicolas Goaziou <mail@nicolasgoaziou.fr> writes:

> Hello,
>
> The following library implements linting for Org syntax. The sole public
> function is `org-lint', which see.
>
> Internally, the library defines a new structure: `org-lint-checker',
> with the following slots:
>
>   - NAME: Unique check identifier, as a symbol. The check is done
>     calling the function `org-lint-NAME' with one mandatory argument,
>     the parse tree describing the current Org buffer. Such function
>     calls are wrapped within a `save-excursion' and point is always at
>     `point-min'. Its return value has to be an alist (POSITION MESSAGE)
>     when POSITION refer to the buffer position of the error, as an
>     integer, and MESSAGE is a strings describing the error.
>
>   - DESCRIPTION: Summary about the check, as a string.
>
>   - CATEGORIES: Categories relative to the check, as a list of symbol.
>     They are used for filtering when calling `org-lint'. Checkers not
>     explicitly associated to a category are collected in the `default'
>     one.
>
>   - TRUST: The trust level one can have in the check. It is either `low'
>     or `high', depending on the heuristics implemented and the nature of
>     the check. This has an indicative value only and is displayed along
>     reports.
>
> All checks have to be listed in `org-lint--checkers'.
>
> Results are displayed in a special "*Org Lint*" buffer with a dedicated
> major mode, derived from `tabulated-list-mode'. In addition to the usual
> key-bindings inherited from it, "C-j" displays problematic line reported
> under point and "RET" jumps to it.
>
> Checks currently implemented are:
>
>   - duplicates CUSTOM_ID properties
>   - duplicate NAME values
>   - duplicate targets
>   - duplicate footnote definitions
>   - orphaned affiliated keywords
>   - obsolete affiliated keywords
>   - missing language in src blocks
>   - NAME values with a colon
>   - wrong header arguments in src blocks
>   - misuse of CATEGORY keyword
>   - "coderef" links with unknown destination
>   - "custom-id" links with unknown destination
>   - "fuzzy" links with unknown destination
>   - "id" links with unknown destination
>   - links to non-existent local files
>   - special properties in properties drawer
>   - obsolete syntax for PROPERTIES drawers
>   - missing definition for footnote references
>   - missing reference for footnote definitions
>   - non-footnote definitions in footnote section
>   - probable invalid keywords
>   - invalid blocks
>   - probable incomplete drawers
>   - obsolete QUOTE section
>
> Since it relies on lexical binding, `pcase' and `string-prefix-p', it
> cannot be added to Org 8.3, but can make it into Org 8.4, if deemed
> useful enough.
>
> Feedback welcome.
>
>
> Regards,

-- 
Rainer M. Krug, PhD (Conservation Ecology, SUN), MSc (Conservation Biology, UCT), Dipl. Phys. (Germany)

Centre of Excellence for Invasion Biology
Stellenbosch University
South Africa

Tel :       +33 - (0)9 53 10 27 44
Cell:       +33 - (0)6 85 62 59 98
Fax :       +33 - (0)9 58 10 27 44

Fax (D):    +49 - (0)3 21 21 25 22 44

email:      Rainer@krugs.de

Skype:      RMkrug

PGP: 0x0F52F982

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 494 bytes --]

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [RFC] Org linting library
  2015-06-05 10:51 ` Rainer M Krug
@ 2015-06-05 21:40   ` Nicolas Goaziou
  2015-06-06 14:44     ` Rainer M Krug
  0 siblings, 1 reply; 53+ messages in thread
From: Nicolas Goaziou @ 2015-06-05 21:40 UTC (permalink / raw)
  To: Rainer M Krug; +Cc: Org Mode List

Rainer M Krug <Rainer@krugs.de> writes:

> The link
>
> ,----
> | [[file:__BOX__/Aerodynamic%20resistances.docx][file:./__BOX__/Aerodynamic resistances.docx]]
> `----
>
> causes the following warnings:
>
> ,----
> |   9094 low   Link to non-existent local file "__BOX__/Aerodynamic%20resistances.docx"

Fixed. Thank you.

> |   9094 low   Link to non-existent local file "./__BOX__/Aerodynamic"
> `----
>
> although the file exists and the link works in org.

Here, Org Lint is correct. Link's description is really a link to
a file, "./__BOX__/Aerodynamic", followed by a space, and
"resistances.docx" text.


Regards,

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [RFC] Org linting library
  2015-06-05 21:40   ` Nicolas Goaziou
@ 2015-06-06 14:44     ` Rainer M Krug
  2015-06-06 23:22       ` Nicolas Goaziou
  0 siblings, 1 reply; 53+ messages in thread
From: Rainer M Krug @ 2015-06-06 14:44 UTC (permalink / raw)
  To: Org Mode List

[-- Attachment #1: Type: text/plain, Size: 1273 bytes --]

Nicolas Goaziou <mail@nicolasgoaziou.fr> writes:

> Rainer M Krug <Rainer@krugs.de> writes:
>
>> The link
>>
>> ,----
>> | [[file:__BOX__/Aerodynamic%20resistances.docx][file:./__BOX__/Aerodynamic resistances.docx]]
>> `----
>>
>> causes the following warnings:
>>
>> ,----
>> |   9094 low   Link to non-existent local file "__BOX__/Aerodynamic%20resistances.docx"
>
> Fixed. Thank you.

Thanks.

>
>> |   9094 low   Link to non-existent local file "./__BOX__/Aerodynamic"
>> `----
>>
>> although the file exists and the link works in org.
>
> Here, Org Lint is correct. Link's description is really a link to
> a file, "./__BOX__/Aerodynamic", followed by a space, and
> "resistances.docx" text.

But it is in the description part - shouldn't this be considered "normal
text" whatever it is?

Rainer

>
>
> Regards,

-- 
Rainer M. Krug, PhD (Conservation Ecology, SUN), MSc (Conservation Biology, UCT), Dipl. Phys. (Germany)

Centre of Excellence for Invasion Biology
Stellenbosch University
South Africa

Tel :       +33 - (0)9 53 10 27 44
Cell:       +33 - (0)6 85 62 59 98
Fax :       +33 - (0)9 58 10 27 44

Fax (D):    +49 - (0)3 21 21 25 22 44

email:      Rainer@krugs.de

Skype:      RMkrug

PGP: 0x0F52F982

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 494 bytes --]

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [RFC] Org linting library
  2015-06-06 14:44     ` Rainer M Krug
@ 2015-06-06 23:22       ` Nicolas Goaziou
  2015-06-08  7:50         ` Rainer M Krug
  0 siblings, 1 reply; 53+ messages in thread
From: Nicolas Goaziou @ 2015-06-06 23:22 UTC (permalink / raw)
  To: Rainer M Krug; +Cc: Org Mode List

Rainer M Krug <Rainer@krugs.de> writes:

>> Here, Org Lint is correct. Link's description is really a link to
>> a file, "./__BOX__/Aerodynamic", followed by a space, and
>> "resistances.docx" text.
>
> But it is in the description part - shouldn't this be considered "normal
> text" whatever it is?

I wish it would. Unfortunately, Org has no proper image syntax, so we
have to support plain links (i.e, no brackets) in descriptions to get
image links.


Regards,

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [RFC] Org linting library
  2015-06-06 23:22       ` Nicolas Goaziou
@ 2015-06-08  7:50         ` Rainer M Krug
  2015-06-09  7:30           ` Nicolas Goaziou
  0 siblings, 1 reply; 53+ messages in thread
From: Rainer M Krug @ 2015-06-08  7:50 UTC (permalink / raw)
  To: Org Mode List

[-- Attachment #1: Type: text/plain, Size: 1300 bytes --]

Nicolas Goaziou <mail@nicolasgoaziou.fr> writes:

> Rainer M Krug <Rainer@krugs.de> writes:
>
>>> Here, Org Lint is correct. Link's description is really a link to
>>> a file, "./__BOX__/Aerodynamic", followed by a space, and
>>> "resistances.docx" text.
>>
>> But it is in the description part - shouldn't this be considered "normal
>> text" whatever it is?
>
> I wish it would. Unfortunately, Org has no proper image syntax, so we
> have to support plain links (i.e, no brackets) in descriptions to get
> image links.

OK - now I get it. The syntax implies that I want to display the image
located there as the descriptor.

Would it be possible for org-lint to give a more direct warning that this
link is in the description and not the link itself?

Thanks for your patience - I am learning a lot about org by using org-lint!

Rainer

>
>
> Regards,

-- 
Rainer M. Krug, PhD (Conservation Ecology, SUN), MSc (Conservation Biology, UCT), Dipl. Phys. (Germany)

Centre of Excellence for Invasion Biology
Stellenbosch University
South Africa

Tel :       +33 - (0)9 53 10 27 44
Cell:       +33 - (0)6 85 62 59 98
Fax :       +33 - (0)9 58 10 27 44

Fax (D):    +49 - (0)3 21 21 25 22 44

email:      Rainer@krugs.de

Skype:      RMkrug

PGP: 0x0F52F982

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 494 bytes --]

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [RFC] Org linting library
  2015-06-08  7:50         ` Rainer M Krug
@ 2015-06-09  7:30           ` Nicolas Goaziou
  2015-06-09  7:50             ` Rainer M Krug
  0 siblings, 1 reply; 53+ messages in thread
From: Nicolas Goaziou @ 2015-06-09  7:30 UTC (permalink / raw)
  To: Rainer M Krug; +Cc: Org Mode List

Rainer M Krug <Rainer@krugs.de> writes:

> OK - now I get it. The syntax implies that I want to display the image
> located there as the descriptor.

Correct.

> Would it be possible for org-lint to give a more direct warning that this
> link is in the description and not the link itself?

Could you suggest an appropriate warning?

Thank you.


Regards,

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [RFC] Org linting library
  2015-06-09  7:30           ` Nicolas Goaziou
@ 2015-06-09  7:50             ` Rainer M Krug
  2015-06-16 21:54               ` Nicolas Goaziou
  0 siblings, 1 reply; 53+ messages in thread
From: Rainer M Krug @ 2015-06-09  7:50 UTC (permalink / raw)
  To: Org Mode List

[-- Attachment #1: Type: text/plain, Size: 1173 bytes --]

Nicolas Goaziou <mail@nicolasgoaziou.fr> writes:

> Rainer M Krug <Rainer@krugs.de> writes:
>
>> OK - now I get it. The syntax implies that I want to display the image
>> located there as the descriptor.
>
> Correct.
>
>> Would it be possible for org-lint to give a more direct warning that this
>> link is in the description and not the link itself?
>
> Could you suggest an appropriate warning?

something along the line of "Link to non-existent local file "..." in
the description part" would make clear that it is ion the description of
a link.

And if the non-existent link is in the link part, the other could be
"Link to non-existent local file "..." in the link part"

Cheers,

Rainer

>
> Thank you.
>
>
> Regards,

-- 
Rainer M. Krug, PhD (Conservation Ecology, SUN), MSc (Conservation Biology, UCT), Dipl. Phys. (Germany)

Centre of Excellence for Invasion Biology
Stellenbosch University
South Africa

Tel :       +33 - (0)9 53 10 27 44
Cell:       +33 - (0)6 85 62 59 98
Fax :       +33 - (0)9 58 10 27 44

Fax (D):    +49 - (0)3 21 21 25 22 44

email:      Rainer@krugs.de

Skype:      RMkrug

PGP: 0x0F52F982

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 494 bytes --]

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [RFC] Org linting library
  2015-06-09  7:50             ` Rainer M Krug
@ 2015-06-16 21:54               ` Nicolas Goaziou
  0 siblings, 0 replies; 53+ messages in thread
From: Nicolas Goaziou @ 2015-06-16 21:54 UTC (permalink / raw)
  To: Rainer M Krug; +Cc: Org Mode List

Rainer M Krug <Rainer@krugs.de> writes:

> something along the line of "Link to non-existent local file "..." in
> the description part" would make clear that it is ion the description of
> a link.

Done. Thanks.

Regards,

^ permalink raw reply	[flat|nested] 53+ messages in thread

end of thread, other threads:[~2015-06-16 21:53 UTC | newest]

Thread overview: 53+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2015-04-19 13:31 [RFC] Org linting library Nicolas Goaziou
2015-04-19 14:23 ` Rasmus
2015-04-19 16:24   ` Nicolas Goaziou
2015-04-19 19:32 ` Marco Wahl
2015-04-20 10:42   ` Nicolas Goaziou
2015-04-20  2:15 ` Charles C. Berry
2015-04-20  9:12   ` Nicolas Goaziou
2015-04-22 19:27 ` Nicolas Goaziou
2015-04-22 19:31   ` Sebastien Vauban
2015-04-22 19:40     ` Nicolas Goaziou
2015-04-23 10:47   ` Eric Abrahamsen
2015-04-23 16:25   ` Sebastien Vauban
2015-04-26 12:50     ` Nicolas Goaziou
2015-04-27 15:22 ` Doug Lewan
2015-05-19 11:54 ` Andreas Leha
2015-05-19 13:39   ` Nicolas Goaziou
2015-05-19 14:54     ` Andreas Leha
2015-05-19 19:09       ` Nicolas Goaziou
2015-05-19 20:02         ` Andreas Leha
2015-05-19 21:03           ` Nicolas Goaziou
2015-05-19 21:10             ` Andreas Leha
2015-05-19 21:26               ` Nicolas Goaziou
2015-05-19 21:35                 ` Andreas Leha
2015-05-19 13:32 ` Rainer M Krug
2015-05-19 13:43   ` Andreas Leha
2015-05-20 15:01     ` Rainer M Krug
2015-05-20 15:08       ` Rainer M Krug
2015-05-20 15:24         ` Rainer M Krug
2015-05-20 20:42           ` Andreas Leha
2015-05-20 21:15             ` Rainer M Krug
2015-05-20 21:46           ` Nicolas Goaziou
2015-05-21  8:40             ` Rainer M Krug
2015-05-21 16:24               ` Nicolas Goaziou
2015-05-21 18:18                 ` Rainer M Krug
2015-05-21 18:23                 ` Rainer M Krug
2015-05-21 18:30                 ` Rainer M Krug
2015-05-21 21:53                   ` Nicolas Goaziou
2015-05-22  8:10                     ` Rainer M Krug
2015-05-22 19:08                       ` Nicolas Goaziou
2015-05-23 12:00                         ` Rainer M Krug
2015-05-24 15:19                           ` Nicolas Goaziou
2015-05-24 15:52                             ` Rainer M Krug
2015-05-27  9:31                               ` Andreas Leha
2015-05-27 10:49                                 ` Nicolas Goaziou
2015-05-19 13:47   ` Nicolas Goaziou
2015-06-05 10:51 ` Rainer M Krug
2015-06-05 21:40   ` Nicolas Goaziou
2015-06-06 14:44     ` Rainer M Krug
2015-06-06 23:22       ` Nicolas Goaziou
2015-06-08  7:50         ` Rainer M Krug
2015-06-09  7:30           ` Nicolas Goaziou
2015-06-09  7:50             ` Rainer M Krug
2015-06-16 21:54               ` Nicolas Goaziou

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs/org-mode.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).