completion on non-dot qualifiers

Hi -- I'm trying to implement reference completion for a custom language and I can't seem to get the completions to pop-up correctly
for qualified types -- where the qualifier symbol is "::". Attached is a picture of my Psi for Type_Reference_Expression Psi node.

and here's a snippet of my current .bnf grammar responsible for the 'type ref' part of my psi tree:

 
OperationProcedureDecl ::= OPERATION identifier LPAREN RPAREN COLON TypeRefNode

TypeRefNode ::= TypeReferenceExpression QualifiedTypeReferenceExpression?
TypeReferenceExpression ::= identifier
{ methods=[getReference getQualifier] }
left QualifiedTypeReferenceExpression ::= '::' identifier {elementType=TypeReferenceExpression}


I've already extended CompletionContributor and of course activated it in the plugin's .xml file. Right now the pattern that I'm using in the
contributor's extend call just looks like this:

 
public RESOLVECompletionContributor() {
    extend
(CompletionType.BASIC, referenceExpression(), new RESOLVEReferenceCompletionProvider());
}

private static PsiElementPattern.Capture<PsiElement> referenceExpression
() {
    return PlatformPatterns.psiElement
().withParent(ResReferenceExpressionBase.class); }


But it doesn't seem to kick in unless I type at least one character past the "::" (which is annoying since I want all visible symbols to appear in
the lookup box initially)..

What's also odd is that If I replace change the '::' part of the grammar above to ".", everything works perfectly.. The completion contributor kicks
in immidiately after I type "." and addCompletion is invoked with the dummy "IntellijIdeaRulezzz" string. Been stuck here for the better part of a day, what's going on here? Why am I not able to use my own qualifier symbol -- do I have to use dot?

Here's the link to the project's branch-in-question on GitHub: https://github.com/Welchd1/resolve-intellij-plugin/tree/bnf/src/edu/clemson/resolve/plugin



Attachment(s):
qualifier-psi.png
11 comments
Comment actions Permalink

Hello,

in RESOLVECompletionContributor

you call extend

.withElementType(ResTypes.IDENTIFIER)


ResTypes.IDENTIFIER is only 1 element type, right?


I'd try this:

change RESOLVECompletionContributor so that it calls extend for every element type which makes up your identifier symbol (assuming the symbol is made up of parts):

IElementType[] types = TokenSet.getTypes();

      for (IElementType t : types) {
         extend(CompletionType,
            PlatformPatterns.psiElement(t)
               .withLanguage(Language),

            CompletionProvider
         );
      }

0
Comment actions Permalink

Hi Imants,

Are you looking at the referenceExpression() capture pattern in RESOLVECompletionContributor? That one doesn't contain a .withElementType(..) ... the usesSpec() pattern does -- but that's not involved here: the one which I'm dealing is used in the second call to extend (that is, the referenceExpression() pattern). That's the one controlling whether the addCompletions() kicks in or not.

And also I believe (if I understand IElementType correctly) the "::" qualifier symbol should be its own element type, as I define a token for it just like that in the lexer.

So I'm still stuck.

0
Comment actions Permalink

well it appears that the reason completion is not triggered is : some element type does not trigger completion, when it should.

I suggest to Capture additional element types.

let's say, your symbol is defined as:

symbol ::= WORD COL WORD

(simplified)

you could create a tokenset "completions" which includes SYMBOL and WORD

then if you call extend with both SYMBOL and WORD, completions may be triggered as expected.



just thought of something: capture is one thing, but trigger may be another.

is addCompletions method of RESOLVEUsesCompletionProvider called?

if yes, does it get past this line:

https://github.com/Welchd1/resolve-intellij-plugin/blob/bnf/src/edu/clemson/resolve/plugin/completion/RESOLVEUsesCompletionProvider.java#L43

?

0
Comment actions Permalink

Yeah, for usesItems my pattern seems to work correctly and addCompletions gets called (and it does indeed make it past that "null" check..),

I've been messing around with my grammar rule a little bit in light of what you mentioned before, but I guess I really just don't understand these patterns... :)

I've broken down and for testing purposes am just trying to capture "::" (for the moment disregarding the "identifier" that immediately precedes it) and I can't even get that to match.. e.g.:

something like:

 
public RESOLVECompletionContributor() {
     extend(CompletionType.BASIC, usesSpec(),
             new RESOLVEUsesCompletionProvider());
     extend(CompletionType.BASIC, referenceTypeExpression(), new RESOLVEReferenceCompletionProvider());
}

// private static PsiElementPattern.Capture<PsiElement> referenceExpression() {
//     return psiElement().withParent(ResReferenceExpressionBase.class);
// }

private static
PsiElementPattern.Capture<PsiElement> referenceTypeExpression() {
     return psiElement().withElementType(ResTypes.COLONCOLON);
}


