branch: elpa/flymake-pyrefly commit 97f20ee73c0f0e48d45d938b655ccac5955affa1 Merge: ed2ab6ed79 c9ad67057e Author: Boris Shminke <1696493+inpef...@users.noreply.github.com> Commit: GitHub <nore...@github.com>
Merge pull request #1 from inpefess/add-code add readme and code --- README.org | 11 +++++ flymake-pyrefly.el | 116 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 127 insertions(+) diff --git a/README.org b/README.org new file mode 100644 index 0000000000..8af3160162 --- /dev/null +++ b/README.org @@ -0,0 +1,11 @@ +* flymake-pyrefly + +A [[https://pyrefly.org/][Pyrefly]] backend for [[https://www.gnu.org/software/emacs/manual/html_node/flymake/index.html#Top][Flymake]]. + +* How to install + +#+begin_src elisp + (use-package flymake-pyrefly + :vc (:url "https://github.com/inpefess/flymake-pyrefly.git") + :hook (python-mode . pyrefly-setup-flymake-backend)) +#+end_src diff --git a/flymake-pyrefly.el b/flymake-pyrefly.el new file mode 100644 index 0000000000..0eacba7e53 --- /dev/null +++ b/flymake-pyrefly.el @@ -0,0 +1,116 @@ +;;; flymake-pyrefly.el --- A Pyrefly Flymake backend. -*- lexical-binding: t; -*- +;; Copyright (C) 2025 Boris Shminke + +;; This program is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see <https://www.gnu.org/licenses/>. + +;;; Commentary: +;; Based on the annotated example from Flymake info. +;; Usage: +;; (use-package flymake-pyrefly +;; :vc (:url "https://github.com/inpefess/flymake-pyrefly.git") +;; :hook (python-mode . pyrefly-setup-flymake-backend)) + +;;; Code: +(require 'cl-lib) +(defvar-local pyrefly--flymake-proc nil) + +(defun flymake-pyrefly (report-fn &rest _args) + "Report pyrefly diagnostic with REPORT-FN." + ;; Not having pyrefly installed is a serious problem which should cause + ;; the backend to disable itself, so an error is signaled. + ;; + (unless (executable-find + "pyrefly") (error "Cannot find pyrefly")) + ;; If a live process launched in an earlier check was found, that + ;; process is killed. When that process's sentinel eventually runs, + ;; it will notice its obsoletion, since it have since reset + ;; `flymake-pyrefly-proc' to a different value + ;; + (when (process-live-p pyrefly--flymake-proc) + (kill-process pyrefly--flymake-proc)) + + ;; Save the current buffer, the narrowing restriction, remove any + ;; narrowing restriction. + ;; + (let ((source (current-buffer))) + (save-restriction + (widen) + ;; Reset the `pyrefly--flymake-proc' process to a new process + ;; calling the pyrefly tool. + ;; + (setq + pyrefly--flymake-proc + (make-process + :name "flymake-pyrefly" :noquery t :connection-type 'pipe + ;; Make output go to a temporary buffer. + ;; + :buffer (generate-new-buffer " *flymake-pyrefly*") + :command + (append + '("pyrefly" "check" "--output-format" "min-text" "--no-summary") + (list buffer-file-name)) + :sentinel + (lambda (proc _event) + ;; Check that the process has indeed exited, as it might + ;; be simply suspended. + ;; + (when (memq (process-status proc) '(exit signal)) + (unwind-protect + ;; Only proceed if `proc' is the same as + ;; `pyrefly--flymake-proc', which indicates that + ;; `proc' is not an obsolete process. + ;; + (if (with-current-buffer source (eq proc pyrefly--flymake-proc)) + (with-current-buffer (process-buffer proc) + (goto-char (point-min)) + ;; Parse the output buffer for diagnostic's + ;; messages and locations, collect them in a list + ;; of objects, and call `report-fn'. + ;; + (cl-loop + while (search-forward-regexp + "^\\([A-Z]+\\) .+\.py:\\([0-9]+\\):[0-9]+\-[0-9]+: \\(.*\\)$" + nil t) + for msg = (match-string 3) + for (beg . end) = (flymake-diag-region + source + (string-to-number (match-string 2))) + for type = (if (equal "ERROR" (match-string 1)) + :error + :warning) + when (and beg end) + collect (flymake-make-diagnostic source + beg + end + type + msg) + into diags + finally (funcall report-fn diags))) + (flymake-log :warning "Canceling obsolete check %s" + proc)) + ;; Cleanup the temporary buffer used to hold the + ;; check's output. + ;; + (kill-buffer (process-buffer proc))))))) + ;; Send the buffer contents to the process's stdin, followed by + ;; an EOF. + ;; + (process-send-region pyrefly--flymake-proc (point-min) (point-max)) + (process-send-eof pyrefly--flymake-proc)))) + +(defun pyrefly-setup-flymake-backend () + "Setup Pyrefly as Flymake backend." + (add-hook 'flymake-diagnostic-functions 'flymake-pyrefly nil t)) +(provide 'flymake-pyrefly) +;;; flymake-pyrefly.el ends here