summary refs log tree commit diff
diff options
context:
space:
mode:
authorFranck Cuny <franckcuny@gmail.com>2016-03-10 17:47:49 -0800
committerFranck Cuny <franckcuny@gmail.com>2016-03-10 17:47:49 -0800
commit5a2f209d0ab366ae99d62c5bd9c5b23b78885736 (patch)
treeeeca3fe257b869a7c3b4e0890af98e80abf5ae02
parentAdd README.md, LICENSE.txt (diff)
downloadpants.el-5a2f209d0ab366ae99d62c5bd9c5b23b78885736.tar.gz
Simple front-end to the `pants' command.
This is the first working version of the front-end to the `pants`
command.
-rw-r--r--README.md59
-rw-r--r--pants.el154
2 files changed, 212 insertions, 1 deletions
diff --git a/README.md b/README.md
index 7a3603d..6be5f70 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,58 @@
-# pants.el
\ No newline at end of file
+# pants.el
+
+## Description
+
+## Install
+
+Get a copy of the repository:
+
+```sh
+git clone git@github.com:franckcuny/pants.el.git
+```
+
+Then update your emacs' configuration:
+
+```elisp
+(load-file "~/src/pants.el/pants.el")
+
+(use-package pants
+  :bind (("C-c b" . pants-find-build-file)
+         ("C-c r" . pants-run-binary)
+         ("C-c t" . pants-run-test))
+  :config
+  (progn
+    (setq pants-source-tree-root "/Users/fcuny/src/source"
+          pants-bury-compilation-buffer t)))
+```
+
+## Configuration
+
+There's a few variables that you can set:
+
+* **pants-source-tree-root**: Path to the repository.
+
+* **pants-ini**: Name of the  pants.ini file to use (default is `pants.ini`).
+
+* **pants-exec-name**: Path to the pants executable in the repository (default is `pants`)
+
+* **pants-build-file**: Name of the BUILD file to look for
+
+* **pants-bury-compilation-buffer**: Set to true if you want to bury the compilation buffer after running successfully a command
+
+## Usage
+
+### Go to the closest BUILD file
+
+Do `m-x pants-find-build-file`.
+
+### Run a binary target
+
+Do `m-x pants-run-binary`. It will present a list of targets and let you select which one to run.
+
+### Run a test target
+
+Do `m-x pants-run-test`. It will present a list of targets and let you select which one to run.
+
+### Jump to a REPL
+
+Do `m-x pants-run-python-repl`. It will present a list of targets and create a REPL.
diff --git a/pants.el b/pants.el
new file mode 100644
index 0000000..0c08ed5
--- /dev/null
+++ b/pants.el
@@ -0,0 +1,154 @@
+;;; pants.el --- A frontend for pants.
+
+;; Package-Requires: ((helm "1.5.3"))
+
+;;; Code:
+(require 'compile)
+(require 'helm)
+(require 'python)
+
+(defcustom pants-source-tree-root nil
+  "Path to the repository.")
+
+(defcustom pants-ini "pants.ini"
+  "Path to the pants.ini file to use. This variable must be set.")
+
+(defcustom pants-exec-name "pants"
+  "Path to the pants executable. This variable must be set.")
+
+(defcustom pants-exec-args "--no-colors"
+  "Arguments to the pants executable. Default is '--no-colors'")
+
+(defcustom pants-build-file "BUILD"
+  "Name of the build files. Default is 'BUILD'")
+
+(defcustom pants-bury-compilation-buffer nil
+  "Set this variable to true to bury the compilation buffer if there's no error.")
+
+(defvar *pants-compilation-buffer* "*pants-compilation-buffer*")
+
+(defun pants--find-directory-containing-build-file (file)
+  "Finds the directory containing the build file next to a give file."
+  (let ((root nil)
+        try)
+    (while (not (or root
+                    (null file)
+                    (string-match locate-dominating-stop-dir-regexp file)))
+      (setq try (if (stringp pants-build-file)
+                   (file-exists-p (expand-file-name pants-build-file file))))
+      (cond (try (setq root file))
+            ((equal file (setq file (file-name-directory
+                                     (directory-file-name file))))
+             (setq file nil))))
+    (and root (expand-file-name (file-name-as-directory root)))))
+
+(defun pants--get-source-tree ()
+  "Returns the name of the directory for the source tree, with a trailing slash."
+  (file-name-as-directory pants-source-tree-root))
+
+(defun pants--build-command ()
+  "Returns the complete command to run."
+  (format "%s%s --config-override=%s%s %s"
+          (pants--get-source-tree) pants-exec-name (pants--get-source-tree) pants-ini pants-exec-args))
+
+(defun pants--python-repl-action (target)
+  "Starts a Python REPL."
+  (let ((pants-repl-command (format "%s -q repl %s" (pants--build-command) target)))
+    (set (make-local-variable 'default-directory) pants-source-tree-root)
+    (set (make-local-variable 'python-shell-exec-path) '(pants-source-tree-root))
+    (set (make-local-variable 'python-shell-interpreter) pants-source-tree-root)
+    (set (make-local-variable 'python-shell-interpreter-args) pants-repl-command)
+    (python-shell-switch-to-shell)))
+
+(defun pants--build-action (target)
+  "Executes the `binary' command"
+  (let ((compile-command (format "%s binary %s" (pants--build-command) target)))
+    (pants--compile compile-command)))
+
+(defun pants--test-action (target)
+  "Executes the `test' command"
+  (let ((compile-command (format "%s test %s" (pants--build-command) target)))
+    (pants--compile compile-command)))
+
+(defun pants--compilation-setup ()
+  "Sets the local configuration for the compile buffer"
+  (set (make-local-variable 'compilation-scroll-output) 'first-error)
+  (set (make-local-variable 'compilation-exit-message-function)
+       (lambda (status code msg)
+         (when (and
+                (eq status 'exit)
+                (zerop code)
+                (and pants-bury-compilation-buffer t)
+                (get-buffer *pants-compilation-buffer*))
+           (bury-buffer)
+           (delete-window (get-buffer-window (get-buffer *pants-compilation-buffer*))))
+         (cons msg code))))
+
+(defun pants--compile (command)
+  "Executes the compilation"
+  (let ((compilation-buffer-name-function (lambda (arg) *pants-compilation-buffer*)))
+    (compilation-start command 'pants-mode)))
+
+(defun pants--build-target-list (file action)
+  "Generates a list of existing targets"
+  (let ((build-command (format "%s list %s:" (pants--build-command) file))
+        targets target)
+    (set (make-local-variable 'default-directory) (pants--get-source-tree))
+    (with-temp-buffer
+      (insert
+       (shell-command-to-string build-command))
+      (goto-char (point-min))
+      (while (re-search-forward "^\\(.+\\)$" nil t)
+        (setq target (match-string 1))
+        (push target targets)))
+    (helm
+     (helm :sources
+           `((name . "Targets")
+             (candidates . ,targets)
+             (action . action))))))
+
+(defun pants--get-build-file-for-current-buffer ()
+  "Finds the nearest build file for the current buffer"
+  (pants--find-directory-containing-build-file (file-name-directory (buffer-file-name))))
+
+(define-compilation-mode pants-mode "pants"
+  (set (make-local-variable 'compilation-process-setup-function)
+       'pants--compilation-setup))
+
+;;;###autoload
+(defun pants-find-build-file ()
+  "Finds the build file and if it exists, open it."
+  (interactive)
+  (let ((build-file (pants--get-build-file-for-current-buffer)))
+    (if build-file
+        (find-file (concat build-file pants-build-file))
+      (error "Could not find %s" pants-build-file))))
+
+;;;###autoload
+(defun pants-run-binary ()
+  "Builds a binary from a target."
+  (interactive)
+  (let ((build-file (pants--get-build-file-for-current-buffer)))
+    (if build-file
+        (pants--build-target-list build-file 'pants--build-action)
+      (error "Could not find %s" pants-build-file))))
+
+;;;###autoload
+(defun pants-run-python-repl ()
+  "Runs a REPL from a target."
+  (interactive)
+  (let ((build-file (pants--get-build-file-for-current-buffer)))
+    (if build-file
+        (pants--build-target-list build-file 'pants--python-repl-action)
+      (error "Could not find %s" pants-build-file))))
+
+;;;###autoload
+(defun pants-run-test ()
+  "Runs the tests from a target."
+  (interactive)
+  (let ((build-file (pants--get-build-file-for-current-buffer)))
+    (if build-file
+        (pants--build-target-list build-file 'pants--test-action)
+      (error "Could not find %s" pants-build-file))))
+
+(provide 'pants)