diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/Breakpoint.java b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/Breakpoint.java index 3ba029ce380d..43b214584b65 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/Breakpoint.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/Breakpoint.java @@ -32,7 +32,7 @@ import com.intellij.debugger.jdi.StackFrameProxyImpl; import com.intellij.debugger.jdi.ThreadReferenceProxyImpl; import com.intellij.debugger.requests.ClassPrepareRequestor; import com.intellij.debugger.settings.DebuggerSettings; -import com.intellij.debugger.ui.impl.watch.EvaluationDescriptor; +import com.intellij.debugger.ui.impl.watch.CompilingEvaluatorImpl; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Computable; @@ -245,7 +245,7 @@ public abstract class Breakpoint

implements } throw new EventProcessingException(title[0], ex.getMessage(), ex); - } + } return true; } @@ -269,7 +269,7 @@ public abstract class Breakpoint

implements SourcePosition position = ContextUtil.getSourcePosition(context); PsiElement element = ContextUtil.getContextElement(context, position); ExpressionEvaluator evaluator = DebuggerInvocationUtil.commitAndRunReadAction(myProject, () -> - createExpressionEvaluator(myProject, context, element, position, expressionToEvaluate, this::createLogMessageCodeFragment)); + createExpressionEvaluator(myProject, element, position, expressionToEvaluate, this::createLogMessageCodeFragment)); Value eval = evaluator.evaluate(context); buf.append(eval instanceof VoidValue ? "void" : DebuggerUtils.getValueAsString(context, eval)); } @@ -342,9 +342,8 @@ public abstract class Breakpoint

