|
| 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 |
0 commit comments