Intention fails to change code in injected language snippets

Hi,

I've implemented an intention action for a custom language. It works well in actual documents but it does work when my language is injected in another document. When being used there, the intention is offered upon alt-enter as usual, but the actual document modification does not happen. The code just is just the same as before. Here are the  relevant code bits:

public void processIntention(@NotNull PsiElement element, @NotNull Project project, Editor editor)
throws IncorrectOperationException {
...

if (Objects.equals(element.getText(), "T")) {
PsiElement replacement = element.replace(RElementFactory.createLeafFromText(element.getProject(), "TRUE"));
editor.getCaretModel().moveToOffset(replacement.getTextOffset() + 4);
}
...

I could imagine that element.getProject is wrong in such a context, but I have not idea about how to do better/different.

Any help is appreciated.

Best,
Holger

0

Have you tried doing a text replacement instead of PSI replacement?

editor.getDocument().replaceString(element.getNode().getStartOffset(), element.getNode().getStartOffset()+1, "TRUE");

 

 

 

0
Avatar
Permanently deleted user

Thanks Vladimir, indeed that may be an option. However,  it lacks beauty and in fact contradicts the idea of having PsiElement::replace as intended API for such a usecase. It's also used by other "official" intentions like com.jetbrains.python.inspections.quickfix.PyAddPropertyForFieldQuickFix or org.jetbrains.plugins.groovy.intentions.conversions.strings.ConvertToDollarSlashRegexIntention.

So I did a little testing and it turned out that the success of replacement depends on the main language of the document. Intentions using PsiElement::replace function correctly in injected chunks in (e.g.) java, bash, R, groovy or html. But fail just in markdown. So I've filed: https://youtrack.jetbrains.com/issue/RUBY-19204

 

 

 

0

Holger,

I guess I am less orthodox about always using PsiElements, since in my plugin for Markdown it is not possible to replace PsiElements because Markdown elements' textual representation depends on context making text based replacement the only option without having to create the full text context of the replacement element.

There is no possibility in these cases just to create an element, but actually having to create an arbitrarily complex nested context.

I found that you can mix both text and Psi based replacements as long as you commit changes between the two types of changes. If you don't you will get exceptions with helpful messages to guide you to the right API usage. :D

0
Avatar
Permanently deleted user

Vladimir,

About your last point: Why are `PsiElement::replace` calls (in contrast to  `document.replaceString` ) possible without committing and wrapping into WriteCommandAction? 

Example PsiElement::replace: org.jetbrains.plugins.groovy.intentions.conversions.strings.ConvertToDollarSlashRegexIntention

Example Document::replaceString: com.intellij.xml.util.CollapseTagIntention

 

Does 

CodeStyleManager.getInstance(project).reformat(tag);

require aWriteCommandAction or I can just call it after PsiElement::replace?

Thanks again for you help,
Holger

0

All modifications have to be wrapped in a WriteAction. The only reason this is not done in you examples is because the caller does the wrapping.

IntentionAction.java:78-87

/**
 * Called when user invokes intention. This method is called inside command.
 * If {@link #startInWriteAction()} returns true, this method is also called
 * inside write action.
 *
 * @param project the project in which the intention is invoked.
 * @param editor the editor in which the intention is invoked.
 * @param file the file open in the editor.
 */
void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException;

After modifying the document commit changes by invoking:

PsiDocumentManager.getInstance(project).commitDocument(document)
0

The reason the second call has an explicit WriteCommand wrapper is because it can be called from a context other than intention invoke() method which means that it may or may not be wrapped in a WriteAction by the caller.

0

Does

CodeStyleManager.getInstance(project).reformat(tag);

require aWriteCommandAction or I can just call it after PsiElement::replace?

Since PsiElement::replace has to be called inside a WriteAction I would assume that you can call reformat() right after replace()

0

请先登录再写评论。