Skip to content

Commit

Permalink
Make `fing-file-in-project' an optional dependency (#1681)
Browse files Browse the repository at this point in the history
  • Loading branch information
galaunay committed Oct 24, 2019
1 parent 7fb3b2e commit 6e1d673
Show file tree
Hide file tree
Showing 8 changed files with 154 additions and 37 deletions.
4 changes: 2 additions & 2 deletions Cask
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@
(package "elpy" "1.31.0" "Emacs Python Development Environment")

(depends-on "company" "0.9.10")
(depends-on "find-file-in-project" "5.7.7")
(depends-on "highlight-indentation" "0.7.0")
(depends-on "pyvenv" "1.20")
(depends-on "yasnippet" "0.13.0")
(depends-on "s" "1.12.0")

(development
(depends-on "f")
(depends-on "ert-runner"))
(depends-on "ert-runner")
(depends-on "find-file-in-project"))
1 change: 0 additions & 1 deletion docs/FAQ.rst
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,6 @@ Install dependencies
(package-install 'yasnippet)
(package-install 'pyvenv)
(package-install 'highlight-indentation)
(package-install 'find-file-in-project)
(package-install 's)
Get latest code from github
Expand Down
5 changes: 3 additions & 2 deletions docs/ide.rst
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,11 @@ inside the project root.
``tests`` subdirectory. If the current file is already called
``test_foo.py``, it will try and find a ``foo.py`` nearby.

This command uses `find-file-in-project`_ under the hood, so see
there for more options.
This command uses `projectile`_ or `find-file-in-project`_ under the hood, so you
need one of them to be installed.

.. _find-file-in-project: https://github.com/technomancy/find-file-in-project
.. _projectile: https://github.com/bbatsov/projectile

.. command:: elpy-rgrep-symbol
:kbd: C-c C-s
Expand Down
1 change: 0 additions & 1 deletion elpy-pkg.el
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
"Emacs Python Development Environment"
'((company "0.9.2")
(emacs "24.4")
(find-file-in-project "3.3")
(highlight-indentation "0.5.0")
(pyvenv "1.3")
(yasnippet "0.8.0")
Expand Down
86 changes: 56 additions & 30 deletions elpy.el
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
;; URL: https://github.com/jorgenschaefer/elpy
;; Version: 1.31.0
;; Keywords: Python, IDE, Languages, Tools
;; Package-Requires: ((company "0.9.2") (emacs "24.4") (find-file-in-project "3.3") (highlight-indentation "0.5.0") (pyvenv "1.3") (yasnippet "0.8.0") (s "1.12.0"))
;; Package-Requires: ((company "0.9.10") (emacs "24.4") (highlight-indentation "0.7.0") (pyvenv "1.20") (yasnippet "0.13.0") (s "1.12.0"))

;; This program is free software; you can redistribute it and/or
;; modify it under the terms of the GNU General Public License
Expand Down Expand Up @@ -52,7 +52,6 @@
(require 'elpy-shell)
(require 'elpy-rpc)
(require 'pyvenv)
(require 'find-file-in-project)

(defconst elpy-version "1.31.0"
"The version of the Elpy Lisp code.")
Expand Down Expand Up @@ -1450,12 +1449,12 @@ This combines
(defun elpy-find-file (&optional dwim)
"Efficiently find a file in the current project.
With prefix argument (or DWIM non-nil), tries to guess what kind of
file the user wants to open.
On an import line, it opens the file of that module.
It necessitates `projectile' or `find-file-in-project' to be installed.
Otherwise, it opens a test file associated with the current file,
With prefix argument (or DWIM non-nil), tries to guess what kind of
file the user wants to open:
- On an import line, it opens the file of that module.
- Otherwise, it opens a test file associated with the current file,
if one exists. A test file is named test_<name>.py if the current
file is <name>.py, and is either in the same directory or a
\"test\" or \"tests\" subdirectory."
Expand All @@ -1480,11 +1479,27 @@ file is <name>.py, and is either in the same directory or a
(if test-file
(find-file test-file)
(elpy-find-file nil))))
(t
((fboundp 'projectile-find-file)
(let ((projectile-globally-ignored-file-suffixes
(delete-dups
(nconc
(cl-copy-list projectile-globally-ignored-file-suffixes)
(cl-copy-list completion-ignored-extensions))))
(projectile-globally-ignored-directories
(delete-dups
(nconc
(cl-copy-list projectile-globally-ignored-directories)
(cl-copy-list elpy-ffip-prune-patterns)
(elpy-project-ignored-directories))))
(projectile-project-root (or (elpy-project-root)
default-directory)))
(projectile-find-file)))
((fboundp 'find-file-in-project)
(let ((ffip-prune-patterns (elpy-ffip-prune-patterns))
(ffip-project-root (or (elpy-project-root)
default-directory))
;; Set up ido to use vertical file lists.
(ffip-prefer-ido-mode t)
(ido-decorations '("\n" "" "\n" "\n..."
"[" "]" " [No match]" " [Matched]"
" [Not readable]" " [Too big]"
Expand All @@ -1495,7 +1510,9 @@ file is <name>.py, and is either in the same directory or a
(define-key ido-completion-map (kbd "<up>")
'ido-prev-match))
ido-setup-hook)))
(find-file-in-project)))))
(find-file-in-project)))
(t
(error "`elpy-find-file' necessitates `projectile' or `find-file-in-project' to be installed"))))

