;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; ;;; ghc-process.el ;;; ;; Author: Kazu Yamamoto ;; Created: Mar 9, 2014 ;;; Code: (require 'ghc-func) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defvar ghc-process-running nil) (defvar-local ghc-process-process-name nil) (defvar-local ghc-process-original-buffer nil) (defvar-local ghc-process-original-file nil) (defvar-local ghc-process-callback nil) (defvar-local ghc-process-hook nil) (defvar-local ghc-process-root nil) (defvar ghc-command "ghc-mod") (defvar ghc-error-buffer "*GHC Error*") ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun ghc-get-project-root () (ghc-run-ghc-mod '("root"))) (defun ghc-with-process (cmd callback &optional hook1 hook2) (let ((root (ghc-get-project-root))) (unless ghc-process-process-name (setq ghc-process-process-name root)) (when (and ghc-process-process-name (not ghc-process-running)) (setq ghc-process-running t) (if hook1 (funcall hook1)) (let* ((cbuf (current-buffer)) (name ghc-process-process-name) (buf (get-buffer-create (concat " ghc-mod:" name))) (file (buffer-file-name)) (cpro (get-process name))) (ghc-with-current-buffer buf (setq ghc-process-original-buffer cbuf) (setq ghc-process-original-file file) (setq ghc-process-callback callback) (setq ghc-process-hook hook2) (setq ghc-process-root root) (erase-buffer) (let ((pro (ghc-get-process cpro name buf))) (process-send-string pro cmd) (when ghc-debug (ghc-with-debug-buffer (insert (format "%% %s" cmd)))) pro)))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun ghc-get-process (cpro name buf) (cond ((not cpro) (ghc-start-process name buf)) ((not (eq (process-status cpro) 'run)) (delete-process cpro) (ghc-start-process name buf)) (t cpro))) (defun ghc-start-process (name buf) (let* ((opts (append '("-b" "\n" "-l" "--line-prefix=O: ,E: ") (ghc-make-ghc-options) '("legacy-interactive"))) (pro (apply 'start-file-process name buf ghc-command opts))) (set-process-filter pro 'ghc-process-filter) (set-process-sentinel pro 'ghc-process-sentinel) (set-process-query-on-exit-flag pro nil) pro)) (defun ghc-process-filter (process string) (let* ((pbuf (process-buffer process)) (tbufname (concat " tmp " (buffer-name pbuf))) tbuf) (if (not (get-buffer pbuf)) (setq ghc-process-running nil) ;; just in case (ghc-with-current-buffer pbuf (when ghc-debug (ghc-with-debug-buffer (insert string))) (with-current-buffer (get-buffer-create tbufname) (setq tbuf (current-buffer)) (goto-char (point-max)) (insert string) (goto-char (point-min)) (let ((cont t) end out) (while (and cont (not (eobp))) (cond ((looking-at "^O: ") (setq out t)) ((looking-at "^E: ") (setq out nil)) (t (setq cont nil))) (when cont (forward-line) (unless (bolp) (setq cont nil))) (when cont (delete-region 1 4) (setq end (point)) (if out (with-current-buffer pbuf (goto-char (point-max)) (insert-buffer-substring tbuf 1 end)) (with-current-buffer (get-buffer-create ghc-error-buffer) (setq buffer-read-only t) (let* ((buffer-read-only nil) (inhibit-read-only t) (cbuf (current-buffer)) cwin) (unless (get-buffer-window cbuf) (display-buffer cbuf)) (setq cwin (get-buffer-window cbuf)) (goto-char (point-max)) (insert-buffer-substring tbuf 1 end) (set-buffer-modified-p nil) (unless (pos-visible-in-window-p (point) cwin) (with-selected-window cwin (scroll-up 2)))) (redisplay))) (delete-region 1 end))))) (goto-char (point-max)) (forward-line -1) (cond ((looking-at "^OK$") (if ghc-process-hook (funcall ghc-process-hook)) (goto-char (point-min)) (funcall ghc-process-callback 'ok) (setq ghc-process-running nil)) ((looking-at "^NG ") (funcall ghc-process-callback 'ng) (setq ghc-process-running nil))))))) (defun ghc-process-sentinel (process event) (setq ghc-process-running nil)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defvar ghc-process-rendezvous nil) (defvar ghc-process-num-of-results nil) (defvar ghc-process-results nil) (defun ghc-sync-process (cmd &optional n hook) (unless ghc-process-running (setq ghc-process-rendezvous nil) (setq ghc-process-results nil) (setq ghc-process-num-of-results (or n 1)) (let ((pro (ghc-with-process cmd 'ghc-process-callback nil hook))) ;; ghc-process-running is now t. ;; But if the process exits abnormally, it is set to nil. (condition-case nil (let ((inhibit-quit nil)) (while (and (null ghc-process-rendezvous) ghc-process-running) (accept-process-output pro 0.1 nil t))) (quit (setq ghc-process-running nil)))) ghc-process-results)) (defun ghc-process-callback (status) (cond ((eq status 'ok) (let* ((n ghc-process-num-of-results) (ret (if (= n 1) (ghc-read-lisp-this-buffer) (ghc-read-lisp-list-this-buffer n)))) (setq ghc-process-results ret))) (t (setq ghc-process-results nil))) (setq ghc-process-num-of-results nil) (setq ghc-process-rendezvous t)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun ghc-kill-process () (interactive) (let* ((name ghc-process-process-name) (cpro (if name (get-process name)))) (if (not cpro) (message "No process") (delete-process cpro) (message "A process was killed")))) (provide 'ghc-process)