;;; alchemist-report.el --- Run command in a process and handles buffer of it ;; Copyright © 2015 Samuel Tonini ;; Author: Samuel Tonini . ;;; 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