diff --git a/java/compiler/impl/src/com/intellij/compiler/server/BuildManager.java b/java/compiler/impl/src/com/intellij/compiler/server/BuildManager.java index e13aab0639d5..4a30867e3aa0 100644 --- a/java/compiler/impl/src/com/intellij/compiler/server/BuildManager.java +++ b/java/compiler/impl/src/com/intellij/compiler/server/BuildManager.java @@ -1523,9 +1523,13 @@ public class BuildManager implements Disposable { final CompilerConfiguration config = CompilerConfiguration.getInstance(project); for (Module module : compileContext.getCompileScope().getAffectedModules()) { if (config.getAnnotationProcessingConfiguration(module).isEnabled()) { - final String path = CompilerPaths.getAnnotationProcessorsGenerationPath(module); - if (path != null) { - candidates.add(path); + final String productionPath = CompilerPaths.getAnnotationProcessorsGenerationPath(module, false); + if (productionPath != null) { + candidates.add(productionPath); + } + final String testsPath = CompilerPaths.getAnnotationProcessorsGenerationPath(module, true); + if (testsPath != null) { + candidates.add(testsPath); } } } diff --git a/java/compiler/impl/src/com/intellij/compiler/server/BuildMessageDispatcher.java b/java/compiler/impl/src/com/intellij/compiler/server/BuildMessageDispatcher.java index 2624cde1af17..ed3bbba1709c 100644 --- a/java/compiler/impl/src/com/intellij/compiler/server/BuildMessageDispatcher.java +++ b/java/compiler/impl/src/com/intellij/compiler/server/BuildMessageDispatcher.java @@ -71,7 +71,14 @@ class BuildMessageDispatcher extends SimpleChannelInboundHandlerAdapter() - - private var unmatchedRunConfigurationTests = arrayListOf() - - private val urlSuites = mutableListOf() - private var unmatchedUrlTests = mutableListOf() - + private var testsWithoutSuites = arrayListOf() - fun addSuite(url: String, magnitude: Magnitude, runDate: Date, runConfiguration: RunnerAndConfigurationSettings?) { - val suite = SuiteEntry(url, magnitude, runDate) - if (runConfiguration != null) { - addRunConfigurationSuite(suite, runConfiguration) - } - else { - addUrlSuite(suite) - } + fun addSuite(url: String, runDate: Date, runConfiguration: RunnerAndConfigurationSettings) { + val suite = SuiteEntry(url, runDate) + addRunConfigurationSuite(suite, runConfiguration) } - fun addTest(url: String, magnitude: Magnitude, runDate: Date, runConfiguration: RunnerAndConfigurationSettings?) { - val test = SingleTestEntry(url, magnitude, runDate) - if (runConfiguration != null) { - addRunConfigurationTest(test, runConfiguration) - } - else { - addUrlTest(test) - } + fun addTest(url: String, magnitude: Magnitude, runDate: Date, runConfiguration: RunnerAndConfigurationSettings) { + val test = SingleTestEntry(url, runDate, magnitude) + addRunConfigurationTest(test, runConfiguration) } - private fun addUrlSuite(suite: SuiteEntry) { - val suiteTests = unmatchedUrlTests.filter { suite.isMyTest(it) } - suiteTests.forEach { suite.addTest(it) } - - unmatchedUrlTests = unmatchedUrlTests.filterTo(arrayListOf(), { !suite.isMyTest(it) }) - - urlSuites.add(suite) - } - private fun addRunConfigurationSuite(suite: SuiteEntry, config: RunnerAndConfigurationSettings) { - val suiteTests = unmatchedRunConfigurationTests.filter { suite.isMyTest(it) } - suiteTests.forEach { suite.addTest(it) } + moveSuiteTestsToSuite(suite, config) - unmatchedRunConfigurationTests = unmatchedRunConfigurationTests.filterTo(arrayListOf(), { !suite.isMyTest(it) }) - val id = config.uniqueID - runConfigurationSuites[id]?.addSuite(suite) ?: runConfigurationSuites.put(id, RunConfigurationEntry(config, suite)) + val entry = runConfigurationSuites[id] + if (entry != null) { + entry.addSuite(suite) + } + else { + runConfigurationSuites.put(id, RunConfigurationEntry(config, suite)) + } } - private fun addUrlTest(test: SingleTestEntry) { - findUrlSuite(test.url)?.addTest(test) ?: unmatchedUrlTests.add(test) + private fun moveSuiteTestsToSuite(suite: SuiteEntry, config: RunnerAndConfigurationSettings) { + val filteredTests = arrayListOf() + + testsWithoutSuites.forEach { + if (suite.isMyTest(it.test) && config.name == it.runConfigurationName) { + suite.addTest(it.test) + } + else { + filteredTests.add(it) + } + } + + testsWithoutSuites = filteredTests } private fun addRunConfigurationTest(test: SingleTestEntry, runConfiguration: RunnerAndConfigurationSettings) { - findRunConfigurationSuite(test.url, runConfiguration)?.addTest(test) ?: unmatchedRunConfigurationTests.add(test) + val suiteEntry = findRunConfigurationSuite(test.url, runConfiguration) + if (suiteEntry != null) { + suiteEntry.addTest(test) + } + else { + testsWithoutSuites.add(SingleTestInfo(test, runConfiguration.name)) + } } - - private fun findUrlSuite(url: String) = urlSuites.find { - val testName = VirtualFileManager.extractPath(url) - testName.startsWith(it.suiteName) - } - + private fun findRunConfigurationSuite(url: String, runConfiguration: RunnerAndConfigurationSettings): SuiteEntry? { val pack: RunConfigurationEntry = runConfigurationSuites[runConfiguration.uniqueID] ?: return null val testName = VirtualFileManager.extractPath(url) @@ -99,28 +92,69 @@ class RecentTestsData { return null } - - fun computeConfigurationSuites() = runConfigurationSuites.values - .fold(arrayListOf(), { total: List, entry: RunConfigurationEntry -> total + entry.suites }) fun getTestsToShow(): List { - val allConfigurationSuites = computeConfigurationSuites() - - unmatchedRunConfigurationTests.forEach { - val currentTest = it - allConfigurationSuites.find { it.isMyTest(currentTest) }?.addTest(currentTest) + val allConfigurations = runConfigurationSuites.values + + val allSuites = allConfigurations.fold(arrayListOf(), { list: List, entry -> list + entry.suites }) + testsWithoutSuites.forEach { + val info = it + allSuites.find { it.isMyTest(info.test) }?.let { info.test.suite = it } } - unmatchedUrlTests.forEach { - val currentTest = it - urlSuites.find { it.isMyTest(currentTest) }?.addTest(currentTest) - } + val testsCollector = SingleTestCollector() + allConfigurations.forEach { it.accept(testsCollector) } + val failedTests = testsCollector.tests.filter { it.failed } - return (runConfigurationSuites.values + urlSuites) - .sortedByDescending { it.runDate } - .fold(listOf(), { popupList, currentEntry -> - popupList + currentEntry.getEntriesToShow() - }) + val configsCollector = ConfigurationsCollector() + allConfigurations.forEach { it.accept(configsCollector) } + val passedConfigurations = configsCollector.entries.filter { !it.failed } + + val entriesToShow = failedTests + passedConfigurations + testsWithoutSuites.map { it.test }.filter { it.suite != null && it.failed } + + return entriesToShow.sortedByDescending { it.runDate } } +} + +class UrlsCollector: TestEntryVisitor() { + val urls = mutableListOf() + + override fun visitSuite(suite: SuiteEntry) { + urls.add(suite.suiteUrl) + suite.tests.forEach { urls.add(it.url) } + } + + override fun visitRunConfiguration(configuration: RunConfigurationEntry) { + configuration.suites.forEach { visitSuite(it) } + } +} + + +class SingleTestCollector : TestEntryVisitor() { + val tests = mutableListOf() + override fun visitTest(test: SingleTestEntry) { + tests.add(test) + } + + override fun visitSuite(suite: SuiteEntry) { + suite.tests.forEach { it.accept(this) } + } + + override fun visitRunConfiguration(configuration: RunConfigurationEntry) { + configuration.suites.forEach { it.accept(this) } + } +} + + +class ConfigurationsCollector : TestEntryVisitor() { + val entries = mutableListOf() + + override fun visitRunConfiguration(configuration: RunConfigurationEntry) { + entries.add(configuration) + } + + override fun visitSuite(suite: SuiteEntry) { + entries.add(suite) + } } \ No newline at end of file diff --git a/java/execution/impl/src/com/intellij/testIntegration/RecentTestsListProvider.java b/java/execution/impl/src/com/intellij/testIntegration/RecentTestsListProvider.java index 66ff8617809e..25a83e32f785 100644 --- a/java/execution/impl/src/com/intellij/testIntegration/RecentTestsListProvider.java +++ b/java/execution/impl/src/com/intellij/testIntegration/RecentTestsListProvider.java @@ -98,8 +98,12 @@ public class RecentTestsListProvider { } RunnerAndConfigurationSettings runConfiguration = myConfigurationProvider.getConfiguration(record); + if (runConfiguration == null) { + return; + } + if (TestLocator.isSuite(url)) { - data.addSuite(url, magnitude, record.date, runConfiguration); + data.addSuite(url, record.date, runConfiguration); } else { data.addTest(url, magnitude, record.date, runConfiguration); diff --git a/java/execution/impl/src/com/intellij/testIntegration/RunConfigurationEntry.kt b/java/execution/impl/src/com/intellij/testIntegration/RunConfigurationEntry.kt index a0263b085be6..daea4350491c 100644 --- a/java/execution/impl/src/com/intellij/testIntegration/RunConfigurationEntry.kt +++ b/java/execution/impl/src/com/intellij/testIntegration/RunConfigurationEntry.kt @@ -16,72 +16,76 @@ package com.intellij.testIntegration import com.intellij.execution.RunnerAndConfigurationSettings +import com.intellij.execution.testframework.TestIconMapper import com.intellij.execution.testframework.sm.runner.states.TestStateInfo -import com.intellij.execution.testframework.sm.runner.states.TestStateInfo.Magnitude.* +import com.intellij.execution.testframework.sm.runner.states.TestStateInfo.Magnitude.ERROR_INDEX +import com.intellij.execution.testframework.sm.runner.states.TestStateInfo.Magnitude.FAILED_INDEX +import com.intellij.icons.AllIcons import com.intellij.openapi.vfs.VirtualFileManager -import com.intellij.psi.PsiElement import java.util.* +import javax.swing.Icon + interface RecentTestsPopupEntry { - val runDate: Date - val magnitude: TestStateInfo.Magnitude + val icon: Icon? val presentation: String - - val testsUrls: List - - fun run(runner: RecentTestRunner) - - open fun navigatableElement(locator: TestLocator): PsiElement? = null + val runDate: Date - fun getEntriesToShow(): List + val failed: Boolean + + fun accept(visitor: TestEntryVisitor) } -open class SingleTestEntry(val url: String, - override val magnitude: TestStateInfo.Magnitude, - override val runDate: Date) : RecentTestsPopupEntry + +abstract class TestEntryVisitor { + open fun visitTest(test: SingleTestEntry) = Unit + open fun visitSuite(suite: SuiteEntry) = Unit + open fun visitRunConfiguration(configuration: RunConfigurationEntry) = Unit +} + +class SingleTestEntry(val url: String, + override val runDate: Date, + private val magnitude: TestStateInfo.Magnitude) : RecentTestsPopupEntry { override val presentation = VirtualFileManager.extractPath(url) - override val testsUrls = listOf(url) - - override fun run(runner: RecentTestRunner) { - runner.run(url) + override val icon = TestIconMapper.getIcon(magnitude) + + override val failed = magnitude == ERROR_INDEX || magnitude == FAILED_INDEX + + var suite: SuiteEntry? = null + + override fun accept(visitor: TestEntryVisitor) { + visitor.visitTest(this) } - - override fun navigatableElement(locator: TestLocator) = locator.getLocation(url)?.psiElement - - override fun getEntriesToShow(): List = listOf(this) } -class SuiteEntry(url: String, magnitude: TestStateInfo.Magnitude, runDate: Date) : SingleTestEntry(url, magnitude, runDate) { - - private val tests = hashSetOf() - override val testsUrls: List - get() = tests.fold(listOf(), { acc, testEntry -> acc + testEntry.testsUrls }) - - val suiteName = VirtualFileManager.extractPath(url) - - val failedTests: List - get() = tests.filter { it.magnitude == FAILED_INDEX || it.magnitude == ERROR_INDEX } - - fun addTest(info: SingleTestEntry) = tests.add(info) +class SuiteEntry(val suiteUrl: String, override val runDate: Date) : RecentTestsPopupEntry { - override val presentation = suiteName - - override fun getEntriesToShow(): List { - val failed = failedTests - if (failed.size > 0) { - return failed.sortedByDescending { it.runDate } + this + val tests = hashSetOf() + val suiteName = VirtualFileManager.extractPath(suiteUrl) + + var runConfiguration: RunConfigurationEntry? = null + + override val presentation = VirtualFileManager.extractPath(suiteUrl) + override val icon = AllIcons.RunConfigurations.Junit + + override val failed: Boolean + get() { + return tests.find { it.failed } != null } - return listOf(this) - } - override val magnitude: TestStateInfo.Magnitude by lazy { - tests.find { it.magnitude != PASSED_INDEX && it.magnitude != COMPLETE_INDEX }?.magnitude ?: PASSED_INDEX + fun addTest(test: SingleTestEntry) { + tests.add(test) + test.suite = this } - + + override fun accept(visitor: TestEntryVisitor) { + visitor.visitSuite(this) + } + } @@ -93,34 +97,28 @@ class RunConfigurationEntry(val runSettings: RunnerAndConfigurationSettings, ini addSuite(initial) } - fun addSuite(s: SuiteEntry) = suites.add(s) + override val runDate: Date + get() { + return suites.minBy { it.runDate }!!.runDate + } + + + override val failed: Boolean + get() { + return suites.find { it.failed } != null + } - override val runDate = suites.map { it.runDate }.min()!! - - override val magnitude: TestStateInfo.Magnitude by lazy { - suites.find { it.magnitude != PASSED_INDEX && it.magnitude != COMPLETE_INDEX }?.magnitude ?: PASSED_INDEX + fun addSuite(suite: SuiteEntry) { + suites.add(suite) + suite.runConfiguration = this } override val presentation = runSettings.name - override val testsUrls: List - get() = suites.fold(listOf(), { list, suite -> list + suite.testsUrls }) - - override fun run(runner: RecentTestRunner) { - runner.run(runSettings) - } + override val icon = AllIcons.RunConfigurations.Junit - override fun getEntriesToShow(): List { - if (suites.size == 1) { - return suites[0].getEntriesToShow() - } - - return suites - .filter { it.failedTests.size > 0} - .sortedByDescending { it.runDate } - .fold(listOf(), { popupList, currentEntry -> - popupList + currentEntry.getEntriesToShow() - }) + this + override fun accept(visitor: TestEntryVisitor) { + visitor.visitRunConfiguration(this) } -} +} \ No newline at end of file diff --git a/java/execution/impl/src/com/intellij/testIntegration/SelectTestStep.kt b/java/execution/impl/src/com/intellij/testIntegration/SelectTestStep.kt index 73e58109d227..d2a5b85a67ff 100644 --- a/java/execution/impl/src/com/intellij/testIntegration/SelectTestStep.kt +++ b/java/execution/impl/src/com/intellij/testIntegration/SelectTestStep.kt @@ -15,12 +15,13 @@ */ package com.intellij.testIntegration -import com.intellij.execution.testframework.TestIconMapper +import com.intellij.icons.AllIcons import com.intellij.openapi.keymap.MacKeymapUtil import com.intellij.openapi.ui.popup.ListPopupStep import com.intellij.openapi.ui.popup.PopupStep import com.intellij.openapi.ui.popup.util.BaseListPopupStep import com.intellij.openapi.util.SystemInfo +import com.intellij.psi.PsiElement import com.intellij.ui.popup.list.ListPopupImpl import com.intellij.util.PsiNavigateUtil import java.awt.event.ActionEvent @@ -31,9 +32,7 @@ import javax.swing.KeyStroke class RecentTestsListPopup(popupStep: ListPopupStep, private val testRunner: RecentTestRunner, - private val locator: TestLocator) - : ListPopupImpl(popupStep) -{ + private val locator: TestLocator) : ListPopupImpl(popupStep) { init { shiftReleased() @@ -58,16 +57,36 @@ class RecentTestsListPopup(popupStep: ListPopupStep, override fun actionPerformed(e: ActionEvent) { val values = selectedValues if (values.size == 1) { - val element = (values[0] as RecentTestsPopupEntry).navigatableElement(locator) - if (element != null) { + val entry = values[0] as RecentTestsPopupEntry + getElement(entry)?.let { cancel() - PsiNavigateUtil.navigate(element) + PsiNavigateUtil.navigate(it) } } } }) } + private fun getElement(entry: RecentTestsPopupEntry): PsiElement? { + var element: PsiElement? = null + entry.accept(object : TestEntryVisitor() { + override fun visitTest(test: SingleTestEntry) { + element = locator.getLocation(test.url)?.psiElement + } + + override fun visitSuite(suite: SuiteEntry) { + element = locator.getLocation(suite.suiteUrl)?.psiElement + } + + override fun visitRunConfiguration(configuration: RunConfigurationEntry) { + if (configuration.suites.size == 1) { + visitSuite(configuration.suites[0]) + } + } + }) + return element + } + private fun shiftPressed() { setCaption("Debug Recent Tests") testRunner.setMode(RecentTestRunner.Mode.DEBUG) @@ -80,22 +99,99 @@ class RecentTestsListPopup(popupStep: ListPopupStep, } -class SelectTestStep(tests: List, - private val runner: RecentTestRunner) - : BaseListPopupStep("Debug Recent Tests", tests) +class SelectTestStep(title: String?, + tests: List, + private val runner: RecentTestRunner) : BaseListPopupStep(title, tests) { override fun getIconFor(value: RecentTestsPopupEntry): Icon? { - return TestIconMapper.getIcon(value.magnitude) + if (value is SingleTestEntry) { + return AllIcons.RunConfigurations.TestFailed + } + else { + return AllIcons.RunConfigurations.TestPassed + } } override fun getTextFor(value: RecentTestsPopupEntry) = value.presentation override fun isSpeedSearchEnabled() = true + override fun hasSubstep(selectedValue: RecentTestsPopupEntry) = getConfigurations(selectedValue).isNotEmpty() + override fun onChosen(entry: RecentTestsPopupEntry, finalChoice: Boolean): PopupStep? { - entry.run(runner) - return null + if (finalChoice) { + runner.run(entry) + return null + } + + val configurations = getConfigurations(entry) + return SelectConfigurationStep(configurations, runner) + } + + private fun getConfigurations(entry: RecentTestsPopupEntry): List { + val items = mutableListOf() + + entry.accept(object : TestEntryVisitor() { + override fun visitTest(test: SingleTestEntry) { + val suite = test.suite ?: return + val configuration = suite.runConfiguration + if (configuration == null) { + items.add(suite) + return + } + + if (isSingleTestConfiguration(configuration)) { + items.add(suite) + return + } + + items.add(configuration) + if (configuration.suites.size > 1) { + items.add(0, suite) + } + } + + private fun isSingleTestConfiguration(configuration: RunConfigurationEntry): Boolean { + val suites = configuration.suites + return suites.size == 1 && suites[0].tests.size == 1 + } + + }) + + return items } } + + +class SelectConfigurationStep(private val items: List, + private val runner: RecentTestRunner) + : BaseListPopupStep(null, items) +{ + + override fun getTextFor(value: RecentTestsPopupEntry): String { + var presentation = value.presentation + value.accept(object : TestEntryVisitor() { + override fun visitSuite(suite: SuiteEntry) { + presentation = "[suite] " + presentation + } + + override fun visitRunConfiguration(configuration: RunConfigurationEntry) { + presentation = "[configuration] " + presentation + } + }) + return presentation + } + + override fun getIconFor(value: RecentTestsPopupEntry?) = AllIcons.RunConfigurations.Junit + + override fun onChosen(selectedValue: RecentTestsPopupEntry, finalChoice: Boolean): PopupStep? { + if (finalChoice) { + runner.run(selectedValue) + } + + return null + } + +} \ No newline at end of file diff --git a/java/execution/impl/src/com/intellij/testIntegration/ShowRecentTests.java b/java/execution/impl/src/com/intellij/testIntegration/ShowRecentTests.java index 2807216931c7..81ceea8be303 100644 --- a/java/execution/impl/src/com/intellij/testIntegration/ShowRecentTests.java +++ b/java/execution/impl/src/com/intellij/testIntegration/ShowRecentTests.java @@ -22,7 +22,6 @@ import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.project.Project; import com.intellij.util.Time; -import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import java.util.Date; @@ -60,7 +59,7 @@ public class ShowRecentTests extends AnAction { List entries = listProvider.getTestsToShow(); - SelectTestStep selectStepTest = new SelectTestStep(entries, testRunner); + SelectTestStep selectStepTest = new SelectTestStep("Debug Recent Tests", entries, testRunner); RecentTestsListPopup popup = new RecentTestsListPopup(selectStepTest, testRunner, testLocator); popup.showCenteredInCurrentWindow(project); @@ -69,9 +68,9 @@ public class ShowRecentTests extends AnAction { } private static void cleanDeadTests(List entries, TestLocator testLocator, TestStateStorage testStorage) { - List urls = ContainerUtil.newArrayList(); - entries.forEach((entry) -> urls.addAll(entry.getTestsUrls())); - ApplicationManager.getApplication().executeOnPooledThread(new DeadTestsCleaner(testStorage, urls, testLocator)); + UrlsCollector collector = new UrlsCollector(); + entries.forEach((e) -> e.accept(collector)); + ApplicationManager.getApplication().executeOnPooledThread(new DeadTestsCleaner(testStorage, collector.getUrls(), testLocator)); } } diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/SimplifyBooleanExpressionFix.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/SimplifyBooleanExpressionFix.java index e94b653a87c5..26074ccd50ad 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/SimplifyBooleanExpressionFix.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/SimplifyBooleanExpressionFix.java @@ -29,10 +29,9 @@ import com.intellij.psi.codeStyle.CodeStyleManager; import com.intellij.psi.tree.IElementType; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtil; -import com.intellij.util.Function; import com.intellij.util.IncorrectOperationException; import com.siyeh.ig.psiutils.ParenthesesUtils; -import com.siyeh.ig.psiutils.VariableSearchUtils; +import com.siyeh.ig.psiutils.DeclarationSearchUtils; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -131,7 +130,7 @@ public class SimplifyBooleanExpressionFix extends LocalQuickFixOnPsiElement { PsiElement parent = orig.getParent(); if (parent == null) return; if (statement instanceof PsiBlockStatement && parent instanceof PsiCodeBlock && - !VariableSearchUtils.containsConflictingDeclarations(((PsiBlockStatement)statement).getCodeBlock(), (PsiCodeBlock)parent)) { + !DeclarationSearchUtils.containsConflictingDeclarations(((PsiBlockStatement)statement).getCodeBlock(), (PsiCodeBlock)parent)) { // See IDEADEV-24277 // Code block can only be inlined into another (parent) code block. // Code blocks, which are if or loop statement branches should not be inlined. diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/miscGenerics/MakeTypeGenericInspection.java b/java/java-analysis-impl/src/com/intellij/codeInspection/miscGenerics/MakeTypeGenericInspection.java index 09b86b2fe9e5..60c8a24e3503 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/miscGenerics/MakeTypeGenericInspection.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/miscGenerics/MakeTypeGenericInspection.java @@ -37,7 +37,8 @@ public class MakeTypeGenericInspection extends BaseJavaBatchLocalInspectionTool @Override public void visitVariable(PsiVariable variable) { super.visitVariable(variable); - if (variable.getTypeElement() != null) { + final PsiTypeElement variableTypeElement = variable.getTypeElement(); + if (variableTypeElement != null) { final PsiType type = getSuggestedType(variable); if (type != null) { final String typeText = type.getCanonicalText(); @@ -46,8 +47,8 @@ public class MakeTypeGenericInspection extends BaseJavaBatchLocalInspectionTool final PsiElement beforeInitializer = PsiTreeUtil.skipSiblingsBackward(variable.getInitializer(), PsiWhiteSpace.class, PsiComment.class); final ProblemDescriptor descriptor = - holder.getManager().createProblemDescriptor(variable.getTypeElement(), - beforeInitializer != null ? beforeInitializer : variable.getTypeElement(), + holder.getManager().createProblemDescriptor(variableTypeElement, + beforeInitializer != null ? beforeInitializer : variableTypeElement, message, ProblemHighlightType.GENERIC_ERROR_OR_WARNING, isOnTheFly, new MyLocalQuickFix(message)); holder.registerProblem(descriptor); diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ReplaceIteratorForEachLoopWithIteratorForLoopFix.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ReplaceIteratorForEachLoopWithIteratorForLoopFix.java index 2d71379b96a0..df5ffe1b6976 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ReplaceIteratorForEachLoopWithIteratorForLoopFix.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ReplaceIteratorForEachLoopWithIteratorForLoopFix.java @@ -42,14 +42,14 @@ public class ReplaceIteratorForEachLoopWithIteratorForLoopFix implements Intenti @NotNull @Override public String getText() { - return "Replace 'for each' loop with iterator 'for' loop"; + return getFamilyName(); } @Nls @NotNull @Override public String getFamilyName() { - return getText(); + return "Replace 'for each' loop with iterator 'for' loop"; } @Override @@ -112,9 +112,11 @@ public class ReplaceIteratorForEachLoopWithIteratorForLoopFix implements Intenti newForLoop = (PsiForStatement)styleManager.reformat(newForLoop); if (forEachBody instanceof PsiBlockStatement) { - final PsiStatement[] statements = ((PsiBlockStatement)forEachBody).getCodeBlock().getStatements(); - for (int i = statements.length - 1; i >= 0; i--) { - newBodyBlock.addAfter(statements[i], newFirstStatement); + final PsiCodeBlock bodyCodeBlock = ((PsiBlockStatement)forEachBody).getCodeBlock(); + final PsiElement firstBodyElement = bodyCodeBlock.getFirstBodyElement(); + final PsiElement lastBodyElement = bodyCodeBlock.getLastBodyElement(); + if (firstBodyElement != null && lastBodyElement != null) { + newBodyBlock.addRangeAfter(firstBodyElement, lastBodyElement, newFirstStatement); } } else if (forEachBody != null && !(forEachBody instanceof PsiEmptyStatement)) { diff --git a/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/HierarchyServiceImpl.java b/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/HierarchyServiceImpl.java index 50e99120097c..d0d171d7fd2a 100644 --- a/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/HierarchyServiceImpl.java +++ b/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/HierarchyServiceImpl.java @@ -79,14 +79,14 @@ public class HierarchyServiceImpl extends HierarchyService { } private void loadUnits(BitSet files, int[] indexKeys, StubEnter stubEnter) { - FileBasedIndexImpl.IdValueProcessor processor = new FileBasedIndexImpl.IdValueProcessor() { + FileBasedIndexImpl.IdValueProcessor processor = new FileBasedIndexImpl.IdValueProcessor() { final ProgressIndicator indicator = ProgressIndicatorProvider.getGlobalProgressIndicator(); int count = 0; @Override - public boolean process(int fileId, IndexTree.Unit unit) { + public boolean process(int fileId, SerializedUnit unit) { if (indicator != null && ++count % 128 == 0) indicator.checkCanceled(); if (files.get(fileId)) { - stubEnter.unitEnter(unit, fileId); + unit.readUnit(stubEnter, fileId); } return true; } diff --git a/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/Imports.java b/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/Imports.java index c5b45aa8287f..baded25636ca 100644 --- a/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/Imports.java +++ b/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/Imports.java @@ -15,10 +15,16 @@ */ package com.intellij.psi.stubsHierarchy.impl; +import com.intellij.psi.impl.java.stubs.hierarchy.IndexTree; import com.intellij.util.ArrayUtil; import com.intellij.util.BitUtil; +import com.intellij.util.io.DataInputOutputUtil; +import org.jetbrains.annotations.NotNull; -public class Imports { +import java.io.DataOutput; +import java.io.IOException; + +class Imports { public final static long[] EMPTY_ARRAY = ArrayUtil.EMPTY_LONG_ARRAY; public static final int onDemandMask = 1 << 29; @@ -30,11 +36,7 @@ public class Imports { return (int)(importMask >>> 32); } - public static QualifiedName getFullName(long importMask, NameEnvironment nameEnvironment) { - return nameEnvironment.qualifiedName(getFullNameId(importMask)); - } - - private static int getFullNameId(long importMask) { + static int getFullNameId(long importMask) { int fullNameId = (int)importMask; fullNameId &= mask; return fullNameId; @@ -48,10 +50,52 @@ public class Imports { return BitUtil.isSet(importMask, staticMask); } - public static long mkImport(QualifiedName fullname, boolean importStatic, boolean onDemand, int alias) { - long lower = fullname.myId; + public static long mkImport(@QNameId int fullname, boolean importStatic, boolean onDemand, int alias) { + long lower = fullname; if (importStatic) lower |= staticMask; if (onDemand) lower |= onDemandMask; return (((long)alias) << 32) | lower; } + + private final static int IS_STATIC = 1; + private final static int IS_ON_DEMAND = 2; + private final static int HAS_ALIAS = 4; + + static void writeImports(@NotNull DataOutput out, IndexTree.Unit value) throws IOException { + DataInputOutputUtil.writeINT(out, value.imports.length); + for (IndexTree.Import anImport : value.imports) { + writeImport(out, anImport); + } + } + + static long[] readImports(UnitInputStream in) throws IOException { + int importCount = DataInputOutputUtil.readINT(in); + long[] imports = importCount == 0 ? EMPTY_ARRAY : new long[importCount]; + for (int i = 0; i < importCount; i++) { + imports[i] = readImport(in); + } + return imports; + } + + private static void writeImport(@NotNull DataOutput out, IndexTree.Import anImport) throws IOException { + SerializedUnit.writeQualifiedName(out, anImport.myFullname); + boolean hasAlias = anImport.myAlias != 0; + int flags = 0; + flags = BitUtil.set(flags, IS_STATIC, anImport.myStaticImport); + flags = BitUtil.set(flags, IS_ON_DEMAND, anImport.myOnDemand); + flags = BitUtil.set(flags, HAS_ALIAS, hasAlias); + out.writeByte(flags); + if (hasAlias) { + out.writeInt(anImport.myAlias); + } + } + + private static long readImport(UnitInputStream in) throws IOException { + int fullname = in.names.readQualifiedName(in); + int flags = in.readByte(); + return mkImport(fullname, + BitUtil.isSet(flags, IS_STATIC), BitUtil.isSet(flags, IS_ON_DEMAND), + BitUtil.isSet(flags, HAS_ALIAS) ? in.readInt() : 0); + } + } diff --git a/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/JavaStubIndexer.java b/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/JavaStubIndexer.java index 616ea7213f3c..5a3c7a8c75fb 100644 --- a/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/JavaStubIndexer.java +++ b/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/JavaStubIndexer.java @@ -28,6 +28,7 @@ import com.intellij.psi.impl.java.stubs.*; import com.intellij.psi.impl.java.stubs.hierarchy.IndexTree; import com.intellij.psi.impl.java.stubs.hierarchy.IndexTree.*; import com.intellij.psi.impl.java.stubs.impl.PsiClassStubImpl; +import com.intellij.psi.impl.source.JavaFileElementType; import com.intellij.psi.stubs.Stub; import com.intellij.psi.stubs.StubElement; import com.intellij.psi.stubs.StubTree; @@ -47,7 +48,7 @@ public class JavaStubIndexer extends StubHierarchyIndexer { @Override public int getVersion() { - return 0; + return JavaFileElementType.STUB_VERSION + 1; } @Override @@ -95,10 +96,7 @@ public class JavaStubIndexer extends StubHierarchyIndexer { @Nullable private static Decl processMember(StubElement el, Set namesCache) { if (el instanceof PsiClassStubImpl) { - PsiClassStubImpl classStub = (PsiClassStubImpl)el; - if (!classStub.isAnonymousInQualifiedNew()) { - return processClassDecl(classStub, namesCache); - } + return processClassDecl((PsiClassStubImpl)el, namesCache); } ArrayList innerList = new ArrayList(); for (StubElement childElement : el.getChildrenStubs()) { @@ -146,6 +144,9 @@ public class JavaStubIndexer extends StubHierarchyIndexer { } int flags = translateFlags(classStub, accessModifiers); + if (classStub.isAnonymousInQualifiedNew()) { + flags |= IndexTree.SUPERS_UNRESOLVED; + } String[] supers = superList.isEmpty() ? ArrayUtil.EMPTY_STRING_ARRAY : ArrayUtil.toStringArray(superList); Decl[] inners = innerList.isEmpty() ? Decl.EMPTY_ARRAY : innerList.toArray(new Decl[innerList.size()]); return new ClassDecl(classStub.id, flags, classStub.getName(), supers, inners); diff --git a/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/NameEnvironment.java b/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/NameEnvironment.java index 4a2d5f3402a9..048d6fbc9682 100644 --- a/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/NameEnvironment.java +++ b/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/NameEnvironment.java @@ -16,60 +16,92 @@ package com.intellij.psi.stubsHierarchy.impl; import com.intellij.openapi.util.UserDataHolderBase; +import com.intellij.psi.CommonClassNames; import com.intellij.psi.impl.java.stubs.hierarchy.IndexTree; -import org.jetbrains.annotations.Nullable; +import com.intellij.util.io.DataInputOutputUtil; +import gnu.trove.TIntArrayList; +import gnu.trove.TLongIntHashMap; -import java.util.Arrays; +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; -public class NameEnvironment extends UserDataHolderBase { +class NameEnvironment extends UserDataHolderBase { public static final int OBJECT_NAME = IndexTree.hashIdentifier("Object"); - public final QualifiedName empty; - public final QualifiedName java_lang; + public static final int NO_NAME = 0; + @QNameId public final int java_lang; public final QualifiedName java_lang_Enum; - public final QualifiedName annotation; - public final NamesEnumerator myNamesEnumerator; + public final QualifiedName java_lang_annotation_Annotation; - public NameEnvironment() { - myNamesEnumerator = new NamesEnumerator(); - empty = myNamesEnumerator.getFullName(new int[]{}, true); - java_lang = fromString("java.lang", true); - java_lang_Enum = fromString("java.lang.Enum", true); - annotation = fromString("java.lang.annotation.Annotation", true); + private final TIntArrayList mySuffixes = new TIntArrayList(); + private final TIntArrayList myStems = new TIntArrayList(); + private final TLongIntHashMap myConcatenations = new TLongIntHashMap(); + + NameEnvironment() { + mySuffixes.add(0); + myStems.add(0); + + java_lang = fromString("java.lang"); + java_lang_Enum = new QualifiedName(fromString(CommonClassNames.JAVA_LANG_ENUM)); + java_lang_annotation_Annotation = new QualifiedName(fromString(CommonClassNames.JAVA_LANG_ANNOTATION_ANNOTATION)); } - @Nullable - public QualifiedName fromString(String s, boolean create) { - return myNamesEnumerator.getFullName(IndexTree.hashQualifiedName(s), create); + @QNameId + int fromString(String s) { + return internQualifiedName(IndexTree.hashQualifiedName(s)); } - public QualifiedName prefix(QualifiedName name) { - if (name.myComponents.length <= 1) { - return empty; + @QNameId int prefixId(@QNameId int nameId) { + return myStems.get(nameId); + } + + @ShortName int shortName(@QNameId int id) { + return mySuffixes.get(id); + } + + @QNameId int findExistingName(@QNameId int stemId, @ShortName int suffix) { + int existing = myConcatenations.get(pack(stemId, suffix)); + return existing > 0 ? existing : -1; + } + + @QNameId int internQualifiedName(@ShortName int[] qname) { + int id = 0; + for (int shortName : qname) { + id = qualifiedName(id, shortName); } - return myNamesEnumerator.getFullName(Arrays.copyOf(name.myComponents, name.myComponents.length - 1), true); + return id; } - public QualifiedName qualifiedName(int id) { - return myNamesEnumerator.qualifiedName(id); + /** + * @see SerializedUnit#writeQualifiedName(DataOutput, int[]) + */ + @QNameId int readQualifiedName(DataInput in) throws IOException { + int id = 0; + int len = DataInputOutputUtil.readINT(in); + for (int i = 0; i < len; i++) { + id = qualifiedName(id, in.readInt()); + } + return id; } - public int shortName(QualifiedName name) { - int[] ids = name.myComponents; - return ids[ids.length - 1]; + int memberQualifiedName(@QNameId int ownerName, @ShortName int name) { + return name == NO_NAME || ownerName < 0 ? -1 : qualifiedName(ownerName, name); } - public QualifiedName qualifiedName(QualifiedName prefix, int shortName, boolean create) { - if (shortName == NamesEnumerator.NO_NAME) - return null; - if (prefix == null || prefix.isEmpty()) - return myNamesEnumerator.getFullName(new int[]{shortName}, create); - - int[] ids = Arrays.copyOf(prefix.myComponents, prefix.myComponents.length + 1); - ids[ids.length - 1] = shortName; - return myNamesEnumerator.getFullName(ids, create); + @QNameId int qualifiedName(@QNameId int prefix, @ShortName int shortName) { + int existing = findExistingName(prefix, shortName); + return existing >= 0 ? existing : addName(prefix, shortName); } - QualifiedName concat(int[] ids, boolean create) { - return myNamesEnumerator.getFullName(ids, create); + private int addName(@QNameId int stemId, @ShortName int suffix) { + int newId = mySuffixes.size(); + mySuffixes.add(suffix); + myStems.add(stemId); + myConcatenations.put(pack(stemId, suffix), newId); + return newId; + } + + private static long pack(@QNameId int stemId, @ShortName int suffix) { + return ((long)suffix << 32) + stemId; } } diff --git a/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/NamesEnumerator.java b/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/NamesEnumerator.java deleted file mode 100644 index cde21cf6ca45..000000000000 --- a/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/NamesEnumerator.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2000-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 com.intellij.psi.stubsHierarchy.impl; - -import gnu.trove.TObjectHashingStrategy; -import gnu.trove.TObjectIntHashMap; - -import java.util.Arrays; - -public class NamesEnumerator { - final static int NO_NAME = 0; - - private final TObjectIntHashMap myFullNameMap = new TObjectIntHashMap(new TObjectHashingStrategy() { - @Override - public int computeHashCode(int[] object) { - return Arrays.hashCode(object); - } - - @Override - public boolean equals(int[] o1, int[] o2) { - return Arrays.equals(o1, o2); - } - }); - private QualifiedName[] myQualifiedNames = new QualifiedName[0x8000]; - - QualifiedName qualifiedName(int id) { - return myQualifiedNames[id]; - } - - public QualifiedName getFullName(int[] ids, boolean create) { - int id = myFullNameMap.get(ids); - if (id == 0 && create) { - id = myFullNameMap.size() + 1; - myFullNameMap.put(ids, id); - ensureFullCapacity(id); - myQualifiedNames[id] = new QualifiedName(id, ids); - } - return myQualifiedNames[id]; - } - - - private void ensureFullCapacity(int maxIndex) { - if (maxIndex >= myQualifiedNames.length) { - int newLength = calculateNewLength(myQualifiedNames.length, maxIndex); - QualifiedName[] names1 = new QualifiedName[newLength]; - System.arraycopy(myQualifiedNames, 0, names1, 0, myQualifiedNames.length); - myQualifiedNames = names1; - } - } - - private static int calculateNewLength(int currentLength, int maxIndex) { - while (currentLength < maxIndex + 1) - currentLength *= 2; - return currentLength; - } - -} diff --git a/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/QualifiedName.java b/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/QualifiedName.java index 2756a2edfebb..575bb59f7b8c 100644 --- a/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/QualifiedName.java +++ b/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/QualifiedName.java @@ -15,33 +15,21 @@ */ package com.intellij.psi.stubsHierarchy.impl; -import java.util.Arrays; - -public final class QualifiedName { - public static final QualifiedName[] EMPTY_ARRAY = new QualifiedName[0]; - - // unique id of this full name +final class QualifiedName { public final int myId; - // ids of parts of this name - public final int[] myComponents; - QualifiedName(int id, int[] components) { + QualifiedName(int id) { this.myId = id; - this.myComponents = components; } public boolean isEmpty() { - return myComponents.length == 0; + return myId == 0; } @Override public boolean equals(Object o) { if (this == o) return true; - if (o instanceof QualifiedName) { - QualifiedName other = (QualifiedName)o; - return myId == other.myId && Arrays.equals(myComponents, other.myComponents); - } - return false; + return o instanceof QualifiedName && myId == ((QualifiedName)o).myId; } @Override diff --git a/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/SerializedUnit.java b/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/SerializedUnit.java new file mode 100644 index 000000000000..492f30a8fe03 --- /dev/null +++ b/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/SerializedUnit.java @@ -0,0 +1,217 @@ +/* + * 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 com.intellij.psi.stubsHierarchy.impl; + +import com.intellij.psi.impl.java.stubs.hierarchy.IndexTree; +import com.intellij.psi.stubsHierarchy.impl.Symbol.ClassSymbol; +import com.intellij.psi.stubsHierarchy.impl.Symbol.MemberSymbol; +import com.intellij.psi.stubsHierarchy.impl.Symbol.PackageSymbol; +import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.io.DataInputOutputUtil; +import com.intellij.util.io.DataOutputStream; +import com.intellij.util.io.UnsyncByteArrayInputStream; +import com.intellij.util.io.UnsyncByteArrayOutputStream; +import org.jetbrains.annotations.NotNull; + +import java.io.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * @author peter + */ +class SerializedUnit { + private final byte[] myBytes; + + SerializedUnit(byte[] bytes) { + myBytes = bytes; + } + + SerializedUnit(IndexTree.Unit unit) { + try { + //noinspection IOResourceOpenedButNotSafelyClosed + UnsyncByteArrayOutputStream stream = new UnsyncByteArrayOutputStream(); + writeUnit(new DataOutputStream(stream), unit); + myBytes = stream.toByteArray(); + } + catch (IOException impossible) { + throw new RuntimeException(impossible); + } + } + + byte[] getSerializedBytes() { + return myBytes; + } + + void readUnit(StubEnter stubEnter, int fileId) { + try { + enterUnit(new UnitInputStream(new UnsyncByteArrayInputStream(myBytes), fileId, stubEnter)); + } + catch (IOException impossible) { + throw new RuntimeException(impossible); + } + } + + /** + * @see NameEnvironment#readQualifiedName(DataInput) + */ + static void writeQualifiedName(DataOutput out, @QNameId int[] array) throws IOException { + DataInputOutputUtil.writeINT(out, array.length); + for (int i : array) { + out.writeInt(i); + } + } + + // unit + + private static void writeUnit(@NotNull DataOutput out, IndexTree.Unit value) throws IOException { + writeQualifiedName(out, value.myPackageName); + out.writeByte(value.myUnitType); + if (value.myUnitType != IndexTree.BYTECODE) { + Imports.writeImports(out, value); + } + // class Declaration + DataInputOutputUtil.writeINT(out, value.myDecls.length); + for (IndexTree.ClassDecl def : value.myDecls) { + saveClassDecl(out, def); + } + } + + private static void enterUnit(UnitInputStream in) throws IOException { + PackageSymbol pkg = in.stubEnter.enterPackage(in); + byte type = in.readByte(); + long[] imports = type == IndexTree.BYTECODE ? Imports.EMPTY_ARRAY : Imports.readImports(in); + UnitInfo unitInfo = UnitInfo.mkUnitInfo(type, imports); + + int classCount = DataInputOutputUtil.readINT(in); + for (int i = 0; i < classCount; i++) { + readClassDecl(in, unitInfo, pkg, pkg.myQualifiedName); + } + } + + // class + + private static void saveClassDecl(@NotNull DataOutput out, IndexTree.ClassDecl value) throws IOException { + DataInputOutputUtil.writeINT(out, value.myStubId); + DataInputOutputUtil.writeINT(out, value.myMods); + out.writeInt(value.myName); + writeSupers(out, value); + writeMembers(out, value.myDecls); + } + + private static ClassSymbol readClassDecl(UnitInputStream in, UnitInfo info, Symbol owner, @QNameId int ownerName) throws IOException { + int stubId = DataInputOutputUtil.readINT(in); + int mods = DataInputOutputUtil.readINT(in); + @ShortName int name = in.readInt(); + @QNameId int[] superNames = readSupers(in); + + @QNameId int qname = in.names.memberQualifiedName(ownerName, name); + ClassSymbol symbol = in.stubEnter.classEnter(info, owner, stubId, mods, name, superNames, qname, in.fileId); + + readMembers(in, info, qname, symbol); + return symbol; + } + + // supers + + private static void writeSupers(@NotNull DataOutput out, IndexTree.ClassDecl value) throws IOException { + DataInputOutputUtil.writeINT(out, value.mySupers.length); + for (int[] aSuper : value.mySupers) { + writeQualifiedName(out, aSuper); + } + } + + private static @QNameId int[] readSupers(UnitInputStream in) throws IOException { + @QNameId int[] superNames = new int[DataInputOutputUtil.readINT(in)]; + for (int i = 0; i < superNames.length; i++) { + superNames[i] = in.names.readQualifiedName(in); + } + return superNames; + } + + // members + + private static void writeMembers(@NotNull DataOutput out, IndexTree.Decl[] decls) throws IOException { + DataInputOutputUtil.writeINT(out, decls.length); + for (IndexTree.Decl def : decls) { + saveDecl(out, def); + } + } + + private static void readMembers(UnitInputStream in, + UnitInfo info, + @QNameId int ownerName, + MemberSymbol symbol) throws IOException { + int memberCount = DataInputOutputUtil.readINT(in); + if (memberCount == 0) return; + + List members = new ArrayList<>(); + for (int i = 0; i < memberCount; i++) { + ContainerUtil.addIfNotNull(members, readDecl(in, info, symbol, ownerName)); + } + symbol.setMembers(members); + } + + // decl: class or member + + private static void saveDecl(@NotNull DataOutput out, IndexTree.Decl value) throws IOException { + if (value instanceof IndexTree.ClassDecl) { + out.writeBoolean(true); + saveClassDecl(out, (IndexTree.ClassDecl)value); + } else if (value instanceof IndexTree.MemberDecl) { + out.writeBoolean(false); + writeMembers(out, ((IndexTree.MemberDecl)value).myDecls); + } + } + + private static ClassSymbol readDecl(UnitInputStream in, UnitInfo info, Symbol owner, @QNameId int ownerName) throws IOException { + if (in.readBoolean()) { + return readClassDecl(in, info, owner, ownerName); + } + + readMembers(in, info, ownerName, new MemberSymbol(owner)); + return null; + } + + @Override + public boolean equals(Object o) { + return this == o || o instanceof SerializedUnit && Arrays.equals(myBytes, ((SerializedUnit)o).myBytes); + } + + @Override + public int hashCode() { + int result = myBytes.length; + int length = Math.min(30, myBytes.length); + for (int i = 0; i < length; i++) { + result = 31 * result + myBytes[i]; + } + return result; + } +} + +class UnitInputStream extends DataInputStream { + final int fileId; + final StubEnter stubEnter; + final NameEnvironment names; + + UnitInputStream(InputStream in, int fileId, StubEnter stubEnter) { + super(in); + this.fileId = fileId; + this.stubEnter = stubEnter; + this.names = stubEnter.myNameEnvironment; + } +} diff --git a/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/SingleClassHierarchy.java b/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/SingleClassHierarchy.java index 982b1af46211..bffe473fee6c 100644 --- a/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/SingleClassHierarchy.java +++ b/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/SingleClassHierarchy.java @@ -26,6 +26,7 @@ import com.intellij.psi.stubsHierarchy.impl.Symbol.ClassSymbol; import com.intellij.util.containers.ContainerUtil; import org.apache.commons.lang.ArrayUtils; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.Arrays; import java.util.BitSet; @@ -71,7 +72,7 @@ public class SingleClassHierarchy extends ClassHierarchy { private static BitSet calcAnonymous(ClassSymbol[] classSymbols) { BitSet answer = new BitSet(); for (ClassSymbol symbol : classSymbols) { - if (!symbol.isHierarchyIncomplete() && symbol.myShortName == NamesEnumerator.NO_NAME) { + if (!symbol.isHierarchyIncomplete() && symbol.myShortName == NameEnvironment.NO_NAME) { answer.set(symbol.myAnchorId); } } @@ -112,13 +113,17 @@ public class SingleClassHierarchy extends ClassHierarchy { @NotNull @Override public SmartClassAnchor[] getDirectSubtypeCandidates(@NotNull PsiClass psiClass) { - VirtualFile vFile = psiClass.getContainingFile().getVirtualFile(); - if (!(vFile instanceof VirtualFileWithId)) return StubClassAnchor.EMPTY_ARRAY; - - SmartClassAnchor anchor = forPsiClass(((VirtualFileWithId)vFile).getId(), psiClass); + SmartClassAnchor anchor = findAnchor(psiClass); return anchor == null ? StubClassAnchor.EMPTY_ARRAY : getDirectSubtypeCandidates(anchor); } + @Override + @Nullable + public SmartClassAnchor findAnchor(@NotNull PsiClass psiClass) { + VirtualFile vFile = psiClass.getContainingFile().getVirtualFile(); + return vFile instanceof VirtualFileWithId ? forPsiClass(((VirtualFileWithId)vFile).getId(), psiClass) : null; + } + @NotNull @Override public SmartClassAnchor[] getDirectSubtypeCandidates(@NotNull SmartClassAnchor anchor) { diff --git a/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/StubEnter.java b/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/StubEnter.java index 9dd9c22425b9..b8d72da404ec 100644 --- a/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/StubEnter.java +++ b/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/StubEnter.java @@ -19,13 +19,15 @@ import com.intellij.psi.impl.java.stubs.hierarchy.IndexTree; import com.intellij.util.BitUtil; import org.jetbrains.annotations.Nullable; +import java.io.DataInput; +import java.io.IOException; import java.util.ArrayList; -import java.util.Arrays; -import static com.intellij.psi.stubsHierarchy.impl.Symbol.*; +import static com.intellij.psi.stubsHierarchy.impl.Symbol.ClassSymbol; +import static com.intellij.psi.stubsHierarchy.impl.Symbol.PackageSymbol; public class StubEnter { - private final NameEnvironment myNameEnvironment; + final NameEnvironment myNameEnvironment; private final Symbols mySymbols; private final StubHierarchyConnector myStubHierarchyConnector; @@ -37,92 +39,32 @@ public class StubEnter { myStubHierarchyConnector = new StubHierarchyConnector(myNameEnvironment, symbols); } - void unitEnter(IndexTree.Unit unit, int fileId) { - PackageSymbol pkg = unit.myPackageName.length > 0 - ? mySymbols.enterPackage(myNameEnvironment.myNamesEnumerator.getFullName(unit.myPackageName, true)) - : mySymbols.myRootPackage; - enter(unit.myDecls, UnitInfo.mkUnitInfo(unit.myUnitType, internImports(unit)), pkg, pkg.myQualifiedName, fileId); + PackageSymbol enterPackage(DataInput in) throws IOException { + return mySymbols.enterPackage(myNameEnvironment.readQualifiedName(in)); } - private long[] internImports(IndexTree.Unit unit) { - long[] imports = unit.imports.length == 0 ? Imports.EMPTY_ARRAY : new long[unit.imports.length]; - for (int i = 0; i < unit.imports.length; i++) { - imports[i] = processImport(unit.imports[i]); - } - return imports; - } - - private long processImport(IndexTree.Import anImport) { - QualifiedName fullname = myNameEnvironment.myNamesEnumerator.getFullName(anImport.myFullname, true); - return Imports.mkImport(fullname, anImport.myStaticImport, anImport.myOnDemand, anImport.myAlias); - } - - private void enter(IndexTree.ClassDecl[] trees, UnitInfo info, Symbol owner, @Nullable QualifiedName ownerName, int fileId) { - for (IndexTree.ClassDecl tree : trees) { - enter(tree, info, owner, ownerName, fileId); - } - } - - private ClassSymbol[] enter(IndexTree.Decl[] trees, UnitInfo info, Symbol owner, @Nullable QualifiedName ownerName, int fileId) { - ClassSymbol[] members = new ClassSymbol[trees.length]; - int i = 0; - for (IndexTree.Decl tree : trees) { - ClassSymbol member = enter(tree, info, owner, ownerName, fileId); - if (member != null && member.myShortName != 0) { - members[i++] = member; - } - } - if (i == 0) return ClassSymbol.EMPTY_ARRAY; - - if (i < members.length) { - members = Arrays.copyOf(members, i); - } - Arrays.sort(members, CLASS_SYMBOL_BY_NAME_COMPARATOR); - return members; - } - - private ClassSymbol enter(IndexTree.Decl tree, UnitInfo info, Symbol owner, QualifiedName ownerName, int fileId) { - if (tree instanceof IndexTree.ClassDecl) { - return classEnter((IndexTree.ClassDecl)tree, info, owner, ownerName, fileId); - } - if (tree instanceof IndexTree.MemberDecl) { - memberEnter((IndexTree.MemberDecl)tree, info, owner, ownerName, fileId); - return null; - } - return null; - } - - private void memberEnter(IndexTree.MemberDecl tree, UnitInfo info, Symbol owner, @Nullable QualifiedName ownerName, int fileId) { - MemberSymbol mc = new MemberSymbol(owner); - mc.setMembers(enter(tree.myDecls, info, mc, ownerName, fileId)); - } - - private ClassSymbol classEnter(IndexTree.ClassDecl tree, UnitInfo info, Symbol owner, @Nullable QualifiedName ownerName, int fileId) { - int flags = checkFlags(tree.myMods, owner); - if (info.getType() == IndexTree.BYTECODE) { - flags |= IndexTree.COMPILED; - } - - int name = tree.myName; - QualifiedName qname = name == NamesEnumerator.NO_NAME || ownerName == null ? null - : myNameEnvironment.qualifiedName(ownerName, name, true); - @CompactArray(QualifiedName.class) Object supers = internSupers(tree.myMods, tree.mySupers); - ClassSymbol classSymbol = mySymbols.enterClass(fileId, tree.myStubId, flags, name, owner, info, supers, qname); + ClassSymbol classEnter(UnitInfo info, + Symbol owner, + int stubId, + int mods, + @ShortName int name, + @QNameId int[] superNames, + @QNameId int qname, int fileId) throws IOException { + int flags = checkFlags(mods, owner, info.getType() == IndexTree.BYTECODE); + @CompactArray(QualifiedName.class) Object supers = internSupers(mods, superNames); + ClassSymbol classSymbol = mySymbols.enterClass(fileId, stubId, flags, name, owner, info, supers, qname); if (uncompleted != null) { uncompleted.add(classSymbol); } - if (tree.myDecls.length > 0) { - classSymbol.setMembers(enter(tree.myDecls, info, classSymbol, qname, fileId)); - } return classSymbol; } @Nullable @CompactArray(QualifiedName.class) - private Object internSupers(int flags, int[][] superNames) { + Object internSupers(int flags, int[] superNames) { if (BitUtil.isSet(flags, IndexTree.ANNOTATION)) { - return myNameEnvironment.annotation; + return myNameEnvironment.java_lang_annotation_Annotation; } boolean isEnum = BitUtil.isSet(flags, IndexTree.ENUM); @@ -130,12 +72,12 @@ public class StubEnter { return isEnum ? myNameEnvironment.java_lang_Enum : null; } if (superNames.length == 1 && !isEnum) { - return myNameEnvironment.concat(superNames[0], true); + return new QualifiedName(superNames[0]); } QualifiedName[] array = new QualifiedName[superNames.length + (isEnum ? 1 : 0)]; for (int i = 0; i < superNames.length; i++) { - array[i] = myNameEnvironment.concat(superNames[i], true); + array[i] = new QualifiedName(superNames[i]); } if (isEnum) { array[array.length - 1] = myNameEnvironment.java_lang_Enum; @@ -157,11 +99,18 @@ public class StubEnter { uncompleted = null; } - public static int checkFlags(long flags, Symbol owner) { + private static int checkFlags(long flags, Symbol owner, boolean compiled) { int mask = 0; if (owner.isClass() && (owner.myOwner.isPackage() || owner.isStatic())) { - if ((flags & (IndexTree.INTERFACE | IndexTree.ENUM | IndexTree.STATIC)) != 0 ) + if ((flags & (IndexTree.INTERFACE | IndexTree.ENUM | IndexTree.STATIC)) != 0) { mask |= IndexTree.STATIC; + } + } + if ((flags & IndexTree.SUPERS_UNRESOLVED) != 0) { + mask |= IndexTree.SUPERS_UNRESOLVED; + } + if (compiled) { + mask |= IndexTree.COMPILED; } return mask; } diff --git a/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/StubHierarchyConnector.java b/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/StubHierarchyConnector.java index 597ab6ab929c..2373dcc3a28f 100644 --- a/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/StubHierarchyConnector.java +++ b/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/StubHierarchyConnector.java @@ -31,14 +31,14 @@ public class StubHierarchyConnector { private void resolveName(Symbol.ClassSymbol place, QualifiedName name, Set result) throws IncompleteHierarchyException { if (place.isCompiled()) { - Symbol.ClassSymbol[] candidates = myResolve.findGlobalType(name); + Symbol.ClassSymbol[] candidates = myResolve.findGlobalType(name.myId); if (candidates.length == 0) { throw new IncompleteHierarchyException(); } Collections.addAll(result, candidates); } else { - for (Symbol symbol : myResolve.resolveBase(place, name.myComponents)) { + for (Symbol symbol : myResolve.resolveBase(place, name.myId, false)) { if (symbol instanceof Symbol.ClassSymbol) { result.add((Symbol.ClassSymbol)symbol); } diff --git a/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/StubHierarchyIndex.java b/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/StubHierarchyIndex.java index 057b7160aa0c..2de47735eaf4 100644 --- a/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/StubHierarchyIndex.java +++ b/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/StubHierarchyIndex.java @@ -21,14 +21,17 @@ import com.intellij.openapi.roots.ProjectFileIndex; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileWithId; import com.intellij.psi.impl.java.stubs.hierarchy.IndexTree; -import com.intellij.psi.impl.java.stubs.index.JavaUnitDescriptor; import com.intellij.psi.stubsHierarchy.StubHierarchyIndexer; import com.intellij.util.indexing.*; import com.intellij.util.io.DataExternalizer; +import com.intellij.util.io.DataInputOutputUtil; import com.intellij.util.io.EnumeratorIntegerDescriptor; import com.intellij.util.io.KeyDescriptor; import org.jetbrains.annotations.NotNull; +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; import java.util.Arrays; import java.util.Collections; import java.util.stream.IntStream; @@ -36,29 +39,29 @@ import java.util.stream.IntStream; /** * @author peter */ -public class StubHierarchyIndex extends FileBasedIndexExtension implements PsiDependentIndex { +public class StubHierarchyIndex extends FileBasedIndexExtension implements PsiDependentIndex { private static final int KEY_COUNT = 20; static final int[] BINARY_KEYS = IntStream.rangeClosed(1, KEY_COUNT).toArray(); static final int[] SOURCE_KEYS = IntStream.rangeClosed(-KEY_COUNT, -1).toArray(); - static final ID INDEX_ID = ID.create("jvm.hierarchy"); + static final ID INDEX_ID = ID.create("jvm.hierarchy"); private static final StubHierarchyIndexer[] ourIndexers = StubHierarchyIndexer.EP_NAME.getExtensions(); @NotNull @Override - public ID getName() { + public ID getName() { return INDEX_ID; } @NotNull @Override - public DataIndexer getIndexer() { + public DataIndexer getIndexer() { return inputData -> { for (StubHierarchyIndexer indexer : ourIndexers) { VirtualFile file = inputData.getFile(); IndexTree.Unit unit = indexer.handlesFile(file) ? indexer.indexFile(inputData) : null; if (unit != null && unit.myDecls.length > 0) { int[] keys = file.getFileType().isBinary() ? BINARY_KEYS : SOURCE_KEYS; - return Collections.singletonMap(keys[((VirtualFileWithId) file).getId() % keys.length], unit); + return Collections.singletonMap(keys[((VirtualFileWithId) file).getId() % keys.length], new SerializedUnit(unit)); } } return Collections.emptyMap(); @@ -73,13 +76,27 @@ public class StubHierarchyIndex extends FileBasedIndexExtension getValueExternalizer() { - return JavaUnitDescriptor.INSTANCE; + public DataExternalizer getValueExternalizer() { + return new DataExternalizer() { + @Override + public void save(@NotNull DataOutput out, SerializedUnit value) throws IOException { + byte[] bytes = value.getSerializedBytes(); + DataInputOutputUtil.writeINT(out, bytes.length); + out.write(bytes); + } + + @Override + public SerializedUnit read(@NotNull DataInput in) throws IOException { + byte[] bytes = new byte[DataInputOutputUtil.readINT(in)]; + in.readFully(bytes); + return new SerializedUnit(bytes); + } + }; } @Override public int getVersion() { - return IndexTree.STUB_HIERARCHY_ENABLED ? 5 + Arrays.stream(ourIndexers).mapToInt(StubHierarchyIndexer::getVersion).sum() : 0; + return IndexTree.STUB_HIERARCHY_ENABLED ? 6 + Arrays.stream(ourIndexers).mapToInt(StubHierarchyIndexer::getVersion).sum() : 0; } @NotNull diff --git a/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/StubResolver.java b/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/StubResolver.java index d8791ff8a2ea..5d35b4210bfd 100644 --- a/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/StubResolver.java +++ b/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/StubResolver.java @@ -17,6 +17,7 @@ package com.intellij.psi.stubsHierarchy.impl; import com.intellij.psi.impl.java.stubs.hierarchy.IndexTree; import com.intellij.util.BitUtil; +import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -36,16 +37,22 @@ public class StubResolver { } // resolve class `sym` extends/implements `baseId` - Set resolveBase(Symbol.ClassSymbol sym, int[] baseId) throws IncompleteHierarchyException { - Set result = findIdent(sym.myOwner, sym.myUnitInfo, baseId[0], baseId.length > 1); - for (int i = 1; i < baseId.length; i++) { - Set prev = result; - int k = (i == baseId.length - 1) ? IndexTree.CLASS : IndexTree.CLASS | IndexTree.PACKAGE; - - result = new HashSet<>(); - for (Symbol symbol : prev) { - selectSym(symbol, baseId[i], k, result); + Set resolveBase(Symbol.ClassSymbol sym, @QNameId int name, boolean processPackages) throws IncompleteHierarchyException { + @QNameId int prefix = myNameEnvironment.prefixId(name); + @ShortName int shortName = myNameEnvironment.shortName(name); + if (prefix == NameEnvironment.NO_NAME) { + Set result = findIdent(sym.myOwner, sym.myUnitInfo, shortName, processPackages); + if (result.isEmpty()) { + throw IncompleteHierarchyException.INSTANCE; } + return result; + } + + int k = processPackages ? IndexTree.CLASS | IndexTree.PACKAGE : IndexTree.CLASS; + Set prev = resolveBase(sym, prefix, true); + Set result = new HashSet<>(); + for (Symbol symbol : prev) { + selectSym(symbol, shortName, k, result); } if (result.isEmpty()) { throw IncompleteHierarchyException.INSTANCE; @@ -54,15 +61,15 @@ public class StubResolver { } @NotNull - private Set findIdent(Symbol startScope, UnitInfo info, int name, boolean processPackages) throws IncompleteHierarchyException { + private Set findIdent(Symbol startScope, UnitInfo info, @ShortName int name, boolean processPackages) throws IncompleteHierarchyException { Set result = new HashSet(); findType(startScope, name, result); findGlobalType(info, name, result); if (processPackages) { - Symbol.PackageSymbol pkg = mySymbols.getPackage(myNameEnvironment.qualifiedName(null, name, false)); - if (pkg != null) - result.add(pkg); + @QNameId int nameId = myNameEnvironment.findExistingName(0, name); + Symbol.PackageSymbol pkg = nameId < 0 ? null : mySymbols.getPackage(nameId); + ContainerUtil.addIfNotNull(result, pkg); } return result; } @@ -76,16 +83,16 @@ public class StubResolver { } // resolving `receiver.name` - private void selectSym(Symbol receiver, int name, int kind, Set symbols) throws IncompleteHierarchyException { + private void selectSym(Symbol receiver, @ShortName int name, int kind, Set symbols) throws IncompleteHierarchyException { if (receiver.isPackage()) findIdentInPackage((Symbol.PackageSymbol)receiver, name, kind, symbols); else findMemberType(receiver, name, symbols); } - private void findIdentInPackage(Symbol.PackageSymbol pck, int name, int kind, Set symbols) { - QualifiedName fullname = mySymbols.myNameEnvironment.qualifiedName(pck.myQualifiedName, name, false); - if (fullname == null) { + private void findIdentInPackage(Symbol.PackageSymbol pck, @ShortName int name, int kind, Set symbols) { + @QNameId int fullname = mySymbols.myNameEnvironment.findExistingName(pck.myQualifiedName, name); + if (fullname < 0) { return; } if (BitUtil.isSet(kind, IndexTree.PACKAGE)) { @@ -94,11 +101,11 @@ public class StubResolver { symbols.add(pkg); } if (BitUtil.isSet(kind, IndexTree.CLASS)) { - Collections.addAll(symbols, loadClass(fullname)); + Collections.addAll(symbols, findGlobalType(fullname)); } } - private void findMemberType(Symbol s, int name, Set symbols) throws IncompleteHierarchyException { + private void findMemberType(Symbol s, @ShortName int name, Set symbols) throws IncompleteHierarchyException { if (s.isClass()) { processInheritedMembers((Symbol.ClassSymbol)s, name, false, symbols, null); } else { @@ -107,7 +114,7 @@ public class StubResolver { } private void processInheritedMembers(Symbol.ClassSymbol s, - int name, + @ShortName int name, boolean requireStatic, Set symbols, @Nullable Set processed) throws IncompleteHierarchyException { @@ -128,23 +135,19 @@ public class StubResolver { } } - private Symbol.ClassSymbol[] loadClass(@NotNull QualifiedName fqn) { - return mySymbols.loadClass(fqn); + public Symbol.ClassSymbol[] findGlobalType(@QNameId int nameId) { + return mySymbols.loadClass(nameId); } - public Symbol.ClassSymbol[] findGlobalType(@NotNull QualifiedName name) { - return loadClass(name); - } - - private void findGlobalType(UnitInfo info, int name, Set symbols) throws IncompleteHierarchyException { + private void findGlobalType(UnitInfo info, @ShortName int name, Set symbols) throws IncompleteHierarchyException { for (long anImport : Translator.getDefaultImports(info.getType(), myNameEnvironment)) handleImport(anImport, name, symbols); for (long anImport : info.getImports()) handleImport(anImport, name, symbols); } - public void handleImport(long tree, int name, Set symbols) throws IncompleteHierarchyException { - QualifiedName fullname = Imports.getFullName(tree, myNameEnvironment); + public void handleImport(long tree, @ShortName int name, Set symbols) throws IncompleteHierarchyException { + @QNameId int fullname = Imports.getFullNameId(tree); if (Imports.isOnDemand(tree)) { if (Imports.isStatic(tree)) { for (Symbol.ClassSymbol p : findGlobalType(fullname)) @@ -155,12 +158,12 @@ public class StubResolver { } } else { - QualifiedName prefix = myNameEnvironment.prefix(fullname); - if (prefix.isEmpty()) { + @QNameId int prefix = myNameEnvironment.prefixId(fullname); + if (prefix == 0) { return; } - int shortName = myNameEnvironment.shortName(fullname); - int alias = Imports.getAlias(tree); + @ShortName int shortName = myNameEnvironment.shortName(fullname); + @ShortName int alias = Imports.getAlias(tree); boolean shouldImport = ((alias & name) == name) || shortName == name; if (!shouldImport) return; @@ -175,22 +178,19 @@ public class StubResolver { } // handling of `import prefix.*` - private void importAll(@NotNull final QualifiedName prefix, int suffix, final Set symbols) { - - QualifiedName fullname = myNameEnvironment.qualifiedName(prefix, suffix, false); - // existing only - if (fullname != null) { - Symbol.ClassSymbol[] ss = findGlobalType(fullname); - Collections.addAll(symbols, ss); - } + private void importAll(@QNameId int prefix, @ShortName int suffix, final Set symbols) { + @QNameId int fullname = myNameEnvironment.findExistingName(prefix, suffix); + if (fullname >= 0) { + Collections.addAll(symbols, findGlobalType(fullname)); + } } // handling of import static `tsym.name` as - private void importNamedStatic(final Symbol.ClassSymbol tsym, final int name, final Set symbols) throws IncompleteHierarchyException { + private void importNamedStatic(final Symbol.ClassSymbol tsym, @ShortName final int name, final Set symbols) throws IncompleteHierarchyException { processInheritedMembers(tsym, name, true, symbols, null); } - private static void processMembers(Symbol.ClassSymbol[] members, int name, Set symbols, boolean requireStatic) { + private static void processMembers(Symbol.ClassSymbol[] members, @ShortName int name, Set symbols, boolean requireStatic) { int index = getIndex(name, members); if (index < 0) return; diff --git a/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/Symbol.java b/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/Symbol.java index 40ef829ae232..ed438237d753 100644 --- a/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/Symbol.java +++ b/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/Symbol.java @@ -21,9 +21,7 @@ import gnu.trove.TIntHashSet; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.Collections; -import java.util.Comparator; -import java.util.Set; +import java.util.*; /** * Java symbols needed for hierarchy building. Mostly classes ({@link ClassSymbol}) or packages ({@link PackageSymbol}), @@ -74,9 +72,9 @@ public abstract class Symbol { } public static class PackageSymbol extends Symbol { - final QualifiedName myQualifiedName; + @QNameId final int myQualifiedName; - public PackageSymbol(Symbol owner, QualifiedName fullname, int name) { + public PackageSymbol(Symbol owner, @QNameId int fullname, int name) { super(IndexTree.PACKAGE, owner, name); myQualifiedName = fullname; } @@ -85,7 +83,6 @@ public abstract class Symbol { /** A class for class symbols */ public static class ClassSymbol extends MemberSymbol { - private static final int HIERARCHY_INCOMPLETE = 1 << 20; private static final int CONNECT_STARTED = 1 << 21; public static final ClassSymbol[] EMPTY_ARRAY = new ClassSymbol[0]; @@ -101,8 +98,10 @@ public abstract class Symbol { @CompactArray(QualifiedName.class) Object supers) { super(flags | IndexTree.CLASS, owner, name); this.myAnchorId = anchorId; - this.mySuperClasses = supers; - this.myUnitInfo = unitInfo; + + boolean incomplete = isHierarchyIncomplete(); + this.mySuperClasses = incomplete ? null : supers; + this.myUnitInfo = incomplete ? null : unitInfo; } @Override @@ -150,7 +149,7 @@ public abstract class Symbol { void markHierarchyIncomplete() { setSupers(Collections.emptySet()); - myFlags = BitUtil.set(myFlags, HIERARCHY_INCOMPLETE, true); + myFlags = BitUtil.set(myFlags, IndexTree.SUPERS_UNRESOLVED, true); } void setSupers(Set supers) { @@ -161,7 +160,7 @@ public abstract class Symbol { } boolean isHierarchyIncomplete() { - return BitUtil.isSet(myFlags, HIERARCHY_INCOMPLETE); + return BitUtil.isSet(myFlags, IndexTree.SUPERS_UNRESOLVED); } boolean hasAmbiguousSupers() { @@ -186,7 +185,7 @@ public abstract class Symbol { @CompactArray(ClassSymbol.class) private Object myMembers = null; MemberSymbol(Symbol owner) { - super(IndexTree.MEMBER, owner, NamesEnumerator.NO_NAME); + super(IndexTree.MEMBER, owner, NameEnvironment.NO_NAME); } MemberSymbol(int flags, Symbol owner, int name) { @@ -199,12 +198,18 @@ public abstract class Symbol { (ClassSymbol[])myMembers; } - void setMembers(ClassSymbol[] members) { - myMembers = members.length == 0 ? null : members.length == 1 ? members[0] : members; + void setMembers(List members) { + myMembers = members.isEmpty() ? null : members.size() == 1 ? members.get(0) : toSortedArray(members); + } + + private static ClassSymbol[] toSortedArray(List members) { + ClassSymbol[] array = members.toArray(new ClassSymbol[members.size()]); + Arrays.sort(array, CLASS_SYMBOL_BY_NAME_COMPARATOR); + return array; } } - public static final Comparator CLASS_SYMBOL_BY_NAME_COMPARATOR = (s1, s2) -> { + private static final Comparator CLASS_SYMBOL_BY_NAME_COMPARATOR = (s1, s2) -> { int name1 = s1.myShortName; int name2 = s2.myShortName; return (name1 < name2) ? -1 : ((name1 == name2) ? 0 : 1); diff --git a/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/Symbols.java b/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/Symbols.java index f4ec857f5fc7..a81281adec67 100644 --- a/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/Symbols.java +++ b/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/Symbols.java @@ -21,35 +21,29 @@ public class Symbols { private Object[] myClassSymbolsByNameId = new Object[0x8000]; protected Symbols() { - myRootPackage = new PackageSymbol(null, myNameEnvironment.empty, NamesEnumerator.NO_NAME); - myPackages.put(myNameEnvironment.empty.myId, myRootPackage); + myRootPackage = new PackageSymbol(null, 0, NameEnvironment.NO_NAME); + myPackages.put(0, myRootPackage); } - public PackageSymbol enterPackage(QualifiedName qualifiedName) { - PackageSymbol p = myPackages.get(qualifiedName.myId); + public PackageSymbol enterPackage(@QNameId int qualifiedName) { + PackageSymbol p = myPackages.get(qualifiedName); if (p == null) { - PackageSymbol owner = enterPackage(myNameEnvironment.prefix(qualifiedName)); + PackageSymbol owner = enterPackage(myNameEnvironment.prefixId(qualifiedName)); int shortName = myNameEnvironment.shortName(qualifiedName); p = new PackageSymbol(owner, qualifiedName, shortName); - myPackages.put(qualifiedName.myId, p); + myPackages.put(qualifiedName, p); } return p; } @Nullable - public PackageSymbol getPackage(QualifiedName qualifiedName) { - if (qualifiedName == null) - return null; - return myPackages.get(qualifiedName.myId); + PackageSymbol getPackage(@QNameId int qualifiedName) { + return myPackages.get(qualifiedName); } @NotNull - public ClassSymbol[] loadClass(@NotNull QualifiedName qualifiedName) { - int i = qualifiedName.myId; - if (i >= myClassSymbolsByNameId.length) { - return ClassSymbol.EMPTY_ARRAY; - } - return getClassSymbols(i); + ClassSymbol[] loadClass(@QNameId int name) { + return name >= myClassSymbolsByNameId.length ? ClassSymbol.EMPTY_ARRAY : getClassSymbols(name); } private ClassSymbol[] getClassSymbols(int id) { @@ -70,12 +64,12 @@ public class Symbols { Symbol owner, UnitInfo info, @CompactArray(QualifiedName.class) Object supers, - @Nullable QualifiedName qualifiedName) { + @QNameId int qualifiedName) { int anchorId = myClassAnchors.registerClass(fileId, stubId); ClassSymbol c = new ClassSymbol(anchorId, flags, owner, shortName, info, supers); myClassSymbols.add(c); - if (qualifiedName != null) { - putClassByName(c, qualifiedName.myId); + if (qualifiedName >= 0) { + putClassByName(c, qualifiedName); } return c; } diff --git a/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/Translator.java b/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/Translator.java index 9baa9d08e2e0..c1e3fff734da 100644 --- a/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/Translator.java +++ b/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/Translator.java @@ -33,7 +33,7 @@ public class Translator { private static long[] createDefaultJavaImports(NameEnvironment nameEnvironment) { return new long[]{ - Imports.mkImport(nameEnvironment.fromString("java.lang", true), false, true, 0) + Imports.mkImport(nameEnvironment.fromString("java.lang"), false, true, 0) }; } @@ -48,14 +48,14 @@ public class Translator { private static long[] createDefaultGroovyImports(NameEnvironment nameEnvironment) { return new long[] { - Imports.mkImport(nameEnvironment.fromString("java.lang", true), false, true, 0), - Imports.mkImport(nameEnvironment.fromString("java.util", true), false, true, 0), - Imports.mkImport(nameEnvironment.fromString("java.io", true), false, true, 0), - Imports.mkImport(nameEnvironment.fromString("java.net", true), false, true, 0), - Imports.mkImport(nameEnvironment.fromString("groovy.lang", true), false, true, 0), - Imports.mkImport(nameEnvironment.fromString("groovy.util", true), false, true, 0), - Imports.mkImport(nameEnvironment.fromString("java.math.BigInteger", true), false, true, 0), - Imports.mkImport(nameEnvironment.fromString("java.math.BigDecimal", true), false, true, 0), + Imports.mkImport(nameEnvironment.fromString("java.lang"), false, true, 0), + Imports.mkImport(nameEnvironment.fromString("java.util"), false, true, 0), + Imports.mkImport(nameEnvironment.fromString("java.io"), false, true, 0), + Imports.mkImport(nameEnvironment.fromString("java.net"), false, true, 0), + Imports.mkImport(nameEnvironment.fromString("groovy.lang"), false, true, 0), + Imports.mkImport(nameEnvironment.fromString("groovy.util"), false, true, 0), + Imports.mkImport(nameEnvironment.fromString("java.math.BigInteger"), false, true, 0), + Imports.mkImport(nameEnvironment.fromString("java.math.BigDecimal"), false, true, 0), }; } diff --git a/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/CompactArray.java b/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/annotations.java similarity index 68% rename from java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/CompactArray.java rename to java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/annotations.java index 3ef96d52169c..0b7b09f32c50 100644 --- a/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/CompactArray.java +++ b/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/annotations.java @@ -15,8 +15,12 @@ */ package com.intellij.psi.stubsHierarchy.impl; +import com.intellij.psi.impl.java.stubs.hierarchy.IndexTree; + +import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; /** * This annotation exists for documentation purposes only.

@@ -24,10 +28,23 @@ import java.lang.annotation.RetentionPolicy; * The value annotated with this annotation is used to save memory when storing mostly-singular or empty collections. * For empty collection, 'null' value is used. For one-element collection, the value is the single element. Otherwise, * an array is used. Possible component types are specified in the annotation value. - * - * @author peter */ @Retention(RetentionPolicy.SOURCE) -public @interface CompactArray { +@Target(ElementType.TYPE_USE) +@interface CompactArray { Class[] value(); } + +/** + * int id of a qualified name in a {@link NameEnvironment} + */ +@Retention(RetentionPolicy.SOURCE) +@Target(ElementType.TYPE_USE) +@interface QNameId { } + +/** + * int hash of a qualified name part, produced by {@link IndexTree#hashIdentifier(String)} + */ +@Retention(RetentionPolicy.SOURCE) +@Target(ElementType.TYPE_USE) +@interface ShortName { } diff --git a/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/test/TestStubHierarchyAction.java b/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/test/TestStubHierarchyAction.java index 2e6566479c91..d5af5482bd4f 100644 --- a/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/test/TestStubHierarchyAction.java +++ b/java/java-impl/src/com/intellij/psi/stubsHierarchy/impl/test/TestStubHierarchyAction.java @@ -23,13 +23,22 @@ import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.CommonClassNames; import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiManager; +import com.intellij.psi.PsiTypeParameter; +import com.intellij.psi.impl.source.PsiFileImpl; +import com.intellij.psi.impl.source.PsiFileWithStubSupport; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.stubs.StubElement; +import com.intellij.psi.stubs.StubTree; import com.intellij.psi.stubsHierarchy.ClassHierarchy; import com.intellij.psi.stubsHierarchy.HierarchyService; import com.intellij.psi.stubsHierarchy.SmartClassAnchor; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.MultiMap; +import com.intellij.util.indexing.FileBasedIndex; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -72,14 +81,48 @@ public class TestStubHierarchyAction extends InheritanceAction { } private void compareSupers(ProgressIndicator indicator, MultiMap supers, ClassHierarchy hierarchy) { - List anchors = hierarchy.getCoveredClasses(); - for (int i = 0; i < anchors.size(); i++) { - indicator.setFraction(i * 1.0 / anchors.size()); - SmartClassAnchor anchor = anchors.get(i); - compareSupers(anchor, supers.get(anchor)); + List covered = getCoveredFiles(indicator, hierarchy); + for (int i = 0; i < covered.size(); i++) { + indicator.setFraction(i * 1.0 / covered.size()); + checkFile(supers, hierarchy, covered.get(i)); } } + private void checkFile(MultiMap supers, ClassHierarchy hierarchy, VirtualFile vFile) { + for (StubElement element : getStubTree(vFile).getPlainListFromAllRoots()) { + Object psi = element.getPsi(); + if (psi instanceof PsiClass && !(psi instanceof PsiTypeParameter)) { + SmartClassAnchor anchor = hierarchy.findAnchor((PsiClass)psi); + if (anchor == null) { + throw new AssertionError("Class not indexed: " + psi + " in " + vFile); + } + compareSupers(anchor, supers.get(anchor)); + } + } + } + + @NotNull + private List getCoveredFiles(ProgressIndicator indicator, ClassHierarchy hierarchy) { + GlobalSearchScope allScope = GlobalSearchScope.allScope(myProject); + GlobalSearchScope uncovered = hierarchy.restrictToUncovered(allScope); + List covered = new ArrayList<>(); + FileBasedIndex.getInstance().iterateIndexableFiles(file -> { + if (!file.isDirectory() && allScope.contains(file) && !uncovered.contains(file)) { + covered.add(file); + } + return true; + }, myProject, indicator); + return covered; + } + + @NotNull + private StubTree getStubTree(VirtualFile vFile) { + PsiFileWithStubSupport psiFile = (PsiFileWithStubSupport)PsiManager.getInstance(myProject).findFile(vFile); + assert psiFile != null : "No PSI for " + vFile; + StubTree stubTree = psiFile.getStubTree(); + return stubTree != null ? stubTree : ((PsiFileImpl)psiFile).calcStubTree(); + } + private void compareSupers(final SmartClassAnchor anchor, final Collection superAnchors) { PsiClass subClass = anchor.retrieveClass(myProject); List stubSuperList = ContainerUtil.map(superAnchors, (anchor1) -> anchor1.retrieveClass(myProject)); diff --git a/java/java-indexing-impl/src/com/intellij/psi/impl/java/stubs/index/JavaShortClassNameIndex.java b/java/java-indexing-impl/src/com/intellij/psi/impl/java/stubs/index/JavaShortClassNameIndex.java index 5f0d9298b1cc..d3a76c2fb37e 100644 --- a/java/java-indexing-impl/src/com/intellij/psi/impl/java/stubs/index/JavaShortClassNameIndex.java +++ b/java/java-indexing-impl/src/com/intellij/psi/impl/java/stubs/index/JavaShortClassNameIndex.java @@ -41,7 +41,7 @@ public class JavaShortClassNameIndex extends StringStubIndexExtension @Override public int getVersion() { - return super.getVersion() + (FileBasedIndex.ourEnableTracingOfKeyHashToVirtualFileMapping ? 1 : 0); + return super.getVersion() + (FileBasedIndex.ourEnableTracingOfKeyHashToVirtualFileMapping ? 2 : 0); } @NotNull diff --git a/java/java-indexing-impl/src/com/intellij/psi/impl/java/stubs/index/JavaUnitDescriptor.java b/java/java-indexing-impl/src/com/intellij/psi/impl/java/stubs/index/JavaUnitDescriptor.java deleted file mode 100644 index ad2fddadbe36..000000000000 --- a/java/java-indexing-impl/src/com/intellij/psi/impl/java/stubs/index/JavaUnitDescriptor.java +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright 2000-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 com.intellij.psi.impl.java.stubs.index; - -import com.intellij.psi.impl.java.stubs.hierarchy.IndexTree; -import com.intellij.util.BitUtil; -import com.intellij.util.io.DataExternalizer; -import com.intellij.util.io.DataInputOutputUtil; -import org.jetbrains.annotations.NotNull; - -import java.io.DataInput; -import java.io.DataOutput; -import java.io.IOException; - -import static com.intellij.psi.impl.java.stubs.index.JavaUnitDescriptor.ImportFlags.*; - -public class JavaUnitDescriptor implements DataExternalizer { - public static final JavaUnitDescriptor INSTANCE = new JavaUnitDescriptor(); - - private static void writeIntArray(DataOutput out, int[] array) throws IOException { - DataInputOutputUtil.writeINT(out, array.length); - for (int i : array) { - out.writeInt(i); - } - } - private static int[] readIntArray(DataInput in) throws IOException { - int length = DataInputOutputUtil.readINT(in); - int[] result = new int[length]; - for (int i = 0; i < length; i++) { - result[i] = in.readInt(); - } - return result; - } - - @Override - public void save(@NotNull DataOutput out, IndexTree.Unit value) throws IOException { - writeIntArray(out, value.myPackageName); - out.writeByte(value.myUnitType); - if (value.myUnitType != IndexTree.BYTECODE) { - DataInputOutputUtil.writeINT(out, value.imports.length); - for (IndexTree.Import anImport : value.imports) { - writeImport(out, anImport); - } - } - // class Declaration - DataInputOutputUtil.writeINT(out, value.myDecls.length); - for (IndexTree.ClassDecl def : value.myDecls) { - saveClassDecl(out, def); - } - } - - private void saveClassDecl(@NotNull DataOutput out, IndexTree.ClassDecl value) throws IOException { - DataInputOutputUtil.writeINT(out, value.myStubId); - DataInputOutputUtil.writeINT(out, value.myMods); - out.writeInt(value.myName); - DataInputOutputUtil.writeINT(out, value.mySupers.length); - for (int[] aSuper : value.mySupers) { - writeIntArray(out, aSuper); - } - DataInputOutputUtil.writeINT(out, value.myDecls.length); - for (IndexTree.Decl def : value.myDecls) { - saveDecl(out, def); - } - } - - private void saveDecl(@NotNull DataOutput out, IndexTree.Decl value) throws IOException { - if (value instanceof IndexTree.ClassDecl) { - out.writeBoolean(true); - saveClassDecl(out, (IndexTree.ClassDecl)value); - } else if (value instanceof IndexTree.MemberDecl) { - out.writeBoolean(false); - IndexTree.MemberDecl memberDecl = (IndexTree.MemberDecl)value; - DataInputOutputUtil.writeINT(out, memberDecl.myDecls.length); - for (IndexTree.Decl def : memberDecl.myDecls) { - saveDecl(out, def); - } - } - } - - @Override - public IndexTree.Unit read(@NotNull DataInput in) throws IOException { - int[] pid = readIntArray(in); - byte type = in.readByte(); - IndexTree.Import[] imports = IndexTree.Import.EMPTY_ARRAY; - if (type != IndexTree.BYTECODE) { - imports = new IndexTree.Import[DataInputOutputUtil.readINT(in)]; - for (int i = 0; i < imports.length; i++) { - imports[i] = readImport(in); - } - } - IndexTree.ClassDecl[] classes = new IndexTree.ClassDecl[DataInputOutputUtil.readINT(in)]; - for (int i = 0; i < classes.length; i++) { - classes[i] = readClassDecl(in); - } - return new IndexTree.Unit(pid, type, imports, classes); - } - - private IndexTree.ClassDecl readClassDecl(DataInput in) throws IOException { - int stubId = DataInputOutputUtil.readINT(in); - int mods = DataInputOutputUtil.readINT(in); - int name = in.readInt(); - int[][] supers = new int[DataInputOutputUtil.readINT(in)][]; - for (int i = 0; i < supers.length; i++) { - supers[i] = readIntArray(in); - } - IndexTree.Decl[] decls = new IndexTree.Decl[DataInputOutputUtil.readINT(in)]; - for (int i = 0; i < decls.length; i++) { - decls[i] = readDecl(in); - } - return new IndexTree.ClassDecl(stubId, mods, name, supers, decls); - } - - private IndexTree.Decl readDecl(DataInput in) throws IOException { - boolean isClassDecl = in.readBoolean(); - if (isClassDecl) { - return readClassDecl(in); - } - else { - IndexTree.Decl[] decls = new IndexTree.Decl[DataInputOutputUtil.readINT(in)]; - for (int i = 0; i < decls.length; i++) { - decls[i] = readDecl(in); - } - return new IndexTree.MemberDecl(decls); - } - } - - interface ImportFlags { - int IS_STATIC = 1; - int IS_ON_DEMAND = 2; - int HAS_ALIAS = 4; - } - - private static void writeImport(@NotNull DataOutput out, IndexTree.Import anImport) throws IOException { - writeIntArray(out, anImport.myFullname); - boolean hasAlias = anImport.myAlias != 0; - int flags = 0; - flags = BitUtil.set(flags, IS_STATIC, anImport.myStaticImport); - flags = BitUtil.set(flags, IS_ON_DEMAND, anImport.myOnDemand); - flags = BitUtil.set(flags, HAS_ALIAS, hasAlias); - out.writeByte(flags); - if (hasAlias) { - out.writeInt(anImport.myAlias); - } - } - - @NotNull - private static IndexTree.Import readImport(@NotNull DataInput in) throws IOException { - int[] fullname = readIntArray(in); - int flags = in.readByte(); - return new IndexTree.Import(fullname, - BitUtil.isSet(flags, IS_STATIC), BitUtil.isSet(flags, IS_ON_DEMAND), - BitUtil.isSet(flags, HAS_ALIAS) ? in.readInt() : 0); - } - -} diff --git a/java/java-indexing-impl/src/com/intellij/psi/stubsHierarchy/ClassHierarchy.java b/java/java-indexing-impl/src/com/intellij/psi/stubsHierarchy/ClassHierarchy.java index 2a2cbcc6d110..00337e7d084e 100644 --- a/java/java-indexing-impl/src/com/intellij/psi/stubsHierarchy/ClassHierarchy.java +++ b/java/java-indexing-impl/src/com/intellij/psi/stubsHierarchy/ClassHierarchy.java @@ -18,6 +18,7 @@ package com.intellij.psi.stubsHierarchy; import com.intellij.psi.PsiClass; import com.intellij.psi.search.GlobalSearchScope; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.List; @@ -43,6 +44,9 @@ public abstract class ClassHierarchy { @NotNull public abstract SmartClassAnchor[] getDirectSubtypeCandidates(@NotNull SmartClassAnchor anchor); + @Nullable + public abstract SmartClassAnchor findAnchor(@NotNull PsiClass psiClass); + /** * @return whether stub hierarchy resolver couldn't determine the super class exactly because there were several possible candidates */ diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/PsiSubstitutorImpl.java b/java/java-psi-impl/src/com/intellij/psi/impl/PsiSubstitutorImpl.java index ebe896f1d844..29cc8832feb7 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/PsiSubstitutorImpl.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/PsiSubstitutorImpl.java @@ -117,22 +117,14 @@ public class PsiSubstitutorImpl implements PsiSubstitutor { if (glb instanceof PsiWildcardType) { return glb; } - if (glb != null ) { - return glb instanceof PsiCapturedWildcardType ? ((PsiCapturedWildcardType)glb).getWildcard() - : PsiWildcardType.createExtends(typeParameter.getManager(), glb); + if (glb instanceof PsiCapturedWildcardType) { + PsiWildcardType wildcard = ((PsiCapturedWildcardType)glb).getWildcard(); + if (!wildcard.isSuper()) return wildcard; } - /* - if (glb instanceof PsiCapturedWildcardType) { - PsiWildcardType wildcard = ((PsiCapturedWildcardType)glb).getWildcard(); - if (!wildcard.isSuper()) return wildcard; - } - - if (glb != null ) { - return PsiWildcardType.createExtends(typeParameter.getManager(), glb); - } - */ - + if (glb != null) { + return PsiWildcardType.createExtends(typeParameter.getManager(), glb); + } } return substituted; } diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/java/stubs/hierarchy/IndexTree.java b/java/java-psi-impl/src/com/intellij/psi/impl/java/stubs/hierarchy/IndexTree.java index 7ec276f72e56..2d8b681f9712 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/java/stubs/hierarchy/IndexTree.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/java/stubs/hierarchy/IndexTree.java @@ -38,6 +38,7 @@ public class IndexTree { public static final int ENUM = 1 << 6; public static final int COMPILED = 1 << 7; public static final int MEMBER = 1 << 8; + public static final int SUPERS_UNRESOLVED = 1 << 9; public static final byte BYTECODE = 0; public static final byte JAVA = 1; diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advFixture/BoundsPromotionWithCapturedWildcards.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advFixture/BoundsPromotionWithCapturedWildcards.java new file mode 100644 index 000000000000..018b0bcbcd73 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advFixture/BoundsPromotionWithCapturedWildcards.java @@ -0,0 +1,13 @@ +import a.Provider; + +public abstract class Test { + private void configure(Provider provider, + Test sBind, + Test eBind) { + sBind.toProvider(provider); + eBind.toProvider(provider) + } + + abstract void toProvider(Provider var1); + abstract void toProvider(b.Provider var1); +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceIteratorForEachWithFor/afterCodeBlockBody.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceIteratorForEachWithFor/afterCodeBlockBody.java index bc57adb5ee1d..85ba91197a34 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceIteratorForEachWithFor/afterCodeBlockBody.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceIteratorForEachWithFor/afterCodeBlockBody.java @@ -6,6 +6,7 @@ public class CodeBlockBody { for (Iterator it2 = it1; it2.hasNext(); ) { Integer integer = it2.next(); System.out.println(integer + " a"); + // a comment System.out.println(integer + " b"); } } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceIteratorForEachWithFor/afterEmptyBlockBody.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceIteratorForEachWithFor/afterEmptyBlockBody.java new file mode 100644 index 000000000000..eaa1ba970096 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceIteratorForEachWithFor/afterEmptyBlockBody.java @@ -0,0 +1,10 @@ +// "Replace 'for each' loop with iterator 'for' loop" "true" +import java.util.Iterator; + +public class EmptyBlockBody { + void foo(Iterator it) { + for (Iterator it1 = it; it1.hasNext(); ) { + Integer integer = it1.next(); + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceIteratorForEachWithFor/beforeCodeBlockBody.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceIteratorForEachWithFor/beforeCodeBlockBody.java index 4e98a66b426d..3aeabbfe935f 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceIteratorForEachWithFor/beforeCodeBlockBody.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceIteratorForEachWithFor/beforeCodeBlockBody.java @@ -5,6 +5,7 @@ public class CodeBlockBody { void foo(Iterator it,Iterator it1) { for (Integer integer : it1) { System.out.println(integer + " a"); + // a comment System.out.println(integer + " b"); } } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceIteratorForEachWithFor/beforeEmptyBlockBody.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceIteratorForEachWithFor/beforeEmptyBlockBody.java new file mode 100644 index 000000000000..2ca6d3cf5ef4 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceIteratorForEachWithFor/beforeEmptyBlockBody.java @@ -0,0 +1,8 @@ +// "Replace 'for each' loop with iterator 'for' loop" "true" +import java.util.Iterator; + +public class EmptyBlockBody { + void foo(Iterator it) { + for (Integer integer : it) {} + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/LightAdvHighlightingFixtureTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/LightAdvHighlightingFixtureTest.java index ec4dac578f33..cae71202d460 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/LightAdvHighlightingFixtureTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/LightAdvHighlightingFixtureTest.java @@ -47,6 +47,14 @@ public class LightAdvHighlightingFixtureTest extends LightCodeInsightFixtureTest assertNull(ReferencesSearch.search(aClass).findFirst()); } + public void testBoundsPromotionWithCapturedWildcards() throws Exception { + myFixture.addClass("package a; public interface Provider {}"); + myFixture.addClass("package b; public interface Provider {}"); + + myFixture.configureByFile(getTestName(false) + ".java"); + myFixture.checkHighlighting(); + } + @Override protected String getBasePath() { return JavaTestUtil.getRelativeJavaTestDataPath() + "/codeInsight/daemonCodeAnalyzer/advFixture"; diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/ReplaceIteratorForEachLoopWithIteratorForLoopFixTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/ReplaceIteratorForEachLoopWithIteratorForLoopFixTest.java index cd7f9319a322..bb4ead8a28f1 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/ReplaceIteratorForEachLoopWithIteratorForLoopFixTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/ReplaceIteratorForEachLoopWithIteratorForLoopFixTest.java @@ -28,9 +28,9 @@ public class ReplaceIteratorForEachLoopWithIteratorForLoopFixTest extends LightQ public void test() throws Exception { doAllTests(); } @Override - protected void beforeActionStarted(String testName, String contents) { - super.beforeActionStarted(testName, contents); - if (testName.startsWith("Final")) { + protected void setUp() throws Exception { + super.setUp(); + if (getTestName(false).startsWith("Final")) { final CodeStyleSettings codeStyleSettings = CodeStyleSettingsManager.getSettings(getProject()); myFinalLocals = codeStyleSettings.GENERATE_FINAL_LOCALS; codeStyleSettings.GENERATE_FINAL_LOCALS = true; @@ -38,12 +38,16 @@ public class ReplaceIteratorForEachLoopWithIteratorForLoopFixTest extends LightQ } @Override - protected void afterActionCompleted(String testName, String contents) { - if (testName.startsWith("Final")) { - final CodeStyleSettings codeStyleSettings = CodeStyleSettingsManager.getSettings(getProject()); - codeStyleSettings.GENERATE_FINAL_LOCALS = myFinalLocals; + protected void tearDown() throws Exception { + try { + if (getTestName(false).startsWith("Final")) { + final CodeStyleSettings codeStyleSettings = CodeStyleSettingsManager.getSettings(getProject()); + codeStyleSettings.GENERATE_FINAL_LOCALS = myFinalLocals; + } + } + finally { + super.tearDown(); } - super.afterActionCompleted(testName, contents); } @Override diff --git a/java/java-tests/testSrc/com/intellij/index/IndexTest.groovy b/java/java-tests/testSrc/com/intellij/index/IndexTest.groovy index d1fd11639587..32ab8e9cdbf7 100644 --- a/java/java-tests/testSrc/com/intellij/index/IndexTest.groovy +++ b/java/java-tests/testSrc/com/intellij/index/IndexTest.groovy @@ -51,6 +51,7 @@ import com.intellij.testFramework.exceptionCases.IllegalArgumentExceptionCase import com.intellij.testFramework.fixtures.JavaCodeInsightFixtureTestCase import com.intellij.util.Processor import com.intellij.util.indexing.FileBasedIndex +import com.intellij.util.indexing.FileBasedIndexImpl import com.intellij.util.indexing.MapIndexStorage import com.intellij.util.indexing.StorageException import com.intellij.util.io.* @@ -409,6 +410,27 @@ public class IndexTest extends JavaCodeInsightFixtureTestCase { } } + public void "test no index stamp update when no change"() throws IOException { + final VirtualFile vFile = myFixture.addClass("class Foo {}").getContainingFile().getVirtualFile(); + def stamp = ((FileBasedIndexImpl)FileBasedIndex.instance).getIndexModificationStamp(IdIndex.NAME, project) + + VfsUtil.saveText(vFile, "Foo class") + assertTrue(stamp == ((FileBasedIndexImpl)FileBasedIndex.instance).getIndexModificationStamp(IdIndex.NAME, project)) + + VfsUtil.saveText(vFile, "class Foo2 {}") + assertTrue(stamp != ((FileBasedIndexImpl)FileBasedIndex.instance).getIndexModificationStamp(IdIndex.NAME, project)) + + final Document document = FileDocumentManager.getInstance().getDocument(vFile); + document.setText("Foo2 class") + stamp = ((FileBasedIndexImpl)FileBasedIndex.instance).getIndexModificationStamp(IdIndex.NAME, project) + document.setText("class Foo2") + assertTrue(stamp == ((FileBasedIndexImpl)FileBasedIndex.instance).getIndexModificationStamp(IdIndex.NAME, project)) + + document.setText("Foo3 class"); + PsiDocumentManager.getInstance(project).commitAllDocuments(); + assertTrue(stamp != ((FileBasedIndexImpl)FileBasedIndex.instance).getIndexModificationStamp(IdIndex.NAME, project)) + } + public void "test do not collect stub tree while holding stub elements"() throws IOException { final VirtualFile vFile = myFixture.addClass("class Foo {}").getContainingFile().getVirtualFile(); diff --git a/java/java-tests/testSrc/com/intellij/testIntergration/RecentTestsOrderTest.kt b/java/java-tests/testSrc/com/intellij/testIntergration/RecentTestsOrderTest.kt index b30883240cf0..364b10ed3595 100644 --- a/java/java-tests/testSrc/com/intellij/testIntergration/RecentTestsOrderTest.kt +++ b/java/java-tests/testSrc/com/intellij/testIntergration/RecentTestsOrderTest.kt @@ -37,11 +37,11 @@ class RecentTestsOrderTest: LightIdeaTestCase() { } fun addPassedSuite(suiteUrl: String, date: Date = Date(), runConfiguration: RunnerAndConfigurationSettings = allTests) { - data.addSuite(suiteUrl, PASSED_INDEX, date, runConfiguration) + data.addSuite(suiteUrl, date, runConfiguration) } fun addFailedSuite(suiteUrl: String, date: Date = Date(), runConfiguration: RunnerAndConfigurationSettings = allTests) { - data.addSuite(suiteUrl, FAILED_INDEX, date, runConfiguration) + data.addSuite(suiteUrl, date, runConfiguration) } fun addPassedTest(testUrl: String, date: Date = Date(), runConfiguration: RunnerAndConfigurationSettings = allTests) { @@ -57,11 +57,11 @@ class RecentTestsOrderTest: LightIdeaTestCase() { val test1 = "MySingleTest.test1".test() data.addTest(test1, PASSED_INDEX, now, allTests) - data.addSuite(suite, PASSED_INDEX, now, allTests) + data.addSuite(suite, now, allTests) val testsToShow = data.getTestsToShow() assertThat(testsToShow).hasSize(1) - assertThat(testsToShow[0].presentation).isEqualTo("MySingleTest") + assertThat(testsToShow[0].presentation).isEqualTo("all tests") } fun `test run configuration with multiple suites shows run configuration name`() { @@ -90,13 +90,10 @@ class RecentTestsOrderTest: LightIdeaTestCase() { addFailedTest(test) val tests = data.getTestsToShow() - assertThat(tests).hasSize(2) + assertThat(tests).hasSize(1) assertThat(tests[0].presentation).isEqualTo("SingleTest.test") - assertThat(tests[0].magnitude).isEqualTo(FAILED_INDEX) - - assertThat(tests[1].presentation).isEqualTo("SingleTest") - assertThat(tests[1].magnitude).isEqualTo(FAILED_INDEX) + assertThat(tests[0].failed).isEqualTo(true) } fun `test show failed suite and test in run configuration`() { @@ -111,41 +108,29 @@ class RecentTestsOrderTest: LightIdeaTestCase() { addPassedSuite("PassedSuite".suite()) val tests = data.getTestsToShow() - assertThat(tests).hasSize(3) + assertThat(tests).hasSize(1) assertThat(tests[0].presentation).isEqualTo("SingleTest.test") - assertThat(tests[0].magnitude).isEqualTo(FAILED_INDEX) - - assertThat(tests[1].presentation).isEqualTo("SingleTest") - assertThat(tests[1].magnitude).isEqualTo(FAILED_INDEX) - - assertThat(tests[2].presentation).isEqualTo("all tests") - assertThat(tests[2].magnitude).isEqualTo(FAILED_INDEX) + assertThat(tests[0].failed).isEqualTo(true) } fun `test single test run doesn't override suite status`() { val singleTestConfig = mockConfiguration("single test", "Junit.single test") val newNow = Date(now.time + 100000) - //current single test run - addFailedSuite("Test".suite(), newNow, singleTestConfig) - addFailedTest("Test.testOK".test(), newNow, singleTestConfig) //previous all suite run - addPassedTest("Test.testOK".test(), now, allTests) addFailedTest("Test.testFailed".test(), now, allTests) + //current single test run + addPassedSuite("Test".suite(), newNow, singleTestConfig) + addPassedTest("Test.testOK".test(), newNow, singleTestConfig) + val tests = data.getTestsToShow() - assertThat(tests).hasSize(3) - - assertThat(tests[0].presentation).isEqualTo("Test.testOK") - assertThat(tests[0].magnitude).isEqualTo(FAILED_INDEX) + assertThat(tests).hasSize(2) + assertThat(tests[0].presentation).isEqualTo("single test") assertThat(tests[1].presentation).isEqualTo("Test.testFailed") - assertThat(tests[1].magnitude).isEqualTo(FAILED_INDEX) - - assertThat(tests[2].presentation).isEqualTo("Test") - assertThat(tests[2].magnitude).isEqualTo(FAILED_INDEX) } @@ -156,18 +141,15 @@ class RecentTestsOrderTest: LightIdeaTestCase() { //previous all suite run addPassedTest("Test.testOK".test(), now, allTests) - addFailedTest("Test.testFailed".test(), now, allTests) //current single test run addFailedSuite("Test".suite(), newNow, singleTestConfig) - addFailedTest("Test.testOK".test(), newNow, singleTestConfig) + addFailedTest("Test.testFailed".test(), newNow, singleTestConfig) val tests = data.getTestsToShow() - assertThat(tests).hasSize(3) + assertThat(tests).hasSize(1) - assertThat(tests[0].presentation).isEqualTo("Test.testOK") - assertThat(tests[1].presentation).isEqualTo("Test.testFailed") - assertThat(tests[2].presentation).isEqualTo("Test") + assertThat(tests[0].presentation).isEqualTo("Test.testFailed") } } \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/testIntergration/RecentTestsTest.kt b/java/java-tests/testSrc/com/intellij/testIntergration/RecentTestsTest.kt index 13967b0baa78..87dd94da7420 100644 --- a/java/java-tests/testSrc/com/intellij/testIntergration/RecentTestsTest.kt +++ b/java/java-tests/testSrc/com/intellij/testIntergration/RecentTestsTest.kt @@ -48,18 +48,10 @@ class RecentTestsStepTest: LightIdeaTestCase() { now = Date() } - fun `test show suites without run configuration`() { - data.addTest("Test.x".test(), PASSED_INDEX, now, null) - data.addSuite("Test".suite(), PASSED_INDEX, now, null) - - val tests = data.getTestsToShow() - assertThat(tests).hasSize(1) - } - fun `test all tests passed`() { data.addTest("Test.textXXX".test(), PASSED_INDEX, now, allTests) - data.addSuite("Test".suite(), PASSED_INDEX, now, allTests) - data.addSuite("JFSDTest".suite(), PASSED_INDEX, now, allTests) + data.addSuite("Test".suite(), now, allTests) + data.addSuite("JFSDTest".suite(), now, allTests) data.addTest("Test.textYYY".test(), PASSED_INDEX, now, allTests) data.addTest("JFSDTest.testItMakesMeSadToFixIt".test(), PASSED_INDEX, now, allTests) data.addTest("Test.textZZZ".test(), PASSED_INDEX, now, allTests) @@ -73,8 +65,8 @@ class RecentTestsStepTest: LightIdeaTestCase() { fun `test if one failed in run configuration show failed suite`() { - data.addSuite("JFSDTest".suite(), FAILED_INDEX, now, allTests) - data.addSuite("Test".test(), PASSED_INDEX, now, allTests) + data.addSuite("JFSDTest".suite(), now, allTests) + data.addSuite("Test".suite(), now, allTests) data.addTest("JFSDTest.testItMakesMeSadToFixIt".test(), FAILED_INDEX, now, allTests) data.addTest("JFSDTest.testUnconditionalAlignmentErrorneous".test(), PASSED_INDEX, now, allTests) @@ -83,28 +75,21 @@ class RecentTestsStepTest: LightIdeaTestCase() { val tests = data.getTestsToShow() - assertThat(tests).hasSize(3) + assertThat(tests).hasSize(1) assertThat(tests[0].presentation).isEqualTo("JFSDTest.testItMakesMeSadToFixIt") - assertThat(tests[0].magnitude).isEqualTo(FAILED_INDEX) - - assertThat(tests[1].presentation).isEqualTo("JFSDTest") - assertThat(tests[1].magnitude).isEqualTo(FAILED_INDEX) - - assertThat(tests[2].presentation).isEqualTo("all tests") - assertThat(tests[2].magnitude).isEqualTo(FAILED_INDEX) + assertThat(tests[0].failed).isEqualTo(true) } fun `test if configuration with single test show failed test`() { - data.addSuite("JFSDTest".suite(), FAILED_INDEX, now, allTests) + data.addSuite("JFSDTest".suite(), now, allTests) data.addTest("JFSDTest.testItMakesMeSadToFixIt".test(), FAILED_INDEX, now, allTests) data.addTest("JFSDTest.testUnconditionalAlignmentErrorneous".test(), PASSED_INDEX, now, allTests) val tests = data.getTestsToShow() - assertThat(tests).hasSize(2) + assertThat(tests).hasSize(1) assertThat(tests[0].presentation).isEqualTo("JFSDTest.testItMakesMeSadToFixIt") - assertThat(tests[1].presentation).isEqualTo("JFSDTest") } diff --git a/jps/jps-builders/src/org/jetbrains/jps/javac/ExternalJavacManager.java b/jps/jps-builders/src/org/jetbrains/jps/javac/ExternalJavacManager.java index bcb413016a94..4723457fcca6 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/javac/ExternalJavacManager.java +++ b/jps/jps-builders/src/org/jetbrains/jps/javac/ExternalJavacManager.java @@ -326,7 +326,7 @@ public class ExternalJavacManager { private class CompilationRequestsHandler extends SimpleChannelInboundHandler { @Override public void channelUnregistered(ChannelHandlerContext ctx) throws Exception { - JavacProcessDescriptor descriptor = ctx.channel().attr(SESSION_DESCRIPTOR).get(); + JavacProcessDescriptor descriptor = ctx.channel().attr(SESSION_DESCRIPTOR).getAndRemove(); if (descriptor != null) { descriptor.setDone(); } diff --git a/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaServiceImpl.java b/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaServiceImpl.java index 7abe451f88c3..26562132b5f0 100644 --- a/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaServiceImpl.java +++ b/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaServiceImpl.java @@ -15,7 +15,6 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.fileTypes.FileType; import com.intellij.openapi.fileTypes.LanguageFileType; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.NullableLazyValue; import com.intellij.openapi.util.Pair; import com.intellij.openapi.vfs.VfsUtil; @@ -99,7 +98,10 @@ public class JsonSchemaServiceImpl implements JsonSchemaServiceEx { for (JsonSchemaProviderFactory factory : factories) { final List providers = factory.getProviders(project); for (JsonSchemaFileProvider provider : providers) { - mySchemaFiles.add(provider.getSchemaFile()); + final VirtualFile schemaFile = provider.getSchemaFile(); + if (schemaFile != null) { + mySchemaFiles.add(schemaFile); + } } } initialized = true; diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/WellFormednessUtils.java b/platform/core-api/src/com/intellij/openapi/vfs/VirtualFileWithoutContent.java similarity index 55% rename from plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/WellFormednessUtils.java rename to platform/core-api/src/com/intellij/openapi/vfs/VirtualFileWithoutContent.java index 6c8bce382525..2651966568e4 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/WellFormednessUtils.java +++ b/platform/core-api/src/com/intellij/openapi/vfs/VirtualFileWithoutContent.java @@ -1,5 +1,5 @@ /* - * Copyright 2003-2005 Dave Griffith + * 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. @@ -13,20 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.siyeh.ig.psiutils; +package com.intellij.openapi.vfs; -import com.intellij.psi.PsiAssignmentExpression; -import com.intellij.psi.PsiExpression; -import org.jetbrains.annotations.NotNull; - -public class WellFormednessUtils { - private WellFormednessUtils() { - super(); - } - - public static boolean isWellFormed(@NotNull PsiAssignmentExpression expression) { - - final PsiExpression rhs = expression.getRExpression(); - return rhs != null; - } +/** + * @author Irina.Chernushina on 7/4/2016. + */ +public interface VirtualFileWithoutContent { } diff --git a/platform/core-impl/src/com/intellij/openapi/application/ex/ApplicationInfoEx.java b/platform/core-impl/src/com/intellij/openapi/application/ex/ApplicationInfoEx.java index 8e805f91cb41..d0d7b52750bf 100644 --- a/platform/core-impl/src/com/intellij/openapi/application/ex/ApplicationInfoEx.java +++ b/platform/core-impl/src/com/intellij/openapi/application/ex/ApplicationInfoEx.java @@ -114,4 +114,17 @@ public abstract class ApplicationInfoEx extends ApplicationInfo { @Nullable public abstract String getCustomizeIDEWizardStepsProvider(); + + public abstract String getSubscriptionFormId(); + + public abstract String getSubscriptionNewsKey(); + + public abstract String getSubscriptionNewsValue(); + + public abstract String getSubscriptionTipsKey(); + + public abstract boolean areSubscriptionTipsAvailable(); + + @Nullable + public abstract String getSubscriptionAdditionalFormData(); } diff --git a/platform/core-impl/src/com/intellij/openapi/application/impl/ApplicationInfoImpl.java b/platform/core-impl/src/com/intellij/openapi/application/impl/ApplicationInfoImpl.java index 30e81f2b7411..be8d164a98eb 100644 --- a/platform/core-impl/src/com/intellij/openapi/application/impl/ApplicationInfoImpl.java +++ b/platform/core-impl/src/com/intellij/openapi/application/impl/ApplicationInfoImpl.java @@ -112,6 +112,13 @@ public class ApplicationInfoImpl extends ApplicationInfoEx { private String myEvalLicenseUrl = "https://www.jetbrains.com/store/license.html"; private String myKeyConversionUrl = "https://www.jetbrains.com/shop/eform/keys-exchange"; + private String mySubscriptionFormId; + private String mySubscriptionNewsKey; + private String mySubscriptionNewsValue; + private String mySubscriptionTipsKey; + private boolean mySubscriptionTipsAvailable; + private String mySubscriptionAdditionalFormData; + private Rectangle myAboutLogoRect; private static final String IDEA_PATH = "/idea/"; @@ -193,6 +200,14 @@ public class ApplicationInfoImpl extends ApplicationInfoEx { private static final String ATTRIBUTE_KEY_CONVERSION_URL = "key-conversion-url"; private static final String ESSENTIAL_PLUGIN = "essential-plugin"; + private static final String ELEMENT_SUBSCRIPTIONS = "subscriptions"; + private static final String ATTRIBUTE_SUBSCRIPTIONS_FORM_ID = "formid"; + private static final String ATTRIBUTE_SUBSCRIPTIONS_NEWS_KEY = "news-key"; + private static final String ATTRIBUTE_SUBSCRIPTIONS_NEWS_VALUE = "news-value"; + private static final String ATTRIBUTE_SUBSCRIPTIONS_TIPS_KEY = "tips-key"; + private static final String ATTRIBUTE_SUBSCRIPTIONS_TIPS_AVAILABLE = "tips-available"; + private static final String ATTRIBUTE_SUBSCRIPTIONS_ADDITIONAL_FORM_DATA = "additional-form-data"; + private static final String DEFAULT_PLUGINS_HOST = "http://plugins.jetbrains.com"; ApplicationInfoImpl() { @@ -554,6 +569,37 @@ public class ApplicationInfoImpl extends ApplicationInfoEx { return myAboutLogoRect; } + @Override + public String getSubscriptionFormId() { + return mySubscriptionFormId; + } + + @Override + public String getSubscriptionNewsKey() { + return mySubscriptionNewsKey; + } + + @Override + public String getSubscriptionNewsValue() { + return mySubscriptionNewsValue; + } + + @Override + public String getSubscriptionTipsKey() { + return mySubscriptionTipsKey; + } + + @Override + public boolean areSubscriptionTipsAvailable() { + return mySubscriptionTipsAvailable; + } + + @Nullable + @Override + public String getSubscriptionAdditionalFormData() { + return mySubscriptionAdditionalFormData; + } + private static ApplicationInfoImpl ourShadowInstance; public boolean isBetaOrRC() { @@ -866,6 +912,15 @@ public class ApplicationInfoImpl extends ApplicationInfoEx { } } + Element subscriptionsElement = parentNode.getChild(ELEMENT_SUBSCRIPTIONS); + if (subscriptionsElement != null) { + mySubscriptionFormId = subscriptionsElement.getAttributeValue(ATTRIBUTE_SUBSCRIPTIONS_FORM_ID); + mySubscriptionNewsKey = subscriptionsElement.getAttributeValue(ATTRIBUTE_SUBSCRIPTIONS_NEWS_KEY); + mySubscriptionNewsValue = subscriptionsElement.getAttributeValue(ATTRIBUTE_SUBSCRIPTIONS_NEWS_VALUE, "yes"); + mySubscriptionTipsKey = subscriptionsElement.getAttributeValue(ATTRIBUTE_SUBSCRIPTIONS_TIPS_KEY); + mySubscriptionTipsAvailable = Boolean.parseBoolean(subscriptionsElement.getAttributeValue(ATTRIBUTE_SUBSCRIPTIONS_TIPS_AVAILABLE)); + mySubscriptionAdditionalFormData = subscriptionsElement.getAttributeValue(ATTRIBUTE_SUBSCRIPTIONS_ADDITIONAL_FORM_DATA); + } } private static void setBuildNumber(String apiVersion, String buildNumber) { diff --git a/platform/diff-impl/src/com/intellij/diff/actions/BaseShowDiffAction.java b/platform/diff-impl/src/com/intellij/diff/actions/BaseShowDiffAction.java index 4f3a3627bf78..2da043cc5812 100644 --- a/platform/diff-impl/src/com/intellij/diff/actions/BaseShowDiffAction.java +++ b/platform/diff-impl/src/com/intellij/diff/actions/BaseShowDiffAction.java @@ -23,6 +23,8 @@ import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.Presentation; import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.project.Project; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.vfs.VirtualFileWithoutContent; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -48,6 +50,10 @@ abstract class BaseShowDiffAction extends AnAction implements DumbAware { protected abstract boolean isAvailable(@NotNull AnActionEvent e); + protected static boolean hasContent(VirtualFile file) { + return !(file instanceof VirtualFileWithoutContent); + } + @Nullable protected abstract DiffRequest getDiffRequest(@NotNull AnActionEvent e); } diff --git a/platform/diff-impl/src/com/intellij/diff/actions/CompareFileWithEditorAction.java b/platform/diff-impl/src/com/intellij/diff/actions/CompareFileWithEditorAction.java index bbaa285236a1..4c7939552a37 100644 --- a/platform/diff-impl/src/com/intellij/diff/actions/CompareFileWithEditorAction.java +++ b/platform/diff-impl/src/com/intellij/diff/actions/CompareFileWithEditorAction.java @@ -72,7 +72,7 @@ public class CompareFileWithEditorAction extends BaseShowDiffAction { } private static boolean canCompare(@NotNull VirtualFile file1, @NotNull VirtualFile file2) { - return file1.isValid() && file2.isValid() && !file1.equals(file2); + return !file1.equals(file2) && hasContent(file1) && hasContent(file2); } @Nullable diff --git a/platform/diff-impl/src/com/intellij/diff/actions/CompareFilesAction.java b/platform/diff-impl/src/com/intellij/diff/actions/CompareFilesAction.java index 39f47ddda532..91b51d357f5f 100644 --- a/platform/diff-impl/src/com/intellij/diff/actions/CompareFilesAction.java +++ b/platform/diff-impl/src/com/intellij/diff/actions/CompareFilesAction.java @@ -83,20 +83,16 @@ public class CompareFilesAction extends BaseShowDiffAction { } if (files.length == 1) { - return isValidAndLocal(files[0]); + return hasContent(files[0]); } else if (files.length == 2) { - return isValidAndLocal(files[0]) && isValidAndLocal(files[1]); + return hasContent(files[0]) && hasContent(files[1]); } else { return false; } } - private static boolean isValidAndLocal(VirtualFile file) { - return file.isValid() && file.isInLocalFileSystem(); - } - @Nullable @Override protected DiffRequest getDiffRequest(@NotNull AnActionEvent e) { @@ -110,7 +106,7 @@ public class CompareFilesAction extends BaseShowDiffAction { if (data.length == 1) { VirtualFile otherFile = getOtherFile(project, data[0]); if (otherFile == null) return null; - if (!data[0].isValid()) return null; + if (!hasContent(data[0])) return null; return DiffRequestFactory.getInstance().createFromFiles(project, data[0], otherFile); } else { diff --git a/platform/diff-impl/src/com/intellij/diff/tools/external/ExternalDiffToolUtil.java b/platform/diff-impl/src/com/intellij/diff/tools/external/ExternalDiffToolUtil.java index b3434dfd28f3..6dafc46c2e22 100644 --- a/platform/diff-impl/src/com/intellij/diff/tools/external/ExternalDiffToolUtil.java +++ b/platform/diff-impl/src/com/intellij/diff/tools/external/ExternalDiffToolUtil.java @@ -34,6 +34,7 @@ import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.vfs.VirtualFileWithoutContent; import com.intellij.util.LineSeparator; import com.intellij.util.PathUtil; import com.intellij.util.TimeoutUtil; @@ -56,7 +57,11 @@ public class ExternalDiffToolUtil { public static boolean canCreateFile(@NotNull DiffContent content) { if (content instanceof EmptyContent) return true; if (content instanceof DocumentContent) return true; - if (content instanceof FileContent) return true; + if (content instanceof FileContent) { + VirtualFile file = ((FileContent)content).getFile(); + if (file instanceof VirtualFileWithoutContent) return false; + return true; + } if (content instanceof DirectoryContent) return ((DirectoryContent)content).getFile().isInLocalFileSystem(); return false; } diff --git a/platform/diff-impl/src/com/intellij/diff/tools/holders/BinaryEditorHolder.java b/platform/diff-impl/src/com/intellij/diff/tools/holders/BinaryEditorHolder.java index 0b455181d04b..2f13d0c8b023 100644 --- a/platform/diff-impl/src/com/intellij/diff/tools/holders/BinaryEditorHolder.java +++ b/platform/diff-impl/src/com/intellij/diff/tools/holders/BinaryEditorHolder.java @@ -34,6 +34,7 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.project.ProjectManager; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.vfs.VirtualFileWithoutContent; import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -130,6 +131,7 @@ public class BinaryEditorHolder extends EditorHolder { if (project == null) project = ProjectManager.getInstance().getDefaultProject(); VirtualFile file = ((FileContent)content).getFile(); if (!file.isValid()) return false; + if (file instanceof VirtualFileWithoutContent) return false; return FileEditorProviderManager.getInstance().getProviders(project, file).length != 0; } return false; diff --git a/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleDiffViewer.java b/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleDiffViewer.java index f45d82dc53ed..7577e69532e7 100644 --- a/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleDiffViewer.java +++ b/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleDiffViewer.java @@ -99,6 +99,7 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer { @CalledInAwt protected void onDispose() { destroyChangedBlocks(); + myFoldingModel.destroy(); super.onDispose(); } @@ -225,6 +226,7 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer { private Runnable apply(@NotNull final CompareData data) { return () -> { myFoldingModel.updateContext(myRequest, getFoldingModelSettings()); + clearDiffPresentation(); myIsContentsEqual = data.isContentsEqual(); @@ -253,6 +255,7 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer { private Runnable applyNotification(@Nullable final JComponent notification) { return () -> { clearDiffPresentation(); + myFoldingModel.destroy(); if (notification != null) myPanel.addNotification(notification); }; } @@ -290,8 +293,6 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer { } myInvalidDiffChanges.clear(); - myFoldingModel.destroy(); - myContentPanel.repaintDivider(); myStatusPanel.update(); } diff --git a/platform/diff-impl/src/com/intellij/diff/tools/simple/ThreesideTextDiffViewerEx.java b/platform/diff-impl/src/com/intellij/diff/tools/simple/ThreesideTextDiffViewerEx.java index 8a413b1b7e21..a8cb44402d44 100644 --- a/platform/diff-impl/src/com/intellij/diff/tools/simple/ThreesideTextDiffViewerEx.java +++ b/platform/diff-impl/src/com/intellij/diff/tools/simple/ThreesideTextDiffViewerEx.java @@ -88,6 +88,7 @@ public abstract class ThreesideTextDiffViewerEx extends ThreesideTextDiffViewer @CalledInAwt protected void onDispose() { destroyChangedBlocks(); + myFoldingModel.destroy(); super.onDispose(); } @@ -119,6 +120,7 @@ public abstract class ThreesideTextDiffViewerEx extends ThreesideTextDiffViewer protected Runnable applyNotification(@Nullable final JComponent notification) { return () -> { clearDiffPresentation(); + myFoldingModel.destroy(); if (notification != null) myPanel.addNotification(notification); }; } @@ -135,7 +137,6 @@ public abstract class ThreesideTextDiffViewerEx extends ThreesideTextDiffViewer @CalledInAwt protected void destroyChangedBlocks() { - myFoldingModel.destroy(); } // diff --git a/platform/diff-impl/src/com/intellij/diff/tools/util/FoldingModelSupport.java b/platform/diff-impl/src/com/intellij/diff/tools/util/FoldingModelSupport.java index f81d6f159692..4d1ef04bcaf9 100644 --- a/platform/diff-impl/src/com/intellij/diff/tools/util/FoldingModelSupport.java +++ b/platform/diff-impl/src/com/intellij/diff/tools/util/FoldingModelSupport.java @@ -100,12 +100,21 @@ public class FoldingModelSupport { @NotNull final Settings settings) { ApplicationManager.getApplication().assertIsDispatchThread(); - if (changedLines == null) return; - if (settings.range == -1) return; + for (FoldedBlock folding : getFoldedBlocks()) { + folding.destroyHighlighter(); + } runBatchOperation(() -> { - FoldingBuilder builder = new FoldingBuilder(context, settings); - builder.build(changedLines); + for (FoldedBlock folding : getFoldedBlocks()) { + folding.destroyFolding(); + } + myFoldings.clear(); + + + if (changedLines != null && settings.range != -1) { + FoldingBuilder builder = new FoldingBuilder(context, settings); + builder.build(changedLines); + } }); updateLineNumbers(true); @@ -244,23 +253,15 @@ public class FoldingModelSupport { } public void destroy() { - for (int i = 0; i < myCount; i++) { - destroyFoldings(i); - } - for (FoldedBlock folding : getFoldedBlocks()) { folding.destroyHighlighter(); } - myFoldings.clear(); - } - private void destroyFoldings(final int index) { - final FoldingModelEx model = myEditors[index].getFoldingModel(); - model.runBatchFoldingOperation(() -> { + runBatchOperation(() -> { for (FoldedBlock folding : getFoldedBlocks()) { - FoldRegion region = folding.getRegion(index); - if (region != null) model.removeFoldRegion(region); + folding.destroyFolding(); } + myFoldings.clear(); }); } @@ -615,6 +616,13 @@ public class FoldingModelSupport { } } + public void destroyFolding() { + for (int i = 0; i < myCount; i++) { + FoldRegion region = myRegions[i]; + if (region != null) myEditors[i].getFoldingModel().removeFoldRegion(region); + } + } + public void destroyHighlighter() { for (RangeHighlighter highlighter : myHighlighters) { highlighter.dispose(); diff --git a/platform/indexing-impl/src/com/intellij/psi/search/FilenameIndex.java b/platform/indexing-impl/src/com/intellij/psi/search/FilenameIndex.java index 0bee35cdbf9a..e1416c5cb667 100644 --- a/platform/indexing-impl/src/com/intellij/psi/search/FilenameIndex.java +++ b/platform/indexing-impl/src/com/intellij/psi/search/FilenameIndex.java @@ -80,7 +80,7 @@ public class FilenameIndex extends ScalarIndexExtension { @Override public int getVersion() { - return 1 + (FileBasedIndex.ourEnableTracingOfKeyHashToVirtualFileMapping ? 1 : 0); + return 1 + (FileBasedIndex.ourEnableTracingOfKeyHashToVirtualFileMapping ? 2 : 0); } public static String[] getAllFilenames(Project project) { diff --git a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/PsiElementListNavigator.java b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/PsiElementListNavigator.java index 5396c133dfc1..259aecaa5e0f 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/PsiElementListNavigator.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/PsiElementListNavigator.java @@ -145,7 +145,7 @@ public class PsiElementListNavigator { setCancelCallback(() -> { HintUpdateSupply.hideHint(list); if (listUpdaterTask != null) { - return listUpdaterTask.cancelTask(); + listUpdaterTask.cancelTask(); } return true; }); diff --git a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/ShowIntentionsPass.java b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/ShowIntentionsPass.java index 856683d72c2f..1db33b8ce8c8 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/ShowIntentionsPass.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/ShowIntentionsPass.java @@ -20,6 +20,7 @@ import com.intellij.codeHighlighting.HighlightDisplayLevel; import com.intellij.codeHighlighting.TextEditorHighlightingPass; import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer; import com.intellij.codeInsight.daemon.HighlightDisplayKey; +import com.intellij.codeInsight.daemon.impl.analysis.HighlightingLevelManager; import com.intellij.codeInsight.hint.HintManager; import com.intellij.codeInsight.intention.IntentionAction; import com.intellij.codeInsight.intention.IntentionManager; @@ -311,7 +312,9 @@ public class ShowIntentionsPass extends TextEditorHighlightingPass { } } - collectIntentionsFromDoNotShowLeveledInspections(project, hostFile, psiElement, offset, intentions); + if (HighlightingLevelManager.getInstance(project).shouldInspect(hostFile)) { + collectIntentionsFromDoNotShowLeveledInspections(project, hostFile, psiElement, offset, intentions); + } final int line = hostDocument.getLineNumber(offset); MarkupModelEx model = (MarkupModelEx)DocumentMarkupModel.forDocument(hostDocument, project, true); diff --git a/platform/lang-impl/src/com/intellij/codeInsight/dataflow/DFAEngine.java b/platform/lang-impl/src/com/intellij/codeInsight/dataflow/DFAEngine.java index 79c7266f5d17..f89260919b09 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/dataflow/DFAEngine.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/dataflow/DFAEngine.java @@ -57,15 +57,14 @@ public class DFAEngine { final boolean[] visited = new boolean[myFlow.length]; - final boolean forward = myDfa.isForward(); final int[] order = ControlFlowUtil.postOrder(myFlow); // Count limit for number of iterations per worklist - final int limit = getIterationLimit(forward); + final int limit = getIterationLimit(); int dfaCount = 0; final long startTime = System.nanoTime(); - for (int i = forward ? 0 : myFlow.length - 1; forward ? i < myFlow.length : i >= 0; ) { + for (int i = 0; i < myFlow.length; i++) { // Check if canceled ProgressManager.checkCanceled(); @@ -124,12 +123,6 @@ public class DFAEngine { } // Move to another worklist - if (forward) { - i++; - } - else { - i--; - } dfaCount += count; } if (LOG.isDebugEnabled()) { @@ -144,10 +137,10 @@ public class DFAEngine { * Every node in dfa should be processed <= pred times * 2 * Multiplier 2 is because of cycles. */ - private int getIterationLimit(final boolean forward) { + private int getIterationLimit() { int allPred = myFlow.length; for (Instruction instruction : myFlow) { - allPred += forward ? instruction.allPred().size() : instruction.allSucc().size(); + allPred += instruction.allPred().size(); } return allPred * 2; } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/dataflow/DfaInstance.java b/platform/lang-impl/src/com/intellij/codeInsight/dataflow/DfaInstance.java index 894e54d0af5b..eea25a3e34c8 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/dataflow/DfaInstance.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/dataflow/DfaInstance.java @@ -26,5 +26,8 @@ public interface DfaInstance { @NotNull E initial(); + /** + * @deprecated + */ boolean isForward(); } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/navigation/GotoTargetHandler.java b/platform/lang-impl/src/com/intellij/codeInsight/navigation/GotoTargetHandler.java index a5b1ecf27c97..6ca91442f373 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/navigation/GotoTargetHandler.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/navigation/GotoTargetHandler.java @@ -184,7 +184,7 @@ public abstract class GotoTargetHandler implements CodeInsightActionHandler { HintUpdateSupply.hideHint(list); final ListBackgroundUpdaterTask task = gotoData.listUpdaterTask; if (task != null) { - return task.cancelTask(); + task.cancelTask(); } return true; }). diff --git a/platform/lang-impl/src/com/intellij/util/indexing/FileBasedIndexImpl.java b/platform/lang-impl/src/com/intellij/util/indexing/FileBasedIndexImpl.java index ce59608b632b..1d3a8a7bc364 100644 --- a/platform/lang-impl/src/com/intellij/util/indexing/FileBasedIndexImpl.java +++ b/platform/lang-impl/src/com/intellij/util/indexing/FileBasedIndexImpl.java @@ -806,6 +806,15 @@ public class FileBasedIndexImpl extends FileBasedIndex { : processValuesInScope(indexId, dataKey, false, filter, idFilter, processor); } + public long getIndexModificationStamp(ID indexId, @NotNull Project project) { + UpdatableIndex index = getState().getIndex(indexId); + if (index instanceof MapReduceIndex) { + ensureUpToDate(indexId, project, GlobalSearchScope.allScope(project)); + return ((MapReduceIndex)index).getModificationStamp(); + } + return -1; + } + public interface IdValueProcessor { /** * @param fileId the id of the file that the value came from diff --git a/platform/lang-impl/src/com/intellij/util/indexing/MapReduceIndex.java b/platform/lang-impl/src/com/intellij/util/indexing/MapReduceIndex.java index 9a324bdbdff7..0252c5e79e03 100644 --- a/platform/lang-impl/src/com/intellij/util/indexing/MapReduceIndex.java +++ b/platform/lang-impl/src/com/intellij/util/indexing/MapReduceIndex.java @@ -46,6 +46,7 @@ import java.io.*; import java.nio.charset.Charset; import java.util.*; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantReadWriteLock; @@ -66,6 +67,7 @@ public class MapReduceIndex implements UpdatableIndex myExtension; private final AtomicBoolean myInMemoryMode = new AtomicBoolean(); + private final AtomicLong myModificationStamp = new AtomicLong(); private final TIntObjectHashMap> myInMemoryKeys = new TIntObjectHashMap>(); private PersistentHashMap myContents; @@ -900,6 +902,10 @@ public class MapReduceIndex implements UpdatableIndex { private final int savedInputId; private final @NotNull Map newData; @@ -934,6 +940,7 @@ public class MapReduceIndex implements UpdatableIndex() { @Override public void process(Key key, int inputId) throws StorageException { + myModificationStamp.incrementAndGet(); myStorage.removeAllValues(key, inputId); } }; @@ -941,6 +948,7 @@ public class MapReduceIndex implements UpdatableIndex myAddedKeyProcessor = new MapDiffUpdateData.AddedKeyProcessor() { @Override public void process(Key key, Value value, int inputId) throws StorageException { + myModificationStamp.incrementAndGet(); myStorage.addValue(key, inputId, value); } }; diff --git a/platform/lang-impl/src/com/intellij/util/indexing/containers/ChangeBufferingList.java b/platform/lang-impl/src/com/intellij/util/indexing/containers/ChangeBufferingList.java index 57512fb4fd4d..7e9076282b8e 100644 --- a/platform/lang-impl/src/com/intellij/util/indexing/containers/ChangeBufferingList.java +++ b/platform/lang-impl/src/com/intellij/util/indexing/containers/ChangeBufferingList.java @@ -84,10 +84,10 @@ public class ChangeBufferingList implements Cloneable { } public void remove(int value) { + ensureCapacity(1); if (checkSet != null) checkSet.remove(value); RandomAccessIntContainer intContainer = randomAccessContainer; if (intContainer == null) { - ensureCapacity(1); addChange(-value); } else { diff --git a/platform/platform-api/src/com/intellij/ui/components/JBScrollPane.java b/platform/platform-api/src/com/intellij/ui/components/JBScrollPane.java index 34fa55ca3efc..2cf04c24af5b 100644 --- a/platform/platform-api/src/com/intellij/ui/components/JBScrollPane.java +++ b/platform/platform-api/src/com/intellij/ui/components/JBScrollPane.java @@ -44,6 +44,7 @@ import java.awt.event.InputEvent; import java.awt.event.MouseEvent; import java.awt.event.MouseWheelEvent; import java.awt.event.MouseWheelListener; +import java.awt.geom.Path2D; import java.awt.geom.RoundRectangle2D; import java.awt.image.*; import java.lang.reflect.Field; @@ -937,13 +938,11 @@ public class JBScrollPane extends JScrollPane { g.fillRoundRect(x, y, width, height, arc, arc); } if (myDrawColor != null) { + Path2D path = new Path2D.Double(Path2D.WIND_EVEN_ODD); + path.append(new RoundRectangle2D.Double(x, y, width, height, arc, arc), false); + path.append(new RoundRectangle2D.Double(x + 1, y + 1, width - 2, height - 2, arc - 2, arc - 2), false); g.setColor(myDrawColor); - if (UIUtil.isRetina(g)) { - g.drawRoundRect(x, y, width, height, arc, arc); - } - else { - g.drawRoundRect(x, y, width - 1, height - 1, arc, arc); - } + g.fill(path); } g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, old); } diff --git a/platform/platform-impl/src/com/intellij/diagnostic/DefaultIdeaErrorLogger.java b/platform/platform-impl/src/com/intellij/diagnostic/DefaultIdeaErrorLogger.java index 9a72786c4f3a..ba429ca53cbc 100644 --- a/platform/platform-impl/src/com/intellij/diagnostic/DefaultIdeaErrorLogger.java +++ b/platform/platform-impl/src/com/intellij/diagnostic/DefaultIdeaErrorLogger.java @@ -88,10 +88,7 @@ public class DefaultIdeaErrorLogger implements ErrorLogger { } else if (!ourOomOccurred) { MessagePool messagePool = MessagePool.getInstance(); - LogMessage message = messagePool.addIdeFatalMessage(event); - if (message != null && ApplicationManager.getApplication() != null) { - ErrorNotifier.notifyUi(message, messagePool); - } + messagePool.addIdeFatalMessage(event); } } catch (Throwable e) { diff --git a/platform/platform-impl/src/com/intellij/diagnostic/ErrorNotifier.java b/platform/platform-impl/src/com/intellij/diagnostic/ErrorNotifier.java deleted file mode 100644 index acc25247f9dd..000000000000 --- a/platform/platform-impl/src/com/intellij/diagnostic/ErrorNotifier.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright 2000-2012 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 com.intellij.diagnostic; - -import com.intellij.notification.Notification; -import com.intellij.notification.NotificationGroup; -import com.intellij.notification.NotificationListener; -import com.intellij.notification.NotificationType; -import com.intellij.openapi.util.text.StringUtil; -import com.intellij.openapi.wm.IdeFrame; -import com.intellij.openapi.wm.StatusBar; -import com.intellij.openapi.wm.StatusBarWidget; -import org.jetbrains.annotations.NotNull; - -import javax.swing.*; -import javax.swing.event.HyperlinkEvent; -import java.awt.*; - -/** - * @author peter - */ -public class ErrorNotifier { - private static final NotificationGroup NOTIFICATION_GROUP = NotificationGroup.logOnlyGroup("IDE Fatal Errors"); - - public static void notifyUi(final LogMessage message, final MessagePool pool) { - //noinspection ThrowableResultOfMethodCallIgnored - if (message.getThrowable() instanceof MessagePool.TooManyErrorsException) { - NOTIFICATION_GROUP.createNotification(message.getMessage(), NotificationType.ERROR).notify(null); - return; - } - - String title = "" + getTitle(message) + ""; - String notificationText = getNotificationText(message); - NotificationListener listener = new NotificationListener() { - @Override - public void hyperlinkUpdate(@NotNull Notification notification, @NotNull HyperlinkEvent event) { - if (event.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { - openFatals(event, message); - } - } - }; - Notification notification = new Notification(NOTIFICATION_GROUP.getDisplayId(), title, notificationText, NotificationType.ERROR, listener) { - @Override - public void expire() { - super.expire(); - if (!message.isRead()) { - message.setRead(true); - } - pool.notifyListenersRead(); - } - }; - notification.notify(null); - message.setNotification(notification); - } - - private static void openFatals(HyperlinkEvent event, LogMessage message) { - Object source = event.getSource(); - if (source instanceof Component) { - Window window = SwingUtilities.getWindowAncestor((Component)source); - if (window instanceof IdeFrame) { - final StatusBar statusBar = ((IdeFrame)window).getStatusBar(); - StatusBarWidget widget = statusBar == null ? null : statusBar.getWidget(IdeMessagePanel.FATAL_ERROR); - if (widget instanceof IdeMessagePanel) { - ((IdeMessagePanel)widget).openFatals(message); - } - } - } - } - - private static String getNotificationText(LogMessage message) { - String text = message.getMessage(); - if (message instanceof LogMessageEx) { - String result = ((LogMessageEx)message).getNotificationText(); - if (result != null) { - text = StringUtil.stripHtml(result, false); - } - } - return text; - } - - private static String getTitle(LogMessage message) { - if (message instanceof LogMessageEx) { - return ((LogMessageEx)message).getTitle(); - } - @SuppressWarnings("ThrowableResultOfMethodCallIgnored") Throwable throwable = message.getThrowable(); - return throwable == null ? "IDE Fatal Error" : throwable.getClass().getSimpleName(); - } - -} diff --git a/platform/platform-impl/src/com/intellij/diagnostic/ReportMessages.java b/platform/platform-impl/src/com/intellij/diagnostic/ReportMessages.java index 93098278e507..a7c0df3f03f6 100644 --- a/platform/platform-impl/src/com/intellij/diagnostic/ReportMessages.java +++ b/platform/platform-impl/src/com/intellij/diagnostic/ReportMessages.java @@ -27,5 +27,5 @@ import com.intellij.notification.NotificationGroup; public class ReportMessages { public static final String ERROR_REPORT = DiagnosticBundle.message("error.report.title"); - public static final NotificationGroup GROUP = new NotificationGroup(ERROR_REPORT, NotificationDisplayType.BALLOON, true); + public static final NotificationGroup GROUP = new NotificationGroup(ERROR_REPORT, NotificationDisplayType.BALLOON, false); } diff --git a/platform/platform-impl/src/com/intellij/ide/MacOSApplicationProvider.java b/platform/platform-impl/src/com/intellij/ide/MacOSApplicationProvider.java index 44b8711158d0..2f6962a46e84 100644 --- a/platform/platform-impl/src/com/intellij/ide/MacOSApplicationProvider.java +++ b/platform/platform-impl/src/com/intellij/ide/MacOSApplicationProvider.java @@ -122,32 +122,34 @@ public class MacOSApplicationProvider implements ApplicationComponent { private static class Worker { public static void initMacApplication() { Application application = Application.getApplication(); - application.setAboutHandler(event -> AboutAction.perform(getProject())); + application.setAboutHandler(event -> AboutAction.perform(getProject(false))); application.setPreferencesHandler(event -> { - Project project = getNotNullProject(); - submit(() -> ShowSettingsAction.perform(project)); + Project project = getProject(true); + submit("Preferences", () -> ShowSettingsAction.perform(project)); }); application.setQuitHandler((event, response) -> { - submit(ExitAction::perform); + submit("Quit", ExitAction::perform); response.cancelQuit(); }); application.setOpenFileHandler(event -> { - Project project = getProject(); + Project project = getProject(false); List list = event.getFiles(); - LOG.debug("MacMenu: files found ", list.size()); if (list.isEmpty()) return; - File file = list.get(0); - submit(() -> { - LOG.debug("MacMenu: try to open file"); - if (ProjectUtil.openOrImport(file.getAbsolutePath(), project, true) != null) { - LOG.debug("MacMenu: load project for ", file); - IdeaApplication.getInstance().setPerformProjectLoad(false); - return; + submit("OpenFile", () -> { + for (File file : list) { + if (ProjectUtil.openOrImport(file.getAbsolutePath(), project, true) != null) { + LOG.debug("MacMenu: load project from ", file); + IdeaApplication.getInstance().setPerformProjectLoad(false); + return; + } } - LOG.debug("MacMenu: project = ", project); - if (project != null && file.exists()) { - LOG.debug("MacMenu: open file ", file); - OpenFileAction.openFile(file.getAbsolutePath(), project); + if (project != null) { + for (File file : list) { + if (file.exists()) { + LOG.debug("MacMenu: open file ", file); + OpenFileAction.openFile(file.getAbsolutePath(), project); + } + } } }); }); @@ -179,33 +181,49 @@ public class MacOSApplicationProvider implements ApplicationComponent { Foundation.invoke(pool, Foundation.createSelector("release")); } - @SuppressWarnings("deprecation") - private static Project getProject() { - return CommonDataKeys.PROJECT.getData(DataManager.getInstance().getDataContext()); + private static Project getProject(boolean useDefault) { + @SuppressWarnings("deprecation") + Project project = CommonDataKeys.PROJECT.getData(DataManager.getInstance().getDataContext()); + if (project == null) { + LOG.debug("MacMenu: no project in data context"); + Project[] projects = ProjectManager.getInstance().getOpenProjects(); + project = projects.length > 0 ? projects[0] : null; + if (project == null && useDefault) { + LOG.debug("MacMenu: use default project instead"); + project = ProjectManager.getInstance().getDefaultProject(); + } + } + LOG.debug("MacMenu: project = ", project); + return project; } - @NotNull - private static Project getNotNullProject() { - Project project = getProject(); - return project != null ? project : ProjectManager.getInstance().getDefaultProject(); - } - - private static void submit(@NotNull Runnable task) { + private static void submit(@NotNull String name, @NotNull Runnable task) { LOG.debug("MacMenu: on EDT = ", SwingUtilities.isEventDispatchThread(), "; ENABLED = ", ENABLED.get()); - if (!ENABLED.get()) return; - - Component component = IdeFocusManager.getGlobalInstance().getFocusOwner(); - if (component == null || IdeKeyEventDispatcher.isModalContext(component)) return; - - ENABLED.set(false); - TransactionGuard.submitTransaction(ApplicationManager.getApplication(), () -> { - try { - task.run(); + if (!ENABLED.get()) { + LOG.debug("MacMenu: disabled"); + } + else { + Component component = IdeFocusManager.getGlobalInstance().getFocusOwner(); + if (component == null) { + LOG.debug("MacMenu: no focused component"); } - finally { - ENABLED.set(true); + else if (IdeKeyEventDispatcher.isModalContext(component)) { + LOG.debug("MacMenu: component in modal context"); } - }); + else { + ENABLED.set(false); + TransactionGuard.submitTransaction(ApplicationManager.getApplication(), () -> { + try { + LOG.debug("MacMenu: init ", name); + task.run(); + } + finally { + LOG.debug("MacMenu: done ", name); + ENABLED.set(true); + } + }); + } + } } } } diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/intellij/WinIntelliJButtonPainter.java b/platform/platform-impl/src/com/intellij/ide/ui/laf/intellij/WinIntelliJButtonPainter.java index e0646ea512ee..a85274185218 100644 --- a/platform/platform-impl/src/com/intellij/ide/ui/laf/intellij/WinIntelliJButtonPainter.java +++ b/platform/platform-impl/src/com/intellij/ide/ui/laf/intellij/WinIntelliJButtonPainter.java @@ -19,6 +19,7 @@ import com.intellij.ide.ui.laf.darcula.ui.DarculaButtonUI; import com.intellij.openapi.actionSystem.ActionToolbar; import com.intellij.ui.Gray; import com.intellij.util.ui.JBUI; +import com.intellij.util.ui.UIUtil; import javax.swing.*; import javax.swing.border.Border; @@ -52,9 +53,10 @@ public class WinIntelliJButtonPainter implements Border, UIResource { g.translate(x,y); if (hasFocus) { - g.setStroke(new BasicStroke(1, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 1, new float[]{1}, 1)); + //g.setStroke(new BasicStroke(1, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 1, new float[]{1}, 1)); g.setColor(Gray.x0F); - g.drawRect(2*dd, 2*dd, width - 4*dd - 1, height - 4*dd - 1); + UIUtil.drawDottedRectangle(g, 2*dd, 2*dd, width - 2*dd - 1, height - 2*dd - 1); + //g.drawRect(2*dd, 2*dd, width - 4*dd - 1, height - 4*dd - 1); } g.translate(-x,-y); } diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StoreUtil.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StoreUtil.java index 4fd37486ca51..623b1bb889ce 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StoreUtil.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StoreUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 JetBrains s.r.o. + * 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. @@ -40,11 +40,11 @@ import org.jetbrains.annotations.Nullable; public final class StoreUtil { private static final Logger LOG = Logger.getInstance(StoreUtil.class); - private StoreUtil() { - } + private StoreUtil() { } public static void save(@NotNull IComponentStore stateStore, @Nullable Project project) { - ShutDownTracker.getInstance().registerStopperThread(Thread.currentThread()); + Thread currentThread = Thread.currentThread(); + ShutDownTracker.getInstance().registerStopperThread(currentThread); try { stateStore.save(new SmartList>()); } @@ -77,7 +77,7 @@ public final class StoreUtil { } } finally { - ShutDownTracker.getInstance().unregisterStopperThread(Thread.currentThread()); + ShutDownTracker.getInstance().unregisterStopperThread(currentThread); } } diff --git a/platform/platform-impl/src/com/intellij/openapi/roots/ProjectRootUtil.java b/platform/platform-impl/src/com/intellij/openapi/roots/ProjectRootUtil.java new file mode 100644 index 000000000000..15017802c552 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/openapi/roots/ProjectRootUtil.java @@ -0,0 +1,42 @@ +/* + * 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 com.intellij.openapi.roots; + +import com.intellij.openapi.project.Project; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.search.FilenameIndex; +import com.intellij.psi.search.GlobalSearchScope; +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; + +public class ProjectRootUtil { + @NotNull + public static VirtualFile findSymlinkedFileInContent(@NotNull Project project, @NotNull VirtualFile forFile) { + GlobalSearchScope scope = GlobalSearchScope.allScope(project); + + if (scope.contains(forFile)) return forFile; + + Collection projectFiles = + FilenameIndex.getVirtualFilesByName(project, forFile.getName(), true, scope); + + for (VirtualFile eachContentFile : projectFiles) { + if (forFile.equals(eachContentFile.getCanonicalFile())) return eachContentFile; + } + + return forFile; + } +} diff --git a/platform/platform-impl/src/com/intellij/ui/EditorTextField.java b/platform/platform-impl/src/com/intellij/ui/EditorTextField.java index 8ca103ce0a7b..8c4c459ba1fa 100644 --- a/platform/platform-impl/src/com/intellij/ui/EditorTextField.java +++ b/platform/platform-impl/src/com/intellij/ui/EditorTextField.java @@ -16,6 +16,7 @@ package com.intellij.ui; import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer; +import com.intellij.ide.ui.UISettings; import com.intellij.ide.ui.laf.darcula.ui.DarculaEditorTextFieldBorder; import com.intellij.openapi.actionSystem.CommonDataKeys; import com.intellij.openapi.actionSystem.DataProvider; @@ -562,7 +563,10 @@ public class EditorTextField extends NonOpaquePanel implements DocumentListener, if (myInheritSwingFont) { editor.getColorsScheme().setEditorFontName(getFont().getFontName()); editor.getColorsScheme().setEditorFontSize(getFont().getSize()); + return; } + UISettings settings = UISettings.getInstance(); + if (settings.PRESENTATION_MODE) editor.setFontSize(settings.PRESENTATION_MODE_FONT_SIZE); } protected boolean shouldHaveBorder() { diff --git a/platform/platform-resources-en/src/messages/CodeInsightBundle.properties b/platform/platform-resources-en/src/messages/CodeInsightBundle.properties index 4f4c29ff3ce9..18d187c8a2bf 100644 --- a/platform/platform-resources-en/src/messages/CodeInsightBundle.properties +++ b/platform/platform-resources-en/src/messages/CodeInsightBundle.properties @@ -168,8 +168,6 @@ intention.join.declaration.family=Join Declaration and Assignment intention.split.declaration.text=Split into separate declarations intention.join.declaration.text=Join declaration and assignment intention.split.declaration.assignment.text=Split into declaration and assignment -intention.add.override.annotation=Add '@Override' annotation -intention.add.override.annotation.family=Add Override Annotation intention.split.if.family=Split If intention.split.if.text=Split into 2 if's intention.split.filter.text=Split into filter's chain diff --git a/platform/platform-resources-en/src/messages/LangBundle.properties b/platform/platform-resources-en/src/messages/LangBundle.properties index 9d140ebe16d8..37b5dea09dc5 100644 --- a/platform/platform-resources-en/src/messages/LangBundle.properties +++ b/platform/platform-resources-en/src/messages/LangBundle.properties @@ -54,7 +54,7 @@ incorrect.name=Incorrect name jrt.node.short=[JRT] jrt.node.long=[Java Run-time Modules] -jrt.not.available.message=You need to run IDEA on Java 8 to be able to use modular JDK. Sorry. +jrt.not.available.message=You need to run IDEA on Java 8+ to be able to use modular JDK. Sorry. jrt.not.available.title=Unsupported JDK ''{0}'' detected compound.run.configuration.cycle={0} ''{1}'' causes dependency cycle and cannot be added diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/roots/ProjectRootUtilSymlinkedFilesTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/roots/ProjectRootUtilSymlinkedFilesTest.java new file mode 100644 index 000000000000..54cf9bdfea8e --- /dev/null +++ b/platform/platform-tests/testSrc/com/intellij/openapi/roots/ProjectRootUtilSymlinkedFilesTest.java @@ -0,0 +1,147 @@ +/* + * 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 com.intellij.openapi.roots; + +import com.intellij.openapi.application.Result; +import com.intellij.openapi.application.WriteAction; +import com.intellij.openapi.util.SystemInfo; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.io.IoTestUtil; +import com.intellij.openapi.vfs.VfsUtil; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.testFramework.PlatformTestCase; +import com.intellij.testFramework.PsiTestUtil; +import org.jetbrains.annotations.NotNull; + +import java.io.File; +import java.io.IOException; + +public class ProjectRootUtilSymlinkedFilesTest extends PlatformTestCase { + private File myCanonicalDir; + private File myCanonicalFile; + private VirtualFile myCanonicalVFile; + private File myContentDir; + private VirtualFile myContentVDir; + private File myLibraryDir; + private VirtualFile myLibraryVDir; + + @Override + protected boolean shouldRunTest() { + return super.shouldRunTest() && SystemInfo.areSymLinksSupported; + } + + @Override + public void setUp() throws Exception { + super.setUp(); + myCanonicalDir = FileUtil.createTempDirectory("canonical", null); + myCanonicalFile = new File(myCanonicalDir, "foo.txt"); + assertTrue(myCanonicalFile.createNewFile()); + + myCanonicalVFile = VfsUtil.findFileByIoFile(myCanonicalFile, true); + assertNotNull(myCanonicalVFile); + + myContentDir = FileUtil.createTempDirectory("content", null); + myContentVDir = VfsUtil.findFileByIoFile(myContentDir, true); + assertNotNull(myContentVDir); + PsiTestUtil.addContentRoot(getModule(), myContentVDir); + + myLibraryDir = FileUtil.createTempDirectory("library", null); + myLibraryVDir = VfsUtil.findFileByIoFile(myLibraryDir, true); + assertNotNull(myLibraryVDir); + PsiTestUtil.addLibrary(getModule(), myLibraryVDir.getPath()); + } + + public void testNoFilesInContent() throws Exception { + assertEquals(myCanonicalVFile, ProjectRootUtil.findSymlinkedFileInContent(getProject(), myCanonicalVFile)); + } + + public void testLinkedDirInContent() throws Exception { + doTestLinkedDirInProjectRoots(true); + } + + public void testLinkedDirInLibrary() throws Exception { + doTestLinkedDirInProjectRoots(false); + } + + public void doTestLinkedDirInProjectRoots(boolean content) throws Exception { + String linkedPath = (content ? myContentDir : myLibraryDir).getPath() + "/linked"; + IoTestUtil.createSymLink(myCanonicalDir.getPath(), linkedPath, true); + + VirtualFile linkedVFile = VfsUtil.findFileByIoFile(new File(linkedPath, myCanonicalFile.getName()), true); + + assertEquals(linkedVFile, ProjectRootUtil.findSymlinkedFileInContent(getProject(), myCanonicalVFile)); + } + + // not supported + public void _testLinkedFileInSources() throws Exception { + String linkedPath = myContentDir.getPath() + "/linked.txt"; + IoTestUtil.createSymLink(myCanonicalVFile.getPath(), linkedPath, true); + + VirtualFile linkedVFile = VfsUtil.findFileByIoFile(new File(linkedPath), true); + assertEquals(linkedVFile, ProjectRootUtil.findSymlinkedFileInContent(getProject(), myCanonicalVFile)); + } + + public void testFileWithTheSameNotButNotLinked() throws Exception { + new WriteAction() { + @Override + protected void run(@NotNull Result result) throws Throwable { + myContentVDir.createChildData(this, myCanonicalFile.getName()); + } + }.execute(); + + assertEquals(myCanonicalVFile, ProjectRootUtil.findSymlinkedFileInContent(getProject(), myCanonicalVFile)); + } + + public void testWhenSeveralLinkedAlwaysReturnTheSameFile() throws Exception { + String linkedPath1 = myContentDir.getPath() + "/linked1"; + IoTestUtil.createSymLink(myCanonicalDir.getPath(), linkedPath1, true); + VirtualFile linkedVFile1 = VfsUtil.findFileByIoFile(new File(linkedPath1, myCanonicalFile.getName()), true); + assertNotNull(linkedVFile1); + + String linkedPath2 = myContentDir.getPath() + "/linked2"; + IoTestUtil.createSymLink(myCanonicalDir.getPath(), linkedPath2, true); + VirtualFile linkedVFile2 = VfsUtil.findFileByIoFile(new File(linkedPath2, myCanonicalFile.getName()), true); + assertNotNull(linkedVFile2); + + VirtualFile found = ProjectRootUtil.findSymlinkedFileInContent(getProject(), myCanonicalVFile); + assertTrue(found.equals(linkedVFile1) || found.equals(linkedVFile2)); + for(int i = 0; i < 10; i++) { + assertEquals("try: " + i, found, ProjectRootUtil.findSymlinkedFileInContent(getProject(), myCanonicalVFile)); + } + } + + public void testLinkedAndCanonicalUnderContent() throws Exception { + doTestLinkedAndCanonicalUnderRoot(true); + } + + public void testLinkedAndCanonicalUnderLibraryRoot() throws Exception { + doTestLinkedAndCanonicalUnderRoot(false); + } + + private void doTestLinkedAndCanonicalUnderRoot(boolean content) throws InterruptedException, IOException { + String linkedPath = myContentDir.getPath() + "/linked"; + IoTestUtil.createSymLink(myCanonicalDir.getPath(), linkedPath, true); + + VirtualFile linkedVFile = VfsUtil.findFileByIoFile(new File(linkedPath, myCanonicalFile.getName()), true); + assertEquals(linkedVFile, ProjectRootUtil.findSymlinkedFileInContent(getProject(), myCanonicalVFile)); + if(content) { + PsiTestUtil.addContentRoot(getModule(), myCanonicalVFile.getParent()); + } else { + PsiTestUtil.addLibrary(getModule(), myCanonicalVFile.getParent().getPath()); + } + assertEquals(myCanonicalVFile, ProjectRootUtil.findSymlinkedFileInContent(getProject(), myCanonicalVFile)); + } +} diff --git a/platform/script-debugger/debugger-ui/src/ScopeVariablesGroup.kt b/platform/script-debugger/debugger-ui/src/ScopeVariablesGroup.kt index 21c77ef18fbf..9b0f5ed3161d 100644 --- a/platform/script-debugger/debugger-ui/src/ScopeVariablesGroup.kt +++ b/platform/script-debugger/debugger-ui/src/ScopeVariablesGroup.kt @@ -44,9 +44,9 @@ class ScopeVariablesGroup(val scope: Scope, parentContext: VariableContext, call promise .done(node) { context.memberFilter - .thenAsyncAccept(node) { memberFilter -> - if (memberFilter.hasNameMappings()) { - memberFilter.sourceNameToRaw(RECEIVER_NAME)?.let { + .thenAsyncAccept(node) { + if (it.hasNameMappings()) { + it.sourceNameToRaw(RECEIVER_NAME)?.let { return@thenAsyncAccept callFrame.evaluateContext.evaluate(it) .done(node) { VariableImpl(RECEIVER_NAME, it.value, null) diff --git a/platform/script-debugger/debugger-ui/src/VariablesGroup.kt b/platform/script-debugger/debugger-ui/src/VariablesGroup.kt index 357e4b8d7a69..8726e4b9f130 100644 --- a/platform/script-debugger/debugger-ui/src/VariablesGroup.kt +++ b/platform/script-debugger/debugger-ui/src/VariablesGroup.kt @@ -29,6 +29,6 @@ internal class VariablesGroup(private val start: Int, private val end: Int, priv } internal fun createArrayRangeGroup(variables: List, start: Int, end: Int, variableContext: VariableContext): VariablesGroup { - val name = "[" + variables[start].name + " \u2026 " + variables[end - 1].name + "]" + val name = "[${variables[start].name} \u2026 ${variables[end - 1].name}]" return VariablesGroup(start, end, variables, variableContext, name) } diff --git a/platform/script-debugger/debugger-ui/src/com/jetbrains/javascript/debugger/NameMapper.kt b/platform/script-debugger/debugger-ui/src/com/jetbrains/javascript/debugger/NameMapper.kt index 556969cf0425..27db8d89a31b 100644 --- a/platform/script-debugger/debugger-ui/src/com/jetbrains/javascript/debugger/NameMapper.kt +++ b/platform/script-debugger/debugger-ui/src/com/jetbrains/javascript/debugger/NameMapper.kt @@ -40,7 +40,8 @@ open class NameMapper(private val document: Document, private val transpiledDocu // PsiNamedElement, JSVariable for example // returns generated name - open fun map(identifierOrNamedElement: PsiElement): String? { + @JvmOverloads + open fun map(identifierOrNamedElement: PsiElement, forceMapBySourceCode: Boolean = false): String? { val offset = identifierOrNamedElement.textOffset val line = document.getLineNumber(offset) @@ -67,7 +68,7 @@ open class NameMapper(private val document: Document, private val transpiledDocu } var sourceName = sourceEntry.name - if (sourceName == null || (Registry.`is`("js.debugger.name.mappings.by.source.code", false) || Registry.`is`("js.debugger.map.this.by.source.code", false))) { + if (sourceName == null || forceMapBySourceCode || Registry.`is`("js.debugger.name.mappings.by.source.code", false)) { sourceName = (identifierOrNamedElement as? PsiNamedElement)?.name ?: identifierOrNamedElement.text ?: sourceName ?: return null } diff --git a/platform/testFramework/src/com/intellij/testFramework/fixtures/CodeInsightTestFixture.java b/platform/testFramework/src/com/intellij/testFramework/fixtures/CodeInsightTestFixture.java index a421c078f493..c3afcddbc9d8 100644 --- a/platform/testFramework/src/com/intellij/testFramework/fixtures/CodeInsightTestFixture.java +++ b/platform/testFramework/src/com/intellij/testFramework/fixtures/CodeInsightTestFixture.java @@ -537,6 +537,8 @@ public interface CodeInsightTestFixture extends IdeaProjectTestFixture { void testFoldingWithCollapseStatus(@NotNull String fileName); + void testRainbow(@NotNull String fileName, @NotNull String text, boolean isRainbowOn, boolean withColor); + void assertPreferredCompletionItems(int selected, @NotNull String... expected); /** diff --git a/platform/testFramework/src/com/intellij/testFramework/fixtures/impl/CodeInsightTestFixtureImpl.java b/platform/testFramework/src/com/intellij/testFramework/fixtures/impl/CodeInsightTestFixtureImpl.java index 2bd3161e69e7..ec06caef30ec 100644 --- a/platform/testFramework/src/com/intellij/testFramework/fixtures/impl/CodeInsightTestFixtureImpl.java +++ b/platform/testFramework/src/com/intellij/testFramework/fixtures/impl/CodeInsightTestFixtureImpl.java @@ -16,6 +16,7 @@ package com.intellij.testFramework.fixtures.impl; import com.intellij.analysis.AnalysisScope; +import com.intellij.codeHighlighting.RainbowHighlighter; import com.intellij.codeInsight.TargetElementUtil; import com.intellij.codeInsight.completion.CodeCompletionHandlerBase; import com.intellij.codeInsight.completion.CompletionProgressIndicator; @@ -70,6 +71,7 @@ import com.intellij.openapi.editor.ex.util.EditorUtil; import com.intellij.openapi.editor.impl.DocumentImpl; import com.intellij.openapi.editor.impl.DocumentMarkupModel; import com.intellij.openapi.editor.markup.RangeHighlighter; +import com.intellij.openapi.editor.markup.TextAttributes; import com.intellij.openapi.extensions.ExtensionPoint; import com.intellij.openapi.extensions.ExtensionPointName; import com.intellij.openapi.extensions.ExtensionsArea; @@ -83,6 +85,8 @@ import com.intellij.openapi.project.DumbService; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.*; import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.registry.Registry; +import com.intellij.openapi.util.registry.RegistryValue; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.*; import com.intellij.profile.Profile; @@ -133,6 +137,7 @@ public class CodeInsightTestFixtureImpl extends BaseFixture implements CodeInsig private static final String START_FOLD = ""; private static final String END_FOLD = ""; + private static final String RAINBOW = "rainbow"; private final IdeaProjectTestFixture myProjectFixture; private final TempDirTestFixture myTempDirFixture; @@ -966,7 +971,7 @@ public class CodeInsightTestFixtureImpl extends BaseFixture implements CodeInsig Assert.assertNotNull("Cannot find handler for: " + targetElement, handler); final PsiElement[] psiElements = ArrayUtil.mergeArrays(handler.getPrimaryElements(), handler.getSecondaryElements()); final FindUsagesOptions options = handler.getFindUsagesOptions(null); - if (scope != null) options.searchScope = scope; + if (scope != null) options.searchScope = scope; for (PsiElement psiElement : psiElements) { handler.processElementUsages(psiElement, processor, options); } @@ -1441,7 +1446,7 @@ public class CodeInsightTestFixtureImpl extends BaseFixture implements CodeInsig private PsiFile configureByFileInner(@NotNull VirtualFile copy) { return configureInner(copy, SelectionAndCaretMarkupLoader.fromFile(copy)); } - + private PsiFile configureInner(@NotNull final VirtualFile copy, @NotNull final SelectionAndCaretMarkupLoader loader) { assertInitialized(); @@ -1750,17 +1755,35 @@ public class CodeInsightTestFixtureImpl extends BaseFixture implements CodeInsig } @NotNull - public String getHighlightingDescription(@NotNull List highlighting, @NotNull String tagName) { + public String getHighlightingDescription(@NotNull List highlighting, @NotNull String tagName, boolean withColor) { final List borders = new LinkedList(); for (HighlightInfo region : highlighting) { - borders.add(new Border(Border.LEFT, region.getStartOffset(), "", false)); + TextAttributes attributes = region.getTextAttributes(null, null); + borders.add(new Border(Border.LEFT, region.getStartOffset(), + attributes == null ? "null" + : attributes.getForegroundColor() == null + ? "null" + : Integer.toHexString(attributes.getForegroundColor().getRGB()), + false)); borders.add(new Border(Border.RIGHT, region.getEndOffset(), "", false)); } Collections.sort(borders); StringBuilder result = new StringBuilder(myEditor.getDocument().getText()); for (Border border : borders) { - result.insert(border.getOffset(), (border.isSide() == Border.LEFT ? "<": ""); + StringBuilder info = new StringBuilder(); + info.append('<'); + if (border.isSide() == Border.LEFT) { + info.append(tagName); + if (withColor) { + info.append(" color=\'").append(border.myText).append('\''); + } + } + else { + info.append('/').append(tagName); + } + info.append('>'); + result.insert(border.getOffset(), info); } return result.toString(); @@ -1809,6 +1832,23 @@ public class CodeInsightTestFixtureImpl extends BaseFixture implements CodeInsig testFoldingRegions(verificationFileName, null, false); } + @Override + public void testRainbow(@NotNull String fileName, @NotNull String text, boolean isRainbowOn, boolean withColor) { + RegistryValue registryValue = Registry.get("editor.rainbow.identifiers"); + final boolean rainbowColors = registryValue.asBoolean(); + try { + registryValue.setValue(isRainbowOn); + configureByText(fileName, text.replaceAll("<" + RAINBOW + "(\\scolor=\'[^\']*\')?>", "").replace("", "")); + + Assert.assertEquals(text, getHighlightingDescription(ContainerUtil.filter(doHighlighting(), + info -> info.type == RainbowHighlighter.RAINBOW_ELEMENT), RAINBOW, + withColor)); + } + finally { + registryValue.setValue(rainbowColors); + } + } + @Override public void assertPreferredCompletionItems(final int selected, @NotNull final String... expected) { final LookupImpl lookup = getLookup(); diff --git a/platform/util/resources/misc/registry.properties b/platform/util/resources/misc/registry.properties index 0b8253ec1108..d9a15866356d 100644 --- a/platform/util/resources/misc/registry.properties +++ b/platform/util/resources/misc/registry.properties @@ -605,7 +605,7 @@ js.debugger.v8.use.any.breakpoint=true js.debugger.chrome.use.any.breakpoint=true nodejs.debugger.use.jb.support=true js.debugger.name.mappings.by.source.code=false -js.debugger.map.this.by.source.code=false +js.debugger.map.this.by.source.code=true js.debugger.v8.log= js.debugger.wip.log= js.debugger.fix.jspm.source.maps=false diff --git a/platform/util/src/com/intellij/util/text/DateFormatUtil.java b/platform/util/src/com/intellij/util/text/DateFormatUtil.java index 0abbf6bc4679..096de12833a2 100644 --- a/platform/util/src/com/intellij/util/text/DateFormatUtil.java +++ b/platform/util/src/com/intellij/util/text/DateFormatUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 JetBrains s.r.o. + * 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. @@ -75,10 +75,6 @@ public class DateFormatUtil { private DateFormatUtil() { } - /** @deprecated use {@link #getIso8601Format()} (to be removed in IDEA 16) */ - @SuppressWarnings("unused") - public static final SyncDateFormat ISO8601_DATE_FORMAT = ISO8601_FORMAT; - public static long getDifferenceInDays(@NotNull Date startDate, @NotNull Date endDate) { return (endDate.getTime() - startDate.getTime() + DAY_FACTOR - 1000) / DAY_FACTOR; } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/LineStatusTracker.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/LineStatusTracker.java index 94045c141fa9..dc6947ae5461 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/LineStatusTracker.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/LineStatusTracker.java @@ -398,9 +398,19 @@ public class LineStatusTracker { } private class MyApplicationListener extends ApplicationAdapter { + private int myWriteActionDepth = 0; + + @Override + public void writeActionStarted(@NotNull Object action) { + myWriteActionDepth++; + } + @Override public void writeActionFinished(@NotNull Object action) { - updateRanges(); + myWriteActionDepth = Math.max(myWriteActionDepth - 1, 0); + if (myWriteActionDepth == 0) { + updateRanges(); + } } } diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/XDebugSessionImpl.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/XDebugSessionImpl.java index de0ae90c6aa6..ca1e01fd3a54 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/XDebugSessionImpl.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/XDebugSessionImpl.java @@ -775,7 +775,7 @@ public class XDebugSessionImpl implements XDebugSession { updateExecutionPosition(); if (myShowTabOnSuspend.compareAndSet(true, false)) { - UIUtil.invokeLaterIfNeeded(() -> { + AppUIUtil.invokeLaterIfProjectAlive(myProject, () -> { initSessionTab(null); showSessionTab(); }); diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/abstraction/DeclareCollectionAsInterfaceInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/abstraction/DeclareCollectionAsInterfaceInspection.java index ea878cda2318..9fa145243c78 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/abstraction/DeclareCollectionAsInterfaceInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/abstraction/DeclareCollectionAsInterfaceInspection.java @@ -1,5 +1,5 @@ /* - * Copyright 2003-2014 Dave Griffith, Bas Leijdekkers + * Copyright 2003-2016 Dave Griffith, Bas Leijdekkers * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,19 +17,16 @@ package com.siyeh.ig.abstraction; import com.intellij.codeInspection.ProblemDescriptor; import com.intellij.codeInspection.ui.MultipleCheckboxOptionsPanel; -import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; import com.intellij.psi.*; import com.intellij.psi.codeStyle.JavaCodeStyleManager; -import com.intellij.psi.search.GlobalSearchScope; -import com.intellij.psi.search.PsiSearchHelper; -import com.intellij.psi.search.SearchScope; import com.intellij.util.IncorrectOperationException; import com.siyeh.InspectionGadgetsBundle; import com.siyeh.ig.BaseInspection; import com.siyeh.ig.BaseInspectionVisitor; import com.siyeh.ig.InspectionGadgetsFix; import com.siyeh.ig.psiutils.CollectionUtils; +import com.siyeh.ig.psiutils.DeclarationSearchUtils; import com.siyeh.ig.psiutils.LibraryUtil; import com.siyeh.ig.psiutils.WeakestTypeFinder; import org.jetbrains.annotations.NotNull; @@ -146,7 +143,7 @@ public class DeclareCollectionAsInterfaceInspection extends BaseInspection { @Override public void visitVariable(@NotNull PsiVariable variable) { - if (isOnTheFly() && !isCheapEnoughToSearch(variable)) { + if (isOnTheFly() && DeclarationSearchUtils.isTooExpensiveToSearch(variable, false)) { return; } if (ignoreLocalVariables && variable instanceof PsiLocalVariable) { @@ -189,7 +186,7 @@ public class DeclareCollectionAsInterfaceInspection extends BaseInspection { method.hasModifierProperty(PsiModifier.PRIVATE)) { return; } - if (isOnTheFly() && !isCheapEnoughToSearch(method)) { + if (isOnTheFly() && DeclarationSearchUtils.isTooExpensiveToSearch(method, false)) { return; } final PsiType type = method.getReturnType(); @@ -234,20 +231,5 @@ public class DeclareCollectionAsInterfaceInspection extends BaseInspection { registerError(nameElement, qualifiedName); } } - - private boolean isCheapEnoughToSearch(PsiNamedElement element) { - final String name = element.getName(); - if (name == null) { - return false; - } - final ProgressManager progressManager = - ProgressManager.getInstance(); - final PsiSearchHelper searchHelper = PsiSearchHelper.SERVICE.getInstance(element.getProject()); - final SearchScope useScope = element.getUseScope(); - if (useScope instanceof GlobalSearchScope) { - return searchHelper.isCheapEnoughToSearch(name, (GlobalSearchScope)useScope, null, progressManager.getProgressIndicator()) != PsiSearchHelper.SearchCostResult.TOO_MANY_OCCURRENCES; - } - return true; - } } } \ No newline at end of file diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/abstraction/MethodOnlyUsedFromInnerClassInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/abstraction/MethodOnlyUsedFromInnerClassInspection.java index 1286843309cf..e43e5bb69aa6 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/abstraction/MethodOnlyUsedFromInnerClassInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/abstraction/MethodOnlyUsedFromInnerClassInspection.java @@ -1,5 +1,5 @@ /* - * Copyright 2005-2012 Bas Leijdekkers + * Copyright 2005-2016 Bas Leijdekkers * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,18 +16,15 @@ package com.siyeh.ig.abstraction; import com.intellij.codeInspection.ui.MultipleCheckboxOptionsPanel; -import com.intellij.openapi.progress.ProgressIndicator; -import com.intellij.openapi.progress.ProgressManager; import com.intellij.psi.*; -import com.intellij.psi.search.PsiSearchHelper; import com.intellij.psi.search.searches.ReferencesSearch; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.Processor; -import com.intellij.util.Query; import com.siyeh.InspectionGadgetsBundle; import com.siyeh.ig.BaseInspection; import com.siyeh.ig.BaseInspectionVisitor; import com.siyeh.ig.psiutils.ClassUtils; +import com.siyeh.ig.psiutils.DeclarationSearchUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -182,17 +179,10 @@ public class MethodOnlyUsedFromInnerClassInspection extends BaseInspection { } public boolean isOnlyAccessedFromInnerClass() { - final PsiSearchHelper searchHelper = PsiSearchHelper.SERVICE.getInstance(method.getProject()); - final ProgressManager progressManager = ProgressManager.getInstance(); - final ProgressIndicator progressIndicator = progressManager.getProgressIndicator(); - final PsiSearchHelper.SearchCostResult searchCost = - searchHelper.isCheapEnoughToSearch(method.getName(), method.getResolveScope(), null, progressIndicator); - if (searchCost == PsiSearchHelper.SearchCostResult.TOO_MANY_OCCURRENCES || - searchCost == PsiSearchHelper.SearchCostResult.ZERO_OCCURRENCES) { - return onlyAccessedFromInnerClass; + if (DeclarationSearchUtils.isTooExpensiveToSearch(method, true)) { + return false; } - final Query query = ReferencesSearch.search(method); - query.forEach(this); + ReferencesSearch.search(method).forEach(this); return onlyAccessedFromInnerClass; } diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/abstraction/StaticMethodOnlyUsedInOneClassInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/abstraction/StaticMethodOnlyUsedInOneClassInspectionBase.java index 9d10a449ec4c..52dcdc9ad1f1 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/abstraction/StaticMethodOnlyUsedInOneClassInspectionBase.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/abstraction/StaticMethodOnlyUsedInOneClassInspectionBase.java @@ -18,9 +18,7 @@ package com.siyeh.ig.abstraction; import com.intellij.codeInspection.ui.MultipleCheckboxOptionsPanel; import com.intellij.openapi.progress.ProgressManager; import com.intellij.psi.*; -import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.search.PsiElementProcessor; -import com.intellij.psi.search.PsiSearchHelper; import com.intellij.psi.search.searches.MethodReferencesSearch; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtil; @@ -30,6 +28,7 @@ import com.siyeh.InspectionGadgetsBundle; import com.siyeh.ig.BaseInspection; import com.siyeh.ig.BaseInspectionVisitor; import com.siyeh.ig.psiutils.ClassUtils; +import com.siyeh.ig.psiutils.DeclarationSearchUtils; import com.siyeh.ig.psiutils.TestUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -110,17 +109,12 @@ public class StaticMethodOnlyUsedInOneClassInspectionBase extends BaseInspection */ @Nullable public PsiClass getUsageClass(final PsiMethod method) { - final ProgressManager progressManager = ProgressManager.getInstance(); - final PsiSearchHelper searchHelper = PsiSearchHelper.SERVICE.getInstance(method.getProject()); - final String name = method.getName(); - final GlobalSearchScope scope = GlobalSearchScope.projectScope(method.getProject()); - if (searchHelper.isCheapEnoughToSearch(name, scope, null, progressManager.getProgressIndicator()) - == PsiSearchHelper.SearchCostResult.TOO_MANY_OCCURRENCES) { + if (DeclarationSearchUtils.isTooExpensiveToSearch(method, true)) { return null; } - progressManager.runProcess(() -> { + ProgressManager.getInstance().runProcess(() -> { final Query query = MethodReferencesSearch.search(method); - if (!query.forEach(UsageProcessor.this)) { + if (!query.forEach(this)) { foundClass.set(null); } }, null); diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/assignment/AssignmentToForLoopParameterInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/assignment/AssignmentToForLoopParameterInspection.java index fa4ffac5679e..e7b45b30a671 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/assignment/AssignmentToForLoopParameterInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/assignment/AssignmentToForLoopParameterInspection.java @@ -1,5 +1,5 @@ /* - * Copyright 2003-2011 Dave Griffith, Bas Leijdekkers + * Copyright 2003-2016 Dave Griffith, Bas Leijdekkers * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,7 +24,6 @@ import com.siyeh.ig.BaseInspection; import com.siyeh.ig.BaseInspectionVisitor; import com.siyeh.ig.InspectionGadgetsFix; import com.siyeh.ig.fixes.ExtractParameterAsLocalVariableFix; -import com.siyeh.ig.psiutils.WellFormednessUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -79,10 +78,9 @@ public class AssignmentToForLoopParameterInspection extends BaseInspectionVisitor { @Override - public void visitAssignmentExpression( - @NotNull PsiAssignmentExpression expression) { + public void visitAssignmentExpression(@NotNull PsiAssignmentExpression expression) { super.visitAssignmentExpression(expression); - if (!WellFormednessUtils.isWellFormed(expression)) { + if (expression.getRExpression() == null) { return; } final PsiExpression lhs = expression.getLExpression(); diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/CovariantEqualsInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/CovariantEqualsInspection.java index d80b0b341616..9310fee55930 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/CovariantEqualsInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/CovariantEqualsInspection.java @@ -1,5 +1,5 @@ /* - * Copyright 2003-2007 Dave Griffith, Bas Leijdekkers + * Copyright 2003-2016 Dave Griffith, Bas Leijdekkers * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,10 +16,12 @@ package com.siyeh.ig.bugs; import com.intellij.psi.*; +import com.intellij.psi.search.searches.SuperMethodsSearch; import com.siyeh.HardcodedMethodConstants; import com.siyeh.InspectionGadgetsBundle; import com.siyeh.ig.BaseInspection; import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.psiutils.MethodUtils; import com.siyeh.ig.psiutils.TypeUtils; import org.jetbrains.annotations.NotNull; @@ -71,25 +73,14 @@ public class CovariantEqualsInspection extends BaseInspection { } final PsiMethod[] methods = aClass.getMethods(); for (PsiMethod method1 : methods) { - if (isNonVariantEquals(method1)) { + if (MethodUtils.isEquals(method1)) { return; } } + if (SuperMethodsSearch.search(method, null, true, false).findFirst() != null) { + return; + } registerMethodError(method); } - - private static boolean isNonVariantEquals(PsiMethod method) { - final String name = method.getName(); - if (!HardcodedMethodConstants.EQUALS.equals(name)) { - return false; - } - final PsiParameterList paramList = method.getParameterList(); - final PsiParameter[] parameters = paramList.getParameters(); - if (parameters.length != 1) { - return false; - } - final PsiType argType = parameters[0].getType(); - return TypeUtils.isJavaLangObject(argType); - } } } \ No newline at end of file diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/OptionalGetWithoutIsPresentInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/OptionalGetWithoutIsPresentInspection.java index cff4e0bca6f0..dc9c9b15194b 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/OptionalGetWithoutIsPresentInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/OptionalGetWithoutIsPresentInspection.java @@ -257,8 +257,8 @@ public class OptionalGetWithoutIsPresentInspection extends BaseInspection { } else if (expression instanceof PsiReferenceExpression) { final PsiReferenceExpression referenceExpression = (PsiReferenceExpression)expression; - final PsiExpression definition = VariableSearchUtils.findDefinition(referenceExpression, null); - final PsiExpression optionalDefinition = VariableSearchUtils.findDefinition(this.referenceExpression, null); + final PsiExpression definition = DeclarationSearchUtils.findDefinition(referenceExpression, null); + final PsiExpression optionalDefinition = DeclarationSearchUtils.findDefinition(this.referenceExpression, null); if (definition == null || optionalDefinition == null || optionalDefinition.getTextOffset() > definition.getTextOffset()) { return false; } diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/controlflow/ConstantIfStatementInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/controlflow/ConstantIfStatementInspection.java index 645fc426027d..de7cd1053bfb 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/controlflow/ConstantIfStatementInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/controlflow/ConstantIfStatementInspection.java @@ -1,5 +1,5 @@ /* - * Copyright 2003-2010 Dave Griffith, Bas Leijdekkers + * Copyright 2003-2016 Dave Griffith, Bas Leijdekkers * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,7 +27,7 @@ import com.siyeh.ig.BaseInspectionVisitor; import com.siyeh.ig.InspectionGadgetsFix; import com.siyeh.ig.PsiReplacementUtil; import com.siyeh.ig.psiutils.BoolUtils; -import com.siyeh.ig.psiutils.VariableSearchUtils; +import com.siyeh.ig.psiutils.DeclarationSearchUtils; import org.jetbrains.annotations.NotNull; public class ConstantIfStatementInspection extends BaseInspection { @@ -116,7 +116,7 @@ public class ConstantIfStatementInspection extends BaseInspection { final PsiCodeBlock block = ((PsiBlockStatement)branch).getCodeBlock(); final boolean hasConflicts = - VariableSearchUtils.containsConflictingDeclarations( + DeclarationSearchUtils.containsConflictingDeclarations( block, parentBlock); if (hasConflicts) { final String elseText = branch.getText(); diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/errorhandling/UnnecessaryInitCauseInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/errorhandling/UnnecessaryInitCauseInspectionBase.java index 2a915015e6f1..31611d90a4da 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/errorhandling/UnnecessaryInitCauseInspectionBase.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/errorhandling/UnnecessaryInitCauseInspectionBase.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 JetBrains s.r.o. + * 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. @@ -26,7 +26,7 @@ import com.siyeh.ig.BaseInspectionVisitor; import com.siyeh.ig.psiutils.ExpressionUtils; import com.siyeh.ig.psiutils.ParenthesesUtils; import com.siyeh.ig.psiutils.TypeUtils; -import com.siyeh.ig.psiutils.VariableSearchUtils; +import com.siyeh.ig.psiutils.DeclarationSearchUtils; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -162,7 +162,7 @@ public class UnnecessaryInitCauseInspectionBase extends BaseInspection { } else if (expression instanceof PsiReferenceExpression) { final PsiReferenceExpression referenceExpression = (PsiReferenceExpression)expression; - final PsiExpression definition = VariableSearchUtils.findDefinition(referenceExpression, null); + final PsiExpression definition = DeclarationSearchUtils.findDefinition(referenceExpression, null); if (!(definition instanceof PsiNewExpression)) { return null; } diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/fixes/DeleteCatchSectionFix.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/fixes/DeleteCatchSectionFix.java index e110d0d96f2b..c1c0c335a871 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/fixes/DeleteCatchSectionFix.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/fixes/DeleteCatchSectionFix.java @@ -18,10 +18,9 @@ package com.siyeh.ig.fixes; import com.intellij.codeInspection.ProblemDescriptor; import com.intellij.openapi.project.Project; import com.intellij.psi.*; -import com.intellij.util.IncorrectOperationException; import com.siyeh.InspectionGadgetsBundle; import com.siyeh.ig.InspectionGadgetsFix; -import com.siyeh.ig.psiutils.VariableSearchUtils; +import com.siyeh.ig.psiutils.DeclarationSearchUtils; import org.jetbrains.annotations.NotNull; /** @@ -53,7 +52,7 @@ public class DeleteCatchSectionFix extends InspectionGadgetsFix { } @Override - protected void doFix(Project project, ProblemDescriptor descriptor) throws IncorrectOperationException { + protected void doFix(Project project, ProblemDescriptor descriptor) { final PsiElement element = descriptor.getPsiElement(); final PsiElement parent = element.getParent(); if (!(parent instanceof PsiParameter)) { @@ -75,7 +74,7 @@ public class DeleteCatchSectionFix extends InspectionGadgetsFix { final boolean keepBlock; if (containingElement instanceof PsiCodeBlock) { final PsiCodeBlock parentBlock = (PsiCodeBlock)containingElement; - keepBlock = VariableSearchUtils.containsConflictingDeclarations(codeBlock, parentBlock); + keepBlock = DeclarationSearchUtils.containsConflictingDeclarations(codeBlock, parentBlock); } else { keepBlock = true; diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/j2me/MultiplyOrDivideByPowerOfTwoInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/j2me/MultiplyOrDivideByPowerOfTwoInspection.java index 217ece7078ba..d913e1c36837 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/j2me/MultiplyOrDivideByPowerOfTwoInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/j2me/MultiplyOrDivideByPowerOfTwoInspection.java @@ -1,5 +1,5 @@ /* - * Copyright 2003-2008 Dave Griffith, Bas Leijdekkers + * Copyright 2003-2016 Dave Griffith, Bas Leijdekkers * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,7 +28,6 @@ import com.siyeh.ig.InspectionGadgetsFix; import com.siyeh.ig.PsiReplacementUtil; import com.siyeh.ig.psiutils.ClassUtils; import com.siyeh.ig.psiutils.ParenthesesUtils; -import com.siyeh.ig.psiutils.WellFormednessUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -194,9 +193,6 @@ public class MultiplyOrDivideByPowerOfTwoInspection @Override public void visitAssignmentExpression(@NotNull PsiAssignmentExpression expression) { super.visitAssignmentExpression(expression); - if (!WellFormednessUtils.isWellFormed(expression)) { - return; - } final IElementType tokenType = expression.getOperationTokenType(); if (!tokenType.equals(JavaTokenType.ASTERISKEQ)) { if (!checkDivision || !tokenType.equals(JavaTokenType.DIVEQ)) { diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/junit/MisorderedAssertEqualsArgumentsInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/junit/MisorderedAssertEqualsArgumentsInspectionBase.java index fa4e9dcabf91..d9f3779709ee 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/junit/MisorderedAssertEqualsArgumentsInspectionBase.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/junit/MisorderedAssertEqualsArgumentsInspectionBase.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 JetBrains s.r.o. + * 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. @@ -210,7 +210,7 @@ public abstract class MisorderedAssertEqualsArgumentsInspectionBase extends Base } else if (target instanceof PsiLocalVariable) { final PsiVariable variable = (PsiLocalVariable)target; - final PsiExpression definition = VariableSearchUtils.findDefinition(referenceExpression, variable); + final PsiExpression definition = DeclarationSearchUtils.findDefinition(referenceExpression, variable); if (LibraryUtil.isOnlyLibraryCodeUsed(definition)) { return true; } diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/VariableSearchUtils.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/DeclarationSearchUtils.java similarity index 80% rename from plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/VariableSearchUtils.java rename to plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/DeclarationSearchUtils.java index 1c3bf112d86b..cd7fe1d0cf24 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/VariableSearchUtils.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/DeclarationSearchUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2003-2015 Dave Griffith, Bas Leijdekkers + * Copyright 2003-2016 Dave Griffith, Bas Leijdekkers * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,9 +15,13 @@ */ package com.siyeh.ig.psiutils; +import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; import com.intellij.psi.*; import com.intellij.psi.controlFlow.DefUseUtil; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.search.PsiSearchHelper; +import com.intellij.psi.search.SearchScope; import com.intellij.psi.util.PsiTreeUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -25,9 +29,9 @@ import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.List; -public class VariableSearchUtils { +public class DeclarationSearchUtils { - private VariableSearchUtils() {} + private DeclarationSearchUtils() {} public static boolean variableNameResolvesToTarget( @NotNull String variableName, @NotNull PsiVariable target, @@ -132,4 +136,23 @@ public class VariableSearchUtils { } return null; } + + public static boolean isTooExpensiveToSearch(PsiNamedElement element, boolean zeroResult) { + final String name = element.getName(); + if (name == null) { + return true; + } + final ProgressManager progressManager = ProgressManager.getInstance(); + final PsiSearchHelper searchHelper = PsiSearchHelper.SERVICE.getInstance(element.getProject()); + final SearchScope useScope = element.getUseScope(); + if (!(useScope instanceof GlobalSearchScope)) { + return zeroResult; + } + final PsiSearchHelper.SearchCostResult cost = + searchHelper.isCheapEnoughToSearch(name, (GlobalSearchScope)useScope, null, progressManager.getProgressIndicator()); + if (cost == PsiSearchHelper.SearchCostResult.ZERO_OCCURRENCES) { + return zeroResult; + } + return cost == PsiSearchHelper.SearchCostResult.TOO_MANY_OCCURRENCES; + } } \ No newline at end of file diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/style/UnnecessaryBlockStatementInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/style/UnnecessaryBlockStatementInspection.java index 59e5c5f8c5ed..85e8be01c4de 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/style/UnnecessaryBlockStatementInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/style/UnnecessaryBlockStatementInspection.java @@ -25,7 +25,7 @@ import com.siyeh.InspectionGadgetsBundle; import com.siyeh.ig.BaseInspection; import com.siyeh.ig.BaseInspectionVisitor; import com.siyeh.ig.InspectionGadgetsFix; -import com.siyeh.ig.psiutils.VariableSearchUtils; +import com.siyeh.ig.psiutils.DeclarationSearchUtils; import org.jetbrains.annotations.NotNull; import javax.swing.*; @@ -127,7 +127,7 @@ public class UnnecessaryBlockStatementInspection extends BaseInspection implemen } final PsiCodeBlock parentBlock = (PsiCodeBlock)parent; if (parentBlock.getStatements().length > 1 && - VariableSearchUtils.containsConflictingDeclarations(codeBlock, parentBlock)) { + DeclarationSearchUtils.containsConflictingDeclarations(codeBlock, parentBlock)) { return; } registerError(brace); diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/style/UnnecessaryThisInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/style/UnnecessaryThisInspection.java index 6c4051066ad5..8ca514b0cc09 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/style/UnnecessaryThisInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/style/UnnecessaryThisInspection.java @@ -1,5 +1,5 @@ /* - * Copyright 2003-2013 Dave Griffith, Bas Leijdekkers + * Copyright 2003-2016 Dave Griffith, Bas Leijdekkers * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,7 +30,7 @@ import com.siyeh.ig.BaseInspectionVisitor; import com.siyeh.ig.InspectionGadgetsFix; import com.siyeh.ig.PsiReplacementUtil; import com.siyeh.ig.psiutils.ClassUtils; -import com.siyeh.ig.psiutils.VariableSearchUtils; +import com.siyeh.ig.psiutils.DeclarationSearchUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -134,7 +134,7 @@ public class UnnecessaryThisInspection extends BaseInspection implements Cleanup return; } final PsiVariable variable = (PsiVariable)target; - if (!VariableSearchUtils.variableNameResolvesToTarget(referenceName, variable, expression)) { + if (!DeclarationSearchUtils.variableNameResolvesToTarget(referenceName, variable, expression)) { return; } if (variable instanceof PsiField && HighlightUtil.isIllegalForwardReferenceToField(expression, (PsiField)variable, true) != null) { @@ -181,7 +181,7 @@ public class UnnecessaryThisInspection extends BaseInspection implements Cleanup return; } final PsiVariable variable = (PsiVariable)target; - if (!VariableSearchUtils.variableNameResolvesToTarget(referenceName, variable, expression)) { + if (!DeclarationSearchUtils.variableNameResolvesToTarget(referenceName, variable, expression)) { return; } PsiClass parentClass = ClassUtils.getContainingClass(expression); diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/threading/SynchronizationOnLocalVariableOrMethodParameterInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/threading/SynchronizationOnLocalVariableOrMethodParameterInspection.java index c3334ebb0d5d..de0e58a7e218 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/threading/SynchronizationOnLocalVariableOrMethodParameterInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/threading/SynchronizationOnLocalVariableOrMethodParameterInspection.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2015 Bas Leijdekkers + * Copyright 2008-2016 Bas Leijdekkers * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,7 +21,7 @@ import com.siyeh.InspectionGadgetsBundle; import com.siyeh.ig.BaseInspection; import com.siyeh.ig.BaseInspectionVisitor; import com.siyeh.ig.psiutils.ParenthesesUtils; -import com.siyeh.ig.psiutils.VariableSearchUtils; +import com.siyeh.ig.psiutils.DeclarationSearchUtils; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; @@ -117,7 +117,7 @@ public class SynchronizationOnLocalVariableOrMethodParameterInspection extends B } private boolean isSynchronizedCollection(@NotNull PsiVariable variable, PsiReferenceExpression referenceExpression) { - final PsiExpression definition = VariableSearchUtils.findDefinition(referenceExpression, variable); + final PsiExpression definition = DeclarationSearchUtils.findDefinition(referenceExpression, variable); if (!(definition instanceof PsiMethodCallExpression)) { return false; } diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/covariantEquals/interf/Interf.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/covariantEquals/interf/Interf.java deleted file mode 100644 index bc7b5a1e40c0..000000000000 --- a/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/covariantEquals/interf/Interf.java +++ /dev/null @@ -1,3 +0,0 @@ -interface I { - boolean equals(I i); -} \ No newline at end of file diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/covariantEquals/interf/expected.xml b/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/covariantEquals/interf/expected.xml deleted file mode 100644 index 4704d91e891d..000000000000 --- a/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/covariantEquals/interf/expected.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/covariant_equals/CovariantEquals.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/covariant_equals/CovariantEquals.java new file mode 100644 index 000000000000..53d950c98cbd --- /dev/null +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/covariant_equals/CovariantEquals.java @@ -0,0 +1,31 @@ +interface I { + boolean equals(I i); +} +class A { + + public boolean equals(A a) { + return false; + } +} +class B extends A { + + @Override + public boolean equals(A a) { + return super.equals(a); + } +} +class C { + public boolean equals(C c) { + return false; + } + + public boolean equals(Object o) { + return true; + } +} +class D implements I { + @Override + public boolean equals(I i) { + return false; + } +} \ No newline at end of file diff --git a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/bugs/CovariantEqualsInspectionTest.java b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/bugs/CovariantEqualsInspectionTest.java index 204c66836339..f751f970a240 100644 --- a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/bugs/CovariantEqualsInspectionTest.java +++ b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/bugs/CovariantEqualsInspectionTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2012 JetBrains s.r.o. + * 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. @@ -15,15 +15,19 @@ */ package com.siyeh.ig.bugs; -import com.siyeh.ig.IGInspectionTestCase; +import com.intellij.codeInspection.InspectionProfileEntry; +import com.siyeh.ig.LightInspectionTestCase; +import org.jetbrains.annotations.Nullable; -public class CovariantEqualsInspectionTest extends IGInspectionTestCase { +public class CovariantEqualsInspectionTest extends LightInspectionTestCase { - public void testInterf() throws Exception { + public void testCovariantEquals() { doTest(); } - private void doTest() throws Exception { - doTest("com/siyeh/igtest/bugs/covariantEquals/" + getTestName(true), new CovariantEqualsInspection()); + @Nullable + @Override + protected InspectionProfileEntry getInspection() { + return new CovariantEqualsInspection(); } } diff --git a/plugins/IntentionPowerPak/src/com/siyeh/ipp/annotation/AnnotateOverriddenMethodsPredicate.java b/plugins/IntentionPowerPak/src/com/siyeh/ipp/annotation/AnnotateOverriddenMethodsPredicate.java index 64423fe68f51..7f55b54a0343 100644 --- a/plugins/IntentionPowerPak/src/com/siyeh/ipp/annotation/AnnotateOverriddenMethodsPredicate.java +++ b/plugins/IntentionPowerPak/src/com/siyeh/ipp/annotation/AnnotateOverriddenMethodsPredicate.java @@ -1,5 +1,5 @@ /* - * Copyright 20112013 Bas Leijdekkers + * Copyright 2011-2016 Bas Leijdekkers * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,15 +16,16 @@ package com.siyeh.ipp.annotation; import com.intellij.codeInsight.AnnotationUtil; -import com.intellij.openapi.project.Project; import com.intellij.psi.*; import com.intellij.psi.search.searches.OverridingMethodsSearch; +import com.siyeh.ig.psiutils.DeclarationSearchUtils; import com.siyeh.ipp.base.PsiElementPredicate; import java.util.Collection; class AnnotateOverriddenMethodsPredicate implements PsiElementPredicate { + @Override public boolean satisfiedBy(PsiElement element) { if (!(element instanceof PsiAnnotation)) { return false; @@ -64,7 +65,9 @@ class AnnotateOverriddenMethodsPredicate implements PsiElementPredicate { parameterIndex = -1; method = (PsiMethod)grandParent; } - final Project project = element.getProject(); + if (DeclarationSearchUtils.isTooExpensiveToSearch(method, true)) { + return false; + } final Collection overridingMethods = OverridingMethodsSearch.search(method).findAll(); if (overridingMethods.isEmpty()) { diff --git a/plugins/IntentionPowerPak/src/com/siyeh/ipp/exceptions/ConvertCatchToThrowsIntention.java b/plugins/IntentionPowerPak/src/com/siyeh/ipp/exceptions/ConvertCatchToThrowsIntention.java index 0673d6f64b50..0bc45c67a74a 100644 --- a/plugins/IntentionPowerPak/src/com/siyeh/ipp/exceptions/ConvertCatchToThrowsIntention.java +++ b/plugins/IntentionPowerPak/src/com/siyeh/ipp/exceptions/ConvertCatchToThrowsIntention.java @@ -20,7 +20,7 @@ import com.intellij.openapi.project.Project; import com.intellij.psi.*; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.IncorrectOperationException; -import com.siyeh.ig.psiutils.VariableSearchUtils; +import com.siyeh.ig.psiutils.DeclarationSearchUtils; import com.siyeh.ipp.base.Intention; import com.siyeh.ipp.base.PsiElementPredicate; import org.jetbrains.annotations.NotNull; @@ -71,7 +71,7 @@ public class ConvertCatchToThrowsIntention extends Intention { return; } final PsiCodeBlock parentCodeBlock = PsiTreeUtil.getParentOfType(tryStatement, PsiCodeBlock.class); - if (parentCodeBlock == null || !VariableSearchUtils.containsConflictingDeclarations(tryBlock, parentCodeBlock)) { + if (parentCodeBlock == null || !DeclarationSearchUtils.containsConflictingDeclarations(tryBlock, parentCodeBlock)) { final PsiElement first = tryBlock.getFirstBodyElement(); final PsiElement last = tryBlock.getLastBodyElement(); if (first != null && last != null) { diff --git a/plugins/git4idea/src/git4idea/annotate/GitFileAnnotation.java b/plugins/git4idea/src/git4idea/annotate/GitFileAnnotation.java index 156f171fb643..faa4425785b0 100644 --- a/plugins/git4idea/src/git4idea/annotate/GitFileAnnotation.java +++ b/plugins/git4idea/src/git4idea/annotate/GitFileAnnotation.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * 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. @@ -101,8 +101,8 @@ public class GitFileAnnotation extends FileAnnotation { final LineInfo info = myLines.get(lineNumber); VcsFileRevision fileRevision = info.myFileRevision; if (fileRevision != null) { - return GitBundle.message("annotation.tool.tip", info.getRevision().asString(), info.getAuthor(), info.getDate(), - fileRevision.getCommitMessage()); + return GitBundle.message("annotation.tool.tip", info.getRevision().asString(), info.getAuthor(), + DateFormatUtil.formatDateTime(info.getDate()), fileRevision.getCommitMessage()); } else { return ""; diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/stubs/elements/GrStubFileElementType.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/stubs/elements/GrStubFileElementType.java index 5d89f4192665..a773914d594f 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/stubs/elements/GrStubFileElementType.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/stubs/elements/GrStubFileElementType.java @@ -40,6 +40,7 @@ import java.io.IOException; * @author ilyas */ public class GrStubFileElementType extends IStubFileElementType { + public static final int STUB_VERSION = 29; public GrStubFileElementType(Language language) { super(language); @@ -84,7 +85,7 @@ public class GrStubFileElementType extends IStubFileElementType { @Override public int getStubVersion() { - return super.getStubVersion() + 29; + return super.getStubVersion() + STUB_VERSION; } @Override diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/stubs/hierarchy/GrStubIndexer.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/stubs/hierarchy/GrStubIndexer.java index c124aae6cc20..1fd114269fae 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/stubs/hierarchy/GrStubIndexer.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/stubs/hierarchy/GrStubIndexer.java @@ -16,6 +16,7 @@ package org.jetbrains.plugins.groovy.lang.psi.stubs.hierarchy; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.PsiFile; import com.intellij.psi.PsiNameHelper; import com.intellij.psi.impl.java.stubs.hierarchy.IndexTree; import com.intellij.psi.impl.java.stubs.hierarchy.IndexTree.*; @@ -25,22 +26,24 @@ import com.intellij.psi.stubs.StubTree; import com.intellij.psi.stubs.StubTreeBuilder; import com.intellij.psi.stubsHierarchy.StubHierarchyIndexer; import com.intellij.util.ArrayUtil; +import com.intellij.util.containers.ContainerUtil; import com.intellij.util.indexing.FileContent; +import com.intellij.util.indexing.FileContentImpl; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.groovy.GroovyFileType; +import org.jetbrains.plugins.groovy.extensions.GroovyScriptTypeDetector; import org.jetbrains.plugins.groovy.lang.parser.GroovyElementTypes; +import org.jetbrains.plugins.groovy.lang.psi.GroovyFile; import org.jetbrains.plugins.groovy.lang.psi.stubs.*; +import org.jetbrains.plugins.groovy.lang.psi.stubs.elements.GrStubFileElementType; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; +import java.util.*; public class GrStubIndexer extends StubHierarchyIndexer { @Override public int getVersion() { - return 0; + return GrStubFileElementType.STUB_VERSION + 1; } @Override @@ -51,6 +54,8 @@ public class GrStubIndexer extends StubHierarchyIndexer { @Nullable @Override public Unit indexFile(@NotNull FileContent content) { + if (!isNormalGroovyFile(((FileContentImpl)content).getPsiFileForPsiDependentIndex())) return null; + Stub stubTree = StubTreeBuilder.buildStubTree(content); if (!(stubTree instanceof GrFileStub)) return null; @@ -70,7 +75,7 @@ public class GrStubIndexer extends StubHierarchyIndexer { } if (el instanceof GrTypeDefinitionStub) { - ClassDecl classDecl = processClassDecl((GrTypeDefinitionStub)el, usedNames); + ClassDecl classDecl = processClassDecl((GrTypeDefinitionStub)el, usedNames, false); if (classDecl != null) { classList.add(classDecl); } @@ -87,10 +92,19 @@ public class GrStubIndexer extends StubHierarchyIndexer { return new Unit(pid, IndexTree.GROOVY, imports, classes); } + private static boolean isNormalGroovyFile(final PsiFile file) { + return file instanceof GroovyFile && !hasSpecialScriptType((GroovyFile)file); + } + + private static boolean hasSpecialScriptType(GroovyFile file) { + return file.isScript() && + ContainerUtil.exists(GroovyScriptTypeDetector.EP_NAME.getExtensions(), detector -> detector.isSpecificScriptFile(file)); + } + @Nullable private static Decl processMember(StubElement el, Set namesCache) { if (el instanceof GrTypeDefinitionStub) { - return processClassDecl((GrTypeDefinitionStub)el, namesCache); + return processClassDecl((GrTypeDefinitionStub)el, namesCache, true); } ArrayList innerList = new ArrayList(); for (StubElement childElement : el.getChildrenStubs()) { @@ -103,7 +117,7 @@ public class GrStubIndexer extends StubHierarchyIndexer { } @Nullable - private static ClassDecl processClassDecl(GrTypeDefinitionStub classStub, Set namesCache) { + private static ClassDecl processClassDecl(GrTypeDefinitionStub classStub, Set namesCache, boolean inner) { ArrayList superList = new ArrayList(); ArrayList innerList = new ArrayList(); if (classStub.isAnonymous()) { @@ -128,6 +142,9 @@ public class GrStubIndexer extends StubHierarchyIndexer { } } int flags = translateFlags(classStub); + if (inner && !superList.isEmpty()) { + flags |= IndexTree.SUPERS_UNRESOLVED; // 'extends' list resolves to classes from the current package first, and those can be in a language unknown to this hierarchy + } String[] supers = superList.isEmpty() ? ArrayUtil.EMPTY_STRING_ARRAY : ArrayUtil.toStringArray(superList); Decl[] inners = innerList.isEmpty() ? Decl.EMPTY_ARRAY : innerList.toArray(new Decl[innerList.size()]); return new ClassDecl(classStub.id, flags, classStub.getName(), supers, inners); diff --git a/plugins/properties/src/com/intellij/lang/properties/editor/ResourceBundleAsVirtualFile.java b/plugins/properties/src/com/intellij/lang/properties/editor/ResourceBundleAsVirtualFile.java index bfc20426a5d0..26f83ecd2f76 100644 --- a/plugins/properties/src/com/intellij/lang/properties/editor/ResourceBundleAsVirtualFile.java +++ b/plugins/properties/src/com/intellij/lang/properties/editor/ResourceBundleAsVirtualFile.java @@ -22,8 +22,8 @@ import com.intellij.lang.properties.psi.PropertiesFile; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileSystem; +import com.intellij.openapi.vfs.VirtualFileWithoutContent; import com.intellij.openapi.vfs.newvfs.RefreshQueue; -import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; @@ -36,7 +36,7 @@ import java.util.List; * @author Alexey */ @Presentation(icon = "AllIcons.Nodes.ResourceBundle") -public class ResourceBundleAsVirtualFile extends VirtualFile { +public class ResourceBundleAsVirtualFile extends VirtualFile implements VirtualFileWithoutContent { private final ResourceBundle myResourceBundle; public ResourceBundleAsVirtualFile(@NotNull final ResourceBundle resourceBundle) { diff --git a/plugins/testng/src/com/theoryinpractice/testng/model/TestNGConfigurationModel.java b/plugins/testng/src/com/theoryinpractice/testng/model/TestNGConfigurationModel.java index db6076395e01..6053686d2f13 100644 --- a/plugins/testng/src/com/theoryinpractice/testng/model/TestNGConfigurationModel.java +++ b/plugins/testng/src/com/theoryinpractice/testng/model/TestNGConfigurationModel.java @@ -112,11 +112,11 @@ public class TestNGConfigurationModel data.MAIN_CLASS_NAME = ""; data.METHOD_NAME = ""; data.SUITE_NAME = ""; - } else if (TestType.METHOD == type || TestType.CLASS == type) { + } else if (TestType.METHOD == type || TestType.CLASS == type || TestType.SOURCE == type) { String className = getText(TestType.CLASS); data.GROUP_NAME = ""; data.SUITE_NAME = ""; - if (TestType.METHOD == type) + if (TestType.METHOD == type || TestType.SOURCE == type) data.METHOD_NAME = getText(TestType.METHOD); PsiClass psiClass = !getProject().isDefault() && !StringUtil.isEmptyOrSpaces(className) ? JUnitUtil.findPsiClass(className, module, getProject()) : null; diff --git a/plugins/yaml/src/org/jetbrains/yaml/psi/impl/YAMLKeyValueImpl.java b/plugins/yaml/src/org/jetbrains/yaml/psi/impl/YAMLKeyValueImpl.java index 6c1e0737cc5b..020b0ae9c980 100644 --- a/plugins/yaml/src/org/jetbrains/yaml/psi/impl/YAMLKeyValueImpl.java +++ b/plugins/yaml/src/org/jetbrains/yaml/psi/impl/YAMLKeyValueImpl.java @@ -138,6 +138,12 @@ public class YAMLKeyValueImpl extends YAMLPsiElementImpl implements YAMLKeyValue } } + @NotNull + @Override + protected Icon getElementIcon(@IconFlags int flags) { + return PlatformIcons.PROPERTY_ICON; + } + @Override public ItemPresentation getPresentation() { final YAMLFile yamlFile = (YAMLFile)getContainingFile(); @@ -155,7 +161,7 @@ public class YAMLKeyValueImpl extends YAMLPsiElementImpl implements YAMLKeyValue } public Icon getIcon(boolean open) { - return PlatformIcons.PROPERTY_ICON; + return YAMLKeyValueImpl.this.getIcon(0); } }; } diff --git a/plugins/yaml/src/org/jetbrains/yaml/structureView/YAMLStructureViewElement.java b/plugins/yaml/src/org/jetbrains/yaml/structureView/YAMLStructureViewElement.java index ee690a525bbf..46678f2d2706 100644 --- a/plugins/yaml/src/org/jetbrains/yaml/structureView/YAMLStructureViewElement.java +++ b/plugins/yaml/src/org/jetbrains/yaml/structureView/YAMLStructureViewElement.java @@ -2,7 +2,6 @@ package org.jetbrains.yaml.structureView; import com.intellij.ide.structureView.StructureViewTreeElement; import com.intellij.navigation.ItemPresentation; -import com.intellij.psi.PsiElement; import com.intellij.util.PlatformIcons; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -82,8 +81,8 @@ public class YAMLStructureViewElement implements StructureViewTreeElement { } public Icon getIcon(boolean open) { - final PsiElement value = kv.getValue(); - return value instanceof YAMLScalar ? PlatformIcons.PROPERTY_ICON : PlatformIcons.XML_TAG_ICON; + final YAMLValue value = kv.getValue(); + return value instanceof YAMLScalar ? kv.getIcon(0) : PlatformIcons.XML_TAG_ICON; } }; } diff --git a/python/src/com/jetbrains/python/inspections/quickfix/PyMakeFunctionFromMethodQuickFix.java b/python/src/com/jetbrains/python/inspections/quickfix/PyMakeFunctionFromMethodQuickFix.java index 952f897683c6..c3cbe6535e4a 100644 --- a/python/src/com/jetbrains/python/inspections/quickfix/PyMakeFunctionFromMethodQuickFix.java +++ b/python/src/com/jetbrains/python/inspections/quickfix/PyMakeFunctionFromMethodQuickFix.java @@ -89,7 +89,6 @@ public class PyMakeFunctionFromMethodQuickFix implements LocalQuickFix { @NotNull final PsiFile usageFile, boolean addImport) { final PyExpression qualifier = element.getQualifier(); if (qualifier == null) return; - //TODO: invalid qualifier after PyUtil.removeQualifier(element), to be fixed by east825 if (qualifier.getText().equals(PyNames.CANONICAL_SELF)) { PyUtil.removeQualifier(element); return; diff --git a/python/src/com/jetbrains/python/testing/VFSTestFrameworkListener.java b/python/src/com/jetbrains/python/testing/VFSTestFrameworkListener.java index 98fa61101ef8..3f655edde3da 100644 --- a/python/src/com/jetbrains/python/testing/VFSTestFrameworkListener.java +++ b/python/src/com/jetbrains/python/testing/VFSTestFrameworkListener.java @@ -42,7 +42,9 @@ import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; /** @@ -102,9 +104,16 @@ public class VFSTestFrameworkListener { } public void updateAllTestFrameworks(@NotNull Sdk sdk) { - checkFrameworkInstalledAndUpdateSettings(sdk, PyNames.PY_TEST); - checkFrameworkInstalledAndUpdateSettings(sdk, PyNames.NOSE_TEST); - checkFrameworkInstalledAndUpdateSettings(sdk, PyNames.AT_TEST); + final Map whichInstalled = checkTestFrameworksInstalled(sdk, PyNames.PY_TEST, PyNames.NOSE_TEST, PyNames.AT_TEST); + ApplicationManager.getApplication().invokeLater(() -> { + for (Map.Entry entry : whichInstalled.entrySet()) { + final Boolean installed = entry.getValue(); + if (installed != null) { + //noinspection ConstantConditions + setTestFrameworkInstalled(installed, sdk.getHomePath(), entry.getKey()); + } + } + }); } private void scheduleTestFrameworkCheck(@NotNull Sdk sdk, @NotNull String testPackageName) { @@ -129,19 +138,27 @@ public class VFSTestFrameworkListener { */ @Contract("null, _ -> null") private Boolean checkTestFrameworkInstalled(@Nullable Sdk sdk, @NotNull String testPackageName) { + return checkTestFrameworksInstalled(sdk, testPackageName).get(testPackageName); + } + + @NotNull + private Map checkTestFrameworksInstalled(@Nullable Sdk sdk, @NotNull String... testPackageNames) { + final Map result = new HashMap<>(); if (sdk == null || StringUtil.isEmptyOrSpaces(sdk.getHomePath())) { LOG.info("Searching test runner in empty sdk"); - return null; + return result; } final PyPackageManager manager = PyPackageManager.getInstance(sdk); final boolean refreshed = PyPackageUtil.updatePackagesSynchronouslyWithGuard(manager, myIsUpdating); if (refreshed) { final List packages = manager.getPackages(); if (packages != null) { - return PyPackageUtil.findPackage(packages, testPackageName) != null; + for (String name : testPackageNames) { + result.put(name, PyPackageUtil.findPackage(packages, name) != null); + } } } - return null; + return result; } private void setPyTestInstalled(boolean installed, @NotNull String sdkHome) {