Reparse after inserting PSI

We've implemented auto-import in our language plugin by overriding  bindToElement() on the refrence and not only binding to the new type, but also adding the import statement.  First, it that ok to do? It feels wrong to add a statement in the PSI tree during bindToElement().  If that's ok, I would like for the containing file to reparse after this, but it doesn't.  What is the best way to force a reparse?  Currently I am setting the text of the document to that of the PSI, which seems brutal.  There must be a better way.

For laughs here's the code that syncs the PSI with the document, and forces a reparse:

  public void syncPsi()
  {
    ApplicationManager.getApplication().runWriteAction(
      new Runnable()
      {
        public void run()
        {
          PsiDocumentManagerImpl.getInstance( getProject() ).doPostponedOperationsAndUnblockDocument( getViewProvider().getDocument() );
          getViewProvider().getDocument().setText( getText() );
        }
      } );
  }

7 comments
Comment actions Permalink

I'm not sure why you would need to reparse since you went through all the trouble of modifying the tree.

I have seen one method that lets you control parsing:

com.intellij.psi.text.BlockSupport.reparseRange()

0
Comment actions Permalink

Yeah, it is curious isn't it..

Long story short, our language plugin's psi parser is really a transformer.  Actual parsing is delegated to our existing parser and the psi parser transforms our parser's AST to IJ's AST/PSI.  This strategy works well after a bit of manipulation on both ends.  For instance, our Annotator provide parse error annotations from the PSI tree that is parsed when the entire file is parsed. Works just great... until we insert new statatements into the tree via quick fixes.  The problem here is that the file is never reparsed after the quick fix, so the errors are reported in terms of offsets from the state of the tree prior to the addition of the quick fix statements i.e., the addition a new statement in the tree prior to the offsets of existing errors has the effect of mismarked errors.  What to do?  My first reaction was to fix up the error offsets in the existing tree, which I could still do.  That doesn't appeal to me for a number of reasons, so I thought, well, why not make the tree reparse as if the quick fix were entered manually. The only way I could get that result was to reset the doc's contents to the PSI tree's text.  But that just feels wrong, especially the way I've done it.

0
Comment actions Permalink

Ok, I recall that I had the same problem when using the ExternalAnnotator EP to do something similar to what you are doing.

I don't know that I ever found a good solution for it, in the end I just went with reusing the language compiler in source form, and modifying it to build the AST/PSI while it was compiling.

Also, are you parsing the file itself or obtaining the text via an API and parsing that.

I recall having issues with the virtual file not getting refreshed all the time and having to call VirtualFileManager.getInstance().refresh(true) manually.

0
Comment actions Permalink

After thinking through this a bit more I've found what I think is the right approach.  I don't really need to sync the PSI tree with the document (those JetBrains guys are smart) what I really need to do is reparse the text of the quick-fixed PSI.  Again, since our PSI parser delegates to our language's internal parser, we attach the results of the parser -- the parsed type along with parse issues -- to the PSI tree.  This is what our Annotator uses to create issue annotations.  Under normal circumstances changes are made to the source in the editor which trigger IJ to call IFileElementType.parseContents().  This results in an AST/PSI that reflects the changed source.  With quick fixes we are inverting that process by directly manipulating the PSI and then parsing the PSI's text so that the parsed results reflect the modified PSI.  I've got it all refactored to behave that way and somehow managed to reduce the amount of code along the way.  Thanks for your help, Jon.

0
Comment actions Permalink

Did you try my suggestion to call : com.intellij.psi.text.BlockSupport.reparseRange() ?

Is your project open source. I'd like to take a looik at how you implemented your external parsing solution at some point.

0
Comment actions Permalink

It turned out that i didn't need to reparse the PSI... as you pondered, why would i do that after manually creating the PSI for the quick fix.  Basically, I just needed to invoke our internal parser on the text from the PSI after the quick fix and the attach the parse results to the PSI file (as user data) as we normally do in parseContents() on the PSI file.

Our plugin is indeed open source, but it hasn't been updated in a while -- what we have internally is much much further along than what is published.  We are planning on releasing an update in a couple of weeks.  In any case the plugin is available on github: https://github.com/gosu-lang/Gosu-IntelliJ-Plugin

Scott

0
Comment actions Permalink

Thanks, Jon.  your suggestion of BlockSupport worked for deleting invalid whitespace between function names and `()` and having the annotation being removed in intellij-elixir.

0

Please sign in to leave a comment.