eclim
eclim copied to clipboard
Feature Request: Jump directly to the implementation of a method in Java
So I've used :JavaSearchContext to jump directly to the declaration of a method; however, what I want to do is jump to the implementation.
In Eclipse you can do this by holding down the Ctrl key(Command key on Mac) and hovering over a method name with your mouse and then you are given a menu option to open the following
- Open Declaration
- Open Implementation
- Open Return Type
When you select "Open Implementation" it will jump you directly to the file that implements that method.
Another way to do this in Eclipse is to open the declaration of the method and then hit "F4" where you will be able to see the Type Hierarchy for the current class you are in and then you can select the class that implements the method you are looking for.
I've looked all through the documentation for this, but I have not been able to locate a way to do this.
I think this ticket is relevant to your interests: https://github.com/ervandew/eclim/issues/344
I actually did look at this ticket. :JavaSearch -x declarations will take you to the declaration of a method, but not its implementation.
To see this difference take any Java code that implements an interface. If you were to go to the declaration of the interface either in Eclipse or in Eclim it will take you to just the method prototype. By comparison if you open Eclipse and go to the implementation of that method rather than the declaration you will actually see all of the code that defines a particular implementation of that interface.
Here's an example:
Declaration
public interface OrderShipmentsDao
{
ERPInvoiceModel getOrderErpInvoice(String invoiceCode);
}
Implementation
public ERPInvoiceModel getOrderErpInvoice(final String invoiceId)
{
final FlexibleSearchQuery query = new FlexibleSearchQuery(
"SELECT {erp:pk} FROM {ERPInvoice as erp} WHERE {erp:invoiceid} = ?attribute1");
query.addQueryParameter("attribute1", invoiceId);
final SearchResult<ERPInvoiceModel> searchResult = flexibleSearchService.search(query);
final List<ERPInvoiceModel> result = searchResult.getResult();
if (!result.isEmpty())
{
return result.get(0);
}
return null;
}
Hmm. I think the behavior you described is what I actually want in that ticket. Weirdly, though, Eclim does take me to the implementation, generally, with :JavaSearch -x declarations
. I think Eclipse considers both the implementation and the definition to be "declarations." Try doing a right-click > declarations > workspace on a call to getOrderErpInvoice
and I bet Eclipse will show you both of those you listed in the search results (which, I assume, is what :JavaSearch
does)—at least, that's what it did for me just now. So, something like
nmap <buffer> <silent> <leader>js :JavaSearch -x declarations -s project<cr>
might work okay for you until something more precise comes along.
Yes, I believe you are correct that they are both considered as "declarations" by Eclipse. When I did right-click > declarations > workspace, both showed up. I also tried what you suggested ":JavaSearch -x declarations -s project"; however, it just takes me directly to the declaration (i.e. the prototype with no implementation).
I think what is needed is a new command (e.g. :JavaSearchContextImpl) that will jump you directly to the implementation of a method just like you can in Eclipse when you hold down the Ctrl key(Command key on Mac) while hovering over the method name and then selecting "Open Implementation" . I can understand why you want to preserve the current behavior for :JavaSearchContext to continue to jump to the declaration, because I think in some instances you may actually want this as well.
In the meantime the best I have come up with is...
nnoremap <silent> <leader>m :JavaSearch -p <c-r>=expand("<cword>")<cr> -t method -x implementors<CR>"
the problem with this workaround is I have to scroll through everything that has a method with this keyword, which for some methods is a very long list.
I actually looked into the code for this earlier today and discovered this comment in the :JavaSearch
command:
// jdt search doesn't support implementors for method searches, so
// switch to declarations.
I'm not super familiar with JDT/etc. but that could be problematic.
Quick question: how do you know which implementation is the one you're looking for? Are you working working with a concrete implementation so you know exactly the type hierarchy that implements the interface? IE:
class Concrete implements ISomething { /* ... */ }
void method(Concrete foo) { foo.doSomething(); }
It seems if you just had:
void method(ISomething foo) { foo.doSomething(); }
the actual implementation would be ambiguous and you'd need the list. If it's the first case, though, I can see how you might have mixed results with the normal JavaSearch
, depending on how the results are sorted—which, it appears, is by "element kind" (whatever that means).
I made an update to my prior comment. I should have said that my workaround causes me to "have to scroll through everything that has a method with this keyword, which for some methods is a very long list."
which is different than scrolling through everything that has implemented the interface. If there are multiple implementations for an interface then I would expect to have to scroll through and select the correct result.
To answer your question...so I know the implementation I am looking for because of the import for the interface that is included by the class that is calling the particular method. For example, if I am calling a method getOrderErpInvoice then I might have something like the following import at the top of the class.
import com.company.project.ordershipments.dao.OrderShipmentsDao;
the actual implementation I'm looking for typically resides in the corresponding package
package com.company.project.ordershipments.dao.OrderShipmentsDao.impl;
So this impl convention used on our projects helps me to identify the right implementation to jump to. Now in the case of multiple classes implementing the interface then Eclipse itself will open a dialog when I click on "Open Implementation" prompting me to choose the implementation I am looking for. Note, that if there is only one implementation then Eclipse just jumps directly to that one implementation without prompting with a dialog. At runtime, in the case of multiple implementations, this gets resolved because Spring is injecting only one particular implementation. (i.e. the implementation I am looking for)
To summarize, I'd like Eclim to achieve the same result as Eclipse by having a command that when executed (while the cursor is on top of a method name) it would jump directly to the implementation of the class for that method. If there are multiple implementations of that class then it should provide a list of the implementations, so I can choose the appropriate one.
Okay, I found a place in some code I have that replicates this issue. The problem is that even though I have two classes that implement the method—which Eclipse successfully locates—the JDT SearchEngine
/SearchPattern
is not finding those—it only finds the interface declaration, as you mentioned.
I tried commenting out the code that changes IJavaSearchConstants.IMPLEMENTORS
to IJavaSearchConstants.DECLARATIONS
for method searches, but got nonsensical results, so he was right to make the switch.
I suspect what Eclipse is actually doing is searching for classes that implement the interface that method belongs to, and filtering by classes that then declare the method (a progress dialog appeared that seemed to flash class names).
I'm have no idea why sometimes JDT can find the implementor but not others. Anecdotally, it worked when the interface was nested in the class from which I did the search. However, there's also only one implementor for that interface; no idea if that's relevant. I was using the interface, but I assigned the value in that same class as well—perhaps JDT is doing some static analysis? Either way, I think the above proposal would be the only reliable solution.
If I have time later today, I may try my hand at that proposal, but we'll see....
@ahnick I just submitted a PR that I think will do what you want. I've mapped it like this:
nmap <buffer> <silent> gd :JavaSearch -x implementors -s workspace<cr>
Feel free to check it out and let me know how it works for you!
Sorry, but it still doesn't. I am using eclim 2.4.1. It doesn't find any results.
You're going to have to provide a minimal example that demonstrates that. @ervandew's merged code works for everyone else's cases.
See discussion here: https://github.com/ervandew/eclim/pull/358
I am having a big project, with 8 eclipse projects (for dao, services, war etc.). If I just do :JavaSearch -p MyClass.MyMethod -t method -x implementors
, then it will go to the desired method. However, If I just do :JavaSearch -p MyMethod
this will return no result. Not even a list of possible results (MyMethod
exists in several classes).
This is a little annoying because I need to do first :JavaSearchContext
, for vim to open the desired interface and to point the cursor on the method, and then I mapped the following command to take me to the implementation: autocmd FileType java nnoremap <leader>i :JavaSearch -p <c-r>=substitute(fnamemodify(@%, ':t'), '\v\.java$', '', 'g')<cr>.<c-r>=expand('<cword>')<cr> -t method -x implementors<cr>
. So, I do it two steps. First I go to the definition and from there to implementation.
But it would be nice if I could go directly to the implementation of the method, if this command would work: :JavaSearch -p MyMethod -t method -x implementors
. I realize that for this to work I need to give back the name of the class, also, not just the method, which is a little bit complicated. The best solution it would be to have something like :JavaSearchContext -x implementors
. That would be basically the best solution.
Until then, I am happy with the workaround I've found.
The code and the feature are for the -x implementors
flag, so it sounds
like there's nothing wrong related to this ticket.
I assume you meant -p MyClass.MyMethod
in your counter example? Haven't
looked at that flag but I suspect without being qualified with the class
name it wouldn't find what you're looking for.
Have you tried simply placing the cursor over the method name and omitting
-p from the command? I never use that flag, myself. Also, if you don't want
implementations, you can try -x declarations
, I believe.
On Mon, Mar 2, 2015, 9:42 AM cosminadrianpopescu [email protected] wrote:
I am having a big project, with 8 eclipse projects (for dao, services, war etc.). If I just do :JavaSearch -p MyClass.MyMethod -t method -x implementors, then it will go to the desired method. However, If I just do :JavaSearch -p MyMethod this will return no result. Not even a list of possible results (MyMethod exists in several classes).
— Reply to this email directly or view it on GitHub https://github.com/ervandew/eclim/issues/349#issuecomment-76722818.
That's exactly the problem. I need to do :JavaSearch MyClass.MyMethod -x implementors
. If I look to the eclim#java#SearchAndDisplay
function I can see that the function eclim#Execute
is called with a different command for JavaSearch
and JavaSearchContext
. For JavaSearch
only a pattern is sent, and then it's normal that it requires the class name. But, take for example the following code:
String s = myClass.MyMethod(params)
Now, if I am with the cursor on top of the MyMethod
word, is kinda difficult to map a vim shortcut there to tell eclim that I am referring to MyClass class.
While the other function, JavaSearchContext
is sending a command indicating to eclim the position in the file, and the file in which we are executing the autocomplete. This way, eclim will know exactly from which class the method MyMethod
comes. But the problem is that this function does not accept the -x
flag.
This is why I need that workaround. Once I do JavaSearchContext
, vim will open the file containing the class in which MyMethod
is defined. From there, if the file is called MyClass.java
, I can assume that the class name is MyClass
(so, the need for fnamemodify
call) and then do the next call with the current word (expand(<cword>)
) adding the name of the class.
If the JavaSearchContext
command would accept the -x
flag and take vim directly to the implementation, this would be great. Otherwise, I will continue to use this workaround or I will keep searching in the source code, maybe I find a better work around.
I'm not sure what is causing your difficulty, but I think there is some
misunderstanding. I'm not sure where you see that a different command is
used. JavaSearchContext
is a convenience for JavaSearch
—If you look at
ftplugin/java.vim, both pass the same args to
eclim#java#search#SearchAndDisplay
, so they both use the java_search
command. The difference is that JavaSearchContext
just tries to figure
out which -x
flag you meant based on the element under the cursor. If you
omit the -p
flag, both methods will use the element under the cursor.
It is not difficult to figure out what method is under the cursor; Eclipse
has a model of the source document and knows exactly what's under it. I
just tried doing :JavaSearch
with no args on a static method as in your
example and it jumped to exactly the right place. Perhaps I'm still
misunderstanding your use case, but it seems like you're trying to find the
definition of a static method. For that case, -x implementors
is not what
you want. That flag is for locating implementations of an abstract
method.
Perhaps you are using an old version of Eclim? I noticed you referenced
eclim#java#SearchAndDisplay
instead of
eclim#java#search#SearchAndDisplay
. Was that just a typo?
On Mon, Mar 2, 2015 at 1:21 PM cosminadrianpopescu [email protected] wrote:
That's exactly the problem. I need to do :JavaSearch MyClass.MyMethod -x implementors. If I look to the eclim#java#SearchAndDisplay function I can see that the function eclim#Execute is called with a different command for JavaSearch and JavaSearchContext. For JavaSearchContext only a pattern is sent, and then it's normal that it requires the class name. But, take for example the following code:
String s = myClass.MyMethod(params)
Now, if I am with the cursor on top of the MyMethod word, is kinda difficult to map something there to tell eclim that I am referring to MyClass class.
While the other function, JavaSearchContext is sending a command indicating to eclim the position in the file, and the file in which we are executing the autocomplete. This way, eclim will know exactly from which class the method MyMethod comes. But the problem is that this function does not accept the -x flag.
This is why I need that workaround. Once I do JavaSearchContext, vim will open the file containing the class in which MyMethod is defined. From there, if the file is called MyClass.java, I can assume that the class name is MyClass (so, the need for fnamemodify call) and then do the next call with the current word (expand(
)) adding the name of the class. If the JavaSearchContext command would accept the -x flag and take vim directly to the implementation, this would be great. Otherwise, I will continue to use this workaround or I will keep searching in the source code, maybe I find a better work around.
— Reply to this email directly or view it on GitHub https://github.com/ervandew/eclim/issues/349#issuecomment-76768721.
That was just a typo. I am using eclim 2.4.1 which I installed a few days ago. JavaSearchContext
is not a convenience for JavaSearch
. Check the eclim#java#search#SearchAndDisplay
function. This is calling the s:Search
function defined in the eclim/java/search.vim
file.
This function has a big if
:
if argline !~ '-p\>'
So, basically, if we are calling JavaSearchContext
we will be on the main branch of this if
. If we are calling JavaSearch
we are going to be on the else branch of this If. So, doing JavaSearch -p
, the command sent to eclim is something like this:
-command java_search -n "spring-framework-2.5" -f "src/org/springframework/aop/aspectj/AbstractAspectJAdvice.java" -p "getUserAttribute"
You can notice that no position is sent regarding the file in which the search is done, so from here we can understand the need to search by prefixing with a class.
But doing JavaSearchContext
with the cursor on top of the getUserAttribute
method, the command sent to the server is
-command java_search -n "spring-framework-2.5" -f "src/org/springframework/aop/aspectj/AbstractAspectJAdvice.java" -o 24521 -e utf-8 -l 16
You can see that now the position in the file is sent to eclim
. So now, eclim will know that the method getUserAttribute
is part of the ProxyMethodInvocation
(examples from the spring framework source code). The problem is that JavaSearchContext
will just ignore the -x
flag and will always redirect vim to the interface. From there, using the workaround described above, I can reach the implementation of the method with another vim
mapping.
Basically I would like for the JavaSearchContext
to take into consideration the -x
flag.
JavaSearchContext is not a convenience for JavaSearch
Yes, it is:
if !exists(":JavaSearch")
command -buffer -nargs=*
\ -complete=customlist,eclim#java#search#CommandCompleteSearch
\ JavaSearch :call eclim#java#search#SearchAndDisplay('java_search', '<args>')
endif
if !exists(":JavaSearchContext")
command -buffer -nargs=?
\ -complete=customlist,eclim#java#search#CommandCompleteSearchContext
\ JavaSearchContext :call eclim#java#search#SearchAndDisplay('java_search', '<args>')
endif
The only difference is the completion function.
So, doing JavaSearch -p, the command sent to eclim is something like this:
No position is sent because you are using -p
. You don't need to; it's optional. Try just using :JavaSearch
with no args and it will probably go where you want. If you are looking an abstract method, then :JavaSearch -x implementors
will do what you expect.
I agree with you that the command is calling the same function. But the <args>
is different for the calls. Inside this function (SearchAndDisplay
) there is a distinction depending on the two totally different sets of arguments.
" check if just a pattern was supplied.
if argline =~ '^\s*\w'
let argline = '-p ' . argline
endif
So, doing JavaSearch
even without -p
, the -p
will be added automatically. So JavaSearch
is the same with or without -p
The position is sent when not using -p
. Please check the two commands. The one from JavaSearchContext
is sending -o 24251
, which is the position inside the file. This is very important. Without the position we don't know from which class the method is coming.
I am speaking about a method from a class which implements an interface. In this case, JavaSearchContext
will take me to the interface, while JavaSearch -p
needs to be prefixed with the class. This is not optimal, because prefixing with a class name cannot be done using a vim mapping. I am trying to go to the definition using a vim mapping.
I can always do JavaSearch -p MyClass.MyMethod -x implementors
. But this cannot be mapped to a key in vim
, since is complicated to extract the name of the class and put it into a mapping. For this I have the workaround.
But it would be nice to jump directly to the function definition, not the interface, by using a single vim
command. The big difference between JavaSearchContext
and JavaSearch
is that JavaSearchContext
is providing the position in the current file to eclim . See the -o
parameter. This way, eclim will find by itself the class and will redirect.
Thank you for your support and your patience.
So JavaSearch is the same with or without -p
No, it's not. You're misreading that. If you do :JavaSearch hello
then it will interpret that as :JavaSearch -p hello
. If you do :JavaSearch
by itself, or with -x implementors
, it will not do a pattern search. Let me prove it to you.
Daniels-iMac:eclim dhleong$ git diff
diff --git a/org.eclim.jdt/java/org/eclim/plugin/jdt/command/search/SearchCommand.java b/org.eclim.jdt/java/org/eclim
index 21deb94..6c5e91a 100644
--- a/org.eclim.jdt/java/org/eclim/plugin/jdt/command/search/SearchCommand.java
+++ b/org.eclim.jdt/java/org/eclim/plugin/jdt/command/search/SearchCommand.java
@@ -38,6 +38,8 @@ import org.eclim.annotation.Command;
import org.eclim.command.CommandLine;
import org.eclim.command.Options;
+import org.eclim.logging.Logger;
+
import org.eclim.plugin.core.command.AbstractCommand;
import org.eclim.plugin.core.util.ProjectUtils;
@@ -88,6 +90,9 @@ import org.eclipse.jdt.core.search.SearchPattern;
public class SearchCommand
extends AbstractCommand
{
+ private static final Logger logger =
+ Logger.getLogger(SearchCommand.class);
+
public static final String CONTEXT_ALL = "all";
public static final String CONTEXT_DECLARATIONS = "declarations";
@@ -166,6 +171,8 @@ public class SearchCommand
String length = commandLine.getValue(Options.LENGTH_OPTION);
String pat = commandLine.getValue(Options.PATTERN_OPTION);
+ logger.info("Offset: " + offset + "; pattern: " + pat);
+
SearchPattern pattern = null;
IJavaProject javaProject = project != null ?
JavaUtils.getJavaProject(project) : null;
I add a log that dumps the "offset" and "pattern" arguments. First:
:JavaSearch
2015-03-02 15:23:45,155 INFO [org.eclim.plugin.jdt.command.search.SearchCommand] Offset: 3677; pattern: null
Next:
:JavaSearch -x implementors
2015-03-02 15:25:13,516 INFO [org.eclim.plugin.jdt.command.search.SearchCommand] Offset: 3677; pattern: null
Now:
:JavaSearch hello
2015-03-02 15:25:45,362 INFO [org.eclim.plugin.jdt.command.search.SearchCommand] Offset: null; pattern: hello
And of course:
:JavaSearch -p hello
2015-03-02 15:26:07,240 INFO [org.eclim.plugin.jdt.command.search.SearchCommand] Offset: null; pattern: hello
Do you believe me now? You do not need -p
.
If you try :JavaSearch -x implementors
on an abstract method reference (eg: ISomething foo = /* .. */; foo.doBar()
, cursor on doBar
) and you get no results, then that would be a bug with the -x implementors
implementation that @ervandew did, and could perhaps be discussed in this ticket. If you do :JavaSearch
on anything and it doesn't find declarations, that would also be a bug, but should be filed separately since it's unrelated to this ticket.
I do believe you, of course. The problem is that this does not work. JavaSearch -x implementors
with the cursor on top of my function does not return any result (No results for 'getContract'
). The command sent to eclim
is this: -command java_search -n "immogest-war" -f "src/main/...java" -o 6089 -e utf-8 -l 11 -x implementors
.
So you are right. But also, doing JavaSearchContext -x implementors
returns the same command. The problem is that this return no results, while ``-command java_search -n "immogest-war" -f "src/main/...java" -o 6089 -e utf-8 -l 11 (exactly the same command but without -x implementors
takes me to the interface definition.
So I suppose I should open a bug?
You are telling me that JavaSearchContext -x implementors
should work?
I think what you want is just :JavaSearch -x implementors
. Based on the
vim declaration of :JavaSearchContext
, (using -nargs=?) it should only
accept at most the optional string pattern.
With that said, yes, -x implementors
should work, if the getContract()
method is abstract and the implementation is on the classpath of the
project. I assume you've double-checked that Eclipse by itself will give
you the right thing if you ctrl-click and select "Open Implementations"? If
that works but Eclim doesn't, then @ervandew will probably need a minimal
project (or projects) that reproduces the bug. I would file it separately.
On Tue, Mar 3, 2015 at 2:34 AM cosminadrianpopescu [email protected] wrote:
I do believe you, of course. The problem is that this does not work. JavaSearch -x implementors with the cursor on top of my function does not return any result (No results for 'getContract'). The command sent to eclim is this: -command java_search -n "immogest-war" -f "src/main/...java" -o 6089 -e utf-8 -l 11 -x implementors.
So you are right. But also, doing JavaSearchContext -x implementors returns the same command. The problem is that this return no results, while
-command java_search -n "immogest-war" -f "src/main/...java" -o 6089 -e utf-8 -l 11 (exactly the same command but without -x implementors
takes me to the interface definition.So I suppose I should open a bug?
You are telling me that JavaSearchContext -x implementors should work?
— Reply to this email directly or view it on GitHub https://github.com/ervandew/eclim/issues/349#issuecomment-76899786.
Yes, of course eclipse works. Also vim eclim works as described above: I can do JavaSearch -p MyClass.getContract -t method -x implementors
and this will work. The problem is that I need to prefix it with the class and by doing this, I cannot have a vim mapping to take me directly to the implementation.
I go first to definition (JavaSearchContext
), and from there I assume that the name of the file is also the name of the class and I can have one vim mapping to execute the JavaSearch -p
command.
Anyway, thank you very much for your support.
This works, will take the method under the cursor and will look for the implemenation (not neccesary to prefix with tthe class name):
nnoremap <leader>jsi :JavaSearch -p <C-R><C-W> -t method -x implementors<CR>
@alnavv , this only works if there exists one implementation of that method. If there is a different class hierarchy where the same method is implemented, this shows all possible implementations of other class hierarchies and not only the ones related to the current context