implements } try { - Project project = context.getProject(); SourcePosition contextSourcePosition = ContextUtil.getSourcePosition(context); - ExpressionEvaluator evaluator = DebuggerInvocationUtil.commitAndRunReadAction(project, () -> { + ExpressionEvaluator evaluator = DebuggerInvocationUtil.commitAndRunReadAction(myProject, () -> { // IMPORTANT: calculate context psi element basing on the location where the exception // has been hit, not on the location where it was set. (For line breakpoints these locations are the same, however, // for method, exception and field breakpoints these locations differ) @@ -352,7 +351,7 @@ public abstract class Breakpoint

implements if (contextPsiElement == null) { contextPsiElement = getEvaluationElement(); // as a last resort } - return createExpressionEvaluator(project, context, contextPsiElement, contextSourcePosition, getCondition(), + return createExpressionEvaluator(myProject, contextPsiElement, contextSourcePosition, getCondition(), this::createConditionCodeFragment); }); return DebuggerUtilsEx.evaluateBoolean(evaluator, context); @@ -368,7 +367,6 @@ public abstract class Breakpoint

implements } private static ExpressionEvaluator createExpressionEvaluator(Project project, - EvaluationContextImpl context, PsiElement contextPsiElement, SourcePosition contextSourcePosition, TextWithImports text, @@ -378,7 +376,7 @@ public abstract class Breakpoint

implements return EvaluatorBuilderImpl.build(text, contextPsiElement, contextSourcePosition, project); } catch (UnsupportedExpressionException ex) { - ExpressionEvaluator eval = EvaluationDescriptor.createCompilingEvaluator(context, contextPsiElement, fragmentFactory); + ExpressionEvaluator eval = CompilingEvaluatorImpl.create(project, contextPsiElement, fragmentFactory); if (eval != null) { return eval; } @@ -387,11 +385,11 @@ public abstract class Breakpoint

implements } private PsiCodeFragment createConditionCodeFragment(PsiElement context) { - return createCodeFragment(getProject(), getCondition(), context); + return createCodeFragment(myProject, getCondition(), context); } private PsiCodeFragment createLogMessageCodeFragment(PsiElement context) { - return createCodeFragment(getProject(), getLogMessage(), context); + return createCodeFragment(myProject, getLogMessage(), context); } private static PsiCodeFragment createCodeFragment(Project project, TextWithImports text, PsiElement context) { diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/CompilingEvaluator.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/CompilingEvaluator.java index 8a945476661c..6a7c25653a0e 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/CompilingEvaluator.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/CompilingEvaluator.java @@ -51,10 +51,12 @@ import java.util.Collection; * @author egor */ public abstract class CompilingEvaluator implements ExpressionEvaluator { + @NotNull protected final Project myProject; @NotNull protected final PsiElement myPsiContext; @NotNull protected final ExtractLightMethodObjectHandler.ExtractedData myData; - public CompilingEvaluator(@NotNull PsiElement context, @NotNull ExtractLightMethodObjectHandler.ExtractedData data) { + public CompilingEvaluator(@NotNull Project project, @NotNull PsiElement context, @NotNull ExtractLightMethodObjectHandler.ExtractedData data) { + myProject = project; myPsiContext = context; myData = data; } @@ -86,16 +88,15 @@ public abstract class CompilingEvaluator implements ExpressionEvaluator { try { // invoke base evaluator on call code - Project project = ApplicationManager.getApplication().runReadAction((Computable)myPsiContext::getProject); SourcePosition position = ContextUtil.getSourcePosition(evaluationContext); ExpressionEvaluator evaluator = - DebuggerInvocationUtil.commitAndRunReadAction(project, new EvaluatingComputable() { + DebuggerInvocationUtil.commitAndRunReadAction(myProject, new EvaluatingComputable() { @Override public ExpressionEvaluator compute() throws EvaluateException { TextWithImports callCode = getCallCode(); PsiElement copyContext = myData.getAnchor(); CodeFragmentFactory factory = DebuggerUtilsEx.findAppropriateCodeFragmentFactory(callCode, copyContext); - return factory.getEvaluatorBuilder().build(factory.createCodeFragment(callCode, copyContext, project), position); + return factory.getEvaluatorBuilder().build(factory.createCodeFragment(callCode, copyContext, myProject), position); } }); ((EvaluationContextImpl)evaluationContext).setClassLoader(classLoader); @@ -148,12 +149,8 @@ public abstract class CompilingEvaluator implements ExpressionEvaluator { protected String getGenClassQName() { - return ApplicationManager.getApplication().runReadAction(new Computable() { - @Override - public String compute() { - return JVMNameUtil.getNonAnonymousClassName(myData.getGeneratedInnerClass()); - } - }); + return ApplicationManager.getApplication().runReadAction( + (Computable)() -> JVMNameUtil.getNonAnonymousClassName(myData.getGeneratedInnerClass())); } ///////////////// Compiler stuff diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/CompilingEvaluatorImpl.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/CompilingEvaluatorImpl.java index e7445b0fc3c6..0fd2b1870129 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/CompilingEvaluatorImpl.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/CompilingEvaluatorImpl.java @@ -18,7 +18,7 @@ package com.intellij.debugger.ui.impl.watch; import com.intellij.compiler.CompilerConfiguration; import com.intellij.compiler.server.BuildManager; import com.intellij.debugger.engine.evaluation.EvaluateException; -import com.intellij.debugger.engine.evaluation.EvaluationContextImpl; +import com.intellij.debugger.engine.evaluation.expression.ExpressionEvaluator; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.compiler.ClassObject; import com.intellij.openapi.compiler.CompilationException; @@ -26,15 +26,20 @@ import com.intellij.openapi.compiler.CompilerManager; import com.intellij.openapi.compiler.CompilerMessageCategory; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleUtilCore; +import com.intellij.openapi.project.Project; import com.intellij.openapi.projectRoots.JavaSdkVersion; import com.intellij.openapi.projectRoots.Sdk; import com.intellij.openapi.roots.ModuleRootManager; import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.Pair; +import com.intellij.openapi.util.ThrowableComputable; import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.registry.Registry; import com.intellij.pom.java.LanguageLevel; +import com.intellij.psi.PsiCodeFragment; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; +import com.intellij.refactoring.extractMethod.PrepareFailedException; import com.intellij.refactoring.extractMethodObject.ExtractLightMethodObjectHandler; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -44,35 +49,31 @@ import org.jetbrains.jps.model.java.compiler.AnnotationProcessingConfiguration; import java.io.File; import java.io.IOException; import java.util.*; +import java.util.function.Function; // todo: consider batching compilations in order not to start a separate process for every class that needs to be compiled public class CompilingEvaluatorImpl extends CompilingEvaluator { - - private final EvaluationContextImpl myEvaluationContext; - - public CompilingEvaluatorImpl(EvaluationContextImpl evaluationContext, @NotNull PsiElement context, @NotNull ExtractLightMethodObjectHandler.ExtractedData data) { - super(context, data); - myEvaluationContext = evaluationContext; + public CompilingEvaluatorImpl(@NotNull Project project, + @NotNull PsiElement context, + @NotNull ExtractLightMethodObjectHandler.ExtractedData data) { + super(project, context, data); } @Override @NotNull protected Collection compile(@Nullable JavaSdkVersion debuggeeVersion) throws EvaluateException { - final Module module = ApplicationManager.getApplication().runReadAction(new Computable() { - @Override - public Module compute() { - return ModuleUtilCore.findModuleForPsiElement(myPsiContext); - } - }); - final List options = new ArrayList<>(); + Module module = ApplicationManager.getApplication().runReadAction( + (Computable)() -> ModuleUtilCore.findModuleForPsiElement(myPsiContext)); + List options = new ArrayList<>(); options.add("-encoding"); options.add("UTF-8"); - final List platformClasspath = new ArrayList<>(); - final List classpath = new ArrayList<>(); + List platformClasspath = new ArrayList<>(); + List classpath = new ArrayList<>(); AnnotationProcessingConfiguration profile = null; if (module != null) { - profile = CompilerConfiguration.getInstance(module.getProject()).getAnnotationProcessingConfiguration(module); - final ModuleRootManager rootManager = ModuleRootManager.getInstance(module); + assert myProject.equals(module.getProject()) : module + " is from another project"; + profile = CompilerConfiguration.getInstance(myProject).getAnnotationProcessingConfiguration(module); + ModuleRootManager rootManager = ModuleRootManager.getInstance(module); for (String s : rootManager.orderEntries().compileOnly().recursively().exportedOnly().withoutSdk().getPathsList().getPathList()) { classpath.add(new File(s)); } @@ -82,31 +83,31 @@ public class CompilingEvaluatorImpl extends CompilingEvaluator { } JavaBuilder.addAnnotationProcessingOptions(options, profile); - final Pair runtime = BuildManager.getJavacRuntimeSdk(myEvaluationContext.getProject()); - final JavaSdkVersion buildRuntimeVersion = runtime.getSecond(); + Pair runtime = BuildManager.getJavacRuntimeSdk(myProject); + JavaSdkVersion buildRuntimeVersion = runtime.getSecond(); // if compiler or debuggee version or both are unknown, let source and target be the compiler's defaults if (buildRuntimeVersion != null && debuggeeVersion != null) { - final JavaSdkVersion minVersion = buildRuntimeVersion.ordinal() > debuggeeVersion.ordinal() ? debuggeeVersion : buildRuntimeVersion; - final String sourceOption = getSourceOption(minVersion.getMaxLanguageLevel()); + JavaSdkVersion minVersion = buildRuntimeVersion.ordinal() > debuggeeVersion.ordinal() ? debuggeeVersion : buildRuntimeVersion; + String sourceOption = getSourceOption(minVersion.getMaxLanguageLevel()); options.add("-source"); options.add(sourceOption); options.add("-target"); options.add(sourceOption); } - final CompilerManager compilerManager = CompilerManager.getInstance(myEvaluationContext.getProject()); + CompilerManager compilerManager = CompilerManager.getInstance(myProject); File sourceFile = null; try { sourceFile = generateTempSourceFile(compilerManager.getJavacCompilerWorkingDir()); - final File srcDir = sourceFile.getParentFile(); - final List sourcePath = Collections.emptyList(); - final Set sources = Collections.singleton(sourceFile); + File srcDir = sourceFile.getParentFile(); + List sourcePath = Collections.emptyList(); + Set sources = Collections.singleton(sourceFile); return compilerManager.compileJavaCode(options, platformClasspath, classpath, Collections.emptyList(), sourcePath, sources, srcDir); } catch (CompilationException e) { - final StringBuilder res = new StringBuilder("Compilation failed:\n"); + StringBuilder res = new StringBuilder("Compilation failed:\n"); for (CompilationException.Message m : e.getMessages()) { if (m.getCategory() == CompilerMessageCategory.ERROR) { res.append(m.getText()).append("\n"); @@ -130,7 +131,7 @@ public class CompilingEvaluatorImpl extends CompilingEvaluator { } private File generateTempSourceFile(File workingDir) throws IOException { - final Pair fileData = ApplicationManager.getApplication().runReadAction((Computable>)() -> { + Pair fileData = ApplicationManager.getApplication().runReadAction((Computable>)() -> { PsiFile file = myData.getGeneratedInnerClass().getContainingFile(); return Pair.create(file.getName(), file.getText()); }); @@ -140,8 +141,34 @@ public class CompilingEvaluatorImpl extends CompilingEvaluator { if (fileData.second == null) { throw new IOException("Class source code not specified"); } - final File file = new File(workingDir, "debugger/src/"+fileData.first); + File file = new File(workingDir, "debugger/src/" + fileData.first); FileUtil.writeToFile(file, fileData.second); return file; } + + @Nullable + public static ExpressionEvaluator create(@NotNull Project project, + @Nullable PsiElement psiContext, + @NotNull Function fragmentFactory) + throws EvaluateException { + if (Registry.is("debugger.compiling.evaluator") && psiContext != null) { + return ApplicationManager.getApplication().runReadAction((ThrowableComputable)() -> { + try { + ExtractLightMethodObjectHandler.ExtractedData data = ExtractLightMethodObjectHandler.extractLightMethodObject( + project, + psiContext.getContainingFile(), + fragmentFactory.apply(psiContext), + getGeneratedClassName()); + if (data != null) { + return new CompilingEvaluatorImpl(project, psiContext, data); + } + } + catch (PrepareFailedException e) { + NodeDescriptorImpl.LOG.info(e); + } + return null; + }); + } + return null; + } } diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/EvaluationDescriptor.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/EvaluationDescriptor.java index e9f4592b5e9f..a84617b9aabe 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/EvaluationDescriptor.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/EvaluationDescriptor.java @@ -30,20 +30,16 @@ import com.intellij.debugger.engine.evaluation.expression.UnsupportedExpressionE import com.intellij.debugger.impl.DebuggerContextImpl; import com.intellij.debugger.impl.DebuggerUtilsEx; import com.intellij.debugger.jdi.StackFrameProxyImpl; -import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.ThrowableComputable; -import com.intellij.openapi.util.registry.Registry; -import com.intellij.psi.*; -import com.intellij.refactoring.extractMethod.PrepareFailedException; -import com.intellij.refactoring.extractMethodObject.ExtractLightMethodObjectHandler; +import com.intellij.psi.PsiCodeFragment; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiExpression; +import com.intellij.psi.PsiExpressionCodeFragment; import com.intellij.xdebugger.frame.XValueModifier; import com.sun.jdi.*; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.function.Function; - /** * @author lex */ @@ -71,25 +67,26 @@ public abstract class EvaluationDescriptor extends ValueDescriptorImpl { return DebuggerUtilsEx.findAppropriateCodeFragmentFactory(text, context).createCodeFragment(text, context, myProject); } - public final Value calcValue(final EvaluationContextImpl evaluationContext) throws EvaluateException { + public final Value calcValue(EvaluationContextImpl evaluationContext) throws EvaluateException { try { - final EvaluationContextImpl thisEvaluationContext = getEvaluationContext(evaluationContext); + EvaluationContextImpl thisEvaluationContext = getEvaluationContext(evaluationContext); SourcePosition position = ContextUtil.getSourcePosition(evaluationContext); PsiElement psiContext = ContextUtil.getContextElement(evaluationContext, position); - ExpressionEvaluator evaluator; - try { - evaluator = DebuggerInvocationUtil.commitAndRunReadAction(myProject, () -> - DebuggerUtilsEx.findAppropriateCodeFragmentFactory(getEvaluationText(), psiContext) + ExpressionEvaluator evaluator = DebuggerInvocationUtil.commitAndRunReadAction(myProject, () -> { + try { + return DebuggerUtilsEx.findAppropriateCodeFragmentFactory(getEvaluationText(), psiContext) .getEvaluatorBuilder() - .build(getEvaluationCode(thisEvaluationContext), position)); - } - catch (UnsupportedExpressionException ex) { - evaluator = createCompilingEvaluator(evaluationContext, psiContext, this::createCodeFragment); - if (evaluator == null) { + .build(getEvaluationCode(thisEvaluationContext), position); + } + catch (UnsupportedExpressionException ex) { + ExpressionEvaluator eval = CompilingEvaluatorImpl.create(myProject, psiContext, this::createCodeFragment); + if (eval != null) { + return eval; + } throw ex; } - } + }); if (!thisEvaluationContext.getDebugProcess().isAttached()) { throw EvaluateExceptionUtil.PROCESS_EXITED; @@ -115,32 +112,6 @@ public abstract class EvaluationDescriptor extends ValueDescriptorImpl { } } - @Nullable - public static ExpressionEvaluator createCompilingEvaluator(EvaluationContextImpl evaluationContext, - @Nullable PsiElement psiContext, - Function fragmentFactory) - throws EvaluateException { - if (Registry.is("debugger.compiling.evaluator") && psiContext != null) { - return ApplicationManager.getApplication().runReadAction((ThrowableComputable)() -> { - try { - ExtractLightMethodObjectHandler.ExtractedData data = ExtractLightMethodObjectHandler.extractLightMethodObject( - evaluationContext.getProject(), - psiContext.getContainingFile(), - fragmentFactory.apply(psiContext), - CompilingEvaluator.getGeneratedClassName()); - if (data != null) { - return new CompilingEvaluatorImpl(evaluationContext, psiContext, data); - } - } - catch (PrepareFailedException e) { - LOG.info(e); - } - return null; - }); - } - return null; - } - public PsiExpression getDescriptorEvaluation(DebuggerContext context) throws EvaluateException { PsiElement evaluationCode = getEvaluationCode(context); if (evaluationCode instanceof PsiExpressionCodeFragment) { diff --git a/java/execution/impl/src/com/intellij/execution/junit/JUnitUtil.java b/java/execution/impl/src/com/intellij/execution/junit/JUnitUtil.java index d13e4ee1240a..5d715ed42399 100644 --- a/java/execution/impl/src/com/intellij/execution/junit/JUnitUtil.java +++ b/java/execution/impl/src/com/intellij/execution/junit/JUnitUtil.java @@ -16,7 +16,6 @@ package com.intellij.execution.junit; import com.intellij.codeInsight.AnnotationUtil; -import com.intellij.codeInsight.MetaAnnotationUtil; import com.intellij.codeInsight.TestFrameworks; import com.intellij.execution.*; import com.intellij.execution.junit2.info.MethodLocation; @@ -26,19 +25,22 @@ import com.intellij.openapi.module.ModuleUtilCore; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Condition; +import com.intellij.openapi.util.Key; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.*; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.search.searches.ClassInheritorsSearch; import com.intellij.psi.util.*; import com.intellij.testIntegration.JavaTestFramework; import com.intellij.testIntegration.TestFramework; +import com.intellij.util.ConcurrencyUtil; +import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.*; -import java.util.stream.Collectors; -import java.util.stream.Stream; +import java.util.concurrent.ConcurrentMap; @SuppressWarnings({"UtilityClassWithoutPrivateConstructor"}) public class JUnitUtil { @@ -223,11 +225,11 @@ public class JUnitUtil { if (module != null) { for (final PsiMethod method : psiClass.getAllMethods()) { ProgressManager.checkCanceled(); - if (isMetaAnnotated(method, TEST5_ANNOTATIONS, module)) return true; + if (isMetaAnnotated(method, TEST5_ANNOTATIONS)) return true; } for (PsiClass aClass : psiClass.getInnerClasses()) { - if (isMetaAnnotated(aClass, JUNIT5_NESTED, module)) return true; + if (isMetaAnnotated(aClass, Collections.singleton(JUNIT5_NESTED))) return true; } } @@ -260,23 +262,62 @@ public class JUnitUtil { return true; } - Module module = ModuleUtilCore.findModuleForPsiElement(method); - return module != null && isMetaAnnotated(method, TEST5_ANNOTATIONS, module); + return isMetaAnnotated(method, TEST5_ANNOTATIONS); } - private static boolean isMetaAnnotated(PsiModifierListOwner owner, final Collection metaAnnotations, final Module module) { - for (String annotation : metaAnnotations) { - if (isMetaAnnotated(owner, annotation, module)) return true; + private static final ConcurrentMap> annotationsKeyForProvider = ContainerUtil.newConcurrentMap(); + @NotNull + private static Key> getKeyForAnnotations(Collection annotations) { + String name = StringUtil.join(annotations, ", "); + Key key = annotationsKeyForProvider.get(name); + if (key == null) { + key = ConcurrencyUtil.cacheOrGet(annotationsKeyForProvider, name, Key.create(name)); + } + //noinspection unchecked + return (Key)key; + } + + private static boolean isMetaAnnotated(PsiModifierListOwner listOwner, final Collection annotations) { + if (AnnotationUtil.isAnnotated(listOwner, annotations, false)) { + return true; + } + PsiModifierList modifierList = listOwner.getModifierList(); + if (modifierList != null) { + for (PsiAnnotation annotation : modifierList.getApplicableAnnotations()) { + PsiJavaCodeReferenceElement nameReferenceElement = annotation.getNameReferenceElement(); + if (nameReferenceElement != null) { + PsiElement resolve = nameReferenceElement.resolve(); + if (resolve instanceof PsiClass) { + Boolean annotated = CachedValuesManager.getManager(listOwner.getProject()).getCachedValue(resolve, getKeyForAnnotations(annotations), () -> new CachedValueProvider.Result<>( + isTestAnnotatedAnnotation((PsiClass)resolve, new HashSet<>(), annotations), PsiModificationTracker.JAVA_STRUCTURE_MODIFICATION_COUNT), false); + if (annotated != null && annotated) return true; + } + } + } } return false; } - private static boolean isMetaAnnotated(PsiModifierListOwner owner, String annotation, Module module) { - Collection annotations = MetaAnnotationUtil.getAnnotationTypesWithChildren(module, annotation, true); - Stream qualifiedNames = annotations.stream().map(psiClass -> psiClass.getQualifiedName()); - if (AnnotationUtil.isAnnotated(owner, qualifiedNames.collect(Collectors.toSet()), false)) { + + private static boolean isTestAnnotatedAnnotation(PsiClass aClass, final Set visited, final Collection annotations) { + if (AnnotationUtil.isAnnotated(aClass, annotations, false)) { return true; } + PsiModifierList modifierList = aClass.getModifierList(); + if (modifierList != null) { + for (PsiAnnotation psiAnnotation : modifierList.getApplicableAnnotations()) { + PsiJavaCodeReferenceElement nameReferenceElement = psiAnnotation.getNameReferenceElement(); + if (nameReferenceElement != null) { + PsiElement resolve = nameReferenceElement.resolve(); + if (resolve instanceof PsiClass && visited.add((PsiClass)resolve) && + ((PsiClass)resolve).isAnnotationType() && + isTestAnnotatedAnnotation((PsiClass)resolve, visited, annotations)) { + return true; + } + } + } + } + return false; } diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/JavaModuleGraphUtil.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/JavaModuleGraphUtil.java index 3aaad5acea97..210616c7ab05 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/JavaModuleGraphUtil.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/JavaModuleGraphUtil.java @@ -51,13 +51,13 @@ public class JavaModuleGraphUtil { public static Collection findCycle(@NotNull PsiJavaModule module) { Project project = module.getProject(); List> cycles = CachedValuesManager.getManager(project).getCachedValue(project, () -> - Result.create(findCycles(project), JavaModuleFileChangeTracker.getInstance(project))); + Result.create(findCycles(project), JavaModuleFileChangeTracker.getDependencies(project))); return ContainerUtil.find(cycles, set -> set.contains(module)); } public static boolean exports(@NotNull PsiJavaModule source, @NotNull String packageName, @NotNull PsiJavaModule target) { Map> exports = CachedValuesManager.getCachedValue(source, () -> - Result.create(exportsMap(source), JavaModuleFileChangeTracker.getInstance(source.getProject()))); + Result.create(exportsMap(source), JavaModuleFileChangeTracker.getDependencies(source.getProject()))); Set targets = exports.get(packageName); return targets != null && (targets.isEmpty() || targets.contains(target.getModuleName())); } @@ -65,7 +65,7 @@ public class JavaModuleGraphUtil { public static boolean reads(@NotNull PsiJavaModule source, @NotNull PsiJavaModule destination) { Project project = source.getProject(); RequiresGraph graph = CachedValuesManager.getManager(project).getCachedValue(project, () -> - Result.create(buildRequiresGraph(project), JavaModuleFileChangeTracker.getInstance(project))); + Result.create(buildRequiresGraph(project), JavaModuleFileChangeTracker.getDependencies(project))); return graph.reads(source, destination); } diff --git a/java/java-impl/src/com/intellij/codeInsight/hints/JavaInlayParameterHintsProvider.kt b/java/java-impl/src/com/intellij/codeInsight/hints/JavaInlayParameterHintsProvider.kt index 2cd887426c2a..fbc0fbecd63e 100644 --- a/java/java-impl/src/com/intellij/codeInsight/hints/JavaInlayParameterHintsProvider.kt +++ b/java/java-impl/src/com/intellij/codeInsight/hints/JavaInlayParameterHintsProvider.kt @@ -15,8 +15,6 @@ */ package com.intellij.codeInsight.hints -import com.intellij.lang.Language -import com.intellij.lang.java.JavaLanguage import com.intellij.psi.PsiCallExpression import com.intellij.psi.PsiElement import com.intellij.psi.PsiMethod @@ -48,8 +46,6 @@ class JavaInlayParameterHintsProvider : InlayParameterHintsProvider { return MethodInfo(fullMethodName, paramNames) } - override val language: Language = JavaLanguage.INSTANCE - override val defaultBlackList = setOf( "(begin*, end*)", "(start*, end*)", diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/light/LightJavaModule.java b/java/java-psi-impl/src/com/intellij/psi/impl/light/LightJavaModule.java index 72464a05e4d9..4283a0415e73 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/light/LightJavaModule.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/light/LightJavaModule.java @@ -150,7 +150,7 @@ public class LightJavaModule extends LightElement implements PsiJavaModule { @Override public Result compute(Pair p) { LightJavaModule module = new LightJavaModule(p.first, p.second); - return Result.create(module, JavaModuleFileChangeTracker.getInstance(p.first.getProject())); + return Result.create(module, JavaModuleFileChangeTracker.getDependencies(p.first.getProject())); } }, false, pair(manager, jarRoot)); } diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/JavaModuleFileChangeTracker.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/JavaModuleFileChangeTracker.java index 8ebd1854d11d..a3f347e26d93 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/JavaModuleFileChangeTracker.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/JavaModuleFileChangeTracker.java @@ -16,57 +16,14 @@ package com.intellij.psi.impl.source; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.ModificationTracker; -import com.intellij.openapi.util.NotNullLazyKey; -import com.intellij.psi.*; -import com.intellij.psi.PsiTreeChangeEvent; -import com.intellij.util.NotNullFunction; +import com.intellij.openapi.roots.ProjectRootModificationTracker; import org.jetbrains.annotations.NotNull; -import static com.intellij.psi.PsiTreeChangeEvent.*; - -public class JavaModuleFileChangeTracker implements ModificationTracker { - private static final NotNullLazyKey KEY = NotNullLazyKey.create("", new NotNullFunction() { - @NotNull - @Override - public ModificationTracker fun(Project project) { - return new JavaModuleFileChangeTracker(project); - } - }); +import static com.intellij.psi.util.PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT; +public class JavaModuleFileChangeTracker { @NotNull - public static ModificationTracker getInstance(@NotNull Project p) { - return KEY.getValue(p); - } - - private volatile long myCount = 0; - - private JavaModuleFileChangeTracker(Project project) { - PsiManager.getInstance(project).addPsiTreeChangeListener(new PsiTreeChangeAdapter() { - @Override public void childAdded(@NotNull PsiTreeChangeEvent event) { process(event.getFile()); } - @Override public void childRemoved(@NotNull PsiTreeChangeEvent event) { process(event.getFile()); } - @Override public void childReplaced(@NotNull PsiTreeChangeEvent event) { process(event.getFile()); } - @Override public void childMoved(@NotNull PsiTreeChangeEvent event) { process(event.getFile()); } - @Override public void childrenChanged(@NotNull PsiTreeChangeEvent event) { process(event.getFile()); } - - private void process(PsiFile file) { - if (file != null && PsiJavaModule.MODULE_INFO_FILE.equals(file.getName())) { - myCount++; - } - } - - @Override - public void propertyChanged(@NotNull PsiTreeChangeEvent event) { - String name = event.getPropertyName(); - if (name == PROP_FILE_NAME || name == PROP_DIRECTORY_NAME || name == PROP_ROOTS) { - myCount++; - } - } - }, project); - } - - @Override - public long getModificationCount() { - return myCount; + public static Object[] getDependencies(@NotNull Project project) { + return new Object[]{OUT_OF_CODE_BLOCK_MODIFICATION_COUNT, ProjectRootModificationTracker.getInstance(project)}; } } \ No newline at end of file diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiJavaModuleReference.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiJavaModuleReference.java index 38dce76f92d7..d61db2d5317d 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiJavaModuleReference.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiJavaModuleReference.java @@ -137,7 +137,7 @@ public class PsiJavaModuleReference extends PsiReferenceBase.Poly compute(Pair p) { Collection modules = Resolver.findModules(refOwner.getContainingFile(), p.first, p.second); PsiJavaModule module = modules.size() == 1 ? modules.iterator().next() : null; - return Result.create(module, JavaModuleFileChangeTracker.getInstance(refOwner.getProject())); + return Result.create(module, JavaModuleFileChangeTracker.getDependencies(refOwner.getProject())); } }, false, pair(refText, incompleteCode)); } diff --git a/lib/required_for_dist.txt b/lib/required_for_dist.txt index 18be950a6b8e..bf43ee4533da 100644 --- a/lib/required_for_dist.txt +++ b/lib/required_for_dist.txt @@ -3,6 +3,7 @@ automaton.jar cglib-nodep-3.2.4.jar cli-parser-1.1.jar commons-codec-1.9.jar +commons-compress-1.10.jar commons-httpclient-3.1-patched.jar commons-logging-1.2.jar commons-net-3.3.jar diff --git a/platform/analysis-impl/src/com/intellij/codeInsight/daemon/impl/HighlightingSessionImpl.java b/platform/analysis-impl/src/com/intellij/codeInsight/daemon/impl/HighlightingSessionImpl.java index fc96bd5347b5..16d7f4548085 100644 --- a/platform/analysis-impl/src/com/intellij/codeInsight/daemon/impl/HighlightingSessionImpl.java +++ b/platform/analysis-impl/src/com/intellij/codeInsight/daemon/impl/HighlightingSessionImpl.java @@ -23,13 +23,11 @@ import com.intellij.openapi.editor.colors.EditorColorsScheme; import com.intellij.openapi.editor.ex.RangeHighlighterEx; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.TextRange; import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiFile; import com.intellij.util.ConcurrencyUtil; -import com.intellij.util.Processor; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.TransferToEDTQueue; import gnu.trove.THashMap; @@ -47,6 +45,8 @@ public class HighlightingSessionImpl implements HighlightingSession { @NotNull private final Project myProject; private final Document myDocument; private final Map myRanges2markersCache = new THashMap<>(); + private final TransferToEDTQueue myAddHighlighterInEDTQueue; + private final TransferToEDTQueue myDisposeHighlighterInEDTQueue; private HighlightingSessionImpl(@NotNull PsiFile psiFile, @Nullable Editor editor, @@ -58,6 +58,18 @@ public class HighlightingSessionImpl implements HighlightingSession { myEditorColorsScheme = editorColorsScheme; myProject = psiFile.getProject(); myDocument = PsiDocumentManager.getInstance(myProject).getDocument(psiFile); + myDisposeHighlighterInEDTQueue = new TransferToEDTQueue<>("Dispose abandoned highlighter", highlighter -> { + highlighter.dispose(); + return true; + }, o -> myProject.isDisposed() || getProgressIndicator().isCanceled(), 200); + myAddHighlighterInEDTQueue = new TransferToEDTQueue<>("Apply highlighting results", info -> { + final EditorColorsScheme colorsScheme = getColorsScheme(); + UpdateHighlightersUtil.addHighlighterToEditorIncrementally(myProject, getDocument(), getPsiFile(), info.myRestrictRange.getStartOffset(), + info.myRestrictRange.getEndOffset(), + info.myInfo, colorsScheme, info.myGroupId, myRanges2markersCache); + + return true; + }, o -> myProject.isDisposed() || getProgressIndicator().isCanceled(), 200); } private static final Key> HIGHLIGHTING_SESSION = Key.create("HIGHLIGHTING_SESSION"); @@ -119,39 +131,6 @@ public class HighlightingSessionImpl implements HighlightingSession { return myEditorColorsScheme; } - private final TransferToEDTQueue myAddHighlighterInEDTQueue = - new TransferToEDTQueue<>("Apply highlighting results", new Processor() { - @Override - public boolean process(Info info) { - final EditorColorsScheme colorsScheme = getColorsScheme(); - UpdateHighlightersUtil - .addHighlighterToEditorIncrementally(myProject, getDocument(), getPsiFile(), info.myRestrictRange.getStartOffset(), - info.myRestrictRange.getEndOffset(), - info.myInfo, colorsScheme, info.myGroupId, myRanges2markersCache); - - return true; - } - }, new Condition() { - @Override - public boolean value(Object o) { - return myProject.isDisposed() || getProgressIndicator().isCanceled(); - } - }, 200); - private final TransferToEDTQueue myDisposeHighlighterInEDTQueue = - new TransferToEDTQueue<>("Dispose abandoned highlighter", new Processor() { - @Override - public boolean process(@NotNull RangeHighlighterEx highlighter) { - highlighter.dispose(); - return true; - } - }, new Condition() { - @Override - public boolean value(Object o) { - return myProject.isDisposed() || getProgressIndicator().isCanceled(); - } - }, 200); - - void queueHighlightInfo(@NotNull HighlightInfo info, @NotNull TextRange priorityRange, @NotNull TextRange restrictedRange, diff --git a/platform/analysis-impl/src/com/intellij/codeInsight/daemon/impl/ProgressableTextEditorHighlightingPass.java b/platform/analysis-impl/src/com/intellij/codeInsight/daemon/impl/ProgressableTextEditorHighlightingPass.java index 32861ec99a6a..564dac79f821 100644 --- a/platform/analysis-impl/src/com/intellij/codeInsight/daemon/impl/ProgressableTextEditorHighlightingPass.java +++ b/platform/analysis-impl/src/com/intellij/codeInsight/daemon/impl/ProgressableTextEditorHighlightingPass.java @@ -17,6 +17,7 @@ package com.intellij.codeInsight.daemon.impl; import com.intellij.codeHighlighting.TextEditorHighlightingPass; +import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.progress.ProgressIndicator; @@ -151,8 +152,16 @@ public abstract class ProgressableTextEditorHighlightingPass extends TextEditorH } } + void waitForHighlightInfosApplied() { + ApplicationManager.getApplication().assertIsDispatchThread(); + HighlightingSessionImpl session = (HighlightingSessionImpl)myHighlightingSession; + if (session != null) { + session.waitForHighlightInfosApplied(); + } + } + static class EmptyPass extends TextEditorHighlightingPass { - public EmptyPass(final Project project, @Nullable final Document document) { + EmptyPass(final Project project, @Nullable final Document document) { super(project, document, false); } diff --git a/platform/build-scripts/groovy/org/jetbrains/intellij/build/CommunityLibraryLicenses.groovy b/platform/build-scripts/groovy/org/jetbrains/intellij/build/CommunityLibraryLicenses.groovy index d54b19666b5b..517eefe1f447 100644 --- a/platform/build-scripts/groovy/org/jetbrains/intellij/build/CommunityLibraryLicenses.groovy +++ b/platform/build-scripts/groovy/org/jetbrains/intellij/build/CommunityLibraryLicenses.groovy @@ -64,6 +64,8 @@ class CommunityLibraryLicenses { license: "Apache 2.0", url: "http://hc.apache.org/httpclient-3.x"), new LibraryLicense(name: "Apache Commons IO", libraryName: "commons-io-1.4.jar", version: "1.4", license: "Apache 2.0", url: "http://commons.apache.org/io/", licenseUrl: "http://www.apache.org/licenses/LICENSE-2.0.txt"), + new LibraryLicense(name: "Apache Commons Lang", libraryName: "commons-lang", version: "2.6", license: "Apache 2.0", + licenseUrl: "http://www.apache.org/licenses/LICENSE-2.0.txt", url: "http://commons.apache.org/proper/commons-lang/"), new LibraryLicense(name: "Apache Commons Lang 3", libraryName: "commons-lang3-3.2.1.jar", version: "3.2.1", license: "Apache 2.0", licenseUrl: "http://www.apache.org/licenses/LICENSE-2.0.txt", url: "http://commons.apache.org/proper/commons-lang/"), new LibraryLicense(name: "Apache Commons Logging", libraryName: "commons-logging", version: "1.1.1", license: "Apache 2.0", diff --git a/platform/lang-api/src/com/intellij/codeInsight/hints/InlayParameterHintsProvider.kt b/platform/lang-api/src/com/intellij/codeInsight/hints/InlayParameterHintsProvider.kt index 6d9dc91dd9bd..e04cd417f15f 100644 --- a/platform/lang-api/src/com/intellij/codeInsight/hints/InlayParameterHintsProvider.kt +++ b/platform/lang-api/src/com/intellij/codeInsight/hints/InlayParameterHintsProvider.kt @@ -15,7 +15,6 @@ */ package com.intellij.codeInsight.hints -import com.intellij.lang.Language import com.intellij.lang.LanguageExtension import com.intellij.psi.PsiElement import org.jetbrains.annotations.ApiStatus @@ -36,13 +35,7 @@ interface InlayParameterHintsProvider { * and selects "Do not show for this method". */ fun getMethodInfo(element: PsiElement): MethodInfo? = null - - /** - * Language used when saving blacklist methods - * Maybe will be moved to MethodInfo - */ - val language: Language - + /** * Default list of methods for which hints should not be shown */ diff --git a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/DefaultHighlightInfoProcessor.java b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/DefaultHighlightInfoProcessor.java index 706582e1d4a1..d5029ddf7ca1 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/DefaultHighlightInfoProcessor.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/DefaultHighlightInfoProcessor.java @@ -33,7 +33,6 @@ import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiFile; import com.intellij.psi.util.PsiUtilBase; import com.intellij.util.Alarm; -import com.intellij.util.Processor; import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; diff --git a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/PassExecutorService.java b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/PassExecutorService.java index 10ffb48c7a8f..57db3cd614a4 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/PassExecutorService.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/PassExecutorService.java @@ -517,6 +517,9 @@ class PassExecutorService implements Disposable { throw new RuntimeException(message, e); } if (threadsToStartCountdown.decrementAndGet() == 0) { + if (pass instanceof ProgressableTextEditorHighlightingPass) { + ((ProgressableTextEditorHighlightingPass)pass).waitForHighlightInfosApplied(); + } log(updateProgress, pass, "Stopping "); updateProgress.stopIfRunning(); } @@ -543,7 +546,7 @@ class PassExecutorService implements Disposable { } private static void sortById(@NotNull List result) { - ContainerUtil.quickSort(result, (o1, o2) -> o1.getId() - o2.getId()); + ContainerUtil.quickSort(result, Comparator.comparingInt(TextEditorHighlightingPass::getId)); } private static int getThreadNum() { diff --git a/platform/lang-impl/src/com/intellij/codeInsight/hints/ParameterHintsPassFactory.java b/platform/lang-impl/src/com/intellij/codeInsight/hints/ParameterHintsPassFactory.java index fd49d5736ed5..f34b61e7db29 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/hints/ParameterHintsPassFactory.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/hints/ParameterHintsPassFactory.java @@ -22,7 +22,9 @@ import com.intellij.codeHighlighting.TextEditorHighlightingPassRegistrar; import com.intellij.codeInsight.daemon.impl.ParameterHintsPresentationManager; import com.intellij.codeInsight.hints.filtering.Matcher; import com.intellij.codeInsight.hints.filtering.MatcherConstructor; +import com.intellij.codeInsight.hints.settings.Diff; import com.intellij.codeInsight.hints.settings.ParameterNameHintsSettings; +import com.intellij.lang.Language; import com.intellij.openapi.components.AbstractProjectComponent; import com.intellij.openapi.editor.Caret; import com.intellij.openapi.editor.Editor; @@ -71,12 +73,14 @@ public class ParameterHintsPassFactory extends AbstractProjectComponent implemen myAnnotations.clear(); if (!isEnabled()) return; - InlayParameterHintsProvider provider = InlayParameterHintsExtension.INSTANCE.forLanguage(myFile.getLanguage()); + Language language = myFile.getLanguage(); + InlayParameterHintsProvider provider = InlayParameterHintsExtension.INSTANCE.forLanguage(language); if (provider == null) return; - List matchers = ParameterNameHintsSettings - .getInstance() - .getIgnorePatternSet(provider) + Diff diff = ParameterNameHintsSettings.getInstance().getBlackListDiff(language); + Set blackList = diff.applyOn(provider.getDefaultBlackList()); + + List matchers = blackList .stream() .map((item) -> MatcherConstructor.INSTANCE.createMatcher(item)) .collect(Collectors.toList()); diff --git a/platform/lang-impl/src/com/intellij/codeInsight/hints/PopupActions.kt b/platform/lang-impl/src/com/intellij/codeInsight/hints/PopupActions.kt index 1d4e224bd6ab..cbbe24034532 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/hints/PopupActions.kt +++ b/platform/lang-impl/src/com/intellij/codeInsight/hints/PopupActions.kt @@ -50,7 +50,7 @@ class ShowParameterHintsSettings : AnAction() { val project = CommonDataKeys.PROJECT.getData(e.dataContext) ?: return val file = CommonDataKeys.PSI_FILE.getData(e.dataContext) ?: return val hintExtension = InlayParameterHintsExtension.forLanguage(file.language) ?: return - val dialog = ParameterNameHintsConfigurable(project, hintExtension) + val dialog = ParameterNameHintsConfigurable(project, hintExtension.defaultBlackList, file.language) dialog.show() } } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/hints/settings/ParameterNameHintsConfigurable.java b/platform/lang-impl/src/com/intellij/codeInsight/hints/settings/ParameterNameHintsConfigurable.java index fc19484b00b4..fad2ac7c7039 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/hints/settings/ParameterNameHintsConfigurable.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/hints/settings/ParameterNameHintsConfigurable.java @@ -15,8 +15,8 @@ */ package com.intellij.codeInsight.hints.settings; -import com.intellij.codeInsight.hints.InlayParameterHintsProvider; import com.intellij.codeInsight.hints.filtering.MatcherConstructor; +import com.intellij.lang.Language; import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.editor.event.DocumentAdapter; import com.intellij.openapi.editor.event.DocumentEvent; @@ -38,12 +38,16 @@ import java.util.stream.Collectors; public class ParameterNameHintsConfigurable extends DialogWrapper { private final Project myProject; - private final InlayParameterHintsProvider myHintsProvider; - - public ParameterNameHintsConfigurable(@NotNull Project project, @NotNull InlayParameterHintsProvider provider) { + private final Set myDefaultBlackList; + private final Language myLanguage; + + public ParameterNameHintsConfigurable(@NotNull Project project, + @NotNull Set defaultBlackList, + @NotNull Language language) { super(project); myProject = project; - myHintsProvider = provider; + myDefaultBlackList = defaultBlackList; + myLanguage = language; setTitle("Configure Parameter Name Hints Blacklist"); init(); } @@ -70,7 +74,8 @@ public class ParameterNameHintsConfigurable extends DialogWrapper { .filter((e) -> !e.trim().isEmpty()) .collect(Collectors.toSet()); - ParameterNameHintsSettings.getInstance().setIgnorePatternSet(myHintsProvider, updatedBlackList); + Diff diff = Diff.Builder.build(myDefaultBlackList, updatedBlackList); + ParameterNameHintsSettings.getInstance().setBlackListDiff(myLanguage, diff); } @Nullable @@ -85,10 +90,11 @@ public class ParameterNameHintsConfigurable extends DialogWrapper { private void createUIComponents() { EditorTextFieldProvider service = ServiceManager.getService(myProject, EditorTextFieldProvider.class); myEditorTextField = service.getEditorField(PlainTextLanguage.INSTANCE, myProject, ContainerUtil.emptyIterable()); - - Set blacklist = ParameterNameHintsSettings.getInstance().getIgnorePatternSet(myHintsProvider); + + Diff diff = ParameterNameHintsSettings.getInstance().getBlackListDiff(myLanguage); + Set blacklist = diff.applyOn(myDefaultBlackList); + String text = StringUtil.join(blacklist, "\n"); - myEditorTextField.setText(text); myEditorTextField.addDocumentListener(new DocumentAdapter() { @Override diff --git a/platform/lang-impl/src/com/intellij/codeInsight/hints/settings/ParameterNameHintsSettings.kt b/platform/lang-impl/src/com/intellij/codeInsight/hints/settings/ParameterNameHintsSettings.kt index e0362885e391..1a941281117d 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/hints/settings/ParameterNameHintsSettings.kt +++ b/platform/lang-impl/src/com/intellij/codeInsight/hints/settings/ParameterNameHintsSettings.kt @@ -1,6 +1,5 @@ package com.intellij.codeInsight.hints.settings -import com.intellij.codeInsight.hints.InlayParameterHintsProvider import com.intellij.lang.Language import com.intellij.openapi.components.PersistentStateComponent import com.intellij.openapi.components.State @@ -19,11 +18,52 @@ private object XmlTagHelper { } +class Diff(val added: Set, val removed: Set) { + + fun applyOn(base: Set): Set { + val baseSet = base.toMutableSet() + added.forEach { baseSet.add(it) } + removed.forEach { baseSet.remove(it) } + return baseSet + } + + companion object Builder { + fun build(base: Set, updated: Set): Diff { + val removed = base.toMutableSet() + removed.removeAll(updated) + + val added = updated.toMutableSet() + added.removeAll(base) + + return Diff(added, removed) + } + } + +} + + @State(name = "ParameterNameHintsSettings", storages = arrayOf(Storage("parameter.hints.xml"))) class ParameterNameHintsSettings : PersistentStateComponent { - private val myRemovedPatterns = hashMapOf>() - private val myAddedPatterns = hashMapOf>() + private val myRemovedPatterns = hashMapOf>() + private val myAddedPatterns = hashMapOf>() + fun addIgnorePattern(language: Language, pattern: String) { + val patternsBefore = getAddedPatterns(language) + setAddedPatterns(language, patternsBefore + pattern) + } + + fun getBlackListDiff(language: Language): Diff { + val added = getAddedPatterns(language) + val removed = getRemovedPatterns(language) + + return Diff(added, removed) + } + + fun setBlackListDiff(language: Language, diff: Diff) { + setAddedPatterns(language, diff.added) + setRemovedPatterns(language, diff.removed) + } + override fun getState(): Element { val root = Element("settings") val blacklists = root.getOrCreateChild(XmlTagHelper.BLACKLISTS) @@ -47,73 +87,43 @@ class ParameterNameHintsSettings : PersistentStateComponent { myRemovedPatterns[language] = it.extractPatterns(XmlTagHelper.REMOVED) } } - - fun addIgnorePattern(language: Language, pattern: String) { - val patternsBefore = getAddedPatterns(language) - setAddedPatterns(language, patternsBefore + pattern) - } - - fun getIgnorePatternSet(hintsProvider: InlayParameterHintsProvider): Set { - val added = getAddedPatterns(hintsProvider.language) - val removed = getRemovedPatterns(hintsProvider.language) - - val updated = hintsProvider.defaultBlackList.toMutableSet() - updated.removeAll(removed) - updated.addAll(added) - - return updated - } - - fun setIgnorePatternSet(provider: InlayParameterHintsProvider, updatedBlackList: Set) { - val defaultBlackList = provider.defaultBlackList - - val removed = defaultBlackList.toMutableSet() - removed.removeAll(updatedBlackList) - - val added = updatedBlackList.toMutableSet() - added.removeAll(defaultBlackList) - - val language = provider.language - setRemovedPatterns(language, removed) - setAddedPatterns(language, added) - } companion object { @JvmStatic fun getInstance() = service() } - private fun getAddedPatterns(language: Language): List { + private fun getAddedPatterns(language: Language): Set { val key = language.displayName - return myAddedPatterns[key] ?: emptyList() + return myAddedPatterns[key] ?: emptySet() } - private fun getRemovedPatterns(language: Language): List { + private fun getRemovedPatterns(language: Language): Set { val key = language.displayName - return myRemovedPatterns[key] ?: emptyList() + return myRemovedPatterns[key] ?: emptySet() } - private fun setRemovedPatterns(language: Language, removed: Collection) { + private fun setRemovedPatterns(language: Language, removed: Set) { val key = language.displayName - myRemovedPatterns[key] = removed.toList() + myRemovedPatterns[key] = removed } - private fun setAddedPatterns(language: Language, added: Collection) { + private fun setAddedPatterns(language: Language, added: Set) { val key = language.displayName - myAddedPatterns[key] = added.toList() + myAddedPatterns[key] = added } } -private fun Element.addLanguagePatternElements(language: String, patterns: List, tag: String) { +private fun Element.addLanguagePatternElements(language: String, patterns: Set, tag: String) { val list = getOrCreateChild(XmlTagHelper.LANGUAGE_LIST) list.setAttribute(XmlTagHelper.LANGUAGE, language) val elements = patterns.map { it.toPatternElement(tag) } list.addContent(elements) } -private fun Element.extractPatterns(tag: String): List { - return getChildren(tag).mapNotNull { it.attributeValue(XmlTagHelper.PATTERN) } +private fun Element.extractPatterns(tag: String): Set { + return getChildren(tag).mapNotNull { it.attributeValue(XmlTagHelper.PATTERN) }.toSet() } private fun Element.attributeValue(attr: String): String? = this.getAttribute(attr)?.value 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 index a3908fa72984..4c0e852b2289 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupPreview.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupPreview.java @@ -24,6 +24,7 @@ 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.refactoring.rename.inplace.InplaceRefactoring; import com.intellij.ui.JBColor; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.FList; @@ -52,11 +53,14 @@ class LookupPreview { myInlays.clear(); String suffix = getSuffixText(item); - if (!suffix.isEmpty() && myLookup.getTopLevelEditor() instanceof EditorImpl) { - myLookup.getTopLevelEditor().getCaretModel().runForEachCaret(caret -> { + Editor editor = myLookup.getTopLevelEditor(); + if (!suffix.isEmpty() && editor instanceof EditorImpl && + !editor.getSelectionModel().hasSelection() && + InplaceRefactoring.getActiveInplaceRenamer(editor) == null) { + for (Caret caret : editor.getCaretModel().getAllCarets()) { ensureCaretBeforeInlays(caret); addInlay(suffix, caret.getOffset()); - }); + } } } diff --git a/platform/lang-impl/src/com/intellij/find/findUsages/CommonFindUsagesDialog.java b/platform/lang-impl/src/com/intellij/find/findUsages/CommonFindUsagesDialog.java index 3315eac0b979..366ae79af5a6 100644 --- a/platform/lang-impl/src/com/intellij/find/findUsages/CommonFindUsagesDialog.java +++ b/platform/lang-impl/src/com/intellij/find/findUsages/CommonFindUsagesDialog.java @@ -47,7 +47,7 @@ public class CommonFindUsagesDialog extends AbstractFindUsagesDialog { boolean isSingleFile, @NotNull FindUsagesHandler handler) { super(project, findUsagesOptions, toShowInNewTab, mustOpenInNewTab, isSingleFile, isTextSearch(element, isSingleFile, handler), - !isSingleFile && !element.getManager().isInProject(element)); + true); myPsiElement = element; myHelpId = ObjectUtils.chooseNotNull(handler.getHelpId(), HelpID.FIND_OTHER_USAGES); init(); diff --git a/platform/lang-impl/src/com/intellij/ide/ui/search/TraverseUIStarter.java b/platform/lang-impl/src/com/intellij/ide/ui/search/TraverseUIStarter.java index 7865871d0f3f..0da1029c159b 100644 --- a/platform/lang-impl/src/com/intellij/ide/ui/search/TraverseUIStarter.java +++ b/platform/lang-impl/src/com/intellij/ide/ui/search/TraverseUIStarter.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 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. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.intellij.ide.ui.search; import com.intellij.application.options.OptionsContainingConfigurable; @@ -34,31 +33,30 @@ import com.intellij.openapi.options.UnnamedConfigurable; import com.intellij.openapi.options.ex.ConfigurableWrapper; import com.intellij.openapi.project.ProjectManager; import com.intellij.openapi.util.JDOMUtil; +import com.intellij.openapi.util.io.FileUtil; import org.jdom.Document; import org.jdom.Element; -import org.jetbrains.annotations.NonNls; import java.io.File; import java.io.IOException; -import java.util.Collections; -import java.util.HashMap; -import java.util.Set; -import java.util.TreeSet; +import java.util.*; /** - * Used by Installer's build buildSearchableOptions step. In order to run locally use TraverseUi configuration. - * Pass corresponding -Didea.platform.prefix=YOUR_IDE_PREFIX to vm options and choose main_YOUR_IDE module + * Used in installer's "build searchable options" step. + * + * In order to run locally, use "TraverseUi" run configuration (pass corresponding "idea.platform.prefix" property via VM options, + * and choose correct main module). */ -@SuppressWarnings({"CallToPrintStackTrace", "SynchronizeOnThis"}) +@SuppressWarnings({"CallToPrintStackTrace", "SynchronizeOnThis", "UseOfSystemOutOrSystemErr"}) public class TraverseUIStarter extends ApplicationStarterEx { - @NonNls private static final String OPTIONS = "options"; - @NonNls private static final String CONFIGURABLE = "configurable"; - @NonNls private static final String ID = "id"; - @NonNls private static final String CONFIGURABLE_NAME = "configurable_name"; - @NonNls private static final String OPTION = "option"; - @NonNls private static final String NAME = "name"; - @NonNls private static final String PATH = "path"; - @NonNls private static final String HIT = "hit"; + private static final String OPTIONS = "options"; + private static final String CONFIGURABLE = "configurable"; + private static final String ID = "id"; + private static final String CONFIGURABLE_NAME = "configurable_name"; + private static final String OPTION = "option"; + private static final String NAME = "name"; + private static final String PATH = "path"; + private static final String HIT = "hit"; private String OUTPUT_PATH; @@ -68,7 +66,6 @@ public class TraverseUIStarter extends ApplicationStarterEx { } @Override - @NonNls public String getCommandName() { return "traverseUI"; } @@ -79,7 +76,7 @@ public class TraverseUIStarter extends ApplicationStarterEx { } @Override - public void main(String[] args){ + public void main(String[] args) { System.out.println("Starting searchable options index builder"); try { startup(OUTPUT_PATH); @@ -93,52 +90,54 @@ public class TraverseUIStarter extends ApplicationStarterEx { } public static void startup(String outputPath) throws IOException { - final HashMap> options = - new HashMap<>(); + Map> options = new HashMap<>(); SearchUtil.processProjectConfigurables(ProjectManager.getInstance().getDefaultProject(), options); + Element root = new Element(OPTIONS); - for (SearchableConfigurable configurable : options.keySet()) { + for (SearchableConfigurable option : options.keySet()) { + SearchableConfigurable configurable = option; + Element configurableElement = new Element(CONFIGURABLE); - final String id = configurable.getId(); - if (id == null) continue; + String id = configurable.getId(); configurableElement.setAttribute(ID, id); configurableElement.setAttribute(CONFIGURABLE_NAME, configurable.getDisplayName()); - final TreeSet sortedOptions = options.get(configurable); + Set sortedOptions = options.get(configurable); writeOptions(configurableElement, sortedOptions); + if (configurable instanceof ConfigurableWrapper) { - final UnnamedConfigurable wrapped = ((ConfigurableWrapper)configurable).getConfigurable(); + UnnamedConfigurable wrapped = ((ConfigurableWrapper)configurable).getConfigurable(); if (wrapped instanceof SearchableConfigurable) { configurable = (SearchableConfigurable)wrapped; } } - if (configurable instanceof KeymapPanel){ + if (configurable instanceof KeymapPanel) { processKeymap(configurableElement); - } else if (configurable instanceof OptionsContainingConfigurable){ + } + else if (configurable instanceof OptionsContainingConfigurable) { processOptionsContainingConfigurable((OptionsContainingConfigurable)configurable, configurableElement); - } else if (configurable instanceof PluginManagerConfigurable) { - final TreeSet descriptions = wordsToOptionDescriptors(Collections.singleton(AvailablePluginsManagerMain.MANAGE_REPOSITORIES)); - for (OptionDescription description : descriptions) { + } + else if (configurable instanceof PluginManagerConfigurable) { + for (OptionDescription description : wordsToOptionDescriptors(Collections.singleton(AvailablePluginsManagerMain.MANAGE_REPOSITORIES))) { append(null, AvailablePluginsManagerMain.MANAGE_REPOSITORIES, description.getOption(), configurableElement); } - } else if (configurable instanceof AllFileTemplatesConfigurable) { + } + else if (configurable instanceof AllFileTemplatesConfigurable) { processFileTemplates(configurableElement); } + root.addContent(configurableElement); configurable.disposeUIResources(); } - final File file = new File(outputPath); - if (!file.isFile()) { - file.getParentFile().mkdirs(); - file.createNewFile(); - } + + FileUtil.ensureCanCreateFile(new File(outputPath)); JDOMUtil.writeDocument(new Document(root), outputPath, "\n"); System.out.println("Searchable options index builder completed"); } private static void processFileTemplates(Element configurableElement) { - final SearchableOptionsRegistrar optionsRegistrar = SearchableOptionsRegistrar.getInstance(); - TreeSet options = new TreeSet<>(); + SearchableOptionsRegistrar optionsRegistrar = SearchableOptionsRegistrar.getInstance(); + Set options = new TreeSet<>(); FileTemplateManager fileTemplateManager = FileTemplateManager.getDefaultInstance(); processTemplates(optionsRegistrar, options, fileTemplateManager.getAllTemplates()); @@ -149,45 +148,30 @@ public class TraverseUIStarter extends ApplicationStarterEx { writeOptions(configurableElement, options); } - private static void processTemplates(SearchableOptionsRegistrar optionsRegistrar, - TreeSet options, - FileTemplate[] templates) { + private static void processTemplates(SearchableOptionsRegistrar registrar, Set options, FileTemplate[] templates) { for (FileTemplate template : templates) { - collectOptions(optionsRegistrar, options, template.getName()); - //collectOptions(optionsRegistrar, options, template.getDescription()); + collectOptions(registrar, options, template.getName(), null); } } - private static void collectOptions(SearchableOptionsRegistrar optionsRegistrar, - TreeSet options, - String text) { - collectOptions(optionsRegistrar, options, text, null); - } - - private static void collectOptions(SearchableOptionsRegistrar optionsRegistrar, - TreeSet options, - String text, - String path) { - final Set strings = optionsRegistrar.getProcessedWordsWithoutStemming(text); - for (String word : strings) { + private static void collectOptions(SearchableOptionsRegistrar registrar, Set options, String text, String path) { + for (String word : registrar.getProcessedWordsWithoutStemming(text)) { options.add(new OptionDescription(word, text, path)); } } - private static void processOptionsContainingConfigurable(final OptionsContainingConfigurable configurable, - final Element configurableElement) { - final Set optionsPath = configurable.processListOptions(); - final TreeSet result = wordsToOptionDescriptors(optionsPath); + private static void processOptionsContainingConfigurable(OptionsContainingConfigurable configurable, Element configurableElement) { + Set optionsPath = configurable.processListOptions(); + Set result = wordsToOptionDescriptors(optionsPath); writeOptions(configurableElement, result); } - private static TreeSet wordsToOptionDescriptors(Set optionsPath) { - SearchableOptionsRegistrar searchableOptionsRegistrar = SearchableOptionsRegistrar.getInstance(); - final TreeSet result = new TreeSet<>(); + private static Set wordsToOptionDescriptors(Set optionsPath) { + SearchableOptionsRegistrar registrar = SearchableOptionsRegistrar.getInstance(); + Set result = new TreeSet<>(); for (String opt : optionsPath) { - final Set words = searchableOptionsRegistrar.getProcessedWordsWithoutStemming(opt); - for (String word : words) { - if (word != null){ + for (String word : registrar.getProcessedWordsWithoutStemming(opt)) { + if (word != null) { result.add(new OptionDescription(word, opt, null)); } } @@ -195,7 +179,7 @@ public class TraverseUIStarter extends ApplicationStarterEx { return result; } - private static void processKeymap(final Element configurableElement){ + private static void processKeymap(Element configurableElement) { final ActionManager actionManager = ActionManager.getInstance(); final String componentName = actionManager.getComponentName(); final SearchableOptionsRegistrar searchableOptionsRegistrar = SearchableOptionsRegistrar.getInstance(); @@ -215,7 +199,7 @@ public class TraverseUIStarter extends ApplicationStarterEx { writeOptions(configurableElement, options); } - private static void writeOptions(Element configurableElement, TreeSet options) { + private static void writeOptions(Element configurableElement, Set options) { for (OptionDescription opt : options) { append(opt.getPath(), opt.getHit(), opt.getOption(), configurableElement); } @@ -230,4 +214,4 @@ public class TraverseUIStarter extends ApplicationStarterEx { optionElement.setAttribute(HIT, hit); configurableElement.addContent(optionElement); } -} +} \ No newline at end of file diff --git a/platform/lang-impl/testSources/com/intellij/codeInsight/hints/settings/ParameterNameSettingsTest.kt b/platform/lang-impl/testSources/com/intellij/codeInsight/hints/settings/ParameterNameSettingsTest.kt index 83bd177c73be..b46d1dc63c5b 100644 --- a/platform/lang-impl/testSources/com/intellij/codeInsight/hints/settings/ParameterNameSettingsTest.kt +++ b/platform/lang-impl/testSources/com/intellij/codeInsight/hints/settings/ParameterNameSettingsTest.kt @@ -16,14 +16,12 @@ package com.intellij.codeInsight.hints.settings import com.intellij.codeInsight.hints.InlayParameterHintsProvider -import com.intellij.lang.Language import com.intellij.openapi.fileTypes.PlainTextLanguage import junit.framework.TestCase -class MockInlayProvider(override val defaultBlackList: Set): InlayParameterHintsProvider { - override val language: Language = PlainTextLanguage.INSTANCE -} +class MockInlayProvider(override val defaultBlackList: Set): InlayParameterHintsProvider + class ParameterNameSettingsTest : TestCase() { @@ -40,14 +38,20 @@ class ParameterNameSettingsTest : TestCase() { } fun addIgnorePattern(newPattern: String) { - settings.addIgnorePattern(inlayProvider.language, newPattern) + settings.addIgnorePattern(PlainTextLanguage.INSTANCE, newPattern) } fun setIgnorePattern(vararg newPatternSet: String) { - settings.setIgnorePatternSet(inlayProvider, setOf(*newPatternSet)) + val base = inlayProvider.defaultBlackList + val diff = Diff.build(base, setOf(*newPatternSet)) + + settings.setBlackListDiff(PlainTextLanguage.INSTANCE, diff) } - fun getIgnoreSet(): Set = settings.getIgnorePatternSet(inlayProvider) + fun getIgnoreSet(): Set { + val diff = settings.getBlackListDiff(PlainTextLanguage.INSTANCE) + return diff.applyOn(inlayProvider.defaultBlackList) + } fun `test ignore pattern is added`() { defaultSettingsUpdated("xxx") diff --git a/platform/platform-api/src/com/intellij/ui/components/JBViewport.java b/platform/platform-api/src/com/intellij/ui/components/JBViewport.java index 601872389483..92136929c053 100644 --- a/platform/platform-api/src/com/intellij/ui/components/JBViewport.java +++ b/platform/platform-api/src/com/intellij/ui/components/JBViewport.java @@ -165,12 +165,26 @@ public class JBViewport extends JViewport implements ZoomableViewport { @Override public void scrollRectToVisible(Rectangle bounds) { Component view = getView(); - if (view instanceof JComponent) { + if (view instanceof JComponent && !isAutoscroll(bounds)) { JBInsets.addTo(bounds, getViewInsets((JComponent)view)); } super.scrollRectToVisible(bounds); } + private boolean isAutoscroll(Rectangle bounds) { + if (bounds.x == -bounds.width || bounds.x == getWidth()) { + if (bounds.y + bounds.height + bounds.y == getHeight()) { + return true; + } + } + if (bounds.y == -bounds.height || bounds.y == getHeight()) { + if (bounds.x + bounds.width + bounds.x == getWidth()) { + return true; + } + } + return false; + } + /** * Returns the alignment of the specified scroll bar * if and only if the specified scroll bar diff --git a/platform/platform-impl/src/com/intellij/ide/ui/search/SearchUtil.java b/platform/platform-impl/src/com/intellij/ide/ui/search/SearchUtil.java index f0c4464c18f7..854fc1e46720 100644 --- a/platform/platform-impl/src/com/intellij/ide/ui/search/SearchUtil.java +++ b/platform/platform-impl/src/com/intellij/ide/ui/search/SearchUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 JetBrains s.r.o. + * Copyright 2000-2016 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.intellij.ide.ui.search; import com.intellij.application.options.SkipSelfSearchComponent; @@ -24,15 +23,12 @@ import com.intellij.openapi.options.MasterDetails; import com.intellij.openapi.options.SearchableConfigurable; import com.intellij.openapi.options.ex.GlassPanel; import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.popup.JBPopup; -import com.intellij.openapi.ui.popup.JBPopupFactory; import com.intellij.openapi.util.text.StringUtil; -import com.intellij.ui.*; -import com.intellij.ui.components.JBList; -import com.intellij.util.Alarm; -import com.intellij.util.Consumer; +import com.intellij.ui.JBColor; +import com.intellij.ui.SimpleColoredComponent; +import com.intellij.ui.SimpleTextAttributes; +import com.intellij.ui.TabbedPaneWrapper; import com.intellij.util.containers.ContainerUtil; -import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -40,36 +36,32 @@ import javax.swing.*; import javax.swing.border.Border; import javax.swing.border.TitledBorder; import java.awt.*; -import java.awt.event.KeyAdapter; -import java.awt.event.KeyEvent; import java.util.*; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; /** - * User: anna - * Date: 07-Feb-2006 + * @author anna + * @since 07.02.2006 */ public class SearchUtil { private static final Pattern HTML_PATTERN = Pattern.compile("<[^<>]*>"); - private static final Pattern QUOTED = Pattern.compile("\\\"([^\\\"]+)\\\""); + private static final Pattern QUOTED = Pattern.compile("\"([^\"]+)\""); public static final String HIGHLIGHT_WITH_BORDER = "searchUtil.highlightWithBorder"; public static final String STYLE_END = ""; - private SearchUtil() { - } + private SearchUtil() { } - public static void processProjectConfigurables(Project project, HashMap> options) { + public static void processProjectConfigurables(Project project, Map> options) { processConfigurables(ShowSettingsUtilImpl.getConfigurables(project, false), options); } - private static void processConfigurables(final Configurable[] configurables, - final HashMap> options) { + private static void processConfigurables(Configurable[] configurables, Map> options) { for (Configurable configurable : configurables) { if (configurable instanceof SearchableConfigurable) { - TreeSet configurableOptions = new TreeSet<>(); + Set configurableOptions = new TreeSet<>(); if (configurable instanceof Configurable.Composite) { final Configurable[] children = ((Configurable.Composite)configurable).getConfigurables(); @@ -86,26 +78,24 @@ public class SearchUtil { if (configurable instanceof MasterDetails) { final MasterDetails md = (MasterDetails)configurable; md.initUi(); - _processComponent(configurable, configurableOptions, md.getMaster()); - _processComponent(configurable, configurableOptions, md.getDetails().getComponent()); + processComponent(configurable, configurableOptions, md.getMaster()); + processComponent(configurable, configurableOptions, md.getDetails().getComponent()); } else { - _processComponent(configurable, configurableOptions, configurable.createComponent()); + processComponent(configurable, configurableOptions, configurable.createComponent()); } } } } - private static void _processComponent(final Configurable configurable, final TreeSet configurableOptions, - final JComponent component) { - - if (component == null) return; - - processUILabel(configurable.getDisplayName(), configurableOptions, null); - processComponent(component, configurableOptions, null); + private static void processComponent(Configurable configurable, Set configurableOptions, JComponent component) { + if (component != null) { + processUILabel(configurable.getDisplayName(), configurableOptions, null); + processComponent(component, configurableOptions, null); + } } - public static void processComponent(final JComponent component, final Set configurableOptions, @NonNls String path) { + private static void processComponent(JComponent component, Set configurableOptions, String path) { if (component instanceof SkipSelfSearchComponent) return; final Border border = component.getBorder(); if (border instanceof TitledBorder) { @@ -122,19 +112,19 @@ public class SearchUtil { } } else if (component instanceof JCheckBox) { - @NonNls final String checkBoxTitle = ((JCheckBox)component).getText(); + final String checkBoxTitle = ((JCheckBox)component).getText(); if (checkBoxTitle != null) { processUILabel(checkBoxTitle, configurableOptions, path); } } else if (component instanceof JRadioButton) { - @NonNls final String radioButtonTitle = ((JRadioButton)component).getText(); + final String radioButtonTitle = ((JRadioButton)component).getText(); if (radioButtonTitle != null) { processUILabel(radioButtonTitle, configurableOptions, path); } } else if (component instanceof JButton) { - @NonNls final String buttonTitle = ((JButton)component).getText(); + final String buttonTitle = ((JButton)component).getText(); if (buttonTitle != null) { processUILabel(buttonTitle, configurableOptions, path); } @@ -163,18 +153,15 @@ public class SearchUtil { } } - private static void processUILabel(@NonNls final String title, final Set configurableOptions, String path) { + private static void processUILabel(String title, Set configurableOptions, String path) { final Set words = SearchableOptionsRegistrar.getInstance().getProcessedWordsWithoutStemming(title); - @NonNls final String regex = "[\\W&&[^\\p{Punct}\\p{Blank}]]"; + final String regex = "[\\W&&[^\\p{Punct}\\p{Blank}]]"; for (String option : words) { configurableOptions.add(new OptionDescription(option, HTML_PATTERN.matcher(title).replaceAll(" ").replaceAll(regex, " "), path)); } } - public static Runnable lightOptions(final SearchableConfigurable configurable, - final JComponent component, - final String option, - final GlassPanel glassPanel) { + public static Runnable lightOptions(SearchableConfigurable configurable, JComponent component, String option, GlassPanel glassPanel) { return () -> { if (!traverseComponentsTree(configurable, glassPanel, component, option, true)) { traverseComponentsTree(configurable, glassPanel, component, option, false); @@ -191,7 +178,8 @@ public class SearchUtil { final Set titleWords = searchableOptionsRegistrar.getProcessedWords(title); pathWords.removeAll(titleWords); if (pathWords.isEmpty()) return i; - } else if (tabIdx.equalsIgnoreCase(title)) { //e.g. only stop words + } + else if (tabIdx.equalsIgnoreCase(title)) { //e.g. only stop words return i; } } @@ -210,12 +198,11 @@ public class SearchUtil { return -1; } - private static boolean traverseComponentsTree(final SearchableConfigurable configurable, + private static boolean traverseComponentsTree(SearchableConfigurable configurable, GlassPanel glassPanel, JComponent rootComponent, String option, boolean force) { - rootComponent.putClientProperty(HIGHLIGHT_WITH_BORDER, null); if (option == null || option.trim().length() == 0) return false; @@ -271,7 +258,6 @@ public class SearchUtil { } } - final Component[] components = rootComponent.getComponents(); for (Component component : components) { if (component instanceof JComponent) { @@ -298,20 +284,19 @@ public class SearchUtil { return highlight; } - public static boolean isComponentHighlighted(String text, String option, final boolean force, final SearchableConfigurable configurable) { + public static boolean isComponentHighlighted(String text, String option, boolean force, final SearchableConfigurable configurable) { if (text == null || option == null || option.length() == 0) return false; final SearchableOptionsRegistrar searchableOptionsRegistrar = SearchableOptionsRegistrar.getInstance(); final Set words = searchableOptionsRegistrar.getProcessedWords(option); - final Set options = - configurable != null ? searchableOptionsRegistrar.replaceSynonyms(words, configurable) : words; + final Set options = configurable != null ? searchableOptionsRegistrar.replaceSynonyms(words, configurable) : words; if (options == null || options.isEmpty()) { - return text.toLowerCase().indexOf(option.toLowerCase()) != -1; + return text.toLowerCase(Locale.US).contains(option.toLowerCase(Locale.US)); } final Set tokens = searchableOptionsRegistrar.getProcessedWords(text); if (!force) { options.retainAll(tokens); final boolean highlight = !options.isEmpty(); - return highlight || text.toLowerCase().indexOf(option.toLowerCase()) != -1; + return highlight || text.toLowerCase(Locale.US).contains(option.toLowerCase(Locale.US)); } else { options.removeAll(tokens); @@ -319,7 +304,7 @@ public class SearchUtil { } } - public static String markup(@NonNls @NotNull String textToMarkup, @Nullable String filter) { + public static String markup(@NotNull String textToMarkup, @Nullable String filter) { if (filter == null || filter.length() == 0) { return textToMarkup; } @@ -332,11 +317,13 @@ public class SearchUtil { head = textToMarkup.substring(0, bodyStart); if (bodyEnd >= 0) { foot = textToMarkup.substring(bodyEnd); - } else { + } + else { foot = ""; } textToMarkup = textToMarkup.substring(bodyStart, bodyEnd); - } else { + } + else { foot = ""; head = ""; } @@ -360,7 +347,7 @@ public class SearchUtil { private static String quoteStrictOccurrences(final String textToMarkup, final String filter) { String cur = ""; - final String s = textToMarkup.toLowerCase(); + final String s = textToMarkup.toLowerCase(Locale.US); for (String part : filter.split(" ")) { if (s.contains(part)) { cur += "\"" + part + "\" "; @@ -372,13 +359,14 @@ public class SearchUtil { return cur; } - private static String markup(@NonNls String textToMarkup, final Pattern insideHtmlTagPattern, final String option) { + private static String markup(String textToMarkup, final Pattern insideHtmlTagPattern, final String option) { final int styleIdx = textToMarkup.indexOf(""); if (styleIdx < 0 || styleEndIdx < 0) { return markupInText(textToMarkup, insideHtmlTagPattern, option); } - return markup(textToMarkup.substring(0, styleIdx), insideHtmlTagPattern, option) + markup(textToMarkup.substring(styleEndIdx + STYLE_END.length()), insideHtmlTagPattern, option); + return markup(textToMarkup.substring(0, styleIdx), insideHtmlTagPattern, option) + + markup(textToMarkup.substring(styleEndIdx + STYLE_END.length()), insideHtmlTagPattern, option); } private static String markupInText(String textToMarkup, Pattern insideHtmlTagPattern, String option) { @@ -403,7 +391,7 @@ public class SearchUtil { } public static void appendFragments(String filter, - @NonNls String text, + String text, @SimpleTextAttributes.StyleAttributeConstant int style, final Color foreground, final Color background, @@ -432,9 +420,10 @@ public class SearchUtil { final int start = index.intValue(); if (pos > start) { final String highlighted = selectedWords.get(selectedWords.size() - 1); - if (highlighted.length() < stripped.length()){ + if (highlighted.length() < stripped.length()) { selectedWords.remove(highlighted); - } else { + } + else { continue; } } @@ -469,111 +458,15 @@ public class SearchUtil { final Set filters = SearchableOptionsRegistrar.getInstance().getProcessedWords(filter); final String[] words = text.substring(pos, end).split("[\\W&&[^-]]+"); for (String word : words) { - if (filters.contains(PorterStemmerUtil.stem(word.toLowerCase()))) { + if (filters.contains(PorterStemmerUtil.stem(word.toLowerCase(Locale.US)))) { selectedWords.add(word); } } } } - @Nullable - private static JBPopup createPopup(final ConfigurableSearchTextField searchField, - final JBPopup[] activePopup, - final Alarm showHintAlarm, - final Consumer selectConfigurable, - final Project project, - final int down) { - - final String filter = searchField.getText(); - if (filter == null || filter.length() == 0) return null; - final Map> hints = SearchableOptionsRegistrar.getInstance().findPossibleExtension(filter, project); - final DefaultListModel model = new DefaultListModel(); - final JList list = new JBList(model); - for (String groupName : hints.keySet()) { - model.addElement(groupName); - final Set descriptions = hints.get(groupName); - if (descriptions != null) { - for (String hit : descriptions) { - if (hit == null) continue; - model.addElement(new OptionDescription(null, groupName, hit, null)); - } - } - } - list.setCellRenderer(new DefaultListCellRenderer() { - public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { - final Component rendererComponent = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); - if (value instanceof String) { - setText("------ " + value + " ------"); - } - else if (value instanceof OptionDescription) { - setText(((OptionDescription)value).getHit()); - } - return rendererComponent; - } - }); - - - if (model.size() > 0) { - final Runnable onChosen = () -> { - final Object selectedValue = list.getSelectedValue(); - if (selectedValue instanceof OptionDescription) { - final OptionDescription description = ((OptionDescription)selectedValue); - searchField.setText(description.getHit()); - searchField.addCurrentTextToHistory(); - SwingUtilities.invokeLater(() -> { //do not show look up again - showHintAlarm.cancelAllRequests(); - selectConfigurable.consume(description.getConfigurableId()); - }); - } - }; - final JBPopup popup = JBPopupFactory.getInstance() - .createListPopupBuilder(list) - .setItemChoosenCallback(onChosen) - .setRequestFocus(down != 0) - .createPopup(); - list.addKeyListener(new KeyAdapter() { - public void keyPressed(final KeyEvent e) { - if (e.getKeyCode() != KeyEvent.VK_ENTER && e.getKeyCode() != KeyEvent.VK_UP && e.getKeyCode() != KeyEvent.VK_DOWN && - e.getKeyCode() != KeyEvent.VK_PAGE_UP && e.getKeyCode() != KeyEvent.VK_PAGE_DOWN) { - searchField.requestFocusInWindow(); - if (cancelPopups(activePopup) && e.getKeyCode() == KeyEvent.VK_ESCAPE) { - return; - } - if (e.getKeyChar() != KeyEvent.CHAR_UNDEFINED) { - searchField.process( - new KeyEvent(searchField, KeyEvent.KEY_TYPED, e.getWhen(), e.getModifiers(), KeyEvent.VK_UNDEFINED, e.getKeyChar())); - } - } - } - - }); - if (down > 0) { - if (list.getSelectedIndex() < list.getModel().getSize() - 1) { - list.setSelectedIndex(list.getSelectedIndex() + 1); - } - } - else if (down < 0) { - if (list.getSelectedIndex() > 0) { - list.setSelectedIndex(list.getSelectedIndex() - 1); - } - } - return popup; - } - return null; - } - - private static boolean cancelPopups(final JBPopup[] activePopup) { - for (JBPopup popup : activePopup) { - if (popup != null && popup.isVisible()) { - popup.cancel(); - return true; - } - } - return false; - } - public static List> findKeys(String filter, Set quoted) { - filter = processFilter(filter.toLowerCase(), quoted); + filter = processFilter(filter.toLowerCase(Locale.US), quoted); final List> keySetList = new ArrayList<>(); final SearchableOptionsRegistrar optionsRegistrar = SearchableOptionsRegistrar.getInstance(); final Set words = optionsRegistrar.getProcessedWords(filter); @@ -609,17 +502,6 @@ public class SearchUtil { return withoutQuoted + " " + filter.substring(beg); } - //to process event - public static class ConfigurableSearchTextField extends SearchTextFieldWithStoredHistory { - public ConfigurableSearchTextField() { - super("ALL_CONFIGURABLES_PANEL_SEARCH_HISTORY"); - } - - public void process(final KeyEvent e) { - ((TextFieldWithProcessing)getTextEditor()).processKeyEvent(e); - } - } - public static List expand(ConfigurableGroup[] groups) { final ArrayList result = new ArrayList<>(); for (ConfigurableGroup eachGroup : groups) { @@ -635,10 +517,9 @@ public class SearchUtil { for (Configurable each : configurables) { addChildren(each, result); } - - result = ContainerUtil.filter(result, configurable -> !(configurable instanceof SearchableConfigurable.Parent) || ((SearchableConfigurable.Parent)configurable).isVisible()); - - return result; + + return ContainerUtil.filter(result, configurable -> !(configurable instanceof SearchableConfigurable.Parent) || + ((SearchableConfigurable.Parent)configurable).isVisible()); } private static void addChildren(Configurable configurable, List list) { @@ -650,5 +531,4 @@ public class SearchUtil { } } } - -} +} \ No newline at end of file diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/SoftWrapModelImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/SoftWrapModelImpl.java index e4a729a65b09..6874fce93a68 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/SoftWrapModelImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/SoftWrapModelImpl.java @@ -455,8 +455,7 @@ public class SoftWrapModelImpl extends InlayModel.SimpleAdapter * @return true if soft wraps-aware processing should be used; false otherwise */ public boolean prepareToMapping() { - if (myUpdateInProgress || myBulkUpdateInProgress || - myActive > 0 || !isSoftWrappingEnabled() || myEditor.getDocument().getTextLength() <= 0) { + if (myUpdateInProgress || myBulkUpdateInProgress || myActive > 0 || !isSoftWrappingEnabled()) { return false; } diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/editor/impl/EditorImplTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/editor/impl/EditorImplTest.java index 0b1dcf73adf1..669f915029a4 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/editor/impl/EditorImplTest.java +++ b/platform/platform-tests/testSrc/com/intellij/openapi/editor/impl/EditorImplTest.java @@ -417,4 +417,20 @@ public class EditorImplTest extends AbstractEditorTest { type('b'); assertEquals(heightInPixels - myEditor.getLineHeight(), myEditor.getScrollingModel().getVerticalScrollOffset()); } + + public void testEditorWithSoftWrapsBecomesVisibleAfterDocumentTextRemoval() throws Exception { + initText("abc def ghi"); + configureSoftWraps(3); + JViewport viewport = ((EditorEx)myEditor).getScrollPane().getViewport(); + viewport.setExtentSize(new Dimension()); // emulate editor becoming invisible + new WriteCommandAction.Simple(getProject()) { + @Override + protected void run() throws Throwable { + Document document = myEditor.getDocument(); + document.deleteString(0, document.getTextLength()); + } + }.execute(); + viewport.setExtentSize(new Dimension(1000, 1000)); // editor becomes visible + verifySoftWrapPositions(); + } } diff --git a/platform/testFramework/src/com/intellij/testFramework/TestLoggerFactory.java b/platform/testFramework/src/com/intellij/testFramework/TestLoggerFactory.java index a28cd96b7806..d23dc0784633 100644 --- a/platform/testFramework/src/com/intellij/testFramework/TestLoggerFactory.java +++ b/platform/testFramework/src/com/intellij/testFramework/TestLoggerFactory.java @@ -171,13 +171,14 @@ public class TestLoggerFactory implements Logger.Factory { if (!success && BUFFER.length() != 0) { if (UsefulTestCase.IS_UNDER_TEAMCITY) { // print in several small statements to avoid service messages tearing causing this fold to expand - System.err.flush(); - System.err.println("##teamcity[blockOpened name='DEBUG log']\n"); - System.err.flush(); - System.err.println(BUFFER); - System.err.flush(); - System.err.println("\n##teamcity[blockClosed name='DEBUG log']\n"); - System.err.flush(); + // using .out instead of .err by the advice from Nikita Skvortsov + System.out.flush(); + System.out.println("##teamcity[blockOpened name='DEBUG log']\n"); + System.out.flush(); + System.out.println(BUFFER); + System.out.flush(); + System.out.println("\n##teamcity[blockClosed name='DEBUG log']\n"); + System.out.flush(); } else { // mark each line in IDEA console with this hidden mark to be able to fold it automatically diff --git a/plugins/coverage-common/src/com/intellij/coverage/SrcFileAnnotator.java b/plugins/coverage-common/src/com/intellij/coverage/SrcFileAnnotator.java index 5c32427e05d0..2621dea74c93 100644 --- a/plugins/coverage-common/src/com/intellij/coverage/SrcFileAnnotator.java +++ b/plugins/coverage-common/src/com/intellij/coverage/SrcFileAnnotator.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 JetBrains s.r.o. + * Copyright 2000-2016 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -377,7 +377,6 @@ public class SrcFileAnnotator implements Disposable { // use id mapping lineNumberInCurrent = line; } - LOG.assertTrue(lineNumberInCurrent < document.getLineCount()); executableLines.put(line, (LineData)lineData); classLines.put(line, postProcessedLines); diff --git a/plugins/junit5_rt_tests/test/com/intellij/junit5/JUnit5GenerationTest.java b/plugins/junit5_rt_tests/test/com/intellij/junit5/JUnit5GenerationTest.java index 8ab147987c75..c05c4d71933c 100644 --- a/plugins/junit5_rt_tests/test/com/intellij/junit5/JUnit5GenerationTest.java +++ b/plugins/junit5_rt_tests/test/com/intellij/junit5/JUnit5GenerationTest.java @@ -27,7 +27,6 @@ class JUnit5GenerationTest extends JUnit5CodeInsightTest { " @Test\n" + " void name() {\n" + " \n" + - "\n" + " }\n" + "\n" + " @Test void m2(){}}"); diff --git a/python/src/com/jetbrains/python/console/PyConsoleStartFolding.java b/python/src/com/jetbrains/python/console/PyConsoleStartFolding.java new file mode 100644 index 000000000000..289fcdbd7c82 --- /dev/null +++ b/python/src/com/jetbrains/python/console/PyConsoleStartFolding.java @@ -0,0 +1,105 @@ +/* + * 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.console; + +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.editor.FoldRegion; +import com.intellij.openapi.editor.FoldingModel; +import com.intellij.openapi.editor.event.DocumentAdapter; +import com.intellij.openapi.editor.event.DocumentEvent; +import com.intellij.openapi.editor.ex.FoldingListener; +import com.intellij.util.DocumentUtil; +import com.jetbrains.python.console.pydev.ConsoleCommunicationListener; +import org.jetbrains.annotations.NotNull; + +public class PyConsoleStartFolding extends DocumentAdapter implements ConsoleCommunicationListener, FoldingListener { + private PythonConsoleView myConsoleView; + private boolean isFirstCommandWasExecuted = false; + private boolean doNotAddFoldingAgain = false; + private FoldRegion myStartFoldRegion; + private static final String DEFAULT_FOLDING_MESSAGE = "Python Console"; + private int startLineOffset = 0; + + public PyConsoleStartFolding(PythonConsoleView consoleView) { + super(); + myConsoleView = consoleView; + } + + public void setStartLineOffset(int startLineOffset) { + this.startLineOffset = startLineOffset; + } + + @Override + public void documentChanged(DocumentEvent event) { + addFolding(); + } + + private void addFolding() { + Document document = myConsoleView.getEditor().getDocument(); + if (doNotAddFoldingAgain || document.getTextLength() == 0) { + return; + } + FoldingModel foldingModel = myConsoleView.getEditor().getFoldingModel(); + foldingModel.runBatchFoldingOperation(() -> { + int start = startLineOffset; + String placeholderText = DEFAULT_FOLDING_MESSAGE; + int firstLine = document.getLineNumber(startLineOffset); + for (int line = firstLine; line < document.getLineCount(); line++) { + String lineText = document.getText(DocumentUtil.getLineTextRange(document, line)); + if (lineText.startsWith("Python")) { + placeholderText = lineText; + start = document.getLineStartOffset(line); + break; + } + } + + if (myStartFoldRegion != null) { + foldingModel.removeFoldRegion(myStartFoldRegion); + } + FoldRegion foldRegion = foldingModel.addFoldRegion(start, document.getTextLength() - 1, placeholderText); + if (foldRegion != null) { + foldRegion.setExpanded(false); + myStartFoldRegion = foldRegion; + } + }); + if (isFirstCommandWasExecuted) { + document.removeDocumentListener(this); + } + } + + @Override + public void commandExecuted(boolean more) { + isFirstCommandWasExecuted = true; + } + + @Override + public void inputRequested() { + + } + + @Override + public void onFoldRegionStateChange(@NotNull FoldRegion region) { + if (region.equals(myStartFoldRegion) && region.isExpanded()) { + myConsoleView.getEditor().getComponent().updateUI(); + doNotAddFoldingAgain = true; + } + } + + @Override + public void onFoldProcessingEnd() { + + } +} diff --git a/python/src/com/jetbrains/python/console/PydevConsoleRunnerImpl.java b/python/src/com/jetbrains/python/console/PydevConsoleRunnerImpl.java index b551bc98f5b9..50231dbe5f59 100644 --- a/python/src/com/jetbrains/python/console/PydevConsoleRunnerImpl.java +++ b/python/src/com/jetbrains/python/console/PydevConsoleRunnerImpl.java @@ -588,7 +588,6 @@ public class PydevConsoleRunnerImpl implements PydevConsoleRunner { myConsoleView.attachToProcess(myProcessHandler); createContentDescriptorAndActions(); - // Run myProcessHandler.startNotify(); }); @@ -635,6 +634,7 @@ public class PydevConsoleRunnerImpl implements PydevConsoleRunner { final List actions = fillToolBarActions(toolbarActions, contentDescriptor); registerActionShortcuts(actions, myConsoleView.getConsoleEditor().getComponent()); registerActionShortcuts(actions, panel); + getConsoleView().addConsoleFolding(false); showContentDescriptor(contentDescriptor); } diff --git a/python/src/com/jetbrains/python/console/PythonConsoleView.java b/python/src/com/jetbrains/python/console/PythonConsoleView.java index 2fef442ee2df..cf2734e57a2c 100644 --- a/python/src/com/jetbrains/python/console/PythonConsoleView.java +++ b/python/src/com/jetbrains/python/console/PythonConsoleView.java @@ -33,6 +33,7 @@ import com.intellij.openapi.editor.colors.EditorColorsManager; import com.intellij.openapi.editor.colors.EditorColorsScheme; import com.intellij.openapi.editor.ex.DocumentEx; import com.intellij.openapi.editor.ex.EditorEx; +import com.intellij.openapi.editor.ex.FoldingModelEx; import com.intellij.openapi.editor.ex.util.LexerEditorHighlighter; import com.intellij.openapi.editor.highlighter.EditorHighlighter; import com.intellij.openapi.editor.markup.HighlighterTargetArea; @@ -114,6 +115,30 @@ public class PythonConsoleView extends LanguageConsoleImpl implements Observable getFile().putCopyableUserData(PydevConsoleRunner.CONSOLE_KEY, communication); } + private PyConsoleStartFolding createConsoleFolding() { + PyConsoleStartFolding startFolding = new PyConsoleStartFolding(this); + myExecuteActionHandler.getConsoleCommunication().addCommunicationListener(startFolding); + getEditor().getDocument().addDocumentListener(startFolding); + ((FoldingModelEx)getEditor().getFoldingModel()).addListener(startFolding, this); + return startFolding; + } + + public void addConsoleFolding(boolean isDebugConsole) { + try { + if (isDebugConsole && myExecuteActionHandler != null) { + PyConsoleStartFolding folding = createConsoleFolding(); + // in debug console we should add folding from the place where the folding was turned on + folding.setStartLineOffset(getEditor().getDocument().getTextLength()); + } + else { + myInitialized.doWhenDone(this::createConsoleFolding); + } + } + catch (Exception e) { + LOG.error(e.getMessage()); + } + } + public void setExecutionHandler(@NotNull PydevConsoleExecuteActionHandler consoleExecuteActionHandler) { myExecuteActionHandler = consoleExecuteActionHandler; } diff --git a/python/src/com/jetbrains/python/console/PythonDebugLanguageConsoleView.java b/python/src/com/jetbrains/python/console/PythonDebugLanguageConsoleView.java index 076c268cc7c7..04129d41ce12 100644 --- a/python/src/com/jetbrains/python/console/PythonDebugLanguageConsoleView.java +++ b/python/src/com/jetbrains/python/console/PythonDebugLanguageConsoleView.java @@ -73,6 +73,7 @@ public class PythonDebugLanguageConsoleView extends DuplexConsoleView