File 9524-Rewrite-erlang-flymake-to-use-modern-flymake-backend.patch of Package erlang
From b91979af13a335ebacba141a509b4cc13490bdc8 Mon Sep 17 00:00:00 2001
From: Bozhidar Batsov <bozhidar@batsov.dev>
Date: Fri, 20 Mar 2026 13:36:19 +0200
Subject: [PATCH 14/17] Rewrite erlang-flymake to use modern flymake backend
API
The old flymake-proc based approach used flymake-allowed-file-name-masks
and flymake-proc-init-create-temp-buffer-copy, which were part of the
legacy flymake system deprecated in Emacs 26.
Rewrite to use the modern flymake diagnostic functions API, which
runs erlc as an async process and parses its output to produce
flymake diagnostics. This handles both new-style (file:line:col)
and old-style (file:line) erlc error output formats.
---
lib/tools/emacs/erlang-flymake.el | 138 +++++++++++++++++++-----------
1 file changed, 87 insertions(+), 51 deletions(-)
diff --git a/lib/tools/emacs/erlang-flymake.el b/lib/tools/emacs/erlang-flymake.el
index e49367aee0..f9cbf0268a 100644
--- a/lib/tools/emacs/erlang-flymake.el
+++ b/lib/tools/emacs/erlang-flymake.el
@@ -29,12 +29,6 @@
;;
;; (require 'erlang-flymake)
;;
-;; Flymake is rather eager and does its syntax checks frequently by
-;; default and if you are bothered by this, you might want to put the
-;; following in your .emacs as well:
-;;
-;; (erlang-flymake-only-on-save)
-;;
;; There are a couple of variables which control the compilation options:
;; * erlang-flymake-get-code-path-dirs-function
;; * erlang-flymake-get-include-dirs-function
@@ -43,14 +37,10 @@
;; This code is inspired by http://www.emacswiki.org/emacs/FlymakeErlang.
(require 'flymake)
-(require 'flymake-proc nil 'noerror)
-
-(eval-when-compile
- (require 'cl-lib))
(defvar erlang-flymake-command
"erlc"
- "The command that will be used to perform the syntax check")
+ "The command that will be used to perform the syntax check.")
(defvar erlang-flymake-get-code-path-dirs-function
'erlang-flymake-get-code-path-dirs
@@ -66,17 +56,7 @@
"+warn_export_vars"
"+strong_validation"
"+report")
- "A list of options that will be passed to the compiler")
-
-(defun erlang-flymake-only-on-save ()
- "Trigger flymake only when the buffer is saved (disables syntax
-check on newline and when there are no changes)."
- (interactive)
- ;; There doesn't seem to be a way of disabling this; set to the
- ;; largest int available as a workaround (most-positive-fixnum
- ;; equates to 8.5 years on my machine, so it ought to be enough ;-) )
- (setq flymake-no-changes-timeout most-positive-fixnum))
-
+ "A list of options that will be passed to the compiler.")
(defun erlang-flymake-get-code-path-dirs ()
(list (concat (erlang-flymake-get-app-dir) "ebin")))
@@ -89,37 +69,93 @@ check on newline and when there are no changes)."
(let ((src-path (file-name-directory (buffer-file-name))))
(file-name-directory (directory-file-name src-path))))
-(defun erlang-flymake-init ()
- (let* ((temp-file
- (cl-letf (((symbol-function 'flymake-get-temp-dir) #'erlang-flymake-temp-dir))
- (flymake-proc-init-create-temp-buffer-copy
- 'flymake-create-temp-with-folder-structure)))
+(defvar-local erlang-flymake--proc nil
+ "Internal variable for the Erlang flymake backend process.")
+
+(defun erlang-flymake-backend (report-fn &rest _args)
+ "Flymake backend for Erlang using erlc.
+REPORT-FN is the flymake callback."
+ (when (process-live-p erlang-flymake--proc)
+ (kill-process erlang-flymake--proc))
+ (let* ((source (current-buffer))
+ (filename (buffer-file-name source))
(code-dir-opts
- (erlang-flymake-flatten
- (mapcar (lambda (dir) (list "-pa" dir))
- (funcall erlang-flymake-get-code-path-dirs-function))))
+ (apply #'append
+ (mapcar (lambda (dir) (list "-pa" dir))
+ (funcall erlang-flymake-get-code-path-dirs-function))))
(inc-dir-opts
- (erlang-flymake-flatten
- (mapcar (lambda (dir) (list "-I" dir))
- (funcall erlang-flymake-get-include-dirs-function))))
- (compile-opts
- (append inc-dir-opts
- code-dir-opts
- erlang-flymake-extra-opts)))
- (list erlang-flymake-command (append compile-opts (list temp-file)))))
-
-(defun erlang-flymake-temp-dir ()
- ;; Squeeze the user's name in there in order to make sure that files
- ;; for two users who are working on the same computer (like a linux
- ;; box) don't collide
- (format "%s/flymake-%s" temporary-file-directory user-login-name))
-
-(defun erlang-flymake-flatten (list)
- (apply #'append list))
-
-(add-to-list 'flymake-allowed-file-name-masks
- '("\\.erl\\'" erlang-flymake-init))
-(add-hook 'erlang-mode-hook 'flymake-mode)
+ (apply #'append
+ (mapcar (lambda (dir) (list "-I" dir))
+ (funcall erlang-flymake-get-include-dirs-function))))
+ (compile-opts (append inc-dir-opts
+ code-dir-opts
+ erlang-flymake-extra-opts
+ (list filename))))
+ (save-restriction
+ (widen)
+ (setq erlang-flymake--proc
+ (make-process
+ :name "erlang-flymake"
+ :noquery t
+ :connection-type 'pipe
+ :buffer (generate-new-buffer " *erlang-flymake*")
+ :command (cons erlang-flymake-command compile-opts)
+ :sentinel
+ (lambda (proc _event)
+ (when (memq (process-status proc) '(exit signal))
+ (unwind-protect
+ (when (with-current-buffer source
+ (eq proc erlang-flymake--proc))
+ (with-current-buffer (process-buffer proc)
+ (goto-char (point-min))
+ (let (diagnostics)
+ (while (search-forward-regexp
+ "^\\(.*\\):\\([0-9]+\\):\\([0-9]+\\):\\s-*\\(warning\\|error\\):\\s-*\\(.*\\)$"
+ nil t)
+ (let* ((file (match-string 1))
+ (line (string-to-number (match-string 2)))
+ (col (string-to-number (match-string 3)))
+ (type (if (string= (match-string 4) "warning")
+ :warning
+ :error))
+ (msg (match-string 5))
+ (region (flymake-diag-region source line col)))
+ (when (and region
+ (string= (file-truename file)
+ (file-truename filename)))
+ (push (flymake-make-diagnostic
+ source (car region) (cdr region)
+ type msg)
+ diagnostics))))
+ ;; Also match old-style output without column:
+ ;; file.erl:LINE: warning/error: message
+ (goto-char (point-min))
+ (while (search-forward-regexp
+ "^\\(.*\\):\\([0-9]+\\):\\s-*\\(warning\\|error\\):\\s-*\\(.*\\)$"
+ nil t)
+ (let* ((file (match-string 1))
+ (line (string-to-number (match-string 2)))
+ (type (if (string= (match-string 3) "warning")
+ :warning
+ :error))
+ (msg (match-string 4))
+ (region (flymake-diag-region source line)))
+ (when (and region
+ (string= (file-truename file)
+ (file-truename filename)))
+ (push (flymake-make-diagnostic
+ source (car region) (cdr region)
+ type msg)
+ diagnostics))))
+ (funcall report-fn (nreverse diagnostics)))))
+ (kill-buffer (process-buffer proc))))))))))
+
+(defun erlang-flymake-setup ()
+ "Set up the Erlang flymake backend for the current buffer."
+ (add-hook 'flymake-diagnostic-functions #'erlang-flymake-backend nil t))
+
+(add-hook 'erlang-mode-hook #'erlang-flymake-setup)
+(add-hook 'erlang-mode-hook #'flymake-mode)
(provide 'erlang-flymake)
;; erlang-flymake ends here
--
2.51.0