From 4b6493d21431a198c77a55d640029cf64386c4c6 Mon Sep 17 00:00:00 2001 From: Haim Ashkenazi Date: Thu, 28 Mar 2013 18:04:48 +0200 Subject: [PATCH 1/6] Insert and update ns now works for custom source paths. It's possible to configure source and test inner directories to add support for hierarchy like 'src/clojure' and 'test/clojure'. Currently only works on inserting and updating ns. --- clojure-mode.el | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/clojure-mode.el b/clojure-mode.el index 569d2bcb..c1745e7b 100644 --- a/clojure-mode.el +++ b/clojure-mode.el @@ -739,6 +739,18 @@ use (put-clojure-indent 'some-symbol 'defun)." :group 'clojure-mode :set 'add-custom-clojure-indents) +(defcustom clojure-source-nested-directory "" + "Custom source path (nested inside the 'src' and 'test' directories). +If your source is in PROJECT_ROOT/src/clojure instead of +PROJECT_ROOT/src and your tests are in PROJECT_ROOT/test/clojure +then set this value to '/clojure' (note the leading '/'). This +affect setting/updating the namespace and switching from test to +implementation. + +Note that it should be the same heirarchy for both src and test!" + :group 'clojure-mode + :type 'string) + (define-clojure-indent ;; built-ins (ns 1) @@ -943,9 +955,10 @@ returned." (let* ((project-dir (file-truename (locate-dominating-file default-directory "project.clj"))) + (depth (length (split-string clojure-source-nested-directory "/"))) (relative (substring (file-truename (buffer-file-name)) (length project-dir) -4))) (replace-regexp-in-string - "_" "-" (mapconcat 'identity (cdr (split-string relative "/")) ".")))) + "_" "-" (mapconcat 'identity (nthcdr depth (split-string relative "/")) ".")))) (defun clojure-insert-ns-form () (interactive) From d9df195138e2a6835f7bbea9fda10216d46673e8 Mon Sep 17 00:00:00 2001 From: Haim Ashkenazi Date: Thu, 28 Mar 2013 19:03:21 +0200 Subject: [PATCH 2/6] Jumping between test and code works for nested directories. I think there are no more issues with custom source/test directories. Time will tell :) --- clojure-mode.el | 10 +++++++--- clojure-test-mode.el | 9 +++++++-- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/clojure-mode.el b/clojure-mode.el index c1745e7b..0616ef02 100644 --- a/clojure-mode.el +++ b/clojure-mode.el @@ -999,9 +999,13 @@ returned." "Returns the path of the test file for the given namespace." (let* ((namespace (clojure-underscores-for-hyphens namespace)) (segments (split-string namespace "\\."))) - (format "%stest/%s_test.clj" - (file-name-as-directory - (locate-dominating-file buffer-file-name "src/")) + (format "%s%s_test.clj" + (file-truename + (concat + (file-name-as-directory + (locate-dominating-file buffer-file-name "src/")) + (file-name-as-directory + (concat "test/" clojure-source-nested-directory)))) (mapconcat 'identity segments "/")))) (defvar clojure-test-for-fn 'clojure-test-for diff --git a/clojure-test-mode.el b/clojure-test-mode.el index 2044483e..e28973e2 100644 --- a/clojure-test-mode.el +++ b/clojure-test-mode.el @@ -336,8 +336,13 @@ Retuns the problem overlay if such a position is found, otherwise nil." (namespace-end (split-string (car (last segments)) "_")) (namespace-end (mapconcat 'identity (butlast namespace-end 1) "_")) (impl-segments (append (butlast segments 1) (list namespace-end)))) - (format "%s/src/%s.clj" - (locate-dominating-file buffer-file-name "src/") + (format "%s/%s.clj" + (file-truename + (concat + (file-name-as-directory + (locate-dominating-file buffer-file-name "src/")) + (file-name-as-directory + (concat "src/" clojure-source-nested-directory)))) (mapconcat 'identity impl-segments "/")))) (defvar clojure-test-implementation-for-fn 'clojure-test-implementation-for From 0a6ab6ebfff45b12dd033b50f4d941429e8f8ee5 Mon Sep 17 00:00:00 2001 From: Haim Ashkenazi Date: Thu, 28 Mar 2013 19:40:06 +0200 Subject: [PATCH 3/6] Set clojure-source-nested-directory safe. Hope it's ok :) --- clojure-mode.el | 1 + 1 file changed, 1 insertion(+) diff --git a/clojure-mode.el b/clojure-mode.el index 0616ef02..00be7652 100644 --- a/clojure-mode.el +++ b/clojure-mode.el @@ -1027,6 +1027,7 @@ Clojure test file for the given namespace.") (progn (put 'clojure-test-ns-segment-position 'safe-local-variable 'integerp) (put 'clojure-mode-load-command 'safe-local-variable 'stringp) + (put 'clojure-source-nested-directory 'safe-local-variable 'stringp) (add-to-list 'auto-mode-alist '("\\.clj\\'" . clojure-mode)) (add-to-list 'auto-mode-alist '("\\.dtm\\'" . clojure-mode)) From 1eb89100dc1e6e8b5a765376802954d67e0edaec Mon Sep 17 00:00:00 2001 From: Haim Ashkenazi Date: Sun, 19 May 2013 13:39:31 +0300 Subject: [PATCH 4/6] Improved safe-local-variable predicate. --- clojure-mode.el | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/clojure-mode.el b/clojure-mode.el index 00be7652..7a79310f 100644 --- a/clojure-mode.el +++ b/clojure-mode.el @@ -1023,11 +1023,19 @@ Clojure test file for the given namespace.") (clojure-test-jump-to-implementation) (clojure-jump-to-test))) +(defun safe-nested-directory-p (v) + "Tests whether the nested directory value is safe for .dir-locals.el. + +It mainly guards against errors trying to make the nested +directory not a subdir of the original source directory" + (and (stringp v) + (not (string-match-p "\\.\\." v)))) + ;;;###autoload (progn (put 'clojure-test-ns-segment-position 'safe-local-variable 'integerp) (put 'clojure-mode-load-command 'safe-local-variable 'stringp) - (put 'clojure-source-nested-directory 'safe-local-variable 'stringp) + (put 'clojure-source-nested-directory 'safe-local-variable 'safe-nested-directory-p) (add-to-list 'auto-mode-alist '("\\.clj\\'" . clojure-mode)) (add-to-list 'auto-mode-alist '("\\.dtm\\'" . clojure-mode)) From a08027de55f0fdab950470883dedc0ef1c963133 Mon Sep 17 00:00:00 2001 From: Haim Ashkenazi Date: Sun, 19 May 2013 18:08:29 +0300 Subject: [PATCH 5/6] Normalizing clojure-source-nested-directory. Following advice from #emacs irc, I created a function with the same name as the custom for accessing the normalized version of the clojure-source-nested-directory value. --- clojure-mode.el | 12 ++++++++++-- clojure-test-mode.el | 3 ++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/clojure-mode.el b/clojure-mode.el index 7a79310f..00ee6fdb 100644 --- a/clojure-mode.el +++ b/clojure-mode.el @@ -950,12 +950,20 @@ returned." +(defun clojure-source-nested-directory () + "clojure-source-nested-directory should start with a slash and end without." + (if (string= "" clojure-source-nested-directory) + clojure-source-nested-directory + (replace-regexp-in-string + "/$" "" + (file-truename (concat "/" clojure-source-nested-directory))))) + (defun clojure-expected-ns () "Returns the namespace name that the file should have." (let* ((project-dir (file-truename (locate-dominating-file default-directory "project.clj"))) - (depth (length (split-string clojure-source-nested-directory "/"))) + (depth (length (split-string (clojure-source-nested-directory) "/"))) (relative (substring (file-truename (buffer-file-name)) (length project-dir) -4))) (replace-regexp-in-string "_" "-" (mapconcat 'identity (nthcdr depth (split-string relative "/")) ".")))) @@ -1005,7 +1013,7 @@ returned." (file-name-as-directory (locate-dominating-file buffer-file-name "src/")) (file-name-as-directory - (concat "test/" clojure-source-nested-directory)))) + (concat "test/" (clojure-source-nested-directory))))) (mapconcat 'identity segments "/")))) (defvar clojure-test-for-fn 'clojure-test-for diff --git a/clojure-test-mode.el b/clojure-test-mode.el index e28973e2..f2e1f15e 100644 --- a/clojure-test-mode.el +++ b/clojure-test-mode.el @@ -117,6 +117,7 @@ (declare-function nrepl-current-ns "nrepl.el") (declare-function nrepl-current-tooling-session "nrepl.el") (declare-function nrepl-current-connection-buffer "nrepl.el") +(declare-function clojure-source-nested-directory "clojure-mode.el") ;; Faces @@ -342,7 +343,7 @@ Retuns the problem overlay if such a position is found, otherwise nil." (file-name-as-directory (locate-dominating-file buffer-file-name "src/")) (file-name-as-directory - (concat "src/" clojure-source-nested-directory)))) + (concat "src/" (clojure-source-nested-directory))))) (mapconcat 'identity impl-segments "/")))) (defvar clojure-test-implementation-for-fn 'clojure-test-implementation-for From fe240e6132fa4b906ba9ce8682af1f5641f12254 Mon Sep 17 00:00:00 2001 From: Haim Ashkenazi Date: Sun, 19 May 2013 23:48:33 +0300 Subject: [PATCH 6/6] Added documentation. --- README.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/README.md b/README.md index a4dcb7cd..ed1ac137 100644 --- a/README.md +++ b/README.md @@ -134,6 +134,31 @@ SLIME is available via [swank-clojure](http://github.com/technomancy/swank-clojure) in `clojure-mode` 1.x. SLIME support was removed in version 2.x in favor of `nrepl.el`. +## Customizing the source path + +In some cases you may want your clojure source to be under a nested +directory inside `src` (e.g. you have more then one language) so you +put your clojure code in `PROJECT_ROOT/src/clj` and your clojure tests +in `PROJECT_ROOT/test/clj`. This breaks 2 features: + +* Jumping to test/implementation. +* Creating/updating the `ns` form. + +In order to get these features back you have to specify the path +inside the `src` and `test` directories. This is done by setting the +`clojure-source-nested-directory` variable. While it could be set +globally (which will affect *all* your projects) it's meant to be set +on per-project basis via the [directory variables][dv] mechanism. +Here's a sample `.dir-locals.el` file that matches the example above. +It should be created at the project root: + +```elisp +((clojure-mode + (clojure-source-nested-directory . "/clj"))) +``` + +[dv]: http://www.gnu.org/software/emacs/manual/html_node/emacs/Directory-Variables.html + ## License Copyright © 2007-2013 Jeffrey Chu, Lennart Staflin, Phil Hagelberg,