branch: elpa/flycheck
commit b254cd0a5ea0df644ce3a7b1a8c3c92d8f96c775
Author: takeokunn <[email protected]>
Commit: takeokunn <[email protected]>
fix(org-lint): run org-lint in current Emacs process
Rewrite the org-lint checker from a command-based subprocess checker
(emacs -Q --batch) to an in-process generic checker using
flycheck-define-generic-checker. The subprocess approach lacks
external packages, causing false "Unknown source block language"
warnings for languages like nix-mode. Running in-process gives
org-lint access to all installed packages, eliminating the issue at
its root.
Fixes #2144
---
CHANGES.rst | 3 ++
doc/languages.rst | 18 +++--------
flycheck.el | 92 ++++++++++++++++++++-----------------------------------
3 files changed, 41 insertions(+), 72 deletions(-)
diff --git a/CHANGES.rst b/CHANGES.rst
index e247dc35eb..2f8730959a 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -17,6 +17,9 @@ Bugs fixed
- [#2086]: Fix the name of the PyMarkdown config.
- [#2036]: Fix ``awk-gawk`` checker passing spurious quotes to ``gawk
--source``.
+- [#2144]: Rewrite ``org-lint`` checker to run in the current Emacs process
+ instead of a ``-Q --batch`` subprocess. This eliminates false "Unknown source
+ block language" warnings for languages from external packages.
35.0 (2025-04-23)
======================
diff --git a/doc/languages.rst b/doc/languages.rst
index bc694df12e..17714fc13a 100644
--- a/doc/languages.rst
+++ b/doc/languages.rst
@@ -924,8 +924,8 @@ to view the docstring of the syntax checker. Likewise, you
may use
An Org mode syntax and style checker using ``org-lint``.
- The checker runs ``org-lint`` in an Emacs subprocess to detect issues
such
- as:
+ The checker runs ``org-lint`` in the current Emacs process to detect
+ issues such as:
- Invalid links
- Dead links
@@ -934,11 +934,9 @@ to view the docstring of the syntax checker. Likewise,
you may use
- Special characters in links
- And more...
- The checker automatically inherits your Org mode configuration, including
- ``org-directory`` and ``org-id-locations-file``. Note that ``load-path``
is
- not inherited for security reasons to prevent potential code injection.
- Org should be installed in a standard location that Emacs can find
without
- a custom ``load-path``.
+ Because the checker runs in the current Emacs process, it has access to
+ all installed packages and user configuration, avoiding false positives
+ for source block languages provided by external packages.
The checker is enabled by default when ``org-lint`` is available (Org
mode
9.0 or later).
@@ -946,12 +944,6 @@ to view the docstring of the syntax checker. Likewise,
you may use
See the ``org-lint`` documentation in Org mode for details about the
checks
performed.
- .. note::
-
- The checker does not inherit ``org-id-locations`` because this
variable
- can contain thousands of entries and exceed shell argument limits. The
- ``org-id-locations-file`` is used instead.
-
.. supported-language:: Opam
.. syntax-checker:: opam
diff --git a/flycheck.el b/flycheck.el
index 135c2ad14a..37bd8acc31 100644
--- a/flycheck.el
+++ b/flycheck.el
@@ -91,6 +91,7 @@
;; Tell the byte compiler about autoloaded functions from packages
(declare-function pkg-info-version-info "pkg-info" (package))
+(declare-function org-lint "org-lint" (&optional arg))
;;; Customization
@@ -8970,23 +8971,10 @@ Variables are taken from
`flycheck-emacs-lisp-checkdoc-variables'."
,@(seq-map (lambda (opt) `(setq-default ,opt ',(symbol-value opt)))
(seq-filter #'boundp flycheck-emacs-lisp-checkdoc-variables))))
-(defconst flycheck-org-lint-variables
- '(org-directory
- org-id-locations-file) ; File path only, not contents
- "Variables inherited by the org-lint subprocess.
-
-Note: We do NOT include `load-path' to prevent potential code injection
-and information disclosure. Org should be installed in a standard
-location that Emacs can find without a custom load-path.
-
-We also do not include `org-id-locations' because it can contain
-thousands of entries and exceed shell argument limits (ARG_MAX).")
-
-(defun flycheck-org-lint-variables-form ()
- "Make a sexp to pass relevant variables to an org-lint subprocess."
- `(progn
- ,@(seq-map (lambda (opt) `(setq-default ,opt ',(symbol-value opt)))
- (seq-filter #'boundp flycheck-org-lint-variables))))
+(defun flycheck-org-lint-available-p ()
+ "Check if org-lint is available."
+ (and (fboundp 'org-lint)
+ (require 'org nil 'no-error)))
(flycheck-define-checker emacs-lisp-checkdoc
"An Emacs Lisp style checker using CheckDoc.
@@ -9002,61 +8990,47 @@ The checker runs `checkdoc-current-buffer'."
:modes (emacs-lisp-mode)
:enabled flycheck--emacs-lisp-checkdoc-enabled-p)
-(defconst flycheck-org-lint-form
- (flycheck-prepare-emacs-lisp-form
- (with-demoted-errors "Org-lint error: %S"
- (require 'org)
- (let ((source (car command-line-args-left))
- (process-default-directory default-directory))
- (with-temp-buffer
- (insert-file-contents source 'visit)
- (setq buffer-file-name source)
- (setq default-directory process-default-directory)
- (delay-mode-hooks (org-mode))
- (setq delayed-mode-hooks nil)
- (dolist (err (org-lint))
- (pcase err
- (`(,_n [,line ,_trust ,desc ,_checker])
- (princ (format "%s:%s: %s\n" source line desc)))
- (_
- (princ (format "%s:1: Unexpected org-lint format: %S\n" source
err))))))))))
-
-(defun flycheck-org-lint-available-p ()
- "Check if org-lint is available."
- (and (fboundp 'org-lint)
- (require 'org nil 'no-error)))
-
(dolist (checker '(emacs-lisp emacs-lisp-checkdoc))
(setf (car (flycheck-checker-get checker 'command))
flycheck-this-emacs-executable))
-(flycheck-define-checker org-lint
+(flycheck-define-generic-checker 'org-lint
"An Org mode syntax checker using `org-lint'.
-The checker runs `org-lint' in an Emacs subprocess."
- :command ("emacs" (eval flycheck-emacs-args)
- "--eval" (eval (flycheck-sexp-to-string
- (flycheck-org-lint-variables-form)))
- "--eval" (eval flycheck-org-lint-form)
- "--" source)
- :error-patterns
- ((info line-start (file-name) ":" line ": " (message) line-end))
- :modes (org-mode)
- :enabled (lambda () (flycheck-org-lint-available-p))
+The checker runs `org-lint' in the current Emacs process, so it
+has access to all installed packages and user configuration."
+ :start (lambda (checker callback)
+ (condition-case err
+ (let ((errors
+ (delq nil
+ (mapcar
+ (lambda (e)
+ (pcase e
+ (`(,_n [,line ,_trust ,desc ,_checker])
+ (flycheck-error-new-at
+ line nil 'info desc
+ :checker checker))
+ (_
+ (flycheck-error-new-at
+ 1 nil 'warning
+ (format "Unexpected org-lint format: %S" e)
+ :checker checker))))
+ (org-lint)))))
+ (funcall callback 'finished errors))
+ (error (funcall callback 'errored
+ (error-message-string err)))))
+ :modes '(org-mode)
+ :enabled #'flycheck-org-lint-available-p
:verify (lambda (_)
(let ((org-version (when (require 'org nil 'no-error)
- (org-version))))
+ (org-version))))
(list (flycheck-verification-result-new
:label "Org-lint available"
:message (if (fboundp 'org-lint)
- (format "yes (Org %s)" org-version)
- "no")
+ (format "yes (Org %s)" org-version)
+ "no")
:face (if (fboundp 'org-lint) 'success 'warning))))))
-;; Set org-lint to use the current Emacs
-(setf (car (flycheck-checker-get 'org-lint 'command))
- flycheck-this-emacs-executable)
-
(defun flycheck-ember-template--check-for-config (&rest _ignored)
"Check the required config file is available up the file system."
(and buffer-file-name