Is it "legal" to replace the parent PsiElement when rename refactoring a child element?

For myplugin (idea-multimarkdown) on rename of links I replace the parent element with a newly created parent element when the link is renamed. The new parent is taken from createFileFromText(). The reason is that the parent link element contains the link text, possible link anchor and title, which may be changed when the link is renamed. Replacing the parent element takes care of all of them in one operation.

Everything worked and works fine for Wiki links. However, now that I do the same for regular links the PsiTree correctly reflects the new element but the document does not and I am getting an exception that the file and Psi don't correspond.

The strange thing is that it happens only for a specific element and PsiViewer shows the modified element in the PsiFile tree but the file shows only one the first child element of new link.

I went through the code execution with a fine tooth comb, including tracing the lexer and at every point the values are what I would expect. Including after the call to element.getParent().replace(newLink). I even examined the containingFile() returned by newLink right after replace() call and it shows the expected values.

I pasted the text used for createFileFromText() and it shows up correctly in the file and psiViewer. The problem only arises when the EXPLICIT_LINK parent is replaced during rename refactoring of the LINK_REF child.

I am running against IDEA CE 14, here is what the file and PsiViewer show the following:

The first one shows the file before rename and the second after the EXPLICIT_LINK element's LINK_REF was renamed from images to imagess.
Screen Shot 2015-11-03 at 1.00.08 PM.png Screen Shot 2015-11-03 at 1.01.22 PM.png
Any pointers would be greatly appreciated. I have been trying to figure out the differences and I see none between the two element types. The implementation code is the same except where IElementType of the nodes differs and child node classes.

Here is the stack trace from the exception:

