Skip to content

Commit

Permalink
Add option to format diagnostics as JSON
Browse files Browse the repository at this point in the history
For more accurate error regions
  • Loading branch information
hochata committed Sep 27, 2023
1 parent 82b1345 commit 68d776b
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 9 deletions.
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,13 @@ Useful when the value of variable `exec-path' is set dynamically and the locatio
"Buffer-local. Set to a filesystem path to use that path as the current working directory of the linting process."
:type 'string
:group 'flymake-eslint)
(defcustom flymake-eslint-prefer-json-diagnostics nil
"Try to use the JSON diagnostic format when runnin ESLint.
This gives more accurate diagnostics but requires having an Emacs
version with JSON support."
:type 'boolean
:group 'flymake-eslint)
```

## Bugs
Expand Down
92 changes: 83 additions & 9 deletions flymake-eslint.el
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
;;;; Requirements

(require 'cl-lib)
(require 'json)

;;;; Customization

Expand Down Expand Up @@ -68,6 +69,13 @@ directory of the linting process."
:type 'string
:group 'flymake-eslint)

(defcustom flymake-eslint-prefer-json-diagnostics nil
"Try to use the JSON diagnostic format when runnin ESLint.
This gives more accurate diagnostics but requires having an Emacs
version with JSON support."
:type 'boolean
:group 'flymake-eslint)

;;;; Variables

(defvar flymake-eslint--message-regexp
Expand Down Expand Up @@ -115,6 +123,58 @@ variable `exec-path'"
(error "Can't find \"%s\" in exec-path - try to configure `%s'"
(symbol-value option) option))))

(defun flymake-eslint--get-position (line column buffer)
"Get the position at LINE and COLUMN for BUFFER."
(with-current-buffer buffer
(save-excursion
(when (and line column)
(goto-char (point-min))
(forward-line (1- line))
(forward-char (1- column))
(point)))))

(defun flymake-eslint--diag-from-eslint (eslint-diag buffer)
"Transform ESLINT-DIAG diagnostic for BUFFER into a Flymake one."
(let* ((beg-line (gethash "line" eslint-diag))
(beg-col (gethash "column" eslint-diag))
(beg-pos (flymake-eslint--get-position beg-line beg-col buffer))
(end-line (gethash "endLine" eslint-diag))
(end-col (gethash "endColumn" eslint-diag))
(end-pos (if end-line
(flymake-eslint--get-position end-line end-col buffer)
(cdr (flymake-diag-region buffer beg-line))))
(lint-rule (gethash "ruleId" eslint-diag))
(severity (gethash "severity" eslint-diag))
(type (if (equal severity 1) :warning :error))
(msg (gethash "message" eslint-diag))
(full-msg (concat
msg
(when flymake-eslint--message-regexp
(format " [%s]" lint-rule)))))
(flymake-make-diagnostic
buffer
beg-pos
end-pos
type
full-msg
(list :rule-name lint-rule))))

(defun flymake-eslint--report-json (eslint-stdout-buffer source-buffer)
"Create Flymake diagnostics from the JSON diagnostic in ESLINT-STDOUT-BUFFER.
The diagnostics are reported against SOURCE-BUFFER."
(with-current-buffer eslint-stdout-buffer
(goto-char (point-min))
(let* ((full-diagnostics (json-parse-buffer))
(eslint-diags (gethash "messages"(elt full-diagnostics 0))))
(seq-map
(lambda (diag)
(flymake-eslint--diag-from-eslint diag source-buffer))
eslint-diags))))

(defun flymake-eslint--use-json-p ()
"Check if ESLint diagnostics should be requested to be formatted as JSON."
(and (json-available-p) flymake-eslint-prefer-json-diagnostics))

(defun flymake-eslint--report (eslint-stdout-buffer source-buffer)
"Create Flymake diag messages from contents of ESLINT-STDOUT-BUFFER.
They are reported against SOURCE-BUFFER. Return a list of
Expand Down Expand Up @@ -160,15 +220,23 @@ CALLBACK accepts a buffer containing stdout from linter as its
argument."
(when (process-live-p flymake-eslint--process)
(kill-process flymake-eslint--process))
(let ((default-directory (or flymake-eslint-project-root default-directory)))
(let ((default-directory (or flymake-eslint-project-root default-directory))
(format-args
(if (flymake-eslint--use-json-p)
'("--format" "json")
"")))
(setq flymake-eslint--process
(make-process
:name "flymake-eslint"
:connection-type 'pipe
:noquery t
:buffer (generate-new-buffer " *flymake-eslint*")
:command `(,flymake-eslint-executable-name
"--no-color" "--no-ignore" "--stdin" "--stdin-filename"
"--no-color"
"--no-ignore"
,@format-args
"--stdin"
"--stdin-filename"
,(buffer-file-name source-buffer)
,@(flymake-eslint--executable-args))
:sentinel
Expand All @@ -190,13 +258,19 @@ argument."
Use REPORT-FN to report results."
(when flymake-eslint-defer-binary-check
(flymake-eslint--ensure-binary-exists))
(flymake-eslint--create-process
source-buffer
(lambda (eslint-stdout)
(funcall report-fn (flymake-eslint--report eslint-stdout source-buffer))))
(with-current-buffer source-buffer
(process-send-string flymake-eslint--process (buffer-string))
(process-send-eof flymake-eslint--process)))
(let ((diag-builder-fn
(if (flymake-eslint--use-json-p)
'flymake-eslint--report-json
'flymake-eslint--report)))
(flymake-eslint--create-process
source-buffer
(lambda (eslint-stdout)
(funcall
report-fn
(funcall diag-builder-fn eslint-stdout source-buffer))))
(with-current-buffer source-buffer
(process-send-string flymake-eslint--process (buffer-string))
(process-send-eof flymake-eslint--process))))

(defun flymake-eslint--checker (report-fn &rest _ignored)
"Run eslint on the current buffer.
Expand Down

0 comments on commit 68d776b

Please sign in to comment.