digma-ai / digma-intellij-plugin

Digma JetBrains plugin
MIT License
32 stars 7 forks source link

How to access PSI and avoid IndexNotReadyException #805

Open shalom938 opened 1 year ago

shalom938 commented 1 year ago

The general rule is: we can't do anything with PSI before intellij indexing is complete. or more accurate while indexing is running. indexing is running always on startup and sometimes while user works and some source code has changed.

IndexNotReadyException will always be thrown if trying to access PSI elements while indexing is running.

this is a delicate work and every piece of code needs to know what its doing and if it needs to wait for smart mode. if the code can't wait for smart mode then it needs to do something else, present something else to the user.

if the code needs to update UI then it needs to wait for smart mode, query the PSI and update the UI.

a common way to do it is: ReadAction.nonBlocking(RunnableCallable { //some background code , query PSI }).inSmartMode(project).withDocumentsCommitted(project) .finishOnUiThread(ModalityState.defaultModalityState(), { //code that updates the UI }) .submit(NonUrgentExecutor.getInstance())

we have to be careful not to run nested ReadAction.nonBlocking because the outer one will loos its context.

IMO we need to remove all ReadAction.nonBlocking from the language services methods, these methods are considered low level and need to run synchronously , the code in language services don't need to consider smart or dumb mode. application code needs to wrap calls to language services with ReadAction, ReadAction can be thought of as a transaction, it should either complete successfully or fail but never do half of its work. its analogous to accessing a data base, you need a driver and you need to manage transactions, the language service is the driver, the ReadAction is like transaction management. currently we have ReadActions that were added without really thinking, usually it was to fix a bug quickly. but continuing like that we will end up with unmaintainable spaghetti code. currently wrapping code with ReadAction.nonBlocking may cause errors or unexpected behavior because some of the language services methods already do a ReadAction.nonBlocking.

IndexNotReadyException keeps coming back again and again every time a new feature is added. if we don't do a global work around it we will probably never get rid of it. we can also protect against these king of errors, intellij platform has assertion methods to test things like isReadAccessAlowed, isSmartMode, isUIThread etc, these assertions can be inserted in crucial code locations and may help detect errors early in development time. another way is testing, we can intentionally run some code while indexing is running and detect if it fails with IndexNotReadyException or correctly waits for smart mode. one thing to remember is that waiting for smart in Rider is different then in Idea or pycharm, in Rider smart mode means backend smart mode.

so this is not an easy one, developers need to know what they are doing.

necessary reads are: https://plugins.jetbrains.com/docs/intellij/general-threading-rules.html https://plugins.jetbrains.com/docs/intellij/psi.html

shaykeren commented 1 year ago

this is a tech story, we must understand and handle this one globally