Incorrect offsets: startOffset=16, endOffset=34, text length=26 java.lang.Throwable      at com.intellij.openapi.diagnostic.Logger.error(Logger.java:115)      at com.intellij.openapi.editor.impl.DocumentImpl.createRangeMarker(DocumentImpl.java:455)      at com.intellij.openapi.editor.impl.DocumentImpl.createRangeMarker(DocumentImpl.java:448)      at com.intellij.openapi.editor.impl.DocumentImpl.createRangeMarker(DocumentImpl.java:1015)      at com.intellij.psi.impl.source.PostprocessReformattingAspect$9.visitNode(PostprocessReformattingAspect.java:548)      at com.intellij.psi.impl.source.tree.RecursiveTreeElementVisitor.visitComposite(RecursiveTreeElementVisitor.java:25)      at com.intellij.psi.impl.source.PostprocessReformattingAspect$9.visitComposite(PostprocessReformattingAspect.java:578)      at com.intellij.psi.impl.source.tree.CompositeElement.acceptTree(CompositeElement.java:158)      at com.intellij.psi.impl.source.PostprocessReformattingAspect.createActionsMap(PostprocessReformattingAspect.java:538)      at com.intellij.psi.impl.source.PostprocessReformattingAspect.doPostponedFormattingInner(PostprocessReformattingAspect.java:339)      at com.intellij.psi.impl.source.PostprocessReformattingAspect.access$1200(PostprocessReformattingAspect.java:62)      at com.intellij.psi.impl.source.PostprocessReformattingAspect$8$1.run(PostprocessReformattingAspect.java:261)      at com.intellij.psi.impl.source.PostprocessReformattingAspect$3.compute(PostprocessReformattingAspect.java:112)      at com.intellij.psi.impl.source.PostprocessReformattingAspect.disablePostprocessFormattingInside(PostprocessReformattingAspect.java:121)      at com.intellij.psi.impl.source.PostprocessReformattingAspect.disablePostprocessFormattingInside(PostprocessReformattingAspect.java:109)      at com.intellij.psi.impl.source.PostprocessReformattingAspect$8.run(PostprocessReformattingAspect.java:258)      at com.intellij.openapi.progress.impl.CoreProgressManager.registerIndicatorAndRun(CoreProgressManager.java:452)      at com.intellij.openapi.progress.impl.CoreProgressManager.executeProcessUnderProgress(CoreProgressManager.java:402)      at com.intellij.openapi.progress.impl.ProgressManagerImpl.executeProcessUnderProgress(ProgressManagerImpl.java:54)      at com.intellij.openapi.progress.impl.CoreProgressManager.executeNonCancelableSection(CoreProgressManager.java:180)      at com.intellij.psi.impl.source.PostprocessReformattingAspect.atomic(PostprocessReformattingAspect.java:174)      at com.intellij.psi.impl.source.PostprocessReformattingAspect.postponedFormattingImpl(PostprocessReformattingAspect.java:252)      at com.intellij.psi.impl.source.PostprocessReformattingAspect.doPostponedFormatting(PostprocessReformattingAspect.java:248)      at com.intellij.psi.impl.source.PostprocessReformattingAspect$7.run(PostprocessReformattingAspect.java:230)      at com.intellij.openapi.progress.impl.CoreProgressManager.registerIndicatorAndRun(CoreProgressManager.java:452)      at com.intellij.openapi.progress.impl.CoreProgressManager.executeProcessUnderProgress(CoreProgressManager.java:402)      at com.intellij.openapi.progress.impl.ProgressManagerImpl.executeProcessUnderProgress(ProgressManagerImpl.java:54)      at com.intellij.openapi.progress.impl.CoreProgressManager.executeNonCancelableSection(CoreProgressManager.java:180)      at com.intellij.psi.impl.source.PostprocessReformattingAspect.atomic(PostprocessReformattingAspect.java:174)      at com.intellij.psi.impl.source.PostprocessReformattingAspect.doPostponedFormatting(PostprocessReformattingAspect.java:223)      at com.intellij.psi.impl.source.PostprocessReformattingAspect.decrementPostponedCounter(PostprocessReformattingAspect.java:160)      at com.intellij.psi.impl.source.PostprocessReformattingAspect.access$300(PostprocessReformattingAspect.java:62)      at com.intellij.psi.impl.source.PostprocessReformattingAspect$2.writeActionFinished(PostprocessReformattingAspect.java:100)      at sun.reflect.GeneratedMethodAccessor24.invoke(Unknown Source)      at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)      at java.lang.reflect.Method.invoke(Method.java:497)      at com.intellij.util.EventDispatcher.dispatch(EventDispatcher.java:88)      at com.intellij.util.EventDispatcher.access$100(EventDispatcher.java:34)      at com.intellij.util.EventDispatcher$1.invoke(EventDispatcher.java:68)      at com.sun.proxy.$Proxy2.writeActionFinished(Unknown Source)      at com.intellij.openapi.application.impl.ApplicationImpl.fireWriteActionFinished(ApplicationImpl.java:1346)      at com.intellij.openapi.application.impl.ApplicationImpl.endWrite(ApplicationImpl.java:1192)      at com.intellij.openapi.application.impl.ApplicationImpl.runWriteAction(ApplicationImpl.java:934)      at com.intellij.refactoring.BaseRefactoringProcessor.doRefactoring(BaseRefactoringProcessor.java:448)      at com.intellij.refactoring.BaseRefactoringProcessor.access$100(BaseRefactoringProcessor.java:75)      at com.intellij.refactoring.BaseRefactoringProcessor$3.run(BaseRefactoringProcessor.java:293)      at com.intellij.openapi.command.impl.CoreCommandProcessor.executeCommand(CoreCommandProcessor.java:124)      at com.intellij.openapi.command.impl.CoreCommandProcessor.executeCommand(CoreCommandProcessor.java:99)      at com.intellij.refactoring.BaseRefactoringProcessor.execute(BaseRefactoringProcessor.java:289)      at com.intellij.refactoring.BaseRefactoringProcessor.doRun(BaseRefactoringProcessor.java:226)      at com.intellij.refactoring.rename.RenameProcessor.doRun(RenameProcessor.java:127)      at com.intellij.refactoring.BaseRefactoringProcessor.run(BaseRefactoringProcessor.java:559)      at com.intellij.refactoring.ui.RefactoringDialog.invokeRefactoring(RefactoringDialog.java:188)      at com.intellij.refactoring.rename.RenameDialog.performRename(RenameDialog.java:327)      at com.intellij.refactoring.rename.RenameDialog.doAction(RenameDialog.java:304)      at com.intellij.refactoring.ui.RefactoringDialog.doRefactorAction(RefactoringDialog.java:88)      at com.intellij.refactoring.ui.RefactoringDialog$RefactorAction.actionPerformed(RefactoringDialog.java:160)      at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:2022)      at javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2346)      at javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:402)      at javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:259)      at javax.swing.AbstractButton.doClick(AbstractButton.java:376)      at javax.swing.plaf.basic.BasicRootPaneUI$Actions.actionPerformed(BasicRootPaneUI.java:208)      at javax.swing.SwingUtilities.notifyAction(SwingUtilities.java:1663)      at javax.swing.JComponent.processKeyBinding(JComponent.java:2882)      at javax.swing.KeyboardManager.fireBinding(KeyboardManager.java:307)      at javax.swing.KeyboardManager.fireKeyboardAction(KeyboardManager.java:250)      at javax.swing.JComponent.processKeyBindingsForAllComponents(JComponent.java:2974)      at javax.swing.JComponent.processKeyBindings(JComponent.java:2966)      at javax.swing.JComponent.processKeyEvent(JComponent.java:2845)      at java.awt.Component.processEvent(Component.java:6302)      at java.awt.Container.processEvent(Container.java:2234)      at java.awt.Component.dispatchEventImpl(Component.java:4881)      at java.awt.Container.dispatchEventImpl(Container.java:2292)      at java.awt.Component.dispatchEvent(Component.java:4703)      at java.awt.KeyboardFocusManager.redispatchEvent(KeyboardFocusManager.java:1954)      at java.awt.DefaultKeyboardFocusManager.dispatchKeyEvent(DefaultKeyboardFocusManager.java:806)      at java.awt.DefaultKeyboardFocusManager.preDispatchKeyEvent(DefaultKeyboardFocusManager.java:1074)      at java.awt.DefaultKeyboardFocusManager.typeAheadAssertions(DefaultKeyboardFocusManager.java:945)      at java.awt.DefaultKeyboardFocusManager.dispatchEvent(DefaultKeyboardFocusManager.java:771)      at java.awt.Component.dispatchEventImpl(Component.java:4752)      at java.awt.Container.dispatchEventImpl(Container.java:2292)      at java.awt.Window.dispatchEventImpl(Window.java:2750)      at java.awt.Component.dispatchEvent(Component.java:4703)      at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:758)      at java.awt.EventQueue.access$500(EventQueue.java:97)      at java.awt.EventQueue$3.run(EventQueue.java:709)      at java.awt.EventQueue$3.run(EventQueue.java:703)      at java.security.AccessController.doPrivileged(Native Method)      at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:75)      at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:86)      at java.awt.EventQueue$4.run(EventQueue.java:731)      at java.awt.EventQueue$4.run(EventQueue.java:729)      at java.security.AccessController.doPrivileged(Native Method)      at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:75)      at java.awt.EventQueue.dispatchEvent(EventQueue.java:728)      at com.intellij.ide.IdeEventQueue.defaultDispatchEvent(IdeEventQueue.java:734)      at com.intellij.ide.IdeEventQueue._dispatchEvent(IdeEventQueue.java:539)      at com.intellij.ide.IdeEventQueue.dispatchEvent(IdeEventQueue.java:382)      at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:201)      at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116)      at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:109)      at java.awt.WaitDispatchSupport$2.run(WaitDispatchSupport.java:184)      at java.awt.WaitDispatchSupport$4.run(WaitDispatchSupport.java:229)      at java.awt.WaitDispatchSupport$4.run(WaitDispatchSupport.java:227)      at java.security.AccessController.doPrivileged(Native Method)      at java.awt.WaitDispatchSupport.enter(WaitDispatchSupport.java:227)      at java.awt.Dialog.show(Dialog.java:1084)      at com.intellij.openapi.ui.impl.DialogWrapperPeerImpl$MyDialog.show(DialogWrapperPeerImpl.java:778)      at com.intellij.openapi.ui.impl.DialogWrapperPeerImpl.show(DialogWrapperPeerImpl.java:465)      at com.intellij.openapi.ui.DialogWrapper.invokeShow(DialogWrapper.java:1614)      at com.intellij.openapi.ui.DialogWrapper.show(DialogWrapper.java:1571)      at com.intellij.refactoring.rename.PsiElementRenameHandler.rename(PsiElementRenameHandler.java:198)      at com.intellij.refactoring.rename.PsiElementRenameHandler.rename(PsiElementRenameHandler.java:169)      at com.intellij.refactoring.rename.PsiElementRenameHandler.invoke(PsiElementRenameHandler.java:119)      at com.intellij.refactoring.rename.PsiElementRenameHandler.invoke(PsiElementRenameHandler.java:79)      at com.intellij.refactoring.actions.BaseRefactoringAction.actionPerformed(BaseRefactoringAction.java:122)      at com.intellij.openapi.keymap.impl.IdeKeyEventDispatcher$3.performAction(IdeKeyEventDispatcher.java:593)      at com.intellij.openapi.keymap.impl.IdeKeyEventDispatcher.processAction(IdeKeyEventDispatcher.java:644)      at com.intellij.openapi.keymap.impl.IdeKeyEventDispatcher.inInitState(IdeKeyEventDispatcher.java:483)      at com.intellij.openapi.keymap.impl.IdeKeyEventDispatcher.dispatchKeyEvent(IdeKeyEventDispatcher.java:213)      at com.intellij.ide.IdeEventQueue._dispatchEvent(IdeEventQueue.java:538)      at com.intellij.ide.IdeEventQueue.dispatchEvent(IdeEventQueue.java:382)      at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:201)      at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116)      at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105)      at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)      at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93)      at java.awt.EventDispatchThread.run(EventDispatchThread.java:82)

