Bloop integration in Emacs

Some quick and hacky integration of Scala bloop with Emacs, using ammonite as the console and projectile for compilation and testing.

;;; bloop --- bloop minor mode
;; Author: Philipp Fehre <>
;; Keywords: scala, bloop, tools, convenience
;;; Commentary:
;; Helpers to integrate better with bloop, inspired by emacs-bloop
;; C-c M-j jack-in a bloop project running a new Ammonite REPL buffer
;; C-c M-z switch to an active Ammonite REPL
;; C-c b c Compile a bloop project backed by projectile-compile-project
;; C-c b t Test a bloop project backed by projectile-test-project
;; C-c b r Run a bloop project
;; Changelog:
;; - 25/8/2020 - Added run command mapping bloop-run (C-c b r)
;; - 1/5/2020 - initial working version
;;; Code:
(require 'projectile)
(require 'scala-mode)
(require 'ammonite-term-repl)
(require 's)
(defgroup bloop nil
"Bloop integration for emacs"
:group 'tools
:group 'convenience
:link '(url-link :tag "Gist" ""))
(defcustom bloop-program-name "bloop"
"Program used to run bloop commands, default to whatever is in the path."
:type 'string
:group 'bloop)
(defcustom bloop-reporter "scalac"
"Either bloop or scalac.
The main difference is that bloop shows errors in reverse order.
Emacs generally assumes the first error in the output is the most
relevant so the scalac reporter will most likely be preferred.
This is used for test and compile."
:type 'string
:group 'bloop)
(defun bloop--command (&rest args)
"Build a bloop command for ARGS."
(s-join " " (cons bloop-program-name args)))
(defun bloop--available-projects ()
"Get a list of currently available projects from bloop."
(projectile-with-default-dir (projectile-project-root)
(let ((projects-string (shell-command-to-string (bloop--command "projects"))))
(split-string projects-string))))
(defun bloop-switch-to-ammonite ()
"Switch to the running Ammonite REPL."
(if-let ((ammonite-buffer (get-buffer ammonite-term-repl-buffer-name)))
(switch-to-buffer ammonite-buffer)
(message "Ammonite is not running try C-c M-j to start an Ammonite REPL for bloop.")))
(defun bloop-run-ammonite (project)
"Run Ammonite for a bloop PROJECT."
(interactive (list (completing-read "Run Ammonite REPL for project: " (bloop--available-projects))))
(projectile-with-default-dir (projectile-project-root)
(let ((ammonite-term-repl-program bloop-program-name)
(ammonite-term-repl-program-args (list "console" project)))
(defun bloop-compile (project)
"Compile a bloop PROJECT."
(interactive (list (completing-read "Compile bloop project: " (bloop--available-projects))))
(let ((command (bloop--command "compile" project)))
(projectile--run-project-cmd command projectile-compilation-cmd-map
:show-prompt 't
:prompt-prefix "Compile command: "
:save-buffers t)))
(defun bloop-test (project)
"Test a bloop PROJECT."
(interactive (list (completing-read "Test bloop project: " (bloop--available-projects))))
(let ((test-command (bloop--command "test" "--reporter" bloop-reporter project)))
(projectile--run-project-cmd test-command projectile-test-cmd-map
:show-prompt 't
:prompt-prefix "Test command: "
:save-buffers t)))
(defun bloop-run (project)
"Run a bloop PROJECT."
(interactive (list (completing-read "Run bloop project: " (bloop--available-projects))))
(let ((run-command (bloop--command "run" project)))
(projectile--run-project-cmd run-command projectile-run-cmd-map
:show-prompt 't
:prompt-prefix "Run command: "
:save-buffers t)))
(define-minor-mode bloop-mode
"Bloop integration for emacs."
:lighter " bloop"
:keymap (let ((map (make-sparse-keymap)))
(define-key map (kbd "C-c M-j") #'bloop-run-ammonite)
(define-key map (kbd "C-c M-z") #'bloop-switch-to-ammonite)
(define-key map (kbd "C-c b c") #'bloop-compile)
(define-key map (kbd "C-c b t") #'bloop-test)
(define-key map (kbd "C-c b r") #'bloop-run)
(add-hook 'scala-mode-hook 'bloop-mode)
(provide 'bloop)
;;; bloop.el ends here
view raw bloop.el hosted with ❤ by GitHub

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.