Navigation to custom language elements from Java code

The new property file support in IDEA 5 includes support for navigating
to properties from ResourceBundle.getString() method calls in Java code
as well as Find Usages for properties.

I was wondering if anybody has some hints for how to implement this? The
language that I'm supporting is just a configuration language (using
Python-like data structures like tuples and maps), so it doesn't have
reference expressions itself. The only expressions referencing the
"properties" in the configuration files would be calls in Java code to
get the property values from the Config objects which represent
configuration files.

From reading the article on developing custom language plugins, it
sounds like the parameters to Config.getX() method calls would have to
be able to resolve() to my custom language elements? How would I do that?

Thanks,
Gordon

--
Gordon Tyler (Software Developer)
Quest Software <http://www.quest.com/>
260 King Street East, Toronto, Ontario M5A 4L5, Canada
Voice: (416) 933-5046 | Fax: (416) 933-5001

12 comments

The way to do this is to use the non-OpenAPI ReferenceProvidersRegistry class.
You might call something like this:

ReferenceProvidersRegistry registry =
ReferenceProvidersRegistry.getInstance(project);
registry.registerReferenceProvider(PsiMethodCallExpression.class, new
PsiReferenceProvider() { ... });

Non-openapi classes are notorious for being flaky and fragile using from the
outside, probably because there's no documentation to guide you. I remember
seeing a post on here from Dmitry or someone from JB saying that this API will
change, but I don't know when.

Gordon Tyler wrote:

The new property file support in IDEA 5 includes support for navigating
to properties from ResourceBundle.getString() method calls in Java code
as well as Find Usages for properties.

I was wondering if anybody has some hints for how to implement this? The
language that I'm supporting is just a configuration language (using
Python-like data structures like tuples and maps), so it doesn't have
reference expressions itself. The only expressions referencing the
"properties" in the configuration files would be calls in Java code to
get the property values from the Config objects which represent
configuration files.

From reading the article on developing custom language plugins, it
sounds like the parameters to Config.getX() method calls would have to
be able to resolve() to my custom language elements? How would I do that?

Thanks,
Gordon

0

Keith Lea wrote:

The way to do this is to use the non-OpenAPI ReferenceProvidersRegistry
class. You might call something like this:

ReferenceProvidersRegistry registry =
ReferenceProvidersRegistry.getInstance(project);
registry.registerReferenceProvider(PsiMethodCallExpression.class, new
PsiReferenceProvider() { ... });


Hmm, okay. I'm implementing PsiReferenceProvider but I'm stuck at the
point where I need to find the elements to which the references resolve.
I have a property name that I extracted from a call to a
Config.getXXX(property) method and I need to scan all my custom
language's files in the project to find the property elements which have
the same name, but I can't find a nice way of doing that without
iterating through all the files in my project. There has to be a better way?

Part of my problem is that what I'm trying to do is not covered by the
"References and Resolve" section of the custom language plugins article
by Dmitry Jemerov, since it seems to assume that the references and the
elements they resolve to are within the same PSI structure. A section on
resolving references from Java code to custom language elements would be
very helpful. Also, tips on implementing PsiPolyVariantReference.

Thanks,
Gordon

--
Gordon Tyler (Software Developer)
Quest Software <http://www.quest.com/>
260 King Street East, Toronto, Ontario M5A 4L5, Canada
Voice: (416) 933-5046 | Fax: (416) 933-5001

0

Hello Gordon,

GT> Hmm, okay. I'm implementing PsiReferenceProvider but I'm stuck at
GT> the point where I need to find the elements to which the references
GT> resolve. I have a property name that I extracted from a call to a
GT> Config.getXXX(property) method and I need to scan all my custom
GT> language's files in the project to find the property elements which
GT> have the same name, but I can't find a nice way of doing that
GT> without iterating through all the files in my project. There has to
GT> be a better way?

You do need to iterate through all files, but there is a nice API for that:
ProjectRootManager.getInstance(project).getFileIndex().iterateContent()

GT> Part of my problem is that what I'm trying to do is not covered by
GT> the "References and Resolve" section of the custom language plugins
GT> article by Dmitry Jemerov, since it seems to assume that the
GT> references and the elements they resolve to are within the same PSI
GT> structure. A section on resolving references from Java code to
GT> custom language elements would be very helpful. Also, tips on
GT> implementing PsiPolyVariantReference.

I'll see what I can do to improve that part of the article.

What's unclear about PsiPolyVariantReference? The implementation of multi-resolve
can be very similar to that of regular resolve - just use a PsiScopeProcessor
which gathers all found references instead of stopping on the first one.

--
Dmitry Jemerov
Software Developer
JetBrains, Inc.
http://www.jetbrains.com
"Develop with pleasure!"


0

Keith Lea wrote:

The way to do this is to use the non-OpenAPI ReferenceProvidersRegistry
class. You might call something like this:

ReferenceProvidersRegistry registry =
ReferenceProvidersRegistry.getInstance(project);
registry.registerReferenceProvider(PsiMethodCallExpression.class, new
PsiReferenceProvider() { ... });


I've run into a problem in that the PsiReferenceProvider implementation
I registered is not having it's getReferencesByElement(PsiElement)
method called.

