Merge remote-tracking branch 'origin/master'
authorDmitry Trofimov <dmitry.trofimov@jetbrains.com>
Tue, 18 Oct 2016 19:13:22 +0000 (21:13 +0200)
committerDmitry Trofimov <dmitry.trofimov@jetbrains.com>
Tue, 18 Oct 2016 19:13:22 +0000 (21:13 +0200)
94 files changed:
bin/linux/restart.py
build/asm/repackage.xml
build/groovy/org/jetbrains/intellij/build/BaseIdeaProperties.groovy
java/compiler/impl/src/com/intellij/compiler/backwardRefs/CompilerReferenceReader.java
java/compiler/impl/src/com/intellij/compiler/backwardRefs/CompilerReferenceServiceImpl.java
java/compiler/impl/src/com/intellij/compiler/backwardRefs/JavaLightUsageAdapter.java
java/compiler/impl/src/com/intellij/compiler/server/BuildManager.java
java/compiler/impl/src/com/intellij/task/impl/InternalProjectTaskRunner.java
java/debugger/impl/src/com/intellij/debugger/engine/requests/RequestManagerImpl.java
java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/MethodBreakpoint.java
java/java-impl/src/com/intellij/codeInsight/hints/JavaParameterHintManager.java
java/java-indexing-impl/src/com/intellij/psi/impl/search/JavaDirectInheritorsSearcher.java
java/java-tests/testSrc/com/intellij/codeInsight/daemon/inlays/JavaParameterNameHintsTest.kt
jps/jps-builders/src/org/jetbrains/jps/cmdline/ClasspathBootstrap.java
jps/model-serialization/src/com/intellij/openapi/application/PathMacroFilter.java
jps/model-serialization/src/com/intellij/openapi/components/PathMacroMap.java
platform/configuration-store-impl/src/FileBasedStorage.kt
platform/configuration-store-impl/src/ModuleStateStorageManager.kt
platform/configuration-store-impl/src/XmlElementStorage.kt
platform/diff-impl/src/com/intellij/diff/DiffContentFactoryImpl.java
platform/diff-impl/src/com/intellij/diff/contents/DiffPsiFileSupport.java
platform/diff-impl/src/com/intellij/diff/contents/DocumentContentImpl.java
platform/diff-impl/src/com/intellij/diff/contents/FileAwareDocumentContent.java [deleted file]
platform/diff-impl/src/com/intellij/diff/contents/FileDocumentContentImpl.java
platform/lang-api/src/com/intellij/execution/ui/ConsoleView.java
platform/lang-api/src/com/intellij/psi/codeStyle/CodeStyleScheme.java
platform/lang-impl/src/com/intellij/codeInsight/hints/PopupActions.kt
platform/lang-impl/src/com/intellij/codeInsight/hints/settings/ParameterNameHintsConfigurable.form
platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupImpl.java
platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupPreview.java [new file with mode: 0644]
platform/lang-impl/src/com/intellij/execution/impl/ConsoleBuffer.java
platform/lang-impl/src/com/intellij/execution/impl/ConsoleState.java
platform/lang-impl/src/com/intellij/execution/impl/ConsoleViewImpl.java
platform/lang-impl/src/com/intellij/execution/impl/ConsoleViewRunningState.java
platform/lang-impl/src/com/intellij/find/impl/FindInProjectSettingsBase.java
platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/CodeStyleSchemeImpl.java
platform/platform-impl/src/com/intellij/ui/popup/PopupDispatcher.java
platform/platform-impl/src/com/intellij/util/Restarter.java
platform/platform-resources/src/META-INF/LangExtensions.xml
platform/projectModel-impl/src/com/intellij/openapi/components/CompositePathMacroFilter.java
platform/projectModel-impl/src/com/intellij/openapi/components/impl/stores/FileStorageCoreUtil.java
platform/testRunner/src/com/intellij/execution/testframework/ui/BaseTestsOutputConsoleView.java
platform/testRunner/src/com/intellij/execution/testframework/ui/TestsConsoleBuilderImpl.java
platform/testRunner/src/com/intellij/execution/testframework/ui/TestsConsoleViewImpl.java
platform/util/resources/misc/registry.properties
platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/migrate/MigrateToNewDiffUtil.java
plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/InspectionGadgetsBundle.properties
plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/junit/AssertEqualsBetweenInconvertibleTypesInspection.java
plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/junit/AssertEqualsCalledOnArrayInspection.java
plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/junit/AssertEqualsHint.java [new file with mode: 0644]
plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/junit/AssertEqualsMayBeAssertSameInspection.java
plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/junit/AssertsWithoutMessagesInspection.java
plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/junit/ConstantJUnitAssertArgumentInspection.java
plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/junit/JUnitAbstractTestClassNamingConventionInspectionBase.java
plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/junit/JUnitCommonClassNames.java [new file with mode: 0644]
plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/junit/JUnitTestClassNamingConventionInspectionBase.java
plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/junit/MalformedSetUpTearDownInspection.java
plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/junit/MisorderedAssertEqualsArgumentsInspectionBase.java
plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/junit/ReplaceAssertEqualsFix.java [new file with mode: 0644]
plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/junit/SimplifiableJUnitAssertionInspection.java
plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/junit/StaticSuiteInspection.java
plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/junit/SuperTearDownInFinallyInspection.java
plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/junit/TestCaseWithNoTestMethodsInspection.java
plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/junit/TestMethodWithoutAssertionInspectionBase.java
plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/junit/UseOfObsoleteAssertInspection.java
plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/TestUtils.java
plugins/InspectionGadgets/test/com/siyeh/igfixes/junit/assertEqualsOnArray/assertEqualsForJunit5.after.java [new file with mode: 0644]
plugins/InspectionGadgets/test/com/siyeh/igfixes/junit/assertEqualsOnArray/assertEqualsForJunit5.java [new file with mode: 0644]
plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/junit/AssertEqualsCalledOnArrayInspectionTest.java [new file with mode: 0644]
plugins/devkit/resources/META-INF/plugin.xml
plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/auxiliary/annotation/GrAnnotationArgumentListImpl.java
plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/auxiliary/annotation/GrAnnotationNameValuePairImpl.java
plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/stubs/elements/GrAnnotationArgumentListElementType.java
plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/stubs/elements/GrStubFileElementType.java
plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/stubs/stubs.kt
plugins/testng/src/com/theoryinpractice/testng/inspection/AssertEqualsBetweenInconvertibleTypesTestNGInspection.java
python/helpers/pydev/_pydev_bundle/pydev_monkey.py
python/helpers/pydev/_pydevd_bundle/pydevd_comm.py
python/helpers/pydev/pydevd.py
python/pydevSrc/com/jetbrains/python/debugger/pydev/AbstractCommand.java
python/pydevSrc/com/jetbrains/python/debugger/pydev/ClientModeMultiProcessDebugger.java [new file with mode: 0644]
python/pydevSrc/com/jetbrains/python/debugger/pydev/ProcessCreatedCommand.java [new file with mode: 0644]
python/pydevSrc/com/jetbrains/python/debugger/pydev/RemoteDebugger.java
python/pydevSrc/com/jetbrains/python/debugger/pydev/transport/BaseDebuggerReader.java [new file with mode: 0644]
python/pydevSrc/com/jetbrains/python/debugger/pydev/transport/BaseDebuggerTransport.java [new file with mode: 0644]
python/pydevSrc/com/jetbrains/python/debugger/pydev/transport/ClientModeDebuggerTransport.java [new file with mode: 0644]
python/pydevSrc/com/jetbrains/python/debugger/pydev/transport/DebuggerTransport.java [new file with mode: 0644]
python/pydevSrc/com/jetbrains/python/debugger/pydev/transport/ServerModeDebuggerTransport.java [new file with mode: 0644]
python/src/META-INF/python-core-common.xml
python/src/com/jetbrains/python/debugger/PyDebugProcess.java
python/src/com/jetbrains/python/debugger/PyDebugRunner.java
python/src/com/jetbrains/python/debugger/PyDebugSessionFactory.java [new file with mode: 0644]
resources/src/META-INF/IdeaPlugin.xml
xml/xml-psi-impl/src/com/intellij/xml/impl/schema/XmlNSDescriptorImpl.java

index 93bf229db65e632dd4dfb4b703816d8d1099b9f6..5633f1289aff027f71a116085eaf2d499b11fac0 100755 (executable)
@@ -3,20 +3,23 @@
 # Waits for the parent process to terminate, then executes specified commands.
 
 import os
+import signal
 import sys
 import time
 
-if len(sys.argv) < 2:
-    raise Exception('At least one argument expected')
+if len(sys.argv) < 3:
+    raise Exception('usage: restart.py <pid> <path> [optional command]')
 
-pid = os.getppid()
+signal.signal(signal.SIGHUP, signal.SIG_IGN)
+
+pid = int(sys.argv[1])
 while os.getppid() == pid:
     time.sleep(0.5)
 
-if len(sys.argv) > 2:
-    os.spawnv(os.P_WAIT, sys.argv[2], sys.argv[2:])
+if len(sys.argv) > 3:
+    os.spawnv(os.P_WAIT, sys.argv[3], sys.argv[3:])
 
-to_launch = sys.argv[1]
+to_launch = sys.argv[2]
 if sys.platform == 'darwin':
     os.execv('/usr/bin/open', ['/usr/bin/open', to_launch])
 else:
index 845c29ecf986f83f7150b678442e471f4cea7bf4..367e420373d61ac252497fae477c6580addceeb5 100644 (file)
     <fail message="Please edit the file ${ant.file} and fill in the 'asm.src' property" unless="asm.src.present"/>
   </target>
 
-  <target name="clean" depends="check">
+  <target name="clean">
     <delete dir="${src.dir}" quiet="true"/>
     <delete dir="${out.dir}" quiet="true"/>
     <delete file="${src.zip}" quiet="true"/>
     <delete file="${out.jar}" quiet="true"/>
   </target>
 
-  <target name="unpack" depends="clean">
+  <target name="unpack" depends="check,clean">
     <unzip src="${asm.src}" dest="${src.dir}"/>
   </target>
 
@@ -61,4 +61,9 @@
     <zip destfile="${src.zip}" basedir="${src.dir}" duplicate="fail"/>
     <jar destfile="${out.jar}" basedir="${out.dir}" duplicate="fail"/>
   </target>
+
+  <target name="update" depends="package">
+    <copy file="${out.jar}" todir="${basedir}/../../lib"/>
+    <copy file="${src.zip}" todir="${basedir}/../../lib/src"/>
+  </target>
 </project>
