diff options
author | Franck Cuny <franckcuny@gmail.com> | 2016-03-10 17:47:49 -0800 |
---|---|---|
committer | Franck Cuny <franckcuny@gmail.com> | 2016-03-10 17:47:49 -0800 |
commit | 5a2f209d0ab366ae99d62c5bd9c5b23b78885736 (patch) | |
tree | eeca3fe257b869a7c3b4e0890af98e80abf5ae02 | |
parent | Add README.md, LICENSE.txt (diff) | |
download | pants.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.md | 59 | ||||
-rw-r--r-- | pants.el | 154 |
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) |