;;; flymake-phpcs.el --- PHP Code Sniffer Flymake backend -*- lexical-binding: t; -*- (defcustom flymake-phpcs-standard "PSR12" "Setting the Coding Standard for PHP CodeSniffer." :type 'string :group 'flymake-phpcs) (defcustom flymake-phpcs-command (executable-find "phpcs") "The name of the phpcs executable." :type 'string :group 'flymake-phpcs) (defcustom flymake-phpcbf-command (executable-find "phpcbf") "The name of the phpcbf executable." :type 'string :group 'flymake-phpcs) (defvar-local phpcs--flymake-proc nil) (defun phpcs-flymake (report-fn &rest _args) (unless (executable-find flymake-phpcs-command) (error "Cannot find phpcs executable")) ;; 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 ;; `phpcs-flymake-proc' to a different value ;; (when (process-live-p phpcs--flymake-proc) (kill-process phpcs--flymake-proc)) ;; Save the current buffer, the narrowing restriction, remove any ;; narrowing restriction. ;; (let ((source (current-buffer))) (save-restriction (widen) ;; Reset the `phpcs--flymake-proc' process to a new process ;; calling the phpcs tool. ;; (setq phpcs--flymake-proc (make-process :name "phpcs-flymake" :noquery t :connection-type 'pipe ;; Make output go to a temporary buffer. ;; :buffer (generate-new-buffer " *phpcs-flymake*") :command `(,flymake-phpcs-command "--report=emacs" ,(concat "--standard=" flymake-phpcs-standard)) :sentinel (lambda (proc _event) ;; Check that the process has indeed exited, as it might ;; be simply suspended. ;; (when (eq 'exit (process-status proc)) (unwind-protect ;; Only proceed if `proc' is the same as ;; `phpcs--flymake-proc', which indicates that ;; `proc' is not an obsolete process. ;; (if (with-current-buffer source (eq proc phpcs--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 "^STDIN:\\([[:digit:]]+\\):\\([[:digit:]]+\\): \\([[:alpha:]]+\\) - \\(.*\\)$" nil t) for lnum = (string-to-number (match-string 1)) ;; for (beg . end) = (flymake-diag-region source lnum (string-to-number (match-string 2))) for (beg . end) = (flymake-diag-region source lnum) for type = (make-symbol (match-string 3)) for msg = (match-string 4) 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 phpcs--flymake-proc (point-min) (point-max)) (process-send-eof phpcs--flymake-proc)))) (defun flymake-phpcs-load () "Configure flymake mode to check the current buffer's PHP syntax." (interactive) (add-hook 'flymake-diagnostic-functions 'phpcs-flymake nil t)) (defun phpcbf () "Run phpcbf on current file (saved-files only)." (interactive) (shell-command (combine-and-quote-strings (list flymake-phpcbf-command (concat "--standard=" flymake-phpcs-standard) (buffer-file-name)))) (revert-buffer :ignore-auto :noconfirm)) (provide 'flymake-phpcs)