branch: elpa/scala-mode commit 74e51944b4b1cd5e1fe445ccc94573d11314df29 Author: Heikki Vesalainen <heikkivesalai...@yahoo.com> Commit: Heikki Vesalainen <heikkivesalai...@yahoo.com>
sbt support for scala-mode2 --- README.md | 22 ++++++++++ scala-mode2-sbt.el | 119 +++++++++++++++++++++++++++++++++++++++++++++++++++++ scala-mode2.el | 1 + 3 files changed, 142 insertions(+) diff --git a/README.md b/README.md index 2189b32..04f6a69 100644 --- a/README.md +++ b/README.md @@ -293,6 +293,28 @@ The commands *forward-sexp* and *backward-sexp* ( **M-C-f**, **M-C-b** ) motion commands will move over reserved words, literals, ids and lists. +# sbt support + +To compile your code using sbt, you can use the sbt support. + +To begin with, configure *scala-sbt:sbt-command*. The default is +`sbt`, which is expected to be found on PATH or in your sbt project +root. + +Before you can compile using sbt, you must start it with the +*scala-sbt:start* command. For this to work, run the command from a +buffer that is within your project tree. + +After you have your sbt running, you can either switch to the `*sbt*` +buffer to interact with it, or send compile commands using the +*scala-sbt:command* command. The latter will always clear the `*sbt*` +buffer and reset errors before running the command. + +In you scala buffer, you can use the normal error-navigation commands +(*next-error*, **C-x \`**, *previous-error*) and in the *sbt* buffer +you can use the commands from the *compilation-mode* (see **C-h-f +compilation-mode** for help). + ## Keymap and other commands For the sake of customizability, scala-mode does not alter the default diff --git a/scala-mode2-sbt.el b/scala-mode2-sbt.el new file mode 100644 index 0000000..9bac026 --- /dev/null +++ b/scala-mode2-sbt.el @@ -0,0 +1,119 @@ +;;; scala-mode-lib.el - Major mode for editing scala, common functions +;;; Copyright (c) 2012 Heikki Vesalainen +;;; For information on the License, see the LICENSE file + +(require 'comint) + +(defcustom scala-sbt:sbt-command "sbt" + "The name of the sbt script to run. This must be either a +command on path or in the sbt root." + :type 'string + :group 'scala) + +(defcustom scala-sbt:default-command "test:compile" + "The default command to run with scala-sbt:command." + :type 'string + :group 'scala) + +(defvar scala-sbt:previous-command scala-sbt:default-command) + +(defun scala-sbt:find-root-impl (name-or-pred &optional dir best-root) + (when (null dir) (setq dir default-directory)) + (let ((parent (if (string-match locate-dominating-stop-dir-regexp dir) nil + (file-name-directory (directory-file-name dir))))) + (cond ((or (null parent) + (equal dir parent)) + best-root) + ((if (stringp name-or-pred) + (file-exists-p (expand-file-name name-or-pred dir)) + (funcall name-or-pred dir)) + (scala-sbt:find-root-impl name-or-pred parent dir)) + ('t + (scala-sbt:find-root-impl name-or-pred parent best-root))))) + +(defun scala-sbt:find-root () + "Starting from the current default-directory, find the top-most +parent directory that is an sbt root. An sbt root directory is +identified by the following rules: + + - a directory containing a 'project/build.properties' in it. + + - a directory that contains a file matching one of the patterns + '*.sbt' or 'project/*.scala' file in it. + +The first rule is applied first and the second is used only if it +fails to find the sbt root." + (or + (scala-sbt:find-root-impl "project/build.properties") + (scala-sbt:find-root-impl + (lambda (dir) + (or (directory-files dir nil ".+\\.sbt$") + (and (file-exists-p (concat dir "project")) + (directory-files (concat dir "project") nil ".+\\.scala$"))))))) + +(defun scala-sbt:start () + "Start sbt in a buffer called *sbt*, stops any existing sbt +running in the same buffer." + (interactive) + (let ((project-root (scala-sbt:find-root)) + (compilation-buffer-name-function (lambda (m) "*sbt*"))) + (when (null project-root) + (error "Could not find project root, type `C-h f scala-sbt:find-root` for help.")) + + (when (not (or (executable-find scala-sbt:sbt-command) + (file-executable-p (concat project-root scala-sbt:sbt-command)))) + (error "Could not find %s in %s or on PATH" scala-sbt:sbt-command project-root)) + + ;; kill existing sbt + (when (get-buffer "*sbt*") (kill-buffer "*sbt*")) + + ;; start new sbt + (with-current-buffer (get-buffer-create "*sbt*") + (display-buffer (current-buffer)) + (buffer-disable-undo) + (cd project-root) + (comint-mode) + (compilation-shell-minor-mode) + (setq compilation-error-regexp-alist + `((,(rx line-start + ?[ (or (group "error") (group "warn") (group "info")) ?] + " " (group (1+ (not (any ": ")))) + ?: (group (1+ digit)) ?:) + 4 5 nil (2 . 3)))) + + (comint-exec (current-buffer) "sbt" scala-sbt:sbt-command nil nil)))) + +(defun scala-sbt:command (&optional command) + "Send a command to the sbt running in the '*sbt*' +buffer. Prompts for the command to send when in interactive +mode. + +This command does the following: + - displays the buffer without moving focus to it + - erases the buffer + - forgets about compilation errors + +The command is most usefull for running a compilation command +that outputs errors." + (interactive) + + (when (not (get-buffer "*sbt*")) + (message "Running scala-sbt:start ...") + (scala-sbt:start) + (sit-for 5)) + + (when (null command) + (setq command (read-string (format "Command to run (default %s): " + scala-sbt:previous-command) + nil nil scala-sbt:previous-command))) + + (save-some-buffers) + (let ((buffer (get-buffer "*sbt*"))) + (with-current-buffer buffer + (display-buffer (current-buffer)) + (compilation-forget-errors) + (erase-buffer) + (comint-send-string buffer (concat "\n" command "\n"))) + (setq scala-sbt:previous-command command))) + +(provide 'scala-mode2-sbt) diff --git a/scala-mode2.el b/scala-mode2.el index 42f3b40..174c7c7 100644 --- a/scala-mode2.el +++ b/scala-mode2.el @@ -11,6 +11,7 @@ (require 'scala-mode2-indent) (require 'scala-mode2-fontlock) (require 'scala-mode2-map) +(require 'scala-mode2-sbt) ;; Tested only for emacs 24 (unless (<= 24 emacs-major-version)