FindReferences in RenamePsiElementProcessor Follow
I'm facing a weird situation, where not all of my referenced variables are renamed. I have a working ReferenceContributor for both, references of variables and references of variable names inside strings. Here is a short code, where the arrows indicate, that all variable usages are resolved to the first "foo"
As you can see all variable references are correctly highlighted by "Highlight element under caret" and indeed, when I when I start to rename, it seems everything works
Unfortunately, the moment I press Enter, the last "newName" is not renamed. I could pin the problem down to
com.intellij.refactoring.rename.RenamePsiElementProcessor#findReferences(com.intellij.psi.PsiElement)
which calls
ReferencesSearch.search(element, GlobalSearchScope.projectScope(element.getProject())).findAll();
I have no idea, why this search doesn't find all references when Show Usage and other features work fine.
Why is that and how can I fix it?
Please sign in to leave a comment.
The usage search first gets all occurrences of "foo" in project, then finds references at those offsets and checks if they point to the correct target. By default, "var_foo" is treated as one word and so "foo" inside it isn't found. I don't know why it is found in some cases, maybe there's some other code in in-place rename. But the general reason seems to be that "foo" isn't a separate word.
There are two ways of dealing with it:
1. implement your own WordsScanner and process foo in var_foo as a word
2. (probably better) create a referencesSearcher that searches for var_foo words in your file type scope. You can look at com.intellij.psi.impl.search.SimpleAccessorReferenceSearcher as an example.
Thank you for your answer, Peter. One question, in Mathematica the underscore is an operator. Therefore, you can look at var_foo as something like
var-foo
in Java. So if the reference searcher, uses my Psi tree, it never sees a variable foo_bar. It always sees two separate variables connected with an operation. Why would the referenceSearcher rather search the text of my file instead of inspecting the Psi tree? This is unexpected.
I didn't realize it's an operator, this changes everything, of course. So your lexer processes var_foo as two different words, right? Then CachesBasedRefSearcher should search the reference correctly.
Have you implemented PsiReference#handleElementRename? Is it called at all during the rename? Is PsiReference#isReferenceTo called?
Exactly! And when I see this correctly, then CachesBasedRefSearcher does find everything, because it is used in the IdentifierHighlighterPass that marks all occurrences when I'm over an identifier with the cursor. Let me give a very short example showing the Psi tree. A "Symbol" is an identifier and you find 4 Symbols in the following code: "foo = 1" is "Set Symbol to Number", "f[var_foo]" is "FunctionCall (function Symbol f) of Symbol var with the Blank operator and Symbol foo"
The moment I press Shift+F6, CachesBasedRefSearcher is called again and it finds again all references because now I rename everything in the preview
The moment I press Enter, however, there is only 1 usage found in
com.intellij.refactoring.rename.RenameProcessor#findUsages
and I have absolutely no idea why. Do you have any hints how I can debug this further?
Edit: And in this particular case, the handleElementRename is only called for the first foo. However, renaming in other very complex constructs with local variables etc. does work correctly and from the viewpoint of the Mathematica language, the "_" operator is similar to all other operators like +,*, etc.
Is PsiReference#isReferenceTo called when finishing in-place rename?
Yes, but only one time for the "self-reference". Since "foo = 1" is the place of definition for foo, it gets a reference that resolves to itself. Here is a excerpt of the thread-dump
at de.halirutan.mathematica.parsing.psi.impl.SymbolPsiReference.isReferenceTo(SymbolPsiReference.java:130)
at com.intellij.psi.search.SingleTargetRequestResultProcessor.processTextOccurrence(SingleTargetRequestResultProcessor.java:52)
at com.intellij.psi.impl.search.PsiSearchHelperImpl$8.lambda$execute$0(PsiSearchHelperImpl.java:734)
at com.intellij.psi.impl.search.PsiSearchHelperImpl$8$$Lambda$748.1863423400.execute(Unknown Source:-1)
at com.intellij.psi.impl.search.LowLevelSearchUtil.processTreeUp(LowLevelSearchUtil.java:138)
at com.intellij.psi.impl.search.LowLevelSearchUtil.processElementsAtOffsets(LowLevelSearchUtil.java:224)
at com.intellij.psi.impl.search.PsiSearchHelperImpl$8.execute(PsiSearchHelperImpl.java:730)
at com.intellij.psi.impl.search.PsiSearchHelperImpl$2.processInReadAction(PsiSearchHelperImpl.java:232)
at com.intellij.psi.impl.search.PsiSearchHelperImpl$2.processInReadAction(PsiSearchHelperImpl.java:228)
at com.intellij.openapi.application.ReadActionProcessor$1.compute(ReadActionProcessor.java:32)
at com.intellij.openapi.application.ReadActionProcessor$1.compute(ReadActionProcessor.java:29)
at com.intellij.openapi.application.impl.ApplicationImpl.runReadAction(ApplicationImpl.java:895)
at com.intellij.openapi.application.ReadActionProcessor.process(ReadActionProcessor.java:29)
at com.intellij.psi.impl.search.PsiSearchHelperImpl.lambda$processVirtualFile$4(PsiSearchHelperImpl.java:376)
at com.intellij.psi.impl.search.PsiSearchHelperImpl$$Lambda$912.1197524689.run(Unknown Source:-1)
at com.intellij.openapi.application.impl.ApplicationImpl.tryRunReadAction(ApplicationImpl.java:1055)
at com.intellij.openapi.application.ex.ApplicationUtil.tryRunReadAction(ApplicationUtil.java:46)
at com.intellij.psi.impl.search.PsiSearchHelperImpl.processVirtualFile(PsiSearchHelperImpl.java:360)
at com.intellij.psi.impl.search.PsiSearchHelperImpl.lambda$processPsiFileRoots$2(PsiSearchHelperImpl.java:313)
at com.intellij.psi.impl.search.PsiSearchHelperImpl$$Lambda$910.509346699.process(Unknown Source:-1)
at com.intellij.concurrency.JobLauncherImpl.lambda$null$0(JobLauncherImpl.java:100)
at com.intellij.concurrency.JobLauncherImpl$$Lambda$747.1264193158.run(Unknown Source:-1)
at com.intellij.openapi.progress.impl.CoreProgressManager.registerIndicatorAndRun(CoreProgressManager.java:568)
at com.intellij.openapi.progress.impl.CoreProgressManager.executeProcessUnderProgress(CoreProgressManager.java:519)
at com.intellij.openapi.progress.impl.ProgressManagerImpl.executeProcessUnderProgress(ProgressManagerImpl.java:54)
at com.intellij.concurrency.JobLauncherImpl.lambda$processImmediatelyIfTooFew$1(JobLauncherImpl.java:96)
at com.intellij.concurrency.JobLauncherImpl$$Lambda$746.116216177.run(Unknown Source:-1)
at com.intellij.concurrency.JobLauncherImpl.processImmediatelyIfTooFew(JobLauncherImpl.java:110)
at com.intellij.concurrency.JobLauncherImpl.invokeConcurrentlyUnderProgress(JobLauncherImpl.java:53)
at com.intellij.psi.impl.search.PsiSearchHelperImpl.processPsiFileRoots(PsiSearchHelperImpl.java:325)
at com.intellij.psi.impl.search.PsiSearchHelperImpl.processElementsWithTextInGlobalScope(PsiSearchHelperImpl.java:281)
at com.intellij.psi.impl.search.PsiSearchHelperImpl.bulkProcessElementsWithWord(PsiSearchHelperImpl.java:179)
at com.intellij.psi.impl.search.PsiSearchHelperImpl.processSingleRequest(PsiSearchHelperImpl.java:897)
at com.intellij.psi.impl.search.PsiSearchHelperImpl.processGlobalRequestsOptimized(PsiSearchHelperImpl.java:638)
at com.intellij.psi.impl.search.PsiSearchHelperImpl.processRequests(PsiSearchHelperImpl.java:586)
at com.intellij.psi.search.SearchRequestQuery.processResults(SearchRequestQuery.java:45)
at com.intellij.util.AbstractQuery.forEach(AbstractQuery.java:79)
at com.intellij.util.MergeQuery.processSubQuery(MergeQuery.java:85)
at com.intellij.util.MergeQuery.forEach(MergeQuery.java:57)
at com.intellij.util.UniqueResultsQuery.process(UniqueResultsQuery.java:66)
at com.intellij.util.UniqueResultsQuery.forEach(UniqueResultsQuery.java:56)
at com.intellij.util.UniqueResultsQuery.findAll(UniqueResultsQuery.java:79)
at com.intellij.refactoring.rename.RenamePsiElementProcessor.findReferences(RenamePsiElementProcessor.java:70)
at com.intellij.refactoring.rename.RenamePsiElementProcessor.findReferences(RenamePsiElementProcessor.java:65)
at com.intellij.refactoring.rename.RenameUtil.findUsages(RenameUtil.java:72)
at com.intellij.refactoring.rename.RenameProcessor.findUsages(RenameProcessor.java:286)
at com.intellij.refactoring.BaseRefactoringProcessor$1$1.compute(BaseRefactoringProcessor.java:168)
at com.intellij.refactoring.BaseRefactoringProcessor$1$1.compute(BaseRefactoringProcessor.java:165)
When that breakpoint is hit, could you please go up the stack to LowLevelSearchUtil.processElementsAtOffsets and check searcher.myJavaIdentifier field? Is it true?
Yes, this field is true.
I see. It appears to be a bug in the searching infrastructure. Sorry for that. I've filed https://youtrack.jetbrains.com/issue/IDEA-165760, please vote/watch it.
As a workaround, you can add your own referencesSearcher that invokes com.intellij.psi.impl.search.PsiSearchHelperImpl#processElementsWithWord(com.intellij.psi.search.TextOccurenceProcessor, com.intellij.psi.search.SearchScope, java.lang.String, short, java.util.EnumSet<com.intellij.psi.impl.search.PsiSearchHelperImpl.Options>, java.lang.String) and doesn't pass PROCESS_ONLY_JAVA_IDENTIFIERS_IF_POSSIBLE option.
I really do appreciate all your help!
Additionally, I have the same problems with variables that contain dollar signs like $var. This is even weirder because they are normal identifiers. So in a code like this
only the definition element at "$var = 1" is renamed, no matter from which position I start the renaming.
What brings the confusion to an even higher level is the setting of these two methods
com.intellij.lang.refactoring.RefactoringSupportProvider#isMemberInplaceRenameAvailable
com.intellij.lang.refactoring.RefactoringSupportProvider#isInplaceRenameAvailable
as soon as I return false and Idea present dialog for renaming the underscore variables are handled correctly. The dollar sign variables however still don't work.
Since there is no documentation on these two methods, can you comment on what the difference between isMemberInplaceRenameAvailable and isInplaceRenameAvailable is?
The both inplace rename options do more or less the same, in one case MemberInplaceRenamer would perform the work in other case - VariableInplaceRenamer. The difference between them is caused by the scope where declaration could apper, if it's local, then it can be VariableInplaceRenamer otherwise, Member. For members case, first the usages in one file are searched and on exit of the template, everything would be reverted and the refactoring would actually performed from scratch on unchanged sources. In variable case, no revert should be performed and on exit of the template just some local conflicts could be resolved. If you return true from both methods, then VariableHandler should win (as declared first).
How do you start the inplace refactorings then? Do you see the suggestion to rename comments?
Thanks
Merry Christmas Anna,
now I understand the weird behaviour. I looked up your code which contains a separate method the "pre-collects" all reference to show the red frame and the rename preview:
com.intellij.refactoring.rename.inplace.MemberInplaceRenamer#collectRefs
But no matter what references you found, you call the usual RenameProcessor with my starting element and this processor does not find all the Usages of my references due to the optimisations Peter was speaking about.
> How do you start the inplace refactorings then? Do you see the suggestion to rename comments?
Always with Shift+F6, but I tried different settings of isInplaceRenameAvailable and isMemberInplaceRenameAvailable just to see if it makes a difference. I see now, why it makes no difference since they all rely on
com.intellij.refactoring.rename.RenameProcessor#findUsages
I'm probably going to ask another questions about this, but thank you very much for all the information.
@Peter For me, an easier fix is to implement a small RenamePsiElementProcessor that just combines the results that I find and the one that idea finds. I haven't tested it thoroughly but is seems to do the trick: