diff --git a/.gitignore b/.gitignore index 7be95799a..26f031b25 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ /elpa +/.elpa /.cask *.elc *~ [#]*[#] -/TAGS \ No newline at end of file +/TAGS diff --git a/CHANGELOG.md b/CHANGELOG.md index f302f9d2d..c4029347f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -60,7 +60,9 @@ * [#576](https://github.com/bbatsov/projectile/issues/576): `projectile-replace` stomps regular expressions. * [#957](https://github.com/bbatsov/projectile/pull/957): When opening a specified file from the terminal, do not error inside of `projectile-cache-current-file`. * [#984](https://github.com/bbatsov/projectile/pull/984): Error when a project is a symlink that changes target. + * [#1013](https://github.com/bbatsov/projectile/issues/1013): `projectile-project-buffer-p` may return incorrect result on Windows. +* [#1044](https://github.com/bbatsov/projectile/issues/1044): Replace `shell-command-to-string` invocations with `call-process` invocations where possible to avoid shell startup cost. ## 0.13.0 (2015-10-21) diff --git a/Cask b/Cask index 0690997e0..389143bd4 100644 --- a/Cask +++ b/Cask @@ -6,4 +6,5 @@ (development (depends-on "noflet") (depends-on "helm") - (depends-on "ag")) + (depends-on "ag") + (depends-on "el-mock")) diff --git a/projectile.el b/projectile.el index b0161224a..1c2c170f5 100644 --- a/projectile.el +++ b/projectile.el @@ -37,11 +37,12 @@ ;;; Code: (require 'cl-lib) -(require 'thingatpt) -(require 'ibuffer) -(require 'ibuf-ext) (require 'compile) +(require 'eshell) (require 'grep) +(require 'ibuf-ext) +(require 'ibuffer) +(require 'thingatpt) (eval-when-compile (defvar ag-ignore-list) @@ -1075,9 +1076,29 @@ they are excluded from the results of this function." (when cmd (projectile-files-via-ext-command cmd)))) +(defun projectile-call-process-to-string (program &rest args) + "Invoke the executable PROGRAM with ARGS and return the output as a string." + (with-temp-buffer + (apply 'call-process program nil (current-buffer) nil args) + (buffer-string))) + +(defun projectile-shell-command-to-string (command) + "Try to run COMMAND without actually using a shell and return the output. + +The function `eshell-search-path' will be used to search the PATH +environment variable for an appropriate executable using the text +occuring before the first space. If no executable is found, +fallback to `shell-command-to-string'" + (cl-destructuring-bind + (the-command . args) (split-string command " ") + (let ((binary-path (eshell-search-path the-command))) + (if binary-path + (apply 'projectile-call-process-to-string binary-path args) + (shell-command-to-string command))))) + (defun projectile-files-via-ext-command (command) "Get a list of relative file names in the project root by executing COMMAND." - (split-string (shell-command-to-string command) "\0" t)) + (split-string (projectile-shell-command-to-string command) "\0" t)) (defun projectile-index-directory (directory patterns progress-reporter) "Index DIRECTORY taking into account PATTERNS. diff --git a/test/projectile-test.el b/test/projectile-test.el index 28b0cfe62..96d359f21 100644 --- a/test/projectile-test.el +++ b/test/projectile-test.el @@ -717,6 +717,27 @@ (should (equal (list (expand-file-name "vendor/client-submodule/" project)) (projectile-get-all-sub-projects project))))))) +(ert-deftest projectile-test-projectile-shell-command-to-string-fallback () + (let ((command "command arg1 arg2") + (command-path "/path/to/command") + shell-command-args call-process-args) + (noflet ((shell-command-to-string (&rest args) + (setq shell-command-args args)) + (call-process (&rest args) + (setq call-process-args args))) + (noflet ((eshell-search-path (_command) nil)) + (projectile-shell-command-to-string command) + (should (equal shell-command-args (list command))) + (should (equal call-process-args nil))) + (setq shell-command-args nil + call-process-args nil) + (noflet ((eshell-search-path (_command-name) command-path)) + (projectile-shell-command-to-string command) + (should (equal shell-command-args nil)) + (should (equal (car call-process-args) command-path)) + (should (equal (-slice call-process-args 4) + (cdr (split-string command " ")))))))) + ;; Local Variables: ;; indent-tabs-mode: nil ;; End: