From a7ed9f4c98863eaf6eb8cc567aea5b252cede839 Mon Sep 17 00:00:00 2001 From: Nadya Zabrodina Date: Wed, 15 Apr 2015 16:34:41 +0400 Subject: [PATCH 01/55] DRY: remove code duplication --- .../src/org/zmlx/hg4idea/util/HgUtil.java | 38 +------------------ 1 file changed, 1 insertion(+), 37 deletions(-) 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); } From dda8aeaf1ad93daf17cfa7e58762bc3b2d2021e8 Mon Sep 17 00:00:00 2001 From: Nadya Zabrodina Date: Wed, 15 Apr 2015 18:09:31 +0400 Subject: [PATCH 02/55] all hg vcs actions changed to be dump aware; unnecessary constructors removed; pull action icon moved to appropriate xml --- plugins/hg4idea/src/META-INF/plugin.xml | 2 +- .../zmlx/hg4idea/action/HgAbstractGlobalAction.java | 12 ++---------- .../action/HgAbstractGlobalSingleRepoAction.java | 9 --------- .../src/org/zmlx/hg4idea/action/HgPullAction.java | 4 ---- 4 files changed, 3 insertions(+), 24 deletions(-) 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) { From 93fc13c5bf71e081735ade0d4e13b88fc204e462 Mon Sep 17 00:00:00 2001 From: "Vladimir.Orlov" Date: Wed, 15 Apr 2015 21:13:19 +0300 Subject: [PATCH 03/55] IDEA-138990 Rearrange jdk distributions in "JRE Redistribution" artifacts --- build/scripts/nsis_installer.gant | 1 + build/scripts/utils.gant | 21 +++++++++++---------- python/edu/build/pycharm_edu_build.gant | 3 +-- 3 files changed, 13 insertions(+), 12 deletions(-) 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/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") From 751076feb24f801c122337e4a7af721be742cc2f Mon Sep 17 00:00:00 2001 From: peter Date: Thu, 16 Apr 2015 09:51:45 +0300 Subject: [PATCH 04/55] LowMemoryWatcher.stopAll should remove low memory listener --- .../openapi/util/LowMemoryWatcher.java | 37 ++++++++++++------- 1 file changed, 23 insertions(+), 14 deletions(-) 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); + } } } From f33feb4ba290e19f1feae33fe4ad760511d8e3be Mon Sep 17 00:00:00 2001 From: Dennis Ushakov Date: Thu, 16 Apr 2015 10:28:22 +0300 Subject: [PATCH 05/55] IDEA-CR-2330 --- .../src/com/intellij/util/indexing/FileBasedIndexImpl.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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; } From 650d5ec707d379bb86f75ed80b30b2873c277970 Mon Sep 17 00:00:00 2001 From: Mikhail Golubev Date: Wed, 15 Apr 2015 14:22:01 +0300 Subject: [PATCH 06/55] Use simple comparison chain to create JQL PSI elements in parser definition Yann Cebron suggested to do so to reduce number of classes loaded at IDE startup. --- .../tasks/jira/jql/JqlElementType.java | 28 ------ .../tasks/jira/jql/JqlElementTypes.java | 97 +++++++++++++++---- .../tasks/jira/jql/JqlParserDefinition.java | 8 +- 3 files changed, 81 insertions(+), 52 deletions(-) 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 From 3d0e9bc228644103a5de48f9d1a52251e6bcc03b Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Thu, 16 Apr 2015 13:06:41 +0300 Subject: [PATCH 07/55] [git] Display rebase error right in the notification Don't tell user to go to the VCS Console. Use the new GitImpl variant to call Git command instead of the obsolete GitTask. Also fix the situation when workingTreeChangeStarted was actually reported after rebase command completed. --- .../git4idea/actions/GitRebaseActionBase.java | 44 ++++++++----------- .../git4idea/src/git4idea/commands/Git.java | 8 ++++ .../src/git4idea/commands/GitImpl.java | 18 ++++++-- .../src/git4idea/i18n/GitBundle.properties | 1 - 4 files changed, 42 insertions(+), 29 deletions(-) diff --git a/plugins/git4idea/src/git4idea/actions/GitRebaseActionBase.java b/plugins/git4idea/src/git4idea/actions/GitRebaseActionBase.java index af0993eef262..9652625f5c68 100644 --- a/plugins/git4idea/src/git4idea/actions/GitRebaseActionBase.java +++ b/plugins/git4idea/src/git4idea/actions/GitRebaseActionBase.java @@ -17,16 +17,17 @@ package git4idea.actions; import com.intellij.dvcs.DvcsUtil; import com.intellij.openapi.application.AccessToken; +import com.intellij.openapi.components.ServiceManager; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.progress.Task; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vcs.VcsNotifier; import com.intellij.openapi.vfs.VirtualFile; import git4idea.GitUtil; +import git4idea.commands.Git; +import git4idea.commands.GitCommandResult; import git4idea.commands.GitLineHandler; -import git4idea.commands.GitTask; -import git4idea.commands.GitTaskResult; -import git4idea.commands.GitTaskResultHandlerAdapter; import git4idea.i18n.GitBundle; import git4idea.rebase.GitInteractiveRebaseEditorHandler; import git4idea.rebase.GitRebaseEditorService; @@ -50,7 +51,7 @@ public abstract class GitRebaseActionBase extends GitRepositoryAction { @NotNull final VirtualFile defaultRoot, final Set 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. @@ -115,15 +115,9 @@ public abstract class GitRebaseActionBase extends GitRepositoryAction { messageId = null; } - 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/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. From 51ac666d43718d5ecc52d1b2fec7c3663deb40c4 Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Thu, 16 Apr 2015 13:10:27 +0300 Subject: [PATCH 08/55] [git] rebase dialog: focus "onto" field by default --- plugins/git4idea/src/git4idea/rebase/GitRebaseDialog.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/plugins/git4idea/src/git4idea/rebase/GitRebaseDialog.java b/plugins/git4idea/src/git4idea/rebase/GitRebaseDialog.java index 649c25486ce6..51c86a93bdbc 100644 --- a/plugins/git4idea/src/git4idea/rebase/GitRebaseDialog.java +++ b/plugins/git4idea/src/git4idea/rebase/GitRebaseDialog.java @@ -33,6 +33,7 @@ import git4idea.repo.GitRemote; import git4idea.repo.GitRepository; import git4idea.ui.GitReferenceValidator; import git4idea.util.GitUIUtil; +import org.jetbrains.annotations.Nullable; import javax.swing.*; import javax.swing.event.DocumentEvent; @@ -162,6 +163,12 @@ public class GitRebaseDialog extends DialogWrapper { validateFields(); } + @Nullable + @Override + public JComponent getPreferredFocusedComponent() { + return myOntoComboBox; + } + public GitLineHandler handler() { GitLineHandler h = new GitLineHandler(myProject, gitRoot(), GitCommand.REBASE); h.setStdoutSuppressed(false); From 043e5d22d4a7d93bc0ad9e0a6fed9a7564a1cf58 Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Thu, 16 Apr 2015 13:22:37 +0300 Subject: [PATCH 09/55] [git] IDEA-137632 speed search highlighting in interactive rebase --- .../git4idea/src/git4idea/rebase/GitRebaseEditor.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/plugins/git4idea/src/git4idea/rebase/GitRebaseEditor.java b/plugins/git4idea/src/git4idea/rebase/GitRebaseEditor.java index 7514aa940bbe..f14ed780c58d 100644 --- a/plugins/git4idea/src/git4idea/rebase/GitRebaseEditor.java +++ b/plugins/git4idea/src/git4idea/rebase/GitRebaseEditor.java @@ -22,7 +22,9 @@ 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; @@ -132,6 +134,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); From 6d405bc74c0ee9d9a31a522bf828e5b6f85a6242 Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Thu, 16 Apr 2015 11:09:03 +0200 Subject: [PATCH 10/55] testng: preserve order in which tests are found --- .../testng/src/com/theoryinpractice/testng/util/TestNGUtil.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/testng/src/com/theoryinpractice/testng/util/TestNGUtil.java b/plugins/testng/src/com/theoryinpractice/testng/util/TestNGUtil.java index 38fd89d5e695..0375b4a1b260 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()); From 538db34fc0d3962eb9c9eb35efcc216d76e4399e Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Thu, 16 Apr 2015 12:18:07 +0200 Subject: [PATCH 11/55] testng: extract strategy --- .../configuration/SearchingForTestsTask.java | 342 +----------------- .../configuration/TestNGConfiguration.java | 73 +--- .../TestNGConfigurationProducer.java | 3 +- .../TestNGConfigurationType.java | 4 +- .../testng/model/TestData.java | 49 +-- .../testng/model/TestNGTestClass.java | 95 +++++ .../testng/model/TestNGTestGroup.java | 63 ++++ .../testng/model/TestNGTestMethod.java | 110 ++++++ .../testng/model/TestNGTestObject.java | 247 +++++++++++++ .../testng/model/TestNGTestPackage.java | 103 ++++++ .../testng/model/TestNGTestPattern.java | 145 ++++++++ .../testng/model/TestNGTestSuite.java | 61 ++++ 12 files changed, 847 insertions(+), 448 deletions(-) create mode 100644 plugins/testng/src/com/theoryinpractice/testng/model/TestNGTestClass.java create mode 100644 plugins/testng/src/com/theoryinpractice/testng/model/TestNGTestGroup.java create mode 100644 plugins/testng/src/com/theoryinpractice/testng/model/TestNGTestMethod.java create mode 100644 plugins/testng/src/com/theoryinpractice/testng/model/TestNGTestObject.java create mode 100644 plugins/testng/src/com/theoryinpractice/testng/model/TestNGTestPackage.java create mode 100644 plugins/testng/src/com/theoryinpractice/testng/model/TestNGTestPattern.java create mode 100644 plugins/testng/src/com/theoryinpractice/testng/model/TestNGTestSuite.java diff --git a/plugins/testng/src/com/theoryinpractice/testng/configuration/SearchingForTestsTask.java b/plugins/testng/src/com/theoryinpractice/testng/configuration/SearchingForTestsTask.java index da4701d312ad..5dade70f8fce 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()); @@ -280,182 +271,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); } } @@ -492,152 +310,4 @@ public class SearchingForTestsTask extends SearchForTestsTask { return params; } - 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/model/TestData.java b/plugins/testng/src/com/theoryinpractice/testng/model/TestData.java index 2a75977483bb..48d925fbf93b 100644 --- a/plugins/testng/src/com/theoryinpractice/testng/model/TestData.java +++ b/plugins/testng/src/com/theoryinpractice/testng/model/TestData.java @@ -18,16 +18,15 @@ package com.theoryinpractice.testng.model; import com.intellij.execution.ExternalizablePath; import com.intellij.execution.JavaExecutionUtil; import com.intellij.execution.Location; -import com.intellij.execution.configurations.JavaRunConfigurationModule; import com.intellij.execution.junit.JUnitUtil; import com.intellij.execution.junit2.info.MethodLocation; import com.intellij.execution.testframework.TestSearchScope; import com.intellij.openapi.module.Module; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Comparing; -import com.intellij.openapi.util.text.StringUtil; -import com.intellij.psi.*; -import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiMethod; +import com.intellij.psi.PsiPackage; import java.util.*; @@ -174,26 +173,6 @@ public class TestData implements Cloneable return data; } - public String getGeneratedName(JavaRunConfigurationModule runconfigurationmodule) { - if (TestType.PACKAGE.getType().equals(TEST_OBJECT)) if (getPackageName().length() == 0) return ""; - 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"); + } + } +} From c88e351c0e0942801d1755b8dfe3acb3a377dde2 Mon Sep 17 00:00:00 2001 From: Eugene Zhuravlev Date: Thu, 16 Apr 2015 12:38:36 +0200 Subject: [PATCH 12/55] module disposed check --- .../impl/src/com/intellij/compiler/impl/CompileDriver.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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..b8c107e3ad35 100644 --- a/java/compiler/impl/src/com/intellij/compiler/impl/CompileDriver.java +++ b/java/compiler/impl/src/com/intellij/compiler/impl/CompileDriver.java @@ -480,7 +480,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)); From ff41cf4e51294920eacd13a142c85cb264101f12 Mon Sep 17 00:00:00 2001 From: "Maxim.Mossienko" Date: Thu, 16 Apr 2015 13:06:40 +0200 Subject: [PATCH 13/55] [performance] avoid extra lexing for lazy parseable elements --- .../intellij/lang/impl/PsiBuilderImpl.java | 65 ++++++++++++++++--- 1 file changed, 55 insertions(+), 10 deletions(-) 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 + "'"); } From 8807c84a84c6cc3a7ddea37b6914be05ab5d8f72 Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Tue, 14 Apr 2015 18:33:41 +0300 Subject: [PATCH 14/55] [git] IDEA-130665 Remember rebase dialog choices --- plugins/git4idea/src/META-INF/plugin.xml | 1 + .../git4idea/config/GitRebaseSettings.java | 89 +++++++++++++++++++ .../src/git4idea/rebase/GitRebaseDialog.java | 64 ++++++++++++- 3 files changed, 153 insertions(+), 1 deletion(-) create mode 100644 plugins/git4idea/src/git4idea/config/GitRebaseSettings.java 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 @@ + { + + 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/rebase/GitRebaseDialog.java b/plugins/git4idea/src/git4idea/rebase/GitRebaseDialog.java index 51c86a93bdbc..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,14 @@ 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.*; @@ -116,7 +120,7 @@ public class GitRebaseDialog extends DialogWrapper { /** * The current branch */ - protected GitBranch myCurrentBranch; + @Nullable protected GitBranch myCurrentBranch; /** * The tags */ @@ -129,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 @@ -143,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(); @@ -158,8 +166,18 @@ 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(); } @@ -169,6 +187,29 @@ public class GitRebaseDialog extends DialogWrapper { 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); @@ -202,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 */ From 0b2e95a23dfd6e9019770b1f94f3e27794caea3a Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Wed, 15 Apr 2015 18:46:58 +0300 Subject: [PATCH 15/55] IDEA-136646 IDE should be responsive during global inspections --- .../ex/GlobalInspectionContextImpl.java | 34 ++++++++++--------- 1 file changed, 18 insertions(+), 16 deletions(-) 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; From 3d75cfde13a84113dbe3903a400fcc8da50c1b84 Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Wed, 15 Apr 2015 18:48:00 +0300 Subject: [PATCH 16/55] notnull --- .../src/com/intellij/codeInsight/TargetElementUtil.java | 3 ++- .../src/com/intellij/codeInsight/TargetElementUtilBase.java | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) 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/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(); From 3fd838db562699e3225265568461e7133f473019 Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Wed, 15 Apr 2015 18:48:09 +0300 Subject: [PATCH 17/55] IDEA-138236 Multiresolve behaves differently in 14.1 --- .../codeInsight/navigation/CtrlMouseHandler.java | 4 ++-- .../navigation/actions/GotoDeclarationAction.java | 11 ++++++----- 2 files changed, 8 insertions(+), 7 deletions(-) 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(); } From bb3cd90e3987dcc61fff1c2f1191084023a5537a Mon Sep 17 00:00:00 2001 From: Eugene Zhuravlev Date: Thu, 16 Apr 2015 13:36:43 +0200 Subject: [PATCH 18/55] module disposed check --- .../src/com/intellij/openapi/compiler/ex/CompilerPathsEx.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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; } From 93b3356fcc240fe96ed5e6746d61afb22c62641d Mon Sep 17 00:00:00 2001 From: "Vladislav.Soroka" Date: Thu, 16 Apr 2015 14:00:55 +0300 Subject: [PATCH 19/55] maven: manifest serialization/deserialization to/from xml fix --- .../MavenCommonArtifactRootCopyingHandlerProvider.java | 4 +++- .../MavenResourceCompilerConfigurationGenerator.java | 8 ++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) 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..477e6cf6811d 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 @@ -58,7 +58,9 @@ public class MavenCommonArtifactRootCopyingHandlerProvider extends ArtifactRootC projectConfiguration.moduleConfigurations.get(getModuleName(artifact.getName())); if (moduleResourceConfiguration != null && moduleResourceConfiguration.manifest != null) { try { - FileUtil.writeToFile(root, moduleResourceConfiguration.manifest); + final String manifest = + StringUtil.unescapeXml(moduleResourceConfiguration.manifest).replaceAll("\n\r|\n|\r", "\r\n") + "\r\n\r\n"; + FileUtil.writeToFile(root, 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..fb2855319089 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 @@ -23,6 +23,9 @@ 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 +225,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("UTF8"), domModel); + resourceConfig.manifest = StringUtil.escapeXml(resolvedText); } finally { StreamUtil.closeStream(outputStream); From 9da6c08cd27013e88c762bc58a912d1e9724866d Mon Sep 17 00:00:00 2001 From: Eugene Zhuravlev Date: Thu, 16 Apr 2015 13:56:21 +0200 Subject: [PATCH 20/55] handling ProcessCanceledException --- .../impl/src/com/intellij/compiler/impl/CompileDriver.java | 3 +++ 1 file changed, 3 insertions(+) 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 b8c107e3ad35..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 } From c59e2edeff640bcf976d00d17f62495beaeb9985 Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Tue, 14 Apr 2015 16:09:17 +0300 Subject: [PATCH 21/55] [git] extract reading interactive rebase data file to a separate class Preparation to fix IDEA-138787 --- .../GitInteractiveRebaseEditorHandler.java | 9 +- .../rebase/GitInteractiveRebaseFile.java | 96 +++++++++++++ .../src/git4idea/rebase/GitRebaseEditor.java | 129 ++---------------- 3 files changed, 116 insertions(+), 118 deletions(-) create mode 100644 plugins/git4idea/src/git4idea/rebase/GitInteractiveRebaseFile.java diff --git a/plugins/git4idea/src/git4idea/rebase/GitInteractiveRebaseEditorHandler.java b/plugins/git4idea/src/git4idea/rebase/GitInteractiveRebaseEditorHandler.java index b07cd4e23f0f..d2e12f0b786f 100644 --- a/plugins/git4idea/src/git4idea/rebase/GitInteractiveRebaseEditorHandler.java +++ b/plugins/git4idea/src/git4idea/rebase/GitInteractiveRebaseEditorHandler.java @@ -24,6 +24,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} @@ -115,15 +116,17 @@ public class GitInteractiveRebaseEditorHandler implements Closeable, GitRebaseEd } } else { + GitInteractiveRebaseFile rebaseFile = new GitInteractiveRebaseFile(myProject, myRoot, path); + List entries = rebaseFile.load(); setRebaseEditorShown(); - GitRebaseEditor editor = new GitRebaseEditor(myProject, myRoot, path); + GitRebaseEditor editor = new GitRebaseEditor(myProject, myRoot, entries); if (editor.showAndGet()) { - editor.save(); + rebaseFile.save(editor.getEntries()); isSuccess.set(true); return; } else { - editor.cancel(); + rebaseFile.cancel(); isSuccess.set(true); } } diff --git a/plugins/git4idea/src/git4idea/rebase/GitInteractiveRebaseFile.java b/plugins/git4idea/src/git4idea/rebase/GitInteractiveRebaseFile.java new file mode 100644 index 000000000000..e23920fc311f --- /dev/null +++ b/plugins/git4idea/src/git4idea/rebase/GitInteractiveRebaseFile.java @@ -0,0 +1,96 @@ +/* + * 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 { + String encoding = GitConfigUtil.getLogEncoding(myProject, myRoot); + List entries = ContainerUtil.newArrayList(); + final StringScanner s = new StringScanner(FileUtil.loadFile(new File(myFile), 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(); + + entries.add(new GitRebaseEntry(action, hash, comment)); + } + 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; + } +} diff --git a/plugins/git4idea/src/git4idea/rebase/GitRebaseEditor.java b/plugins/git4idea/src/git4idea/rebase/GitRebaseEditor.java index f14ed780c58d..51edcf439623 100644 --- a/plugins/git4idea/src/git4idea/rebase/GitRebaseEditor.java +++ b/plugins/git4idea/src/git4idea/rebase/GitRebaseEditor.java @@ -17,8 +17,6 @@ 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; @@ -33,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.*; @@ -48,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; @@ -83,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()); @@ -225,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} */ @@ -257,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; } @@ -284,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} */ @@ -367,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) { @@ -385,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(); From 970dbb2adbc1896ef153550c9384b6609bf3f61d Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Tue, 14 Apr 2015 16:45:25 +0300 Subject: [PATCH 22/55] [git] IDEA-138787 Handle 'noop' situation: show a confirmation message --- .../GitInteractiveRebaseEditorHandler.java | 36 +++++++++++++------ .../rebase/GitInteractiveRebaseFile.java | 16 +++++++-- 2 files changed, 40 insertions(+), 12 deletions(-) diff --git a/plugins/git4idea/src/git4idea/rebase/GitInteractiveRebaseEditorHandler.java b/plugins/git4idea/src/git4idea/rebase/GitInteractiveRebaseEditorHandler.java index d2e12f0b786f..c048c464ca48 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; @@ -116,18 +118,32 @@ public class GitInteractiveRebaseEditorHandler implements Closeable, GitRebaseEd } } else { - GitInteractiveRebaseFile rebaseFile = new GitInteractiveRebaseFile(myProject, myRoot, path); - List entries = rebaseFile.load(); setRebaseEditorShown(); - GitRebaseEditor editor = new GitRebaseEditor(myProject, myRoot, entries); - if (editor.showAndGet()) { - rebaseFile.save(editor.getEntries()); - 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(); + isSuccess.set(true); + } } - else { - rebaseFile.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 index e23920fc311f..6964fb73bb55 100644 --- a/plugins/git4idea/src/git4idea/rebase/GitInteractiveRebaseFile.java +++ b/plugins/git4idea/src/git4idea/rebase/GitInteractiveRebaseFile.java @@ -42,12 +42,18 @@ class GitInteractiveRebaseFile { } @NotNull - public List load() throws IOException { + 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.startsWith("noop")) { + if (s.isEol() || s.startsWith('#')) { + s.nextLine(); + continue; + } + if (s.startsWith("noop")) { + noop = true; s.nextLine(); continue; } @@ -57,6 +63,9 @@ class GitInteractiveRebaseFile { entries.add(new GitRebaseEntry(action, hash, comment)); } + if (noop && entries.isEmpty()) { + throw new NoopException(); + } return entries; } @@ -93,4 +102,7 @@ class GitInteractiveRebaseFile { } return file; } + + static class NoopException extends Exception { + } } From 3d79b7fea6dd78817790adc67974b18b43ec276f Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Thu, 16 Apr 2015 15:17:39 +0300 Subject: [PATCH 23/55] [git] interactive rebase: remove pointless statements without "return", `success` will be overwritten by false later, and it is correct when rebase is cancelled. --- .../git4idea/rebase/GitInteractiveRebaseEditorHandler.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/plugins/git4idea/src/git4idea/rebase/GitInteractiveRebaseEditorHandler.java b/plugins/git4idea/src/git4idea/rebase/GitInteractiveRebaseEditorHandler.java index c048c464ca48..0c1dba06ed74 100644 --- a/plugins/git4idea/src/git4idea/rebase/GitInteractiveRebaseEditorHandler.java +++ b/plugins/git4idea/src/git4idea/rebase/GitInteractiveRebaseEditorHandler.java @@ -113,9 +113,6 @@ public class GitInteractiveRebaseEditorHandler implements Closeable, GitRebaseEd isSuccess.set(true); return; } - else { - isSuccess.set(false); - } } else { setRebaseEditorShown(); @@ -130,7 +127,6 @@ public class GitInteractiveRebaseEditorHandler implements Closeable, GitRebaseEd } else { rebaseFile.cancel(); - isSuccess.set(true); } } catch (GitInteractiveRebaseFile.NoopException e) { From ecfe82c04a58d2a684fa892ba0f4276f5a2fac29 Mon Sep 17 00:00:00 2001 From: "Vladislav.Soroka" Date: Thu, 16 Apr 2015 15:19:12 +0300 Subject: [PATCH 24/55] maven: manifest serialization/deserialization to/from xml fix --- .../MavenCommonArtifactRootCopyingHandlerProvider.java | 4 ++-- .../project/MavenResourceCompilerConfigurationGenerator.java | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) 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 477e6cf6811d..65973a4856f4 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,8 +59,7 @@ public class MavenCommonArtifactRootCopyingHandlerProvider extends ArtifactRootC projectConfiguration.moduleConfigurations.get(getModuleName(artifact.getName())); if (moduleResourceConfiguration != null && moduleResourceConfiguration.manifest != null) { try { - final String manifest = - StringUtil.unescapeXml(moduleResourceConfiguration.manifest).replaceAll("\n\r|\n|\r", "\r\n") + "\r\n\r\n"; + final String manifest = new String(Base64.decode(moduleResourceConfiguration.manifest)); FileUtil.writeToFile(root, manifest); } catch (IOException 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 fb2855319089..85b1cb0aa4e7 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 @@ -17,6 +17,7 @@ import com.intellij.openapi.util.text.StringUtil; 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; @@ -227,7 +228,7 @@ public class MavenResourceCompilerConfigurationGenerator { manifest.write(outputStream); MavenDomProjectModel domModel = MavenDomUtil.getMavenDomProjectModel(module.getProject(), mavenProject.getFile()); final String resolvedText = MavenPropertyResolver.resolve(outputStream.toString("UTF8"), domModel); - resourceConfig.manifest = StringUtil.escapeXml(resolvedText); + resourceConfig.manifest = Base64.encode(resolvedText.getBytes("UTF8")); } finally { StreamUtil.closeStream(outputStream); From cf1e7bb5e93cd361d6521f110f4387ef41e99ccc Mon Sep 17 00:00:00 2001 From: Aleksey Pivovarov Date: Thu, 16 Apr 2015 15:23:34 +0300 Subject: [PATCH 25/55] diff: fix foldings at the start/end of file inner foldings shouldn't expand and show the unchanged beginning of the file --- .../diff/tools/util/FoldingModelSupport.java | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) 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; } From 160a8cb88143d2c189044ae0cdf1b3c8bbbad04e Mon Sep 17 00:00:00 2001 From: Eugene Zhuravlev Date: Thu, 16 Apr 2015 14:20:27 +0200 Subject: [PATCH 26/55] logging task class for better diagnostics --- .../src/com/intellij/openapi/project/DumbServiceImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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); } } } From 2a13e385f56f6189ff262c57ffc0de1da4b567ed Mon Sep 17 00:00:00 2001 From: nik Date: Thu, 16 Apr 2015 15:32:27 +0300 Subject: [PATCH 27/55] IDEA-139244: Build intellij-core.zip artifact with content of "core" artifacts folder --- build/scripts/dist.gant | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/build/scripts/dist.gant b/build/scripts/dist.gant index efc8f4fd3b65..8bc0432edbfb 100644 --- a/build/scripts/dist.gant +++ b/build/scripts/dist.gant @@ -84,7 +84,7 @@ target('default': 'The default target') { indexSearchableOptions() - layoutAll([buildNumber: "IC-$snapshot", + layoutAll([buildNumber: snapshot, system_selector: "IdeaIC${p("component.version.major")}", tools_jar: true], home, null, paths, true) @@ -100,7 +100,7 @@ target('build-dist-jars' : 'Target to build jars from locally compiled classes') indexSearchableOptions() - layoutAll([buildNumber: "IC-$snapshot", + layoutAll([buildNumber: snapshot, system_selector: "IdeaIC${p("component.version.major")}", tools_jar: true], home, null, paths) @@ -120,16 +120,20 @@ private void indexSearchableOptions() { def layoutAll(Map args, String home, String out, Paths _paths = null, buildJps = false) { Paths paths = _paths != null ? _paths : new Paths(out) - - wireBuildDate(args.buildNumber, appInfoFile()) - ant.echo(message: args.buildNumber, file: "$paths.distAll/build.txt") + def fullBuildNumber = "IC-${args.buildNumber}" + wireBuildDate(fullBuildNumber, appInfoFile()) + ant.echo(message: fullBuildNumber, file: "$paths.distAll/build.txt") 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-${args.buildNumber}.zip") { + fileset(dir: paths.artifacts_core) + } + notifyArtifactBuilt(paths.artifacts_core) if (buildJps) { - layouts.layoutJps(home, paths.artifacts_jps, args.buildNumber, {}) + layouts.layoutJps(home, paths.artifacts_jps, fullBuildNumber, {}) notifyArtifactBuilt(paths.artifacts_jps) } @@ -151,16 +155,16 @@ def layoutAll(Map args, String home, String out, Paths _paths = null, buildJps = layoutMac(args, home, paths) layoutUnix(args, home, paths) - buildWinZip("$paths.artifacts/idea${args.buildNumber}.win.zip", [paths.distAll, paths.distWin]) + buildWinZip("$paths.artifacts/idea${fullBuildNumber}.win.zip", [paths.distAll, paths.distWin]) def macAppRoot = isEap() ? "IntelliJ IDEA ${p("component.version.major")} CE EAP.app/Contents" : "IntelliJ IDEA ${p("component.version.major")} CE.app/Contents" - String macZip = "$paths.artifacts/idea${args.buildNumber}.mac.zip" + String macZip = "$paths.artifacts/idea${fullBuildNumber}.mac.zip" buildMacZip(macAppRoot, macZip, [paths.distAll], paths.distMac) - buildTarGz("idea-${args.buildNumber}", "$paths.artifacts/idea${args.buildNumber}.tar", [paths.distAll, paths.distUnix]) + buildTarGz("idea-${fullBuildNumber}", "$paths.artifacts/idea${fullBuildNumber}.tar", [paths.distAll, paths.distUnix]) return info } From c11c02f9267e1d43cf1affbfd18a33945a5798cc Mon Sep 17 00:00:00 2001 From: Mikhail Golubev Date: Wed, 15 Apr 2015 17:39:50 +0300 Subject: [PATCH 28/55] PY-15564 Cleanup in PyJoinLinesHandler and PyJoinLinesTest --- .../python/editor/PyJoinLinesHandler.java | 217 ++++++++++-------- .../joinLines/StatementColon-after.py | 3 +- .../StatementCommentStatement-after.py | 3 +- .../joinLines/StringOneQuotePlainRaw-after.py | 3 +- .../joinLines/StringOneQuotePlainU-after.py | 3 +- .../StringTripleQuotesDifferent-after.py | 3 +- .../com/jetbrains/python/PyJoinLinesTest.java | 171 +++++++++----- 7 files changed, 234 insertions(+), 169 deletions(-) diff --git a/python/src/com/jetbrains/python/editor/PyJoinLinesHandler.java b/python/src/com/jetbrains/python/editor/PyJoinLinesHandler.java index 8fca92e81d67..25ab2ef4f2a2 100644 --- a/python/src/com/jetbrains/python/editor/PyJoinLinesHandler.java +++ b/python/src/com/jetbrains/python/editor/PyJoinLinesHandler.java @@ -27,19 +27,23 @@ 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 { @@ -49,42 +53,42 @@ 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; + final 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 // 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(i); + final PsiElement rightElement = file.findElementAt(end); + if (leftElement != null && rightElement != null) { + PyExpression leftExpr = PsiTreeUtil.getParentOfType(leftElement, PyExpression.class); + if (leftExpr instanceof PsiFile) leftExpr = null; + PyExpression rightExpr = PsiTreeUtil.getParentOfType(rightElement, PyExpression.class); + if (rightExpr instanceof PsiFile) rightExpr = null; - Joiner[] joiners = { // these are featherweight, will create and gc instantly + final 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(), }; - Request request = new Request(document, left_element, left_expr, right_element, right_expr); + final Request request = new Request(document, leftElement, leftExpr, rightElement, rightExpr); for (Joiner joiner : joiners) { - Result res = joiner.join(request); + 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(); + final int cutStart = i + 1 - res.getCutFromLeft(); + document.deleteString(cutStart, end + res.getCutIntoRight()); + document.insertString(cutStart, res.getInsert()); + return cutStart + res.getCursorOffset(); } } @@ -92,7 +96,7 @@ public class PyJoinLinesHandler implements JoinRawLinesHandlerDelegate { final PyExpression leftExpression = request.leftExpr(); final PyExpression rightExpression = request.rightExpr(); if (request.leftElem() == request.rightElem()) { - IElementType type = request.leftElem().getNode().getElementType(); + final 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)) { @@ -102,10 +106,12 @@ public class PyJoinLinesHandler implements JoinRawLinesHandlerDelegate { } PsiElement expression = null; if (leftExpression != null && rightExpression != null) { - if (PsiTreeUtil.isAncestor(leftExpression, rightExpression, false)) + if (PsiTreeUtil.isAncestor(leftExpression, rightExpression, false)) { expression = leftExpression; - else if (PsiTreeUtil.isAncestor(rightExpression, leftExpression, false)) + } + else if (PsiTreeUtil.isAncestor(rightExpression, leftExpression, false)) { expression = rightExpression; + } if (expression != null && !(expression instanceof PyStringLiteralExpression)) { if (removeBackSlash(document, expression, true)) { return expression.getTextOffset(); @@ -116,10 +122,10 @@ public class PyJoinLinesHandler implements JoinRawLinesHandlerDelegate { return CANNOT_JOIN; } - private static boolean removeBackSlash(Document document, PsiElement element, boolean trim) { - String[] substrings = element.getText().split("\n"); + private static boolean removeBackSlash(@NotNull Document document, @NotNull PsiElement element, boolean trim) { + final String[] substrings = element.getText().split("\n"); if (substrings.length != 1) { - StringBuilder replacement = new StringBuilder(); + final StringBuilder replacement = new StringBuilder(); for (int i = 0; i < substrings.length; i++) { String string = substrings[i]; if (trim) { @@ -137,7 +143,7 @@ public class PyJoinLinesHandler implements JoinRawLinesHandlerDelegate { replacement.append(" "); } } - document.replaceString(element.getTextOffset(), element.getTextOffset()+element.getTextLength(), replacement); + document.replaceString(element.getTextOffset(), element.getTextOffset() + element.getTextLength(), replacement); return true; } return false; @@ -152,7 +158,8 @@ public class PyJoinLinesHandler implements JoinRawLinesHandlerDelegate { /** * Result of a join operation. - * @param insert: what string to insert at start position + * + * @param insert: 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) { @@ -164,9 +171,10 @@ public class PyJoinLinesHandler implements JoinRawLinesHandlerDelegate { /** * Result of a join operation. - * @param insert what to insert into the cut place + * + * @param insert 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) { @@ -228,7 +236,6 @@ public class PyJoinLinesHandler implements JoinRawLinesHandlerDelegate { public PyExpression rightExpr() { return myRightExpr; } - } private static abstract class Joiner { @@ -242,9 +249,10 @@ public class PyJoinLinesHandler implements JoinRawLinesHandlerDelegate { } private static class OpenBracketJoiner extends Joiner { - private static TokenSet OPENS = TokenSet.create(PyTokenTypes.LBRACKET, PyTokenTypes.LBRACE, PyTokenTypes.LPAR); + private static final TokenSet OPENS = TokenSet.create(PyTokenTypes.LBRACKET, PyTokenTypes.LBRACE, PyTokenTypes.LPAR); + @Override - public Result join(Request req) { + 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); @@ -254,9 +262,10 @@ 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 final TokenSet CLOSES = TokenSet.create(PyTokenTypes.RBRACKET, PyTokenTypes.RBRACE, PyTokenTypes.RPAR); + @Override - public Result join(Request req) { + 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); @@ -267,7 +276,7 @@ public class PyJoinLinesHandler implements JoinRawLinesHandlerDelegate { private static class BinaryExprJoiner extends Joiner { @Override - public Result join(Request req) { + 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); @@ -278,12 +287,12 @@ public class PyJoinLinesHandler implements JoinRawLinesHandlerDelegate { private static class ListLikeExprJoiner extends 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) - ) { + public Result join(@NotNull Request req) { + final boolean leftIsListLike = PyUtil.instanceOf(req.leftExpr(), PyListLiteralExpression.class, PyTupleExpression.class); + if (leftIsListLike || PyUtil.instanceOf(req.rightExpr(), PyListLiteralExpression.class, PyTupleExpression.class) + ) { String insert = ""; - if (left_is_list_like) { // we join "a, \n b", not "a \n ,b" + if (leftIsListLike) { // 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()); @@ -294,11 +303,11 @@ public class PyJoinLinesHandler implements JoinRawLinesHandlerDelegate { 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 } @@ -309,32 +318,32 @@ public class PyJoinLinesHandler implements JoinRawLinesHandlerDelegate { private static class StringLiteralJoiner extends Joiner { @Override - public Result join(Request req) { + 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,36 +352,40 @@ 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) { + protected static boolean containsChar(@NotNull CharSequence text, @NotNull TextRange range, char c) { + for (int i = range.getStartOffset(); i <= range.getEndOffset(); i += 1) { if (text.charAt(i) == c) return true; } return false; } private static class StrMod { - private final String myPrefix; // "u", "b", or "" + @NotNull 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. + @NotNull private final String myQuote; // single or double, one or triple. private final boolean myOk; // true if parsing went ok - private final TextRange myInnerRange; + @Nullable private final TextRange myInnerRange; - public StrMod(CharSequence text, TextRange range) { + 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 = ""; @@ -380,23 +393,23 @@ public class PyJoinLinesHandler implements JoinRawLinesHandlerDelegate { 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()); + myInnerRange = TextRange.from(range.getStartOffset() + getStartPadding(), range.getLength() - getStartPadding() - quote().length()); myOk = true; } @@ -404,10 +417,12 @@ public class PyJoinLinesHandler implements JoinRawLinesHandlerDelegate { return myOk; } + @NotNull public String prefix() { return myPrefix; } + @NotNull public String quote() { return myQuote; } @@ -434,44 +449,42 @@ 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) { + public boolean compatibleTo(@NotNull StrMod other) { return myOk && 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 { @Override - public Result join(Request req) { + public Result join(@NotNull 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(); + 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; } } - - } 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..cae4d6df54ed 100644 --- a/python/testSrc/com/jetbrains/python/PyJoinLinesTest.java +++ b/python/testSrc/com/jetbrains/python/PyJoinLinesTest.java @@ -15,70 +15,127 @@ */ 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(); + } } From e104a7c8fd86fe081d14971dba23779325507cef Mon Sep 17 00:00:00 2001 From: Mikhail Golubev Date: Wed, 15 Apr 2015 18:58:45 +0300 Subject: [PATCH 29/55] PY-15564 Remove text after the first backslash inside whitespace during line joining --- .../jetbrains/python/editor/PyJoinLinesHandler.java | 6 ++++++ .../BackslashBetweenTargetsInFromImport-after.py | 1 + .../joinLines/BackslashBetweenTargetsInFromImport.py | 2 ++ .../joinLines/BackslashBetweenTargetsInImport-after.py | 1 + .../joinLines/BackslashBetweenTargetsInImport.py | 2 ++ .../testSrc/com/jetbrains/python/PyJoinLinesTest.java | 10 ++++++++++ 6 files changed, 22 insertions(+) create mode 100644 python/testData/joinLines/BackslashBetweenTargetsInFromImport-after.py create mode 100644 python/testData/joinLines/BackslashBetweenTargetsInFromImport.py create mode 100644 python/testData/joinLines/BackslashBetweenTargetsInImport-after.py create mode 100644 python/testData/joinLines/BackslashBetweenTargetsInImport.py diff --git a/python/src/com/jetbrains/python/editor/PyJoinLinesHandler.java b/python/src/com/jetbrains/python/editor/PyJoinLinesHandler.java index 25ab2ef4f2a2..2631738165ac 100644 --- a/python/src/com/jetbrains/python/editor/PyJoinLinesHandler.java +++ b/python/src/com/jetbrains/python/editor/PyJoinLinesHandler.java @@ -119,6 +119,12 @@ public class PyJoinLinesHandler implements JoinRawLinesHandlerDelegate { } } } + final String sequence = text.subSequence(i + 1, end).toString(); + final int index = sequence.indexOf('\\'); + if (index >= 0) { + document.replaceString(i + 1, end, sequence.substring(0, index)); + return i + 1 + index; + } return CANNOT_JOIN; } 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/testSrc/com/jetbrains/python/PyJoinLinesTest.java b/python/testSrc/com/jetbrains/python/PyJoinLinesTest.java index cae4d6df54ed..994aa7880fa1 100644 --- a/python/testSrc/com/jetbrains/python/PyJoinLinesTest.java +++ b/python/testSrc/com/jetbrains/python/PyJoinLinesTest.java @@ -138,4 +138,14 @@ public class PyJoinLinesTest extends PyTestCase { public void testListComprehension() { doTest(); } + + // PY-15564 + public void testBackslashBetweenTargetsInImport() { + doTest(); + } + + // PY-15564 + public void testBackslashBetweenTargetsInFromImport() { + doTest(); + } } From 573cb99c22f82c1becdf36e4707613f5c8c4a22b Mon Sep 17 00:00:00 2001 From: Mikhail Golubev Date: Wed, 15 Apr 2015 21:50:41 +0300 Subject: [PATCH 30/55] PY-15564 Further simplify PyJoinLinesHandler * lightened inner helper classes * joiners are listed in static fields * added missing nullability annotations * Joiner is an inner interface now * removed ListLikeExprJoiner and other redundant code * extract logic related to removal of backslashes in separate joiner --- .../python/editor/PyJoinLinesHandler.java | 319 ++++++------------ 1 file changed, 111 insertions(+), 208 deletions(-) diff --git a/python/src/com/jetbrains/python/editor/PyJoinLinesHandler.java b/python/src/com/jetbrains/python/editor/PyJoinLinesHandler.java index 2631738165ac..dac60d10684d 100644 --- a/python/src/com/jetbrains/python/editor/PyJoinLinesHandler.java +++ b/python/src/com/jetbrains/python/editor/PyJoinLinesHandler.java @@ -22,7 +22,6 @@ 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; @@ -46,6 +45,16 @@ import java.util.Locale; * @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) { @@ -57,209 +66,113 @@ public class PyJoinLinesHandler implements JoinRawLinesHandlerDelegate { if (!(file instanceof PyFile)) return CANNOT_JOIN; // step back the probable "\" and space before it. - int i = start; final 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 + 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 - final PsiElement leftElement = file.findElementAt(i); + final PsiElement leftElement = file.findElementAt(start); final PsiElement rightElement = file.findElementAt(end); if (leftElement != null && rightElement != null) { - PyExpression leftExpr = PsiTreeUtil.getParentOfType(leftElement, PyExpression.class); - if (leftExpr instanceof PsiFile) leftExpr = null; - PyExpression rightExpr = PsiTreeUtil.getParentOfType(rightElement, PyExpression.class); - if (rightExpr instanceof PsiFile) rightExpr = null; + final PyExpression leftExpr = PsiTreeUtil.getParentOfType(leftElement, PyExpression.class); + final PyExpression rightExpr = PsiTreeUtil.getParentOfType(rightElement, PyExpression.class); - final 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); - final Request request = new Request(document, leftElement, leftExpr, rightElement, rightExpr); - - for (Joiner joiner : joiners) { + for (Joiner joiner : JOINERS) { final Result res = joiner.join(request); if (res != null) { - final int cutStart = i + 1 - res.getCutFromLeft(); - document.deleteString(cutStart, end + res.getCutIntoRight()); - document.insertString(cutStart, res.getInsert()); - return cutStart + res.getCursorOffset(); + final int cutStart = start + 1 - res.cutFromLeft; + document.replaceString(cutStart, end + res.cutIntoRight, res.replacement); + return cutStart + res.caretOffset; } } - - // single string case PY-4375 - final PyExpression leftExpression = request.leftExpr(); - final PyExpression rightExpression = request.rightExpr(); - if (request.leftElem() == request.rightElem()) { - final 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 String sequence = text.subSequence(i + 1, end).toString(); - final int index = sequence.indexOf('\\'); - if (index >= 0) { - document.replaceString(i + 1, end, sequence.substring(0, index)); - return i + 1 + index; } return CANNOT_JOIN; } - private static boolean removeBackSlash(@NotNull Document document, @NotNull PsiElement element, boolean trim) { - final String[] substrings = element.getText().split("\n"); - if (substrings.length != 1) { - final 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 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; - } - - 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 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; } } - 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 class OpenBracketJoiner implements Joiner { private static final TokenSet OPENS = TokenSet.create(PyTokenTypes.LBRACKET, PyTokenTypes.LBRACE, PyTokenTypes.LPAR); @Override public Result join(@NotNull Request req) { - if (OPENS.contains(req.leftElem().getNode().getElementType())) { + if (OPENS.contains(req.leftElem.getNode().getElementType())) { // TODO: look at settings for space after opening paren return new Result("", 0); } @@ -267,12 +180,12 @@ public class PyJoinLinesHandler implements JoinRawLinesHandlerDelegate { } } - private static class CloseBracketJoiner extends Joiner { + private static class CloseBracketJoiner implements Joiner { private static final TokenSet CLOSES = TokenSet.create(PyTokenTypes.RBRACKET, PyTokenTypes.RBRACE, PyTokenTypes.RPAR); @Override public Result join(@NotNull Request req) { - if (CLOSES.contains(req.rightElem().getNode().getElementType())) { + if (CLOSES.contains(req.rightElem.getNode().getElementType())) { // TODO: look at settings for space before closing paren return new Result("", 0); } @@ -280,10 +193,10 @@ public class PyJoinLinesHandler implements JoinRawLinesHandlerDelegate { } } - private static class BinaryExprJoiner extends Joiner { + private static class BinaryExprJoiner implements Joiner { @Override public Result join(@NotNull Request req) { - if (req.leftExpr() instanceof PyBinaryExpression || req.rightExpr() instanceof PyBinaryExpression) { + if (req.leftExpr instanceof PyBinaryExpression || req.rightExpr instanceof PyBinaryExpression) { // TODO: look at settings for space around binary exprs return new Result(" ", 1); } @@ -291,28 +204,12 @@ public class PyJoinLinesHandler implements JoinRawLinesHandlerDelegate { } } - private static class ListLikeExprJoiner extends Joiner { + private static class StmtJoiner implements Joiner { @Override public Result join(@NotNull Request req) { - final boolean leftIsListLike = PyUtil.instanceOf(req.leftExpr(), PyListLiteralExpression.class, PyTupleExpression.class); - if (leftIsListLike || PyUtil.instanceOf(req.rightExpr(), PyListLiteralExpression.class, PyTupleExpression.class) - ) { - String insert = ""; - if (leftIsListLike) { // 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(@NotNull Request req) { - final PyStatement leftStmt = PsiTreeUtil.getParentOfType(req.leftExpr(), PyStatement.class); + final PyStatement leftStmt = PsiTreeUtil.getParentOfType(req.leftExpr, PyStatement.class); if (leftStmt != null) { - final PyStatement rightStmt = PsiTreeUtil.getParentOfType(req.rightExpr(), PyStatement.class); + 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 @@ -322,18 +219,17 @@ public class PyJoinLinesHandler implements JoinRawLinesHandlerDelegate { } } - private static class StringLiteralJoiner extends Joiner { + private static class StringLiteralJoiner implements Joiner { @Override 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) - ) { + 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 - final CharSequence text = req.document().getCharsSequence(); - final StrMod leftMod = new StrMod(text, req.leftElem().getTextRange()); - final StrMod rightMod = new StrMod(text, req.rightElem().getTextRange()); + 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)) { @@ -343,12 +239,12 @@ public class PyJoinLinesHandler implements JoinRawLinesHandlerDelegate { // maybe fit one literal's quotes to match other's if (!containsChar(text, rightMod.getInnerRange(), leftMod.quote().charAt(0))) { final int quotePos = rightMod.getInnerRange().getEndOffset(); - req.document().replaceString(quotePos, quotePos + 1, leftMod.quote()); + req.document.replaceString(quotePos, quotePos + 1, leftMod.quote()); return new Result("", 0, leftMod.quote().length(), rightMod.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()); + req.document.replaceString(quotePos, quotePos + 1, rightMod.quote()); return new Result("", 0, leftMod.quote().length(), rightMod.getStartPadding()); } } @@ -359,17 +255,15 @@ public class PyJoinLinesHandler implements JoinRawLinesHandlerDelegate { } protected static boolean containsChar(@NotNull CharSequence text, @NotNull TextRange range, char c) { - for (int i = range.getStartOffset(); i <= range.getEndOffset(); i += 1) { - if (text.charAt(i) == c) return true; - } - return false; + return StringUtil.contains(text, range.getStartOffset(), range.getEndOffset(), c); } + 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. - private final boolean myOk; // true if parsing went ok + @Nullable private final TextRange myInnerRange; public StrMod(@NotNull CharSequence text, @NotNull TextRange range) { @@ -395,7 +289,6 @@ public class PyJoinLinesHandler implements JoinRawLinesHandlerDelegate { 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 @@ -403,7 +296,6 @@ public class PyJoinLinesHandler implements JoinRawLinesHandlerDelegate { myQuote = text.subSequence(pos, pos + 3).toString(); if (!myQuote.equals(text.subSequence(range.getEndOffset() - 3, range.getEndOffset()).toString())) { myInnerRange = null; - myOk = false; return; } } @@ -411,16 +303,14 @@ public class PyJoinLinesHandler implements JoinRawLinesHandlerDelegate { 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; } public boolean isOk() { - return myOk; + return myInnerRange != null; } @NotNull @@ -441,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; } @@ -463,9 +348,8 @@ public class PyJoinLinesHandler implements JoinRawLinesHandlerDelegate { * @return true iff this and other have the same byte/unicode and raw prefixes. */ public boolean compatibleTo(@NotNull StrMod other) { - return myOk && other.isOk() && myRaw == other.isRaw() && myPrefix.equals(other.prefix()); + return isOk() && other.isOk() && myRaw == other.isRaw() && myPrefix.equals(other.prefix()); } - /** * @return range of text part inside quotes */ @@ -476,13 +360,12 @@ public class PyJoinLinesHandler implements JoinRawLinesHandlerDelegate { } } - - private static class CommentJoiner extends Joiner { + private static class CommentJoiner implements Joiner { @Override 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(); + 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(); @@ -493,4 +376,24 @@ public class PyJoinLinesHandler implements JoinRawLinesHandlerDelegate { 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; + } + } } From 7e5720d4608e4f14721b77e27e5df7c03eb84e98 Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Thu, 16 Apr 2015 15:43:38 +0300 Subject: [PATCH 31/55] [git] throw ISE in impossible default case --- plugins/git4idea/src/git4idea/actions/GitRebaseActionBase.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/git4idea/src/git4idea/actions/GitRebaseActionBase.java b/plugins/git4idea/src/git4idea/actions/GitRebaseActionBase.java index 9652625f5c68..4cc13114fe17 100644 --- a/plugins/git4idea/src/git4idea/actions/GitRebaseActionBase.java +++ b/plugins/git4idea/src/git4idea/actions/GitRebaseActionBase.java @@ -112,7 +112,7 @@ public abstract class GitRebaseActionBase extends GitRepositoryAction { messageId = "rebase.result.success"; break; default: - messageId = null; + throw new IllegalStateException("Unsupported rebase result: " + result.status); } String title = GitBundle.message(messageId + ".title"); From 4438cf9bced78ee825920cce2c49e742d8382a76 Mon Sep 17 00:00:00 2001 From: "Vladislav.Soroka" Date: Thu, 16 Apr 2015 15:41:11 +0300 Subject: [PATCH 32/55] maven: manifest serialization/deserialization to/from xml fix --- .../MavenCommonArtifactRootCopyingHandlerProvider.java | 3 +-- .../project/MavenResourceCompilerConfigurationGenerator.java | 5 +++-- 2 files changed, 4 insertions(+), 4 deletions(-) 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 65973a4856f4..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 @@ -59,8 +59,7 @@ public class MavenCommonArtifactRootCopyingHandlerProvider extends ArtifactRootC projectConfiguration.moduleConfigurations.get(getModuleName(artifact.getName())); if (moduleResourceConfiguration != null && moduleResourceConfiguration.manifest != null) { try { - final String manifest = new String(Base64.decode(moduleResourceConfiguration.manifest)); - FileUtil.writeToFile(root, 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 85b1cb0aa4e7..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,6 +14,7 @@ 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; @@ -227,8 +228,8 @@ public class MavenResourceCompilerConfigurationGenerator { try { manifest.write(outputStream); MavenDomProjectModel domModel = MavenDomUtil.getMavenDomProjectModel(module.getProject(), mavenProject.getFile()); - final String resolvedText = MavenPropertyResolver.resolve(outputStream.toString("UTF8"), domModel); - resourceConfig.manifest = Base64.encode(resolvedText.getBytes("UTF8")); + final String resolvedText = MavenPropertyResolver.resolve(outputStream.toString(CharsetToolkit.UTF8), domModel); + resourceConfig.manifest = Base64.encode(resolvedText.getBytes(CharsetToolkit.UTF8)); } finally { StreamUtil.closeStream(outputStream); From f0e69b638ef8a79224a5135684eecd06bd56facd Mon Sep 17 00:00:00 2001 From: Dennis Ushakov Date: Thu, 16 Apr 2015 16:09:33 +0300 Subject: [PATCH 33/55] IDEA-CR-2330: test --- .../intellij/util/indexing/FileBasedIndexTest.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) 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)); + } } From 11785d095f52f0aaba4bde6091baaa56e0e4de33 Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Thu, 16 Apr 2015 13:01:31 +0200 Subject: [PATCH 34/55] sm junit: eliminate junit handler for sm tests as extractors won't be used that way --- .../intellij/execution/junit/TestObject.java | 33 ++++++++++++++----- .../intellij/execution/junit/TestPackage.java | 4 +-- 2 files changed, 27 insertions(+), 10 deletions(-) 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; } From c9915252d82e6bcc777cf3b018f3a1020e8e4911 Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Thu, 16 Apr 2015 13:08:39 +0200 Subject: [PATCH 35/55] testng: use killable handler --- .../testng/configuration/TestNGRunnableState.java | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) 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 Date: Thu, 16 Apr 2015 13:09:45 +0200 Subject: [PATCH 36/55] testng: simplification --- .../configuration/SearchingForTestsTask.java | 25 ++++++------------- 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/plugins/testng/src/com/theoryinpractice/testng/configuration/SearchingForTestsTask.java b/plugins/testng/src/com/theoryinpractice/testng/configuration/SearchingForTestsTask.java index 5dade70f8fce..b1040cba79d1 100644 --- a/plugins/testng/src/com/theoryinpractice/testng/configuration/SearchingForTestsTask.java +++ b/plugins/testng/src/com/theoryinpractice/testng/configuration/SearchingForTestsTask.java @@ -224,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 { @@ -281,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()); } } @@ -307,7 +296,7 @@ public class SearchingForTestsTask extends SearchForTestsTask { } } } - return params; + testParams.putAll(myData.TEST_PROPERTIES); + return testParams; } - } From 278978f1f624a69580302fff288e0134e873f363 Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Thu, 16 Apr 2015 15:07:35 +0200 Subject: [PATCH 37/55] testng: tests on collecting classes/methods to run --- .../testng/util/TestNGUtil.java | 4 +- .../configuration/DetectClassesToRunTest.java | 135 ++++++++++++++++++ 2 files changed, 138 insertions(+), 1 deletion(-) create mode 100644 plugins/testng/testSources/com/theoryinpractice/testng/configuration/DetectClassesToRunTest.java diff --git a/plugins/testng/src/com/theoryinpractice/testng/util/TestNGUtil.java b/plugins/testng/src/com/theoryinpractice/testng/util/TestNGUtil.java index 0375b4a1b260..94969e358c85 100644 --- a/plugins/testng/src/com/theoryinpractice/testng/util/TestNGUtil.java +++ b/plugins/testng/src/com/theoryinpractice/testng/util/TestNGUtil.java @@ -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()); + } + } +} From 9316df3e53e844906c656c0da3462e1b8297fac5 Mon Sep 17 00:00:00 2001 From: Konstantin Bulenkov Date: Thu, 16 Apr 2015 16:14:26 +0300 Subject: [PATCH 38/55] open all project in group at once --- .../OpenSelectedProjectsAction.java | 47 ++++++++++++++++--- 1 file changed, 41 insertions(+), 6 deletions(-) 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); } } From b94b1cdf08791380f3f281da6fb47e8122773cba Mon Sep 17 00:00:00 2001 From: nik Date: Thu, 16 Apr 2015 16:31:32 +0300 Subject: [PATCH 39/55] build scripts: restored product code in build numbers for Mac --- build/scripts/dist.gant | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/build/scripts/dist.gant b/build/scripts/dist.gant index 8bc0432edbfb..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 @@ -84,7 +85,7 @@ target('default': 'The default target') { indexSearchableOptions() - layoutAll([buildNumber: snapshot, + layoutAll([buildNumber: "IC-$snapshot", system_selector: "IdeaIC${p("component.version.major")}", tools_jar: true], home, null, paths, true) @@ -100,7 +101,7 @@ target('build-dist-jars' : 'Target to build jars from locally compiled classes') indexSearchableOptions() - layoutAll([buildNumber: snapshot, + layoutAll([buildNumber: "IC-$snapshot", system_selector: "IdeaIC${p("component.version.major")}", tools_jar: true], home, null, paths) @@ -120,20 +121,20 @@ private void indexSearchableOptions() { def layoutAll(Map args, String home, String out, Paths _paths = null, buildJps = false) { Paths paths = _paths != null ? _paths : new Paths(out) - def fullBuildNumber = "IC-${args.buildNumber}" - wireBuildDate(fullBuildNumber, appInfoFile()) - ant.echo(message: fullBuildNumber, file: "$paths.distAll/build.txt") + + wireBuildDate(args.buildNumber, appInfoFile()) + ant.echo(message: args.buildNumber, file: "$paths.distAll/build.txt") 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-${args.buildNumber}.zip") { + 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, fullBuildNumber, {}) + layouts.layoutJps(home, paths.artifacts_jps, args.buildNumber, {}) notifyArtifactBuilt(paths.artifacts_jps) } @@ -155,16 +156,16 @@ def layoutAll(Map args, String home, String out, Paths _paths = null, buildJps = layoutMac(args, home, paths) layoutUnix(args, home, paths) - buildWinZip("$paths.artifacts/idea${fullBuildNumber}.win.zip", [paths.distAll, paths.distWin]) + buildWinZip("$paths.artifacts/idea${args.buildNumber}.win.zip", [paths.distAll, paths.distWin]) def macAppRoot = isEap() ? "IntelliJ IDEA ${p("component.version.major")} CE EAP.app/Contents" : "IntelliJ IDEA ${p("component.version.major")} CE.app/Contents" - String macZip = "$paths.artifacts/idea${fullBuildNumber}.mac.zip" + String macZip = "$paths.artifacts/idea${args.buildNumber}.mac.zip" buildMacZip(macAppRoot, macZip, [paths.distAll], paths.distMac) - buildTarGz("idea-${fullBuildNumber}", "$paths.artifacts/idea${fullBuildNumber}.tar", [paths.distAll, paths.distUnix]) + buildTarGz("idea-${args.buildNumber}", "$paths.artifacts/idea${args.buildNumber}.tar", [paths.distAll, paths.distUnix]) return info } From 2c0720b2c8a43dddb1040391c82d0de45affefc8 Mon Sep 17 00:00:00 2001 From: Aleksey Pivovarov Date: Thu, 16 Apr 2015 16:26:18 +0300 Subject: [PATCH 40/55] IDEA-139223 IDEA-138359 diff: better detection of modality state IdeFocusManager.isFocusBeingTransferred() could be true at this moment --- platform/diff-impl/src/com/intellij/diff/util/DiffUtil.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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; From 8d231f82ef73bc2efbb504c70d9bbef682f7c341 Mon Sep 17 00:00:00 2001 From: nik Date: Thu, 16 Apr 2015 18:01:37 +0300 Subject: [PATCH 41/55] modified ideaCE*-launcher.properties files in sources instead of changing them during build, unnecessary duplicated files removed --- build/conf/ideaCE-launcher.properties | 1 + build/conf/ideaCE64-launcher.properties | 1 + 2 files changed, 2 insertions(+) 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__ From 9f389a1f72111c19bee39ea596a0565bd4fb011f Mon Sep 17 00:00:00 2001 From: Aleksey Pivovarov Date: Thu, 16 Apr 2015 18:16:33 +0300 Subject: [PATCH 42/55] EA-67323 vcs: fix AIOOBE - common shortcuts can be empty --- .../openapi/vcs/history/FileHistoryPanelImpl.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) 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); From f33140daa50479621f92dba3578505f72f0f8111 Mon Sep 17 00:00:00 2001 From: Dmitry Avdeev Date: Thu, 16 Apr 2015 15:22:54 +0300 Subject: [PATCH 43/55] cleanup --- .../tasks-api/src/com/intellij/tasks/TaskRepositoryType.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 From 555ff3f0982c33f50ca64cece62c6886428aab71 Mon Sep 17 00:00:00 2001 From: Dmitry Avdeev Date: Thu, 16 Apr 2015 17:56:37 +0300 Subject: [PATCH 44/55] cleanup --- .../plugins/xpathView/search/FindByXPathAction.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) 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"); From f2d5c81134b3da331bfe340c853cbb599119c2c9 Mon Sep 17 00:00:00 2001 From: Dmitry Avdeev Date: Thu, 16 Apr 2015 18:32:24 +0300 Subject: [PATCH 45/55] IDEA-138470 IntelliJ IDEA : find by xpath expression : input field too small : where are my under... --- .../src/com/intellij/ui/EditorComboBox.java | 2 +- .../plugins/xpathView/HistoryElement.java | 5 ++ .../xpathView/ui/InputExpressionDialog.java | 56 ++++--------------- 3 files changed, 16 insertions(+), 47 deletions(-) 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/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/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 From 8d049b8d7c30ed250e174159c3b817b94649c31d Mon Sep 17 00:00:00 2001 From: Bas Leijdekkers Date: Thu, 16 Apr 2015 16:36:31 +0200 Subject: [PATCH 46/55] SSR: improve java array initializer matching and add more tests --- .../impl/matcher/JavaMatchingVisitor.java | 18 +++++----- .../StructuralSearchTest.java | 35 +++++++++++++++---- 2 files changed, 38 insertions(+), 15 deletions(-) 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/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() { From 51e23e60633aa74078e2d5a5a219f042887bd195 Mon Sep 17 00:00:00 2001 From: peter Date: Thu, 16 Apr 2015 16:58:39 +0300 Subject: [PATCH 47/55] IDEA-139301 "Constant conditions & exceptions" flaggs nullable expression when implementing a method returning void --- .../dataFlow/StandardDataFlowRunner.java | 2 +- .../dataFlow/fixture/NullableVoidLambda.java | 12 ++++++++++++ .../codeInspection/DataFlowInspection8Test.java | 1 + 3 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 java/java-tests/testData/inspection/dataFlow/fixture/NullableVoidLambda.java 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-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/codeInspection/DataFlowInspection8Test.java b/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspection8Test.java index 34b1c015678f..caca819a9245 100644 --- a/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspection8Test.java +++ b/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspection8Test.java @@ -77,6 +77,7 @@ public class DataFlowInspection8Test extends LightCodeInsightFixtureTestCase { } public void testMethodReferenceOnNullable() { doTest(); } + public void testNullableVoidLambda() { doTest(); } public void testNullableForeachVariable() { setupCustomAnnotations(); From 10560f057f08b10d510fb1b48726a54c943d969f Mon Sep 17 00:00:00 2001 From: peter Date: Thu, 16 Apr 2015 17:59:24 +0300 Subject: [PATCH 48/55] PsiTypeMapper: preserve array type annotations --- java/java-psi-impl/src/com/intellij/psi/PsiTypeMapper.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 From ce9914e69781674927e398e9bca8b46e7b7628df Mon Sep 17 00:00:00 2001 From: peter Date: Thu, 16 Apr 2015 17:59:54 +0300 Subject: [PATCH 49/55] IDEA-139253 TYPE_USE @Nullable-Annotations don't work correctly with arrays --- .../dataFlow/ControlFlowAnalyzer.java | 2 +- .../codeInspection/dataFlow/DfaPsiUtil.java | 14 +++++----- .../dataFlow/value/DfaExpressionFactory.java | 6 +++-- .../fixture/NullableArrayComponent.java | 27 +++++++++++++++++++ .../DataFlowInspection8Test.java | 8 ++++++ 5 files changed, 47 insertions(+), 10 deletions(-) create mode 100644 java/java-tests/testData/inspection/dataFlow/fixture/NullableArrayComponent.java 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/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-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/testSrc/com/intellij/codeInspection/DataFlowInspection8Test.java b/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspection8Test.java index caca819a9245..dcffac411bfc 100644 --- a/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspection8Test.java +++ b/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspection8Test.java @@ -83,4 +83,12 @@ public class DataFlowInspection8Test extends LightCodeInsightFixtureTestCase { 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"); + } } From 563643999bdaa773af3cc3a3d6d975c2fd212e4f Mon Sep 17 00:00:00 2001 From: peter Date: Thu, 16 Apr 2015 18:06:18 +0300 Subject: [PATCH 50/55] IDEA-138667 Save Groovy-Eclipse jar path setting as a system-independent name using path variables --- .../groovy/compiler/GreclipseConfigurable.java | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) 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); } From a577297f813a29f80d8a0a3b6627d6869d72b4d6 Mon Sep 17 00:00:00 2001 From: peter Date: Thu, 16 Apr 2015 19:04:56 +0300 Subject: [PATCH 51/55] a bombed test for IDEA-126629 java.io.Closeable is not AutoCloseable in some cases --- .../java3/p/CloseableAutoCloseable.java | 5 +++ .../java7/p/CloseableAutoCloseable.java | 11 +++++++ .../MultipleJdksHighlightingTest.java | 33 +++++++++++++++++-- 3 files changed, 46 insertions(+), 3 deletions(-) create mode 100644 java/java-tests/testData/codeInsight/multipleJdks/java3/p/CloseableAutoCloseable.java create mode 100644 java/java-tests/testData/codeInsight/multipleJdks/java7/p/CloseableAutoCloseable.java 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/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(); From 87a6e619b48c2471341aa5164948f76f352c5a7c Mon Sep 17 00:00:00 2001 From: Elizaveta Shashkova Date: Thu, 16 Apr 2015 19:45:43 +0300 Subject: [PATCH 52/55] Handle -m option in debugger (PY-15230) Do not leave it as interpreter option, but pass it to debugger as additional parameter --- python/helpers/pydev/pydevd.py | 35 +++++++++++++++++-- .../python/debugger/PyDebugRunner.java | 30 +++++++++++++++- 2 files changed, 61 insertions(+), 4 deletions(-) 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"); } From a97b76faea09de56bb177b37bd8bf889514d6ea8 Mon Sep 17 00:00:00 2001 From: "Egor.Ushakov" Date: Thu, 16 Apr 2015 20:03:52 +0300 Subject: [PATCH 53/55] use all available space for stepping filters --- .../settings/DebuggerSteppingConfigurable.java | 4 ++-- .../settings/MergedCompositeConfigurable.java | 17 ++++++++++++++++- 2 files changed, 18 insertions(+), 3 deletions(-) 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/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)); From 35639e517a0068260390a3221c533e9d6a4c396e Mon Sep 17 00:00:00 2001 From: Yaroslav Lepenkin Date: Thu, 16 Apr 2015 19:09:08 +0300 Subject: [PATCH 54/55] Preserving leading and trailing asterisks in javadoc while formatting (IDEA-138799) --- .../codeStyle/javadoc/CommentFormatter.java | 108 +++++------------ .../source/codeStyle/javadoc/JDComment.java | 14 ++- .../source/codeStyle/javadoc/JDParser.java | 110 ++++++++++++++++-- .../formatter/java/JavadocFormatterTest.java | 55 +++++++++ 4 files changed, 197 insertions(+), 90 deletions(-) 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-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; From ac42323ceb8d1793c467e1ee786dcc18bf3817c2 Mon Sep 17 00:00:00 2001 From: Yaroslav Lepenkin Date: Thu, 16 Apr 2015 19:17:57 +0300 Subject: [PATCH 55/55] Do not log CancellationException --- .../codeInsight/actions/AbstractLayoutCodeProcessor.java | 3 +++ 1 file changed, 3 insertions(+) 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); }