diff --git a/.idea/libraries/testng_plugin.xml b/.idea/libraries/testng_plugin.xml
index b4c825d8436..eb3ee15d7d5 100644
--- a/.idea/libraries/testng_plugin.xml
+++ b/.idea/libraries/testng_plugin.xml
@@ -6,6 +6,7 @@
+
diff --git a/idea/idea-test-framework/src/org/jetbrains/kotlin/idea/test/ConfigLibraryUtil.java b/idea/idea-test-framework/src/org/jetbrains/kotlin/idea/test/ConfigLibraryUtil.java
index 9d8c51954de..8c878be20fa 100644
--- a/idea/idea-test-framework/src/org/jetbrains/kotlin/idea/test/ConfigLibraryUtil.java
+++ b/idea/idea-test-framework/src/org/jetbrains/kotlin/idea/test/ConfigLibraryUtil.java
@@ -25,8 +25,10 @@ import com.intellij.openapi.roots.libraries.Library;
import com.intellij.openapi.roots.ui.configuration.libraryEditor.NewLibraryEditor;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.vfs.VfsUtil;
+import kotlin.jvm.functions.Function0;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.idea.js.KotlinJavaScriptLibraryManager;
+import org.jetbrains.kotlin.idea.util.application.ApplicationUtilsKt;
import org.jetbrains.kotlin.utils.PathUtil;
import java.io.File;
@@ -121,42 +123,49 @@ public class ConfigLibraryUtil {
}
- public static void removeLibrary(@NotNull final Module module, @NotNull final String libraryName) {
- ApplicationManager.getApplication().runWriteAction(new Runnable() {
- @Override
- public void run() {
- ModuleRootManager rootManager = ModuleRootManager.getInstance(module);
- ModifiableRootModel model = rootManager.getModifiableModel();
+ public static boolean removeLibrary(@NotNull final Module module, @NotNull final String libraryName) {
+ return ApplicationUtilsKt.runWriteAction(
+ new Function0() {
+ @Override
+ public Boolean invoke() {
+ boolean removed = false;
- for (OrderEntry orderEntry : model.getOrderEntries()) {
- if (orderEntry instanceof LibraryOrderEntry) {
- LibraryOrderEntry libraryOrderEntry = (LibraryOrderEntry) orderEntry;
+ ModuleRootManager rootManager = ModuleRootManager.getInstance(module);
+ ModifiableRootModel model = rootManager.getModifiableModel();
- Library library = libraryOrderEntry.getLibrary();
- if (library != null) {
- String name = library.getName();
- if (name != null && name.equals(libraryName)) {
+ for (OrderEntry orderEntry : model.getOrderEntries()) {
+ if (orderEntry instanceof LibraryOrderEntry) {
+ LibraryOrderEntry libraryOrderEntry = (LibraryOrderEntry) orderEntry;
- // Dispose attached roots
- Library.ModifiableModel modifiableModel = library.getModifiableModel();
- for (String rootUrl : library.getRootProvider().getUrls(OrderRootType.CLASSES)) {
- modifiableModel.removeRoot(rootUrl, OrderRootType.CLASSES);
+ Library library = libraryOrderEntry.getLibrary();
+ if (library != null) {
+ String name = library.getName();
+ if (name != null && name.equals(libraryName)) {
+
+ // Dispose attached roots
+ Library.ModifiableModel modifiableModel = library.getModifiableModel();
+ for (String rootUrl : library.getRootProvider().getUrls(OrderRootType.CLASSES)) {
+ modifiableModel.removeRoot(rootUrl, OrderRootType.CLASSES);
+ }
+ for (String rootUrl : library.getRootProvider().getUrls(OrderRootType.SOURCES)) {
+ modifiableModel.removeRoot(rootUrl, OrderRootType.SOURCES);
+ }
+ modifiableModel.commit();
+
+ model.getModuleLibraryTable().removeLibrary(library);
+
+ removed = true;
+ break;
+ }
}
- for (String rootUrl : library.getRootProvider().getUrls(OrderRootType.SOURCES)) {
- modifiableModel.removeRoot(rootUrl, OrderRootType.SOURCES);
- }
- modifiableModel.commit();
-
- model.getModuleLibraryTable().removeLibrary(library);
-
- break;
}
}
+
+ model.commit();
+
+ return removed;
}
}
-
- model.commit();
- }
- });
+ );
}
}
diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/KotlinAddOrderEntryActionFactory.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/KotlinAddOrderEntryActionFactory.kt
new file mode 100644
index 00000000000..92e99a70a2f
--- /dev/null
+++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/KotlinAddOrderEntryActionFactory.kt
@@ -0,0 +1,58 @@
+/*
+ * 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.idea.quickfix
+
+import com.intellij.codeInsight.daemon.impl.quickfix.OrderEntryFix
+import com.intellij.codeInsight.daemon.impl.quickfix.QuickFixActionRegistrarImpl
+import com.intellij.codeInsight.intention.IntentionAction
+import com.intellij.openapi.util.TextRange
+import com.intellij.psi.PsiElement
+import com.intellij.psi.PsiReference
+import com.intellij.psi.PsiReferenceBase
+import org.jetbrains.kotlin.diagnostics.Diagnostic
+import org.jetbrains.kotlin.idea.references.JetSimpleNameReference.ShorteningMode
+import org.jetbrains.kotlin.idea.references.mainReference
+import org.jetbrains.kotlin.psi.JetElement
+import org.jetbrains.kotlin.psi.JetSimpleNameExpression
+import org.jetbrains.kotlin.psi.psiUtil.getQualifiedElement
+import org.jetbrains.kotlin.psi.psiUtil.startOffset
+
+public object KotlinAddOrderEntryActionFactory : JetIntentionActionsFactory() {
+ override fun doCreateActions(diagnostic: Diagnostic): List? {
+ val simpleExpression = diagnostic.psiElement as? JetSimpleNameExpression ?: return emptyList()
+ val refElement = simpleExpression.getQualifiedElement()
+
+ val reference = object: PsiReferenceBase(refElement) {
+ override fun resolve() = null
+
+ override fun getVariants() = PsiReference.EMPTY_ARRAY
+
+ override fun getRangeInElement(): TextRange? {
+ val offset = simpleExpression.startOffset - refElement.startOffset
+ return TextRange(offset, offset + simpleExpression.textLength)
+ }
+
+ override fun getCanonicalText() = refElement.text
+
+ override fun bindToElement(element: PsiElement): PsiElement {
+ return simpleExpression.mainReference.bindToElement(element, ShorteningMode.FORCED_SHORTENING)
+ }
+ }
+ @Suppress("UNCHECKED_CAST")
+ return OrderEntryFix.registerFixes(QuickFixActionRegistrarImpl(null), reference) as List? ?: emptyList()
+ }
+}
diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt
index 3448bc85c0b..8ec1482dae5 100644
--- a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt
+++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt
@@ -338,5 +338,7 @@ public class QuickFixRegistrar : QuickFixContributor {
CALLABLE_REFERENCE_TO_MEMBER_OR_EXTENSION_WITH_EMPTY_LHS.registerFactory(AddTypeToLHSOfCallableReferenceFix)
DEPRECATED_TYPE_PARAMETER_SYNTAX.registerFactory(MigrateTypeParameterListFix)
+
+ UNRESOLVED_REFERENCE.registerFactory(KotlinAddOrderEntryActionFactory)
}
}
diff --git a/idea/testData/quickfix/libraries/junit.kt b/idea/testData/quickfix/libraries/junit.kt
new file mode 100644
index 00000000000..6a727e42576
--- /dev/null
+++ b/idea/testData/quickfix/libraries/junit.kt
@@ -0,0 +1,14 @@
+// "Add 'JUnit4' to classpath" "true"
+// ERROR: Unresolved reference: Before
+// ERROR: Unresolved reference: junit
+// UNCONFIGURE_LIBRARY: JUnit4
+package some
+
+import org.junit.Before
+
+open class KBase {
+ @Before
+ fun setUp() {
+ throw UnsupportedOperationException()
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/quickfix/libraries/junit.kt.after b/idea/testData/quickfix/libraries/junit.kt.after
new file mode 100644
index 00000000000..2eb50cd02b7
--- /dev/null
+++ b/idea/testData/quickfix/libraries/junit.kt.after
@@ -0,0 +1,14 @@
+// "Add 'JUnit4' to classpath" "true"
+// ERROR: Unresolved reference: Before
+// ERROR: Unresolved reference: junit
+// UNCONFIGURE_LIBRARY: JUnit4
+package some
+
+import org.junit.Before
+
+open class KBase {
+ @Before
+ fun setUp() {
+ throw UnsupportedOperationException()
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/quickfix/libraries/testNG.kt b/idea/testData/quickfix/libraries/testNG.kt
new file mode 100644
index 00000000000..3462c5fda28
--- /dev/null
+++ b/idea/testData/quickfix/libraries/testNG.kt
@@ -0,0 +1,12 @@
+// "Add 'testng' to classpath" "true"
+// ERROR: Unresolved reference: BeforeMethod
+// ERROR: Unresolved reference: testng
+// UNCONFIGURE_LIBRARY: testng
+package some
+
+abstract class KBase {
+ @BeforeMethod
+ fun setUp() {
+ throw UnsupportedOperationException()
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/quickfix/libraries/testNG.kt.after b/idea/testData/quickfix/libraries/testNG.kt.after
new file mode 100644
index 00000000000..f8bbf52fa79
--- /dev/null
+++ b/idea/testData/quickfix/libraries/testNG.kt.after
@@ -0,0 +1,14 @@
+// "Add 'testng' to classpath" "true"
+// ERROR: Unresolved reference: BeforeMethod
+// ERROR: Unresolved reference: testng
+// UNCONFIGURE_LIBRARY: testng
+package some
+
+import org.testng.annotations.BeforeMethod
+
+abstract class KBase {
+ @BeforeMethod
+ fun setUp() {
+ throw UnsupportedOperationException()
+ }
+}
\ No newline at end of file
diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/AbstractQuickFixTest.java b/idea/tests/org/jetbrains/kotlin/idea/quickfix/AbstractQuickFixTest.java
index cf58bf78f39..890dbc61934 100644
--- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/AbstractQuickFixTest.java
+++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/AbstractQuickFixTest.java
@@ -24,6 +24,7 @@ import com.intellij.codeInspection.SuppressableProblemGroup;
import com.intellij.ide.startup.impl.StartupManagerImpl;
import com.intellij.lang.annotation.ProblemGroup;
import com.intellij.openapi.command.CommandProcessor;
+import com.intellij.openapi.module.Module;
import com.intellij.openapi.projectRoots.JavaSdk;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.startup.StartupManager;
@@ -95,8 +96,10 @@ public abstract class AbstractQuickFixTest extends KotlinLightQuickFixTestCase {
@SuppressWarnings({"AssignmentToStaticFieldFromInstanceMethod", "CallToPrintStackTrace"})
@Override
public void run() {
+ String fileText = "";
try {
- String contents = StringUtil.convertLineSeparators(FileUtil.loadFile(testFile, CharsetToolkit.UTF8_CHARSET));
+ fileText = FileUtil.loadFile(testFile, CharsetToolkit.UTF8_CHARSET);
+ String contents = StringUtil.convertLineSeparators(fileText);
quickFixTestCase.configureFromFileText(testFile.getName(), contents);
quickFixTestCase.bringRealEditorBack();
@@ -110,11 +113,21 @@ public abstract class AbstractQuickFixTest extends KotlinLightQuickFixTestCase {
catch (Throwable e) {
e.printStackTrace();
fail(testName);
+ } finally {
+ unconfigureLibrariesAsSpecified(fileText);
}
}
}, "", "");
}
+ private static void unconfigureLibrariesAsSpecified(String fileText) {
+ Module module = getModule();
+ for (String libraryName : InTextDirectivesUtils.findListWithPrefixes(fileText, "// UNCONFIGURE_LIBRARY: ")) {
+ if (ConfigLibraryUtil.removeLibrary(module, libraryName)) continue;
+ fail("Library '" + libraryName + "' wasn't found");
+ }
+ }
+
private static void applyAction(String contents, QuickFixTestCase quickFixTestCase, String testName, String testFullPath) throws Exception {
Pair pair = quickFixTestCase.parseActionHintImpl(quickFixTestCase.getFile(), contents);
String text = pair.getFirst();
diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java
index 2eea42e2e24..db312750c6f 100644
--- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java
+++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java
@@ -4077,6 +4077,27 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
}
}
+ @TestMetadata("idea/testData/quickfix/libraries")
+ @TestDataPath("$PROJECT_ROOT")
+ @RunWith(JUnit3RunnerWithInners.class)
+ public static class Libraries extends AbstractQuickFixTest {
+ public void testAllFilesPresentInLibraries() throws Exception {
+ JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/libraries"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), true);
+ }
+
+ @TestMetadata("junit.kt")
+ public void testJunit() throws Exception {
+ String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/libraries/junit.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("testNG.kt")
+ public void testTestNG() throws Exception {
+ String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/libraries/testNG.kt");
+ doTest(fileName);
+ }
+ }
+
@TestMetadata("idea/testData/quickfix/migration")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)