Angular2 HTML vs HTML PsiReferenceRegistrar

Answered

I'm having an issue creating a reference from an Angular2 HTML template into my DSL. I have a PsiReferenceRegistrar that can succesfully create a reference and resolve it when the language is plain HTML. For this I have placed a reference on the XmlAttributeValue with the specific TextRange and all works fine.

However, most people in the project will use the Angular2 HTML template dialect which creates a whole new set of PSI elements in the tree. The XmlAttributeValue is still provided as a pluggable element and I can see the reference being created. However, when I try to goto the declaration it never resolves to the target element in my DSL. I have tried to alternate the TextRange from the full XmlAttributeValue to the lowest PsiElement in the tree (JSLiteralExpression), in every case I can see the reference being created and, is initially resolved by the annotator. When I deliberatly make the reference unresolvable I can see a 'Cannot resolve symbol' warning.

The html code:

<element onClick="mycomponent['somekey'].doSomething()" />

I want to create a reference on 'somekey', preferably without the quotations. I have tried it with TextRange that matches somekey, 'somekey' and the entire XML_ATTRIBUTE_VALUE. All return a resolvable reference, but that specified reference is not resolved when I ctrl+click the element. 

I can see the nodetypes: HTMLTAG, XML_ATTRIBUTE and XML_ATTRIBUTE_VALUE being pluggable (I mean they are provided to the pattern matcher in the PsiReferenceRegistrar).

Is there something in the PsiTree that is overshadowing the reference that I'm creating? And can I make a Psi element in another language pluggable so it's added to the list of types that is considered by the PsiReferenceRegistrar.

This issue is kind of blocking for my next release since I wouldn't want to tell people they have to use the plain HTML parser to make the plugin integration work

Any help is much appreciated.

3 comments
Comment actions Permalink

I found a solution by making a custom GotoDeclarationHandler extension to capture the reference and ReferencesSearch extension to consume the reference for the FindUsages. It works fine but it feels like a lot of code for something quite generic in the platform. Still hoping there is a cleaner way to do this :)

0
Comment actions Permalink

Hi Tim!

In case of attributes with JavaScript expressions, the content in XML Attribute Value is lazy parsed as Embedded Java Script. The navigation doesn't work, because there are JavaScript PSI elements within the attribute value and they take over navigation. If you want to inject a reference into a JavaScript literal it is much better to contribute reference into JSLiteralExpression PSI element using a PsiReferenceContributor (e.g. https://github.com/JetBrains/intellij-plugins/blob/master/AngularJS/src/org/angular2/codeInsight/refs/Angular2ReferencesContributor.java and VIEW_CHILD_PATTERN contribution). To limit references provision to HTML context only, you can check the language of the file or the host file (in case JavaScript is injected) - use com.intellij.psi.impl.source.resolve.FileContextUtil#getContextFile() to obtain the top-most file. Let me know if this approach worked for you.

Are you using Psi Viewer plugin to explore the PSI tree of the file?

0
Comment actions Permalink

Hi Piotr, 

thanks for your detailed answer. I'm pretty sure that I have tried something similar as you suggested but the pattern you describe (VIEW_CHILD_PATTERN) takes a JSLiteralExpression. Not all psi elements are provided, when I debug I can only see HTMLTAG, XML_ATTRIBUTE and XML_ATTRIBUTE_VALUE being checked in the pattern.

To debug I used:

registrar.registerReferenceProvider(PlatformPatterns.psiElement().with(pattern)

and then see which elements are entering the pattern

 

Yes I'm using the Psi Viewer plugin

0

Please sign in to leave a comment.