addCompletions in RESOLVEReferenceCompletionProvider never even gets called here... grr.

0
Comment actions Permalink

could you update git with the most recent .bnf and completion files, please?

0
Comment actions Permalink

Just pushed to branch bnf (was pretty up to date). I might've just made my TypeRefNode grammar rule worse though. Hard for me to say.

Here's the link to main page for the bnf branch

https://github.com/Welchd1/resolve-intellij-plugin/tree/bnf

0
Comment actions Permalink

can you autogen psi from this bnf?

identifier does not seem to be defined anywhere, no? I mean, like this:

identifier ::= ...

0
Comment actions Permalink

Yeah, it's admittedly odd but that seems to be how other (bigger-name ;) )  plugins do it. For instance if you look at the .bnf grammar for the go language (https://github.com/go-lang-plugin-org/go-lang-idea-plugin/blob/master/grammars/go.bnf) they also don't have a parser rule for that (instead its defined in the lexer as IDENT = {LETTER} ({LETTER} | {DIGIT} )*

(you can read their flex file here: https://github.com/go-lang-plugin-org/go-lang-idea-plugin/blob/master/src/com/goide/lexer/go.flex)

 

for then they connect IDENT to 'identifier' in the lower bit: {IDENT}                                  {  yybegin(MAYBE_SEMICOLON); return IDENTIFIER; } (though I don't do this yybegin(MAYBE...) stuff..)

I'm certainly not well versed in JFlex or .BN, but am slowly picking up more -- so pardon my ignorance on the subject. Personally I'd prefer to be using ANTLR -- but oh well.

EDIT:
And yes: a workable PSI get's autogened from .bnf you're looking at.

0
Comment actions Permalink

well if it works, this is all that matters ;)

I do not understand bnf well. Particularly, what is this "identifier" thingy.

in this example:
http://cui.unige.ch/db-research/Enseignement/analyseinfo/AboutBNF.html

there is explicit definition:

identifier ::=  letter { letter | digit }




anyway, back to this case in hand:

could you highlight the definition in bnf which describes the case where completion should be triggered but is not?

0
Comment actions Permalink

Sure, I'd like the completion to get triggered immediately after I finish typing "::". So in .bnf:

 
TypeReferenceExpression ::= (Qualifier COLONCOLON)?


immediately after the COLONCOLON... Though I haven't been able to write a pattern that matches it... So just to be clear, here is the original "TypeReferenceExpression" rule I was using:

 
TypeRefNode ::= TypeReferenceExpression QualifiedTypeReferenceExpression?
TypeReferenceExpression ::= identifier { methods=[getReference getQualifier] }
left QualifiedTypeReferenceExpression ::= COLONCOLON identifier {elementType=TypeReferenceExpression}


and here is the pattern that I was hoping that would trigger the completion immediately after users typed the COLONCOLON piece:

 
private static PsiElementPattern.Capture<PsiElement> referenceExpression() {
     return psiElement().withParent(ResReferenceExpressionBase.class);
}

This is essentially the same as Go's typeReferenceExpression except they use a "." instead of "::". And for some reason, if I change that COLONCOLON above to a DOT, addCompletion correctly kicks in... It's  just for some reason changing the DOT to COLONCOLON breaks that.. and I can't seem to figure out why. I guess it has to do with the pattern -- psiViewer isn't being especially helpful here because I can't really see via the actual tree what makes the two situations so distinct..

After talking with you I've since changed the grammar rule into a form that I thought I could write a more specific pattern for, I.e., this:

 
TypeRefNode ::= TypeReferenceExpression
TypeReferenceExpression ::= (Qualifier COLONCOLON)? identifier { methods=[getReference getQualifier] }
Qualifier ::= identifier {elementType=TypeReferenceExpression}


but haven't been successful. That being said the first rule is "closer" to what I'm looking in that I can get it to work for DOT -- but frustratingly further since the seemingly superficial change to COLONCOLON somehow 'breaks it'.

EDIT:

and googling BNF grammar isn't especially helpful in this regard since you'll just get articles discussing Backaus Naur Form (which is just a way of presenting grammar rules in a general manner) and doesn't have really have anything to do with intellij's .bnf grammar-kit grammars.

0
Comment actions Permalink

if this "identifier" were derived type (defined in other terms), I would run "extend" with each base type.

e.g.

1) private static PsiElementPattern.Capture<PsiElement> usesSpec() {
        return psiElement()
                .withElementType(ResTypes.IDENTIFIER)

2) ResTypes.Qualifier               
3) ResTypes.USES
4) ResTypes.TYPE
...

you see, when user types something, lexer "sees" base types before derived types are formed by parser. At some points, there is ambiguity re: what derived type will be made up from the base types.

this is one possibility I can think of.

0

Please sign in to leave a comment.