blob: 2d2a65a0fd995bf3f1766795a99b6f61d2c47035 (
about) (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
|
;;; flymake-guile.el --- Flymake checker using `guild compile' -*- lexical-binding: t -*-
;;; Commentary:
;;; "guild compile" compiles Guile code to bytecode and can output a few basic
;;; warnings. Let's use this as a linter!
;;; Code:
(defcustom flymake-guile-guild-executable "guild"
"The guild executable to use. This will be looked up in $PATH."
:type '(string)
:risky t
:group 'flymake-guile)
(defvar flymake-guile--message-regexp
(rx bol (group (+? not-newline)) ":" ; filename
(group (+ digit)) ":" ; line
(group (+ digit)) ": " ; column
(group (or "warning" "error")) ": " ; type
(group (+ not-newline)) eol) ; message
"Regular expression matching messages from guild compile.
`flymake-guile' expects the following capturing groups in this regexp: (1)
file name; (2) line number; (3) column number; (4) error type; (5) message.")
(defvar-local flymake-guile--flymake-proc nil
"The latest invocation of guild compile.")
(defvar-local flymake-guile--temp-file nil
"The temporary file name to pass to guild.")
;; See info node: (flymake)An annotated example backend.
(defun flymake-guile (report-fn &rest _args)
"Run guild compile and report diagnostics from it using REPORT-FN.
Any running invocations are killed before running another one."
(unless (executable-find flymake-guile-guild-executable)
(funcall report-fn :panic
:explanation "Cannot find `flymake-guile-guild-executable' program")
(error "Cannot find guild executable"))
(unless flymake-guile--temp-file
(setq-local
flymake-guile--temp-file
(concat "/tmp/flymake-guile-"
(string-replace
"/" "!" ; we don't want to create subdirs under /tmp
(or (buffer-file-name)
(format "temp-%s.scm"
(random most-positive-fixnum)))))))
;; Kill previous check, if it's still running.
(when (process-live-p flymake-guile--flymake-proc)
(kill-process flymake-guile--flymake-proc))
;; This needs `lexical-binding'.
(let ((source (current-buffer)))
(save-restriction
(widen)
;; Send the buffer to guild on stdin.
(with-temp-file flymake-guile--temp-file
(insert-buffer-substring-no-properties source))
(setq flymake-guile--flymake-proc
(make-process
:name "flymake-guild" :noquery t :connection-type 'pipe
;; Direct output to a temporary buffer.
:buffer (generate-new-buffer " *flymake-guile*")
;; Guild can't read from stdin; it needs a file.
:command (list flymake-guile-guild-executable "compile" "-W3" flymake-guile--temp-file)
:sentinel
(lambda (proc _event)
"Parse diagnostic messages once the process PROC has exited."
;; Check the process has actually exited, not just been suspended.
(when (memq (process-status proc) '(exit signal))
(unwind-protect
;; Only proceed if we've got the "latest" process.
(if (with-current-buffer source (eq proc flymake-guile--flymake-proc))
(with-current-buffer (process-buffer proc)
(goto-char (point-min))
(cl-loop
while (search-forward-regexp flymake-guile--message-regexp nil t)
for (beg . end) = (flymake-diag-region
(match-string 1) ; source
(string-to-number (match-string 2))
(string-to-number (match-string 3)))
for type = (pcase (match-string 4)
("warning" :warning)
("error" :error)
(type (error "Unknown guild error type %s" type)))
collect (flymake-make-diagnostic source beg end type (match-string 5))
into diags
finally (funcall report-fn diags)))
(flymake-log :warning "Canceling obsolete check %s" proc))
;; Clean up temporary buffer.
(kill-buffer (process-buffer proc))
(delete-file flymake-guile--temp-file)))))))))
(provide 'flymake-guile)
;;; flymake-guile.el ends here
|