branch: master commit 8d15938d443e0358483932054b407c802ca77720 Author: Jackson Ray Hamilton <jack...@jacksonrayhamilton.com> Commit: Jackson Ray Hamilton <jack...@jacksonrayhamilton.com>
Cleaner server implementation. --- context-coloring.el | 153 ++++++++++++++++++++++++++++----------------------- 1 files changed, 85 insertions(+), 68 deletions(-) diff --git a/context-coloring.el b/context-coloring.el index b1f5963..0034761 100644 --- a/context-coloring.el +++ b/context-coloring.el @@ -754,45 +754,44 @@ to colorize the buffer." (defvar-local context-coloring-scopifier-process nil "The single scopifier process that can be running.") -(defun context-coloring-kill-scopifier () - "Kill the currently-running scopifier process." - (cond - (context-coloring-scopifier-cancel-function +(defun context-coloring-cancel-scopification () + "Stop the currently-running scopifier from scopifying." + (when context-coloring-scopifier-cancel-function (funcall context-coloring-scopifier-cancel-function) (setq context-coloring-scopifier-cancel-function nil)) - ;; ((not (null context-coloring-scopifier-process)) - ;; (delete-process context-coloring-scopifier-process) - ;; (setq context-coloring-scopifier-process nil)) - )) - -(defun context-coloring-scopify-shell-command (command callback) - "Invoke a scopifier via COMMAND, read its response -asynchronously and invoke CALLBACK with its output." - - ;; Prior running tokenization is implicitly obsolete if this function is - ;; called. - (context-coloring-kill-scopifier) - - ;; Start the process. - (setq context-coloring-scopifier-process - (start-process-shell-command "scopifier" nil command)) - - (let ((output "")) - + (when (not (null context-coloring-scopifier-process)) + (delete-process context-coloring-scopifier-process) + (setq context-coloring-scopifier-process nil))) + +(defun context-coloring-shell-command (command callback) + "Invoke COMMAND, read its response asynchronously and invoke +CALLBACK with its output. Return the command process." + (let ((process (start-process-shell-command "context-coloring-process" nil command)) + (output "")) ;; The process may produce output in multiple chunks. This filter ;; accumulates the chunks into a message. (set-process-filter - context-coloring-scopifier-process + process (lambda (_process chunk) (setq output (concat output chunk)))) - ;; When the process's message is complete, this sentinel parses it as JSON ;; and applies the tokens to the buffer. (set-process-sentinel - context-coloring-scopifier-process + process (lambda (_process event) (when (equal "finished\n" event) - (funcall callback output)))))) + (funcall callback output)))) + process)) + +(defun context-coloring-scopify-shell-command (command callback) + "Invoke a scopifier via COMMAND, read its response +asynchronously and invoke CALLBACK with its output." + ;; Prior running tokenization is implicitly obsolete if this function is + ;; called. + (context-coloring-cancel-scopification) + ;; Start the process. + (setq context-coloring-scopifier-process + (context-coloring-shell-command command callback))) (defun context-coloring-send-buffer-to-scopifier () "Give the scopifier process its input so it can begin @@ -803,42 +802,36 @@ scopifying." (process-send-eof context-coloring-scopifier-process)) -(defcustom context-coloring-js-scopifier-server-host "localhost" - "Host for the JavaScript scopifier server." - :group 'context-coloring) - -(defcustom context-coloring-js-scopifier-server-port 6969 - "Port for the JavaScript scopifier server." - :group 'context-coloring) - -(defun context-coloring-js-start-scopifier-server (callback) - (let (stream) +(defun context-coloring-start-scopifier-server (command host port callback) + (let* ((connect + (lambda () + (let ((stream (open-network-stream "context-coloring-stream" nil host port))) + (funcall callback stream))))) + ;; Try to connect in case a server is running, otherwise start one. (condition-case nil (progn - (setq stream (open-network-stream - "scopifier" nil - context-coloring-js-scopifier-server-host - context-coloring-js-scopifier-server-port)) - (funcall callback stream)) + (funcall connect)) (error - (context-coloring-scopify-shell-command - (context-coloring-join - (list "scopifier" - "--server" - "--host" context-coloring-js-scopifier-server-host - "--post" (number-to-string - context-coloring-js-scopifier-server-port)) - " ") - (lambda (_output) - (setq stream (open-network-stream - "scopifier" nil - context-coloring-js-scopifier-server-host - context-coloring-js-scopifier-server-port)) - (funcall callback stream))))))) - -(defun context-coloring-send-buffer-to-scopifier-server (callback) - ;; TODO: Dispatch configuration. - (context-coloring-js-start-scopifier-server + (let ((server (start-process-shell-command + "context-coloring-scopifier-server" nil + (context-coloring-join + (list command + "--server" + "--host" host + "--port" (number-to-string port)) + " "))) + (output "")) + ;; Connect as soon as the "listening" message is printed. + (set-process-filter + server + (lambda (_process chunk) + (setq output (concat output chunk)) + (when (string-match-p (format "^Scopifier listening at %s:%s$" host port) output) + (funcall connect))))))))) + +(defun context-coloring-send-buffer-to-scopifier-server (command host port callback) + (context-coloring-start-scopifier-server + command host port (lambda (process) (let* ((body (buffer-substring-no-properties (point-min) (point-max))) (header (concat "POST / HTTP/1.0\r\n" @@ -868,7 +861,21 @@ scopifying." "Cancel this scopification." (setq active nil))))))) -;; TODO: Maybe have a function dedicated to servers instead. +(defun context-coloring-scopify-and-colorize-server (command host port &optional callback) + "Contact or start a scopifier server via COMMAND at HOST and +PORT with the current buffer's contents, read the scopifier's +response asynchronously and apply a parsed list of tokens to +`context-coloring-apply-tokens'. + +Invoke CALLBACK when complete." + (let ((buffer (current-buffer))) + (context-coloring-send-buffer-to-scopifier-server + command host port + (lambda (output) + (with-current-buffer buffer + (context-coloring-parse-array output)) + (when callback (funcall callback)))))) + (defun context-coloring-scopify-and-colorize (command &optional callback) "Invoke a scopifier via COMMAND with the current buffer's contents, read the scopifier's response asynchronously and apply a parsed @@ -876,12 +883,14 @@ list of tokens to `context-coloring-apply-tokens'. Invoke CALLBACK when complete." (let ((buffer (current-buffer))) - (context-coloring-send-buffer-to-scopifier-server + (context-coloring-scopify-shell-command + command (lambda (output) (with-current-buffer buffer (context-coloring-parse-array output)) (setq context-coloring-scopifier-process nil) - (when callback (funcall callback)))))) + (when callback (funcall callback))))) + (context-coloring-send-buffer-to-scopifier)) ;;; Dispatch @@ -974,7 +983,7 @@ used.") (defun context-coloring-change-function (_start _end _length) "Register a change so that a buffer can be colorized soon." ;; Tokenization is obsolete if there was a change. - (context-coloring-kill-scopifier) + (context-coloring-cancel-scopification) (setq context-coloring-changed t)) (defun context-coloring-maybe-colorize (buffer) @@ -1028,7 +1037,7 @@ version number required for the current major mode." (when dispatch (let ((version (plist-get dispatch :version)) (command (plist-get dispatch :command))) - (context-coloring-scopify-shell-command + (context-coloring-shell-command (context-coloring-join (list command "--version") " ") (lambda (output) (if (context-coloring-check-version version output) @@ -1409,7 +1418,7 @@ Supported modes: `js-mode', `js3-mode', `emacs-lisp-mode'" (defun context-coloring-teardown-idle-change-detection () "Teardown idle change detection." - (context-coloring-kill-scopifier) + (context-coloring-cancel-scopification) (when context-coloring-colorize-idle-timer (cancel-timer context-coloring-colorize-idle-timer)) (remove-hook @@ -1425,7 +1434,9 @@ Supported modes: `js-mode', `js3-mode', `emacs-lisp-mode'" :modes '(js-mode js3-mode) :executable "scopifier" :command "scopifier" - :version "v1.1.1") + :version "v1.1.1" ; TODO: v1.2.0 + :host "localhost" + :port 6969) (context-coloring-define-dispatch 'javascript-js2 @@ -1454,6 +1465,8 @@ elisp tracks, and asynchronously for shell command tracks." (let* ((dispatch (context-coloring-get-dispatch-for-mode major-mode)) (colorizer (plist-get dispatch :colorizer)) (command (plist-get dispatch :command)) + (host (plist-get dispatch :host)) + (port (plist-get dispatch :port)) interrupted-p) (cond (colorizer @@ -1466,7 +1479,11 @@ elisp tracks, and asynchronously for shell command tracks." (t (when callback (funcall callback))))) (command - (context-coloring-scopify-and-colorize command callback))))) + (cond + ((and host port) + (context-coloring-scopify-and-colorize-server command host port callback)) + (t + (context-coloring-scopify-and-colorize command callback))))))) ;;; Minor mode