Documentation for LookupItems

Hi ! I'm trying to show the quick documentation for looupitems in the code completion list.

But the problem is that I need to return a PsiElement from:

public PsiElement getDocumentationElementForLookupItem(PsiManager psiManager, Object object, PsiElement element)


but how can I return the PsiElement if it doesn't exist yet ?? (I want to show the documentation for an item in the completion list...)

Thanks !!

0
11 comments

This should be the item you're completing, most probably the result of LookupElement.getObject(). Or some fake PSI element if your object is not a PSI element. The main thing should be that your DocumentationProvider should be able to recognize this element and generate the correct documentation.

0
Avatar
Permanently deleted user

Thanks Peter,maybe I should pass something more useful to my object inside my LookupElement.

When I'm creating my LookupElement I'm doing in this way:

LookupElement element = LookupElementBuilder.create(count, value)          // note the "count" var
                                        .setPresentableText(name)
                                        .setIcon(myIcon)
                                        .setInsertHandler(INSERT_HANDLER)
                                        .setTailText(tailText, true)


Note that I'm creating the LookupElement from text only:
value is something like "<tagName></tagName>"
and I'm putting inside the object an integer, that I'm using inside my own weigher for sorting the results (I was thinking in extending LookupElementBuilder to add an extra property and saving there my count var, but LookupElementBuilder is not extensible, it has a lot of final and private members).

I don't have any PSI in my LookupElement. Please tell me if I'm doing it wrong.

I need to add documentation for the node to be inserted, so I need the PSI element to be inserted, not the current PsiElement.

0

Instead of putting an integer as an Object, I'd use PrioritizedLookupElement or user data, and make something else a lookup object. Something, which identifies your item unambiguously. Maybe a your own instance of FakePsiElement. Or any custom object, but then you'd have to create that fake element in the documentation provider.

Documentation requiring a PsiElement is a relic of old times when every lookup item had to have a PSI, sorry for that. We're going to decouple it from PSI when we know how.

0
Avatar
Permanently deleted user

Peter, can you show me how to create a FakePsiElement?
I need the full hierarchy of the original PsiElement for finding the right documentation. And I'm seeing the implementation of FakePsiElement has a lot of methods with return null.
Should I create my own class extending FakePsiElement?

Also, is possible to restrict my documentationProvider to some file extension, or something like that, directly from the plugin.xml? or I need to make my own check/filter inside my documentationProvider?

0

It appears that it's actually easier to create LightElement, which is almost the same (don't ask me why we have two). You subclass it, no need to implement anything, and then check instanceof your class. In your element, you may have all the information necessary for documentation retrieval. So, after instanceof-ing, you just get it from there.

0
Avatar
Permanently deleted user

I need the full hierarchy of my XML tag to show the right documentation.

This is my solution:

            String name = (String) object;
            PsiElement psiParent = PsiTreeUtil.getParentOfType(element, XmlTag.class);
            if(psiParent != null && XmlHelper.isXmlTag(psiParent)){
                XmlTag xmlTagParent = (XmlTag) psiParent;
                if(XmlHelper.isXmlTagIncomplete(xmlTagParent)){
                    xmlTagParent = PsiTreeUtil.getParentOfType(xmlTagParent, XmlTag.class);
                }
                if(xmlTagParent != null){
                    
                    XmlTagParentsFakeElement fake = new XmlTagParentsFakeElement();
                    fake.setParent(xmlTagParent);                    
                    fake.setName(name);                    
                    return fake;

                }
            }


This works, but I don't like it :(

Before that I've tried this:


            String name = (String) object;
            PsiElement psiParent = PsiTreeUtil.getParentOfType(element, XmlTag.class);
            if(psiParent != null && XmlHelper.isXmlTag(psiParent)){
                XmlTag xmlTagParent = (XmlTag) psiParent;
                if(XmlHelper.isXmlTagIncomplete(xmlTagParent)){
                    xmlTagParent = PsiTreeUtil.getParentOfType(xmlTagParent, XmlTag.class);
                }
                if(xmlTagParent != null){

                    XmlTag fake = (XmlTag) xmlTagParent.copy();
                    XmlTag newTag = XmlElementFactory.getInstance(xmlTagParent.getProject()).createTagFromText('<' + name + "></" + name + '>');
                    fake.addSubTag(newTag, true);
                    return XmlUtil.findSubTag( fake, newTag.getName());

                }
            }


but it didn't work, I couldn't add the newTag to the "fake" tag (I can see it with getChildren() but not with getSubTags(), and I couldn't make "fake" as the parent of "newTag" )

0

I'm not sure I understand this completely, in particular, what element variable is.
One should avoid calling setParent, it's too low-level an API, so yes, I don't like it either :)
Why didn't addSubTag work? What was the error you got?

