package com.intellij.flex.codeInsight;
import com.intellij.codeInsight.CodeInsightTestCase;
import com.intellij.codeInsight.hint.actions.ShowImplementationsAction;
import com.intellij.codeInsight.navigation.GotoTargetHandler;
import com.intellij.flex.util.FlexTestUtils;
import com.intellij.ide.DataManager;
import com.intellij.javascript.flex.css.FlexStylesIndexableSetContributor;
import com.intellij.javascript.flex.mxml.schema.FlexSchemaHandler;
import com.intellij.lang.javascript.JSTestOption;
import com.intellij.lang.javascript.JSTestOptions;
import com.intellij.lang.javascript.JavaScriptSupportLoader;
import com.intellij.lang.javascript.dialects.JSDialectSpecificHandlersFactory;
import com.intellij.lang.javascript.flex.FlexModuleType;
import com.intellij.lang.javascript.psi.JSFunction;
import com.intellij.lang.javascript.psi.JSNamedElement;
import com.intellij.lang.javascript.psi.ecmal4.JSClass;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.module.ModuleType;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ContentEntry;
import com.intellij.openapi.roots.ModifiableRootModel;
import com.intellij.openapi.roots.ModuleRootManager;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.ThrowableComputable;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.newvfs.impl.VfsRootAccess;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.testFramework.fixtures.CodeInsightTestUtil;
import com.intellij.util.ArrayUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import static com.intellij.openapi.vfs.VfsUtilCore.convertFromUrl;
import static com.intellij.openapi.vfs.VfsUtilCore.urlToPath;
public class FlexGotoImplementationsTest extends CodeInsightTestCase {
private static final String BASE_PATH = "gotoImplementations/";
private Runnable myAfterCommitRunnable = null;
@Override
protected void setUp() throws Exception {
VfsRootAccess.allowRootAccess(getTestRootDisposable(),
urlToPath(convertFromUrl(FlexSchemaHandler.class.getResource("z.xsd"))),
urlToPath(convertFromUrl(FlexStylesIndexableSetContributor.class.getResource("FlexStyles.as"))));
super.setUp();
myAfterCommitRunnable = null;
}
@Override
protected void tearDown() throws Exception {
myAfterCommitRunnable = null;
super.tearDown();
}
@Override
protected String getTestDataPath() {
return FlexTestUtils.getTestDataPath("");
}
@Override
protected ModuleType getModuleType() {
return FlexModuleType.getInstance();
}
@Override
protected void setUpJdk() {
FlexTestUtils.setupFlexSdk(getModule(), getTestName(false), getClass());
}
@Override
protected void doCommitModel(@NotNull ModifiableRootModel rootModel) {
super.doCommitModel(rootModel);
if (myAfterCommitRunnable != null) {
myAfterCommitRunnable.run();
}
}
private void doTestForClass(String baseClassQualifiedName, String expected) throws Exception {
configureByElement(baseClassQualifiedName, null);
invokeAndCheck(expected);
}
private void doTestForMethod(String baseClassQualifiedName, @Nullable String methodName, int inheritors) throws Exception {
configureByElement(baseClassQualifiedName, methodName);
StringBuilder expected = new StringBuilder();
for (int i = 0; i < inheritors; i++) {
if (expected.length() > 0) expected.append(",");
expected.append(methodName);
}
invokeAndCheck(expected.toString());
}
// open editor for a class with specified qname
private void configureByElement(String classQName, @Nullable String methodName) throws IOException {
ApplicationManager.getApplication().runWriteAction(new ThrowableComputable<Object, IOException>() {
@Override
public Object compute() throws IOException {
myFile = null;
myEditor = null;
final ModuleRootManager rootManager = ModuleRootManager.getInstance(myModule);
final ModifiableRootModel rootModel = rootManager.getModifiableModel();
if (clearModelBeforeConfiguring()) {
rootModel.clear();
}
File toDirIO = createTempDirectory();
VirtualFile toDir =
LocalFileSystem.getInstance().refreshAndFindFileByPath(toDirIO.getCanonicalPath().replace(File.separatorChar, '/'));
boolean sourceRootAdded = false;
if (isAddDirToContentRoot()) {
// we may need a content root to add a library
final ContentEntry contentEntry = rootModel.addContentEntry(toDir);
if (isAddDirToSource()) {
sourceRootAdded = true;
contentEntry.addSourceFolder(toDir, false);
}
}
doCommitModel(rootModel);
if (sourceRootAdded) {
sourceRootAdded(toDir);
}
PsiElement element = JSDialectSpecificHandlersFactory.forLanguage(JavaScriptSupportLoader.ECMA_SCRIPT_L4).getClassResolver()
.findClassByQName(classQName, GlobalSearchScope.moduleWithDependenciesAndLibrariesScope(getModule()));
assertTrue("Class " + classQName + " not found", element instanceof JSClass);
if (methodName != null) {
element = ((JSClass)element).findFunctionByName(methodName);
assertTrue("Class " + classQName + " has not have method " + methodName, element instanceof JSFunction);
}
PsiElement navElement = element.getNavigationElement();
final VirtualFile virtualFile = navElement.getContainingFile().getVirtualFile();
setActiveEditor(createEditor(virtualFile));
myEditor.getCaretModel().moveToOffset(navElement.getTextOffset());
return null;
}
});
}
private void invokeAndCheck(String expected) {
GotoTargetHandler.GotoData pair = CodeInsightTestUtil.gotoImplementation(getEditor(), getFile());
PsiElement base = pair.source;
PsiElement[] implementations = pair.targets;
check(expected, implementations);
final Ref<PsiElement[]> ref = new Ref<>();
new ShowImplementationsAction() {
@Override
protected void showImplementations(@NotNull PsiElement[] impls,
@NotNull Project project,
String text,
Editor editor,
PsiFile file,
PsiElement element,
boolean invokedFromEditor,
boolean invokedByShortcut) {
ref.set(impls);
}
}.performForContext(DataManager.getInstance().getDataContext());
implementations = ArrayUtil.remove(ref.get(), base);
check(expected, implementations);
}
private static void check(String expected, PsiElement[] implementations) {
List<String> strings = new ArrayList<>(implementations.length);
for (PsiElement element : implementations) {
assertTrue("unexpected result item: " + element, element instanceof JSNamedElement);
strings.add(((JSNamedElement)element).getName());
}
Collections.sort(strings);
assertEquals(expected, StringUtil.join(strings, ","));
}
@JSTestOptions({JSTestOption.WithFlexFacet, JSTestOption.WithGumboSdk})
public void testClassInheritors() throws Exception {
addLib();
doTestForClass("MySkin", "MySkinEx,MySkinEx2");
}
@JSTestOptions({JSTestOption.WithFlexFacet, JSTestOption.WithGumboSdk})
public void testClassMethodInheritors() throws Exception {
addLib();
doTestForMethod("MySkin", "foo", 2);
}
@JSTestOptions({JSTestOption.WithFlexFacet, JSTestOption.WithGumboSdk})
public void testInterfaceInheritors() throws Exception {
addLib();
doTestForClass("MyInt", "MyImpl,MyImpl2,MyIntEx");
}
@JSTestOptions({JSTestOption.WithFlexFacet, JSTestOption.WithGumboSdk})
public void testIntergaceMethodInheritors() throws Exception {
addLib();
doTestForMethod("MyInt", "zz", 2);
}
private void addLib() {
myAfterCommitRunnable =
() -> FlexTestUtils.addLibrary(myModule, "TestLib", getTestDataPath() + BASE_PATH, "untitled37lib.swc", "untitled37lib.zip", null);
}
}