Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
tag: release-0.1
Fetching contributors…

Cannot retrieve contributors at this time

221 lines (186 sloc) 7.365 kb
;;; elein.el --- running leiningen commands from emacs
;; Copyright (C) 2010, 2011 R.W van 't Veer
;; Author: R.W. van 't Veer
;; Created: 2 Aug 2010
;; Keywords: tools processes
;; Version: 0.1
;; URL: https://github.com/remvee/elein
;; 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 GNU Emacs; see the file COPYING. If not, write to the
;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
;; Boston, MA 02110-1301, USA.
;;; Commentary:
;; Provides support for running leiningen commands like swank and test.
;;; Code:
(eval-when-compile (require 'cl))
(defgroup elein nil
"running leiningen commands from emacs"
:prefix "elein-"
:group 'applications)
(defcustom elein-lein "lein"
"Leiningen 'lein' command."
:type 'string
:group 'elein)
(defcustom elein-standalone-swank-command "~/.lein/bin/swank-clojure"
"Leiningen 'swank-clojure' command for standalone execution."
:type 'string
:group 'elein)
(defcustom elein-swank-buffer-name "*elein-swank*"
"Buffer name for swank process."
:type 'string
:group 'elein)
(defcustom elein-swank-port 4005
"Swank port to listen."
:type 'integer
:group 'elein)
(defcustom elein-swank-host "127.0.0.1"
"Swank address to listen."
:type 'string
:group 'elein)
(defcustom elein-swank-options ":encoding '\"utf-8\"'"
"Swank options."
:type 'string
:group 'elein)
(defun elein-project-root ()
"Look for project.clj file to find project root."
(locate-dominating-file default-directory "project.clj"))
(defmacro elein-in-project-root (body)
"Wrap BODY to make `default-directory' the project root."
(let ((dir (gensym)))
`(let ((,dir (elein-project-root)))
(if ,dir
(let ((default-directory ,dir)) ,body)
(error "No leiningen project root found")))))
(defvar elein-task-alist nil
"Holds cached task list by directory name. The car of the
value is the mtime of the project.clj file and the cdr is the
task list itself.")
(defun elein-project-clj-mtime ()
"Get mtime from the project.clj in the current project."
(nth 5 (elein-in-project-root
(file-attributes "project.clj"))))
(defun elein-list-tasks ()
"Collect tasks for current project."
(let* ((root (elein-project-root))
(cached (assoc root elein-task-alist)))
(if (and cached (equal (elein-project-clj-mtime) (cadr cached)))
(cddr cached)
(let ((tasks (elein-in-project-root
(let ((output (shell-command-to-string (concat elein-lein " help")))
(result nil)
(offset 0))
(while (string-match "^ \\(.*\\)" output offset)
(setq result (cons (match-string 1 output) result))
(setq offset (match-end 0)))
(sort result (lambda (a b) (string< a b)))))))
(setq elein-task-alist (cons (cons root (cons (elein-project-clj-mtime)
tasks))
elein-task-alist))
tasks))))
(defun elein-swank-command ()
"Build lein swank command from customization variables."
(format "%s swank %d %s %s"
elein-lein
elein-swank-port
elein-swank-host
elein-swank-options))
(defun elein-standalone-swank-command ()
"Build projectless lein swank command."
(unless (file-exists-p (expand-file-name elein-standalone-swank-command))
(error "can not find %s; use 'lein install swank-clojure VERSION' to install it"
elein-standalone-swank-command))
(format "%s %d :host %s %s"
(expand-file-name elein-standalone-swank-command)
elein-swank-port
elein-swank-host
elein-swank-options))
(defun elein-burried-shell-command (command buffer)
"Same as `shell-command' but run process asynchronously, do not
show output and burry the given BUFFER."
(flet ((display-buffer (buffer-or-name &optional not-this-window frame) nil))
(bury-buffer buffer)
(shell-command (concat command "&") buffer)))
(defun elein-swank-process-filter (process output)
"Swank process filter to launch `slime-connect' when process is ready."
(with-current-buffer elein-swank-buffer-name (insert output))
(when (string-match "Connection opened on local port +\\([0-9]+\\)" output)
(slime-set-inferior-process
(slime-connect "localhost" (match-string 1 output))
process)
(set-process-filter process nil)))
;;;###autoload
(defun elein-swank (&optional prefix)
"Launch lein swank and connect slime to it. Interactively, a
PREFIX means launch a standalone swank session without a
project."
(interactive "P")
(let ((buffer (get-buffer-create elein-swank-buffer-name)))
(if prefix
(elein-burried-shell-command (elein-standalone-swank-command) buffer)
(elein-in-project-root
(elein-burried-shell-command (elein-swank-command) buffer)))
(set-process-filter (get-buffer-process buffer) #'elein-swank-process-filter)
(message "Starting swank..")))
;;;###autoload
(defun elein-kill-swank ()
"Kill swank process started by lein swank."
(interactive)
(let ((process (get-buffer-process "*elein-swank*")))
(when process
(ignore-errors (slime-quit-lisp))
(let ((timeout 10))
(while (and (> timeout 0)
(eql 'run (process-status process)))
(sit-for 1)
(decf timeout)))
(ignore-errors (kill-buffer "*elein-swank*")))))
;;;###autoload
(defun elein-reswank ()
"Kill current lisp, restart lein swank and connect slime to it."
(interactive)
(elein-kill-swank)
(elein-swank))
;;;###autoload
(defun elein-run-cmd (args)
"Run 'lein ARGS' using `compile' in the project root directory."
(interactive "sArguments: ")
(elein-in-project-root (compile (concat elein-lein " " args))))
;;;###autoload
(defun elein-run-task (task)
"Run 'lein TASK' using `compile' in the project root directory."
(interactive (list (completing-read "Task: " (elein-list-tasks))))
(elein-run-cmd task))
(defmacro elein-defun-run-task (task)
"Define shortcut function for `elein-run-task' with argument TASK."
`(defun ,(intern (concat "elein-" task)) ()
,(concat "Run 'lein " task "' in the project root directory.")
(interactive)
(elein-run-task ,task)))
;; define interactive elein-TASK commands for common tasks
(dolist (task '(classpath
clean
compile
deps
help
install
jar
new
pom
repl
test
uberjar
upgrade
version))
(eval `(elein-defun-run-task ,(symbol-name task))))
(provide 'elein)
;;; elein.el ends here
Jump to Line
Something went wrong with that request. Please try again.