diff --git a/build/conf/ideaCE-launcher.properties b/build/conf/ideaCE-launcher.properties index 38866d93f26a..447e5e48859a 100644 --- a/build/conf/ideaCE-launcher.properties +++ b/build/conf/ideaCE-launcher.properties @@ -5,3 +5,4 @@ IDC_WINLAUNCHER=__IDE-NAME___LAUNCHER IDS_PROPS_ENV_VAR=__IDE-NAME___PROPERTIES IDS_VM_OPTIONS_ENV_VAR=__IDE-NAME___VM_OPTIONS IDS_ERROR_LAUNCHING_APP=Error launching __IDE-NAME__ +IDS_VM_OPTIONS=-Didea.paths.selector=__PRODUCT_PATHS_SELECTOR__ diff --git a/build/conf/ideaCE64-launcher.properties b/build/conf/ideaCE64-launcher.properties index 0567cc288251..03fe514c2f16 100644 --- a/build/conf/ideaCE64-launcher.properties +++ b/build/conf/ideaCE64-launcher.properties @@ -6,3 +6,4 @@ IDC_WINLAUNCHER=__IDE-NAME___LAUNCHER IDS_PROPS_ENV_VAR=__IDE-NAME___PROPERTIES IDS_VM_OPTIONS_ENV_VAR=__IDE-NAME__64_VM_OPTIONS IDS_ERROR_LAUNCHING_APP=Error launching __IDE-NAME__ +IDS_VM_OPTIONS=-Didea.paths.selector=__PRODUCT_PATHS_SELECTOR__ diff --git a/build/scripts/dist.gant b/build/scripts/dist.gant index efc8f4fd3b65..61597d1b8e3b 100644 --- a/build/scripts/dist.gant +++ b/build/scripts/dist.gant @@ -15,6 +15,7 @@ */ +import com.intellij.openapi.util.text.StringUtil import org.jetbrains.jps.gant.LayoutInfo import static org.jetbrains.jps.idea.IdeaProjectLoader.guessHome @@ -127,6 +128,10 @@ def layoutAll(Map args, String home, String out, Paths _paths = null, buildJps = def layouts = includeFile("$home/build/scripts/layouts.gant") LayoutInfo info = layouts.layoutFull(home, paths.distAll, null) layouts.layout_core(home, paths.artifacts_core) + ant.zip(destfile: "${paths.artifacts}/intellij-core-${StringUtil.trimStart(args.buildNumber, "IC-")}.zip") { + fileset(dir: paths.artifacts_core) + } + notifyArtifactBuilt(paths.artifacts_core) if (buildJps) { layouts.layoutJps(home, paths.artifacts_jps, args.buildNumber, {}) diff --git a/build/scripts/nsis_installer.gant b/build/scripts/nsis_installer.gant index a74a7e6f36f6..39795a6cf4b6 100644 --- a/build/scripts/nsis_installer.gant +++ b/build/scripts/nsis_installer.gant @@ -25,6 +25,7 @@ def nsis_installer(pathsToInclude, stringsFile, pathsFile, outNamePrefix, includ 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") { 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/compiler/impl/src/com/intellij/compiler/impl/CompileDriver.java b/java/compiler/impl/src/com/intellij/compiler/impl/CompileDriver.java index a5685017d365..a9f1e16d9622 100644 --- a/java/compiler/impl/src/com/intellij/compiler/impl/CompileDriver.java +++ b/java/compiler/impl/src/com/intellij/compiler/impl/CompileDriver.java @@ -415,6 +415,9 @@ public class CompileDriver { } } } + catch (ProcessCanceledException ignored) { + compileContext.putUserDataIfAbsent(COMPILE_SERVER_BUILD_STATUS, ExitStatus.CANCELLED); + } catch (Throwable e) { LOG.error(e); // todo } @@ -480,7 +483,7 @@ public class CompileDriver { final Set genSourceRoots = new THashSet(FileUtil.FILE_HASHING_STRATEGY); final CompilerConfiguration config = CompilerConfiguration.getInstance(myProject); for (Module module : affectedModules) { - if (config.getAnnotationProcessingConfiguration(module).isEnabled()) { + if (!module.isDisposed() && config.getAnnotationProcessingConfiguration(module).isEnabled()) { final String path = CompilerPaths.getAnnotationProcessorsGenerationPath(module); if (path != null) { genSourceRoots.add(new File(path)); diff --git a/java/compiler/openapi/src/com/intellij/openapi/compiler/ex/CompilerPathsEx.java b/java/compiler/openapi/src/com/intellij/openapi/compiler/ex/CompilerPathsEx.java index 8235094c6e93..d3ece4f553bd 100644 --- a/java/compiler/openapi/src/com/intellij/openapi/compiler/ex/CompilerPathsEx.java +++ b/java/compiler/openapi/src/com/intellij/openapi/compiler/ex/CompilerPathsEx.java @@ -78,7 +78,7 @@ public class CompilerPathsEx extends CompilerPaths { public static String[] getOutputPaths(Module[] modules) { final Set outputPaths = new OrderedSet(); for (Module module : modules) { - final CompilerModuleExtension compilerModuleExtension = CompilerModuleExtension.getInstance(module); + final CompilerModuleExtension compilerModuleExtension = !module.isDisposed()? CompilerModuleExtension.getInstance(module) : null; if (compilerModuleExtension == null) { continue; } diff --git a/java/debugger/impl/src/com/intellij/debugger/settings/DebuggerSteppingConfigurable.java b/java/debugger/impl/src/com/intellij/debugger/settings/DebuggerSteppingConfigurable.java index 5ec0450b8db7..14d5b1287222 100644 --- a/java/debugger/impl/src/com/intellij/debugger/settings/DebuggerSteppingConfigurable.java +++ b/java/debugger/impl/src/com/intellij/debugger/settings/DebuggerSteppingConfigurable.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. @@ -143,7 +143,7 @@ class DebuggerSteppingConfigurable implements ConfigurableUi { final JPanel evalFinallyPanel = new JPanel(new BorderLayout()); evalFinallyPanel.add(box, BorderLayout.CENTER); evalFinallyPanel.add(new JLabel(DebuggerBundle.message("label.debugger.general.configurable.evaluate.finally.on.pop")), BorderLayout.WEST); - panel.add(evalFinallyPanel, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 1.0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(4, cbLeftOffset, 0, 0), 0, 0)); + panel.add(evalFinallyPanel, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(4, cbLeftOffset, 0, 0), 0, 0)); return panel; } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java index 3cde802544c8..d0277398eff0 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java @@ -212,7 +212,7 @@ public class ControlFlowAnalyzer extends JavaElementVisitor { private void flushArrayElementsOnUnknownIndexAssignment(PsiExpression lExpr) { if (lExpr instanceof PsiArrayAccessExpression && - myFactory.createValue(lExpr) == null // check for unknown index, otherwise AssignInstruction will flush only that element + !(myFactory.createValue(lExpr) instanceof DfaVariableValue) // check for unknown index, otherwise AssignInstruction will flush only that element ) { DfaValue arrayVar = myFactory.createValue(((PsiArrayAccessExpression)lExpr).getArrayExpression()); if (arrayVar instanceof DfaVariableValue) { diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaPsiUtil.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaPsiUtil.java index 090d1389c8d1..6ca85c46d20f 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaPsiUtil.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaPsiUtil.java @@ -70,13 +70,6 @@ public class DfaPsiUtil { return Nullness.UNKNOWN; } - if (NullableNotNullManager.isNullable(owner)) { - return Nullness.NULLABLE; - } - if (NullableNotNullManager.isNotNull(owner)) { - return Nullness.NOT_NULL; - } - if (resultType != null) { NullableNotNullManager nnn = NullableNotNullManager.getInstance(owner.getProject()); for (PsiAnnotation annotation : resultType.getAnnotations()) { @@ -95,6 +88,13 @@ public class DfaPsiUtil { } } + if (NullableNotNullManager.isNullable(owner)) { + return Nullness.NULLABLE; + } + if (NullableNotNullManager.isNotNull(owner)) { + return Nullness.NOT_NULL; + } + return Nullness.UNKNOWN; } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StandardDataFlowRunner.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StandardDataFlowRunner.java index 4f9108befb17..0d951c81f11d 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StandardDataFlowRunner.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StandardDataFlowRunner.java @@ -74,7 +74,7 @@ public class StandardDataFlowRunner extends DataFlowRunner { } PsiType returnType = method.getReturnType(); - return returnType != null && returnType.equalsToText(CommonClassNames.JAVA_LANG_VOID); + return returnType != null && (returnType == PsiType.VOID || returnType.equalsToText(CommonClassNames.JAVA_LANG_VOID)); } public void onInstructionProducesCCE(Instruction instruction) { diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaExpressionFactory.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaExpressionFactory.java index 10332afeaed1..7c630368a13f 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaExpressionFactory.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaExpressionFactory.java @@ -70,6 +70,7 @@ public class DfaExpressionFactory { return getExpressionDfaValue(((PsiParenthesizedExpression)expression).getExpression()); } + PsiType type = expression.getType(); if (expression instanceof PsiArrayAccessExpression) { PsiExpression arrayExpression = ((PsiArrayAccessExpression)expression).getArrayExpression(); DfaValue qualifier = getExpressionDfaValue(arrayExpression); @@ -79,7 +80,9 @@ public class DfaExpressionFactory { return myFactory.getVarFactory().createVariableValue(indexVar, expression.getType(), false, (DfaVariableValue)qualifier); } } - return null; + if (type != null) { + return myFactory.createTypeValue(type, DfaPsiUtil.getElementNullability(type, null)); + } } if (expression instanceof PsiMethodCallExpression) { @@ -99,7 +102,6 @@ public class DfaExpressionFactory { } final Object value = JavaConstantExpressionEvaluator.computeConstantExpression(expression, false); - PsiType type = expression.getType(); if (value != null && type != null) { if (value instanceof String) { return myFactory.createTypeValue(type, Nullness.NOT_NULL); // Non-null string literal. diff --git a/java/java-impl/src/com/intellij/codeInsight/TargetElementUtil.java b/java/java-impl/src/com/intellij/codeInsight/TargetElementUtil.java index 153bafb001f0..c9931b23cbd7 100644 --- a/java/java-impl/src/com/intellij/codeInsight/TargetElementUtil.java +++ b/java/java-impl/src/com/intellij/codeInsight/TargetElementUtil.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. @@ -212,6 +212,7 @@ public class TargetElementUtil extends TargetElementUtilBase { return null; } + @NotNull @Override public Collection getTargetCandidates(final PsiReference reference) { PsiElement parent = reference.getElement().getParent(); diff --git a/java/java-impl/src/com/intellij/psi/impl/source/codeStyle/javadoc/CommentFormatter.java b/java/java-impl/src/com/intellij/psi/impl/source/codeStyle/javadoc/CommentFormatter.java index e08bb4844969..8e3b75b6fab7 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/codeStyle/javadoc/CommentFormatter.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/codeStyle/javadoc/CommentFormatter.java @@ -15,7 +15,6 @@ */ package com.intellij.psi.impl.source.codeStyle.javadoc; -import com.intellij.codeInsight.javadoc.JavaDocUtil; import com.intellij.ide.highlighter.JavaFileType; import com.intellij.lang.ASTNode; import com.intellij.lang.java.JavaLanguage; @@ -31,9 +30,12 @@ import com.intellij.psi.impl.source.SourceTreeToPsiMap; import com.intellij.psi.javadoc.PsiDocComment; import com.intellij.psi.util.PsiUtil; import com.intellij.util.IncorrectOperationException; +import com.intellij.util.text.CharArrayUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import static com.intellij.psi.impl.source.codeStyle.javadoc.JDParser.CommentInfo; + /** * @author max */ @@ -63,47 +65,12 @@ public class CommentFormatter { if (!getSettings().ENABLE_JAVADOC_FORMATTING) return; PsiElement psiElement = SourceTreeToPsiMap.treeElementToPsi(element); - processElementComment(psiElement); - } - - private void processElementComment(@Nullable PsiElement psiElement) { - if (psiElement instanceof PsiClass) { - String newCommentText = formatClassComment((PsiClass)psiElement); - replaceDocComment(newCommentText, (PsiDocCommentOwner)psiElement); - } - else if (psiElement instanceof PsiMethod) { - String newCommentText = formatMethodComment((PsiMethod)psiElement); - replaceDocComment(newCommentText, (PsiDocCommentOwner)psiElement); - } - else if (psiElement instanceof PsiField) { - String newCommentText = formatFieldComment((PsiField)psiElement); - replaceDocComment(newCommentText, (PsiDocCommentOwner)psiElement); - } - else if (psiElement instanceof PsiDocComment) { - PsiDocComment comment = (PsiDocComment)psiElement; - if (JavaDocUtil.isInsidePackageInfo(comment)) { - String newCommentText = formatPackageComment(comment); - replaceCommentText(newCommentText, comment); - } else { - processElementComment(psiElement.getParent()); - } + if (psiElement != null) { + getParser().formatCommentText(psiElement, this); } } - private String formatPackageComment(@NotNull PsiDocComment comment) { - final String info = getCommentInfo(comment); - if (info == null) return null; - - JDComment jdComment = getParser().parse(info, new JDComment(this)); - return jdComment.generate(""); - } - - private void replaceDocComment(@Nullable String newCommentText, @NotNull final PsiDocCommentOwner psiDocCommentOwner) { - final PsiDocComment oldComment = psiDocCommentOwner.getDocComment(); - replaceCommentText(newCommentText, oldComment); - } - - private void replaceCommentText(@Nullable String newCommentText, @Nullable PsiDocComment oldComment) { + public void replaceCommentText(@Nullable String newCommentText, @Nullable PsiDocComment oldComment) { if (newCommentText != null) newCommentText = stripSpaces(newCommentText); if (newCommentText == null || oldComment == null || newCommentText.equals(oldComment.getText())) { return; @@ -142,53 +109,22 @@ public class CommentFormatter { } @Nullable - private String formatClassComment(@NotNull PsiClass psiClass) { - final String info = getOrigCommentInfo(psiClass); - if (info == null) return null; - - JDComment comment = getParser().parse(info, new JDClassComment(this)); - return comment.generate(getIndent(psiClass)); - } - - @Nullable - private String formatMethodComment(@NotNull PsiMethod psiMethod) { - final String info = getOrigCommentInfo(psiMethod); - if (info == null) return null; - - JDComment comment = getParser().parse(info, new JDMethodComment(this)); - return comment.generate(getIndent(psiMethod)); - } - - @Nullable - private String formatFieldComment(@NotNull PsiField psiField) { - final String info = getOrigCommentInfo(psiField); - if (info == null) return null; - - JDComment comment = getParser().parse(info, new JDComment(this)); - return comment.generate(getIndent(psiField)); - } - - - /** - * Returns the original comment info of the specified element or null - * - * @param element the specified element - * @return text chunk - */ - @Nullable - private static String getOrigCommentInfo(PsiDocCommentOwner element) { + public static CommentInfo getOrigCommentInfo(PsiDocCommentOwner element) { PsiElement e = element.getFirstChild(); if (!(e instanceof PsiComment)) { //no comments for this element return null; } else { - return getCommentInfo(((PsiComment)e)); + return getCommentInfo((PsiComment)e); } } @Nullable - private static String getCommentInfo(PsiComment element) { + public static CommentInfo getCommentInfo(PsiComment element) { + String commentHeader = null; + String commentFooter = null; + StringBuilder sb = new StringBuilder(); PsiElement e = element; boolean first = true; @@ -201,11 +137,18 @@ public class CommentFormatter { sb.append(text.substring(2).trim()); } else if (text.startsWith("/*")) { - if (text.charAt(2) == '*') { - text = text.substring(3, Math.max(3, text.length() - 2)); + int commentHeaderEndOffset = CharArrayUtil.shiftForward(text, 1, "*"); + int commentFooterStartOffset = CharArrayUtil.shiftBackward(text, text.length() - 2, "*"); + + if (commentHeaderEndOffset <= commentFooterStartOffset) { + commentHeader = text.substring(0, commentHeaderEndOffset); + commentFooter = text.substring(commentFooterStartOffset + 1); + text = text.substring(commentHeaderEndOffset, commentFooterStartOffset + 1); } else { - text = text.substring(2, Math.max(2, text.length() - 2)); + commentHeader = text.substring(0, commentHeaderEndOffset); + text = ""; + commentFooter = ""; } sb.append(text); } @@ -217,7 +160,7 @@ public class CommentFormatter { e = e.getNextSibling(); } - return sb.toString(); + return new CommentInfo(commentHeader, sb.toString(), commentFooter); } /** @@ -226,6 +169,9 @@ public class CommentFormatter { * @return indentation size */ private int getIndentSpecial(@NotNull PsiElement element) { + if (element instanceof PsiDocComment) { + return 0; + } LOG.assertTrue(element instanceof PsiClass || element instanceof PsiField || element instanceof PsiMethod); @@ -252,7 +198,7 @@ public class CommentFormatter { * @return indent which would be used for the given element when it's formatted according to the current code style settings */ @NotNull - private String getIndent(@NotNull PsiElement element) { + public String getIndent(@NotNull PsiElement element) { return StringUtil.repeatSymbol(' ', getIndentSpecial(element)); } } diff --git a/java/java-impl/src/com/intellij/psi/impl/source/codeStyle/javadoc/JDComment.java b/java/java-impl/src/com/intellij/psi/impl/source/codeStyle/javadoc/JDComment.java index 0d51c0d2111f..69bdd3c41bf9 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/codeStyle/javadoc/JDComment.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/codeStyle/javadoc/JDComment.java @@ -37,6 +37,8 @@ public class JDComment { private String mySince; private String myDeprecated; private boolean myMultiLineComment; + private String myFirstLine = "/**"; + private String myEndLine = "*/"; public JDComment(@NotNull CommentFormatter formatter) { myFormatter = formatter; @@ -128,13 +130,13 @@ public class JDComment { || sb.indexOf("\n") != sb.length() - 1) // If comment has become multiline after formatting - it must be shown as multiline. // Last symbol is always '\n', so we need to check if there is one more LF symbol before it. { - sb.insert(0, "/**\n"); + sb.insert(0, myFirstLine + '\n'); sb.append(indent); } else { sb.replace(0, prefix.length(), "/** "); sb.deleteCharAt(sb.length()-1); } - sb.append(" */"); + sb.append(' ').append(myEndLine); return sb.toString(); } @@ -142,6 +144,14 @@ public class JDComment { protected void generateSpecial(@NotNull String prefix, @NotNull StringBuilder sb) { } + public void setFirstCommentLine(@NotNull String firstCommentLine) { + myFirstLine = firstCommentLine; + } + + public void setLastCommentLine(@NotNull String lastCommentLine) { + myEndLine = lastCommentLine; + } + public void addSeeAlso(@NotNull String seeAlso) { if (mySeeAlsoList == null) { mySeeAlsoList = ContainerUtilRt.newArrayList(); diff --git a/java/java-impl/src/com/intellij/psi/impl/source/codeStyle/javadoc/JDParser.java b/java/java-impl/src/com/intellij/psi/impl/source/codeStyle/javadoc/JDParser.java index f6dc9b9697f2..4745c3890cee 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/codeStyle/javadoc/JDParser.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/codeStyle/javadoc/JDParser.java @@ -19,7 +19,9 @@ import com.intellij.lang.java.JavaLanguage; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.text.StringUtil; import com.intellij.pom.java.LanguageLevel; +import com.intellij.psi.*; import com.intellij.psi.codeStyle.CodeStyleSettings; +import com.intellij.psi.javadoc.PsiDocComment; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -40,6 +42,8 @@ public class JDParser { private static final String P_START_TAG = "

"; private static final String SELF_CLOSED_P_TAG = "

"; + private static final char lineSeparator = '\n'; + private final CodeStyleSettings mySettings; private final LanguageLevel myLanguageLevel; @@ -48,11 +52,74 @@ public class JDParser { myLanguageLevel = languageLevel; } - private static final char lineSeparator = '\n'; + public void formatCommentText(@NotNull PsiElement element, @NotNull CommentFormatter formatter) { + CommentInfo info = getElementsCommentInfo(element); + JDComment comment = info != null ? parse(info, formatter) : null; + if (comment != null) { + String indent = formatter.getIndent(info.getCommentOwner()); + String commentText = comment.generate(indent); + formatter.replaceCommentText(commentText, (PsiDocComment)info.psiComment); + } + } + + private CommentInfo getElementsCommentInfo(@Nullable PsiElement psiElement) { + CommentInfo info = null; + if (psiElement instanceof PsiDocComment) { + final PsiDocComment docComment = (PsiDocComment)psiElement; + if (docComment.getOwner() == null && docComment.getParent() instanceof PsiJavaFile) { + info = CommentFormatter.getCommentInfo(docComment); + if (info != null) { + info.setCommentOwner(docComment); + info.setComment(docComment); + } + } + else { + return getElementsCommentInfo(psiElement.getParent()); + } + } + else if (psiElement instanceof PsiDocCommentOwner) { + PsiDocCommentOwner owner = (PsiDocCommentOwner)psiElement; + info = CommentFormatter.getOrigCommentInfo(owner); + if (info != null) { + info.setCommentOwner(owner); + info.setComment(owner.getDocComment()); + } + } + return info; + } + + private JDComment parse(@NotNull CommentInfo info, @NotNull CommentFormatter formatter) { + PsiElement owner = info.getCommentOwner(); + JDComment comment = createComment(owner, formatter); + if (comment == null) return null; + + parse(info.comment, comment); + if (info.commentHeader != null) { + comment.setFirstCommentLine(info.commentHeader); + } + if (info.commentFooter != null) { + comment.setLastCommentLine(info.commentFooter); + } + + return comment; + } + + private JDComment createComment(@NotNull PsiElement psiElement, @NotNull CommentFormatter formatter) { + if (psiElement instanceof PsiClass) { + return new JDClassComment(formatter); + } + else if (psiElement instanceof PsiMethod) { + return new JDMethodComment(formatter); + } + else if (psiElement instanceof PsiField || psiElement instanceof PsiDocComment) { + return new JDComment(formatter); + } + return null; + } @NotNull - public JDComment parse(@Nullable String text, @NotNull JDComment comment) { - if (text == null) return comment; + private void parse(@Nullable String text, @NotNull JDComment comment) { + if (text == null) return; List markers = new ArrayList(); List l = toArray(text, "\n", markers); @@ -66,9 +133,9 @@ public class JDParser { comment.setMultiLine(true); } - if (l == null) return comment; + if (l == null) return; int size = l.size(); - if (size == 0) return comment; + if (size == 0) return; // preprocess strings - removes first '*' for (int i = 0; i < size; i++) { @@ -140,8 +207,6 @@ public class JDParser { } } } - - return comment; } /** @@ -544,4 +609,35 @@ public class JDParser { return sb; } + + public static class CommentInfo { + public final String commentHeader; + public final String comment; + public final String commentFooter; + + private PsiComment psiComment; + private PsiElement myCommentOwner; + + public CommentInfo(String commentHeader, String comment, String commentFooter) { + this.commentHeader = commentHeader; + this.comment = comment; + this.commentFooter = commentFooter; + } + + public void setCommentOwner(PsiElement commentOwner) { + myCommentOwner = commentOwner; + } + + public PsiElement getCommentOwner() { + return myCommentOwner; + } + + public void setComment(PsiDocComment comment) { + psiComment = comment; + } + + public PsiComment getComment() { + return psiComment; + } + } } diff --git a/java/java-psi-impl/src/com/intellij/psi/PsiTypeMapper.java b/java/java-psi-impl/src/com/intellij/psi/PsiTypeMapper.java index ad93a16bc180..a828306e9f7f 100644 --- a/java/java-psi-impl/src/com/intellij/psi/PsiTypeMapper.java +++ b/java/java-psi-impl/src/com/intellij/psi/PsiTypeMapper.java @@ -42,7 +42,7 @@ public abstract class PsiTypeMapper extends PsiTypeVisitorEx { PsiType mappedComponent = mapType(componentType); if (mappedComponent == null) return null; if (mappedComponent == componentType) return type; - return new PsiArrayType(mappedComponent); + return new PsiArrayType(mappedComponent, type.getAnnotations()); } @Override @@ -51,7 +51,7 @@ public abstract class PsiTypeMapper extends PsiTypeVisitorEx { PsiType mappedComponent = mapType(componentType); if (mappedComponent == null) return null; if (mappedComponent == componentType) return type; - return new PsiEllipsisType(mappedComponent); + return new PsiEllipsisType(mappedComponent, type.getAnnotations()); } @Override diff --git a/java/java-tests/testData/codeInsight/multipleJdks/java3/p/CloseableAutoCloseable.java b/java/java-tests/testData/codeInsight/multipleJdks/java3/p/CloseableAutoCloseable.java new file mode 100644 index 000000000000..f40f4c029ded --- /dev/null +++ b/java/java-tests/testData/codeInsight/multipleJdks/java3/p/CloseableAutoCloseable.java @@ -0,0 +1,5 @@ +package p; + +import java.io.FileReader; + +class MyReader extends FileReader {} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/multipleJdks/java7/p/CloseableAutoCloseable.java b/java/java-tests/testData/codeInsight/multipleJdks/java7/p/CloseableAutoCloseable.java new file mode 100644 index 000000000000..befd13ee94e2 --- /dev/null +++ b/java/java-tests/testData/codeInsight/multipleJdks/java7/p/CloseableAutoCloseable.java @@ -0,0 +1,11 @@ +package p; + +class Foo { + + { + try (MyReader c1 = new MyReader() {}) { + + } + } + +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/NullableArrayComponent.java b/java/java-tests/testData/inspection/dataFlow/fixture/NullableArrayComponent.java new file mode 100644 index 000000000000..b1222bd7f654 --- /dev/null +++ b/java/java-tests/testData/inspection/dataFlow/fixture/NullableArrayComponent.java @@ -0,0 +1,27 @@ +import foo.*; + +import java.util.List; + +class TestCompilerWarnings { + public void m(@NotNull Object x) { + assert x != null; + } + + public void test1Array(@Nullable String @NotNull [] x) { + if (x == null) { + System.out.println("x is null"); + } + m(x); + m(x[0]); + } + + public void test2Array(@NotNull String @Nullable [] x) { + if (x == null) { + System.out.println("x is null"); + } else { + m(x[0]); + } + m(x); + } + +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/NullableVoidLambda.java b/java/java-tests/testData/inspection/dataFlow/fixture/NullableVoidLambda.java new file mode 100644 index 000000000000..1dfd1c8f9263 --- /dev/null +++ b/java/java-tests/testData/inspection/dataFlow/fixture/NullableVoidLambda.java @@ -0,0 +1,12 @@ +import org.jetbrains.annotations.Nullable; + +class Example { + void foo() { + Runnable runnable = () -> bar(); + } + + @Nullable + String bar() { + return null; + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/MultipleJdksHighlightingTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/MultipleJdksHighlightingTest.java index 71e99d478a83..18de68b92f6b 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/MultipleJdksHighlightingTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/MultipleJdksHighlightingTest.java @@ -16,6 +16,7 @@ package com.intellij.codeInsight; +import com.intellij.idea.Bombed; import com.intellij.openapi.application.ex.PathManagerEx; import com.intellij.openapi.module.Module; import com.intellij.openapi.roots.ModifiableRootModel; @@ -29,6 +30,8 @@ import com.intellij.testFramework.builders.JavaModuleFixtureBuilder; import com.intellij.testFramework.fixtures.*; import com.intellij.util.Consumer; +import java.util.Calendar; + public class MultipleJdksHighlightingTest extends UsefulTestCase { private CodeInsightTestFixture myFixture; @@ -69,8 +72,7 @@ public class MultipleJdksHighlightingTest extends UsefulTestCase { ModuleRootModificationUtil.updateModel(myJava3Module, new Consumer() { @Override public void consume(ModifiableRootModel model) { - model.addModuleOrderEntry(myJava7Module); - model.setSdk(IdeaTestUtil.getMockJdk17()); + model.setSdk(IdeaTestUtil.getMockJdk14()); String contentUrl = VfsUtilCore.pathToUrl(myFixture.getTempDirPath()) + "/java3"; model.addContentEntry(contentUrl).addSourceFolder(contentUrl, false); } @@ -79,7 +81,6 @@ public class MultipleJdksHighlightingTest extends UsefulTestCase { ModuleRootModificationUtil.updateModel(myJava7Module, new Consumer() { @Override public void consume(ModifiableRootModel model) { - model.addModuleOrderEntry(myJava8Module); model.setSdk(IdeaTestUtil.getMockJdk17()); String contentUrl = VfsUtilCore.pathToUrl(myFixture.getTempDirPath()) + "/java7"; model.addContentEntry(contentUrl).addSourceFolder(contentUrl, false); @@ -96,11 +97,18 @@ public class MultipleJdksHighlightingTest extends UsefulTestCase { }); } + private void addDependencies_37_78() { + ModuleRootModificationUtil.addDependency(myJava7Module, myJava8Module); + ModuleRootModificationUtil.addDependency(myJava3Module, myJava7Module); + } + public void testGetClass() throws Exception { + addDependencies_37_78(); doTest(); } public void testWrongSuperInLibrary() throws Exception { + addDependencies_37_78(); final String name = getTestName(false); for (Module module : new Module[] {myJava7Module, myJava8Module}) { ModuleRootModificationUtil.updateModel(module, new Consumer() { @@ -116,42 +124,61 @@ public class MultipleJdksHighlightingTest extends UsefulTestCase { } public void testWrongComparator() throws Exception { + addDependencies_37_78(); doTestWithoutLibrary(); } public void testGenericComparator() throws Exception { + addDependencies_37_78(); doTestWithoutLibrary(); } public void testGenericCallableWithDifferentTypeArgs() throws Exception { + addDependencies_37_78(); doTestWithoutLibrary(); } public void testSuperclassImplementsUnknownType() throws Exception { + addDependencies_37_78(); doTestWithoutLibrary(); } public void testDeclaredTypeOfVariableImplementsUnknownType() throws Exception { + addDependencies_37_78(); doTestWithoutLibrary(); } public void testSuperclassImplementsGenericsOfUnknownType() throws Exception { + addDependencies_37_78(); doTestWithoutLibrary(); } public void testSuperMethodNotExist() throws Exception { + addDependencies_37_78(); doTestWithoutLibrary(); } public void testNoOverriding() throws Exception { + addDependencies_37_78(); doTestWithoutLibrary(); } public void testStaticCallOnChildWithNotAccessibleParent() throws Exception { + addDependencies_37_78(); doTest3Modules(); } + @Bombed(user = "peter,anna", year = 2015, month = Calendar.MAY, day = 1) + public void testCloseableAutoCloseable() { + ModuleRootModificationUtil.addDependency(myJava7Module, myJava3Module); + final String name = getTestName(false); + myFixture.copyFileToProject("java3/p/" + name + ".java"); + myFixture.configureByFiles("java7/p/" + name + ".java"); + myFixture.checkHighlighting(); + } + public void testLanguageLevelInReturnTypeCheck() throws Exception { + addDependencies_37_78(); final String name = getTestName(false); myFixture.configureByFiles("java3/p/" + name + ".java", "java7/p/" + name + ".java"); myFixture.checkHighlighting(); diff --git a/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspection8Test.java b/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspection8Test.java index 34b1c015678f..dcffac411bfc 100644 --- a/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspection8Test.java +++ b/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspection8Test.java @@ -77,9 +77,18 @@ public class DataFlowInspection8Test extends LightCodeInsightFixtureTestCase { } public void testMethodReferenceOnNullable() { doTest(); } + public void testNullableVoidLambda() { doTest(); } public void testNullableForeachVariable() { setupCustomAnnotations(); doTest(); } + + public void testNullableArrayComponent() { + setupCustomAnnotations(); + final DataFlowInspection inspection = new DataFlowInspection(); + inspection.IGNORE_ASSERT_STATEMENTS = true; + myFixture.enableInspections(inspection); + myFixture.testHighlighting(true, false, true, getTestName(false) + ".java"); + } } diff --git a/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavadocFormatterTest.java b/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavadocFormatterTest.java index d38ec5553d5e..f1a2f73609a6 100644 --- a/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavadocFormatterTest.java +++ b/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavadocFormatterTest.java @@ -56,6 +56,61 @@ public class JavadocFormatterTest extends AbstractJavaFormatterTest { ); } + public void test_do_wrap_on_asterisks() { + doTextTest( + "/***********\n" + + " *\n" + + " *********************/\n" + + "\n" + + "\n" + + " public class Test {\n" + + "}\n", + "/***********\n" + + " *\n" + + " *********************/\n" + + "\n" + + "\n" + + "public class Test {\n" + + "}\n" + ); + } + + public void test_wrap_after_asterisks() { + doTextTest( + "/******* hollla la\n" + + " * I am javadoc comment\n" + + " * heey ***********/\n" + + " class T { }\n", + "/*******\n" + + " * hollla la\n" + + " * I am javadoc comment\n" + + " * heey\n" + + " ***********/\n" + + "class T {\n" + + "}\n" + ); + } + + public void test_strange_comment() { + doTextTest( + "/******F*****/\n" + + "public class T {\n" + + "}", + "/******\n" + + " * F\n" + + " *****/\n" + + "public class T {\n" + + "}" + ); + } + + public void test_incomplete_javadoc() { + doTextTest( + "/**\n", + "/**\n" + ); + } + public void testEA49739() throws Exception { getSettings().WRAP_LONG_LINES = true; getSettings().RIGHT_MARGIN = 35; diff --git a/java/java-tests/testSrc/com/intellij/util/indexing/FileBasedIndexTest.java b/java/java-tests/testSrc/com/intellij/util/indexing/FileBasedIndexTest.java index 426afcbc7ad0..bd3c79454d2e 100644 --- a/java/java-tests/testSrc/com/intellij/util/indexing/FileBasedIndexTest.java +++ b/java/java-tests/testSrc/com/intellij/util/indexing/FileBasedIndexTest.java @@ -1,6 +1,10 @@ package com.intellij.util.indexing; +import com.intellij.openapi.util.io.FileUtilRt; +import com.intellij.psi.stubs.StubUpdatingIndex; +import com.intellij.testFramework.LightVirtualFile; import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase; +import com.intellij.util.text.CharArrayCharSequence; /** * @author Dmitry Avdeev @@ -15,4 +19,14 @@ public class FileBasedIndexTest extends LightCodeInsightFixtureTestCase { myFixture.configureByText("Bar.java", "class Bar { String bar; }"); myFixture.testHighlighting(); } + + public void testLargeFile() throws Exception { + char[] text = new char[FileUtilRt.LARGE_FOR_CONTENT_LOADING + 42]; + final String clazz = "class Foo { String bar; }"; + for (int i = 0; i < text.length; i++) { + text[i] = i < clazz.length() ? clazz.charAt(i) : ' '; + } + final LightVirtualFile file = new LightVirtualFile("Foo.java", new CharArrayCharSequence(text)); + assertFalse(((FileBasedIndexImpl)FileBasedIndex.getInstance()).isIndexingCandidate(file, StubUpdatingIndex.INDEX_ID)); + } } diff --git a/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/JavaMatchingVisitor.java b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/JavaMatchingVisitor.java index 9640351c40cc..af934b40b462 100644 --- a/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/JavaMatchingVisitor.java +++ b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/JavaMatchingVisitor.java @@ -1478,20 +1478,20 @@ public class JavaMatchingVisitor extends JavaElementVisitor { new1.getArrayDimensions().length == 0 && new1.getArrayInitializer() != null ) { + final MatchContext matchContext = myMatchingVisitor.getMatchContext(); + final MatchingHandler handler = matchContext.getPattern().getHandler(classReference); final boolean looseMatching = myMatchingVisitor.getMatchContext().getOptions().isLooseMatching(); - final boolean typedVar = myMatchingVisitor.getMatchContext().getPattern().isTypedVar(classReference); - if ((typedVar || !looseMatching) && !allowsAbsenceOfMatch(classReference)) { + if ((handler instanceof SubstitutionHandler && ((SubstitutionHandler)handler).getMinOccurs() != 0) || !looseMatching) { myMatchingVisitor.setResult(false); return; } - final PsiElementFactory factory = JavaPsiFacade.getElementFactory(other.getProject()); - final PsiType otherType = ((PsiVariable)other.getParent()).getType(); - final PsiTypeElement otherTypeElement = factory.createTypeElement(otherType.getDeepComponentType()); - final MatchContext matchContext = myMatchingVisitor.getMatchContext(); - final MatchingHandler handler = matchContext.getPattern().getHandler(classReference); - if (handler instanceof SubstitutionHandler) { + final PsiType otherType = ((PsiArrayInitializerExpression)other).getType(); + if (handler instanceof SubstitutionHandler && otherType != null) { + final PsiElementFactory factory = JavaPsiFacade.getElementFactory(other.getProject()); + final PsiTypeElement otherTypeElement = factory.createTypeElement(otherType.getDeepComponentType()); final SubstitutionHandler substitutionHandler = (SubstitutionHandler)handler; - myMatchingVisitor.setResult(substitutionHandler.handle(otherTypeElement, matchContext)); + final MatchPredicate predicate = substitutionHandler.getPredicate(); + myMatchingVisitor.setResult(predicate == null || predicate.match(null, otherTypeElement, matchContext)); } else { final PsiType type = new1.getType(); diff --git a/platform/core-impl/src/com/intellij/lang/impl/PsiBuilderImpl.java b/platform/core-impl/src/com/intellij/lang/impl/PsiBuilderImpl.java index e34406868d95..18888bd1765a 100644 --- a/platform/core-impl/src/com/intellij/lang/impl/PsiBuilderImpl.java +++ b/platform/core-impl/src/com/intellij/lang/impl/PsiBuilderImpl.java @@ -133,7 +133,7 @@ public class PsiBuilderImpl extends UserDataHolderBase implements PsiBuilder { @Nullable ASTNode originalTree, @Nullable MyTreeStructure parentLightTree) { this(project, containingFile, parserDefinition.getWhitespaceTokens(), parserDefinition.getCommentTokens(), lexer, charTable, text, - originalTree, parentLightTree, 0); + originalTree, parentLightTree, null); } public PsiBuilderImpl(Project project, @@ -145,7 +145,7 @@ public class PsiBuilderImpl extends UserDataHolderBase implements PsiBuilder { @NotNull final CharSequence text, @Nullable ASTNode originalTree, @Nullable MyTreeStructure parentLightTree) { - this(project, containingFile, whiteSpaces, comments, lexer, charTable, text, originalTree, parentLightTree, 0); + this(project, containingFile, whiteSpaces, comments, lexer, charTable, text, originalTree, parentLightTree, null); } private PsiBuilderImpl(Project project, @@ -157,7 +157,7 @@ public class PsiBuilderImpl extends UserDataHolderBase implements PsiBuilder { @NotNull final CharSequence text, @Nullable ASTNode originalTree, @Nullable MyTreeStructure parentLightTree, - int offset) { + LazyParseableToken parentToken) { myProject = project; myFile = containingFile; @@ -170,9 +170,9 @@ public class PsiBuilderImpl extends UserDataHolderBase implements PsiBuilder { myCharTable = charTable; myOriginalTree = originalTree; myParentLightTree = parentLightTree; - myOffset = offset; + myOffset = parentToken != null ? parentToken.getStartOffset() : 0; - cacheLexemes(); + cacheLexemes(parentToken); } public PsiBuilderImpl(@NotNull final Project project, @@ -190,10 +190,41 @@ public class PsiBuilderImpl extends UserDataHolderBase implements PsiBuilder { @NotNull final LighterLazyParseableNode chameleon, @NotNull final CharSequence text) { this(project, chameleon.getContainingFile(), parserDefinition.getWhitespaceTokens(), parserDefinition.getCommentTokens(), lexer, - chameleon.getCharTable(), text, null, ((LazyParseableToken)chameleon).myParent, chameleon.getStartOffset()); + chameleon.getCharTable(), text, null, ((LazyParseableToken)chameleon).myParent, ((LazyParseableToken)chameleon) + ); } - private void cacheLexemes() { + private void cacheLexemes(LazyParseableToken parentToken) { + int[] lexStarts = null; + IElementType[] lexTypes = null; + int lexemeCount = -1; + boolean doLexingOptimizationCorrectionCheck = false; + + if (parentToken != null) { + // there are two types of lazy parseable tokens out there: collapsed out of individual tokens or single token that needs to be expanded + // in first case parent PsiBuilder has all our text lexed so no need to do it again + int tokenCount = parentToken.myEndIndex - parentToken.myStartIndex; + if (tokenCount != 1) { // not expand single lazy parseable token case + lexStarts = new int[tokenCount + 1]; + System.arraycopy(parentToken.myBuilder.myLexStarts, parentToken.myStartIndex, lexStarts, 0, tokenCount); + int diff = parentToken.myBuilder.myLexStarts[parentToken.myStartIndex]; + for(int i = 0; i < tokenCount; ++i) lexStarts[i] -= diff; + lexStarts[tokenCount] = myText.length(); + + lexTypes = new IElementType[tokenCount]; + System.arraycopy(parentToken.myBuilder.myLexTypes, parentToken.myStartIndex, lexTypes, 0, tokenCount); + lexemeCount = tokenCount; + } + ProgressIndicatorProvider.checkCanceled(); + + if (!doLexingOptimizationCorrectionCheck) { + myLexStarts = lexStarts; + myLexTypes = lexTypes; + myLexemeCount = lexemeCount; + return; + } + } + int approxLexCount = Math.max(10, myText.length() / 5); myLexStarts = new int[approxLexCount]; @@ -216,7 +247,8 @@ public class PsiBuilderImpl extends UserDataHolderBase implements PsiBuilder { final IElementType tokenType = myLexer.getTokenType(); sb.append("Token sequence broken") .append("\n this: '").append(myLexer.getTokenText()).append("' (").append(tokenType).append(':') - .append(tokenType != null ? tokenType.getLanguage() : null).append(") ").append(tokenStart).append(":").append(myLexer.getTokenEnd()); + .append(tokenType != null ? tokenType.getLanguage() : null).append(") ").append(tokenStart).append(":") + .append(myLexer.getTokenEnd()); if (i > 0) { final int prevStart = myLexStarts[i - 1]; sb.append("\n prev: '").append(myText.subSequence(prevStart, offset)).append("' (").append(myLexTypes[i - 1]).append(':') @@ -238,6 +270,19 @@ public class PsiBuilderImpl extends UserDataHolderBase implements PsiBuilder { myLexemeCount = i; clearCachedTokenType(); + + //noinspection ConstantConditions + if (doLexingOptimizationCorrectionCheck) { + if (lexemeCount != -1) { + assert lexemeCount == myLexemeCount; + for(int j = 0; j < lexemeCount; ++j) { + if (myLexStarts[j] != lexStarts[j] || myLexTypes[j] != lexTypes[j]) { + assert false; + } + } + assert myLexStarts[lexemeCount] == lexStarts[lexemeCount]; + } + } } @Override @@ -1215,8 +1260,8 @@ public class PsiBuilderImpl extends UserDataHolderBase implements PsiBuilder { CharSequence context = index < myLexStarts.length ? myText.subSequence(Math.max(0, myLexStarts[index] - 1000), myLexStarts[index]) : ""; String language = myFile != null ? myFile.getLanguage() + ", " : ""; - LOG.error(UNBALANCED_MESSAGE + "\n" + - "language: " + language + "\n" + + LOG.error(UNBALANCED_MESSAGE + "\n" + + "language: " + language + "\n" + "context: '" + context + "'"); } diff --git a/platform/diff-impl/src/com/intellij/diff/tools/util/FoldingModelSupport.java b/platform/diff-impl/src/com/intellij/diff/tools/util/FoldingModelSupport.java index 3e7f1d20b739..76e2baf88284 100644 --- a/platform/diff-impl/src/com/intellij/diff/tools/util/FoldingModelSupport.java +++ b/platform/diff-impl/src/com/intellij/diff/tools/util/FoldingModelSupport.java @@ -161,15 +161,18 @@ public class FoldingModelSupport { private void addRange(int[] starts, int[] ends) { List result = new ArrayList(3); + int[] rangeStarts = new int[myCount]; + int[] rangeEnds = new int[myCount]; + for (int number = 0; ; number++) { int shift = getRangeShift(mySettings.range, number); if (shift == -1) break; for (int i = 0; i < myCount; i++) { - starts[i] = bound(starts[i] + shift, i); - ends[i] = bound(ends[i] - shift, i); + rangeStarts[i] = bound(starts[i] + shift, i); + rangeEnds[i] = bound(ends[i] - shift, i); } - ContainerUtil.addAllNotNull(result, createRange(starts, ends, myExpandSuggester.isExpanded(starts, ends))); + ContainerUtil.addAllNotNull(result, createRange(rangeStarts, rangeEnds, myExpandSuggester.isExpanded(rangeStarts, rangeEnds))); } if (result.size() > 0) { @@ -700,20 +703,17 @@ public class FoldingModelSupport { // /* - * number: - * 0: unchanged -> first folding - * 1: first folding-> second folding - * ... - * -1 - end + * number - depth of folding insertion (from zero) + * return: number of context lines. ('-1' - end) */ private static int getRangeShift(int range, int number) { switch (number) { case 0: return range; case 1: - return range; - case 2: return range * 2; + case 2: + return range * 4; default: return -1; } 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 d1df97e088b3..94f5cf25985d 100644 --- a/platform/diff-impl/src/com/intellij/diff/util/DiffUtil.java +++ b/platform/diff-impl/src/com/intellij/diff/util/DiffUtil.java @@ -37,6 +37,7 @@ import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.DataProvider; import com.intellij.openapi.actionSystem.DefaultActionGroup; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.impl.LaterInvocator; import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.components.StoragePathMacros; import com.intellij.openapi.diagnostic.Logger; @@ -822,7 +823,7 @@ public class DiffUtil { public static WindowWrapper.Mode getWindowMode(@NotNull DiffDialogHints hints) { WindowWrapper.Mode mode = hints.getMode(); if (mode == null) { - boolean isUnderDialog = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusedWindow() instanceof JDialog; + boolean isUnderDialog = LaterInvocator.isInModalContext(); mode = isUnderDialog ? WindowWrapper.Mode.MODAL : WindowWrapper.Mode.FRAME; } return mode; diff --git a/platform/lang-impl/src/com/intellij/codeInsight/TargetElementUtilBase.java b/platform/lang-impl/src/com/intellij/codeInsight/TargetElementUtilBase.java index b46ea2e52762..7eb959506722 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/TargetElementUtilBase.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/TargetElementUtilBase.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. @@ -336,10 +336,11 @@ public class TargetElementUtilBase { } } + @NotNull public Collection getTargetCandidates(PsiReference reference) { if (reference instanceof PsiPolyVariantReference) { final ResolveResult[] results = ((PsiPolyVariantReference)reference).multiResolve(false); - final ArrayList navigatableResults = new ArrayList(results.length); + List navigatableResults = new ArrayList(results.length); for(ResolveResult r:results) { PsiElement element = r.getElement(); diff --git a/platform/lang-impl/src/com/intellij/codeInsight/actions/AbstractLayoutCodeProcessor.java b/platform/lang-impl/src/com/intellij/codeInsight/actions/AbstractLayoutCodeProcessor.java index 64584a6979d3..5cf0b0d27778 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/actions/AbstractLayoutCodeProcessor.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/actions/AbstractLayoutCodeProcessor.java @@ -60,6 +60,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.concurrent.Callable; +import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; @@ -345,6 +346,8 @@ public abstract class AbstractLayoutCodeProcessor { try { task.get(); } + catch (CancellationException ignored) { + } catch (Exception e) { LOG.error(e); } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/navigation/CtrlMouseHandler.java b/platform/lang-impl/src/com/intellij/codeInsight/navigation/CtrlMouseHandler.java index 1d95b055d70b..bb964bbe5795 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/navigation/CtrlMouseHandler.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/navigation/CtrlMouseHandler.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. @@ -558,7 +558,7 @@ public class CtrlMouseHandler extends AbstractProjectComponent { } } - final PsiNameIdentifierOwner element = GotoDeclarationAction.findElementToShowUsagesOf(editor, file, offset); + final PsiNameIdentifierOwner element = GotoDeclarationAction.findElementToShowUsagesOf(editor, offset); if (element != null) { PsiElement identifier = element.getNameIdentifier(); return new Info(identifier){ diff --git a/platform/lang-impl/src/com/intellij/codeInsight/navigation/actions/GotoDeclarationAction.java b/platform/lang-impl/src/com/intellij/codeInsight/navigation/actions/GotoDeclarationAction.java index a633b302c969..a7326672a407 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/navigation/actions/GotoDeclarationAction.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/navigation/actions/GotoDeclarationAction.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. @@ -83,8 +83,8 @@ public class GotoDeclarationAction extends BaseCodeInsightAction implements Code FeatureUsageTracker.getInstance().triggerFeatureUsed("navigation.goto.declaration"); if (elements.length != 1) { - if (elements.length == 0) { - PsiElement element = findElementToShowUsagesOf(editor, file, editor.getCaretModel().getOffset()); + if (elements.length == 0 && suggestCandidates(TargetElementUtilBase.findReference(editor, offset)).isEmpty()) { + PsiElement element = findElementToShowUsagesOf(editor, editor.getCaretModel().getOffset()); if (element != null) { ShowUsagesAction showUsages = (ShowUsagesAction)ActionManager.getInstance().getAction(ShowUsagesAction.ID); RelativePoint popupPosition = JBPopupFactory.getInstance().guessBestPopupLocation(editor); @@ -111,7 +111,7 @@ public class GotoDeclarationAction extends BaseCodeInsightAction implements Code } } - public static PsiNameIdentifierOwner findElementToShowUsagesOf(@NotNull Editor editor, @NotNull PsiFile file, int offset) { + public static PsiNameIdentifierOwner findElementToShowUsagesOf(@NotNull Editor editor, int offset) { PsiElement elementAt = TargetElementUtilBase.getInstance().findTargetElement(editor, TargetElementUtilBase.ELEMENT_NAME_ACCEPTED, offset); if (elementAt instanceof PsiNameIdentifierOwner) { return (PsiNameIdentifierOwner)elementAt; @@ -184,7 +184,8 @@ public class GotoDeclarationAction extends BaseCodeInsightAction implements Code return false; } - private static Collection suggestCandidates(final PsiReference reference) { + @NotNull + private static Collection suggestCandidates(@Nullable PsiReference reference) { if (reference == null) { return Collections.emptyList(); } diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ex/GlobalInspectionContextImpl.java b/platform/lang-impl/src/com/intellij/codeInspection/ex/GlobalInspectionContextImpl.java index 8aedfc4f4cae..4e3d7b53feb8 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/ex/GlobalInspectionContextImpl.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/ex/GlobalInspectionContextImpl.java @@ -325,12 +325,7 @@ public class GlobalInspectionContextImpl extends GlobalInspectionContextBase imp appendPairedInspectionsForUnfairTools(globalTools, globalSimpleTools, localTools); ((RefManagerImpl)getRefManager()).initializeAnnotators(); - ApplicationManager.getApplication().runReadAction(new Runnable() { - @Override - public void run() { - runGlobalTools(scope, inspectionManager, globalTools); - } - }); + runGlobalTools(scope, inspectionManager, globalTools); if (runGlobalToolsOnly) return; @@ -497,15 +492,16 @@ public class GlobalInspectionContextImpl extends GlobalInspectionContextBase imp return PsiDocumentManager.getInstance(getProject()).getDocument(file); } - private void runGlobalTools(@NotNull AnalysisScope scope, @NotNull InspectionManager inspectionManager, @NotNull List globalTools) { + private void runGlobalTools(@NotNull final AnalysisScope scope, @NotNull final InspectionManager inspectionManager, @NotNull List globalTools) { + LOG.assertTrue(!ApplicationManager.getApplication().isReadAccessAllowed(), "Must not run under read action, too unresponsive"); final List needRepeatSearchRequest = new ArrayList(); - final boolean surelyNoExternalUsages = scope.getScopeType() == AnalysisScope.PROJECT; + final boolean canBeExternalUsages = scope.getScopeType() != AnalysisScope.PROJECT; for (Tools tools : globalTools) { for (ScopeToolState state : tools.getTools()) { - InspectionToolWrapper toolWrapper = state.getTool(); - GlobalInspectionTool tool = (GlobalInspectionTool)toolWrapper.getTool(); - InspectionToolPresentation toolPresentation = getPresentation(toolWrapper); + final InspectionToolWrapper toolWrapper = state.getTool(); + final GlobalInspectionTool tool = (GlobalInspectionTool)toolWrapper.getTool(); + final InspectionToolPresentation toolPresentation = getPresentation(toolWrapper); try { if (tool.isGraphNeeded()) { try { @@ -516,11 +512,17 @@ public class GlobalInspectionContextImpl extends GlobalInspectionContextBase imp throw e; } } - tool.runInspection(scope, inspectionManager, this, toolPresentation); - //skip phase when we are sure that scope already contains everything - if (!surelyNoExternalUsages && tool.queryExternalUsagesRequests(inspectionManager, this, toolPresentation)) { - needRepeatSearchRequest.add(toolWrapper); - } + ApplicationManager.getApplication().runReadAction(new Runnable() { + @Override + public void run() { + tool.runInspection(scope, inspectionManager, GlobalInspectionContextImpl.this, toolPresentation); + //skip phase when we are sure that scope already contains everything + if (canBeExternalUsages && + tool.queryExternalUsagesRequests(inspectionManager, GlobalInspectionContextImpl.this, toolPresentation)) { + needRepeatSearchRequest.add(toolWrapper); + } + } + }); } catch (ProcessCanceledException e) { throw e; 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-impl/src/com/intellij/openapi/project/DumbServiceImpl.java b/platform/platform-impl/src/com/intellij/openapi/project/DumbServiceImpl.java index 9c571022140f..3c7435453b4e 100644 --- a/platform/platform-impl/src/com/intellij/openapi/project/DumbServiceImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/project/DumbServiceImpl.java @@ -269,7 +269,7 @@ public class DumbServiceImpl extends DumbService implements Disposable, Modifica runnable.run(); } catch (Throwable e) { - LOG.error(e); + LOG.error("Error executing task " + runnable, e); } } } diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/welcomeScreen/OpenSelectedProjectsAction.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/welcomeScreen/OpenSelectedProjectsAction.java index d04b2dea2799..fa944f8776b8 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/welcomeScreen/OpenSelectedProjectsAction.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/welcomeScreen/OpenSelectedProjectsAction.java @@ -15,28 +15,63 @@ */ package com.intellij.openapi.wm.impl.welcomeScreen; +import com.intellij.ide.ProjectGroupActionGroup; +import com.intellij.ide.ReopenProjectAction; import com.intellij.openapi.actionSystem.ActionPlaces; +import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.Presentation; -import com.intellij.openapi.project.DumbAwareAction; + +import java.awt.event.InputEvent; +import java.util.List; /** * @author Konstantin Bulenkov */ -public class OpenSelectedProjectsAction extends DumbAwareAction { +public class OpenSelectedProjectsAction extends RecentProjectsWelcomeScreenActionBase { @Override public void actionPerformed(AnActionEvent e) { - + List elements = getSelectedElements(e); + e = new AnActionEvent(e.getInputEvent(), e.getDataContext(), e.getPlace(), e.getPresentation(), e.getActionManager(), InputEvent.SHIFT_MASK); + for (AnAction element : elements) { + if (element instanceof ProjectGroupActionGroup) { + for (AnAction action : ((ProjectGroupActionGroup)element).getChildren(e)) { + action.actionPerformed(e); + } + } else { + element.actionPerformed(e); + } + } } @Override public void update(AnActionEvent e) { final Presentation presentation = e.getPresentation(); + List selectedElements = getSelectedElements(e); + boolean hasProject = false; + boolean hasGroup = false; + for (AnAction element : selectedElements) { + if (element instanceof ReopenProjectAction) { + hasProject = true; + } + if (element instanceof ProjectGroupActionGroup) { + hasGroup = true; + } + + if (hasGroup && hasProject) { + e.getPresentation().setEnabled(false); + return; + } + } if (ActionPlaces.WELCOME_SCREEN.equals(e.getPlace())) { presentation.setEnabledAndVisible(true); - presentation.setText("Open Selected"); - return; + if (selectedElements.size() == 1 && selectedElements.get(0) instanceof ProjectGroupActionGroup) { + presentation.setText("Open All Projects in Group"); + } else { + presentation.setText("Open Selected"); + } + } else { + presentation.setEnabledAndVisible(false); } - presentation.setEnabledAndVisible(false); } } diff --git a/platform/platform-impl/src/com/intellij/ui/EditorComboBox.java b/platform/platform-impl/src/com/intellij/ui/EditorComboBox.java index 769296b0e731..1d28ba8e297e 100644 --- a/platform/platform-impl/src/com/intellij/ui/EditorComboBox.java +++ b/platform/platform-impl/src/com/intellij/ui/EditorComboBox.java @@ -270,7 +270,7 @@ public class EditorComboBox extends JComboBox implements DocumentListener { @Override public void setItem(Object anObject) { if (anObject != null) { - EditorComboBox.this.setText((String)anObject); + EditorComboBox.this.setText(anObject.toString()); } else { EditorComboBox.this.setText(""); } diff --git a/platform/structuralsearch/testSource/com/intellij/structuralsearch/StructuralSearchTest.java b/platform/structuralsearch/testSource/com/intellij/structuralsearch/StructuralSearchTest.java index a67bfcdffa19..cec083fbb7fd 100644 --- a/platform/structuralsearch/testSource/com/intellij/structuralsearch/StructuralSearchTest.java +++ b/platform/structuralsearch/testSource/com/intellij/structuralsearch/StructuralSearchTest.java @@ -551,13 +551,36 @@ public class StructuralSearchTest extends StructuralSearchTestCase { String s9 = "int a[] = new int[] { 1,2,3,4};\n" + "int b[] = { 2,3,4,5 };\n" + - "Object[] c = new Object[] { \"\", null};"; + "Object[] c = new Object[] { \"\", null};\n" + + "Object[] d = {null, null};\n" + + "Object[] e = {};\n" + + "Object[] f = new Object[]{}\n" + + "String[] g = new String[]{}\n" + + "String[] h = new String[]{new String()}"; - assertEquals("Find array instantiation 1", 2, findMatchesCount(s9, "new '_ []{ '_* }")); - assertEquals("Find array instantiation 2", 2, findMatchesCount(s9, "new int []{ '_* }")); - assertEquals("Find array instantiation 3", 2, findMatchesCount(s9, "new 'a?:int [] { '_* }")); - assertEquals("Find array instantiation 4", 3, findMatchesCount(s9, "new '_? []{ '_* }")); - assertEquals("Find array instantiation 5", 1, findMatchesCount(s9, "new Object[] { '_* }")); + assertEquals("Find new array expressions, but no array initializer expressions", 5, + findMatchesCount(s9, "new '_ []{ '_* }")); + + assertEquals("Find new int array expressions, including array initializer expressions", 2, + findMatchesCount(s9, "new int []{ '_* }")); + + assertEquals("Find new int array expressions, including array initializer expressions using variable ", 2, + findMatchesCount(s9, "new 'a?:int [] { '_* }")); + + assertEquals("Find all new array expressions, including array initializers", 8, + findMatchesCount(s9, "new '_? []{ '_* }")); + + assertEquals("Find new Object array expressions, including array initializer expressions", 4, + findMatchesCount(s9, "new Object[] { '_* }")); + + assertEquals("Find only array initializer expressions", 3, + findMatchesCount(s9, "new '_{0,0}[] { '_* }")); + + assertEquals("Find only int array initializer expressions", 1, + findMatchesCount(s9, "new '_{0,0}:int [] { '_* }")); + + assertEquals("Try to find String array initializer expressions", 0, + findMatchesCount(s9, "new '_{0,0}:String [] { '_* }")); } public void testLiteral() { 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/vcs-impl/src/com/intellij/openapi/vcs/history/FileHistoryPanelImpl.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/history/FileHistoryPanelImpl.java index bdf630fc1075..5632df4dcbd2 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/history/FileHistoryPanelImpl.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/history/FileHistoryPanelImpl.java @@ -64,6 +64,7 @@ import com.intellij.ui.content.ContentManager; import com.intellij.ui.dualView.*; import com.intellij.ui.table.TableView; import com.intellij.util.*; +import com.intellij.util.containers.ContainerUtil; import com.intellij.util.text.DateFormatUtil; import com.intellij.util.ui.*; import org.jetbrains.annotations.NonNls; @@ -764,12 +765,13 @@ public class FileHistoryPanelImpl extends PanelWithActionsAndCloseButton { final MyDiffAction diffAction = new MyDiffAction(); result.add(diffAction); if (!popup) { - diffAction.registerCustomShortcutSet(new CustomShortcutSet( - CommonShortcuts.getDiff().getShortcuts() [0], - CommonShortcuts.DOUBLE_CLICK_1.getShortcuts() [0]), myDualView.getFlatView()); - diffAction.registerCustomShortcutSet(new CustomShortcutSet( - CommonShortcuts.getDiff().getShortcuts() [0], - CommonShortcuts.DOUBLE_CLICK_1.getShortcuts() [0]), myDualView.getTreeView()); + List shortcuts = new SmartList(); + ContainerUtil.addAll(shortcuts, CommonShortcuts.getDiff().getShortcuts()); + ContainerUtil.addAll(shortcuts, CommonShortcuts.DOUBLE_CLICK_1.getShortcuts()); + CustomShortcutSet shortcutSet = new CustomShortcutSet(ContainerUtil.toArray(shortcuts, new Shortcut[shortcuts.size()])); + + diffAction.registerCustomShortcutSet(shortcutSet, myDualView.getFlatView()); + diffAction.registerCustomShortcutSet(shortcutSet, myDualView.getTreeView()); } else { diffAction.registerCustomShortcutSet(CommonShortcuts.getDiff(), this); diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/settings/MergedCompositeConfigurable.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/settings/MergedCompositeConfigurable.java index ccfc2708cb9e..80f8c5379eb8 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/settings/MergedCompositeConfigurable.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/settings/MergedCompositeConfigurable.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 com.intellij.xdebugger.impl.settings; import com.intellij.openapi.options.Configurable; @@ -121,7 +136,7 @@ class MergedCompositeConfigurable implements SearchableConfigurable { @NotNull static JPanel createPanel(boolean isUseTitledBorder) { int verticalGap = TitledSeparator.TOP_INSET; - JPanel panel = new JPanel(new VerticalFlowLayout(0, isUseTitledBorder ? 0 : verticalGap)); + JPanel panel = new JPanel(new VerticalFlowLayout(VerticalFlowLayout.TOP, 0, isUseTitledBorder ? 0 : verticalGap, true, true)); // VerticalFlowLayout incorrectly use vertical gap as top inset if (!isUseTitledBorder) { panel.setBorder(new EmptyBorder(-verticalGap, 0, 0, 0)); diff --git a/plugins/git4idea/src/META-INF/plugin.xml b/plugins/git4idea/src/META-INF/plugin.xml index c08dd9e5ddae..cbe70496408e 100644 --- a/plugins/git4idea/src/META-INF/plugin.xml +++ b/plugins/git4idea/src/META-INF/plugin.xml @@ -157,6 +157,7 @@ + affectedRoots, final List exceptions) throws VcsException { - GitLineHandler h = createHandler(project, gitRoots, defaultRoot); + final GitLineHandler h = createHandler(project, gitRoots, defaultRoot); if (h == null) { return; } @@ -63,34 +64,32 @@ public abstract class GitRebaseActionBase extends GitRepositoryAction { affectedRoots.add(root); service.configureHandler(h, editor.getHandlerNo()); - GitTask task = new GitTask(project, h, GitBundle.getString("rebasing.title")); - task.executeInBackground(false, new GitTaskResultHandlerAdapter() { + new Task.Backgroundable(project, GitBundle.getString("rebasing.title"), false) { @Override - protected void run(GitTaskResult taskResult) { + public void run(@NotNull ProgressIndicator indicator) { AccessToken token = DvcsUtil.workingTreeChangeStarted(project); try { + GitCommandResult result = ServiceManager.getService(Git.class).runCommand(h); editor.close(); GitRepositoryManager manager = GitUtil.getRepositoryManager(project); manager.updateRepository(root); root.refresh(false, true); - notifyAboutResult(taskResult, resultListener, exceptions, project); + notifyAboutResult(result, resultListener, exceptions, project); } finally { DvcsUtil.workingTreeChangeFinished(project, token); } } - }); + }.queue(); } - private static void notifyAboutResult(GitTaskResult taskResult, - GitRebaseLineListener resultListener, - List exceptions, - Project project) { - if (taskResult == GitTaskResult.CANCELLED) { - return; - } + private static void notifyAboutResult(@NotNull GitCommandResult commandResult, + @NotNull GitRebaseLineListener resultListener, + @NotNull List exceptions, + @NotNull Project project) { final GitRebaseLineListener.Result result = resultListener.getResult(); String messageId; + String message = null; boolean isError = true; switch (result.status) { case CONFLICT: @@ -98,6 +97,7 @@ public abstract class GitRebaseActionBase extends GitRepositoryAction { break; case ERROR: messageId = "rebase.result.error"; + message = commandResult.getErrorOutputAsHtmlString(); break; case CANCELLED: // we do not need to show a message if editing was cancelled. @@ -112,18 +112,12 @@ public abstract class GitRebaseActionBase extends GitRepositoryAction { messageId = "rebase.result.success"; break; default: - messageId = null; + throw new IllegalStateException("Unsupported rebase result: " + result.status); } - String message; - String title; - if (messageId != null) { + String title = GitBundle.message(messageId + ".title"); + if (message == null) { message = GitBundle.message(messageId, result.current, result.total); - title = GitBundle.message(messageId + ".title"); - } - else { - message = "Rebase finished: " + StringUtil.capitalize(StringUtil.toLowerCase(result.status.name())); - title = ""; } if (isError) { diff --git a/plugins/git4idea/src/git4idea/commands/Git.java b/plugins/git4idea/src/git4idea/commands/Git.java index c56a3d729fe0..5b7aca309edc 100644 --- a/plugins/git4idea/src/git4idea/commands/Git.java +++ b/plugins/git4idea/src/git4idea/commands/Git.java @@ -42,6 +42,14 @@ public interface Git { @NotNull GitCommandResult runCommand(@NotNull Computable handlerConstructor); + /** + * A generic method to run a Git command, when existing methods are not sufficient.
+ * Can be used instead of {@link #runCommand(Computable)} if the operation will not need to be repeated for sure + * (e.g. it is a completely local operation). + */ + @NotNull + GitCommandResult runCommand(@NotNull GitLineHandler handler); + @NotNull GitCommandResult init(@NotNull Project project, @NotNull VirtualFile root, @NotNull GitLineHandlerListener... listeners); diff --git a/plugins/git4idea/src/git4idea/commands/GitImpl.java b/plugins/git4idea/src/git4idea/commands/GitImpl.java index a658879a9ed1..93ec7cde808b 100644 --- a/plugins/git4idea/src/git4idea/commands/GitImpl.java +++ b/plugins/git4idea/src/git4idea/commands/GitImpl.java @@ -47,6 +47,7 @@ import static java.util.Collections.singleton; * * @author Kirill Likhodedov */ +@SuppressWarnings("StringToUpperCaseOrToLowerCaseWithoutLocale") public class GitImpl implements Git { private final Logger LOG = Logger.getInstance(Git.class); @@ -624,13 +625,24 @@ public class GitImpl implements Git { public GitCommandResult runCommand(@NotNull Computable handlerConstructor) { return run(handlerConstructor); } - + + @NotNull + @Override + public GitCommandResult runCommand(@NotNull final GitLineHandler handler) { + return runCommand(new Computable() { + @Override + public GitLineHandler compute() { + return handler; + } + }); + } + /** * Check if the line looks line an error message */ private static boolean isError(String text) { for (String indicator : ERROR_INDICATORS) { - if (text.startsWith(indicator.toLowerCase())) { + if (text.trim().toLowerCase().startsWith(indicator.toLowerCase())) { return true; } } @@ -640,7 +652,7 @@ public class GitImpl implements Git { // could be upper-cased, so should check case-insensitively public static final String[] ERROR_INDICATORS = { "error", "remote: error", "fatal", - "Cannot apply", "Could not", "Interactive rebase already started", "refusing to pull", "cannot rebase:", "conflict", + "Cannot", "Could not", "Interactive rebase already started", "refusing to pull", "cannot rebase:", "conflict", "unable" }; } diff --git a/plugins/git4idea/src/git4idea/config/GitRebaseSettings.java b/plugins/git4idea/src/git4idea/config/GitRebaseSettings.java new file mode 100644 index 000000000000..c1e498957c62 --- /dev/null +++ b/plugins/git4idea/src/git4idea/config/GitRebaseSettings.java @@ -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 git4idea.config; + +import com.intellij.openapi.components.PersistentStateComponent; +import com.intellij.openapi.components.State; +import com.intellij.openapi.components.Storage; +import com.intellij.openapi.components.StoragePathMacros; +import org.jetbrains.annotations.Nullable; + +@State(name = "Git.Rebase.Settings", storages = {@Storage(file = StoragePathMacros.WORKSPACE_FILE)}) +public class GitRebaseSettings implements PersistentStateComponent { + + private State myState = new State(); + + public static class State { + public boolean INTERACTIVE = true; + public boolean PRESERVE_MERGES = false; + public boolean SHOW_TAGS = false; + public boolean SHOW_REMOTE_BRANCHES = false; + public String ONTO = null; + } + + @Nullable + @Override + public State getState() { + return myState; + } + + @Override + public void loadState(State state) { + myState = state; + } + + public boolean isInteractive() { + return myState.INTERACTIVE; + } + + public void setInteractive(boolean interactive) { + myState.INTERACTIVE = interactive; + } + + public boolean isPreserveMerges() { + return myState.PRESERVE_MERGES; + } + + public void setPreserveMerges(boolean preserveMerges) { + myState.PRESERVE_MERGES = preserveMerges; + } + + public boolean showTags() { + return myState.SHOW_TAGS; + } + + public void setShowTags(boolean showTags) { + myState.SHOW_TAGS = showTags; + } + + public boolean showRemoteBranches() { + return myState.SHOW_REMOTE_BRANCHES; + } + + public void setShowRemoteBranches(boolean showRemoteBranches) { + myState.SHOW_REMOTE_BRANCHES = showRemoteBranches; + } + + @Nullable + public String getOnto() { + return myState.ONTO; + } + + public void setOnto(@Nullable String onto) { + myState.ONTO = onto; + } + +} diff --git a/plugins/git4idea/src/git4idea/i18n/GitBundle.properties b/plugins/git4idea/src/git4idea/i18n/GitBundle.properties index 7b1ad1821161..19d32daf953d 100644 --- a/plugins/git4idea/src/git4idea/i18n/GitBundle.properties +++ b/plugins/git4idea/src/git4idea/i18n/GitBundle.properties @@ -290,7 +290,6 @@ rebase.result.cancelled=The rebase process was cancelled. rebase.result.conflict.title=Rebase Suspended rebase.result.conflict=Stopped rebasing because of conflict ({0}/{1}).\nResolve conflicts and continue the rebase process. rebase.result.error.title=Rebase Error -rebase.result.error=Stopped rebasing because of error while rebasing ({0}/{1}).\nCheck vcs console for details. rebase.show.remote.branches.tooltip=If selected, remote branches are shown in drop down as well. rebase.show.remote.branches=Show Re&mote Branches rebase.show.tags.tooltip=Show tags in \"from\" and \"onto\" comboboxes. diff --git a/plugins/git4idea/src/git4idea/rebase/GitInteractiveRebaseEditorHandler.java b/plugins/git4idea/src/git4idea/rebase/GitInteractiveRebaseEditorHandler.java index b07cd4e23f0f..0c1dba06ed74 100644 --- a/plugins/git4idea/src/git4idea/rebase/GitInteractiveRebaseEditorHandler.java +++ b/plugins/git4idea/src/git4idea/rebase/GitInteractiveRebaseEditorHandler.java @@ -15,8 +15,10 @@ */ package git4idea.rebase; +import com.intellij.CommonBundle; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.Ref; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.ui.UIUtil; @@ -24,6 +26,7 @@ import git4idea.commands.GitHandler; import org.jetbrains.annotations.NotNull; import java.io.Closeable; +import java.util.List; /** * The handler for rebase editor request. The handler shows {@link git4idea.rebase.GitRebaseEditor} @@ -110,21 +113,33 @@ public class GitInteractiveRebaseEditorHandler implements Closeable, GitRebaseEd isSuccess.set(true); return; } - else { - isSuccess.set(false); - } } else { setRebaseEditorShown(); - GitRebaseEditor editor = new GitRebaseEditor(myProject, myRoot, path); - if (editor.showAndGet()) { - editor.save(); - isSuccess.set(true); - return; + GitInteractiveRebaseFile rebaseFile = new GitInteractiveRebaseFile(myProject, myRoot, path); + try { + List entries = rebaseFile.load(); + GitRebaseEditor editor = new GitRebaseEditor(myProject, myRoot, entries); + if (editor.showAndGet()) { + rebaseFile.save(editor.getEntries()); + isSuccess.set(true); + return; + } + else { + rebaseFile.cancel(); + } } - else { - editor.cancel(); - isSuccess.set(true); + catch (GitInteractiveRebaseFile.NoopException e) { + LOG.info("Noop situation while rebasing " + myRoot); + String message = "There are no commits to rebase because the current branch is directly below the base branch " + + "(the 'noop' situation).\n" + + "Do you want to continue (this will reset the current branch to the base branch)?"; + int rebase = Messages.showOkCancelDialog(myProject, message, "Git Rebase", CommonBundle.getOkButtonText(), + CommonBundle.getCancelButtonText(), Messages.getQuestionIcon()); + if (rebase == Messages.OK) { + isSuccess.set(true); + return; + } } } } diff --git a/plugins/git4idea/src/git4idea/rebase/GitInteractiveRebaseFile.java b/plugins/git4idea/src/git4idea/rebase/GitInteractiveRebaseFile.java new file mode 100644 index 000000000000..6964fb73bb55 --- /dev/null +++ b/plugins/git4idea/src/git4idea/rebase/GitInteractiveRebaseFile.java @@ -0,0 +1,108 @@ +/* + * 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 git4idea.rebase; + +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.SystemInfo; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.containers.ContainerUtil; +import git4idea.config.GitConfigUtil; +import git4idea.util.StringScanner; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; + +import java.io.*; +import java.util.List; + +class GitInteractiveRebaseFile { + @NonNls private static final String CYGDRIVE_PREFIX = "/cygdrive/"; + + @NotNull private final Project myProject; + @NotNull private final VirtualFile myRoot; + @NotNull private final String myFile; + + GitInteractiveRebaseFile(@NotNull Project project, @NotNull VirtualFile root, @NotNull String rebaseFilePath) { + myProject = project; + myRoot = root; + myFile = adjustFilePath(rebaseFilePath); + } + + @NotNull + public List load() throws IOException, NoopException { + String encoding = GitConfigUtil.getLogEncoding(myProject, myRoot); + List entries = ContainerUtil.newArrayList(); + final StringScanner s = new StringScanner(FileUtil.loadFile(new File(myFile), encoding)); + boolean noop = false; + while (s.hasMoreData()) { + if (s.isEol() || s.startsWith('#')) { + s.nextLine(); + continue; + } + if (s.startsWith("noop")) { + noop = true; + s.nextLine(); + continue; + } + String action = s.spaceToken(); + String hash = s.spaceToken(); + String comment = s.line(); + + entries.add(new GitRebaseEntry(action, hash, comment)); + } + if (noop && entries.isEmpty()) { + throw new NoopException(); + } + return entries; + } + + public void cancel() throws IOException { + PrintWriter out = new PrintWriter(new FileWriter(myFile)); + try { + out.println("# rebase is cancelled"); + } + finally { + out.close(); + } + } + + public void save(@NotNull List entries) throws IOException { + String encoding = GitConfigUtil.getLogEncoding(myProject, myRoot); + PrintWriter out = new PrintWriter(new OutputStreamWriter(new FileOutputStream(myFile), encoding)); + try { + for (GitRebaseEntry e : entries) { + if (e.getAction() != GitRebaseEntry.Action.skip) { + out.println(e.getAction().toString() + " " + e.getCommit() + " " + e.getSubject()); + } + } + } + finally { + out.close(); + } + } + + @NotNull + private static String adjustFilePath(@NotNull String file) { + if (SystemInfo.isWindows && file.startsWith(CYGDRIVE_PREFIX)) { + final int prefixSize = CYGDRIVE_PREFIX.length(); + return file.substring(prefixSize, prefixSize + 1) + ":" + file.substring(prefixSize + 1); + } + return file; + } + + static class NoopException extends Exception { + } +} diff --git a/plugins/git4idea/src/git4idea/rebase/GitRebaseDialog.java b/plugins/git4idea/src/git4idea/rebase/GitRebaseDialog.java index 649c25486ce6..9a3119803309 100644 --- a/plugins/git4idea/src/git4idea/rebase/GitRebaseDialog.java +++ b/plugins/git4idea/src/git4idea/rebase/GitRebaseDialog.java @@ -15,10 +15,12 @@ */ package git4idea.rebase; +import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.ComboBox; import com.intellij.openapi.ui.DialogWrapper; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.ui.DocumentAdapter; @@ -27,12 +29,15 @@ import git4idea.branch.GitBranchUtil; import git4idea.commands.GitCommand; import git4idea.commands.GitLineHandler; import git4idea.config.GitConfigUtil; +import git4idea.config.GitRebaseSettings; import git4idea.i18n.GitBundle; import git4idea.merge.GitMergeUtil; import git4idea.repo.GitRemote; import git4idea.repo.GitRepository; import git4idea.ui.GitReferenceValidator; import git4idea.util.GitUIUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import javax.swing.*; import javax.swing.event.DocumentEvent; @@ -115,7 +120,7 @@ public class GitRebaseDialog extends DialogWrapper { /** * The current branch */ - protected GitBranch myCurrentBranch; + @Nullable protected GitBranch myCurrentBranch; /** * The tags */ @@ -128,6 +133,9 @@ public class GitRebaseDialog extends DialogWrapper { * The validator for from field */ private final GitReferenceValidator myFromValidator; + @NotNull private final GitRebaseSettings mySettings; + + @Nullable private final String myOriginalOntoBranch; /** * A constructor @@ -142,6 +150,7 @@ public class GitRebaseDialog extends DialogWrapper { setOKButtonText(GitBundle.getString("rebase.button")); init(); myProject = project; + mySettings = ServiceManager.getService(myProject, GitRebaseSettings.class); final Runnable validateRunnable = new Runnable() { public void run() { validateFields(); @@ -157,11 +166,50 @@ public class GitRebaseDialog extends DialogWrapper { validateFields(); } }); + setupBranches(); setupStrategy(); + + myInteractiveCheckBox.setSelected(mySettings.isInteractive()); + myPreserveMergesCheckBox.setSelected(mySettings.isPreserveMerges()); + myShowTagsCheckBox.setSelected(mySettings.showTags()); + myShowRemoteBranchesCheckBox.setSelected(mySettings.showRemoteBranches()); + overwriteOntoForCurrentBranch(mySettings); + + myOriginalOntoBranch = GitUIUtil.getTextField(myOntoComboBox).getText(); + validateFields(); } + @Nullable + @Override + public JComponent getPreferredFocusedComponent() { + return myOntoComboBox; + } + + private void overwriteOntoForCurrentBranch(@NotNull GitRebaseSettings settings) { + String onto = settings.getOnto(); + if (onto != null && !onto.equals(myBranchComboBox.getSelectedItem())) { + if (!isValidRevision(onto)) { + mySettings.setOnto(null); + } + else { + myOntoComboBox.setSelectedItem(onto); + } + } + } + + private boolean isValidRevision(@NotNull String revisionExpression) { + try { + GitRevisionNumber.resolve(myProject, gitRoot(), revisionExpression); + return true; + } + catch (VcsException e) { + LOG.debug(e); + return false; + } + } + public GitLineHandler handler() { GitLineHandler h = new GitLineHandler(myProject, gitRoot(), GitCommand.REBASE); h.setStdoutSuppressed(false); @@ -195,6 +243,27 @@ public class GitRebaseDialog extends DialogWrapper { return h; } + @Override + protected void doOKAction() { + try { + rememberFields(); + } + finally { + super.doOKAction(); + } + } + + private void rememberFields() { + mySettings.setInteractive(myInteractiveCheckBox.isSelected()); + mySettings.setPreserveMerges(myPreserveMergesCheckBox.isSelected()); + mySettings.setShowTags(myShowTagsCheckBox.isSelected()); + mySettings.setShowRemoteBranches(myShowRemoteBranchesCheckBox.isSelected()); + String onto = StringUtil.nullize(GitUIUtil.getTextField(myOntoComboBox).getText(), true); + if (onto != null && !onto.equals(myOriginalOntoBranch)) { + mySettings.setOnto(onto); + } + } + /** * Setup strategy */ diff --git a/plugins/git4idea/src/git4idea/rebase/GitRebaseEditor.java b/plugins/git4idea/src/git4idea/rebase/GitRebaseEditor.java index 7514aa940bbe..51edcf439623 100644 --- a/plugins/git4idea/src/git4idea/rebase/GitRebaseEditor.java +++ b/plugins/git4idea/src/git4idea/rebase/GitRebaseEditor.java @@ -17,12 +17,12 @@ package git4idea.rebase; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; -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.ColoredTableCellRenderer; import com.intellij.ui.TableSpeedSearch; +import com.intellij.ui.speedSearch.SpeedSearchUtil; import com.intellij.ui.table.JBTable; import com.intellij.util.ArrayUtil; import com.intellij.util.ListWithSelection; @@ -31,10 +31,8 @@ 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.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; @@ -46,8 +44,7 @@ import javax.swing.table.AbstractTableModel; import javax.swing.table.TableColumn; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import java.io.*; -import java.util.ArrayList; +import java.io.IOException; import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -81,45 +78,21 @@ public class GitRebaseEditor extends DialogWrapper { * Table model */ private final MyTableModel myTableModel; - /** - * The file name - */ - private final String myFile; - /** - * The project - */ - private final Project myProject; - /** - * The git root - */ - private final VirtualFile myGitRoot; - /** - * The cygwin drive prefix - */ - @NonNls private static final String CYGDRIVE_PREFIX = "/cygdrive/"; /** * The constructor * * @param project the project * @param gitRoot the git root - * @param file the file to edit + * @param entries the file to edit * @throws IOException if file could not be loaded */ - protected GitRebaseEditor(final Project project, final VirtualFile gitRoot, String file) throws IOException { + protected GitRebaseEditor(final Project project, final VirtualFile gitRoot, List entries) throws IOException { super(project, true); - myProject = project; - myGitRoot = gitRoot; setTitle(GitBundle.getString("rebase.editor.title")); setOKButtonText(GitBundle.getString("rebase.editor.button")); - if (SystemInfo.isWindows && file.startsWith(CYGDRIVE_PREFIX)) { - final int prefixSize = CYGDRIVE_PREFIX.length(); - file = file.substring(prefixSize, prefixSize + 1) + ":" + file.substring(prefixSize + 1); - } - myFile = file; - myTableModel = new MyTableModel(); - myTableModel.load(file); + myTableModel = new MyTableModel(entries); myCommitsTable.setModel(myTableModel); myCommitsTable.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION); myCommitsTable.setIntercellSpacing(JBUI.emptySize()); @@ -132,6 +105,14 @@ public class GitRebaseEditor extends DialogWrapper { actionColumn.setCellEditor(new DefaultCellEditor(editorComboBox)); actionColumn.setCellRenderer(ComboBoxTableCellRenderer.INSTANCE); + myCommitsTable.setDefaultRenderer(String.class, new ColoredTableCellRenderer() { + @Override + protected void customizeCellRenderer(JTable table, Object value, boolean selected, boolean hasFocus, int row, int column) { + append(value.toString()); + SpeedSearchUtil.applySpeedSearchHighlighting(myCommitsTable, this, true, selected); + } + }); + myCommitsTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() { public void valueChanged(final ListSelectionEvent e) { myViewButton.setEnabled(myCommitsTable.getSelectedRowCount() == 1); @@ -215,15 +196,6 @@ public class GitRebaseEditor extends DialogWrapper { setOKActionEnabled(true); } - /** - * Save entries back to the file - * - * @throws IOException if there is IO problem with saving - */ - public void save() throws IOException { - myTableModel.save(myFile); - } - /** * {@inheritDoc} */ @@ -247,13 +219,9 @@ public class GitRebaseEditor extends DialogWrapper { return "reference.VersionControl.Git.RebaseCommits"; } - /** - * Cancel rebase - * - * @throws IOException if file cannot be reset to empty one - */ - public void cancel() throws IOException { - myTableModel.cancel(myFile); + @NotNull + public List getEntries() { + return myTableModel.myEntries; } @@ -274,12 +242,13 @@ public class GitRebaseEditor extends DialogWrapper { */ private static final int SUBJECT = 2; - /** - * The entries - */ - final List myEntries = new ArrayList(); + @NotNull private final List myEntries; private int[] myLastEditableSelectedRows = new int[]{}; + MyTableModel(@NotNull List entries) { + myEntries = entries; + } + /** * {@inheritDoc} */ @@ -357,7 +326,7 @@ public class GitRebaseEditor extends DialogWrapper { } private void setSelection(ContiguousIntIntervalTracker intervalBuilder) { - myCommitsTable.getSelectionModel().setSelectionInterval( intervalBuilder.getMin() , intervalBuilder.getMax() ); + myCommitsTable.getSelectionModel().setSelectionInterval(intervalBuilder.getMin(), intervalBuilder.getMax()); } private void setRowAction(Object aValue, int rowIndex, int columnIndex) { @@ -375,66 +344,6 @@ public class GitRebaseEditor extends DialogWrapper { return columnIndex == ACTION; } - /** - * Load data from the file - * - * @param file the file to load - * @throws IOException if file could not be loaded - */ - public void load(final String file) throws IOException { - String encoding = GitConfigUtil.getLogEncoding(myProject, myGitRoot); - final StringScanner s = new StringScanner(FileUtil.loadFile(new File(file), encoding)); - while (s.hasMoreData()) { - if (s.isEol() || s.startsWith('#') || s.startsWith("noop")) { - s.nextLine(); - continue; - } - String action = s.spaceToken(); - String hash = s.spaceToken(); - String comment = s.line(); - myEntries.add(new GitRebaseEntry(action, hash, comment)); - } - } - - /** - * Save text to the file - * - * @param file the file to save to - * @throws IOException if there is IO problem - */ - public void save(final String file) throws IOException { - String encoding = GitConfigUtil.getLogEncoding(myProject, myGitRoot); - PrintWriter out = new PrintWriter(new OutputStreamWriter(new FileOutputStream(file), encoding)); - try { - for (GitRebaseEntry e : myEntries) { - if (e.getAction() != GitRebaseEntry.Action.skip) { - out.println(e.getAction().toString() + " " + e.getCommit() + " " + e.getSubject()); - } - } - } - finally { - out.close(); - } - } - - /** - * Save text to the file - * - * @param file the file to save to - * @throws IOException if there is IO problem - */ - public void cancel(final String file) throws IOException { - PrintWriter out = new PrintWriter(new FileWriter(file)); - try { - //noinspection HardCodedStringLiteral - out.println("# rebase is cancelled"); - } - finally { - out.close(); - } - } - - public void moveRows(int[] rows, MoveDirection direction) { myCommitsTable.removeEditor(); diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/compiler/GreclipseConfigurable.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/compiler/GreclipseConfigurable.java index 8bd61c0b6f61..1227d0aa1cb8 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/compiler/GreclipseConfigurable.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/compiler/GreclipseConfigurable.java @@ -20,9 +20,11 @@ import com.intellij.openapi.options.Configurable; import com.intellij.openapi.options.ConfigurationException; import com.intellij.openapi.ui.TextFieldWithBrowseButton; import com.intellij.openapi.util.Comparing; +import com.intellij.openapi.util.io.FileUtil; import com.intellij.ui.RawCommandLineEditor; import com.intellij.ui.components.JBCheckBox; import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jps.incremental.groovy.GreclipseSettings; @@ -65,21 +67,26 @@ public class GreclipseConfigurable implements Configurable { @Override public boolean isModified() { - return !Comparing.equal(myJarPath.getText(), mySettings.greclipsePath) || + return !Comparing.equal(getExternalizableJarPath(), mySettings.greclipsePath) || !Comparing.equal(myCmdLineParams.getText(), mySettings.cmdLineParams) || !Comparing.equal(myGenerateDebugInfo.isSelected(), mySettings.debugInfo); } @Override public void apply() throws ConfigurationException { - mySettings.greclipsePath = myJarPath.getText(); + mySettings.greclipsePath = getExternalizableJarPath(); mySettings.cmdLineParams = myCmdLineParams.getText(); mySettings.debugInfo = myGenerateDebugInfo.isSelected(); } + @NotNull + private String getExternalizableJarPath() { + return FileUtil.toSystemIndependentName(myJarPath.getText()); + } + @Override public void reset() { - myJarPath.setText(mySettings.greclipsePath); + myJarPath.setText(FileUtil.toSystemDependentName(mySettings.greclipsePath)); myCmdLineParams.setText(mySettings.cmdLineParams); myGenerateDebugInfo.setSelected(mySettings.debugInfo); } diff --git a/plugins/hg4idea/src/META-INF/plugin.xml b/plugins/hg4idea/src/META-INF/plugin.xml index ce10a3601a37..1dcb4833ceeb 100644 --- a/plugins/hg4idea/src/META-INF/plugin.xml +++ b/plugins/hg4idea/src/META-INF/plugin.xml @@ -70,7 +70,7 @@ - + diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/action/HgAbstractGlobalAction.java b/plugins/hg4idea/src/org/zmlx/hg4idea/action/HgAbstractGlobalAction.java index 324cf104f0e4..794e6d81b977 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/action/HgAbstractGlobalAction.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/action/HgAbstractGlobalAction.java @@ -12,10 +12,10 @@ // limitations under the License. package org.zmlx.hg4idea.action; -import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.CommonDataKeys; import com.intellij.openapi.actionSystem.DataContext; +import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; import com.intellij.openapi.vcs.ProjectLevelVcsManager; import com.intellij.openapi.vfs.VirtualFile; @@ -25,19 +25,12 @@ import org.zmlx.hg4idea.repo.HgRepository; import org.zmlx.hg4idea.repo.HgRepositoryManager; import org.zmlx.hg4idea.util.HgUtil; -import javax.swing.*; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; -public abstract class HgAbstractGlobalAction extends AnAction { - protected HgAbstractGlobalAction(Icon icon) { - super(icon); - } - - protected HgAbstractGlobalAction() { - } +public abstract class HgAbstractGlobalAction extends DumbAwareAction { public void actionPerformed(@NotNull AnActionEvent event) { final DataContext dataContext = event.getDataContext(); @@ -82,5 +75,4 @@ public abstract class HgAbstractGlobalAction extends AnAction { } return true; } - } diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/action/HgAbstractGlobalSingleRepoAction.java b/plugins/hg4idea/src/org/zmlx/hg4idea/action/HgAbstractGlobalSingleRepoAction.java index bfb2d7715e68..51ca8c598f59 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/action/HgAbstractGlobalSingleRepoAction.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/action/HgAbstractGlobalSingleRepoAction.java @@ -20,20 +20,11 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.zmlx.hg4idea.repo.HgRepository; -import javax.swing.*; import java.util.Collection; import java.util.List; public abstract class HgAbstractGlobalSingleRepoAction extends HgAbstractGlobalAction { - public HgAbstractGlobalSingleRepoAction(Icon icon) { - super(icon); - } - - public HgAbstractGlobalSingleRepoAction() { - super(); - } - @Override protected void execute(@NotNull Project project, @NotNull Collection repositories, diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/action/HgPullAction.java b/plugins/hg4idea/src/org/zmlx/hg4idea/action/HgPullAction.java index 4d4a72894bdf..11fa9d5ffc32 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/action/HgPullAction.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/action/HgPullAction.java @@ -12,7 +12,6 @@ // limitations under the License. package org.zmlx.hg4idea.action; -import com.intellij.icons.AllIcons; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.Task; import com.intellij.openapi.project.Project; @@ -26,9 +25,6 @@ import org.zmlx.hg4idea.util.HgErrorUtil; import java.util.Collection; public class HgPullAction extends HgAbstractGlobalSingleRepoAction { - public HgPullAction() { - super(AllIcons.Actions.CheckOut); - } @Override protected void execute(@NotNull final Project project, @NotNull Collection repos, @Nullable HgRepository selectedRepo) { diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/util/HgUtil.java b/plugins/hg4idea/src/org/zmlx/hg4idea/util/HgUtil.java index bb99bff7ea13..4663940bd822 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/util/HgUtil.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/util/HgUtil.java @@ -16,12 +16,6 @@ import com.intellij.dvcs.DvcsUtil; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.fileEditor.FileDocumentManager; -import com.intellij.openapi.fileEditor.FileEditor; -import com.intellij.openapi.fileEditor.FileEditorManager; -import com.intellij.openapi.fileEditor.TextEditor; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.Couple; @@ -38,9 +32,6 @@ import com.intellij.openapi.vfs.CharsetToolkit; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.openapi.wm.StatusBar; -import com.intellij.openapi.wm.WindowManager; -import com.intellij.openapi.wm.impl.status.StatusBarUtil; import com.intellij.ui.GuiUtils; import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtil; @@ -284,36 +275,9 @@ public abstract class HgUtil { return getHgRootOrThrow(project, VcsUtil.getFilePath(file.getPath())); } - /** - * Returns the currently selected file, based on which HgBranch components will identify the current repository root. - */ - @Nullable - public static VirtualFile getSelectedFile(@NotNull Project project) { - StatusBar statusBar = WindowManager.getInstance().getStatusBar(project); - final FileEditor fileEditor = StatusBarUtil.getCurrentFileEditor(project, statusBar); - VirtualFile result = null; - if (fileEditor != null) { - if (fileEditor instanceof TextEditor) { - Document document = ((TextEditor)fileEditor).getEditor().getDocument(); - result = FileDocumentManager.getInstance().getFile(document); - } - } - - if (result == null) { - final FileEditorManager manager = FileEditorManager.getInstance(project); - if (manager != null) { - Editor editor = manager.getSelectedTextEditor(); - if (editor != null) { - result = FileDocumentManager.getInstance().getFile(editor.getDocument()); - } - } - } - return result; - } - @Nullable public static VirtualFile getRootForSelectedFile(@NotNull Project project) { - VirtualFile selectedFile = getSelectedFile(project); + VirtualFile selectedFile = DvcsUtil.getSelectedFile(project); if (selectedFile != null) { return getHgRootOrNull(project, selectedFile); } diff --git a/plugins/junit/src/com/intellij/execution/junit/TestObject.java b/plugins/junit/src/com/intellij/execution/junit/TestObject.java index 8dab606da063..1085ccb2199c 100644 --- a/plugins/junit/src/com/intellij/execution/junit/TestObject.java +++ b/plugins/junit/src/com/intellij/execution/junit/TestObject.java @@ -17,10 +17,7 @@ package com.intellij.execution.junit; import com.intellij.execution.*; -import com.intellij.execution.configurations.JavaParameters; -import com.intellij.execution.configurations.ParametersList; -import com.intellij.execution.configurations.RunnerSettings; -import com.intellij.execution.configurations.RuntimeConfigurationException; +import com.intellij.execution.configurations.*; import com.intellij.execution.junit2.TestProxy; import com.intellij.execution.junit2.segments.DeferredActionsQueue; import com.intellij.execution.junit2.segments.DeferredActionsQueueImpl; @@ -33,8 +30,7 @@ import com.intellij.execution.junit2.ui.model.CompletionEvent; import com.intellij.execution.junit2.ui.model.JUnitRunningModel; import com.intellij.execution.junit2.ui.model.RootTestInfo; import com.intellij.execution.junit2.ui.properties.JUnitConsoleProperties; -import com.intellij.execution.process.ProcessAdapter; -import com.intellij.execution.process.ProcessEvent; +import com.intellij.execution.process.*; import com.intellij.execution.runners.ExecutionEnvironment; import com.intellij.execution.runners.ProgramRunner; import com.intellij.execution.testframework.*; @@ -173,7 +169,7 @@ public abstract class TestObject extends JavaTestFrameworkRunnableState 0 + ? RepeatCount.getCountString(repeatCount) + : repeatMode; + getJavaParameters().getProgramParametersList().add(countString); + } + + final OSProcessHandler processHandler = new KillableColoredProcessHandler(createCommandLine()); + ProcessTerminatedListener.attach(processHandler); + final SearchForTestsTask searchForTestsTask = createSearchingForTestsTask(); + if (searchForTestsTask != null) { + searchForTestsTask.attachTaskToProcess(processHandler); + } + return processHandler; + } + @NotNull @Override protected SMTRunnerConsoleProperties createTestConsoleProperties(Executor executor) { diff --git a/plugins/junit/src/com/intellij/execution/junit/TestPackage.java b/plugins/junit/src/com/intellij/execution/junit/TestPackage.java index 211d587b42f5..e97dd1d4c532 100644 --- a/plugins/junit/src/com/intellij/execution/junit/TestPackage.java +++ b/plugins/junit/src/com/intellij/execution/junit/TestPackage.java @@ -60,8 +60,8 @@ public class TestPackage extends TestObject { @NotNull @Override - protected JUnitProcessHandler createHandler(Executor executor) throws ExecutionException { - final JUnitProcessHandler handler = super.createHandler(executor); + protected JUnitProcessHandler createJUnitHandler(Executor executor) throws ExecutionException { + final JUnitProcessHandler handler = super.createJUnitHandler(executor); createSearchingForTestsTask().attachTaskToProcess(handler); return handler; } diff --git a/plugins/maven/jps-plugin/src/org/jetbrains/jps/maven/compiler/MavenCommonArtifactRootCopyingHandlerProvider.java b/plugins/maven/jps-plugin/src/org/jetbrains/jps/maven/compiler/MavenCommonArtifactRootCopyingHandlerProvider.java index 9d66bd7f91ab..57a561be54ec 100644 --- a/plugins/maven/jps-plugin/src/org/jetbrains/jps/maven/compiler/MavenCommonArtifactRootCopyingHandlerProvider.java +++ b/plugins/maven/jps-plugin/src/org/jetbrains/jps/maven/compiler/MavenCommonArtifactRootCopyingHandlerProvider.java @@ -18,6 +18,7 @@ package org.jetbrains.jps.maven.compiler; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.util.Base64; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jps.builders.storage.BuildDataPaths; @@ -58,7 +59,7 @@ public class MavenCommonArtifactRootCopyingHandlerProvider extends ArtifactRootC projectConfiguration.moduleConfigurations.get(getModuleName(artifact.getName())); if (moduleResourceConfiguration != null && moduleResourceConfiguration.manifest != null) { try { - FileUtil.writeToFile(root, moduleResourceConfiguration.manifest); + FileUtil.writeToFile(root, Base64.decode(moduleResourceConfiguration.manifest)); } catch (IOException e) { LOG.debug(e); diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenResourceCompilerConfigurationGenerator.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenResourceCompilerConfigurationGenerator.java index 871b204bb9dd..795aac27c5a7 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenResourceCompilerConfigurationGenerator.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenResourceCompilerConfigurationGenerator.java @@ -14,15 +14,20 @@ import com.intellij.openapi.util.JDOMUtil; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.io.StreamUtil; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vfs.CharsetToolkit; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.Base64; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.xmlb.XmlSerializer; import org.jdom.Document; import org.jdom.Element; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.maven.dom.MavenDomUtil; +import org.jetbrains.idea.maven.dom.MavenPropertyResolver; +import org.jetbrains.idea.maven.dom.model.MavenDomProjectModel; import org.jetbrains.idea.maven.dom.references.MavenFilteredPropertyPsiReferenceProvider; import org.jetbrains.idea.maven.model.MavenId; import org.jetbrains.idea.maven.model.MavenResource; @@ -222,8 +227,9 @@ public class MavenResourceCompilerConfigurationGenerator { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); try { manifest.write(outputStream); - resourceConfig.manifest = outputStream.toString("UTF8"); - + MavenDomProjectModel domModel = MavenDomUtil.getMavenDomProjectModel(module.getProject(), mavenProject.getFile()); + final String resolvedText = MavenPropertyResolver.resolve(outputStream.toString(CharsetToolkit.UTF8), domModel); + resourceConfig.manifest = Base64.encode(resolvedText.getBytes(CharsetToolkit.UTF8)); } finally { StreamUtil.closeStream(outputStream); diff --git a/plugins/tasks/tasks-api/src/com/intellij/tasks/TaskRepositoryType.java b/plugins/tasks/tasks-api/src/com/intellij/tasks/TaskRepositoryType.java index d4575cf3eb92..948ddd6a7d94 100644 --- a/plugins/tasks/tasks-api/src/com/intellij/tasks/TaskRepositoryType.java +++ b/plugins/tasks/tasks-api/src/com/intellij/tasks/TaskRepositoryType.java @@ -23,7 +23,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; -import java.util.Arrays; +import java.util.Collections; import java.util.EnumSet; import java.util.List; @@ -53,7 +53,7 @@ public abstract class TaskRepositoryType implements Ta public abstract TaskRepositoryEditor createEditor(T repository, Project project, Consumer changeListener); public List getAvailableSubtypes() { - return Arrays.asList((TaskRepositorySubtype)this); + return Collections.singletonList((TaskRepositorySubtype)this); } @NotNull 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 da4701d312ad..b1040cba79d1 100644 --- a/plugins/testng/src/com/theoryinpractice/testng/configuration/SearchingForTestsTask.java +++ b/plugins/testng/src/com/theoryinpractice/testng/configuration/SearchingForTestsTask.java @@ -20,40 +20,31 @@ */ package com.theoryinpractice.testng.configuration; -import com.intellij.codeInsight.AnnotationUtil; import com.intellij.execution.CantRunException; import com.intellij.execution.ExecutionException; import com.intellij.execution.testframework.SearchForTestsTask; -import com.intellij.execution.testframework.TestSearchScope; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.PathManager; import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.module.Module; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Computable; 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.vfs.CharsetToolkit; -import com.intellij.psi.*; -import com.intellij.psi.search.GlobalSearchScope; -import com.intellij.psi.search.PackageScope; -import com.intellij.psi.search.searches.AnnotatedMembersSearch; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiMethod; import com.intellij.psi.util.ClassUtil; import com.theoryinpractice.testng.model.IDEARemoteTestRunnerClient; -import com.theoryinpractice.testng.model.TestClassFilter; import com.theoryinpractice.testng.model.TestData; +import com.theoryinpractice.testng.model.TestNGTestObject; 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.*; import java.io.*; import java.net.ServerSocket; import java.util.*; -import java.util.regex.Pattern; -import java.util.regex.PatternSyntaxException; public class SearchingForTestsTask extends SearchForTestsTask { private static final Logger LOG = Logger.getInstance("#" + SearchingForTestsTask.class.getName()); @@ -233,14 +224,12 @@ public class SearchingForTestsTask extends SearchForTestsTask { } private boolean shouldSearchForTestMethods() { - boolean dependantMethods = false; for (Map> methods : myClasses.values()) { if (!methods.isEmpty()) { - dependantMethods = true; - break; + return true; } } - return dependantMethods; + return false; } private void composeTestSuiteFromXml() throws CantRunException { @@ -280,182 +269,9 @@ public class SearchingForTestsTask extends SearchForTestsTask { protected void fillTestObjects(final Map>> classes) throws CantRunException { - final TestData data = myConfig.getPersistantData(); - final PsiManager psiManager = PsiManager.getInstance(myProject); - if (data.TEST_OBJECT.equals(TestType.PACKAGE.getType())) { - final String packageName = data.getPackageName(); - PsiPackage psiPackage = ApplicationManager.getApplication().runReadAction( - new Computable() { - @Nullable - public PsiPackage compute() { - return JavaPsiFacade.getInstance(psiManager.getProject()).findPackage(packageName); - } - } - ); - if (psiPackage == null) { - throw CantRunException.packageNotFound(packageName); - } - else { - TestSearchScope scope = myConfig.getPersistantData().getScope(); - //TODO we should narrow this down by module really, if that's what's specified - TestClassFilter projectFilter = - new TestClassFilter(scope.getSourceScope(myConfig).getGlobalSearchScope(), myProject, true, true); - TestClassFilter filter = projectFilter.intersectionWith(PackageScope.packageScope(psiPackage, true)); - calculateDependencies(null, classes, TestNGUtil.getAllTestClasses(filter, false)); - if (classes.size() == 0) { - throw new CantRunException("No tests found in the package \"" + packageName + '\"'); - } - } - } - else if (data.TEST_OBJECT.equals(TestType.CLASS.getType())) { - //it's a class - final PsiClass psiClass = ApplicationManager.getApplication().runReadAction( - new Computable() { - @Nullable - public PsiClass compute() { - return ClassUtil.findPsiClass(psiManager, data.getMainClassName().replace('/', '.'), null, true, getSearchScope()); - } - } - ); - if (psiClass == null) { - throw new CantRunException("No tests found in the class \"" + data.getMainClassName() + '\"'); - } - if (null == ApplicationManager.getApplication().runReadAction(new Computable() { - @Nullable - public String compute() { - return psiClass.getQualifiedName(); - } - })) { - throw new CantRunException("Cannot test anonymous or local class \"" + data.getMainClassName() + '\"'); - } - calculateDependencies(null, classes, psiClass); - } - else if (data.TEST_OBJECT.equals(TestType.METHOD.getType())) { - //it's a method - final PsiClass psiClass = ApplicationManager.getApplication().runReadAction( - new Computable() { - @Nullable - public PsiClass compute() { - return ClassUtil.findPsiClass(psiManager, data.getMainClassName().replace('/', '.'), null, true, getSearchScope()); - } - } - ); - if (psiClass == null) { - throw new CantRunException("No tests found in the class \"" + data.getMainClassName() + '\"'); - } - if (null == ApplicationManager.getApplication().runReadAction( - new Computable() { - @Nullable - public String compute() { - return psiClass.getQualifiedName(); - } - } - )) { - throw new CantRunException("Cannot test anonymous or local class \"" + data.getMainClassName() + '\"'); - } - collectTestMethods(classes, psiClass, data.getMethodName()); - } - else if (data.TEST_OBJECT.equals(TestType.GROUP.getType())) { - //for a group, we include all classes - PsiClass[] testClasses = TestNGUtil - .getAllTestClasses(new TestClassFilter(data.getScope().getSourceScope(myConfig).getGlobalSearchScope(), myProject, true, true), false); - if (testClasses != null) { - for (PsiClass c : testClasses) { - classes.put(c, new LinkedHashMap>()); - } - } - } - else if (data.TEST_OBJECT.equals(TestType.PATTERN.getType())) { - for (final String pattern : data.getPatterns()) { - final String className; - final String methodName; - if (pattern.contains(",")) { - methodName = StringUtil.getShortName(pattern, ','); - className = StringUtil.getPackageName(pattern, ','); - } else { - className = pattern; - methodName = null; - } - - final PsiClass psiClass = ApplicationManager.getApplication().runReadAction(new Computable() { - @Nullable - @Override - public PsiClass compute() { - return ClassUtil.findPsiClass(psiManager, className.replace('/', '.'), null, true, getSearchScope()); - } - }); - if (psiClass != null) { - final Boolean hasTest = ApplicationManager.getApplication().runReadAction(new Computable() { - @Override - public Boolean compute() { - return TestNGUtil.hasTest(psiClass); - } - }); - if (hasTest) { - if (StringUtil.isEmpty(methodName)) { - calculateDependencies(null, classes, psiClass); - } - else { - collectTestMethods(classes, psiClass, methodName); - } - } else { - throw new CantRunException("No tests found in class " + className); - } - } - } - if (classes.size() != data.getPatterns().size()) { - TestSearchScope scope = myConfig.getPersistantData().getScope(); - final List compilePatterns = new ArrayList(); - for (String p : data.getPatterns()) { - final Pattern compilePattern; - try { - compilePattern = Pattern.compile(p); - } - catch (PatternSyntaxException e) { - continue; - } - if (compilePattern != null) { - compilePatterns.add(compilePattern); - } - } - TestClassFilter projectFilter = - new TestClassFilter(scope.getSourceScope(myConfig).getGlobalSearchScope(), myProject, true, true){ - @Override - public boolean isAccepted(PsiClass psiClass) { - if (super.isAccepted(psiClass)) { - final String qualifiedName = psiClass.getQualifiedName(); - LOG.assertTrue(qualifiedName != null); - for (Pattern pattern : compilePatterns) { - if (pattern.matcher(qualifiedName).matches()) return true; - } - } - return false; - } - }; - calculateDependencies(null, classes, TestNGUtil.getAllTestClasses(projectFilter, false)); - if (classes.size() == 0) { - throw new CantRunException("No tests found in for patterns \"" + StringUtil.join(data.getPatterns(), " || ") + '\"'); - } - } - } - } - - private void collectTestMethods(Map>> classes, final PsiClass psiClass, final String methodName) { - final PsiMethod[] methods = ApplicationManager.getApplication().runReadAction( - new Computable() { - public PsiMethod[] compute() { - return psiClass.findMethodsByName(methodName, true); - } - } - ); - calculateDependencies(methods, classes, psiClass); - Map> psiMethods = classes.get(psiClass); - if (psiMethods == null) { - psiMethods = new LinkedHashMap>(); - classes.put(psiClass, psiMethods); - } - for (PsiMethod method : methods) { - psiMethods.put(method, Collections.emptyList()); + final TestNGTestObject testObject = TestNGTestObject.fromConfig(myConfig); + if (testObject != null) { + testObject.fillTestObjects(classes); } } @@ -463,24 +279,15 @@ public class SearchingForTestsTask extends SearchForTestsTask { Map testParams = new HashMap(); // Override with those from the test runner configuration - testParams.putAll(convertPropertiesFileToMap(myData.PROPERTIES_FILE)); - testParams.putAll(myData.TEST_PROPERTIES); - - return testParams; - } - - private static Map convertPropertiesFileToMap(String properties_file) { - Map params = new HashMap(); - - if (properties_file != null) { - File propertiesFile = new File(properties_file); + if (myData.PROPERTIES_FILE != null) { + File propertiesFile = new File(myData.PROPERTIES_FILE); if (propertiesFile.exists()) { Properties properties = new Properties(); try { properties.load(new FileInputStream(propertiesFile)); for (Map.Entry entry : properties.entrySet()) { - params.put((String)entry.getKey(), (String)entry.getValue()); + testParams.put((String)entry.getKey(), (String)entry.getValue()); } } @@ -489,155 +296,7 @@ public class SearchingForTestsTask extends SearchForTestsTask { } } } - return params; + testParams.putAll(myData.TEST_PROPERTIES); + return testParams; } - - private void calculateDependencies(PsiMethod[] methods, - final Map>> results, - @Nullable final PsiClass... classes) { - calculateDependencies(methods, results, new LinkedHashSet(), classes); - } - - private void calculateDependencies(final PsiMethod[] methods, - final Map>> results, - final Set alreadyMarkedToBeChecked, - @Nullable final PsiClass... classes) { - if (classes != null && classes.length > 0) { - final Set groupDependencies = new LinkedHashSet(); - TestNGUtil.collectAnnotationValues(groupDependencies, "dependsOnGroups", methods, classes); - final Set membersToCheckNow = new LinkedHashSet(); - if (!groupDependencies.isEmpty()) { - ApplicationManager.getApplication().runReadAction(new Runnable() { - public void run() { - final Project project = classes[0].getProject(); - final PsiClass testAnnotation = - JavaPsiFacade.getInstance(project).findClass(TestNGUtil.TEST_ANNOTATION_FQN, GlobalSearchScope.allScope(project)); - LOG.assertTrue(testAnnotation != null); - for (PsiMember psiMember : AnnotatedMembersSearch.search(testAnnotation, getSearchScope())) { - final PsiAnnotation annotation = AnnotationUtil.findAnnotation(psiMember, TestNGUtil.TEST_ANNOTATION_FQN); - if (TestNGUtil.isAnnotatedWithParameter(annotation, "groups", groupDependencies)) { - if (appendMember(psiMember, alreadyMarkedToBeChecked, results)) { - membersToCheckNow.add(psiMember); - } - } - } - } - }); - } - - collectDependsOnMethods(results, alreadyMarkedToBeChecked, membersToCheckNow, methods, classes); - - if (methods == null) { - for (PsiClass c : classes) { - results.put(c, new LinkedHashMap>()); - } - } else { - for (PsiMember psiMember : membersToCheckNow) { - PsiClass psiClass; - PsiMethod[] meths = null; - if (psiMember instanceof PsiMethod) { - psiClass = psiMember.getContainingClass(); - meths = new PsiMethod[] {(PsiMethod)psiMember}; - } else { - psiClass = (PsiClass)psiMember; - } - calculateDependencies(meths, results, alreadyMarkedToBeChecked, psiClass); - } - } - } - } - - private static void collectDependsOnMethods(final Map>> results, - final Set alreadyMarkedToBeChecked, - final Set membersToCheckNow, - final PsiMethod[] methods, - final PsiClass... classes) { - final PsiClass[] psiClasses; - if (methods != null && methods.length > 0) { - final Set containingClasses = new LinkedHashSet(); - for (final PsiMethod method : methods) { - containingClasses.add(ApplicationManager.getApplication().runReadAction(new Computable() { - @Override - public PsiClass compute() { - return method.getContainingClass(); - } - })); - } - psiClasses = containingClasses.toArray(new PsiClass[containingClasses.size()]); - } else { - psiClasses = classes; - } - for (final PsiClass containingClass : psiClasses) { - final Set testMethodDependencies = new LinkedHashSet(); - TestNGUtil.collectAnnotationValues(testMethodDependencies, "dependsOnMethods", methods, containingClass); - if (!testMethodDependencies.isEmpty()) { - ApplicationManager.getApplication().runReadAction(new Runnable() { - public void run() { - final Project project = containingClass.getProject(); - final JavaPsiFacade psiFacade = JavaPsiFacade.getInstance(project); - for (String dependency : testMethodDependencies) { - final String className = StringUtil.getPackageName(dependency); - final String methodName = StringUtil.getShortName(dependency); - if (StringUtil.isEmpty(className)) { - checkClassMethods(methodName, containingClass, alreadyMarkedToBeChecked, membersToCheckNow, results); - } - else { - final PsiClass aClass = psiFacade.findClass(className, containingClass.getResolveScope()); - if (aClass != null) { - checkClassMethods(methodName, aClass, alreadyMarkedToBeChecked, membersToCheckNow, results); - } - } - } - } - }); - } - } - } - - private static void checkClassMethods(String methodName, - PsiClass containingClass, - Set alreadyMarkedToBeChecked, - Set membersToCheckNow, - Map>> results) { - final PsiMethod[] psiMethods = containingClass.findMethodsByName(methodName, true); - for (PsiMethod method : psiMethods) { - if (AnnotationUtil.isAnnotated(method, TestNGUtil.TEST_ANNOTATION_FQN, false) && - appendMember(method, alreadyMarkedToBeChecked, results)) { - membersToCheckNow.add(method); - } - } - } - - private static boolean appendMember(final PsiMember psiMember, - final Set underConsideration, - final Map>> results) { - boolean result = false; - final PsiClass psiClass = psiMember instanceof PsiClass ? ((PsiClass)psiMember) : psiMember.getContainingClass(); - Map> psiMethods = results.get(psiClass); - if (psiMethods == null) { - psiMethods = new LinkedHashMap>(); - results.put(psiClass, psiMethods); - if (psiMember instanceof PsiClass) { - result = underConsideration.add(psiMember); - } - } - if (psiMember instanceof PsiMethod) { - final boolean add = psiMethods.put((PsiMethod)psiMember, Collections.emptyList()) != null; - if (add) { - return underConsideration.add(psiMember); - } - return false; - } - return result; - } - - @NotNull - private GlobalSearchScope getSearchScope() { - final TestData data = myConfig.getPersistantData(); - final Module module = myConfig.getConfigurationModule().getModule(); - return data.TEST_OBJECT.equals(TestType.PACKAGE.getType()) - ? myConfig.getPersistantData().getScope().getSourceScope(myConfig).getGlobalSearchScope() - : module != null ? GlobalSearchScope.moduleWithDependenciesScope(module) : GlobalSearchScope.projectScope(myConfig.getProject()); - } - } diff --git a/plugins/testng/src/com/theoryinpractice/testng/configuration/TestNGConfiguration.java b/plugins/testng/src/com/theoryinpractice/testng/configuration/TestNGConfiguration.java index e051d95331e8..376e74bfcb1d 100644 --- a/plugins/testng/src/com/theoryinpractice/testng/configuration/TestNGConfiguration.java +++ b/plugins/testng/src/com/theoryinpractice/testng/configuration/TestNGConfiguration.java @@ -29,7 +29,6 @@ import com.intellij.execution.configuration.EnvironmentVariablesComponent; import com.intellij.execution.configurations.*; import com.intellij.execution.junit.RefactoringListeners; import com.intellij.execution.runners.ExecutionEnvironment; -import com.intellij.execution.testframework.SourceScope; import com.intellij.execution.testframework.TestSearchScope; import com.intellij.execution.util.JavaParametersUtil; import com.intellij.execution.util.ProgramParametersUtil; @@ -47,12 +46,12 @@ import com.intellij.refactoring.listeners.RefactoringElementAdapter; import com.intellij.refactoring.listeners.RefactoringElementListener; import com.intellij.refactoring.listeners.UndoRefactoringElementListener; import com.theoryinpractice.testng.model.TestData; +import com.theoryinpractice.testng.model.TestNGTestObject; import com.theoryinpractice.testng.model.TestType; import org.jdom.Element; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.testng.xml.Parser; import java.util.*; @@ -68,7 +67,7 @@ public class TestNGConfiguration extends ModuleBasedConfiguration 0) { - return "Tests in \"" + data.getPackageName() + '\"'; - } - else { - return "All Tests"; - } - } - if (TestType.METHOD.getType().equals(data.TEST_OBJECT)) { - return data.getMethodName() + "()"; - } - if (TestType.SUITE.getType().equals(data.TEST_OBJECT)) { - return data.getSuiteName(); - } - return data.getGroupName(); + final TestNGTestObject testObject = TestNGTestObject.fromConfig(this); + return testObject != null ? ProgramRunnerUtil.shortenName(testObject.getActionName(), 0) : null; } public void setVMParameters(String value) { @@ -299,45 +280,9 @@ public class TestNGConfiguration extends ModuleBasedConfiguration patterns = data.getPatterns(); - if (patterns.isEmpty()) { - throw new RuntimeConfigurationWarning("No pattern selected"); - } + final TestNGTestObject testObject = TestNGTestObject.fromConfig(this); + if (testObject != null) { + testObject.checkConfiguration(); } JavaRunConfigurationExtensionManager.checkConfigurationIsValid(this); ProgramParametersUtil.checkWorkingDirectoryExist(this, getProject(), getConfigurationModule().getModule()); diff --git a/plugins/testng/src/com/theoryinpractice/testng/configuration/TestNGConfigurationProducer.java b/plugins/testng/src/com/theoryinpractice/testng/configuration/TestNGConfigurationProducer.java index 5203f6c09819..f5dfadb86aef 100644 --- a/plugins/testng/src/com/theoryinpractice/testng/configuration/TestNGConfigurationProducer.java +++ b/plugins/testng/src/com/theoryinpractice/testng/configuration/TestNGConfigurationProducer.java @@ -33,6 +33,7 @@ import com.intellij.openapi.module.Module; import com.intellij.openapi.util.Comparing; import com.intellij.psi.PsiElement; import com.theoryinpractice.testng.model.TestData; +import com.theoryinpractice.testng.model.TestNGTestObject; import org.testng.IDEATestNGRemoteListener; public abstract class TestNGConfigurationProducer extends JavaRunConfigurationProducerBase implements Cloneable { @@ -58,7 +59,7 @@ public abstract class TestNGConfigurationProducer extends JavaRunConfigurationPr String paramSetName = contextLocation instanceof PsiMemberParameterizedLocation ? getInvocationNumber(((PsiMemberParameterizedLocation)contextLocation).getParamSetName()) : null; if (paramSetName != null && !Comparing.strEqual(paramSetName, testNGConfiguration.getProgramParameters())) return false; - TestData testobject = testNGConfiguration.getPersistantData(); + TestNGTestObject testobject = TestNGTestObject.fromConfig(testNGConfiguration); if (testobject != null) { if (testobject.isConfiguredByElement(element)) { final Module configurationModule = testNGConfiguration.getConfigurationModule().getModule(); diff --git a/plugins/testng/src/com/theoryinpractice/testng/configuration/TestNGConfigurationType.java b/plugins/testng/src/com/theoryinpractice/testng/configuration/TestNGConfigurationType.java index 3b7829dcb8b2..882e61c53b22 100644 --- a/plugins/testng/src/com/theoryinpractice/testng/configuration/TestNGConfigurationType.java +++ b/plugins/testng/src/com/theoryinpractice/testng/configuration/TestNGConfigurationType.java @@ -33,6 +33,7 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Comparing; import com.intellij.psi.PsiElement; import com.theoryinpractice.testng.model.TestData; +import com.theoryinpractice.testng.model.TestNGTestObject; import icons.TestngIcons; import org.jetbrains.annotations.NotNull; @@ -72,7 +73,8 @@ public class TestNGConfigurationType implements ConfigurationType return false; else { final PsiElement element = location.getPsiElement(); - if (testobject.isConfiguredByElement(element)) { + final TestNGTestObject testNGTestObject = TestNGTestObject.fromConfig(config); + if (testNGTestObject != null && testNGTestObject.isConfiguredByElement(element)) { final Module configurationModule = config.getConfigurationModule().getModule(); if (Comparing.equal(location.getModule(), configurationModule)) return true; diff --git a/plugins/testng/src/com/theoryinpractice/testng/configuration/TestNGRunnableState.java b/plugins/testng/src/com/theoryinpractice/testng/configuration/TestNGRunnableState.java index 14591ab39a4b..ad2157c3070c 100644 --- a/plugins/testng/src/com/theoryinpractice/testng/configuration/TestNGRunnableState.java +++ b/plugins/testng/src/com/theoryinpractice/testng/configuration/TestNGRunnableState.java @@ -18,9 +18,7 @@ package com.theoryinpractice.testng.configuration; import com.intellij.execution.*; import com.intellij.execution.configurations.*; -import com.intellij.execution.process.OSProcessHandler; -import com.intellij.execution.process.ProcessAdapter; -import com.intellij.execution.process.ProcessEvent; +import com.intellij.execution.process.*; import com.intellij.execution.runners.ExecutionEnvironment; import com.intellij.execution.runners.ProgramRunner; import com.intellij.execution.testframework.*; @@ -76,10 +74,10 @@ public class TestNGRunnableState extends JavaTestFrameworkRunnableState"; - else return getPackageName(); - String name = JavaExecutionUtil.getPresentableClassName(getMainClassName()); - if (TestType.METHOD.getType().equals(TEST_OBJECT)) { - return name + '.' + getMethodName(); - } - else if (TestType.SUITE.getType().equals(TEST_OBJECT)) { - return getSuiteName(); - } - else { - if (TestType.PATTERN.getType().equals(TEST_OBJECT)) { - final int size = myPatterns.size(); - if (size == 0) return "Temp suite"; - return StringUtil.getShortName(myPatterns.iterator().next()) + (size > 1 ? " and " + (size - 1) + " more" : ""); - } - return name; - } - } - public String getMainClassName() { return MAIN_CLASS_NAME == null ? "" : MAIN_CLASS_NAME; } @@ -217,28 +196,6 @@ public class TestData implements Cloneable return JavaExecutionUtil.findModule(psiclass); } - public boolean isConfiguredByElement(PsiElement element) { - if (TEST_OBJECT.equals(TestType.PACKAGE.getType())) { - if (element instanceof PsiPackage) { - return Comparing.strEqual(PACKAGE_NAME, ((PsiPackage) element).getQualifiedName()); - } else if (element instanceof PsiDirectory) { - final PsiPackage psiPackage = JavaDirectoryService.getInstance().getPackage(((PsiDirectory)element)); - return psiPackage != null && Comparing.strEqual(PACKAGE_NAME, psiPackage.getQualifiedName()); - } - } - - element = PsiTreeUtil.getParentOfType(element, PsiModifierListOwner.class, false); - if (element instanceof PsiMethod && TEST_OBJECT.equals(TestType.METHOD.getType())) { - final PsiClass aClass = ((PsiMethod) element).getContainingClass(); - return aClass != null && - Comparing.strEqual(MAIN_CLASS_NAME, JavaExecutionUtil.getRuntimeQualifiedName(aClass)) && - Comparing.strEqual(METHOD_NAME, ((PsiMethod) element).getName()); - } else if (element instanceof PsiClass && TEST_OBJECT.equals(TestType.CLASS.getType())) { - return Comparing.strEqual(MAIN_CLASS_NAME, JavaExecutionUtil.getRuntimeQualifiedName((PsiClass) element)); - } - return false; - } - public Map getEnvs() { return ENVS; } diff --git a/plugins/testng/src/com/theoryinpractice/testng/model/TestNGTestClass.java b/plugins/testng/src/com/theoryinpractice/testng/model/TestNGTestClass.java new file mode 100644 index 000000000000..16e1cd43afaf --- /dev/null +++ b/plugins/testng/src/com/theoryinpractice/testng/model/TestNGTestClass.java @@ -0,0 +1,95 @@ +/* + * 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.theoryinpractice.testng.model; + +import com.intellij.execution.CantRunException; +import com.intellij.execution.JavaExecutionUtil; +import com.intellij.execution.configurations.RuntimeConfigurationException; +import com.intellij.execution.testframework.SourceScope; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.util.Comparing; +import com.intellij.openapi.util.Computable; +import com.intellij.psi.*; +import com.intellij.psi.util.ClassUtil; +import com.intellij.psi.util.PsiTreeUtil; +import com.theoryinpractice.testng.configuration.TestNGConfiguration; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.Map; + +public class TestNGTestClass extends TestNGTestObject { + public TestNGTestClass(TestNGConfiguration config) { + super(config); + } + + @Override + public void fillTestObjects(Map>> classes) + throws CantRunException { + final TestData data = myConfig.getPersistantData(); + //it's a class + final PsiClass psiClass = ApplicationManager.getApplication().runReadAction( + new Computable() { + @Nullable + public PsiClass compute() { + return ClassUtil.findPsiClass(PsiManager.getInstance(myConfig.getProject()), data.getMainClassName().replace('/', '.'), null, true, getSearchScope()); + } + } + ); + if (psiClass == null) { + throw new CantRunException("No tests found in the class \"" + data.getMainClassName() + '\"'); + } + if (null == ApplicationManager.getApplication().runReadAction(new Computable() { + @Nullable + public String compute() { + return psiClass.getQualifiedName(); + } + })) { + throw new CantRunException("Cannot test anonymous or local class \"" + data.getMainClassName() + '\"'); + } + calculateDependencies(null, classes, psiClass); + } + + @Override + public String getGeneratedName() { + return JavaExecutionUtil.getPresentableClassName(myConfig.getPersistantData().getMainClassName()); + } + + @Override + public String getActionName() { + return JavaExecutionUtil.getShortClassName(myConfig.getPersistantData().MAIN_CLASS_NAME); + } + + @Override + public void checkConfiguration() throws RuntimeConfigurationException { + final TestData data = myConfig.getPersistantData(); + final SourceScope scope = data.getScope().getSourceScope(myConfig); + if (scope == null) { + throw new RuntimeConfigurationException("Invalid scope specified"); + } + PsiClass psiClass = JavaPsiFacade.getInstance(myConfig.getProject()).findClass(data.getMainClassName(), scope.getGlobalSearchScope()); + if (psiClass == null) throw new RuntimeConfigurationException("Class '" + data.getMainClassName() + "' not found"); + } + + @Override + public boolean isConfiguredByElement(PsiElement element) { + element = PsiTreeUtil.getParentOfType(element, PsiModifierListOwner.class, false); + if (element instanceof PsiClass) { + return Comparing.strEqual(myConfig.getPersistantData().getMethodName(), JavaExecutionUtil.getRuntimeQualifiedName((PsiClass) element)); + } + return false; + } +} diff --git a/plugins/testng/src/com/theoryinpractice/testng/model/TestNGTestGroup.java b/plugins/testng/src/com/theoryinpractice/testng/model/TestNGTestGroup.java new file mode 100644 index 000000000000..ef24a830ec65 --- /dev/null +++ b/plugins/testng/src/com/theoryinpractice/testng/model/TestNGTestGroup.java @@ -0,0 +1,63 @@ +/* + * 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.theoryinpractice.testng.model; + +import com.intellij.execution.CantRunException; +import com.intellij.execution.configurations.RuntimeConfigurationException; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiMethod; +import com.theoryinpractice.testng.configuration.TestNGConfiguration; +import com.theoryinpractice.testng.util.TestNGUtil; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +public class TestNGTestGroup extends TestNGTestObject { + public TestNGTestGroup(TestNGConfiguration config) { + super(config); + } + + @Override + public void fillTestObjects(Map>> classes) + throws CantRunException { + final TestData data = myConfig.getPersistantData(); + //for a group, we include all classes + final TestClassFilter classFilter = + new TestClassFilter(data.getScope().getSourceScope(myConfig).getGlobalSearchScope(), myConfig.getProject(), true, true); + PsiClass[] testClasses = TestNGUtil.getAllTestClasses(classFilter, false); + if (testClasses != null) { + for (PsiClass c : testClasses) { + classes.put(c, new LinkedHashMap>()); + } + } + } + + @Override + public String getGeneratedName() { + return myConfig.getPersistantData().getGroupName(); + } + + @Override + public String getActionName() { + return myConfig.getPersistantData().getGroupName(); + } + + @Override + public void checkConfiguration() throws RuntimeConfigurationException { + //check group exist? + } +} diff --git a/plugins/testng/src/com/theoryinpractice/testng/model/TestNGTestMethod.java b/plugins/testng/src/com/theoryinpractice/testng/model/TestNGTestMethod.java new file mode 100644 index 000000000000..16e08f4de0de --- /dev/null +++ b/plugins/testng/src/com/theoryinpractice/testng/model/TestNGTestMethod.java @@ -0,0 +1,110 @@ +/* + * 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.theoryinpractice.testng.model; + +import com.intellij.execution.CantRunException; +import com.intellij.execution.JavaExecutionUtil; +import com.intellij.execution.configurations.RuntimeConfigurationException; +import com.intellij.execution.testframework.SourceScope; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.util.Comparing; +import com.intellij.openapi.util.Computable; +import com.intellij.psi.*; +import com.intellij.psi.util.ClassUtil; +import com.intellij.psi.util.PsiTreeUtil; +import com.theoryinpractice.testng.configuration.TestNGConfiguration; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.Map; + +public class TestNGTestMethod extends TestNGTestObject { + public TestNGTestMethod(TestNGConfiguration config) { + super(config); + } + + @Override + public void fillTestObjects(Map>> classes) + throws CantRunException { + final TestData data = myConfig.getPersistantData(); + final PsiClass psiClass = ApplicationManager.getApplication().runReadAction( + new Computable() { + @Nullable + public PsiClass compute() { + return ClassUtil.findPsiClass(PsiManager.getInstance(myConfig.getProject()), data.getMainClassName().replace('/', '.'), null, true, getSearchScope()); + } + } + ); + if (psiClass == null) { + throw new CantRunException("No tests found in the class \"" + data.getMainClassName() + '\"'); + } + if (null == ApplicationManager.getApplication().runReadAction( + new Computable() { + @Nullable + public String compute() { + return psiClass.getQualifiedName(); + } + } + )) { + throw new CantRunException("Cannot test anonymous or local class \"" + data.getMainClassName() + '\"'); + } + collectTestMethods(classes, psiClass, data.getMethodName()); + } + + @Override + public String getGeneratedName() { + final TestData data = myConfig.getPersistantData(); + return JavaExecutionUtil.getPresentableClassName(data.getMainClassName()) + '.' + data.getMethodName(); + } + + @Override + public String getActionName() { + return myConfig.getPersistantData().getMethodName() + "()"; + } + + @Override + public void checkConfiguration() throws RuntimeConfigurationException { + final TestData data = myConfig.getPersistantData(); + final SourceScope scope = data.getScope().getSourceScope(myConfig); + if (scope == null) { + throw new RuntimeConfigurationException("Invalid scope specified"); + } + PsiClass psiClass = JavaPsiFacade.getInstance(myConfig.getProject()).findClass(data.getMainClassName(), scope.getGlobalSearchScope()); + if (psiClass == null) throw new RuntimeConfigurationException("Class '" + data.getMainClassName() + "' not found"); + PsiMethod[] methods = psiClass.findMethodsByName(data.getMethodName(), true); + if (methods.length == 0) { + throw new RuntimeConfigurationException("Method '" + data.getMethodName() + "' not found"); + } + for (PsiMethod method : methods) { + if (!method.hasModifierProperty(PsiModifier.PUBLIC)) { + throw new RuntimeConfigurationException("Non public method '" + data.getMethodName() + "'specified"); + } + } + } + + @Override + public boolean isConfiguredByElement(PsiElement element) { + element = PsiTreeUtil.getParentOfType(element, PsiModifierListOwner.class, false); + if (element instanceof PsiMethod) { + final PsiClass aClass = ((PsiMethod) element).getContainingClass(); + final TestData data = myConfig.getPersistantData(); + return aClass != null && + Comparing.strEqual(data.MAIN_CLASS_NAME, JavaExecutionUtil.getRuntimeQualifiedName(aClass)) && + Comparing.strEqual(data.METHOD_NAME, ((PsiMethod) element).getName()); + } + return false; + } +} diff --git a/plugins/testng/src/com/theoryinpractice/testng/model/TestNGTestObject.java b/plugins/testng/src/com/theoryinpractice/testng/model/TestNGTestObject.java new file mode 100644 index 000000000000..2f839e5aaefb --- /dev/null +++ b/plugins/testng/src/com/theoryinpractice/testng/model/TestNGTestObject.java @@ -0,0 +1,247 @@ +/* + * 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.theoryinpractice.testng.model; + +import com.intellij.codeInsight.AnnotationUtil; +import com.intellij.execution.CantRunException; +import com.intellij.execution.configurations.RuntimeConfigurationException; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Computable; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.psi.*; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.search.searches.AnnotatedMembersSearch; +import com.theoryinpractice.testng.configuration.TestNGConfiguration; +import com.theoryinpractice.testng.util.TestNGUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.*; + +public abstract class TestNGTestObject { + private static final Logger LOG = Logger.getInstance("#" + TestNGTestObject.class.getName()); + protected final TestNGConfiguration myConfig; + + public TestNGTestObject(TestNGConfiguration config) { + myConfig = config; + } + + public static TestNGTestObject fromConfig(TestNGConfiguration config) { + final TestData data = config.getPersistantData(); + if (data.TEST_OBJECT.equals(TestType.PACKAGE.getType())) { + return new TestNGTestPackage(config); + } + if (data.TEST_OBJECT.equals(TestType.CLASS.getType())) { + return new TestNGTestClass(config); + } + if (data.TEST_OBJECT.equals(TestType.METHOD.getType())) { + return new TestNGTestMethod(config); + } + + if (data.TEST_OBJECT.equals(TestType.GROUP.getType())) { + return new TestNGTestGroup(config); + } + + if (data.TEST_OBJECT.equals(TestType.PATTERN.getType())) { + return new TestNGTestPattern(config); + } + + if (data.TEST_OBJECT.equals(TestType.SUITE.getType())){ + return new TestNGTestSuite(config); + } + assert false : data.TEST_OBJECT; + return null; + } + + public abstract void fillTestObjects(final Map>> classes) throws CantRunException; + public abstract String getGeneratedName(); + public abstract String getActionName(); + public abstract void checkConfiguration() throws RuntimeConfigurationException; + + public boolean isConfiguredByElement(PsiElement element) { + return false; + } + + protected void calculateDependencies(PsiMethod[] methods, + final Map>> results, + @Nullable final PsiClass... classes) { + calculateDependencies(methods, results, new LinkedHashSet(), classes); + } + + private void calculateDependencies(final PsiMethod[] methods, + final Map>> results, + final Set alreadyMarkedToBeChecked, + @Nullable final PsiClass... classes) { + if (classes != null && classes.length > 0) { + final Set groupDependencies = new LinkedHashSet(); + TestNGUtil.collectAnnotationValues(groupDependencies, "dependsOnGroups", methods, classes); + final Set membersToCheckNow = new LinkedHashSet(); + if (!groupDependencies.isEmpty()) { + ApplicationManager.getApplication().runReadAction(new Runnable() { + public void run() { + final Project project = classes[0].getProject(); + final PsiClass testAnnotation = + JavaPsiFacade.getInstance(project).findClass(TestNGUtil.TEST_ANNOTATION_FQN, GlobalSearchScope.allScope(project)); + LOG.assertTrue(testAnnotation != null); + for (PsiMember psiMember : AnnotatedMembersSearch.search(testAnnotation, getSearchScope())) { + final PsiAnnotation annotation = AnnotationUtil.findAnnotation(psiMember, TestNGUtil.TEST_ANNOTATION_FQN); + if (TestNGUtil.isAnnotatedWithParameter(annotation, "groups", groupDependencies)) { + if (appendMember(psiMember, alreadyMarkedToBeChecked, results)) { + membersToCheckNow.add(psiMember); + } + } + } + } + }); + } + + collectDependsOnMethods(results, alreadyMarkedToBeChecked, membersToCheckNow, methods, classes); + + if (methods == null) { + for (PsiClass c : classes) { + results.put(c, new LinkedHashMap>()); + } + } else { + for (PsiMember psiMember : membersToCheckNow) { + PsiClass psiClass; + PsiMethod[] meths = null; + if (psiMember instanceof PsiMethod) { + psiClass = psiMember.getContainingClass(); + meths = new PsiMethod[] {(PsiMethod)psiMember}; + } else { + psiClass = (PsiClass)psiMember; + } + calculateDependencies(meths, results, alreadyMarkedToBeChecked, psiClass); + } + } + } + } + + private static void collectDependsOnMethods(final Map>> results, + final Set alreadyMarkedToBeChecked, + final Set membersToCheckNow, + final PsiMethod[] methods, + final PsiClass... classes) { + final PsiClass[] psiClasses; + if (methods != null && methods.length > 0) { + final Set containingClasses = new LinkedHashSet(); + for (final PsiMethod method : methods) { + containingClasses.add(ApplicationManager.getApplication().runReadAction(new Computable() { + @Override + public PsiClass compute() { + return method.getContainingClass(); + } + })); + } + psiClasses = containingClasses.toArray(new PsiClass[containingClasses.size()]); + } else { + psiClasses = classes; + } + for (final PsiClass containingClass : psiClasses) { + final Set testMethodDependencies = new LinkedHashSet(); + TestNGUtil.collectAnnotationValues(testMethodDependencies, "dependsOnMethods", methods, containingClass); + if (!testMethodDependencies.isEmpty()) { + ApplicationManager.getApplication().runReadAction(new Runnable() { + public void run() { + final Project project = containingClass.getProject(); + final JavaPsiFacade psiFacade = JavaPsiFacade.getInstance(project); + for (String dependency : testMethodDependencies) { + final String className = StringUtil.getPackageName(dependency); + final String methodName = StringUtil.getShortName(dependency); + if (StringUtil.isEmpty(className)) { + checkClassMethods(methodName, containingClass, alreadyMarkedToBeChecked, membersToCheckNow, results); + } + else { + final PsiClass aClass = psiFacade.findClass(className, containingClass.getResolveScope()); + if (aClass != null) { + checkClassMethods(methodName, aClass, alreadyMarkedToBeChecked, membersToCheckNow, results); + } + } + } + } + }); + } + } + } + + private static void checkClassMethods(String methodName, + PsiClass containingClass, + Set alreadyMarkedToBeChecked, + Set membersToCheckNow, + Map>> results) { + final PsiMethod[] psiMethods = containingClass.findMethodsByName(methodName, true); + for (PsiMethod method : psiMethods) { + if (AnnotationUtil.isAnnotated(method, TestNGUtil.TEST_ANNOTATION_FQN, false) && + appendMember(method, alreadyMarkedToBeChecked, results)) { + membersToCheckNow.add(method); + } + } + } + + private static boolean appendMember(final PsiMember psiMember, + final Set underConsideration, + final Map>> results) { + boolean result = false; + final PsiClass psiClass = psiMember instanceof PsiClass ? ((PsiClass)psiMember) : psiMember.getContainingClass(); + Map> psiMethods = results.get(psiClass); + if (psiMethods == null) { + psiMethods = new LinkedHashMap>(); + results.put(psiClass, psiMethods); + if (psiMember instanceof PsiClass) { + result = underConsideration.add(psiMember); + } + } + if (psiMember instanceof PsiMethod) { + final boolean add = psiMethods.put((PsiMethod)psiMember, Collections.emptyList()) != null; + if (add) { + return underConsideration.add(psiMember); + } + return false; + } + return result; + } + + @NotNull + protected GlobalSearchScope getSearchScope() { + final TestData data = myConfig.getPersistantData(); + final Module module = myConfig.getConfigurationModule().getModule(); + return data.TEST_OBJECT.equals(TestType.PACKAGE.getType()) + ? myConfig.getPersistantData().getScope().getSourceScope(myConfig).getGlobalSearchScope() + : module != null ? GlobalSearchScope.moduleWithDependenciesScope(module) : GlobalSearchScope.projectScope(myConfig.getProject()); + } + + protected void collectTestMethods(Map>> classes, final PsiClass psiClass, final String methodName) { + final PsiMethod[] methods = ApplicationManager.getApplication().runReadAction( + new Computable() { + public PsiMethod[] compute() { + return psiClass.findMethodsByName(methodName, true); + } + } + ); + calculateDependencies(methods, classes, psiClass); + Map> psiMethods = classes.get(psiClass); + if (psiMethods == null) { + psiMethods = new LinkedHashMap>(); + classes.put(psiClass, psiMethods); + } + for (PsiMethod method : methods) { + psiMethods.put(method, Collections.emptyList()); + } + } +} diff --git a/plugins/testng/src/com/theoryinpractice/testng/model/TestNGTestPackage.java b/plugins/testng/src/com/theoryinpractice/testng/model/TestNGTestPackage.java new file mode 100644 index 000000000000..0a6315b41706 --- /dev/null +++ b/plugins/testng/src/com/theoryinpractice/testng/model/TestNGTestPackage.java @@ -0,0 +1,103 @@ +/* + * 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.theoryinpractice.testng.model; + +import com.intellij.execution.CantRunException; +import com.intellij.execution.JavaExecutionUtil; +import com.intellij.execution.configurations.RuntimeConfigurationException; +import com.intellij.execution.testframework.TestSearchScope; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.util.Comparing; +import com.intellij.openapi.util.Computable; +import com.intellij.psi.*; +import com.intellij.psi.search.PackageScope; +import com.intellij.psi.util.PsiTreeUtil; +import com.theoryinpractice.testng.configuration.TestNGConfiguration; +import com.theoryinpractice.testng.util.TestNGUtil; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.Map; + +public class TestNGTestPackage extends TestNGTestObject { + public TestNGTestPackage(TestNGConfiguration configuration) { + super(configuration); + } + + @Override + public void fillTestObjects(Map>> classes) throws CantRunException { + final String packageName = myConfig.getPersistantData().getPackageName(); + PsiPackage psiPackage = ApplicationManager.getApplication().runReadAction( + new Computable() { + @Nullable + public PsiPackage compute() { + return JavaPsiFacade.getInstance(myConfig.getProject()).findPackage(packageName); + } + } + ); + if (psiPackage == null) { + throw CantRunException.packageNotFound(packageName); + } + else { + TestSearchScope scope = myConfig.getPersistantData().getScope(); + //TODO we should narrow this down by module really, if that's what's specified + TestClassFilter projectFilter = + new TestClassFilter(scope.getSourceScope(myConfig).getGlobalSearchScope(), myConfig.getProject(), true, true); + TestClassFilter filter = projectFilter.intersectionWith(PackageScope.packageScope(psiPackage, true)); + calculateDependencies(null, classes, TestNGUtil.getAllTestClasses(filter, false)); + if (classes.size() == 0) { + throw new CantRunException("No tests found in the package \"" + packageName + '\"'); + } + } + } + + @Override + public String getGeneratedName() { + final String packageName = myConfig.getPersistantData().getPackageName(); + return packageName.length() == 0 ? "" : packageName; + } + + @Override + public String getActionName() { + String s = myConfig.getName(); + if (!myConfig.isGeneratedName()) return '\"' + s + '\"'; + if (myConfig.getPersistantData().getPackageName().trim().length() > 0) { + return "Tests in \"" + myConfig.getPersistantData().getPackageName() + '\"'; + } + else { + return "All Tests"; + } + } + + @Override + public void checkConfiguration() throws RuntimeConfigurationException { + final TestData data = myConfig.getPersistantData(); + PsiPackage psiPackage = JavaPsiFacade.getInstance(myConfig.getProject()).findPackage(data.getPackageName()); + if (psiPackage == null) throw new RuntimeConfigurationException("Package '" + data.getPackageName() + "' not found"); + } + + @Override + public boolean isConfiguredByElement(PsiElement element) { + final String packageName = myConfig.getPersistantData().getPackageName(); + if (element instanceof PsiPackage) { + return Comparing.strEqual(packageName, ((PsiPackage)element).getQualifiedName()); + } else if (element instanceof PsiDirectory) { + final PsiPackage psiPackage = JavaDirectoryService.getInstance().getPackage(((PsiDirectory)element)); + return psiPackage != null && Comparing.strEqual(packageName, psiPackage.getQualifiedName()); + } + return false; + } +} diff --git a/plugins/testng/src/com/theoryinpractice/testng/model/TestNGTestPattern.java b/plugins/testng/src/com/theoryinpractice/testng/model/TestNGTestPattern.java new file mode 100644 index 000000000000..78e56e27de73 --- /dev/null +++ b/plugins/testng/src/com/theoryinpractice/testng/model/TestNGTestPattern.java @@ -0,0 +1,145 @@ +/* + * 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.theoryinpractice.testng.model; + +import com.intellij.execution.CantRunException; +import com.intellij.execution.configurations.RuntimeConfigurationException; +import com.intellij.execution.configurations.RuntimeConfigurationWarning; +import com.intellij.execution.testframework.TestSearchScope; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.util.Computable; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiManager; +import com.intellij.psi.PsiMethod; +import com.intellij.psi.util.ClassUtil; +import com.theoryinpractice.testng.configuration.TestNGConfiguration; +import com.theoryinpractice.testng.util.TestNGUtil; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; + +public class TestNGTestPattern extends TestNGTestObject { + private static final Logger LOG = Logger.getInstance("#" + TestNGTestPattern.class.getName()); + + public TestNGTestPattern(TestNGConfiguration config) { + super(config); + } + + @Override + public void fillTestObjects(Map>> classes) + throws CantRunException { + final TestData data = myConfig.getPersistantData(); + for (final String pattern : data.getPatterns()) { + final String className; + final String methodName; + if (pattern.contains(",")) { + methodName = StringUtil.getShortName(pattern, ','); + className = StringUtil.getPackageName(pattern, ','); + } else { + className = pattern; + methodName = null; + } + + final PsiClass psiClass = ApplicationManager.getApplication().runReadAction(new Computable() { + @Nullable + @Override + public PsiClass compute() { + return ClassUtil.findPsiClass(PsiManager.getInstance(myConfig.getProject()), className.replace('/', '.'), null, true, getSearchScope()); + } + }); + if (psiClass != null) { + final Boolean hasTest = ApplicationManager.getApplication().runReadAction(new Computable() { + @Override + public Boolean compute() { + return TestNGUtil.hasTest(psiClass); + } + }); + if (hasTest) { + if (StringUtil.isEmpty(methodName)) { + calculateDependencies(null, classes, psiClass); + } + else { + collectTestMethods(classes, psiClass, methodName); + } + } else { + throw new CantRunException("No tests found in class " + className); + } + } + } + if (classes.size() != data.getPatterns().size()) { + TestSearchScope scope = myConfig.getPersistantData().getScope(); + final List compilePatterns = new ArrayList(); + for (String p : data.getPatterns()) { + final Pattern compilePattern; + try { + compilePattern = Pattern.compile(p); + } + catch (PatternSyntaxException e) { + continue; + } + if (compilePattern != null) { + compilePatterns.add(compilePattern); + } + } + TestClassFilter projectFilter = + new TestClassFilter(scope.getSourceScope(myConfig).getGlobalSearchScope(), myConfig.getProject(), true, true){ + @Override + public boolean isAccepted(PsiClass psiClass) { + if (super.isAccepted(psiClass)) { + final String qualifiedName = psiClass.getQualifiedName(); + LOG.assertTrue(qualifiedName != null); + for (Pattern pattern : compilePatterns) { + if (pattern.matcher(qualifiedName).matches()) return true; + } + } + return false; + } + }; + calculateDependencies(null, classes, TestNGUtil.getAllTestClasses(projectFilter, false)); + if (classes.size() == 0) { + throw new CantRunException("No tests found in for patterns \"" + StringUtil.join(data.getPatterns(), " || ") + '\"'); + } + } + } + + @Override + public String getGeneratedName() { + final Set patterns = myConfig.getPersistantData().getPatterns(); + final int size = patterns.size(); + if (size == 0) return "Temp suite"; + return StringUtil.getShortName(patterns.iterator().next()) + (size > 1 ? " and " + (size - 1) + " more" : ""); + } + + @Override + public String getActionName() { + return getGeneratedName(); + } + + @Override + public void checkConfiguration() throws RuntimeConfigurationException { + final Set patterns = myConfig.getPersistantData().getPatterns(); + if (patterns.isEmpty()) { + throw new RuntimeConfigurationWarning("No pattern selected"); + } + } +} diff --git a/plugins/testng/src/com/theoryinpractice/testng/model/TestNGTestSuite.java b/plugins/testng/src/com/theoryinpractice/testng/model/TestNGTestSuite.java new file mode 100644 index 000000000000..875891671ccb --- /dev/null +++ b/plugins/testng/src/com/theoryinpractice/testng/model/TestNGTestSuite.java @@ -0,0 +1,61 @@ +/* + * 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.theoryinpractice.testng.model; + +import com.intellij.execution.CantRunException; +import com.intellij.execution.configurations.RuntimeConfigurationException; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiMethod; +import com.theoryinpractice.testng.configuration.TestNGConfiguration; +import org.testng.xml.Parser; + +import java.util.List; +import java.util.Map; + +public class TestNGTestSuite extends TestNGTestObject { + private static final Object PARSE_LOCK = new Object(); + public TestNGTestSuite(TestNGConfiguration config) { + super(config); + } + + @Override + public void fillTestObjects(Map>> classes) throws CantRunException {} + + @Override + public String getGeneratedName() { + return myConfig.getPersistantData().getSuiteName(); + } + + @Override + public String getActionName() { + return myConfig.getPersistantData().getSuiteName(); + } + + @Override + public void checkConfiguration() throws RuntimeConfigurationException { + final TestData data = myConfig.getPersistantData(); + try { + final Parser parser = new Parser(data.getSuiteName()); + parser.setLoadClasses(false); + synchronized (PARSE_LOCK) { + parser.parse();//try to parse suite.xml + } + } + catch (Exception e) { + throw new RuntimeConfigurationException("Unable to parse '" + data.getSuiteName() + "' specified"); + } + } +} diff --git a/plugins/testng/src/com/theoryinpractice/testng/util/TestNGUtil.java b/plugins/testng/src/com/theoryinpractice/testng/util/TestNGUtil.java index 38fd89d5e695..94969e358c85 100644 --- a/plugins/testng/src/com/theoryinpractice/testng/util/TestNGUtil.java +++ b/plugins/testng/src/com/theoryinpractice/testng/util/TestNGUtil.java @@ -436,7 +436,7 @@ public class TestNGUtil { public void run() { final ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator(); - final Collection set = new HashSet(); + final Collection set = new LinkedHashSet(); PsiManager manager = PsiManager.getInstance(filter.getProject()); GlobalSearchScope scope = filter.getScope(); GlobalSearchScope projectScope = GlobalSearchScope.projectScope(manager.getProject()); @@ -445,7 +445,9 @@ public class TestNGUtil { ApplicationManager.getApplication().runReadAction(new Runnable() { public void run() { if (filter.isAccepted(psiClass)) { - indicator.setText2("Found test class " + psiClass.getQualifiedName()); + if (indicator != null) { + indicator.setText2("Found test class " + psiClass.getQualifiedName()); + } set.add(psiClass); } } diff --git a/plugins/testng/testSources/com/theoryinpractice/testng/configuration/DetectClassesToRunTest.java b/plugins/testng/testSources/com/theoryinpractice/testng/configuration/DetectClassesToRunTest.java new file mode 100644 index 000000000000..368e8da94407 --- /dev/null +++ b/plugins/testng/testSources/com/theoryinpractice/testng/configuration/DetectClassesToRunTest.java @@ -0,0 +1,135 @@ +/* + * 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.theoryinpractice.testng.configuration; + +import com.intellij.execution.CantRunException; +import com.intellij.execution.testframework.TestSearchScope; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiMethod; +import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase; +import com.theoryinpractice.testng.model.TestData; +import com.theoryinpractice.testng.model.TestNGTestObject; +import com.theoryinpractice.testng.model.TestType; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +public class DetectClassesToRunTest extends LightCodeInsightFixtureTestCase { + @BeforeMethod + @Override + protected void setUp() throws Exception { + super.setUp(); + myFixture.addClass("package org.testng.annotations; @interface Test {public String[] dependsOnMethods() default {};}"); + myFixture.addClass("package org.testng.annotations; @interface BeforeClass {}"); + } + + @AfterMethod + @Override + protected void tearDown() throws Exception { + super.tearDown(); + } + + @Test + public void testNonRelatedBeforeClassIncluded() throws Exception { + final PsiClass configClass = myFixture.addClass("package p; public class AConfig {@org.testng.annotations.BeforeClass public void setup(){}}"); + final PsiClass testClass = myFixture.addClass("package p; public class ATest {@org.testng.annotations.Test public void testOne(){}}"); + //should not be included in resulted xml + myFixture.addClass("package p; public class BTest {}"); + + doTestPackageConfiguration(configClass, testClass); + } + + @Test + public void testNonRelatedIncludedWhenConfigIsLocatedInSuperclassInAnotherPackage() throws Exception { + myFixture.addClass("package a; public class AConfig {@org.testng.annotations.BeforeClass public void setup(){}}"); + final PsiClass emptyClassWithSuperConfig = myFixture.addClass("package p; import a.AConfig; public class BConfig extends AConfig {}"); + final PsiClass testClass = myFixture.addClass("package p; public class ATest {@org.testng.annotations.Test public void testOne(){}}"); + doTestPackageConfiguration(emptyClassWithSuperConfig, testClass); + } + + @Test + public void testBeforeClassIsIncludedIfRunOnlyOneMethod() throws Exception { + final PsiClass aClass = + myFixture.addClass("package a; public class AConfig {" + + "@org.testng.annotations.BeforeClass public void setup(){}\n" + + "@org.testng.annotations.Test public void testOne(){}\n" + + "}"); + doTestMethodConfiguration(aClass, aClass.getMethods()[1]); + } + + @Test + public void testOneMethodWhenAnnotationIsOnBaseClassOnly() throws Exception { + myFixture.addClass("package a; @org.testng.annotations.Test public class BaseClass {}"); + final PsiClass aClass = + myFixture.addClass("package a; public class ATest extends BaseClass {" + + " public void testOne(){}\n" + + "}"); + doTestMethodConfiguration(aClass, aClass.getMethods()); + } + + @Test + public void testOneMethodWithDependencies() throws Exception { + final PsiClass aClass = + myFixture.addClass("package a; public class ATest {" + + " @org.testng.annotations.Test\n" + + " public void testTwo(){}\n " + + " @org.testng.annotations.Test(dependsOnMethods=\"testTwo\")\n" + + " public void testOne(){}\n" + + "}"); + doTestMethodConfiguration(aClass, aClass.getMethods()); + } + + private void doTestMethodConfiguration(PsiClass aClass, PsiMethod... expectedMethods) throws CantRunException { + final TestNGConfiguration configuration = + new TestNGConfiguration("testOne", getProject(), TestNGConfigurationType.getInstance().getConfigurationFactories()[0]); + final TestData data = configuration.getPersistantData(); + data.TEST_OBJECT = TestType.METHOD.getType(); + data.METHOD_NAME = "testOne"; + data.setScope(TestSearchScope.SINGLE_MODULE); + configuration.setModule(data.setMainClass(aClass)); + + final TestNGTestObject testObject = TestNGTestObject.fromConfig(configuration); + assertNotNull(testObject); + final LinkedHashMap>> classes = new LinkedHashMap>>(); + testObject.fillTestObjects(classes); + assertContainsElements(classes.keySet(), aClass); + final Map> methods = classes.get(aClass); + assertContainsElements(methods.keySet(), expectedMethods); + } + + private void doTestPackageConfiguration(PsiClass... containingClasses) throws CantRunException { + final TestNGConfiguration configuration = + new TestNGConfiguration("p", getProject(), TestNGConfigurationType.getInstance().getConfigurationFactories()[0]); + final TestData data = configuration.getPersistantData(); + data.TEST_OBJECT = TestType.PACKAGE.getType(); + data.PACKAGE_NAME = "p"; + data.setScope(TestSearchScope.WHOLE_PROJECT); + + final TestNGTestObject testObject = TestNGTestObject.fromConfig(configuration); + assertNotNull(testObject); + final LinkedHashMap>> classes = new LinkedHashMap>>(); + testObject.fillTestObjects(classes); + assertContainsElements(classes.keySet(), containingClasses); + + for (PsiClass psiClass : containingClasses) { + assertEmpty(classes.get(psiClass).keySet()); + } + } +} diff --git a/plugins/xpath/xpath-view/src/org/intellij/plugins/xpathView/HistoryElement.java b/plugins/xpath/xpath-view/src/org/intellij/plugins/xpathView/HistoryElement.java index cde2e552745b..5788e4d77796 100644 --- a/plugins/xpath/xpath-view/src/org/intellij/plugins/xpathView/HistoryElement.java +++ b/plugins/xpath/xpath-view/src/org/intellij/plugins/xpathView/HistoryElement.java @@ -68,4 +68,9 @@ public final class HistoryElement implements Copyable { public HistoryElement changeExpression(String expression) { return new HistoryElement(expression, variables, namespaces); } + + @Override + public String toString() { + return expression; + } } diff --git a/plugins/xpath/xpath-view/src/org/intellij/plugins/xpathView/search/FindByXPathAction.java b/plugins/xpath/xpath-view/src/org/intellij/plugins/xpathView/search/FindByXPathAction.java index cfe4a435e5b4..ea96e67b063f 100644 --- a/plugins/xpath/xpath-view/src/org/intellij/plugins/xpathView/search/FindByXPathAction.java +++ b/plugins/xpath/xpath-view/src/org/intellij/plugins/xpathView/search/FindByXPathAction.java @@ -20,6 +20,7 @@ import com.intellij.find.FindProgressIndicator; import com.intellij.find.FindSettings; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.CommonDataKeys; import com.intellij.openapi.actionSystem.LangDataKeys; import com.intellij.openapi.module.Module; import com.intellij.openapi.progress.ProgressIndicator; @@ -34,6 +35,7 @@ import org.intellij.plugins.xpathView.XPathEvalAction; import org.intellij.plugins.xpathView.XPathProjectComponent; import org.intellij.plugins.xpathView.support.XPathSupport; import org.intellij.plugins.xpathView.ui.InputExpressionDialog; +import org.intellij.plugins.xpathView.util.Namespace; import org.jaxen.JaxenException; import org.jaxen.XPathSyntaxException; import org.jetbrains.annotations.NotNull; @@ -43,12 +45,12 @@ import java.util.Collections; public class FindByXPathAction extends AnAction { public void update(AnActionEvent e) { - final Project project = LangDataKeys.PROJECT.getData(e.getDataContext()); + final Project project = CommonDataKeys.PROJECT.getData(e.getDataContext()); e.getPresentation().setEnabled(project != null); } public void actionPerformed(AnActionEvent e) { - final Project project = LangDataKeys.PROJECT.getData(e.getDataContext()); + final Project project = CommonDataKeys.PROJECT.getData(e.getDataContext()); final Module module = LangDataKeys.MODULE.getData(e.getDataContext()); if (project != null) { @@ -141,7 +143,7 @@ public class FindByXPathAction extends AnAction { private static boolean validateExpression(Project project, String expression) { try { //noinspection unchecked - XPathSupport.getInstance().createXPath(null, expression, Collections.emptyList()); + XPathSupport.getInstance().createXPath(null, expression, Collections.emptyList()); return true; } catch (XPathSyntaxException e) { Messages.showErrorDialog(project, e.getMultilineMessage(), "XPath Syntax Error"); diff --git a/plugins/xpath/xpath-view/src/org/intellij/plugins/xpathView/ui/InputExpressionDialog.java b/plugins/xpath/xpath-view/src/org/intellij/plugins/xpathView/ui/InputExpressionDialog.java index 0ac85440cb85..d868ef744dcc 100644 --- a/plugins/xpath/xpath-view/src/org/intellij/plugins/xpathView/ui/InputExpressionDialog.java +++ b/plugins/xpath/xpath-view/src/org/intellij/plugins/xpathView/ui/InputExpressionDialog.java @@ -23,13 +23,12 @@ import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.event.DocumentAdapter; import com.intellij.openapi.editor.event.DocumentEvent; import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.ComboBox; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.Pair; import com.intellij.psi.*; import com.intellij.psi.xml.XmlElement; import com.intellij.psi.xml.XmlFile; -import com.intellij.ui.EditorTextField; +import com.intellij.ui.EditorComboBox; import com.intellij.ui.ListCellRendererWrapper; import com.intellij.uiDesigner.core.GridConstraints; import com.intellij.util.ArrayUtil; @@ -54,14 +53,12 @@ import org.jetbrains.annotations.Nullable; import javax.swing.*; import javax.swing.event.ListDataEvent; import javax.swing.event.ListDataListener; -import javax.swing.plaf.basic.BasicComboBoxEditor; import javax.xml.namespace.QName; -import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.*; -import java.util.List; +@SuppressWarnings("unchecked") public abstract class InputExpressionDialog extends ModeSwitchableDialog { protected final Project myProject; protected final FormType myForm; @@ -72,8 +69,7 @@ public abstract class InputExpressionDialog extends private final Document myDocument; private final MultilineEditor myEditor; - private final EditorTextField myComboboxEditor; - private final ComboBox myComboBox = new ComboBox(300); + private final EditorComboBox myComboBox; private JComponent myEditorComponent; @Nullable private Set myNamespaceCache; @@ -97,7 +93,6 @@ public abstract class InputExpressionDialog extends myModel = new HistoryModel(_history, myDocument); myEditor = new MultilineEditor(myDocument, project, XPathFileType.XPATH, myModel); myModel.addListDataListener(new ListDataListener() { - final PsiDocumentManager docMgr = PsiDocumentManager.getInstance(project); final DaemonCodeAnalyzer analyzer = DaemonCodeAnalyzer.getInstance(project); public void intervalAdded(ListDataEvent e) { @@ -116,17 +111,15 @@ public abstract class InputExpressionDialog extends } } }); - - myComboboxEditor = new EditorTextField(myDocument, project, XPathFileType.XPATH); + myComboBox = new EditorComboBox(myDocument, project, XPathFileType.XPATH); myComboBox.setRenderer(new ListCellRendererWrapper() { - @Override - public void customize(JList list, HistoryElement value, int index, boolean selected, boolean hasFocus) { - setText(value != null ? value.expression : ""); - } + @Override + public void customize(JList list, HistoryElement value, int index, boolean selected, boolean hasFocus) { + setText(value != null ? value.expression : ""); + } }); myComboBox.setModel(myModel); - myComboBox.setEditor(new EditorAdapter(myComboboxEditor)); myComboBox.setEditable(true); myDocument.addDocumentListener(new DocumentAdapter() { @@ -254,7 +247,7 @@ public abstract class InputExpressionDialog extends if (getMode() == Mode.ADVANCED) { return myEditor.getField().getEditor(); } else { - return myComboboxEditor.getEditor(); + return myComboBox.getEditorEx(); } } @@ -410,35 +403,6 @@ public abstract class InputExpressionDialog extends } } - protected class EditorAdapter extends BasicComboBoxEditor { - private final EditorTextField myTf; - - public EditorAdapter(EditorTextField tf) { - myTf = tf; - } - - public Component getEditorComponent() { - return myTf.getComponent(); - } - - @Nullable - public Object getItem() { - return myModel.getSelectedItem(); - } - - public void selectAll() { - myTf.selectAll(); - } - - public void setItem(Object object) { - if (object == null) { - myEditor.getField().setText(""); - } else { - myEditor.getField().setText(((HistoryElement)object).expression); - } - } - } - private static class MyVariableResolver extends SimpleVariableContext { private final HistoryModel myModel; @@ -568,7 +532,7 @@ public abstract class InputExpressionDialog extends @NotNull public String getText() { - return "Register Namespace Prefix"; + return "Register namespace prefix"; } @NotNull 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/pydev/pydevd.py b/python/helpers/pydev/pydevd.py index 7b9bdd6dfaaa..a10de2716297 100644 --- a/python/helpers/pydev/pydevd.py +++ b/python/helpers/pydev/pydevd.py @@ -96,6 +96,11 @@ PluginManager = None if SUPPORT_PLUGINS: from pydevd_plugin_utils import PluginManager +if IS_PY3K: + import pkgutil +else: + from _pydev_imps import _pydev_pkgutil_old as pkgutil + threadingEnumerate = threading.enumerate threadingCurrentThread = threading.currentThread @@ -1684,8 +1689,27 @@ class PyDB: from pydev_monkey import patch_thread_modules patch_thread_modules() + def get_fullname(self, mod_name): + try: + loader = pkgutil.get_loader(mod_name) + except: + return None + if loader is not None: + for attr in ("get_filename", "_get_filename"): + meth = getattr(loader, attr, None) + if meth is not None: + return meth(mod_name) + return None + + def run(self, file, globals=None, locals=None, module=False, set_trace=True): + if module: + filename = self.get_fullname(file) + if filename is None: + sys.stderr.write("No module named %s\n" % file) + return + else: + file = filename - def run(self, file, globals=None, locals=None, set_trace=True): if os.path.isdir(file): new_target = os.path.join(file, '__main__.py') if os.path.isfile(new_target): @@ -1773,6 +1797,7 @@ def processCommandLine(argv): setup['save-signatures'] = False setup['print-in-debugger-startup'] = False setup['cmd-line'] = False + setup['module'] = False i = 0 del argv[0] while (i < len(argv)): @@ -1816,6 +1841,9 @@ def processCommandLine(argv): elif (argv[i] == '--cmd-line'): del argv[i] setup['cmd-line'] = True + elif (argv[i] == '--module'): + del argv[i] + setup['module'] = True else: raise ValueError("unexpected option " + argv[i]) return setup @@ -2256,6 +2284,7 @@ if __name__ == '__main__': pass # It's ok not having stackless there... debugger = PyDB() + is_module = setup['module'] if fix_app_engine_debug: sys.stderr.write("pydev debugger: google app engine integration enabled\n") @@ -2270,7 +2299,7 @@ if __name__ == '__main__': sys.argv.insert(4, '--max_module_instances=1') # Run the dev_appserver - debugger.run(setup['file'], None, None, set_trace=False) + debugger.run(setup['file'], None, None, is_module, set_trace=False) else: # as to get here all our imports are already resolved, the psyco module can be # changed and we'll still get the speedups in the debugger, as those functions @@ -2303,7 +2332,7 @@ if __name__ == '__main__': connected = True # Mark that we're connected when started from inside ide. - globals = debugger.run(setup['file'], None, None) + globals = debugger.run(setup['file'], None, None, is_module) if setup['cmd-line']: debugger.wait_for_commands(globals) diff --git a/python/src/com/jetbrains/python/debugger/PyDebugRunner.java b/python/src/com/jetbrains/python/debugger/PyDebugRunner.java index 206916bdf36d..f5f25106982c 100644 --- a/python/src/com/jetbrains/python/debugger/PyDebugRunner.java +++ b/python/src/com/jetbrains/python/debugger/PyDebugRunner.java @@ -66,6 +66,7 @@ public class PyDebugRunner extends GenericProgramRunner { public static final String IDE_PROJECT_ROOTS = "IDE_PROJECT_ROOTS"; @SuppressWarnings("SpellCheckingInspection") public static final String GEVENT_SUPPORT = "GEVENT_SUPPORT"; + public static boolean isModule = false; @Override @NotNull @@ -182,14 +183,37 @@ public class PyDebugRunner extends GenericProgramRunner { final PythonCommandLineState pyState, final int serverLocalPort) { return new CommandLinePatcher() { + + private void patchExeParams(ParametersList parametersList) { + // we should remove '-m' parameter, but notify debugger of it + // but we can't remove one parameter from group, so we create new parameters group + ParamsGroup newExeParams = new ParamsGroup(PythonCommandLineState.GROUP_EXE_OPTIONS); + int exeParamsIndex = parametersList.getParamsGroups().indexOf( + parametersList.getParamsGroup(PythonCommandLineState.GROUP_EXE_OPTIONS)); + ParamsGroup exeParamsOld = parametersList.removeParamsGroup(exeParamsIndex); + isModule = false; + for (String param: exeParamsOld.getParameters()) { + if (!param.equals("-m")) { + newExeParams.addParameter(param); + } else { + isModule = true; + } + } + + parametersList.addParamsGroupAt(exeParamsIndex, newExeParams); + } + + @Override public void patchCommandLine(GeneralCommandLine commandLine) { // script name is the last parameter; all other params are for python interpreter; insert just before name - final ParametersList parametersList = commandLine.getParametersList(); + ParametersList parametersList = commandLine.getParametersList(); @SuppressWarnings("ConstantConditions") @NotNull ParamsGroup debugParams = parametersList.getParamsGroup(PythonCommandLineState.GROUP_DEBUGGER); + patchExeParams(parametersList); + @SuppressWarnings("ConstantConditions") @NotNull ParamsGroup exeParams = parametersList.getParamsGroup(PythonCommandLineState.GROUP_EXE_OPTIONS); @@ -218,6 +242,10 @@ public class PyDebugRunner extends GenericProgramRunner { debugParams.addParameter("--multiproc"); } + if (isModule) { + debugParams.addParameter("--module"); + } + if (ApplicationManager.getApplication().isUnitTestMode()) { debugParams.addParameter("--DEBUG"); } diff --git a/python/src/com/jetbrains/python/editor/PyJoinLinesHandler.java b/python/src/com/jetbrains/python/editor/PyJoinLinesHandler.java index 8fca92e81d67..dac60d10684d 100644 --- a/python/src/com/jetbrains/python/editor/PyJoinLinesHandler.java +++ b/python/src/com/jetbrains/python/editor/PyJoinLinesHandler.java @@ -22,26 +22,39 @@ import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiComment; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; -import com.intellij.psi.tree.IElementType; import com.intellij.psi.tree.TokenSet; import com.intellij.psi.util.PsiTreeUtil; import com.jetbrains.python.PyTokenTypes; import com.jetbrains.python.psi.*; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.Locale; + /** * Joins lines sanely. - * - statement lines: add a semicolon; - * - list-like lines: keep one space after comma; - * - lines inside a multiline string: remove excess indentation; - * - multi-constant string like "a" "b": join into one; - * - comment and comment: remove indentation and hash sign; - * - second line is 'class' or 'def': fail. - *
- * User: dcheryasov - * Date: Sep 6, 2010 2:25:48 AM + *

    + *
  • statement lines: add a semicolon;
  • + *
  • list-like lines: keep one space after comma;
  • + *
  • lines inside a multiline string: remove excess indentation;
  • + *
  • multi-constant string like "a" "b": join into one;
  • + *
  • comment and comment: remove indentation and hash sign;
  • + *
  • second line is 'class' or 'def': fail.
  • + *
+ * + * @author dcheryasov */ public class PyJoinLinesHandler implements JoinRawLinesHandlerDelegate { + private final static Joiner[] JOINERS = { + new OpenBracketJoiner(), + new CloseBracketJoiner(), + new StringLiteralJoiner(), + new StmtJoiner(), // strings before stmts to let doc strings join + new BinaryExprJoiner(), + new CommentJoiner(), + new StripBackslashJoiner() + }; + @Override public int tryJoinLines(Document document, PsiFile file, int start, int end) { @@ -49,203 +62,117 @@ public class PyJoinLinesHandler implements JoinRawLinesHandlerDelegate { } @Override - public int tryJoinRawLines(Document document, PsiFile file, int start, int end) { + public int tryJoinRawLines(@NotNull Document document, PsiFile file, int start, int end) { if (!(file instanceof PyFile)) return CANNOT_JOIN; // step back the probable "\" and space before it. - int i = start; - CharSequence text = document.getCharsSequence(); - if (i>= 0 && text.charAt(i) == '\n') i -=1; - if (i>= 0 && text.charAt(i) == '\\') i -=1; - while (i>=0 && text.charAt(i) == ' ' || text.charAt(i) == '\t') i -=1; - if (i < 0) return CANNOT_JOIN; // TODO: join with empty BOF, too + final CharSequence text = document.getCharsSequence(); + if (start >= 0 && text.charAt(start) == '\n') start -= 1; + if (start >= 0 && text.charAt(start) == '\\') start -= 1; + while (start >= 0 && text.charAt(start) == ' ' || text.charAt(start) == '\t') { + start -= 1; + } + if (start < 0) { + return CANNOT_JOIN; // TODO: join with empty BOF, too + } // detect elements around the join - PsiElement left_element = file.findElementAt(i); - PsiElement right_element = file.findElementAt(end); - if (left_element != null && right_element != null) { - PyExpression left_expr = PsiTreeUtil.getParentOfType(left_element, PyExpression.class); - if (left_expr instanceof PsiFile) left_expr = null; - PyExpression right_expr = PsiTreeUtil.getParentOfType(right_element, PyExpression.class); - if (right_expr instanceof PsiFile) right_expr = null; + final PsiElement leftElement = file.findElementAt(start); + final PsiElement rightElement = file.findElementAt(end); + if (leftElement != null && rightElement != null) { + final PyExpression leftExpr = PsiTreeUtil.getParentOfType(leftElement, PyExpression.class); + final PyExpression rightExpr = PsiTreeUtil.getParentOfType(rightElement, PyExpression.class); - Joiner[] joiners = { // these are featherweight, will create and gc instantly - new OpenBracketJoiner(), new CloseBracketJoiner(), - new StringLiteralJoiner(), new StmtJoiner(), // strings before stmts to let doc strings join - new BinaryExprJoiner(), new ListLikeExprJoiner(), - new CommentJoiner(), - }; + final Request request = new Request(document, start, end, leftElement, leftExpr, rightElement, rightExpr); - Request request = new Request(document, left_element, left_expr, right_element, right_expr); - - for (Joiner joiner : joiners) { - Result res = joiner.join(request); + for (Joiner joiner : JOINERS) { + final Result res = joiner.join(request); if (res != null) { - final int cut_start = i + 1 - res.getCutFromLeft(); - document.deleteString(cut_start, end + res.getCutIntoRight()); - document.insertString(cut_start, res.getInsert()); - return cut_start + res.getCursorOffset(); - } - } - - // single string case PY-4375 - final PyExpression leftExpression = request.leftExpr(); - final PyExpression rightExpression = request.rightExpr(); - if (request.leftElem() == request.rightElem()) { - IElementType type = request.leftElem().getNode().getElementType(); - if (PyTokenTypes.SINGLE_QUOTED_STRING == type || PyTokenTypes.SINGLE_QUOTED_UNICODE == type) { - if (leftExpression == null) return CANNOT_JOIN; - if (removeBackSlash(document, leftExpression, false)) { - return leftExpression.getTextOffset(); - } - } - } - PsiElement expression = null; - if (leftExpression != null && rightExpression != null) { - if (PsiTreeUtil.isAncestor(leftExpression, rightExpression, false)) - expression = leftExpression; - else if (PsiTreeUtil.isAncestor(rightExpression, leftExpression, false)) - expression = rightExpression; - if (expression != null && !(expression instanceof PyStringLiteralExpression)) { - if (removeBackSlash(document, expression, true)) { - return expression.getTextOffset(); - } + final int cutStart = start + 1 - res.cutFromLeft; + document.replaceString(cutStart, end + res.cutIntoRight, res.replacement); + return cutStart + res.caretOffset; } } } return CANNOT_JOIN; } - private static boolean removeBackSlash(Document document, PsiElement element, boolean trim) { - String[] substrings = element.getText().split("\n"); - if (substrings.length != 1) { - StringBuilder replacement = new StringBuilder(); - for (int i = 0; i < substrings.length; i++) { - String string = substrings[i]; - if (trim) { - string = StringUtil.trimLeading(string); - } - if (string.trim().endsWith("\\")) { - replacement.append(string.substring(0, string.length() - 1)); - } - else { - replacement.append(string); - } - - if (i != substrings.length - 1 && !(element instanceof PyReferenceExpression) && - !(element instanceof PyStringLiteralExpression)) { - replacement.append(" "); - } - } - document.replaceString(element.getTextOffset(), element.getTextOffset()+element.getTextLength(), replacement); - return true; - } - return false; - } - // a dumb immutable result holder private static class Result { - final String myInsert; - final int myOffset; - final int myCutFromLeft; - final int myCutIntoRight; + final String replacement; + final int caretOffset; + final int cutFromLeft; + final int cutIntoRight; /** * Result of a join operation. - * @param insert: what string to insert at start position + * + * @param replacement: what string to insert at start position * @param cursorOffset: how to move cursor relative to start (0 = stand at start) */ - Result(String insert, int cursorOffset) { - myInsert = insert; - myOffset = cursorOffset; - myCutFromLeft = 0; - myCutIntoRight = 0; + Result(@NotNull String replacement, int cursorOffset) { + this(replacement, cursorOffset, 0, 0); } /** * Result of a join operation. - * @param insert what to insert into the cut place + * + * @param replacement what to insert into the cut place * @param cursorOffset where to put cursor, relative to the start cursorOffset of cutting - * @param cutFromLeft how many chars to cut from the end on left string, >0 moves start cursorOffset of cutting to the left. + * @param cutFromLeft how many chars to cut from the end on left string, >0 moves start cursorOffset of cutting to the left. * @param cutIntoRight how many chars to cut from the beginning on right string, >0 moves start cursorOffset of cutting to the right. */ - private Result(String insert, int cursorOffset, int cutFromLeft, int cutIntoRight) { - myCutFromLeft = cutFromLeft; - myCutIntoRight = cutIntoRight; - myInsert = insert; - myOffset = cursorOffset; - } - - public String getInsert() { - return myInsert; - } - - public int getCursorOffset() { - return myOffset; - } - - public int getCutFromLeft() { - return myCutFromLeft; - } - - public int getCutIntoRight() { - return myCutIntoRight; + Result(@NotNull String replacement, int cursorOffset, int cutFromLeft, int cutIntoRight) { + this.cutFromLeft = cutFromLeft; + this.cutIntoRight = cutIntoRight; + this.replacement = replacement; + caretOffset = cursorOffset; } } // a dumb immutable request items holder private static class Request { - final Document myDocument; - final PsiElement myLeftElem; - final PsiElement myRightElem; - final PyExpression myLeftExpr; - final PyExpression myRightExpr; + final Document document; + final PsiElement leftElem; + final PsiElement rightElem; + final PyExpression leftExpr; + final PyExpression rightExpr; + final int secondLineStartOffset; + final int firstLineEndOffset; - private Request(Document document, PsiElement leftElem, PyExpression leftExpr, PsiElement rightElem, PyExpression rightExpr) { - myDocument = document; - myLeftElem = leftElem; - myLeftExpr = leftExpr; - myRightElem = rightElem; - myRightExpr = rightExpr; + private Request(@NotNull Document document, + int firstLineEndOffset, + int secondLineStartOffset, + @NotNull PsiElement leftElem, + @Nullable PyExpression leftExpr, + @NotNull PsiElement rightElem, + @Nullable PyExpression rightExpr) { + this.document = document; + this.firstLineEndOffset = firstLineEndOffset; + this.secondLineStartOffset = secondLineStartOffset; + this.leftElem = leftElem; + this.rightElem = rightElem; + this.leftExpr = leftExpr; + this.rightExpr = rightExpr; } - - public Document document() { - return myDocument; - } - - public PsiElement leftElem() { - return myLeftElem; - } - - public PyExpression leftExpr() { - return myLeftExpr; - } - - public PsiElement rightElem() { - return myRightElem; - } - - public PyExpression rightExpr() { - return myRightExpr; - } - } - private static abstract class Joiner { + private interface Joiner { /** * Try to join lines. * * @param req@return null if cannot join, or ("what to insert", cursor_offset). */ @Nullable - abstract public Result join(Request req); + Result join(@NotNull Request req); } - private static class OpenBracketJoiner extends Joiner { - private static TokenSet OPENS = TokenSet.create(PyTokenTypes.LBRACKET, PyTokenTypes.LBRACE, PyTokenTypes.LPAR); + private static class OpenBracketJoiner implements Joiner { + private static final TokenSet OPENS = TokenSet.create(PyTokenTypes.LBRACKET, PyTokenTypes.LBRACE, PyTokenTypes.LPAR); + @Override - public Result join(Request req) { - if (OPENS.contains(req.leftElem().getNode().getElementType())) { + public Result join(@NotNull Request req) { + if (OPENS.contains(req.leftElem.getNode().getElementType())) { // TODO: look at settings for space after opening paren return new Result("", 0); } @@ -253,11 +180,12 @@ public class PyJoinLinesHandler implements JoinRawLinesHandlerDelegate { } } - private static class CloseBracketJoiner extends Joiner { - private static TokenSet CLOSES = TokenSet.create(PyTokenTypes.RBRACKET, PyTokenTypes.RBRACE, PyTokenTypes.RPAR); + private static class CloseBracketJoiner implements Joiner { + private static final TokenSet CLOSES = TokenSet.create(PyTokenTypes.RBRACKET, PyTokenTypes.RBRACE, PyTokenTypes.RPAR); + @Override - public Result join(Request req) { - if (CLOSES.contains(req.rightElem().getNode().getElementType())) { + public Result join(@NotNull Request req) { + if (CLOSES.contains(req.rightElem.getNode().getElementType())) { // TODO: look at settings for space before closing paren return new Result("", 0); } @@ -265,10 +193,10 @@ public class PyJoinLinesHandler implements JoinRawLinesHandlerDelegate { } } - private static class BinaryExprJoiner extends Joiner { + private static class BinaryExprJoiner implements Joiner { @Override - public Result join(Request req) { - if (req.leftExpr() instanceof PyBinaryExpression || req.rightExpr() instanceof PyBinaryExpression) { + public Result join(@NotNull Request req) { + if (req.leftExpr instanceof PyBinaryExpression || req.rightExpr instanceof PyBinaryExpression) { // TODO: look at settings for space around binary exprs return new Result(" ", 1); } @@ -276,29 +204,13 @@ public class PyJoinLinesHandler implements JoinRawLinesHandlerDelegate { } } - private static class ListLikeExprJoiner extends Joiner { + private static class StmtJoiner implements Joiner { @Override - public Result join(Request req) { - final boolean left_is_list_like = PyUtil.instanceOf(req.leftExpr(), PyListLiteralExpression.class, PyTupleExpression.class); - if (left_is_list_like || PyUtil.instanceOf(req.rightExpr(), PyListLiteralExpression.class, PyTupleExpression.class) - ) { - String insert = ""; - if (left_is_list_like) { // we join "a, \n b", not "a \n ,b" - insert = " "; // TODO: look at settings for space after commas in lists - } - return new Result(insert, insert.length()); - } - return null; - } - } - - private static class StmtJoiner extends Joiner { - @Override - public Result join(Request req) { - PyStatement left_stmt = PsiTreeUtil.getParentOfType(req.leftExpr(), PyStatement.class); - if (left_stmt != null) { - PyStatement right_stmt = PsiTreeUtil.getParentOfType(req.rightExpr(), PyStatement.class); - if (right_stmt != null && right_stmt != left_stmt) { + public Result join(@NotNull Request req) { + final PyStatement leftStmt = PsiTreeUtil.getParentOfType(req.leftExpr, PyStatement.class); + if (leftStmt != null) { + final PyStatement rightStmt = PsiTreeUtil.getParentOfType(req.rightExpr, PyStatement.class); + if (rightStmt != null && rightStmt != leftStmt) { // TODO: look at settings for space after semicolon return new Result("; ", 1); // cursor after semicolon } @@ -307,34 +219,33 @@ public class PyJoinLinesHandler implements JoinRawLinesHandlerDelegate { } } - private static class StringLiteralJoiner extends Joiner { + private static class StringLiteralJoiner implements Joiner { @Override - public Result join(Request req) { - if (req.leftElem() != req.rightElem()) { - final PsiElement parent = req.rightElem().getParent(); - if ((req.leftElem().getParent() == parent && parent instanceof PyStringLiteralExpression) || - (req.leftExpr() instanceof PyStringLiteralExpression && req.rightExpr() instanceof PyStringLiteralExpression) - ) { + public Result join(@NotNull Request req) { + if (req.leftElem != req.rightElem) { + final PsiElement parent = req.rightElem.getParent(); + if ((req.leftElem.getParent() == parent && parent instanceof PyStringLiteralExpression) || + (req.leftExpr instanceof PyStringLiteralExpression && req.rightExpr instanceof PyStringLiteralExpression)) { // two quoted strings close by - CharSequence text = req.document().getCharsSequence(); - StrMod left_mod = new StrMod(text, req.leftElem().getTextRange()); - StrMod right_mod = new StrMod(text, req.rightElem().getTextRange()); - if (left_mod.isOk() && right_mod.isOk()) { - final String lquo = left_mod.quote(); - if (left_mod.equals(right_mod)) { - return new Result("", 0, lquo.length(), right_mod.getStartPadding()); + final CharSequence text = req.document.getCharsSequence(); + final StrMod leftMod = new StrMod(text, req.leftElem.getTextRange()); + final StrMod rightMod = new StrMod(text, req.rightElem.getTextRange()); + if (leftMod.isOk() && rightMod.isOk()) { + final String lquo = leftMod.quote(); + if (leftMod.equals(rightMod)) { + return new Result("", 0, lquo.length(), rightMod.getStartPadding()); } - else if (left_mod.compatibleTo(right_mod) && lquo.length() == 1 && right_mod.quote().length() == 1) { + else if (leftMod.compatibleTo(rightMod) && lquo.length() == 1 && rightMod.quote().length() == 1) { // maybe fit one literal's quotes to match other's - if (! containsChar(text, right_mod.getInnerRange(), left_mod.quote().charAt(0))) { - int quote_pos = right_mod.getInnerRange().getEndOffset(); - req.document().replaceString(quote_pos, quote_pos+1, left_mod.quote()); - return new Result("", 0, left_mod.quote().length(), right_mod.getStartPadding()); + if (!containsChar(text, rightMod.getInnerRange(), leftMod.quote().charAt(0))) { + final int quotePos = rightMod.getInnerRange().getEndOffset(); + req.document.replaceString(quotePos, quotePos + 1, leftMod.quote()); + return new Result("", 0, leftMod.quote().length(), rightMod.getStartPadding()); } - else if (! containsChar(text, left_mod.getInnerRange(), right_mod.quote().charAt(0))) { - int quote_pos = left_mod.getInnerRange().getStartOffset()-1; - req.document().replaceString(quote_pos, quote_pos+1, right_mod.quote()); - return new Result("", 0, left_mod.quote().length(), right_mod.getStartPadding()); + else if (!containsChar(text, leftMod.getInnerRange(), rightMod.quote().charAt(0))) { + final int quotePos = leftMod.getInnerRange().getStartOffset() - 1; + req.document.replaceString(quotePos, quotePos + 1, rightMod.quote()); + return new Result("", 0, leftMod.quote().length(), rightMod.getStartPadding()); } } } @@ -343,71 +254,71 @@ public class PyJoinLinesHandler implements JoinRawLinesHandlerDelegate { return null; } - protected static boolean containsChar(CharSequence text, TextRange range, char c) { - for (int i=range.getStartOffset(); i <= range.getEndOffset(); i+=1) { - if (text.charAt(i) == c) return true; - } - return false; + protected static boolean containsChar(@NotNull CharSequence text, @NotNull TextRange range, char c) { + return StringUtil.contains(text, range.getStartOffset(), range.getEndOffset(), c); } - private static class StrMod { - private final String myPrefix; // "u", "b", or "" - private final boolean myRaw; // is raw or not - private final String myQuote; // single or double, one or triple. - private final boolean myOk; // true if parsing went ok - private final TextRange myInnerRange; - public StrMod(CharSequence text, TextRange range) { + private static class StrMod { + @NotNull private final String myPrefix; // "u", "b", or "" + private final boolean myRaw; // is raw or not + @NotNull private final String myQuote; // single or double, one or triple. + + @Nullable private final TextRange myInnerRange; + + public StrMod(@NotNull CharSequence text, @NotNull TextRange range) { int pos = range.getStartOffset(); char c = text.charAt(pos); if ("Uu".indexOf(c) > -1 || "Bb".indexOf(c) > -1) { - myPrefix = String.valueOf(c).toLowerCase(); - pos +=1; + myPrefix = String.valueOf(c).toLowerCase(Locale.US); + pos += 1; c = text.charAt(pos); } - else myPrefix = ""; + else { + myPrefix = ""; + } if ("Rr".indexOf(c) > -1) { myRaw = true; - pos +=1; + pos += 1; c = text.charAt(pos); } - else myRaw = false; - char quote = c; + else { + myRaw = false; + } + final char quote = c; if ("'\"".indexOf(quote) < 0) { myInnerRange = null; myQuote = ""; - myOk = false; return; // failed to find a quote } // TODO: we could run a simple but complete parser here, only checking escapes - if (range.getLength() >= 6 && text.charAt(pos+1) == quote && text.charAt(pos+2) == quote) { - myQuote = text.subSequence(pos, pos+3).toString(); - if (!myQuote.equals(text.subSequence(range.getEndOffset()-3, range.getEndOffset()).toString())) { + if (range.getLength() >= 6 && text.charAt(pos + 1) == quote && text.charAt(pos + 2) == quote) { + myQuote = text.subSequence(pos, pos + 3).toString(); + if (!myQuote.equals(text.subSequence(range.getEndOffset() - 3, range.getEndOffset()).toString())) { myInnerRange = null; - myOk = false; return; } } else { - myQuote = text.subSequence(pos, pos+1).toString(); - if (!myQuote.equals(text.subSequence(range.getEndOffset()-1, range.getEndOffset()).toString())) { + myQuote = text.subSequence(pos, pos + 1).toString(); + if (!myQuote.equals(text.subSequence(range.getEndOffset() - 1, range.getEndOffset()).toString())) { myInnerRange = null; - myOk = false; return; } } - myInnerRange = TextRange.from(range.getStartOffset()+getStartPadding(), range.getLength()-getStartPadding()-quote().length()); - myOk = true; + myInnerRange = TextRange.from(range.getStartOffset() + getStartPadding(), range.getLength() - getStartPadding() - quote().length()); } public boolean isOk() { - return myOk; + return myInnerRange != null; } + @NotNull public String prefix() { return myPrefix; } + @NotNull public String quote() { return myQuote; } @@ -420,12 +331,7 @@ public class PyJoinLinesHandler implements JoinRawLinesHandlerDelegate { public boolean equals(Object o) { if (o instanceof StrMod) { final StrMod other = (StrMod)o; - return ( - myOk && other.isOk() && - myRaw == other.isRaw() && - myPrefix.equals(other.prefix()) && - myQuote.equals(other.quote()) - ); + return compatibleTo(other) && myQuote.equals(other.quote()); } return false; } @@ -434,44 +340,60 @@ public class PyJoinLinesHandler implements JoinRawLinesHandlerDelegate { * @return combined length of initial modifier letters and opening quotes */ public int getStartPadding() { - return myQuote.length() + myPrefix.length() + (myRaw? 1 : 0); + return myQuote.length() + myPrefix.length() + (myRaw ? 1 : 0); } /** * @param other * @return true iff this and other have the same byte/unicode and raw prefixes. */ - public boolean compatibleTo(StrMod other) { - return myOk && other.isOk() && myRaw == other.isRaw() && myPrefix.equals(other.prefix()); + public boolean compatibleTo(@NotNull StrMod other) { + return isOk() && other.isOk() && myRaw == other.isRaw() && myPrefix.equals(other.prefix()); } - /** * @return range of text part inside quotes */ + @Nullable public TextRange getInnerRange() { return myInnerRange; } } - } - - private static class CommentJoiner extends Joiner { + private static class CommentJoiner implements Joiner { @Override - public Result join(Request req) { - if (req.leftElem() instanceof PsiComment && req.rightElem() instanceof PsiComment) { - CharSequence text = req.document().getCharsSequence(); - final TextRange right_range = req.rightElem().getTextRange(); - int initial_pos = right_range.getStartOffset() + 1; - int pos = initial_pos; // cut '#' - int last = right_range.getEndOffset(); + public Result join(@NotNull Request req) { + if (req.leftElem instanceof PsiComment && req.rightElem instanceof PsiComment) { + final CharSequence text = req.document.getCharsSequence(); + final TextRange rightRange = req.rightElem.getTextRange(); + final int initialPos = rightRange.getStartOffset() + 1; + int pos = initialPos; // cut '#' + final int last = rightRange.getEndOffset(); while (pos < last && " \t".indexOf(text.charAt(pos)) >= 0) pos += 1; - int right = pos - initial_pos + 1; // account for the '#' + final int right = pos - initialPos + 1; // account for the '#' return new Result(" ", 0, 0, right); } return null; } } + private static class StripBackslashJoiner implements Joiner { + static final TokenSet SINGLE_QUOTED_STRINGS = TokenSet.create(PyTokenTypes.SINGLE_QUOTED_STRING, PyTokenTypes.SINGLE_QUOTED_UNICODE); + @Nullable + @Override + public Result join(@NotNull Request req) { + final String gap = req.document.getText(new TextRange(req.firstLineEndOffset + 1, req.secondLineStartOffset)); + final int index = gap.indexOf('\\'); + if (index >= 0) { + if (req.leftElem == req.rightElem && SINGLE_QUOTED_STRINGS.contains(req.leftElem.getNode().getElementType())) { + return new Result(gap.replaceFirst("\\\\\\n", ""), 0); + } + else { + return new Result(gap.substring(0, index), 0); + } + } + return null; + } + } } diff --git a/python/testData/joinLines/BackslashBetweenTargetsInFromImport-after.py b/python/testData/joinLines/BackslashBetweenTargetsInFromImport-after.py new file mode 100644 index 000000000000..c5e9ca390478 --- /dev/null +++ b/python/testData/joinLines/BackslashBetweenTargetsInFromImport-after.py @@ -0,0 +1 @@ +from foo import bar, baz \ No newline at end of file diff --git a/python/testData/joinLines/BackslashBetweenTargetsInFromImport.py b/python/testData/joinLines/BackslashBetweenTargetsInFromImport.py new file mode 100644 index 000000000000..111ebe2309cb --- /dev/null +++ b/python/testData/joinLines/BackslashBetweenTargetsInFromImport.py @@ -0,0 +1,2 @@ +from foo import bar, \ + baz \ No newline at end of file diff --git a/python/testData/joinLines/BackslashBetweenTargetsInImport-after.py b/python/testData/joinLines/BackslashBetweenTargetsInImport-after.py new file mode 100644 index 000000000000..dbb57650d11d --- /dev/null +++ b/python/testData/joinLines/BackslashBetweenTargetsInImport-after.py @@ -0,0 +1 @@ +import foo, bar \ No newline at end of file diff --git a/python/testData/joinLines/BackslashBetweenTargetsInImport.py b/python/testData/joinLines/BackslashBetweenTargetsInImport.py new file mode 100644 index 000000000000..a4b81ae12319 --- /dev/null +++ b/python/testData/joinLines/BackslashBetweenTargetsInImport.py @@ -0,0 +1,2 @@ +import foo, \ + bar \ No newline at end of file diff --git a/python/testData/joinLines/StatementColon-after.py b/python/testData/joinLines/StatementColon-after.py index f24e21a96770..87edeeb4a4b2 100644 --- a/python/testData/joinLines/StatementColon-after.py +++ b/python/testData/joinLines/StatementColon-after.py @@ -1,2 +1 @@ -if True: - return "No special handling" +if True: return "No special handling" diff --git a/python/testData/joinLines/StatementCommentStatement-after.py b/python/testData/joinLines/StatementCommentStatement-after.py index f7f12f8a4499..dead19b0a86c 100644 --- a/python/testData/joinLines/StatementCommentStatement-after.py +++ b/python/testData/joinLines/StatementCommentStatement-after.py @@ -1,2 +1 @@ -a = 1 # surprise! -b = 2 +a = 1 # surprise! b = 2 diff --git a/python/testData/joinLines/StringOneQuotePlainRaw-after.py b/python/testData/joinLines/StringOneQuotePlainRaw-after.py index b25c96d6cf30..612be2bc5e33 100644 --- a/python/testData/joinLines/StringOneQuotePlainRaw-after.py +++ b/python/testData/joinLines/StringOneQuotePlainRaw-after.py @@ -1,2 +1 @@ -print (r"See, \n separates two lines: " - "this and\nthe other") +print (r"See, \n separates two lines: " "this and\nthe other") diff --git a/python/testData/joinLines/StringOneQuotePlainU-after.py b/python/testData/joinLines/StringOneQuotePlainU-after.py index 52a8d45e4d46..54db7a68ae2a 100644 --- a/python/testData/joinLines/StringOneQuotePlainU-after.py +++ b/python/testData/joinLines/StringOneQuotePlainU-after.py @@ -1,2 +1 @@ -a = (u"Ein Deutsche Text. " - "An English text") +a = (u"Ein Deutsche Text. " "An English text") diff --git a/python/testData/joinLines/StringTripleQuotesDifferent-after.py b/python/testData/joinLines/StringTripleQuotesDifferent-after.py index 8adc69904a41..5c73f88dcbae 100644 --- a/python/testData/joinLines/StringTripleQuotesDifferent-after.py +++ b/python/testData/joinLines/StringTripleQuotesDifferent-after.py @@ -1,3 +1,2 @@ -'''This doc comment -is quite separate''' +'''This doc comment is quite separate''' """from the literal below""" diff --git a/python/testSrc/com/jetbrains/python/PyJoinLinesTest.java b/python/testSrc/com/jetbrains/python/PyJoinLinesTest.java index 856ddb708b0a..994aa7880fa1 100644 --- a/python/testSrc/com/jetbrains/python/PyJoinLinesTest.java +++ b/python/testSrc/com/jetbrains/python/PyJoinLinesTest.java @@ -15,70 +15,137 @@ */ package com.jetbrains.python; -import com.intellij.codeInsight.editorActions.JoinRawLinesHandlerDelegate; -import com.intellij.openapi.command.WriteCommandAction; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.editor.Editor; -import com.jetbrains.python.editor.PyJoinLinesHandler; +import com.intellij.openapi.actionSystem.IdeActions; import com.jetbrains.python.fixtures.PyTestCase; /** * Tests the "Join lines" handler. - *
- * User: dcheryasov - * Date: 1/29/11 2:33 AM + * + * @author dcheryasov */ public class PyJoinLinesTest extends PyTestCase { private void doTest() { - final String path = "joinLines/"; - myFixture.configureByFile(path + getTestName(false) + ".py"); - // detect whitespace around EOL, the way JoinLinesHandler does. - final Editor editor = myFixture.getEditor(); - final Document doc = editor.getDocument(); - final int caret_line = doc.getLineNumber(editor.getCaretModel().getOffset()); - final int eol_pos = doc.getLineEndOffset(caret_line); - CharSequence text = doc.getCharsSequence(); - int i = eol_pos; - while (" \n\t".indexOf(text.charAt(i)) >= 0) i -= 1; - final int start = i+1; - i = eol_pos; - while (" \n\t".indexOf(text.charAt(i)) >= 0) i += 1; - final int end = i; - final JoinRawLinesHandlerDelegate handler = new PyJoinLinesHandler(); - WriteCommandAction.runWriteCommandAction(myFixture.getProject(), new Runnable() { - @Override - public void run() { - handler.tryJoinRawLines(doc, myFixture.getFile(), start, end); - } - }); - myFixture.checkResultByFile(path + getTestName(false) + "-after.py"); + myFixture.configureByFile("joinLines/" + getTestName(false) + ".py"); + myFixture.performEditorAction(IdeActions.ACTION_EDITOR_JOIN_LINES); + myFixture.checkResultByFile("joinLines/" + getTestName(false) + "-after.py"); } - public void testBinaryOpBelow() { doTest(); } - public void testBinaryOp() { doTest(); } - public void testDictLCurly() { doTest(); } - public void testDictRCurly() { doTest(); } - public void testListLBracket() { doTest(); } - public void testList() { doTest(); } - public void testListRBracket() { doTest(); } - public void testStatementColon() { doTest(); } - public void testStatementComment() { doTest(); } - public void testStatementCommentStatement() { doTest(); } - public void testStringDifferentOneQuotes() { doTest(); } - public void testStringDifferentOneQuotesBelow() { doTest(); } - public void testStringOneQuoteEscEOL() { doTest(); } - public void testStringOneQuotePlainRaw() { doTest(); } - public void testStringOneQuotePlainU() { doTest(); } - public void testStringTripleQuotesDifferent() { doTest(); } - public void testStringTripleQuotes() { doTest(); } - public void testTupleLPar() { doTest(); } - public void testTuple() { doTest(); } - public void testTupleRPar() { doTest(); } - public void testTwoComments() { doTest(); } - public void testTwoComments2() { doTest(); } // PY-7286 - public void testTwoStatements() { doTest(); } - public void testStringWithSlash() { doTest(); } - public void testListOfStrings() { doTest(); } - public void testLongExpression() { doTest(); } - public void testListComprehension() { doTest(); } + public void testBinaryOpBelow() { + doTest(); + } + + public void testBinaryOp() { + doTest(); + } + + public void testDictLCurly() { + doTest(); + } + + public void testDictRCurly() { + doTest(); + } + + public void testListLBracket() { + doTest(); + } + + public void testList() { + doTest(); + } + + public void testListRBracket() { + doTest(); + } + + public void testStatementColon() { + doTest(); + } + + public void testStatementComment() { + doTest(); + } + + public void testStatementCommentStatement() { + doTest(); + } + + public void testStringDifferentOneQuotes() { + doTest(); + } + + public void testStringDifferentOneQuotesBelow() { + doTest(); + } + + public void testStringOneQuoteEscEOL() { + doTest(); + } + + public void testStringOneQuotePlainRaw() { + doTest(); + } + + public void testStringOneQuotePlainU() { + doTest(); + } + + public void testStringTripleQuotesDifferent() { + doTest(); + } + + public void testStringTripleQuotes() { + doTest(); + } + + public void testTupleLPar() { + doTest(); + } + + public void testTuple() { + doTest(); + } + + public void testTupleRPar() { + doTest(); + } + + public void testTwoComments() { + doTest(); + } + + // PY-7286 + public void testTwoComments2() { + doTest(); + } + + public void testTwoStatements() { + doTest(); + } + + public void testStringWithSlash() { + doTest(); + } + + public void testListOfStrings() { + doTest(); + } + + public void testLongExpression() { + doTest(); + } + + public void testListComprehension() { + doTest(); + } + + // PY-15564 + public void testBackslashBetweenTargetsInImport() { + doTest(); + } + + // PY-15564 + public void testBackslashBetweenTargetsInFromImport() { + doTest(); + } }