Skip to content

Commit

Permalink
plugin/emacs: recover from quick-lint-js crashes in Flymake plugin
Browse files Browse the repository at this point in the history
Signed-off-by: wagner riffel <w@104d.net>
  • Loading branch information
wgrr committed Jul 29, 2021
1 parent 88a9792 commit 1d5e4e4
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 35 deletions.
112 changes: 77 additions & 35 deletions plugin/emacs/flymake-quicklintjs.el
Original file line number Diff line number Diff line change
Expand Up @@ -40,47 +40,89 @@ Return a list of Flymake diagnostic objects in source buffer SRC-BUF."
(if (= sev 0) :error :warning) msg)))
quicklintjs-output-alist))

(defun flymake-quicklintjs--report-with-crash (qljs-stdout-buf
qljs-stderr-buf
src-buf report-fn)
"Similar to `flymake-quicklintjs--report' but tries to recover errors from
quick-lint-js crashes and logs whatever is in QLJS-STDERR-BUF"
(flymake-log :warning
"quick-lint-js exited with a deadly signal.\n\
Please consider filing a bug at\
https://github.com/quick-lint/quick-lint-js/issues/new\n\
qjls-stderr-buf:\n\
%s\n"
(with-current-buffer qljs-stderr-buf
(buffer-substring-no-properties (point-min) (point-max))))
(with-current-buffer qljs-stdout-buf
(save-excursion
;; a crash without any previous issue, try to fix an empty buffer
(when (zerop (buffer-size))
(goto-char (point-min))
(insert-char ?\())
;; the above left a '(' danlging or crashed and
;; emacs_lisp_error_reporter::finish() haven't been called yet
(goto-char (point-min))
(when (eq (char-after 1) ?\()
(goto-char (point-max))
(insert-char ?\)))))
(condition-case nil
(flymake-quicklintjs--report qljs-stdout-buf src-buf report-fn)
(funcall report-fn '())))

(defun flymake-quicklintjs--report (qljs-stdout-buf src-buf report-fn)
"Call REPORT-FN to highlight reports in SRC_BUF reported in QLJS-STDOUT-BUF.
QLJS-STDOUT-BUF is entirely read and it's expected to be in
QUICKLINTJS-OUTPUT-ALIST format."
(with-current-buffer qljs-stdout-buf
(funcall report-fn
(flymake-quicklintjs--make-diagnostics
src-buf
(car (read-from-string
(buffer-substring-no-properties (point-min)
(point-max))))))))

;;;###autoload
(defun flymake-quicklintjs (report-fn &rest _args)
"Flymake backend for quick-lint-js linter.
This backend uses `flymake-quicklintjs-program' (which see) to launch a
quick-lint-js process that is passed the current buffer's contents via stdin.
This backend uses `quicklintjs-find-program' to find and launch a quick-lint-js
process that is passed the current buffer's contents via stdin.
REPORT-FN is Flymake's callback."
(when (process-live-p flymake-quicklintjs--proc)
(kill-process flymake-quicklintjs--proc))
(let ((src-buf (current-buffer)))
(setq flymake-quicklintjs--proc
(make-process
:name "flymake-quicklintjs"
:connection-type 'pipe
:noquery t
:buffer (get-buffer-create " *flymake-quicklintjs*")
:command (quicklintjs-find-program
(let ((file (buffer-file-name)))
(when file (concat "--path-for-config-search=" file)))
"--stdin" "--output-format=emacs-lisp")
:sentinel
(lambda (p _ev)
(unwind-protect
(when (and (eq 'exit (process-status p))
(eq p flymake-quicklintjs--proc))
(with-current-buffer (process-buffer p)
(let ((diags (flymake-quicklintjs--make-diagnostics
src-buf
(car (read-from-string
(buffer-substring-no-properties
(point-min) (point-max)))))))
(if (or diags (zerop (process-exit-status p)))
(funcall report-fn diags)
(funcall report-fn
:panic :explanation
(buffer-substring
(point-min) (progn (goto-char (point-min))
(line-end-position))))))))
(unless (process-live-p p)
(kill-buffer (process-buffer p)))))))
(process-send-region flymake-quicklintjs--proc (point-min) (point-max))
(process-send-eof flymake-quicklintjs--proc)))
(let ((src-buf (current-buffer))
(stdout-buf (generate-new-buffer " *flymake-quicklintjs-stdout*"))
(stderr-buf (generate-new-buffer " *flymake-quicklintjs-stderr*")))
(save-restriction
(widen)
(setq flymake-quicklintjs--proc
(make-process
:name "flymake-quicklintjs"
:connection-type 'pipe
:noquery t
:buffer stdout-buf
:stderr stderr-buf
:command (quicklintjs-find-program
(let ((file (buffer-file-name)))
(when file (concat "--path-for-config-search=" file)))
"--stdin" "--output-format=emacs-lisp")
:sentinel
(lambda (p _ev)
(let ((proc-status (process-status p)))
(unwind-protect
(when (and (or (eq proc-status 'exit)
(eq proc-status 'signal))
(with-current-buffer src-buf
(eq p flymake-quicklintjs--proc)))
(if (zerop (process-exit-status p))
(flymake-quicklintjs--report stdout-buf
src-buf
report-fn)
(flymake-quicklintjs--report-with-crash
stdout-buf stderr-buf src-buf report-fn)))
(progn (kill-buffer stdout-buf)
(kill-buffer stderr-buf)))))))
(process-send-region flymake-quicklintjs--proc (point-min) (point-max))
(process-send-eof flymake-quicklintjs--proc))))

(provide 'flymake-quicklintjs)

Expand Down
1 change: 1 addition & 0 deletions src/emacs-lisp-error-reporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ void emacs_lisp_error_formatter::write_after_message(severity sev,
return;
}
this->output_ << "\")";
this->output_.flush();
}
}

Expand Down

0 comments on commit 1d5e4e4

Please sign in to comment.