2 comments
Comment actions Permalink

Found a workaround, I replaced the call to replace() on parent PsiElement with add/delete children from the new node's AST. The only side-effect is that the caret position is not preserved after rename refactroing, which I can always save and restore from the document. The commented out code is replaced by what looks like equivalent calls:

                //element.getParent().replace(newLink);                 ASTNode parent = element.getParent().getNode();                 ASTNode firstChildNode = parent.getFirstChildNode();                 parent.addChildren(newLink.getFirstChild().getNode(), null, firstChildNode);                 parent.removeRange(firstChildNode, null);



I suspect that it is a bug since the equivalent code works but I am not sure if the parent replacement is expected during rename refactoring.

0
Comment actions Permalink

Now I am encountering issues with trying to replace one type of PsiElement with another. For example Wiki Links with Explicit Links. The same issue as I had with trying to replace explicit link with one created via a file. I now get that same thing when trying to replace a wiki link.

Most of the time it gets the above stack trace. Once I saw what looked like the cause. There was a null pointer exception on a child of ExplicitLink complaining that the parent is null. However, when I examine the newly created ExplicitLink PsiElement before using it in the replace() call, all the children look fine. The only elements that have null parent are WHITE_SPACE, which was not the cause of the problem.

Now I have resorted to simply replacing the text in the document that represents the element instead of using the PsiTree manipulations. I use the same function to create the text as was used to createFileFromText and just replace the old element text range with the new text.

I would like to figure out what is causing the problem but I am at a loss what to look for. I can replace explict link elements with wiki links without errors but the reverse causes validation of resulting file to fail after psi element replacement.

0

Please sign in to leave a comment.