emacs-elpa-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[nongnu] elpa/projectile 6b3a73fddc 1/2: [Fix #1765] Re-add defaults for


From: ELPA Syncer
Subject: [nongnu] elpa/projectile 6b3a73fddc 1/2: [Fix #1765] Re-add defaults for src-dir and test-dir
Date: Tue, 19 Apr 2022 01:58:38 -0400 (EDT)

branch: elpa/projectile
commit 6b3a73fddc04031ea36f677c26cc85af4c25e8fe
Author: lWarne <laurencewarne@gmail.com>
Commit: Bozhidar Batsov <bozhidar@batsov.dev>

    [Fix #1765] Re-add defaults for src-dir and test-dir
    
    Use "src/" and "test/" as defaults for the src-dir and test-dir
    attributes of a project type when toggling between implementation and
    test files.
    
    Improve error messages for implementation and test file toggling
---
 projectile.el           | 189 ++++++++++++++++++++++++++++++++++++------------
 test/projectile-test.el |  80 +++++++++++++-------
 2 files changed, 197 insertions(+), 72 deletions(-)

diff --git a/projectile.el b/projectile.el
index 7db137f93a..a70a8021f3 100644
--- a/projectile.el
+++ b/projectile.el
@@ -530,6 +530,22 @@ See also `projectile-update-mode-line'."
   :type 'function
   :package-version '(projectile . "2.0.0"))
 
+(defcustom projectile-default-src-directory "src/"
+  "The default value of a project's src-dir property.
+
+It's used as a fallback in the case the property is not set for a project
+type when `projectile-toggle-between-implementation-and-test' is used."
+  :group 'projectile
+  :type 'string)
+
+(defcustom projectile-default-test-directory "test/"
+  "The default value of a project's test-dir property.
+
+It's used as a fallback in the case the property is not set for a project
+type when `projectile-toggle-between-implementation-and-test' is used."
+  :group 'projectile
+  :type 'string)
+
 
 ;;; Idle Timer
 (defvar projectile-idle-timer nil
@@ -3426,7 +3442,7 @@ IMPL-FILE-PATH may be a absolute path, relative path or a 
file name."
     (cond
      (test-prefix (concat test-prefix impl-file-name "." impl-file-ext))
      (test-suffix (concat impl-file-name test-suffix "." impl-file-ext))
-     (t (error "Project type `%s' not supported!" project-type)))))
+     (t (error "Cannot determine a test file name, one of \"test-suffix\" or 
\"test-prefix\" must be set for project type `%s'" project-type)))))
 
 (defun projectile--impl-name-for-test-name (test-file-path)
   "Determine the name of the implementation file for TEST-FILE-PATH.
@@ -3442,7 +3458,7 @@ TEST-FILE-PATH may be a absolute path, relative path or a 
file name."
       (concat (string-remove-prefix test-prefix test-file-name) "." 
test-file-ext))
      (test-suffix
       (concat (string-remove-suffix test-suffix test-file-name) "." 
test-file-ext))
-     (t (error "Project type `%s' not supported!" project-type)))))
+     (t (error "Cannot determine an implementation file name, one of 
\"test-suffix\" or \"test-prefix\" must be set for project type `%s'" 
project-type)))))
 
 (defun projectile--test-to-impl-dir (test-dir-path)
   "Return the directory path of an impl file with test file in TEST-DIR-PATH.
@@ -3452,12 +3468,50 @@ string) are replaced with the current project type's 
src-dir property
  (which should be a string) to obtain the new directory.
 
 Nil is returned if either the src-dir or test-dir properties are not strings."
