diff --git a/.idea/inspectionProfiles/idea_default.xml b/.idea/inspectionProfiles/idea_default.xml index e3a1f1109e8f..3366d229b7d4 100644 --- a/.idea/inspectionProfiles/idea_default.xml +++ b/.idea/inspectionProfiles/idea_default.xml @@ -513,6 +513,9 @@ + + + diff --git a/build/scripts/nsis_installer.gant b/build/scripts/nsis_installer.gant new file mode 100644 index 000000000000..39795a6cf4b6 --- /dev/null +++ b/build/scripts/nsis_installer.gant @@ -0,0 +1,105 @@ +target(nsis: 'Build nsis windows installer') { + nsis_installer() +} + +String p(String key) { + return getProperty(key) +} + +def nsis_installer(pathsToInclude, stringsFile, pathsFile, outNamePrefix, includeToolsJar = true, associateIpr = true, + system_selector = getProperty("system_selector"), failOnSignError = false, outFileName = null, + boolean hasBundledJRE = false) { + // p.s: Groovy reevaluates default value of system_selector on each call, so it always uses actual property value. + + if (outFileName == null) { + outFileName = "${outNamePrefix}${buildNumber}" + } + + ant.taskdef(name: "nsis", classname: "com.intellij.internalUtilities.ant.NsiFiles", classpath: "${home}/community/build/lib/NsiFiles.jar") + + def box = paths.sandbox + ant.mkdir(dir: "$box/bin") + ant.mkdir(dir: "$box/nsiconf") + + if (!hasBundledJRE) { + ant.mkdir(dir: "$box/jre") + requireProperty("jdk_bundled_win", "1.7") + def jdk_bundled_version = p("jdk_bundled_win") == "1.8" ? "${home}/build/jdk/jdk8_redist.zip" : "${home}/build/jdk/jdk_redist.zip" + getBundledJDK(new File("${home}/build/jdk/win"), "jdk"+ p("jdk_bundled_win"), "x32.zip", jdk_bundled_version) + ant.unzip(dest: "$box/jre", src: "${jdk_bundled_version}") + + ant.copy(todir: "$box/bin") { + fileset(dir: "$box/jre/jre/bin") { + include(name: "msvcr71.dll") + } + } + } + + ant.copy(todir: "$box/nsiconf") { + fileset(dir: "$home/community/build/conf/nsis") { + include(name: "*") + exclude(name: "version*") + exclude(name: "strings*") + exclude(name: "paths*") + } + } + + if (isEap()) { + ant.copy(file: "$home/community/build/conf/nsis/version.eap.nsi", + tofile: "$box/nsiconf/version.nsi", overwrite: true) + } + else { + ant.copy(file: "$home/community/build/conf/nsis/version.nsi", + tofile: "$box/nsiconf/version.nsi", overwrite: true) + } + ant.copy(file: pathsFile, toFile: "$box/nsiconf/paths.nsi", overwrite: true) + + ant.nsis(instfile: "$box/nsiconf/idea_win.nsh", uninstfile: "$box/nsiconf/unidea_win.nsh") { + pathsToInclude.each { + fileset(dir: it, includes: "**/*") { + exclude(name: "**/idea.properties") + exclude(name: "**/*.vmoptions") + } + } + fileset(dir: box, includes: "bin/msvcr71.dll") + if (!hasBundledJRE) { + fileset(dir: box, includes: "jre/**/*") + if (includeToolsJar) { + fileset(dir: box) { + include(name: "jre/lib/tools.jar") + } + } + } + } + + ant.replace(file: "$stringsFile") { + replacefilter(token: "__VERSION_MAJOR__", value: p("component.version.major")) + replacefilter(token: "__VERSION_MINOR__", value: p("component.version.minor")) + } + ant.copy(file: stringsFile, toFile: "$box/nsiconf/strings.nsi", overwrite: true) + + ant.replace(file: "$box/nsiconf/version.nsi") { + replacefilter(token: "__BUILD_NUMBER__", value: buildNumber) + replacefilter(token: "__VERSION_MAJOR__", value: p("component.version.major")) + replacefilter(token: "__VERSION_MINOR__", value: p("component.version.minor")) + replacefilter(token: "__MIN_UPGRADE_BUILD__", value: p("component.install-over.minbuild")) + replacefilter(token: "__MAX_UPGRADE_BUILD__", value: p("component.install-over.maxbuild")) + replacefilter(token: "__UPGRADE_VERSION__", value: p("component.install-over.version")) + replacefilter(token: "__PRODUCT_PATHS_SELECTOR__", value: system_selector) + } + + ant.unzip(src: "$home/build/tools/NSIS.zip", dest: box) + ant.exec(command: "$box/NSIS/makensis.exe" + + " /DBASE_DIR=\"$home\"" + + " /DCOMMUNITY_DIR=\"$home/community\"" + + " /DIPR=\"${associateIpr}\"" + + " /DOUT_FILE=\"${outFileName}\"" + + " /DOUT_DIR=\"$paths.artifacts\"" + + " $box/nsiconf/idea.nsi") + + projectBuilder.stage("Signing binaries") + executeExternalAnt(["dirName": "${paths.artifacts}", "fileName": "${outFileName}.exe"], "$home/build/signBuild.xml") + projectBuilder.stage("Signing done") + + return "${paths.artifacts}/${outFileName}.exe" +} diff --git a/build/scripts/utils.gant b/build/scripts/utils.gant index 2ee16b096b45..c1549976e15e 100644 --- a/build/scripts/utils.gant +++ b/build/scripts/utils.gant @@ -754,17 +754,18 @@ private List getChildren(File file) { return file.listFiles().sort { File f -> f.name.toLowerCase() } } -binding.setVariable("getCustomJDK", { File customJdkDir -> - getChildren(customJdkDir).each { - if (it.getName().endsWith(".tar.gz")) { - def customJdkFileName = it.getAbsolutePath().substring(0, it.getAbsolutePath().length() - 3) - if (new File(customJdkFileName).exists()){ ant.delete(file: customJdkFileName) } - if (new File("${home}/build/jdk/custom_jdk_${buildNumber}.tar").exists()){ - ant.delete(file: "${home}/build/jdk/custom_jdk_${buildNumber}.tar") +binding.setVariable("getBundledJDK", { File jdkDir, String prefix, String ext, String bundledJDKFileName -> + getChildren(jdkDir).each { + if (it.getName().startsWith(prefix) && it.getName().endsWith(ext)) { + if (new File(bundledJDKFileName).exists()) { ant.delete(file: bundledJDKFileName) } + def JdkFileName = it.getAbsolutePath() + if (ext == ".tar.gz") { + JdkFileName = JdkFileName.substring(0, JdkFileName.length() - 3) + if (new File(JdkFileName).exists()) { ant.delete(file: JdkFileName) } + ant.gunzip(src: it.getAbsolutePath()) } - ant.gunzip(src: it.getAbsolutePath()) - projectBuilder.info("customJdkFileName: " + customJdkFileName) - ant.copy(file: customJdkFileName, tofile: "${home}/build/jdk/custom_jdk_${buildNumber}.tar") + projectBuilder.info("JdkFileName: " + JdkFileName) + ant.copy(file: JdkFileName, tofile: bundledJDKFileName) } } }) diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerManagerImpl.java b/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerManagerImpl.java index 9c094e255ca9..9b9a45c70c4d 100644 --- a/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerManagerImpl.java +++ b/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerManagerImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -240,7 +240,14 @@ public class DebuggerManagerImpl extends DebuggerManagerEx implements Persistent } }); DebuggerSession session = new DebuggerSession(environment.getSessionName(), debugProcess); - final ExecutionResult executionResult = session.attach(environment); + ExecutionResult executionResult; + try { + executionResult = session.attach(environment); + } + catch (ExecutionException e) { + session.dispose(); + throw e; + } if (executionResult == null) { return null; } diff --git a/java/execution/impl/src/com/intellij/execution/JavaTestFrameworkRunnableState.java b/java/execution/impl/src/com/intellij/execution/JavaTestFrameworkRunnableState.java index bc75c4f1359d..f98778128f5d 100644 --- a/java/execution/impl/src/com/intellij/execution/JavaTestFrameworkRunnableState.java +++ b/java/execution/impl/src/com/intellij/execution/JavaTestFrameworkRunnableState.java @@ -45,6 +45,7 @@ import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.Getter; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.registry.Registry; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.util.text.StringUtilRt; import com.intellij.openapi.vfs.CharsetToolkit; import com.intellij.psi.JavaPsiFacade; @@ -195,6 +196,11 @@ public abstract class JavaTestFrameworkRunnableState & CommonJavaRunConfigurationParameters> extends SMTRunnerConsoleProperties { @@ -48,6 +49,11 @@ public abstract class JavaAwareTestConsoleProperties getLocation(@NotNull String protocolId, @NotNull String locationData, Project project) { List results = Collections.emptyList(); + final int idx = locationData.indexOf("["); + final String paramName = idx >= 0 ? locationData.substring(idx) : null; + if (idx >= 0) { + locationData = locationData.substring(0, idx); + } + final JavaPsiFacade javaPsiFacade = JavaPsiFacade.getInstance(project); if (SUITE_PROTOCOL.equals(protocolId)) { + locationData = StringUtil.trimEnd(locationData, "."); PsiClass[] classes = javaPsiFacade.findClasses(locationData, myScope); if (classes.length > 0) { results = ContainerUtil.newSmartList(); for (PsiClass aClass : classes) { - results.add(new PsiLocation(project, aClass)); - } - } - else { - //parameter root for parameterized tests: ClassName.[paramName] - final String className = StringUtil.getPackageName(locationData); - classes = javaPsiFacade.findClasses(className, myScope); - if (classes.length > 0) { - final String paramName = StringUtil.getShortName(locationData); - results = ContainerUtil.newSmartList(); - for (PsiClass aClass : classes) { - results.add(PsiMemberParameterizedLocation.getParameterizedLocation(aClass, paramName)); - } + results.add(paramName != null ? PsiMemberParameterizedLocation.getParameterizedLocation(aClass, paramName) + : new PsiLocation(project, aClass)); } } } @@ -80,18 +76,8 @@ public class JavaTestLocationProvider implements TestLocationProvider { PsiMethod[] methods = aClass.findMethodsByName(methodName, true); if (methods.length > 0) { for (PsiMethod method : methods) { - results.add(MethodLocation.elementInClass(method, aClass)); - } - } - else { - //parameterized tests: ClassName.testName[paramName] - final int paramIdx = methodName.indexOf("["); - if (paramIdx > -1 && methodName.endsWith("]")) { - final String paramName = methodName.substring(paramIdx); - methods = aClass.findMethodsByName(methodName.substring(0, paramIdx), true); - for (PsiMethod method : methods) { - results.add(new PsiMemberParameterizedLocation(project, method, aClass, paramName)); - } + results.add(paramName != null ? new PsiMemberParameterizedLocation(project, method, aClass, paramName) + : MethodLocation.elementInClass(method, aClass)); } } } diff --git a/java/idea-ui/src/com/intellij/ide/actions/ImportModuleAction.java b/java/idea-ui/src/com/intellij/ide/actions/ImportModuleAction.java index c992d544a4e3..40206ba56b1e 100644 --- a/java/idea-ui/src/com/intellij/ide/actions/ImportModuleAction.java +++ b/java/idea-ui/src/com/intellij/ide/actions/ImportModuleAction.java @@ -82,7 +82,7 @@ public class ImportModuleAction extends AnAction { } public static List createFromWizard(@Nullable Project project, AbstractProjectWizard wizard) { - if (project == null && wizard.getStepCount() > 0) { + if (project == null) { Project newProject = NewProjectUtil.createFromWizard(wizard, null); return newProject == null ? Collections.emptyList() : Arrays.asList(ModuleManager.getInstance(newProject).getModules()); } @@ -94,7 +94,6 @@ public class ImportModuleAction extends AnAction { return Collections.singletonList(module); } else { - assert project != null; return projectBuilder.commit(project); } } diff --git a/java/idea-ui/src/com/intellij/openapi/projectRoots/ui/SdkEditor.java b/java/idea-ui/src/com/intellij/openapi/projectRoots/ui/SdkEditor.java index 0c41048c0db8..ae544ea419ed 100644 --- a/java/idea-ui/src/com/intellij/openapi/projectRoots/ui/SdkEditor.java +++ b/java/idea-ui/src/com/intellij/openapi/projectRoots/ui/SdkEditor.java @@ -331,7 +331,9 @@ public class SdkEditor implements Configurable, Place.Navigator { } sdkModificator = dummySdk.getSdkModificator(); for (OrderRootType type : myPathEditors.keySet()) { - myPathEditors.get(type).addPaths(sdkModificator.getRoots(type)); + SdkPathEditor pathEditor = myPathEditors.get(type); + pathEditor.setAddBaseDir(dummySdk.getHomeDirectory()); + pathEditor.addPaths(sdkModificator.getRoots(type)); } mySdkModel.getMulticaster().sdkHomeSelected(dummySdk, homePath); } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DataFlowInspectionBase.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DataFlowInspectionBase.java index 68a74f887f3f..8c3804d8a500 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DataFlowInspectionBase.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DataFlowInspectionBase.java @@ -53,7 +53,6 @@ import com.intellij.util.SmartList; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.MultiMap; import org.jdom.Element; -import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -211,7 +210,7 @@ public class DataFlowInspectionBase extends BaseJavaBatchLocalInspectionTool { } } - ContainerUtil.addIfNotNull(fixes, ReplaceOptionalOfWithOfNullableFix.registerReplaceOptionalOfWithOfNullableFix(qualifier)); + ContainerUtil.addIfNotNull(fixes, DfaOptionalSupport.registerReplaceOptionalOfWithOfNullableFix(qualifier)); return fixes.isEmpty() ? null : fixes.toArray(new LocalQuickFix[fixes.size()]); } catch (IncorrectOperationException e) { @@ -272,11 +271,29 @@ public class DataFlowInspectionBase extends BaseJavaBatchLocalInspectionTool { reportNullableArgumentsPassedToNonAnnotated(visitor, holder, reportedAnchors); } + reportOptionalOfNullableImprovements(holder, visitor, reportedAnchors); + + if (REPORT_CONSTANT_REFERENCE_VALUES) { reportConstantReferenceValues(holder, visitor, reportedAnchors); } } + private static void reportOptionalOfNullableImprovements(ProblemsHolder holder, + DataFlowInstructionVisitor visitor, + HashSet reportedAnchors) { + for (PsiElement expr : visitor.getProblems(NullabilityProblem.passingNullToOptional)) { + if (!reportedAnchors.add(expr)) continue; + holder.registerProblem(expr, "Passing null argument to Optional", + DfaOptionalSupport.createReplaceOptionalOfNullableWithEmptyFix(expr)); + } + for (PsiElement expr : visitor.getProblems(NullabilityProblem.passingNotNullToOptional)) { + if (!reportedAnchors.add(expr)) continue; + holder.registerProblem(expr, "Passing a non-null argument to Optional", + DfaOptionalSupport.createReplaceOptionalOfNullableWithOfFix()); + } + } + private static void reportConstantReferenceValues(ProblemsHolder holder, StandardInstructionVisitor visitor, Set reportedAnchors) { for (Pair pair : visitor.getConstantReferenceValues()) { PsiReferenceExpression ref = pair.first; @@ -764,67 +781,4 @@ public class DataFlowInspectionBase extends BaseJavaBatchLocalInspectionTool { boolean normalOk; } } - - private static class ReplaceOptionalOfWithOfNullableFix implements LocalQuickFix { - - private static final String GUAVA_OPTIONAL = "com.google.common.base.Optional"; - private final String myTargetMethodName; - - public ReplaceOptionalOfWithOfNullableFix(final String targetMethodName) { - myTargetMethodName = targetMethodName; - } - - private static LocalQuickFix registerReplaceOptionalOfWithOfNullableFix(PsiExpression qualifier) { - final PsiElement argList = PsiUtil.skipParenthesizedExprUp(qualifier).getParent(); - if (argList instanceof PsiExpressionList) { - final PsiElement parent = argList.getParent(); - if (parent instanceof PsiMethodCallExpression) { - final PsiMethod method = ((PsiMethodCallExpression)parent).resolveMethod(); - if (method != null) { - final PsiClass containingClass = method.getContainingClass(); - if ("of".equals(method.getName()) && containingClass != null) { - final String qualifiedName = containingClass.getQualifiedName(); - if (CommonClassNames.JAVA_UTIL_OPTIONAL.equals(qualifiedName)) { - return new ReplaceOptionalOfWithOfNullableFix("ofNullable"); - } - else if (GUAVA_OPTIONAL.equals(qualifiedName)) { - return new ReplaceOptionalOfWithOfNullableFix("fromNullable"); - } - } - } - } - } - return null; - } - - @Nls - @NotNull - @Override - public String getName() { - return getFamilyName(); - } - - @NotNull - @Override - public String getFamilyName() { - return "Replace with '." + myTargetMethodName + "()'"; - } - - @Override - public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { - final PsiMethodCallExpression - methodCallExpression = PsiTreeUtil.getParentOfType(descriptor.getPsiElement(), PsiMethodCallExpression.class); - if (methodCallExpression != null) { - final PsiElement ofNullableExprName = - ((PsiMethodCallExpression)JavaPsiFacade.getElementFactory(project) - .createExpressionFromText("Optional.ofNullable(null)", null)).getMethodExpression(); - final PsiElement referenceNameElement = methodCallExpression.getMethodExpression().getReferenceNameElement(); - if (referenceNameElement != null) { - final PsiElement ofNullableNameElement = ((PsiReferenceExpression)ofNullableExprName).getReferenceNameElement(); - LOG.assertTrue(ofNullableNameElement != null); - referenceNameElement.replace(ofNullableNameElement); - } - } - } - } } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaOptionalSupport.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaOptionalSupport.java new file mode 100644 index 000000000000..12c0d3ca6f3f --- /dev/null +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaOptionalSupport.java @@ -0,0 +1,139 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.codeInspection.dataFlow; + +import com.intellij.codeInspection.LocalQuickFix; +import com.intellij.codeInspection.ProblemDescriptor; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.project.Project; +import com.intellij.psi.*; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.util.PsiUtil; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * @author anet, peter + */ +class DfaOptionalSupport { + private static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.dataFlow.DfaOptionalSupport"); + private static final String GUAVA_OPTIONAL = "com.google.common.base.Optional"; + + @Nullable + static LocalQuickFix registerReplaceOptionalOfWithOfNullableFix(@NotNull PsiExpression qualifier) { + final PsiElement call = findCallExpression(qualifier); + final PsiMethod method = call == null ? null : ((PsiMethodCallExpression)call).resolveMethod(); + final PsiClass containingClass = method == null ? null : method.getContainingClass(); + if (containingClass != null && "of".equals(method.getName())) { + final String qualifiedName = containingClass.getQualifiedName(); + if (CommonClassNames.JAVA_UTIL_OPTIONAL.equals(qualifiedName)) { + return new ReplaceOptionalCallFix("ofNullable", false); + } + if (GUAVA_OPTIONAL.equals(qualifiedName)) { + return new ReplaceOptionalCallFix("fromNullable", false); + } + } + return null; + } + + private static PsiMethodCallExpression findCallExpression(@NotNull PsiElement anchor) { + final PsiElement argList = PsiUtil.skipParenthesizedExprUp(anchor).getParent(); + if (argList instanceof PsiExpressionList) { + final PsiElement parent = argList.getParent(); + if (parent instanceof PsiMethodCallExpression) { + return (PsiMethodCallExpression)parent; + } + } + return null; + } + private static boolean isJdkOptional(@NotNull PsiElement anchor) { + final PsiElement parent = findCallExpression(anchor); + PsiMethod method = parent == null ? null : resolveOfNullable(findCallExpression(anchor)); + return method != null && "ofNullable".equals(method.getName()); + } + + @NotNull + static LocalQuickFix createReplaceOptionalOfNullableWithEmptyFix(@NotNull PsiElement anchor) { + return new ReplaceOptionalCallFix(isJdkOptional(anchor) ? "empty" : "absent", true); + } + + @NotNull + static LocalQuickFix createReplaceOptionalOfNullableWithOfFix() { + return new ReplaceOptionalCallFix("of", false); + } + + @Nullable + static PsiMethod resolveOfNullable(PsiCallExpression expression) { + String name = ((PsiMethodCallExpression)expression).getMethodExpression().getReferenceName(); + if ("ofNullable".equals(name) || "fromNullable".equals(name)) { + PsiMethod method = expression.resolveMethod(); + PsiClass psiClass = method == null ? null : method.getContainingClass(); + String qname = psiClass == null ? null : psiClass.getQualifiedName(); + if (CommonClassNames.JAVA_UTIL_OPTIONAL.equals(qname) || GUAVA_OPTIONAL.equals(qname)) { + return method; + } + } + return null; + } + + private static class ReplaceOptionalCallFix implements LocalQuickFix { + private final String myTargetMethodName; + private final boolean myClearArguments; + + public ReplaceOptionalCallFix(final String targetMethodName, boolean clearArguments) { + myTargetMethodName = targetMethodName; + myClearArguments = clearArguments; + } + + @Nls + @NotNull + @Override + public String getName() { + return getFamilyName(); + } + + @NotNull + @Override + public String getFamilyName() { + return "Replace with '." + myTargetMethodName + "()'"; + } + + @Override + public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { + final PsiMethodCallExpression + methodCallExpression = PsiTreeUtil.getParentOfType(descriptor.getPsiElement(), PsiMethodCallExpression.class); + if (methodCallExpression != null) { + final PsiElement ofNullableExprName = + ((PsiMethodCallExpression)JavaPsiFacade.getElementFactory(project) + .createExpressionFromText("Optional." + myTargetMethodName + "(null)", null)).getMethodExpression(); + final PsiElement referenceNameElement = methodCallExpression.getMethodExpression().getReferenceNameElement(); + if (referenceNameElement != null) { + final PsiElement ofNullableNameElement = ((PsiReferenceExpression)ofNullableExprName).getReferenceNameElement(); + LOG.assertTrue(ofNullableNameElement != null); + referenceNameElement.replace(ofNullableNameElement); + } + if (myClearArguments) { + PsiExpressionList argList = methodCallExpression.getArgumentList(); + PsiExpression[] args = argList.getExpressions(); + if (args.length > 0) { + argList.deleteChildRange(args[0], args[args.length - 1]); + } + } + } + } + } +} diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/NullabilityProblem.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/NullabilityProblem.java index 8155149ed1ec..2810ffdb2e34 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/NullabilityProblem.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/NullabilityProblem.java @@ -11,4 +11,6 @@ public enum NullabilityProblem { nullableReturn, passingNullableToNotNullParameter, passingNullableArgumentToNonAnnotatedParameter, + passingNullToOptional, + passingNotNullToOptional } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StandardInstructionVisitor.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StandardInstructionVisitor.java index 898c744286bc..66893c0c0c1b 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StandardInstructionVisitor.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StandardInstructionVisitor.java @@ -57,6 +57,15 @@ public class StandardInstructionVisitor extends InstructionVisitor { return callExpression != null ? DfaPsiUtil.getElementNullability(key.getResultType(), callExpression.resolveMethod()) : null; } }; + @SuppressWarnings("MismatchedQueryAndUpdateOfCollection") + private final FactoryMap myOptionOfNullable = new FactoryMap() { + @Nullable + @Override + protected Boolean create(MethodCallInstruction key) { + PsiCallExpression expression = key.getCallExpression(); + return expression instanceof PsiMethodCallExpression && DfaOptionalSupport.resolveOfNullable(expression) != null; + } + }; @Override public DfaInstructionState[] visitAssign(AssignInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) { @@ -231,6 +240,10 @@ public class StandardInstructionVisitor extends InstructionVisitor { forceNotNull(runner, memState, arg); } } + else if (myOptionOfNullable.get(instruction)) { + checkNotNullable(memState, arg, NullabilityProblem.passingNotNullToOptional, expr); + checkNotNullable(memState, arg, NullabilityProblem.passingNullToOptional, expr); + } else if (requiredNullability == Nullness.UNKNOWN) { checkNotNullable(memState, arg, NullabilityProblem.passingNullableArgumentToNonAnnotatedParameter, expr); } @@ -371,8 +384,14 @@ public class StandardInstructionVisitor extends InstructionVisitor { protected boolean checkNotNullable(DfaMemoryState state, DfaValue value, NullabilityProblem problem, PsiElement anchor) { + if (problem == NullabilityProblem.passingNotNullToOptional) { + return !state.isNotNull(value); + } + boolean notNullable = state.checkNotNullable(value); - if (notNullable && problem != NullabilityProblem.passingNullableArgumentToNonAnnotatedParameter) { + if (notNullable && + problem != NullabilityProblem.passingNullableArgumentToNonAnnotatedParameter && + problem != NullabilityProblem.passingNullToOptional) { DfaValueFactory factory = ((DfaMemoryStateImpl)state).getFactory(); state.applyCondition(factory.getRelationFactory().createRelation(value, factory.getConstFactory().getNull(), NE, false)); } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/java15api/Java15APIUsageInspectionBase.java b/java/java-analysis-impl/src/com/intellij/codeInspection/java15api/Java15APIUsageInspectionBase.java index 63f50480bcfc..37a41a198182 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/java15api/Java15APIUsageInspectionBase.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/java15api/Java15APIUsageInspectionBase.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,6 +21,7 @@ import com.intellij.codeInsight.AnnotationUtil; import com.intellij.codeInsight.daemon.GroupNames; import com.intellij.codeInsight.intention.QuickFixFactory; import com.intellij.codeInspection.*; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.extensions.ExtensionPoint; import com.intellij.openapi.extensions.ExtensionPointName; import com.intellij.openapi.extensions.Extensions; @@ -31,6 +32,7 @@ import com.intellij.openapi.projectRoots.JavaSdkVersion; import com.intellij.openapi.projectRoots.JavaVersionService; import com.intellij.openapi.util.InvalidDataException; import com.intellij.openapi.util.WriteExternalException; +import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.CharsetToolkit; import com.intellij.pom.java.LanguageLevel; import com.intellij.psi.*; @@ -43,7 +45,6 @@ import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.hash.HashSet; import gnu.trove.THashSet; import org.jdom.Element; -import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -52,6 +53,7 @@ import java.io.IOException; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.lang.ref.Reference; +import java.net.URL; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -107,16 +109,17 @@ public class Java15APIUsageInspectionBase extends BaseJavaBatchLocalInspectionTo return result; } - private static void loadForbiddenApi(@NonNls String fileName, Set set) { + private static void loadForbiddenApi(String fileName, Set set) { + URL resource = Java15APIUsageInspectionBase.class.getResource(fileName); + if (resource == null) { + Logger.getInstance(Java15APIUsageInspectionBase.class).warn("not found: " + fileName); + return; + } + try { - Class aClass = Java15APIUsageInspectionBase.class; - BufferedReader reader = new BufferedReader(new InputStreamReader(aClass.getResourceAsStream(fileName), CharsetToolkit.UTF8_CHARSET)); + BufferedReader reader = new BufferedReader(new InputStreamReader(resource.openStream(), CharsetToolkit.UTF8_CHARSET)); try { - do { - String line = reader.readLine(); - if (line == null) break; - set.add(line); - } while(true); + set.addAll(FileUtil.loadLines(reader)); } finally { reader.close(); @@ -330,6 +333,7 @@ public class Java15APIUsageInspectionBase extends BaseJavaBatchLocalInspectionTo private void registerError(PsiJavaCodeReferenceElement reference, LanguageLevel api) { if (reference != null && isInProject(reference)) { + //noinspection DialogTitleCapitalization myHolder.registerProblem(reference, InspectionsBundle.message("inspection.1.5.problem.descriptor", getShortName(api))); } } @@ -382,7 +386,7 @@ public class Java15APIUsageInspectionBase extends BaseJavaBatchLocalInspectionTo } /** - * please leave public for {@link #com.intellij.codeInspection.JavaAPIUsagesInspectionTest#testCollectSinceApiUsages} + * please leave public for JavaAPIUsagesInspectionTest#testCollectSinceApiUsages */ @Nullable public static String getSignature(@Nullable PsiMember member) { diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaInheritorsGetter.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaInheritorsGetter.java index e96eb36c6385..a6ee18c6e4a1 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaInheritorsGetter.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaInheritorsGetter.java @@ -160,20 +160,23 @@ public class JavaInheritorsGetter extends CompletionProvider()", parameters.getOriginalFile()); - final PsiVariable declaredVar = (PsiVariable)((PsiDeclarationStatement)statement).getDeclaredElements()[0]; - final PsiNewExpression initializer = (PsiNewExpression)declaredVar.getInitializer(); - final boolean hasDefaultConstructorOrNoGenericsOne = PsiDiamondTypeImpl.hasDefaultConstructor(psiClass) || - !PsiDiamondTypeImpl.haveConstructorsGenericsParameters(psiClass); - if (hasDefaultConstructorOrNoGenericsOne) { - final PsiDiamondTypeImpl.DiamondInferenceResult inferenceResult = PsiDiamondTypeImpl.resolveInferredTypes(initializer); - if (inferenceResult.getErrorMessage() == null && - !psiClass.hasModifierProperty(PsiModifier.ABSTRACT) && - areInferredTypesApplicable(inferenceResult.getTypes(), parameters.getPosition())) { - psiType = initializer.getType(); + try { + final PsiStatement statement = elementFactory + .createStatementFromText(canonicalText + " v = new " + erasedText + "<>()", parameters.getOriginalFile()); + final PsiVariable declaredVar = (PsiVariable)((PsiDeclarationStatement)statement).getDeclaredElements()[0]; + final PsiNewExpression initializer = (PsiNewExpression)declaredVar.getInitializer(); + final boolean hasDefaultConstructorOrNoGenericsOne = PsiDiamondTypeImpl.hasDefaultConstructor(psiClass) || + !PsiDiamondTypeImpl.haveConstructorsGenericsParameters(psiClass); + if (hasDefaultConstructorOrNoGenericsOne) { + final PsiDiamondTypeImpl.DiamondInferenceResult inferenceResult = PsiDiamondTypeImpl.resolveInferredTypes(initializer); + if (inferenceResult.getErrorMessage() == null && + !psiClass.hasModifierProperty(PsiModifier.ABSTRACT) && + areInferredTypesApplicable(inferenceResult.getTypes(), parameters.getPosition())) { + psiType = initializer.getType(); + } } } + catch (IncorrectOperationException ignore) {} } } final PsiTypeLookupItem item = PsiTypeLookupItem.createLookupItem(psiType, position); diff --git a/java/java-impl/src/com/intellij/refactoring/changeSignature/JavaChangeSignatureDialog.java b/java/java-impl/src/com/intellij/refactoring/changeSignature/JavaChangeSignatureDialog.java index ce84d8a84dbc..11df0db74a8a 100644 --- a/java/java-impl/src/com/intellij/refactoring/changeSignature/JavaChangeSignatureDialog.java +++ b/java/java-impl/src/com/intellij/refactoring/changeSignature/JavaChangeSignatureDialog.java @@ -26,7 +26,6 @@ import com.intellij.openapi.editor.colors.EditorColorsManager; import com.intellij.openapi.editor.colors.EditorFontType; import com.intellij.openapi.editor.event.DocumentAdapter; import com.intellij.openapi.editor.event.DocumentEvent; -import com.intellij.openapi.fileTypes.FileType; import com.intellij.openapi.fileTypes.LanguageFileType; import com.intellij.openapi.fileTypes.StdFileTypes; import com.intellij.openapi.project.Project; @@ -226,7 +225,8 @@ public class JavaChangeSignatureDialog extends ChangeSignatureDialogBase item = getRowItem(row); @@ -249,12 +249,6 @@ public class JavaChangeSignatureDialog extends ChangeSignatureDialogBase1); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/afterGuavaNullLiteral.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/afterGuavaNullLiteral.java new file mode 100644 index 000000000000..e5599f2a86b6 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/afterGuavaNullLiteral.java @@ -0,0 +1,6 @@ +// "Replace with '.absent()'" "true" +class A{ + void test(){ + com.google.common.base.Optional.absent(); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/afterNotNullLiteral.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/afterNotNullLiteral.java new file mode 100644 index 000000000000..bfe725293c8b --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/afterNotNullLiteral.java @@ -0,0 +1,6 @@ +// "Replace with '.of()'" "true" +class A{ + void test(){ + java.util.Optional.of(11); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/afterNotNullVariable.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/afterNotNullVariable.java new file mode 100644 index 000000000000..c1e85703be4b --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/afterNotNullVariable.java @@ -0,0 +1,8 @@ +// "Replace with '.of()'" "true" + +class A{ + void test(String s){ + assert s != null; + java.util.Optional.of(s); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/afterNullLiteral.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/afterNullLiteral.java new file mode 100644 index 000000000000..850a7c3df1c8 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/afterNullLiteral.java @@ -0,0 +1,7 @@ +// "Replace with '.empty()'" "true" +class A { + void test() { + String s = null; + java.util.Optional.empty(); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/afterNullVariable.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/afterNullVariable.java new file mode 100644 index 000000000000..850a7c3df1c8 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/afterNullVariable.java @@ -0,0 +1,7 @@ +// "Replace with '.empty()'" "true" +class A { + void test() { + String s = null; + java.util.Optional.empty(); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/beforeGuavaNotNullLiteral.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/beforeGuavaNotNullLiteral.java new file mode 100644 index 000000000000..8c7c9be02f35 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/beforeGuavaNotNullLiteral.java @@ -0,0 +1,6 @@ +// "Replace with '.of()'" "true" +class A{ + void test(){ + com.google.common.base.Optional.fromNullable(11); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/beforeGuavaNullLiteral.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/beforeGuavaNullLiteral.java new file mode 100644 index 000000000000..fa25c555e2d0 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/beforeGuavaNullLiteral.java @@ -0,0 +1,6 @@ +// "Replace with '.absent()'" "true" +class A{ + void test(){ + com.google.common.base.Optional.fromNullable(null); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/beforeNotNullLiteral.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/beforeNotNullLiteral.java new file mode 100644 index 000000000000..5ef9972f2ca8 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/beforeNotNullLiteral.java @@ -0,0 +1,6 @@ +// "Replace with '.of()'" "true" +class A{ + void test(){ + java.util.Optional.ofNullable(11); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/beforeNotNullVariable.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/beforeNotNullVariable.java new file mode 100644 index 000000000000..53c9a43841f6 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/beforeNotNullVariable.java @@ -0,0 +1,8 @@ +// "Replace with '.of()'" "true" + +class A{ + void test(String s){ + assert s != null; + java.util.Optional.ofNullable(s); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/beforeNullLiteral.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/beforeNullLiteral.java new file mode 100644 index 000000000000..b57dda49a134 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/beforeNullLiteral.java @@ -0,0 +1,7 @@ +// "Replace with '.empty()'" "true" +class A { + void test() { + String s = null; + java.util.Optional.ofNullable(null); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/beforeNullVariable.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/beforeNullVariable.java new file mode 100644 index 000000000000..5f6d350318ae --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/beforeNullVariable.java @@ -0,0 +1,7 @@ +// "Replace with '.empty()'" "true" +class A { + void test() { + String s = null; + java.util.Optional.ofNullable(s); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/beforeUnknown.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/beforeUnknown.java new file mode 100644 index 000000000000..6d506e8e8529 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/beforeUnknown.java @@ -0,0 +1,6 @@ +// "Replace with '.of()'" "false" +class A{ + void test(String s){ + java.util.Optional.ofNullable(s); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceWithOfNullable/afterGuavaNullable.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceWithOfNullable/afterGuavaNullable.java new file mode 100644 index 000000000000..007fbb6d4e2a --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceWithOfNullable/afterGuavaNullable.java @@ -0,0 +1,6 @@ +// "Replace with '.fromNullable()'" "true" +class A{ + void test(){ + com.google.common.base.Optional.fromNullable(null); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceWithOfNullable/beforeGuavaNullable.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceWithOfNullable/beforeGuavaNullable.java new file mode 100644 index 000000000000..a438e6a82110 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceWithOfNullable/beforeGuavaNullable.java @@ -0,0 +1,6 @@ +// "Replace with '.fromNullable()'" "true" +class A{ + void test(){ + com.google.common.base.Optional.of(null); + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/ReplaceFromOfNullableFixTest.groovy b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/ReplaceFromOfNullableFixTest.groovy new file mode 100644 index 000000000000..15ef5ff77f14 --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/ReplaceFromOfNullableFixTest.groovy @@ -0,0 +1,89 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.intellij.codeInsight.daemon.quickFix +import com.intellij.codeInspection.LocalInspectionTool +import com.intellij.codeInspection.dataFlow.DataFlowInspection +import com.intellij.openapi.Disposable +import com.intellij.openapi.command.WriteCommandAction +import com.intellij.openapi.projectRoots.Sdk +import com.intellij.openapi.vfs.VfsUtil +import com.intellij.openapi.vfs.VirtualFile +import com.intellij.testFramework.IdeaTestUtil +import org.jetbrains.annotations.NotNull + +public class ReplaceFromOfNullableFixTest extends LightQuickFixParameterizedTestCase { + @NotNull + @Override + protected LocalInspectionTool[] configureLocalInspectionTools() { + return [new DataFlowInspection()] as LocalInspectionTool[] + } + + public void test() throws Exception { + doAllTests(); + } + + @Override + protected String getBasePath() { + return "/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable"; + } + + static void addGuavaOptional(Disposable parent) { + WriteCommandAction.runWriteCommandAction(project) { + VirtualFile optional = getSourceRoot() + .createChildDirectory(this, "com") + .createChildDirectory(this, "google") + .createChildDirectory(this, "common") + .createChildDirectory(this, "base") + .createChildData(this, "Optional.java"); + VfsUtil.saveText(optional, """ +package com.google.common.base; +public abstract class Optional { + public static Optional absent() { } + + public static Optional of(@org.jetbrains.annotations.NotNull T reference) { } + + public static Optional fromNullable(T nullableReference) { } +} +""") + } + } + + static void cleanupGuava() { + WriteCommandAction.runWriteCommandAction(project) { + getSourceRoot().findChild("com")?.delete(this) + } + } + + @Override + protected void beforeActionStarted(String testName, String contents) { + if (testName.contains("Guava")) { + addGuavaOptional(testRootDisposable) + } + super.beforeActionStarted(testName, contents) + } + + @Override + protected void afterActionCompleted(String testName, String contents) { + cleanupGuava() + super.afterActionCompleted(testName, contents) + } + + @Override + protected Sdk getProjectJDK() { + return IdeaTestUtil.getMockJdk18(); + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/ReplaceWithOfNullableFixTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/ReplaceWithOfNullableFixTest.java index 6eef97352f65..9c5c07013b7e 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/ReplaceWithOfNullableFixTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/ReplaceWithOfNullableFixTest.java @@ -42,6 +42,20 @@ public class ReplaceWithOfNullableFixTest extends LightQuickFixParameterizedTest return "/codeInsight/daemonCodeAnalyzer/quickFix/replaceWithOfNullable"; } + @Override + protected void beforeActionStarted(String testName, String contents) { + if (testName.contains("Guava")) { + ReplaceFromOfNullableFixTest.addGuavaOptional(myTestRootDisposable); + } + super.beforeActionStarted(testName, contents); + } + + @Override + protected void afterActionCompleted(String testName, String contents) { + ReplaceFromOfNullableFixTest.cleanupGuava(); + super.afterActionCompleted(testName, contents); + } + @Override protected Sdk getProjectJDK() { return IdeaTestUtil.getMockJdk18(); diff --git a/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTestSuite.java b/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTestSuite.java index 674a0f8cbb50..c7664bd15bc8 100644 --- a/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTestSuite.java +++ b/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTestSuite.java @@ -17,6 +17,9 @@ package com.intellij.codeInspection; import com.intellij.codeInsight.completion.NormalCompletionDfaTest; import com.intellij.codeInsight.completion.SmartTypeCompletionDfaTest; +import com.intellij.codeInsight.daemon.quickFix.AddAssertStatementFixTest; +import com.intellij.codeInsight.daemon.quickFix.ReplaceFromOfNullableFixTest; +import com.intellij.codeInsight.daemon.quickFix.ReplaceWithOfNullableFixTest; import com.intellij.slicer.SliceBackwardTest; import com.intellij.slicer.SliceTreeTest; import junit.framework.Test; @@ -25,20 +28,29 @@ import junit.framework.TestSuite; public class DataFlowInspectionTestSuite { public static Test suite() { TestSuite suite = new TestSuite(); + suite.addTestSuite(DataFlowInspectionTest.class); suite.addTestSuite(DataFlowInspection8Test.class); suite.addTestSuite(DataFlowInspectionAncientTest.class); suite.addTestSuite(ContractCheckTest.class); + suite.addTestSuite(ContractInferenceFromSourceTest.class); suite.addTestSuite(NullityInferenceFromSourceTestCase.DfaInferenceTest.class); suite.addTestSuite(NullityInferenceFromSourceTestCase.LightInferenceTest.class); suite.addTestSuite(PurityInferenceFromSourceTest.class); + suite.addTestSuite(SliceTreeTest.class); suite.addTestSuite(SliceBackwardTest.class); + suite.addTestSuite(SmartTypeCompletionDfaTest.class); suite.addTestSuite(NormalCompletionDfaTest.class); + suite.addTestSuite(NullableStuffInspectionTest.class); suite.addTestSuite(NullableStuffInspection14Test.class); + + suite.addTestSuite(AddAssertStatementFixTest.class); + suite.addTestSuite(ReplaceWithOfNullableFixTest.class); + suite.addTestSuite(ReplaceFromOfNullableFixTest.class); return suite; } } diff --git a/java/java-tests/testSrc/com/intellij/execution/ConfigurationsTest.java b/java/java-tests/testSrc/com/intellij/execution/ConfigurationsTest.java index 73ddb14b4805..1bc6d3e6a92b 100644 --- a/java/java-tests/testSrc/com/intellij/execution/ConfigurationsTest.java +++ b/java/java-tests/testSrc/com/intellij/execution/ConfigurationsTest.java @@ -136,8 +136,9 @@ public class ConfigurationsTest extends BaseConfigurationTestCase { JUnitConfiguration configuration = createConfiguration(testA); JavaParameters parameters = checkCanRun(configuration); CHECK.empty(parameters.getVMParametersList().getList()); + final SegmentedOutputStream notifications = new SegmentedOutputStream(System.out); assertTrue(JUnitStarter.checkVersion(parameters.getProgramParametersList().getArray(), - new SegmentedOutputStream(System.out))); + new PrintStream(notifications))); assertTrue(parameters.getProgramParametersList().getList().contains(testA.getQualifiedName())); assertEquals(JUnitStarter.class.getName(), parameters.getMainClass()); assertEquals(myJdk.getHomeDirectory().getPresentableUrl(), parameters.getJdkPath()); diff --git a/java/java-tests/testSrc/com/intellij/execution/configurations/JavaCommandLineTest.java b/java/java-tests/testSrc/com/intellij/execution/configurations/JavaCommandLineTest.java index e4d0bc039c5d..730ea6005104 100644 --- a/java/java-tests/testSrc/com/intellij/execution/configurations/JavaCommandLineTest.java +++ b/java/java-tests/testSrc/com/intellij/execution/configurations/JavaCommandLineTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2011 JetBrains s.r.o. + * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ package com.intellij.execution.configurations; import com.intellij.execution.CantRunException; import com.intellij.execution.ExecutionBundle; import com.intellij.execution.ExecutionException; -import com.intellij.execution.process.DefaultJavaProcessHandler; +import com.intellij.execution.process.KillableColoredProcessHandler; import com.intellij.ide.IdeBundle; import com.intellij.openapi.projectRoots.Sdk; import com.intellij.openapi.projectRoots.impl.JavaAwareProjectJdkTableImpl; @@ -101,7 +101,7 @@ public class JavaCommandLineTest extends LightIdeaTestCase { public void testCreateProcess() { try { - new DefaultJavaProcessHandler(new GeneralCommandLine()); + new KillableColoredProcessHandler(new GeneralCommandLine()); fail("ExecutionException (executable is not specified) expected"); } catch (ExecutionException e) { diff --git a/java/manifest/src/org/jetbrains/lang/manifest/highlighting/MissingFinalNewlineInspection.java b/java/manifest/src/org/jetbrains/lang/manifest/highlighting/MissingFinalNewlineInspection.java index 7404b27de30f..60bfce5fd13c 100644 --- a/java/manifest/src/org/jetbrains/lang/manifest/highlighting/MissingFinalNewlineInspection.java +++ b/java/manifest/src/org/jetbrains/lang/manifest/highlighting/MissingFinalNewlineInspection.java @@ -61,16 +61,14 @@ public class MissingFinalNewlineInspection extends LocalInspectionTool { return null; } - private static class AddNewlineQuickFix implements LocalQuickFix { - private final Section mySection; - + private static class AddNewlineQuickFix extends LocalQuickFixOnPsiElement { private AddNewlineQuickFix(Section section) { - mySection = section; + super(section); } @NotNull @Override - public String getName() { + public String getText() { return ManifestBundle.message("inspection.newline.fix"); } @@ -81,8 +79,8 @@ public class MissingFinalNewlineInspection extends LocalInspectionTool { } @Override - public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { - PsiElement lastChild = mySection.getLastChild(); + public void invoke(@NotNull Project project, @NotNull PsiFile file, @NotNull PsiElement startElement, @NotNull PsiElement endElement) { + PsiElement lastChild = startElement.getLastChild(); if (lastChild instanceof Header) { lastChild.getNode().addLeaf(ManifestTokenType.NEWLINE, "\n", null); } diff --git a/java/manifest/test/org/jetbrains/lang/manifest/MissingFinalNewlineInspectionTest.java b/java/manifest/test/org/jetbrains/lang/manifest/MissingFinalNewlineInspectionTest.java index a55b938145cb..d0b56a2fd6df 100644 --- a/java/manifest/test/org/jetbrains/lang/manifest/MissingFinalNewlineInspectionTest.java +++ b/java/manifest/test/org/jetbrains/lang/manifest/MissingFinalNewlineInspectionTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,7 +17,6 @@ package org.jetbrains.lang.manifest; import com.intellij.codeInsight.intention.IntentionAction; import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase; -import junit.framework.Assert; import org.jetbrains.lang.manifest.highlighting.MissingFinalNewlineInspection; public class MissingFinalNewlineInspectionTest extends LightCodeInsightFixtureTestCase { @@ -29,12 +28,12 @@ public class MissingFinalNewlineInspectionTest extends LightCodeInsightFixtureTe public void testEmptyFile() { myFixture.configureByText(ManifestFileTypeFactory.MANIFEST, ""); - Assert.assertEquals(0, myFixture.getAvailableIntentions().size()); + assertEquals(0, myFixture.getAvailableIntentions().size()); } public void testNoProblem() { myFixture.configureByText(ManifestFileTypeFactory.MANIFEST, "Manifest-Version: 1.0\n"); - Assert.assertEquals(0, myFixture.getAvailableIntentions().size()); + assertEquals(0, myFixture.getAvailableIntentions().size()); } public void testFix() { diff --git a/json/src/com/intellij/json/codeinsight/JsonLiteralAnnotator.java b/json/src/com/intellij/json/codeinsight/JsonLiteralAnnotator.java index 2854c8a7132e..c468c0767697 100644 --- a/json/src/com/intellij/json/codeinsight/JsonLiteralAnnotator.java +++ b/json/src/com/intellij/json/codeinsight/JsonLiteralAnnotator.java @@ -54,7 +54,7 @@ public class JsonLiteralAnnotator implements Annotator { final int length = text.length(); // Check that string literal is closed properly - if (length <= 1 || text.charAt(0) != text.charAt(length - 1) || quoteEscaped(text, length - 1)) { + if (length <= 1 || text.charAt(0) != text.charAt(length - 1) || JsonPsiUtil.isEscapedChar(text, length - 1)) { holder.createErrorAnnotation(element, JsonBundle.message("msg.missing.closing.quote")); } @@ -79,12 +79,4 @@ public class JsonLiteralAnnotator implements Annotator { } } } - - private static boolean quoteEscaped(String text, int quotePos) { - int count = 0; - for (int i = quotePos - 1; i >= 0 && text.charAt(i) == '\\'; i--) { - count++; - } - return count % 2 != 0; - } } diff --git a/json/src/com/intellij/json/codeinsight/JsonStandardComplianceInspection.java b/json/src/com/intellij/json/codeinsight/JsonStandardComplianceInspection.java index 58dab5945dfc..b4f7ffc2374d 100644 --- a/json/src/com/intellij/json/codeinsight/JsonStandardComplianceInspection.java +++ b/json/src/com/intellij/json/codeinsight/JsonStandardComplianceInspection.java @@ -6,9 +6,9 @@ import com.intellij.codeInspection.ui.MultipleCheckboxOptionsPanel; import com.intellij.json.JsonBundle; import com.intellij.json.JsonElementTypes; import com.intellij.json.psi.*; +import com.intellij.lang.ASTNode; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiComment; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiElementVisitor; @@ -139,13 +139,42 @@ public class JsonStandardComplianceInspection extends LocalInspectionTool { @Override public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { final PsiElement element = descriptor.getPsiElement(); + final String rawText = element.getText(); if (element instanceof JsonLiteral || element instanceof JsonReferenceExpression) { - final String content = StringUtil.stripQuotesAroundValue(element.getText()); - element.replace(new JsonElementGenerator(project).createStringLiteral(content)); + String content = JsonPsiUtil.stripQuotes(rawText); + if (element instanceof JsonStringLiteral && rawText.startsWith("'")) { + content = escapeSingleQuotedStringContent(content); + } + // TODO: find out better way to replace element and skip reformatting step afterwards + final ASTNode replacement = new JsonElementGenerator(project).createValue("\"" + content + "\"").getNode(); + element.getParent().getNode().replaceChild(element.getNode(), replacement); } else if (element != null) { - LOG.error("Quick fix was applied to unexpected element", element.getText(), element.getParent().getText()); + LOG.error("Quick fix was applied to unexpected element", rawText, element.getParent().getText()); } } + + @NotNull + private static String escapeSingleQuotedStringContent(@NotNull String content) { + final StringBuilder result = new StringBuilder(); + boolean nextCharEscaped = false; + for (int i = 0; i < content.length(); i++) { + final char c = content.charAt(i); + if ((nextCharEscaped && c != '\'') || (!nextCharEscaped && c == '"')) { + result.append('\\'); + } + if (c != '\\' || nextCharEscaped) { + result.append(c); + nextCharEscaped = false; + } + else { + nextCharEscaped = true; + } + } + if (nextCharEscaped) { + result.append('\\'); + } + return result.toString(); + } } } diff --git a/json/src/com/intellij/json/formatter/JsonBlock.java b/json/src/com/intellij/json/formatter/JsonBlock.java index c503c0df2290..174e1607f010 100644 --- a/json/src/com/intellij/json/formatter/JsonBlock.java +++ b/json/src/com/intellij/json/formatter/JsonBlock.java @@ -66,10 +66,10 @@ public class JsonBlock implements ASTBlock { mySpacingBuilder = JsonFormattingBuilderModel.createSpacingBuilder(settings); if (myPsiElement instanceof JsonObject) { - myChildWrap = Wrap.createWrap(getCustomSettings().OBJECT_WRAPPING, false); + myChildWrap = Wrap.createWrap(getCustomSettings().OBJECT_WRAPPING, true); } else if (myPsiElement instanceof JsonArray) { - myChildWrap = Wrap.createWrap(getCustomSettings().ARRAY_WRAPPING, false); + myChildWrap = Wrap.createWrap(getCustomSettings().ARRAY_WRAPPING, true); } else { myChildWrap = null; @@ -113,7 +113,10 @@ public class JsonBlock implements ASTBlock { final JsonCodeStyleSettings customSettings = getCustomSettings(); if (hasElementType(myNode, JSON_CONTAINERS)) { - if (!hasElementType(childNode, COMMA) && !hasElementType(childNode, JSON_ALL_BRACES)) { + if (hasElementType(childNode, COMMA)) { + wrap = Wrap.createWrap(WrapType.NONE, true); + } + else if (!hasElementType(childNode, JSON_ALL_BRACES)) { assert myChildWrap != null; wrap = myChildWrap; indent = Indent.getNormalIndent(); diff --git a/json/src/com/intellij/json/psi/JsonPsiUtil.java b/json/src/com/intellij/json/psi/JsonPsiUtil.java index 66f785c0e76b..80105014aa39 100644 --- a/json/src/com/intellij/json/psi/JsonPsiUtil.java +++ b/json/src/com/intellij/json/psi/JsonPsiUtil.java @@ -137,4 +137,46 @@ public class JsonPsiUtil { return element.getText(); } } + + /** + * Returns content of the string literal (without escaping) striving to preserve as much of user data as possible. + *
    + *
  • If literal length is greater than one and it starts and ends with the same quote and the last quote is not escaped, returns + * text without first and last characters.
  • + *
  • Otherwise if literal still begins with a quote, returns text without first character only.
  • + *
  • Returns unmodified text in all other cases.
  • + *
+ * + * @param text presumably result of {@link JsonStringLiteral#getText()} + * @return + */ + @NotNull + public static String stripQuotes(@NotNull String text) { + if (text.length() > 0) { + final char firstChar = text.charAt(0); + final char lastChar = text.charAt(text.length() - 1); + if (firstChar == '\'' || firstChar == '"') { + if (text.length() > 1 && firstChar == lastChar && !isEscapedChar(text, text.length() - 1)) { + return text.substring(1, text.length() - 1); + } + return text.substring(1); + } + } + return text; + } + + /** + * Checks that character in given position is escaped with backslashes. + * + * @param text text character belongs to + * @param position position of the character + * @return whether character at given position is escaped, i.e. preceded by odd number of backslashes + */ + public static boolean isEscapedChar(@NotNull String text, int position) { + int count = 0; + for (int i = position - 1; i >= 0 && text.charAt(i) == '\\'; i--) { + count++; + } + return count % 2 != 0; + } } diff --git a/json/src/com/intellij/json/psi/impl/JsonPsiImplUtils.java b/json/src/com/intellij/json/psi/impl/JsonPsiImplUtils.java index 59a112103398..d3814dc6c498 100644 --- a/json/src/com/intellij/json/psi/impl/JsonPsiImplUtils.java +++ b/json/src/com/intellij/json/psi/impl/JsonPsiImplUtils.java @@ -28,7 +28,7 @@ public class JsonPsiImplUtils { @NotNull public static String getName(@NotNull JsonProperty property) { - return StringUtil.stripQuotesAroundValue(property.getNameElement().getText()); + return StringUtil.unescapeStringCharacters(JsonPsiUtil.stripQuotes(property.getNameElement().getText())); } /** @@ -210,7 +210,7 @@ public class JsonPsiImplUtils { @NotNull public static String getValue(@NotNull JsonStringLiteral literal) { - return StringUtil.unescapeStringCharacters(StringUtil.unquoteString(literal.getText())); + return StringUtil.unescapeStringCharacters(JsonPsiUtil.stripQuotes(literal.getText())); } public static boolean getValue(@NotNull JsonBooleanLiteral literal) { diff --git a/json/tests/test/com/intellij/json/JsonFormattingTest.java b/json/tests/test/com/intellij/json/JsonFormattingTest.java index ff2b8a915eaf..f8d8d55f0691 100644 --- a/json/tests/test/com/intellij/json/JsonFormattingTest.java +++ b/json/tests/test/com/intellij/json/JsonFormattingTest.java @@ -101,6 +101,16 @@ public class JsonFormattingTest extends FormatterTestCase { }); } + // IDEA-138902 + public void testObjectsWithSingleProperty() throws Exception { + withPreservedSettings(new ThrowableRunnable() { + @Override + public void run() throws Exception { + doTest(); + } + }); + } + // Moved from JavaScript public void testWeb3830() throws Exception { diff --git a/json/tests/test/com/intellij/json/JsonQuickFixTest.java b/json/tests/test/com/intellij/json/JsonQuickFixTest.java index 8a80967b4da7..92d4cc4bb175 100644 --- a/json/tests/test/com/intellij/json/JsonQuickFixTest.java +++ b/json/tests/test/com/intellij/json/JsonQuickFixTest.java @@ -27,8 +27,10 @@ public class JsonQuickFixTest extends JsonTestCase { checkWrapInDoubleQuotes("{42: false}", "{\"42\": false}"); checkWrapInDoubleQuotes("{foo: false}", "{\"foo\": false}"); checkWrapInDoubleQuotes("{'foo': false}", "{\"foo\": false}"); + checkWrapInDoubleQuotes("'foo\\\"", "\"foo\\\"\""); checkWrapInDoubleQuotes("{\"foo\": bar}", "{\"foo\": \"bar\"}"); checkWrapInDoubleQuotes("{\"foo\": 'bar'}", "{\"foo\": \"bar\"}"); + checkWrapInDoubleQuotes("'foo\\n\\'\"\\\\\\\"bar", "\"foo\\n'\\\"\\\\\\\"bar\""); } private void checkWrapInDoubleQuotes(@NotNull String before, @NotNull String after) { diff --git a/json/tests/testData/formatting/ObjectsWithSingleProperty.json b/json/tests/testData/formatting/ObjectsWithSingleProperty.json new file mode 100644 index 000000000000..a75a8d3af061 --- /dev/null +++ b/json/tests/testData/formatting/ObjectsWithSingleProperty.json @@ -0,0 +1 @@ +{"level0": {"level1": {"level2": "someValue"}}} \ No newline at end of file diff --git a/json/tests/testData/formatting/ObjectsWithSingleProperty_after.json b/json/tests/testData/formatting/ObjectsWithSingleProperty_after.json new file mode 100644 index 000000000000..fe42890beca0 --- /dev/null +++ b/json/tests/testData/formatting/ObjectsWithSingleProperty_after.json @@ -0,0 +1,7 @@ +{ + "level0": { + "level1": { + "level2": "someValue" + } + } +} \ No newline at end of file diff --git a/json/tests/testData/formatting/Wrapping_after.json b/json/tests/testData/formatting/Wrapping_after.json index f6f41b61f43b..e53138a182de 100644 --- a/json/tests/testData/formatting/Wrapping_after.json +++ b/json/tests/testData/formatting/Wrapping_after.json @@ -8,5 +8,7 @@ 5, 6, 7, - ["this line is long too"] + [ + "this line is long too" + ] ] \ No newline at end of file diff --git a/json/tests/testData/surround/SingleValue_after.json b/json/tests/testData/surround/SingleValue_after.json index 440c9ce72ee7..876ab8e46e66 100644 --- a/json/tests/testData/surround/SingleValue_after.json +++ b/json/tests/testData/surround/SingleValue_after.json @@ -1,3 +1,5 @@ { - "property": [null] + "property": [ + null + ] } \ No newline at end of file diff --git a/platform/core-api/src/com/intellij/lang/FCTSBackedLighterAST.java b/platform/core-api/src/com/intellij/lang/FCTSBackedLighterAST.java index dc241b519872..33151bba0359 100644 --- a/platform/core-api/src/com/intellij/lang/FCTSBackedLighterAST.java +++ b/platform/core-api/src/com/intellij/lang/FCTSBackedLighterAST.java @@ -21,6 +21,7 @@ import com.intellij.util.containers.ContainerUtil; import com.intellij.util.diff.FlyweightCapableTreeStructure; import org.jetbrains.annotations.NotNull; +import java.util.AbstractList; import java.util.List; public class FCTSBackedLighterAST extends LighterAST { @@ -53,6 +54,38 @@ public class FCTSBackedLighterAST extends LighterAST { } LighterASTNode[] elements = into.get(); assert elements != null : myTreeStructure +" ("+parent+")"; - return ContainerUtil.newArrayList(elements, 0, numKids); + return new LighterASTNodeList(numKids, elements); + } + + public void disposeChildren(@NotNull List children) { + if (children instanceof LighterASTNodeList) { + LighterASTNodeList nodes = (LighterASTNodeList)children; + + myTreeStructure.disposeChildren(nodes.myElements, nodes.mySize); + } else { + LighterASTNode[] astNodes = new LighterASTNode[children.size()]; + myTreeStructure.disposeChildren(children.toArray(astNodes), astNodes.length); + } + } + + private static class LighterASTNodeList extends AbstractList { + private final int mySize; + private final LighterASTNode[] myElements; + + public LighterASTNodeList(int size, LighterASTNode[] elements) { + mySize = size; + myElements = elements; + } + + @Override + public LighterASTNode get(final int index) { + if (index < 0 || index >= mySize) throw new IndexOutOfBoundsException("index:" + index + " size:" + mySize); + return myElements[index]; + } + + @Override + public int size() { + return mySize; + } } } diff --git a/platform/core-api/src/com/intellij/lang/LighterAST.java b/platform/core-api/src/com/intellij/lang/LighterAST.java index 73bedde677e9..7d60edca0591 100644 --- a/platform/core-api/src/com/intellij/lang/LighterAST.java +++ b/platform/core-api/src/com/intellij/lang/LighterAST.java @@ -44,4 +44,6 @@ public abstract class LighterAST { @NotNull public abstract List getChildren(@NotNull final LighterASTNode parent); + + public abstract void disposeChildren(@NotNull List children); } \ No newline at end of file diff --git a/platform/core-api/src/com/intellij/lang/TreeBackedLighterAST.java b/platform/core-api/src/com/intellij/lang/TreeBackedLighterAST.java index 8bfbd3c5da36..277e98650305 100644 --- a/platform/core-api/src/com/intellij/lang/TreeBackedLighterAST.java +++ b/platform/core-api/src/com/intellij/lang/TreeBackedLighterAST.java @@ -45,9 +45,8 @@ public class TreeBackedLighterAST extends LighterAST { @Override public List getChildren(@NotNull final LighterASTNode parent) { final ASTNode[] children = ((NodeWrapper)parent).myNode.getChildren(null); - if (children == null || children.length == 0) { - return ContainerUtil.emptyList(); - } + if (children.length == 0) return ContainerUtil.emptyList(); + List result = new ArrayList(children.length); for (final ASTNode child : children) { result.add(wrap(child)); @@ -55,6 +54,9 @@ public class TreeBackedLighterAST extends LighterAST { return result; } + public void disposeChildren(@NotNull List children) { + } + @NotNull private static LighterASTNode wrap(@NotNull ASTNode node) { return node.getFirstChildNode() == null && node.getTextLength() > 0 ? new TokenNodeWrapper(node) : new NodeWrapper(node); diff --git a/platform/core-api/src/com/intellij/lang/folding/CustomFoldingBuilder.java b/platform/core-api/src/com/intellij/lang/folding/CustomFoldingBuilder.java index a247bedf51e2..0959b4e3f4c0 100644 --- a/platform/core-api/src/com/intellij/lang/folding/CustomFoldingBuilder.java +++ b/platform/core-api/src/com/intellij/lang/folding/CustomFoldingBuilder.java @@ -19,6 +19,8 @@ import com.intellij.lang.ASTNode; import com.intellij.openapi.editor.Document; import com.intellij.openapi.project.PossiblyDumbAware; import com.intellij.openapi.util.TextRange; +import com.intellij.openapi.util.registry.Registry; +import com.intellij.openapi.util.registry.RegistryValue; import com.intellij.psi.PsiComment; import com.intellij.psi.PsiElement; import com.intellij.util.containers.Stack; @@ -38,7 +40,7 @@ import java.util.Set; public abstract class CustomFoldingBuilder extends FoldingBuilderEx implements PossiblyDumbAware { private CustomFoldingProvider myDefaultProvider; - private static final int MAX_LOOKUP_DEPTH = 10; + private final RegistryValue myMaxLookupDepth = Registry.get("custom.folding.max.lookup.depth"); private static final ThreadLocal> ourCustomRegionElements = new ThreadLocal>(); @NotNull @@ -103,7 +105,7 @@ public abstract class CustomFoldingBuilder extends FoldingBuilderEx implements P } } else { - if (currDepth < MAX_LOOKUP_DEPTH) { + if (currDepth < myMaxLookupDepth.asInteger()) { addCustomFoldingRegionsRecursively(localFoldingStack, child, descriptors, currDepth + 1); } } diff --git a/platform/core-impl/src/com/intellij/openapi/application/impl/ApplicationInfoImpl.java b/platform/core-impl/src/com/intellij/openapi/application/impl/ApplicationInfoImpl.java index 90c243c6a947..b9a0330c90e3 100644 --- a/platform/core-impl/src/com/intellij/openapi/application/impl/ApplicationInfoImpl.java +++ b/platform/core-impl/src/com/intellij/openapi/application/impl/ApplicationInfoImpl.java @@ -210,7 +210,7 @@ public class ApplicationInfoImpl extends ApplicationInfoEx implements JDOMExtern @Override public String getApiVersion() { if (myApiVersion != null) { - return BuildNumber.fromString(myApiVersion, getProductPrefix()).asString(); + return BuildNumber.fromString(myApiVersion, getBuild().getProductCode()).asString(); } return getBuild().asString(); } diff --git a/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleDiffViewer.java b/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleDiffViewer.java index 761a7ce8467f..f81a25c9b238 100644 --- a/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleDiffViewer.java +++ b/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleDiffViewer.java @@ -411,12 +411,19 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer { return true; } - private void doScrollToChange(@NotNull SimpleDiffChange change, boolean animated) { + private void doScrollToChange(@NotNull SimpleDiffChange change, final boolean animated) { if (myEditor1 == null || myEditor2 == null) return; + assert mySyncScrollSupport != null; - EditorEx editor = getCurrentEditor(); - int line = change.getStartLine(getCurrentSide()); - DiffUtil.scrollEditor(editor, line, animated); + final int line1 = change.getStartLine(Side.LEFT); + final int line2 = change.getStartLine(Side.RIGHT); + final int endLine1 = change.getEndLine(Side.LEFT); + final int endLine2 = change.getEndLine(Side.RIGHT); + + DiffUtil.moveCaret(myEditor1, line1); + DiffUtil.moveCaret(myEditor2, line2); + + mySyncScrollSupport.makeVisible(getCurrentSide(), line1, endLine1, line2, endLine2, animated); } @Override diff --git a/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleThreesideDiffViewer.java b/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleThreesideDiffViewer.java index 64e54c98258b..1471ad397d33 100644 --- a/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleThreesideDiffViewer.java +++ b/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleThreesideDiffViewer.java @@ -346,6 +346,7 @@ public class SimpleThreesideDiffViewer extends ThreesideTextDiffViewer { } private void doScrollToChange(@NotNull SimpleThreesideDiffChange change, boolean animated) { + // TODO: use anchors to fix scrolling issue at the start/end of file EditorEx editor = getCurrentEditor(); int line = change.getStartLine(getCurrentSide()); DiffUtil.scrollEditor(editor, line, animated); diff --git a/platform/diff-impl/src/com/intellij/diff/tools/util/SyncScrollSupport.java b/platform/diff-impl/src/com/intellij/diff/tools/util/SyncScrollSupport.java index 68788e3b9030..3ae8591b103e 100644 --- a/platform/diff-impl/src/com/intellij/diff/tools/util/SyncScrollSupport.java +++ b/platform/diff-impl/src/com/intellij/diff/tools/util/SyncScrollSupport.java @@ -15,15 +15,18 @@ */ package com.intellij.diff.tools.util; +import com.intellij.diff.util.IntPair; import com.intellij.diff.util.Side; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.LogicalPosition; import com.intellij.openapi.editor.ScrollingModel; import com.intellij.openapi.editor.event.VisibleAreaEvent; import com.intellij.openapi.editor.event.VisibleAreaListener; +import com.intellij.openapi.editor.ex.EditorEx; import gnu.trove.TIntFunction; import org.jetbrains.annotations.CalledInAwt; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.awt.*; @@ -82,6 +85,51 @@ public class SyncScrollSupport { public boolean isDuringSyncScroll() { return myDuringSyncScroll; } + + public void makeVisible(@NotNull Side masterSide, + int startLine1, int endLine1, int startLine2, int endLine2, + final boolean animate) { + Side slaveSide = masterSide.other(); + + final IntPair offsets = getTargetOffsets(myEditor1, myEditor2, startLine1, endLine1, startLine2, endLine2); + + final Editor masterEditor = masterSide.select(myEditor1, myEditor2); + final Editor slaveEditor = slaveSide.select(myEditor1, myEditor2); + + final int masterOffset = masterSide.select(offsets.val1, offsets.val2); + final int slaveOffset = slaveSide.select(offsets.val1, offsets.val2); + + int startOffset1 = myEditor1.getScrollingModel().getVisibleArea().y; + int startOffset2 = myEditor2.getScrollingModel().getVisibleArea().y; + final int masterStartOffset = masterSide.select(startOffset1, startOffset2); + + myHelper1.setAnchor(startOffset1, offsets.val1, startOffset2, offsets.val2); + myHelper2.setAnchor(startOffset2, offsets.val2, startOffset1, offsets.val1); + + doScrollHorizontally(masterEditor, 0, false); // animation will be canceled by "scroll vertically" anyway + doScrollVertically(masterEditor, masterOffset, animate); + + masterEditor.getScrollingModel().runActionOnScrollingFinished(new Runnable() { + @Override + public void run() { + myHelper1.removeAnchor(); + myHelper2.removeAnchor(); + + if (masterOffset == masterStartOffset) { // master editor didn't scrolled + myDuringSyncScroll = true; + + doScrollVertically(slaveEditor, slaveOffset, animate); + + slaveEditor.getScrollingModel().runActionOnScrollingFinished(new Runnable() { + @Override + public void run() { + myDuringSyncScroll = false; + } + }); + } + } + }); + } } public static class ThreesideSyncScrollSupport { @@ -157,78 +205,150 @@ public class SyncScrollSupport { private static class MyScrollHelper implements VisibleAreaListener { @NotNull private final Editor myMaster; @NotNull private final Editor mySlave; - @NotNull private final TIntFunction myConvertor; + @Nullable private Anchor myAnchor; + public MyScrollHelper(@NotNull Editor master, @NotNull Editor slave, @NotNull TIntFunction convertor) { myMaster = master; mySlave = slave; myConvertor = convertor; } + public void setAnchor(int masterStartOffset, int masterEndOffset, int slaveStartOffset, int slaveEndOffset) { + myAnchor = new Anchor(masterStartOffset, masterEndOffset, slaveStartOffset, slaveEndOffset); + } + + public void removeAnchor() { + myAnchor = null; + } + @Override public void visibleAreaChanged(VisibleAreaEvent e) { Rectangle newRectangle = e.getNewRectangle(); Rectangle oldRectangle = e.getOldRectangle(); if (oldRectangle == null) return; - syncVerticalScroll(newRectangle, oldRectangle); - syncHorizontalScroll(newRectangle, oldRectangle); + if (newRectangle.x != oldRectangle.x) syncHorizontalScroll(false); + if (newRectangle.y != oldRectangle.y) syncVerticalScroll(false); } - private void syncVerticalScroll(@NotNull Rectangle newRectangle, @NotNull Rectangle oldRectangle) { - if (newRectangle.y == oldRectangle.y) return; - + private void syncVerticalScroll(boolean animated) { if (myMaster.getDocument().getTextLength() == 0) return; - int masterVerticalScrollOffset = myMaster.getScrollingModel().getVerticalScrollOffset(); - Rectangle viewRect = myMaster.getScrollingModel().getVisibleArea(); int middleY = viewRect.height / 3; - LogicalPosition masterPos = myMaster.xyToLogicalPosition(new Point(viewRect.x, masterVerticalScrollOffset + middleY)); - int masterCenterLine = masterPos.line; - int scrollToLine = myConvertor.execute(masterCenterLine); + int offset; + if (myAnchor == null) { + LogicalPosition masterPos = myMaster.xyToLogicalPosition(new Point(viewRect.x, viewRect.y + middleY)); + int masterCenterLine = masterPos.line; + int convertedCenterLine = myConvertor.execute(masterCenterLine); - int correction = (masterVerticalScrollOffset + middleY) % myMaster.getLineHeight(); - Point point = mySlave.logicalPositionToXY(new LogicalPosition(scrollToLine, masterPos.column)); - int offset = point.y - middleY + correction; + Point point = mySlave.logicalPositionToXY(new LogicalPosition(convertedCenterLine, masterPos.column)); + int correction = (viewRect.y + middleY) % myMaster.getLineHeight(); + offset = point.y - middleY + correction; + } + else { + double progress = myAnchor.masterStartOffset == myAnchor.masterEndOffset || viewRect.y == myAnchor.masterEndOffset ? 1 : + ((double)(viewRect.y - myAnchor.masterStartOffset)) / (myAnchor.masterEndOffset - myAnchor.masterStartOffset); + + offset = myAnchor.slaveStartOffset + (int)((myAnchor.slaveEndOffset - myAnchor.slaveStartOffset) * progress); + } int deltaHeaderOffset = getHeaderOffset(mySlave) - getHeaderOffset(myMaster); - doScrollVertically(mySlave.getScrollingModel(), offset + deltaHeaderOffset); + doScrollVertically(mySlave, offset + deltaHeaderOffset, animated); } - private void syncHorizontalScroll(@NotNull Rectangle newRectangle, @NotNull Rectangle oldRectangle) { - if (newRectangle.x == oldRectangle.x) return; - - int offset = newRectangle.x; - - doScrollHorizontally(mySlave.getScrollingModel(), offset); + private void syncHorizontalScroll(boolean animated) { + int offset = myMaster.getScrollingModel().getVisibleArea().x; + doScrollHorizontally(mySlave, offset, animated); } } - private static void doScrollVertically(@NotNull ScrollingModel model, int offset) { - model.disableAnimation(); - try { - model.scrollVertically(offset); - } - finally { - model.enableAnimation(); - } + private static void doScrollVertically(@NotNull Editor editor, int offset, boolean animated) { + ScrollingModel model = editor.getScrollingModel(); + if (!animated) model.disableAnimation(); + model.scrollVertically(offset); + if (!animated) model.enableAnimation(); } - private static void doScrollHorizontally(@NotNull ScrollingModel model, int offset) { - model.disableAnimation(); - try { - model.scrollHorizontally(offset); - } - finally { - model.enableAnimation(); - } + private static void doScrollHorizontally(@NotNull Editor editor, int offset, boolean animated) { + ScrollingModel model = editor.getScrollingModel(); + if (!animated) model.disableAnimation(); + model.scrollHorizontally(offset); + if (!animated) model.enableAnimation(); } private static int getHeaderOffset(@NotNull final Editor editor) { final JComponent header = editor.getHeaderComponent(); return header == null ? 0 : header.getHeight(); } + + @NotNull + private static IntPair getTargetOffsets(@NotNull Editor editor1, @NotNull Editor editor2, + int startLine1, int endLine1, int startLine2, int endLine2) { + int topOffset1 = editor1.logicalPositionToXY(new LogicalPosition(startLine1, 0)).y; + int bottomOffset1 = editor1.logicalPositionToXY(new LogicalPosition(endLine1 + 1, 0)).y; + int topOffset2 = editor2.logicalPositionToXY(new LogicalPosition(startLine2, 0)).y; + int bottomOffset2 = editor2.logicalPositionToXY(new LogicalPosition(endLine2 + 1, 0)).y; + + int rangeHeight1 = bottomOffset1 - topOffset1; + int rangeHeight2 = bottomOffset2 - topOffset2; + + int gapLines1 = 2 * editor1.getLineHeight(); + int gapLines2 = 2 * editor2.getLineHeight(); + + int editorHeight1 = editor1.getScrollingModel().getVisibleArea().height; + int editorHeight2 = editor2.getScrollingModel().getVisibleArea().height; + + int maximumOffset1 = ((EditorEx)editor1).getScrollPane().getVerticalScrollBar().getMaximum() - editorHeight1; + int maximumOffset2 = ((EditorEx)editor1).getScrollPane().getVerticalScrollBar().getMaximum() - editorHeight2; + + // 'shift' here - distance between editor's top and first line of range + + // make whole range visible. If possible, locate it at 'center' (1/3 of height) + // If can't show whole range - show as much as we can + boolean canShow1 = 2 * gapLines1 + rangeHeight1 <= editorHeight1; + boolean canShow2 = 2 * gapLines2 + rangeHeight2 <= editorHeight2; + + int topShift1 = canShow1 ? Math.min(editorHeight1 - gapLines1 - rangeHeight1, editorHeight1 / 3) : gapLines1; + int topShift2 = canShow2 ? Math.min(editorHeight2 - gapLines2 - rangeHeight2, editorHeight2 / 3) : gapLines2; + + int topShift = Math.min(topShift1, topShift2); + + // check if we're at the top of file + topShift = Math.min(topShift, Math.min(topOffset1, topOffset2)); + + int offset1 = topOffset1 - topShift; + int offset2 = topOffset2 - topShift; + if (maximumOffset1 > offset1 && maximumOffset2 > offset2) return new IntPair(offset1, offset2); + + // One of the ranges is at end of file - we can't scroll where we want to. + topShift = Math.min(topOffset1 - maximumOffset1, topOffset2 - maximumOffset2); + + // Try to show as much of range as we can (even if it breaks alignment) + offset1 = topOffset1 - topShift + Math.max(topShift + rangeHeight1 + gapLines1 - editorHeight1, 0); + offset2 = topOffset2 - topShift + Math.max(topShift + rangeHeight2 + gapLines2 - editorHeight2, 0); + + // always show top of the range + offset1 = Math.min(offset1, topOffset1 - gapLines1); + offset2 = Math.min(offset2, topOffset2 - gapLines2); + + return new IntPair(offset1, offset2); + } + + private static class Anchor { + public final int masterStartOffset; + public final int masterEndOffset; + public final int slaveStartOffset; + public final int slaveEndOffset; + + public Anchor(int masterStartOffset, int masterEndOffset, int slaveStartOffset, int slaveEndOffset) { + this.masterStartOffset = masterStartOffset; + this.masterEndOffset = masterEndOffset; + this.slaveStartOffset = slaveStartOffset; + this.slaveEndOffset = slaveEndOffset; + } + } } diff --git a/platform/diff-impl/src/com/intellij/diff/tools/util/twoside/TwosideTextDiffViewer.java b/platform/diff-impl/src/com/intellij/diff/tools/util/twoside/TwosideTextDiffViewer.java index cc81f9d39742..011b4459bc02 100644 --- a/platform/diff-impl/src/com/intellij/diff/tools/util/twoside/TwosideTextDiffViewer.java +++ b/platform/diff-impl/src/com/intellij/diff/tools/util/twoside/TwosideTextDiffViewer.java @@ -82,7 +82,7 @@ public abstract class TwosideTextDiffViewer extends TextDiffViewerBase { @NotNull private final MyScrollToLineHelper myScrollToLineHelper = new MyScrollToLineHelper(); - @Nullable private TwosideSyncScrollSupport mySyncScrollListener; + @Nullable protected TwosideSyncScrollSupport mySyncScrollSupport; @NotNull private Side myCurrentSide; @@ -230,7 +230,7 @@ public abstract class TwosideTextDiffViewer extends TextDiffViewerBase { if (myEditor1 != null && myEditor2 != null) { SyncScrollSupport.SyncScrollable scrollable = getSyncScrollable(); if (scrollable != null) { - mySyncScrollListener = new TwosideSyncScrollSupport(myEditor1, myEditor2, scrollable); + mySyncScrollSupport = new TwosideSyncScrollSupport(myEditor1, myEditor2, scrollable); } } } @@ -248,15 +248,15 @@ public abstract class TwosideTextDiffViewer extends TextDiffViewerBase { myEditor2.getScrollingModel().removeVisibleAreaListener(myVisibleAreaListener); } if (myEditor1 != null && myEditor2 != null) { - if (mySyncScrollListener != null) { - mySyncScrollListener = null; + if (mySyncScrollSupport != null) { + mySyncScrollSupport = null; } } } protected void disableSyncScrollSupport(boolean disable) { - if (mySyncScrollListener != null) { - mySyncScrollListener.myDuringSyncScroll = disable; + if (mySyncScrollSupport != null) { + mySyncScrollSupport.myDuringSyncScroll = disable; } } @@ -325,8 +325,8 @@ public abstract class TwosideTextDiffViewer extends TextDiffViewerBase { @CalledInAwt @NotNull protected LogicalPosition transferPosition(@NotNull Side baseSide, @NotNull LogicalPosition position) { - if (mySyncScrollListener == null) return position; - int line = mySyncScrollListener.getScrollable().transfer(baseSide, position.line); + if (mySyncScrollSupport == null) return position; + int line = mySyncScrollSupport.getScrollable().transfer(baseSide, position.line); return new LogicalPosition(line, position.column); } @@ -479,7 +479,7 @@ public abstract class TwosideTextDiffViewer extends TextDiffViewerBase { private class MyVisibleAreaListener implements VisibleAreaListener { @Override public void visibleAreaChanged(VisibleAreaEvent e) { - if (mySyncScrollListener != null) mySyncScrollListener.visibleAreaChanged(e); + if (mySyncScrollSupport != null) mySyncScrollSupport.visibleAreaChanged(e); if (Registry.is("diff.divider.repainting.fix")) { myContentPanel.repaint(); } diff --git a/platform/diff-impl/src/com/intellij/diff/util/DiffUtil.java b/platform/diff-impl/src/com/intellij/diff/util/DiffUtil.java index b2af70e18c92..d1df97e088b3 100644 --- a/platform/diff-impl/src/com/intellij/diff/util/DiffUtil.java +++ b/platform/diff-impl/src/com/intellij/diff/util/DiffUtil.java @@ -191,6 +191,12 @@ public class DiffUtil { // Scrolling // + public static void moveCaret(@Nullable final Editor editor, int line) { + if (editor == null) return; + editor.getCaretModel().removeSecondaryCarets(); + editor.getCaretModel().moveToLogicalPosition(new LogicalPosition(line, 0)); + } + public static void scrollEditor(@Nullable final Editor editor, int line, boolean animated) { scrollEditor(editor, line, 0, animated); } @@ -203,11 +209,15 @@ public class DiffUtil { } public static void scrollToPoint(@Nullable Editor editor, @NotNull Point point) { + scrollToPoint(editor, point, false); + } + + public static void scrollToPoint(@Nullable Editor editor, @NotNull Point point, boolean animated) { if (editor == null) return; - editor.getScrollingModel().disableAnimation(); + if (!animated) editor.getScrollingModel().disableAnimation(); editor.getScrollingModel().scrollHorizontally(point.x); editor.getScrollingModel().scrollVertically(point.y); - editor.getScrollingModel().enableAnimation(); + if (!animated) editor.getScrollingModel().enableAnimation(); } public static void scrollToCaret(@Nullable Editor editor, boolean animated) { diff --git a/platform/lang-impl/src/com/intellij/execution/runners/FakeRerunAction.java b/platform/lang-impl/src/com/intellij/execution/runners/FakeRerunAction.java index 34f96b0b51c6..be7a2da4ef41 100644 --- a/platform/lang-impl/src/com/intellij/execution/runners/FakeRerunAction.java +++ b/platform/lang-impl/src/com/intellij/execution/runners/FakeRerunAction.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -37,11 +37,11 @@ class FakeRerunAction extends AnAction implements DumbAware { if (environment != null) { presentation.setText(ExecutionBundle.message("rerun.configuration.action.name", environment.getRunProfile().getName())); presentation.setIcon(ExecutionManagerImpl.isProcessRunning(getDescriptor(event)) ? AllIcons.Actions.Restart : environment.getExecutor().getIcon()); - presentation.setEnabledAndVisible(isEnabled(event)); + presentation.setEnabled(isEnabled(event)); return; } - presentation.setEnabledAndVisible(false); + presentation.setEnabled(false); } @Override diff --git a/platform/lang-impl/src/com/intellij/ide/projectView/impl/ProjectViewPane.java b/platform/lang-impl/src/com/intellij/ide/projectView/impl/ProjectViewPane.java index 788a6d48366f..9f15bffa9a88 100644 --- a/platform/lang-impl/src/com/intellij/ide/projectView/impl/ProjectViewPane.java +++ b/platform/lang-impl/src/com/intellij/ide/projectView/impl/ProjectViewPane.java @@ -35,6 +35,7 @@ import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.DefaultActionGroup; import com.intellij.openapi.actionSystem.Presentation; import com.intellij.openapi.actionSystem.ToggleAction; +import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.InvalidDataException; import com.intellij.openapi.util.JDOMExternalizerUtil; @@ -191,7 +192,7 @@ public class ProjectViewPane extends AbstractProjectViewPSIPane { } } - private final class ShowExcludedFilesAction extends ToggleAction { + private final class ShowExcludedFilesAction extends ToggleAction implements DumbAware { private ShowExcludedFilesAction() { super(IdeBundle.message("action.show.excluded.files"), IdeBundle.message("action.show.hide.excluded.files"), null); } diff --git a/platform/lang-impl/src/com/intellij/util/indexing/FileBasedIndexImpl.java b/platform/lang-impl/src/com/intellij/util/indexing/FileBasedIndexImpl.java index c04131596627..7f58f14c89ff 100644 --- a/platform/lang-impl/src/com/intellij/util/indexing/FileBasedIndexImpl.java +++ b/platform/lang-impl/src/com/intellij/util/indexing/FileBasedIndexImpl.java @@ -2456,14 +2456,14 @@ public class FileBasedIndexImpl extends FileBasedIndex { private boolean isTooLarge(@NotNull VirtualFile file) { if (SingleRootFileViewProvider.isTooLargeForIntelligence(file)) { - return !myNoLimitCheckTypes.contains(file.getFileType()) && !SingleRootFileViewProvider.isTooLargeForContentLoading(file); + return !myNoLimitCheckTypes.contains(file.getFileType()) || SingleRootFileViewProvider.isTooLargeForContentLoading(file); } return false; } private boolean isTooLarge(@NotNull VirtualFile file, long contentSize) { if (SingleRootFileViewProvider.isTooLargeForIntelligence(file, contentSize)) { - return !myNoLimitCheckTypes.contains(file.getFileType()) && !SingleRootFileViewProvider.isTooLargeForContentLoading(file, contentSize); + return !myNoLimitCheckTypes.contains(file.getFileType()) || SingleRootFileViewProvider.isTooLargeForContentLoading(file, contentSize); } return false; } diff --git a/platform/platform-api/src/com/intellij/ui/ListUtil.java b/platform/platform-api/src/com/intellij/ui/ListUtil.java index 7f36ffd55bb8..23e99899048f 100644 --- a/platform/platform-api/src/com/intellij/ui/ListUtil.java +++ b/platform/platform-api/src/com/intellij/ui/ListUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -37,7 +37,8 @@ public class ListUtil { boolean myIsEngaged = false; public void mouseMoved(MouseEvent e) { - if (myIsEngaged && !UIUtil.isSelectionButtonDown(e)) { + Component focusOwner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner(); + if (myIsEngaged && !UIUtil.isSelectionButtonDown(e) && !(focusOwner instanceof JRootPane)) { Point point = e.getPoint(); int index = list.locationToIndex(point); list.putClientProperty(SELECTED_BY_MOUSE_EVENT, Boolean.TRUE); diff --git a/platform/platform-api/src/com/intellij/ui/table/JBTable.java b/platform/platform-api/src/com/intellij/ui/table/JBTable.java index 2d86e10c244e..a375375f1dae 100644 --- a/platform/platform-api/src/com/intellij/ui/table/JBTable.java +++ b/platform/platform-api/src/com/intellij/ui/table/JBTable.java @@ -45,6 +45,7 @@ import java.util.EventObject; public class JBTable extends JTable implements ComponentWithEmptyText, ComponentWithExpandableItems { public static final int PREFERRED_SCROLLABLE_VIEWPORT_HEIGHT_IN_ROWS = 7; public static final int COLUMN_RESIZE_AREA_WIDTH = 3; // same as in BasicTableHeaderUI + private static final int DEFAULT_MIN_COLUMN_WIDTH = 15; // see TableColumn constructor javadoc private final StatusText myEmptyText; private final ExpandableItemsHandler myExpandableItemsHandler; @@ -794,27 +795,6 @@ public class JBTable extends JTable implements ComponentWithEmptyText, Component }); } - protected int getExpandedColumnWidth(int columnToExpand) { - int expandedWidth = getPreferredHeaderWidth(columnToExpand); - for (int row = 0; row < getRowCount(); row++) { - TableCellRenderer cellRenderer = getCellRenderer(row, columnToExpand); - if (cellRenderer != null) { - Component c = JBTable.this.prepareRenderer(cellRenderer, row, columnToExpand); - expandedWidth = Math.max(expandedWidth, c.getPreferredSize().width); - } - } - return expandedWidth; - } - - private int getPreferredHeaderWidth(int columnIdx) { - TableColumn column = getColumnModel().getColumn(columnIdx); - TableCellRenderer renderer = column.getHeaderRenderer(); - renderer = renderer == null ? getDefaultRenderer() : renderer; - Object headerValue = column.getHeaderValue(); - Component headerCellRenderer = renderer.getTableCellRendererComponent(JBTable.this, headerValue, false, false, -1, columnIdx); - return headerCellRenderer.getPreferredSize().width; - } - private int getColumnToPack(Point p) { int viewColumnIdx = JBTable.this.columnAtPoint(p); if (viewColumnIdx == -1) return -1; @@ -836,6 +816,33 @@ public class JBTable extends JTable implements ComponentWithEmptyText, Component } } + public int getExpandedColumnWidth(int columnToExpand) { + int expandedWidth = getPreferredHeaderWidth(columnToExpand); + for (int row = 0; row < getRowCount(); row++) { + TableCellRenderer cellRenderer = getCellRenderer(row, columnToExpand); + if (cellRenderer != null) { + Component c = prepareRenderer(cellRenderer, row, columnToExpand); + expandedWidth = Math.max(expandedWidth, c.getPreferredSize().width); + } + } + return expandedWidth; + } + + private int getPreferredHeaderWidth(int columnIdx) { + TableColumn column = getColumnModel().getColumn(columnIdx); + TableCellRenderer renderer = column.getHeaderRenderer(); + if (renderer == null) { + JTableHeader header = getTableHeader(); + if (header == null) { + return DEFAULT_MIN_COLUMN_WIDTH; + } + renderer = header.getDefaultRenderer(); + } + Object headerValue = column.getHeaderValue(); + Component headerCellRenderer = renderer.getTableCellRendererComponent(this, headerValue, false, false, -1, columnIdx); + return headerCellRenderer.getPreferredSize().width; + } + /** * JTable gets table data from model lazily - only for a table part to be shown. * JBTable loads all the data on initialization to calculate cell size. diff --git a/platform/platform-impl/src/com/intellij/ide/plugins/PluginManagerMain.form b/platform/platform-impl/src/com/intellij/ide/plugins/PluginManagerMain.form index 1f9cea154af4..bba955d00422 100644 --- a/platform/platform-impl/src/com/intellij/ide/plugins/PluginManagerMain.form +++ b/platform/platform-impl/src/com/intellij/ide/plugins/PluginManagerMain.form @@ -54,7 +54,10 @@ - + + + + @@ -91,7 +94,10 @@ - + + + + diff --git a/platform/platform-impl/src/com/intellij/idea/StartupUtil.java b/platform/platform-impl/src/com/intellij/idea/StartupUtil.java index e4805a1efff8..6e0348d8e0d3 100644 --- a/platform/platform-impl/src/com/intellij/idea/StartupUtil.java +++ b/platform/platform-impl/src/com/intellij/idea/StartupUtil.java @@ -124,7 +124,7 @@ public class StartupUtil { if (!"true".equals(System.getProperty("idea.no.jre.check"))) { try { // try to find a class from tools.jar - Class.forName("com.sun.jdi.Field"); + Class.forName("com.sun.jdi.Field", false, StartupUtil.class.getClassLoader()); } catch (ClassNotFoundException e) { String message = "'tools.jar' seems to be not in " + ApplicationNamesInfo.getInstance().getProductName() + " classpath.\n" + @@ -132,6 +132,12 @@ public class StartupUtil { Main.showMessage("JDK Required", message, true); return false; } + catch (LinkageError e) { + String message = "Cannot load a class from 'tools.jar': " + e.getMessage() + "\n" + + "Please ensure JAVA_HOME points to JDK rather than JRE."; + Main.showMessage("JDK Required", message, true); + return false; + } if (StringUtil.containsIgnoreCase(System.getProperty("java.vm.name", ""), "OpenJDK") && !SystemInfo.isJavaVersionAtLeast("1.7")) { String message = "OpenJDK 6 is not supported. Please use Oracle Java or newer OpenJDK."; diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/welcomeScreen/NewRecentProjectPanel.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/welcomeScreen/NewRecentProjectPanel.java index bc8910697014..55d4d523bb31 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/welcomeScreen/NewRecentProjectPanel.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/welcomeScreen/NewRecentProjectPanel.java @@ -27,6 +27,7 @@ import com.intellij.ui.PopupHandler; import com.intellij.ui.components.JBList; import com.intellij.ui.components.panels.NonOpaquePanel; import com.intellij.ui.speedSearch.ListWithFilter; +import com.intellij.ui.speedSearch.NameFilteringListModel; import com.intellij.util.ui.JBUI; import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.Nullable; @@ -80,17 +81,44 @@ public class NewRecentProjectPanel extends RecentProjectPanel { list.addKeyListener(new KeyAdapter() { @Override public void keyPressed(KeyEvent e) { - if (e.getKeyCode() == KeyEvent.VK_RIGHT) { - FlatWelcomeFrame frame = UIUtil.getParentOfType(FlatWelcomeFrame.class, list); - if (frame != null) { - FocusTraversalPolicy policy = frame.getFocusTraversalPolicy(); - if (policy != null) { - Component next = policy.getComponentAfter(frame, list); - if (next != null) { - next.requestFocus(); + Object selected = list.getSelectedValue(); + final ProjectGroup group; + if (selected instanceof ProjectGroupActionGroup) { + group = ((ProjectGroupActionGroup)selected).getGroup(); + } else { + group = null; + } + + int keyCode = e.getKeyCode(); + if (keyCode == KeyEvent.VK_RIGHT) { + if (group != null) { + if (!group.isExpanded()) { + group.setExpanded(true); + ListModel model = ((NameFilteringListModel)list.getModel()).getOriginalModel(); + int index = list.getSelectedIndex(); + RecentProjectsWelcomeScreenActionBase.rebuildRecentProjectDataModel((DefaultListModel)model); + list.setSelectedIndex(group.getProjects().isEmpty() ? index : index + 1); + } + } else { + FlatWelcomeFrame frame = UIUtil.getParentOfType(FlatWelcomeFrame.class, list); + if (frame != null) { + FocusTraversalPolicy policy = frame.getFocusTraversalPolicy(); + if (policy != null) { + Component next = policy.getComponentAfter(frame, list); + if (next != null) { + next.requestFocus(); + } } } } + } else if (keyCode == KeyEvent.VK_LEFT ) { + if (group != null && group.isExpanded()) { + group.setExpanded(false); + int index = list.getSelectedIndex(); + ListModel model = ((NameFilteringListModel)list.getModel()).getOriginalModel(); + RecentProjectsWelcomeScreenActionBase.rebuildRecentProjectDataModel((DefaultListModel)model); + list.setSelectedIndex(index); + } } } }); @@ -186,7 +214,7 @@ public class NewRecentProjectPanel extends RecentProjectPanel { return new JPanel() { { setLayout(new BorderLayout()); - setBackground(UIUtil.getListBackground(isSelected)); + setBackground(back); boolean isGroup = value instanceof ProjectGroupActionGroup; boolean isInsideGroup = false; diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/welcomeScreen/RecentProjectPanel.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/welcomeScreen/RecentProjectPanel.java index 0137e46f8c6a..ccdc1236df17 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/welcomeScreen/RecentProjectPanel.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/welcomeScreen/RecentProjectPanel.java @@ -235,7 +235,8 @@ public class RecentProjectPanel extends JPanel { boolean myIsEngaged = false; @Override public void mouseMoved(MouseEvent e) { - if (myIsEngaged && !UIUtil.isSelectionButtonDown(e)) { + Component focusOwner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner(); + if (myIsEngaged && !UIUtil.isSelectionButtonDown(e) && !(focusOwner instanceof JRootPane)) { Point point = e.getPoint(); int index = myList.locationToIndex(point); myList.setSelectedIndex(index); diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/welcomeScreen/RecentProjectsWelcomeScreenActionBase.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/welcomeScreen/RecentProjectsWelcomeScreenActionBase.java index ca83e853a8a6..bffa0b76d8b7 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/welcomeScreen/RecentProjectsWelcomeScreenActionBase.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/welcomeScreen/RecentProjectsWelcomeScreenActionBase.java @@ -35,7 +35,7 @@ import java.util.List; */ public abstract class RecentProjectsWelcomeScreenActionBase extends DumbAwareAction { @Nullable - public DefaultListModel getDataModel(AnActionEvent e) { + public static DefaultListModel getDataModel(AnActionEvent e) { final JList list = getList(e); if (list != null) { ListModel model = list.getModel(); @@ -50,7 +50,7 @@ public abstract class RecentProjectsWelcomeScreenActionBase extends DumbAwareAct } @NotNull - public List getSelectedElements(AnActionEvent e) { + public static List getSelectedElements(AnActionEvent e) { final JList list = getList(e); final List actions = new ArrayList(); if (list != null) { @@ -64,7 +64,7 @@ public abstract class RecentProjectsWelcomeScreenActionBase extends DumbAwareAct } @Nullable - public JList getList(AnActionEvent e) { + public static JList getList(AnActionEvent e) { final Component component = e.getData(PlatformDataKeys.CONTEXT_COMPONENT); if (component instanceof JList) { return (JList)component; @@ -72,7 +72,7 @@ public abstract class RecentProjectsWelcomeScreenActionBase extends DumbAwareAct return null; } - public boolean hasGroupSelected(AnActionEvent e) { + public static boolean hasGroupSelected(AnActionEvent e) { for (AnAction action : getSelectedElements(e)) { if (action instanceof ProjectGroupActionGroup) { return true; @@ -81,14 +81,18 @@ public abstract class RecentProjectsWelcomeScreenActionBase extends DumbAwareAct return false; } - public void rebuildRecentProjectsList(AnActionEvent e) { + public static void rebuildRecentProjectsList(AnActionEvent e) { final DefaultListModel model = getDataModel(e); if (model != null) { - model.clear(); - for (AnAction action : RecentProjectsManager.getInstance().getRecentProjectsActions(false, FlatWelcomeFrame.isUseProjectGroups())) { - //noinspection unchecked - model.addElement(action); - } + rebuildRecentProjectDataModel(model); + } + } + + public static void rebuildRecentProjectDataModel(@NotNull DefaultListModel model) { + model.clear(); + for (AnAction action : RecentProjectsManager.getInstance().getRecentProjectsActions(false, FlatWelcomeFrame.isUseProjectGroups())) { + //noinspection unchecked + model.addElement(action); } } } diff --git a/platform/platform-impl/src/com/intellij/ui/EditorTextFieldCellRenderer.java b/platform/platform-impl/src/com/intellij/ui/EditorTextFieldCellRenderer.java index 4fdd374d4bca..293ae39c8db1 100644 --- a/platform/platform-impl/src/com/intellij/ui/EditorTextFieldCellRenderer.java +++ b/platform/platform-impl/src/com/intellij/ui/EditorTextFieldCellRenderer.java @@ -21,7 +21,6 @@ import com.intellij.openapi.editor.RangeMarker; import com.intellij.openapi.editor.SelectionModel; import com.intellij.openapi.editor.colors.EditorColors; import com.intellij.openapi.editor.colors.EditorColorsManager; -import com.intellij.openapi.editor.colors.EditorColorsScheme; import com.intellij.openapi.editor.colors.impl.DelegateColorScheme; import com.intellij.openapi.editor.event.DocumentListener; import com.intellij.openapi.editor.ex.*; @@ -58,54 +57,37 @@ import java.util.List; */ public abstract class EditorTextFieldCellRenderer implements TableCellRenderer, Disposable { - private static final Key MY_PANEL_PROPERTY = Key.create("EditorTextFieldCellRenderer.MyEditorPanel"); + private static final Key MY_PANEL_PROPERTY = Key.create("EditorTextFieldCellRenderer.MyEditorPanel"); private final Project myProject; + private final FileType myFileType; private final boolean myInheritFontFromLaF; - protected EditorTextFieldCellRenderer(@Nullable Project project, @NotNull Disposable parent) { - this(project, true, parent); + protected EditorTextFieldCellRenderer(@Nullable Project project, @Nullable FileType fileType, @NotNull Disposable parent) { + this(project, fileType, true, parent); } - protected EditorTextFieldCellRenderer(@Nullable Project project, boolean inheritFontFromLaF, @NotNull Disposable parent) { + protected EditorTextFieldCellRenderer(@Nullable Project project, @Nullable FileType fileType, + boolean inheritFontFromLaF, @NotNull Disposable parent) { myProject = project; + myFileType = fileType; myInheritFontFromLaF = inheritFontFromLaF; Disposer.register(parent, this); } protected abstract String getText(JTable table, Object value, int row, int column); - @Nullable - protected TextAttributes getTextAttributes(JTable table, Object value, boolean selected, boolean focused, int row, int col) { - return null; - } - - protected Color getCellBackground(JTable table, Object value, boolean selected, boolean focused, int row, int column) { - return UIUtil.getTableBackground(selected); - } - - @Nullable - protected FileType getFileType() { - return null; - } - - @NotNull - protected EditorColorsScheme getColorScheme() { - return EditorColorsManager.getInstance().getGlobalScheme(); - } - @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean selected, boolean focused, int row, int column) { - MyPanel panel = getEditorPanel(table); - EditorEx editor = panel.myEditor; + RendererComponent panel = getEditorPanel(table); + EditorEx editor = panel.getEditor(); editor.getColorsScheme().setEditorFontSize(table.getFont().getSize()); String text = getText(table, value, row, column); - TextAttributes textAttributes = getTextAttributes(table, value, selected, focused, row, column); - panel.setText(text, textAttributes, selected); + panel.setText(text, null, selected); editor.getColorsScheme().setColor(EditorColors.SELECTION_BACKGROUND_COLOR, table.getSelectionBackground()); editor.getColorsScheme().setColor(EditorColors.SELECTION_FOREGROUND_COLOR, table.getSelectionForeground()); - editor.setBackgroundColor(getCellBackground(table, value, selected, focused, row, column)); + editor.setBackgroundColor(selected ? table.getSelectionBackground() : table.getBackground()); panel.setOpaque(!Comparing.equal(editor.getBackgroundColor(), table.getBackground())); panel.setBorder(null); // prevents double border painting when ExtendedItemRendererComponentWrapper is used @@ -114,29 +96,15 @@ public abstract class EditorTextFieldCellRenderer implements TableCellRenderer, } @NotNull - private MyPanel getEditorPanel(final JTable table) { - MyPanel panel = UIUtil.getClientProperty(table, MY_PANEL_PROPERTY); + private RendererComponent getEditorPanel(final JTable table) { + RendererComponent panel = UIUtil.getClientProperty(table, MY_PANEL_PROPERTY); if (panel != null) { DelegateColorScheme scheme = (DelegateColorScheme)panel.myEditor.getColorsScheme(); - scheme.setDelegate(getColorScheme()); + scheme.setDelegate(EditorColorsManager.getInstance().getGlobalScheme()); return panel; } - FileType fileType = ObjectUtils.notNull(getFileType(), FileTypes.PLAIN_TEXT); - EditorTextField field = new EditorTextField(new MyDocument(), myProject, fileType, false, false); - field.setSupplementary(true); - field.setFontInheritedFromLAF(myInheritFontFromLaF); - field.addNotify(); // creates editor - - EditorEx editor = (EditorEx)ObjectUtils.assertNotNull(field.getEditor()); - editor.setRendererMode(true); - - editor.setColorsScheme(editor.createBoundColorSchemeDelegate(null)); - editor.getSettings().setCaretRowShown(false); - - editor.getScrollPane().setBorder(null); - - panel = new MyPanel(editor); + panel = new RendererComponent(myProject, myFileType, myInheritFontFromLaF); Disposer.register(this, panel); Disposer.register(this, new Disposable() { @Override @@ -153,7 +121,7 @@ public abstract class EditorTextFieldCellRenderer implements TableCellRenderer, public void dispose() { } - private static class MyPanel extends CellRendererPanel implements Disposable { + public static class RendererComponent extends CellRendererPanel implements Disposable { private static final char ABBREVIATION_SUFFIX = '\u2026'; // 2026 '...' private static final char RETURN_SYMBOL = '\u23ce'; @@ -165,9 +133,13 @@ public abstract class EditorTextFieldCellRenderer implements TableCellRenderer, private TextAttributes myTextAttributes; private boolean mySelected; - public MyPanel(EditorEx editor) { - add(editor.getContentComponent()); - this.myEditor = editor; + public RendererComponent(Project project, @Nullable FileType fileType, boolean inheritFontFromLaF) { + myEditor = createEditor(project, fileType, inheritFontFromLaF); + add(myEditor.getContentComponent()); + } + + public EditorEx getEditor() { + return myEditor; } @Override @@ -200,14 +172,6 @@ public abstract class EditorTextFieldCellRenderer implements TableCellRenderer, @Override protected void paintComponent(Graphics g) { - if (getBorder() == null || !myEditor.getContentComponent().isOpaque()) return; - - Color oldColor = g.getColor(); - g.setColor(myEditor.getBackgroundColor()); - Insets insets = getInsets(); - g.fillRect(0, 0, insets.left, getHeight()); - g.fillRect(getWidth() - insets.left - insets.right, 0, getWidth(), getHeight()); - g.setColor(oldColor); } @Override @@ -325,6 +289,25 @@ public abstract class EditorTextFieldCellRenderer implements TableCellRenderer, return abbrLength; } + + @NotNull + private static EditorEx createEditor(Project project, @Nullable FileType fileType, boolean inheritFontFromLaF) { + fileType = ObjectUtils.notNull(fileType, FileTypes.PLAIN_TEXT); + EditorTextField field = new EditorTextField(new MyDocument(), project, fileType, false, false); + field.setSupplementary(true); + field.setFontInheritedFromLAF(inheritFontFromLaF); + field.addNotify(); // creates editor + + EditorEx editor = (EditorEx)ObjectUtils.assertNotNull(field.getEditor()); + editor.setRendererMode(true); + + editor.setColorsScheme(editor.createBoundColorSchemeDelegate(null)); + editor.getSettings().setCaretRowShown(false); + + editor.getScrollPane().setBorder(null); + + return editor; + } } private static class MyDocument extends UserDataHolderBase implements DocumentEx { diff --git a/platform/platform-impl/src/com/intellij/util/ui/table/EditorTextFieldJBTableRowRenderer.java b/platform/platform-impl/src/com/intellij/util/ui/table/EditorTextFieldJBTableRowRenderer.java index 5c64391669ec..c6553dcfec70 100644 --- a/platform/platform-impl/src/com/intellij/util/ui/table/EditorTextFieldJBTableRowRenderer.java +++ b/platform/platform-impl/src/com/intellij/util/ui/table/EditorTextFieldJBTableRowRenderer.java @@ -16,18 +16,17 @@ package com.intellij.util.ui.table; import com.intellij.openapi.Disposable; -import com.intellij.openapi.editor.markup.TextAttributes; +import com.intellij.openapi.fileTypes.FileType; import com.intellij.openapi.project.Project; import com.intellij.ui.EditorTextFieldCellRenderer; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; -import java.awt.*; public abstract class EditorTextFieldJBTableRowRenderer extends EditorTextFieldCellRenderer implements JBTableRowRenderer { - protected EditorTextFieldJBTableRowRenderer(@Nullable Project project, @NotNull Disposable parent) { - super(project, parent); + protected EditorTextFieldJBTableRowRenderer(@Nullable Project project, @Nullable FileType fileType, @NotNull Disposable parent) { + super(project, fileType, parent); } @Override @@ -40,25 +39,5 @@ public abstract class EditorTextFieldJBTableRowRenderer extends EditorTextFieldC return getText(table, row); } - @Override - protected final Color getCellBackground(JTable table, Object value, boolean selected, boolean focused, int row, int column) { - return getRowBackground(table, selected, focused, row); - } - - @Nullable - @Override - protected final TextAttributes getTextAttributes(JTable table, Object value, boolean selected, boolean focused, int row, int col) { - return getTextAttributes(table, row); - } - protected abstract String getText(JTable table, int row); - - @Nullable - protected TextAttributes getTextAttributes(JTable table, int row) { - return null; - } - - protected Color getRowBackground(JTable table, boolean selected, boolean focused, int row) { - return super.getCellBackground(table, null, selected, focused, row, 0); - } } diff --git a/platform/platform-resources/src/idea/PlatformActions.xml b/platform/platform-resources/src/idea/PlatformActions.xml index 339d402bca62..a9b758c281b0 100644 --- a/platform/platform-resources/src/idea/PlatformActions.xml +++ b/platform/platform-resources/src/idea/PlatformActions.xml @@ -555,7 +555,7 @@ - + diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/keymap/impl/ui/ActionsTreeTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/keymap/impl/ui/ActionsTreeTest.java index 465b570afe81..6e9b494183c1 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/keymap/impl/ui/ActionsTreeTest.java +++ b/platform/platform-tests/testSrc/com/intellij/openapi/keymap/impl/ui/ActionsTreeTest.java @@ -28,6 +28,7 @@ import com.intellij.openapi.keymap.impl.ShortcutRestrictions; import com.intellij.openapi.util.text.StringUtil; import com.intellij.testFramework.LightPlatformCodeInsightTestCase; import com.intellij.testFramework.PlatformTestCase; +import com.intellij.util.SmartList; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.picocontainer.MutablePicoContainer; @@ -208,6 +209,8 @@ public class ActionsTreeTest extends LightPlatformCodeInsightTestCase { public void testPresentation() { ActionManager manager = ActionManager.getInstance(); + + List failures = new SmartList(); for (String id : manager.getActionIds("")) { if (!ACTION_WITHOUT_TEXT_AND_DESCRIPTION.equals(id)) { try { @@ -224,8 +227,8 @@ public class ActionsTreeTest extends LightPlatformCodeInsightTestCase { if (action instanceof ActionGroup) { System.out.println("ignored action group: " + message); } - else { - assertFalse("no text: " + message, StringUtil.isEmpty(action.getTemplatePresentation().getText())); + else if (StringUtil.isEmpty(action.getTemplatePresentation().getText())) { + failures.add("no text: " + message); } } catch (PluginException exception) { @@ -233,6 +236,8 @@ public class ActionsTreeTest extends LightPlatformCodeInsightTestCase { } } } + + assertEmpty(failures); } private static void checkPresentationProperty(String name, String message, Object expected, Object actual) { diff --git a/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/MemberFilter.java b/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/MemberFilter.java index 7d39fb8ca451..b25d555d465c 100644 --- a/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/MemberFilter.java +++ b/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/MemberFilter.java @@ -1,3 +1,18 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package org.jetbrains.debugger; import org.jetbrains.annotations.NotNull; @@ -6,7 +21,7 @@ import org.jetbrains.annotations.Nullable; import java.util.Collection; public interface MemberFilter { - boolean isMemberVisible(@NotNull Variable variable, boolean filterFunctions); + boolean isMemberVisible(@NotNull Variable variable); @NotNull Collection getAdditionalVariables(); diff --git a/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/MemberFilterBase.java b/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/MemberFilterBase.java index 414b169b4679..27ca585710ab 100644 --- a/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/MemberFilterBase.java +++ b/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/MemberFilterBase.java @@ -1,3 +1,18 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package org.jetbrains.debugger; import org.jetbrains.annotations.NotNull; @@ -8,7 +23,7 @@ import java.util.Collections; public abstract class MemberFilterBase implements MemberFilter { @Override - public boolean isMemberVisible(@NotNull Variable variable, boolean filterFunctions) { + public boolean isMemberVisible(@NotNull Variable variable) { return variable.isReadable(); } diff --git a/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/VariableView.java b/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/VariableView.java index 8e3bf4b8c93c..e6462e61cce5 100644 --- a/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/VariableView.java +++ b/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/VariableView.java @@ -431,7 +431,7 @@ public final class VariableView extends XNamedValue implements VariableContext { } private void computeArrayRanges(@NotNull List properties, @NotNull XCompositeNode node) { - final List variables = Variables.filterAndSort(properties, memberFilter, false); + final List variables = Variables.filterAndSort(properties, memberFilter); int count = variables.size(); int bucketSize = XCompositeNode.MAX_CHILDREN_TO_SHOW; if (count <= bucketSize) { diff --git a/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/Variables.java b/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/Variables.java index 9d3fbc5724ac..ad38b972d3b6 100644 --- a/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/Variables.java +++ b/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/Variables.java @@ -1,3 +1,18 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package org.jetbrains.debugger; import com.intellij.openapi.util.text.StringUtil; @@ -65,7 +80,7 @@ public final class Variables { List properties = new ArrayList(variables.size() + additionalVariables.size()); List functions = new SmartList(); for (Variable variable : variables) { - if (memberFilter.isMemberVisible(variable, false)) { + if (memberFilter.isMemberVisible(variable)) { Value value = variable.getValue(); if (value != null && value.getType() == ValueType.FUNCTION && @@ -112,7 +127,7 @@ public final class Variables { @NotNull MemberFilter memberFilter, int maxChildrenToAdd, boolean defaultIsLast) { - List list = filterAndSort(variables, memberFilter, true); + List list = filterAndSort(variables, memberFilter); if (list.isEmpty()) { if (defaultIsLast) { node.addChildren(XValueChildrenList.EMPTY, true); @@ -133,7 +148,7 @@ public final class Variables { } @NotNull - public static List filterAndSort(@NotNull List variables, @NotNull MemberFilter memberFilter, boolean filterFunctions) { + public static List filterAndSort(@NotNull List variables, @NotNull MemberFilter memberFilter) { if (variables.isEmpty()) { return Collections.emptyList(); } @@ -141,7 +156,7 @@ public final class Variables { Collection additionalVariables = memberFilter.getAdditionalVariables(); List result = new ArrayList(variables.size() + additionalVariables.size()); for (Variable variable : variables) { - if (memberFilter.isMemberVisible(variable, filterFunctions)) { + if (memberFilter.isMemberVisible(variable)) { result.add(variable); } } diff --git a/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/GeneralToSMTRunnerEventsConvertor.java b/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/GeneralToSMTRunnerEventsConvertor.java index e3e1fc3543b6..d6232de605ee 100644 --- a/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/GeneralToSMTRunnerEventsConvertor.java +++ b/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/GeneralToSMTRunnerEventsConvertor.java @@ -52,6 +52,7 @@ public class GeneralToSMTRunnerEventsConvertor extends GeneralTestEventsProcesso private final String myTestFrameworkName; private boolean myIsTestingFinished; private TestLocationProvider myLocator = null; + private boolean myTreeBuildBeforeStart = false; public GeneralToSMTRunnerEventsConvertor(@NotNull final SMTestProxy.SMRootTestProxy testsRootNode, @NotNull final String testFrameworkName) { @@ -117,6 +118,7 @@ public class GeneralToSMTRunnerEventsConvertor extends GeneralTestEventsProcesso @Override public void onSuiteTreeNodeAdded(final String testName, final String locationHint) { + myTreeBuildBeforeStart = true; addToInvokeLater(new Runnable() { @Override public void run() { @@ -132,6 +134,7 @@ public class GeneralToSMTRunnerEventsConvertor extends GeneralTestEventsProcesso @Override public void onSuiteTreeStarted(final String suiteName, final String locationHint) { + myTreeBuildBeforeStart = true; addToInvokeLater(new Runnable() { @Override public void run() { @@ -229,10 +232,12 @@ public class GeneralToSMTRunnerEventsConvertor extends GeneralTestEventsProcesso }); } - private static SMTestProxy findChildByName(SMTestProxy parentSuite, String fullName) { - for (SMTestProxy proxy : parentSuite.getChildren()) { - if (fullName.equals(proxy.getName())) { - return proxy; + private SMTestProxy findChildByName(SMTestProxy parentSuite, String fullName) { + if (myTreeBuildBeforeStart) { + for (SMTestProxy proxy : parentSuite.getChildren()) { + if (fullName.equals(proxy.getName())) { + return proxy; + } } } return null; diff --git a/platform/testRunner/src/com/intellij/execution/testframework/TestConsoleProperties.java b/platform/testRunner/src/com/intellij/execution/testframework/TestConsoleProperties.java index fbfd55d0cc85..568c52898594 100644 --- a/platform/testRunner/src/com/intellij/execution/testframework/TestConsoleProperties.java +++ b/platform/testRunner/src/com/intellij/execution/testframework/TestConsoleProperties.java @@ -37,9 +37,11 @@ import com.intellij.util.config.ToggleBooleanProperty; import com.intellij.util.containers.ContainerUtil; import com.intellij.xdebugger.XDebugSession; import com.intellij.xdebugger.XDebuggerManager; +import org.intellij.lang.annotations.JdkConstants; import org.jetbrains.annotations.NotNull; import javax.swing.*; +import javax.swing.tree.TreeSelectionModel; import java.util.List; import java.util.Map; @@ -195,4 +197,9 @@ public abstract class TestConsoleProperties extends StoringPropertyContainer imp Icon icon = AllIcons.RunConfigurations.IncludeNonStartedTests_Rerun; return new ToggleBooleanProperty(text, null, icon, this, INCLUDE_NON_STARTED_IN_RERUN_FAILED); } + + @JdkConstants.TreeSelectionMode + protected int getSelectionMode() { + return TreeSelectionModel.SINGLE_TREE_SELECTION; + } } diff --git a/platform/testRunner/src/com/intellij/execution/testframework/TestTreeView.java b/platform/testRunner/src/com/intellij/execution/testframework/TestTreeView.java index 5d1d2b346a85..ace77525a893 100644 --- a/platform/testRunner/src/com/intellij/execution/testframework/TestTreeView.java +++ b/platform/testRunner/src/com/intellij/execution/testframework/TestTreeView.java @@ -21,7 +21,6 @@ package com.intellij.execution.testframework; import com.intellij.execution.Location; -import com.intellij.execution.testframework.actions.ViewAssertEqualsDiffAction; import com.intellij.ide.CopyProvider; import com.intellij.ide.actions.CopyReferenceAction; import com.intellij.openapi.Disposable; @@ -35,12 +34,14 @@ import com.intellij.ui.treeStructure.Tree; import com.intellij.util.EditSourceOnDoubleClickHandler; import com.intellij.util.containers.Convertor; import com.intellij.util.ui.tree.TreeUtil; -import org.intellij.lang.annotations.JdkConstants; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.plaf.TreeUI; -import javax.swing.tree.*; +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.DefaultTreeModel; +import javax.swing.tree.TreeCellRenderer; +import javax.swing.tree.TreePath; import java.awt.datatransfer.StringSelection; import java.util.ArrayList; import java.util.List; @@ -68,7 +69,7 @@ public abstract class TestTreeView extends Tree implements DataProvider, CopyPro public void attachToModel(final TestFrameworkRunningModel model) { setModel(new DefaultTreeModel(new DefaultMutableTreeNode(model.getRoot()))); - getSelectionModel().setSelectionMode(getSelectionMode()); + getSelectionModel().setSelectionMode(model.getProperties().getSelectionMode()); myModel = model; Disposer.register(myModel, myModel.getRoot()); Disposer.register(myModel, new Disposable() { @@ -169,9 +170,4 @@ public abstract class TestTreeView extends Tree implements DataProvider, CopyPro TreeUtil.installActions(this); PopupHandler.installPopupHandler(this, IdeActions.GROUP_TESTTREE_POPUP, ActionPlaces.TESTTREE_VIEW_POPUP); } - - @JdkConstants.TreeSelectionMode - protected int getSelectionMode() { - return TreeSelectionModel.SINGLE_TREE_SELECTION; - } } \ No newline at end of file diff --git a/platform/util/resources/misc/registry.properties b/platform/util/resources/misc/registry.properties index c2dd64c84412..bc8338036e1f 100644 --- a/platform/util/resources/misc/registry.properties +++ b/platform/util/resources/misc/registry.properties @@ -575,3 +575,6 @@ search.everywhere.pattern.checking.description=Avoid searching classes and symbo idea.concurrent.scanning.files.to.index=true welcome.screen.project.grouping.enabled=true welcome.screen.project.grouping.enabled.description=Allows to group recent projects + +custom.folding.max.lookup.depth=50 +custom.folding.max.lookup.depth.description=Max lookup depth for custom folding comments in a PSI tree. \ No newline at end of file diff --git a/platform/util/src/com/intellij/openapi/util/LowMemoryWatcher.java b/platform/util/src/com/intellij/openapi/util/LowMemoryWatcher.java index 0bf9ba824f53..18d35615f54f 100644 --- a/platform/util/src/com/intellij/openapi/util/LowMemoryWatcher.java +++ b/platform/util/src/com/intellij/openapi/util/LowMemoryWatcher.java @@ -21,6 +21,7 @@ import com.intellij.util.ConcurrencyUtil; import com.intellij.util.containers.WeakList; import org.jetbrains.annotations.NotNull; +import javax.management.ListenerNotFoundException; import javax.management.Notification; import javax.management.NotificationEmitter; import javax.management.NotificationListener; @@ -66,6 +67,21 @@ public class LowMemoryWatcher { } } }; + private static final NotificationListener ourLowMemoryListener = new NotificationListener() { + @Override + public void handleNotification(Notification n, Object hb) { + if (MemoryNotificationInfo.MEMORY_THRESHOLD_EXCEEDED.equals(n.getType()) || + MemoryNotificationInfo.MEMORY_COLLECTION_THRESHOLD_EXCEEDED.equals(n.getType())) { + synchronized (ourJanitor) { + if (!ourSubmitted) { + //noinspection AssignmentToStaticFieldFromInstanceMethod + ourSubmitted = true; + ourExecutor.submit(ourJanitor); + } + } + } + } + }; private final Runnable myRunnable; @@ -79,20 +95,7 @@ public class LowMemoryWatcher { } } } - ((NotificationEmitter)ManagementFactory.getMemoryMXBean()).addNotificationListener(new NotificationListener() { - @Override - public void handleNotification(Notification n, Object hb) { - if (MemoryNotificationInfo.MEMORY_THRESHOLD_EXCEEDED.equals(n.getType()) || MemoryNotificationInfo.MEMORY_COLLECTION_THRESHOLD_EXCEEDED.equals(n.getType())) { - synchronized (ourJanitor) { - if (!ourSubmitted) { - //noinspection AssignmentToStaticFieldFromInstanceMethod - ourSubmitted = true; - ourExecutor.submit(ourJanitor); - } - } - } - } - }, null, null); + ((NotificationEmitter)ManagementFactory.getMemoryMXBean()).addNotificationListener(ourLowMemoryListener, null, null); } /** @@ -135,6 +138,12 @@ public class LowMemoryWatcher { public static void stopAll() { ourExecutor.shutdown(); ourInstances.clear(); + try { + ((NotificationEmitter)ManagementFactory.getMemoryMXBean()).removeNotificationListener(ourLowMemoryListener); + } + catch (ListenerNotFoundException e) { + LOG.error(e); + } } } diff --git a/platform/util/src/com/intellij/openapi/util/text/StringUtil.java b/platform/util/src/com/intellij/openapi/util/text/StringUtil.java index efff439b12c2..ebfc3caf7161 100644 --- a/platform/util/src/com/intellij/openapi/util/text/StringUtil.java +++ b/platform/util/src/com/intellij/openapi/util/text/StringUtil.java @@ -1503,14 +1503,13 @@ public class StringUtil extends StringUtilRt { } /** - * Strips quotes around the value. - * Quotes are removed even if leading and trailing quotes are different or if there is only one quote (leading or trailing). - * @deprecated use {@link com.intellij.openapi.util.text.StringUtil#unquoteString(String)} instead - * To be removed in IDEA 17 + * Consider using {@link StringUtil#unquoteString(String)} instead. + * Note: this method has an odd behavior: + * Quotes are removed even if leading and trailing quotes are different or + * if there is only one quote (leading or trailing). */ @NotNull @Contract(pure = true) - @Deprecated public static String stripQuotesAroundValue(@NotNull String text) { final int len = text.length(); if (len > 0) { diff --git a/platform/util/src/com/intellij/util/ui/UIUtil.java b/platform/util/src/com/intellij/util/ui/UIUtil.java index b1d27b3c27fb..8620f356f11d 100644 --- a/platform/util/src/com/intellij/util/ui/UIUtil.java +++ b/platform/util/src/com/intellij/util/ui/UIUtil.java @@ -314,13 +314,16 @@ public class UIUtil { } try { - isRetina = (getScaleFactorMethod == null) || ((Integer)getScaleFactorMethod.invoke(device) != 1); + isRetina = getScaleFactorMethod == null || (Integer)getScaleFactorMethod.invoke(device) != 1; } catch (IllegalAccessException e) { LOG.debug("CGraphicsDevice.getScaleFactor(): Access issue"); isRetina = false; } catch (InvocationTargetException e) { LOG.debug("CGraphicsDevice.getScaleFactor(): Invocation issue"); isRetina = false; + } catch (IllegalArgumentException e) { + LOG.debug("object is not an instance of declaring class: " + device.getClass().getName()); + isRetina = false; } devicesToRetinaSupportCacheMap.put(device, isRetina); @@ -1783,7 +1786,7 @@ public class UIUtil { } public static BufferedImage createImageForGraphics(Graphics2D g, int width, int height, int type) { - if (DetectRetinaKit.isMacRetina(g)) { + if (isRetina(g)) { return RetinaImage.create(width, height, type); } //noinspection UndesirableClassUsage diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/diff/ChangeGoToChangePopupAction.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/diff/ChangeGoToChangePopupAction.java index 2e5b6dfd2c30..012591b0baa5 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/diff/ChangeGoToChangePopupAction.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/diff/ChangeGoToChangePopupAction.java @@ -1,5 +1,7 @@ package com.intellij.openapi.vcs.changes.actions.diff; +import com.intellij.diff.actions.impl.GoToChangePopupBuilder; +import com.intellij.diff.chains.DiffRequestChain; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.DefaultActionGroup; import com.intellij.openapi.project.Project; @@ -7,12 +9,11 @@ import com.intellij.openapi.project.ProjectManager; import com.intellij.openapi.ui.popup.JBPopup; import com.intellij.openapi.ui.popup.JBPopupFactory; import com.intellij.openapi.util.Ref; -import com.intellij.diff.actions.impl.GoToChangePopupBuilder; -import com.intellij.diff.chains.DiffRequestChain; import com.intellij.openapi.vcs.changes.Change; import com.intellij.openapi.vcs.changes.ui.ChangesBrowser; import com.intellij.openapi.wm.IdeFocusManager; import com.intellij.util.Consumer; +import com.intellij.util.ui.update.UiNotifyConnector; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -68,16 +69,22 @@ public abstract class ChangeGoToChangePopupAction myPopup; + @NotNull private final Ref myPopup; public MyChangesBrowser(@NotNull Project project, @NotNull List changes, - @Nullable Change currentChange, + @Nullable final Change currentChange, @NotNull Ref popup) { super(project, null, changes, null, false, false, null, MyUseCase.LOCAL_CHANGES, null); setSelectionMode(ListSelectionModel.SINGLE_SELECTION); setChangesToDisplay(changes); - if (currentChange != null) select(Collections.singletonList(currentChange)); + + UiNotifyConnector.doWhenFirstShown(this, new Runnable() { + @Override + public void run() { + if (currentChange != null) select(Collections.singletonList(currentChange)); + } + }); myPopup = popup; } diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/tree/actions/XFetchValueActionBase.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/tree/actions/XFetchValueActionBase.java index f629904d14cc..b663348762a1 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/tree/actions/XFetchValueActionBase.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/tree/actions/XFetchValueActionBase.java @@ -48,14 +48,7 @@ public abstract class XFetchValueActionBase extends AnAction { if (paths != null) { for (TreePath path : paths) { Object node = path.getLastPathComponent(); - if (node instanceof XValueNodeImpl) { - if (((XValueNodeImpl)node).isComputed()) { - e.getPresentation().setEnabled(true); - return; - } - } - else if (node instanceof WatchMessageNode) { - e.getPresentation().setEnabled(true); + if (isEnabled(e, node)) { return; } } @@ -63,8 +56,22 @@ public abstract class XFetchValueActionBase extends AnAction { e.getPresentation().setEnabled(false); } + protected boolean isEnabled(@NotNull AnActionEvent event, @NotNull Object node) { + if (node instanceof XValueNodeImpl) { + if (((XValueNodeImpl)node).isComputed()) { + event.getPresentation().setEnabled(true); + return true; + } + } + else if (node instanceof WatchMessageNode) { + event.getPresentation().setEnabled(true); + return true; + } + return false; + } + @Override - public void actionPerformed(@NotNull final AnActionEvent e) { + public void actionPerformed(@NotNull AnActionEvent e) { TreePath[] paths = getSelectedNodes(e.getDataContext()); if (paths == null) { return; @@ -72,28 +79,31 @@ public abstract class XFetchValueActionBase extends AnAction { ValueCollector valueCollector = createCollector(e); for (TreePath path : paths) { - Object node = path.getLastPathComponent(); - if (node instanceof XValueNodeImpl) { - XValueNodeImpl valueNode = (XValueNodeImpl)node; - XFullValueEvaluator fullValueEvaluator = valueNode.getFullValueEvaluator(); - if (paths.length > 1) { // multiselection - copy the whole node text, see IDEA-136722 - valueCollector.add(valueNode.getText().toString(), valueNode.getPath().getPathCount()); - } - else { - if (fullValueEvaluator == null || !fullValueEvaluator.isShowValuePopup()) { - valueCollector.add(StringUtil.notNullize(DebuggerUIUtil.getNodeRawValue(valueNode))); - } - else { - new CopyValueEvaluationCallback(valueNode, valueCollector).startFetchingValue(fullValueEvaluator); - } - } - } - else if (node instanceof WatchMessageNode) { - valueCollector.add(((WatchMessageNode)node).getExpression().getExpression()); - } + addToCollector(paths, path.getLastPathComponent(), valueCollector); } valueCollector.processed = true; - valueCollector.finish(e.getProject()); + valueCollector.finish(); + } + + protected void addToCollector(@NotNull TreePath[] paths, @NotNull Object node, @NotNull ValueCollector valueCollector) { + if (node instanceof XValueNodeImpl) { + XValueNodeImpl valueNode = (XValueNodeImpl)node; + XFullValueEvaluator fullValueEvaluator = valueNode.getFullValueEvaluator(); + if (paths.length > 1) { // multiselection - copy the whole node text, see IDEA-136722 + valueCollector.add(valueNode.getText().toString(), valueNode.getPath().getPathCount()); + } + else { + if (fullValueEvaluator == null || !fullValueEvaluator.isShowValuePopup()) { + valueCollector.add(StringUtil.notNullize(DebuggerUIUtil.getNodeRawValue(valueNode))); + } + else { + new CopyValueEvaluationCallback(valueNode, valueCollector).startFetchingValue(fullValueEvaluator); + } + } + } + else if (node instanceof WatchMessageNode) { + valueCollector.add(((WatchMessageNode)node).getExpression().getExpression()); + } } @NotNull @@ -120,7 +130,8 @@ public abstract class XFetchValueActionBase extends AnAction { indents.put(values.size() - 1, indent); } - public void finish(Project project) { + public void finish() { + Project project = myTree.getProject(); if (processed && !values.contains(null) && !project.isDisposed()) { int minIndent = Integer.MAX_VALUE; for (int indent : indents.getValues()) { @@ -151,12 +162,12 @@ public abstract class XFetchValueActionBase extends AnAction { return index; } - public void evaluationComplete(final int index, @NotNull final String value, final Project project) { + public void evaluationComplete(final int index, @NotNull final String value) { AppUIUtil.invokeOnEdt(new Runnable() { @Override public void run() { values.set(index, value); - finish(project); + finish(); } }); } @@ -177,7 +188,7 @@ public abstract class XFetchValueActionBase extends AnAction { @Override protected void evaluationComplete(@NotNull String value, @NotNull Project project) { - myValueCollector.evaluationComplete(myValueIndex, value, project); + myValueCollector.evaluationComplete(myValueIndex, value); } } } diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/META-INF/InspectionGadgets.xml b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/META-INF/InspectionGadgets.xml index 21ba183afedc..0bec30a07106 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/META-INF/InspectionGadgets.xml +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/META-INF/InspectionGadgets.xml @@ -149,6 +149,9 @@ + #ref replaceable by 'Objects.equals()' expression #loc -equals.replaceable.by.objects.call.quickfix=Replace with 'Objects.equals()' expression \ No newline at end of file +equals.replaceable.by.objects.call.quickfix=Replace with 'Objects.equals()' expression +array.objects.equals.display.name='Objects.equals()' called on arrays +array.objects.equals.problem.descriptor=Objects.#ref() on arrays should probably be 'Arrays.equals()' #loc +array.objects.deep.equals.problem.descriptor=Objects.#ref() on arrays should probably be 'Arrays.deepEquals()' #loc \ No newline at end of file diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/ArrayEqualityInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/ArrayEqualityInspection.java index b52b9be3d873..f42ec5eba0ac 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/ArrayEqualityInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/ArrayEqualityInspection.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2013 Dave Griffith, Bas Leijdekkers + * Copyright 2011-2015 Dave Griffith, Bas Leijdekkers * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -79,7 +79,7 @@ public class ArrayEqualityInspection extends BaseInspection { @NotNull @Override public String getFamilyName() { - return "Replace with implicit equals"; + return InspectionGadgetsBundle.message("replace.with.arrays.equals"); } @Override diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/ArrayObjectsEqualsInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/ArrayObjectsEqualsInspection.java new file mode 100644 index 000000000000..6b08cf993657 --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/ArrayObjectsEqualsInspection.java @@ -0,0 +1,149 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.bugs; + +import com.intellij.codeInspection.ProblemDescriptor; +import com.intellij.openapi.project.Project; +import com.intellij.psi.*; +import com.siyeh.HardcodedMethodConstants; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.InspectionGadgetsFix; +import com.siyeh.ig.PsiReplacementUtil; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * @author Bas Leijdekkers + */ +public class ArrayObjectsEqualsInspection extends BaseInspection { + @Nls + @NotNull + @Override + public String getDisplayName() { + return InspectionGadgetsBundle.message("array.objects.equals.display.name"); + } + + @NotNull + @Override + protected String buildErrorString(Object... infos) { + final boolean deep = ((Boolean)infos[0]).booleanValue(); + return deep + ? InspectionGadgetsBundle.message("array.objects.deep.equals.problem.descriptor") + : InspectionGadgetsBundle.message("array.objects.equals.problem.descriptor"); + } + + @Override + public boolean isEnabledByDefault() { + return true; + } + + @Nullable + @Override + protected InspectionGadgetsFix buildFix(Object... infos) { + final boolean deep = ((Boolean)infos[0]).booleanValue(); + return new ArrayObjectsEqualsFix(deep); + } + + private static class ArrayObjectsEqualsFix extends InspectionGadgetsFix { + + private final boolean myDeep; + + public ArrayObjectsEqualsFix(boolean deep) { + myDeep = deep; + } + + @Nls + @NotNull + @Override + public String getName() { + return myDeep ? + InspectionGadgetsBundle.message("replace.with.arrays.deep.equals") : + InspectionGadgetsBundle.message("replace.with.arrays.equals"); + } + + @NotNull + @Override + public String getFamilyName() { + return InspectionGadgetsBundle.message("replace.with.arrays.equals"); + } + + @Override + protected void doFix(Project project, ProblemDescriptor descriptor) { + final PsiElement element = descriptor.getPsiElement().getParent().getParent(); + if (!(element instanceof PsiMethodCallExpression)) { + return; + } + final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)element; + final StringBuilder newExpression = new StringBuilder("java.util.Arrays."); + if (myDeep) { + newExpression.append("deepEquals"); + } + else { + newExpression.append("equals"); + } + newExpression.append(methodCallExpression.getArgumentList().getText()); + PsiReplacementUtil.replaceExpressionAndShorten(methodCallExpression, newExpression.toString()); + } + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new ArrayObjectsEqualsVisitor(); + } + + private static class ArrayObjectsEqualsVisitor extends BaseInspectionVisitor { + + @Override + public void visitMethodCallExpression(@NotNull PsiMethodCallExpression expression) { + final PsiReferenceExpression methodExpression = expression.getMethodExpression(); + final String methodName = methodExpression.getReferenceName(); + if (!HardcodedMethodConstants.EQUALS.equals(methodName)) { + return; + } + final PsiExpressionList argumentList = expression.getArgumentList(); + final PsiExpression[] expressions = argumentList.getExpressions(); + if (expressions.length != 2) { + return; + } + final PsiExpression argument1 = expressions[0]; + final PsiType type1 = argument1.getType(); + if (!(type1 instanceof PsiArrayType)) { + return; + } + final PsiExpression argument2 = expressions[1]; + final PsiType type2 = argument2.getType(); + if (!(type2 instanceof PsiArrayType)) { + return; + } + final int dimensions = type1.getArrayDimensions(); + if (dimensions != type2.getArrayDimensions()) { + return; + } + final PsiMethod method = expression.resolveMethod(); + if (method == null) { + return; + } + final PsiClass containingClass = method.getContainingClass(); + if (containingClass == null || !"java.util.Objects".equals(containingClass.getQualifiedName())) { + return; + } + registerMethodCallError(expression, Boolean.valueOf(dimensions > 1)); + } + } +} diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/ArrayEquality.html b/plugins/InspectionGadgets/src/inspectionDescriptions/ArrayEquality.html index 3b44d8e6846f..09c017ac0fdc 100644 --- a/plugins/InspectionGadgets/src/inspectionDescriptions/ArrayEquality.html +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/ArrayEquality.html @@ -1,7 +1,7 @@ -Reports any use of == to test for Array equality, -rather than the "java.util.Arrays.equals()" method. +Reports any use of == or != to test for array equality, +rather than the java.util.Arrays.equals() method.

diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/ArrayObjectsEquals.html b/plugins/InspectionGadgets/src/inspectionDescriptions/ArrayObjectsEquals.html new file mode 100644 index 000000000000..ebed149a381f --- /dev/null +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/ArrayObjectsEquals.html @@ -0,0 +1,11 @@ + + +Reports java.util.Objects.equals() called on two arrays. +Calling java.util.Objects.equals() with array arguments compares identity and is equivalent to using ==. +Use Arrays.equals() to compare the contents of two arrays +or Arrays.deepEquals() to compare the contents of two multi-dimensional arrays. + +

+New in 15 + + diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/NumberEquality.html b/plugins/InspectionGadgets/src/inspectionDescriptions/NumberEquality.html index 29933a8529f6..7d6c87fcd44a 100644 --- a/plugins/InspectionGadgets/src/inspectionDescriptions/NumberEquality.html +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/NumberEquality.html @@ -1,7 +1,7 @@ -Reports any use of == to test for Number equality, -rather than the ".equals()" method. With auto-boxing it is easy +Reports any use of == or != to test for Number equality, +rather than the equals() method. With auto-boxing it is easy to make the mistake of comparing two Integer (or other subclass of java.lang.Number) objects instead of two ints. diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/ObjectEquality.html b/plugins/InspectionGadgets/src/inspectionDescriptions/ObjectEquality.html index 3dccf4b78876..cef5adf14edf 100644 --- a/plugins/InspectionGadgets/src/inspectionDescriptions/ObjectEquality.html +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/ObjectEquality.html @@ -1,7 +1,7 @@ -Reports any use of == -to test for Object equality, rather than the ".equals()" method. +Reports any use of == or != +to test for Object equality, rather than the equals() method. Note that comparison of Strings or Numbers using == is not reported by this inspection, nor is the comparison of an object to null using ==, or the comparison of two array objects. diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/StringEquality.html b/plugins/InspectionGadgets/src/inspectionDescriptions/StringEquality.html index 0a4cc9ba3578..21415b2bfa45 100644 --- a/plugins/InspectionGadgets/src/inspectionDescriptions/StringEquality.html +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/StringEquality.html @@ -1,7 +1,7 @@ -Reports any use of == to test for String equality, -rather than the ".equals()" method. +Reports any use of == or != to test for String equality, +rather than the equals() method.

diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/array_objects_equals/ArrayObjectsEquals.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/array_objects_equals/ArrayObjectsEquals.java new file mode 100644 index 000000000000..f8f863dc5c6b --- /dev/null +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/array_objects_equals/ArrayObjectsEquals.java @@ -0,0 +1,14 @@ +class ArrayObjectsEquals { + + boolean one(String[] ss1, String[] ss2) { + return java.util.Objects.equals(ss1, ss2); + } + + boolean two(String[][] ss1, String[][] ss2) { + return java.util.Objects.equals(ss1, ss2); + } + + boolean noWarn(String s1, String[] ss2) { + return java.util.Objects.equals(s1, ss2); + } +} \ No newline at end of file diff --git a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/bugs/ArrayObjectsEqualsInspectionTest.java b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/bugs/ArrayObjectsEqualsInspectionTest.java new file mode 100644 index 000000000000..a8b7fae1aa4e --- /dev/null +++ b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/bugs/ArrayObjectsEqualsInspectionTest.java @@ -0,0 +1,47 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.bugs; + +import com.intellij.codeInspection.InspectionProfileEntry; +import com.siyeh.ig.LightInspectionTestCase; +import junit.framework.TestCase; + +/** + * @author Bas Leijdekkers + */ +public class ArrayObjectsEqualsInspectionTest extends LightInspectionTestCase { + + public void testArrayObjectsEquals() { + doTest(); + } + + @Override + protected InspectionProfileEntry getInspection() { + return new ArrayObjectsEqualsInspection(); + } + + @Override + protected String[] getEnvironmentClasses() { + return new String[]{ + "package java.util;" + + "public class Objects {" + + " public static boolean equals(Object a, Object b) {" + + " return (a == b) || (a != null && a.equals(b));" + + " }" + + "}" + }; + } +} \ No newline at end of file diff --git a/plugins/editorconfig/src/org/editorconfig/configmanagement/LineEndingsManager.java b/plugins/editorconfig/src/org/editorconfig/configmanagement/LineEndingsManager.java index a65d00796699..0cfd3d6e9b36 100644 --- a/plugins/editorconfig/src/org/editorconfig/configmanagement/LineEndingsManager.java +++ b/plugins/editorconfig/src/org/editorconfig/configmanagement/LineEndingsManager.java @@ -48,7 +48,7 @@ public class LineEndingsManager extends FileDocumentManagerAdapter { @Override public void run() { IdeFrame frame = WindowManager.getInstance().getIdeFrame(myProject); - StatusBar statusBar = frame.getStatusBar(); + StatusBar statusBar = frame != null ? frame.getStatusBar() : null; StatusBarWidget widget = statusBar != null ? statusBar.getWidget("LineSeparator") : null; if (widget instanceof LineSeparatorPanel) { diff --git a/plugins/git4idea/src/git4idea/actions/GitRepositoryAction.java b/plugins/git4idea/src/git4idea/actions/GitRepositoryAction.java index 042b651c6840..50ba95a046fb 100644 --- a/plugins/git4idea/src/git4idea/actions/GitRepositoryAction.java +++ b/plugins/git4idea/src/git4idea/actions/GitRepositoryAction.java @@ -29,6 +29,7 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.vcsUtil.VcsFileUtil; import git4idea.GitUtil; import git4idea.GitVcs; +import git4idea.branch.GitBranchUtil; import git4idea.i18n.GitBundle; import git4idea.repo.GitRepository; import git4idea.repo.GitRepositoryManager; @@ -57,22 +58,8 @@ public abstract class GitRepositoryAction extends DumbAwareAction { GitVcs vcs = GitVcs.getInstance(project); final List roots = getGitRoots(project, vcs); if (roots == null) return; - // get default root - final VirtualFile[] vFiles = e.getData(CommonDataKeys.VIRTUAL_FILE_ARRAY); - VirtualFile defaultRootVar = null; - if (vFiles != null) { - for (VirtualFile file : vFiles) { - final VirtualFile root = GitUtil.gitRootOrNull(file); - if (root != null) { - defaultRootVar = root; - break; - } - } - } - if (defaultRootVar == null) { - defaultRootVar = roots.get(0); - } - final VirtualFile defaultRoot = defaultRootVar; + + final VirtualFile defaultRoot = getDefaultRoot(project, roots, e.getData(CommonDataKeys.VIRTUAL_FILE_ARRAY)); final Set affectedRoots = new HashSet(); String actionName = getActionName(); @@ -88,6 +75,20 @@ public abstract class GitRepositoryAction extends DumbAwareAction { } } + @NotNull + private static VirtualFile getDefaultRoot(@NotNull Project project, @NotNull List roots, @Nullable VirtualFile[] vFiles) { + if (vFiles != null) { + for (VirtualFile file : vFiles) { + VirtualFile root = GitUtil.gitRootOrNull(file); + if (root != null) { + return root; + } + } + } + GitRepository currentRepository = GitBranchUtil.getCurrentRepository(project); + return currentRepository != null ? currentRepository.getRoot() : roots.get(0); + } + protected final void runFinalTasks(Project project, GitVcs vcs, Set affectedRoots, String actionName, List exceptions) { VcsFileUtil.refreshFiles(project, affectedRoots); diff --git a/plugins/git4idea/src/git4idea/i18n/GitBundle.properties b/plugins/git4idea/src/git4idea/i18n/GitBundle.properties index a141fbe4cb86..7b1ad1821161 100644 --- a/plugins/git4idea/src/git4idea/i18n/GitBundle.properties +++ b/plugins/git4idea/src/git4idea/i18n/GitBundle.properties @@ -256,7 +256,7 @@ rebase.editor.button=Start Rebasing rebase.editor.comment.column=Comment rebase.editor.commit.column=Commit rebase.editor.invalid.entryset=No commits found to rebase -rebase.editor.invalid.squash=The first non-skip commit can't be marked as {0} since it merges commit with the previous commit. +rebase.editor.invalid.squash=The first non-skip commit can''t be marked as {0} since it merges commit with the previous commit. rebase.editor.message=Reorder and edit &rebased commits rebase.editor.move.down.tooltip=Move commit down (commit will be applied later) rebase.editor.move.down=Move &Down diff --git a/plugins/git4idea/src/git4idea/rebase/GitRebaseEditor.java b/plugins/git4idea/src/git4idea/rebase/GitRebaseEditor.java index e0c9aa035257..7514aa940bbe 100644 --- a/plugins/git4idea/src/git4idea/rebase/GitRebaseEditor.java +++ b/plugins/git4idea/src/git4idea/rebase/GitRebaseEditor.java @@ -21,14 +21,21 @@ import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.ui.Cell; +import com.intellij.ui.TableSpeedSearch; +import com.intellij.ui.table.JBTable; import com.intellij.util.ArrayUtil; import com.intellij.util.ListWithSelection; +import com.intellij.util.PairFunction; import com.intellij.util.ui.ComboBoxTableCellRenderer; +import com.intellij.util.ui.JBUI; +import com.intellij.util.ui.UIUtil; import git4idea.GitUtil; import git4idea.config.GitConfigUtil; import git4idea.i18n.GitBundle; import git4idea.util.StringScanner; import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.Nullable; import javax.swing.*; import javax.swing.event.ListSelectionEvent; @@ -53,7 +60,7 @@ public class GitRebaseEditor extends DialogWrapper { /** * The table that lists all commits */ - private JTable myCommitsTable; + private JBTable myCommitsTable; /** * The move up button */ @@ -115,6 +122,7 @@ public class GitRebaseEditor extends DialogWrapper { myTableModel.load(file); myCommitsTable.setModel(myTableModel); myCommitsTable.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION); + myCommitsTable.setIntercellSpacing(JBUI.emptySize()); final JComboBox editorComboBox = new JComboBox(); for (Object option : GitRebaseEntry.Action.values()) { @@ -128,9 +136,9 @@ public class GitRebaseEditor extends DialogWrapper { public void valueChanged(final ListSelectionEvent e) { myViewButton.setEnabled(myCommitsTable.getSelectedRowCount() == 1); final ListSelectionModel selectionModel = myCommitsTable.getSelectionModel(); - myMoveUpButton.setEnabled( selectionModel.getMinSelectionIndex() > 0); - myMoveDownButton.setEnabled( selectionModel.getMaxSelectionIndex() != -1 && - selectionModel.getMaxSelectionIndex() < myTableModel.myEntries.size() - 1); + myMoveUpButton.setEnabled(selectionModel.getMinSelectionIndex() > 0); + myMoveDownButton.setEnabled(selectionModel.getMaxSelectionIndex() != -1 && + selectionModel.getMaxSelectionIndex() < myTableModel.myEntries.size() - 1); } }); myViewButton.addActionListener(new ActionListener() { @@ -152,8 +160,37 @@ public class GitRebaseEditor extends DialogWrapper { validateFields(); } }); + + installSpeedSearch(); + + adjustColumnWidth(0); + adjustColumnWidth(1); init(); } + + private void installSpeedSearch() { + new TableSpeedSearch(myCommitsTable, new PairFunction() { + @Nullable + @Override + public String fun(Object o, Cell cell) { + return cell.column == 0 ? null : String.valueOf(o); + } + }); + } + + @Nullable + @Override + public JComponent getPreferredFocusedComponent() { + return myCommitsTable; + } + + private void adjustColumnWidth(int columnIndex) { + int contentWidth = myCommitsTable.getExpandedColumnWidth(columnIndex) + UIUtil.DEFAULT_HGAP; + TableColumn column = myCommitsTable.getColumnModel().getColumn(columnIndex); + column.setMaxWidth(contentWidth); + column.setPreferredWidth(contentWidth); + } + /** * Validate fields */ diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/console/GroovyConsoleStateService.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/console/GroovyConsoleStateService.java index 30503b0bba59..2f007bda5b28 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/console/GroovyConsoleStateService.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/console/GroovyConsoleStateService.java @@ -71,7 +71,7 @@ public class GroovyConsoleStateService implements PersistentStateComponent 0) { diff --git a/plugins/junit/src/com/intellij/execution/junit2/ui/JUnitTestTreeView.java b/plugins/junit/src/com/intellij/execution/junit2/ui/JUnitTestTreeView.java index efc51cce4774..334e57805087 100644 --- a/plugins/junit/src/com/intellij/execution/junit2/ui/JUnitTestTreeView.java +++ b/plugins/junit/src/com/intellij/execution/junit2/ui/JUnitTestTreeView.java @@ -22,7 +22,6 @@ import com.intellij.execution.testframework.TestTreeView; import org.jetbrains.annotations.NotNull; import javax.swing.tree.TreePath; -import javax.swing.tree.TreeSelectionModel; public class JUnitTestTreeView extends TestTreeView { @@ -42,9 +41,4 @@ public class JUnitTestTreeView extends TestTreeView { final boolean hasFocus) { return Formatters.printTest(TestProxyClient.from(value)); } - - @Override - protected int getSelectionMode() { - return TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION; - } } diff --git a/plugins/junit/test/com/intellij/execution/junit/JUnitClasspathTest.java b/plugins/junit/test/com/intellij/execution/junit/JUnitClasspathTest.java index 27757569ab49..8e3b6eb16bcc 100644 --- a/plugins/junit/test/com/intellij/execution/junit/JUnitClasspathTest.java +++ b/plugins/junit/test/com/intellij/execution/junit/JUnitClasspathTest.java @@ -83,11 +83,11 @@ public class JUnitClasspathTest extends JavaCodeInsightFixtureTestCase { String file = preparePathsForComparison(FileUtil.loadFile(workingDirsFile), mod1, mod2); assertEquals("p\n" + "MODULE_1\n" + - "IDEA_HOME/lib/junit-4.12.jar;IDEA_HOME/java/mockJDK-1.7/jre/lib/annotations.jar;IDEA_HOME/java/mockJDK-1.7/jre/lib/rt.jar\n" + + "IDEA_HOME/lib/junit-4.12.jar;IDEA_HOME/java/mockJDK-1.8/jre/lib/rt.jar\n" + "1\n" + "p.T1\n" + "MODULE_2\n" + - "IDEA_HOME/lib/junit-4.12.jar;IDEA_HOME/java/mockJDK-1.7/jre/lib/annotations.jar;IDEA_HOME/java/mockJDK-1.7/jre/lib/rt.jar\n" + + "IDEA_HOME/lib/junit-4.12.jar;IDEA_HOME/java/mockJDK-1.8/jre/lib/rt.jar\n" + "1\n" + "p.T2\n", file); @@ -99,11 +99,11 @@ public class JUnitClasspathTest extends JavaCodeInsightFixtureTestCase { file = preparePathsForComparison(FileUtil.loadFile(workingDirsFile), mod1, mod2); assertEquals("p\n" + "MODULE_1\n" + - "IDEA_HOME/lib/serviceMessages.jar;IDEA_HOME/lib/junit-4.12.jar;IDEA_HOME/java/mockJDK-1.7/jre/lib/annotations.jar;IDEA_HOME/java/mockJDK-1.7/jre/lib/rt.jar\n" + + "IDEA_HOME/lib/serviceMessages.jar;IDEA_HOME/lib/junit-4.12.jar;IDEA_HOME/java/mockJDK-1.8/jre/lib/rt.jar\n" + "1\n" + "p.T1\n" + "MODULE_2\n" + - "IDEA_HOME/lib/serviceMessages.jar;IDEA_HOME/lib/junit-4.12.jar;IDEA_HOME/java/mockJDK-1.7/jre/lib/annotations.jar;IDEA_HOME/java/mockJDK-1.7/jre/lib/rt.jar\n" + + "IDEA_HOME/lib/serviceMessages.jar;IDEA_HOME/lib/junit-4.12.jar;IDEA_HOME/java/mockJDK-1.8/jre/lib/rt.jar\n" + "1\n" + "p.T2\n", file); } @@ -133,7 +133,7 @@ public class JUnitClasspathTest extends JavaCodeInsightFixtureTestCase { private Module setupModule(String moduleName, final String className) throws IOException { final VirtualFile root1 = myFixture.getTempDirFixture().findOrCreateDir(moduleName); final Module module = PsiTestUtil.addModule(getProject(), JavaModuleType.getModuleType(), moduleName, root1); - PsiTestUtil.removeAllRoots(module, IdeaTestUtil.getMockJdk17()); + PsiTestUtil.removeAllRoots(module, IdeaTestUtil.getMockJdk18()); PsiTestUtil.addSourceRoot(module, root1, true); myFixture.addFileToProject(moduleName + "/p/" + className + ".java", "package p;\n" + diff --git a/plugins/junit/test/com/intellij/execution/junit/JUnitRerunFailedTestsTest.java b/plugins/junit/test/com/intellij/execution/junit/JUnitRerunFailedTestsTest.java index d42b741ad653..82e5f466788e 100644 --- a/plugins/junit/test/com/intellij/execution/junit/JUnitRerunFailedTestsTest.java +++ b/plugins/junit/test/com/intellij/execution/junit/JUnitRerunFailedTestsTest.java @@ -87,10 +87,10 @@ public class JUnitRerunFailedTestsTest extends LightCodeInsightFixtureTestCase final SMTestProxy rootProxy = new SMTestProxy("MyTest", true, "java:suite://MyTest"); rootProxy.setLocator(locationProvider); - final SMTestProxy proxyParam = new SMTestProxy("[0]", true, "java:suite://MyTest.[0]"); + final SMTestProxy proxyParam = new SMTestProxy("[0.java]", true, "java:suite://MyTest.[0.java]"); proxyParam.setLocator(locationProvider); - final SMTestProxy parameterizedTestProxy = new SMTestProxy("testName1[0]", false, "java:test://MyTest.testName1[0]"); + final SMTestProxy parameterizedTestProxy = new SMTestProxy("testName1[0.java]", false, "java:test://MyTest.testName1[0.java]"); parameterizedTestProxy.setLocator(locationProvider); final Location rootLocation = rootProxy.getLocation(project, searchScope); @@ -100,15 +100,15 @@ public class JUnitRerunFailedTestsTest extends LightCodeInsightFixtureTestCase final Location proxyParamLocation = proxyParam.getLocation(project, searchScope); assertNotNull(proxyParamLocation); assertInstanceOf(proxyParamLocation, PsiMemberParameterizedLocation.class); - assertEquals("[0]", ((PsiMemberParameterizedLocation)proxyParamLocation).getParamSetName()); + assertEquals("[0.java]", ((PsiMemberParameterizedLocation)proxyParamLocation).getParamSetName()); assertEquals(testClass, proxyParamLocation.getPsiElement()); final Location parameterizedTestProxyLocation = parameterizedTestProxy.getLocation(project, searchScope); assertNotNull(parameterizedTestProxyLocation); assertInstanceOf(parameterizedTestProxyLocation, PsiMemberParameterizedLocation.class); - assertEquals("[0]", ((PsiMemberParameterizedLocation)parameterizedTestProxyLocation).getParamSetName()); + assertEquals("[0.java]", ((PsiMemberParameterizedLocation)parameterizedTestProxyLocation).getParamSetName()); assertEquals(testClass.getMethods()[0], parameterizedTestProxyLocation.getPsiElement()); assertEquals(testClass, ((PsiMemberParameterizedLocation)parameterizedTestProxyLocation).getContainingClass()); - assertEquals("MyTest,testName1[0]", TestMethods.getTestPresentation(parameterizedTestProxy, project, searchScope)); + assertEquals("MyTest,testName1[0.java]", TestMethods.getTestPresentation(parameterizedTestProxy, project, searchScope)); } } diff --git a/plugins/junit_rt/src/com/intellij/junit3/JUnit3IdeaTestRunner.java b/plugins/junit_rt/src/com/intellij/junit3/JUnit3IdeaTestRunner.java index d5fec550859a..60318a25c2a7 100644 --- a/plugins/junit_rt/src/com/intellij/junit3/JUnit3IdeaTestRunner.java +++ b/plugins/junit_rt/src/com/intellij/junit3/JUnit3IdeaTestRunner.java @@ -17,15 +17,14 @@ package com.intellij.junit3; import com.intellij.rt.execution.junit.*; import com.intellij.rt.execution.junit.segments.OutputObjectRegistry; -import com.intellij.rt.execution.junit.segments.SegmentedOutputStream; +import com.intellij.rt.execution.junit.segments.PacketProcessor; +import jetbrains.buildServer.messages.serviceMessages.ServiceMessage; +import jetbrains.buildServer.messages.serviceMessages.ServiceMessageTypes; import junit.framework.*; import junit.textui.ResultPrinter; import junit.textui.TestRunner; -import java.util.ArrayList; -import java.util.Enumeration; -import java.util.List; -import java.util.Vector; +import java.util.*; public class JUnit3IdeaTestRunner extends TestRunner implements IdeaTestRunner { private TestListener myTestsListener; @@ -39,7 +38,7 @@ public class JUnit3IdeaTestRunner extends TestRunner implements IdeaTestRunner { public int startRunnerWithArgs(String[] args, ArrayList listeners, String name, int count, boolean sendTree) { myListeners = listeners; - mySendTree = sendTree; + mySendTree = sendTree && !(myTestsListener instanceof SMTestListener); if (sendTree) { setPrinter(new TimeSender(myRegistry)); } @@ -69,9 +68,13 @@ public class JUnit3IdeaTestRunner extends TestRunner implements IdeaTestRunner { super.runFailed(message); } - public void setStreams(SegmentedOutputStream segmentedOut, SegmentedOutputStream segmentedErr, int lastIdx) { - myRegistry = new JUnit3OutputObjectRegistry(segmentedOut, lastIdx); - myTestsListener = new TestResultsSender(myRegistry); + public void setStreams(Object segmentedOut, Object segmentedErr, int lastIdx) { + if (JUnitStarter.SM_RUNNER) { + myTestsListener = new SMTestListener(); + } else { + myRegistry = new JUnit3OutputObjectRegistry((PacketProcessor)segmentedOut, lastIdx); + myTestsListener = new TestResultsSender(myRegistry); + } } public Object getTestToStart(String[] args, String name) { @@ -138,7 +141,11 @@ public class JUnit3IdeaTestRunner extends TestRunner implements IdeaTestRunner { System.err.println("Internal Error occured."); e.printStackTrace(System.err); } - return super.doRun(suite, wait); + final TestResult testResult = super.doRun(suite, wait); + if (myTestsListener instanceof SMTestListener) { + ((SMTestListener)myTestsListener).finishSuite(); + } + return testResult; } static Vector getTestCasesOf(Test test) { @@ -163,4 +170,53 @@ public class JUnit3IdeaTestRunner extends TestRunner implements IdeaTestRunner { super(DeafStream.DEAF_PRINT_STREAM); } } + + private static class SMTestListener implements TestListener { + private String myClassName; + + public void addError(Test test, Throwable e) { + final String failureMessage = e.getMessage(); + final Map attrs = new HashMap(); + attrs.put("name", getMethodName(test)); + attrs.put("message", failureMessage != null ? failureMessage : ""); + System.out.println(ServiceMessage.asString(ServiceMessageTypes.TEST_FAILED, attrs)); + } + + private static String getMethodName(Test test) { + final String toString = test.toString(); + final int braceIdx = toString.indexOf("("); + return braceIdx > 0 ? toString.substring(0, braceIdx) : toString; + } + + private static String getClassName(Test test) { + final String toString = test.toString(); + final int braceIdx = toString.indexOf("("); + return braceIdx > 0 && toString.endsWith(")") ? toString.substring(braceIdx + 1, toString.length() - 1) : null; + } + + public void addFailure(Test test, AssertionFailedError e) { + addError(test, e); + } + + public void endTest(Test test) { + System.out.println("\n##teamcity[testFinished name=\'" + getMethodName(test) + "\']"); + } + + public void startTest(Test test) { + final String className = getClassName(test); + if (className != null && !className.equals(myClassName)) { + finishSuite(); + myClassName = className; + System.out.println("##teamcity[testSuiteStarted name =\'" + myClassName + "\' locationHint=\'java:suite://" + className + "\']"); + } + final String methodName = getMethodName(test); + System.out.println("##teamcity[testStarted name=\'" + methodName + "\' locationHint=\'java:test://" + className + "." + methodName + "\']"); + } + + protected void finishSuite() { + if (myClassName != null) { + System.out.println("##teamcity[testSuiteFinished name=\'" + myClassName + "\']"); + } + } + } } diff --git a/plugins/junit_rt/src/com/intellij/junit4/JUnit4IdeaTestRunner.java b/plugins/junit_rt/src/com/intellij/junit4/JUnit4IdeaTestRunner.java index 21ae31db25e3..7512a386d5d4 100644 --- a/plugins/junit_rt/src/com/intellij/junit4/JUnit4IdeaTestRunner.java +++ b/plugins/junit_rt/src/com/intellij/junit4/JUnit4IdeaTestRunner.java @@ -17,8 +17,7 @@ package com.intellij.junit4; import com.intellij.rt.execution.junit.*; import com.intellij.rt.execution.junit.segments.OutputObjectRegistry; -import com.intellij.rt.execution.junit.segments.Packet; -import com.intellij.rt.execution.junit.segments.SegmentedOutputStream; +import com.intellij.rt.execution.junit.segments.PacketProcessor; import org.junit.internal.requests.ClassRequest; import org.junit.internal.requests.FilterRequest; import org.junit.runner.*; @@ -183,11 +182,11 @@ public class JUnit4IdeaTestRunner implements IdeaTestRunner { } - public void setStreams(SegmentedOutputStream segmentedOut, SegmentedOutputStream segmentedErr, int lastIdx) { + public void setStreams(Object segmentedOut, Object segmentedErr, int lastIdx) { if (JUnitStarter.SM_RUNNER) { myTestsListener = new SMTestSender(); } else { - myRegistry = new JUnit4OutputObjectRegistry(segmentedOut, lastIdx); + myRegistry = new JUnit4OutputObjectRegistry((PacketProcessor)segmentedOut, lastIdx); myTestsListener = new JUnit4TestResultsSender(myRegistry); } } diff --git a/plugins/junit_rt/src/com/intellij/junit4/SMTestSender.java b/plugins/junit_rt/src/com/intellij/junit4/SMTestSender.java index f513cd375bc6..3667bb19ef89 100644 --- a/plugins/junit_rt/src/com/intellij/junit4/SMTestSender.java +++ b/plugins/junit_rt/src/com/intellij/junit4/SMTestSender.java @@ -46,15 +46,15 @@ class SMTestSender extends RunListener { public void testRunStarted(Description description) throws Exception { myCurrentClassName = myIgnoreTopSuite ? description.toString() : null; - System.out.println("##teamcity[enteredTheMatrix]"); + System.out.println("##teamcity[enteredTheMatrix]\n"); } public void testRunFinished(Result result) throws Exception { if (myParamName != null) { - System.out.println("##teamcity[testSuiteFinished name=\'" + myParamName + "\']"); + System.out.println("##teamcity[testSuiteFinished name=\'" + myParamName + "\']\n"); } if (myCurrentClassName != null) { - System.out.println("##teamcity[testSuiteFinished name=\'" + myCurrentClassName + "\']"); + System.out.println("##teamcity[testSuiteFinished name=\'" + myCurrentClassName + "\']\n"); } } @@ -87,7 +87,7 @@ class SMTestSender extends RunListener { } public void testFinished(Description description) throws Exception { - System.out.println("##teamcity[testFinished name=\'" + JUnit4ReflectionUtil.getMethodName(description) + "\']"); + System.out.println("\n##teamcity[testFinished name=\'" + JUnit4ReflectionUtil.getMethodName(description) + "\']"); } public void testFailure(Failure failure) throws Exception { diff --git a/plugins/junit_rt/src/com/intellij/rt/execution/junit/IdeaTestRunner.java b/plugins/junit_rt/src/com/intellij/rt/execution/junit/IdeaTestRunner.java index a37c250b20a3..c6d9942f72da 100644 --- a/plugins/junit_rt/src/com/intellij/rt/execution/junit/IdeaTestRunner.java +++ b/plugins/junit_rt/src/com/intellij/rt/execution/junit/IdeaTestRunner.java @@ -21,7 +21,6 @@ package com.intellij.rt.execution.junit; import com.intellij.rt.execution.junit.segments.OutputObjectRegistry; -import com.intellij.rt.execution.junit.segments.SegmentedOutputStream; import java.util.ArrayList; import java.util.List; @@ -29,7 +28,7 @@ import java.util.List; public interface IdeaTestRunner { int startRunnerWithArgs(String[] args, ArrayList listeners, String name, int count, boolean sendTree); - void setStreams(SegmentedOutputStream segmentedOut, SegmentedOutputStream segmentedErr, int lastIdx); + void setStreams(Object segmentedOut, Object segmentedErr, int lastIdx); Object getTestToStart(String[] args, String name); List getChildTests(Object description); diff --git a/plugins/junit_rt/src/com/intellij/rt/execution/junit/JUnitForkedStarter.java b/plugins/junit_rt/src/com/intellij/rt/execution/junit/JUnitForkedStarter.java index 1cb4e9b4f169..8925177dfe08 100644 --- a/plugins/junit_rt/src/com/intellij/rt/execution/junit/JUnitForkedStarter.java +++ b/plugins/junit_rt/src/com/intellij/rt/execution/junit/JUnitForkedStarter.java @@ -72,8 +72,8 @@ public class JUnitForkedStarter { String[] args, boolean isJUnit4, List listeners, - String params, SegmentedOutputStream out, - SegmentedOutputStream err, + String params, Object out, + Object err, String forkMode, String path) throws Exception { final List parameters = new ArrayList(); @@ -178,8 +178,8 @@ public class JUnitForkedStarter { private static int processChildren(boolean isJUnit4, List listeners, - SegmentedOutputStream out, - SegmentedOutputStream err, + Object out, + Object err, List parameters, IdeaTestRunner testRunner, List children, @@ -206,8 +206,8 @@ public class JUnitForkedStarter { private static int runChild(boolean isJUnit4, List listeners, - SegmentedOutputStream out, - SegmentedOutputStream err, + Object out, + Object err, List parameters, String description, File workingDir, @@ -265,7 +265,9 @@ public class JUnitForkedStarter { final Process exec = builder.createProcess(); final int result = exec.waitFor(); - ForkedVMWrapper.readWrapped(testOutputPath, out.getPrintStream(), err.getPrintStream()); + ForkedVMWrapper.readWrapped(testOutputPath, + JUnitStarter.SM_RUNNER ? ((PrintStream)out) : ((SegmentedOutputStream)out).getPrintStream(), + JUnitStarter.SM_RUNNER ? ((PrintStream)err) : ((SegmentedOutputStream)err).getPrintStream()); return result; } } diff --git a/plugins/junit_rt/src/com/intellij/rt/execution/junit/JUnitStarter.java b/plugins/junit_rt/src/com/intellij/rt/execution/junit/JUnitStarter.java index 6ba413034184..7fce15057ead 100644 --- a/plugins/junit_rt/src/com/intellij/rt/execution/junit/JUnitStarter.java +++ b/plugins/junit_rt/src/com/intellij/rt/execution/junit/JUnitStarter.java @@ -51,8 +51,6 @@ public class JUnitStarter { } public static void main(String[] args) throws IOException { - SegmentedOutputStream out = new SegmentedOutputStream(System.out); - SegmentedOutputStream err = new SegmentedOutputStream(System.err); Vector argList = new Vector(); for (int i = 0; i < args.length; i++) { String arg = args[i]; @@ -64,18 +62,16 @@ public class JUnitStarter { boolean isJUnit4 = processParameters(argList, listeners, name); - if (!canWorkWithJUnitVersion(err, isJUnit4)) { - err.flush(); + if (!canWorkWithJUnitVersion(System.err, isJUnit4)) { System.exit(-3); } - if (!checkVersion(args, err)) { - err.flush(); + if (!checkVersion(args, System.err)) { System.exit(-3); } String[] array = new String[argList.size()]; argList.copyInto(array); - int exitCode = prepareStreamsAndStart(array, isJUnit4, listeners, name[0], out, err); + int exitCode = prepareStreamsAndStart(array, isJUnit4, listeners, name[0]); System.exit(exitCode); } @@ -171,15 +167,14 @@ public class JUnitStarter { } } - public static boolean checkVersion(String[] args, SegmentedOutputStream notifications) { + public static boolean checkVersion(String[] args, PrintStream printStream) { for (int i = 0; i < args.length; i++) { String arg = args[i]; if (arg.startsWith(IDE_VERSION)) { int ideVersion = Integer.parseInt(arg.substring(IDE_VERSION.length(), arg.length())); if (ideVersion != VERSION) { - PrintStream stream = new PrintStream(notifications); - stream.println("Wrong agent version: " + VERSION + ". IDE expects version: " + ideVersion); - stream.flush(); + printStream.println("Wrong agent version: " + VERSION + ". IDE expects version: " + ideVersion); + printStream.flush(); return false; } else return true; @@ -188,18 +183,17 @@ public class JUnitStarter { return false; } - private static boolean canWorkWithJUnitVersion(OutputStream notifications, boolean isJUnit4) { - final PrintStream stream = new PrintStream(notifications); + private static boolean canWorkWithJUnitVersion(PrintStream printStream, boolean isJUnit4) { try { junitVersionChecks(isJUnit4); } catch (Throwable e) { - stream.println("!!! JUnit version 3.8 or later expected:"); - stream.println(); - e.printStackTrace(stream); - stream.flush(); + printStream.println("!!! JUnit version 3.8 or later expected:"); + printStream.println(); + e.printStackTrace(printStream); + printStream.flush(); return false; } finally { - stream.flush(); + printStream.flush(); } return true; } @@ -214,20 +208,22 @@ public class JUnitStarter { private static int prepareStreamsAndStart(String[] args, final boolean isJUnit4, ArrayList listeners, - String name, - SegmentedOutputStream out, - SegmentedOutputStream err) { + String name) { PrintStream oldOut = System.out; PrintStream oldErr = System.err; try { - System.setOut(new PrintStream(out)); - System.setErr(new PrintStream(err)); + IdeaTestRunner testRunner = (IdeaTestRunner)getAgentClass(isJUnit4).newInstance(); + Object out = SM_RUNNER ? System.out : (Object)new SegmentedOutputStream(System.out); + Object err = SM_RUNNER ? System.err : (Object)new SegmentedOutputStream(System.err); + if (!SM_RUNNER) { + System.setOut(new PrintStream((OutputStream)out)); + System.setErr(new PrintStream((OutputStream)err)); + } if (ourCommandFileName != null) { if (!"none".equals(ourForkMode) || ourWorkingDirs != null && new File(ourWorkingDirs).length() > 0) { return JUnitForkedStarter.startForkedVMs(ourWorkingDirs, args, isJUnit4, listeners, name, out, err, ourForkMode, ourCommandFileName); } } - IdeaTestRunner testRunner = (IdeaTestRunner)getAgentClass(isJUnit4).newInstance(); testRunner.setStreams(out, err, 0); return testRunner.startRunnerWithArgs(args, listeners, name, ourCount, !SM_RUNNER); } diff --git a/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/editor/PropertiesAnchorizer.java b/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/editor/PropertiesAnchorizer.java new file mode 100644 index 000000000000..af603b25d2cc --- /dev/null +++ b/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/editor/PropertiesAnchorizer.java @@ -0,0 +1,119 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.lang.properties.editor; + +import com.intellij.lang.properties.IProperty; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiManager; +import com.intellij.psi.PsiTreeChangeAdapter; +import com.intellij.psi.PsiTreeChangeEvent; +import com.intellij.util.SmartList; +import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.containers.hash.HashMap; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * @author Dmitry Batkovich + */ +public class PropertiesAnchorizer { + private final static Logger LOG = Logger.getInstance(PropertiesAnchorizer.class); + + private final Map myAnchors = new HashMap(); + + public PropertiesAnchorizer(Project project) { + PsiManager.getInstance(project).addPsiTreeChangeListener(new PsiTreeChangeAdapter() { + @Override + public void childRemoved(@NotNull PsiTreeChangeEvent event) { + final PsiElement removed = event.getChild(); + if (removed instanceof IProperty) { + myAnchors.remove(removed); + } + } + }); + } + + public static final class PropertyAnchor { + private final Collection myProperties; + + public PropertyAnchor(Collection properties) { + myProperties = new ArrayList(properties); + } + + @NotNull + public String getName() { + return getRepresentative().getName(); + } + + @NotNull + public IProperty getRepresentative() { + return ContainerUtil.getFirstItem(myProperties); + } + + @Override + public String toString() { + return "PropertyAnchor:" + getName(); + } + + private void addProperties(final Collection properties) { + myProperties.addAll(properties); + } + } + + @NotNull + public PropertyAnchor get(IProperty property) { + final PropertyAnchor anchor = myAnchors.get(property); + LOG.assertTrue(anchor != null); + return anchor; + } + + public PropertyAnchor createOrUpdate(final Collection properties) { + LOG.assertTrue(!properties.isEmpty()); + + final List propertiesWithoutAnchor = new SmartList(); + PropertyAnchor representativeAnchor = null; + for (IProperty property : properties) { + final PropertyAnchor anchor = myAnchors.get(property); + if (anchor == null) { + propertiesWithoutAnchor.add(property); + } else { + if (representativeAnchor != null) { + LOG.assertTrue(representativeAnchor == anchor); + } + representativeAnchor = anchor; + } + } + + if (representativeAnchor == null) { + LOG.assertTrue(propertiesWithoutAnchor.size() == properties.size()); + representativeAnchor = new PropertyAnchor(properties); + } else { + representativeAnchor.addProperties(propertiesWithoutAnchor); + } + + for (IProperty property : propertiesWithoutAnchor) { + myAnchors.put(property, representativeAnchor); + } + + return representativeAnchor; + } +} diff --git a/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/editor/ResourceBundleFileStructureViewElement.java b/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/editor/ResourceBundleFileStructureViewElement.java index dde89d00cdca..daa8c701d19a 100644 --- a/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/editor/ResourceBundleFileStructureViewElement.java +++ b/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/editor/ResourceBundleFileStructureViewElement.java @@ -28,23 +28,23 @@ import com.intellij.navigation.ItemPresentation; import com.intellij.psi.PsiElement; import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.containers.MultiMap; import gnu.trove.TObjectIntHashMap; import gnu.trove.TObjectIntProcedure; import org.jetbrains.annotations.NotNull; import javax.swing.*; -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; +import java.util.*; public class ResourceBundleFileStructureViewElement implements StructureViewTreeElement, ResourceBundleEditorViewElement { private final ResourceBundle myResourceBundle; private boolean myShowOnlyIncomplete; + private PropertiesAnchorizer myAnchorizer; - public ResourceBundleFileStructureViewElement(final ResourceBundle resourceBundle) { + public ResourceBundleFileStructureViewElement(final ResourceBundle resourceBundle, PropertiesAnchorizer anchorizer) { myResourceBundle = resourceBundle; + myAnchorizer = anchorizer; } public void setShowOnlyIncomplete(boolean showOnlyIncomplete) { @@ -62,50 +62,49 @@ public class ResourceBundleFileStructureViewElement implements StructureViewTree @NotNull public StructureViewTreeElement[] getChildren() { - final Map propertyNames = getPropertiesMap(myResourceBundle, myShowOnlyIncomplete); + final MultiMap propertyNames = getPropertiesMap(myResourceBundle, myShowOnlyIncomplete); List result = new ArrayList(propertyNames.size()); - for (IProperty property : propertyNames.values()) { - result.add(new ResourceBundlePropertyStructureViewElement(myResourceBundle, property)); + for (Map.Entry> entry : propertyNames.entrySet()) { + final Collection properties = entry.getValue(); + final PropertiesAnchorizer.PropertyAnchor anchor = myAnchorizer.createOrUpdate(properties); + result.add(new ResourceBundlePropertyStructureViewElement(myResourceBundle, anchor)); } return result.toArray(new StructureViewTreeElement[result.size()]); } - public static Map getPropertiesMap(ResourceBundle resourceBundle, boolean onlyIncomplete) { + public static MultiMap getPropertiesMap(ResourceBundle resourceBundle, boolean onlyIncomplete) { List propertiesFiles = resourceBundle.getPropertiesFiles(); - final Map propertyNames; + final MultiMap propertyNames; if (onlyIncomplete) { propertyNames = getChildrenIdShowOnlyIncomplete(resourceBundle); } else { - propertyNames = new LinkedHashMap(); + propertyNames = MultiMap.createLinked(); for (PropertiesFile propertiesFile : propertiesFiles) { List properties = propertiesFile.getProperties(); for (IProperty property : properties) { String name = property.getKey(); - if (!propertyNames.containsKey(name)) { - propertyNames.put(name, property); - } + propertyNames.putValue(name, property); } } } return propertyNames; } - private static Map getChildrenIdShowOnlyIncomplete(ResourceBundle resourceBundle) { - final Map propertyNames = new LinkedHashMap(); + private static MultiMap getChildrenIdShowOnlyIncomplete(ResourceBundle resourceBundle) { + final MultiMap propertyNames = MultiMap.createLinked(); TObjectIntHashMap occurrences = new TObjectIntHashMap(); for (PropertiesFile file : resourceBundle.getPropertiesFiles()) { - Map currentFilePropertyNames = new LinkedHashMap(); + MultiMap currentFilePropertyNames = MultiMap.createLinked(); for (IProperty property : file.getProperties()) { String name = property.getKey(); - if (!currentFilePropertyNames.containsKey(name)) { - currentFilePropertyNames.put(name, property); - } + currentFilePropertyNames.putValue(name, property); } - propertyNames.putAll(currentFilePropertyNames); + propertyNames.putAllValues(currentFilePropertyNames); for (String propertyName : currentFilePropertyNames.keySet()) { if (occurrences.contains(propertyName)) { occurrences.adjustValue(propertyName, 1); - } else { + } + else { occurrences.put(propertyName, 1); } } diff --git a/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/editor/ResourceBundlePropertyStructureViewElement.java b/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/editor/ResourceBundlePropertyStructureViewElement.java index 79ef2fe97c79..19c62545dbf3 100644 --- a/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/editor/ResourceBundlePropertyStructureViewElement.java +++ b/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/editor/ResourceBundlePropertyStructureViewElement.java @@ -41,7 +41,7 @@ import java.awt.*; public class ResourceBundlePropertyStructureViewElement implements StructureViewTreeElement, ResourceBundleEditorViewElement { private final ResourceBundle myResourceBundle; - private final IProperty myProperty; + @NotNull private final PropertiesAnchorizer.PropertyAnchor myAnchor; private String myPresentableName; private static final TextAttributesKey INCOMPLETE_PROPERTY_KEY; @@ -64,18 +64,18 @@ public class ResourceBundlePropertyStructureViewElement implements StructureView INCOMPLETE_GROUP_KEY = TextAttributesKey.createTextAttributesKey("INCOMPLETE_GROUP_KEY", incompleteGroupKeyTextAttribute); } - public ResourceBundlePropertyStructureViewElement(final ResourceBundle resourceBundle, final IProperty property) { + public ResourceBundlePropertyStructureViewElement(final ResourceBundle resourceBundle, final @NotNull PropertiesAnchorizer.PropertyAnchor anchor) { myResourceBundle = resourceBundle; - myProperty = property; + myAnchor = anchor; } public IProperty getProperty() { - return myProperty; + return getValue().getRepresentative(); } @Override public PsiElement[] getPsiElements() { - return new PsiElement[] {getValue()}; + return new PsiElement[] {getProperty().getPsiElement()}; } public void setPresentableName(final String presentableName) { @@ -83,8 +83,8 @@ public class ResourceBundlePropertyStructureViewElement implements StructureView } @Override - public PsiElement getValue() { - return myProperty.getPsiElement(); + public PropertiesAnchorizer.PropertyAnchor getValue() { + return myAnchor; } @Override @@ -99,7 +99,7 @@ public class ResourceBundlePropertyStructureViewElement implements StructureView return new ColoredItemPresentation() { @Override public String getPresentableText() { - return myPresentableName == null ? myProperty.getName() : myPresentableName.isEmpty() ? PROPERTY_GROUP_KEY_TEXT : myPresentableName; + return myPresentableName == null ? getProperty().getName() : myPresentableName.isEmpty() ? PROPERTY_GROUP_KEY_TEXT : myPresentableName; } @Override @@ -115,11 +115,11 @@ public class ResourceBundlePropertyStructureViewElement implements StructureView @Override public TextAttributesKey getTextAttributesKey() { if (myPresentableName != null && myPresentableName.isEmpty()) { - return PropertiesUtil.isPropertyComplete(myResourceBundle, myProperty.getName()) + return PropertiesUtil.isPropertyComplete(myResourceBundle, getProperty().getName()) ? GROUP_KEY : INCOMPLETE_GROUP_KEY; } - return PropertiesUtil.isPropertyComplete(myResourceBundle, myProperty.getName()) + return PropertiesUtil.isPropertyComplete(myResourceBundle, getProperty().getName()) ? PropertiesHighlighter.PROPERTY_KEY : INCOMPLETE_PROPERTY_KEY; } diff --git a/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/editor/ResourceBundleStructureViewModel.java b/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/editor/ResourceBundleStructureViewModel.java index ba6a85a1ee7f..bad50426e66a 100644 --- a/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/editor/ResourceBundleStructureViewModel.java +++ b/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/editor/ResourceBundleStructureViewModel.java @@ -35,12 +35,12 @@ public class ResourceBundleStructureViewModel implements PropertiesGroupingStruc private final GroupByWordPrefixes myByWordPrefixesGrouper; private final ResourceBundleFileStructureViewElement myRoot; - public ResourceBundleStructureViewModel(ResourceBundle root) { + public ResourceBundleStructureViewModel(ResourceBundle root, PropertiesAnchorizer anchorizer) { myResourceBundle = root; String separator = PropertiesSeparatorManager.getInstance(root.getProject()). getSeparator(myResourceBundle); myByWordPrefixesGrouper = new GroupByWordPrefixes(separator); - myRoot = new ResourceBundleFileStructureViewElement(myResourceBundle); + myRoot = new ResourceBundleFileStructureViewElement(myResourceBundle, anchorizer); } public void setSeparator(String separator) { diff --git a/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/structureView/GroupByWordPrefixes.java b/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/structureView/GroupByWordPrefixes.java index efdbeaf3e863..79472a5d1691 100644 --- a/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/structureView/GroupByWordPrefixes.java +++ b/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/structureView/GroupByWordPrefixes.java @@ -21,6 +21,7 @@ import com.intellij.ide.util.treeView.AbstractTreeNode; import com.intellij.ide.util.treeView.smartTree.*; import com.intellij.lang.properties.IProperty; import com.intellij.lang.properties.PropertiesBundle; +import com.intellij.lang.properties.editor.PropertiesAnchorizer; import com.intellij.lang.properties.editor.ResourceBundlePropertyStructureViewElement; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.Comparing; @@ -69,7 +70,10 @@ public class GroupByWordPrefixes implements Grouper, Sorter { if (!(element instanceof StructureViewTreeElement)) { continue; } - final Object value = ((StructureViewTreeElement)element).getValue(); + Object value = ((StructureViewTreeElement)element).getValue(); + if (value instanceof PropertiesAnchorizer.PropertyAnchor) { + value = ((PropertiesAnchorizer.PropertyAnchor)value).getRepresentative(); + } if (!(value instanceof IProperty)) { continue; } @@ -182,6 +186,11 @@ public class GroupByWordPrefixes implements Grouper, Sorter { this.words = words; this.node = node; } + + @Override + public String toString() { + return "Key{words=" + words + ", node=" + node + '}'; + } } } diff --git a/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/structureView/PropertiesPrefixGroup.java b/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/structureView/PropertiesPrefixGroup.java index 981d6b59a9ee..6a4cbbe58703 100644 --- a/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/structureView/PropertiesPrefixGroup.java +++ b/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/structureView/PropertiesPrefixGroup.java @@ -20,6 +20,7 @@ import com.intellij.ide.structureView.StructureViewTreeElement; import com.intellij.ide.util.treeView.smartTree.Group; import com.intellij.ide.util.treeView.smartTree.TreeElement; import com.intellij.lang.properties.IProperty; +import com.intellij.lang.properties.editor.PropertiesAnchorizer; import com.intellij.lang.properties.editor.ResourceBundleEditorViewElement; import com.intellij.lang.properties.editor.ResourceBundlePropertyStructureViewElement; import com.intellij.lang.properties.psi.Property; @@ -95,7 +96,10 @@ public class PropertiesPrefixGroup implements Group, ResourceBundleEditorViewEle if (!(treeElement instanceof StructureViewTreeElement)) { continue; } - final Object value = ((StructureViewTreeElement)treeElement).getValue(); + Object value = ((StructureViewTreeElement)treeElement).getValue(); + if (value instanceof PropertiesAnchorizer.PropertyAnchor) { + value = ((PropertiesAnchorizer.PropertyAnchor)value).getRepresentative(); + } if (!(value instanceof IProperty)) { continue; } diff --git a/plugins/properties/src/com/intellij/lang/properties/editor/ResourceBundleEditor.java b/plugins/properties/src/com/intellij/lang/properties/editor/ResourceBundleEditor.java index 766806525c39..52036a407e3f 100644 --- a/plugins/properties/src/com/intellij/lang/properties/editor/ResourceBundleEditor.java +++ b/plugins/properties/src/com/intellij/lang/properties/editor/ResourceBundleEditor.java @@ -105,6 +105,7 @@ public class ResourceBundleEditor extends UserDataHolderBase implements FileEdit // we cannot store it back to properties file right now, so just append the backslash to the editor and wait for the subsequent chars private final Set myBackSlashPressed = new THashSet(); private final Alarm mySelectionChangeAlarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD); + private final PropertiesAnchorizer myPropertiesAnchorizer; private JPanel myValuesPanel; private JPanel myStructureViewPanel; @@ -131,7 +132,8 @@ public class ResourceBundleEditor extends UserDataHolderBase implements FileEdit myResourceBundle = resourceBundle; myPropertiesInsertDeleteManager = ResourceBundlePropertiesInsertManagerImpl.create(resourceBundle); - myStructureViewComponent = new ResourceBundleStructureViewComponent(myResourceBundle, this); + myPropertiesAnchorizer = new PropertiesAnchorizer(myResourceBundle.getProject()); + myStructureViewComponent = new ResourceBundleStructureViewComponent(myResourceBundle, this, myPropertiesAnchorizer); myStructureViewPanel.setLayout(new BorderLayout()); myStructureViewPanel.add(myStructureViewComponent, BorderLayout.CENTER); @@ -284,10 +286,11 @@ public class ResourceBundleEditor extends UserDataHolderBase implements FileEdit while (!toCheck.isEmpty()) { TreeElement element = toCheck.pop(); PsiElement value = element instanceof ResourceBundlePropertyStructureViewElement - ? ((ResourceBundlePropertyStructureViewElement)element).getValue() + ? ((ResourceBundlePropertyStructureViewElement)element).getProperty().getPsiElement() : null; if (value instanceof IProperty && propertyName.equals(((IProperty)value).getUnescapedKey())) { - myStructureViewComponent.select(value, true); + final PropertiesAnchorizer.PropertyAnchor anchor = myPropertiesAnchorizer.get((IProperty)value); + myStructureViewComponent.select(anchor, true); selectionChanged(); return; } @@ -361,7 +364,6 @@ public class ResourceBundleEditor extends UserDataHolderBase implements FileEdit } } }); - } }); } @@ -596,11 +598,11 @@ public class ResourceBundleEditor extends UserDataHolderBase implements FileEdit @Override public void childrenChanged(@NotNull PsiTreeChangeEvent event) { - final PsiFile file = event.getFile(); - PropertiesFile propertiesFile = PropertiesImplUtil.getPropertiesFile(file); - if (propertiesFile == null) return; - if (!propertiesFile.getResourceBundle().equals(myResourceBundle)) return; - updateEditorsFromProperties(); + // final PsiFile file = event.getFile(); + //PropertiesFile propertiesFile = PropertiesImplUtil.getPropertiesFile(file); + //if (propertiesFile == null) return; + //if (!propertiesFile.getResourceBundle().equals(myResourceBundle)) return; + //updateEditorsFromProperties(); } }; PsiManager.getInstance(myProject).addPsiTreeChangeListener(psiTreeChangeAdapter, this); @@ -693,10 +695,9 @@ public class ResourceBundleEditor extends UserDataHolderBase implements FileEdit final ResourceBundleFileStructureViewElement root = (ResourceBundleFileStructureViewElement)myStructureViewComponent.getTreeModel().getRoot(); - final Map propertiesMap = - ResourceBundleFileStructureViewElement.getPropertiesMap(myResourceBundle, root.isShowOnlyIncomplete()); + final Set propertyKeys = ResourceBundleFileStructureViewElement.getPropertiesMap(myResourceBundle, root.isShowOnlyIncomplete()).keySet(); final boolean isAlphaSorted = myStructureViewComponent.isActionActive(Sorter.ALPHA_SORTER_ID); - final List keysOrder = new ArrayList(propertiesMap.keySet()); + final List keysOrder = new ArrayList(propertyKeys); if (isAlphaSorted) { Collections.sort(keysOrder); } diff --git a/plugins/properties/src/com/intellij/lang/properties/editor/ResourceBundleStructureViewComponent.java b/plugins/properties/src/com/intellij/lang/properties/editor/ResourceBundleStructureViewComponent.java index c6ed5f82e254..e6e8fa495a5c 100644 --- a/plugins/properties/src/com/intellij/lang/properties/editor/ResourceBundleStructureViewComponent.java +++ b/plugins/properties/src/com/intellij/lang/properties/editor/ResourceBundleStructureViewComponent.java @@ -53,8 +53,10 @@ public class ResourceBundleStructureViewComponent extends PropertiesGroupingStru private final ResourceBundle myResourceBundle; - public ResourceBundleStructureViewComponent(final ResourceBundle resourceBundle, final ResourceBundleEditor editor) { - super(resourceBundle.getProject(), editor, new ResourceBundleStructureViewModel(resourceBundle)); + public ResourceBundleStructureViewComponent(final ResourceBundle resourceBundle, + final ResourceBundleEditor editor, + final PropertiesAnchorizer anchorizer) { + super(resourceBundle.getProject(), editor, new ResourceBundleStructureViewModel(resourceBundle, anchorizer)); myResourceBundle = resourceBundle; tunePopupActionGroup(); } diff --git a/plugins/tasks/tasks-core/jira/src/com/intellij/tasks/jira/jql/JqlElementType.java b/plugins/tasks/tasks-core/jira/src/com/intellij/tasks/jira/jql/JqlElementType.java index 9d9dc9118997..69991ff7d7ba 100644 --- a/plugins/tasks/tasks-core/jira/src/com/intellij/tasks/jira/jql/JqlElementType.java +++ b/plugins/tasks/tasks-core/jira/src/com/intellij/tasks/jira/jql/JqlElementType.java @@ -15,49 +15,21 @@ */ package com.intellij.tasks.jira.jql; -import com.intellij.extapi.psi.ASTWrapperPsiElement; -import com.intellij.lang.ASTNode; -import com.intellij.psi.PsiElement; import com.intellij.psi.tree.IElementType; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; -import java.lang.reflect.Constructor; - /** * @author Mikhail Golubev */ public class JqlElementType extends IElementType { - private static final Class[] PARAMETER_TYPES = {ASTNode.class}; - - private final Class myClass; - private Constructor myConstructor; public JqlElementType(@NotNull @NonNls String debugName) { - this(debugName, ASTWrapperPsiElement.class); - } - - public JqlElementType(@NotNull @NonNls String debugName, @NotNull Class cls) { super(debugName, JqlLanguage.INSTANCE); - myClass = cls; } @Override public String toString() { return "JQL: " + super.toString(); } - - @NotNull - public PsiElement createElement(@NotNull ASTNode node) { - try { - if (myConstructor == null) { - myConstructor = myClass.getConstructor(PARAMETER_TYPES); - } - return myConstructor.newInstance(node); - } - catch (Exception e) { - throw new AssertionError( - String.format("Class %s must have constructor accepting single ASTNode parameter", myClass.getName())); - } - } } diff --git a/plugins/tasks/tasks-core/jira/src/com/intellij/tasks/jira/jql/JqlElementTypes.java b/plugins/tasks/tasks-core/jira/src/com/intellij/tasks/jira/jql/JqlElementTypes.java index 6c822177b774..06b68634f7e6 100644 --- a/plugins/tasks/tasks-core/jira/src/com/intellij/tasks/jira/jql/JqlElementTypes.java +++ b/plugins/tasks/tasks-core/jira/src/com/intellij/tasks/jira/jql/JqlElementTypes.java @@ -1,9 +1,13 @@ package com.intellij.tasks.jira.jql; +import com.intellij.extapi.psi.ASTWrapperPsiElement; +import com.intellij.lang.ASTNode; +import com.intellij.psi.PsiElement; import com.intellij.psi.tree.IElementType; import com.intellij.psi.tree.IFileElementType; import com.intellij.psi.tree.TokenSet; import com.intellij.tasks.jira.jql.psi.impl.*; +import org.jetbrains.annotations.NotNull; /** * @author Mikhail Golubev @@ -69,30 +73,89 @@ import com.intellij.tasks.jira.jql.psi.impl.*; */ public interface JqlElementTypes { IFileElementType FILE = new IFileElementType(JqlLanguage.INSTANCE); - IElementType QUERY = new JqlElementType("QUERY", JqlQueryImpl.class); - IElementType OR_CLAUSE = new JqlElementType("OR_CLAUSE", JqlOrClauseImpl.class); - IElementType AND_CLAUSE = new JqlElementType("AND_CLAUSE", JqlAndClauseImpl.class); - IElementType NOT_CLAUSE = new JqlElementType("NOT_CLAUSE", JqlNotClauseImpl.class); + IElementType QUERY = new JqlElementType("QUERY"); + IElementType OR_CLAUSE = new JqlElementType("OR_CLAUSE"); + IElementType AND_CLAUSE = new JqlElementType("AND_CLAUSE"); + IElementType NOT_CLAUSE = new JqlElementType("NOT_CLAUSE"); // actually parenthesized clause, named so to be consistent with official grammar - IElementType SUB_CLAUSE = new JqlElementType("SUB_CLAUSE", JqlSubClauseImpl.class); + IElementType SUB_CLAUSE = new JqlElementType("SUB_CLAUSE"); //IElementType TERMINAL_CLAUSE = new JqlElementType("TERMINAL_CLAUSE"); // field (= | != | ~ | !~ | < | > | <= | >= | is [not] | [not] in) value - IElementType SIMPLE_CLAUSE = new JqlElementType("SIMPLE_CLAUSE", JqlSimpleClauseImpl.class); + IElementType SIMPLE_CLAUSE = new JqlElementType("SIMPLE_CLAUSE"); // field was [not] [in] value {history_predicate} - IElementType WAS_CLAUSE = new JqlElementType("WAS_CLAUSE", JqlWasClauseImpl.class); + IElementType WAS_CLAUSE = new JqlElementType("WAS_CLAUSE"); // field changed {history_predicate} - IElementType CHANGED_CLAUSE = new JqlElementType("CHANGED_CLAUSE", JqlChangedClauseImpl.class); - IElementType LIST = new JqlElementType("LIST", JqlListImpl.class); - IElementType ORDER_BY = new JqlElementType("ORDER_BY", JqlOrderByImpl.class); - IElementType IDENTIFIER = new JqlElementType("IDENTIFIER", JqlIdentifierImpl.class); - IElementType LITERAL = new JqlElementType("LITERAL", JqlLiteralImpl.class); - IElementType FUNCTION_CALL = new JqlElementType("FUNCTION_CALL", JqlFunctionCallImpl.class); - IElementType ARGUMENT_LIST = new JqlElementType("ARGUMENT_LIST", JqlArgumentListImpl.class); - IElementType SORT_KEY = new JqlElementType("SORT_KEY", JqlSortKeyImpl.class); - IElementType EMPTY = new JqlElementType("EMPTY", JqlEmptyValueImpl.class); - IElementType HISTORY_PREDICATE = new JqlElementType("HISTORY_PREDICATE", JqlHistoryPredicateImpl.class); + IElementType CHANGED_CLAUSE = new JqlElementType("CHANGED_CLAUSE"); + IElementType LIST = new JqlElementType("LIST"); + IElementType ORDER_BY = new JqlElementType("ORDER_BY"); + IElementType IDENTIFIER = new JqlElementType("IDENTIFIER"); + IElementType LITERAL = new JqlElementType("LITERAL"); + IElementType FUNCTION_CALL = new JqlElementType("FUNCTION_CALL"); + IElementType ARGUMENT_LIST = new JqlElementType("ARGUMENT_LIST"); + IElementType SORT_KEY = new JqlElementType("SORT_KEY"); + IElementType EMPTY = new JqlElementType("EMPTY"); + IElementType HISTORY_PREDICATE = new JqlElementType("HISTORY_PREDICATE"); TokenSet OPERAND_NODES = TokenSet.create( JqlTokenTypes.NUMBER_LITERAL, JqlTokenTypes.STRING_LITERAL, LIST, FUNCTION_CALL, EMPTY ); + + class Factory { + @NotNull + public static PsiElement createElement(@NotNull ASTNode node) { + final IElementType type = node.getElementType(); + if (type == QUERY) { + return new JqlQueryImpl(node); + } + else if (type == OR_CLAUSE) { + return new JqlOrClauseImpl(node); + } + else if (type == AND_CLAUSE) { + return new JqlAndClauseImpl(node); + } + else if (type == NOT_CLAUSE) { + return new JqlNotClauseImpl(node); + } + else if (type == SUB_CLAUSE) { + return new JqlSubClauseImpl(node); + } + else if (type == SIMPLE_CLAUSE) { + return new JqlSimpleClauseImpl(node); + } + else if (type == WAS_CLAUSE) { + return new JqlWasClauseImpl(node); + } + else if (type == CHANGED_CLAUSE) { + return new JqlChangedClauseImpl(node); + } + else if (type == LIST) { + return new JqlListImpl(node); + } + else if (type == ORDER_BY) { + return new JqlOrderByImpl(node); + } + else if (type == IDENTIFIER) { + return new JqlIdentifierImpl(node); + } + else if (type == LITERAL) { + return new JqlLiteralImpl(node); + } + else if (type == FUNCTION_CALL) { + return new JqlFunctionCallImpl(node); + } + else if (type == ARGUMENT_LIST) { + return new JqlArgumentListImpl(node); + } + else if (type == SORT_KEY) { + return new JqlSortKeyImpl(node); + } + else if (type == EMPTY) { + return new JqlEmptyValueImpl(node); + } + else if (type == HISTORY_PREDICATE) { + return new JqlHistoryPredicateImpl(node); + } + return new ASTWrapperPsiElement(node); + } + } } diff --git a/plugins/tasks/tasks-core/jira/src/com/intellij/tasks/jira/jql/JqlParserDefinition.java b/plugins/tasks/tasks-core/jira/src/com/intellij/tasks/jira/jql/JqlParserDefinition.java index a8269625fcfd..5c535cc0d9f9 100644 --- a/plugins/tasks/tasks-core/jira/src/com/intellij/tasks/jira/jql/JqlParserDefinition.java +++ b/plugins/tasks/tasks-core/jira/src/com/intellij/tasks/jira/jql/JqlParserDefinition.java @@ -1,6 +1,5 @@ package com.intellij.tasks.jira.jql; -import com.intellij.extapi.psi.ASTWrapperPsiElement; import com.intellij.lang.ASTNode; import com.intellij.lang.ParserDefinition; import com.intellij.lang.PsiParser; @@ -10,7 +9,6 @@ import com.intellij.openapi.project.Project; import com.intellij.psi.FileViewProvider; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; -import com.intellij.psi.tree.IElementType; import com.intellij.psi.tree.IFileElementType; import com.intellij.psi.tree.TokenSet; import org.jetbrains.annotations.NotNull; @@ -58,11 +56,7 @@ public class JqlParserDefinition implements ParserDefinition { @NotNull @Override public PsiElement createElement(ASTNode node) { - final IElementType type = node.getElementType(); - if (type instanceof JqlElementType) { - return ((JqlElementType)type).createElement(node); - } - return new ASTWrapperPsiElement(node); + return JqlElementTypes.Factory.createElement(node); } @Override diff --git a/plugins/testng/src/com/theoryinpractice/testng/configuration/SearchingForTestsTask.java b/plugins/testng/src/com/theoryinpractice/testng/configuration/SearchingForTestsTask.java index edf561ea8d25..da4701d312ad 100644 --- a/plugins/testng/src/com/theoryinpractice/testng/configuration/SearchingForTestsTask.java +++ b/plugins/testng/src/com/theoryinpractice/testng/configuration/SearchingForTestsTask.java @@ -40,7 +40,6 @@ import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.search.PackageScope; import com.intellij.psi.search.searches.AnnotatedMembersSearch; import com.intellij.psi.util.ClassUtil; -import com.intellij.util.containers.ContainerUtil; import com.theoryinpractice.testng.model.IDEARemoteTestRunnerClient; import com.theoryinpractice.testng.model.TestClassFilter; import com.theoryinpractice.testng.model.TestData; @@ -48,10 +47,7 @@ import com.theoryinpractice.testng.model.TestType; import com.theoryinpractice.testng.util.TestNGUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.testng.xml.LaunchSuite; -import org.testng.xml.Parser; -import org.testng.xml.SuiteGenerator; -import org.testng.xml.XmlSuite; +import org.testng.xml.*; import java.io.*; import java.net.ServerSocket; @@ -61,7 +57,7 @@ import java.util.regex.PatternSyntaxException; public class SearchingForTestsTask extends SearchForTestsTask { private static final Logger LOG = Logger.getInstance("#" + SearchingForTestsTask.class.getName()); - private final Map> myClasses; + private final Map>> myClasses; private final TestData myData; private final Project myProject; private final TestNGConfiguration myConfig; @@ -78,7 +74,7 @@ public class SearchingForTestsTask extends SearchForTestsTask { myProject = config.getProject(); myConfig = config; myTempFile = tempFile; - myClasses = new LinkedHashMap>(); + myClasses = new LinkedHashMap>>(); } @Override @@ -126,20 +122,20 @@ public class SearchingForTestsTask extends SearchForTestsTask { } private void composeTestSuiteFromClasses() { - Map> map = new LinkedHashMap>(); + Map>> map = new LinkedHashMap>>(); final boolean findTestMethodsForClass = shouldSearchForTestMethods(); - for (final Map.Entry> entry : myClasses.entrySet()) { - final Collection depMethods = entry.getValue(); - Collection methods = new LinkedHashSet(depMethods.size()); - for (PsiMethod method : depMethods) { - methods.add(method.getName()); + for (final Map.Entry>> entry : myClasses.entrySet()) { + final Map> depMethods = entry.getValue(); + LinkedHashMap> methods = new LinkedHashMap>(); + for (Map.Entry> method : depMethods.entrySet()) { + methods.put(method.getKey().getName(), method.getValue()); } if (findTestMethodsForClass && depMethods.isEmpty()) { for (PsiMethod method : entry.getKey().getMethods()) { if (TestNGUtil.hasTest(method)) { - methods.add(method.getName()); + methods.put(method.getName(), Collections.emptyList()); } } } @@ -180,10 +176,53 @@ public class SearchingForTestsTask extends SearchForTestsTask { logLevel = 1; } - LaunchSuite suite = - SuiteGenerator.createSuite(myProject.getName(), null, map, groupNames, testParams, "jdk", logLevel); - - File xmlFile = suite.save(new File(PathManager.getSystemPath())); + File xmlFile; + if (groupNames != null) { + final LinkedHashMap> methodNames = new LinkedHashMap>(); + for (Map.Entry>> entry : map.entrySet()) { + methodNames.put(entry.getKey(), entry.getValue().keySet()); + } + LaunchSuite suite = + SuiteGenerator.createSuite(myProject.getName(), null, methodNames, groupNames, testParams, "jdk", logLevel); + xmlFile = suite.save(new File(PathManager.getSystemPath())); + } + else { + final XmlSuite xmlSuite = new XmlSuite(); + XmlTest xmlTest = new XmlTest(xmlSuite); + xmlTest.setName(myProject.getName()); + xmlTest.setParameters(testParams); + List xmlClasses = new ArrayList(); + int idx = 0; + for (String className : map.keySet()) { + final XmlClass xmlClass = new XmlClass(className, idx++, false); + final ArrayList includedMethods = new ArrayList(); + final Map> collection = map.get(className); + int mIdx = 0; + for (String methodName : collection.keySet()) { + final List includes = new ArrayList(); + for (String include : collection.get(methodName)) { + try { + includes.add(Integer.parseInt(include)); + } + catch (NumberFormatException e) { + LOG.error(e); + } + } + includedMethods.add(new XmlInclude(methodName, includes, mIdx++)); + } + xmlClass.setIncludedMethods(includedMethods); + xmlClasses.add(xmlClass); + } + xmlTest.setXmlClasses(xmlClasses); + xmlFile = new File(PathManager.getSystemPath(), "temp-testng-customsuite.xml"); + final String toXml = xmlSuite.toXml(); + try { + FileUtil.writeToFile(xmlFile, toXml); + } + catch (IOException e) { + LOG.error(e); + } + } String path = xmlFile.getAbsolutePath() + "\n"; try { FileUtil.writeToFile(myTempFile, path.getBytes(CharsetToolkit.UTF8_CHARSET), true); @@ -195,7 +234,7 @@ public class SearchingForTestsTask extends SearchForTestsTask { private boolean shouldSearchForTestMethods() { boolean dependantMethods = false; - for (Collection methods : myClasses.values()) { + for (Map> methods : myClasses.values()) { if (!methods.isEmpty()) { dependantMethods = true; break; @@ -239,7 +278,7 @@ public class SearchingForTestsTask extends SearchForTestsTask { } } - protected void fillTestObjects(final Map> classes) + protected void fillTestObjects(final Map>> classes) throws CantRunException { final TestData data = myConfig.getPersistantData(); final PsiManager psiManager = PsiManager.getInstance(myProject); @@ -322,7 +361,7 @@ public class SearchingForTestsTask extends SearchForTestsTask { .getAllTestClasses(new TestClassFilter(data.getScope().getSourceScope(myConfig).getGlobalSearchScope(), myProject, true, true), false); if (testClasses != null) { for (PsiClass c : testClasses) { - classes.put(c, new HashSet()); + classes.put(c, new LinkedHashMap>()); } } } @@ -401,7 +440,7 @@ public class SearchingForTestsTask extends SearchForTestsTask { } } - private void collectTestMethods(Map> classes, final PsiClass psiClass, final String methodName) { + private void collectTestMethods(Map>> classes, final PsiClass psiClass, final String methodName) { final PsiMethod[] methods = ApplicationManager.getApplication().runReadAction( new Computable() { public PsiMethod[] compute() { @@ -410,12 +449,14 @@ public class SearchingForTestsTask extends SearchForTestsTask { } ); calculateDependencies(methods, classes, psiClass); - Collection psiMethods = classes.get(psiClass); + Map> psiMethods = classes.get(psiClass); if (psiMethods == null) { - psiMethods = new LinkedHashSet(); + psiMethods = new LinkedHashMap>(); classes.put(psiClass, psiMethods); } - ContainerUtil.addAll(psiMethods, methods); + for (PsiMethod method : methods) { + psiMethods.put(method, Collections.emptyList()); + } } private Map buildTestParameters() { @@ -452,13 +493,13 @@ public class SearchingForTestsTask extends SearchForTestsTask { } private void calculateDependencies(PsiMethod[] methods, - final Map> results, + final Map>> results, @Nullable final PsiClass... classes) { calculateDependencies(methods, results, new LinkedHashSet(), classes); } private void calculateDependencies(final PsiMethod[] methods, - final Map> results, + final Map>> results, final Set alreadyMarkedToBeChecked, @Nullable final PsiClass... classes) { if (classes != null && classes.length > 0) { @@ -488,7 +529,7 @@ public class SearchingForTestsTask extends SearchForTestsTask { if (methods == null) { for (PsiClass c : classes) { - results.put(c, new LinkedHashSet()); + results.put(c, new LinkedHashMap>()); } } else { for (PsiMember psiMember : membersToCheckNow) { @@ -506,7 +547,7 @@ public class SearchingForTestsTask extends SearchForTestsTask { } } - private static void collectDependsOnMethods(final Map> results, + private static void collectDependsOnMethods(final Map>> results, final Set alreadyMarkedToBeChecked, final Set membersToCheckNow, final PsiMethod[] methods, @@ -557,7 +598,7 @@ public class SearchingForTestsTask extends SearchForTestsTask { PsiClass containingClass, Set alreadyMarkedToBeChecked, Set membersToCheckNow, - Map> results) { + Map>> results) { final PsiMethod[] psiMethods = containingClass.findMethodsByName(methodName, true); for (PsiMethod method : psiMethods) { if (AnnotationUtil.isAnnotated(method, TestNGUtil.TEST_ANNOTATION_FQN, false) && @@ -569,19 +610,19 @@ public class SearchingForTestsTask extends SearchForTestsTask { private static boolean appendMember(final PsiMember psiMember, final Set underConsideration, - final Map> results) { + final Map>> results) { boolean result = false; final PsiClass psiClass = psiMember instanceof PsiClass ? ((PsiClass)psiMember) : psiMember.getContainingClass(); - Collection psiMethods = results.get(psiClass); + Map> psiMethods = results.get(psiClass); if (psiMethods == null) { - psiMethods = new LinkedHashSet(); + psiMethods = new LinkedHashMap>(); results.put(psiClass, psiMethods); if (psiMember instanceof PsiClass) { result = underConsideration.add(psiMember); } } if (psiMember instanceof PsiMethod) { - final boolean add = psiMethods.add((PsiMethod)psiMember); + final boolean add = psiMethods.put((PsiMethod)psiMember, Collections.emptyList()) != null; if (add) { return underConsideration.add(psiMember); } diff --git a/plugins/testng/src/com/theoryinpractice/testng/configuration/TestNGConfigurationProducer.java b/plugins/testng/src/com/theoryinpractice/testng/configuration/TestNGConfigurationProducer.java index ce777ae383bb..5203f6c09819 100644 --- a/plugins/testng/src/com/theoryinpractice/testng/configuration/TestNGConfigurationProducer.java +++ b/plugins/testng/src/com/theoryinpractice/testng/configuration/TestNGConfigurationProducer.java @@ -20,21 +20,20 @@ */ package com.theoryinpractice.testng.configuration; -import com.intellij.execution.*; +import com.intellij.execution.JavaExecutionUtil; +import com.intellij.execution.Location; +import com.intellij.execution.RunManager; +import com.intellij.execution.RunnerAndConfigurationSettings; import com.intellij.execution.actions.ConfigurationContext; import com.intellij.execution.actions.RunConfigurationProducer; import com.intellij.execution.configurations.RunConfiguration; -import com.intellij.execution.impl.RunManagerImpl; -import com.intellij.execution.junit.JUnitUtil; import com.intellij.execution.junit.JavaRunConfigurationProducerBase; -import com.intellij.execution.junit.JavaRuntimeConfigurationProducerBase; +import com.intellij.execution.junit2.PsiMemberParameterizedLocation; import com.intellij.openapi.module.Module; import com.intellij.openapi.util.Comparing; -import com.intellij.psi.*; +import com.intellij.psi.PsiElement; import com.theoryinpractice.testng.model.TestData; -import org.jetbrains.annotations.NotNull; - -import java.util.List; +import org.testng.IDEATestNGRemoteListener; public abstract class TestNGConfigurationProducer extends JavaRunConfigurationProducerBase implements Cloneable { @@ -48,13 +47,17 @@ public abstract class TestNGConfigurationProducer extends JavaRunConfigurationPr return false; } final RunConfiguration predefinedConfiguration = context.getOriginalConfiguration(TestNGConfigurationType.getInstance()); - Location location = JavaExecutionUtil.stepIntoSingleClass(context.getLocation()); + final Location contextLocation = context.getLocation(); + Location location = JavaExecutionUtil.stepIntoSingleClass(contextLocation); final PsiElement element = location.getPsiElement(); RunnerAndConfigurationSettings template = RunManager.getInstance(location.getProject()).getConfigurationTemplate(getConfigurationFactory()); final Module predefinedModule = ((TestNGConfiguration)template.getConfiguration()).getConfigurationModule().getModule(); final String vmParameters = predefinedConfiguration instanceof TestNGConfiguration ? ((TestNGConfiguration)predefinedConfiguration).getVMParameters() : null; if (vmParameters != null && !Comparing.strEqual(vmParameters, testNGConfiguration.getVMParameters())) return false; + String paramSetName = contextLocation instanceof PsiMemberParameterizedLocation + ? getInvocationNumber(((PsiMemberParameterizedLocation)contextLocation).getParamSetName()) : null; + if (paramSetName != null && !Comparing.strEqual(paramSetName, testNGConfiguration.getProgramParameters())) return false; TestData testobject = testNGConfiguration.getPersistantData(); if (testobject != null) { if (testobject.isConfiguredByElement(element)) { @@ -65,4 +68,15 @@ public abstract class TestNGConfigurationProducer extends JavaRunConfigurationPr } return false; } + + public static String getInvocationNumber(String str) { + final int indexOf = str.indexOf(IDEATestNGRemoteListener.INVOCATION_NUMBER); + if (indexOf > 0) { + final int lastIdx = str.indexOf(")", indexOf); + if (lastIdx > 0) { + return str.substring(indexOf + IDEATestNGRemoteListener.INVOCATION_NUMBER.length(), lastIdx); + } + } + return null; + } } \ No newline at end of file diff --git a/plugins/testng/src/com/theoryinpractice/testng/configuration/TestNGInClassConfigurationProducer.java b/plugins/testng/src/com/theoryinpractice/testng/configuration/TestNGInClassConfigurationProducer.java index fdc1387f3c57..497967e352cf 100644 --- a/plugins/testng/src/com/theoryinpractice/testng/configuration/TestNGInClassConfigurationProducer.java +++ b/plugins/testng/src/com/theoryinpractice/testng/configuration/TestNGInClassConfigurationProducer.java @@ -20,12 +20,14 @@ */ package com.theoryinpractice.testng.configuration; +import com.intellij.execution.Location; import com.intellij.execution.PsiLocation; import com.intellij.execution.RunnerAndConfigurationSettings; import com.intellij.execution.actions.ConfigurationContext; import com.intellij.execution.actions.ConfigurationFromContext; import com.intellij.execution.actions.RunConfigurationProducer; import com.intellij.execution.junit.InheritorChooser; +import com.intellij.execution.junit2.PsiMemberParameterizedLocation; import com.intellij.execution.junit2.info.MethodLocation; import com.intellij.openapi.module.Module; import com.intellij.openapi.project.Project; @@ -100,6 +102,14 @@ public class TestNGInClassConfigurationProducer extends TestNGConfigurationProdu return false; } + final Location contextLocation = context.getLocation(); + if (contextLocation instanceof PsiMemberParameterizedLocation) { + final String paramSetName = getInvocationNumber(((PsiMemberParameterizedLocation)contextLocation).getParamSetName()); + if (paramSetName != null) { + configuration.setProgramParameters(paramSetName); + } + } + PsiClass psiClass = null; PsiElement element = context.getPsiLocation(); while (element != null) { diff --git a/plugins/testng/src/com/theoryinpractice/testng/configuration/TestNGRunnableState.java b/plugins/testng/src/com/theoryinpractice/testng/configuration/TestNGRunnableState.java index 39774fca9065..14591ab39a4b 100644 --- a/plugins/testng/src/com/theoryinpractice/testng/configuration/TestNGRunnableState.java +++ b/plugins/testng/src/com/theoryinpractice/testng/configuration/TestNGRunnableState.java @@ -244,7 +244,9 @@ public class TestNGRunnableState extends JavaTestFrameworkRunnableState> classes) throws CantRunException { - final HashMap> fullClassList = ContainerUtil.newHashMap(); + protected void fillTestObjects(final Map>> classes) throws CantRunException { + final HashMap>> fullClassList = ContainerUtil.newHashMap(); super.fillTestObjects(fullClassList); for (final PsiClass aClass : fullClassList.keySet()) { if (!ApplicationManager.getApplication().runReadAction(new Computable() { @@ -78,7 +77,7 @@ public class RerunFailedTestsAction extends JavaRerunFailedTestsAction { } } - private void includeFailedTestWithDependencies(Map> classes, + private void includeFailedTestWithDependencies(Map>> classes, GlobalSearchScope scope, Project project, AbstractTestProxy proxy) { @@ -95,12 +94,23 @@ public class RerunFailedTestsAction extends JavaRerunFailedTestsAction { psiClass = (PsiClass)elt; } } - Collection psiMethods = classes.get(psiClass); + Map> psiMethods = classes.get(psiClass); if (psiMethods == null) { - psiMethods = new ArrayList(); + psiMethods = new LinkedHashMap>(); classes.put(psiClass, psiMethods); } - psiMethods.add(psiMethod); + List strings = psiMethods.get(psiMethod); + if (strings == null) { + strings = new ArrayList(); + } + if (location instanceof PsiMemberParameterizedLocation) { + final String paramSetName = ((PsiMemberParameterizedLocation)location).getParamSetName(); + final String invocationNumber = TestNGConfigurationProducer.getInvocationNumber(paramSetName); + if (invocationNumber != null) { + strings.add(invocationNumber); + } + } + psiMethods.put(psiMethod, strings); } } } diff --git a/plugins/testng/src/com/theoryinpractice/testng/util/TestNGUtil.java b/plugins/testng/src/com/theoryinpractice/testng/util/TestNGUtil.java index 565af249a27f..38fd89d5e695 100644 --- a/plugins/testng/src/com/theoryinpractice/testng/util/TestNGUtil.java +++ b/plugins/testng/src/com/theoryinpractice/testng/util/TestNGUtil.java @@ -25,7 +25,6 @@ import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.ModuleRootModificationUtil; import com.intellij.openapi.ui.Messages; -import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.io.JarUtil; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VfsUtil; @@ -91,6 +90,20 @@ public class TestNGUtil { AfterTest.class.getName() }; + @SuppressWarnings("deprecation") public static final String[] CONFIG_ANNOTATIONS_FQN_NO_TEST_LEVEL = { + Configuration.class.getName(), + Factory.class.getName(), + ObjectFactory.class.getName(), + BeforeClass.class.getName(), + BeforeGroups.class.getName(), + BeforeSuite.class.getName(), + BeforeTest.class.getName(), + AfterClass.class.getName(), + AfterGroups.class.getName(), + AfterSuite.class.getName(), + AfterTest.class.getName() + }; + @NonNls private static final String[] CONFIG_JAVADOC_TAGS = { "testng.configuration", @@ -113,19 +126,24 @@ public class TestNGUtil { private static final String SUITE_TAG_NAME = "suite"; public static boolean hasConfig(PsiModifierListOwner element) { + return hasConfig(element, CONFIG_ANNOTATIONS_FQN); + } + + public static boolean hasConfig(PsiModifierListOwner element, + String[] configAnnotationsFqn) { if (element instanceof PsiClass) { for (PsiMethod method : ((PsiClass)element).getAllMethods()) { - if (isConfigMethod(method)) return true; + if (isConfigMethod(method, configAnnotationsFqn)) return true; } } else { if (!(element instanceof PsiMethod)) return false; - return isConfigMethod((PsiMethod)element); + return isConfigMethod((PsiMethod)element, configAnnotationsFqn); } return false; } - private static boolean isConfigMethod(PsiMethod method) { - for (String fqn : CONFIG_ANNOTATIONS_FQN) { + private static boolean isConfigMethod(PsiMethod method, String[] configAnnotationsFqn) { + for (String fqn : configAnnotationsFqn) { if (AnnotationUtil.isAnnotated(method, fqn, false)) return true; } @@ -191,21 +209,29 @@ public class TestNGUtil { if (element instanceof PsiClass) { PsiClass psiClass = (PsiClass) element; for (PsiMethod method : psiClass.getAllMethods()) { - if (AnnotationUtil.isAnnotated(method, TEST_ANNOTATION_FQN, false, true)) return true; + PsiAnnotation annotation = AnnotationUtil.findAnnotation(method, true, TEST_ANNOTATION_FQN); + if (annotation != null) { + if (checkDisabled) { + if (isDisabled(annotation)) continue; + } + return true; + } if (AnnotationUtil.isAnnotated(method, FACTORY_ANNOTATION_FQN, false, true)) return true; if (hasTestJavaDoc(method, checkJavadoc)) return true; } - return AnnotationUtil.isAnnotated(element, TEST_ANNOTATION_FQN, true, true); + return false; } else if (element instanceof PsiMethod) { //if it's a method, we check if the class it's in has a global @Test annotation PsiClass psiClass = ((PsiMethod)element).getContainingClass(); if (psiClass != null) { - if (AnnotationUtil.isAnnotated(psiClass, TEST_ANNOTATION_FQN, true, true)) { + final PsiAnnotation annotation = AnnotationUtil.findAnnotation(psiClass, true, TEST_ANNOTATION_FQN); + if (annotation != null) { + if (checkDisabled && isDisabled(annotation)) return false; //even if it has a global test, we ignore private methods boolean isPrivate = element.hasModifierProperty(PsiModifier.PRIVATE); return !isPrivate && !element.hasModifierProperty(PsiModifier.STATIC) && !hasConfig(element); } - if (hasTestJavaDoc(psiClass, checkJavadoc)) return true; + else if (hasTestJavaDoc(psiClass, checkJavadoc)) return true; } } return false; diff --git a/plugins/testng_rt/src/org/testng/IDEARemoteTestNG.java b/plugins/testng_rt/src/org/testng/IDEARemoteTestNG.java index baa732ad087b..a0a7d064eedb 100644 --- a/plugins/testng_rt/src/org/testng/IDEARemoteTestNG.java +++ b/plugins/testng_rt/src/org/testng/IDEARemoteTestNG.java @@ -23,11 +23,17 @@ import org.testng.xml.XmlInclude; import org.testng.xml.XmlSuite; import org.testng.xml.XmlTest; +import java.util.Arrays; import java.util.HashMap; import java.util.List; public class IDEARemoteTestNG extends TestNG { + private final String myParam; + public IDEARemoteTestNG(String param) { + myParam = param; + } + private static void calculateAllSuites(List suites, List outSuites) { for (XmlSuite s : suites) { outSuites.add(s); @@ -51,9 +57,12 @@ public class IDEARemoteTestNG extends TestNG { for (XmlTest test : tests) { for (XmlClass aClass : test.getXmlClasses()) { System.out.println("##teamcity[suiteTreeStarted name=\'" + aClass.getName() + "\' locationHint=\'java:suite://" + aClass.getName() + "\']"); - for (XmlInclude include : aClass.getIncludedMethods()) { - System.out.println("##teamcity[suiteTreeNode name=\'" + include.getName() + "\']"); + if (myParam != null) { + for (XmlInclude include : aClass.getIncludedMethods()) { + aClass.setIncludedMethods(Arrays.asList(new XmlInclude(include.getName(), Arrays.asList(Integer.parseInt(myParam)), 0))); + } } + System.out.println("##teamcity[suiteTreeEnded name=\'" + aClass.getName() + "\']"); } testCount += test.getClasses().size(); diff --git a/plugins/testng_rt/src/org/testng/IDEATestNGRemoteListener.java b/plugins/testng_rt/src/org/testng/IDEATestNGRemoteListener.java index 75bd8a157d88..303ae5bc6125 100644 --- a/plugins/testng_rt/src/org/testng/IDEATestNGRemoteListener.java +++ b/plugins/testng_rt/src/org/testng/IDEATestNGRemoteListener.java @@ -7,6 +7,7 @@ import org.testng.internal.IResultListener; import java.io.PrintWriter; import java.io.StringWriter; import java.util.HashMap; +import java.util.List; import java.util.Map; /** @@ -15,18 +16,30 @@ import java.util.Map; */ public class IDEATestNGRemoteListener implements ISuiteListener, IResultListener{ + public static final String INVOCATION_NUMBER = "invocation number: "; private String myCurrentClassName; + private String myMethodName; + private int myInvocationCount = 0; - public void onConfigurationSuccess(ITestResult itr) { - //won't be called + public void onConfigurationSuccess(ITestResult result) { + final String className = result.getTestClass().getName(); + System.out.println("##teamcity[testSuiteStarted name=\'" + className + "\']"); + final String methodName = result.getMethod().getMethodName(); + System.out.println("##teamcity[testStarted name=\'" + methodName + "\']"); + onTestSuccess(result); + System.out.println("\n##teamcity[testSuiteFinished name=\'" + className + "\']"); } - public void onConfigurationFailure(ITestResult itr) { - //won't be called + public void onConfigurationFailure(ITestResult result) { + final String className = result.getTestClass().getName(); + System.out.println("##teamcity[testSuiteStarted name=\'" + className + "\']"); + final String methodName = result.getMethod().getMethodName(); + System.out.println("##teamcity[testStarted name=\'" + methodName + "\']"); + onTestFailure(result); + System.out.println("\n##teamcity[testSuiteFinished name=\'" + className + "\']"); } public void onConfigurationSkip(ITestResult itr) { - //won't be called } public void onStart(ISuite suite) { @@ -46,14 +59,37 @@ public class IDEATestNGRemoteListener implements ISuiteListener, IResultListener } System.out.println("##teamcity[testSuiteStarted name =\'" + className + "\']"); myCurrentClassName = className; + myInvocationCount = 0; } - final String methodName = result.getMethod().getMethodName(); + String methodName = getMethodName(result, false); System.out.println("##teamcity[testStarted name=\'" + methodName + "\' locationHint=\'java:test://" + className + "." + methodName + "\']"); } + private String getMethodName(ITestResult result) { + return getMethodName(result, true); + } + + private String getMethodName(ITestResult result, boolean changeCount) { + String methodName = result.getMethod().getMethodName(); + final Object[] parameters = result.getParameters(); + if (!methodName.equals(myMethodName)) { + myInvocationCount = 0; + myMethodName = methodName; + } + if (parameters.length > 0) { + final List invocationNumbers = result.getMethod().getInvocationNumbers(); + methodName += "[" + parameters[0].toString() + " (" + INVOCATION_NUMBER + + (invocationNumbers.isEmpty() ? myInvocationCount : invocationNumbers.get(myInvocationCount)) + ")" + "]"; + if (changeCount) { + myInvocationCount++; + } + } + return methodName; + } + public void onTestSuccess(ITestResult result) { - System.out.println("##teamcity[testFinished name=\'" + result.getMethod().getMethodName() + "\']"); + System.out.println("\n##teamcity[testFinished name=\'" + getMethodName(result) + "\']"); } public String getTrace(Throwable tr) { @@ -68,17 +104,18 @@ public class IDEATestNGRemoteListener implements ISuiteListener, IResultListener final Throwable ex = result.getThrowable(); final String trace = getTrace(ex); final Map attrs = new HashMap(); - attrs.put("name", result.getMethod().getMethodName()); + final String methodName = getMethodName(result); + attrs.put("name", methodName); final String failureMessage = ex.getMessage(); attrs.put("message", failureMessage != null ? failureMessage : ""); attrs.put("details", trace); attrs.put("error", "true"); System.out.println(ServiceMessage.asString(ServiceMessageTypes.TEST_FAILED, attrs)); - System.out.println("##teamcity[testFinished name=\'" + result.getMethod().getMethodName() + "\']"); + System.out.println("\n##teamcity[testFinished name=\'" + methodName + "\']"); } public void onTestSkipped(ITestResult result) { - System.out.println("##teamcity[testFinished name=\'" + result.getMethod().getMethodName() + "\']"); + System.out.println("\n##teamcity[testFinished name=\'" + getMethodName(result) + "\']"); } public void onTestFailedButWithinSuccessPercentage(ITestResult result) { diff --git a/plugins/testng_rt/src/org/testng/RemoteTestNGStarter.java b/plugins/testng_rt/src/org/testng/RemoteTestNGStarter.java index 3aa287ec66a1..8068d099ee99 100644 --- a/plugins/testng_rt/src/org/testng/RemoteTestNGStarter.java +++ b/plugins/testng_rt/src/org/testng/RemoteTestNGStarter.java @@ -38,10 +38,14 @@ public class RemoteTestNGStarter { private static final String SOCKET = "-socket"; public static void main(String[] args) throws Exception { int i = 0; + String param = null; Vector resultArgs = new Vector(); for (; i < args.length; i++) { String arg = args[i]; - if (arg.startsWith(SOCKET)) { + if (arg.startsWith("@name")) { + param = arg.substring(5); + continue; + } else if (arg.startsWith(SOCKET)) { final int port = Integer.parseInt(arg.substring(SOCKET.length())); try { final Socket socket = new Socket(InetAddress.getByName("127.0.0.1"), port); //start collecting tests @@ -95,7 +99,7 @@ public class RemoteTestNGStarter { } if (SM_RUNNER) { - final IDEARemoteTestNG testNG = new IDEARemoteTestNG(); + final IDEARemoteTestNG testNG = new IDEARemoteTestNG(param); CommandLineArgs cla = new CommandLineArgs(); RemoteArgs ra = new RemoteArgs(); new JCommander(Arrays.asList(cla, ra), (String[])resultArgs.toArray(new String[resultArgs.size()])); diff --git a/python/edu/build/pycharm_edu_build.gant b/python/edu/build/pycharm_edu_build.gant index 75e71cb08843..b14d74790f06 100644 --- a/python/edu/build/pycharm_edu_build.gant +++ b/python/edu/build/pycharm_edu_build.gant @@ -27,8 +27,7 @@ setProperty("ch", "$home/community") setProperty("pythonCommunityHome", "$ch/python") setProperty("pythonEduHome", "$ch/python/edu") requireProperty("jdk_bundled_mac", "1.7") -def jdk_bundled_version = p("jdk_bundled_mac") == "1.8" ? "jdk8_mac_redist.tar" : "jdk_mac_redist.tar" -ant.copy(file: "${home}/build/jdk/${jdk_bundled_version}", tofile: "${home}/build/jdk/jdk_mac_redist_for_${buildNumber}.tar") +getBundledJDK(new File("${home}/build/jdk/mac"), "jdk" + p("jdk_bundled_mac"), ".tar", "${home}/build/jdk/jdk_mac_redist_for_${buildNumber}.tar") // load ApplicationInfo.xml properties ant.xmlproperty(file: "$pythonEduHome/resources/idea/PyCharmEduApplicationInfo.xml", collapseAttributes: "true") diff --git a/python/helpers/pycharm/_bdd_utils.py b/python/helpers/pycharm/_bdd_utils.py index a71cc37e346d..4d4116197ff4 100644 --- a/python/helpers/pycharm/_bdd_utils.py +++ b/python/helpers/pycharm/_bdd_utils.py @@ -8,7 +8,7 @@ You may also need "get_what_to_run_by_env" that gets folder (current or passed a import os import time import abc - +import sys import tcmessages @@ -139,11 +139,11 @@ class BddRunner(object): :param name: test name :type name str :param message: failure message - :type message str + :type message basestring :param details: failure details (probably stacktrace) :type details str """ - self.tc_messages.testFailed(name, message=message, details=details) + self.tc_messages.testFailed(name, message=VersionAgnosticUtils().to_unicode(message), details=details) self.__last_test_name = None def _test_passed(self, name, duration=None): @@ -229,3 +229,45 @@ class BddRunner(object): pass +class VersionAgnosticUtils(object): + """ + "six" emulator: this class fabrics appropriate tool to use regardless python version. + Use it to write code that works both on py2 and py3 + """ + + @staticmethod + def __new__(cls, *more): + """ + Fabrics Py2 or Py3 instance based on py version + """ + real_class = _Py3KUtils if sys.version_info >= (3, 0) else _Py2Utils + return super(cls, real_class).__new__(real_class, *more) + + def to_unicode(self, obj): + """ + + :param obj: string to convert to unicode + :return: unicode string + """ + + raise NotImplementedError() + + + +class _Py2Utils(VersionAgnosticUtils): + """ + Util for Py2 + """ + def to_unicode(self, obj): + if isinstance(obj, unicode): + return obj + return unicode(obj.decode("utf-8")) + + + +class _Py3KUtils(VersionAgnosticUtils): + """ + Util for Py3 + """ + def to_unicode(self, obj): + return str(obj) \ No newline at end of file diff --git a/python/helpers/pycharm/behave_runner.py b/python/helpers/pycharm/behave_runner.py index f35c7e242021..7072e47e615b 100644 --- a/python/helpers/pycharm/behave_runner.py +++ b/python/helpers/pycharm/behave_runner.py @@ -136,9 +136,10 @@ class _BehaveRunner(_bdd_utils.BddRunner): :param element feature/suite/step """ element.location.file = element.location.filename # To preserve _bdd_utils contract + utils = _bdd_utils.VersionAgnosticUtils() if isinstance(element, Step): # Process step - step_name = "{0} {1}".format(element.keyword, element.name) + step_name = u"{0} {1}".format(utils.to_unicode(element.keyword), utils.to_unicode(element.name)) if is_started: self._test_started(step_name, element.location) elif element.status == 'passed': @@ -148,9 +149,10 @@ class _BehaveRunner(_bdd_utils.BddRunner): trace = traceback.format_exc() except Exception: trace = "".join(traceback.format_tb(element.exc_traceback)) - if trace in str(element.error_message): - trace = None # No reason to duplicate output (see PY-13647) - self._test_failed(step_name, element.error_message, trace) + error_message = utils.to_unicode(element.error_message) + if "Traceback " in error_message: + error_message = "" # No reason to duplicate output (see PY-13647) + self._test_failed(step_name, error_message, trace) elif element.status == 'undefined': self._test_undefined(step_name, element.location) else: diff --git a/python/helpers/pycharm/lettuce_runner.py b/python/helpers/pycharm/lettuce_runner.py index b61f7af9a332..2c97672b69cb 100644 --- a/python/helpers/pycharm/lettuce_runner.py +++ b/python/helpers/pycharm/lettuce_runner.py @@ -103,7 +103,7 @@ class _LettuceRunner(_bdd_utils.BddRunner): elif step.failed: reason = step.why assert isinstance(reason, ReasonToFail), reason - self._test_failed(test_name, message=reason.exception, details=reason.traceback) + self._test_failed(test_name, message=reason.exception.message, details=reason.traceback) elif step.has_definition: self._test_skipped(test_name, "In lettuce, we do know the reason", step.described_at) else: diff --git a/python/psi-api/src/com/jetbrains/python/psi/PySequenceExpression.java b/python/psi-api/src/com/jetbrains/python/psi/PySequenceExpression.java index 030c5cf560ff..0a070be196ab 100644 --- a/python/psi-api/src/com/jetbrains/python/psi/PySequenceExpression.java +++ b/python/psi-api/src/com/jetbrains/python/psi/PySequenceExpression.java @@ -23,4 +23,12 @@ import org.jetbrains.annotations.NotNull; public interface PySequenceExpression extends PyExpression{ @NotNull PyExpression[] getElements(); + + /** + * Calling {@link #getElements()} may take too much time in case of large literals with thousands of elements. If you only need to + * know whether collection is empty, use this method instead. + * + * @return true if sequence expression contains no elements + */ + boolean isEmpty(); } diff --git a/python/src/com/jetbrains/commandInterface/console/CommandConsole.java b/python/src/com/jetbrains/commandInterface/console/CommandConsole.java index 819293b9d2bd..aef12c255109 100644 --- a/python/src/com/jetbrains/commandInterface/console/CommandConsole.java +++ b/python/src/com/jetbrains/commandInterface/console/CommandConsole.java @@ -134,6 +134,7 @@ final class CommandConsole extends LanguageConsoleImpl implements Consumer "); - ApplicationManager.getApplication().invokeAndWait(new Runnable() { @Override public void run() { notifyStateChangeListeners(); + configureLeftBorder(true, getConsoleEditor(), getHistoryViewer()); setLanguage(CommandLineLanguage.INSTANCE); final CommandLineFile file = PyUtil.as(getFile(), CommandLineFile.class); resetConsumer(null); @@ -190,11 +190,11 @@ final class CommandConsole extends LanguageConsoleImpl implements Consumer mySubBlocks = null; private Alignment myChildAlignment; - private final boolean myEmptyList; + private final boolean myEmptySequence; public PyBlock(final PyBlock parent, final ASTNode node, @@ -96,7 +97,7 @@ public class PyBlock implements ASTBlock { myNode = node; myWrap = wrap; myContext = context; - myEmptyList = node.getPsi() instanceof PySequenceExpression && ((PySequenceExpression)node.getPsi()).getElements().length == 0; + myEmptySequence = isEmptySequence(node); } @NotNull @@ -133,7 +134,7 @@ public class PyBlock implements ASTBlock { final IElementType childType = child.getElementType(); - if (child.getTextRange().getLength() == 0) continue; + if (child.getTextRange().isEmpty()) continue; if (childType == TokenType.WHITE_SPACE) { continue; @@ -160,7 +161,7 @@ public class PyBlock implements ASTBlock { while (p != null) { final ASTNode pNode = p.getNode(); if (ourListElementTypes.contains(pNode.getElementType())) { - if (needListAlignment(child) && !myEmptyList) { + if (needListAlignment(child) && !myEmptySequence) { childAlignment = p.getChildAlignment(); break; @@ -198,7 +199,7 @@ public class PyBlock implements ASTBlock { !isSliceOperand(child) /*&& !isSubscriptionOperand(child)*/) { wrap = Wrap.createWrap(WrapType.NORMAL, true); } - if (needListAlignment(child) && !myEmptyList) { + if (needListAlignment(child) && !myEmptySequence) { childAlignment = getAlignmentForChildren(); } if (childType == PyTokenTypes.END_OF_LINE_COMMENT) { @@ -349,6 +350,10 @@ public class PyBlock implements ASTBlock { return new PyBlock(this, child, childAlignment, childIndent, wrap, myContext); } + private static boolean isEmptySequence(@NotNull ASTNode node) { + return node.getPsi() instanceof PySequenceExpression && ((PySequenceExpression)node.getPsi()).isEmpty(); + } + private boolean argumentMayHaveSameIndentAsFollowingStatementList() { // This check is supposed to prevent PEP8's error: Continuation line with the same indent as next logical line final PsiElement header = getControlStatementHeader(myNode); @@ -499,11 +504,8 @@ public class PyBlock implements ASTBlock { if (PyTokenTypes.OPEN_BRACES.contains(childType)) { return false; } - if (PyTokenTypes.OPEN_BRACES.contains(firstGrandchildType)) { - final PsiElement psi = child.getPsi(); - if (psi instanceof PySequenceExpression && ((PySequenceExpression)psi).getElements().length == 0) { - return false; - } + if (PyTokenTypes.OPEN_BRACES.contains(firstGrandchildType) && isEmptySequence(child)) { + return false; } if (PyTokenTypes.CLOSE_BRACES.contains(childType)) { final ASTNode prevNonSpace = findPrevNonSpaceNode(child); @@ -757,12 +759,8 @@ public class PyBlock implements ASTBlock { return null; } if (myNode.getPsi() instanceof PyDictLiteralExpression) { - final PyKeyValueExpression[] elements = ((PyDictLiteralExpression)myNode.getPsi()).getElements(); - if (elements.length == 0) { - return null; - } - final PyKeyValueExpression last = elements[elements.length - 1]; - if (last.getValue() == null) { // incomplete + final PyKeyValueExpression lastElement = ArrayUtil.getLastElement(((PyDictLiteralExpression)myNode.getPsi()).getElements()); + if (lastElement == null || lastElement.getValue() == null /* incomplete */) { return null; } } diff --git a/python/src/com/jetbrains/python/inspections/PyDictDuplicateKeysInspection.java b/python/src/com/jetbrains/python/inspections/PyDictDuplicateKeysInspection.java index 571e4f6d5fcf..bb913ad16b56 100644 --- a/python/src/com/jetbrains/python/inspections/PyDictDuplicateKeysInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyDictDuplicateKeysInspection.java @@ -58,9 +58,10 @@ public class PyDictDuplicateKeysInspection extends PyInspection { @Override public void visitPyDictLiteralExpression(PyDictLiteralExpression node) { - if (node.getElements().length != 0){ + final PyKeyValueExpression[] elements = node.getElements(); + if (elements.length != 0){ final Map map = new HashMap(); - for (PyExpression exp : node.getElements()) { + for (PyExpression exp : elements) { final PyExpression key = ((PyKeyValueExpression)exp).getKey(); if (key instanceof PyNumericLiteralExpression || key instanceof PyStringLiteralExpression || key instanceof PyReferenceExpression) { diff --git a/python/src/com/jetbrains/python/inspections/quickfix/PyReplaceTupleWithListQuickFix.java b/python/src/com/jetbrains/python/inspections/quickfix/PyReplaceTupleWithListQuickFix.java index e786dcec1a31..d480d339bb36 100644 --- a/python/src/com/jetbrains/python/inspections/quickfix/PyReplaceTupleWithListQuickFix.java +++ b/python/src/com/jetbrains/python/inspections/quickfix/PyReplaceTupleWithListQuickFix.java @@ -59,7 +59,7 @@ public class PyReplaceTupleWithListQuickFix implements LocalQuickFix { } private static void replaceWithListLiteral(PsiElement element, PyTupleExpression expression) { - final String expressionText = expression.getElements().length == 0 ? "" :expression.getText(); + final String expressionText = expression.isEmpty() ? "" :expression.getText(); final PyExpression literal = PyElementGenerator.getInstance(element.getProject()). createExpressionFromText(LanguageLevel.forElement(element), "[" + expressionText + "]"); diff --git a/python/src/com/jetbrains/python/psi/impl/PyBaseElementImpl.java b/python/src/com/jetbrains/python/psi/impl/PyBaseElementImpl.java index 72a2a64528b3..cb155cebd8f1 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyBaseElementImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyBaseElementImpl.java @@ -110,6 +110,13 @@ public class PyBaseElementImpl extends StubBasedPsiElemen return (T)node.getPsi(); } + @Nullable + protected T childToPsi(@NotNull TokenSet elTypes) { + final ASTNode node = getNode().findChildByType(elTypes); + //noinspection unchecked + return node != null ? (T)node.getPsi() : null; + } + @NotNull protected T childToPsiNotNull(TokenSet filterSet, int index) { final PyElement child = childToPsi(filterSet, index); diff --git a/python/src/com/jetbrains/python/psi/impl/PySequenceExpressionImpl.java b/python/src/com/jetbrains/python/psi/impl/PySequenceExpressionImpl.java index 1eb6ea0f4cb6..b76c73e24474 100644 --- a/python/src/com/jetbrains/python/psi/impl/PySequenceExpressionImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PySequenceExpressionImpl.java @@ -4,12 +4,13 @@ import com.intellij.lang.ASTNode; import com.intellij.util.ArrayUtil; import com.jetbrains.python.PythonDialectsTokenSetProvider; import com.jetbrains.python.psi.PyExpression; +import com.jetbrains.python.psi.PySequenceExpression; import org.jetbrains.annotations.NotNull; /** * @author Mikhail Golubev */ -public class PySequenceExpressionImpl extends PyElementImpl { +public abstract class PySequenceExpressionImpl extends PyElementImpl implements PySequenceExpression { public PySequenceExpressionImpl(ASTNode astNode) { super(astNode); } @@ -26,4 +27,9 @@ public class PySequenceExpressionImpl extends PyElementImpl { public PyExpression[] getElements() { return childrenToPsi(PythonDialectsTokenSetProvider.INSTANCE.getExpressionTokens(), PyExpression.EMPTY_ARRAY); } + + @Override + public boolean isEmpty() { + return childToPsi(PythonDialectsTokenSetProvider.INSTANCE.getExpressionTokens()) == null; + } } diff --git a/python/src/com/jetbrains/python/validation/AssignTargetAnnotator.java b/python/src/com/jetbrains/python/validation/AssignTargetAnnotator.java index a648f37b8ec1..a55011e37693 100644 --- a/python/src/com/jetbrains/python/validation/AssignTargetAnnotator.java +++ b/python/src/com/jetbrains/python/validation/AssignTargetAnnotator.java @@ -130,7 +130,7 @@ public class AssignTargetAnnotator extends PyAnnotator { @Override public void visitPyTupleExpression(final PyTupleExpression node) { - if (node.getElements().length == 0) { + if (node.isEmpty()) { getHolder().createErrorAnnotation(node, message("ANN.cant.assign.to.parens")); } else if (myOp == Operation.AugAssign) { @@ -153,7 +153,7 @@ public class AssignTargetAnnotator extends PyAnnotator { @Override public void visitPyListLiteralExpression(final PyListLiteralExpression node) { - if (node.getElements().length == 0) { + if (node.isEmpty()) { getHolder().createErrorAnnotation(node, message("ANN.cant.assign.to.brackets")); } else if (myOp == Operation.AugAssign) { diff --git a/resources/src/DebuggerSettings.xml b/resources/src/DebuggerSettings.xml index e1371944741b..02fb60d7ddab 100644 --- a/resources/src/DebuggerSettings.xml +++ b/resources/src/DebuggerSettings.xml @@ -76,4 +76,16 @@

test
\ No newline at end of file diff --git a/spellchecker/testSrc/com/intellij/spellchecker/inspection/XmlWithMistakesInspectionTest.java b/spellchecker/testSrc/com/intellij/spellchecker/inspection/XmlWithMistakesInspectionTest.java index a212cda9aad2..18f039b21533 100644 --- a/spellchecker/testSrc/com/intellij/spellchecker/inspection/XmlWithMistakesInspectionTest.java +++ b/spellchecker/testSrc/com/intellij/spellchecker/inspection/XmlWithMistakesInspectionTest.java @@ -27,4 +27,8 @@ public class XmlWithMistakesInspectionTest extends SpellcheckerInspectionTestCas public void testXml() { doTest("test.xml"); } + + public void testCharacterData() { + doTest("test.html"); + } } diff --git a/xml/dom-impl/src/com/intellij/util/xml/reflect/DomExtenderEP.java b/xml/dom-impl/src/com/intellij/util/xml/reflect/DomExtenderEP.java index 1f12500aef34..f051c2d1d879 100644 --- a/xml/dom-impl/src/com/intellij/util/xml/reflect/DomExtenderEP.java +++ b/xml/dom-impl/src/com/intellij/util/xml/reflect/DomExtenderEP.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,39 +29,58 @@ import org.jetbrains.annotations.Nullable; * @author peter */ public class DomExtenderEP extends AbstractExtensionPointBean { - private static final Logger LOG = Logger.getInstance("#com.intellij.util.xml.reflect.DomExtenderEP"); + public static final ExtensionPointName EP_NAME = ExtensionPointName.create("com.intellij.dom.extender"); + private static final Logger LOG = Logger.getInstance("#com.intellij.util.xml.reflect.DomExtenderEP"); + @Attribute("domClass") public String domClassName; @Attribute("extenderClass") public String extenderClassName; - private Class myDomClass; - private DomExtender myExtender; - + private volatile Class myDomClass; + private volatile DomExtender myExtender; @Nullable - public DomExtensionsRegistrarImpl extend(@NotNull final Project project, @NotNull final DomInvocationHandler handler, @Nullable DomExtensionsRegistrarImpl registrar) { - if (myExtender == null) { + public DomExtensionsRegistrarImpl extend(@NotNull final Project project, + @NotNull final DomInvocationHandler handler, + @Nullable DomExtensionsRegistrarImpl registrar) { + if (myDomClass == null) { try { myDomClass = findClass(domClassName); + } + catch (Exception e) { + LOG.error(e); + return registrar; + } + } + + if (!myDomClass.isAssignableFrom(handler.getRawType())) { + return registrar; + } + + + if (myExtender == null) { + try { myExtender = instantiate(extenderClassName, project.getPicoContainer()); } - catch(Exception e) { + catch (Exception e) { LOG.error(e); - return null; + return registrar; } } - if (myDomClass.isAssignableFrom(handler.getRawType())) { - if (!myExtender.supportsStubs() && XmlUtil.isStubBuilding()) return registrar; - if (registrar == null) { - registrar = new DomExtensionsRegistrarImpl(); - } - //noinspection unchecked - myExtender.registerExtensions(handler.getProxy(), registrar); + + if (!myExtender.supportsStubs() && XmlUtil.isStubBuilding()) { + return registrar; } + + if (registrar == null) { + registrar = new DomExtensionsRegistrarImpl(); + } + //noinspection unchecked + myExtender.registerExtensions(handler.getProxy(), registrar); + return registrar; } - }