|
|
;;; alchemist-server.el --- Interface to the Alchemist Elixir server. -*- lexical-binding: t -*-
|
|
|
|
|
|
;; 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:
|
|
|
|
|
|
;; Interface to the Alchemist Elixir server.
|
|
|
|
|
|
;;; Code:
|
|
|
|
|
|
(require 'alchemist-execute)
|
|
|
|
|
|
(defgroup alchemist-server nil
|
|
|
"Interface to the Alchemist Elixir server."
|
|
|
:prefix "alchemist-server-"
|
|
|
:group 'alchemist)
|
|
|
|
|
|
(defvar alchemist-server-processes '()
|
|
|
"Store running Alchemist server processes.")
|
|
|
|
|
|
(defvar alchemist-server-env "dev"
|
|
|
"Default environment in for the Alchemist server.")
|
|
|
|
|
|
(defvar alchemist-server-envs '("dev" "prod" "test" "shared")
|
|
|
"List of available Alchemist server environments.")
|
|
|
|
|
|
(defconst alchemist-server
|
|
|
(concat (file-name-directory load-file-name) "alchemist-server/run.exs")
|
|
|
"Path to the Alchemist server file.")
|
|
|
|
|
|
(defconst alchemist-server-command
|
|
|
(format "%s %s %s"
|
|
|
alchemist-execute-command
|
|
|
alchemist-server
|
|
|
alchemist-server-env)
|
|
|
"Alchemist server command.")
|
|
|
|
|
|
(defconst alchemist-server-codes '((server-eval "EVAL")
|
|
|
(server-defl "DEFL")
|
|
|
(server-info "INFO")
|
|
|
(server-docl "DOCL")
|
|
|
(server-comp "COMP"))
|
|
|
"Alchemist server API codes.")
|
|
|
|
|
|
(defun alchemist-server-start (env)
|
|
|
"Start alchemist server for the current mix project in specific ENV.
|
|
|
|
|
|
If a server already running, the current one will be killed and new one
|
|
|
will be started instead."
|
|
|
(interactive (list
|
|
|
(completing-read (format "(Alchemist-Server) run in environment: (default: %s) " alchemist-server-env)
|
|
|
alchemist-server-envs nil nil nil)))
|
|
|
(when (alchemist-server-process-p)
|
|
|
(kill-process (alchemist-server-process)))
|
|
|
(alchemist-server-start-in-env env))
|
|
|
|
|
|
(defun alchemist-server-start-if-not-running ()
|
|
|
"Start a new Alchemist server if not already running.
|
|
|
|
|
|
An Alchemist server will be started for the current Elixir mix project."
|
|
|
(unless (alchemist-server-process-p)
|
|
|
(alchemist-server-start-in-env alchemist-server-env)))
|
|
|
|
|
|
(defun alchemist-server-start-in-env (env)
|
|
|
"Start an Alchemist server with the ENV."
|
|
|
(let* ((process-name (alchemist-server-process-name))
|
|
|
(default-directory (if (string= process-name "alchemist-server")
|
|
|
default-directory
|
|
|
process-name))
|
|
|
(server-command (format "elixir %s %s" alchemist-server env))
|
|
|
(process (start-process-shell-command process-name "*alchemist-server*" server-command)))
|
|
|
(set-process-query-on-exit-flag process nil)
|
|
|
(alchemist-server--store-process process)))
|
|
|
|
|
|
(defun alchemist-server--store-process (process)
|
|
|
"Store PROCESS in `alchemist-server-processes'."
|
|
|
(let ((process-name (alchemist-server-process-name)))
|
|
|
(if (cdr (assoc process-name alchemist-server-processes))
|
|
|
(setq alchemist-server-processes
|
|
|
(delq (assoc process-name alchemist-server-processes) alchemist-server-processes)))
|
|
|
(add-to-list 'alchemist-server-processes (cons process-name process))))
|
|
|
|
|
|
(defun alchemist-server-process-p ()
|
|
|
"Return non-nil if a process for the current
|
|
|
Elixir mix project is live."
|
|
|
(process-live-p (alchemist-server-process)))
|
|
|
|
|
|
(defun alchemist-server-process ()
|
|
|
"Return process for the current Elixir mix project."
|
|
|
(cdr (assoc (alchemist-server-process-name) alchemist-server-processes)))
|
|
|
|
|
|
(defun alchemist-server-process-name ()
|
|
|
"Return process name for the current Elixir mix project."
|
|
|
(let* ((process-name (if (alchemist-project-elixir-p)
|
|
|
"alchemist-server"
|
|
|
(alchemist-project-root)))
|
|
|
(process-name (if process-name
|
|
|
process-name
|
|
|
"alchemist-server")))
|
|
|
process-name))
|
|
|
|
|
|
(defun alchemist-server-api-code (symbol)
|
|
|
"Return Alchemist server API code for SYMBOL."
|
|
|
(car (cdr (assoc symbol alchemist-server-codes))))
|
|
|
|
|
|
(defconst alchemist-server-code-end-marker-regex
|
|
|
(format "END-OF-\\(%s\\|%s\\|%s\\|%s\\|%s\\)$"
|
|
|
(alchemist-server-api-code 'server-eval)
|
|
|
(alchemist-server-api-code 'server-defl)
|
|
|
(alchemist-server-api-code 'server-info)
|
|
|
(alchemist-server-api-code 'server-docl)
|
|
|
(alchemist-server-api-code 'server-comp))
|
|
|
"Regular expression to identify Alchemist server API end markers.")
|
|
|
|
|
|
(defun alchemist-server-contains-end-marker-p (string)
|
|
|
"Return non-nil if STRING contain an Alchemist server API end marker."
|
|
|
(string-match-p alchemist-server-code-end-marker-regex string))
|
|
|
|
|
|
(defun alchemist-server-build-request-string (code &optional args)
|
|
|
"Build Alchemist server request string for CODE.
|
|
|
|
|
|
If ARGS available add them to the request string."
|
|
|
(let* ((code (car (cdr (assoc code alchemist-server-codes)))))
|
|
|
(if args
|
|
|
(format "%s %s\n" code args)
|
|
|
(format "%s\n" code))))
|
|
|
|
|
|
(defun alchemist-server-prepare-filter-output (output)
|
|
|
"Clean OUTPUT by remove Alchemist server API end markes."
|
|
|
(let* ((output (apply #'concat (reverse output)))
|
|
|
(output (replace-regexp-in-string alchemist-server-code-end-marker-regex "" output))
|
|
|
(output (replace-regexp-in-string "\n+$" "" output)))
|
|
|
output))
|
|
|
|
|
|
(defun alchemist-server-send-request (string filter)
|
|
|
"Send STRING to Alchemist server API and set FILTER to process."
|
|
|
(alchemist-server-start-if-not-running)
|
|
|
(set-process-filter (alchemist-server-process) filter)
|
|
|
(process-send-string (alchemist-server-process) string))
|
|
|
|
|
|
(defun alchemist-server-goto (args filter)
|
|
|
"Make an Alchemist server source request with ARGS.
|
|
|
|
|
|
Process server respond with FILTER."
|
|
|
(alchemist-server-start-if-not-running)
|
|
|
(alchemist-server-send-request (alchemist-server-build-request-string 'server-defl args) filter))
|
|
|
|
|
|
(defun alchemist-server-info (args filter)
|
|
|
"Make an Alchemist server mix request.
|
|
|
|
|
|
Process server respond with FILTER."
|
|
|
(alchemist-server-start-if-not-running)
|
|
|
(alchemist-server-send-request (alchemist-server-build-request-string 'server-info args) filter))
|
|
|
|
|
|
(defun alchemist-server-help-with-modules (filter)
|
|
|
"Make an Alchemist server modules request.
|
|
|
|
|
|
Process server respond with FILTER."
|
|
|
(alchemist-server-start-if-not-running)
|
|
|
(alchemist-server-send-request (alchemist-server-build-request-string 'server-info) filter))
|
|
|
|
|
|
(defun alchemist-server-help (args filter)
|
|
|
"Make an Alchemist server doc request with ARGS.
|
|
|
|
|
|
Process server respond with FILTER."
|
|
|
(alchemist-server-start-if-not-running)
|
|
|
(alchemist-server-send-request (alchemist-server-build-request-string 'server-docl args) filter))
|
|
|
|
|
|
(defun alchemist-server-eval (args filter)
|
|
|
"Make an Alchemist server evaluate request with FILE.
|
|
|
|
|
|
Process server respond with FILTER."
|
|
|
(alchemist-server-start-if-not-running)
|
|
|
(alchemist-server-send-request (alchemist-server-build-request-string 'server-eval args) filter))
|
|
|
|
|
|
(defun alchemist-server-complete-candidates (args filter)
|
|
|
"Make an Alchemist server complete request with ARGS.
|
|
|
|
|
|
Process server respond with FILTER."
|
|
|
(alchemist-server-start-if-not-running)
|
|
|
(alchemist-server-send-request (alchemist-server-build-request-string 'server-comp args) filter))
|
|
|
|
|
|
(defun alchemist-server-status ()
|
|
|
"Report the server status for the current Elixir project."
|
|
|
(interactive)
|
|
|
(message "Alchemist-Server-Status: [Project: %s Status: %s]"
|
|
|
(alchemist-server-process-name)
|
|
|
(if (alchemist-server-process-p)
|
|
|
"Connected"
|
|
|
"Not Connected")))
|
|
|
|
|
|
(provide 'alchemist-server)
|
|
|
|
|
|
;;; alchemist-server.el ends here
|