Take the 2-minute tour ×
Emacs Stack Exchange is a question and answer site for for those using, extending or developing emacs. It's 100% free, no registration required.

I have been trying to set up a shortcut for jumping between a JavaScript implementation and test. I have particular requirements that are making examples I see harder to apply.

I currently work with many very small javascript projects. The structure of the project normally has its tests in a mirrored directory structure (like so: )

lib/foo.js lib/someDir/bar.js test/fooSpec.js test/someDir/barSpec.js

So the spec files are not in the same directory, as the implementation

I first was looking at examples for ff-find-other-file, but they were for situations when the files were located in the same directories. I'm looking to configure it a little more dynamically (walking up the directory tree until lib, then adding a similar path with test replacing lib)

I also know projectile has a function for doing just this. I use projectile in my setup, but when attempting to use its built in method (C-c p t) I get the message "Project type not supported!". Interestingly enough, projectile is able to list all my test files via (C-c p T). Is there way of configuring projectile for javascript, so that it knows how to find a corresponding spec file?

I also saw the project toggle test, but am looking for a more automatic solution. It requires a configuration for each project to enable toggling between tests. This is rather inconvenient to me because when working with Node/JS I bounce between many projects (it's very common to divide an application into many very small projects with their own tests). I also often look at external dependencies, and then their specs. More often then not they have this structure. Having to configure toggle test each time I need to use this feature makes it not very useful.

share|improve this question
    
Projectile has the command projectile-find-other-file that works the same as ff-find-other-file, except that it lists all possible other files in your project. You can custom with projectile-other-file-alist variable, –  Tu Do Jan 12 at 19:10
    
FWIW, the ff-search-directories variable provides limited functionality for finding files in other directories (albeit not arbitrary directory walking). See stackoverflow.com/questions/23660637/smarter-ff-find-other-file –  phils Jan 12 at 20:54

2 Answers 2

up vote 2 down vote accepted

I know nothing about projectile, but if I needed something like what you described, I'd possibly go with something like this:

(defun jump-to-test ()
  (interactive)
  (find-file-other-window
   (cl-loop with parts = (reverse (split-string (buffer-file-name) "/"))
            with fname = (file-name-sans-extension (cl-first parts))
            for (name . rest) on (cl-rest parts)
            until (string-equal name "lib")
            collect name into names
            finally (cl-return
                     (mapconcat 'identity
                                (nconc (reverse rest)
                                       (list "test")
                                       (reverse names)
                                       (list (format "%sSpec.js" fname))) "/")))))
share|improve this answer
    
thanks, this is nearly exactly what i'm looking for. And is educating me on elisp. I made a gist containing the opposite side (jump to implementation from test). How about a means to determine which path structure I'm currently in, so that I can call the appropriate function using the same keystroke –  Mike McFarland Jan 12 at 20:01
1  
never-mind, updated the gist containing the function. Thanks for the help. You've made my day! –  Mike McFarland Jan 12 at 20:18
    
@MikeMcFarland ha! Glad I could help :) –  wvxvw Jan 12 at 23:07

Tweaked wvxvw's answer to provide it as a toggle between files. It also doesn't open it in a new window.

(defun js-jump-to (current from to format-name)
  (find-file
   (cl-loop with parts = (reverse current)
            with fname = (file-name-sans-extension (cl-first parts))
            for (name . rest) on (cl-rest parts)
            until (string-equal name from)
            collect name into names
            finally (cl-return
                     (mapconcat 'identity
                                (nconc (reverse rest)
                                       (list to)
                                       (reverse names)
                                       (list (funcall format-name fname) )) "/" )))))

(defun js-format-impl-name (fname)
  (format "%s.js" (replace-regexp-in-string "Spec" "" fname)))

(defun js-format-test-name (fname)
  (format "%sSpec.js" fname))

(defun js-jump-to-implementation-or-test ()
  (interactive)
  (let ((current (split-string (buffer-file-name) "/")))
    (cond
     ((member "test" current) (js-jump-to current "test" "lib" 'js-format-impl-name))
     ((member "lib" current)  (js-jump-to current "lib" "test" 'js-format-test-name))
     (t (error "not within a test or lib directory"))
  )))
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.