Skip to content

Commit d5ebeb6

Browse files
committed
plugin/emacs: add Flamake backend support
Signed-off-by: wagner riffel <[email protected]>
1 parent 9bf525e commit d5ebeb6

File tree

3 files changed

+168
-0
lines changed

3 files changed

+168
-0
lines changed

plugin/emacs/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ install(
88
FILES flycheck-quicklintjs.el
99
FILES lsp-quicklintjs.el
1010
FILES eglot-quicklintjs.el
11+
FILES flymake-quicklintjs.el
1112
DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/emacs/site-lisp"
1213
)
1314

plugin/emacs/flymake-quicklintjs.el

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
;;; flymake-quicklintjs.el --- Flymake support for quick-lint-js -*- lexical-binding: t; -*-
2+
3+
;;; Commentary:
4+
5+
;; Flymake support for quick-lint-js.
6+
7+
;; Example usage in your init.el:
8+
;;
9+
;; (require 'flymake-quicklintjs)
10+
;;
11+
;; (defun my-flymake-quicklintjs-setup ()
12+
;; "Configure flymake-quicklintjs for better experience."
13+
;;
14+
;; ;; Enable Flymake
15+
;; (unless (bound-and-true-p flymake-mode)
16+
;; (flymake-mode))
17+
;; (add-hook 'flymake-diagnostic-functions #'flymake-quicklintjs nil t)
18+
;;
19+
;; ;; Remove the time to wait after last change before automatically checking
20+
;; ;; buffer. The default is 0.5 (500ms)
21+
;; (setq-local flymake-no-changes-timeout 0))
22+
;; (add-hook 'js-mode-hook #'my-flymake-quicklintjs-setup)
23+
24+
;;; Code:
25+
26+
(defgroup flymake-quicklintjs nil
27+
"Flymake backend for quick-lint-js"
28+
:link '(url-link :tag "Website" "https://quick-lint-js.com"))
29+
30+
(defcustom flymake-quicklintjs-program "quick-lint-js"
31+
"Path to quick-lint-js program to run."
32+
:group 'flymake-quicklintjs
33+
:type 'stringp)
34+
35+
(defcustom flymake-quicklintjs-args nil
36+
"Arguments to quick-lint-js."
37+
:group 'flymake-quicklintjs
38+
:type '(repeat 'string))
39+
40+
(defconst flymake-quicklintjs--regexp (concat
41+
"^<stdin>:\\([0-9]+\\):\\([0-9]+\\): "
42+
"\\(warning\\|error\\): "
43+
"\\(.+\\) \\(\\[E[0-9]+\\]\\)$")
44+
"Regular expression to match quick-lint-js gnu-like output format.")
45+
46+
(defvar-local flymake-quicklintjs--proc nil
47+
"Internal variable for `flymake-quicklintjs'")
48+
49+
(defun flymake-quicklintjs--error-region (src-buf line col)
50+
"Compute SRC-BUF region (BEG . END) corresponding to LINE and COL."
51+
(with-current-buffer src-buf
52+
(save-excursion
53+
(goto-char (point-min))
54+
(forward-line (1- line))
55+
(goto-char (min (+ (line-beginning-position) col) (line-end-position)))
56+
(or (bounds-of-thing-at-point 'word) (cons (point) (point))))))
57+
58+
(defun flymake-quicklintjs--make-diagnostics (src-buf)
59+
"Parse gnu-like compilation messages in current buffer.
60+
Return a list of Flymake diagnostic objects for the source buffer
61+
SRC-BUF."
62+
(let (diag-accum)
63+
(while (not (eobp))
64+
(when (looking-at flymake-quicklintjs--regexp)
65+
(let* ((line (string-to-number (match-string 1)))
66+
(col (string-to-number (match-string 2)))
67+
(type (match-string 3))
68+
(msg (match-string 4))
69+
(type-sym (if (string= "error" type) :error :warning)))
70+
(let* ((diag-region (flymake-quicklintjs--error-region src-buf line col))
71+
(beg (car diag-region))
72+
(end (min (buffer-size src-buf) (cdr diag-region))))
73+
(push (flymake-make-diagnostic src-buf beg end type-sym msg)
74+
diag-accum))))
75+
(forward-line 1))
76+
diag-accum))
77+
78+
;;;###autoload
79+
(defun flymake-quicklintjs (report-fn &rest _args)
80+
"Flymake backend for quick-lint-js linter.
81+
This backend uses `flymake-quicklintjs-program' (which see) to launch a
82+
quick-lint-js process that is passed the current buffer's contents via stdin.
83+
REPORT-FN is Flymake's callback."
84+
(when (process-live-p flymake-quicklintjs--proc)
85+
(kill-process flymake-quicklintjs--proc))
86+
(let ((src-buf (current-buffer)))
87+
(setq flymake-quicklintjs--proc
88+
(make-process
89+
:name "flymake-quicklintjs"
90+
:connection-type 'pipe
91+
:noquery t
92+
:buffer (generate-new-buffer " *flymake-quicklintjs*")
93+
:command `(,flymake-quicklintjs-program
94+
"--stdin" "--output-format=gnu-like"
95+
,@flymake-quicklintjs-args)
96+
:sentinel
97+
(lambda (p _ev)
98+
(unwind-protect
99+
(when (eq 'exit (process-status p))
100+
(when (with-current-buffer src-buf (eq p flymake-quicklintjs--proc))
101+
(with-current-buffer (process-buffer p)
102+
(goto-char (point-min))
103+
(let ((diags
104+
(flymake-quicklintjs--make-diagnostics src-buf)))
105+
(if (or diags (zerop (process-exit-status p)))
106+
(funcall report-fn diags
107+
:region (cons (point-min) (point-max)))
108+
(funcall report-fn
109+
:panic :explanation
110+
(buffer-substring
111+
(point-min) (progn (goto-char (point-min))
112+
(line-end-position)))))))))
113+
(unless (process-live-p p)
114+
(kill-buffer (process-buffer p)))))))
115+
(process-send-region flymake-quicklintjs--proc (point-min) (point-max))
116+
(process-send-eof flymake-quicklintjs--proc)))
117+
118+
(provide 'flymake-quicklintjs)
119+
120+
;; quick-lint-js finds bugs in JavaScript programs.
121+
;; Copyright (C) 2020 Matthew Glazar
122+
;;
123+
;; This file is part of quick-lint-js.
124+
;;
125+
;; quick-lint-js is free software: you can redistribute it and/or modify
126+
;; it under the terms of the GNU General Public License as published by
127+
;; the Free Software Foundation, either version 3 of the License, or
128+
;; (at your option) any later version.
129+
;;
130+
;; quick-lint-js is distributed in the hope that it will be useful,
131+
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
132+
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
133+
;; GNU General Public License for more details.
134+
;;
135+
;; You should have received a copy of the GNU General Public License
136+
;; along with quick-lint-js. If not, see <https://www.gnu.org/licenses/>.
137+
138+
;;; flymake-quicklintjs.el ends here

plugin/emacs/test/quicklintjs-test.el

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,37 @@
3131
(def-flycheck-tests)
3232
(def-eglot-tests)
3333
(def-lsp-tests)
34+
(def-flymake-tests)
3435
(ert-run-tests-batch-and-exit))
3536

37+
(defun def-flymake-tests ()
38+
(require 'flymake-quicklintjs)
39+
(ert-deftest quicklintjs-flymake-parse-errors-and-warnings ()
40+
(let ((errors-buf (generate-new-buffer "*errors-buf*"))
41+
(js-buf (generate-new-buffer "*js-buf*")))
42+
(with-current-buffer js-buf
43+
(insert "foobar\n")
44+
(insert "/*💩*/ foobar \n")
45+
(insert "foobar /*💩*/\n")
46+
(insert "function\n"))
47+
(with-current-buffer errors-buf
48+
(insert "<stdin>:4:1: error: missing name in function statement [E061]\n")
49+
(insert "<stdin>:1:1: warning: use of undeclared variable: foobar [E057]\n")
50+
(insert "<stdin>:2:12: warning: use of undeclared variable: foobar [E057]\n")
51+
(insert "<stdin>:3:1: warning: use of undeclared variable: foobar [E057]\n")
52+
(goto-char (point-min))
53+
54+
(let ((diags (list
55+
(flymake-make-diagnostic js-buf 25 31 :warning
56+
"use of undeclared variable: foobar")
57+
(flymake-make-diagnostic js-buf 16 22 :warning
58+
"use of undeclared variable: foobar")
59+
(flymake-make-diagnostic js-buf 1 7 :warning
60+
"use of undeclared variable: foobar")
61+
(flymake-make-diagnostic js-buf 38 46 :error
62+
"missing name in function statement"))))
63+
(should (equal (flymake-quicklintjs--make-diagnostics js-buf) diags)))))))
64+
3665
(defun def-eglot-tests ()
3766
(when (>= emacs-major-version 26)
3867
(require 'eglot-quicklintjs)

0 commit comments

Comments
 (0)