You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

167 lines
6.7 KiB

;;; alchemist-report.el --- Run command in a process and handles buffer of it
;; Copyright © 2015 Samuel Tonini
;; Author: Samuel Tonini <tonini.samuel@gmail.com
;; This file is not part of GNU Emacs.
;; 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 <http://www.gnu.org/licenses/>.
;;; Commentary:
;; Run command in a process and handles buffer output and display
;;; Code:
(require 'ansi-color)
(require 'alchemist-project)
(defgroup alchemist-report nil
"Run command in a process and handles buffer output and display"
:prefix "alchemist-report-"
:group 'alchemist)
(defvar alchemist-report-on-exit nil)
(defvar alchemist-report-on-exit-function nil)
(defvar alchemist-report-on-render nil)
(defvar alchemist-report-on-render-function nil)
(defvar alchemist-report--last-run-status nil)
(defvar alchemist-report-mode-name nil)
(defun alchemist-report--kill-process (process)
"Interrupt and kill the running report PROCESS."
(when process
(let ((mode-name (replace-regexp-in-string ":.+$" "" mode-name)))
(if (or (not (eq (process-status process) 'run))
(eq (process-query-on-exit-flag process) nil)
(yes-or-no-p
(format "A %s process already running; kill it? "
mode-name)))
(condition-case ()
(progn
(interrupt-process process)
(sit-for 1)
(delete-process process))
(error nil))
(error "Cannot have two processes in `%s' at once"
(buffer-name))))))
(defun alchemist-report--sentinel (process status)
"Sentinel for test report buffer."
(if (memq (process-status process) '(exit signal))
(let ((buffer (process-buffer process)))
(if (null (buffer-name buffer))
(set-process-buffer process nil)
(progn
(alchemist-report--render-report buffer)
(alchemist-report--handle-exit status buffer)
(alchemist-report-update-mode-name process)
(delete-process process))))))
(defun alchemist-report--render-report (buffer)
"Call the defined render functions for the BUFFER."
(when alchemist-report-on-render-function
(funcall alchemist-report-on-render-function buffer)))
(defun alchemist-report--handle-exit (status buffer)
"Call the defined exit function specified in `alchemist-report-on-exit-function'.
Argument for the exit function is the STATUS and BUFFER of the finished process."
(alchemist-report--store-process-status status)
(when alchemist-report-on-exit-function
(funcall alchemist-report-on-exit-function status buffer)))
(defun alchemist-report--store-process-status (status)
"Store STATUS of the last finished process."
(setq alchemist-report--last-run-status status))
(defun alchemist-report--last-run-successful-p ()
"Return non-nil if the last process successfully finished."
(when (string-prefix-p "finished" alchemist-report--last-run-status) t))
(defun alchemist-report-filter (process output)
"Process filter for report buffers."
(with-current-buffer (process-buffer process)
(let* ((buffer-read-only nil)
(output (if (string= (process-name process) alchemist-test-report-process-name)
(alchemist-test-clean-compilation-output output)
output))
(moving (= (point) (process-mark process))))
(save-excursion
(goto-char (process-mark process))
(insert output)
(set-marker (process-mark process) (point))
(ansi-color-apply-on-region (point-min) (point-max)))
(if moving (goto-char (process-mark process))))))
(defun alchemist-report-update-mode-name (process)
"Update the `mode-name' with the status of PROCESS."
(with-current-buffer (process-buffer process)
(setq-local mode-name (format "%s:%s"
(replace-regexp-in-string ":.+$" "" mode-name)
(process-status process)))))
(defun alchemist-report-interrupt-current-process ()
"Interrupt the current running report process."
(interactive)
(let ((buffer (current-buffer))
(name (replace-regexp-in-string ":.+" "" mode-name)))
(if (get-buffer-process buffer)
(interrupt-process (get-buffer-process buffer))
(error "The [%s] process is not running" (downcase name)))))
(defun alchemist-report-cleanup-process-buffer (buffer)
"Clean the content BUFFER of process.
If there is already a running process, ask for interrupting it."
(with-current-buffer buffer
(let ((inhibit-read-only t)
(process (get-buffer-process buffer)))
(erase-buffer))))
(defun alchemist-report-display-buffer (buffer)
"Display the BUFFER."
(display-buffer buffer))
(defun alchemist-report-activate-mode (mode buffer)
"Enable MODE inside BUFFER."
(with-current-buffer buffer
(funcall mode)
(setq-local truncate-lines t) ;; Do not display continuation lines.
(setq-local window-point-insertion-type t)))
(defun alchemist-report-run (command process-name buffer-name mode &optional on-exit hidden)
"Run COMMAND in a new process called PROCESS-NAME.
The output of PROCESS-NAME will be displayed in BUFFER-NAME.
After displaying BUFFER-NAME, the MODE function will be called within.
Optional ON-EXIT and HIDDEN functions could be defined.
The function ON-EXIT will be called when PROCESS-NAME is finished.
The HIDDEN variable defines if PROCESS-NAME should run in the background."
(let* ((buffer (get-buffer-create buffer-name))
(default-directory (alchemist-project-root-or-default-dir)))
(alchemist-report-cleanup-process-buffer buffer)
(alchemist-report--kill-process (get-buffer-process buffer))
(start-process-shell-command process-name buffer command)
(when on-exit
(setq alchemist-report-on-exit-function on-exit))
(set-process-sentinel (get-buffer-process buffer) 'alchemist-report--sentinel)
(set-process-filter (get-buffer-process buffer) 'alchemist-report-filter)
(alchemist-report-activate-mode mode buffer)
(if (not hidden)
(alchemist-report-display-buffer buffer))
(alchemist-report-update-mode-name (get-buffer-process buffer))))
(provide 'alchemist-report)
;;; alchemist-report.el ends here