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
Please sign in to leave a comment.
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
);
}
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.
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
?
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.
could you update git with the most recent .bnf and completion files, please?
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
can you autogen psi from this bnf?
identifier does not seem to be defined anywhere, no? I mean, like this:
identifier ::= ...
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.
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?
Sure, I'd like the completion to get triggered immediately after I finish typing "::". So in .bnf:
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:
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:
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.
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.