In my ProjectComponent's projectOpened method, I have the following code
to register my provider:

ReferenceProvidersRegistry registry =
ReferenceProvidersRegistry.getInstance(mProject);
registry.registerReferenceProvider(PsiMethodCallExpression.class, new
PCPropertyReferenceProvider(mProject));

I'm certain that this code is being called, since I stepped through it
with the debugger. However, a breakpoint set at the beginning of my
provider's getReferencesByElement method is never triggered.

Thanks,
Gordon

--
Gordon Tyler (Software Developer)
Quest Software <http://www.quest.com/>
260 King Street East, Toronto, Ontario M5A 4L5, Canada
Voice: (416) 933-5046 | Fax: (416) 933-5001

0

Gordon Tyler wrote:

I've run into a problem in that the PsiReferenceProvider implementation
I registered is not having it's getReferencesByElement(PsiElement)
method called.


No hints?

Thanks,
Gordon

--
Gordon Tyler (Software Developer)
Quest Software <http://www.quest.com/>
260 King Street East, Toronto, Ontario M5A 4L5, Canada
Voice: (416) 933-5046 | Fax: (416) 933-5001

0

Hello Gordon,

>> I've run into a problem in that the PsiReferenceProvider
>> implementation I registered is not having it's
>> getReferencesByElement(PsiElement) method called.
>>
GT> No hints?

Well, you see, there is a reason that usage of non-OpenAPI classes is called
"unsupported". :) When PsiReferenceProvider moves to OpenAPI (and I hope
that it happens quite soon), I'll be happy to assist you with its usage and
implementation.

--
Dmitry Jemerov
Software Developer
JetBrains, Inc.
http://www.jetbrains.com
"Develop with pleasure!"


0

Dmitry Jemerov (JetBrains) wrote:

Well, you see, there is a reason that usage of non-OpenAPI classes is
called "unsupported". :) When PsiReferenceProvider moves to OpenAPI
(and I hope that it happens quite soon), I'll be happy to assist you
with its usage and implementation.


Foo. 8( I was hoping since others had managed to use it successfully
that there was something I was just missing.

No hints whatsoever about the conditions which determine when the method
is called? 8)

Ciao,
Gordon

--
Gordon Tyler (Software Developer)
Quest Software <http://www.quest.com/>
260 King Street East, Toronto, Ontario M5A 4L5, Canada
Voice: (416) 933-5046 | Fax: (416) 933-5001

0

Hello Gordon,

GT> No hints whatsoever about the conditions which determine when the
GT> method is called? 8)

PsiReferenceProviders are currently only called to find references in plain-text
files, string literals and certain places in XML files (XML text, attribute
values and some others). They're not called for method call expressions.

--
Dmitry Jemerov
Software Developer
JetBrains, Inc.
http://www.jetbrains.com
"Develop with pleasure!"


0

Dmitry Jemerov (JetBrains) wrote:

PsiReferenceProviders are currently only called to find references in
plain-text files, string literals and certain places in XML files (XML
text, attribute values and some others). They're not called for method
call expressions.


Ah, that would do it. Hmm... I think I can work with that.

Thanks,
Gordon

--
Gordon Tyler (Software Developer)
Quest Software <http://www.quest.com/>
260 King Street East, Toronto, Ontario M5A 4L5, Canada
Voice: (416) 933-5046 | Fax: (416) 933-5001

0

Cool, that works. Except it's really slow using
ProjectRootManager.getInstance(project).getFileIndex().iterateContent().

I suppose the properties file support does some sort of caching when the
project is opened so that it can do fast lookups on property names?

Ciao,
Gordon

--
Gordon Tyler (Software Developer)
Quest Software <http://www.quest.com/>
260 King Street East, Toronto, Ontario M5A 4L5, Canada
Voice: (416) 933-5046 | Fax: (416) 933-5001

0

Hello Gordon,

GT> Cool, that works. Except it's really slow using
GT> ProjectRootManager.getInstance(project).getFileIndex().iterateConten
GT> t().
GT>
GT> I suppose the properties file support does some sort of caching when
GT> the project is opened so that it can do fast lookups on property
GT> names?

Yes, it caches the list of all property files in the project in one pass
of iterateContent(), and then uses VirtualFileManager.addVirtualFileListener()
to update the list when property files are added, moved or deleted. The contents
of property files are also cached.

--
Dmitry Jemerov
Software Developer
JetBrains, Inc.
http://www.jetbrains.com
"Develop with pleasure!"


0

Dmitry Jemerov (JetBrains) wrote:

Yes, it caches the list of all property files in the project in one pass
of iterateContent(), and then uses
VirtualFileManager.addVirtualFileListener() to update the list when
property files are added, moved or deleted. The contents of property
files are also cached.


Thanks, that gives me some direction to follow 8)

Ciao,
Gordon

--
Gordon Tyler (Software Developer)
Quest Software <http://www.quest.com/>
260 King Street East, Toronto, Ontario M5A 4L5, Canada
Voice: (416) 933-5046 | Fax: (416) 933-5001

0

Please sign in to leave a comment.