aboutsummaryrefslogtreecommitdiff
path: root/.ai/scripts/lint-org.el
diff options
context:
space:
mode:
Diffstat (limited to '.ai/scripts/lint-org.el')
-rw-r--r--.ai/scripts/lint-org.el60
1 files changed, 60 insertions, 0 deletions
diff --git a/.ai/scripts/lint-org.el b/.ai/scripts/lint-org.el
index 85886af..5d47644 100644
--- a/.ai/scripts/lint-org.el
+++ b/.ai/scripts/lint-org.el
@@ -42,6 +42,7 @@
(require 'org-lint)
(require 'cl-lib)
(require 'subr-x)
+(require 'wrap-org-table) ; render-width + table parsing for the table check
(defvar lo-fixes 0
"Count of mechanical fixes applied (or would-apply in --check) on the last file.")
@@ -285,6 +286,62 @@ Craig-specific annotation marker rather than Babel src-block syntax."
(lo--emit-judgment name line msg)))))
;;; ---------------------------------------------------------------------------
+;;; org-table-standard check (claude-rules/org-tables.md)
+;;
+;; Not an org-lint checker — a custom scan run alongside the org-lint pass.
+;; Violations surface as judgment items (checker `org-table-standard'), never
+;; auto-fixed: reflowing a table is a visible layout change that
+;; wrap-org-table.el performs on request, not something a lint sweep does
+;; silently.
+
+(defun lo--table-violations (lines)
+ "Standard violations for the table given as LINES, as message strings.
+Width is render-measured (links count as their labels, per wot-render-width).
+Rules: an hline must follow the header and every logical data row, closing
+rule included; continuation lines inside a rule-delimited group are one
+logical row, matching wrap-org-table.el's grouping."
+ (let ((violations nil)
+ (max-width (apply #'max (mapcar #'wot-render-width lines))))
+ (when (> max-width wot-default-budget)
+ (push (format "renders %d wide (budget %d)" max-width wot-default-budget)
+ violations))
+ (let* ((parsed (mapcar #'wot--parse-row lines))
+ (header-p (and (listp (car parsed)) (eq (cadr parsed) 'hline)))
+ (data (if header-p (cddr parsed) parsed)))
+ (when (and (cl-some #'listp data)
+ (not (eq (car (last data)) 'hline)))
+ (push "no closing rule" violations))
+ (let ((group nil))
+ (cl-loop for e in data
+ if (eq e 'hline) do (setq group nil)
+ else do (push e group)
+ when (and (> (length group) 1)
+ (not (wot--continuation-group-p (reverse group))))
+ return (push "missing rule between rows" violations))))
+ (nreverse violations)))
+
+(defun lo--check-tables ()
+ "Scan the current buffer for org tables violating the table standard.
+Emits one judgment item per violating table."
+ (save-excursion
+ (goto-char (point-min))
+ (while (re-search-forward "^[ \t]*|" nil t)
+ (let ((start-line (line-number-at-pos))
+ (lines nil))
+ (beginning-of-line)
+ (while (and (not (eobp)) (looking-at "[ \t]*|"))
+ (push (buffer-substring-no-properties (line-beginning-position)
+ (line-end-position))
+ lines)
+ (forward-line 1))
+ (let ((violations (lo--table-violations (nreverse lines))))
+ (when violations
+ (lo--emit-judgment
+ 'org-table-standard start-line
+ (format "table violates the org-table standard: %s — wrap-org-table.el reflows it"
+ (string-join violations "; ")))))))))
+
+;;; ---------------------------------------------------------------------------
;;; File processing
(defun lo--backup (file)
@@ -314,6 +371,9 @@ left unmodified and mechanical entries are recorded with :preview t."
(lambda (a b) (> (lo--line a) (lo--line b))))))
(dolist (item sorted)
(lo--handle-item item)))
+ ;; After org-lint items: the custom table-standard scan. Runs on the
+ ;; post-fix buffer; judgment-only, so order doesn't perturb fixes.
+ (lo--check-tables)
(when (and (not lo-check-only) (buffer-modified-p))
(save-buffer)))
(with-current-buffer buf (set-buffer-modified-p nil))