intelliLang: replaceInjectionsWithUndo filters compiled elements + tests added (IDEA-CR-33537)

This commit is contained in:
Nicolay Mitropolsky
2018-06-28 21:16:10 +03:00
parent de146d50b8
commit bf77979a70
5 changed files with 175 additions and 46 deletions
@@ -0,0 +1,57 @@
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.intellij.plugins.intelliLang
import com.intellij.lang.injection.InjectedLanguageManager
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.fileEditor.FileEditorManager
import com.intellij.openapi.fileEditor.TextEditor
import com.intellij.openapi.util.TextRange
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiLanguageInjectionHost
import com.intellij.psi.SmartPsiElementPointer
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase
import com.intellij.util.Processor
import junit.framework.TestCase
import org.intellij.plugins.intelliLang.inject.InjectLanguageAction
abstract class AbstractLanguageInjectionTestCase : LightCodeInsightFixtureTestCase() {
fun assertInjectedLangAtCaret(lang: String?) {
val injectedElement = injectedLanguageManager.findInjectedElementAt(topLevelFile, topLevelCaretPosition)
if (lang != null) {
TestCase.assertNotNull("injection of '$lang' expected", injectedElement)
TestCase.assertEquals(lang, injectedElement!!.language.id)
}
else {
TestCase.assertNull(injectedElement)
}
}
val topLevelEditor get() = (FileEditorManager.getInstance(project).getSelectedEditor(topLevelFile.virtualFile) as TextEditor).editor
val topLevelCaretPosition get() = topLevelEditor.caretModel.offset
val injectedLanguageManager: InjectedLanguageManager get() = InjectedLanguageManager.getInstance(project)
val topLevelFile: PsiFile get() = file.let { injectedLanguageManager.getTopLevelFile(it) }
}
class StoringFixPresenter : InjectLanguageAction.FixPresenter {
private lateinit var processor: Processor<PsiLanguageInjectionHost>
private lateinit var pointer: SmartPsiElementPointer<PsiLanguageInjectionHost>
override fun showFix(editor: Editor,
range: TextRange,
pointer: SmartPsiElementPointer<PsiLanguageInjectionHost>,
text: String,
data: Processor<PsiLanguageInjectionHost>) {
this.processor = data
this.pointer = pointer
}
fun process() = process(pointer.element ?: throw IllegalStateException("element was invalidated"))
fun process(injectionHost: PsiLanguageInjectionHost) = processor.process(injectionHost)
}
@@ -0,0 +1,103 @@
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.intellij.plugins.intelliLang
import com.intellij.lang.Language
import com.intellij.psi.PsiMethodCallExpression
import com.intellij.psi.injection.Injectable
import com.intellij.psi.util.parentOfType
import junit.framework.TestCase
import org.intellij.plugins.intelliLang.inject.InjectLanguageAction
import org.intellij.plugins.intelliLang.inject.InjectorUtils
import org.intellij.plugins.intelliLang.inject.UnInjectLanguageAction
import org.intellij.plugins.intelliLang.inject.config.BaseInjection
import org.intellij.plugins.intelliLang.inject.java.JavaLanguageInjectionSupport
class JavaLanguageInjectionSupportTest : AbstractLanguageInjectionTestCase() {
fun testAnnotationInjection() {
myFixture.configureByText("Foo.java", """
class Foo {
void bar() {
baz("{\"a\": <caret> 1 }");
}
void baz(String json){}
}
""")
StoringFixPresenter().apply {
InjectLanguageAction.invokeImpl(project,
myFixture.editor,
myFixture.file,
Injectable.fromLanguage(Language.findLanguageByID("JSON")),
this
)
}.process()
assertInjectedLangAtCaret("JSON")
myFixture.checkResult("""
|import org.intellij.lang.annotations.Language;
|
|class Foo {
| void bar() {
| baz("{\"a\": 1 }");
| }
|
| void baz(@Language("JSON") String json){}
| }
| """.trimMargin())
UnInjectLanguageAction.invokeImpl(project, topLevelEditor, topLevelFile)
assertInjectedLangAtCaret(null)
}
fun testConfigInjection() {
fun currentPrintlnInjection(): BaseInjection? {
val psiMethod = topLevelFile.findElementAt(topLevelCaretPosition)!!.parentOfType<PsiMethodCallExpression>()!!.resolveMethod()!!
val injection = JavaLanguageInjectionSupport.makeParameterInjection(psiMethod, 0, "JSON");
return InjectorUtils.getEditableInstance(project).findExistingInjection(injection)
}
myFixture.configureByText("Foo.java", """
class Foo {
void bar() {
System.out.println("{\"a\": <caret> 1 }");
}
}
""")
TestCase.assertNull(currentPrintlnInjection())
InjectLanguageAction.invokeImpl(project,
myFixture.editor,
myFixture.file,
Injectable.fromLanguage(Language.findLanguageByID("JSON")))
assertInjectedLangAtCaret("JSON")
TestCase.assertNotNull(currentPrintlnInjection())
myFixture.configureByText("Another.java", """
class Another {
void bar() {
System.out.println("{\"a\": <caret> 1 }");
}
}
""")
assertInjectedLangAtCaret("JSON")
UnInjectLanguageAction.invokeImpl(project, topLevelEditor, topLevelFile)
TestCase.assertNull(currentPrintlnInjection())
assertInjectedLangAtCaret(null)
}
}
@@ -1,20 +1,15 @@
package org.intellij.plugins.intelliLang;
import com.intellij.lang.Language;
import com.intellij.lang.injection.InjectedLanguageManager;
import com.intellij.openapi.command.undo.UndoManager;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.fileEditor.TextEditor;
import com.intellij.openapi.fileEditor.impl.text.TextEditorProvider;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.ui.TestDialog;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.resolve.reference.impl.providers.FileReference;
import com.intellij.psi.injection.Injectable;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase;
import com.intellij.util.Processor;
import com.intellij.util.ui.UIUtil;
import org.intellij.plugins.intelliLang.inject.InjectLanguageAction;
import org.intellij.plugins.intelliLang.inject.UnInjectLanguageAction;
@@ -22,12 +17,11 @@ import org.intellij.plugins.intelliLang.references.FileReferenceInjector;
import org.intellij.plugins.intelliLang.references.InjectedReferencesContributor;
import org.intellij.plugins.intelliLang.references.InjectedReferencesInspection;
import org.jdom.Element;
import org.jetbrains.annotations.NotNull;
/**
* @author Dmitry Avdeev
*/
public class ReferenceInjectionTest extends LightCodeInsightFixtureTestCase {
public class ReferenceInjectionTest extends AbstractLanguageInjectionTestCase {
public void testInjectReference() {
myFixture.configureByText("foo.xml", "<foo xmlns=\"http://foo.bar\" \n" +
" xxx=\"ba<caret>r\"/>");
@@ -217,18 +211,6 @@ public class ReferenceInjectionTest extends LightCodeInsightFixtureTestCase {
assertNull(getInjectedReferences());
}
private void assertInjectedLangAtCaret(String lang) {
InjectedLanguageManager injectedLanguageManager = InjectedLanguageManager.getInstance(getProject());
PsiElement injectedElement = injectedLanguageManager.findInjectedElementAt(getFile(), getEditor().getCaretModel().getOffset());
if (lang != null) {
assertNotNull("injection of '" + lang + "' expected", injectedElement);
assertEquals(lang, injectedElement.getLanguage().getID());
}
else {
assertNull(injectedElement);
}
}
public void testTernary() {
myFixture.configureByText("Foo.java", "class Foo {\n" +
" void bar() {\n" +
@@ -267,22 +249,4 @@ public class ReferenceInjectionTest extends LightCodeInsightFixtureTestCase {
myFixture.disableInspections(new InjectedReferencesInspection());
super.tearDown();
}
private static class StoringFixPresenter implements InjectLanguageAction.FixPresenter {
private Processor<PsiLanguageInjectionHost> processor;
@Override
public void showFix(@NotNull Editor editor,
@NotNull TextRange range,
@NotNull SmartPsiElementPointer<PsiLanguageInjectionHost> pointer,
@NotNull String text,
@NotNull Processor<PsiLanguageInjectionHost> data) {
this.processor = data;
}
public void process(PsiLanguageInjectionHost injectionHost) {
if (processor == null) throw new IllegalStateException("fix was not set");
processor.process(injectionHost);
}
}
}
@@ -294,10 +294,21 @@ public class JavaLanguageInjectionSupport extends AbstractLanguageInjectionSuppo
assert containingClass != null;
final PsiModifierList classModifiers = containingClass.getModifierList();
if (classModifiers != null && (classModifiers.hasModifierProperty(PsiModifier.PRIVATE) || classModifiers.hasModifierProperty(PsiModifier.PACKAGE_LOCAL))) {
return doAddLanguageAnnotation(project, parameterIndex >= 0? psiMethod.getParameterList().getParameters()[parameterIndex] : psiMethod,
return doAddLanguageAnnotation(project, parameterIndex >= 0 ? psiMethod.getParameterList().getParameters()[parameterIndex] : psiMethod,
host, languageId);
}
final MethodParameterInjection injection = makeParameterInjection(psiMethod, parameterIndex, languageId);
doEditInjection(project, injection, psiMethod);
return true;
}
@NotNull
public static MethodParameterInjection makeParameterInjection(@NotNull PsiMethod psiMethod,
int parameterIndex,
@NotNull String languageId) {
final PsiClass containingClass = psiMethod.getContainingClass();
assert containingClass != null;
final String className = containingClass.getQualifiedName();
assert className != null;
final MethodParameterInjection injection = new MethodParameterInjection();
@@ -312,8 +323,7 @@ public class JavaLanguageInjectionSupport extends AbstractLanguageInjectionSuppo
}
injection.setMethodInfos(Collections.singletonList(info));
injection.generatePlaces();
doEditInjection(project, injection, psiMethod);
return true;
return injection;
}
static int findParameterIndex(final PsiElement target, final PsiExpressionList parent) {
@@ -548,18 +548,13 @@ public class Configuration extends SimpleModificationTracker implements Persiste
PsiFile[] psiFiles = StreamEx.ofNullable(hostFile)
.append(psiElementsToRemove
.stream()
.filter(e -> !(e instanceof PsiCompiledElement))
.map(e -> e.getContainingFile()))
.filter(e -> !(e instanceof PsiCompiledElement))
.toArray(PsiFile.class);
DocumentReference[] documentReferences = ContainerUtil
.map2Array(psiFiles, DocumentReference.class, file -> DocumentReferenceManager.getInstance().create(file.getVirtualFile()));
if (documentReferences.length == 0) {
LOG.error("documentReferences array is empty, undo-redo for language injection will not be registered for any document/file," +
" please pass a proper `hostFile`, current hostFile = '" + hostFile + "'"); //refer IDEA-109366
}
final UndoableAction action = new GlobalUndoableAction(documentReferences) {
@Override
public void undo() {