(defun elpy-find--test-file ()
"Return the test file for the current file, if any.
Expand All @@ -1505,27 +1522,36 @@ If this is a test file, return the non-test file.
A test file is named test_<name>.py if the current file is
<name>.py, and is either in the same directors or a \"test\" or
\"tests\" subdirectory."
(catch 'return
(let (full-name directory file)
(setq full-name (buffer-file-name))
(unless full-name
(throw 'return nil))
(setq full-name (expand-file-name full-name)
directory (file-name-directory full-name)
file (file-name-nondirectory full-name))
(if (string-match "^test_" file)
(let ((file (substring file 5)))
(dolist (implementation (list (format "%s/%s" directory file)
(format "%s/../%s" directory file)))
(when (file-exists-p implementation)
(throw 'return implementation))))
(dolist (test (list (format "%s/test_%s" directory file)
(format "%s/test/test_%s" directory file)
(format "%s/tests/test_%s" directory file)
(format "%s/../test/test_%s" directory file)
(format "%s/../tests/test_%s" directory file)))
(when (file-exists-p test)
(throw 'return test)))))))
(let* ((project-root (or (elpy-project-root) default-directory))
(filename (file-name-base (buffer-file-name)))
(impl-file (when (string-match "test_\\(.*\\)" filename)
(match-string 1 filename)))
(files
(cond
((fboundp 'projectile-find-file)
(let ((projectile-project-root project-root))
(projectile-current-project-files)))
((fboundp 'find-file-in-project)
(cl-map 'list 'cdr (ffip-project-search nil nil project-root)))
(t '())))
(file (if impl-file
(cl-remove-if (lambda (file)
(not (string=
impl-file
(file-name-base file))))
files)
(cl-remove-if (lambda (file)
(not (string=
(format "test_%s" filename)
(file-name-base file))))
files))))
(when file
(if (> (length file) 1)
(setq file (completing-read "Which file: " file))
(setq file (car file)))
(if (file-name-absolute-p file)
file
(concat (file-name-as-directory project-root) file)))))

(defun elpy-find--module-path (module)
"Return a directory path for MODULE.
Expand Down
4 changes: 3 additions & 1 deletion scripts/start-clean-elpy.el
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@
(package-initialize)
;; Install dependencies
(package-install 'company)
(package-install 'find-file-in-project)
(package-install 'highlight-indentation)
(package-install 'pyvenv)
(package-install 'yasnippet)
(package-install 's)
;; Install optional dependencies
(package-install 'find-file-in-project)
(package-install 'projectile)
;; Load local Elpy
(require 'f)
(require 'cl-extra)
Expand Down
72 changes: 72 additions & 0 deletions test/elpy-find--test-file-test.el
Original file line number Diff line number Diff line change
@@ -1,55 +1,127 @@
(ert-deftest elpy-find--test-file-should-return-nil-if-not-found ()
(elpy-testcase ((:project project-root "foo.py"))
(find-file (f-join project-root "foo.py"))
(elpy-set-project-root project-root)

(defun projectile-find-file ())
(when (require 'projectile nil t)
(fmakunbound 'find-file-in-project)
(should (equal (elpy-find--test-file)
nil)))
(defun find-file-in-project ())
(require 'find-file-in-project)
(fmakunbound 'projectile-find-file)
(should (equal (elpy-find--test-file)
nil))))

(ert-deftest elpy-find--test-file-should-find-test-file-same-directory ()
(elpy-testcase ((:project project-root "foo.py" "test_foo.py"))
(find-file (f-join project-root "foo.py"))
(elpy-set-project-root project-root)

(defun projectile-find-file ())
(when (require 'projectile nil t)
(fmakunbound 'find-file-in-project)
(should (f-equal? (elpy-find--test-file)
(f-join project-root "test_foo.py"))))
(defun find-file-in-project ())
(require 'find-file-in-project)
(fmakunbound 'projectile-find-file)
(should (f-equal? (elpy-find--test-file)
(f-join project-root "test_foo.py")))))

(ert-deftest elpy-find--test-file-should-find-test-file-in-test-directory ()
(elpy-testcase ((:project project-root "foo.py" "test/test_foo.py"))
(find-file (f-join project-root "foo.py"))
(elpy-set-project-root project-root)

(defun projectile-find-file ())
(when (require 'projectile nil t)
(fmakunbound 'find-file-in-project)
(should (f-equal? (elpy-find--test-file)
(f-join project-root "test/test_foo.py"))))
(defun find-file-in-project ())
(require 'find-file-in-project)
(fmakunbound 'projectile-find-file)
(should (f-equal? (elpy-find--test-file)
(f-join project-root "test/test_foo.py")))))

(ert-deftest elpy-find--test-file-should-find-test-file-in-tests-directory ()
(elpy-testcase ((:project project-root "foo.py" "tests/test_foo.py"))
(find-file (f-join project-root "foo.py"))
(elpy-set-project-root project-root)

(defun projectile-find-file ())
(when (require 'projectile nil t)
(fmakunbound 'find-file-in-project)
(should (f-equal? (elpy-find--test-file)
(f-join project-root "tests/test_foo.py"))))
(defun find-file-in-project ())
(require 'find-file-in-project)
(fmakunbound 'projectile-find-file)
(should (f-equal? (elpy-find--test-file)
(f-join project-root "tests/test_foo.py")))))

(ert-deftest elpy-find--test-file-should-find-test-file-in-test-superdirectory ()
(elpy-testcase ((:project project-root "pkg/foo.py" "test/test_foo.py"))
(find-file (f-join project-root "pkg/foo.py"))
(elpy-set-project-root project-root)

(defun projectile-find-file ())
(when (require 'projectile nil t)
(fmakunbound 'find-file-in-project)
(should (f-equal? (elpy-find--test-file)
(f-join project-root "test/test_foo.py"))))
(defun find-file-in-project ())
(require 'find-file-in-project)
(fmakunbound 'projectile-find-file)
(should (f-equal? (elpy-find--test-file)
(f-join project-root "test/test_foo.py")))))

(ert-deftest elpy-find--test-file-should-find-test-file-in-tests-superdirectory ()
(elpy-testcase ((:project project-root "pkg/foo.py" "tests/test_foo.py"))
(find-file (f-join project-root "pkg/foo.py"))
(elpy-set-project-root project-root)

(defun projectile-find-file ())
(when (require 'projectile nil t)
(fmakunbound 'find-file-in-project)
(should (f-equal? (elpy-find--test-file)
(f-join project-root "tests/test_foo.py"))))
(defun find-file-in-project ())
(require 'find-file-in-project)
(fmakunbound 'projectile-find-file)
(should (f-equal? (elpy-find--test-file)
(f-join project-root "tests/test_foo.py")))))

(ert-deftest elpy-find--test-file-should-find-implementation-file-same-directory ()
(elpy-testcase ((:project project-root "foo.py" "test_foo.py"))
(find-file (f-join project-root "test_foo.py"))
(elpy-set-project-root project-root)

(defun projectile-find-file ())
(when (require 'projectile nil t)
(fmakunbound 'find-file-in-project)
(should (f-equal? (elpy-find--test-file)
(f-join project-root "foo.py"))))
(defun find-file-in-project ())
(require 'find-file-in-project)
(fmakunbound 'projectile-find-file)
(should (f-equal? (elpy-find--test-file)
(f-join project-root "foo.py")))))

(ert-deftest elpy-find--test-file-should-find-implementation-file-superdirectory ()
(elpy-testcase ((:project project-root "foo.py" "tests/test_foo.py"))
(find-file (f-join project-root "tests/test_foo.py"))
(elpy-set-project-root project-root)

(defun projectile-find-file ())
(when (require 'projectile nil t)
(fmakunbound 'find-file-in-project)
(should (f-equal? (elpy-find--test-file)
(f-join project-root "foo.py"))))
(defun find-file-in-project ())
(require 'find-file-in-project)
(fmakunbound 'projectile-find-file)
(should (f-equal? (elpy-find--test-file)
(f-join project-root "foo.py")))))
18 changes: 18 additions & 0 deletions test/elpy-find-file-test.el
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@
(elpy-testcase ((:project project-root "module.py" "test_module.py"))
(find-file (f-join project-root "module.py"))

(fmakunbound 'projectile-find-file)
(defun find-file-in-project ())
(require 'find-file-in-project)
(elpy-find-file t)

(should (f-equal? (buffer-file-name)
Expand All @@ -67,10 +70,25 @@
(elpy-testcase ()
;; The test failed on Travis in 24.3 because the function was not
;; defined. Weird. Well, call it in explicitly.
(fmakunbound 'projectile-find-file)
(defun find-file-in-project ())
(require 'find-file-in-project)
(mletf* ((ffip-called nil)
(find-file-in-project () (setq ffip-called t)))

(elpy-find-file)

(should ffip-called))))

(ert-deftest elpy-find-file-should-call-projectile ()
(elpy-testcase ()
;; The test failed on Travis in 24.3 because the function was not
;; defined. Weird. Well, call it in explicitly.
(fmakunbound 'find-file-in-project)
(defun projectile-find-file ())
(when (require 'projectile nil t)
(mletf* ((projectile-called nil)
(projectile-find-file () (setq projectile-called t)))

(elpy-find-file)
(should projectile-called)))))

0 comments on commit 6e1d673

Please sign in to comment.