\ No newline at end of file
index 4820fd9a18625763bef8ab7912ddc747607ac16b..2829be5dc81397c2ae3717ca96711ec2408d9890 100644 (file)
@@ -93,7 +93,7 @@ abstract class BaseIdeaProperties extends ProductProperties {
     productLayout.additionalPlatformJars.put("forms_rt.jar", "forms-compiler")
     productLayout.additionalPlatformJars.putAll("resources.jar", ["resources", "resources-en"])
     productLayout.additionalPlatformJars.
-      putAll("javac2.jar", ["javac2", "forms-compiler", "forms_rt", "instrumentation-util", "instrumentation-util-8"])
+      putAll("javac2.jar", ["javac2", "forms-compiler", "forms_rt", "instrumentation-util", "instrumentation-util-8", "javac-ref-scanner-8"])
     productLayout.additionalPlatformJars.putAll("annotations-java8.jar", ["annotations-common", "annotations-java8"])
 
     productLayout.platformLayoutCustomizer = { PlatformLayout layout ->
index 2c21dde2bf7819447763eec38ad224e79ab9b76c..82707ee2b2e76b74a56f68c574a07d0968683d5e 100644 (file)
@@ -17,6 +17,7 @@ package com.intellij.compiler.backwardRefs;
 
 import com.intellij.compiler.server.BuildManager;
 import com.intellij.openapi.application.ReadAction;
+import com.intellij.openapi.diagnostic.Logger;
 import com.intellij.openapi.fileTypes.FileType;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.util.Couple;
@@ -45,6 +46,8 @@ import java.util.*;
 import static java.util.stream.Collectors.*;
 
 class CompilerReferenceReader {
+  private final static Logger LOG = Logger.getInstance(CompilerReferenceReader.class);
+
   private final CompilerBackwardReferenceIndex myIndex;
 
   private CompilerReferenceReader(File buildDir) throws IOException {
@@ -85,6 +88,7 @@ class CompilerReferenceReader {
 
     GlobalSearchScope effectiveSearchScope = GlobalSearchScope.notScope(dirtyScope).intersectWith(searchScope);
     LanguageLightRefAdapter adapter = CompilerReferenceServiceImpl.findAdapterForFileType(fileType);
+    LOG.assertTrue(adapter != null, "adapter is null for file type: " + fileType);
     Class<? extends LightRef> requiredLightRefClass = searchType.getRequiredClass(adapter);
     Map<VirtualFile, List<LightRef>> candidatesPerFile = candidates
       .stream()
index 7cf8a437061dfde9d7336c58ee9ed60ecef73bd7..34719a9bbf32bd6906428bde067f0c35b9b4537e 100644 (file)
@@ -25,9 +25,6 @@ import com.intellij.openapi.application.ReadAction;
 import com.intellij.openapi.compiler.*;
 import com.intellij.openapi.fileTypes.FileType;
 import com.intellij.openapi.module.Module;
-import com.intellij.openapi.progress.ProgressIndicator;
-import com.intellij.openapi.progress.ProgressManager;
-import com.intellij.openapi.progress.Task;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.roots.ProjectFileIndex;
 import com.intellij.openapi.roots.ProjectRootManager;
@@ -133,24 +130,20 @@ public class CompilerReferenceServiceImpl extends CompilerReferenceService imple
 
       myDirtyModulesHolder.installVFSListener();
 
-      ProgressManager.getInstance().run(new Task.Backgroundable(myProject, CompilerBundle.message("compiler.ref.service.validation.task.name")) {
-          @Override
-          public void run(@NotNull ProgressIndicator indicator) {
-            indicator.setText(CompilerBundle.message("compiler.ref.service.validation.progress.text"));
-            CompileScope projectCompileScope = compilerManager.createProjectCompileScope(myProject);
-            boolean isUpToDate = compilerManager.isUpToDate(projectCompileScope);
-            executeOnBuildThread(() -> {
-              if (isUpToDate) {
-                myDirtyModulesHolder.compilerActivityFinished(projectCompileScope.getAffectedModules(), Module.EMPTY_ARRAY);
-                myCompilationCount.increment();
-                openReaderIfNeed();
-              }
-              else {
-                myDirtyModulesHolder.compilerActivityFinished(Module.EMPTY_ARRAY, projectCompileScope.getAffectedModules());
-              }
-            });
+      ApplicationManager.getApplication().executeOnPooledThread(() -> {
+        CompileScope projectCompileScope = compilerManager.createProjectCompileScope(myProject);
+        boolean isUpToDate = compilerManager.isUpToDate(projectCompileScope);
+        executeOnBuildThread(() -> {
+          if (isUpToDate) {
+            myDirtyModulesHolder.compilerActivityFinished(projectCompileScope.getAffectedModules(), Module.EMPTY_ARRAY);
+            myCompilationCount.increment();
+            openReaderIfNeed();
+          }
+          else {
+            myDirtyModulesHolder.compilerActivityFinished(Module.EMPTY_ARRAY, projectCompileScope.getAffectedModules());
           }
         });
+      });
     }
   }
 
@@ -194,7 +187,7 @@ public class CompilerReferenceServiceImpl extends CompilerReferenceService imple
                                                                                  @NotNull GlobalSearchScope searchScope,
                                                                                  @NotNull FileType searchFileType,
                                                                                  @NotNull CompilerHierarchySearchType searchType) {
-      if (!isServiceEnabledFor(aClass) || searchScope == LibraryScopeCache.getInstance(myProject).getLibrariesOnlyScope()) return null;
+    if (!isServiceEnabledFor(aClass) || searchScope == LibraryScopeCache.getInstance(myProject).getLibrariesOnlyScope()) return null;
 
     Couple<Map<VirtualFile, T[]>> directInheritorsAndCandidates =
       CachedValuesManager.getCachedValue(aClass, () -> CachedValueProvider.Result.create(
@@ -211,7 +204,7 @@ public class CompilerReferenceServiceImpl extends CompilerReferenceService imple
 
     if (directInheritorsAndCandidates == null) return null;
     GlobalSearchScope dirtyScope = myDirtyModulesHolder.getDirtyScope();
-    if (ElementPlace.LIB == ElementPlace.get(aClass.getContainingFile().getVirtualFile(), myProjectFileIndex)) {
+    if (ElementPlace.LIB == ReadAction.compute(() -> ElementPlace.get(aClass.getContainingFile().getVirtualFile(), myProjectFileIndex))) {
       dirtyScope = dirtyScope.union(LibraryScopeCache.getInstance(myProject).getLibrariesOnlyScope());
     }
     return new CompilerHierarchyInfoImpl<>(directInheritorsAndCandidates, dirtyScope, searchScope);
@@ -278,12 +271,13 @@ public class CompilerReferenceServiceImpl extends CompilerReferenceService imple
     myReadDataLock.lock();
     try {
       if (myReader == null) return null;
-      VirtualFile file = PsiUtilCore.getVirtualFile(psiElement);
+      VirtualFile file = ReadAction.compute(() -> PsiUtilCore.getVirtualFile(psiElement));
       ElementPlace place = ElementPlace.get(file, myProjectFileIndex);
       if (place == null || (place == ElementPlace.SRC && myDirtyModulesHolder.contains(file))) {
         return null;
       }
       final LanguageLightRefAdapter<?, ?> adapter = findAdapterForFileType(file.getFileType());
+      if (adapter == null) return null;
       final LightRef ref = ReadAction.compute(() -> adapter.asLightUsage(psiElement, myReader.getNameEnumerator()));
       if (ref == null) return null;
       if (place == ElementPlace.LIB && buildHierarchyForLibraryElements) {
@@ -360,13 +354,14 @@ public class CompilerReferenceServiceImpl extends CompilerReferenceService imple
     return myProject;
   }
 
+  @Nullable
   static LanguageLightRefAdapter findAdapterForFileType(@NotNull FileType fileType) {
     for (LanguageLightRefAdapter adapter : LanguageLightRefAdapter.INSTANCES) {
       if (adapter.getFileTypes().contains(fileType)) {
         return adapter;
       }
     }
-    throw new AssertionError("adapter is not found for: " + fileType);
+    return null;
   }
 
   private static void executeOnBuildThread(Runnable compilationFinished) {
index 63ccf50b42382be2edbcf76afc229644785a370d..36aeaebbe8fb8ff71c1fe1343a264fb5a5372aee 100644 (file)
@@ -17,6 +17,7 @@ package com.intellij.compiler.backwardRefs;
 
 import com.intellij.ide.highlighter.JavaClassFileType;
 import com.intellij.ide.highlighter.JavaFileType;
+import com.intellij.openapi.application.ReadAction;
 import com.intellij.openapi.fileTypes.FileType;
 import com.intellij.openapi.roots.impl.LibraryScopeCache;
 import com.intellij.psi.*;
@@ -82,12 +83,12 @@ public class JavaLightUsageAdapter implements LanguageLightRefAdapter<PsiClass,
   public List<LightRef> getHierarchyRestrictedToLibraryScope(@NotNull LightRef baseRef,
                                                              @NotNull PsiElement basePsi,
                                                              @NotNull ByteArrayEnumerator names, @NotNull GlobalSearchScope libraryScope) {
-    final PsiClass baseClass = ObjectUtils.notNull(basePsi instanceof PsiClass ? (PsiClass)basePsi : ((PsiMember)basePsi).getContainingClass());
+    final PsiClass baseClass = ObjectUtils.notNull(basePsi instanceof PsiClass ? (PsiClass)basePsi : ReadAction.compute(() -> (PsiMember)basePsi).getContainingClass());
 
     final List<LightRef> overridden = new ArrayList<>();
     Processor<PsiClass> processor = c -> {
       if (c.hasModifierProperty(PsiModifier.PRIVATE)) return true;
-      String qName = c.getQualifiedName();
+      String qName = ReadAction.compute(() -> c.getQualifiedName());
       if (qName == null) return true;
       overridden.add(baseRef.override(id(qName, names)));
       return true;
index 7e5d767fb9d4315facc8774dc9b0fce993140c71..78d393c990304afd19a7bae6f1014fc8b7cb7681 100644 (file)
@@ -17,10 +17,8 @@ package com.intellij.compiler.server;
 
 import com.intellij.ProjectTopics;
 import com.intellij.compiler.CompilerConfiguration;
-import com.intellij.compiler.CompilerConfigurationImpl;
 import com.intellij.compiler.CompilerWorkspaceConfiguration;
 import com.intellij.compiler.impl.CompilerUtil;
-import com.intellij.compiler.impl.javaCompiler.BackendCompiler;
 import com.intellij.compiler.impl.javaCompiler.javac.JavacConfiguration;
 import com.intellij.compiler.server.impl.BuildProcessClasspathManager;
 import com.intellij.concurrency.JobScheduler;
@@ -106,7 +104,6 @@ import org.jetbrains.jps.cmdline.BuildMain;
 import org.jetbrains.jps.cmdline.ClasspathBootstrap;
 import org.jetbrains.jps.incremental.Utils;
 import org.jetbrains.jps.model.java.JpsJavaSdkType;
-import org.jetbrains.jps.model.java.compiler.JavaCompilers;
 import org.jetbrains.jps.model.serialization.JpsGlobalLoader;
 
 import javax.tools.*;
@@ -921,7 +918,7 @@ public class BuildManager implements Disposable {
       }
     }
 
-    final JavaSdkVersion oldestPossible = getOldestPossiblePlatformForBuildProcess(project);
+    final JavaSdkVersion oldestPossible = JavaSdkVersion.JDK_1_8;
 
     if (projectJdk == null || sdkVersion == null || !sdkVersion.isAtLeast(oldestPossible)) {
       final Sdk internalJdk = JavaAwareProjectJdkTableImpl.getInstanceEx().getInternalJdk();
@@ -945,16 +942,6 @@ public class BuildManager implements Disposable {
     return version;
   }
 
-  @NotNull
-  private static JavaSdkVersion getOldestPossiblePlatformForBuildProcess(Project project) {
-    final BackendCompiler javaCompiler = ((CompilerConfigurationImpl)CompilerConfiguration.getInstance(project)).getDefaultCompiler();
-    final String id = javaCompiler != null? javaCompiler.getId() : JavaCompilers.JAVAC_ID;
-    if (id == JavaCompilers.ECLIPSE_ID || id == JavaCompilers.ECLIPSE_EMBEDDED_ID) {
-      return JavaSdkVersion.JDK_1_7; // because bundled ecj is compiled against 1.7
-    }
-    return JavaSdkVersion.JDK_1_6;
-  }
-
   private Future<Pair<RequestFuture<PreloadedProcessMessageHandler>, OSProcessHandler>> launchPreloadedBuildProcess(final Project project, ExecutorService projectTaskQueue) throws Exception {
     ensureListening();
 
index 5aa4c074f6d2567ed75d8875bc3520a3460a0d56..d922e54f3883a0c75bec4cbe783acce11f91678f 100644 (file)
@@ -25,6 +25,7 @@ import com.intellij.openapi.compiler.CompilerManager;
 import com.intellij.openapi.diagnostic.Logger;
 import com.intellij.openapi.module.Module;
 import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.text.StringUtil;
 import com.intellij.openapi.vfs.VirtualFile;
 import com.intellij.packaging.artifacts.Artifact;
 import com.intellij.packaging.impl.compiler.ArtifactCompileScope;
@@ -89,54 +90,79 @@ public class InternalProjectTaskRunner extends ProjectTaskRunner {
                                            @Nullable CompileStatusNotification compileNotification,
                                            @NotNull Map<Class<? extends ProjectTask>, List<ProjectTask>> tasksMap) {
     Collection<? extends ProjectTask> buildTasks = tasksMap.get(ModuleBuildTask.class);
+    if (ContainerUtil.isEmpty(buildTasks)) return;
+    ModulesBuildSettings modulesBuildSettings = assembleModulesBuildSettings(buildTasks);
+
+    CompilerManager compilerManager = CompilerManager.getInstance(project);
+    CompileScope scope = createScope(compilerManager, context,
+                                     modulesBuildSettings.modules,
+                                     modulesBuildSettings.includeDependentModules,
+                                     modulesBuildSettings.includeRuntimeDependencies);
+    if (modulesBuildSettings.isIncrementalBuild) {
+      compilerManager.make(scope, compileNotification);
+    }
+    else {
+      compilerManager.compile(scope, compileNotification);
+    }
+  }
 
+  private static class ModulesBuildSettings {
+    final boolean isIncrementalBuild;
+    final boolean includeDependentModules;
+    final boolean includeRuntimeDependencies;
+    final Collection<Module> modules;
+
+    public ModulesBuildSettings(boolean isIncrementalBuild,
+                                boolean includeDependentModules,
+                                boolean includeRuntimeDependencies,
+                                Collection<Module> modules) {
+      this.isIncrementalBuild = isIncrementalBuild;
+      this.includeDependentModules = includeDependentModules;
+      this.includeRuntimeDependencies = includeRuntimeDependencies;
+      this.modules = modules;
+    }
+  }
 
-    if (!ContainerUtil.isEmpty(buildTasks)) {
-      List<Module> modules = new SmartList<>();
+  private static ModulesBuildSettings assembleModulesBuildSettings(Collection<? extends ProjectTask> buildTasks) {
+    Collection<Module> modules = new SmartList<>();
+    Collection<ModuleBuildTask> incrementalTasks = ContainerUtil.newSmartList();
+    Collection<ModuleBuildTask> excludeDependentTasks = ContainerUtil.newSmartList();
+    Collection<ModuleBuildTask> excludeRuntimeTasks = ContainerUtil.newSmartList();
 
-      Boolean isIncrementalBuild = null;
-      Boolean includeDependentModules = null;
-      Boolean includeRuntimeDependencies = null;
+    for (ProjectTask buildProjectTask : buildTasks) {
+      ModuleBuildTask moduleBuildTask = (ModuleBuildTask)buildProjectTask;
+      modules.add(moduleBuildTask.getModule());
 
-      for (ProjectTask buildProjectTask : buildTasks) {
-        ModuleBuildTask moduleBuildTask = (ModuleBuildTask)buildProjectTask;
-        assertModuleBuildSettings(moduleBuildTask, isIncrementalBuild, includeDependentModules, includeRuntimeDependencies);
-        modules.add(moduleBuildTask.getModule());
-        if (!moduleBuildTask.isIncrementalBuild()) {
-          isIncrementalBuild = false;
-        }
-        if (moduleBuildTask.isIncludeDependentModules()) {
-          includeDependentModules = true;
-        }
-        if (moduleBuildTask.isIncludeRuntimeDependencies()) {
-          includeRuntimeDependencies = true;
-        }
+      if (moduleBuildTask.isIncrementalBuild()) {
+        incrementalTasks.add(moduleBuildTask);
       }
-      CompilerManager compilerManager = CompilerManager.getInstance(project);
-      CompileScope scope = createScope(
-        compilerManager, context, modules, includeDependentModules != null, includeRuntimeDependencies != null);
-      if (isIncrementalBuild == null) {
-        compilerManager.make(scope, compileNotification);
+      if (!moduleBuildTask.isIncludeDependentModules()) {
+        excludeDependentTasks.add(moduleBuildTask);
       }
-      else {
-        compilerManager.compile(scope, compileNotification);
+      if (!moduleBuildTask.isIncludeRuntimeDependencies()) {
+        excludeRuntimeTasks.add(moduleBuildTask);
       }
     }
-  }
 
-  private static void assertModuleBuildSettings(ModuleBuildTask moduleBuildTask,
-                                                Boolean isIncrementalBuild,
-                                                Boolean includeDependentModules,
-                                                Boolean includeRuntimeDependencies) {
-    if (isIncrementalBuild != null && moduleBuildTask.isIncrementalBuild()) {
-      LOG.warn("Incremental build setting for the module '" + moduleBuildTask.getModule().getName() + "' will be ignored");
+    boolean isIncrementalBuild = incrementalTasks.size() == buildTasks.size();
+    boolean includeDependentModules = excludeDependentTasks.size() != buildTasks.size();
+    boolean includeRuntimeDependencies = excludeRuntimeTasks.size() != buildTasks.size();
+
+    if (!isIncrementalBuild && !incrementalTasks.isEmpty()) {
+      assertModuleBuildSettingsConsistent(incrementalTasks, "will be built ignoring incremental build setting");
     }
-    if (includeDependentModules != null && !moduleBuildTask.isIncludeDependentModules()) {
-      LOG.warn("'Module '" + moduleBuildTask.getModule().getName() + "' will be built along with dependent modules");
+    if (includeDependentModules && !excludeDependentTasks.isEmpty()) {
+      assertModuleBuildSettingsConsistent(excludeDependentTasks, "will be built along with dependent modules");
     }
-    if (includeRuntimeDependencies != null && !moduleBuildTask.isIncludeRuntimeDependencies()) {
-      LOG.warn("'Module '" + moduleBuildTask.getModule().getName() + "' will be built along with runtime dependencies");
+    if (includeRuntimeDependencies && !excludeRuntimeTasks.isEmpty()) {
+      assertModuleBuildSettingsConsistent(excludeRuntimeTasks, "will be built along with runtime dependencies");
     }
+    return new ModulesBuildSettings(isIncrementalBuild, includeDependentModules, includeRuntimeDependencies, modules);
+  }
+
+  private static void assertModuleBuildSettingsConsistent(Collection<ModuleBuildTask> moduleBuildTasks, String warnMsg) {
+    String moduleNames = StringUtil.join(moduleBuildTasks, task -> task.getModule().getName(), ", ");
+    LOG.warn("Module" + (moduleBuildTasks.size() > 1 ? "s": "") + " : '" + moduleNames + "' " + warnMsg);
   }
 
   private static CompileScope createScope(CompilerManager compilerManager,
index ee6cefa4e0c2209a2725ee7a21e37620f521e28f..35a3a04684abe5c788ee1d94ac9a7249476994c8 100644 (file)
@@ -30,6 +30,7 @@ import com.intellij.openapi.application.ApplicationManager;
 import com.intellij.openapi.diagnostic.Logger;
 import com.intellij.openapi.util.Computable;
 import com.intellij.openapi.util.Key;
+import com.intellij.openapi.util.text.StringUtil;
 import com.intellij.psi.PsiClass;
 import com.intellij.ui.classFilter.ClassFilter;
 import com.intellij.util.containers.HashMap;
@@ -205,8 +206,10 @@ public class RequestManagerImpl extends DebugProcessAdapterImpl implements Reque
 
     ClassPrepareRequest classPrepareRequest = myEventRequestManager.createClassPrepareRequest();
     classPrepareRequest.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD);
-    classPrepareRequest.addClassFilter(pattern);
-    classPrepareRequest.putProperty(CLASS_NAME, pattern);
+    if (!StringUtil.isEmpty(pattern)) {
+      classPrepareRequest.addClassFilter(pattern);
+      classPrepareRequest.putProperty(CLASS_NAME, pattern);
+    }
 
     registerRequestInternal(requestor, classPrepareRequest);
     return classPrepareRequest;
index 444d261ea2d8c7272760ecbb406bea7272f61961..d88a3e2d323a04cca511df443bdeb1624920edbf 100644 (file)
@@ -24,6 +24,7 @@ import com.intellij.debugger.DebuggerBundle;
 import com.intellij.debugger.DebuggerManagerEx;
 import com.intellij.debugger.SourcePosition;
 import com.intellij.debugger.engine.DebugProcessImpl;
+import com.intellij.debugger.engine.DebuggerManagerThreadImpl;
 import com.intellij.debugger.engine.JVMName;
 import com.intellij.debugger.engine.JVMNameUtil;
 import com.intellij.debugger.engine.evaluation.EvaluateException;
@@ -42,17 +43,17 @@ import com.intellij.openapi.util.Computable;
 import com.intellij.openapi.util.InvalidDataException;
 import com.intellij.openapi.util.JDOMExternalizerUtil;
 import com.intellij.openapi.util.Key;
+import com.intellij.openapi.util.registry.Registry;
 import com.intellij.psi.*;
 import com.intellij.util.StringBuilderSpinAllocator;
+import com.intellij.util.containers.ContainerUtil;
 import com.intellij.util.text.CharArrayUtil;
 import com.intellij.xdebugger.breakpoints.XBreakpoint;
-import com.sun.jdi.AbsentInformationException;
-import com.sun.jdi.Location;
-import com.sun.jdi.Method;
-import com.sun.jdi.ReferenceType;
+import com.sun.jdi.*;
 import com.sun.jdi.event.LocatableEvent;
 import com.sun.jdi.event.MethodEntryEvent;
 import com.sun.jdi.event.MethodExitEvent;
+import com.sun.jdi.request.ClassPrepareRequest;
 import com.sun.jdi.request.EventRequest;
 import com.sun.jdi.request.MethodEntryRequest;
 import com.sun.jdi.request.MethodExitRequest;
@@ -63,7 +64,9 @@ import org.jetbrains.annotations.Nullable;
 import org.jetbrains.java.debugger.breakpoints.properties.JavaMethodBreakpointProperties;
 
 import javax.swing.*;
+import java.util.List;
 import java.util.Set;
+import java.util.function.Consumer;
 
 public class MethodBreakpoint extends BreakpointWithHighlighter<JavaMethodBreakpointProperties> {
   private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.ui.breakpoints.MethodBreakpoint");
@@ -123,7 +126,50 @@ public class MethodBreakpoint extends BreakpointWithHighlighter<JavaMethodBreakp
     }
   }
 
+  private void createRequestForSubClasses(@NotNull DebugProcessImpl debugProcess, @NotNull ReferenceType baseType) {
+    DebuggerManagerThreadImpl.assertIsManagerThread();
+    ClassPrepareRequest request = debugProcess.getRequestsManager().createClassPrepareRequest((debuggerProcess, referenceType) -> {
+      if (instanceOf(referenceType, baseType)) {
+        createRequestForPreparedClassEmulated(debugProcess, referenceType, () -> {});
+      }
+    }, null);
+    if (request != null) {
+      request.enable();
+    }
+
+    processSubTypes(baseType, subType -> createRequestForPreparedClassEmulated(debugProcess, subType, () -> {}));
+  }
+
+  private void createRequestForPreparedClassEmulated(@NotNull DebugProcessImpl debugProcess, @NotNull ReferenceType classType, Runnable onCreated) {
+    try {
+      for (Method method : classType.methods()) {
+        if (getMethodName().equals(method.name()) && mySignature.getName(debugProcess).equals(method.signature())) {
+          // desired class found - now also track all new classes
+          Location firstLocation = ContainerUtil.getFirstItem(method.allLineLocations());
+          if (firstLocation != null) {
+            RequestManagerImpl requestsManager = debugProcess.getRequestsManager();
+            requestsManager.enableRequest(requestsManager.createBreakpointRequest(this, firstLocation));
+          }
+          onCreated.run();
+          break;
+        }
+      }
+    }
+    catch (Exception e) {
+      LOG.debug(e);
+    }
+  }
+
   protected void createRequestForPreparedClass(@NotNull DebugProcessImpl debugProcess, @NotNull ReferenceType classType) {
+    if (Registry.is("debugger.emulate.method.breakpoints")) {
+      createRequestForPreparedClassEmulated(debugProcess, classType, () -> createRequestForSubClasses(debugProcess, classType));
+    }
+    else {
+      createRequestForPreparedClassOriginal(debugProcess, classType);
+    }
+  }
+
+  private void createRequestForPreparedClassOriginal(@NotNull DebugProcessImpl debugProcess, @NotNull ReferenceType classType) {
     try {
       boolean hasMethod = false;
       for (Method method : classType.allMethods()) {
@@ -399,4 +445,37 @@ public class MethodBreakpoint extends BreakpointWithHighlighter<JavaMethodBreakp
     boolean isStatic;
     int methodLine;
   }
+
+  private static boolean instanceOf(@Nullable Type type, @NotNull Type superType) {
+    if (type == null) {
+      return false;
+    }
+    if (superType.equals(type)) {
+      return true;
+    }
+    if (type instanceof InterfaceType) {
+      return ((InterfaceType)type).superinterfaces().stream().anyMatch(t -> instanceOf(t, superType));
+    } else if (type instanceof ClassType) {
+      if (((ClassType)type).interfaces().stream().anyMatch(t -> instanceOf(t, superType))) {
+        return true;
+      }
+      return instanceOf(((ClassType)type).superclass(), superType);
+    }
+    return false;
+  }
+
+  private static void processSubTypes(ReferenceType classType, Consumer<ReferenceType> consumer) {
+    List<? extends ReferenceType> inheritors = null;
+    if (classType instanceof InterfaceType) {
+      inheritors = ContainerUtil.concat(((InterfaceType)classType).subinterfaces(), ((InterfaceType)classType).implementors());
+    } else if (classType instanceof ClassType) {
+      inheritors = ((ClassType)classType).subclasses();
+    }
+    if (inheritors != null) {
+      inheritors.forEach(type -> {
+        consumer.accept(type);
+        processSubTypes(type, consumer);
+      });
+    }
+  }
 }
index ef19b296623cec36bb5de9042d976df232548acc..3c7c177cfad2fc2621b3def919de1530dd2fa210 100644 (file)
@@ -95,7 +95,10 @@ public class JavaParameterHintManager {
               || JavaTokenType.PLUS.equals(tokenType)) && expr.getOperand() instanceof PsiLiteralExpression;
     }
 
-    if (callArgument instanceof PsiThisExpression) {
+    if (callArgument instanceof PsiThisExpression 
+        || callArgument instanceof PsiBinaryExpression 
+        || callArgument instanceof PsiPolyadicExpression) 
+    {
       return true;
     }
 
index e267486b576dfbbfbfdc7cb576550c59ea2fbb3f..cf7ef670c80a3d6c1bf3cecc11dd577caf6f295a 100644 (file)
@@ -281,7 +281,7 @@ public class JavaDirectInheritorsSearcher implements QueryExecutor<PsiClass, Dir
     SearchScope scope = parameters.getScope();
     if (!(scope instanceof GlobalSearchScope)) return null;
 
-    PsiClass searchClass = (PsiClass)PsiUtil.preferCompiledElement(parameters.getClassToProcess());
+    PsiClass searchClass = ReadAction.compute(() -> (PsiClass)PsiUtil.preferCompiledElement(parameters.getClassToProcess()));
     final CompilerReferenceService compilerReferenceService = CompilerReferenceService.getInstance(project);
     return compilerReferenceService.getDirectInheritors(searchClass,
                                                         (GlobalSearchScope)useScope,
index 295cc05e8db5d2b1c35803812adac26a23974be7..709df2f2afbfa6d55a9cf6adec38b05d33350da3 100644 (file)
@@ -662,5 +662,20 @@ class Key {
     onLineStartingWith("System.set").assertNoInlays()
     onLineStartingWith("new").assertNoInlays()
   }
+  
+  fun `test poly and binary expressions`() {
+    setup("""
+class Test {
+  void test() {
+    check(1 + 1);
+    int i=1; check(1 + 1 + 1);
+  }
+  void check(int isShow) {}
+}
+""")
+    
+    onLineStartingWith("check").assertInlays("isShow->1")
+    onLineStartingWith("int").assertInlays("isShow->1")
+  }
 
 }
\ No newline at end of file
index 795f3b94c4ebb88f10926d24baf40847ed765673..5fefc7dff7ba0513ac83fd80cf2faecb21bdb918 100644 (file)
@@ -144,7 +144,7 @@ public class ClasspathBootstrap {
     cp.addAll(getInstrumentationUtilRoots());
     cp.add(getResourcePath(IXMLBuilder.class));  // nano-xml
     cp.add(getJpsPluginSystemClassesPath().getAbsolutePath().replace('\\', '/'));
-    cp.addAll(getJavac8RefeScannerClasspath());
+    cp.addAll(getJavac8RefScannerClasspath());
     //don't forget to update layoutCommunityJps() in layouts.gant accordingly
 
     if (!isLauncherUsed) {
@@ -311,15 +311,15 @@ public class ClasspathBootstrap {
     }
   }
 
-  private static List<String> getJavac8RefeScannerClasspath() {
-    String jpsBuildersPath = getResourcePath(ClasspathBootstrap.class);
-    File jpsBuilders = new File(jpsBuildersPath);
-    if (jpsBuilders.isDirectory()) {
+  private static List<String> getJavac8RefScannerClasspath() {
+    String instrumentationPath = getResourcePath(NotNullVerifyingInstrumenter.class);
+    File instrumentationUtil = new File(instrumentationPath);
+    if (instrumentationUtil.isDirectory()) {
       //running from sources: load classes from .../out/production/javac-ref-scanner-8
-      return Collections.singletonList(new File(jpsBuilders.getParentFile(), "javac-ref-scanner-8").getAbsolutePath());
+      return Collections.singletonList(new File(instrumentationUtil.getParentFile(), "javac-ref-scanner-8").getAbsolutePath());
     }
     else {
-      return Collections.emptyList();
+      return Collections.singletonList(instrumentationPath);
     }
   }
 }
index e5f4ca26e492b6a4c55c910517b3acd5c53c777f..f6beb95d42394476a825b05bcc6d07deb3a87c9e 100644 (file)
@@ -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.
@@ -16,7 +16,9 @@
 package com.intellij.openapi.application;
 
 import org.jdom.Attribute;
+import org.jdom.Element;
 import org.jdom.Text;
+import org.jetbrains.annotations.NotNull;
 
 /**
  * Allows to disable expansion of path macros in the values of certain properties.
@@ -24,6 +26,9 @@ import org.jdom.Text;
  * @author yole
  */
 public abstract class PathMacroFilter {
+  public boolean skipPathMacros(@NotNull Element element) {
+    return false;
+  }
 
   public boolean skipPathMacros(Text element) {
     return false;
index 781d0f9ebdaf15a2bb0f92833e39136d7c146ab7..ba6b779a3766d2bb4eafeb90bc623e83e136b3f6 100644 (file)
@@ -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.
@@ -36,6 +36,10 @@ public abstract class PathMacroMap {
   }
 
   public final void substitute(@NotNull Element e, boolean caseSensitive, boolean recursively, @Nullable PathMacroFilter filter) {
+    if (filter != null && filter.skipPathMacros(e)) {
+      return;
+    }
+
     for (Content child : e.getContent()) {
       if (child instanceof Element) {
         substitute((Element)child, caseSensitive, recursively, filter);
index 5ae53956ebfbfe34d4e8e247466355d27aa193e7..e79258927e630f8e2afe0c6f3aa645c202d8c514 100644 (file)
@@ -202,12 +202,7 @@ private fun isEqualContent(result: VirtualFile, lineSeparator: LineSeparator, co
     return false
   }
 
-  for (i in headerLength..oldContent.size - 1) {
-    if (oldContent[i] != content.internalBuffer[i - headerLength]) {
-      return false
-    }
-  }
-  return true
+  return (headerLength..oldContent.size - 1).all { oldContent[it] == content.internalBuffer[it - headerLength] }
 }
 
 private fun doWrite(requestor: Any, file: VirtualFile, content: Any, lineSeparator: LineSeparator, prependXmlProlog: Boolean) {
@@ -274,7 +269,7 @@ private fun deleteFile(file: Path, requestor: Any, virtualFile: VirtualFile?) {
   }
 }
 
-fun deleteFile(requestor: Any, virtualFile: VirtualFile) {
+internal fun deleteFile(requestor: Any, virtualFile: VirtualFile) {
   runWriteAction { virtualFile.delete(requestor) }
 }
 
index 3d430c819f8d72cd6d82b3f74935d4af8c4ebd39..6687d5bdb0fc5b64181a528f54ffbd7173a9ae7c 100644 (file)
@@ -67,7 +67,7 @@ class ModuleStateStorageManager(macroSubstitutor: TrackingPathMacroSubstitutor,
         for (option in component.getChildren("option")) {
           element.setAttribute(option.getAttributeValue("key"), option.getAttributeValue("value"))
         }
-        break;
+        break
       }
     }
 
index ab3cfffef2d0ed43ad1bcf15972128ff3a628193..e89825d1420fcd0f9274a834b3424b40bea04572 100644 (file)
@@ -19,9 +19,11 @@ import com.intellij.openapi.components.RoamingType
 import com.intellij.openapi.components.StateStorage
 import com.intellij.openapi.components.TrackingPathMacroSubstitutor
 import com.intellij.openapi.components.impl.stores.FileStorageCoreUtil
+import com.intellij.openapi.diagnostic.catchAndLog
 import com.intellij.openapi.diagnostic.debug
 import com.intellij.openapi.util.JDOMUtil
 import com.intellij.util.containers.SmartHashSet
+import com.intellij.util.isEmpty
 import com.intellij.util.loadElement
 import gnu.trove.THashMap
 import org.jdom.Attribute
@@ -66,7 +68,7 @@ abstract class XmlElementStorage protected constructor(protected val fileSpec: S
   protected open fun dataLoadedFromProvider(element: Element?) {
   }
 
-  private fun loadDataFromProvider() = provider!!.read(fileSpec, roamingType)?.let { loadElement(it) }
+  private fun loadDataFromProvider() = provider!!.read(fileSpec, roamingType)?.let(::loadElement)
 
   private fun loadState(element: Element): StateMap {
     beforeElementLoaded(element)
@@ -151,12 +153,12 @@ abstract class XmlElementStorage protected constructor(protected val fileSpec: S
   }
 
   protected open fun beforeElementSaved(element: Element) {
-    if (pathMacroSubstitutor != null) {
+    pathMacroSubstitutor?.let {
       try {
-        pathMacroSubstitutor.collapsePaths(element)
+        it.collapsePaths(element)
       }
       finally {
-        pathMacroSubstitutor.reset()
+        it.reset()
       }
     }
   }
@@ -171,7 +173,7 @@ abstract class XmlElementStorage protected constructor(protected val fileSpec: S
       return
     }
 
-    try {
+    LOG.catchAndLog {
       val newElement = if (deleted) null else if (streamProvider) loadDataFromProvider() else loadLocalData()
       val states = storageDataRef.get()
       if (newElement == null) {
@@ -187,9 +189,6 @@ abstract class XmlElementStorage protected constructor(protected val fileSpec: S
         setStates(states, newStates)
       }
     }
-    catch (e: Throwable) {
-      LOG.error(e)
-    }
   }
 }
 
@@ -235,7 +234,7 @@ private fun save(states: StateMap, rootElementName: String?, newLiveStates: Map<
 
     rootElement.addContent(element)
   }
-  return if (JDOMUtil.isEmpty(rootElement)) null else rootElement
+  return if (rootElement.isEmpty()) null else rootElement
 }
 
 internal fun Element.normalizeRootName(): Element {
index 29da3e6002b5bff735450bb4f5e0aa349cd8ee6a..4cfd88f3035fe28a24974b7bd3a54722700cac9f 100644 (file)
@@ -153,7 +153,7 @@ public class DiffContentFactoryImpl extends DiffContentFactoryEx {
   @Override
   public DocumentContent create(@Nullable Project project, @NotNull Document document, @Nullable FileType fileType) {
     VirtualFile file = FileDocumentManager.getInstance().getFile(document);
-    if (file == null) return new FileAwareDocumentContent(project, document, fileType, null, null, null, null);
+    if (file == null) return new DocumentContentImpl(project, document, fileType, null, null, null, null);
     return create(project, document, file);
   }
 
@@ -168,7 +168,7 @@ public class DiffContentFactoryImpl extends DiffContentFactoryEx {
   @Override
   public DocumentContent create(@Nullable Project project, @NotNull Document document, @Nullable DocumentContent referent) {
     if (referent == null) return new DocumentContentImpl(document);
-    return new FileAwareDocumentContent(project, document, referent.getContentType(), referent.getHighlightFile(), null, null, null);
+    return new DocumentContentImpl(project, document, referent.getContentType(), referent.getHighlightFile(), null, null, null);
   }
 
 
@@ -333,7 +333,7 @@ public class DiffContentFactoryImpl extends DiffContentFactoryEx {
     String correctedContent = StringUtil.convertLineSeparators(text);
 
     Document document = createDocument(project, correctedContent, fileType, fileName, readOnly);
-    FileAwareDocumentContent content = new FileAwareDocumentContent(project, document, fileType, highlightFile, separator, charset, bom);
+    DocumentContent content = new DocumentContentImpl(project, document, fileType, highlightFile, separator, charset, bom);
 
     if (fileName != null) content.putUserData(DiffUserDataKeysEx.FILE_NAME, fileName);
 
index 28f83801977e24f34e5cbfef1372af1edfbb5e2f..fc56425127b85dc156f8e30dadde72ce465733b8 100644 (file)
@@ -58,11 +58,11 @@ public class DiffPsiFileSupport {
   }
 
 
-  private static boolean isDiffFile(@Nullable PsiFile file) {
+  public static boolean isDiffFile(@Nullable PsiFile file) {
     return file != null && isDiffFile(file.getVirtualFile());
   }
 
-  private static boolean isDiffFile(@Nullable VirtualFile file) {
+  public static boolean isDiffFile(@Nullable VirtualFile file) {
     return file != null && file.getUserData(KEY) == Boolean.TRUE;
   }
 }
index f4547ff03ae7516bf0c479072e47a196ce860857..257c832551aa8bfeebdd508be946da32e9e03da9 100644 (file)
@@ -17,10 +17,15 @@ package com.intellij.diff.contents;
 
 import com.intellij.diff.util.LineCol;
 import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.fileEditor.FileDocumentManager;
+import com.intellij.openapi.fileEditor.OpenFileDescriptor;
 import com.intellij.openapi.fileTypes.FileType;
+import com.intellij.openapi.project.Project;
 import com.intellij.openapi.vfs.VirtualFile;
 import com.intellij.pom.Navigatable;
 import com.intellij.util.LineSeparator;
+import com.intellij.util.diff.Diff;
+import com.intellij.util.diff.FilesTooBigForDiffException;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
@@ -30,6 +35,8 @@ import java.nio.charset.Charset;
  * Allows to compare some text associated with document.
  */
 public class DocumentContentImpl extends DiffContentBase implements DocumentContent {
+  @Nullable private final Project myProject;
+
   @NotNull private final Document myDocument;
 
   @Nullable private final FileType myType;
@@ -40,15 +47,17 @@ public class DocumentContentImpl extends DiffContentBase implements DocumentCont
   @Nullable private final Boolean myBOM;
 
   public DocumentContentImpl(@NotNull Document document) {
-    this(document, null, null, null, null, null);
+    this(null, document, null, null, null, null, null);
   }
 
-  public DocumentContentImpl(@NotNull Document document,
+  public DocumentContentImpl(@Nullable Project project,
+                             @NotNull Document document,
                              @Nullable FileType type,
                              @Nullable VirtualFile highlightFile,
                              @Nullable LineSeparator separator,
                              @Nullable Charset charset,
                              @Nullable Boolean bom) {
+    myProject = project;
     myDocument = document;
     myType = type;
     myHighlightFile = highlightFile;
@@ -57,6 +66,11 @@ public class DocumentContentImpl extends DiffContentBase implements DocumentCont
     myBOM = bom;
   }
 
+  @Nullable
+  public Project getProject() {
+    return myProject;
+  }
+
   @NotNull
   @Override
   public Document getDocument() {
@@ -72,7 +86,8 @@ public class DocumentContentImpl extends DiffContentBase implements DocumentCont
   @Nullable
   @Override
   public Navigatable getNavigatable(@NotNull LineCol position) {
-    return null;
+    if (myProject == null || getHighlightFile() == null || !getHighlightFile().isValid()) return null;
+    return new MyNavigatable(myProject, getHighlightFile(), getDocument(), position);
   }
 
   @Nullable
@@ -104,4 +119,49 @@ public class DocumentContentImpl extends DiffContentBase implements DocumentCont
   public Charset getCharset() {
     return myCharset;
   }
+
+
+  private static class MyNavigatable implements Navigatable {
+    @NotNull private final Project myProject;
+    @NotNull private final VirtualFile myTargetFile;
+    @NotNull private final Document myDocument;
+    @NotNull private final LineCol myPosition;
+
+    public MyNavigatable(@NotNull Project project, @NotNull VirtualFile targetFile, @NotNull Document document, @NotNull LineCol position) {
+      myProject = project;
+      myTargetFile = targetFile;
+      myDocument = document;
+      myPosition = position;
+    }
+
+    @Override
+    public void navigate(boolean requestFocus) {
+      Document targetDocument = FileDocumentManager.getInstance().getDocument(myTargetFile);
+      LineCol targetPosition = translatePosition(myDocument, targetDocument, myPosition);
+      OpenFileDescriptor descriptor = new OpenFileDescriptor(myProject, myTargetFile, targetPosition.line, targetPosition.column);
+      if (descriptor.canNavigate()) descriptor.navigate(true);
+    }
+
+    @Override
+    public boolean canNavigate() {
+      return myTargetFile.isValid();
+    }
+
+    @Override
+    public boolean canNavigateToSource() {
+      return false;
+    }
+
+    @NotNull
+    private static LineCol translatePosition(@NotNull Document fromDocument, @Nullable Document toDocument, @NotNull LineCol position) {
+      try {
+        if (toDocument == null) return position;
+        int targetLine = Diff.translateLine(fromDocument.getCharsSequence(), toDocument.getCharsSequence(), position.line, true);
+        return new LineCol(targetLine, position.column);
+      }
+      catch (FilesTooBigForDiffException ignore) {
+        return position;
+      }
+    }
+  }
 }
diff --git a/platform/diff-impl/src/com/intellij/diff/contents/FileAwareDocumentContent.java b/platform/diff-impl/src/com/intellij/diff/contents/FileAwareDocumentContent.java
deleted file mode 100644 (file)
index 9ef1116..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-package com.intellij.diff.contents;
-
-import com.intellij.diff.util.LineCol;
-import com.intellij.openapi.editor.Document;
-import com.intellij.openapi.fileEditor.FileDocumentManager;
-import com.intellij.openapi.fileEditor.OpenFileDescriptor;
-import com.intellij.openapi.fileTypes.FileType;
-import com.intellij.openapi.project.Project;
-import com.intellij.openapi.vfs.VirtualFile;
-import com.intellij.pom.Navigatable;
-import com.intellij.util.LineSeparator;
-import com.intellij.util.diff.Diff;
-import com.intellij.util.diff.FilesTooBigForDiffException;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.nio.charset.Charset;
-
-public class FileAwareDocumentContent extends DocumentContentImpl {
-  @Nullable private final Project myProject;
-
-  public FileAwareDocumentContent(@Nullable Project project,
-                                  @NotNull Document document,
-                                  @Nullable FileType fileType,
-                                  @Nullable VirtualFile highlightFile,
-                                  @Nullable LineSeparator separator,
-                                  @Nullable Charset charset,
-                                  @Nullable Boolean bom) {
-    super(document, fileType, highlightFile, separator, charset, bom);
-    myProject = project;
-  }
-
-  @Override
-  public Navigatable getNavigatable(@NotNull LineCol position) {
-    if (myProject == null || getHighlightFile() == null || !getHighlightFile().isValid()) return null;
-    return new MyNavigatable(myProject, getHighlightFile(), getDocument(), position);
-  }
-
-  private static class MyNavigatable implements Navigatable {
-    @NotNull private final Project myProject;
-    @NotNull private final VirtualFile myTargetFile;
-    @NotNull private final Document myDocument;
-    @NotNull private final LineCol myPosition;
-
-    public MyNavigatable(@NotNull Project project, @NotNull VirtualFile targetFile, @NotNull Document document, @NotNull LineCol position) {
-      myProject = project;
-      myTargetFile = targetFile;
-      myDocument = document;
-      myPosition = position;
-    }
-
-    @Override
-    public void navigate(boolean requestFocus) {
-      Document targetDocument = FileDocumentManager.getInstance().getDocument(myTargetFile);
-      LineCol targetPosition = translatePosition(myDocument, targetDocument, myPosition);
-      OpenFileDescriptor descriptor = new OpenFileDescriptor(myProject, myTargetFile, targetPosition.line, targetPosition.column);
-      if (descriptor.canNavigate()) descriptor.navigate(true);
-    }
-
-    @Override
-    public boolean canNavigate() {
-      return myTargetFile.isValid();
-    }
-
-    @Override
-    public boolean canNavigateToSource() {
-      return false;
-    }
-
-    @NotNull
-    private static LineCol translatePosition(@NotNull Document fromDocument, @Nullable Document toDocument, @NotNull LineCol position) {
-      try {
-        if (toDocument == null) return position;
-        int targetLine = Diff.translateLine(fromDocument.getCharsSequence(), toDocument.getCharsSequence(), position.line, true);
-        return new LineCol(targetLine, position.column);
-      }
-      catch (FilesTooBigForDiffException ignore) {
-        return position;
-      }
-    }
-  }
-}
index 12d24eeeac883a10a21945d64f2e70b9e0cb55c8..1cb2286dad68fae9bf5e08e9bbd2682a5aaa82e9 100644 (file)
@@ -29,22 +29,21 @@ import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
 public class FileDocumentContentImpl extends DocumentContentImpl implements FileContent {
-  @Nullable private final Project myProject;
   @NotNull private final VirtualFile myFile;
 
   public FileDocumentContentImpl(@Nullable Project project,
                                  @NotNull Document document,
                                  @NotNull VirtualFile file) {
-    super(document, file.getFileType(), file, getSeparator(file), file.getCharset(), file.getBOM() != null);
-    myProject = project;
+    super(project, document, file.getFileType(), file, getSeparator(file), file.getCharset(), file.getBOM() != null);
     myFile = file;
   }
 
   @Nullable
   @Override
   public Navigatable getNavigatable(@NotNull LineCol position) {
-    if (myProject == null || myProject.isDefault() || !myFile.isValid()) return null;
-    return new OpenFileDescriptor(myProject, myFile, position.line, position.column);
+    Project project = getProject();
+    if (project == null || project.isDefault() || !myFile.isValid()) return null;
+    return new OpenFileDescriptor(project, myFile, position.line, position.column);
   }
 
   @Nullable
index 2af233072646d63d4d77c144b81de3f79715cb41..8d0281086a7fd538f1790ed6e2181875ad67fb9b 100644 (file)
@@ -22,7 +22,7 @@ import com.intellij.openapi.actionSystem.AnAction;
 import org.jetbrains.annotations.NotNull;
 
 public interface ConsoleView extends ExecutionConsole {
-  void print(@NotNull String s, @NotNull ConsoleViewContentType contentType);
+  void print(@NotNull String text, @NotNull ConsoleViewContentType contentType);
 
   void clear();
 
index ba953d455a1a71dba8a6ed90d87a1f6f9fc2363f..9f811d5a59a08189499deb950aa8c0b12df5c0e4 100644 (file)
@@ -27,5 +27,7 @@ public interface CodeStyleScheme extends Scheme {
   @NotNull
   String getName();
   boolean isDefault();
+  
+  @NotNull
   CodeStyleSettings getCodeStyleSettings();
 }
index c26f3c889722170316ec31b520d671f912eeecb4..aec74685d04eafa68dad8708b3e91215f8b5f5e6 100644 (file)
@@ -26,6 +26,7 @@ import com.intellij.openapi.actionSystem.AnAction
 import com.intellij.openapi.actionSystem.AnActionEvent
 import com.intellij.openapi.actionSystem.CommonDataKeys
 import com.intellij.openapi.editor.Editor
+import com.intellij.openapi.editor.InlayModel
 import com.intellij.openapi.editor.ex.EditorSettingsExternalizable
 import com.intellij.openapi.editor.impl.InlayModelImpl
 import com.intellij.openapi.fileEditor.FileEditorManager
@@ -80,7 +81,7 @@ class BlacklistCurrentMethodIntention : IntentionAction, HighPriorityAction {
   override fun getFamilyName(): String = presentableFamilyName
 
   override fun isAvailable(project: Project, editor: Editor, file: PsiFile): Boolean {
-    return InlayParameterHintsExtension.hasAnyExtensions() && hasParameterHintAtOffset(editor)
+    return InlayParameterHintsExtension.hasAnyExtensions() && hasParameterHintAtOffset(editor, file)
   }
 
   override fun invoke(project: Project, editor: Editor, file: PsiFile) {
@@ -117,10 +118,17 @@ class ToggleInlineHintsAction : AnAction() {
   }
 }
 
-private fun hasParameterHintAtOffset(editor: Editor): Boolean {
+private fun hasParameterHintAtOffset(editor: Editor, file: PsiFile): Boolean {
+  if (editor.inlayModel !is InlayModel) return false
+  
   val offset = editor.caretModel.offset
+  val element = file.findElementAt(offset)
+  
+  val startOffset = element?.textRange?.startOffset ?: offset
+  val endOffset = element?.textRange?.endOffset ?: offset
+  
   return editor.inlayModel is InlayModelImpl && editor.inlayModel
-      .getInlineElementsInRange(offset, offset)
+      .getInlineElementsInRange(startOffset, endOffset)
       .find { ParameterHintsPresentationManager.getInstance().isParameterHint(it) } != null
 }
 
index c60874b8a8e49f826ef286bad273098e1bce462a..6c2ace04b4b98fdf0dbf524d5da25e0b372acc10 100644 (file)
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.intellij.codeInsight.hints.settings.ParameterNameHintsConfigurable">
-  <grid id="27dc6" binding="myConfigurable" layout-manager="GridLayoutManager" row-count="2" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+  <grid id="27dc6" binding="myConfigurable" layout-manager="GridLayoutManager" row-count="3" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
     <margin top="0" left="0" bottom="0" right="0"/>
     <constraints>
       <xy x="20" y="20" width="2069" height="400"/>
@@ -44,7 +44,7 @@
         <children>
           <component id="d30b0" class="com.intellij.ui.components.JBLabel">
             <constraints>
-              <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="0" fill="0" indent="0" use-parent-layout="false"/>
+              <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
             </constraints>
             <properties>
               <text value="&lt;html&gt;&#10;No hints will be shown for methods matched by any of these patterns.&lt;br&gt;&#10;Pattern matches fully quialifed method name, parameters count and parameter names.&#10;&lt;ul&gt;&#10;&lt;li&gt;java.lang matches all methods from &quot;java.lang&quot; package&lt;/li&gt;&#10;&lt;li&gt;java.lang.*(*, *) matches all methods from &quot;java.lang&quot; package with couple parameters&lt;/li&gt;&#10;&lt;li&gt;(*info) matches all single parameter methods when parameter name ends with &quot;info&quot;&lt;/li&gt;&#10;&lt;li&gt;(key, value) will match all methods with parameters &quot;key&quot; and &quot;value&quot;&lt;/li&gt;&#10;&lt;li&gt;*.put(key, value) will match all put methods with &quot;key&quot; and &quot;value&quot; params&#10;&lt;/ul&gt;&#10;&lt;/html&gt;"/>
           </component>
         </children>
       </grid>
+      <vspacer id="ca768">
+        <constraints>
+          <grid row="2" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
+        </constraints>
+      </vspacer>
     </children>
   </grid>
 </form>
index dafddf1bab56678e5c6342244aa0789afaa32b5e..fc39d201bee8bc51ece9823d8a41257f9a7cf612 100644 (file)
@@ -105,6 +105,7 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable
 
   private final List<LookupListener> myListeners = ContainerUtil.createLockFreeCopyOnWriteList();
   private PrefixChangeListener myPrefixChangeListener = new PrefixChangeListener.Adapter() {};
+  private final LookupPreview myPreview = new LookupPreview(this);
 
   private long myStampShown = 0;
   private boolean myShown = false;
@@ -853,6 +854,7 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable
         listener.currentItemChanged(event);
       }
     }
+    myPreview.updatePreview(currentItem);
   }
 
   public boolean fillInCommonPrefix(boolean explicitlyInvoked) {
diff --git a/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupPreview.java b/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupPreview.java
new file mode 100644 (file)
index 0000000..aa3cf7e
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * 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.codeInsight.lookup.impl;
+
+import com.intellij.codeInsight.lookup.LookupElement;
+import com.intellij.codeInsight.lookup.LookupElementPresentation;
+import com.intellij.openapi.editor.*;
+import com.intellij.openapi.editor.colors.EditorColors;
+import com.intellij.openapi.editor.colors.EditorFontType;
+import com.intellij.openapi.editor.impl.EditorImpl;
+import com.intellij.openapi.util.Disposer;
+import com.intellij.openapi.util.TextRange;
+import com.intellij.openapi.util.registry.Registry;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.ui.JBColor;
+import com.intellij.util.containers.ContainerUtil;
+import com.intellij.util.containers.FList;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.awt.*;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author peter
+ */
+class LookupPreview {
+  private final List<Inlay> myInlays = new ArrayList<>();
+  private final LookupImpl myLookup;
+
+  LookupPreview(LookupImpl lookup) {
+    myLookup = lookup;
+  }
+
+  void updatePreview(@Nullable LookupElement item) {
+    if (!Registry.is("ide.lookup.preview.insertion")) return;
+
+    myInlays.forEach(Disposer::dispose);
+    myInlays.clear();
+
+    String suffix = getSuffixText(item);
+    if (!suffix.isEmpty() && myLookup.getTopLevelEditor() instanceof EditorImpl) {
+      myLookup.getTopLevelEditor().getCaretModel().runForEachCaret(caret -> {
+        ensureCaretBeforeInlays(caret);
+        addInlay(suffix, caret.getOffset());
+      });
+    }
+  }
+
+  private static void ensureCaretBeforeInlays(Caret caret) {
+    LogicalPosition position = caret.getLogicalPosition();
+    if (position.leansForward) {
+      caret.moveToLogicalPosition(position.leanForward(false));
+    }
+  }
+
+  private String getSuffixText(@Nullable LookupElement item) {
+    if (item != null) {
+      String itemText = StringUtil.notNullize(LookupElementPresentation.renderElement(item).getItemText());
+      FList<TextRange> fragments = LookupCellRenderer.getMatchingFragments(myLookup.itemPattern(item), itemText);
+      if (fragments != null && !fragments.isEmpty()) {
+        List<TextRange> list = ContainerUtil.newArrayList(fragments);
+        return itemText.substring(list.get(list.size() - 1).getEndOffset(), itemText.length());
+      }
+    }
+    return "";
+  }
+
+  private void addInlay(String suffix, int caretOffset) {
+    Inlay inlay = myLookup.getTopLevelEditor().getInlayModel().addInlineElement(caretOffset, createGrayRenderer(suffix));
+    if (inlay != null) {
+      myInlays.add(inlay);
+      Disposer.register(myLookup, inlay);
+    }
+  }
+
+  @NotNull
+  private static EditorCustomElementRenderer createGrayRenderer(final String suffix) {
+    return new EditorCustomElementRenderer() {
+      @Override
+      public int calcWidthInPixels(@NotNull Editor editor) {
+        return editor.getContentComponent().getFontMetrics(getFont(editor)).stringWidth(suffix);
+      }
+
+      @Override
+      public void paint(@NotNull Editor editor, @NotNull Graphics g, @NotNull Rectangle r) {
+        g.setColor(editor.getColorsScheme().getColor(EditorColors.CARET_ROW_COLOR));
+        g.fillRect(r.x, r.y, r.width, r.height);
+        g.setColor(JBColor.GRAY);
+        g.setFont(getFont(editor));
+        g.drawString(suffix, r.x, r.y + ((EditorImpl)editor).getAscent());
+      }
+
+      private Font getFont(@NotNull Editor editor) {
+        return editor.getColorsScheme().getFont(EditorFontType.PLAIN);
+      }
+    };
+  }
+}
index e0c8ff90d16080f182875a65fbe07ee9c159ec1e..ff7aeb6fc1fdeca5d4cc0116224e479d3984f558 100644 (file)
@@ -29,9 +29,6 @@ import org.jetbrains.annotations.Nullable;
 
 import java.util.*;
 
-import static com.intellij.execution.impl.ConsoleViewImpl.HyperlinkTokenInfo;
-import static com.intellij.execution.impl.ConsoleViewImpl.TokenInfo;
-
 /**
  * IJ user may want the console to use cyclic buffer, i.e. don't keep more than particular amount of symbols. So, we need
  * to have a data structure that allow to achieve that. This class serves for that purpose.
@@ -107,16 +104,16 @@ public class ConsoleBuffer {
    * <p/>
    * Target offsets are anchored to the {@link #myDeferredOutput deferred buffer}.
    */
-  private final List<TokenInfo> myDeferredTokens = new ArrayList<>();
+  private final List<ConsoleViewImpl.TokenInfo> myDeferredTokens = new ArrayList<>();
   private final Set<ConsoleViewContentType> myDeferredTypes = new HashSet<>();
 
   private boolean myKeepSlashR = true;
 
-  public ConsoleBuffer() {
+  ConsoleBuffer() {
     this(useCycleBuffer(), getCycleBufferSize(), DEFAULT_CYCLIC_BUFFER_UNIT_SIZE);
   }
 
-  public ConsoleBuffer(boolean useCyclicBuffer, int cyclicBufferSize, int cyclicBufferUnitSize) {
+  ConsoleBuffer(boolean useCyclicBuffer, int cyclicBufferSize, int cyclicBufferUnitSize) {
     myUseCyclicBuffer = useCyclicBuffer;
     myCyclicBufferSize = Math.max(cyclicBufferSize, 0);
     myCyclicBufferUnitSize = cyclicBufferUnitSize;
@@ -138,11 +135,11 @@ public class ConsoleBuffer {
     return SystemProperties.getIntProperty("idea.cycle.buffer.size", 1024) * 1024;
   }
 
-  public boolean isUseCyclicBuffer() {
+  boolean isUseCyclicBuffer() {
     return myUseCyclicBuffer;
   }
 
-  public int getCyclicBufferSize() {
+  int getCyclicBufferSize() {
     return myCyclicBufferSize;
   }
 
@@ -151,33 +148,38 @@ public class ConsoleBuffer {
   }
 
   public boolean isEmpty() {
-    return myDeferredOutput.isEmpty() || (myDeferredOutput.size() == 1 && myDeferredOutput.getFirst().length() <= 0);
+    return myDeferredOutput.isEmpty() || myDeferredOutput.size() == 1 && myDeferredOutput.getFirst().length() <= 0;
   }
 
   public int getLength() {
     return myDeferredOutputLength;
   }
 
-  public int getUserInputLength() {
+  int getUserInputLength() {
     return myDeferredUserInput.length();
   }
 
+  @NotNull
   public String getUserInput() {
     return myDeferredUserInput.toString();
   }
 
-  public List<TokenInfo> getDeferredTokens() {
+  @NotNull
+  List<ConsoleViewImpl.TokenInfo> getDeferredTokens() {
     return myDeferredTokens;
   }
 
-  public Set<ConsoleViewContentType> getDeferredTokenTypes() {
+  @NotNull
+  Set<ConsoleViewContentType> getDeferredTokenTypes() {
     return myDeferredTypes;
   }
 
-  public Deque<StringBuilder> getDeferredOutput() {
+  @NotNull
+  Deque<StringBuilder> getDeferredOutput() {
     return myDeferredOutput;
   }
 
+  @NotNull
   public String getText() {
     if (myDeferredOutput.size() > 1) {
       final StringBuilder buffer = new StringBuilder();
@@ -186,12 +188,10 @@ public class ConsoleBuffer {
       }
       return buffer.toString();
     }
-    else if (myDeferredOutput.size() == 1) {
+    if (myDeferredOutput.size() == 1) {
       return myDeferredOutput.getFirst().substring(0);
     }
-    else {
-      return "";
-    }
+    return "";
   }
 
   /**
@@ -203,7 +203,7 @@ public class ConsoleBuffer {
    *
    * @param types content types that should not be stripped during the buffer's cycling
    */
-  public void setContentTypesToNotStripOnCycling(@NotNull Collection<ConsoleViewContentType> types) {
+  void setContentTypesToNotStripOnCycling(@NotNull Collection<ConsoleViewContentType> types) {
     myContentTypesToNotStripOnCycling.clear();
     myContentTypesToNotStripOnCycling.addAll(types);
   }
@@ -231,23 +231,22 @@ public class ConsoleBuffer {
   }
 
   @Nullable
-  public String cutFirstUserInputLine() {
+  String cutFirstUserInputLine() {
     final String text = myDeferredUserInput.substring(0, myDeferredUserInput.length());
     final int index = Math.max(text.lastIndexOf('\n'), text.lastIndexOf('\r'));
     if (index < 0) {
       return null;
     }
-    final String result = text.substring(0, index + 1);
     myDeferredUserInput.setLength(0);
     myDeferredUserInput.append(text.substring(index + 1));
-    return result;
+    return text.substring(0, index + 1);
   }
 
-  public void addUserText(int offset, String text) {
+  void addUserText(int offset, @NotNull String text) {
     myDeferredUserInput.insert(offset, text);
   }
 
-  public void removeUserText(int startOffset, int endOffset) {
+  void removeUserText(int startOffset, int endOffset) {
     if (startOffset >= myDeferredUserInput.length()) {
       return;
     }
@@ -256,7 +255,7 @@ public class ConsoleBuffer {
     myDeferredUserInput.delete(startToUse, endToUse);
   }
 
-  public void replaceUserText(int startOffset, int endOffset, String text) {
+  void replaceUserText(int startOffset, int endOffset, @NotNull String text) {
     myDeferredUserInput.replace(startOffset, endOffset, text);
   }
 
@@ -395,7 +394,6 @@ public class ConsoleBuffer {
    * @param numberOfNewSymbols number of symbols read from the managed process output
    * @return number of newly read symbols that should be accepted
    */
-  @SuppressWarnings({"ForLoopReplaceableByForEach"})
   private int trimDeferredOutputIfNecessary(final int numberOfNewSymbols) {
     if (!myUseCyclicBuffer || myDeferredOutputLength + numberOfNewSymbols <= myCyclicBufferSize) {
       return numberOfNewSymbols;
@@ -416,7 +414,7 @@ public class ConsoleBuffer {
 
     TIntArrayList indicesOfTokensToRemove = new TIntArrayList();
     for (int i = 0; i < myDeferredTokens.size(); i++) {
-      TokenInfo tokenInfo = myDeferredTokens.get(i);
+      ConsoleViewImpl.TokenInfo tokenInfo = myDeferredTokens.get(i);
       tokenInfo.startOffset -= context.removedSymbolsNumber;
       tokenInfo.endOffset -= context.removedSymbolsNumber;
 
@@ -453,12 +451,12 @@ public class ConsoleBuffer {
     }
 
     if (!myDeferredTokens.isEmpty()) {
-      TokenInfo tokenInfo = myDeferredTokens.get(0);
+      ConsoleViewImpl.TokenInfo tokenInfo = myDeferredTokens.get(0);
       if (tokenInfo.startOffset > 0) {
         final HyperlinkInfo hyperlinkInfo = tokenInfo.getHyperlinkInfo();
         myDeferredTokens
-          .add(0, hyperlinkInfo != null ? new HyperlinkTokenInfo(ConsoleViewContentType.USER_INPUT, 0, tokenInfo.startOffset, hyperlinkInfo)
-                                        : new TokenInfo(ConsoleViewContentType.USER_INPUT, 0, tokenInfo.startOffset));
+          .add(0, hyperlinkInfo != null ? new ConsoleViewImpl.HyperlinkTokenInfo(ConsoleViewContentType.USER_INPUT, 0, tokenInfo.startOffset, hyperlinkInfo)
+                                        : new ConsoleViewImpl.TokenInfo(ConsoleViewContentType.USER_INPUT, 0, tokenInfo.startOffset));
         myDeferredTypes.add(ConsoleViewContentType.USER_INPUT);
       }
     }
@@ -542,29 +540,23 @@ public class ConsoleBuffer {
   }
 
   private final class Context {
-
-    public final int numberOfSymbolsToRemove;
-    public StringBuilder currentBuffer;
+    private final int numberOfSymbolsToRemove;
+    StringBuilder currentBuffer;
     public Iterator<StringBuilder> iterator;
-    public int bufferOffset;
-    public int removedSymbolsNumber;
+    int bufferOffset;
+    int removedSymbolsNumber;
 
     Context(int numberOfSymbolsToRemove) {
       this.numberOfSymbolsToRemove = numberOfSymbolsToRemove;
       iterator = myDeferredOutput.iterator();
-      if (iterator.hasNext()) {
-        currentBuffer = iterator.next();
-      }
-      else {
-        currentBuffer = null;
-      }
+      currentBuffer = iterator.hasNext() ? iterator.next() : null;
     }
 
-    public boolean canContinueProcessing() {
+    boolean canContinueProcessing() {
       return removedSymbolsNumber < numberOfSymbolsToRemove && currentBuffer != null;
     }
 
-    public boolean nextBuffer() {
+    boolean nextBuffer() {
       if (iterator.hasNext()) {
         currentBuffer = iterator.next();
         bufferOffset = 0;
@@ -574,13 +566,12 @@ public class ConsoleBuffer {
     }
   }
 
-  @SuppressWarnings({"PointlessBooleanExpression", "ConstantConditions"})
   private void dumpDeferredOutput() {
     if (!DEBUG_PROCESSING) {
       return;
     }
     log("Tokens:");
-    for (TokenInfo token : myDeferredTokens) {
+    for (ConsoleViewImpl.TokenInfo token : myDeferredTokens) {
       log("\t" + token);
     }
     log("Data:");
@@ -590,7 +581,6 @@ public class ConsoleBuffer {
     log("-----------------------------------------------------------------------------------------------------");
   }
 
-  @SuppressWarnings({"UnusedDeclaration", "CallToPrintStackTrace"})
   private static void log(Object o) {
     //try {
     //  doLog(o);
@@ -600,7 +590,6 @@ public class ConsoleBuffer {
     //}
   }
 
-  @SuppressWarnings({"UnusedDeclaration", "CallToPrintStackTrace"})
   private static void log(String message, Object... formatData) {
     //try {
     //  doLog(String.format(message, formatData));
index d1fe83536f957dc6687722395573137a49ef89e5..cd84071790ed5339382215019ff2898b8c982132 100644 (file)
@@ -22,7 +22,8 @@ import org.jetbrains.annotations.NotNull;
 import java.io.IOException;
 
 public abstract class ConsoleState {
-  public abstract ConsoleState attachTo(ConsoleViewImpl console, ProcessHandler processHandler);
+  @NotNull
+  public abstract ConsoleState attachTo(@NotNull ConsoleViewImpl console, ProcessHandler processHandler);
   @NotNull
   public abstract ConsoleState dispose();
 
@@ -34,7 +35,7 @@ public abstract class ConsoleState {
     return false;
   }
 
-  public void sendUserInput(final String input) throws IOException {}
+  public void sendUserInput(@NotNull String input) throws IOException {}
 
   public abstract static class NotStartedStated extends ConsoleState {
     @NotNull
index 79c9b5e1ecc6df779ef86dcee3f4b4bbb09885f2..1daa5aaafa3afd2e6a81f2f2b8b38e2b5ff29de1 100644 (file)
@@ -261,8 +261,9 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo
                          boolean usePredefinedMessageFilter) {
     this(project, searchScope, viewer,
          new ConsoleState.NotStartedStated() {
+           @NotNull
            @Override
-           public ConsoleState attachTo(ConsoleViewImpl console, ProcessHandler processHandler) {
+           public ConsoleState attachTo(@NotNull ConsoleViewImpl console, ProcessHandler processHandler) {
              return new ConsoleViewRunningState(console, processHandler, this, true, true);
            }
          },
@@ -521,19 +522,16 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo
     myEditor.getScrollPane().getVerticalScrollBar().addMouseListener(mouseListener);
     myEditor.getScrollPane().getVerticalScrollBar().addMouseMotionListener(mouseListener);
     myHyperlinks = new EditorHyperlinkSupport(myEditor, myProject);
-    myEditor.getScrollingModel().addVisibleAreaListener(new VisibleAreaListener() {
-      @Override
-      public void visibleAreaChanged(VisibleAreaEvent e) {
-        // There is a possible case that the console text is populated while the console is not shown (e.g. we're debugging and
-        // 'Debugger' tab is active while 'Console' is not). It's also possible that newly added text contains long lines that
-        // are soft wrapped. We want to update viewport position then when the console becomes visible.
-        Rectangle oldR = e.getOldRectangle();
-
-        if (oldR != null && oldR.height <= 0 &&
-            e.getNewRectangle().height > 0 &&
-            isStickingToEnd()) {
-          scrollToEnd();
-        }
+    myEditor.getScrollingModel().addVisibleAreaListener(e -> {
+      // There is a possible case that the console text is populated while the console is not shown (e.g. we're debugging and
+      // 'Debugger' tab is active while 'Console' is not). It's also possible that newly added text contains long lines that
+      // are soft wrapped. We want to update viewport position then when the console becomes visible.
+      Rectangle oldR = e.getOldRectangle();
+
+      if (oldR != null && oldR.height <= 0 &&
+          e.getNewRectangle().height > 0 &&
+          isStickingToEnd()) {
+        scrollToEnd();
       }
     });
   }
@@ -593,15 +591,15 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo
   }
 
   @Override
-  public void print(@NotNull String s, @NotNull ConsoleViewContentType contentType) {
+  public void print(@NotNull String text, @NotNull ConsoleViewContentType contentType) {
     if (myInputMessageFilter == null) {
-      printHyperlink(s, contentType, null);
+      printHyperlink(text, contentType, null);
       return;
     }
 
-    List<Pair<String, ConsoleViewContentType>> result = myInputMessageFilter.applyFilter(s, contentType);
+    List<Pair<String, ConsoleViewContentType>> result = myInputMessageFilter.applyFilter(text, contentType);
     if (result == null) {
-      printHyperlink(s, contentType, null);
+      printHyperlink(text, contentType, null);
     }
     else {
       for (Pair<String, ConsoleViewContentType> pair : result) {
@@ -1113,7 +1111,7 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo
     }
 
     final ConsoleFolding prevFolding = myFolding.get(line - 1);
-    if (current == null && prevFolding != null) {
+    if (prevFolding != null && !prevFolding.equals(current)) {
       final int lEnd = line - 1;
       int lStart = lEnd;
       while (prevFolding.equals(myFolding.get(lStart - 1))) lStart--;
@@ -1148,7 +1146,6 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo
     return null;
   }
 
-
   public static class ClearAllAction extends DumbAwareAction {
     private final ConsoleView myConsoleView;
 
@@ -1268,13 +1265,13 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo
         if (myOriginalHandler != null) myOriginalHandler.execute(editor, charTyped, dataContext);
       }
       else {
-        final String s = String.valueOf(charTyped);
+        final String text = String.valueOf(charTyped);
         SelectionModel selectionModel = editor.getSelectionModel();
         if (selectionModel.hasSelection()) {
-          consoleView.replaceUserText(s, selectionModel.getSelectionStart(), selectionModel.getSelectionEnd());
+          consoleView.replaceUserText(text, selectionModel.getSelectionStart(), selectionModel.getSelectionEnd());
         }
         else {
-          consoleView.insertUserText(s, editor.getCaretModel().getOffset());
+          consoleView.insertUserText(text, editor.getCaretModel().getOffset());
         }
       }
     }
@@ -1323,17 +1320,17 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo
   private static class PasteHandler extends ConsoleAction {
     @Override
     public void execute(final ConsoleViewImpl consoleView, final DataContext context) {
-      String s = CopyPasteManager.getInstance().getContents(DataFlavor.stringFlavor);
-      if (s == null) return;
-      s = StringUtil.convertLineSeparators(s);
+      String text = CopyPasteManager.getInstance().getContents(DataFlavor.stringFlavor);
+      if (text == null) return;
+      text = StringUtil.convertLineSeparators(text);
       ApplicationManager.getApplication().assertIsDispatchThread();
       Editor editor = consoleView.myEditor;
       SelectionModel selectionModel = editor.getSelectionModel();
       if (selectionModel.hasSelection()) {
-        consoleView.replaceUserText(s, selectionModel.getSelectionStart(), selectionModel.getSelectionEnd());
+        consoleView.replaceUserText(text, selectionModel.getSelectionStart(), selectionModel.getSelectionEnd());
       }
       else {
-        consoleView.insertUserText(s, editor.getCaretModel().getOffset());
+        consoleView.insertUserText(text, editor.getCaretModel().getOffset());
       }
     }
   }
@@ -1540,12 +1537,7 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo
   @Override
   public void addChangeListener(@NotNull final ChangeListener listener, @NotNull final Disposable parent) {
     myListeners.add(listener);
-    Disposer.register(parent, new Disposable() {
-      @Override
-      public void dispose() {
-        myListeners.remove(listener);
-      }
-    });
+    Disposer.register(parent, () -> myListeners.remove(listener));
   }
 
   /**
@@ -1583,12 +1575,7 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo
       }
 
       final int deferredOffset = myContentSize - buffer.getLength() - buffer.getUserInputLength();
-      if (offset > info.endOffset) {
-        startOffset = info.endOffset;
-      }
-      else {
-        startOffset = Math.max(deferredOffset, Math.max(info.startOffset, offset));
-      }
+      startOffset = offset > info.endOffset ? info.endOffset : Math.max(deferredOffset, Math.max(info.startOffset, offset));
 
       buffer.addUserText(startOffset - deferredOffset, textToUse);
 
@@ -1612,13 +1599,13 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo
   /**
    * replace text
    *
-   * @param s     text for replace
+   * @param text     text for replace
    * @param start relatively to all document text
    * @param end   relatively to all document text
    */
-  private void replaceUserText(final String s, int start, int end) {
+  private void replaceUserText(@NotNull String text, int start, int end) {
     if (start == end) {
-      insertUserText(s, start);
+      insertUserText(text, start);
       return;
     }
     final ConsoleViewImpl consoleView = this;
@@ -1632,7 +1619,7 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo
       if (consoleView.myTokens.isEmpty()) return;
       final TokenInfo info = consoleView.myTokens.get(consoleView.myTokens.size() - 1);
       if (info.contentType != ConsoleViewContentType.USER_INPUT) {
-        consoleView.print(s, ConsoleViewContentType.USER_INPUT);
+        consoleView.print(text, ConsoleViewContentType.USER_INPUT);
         consoleView.flushDeferredText();
         editor.getCaretModel().moveToOffset(document.getTextLength());
         editor.getSelectionModel().removeSelection();
@@ -1652,10 +1639,10 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo
         editor.getCaretModel().moveToOffset(start);
         return;
       }
-      int charCountToReplace = s.length() - endOffset + startOffset;
 
-      buffer.replaceUserText(startOffset - deferredOffset, endOffset - deferredOffset, s);
+      buffer.replaceUserText(startOffset - deferredOffset, endOffset - deferredOffset, text);
 
+      int charCountToReplace = text.length() - endOffset + startOffset;
       info.endOffset += charCountToReplace;
       if (info.startOffset == info.endOffset) {
         consoleView.myTokens.remove(consoleView.myTokens.size() - 1);
@@ -1665,12 +1652,12 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo
 
     try {
       myInDocumentUpdate = true;
-      document.replaceString(startOffset, endOffset, s);
+      document.replaceString(startOffset, endOffset, text);
     }
     finally {
       myInDocumentUpdate = false;
     }
-    editor.getCaretModel().moveToOffset(startOffset + s.length());
+    editor.getCaretModel().moveToOffset(startOffset + text.length());
     editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
     editor.getSelectionModel().removeSelection();
   }
@@ -1768,7 +1755,7 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo
      * Checks if target line should be folded and returns its placeholder if the examination succeeds.
      *
      * @param line index of line to check
-     * @return placeholder text if given line should be folded; <code>null</code> otherwise
+     * @return placeholder text if given line should be folded; {@code null} otherwise
      */
     @Nullable
     private String getPlaceholder(int line) {
@@ -1786,8 +1773,7 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo
         index = text.indexOf('"', 1) + 1;
       }
       if (index == 0) {
-        boolean nonWhiteSpaceFound = false;
-        for (; index < text.length(); index++) {
+        for (boolean nonWhiteSpaceFound = false; index < text.length(); index++) {
           char c = text.charAt(index);
           if (c != ' ' && c != '\t') {
             nonWhiteSpaceFound = true;
index 63d0376d2b098e8d0ae6e638714bf7ec508f6866..85069452feb27419fa3a44edaeb729c5e4f00dae 100644 (file)
@@ -99,7 +99,7 @@ public class ConsoleViewRunningState extends ConsoleState {
   }
 
   @Override
-  public void sendUserInput(final String input) throws IOException {
+  public void sendUserInput(@NotNull final String input) throws IOException {
     if (myUserInputWriter == null) {
       throw new IOException(ExecutionBundle.message("no.user.process.input.error.message"));
     }
@@ -107,8 +107,9 @@ public class ConsoleViewRunningState extends ConsoleState {
     myUserInputWriter.flush();
   }
 
+  @NotNull
   @Override
-  public ConsoleState attachTo(final ConsoleViewImpl console, final ProcessHandler processHandler) {
+  public ConsoleState attachTo(@NotNull final ConsoleViewImpl console, final ProcessHandler processHandler) {
     return dispose().attachTo(console, processHandler);
   }
 
index a56d5ef2e16302c70ade1e6e8f4dad8cc8f23820..b1120fc9d2fd2e0ffe98845178bfb1a03b52f4c0 100644 (file)
  */
 package com.intellij.find.impl;
 
+import com.intellij.openapi.application.PathMacroFilter;
 import com.intellij.openapi.components.PersistentStateComponent;
+import com.intellij.openapi.components.impl.stores.FileStorageCoreUtil;
 import com.intellij.util.ArrayUtil;
 import com.intellij.util.xmlb.XmlSerializerUtil;
 import com.intellij.util.xmlb.annotations.AbstractCollection;
 import com.intellij.util.xmlb.annotations.Property;
 import com.intellij.util.xmlb.annotations.Tag;
+import org.jdom.Element;
 import org.jetbrains.annotations.NotNull;
 
 import java.util.ArrayList;
@@ -33,17 +36,17 @@ public class FindInProjectSettingsBase implements PersistentStateComponent<FindI
   @Tag("findStrings")
   @Property(surroundWithTag = false)
   @AbstractCollection(surroundWithTag = false, elementTag = "find", elementValueAttribute = "")
-  public List<String> findStrings = new ArrayList<String>();
+  public List<String> findStrings = new ArrayList<>();
 
   @Tag("replaceStrings")
   @Property(surroundWithTag = false)
   @AbstractCollection(surroundWithTag = false, elementTag = "replace", elementValueAttribute = "")
-  public List<String> replaceStrings = new ArrayList<String>();
+  public List<String> replaceStrings = new ArrayList<>();
 
   @Tag("dirStrings")
   @Property(surroundWithTag = false)
   @AbstractCollection(surroundWithTag = false, elementTag = "dir", elementValueAttribute = "")
-  public List<String> dirStrings = new ArrayList<String>();
+  public List<String> dirStrings = new ArrayList<>();
 
   @Override
   public void loadState(FindInProjectSettingsBase state) {
@@ -78,7 +81,7 @@ public class FindInProjectSettingsBase implements PersistentStateComponent<FindI
 
   @NotNull
   public List<String> getRecentDirectories() {
-    return new ArrayList<String>(dirStrings);
+    return new ArrayList<>(dirStrings);
   }
 
   public void addStringToFind(@NotNull String s){
@@ -115,4 +118,17 @@ public class FindInProjectSettingsBase implements PersistentStateComponent<FindI
       list.remove(0);
     }
   }
+
+  static class FindInProjectPathMacroFilter extends PathMacroFilter {
+    @Override
+    public boolean skipPathMacros(@NotNull Element element) {
+      String tag = element.getName();
+      // dirStrings must be replaced, so, we must not skip it
+      if (tag.equals("findStrings") || tag.equals("replaceStrings")) {
+        String component = FileStorageCoreUtil.findComponentName(element);
+        return component != null && (component.equals("FindSettings") || component.equals("FindInProjectRecents"));
+      }
+      return false;
+    }
+  }
 }
index 2266f00bfd61ac3e5aa2c9b5a2ae3832361e247f..ce88efbd47513ab9b361a0769a65dc9031748af2 100644 (file)
@@ -71,6 +71,7 @@ public class CodeStyleSchemeImpl extends ExternalizableSchemeAdapter implements
   }
 
   @Override
+  @NotNull
   public CodeStyleSettings getCodeStyleSettings() {
     SchemeDataHolder<? super CodeStyleSchemeImpl> dataHolder = myDataHolder;
     if (dataHolder != null) {
index 1815ea3be21701f56936941f952687d2775a2374..ab18b4e9e4380f9c6d30fde2533e106c12b6490f 100644 (file)
@@ -129,14 +129,7 @@ public class PopupDispatcher implements AWTEventListener, KeyEventDispatcher, Id
   }
 
   public static void unsetShowing(WizardPopup aBaseWizardPopup) {
-    if (ourActiveWizardRoot != null) {
-      for (WizardPopup wp = ourActiveWizardRoot; wp != null; wp = wp.getParent()) {
-        if (wp == aBaseWizardPopup) {
-          ourShowingStep = aBaseWizardPopup.getParent();
-          return;
-        }
-      }
-    }
+    ourShowingStep = aBaseWizardPopup.getParent();
   }
 
   public static WizardPopup getActiveRoot() {
index ad3fac4234eaf76cde1f1eff7aed522fdadbadda..69610eb6cbe91d823775acaa794182b7e89f8264 100644 (file)
@@ -21,6 +21,7 @@ import com.intellij.openapi.application.PathManager;
 import com.intellij.openapi.util.SystemInfo;
 import com.intellij.openapi.util.io.FileUtilRt;
 import com.intellij.openapi.util.text.StringUtil;
+import com.sun.jna.Library;
 import com.sun.jna.Native;
 import com.sun.jna.Pointer;
 import com.sun.jna.WString;
@@ -46,7 +47,9 @@ public class Restarter {
       return PathManager.getHomePath().contains(".app") && new File(PathManager.getBinPath(), "restarter").canExecute();
     }
     if (SystemInfo.isUnix) {
-      return CreateDesktopEntryAction.getLauncherScript() != null && new File(PathManager.getBinPath(), "restart.py").canExecute();
+      return JnaLoader.isLoaded() &&
+             CreateDesktopEntryAction.getLauncherScript() != null &&
+             new File(PathManager.getBinPath(), "restart.py").canExecute();
     }
 
     return false;
@@ -135,7 +138,12 @@ public class Restarter {
   private static void restartOnUnix(String... beforeRestart) throws IOException {
     String launcherScript = CreateDesktopEntryAction.getLauncherScript();
     if (launcherScript == null) throw new IOException("Launcher script not found in " + PathManager.getBinPath());
+
+    LibC lib = (LibC)Native.loadLibrary("c", LibC.class);
+    int pid = lib.getpid();
+
     doScheduleRestart(new File(PathManager.getBinPath(), "restart.py"), commands -> {
+      commands.add(String.valueOf(pid));
       commands.add(launcherScript);
       Collections.addAll(commands, beforeRestart);
     });
@@ -182,4 +190,9 @@ public class Restarter {
   private interface Shell32 extends StdCallLibrary {
     Pointer CommandLineToArgvW(WString command_line, IntByReference argc);
   }
+
+  @SuppressWarnings("SpellCheckingInspection")
+  private interface LibC extends Library {
+    int getpid();
+  }
 }
\ No newline at end of file
index d7ff268dbd3f47d184948fd691c4f3a5e355fc35..05bea8ddcbfc9bc5487f505b61a36f9d45f01d88 100644 (file)
                         serviceImplementation="com.intellij.find.impl.FindSettingsImpl"/>
     <applicationService serviceImplementation="com.intellij.find.impl.FindSettingsImpl$FindRecents"/>
     <projectService serviceInterface="com.intellij.find.FindInProjectSettings" serviceImplementation="com.intellij.find.impl.FindInProjectRecents"/>
+    <pathMacroFilter implementation="com.intellij.find.impl.FindInProjectSettingsBase$FindInProjectPathMacroFilter"/>
 
     <applicationService serviceImplementation="com.intellij.codeInsight.CodeInsightSettings"/>
 
 
     <applicationService serviceInterface="com.intellij.execution.console.ConsoleFoldingSettings"
                         serviceImplementation="com.intellij.execution.console.ConsoleFoldingSettings"/>
+
     <console.folding implementation="com.intellij.execution.console.SubstringConsoleFolding"/>
 
     <lookup.charFilter implementation="com.intellij.codeInsight.template.impl.LiveTemplateCharFilter" order="first" id="liveTemplate"/>
index ae134d493fb21cca645822baad6dfe54c79cc260..93bd2a16f31784a8ace25f8bece1adf885fc2529 100644 (file)
@@ -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.
@@ -17,7 +17,9 @@ package com.intellij.openapi.components;
 
 import com.intellij.openapi.application.PathMacroFilter;
 import org.jdom.Attribute;
+import org.jdom.Element;
 import org.jdom.Text;
+import org.jetbrains.annotations.NotNull;
 
 /**
  * @author yole
@@ -29,6 +31,16 @@ public class CompositePathMacroFilter extends PathMacroFilter {
     myFilters = filters;
   }
 
+  @Override
+  public boolean skipPathMacros(@NotNull Element element) {
+    for (PathMacroFilter filter : myFilters) {
+      if (filter.skipPathMacros(element)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
   @Override
   public boolean skipPathMacros(Text element) {
     for (PathMacroFilter filter : myFilters) {
index 1be7ff22a76ea709dbafb37ddac0db45be66840b..7ba9ad1d35e4264429cad3e26d162e24efa8e79a 100644 (file)
 package com.intellij.openapi.components.impl.stores;
 
 import com.intellij.application.options.PathMacrosCollector;
+import com.intellij.openapi.application.PathMacros;
+import com.intellij.openapi.components.CompositePathMacroFilter;
 import com.intellij.openapi.components.PathMacroSubstitutor;
 import com.intellij.openapi.components.TrackingPathMacroSubstitutor;
 import com.intellij.openapi.diagnostic.Logger;
 import com.intellij.openapi.util.JDOMUtil;
 import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.util.SmartList;
 import com.intellij.util.containers.StringInterner;
 import org.jdom.Element;
+import org.jdom.Parent;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
-import java.util.Collections;
+import java.util.Iterator;
 import java.util.List;
 import java.util.TreeMap;
 
@@ -47,16 +51,23 @@ public class FileStorageCoreUtil {
     List<Element> children = rootElement.getChildren(COMPONENT);
     if (children.isEmpty() && rootElement.getName().equals(COMPONENT) && rootElement.getAttributeValue(NAME) != null) {
       // exclusive component data
-      children = Collections.singletonList(rootElement);
+      // singleton must be not used here - later we modify list
+      children = new SmartList<>(rootElement);
     }
 
+    CompositePathMacroFilter filter = null;
+
     TreeMap<String, Element> map = new TreeMap<>();
-    for (Element element : children) {
+    for (Iterator<Element> iterator = children.iterator(); iterator.hasNext(); ) {
+      Element element = iterator.next();
       String name = getComponentNameIfValid(element);
       if (name == null || !(element.getAttributes().size() > 1 || !element.getChildren().isEmpty())) {
         continue;
       }
 
+      // so, PathMacroFilter can easily find component name (null parent)
+      iterator.remove();
+
       if (interner != null) {
         JDOMUtil.internElement(element, interner);
       }
@@ -64,7 +75,12 @@ public class FileStorageCoreUtil {
       map.put(name, element);
 
       if (pathMacroSubstitutor instanceof TrackingPathMacroSubstitutor) {
-        ((TrackingPathMacroSubstitutor)pathMacroSubstitutor).addUnknownMacros(name, PathMacrosCollector.getMacroNames(element));
+        if (filter == null) {
+          filter = new CompositePathMacroFilter(PathMacrosCollector.MACRO_FILTER_EXTENSION_POINT_NAME.getExtensions());
+        }
+
+        ((TrackingPathMacroSubstitutor)pathMacroSubstitutor)
+          .addUnknownMacros(name, PathMacrosCollector.getMacroNames(element, filter, PathMacros.getInstance()));
       }
 
       // remove only after "getMacroNames" - some PathMacroFilter requires element name attribute
@@ -82,4 +98,19 @@ public class FileStorageCoreUtil {
     }
     return name;
   }
+
+  @Nullable
+  public static String findComponentName(@NotNull Element element) {
+    Element componentElement = element;
+    while (true) {
+      Parent parent = componentElement.getParent();
+      if (parent == null || !(parent instanceof Element)) {
+        break;
+      }
+
+      componentElement = (Element)parent;
+    }
+
+    return StringUtil.nullize(componentElement.getAttributeValue(NAME));
+  }
 }
index e1d950aa10ba43d2a9cd7f59053c920e8d1e79c3..11a8986e0fbe1ef8e41faf357f006aa77714952a 100644 (file)
@@ -35,7 +35,7 @@ public abstract class BaseTestsOutputConsoleView implements ConsoleView, Observa
   private ConsoleView myConsole;
   private TestsOutputConsolePrinter myPrinter;
   protected TestConsoleProperties myProperties;
-  protected TestResultsPanel myTestResultsPanel;
+  private TestResultsPanel myTestResultsPanel;
 
   public BaseTestsOutputConsoleView(final TestConsoleProperties properties, final AbstractTestProxy unboundOutputRoot) {
     myProperties = properties;
@@ -65,13 +65,8 @@ public abstract class BaseTestsOutputConsoleView implements ConsoleView, Observa
   }
 
   @Override
-  public void print(@NotNull final String s, @NotNull final ConsoleViewContentType contentType) {
-    printNew(new Printable() {
-      @Override
-      public void printOn(final Printer printer) {
-        printer.print(s, contentType);
-      }
-    });
+  public void print(@NotNull final String text, @NotNull final ConsoleViewContentType contentType) {
+    printNew(printer -> printer.print(text, contentType));
   }
 
   @Override
index c92e8011544ba4a63a8063bcf5fb4376d769fa3f..089f3d0b4cd861de788e4ff790ee77e7f655b2bd 100644 (file)
@@ -24,10 +24,10 @@ import com.intellij.psi.search.GlobalSearchScope;
  * @author Roman.Chernyatchik
  */
 public class TestsConsoleBuilderImpl extends TextConsoleBuilderImpl {
-  public TestsConsoleBuilderImpl(final Project project,
-                                 final GlobalSearchScope scope,
-                                 boolean isViewer,
-                                 boolean usePredefinedMessageFilter) {
+  TestsConsoleBuilderImpl(final Project project,
+                          final GlobalSearchScope scope,
+                          boolean isViewer,
+                          boolean usePredefinedMessageFilter) {
     super(project, scope);
     setViewer(isViewer);
     setUsePredefinedMessageFilter(usePredefinedMessageFilter);
index 380163913197c786e355ac45792d89abb45cb2bc..a2630f9cacd3dfb54721a165bfc7f11305d6d541 100644 (file)
@@ -21,20 +21,22 @@ import com.intellij.execution.impl.ConsoleViewRunningState;
 import com.intellij.execution.process.ProcessHandler;
 import com.intellij.openapi.project.Project;
 import com.intellij.psi.search.GlobalSearchScope;
+import org.jetbrains.annotations.NotNull;
 
 /**
  * @author Roman.Chernyatchik
  */
-public class TestsConsoleViewImpl extends ConsoleViewImpl {
+class TestsConsoleViewImpl extends ConsoleViewImpl {
 
-  public TestsConsoleViewImpl(final Project project,
-                              final GlobalSearchScope searchScope,
-                              final boolean viewer,
-                              boolean usePredefinedMessageFilter) {
+  TestsConsoleViewImpl(final Project project,
+                       final GlobalSearchScope searchScope,
+                       final boolean viewer,
+                       boolean usePredefinedMessageFilter) {
     super(project, searchScope, viewer,
           new ConsoleState.NotStartedStated() {
+            @NotNull
             @Override
-            public ConsoleState attachTo(ConsoleViewImpl console, ProcessHandler processHandler) {
+            public ConsoleState attachTo(@NotNull ConsoleViewImpl console, ProcessHandler processHandler) {
               return new ConsoleViewRunningState(console, processHandler, this, false, !viewer);
             }
           },
index 28f5e4fe5d008acd877bd260fba97defaae3e456..6449f11f07b5d9c4c5013d61e5c6473356f5c92d 100644 (file)
@@ -231,6 +231,8 @@ debugger.renderers.file=true
 debugger.renderers.file.description=Enable file object renderer
 debugger.single.smart.step.force=true
 debugger.single.smart.step.force.description=Do force step into on single variant smart step into
+debugger.emulate.method.breakpoints=false
+debugger.emulate.method.breakpoints.description=Emulate method breakpoints with line breakpoints
 
 analyze.exceptions.on.the.fly=false
 analyze.exceptions.on.the.fly.description=Automatically analyze clipboard on frame activation,\
@@ -379,6 +381,9 @@ ide.completion.show.lower.case.classes.description=Show non-imported classes sta
 ide.completion.delay.autopopup.until.completed=false
 ide.completion.delay.autopopup.until.completed.description=Controls if completion autopopup is shown immediately and populated in background, or delayed until all suggestion are calculated
 
+ide.lookup.preview.insertion=false
+ide.lookup.preview.insertion.description=Preview in the editor of the approximate insertion result of the currently selected lookup suggestion
+
 ide.completion.middle.matching=true
 ide.completion.middle.matching.description=Suggest items in completion that contain the entered string somewhere in the middle.
 
@@ -886,4 +891,4 @@ ide.projectView.globalOptions.description=Make Project View options such as auto
 
 compiler.ref.index=false
 compiler.ref.index.description=Enables find usages using references from compiler indices
-compiler.ref.index.restartRequired=true
\ No newline at end of file
+compiler.ref.index.restartRequired=true
index d48253ef8d192156cdb93df4caf2211507482268..74f6e961f0dc7a9f92b3352db716d7c7be63410d 100644 (file)
@@ -91,7 +91,7 @@ public class MigrateToNewDiffUtil {
     else {
       Document document = oldContent.getDocument();
       if (document == null) return null;
-      return new DocumentContentImpl(document, oldContent.getContentType(), oldContent.getFile(), oldContent.getLineSeparator(), null, null) {
+      return new DocumentContentImpl(project, document, oldContent.getContentType(), oldContent.getFile(), oldContent.getLineSeparator(), null, null) {
         @Nullable
         @Override
         public Navigatable getNavigatable(@NotNull LineCol position) {
index b64cefa9fabcbc50eb6d2ac49017c94fbc03f610..ff0227571fedb9eb5def05e26bf42bdea463d804 100644 (file)
@@ -1791,7 +1791,7 @@ unnecessarily.qualified.statically.imported.element.display.name=Unnecessarily q
 unnecessarily.qualified.statically.imported.element.problem.descriptor=Statically imported element ''{0}'' is unnecessarily qualified with <code>#ref</code> #loc
 unnecessarily.qualified.statically.imported.element.quickfix=Remove unnecessary qualifier
 ignore.instanceof.on.library.classes=Ignore instanceof on library classes
-assertequals.may.be.assertsame.quickfix=Replace with 'assertSame()'
+replace.assertequals.quickfix=Replace with ''{0}''
 for.can.be.foreach.option=Report indexed 'java.util.List' loops
 for.can.be.foreach.option2=Do not report iterations over untyped collections
 cast.conflicts.with.instanceof.quickfix1=Replace ''{0}'' with ''{1}'' in cast
index 4c71b52fbb443533a7ebf74c2aea605fc381bb7a..916dd8e8d0819712e06fd800d697a87194693b3e 100644 (file)
  */
 package com.siyeh.ig.junit;
 
-import com.intellij.openapi.util.text.StringUtil;
-import com.intellij.psi.*;
-import com.intellij.psi.util.InheritanceUtil;
+import com.intellij.psi.PsiMethodCallExpression;
 import com.siyeh.InspectionGadgetsBundle;
 import com.siyeh.ig.BaseInspection;
 import com.siyeh.ig.BaseInspectionVisitor;
-import com.siyeh.ig.psiutils.TypeUtils;
-import org.jetbrains.annotations.NonNls;
 import org.jetbrains.annotations.NotNull;
 
 public class AssertEqualsBetweenInconvertibleTypesInspection extends BaseInspection {
@@ -36,13 +32,7 @@ public class AssertEqualsBetweenInconvertibleTypesInspection extends BaseInspect
   @Override
   @NotNull
   public String buildErrorString(Object... infos) {
-    final PsiType comparedType = (PsiType)infos[0];
-    final PsiType comparisonType = (PsiType)infos[1];
-    final String comparedTypeText = comparedType.getPresentableText();
-    final String comparisonTypeText = comparisonType.getPresentableText();
-    return InspectionGadgetsBundle.message("assertequals.between.inconvertible.types.problem.descriptor",
-                                           StringUtil.escapeXml(comparedTypeText),
-                                           StringUtil.escapeXml(comparisonTypeText));
+    return (String)infos[0];
   }
 
   @Override
@@ -60,63 +50,10 @@ public class AssertEqualsBetweenInconvertibleTypesInspection extends BaseInspect
     @Override
     public void visitMethodCallExpression(@NotNull PsiMethodCallExpression expression) {
       super.visitMethodCallExpression(expression);
-      final PsiReferenceExpression methodExpression = expression.getMethodExpression();
-      @NonNls final String methodName = methodExpression.getReferenceName();
-      if (!"assertEquals".equals(methodName)) {
-        return;
+      final String compatibilityErrorMessage = AssertEqualsHint.areExpectedActualTypesCompatible(expression);
+      if (compatibilityErrorMessage != null) {
+        registerMethodCallError(expression, compatibilityErrorMessage);
       }
-      final PsiMethod method = expression.resolveMethod();
-      if (method == null) {
-        return;
-      }
-      final PsiClass containingClass = method.getContainingClass();
-      final boolean junit5Assertions = InheritanceUtil.isInheritor(containingClass, "org.junit.jupiter.api.Assertions");
-      if (!InheritanceUtil.isInheritor(containingClass, "junit.framework.Assert") &&
-          !InheritanceUtil.isInheritor(containingClass, "org.junit.Assert") &&
-          !junit5Assertions) {
-        return;
-      }
-      final PsiParameterList parameterList = method.getParameterList();
-      final PsiParameter[] parameters = parameterList.getParameters();
-      if (parameters.length < 2) {
-        return;
-      }
-      final PsiType firstParameterType = parameters[0].getType();
-      final PsiExpressionList argumentList = expression.getArgumentList();
-      final PsiExpression[] arguments = argumentList.getExpressions();
-      final int argumentIndex;
-      if (!junit5Assertions && firstParameterType.equalsToText(CommonClassNames.JAVA_LANG_STRING)) {
-        if (arguments.length < 3) {
-          return;
-        }
-        argumentIndex = 1;
-      }
-      else {
-        if (arguments.length < 2) {
-          return;
-        }
-        argumentIndex = 0;
-      }
-      final PsiExpression expression1 = arguments[argumentIndex];
-      final PsiExpression expression2 = arguments[argumentIndex + 1];
-      final PsiType type1 = expression1.getType();
-      if (type1 == null) {
-        return;
-      }
-      final PsiType type2 = expression2.getType();
-      if (type2 == null) {
-        return;
-      }
-      final PsiType parameterType1 = parameters[argumentIndex].getType();
-      final PsiType parameterType2 = parameters[argumentIndex + 1].getType();
-      final PsiClassType objectType = TypeUtils.getObjectType(expression);
-      if (!objectType.equals(parameterType1) || !objectType.equals(parameterType2)) {
-        return;
-      }
-      if (TypeUtils.areConvertible(type1, type2)) {
-        return;
-      }
-      registerMethodCallError(expression, type1, type2);
     }
   }
 }
index 29285d53899562e2a2d3b30e5e8f391ca0196404..1aaed46c75acb18bfc707655bcea8e87db762ea7 100644 (file)
  */
 package com.siyeh.ig.junit;
 
-import com.intellij.codeInspection.ProblemDescriptor;
-import com.intellij.openapi.project.Project;
 import com.intellij.psi.*;
-import com.intellij.psi.util.InheritanceUtil;
-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.PsiReplacementUtil;
-import com.siyeh.ig.psiutils.ImportUtils;
 import org.jetbrains.annotations.Nls;
-import org.jetbrains.annotations.NonNls;
 import org.jetbrains.annotations.NotNull;
 
 public class AssertEqualsCalledOnArrayInspection extends BaseInspection {
+
   @Nls
   @NotNull
   @Override
@@ -46,33 +40,7 @@ public class AssertEqualsCalledOnArrayInspection extends BaseInspection {
 
   @Override
   protected InspectionGadgetsFix buildFix(Object... infos) {
-    return new AssertEqualsCalledOnArrayFix();
-  }
-
-  private static class AssertEqualsCalledOnArrayFix extends InspectionGadgetsFix {
-
-    @Override
-    @NotNull
-    public String getFamilyName() {
-      return InspectionGadgetsBundle.message("assertequals.called.on.arrays.quickfix");
-    }
-
-    @Override
-    protected void doFix(Project project, ProblemDescriptor descriptor) throws IncorrectOperationException {
-      final PsiElement methodNameIdentifier = descriptor.getPsiElement();
-      final PsiElement parent = methodNameIdentifier.getParent();
-      if (!(parent instanceof PsiReferenceExpression)) {
-        return;
-      }
-      final PsiReferenceExpression methodExpression = (PsiReferenceExpression)parent;
-      final PsiExpression qualifier = methodExpression.getQualifierExpression();
-      if (qualifier == null && ImportUtils.addStaticImport("org.junit.Assert", "assertArrayEquals", methodExpression)) {
-        PsiReplacementUtil.replaceExpression(methodExpression, "assertArrayEquals");
-      }
-      else {
-        PsiReplacementUtil.replaceExpression(methodExpression, "org.junit.Assert.assertArrayEquals");
-      }
-    }
+    return new ReplaceAssertEqualsFix("assertArrayEquals");
   }
 
   @Override
@@ -85,43 +53,18 @@ public class AssertEqualsCalledOnArrayInspection extends BaseInspection {
     @Override
     public void visitMethodCallExpression(PsiMethodCallExpression expression) {
       super.visitMethodCallExpression(expression);
-      final PsiReferenceExpression methodExpression = expression.getMethodExpression();
-      @NonNls final String methodName = methodExpression.getReferenceName();
-      if (!"assertEquals".equals(methodName)) {
+      final AssertEqualsHint assertEqualsHint = AssertEqualsHint.create(expression);
+      if (assertEqualsHint == null) {
         return;
       }
       final PsiExpressionList argumentList = expression.getArgumentList();
       final PsiExpression[] arguments = argumentList.getExpressions();
-      final PsiType type1;
-      final PsiType type2;
-      if (arguments.length == 2) {
-        final PsiExpression argument0 = arguments[0];
-        type1 = argument0.getType();
-        final PsiExpression argument1 = arguments[1];
-        type2 = argument1.getType();
-      }
-      else if (arguments.length == 3) {
-        final PsiExpression argument0 = arguments[1];
-        type1 = argument0.getType();
-        final PsiExpression argument1 = arguments[2];
-        type2 = argument1.getType();
-      }
-      else {
-        return;
-      }
+      final int argIndex = assertEqualsHint.getArgIndex();
+      final PsiType type1 = arguments[argIndex].getType();
+      final PsiType type2 = arguments[argIndex + 1].getType();
       if (!(type1 instanceof PsiArrayType) || !(type2 instanceof PsiArrayType)) {
         return;
       }
-      final PsiMethod method = expression.resolveMethod();
-      if (method == null) {
-        return;
-      }
-      final PsiClass containingClass = method.getContainingClass();
-      if (!InheritanceUtil.isInheritor(containingClass, "junit.framework.Assert") &&
-          !InheritanceUtil.isInheritor(containingClass, "org.junit.Assert") &&
-          !InheritanceUtil.isInheritor(containingClass, "org.testng.AssertJUnit")) {
-        return;
-      }
       registerMethodCallError(expression);
     }
   }
diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/junit/AssertEqualsHint.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/junit/AssertEqualsHint.java
new file mode 100644 (file)
index 0000000..3479e2f
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * 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.siyeh.ig.junit;
+
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.psi.*;
+import com.intellij.psi.util.InheritanceUtil;
+import com.siyeh.InspectionGadgetsBundle;
+import com.siyeh.ig.psiutils.TypeUtils;
+import org.jetbrains.annotations.NonNls;
+
+public class AssertEqualsHint {
+  private final int myArgIndex;
+  private final PsiMethod myMethod;
+
+  private AssertEqualsHint(int index, PsiMethod method) {
+    myArgIndex = index;
+    myMethod = method;
+  }
+
+  public int getArgIndex() {
+    return myArgIndex;
+  }
+
+  public PsiMethod getMethod() {
+    return myMethod;
+  }
+
+  public static AssertEqualsHint create(PsiMethodCallExpression expression) {
+    final PsiReferenceExpression methodExpression = expression.getMethodExpression();
+    @NonNls final String methodName = methodExpression.getReferenceName();
+    if (!"assertEquals".equals(methodName)) {
+      return null;
+    }
+    final PsiMethod method = expression.resolveMethod();
+    if (method == null) {
+      return null;
+    }
+    final PsiClass containingClass = method.getContainingClass();
+    final boolean messageOnLastPosition = InheritanceUtil.isInheritor(containingClass, JUnitCommonClassNames.ORG_JUNIT_JUPITER_API_ASSERTIONS) ||
+                                          InheritanceUtil.isInheritor(containingClass, "org.testng.Assert");
+    if (!InheritanceUtil.isInheritor(containingClass, JUnitCommonClassNames.JUNIT_FRAMEWORK_ASSERT) &&
+        !InheritanceUtil.isInheritor(containingClass, JUnitCommonClassNames.ORG_JUNIT_ASSERT) &&
+        !InheritanceUtil.isInheritor(containingClass, JUnitCommonClassNames.JUNIT_FRAMEWORK_TEST_CASE) &&
+        !InheritanceUtil.isInheritor(containingClass, "org.testng.AssertJUnit") &&
+        !messageOnLastPosition) {
+      return null;
+    }
+    final PsiParameterList parameterList = method.getParameterList();
+    final PsiParameter[] parameters = parameterList.getParameters();
+    if (parameters.length < 2) {
+      return null;
+    }
+    final PsiType firstParameterType = parameters[0].getType();
+    final PsiExpressionList argumentList = expression.getArgumentList();
+    final PsiExpression[] arguments = argumentList.getExpressions();
+    final int argumentIndex;
+    if (!messageOnLastPosition && firstParameterType.equalsToText(CommonClassNames.JAVA_LANG_STRING)) {
+      if (arguments.length < 3) {
+        return null;
+      }
+      argumentIndex = 1;
+    }
+    else {
+      if (arguments.length < 2) {
+        return null;
+      }
+      argumentIndex = 0;
+    }
+    return new AssertEqualsHint(argumentIndex, method);
+  }
+
+  public static String areExpectedActualTypesCompatible(PsiMethodCallExpression expression) {
+    final AssertEqualsHint assertEqualsHint = create(expression);
+    if (assertEqualsHint == null) return null;
+    final PsiExpression[] arguments = expression.getArgumentList().getExpressions();
+    final int argIndex = assertEqualsHint.getArgIndex();
+    final PsiType type1 = arguments[argIndex].getType();
+    if (type1 == null) {
+      return null;
+    }
+    final PsiType type2 = arguments[argIndex + 1].getType();
+    if (type2 == null) {
+      return null;
+    }
+    final PsiParameter[] parameters = assertEqualsHint.getMethod().getParameterList().getParameters();
+    final PsiType parameterType1 = parameters[argIndex].getType();
+    final PsiType parameterType2 = parameters[argIndex + 1].getType();
+    final PsiClassType objectType = TypeUtils.getObjectType(expression);
+    if (!objectType.equals(parameterType1) || !objectType.equals(parameterType2)) {
+      return null;
+    }
+    if (TypeUtils.areConvertible(type1, type2)) {
+      return null;
+    }
+    final String comparedTypeText = type1.getPresentableText();
+    final String comparisonTypeText = type2.getPresentableText();
+    return InspectionGadgetsBundle.message("assertequals.between.inconvertible.types.problem.descriptor",
+                                           StringUtil.escapeXml(comparedTypeText),
+                                           StringUtil.escapeXml(comparisonTypeText));
+  }
+}
index 70ad1c28657c4e552fe52e3c528a2467270beac7..c0e46bc62a54191c5e341530763206cfc9e63e68 100644 (file)
  */
 package com.siyeh.ig.junit;
 
-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.BaseInspection;
 import com.siyeh.ig.BaseInspectionVisitor;
 import com.siyeh.ig.InspectionGadgetsFix;
-import com.siyeh.ig.PsiReplacementUtil;
-import com.siyeh.ig.psiutils.ImportUtils;
-import org.jetbrains.annotations.NonNls;
 import org.jetbrains.annotations.NotNull;
 
 public class AssertEqualsMayBeAssertSameInspection extends BaseInspection {
@@ -44,50 +38,7 @@ public class AssertEqualsMayBeAssertSameInspection extends BaseInspection {
 
   @Override
   protected InspectionGadgetsFix buildFix(Object... infos) {
-    return new AssertEqualsMayBeAssertSameFix();
-  }
-
-  private static class AssertEqualsMayBeAssertSameFix extends InspectionGadgetsFix {
-
-    @Override
-    @NotNull
-    public String getFamilyName() {
-      return InspectionGadgetsBundle.message("assertequals.may.be.assertsame.quickfix");
-    }
-
-    @Override
-    protected void doFix(Project project, ProblemDescriptor descriptor) throws IncorrectOperationException {
-      final PsiElement element = descriptor.getPsiElement();
-      final PsiElement parent = element.getParent();
-      if (!(parent instanceof PsiReferenceExpression)) {
-        return;
-      }
-      final PsiReferenceExpression methodExpression = (PsiReferenceExpression)parent;
-      final PsiElement grandParent = methodExpression.getParent();
-      if (!(grandParent instanceof PsiMethodCallExpression)) {
-        return;
-      }
-      final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)grandParent;
-      final PsiMethod method = methodCallExpression.resolveMethod();
-      if (method == null) {
-        return;
-      }
-      final PsiClass containingClass = method.getContainingClass();
-      if (containingClass ==  null) {
-        return;
-      }
-      final String className = containingClass.getQualifiedName();
-      if (className == null) {
-        return;
-      }
-      final PsiExpression qualifier = methodExpression.getQualifierExpression();
-      if (qualifier == null && ImportUtils.addStaticImport(className, "assertSame", methodExpression)) {
-        PsiReplacementUtil.replaceExpression(methodExpression, "assertSame");
-      }
-      else {
-        PsiReplacementUtil.replaceExpression(methodExpression, className + ".assertSame");
-      }
-    }
+    return new ReplaceAssertEqualsFix("assertSame");
   }
 
   @Override
@@ -100,9 +51,8 @@ public class AssertEqualsMayBeAssertSameInspection extends BaseInspection {
     @Override
     public void visitMethodCallExpression(PsiMethodCallExpression expression) {
       super.visitMethodCallExpression(expression);
-      final PsiReferenceExpression methodExpression = expression.getMethodExpression();
-      @NonNls final String name = methodExpression.getReferenceName();
-      if (!"assertEquals".equals(name)) {
+      final AssertEqualsHint assertEqualsHint = AssertEqualsHint.create(expression);
+      if (assertEqualsHint == null) {
         return;
       }
       final PsiExpressionList argumentList = expression.getArgumentList();
@@ -110,24 +60,12 @@ public class AssertEqualsMayBeAssertSameInspection extends BaseInspection {
       if (arguments.length != 3 && arguments.length != 2) {
         return;
       }
-      final PsiMethod method = expression.resolveMethod();
-      if (method == null) {
-        return;
-      }
-      final PsiClass aClass = method.getContainingClass();
-      if (aClass == null) {
-        return;
-      }
-      final String qualifiedName = aClass.getQualifiedName();
-      if (!"org.junit.Assert".equals(qualifiedName) && !"junit.framework.Assert".equals(qualifiedName) &&
-          !"junit.framework.TestCase".equals(qualifiedName)) {
-        return;
-      }
-      final PsiExpression argument1 = arguments[arguments.length - 2];
+      final int argIndex = assertEqualsHint.getArgIndex();
+      final PsiExpression argument1 = arguments[argIndex];
       if (!couldBeAssertSameArgument(argument1)) {
         return;
       }
-      final PsiExpression argument2 = arguments[arguments.length - 1];
+      final PsiExpression argument2 = arguments[argIndex + 1];
       if (!couldBeAssertSameArgument(argument2)) {
         return;
       }
index 9a928a8fd2c3c262d411aeb7a43ef832dc4d31cb..87d5ed2a37fb4eef3affca660018a560a2c071d8 100644 (file)
@@ -82,8 +82,8 @@ public class AssertsWithoutMessagesInspection extends BaseInspection {
         return;
       }
       final PsiClass containingClass = method.getContainingClass();
-      if (!InheritanceUtil.isInheritor(containingClass, "junit.framework.Assert") &&
-          !InheritanceUtil.isInheritor(containingClass, "org.junit.Assert")) {
+      if (!InheritanceUtil.isInheritor(containingClass, JUnitCommonClassNames.JUNIT_FRAMEWORK_ASSERT) &&
+          !InheritanceUtil.isInheritor(containingClass, JUnitCommonClassNames.ORG_JUNIT_ASSERT)) {
         return;
       }
       final PsiParameterList parameterList = method.getParameterList();
index 8e4b25a0b284040d9cbeaf0378ff70728226e435..e02bfb7bc0fdc5a2459afd024dfd6ed9f45bc6e2 100644 (file)
@@ -79,9 +79,9 @@ public class ConstantJUnitAssertArgumentInspection extends BaseInspection {
       }
       final PsiClass containingClass = method.getContainingClass();
       if (!InheritanceUtil.isInheritor(containingClass,
-                                       "junit.framework.Assert") &&
+                                       JUnitCommonClassNames.JUNIT_FRAMEWORK_ASSERT) &&
           !InheritanceUtil.isInheritor(containingClass,
-                                       "org.junit.Assert")) {
+                                       JUnitCommonClassNames.ORG_JUNIT_ASSERT)) {
         return;
       }
       final PsiExpressionList argumentList = expression.getArgumentList();
index 448d826409ef26151aa54130914032c593f3ef3b..a2512b6398ba79a39f96efca08bac4c0df31fb40 100644 (file)
@@ -83,7 +83,7 @@ public class JUnitAbstractTestClassNamingConventionInspectionBase extends Conven
         return;
       }
       if (!InheritanceUtil.isInheritor(aClass,
-                                       "junit.framework.TestCase")) {
+                                       JUnitCommonClassNames.JUNIT_FRAMEWORK_TEST_CASE)) {
         return;
       }
       final String name = aClass.getName();
diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/junit/JUnitCommonClassNames.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/junit/JUnitCommonClassNames.java
new file mode 100644 (file)
index 0000000..66c3cf4
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * 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.siyeh.ig.junit;
+
+public class JUnitCommonClassNames {
+  public static final String ORG_JUNIT_ASSERT = "org.junit.Assert";
+  public static final String JUNIT_FRAMEWORK_ASSERT = "junit.framework.Assert";
+  public static final String ORG_JUNIT_JUPITER_API_ASSERTIONS = "org.junit.jupiter.api.Assertions";
+  public static final String JUNIT_FRAMEWORK_TEST_CASE = "junit.framework.TestCase";
+}
index 3d7a04f7a53af8f836b076b513f4439bed607407..d44126fd027c3bf9cfd8503b385d3df2ecc8b025 100644 (file)
@@ -83,7 +83,7 @@ public class JUnitTestClassNamingConventionInspectionBase extends ConventionInsp
         return;
       }
       if (!InheritanceUtil.isInheritor(aClass,
-                                       "junit.framework.TestCase")) {
+                                       JUnitCommonClassNames.JUNIT_FRAMEWORK_TEST_CASE)) {
         if (!hasJUnit4TestMethods(aClass)) {
           return;
         }
index e9701710d578cee406b910873ab24f3fa124dcd3..be5f036e43b75e0c3e649b296f6ad665230d5986 100644 (file)
@@ -56,7 +56,7 @@ public class MalformedSetUpTearDownInspection extends BaseInspection {
         return;
       }
       final PsiClass targetClass = method.getContainingClass();
-      if (!InheritanceUtil.isInheritor(targetClass, "junit.framework.TestCase")) {
+      if (!InheritanceUtil.isInheritor(targetClass, JUnitCommonClassNames.JUNIT_FRAMEWORK_TEST_CASE)) {
         return;
       }
       if (method.getParameterList().getParametersCount() != 0 ||
index cafd4282d9795037d06efba9e9a19deaa5d41283..7f3cc208f61e15bc5e7901ccf2d859c0915636cd 100644 (file)
@@ -91,8 +91,8 @@ public abstract class MisorderedAssertEqualsArgumentsInspectionBase extends Base
         junit = false;
       }
       else if (InheritanceUtil.isInheritor(containingClass, "org.testng.AssertJUnit") ||
-               InheritanceUtil.isInheritor(containingClass, "junit.framework.Assert") ||
-               InheritanceUtil.isInheritor(containingClass, "org.junit.Assert")) {
+               InheritanceUtil.isInheritor(containingClass, JUnitCommonClassNames.JUNIT_FRAMEWORK_ASSERT) ||
+               InheritanceUtil.isInheritor(containingClass, JUnitCommonClassNames.ORG_JUNIT_ASSERT)) {
         junit = true;
       }
       else {
@@ -156,8 +156,8 @@ public abstract class MisorderedAssertEqualsArgumentsInspectionBase extends Base
       final PsiExpression actualArgument;
       if (checkTestNG() ?
           InheritanceUtil.isInheritor(containingClass, "org.testng.AssertJUnit") :
-          InheritanceUtil.isInheritor(containingClass, "junit.framework.Assert") ||
-          InheritanceUtil.isInheritor(containingClass, "org.junit.Assert")) {
+          InheritanceUtil.isInheritor(containingClass, JUnitCommonClassNames.JUNIT_FRAMEWORK_ASSERT) ||
+          InheritanceUtil.isInheritor(containingClass, JUnitCommonClassNames.ORG_JUNIT_ASSERT)) {
         final PsiType firstArgumentType = arguments[0].getType();
         if (stringType.equals(firstArgumentType) && arguments.length > 2) {
           expectedArgument = arguments[1];
diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/junit/ReplaceAssertEqualsFix.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/junit/ReplaceAssertEqualsFix.java
new file mode 100644 (file)
index 0000000..a91b115
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * 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.siyeh.ig.junit;
+
+import com.intellij.codeInspection.ProblemDescriptor;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.psi.*;
+import com.intellij.util.IncorrectOperationException;
+import com.siyeh.InspectionGadgetsBundle;
+import com.siyeh.ig.InspectionGadgetsFix;
+import com.siyeh.ig.PsiReplacementUtil;
+import com.siyeh.ig.psiutils.ImportUtils;
+import org.jetbrains.annotations.NotNull;
+
+class ReplaceAssertEqualsFix extends InspectionGadgetsFix {
+  private final String myMethodName;
+
+  public ReplaceAssertEqualsFix(String methodName) {
+    myMethodName = methodName;
+  }
+
+  @Override
+  @NotNull
+  public String getFamilyName() {
+    return InspectionGadgetsBundle.message("replace.assertequals.quickfix", myMethodName);
+  }
+
+  @Override
+  protected void doFix(Project project, ProblemDescriptor descriptor) throws IncorrectOperationException {
+    final PsiElement element = descriptor.getPsiElement();
+    final PsiElement parent = element.getParent();
+    if (!(parent instanceof PsiReferenceExpression)) {
+      return;
+    }
+    final PsiReferenceExpression methodExpression = (PsiReferenceExpression)parent;
+    final PsiElement grandParent = methodExpression.getParent();
+    if (!(grandParent instanceof PsiMethodCallExpression)) {
+      return;
+    }
+    final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)grandParent;
+    final PsiMethod method = methodCallExpression.resolveMethod();
+    if (method == null) {
+      return;
+    }
+    final PsiClass containingClass = method.getContainingClass();
+    if (containingClass ==  null) {
+      return;
+    }
+    final String className = containingClass.getQualifiedName();
+    if (className == null) {
+      return;
+    }
+    final PsiExpression qualifier = methodExpression.getQualifierExpression();
+    if (qualifier == null && ImportUtils.addStaticImport(className, myMethodName, methodExpression)) {
+      PsiReplacementUtil.replaceExpression(methodExpression, myMethodName);
+    }
+    else {
+      PsiReplacementUtil.replaceExpression(methodExpression, StringUtil.getQualifiedName(className, myMethodName));
+    }
+  }
+}
index fcab7d46a7b914dc094ea39b7bc85886545a3738..acda10bd2c9576c07c0c828d40a4ae9bc28ce5f1 100644 (file)
@@ -90,8 +90,8 @@ public class SimplifiableJUnitAssertionInspection extends BaseInspection {
           return;
         }
         final PsiClass containingClass = method.getContainingClass();
-        if (containingClass != null && "org.junit.Assert".equals(containingClass.getQualifiedName()) &&
-            !ImportUtils.addStaticImport("org.junit.Assert", methodName, originalMethodCall)) {
+        if (containingClass != null && JUnitCommonClassNames.ORG_JUNIT_ASSERT.equals(containingClass.getQualifiedName()) &&
+            !ImportUtils.addStaticImport(JUnitCommonClassNames.ORG_JUNIT_ASSERT, methodName, originalMethodCall)) {
           // add qualifier if old call was to JUnit4 method and adding static import failed
           out.append("org.junit.Assert.");
         }
@@ -721,7 +721,8 @@ public class SimplifiableJUnitAssertionInspection extends BaseInspection {
       return false;
     }
     final String qualifiedName = targetClass.getQualifiedName();
-    return "junit.framework.Assert".equals(qualifiedName) || "junit.framework.TestCase".equals(qualifiedName) ||
-           "org.junit.Assert".equals(qualifiedName);
+    return JUnitCommonClassNames.JUNIT_FRAMEWORK_ASSERT.equals(qualifiedName) || JUnitCommonClassNames.JUNIT_FRAMEWORK_TEST_CASE
+      .equals(qualifiedName) ||
+           JUnitCommonClassNames.ORG_JUNIT_ASSERT.equals(qualifiedName);
   }
 }
index acd8a2415af07f304d6880290f790ec7f77c2658..7bb52b0c38bb016040b928b27d1c9ed524331975 100644 (file)
@@ -66,7 +66,7 @@ public class StaticSuiteInspection extends BaseInspection {
         return;
       }
       if (!InheritanceUtil.isInheritor(aClass,
-                                       "junit.framework.TestCase")) {
+                                       JUnitCommonClassNames.JUNIT_FRAMEWORK_TEST_CASE)) {
         return;
       }
       final PsiParameterList parameterList = method.getParameterList();
index a52477f6a55dddb86a56f6fecbfbe9459605cb15..6dd155c9385e571e081536b70cddac585bf1df3f 100644 (file)
@@ -59,7 +59,7 @@ public class SuperTearDownInFinallyInspection extends BaseInspection {
         return;
       }
       final PsiClass containingClass = method.getContainingClass();
-      if (!InheritanceUtil.isInheritor(containingClass, "junit.framework.TestCase")) {
+      if (!InheritanceUtil.isInheritor(containingClass, JUnitCommonClassNames.JUNIT_FRAMEWORK_TEST_CASE)) {
         return;
       }
       final PsiTryStatement tryStatement =
index 4c82c3e1f107f3155942e3c58d0c819d4a566045..6c69b811c1dcb33f8a888db0fb2e97c753318f64 100644 (file)
@@ -84,7 +84,7 @@ public class TestCaseWithNoTestMethodsInspection extends BaseInspection {
         return;
       }
       if (!InheritanceUtil.isInheritor(aClass,
-                                       "junit.framework.TestCase")) {
+                                       JUnitCommonClassNames.JUNIT_FRAMEWORK_TEST_CASE)) {
         return;
       }
       final PsiMethod[] methods = aClass.getMethods();
index 62bb0b940c861709890dc64b89f0acfa2e312b56..f794e08a9c083e33d8c2bb54c189f46ac572059a 100644 (file)
@@ -35,8 +35,8 @@ public class TestMethodWithoutAssertionInspectionBase extends BaseInspection {
 
   public TestMethodWithoutAssertionInspectionBase() {
     methodMatcher = new MethodMatcher(true, "assertionMethods")
-      .add("org.junit.Assert", "assert.*|fail.*")
-      .add("junit.framework.Assert", "assert.*|fail.*")
+      .add(JUnitCommonClassNames.ORG_JUNIT_ASSERT, "assert.*|fail.*")
+      .add(JUnitCommonClassNames.JUNIT_FRAMEWORK_ASSERT, "assert.*|fail.*")
       .add("org.mockito.Mockito", "verify.*")
       .add("org.mockito.InOrder", "verify")
       .add("org.junit.rules.ExpectedException", "expect.*")
index e64db5b5e56f9c87e4325e99f2a0af4550701e27..01fc3412f67b9b53098e579055b5de39297a8114 100644 (file)
@@ -67,7 +67,7 @@ public class UseOfObsoleteAssertInspection extends BaseInspection {
         return;
       }
       final PsiClass newAssertClass = JavaPsiFacade.getInstance(project)
-        .findClass("org.junit.Assert", GlobalSearchScope.moduleWithDependenciesAndLibrariesScope(module));
+        .findClass(JUnitCommonClassNames.ORG_JUNIT_ASSERT, GlobalSearchScope.moduleWithDependenciesAndLibrariesScope(module));
       if (newAssertClass == null) {
         return;
       }
@@ -80,7 +80,7 @@ public class UseOfObsoleteAssertInspection extends BaseInspection {
         return;
       }
       final String name = containingClass.getQualifiedName();
-      if ("junit.framework.Assert".equals(name) || "junit.framework.TestCase".equals(name)) {
+      if (JUnitCommonClassNames.JUNIT_FRAMEWORK_ASSERT.equals(name) || JUnitCommonClassNames.JUNIT_FRAMEWORK_TEST_CASE.equals(name)) {
         registerMethodCallError(expression, name);
       }
     }
@@ -94,9 +94,9 @@ public class UseOfObsoleteAssertInspection extends BaseInspection {
         return;
       }
       final PsiClass newAssertClass =
-        JavaPsiFacade.getInstance(project).findClass("org.junit.Assert", GlobalSearchScope.allScope(project));
+        JavaPsiFacade.getInstance(project).findClass(JUnitCommonClassNames.ORG_JUNIT_ASSERT, GlobalSearchScope.allScope(project));
       final PsiClass oldAssertClass =
-        JavaPsiFacade.getInstance(project).findClass("junit.framework.Assert", GlobalSearchScope.allScope(project));
+        JavaPsiFacade.getInstance(project).findClass(JUnitCommonClassNames.JUNIT_FRAMEWORK_ASSERT, GlobalSearchScope.allScope(project));
 
       if (newAssertClass == null) {
         return;
index 1020bb1f9ef414c4a6038690aa0fb8eec49750f4..eadaa038c86e0e0c494b5ddfdf03c9018723e7ca 100644 (file)
@@ -24,6 +24,7 @@ import com.intellij.openapi.vfs.VirtualFile;
 import com.intellij.psi.*;
 import com.intellij.psi.util.InheritanceUtil;
 import com.intellij.psi.util.PsiTreeUtil;
+import com.siyeh.ig.junit.JUnitCommonClassNames;
 import org.jetbrains.annotations.NonNls;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
@@ -99,7 +100,7 @@ public class TestUtils {
   }
 
   public static boolean isJUnitTestClass(@Nullable PsiClass targetClass) {
-    return targetClass != null && InheritanceUtil.isInheritor(targetClass, "junit.framework.TestCase");
+    return targetClass != null && InheritanceUtil.isInheritor(targetClass, JUnitCommonClassNames.JUNIT_FRAMEWORK_TEST_CASE);
   }
 
   public static boolean isJUnit4TestClass(@Nullable PsiClass aClass, boolean runWithIsTestClass) {
diff --git a/plugins/InspectionGadgets/test/com/siyeh/igfixes/junit/assertEqualsOnArray/assertEqualsForJunit5.after.java b/plugins/InspectionGadgets/test/com/siyeh/igfixes/junit/assertEqualsOnArray/assertEqualsForJunit5.after.java
new file mode 100644 (file)
index 0000000..e1790b5
--- /dev/null
@@ -0,0 +1,7 @@
+class MyTest {
+  void myTest(){
+    Object[] a = {};
+    Object[] e = {""};
+      org.junit.jupiter.api.Assertions.assertArrayEquals(a, e, "message");
+  }
+}
\ No newline at end of file
diff --git a/plugins/InspectionGadgets/test/com/siyeh/igfixes/junit/assertEqualsOnArray/assertEqualsForJunit5.java b/plugins/InspectionGadgets/test/com/siyeh/igfixes/junit/assertEqualsOnArray/assertEqualsForJunit5.java
new file mode 100644 (file)
index 0000000..fdb1e75
--- /dev/null
@@ -0,0 +1,7 @@
+class MyTest {
+  void myTest(){
+    Object[] a = {};
+    Object[] e = {""};
+    org.junit.jupiter.api.Assertions.assert<caret>Equals(a, e, "message");
+  }
+}
\ No newline at end of file
diff --git a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/junit/AssertEqualsCalledOnArrayInspectionTest.java b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/junit/AssertEqualsCalledOnArrayInspectionTest.java
new file mode 100644 (file)
index 0000000..669f07c
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * 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.siyeh.ig.fixes.junit;
+
+import com.siyeh.InspectionGadgetsBundle;
+import com.siyeh.ig.IGQuickFixesTestCase;
+import com.siyeh.ig.junit.AssertEqualsCalledOnArrayInspection;
+import com.siyeh.ig.junit.UseOfObsoleteAssertInspection;
+
+public class AssertEqualsCalledOnArrayInspectionTest extends IGQuickFixesTestCase {
+  @Override
+  public void setUp() throws Exception {
+    super.setUp();
+
+
+    myFixture.addClass("package org.junit.jupiter.api;\n" +
+                       "import java.util.function.Supplier;\n" +
+                       "public final class Assertions {\n" +
+                       "    public static void assertArrayEquals(Object[] expected, Object[] actual) {}\n" +
+                       "    public static void assertArrayEquals(Object[] expected, Object[] actual, String message) {}\n" +
+                       "    public static void assertEquals(Object expected, Object actual) {}\n" +
+                       "    public static void assertEquals(Object expected, Object actual, String message) {}\n" +
+                       "}");
+
+    myFixture.enableInspections(new AssertEqualsCalledOnArrayInspection());
+  }
+
+  public void testAssertEqualsForJunit5() {
+    doFixTest();
+  }
+
+  private void doFixTest() {
+    doTest(getTestName(true), InspectionGadgetsBundle.message("replace.assertequals.quickfix", "assertArrayEquals"));
+  }
+
+  @Override
+  protected String getRelativePath() {
+    return "junit/assertEqualsOnArray";
+  }
+}
index 05c92ded31961cbd4ae0731febf7605f6f488299..d36b527e5af03bbcf10469641e97f44b584368b8 100644 (file)
 
     <moduleService serviceImplementation="org.jetbrains.idea.devkit.build.PluginBuildConfiguration"/>
     <generatedSourcesFilter implementation="org.jetbrains.idea.devkit.internal.IconsGeneratedSourcesFilter"/>
+
+    <!-- must be before SubstringConsoleFolding to be able to fold the entire stacktrace -->
+    <console.folding order="first" implementation="com.intellij.testFramework.FailedTestDebugLogConsoleFolding"/>
   </extensions>
 
   <project-components>
index 49408103f50f000d8cc39980c40c467e1bb720ad..a3de593134f661b5d0a41eab708d16117de7ab31 100644 (file)
@@ -21,6 +21,7 @@ import com.intellij.openapi.diagnostic.Logger;
 import com.intellij.psi.PsiAnnotation;
 import com.intellij.psi.PsiNameValuePair;
 import com.intellij.psi.StubBasedPsiElement;
+import com.intellij.psi.stubs.EmptyStub;
 import com.intellij.util.IncorrectOperationException;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes;
@@ -30,14 +31,13 @@ import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory;
 import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.annotation.GrAnnotationArgumentList;
 import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.annotation.GrAnnotationNameValuePair;
 import org.jetbrains.plugins.groovy.lang.psi.impl.GrStubElementBase;
-import org.jetbrains.plugins.groovy.lang.psi.stubs.GrAnnotationArgumentListStub;
 
-public class GrAnnotationArgumentListImpl extends GrStubElementBase<GrAnnotationArgumentListStub>
-  implements GrAnnotationArgumentList, StubBasedPsiElement<GrAnnotationArgumentListStub> {
+public class GrAnnotationArgumentListImpl extends GrStubElementBase<EmptyStub>
+  implements GrAnnotationArgumentList, StubBasedPsiElement<EmptyStub> {
 
   private static final Logger LOG = Logger.getInstance(GrAnnotationArgumentListImpl.class);
 
-  public GrAnnotationArgumentListImpl(@NotNull GrAnnotationArgumentListStub stub) {
+  public GrAnnotationArgumentListImpl(@NotNull EmptyStub stub) {
     super(stub, GroovyElementTypes.ANNOTATION_ARGUMENTS);
   }
 
index a549bbf1cf95ca2f18f3fdbfbea134b4b48e1e65..ad347d0a543299308e1720175cf1dc3a6b303a42 100644 (file)
@@ -21,6 +21,7 @@ import com.intellij.openapi.util.TextRange;
 import com.intellij.psi.*;
 import com.intellij.psi.tree.IElementType;
 import com.intellij.reference.SoftReference;
+import com.intellij.testFramework.LightVirtualFile;
 import com.intellij.util.ArrayUtilRt;
 import com.intellij.util.IncorrectOperationException;
 import com.intellij.util.containers.ContainerUtilRt;
@@ -112,6 +113,7 @@ public class GrAnnotationNameValuePairImpl extends GrStubElementBase<GrNameValue
         GrAnnotation annotation = GroovyPsiElementFactory.getInstance(getProject()).createAnnotationFromText(
           "@F(" + text + ")", this
         );
+        ((LightVirtualFile)annotation.getContainingFile().getViewProvider().getVirtualFile()).setWritable(false);
         PsiAnnotationMemberValue value = annotation.findAttributeValue(null);
         myDetachedValue = new SoftReference<>(result = value);
       }
index 866bdb0f812dfc41d3d4342d852a19ccc6bbbbfe..f5231c5fec571ea73956760f9cec640857c72807 100644 (file)
  */
 package org.jetbrains.plugins.groovy.lang.psi.stubs.elements;
 
-import com.intellij.psi.stubs.StubElement;
-import com.intellij.psi.stubs.StubInputStream;
-import com.intellij.psi.stubs.StubOutputStream;
+import com.intellij.psi.stubs.EmptyStub;
+import com.intellij.psi.stubs.EmptyStubElementType;
 import org.jetbrains.annotations.NotNull;
+import org.jetbrains.plugins.groovy.GroovyLanguage;
 import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.annotation.GrAnnotationArgumentList;
 import org.jetbrains.plugins.groovy.lang.psi.impl.auxiliary.annotation.GrAnnotationArgumentListImpl;
-import org.jetbrains.plugins.groovy.lang.psi.stubs.GrAnnotationArgumentListStub;
 
-import java.io.IOException;
-
-public class GrAnnotationArgumentListElementType extends GrStubElementType<GrAnnotationArgumentListStub, GrAnnotationArgumentList> {
+public class GrAnnotationArgumentListElementType extends EmptyStubElementType<GrAnnotationArgumentList> {
 
   public GrAnnotationArgumentListElementType() {
-    super("annotation arguments");
+    super("annotation arguments", GroovyLanguage.INSTANCE);
   }
 
   @Override
@@ -37,23 +34,7 @@ public class GrAnnotationArgumentListElementType extends GrStubElementType<GrAnn
   }
 
   @Override
-  public void serialize(@NotNull GrAnnotationArgumentListStub stub, @NotNull StubOutputStream dataStream) throws IOException {
-  }
-
-  @NotNull
-  @Override
-  public GrAnnotationArgumentListStub deserialize(@NotNull StubInputStream dataStream, StubElement parentStub) throws IOException {
-    return new GrAnnotationArgumentListStub(parentStub);
-  }
-
-  @Override
-  public GrAnnotationArgumentList createPsi(@NotNull GrAnnotationArgumentListStub stub) {
+  public GrAnnotationArgumentList createPsi(@NotNull EmptyStub stub) {
     return new GrAnnotationArgumentListImpl(stub);
   }
-
-  @NotNull
-  @Override
-  public GrAnnotationArgumentListStub createStub(@NotNull GrAnnotationArgumentList psi, StubElement parentStub) {
-    return new GrAnnotationArgumentListStub(parentStub);
-  }
 }
index 829e6d02c2e55f593aa192dc95cab1b86540c8dc..ff40490b581439434f874142755958d220b5ae86 100644 (file)
@@ -40,7 +40,7 @@ import java.io.IOException;
  * @author ilyas
  */
 public class GrStubFileElementType extends IStubFileElementType<GrFileStub> {
-  public static final int STUB_VERSION = 31;
+  public static final int STUB_VERSION = 32;
 
   public GrStubFileElementType(Language language) {
     super(language);
index 2508e5c57e4ba2c69fd3bf5d3f1d0d3fd3758998..4b97dcc8b35d9fa4b39c0c908d4b3ea84127eda2 100644 (file)
@@ -17,13 +17,8 @@ package org.jetbrains.plugins.groovy.lang.psi.stubs
 
 import com.intellij.psi.stubs.StubBase
 import com.intellij.psi.stubs.StubElement
-import org.jetbrains.plugins.groovy.lang.parser.GroovyElementTypes.ANNOTATION_ARGUMENTS
 import org.jetbrains.plugins.groovy.lang.parser.GroovyElementTypes.ANNOTATION_MEMBER_VALUE_PAIR
-import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.annotation.GrAnnotationArgumentList
 import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.annotation.GrAnnotationNameValuePair
 
 class GrNameValuePairStub(parent: StubElement<*>?, val name: String?, val value: String?)
 : StubBase<GrAnnotationNameValuePair>(parent, ANNOTATION_MEMBER_VALUE_PAIR)
-
-class GrAnnotationArgumentListStub(parent: StubElement<*>?)
-: StubBase<GrAnnotationArgumentList>(parent, ANNOTATION_ARGUMENTS)
index 74c2f7a4c9a96d617105949308b64eaf87f119e5..0e9f9b42af0f659ced8f3b35c7e3897d0bef1b03 100644 (file)
@@ -17,12 +17,11 @@ package com.theoryinpractice.testng.inspection;
 
 import com.intellij.codeInspection.BaseJavaBatchLocalInspectionTool;
 import com.intellij.codeInspection.ProblemsHolder;
-import com.intellij.openapi.util.text.StringUtil;
-import com.intellij.psi.*;
-import com.intellij.psi.search.GlobalSearchScope;
-import com.intellij.psi.util.InheritanceUtil;
-import com.siyeh.ig.psiutils.TypeUtils;
-import org.jetbrains.annotations.NonNls;
+import com.intellij.psi.JavaElementVisitor;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiElementVisitor;
+import com.intellij.psi.PsiMethodCallExpression;
+import com.siyeh.ig.junit.AssertEqualsHint;
 import org.jetbrains.annotations.NotNull;
 
 /**
@@ -32,95 +31,20 @@ public class AssertEqualsBetweenInconvertibleTypesTestNGInspection extends BaseJ
   @NotNull
   @Override
   public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
-    return new AssertEqualsBetweenInconvertibleTypesVisitor(holder);
-  }
-
-  private static class AssertEqualsBetweenInconvertibleTypesVisitor extends JavaElementVisitor {
-
-    private final ProblemsHolder myProblemsHolder;
+    return new JavaElementVisitor() {
+      @Override
+      public void visitMethodCallExpression(@NotNull PsiMethodCallExpression expression) {
+        super.visitMethodCallExpression(expression);
+        final String errorMessage = AssertEqualsHint.areExpectedActualTypesCompatible(expression);
+        if (errorMessage != null) {
+          final PsiElement referenceNameElement = expression.getMethodExpression().getReferenceNameElement();
+          if (referenceNameElement == null) {
+            return;
+          }
 
-    public AssertEqualsBetweenInconvertibleTypesVisitor(ProblemsHolder problemsHolder) {
-      myProblemsHolder = problemsHolder;
-    }
-
-    @Override
-    public void visitMethodCallExpression(@NotNull PsiMethodCallExpression expression) {
-      super.visitMethodCallExpression(expression);
-      final PsiReferenceExpression methodExpression = expression.getMethodExpression();
-      @NonNls final String methodName = methodExpression.getReferenceName();
-      if (!"assertEquals".equals(methodName)) {
-        return;
-      }
-      final PsiMethod method = expression.resolveMethod();
-      if (method == null) {
-        return;
-      }
-      final PsiClass containingClass = method.getContainingClass();
-      final boolean junit;
-      if (InheritanceUtil.isInheritor(containingClass, "org.testng.Assert")) {
-        junit = false;
-      }
-      else if (InheritanceUtil.isInheritor(containingClass, "org.testng.AssertJUnit")) {
-        junit = true;
-      }
-      else {
-        return;
-      }
-      final PsiParameterList parameterList = method.getParameterList();
-      final PsiParameter[] parameters = parameterList.getParameters();
-      if (parameters.length < 2) {
-        return;
-      }
-      final PsiType firstParameterType = parameters[0].getType();
-      final PsiExpressionList argumentList = expression.getArgumentList();
-      final PsiExpression[] arguments = argumentList.getExpressions();
-      final PsiExpression expression1;
-      final PsiExpression expression2;
-      final PsiType parameterType1;
-      final PsiType parameterType2;
-      if (junit && firstParameterType.equalsToText(CommonClassNames.JAVA_LANG_STRING)) {
-        if (arguments.length < 3) {
-          return;
-        }
-        expression1 = arguments[1];
-        expression2 = arguments[2];
-        parameterType1 = parameters[1].getType();
-        parameterType2 = parameters[2].getType();
-      }
-      else {
-        if (arguments.length < 2) {
-          return;
+          holder.registerProblem(referenceNameElement, errorMessage);
         }
-        expression1 = arguments[0];
-        expression2 = arguments[1];
-        parameterType1 = parameters[0].getType();
-        parameterType2 = parameters[1].getType();
-      }
-      final PsiType type1 = expression1.getType();
-      if (type1 == null) {
-        return;
-      }
-      final PsiType type2 = expression2.getType();
-      if (type2 == null) {
-        return;
-      }
-      final PsiManager manager = expression.getManager();
-      final GlobalSearchScope scope = expression.getResolveScope();
-      final PsiClassType objectType = PsiType.getJavaLangObject(manager, scope);
-      if (!objectType.equals(parameterType1) || !objectType.equals(parameterType2)) {
-        return;
-      }
-      if (TypeUtils.areConvertible(type1, type2)) {
-        return;
-      }
-      final PsiElement referenceNameElement = methodExpression.getReferenceNameElement();
-      if (referenceNameElement == null) {
-        return;
       }
-      myProblemsHolder.registerProblem(referenceNameElement,
-                                       "<code>#ref()</code> between objects of inconvertible types '" +
-                                       StringUtil.escapeXml(type1.getPresentableText()) + "' and '" +
-                                       StringUtil.escapeXml(type2.getPresentableText()) + "' #loc");
-    }
+    };
   }
 }
\ No newline at end of file
index 48668c33e52bf77ffff4db87afd62da29ee7cb1d..74ddd2011eeec4049236abd0ab43aa28a7201887 100644 (file)
@@ -458,6 +458,12 @@ def create_fork(original_name):
         if not child_process:
             if is_new_python_process:
                 _on_forked_process()
+        else:
+            if is_new_python_process:
+                from _pydevd_bundle.pydevd_comm import get_global_debugger
+                debugger = get_global_debugger()
+                if debugger is not None:
+                    debugger.send_process_created_message()
         return child_process
     return new_fork
 
index 8276001789ac12768cb206cd54b2afd55a73f6b3..0d173bba8c22d2ff6226188bf500ce7e3afd04f6 100644 (file)
@@ -58,12 +58,14 @@ each command has a format:
     * PYDB - pydevd, the python end
 '''
 
+import os
+
 from _pydev_bundle.pydev_imports import _queue
 from _pydev_imps._pydev_saved_modules import time
 from _pydev_imps._pydev_saved_modules import thread
 from _pydev_imps._pydev_saved_modules import threading
 from _pydev_imps._pydev_saved_modules import socket
-from socket import socket, AF_INET, SOCK_STREAM, SHUT_RD, SHUT_WR
+from socket import socket, AF_INET, SOCK_STREAM, SHUT_RD, SHUT_WR, SOL_SOCKET, SO_REUSEADDR, SO_REUSEPORT, SHUT_RDWR, timeout
 from _pydevd_bundle.pydevd_constants import DebugInfoHolder, dict_contains, get_thread_id, IS_JYTHON, IS_PY2, IS_PY3K, STATE_RUN
 
 try:
@@ -143,6 +145,8 @@ CMD_SHOW_RETURN_VALUES = 146
 CMD_INPUT_REQUESTED = 147
 CMD_GET_DESCRIPTION = 148
 
+CMD_PROCESS_CREATED = 149
+
 CMD_VERSION = 501
 CMD_RETURN = 502
 CMD_ERROR = 901
@@ -199,6 +203,8 @@ ID_TO_MEANING = {
     '147': 'CMD_INPUT_REQUESTED',
     '148': 'CMD_GET_DESCRIPTION',
 
+    '149': 'CMD_PROCESS_CREATED',
+
     '501': 'CMD_VERSION',
     '502': 'CMD_RETURN',
     '901': 'CMD_ERROR',
@@ -479,10 +485,30 @@ class WriterThread(PyDBDaemonThread):
 def start_server(port):
     """ binds to a port, waits for the debugger to connect """
     s = socket(AF_INET, SOCK_STREAM)
+    s.settimeout(None)
+
+    if os.name == 'nt':
+        s.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
+    else:
+        s.setsockopt(SOL_SOCKET, SO_REUSEPORT, 1)
+
     s.bind(('', port))
-    s.listen(1)
-    newSock, _addr = s.accept()
-    return newSock
+    pydevd_log(1, "Bound to port ", str(port))
+
+    try:
+        s.listen(1)
+        newSock, _addr = s.accept()
+        pydevd_log(1, "Connection accepted")
+        # closing server socket is not necessary but we don't need it
+        s.shutdown(SHUT_RDWR)
+        s.close()
+        return newSock
+
+    except:
+        sys.stderr.write("Could not bind to port: %s\n" % (port,))
+        sys.stderr.flush()
+        traceback.print_exc()
+        sys.exit(1) #TODO: is it safe?
 
 #=======================================================================================================================
 # start_client
@@ -559,6 +585,9 @@ class NetCommandFactory:
         cmdText = "<xml>" + self._thread_to_xml(thread) + "</xml>"
         return NetCommand(CMD_THREAD_CREATE, 0, cmdText)
 
+    def make_process_created_message(self):
+        cmdText = '<process/>'
+        return NetCommand(CMD_PROCESS_CREATED, 0, cmdText)
 
     def make_custom_frame_created_message(self, frameId, frameDescription):
         frameDescription = pydevd_xml.make_valid_xml_value(frameDescription)
index 7e430f68351323b011312098a863dcd5896f2e50..ee42fb00294ac593ff0fe72d9a49a0534831ab2d 100644 (file)
@@ -682,6 +682,13 @@ class PyDB:
         self.process_internal_commands()
 
 
+    def send_process_created_message(self):
+        """Sends a message that a new process has been created.
+        """
+        cmd = self.cmd_factory.make_process_created_message()
+        self.writer.add_command(cmd)
+
+
     def do_wait_suspend(self, thread, frame, event, arg): #@UnusedVariable
         """ busy waits until the thread state changes to RUN
         it expects thread's state as attributes of the thread.
@@ -1004,7 +1011,7 @@ def process_command_line(argv):
     """ parses the arguments.
         removes our arguments from the command line """
     setup = {}
-    setup['client'] = ''
+    setup['client'] = None
     setup['server'] = False
     setup['port'] = 0
     setup['file'] = ''
@@ -1180,10 +1187,6 @@ def _locked_settrace(
         else:
             pydev_monkey.patch_new_process_functions()
 
-    if host is None:
-        from _pydev_bundle import pydev_localhost
-        host = pydev_localhost.get_localhost()
-
     global connected
     global bufferStdOutToServer
     global bufferStdErrToServer
index 9090238a6a457db1b7e3f9506d3647d6abd6355f..c67c4dcb052536c0ac4d752bb4235ab7bb3fe622 100644 (file)
@@ -54,6 +54,7 @@ public abstract class AbstractCommand<T> {
   public static final int SHOW_RETURN_VALUES = 146;
   public static final int INPUT_REQUESTED = 147;
 
+  public static final int PROCESS_CREATED = 149;
 
   public static final int ERROR = 901;
 
diff --git a/python/pydevSrc/com/jetbrains/python/debugger/pydev/ClientModeMultiProcessDebugger.java b/python/pydevSrc/com/jetbrains/python/debugger/pydev/ClientModeMultiProcessDebugger.java
new file mode 100644 (file)
index 0000000..f3111c1
--- /dev/null
@@ -0,0 +1,416 @@
+package com.jetbrains.python.debugger.pydev;
+
+import com.google.common.base.Function;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.xdebugger.breakpoints.SuspendPolicy;
+import com.intellij.xdebugger.frame.XValueChildrenList;
+import com.jetbrains.python.console.pydev.PydevCompletionVariant;
+import com.jetbrains.python.debugger.*;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.*;
+
+/**
+ * @author traff
+ */
+public class ClientModeMultiProcessDebugger implements ProcessDebugger {
+  private static final Logger LOG = Logger.getInstance(ClientModeMultiProcessDebugger.class);
+
+  private final IPyDebugProcess myDebugProcess;
+  @NotNull private final String myHost;
+  private final int myPort;
+
+  @NotNull private final RemoteDebugger myMainDebugger;
+  private final Object myOtherDebuggersObject = new Object();
+  private final List<RemoteDebugger> myOtherDebuggers = Lists.newArrayList();
+
+  private ThreadRegistry myThreadRegistry = new ThreadRegistry();
+  @NotNull private final Object mySocketsObject = new Object();
+
+  public ClientModeMultiProcessDebugger(@NotNull final IPyDebugProcess debugProcess,
+                                        @NotNull String host, int port) {
+    myDebugProcess = debugProcess;
+    myHost = host;
+    myPort = port;
+
+    myMainDebugger = createDebugger();
+
+    myOtherDebuggers.add(myMainDebugger);
+  }
+
+  @NotNull
+  private RemoteDebugger createDebugger() {
+    return new RemoteDebugger(myDebugProcess, myHost, myPort) {
+      @Override
+      protected void onProcessCreatedEvent() throws PyDebuggerException {
+        ApplicationManager.getApplication().executeOnPooledThread(ClientModeMultiProcessDebugger.this::connectToSubprocess);
+      }
+    };
+  }
+
+  @Override
+  public boolean isConnected() {
+    synchronized (myOtherDebuggersObject) {
+      return myOtherDebuggers.stream().anyMatch(RemoteDebugger::isConnected);
+    }
+  }
+
+  @Override
+  public void waitForConnect() throws Exception {
+    Thread.sleep(500L);
+
+    synchronized (mySocketsObject) {
+      myMainDebugger.waitForConnect();
+    }
+  }
+
+  private void connectToSubprocess() {
+    try {
+      RemoteDebugger debugger = createDebugger();
+      debugger.waitForConnect();
+
+      addDebugger(debugger);
+
+      LOG.debug("Connected to subprocess on attempt");
+
+      return;
+    }
+    catch (RuntimeException e) {
+      LOG.warn(e);
+    }
+    catch (Exception ignored) {
+    }
+    try {
+      //noinspection BusyWait
+      Thread.sleep(50L);
+    }
+    catch (InterruptedException ignored) {
+    }
+    LOG.debug("Could not connect to subprocess");
+  }
+
+  @Override
+  public void close() {
+    for (ProcessDebugger d : allDebuggers()) {
+      d.close();
+    }
+  }
+
+  private List<RemoteDebugger> allDebuggers() {
+    List<RemoteDebugger> result = new ArrayList<>();
+    synchronized (myOtherDebuggersObject) {
+      result.addAll(myOtherDebuggers);
+    }
+    return result;
+  }
+
+  @Override
+  public void disconnect() {
+    for (ProcessDebugger d : allDebuggers()) {
+      d.disconnect();
+    }
+  }
+
+  @Override
+  public String handshake() throws PyDebuggerException {
+    return myMainDebugger.handshake();
+  }
+
+  @Override
+  public PyDebugValue evaluate(String threadId, String frameId, String expression, boolean execute) throws PyDebuggerException {
+    return debugger(threadId).evaluate(threadId, frameId, expression, execute);
+  }
+
+  @Override
+  public PyDebugValue evaluate(String threadId, String frameId, String expression, boolean execute, boolean trimResult)
+    throws PyDebuggerException {
+    return debugger(threadId).evaluate(threadId, frameId, expression, execute, trimResult);
+  }
+
+  @Override
+  public void consoleExec(String threadId, String frameId, String expression, PyDebugCallback<String> callback) {
+    debugger(threadId).consoleExec(threadId, frameId, expression, callback);
+  }
+
+  @Override
+  public XValueChildrenList loadFrame(String threadId, String frameId) throws PyDebuggerException {
+    return debugger(threadId).loadFrame(threadId, frameId);
+  }
+
+  @Override
+  public XValueChildrenList loadVariable(String threadId, String frameId, PyDebugValue var) throws PyDebuggerException {
+    return debugger(threadId).loadVariable(threadId, frameId, var);
+  }
+
+  public ArrayChunk loadArrayItems(String threadId,
+                                   String frameId,
+                                   PyDebugValue var,
+                                   int rowOffset,
+                                   int colOffset,
+                                   int rows,
+                                   int cols,
+                                   String format) throws PyDebuggerException {
+    return debugger(threadId).loadArrayItems(threadId, frameId, var, rowOffset, colOffset, rows, cols, format);
+  }
+
+  @Override
+  public void loadReferrers(String threadId, String frameId, PyReferringObjectsValue var, PyDebugCallback<XValueChildrenList> callback) {
+    debugger(threadId).loadReferrers(threadId, frameId, var, callback);
+  }
+
+  @NotNull
+  private ProcessDebugger debugger(@NotNull String threadId) {
+    ProcessDebugger debugger = myThreadRegistry.getDebugger(threadId);
+    if (debugger != null) {
+      return debugger;
+    }
+    else {
+      // thread is not found in registry - lets search for it in attached debuggers
+
+      for (ProcessDebugger d : myOtherDebuggers) {
+        for (PyThreadInfo thread : d.getThreads()) {
+          if (threadId.equals(thread.getId())) {
+            return d;
+          }
+        }
+      }
+
+      //if not found then return main debugger
+      return myMainDebugger;
+    }
+  }
+
+  @Override
+  public PyDebugValue changeVariable(String threadId, String frameId, PyDebugValue var, String value) throws PyDebuggerException {
+    return debugger(threadId).changeVariable(threadId, frameId, var, value);
+  }
+
+  @Override
+  public String loadSource(String path) {
+    return myMainDebugger.loadSource(path);
+  }
+
+
+  private static class ThreadRegistry {
+    private Map<String, RemoteDebugger> myThreadIdToDebugger = Maps.newHashMap();
+
+    public void register(String id, RemoteDebugger debugger) {
+      myThreadIdToDebugger.put(id, debugger);
+    }
+
+    public RemoteDebugger getDebugger(String threadId) {
+      return myThreadIdToDebugger.get(threadId);
+    }
+
+    public static String threadName(@NotNull String name, @NotNull String id) {
+      int indx = id.indexOf("_", id.indexOf("_") + 1);
+      if (indx != -1) {
+        id = id.substring(0, indx);
+      }
+
+      return name + "(" + id + ")";
+    }
+  }
+
+  @Override
+  public Collection<PyThreadInfo> getThreads() {
+    cleanOtherDebuggers();
+
+    List<PyThreadInfo> threads = collectAllThreads();
+
+    if (myOtherDebuggers.size() > 0) {
+      //here we add process id to thread name in case there are more then one process
+      return Collections.unmodifiableCollection(Collections2.transform(threads, new Function<PyThreadInfo, PyThreadInfo>() {
+        @Override
+        public PyThreadInfo apply(PyThreadInfo t) {
+          String threadName = ThreadRegistry.threadName(t.getName(), t.getId());
+          PyThreadInfo newThread =
+            new PyThreadInfo(t.getId(), threadName, t.getFrames(),
+                             t.getStopReason(),
+                             t.getMessage());
+          newThread.updateState(t.getState(), t.getFrames());
+          return newThread;
+        }
+      }));
+    }
+    else {
+      return Collections.unmodifiableCollection(threads);
+    }
+  }
+
+  private List<PyThreadInfo> collectAllThreads() {
+    List<PyThreadInfo> result = Lists.newArrayList();
+
+    result.addAll(myMainDebugger.getThreads());
+
+    //collect threads and add them to registry to faster access
+    //we don't register mainDebugger as it is default if there is no mapping
+    for (RemoteDebugger d : myOtherDebuggers) {
+      result.addAll(d.getThreads());
+      for (PyThreadInfo t : d.getThreads()) {
+        myThreadRegistry.register(t.getId(), d);
+      }
+    }
+
+    return result;
+  }
+
+
+  private void cleanOtherDebuggers() {
+    synchronized (myOtherDebuggersObject) {
+      removeDisconnected(getOtherDebuggers());
+    }
+  }
+
+  private void removeDisconnected(ArrayList<RemoteDebugger> debuggers) {
+    boolean allConnected = true;
+    for (RemoteDebugger d : debuggers) {
+      if (!d.isConnected()) {
+        allConnected = false;
+      }
+    }
+    if (!allConnected) {
+      List<RemoteDebugger> newList = Lists.newArrayList();
+      for (RemoteDebugger d : debuggers) {
+        if (d.isConnected()) {
+          newList.add(d);
+        }
+      }
+
+      synchronized (myOtherDebuggersObject) {
+        myOtherDebuggers.clear();
+        myOtherDebuggers.addAll(newList);
+      }
+    }
+  }
+
+  private ArrayList<RemoteDebugger> getOtherDebuggers() {
+    synchronized (myOtherDebuggersObject) {
+      return Lists.newArrayList(myOtherDebuggers);
+    }
+  }
+
+
+  @Override
+  public void execute(@NotNull AbstractCommand command) {
+    for (ProcessDebugger d : allDebuggers()) {
+      d.execute(command);
+    }
+  }
+
+  @Override
+  public void suspendAllThreads() {
+    for (ProcessDebugger d : allDebuggers()) {
+      d.suspendAllThreads();
+    }
+  }
+
+  @Override
+  public void suspendThread(String threadId) {
+    debugger(threadId).suspendThread(threadId);
+  }
+
+  @Override
+  public void run() throws PyDebuggerException {
+    myMainDebugger.run();
+  }
+
+  @Override
+  public void smartStepInto(String threadId, String functionName) {
+    debugger(threadId).smartStepInto(threadId, functionName);
+  }
+
+  @Override
+  public void resumeOrStep(String threadId, ResumeOrStepCommand.Mode mode) {
+    debugger(threadId).resumeOrStep(threadId, mode);
+  }
+
+  @Override
+  public void setTempBreakpoint(@NotNull String type, @NotNull String file, int line) {
+    for (ProcessDebugger d : allDebuggers()) {
+      d.setTempBreakpoint(type, file, line);
+    }
+  }
+
+  @Override
+  public void removeTempBreakpoint(@NotNull String file, int line) {
+    for (ProcessDebugger d : allDebuggers()) {
+      d.removeTempBreakpoint(file, line);
+    }
+  }
+
+  @Override
+  public void setBreakpoint(@NotNull String typeId,
+                            @NotNull String file,
+                            int line,
+                            @Nullable String condition,
+                            @Nullable String logExpression,
+                            @Nullable String funcName,
+                            @NotNull SuspendPolicy policy) {
+    for (ProcessDebugger d : allDebuggers()) {
+      d.setBreakpoint(typeId, file, line, condition, logExpression, funcName, policy);
+    }
+  }
+
+  @Override
+  public void removeBreakpoint(@NotNull String typeId, @NotNull String file, int line) {
+    for (ProcessDebugger d : allDebuggers()) {
+      d.removeBreakpoint(typeId, file, line);
+    }
+  }
+
+  @Override
+  public void setShowReturnValues(boolean isShowReturnValues) {
+    for (ProcessDebugger d : allDebuggers()) {
+      d.setShowReturnValues(isShowReturnValues);
+    }
+  }
+
+  private void addDebugger(RemoteDebugger debugger) {
+    synchronized (myOtherDebuggersObject) {
+      myOtherDebuggers.add(debugger);
+    }
+  }
+
+  public void addCloseListener(RemoteDebuggerCloseListener listener) {
+    myMainDebugger.addCloseListener(listener);
+  }
+
+  @Override
+  public List<PydevCompletionVariant> getCompletions(String threadId, String frameId, String prefix) {
+    return debugger(threadId).getCompletions(threadId, frameId, prefix);
+  }
+
+  @Override
+  public String getDescription(String threadId, String frameId, String cmd) {
+    return debugger(threadId).getDescription(threadId, frameId, cmd);
+  }
+
+  @Override
+  public void addExceptionBreakpoint(ExceptionBreakpointCommandFactory factory) {
+    for (RemoteDebugger d : allDebuggers()) {
+      d.execute(factory.createAddCommand(d));
+    }
+  }
+
+  @Override
+  public void removeExceptionBreakpoint(ExceptionBreakpointCommandFactory factory) {
+    for (RemoteDebugger d : allDebuggers()) {
+      d.execute(factory.createRemoveCommand(d));
+    }
+  }
+
+  @Override
+  public void suspendOtherThreads(PyThreadInfo thread) {
+    for (RemoteDebugger d : allDebuggers()) {
+      // we should notify the debugger in each process about suspending all threads
+      d.suspendOtherThreads(thread);
+    }
+  }
+
+}
diff --git a/python/pydevSrc/com/jetbrains/python/debugger/pydev/ProcessCreatedCommand.java b/python/pydevSrc/com/jetbrains/python/debugger/pydev/ProcessCreatedCommand.java
new file mode 100644 (file)
index 0000000..8bc9a8a
--- /dev/null
@@ -0,0 +1,20 @@
+package com.jetbrains.python.debugger.pydev;
+
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author Alexander Koshevoy
+ */
+public class ProcessCreatedCommand extends AbstractCommand<ProcessCreatedCommand> {
+  public ProcessCreatedCommand(@NotNull RemoteDebugger debugger, int commandCode) {
+    super(debugger, commandCode);
+  }
+
+  @Override
+  protected void buildPayload(Payload payload) {
+  }
+
+  public static boolean isProcessCreatedCommand(int command) {
+    return command == AbstractCommand.PROCESS_CREATED;
+  }
+}
index 9b39966a51c11106965c3b5e60632de81eba187c..93172ffe7b5a0f62df952736f39998eedb24582b 100644 (file)
@@ -7,32 +7,27 @@ package com.jetbrains.python.debugger.pydev;
 
 import com.google.common.collect.Maps;
 import com.intellij.execution.ui.ConsoleViewContentType;
-import com.intellij.openapi.application.ApplicationManager;
 import com.intellij.openapi.diagnostic.Logger;
 import com.intellij.openapi.util.Pair;
 import com.intellij.openapi.util.SystemInfo;
 import com.intellij.openapi.util.text.StringUtil;
-import com.intellij.openapi.vfs.CharsetToolkit;
-import com.intellij.util.TimeoutUtil;
 import com.intellij.util.containers.ContainerUtil;
-import com.intellij.util.io.BaseOutputReader;
 import com.intellij.xdebugger.breakpoints.SuspendPolicy;
 import com.intellij.xdebugger.frame.XValueChildrenList;
 import com.jetbrains.python.console.pydev.PydevCompletionVariant;
 import com.jetbrains.python.debugger.*;
+import com.jetbrains.python.debugger.pydev.transport.ClientModeDebuggerTransport;
+import com.jetbrains.python.debugger.pydev.transport.DebuggerTransport;
+import com.jetbrains.python.debugger.pydev.transport.ServerModeDebuggerTransport;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
 import java.net.ServerSocket;
-import java.net.Socket;
-import java.net.SocketException;
 import java.security.SecureRandom;
 import java.util.*;
 import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.Future;
+
+import static com.jetbrains.python.debugger.pydev.transport.BaseDebuggerTransport.logFrame;
 
 
 public class RemoteDebugger implements ProcessDebugger {
@@ -47,13 +42,6 @@ public class RemoteDebugger implements ProcessDebugger {
 
   private final IPyDebugProcess myDebugProcess;
 
-  @NotNull
-  private final ServerSocket myServerSocket;
-
-  private final int myConnectionTimeout;
-  private final Object mySocketObject = new Object(); // for synchronization on socket
-  private Socket mySocket;
-  private volatile boolean myConnected = false;
   private int mySequence = -1;
   private final Object mySequenceObject = new Object(); // for synchronization on mySequence
   private final Map<String, PyThreadInfo> myThreads = new ConcurrentHashMap<>();
@@ -64,12 +52,22 @@ public class RemoteDebugger implements ProcessDebugger {
 
 
   private final List<RemoteDebuggerCloseListener> myCloseListeners = ContainerUtil.createLockFreeCopyOnWriteList();
-  private DebuggerReader myDebuggerReader;
 
-  public RemoteDebugger(final IPyDebugProcess debugProcess, @NotNull final ServerSocket serverSocket, final int timeout) {
+  @NotNull private final DebuggerTransport myDebuggerTransport;
+
+  public RemoteDebugger(@NotNull IPyDebugProcess debugProcess, @NotNull String host, int port) {
+    myDebugProcess = debugProcess;
+    myDebuggerTransport = new ClientModeDebuggerTransport(debugProcess, this, host, port);
+  }
+
+  public RemoteDebugger(@NotNull IPyDebugProcess debugProcess, @NotNull ServerSocket socket, int timeout) {
+    myDebugProcess = debugProcess;
+    myDebuggerTransport = new ServerModeDebuggerTransport(this, socket, timeout);
+  }
+
+  protected RemoteDebugger(@NotNull IPyDebugProcess debugProcess, @NotNull DebuggerTransport debuggerTransport) {
     myDebugProcess = debugProcess;
-    myServerSocket = serverSocket;
-    myConnectionTimeout = timeout;
+    myDebuggerTransport = debuggerTransport;
   }
 
   public IPyDebugProcess getDebugProcess() {
@@ -78,35 +76,23 @@ public class RemoteDebugger implements ProcessDebugger {
 
   @Override
   public boolean isConnected() {
-    return myConnected;
+    return myDebuggerTransport.isConnected();
   }
 
-
   @Override
   public void waitForConnect() throws Exception {
-    try {
-      //noinspection SocketOpenedButNotSafelyClosed
-      myServerSocket.setSoTimeout(myConnectionTimeout);
-      synchronized (mySocketObject) {
-        mySocket = myServerSocket.accept();
-        myConnected = true;
-      }
-    }
-    finally {
-      //it is closed in close() method on process termination
-    }
+    myDebuggerTransport.waitForConnect();
+  }
 
-    if (myConnected) {
-      try {
-        myDebuggerReader = createReader();
-      }
-      catch (Exception e) {
-        synchronized (mySocketObject) {
-          mySocket.close();
-        }
-        throw e;
-      }
+  private void writeToConsole(PyIo io) {
+    ConsoleViewContentType contentType;
+    if (io.getCtx() == 2) {
+      contentType = ConsoleViewContentType.ERROR_OUTPUT;
     }
+    else {
+      contentType = ConsoleViewContentType.NORMAL_OUTPUT;
+    }
+    myDebugProcess.printToConsole(io.getText(), contentType);
   }
 
   @Override
@@ -348,31 +334,7 @@ public class RemoteDebugger implements ProcessDebugger {
   }
 
   boolean sendFrame(final ProtocolFrame frame) {
-    logFrame(frame, true);
-
-    try {
-      final byte[] packed = frame.pack();
-      synchronized (mySocketObject) {
-        final OutputStream os = mySocket.getOutputStream();
-        os.write(packed);
-        os.flush();
-        return true;
-      }
-    }
-    catch (SocketException se) {
-      disconnect();
-      fireCommunicationError();
-    }
-    catch (IOException e) {
-      LOG.error(e);
-    }
-    return false;
-  }
-
-  private static void logFrame(ProtocolFrame frame, boolean out) {
-    if (LOG.isDebugEnabled()) {
-      LOG.debug(String.format("%1$tH:%1$tM:%1$tS.%1$tL %2$s %3$s\n", new Date(), (out ? "<<<" : ">>>"), frame));
-    }
+    return myDebuggerTransport.sendFrame(frame);
   }
 
   @Override
@@ -391,33 +353,13 @@ public class RemoteDebugger implements ProcessDebugger {
 
   @Override
   public void close() {
-    if (!myServerSocket.isClosed()) {
-      try {
-        myServerSocket.close();
-      }
-      catch (IOException e) {
-        LOG.warn("Error closing socket", e);
-      }
-    }
-    if (myDebuggerReader != null) {
-      myDebuggerReader.stop();
-    }
+    myDebuggerTransport.close();
     fireCloseEvent();
   }
 
   @Override
   public void disconnect() {
-    synchronized (mySocketObject) {
-      myConnected = false;
-
-      if (mySocket != null && !mySocket.isClosed()) {
-        try {
-          mySocket.close();
-        }
-        catch (IOException ignore) {
-        }
-      }
-    }
+    myDebuggerTransport.disconnect();
 
     cleanUp();
   }
@@ -490,223 +432,135 @@ public class RemoteDebugger implements ProcessDebugger {
     execute(command);
   }
 
-  private DebuggerReader createReader() throws IOException {
-    synchronized (mySocketObject) {
-      //noinspection IOResourceOpenedButNotSafelyClosed
-      return new DebuggerReader(mySocket.getInputStream());
-    }
-  }
-
-  private class DebuggerReader extends BaseOutputReader {
-    private StringBuilder myTextBuilder = new StringBuilder();
-
-    private DebuggerReader(final InputStream stream) throws IOException {
-      super(stream, CharsetToolkit.UTF8_CHARSET); //TODO: correct encoding?
-      start(getClass().getName());
-    }
-
-    protected void doRun() {
-      try {
-        while (true) {
-          boolean read = readAvailableBlocking();
+  // for DebuggerReader only
+  public void processResponse(@NotNull final String line) {
+    try {
+      final ProtocolFrame frame = new ProtocolFrame(line);
+      logFrame(frame, false);
 
-          if (!read) {
-            break;
-          }
-          else {
-            if (isStopped) {
-              break;
-            }
+      myDebuggerTransport.messageReceived(frame);
 
-            TimeoutUtil.sleep(mySleepingPolicy.getTimeToSleep(true));
-          }
-        }
+      if (AbstractThreadCommand.isThreadCommand(frame.getCommand())) {
+        processThreadEvent(frame);
+      }
+      else if (AbstractCommand.isWriteToConsole(frame.getCommand())) {
+        writeToConsole(ProtocolParser.parseIo(frame.getPayload()));
       }
-      catch (Exception e) {
+      else if (AbstractCommand.isExitEvent(frame.getCommand())) {
         fireCommunicationError();
       }
-      finally {
-        close();
-        fireExitEvent();
+      else if (AbstractCommand.isCallSignatureTrace(frame.getCommand())) {
+        recordCallSignature(ProtocolParser.parseCallSignature(frame.getPayload()));
       }
-    }
-
-    private void processResponse(final String line) {
-      try {
-        final ProtocolFrame frame = new ProtocolFrame(line);
-        logFrame(frame, false);
-
-        if (AbstractThreadCommand.isThreadCommand(frame.getCommand())) {
-          processThreadEvent(frame);
-        }
-        else if (AbstractCommand.isWriteToConsole(frame.getCommand())) {
-          writeToConsole(ProtocolParser.parseIo(frame.getPayload()));
-        }
-        else if (AbstractCommand.isExitEvent(frame.getCommand())) {
-          fireCommunicationError();
-        }
-        else if (AbstractCommand.isCallSignatureTrace(frame.getCommand())) {
-          recordCallSignature(ProtocolParser.parseCallSignature(frame.getPayload()));
-        }
-        else if (AbstractCommand.isConcurrencyEvent(frame.getCommand())) {
-          recordConcurrencyEvent(ProtocolParser.parseConcurrencyEvent(frame.getPayload(), myDebugProcess.getPositionConverter()));
-        }
-        else if (AbstractCommand.isInputRequested(frame.getCommand())) {
-          myDebugProcess.consoleInputRequested(ProtocolParser.parseInputCommand(frame.getPayload()));
-        }
-        else {
-          placeResponse(frame.getSequence(), frame);
-        }
+      else if (AbstractCommand.isConcurrencyEvent(frame.getCommand())) {
+        recordConcurrencyEvent(ProtocolParser.parseConcurrencyEvent(frame.getPayload(), myDebugProcess.getPositionConverter()));
+      }
+      else if (AbstractCommand.isInputRequested(frame.getCommand())) {
+        myDebugProcess.consoleInputRequested(ProtocolParser.parseInputCommand(frame.getPayload()));
       }
-      catch (Throwable t) {
-        // shouldn't interrupt reader thread
-        LOG.error(t);
+      else if (ProcessCreatedCommand.isProcessCreatedCommand(frame.getCommand())) {
+        onProcessCreatedEvent();
+      }
+      else {
+        placeResponse(frame.getSequence(), frame);
       }
     }
-
-    private void recordCallSignature(PySignature signature) {
-      myDebugProcess.recordSignature(signature);
+    catch (Throwable t) {
+      // shouldn't interrupt reader thread
+      LOG.error(t);
     }
+  }
 
-    private void recordConcurrencyEvent(PyConcurrencyEvent event) {
-      myDebugProcess.recordLogEvent(event);
-    }
+  private void recordCallSignature(PySignature signature) {
+    myDebugProcess.recordSignature(signature);
+  }
 
-    // todo: extract response processing
-    private void processThreadEvent(ProtocolFrame frame) throws PyDebuggerException {
-      switch (frame.getCommand()) {
-        case AbstractCommand.CREATE_THREAD: {
-          final PyThreadInfo thread = parseThreadEvent(frame);
-          if (!thread.isPydevThread()) {  // ignore pydevd threads
-            myThreads.put(thread.getId(), thread);
-            if (myDebugProcess.getSession().isSuspended() && myDebugProcess.isSuspendedOnAllThreadsPolicy()) {
-              // Sometimes the notification about new threads may come slow from the Python side. We should check if
-              // the current session is suspended in the "Suspend all threads" mode and suspend new thread, which hasn't been suspended
-              suspendThread(thread.getId());
-            }
+  private void recordConcurrencyEvent(PyConcurrencyEvent event) {
+    myDebugProcess.recordLogEvent(event);
+  }
+
+  // todo: extract response processing
+  private void processThreadEvent(ProtocolFrame frame) throws PyDebuggerException {
+    switch (frame.getCommand()) {
+      case AbstractCommand.CREATE_THREAD: {
+        final PyThreadInfo thread = parseThreadEvent(frame);
+        if (!thread.isPydevThread()) {  // ignore pydevd threads
+          myThreads.put(thread.getId(), thread);
+          if (myDebugProcess.getSession().isSuspended() && myDebugProcess.isSuspendedOnAllThreadsPolicy()) {
+            // Sometimes the notification about new threads may come slow from the Python side. We should check if
+            // the current session is suspended in the "Suspend all threads" mode and suspend new thread, which hasn't been suspended
+            suspendThread(thread.getId());
           }
-          break;
         }
-        case AbstractCommand.SUSPEND_THREAD: {
-          final PyThreadInfo event = parseThreadEvent(frame);
-          PyThreadInfo thread = myThreads.get(event.getId());
-          if (thread == null) {
-            LOG.error("Trying to stop on non-existent thread: " + event.getId() + ", " + event.getStopReason() + ", " + event.getMessage());
-            myThreads.put(event.getId(), event);
-            thread = event;
-          }
-          thread.updateState(PyThreadInfo.State.SUSPENDED, event.getFrames());
-          thread.setStopReason(event.getStopReason());
-          thread.setMessage(event.getMessage());
-          boolean updateSourcePosition = true;
-          if (event.getStopReason() == AbstractCommand.SUSPEND_THREAD) {
-            // That means that the thread was stopped manually from the Java side either while suspending all threads
-            // or after the "Pause" command. In both cases we shouldn't change debugger focus if session is already suspended.
-            updateSourcePosition = !myDebugProcess.getSession().isSuspended();
-          }
-          myDebugProcess.threadSuspended(thread, updateSourcePosition);
-          break;
+        break;
+      }
+      case AbstractCommand.SUSPEND_THREAD: {
+        final PyThreadInfo event = parseThreadEvent(frame);
+        PyThreadInfo thread = myThreads.get(event.getId());
+        if (thread == null) {
+          LOG.error("Trying to stop on non-existent thread: " + event.getId() + ", " + event.getStopReason() + ", " + event.getMessage());
+          myThreads.put(event.getId(), event);
+          thread = event;
         }
-        case AbstractCommand.RESUME_THREAD: {
-          final String id = ProtocolParser.getThreadId(frame.getPayload());
-          final PyThreadInfo thread = myThreads.get(id);
-          if (thread != null) {
-            thread.updateState(PyThreadInfo.State.RUNNING, null);
-            myDebugProcess.threadResumed(thread);
-          }
-          break;
+        thread.updateState(PyThreadInfo.State.SUSPENDED, event.getFrames());
+        thread.setStopReason(event.getStopReason());
+        thread.setMessage(event.getMessage());
+        boolean updateSourcePosition = true;
+        if (event.getStopReason() == AbstractCommand.SUSPEND_THREAD) {
+          // That means that the thread was stopped manually from the Java side either while suspending all threads
+          // or after the "Pause" command. In both cases we shouldn't change debugger focus if session is already suspended.
+          updateSourcePosition = !myDebugProcess.getSession().isSuspended();
         }
-        case AbstractCommand.KILL_THREAD: {
-          final String id = frame.getPayload();
-          final PyThreadInfo thread = myThreads.get(id);
-          if (thread != null) {
-            thread.updateState(PyThreadInfo.State.KILLED, null);
-            myThreads.remove(id);
-          }
-          if (myDebugProcess.getSession().getCurrentPosition() == null) {
-            for (PyThreadInfo threadInfo : myThreads.values()) {
-              // notify UI of suspended threads left in debugger if one thread finished its work
-              if ((threadInfo != null) && (threadInfo.getState() == PyThreadInfo.State.SUSPENDED)) {
-                myDebugProcess.threadResumed(threadInfo);
-                myDebugProcess.threadSuspended(threadInfo, true);
-              }
-            }
-          }
-          break;
+        myDebugProcess.threadSuspended(thread, updateSourcePosition);
+        break;
+      }
+      case AbstractCommand.RESUME_THREAD: {
+        final String id = ProtocolParser.getThreadId(frame.getPayload());
+        final PyThreadInfo thread = myThreads.get(id);
+        if (thread != null) {
+          thread.updateState(PyThreadInfo.State.RUNNING, null);
+          myDebugProcess.threadResumed(thread);
+        }
+        break;
+      }
+      case AbstractCommand.KILL_THREAD: {
+        final String id = frame.getPayload();
+        final PyThreadInfo thread = myThreads.get(id);
+        if (thread != null) {
+          thread.updateState(PyThreadInfo.State.KILLED, null);
+          myThreads.remove(id);
         }
-        case AbstractCommand.SHOW_CONSOLE: {
-          final PyThreadInfo event = parseThreadEvent(frame);
-          PyThreadInfo thread = myThreads.get(event.getId());
-          if (thread == null) {
-            myThreads.put(event.getId(), event);
-            thread = event;
+        if (myDebugProcess.getSession().getCurrentPosition() == null) {
+          for (PyThreadInfo threadInfo : myThreads.values()) {
+            // notify UI of suspended threads left in debugger if one thread finished its work
+            if ((threadInfo != null) && (threadInfo.getState() == PyThreadInfo.State.SUSPENDED)) {
+              myDebugProcess.threadResumed(threadInfo);
+              myDebugProcess.threadSuspended(threadInfo, true);
+            }
           }
-          thread.updateState(PyThreadInfo.State.SUSPENDED, event.getFrames());
-          thread.setStopReason(event.getStopReason());
-          thread.setMessage(event.getMessage());
-          myDebugProcess.showConsole(thread);
-          break;
         }
+        break;
       }
-    }
-
-    private PyThreadInfo parseThreadEvent(ProtocolFrame frame) throws PyDebuggerException {
-      return ProtocolParser.parseThread(frame.getPayload(), myDebugProcess.getPositionConverter());
-    }
-
-    @NotNull
-    @Override
-    protected Future<?> executeOnPooledThread(@NotNull Runnable runnable) {
-      return ApplicationManager.getApplication().executeOnPooledThread(runnable);
-    }
-
-    @Override
-    protected void close() {
-      try {
-        super.close();
-      }
-      catch (IOException e) {
-        LOG.error(e);
-      }
-    }
-
-    @Override
-    public void stop() {
-      super.stop();
-      close();
-    }
-
-    @Override
-    protected void onTextAvailable(@NotNull String text) {
-      myTextBuilder.append(text);
-      if (text.contains("\n")) {
-        String[] lines = myTextBuilder.toString().split("\n");
-        myTextBuilder = new StringBuilder();
-
-        if (!text.endsWith("\n")) {
-          myTextBuilder.append(lines[lines.length - 1]);
-          lines = Arrays.copyOfRange(lines, 0, lines.length - 1);
-        }
-
-        for (String line : lines) {
-          processResponse(line + "\n");
+      case AbstractCommand.SHOW_CONSOLE: {
+        final PyThreadInfo event = parseThreadEvent(frame);
+        PyThreadInfo thread = myThreads.get(event.getId());
+        if (thread == null) {
+          myThreads.put(event.getId(), event);
+          thread = event;
         }
+        thread.updateState(PyThreadInfo.State.SUSPENDED, event.getFrames());
+        thread.setStopReason(event.getStopReason());
+        thread.setMessage(event.getMessage());
+        myDebugProcess.showConsole(thread);
+        break;
       }
     }
   }
 
-  private void writeToConsole(PyIo io) {
-    ConsoleViewContentType contentType;
-    if (io.getCtx() == 2) {
-      contentType = ConsoleViewContentType.ERROR_OUTPUT;
-    }
-    else {
-      contentType = ConsoleViewContentType.NORMAL_OUTPUT;
-    }
-    myDebugProcess.printToConsole(io.getText(), contentType);
+  private PyThreadInfo parseThreadEvent(ProtocolFrame frame) throws PyDebuggerException {
+    return ProtocolParser.parseThread(frame.getPayload(), myDebugProcess.getPositionConverter());
   }
 
-
   private static class TempVarsHolder {
     private final Map<String, Map<String, Set<String>>> myData = new HashMap<>();
 
@@ -720,7 +574,7 @@ public class RemoteDebugger implements ProcessDebugger {
       return frameVars.contains(name);
     }
 
-    private void put(final String threadId, final String frameId, final String name) {
+    protected void put(final String threadId, final String frameId, final String name) {
       Map<String, Set<String>> threadVars = myData.get(threadId);
       if (threadVars == null) myData.put(threadId, (threadVars = new HashMap<>()));
 
@@ -730,15 +584,15 @@ public class RemoteDebugger implements ProcessDebugger {
       frameVars.add(name);
     }
 
-    private Map<String, Set<String>> get(final String threadId) {
+    protected Map<String, Set<String>> get(final String threadId) {
       return myData.get(threadId);
     }
 
-    private void clear() {
+    protected void clear() {
       myData.clear();
     }
 
-    private void clear(final String threadId) {
+    protected void clear(final String threadId) {
       final Map<String, Set<String>> threadVars = myData.get(threadId);
       if (threadVars != null) {
         threadVars.clear();
@@ -791,19 +645,23 @@ public class RemoteDebugger implements ProcessDebugger {
     }
   }
 
-  private void fireCloseEvent() {
+  protected void onProcessCreatedEvent() throws PyDebuggerException {
+  }
+
+  protected void fireCloseEvent() {
     for (RemoteDebuggerCloseListener listener : myCloseListeners) {
       listener.closed();
     }
   }
 
-  private void fireCommunicationError() {
+  public void fireCommunicationError() {
     for (RemoteDebuggerCloseListener listener : myCloseListeners) {
       listener.communicationError();
     }
   }
 
-  private void fireExitEvent() {
+  // for DebuggerReader only
+  public void fireExitEvent() {
     for (RemoteDebuggerCloseListener listener : myCloseListeners) {
       listener.detached();
     }
diff --git a/python/pydevSrc/com/jetbrains/python/debugger/pydev/transport/BaseDebuggerReader.java b/python/pydevSrc/com/jetbrains/python/debugger/pydev/transport/BaseDebuggerReader.java
new file mode 100644 (file)
index 0000000..7b53407
--- /dev/null
@@ -0,0 +1,102 @@
+package com.jetbrains.python.debugger.pydev.transport;
+
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.util.TimeoutUtil;
+import com.intellij.util.io.BaseOutputReader;
+import com.jetbrains.python.debugger.pydev.RemoteDebugger;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.Charset;
+import java.util.Arrays;
+import java.util.concurrent.Future;
+
+/**
+ * @author Alexander Koshevoy
+ */
+public abstract class BaseDebuggerReader extends BaseOutputReader {
+  private static final Logger LOG = Logger.getInstance(BaseDebuggerReader.class);
+
+  @NotNull private final RemoteDebugger myDebugger;
+  @NotNull private StringBuilder myTextBuilder = new StringBuilder();
+
+  public BaseDebuggerReader(@NotNull InputStream inputStream, @NotNull Charset charset, @NotNull RemoteDebugger debugger) {
+    super(inputStream, charset);
+    myDebugger = debugger;
+  }
+
+  @NotNull
+  protected RemoteDebugger getDebugger() {
+    return myDebugger;
+  }
+
+  protected void doRun() {
+    try {
+      while (true) {
+        boolean read = readAvailableBlocking();
+
+        if (!read) {
+          break;
+        }
+        else {
+          if (isStopped) {
+            break;
+          }
+
+          TimeoutUtil.sleep(mySleepingPolicy.getTimeToSleep(true));
+        }
+      }
+    }
+    catch (Exception e) {
+      onCommunicationError();
+    }
+    finally {
+      close();
+      myDebugger.fireExitEvent();
+    }
+  }
+
+  protected abstract void onCommunicationError();
+
+  @NotNull
+  @Override
+  protected Future<?> executeOnPooledThread(@NotNull Runnable runnable) {
+    return ApplicationManager.getApplication().executeOnPooledThread(runnable);
+  }
+
+  @Override
+  protected void close() {
+    try {
+      super.close();
+    }
+    catch (IOException e) {
+      LOG.error(e);
+    }
+  }
+
+  @Override
+  public void stop() {
+    super.stop();
+    close();
+  }
+
+  @Override
+  protected void onTextAvailable(@NotNull String text) {
+    myTextBuilder.append(text);
+    if (text.contains("\n")) {
+      String[] lines = myTextBuilder.toString().split("\n");
+      myTextBuilder = new StringBuilder();
+
+      if (!text.endsWith("\n")) {
+        myTextBuilder.append(lines[lines.length - 1]);
+        lines = Arrays.copyOfRange(lines, 0, lines.length - 1);
+      }
+
+      for (String line : lines) {
+        myDebugger.processResponse(line + "\n");
+      }
+    }
+  }
+}
diff --git a/python/pydevSrc/com/jetbrains/python/debugger/pydev/transport/BaseDebuggerTransport.java b/python/pydevSrc/com/jetbrains/python/debugger/pydev/transport/BaseDebuggerTransport.java
new file mode 100644 (file)
index 0000000..f7b8ab2
--- /dev/null
@@ -0,0 +1,49 @@
+package com.jetbrains.python.debugger.pydev.transport;
+
+import com.intellij.openapi.diagnostic.Logger;
+import com.jetbrains.python.debugger.pydev.ProtocolFrame;
+import com.jetbrains.python.debugger.pydev.RemoteDebugger;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.IOException;
+import java.net.SocketException;
+import java.util.Date;
+
+/**
+ * @author Alexander Koshevoy
+ */
+public abstract class BaseDebuggerTransport implements DebuggerTransport {
+  private static final Logger LOG = Logger.getInstance(BaseDebuggerTransport.class);
+
+  protected final Object mySocketObject = new Object();
+
+  @NotNull protected final RemoteDebugger myDebugger;
+
+  protected BaseDebuggerTransport(@NotNull RemoteDebugger debugger) {myDebugger = debugger;}
+
+  @Override
+  public boolean sendFrame(@NotNull final ProtocolFrame frame) {
+    logFrame(frame, true);
+
+    try {
+      final byte[] packed = frame.pack();
+      return sendMessageImpl(packed);
+    }
+    catch (SocketException se) {
+      myDebugger.disconnect();
+      myDebugger.fireCommunicationError();
+    }
+    catch (IOException e) {
+      LOG.error(e);
+    }
+    return false;
+  }
+
+  protected abstract boolean sendMessageImpl(byte[] packed) throws IOException;
+
+  public static void logFrame(ProtocolFrame frame, boolean out) {
+    if (LOG.isDebugEnabled()) {
+      LOG.debug(String.format("%1$tH:%1$tM:%1$tS.%1$tL %2$s %3$s\n", new Date(), (out ? "<<<" : ">>>"), frame));
+    }
+  }
+}
diff --git a/python/pydevSrc/com/jetbrains/python/debugger/pydev/transport/ClientModeDebuggerTransport.java b/python/pydevSrc/com/jetbrains/python/debugger/pydev/transport/ClientModeDebuggerTransport.java
new file mode 100644 (file)
index 0000000..8cf033a
--- /dev/null
@@ -0,0 +1,276 @@
+package com.jetbrains.python.debugger.pydev.transport;
+
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.vfs.CharsetToolkit;
+import com.jetbrains.python.debugger.IPyDebugProcess;
+import com.jetbrains.python.debugger.PyDebuggerException;
+import com.jetbrains.python.debugger.pydev.AbstractCommand;
+import com.jetbrains.python.debugger.pydev.ClientModeMultiProcessDebugger;
+import com.jetbrains.python.debugger.pydev.ProtocolFrame;
+import com.jetbrains.python.debugger.pydev.RemoteDebugger;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.ConnectException;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * {@link DebuggerTransport} implementation that expects a debugging script to behave as a server. The main process of the debugging script
+ * and all of the Python processes forked and created within it receives incoming connections on the <b>same port</b> using
+ * {@code SO_REUSEPORT} server socket option on Linux (available since 3.9 core version), Mac OS, BSD platforms and {@code SO_REUSEADDR}
+ * server socket option on Windows platforms (see {@code start_server(port)} method in <i>pydevd_comm.py</i>).
+ * <p>
+ * Each Python process within the debugging script requires <b>single connection</b> from the IDE. When a new Python process is created the
+ * originator process sends {@link AbstractCommand#PROCESS_CREATED} message to the IDE. The new process binds server socket to the same
+ * address and port as the originator process and starts listening for an incoming connection. The IDE tries to establish a new
+ * connection with the script.
+ * <p>
+ * At the last point the following problem could arise. When several processes are created almost simultaneously and they become
+ * bound to the single port the IDE could establish the connection to some of the processes twice or more times. The first connection is
+ * accepted by the Python process but the others are not. Other connections would stay in <i>completed connection queue</i> until a timeout
+ * for a response for the {@link AbstractCommand#RUN} arouse. To solve this problem {@link ClientModeDebuggerTransport} has several
+ * states. The transport is created with {@link State#INIT}. After the connection is established the transport object is transferred to
+ * {@link State#CONNECTED}. After the first message is received from the debugging script the transport is transferred to
+ * {@link State#APPROVED}. After the transport is transferred to {@link State#CONNECTED} a task is scheduled in
+ * {@link ClientModeDebuggerTransport#CHECK_CONNECTION_APPROVED_DELAY} to check if the state has been changed to {@link State#APPROVED}. If
+ * the state had been changed then the current connection is kept and the normal communication with debugging script is performed. If the
+ * state has not been changed for this period of time the transport tries to reconnect and schedules the check again.
+ *
+ * @author Alexander Koshevoy
+ * @see ClientModeMultiProcessDebugger
+ */
+public class ClientModeDebuggerTransport extends BaseDebuggerTransport {
+  private static final Logger LOG = Logger.getInstance(ClientModeDebuggerTransport.class);
+
+  private static final ScheduledExecutorService myScheduledExecutor = new ScheduledThreadPoolExecutor(1, new ThreadFactory() {
+    private AtomicInteger num = new AtomicInteger(1);
+
+    @Override
+    public Thread newThread(@NotNull Runnable r) {
+      return new Thread(r, "Python Debug Script Connection " + num.getAndIncrement());
+    }
+  });
+
+  private static final int MAX_CONNECTION_TRIES = 10;
+  private static final long CHECK_CONNECTION_APPROVED_DELAY = 1000L;
+  private static final long SLEEP_TIME_BETWEEN_CONNECTION_TRIES = 150L;
+
+  @NotNull private final IPyDebugProcess myDebugProcess;
+
+  @NotNull private final String myHost;
+  private final int myPort;
+
+  @NotNull private State myState = State.INIT;
+
+  @Nullable private Socket mySocket;
+  @Nullable private DebuggerReader myDebuggerReader;
+
+  public ClientModeDebuggerTransport(@NotNull IPyDebugProcess debugProcess,
+                                     @NotNull RemoteDebugger debugger,
+                                     @NotNull String host,
+                                     int port) {
+    super(debugger);
+    myDebugProcess = debugProcess;
+    myHost = host;
+    myPort = port;
+  }
+
+  @Override
+  public void waitForConnect() throws IOException {
+    try {
+      Thread.sleep(500L);
+    }
+    catch (InterruptedException e) {
+      throw new IOException(e);
+    }
+    synchronized (mySocketObject) {
+      if (myState != State.INIT) {
+        throw new IllegalStateException(
+          "Inappropriate state of Python debugger for connecting to Python debugger: " + myState + "; " + State.INIT + " is expected");
+      }
+
+      doConnect();
+    }
+  }
+
+  private void doConnect() throws IOException {
+    synchronized (mySocketObject) {
+      if (mySocket != null) {
+        try {
+          mySocket.close();
+        }
+        catch (IOException e) {
+          LOG.debug("Failed to close previously opened socket", e);
+        }
+        finally {
+          mySocket = null;
+        }
+      }
+
+      int i = 0;
+      boolean connected = false;
+      while (!connected && i < MAX_CONNECTION_TRIES) {
+        i++;
+        try {
+          Socket clientSocket = new Socket();
+          clientSocket.setSoTimeout(0);
+          clientSocket.connect(new InetSocketAddress(myHost, myPort));
+
+          try {
+            myDebuggerReader = new DebuggerReader(myDebugger, clientSocket.getInputStream());
+          }
+          catch (IOException e) {
+            LOG.debug("Failed to create debugger reader", e);
+            throw e;
+          }
+
+          mySocket = clientSocket;
+          connected = true;
+        }
+        catch (ConnectException e) {
+          if (i < MAX_CONNECTION_TRIES) {
+            try {
+              Thread.sleep(SLEEP_TIME_BETWEEN_CONNECTION_TRIES);
+            }
+            catch (InterruptedException e1) {
+              throw new IOException(e1);
+            }
+          }
+        }
+      }
+
+      if (!connected) {
+        myState = State.DISCONNECTED;
+        throw new IOException("Failed to connect to debugging script");
+      }
+
+      myState = State.CONNECTED;
+      LOG.debug("Connected to Python debugger script on #" + i + " attempt");
+
+
+      try {
+        myDebugProcess.init();
+        myDebugger.run();
+      }
+      catch (PyDebuggerException e) {
+        myState = State.DISCONNECTED;
+        throw new IOException("Failed to send run command", e);
+      }
+
+      myScheduledExecutor.schedule(() -> {
+        synchronized (mySocketObject) {
+          if (myState == State.CONNECTED) {
+            try {
+              LOG.debug("Reconnecting...");
+              doConnect();
+            }
+            catch (IOException e) {
+              LOG.debug(e);
+              myDebugger.fireCommunicationError();
+            }
+          }
+        }
+      }, CHECK_CONNECTION_APPROVED_DELAY, TimeUnit.MILLISECONDS);
+    }
+  }
+
+  @Override
+  protected boolean sendMessageImpl(byte[] packed) throws IOException {
+    synchronized (mySocketObject) {
+      if (mySocket == null) {
+        return false;
+      }
+      final OutputStream os = mySocket.getOutputStream();
+      os.write(packed);
+      os.flush();
+      return true;
+    }
+  }
+
+  @Override
+  public void close() {
+    synchronized (mySocketObject) {
+      try {
+        if (myDebuggerReader != null) {
+          myDebuggerReader.stop();
+        }
+      }
+      finally {
+        if (mySocket != null) {
+          try {
+            mySocket.close();
+          }
+          catch (IOException ignored) {
+          }
+        }
+      }
+    }
+  }
+
+  @Override
+  public boolean isConnected() {
+    synchronized (mySocketObject) {
+      return myState == State.APPROVED;
+      //return myConnected && mySocket != null && !mySocket.isClosed();
+    }
+  }
+
+  @Override
+  public void disconnect() {
+    // TODO disconnect?
+  }
+
+  @Override
+  public void messageReceived(@NotNull ProtocolFrame frame) {
+    synchronized (mySocketObject) {
+      if (myState == State.CONNECTED) {
+        myState = State.APPROVED;
+      }
+    }
+  }
+
+  private enum State {
+    /**
+     * Before calling {@link #waitForConnect()}
+     */
+    INIT,
+    /**
+     * Socket connection to the debugger host:port address established and no messages has been received from the debugging script yet.
+     * The connection might be ephemeral at this point (see {@link ClientModeDebuggerTransport}).
+     */
+    CONNECTED,
+    /**
+     * Socket connection to the debugger host:port address established and at least one message has been received from the debugging script.
+     * This state means that a script is on the other end had accepted the connection.
+     */
+    APPROVED,
+    /**
+     * Debugger disconnected
+     */
+    DISCONNECTED
+  }
+
+  public class DebuggerReader extends BaseDebuggerReader {
+    public DebuggerReader(@NotNull RemoteDebugger debugger, @NotNull InputStream stream) throws IOException {
+      super(stream, CharsetToolkit.UTF8_CHARSET, debugger); //TODO: correct encoding?
+      start(getClass().getName());
+    }
+
+    protected void onCommunicationError() {
+      synchronized (mySocketObject) {
+        if (myState == State.APPROVED) {
+          getDebugger().fireCommunicationError();
+        }
+      }
+    }
+  }
+}
diff --git a/python/pydevSrc/com/jetbrains/python/debugger/pydev/transport/DebuggerTransport.java b/python/pydevSrc/com/jetbrains/python/debugger/pydev/transport/DebuggerTransport.java
new file mode 100644 (file)
index 0000000..c2578d3
--- /dev/null
@@ -0,0 +1,23 @@
+package com.jetbrains.python.debugger.pydev.transport;
+
+import com.jetbrains.python.debugger.pydev.ProtocolFrame;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.IOException;
+
+/**
+ * @author Alexander Koshevoy
+ */
+public interface DebuggerTransport {
+  void waitForConnect() throws IOException;
+
+  void close();
+
+  boolean sendFrame(@NotNull ProtocolFrame frame);
+
+  boolean isConnected();
+
+  void disconnect();
+
+  void messageReceived(@NotNull ProtocolFrame frame);
+}
diff --git a/python/pydevSrc/com/jetbrains/python/debugger/pydev/transport/ServerModeDebuggerTransport.java b/python/pydevSrc/com/jetbrains/python/debugger/pydev/transport/ServerModeDebuggerTransport.java
new file mode 100644 (file)
index 0000000..74164e4
--- /dev/null
@@ -0,0 +1,128 @@
+package com.jetbrains.python.debugger.pydev.transport;
+
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.vfs.CharsetToolkit;
+import com.jetbrains.python.debugger.pydev.ProtocolFrame;
+import com.jetbrains.python.debugger.pydev.RemoteDebugger;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.ServerSocket;
+import java.net.Socket;
+
+/**
+ * @author Alexander Koshevoy
+ */
+public class ServerModeDebuggerTransport extends BaseDebuggerTransport {
+  private static final Logger LOG = Logger.getInstance(ServerModeDebuggerTransport.class);
+
+  @NotNull private final ServerSocket myServerSocket;
+  @Nullable private DebuggerReader myDebuggerReader;
+
+  private volatile boolean myConnected = false;
+  @Nullable private Socket mySocket;
+  private int myConnectionTimeout;
+
+  public ServerModeDebuggerTransport(RemoteDebugger debugger, @NotNull ServerSocket socket, int connectionTimeout) {
+    super(debugger);
+    myServerSocket = socket;
+    myConnectionTimeout = connectionTimeout;
+  }
+
+  @Override
+  public void waitForConnect() throws IOException {
+    synchronized (mySocketObject) {
+      //noinspection SocketOpenedButNotSafelyClosed
+      myServerSocket.setSoTimeout(myConnectionTimeout);
+
+      Socket socket = myServerSocket.accept();
+      try {
+        myDebuggerReader = new DebuggerReader(myDebugger, socket.getInputStream());
+      }
+      catch (IOException e) {
+        try {
+          socket.close();
+        }
+        catch (IOException ignore) {
+        }
+        throw e;
+      }
+      mySocket = socket;
+
+      // mySocket is closed in close() method on process termination
+    }
+  }
+
+  @Override
+  public void close() {
+    synchronized (mySocketObject) {
+      try {
+        if (myDebuggerReader != null) {
+          myDebuggerReader.stop();
+        }
+      }
+      finally {
+        if (!myServerSocket.isClosed()) {
+          try {
+            myServerSocket.close();
+          }
+          catch (IOException e) {
+            LOG.warn("Error closing socket", e);
+          }
+        }
+      }
+    }
+  }
+
+  @Override
+  public boolean isConnected() {
+    synchronized (mySocketObject) {
+      return myConnected && mySocket != null && !mySocket.isClosed();
+    }
+  }
+
+  @Override
+  public void disconnect() {
+    synchronized (mySocketObject) {
+      myConnected = false;
+
+      if (mySocket != null && !mySocket.isClosed()) {
+        try {
+          mySocket.close();
+        }
+        catch (IOException ignore) {
+        }
+      }
+    }
+  }
+
+  @Override
+  public void messageReceived(@NotNull ProtocolFrame frame) {
+    // do nothing
+  }
+
+  @Override
+  protected boolean sendMessageImpl(byte[] packed) throws IOException {
+    synchronized (mySocketObject) {
+      if (mySocket == null) {
+        return false;
+      }
+      final OutputStream os = mySocket.getOutputStream();
+      os.write(packed);
+      os.flush();
+      return true;
+    }
+  }
+
+  public static class DebuggerReader extends BaseDebuggerReader {
+    public DebuggerReader(@NotNull RemoteDebugger debugger, @NotNull InputStream stream) throws IOException {
+      super(stream, CharsetToolkit.UTF8_CHARSET, debugger); //TODO: correct encoding?
+      start(getClass().getName());
+    }
+
+    protected void onCommunicationError() {getDebugger().fireCommunicationError();}
+  }
+}
index 7c2d825b4d077705bbba880f4600940b872e4cb8..57df91bfea5e950a4ccd25f5c2bc0d176665dae3 100644 (file)
     <extensionPoint qualifiedName="Pythonid.pyCustomSdkUiProvider" interface="com.jetbrains.python.sdk.PyCustomSdkUiProvider"/>
     <extensionPoint qualifiedName="Pythonid.pep8ProblemSuppressor" interface="com.jetbrains.python.validation.Pep8ProblemSuppressor"/>
     <extensionPoint qualifiedName="Pythonid.pythonFlavorProvider" interface="com.jetbrains.python.sdk.flavors.PythonFlavorProvider"/>
+    <extensionPoint qualifiedName="Pythonid.debugSessionFactory" interface="com.jetbrains.python.debugger.PyDebugSessionFactory"/>
   </extensionPoints>
 
   <extensions defaultExtensionNs="Pythonid">
index 42b6d9c2fbe9a3a30bdd711f4c0c988bacf342b6..f16ee5c7987a972e88f08b036ed2b0f214a1a175 100644 (file)
@@ -117,18 +117,31 @@ public class PyDebugProcess extends XDebugProcess implements IPyDebugProcess, Pr
   private PyStackFrame myConsoleContextFrame = null;
   private PyReferrersLoader myReferrersProvider;
 
+  public PyDebugProcess(@NotNull XDebugSession session,
+                        @NotNull ServerSocket serverSocket,
+                        @NotNull ExecutionConsole executionConsole,
+                        @Nullable ProcessHandler processHandler, boolean multiProcess) {
+    this(session, multiProcess ? process -> process.createMultiprocessDebugger(serverSocket)
+                               : process -> new RemoteDebugger(process, serverSocket, process.getConnectTimeout()),
+         executionConsole, processHandler);
+  }
+
   public PyDebugProcess(final @NotNull XDebugSession session,
-                        @NotNull final ServerSocket serverSocket,
                         @NotNull final ExecutionConsole executionConsole,
-                        @Nullable final ProcessHandler processHandler, boolean multiProcess) {
+                        @Nullable final ProcessHandler processHandler,
+                        @NotNull String serverHost, int serverPort) {
+    this(session, process -> new ClientModeMultiProcessDebugger(process, serverHost, serverPort), executionConsole, processHandler);
+  }
+
+  private PyDebugProcess(@NotNull XDebugSession session,
+                        @NotNull DebuggerFactory debuggerFactory,
+                        @NotNull ExecutionConsole executionConsole,
+                        @Nullable ProcessHandler processHandler) {
     super(session);
+
     session.setPauseActionSupported(true);
-    if (multiProcess) {
-      myDebugger = createMultiprocessDebugger(serverSocket);
-    }
-    else {
-      myDebugger = new RemoteDebugger(this, serverSocket, getConnectTimeout());
-    }
+
+    myDebugger = debuggerFactory.createDebugger(this);
 
     List<XBreakpointHandler> breakpointHandlers = new ArrayList<>();
     breakpointHandlers.add(new PyLineBreakpointHandler(this));
@@ -1163,4 +1176,8 @@ public class PyDebugProcess extends XDebugProcess implements IPyDebugProcess, Pr
     }
     return pyType;
   }
+
+  private interface DebuggerFactory {
+    @NotNull ProcessDebugger createDebugger(@NotNull PyDebugProcess process);
+  }
 }
index 4418b39b293a1a7bb9cea4c40a4db24abf894109..b6e88780f2ba18a591e810e2ea89ea4801544ebd 100644 (file)
@@ -33,6 +33,7 @@ import com.intellij.openapi.roots.OrderRootType;
 import com.intellij.openapi.roots.ProjectRootManager;
 import com.intellij.openapi.util.text.StringUtil;
 import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.remote.RemoteSdkAdditionalData;
 import com.intellij.xdebugger.XDebugProcess;
 import com.intellij.xdebugger.XDebugProcessStarter;
 import com.intellij.xdebugger.XDebugSession;
@@ -69,6 +70,7 @@ public class PyDebugRunner extends GenericProgramRunner {
   public static final String PORT_PARAM = "--port";
   public static final String FILE_PARAM = "--file";
   public static final String MODULE_PARAM = "--module";
+  public static final String MULTIPROCESS_PARAM = "--multiprocess";
   public static final String IDE_PROJECT_ROOTS = "IDE_PROJECT_ROOTS";
   public static final String LIBRARY_ROOTS = "LIBRARY_ROOTS";
   public static final String PYTHON_ASYNCIO_DEBUG = "PYTHONASYNCIODEBUG";
@@ -121,6 +123,13 @@ public class PyDebugRunner extends GenericProgramRunner {
     FileDocumentManager.getInstance().saveAllDocuments();
 
     final PythonCommandLineState pyState = (PythonCommandLineState)state;
+
+    Sdk sdk = pyState.getSdk();
+    PyDebugSessionFactory sessionCreator = PyDebugSessionFactory.findExtension(sdk);
+    if (sessionCreator != null) {
+      return sessionCreator.createSession(this, pyState, environment);
+    }
+
     final ServerSocket serverSocket = PythonCommandLineState.createServerSocket();
     final int serverLocalPort = serverSocket.getLocalPort();
     RunProfile profile = environment.getRunProfile();
@@ -215,7 +224,7 @@ public class PyDebugRunner extends GenericProgramRunner {
   }
 
   @Nullable
-  private static CommandLinePatcher createRunConfigPatcher(RunProfileState state, RunProfile profile) {
+  public static CommandLinePatcher createRunConfigPatcher(RunProfileState state, RunProfile profile) {
     CommandLinePatcher runConfigPatcher = null;
     if (state instanceof PythonCommandLineState && profile instanceof AbstractPythonRunConfiguration) {
       runConfigPatcher = (AbstractPythonRunConfiguration)profile;
@@ -283,10 +292,10 @@ public class PyDebugRunner extends GenericProgramRunner {
   }
 
   private void fillDebugParameters(@NotNull Project project,
-                                          @NotNull ParamsGroup debugParams,
-                                          int serverLocalPort,
-                                          @NotNull PythonCommandLineState pyState,
-                                          @NotNull GeneralCommandLine cmd) {
+                                   @NotNull ParamsGroup debugParams,
+                                   int serverLocalPort,
+                                   @NotNull PythonCommandLineState pyState,
+                                   @NotNull GeneralCommandLine cmd) {
     PythonHelper.DEBUGGER.addToGroup(debugParams, cmd);
 
     configureDebugParameters(project, debugParams, pyState, cmd);
@@ -294,14 +303,7 @@ public class PyDebugRunner extends GenericProgramRunner {
 
     configureDebugEnvironment(project, cmd.getEnvironment());
 
-    final String[] debuggerArgs = new String[]{
-      CLIENT_PARAM, "127.0.0.1",
-      PORT_PARAM, String.valueOf(serverLocalPort),
-      FILE_PARAM
-    };
-    for (String s : debuggerArgs) {
-      debugParams.addParameter(s);
-    }
+    configureDebugConnectionParameters(debugParams, serverLocalPort);
   }
 
   public static void configureDebugEnvironment(@NotNull Project project, Map<String, String> environment) {
@@ -324,14 +326,19 @@ public class PyDebugRunner extends GenericProgramRunner {
   }
 
   protected void configureDebugParameters(@NotNull Project project,
-                                        @NotNull ParamsGroup debugParams,
-                                        @NotNull PythonCommandLineState pyState,
-                                        @NotNull GeneralCommandLine cmd) {
+                                          @NotNull ParamsGroup debugParams,
+                                          @NotNull PythonCommandLineState pyState,
+                                          @NotNull GeneralCommandLine cmd) {
     if (pyState.isMultiprocessDebug()) {
       //noinspection SpellCheckingInspection
       debugParams.addParameter("--multiproc");
     }
 
+    configureCommonDebugParameters(project, debugParams);
+  }
+
+  public static void configureCommonDebugParameters(@NotNull Project project,
+                                                    @NotNull ParamsGroup debugParams) {
     if (isModule) {
       debugParams.addParameter("--module");
     }
@@ -349,6 +356,17 @@ public class PyDebugRunner extends GenericProgramRunner {
     }
   }
 
+  private static void configureDebugConnectionParameters(@NotNull ParamsGroup debugParams, int serverLocalPort) {
+    final String[] debuggerArgs = new String[]{
+      CLIENT_PARAM, "127.0.0.1",
+      PORT_PARAM, String.valueOf(serverLocalPort),
+      FILE_PARAM
+    };
+    for (String s : debuggerArgs) {
+      debugParams.addParameter(s);
+    }
+  }
+
   private static void addProjectRootsToEnv(@NotNull Project project, @NotNull Map<String, String> environment) {
 
     List<String> roots = Lists.newArrayList();
diff --git a/python/src/com/jetbrains/python/debugger/PyDebugSessionFactory.java b/python/src/com/jetbrains/python/debugger/PyDebugSessionFactory.java
new file mode 100644 (file)
index 0000000..b0376ed
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * 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.jetbrains.python.debugger;
+
+import com.intellij.execution.ExecutionException;
+import com.intellij.execution.runners.ExecutionEnvironment;
+import com.intellij.openapi.extensions.ExtensionPointName;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.xdebugger.XDebugSession;
+import com.jetbrains.python.run.PythonCommandLineState;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author Alexander Koshevoy
+ */
+public abstract class PyDebugSessionFactory {
+  public static final ExtensionPointName<PyDebugSessionFactory> EP_NAME
+    = ExtensionPointName.create("Pythonid.debugSessionFactory");
+
+  protected abstract boolean appliesTo(@NotNull Sdk sdk);
+
+  @NotNull
+  public abstract XDebugSession createSession(@NotNull PyDebugRunner runner,
+                                              @NotNull PythonCommandLineState state,
+                                              @NotNull ExecutionEnvironment environment)
+    throws ExecutionException;
+
+  @Contract("null -> null")
+  @Nullable
+  public static PyDebugSessionFactory findExtension(@Nullable Sdk sdk) {
+    if (sdk == null) {
+      return null;
+    }
+    for (PyDebugSessionFactory sessionCreator : EP_NAME.getExtensions()) {
+      if (sessionCreator.appliesTo(sdk)) {
+        return sessionCreator;
+      }
+    }
+    return null;
+  }
+}
index d537318ea920db8603945d6d08d93ae8d06c679a..fb9d585f9942eb3fdc7f897256e881050cf658f4 100644 (file)
     <stacktrace.fold substring="at sun.rmi."/>
     <stacktrace.fold substring="at com.sun.proxy.$Proxy"/>
     <stacktrace.fold substring="at com.intellij.rt.execution."/>
-    <console.folding implementation="com.intellij.testFramework.FailedTestDebugLogConsoleFolding"/>
 
     <debuggerEditorTextProvider language="JAVA" implementationClass="com.intellij.debugger.impl.JavaEditorTextProviderImpl"/>
 
index 010162102ae67965fcc44c75c4d6f119bcb47ba7..76594d8099cbcfe813b7d23e65c6da31a4c274b9 100644 (file)
@@ -20,6 +20,7 @@ import com.intellij.javaee.ExternalResourceManager;
 import com.intellij.openapi.diagnostic.Logger;
 import com.intellij.openapi.project.DumbAware;
 import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.util.RecursionManager;
 import com.intellij.openapi.util.text.StringUtil;
 import com.intellij.psi.PsiElement;
 import com.intellij.psi.PsiFile;
@@ -547,7 +548,7 @@ public class XmlNSDescriptorImpl implements XmlNSDescriptorEx,Validator<XmlDocum
       return getTypeDescriptor(type, descriptorTag);
     }
 
-    return findTypeDescriptorImpl(descriptorTag, null, null, null);
+    return findTypeDescriptorImpl(descriptorTag, null, null);
   }
 
   @Override
@@ -587,54 +588,50 @@ public class XmlNSDescriptorImpl implements XmlNSDescriptorEx,Validator<XmlDocum
 
   @Nullable
   private TypeDescriptor findTypeDescriptor(String localName, String namespace) {
-    return findTypeDescriptorImpl(myTag, localName, namespace, null);
+    return findTypeDescriptorImpl(myTag, localName, namespace);
   }
 
   @Nullable
-  protected TypeDescriptor findTypeDescriptorImpl(XmlTag rootTag, final String name, String namespace, Set<XmlTag> visited) {
-    XmlNSDescriptorImpl responsibleDescriptor = this;
-    if (namespace != null && namespace.length() != 0 && !namespace.equals(getDefaultNamespace())) {
-      final XmlNSDescriptor nsDescriptor = rootTag.getNSDescriptor(namespace, true);
-
-      if (nsDescriptor instanceof XmlNSDescriptorImpl) {
-        responsibleDescriptor = (XmlNSDescriptorImpl)nsDescriptor;
+  protected TypeDescriptor findTypeDescriptorImpl(XmlTag rootTag, final String name, String namespace) {
+    return RecursionManager.createGuard("findDescriptor").doPreventingRecursion(rootTag, true, () -> {
+      XmlNSDescriptorImpl responsibleDescriptor = this;
+      if (namespace != null && namespace.length() != 0 && !namespace.equals(getDefaultNamespace())) {
+        final XmlNSDescriptor nsDescriptor = rootTag.getNSDescriptor(namespace, true);
+
+        if (nsDescriptor instanceof XmlNSDescriptorImpl) {
+          responsibleDescriptor = (XmlNSDescriptorImpl)nsDescriptor;
+        }
       }
-    }
-
-    if (responsibleDescriptor != this) {
-      return responsibleDescriptor.findTypeDescriptor(XmlUtil.findLocalNameByQualifiedName(name));
-    }
-
-    if (rootTag == null) return null;
-    if (visited != null) {
-      if (visited.contains(rootTag)) return null;
-      visited.add(rootTag);
-    }
-
-    final Pair<QNameKey, XmlTag> pair = Pair.create(new QNameKey(name, namespace), rootTag);
 
-    final CachedValue<TypeDescriptor> descriptor = myTypesMap.get(pair);
-    if(descriptor != null) {
-      TypeDescriptor value = descriptor.getValue();
-      if (value == null ||
-          ( value instanceof ComplexTypeDescriptor &&
-            ((ComplexTypeDescriptor)value).getDeclaration().isValid()
-          )
-         )
-      return value;
-    }
+      if (responsibleDescriptor != this) {
+        return responsibleDescriptor.findTypeDescriptor(XmlUtil.findLocalNameByQualifiedName(name));
+      }
 
-    XmlTag[] tags = rootTag.getSubTags();
+      if (rootTag == null) return null;
 
-    if (visited == null) {
-      visited = new HashSet<>(1);
-      visited.add(rootTag);
-    }
+      final Pair<QNameKey, XmlTag> pair = Pair.create(new QNameKey(name, namespace), rootTag);
+      final CachedValue<TypeDescriptor> descriptor = myTypesMap.get(pair);
+      if (descriptor != null) {
+        TypeDescriptor value = descriptor.getValue();
+        if (value == null ||
+            (value instanceof ComplexTypeDescriptor &&
+             ((ComplexTypeDescriptor)value).getDeclaration().isValid()
+            )
+          ) {
+          return value;
+        }
+      }
 
-    return doFindIn(tags, name, namespace, pair, rootTag, visited);
+      XmlTag[] tags = rootTag.getSubTags();
+      return doFindIn(tags, name, namespace, pair, rootTag);
+    });
   }
 
-  private TypeDescriptor doFindIn(final XmlTag[] tags, final String name, final String namespace, final Pair<QNameKey, XmlTag> pair, final XmlTag rootTag, final Set<XmlTag> visited) {
+  private TypeDescriptor doFindIn(final XmlTag[] tags,
+                                  final String name,
+                                  final String namespace,
+                                  final Pair<QNameKey, XmlTag> pair,
+                                  final XmlTag rootTag) {
     for (final XmlTag tag : tags) {
       if (equalsToSchemaName(tag, "complexType")) {
         if (name == null) {
@@ -664,8 +661,8 @@ public class XmlNSDescriptorImpl implements XmlNSDescriptorEx,Validator<XmlDocum
         }
       }
       else if (equalsToSchemaName(tag, INCLUDE_TAG_NAME) ||
-               ( equalsToSchemaName(tag, IMPORT_TAG_NAME) &&
-                 (namespace == null || !namespace.equals(getDefaultNamespace()))
+               (equalsToSchemaName(tag, IMPORT_TAG_NAME) &&
+                (namespace == null || !namespace.equals(getDefaultNamespace()))
                )
               ) {
         final String schemaLocation = tag.getAttributeValue("schemaLocation");
@@ -699,7 +696,7 @@ public class XmlNSDescriptorImpl implements XmlNSDescriptorEx,Validator<XmlDocum
 
                 final XmlTag rTag = document1.getRootTag();
 
-                final TypeDescriptor complexTypeDescriptor = nsDescriptor.findTypeDescriptorImpl(rTag, name, namespace, visited);
+                final TypeDescriptor complexTypeDescriptor = nsDescriptor.findTypeDescriptorImpl(rTag, name, namespace);
                 return new CachedValueProvider.Result<>(complexTypeDescriptor, rTag);
               }, false
               );
@@ -711,15 +708,16 @@ public class XmlNSDescriptorImpl implements XmlNSDescriptorEx,Validator<XmlDocum
             }
           }
         }
-      } else if (equalsToSchemaName(tag, REDEFINE_TAG_NAME)) {
+      }
+      else if (equalsToSchemaName(tag, REDEFINE_TAG_NAME)) {
         final XmlTag[] subTags = tag.getSubTags();
-        TypeDescriptor descriptor = doFindIn(subTags, name, namespace, pair, rootTag, visited);
+        TypeDescriptor descriptor = doFindIn(subTags, name, namespace, pair, rootTag);
         if (descriptor != null) return descriptor;
 
         final XmlNSDescriptorImpl nsDescriptor = getRedefinedElementDescriptor(tag);
         if (nsDescriptor != null) {
           final XmlTag redefinedRootTag = ((XmlDocument)nsDescriptor.getDeclaration()).getRootTag();
-          descriptor = doFindIn(redefinedRootTag.getSubTags(), name, namespace, pair, redefinedRootTag, visited);
+          descriptor = doFindIn(redefinedRootTag.getSubTags(), name, namespace, pair, redefinedRootTag);
           if (descriptor != null) return descriptor;
         }
       }