-  (let ((test-dir (projectile-project-type-attribute
-                   (projectile-project-type) 'test-dir))
-        (impl-dir (projectile-project-type-attribute
-                   (projectile-project-type) 'src-dir)))
+  (let* ((project-type (projectile-project-type))
+         (test-dir (projectile-project-type-attribute project-type 'test-dir))
+         (impl-dir (projectile-project-type-attribute project-type 'src-dir)))
     (when (and (stringp test-dir) (stringp impl-dir))
-      (projectile-complementary-dir test-dir-path test-dir impl-dir))))
+      (if (not (string-match-p test-dir (file-name-directory test-dir-path)))
+          (error "Attempted to find a implementation file by switching this 
project type's (%s) test-dir property \"%s\" with this project type's src-dir 
property \"%s\", but %s does not contain \"%s\""
+                 project-type test-dir impl-dir test-dir-path test-dir)
+        (projectile-complementary-dir test-dir-path test-dir impl-dir)))))
+
+(defun projectile--impl-to-test-dir-fallback (impl-dir-path)
+  "Return the test file for IMPL-DIR-PATH by guessing a test directory.
+
+Occurrences of the `projectile-default-src-directory' in the directory of
+IMPL-DIR-PATH are replaced with `projectile-default-test-directory'.  Nil is
+returned if `projectile-default-src-directory' is not a substring of
+IMPL-DIR-PATH."
+  (when-let ((file (projectile--complementary-file
+                    impl-dir-path
+                    (lambda (f)
+                      (when (string-match-p projectile-default-src-directory f)
+                        (projectile-complementary-dir
+                         impl-dir-path
+                         projectile-default-src-directory
+                         projectile-default-test-directory)))
+                    #'projectile--test-name-for-impl-name)))
+    (file-relative-name file (projectile-project-root))))
+
+(defun projectile--test-to-impl-dir-fallback (test-dir-path)
+  "Return the impl file for TEST-DIR-PATH by guessing a source directory.
+
+Occurrences of `projectile-default-test-directory' in the directory of
+TEST-DIR-PATH are replaced with `projectile-default-src-directory'.  Nil is
+returned if `projectile-default-test-directory' is not a substring of
+TEST-DIR-PATH."
+  (when-let ((file (projectile--complementary-file
+                    test-dir-path
+                    (lambda (f)
+                      (when (string-match-p projectile-default-test-directory 
f)
+                        (projectile-complementary-dir
+                         test-dir-path
+                         projectile-default-test-directory
+                         projectile-default-src-directory)))
+                    #'projectile--impl-name-for-test-name)))
+    (file-relative-name file (projectile-project-root))))
 
 (defun projectile--impl-to-test-dir (impl-dir-path)
   "Return the directory path of a test whose impl file resides in 
IMPL-DIR-PATH.
@@ -3466,13 +3520,19 @@ Occurrences of the current project type's src-dir 
property (which should be a
 string) are replaced with the current project type's test-dir property
  (which should be a string) to obtain the new directory.
 
+If the src-dir property is set and IMPL-DIR-PATH does not contain (as a
+substring) the src-dir property of the current project type, an error is
+signalled.
+
 Nil is returned if either the src-dir or test-dir properties are not strings."
-  (let ((test-dir (projectile-project-type-attribute
-                   (projectile-project-type) 'test-dir))
-        (impl-dir (projectile-project-type-attribute
-                   (projectile-project-type) 'src-dir)))
+  (let* ((project-type (projectile-project-type))
+         (test-dir (projectile-project-type-attribute project-type 'test-dir))
+         (impl-dir (projectile-project-type-attribute project-type 'src-dir)))
     (when (and (stringp test-dir) (stringp impl-dir))
-      (projectile-complementary-dir impl-dir-path impl-dir test-dir))))
+      (if (not (string-match-p impl-dir (file-name-directory impl-dir-path)))
+          (error "Attempted to find a test file by switching this project 
type's (%s) src-dir property \"%s\" with this project type's test-dir property 
\"%s\", but %s does not contain \"%s\""
+                 project-type impl-dir test-dir impl-dir-path impl-dir)
+        (projectile-complementary-dir impl-dir-path impl-dir test-dir)))))
 
 (defun projectile-complementary-dir (dir-path string replacement)
   "Return the \"complementary\" directory of DIR-PATH.
@@ -3516,25 +3576,38 @@ test file."
             (projectile-create-missing-test-files
              (projectile--create-directories-for expanded-test-file)
              expanded-test-file)
-            (t (progn (error error-msg)))))))
+            (t (error "Determined test file to be \"%s\", which does not 
exist.  Set `projectile-create-missing-test-files' to allow 
`projectile-find-implementation-or-test' to create new files" test-file))))))
 
 ;;;###autoload
 (defun projectile-find-implementation-or-test-other-window ()
-  "Open matching implementation or test file in other window."
+  "Open matching implementation or test file in other window.
+
+See the documentation of `projectile--find-matching-file' and
+`projectile--find-matching-test' for how implementation and test files
+are determined."
   (interactive)
   (find-file-other-window
    (projectile-find-implementation-or-test (buffer-file-name))))
 
 ;;;###autoload
 (defun projectile-find-implementation-or-test-other-frame ()
-  "Open matching implementation or test file in other frame."
+  "Open matching implementation or test file in other frame.
+
+See the documentation of `projectile--find-matching-file' and
+`projectile--find-matching-test' for how implementation and test files
+are determined."
   (interactive)
   (find-file-other-frame
    (projectile-find-implementation-or-test (buffer-file-name))))
 
 ;;;###autoload
 (defun projectile-toggle-between-implementation-and-test ()
-  "Toggle between an implementation file and its test file."
+  "Toggle between an implementation file and its test file.
+
+
+See the documentation of `projectile--find-matching-file' and
+`projectile--find-matching-test' for how implementation and test files
+are determined."
   (interactive)
   (find-file
    (projectile-find-implementation-or-test (buffer-file-name))))
@@ -3562,11 +3635,13 @@ Fallback to DEFAULT-VALUE for missing attributes."
 
 (defun projectile-src-directory (project-type)
   "Find default src directory based on PROJECT-TYPE."
-  (projectile-project-type-attribute project-type 'src-dir "src/"))
+  (projectile-project-type-attribute
+   project-type 'src-dir projectile-default-src-directory))
 
 (defun projectile-test-directory (project-type)
   "Find default test directory based on PROJECT-TYPE."
-  (projectile-project-type-attribute project-type 'test-dir "test/"))
+  (projectile-project-type-attribute
+   project-type 'test-dir projectile-default-test-directory))
 
 (defun projectile-dirname-matching-count (a b)
   "Count matching dirnames ascending file paths in A and B."
@@ -3674,6 +3749,25 @@ to a custom function, else return nil."
         #'projectile--test-name-for-impl-name)
        (projectile-project-root)))))
 
+(defmacro projectile--acond (&rest clauses)
+  "Like `cond', but the result of each condition is bound to `it'.
+
+The variable `it' is available within the remainder of each of CLAUSES.
+
+CLAUSES are otherwise as documented for `cond'.  This is copied from
+anaphora.el."
+  (declare (debug cond))
+  (if (null clauses)
+      nil
+    (let ((cl1 (car clauses))
+          (sym (cl-gensym)))
+      `(let ((,sym ,(car cl1)))
+         (if ,sym
+             (if (null ',(cdr cl1))
+                 ,sym
+               (let ((it ,sym)) ,@(cdr cl1)))
+           (projectile--acond ,@(cdr clauses)))))))
+
 (defun projectile--find-matching-test (impl-file)
   "Return a list of test files for IMPL-FILE.
 
@@ -3682,18 +3776,21 @@ The precendence for determining test files to return is:
 1. Use the project type's test-dir property if it's set to a function
 2. Use the project type's related-files-fn property if set
 3. Use the project type's test-dir property if it's set to a string
-4. Default to a fallback which matches all project files against
-   `projectile--impl-to-test-predicate'"
-  (if-let ((test-file-from-test-dir-fn
-            (projectile--test-file-from-test-dir-fn impl-file)))
-      (list test-file-from-test-dir-fn)
-    (if-let ((plist (projectile--related-files-plist-by-kind impl-file :test)))
-        (projectile--related-files-from-plist plist)
-      (if-let ((test-file (projectile--test-file-from-test-dir-str impl-file)))
-          (list test-file)
-        (when-let ((predicate (projectile--impl-to-test-predicate impl-file)))
-          (projectile--best-or-all-candidates-based-on-parents-dirs
-           impl-file (cl-remove-if-not predicate 
(projectile-current-project-files))))))))
+4. Attempt to find a file by matching all project files against
+   `projectile--impl-to-test-predicate'
+5. Fallback to swapping \"src\" for \"test\" in IMPL-FILE if \"src\"
+   is a substring of IMPL-FILE."
+  (projectile--acond
+   ((projectile--test-file-from-test-dir-fn impl-file) (list it))
+   ((projectile--related-files-plist-by-kind impl-file :test)
+    (projectile--related-files-from-plist it))
+   ((projectile--test-file-from-test-dir-str impl-file) (list it))
+   ((projectile--best-or-all-candidates-based-on-parents-dirs
+     impl-file (cl-remove-if-not
+                (projectile--impl-to-test-predicate impl-file)
+                (projectile-current-project-files))) it)
+   ((projectile--impl-to-test-dir-fallback impl-file)
+    (list it))))
 
 (defun projectile--test-to-impl-predicate (test-file)
   "Return a predicate, which returns t for any impl files for TEST-FILE."
@@ -3714,17 +3811,19 @@ The precendence for determining implementation files to 
return is:
 2. Use the project type's related-files-fn property if set
 3. Use the project type's src-dir property if it's set to a string
 4. Default to a fallback which matches all project files against
-   `projectile--test-to-impl-predicate'"
-  (if-let ((impl-file-from-src-dir-fn
-            (projectile--impl-file-from-src-dir-fn test-file)))
-      (list impl-file-from-src-dir-fn)
-    (if-let ((plist (projectile--related-files-plist-by-kind test-file :impl)))
-        (projectile--related-files-from-plist plist)
-      (if-let ((impl-file (projectile--impl-file-from-src-dir-str test-file)))
-          (list impl-file)
-        (when-let ((predicate (projectile--test-to-impl-predicate test-file)))
-          (projectile--best-or-all-candidates-based-on-parents-dirs
-           test-file (cl-remove-if-not predicate 
(projectile-current-project-files))))))))
+   `projectile--test-to-impl-predicate'
+5. Fallback to swapping \"test\" for \"src\" in TEST-FILE if \"test\"
+   is a substring of TEST-FILE."
+  (projectile--acond
+   ((projectile--impl-file-from-src-dir-fn test-file) (list it))
+   ((projectile--related-files-plist-by-kind test-file :impl)
+    (projectile--related-files-from-plist it))
+   ((projectile--impl-file-from-src-dir-str test-file) (list it))
+   ((projectile--best-or-all-candidates-based-on-parents-dirs
+     test-file (cl-remove-if-not
+                (projectile--test-to-impl-predicate test-file)
+                (projectile-current-project-files))) it)
+   ((projectile--test-to-impl-dir-fallback test-file) (list it))))
 
 (defun projectile--choose-from-candidates (candidates)
   "Choose one item from CANDIDATES."
@@ -3734,13 +3833,13 @@ The precendence for determining implementation files to 
return is:
 
 (defun projectile-find-matching-test (impl-file)
   "Compute the name of the test matching IMPL-FILE."
-  (if-let ((candidates (projectile--find-matching-test impl-file)))
-      (projectile--choose-from-candidates candidates)))
+  (when-let ((candidates (projectile--find-matching-test impl-file)))
+    (projectile--choose-from-candidates candidates)))
 
 (defun projectile-find-matching-file (test-file)
   "Compute the name of a file matching TEST-FILE."
-  (if-let ((candidates (projectile--find-matching-file test-file)))
-      (projectile--choose-from-candidates candidates)))
+  (when-let ((candidates (projectile--find-matching-file test-file)))
+    (projectile--choose-from-candidates candidates)))
 
 (defun projectile-grep-default-files ()
   "Try to find a default pattern for `projectile-grep'.
diff --git a/test/projectile-test.el b/test/projectile-test.el
index bd86763e55..e3d5a7feef 100644
--- a/test/projectile-test.el
+++ b/test/projectile-test.el
@@ -1328,7 +1328,23 @@ Just delegates OPERATION and ARGS for all operations 
except for`shell-command`'.
           (expect (projectile--find-matching-file
                    (projectile-expand-root "test/foo/FooTest.cpp"))
                   :to-equal
-                  (list "src/foo/Foo.cpp"))))))
+                  (list "src/foo/Foo.cpp")))))
+
+  (it "defers to a fallback using \"src\" and \"test\""
+    (projectile-test-with-sandbox
+      (projectile-test-with-files-using-custom-project
+          ("project.clj"
+           "src/example/core.clj"
+           "test/example/core2_test.clj")
+          (:test-suffix "_test")
+        (expect (projectile--find-matching-test
+                 (projectile-expand-root "src/example/core.clj"))
+                :to-equal
+                (list "test/example/core_test.clj"))
+        (expect (projectile--find-matching-file
+                 (projectile-expand-root "test/example/core2_test.clj"))
+                :to-equal
+                (list "src/example/core2.clj"))))))
 
 (describe "projectile--related-files"
   (it "returns related files for the given file"
@@ -1887,34 +1903,44 @@ projectile-process-current-project-buffers-current to 
have similar behaviour"
               :to-equal "foo/test/dir/Foo.test"))))
 
 (describe "projectile--impl-to-test-dir"
-    :var ((mock-projectile-project-types
-           '((foo test-dir "test" src-dir "src")
-             (bar test-dir identity src-dir "src"))))
-    (it "replaces occurrences of src-dir with test-dir"
-      (cl-letf (((symbol-function 'projectile-project-root) (lambda () "foo"))
-                ((symbol-function 'projectile-project-type) (lambda () 'foo))
-                (projectile-project-types mock-projectile-project-types))
-        (expect (projectile--impl-to-test-dir "/foo/src/Foo") :to-equal 
"/foo/test/")))
-    (it "nil returned when test-dir property is not a string"
-      (cl-letf (((symbol-function 'projectile-project-root) (lambda () "bar"))
-                ((symbol-function 'projectile-project-type) (lambda () 'bar))
-                (projectile-project-types mock-projectile-project-types))
-        (expect (projectile--impl-to-test-dir "/bar/src/bar") :to-be nil))))
+  :var ((mock-projectile-project-types
+         '((foo test-dir "test" src-dir "src")
+           (bar test-dir identity src-dir "src"))))
+  (it "replaces occurrences of src-dir with test-dir"
+    (cl-letf (((symbol-function 'projectile-project-root) (lambda () "foo"))
+              ((symbol-function 'projectile-project-type) (lambda () 'foo))
+              (projectile-project-types mock-projectile-project-types))
+      (expect (projectile--impl-to-test-dir "/foo/src/Foo") :to-equal 
"/foo/test/")))
+  (it "nil returned when test-dir property is not a string"
+    (cl-letf (((symbol-function 'projectile-project-root) (lambda () "bar"))
+              ((symbol-function 'projectile-project-type) (lambda () 'bar))
+              (projectile-project-types mock-projectile-project-types))
+      (expect (projectile--impl-to-test-dir "/bar/src/bar") :to-be nil)))
+  (it "error when src-dir not a substring of impl file"
+    (cl-letf (((symbol-function 'projectile-project-root) (lambda () "foo"))
+              ((symbol-function 'projectile-project-type) (lambda () 'foo))
+              (projectile-project-types mock-projectile-project-types))
+      (expect (projectile--impl-to-test-dir "/bar/other/bar") :to-throw))))
 
 (describe "projectile--test-to-impl-dir"
-    :var ((mock-projectile-project-types
-           '((foo test-dir "test" src-dir "src")
-             (bar test-dir "test" src-dir identity))))
-    (it "replaces occurrences of test-dir with src-dir"
-      (cl-letf (((symbol-function 'projectile-project-root) (lambda () "foo"))
-                ((symbol-function 'projectile-project-type) (lambda () 'foo))
-                (projectile-project-types mock-projectile-project-types))
-        (expect (projectile--test-to-impl-dir "/foo/test/Foo") :to-equal 
"/foo/src/")))
-    (it "nil returned when src-dir property is not a string"
-      (cl-letf (((symbol-function 'projectile-project-root) (lambda () "bar"))
-                ((symbol-function 'projectile-project-type) (lambda () 'bar))
-                (projectile-project-types mock-projectile-project-types))
-        (expect (projectile--test-to-impl-dir "/bar/test/bar") :to-be nil))))
+  :var ((mock-projectile-project-types
+         '((foo test-dir "test" src-dir "src")
+           (bar test-dir "test" src-dir identity))))
+  (it "replaces occurrences of test-dir with src-dir"
+    (cl-letf (((symbol-function 'projectile-project-root) (lambda () "foo"))
+              ((symbol-function 'projectile-project-type) (lambda () 'foo))
+              (projectile-project-types mock-projectile-project-types))
+      (expect (projectile--test-to-impl-dir "/foo/test/Foo") :to-equal 
"/foo/src/")))
+  (it "nil returned when src-dir property is not a string"
+    (cl-letf (((symbol-function 'projectile-project-root) (lambda () "bar"))
+              ((symbol-function 'projectile-project-type) (lambda () 'bar))
+              (projectile-project-types mock-projectile-project-types))
+      (expect (projectile--test-to-impl-dir "/bar/test/bar") :to-be nil)))
+  (it "error when test-dir not a substring of test file"
+    (cl-letf (((symbol-function 'projectile-project-root) (lambda () "foo"))
+              ((symbol-function 'projectile-project-type) (lambda () 'foo))
+              (projectile-project-types mock-projectile-project-types))
+      (expect (projectile--test-to-impl-dir "/bar/other/bar") :to-throw))))
 
 (describe "projectile-run-shell-command-in-root"
   (describe "when called directly in elisp"



reply via email to

[Prev in Thread] Current Thread [Next in Thread]