Detect parsing for completion

Hi.  I'm wondering what is the best way to detect that IStubFileElementType.parseContents() is being called for code completion.  Our parser needs to know and I'd rather not check for "IntellijIdeaRulezzz" in the contents if I can avoid that.  Thanks.

Scott

7 comments
Comment actions Permalink

Hello Scott,

Why exactly do you need to know that?

Hi.  I'm wondering what is the best way to detect that
IStubFileElementType.parseContents() is being called for code
completion.  Our parser needs to know and I'd rather not check for
"IntellijIdeaRulezzz" in the contents if I can avoid that.  Thanks.


--
Dmitry Jemerov
Development Lead
JetBrains, Inc.
http://www.jetbrains.com/
"Develop with Pleasure!"


0
Comment actions Permalink

Our plugin utilizes our language's internal parser and transforms our parse tree to IntelliJ's AST.  This strategy saves us from having to build a whole new parser for the plugin, which would be rather costly.  Our Annotator implementation needs to access the errors and warning corresponding with the most recent change in the editor. But annotators in general, it seems, work off the original PsiFile and not the one passed to IStubFileElementType.parseContents().  Since the incremental changes made to the original PsiFile are not parsed by our parser, the errors and warnings are never updated. Thus, our impl of parseContents() needs to grab the errors and warnings from the latest parse and hang them on the original file.  However, we don't want to do that if parseContents() is called for the purposes of generating an error for code completion.

Scott

0
Comment actions Permalink

Hello Scott,

During completion, a copy of the PSI file is created, and the dummy identifier
for completion is inserted into the copy. The copy is also created during
some other operations.

You can check that the reparse is being performed on a copy and not on the
original file using the following check in parseContents():

(PsiFile) chameleon.getPsi == ((PsiFile) chameleon.getPsi()).getOriginalFile()

If the two instances are the same, the parse is performed on the original
file. If they're different, the parse is performed on a copy.

Our plugin utilizes our language's internal parser and transforms our
parse tree to IntelliJ's AST.  This strategy saves us from having to
build a whole new parser for the plugin, which would be rather costly.
Our Annotator implementation needs to access the errors and warning
corresponding with the most recent change in the editor. But
annotators in general, it seems, work off the original PsiFile and not
the one passed to IStubFileElementType.parseContents().  Since the
incremental changes made to the original PsiFile are not parsed by our
parser, the errors and warnings are never updated. Thus, our impl of
parseContents() needs to grab the errors and warnings from the latest
parse and hang them on the original file.  However, we don't want to
do that if parseContents() is called for the purposes of generating an
error for code completion.


--
Dmitry Jemerov
Development Lead
JetBrains, Inc.
http://www.jetbrains.com/
"Develop with Pleasure!"


0
Comment actions Permalink

Thanks, Dmitry.  But from what I can gather changes in the editor are parsed as a *copy* and not on the original file.  From my observations the original file is parsed once and never again. It seems incremental changes in the editor are patched into the original file, it is never reparsed. Instead incremental changes are parsed separately via a copy of the file passed to parseContents().  However, this parsed copy is not passed to the annotator, it is always the original, which is why I need to know what operation is going on during parseContents().

Scott

0
Comment actions Permalink

Hello Scott,

Yep, that's actually correct. The copy created for the completion can be
distinguished in the following way:
chameleon.getPsi().getUserData(CodeCompletionHandlerBase.FILE_COPY_KEY) !=
null

This is, however, an implementation detail, and I can't promise that it won't
change in future IDEA versions.

Thanks, Dmitry.  But from what I can gather changes in the editor are
parsed as a copy and not on the original file.  From my observations
the original file is parsed once and never again. It seems incremental
changes in the editor are patched into the original file, it is never
reparsed. Instead incremental changes are parsed separately via a copy
of the file passed to parseContents().  However, this parsed copy is
not passed to the annotator, it is always the original, which is why I
need to know what operation is going on during parseContents().


--
Dmitry Jemerov
Development Lead
JetBrains, Inc.
http://www.jetbrains.com/
"Develop with Pleasure!"


0
Comment actions Permalink

Thanks again, Dmitry.  Unfortunately, that doesn't quite work.  After digging into the code a bit it appears the COMPLETION_CONTEXT_KEY isn't set until after parseContents() is called. Specifically, CodeCompletionHandlerBase.createCompletionParameters() commits the document via insertDummyIdentifier(), this in turn invokes the parser on the content.  Ironically, the result of insertDummyIdentifier() is the value of the COMPLETION_CONTEXT_KEY -- sort of a chicken and egg scenario. I'll dig a bit more and see if perhaps there's some other pattern or impl detail I can detect.  The bottom line is I'd like to avoid the expensive search for the dummy token in the content if at all possible.  Thanks.

Scott

0
Comment actions Permalink

I should clarify.  The FILE_COPY_KEY is also not set for similar reasons; the document is committed and parsed before the key is set.

Scott

0

Please sign in to leave a comment.