Can't call saveDocumentAsIs() from doAnnotate()
Answered
In short, I need to safely save the file before running my annotator. Creating a virtual file won't do, unless I were to copy the whole project.
It seems that what I need is FileDocumentManager.saveDocumentAsIs()
, but that raises a PluginException
with a stack trace linking back to that call. The documentation comment says that doAnnotate()
is called from outside a read action, but that doesn't seem to be the case.
Here's my code for now:
class CustomAnnotator : ExternalAnnotator<..., ...> {
// ...
override fun doAnnotate(collectedInfo: Info?): Result? {
if (collectedInfo == null) {
return null
}
val file = collectedInfo.file
val documentManager = FileDocumentManager.getInstance()
val document = documentManager.getDocument(file.virtualFile) ?: return null
documentManager.saveDocumentAsIs(document)
return Result(frobnicate(file))
}
// ...
}
And here's a part of the stack trace:
Caused by: com.intellij.openapi.diagnostic.RuntimeExceptionWithAttachments: Access is allowed from Event Dispatch Thread (EDT) only; see https://jb.gg/ij-platform-threading for details
Current thread: Thread[Alarm Pool,4,main] 1781998448 (EventQueue.isDispatchThread()=false)
SystemEventQueueThread: Thread[AWT-EventQueue-0,6,main] 1578447637
at com.intellij.util.concurrency.ThreadingAssertions.createThreadAccessException(ThreadingAssertions.java:149)
at com.intellij.util.concurrency.ThreadingAssertions.throwThreadAccessException(ThreadingAssertions.java:143)
at com.intellij.util.concurrency.ThreadingAssertions.assertEventDispatchThread(ThreadingAssertions.java:65)
<the saveDocumentAsIs() calls>
<the doAnnotate() calls>
(Off-topic, how do you insert a non-plain-text code block using this WYSIWYG editor?)
Please sign in to leave a comment.
Hi,
Please explain why you need to save the document before running an annotator. Please describe your use case, as there is probably a more correct solution.
The CLI tool I need to use doesn't support passing content via stdio. I contacted the maintainers and they refused to add such a feature, saying I should be using the corresponding LSP tool instead. That would be fine and all, except for that LSP support is experimental and is only available in paid IDEs, whereas I want to also support community versions.
Sorry, but it doesn't clarify the use case. You want to save a document before running an annotator, and you didn't explain why. What does this file contain? Why is it needed to save it in order to run the annotator?
Because I can't run that (static analysis) tool on an unsaved file (of code). That would create discrepancies between the results and the content visible to the user, which might cause an
IndexOutOfBoundsException
and, even worse, incorrect annotations.Hello. During highlighting it's explicitly forbidden to call expensive methods, in particular I/O and write actions, to make the highlighting more parallel and fast. To workaround, you can call “save” before the highlighting starts, or you can call Application.invokeLater(saveAll), which will cancel highlighting, save documents and restart the highlighting
From which method of
ExternalAnnotator
can I do that?collectInformation()
,doAnnotate()
andapply()
are all called from within a read action. Or must it be called in another class/method? If so, which one?The same question. If I call
invokeLater()
from any of the three aforementioned, wouldn't it create an infinite loop?>From which method of
ExternalAnnotator
can I do that?collectInformation()
,doAnnotate()
andapply()
are all called from within a read action. Or must it be called in another class/method? If so, which one?It's for your plugin to decide. For example, before your plugin calls the CLI tool you mentioned, it can save documents first.
>The same question. If I call
invokeLater()
from any of the three aforementioned, wouldn't it create an infinite loop?You should call it only when there are documents to save, e.g. when com.intellij.openapi.fileEditor.FileDocumentManager#isDocumentUnsaved == true for the document you are trying to highlight
I'm currently extending the
externalAnnotator
extension point:I think I need a listener which runs whenever the document is changed. The interface which I would need to implement is probably
FileDocumentManagerListener
; is that the one?Forgive me for asking such stupid questions. I'm truly thankful that you spend your time answering us plugin developers' queries.
>I think I need a listener which runs whenever the document is changed.
If you decide to go “save document in invokeLater()” path, then you don't need a listener.
Just do smth like this in your annotator:
```
if (document is needed for my CLI tool) {
if (FileDocumentManager.isDocumentUnsaved(doc) {
invokeLater(FileDocumentManager.saveDocument(doc));
return; // break out of annotator because it will be canceled anyway by document save in EDT
}
}
```
Thanks a lot! That seems to work. Again, your answers are very much appreciated!