IntelliJ Platform SDK DevGuide

Edit page

18. Quick Fix

A quick fix allows to apply an automatic changes to the code via Show Intention Actions (Alt + Enter).

Let’s add a quick fix which helps to define an unresolved property from its usage.

18.1. Update the element factory

package com.simpleplugin.psi; import com.intellij.openapi.project.Project; import com.intellij.psi.*; import com.simpleplugin.SimpleFileType; public class SimpleElementFactory { public static SimpleProperty createProperty(Project project, String name, String value) { final SimpleFile file = createFile(project, name + " = " + value); return (SimpleProperty) file.getFirstChild(); } public static SimpleProperty createProperty(Project project, String name) { final SimpleFile file = createFile(project, name); return (SimpleProperty) file.getFirstChild(); } public static PsiElement createCRLF(Project project) { final SimpleFile file = createFile(project, "\n"); return file.getFirstChild(); } public static SimpleFile createFile(Project project, String text) { String name = "dummy.simple"; return (SimpleFile) PsiFileFactory.getInstance(project). createFileFromText(name, SimpleFileType.INSTANCE, text); } }

18.2. Define an intention action

The quick fix will create a property in the file chosen by user, and navigate to this property after creation.

package com.simpleplugin; import com.intellij.codeInsight.intention.impl.BaseIntentionAction; import com.intellij.lang.ASTNode; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.command.WriteCommandAction; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.fileChooser.FileChooser; import com.intellij.openapi.fileChooser.FileChooserDescriptor; import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory; import com.intellij.openapi.fileEditor.FileEditorManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.pom.Navigatable; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiManager; import com.intellij.psi.search.FileTypeIndex; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.util.IncorrectOperationException; import com.intellij.util.indexing.FileBasedIndex; import com.simpleplugin.psi.SimpleElementFactory; import com.simpleplugin.psi.SimpleFile; import com.simpleplugin.psi.SimpleProperty; import org.jetbrains.annotations.NotNull; import java.util.Collection; class CreatePropertyQuickFix extends BaseIntentionAction { private String key; CreatePropertyQuickFix(String key) { this.key = key; } @NotNull @Override public String getText() { return "Create property"; } @NotNull @Override public String getFamilyName() { return "Simple properties"; } @Override public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) { return true; } @Override public void invoke(@NotNull final Project project, final Editor editor, PsiFile file) throws IncorrectOperationException { ApplicationManager.getApplication().invokeLater(new Runnable() { @Override public void run() { Collection<VirtualFile> virtualFiles = FileBasedIndex.getInstance().getContainingFiles(FileTypeIndex.NAME, SimpleFileType.INSTANCE, GlobalSearchScope.allScope(project)); if (virtualFiles.size() == 1) { createProperty(project, virtualFiles.iterator().next()); } else { final FileChooserDescriptor descriptor = FileChooserDescriptorFactory.createSingleFileDescriptor(SimpleFileType.INSTANCE); descriptor.setRoots(project.getBaseDir()); final VirtualFile file = FileChooser.chooseFile(descriptor, project, null); if (file != null) { createProperty(project, file); } } } }); } private void createProperty(final Project project, final VirtualFile file) { new WriteCommandAction.Simple(project) { @Override public void run() { SimpleFile simpleFile = (SimpleFile) PsiManager.getInstance(project).findFile(file); ASTNode lastChildNode = simpleFile.getNode().getLastChildNode(); // TODO: Add another check for CRLF if (lastChildNode != null/* && !lastChildNode.getElementType().equals(SimpleTypes.CRLF)*/) { simpleFile.getNode().addChild(SimpleElementFactory.createCRLF(project).getNode()); } // IMPORTANT: change spaces to escaped spaces or the new node will only have the first word for the key SimpleProperty property = SimpleElementFactory.createProperty(project, key.replaceAll(" ", "\\\\ "), ""); simpleFile.getNode().addChild(property.getNode()); ((Navigatable) property.getLastChild().getNavigationElement()).navigate(true); FileEditorManager.getInstance(project).getSelectedTextEditor().getCaretModel(). moveCaretRelatively(2, 0, false, false, false); // almost the same thing but manipulating plain text of the document instead of PSI // FileEditorManager.getInstance(project).openFile(file, true); // final Editor editor = FileEditorManager.getInstance(project).getSelectedTextEditor(); // final Document document = editor.getDocument(); // document.insertString(document.getTextLength(), "\n" + key.replaceAll(" ", "\\\\ ") + " = "); // editor.getCaretModel().getPrimaryCaret().moveToOffset(document.getTextLength()); } }.execute(); } }

18.3. Update the annotator

Note the call to registerFix.

package com.simpleplugin; import com.intellij.lang.annotation.*; import com.intellij.openapi.editor.DefaultLanguageHighlighterColors; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.TextRange; import com.intellij.psi.*; import com.simpleplugin.psi.SimpleProperty; import org.jetbrains.annotations.NotNull; import java.util.List; public class SimpleAnnotator implements Annotator { @Override public void annotate(@NotNull final PsiElement element, @NotNull AnnotationHolder holder) { if (element instanceof PsiLiteralExpression) { PsiLiteralExpression literalExpression = (PsiLiteralExpression) element; String value = literalExpression.getValue() instanceof String ? (String) literalExpression.getValue() : null; if (value != null && value.startsWith("simple" + ":")) { Project project = element.getProject(); String key = value.substring(7); List<SimpleProperty> properties = SimpleUtil.findProperties(project, key); if (properties.size() == 1) { TextRange range = new TextRange(element.getTextRange().getStartOffset() + 7, element.getTextRange().getStartOffset() + 7); Annotation annotation = holder.createInfoAnnotation(range, null); annotation.setTextAttributes(DefaultLanguageHighlighterColors.LINE_COMMENT); } else if (properties.size() == 0) { TextRange range = new TextRange(element.getTextRange().getStartOffset() + 8, element.getTextRange().getEndOffset()); holder.createErrorAnnotation(range, "Unresolved property"). registerFix(new CreatePropertyQuickFix(key)); } } } } }

18.4. Run the project

Now let’s try to use a property which is not defined yet.

Quick Fix

Last modified: 12 October 2017