0
Avatar
Permanently deleted user

element is the element from the function parameter:

public PsiElement getDocumentationElementForLookupItem(PsiManager psiManager, Object object, PsiElement element) {

        if(object instanceof String){
            String name = (String) object;
            PsiElement psiParent = PsiTreeUtil.getParentOfType(element, XmlTag.class);
            if(psiParent != null && XmlHelper.isXmlTag(psiParent)){
                XmlTag xmlTagParent = (XmlTag) psiParent;
                if(XmlHelper.isXmlTagIncomplete(xmlTagParent)){
                    xmlTagParent = PsiTreeUtil.getParentOfType(xmlTagParent, XmlTag.class);
                }
                if(xmlTagParent != null){

                    XmlTagParentsFakeElement fake = new XmlTagParentsFakeElement();
                    fake.setParent(xmlTagParent);
                    fake.setName(name);

//                    XmlTag fake = (XmlTag) xmlTagParent.copy();
//                    XmlTag newTag = XmlElementFactory.getInstance(xmlTagParent.getProject()).createTagFromText('<' + name + "></" + name + '>');
//                    fake.addSubTag(newTag, true);
//                    return XmlUtil.findSubTag( fake, newTag.getName());

                    return fake;

                }
            }
        }

        return null;

    }


my setParent is really really fast (I guess), this is my class

public class XmlTagParentsFakeElement extends FakePsiElement /*LightElement*/ {

    protected PsiElement parent;
    protected String name;

    @Override
    public PsiElement getParent() {
        return parent;
    }

    public void setParent(PsiElement newParent){
        parent = newParent;
    }

    public PsiElement setName(String tagName){
        name = tagName;
        return this;
    }

    public String getName(){
        return name;
    }

    public List<String> getParents()
    {
        //List<XmlTag> parents = XmlHelper.getParents(getParent());
        List<XmlTag> parents = XmlHelper.getParents(this);
        List<String> parentsNames = new ArrayList<String>();
        for(XmlTag curTag : parents) {
            parentsNames.add(curTag.getName());
        }
        return parentsNames;
    }

    public List<String> getFullPath()
    {
        List<String> path = getParents();
        path.add(this.getName());
        return path;
    }

}



also, the addSubTag is not trowing any error, is just not working, that is, when I call fake.getSubTags() I get only a black XmlTag (which is the original I guess). When I call xmlTag.getParent() I get the DocumentRoot (not the fake XmlTag) and when I call XmlUtil.findSubTag( fake, newTag.getName()) I get null.

0

That's really strange because the approach with copying a tag and adding a child to it should work. Perhaps it doesn't work good with incomplete tags. You may try to debug addSubTag, it's not very complex. You may also want to look at what all those elements' getText returns.

0
Avatar
Permanently deleted user

Peter, I think the problem was the ".copy()"
I've tried this:


                    //XmlTag fake = xmlTagParent.copy();
                    final XmlTag fake = xmlTagParent;
                    final XmlTag newTag = XmlElementFactory.getInstance(xmlTagParent.getProject()).createTagFromText('<' + name + "></" + name + '>');
                    ApplicationManager.getApplication().runWriteAction(new Runnable() {
                        @Override
                        public void run() {
                            fake.addSubTag(newTag, true);
                        }
                    });
                    return XmlUtil.findSubTag( fake, newTag.getName());


And it works, but I'm not sure if it's correct to do something like that, I'm adding a new PsiElement to the original Psi, what happens when I do that? I was expecting to see that new tag on my editor, but it's not, I don't now...
Also, I'm seeing a very weird behavior:

When I request the documentation on my lookupElement it executes my provider, creates the new "fake" subtag, etc.
But then if I request again the documentation for the same lookupElement my provider is not executed !
Even more, when I click Ctrl+Space again for showing the completion list I have a new LookupElement on that list, which seems to be a copy of the original item (the one I requested the documentation in first place). This new lookupelement is on the top of my completion list, is duplicated (I have the original too), and it's doing the same the original one (but it doesn't have my custom icon).

EDIT: about the last thing, it's not related with the documentationProvider. I think it's a typical behavior from jIDEA, it's suggesting on my code completion list all the siblings when I request the code completion after writting "<". I'm solving that with result.stopHere().

0

Stranger and stranger. Is it possible for you to just share the code so that I could look at the whole picture?

BTW have you tried to do all that tag copying/creation in CompletionContributor, and just attaching it to your LookupElement in any way, user data perhaps? That would eliminate changing physical PSI and probably make things a bit faster.

0

Please sign in to leave a comment.