193: Fix tests compilation for 193 platform
This commit is contained in:
+332
@@ -0,0 +1,332 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2000-2016 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.test.testFramework;
|
||||||
|
|
||||||
|
import com.intellij.core.CoreASTFactory;
|
||||||
|
import com.intellij.lang.*;
|
||||||
|
import com.intellij.lang.impl.PsiBuilderFactoryImpl;
|
||||||
|
import com.intellij.mock.*;
|
||||||
|
import com.intellij.openapi.Disposable;
|
||||||
|
import com.intellij.openapi.application.PathManager;
|
||||||
|
import com.intellij.openapi.editor.Document;
|
||||||
|
import com.intellij.openapi.editor.EditorFactory;
|
||||||
|
import com.intellij.openapi.extensions.ExtensionPointName;
|
||||||
|
import com.intellij.openapi.extensions.Extensions;
|
||||||
|
import com.intellij.openapi.fileEditor.FileDocumentManager;
|
||||||
|
import com.intellij.openapi.fileEditor.impl.LoadTextUtil;
|
||||||
|
import com.intellij.openapi.fileTypes.FileTypeFactory;
|
||||||
|
import com.intellij.openapi.fileTypes.FileTypeManager;
|
||||||
|
import com.intellij.openapi.progress.EmptyProgressIndicator;
|
||||||
|
import com.intellij.openapi.progress.ProgressManager;
|
||||||
|
import com.intellij.openapi.progress.impl.CoreProgressManager;
|
||||||
|
import com.intellij.openapi.util.Disposer;
|
||||||
|
import com.intellij.openapi.util.Key;
|
||||||
|
import com.intellij.openapi.util.TextRange;
|
||||||
|
import com.intellij.openapi.util.io.FileUtil;
|
||||||
|
import com.intellij.openapi.vfs.CharsetToolkit;
|
||||||
|
import com.intellij.pom.PomModel;
|
||||||
|
import com.intellij.pom.core.impl.PomModelImpl;
|
||||||
|
import com.intellij.pom.tree.TreeAspect;
|
||||||
|
import com.intellij.psi.*;
|
||||||
|
import com.intellij.psi.impl.*;
|
||||||
|
import com.intellij.psi.util.CachedValuesManager;
|
||||||
|
import com.intellij.testFramework.LightVirtualFile;
|
||||||
|
import com.intellij.testFramework.TestDataFile;
|
||||||
|
import com.intellij.util.CachedValuesManagerImpl;
|
||||||
|
import com.intellij.util.Function;
|
||||||
|
import com.intellij.util.messages.MessageBus;
|
||||||
|
import com.intellij.util.messages.MessageBusFactory;
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
import org.jetbrains.annotations.NonNls;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.picocontainer.ComponentAdapter;
|
||||||
|
import org.picocontainer.MutablePicoContainer;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
@SuppressWarnings("ALL")
|
||||||
|
public abstract class KtParsingTestCase extends KtPlatformLiteFixture {
|
||||||
|
public static final Key<Document> HARD_REF_TO_DOCUMENT_KEY = Key.create("HARD_REF_TO_DOCUMENT_KEY");
|
||||||
|
protected String myFilePrefix = "";
|
||||||
|
protected String myFileExt;
|
||||||
|
protected final String myFullDataPath;
|
||||||
|
protected PsiFile myFile;
|
||||||
|
private MockPsiManager myPsiManager;
|
||||||
|
private PsiFileFactoryImpl myFileFactory;
|
||||||
|
protected Language myLanguage;
|
||||||
|
private final ParserDefinition[] myDefinitions;
|
||||||
|
private final boolean myLowercaseFirstLetter;
|
||||||
|
|
||||||
|
protected KtParsingTestCase(@NonNls @NotNull String dataPath, @NotNull String fileExt, @NotNull ParserDefinition... definitions) {
|
||||||
|
this(dataPath, fileExt, false, definitions);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected KtParsingTestCase(@NonNls @NotNull String dataPath, @NotNull String fileExt, boolean lowercaseFirstLetter, @NotNull ParserDefinition... definitions) {
|
||||||
|
myDefinitions = definitions;
|
||||||
|
myFullDataPath = getTestDataPath() + "/" + dataPath;
|
||||||
|
myFileExt = fileExt;
|
||||||
|
myLowercaseFirstLetter = lowercaseFirstLetter;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void setUp() throws Exception {
|
||||||
|
super.setUp();
|
||||||
|
initApplication();
|
||||||
|
ComponentAdapter component = getApplication().getPicoContainer().getComponentAdapter(ProgressManager.class.getName());
|
||||||
|
|
||||||
|
Extensions.registerAreaClass("IDEA_PROJECT", null);
|
||||||
|
myProject = new MockProjectEx(getTestRootDisposable());
|
||||||
|
myPsiManager = new MockPsiManager(myProject);
|
||||||
|
myFileFactory = new PsiFileFactoryImpl(myPsiManager);
|
||||||
|
MutablePicoContainer appContainer = getApplication().getPicoContainer();
|
||||||
|
registerComponentInstance(appContainer, MessageBus.class, MessageBusFactory.newMessageBus(getApplication()));
|
||||||
|
final MockEditorFactory editorFactory = new MockEditorFactory();
|
||||||
|
registerComponentInstance(appContainer, EditorFactory.class, editorFactory);
|
||||||
|
registerComponentInstance(appContainer, FileDocumentManager.class, new MockFileDocumentManagerImpl(new Function<CharSequence, Document>() {
|
||||||
|
@Override
|
||||||
|
public Document fun(CharSequence charSequence) {
|
||||||
|
return editorFactory.createDocument(charSequence);
|
||||||
|
}
|
||||||
|
}, HARD_REF_TO_DOCUMENT_KEY));
|
||||||
|
registerComponentInstance(appContainer, PsiDocumentManager.class, new MockPsiDocumentManager());
|
||||||
|
registerApplicationService(PsiBuilderFactory.class, new PsiBuilderFactoryImpl());
|
||||||
|
registerApplicationService(DefaultASTFactory.class, new CoreASTFactory());
|
||||||
|
|
||||||
|
registerApplicationService(ProgressManager.class, new CoreProgressManager());
|
||||||
|
|
||||||
|
myProject.registerService(CachedValuesManager.class, new CachedValuesManagerImpl(myProject, new PsiCachedValuesFactory(myPsiManager)));
|
||||||
|
myProject.registerService(PsiManager.class, myPsiManager);
|
||||||
|
|
||||||
|
this.registerExtensionPoint(FileTypeFactory.FILE_TYPE_FACTORY_EP, FileTypeFactory.class);
|
||||||
|
registerExtensionPoint(MetaLanguage.EP_NAME, MetaLanguage.class);
|
||||||
|
|
||||||
|
for (ParserDefinition definition : myDefinitions) {
|
||||||
|
addExplicitExtension(LanguageParserDefinitions.INSTANCE, definition.getFileNodeType().getLanguage(), definition);
|
||||||
|
}
|
||||||
|
if (myDefinitions.length > 0) {
|
||||||
|
configureFromParserDefinition(myDefinitions[0], myFileExt);
|
||||||
|
}
|
||||||
|
|
||||||
|
// That's for reparse routines
|
||||||
|
final PomModelImpl pomModel = new PomModelImpl(myProject);
|
||||||
|
myProject.registerService(PomModel.class, pomModel);
|
||||||
|
new TreeAspect(pomModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void configureFromParserDefinition(ParserDefinition definition, String extension) {
|
||||||
|
myLanguage = definition.getFileNodeType().getLanguage();
|
||||||
|
myFileExt = extension;
|
||||||
|
addExplicitExtension(LanguageParserDefinitions.INSTANCE, this.myLanguage, definition);
|
||||||
|
registerComponentInstance(
|
||||||
|
getApplication().getPicoContainer(), FileTypeManager.class,
|
||||||
|
new MockFileTypeManager(new MockLanguageFileType(myLanguage, myFileExt)));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected <T> void addExplicitExtension(final LanguageExtension<T> instance, final Language language, final T object) {
|
||||||
|
instance.addExplicitExtension(language, object);
|
||||||
|
Disposer.register(myProject, new Disposable() {
|
||||||
|
@Override
|
||||||
|
public void dispose() {
|
||||||
|
instance.removeExplicitExtension(language, object);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected <T> void registerExtensionPoint(final ExtensionPointName<T> extensionPointName, Class<T> aClass) {
|
||||||
|
super.registerExtensionPoint(extensionPointName, aClass);
|
||||||
|
Disposer.register(myProject, new Disposable() {
|
||||||
|
@Override
|
||||||
|
public void dispose() {
|
||||||
|
Extensions.getRootArea().unregisterExtensionPoint(extensionPointName.getName());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected <T> void registerApplicationService(final Class<T> aClass, T object) {
|
||||||
|
getApplication().registerService(aClass, object);
|
||||||
|
Disposer.register(myProject, new Disposable() {
|
||||||
|
@Override
|
||||||
|
public void dispose() {
|
||||||
|
getApplication().getPicoContainer().unregisterComponent(aClass.getName());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public MockProjectEx getProject() {
|
||||||
|
return myProject;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MockPsiManager getPsiManager() {
|
||||||
|
return myPsiManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void tearDown() throws Exception {
|
||||||
|
super.tearDown();
|
||||||
|
myFile = null;
|
||||||
|
myProject = null;
|
||||||
|
myPsiManager = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String getTestDataPath() {
|
||||||
|
return PathManager.getHomePath();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public final String getTestName() {
|
||||||
|
return getTestName(myLowercaseFirstLetter);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean includeRanges() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean skipSpaces() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean checkAllPsiRoots() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void doTest(boolean checkResult) {
|
||||||
|
String name = getTestName();
|
||||||
|
try {
|
||||||
|
String text = loadFile(name + "." + myFileExt);
|
||||||
|
myFile = createPsiFile(name, text);
|
||||||
|
ensureParsed(myFile);
|
||||||
|
assertEquals("light virtual file text mismatch", text, ((LightVirtualFile)myFile.getVirtualFile()).getContent().toString());
|
||||||
|
assertEquals("virtual file text mismatch", text, LoadTextUtil.loadText(myFile.getVirtualFile()));
|
||||||
|
assertEquals("doc text mismatch", text, myFile.getViewProvider().getDocument().getText());
|
||||||
|
assertEquals("psi text mismatch", text, myFile.getText());
|
||||||
|
ensureCorrectReparse(myFile);
|
||||||
|
if (checkResult){
|
||||||
|
checkResult(name, myFile);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
toParseTreeText(myFile, skipSpaces(), includeRanges());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void doTest(String suffix) throws IOException {
|
||||||
|
String name = getTestName();
|
||||||
|
String text = loadFile(name + "." + myFileExt);
|
||||||
|
myFile = createPsiFile(name, text);
|
||||||
|
ensureParsed(myFile);
|
||||||
|
assertEquals(text, myFile.getText());
|
||||||
|
checkResult(name + suffix, myFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void doCodeTest(String code) throws IOException {
|
||||||
|
String name = getTestName();
|
||||||
|
myFile = createPsiFile("a", code);
|
||||||
|
ensureParsed(myFile);
|
||||||
|
assertEquals(code, myFile.getText());
|
||||||
|
checkResult(myFilePrefix + name, myFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected PsiFile createPsiFile(String name, String text) {
|
||||||
|
return createFile(name + "." + myFileExt, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected PsiFile createFile(@NonNls String name, String text) {
|
||||||
|
LightVirtualFile virtualFile = new LightVirtualFile(name, myLanguage, text);
|
||||||
|
virtualFile.setCharset(CharsetToolkit.UTF8_CHARSET);
|
||||||
|
return createFile(virtualFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected PsiFile createFile(LightVirtualFile virtualFile) {
|
||||||
|
return myFileFactory.trySetupPsiForFile(virtualFile, myLanguage, true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void checkResult(@NonNls @TestDataFile String targetDataName, final PsiFile file) throws IOException {
|
||||||
|
doCheckResult(myFullDataPath, file, checkAllPsiRoots(), targetDataName, skipSpaces(), includeRanges());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void doCheckResult(String testDataDir,
|
||||||
|
PsiFile file,
|
||||||
|
boolean checkAllPsiRoots,
|
||||||
|
String targetDataName,
|
||||||
|
boolean skipSpaces,
|
||||||
|
boolean printRanges) throws IOException {
|
||||||
|
FileViewProvider provider = file.getViewProvider();
|
||||||
|
Set<Language> languages = provider.getLanguages();
|
||||||
|
|
||||||
|
if (!checkAllPsiRoots || languages.size() == 1) {
|
||||||
|
doCheckResult(testDataDir, targetDataName + ".txt", toParseTreeText(file, skipSpaces, printRanges).trim());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Language language : languages) {
|
||||||
|
PsiFile root = provider.getPsi(language);
|
||||||
|
String expectedName = targetDataName + "." + language.getID() + ".txt";
|
||||||
|
doCheckResult(testDataDir, expectedName, toParseTreeText(root, skipSpaces, printRanges).trim());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void checkResult(String actual) throws IOException {
|
||||||
|
String name = getTestName();
|
||||||
|
doCheckResult(myFullDataPath, myFilePrefix + name + ".txt", actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void checkResult(@TestDataFile @NonNls String targetDataName, String actual) throws IOException {
|
||||||
|
doCheckResult(myFullDataPath, targetDataName, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void doCheckResult(String fullPath, String targetDataName, String actual) throws IOException {
|
||||||
|
String expectedFileName = fullPath + File.separatorChar + targetDataName;
|
||||||
|
KtUsefulTestCase.assertSameLinesWithFile(expectedFileName, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static String toParseTreeText(PsiElement file, boolean skipSpaces, boolean printRanges) {
|
||||||
|
return DebugUtil.psiToString(file, skipSpaces, printRanges);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String loadFile(@NonNls @TestDataFile String name) throws IOException {
|
||||||
|
return loadFileDefault(myFullDataPath, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String loadFileDefault(String dir, String name) throws IOException {
|
||||||
|
return FileUtil.loadFile(new File(dir, name), CharsetToolkit.UTF8, true).trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ensureParsed(PsiFile file) {
|
||||||
|
file.accept(new PsiElementVisitor() {
|
||||||
|
@Override
|
||||||
|
public void visitElement(PsiElement element) {
|
||||||
|
element.acceptChildren(this);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ensureCorrectReparse(@NotNull PsiFile file) {
|
||||||
|
String psiToStringDefault = DebugUtil.psiToString(file, false, false);
|
||||||
|
String fileText = file.getText();
|
||||||
|
DiffLog diffLog = (new BlockSupportImpl()).reparseRange(
|
||||||
|
file, file.getNode(), TextRange.allOf(fileText), fileText, new EmptyProgressIndicator(), fileText);
|
||||||
|
diffLog.performActualPsiChange(file);
|
||||||
|
|
||||||
|
TestCase.assertEquals(psiToStringDefault, DebugUtil.psiToString(file, false, false));
|
||||||
|
}
|
||||||
|
}
|
||||||
+81
@@ -0,0 +1,81 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2000-2014 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.test.testFramework;
|
||||||
|
|
||||||
|
import com.intellij.core.CoreEncodingProjectManager;
|
||||||
|
import com.intellij.mock.MockApplicationEx;
|
||||||
|
import com.intellij.openapi.application.ApplicationManager;
|
||||||
|
import com.intellij.openapi.extensions.ExtensionPoint;
|
||||||
|
import com.intellij.openapi.extensions.ExtensionPointName;
|
||||||
|
import com.intellij.openapi.extensions.Extensions;
|
||||||
|
import com.intellij.openapi.extensions.ExtensionsArea;
|
||||||
|
import com.intellij.openapi.fileTypes.FileTypeManager;
|
||||||
|
import com.intellij.openapi.vfs.encoding.EncodingManager;
|
||||||
|
import org.picocontainer.MutablePicoContainer;
|
||||||
|
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
|
||||||
|
public abstract class KtPlatformLiteFixture extends KtUsefulTestCase {
|
||||||
|
protected MockProjectEx myProject;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void setUp() throws Exception {
|
||||||
|
super.setUp();
|
||||||
|
// FIXME: There is no `Extensions.cleanRootArea` in 193
|
||||||
|
// Extensions.cleanRootArea(getTestRootDisposable());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MockApplicationEx getApplication() {
|
||||||
|
return (MockApplicationEx)ApplicationManager.getApplication();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void initApplication() {
|
||||||
|
MockApplicationEx instance = new MockApplicationEx(getTestRootDisposable());
|
||||||
|
ApplicationManager.setApplication(instance, FileTypeManager::getInstance, getTestRootDisposable());
|
||||||
|
getApplication().registerService(EncodingManager.class, CoreEncodingProjectManager.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void tearDown() throws Exception {
|
||||||
|
super.tearDown();
|
||||||
|
clearFields(this);
|
||||||
|
myProject = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected <T> void registerExtensionPoint(ExtensionPointName<T> extensionPointName, Class<T> aClass) {
|
||||||
|
registerExtensionPoint(Extensions.getRootArea(), extensionPointName, aClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <T> void registerExtensionPoint(
|
||||||
|
ExtensionsArea area, ExtensionPointName<T> extensionPointName,
|
||||||
|
Class<? extends T> aClass
|
||||||
|
) {
|
||||||
|
String name = extensionPointName.getName();
|
||||||
|
if (!area.hasExtensionPoint(name)) {
|
||||||
|
ExtensionPoint.Kind kind = aClass.isInterface() || (aClass.getModifiers() & Modifier.ABSTRACT) != 0 ? ExtensionPoint.Kind.INTERFACE : ExtensionPoint.Kind.BEAN_CLASS;
|
||||||
|
area.registerExtensionPoint(name, aClass.getName(), kind);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static <T> T registerComponentInstance(MutablePicoContainer container, Class<T> key, T implementation) {
|
||||||
|
Object old = container.getComponentInstance(key);
|
||||||
|
container.unregisterComponent(key);
|
||||||
|
container.registerComponentInstance(key, implementation);
|
||||||
|
return (T)old;
|
||||||
|
}
|
||||||
|
}
|
||||||
+179
@@ -0,0 +1,179 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.completion.test.confidence;
|
||||||
|
|
||||||
|
import com.intellij.codeInsight.CodeInsightSettings;
|
||||||
|
import com.intellij.codeInsight.completion.CodeCompletionHandlerBase;
|
||||||
|
import com.intellij.codeInsight.completion.CompletionType;
|
||||||
|
import com.intellij.codeInsight.completion.LightCompletionTestCase;
|
||||||
|
import com.intellij.codeInsight.lookup.LookupElement;
|
||||||
|
import com.intellij.codeInsight.lookup.LookupManager;
|
||||||
|
import com.intellij.codeInsight.lookup.impl.LookupImpl;
|
||||||
|
import com.intellij.openapi.editor.Editor;
|
||||||
|
import com.intellij.openapi.projectRoots.JavaSdk;
|
||||||
|
import com.intellij.openapi.projectRoots.Sdk;
|
||||||
|
import com.intellij.openapi.util.text.StringUtil;
|
||||||
|
import com.intellij.psi.PsiFile;
|
||||||
|
import com.intellij.util.ArrayUtil;
|
||||||
|
import org.apache.commons.lang.SystemUtils;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.kotlin.idea.completion.test.CompletionTestUtilKt;
|
||||||
|
import org.jetbrains.kotlin.idea.test.TestUtilsKt;
|
||||||
|
import org.jetbrains.kotlin.test.InTextDirectivesUtils;
|
||||||
|
import org.jetbrains.kotlin.test.JUnit3WithIdeaConfigurationRunner;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@RunWith(JUnit3WithIdeaConfigurationRunner.class)
|
||||||
|
public class KotlinConfidenceTest extends LightCompletionTestCase {
|
||||||
|
private static final String TYPE_DIRECTIVE_PREFIX = "// TYPE:";
|
||||||
|
private final ThreadLocal<Boolean> skipComplete = ThreadLocal.withInitial(() -> false);
|
||||||
|
|
||||||
|
public void testCompleteOnDotOutOfRanges() {
|
||||||
|
doTest();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testImportAsConfidence() {
|
||||||
|
doTest();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testInBlockOfFunctionLiteral() {
|
||||||
|
doTest();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testInModifierList() {
|
||||||
|
doTest();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testNoAutoCompletionForRangeOperator() {
|
||||||
|
doTest();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testNoAutoPopupInString() {
|
||||||
|
doTest();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testAutoPopupInStringTemplate() {
|
||||||
|
doTest();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testAutoPopupInStringTemplateAfterDollar() {
|
||||||
|
doTest();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testNoAutoPopupInStringTemplateAfterSpace() {
|
||||||
|
doTest();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testNoAutoPopupInRawStringTemplateAfterNewLine() {
|
||||||
|
doTest();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void setUp() throws Exception {
|
||||||
|
super.setUp();
|
||||||
|
TestUtilsKt.invalidateLibraryCache(getProject());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void doTest() {
|
||||||
|
boolean completeByChars = CodeInsightSettings.getInstance().SELECT_AUTOPOPUP_SUGGESTIONS_BY_CHARS;
|
||||||
|
|
||||||
|
CodeInsightSettings.getInstance().SELECT_AUTOPOPUP_SUGGESTIONS_BY_CHARS = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
skipComplete.set(true);
|
||||||
|
try {
|
||||||
|
configureByFile(getBeforeFileName());
|
||||||
|
} finally {
|
||||||
|
skipComplete.set(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
String text = getEditor().getDocument().getText();
|
||||||
|
boolean noLookup = InTextDirectivesUtils.isDirectiveDefined(text, "// NO_LOOKUP");
|
||||||
|
if (!noLookup) complete(); //This will cause NPE in case of absence of autopopup completion
|
||||||
|
|
||||||
|
List<String> expectedElements = InTextDirectivesUtils.findLinesWithPrefixesRemoved(text, "// ELEMENT:");
|
||||||
|
assertFalse("Can't both expect lookup elements and no lookup", !expectedElements.isEmpty() && noLookup);
|
||||||
|
|
||||||
|
if (noLookup) {
|
||||||
|
try {
|
||||||
|
Method m = null; //Looking for shouldSkipAutoPopup method (note, we can't use name because of scrambling in Ultimate)
|
||||||
|
for (Method method : CodeCompletionHandlerBase.class.getDeclaredMethods()) {
|
||||||
|
Class<?>[] parameterTypes = method.getParameterTypes();
|
||||||
|
if (method.getReturnType().equals(Boolean.TYPE) &&
|
||||||
|
parameterTypes.length == 2 &&
|
||||||
|
parameterTypes[0].equals(Editor.class) &&
|
||||||
|
parameterTypes[1].equals(PsiFile.class)) {
|
||||||
|
assertNull("Only one method with such signature should exist", m);
|
||||||
|
m = method;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertNotNull(m);
|
||||||
|
m.setAccessible(true);
|
||||||
|
Object o = m.invoke(null, getEditor(), getFile());
|
||||||
|
assert o instanceof Boolean;
|
||||||
|
assertTrue("Should skip autopopup completion", ((Boolean) o).booleanValue());
|
||||||
|
} catch (Throwable e) {
|
||||||
|
throw new AssertionError(e);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String typeText = getTypeText(text);
|
||||||
|
if (!expectedElements.isEmpty()) {
|
||||||
|
assertContainsItems(ArrayUtil.toStringArray(expectedElements));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
assertNotNull("You must type something, use // TYPE:", typeText);
|
||||||
|
type(typeText);
|
||||||
|
checkResultByFile(getAfterFileName());
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
CodeInsightSettings.getInstance().SELECT_AUTOPOPUP_SUGGESTIONS_BY_CHARS = completeByChars;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getTypeText(String text) {
|
||||||
|
String[] directives = InTextDirectivesUtils.findArrayWithPrefixes(text, TYPE_DIRECTIVE_PREFIX);
|
||||||
|
if (directives.length == 0) return null;
|
||||||
|
assertEquals("One directive with \"" + TYPE_DIRECTIVE_PREFIX +"\" expected", 1, directives.length);
|
||||||
|
|
||||||
|
return StringUtil.unquoteString(directives[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getBeforeFileName() {
|
||||||
|
return getTestName(false) + ".kt";
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getAfterFileName() {
|
||||||
|
return getTestName(false) + ".kt.after";
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
protected String getTestDataPath() {
|
||||||
|
return new File(CompletionTestUtilKt.getCOMPLETION_TEST_DATA_BASE_PATH(), "/confidence/").getPath() + File.separator;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Sdk getProjectJDK() {
|
||||||
|
return JavaSdk.getInstance().createJdk("JDK", SystemUtils.getJavaHome().getAbsolutePath());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void complete() {
|
||||||
|
if (skipComplete.get()) return;
|
||||||
|
new CodeCompletionHandlerBase(CompletionType.BASIC, false, true, true).invokeCompletion(
|
||||||
|
getProject(), getEditor(), 0, false);
|
||||||
|
|
||||||
|
LookupImpl lookup = (LookupImpl) LookupManager.getActiveLookup(getEditor());
|
||||||
|
myItems = lookup == null ? null : lookup.getItems().toArray(LookupElement.EMPTY_ARRAY);
|
||||||
|
myPrefix = lookup == null ? null : lookup.itemPattern(lookup.getItems().get(0));
|
||||||
|
}
|
||||||
|
}
|
||||||
+244
@@ -0,0 +1,244 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2016 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.jetbrains.kotlin.idea.codeInsight.gradle;
|
||||||
|
|
||||||
|
import com.google.common.collect.Multimap;
|
||||||
|
import com.intellij.openapi.util.io.FileUtil;
|
||||||
|
import com.intellij.openapi.util.io.StreamUtil;
|
||||||
|
import com.intellij.testFramework.IdeaTestUtil;
|
||||||
|
import com.intellij.util.containers.ContainerUtil;
|
||||||
|
import org.codehaus.groovy.runtime.typehandling.ShortTypeHandling;
|
||||||
|
import org.gradle.tooling.BuildActionExecuter;
|
||||||
|
import org.gradle.tooling.GradleConnector;
|
||||||
|
import org.gradle.tooling.ProjectConnection;
|
||||||
|
import org.gradle.tooling.internal.consumer.DefaultGradleConnector;
|
||||||
|
import org.gradle.util.GradleVersion;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.plugins.gradle.model.ClassSetProjectImportModelProvider;
|
||||||
|
import org.jetbrains.plugins.gradle.model.ExternalProject;
|
||||||
|
import org.jetbrains.plugins.gradle.model.ProjectImportAction;
|
||||||
|
import org.jetbrains.plugins.gradle.service.execution.GradleExecutionHelper;
|
||||||
|
import org.jetbrains.plugins.gradle.tooling.builder.ModelBuildScriptClasspathBuilderImpl;
|
||||||
|
import org.jetbrains.plugins.gradle.util.GradleConstants;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.rules.TestName;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.junit.runners.Parameterized;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
import static org.junit.Assume.assumeThat;
|
||||||
|
|
||||||
|
// part of org.jetbrains.plugins.gradle.tooling.builder.AbstractModelBuilderTest
|
||||||
|
@RunWith(value = Parameterized.class)
|
||||||
|
public abstract class AbstractModelBuilderTest {
|
||||||
|
|
||||||
|
public static final Object[][] SUPPORTED_GRADLE_VERSIONS = {{"3.5"}, {"4.9"}};
|
||||||
|
|
||||||
|
private static final Pattern TEST_METHOD_NAME_PATTERN = Pattern.compile("(.*)\\[(\\d*: with Gradle-.*)\\]");
|
||||||
|
|
||||||
|
private static File ourTempDir;
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private final String gradleVersion;
|
||||||
|
private File testDir;
|
||||||
|
private ProjectImportAction.AllModels allModels;
|
||||||
|
|
||||||
|
@Rule public TestName name = new TestName();
|
||||||
|
@Rule public VersionMatcherRule versionMatcherRule = new VersionMatcherRule();
|
||||||
|
|
||||||
|
public AbstractModelBuilderTest(@NotNull String gradleVersion) {
|
||||||
|
this.gradleVersion = gradleVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Parameterized.Parameters(name = "{index}: with Gradle-{0}")
|
||||||
|
public static Collection<Object[]> data() {
|
||||||
|
return Arrays.asList(SUPPORTED_GRADLE_VERSIONS);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
assumeThat(gradleVersion, versionMatcherRule.getMatcher());
|
||||||
|
|
||||||
|
ensureTempDirCreated();
|
||||||
|
|
||||||
|
String methodName = name.getMethodName();
|
||||||
|
Matcher m = TEST_METHOD_NAME_PATTERN.matcher(methodName);
|
||||||
|
if (m.matches()) {
|
||||||
|
methodName = m.group(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
testDir = new File(ourTempDir, methodName);
|
||||||
|
FileUtil.ensureExists(testDir);
|
||||||
|
|
||||||
|
InputStream buildScriptStream = getClass().getResourceAsStream("/" + methodName + "/" + GradleConstants.DEFAULT_SCRIPT_NAME);
|
||||||
|
try {
|
||||||
|
FileUtil.writeToFile(
|
||||||
|
new File(testDir, GradleConstants.DEFAULT_SCRIPT_NAME),
|
||||||
|
FileUtil.loadTextAndClose(buildScriptStream)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
StreamUtil.closeStream(buildScriptStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
InputStream settingsStream = getClass().getResourceAsStream("/" + methodName + "/" + GradleConstants.SETTINGS_FILE_NAME);
|
||||||
|
try {
|
||||||
|
if (settingsStream != null) {
|
||||||
|
FileUtil.writeToFile(
|
||||||
|
new File(testDir, GradleConstants.SETTINGS_FILE_NAME),
|
||||||
|
FileUtil.loadTextAndClose(settingsStream)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
StreamUtil.closeStream(settingsStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
GradleConnector connector = GradleConnector.newConnector();
|
||||||
|
|
||||||
|
URI distributionUri = new DistributionLocator().getDistributionFor(GradleVersion.version(gradleVersion));
|
||||||
|
connector.useDistribution(distributionUri);
|
||||||
|
connector.forProjectDirectory(testDir);
|
||||||
|
int daemonMaxIdleTime = 10;
|
||||||
|
try {
|
||||||
|
daemonMaxIdleTime = Integer.parseInt(System.getProperty("gradleDaemonMaxIdleTime", "10"));
|
||||||
|
}
|
||||||
|
catch (NumberFormatException ignore) {
|
||||||
|
}
|
||||||
|
|
||||||
|
((DefaultGradleConnector) connector).daemonMaxIdleTime(daemonMaxIdleTime, TimeUnit.SECONDS);
|
||||||
|
ProjectConnection connection = connector.connect();
|
||||||
|
|
||||||
|
try {
|
||||||
|
ProjectImportAction projectImportAction = new ProjectImportAction(false);
|
||||||
|
projectImportAction.addProjectImportModelProvider(new ClassSetProjectImportModelProvider(getModels()));
|
||||||
|
BuildActionExecuter<ProjectImportAction.AllModels> buildActionExecutor = connection.action(projectImportAction);
|
||||||
|
File initScript = GradleExecutionHelper.generateInitScript(false, getToolingExtensionClasses());
|
||||||
|
assertNotNull(initScript);
|
||||||
|
String jdkHome = IdeaTestUtil.requireRealJdkHome();
|
||||||
|
buildActionExecutor.setJavaHome(new File(jdkHome));
|
||||||
|
buildActionExecutor.setJvmArguments("-Xmx128m", "-XX:MaxPermSize=64m");
|
||||||
|
buildActionExecutor
|
||||||
|
.withArguments("--info", "--recompile-scripts", GradleConstants.INIT_SCRIPT_CMD_OPTION, initScript.getAbsolutePath());
|
||||||
|
allModels = buildActionExecutor.run();
|
||||||
|
assertNotNull(allModels);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
connection.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private static Set<Class> getToolingExtensionClasses() {
|
||||||
|
Set<Class> classes = ContainerUtil.<Class>set(
|
||||||
|
ExternalProject.class,
|
||||||
|
// gradle-tooling-extension-api jar
|
||||||
|
ProjectImportAction.class,
|
||||||
|
// gradle-tooling-extension-impl jar
|
||||||
|
ModelBuildScriptClasspathBuilderImpl.class,
|
||||||
|
Multimap.class,
|
||||||
|
ShortTypeHandling.class
|
||||||
|
);
|
||||||
|
|
||||||
|
ContainerUtil.addAllNotNull(classes, doGetToolingExtensionClasses());
|
||||||
|
return classes;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private static Set<Class> doGetToolingExtensionClasses() {
|
||||||
|
return Collections.emptySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void tearDown() throws Exception {
|
||||||
|
if (testDir != null) {
|
||||||
|
FileUtil.delete(testDir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract Set<Class> getModels();
|
||||||
|
|
||||||
|
|
||||||
|
private static void ensureTempDirCreated() throws IOException {
|
||||||
|
if (ourTempDir != null) return;
|
||||||
|
|
||||||
|
ourTempDir = new File(FileUtil.getTempDirectory(), "gradleTests");
|
||||||
|
FileUtil.delete(ourTempDir);
|
||||||
|
FileUtil.ensureExists(ourTempDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class DistributionLocator {
|
||||||
|
private static final String RELEASE_REPOSITORY_ENV = "GRADLE_RELEASE_REPOSITORY";
|
||||||
|
private static final String SNAPSHOT_REPOSITORY_ENV = "GRADLE_SNAPSHOT_REPOSITORY";
|
||||||
|
private static final String GRADLE_RELEASE_REPO = "https://services.gradle.org/distributions";
|
||||||
|
private static final String GRADLE_SNAPSHOT_REPO = "https://services.gradle.org/distributions-snapshots";
|
||||||
|
|
||||||
|
@NotNull private final String myReleaseRepoUrl;
|
||||||
|
@NotNull private final String mySnapshotRepoUrl;
|
||||||
|
|
||||||
|
public DistributionLocator() {
|
||||||
|
this(DistributionLocator.getRepoUrl(false), DistributionLocator.getRepoUrl(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
public DistributionLocator(@NotNull String releaseRepoUrl, @NotNull String snapshotRepoUrl) {
|
||||||
|
myReleaseRepoUrl = releaseRepoUrl;
|
||||||
|
mySnapshotRepoUrl = snapshotRepoUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public URI getDistributionFor(@NotNull GradleVersion version) throws URISyntaxException {
|
||||||
|
return getDistribution(getDistributionRepository(version), version, "gradle", "bin");
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private String getDistributionRepository(@NotNull GradleVersion version) {
|
||||||
|
return version.isSnapshot() ? mySnapshotRepoUrl : myReleaseRepoUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static URI getDistribution(
|
||||||
|
@NotNull String repositoryUrl,
|
||||||
|
@NotNull GradleVersion version,
|
||||||
|
@NotNull String archiveName,
|
||||||
|
@NotNull String archiveClassifier
|
||||||
|
) throws URISyntaxException {
|
||||||
|
return new URI(String.format("%s/%s-%s-%s.zip", repositoryUrl, archiveName, version.getVersion(), archiveClassifier));
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public static String getRepoUrl(boolean isSnapshotUrl) {
|
||||||
|
String envRepoUrl = System.getenv(isSnapshotUrl ? SNAPSHOT_REPOSITORY_ENV : RELEASE_REPOSITORY_ENV);
|
||||||
|
if (envRepoUrl != null) return envRepoUrl;
|
||||||
|
|
||||||
|
return isSnapshotUrl ? GRADLE_SNAPSHOT_REPO : GRADLE_RELEASE_REPO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+551
@@ -0,0 +1,551 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.perf
|
||||||
|
|
||||||
|
import com.intellij.codeInsight.daemon.*
|
||||||
|
import com.intellij.codeInsight.daemon.impl.DaemonCodeAnalyzerImpl
|
||||||
|
import com.intellij.codeInsight.daemon.impl.HighlightInfo
|
||||||
|
import com.intellij.codeInsight.daemon.impl.IdentifierHighlighterPassFactory
|
||||||
|
import com.intellij.codeInsight.lookup.LookupElement
|
||||||
|
import com.intellij.ide.highlighter.ModuleFileType
|
||||||
|
import com.intellij.ide.startup.impl.StartupManagerImpl
|
||||||
|
import com.intellij.idea.IdeaTestApplication
|
||||||
|
import com.intellij.openapi.application.ApplicationManager
|
||||||
|
import com.intellij.openapi.application.runWriteAction
|
||||||
|
import com.intellij.openapi.editor.EditorFactory
|
||||||
|
import com.intellij.openapi.externalSystem.service.project.manage.ExternalProjectsManagerImpl
|
||||||
|
import com.intellij.openapi.fileEditor.FileDocumentManager
|
||||||
|
import com.intellij.openapi.fileEditor.FileEditorManager
|
||||||
|
import com.intellij.openapi.fileEditor.impl.FileEditorManagerImpl
|
||||||
|
import com.intellij.openapi.module.ModuleManager
|
||||||
|
import com.intellij.openapi.module.ModuleTypeId
|
||||||
|
import com.intellij.openapi.project.DumbService
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.openapi.project.ex.ProjectManagerEx
|
||||||
|
import com.intellij.openapi.project.impl.ProjectImpl
|
||||||
|
import com.intellij.openapi.projectRoots.JavaSdk
|
||||||
|
import com.intellij.openapi.projectRoots.ProjectJdkTable
|
||||||
|
import com.intellij.openapi.projectRoots.Sdk
|
||||||
|
import com.intellij.openapi.projectRoots.impl.JavaAwareProjectJdkTableImpl
|
||||||
|
import com.intellij.openapi.roots.ProjectRootManager
|
||||||
|
import com.intellij.openapi.roots.ex.ProjectRootManagerEx
|
||||||
|
import com.intellij.openapi.startup.StartupManager
|
||||||
|
import com.intellij.openapi.vcs.changes.ChangeListManager
|
||||||
|
import com.intellij.openapi.vcs.changes.ChangeListManagerImpl
|
||||||
|
import com.intellij.openapi.vfs.LocalFileSystem
|
||||||
|
import com.intellij.openapi.vfs.VirtualFileManager
|
||||||
|
import com.intellij.psi.PsiDocumentManager
|
||||||
|
import com.intellij.psi.PsiFile
|
||||||
|
import com.intellij.psi.PsiManager
|
||||||
|
import com.intellij.testFramework.*
|
||||||
|
import com.intellij.testFramework.fixtures.impl.CodeInsightTestFixtureImpl
|
||||||
|
import com.intellij.util.ArrayUtilRt
|
||||||
|
import com.intellij.util.ThrowableRunnable
|
||||||
|
import com.intellij.util.indexing.UnindexedFilesUpdater
|
||||||
|
import com.intellij.util.io.exists
|
||||||
|
import org.jetbrains.kotlin.idea.configuration.getModulesWithKotlinFiles
|
||||||
|
import org.jetbrains.kotlin.idea.core.script.ScriptConfigurationManager
|
||||||
|
import org.jetbrains.kotlin.idea.framework.KotlinSdkType
|
||||||
|
import org.jetbrains.kotlin.idea.perf.Stats.Companion.WARM_UP
|
||||||
|
import org.jetbrains.kotlin.idea.perf.Stats.Companion.runAndMeasure
|
||||||
|
import org.jetbrains.kotlin.idea.project.getAndCacheLanguageLevelByDependencies
|
||||||
|
import org.jetbrains.kotlin.idea.test.ConfigLibraryUtil
|
||||||
|
import org.jetbrains.kotlin.idea.test.invalidateLibraryCache
|
||||||
|
import org.jetbrains.kotlin.idea.testFramework.*
|
||||||
|
import org.jetbrains.kotlin.idea.testFramework.Fixture.Companion.cleanupCaches
|
||||||
|
import org.jetbrains.kotlin.idea.testFramework.Fixture.Companion.isAKotlinScriptFile
|
||||||
|
import org.jetbrains.kotlin.idea.testFramework.Fixture.Companion.openFileInEditor
|
||||||
|
import org.jetbrains.kotlin.idea.testFramework.Fixture.Companion.openFixture
|
||||||
|
import java.io.File
|
||||||
|
import java.nio.file.Paths
|
||||||
|
|
||||||
|
abstract class AbstractPerformanceProjectsTest : UsefulTestCase() {
|
||||||
|
|
||||||
|
// myProject is not required for all potential perf test cases
|
||||||
|
protected var myProject: Project? = null
|
||||||
|
private lateinit var jdk18: Sdk
|
||||||
|
private lateinit var myApplication: IdeaTestApplication
|
||||||
|
|
||||||
|
override fun isStressTest(): Boolean = true
|
||||||
|
|
||||||
|
override fun isPerformanceTest(): Boolean = false
|
||||||
|
|
||||||
|
override fun setUp() {
|
||||||
|
super.setUp()
|
||||||
|
|
||||||
|
myApplication = IdeaTestApplication.getInstance()
|
||||||
|
runWriteAction {
|
||||||
|
val jdkTableImpl = JavaAwareProjectJdkTableImpl.getInstanceEx()
|
||||||
|
val homePath = if (jdkTableImpl.internalJdk.homeDirectory!!.name == "jre") {
|
||||||
|
jdkTableImpl.internalJdk.homeDirectory!!.parent.path
|
||||||
|
} else {
|
||||||
|
jdkTableImpl.internalJdk.homePath!!
|
||||||
|
}
|
||||||
|
|
||||||
|
val javaSdk = JavaSdk.getInstance()
|
||||||
|
jdk18 = javaSdk.createJdk("1.8", homePath)
|
||||||
|
val internal = javaSdk.createJdk("IDEA jdk", homePath)
|
||||||
|
|
||||||
|
val jdkTable = ProjectJdkTable.getInstance()
|
||||||
|
jdkTable.addJdk(jdk18, testRootDisposable)
|
||||||
|
jdkTable.addJdk(internal, testRootDisposable)
|
||||||
|
KotlinSdkType.setUpIfNeeded()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun warmUpProject(stats: Stats) {
|
||||||
|
val project = perfOpenHelloWorld(stats, WARM_UP)
|
||||||
|
try {
|
||||||
|
val perfHighlightFile = perfHighlightFile(project, "src/HelloMain.kt", stats, WARM_UP)
|
||||||
|
assertTrue("kotlin project has been not imported properly", perfHighlightFile.isNotEmpty())
|
||||||
|
} finally {
|
||||||
|
closeProject(project)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun tearDown() {
|
||||||
|
commitAllDocuments()
|
||||||
|
RunAll(
|
||||||
|
ThrowableRunnable { super.tearDown() },
|
||||||
|
ThrowableRunnable {
|
||||||
|
if (myProject != null) {
|
||||||
|
DaemonCodeAnalyzerSettings.getInstance().isImportHintEnabled = true // return default value to avoid unnecessary save
|
||||||
|
(StartupManager.getInstance(myProject!!) as StartupManagerImpl).checkCleared()
|
||||||
|
(DaemonCodeAnalyzer.getInstance(myProject!!) as DaemonCodeAnalyzerImpl).cleanupAfterTest()
|
||||||
|
closeProject(myProject!!)
|
||||||
|
myProject = null
|
||||||
|
}
|
||||||
|
}).run()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun simpleFilename(fileName: String): String {
|
||||||
|
val lastIndexOf = fileName.lastIndexOf('/')
|
||||||
|
return if (lastIndexOf >= 0) fileName.substring(lastIndexOf + 1) else fileName
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun perfOpenKotlinProjectFast(stats: Stats) =
|
||||||
|
perfOpenKotlinProject(stats, fast = true)
|
||||||
|
|
||||||
|
protected fun perfOpenKotlinProject(stats: Stats, fast: Boolean = false) {
|
||||||
|
myProject = innerPerfOpenProject("kotlin", stats = stats, path = "../perfTestProject", note = "", fast = fast)
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun perfOpenHelloWorld(stats: Stats, note: String = ""): Project =
|
||||||
|
innerPerfOpenProject("helloKotlin", stats, note, path = "idea/testData/perfTest/helloKotlin", simpleModule = true)
|
||||||
|
|
||||||
|
protected fun innerPerfOpenProject(
|
||||||
|
name: String,
|
||||||
|
stats: Stats,
|
||||||
|
note: String,
|
||||||
|
path: String,
|
||||||
|
simpleModule: Boolean = false,
|
||||||
|
fast: Boolean = false
|
||||||
|
): Project {
|
||||||
|
val projectPath = File(path).canonicalPath
|
||||||
|
|
||||||
|
assertTrue("path $path does not exist, check README.md", File(projectPath).exists())
|
||||||
|
|
||||||
|
val warmUpIterations = if (fast) 0 else 1
|
||||||
|
val iterations = if (fast) 1 else 3
|
||||||
|
val projectManagerEx = ProjectManagerEx.getInstanceEx()
|
||||||
|
|
||||||
|
var lastProject: Project? = null
|
||||||
|
var counter = 0
|
||||||
|
|
||||||
|
stats.perfTest<Unit, Project>(
|
||||||
|
warmUpIterations = warmUpIterations,
|
||||||
|
iterations = iterations,
|
||||||
|
testName = "open project${if (note.isNotEmpty()) " $note" else ""}",
|
||||||
|
test = {
|
||||||
|
val project = if (!simpleModule) {
|
||||||
|
val project = projectManagerEx.loadProject(Paths.get(path), name)
|
||||||
|
assertNotNull("project $name at $path is not loaded", project)
|
||||||
|
val projectRootManager = ProjectRootManager.getInstance(project!!)
|
||||||
|
|
||||||
|
runWriteAction {
|
||||||
|
with(projectRootManager) {
|
||||||
|
projectSdk = jdk18
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertTrue("project $name at $path is not opened", projectManagerEx.openProject(project))
|
||||||
|
project
|
||||||
|
} else {
|
||||||
|
val project = projectManagerEx.loadAndOpenProject(projectPath)!!
|
||||||
|
initKotlinProject(project, projectPath, name)
|
||||||
|
project
|
||||||
|
}
|
||||||
|
|
||||||
|
(project as ProjectImpl).registerComponentImplementation(
|
||||||
|
FileEditorManager::class.java,
|
||||||
|
FileEditorManagerImpl::class.java
|
||||||
|
)
|
||||||
|
|
||||||
|
dispatchAllInvocationEvents()
|
||||||
|
|
||||||
|
with(StartupManager.getInstance(project) as StartupManagerImpl) {
|
||||||
|
ProjectRootManagerEx.getInstanceEx(project).markRootsForRefresh()
|
||||||
|
VirtualFileManager.getInstance().syncRefresh()
|
||||||
|
|
||||||
|
runStartupActivities()
|
||||||
|
runPostStartupActivities()
|
||||||
|
}
|
||||||
|
|
||||||
|
logMessage { "project $name is ${if (project.isInitialized) "initialized" else "not initialized"}" }
|
||||||
|
|
||||||
|
with(ChangeListManager.getInstance(project) as ChangeListManagerImpl) {
|
||||||
|
waitUntilRefreshed()
|
||||||
|
}
|
||||||
|
|
||||||
|
it.value = project
|
||||||
|
},
|
||||||
|
tearDown = {
|
||||||
|
it.value?.let { project ->
|
||||||
|
|
||||||
|
runAndMeasure("refresh gradle project $name") {
|
||||||
|
refreshGradleProjectIfNeeded(projectPath, project)
|
||||||
|
}
|
||||||
|
|
||||||
|
ApplicationManager.getApplication().executeOnPooledThread {
|
||||||
|
DumbService.getInstance(project).waitForSmartMode()
|
||||||
|
|
||||||
|
for (module in getModulesWithKotlinFiles(project)) {
|
||||||
|
module.getAndCacheLanguageLevelByDependencies()
|
||||||
|
}
|
||||||
|
}.get()
|
||||||
|
|
||||||
|
val modules = ModuleManager.getInstance(project).modules
|
||||||
|
assertTrue("project has to have at least one module", modules.isNotEmpty())
|
||||||
|
|
||||||
|
logMessage { "modules of $name: ${modules.map { m -> m.name }}" }
|
||||||
|
|
||||||
|
lastProject = project
|
||||||
|
VirtualFileManager.getInstance().syncRefresh()
|
||||||
|
|
||||||
|
runWriteAction {
|
||||||
|
project.save()
|
||||||
|
}
|
||||||
|
|
||||||
|
logMessage { "project '$name' successfully opened" }
|
||||||
|
|
||||||
|
// close all project but last - we're going to return and use it further
|
||||||
|
if (counter < warmUpIterations + iterations - 1) {
|
||||||
|
closeProject(project)
|
||||||
|
}
|
||||||
|
counter++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// indexing
|
||||||
|
lastProject?.let { project ->
|
||||||
|
invalidateLibraryCache(project)
|
||||||
|
|
||||||
|
CodeInsightTestFixtureImpl.ensureIndexesUpToDate(project)
|
||||||
|
|
||||||
|
dispatchAllInvocationEvents()
|
||||||
|
|
||||||
|
logMessage { "project $name is ${if (project.isInitialized) "initialized" else "not initialized"}" }
|
||||||
|
|
||||||
|
with(DumbService.getInstance(project)) {
|
||||||
|
queueTask(UnindexedFilesUpdater(project))
|
||||||
|
completeJustSubmittedTasks()
|
||||||
|
}
|
||||||
|
dispatchAllInvocationEvents()
|
||||||
|
|
||||||
|
Fixture.enableAnnotatorsAndLoadDefinitions(project)
|
||||||
|
}
|
||||||
|
|
||||||
|
return lastProject ?: error("unable to open project $name at $path")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun refreshGradleProjectIfNeeded(projectPath: String, project: Project) {
|
||||||
|
if (listOf("build.gradle.kts", "build.gradle").map { name -> Paths.get(projectPath, name).exists() }.find { e -> e } != true) return
|
||||||
|
|
||||||
|
ExternalProjectsManagerImpl.getInstance(project).setStoreExternally(true)
|
||||||
|
|
||||||
|
refreshGradleProject(projectPath, project)
|
||||||
|
|
||||||
|
//ExternalProjectsManagerImpl.getInstance(project).setStoreExternally(false)
|
||||||
|
dispatchAllInvocationEvents()
|
||||||
|
|
||||||
|
// WARNING: [VD] DO NOT SAVE PROJECT AS IT COULD PERSIST WRONG MODULES INFO
|
||||||
|
|
||||||
|
// runInEdtAndWait {
|
||||||
|
// PlatformTestUtil.saveProject(project)
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun perfTypeAndAutocomplete(
|
||||||
|
stats: Stats,
|
||||||
|
fileName: String,
|
||||||
|
marker: String,
|
||||||
|
insertString: String,
|
||||||
|
surroundItems: String = "\n",
|
||||||
|
lookupElements: List<String>,
|
||||||
|
typeAfterMarker: Boolean = true,
|
||||||
|
revertChangesAtTheEnd: Boolean = true,
|
||||||
|
note: String = ""
|
||||||
|
) = perfTypeAndAutocomplete(
|
||||||
|
myProject!!, stats, fileName, marker, insertString, surroundItems,
|
||||||
|
lookupElements = lookupElements, typeAfterMarker = typeAfterMarker,
|
||||||
|
revertChangesAtTheEnd = revertChangesAtTheEnd, note = note
|
||||||
|
)
|
||||||
|
|
||||||
|
fun perfTypeAndAutocomplete(
|
||||||
|
project: Project,
|
||||||
|
stats: Stats,
|
||||||
|
fileName: String,
|
||||||
|
marker: String,
|
||||||
|
insertString: String,
|
||||||
|
surroundItems: String = "\n",
|
||||||
|
lookupElements: List<String>,
|
||||||
|
typeAfterMarker: Boolean = true,
|
||||||
|
revertChangesAtTheEnd: Boolean = true,
|
||||||
|
note: String = ""
|
||||||
|
) {
|
||||||
|
assertTrue("lookupElements has to be not empty", lookupElements.isNotEmpty())
|
||||||
|
stats.perfTest<Pair<String, Fixture>, Array<LookupElement>>(
|
||||||
|
warmUpIterations = 8,
|
||||||
|
iterations = 15,
|
||||||
|
testName = "typeAndAutocomplete ${notePrefix(note)}$fileName",
|
||||||
|
setUp = {
|
||||||
|
val fixture = openFixture(project, fileName)
|
||||||
|
val editor = fixture.editor
|
||||||
|
|
||||||
|
val initialText = editor.document.text
|
||||||
|
if (isAKotlinScriptFile(fileName)) {
|
||||||
|
runAndMeasure("update script dependencies for $fileName") {
|
||||||
|
ScriptConfigurationManager.updateScriptDependenciesSynchronously(fixture.psiFile, project)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val tasksIdx = editor.document.text.indexOf(marker)
|
||||||
|
assertTrue("marker '$marker' not found in $fileName", tasksIdx > 0)
|
||||||
|
if (typeAfterMarker) {
|
||||||
|
editor.caretModel.moveToOffset(tasksIdx + marker.length + 1)
|
||||||
|
} else {
|
||||||
|
editor.caretModel.moveToOffset(tasksIdx - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
for (surroundItem in surroundItems) {
|
||||||
|
EditorTestUtil.performTypingAction(editor, surroundItem)
|
||||||
|
}
|
||||||
|
|
||||||
|
editor.caretModel.moveToOffset(editor.caretModel.offset - if (typeAfterMarker) 1 else 2)
|
||||||
|
|
||||||
|
if (!typeAfterMarker) {
|
||||||
|
for (surroundItem in surroundItems) {
|
||||||
|
EditorTestUtil.performTypingAction(editor, surroundItem)
|
||||||
|
}
|
||||||
|
editor.caretModel.moveToOffset(editor.caretModel.offset - 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
fixture.type(insertString)
|
||||||
|
|
||||||
|
it.setUpValue = Pair(initialText, fixture)
|
||||||
|
},
|
||||||
|
test = {
|
||||||
|
val fixture = it.setUpValue!!.second
|
||||||
|
it.value = fixture.complete()
|
||||||
|
},
|
||||||
|
tearDown = {
|
||||||
|
val items = it.value?.map { e -> e.lookupString }?.toList() ?: emptyList()
|
||||||
|
try {
|
||||||
|
for (lookupElement in lookupElements) {
|
||||||
|
assertTrue("'$lookupElement' has to be present in items", items.contains(lookupElement))
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
it.setUpValue?.let { pair ->
|
||||||
|
pair.second.revertChanges(revertChangesAtTheEnd, pair.first)
|
||||||
|
}
|
||||||
|
commitAllDocuments()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun perfTypeAndHighlight(
|
||||||
|
stats: Stats,
|
||||||
|
fileName: String,
|
||||||
|
marker: String,
|
||||||
|
insertString: String,
|
||||||
|
surroundItems: String = "\n",
|
||||||
|
typeAfterMarker: Boolean = true,
|
||||||
|
revertChangesAtTheEnd: Boolean = true,
|
||||||
|
note: String = ""
|
||||||
|
) = perfTypeAndHighlight(
|
||||||
|
myProject!!, stats, fileName, marker, insertString, surroundItems,
|
||||||
|
typeAfterMarker = typeAfterMarker,
|
||||||
|
revertChangesAtTheEnd = revertChangesAtTheEnd, note = note
|
||||||
|
)
|
||||||
|
|
||||||
|
fun perfTypeAndHighlight(
|
||||||
|
project: Project,
|
||||||
|
stats: Stats,
|
||||||
|
fileName: String,
|
||||||
|
marker: String,
|
||||||
|
insertString: String,
|
||||||
|
surroundItems: String = "\n",
|
||||||
|
typeAfterMarker: Boolean = true,
|
||||||
|
revertChangesAtTheEnd: Boolean = true,
|
||||||
|
note: String = ""
|
||||||
|
) {
|
||||||
|
stats.perfTest<Pair<String, Fixture>, List<HighlightInfo>>(
|
||||||
|
warmUpIterations = 8,
|
||||||
|
iterations = 15,
|
||||||
|
testName = "typeAndHighlight ${notePrefix(note)}$fileName",
|
||||||
|
setUp = {
|
||||||
|
val fixture = openFixture(project, fileName)
|
||||||
|
val editor = fixture.editor
|
||||||
|
|
||||||
|
val initialText = editor.document.text
|
||||||
|
if (isAKotlinScriptFile(fileName)) {
|
||||||
|
runAndMeasure("update script dependencies for $fileName") {
|
||||||
|
ScriptConfigurationManager.updateScriptDependenciesSynchronously(fixture.psiFile, project)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val tasksIdx = editor.document.text.indexOf(marker)
|
||||||
|
assertTrue("marker '$marker' not found in $fileName", tasksIdx > 0)
|
||||||
|
if (typeAfterMarker) {
|
||||||
|
editor.caretModel.moveToOffset(tasksIdx + marker.length + 1)
|
||||||
|
} else {
|
||||||
|
editor.caretModel.moveToOffset(tasksIdx - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
for (surroundItem in surroundItems) {
|
||||||
|
EditorTestUtil.performTypingAction(editor, surroundItem)
|
||||||
|
}
|
||||||
|
|
||||||
|
editor.caretModel.moveToOffset(editor.caretModel.offset - if (typeAfterMarker) 1 else 2)
|
||||||
|
|
||||||
|
if (!typeAfterMarker) {
|
||||||
|
for (surroundItem in surroundItems) {
|
||||||
|
EditorTestUtil.performTypingAction(editor, surroundItem)
|
||||||
|
}
|
||||||
|
editor.caretModel.moveToOffset(editor.caretModel.offset - 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
fixture.type(insertString)
|
||||||
|
|
||||||
|
it.setUpValue = Pair(initialText, fixture)
|
||||||
|
},
|
||||||
|
test = {
|
||||||
|
val fixture = it.setUpValue!!.second
|
||||||
|
it.value = fixture.doHighlighting()
|
||||||
|
},
|
||||||
|
tearDown = {
|
||||||
|
it.value?.let { list ->
|
||||||
|
assertNotEmpty(list)
|
||||||
|
}
|
||||||
|
it.setUpValue?.let { pair ->
|
||||||
|
pair.second.revertChanges(revertChangesAtTheEnd, pair.first)
|
||||||
|
}
|
||||||
|
commitAllDocuments()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initKotlinProject(
|
||||||
|
project: Project,
|
||||||
|
projectPath: String,
|
||||||
|
name: String
|
||||||
|
) {
|
||||||
|
val modulePath = "$projectPath/$name${ModuleFileType.DOT_DEFAULT_EXTENSION}"
|
||||||
|
val projectFile = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(File(projectPath))!!
|
||||||
|
val srcFile = projectFile.findChild("src")!!
|
||||||
|
val module = runWriteAction {
|
||||||
|
val projectRootManager = ProjectRootManager.getInstance(project)
|
||||||
|
with(projectRootManager) {
|
||||||
|
projectSdk = jdk18
|
||||||
|
}
|
||||||
|
val moduleManager = ModuleManager.getInstance(project)
|
||||||
|
val module = moduleManager.newModule(modulePath, ModuleTypeId.JAVA_MODULE)
|
||||||
|
PsiTestUtil.addSourceRoot(module, srcFile)
|
||||||
|
module
|
||||||
|
}
|
||||||
|
ConfigLibraryUtil.configureKotlinRuntimeAndSdk(module, jdk18)
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun perfHighlightFile(name: String, stats: Stats): List<HighlightInfo> =
|
||||||
|
perfHighlightFile(myProject!!, name, stats)
|
||||||
|
|
||||||
|
protected fun perfHighlightFile(
|
||||||
|
project: Project,
|
||||||
|
fileName: String,
|
||||||
|
stats: Stats,
|
||||||
|
note: String = ""
|
||||||
|
): List<HighlightInfo> {
|
||||||
|
return highlightFile {
|
||||||
|
val isWarmUp = note == WARM_UP
|
||||||
|
var highlightInfos: List<HighlightInfo> = emptyList()
|
||||||
|
stats.perfTest<EditorFile, List<HighlightInfo>>(
|
||||||
|
warmUpIterations = if (isWarmUp) 1 else 3,
|
||||||
|
iterations = if (isWarmUp) 2 else 10,
|
||||||
|
testName = "highlighting ${notePrefix(note)}${simpleFilename(fileName)}",
|
||||||
|
setUp = {
|
||||||
|
it.setUpValue = openFileInEditor(project, fileName)
|
||||||
|
},
|
||||||
|
test = {
|
||||||
|
val file = it.setUpValue
|
||||||
|
it.value = highlightFile(project, file!!.psiFile)
|
||||||
|
},
|
||||||
|
tearDown = {
|
||||||
|
highlightInfos = it.value ?: emptyList()
|
||||||
|
commitAllDocuments()
|
||||||
|
FileEditorManager.getInstance(project).closeFile(it.setUpValue!!.psiFile.virtualFile)
|
||||||
|
PsiManager.getInstance(project).dropPsiCaches()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
highlightInfos
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T> highlightFile(block: () -> T): T {
|
||||||
|
var value: T? = null
|
||||||
|
IdentifierHighlighterPassFactory.doWithHighlightingEnabled {
|
||||||
|
value = block()
|
||||||
|
}
|
||||||
|
return value!!
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun highlightFile(project: Project, psiFile: PsiFile): List<HighlightInfo> {
|
||||||
|
val document = FileDocumentManager.getInstance().getDocument(psiFile.virtualFile)!!
|
||||||
|
val editor = EditorFactory.getInstance().getEditors(document).first()
|
||||||
|
PsiDocumentManager.getInstance(project).commitAllDocuments()
|
||||||
|
return CodeInsightTestFixtureImpl.instantiateAndRun(psiFile, editor, ArrayUtilRt.EMPTY_INT_ARRAY, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun perfScriptDependencies(name: String, stats: Stats, note: String = "") =
|
||||||
|
perfScriptDependencies(myProject!!, name, stats, note = note)
|
||||||
|
|
||||||
|
private fun perfScriptDependencies(
|
||||||
|
project: Project,
|
||||||
|
fileName: String,
|
||||||
|
stats: Stats,
|
||||||
|
note: String = ""
|
||||||
|
) {
|
||||||
|
if (!isAKotlinScriptFile(fileName)) return
|
||||||
|
stats.perfTest<EditorFile, EditorFile>(
|
||||||
|
testName = "updateScriptDependencies ${notePrefix(note)}${simpleFilename(fileName)}",
|
||||||
|
setUp = { it.setUpValue = openFileInEditor(project, fileName) },
|
||||||
|
test = {
|
||||||
|
ScriptConfigurationManager.updateScriptDependenciesSynchronously(it.setUpValue!!.psiFile, project)
|
||||||
|
it.value = it.setUpValue
|
||||||
|
},
|
||||||
|
tearDown = {
|
||||||
|
it.setUpValue?.let { ef -> cleanupCaches(project, ef.psiFile.virtualFile) }
|
||||||
|
it.value?.let { v -> assertNotNull(v) }
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun notePrefix(note: String) = if (note.isNotEmpty()) {
|
||||||
|
if (note.endsWith("/")) note else "$note "
|
||||||
|
} else ""
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.testFramework
|
||||||
|
|
||||||
|
import com.intellij.lang.LanguageAnnotators
|
||||||
|
import com.intellij.lang.LanguageExtensionPoint
|
||||||
|
import com.intellij.lang.annotation.Annotator
|
||||||
|
import com.intellij.openapi.Disposable
|
||||||
|
import com.intellij.openapi.editor.Document
|
||||||
|
import com.intellij.openapi.extensions.ExtensionPointName
|
||||||
|
import com.intellij.openapi.fileEditor.FileDocumentManager
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.openapi.project.ex.ProjectManagerEx
|
||||||
|
import com.intellij.psi.PsiDocumentManager
|
||||||
|
import com.intellij.psi.impl.PsiDocumentManagerBase
|
||||||
|
import com.intellij.testFramework.ExtensionTestUtil
|
||||||
|
import com.intellij.testFramework.runInEdtAndWait
|
||||||
|
import com.intellij.util.ui.UIUtil
|
||||||
|
import org.jetbrains.kotlin.idea.parameterInfo.HintType
|
||||||
|
|
||||||
|
fun commitAllDocuments() {
|
||||||
|
val fileDocumentManager = FileDocumentManager.getInstance()
|
||||||
|
runInEdtAndWait {
|
||||||
|
fileDocumentManager.saveAllDocuments()
|
||||||
|
}
|
||||||
|
|
||||||
|
ProjectManagerEx.getInstanceEx().openProjects.forEach { project ->
|
||||||
|
val psiDocumentManagerBase = PsiDocumentManager.getInstance(project) as PsiDocumentManagerBase
|
||||||
|
|
||||||
|
runInEdtAndWait {
|
||||||
|
psiDocumentManagerBase.clearUncommittedDocuments()
|
||||||
|
psiDocumentManagerBase.commitAllDocuments()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun commitDocument(project: Project, document: Document) {
|
||||||
|
val psiDocumentManagerBase = PsiDocumentManager.getInstance(project) as PsiDocumentManagerBase
|
||||||
|
runInEdtAndWait {
|
||||||
|
psiDocumentManagerBase.commitDocument(document)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun saveDocument(document: Document) {
|
||||||
|
val fileDocumentManager = FileDocumentManager.getInstance()
|
||||||
|
|
||||||
|
runInEdtAndWait {
|
||||||
|
fileDocumentManager.saveDocument(document)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun enableHints(enable: Boolean) =
|
||||||
|
HintType.values().forEach { it.option.set(enable) }
|
||||||
|
|
||||||
|
fun dispatchAllInvocationEvents() {
|
||||||
|
runInEdtAndWait {
|
||||||
|
UIUtil.dispatchAllInvocationEvents()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun closeProject(project: Project) {
|
||||||
|
dispatchAllInvocationEvents()
|
||||||
|
val projectManagerEx = ProjectManagerEx.getInstanceEx()
|
||||||
|
projectManagerEx.forceCloseProject(project, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun waitForAllEditorsFinallyLoaded(project: Project) {
|
||||||
|
// routing is obsolete in 192
|
||||||
|
}
|
||||||
|
|
||||||
|
fun replaceWithCustomHighlighter(parentDisposable: Disposable, fromImplementationClass: String, toImplementationClass: String) {
|
||||||
|
val pointName = ExtensionPointName.create<LanguageExtensionPoint<Annotator>>(LanguageAnnotators.EP_NAME)
|
||||||
|
val extensionPoint = pointName.getPoint(null)
|
||||||
|
|
||||||
|
val point = LanguageExtensionPoint<Annotator>()
|
||||||
|
point.language = "kotlin"
|
||||||
|
point.implementationClass = toImplementationClass
|
||||||
|
|
||||||
|
val extensions = extensionPoint.extensions
|
||||||
|
val filteredExtensions =
|
||||||
|
extensions.filter { it.language != "kotlin" || it.implementationClass != fromImplementationClass }
|
||||||
|
.toList()
|
||||||
|
// custom highlighter is already registered if filteredExtensions has the same size as extensions
|
||||||
|
if (filteredExtensions.size < extensions.size) {
|
||||||
|
ExtensionTestUtil.maskExtensions(pointName, filteredExtensions + listOf(point), parentDisposable)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun logMessage(message: () -> String) {
|
||||||
|
println("-- ${message()}")
|
||||||
|
}
|
||||||
@@ -0,0 +1,146 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.formatter;
|
||||||
|
|
||||||
|
import com.intellij.openapi.application.ApplicationManager;
|
||||||
|
import com.intellij.openapi.command.CommandProcessor;
|
||||||
|
import com.intellij.openapi.editor.Document;
|
||||||
|
import com.intellij.openapi.editor.impl.DocumentImpl;
|
||||||
|
import com.intellij.openapi.roots.LanguageLevelProjectExtension;
|
||||||
|
import com.intellij.openapi.util.TextRange;
|
||||||
|
import com.intellij.openapi.util.io.FileUtil;
|
||||||
|
import com.intellij.pom.java.LanguageLevel;
|
||||||
|
import com.intellij.psi.PsiDocumentManager;
|
||||||
|
import com.intellij.psi.PsiFile;
|
||||||
|
import com.intellij.psi.codeStyle.CodeStyleManager;
|
||||||
|
import com.intellij.psi.codeStyle.CodeStyleSettings;
|
||||||
|
import com.intellij.testFramework.LightIdeaTestCase;
|
||||||
|
import com.intellij.util.IncorrectOperationException;
|
||||||
|
import org.jetbrains.annotations.NonNls;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.kotlin.idea.KotlinLanguage;
|
||||||
|
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase;
|
||||||
|
import org.jetbrains.kotlin.test.InTextDirectivesUtils;
|
||||||
|
import org.jetbrains.kotlin.test.KotlinTestUtils;
|
||||||
|
import org.jetbrains.kotlin.test.SettingsConfigurator;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.EnumMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
// Based on from com.intellij.psi.formatter.java.AbstractJavaFormatterTest
|
||||||
|
@SuppressWarnings("UnusedDeclaration")
|
||||||
|
public abstract class AbstractFormatterTest extends LightIdeaTestCase {
|
||||||
|
|
||||||
|
protected enum Action {REFORMAT, INDENT}
|
||||||
|
|
||||||
|
private interface TestFormatAction {
|
||||||
|
void run(PsiFile psiFile, int startOffset, int endOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Map<Action, TestFormatAction> ACTIONS = new EnumMap<>(Action.class);
|
||||||
|
static {
|
||||||
|
ACTIONS.put(Action.REFORMAT,
|
||||||
|
(psiFile, startOffset, endOffset) -> CodeStyleManager.getInstance(psiFile.getProject()).reformatText(psiFile, startOffset, endOffset));
|
||||||
|
ACTIONS.put(Action.INDENT,
|
||||||
|
(psiFile, startOffset, endOffset) -> CodeStyleManager.getInstance(psiFile.getProject()).adjustLineIndent(psiFile, startOffset));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final String BASE_PATH =
|
||||||
|
new File(PluginTestCaseBase.getTestDataPathBase(), "/formatter/").getAbsolutePath();
|
||||||
|
|
||||||
|
public TextRange myTextRange;
|
||||||
|
public TextRange myLineRange;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void setUp() throws Exception {
|
||||||
|
super.setUp();
|
||||||
|
LanguageLevelProjectExtension.getInstance(getProject()).setLanguageLevel(LanguageLevel.HIGHEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void doTextTest(@NonNls String text, File fileAfter, String extension) throws IncorrectOperationException {
|
||||||
|
doTextTest(Action.REFORMAT, text, fileAfter, extension);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void doTextTest(final Action action, final String text, File fileAfter, String extension) throws IncorrectOperationException {
|
||||||
|
final PsiFile file = createFile("A" + extension, text);
|
||||||
|
|
||||||
|
if (myLineRange != null) {
|
||||||
|
DocumentImpl document = new DocumentImpl(text);
|
||||||
|
myTextRange =
|
||||||
|
new TextRange(document.getLineStartOffset(myLineRange.getStartOffset()), document.getLineEndOffset(myLineRange.getEndOffset()));
|
||||||
|
}
|
||||||
|
|
||||||
|
final PsiDocumentManager manager = PsiDocumentManager.getInstance(getProject());
|
||||||
|
final Document document = manager.getDocument(file);
|
||||||
|
|
||||||
|
CommandProcessor.getInstance().executeCommand(getProject(), new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
ApplicationManager.getApplication().runWriteAction(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
document.replaceString(0, document.getTextLength(), text);
|
||||||
|
manager.commitDocument(document);
|
||||||
|
try {
|
||||||
|
TextRange rangeToUse = myTextRange;
|
||||||
|
if (rangeToUse == null) {
|
||||||
|
rangeToUse = file.getTextRange();
|
||||||
|
}
|
||||||
|
ACTIONS.get(action).run(file, rangeToUse.getStartOffset(), rangeToUse.getEndOffset());
|
||||||
|
}
|
||||||
|
catch (IncorrectOperationException e) {
|
||||||
|
assertTrue(e.getLocalizedMessage(), false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, "", "");
|
||||||
|
|
||||||
|
|
||||||
|
if (document == null) {
|
||||||
|
fail("Don't expect the document to be null");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
KotlinTestUtils.assertEqualsToFile(fileAfter, document.getText());
|
||||||
|
manager.commitDocument(document);
|
||||||
|
KotlinTestUtils.assertEqualsToFile(fileAfter, file.getText());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void doTest(@NotNull String expectedFileNameWithExtension) throws Exception {
|
||||||
|
doTest(expectedFileNameWithExtension, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void doTestInverted(@NotNull String expectedFileNameWithExtension) throws Exception {
|
||||||
|
doTest(expectedFileNameWithExtension, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void doTest(@NotNull String expectedFileNameWithExtension, boolean inverted) throws Exception {
|
||||||
|
String testFileName = expectedFileNameWithExtension.substring(0, expectedFileNameWithExtension.indexOf("."));
|
||||||
|
String testFileExtension = expectedFileNameWithExtension.substring(expectedFileNameWithExtension.lastIndexOf("."));
|
||||||
|
String originalFileText = FileUtil.loadFile(new File(testFileName + testFileExtension), true);
|
||||||
|
|
||||||
|
CodeStyleSettings codeStyleSettings = FormatSettingsUtil.getSettings(getProject());
|
||||||
|
try {
|
||||||
|
Integer rightMargin = InTextDirectivesUtils.getPrefixedInt(originalFileText, "// RIGHT_MARGIN: ");
|
||||||
|
if (rightMargin != null) {
|
||||||
|
codeStyleSettings.setRightMargin(KotlinLanguage.INSTANCE, rightMargin);
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingsConfigurator configurator = FormatSettingsUtil.createConfigurator(originalFileText, codeStyleSettings);
|
||||||
|
if (!inverted) {
|
||||||
|
configurator.configureSettings();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
configurator.configureInvertedSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
doTextTest(originalFileText, new File(expectedFileNameWithExtension), testFileExtension);
|
||||||
|
} finally {
|
||||||
|
codeStyleSettings.clearCodeStyleSettings();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.formatter;
|
||||||
|
|
||||||
|
import com.intellij.application.options.CodeStyle;
|
||||||
|
import com.intellij.openapi.editor.CaretModel;
|
||||||
|
import com.intellij.openapi.util.io.FileUtil;
|
||||||
|
import com.intellij.psi.codeStyle.CodeStyleSettings;
|
||||||
|
import com.intellij.testFramework.EditorTestUtil;
|
||||||
|
import com.intellij.testFramework.LightCodeInsightTestCase;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.kotlin.test.KotlinTestUtils;
|
||||||
|
import org.jetbrains.kotlin.test.SettingsConfigurator;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
public abstract class AbstractTypingIndentationTestBase extends LightCodeInsightTestCase {
|
||||||
|
public void doNewlineTest(String afterFilePath) throws Exception {
|
||||||
|
doNewlineTest(afterFilePath, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void doNewlineTestWithInvert(String afterInvFilePath) throws Exception {
|
||||||
|
doNewlineTest(afterInvFilePath, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void doNewlineTest(String afterFilePath, boolean inverted) throws Exception {
|
||||||
|
String testFileName = afterFilePath.substring(0, afterFilePath.indexOf("."));
|
||||||
|
String testFileExtension = afterFilePath.substring(afterFilePath.lastIndexOf("."));
|
||||||
|
|
||||||
|
String originFilePath = testFileName + testFileExtension;
|
||||||
|
String originalFileText = FileUtil.loadFile(new File(originFilePath), true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
SettingsConfigurator configurator = FormatSettingsUtil.createConfigurator(originalFileText, getProject());
|
||||||
|
if (!inverted) {
|
||||||
|
configurator.configureSettings();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
configurator.configureInvertedSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
doNewlineTest(originFilePath, afterFilePath);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
CodeStyle.getSettings(getProject()).clearCodeStyleSettings();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doNewlineTest(String beforeFilePath, String afterFilePath) {
|
||||||
|
configureByFile(beforeFilePath);
|
||||||
|
type('\n');
|
||||||
|
|
||||||
|
CaretModel caretModel = getEditor().getCaretModel();
|
||||||
|
int offset = caretModel.getOffset();
|
||||||
|
String actualTextWithCaret = new StringBuilder(getEditor().getDocument().getText()).insert(offset, EditorTestUtil.CARET_TAG).toString();
|
||||||
|
|
||||||
|
KotlinTestUtils.assertEqualsToFile(new File(afterFilePath), actualTextWithCaret);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
protected String getTestDataPath() {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.actions
|
||||||
|
|
||||||
|
import com.intellij.ide.actions.CopyReferenceAction
|
||||||
|
import com.intellij.psi.PsiElement
|
||||||
|
import com.intellij.testFramework.LightCodeInsightTestCase
|
||||||
|
import org.jetbrains.kotlin.psi.KtNamedDeclaration
|
||||||
|
import org.jetbrains.kotlin.psi.KtVisitorVoid
|
||||||
|
import org.jetbrains.kotlin.test.JUnit3WithIdeaConfigurationRunner
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
@RunWith(JUnit3WithIdeaConfigurationRunner::class)
|
||||||
|
class QualifiedNamesTest : LightCodeInsightTestCase() {
|
||||||
|
fun testClassRef() {
|
||||||
|
configureFromFileText(
|
||||||
|
"class.kt",
|
||||||
|
"""
|
||||||
|
package foo.bar
|
||||||
|
|
||||||
|
class Klass {
|
||||||
|
class Nested
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object Object {
|
||||||
|
}
|
||||||
|
|
||||||
|
val anonymous = object {
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
assertEquals(listOf("foo.bar.Klass", "foo.bar.Klass.Nested", "foo.bar.Klass.Companion", "foo.bar.Object", "foo.bar.ClassKt#getAnonymous", null),
|
||||||
|
getQualifiedNamesForDeclarations())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testFunRef() {
|
||||||
|
configureFromFileText(
|
||||||
|
"fun.kt",
|
||||||
|
"""
|
||||||
|
package foo.bar
|
||||||
|
|
||||||
|
class Klass {
|
||||||
|
fun memberFun() {
|
||||||
|
}
|
||||||
|
|
||||||
|
val memberVal = ":)"
|
||||||
|
}
|
||||||
|
|
||||||
|
fun topLevelFun()
|
||||||
|
|
||||||
|
val topLevelVal = ":)"
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
assertEquals(listOf("foo.bar.Klass", "foo.bar.Klass#memberFun", "foo.bar.Klass#getMemberVal", "foo.bar.FunKt#topLevelFun", "foo.bar.FunKt#getTopLevelVal"),
|
||||||
|
getQualifiedNamesForDeclarations())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getQualifiedNamesForDeclarations(): List<String?> {
|
||||||
|
val result = ArrayList<String?>()
|
||||||
|
file.accept(object : KtVisitorVoid() {
|
||||||
|
override fun visitElement(element: PsiElement) {
|
||||||
|
element.acceptChildren(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitNamedDeclaration(declaration: KtNamedDeclaration) {
|
||||||
|
result.add(CopyReferenceAction.elementToFqn(declaration))
|
||||||
|
super.visitNamedDeclaration(declaration)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
+127
@@ -0,0 +1,127 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.codeInsight.moveUpDown
|
||||||
|
|
||||||
|
import com.intellij.codeInsight.editorActions.moveLeftRight.MoveElementLeftAction
|
||||||
|
import com.intellij.codeInsight.editorActions.moveLeftRight.MoveElementRightAction
|
||||||
|
import com.intellij.codeInsight.editorActions.moveUpDown.MoveStatementDownAction
|
||||||
|
import com.intellij.codeInsight.editorActions.moveUpDown.MoveStatementUpAction
|
||||||
|
import com.intellij.codeInsight.editorActions.moveUpDown.StatementUpDownMover
|
||||||
|
import com.intellij.openapi.application.ApplicationManager
|
||||||
|
import com.intellij.openapi.application.runWriteAction
|
||||||
|
import com.intellij.openapi.editor.actionSystem.EditorAction
|
||||||
|
import com.intellij.openapi.extensions.Extensions
|
||||||
|
import com.intellij.openapi.util.io.FileUtil
|
||||||
|
import com.intellij.testFramework.LightCodeInsightTestCase
|
||||||
|
import com.intellij.testFramework.LightPlatformCodeInsightTestCase
|
||||||
|
import junit.framework.ComparisonFailure
|
||||||
|
import junit.framework.TestCase
|
||||||
|
import org.jetbrains.kotlin.formatter.FormatSettingsUtil
|
||||||
|
import org.jetbrains.kotlin.idea.codeInsight.upDownMover.KotlinDeclarationMover
|
||||||
|
import org.jetbrains.kotlin.idea.codeInsight.upDownMover.KotlinExpressionMover
|
||||||
|
import org.jetbrains.kotlin.idea.core.script.isScriptDependenciesUpdaterDisabled
|
||||||
|
import org.jetbrains.kotlin.test.InTextDirectivesUtils
|
||||||
|
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
abstract class AbstractMoveStatementTest : AbstractCodeMoverTest() {
|
||||||
|
protected fun doTestClassBodyDeclaration(path: String) {
|
||||||
|
doTest(path, KotlinDeclarationMover::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun doTestExpression(path: String) {
|
||||||
|
doTest(path, KotlinExpressionMover::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun doTest(path: String, defaultMoverClass: Class<out StatementUpDownMover>) {
|
||||||
|
doTest(path) { isApplicableExpected, direction ->
|
||||||
|
val movers = Extensions.getExtensions(StatementUpDownMover.STATEMENT_UP_DOWN_MOVER_EP)
|
||||||
|
val info = StatementUpDownMover.MoveInfo()
|
||||||
|
val actualMover = movers.firstOrNull {
|
||||||
|
it.checkAvailable(editor, file, info, direction == "down")
|
||||||
|
} ?: error("No mover found")
|
||||||
|
|
||||||
|
assertEquals("Unmatched movers", defaultMoverClass.name, actualMover::class.java.name)
|
||||||
|
assertEquals("Invalid applicability", isApplicableExpected, info.toMove2 != null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class AbstractMoveLeftRightTest : AbstractCodeMoverTest() {
|
||||||
|
protected fun doTest(path: String) {
|
||||||
|
doTest(path) { _, _ -> }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class AbstractCodeMoverTest : LightCodeInsightTestCase() {
|
||||||
|
override fun setUp() {
|
||||||
|
super.setUp()
|
||||||
|
ApplicationManager.getApplication().isScriptDependenciesUpdaterDisabled = true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun tearDown() {
|
||||||
|
ApplicationManager.getApplication().isScriptDependenciesUpdaterDisabled = false
|
||||||
|
super.tearDown()
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun doTest(path: String, isApplicableChecker: (isApplicableExpected: Boolean, direction: String) -> Unit) {
|
||||||
|
configureByFile(path)
|
||||||
|
|
||||||
|
val fileText = FileUtil.loadFile(File(path), true)
|
||||||
|
val direction = InTextDirectivesUtils.findStringWithPrefixes(fileText, "// MOVE: ")
|
||||||
|
?: error("No MOVE directive found")
|
||||||
|
|
||||||
|
val action = when (direction) {
|
||||||
|
"up" -> MoveStatementUpAction()
|
||||||
|
"down" -> MoveStatementDownAction()
|
||||||
|
"left" -> MoveElementLeftAction()
|
||||||
|
"right" -> MoveElementRightAction()
|
||||||
|
else -> error("Unknown direction: $direction")
|
||||||
|
}
|
||||||
|
|
||||||
|
val isApplicableString = InTextDirectivesUtils.findStringWithPrefixes(fileText, "// IS_APPLICABLE: ")
|
||||||
|
val isApplicableExpected = isApplicableString == null || isApplicableString == "true"
|
||||||
|
|
||||||
|
isApplicableChecker(isApplicableExpected, direction)
|
||||||
|
|
||||||
|
invokeAndCheck(fileText, path, action, isApplicableExpected)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun invokeAndCheck(fileText: String, path: String, action: EditorAction, isApplicableExpected: Boolean) {
|
||||||
|
val project = editor.project!!
|
||||||
|
|
||||||
|
val codeStyleSettings = FormatSettingsUtil.getSettings(project)
|
||||||
|
val configurator = FormatSettingsUtil.createConfigurator(fileText, codeStyleSettings)
|
||||||
|
configurator.configureSettings()
|
||||||
|
|
||||||
|
try {
|
||||||
|
val dataContext = currentEditorDataContext
|
||||||
|
|
||||||
|
val before = editor.document.text
|
||||||
|
runWriteAction { action.actionPerformed(editor, dataContext) }
|
||||||
|
|
||||||
|
val after = editor.document.text
|
||||||
|
val actionDoesNothing = after == before
|
||||||
|
|
||||||
|
TestCase.assertEquals(isApplicableExpected, !actionDoesNothing)
|
||||||
|
|
||||||
|
if (isApplicableExpected) {
|
||||||
|
val afterFilePath = path + ".after"
|
||||||
|
try {
|
||||||
|
checkResultByFile(afterFilePath)
|
||||||
|
}
|
||||||
|
catch (e: ComparisonFailure) {
|
||||||
|
KotlinTestUtils.assertEqualsToFile(File(afterFilePath), editor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
codeStyleSettings.clearCodeStyleSettings()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getTestDataPath() = ""
|
||||||
|
}
|
||||||
+75
@@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
package org.jetbrains.kotlin.idea.debugger.sequence
|
||||||
|
|
||||||
|
import com.intellij.debugger.streams.test.StreamChainBuilderTestCase
|
||||||
|
import com.intellij.debugger.streams.wrapper.StreamChain
|
||||||
|
import com.intellij.debugger.streams.wrapper.StreamChainBuilder
|
||||||
|
import com.intellij.openapi.application.ApplicationManager
|
||||||
|
import com.intellij.openapi.projectRoots.Sdk
|
||||||
|
import com.intellij.openapi.roots.impl.libraries.ProjectLibraryTable
|
||||||
|
import com.intellij.testFramework.LightPlatformTestCase
|
||||||
|
import com.intellij.testFramework.PsiTestUtil
|
||||||
|
import junit.framework.TestCase
|
||||||
|
import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime
|
||||||
|
import org.jetbrains.kotlin.idea.caches.project.LibraryModificationTracker
|
||||||
|
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase
|
||||||
|
import java.io.File
|
||||||
|
import java.nio.file.Paths
|
||||||
|
|
||||||
|
abstract class KotlinPsiChainBuilderTestCase(private val relativePath: String) : StreamChainBuilderTestCase() {
|
||||||
|
override fun getTestDataPath(): String =
|
||||||
|
Paths.get(File("").absolutePath, "idea/testData/debugger/sequence/psi/$relativeTestPath/").toString()
|
||||||
|
|
||||||
|
override fun getFileExtension(): String = ".kt"
|
||||||
|
abstract val kotlinChainBuilder: StreamChainBuilder
|
||||||
|
override fun getChainBuilder(): StreamChainBuilder = kotlinChainBuilder
|
||||||
|
private val stdLibName = "kotlin-stdlib"
|
||||||
|
|
||||||
|
protected abstract fun doTest()
|
||||||
|
|
||||||
|
final override fun getRelativeTestPath(): String = relativePath
|
||||||
|
|
||||||
|
override fun setUp() {
|
||||||
|
super.setUp()
|
||||||
|
ApplicationManager.getApplication().runWriteAction {
|
||||||
|
if (ProjectLibraryTable.getInstance(project).getLibraryByName(stdLibName) == null) {
|
||||||
|
val stdLibPath = ForTestCompileRuntime.runtimeJarForTests()
|
||||||
|
PsiTestUtil.addLibrary(
|
||||||
|
testRootDisposable,
|
||||||
|
module,
|
||||||
|
stdLibName,
|
||||||
|
stdLibPath.parent,
|
||||||
|
stdLibPath.name
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LibraryModificationTracker.getInstance(project).incModificationCount()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override fun getProjectJDK(): Sdk {
|
||||||
|
return PluginTestCaseBase.mockJdk9()
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class Positive(relativePath: String) : KotlinPsiChainBuilderTestCase(relativePath) {
|
||||||
|
override fun doTest() {
|
||||||
|
val chains = buildChains()
|
||||||
|
checkChains(chains)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun checkChains(chains: MutableList<StreamChain>) {
|
||||||
|
TestCase.assertFalse(chains.isEmpty())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class Negative(relativePath: String) : KotlinPsiChainBuilderTestCase(relativePath) {
|
||||||
|
override fun doTest() {
|
||||||
|
val elementAtCaret = configureAndGetElementAtCaret()
|
||||||
|
TestCase.assertFalse(chainBuilder.isChainExists(elementAtCaret))
|
||||||
|
TestCase.assertTrue(chainBuilder.build(elementAtCaret).isEmpty())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.editor;
|
||||||
|
|
||||||
|
import com.intellij.openapi.util.io.FileUtil;
|
||||||
|
import com.intellij.psi.codeStyle.CodeStyleSettings;
|
||||||
|
import com.intellij.testFramework.EditorTestUtil;
|
||||||
|
import com.intellij.testFramework.LightCodeInsightTestCase;
|
||||||
|
import org.jetbrains.kotlin.formatter.FormatSettingsUtil;
|
||||||
|
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase;
|
||||||
|
import org.jetbrains.kotlin.test.JUnit3WithIdeaConfigurationRunner;
|
||||||
|
import org.jetbrains.kotlin.test.KotlinTestUtils;
|
||||||
|
import org.jetbrains.kotlin.test.SettingsConfigurator;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
@RunWith(JUnit3WithIdeaConfigurationRunner.class)
|
||||||
|
public class KotlinCommenterTest extends LightCodeInsightTestCase {
|
||||||
|
private static final String BASE_PATH =
|
||||||
|
new File(PluginTestCaseBase.getTestDataPathBase(), "/editor/commenter/").getAbsolutePath();
|
||||||
|
|
||||||
|
public void testGenerateDocComment() throws Exception {
|
||||||
|
doNewLineTypingTest();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testNewLineInComment() throws Exception {
|
||||||
|
doNewLineTypingTest();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testNewLineInTag() throws Exception {
|
||||||
|
doNewLineTypingTest();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testNotFirstColumnWithSpace() throws Exception {
|
||||||
|
doLineCommentTest();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testNotFirstColumnWithoutSpace() throws Exception {
|
||||||
|
doLineCommentTest();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doNewLineTypingTest() throws Exception {
|
||||||
|
configure();
|
||||||
|
EditorTestUtil.performTypingAction(getEditor(), '\n');
|
||||||
|
check();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doLineCommentTest() throws Exception {
|
||||||
|
configure();
|
||||||
|
|
||||||
|
CodeStyleSettings codeStyleSettings = FormatSettingsUtil.getSettings(getProject());
|
||||||
|
try {
|
||||||
|
String text = getFile().getText();
|
||||||
|
|
||||||
|
SettingsConfigurator configurator = FormatSettingsUtil.createConfigurator(text, codeStyleSettings);
|
||||||
|
configurator.configureSettings();
|
||||||
|
|
||||||
|
executeAction("CommentByLineComment");
|
||||||
|
} finally {
|
||||||
|
codeStyleSettings.clearCodeStyleSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
check();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void configure() throws Exception {
|
||||||
|
configureFromFileText("a.kt", loadFile(getTestName(true) + ".kt"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void check() {
|
||||||
|
File afterFile = getTestFile(getTestName(true) + "_after.kt");
|
||||||
|
KotlinTestUtils.assertEqualsToFile(afterFile, getEditor(), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static File getTestFile(String name) {
|
||||||
|
return new File(BASE_PATH, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String loadFile(String name) throws Exception {
|
||||||
|
File file = getTestFile(name);
|
||||||
|
return FileUtil.loadFile(file, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,935 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.editor
|
||||||
|
|
||||||
|
import com.intellij.application.options.CodeStyle
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.testFramework.EditorTestUtil
|
||||||
|
import com.intellij.testFramework.LightCodeInsightTestCase
|
||||||
|
import com.intellij.testFramework.LightPlatformCodeInsightTestCase
|
||||||
|
import com.intellij.testFramework.LightPlatformTestCase
|
||||||
|
import org.jetbrains.kotlin.idea.KotlinFileType
|
||||||
|
import org.jetbrains.kotlin.idea.formatter.KotlinStyleGuideCodeStyle
|
||||||
|
import org.jetbrains.kotlin.idea.formatter.ktCodeStyleSettings
|
||||||
|
import org.jetbrains.kotlin.test.JUnit3WithIdeaConfigurationRunner
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
|
||||||
|
@RunWith(JUnit3WithIdeaConfigurationRunner::class)
|
||||||
|
class TypedHandlerTest : LightCodeInsightTestCase() {
|
||||||
|
private val dollar = '$'
|
||||||
|
|
||||||
|
fun testTypeStringTemplateStart() = doTypeTest(
|
||||||
|
'{',
|
||||||
|
"""val x = "$<caret>" """,
|
||||||
|
"""val x = "$dollar{}" """
|
||||||
|
)
|
||||||
|
|
||||||
|
fun testAutoIndentRightOpenBrace() = doTypeTest(
|
||||||
|
'{',
|
||||||
|
|
||||||
|
"fun test() {\n" +
|
||||||
|
"<caret>\n" +
|
||||||
|
"}",
|
||||||
|
|
||||||
|
"fun test() {\n" +
|
||||||
|
" {<caret>}\n" +
|
||||||
|
"}"
|
||||||
|
)
|
||||||
|
|
||||||
|
fun testAutoIndentLeftOpenBrace() = doTypeTest(
|
||||||
|
'{',
|
||||||
|
|
||||||
|
"fun test() {\n" +
|
||||||
|
" <caret>\n" +
|
||||||
|
"}",
|
||||||
|
|
||||||
|
"fun test() {\n" +
|
||||||
|
" {<caret>}\n" +
|
||||||
|
"}"
|
||||||
|
)
|
||||||
|
|
||||||
|
fun testTypeStringTemplateStartWithCloseBraceAfter() = doTypeTest(
|
||||||
|
'{',
|
||||||
|
"""fun foo() { "$<caret>" }""",
|
||||||
|
"""fun foo() { "$dollar{}" }"""
|
||||||
|
)
|
||||||
|
|
||||||
|
fun testTypeStringTemplateStartBeforeStringWithExistingDollar() = doTypeTest(
|
||||||
|
'{',
|
||||||
|
"""fun foo() { "$<caret>something" }""",
|
||||||
|
"""fun foo() { "$dollar{something" }"""
|
||||||
|
)
|
||||||
|
|
||||||
|
fun testTypeStringTemplateStartBeforeStringWithNoDollar() = doTypeTest(
|
||||||
|
"$dollar{",
|
||||||
|
"""fun foo() { "<caret>something" }""",
|
||||||
|
"""fun foo() { "$dollar{<caret>}something" }"""
|
||||||
|
)
|
||||||
|
|
||||||
|
fun testTypeStringTemplateWithUnmatchedBrace() = doTypeTest(
|
||||||
|
"$dollar{",
|
||||||
|
"""val a = "<caret>bar}foo"""",
|
||||||
|
"""val a = "$dollar{<caret>bar}foo""""
|
||||||
|
)
|
||||||
|
|
||||||
|
fun testTypeStringTemplateWithUnmatchedBraceComplex() = doTypeTest(
|
||||||
|
"$dollar{",
|
||||||
|
"""val a = "<caret>bar + more}foo"""",
|
||||||
|
"""val a = "$dollar{<caret>}bar + more}foo""""
|
||||||
|
)
|
||||||
|
|
||||||
|
fun testTypeStringTemplateStartInStringWithBraceLiterals() = doTypeTest(
|
||||||
|
"$dollar{",
|
||||||
|
"""val test = "{ code <caret>other }"""",
|
||||||
|
"""val test = "{ code $dollar{<caret>}other }""""
|
||||||
|
)
|
||||||
|
|
||||||
|
fun testTypeStringTemplateStartInEmptyString() = doTypeTest(
|
||||||
|
'{',
|
||||||
|
"""fun foo() { "$<caret>" }""",
|
||||||
|
"""fun foo() { "$dollar{<caret>}" }"""
|
||||||
|
)
|
||||||
|
|
||||||
|
fun testKT3575() = doTypeTest(
|
||||||
|
'{',
|
||||||
|
"""val x = "$<caret>]" """,
|
||||||
|
"""val x = "$dollar{}]" """
|
||||||
|
)
|
||||||
|
|
||||||
|
fun testAutoCloseRawStringInEnd() = doTypeTest(
|
||||||
|
'"',
|
||||||
|
"""val x = ""<caret>""",
|
||||||
|
"""val x = ""${'"'}<caret>""${'"'}"""
|
||||||
|
)
|
||||||
|
|
||||||
|
fun testNoAutoCloseRawStringInEnd() = doTypeTest(
|
||||||
|
'"',
|
||||||
|
"""val x = ""${'"'}<caret>""",
|
||||||
|
"""val x = ""${'"'}""""
|
||||||
|
)
|
||||||
|
|
||||||
|
fun testAutoCloseRawStringInMiddle() = doTypeTest(
|
||||||
|
'"',
|
||||||
|
"""
|
||||||
|
val x = ""<caret>
|
||||||
|
val y = 12
|
||||||
|
""".trimIndent(),
|
||||||
|
"""
|
||||||
|
val x = ""${'"'}<caret>""${'"'}
|
||||||
|
val y = 12
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
|
||||||
|
fun testNoAutoCloseBetweenMultiQuotes() = doTypeTest(
|
||||||
|
'"',
|
||||||
|
"""val x = ""${'"'}<caret>${'"'}""/**/""",
|
||||||
|
"""val x = ""${'"'}${'"'}<caret>""/**/"""
|
||||||
|
)
|
||||||
|
|
||||||
|
fun testNoAutoCloseBetweenMultiQuotes1() = doTypeTest(
|
||||||
|
'"',
|
||||||
|
"""val x = ""${'"'}"<caret>"${'"'}/**/""",
|
||||||
|
"""val x = ""${'"'}""<caret>${'"'}/**/"""
|
||||||
|
)
|
||||||
|
|
||||||
|
fun testNoAutoCloseAfterEscape() = doTypeTest(
|
||||||
|
'"',
|
||||||
|
"""val x = "\""<caret>""",
|
||||||
|
"""val x = "\""${'"'}<caret>""""
|
||||||
|
)
|
||||||
|
|
||||||
|
fun testAutoCloseBraceInFunctionDeclaration() = doTypeTest(
|
||||||
|
'{',
|
||||||
|
"fun foo() <caret>",
|
||||||
|
"fun foo() {<caret>}"
|
||||||
|
)
|
||||||
|
|
||||||
|
fun testAutoCloseBraceInLocalFunctionDeclaration() = doTypeTest(
|
||||||
|
'{',
|
||||||
|
|
||||||
|
"fun foo() {\n" +
|
||||||
|
" fun bar() <caret>\n" +
|
||||||
|
"}",
|
||||||
|
|
||||||
|
"fun foo() {\n" +
|
||||||
|
" fun bar() {<caret>}\n" +
|
||||||
|
"}"
|
||||||
|
)
|
||||||
|
|
||||||
|
fun testAutoCloseBraceInAssignment() = doTypeTest(
|
||||||
|
'{',
|
||||||
|
"fun foo() {\n" +
|
||||||
|
" val a = <caret>\n" +
|
||||||
|
"}",
|
||||||
|
|
||||||
|
"fun foo() {\n" +
|
||||||
|
" val a = {<caret>}\n" +
|
||||||
|
"}"
|
||||||
|
)
|
||||||
|
|
||||||
|
fun testDoNotAutoCloseBraceInUnfinishedIfSurroundOnSameLine() = doTypeTest(
|
||||||
|
'{',
|
||||||
|
|
||||||
|
"fun foo() {\n" +
|
||||||
|
" if() <caret>foo()\n" +
|
||||||
|
"}",
|
||||||
|
|
||||||
|
"fun foo() {\n" +
|
||||||
|
" if() {foo()\n" +
|
||||||
|
"}"
|
||||||
|
)
|
||||||
|
|
||||||
|
fun testDoNotAutoCloseBraceInUnfinishedElseSurroundOnSameLine() = doTypeTest(
|
||||||
|
'{',
|
||||||
|
|
||||||
|
"fun foo() {\n" +
|
||||||
|
" if(true) {} else <caret>foo()\n" +
|
||||||
|
"}",
|
||||||
|
|
||||||
|
"fun foo() {\n" +
|
||||||
|
" if(true) {} else {foo()\n" +
|
||||||
|
"}"
|
||||||
|
)
|
||||||
|
|
||||||
|
fun testDoNotAutoCloseBraceInUnfinishedTryOnSameLine() = doTypeTest(
|
||||||
|
'{',
|
||||||
|
|
||||||
|
"fun foo() {\n" +
|
||||||
|
" try <caret>foo()\n" +
|
||||||
|
"}",
|
||||||
|
|
||||||
|
"fun foo() {\n" +
|
||||||
|
" try {foo()\n" +
|
||||||
|
"}"
|
||||||
|
)
|
||||||
|
|
||||||
|
fun testDoNotAutoCloseBraceInUnfinishedCatchOnSameLine() = doTypeTest(
|
||||||
|
'{',
|
||||||
|
|
||||||
|
"fun foo() {\n" +
|
||||||
|
" try {} catch (e: Exception) <caret>foo()\n" +
|
||||||
|
"}",
|
||||||
|
|
||||||
|
"fun foo() {\n" +
|
||||||
|
" try {} catch (e: Exception) {foo()\n" +
|
||||||
|
"}"
|
||||||
|
)
|
||||||
|
|
||||||
|
fun testDoNotAutoCloseBraceInUnfinishedFinallyOnSameLine() = doTypeTest(
|
||||||
|
'{',
|
||||||
|
|
||||||
|
"fun foo() {\n" +
|
||||||
|
" try {} catch (e: Exception) finally <caret>foo()\n" +
|
||||||
|
"}",
|
||||||
|
|
||||||
|
"fun foo() {\n" +
|
||||||
|
" try {} catch (e: Exception) finally {foo()\n" +
|
||||||
|
"}"
|
||||||
|
)
|
||||||
|
|
||||||
|
fun testDoNotAutoCloseBraceInUnfinishedWhileSurroundOnSameLine() = doTypeTest(
|
||||||
|
'{',
|
||||||
|
|
||||||
|
"fun foo() {\n" +
|
||||||
|
" while() <caret>foo()\n" +
|
||||||
|
"}",
|
||||||
|
|
||||||
|
"fun foo() {\n" +
|
||||||
|
" while() {foo()\n" +
|
||||||
|
"}"
|
||||||
|
)
|
||||||
|
|
||||||
|
fun testDoNotAutoCloseBraceInUnfinishedWhileSurroundOnNewLine() = doTypeTest(
|
||||||
|
'{',
|
||||||
|
|
||||||
|
"fun foo() {\n" +
|
||||||
|
" while()\n" +
|
||||||
|
"<caret>\n" +
|
||||||
|
" foo()\n" +
|
||||||
|
"}",
|
||||||
|
|
||||||
|
"fun foo() {\n" +
|
||||||
|
" while()\n" +
|
||||||
|
" {\n" +
|
||||||
|
" foo()\n" +
|
||||||
|
"}"
|
||||||
|
)
|
||||||
|
|
||||||
|
fun testDoNotAutoCloseBraceInUnfinishedIfSurroundOnOtherLine() = doTypeTest(
|
||||||
|
'{',
|
||||||
|
|
||||||
|
"fun foo() {\n" +
|
||||||
|
" if(true) <caret>\n" +
|
||||||
|
" foo()\n" +
|
||||||
|
"}",
|
||||||
|
|
||||||
|
"fun foo() {\n" +
|
||||||
|
" if(true) {<caret>\n" +
|
||||||
|
" foo()\n" +
|
||||||
|
"}"
|
||||||
|
)
|
||||||
|
|
||||||
|
fun testDoNotAutoCloseBraceInUnfinishedElseSurroundOnOtherLine() = doTypeTest(
|
||||||
|
'{',
|
||||||
|
|
||||||
|
"fun foo() {\n" +
|
||||||
|
" if(true) {} else <caret>\n" +
|
||||||
|
" foo()\n" +
|
||||||
|
"}",
|
||||||
|
|
||||||
|
"fun foo() {\n" +
|
||||||
|
" if(true) {} else {<caret>\n" +
|
||||||
|
" foo()\n" +
|
||||||
|
"}"
|
||||||
|
)
|
||||||
|
|
||||||
|
fun testDoNotAutoCloseBraceInUnfinishedTryOnOtherLine() = doTypeTest(
|
||||||
|
'{',
|
||||||
|
|
||||||
|
"fun foo() {\n" +
|
||||||
|
" try <caret>\n" +
|
||||||
|
" foo()\n" +
|
||||||
|
"}",
|
||||||
|
|
||||||
|
"fun foo() {\n" +
|
||||||
|
" try {<caret>\n" +
|
||||||
|
" foo()\n" +
|
||||||
|
"}"
|
||||||
|
)
|
||||||
|
|
||||||
|
fun testDoNotAutoCloseBraceInUnfinishedIfSurroundOnNewLine() = doTypeTest(
|
||||||
|
'{',
|
||||||
|
|
||||||
|
"fun foo() {\n" +
|
||||||
|
" if(true)\n" +
|
||||||
|
" <caret>\n" +
|
||||||
|
" foo()\n" +
|
||||||
|
"}",
|
||||||
|
|
||||||
|
"fun foo() {\n" +
|
||||||
|
" if(true)\n" +
|
||||||
|
" {<caret>\n" +
|
||||||
|
" foo()\n" +
|
||||||
|
"}"
|
||||||
|
)
|
||||||
|
|
||||||
|
fun testDoNotAutoCloseBraceInUnfinishedElseSurroundOnNewLine() = doTypeTest(
|
||||||
|
'{',
|
||||||
|
|
||||||
|
"fun foo() {\n" +
|
||||||
|
" if(true) {} else\n" +
|
||||||
|
" <caret>\n" +
|
||||||
|
" foo()\n" +
|
||||||
|
"}",
|
||||||
|
|
||||||
|
"fun foo() {\n" +
|
||||||
|
" if(true) {} else\n" +
|
||||||
|
" {<caret>\n" +
|
||||||
|
" foo()\n" +
|
||||||
|
"}"
|
||||||
|
)
|
||||||
|
|
||||||
|
fun testDoNotAutoCloseBraceInUnfinishedTryOnNewLine() = doTypeTest(
|
||||||
|
'{',
|
||||||
|
|
||||||
|
"fun foo() {\n" +
|
||||||
|
" try\n" +
|
||||||
|
" <caret>\n" +
|
||||||
|
" foo()\n" +
|
||||||
|
"}",
|
||||||
|
|
||||||
|
"fun foo() {\n" +
|
||||||
|
" try\n" +
|
||||||
|
" {<caret>\n" +
|
||||||
|
" foo()\n" +
|
||||||
|
"}"
|
||||||
|
)
|
||||||
|
|
||||||
|
fun testAutoCloseBraceInsideFor() = doTypeTest(
|
||||||
|
'{',
|
||||||
|
|
||||||
|
"fun foo() {\n" +
|
||||||
|
" for (elem in some.filter <caret>) {\n" +
|
||||||
|
" }\n" +
|
||||||
|
"}",
|
||||||
|
|
||||||
|
"fun foo() {\n" +
|
||||||
|
" for (elem in some.filter {<caret>}) {\n" +
|
||||||
|
" }\n" +
|
||||||
|
"}"
|
||||||
|
)
|
||||||
|
|
||||||
|
fun testAutoCloseBraceInsideForAfterCloseParen() = doTypeTest(
|
||||||
|
'{',
|
||||||
|
|
||||||
|
"fun foo() {\n" +
|
||||||
|
" for (elem in some.foo(true) <caret>) {\n" +
|
||||||
|
" }\n" +
|
||||||
|
"}",
|
||||||
|
|
||||||
|
"fun foo() {\n" +
|
||||||
|
" for (elem in some.foo(true) {<caret>}) {\n" +
|
||||||
|
" }\n" +
|
||||||
|
"}"
|
||||||
|
)
|
||||||
|
|
||||||
|
fun testAutoCloseBraceBeforeIf() = doTypeTest(
|
||||||
|
'{',
|
||||||
|
|
||||||
|
"fun foo() {\n" +
|
||||||
|
" <caret>if (true) {}\n" +
|
||||||
|
"}",
|
||||||
|
|
||||||
|
"fun foo() {\n" +
|
||||||
|
" {<caret>if (true) {}\n" +
|
||||||
|
"}"
|
||||||
|
)
|
||||||
|
|
||||||
|
fun testAutoCloseBraceInIfCondition() = doTypeTest(
|
||||||
|
'{',
|
||||||
|
|
||||||
|
"fun foo() {\n" +
|
||||||
|
" if (some.hello (12) <caret>)\n" +
|
||||||
|
"}",
|
||||||
|
|
||||||
|
"fun foo() {\n" +
|
||||||
|
" if (some.hello (12) {<caret>})\n" +
|
||||||
|
"}"
|
||||||
|
)
|
||||||
|
|
||||||
|
fun testInsertSpaceAfterRightBraceOfNestedLambda() = doTypeTest(
|
||||||
|
'{',
|
||||||
|
"val t = Array(100) { Array(200) <caret>}",
|
||||||
|
"val t = Array(100) { Array(200) {<caret>} }"
|
||||||
|
)
|
||||||
|
|
||||||
|
fun testAutoInsertParenInStringLiteral() = doTypeTest(
|
||||||
|
'(',
|
||||||
|
"""fun f() { println("$dollar{f<caret>}") }""",
|
||||||
|
"""fun f() { println("$dollar{f(<caret>)}") }"""
|
||||||
|
)
|
||||||
|
|
||||||
|
fun testAutoInsertParenInCode() = doTypeTest(
|
||||||
|
'(',
|
||||||
|
"""fun f() { val a = f<caret> }""",
|
||||||
|
"""fun f() { val a = f(<caret>) }"""
|
||||||
|
)
|
||||||
|
|
||||||
|
fun testSplitStringByEnter() = doTypeTest(
|
||||||
|
'\n',
|
||||||
|
"""val s = "foo<caret>bar"""",
|
||||||
|
"val s = \"foo\" +\n" +
|
||||||
|
" \"bar\""
|
||||||
|
)
|
||||||
|
|
||||||
|
fun testSplitStringByEnterEmpty() = doTypeTest(
|
||||||
|
'\n',
|
||||||
|
"""val s = "<caret>"""",
|
||||||
|
"val s = \"\" +\n" +
|
||||||
|
" \"\""
|
||||||
|
)
|
||||||
|
|
||||||
|
fun testSplitStringByEnterBeforeEscapeSequence() = doTypeTest(
|
||||||
|
'\n',
|
||||||
|
"""val s = "foo<caret>\nbar"""",
|
||||||
|
"val s = \"foo\" +\n" +
|
||||||
|
" \"\\nbar\""
|
||||||
|
)
|
||||||
|
|
||||||
|
fun testSplitStringByEnterBeforeSubstitution() = doTypeTest(
|
||||||
|
'\n',
|
||||||
|
"""val s = "foo<caret>${dollar}bar"""",
|
||||||
|
"val s = \"foo\" +\n" +
|
||||||
|
" \"${dollar}bar\""
|
||||||
|
)
|
||||||
|
|
||||||
|
fun testSplitStringByEnterAddParentheses() = doTypeTest(
|
||||||
|
'\n',
|
||||||
|
"""val l = "foo<caret>bar".length()""",
|
||||||
|
"val l = (\"foo\" +\n" +
|
||||||
|
" \"bar\").length()"
|
||||||
|
)
|
||||||
|
|
||||||
|
fun testSplitStringByEnterExistingParentheses() = doTypeTest(
|
||||||
|
'\n',
|
||||||
|
"""val l = ("asdf" + "foo<caret>bar").length()""",
|
||||||
|
"val l = (\"asdf\" + \"foo\" +\n" +
|
||||||
|
" \"bar\").length()"
|
||||||
|
)
|
||||||
|
|
||||||
|
fun testTypeLtInFunDeclaration() {
|
||||||
|
doLtGtTest("fun <caret>")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testTypeLtInOngoingConstructorCall() {
|
||||||
|
doLtGtTest("fun test() { Collection<caret> }")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testTypeLtInClassDeclaration() {
|
||||||
|
doLtGtTest("class Some<caret> {}")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testTypeLtInPropertyType() {
|
||||||
|
doLtGtTest("val a: List<caret> ")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testTypeLtInExtensionFunctionReceiver() {
|
||||||
|
doLtGtTest("fun <T> Collection<caret> ")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testTypeLtInFunParam() {
|
||||||
|
doLtGtTest("fun some(a : HashSet<caret>)")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testTypeLtInFun() {
|
||||||
|
doLtGtTestNoAutoClose("fun some() { <<caret> }")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testTypeLtInLess() {
|
||||||
|
doLtGtTestNoAutoClose("fun some() { val a = 12; a <<caret> }")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testColonOfSuperTypeList() {
|
||||||
|
doTypeTest(
|
||||||
|
':',
|
||||||
|
"""
|
||||||
|
|open class A
|
||||||
|
|class B
|
||||||
|
|<caret>
|
||||||
|
""",
|
||||||
|
"""
|
||||||
|
|open class A
|
||||||
|
|class B
|
||||||
|
| :<caret>
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testColonOfSuperTypeListInObject() {
|
||||||
|
doTypeTest(
|
||||||
|
':',
|
||||||
|
"""
|
||||||
|
|interface A
|
||||||
|
|object B
|
||||||
|
|<caret>
|
||||||
|
""",
|
||||||
|
"""
|
||||||
|
|interface A
|
||||||
|
|object B
|
||||||
|
| :<caret>
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testColonOfSuperTypeListInCompanionObject() {
|
||||||
|
doTypeTest(
|
||||||
|
':',
|
||||||
|
"""
|
||||||
|
|interface A
|
||||||
|
|class B {
|
||||||
|
| companion object
|
||||||
|
| <caret>
|
||||||
|
|}
|
||||||
|
""",
|
||||||
|
"""
|
||||||
|
|interface A
|
||||||
|
|class B {
|
||||||
|
| companion object
|
||||||
|
| :<caret>
|
||||||
|
|}
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testColonOfSuperTypeListBeforeBody() {
|
||||||
|
doTypeTest(
|
||||||
|
':',
|
||||||
|
"""
|
||||||
|
|open class A
|
||||||
|
|class B
|
||||||
|
|<caret> {
|
||||||
|
|}
|
||||||
|
""",
|
||||||
|
"""
|
||||||
|
|open class A
|
||||||
|
|class B
|
||||||
|
| :<caret> {
|
||||||
|
|}
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testColonOfSuperTypeListNotNullIndent() {
|
||||||
|
doTypeTest(
|
||||||
|
':',
|
||||||
|
"""
|
||||||
|
|fun test() {
|
||||||
|
| open class A
|
||||||
|
| class B
|
||||||
|
| <caret>
|
||||||
|
|}
|
||||||
|
""",
|
||||||
|
"""
|
||||||
|
|fun test() {
|
||||||
|
| open class A
|
||||||
|
| class B
|
||||||
|
| :<caret>
|
||||||
|
|}
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testChainCallContinueWithDot() {
|
||||||
|
doTypeTest(
|
||||||
|
'.',
|
||||||
|
"""
|
||||||
|
|class Test{ fun test() = this }
|
||||||
|
|fun some() {
|
||||||
|
| Test()
|
||||||
|
| <caret>
|
||||||
|
|}
|
||||||
|
""",
|
||||||
|
"""
|
||||||
|
|class Test{ fun test() = this }
|
||||||
|
|fun some() {
|
||||||
|
| Test()
|
||||||
|
| .<caret>
|
||||||
|
|}
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testChainCallContinueWithSafeCall() {
|
||||||
|
doTypeTest(
|
||||||
|
'.',
|
||||||
|
"""
|
||||||
|
|class Test{ fun test() = this }
|
||||||
|
|fun some() {
|
||||||
|
| Test()
|
||||||
|
| ?<caret>
|
||||||
|
|}
|
||||||
|
""",
|
||||||
|
"""
|
||||||
|
|class Test{ fun test() = this }
|
||||||
|
|fun some() {
|
||||||
|
| Test()
|
||||||
|
| ?.<caret>
|
||||||
|
|}
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testContinueWithElvis() {
|
||||||
|
doTypeTest(
|
||||||
|
':',
|
||||||
|
"""
|
||||||
|
|fun test(): Any? = null
|
||||||
|
|fun some() {
|
||||||
|
| test()
|
||||||
|
| ?<caret>
|
||||||
|
|}
|
||||||
|
""",
|
||||||
|
"""
|
||||||
|
|fun test(): Any? = null
|
||||||
|
|fun some() {
|
||||||
|
| test()
|
||||||
|
| ?:<caret>
|
||||||
|
|}
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testContinueWithOr() {
|
||||||
|
doTypeTest(
|
||||||
|
'|',
|
||||||
|
"""
|
||||||
|
|fun some() {
|
||||||
|
| if (true
|
||||||
|
| |<caret>)
|
||||||
|
|}
|
||||||
|
""",
|
||||||
|
"""
|
||||||
|
|fun some() {
|
||||||
|
| if (true
|
||||||
|
| ||<caret>)
|
||||||
|
|}
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testContinueWithAnd() {
|
||||||
|
doTypeTest(
|
||||||
|
'&',
|
||||||
|
"""
|
||||||
|
|fun some() {
|
||||||
|
| val test = true
|
||||||
|
| &<caret>
|
||||||
|
|}
|
||||||
|
""",
|
||||||
|
"""
|
||||||
|
|fun some() {
|
||||||
|
| val test = true
|
||||||
|
| &&<caret>
|
||||||
|
|}
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testSpaceAroundRange() {
|
||||||
|
doTypeTest(
|
||||||
|
'.',
|
||||||
|
"""
|
||||||
|
| val test = 1 <caret>
|
||||||
|
""",
|
||||||
|
"""
|
||||||
|
| val test = 1 .<caret>
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testIndentBeforeElseWithBlock() {
|
||||||
|
doTypeTest(
|
||||||
|
'\n',
|
||||||
|
"""
|
||||||
|
|fun test(b: Boolean) {
|
||||||
|
| if (b) {
|
||||||
|
| }<caret>
|
||||||
|
| else if (!b) {
|
||||||
|
| }
|
||||||
|
|}
|
||||||
|
""",
|
||||||
|
"""
|
||||||
|
|fun test(b: Boolean) {
|
||||||
|
| if (b) {
|
||||||
|
| }
|
||||||
|
| <caret>
|
||||||
|
| else if (!b) {
|
||||||
|
| }
|
||||||
|
|}
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testIndentBeforeElseWithoutBlock() {
|
||||||
|
doTypeTest(
|
||||||
|
'\n',
|
||||||
|
"""
|
||||||
|
|fun test(b: Boolean) {
|
||||||
|
| if (b)
|
||||||
|
| foo()<caret>
|
||||||
|
| else {
|
||||||
|
| }
|
||||||
|
|}
|
||||||
|
""",
|
||||||
|
"""
|
||||||
|
|fun test(b: Boolean) {
|
||||||
|
| if (b)
|
||||||
|
| foo()
|
||||||
|
| <caret>
|
||||||
|
| else {
|
||||||
|
| }
|
||||||
|
|}
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testIndentOnFinishedVariableEndAfterEquals() {
|
||||||
|
doTypeTest(
|
||||||
|
'\n',
|
||||||
|
"""
|
||||||
|
|fun test() {
|
||||||
|
| val a =<caret>
|
||||||
|
| foo()
|
||||||
|
|}
|
||||||
|
""",
|
||||||
|
"""
|
||||||
|
|fun test() {
|
||||||
|
| val a =
|
||||||
|
| <caret>
|
||||||
|
| foo()
|
||||||
|
|}
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testIndentNotFinishedVariableEndAfterEquals() {
|
||||||
|
doTypeTest(
|
||||||
|
'\n',
|
||||||
|
"""
|
||||||
|
|fun test() {
|
||||||
|
| val a =<caret>
|
||||||
|
|}
|
||||||
|
""",
|
||||||
|
"""
|
||||||
|
|fun test() {
|
||||||
|
| val a =
|
||||||
|
| <caret>
|
||||||
|
|}
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testSmartEnterWithTabsOnConstructorParameters() {
|
||||||
|
doTypeTest(
|
||||||
|
'\n',
|
||||||
|
"""
|
||||||
|
|class A(
|
||||||
|
| a: Int,<caret>
|
||||||
|
|)
|
||||||
|
""",
|
||||||
|
"""
|
||||||
|
|class A(
|
||||||
|
| a: Int,
|
||||||
|
| <caret>
|
||||||
|
|)
|
||||||
|
""",
|
||||||
|
enableSmartEnterWithTabs()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testSmartEnterWithTabsInMethodParameters() {
|
||||||
|
doTypeTest(
|
||||||
|
'\n',
|
||||||
|
"""
|
||||||
|
|fun method(
|
||||||
|
| arg1: String,<caret>
|
||||||
|
|) {}
|
||||||
|
""",
|
||||||
|
"""
|
||||||
|
|fun method(
|
||||||
|
| arg1: String,
|
||||||
|
| <caret>
|
||||||
|
|) {}
|
||||||
|
""",
|
||||||
|
enableSmartEnterWithTabs()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testAutoIndentInWhenClause() {
|
||||||
|
doTypeTest(
|
||||||
|
'\n',
|
||||||
|
"""
|
||||||
|
|fun test() {
|
||||||
|
| when (2) {
|
||||||
|
| is Int -><caret>
|
||||||
|
| }
|
||||||
|
|}
|
||||||
|
""",
|
||||||
|
"""
|
||||||
|
|fun test() {
|
||||||
|
| when (2) {
|
||||||
|
| is Int ->
|
||||||
|
| <caret>
|
||||||
|
| }
|
||||||
|
|}
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testEnterInFunctionWithExpressionBody() {
|
||||||
|
doTypeTest(
|
||||||
|
'\n',
|
||||||
|
"""
|
||||||
|
|fun test() =<caret>
|
||||||
|
""",
|
||||||
|
"""
|
||||||
|
|fun test() =
|
||||||
|
| <caret>
|
||||||
|
""",
|
||||||
|
ENABLE_KOTLIN_OFFICIAL_CODE_STYLE
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testEnterInMultiDeclaration() {
|
||||||
|
doTypeTest(
|
||||||
|
'\n',
|
||||||
|
"""
|
||||||
|
|fun test() {
|
||||||
|
| val (a, b) =<caret>
|
||||||
|
|}
|
||||||
|
""",
|
||||||
|
"""
|
||||||
|
|fun test() {
|
||||||
|
| val (a, b) =
|
||||||
|
| <caret>
|
||||||
|
|}
|
||||||
|
""",
|
||||||
|
ENABLE_KOTLIN_OFFICIAL_CODE_STYLE
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testEnterInVariableDeclaration() {
|
||||||
|
doTypeTest(
|
||||||
|
'\n',
|
||||||
|
"""
|
||||||
|
|val test =<caret>
|
||||||
|
""",
|
||||||
|
"""
|
||||||
|
|val test =
|
||||||
|
| <caret>
|
||||||
|
""",
|
||||||
|
ENABLE_KOTLIN_OFFICIAL_CODE_STYLE
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testMoveThroughGT() {
|
||||||
|
configureFromFileText("a.kt", "val a: List<Set<Int<caret>>>")
|
||||||
|
EditorTestUtil.performTypingAction(editor, '>')
|
||||||
|
EditorTestUtil.performTypingAction(editor, '>')
|
||||||
|
checkResultByText("val a: List<Set<Int>><caret>")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testCharClosingQuote() {
|
||||||
|
doTypeTest('\'', "val c = <caret>", "val c = ''")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun enableSmartEnterWithTabs(): (Project) -> Unit = {
|
||||||
|
val indentOptions = CodeStyle.getSettings(it).getIndentOptions(KotlinFileType.INSTANCE)
|
||||||
|
indentOptions.USE_TAB_CHARACTER = true
|
||||||
|
indentOptions.SMART_TABS = true
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun doTypeTest(ch: Char, beforeText: String, afterText: String, settingsModifier: ((Project) -> Unit)? = null) {
|
||||||
|
doTypeTest(ch.toString(), beforeText, afterText, settingsModifier)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun doTypeTest(text: String, beforeText: String, afterText: String, settingsModifier: ((Project) -> Unit)? = null) {
|
||||||
|
try {
|
||||||
|
if (settingsModifier != null) {
|
||||||
|
settingsModifier(project)
|
||||||
|
}
|
||||||
|
|
||||||
|
configureFromFileText("a.kt", beforeText.trimMargin())
|
||||||
|
for (ch in text) {
|
||||||
|
EditorTestUtil.performTypingAction(editor, ch)
|
||||||
|
}
|
||||||
|
checkResultByText(afterText.trimMargin())
|
||||||
|
} finally {
|
||||||
|
if (settingsModifier != null) {
|
||||||
|
CodeStyle.getSettings(project).clearCodeStyleSettings()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun doLtGtTestNoAutoClose(initText: String) {
|
||||||
|
doLtGtTest(initText, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun doLtGtTest(initText: String, shouldCloseBeInsert: Boolean) {
|
||||||
|
configureFromFileText("a.kt", initText)
|
||||||
|
|
||||||
|
EditorTestUtil.performTypingAction(editor, '<')
|
||||||
|
checkResultByText(if (shouldCloseBeInsert) initText.replace("<caret>", "<<caret>>") else initText.replace("<caret>", "<<caret>"))
|
||||||
|
|
||||||
|
EditorTestUtil.performTypingAction(editor, EditorTestUtil.BACKSPACE_FAKE_CHAR)
|
||||||
|
checkResultByText(initText)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun doLtGtTest(initText: String) {
|
||||||
|
doLtGtTest(initText, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val ENABLE_KOTLIN_OFFICIAL_CODE_STYLE: (Project) -> Unit = {
|
||||||
|
val settings = ktCodeStyleSettings(it)?.all ?: error("No Settings")
|
||||||
|
KotlinStyleGuideCodeStyle.apply(settings)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.kdoc;
|
||||||
|
|
||||||
|
import com.intellij.codeInsight.TargetElementUtil;
|
||||||
|
import com.intellij.ide.startup.impl.StartupManagerImpl;
|
||||||
|
import com.intellij.openapi.startup.StartupManager;
|
||||||
|
import com.intellij.psi.PsiElement;
|
||||||
|
import com.intellij.refactoring.rename.RenameProcessor;
|
||||||
|
import com.intellij.testFramework.LightCodeInsightTestCase;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase;
|
||||||
|
import org.jetbrains.kotlin.test.JUnit3WithIdeaConfigurationRunner;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
@RunWith(JUnit3WithIdeaConfigurationRunner.class)
|
||||||
|
public class KdocRenameTest extends LightCodeInsightTestCase {
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
protected String getTestDataPath() {
|
||||||
|
return PluginTestCaseBase.getTestDataPathBase() + "/kdoc/rename/";
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testParamReference() throws Exception {
|
||||||
|
doTest("bar");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testTypeParamReference() throws Exception {
|
||||||
|
doTest("R");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testCodeReference() throws Exception {
|
||||||
|
doTest("xyzzy");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void setUp() throws Exception {
|
||||||
|
super.setUp();
|
||||||
|
((StartupManagerImpl) StartupManager.getInstance(getProject())).runPostStartupActivities();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doTest(String newName) throws Exception {
|
||||||
|
configureByFile(getTestName(false) + ".kt");
|
||||||
|
PsiElement element = TargetElementUtil
|
||||||
|
.findTargetElement(getEditor(),
|
||||||
|
TargetElementUtil.ELEMENT_NAME_ACCEPTED | TargetElementUtil.REFERENCED_ELEMENT_ACCEPTED);
|
||||||
|
assertNotNull(element);
|
||||||
|
new RenameProcessor(getProject(), element, newName, true, true).run();
|
||||||
|
checkResultByFile(getTestName(false) + ".kt.after");
|
||||||
|
}
|
||||||
|
}
|
||||||
+31
@@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.navigation
|
||||||
|
|
||||||
|
import com.intellij.openapi.projectRoots.Sdk
|
||||||
|
import com.intellij.testFramework.LightCodeInsightTestCase
|
||||||
|
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase
|
||||||
|
import org.jetbrains.kotlin.idea.test.invalidateLibraryCache
|
||||||
|
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
abstract class AbstractKotlinGotoImplementationTest : LightCodeInsightTestCase() {
|
||||||
|
|
||||||
|
override fun setUp() {
|
||||||
|
super.setUp()
|
||||||
|
invalidateLibraryCache(project)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getTestDataPath(): String = KotlinTestUtils.getHomeDirectory() + File.separator
|
||||||
|
|
||||||
|
override fun getProjectJDK(): Sdk = PluginTestCaseBase.mockJdk()
|
||||||
|
|
||||||
|
protected fun doTest(path: String) {
|
||||||
|
configureByFile(path)
|
||||||
|
val gotoData = NavigationTestUtils.invokeGotoImplementations(editor, file)
|
||||||
|
NavigationTestUtils.assertGotoDataMatching(editor, gotoData)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,256 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.refactoring
|
||||||
|
|
||||||
|
import com.intellij.codeInsight.TargetElementUtil
|
||||||
|
import com.intellij.codeInsight.template.impl.TemplateManagerImpl
|
||||||
|
import com.intellij.openapi.actionSystem.CommonDataKeys
|
||||||
|
import com.intellij.openapi.actionSystem.impl.SimpleDataContext
|
||||||
|
import com.intellij.openapi.command.WriteCommandAction
|
||||||
|
import com.intellij.refactoring.BaseRefactoringProcessor
|
||||||
|
import com.intellij.refactoring.rename.inplace.VariableInplaceRenameHandler
|
||||||
|
import com.intellij.testFramework.LightPlatformCodeInsightTestCase
|
||||||
|
import com.intellij.testFramework.fixtures.CodeInsightTestUtil
|
||||||
|
import junit.framework.TestCase
|
||||||
|
import org.jetbrains.kotlin.idea.refactoring.rename.KotlinMemberInplaceRenameHandler
|
||||||
|
import org.jetbrains.kotlin.idea.refactoring.rename.KotlinVariableInplaceRenameHandler
|
||||||
|
import org.jetbrains.kotlin.idea.refactoring.rename.RenameKotlinImplicitLambdaParameter
|
||||||
|
import org.jetbrains.kotlin.idea.refactoring.rename.findElementForRename
|
||||||
|
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase
|
||||||
|
import org.jetbrains.kotlin.psi.KtNameReferenceExpression
|
||||||
|
import org.jetbrains.kotlin.test.InTextDirectivesUtils
|
||||||
|
import org.jetbrains.kotlin.test.JUnit3WithIdeaConfigurationRunner
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import kotlin.test.assertFalse
|
||||||
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
|
@RunWith(JUnit3WithIdeaConfigurationRunner::class)
|
||||||
|
class InplaceRenameTest : LightPlatformCodeInsightTestCase() {
|
||||||
|
override fun isRunInWriteAction(): Boolean = false
|
||||||
|
override fun getTestDataPath(): String = PluginTestCaseBase.getTestDataPathBase() + "/refactoring/rename/inplace/"
|
||||||
|
|
||||||
|
fun testLocalVal() {
|
||||||
|
doTestMemberInplaceRename("y")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testForLoop() {
|
||||||
|
doTestInplaceRename("j")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testTryCatch() {
|
||||||
|
doTestInplaceRename("e1")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testFunctionLiteral() {
|
||||||
|
doTestInplaceRename("y")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testFunctionLiteralIt() {
|
||||||
|
doTestImplicitLambdaParameter("y")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testFunctionLiteralItEndCaret() {
|
||||||
|
doTestImplicitLambdaParameter("y")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testFunctionLiteralParenthesis() {
|
||||||
|
doTestInplaceRename("y")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testLocalFunction() {
|
||||||
|
doTestMemberInplaceRename("bar")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testFunctionParameterNotInplace() {
|
||||||
|
doTestInplaceRename(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testGlobalFunctionNotInplace() {
|
||||||
|
doTestInplaceRename(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testTopLevelValNotInplace() {
|
||||||
|
doTestInplaceRename(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testLabelFromFunction() {
|
||||||
|
doTestMemberInplaceRename("foo")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testMultiDeclaration() {
|
||||||
|
doTestInplaceRename("foo")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testLocalVarShadowingMemberProperty() {
|
||||||
|
doTestMemberInplaceRename("name1")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testNoReformat() {
|
||||||
|
doTestMemberInplaceRename("subject2")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testInvokeToFoo() {
|
||||||
|
doTestMemberInplaceRename("foo")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testInvokeToGet() {
|
||||||
|
doTestMemberInplaceRename("get")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testInvokeToGetWithQualifiedExpr() {
|
||||||
|
doTestMemberInplaceRename("get")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testInvokeToGetWithSafeQualifiedExpr() {
|
||||||
|
doTestMemberInplaceRename("get")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testInvokeToPlus() {
|
||||||
|
doTestMemberInplaceRename("plus")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testGetToFoo() {
|
||||||
|
doTestMemberInplaceRename("foo")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testGetToInvoke() {
|
||||||
|
doTestMemberInplaceRename("invoke")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testGetToInvokeWithQualifiedExpr() {
|
||||||
|
doTestMemberInplaceRename("invoke")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testGetToInvokeWithSafeQualifiedExpr() {
|
||||||
|
doTestMemberInplaceRename("invoke")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testGetToPlus() {
|
||||||
|
doTestMemberInplaceRename("plus")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testAddQuotes() {
|
||||||
|
doTestMemberInplaceRename("is")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testAddThis() {
|
||||||
|
doTestMemberInplaceRename("foo")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testExtensionAndNoReceiver() {
|
||||||
|
doTestMemberInplaceRename("b")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testTwoExtensions() {
|
||||||
|
doTestMemberInplaceRename("example")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testQuotedLocalVar() {
|
||||||
|
doTestMemberInplaceRename("x")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testQuotedParameter() {
|
||||||
|
doTestMemberInplaceRename("x")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testEraseCompanionName() {
|
||||||
|
doTestMemberInplaceRename("")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testLocalVarRedeclaration() {
|
||||||
|
doTestMemberInplaceRename("localValB")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testLocalFunRedeclaration() {
|
||||||
|
doTestMemberInplaceRename("localFunB")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testLocalClassRedeclaration() {
|
||||||
|
doTestMemberInplaceRename("LocalClassB")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testBacktickedWithAccessors() {
|
||||||
|
doTestMemberInplaceRename("`object`")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testNoTextUsagesForLocalVar() {
|
||||||
|
doTestMemberInplaceRename("w")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun doTestImplicitLambdaParameter(newName: String) {
|
||||||
|
configureByFile(getTestName(false) + ".kt")
|
||||||
|
|
||||||
|
// This code is copy-pasted from CodeInsightTestUtil.doInlineRename() and slightly modified.
|
||||||
|
// Original method was not suitable because it expects renamed element to be reference to other or referrable
|
||||||
|
|
||||||
|
val file = getFile()!!
|
||||||
|
val editor = getEditor()!!
|
||||||
|
val element = file.findElementForRename<KtNameReferenceExpression>(editor.caretModel.offset)!!
|
||||||
|
assertNotNull(element)
|
||||||
|
|
||||||
|
val dataContext = SimpleDataContext.getSimpleContext(CommonDataKeys.PSI_ELEMENT.name, element,
|
||||||
|
getCurrentEditorDataContext())
|
||||||
|
val handler = RenameKotlinImplicitLambdaParameter()
|
||||||
|
|
||||||
|
assertTrue(handler.isRenaming(dataContext), "In-place rename not allowed for " + element)
|
||||||
|
|
||||||
|
val project = editor.project!!
|
||||||
|
|
||||||
|
TemplateManagerImpl.setTemplateTesting(project, testRootDisposable)
|
||||||
|
|
||||||
|
object : WriteCommandAction.Simple<Any>(project) {
|
||||||
|
override fun run() {
|
||||||
|
handler.invoke(project, editor, file, dataContext)
|
||||||
|
}
|
||||||
|
}.execute()
|
||||||
|
|
||||||
|
var state = TemplateManagerImpl.getTemplateState(editor)
|
||||||
|
assert(state != null)
|
||||||
|
val range = state!!.currentVariableRange
|
||||||
|
assert(range != null)
|
||||||
|
object : WriteCommandAction.Simple<Any>(project) {
|
||||||
|
override fun run() {
|
||||||
|
editor.document.replaceString(range!!.startOffset, range.endOffset, newName)
|
||||||
|
}
|
||||||
|
}.execute().throwException()
|
||||||
|
|
||||||
|
state = TemplateManagerImpl.getTemplateState(editor)
|
||||||
|
assert(state != null)
|
||||||
|
state!!.gotoEnd(false)
|
||||||
|
|
||||||
|
checkResultByFile(getTestName(false) + ".kt.after")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun doTestMemberInplaceRename(newName: String?) {
|
||||||
|
doTestInplaceRename(newName, KotlinMemberInplaceRenameHandler())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun doTestInplaceRename(newName: String?, handler: VariableInplaceRenameHandler = KotlinVariableInplaceRenameHandler()) {
|
||||||
|
configureByFile(getTestName(false) + ".kt")
|
||||||
|
val element = TargetElementUtil.findTargetElement(
|
||||||
|
editor,
|
||||||
|
TargetElementUtil.ELEMENT_NAME_ACCEPTED or TargetElementUtil.REFERENCED_ELEMENT_ACCEPTED
|
||||||
|
)
|
||||||
|
|
||||||
|
assertNotNull(element)
|
||||||
|
|
||||||
|
val dataContext = SimpleDataContext.getSimpleContext(CommonDataKeys.PSI_ELEMENT.name, element!!, currentEditorDataContext)
|
||||||
|
|
||||||
|
if (newName == null) {
|
||||||
|
assertFalse(handler.isRenaming(dataContext), "In-place rename is allowed for " + element)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
try {
|
||||||
|
assertTrue(handler.isRenaming(dataContext), "In-place rename not allowed for " + element)
|
||||||
|
CodeInsightTestUtil.doInlineRename(handler, newName, getEditor(), element)
|
||||||
|
checkResultByFile(getTestName(false) + ".kt.after")
|
||||||
|
} catch (e: BaseRefactoringProcessor.ConflictsInTestsException) {
|
||||||
|
val expectedMessage = InTextDirectivesUtils.findStringWithPrefixes(file.text, "// SHOULD_FAIL_WITH: ")
|
||||||
|
TestCase.assertEquals(expectedMessage, e.messages.joinToString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+60
@@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.refactoring.rename;
|
||||||
|
|
||||||
|
import com.intellij.codeInsight.TargetElementUtil;
|
||||||
|
import com.intellij.psi.PsiElement;
|
||||||
|
import com.intellij.refactoring.rename.RenameProcessor;
|
||||||
|
import com.intellij.testFramework.LightCodeInsightTestCase;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase;
|
||||||
|
|
||||||
|
public class SimpleNameReferenceRenameTest extends LightCodeInsightTestCase {
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
protected String getTestDataPath() {
|
||||||
|
return PluginTestCaseBase.getTestDataPathBase() + "/refactoring/rename/simpleNameReference/";
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testRenameLabel() throws Exception {
|
||||||
|
doTest("foo");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testRenameLabel2() throws Exception {
|
||||||
|
doTest("anotherFoo");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testRenameField() throws Exception {
|
||||||
|
doTest("renamed");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testRenameFieldIdentifier() throws Exception {
|
||||||
|
doTest("anotherRenamed");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testMemberOfLocalObject() throws Exception {
|
||||||
|
doTest("bar");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testLocalFunction() throws Exception {
|
||||||
|
doTest("xyzzy");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testParameterOfCopyMethod() throws Exception {
|
||||||
|
doTest("y");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doTest(String newName) throws Exception {
|
||||||
|
configureByFile(getTestName(true) + ".kt");
|
||||||
|
PsiElement element = TargetElementUtil
|
||||||
|
.findTargetElement(getEditor(),
|
||||||
|
TargetElementUtil.ELEMENT_NAME_ACCEPTED | TargetElementUtil.REFERENCED_ELEMENT_ACCEPTED);
|
||||||
|
assertNotNull(element);
|
||||||
|
new RenameProcessor(getProject(), element, newName, true, true).run();
|
||||||
|
checkResultByFile(getTestName(true) + ".kt.after");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,311 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.script
|
||||||
|
|
||||||
|
import com.intellij.openapi.application.ApplicationManager
|
||||||
|
import com.intellij.openapi.extensions.Extensions
|
||||||
|
import com.intellij.openapi.module.JavaModuleType
|
||||||
|
import com.intellij.openapi.module.Module
|
||||||
|
import com.intellij.openapi.projectRoots.ProjectJdkTable
|
||||||
|
import com.intellij.openapi.roots.ModuleRootModificationUtil
|
||||||
|
import com.intellij.openapi.roots.ProjectRootManager
|
||||||
|
import com.intellij.openapi.util.io.FileUtil
|
||||||
|
import com.intellij.openapi.util.text.StringUtil
|
||||||
|
import com.intellij.openapi.vfs.LocalFileSystem
|
||||||
|
import com.intellij.openapi.vfs.VfsUtil
|
||||||
|
import com.intellij.openapi.vfs.VirtualFile
|
||||||
|
import com.intellij.testFramework.PlatformTestCase
|
||||||
|
import com.intellij.testFramework.PlatformTestUtil
|
||||||
|
import com.intellij.testFramework.PsiTestUtil
|
||||||
|
import com.intellij.util.ui.UIUtil
|
||||||
|
import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime
|
||||||
|
import org.jetbrains.kotlin.idea.completion.test.KotlinCompletionTestCase
|
||||||
|
import org.jetbrains.kotlin.idea.core.script.IdeScriptReportSink
|
||||||
|
import org.jetbrains.kotlin.idea.core.script.ScriptConfigurationManager.Companion.updateScriptDependenciesSynchronously
|
||||||
|
import org.jetbrains.kotlin.idea.core.script.ScriptDefinitionContributor
|
||||||
|
import org.jetbrains.kotlin.idea.core.script.ScriptDefinitionsManager
|
||||||
|
import org.jetbrains.kotlin.idea.core.script.isScriptDependenciesUpdaterDisabled
|
||||||
|
import org.jetbrains.kotlin.idea.highlighter.KotlinHighlightingUtil
|
||||||
|
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase
|
||||||
|
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
||||||
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
|
import org.jetbrains.kotlin.scripting.definitions.findScriptDefinition
|
||||||
|
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||||
|
import org.jetbrains.kotlin.test.MockLibraryUtil
|
||||||
|
import org.jetbrains.kotlin.test.TestJdkKind
|
||||||
|
import org.jetbrains.kotlin.test.util.addDependency
|
||||||
|
import org.jetbrains.kotlin.test.util.projectLibrary
|
||||||
|
import org.jetbrains.kotlin.utils.PathUtil
|
||||||
|
import org.jetbrains.kotlin.utils.PathUtil.KOTLIN_JAVA_SCRIPT_RUNTIME_JAR
|
||||||
|
import org.jetbrains.kotlin.utils.PathUtil.KOTLIN_SCRIPTING_COMMON_JAR
|
||||||
|
import org.jetbrains.kotlin.utils.PathUtil.KOTLIN_SCRIPTING_JVM_JAR
|
||||||
|
import java.io.File
|
||||||
|
import java.util.regex.Pattern
|
||||||
|
import kotlin.script.dependencies.Environment
|
||||||
|
import kotlin.script.experimental.api.ScriptDiagnostic
|
||||||
|
|
||||||
|
private val validKeys = setOf("javaHome", "sources", "classpath", "imports", "template-classes-names")
|
||||||
|
private const val useDefaultTemplate = "// DEPENDENCIES:"
|
||||||
|
private const val templatesSettings = "// TEMPLATES: "
|
||||||
|
// some bugs can only be reproduced when some module and script have intersecting library dependencies
|
||||||
|
private const val configureConflictingModule = "// CONFLICTING_MODULE"
|
||||||
|
|
||||||
|
private fun String.splitOrEmpty(delimeters: String) = split(delimeters).takeIf { it.size > 1 } ?: emptyList()
|
||||||
|
internal val switches = listOf(
|
||||||
|
useDefaultTemplate,
|
||||||
|
configureConflictingModule
|
||||||
|
)
|
||||||
|
|
||||||
|
abstract class AbstractScriptConfigurationTest : KotlinCompletionTestCase() {
|
||||||
|
companion object {
|
||||||
|
private const val SCRIPT_NAME = "script.kts"
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setUpModule() {
|
||||||
|
// do not create default module
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun findMainScript(testDir: String): File {
|
||||||
|
val scriptFile = File(testDir).walkTopDown().find { it.name == SCRIPT_NAME }
|
||||||
|
if (scriptFile != null) return scriptFile
|
||||||
|
|
||||||
|
return File(testDir).walkTopDown().singleOrNull { it.name.contains("script") }
|
||||||
|
?: error("Couldn't find $SCRIPT_NAME file in $testDir")
|
||||||
|
}
|
||||||
|
|
||||||
|
private val sdk by lazy {
|
||||||
|
val jdk = PluginTestCaseBase.jdk(TestJdkKind.MOCK_JDK)
|
||||||
|
runWriteAction {
|
||||||
|
ProjectJdkTable.getInstance().addJdk(jdk, testRootDisposable)
|
||||||
|
ProjectRootManager.getInstance(project).projectSdk = jdk
|
||||||
|
}
|
||||||
|
jdk
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun configureScriptFile(path: String) {
|
||||||
|
val mainScriptFile = findMainScript(path)
|
||||||
|
val environment = createScriptEnvironment(mainScriptFile)
|
||||||
|
registerScriptTemplateProvider(environment)
|
||||||
|
|
||||||
|
File(path, "mainModule").takeIf { it.exists() }?.let {
|
||||||
|
myModule = createTestModuleFromDir(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
File(path).listFiles { file -> file.name.startsWith("module") }.filter { it.exists() }.forEach {
|
||||||
|
val newModule = createTestModuleFromDir(it)
|
||||||
|
assert(myModule != null) { "Main module should exists" }
|
||||||
|
ModuleRootModificationUtil.addDependency(myModule, newModule)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (module != null) {
|
||||||
|
module.addDependency(
|
||||||
|
projectLibrary(
|
||||||
|
"script-runtime",
|
||||||
|
classesRoot = VfsUtil.findFileByIoFile(PathUtil.kotlinPathsForDistDirectory.scriptRuntimePath, true)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
if (environment["template-classes"] != null) {
|
||||||
|
module.addDependency(
|
||||||
|
projectLibrary(
|
||||||
|
"script-template-library",
|
||||||
|
classesRoot = VfsUtil.findFileByIoFile(environment["template-classes"] as File, true)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (configureConflictingModule in environment) {
|
||||||
|
val sharedLib = VfsUtil.findFileByIoFile(environment["lib-classes"] as File, true)!!
|
||||||
|
if (module == null) {
|
||||||
|
// Force create module if it doesn't exist
|
||||||
|
myModule = createTestModuleByName("mainModule")
|
||||||
|
}
|
||||||
|
module.addDependency(projectLibrary("sharedLib", classesRoot = sharedLib))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (module != null) {
|
||||||
|
ModuleRootModificationUtil.updateModel(module) { model ->
|
||||||
|
model.sdk = sdk
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
createFileAndSyncDependencies(mainScriptFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val oldScripClasspath: String? = System.getProperty("kotlin.script.classpath")
|
||||||
|
|
||||||
|
override fun setUp() {
|
||||||
|
super.setUp()
|
||||||
|
ApplicationManager.getApplication().isScriptDependenciesUpdaterDisabled = true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun tearDown() {
|
||||||
|
ApplicationManager.getApplication().isScriptDependenciesUpdaterDisabled = false
|
||||||
|
|
||||||
|
System.setProperty("kotlin.script.classpath", oldScripClasspath ?: "")
|
||||||
|
|
||||||
|
super.tearDown()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createTestModuleByName(name: String): Module {
|
||||||
|
val newModuleDir = runWriteAction { VfsUtil.createDirectoryIfMissing(project.baseDir, name) }
|
||||||
|
val newModule = createModuleAt(name, project, JavaModuleType.getModuleType(), newModuleDir.path)
|
||||||
|
|
||||||
|
// Return type was changed, but it's not used. BUNCH: 183
|
||||||
|
@Suppress("MissingRecentApi")
|
||||||
|
PsiTestUtil.addSourceContentToRoots(newModule, newModuleDir)
|
||||||
|
return newModule
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createTestModuleFromDir(dir: File): Module {
|
||||||
|
return createTestModuleByName(dir.name).apply {
|
||||||
|
PlatformTestCase.copyDirContentsTo(LocalFileSystem.getInstance().findFileByIoFile(dir)!!, moduleFile!!.parent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createScriptEnvironment(scriptFile: File): Environment {
|
||||||
|
val defaultEnvironment = defaultEnvironment(scriptFile.parent + File.separator)
|
||||||
|
val env = mutableMapOf<String, Any?>()
|
||||||
|
scriptFile.forEachLine { line ->
|
||||||
|
|
||||||
|
fun iterateKeysInLine(prefix: String) {
|
||||||
|
if (line.contains(prefix)) {
|
||||||
|
line.trim().substringAfter(prefix).split(";").forEach { entry ->
|
||||||
|
val (key, values) = entry.splitOrEmpty(":").map { it.trim() }
|
||||||
|
assert(key in validKeys) { "Unexpected key: $key" }
|
||||||
|
env[key] = values.split(",").map {
|
||||||
|
val str = it.trim()
|
||||||
|
defaultEnvironment[str] ?: str
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
iterateKeysInLine(useDefaultTemplate)
|
||||||
|
iterateKeysInLine(templatesSettings)
|
||||||
|
|
||||||
|
switches.forEach {
|
||||||
|
if (it in line) {
|
||||||
|
env[it] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (env[useDefaultTemplate] != true && env["template-classes-names"] == null) {
|
||||||
|
env["template-classes-names"] = listOf("custom.scriptDefinition.Template")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (env["javaHome"] != null) {
|
||||||
|
val jdkKind = when ((env["javaHome"] as? List<String>)?.singleOrNull()) {
|
||||||
|
"9" -> TestJdkKind.FULL_JDK_9
|
||||||
|
else -> TestJdkKind.MOCK_JDK
|
||||||
|
}
|
||||||
|
runWriteAction {
|
||||||
|
val jdk = PluginTestCaseBase.jdk(jdkKind)
|
||||||
|
ProjectJdkTable.getInstance().addJdk(jdk, testRootDisposable)
|
||||||
|
env["javaHome"] = File(jdk.homePath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
env.putAll(defaultEnvironment)
|
||||||
|
return env
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun defaultEnvironment(path: String): Map<String, File?> {
|
||||||
|
val templateOutDir = File("${path}template").takeIf { it.isDirectory }?.let {
|
||||||
|
compileLibToDir(it, *scriptClasspath())
|
||||||
|
} ?: File("idea/testData/script/definition/defaultTemplate").takeIf { it.isDirectory }?.let {
|
||||||
|
compileLibToDir(it, *scriptClasspath())
|
||||||
|
}
|
||||||
|
|
||||||
|
val libSrcDir = File("${path}lib").takeIf { it.isDirectory }
|
||||||
|
|
||||||
|
val libClasses = libSrcDir?.let { compileLibToDir(it) }
|
||||||
|
|
||||||
|
return mapOf(
|
||||||
|
"runtime-classes" to ForTestCompileRuntime.runtimeJarForTests(),
|
||||||
|
"runtime-source" to File("libraries/stdlib/src"),
|
||||||
|
"lib-classes" to libClasses,
|
||||||
|
"lib-source" to libSrcDir,
|
||||||
|
"template-classes" to templateOutDir
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun scriptClasspath(): Array<String> {
|
||||||
|
return with(PathUtil.kotlinPathsForDistDirectory) {
|
||||||
|
arrayOf(
|
||||||
|
File(libPath, KOTLIN_JAVA_SCRIPT_RUNTIME_JAR).path,
|
||||||
|
File(libPath, KOTLIN_SCRIPTING_COMMON_JAR).path,
|
||||||
|
File(libPath, KOTLIN_SCRIPTING_JVM_JAR).path
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createFileAndSyncDependencies(scriptFile: File) {
|
||||||
|
var script: VirtualFile? = null
|
||||||
|
if (module != null) {
|
||||||
|
script = module.moduleFile?.parent?.findChild(scriptFile.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (script == null) {
|
||||||
|
val target = File(project.basePath, scriptFile.name)
|
||||||
|
scriptFile.copyTo(target)
|
||||||
|
script = VfsUtil.findFileByIoFile(target, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (script == null) error("Test file with script couldn't be found in test project")
|
||||||
|
|
||||||
|
configureByExistingFile(script)
|
||||||
|
updateScriptDependenciesSynchronously(myFile, project)
|
||||||
|
|
||||||
|
VfsUtil.markDirtyAndRefresh(false, true, true, project.baseDir)
|
||||||
|
// This is needed because updateScriptDependencies invalidates psiFile that was stored in myFile field
|
||||||
|
myFile = psiManager.findFile(script)
|
||||||
|
|
||||||
|
val ktFile = myFile as KtFile
|
||||||
|
val reports = IdeScriptReportSink.getReports(ktFile)
|
||||||
|
val isFatalErrorPresent = reports.any { it.severity == ScriptDiagnostic.Severity.FATAL }
|
||||||
|
assert(isFatalErrorPresent || KotlinHighlightingUtil.shouldHighlight(myFile)) {
|
||||||
|
"Highlighting is switched off for ${myFile.virtualFile.path}\n" +
|
||||||
|
"reports=$reports\n" +
|
||||||
|
"scriptDefinition=${ktFile.findScriptDefinition()}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun compileLibToDir(srcDir: File, vararg classpath: String): File {
|
||||||
|
//TODO: tmpDir would be enough, but there is tricky fail under AS otherwise
|
||||||
|
val outDir = KotlinTestUtils.tmpDirForReusableFolder("${getTestName(false)}${srcDir.name}Out")
|
||||||
|
|
||||||
|
val kotlinSourceFiles = FileUtil.findFilesByMask(Pattern.compile(".+\\.kt$"), srcDir)
|
||||||
|
if (kotlinSourceFiles.isNotEmpty()) {
|
||||||
|
MockLibraryUtil.compileKotlin(srcDir.path, outDir, extraClasspath = *classpath)
|
||||||
|
}
|
||||||
|
|
||||||
|
val javaSourceFiles = FileUtil.findFilesByMask(Pattern.compile(".+\\.java$"), srcDir)
|
||||||
|
if (javaSourceFiles.isNotEmpty()) {
|
||||||
|
KotlinTestUtils.compileJavaFiles(
|
||||||
|
javaSourceFiles,
|
||||||
|
listOf("-cp", StringUtil.join(listOf(*classpath, outDir), File.pathSeparator), "-d", outDir.path)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return outDir
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun registerScriptTemplateProvider(environment: Environment) {
|
||||||
|
val provider = if (environment[useDefaultTemplate] == true) {
|
||||||
|
FromTextTemplateProvider(environment)
|
||||||
|
} else {
|
||||||
|
CustomScriptTemplateProvider(environment)
|
||||||
|
}
|
||||||
|
|
||||||
|
ScriptDefinitionContributor.EP_NAME.getPoint(null).registerExtension(provider)
|
||||||
|
|
||||||
|
ScriptDefinitionsManager.getInstance(project).reloadScriptDefinitions()
|
||||||
|
|
||||||
|
UIUtil.dispatchAllInvocationEvents()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.test.util
|
||||||
|
|
||||||
|
import com.intellij.openapi.module.Module
|
||||||
|
import com.intellij.openapi.roots.DependencyScope
|
||||||
|
import com.intellij.openapi.roots.ModuleRootModificationUtil
|
||||||
|
import com.intellij.openapi.roots.OrderRootType
|
||||||
|
import com.intellij.openapi.roots.impl.libraries.ProjectLibraryTable
|
||||||
|
import com.intellij.openapi.roots.libraries.Library
|
||||||
|
import com.intellij.openapi.roots.libraries.PersistentLibraryKind
|
||||||
|
import com.intellij.openapi.vfs.JarFileSystem
|
||||||
|
import com.intellij.openapi.vfs.LocalFileSystem
|
||||||
|
import com.intellij.openapi.vfs.VirtualFile
|
||||||
|
import com.intellij.testFramework.HeavyPlatformTestCase
|
||||||
|
import org.jetbrains.kotlin.test.testFramework.runWriteAction
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
fun HeavyPlatformTestCase.projectLibrary(
|
||||||
|
libraryName: String = "TestLibrary",
|
||||||
|
classesRoot: VirtualFile? = null,
|
||||||
|
sourcesRoot: VirtualFile? = null,
|
||||||
|
kind: PersistentLibraryKind<*>? = null
|
||||||
|
): Library {
|
||||||
|
return runWriteAction {
|
||||||
|
val modifiableModel = ProjectLibraryTable.getInstance(project).modifiableModel
|
||||||
|
val library = try {
|
||||||
|
modifiableModel.createLibrary(libraryName, kind)
|
||||||
|
} finally {
|
||||||
|
modifiableModel.commit()
|
||||||
|
}
|
||||||
|
with (library.modifiableModel) {
|
||||||
|
classesRoot?.let { addRoot(it, OrderRootType.CLASSES) }
|
||||||
|
sourcesRoot?.let { addRoot(it, OrderRootType.SOURCES) }
|
||||||
|
commit()
|
||||||
|
}
|
||||||
|
library
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val File.jarRoot: VirtualFile
|
||||||
|
get() {
|
||||||
|
val virtualFile = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(this) ?: error("Cannot find file $this")
|
||||||
|
return JarFileSystem.getInstance().getRootByLocal(virtualFile) ?: error("Can't find root by file $virtualFile")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Module.addDependency(
|
||||||
|
library: Library,
|
||||||
|
dependencyScope: DependencyScope = DependencyScope.COMPILE,
|
||||||
|
exported: Boolean = false
|
||||||
|
) = ModuleRootModificationUtil.addDependency(this, library, dependencyScope, exported)
|
||||||
+136
@@ -0,0 +1,136 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2015 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.j2k
|
||||||
|
|
||||||
|
import com.intellij.codeInsight.ContainerProvider
|
||||||
|
import com.intellij.codeInsight.NullableNotNullManager
|
||||||
|
import com.intellij.codeInsight.runner.JavaMainMethodProvider
|
||||||
|
import com.intellij.core.CoreApplicationEnvironment
|
||||||
|
import com.intellij.core.JavaCoreApplicationEnvironment
|
||||||
|
import com.intellij.core.JavaCoreProjectEnvironment
|
||||||
|
import com.intellij.lang.MetaLanguage
|
||||||
|
import com.intellij.lang.jvm.facade.JvmElementProvider
|
||||||
|
import com.intellij.openapi.extensions.Extensions
|
||||||
|
import com.intellij.openapi.extensions.ExtensionsArea
|
||||||
|
import com.intellij.openapi.fileTypes.FileTypeExtensionPoint
|
||||||
|
import com.intellij.openapi.util.Disposer
|
||||||
|
import com.intellij.openapi.util.io.FileUtil
|
||||||
|
import com.intellij.psi.*
|
||||||
|
import com.intellij.psi.augment.PsiAugmentProvider
|
||||||
|
import com.intellij.psi.augment.TypeAnnotationModifier
|
||||||
|
import com.intellij.psi.compiled.ClassFileDecompilers
|
||||||
|
import com.intellij.psi.impl.JavaClassSupersImpl
|
||||||
|
import com.intellij.psi.impl.PsiTreeChangePreprocessor
|
||||||
|
import com.intellij.psi.meta.MetaDataContributor
|
||||||
|
import com.intellij.psi.stubs.BinaryFileStubBuilders
|
||||||
|
import com.intellij.psi.util.JavaClassSupers
|
||||||
|
import junit.framework.TestCase
|
||||||
|
import org.jetbrains.kotlin.utils.PathUtil
|
||||||
|
import java.io.File
|
||||||
|
import java.net.URLClassLoader
|
||||||
|
|
||||||
|
abstract class AbstractJavaToKotlinConverterForWebDemoTest : TestCase() {
|
||||||
|
val DISPOSABLE = Disposer.newDisposable()
|
||||||
|
|
||||||
|
fun doTest(javaPath: String) {
|
||||||
|
try {
|
||||||
|
val fileContents = FileUtil.loadFile(File(javaPath), true)
|
||||||
|
val javaCoreEnvironment: JavaCoreProjectEnvironment = setUpJavaCoreEnvironment()
|
||||||
|
translateToKotlin(fileContents, javaCoreEnvironment.project)
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
Disposer.dispose(DISPOSABLE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setUpJavaCoreEnvironment(): JavaCoreProjectEnvironment {
|
||||||
|
// FIXME: There is no `Extensions.cleanRootArea` in 193 platform
|
||||||
|
// Extensions.cleanRootArea(DISPOSABLE)
|
||||||
|
val area = Extensions.getRootArea()
|
||||||
|
|
||||||
|
registerExtensionPoints(area)
|
||||||
|
|
||||||
|
val applicationEnvironment = JavaCoreApplicationEnvironment(DISPOSABLE)
|
||||||
|
val javaCoreEnvironment = object : JavaCoreProjectEnvironment(DISPOSABLE, applicationEnvironment) {
|
||||||
|
override fun preregisterServices() {
|
||||||
|
val projectArea = Extensions.getArea(project)
|
||||||
|
CoreApplicationEnvironment.registerExtensionPoint(projectArea, PsiTreeChangePreprocessor.EP_NAME, PsiTreeChangePreprocessor::class.java)
|
||||||
|
CoreApplicationEnvironment.registerExtensionPoint(projectArea, PsiElementFinder.EP_NAME, PsiElementFinder::class.java)
|
||||||
|
CoreApplicationEnvironment.registerExtensionPoint(projectArea, JvmElementProvider.EP_NAME, JvmElementProvider::class.java)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
javaCoreEnvironment.project.registerService(NullableNotNullManager::class.java, object : NullableNotNullManager(javaCoreEnvironment.project) {
|
||||||
|
override fun isNullable(owner: PsiModifierListOwner, checkBases: Boolean) = !isNotNull(owner, checkBases)
|
||||||
|
override fun isNotNull(owner: PsiModifierListOwner, checkBases: Boolean) = true
|
||||||
|
override fun hasHardcodedContracts(element: PsiElement): Boolean = false
|
||||||
|
override fun getNullables() = emptyList<String>()
|
||||||
|
override fun setNullables(vararg p0: String) = Unit
|
||||||
|
override fun getNotNulls() = emptyList<String>()
|
||||||
|
override fun setNotNulls(vararg p0: String) = Unit
|
||||||
|
override fun getDefaultNullable() = ""
|
||||||
|
override fun setDefaultNullable(defaultNullable: String) = Unit
|
||||||
|
override fun getDefaultNotNull() = ""
|
||||||
|
override fun setDefaultNotNull(p0: String) = Unit
|
||||||
|
override fun setInstrumentedNotNulls(p0: List<String>) = Unit
|
||||||
|
override fun getInstrumentedNotNulls() = emptyList<String>()
|
||||||
|
})
|
||||||
|
|
||||||
|
applicationEnvironment.application.registerService(JavaClassSupers::class.java, JavaClassSupersImpl::class.java)
|
||||||
|
|
||||||
|
for (root in PathUtil.getJdkClassesRootsFromCurrentJre()) {
|
||||||
|
javaCoreEnvironment.addJarToClassPath(root)
|
||||||
|
}
|
||||||
|
val annotations: File? = findAnnotations()
|
||||||
|
if (annotations != null && annotations.exists()) {
|
||||||
|
javaCoreEnvironment.addJarToClassPath(annotations)
|
||||||
|
}
|
||||||
|
return javaCoreEnvironment
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun registerExtensionPoints(area: ExtensionsArea) {
|
||||||
|
CoreApplicationEnvironment.registerExtensionPoint(area, BinaryFileStubBuilders.EP_NAME, FileTypeExtensionPoint::class.java)
|
||||||
|
CoreApplicationEnvironment.registerExtensionPoint(area, FileContextProvider.EP_NAME, FileContextProvider::class.java)
|
||||||
|
|
||||||
|
CoreApplicationEnvironment.registerExtensionPoint(area, MetaDataContributor.EP_NAME, MetaDataContributor::class.java)
|
||||||
|
CoreApplicationEnvironment.registerExtensionPoint(area, PsiAugmentProvider.EP_NAME, PsiAugmentProvider::class.java)
|
||||||
|
CoreApplicationEnvironment.registerExtensionPoint(area, JavaMainMethodProvider.EP_NAME, JavaMainMethodProvider::class.java)
|
||||||
|
|
||||||
|
CoreApplicationEnvironment.registerExtensionPoint(area, ContainerProvider.EP_NAME, ContainerProvider::class.java)
|
||||||
|
CoreApplicationEnvironment.registerExtensionPoint(area, ClassFileDecompilers.EP_NAME, ClassFileDecompilers.Decompiler::class.java)
|
||||||
|
|
||||||
|
CoreApplicationEnvironment.registerExtensionPoint(area, TypeAnnotationModifier.EP_NAME, TypeAnnotationModifier::class.java)
|
||||||
|
CoreApplicationEnvironment.registerExtensionPoint(area, MetaLanguage.EP_NAME, MetaLanguage::class.java)
|
||||||
|
CoreApplicationEnvironment.registerExtensionPoint(area, JavaModuleSystem.EP_NAME, JavaModuleSystem::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun findAnnotations(): File? {
|
||||||
|
var classLoader = JavaToKotlinTranslator::class.java.classLoader
|
||||||
|
while (classLoader != null) {
|
||||||
|
val loader = classLoader
|
||||||
|
if (loader is URLClassLoader) {
|
||||||
|
for (url in loader.urLs) {
|
||||||
|
if ("file" == url.protocol && url.file!!.endsWith("/annotations.jar")) {
|
||||||
|
return File(url.file!!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
classLoader = classLoader.parent
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,127 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.uast.test.kotlin
|
||||||
|
|
||||||
|
import com.intellij.openapi.Disposable
|
||||||
|
import com.intellij.openapi.application.ApplicationManager
|
||||||
|
import com.intellij.openapi.extensions.Extensions
|
||||||
|
import com.intellij.openapi.util.Disposer
|
||||||
|
import com.intellij.patterns.uast.injectionHostUExpression
|
||||||
|
import com.intellij.psi.*
|
||||||
|
import com.intellij.psi.impl.source.resolve.reference.PsiReferenceContributorEP
|
||||||
|
import com.intellij.psi.impl.source.resolve.reference.ReferenceProvidersRegistry
|
||||||
|
import com.intellij.psi.impl.source.resolve.reference.ReferenceProvidersRegistryImpl
|
||||||
|
import com.intellij.psi.util.PropertyUtil
|
||||||
|
import com.intellij.testFramework.LightProjectDescriptor
|
||||||
|
import com.intellij.testFramework.registerServiceInstance
|
||||||
|
import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase
|
||||||
|
import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescriptor
|
||||||
|
import org.jetbrains.kotlin.test.JUnit3WithIdeaConfigurationRunner
|
||||||
|
import org.jetbrains.uast.UExpression
|
||||||
|
import org.jetbrains.uast.evaluateString
|
||||||
|
import org.jetbrains.uast.toUElementOfType
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import kotlin.test.fail
|
||||||
|
|
||||||
|
@RunWith(JUnit3WithIdeaConfigurationRunner::class)
|
||||||
|
class KotlinUastReferencesTest : KotlinLightCodeInsightFixtureTestCase() {
|
||||||
|
|
||||||
|
override fun getProjectDescriptor(): LightProjectDescriptor = KotlinWithJdkAndRuntimeLightProjectDescriptor.INSTANCE
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `test original getter is visible when reference is under renaming`() {
|
||||||
|
|
||||||
|
registerReferenceProviders(testRootDisposable) {
|
||||||
|
registerUastReferenceProvider(injectionHostUExpression(), uastInjectionHostReferenceProvider { _, psiLanguageInjectionHost ->
|
||||||
|
arrayOf(GetterReference("KotlinBean", psiLanguageInjectionHost))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
myFixture.configureByText(
|
||||||
|
"KotlinBean.kt", """
|
||||||
|
data class KotlinBean(val myF<caret>ield: String)
|
||||||
|
|
||||||
|
val reference = "myField"
|
||||||
|
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
|
||||||
|
myFixture.renameElementAtCaret("myRenamedField")
|
||||||
|
|
||||||
|
myFixture.checkResult(
|
||||||
|
"""
|
||||||
|
data class KotlinBean(val myRenamedField: String)
|
||||||
|
|
||||||
|
val reference = "myRenamedField"
|
||||||
|
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private class GetterReference(
|
||||||
|
val className: String,
|
||||||
|
psiElement: PsiElement
|
||||||
|
) : PsiReferenceBase<PsiElement>(psiElement) {
|
||||||
|
override fun resolve(): PsiMethod? {
|
||||||
|
val psiClass = JavaPsiFacade.getInstance(element.project).findClass(className, element.resolveScope) ?: return null
|
||||||
|
val name = element.toUElementOfType<UExpression>()?.evaluateString() ?: return null
|
||||||
|
return PropertyUtil.getGetters(psiClass, name).firstOrNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun handleElementRename(newElementName: String): PsiElement {
|
||||||
|
val resolve = resolve()
|
||||||
|
?: fail("can't resolve during rename, looks like someone renamed or removed the source element before updating references")
|
||||||
|
|
||||||
|
val newName =
|
||||||
|
if (PropertyUtil.getPropertyName(resolve) != null)
|
||||||
|
PropertyUtil.getPropertyName(newElementName) ?: newElementName
|
||||||
|
else newElementName
|
||||||
|
|
||||||
|
return super.handleElementRename(newName)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getVariants(): Array<Any> = emptyArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun registerReferenceProviders(disposable: Disposable, registerContributors: PsiReferenceRegistrar.() -> Unit) {
|
||||||
|
registerReferenceContributor(object : PsiReferenceContributor() {
|
||||||
|
override fun registerReferenceProviders(registrar: PsiReferenceRegistrar) = registrar.registerContributors()
|
||||||
|
}, disposable)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun registerReferenceContributor(contributor: PsiReferenceContributor, disposable: Disposable) {
|
||||||
|
error("PsiReferenceContributorEP is final in 193 platform")
|
||||||
|
// val referenceContributorEp = Extensions.getArea(null).getExtensionPoint<PsiReferenceContributorEP>(PsiReferenceContributor.EP_NAME.name)
|
||||||
|
//
|
||||||
|
// val contributorEp = object : PsiReferenceContributorEP() {
|
||||||
|
// override fun getInstance(): PsiReferenceContributor = contributor
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// referenceContributorEp.registerExtension(contributorEp)
|
||||||
|
//
|
||||||
|
// val application = ApplicationManager.getApplication()
|
||||||
|
//
|
||||||
|
// //we need a fresh ReferenceProvidersRegistry after updating ReferenceContributors
|
||||||
|
// val oldReferenceProviderRegistry =
|
||||||
|
// application.picoContainer.getComponentInstance(ReferenceProvidersRegistry::class.java) as ReferenceProvidersRegistry
|
||||||
|
// application.registerServiceInstance(ReferenceProvidersRegistry::class.java, ReferenceProvidersRegistryImpl())
|
||||||
|
//
|
||||||
|
// Disposer.register(disposable, Disposable {
|
||||||
|
// referenceContributorEp.unregisterExtension(contributorEp)
|
||||||
|
// application.registerServiceInstance(ReferenceProvidersRegistry::class.java, oldReferenceProviderRegistry)
|
||||||
|
// })
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user