From 0fbaee3fad39cae9de07a4f592ed679dd040444e Mon Sep 17 00:00:00 2001 From: Liana Bakradze Date: Fri, 8 May 2015 15:43:21 +0300 Subject: [PATCH 01/28] fixed resource roots deletion from project structure in plugin --- .../src/com/jetbrains/python/module/PyContentEntriesEditor.java | 2 +- python/src/com/jetbrains/python/module/PyRootTypeProvider.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/python/src/com/jetbrains/python/module/PyContentEntriesEditor.java b/python/src/com/jetbrains/python/module/PyContentEntriesEditor.java index ae7229d56b8f..e00010e08903 100644 --- a/python/src/com/jetbrains/python/module/PyContentEntriesEditor.java +++ b/python/src/com/jetbrains/python/module/PyContentEntriesEditor.java @@ -171,7 +171,7 @@ public class PyContentEntriesEditor extends CommonContentEntriesEditor { } VirtualFilePointer root = getRoot(provider, folder); if (root != null) { - provider.removeRoot(contentEntry, root); + provider.removeRoot(contentEntry, root, getModel()); fireUpdate(); } } diff --git a/python/src/com/jetbrains/python/module/PyRootTypeProvider.java b/python/src/com/jetbrains/python/module/PyRootTypeProvider.java index 56610ea41ae3..dd5a0631876e 100644 --- a/python/src/com/jetbrains/python/module/PyRootTypeProvider.java +++ b/python/src/com/jetbrains/python/module/PyRootTypeProvider.java @@ -56,7 +56,7 @@ public abstract class PyRootTypeProvider { public abstract boolean isMine(ContentFolder folder); - public void removeRoot(ContentEntry contentEntry, @NotNull final VirtualFilePointer root) { + public void removeRoot(ContentEntry contentEntry, @NotNull final VirtualFilePointer root, ModifiableRootModel model) { getRoots().remove(contentEntry, root); } public abstract MultiMap getRoots(); From 07feeea9d131db53b6c23b62d94bf6374212a8a6 Mon Sep 17 00:00:00 2001 From: Nadya Zabrodina Date: Wed, 6 May 2015 18:38:31 +0400 Subject: [PATCH 02/28] Provide default focused component in createPatchDialog in a more convenient way: using IdeFocusTraversalPolicy.getPreferredFocusedComponent --- .../integration/ui/views/HistoryDialog.java | 6 ++-- .../patch/CreatePatchConfigurationPanel.java | 15 +------- .../ui/ComplexFocusedComponentWrapper.java | 34 ------------------- .../openapi/vcs/changes/ui/SessionDialog.java | 4 +-- 4 files changed, 7 insertions(+), 52 deletions(-) delete mode 100644 platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ComplexFocusedComponentWrapper.java diff --git a/platform/lvcs-impl/src/com/intellij/history/integration/ui/views/HistoryDialog.java b/platform/lvcs-impl/src/com/intellij/history/integration/ui/views/HistoryDialog.java index b4d9521b00b0..4c909aeeb01b 100644 --- a/platform/lvcs-impl/src/com/intellij/history/integration/ui/views/HistoryDialog.java +++ b/platform/lvcs-impl/src/com/intellij/history/integration/ui/views/HistoryDialog.java @@ -50,6 +50,7 @@ import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vcs.changes.patch.CreatePatchConfigurationPanel; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.wm.ex.IdeFocusTraversalPolicy; import com.intellij.ui.*; import com.intellij.ui.awt.RelativePoint; import com.intellij.ui.components.JBLayeredPane; @@ -494,9 +495,10 @@ public abstract class HistoryDialog extends FrameW private boolean showAsDialog(CreatePatchConfigurationPanel p) { final DialogBuilder b = new DialogBuilder(myProject); - b.setPreferredFocusComponent(p.getPreferredFocusComponent()); + JComponent createPatchPanel = p.getPanel(); + b.setPreferredFocusComponent(IdeFocusTraversalPolicy.getPreferredFocusedComponent(createPatchPanel)); b.setTitle(message("create.patch.dialog.title")); - b.setCenterPanel(p.getPanel()); + b.setCenterPanel(createPatchPanel); p.installOkEnabledListener(new Consumer() { public void consume(final Boolean aBoolean) { b.setOkActionEnabled(aBoolean); diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/CreatePatchConfigurationPanel.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/CreatePatchConfigurationPanel.java index 2a98132bbbdb..c398b5fbd62c 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/CreatePatchConfigurationPanel.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/CreatePatchConfigurationPanel.java @@ -34,7 +34,6 @@ import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vcs.VcsBundle; import com.intellij.openapi.vcs.VcsConfiguration; import com.intellij.openapi.vcs.changes.Change; -import com.intellij.openapi.vcs.changes.ui.ComplexFocusedComponentWrapper; import com.intellij.openapi.vfs.CharsetToolkit; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; @@ -235,20 +234,8 @@ public class CreatePatchConfigurationPanel { return myIncludedChanges; } - @NotNull public JComponent getPanel() { - return new ComplexFocusedComponentWrapper( - !myIncludeBaseRevisionTextCheckBox.isVisible() || myChanges.isEmpty() ? myMainPanel : myPanelWithSelectedFiles) { - @Override - public JComponent getPreferredFocusedSimpleComponent() { - return getPreferredFocusComponent(); - } - }; - } - - @NotNull - public JComponent getPreferredFocusComponent() { - return myFileNameField; + return !myIncludeBaseRevisionTextCheckBox.isVisible() || myChanges.isEmpty() ? myMainPanel : myPanelWithSelectedFiles; } public void installOkEnabledListener(final Consumer runnable) { diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ComplexFocusedComponentWrapper.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ComplexFocusedComponentWrapper.java deleted file mode 100644 index dc287817654b..000000000000 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ComplexFocusedComponentWrapper.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * 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.openapi.vcs.changes.ui; - -import org.jetbrains.annotations.NotNull; - -import javax.swing.*; -import java.awt.*; - -public class ComplexFocusedComponentWrapper extends JPanel { - - public ComplexFocusedComponentWrapper(@NotNull JComponent component) { - super(new BorderLayout()); - add(component); - } - - public JComponent getPreferredFocusedSimpleComponent() { - //return all panel by default - return this; - } -} diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/SessionDialog.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/SessionDialog.java index 5980ea0635f2..fccbcf69af90 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/SessionDialog.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/SessionDialog.java @@ -21,6 +21,7 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.vcs.changes.Change; import com.intellij.openapi.vcs.changes.CommitSession; +import com.intellij.openapi.wm.ex.IdeFocusTraversalPolicy; import com.intellij.util.Alarm; import org.jetbrains.annotations.Nullable; @@ -68,8 +69,7 @@ public class SessionDialog extends DialogWrapper { @Override public JComponent getPreferredFocusedComponent() { - return myConfigurationComponent instanceof ComplexFocusedComponentWrapper ? - ((ComplexFocusedComponentWrapper)myConfigurationComponent).getPreferredFocusedSimpleComponent() : myConfigurationComponent; + return IdeFocusTraversalPolicy.getPreferredFocusedComponent(myConfigurationComponent); } private void updateButtons() { From 1271bb77f3cecde84d4e7fd21dd4f27ddd374c4e Mon Sep 17 00:00:00 2001 From: Nadya Zabrodina Date: Thu, 7 May 2015 14:56:27 +0400 Subject: [PATCH 03/28] Vcs Repository Manager refactoring: recently added ugly api method removed; validation method optimized and reused; getRepositoryForRoot method without refresh provided only in impl module --- .../intellij/dvcs/repo/RepositoryManager.java | 3 --- .../dvcs/repo/AbstractRepositoryManager.java | 27 +++++++++---------- .../dvcs/repo/VcsRepositoryManager.java | 19 ++++++++----- .../VcsRepositoryManagerTest.java | 4 +-- .../repo/GitUntrackedFilesHolder.java | 3 ++- 5 files changed, 28 insertions(+), 28 deletions(-) diff --git a/platform/dvcs-api/src/com/intellij/dvcs/repo/RepositoryManager.java b/platform/dvcs-api/src/com/intellij/dvcs/repo/RepositoryManager.java index 2292220e6672..7c91d458f622 100644 --- a/platform/dvcs-api/src/com/intellij/dvcs/repo/RepositoryManager.java +++ b/platform/dvcs-api/src/com/intellij/dvcs/repo/RepositoryManager.java @@ -47,9 +47,6 @@ public interface RepositoryManager { @Nullable T getRepositoryForFile(@NotNull VirtualFile file); - @Nullable - T getRepositoryForFile(@NotNull VirtualFile file, boolean updateIfNeeded); - /** * Returns the {@link Repository} which the given file belongs to, or {@code null} if the file is not under any Git ot Hg repository. */ diff --git a/platform/dvcs-impl/src/com/intellij/dvcs/repo/AbstractRepositoryManager.java b/platform/dvcs-impl/src/com/intellij/dvcs/repo/AbstractRepositoryManager.java index 6a0d2fce2fe8..69a6e9afc41d 100644 --- a/platform/dvcs-impl/src/com/intellij/dvcs/repo/AbstractRepositoryManager.java +++ b/platform/dvcs-impl/src/com/intellij/dvcs/repo/AbstractRepositoryManager.java @@ -29,9 +29,12 @@ public abstract class AbstractRepositoryManager @Override @Nullable public T getRepositoryForRoot(@Nullable VirtualFile root) { - Repository repository = myGlobalRepositoryManager.getRepositoryForRoot(root, true); - //noinspection unchecked - return isRootValid(repository) ? (T)repository : null; + return validateAndGetRepository(myGlobalRepositoryManager.getRepositoryForRoot(root)); + } + + @Nullable + public T getRepositoryForRootQuick(@Nullable VirtualFile root) { + return validateAndGetRepository(myGlobalRepositoryManager.getRepositoryForRootQuick(root)); } @Override @@ -52,15 +55,7 @@ public abstract class AbstractRepositoryManager @Override @Nullable public T getRepositoryForFile(@NotNull VirtualFile file) { - return getRepositoryForFile(file, true); - } - - @Override - @Nullable - public T getRepositoryForFile(@NotNull VirtualFile file, boolean updateIfNeeded) { - Repository repository = myGlobalRepositoryManager.getRepositoryForFile(file, updateIfNeeded); - //noinspection unchecked - return repository != null && myVcs.equals(repository.getVcs()) ? (T)repository : null; + return validateAndGetRepository(myGlobalRepositoryManager.getRepositoryForFile(file)); } @Override @@ -103,9 +98,11 @@ public abstract class AbstractRepositoryManager }); } - private boolean isRootValid(@Nullable Repository repository) { - if (repository == null || !myVcs.equals(repository.getVcs())) return false; + @Nullable + private T validateAndGetRepository(@Nullable Repository repository) { + if (repository == null || !myVcs.equals(repository.getVcs())) return null; VirtualFile vcsDir = repository.getRoot().findChild(myRepoDirName); - return vcsDir != null && vcsDir.exists(); + //noinspection unchecked + return vcsDir != null && vcsDir.exists() ? (T)repository : null; } } diff --git a/platform/dvcs-impl/src/com/intellij/dvcs/repo/VcsRepositoryManager.java b/platform/dvcs-impl/src/com/intellij/dvcs/repo/VcsRepositoryManager.java index c55cac8d7357..ba27ce4d2b19 100644 --- a/platform/dvcs-impl/src/com/intellij/dvcs/repo/VcsRepositoryManager.java +++ b/platform/dvcs-impl/src/com/intellij/dvcs/repo/VcsRepositoryManager.java @@ -80,17 +80,22 @@ public class VcsRepositoryManager extends AbstractProjectComponent implements Di @Nullable public Repository getRepositoryForFile(@NotNull VirtualFile file) { - return getRepositoryForFile(file, true); - } - - @Nullable - public Repository getRepositoryForFile(@NotNull VirtualFile file, boolean updateIfNeeded) { final VcsRoot vcsRoot = myVcsManager.getVcsRootObjectFor(file); - return vcsRoot != null ? getRepositoryForRoot(vcsRoot.getPath(), updateIfNeeded) : null; + return vcsRoot != null ? getRepositoryForRoot(vcsRoot.getPath()) : null; } @Nullable - public Repository getRepositoryForRoot(@Nullable VirtualFile root, boolean updateIfNeeded) { + public Repository getRepositoryForRootQuick(@Nullable VirtualFile root) { + return getRepositoryForRoot(root, false); + } + + @Nullable + public Repository getRepositoryForRoot(@Nullable VirtualFile root) { + return getRepositoryForRoot(root, true); + } + + @Nullable + private Repository getRepositoryForRoot(@Nullable VirtualFile root, boolean updateIfNeeded) { if (root == null) return null; Repository result; try { diff --git a/platform/dvcs-impl/testSrc/com.intellij.dvcs/VcsRepositoryManagerTest.java b/platform/dvcs-impl/testSrc/com.intellij.dvcs/VcsRepositoryManagerTest.java index f2519070ae5d..022a644570ce 100644 --- a/platform/dvcs-impl/testSrc/com.intellij.dvcs/VcsRepositoryManagerTest.java +++ b/platform/dvcs-impl/testSrc/com.intellij.dvcs/VcsRepositoryManagerTest.java @@ -120,12 +120,12 @@ public class VcsRepositoryManagerTest extends UsefulTestCase { public void testRepositoryInfoReadingWhileModifying() throws Exception { final VirtualFile repositoryFile = createExternalRepository(); - assertNotNull(myGlobalRepositoryManager.getRepositoryForRoot(repositoryFile, true)); + assertNotNull(myGlobalRepositoryManager.getRepositoryForRoot(repositoryFile)); FutureTask readExistingRepo = new FutureTask(new Callable() { @Override public Repository call() throws Exception { - return myGlobalRepositoryManager.getRepositoryForRoot(repositoryFile, true); + return myGlobalRepositoryManager.getRepositoryForRoot(repositoryFile); } }); diff --git a/plugins/git4idea/src/git4idea/repo/GitUntrackedFilesHolder.java b/plugins/git4idea/src/git4idea/repo/GitUntrackedFilesHolder.java index cc2b8c0245a5..0091fdd085d6 100644 --- a/plugins/git4idea/src/git4idea/repo/GitUntrackedFilesHolder.java +++ b/plugins/git4idea/src/git4idea/repo/GitUntrackedFilesHolder.java @@ -15,6 +15,7 @@ */ package git4idea.repo; +import com.intellij.dvcs.DvcsUtil; import com.intellij.openapi.Disposable; import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.diagnostic.Logger; @@ -298,7 +299,7 @@ public class GitUntrackedFilesHolder implements Disposable, BulkFileListener { private boolean belongsToThisRepository(VirtualFile file) { // this check should be quick // we shouldn't create a full instance repository here because it may lead to SOE while many unversioned files will be processed - final GitRepository repository = myRepositoryManager.getRepositoryForFile(file, false); + final GitRepository repository = myRepositoryManager.getRepositoryForRootQuick(DvcsUtil.getVcsRoot(myProject, file)); return repository != null && repository.getRoot().equals(myRoot); } } From 58a549f5aae08167d39c7e97c87488e223371375 Mon Sep 17 00:00:00 2001 From: irengrig Date: Fri, 8 May 2015 14:05:51 +0200 Subject: [PATCH 04/28] tree table: correctly define if line is focused; do not always (have setting) paint selection background for darcula/aqua for tree table as if it has focus --- .../ui/treeStructure/treetable/TreeTableCellRenderer.java | 7 ++++--- .../src/com/intellij/util/ui/tree/WideSelectionTreeUI.java | 4 +++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/platform/platform-api/src/com/intellij/ui/treeStructure/treetable/TreeTableCellRenderer.java b/platform/platform-api/src/com/intellij/ui/treeStructure/treetable/TreeTableCellRenderer.java index 19e12595de77..1bd5f1d303e2 100644 --- a/platform/platform-api/src/com/intellij/ui/treeStructure/treetable/TreeTableCellRenderer.java +++ b/platform/platform-api/src/com/intellij/ui/treeStructure/treetable/TreeTableCellRenderer.java @@ -41,14 +41,15 @@ public class TreeTableCellRenderer implements TableCellRenderer, ClientPropertyH public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { int modelRow = table.convertRowIndexToModel(row); + final boolean lineHasFocus = table.hasFocus(); if (myTreeCellRenderer != null) myTree.setCellRenderer(myTreeCellRenderer); if (isSelected){ - myTree.setBackground(table.getSelectionBackground()); + myTree.setBackground(lineHasFocus ? table.getSelectionBackground() : UIUtil.getTreeUnfocusedSelectionBackground()); myTree.setForeground(table.getSelectionForeground()); } - else{ + else { myTree.setBackground(table.getBackground()); myTree.setForeground(table.getForeground()); } @@ -60,7 +61,7 @@ public class TreeTableCellRenderer implements TableCellRenderer, ClientPropertyH final Object treeObject = myTree.getPathForRow(modelRow).getLastPathComponent(); boolean leaf = myTree.getModel().isLeaf(treeObject); final boolean expanded = myTree.isExpanded(modelRow); - Component component = myTree.getCellRenderer().getTreeCellRendererComponent(myTree, treeObject, isSelected, expanded, leaf, modelRow, hasFocus); + Component component = myTree.getCellRenderer().getTreeCellRendererComponent(myTree, treeObject, isSelected, expanded, leaf, modelRow, lineHasFocus); if (component instanceof JComponent) { table.setToolTipText(((JComponent)component).getToolTipText()); } diff --git a/platform/util/src/com/intellij/util/ui/tree/WideSelectionTreeUI.java b/platform/util/src/com/intellij/util/ui/tree/WideSelectionTreeUI.java index 93cdf6a01822..e2e1f74c3690 100644 --- a/platform/util/src/com/intellij/util/ui/tree/WideSelectionTreeUI.java +++ b/platform/util/src/com/intellij/util/ui/tree/WideSelectionTreeUI.java @@ -38,6 +38,7 @@ import java.awt.event.MouseListener; */ public class WideSelectionTreeUI extends BasicTreeUI { public static final String TREE_TABLE_TREE_KEY = "TreeTableTree"; + public static final String NO_SELECTION_PAINTING_DARK = "NO_SELECTION_PAINTING_DARK"; @NonNls public static final String SOURCE_LIST_CLIENT_PROPERTY = "mac.ui.source.list"; @NonNls public static final String STRIPED_CLIENT_PROPERTY = "mac.ui.striped"; @@ -361,7 +362,8 @@ public class WideSelectionTreeUI extends BasicTreeUI { } } else { - if (selected && (UIUtil.isUnderAquaBasedLookAndFeel() || UIUtil.isUnderDarcula() || UIUtil.isUnderIntelliJLaF())) { + if (selected && (UIUtil.isUnderAquaBasedLookAndFeel() || UIUtil.isUnderDarcula() || UIUtil.isUnderIntelliJLaF()) + && ! Boolean.TRUE.equals(tree.getClientProperty(NO_SELECTION_PAINTING_DARK))) { Color bg = UIUtil.getTreeSelectionBackground(tree.hasFocus() || Boolean.TRUE.equals(tree.getClientProperty(TREE_TABLE_TREE_KEY))); if (myWideSelectionCondition.value(row)) { From c5937da471b84346ee98b5c6c257caa1699fc9d2 Mon Sep 17 00:00:00 2001 From: peter Date: Fri, 8 May 2015 14:21:28 +0200 Subject: [PATCH 05/28] debug JavaAutoPopupTest.testEveryPossibleWayToTypeIf --- .../codeInsight/completion/JavaAutoPopupTest.groovy | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/completion/JavaAutoPopupTest.groovy b/java/java-tests/testSrc/com/intellij/codeInsight/completion/JavaAutoPopupTest.groovy index d9b143677dbb..2b3bba8b5f56 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/completion/JavaAutoPopupTest.groovy +++ b/java/java-tests/testSrc/com/intellij/codeInsight/completion/JavaAutoPopupTest.groovy @@ -39,6 +39,8 @@ import com.intellij.openapi.command.undo.UndoManager import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.EditorFactory import com.intellij.openapi.editor.actionSystem.EditorActionManager +import com.intellij.openapi.editor.event.DocumentAdapter +import com.intellij.openapi.editor.event.DocumentEvent import com.intellij.openapi.editor.ex.EditorEx import com.intellij.openapi.extensions.Extensions import com.intellij.openapi.extensions.LoadingOrder @@ -950,6 +952,15 @@ class Foo { for (a1 in 0..actions) { for (a2 in 0..actions) { myFixture.configureByText("$a1 $a2 .java", src) + myFixture.editor.document.addDocumentListener(new DocumentAdapter() { + @Override + void documentChanged(DocumentEvent e) { + if (e.newFragment.toString().contains("a")) { + fail(e.toString()) + } + super.documentChanged(e) + } + }) myFixture.type 'i' joinSomething(a1) myFixture.type 'f' From 6afb1cb83f0deba1f67c3b860ccef5e98f515007 Mon Sep 17 00:00:00 2001 From: peter Date: Fri, 8 May 2015 15:10:28 +0200 Subject: [PATCH 06/28] java completion: don't use statistics when completing a super method call --- .../completion/JavaCompletionStatistician.java | 8 ++++++++ .../normalSorting/NoStatsInSuperInvocation.java | 10 ++++++++++ .../completion/NormalCompletionOrderingTest.groovy | 12 ++++++++++++ 3 files changed, 30 insertions(+) create mode 100644 java/java-tests/testData/codeInsight/completion/normalSorting/NoStatsInSuperInvocation.java diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionStatistician.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionStatistician.java index c5c1e3994a46..8fdcae303022 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionStatistician.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionStatistician.java @@ -19,6 +19,7 @@ import com.intellij.codeInsight.ExpectedTypeInfo; import com.intellij.codeInsight.ExpectedTypeInfoImpl; import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.codeInsight.lookup.LookupItem; +import com.intellij.patterns.ElementPattern; import com.intellij.psi.*; import com.intellij.psi.statistics.JavaStatisticsManager; import com.intellij.psi.statistics.StatisticsInfo; @@ -27,10 +28,13 @@ import com.intellij.util.containers.ContainerUtil; import java.util.List; +import static com.intellij.patterns.PsiJavaPatterns.psiElement; + /** * @author peter */ public class JavaCompletionStatistician extends CompletionStatistician{ + private static final ElementPattern SUPER_CALL = psiElement().afterLeaf(psiElement().withText(".").afterLeaf(PsiKeyword.SUPER)); @Override public StatisticsInfo serialize(final LookupElement element, final CompletionLocation location) { @@ -40,6 +44,10 @@ public class JavaCompletionStatistician extends CompletionStatistician{ return StatisticsInfo.EMPTY; } + if (SUPER_CALL.accepts(location.getCompletionParameters().getPosition())) { + return StatisticsInfo.EMPTY; + } + LookupItem item = element.as(LookupItem.CLASS_CONDITION_KEY); if (item == null) return null; diff --git a/java/java-tests/testData/codeInsight/completion/normalSorting/NoStatsInSuperInvocation.java b/java/java-tests/testData/codeInsight/completion/normalSorting/NoStatsInSuperInvocation.java new file mode 100644 index 000000000000..a558e824c74c --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/normalSorting/NoStatsInSuperInvocation.java @@ -0,0 +1,10 @@ +import java.lang.Object; +import java.lang.Override; +import java.util.HashMap; + +class Foo extends HashMap { + @Override + public Object get(Object key) { + pu + } +} diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/completion/NormalCompletionOrderingTest.groovy b/java/java-tests/testSrc/com/intellij/codeInsight/completion/NormalCompletionOrderingTest.groovy index e0bc158b921b..b472f87f9c8d 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/completion/NormalCompletionOrderingTest.groovy +++ b/java/java-tests/testSrc/com/intellij/codeInsight/completion/NormalCompletionOrderingTest.groovy @@ -658,4 +658,16 @@ interface TxANotAnno {} assertPreferredItems 0, 'newLinkedSet1', 'newLinkedSet0', 'newLinkedSet2' } + public void testNoStatsInSuperInvocation() { + checkPreferredItems 0, 'put', 'putAll' + + myFixture.type('\n') + assert myFixture.editor.document.text.contains("put") + + myFixture.type(');\nsuper.') + myFixture.completeBasic() + + assertPreferredItems 0, 'get' + } + } From 4cd2509c294dd85525527ea05f21ad66c31cff17 Mon Sep 17 00:00:00 2001 From: peter Date: Fri, 8 May 2015 15:22:01 +0200 Subject: [PATCH 07/28] don't blink in JavaAutoPopupTest.testExplicitSelectionShouldSurvive --- .../intellij/codeInsight/completion/JavaAutoPopupTest.groovy | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/completion/JavaAutoPopupTest.groovy b/java/java-tests/testSrc/com/intellij/codeInsight/completion/JavaAutoPopupTest.groovy index 2b3bba8b5f56..502acd69a68d 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/completion/JavaAutoPopupTest.groovy +++ b/java/java-tests/testSrc/com/intellij/codeInsight/completion/JavaAutoPopupTest.groovy @@ -154,10 +154,11 @@ class JavaAutoPopupTest extends CompletionAutoPopupTestCase { assertEquals 'iterable', lookup.currentItem.lookupString edt { myFixture.performEditorAction IdeActions.ACTION_EDITOR_MOVE_CARET_DOWN } - assertEquals 'iterable2', lookup.currentItem.lookupString + assert lookup.currentItem.lookupString == 'iterable2' type "r" - myFixture.assertPreferredCompletionItems 2, "iter", "iterable", 'iterable2' + assert lookup.items[0].lookupString == 'iter' + assert lookup.currentItem.lookupString == 'iterable2' } From f093653738b6f9650fcbec778916ccf9b14df0a2 Mon Sep 17 00:00:00 2001 From: "Egor.Ushakov" Date: Fri, 8 May 2015 16:44:09 +0300 Subject: [PATCH 08/28] fixed forked tests debugging did not stop in the first sub-session --- .../intellij/debugger/engine/DebugProcessImpl.java | 1 + .../intellij/xdebugger/impl/XDebugSessionImpl.java | 12 ++++++++---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/DebugProcessImpl.java b/java/debugger/impl/src/com/intellij/debugger/engine/DebugProcessImpl.java index 7c2a2df15a33..13ef8d37cd5c 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/DebugProcessImpl.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/DebugProcessImpl.java @@ -1769,6 +1769,7 @@ public abstract class DebugProcessImpl extends UserDataHolderBase implements Deb ApplicationManager.getApplication().assertIsDispatchThread(); //TODO: remove this requirement ((XDebugSessionImpl)getXdebugProcess().getSession()).reset(); myState.set(STATE_INITIAL); + myRequestManager.processDetached(this, false); myConnection = environment.getRemoteConnection(); getManagerThread().restartIfNeeded(); createVirtualMachine(environment.getSessionName(), environment.isPollConnection()); diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/XDebugSessionImpl.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/XDebugSessionImpl.java index e83d1eb9f657..834f77d9e63e 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/XDebugSessionImpl.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/XDebugSessionImpl.java @@ -302,10 +302,14 @@ public class XDebugSessionImpl implements XDebugSession { disableSlaveBreakpoints(dependentBreakpointManager); processAllBreakpoints(true, false); - myBreakpointListener = new MyBreakpointListener(); - breakpointManager.addBreakpointListener(myBreakpointListener); - myDependentBreakpointListener = new MyDependentBreakpointListener(); - dependentBreakpointManager.addListener(myDependentBreakpointListener); + if (myBreakpointListener == null) { + myBreakpointListener = new MyBreakpointListener(); + breakpointManager.addBreakpointListener(myBreakpointListener); + } + if (myDependentBreakpointListener == null) { + myDependentBreakpointListener = new MyDependentBreakpointListener(); + dependentBreakpointManager.addListener(myDependentBreakpointListener); + } } @Override From 7cb6c5c3f43c0469f1d01c8dc30fdb1f5a0261d9 Mon Sep 17 00:00:00 2001 From: Aleksey Pivovarov Date: Fri, 8 May 2015 16:14:21 +0300 Subject: [PATCH 09/28] diff: try to fallback to patience diff on FilesTooBigForDiffException PatienceLCS can split big input into smaller ones, and thus will be able to compute the diff. --- .../util/src/com/intellij/util/diff/Diff.java | 14 +++++++++++--- .../com/intellij/util/diff/PatienceIntLCS.java | 16 ++++++++++++++++ 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/platform/util/src/com/intellij/util/diff/Diff.java b/platform/util/src/com/intellij/util/diff/Diff.java index f6ee59c5dbef..2b253e471212 100644 --- a/platform/util/src/com/intellij/util/diff/Diff.java +++ b/platform/util/src/com/intellij/util/diff/Diff.java @@ -80,9 +80,17 @@ public class Diff { changes = patienceIntLCS.getChanges(); } else { - IntLCS intLCS = new IntLCS(discarded[0], discarded[1]); - intLCS.execute(); - changes = intLCS.getChanges(); + try { + IntLCS intLCS = new IntLCS(discarded[0], discarded[1]); + intLCS.execute(); + changes = intLCS.getChanges(); + } + catch (FilesTooBigForDiffException e) { + PatienceIntLCS patienceIntLCS = new PatienceIntLCS(discarded[0], discarded[1]); + patienceIntLCS.failOnSmallSizeReduction(); + patienceIntLCS.execute(); + changes = patienceIntLCS.getChanges(); + } } reindexer.reindex(changes, builder); diff --git a/platform/util/src/com/intellij/util/diff/PatienceIntLCS.java b/platform/util/src/com/intellij/util/diff/PatienceIntLCS.java index f20353e48ccf..672a4b6190f5 100644 --- a/platform/util/src/com/intellij/util/diff/PatienceIntLCS.java +++ b/platform/util/src/com/intellij/util/diff/PatienceIntLCS.java @@ -29,6 +29,8 @@ public class PatienceIntLCS { private final BitSet myChanges1; private final BitSet myChanges2; + private boolean myFailOnSmallReduction; + public PatienceIntLCS(int[] first, int[] second) { this(first, second, 0, first.length, 0, second.length, new BitSet(first.length), new BitSet(second.length)); } @@ -45,6 +47,10 @@ public class PatienceIntLCS { myChanges2 = changes2; } + public void failOnSmallSizeReduction() { + myFailOnSmallReduction = true; + } + public void execute() throws FilesTooBigForDiffException { if (myCount1 == 0 && myCount2 == 0) { return; @@ -71,6 +77,7 @@ public class PatienceIntLCS { int[][] matching = uniqueLCS.execute(); if (matching == null) { + checkReduction(count1, count2); IntLCS intLCS = new IntLCS(myFirst, mySecond, start1, count1, start2, count2, myChanges1, myChanges2); intLCS.execute(); } @@ -91,6 +98,7 @@ public class PatienceIntLCS { c2 = matching[1][i] - s2; if (c1 > 0 || c2 > 0) { + checkReduction(c1, c2); patienceDiff = new PatienceIntLCS(myFirst, mySecond, start1 + s1, c1, start2 + s2, c2, myChanges1, myChanges2); patienceDiff.execute(); } @@ -113,6 +121,7 @@ public class PatienceIntLCS { c2 = count2 - s2; } + checkReduction(c1, c2); patienceDiff = new PatienceIntLCS(myFirst, mySecond, start1 + s1, c1, start2 + s2, c2, myChanges1, myChanges2); patienceDiff.execute(); } @@ -147,4 +156,11 @@ public class PatienceIntLCS { public BitSet[] getChanges() { return new BitSet[]{myChanges1, myChanges2}; } + + private void checkReduction(int count1, int count2) throws FilesTooBigForDiffException { + if (!myFailOnSmallReduction) return; + if (count1 * 2 < myCount1) return; + if (count2 * 2 < myCount2) return; + throw new FilesTooBigForDiffException(0); + } } From 03c13df1e3f911366af722d11fdfab64ce19d400 Mon Sep 17 00:00:00 2001 From: peter Date: Fri, 8 May 2015 15:52:16 +0200 Subject: [PATCH 10/28] [^ann] InferenceVariable: make isInheritor consistent with its super types --- .../graphInference/InferenceVariable.java | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceVariable.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceVariable.java index 79242a72184f..0fa6744271df 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceVariable.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceVariable.java @@ -15,10 +15,7 @@ */ package com.intellij.psi.impl.source.resolve.graphInference; -import com.intellij.psi.PsiClassType; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiType; -import com.intellij.psi.PsiTypeParameter; +import com.intellij.psi.*; import com.intellij.psi.impl.light.LightTypeParameter; import com.intellij.psi.util.PsiUtil; import org.jetbrains.annotations.NotNull; @@ -133,6 +130,19 @@ public class InferenceVariable extends LightTypeParameter { myThrownBound = true; } + @Override + public boolean isInheritor(@NotNull PsiClass baseClass, boolean checkDeep) { + for (PsiType type : getBounds(InferenceBound.UPPER)) { + PsiClass psiClass = PsiUtil.resolveClassInClassTypeOnly(type); + if (psiClass != null) { + if (getManager().areElementsEquivalent(baseClass, psiClass)) return true; + if (checkDeep && psiClass.isInheritor(baseClass, true)) return true; + } + } + + return super.isInheritor(baseClass, checkDeep); + } + @Override public boolean isEquivalentTo(PsiElement another) { return this == another || getDelegate() == another; From 0d9b64c67483e86060eec64173c215256c752635 Mon Sep 17 00:00:00 2001 From: peter Date: Fri, 8 May 2015 15:54:21 +0200 Subject: [PATCH 11/28] scope-dependent java type assignability check (IDEA-126629) --- .../intellij/psi/util/JavaClassSupers.java | 50 ++++++++ .../intellij/psi/util/TypeConversionUtil.java | 19 +-- .../psi/impl/JavaClassSupersImpl.java | 112 +++++++++++++++++ .../psi/impl/ScopedClassHierarchy.java | 117 ++++++++++++++++++ .../MultipleJdksHighlightingTest.java | 5 +- resources/src/META-INF/IdeaPlugin.xml | 3 + 6 files changed, 293 insertions(+), 13 deletions(-) create mode 100644 java/java-psi-api/src/com/intellij/psi/util/JavaClassSupers.java create mode 100644 java/java-psi-impl/src/com/intellij/psi/impl/JavaClassSupersImpl.java create mode 100644 java/java-psi-impl/src/com/intellij/psi/impl/ScopedClassHierarchy.java diff --git a/java/java-psi-api/src/com/intellij/psi/util/JavaClassSupers.java b/java/java-psi-api/src/com/intellij/psi/util/JavaClassSupers.java new file mode 100644 index 000000000000..5acfb3cfb4a6 --- /dev/null +++ b/java/java-psi-api/src/com/intellij/psi/util/JavaClassSupers.java @@ -0,0 +1,50 @@ +/* + * 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.psi.util; + +import com.intellij.openapi.components.ServiceManager; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiSubstitutor; +import com.intellij.psi.search.GlobalSearchScope; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * @author peter + */ +public abstract class JavaClassSupers { + + public static JavaClassSupers getInstance() { + return ServiceManager.getService(JavaClassSupers.class); + } + + /** + * Calculates substitutor that binds type parameters in superClass with + * values that they have in derivedClass, given that type parameters in + * derivedClass are bound by derivedSubstitutor. + * + * @return substitutor or null, if derivedClass doesn't inherit superClass + * @see PsiClass#isInheritor(PsiClass, boolean) + * @see InheritanceUtil#isInheritorOrSelf(PsiClass, PsiClass, boolean) + */ + @Nullable + public abstract PsiSubstitutor getSuperClassSubstitutor(@NotNull PsiClass superClass, + @NotNull PsiClass derivedClass, + @NotNull GlobalSearchScope resolveScope, + @NotNull PsiSubstitutor derivedSubstitutor); + + +} diff --git a/java/java-psi-api/src/com/intellij/psi/util/TypeConversionUtil.java b/java/java-psi-api/src/com/intellij/psi/util/TypeConversionUtil.java index 76fcac865c45..d4654337a9b6 100644 --- a/java/java-psi-api/src/com/intellij/psi/util/TypeConversionUtil.java +++ b/java/java-psi-api/src/com/intellij/psi/util/TypeConversionUtil.java @@ -830,7 +830,7 @@ public class TypeConversionUtil { && rText.endsWith(lText) && rText.charAt(rText.length() - lText.length() - 1) == '.'; } - return isClassAssignable(leftResult, rightResult, allowUncheckedConversion); + return isClassAssignable(leftResult, rightResult, allowUncheckedConversion, left.getResolveScope()); } private static boolean isAssignableFromWildcard(@NotNull PsiType left, @NotNull PsiWildcardType rightWildcardType) { @@ -923,18 +923,19 @@ public class TypeConversionUtil { private static boolean isClassAssignable(@NotNull PsiClassType.ClassResolveResult leftResult, @NotNull PsiClassType.ClassResolveResult rightResult, - boolean allowUncheckedConversion) { + boolean allowUncheckedConversion, GlobalSearchScope resolveScope) { final PsiClass leftClass = leftResult.getElement(); final PsiClass rightClass = rightResult.getElement(); - return leftClass != null - && rightClass != null - && InheritanceUtil.isInheritorOrSelf(rightClass, leftClass, true) - && typeParametersAgree(leftResult, rightResult, allowUncheckedConversion); + if (leftClass == null || rightClass == null) return false; + + PsiSubstitutor superSubstitutor = JavaClassSupers.getInstance().getSuperClassSubstitutor(leftClass, rightClass, resolveScope, + rightResult.getSubstitutor()); + return superSubstitutor != null && typeParametersAgree(leftResult, rightResult, allowUncheckedConversion, superSubstitutor); } private static boolean typeParametersAgree(@NotNull PsiClassType.ClassResolveResult leftResult, @NotNull PsiClassType.ClassResolveResult rightResult, - boolean allowUncheckedConversion) { + boolean allowUncheckedConversion, PsiSubstitutor superSubstitutor) { PsiSubstitutor rightSubstitutor = rightResult.getSubstitutor(); PsiClass leftClass = leftResult.getElement(); PsiClass rightClass = rightResult.getElement(); @@ -945,7 +946,7 @@ public class TypeConversionUtil { PsiSubstitutor leftSubstitutor = leftResult.getSubstitutor(); if (!leftClass.getManager().areElementsEquivalent(leftClass, rightClass)) { - rightSubstitutor = getSuperClassSubstitutor(leftClass, rightClass, rightSubstitutor); + rightSubstitutor = superSubstitutor; rightClass = leftClass; } else if (!PsiUtil.typeParametersIterator(rightClass).hasNext()) return true; @@ -1063,7 +1064,7 @@ public class TypeConversionUtil { * * @return substitutor (never returns null) * @see PsiClass#isInheritor(PsiClass, boolean) - * @see InheritanceUtil#isInheritorOrSelf(com.intellij.psi.PsiClass, com.intellij.psi.PsiClass, boolean) + * @see InheritanceUtil#isInheritorOrSelf(PsiClass, PsiClass, boolean) */ @NotNull public static PsiSubstitutor getSuperClassSubstitutor(@NotNull PsiClass superClass, diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/JavaClassSupersImpl.java b/java/java-psi-impl/src/com/intellij/psi/impl/JavaClassSupersImpl.java new file mode 100644 index 000000000000..4fed53fbd002 --- /dev/null +++ b/java/java-psi-impl/src/com/intellij/psi/impl/JavaClassSupersImpl.java @@ -0,0 +1,112 @@ +/* + * 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.psi.impl; + +import com.intellij.psi.*; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.util.JavaClassSupers; +import com.intellij.psi.util.PsiUtil; +import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Map; +import java.util.Set; + +/** + * @author peter + */ +public class JavaClassSupersImpl extends JavaClassSupers { + + @Nullable + public PsiSubstitutor getSuperClassSubstitutor(@NotNull PsiClass superClass, + @NotNull PsiClass derivedClass, + @NotNull GlobalSearchScope scope, + @NotNull PsiSubstitutor derivedSubstitutor) { + if (isJavaLangObject(superClass)) return PsiSubstitutor.EMPTY; + + return derivedClass instanceof PsiTypeParameter + ? processTypeParameter((PsiTypeParameter)derivedClass, scope, superClass, ContainerUtil.newTroveSet(), derivedSubstitutor) + : getSuperSubstitutorWithCaching(superClass, derivedClass, scope, derivedSubstitutor); + } + + @Nullable + private static PsiSubstitutor getSuperSubstitutorWithCaching(@NotNull PsiClass superClass, + @NotNull PsiClass derivedClass, + @NotNull GlobalSearchScope resolveScope, + @NotNull PsiSubstitutor derivedSubstitutor) { + PsiSubstitutor substitutor = ScopedClassHierarchy.getSuperClassSubstitutor(derivedClass, resolveScope, superClass); + if (substitutor == null) return null; + if (PsiUtil.isRawSubstitutor(derivedClass, derivedSubstitutor)) return createRawSubstitutor(superClass); + + return composeSubstitutors(derivedSubstitutor, substitutor); + } + + static boolean isJavaLangObject(PsiClass psiClass) { + return CommonClassNames.JAVA_LANG_OBJECT.equals(psiClass.getQualifiedName()); + } + + @NotNull + static PsiSubstitutor createRawSubstitutor(@NotNull PsiClass superClass) { + return JavaPsiFacade.getElementFactory(superClass.getProject()).createRawSubstitutor(superClass); + } + + @NotNull + private static PsiSubstitutor composeSubstitutors(PsiSubstitutor outer, PsiSubstitutor inner) { + PsiSubstitutor answer = PsiSubstitutor.EMPTY; + for (Map.Entry entry : inner.getSubstitutionMap().entrySet()) { + answer = answer.put(entry.getKey(), outer.substitute(entry.getValue())); + } + return answer; + } + + /** + * Some type parameters (e.g. {@link com.intellij.psi.impl.source.resolve.graphInference.InferenceVariable} change their supers at will, + * so caching the hierarchy is impossible. + */ + @Nullable + private static PsiSubstitutor processTypeParameter(PsiTypeParameter parameter, + GlobalSearchScope scope, + PsiClass superClass, + Set visited, + PsiSubstitutor derivedSubstitutor) { + if (parameter.getManager().areElementsEquivalent(parameter, superClass)) return PsiSubstitutor.EMPTY; + if (!visited.add(parameter)) return null; + + for (PsiClassType type : parameter.getExtendsListTypes()) { + PsiClassType.ClassResolveResult result = type.resolveGenerics(); + PsiClass psiClass = result.getElement(); + if (psiClass == null) continue; + + PsiSubstitutor answer; + if (psiClass instanceof PsiTypeParameter) { + answer = processTypeParameter((PsiTypeParameter)psiClass, scope, superClass, visited, derivedSubstitutor); + if (answer != null) { + return answer; + } + } + else { + answer = getSuperSubstitutorWithCaching(superClass, psiClass, scope, result.getSubstitutor()); + if (answer != null) { + return composeSubstitutors(derivedSubstitutor, answer); + } + } + } + + return null; + } + +} diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/ScopedClassHierarchy.java b/java/java-psi-impl/src/com/intellij/psi/impl/ScopedClassHierarchy.java new file mode 100644 index 000000000000..0145def28601 --- /dev/null +++ b/java/java-psi-impl/src/com/intellij/psi/impl/ScopedClassHierarchy.java @@ -0,0 +1,117 @@ +/* + * 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.psi.impl; + +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.psi.*; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.util.*; +import com.intellij.util.containers.ConcurrentFactoryMap; +import com.intellij.util.containers.ContainerUtil; +import gnu.trove.TObjectHashingStrategy; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Iterator; +import java.util.Map; + +import static com.intellij.util.ObjectUtils.assertNotNull; + +/** + * @author peter + */ +class ScopedClassHierarchy { + private static final TObjectHashingStrategy CLASS_HASHING_STRATEGY = new TObjectHashingStrategy() { + @Override + public int computeHashCode(PsiClass object) { + return StringUtil.notNullize(object.getQualifiedName()).hashCode(); + } + + @Override + public boolean equals(PsiClass o1, PsiClass o2) { + return o1.getManager().areElementsEquivalent(o1, o2); + } + }; + private final Map mySupersWithSubstitutors = ContainerUtil.newTroveMap(CLASS_HASHING_STRATEGY); + + private ScopedClassHierarchy(PsiClass psiClass, GlobalSearchScope resolveScope) { + PsiClassType type = JavaPsiFacade.getElementFactory(psiClass.getProject()).createType(psiClass, PsiSubstitutor.EMPTY); + visitTypes(resolveScope, PsiSubstitutor.EMPTY, type); + } + + private void visitTypes(GlobalSearchScope resolveScope, PsiSubstitutor substitutor, PsiType... types) { + for (PsiType type : types) { + type = PsiClassImplUtil.correctType(substitutor.substitute(type), resolveScope); + if (!(type instanceof PsiClassType)) continue; + + PsiClassType.ClassResolveResult resolveResult = ((PsiClassType)type).resolveGenerics(); + PsiClass psiClass = resolveResult.getElement(); + if (psiClass == null || JavaClassSupersImpl.isJavaLangObject(psiClass) || mySupersWithSubstitutors.containsKey(psiClass)) continue; + + mySupersWithSubstitutors.put(psiClass, resolveResult); + + if (psiClass instanceof PsiAnonymousClass) { + visitTypes(resolveScope, resolveResult.getSubstitutor(), ((PsiAnonymousClass)psiClass).getBaseClassType()); + } + visitTypes(resolveScope, resolveResult.getSubstitutor(), psiClass.getExtendsListTypes()); + visitTypes(resolveScope, resolveResult.getSubstitutor(), psiClass.getImplementsListTypes()); + } + } + + @NotNull + private static ScopedClassHierarchy getHierarchy(@NotNull final PsiClass psiClass, @NotNull final GlobalSearchScope resolveScope) { + return CachedValuesManager.getCachedValue(psiClass, new CachedValueProvider>() { + @Nullable + @Override + public Result> compute() { + Map result = new ConcurrentFactoryMap() { + @Nullable + @Override + protected ScopedClassHierarchy create(GlobalSearchScope resolveScope) { + return new ScopedClassHierarchy(psiClass, resolveScope); + } + }; + return Result.create(result, PsiModificationTracker.JAVA_STRUCTURE_MODIFICATION_COUNT); + } + }).get(resolveScope); + } + + @Nullable + static PsiSubstitutor getSuperClassSubstitutor(@NotNull PsiClass derivedClass, @NotNull GlobalSearchScope scope, @NotNull PsiClass superClass) { + PsiClassType.ClassResolveResult resolveResult = getHierarchy(derivedClass, scope).mySupersWithSubstitutors.get(superClass); + if (resolveResult == null) return null; + + PsiClass cachedClass = assertNotNull(resolveResult.getElement()); + PsiSubstitutor cachedSubstitutor = resolveResult.getSubstitutor(); + return cachedClass == superClass ? cachedSubstitutor : mirrorSubstitutor(superClass, cachedClass, cachedSubstitutor); + } + + @NotNull + private static PsiSubstitutor mirrorSubstitutor(@NotNull PsiClass from, @NotNull final PsiClass to, @NotNull PsiSubstitutor substitutor) { + Iterator baseParams = PsiUtil.typeParametersIterator(to); + Iterator candidateParams = PsiUtil.typeParametersIterator(from); + + PsiSubstitutor answer = PsiSubstitutor.EMPTY; + while (baseParams.hasNext()) { + // if equivalent classes "from" and "to" have different number of type parameters, then treat "to" as a raw type + if (!candidateParams.hasNext()) return JavaClassSupersImpl.createRawSubstitutor(to); + + answer = answer.put(baseParams.next(), substitutor.substitute(candidateParams.next())); + } + return answer; + } + +} diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/MultipleJdksHighlightingTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/MultipleJdksHighlightingTest.java index 7a9220b850dc..5ec5e6c9425b 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/MultipleJdksHighlightingTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/MultipleJdksHighlightingTest.java @@ -16,7 +16,6 @@ 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; @@ -30,8 +29,6 @@ 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; @@ -168,8 +165,8 @@ public class MultipleJdksHighlightingTest extends UsefulTestCase { doTest3Modules(); } - @Bombed(user = "peter,anna", year = 2015, month = Calendar.MAY, day = 15) public void testCloseableAutoCloseable() { + IdeaTestUtil.setModuleLanguageLevel(myJava7Module, LanguageLevel.JDK_1_7); ModuleRootModificationUtil.addDependency(myJava7Module, myJava3Module); final String name = getTestName(false); myFixture.copyFileToProject("java3/p/" + name + ".java"); diff --git a/resources/src/META-INF/IdeaPlugin.xml b/resources/src/META-INF/IdeaPlugin.xml index fb9ce9850db6..2a72949c0945 100644 --- a/resources/src/META-INF/IdeaPlugin.xml +++ b/resources/src/META-INF/IdeaPlugin.xml @@ -448,6 +448,9 @@ + + From 4b8ff1d2cdbaf4714742707165643564bf6921ce Mon Sep 17 00:00:00 2001 From: Sergey Ignatov Date: Fri, 8 May 2015 17:09:29 +0300 Subject: [PATCH 12/28] db view: fix icons for retina --- platform/core-api/src/com/intellij/util/IconUtil.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/platform/core-api/src/com/intellij/util/IconUtil.java b/platform/core-api/src/com/intellij/util/IconUtil.java index 12c44dd12791..78c7c4d38752 100644 --- a/platform/core-api/src/com/intellij/util/IconUtil.java +++ b/platform/core-api/src/com/intellij/util/IconUtil.java @@ -446,6 +446,11 @@ public class IconUtil { } } + return createImageIcon(img); + } + + @NotNull + public static JBImageIcon createImageIcon(@NotNull final BufferedImage img) { return new JBImageIcon(img) { @Override public int getIconWidth() { From 96de0f83f4c87fe66612c69b8914c9fc154334b1 Mon Sep 17 00:00:00 2001 From: Julia Beliaeva Date: Thu, 7 May 2015 16:07:09 +0300 Subject: [PATCH 13/28] [vcs-log] fix empty Author field in table when only an email was specified IDEA-137361 --- .../intellij/vcs/log/ui/tables/GraphTableModel.java | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/tables/GraphTableModel.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/tables/GraphTableModel.java index 476bab69af06..d76862ffb3d9 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/tables/GraphTableModel.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/tables/GraphTableModel.java @@ -10,7 +10,10 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.text.DateFormatUtil; -import com.intellij.vcs.log.*; +import com.intellij.vcs.log.Hash; +import com.intellij.vcs.log.VcsFullCommitDetails; +import com.intellij.vcs.log.VcsRef; +import com.intellij.vcs.log.VcsShortCommitDetails; import com.intellij.vcs.log.data.LoadingDetails; import com.intellij.vcs.log.data.VcsLogDataHolder; import com.intellij.vcs.log.data.VisiblePack; @@ -157,6 +160,7 @@ public class GraphTableModel extends AbstractTableModel { /** * Requests the proper data provider to load more data from the log & recreate the model. + * * @param onLoaded will be called upon task completion on the EDT. */ public void requestToLoadMore(@NotNull Runnable onLoaded) { @@ -183,7 +187,9 @@ public class GraphTableModel extends AbstractTableModel { return ""; } else { - return data.getAuthor().getName() + (data.getAuthor().equals(data.getCommitter()) ? "" : "*"); + String authorString = data.getAuthor().getName(); + if (authorString.isEmpty()) authorString = data.getAuthor().getEmail(); + return authorString + (data.getAuthor().equals(data.getCommitter()) ? "" : "*"); } case DATE_COLUMN: if (data == null || data.getAuthorTime() < 0) { @@ -208,6 +214,7 @@ public class GraphTableModel extends AbstractTableModel { * Returns Changes for commits at selected rows.
* Rows are given in the order as they appear in the table, i. e. in reverse chronological order.
* Changes can be returned as-is, i.e. with duplicate changes for a single file. + * * @return Changes selected in all rows, or null if this data is not ready yet. */ @Nullable From d95cd6665229a79c5bb622ec2982beac433179a6 Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Fri, 8 May 2015 16:36:40 +0200 Subject: [PATCH 14/28] junit sm runner: remember parents for every description to be able to restore the state and be always in sync with the initial tree (IDEA-135907) --- .../JUnitTreeByDescriptionHierarchyTest.java | 76 +++++--- .../src/com/intellij/junit4/SMTestSender.java | 162 +++++++----------- 2 files changed, 109 insertions(+), 129 deletions(-) diff --git a/plugins/junit/test/com/intellij/execution/junit/JUnitTreeByDescriptionHierarchyTest.java b/plugins/junit/test/com/intellij/execution/junit/JUnitTreeByDescriptionHierarchyTest.java index d46dff96d663..c5c051fe47bb 100644 --- a/plugins/junit/test/com/intellij/execution/junit/JUnitTreeByDescriptionHierarchyTest.java +++ b/plugins/junit/test/com/intellij/execution/junit/JUnitTreeByDescriptionHierarchyTest.java @@ -65,26 +65,25 @@ public class JUnitTreeByDescriptionHierarchyTest { "##teamcity[enteredTheMatrix]\n" + "##teamcity[rootName name = 'root' location = 'java:suite://root']\n" + - "##teamcity[testSuiteFinished name='root']\n" + - "##teamcity[testSuiteStarted name ='TestA']\n" + - "##teamcity[testSuiteStarted name ='|[0|]']\n" + + "##teamcity[testSuiteStarted name='TestA']\n" + + "##teamcity[testSuiteStarted name='|[0|]']\n" + "##teamcity[testStarted name='testName|[0|]' locationHint='java:test://a.TestA.testName|[0|]']\n" + "\n" + "##teamcity[testFinished name='testName|[0|]']\n" + "##teamcity[testSuiteFinished name='|[0|]']\n" + - "##teamcity[testSuiteStarted name ='|[1|]']\n" + + "##teamcity[testSuiteStarted name='|[1|]']\n" + "##teamcity[testStarted name='testName|[1|]' locationHint='java:test://a.TestA.testName|[1|]']\n" + "\n" + "##teamcity[testFinished name='testName|[1|]']\n" + "##teamcity[testSuiteFinished name='|[1|]']\n" + "##teamcity[testSuiteFinished name='TestA']\n" + - "##teamcity[testSuiteStarted name ='TestB']\n" + - "##teamcity[testSuiteStarted name ='|[0|]']\n" + + "##teamcity[testSuiteStarted name='TestB']\n" + + "##teamcity[testSuiteStarted name='|[0|]']\n" + "##teamcity[testStarted name='testName|[0|]' locationHint='java:test://a.TestB.testName|[0|]']\n" + "\n" + "##teamcity[testFinished name='testName|[0|]']\n" + "##teamcity[testSuiteFinished name='|[0|]']\n" + - "##teamcity[testSuiteStarted name ='|[1|]']\n" + + "##teamcity[testSuiteStarted name='|[1|]']\n" + "##teamcity[testStarted name='testName|[1|]' locationHint='java:test://a.TestB.testName|[1|]']\n" + "\n" + "##teamcity[testFinished name='testName|[1|]']\n" + @@ -109,17 +108,16 @@ public class JUnitTreeByDescriptionHierarchyTest { //start "##teamcity[enteredTheMatrix]\n" + "##teamcity[rootName name = 'TestA' comment = 'a' location = 'java:suite://a.TestA']\n" + - "##teamcity[testSuiteStarted name ='|[0|]']\n" + + "##teamcity[testSuiteStarted name='|[0|]']\n" + "##teamcity[testStarted name='testName|[0|]' locationHint='java:test://a.TestA.testName|[0|]']\n" + "\n" + "##teamcity[testFinished name='testName|[0|]']\n" + "##teamcity[testSuiteFinished name='|[0|]']\n" + - "##teamcity[testSuiteStarted name ='|[1|]']\n" + + "##teamcity[testSuiteStarted name='|[1|]']\n" + "##teamcity[testStarted name='testName|[1|]' locationHint='java:test://a.TestA.testName|[1|]']\n" + "\n" + "##teamcity[testFinished name='testName|[1|]']\n" + - "##teamcity[testSuiteFinished name='|[1|]']\n" + - "##teamcity[testSuiteFinished name='TestA']\n"); + "##teamcity[testSuiteFinished name='|[1|]']\n"); } @Test @@ -154,16 +152,15 @@ public class JUnitTreeByDescriptionHierarchyTest { //started "##teamcity[enteredTheMatrix]\n" + "##teamcity[rootName name = 'root' location = 'java:suite://root']\n" + - "##teamcity[testSuiteFinished name='root']\n" + - "##teamcity[testSuiteStarted name ='ASuite1']\n" + - "##teamcity[testSuiteStarted name ='ATest']\n" + + "##teamcity[testSuiteStarted name='ASuite1']\n" + + "##teamcity[testSuiteStarted name='ATest']\n" + "##teamcity[testStarted name='test1' locationHint='java:test://ATest.test1']\n" + "\n" + "##teamcity[testFinished name='test1']\n" + "##teamcity[testSuiteFinished name='ATest']\n" + "##teamcity[testSuiteFinished name='ASuite1']\n" + - "##teamcity[testSuiteStarted name ='ASuite2']\n" + - "##teamcity[testSuiteStarted name ='ATest']\n" + + "##teamcity[testSuiteStarted name='ASuite2']\n" + + "##teamcity[testSuiteStarted name='ATest']\n" + "##teamcity[testStarted name='test1' locationHint='java:test://ATest.test1']\n" + "\n" + "##teamcity[testFinished name='test1']\n" + @@ -204,8 +201,37 @@ public class JUnitTreeByDescriptionHierarchyTest { "##teamcity[rootName name = 'TestA' location = 'java:suite://TestA']\n" + "##teamcity[testStarted name='testName' locationHint='java:test://TestA.testName']\n" + "\n" + - "##teamcity[testFinished name='testName']\n" + - "##teamcity[testSuiteFinished name='TestA']\n"); + "##teamcity[testFinished name='testName']\n"); + } + + @Test + public void testParameterizedTestsUpsideDown() throws Exception { + final Description aTestClass = Description.createSuiteDescription("ATest"); + final ArrayList tests = new ArrayList(); + final Description testMethod = Description.createSuiteDescription("testName"); + aTestClass.addChild(testMethod); + for (String paramName : new String[]{"[0]", "[1]"}) { + final Description testDescription = Description.createTestDescription("ATest", "testName" + paramName); + tests.add(testDescription); + testMethod.addChild(testDescription); + } + doTest(aTestClass, tests, + "##teamcity[suiteTreeStarted name='testName' locationHint='java:suite://testName']\n" + + "##teamcity[suiteTreeNode name='testName|[0|]' locationHint='java:test://ATest.testName|[0|]']\n" + + "##teamcity[suiteTreeNode name='testName|[1|]' locationHint='java:test://ATest.testName|[1|]']\n" + + "##teamcity[suiteTreeEnded name='testName']\n", + + + "##teamcity[enteredTheMatrix]\n" + + "##teamcity[rootName name = 'ATest' location = 'java:suite://ATest']\n" + + "##teamcity[testSuiteStarted name='testName']\n" + + "##teamcity[testStarted name='testName|[0|]' locationHint='java:test://ATest.testName|[0|]']\n" + + "\n" + + "##teamcity[testFinished name='testName|[0|]']\n" + + "##teamcity[testStarted name='testName|[1|]' locationHint='java:test://ATest.testName|[1|]']\n" + + "\n" + + "##teamcity[testFinished name='testName|[1|]']\n" + + "##teamcity[testSuiteFinished name='testName']\n"); } @Test @@ -240,21 +266,20 @@ public class JUnitTreeByDescriptionHierarchyTest { //start "##teamcity[enteredTheMatrix]\n" + "##teamcity[rootName name = 'root' location = 'java:suite://root']\n" + - "##teamcity[testSuiteFinished name='root']\n" + - "##teamcity[testSuiteStarted name ='ATest']\n" + - "##teamcity[testSuiteStarted name ='|[0|]']\n" + + "##teamcity[testSuiteStarted name='ATest']\n" + + "##teamcity[testSuiteStarted name='|[0|]']\n" + "##teamcity[testStarted name='testName|[0|]' locationHint='java:test://ATest.testName|[0|]']\n" + "\n" + "##teamcity[testFinished name='testName|[0|]']\n" + "##teamcity[testSuiteFinished name='|[0|]']\n" + - "##teamcity[testSuiteStarted name ='|[1|]']\n" + + "##teamcity[testSuiteStarted name='|[1|]']\n" + "##teamcity[testStarted name='testName|[1|]' locationHint='java:test://ATest.testName|[1|]']\n" + "\n" + "##teamcity[testFinished name='testName|[1|]']\n" + "##teamcity[testSuiteFinished name='|[1|]']\n" + "##teamcity[testSuiteFinished name='ATest']\n" + - "##teamcity[testSuiteStarted name ='suite']\n" + - "##teamcity[testSuiteStarted name ='ATest']\n" + + "##teamcity[testSuiteStarted name='suite']\n" + + "##teamcity[testSuiteStarted name='ATest']\n" + "##teamcity[testStarted name='test' locationHint='java:test://ATest.test']\n" + "\n" + "##teamcity[testFinished name='test']\n" + @@ -298,7 +323,6 @@ public class JUnitTreeByDescriptionHierarchyTest { "##teamcity[rootName name = 'TestA' location = 'java:suite://TestA']\n" + "##teamcity[testStarted name='warning' locationHint='java:test://junit.framework.TestSuite$1.warning']\n" + "\n" + - "##teamcity[testFinished name='warning']\n" + - "##teamcity[testSuiteFinished name='TestA']\n"); + "##teamcity[testFinished name='warning']\n"); } } diff --git a/plugins/junit_rt/src/com/intellij/junit4/SMTestSender.java b/plugins/junit_rt/src/com/intellij/junit4/SMTestSender.java index 453a60e6fe7c..0523dd50800b 100644 --- a/plugins/junit_rt/src/com/intellij/junit4/SMTestSender.java +++ b/plugins/junit_rt/src/com/intellij/junit4/SMTestSender.java @@ -41,16 +41,14 @@ public class SMTestSender extends RunListener { public static final String EMPTY_SUITE_NAME = "junit.framework.TestSuite$1"; public static final String EMPTY_SUITE_WARNING = "warning"; - private String myCurrentSuiteName; - private String myCurrentClassName; - private String myParamName; + private List myStartedSuites = new ArrayList(); + private Map myParents = new HashMap(); + private final PrintStream myPrintStream; + private String myRootName; - private PrintStream myPrintStream = System.out; - private final Map myParents = new HashMap(); - private final Map mySuites = new HashMap(); - - - public SMTestSender() {} + public SMTestSender() { + myPrintStream = System.out; + } public SMTestSender(PrintStream printStream) { myPrintStream = printStream; @@ -62,100 +60,69 @@ public class SMTestSender extends RunListener { public void testRunStarted(Description description) throws Exception { myPrintStream.println("##teamcity[enteredTheMatrix]"); - if (myCurrentClassName != null && !myCurrentClassName.startsWith("[")) { - int lastPointIdx = myCurrentClassName.lastIndexOf('.'); - String name = myCurrentClassName; + if (myRootName != null && !myRootName.startsWith("[")) { + int lastPointIdx = myRootName.lastIndexOf('.'); + String name = myRootName; String comment = null; if (lastPointIdx >= 0) { - name = myCurrentClassName.substring(lastPointIdx + 1); - comment = myCurrentClassName.substring(0, lastPointIdx); + name = myRootName.substring(lastPointIdx + 1); + comment = myRootName.substring(0, lastPointIdx); } myPrintStream.println("##teamcity[rootName name = \'" + escapeName(name) + (comment != null ? ("\' comment = \'" + escapeName(comment)) : "") + "\'" + - " location = \'java:suite://" + escapeName(myCurrentClassName) + + " location = \'java:suite://" + escapeName(myRootName) + "\']"); - myCurrentClassName = getShortName(myCurrentClassName); + myRootName = getShortName(myRootName); } } public void testRunFinished(Result result) throws Exception { - if (myParamName != null) { - myPrintStream.println("##teamcity[testSuiteFinished name=\'" + escapeName(myParamName) + "\']"); - } - if (myCurrentClassName != null) { - myPrintStream.println("##teamcity[testSuiteFinished name=\'" + escapeName(myCurrentClassName) + "\']"); - } - if (myCurrentSuiteName != null) { - myPrintStream.println("##teamcity[testSuiteFinished name=\'" + escapeName(getShortName(myCurrentSuiteName)) + "\']"); + for (int i = myStartedSuites.size() - 1; i>= 0; i--) { + Object parent = myStartedSuites.get(i); + myPrintStream.println("##teamcity[testSuiteFinished name=\'" + escapeName((String)parent) + "\']"); } + myStartedSuites.clear(); } public void testStarted(Description description) throws Exception { final String methodName = JUnit4ReflectionUtil.getMethodName(description); final String classFQN = JUnit4ReflectionUtil.getClassName(description); - final int paramStart = methodName.indexOf('['); - if (myParamName != null){ - myPrintStream.println("##teamcity[testSuiteFinished name=\'" + escapeName(myParamName) + "\']"); - myParamName = null; - } - final List suites = (List)myParents.get(description); - if (suites != null && !suites.isEmpty()) { - String currentSuite = (String)suites.get(0); - List descriptors = (List)mySuites.get(currentSuite); + final List parents = (List)myParents.get(description); + if (parents != null) { - if (descriptors.isEmpty()) { - currentSuite = (String)suites.get(1); - descriptors = (List)mySuites.get(currentSuite); + List parentsHierarchy = (List)parents.remove(0); + + int idx = 0; + String currentClass; + String currentParent; + while (idx < myStartedSuites.size() && idx < parentsHierarchy.size()) { + currentClass = (String)myStartedSuites.get(idx); + currentParent = getShortName(JUnit4ReflectionUtil.getClassName((Description)parentsHierarchy.get(parentsHierarchy.size() - 1 - idx))); + if (!currentClass.equals(currentParent)) break; + idx++; } - if (!currentSuite.equals(myCurrentSuiteName)) { - finishCurrentSuite(); - myCurrentSuiteName = currentSuite; - myPrintStream.println("##teamcity[testSuiteStarted name =\'" + escapeName(getShortName(myCurrentSuiteName)) + "\']"); + for (int i = myStartedSuites.size() - 1; i >= idx; i--) { + currentClass = (String)myStartedSuites.remove(i); + myPrintStream.println("##teamcity[testSuiteFinished name=\'" + escapeName(currentClass) + "\']"); } - descriptors.remove(description); - } - else if (myCurrentSuiteName != null){ - finishCurrentSuite(); - myCurrentSuiteName = null; - } - - String className = getShortName(classFQN); - if (!myEmptyTests.isEmpty() && isWarning(methodName, classFQN)) { - className = (String)myEmptyTests.remove(0); - } - - if (!className.equals(myCurrentClassName)) { - if (myCurrentClassName != null) { - myPrintStream.println("##teamcity[testSuiteFinished name=\'" + escapeName(myCurrentClassName) + "\']"); - } - myCurrentClassName = className; - myPrintStream.println("##teamcity[testSuiteStarted name =\'" + escapeName(myCurrentClassName) + "\']"); - } - if (paramStart > -1) { - final String paramName = methodName.substring(paramStart, methodName.length()); - if (!paramName.equals(myParamName)) { - myParamName = paramName; - myPrintStream.println("##teamcity[testSuiteStarted name =\'" + escapeName(myParamName) + "\']"); + for (int i = idx; i < parentsHierarchy.size(); i++) { + Description parent = (Description)parentsHierarchy.get(parentsHierarchy.size() - 1 - i); + final String className = getShortName(JUnit4ReflectionUtil.getClassName(parent)); + if (!className.equals(myRootName)) { + myPrintStream.println("##teamcity[testSuiteStarted name=\'" + escapeName(className) + "\']"); + myStartedSuites.add(className); + } } } + myPrintStream.println("##teamcity[testStarted name=\'" + escapeName(methodName) + "\' " + getTestMethodLocation(methodName, classFQN) + "]"); } - private void finishCurrentSuite() { - if (myCurrentClassName != null) { - myPrintStream.println("##teamcity[testSuiteFinished name=\'" + escapeName(myCurrentClassName) + "\']"); - myCurrentClassName = null; - } - if (myCurrentSuiteName != null) { - myPrintStream.println("##teamcity[testSuiteFinished name=\'" + escapeName(getShortName(myCurrentSuiteName)) + "\']"); - } - } - public void testFinished(Description description) throws Exception { myPrintStream.println("\n##teamcity[testFinished name=\'" + escapeName(JUnit4ReflectionUtil.getMethodName(description)) + "\']"); } @@ -249,39 +216,29 @@ public class SMTestSender extends RunListener { return messageLength < threshold; } - private final List myEmptyTests = new ArrayList(); - - private void sendTree(Description description, Description parent, Description suiteParent) { + private void sendTree(Description description, Description parent, List currentParents) { + List pParents = new ArrayList(3); + pParents.addAll(currentParents); + if (parent != null && !myRootName.equals(JUnit4ReflectionUtil.getClassName(parent))) { + pParents.add(0, parent); + } + String className = JUnit4ReflectionUtil.getClassName(description); if (description.getChildren().isEmpty()) { final String methodName = JUnit4ReflectionUtil.getMethodName((Description)description); if (methodName != null) { - if (isWarning(methodName, className)) { - className = JUnit4ReflectionUtil.getClassName(parent); - myEmptyTests.add(getShortName(className)); - } - myPrintStream.println("##teamcity[suiteTreeNode name=\'" + escapeName(methodName) + "\' " + getTestMethodLocation(methodName, className) + "]"); - } - - if (suiteParent != null ) { - final String parentFQName = JUnit4ReflectionUtil.getClassName(suiteParent); - if (!myCurrentClassName.equals(parentFQName)) { + if (parent != null) { List parents = (List)myParents.get(description); if (parents == null) { - parents = new ArrayList(); + parents = new ArrayList(1); myParents.put(description, parents); } - if (!parents.contains(parentFQName)) { - parents.add(parentFQName); - } - - List descriptors = (List)mySuites.get(parentFQName); - if (descriptors == null) { - descriptors = new ArrayList(); - mySuites.put(parentFQName, descriptors); - } - descriptors.add(description); + parents.add(pParents); } + if (isWarning(methodName, className)) { + className = JUnit4ReflectionUtil.getClassName(parent); + } + myPrintStream.println("##teamcity[suiteTreeNode name=\'" + escapeName(methodName) + "\' " + getTestMethodLocation(methodName, className) + "]"); } return; @@ -292,8 +249,7 @@ public class SMTestSender extends RunListener { for (Iterator iterator = tests.iterator(); iterator.hasNext(); ) { final Object next = iterator.next(); final Description nextDescription = (Description)next; - final List childTests = nextDescription.getChildren(); - if ((myCurrentClassName == null || !myCurrentClassName.equals(className)) && !pass) { + if ((myRootName == null || !myRootName.equals(className)) && !pass) { pass = true; String locationHint = className; if (isParameter((Description)description)) { @@ -308,7 +264,7 @@ public class SMTestSender extends RunListener { } myPrintStream.println("##teamcity[suiteTreeStarted name=\'" + escapeName(getShortName(className)) + "\' locationHint=\'java:suite://" + escapeName(locationHint) + "\']"); } - sendTree(nextDescription, description, isParameter(description) ? null : childTests.isEmpty() && parent != null ? parent : description); + sendTree(nextDescription, description, pParents); } if (pass) { myPrintStream.println("##teamcity[suiteTreeEnded name=\'" + escapeName(getShortName(JUnit4ReflectionUtil.getClassName((Description)description))) + "\']"); @@ -329,8 +285,8 @@ public class SMTestSender extends RunListener { } public void sendTree(Description description) { - myCurrentClassName = JUnit4ReflectionUtil.getClassName((Description)description); - sendTree(description, null, null); + myRootName = JUnit4ReflectionUtil.getClassName((Description)description); + sendTree(description, null, new ArrayList()); } private static String getShortName(String fqName) { From baa93aaed9ce80e3aea2c6a73879aef62906443f Mon Sep 17 00:00:00 2001 From: Aleksey Pivovarov Date: Fri, 8 May 2015 17:45:20 +0300 Subject: [PATCH 15/28] diff: try to fallback to patience diff on FilesTooBigForDiffException - add missed check --- .../util/src/com/intellij/util/diff/PatienceIntLCS.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/platform/util/src/com/intellij/util/diff/PatienceIntLCS.java b/platform/util/src/com/intellij/util/diff/PatienceIntLCS.java index 672a4b6190f5..947f144e8b3a 100644 --- a/platform/util/src/com/intellij/util/diff/PatienceIntLCS.java +++ b/platform/util/src/com/intellij/util/diff/PatienceIntLCS.java @@ -86,8 +86,12 @@ public class PatienceIntLCS { int matched = matching[0].length; assert matched > 0; + c1 = matching[0][0]; + c2 = matching[1][0]; + + checkReduction(c1, c2); PatienceIntLCS patienceDiff = - new PatienceIntLCS(myFirst, mySecond, start1, matching[0][0], start2, matching[1][0], myChanges1, myChanges2); + new PatienceIntLCS(myFirst, mySecond, start1, c1, start2, c2, myChanges1, myChanges2); patienceDiff.execute(); for (int i = 1; i < matching[0].length; i++) { From 3543124c4250a97399c087b7bd411a88080bd01b Mon Sep 17 00:00:00 2001 From: nik Date: Fri, 8 May 2015 16:40:10 +0200 Subject: [PATCH 16/28] vcs test cases moved to vcs-impl module to avoid circular dependency between vcs-impl and testFramework --- platform/testFramework/testFramework.iml | 7 +- .../ExternalChangesDetectionVcsTest.java | 211 +++++++++++ .../committed/VcsEventsListenerTest.java | 355 ++++++++++++++++++ .../vcs/AbstractJunitVcsTestCase.java | 0 .../vcs/AbstractVcsTestCase.java | 0 5 files changed, 569 insertions(+), 4 deletions(-) create mode 100644 platform/vcs-impl/testSrc/com/intellij/openapi/vcs/changes/committed/ExternalChangesDetectionVcsTest.java create mode 100644 platform/vcs-impl/testSrc/com/intellij/openapi/vcs/changes/committed/VcsEventsListenerTest.java rename platform/{testFramework => vcs-impl}/testSrc/com/intellij/testFramework/vcs/AbstractJunitVcsTestCase.java (100%) rename platform/{testFramework => vcs-impl}/testSrc/com/intellij/testFramework/vcs/AbstractVcsTestCase.java (100%) diff --git a/platform/testFramework/testFramework.iml b/platform/testFramework/testFramework.iml index 19a4097b1f15..bb99b4dbda56 100644 --- a/platform/testFramework/testFramework.iml +++ b/platform/testFramework/testFramework.iml @@ -14,12 +14,12 @@ - + - + @@ -27,5 +27,4 @@ - - + \ No newline at end of file diff --git a/platform/vcs-impl/testSrc/com/intellij/openapi/vcs/changes/committed/ExternalChangesDetectionVcsTest.java b/platform/vcs-impl/testSrc/com/intellij/openapi/vcs/changes/committed/ExternalChangesDetectionVcsTest.java new file mode 100644 index 000000000000..ece7f752ddc7 --- /dev/null +++ b/platform/vcs-impl/testSrc/com/intellij/openapi/vcs/changes/committed/ExternalChangesDetectionVcsTest.java @@ -0,0 +1,211 @@ +package com.intellij.openapi.vcs.changes.committed; + +import com.intellij.ide.startup.impl.StartupManagerImpl; +import com.intellij.openapi.components.ProjectComponent; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.startup.StartupManager; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.vcs.FilePath; +import com.intellij.openapi.vcs.ProjectLevelVcsManager; +import com.intellij.openapi.vcs.VcsException; +import com.intellij.openapi.vcs.changes.*; +import com.intellij.openapi.vcs.impl.ProjectLevelVcsManagerImpl; +import com.intellij.openapi.vfs.LocalFileSystem; +import com.intellij.openapi.vfs.VfsUtil; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.testFramework.fixtures.IdeaTestFixtureFactory; +import com.intellij.testFramework.fixtures.TempDirTestFixture; +import com.intellij.testFramework.vcs.AbstractJunitVcsTestCase; +import com.intellij.util.Processor; +import com.intellij.util.ui.UIUtil; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.io.File; +import java.util.List; +import java.util.regex.Pattern; + +/** + * @author irengrig + */ +public class ExternalChangesDetectionVcsTest extends AbstractJunitVcsTestCase { + private MockAbstractVcs myVcs; + private ProjectLevelVcsManagerImpl myVcsManager; + private LocalFileSystem myLFS; + private ChangeListManager myChangeListManager; + private VcsDirtyScopeManager myVcsDirtyScopeManager; + private TempDirTestFixture myTempDirTestFixture; + private File myClientRoot; + + @Before + public void setUp() throws Exception { + UIUtil.invokeAndWaitIfNeeded(new Runnable() { + @Override + public void run() { + try { + final IdeaTestFixtureFactory fixtureFactory = IdeaTestFixtureFactory.getFixtureFactory(); + myTempDirTestFixture = fixtureFactory.createTempDirTestFixture(); + myTempDirTestFixture.setUp(); + + myClientRoot = new File(myTempDirTestFixture.getTempDirPath(), "clientroot"); + myClientRoot.mkdir(); + + initProject(myClientRoot, ExternalChangesDetectionVcsTest.this.getTestName()); + + ((StartupManagerImpl)StartupManager.getInstance(myProject)).runPostStartupActivities(); + + myVcs = new MockAbstractVcs(myProject); + myVcs.setChangeProvider(new MyMockChangeProvider()); + myVcsManager = (ProjectLevelVcsManagerImpl)ProjectLevelVcsManager.getInstance(myProject); + myVcsManager.registerVcs(myVcs); + myVcsManager.setDirectoryMapping("", myVcs.getName()); + + myLFS = LocalFileSystem.getInstance(); + myChangeListManager = ChangeListManager.getInstance(myProject); + ((ProjectComponent)myChangeListManager).projectOpened(); + myVcsDirtyScopeManager = VcsDirtyScopeManager.getInstance(myProject); + ((ProjectComponent)myVcsDirtyScopeManager).projectOpened(); + } + catch (Exception e) { + throw new RuntimeException(e); + } + } + }); + } + + @After + public void tearDown() throws Exception { + UIUtil.invokeAndWaitIfNeeded(new Runnable() { + @Override + public void run() { + try { + myVcsManager.unregisterVcs(myVcs); + ((ProjectComponent)myChangeListManager).projectClosed(); + ((ProjectComponent)myVcsDirtyScopeManager).projectClosed(); + + tearDownProject(); + if (myTempDirTestFixture != null) { + myTempDirTestFixture.tearDown(); + myTempDirTestFixture = null; + } + FileUtil.delete(myClientRoot); + } + catch (Exception e) { + throw new RuntimeException(e); + } + } + }); + } + + @Test + public void testDeletion() throws Exception { + final File f = new File(myClientRoot, "f.txt"); + f.createNewFile(); + final VirtualFile vf = myLFS.refreshAndFindFileByIoFile(f); + myChangeListManager.ensureUpToDate(false); + ((ChangeListManagerImpl) myChangeListManager).getUnversionedFiles().contains(vf); + FileUtil.delete(f); + myWorkingCopyDir.refresh(false, true); + myChangeListManager.ensureUpToDate(false); + ((ChangeListManagerImpl) myChangeListManager).getUnversionedFiles().isEmpty(); + } + + @Test + public void testGeneration() throws Exception { + for (int i = 0; i < 100; i++) { + final File f = new File(myClientRoot, "f" + i + ".txt"); + f.createNewFile(); + } + myWorkingCopyDir.refresh(false, true); + myChangeListManager.ensureUpToDate(false); + final List unversionedFiles = ((ChangeListManagerImpl)myChangeListManager).getUnversionedFiles(); + final Pattern pattern = Pattern.compile("f([0-9])+\\.txt"); + int cnt = 0; + for (VirtualFile unversionedFile : unversionedFiles) { + if (VfsUtil.isAncestor(myWorkingCopyDir, unversionedFile, true)) { + ++ cnt; + Assert.assertTrue(pattern.matcher(unversionedFile.getName()).matches()); + } + } + Assert.assertEquals(100, cnt); + } + + /*@Test + public void testMoveDir() throws Exception { + final File root = new File(myClientRoot, "was"); + root.mkdir(); + for (int i = 0; i < 10; i++) { + final File dir = new File(root, "dir" + i); + dir.mkdir(); + for (int j = 0; j < 10; j++) { + final File f = new File(dir, "f" + j + ".txt"); + f.createNewFile(); + } + } + myWorkingCopyDir.refresh(false, true); + myChangeListManager.ensureUpToDate(false); + List unversionedFiles = ((ChangeListManagerImpl)myChangeListManager).getUnversionedFiles(); + final Pattern pattern = Pattern.compile("f([0-9])+\\.txt"); + final Pattern patternDir = Pattern.compile("dir([0-9])+"); + int cnt = 0; + for (VirtualFile unversionedFile : unversionedFiles) { + if (VfsUtil.isAncestor(myWorkingCopyDir, unversionedFile, true) && (! "was".equals(unversionedFile.getName()))) { + ++ cnt; + Assert.assertTrue(pattern.matcher(unversionedFile.getName()).matches() || patternDir.matcher(unversionedFile.getName()).matches()); + } + } + Assert.assertEquals(110, cnt); + final File renamedFile = new File(myClientRoot, "newName"); + FileUtil.copyDir(root, renamedFile); + FileUtil.delete(root); + + myWorkingCopyDir.refresh(false, true); + myChangeListManager.ensureUpToDate(false); + unversionedFiles = ((ChangeListManagerImpl)myChangeListManager).getUnversionedFiles(); + + cnt = 0; + for (VirtualFile unversionedFile : unversionedFiles) { + if (VfsUtil.isAncestor(myWorkingCopyDir, unversionedFile, true) && (! "newName".equals(unversionedFile.getName()))) { + ++ cnt; + Assert.assertTrue((pattern.matcher(unversionedFile.getName()).matches() || patternDir.matcher(unversionedFile.getName()).matches())); + if (unversionedFile.getPath().indexOf("newName") == -1) { + System.out.println("wrong " + unversionedFile.getPath()); + } + } + } + Assert.assertEquals(110, cnt); + }*/ + + private static class MyMockChangeProvider implements ChangeProvider { + @Override + public void doCleanup(List files) { + } + + @Override + public void getChanges(VcsDirtyScope dirtyScope, + final ChangelistBuilder builder, + ProgressIndicator progress, + ChangeListManagerGate addGate) throws VcsException { + for (FilePath path : dirtyScope.getDirtyFiles()) { + builder.processUnversionedFile(path.getVirtualFile()); + } + final Processor processor = new Processor() { + @Override + public boolean process(final VirtualFile vf) { + builder.processUnversionedFile(vf); + return true; + } + }; + for (FilePath dir : dirtyScope.getRecursivelyDirtyDirectories()) { + VfsUtil.processFilesRecursively(dir.getVirtualFile(), processor); + } + } + + @Override + public boolean isModifiedDocumentTrackingRequired() { + return false; + } + } +} diff --git a/platform/vcs-impl/testSrc/com/intellij/openapi/vcs/changes/committed/VcsEventsListenerTest.java b/platform/vcs-impl/testSrc/com/intellij/openapi/vcs/changes/committed/VcsEventsListenerTest.java new file mode 100644 index 000000000000..f24ad1fed284 --- /dev/null +++ b/platform/vcs-impl/testSrc/com/intellij/openapi/vcs/changes/committed/VcsEventsListenerTest.java @@ -0,0 +1,355 @@ +package com.intellij.openapi.vcs.changes.committed; + +import com.intellij.ide.startup.impl.StartupManagerImpl; +import com.intellij.openapi.components.ProjectComponent; +import com.intellij.openapi.options.Configurable; +import com.intellij.openapi.progress.ProcessCanceledException; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.startup.StartupManager; +import com.intellij.openapi.util.Ref; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.vcs.*; +import com.intellij.openapi.vcs.changes.Change; +import com.intellij.openapi.vcs.changes.ChangeList; +import com.intellij.openapi.vcs.changes.ChangeListManager; +import com.intellij.openapi.vcs.checkin.CheckinEnvironment; +import com.intellij.openapi.vcs.impl.ProjectLevelVcsManagerImpl; +import com.intellij.openapi.vcs.rollback.RollbackEnvironment; +import com.intellij.openapi.vcs.rollback.RollbackProgressListener; +import com.intellij.openapi.vcs.ui.RefreshableOnComponent; +import com.intellij.openapi.vcs.update.SequentialUpdatesContext; +import com.intellij.openapi.vcs.update.UpdateEnvironment; +import com.intellij.openapi.vcs.update.UpdateSession; +import com.intellij.openapi.vcs.update.UpdatedFiles; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.testFramework.UsefulTestCase; +import com.intellij.testFramework.fixtures.IdeaTestFixtureFactory; +import com.intellij.testFramework.fixtures.TempDirTestFixture; +import com.intellij.testFramework.vcs.AbstractJunitVcsTestCase; +import com.intellij.util.NullableFunction; +import com.intellij.util.PairConsumer; +import com.intellij.util.ui.UIUtil; +import junit.framework.Assert; +import org.jetbrains.annotations.NotNull; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.io.File; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Set; + +/** + * @author irengrig + * Date: 12/21/10 + * Time: 2:03 PM + */ +public class VcsEventsListenerTest extends AbstractJunitVcsTestCase { + private AbstractVcs myVcs; + private ProjectLevelVcsManagerImpl myVcsManager; + private ChangeListManager myChangeListManager; + private TempDirTestFixture myTempDirFixture; + private File myClientRoot; + + @Before + public void setUp() { + UIUtil.invokeAndWaitIfNeeded(new Runnable() { + @Override + public void run() { + try { + final IdeaTestFixtureFactory fixtureFactory = IdeaTestFixtureFactory.getFixtureFactory(); + myTempDirFixture = fixtureFactory.createTempDirTestFixture(); + myTempDirFixture.setUp(); + + myClientRoot = new File(myTempDirFixture.getTempDirPath(), "clientroot"); + myClientRoot.mkdir(); + + initProject(myClientRoot, VcsEventsListenerTest.this.getTestName()); + + ((StartupManagerImpl)StartupManager.getInstance(myProject)).runPostStartupActivities(); + + myChangeListManager = ChangeListManager.getInstance(myProject); + myVcs = VcsActiveEnvironmentsProxy.proxyVcs(new MyVcs(myProject, "mock")); + myVcsManager = (ProjectLevelVcsManagerImpl)ProjectLevelVcsManager.getInstance(myProject); + myVcsManager.registerVcs(myVcs); + myVcsManager.setDirectoryMapping(myWorkingCopyDir.getPath(), myVcs.getName()); + + ((ProjectComponent)myChangeListManager).projectOpened(); + } + catch (Exception e) { + tearDown(); + throw new RuntimeException(e); + } + } + }); + } + + @After + public void tearDown() { + UIUtil.invokeAndWaitIfNeeded(new Runnable() { + @Override + public void run() { + try { + if (myVcsManager != null && myVcs != null) { + myVcsManager.unregisterVcs(myVcs); + } + + ((ProjectComponent)myChangeListManager).projectClosed(); + tearDownProject(); + if (myTempDirFixture != null) { + myTempDirFixture.tearDown(); + myTempDirFixture = null; + } + FileUtil.delete(myClientRoot); + } + catch (Exception e) { + throw new RuntimeException(e); + } + finally { + try { + UsefulTestCase.clearFields(this); + } + catch (IllegalAccessException e) { + //noinspection ThrowFromFinallyBlock + throw new RuntimeException(e); + } + } + } + }); + } + + @Test + public void testSimpleListeningWithProxy() throws Exception { + final VcsEventsListenerManager manager = myVcsManager.getVcsEventsListenerManager(); + final List list = Arrays.asList(new VirtualFile[]{myWorkingCopyDir}); + final MyCheckinListener listener = new MyCheckinListener(list); + final Object key = manager.addCheckin(new ForwardingListener(listener)); + + myVcs.getCheckinEnvironment().scheduleUnversionedFilesForAddition(list); + + Assert.assertTrue(listener.isChecked()); + listener.assertCheckOk(); + + listener.reset(); + + manager.removeCheckin(key); + listener.reset(); + myVcs.getCheckinEnvironment().scheduleUnversionedFilesForAddition(list); + Assert.assertFalse(listener.isChecked()); + } + + @Test + public void testSimpleListeningWithoutProxy() throws Exception { + myVcsManager.setDirectoryMapping(myWorkingCopyDir.getPath(), "svn"); + + testSimpleListeningWithProxy(); + } + + private static class MyVcs extends MockAbstractVcs { + private MyVcs(Project project, String name) { + super(project, name); + } + + @Override + protected UpdateEnvironment createUpdateEnvironment() { + return new UpdateEnvironment() { + @Override + public void fillGroups(UpdatedFiles updatedFiles) { + //To change body of implemented methods use File | Settings | File Templates. + } + + @NotNull + @Override + public UpdateSession updateDirectories(@NotNull FilePath[] contentRoots, + UpdatedFiles updatedFiles, + ProgressIndicator progressIndicator, + @NotNull Ref context) throws ProcessCanceledException { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public Configurable createConfigurable(Collection files) { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public boolean validateOptions(Collection roots) { + return false; //To change body of implemented methods use File | Settings | File Templates. + } + }; + } + + @Override + protected RollbackEnvironment createRollbackEnvironment() { + return new RollbackEnvironment() { + @Override + public String getRollbackOperationName() { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public void rollbackChanges(List changes, + List vcsExceptions, + @NotNull RollbackProgressListener listener) { + //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public void rollbackMissingFileDeletion(List files, + List exceptions, + RollbackProgressListener listener) { + //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public void rollbackModifiedWithoutCheckout(List files, + List exceptions, + RollbackProgressListener listener) { + //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public void rollbackIfUnchanged(VirtualFile file) { + //To change body of implemented methods use File | Settings | File Templates. + } + }; + } + + @Override + protected CheckinEnvironment createCheckinEnvironment() { + return new CheckinEnvironment() { + @Override + public RefreshableOnComponent createAdditionalOptionsPanel(CheckinProjectPanel panel, + PairConsumer additionalDataConsumer) { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public String getDefaultMessageFor(FilePath[] filesToCheckin) { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public String getHelpId() { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public String getCheckinOperationName() { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public List commit(List changes, String preparedComment) { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public List commit(List changes, + String preparedComment, + @NotNull NullableFunction parametersHolder, Set feedback) { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public List scheduleMissingFileForDeletion(List files) { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public List scheduleUnversionedFilesForAddition(List files) { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public boolean keepChangeListAfterCommit(ChangeList changeList) { + return false; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public boolean isRefreshAfterCommitNeeded() { + return true; + } + }; + } + } + + private static class MyCheckinListener implements CheckinEnvironment { + private boolean myChecked; + private boolean myAssertOk; + private final List myCheckList; + + public MyCheckinListener(final List checkList) { + myCheckList = checkList; + myChecked = false; + myAssertOk = true; + } + + @Override + public RefreshableOnComponent createAdditionalOptionsPanel(CheckinProjectPanel panel, + PairConsumer additionalDataConsumer) { + return null; + } + @Override + public String getDefaultMessageFor(FilePath[] filesToCheckin) { + return null; + } + @Override + public String getHelpId() { + return null; + } + @Override + public String getCheckinOperationName() { + return null; + } + @Override + public List commit(List changes, String preparedComment) { + return null; + } + + @Override + public List commit(List changes, + String preparedComment, + @NotNull NullableFunction parametersHolder, Set feedback) { + return null; + } + + @Override + public List scheduleMissingFileForDeletion(List files) { + return null; + } + + @Override + public List scheduleUnversionedFilesForAddition(List files) { + myChecked = true; + myAssertOk = myCheckList.equals(files); + return null; + } + + @Override + public boolean keepChangeListAfterCommit(ChangeList changeList) { + return false; + } + + @Override + public boolean isRefreshAfterCommitNeeded() { + return true; + } + + public boolean isChecked() { + return myChecked; + } + + public void assertCheckOk() { + Assert.assertTrue(myAssertOk); + } + + public void reset() { + myAssertOk = true; + myChecked = false; + } + } +} diff --git a/platform/testFramework/testSrc/com/intellij/testFramework/vcs/AbstractJunitVcsTestCase.java b/platform/vcs-impl/testSrc/com/intellij/testFramework/vcs/AbstractJunitVcsTestCase.java similarity index 100% rename from platform/testFramework/testSrc/com/intellij/testFramework/vcs/AbstractJunitVcsTestCase.java rename to platform/vcs-impl/testSrc/com/intellij/testFramework/vcs/AbstractJunitVcsTestCase.java diff --git a/platform/testFramework/testSrc/com/intellij/testFramework/vcs/AbstractVcsTestCase.java b/platform/vcs-impl/testSrc/com/intellij/testFramework/vcs/AbstractVcsTestCase.java similarity index 100% rename from platform/testFramework/testSrc/com/intellij/testFramework/vcs/AbstractVcsTestCase.java rename to platform/vcs-impl/testSrc/com/intellij/testFramework/vcs/AbstractVcsTestCase.java From f4e1e4e5e98504ccb028573975cd2bbf30ce3339 Mon Sep 17 00:00:00 2001 From: "Maxim.Mossienko" Date: Fri, 8 May 2015 15:26:59 +0200 Subject: [PATCH 17/28] no dependency for util except classloader classes --- .../util/src/com/intellij/util/lang/JarMemoryLoader.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/platform/util/src/com/intellij/util/lang/JarMemoryLoader.java b/platform/util/src/com/intellij/util/lang/JarMemoryLoader.java index f012f1fc6526..497db669ba7f 100644 --- a/platform/util/src/com/intellij/util/lang/JarMemoryLoader.java +++ b/platform/util/src/com/intellij/util/lang/JarMemoryLoader.java @@ -15,9 +15,7 @@ */ package com.intellij.util.lang; -import com.intellij.openapi.util.io.FileUtil; -import com.intellij.util.containers.hash.HashMap; -import com.intellij.util.io.zip.ZipShort; +import com.intellij.openapi.util.io.FileUtilRt; import org.jetbrains.annotations.Nullable; import sun.misc.Resource; @@ -26,6 +24,7 @@ import java.io.IOException; import java.net.URL; import java.util.Collections; import java.util.Enumeration; +import java.util.HashMap; import java.util.Map; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; @@ -64,8 +63,8 @@ public class JarMemoryLoader { ZipEntry sizeEntry = entries.nextElement(); if (sizeEntry == null || !sizeEntry.getName().equals(SIZE_ENTRY)) return null; - byte[] bytes = FileUtil.loadBytes(zipFile.getInputStream(sizeEntry), 2); - int size = ZipShort.getValue(bytes); + byte[] bytes = FileUtilRt.loadBytes(zipFile.getInputStream(sizeEntry), 2); + int size = ((bytes[1] & 0xFF) << 8) + (bytes[0] & 0xFF); JarMemoryLoader loader = new JarMemoryLoader(); for (int i = 0; i < size && entries.hasMoreElements(); i++) { From 9e3c44b2fec06eb8d08795059b8d4f3f8641ac3f Mon Sep 17 00:00:00 2001 From: "Maxim.Mossienko" Date: Fri, 8 May 2015 15:50:12 +0200 Subject: [PATCH 18/28] [optimization] we need to use cached ZipFile's in UrlClassLoader that is switched on when canLockJar = true --- .../src/com/intellij/ide/BootstrapClassLoaderUtil.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/bootstrap/src/com/intellij/ide/BootstrapClassLoaderUtil.java b/platform/bootstrap/src/com/intellij/ide/BootstrapClassLoaderUtil.java index 6e313990e112..9dab9c62b3a3 100644 --- a/platform/bootstrap/src/com/intellij/ide/BootstrapClassLoaderUtil.java +++ b/platform/bootstrap/src/com/intellij/ide/BootstrapClassLoaderUtil.java @@ -58,7 +58,7 @@ public class BootstrapClassLoaderUtil extends ClassUtilCore { UrlClassLoader.Builder builder = UrlClassLoader.build() .urls(filterClassPath(new ArrayList(classpath))) - .allowLock(false) + .allowLock() .usePersistentClasspathIndexForLocalClassDirectories() .useCache(); if (Boolean.valueOf(System.getProperty(PROPERTY_ALLOW_BOOTSTRAP_RESOURCES, "true"))) { From cd114b4490f6bd17b03b7e4e736bd87977fcad40 Mon Sep 17 00:00:00 2001 From: "Maxim.Mossienko" Date: Fri, 8 May 2015 16:54:37 +0200 Subject: [PATCH 19/28] [performance] SoftReference for ZipFile is back, also decreased dependencies slightly --- .../src/com/intellij/util/lang/JarLoader.java | 75 ++++++++++++++----- 1 file changed, 57 insertions(+), 18 deletions(-) diff --git a/platform/util/src/com/intellij/util/lang/JarLoader.java b/platform/util/src/com/intellij/util/lang/JarLoader.java index 68c0bfb5479e..17b8181c619b 100644 --- a/platform/util/src/com/intellij/util/lang/JarLoader.java +++ b/platform/util/src/com/intellij/util/lang/JarLoader.java @@ -17,11 +17,11 @@ package com.intellij.util.lang; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.io.FileUtil; -import com.intellij.util.io.URLUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import sun.misc.Resource; +import java.io.File; import java.io.IOException; import java.lang.ref.SoftReference; import java.net.URL; @@ -30,15 +30,18 @@ import java.util.zip.ZipEntry; import java.util.zip.ZipFile; class JarLoader extends Loader { - private final URL myURL; + private final File myCanonicalFile; + private final boolean myCanLockJar; // true implies that the zipfile will not be modified in the lifetime of the JarLoader private SoftReference myMemoryLoader; + private volatile SoftReference myZipFileSoftReference; // Used only when myCanLockJar==true - // todo drop unused parameter JarLoader(URL url, @SuppressWarnings("unused") boolean canLockJar, int index, boolean preloadJarContents) throws IOException { - super(new URL(URLUtil.JAR_PROTOCOL, "", -1, url + "!/"), index); - myURL = url; + super(new URL("jar", "", -1, url + "!/"), index); - ZipFile zipFile = new ZipFile(getFileUrl()); + myCanonicalFile = new File(FileUtil.unquote(url.getFile())).getCanonicalFile(); + myCanLockJar = canLockJar; + + ZipFile zipFile = getZipFile(); // IOException from opening is propagated to caller if zip file isn't valid, try { if (preloadJarContents) { JarMemoryLoader loader = JarMemoryLoader.load(zipFile, getBaseURL()); @@ -48,18 +51,14 @@ class JarLoader extends Loader { } } finally { - zipFile.close(); + releaseZipFile(zipFile); } } - private String getFileUrl() throws IOException { - return FileUtil.unquote(myURL.getFile()); - } - @NotNull @Override public ClasspathCache.LoaderData buildData() throws IOException { - ZipFile zipFile = new ZipFile(getFileUrl()); + ZipFile zipFile = getZipFile(); try { ClasspathCache.LoaderData loaderData = new ClasspathCache.LoaderData(); Enumeration entries = zipFile.entries(); @@ -72,21 +71,21 @@ class JarLoader extends Loader { return loaderData; } finally { - zipFile.close(); + releaseZipFile(zipFile); } } @Override @Nullable Resource getResource(String name, boolean flag) { - JarMemoryLoader loader = com.intellij.reference.SoftReference.dereference(myMemoryLoader); + JarMemoryLoader loader = myMemoryLoader != null? myMemoryLoader.get() : null; if (loader != null) { Resource resource = loader.getResource(name); if (resource != null) return resource; } try { - ZipFile zipFile = new ZipFile(getFileUrl()); + ZipFile zipFile = getZipFile(); try { ZipEntry entry = zipFile.getEntry(name); if (entry != null) { @@ -94,18 +93,58 @@ class JarLoader extends Loader { } } finally { - zipFile.close(); + releaseZipFile(zipFile); } } catch (Exception e) { - Logger.getInstance(JarLoader.class).error("url: " + myURL, e); + error("file: " + myCanonicalFile, e); } return null; } + protected void error(String message, Throwable t) { + //Logger.getLogger(JarLoader.class.getName()).log(Level.SEVERE, message, t); + Logger.getInstance(JarLoader.class).error(message, t); + } + + private void releaseZipFile(ZipFile zipFile) throws IOException { + // Closing of zip file when myCanLockJar=true happens in ZipFile.finalize + if (!myCanLockJar) { + zipFile.close(); + } + } + + @NotNull + private ZipFile getZipFile() throws IOException { + // This code is executed at least 100K times (O(number of classes needed to load)) and it takes considerable time to open ZipFile's + // such number of times so we store reference to ZipFile if we allowed to lock the file (assume it isn't changed) + if (myCanLockJar) { + SoftReference zipFileSoftReference = myZipFileSoftReference; + if(zipFileSoftReference != null) { + ZipFile existingZipFile = zipFileSoftReference.get(); + if (existingZipFile != null) return existingZipFile; + } + synchronized (ourLock) { + zipFileSoftReference = myZipFileSoftReference; + if(zipFileSoftReference != null) { + ZipFile existingZipFile = zipFileSoftReference.get(); + if (existingZipFile != null) return existingZipFile; + } + // ZipFile's native implementation (ZipFile.c, zip_util.c) has path -> file descriptor cache + ZipFile zipFile = new ZipFile(myCanonicalFile); + myZipFileSoftReference = new SoftReference(zipFile); + return zipFile; + } + } else { + return new ZipFile(myCanonicalFile); + } + } + @Override public String toString() { - return "JarLoader [" + myURL + "]"; + return "JarLoader [" + myCanonicalFile + "]"; } + + private static final Object ourLock = new Object(); } From 6a2a3c2ee934e39cb7470528654572fc4bb2a2d1 Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Fri, 8 May 2015 18:12:28 +0300 Subject: [PATCH 20/28] Enable resolving to the roots of the modules for Scratch files (PY-15818) Scratch files are now located outside of the project, so foothold PSI elements in them don't allow us to find their modules. For Scratch files we explicitly add the ability to resolve to any of the modules of the project, as we did earlier for the console runner. --- python/src/com/jetbrains/python/psi/PyUtil.java | 7 +++++++ .../python/psi/resolve/QualifiedNameResolverImpl.java | 3 ++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/python/src/com/jetbrains/python/psi/PyUtil.java b/python/src/com/jetbrains/python/psi/PyUtil.java index 4d104f1c2629..dbe5c44d6bfe 100644 --- a/python/src/com/jetbrains/python/psi/PyUtil.java +++ b/python/src/com/jetbrains/python/psi/PyUtil.java @@ -23,6 +23,7 @@ import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.codeInsight.lookup.LookupElementBuilder; import com.intellij.ide.fileTemplates.FileTemplate; import com.intellij.ide.fileTemplates.FileTemplateManager; +import com.intellij.ide.scratch.ScratchFileService; import com.intellij.injected.editor.VirtualFileWindow; import com.intellij.lang.ASTFactory; import com.intellij.lang.ASTNode; @@ -1803,6 +1804,12 @@ public class PyUtil { return type == builtinCache.getObjectType() || type == builtinCache.getOldstyleClassobjType(); } + public static boolean isInScratchFile(@NotNull PsiElement element) { + final ScratchFileService service = ScratchFileService.getInstance(); + final PsiFile file = element.getContainingFile(); + return service != null && file != null && service.getRootType(file.getVirtualFile()) != null; + } + /** * This helper class allows to collect various information about AST nodes composing {@link PyStringLiteralExpression}. */ diff --git a/python/src/com/jetbrains/python/psi/resolve/QualifiedNameResolverImpl.java b/python/src/com/jetbrains/python/psi/resolve/QualifiedNameResolverImpl.java index b073db8c6955..e0ce6f2dfc1a 100644 --- a/python/src/com/jetbrains/python/psi/resolve/QualifiedNameResolverImpl.java +++ b/python/src/com/jetbrains/python/psi/resolve/QualifiedNameResolverImpl.java @@ -37,6 +37,7 @@ import com.jetbrains.python.codeInsight.userSkeletons.PyUserSkeletonsUtil; import com.jetbrains.python.console.PydevConsoleRunner; import com.jetbrains.python.facet.PythonPathContributingFacet; import com.jetbrains.python.psi.PyFile; +import com.jetbrains.python.psi.PyUtil; import com.jetbrains.python.psi.impl.PyBuiltinCache; import com.jetbrains.python.psi.impl.PyImportResolver; import com.jetbrains.python.sdk.PySdkUtil; @@ -84,7 +85,7 @@ public class QualifiedNameResolverImpl implements RootVisitor, QualifiedNameReso @Override public QualifiedNameResolver fromElement(@NotNull PsiElement foothold) { myContext.setFromElement(foothold); - if (PydevConsoleRunner.isInPydevConsole(foothold)) { + if (PydevConsoleRunner.isInPydevConsole(foothold) || PyUtil.isInScratchFile(foothold)) { withAllModules(); Sdk sdk = PydevConsoleRunner.getConsoleSdk(foothold); if (sdk != null) { From b1168f8d3e656c8a28ceff905926a66c371abc60 Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Fri, 8 May 2015 18:17:00 +0300 Subject: [PATCH 21/28] Fixed NPE --- python/src/com/jetbrains/python/psi/PyUtil.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/python/src/com/jetbrains/python/psi/PyUtil.java b/python/src/com/jetbrains/python/psi/PyUtil.java index dbe5c44d6bfe..5ec8c8d0e16b 100644 --- a/python/src/com/jetbrains/python/psi/PyUtil.java +++ b/python/src/com/jetbrains/python/psi/PyUtil.java @@ -1807,7 +1807,11 @@ public class PyUtil { public static boolean isInScratchFile(@NotNull PsiElement element) { final ScratchFileService service = ScratchFileService.getInstance(); final PsiFile file = element.getContainingFile(); - return service != null && file != null && service.getRootType(file.getVirtualFile()) != null; + if (file != null) { + final VirtualFile virtualFile = file.getVirtualFile(); + return service != null && virtualFile != null && service.getRootType(virtualFile) != null; + } + return false; } /** From cc077fce25b0978cd440650dcda58811881842c1 Mon Sep 17 00:00:00 2001 From: Bas Leijdekkers Date: Fri, 8 May 2015 11:52:35 +0200 Subject: [PATCH 22/28] IDEA-140082 (Garbage text in "Unused declaration" batch inspection result) --- .../src/com/intellij/codeInspection/ex/HTMLComposerImpl.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/platform/analysis-impl/src/com/intellij/codeInspection/ex/HTMLComposerImpl.java b/platform/analysis-impl/src/com/intellij/codeInspection/ex/HTMLComposerImpl.java index c8a8361b020b..a00ffe6c7f4d 100644 --- a/platform/analysis-impl/src/com/intellij/codeInspection/ex/HTMLComposerImpl.java +++ b/platform/analysis-impl/src/com/intellij/codeInspection/ex/HTMLComposerImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -286,7 +286,6 @@ public abstract class HTMLComposerImpl extends HTMLComposer { @Override public void appendListItem(StringBuffer buf, RefElement refElement) { startListItem(buf); - buf.append(CLOSE_TAG); appendElementReference(buf, refElement, true); appendAdditionalListItemInfo(buf, refElement); doneListItem(buf); From df63f2a5aca780ede0f3501941f7816efbe047ac Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Fri, 8 May 2015 18:11:46 +0300 Subject: [PATCH 23/28] [git] IDEA-91996 Guess repository if there are no virtual files in the scope --- .../src/git4idea/actions/GitRepositoryAction.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/plugins/git4idea/src/git4idea/actions/GitRepositoryAction.java b/plugins/git4idea/src/git4idea/actions/GitRepositoryAction.java index 50ba95a046fb..68d5aba3f6e0 100644 --- a/plugins/git4idea/src/git4idea/actions/GitRepositoryAction.java +++ b/plugins/git4idea/src/git4idea/actions/GitRepositoryAction.java @@ -15,6 +15,7 @@ */ package git4idea.actions; +import com.intellij.dvcs.repo.Repository; import com.intellij.openapi.actionSystem.ActionPlaces; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.CommonDataKeys; @@ -114,19 +115,18 @@ public abstract class GitRepositoryAction extends DumbAwareAction { if (files != null) { for (VirtualFile file : files) { GitRepositoryManager manager = GitUtil.getRepositoryManager(project); - if (manager == null) { - return false; - } - final GitRepository repositoryForFile = manager.getRepositoryForFile(file); - if (repositoryForFile != null && repositoryForFile.getState() == GitRepository.State.REBASING) { - return true; - } + if (isRebasing(manager.getRepositoryForFile(file))) return true; } } + if (isRebasing(GitBranchUtil.getCurrentRepository(project))) return true; } return false; } + private static boolean isRebasing(@Nullable GitRepository repository) { + return repository != null && repository.getState() == Repository.State.REBASING; + } + /** * Get git roots for the project. The method shows dialogs in the case when roots cannot be retrieved, so it should be called * from the event dispatch thread. From 12ae1f7872ed34d36fdf111b2b0d8dd228c3b204 Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Fri, 8 May 2015 17:24:55 +0200 Subject: [PATCH 24/28] java sm locator: accept vm class names (IDEA-105556) --- .../testframework/JavaTestLocator.java | 30 ++++++++----------- .../junit/JUnitRerunFailedTestsTest.java | 19 ++++++++++++ 2 files changed, 32 insertions(+), 17 deletions(-) diff --git a/java/execution/impl/src/com/intellij/execution/testframework/JavaTestLocator.java b/java/execution/impl/src/com/intellij/execution/testframework/JavaTestLocator.java index d4e529772a72..ac4c581f15e9 100644 --- a/java/execution/impl/src/com/intellij/execution/testframework/JavaTestLocator.java +++ b/java/execution/impl/src/com/intellij/execution/testframework/JavaTestLocator.java @@ -22,10 +22,11 @@ import com.intellij.execution.junit2.info.MethodLocation; import com.intellij.execution.testframework.sm.runner.SMTestLocator; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.text.StringUtil; -import com.intellij.psi.JavaPsiFacade; import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiManager; import com.intellij.psi.PsiMethod; import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.util.ClassUtil; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; @@ -50,32 +51,27 @@ public class JavaTestLocator implements SMTestLocator { path = path.substring(0, idx); } - JavaPsiFacade javaPsiFacade = JavaPsiFacade.getInstance(project); if (SUITE_PROTOCOL.equals(protocol)) { path = StringUtil.trimEnd(path, "."); - PsiClass[] classes = javaPsiFacade.findClasses(path, scope); - if (classes.length > 0) { + PsiClass aClass = ClassUtil.findPsiClass(PsiManager.getInstance(project), path, null, true, scope); + if (aClass != null) { results = ContainerUtil.newSmartList(); - for (PsiClass aClass : classes) { - results.add(paramName != null ? PsiMemberParameterizedLocation.getParameterizedLocation(aClass, paramName) - : new PsiLocation(project, aClass)); - } + results.add(paramName != null ? PsiMemberParameterizedLocation.getParameterizedLocation(aClass, paramName) + : new PsiLocation(project, aClass)); } } else if (TEST_PROTOCOL.equals(protocol)) { String className = StringUtil.getPackageName(path); if (!StringUtil.isEmpty(className)) { String methodName = StringUtil.getShortName(path); - PsiClass[] classes = javaPsiFacade.findClasses(className, scope); - if (classes.length > 0) { + PsiClass aClass = ClassUtil.findPsiClass(PsiManager.getInstance(project), className, null, true, scope); + if (aClass != null) { results = ContainerUtil.newSmartList(); - for (PsiClass aClass : classes) { - PsiMethod[] methods = aClass.findMethodsByName(methodName, true); - if (methods.length > 0) { - for (PsiMethod method : methods) { - results.add(paramName != null ? new PsiMemberParameterizedLocation(project, method, aClass, paramName) - : MethodLocation.elementInClass(method, aClass)); - } + PsiMethod[] methods = aClass.findMethodsByName(methodName, true); + if (methods.length > 0) { + for (PsiMethod method : methods) { + results.add(paramName != null ? new PsiMemberParameterizedLocation(project, method, aClass, paramName) + : MethodLocation.elementInClass(method, aClass)); } } } diff --git a/plugins/junit/test/com/intellij/execution/junit/JUnitRerunFailedTestsTest.java b/plugins/junit/test/com/intellij/execution/junit/JUnitRerunFailedTestsTest.java index f0273b478608..4a270259d8c0 100644 --- a/plugins/junit/test/com/intellij/execution/junit/JUnitRerunFailedTestsTest.java +++ b/plugins/junit/test/com/intellij/execution/junit/JUnitRerunFailedTestsTest.java @@ -130,4 +130,23 @@ public class JUnitRerunFailedTestsTest extends LightCodeInsightFixtureTestCase }); assertNull(TestMethods.getTestPresentation(testProxy, project, searchScope)); } + + public void testInnerClass() throws Exception { + myFixture.addClass("public class TestClass {\n" + + " public static class Tests extends junit.framework.TestCase {\n" + + " public void testFoo() throws Exception {}\n" + + " }\n" + + "}"); + + final SMTestProxy testProxy = new SMTestProxy("testFoo", false, "java:test://TestClass$Tests.testFoo"); + final Project project = getProject(); + final GlobalSearchScope searchScope = GlobalSearchScope.projectScope(project); + testProxy.setLocator(JavaTestLocator.INSTANCE); + Location location = testProxy.getLocation(project, searchScope); + assertNotNull(location); + PsiElement element = location.getPsiElement(); + assertTrue(element instanceof PsiMethod); + String name = ((PsiMethod)element).getName(); + assertEquals(name, "testFoo"); + } } From 3346704a9b287e121e692220c6ecb70ebdac9864 Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Fri, 8 May 2015 18:22:34 +0200 Subject: [PATCH 25/28] junit sm runner: ensure testFailed even if getMessage() throws an exception --- .../testFramework/CompositeException.java | 3 ++- .../src/com/intellij/junit4/SMTestSender.java | 21 ++++++++++++++----- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/platform/testFramework/src/com/intellij/testFramework/CompositeException.java b/platform/testFramework/src/com/intellij/testFramework/CompositeException.java index 59e73bf718d4..4ec1c8b21107 100644 --- a/platform/testFramework/src/com/intellij/testFramework/CompositeException.java +++ b/platform/testFramework/src/com/intellij/testFramework/CompositeException.java @@ -130,7 +130,8 @@ public class CompositeException extends Exception { sb.append(line); line = exceptionProcessor.fun(exception); - if (!line.endsWith("\n")) line += '\n'; + if (line == null) line = "null\n"; + else if (!line.endsWith("\n")) line += '\n'; stringProcessor.process(line); sb.append(line); } diff --git a/plugins/junit_rt/src/com/intellij/junit4/SMTestSender.java b/plugins/junit_rt/src/com/intellij/junit4/SMTestSender.java index 0523dd50800b..b835081a92b3 100644 --- a/plugins/junit_rt/src/com/intellij/junit4/SMTestSender.java +++ b/plugins/junit_rt/src/com/intellij/junit4/SMTestSender.java @@ -32,6 +32,8 @@ import org.junit.runner.notification.Failure; import org.junit.runner.notification.RunListener; import java.io.PrintStream; +import java.io.PrintWriter; +import java.io.StringWriter; import java.util.*; public class SMTestSender extends RunListener { @@ -128,13 +130,22 @@ public class SMTestSender extends RunListener { } public void testFailure(Failure failure) throws Exception { - final String failureMessage = failure.getMessage(); - final String trace = failure.getTrace(); final Map attrs = new HashMap(); attrs.put("name", JUnit4ReflectionUtil.getMethodName(failure.getDescription())); - final ComparisonFailureData notification = createExceptionNotification(failure.getException()); - ComparisonFailureData.registerSMAttributes(notification, trace, failureMessage, attrs); - myPrintStream.println(ServiceMessage.asString(ServiceMessageTypes.TEST_FAILED, attrs)); + try { + final String trace = failure.getTrace(); + final ComparisonFailureData notification = createExceptionNotification(failure.getException()); + ComparisonFailureData.registerSMAttributes(notification, trace, failure.getMessage(), attrs); + } + catch (Throwable e) { + final StringWriter stringWriter = new StringWriter(); + final PrintWriter writer = new PrintWriter(stringWriter); + e.printStackTrace(writer); + ComparisonFailureData.registerSMAttributes(null, stringWriter.toString(), e.getMessage(), attrs); + } + finally { + myPrintStream.println(ServiceMessage.asString(ServiceMessageTypes.TEST_FAILED, attrs)); + } } public void testAssumptionFailure(Failure failure) { From c3d95ce9eda1a554f6ab2723f5defc676f3baf8d Mon Sep 17 00:00:00 2001 From: Aleksey Pivovarov Date: Tue, 10 Feb 2015 17:33:43 +0300 Subject: [PATCH 26/28] diff: split complex queue into two classes --- .../impl/CacheDiffRequestChainProcessor.java | 19 +-- .../diff/impl/DiffRequestProcessor.java | 1 + .../diff/tools/util/base/DiffViewerBase.java | 26 ++-- ...kExecutor.java => BackgroundTaskUtil.java} | 66 ++++------ .../com/intellij/diff/util/DiffTaskQueue.java | 116 ++++++++++++++++++ .../vcs/changes/CacheChangeProcessor.java | 21 ++-- 6 files changed, 174 insertions(+), 75 deletions(-) rename platform/diff-impl/src/com/intellij/diff/util/{WaitingBackgroundableTaskExecutor.java => BackgroundTaskUtil.java} (63%) create mode 100644 platform/diff-impl/src/com/intellij/diff/util/DiffTaskQueue.java diff --git a/platform/diff-impl/src/com/intellij/diff/impl/CacheDiffRequestChainProcessor.java b/platform/diff-impl/src/com/intellij/diff/impl/CacheDiffRequestChainProcessor.java index 49a400022a99..63aabc7bf446 100644 --- a/platform/diff-impl/src/com/intellij/diff/impl/CacheDiffRequestChainProcessor.java +++ b/platform/diff-impl/src/com/intellij/diff/impl/CacheDiffRequestChainProcessor.java @@ -21,9 +21,9 @@ import com.intellij.diff.chains.DiffRequestProducer; import com.intellij.diff.chains.DiffRequestProducerException; import com.intellij.diff.requests.*; import com.intellij.diff.tools.util.SoftHardCacheMap; +import com.intellij.diff.util.DiffTaskQueue; import com.intellij.diff.util.DiffUserDataKeys; import com.intellij.diff.util.DiffUserDataKeysEx.ScrollToPolicy; -import com.intellij.diff.util.WaitingBackgroundableTaskExecutor; import com.intellij.icons.AllIcons; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; @@ -33,9 +33,11 @@ import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.util.ProgressWindow; import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Disposer; import com.intellij.util.Consumer; +import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtil; -import com.intellij.util.containers.Convertor; +import org.jetbrains.annotations.CalledInAwt; import org.jetbrains.annotations.CalledInBackground; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -51,7 +53,7 @@ public abstract class CacheDiffRequestChainProcessor extends DiffRequestProcesso @NotNull private final SoftHardCacheMap myRequestCache = new SoftHardCacheMap(5, 5); - @NotNull private final WaitingBackgroundableTaskExecutor myTaskExecutor = new WaitingBackgroundableTaskExecutor(); + @NotNull private final DiffTaskQueue myQueue = new DiffTaskQueue(); public CacheDiffRequestChainProcessor(@Nullable Project project, @NotNull DiffRequestChain requestChain) { super(project, requestChain); @@ -62,6 +64,7 @@ public abstract class CacheDiffRequestChainProcessor extends DiffRequestProcesso // Update // + @CalledInAwt public void updateRequest(final boolean force, @Nullable final ScrollToPolicy scrollToChangePolicy) { List requests = myRequestChain.getRequests(); int index = myRequestChain.getIndex(); @@ -78,12 +81,13 @@ public abstract class CacheDiffRequestChainProcessor extends DiffRequestProcesso return; } - myTaskExecutor.execute( - new Convertor() { + myQueue.executeAndTryWait( + new Function() { @Override - public Runnable convert(ProgressIndicator indicator) { + public Runnable fun(ProgressIndicator indicator) { final DiffRequest request = loadRequest(producer, indicator); return new Runnable() { + @CalledInAwt @Override public void run() { myRequestCache.put(producer, request); @@ -131,9 +135,10 @@ public abstract class CacheDiffRequestChainProcessor extends DiffRequestProcesso // @Override + @CalledInAwt protected void onDispose() { super.onDispose(); - myTaskExecutor.abort(); + Disposer.dispose(myQueue); myRequestCache.clear(); } diff --git a/platform/diff-impl/src/com/intellij/diff/impl/DiffRequestProcessor.java b/platform/diff-impl/src/com/intellij/diff/impl/DiffRequestProcessor.java index 2b6fbcb3a798..74674c4f83ab 100644 --- a/platform/diff-impl/src/com/intellij/diff/impl/DiffRequestProcessor.java +++ b/platform/diff-impl/src/com/intellij/diff/impl/DiffRequestProcessor.java @@ -299,6 +299,7 @@ public abstract class DiffRequestProcessor implements Disposable { protected void onAfterNavigate() { } + @CalledInAwt protected void onDispose() { } diff --git a/platform/diff-impl/src/com/intellij/diff/tools/util/base/DiffViewerBase.java b/platform/diff-impl/src/com/intellij/diff/tools/util/base/DiffViewerBase.java index 16dd6a0fd80c..8e03481e1c7a 100644 --- a/platform/diff-impl/src/com/intellij/diff/tools/util/base/DiffViewerBase.java +++ b/platform/diff-impl/src/com/intellij/diff/tools/util/base/DiffViewerBase.java @@ -20,7 +20,7 @@ import com.intellij.diff.FrameDiffTool; import com.intellij.diff.FrameDiffTool.DiffViewer; import com.intellij.diff.requests.ContentDiffRequest; import com.intellij.diff.tools.util.DiffDataKeys; -import com.intellij.diff.util.WaitingBackgroundableTaskExecutor; +import com.intellij.diff.util.DiffTaskQueue; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.CommonDataKeys; import com.intellij.openapi.actionSystem.DataProvider; @@ -31,8 +31,7 @@ import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.util.ProgressWindow; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Disposer; -import com.intellij.util.Alarm; -import com.intellij.util.containers.Convertor; +import com.intellij.util.Function; import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.*; @@ -47,9 +46,7 @@ public abstract class DiffViewerBase implements DiffViewer, DataProvider { @NotNull protected final DiffContext myContext; @NotNull protected final ContentDiffRequest myRequest; - @NotNull private final WaitingBackgroundableTaskExecutor myTaskExecutor = new WaitingBackgroundableTaskExecutor(); - @NotNull private final Alarm myAlarm = new Alarm(); - + @NotNull private final DiffTaskQueue myTaskExecutor = new DiffTaskQueue(); @NotNull private final AtomicBoolean myDisposed = new AtomicBoolean(false); public DiffViewerBase(@NotNull DiffContext context, @NotNull ContentDiffRequest request) { @@ -75,15 +72,11 @@ public abstract class DiffViewerBase implements DiffViewer, DataProvider { public final void dispose() { if (!myDisposed.compareAndSet(false, true)) return; - Disposer.dispose(myAlarm); - abortRediff(); - onDispose(); UIUtil.invokeLaterIfNeeded(new Runnable() { @Override public void run() { - abortRediff(); onDisposeAwt(); } }); @@ -92,14 +85,10 @@ public abstract class DiffViewerBase implements DiffViewer, DataProvider { @CalledInAwt public final void scheduleRediff() { if (myDisposed.get()) return; - myTaskExecutor.abort(); - final int modificationStamp = myTaskExecutor.getModificationStamp(); - myAlarm.cancelAllRequests(); - myAlarm.addRequest(new Runnable() { + myTaskExecutor.abortAndSchedule(new Runnable() { @Override public void run() { - if (modificationStamp != myTaskExecutor.getModificationStamp()) return; rediff(); } }, ProgressWindow.DEFAULT_PROGRESS_DIALOG_POSTPONE_TIME_MILLIS); @@ -127,10 +116,10 @@ public abstract class DiffViewerBase implements DiffViewer, DataProvider { int waitMillis = trySync || tryRediffSynchronously() ? ProgressWindow.DEFAULT_PROGRESS_DIALOG_POSTPONE_TIME_MILLIS : 0; - myTaskExecutor.execute( - new Convertor() { + myTaskExecutor.executeAndTryWait( + new Function() { @Override - public Runnable convert(ProgressIndicator indicator) { + public Runnable fun(ProgressIndicator indicator) { return performRediff(indicator); } }, @@ -199,6 +188,7 @@ public abstract class DiffViewerBase implements DiffViewer, DataProvider { protected abstract Runnable performRediff(@NotNull ProgressIndicator indicator); protected void onDispose() { + Disposer.dispose(myTaskExecutor); } @CalledInAwt diff --git a/platform/diff-impl/src/com/intellij/diff/util/WaitingBackgroundableTaskExecutor.java b/platform/diff-impl/src/com/intellij/diff/util/BackgroundTaskUtil.java similarity index 63% rename from platform/diff-impl/src/com/intellij/diff/util/WaitingBackgroundableTaskExecutor.java rename to platform/diff-impl/src/com/intellij/diff/util/BackgroundTaskUtil.java index 3a95b5c60b9f..9cf267a06075 100644 --- a/platform/diff-impl/src/com/intellij/diff/util/WaitingBackgroundableTaskExecutor.java +++ b/platform/diff-impl/src/com/intellij/diff/util/BackgroundTaskUtil.java @@ -21,7 +21,7 @@ import com.intellij.openapi.progress.EmptyProgressIndicator; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.util.EmptyRunnable; -import com.intellij.util.containers.Convertor; +import com.intellij.util.Function; import org.jetbrains.annotations.CalledInAwt; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -34,59 +34,38 @@ import java.util.concurrent.atomic.AtomicReference; * Executor to perform long operations on pooled thread * Is is used to reduce blinking, in case of fast end of background task. */ -public class WaitingBackgroundableTaskExecutor { +public class BackgroundTaskUtil { private static final Runnable TOO_SLOW_OPERATION = new EmptyRunnable(); - private int myModificationStamp = 0; - @Nullable private ProgressIndicator myProgressIndicator; - @CalledInAwt - public int getModificationStamp() { - return myModificationStamp; + @NotNull + public static ProgressIndicator executeAndTryWait(@NotNull final Function backgroundTask, + @Nullable final Runnable onSlowAction, + final int waitMillis) { + return executeAndTryWait(backgroundTask, onSlowAction, waitMillis, false); } @CalledInAwt - public void abort() { - if (myProgressIndicator != null) { - myProgressIndicator.cancel(); - myProgressIndicator = null; - myModificationStamp++; - } - } - - @CalledInAwt - public void execute(@NotNull final Convertor backgroundTask, - @Nullable final Runnable onSlowAction, - final int waitMillis) { - execute(backgroundTask, onSlowAction, waitMillis, false); - } - - @CalledInAwt - public void execute(@NotNull final Convertor backgroundTask, - @Nullable final Runnable onSlowAction, - final int waitMillis, - final boolean forceEDT) { - abort(); - - myModificationStamp++; - final int modificationStamp = myModificationStamp; - + @NotNull + public static ProgressIndicator executeAndTryWait(@NotNull final Function backgroundTask, + @Nullable final Runnable onSlowAction, + final int waitMillis, + final boolean forceEDT) { final ModalityState modality = ModalityState.current(); - myProgressIndicator = new EmptyProgressIndicator() { + final ProgressIndicator indicator = new EmptyProgressIndicator() { @NotNull @Override public ModalityState getModalityState() { return modality; } }; - final ProgressIndicator indicator = myProgressIndicator; final Semaphore semaphore = new Semaphore(0); final AtomicReference resultRef = new AtomicReference(); if (forceEDT) { - Runnable result = backgroundTask.convert(indicator); - finish(result, modificationStamp, indicator); + Runnable callback = backgroundTask.fun(indicator); + finish(callback, indicator); } else { ApplicationManager.getApplication().executeOnPooledThread(new Runnable() { @@ -95,18 +74,18 @@ public class WaitingBackgroundableTaskExecutor { ProgressManager.getInstance().executeProcessUnderProgress(new Runnable() { @Override public void run() { - final Runnable result = backgroundTask.convert(indicator); + final Runnable callback = backgroundTask.fun(indicator); if (indicator.isCanceled()) { semaphore.release(); return; } - if (!resultRef.compareAndSet(null, result)) { + if (!resultRef.compareAndSet(null, callback)) { ApplicationManager.getApplication().invokeLater(new Runnable() { @Override public void run() { - finish(result, modificationStamp, indicator); + finish(callback, indicator); } }, modality); } @@ -123,19 +102,20 @@ public class WaitingBackgroundableTaskExecutor { } if (!resultRef.compareAndSet(null, TOO_SLOW_OPERATION)) { // update presentation in the same thread to reduce blinking, caused by 'invokeLater' and fast background operation - finish(resultRef.get(), modificationStamp, indicator); + finish(resultRef.get(), indicator); } else { if (onSlowAction != null) onSlowAction.run(); } } + + return indicator; } @CalledInAwt - private void finish(@NotNull Runnable result, int modificationStamp, @NotNull ProgressIndicator indicator) { + private static void finish(@NotNull Runnable result, @NotNull ProgressIndicator indicator) { if (indicator.isCanceled()) return; - if (myModificationStamp != modificationStamp) return; - result.run(); + indicator.stop(); } } diff --git a/platform/diff-impl/src/com/intellij/diff/util/DiffTaskQueue.java b/platform/diff-impl/src/com/intellij/diff/util/DiffTaskQueue.java new file mode 100644 index 000000000000..96027e2a7356 --- /dev/null +++ b/platform/diff-impl/src/com/intellij/diff/util/DiffTaskQueue.java @@ -0,0 +1,116 @@ +/* + * 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.diff.util; + +import com.intellij.openapi.Disposable; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.util.Disposer; +import com.intellij.util.Alarm; +import com.intellij.util.Function; +import org.jetbrains.annotations.*; + +import java.util.concurrent.atomic.AtomicReference; + +public class DiffTaskQueue implements Disposable { + @NotNull private final Object LOCK = new Object(); + @NotNull private final Alarm myAlarm = new Alarm(); + + private boolean myDisposed; + @NotNull private final AtomicReference myProgressIndicator = new AtomicReference(); + + @CalledInAny + public void dispose() { + // if EDT is awaiting for background progress in executeAndTryWait - it holds LOCK. + // so we want to try cancel indicator before getting the lock. + cancelProgress(); + synchronized (LOCK) { + if (myDisposed) return; + myDisposed = true; + cancelProgress(); + Disposer.dispose(myAlarm); + } + } + + @CalledInAwt + public void abort() { + synchronized (LOCK) { + cancelProgress(); + myAlarm.cancelAllRequests(); + } + } + + private void cancelProgress() { + ProgressIndicator indicator = myProgressIndicator.getAndSet(null); + if (indicator != null) indicator.cancel(); + } + + @CalledInAwt + public void abortAndSchedule(@NotNull final Runnable task, int millis) { + synchronized (LOCK) { + if (myDisposed) return; + abort(); + + myAlarm.addRequest(new Runnable() { + @Override + public void run() { + synchronized (LOCK) { + if (myDisposed) return; + } + task.run(); + } + }, millis); + } + } + + @CalledInAwt + public void executeAndTryWait(@NotNull final Function backgroundTask, + @Nullable final Runnable onSlowAction, + final int waitMillis) { + executeAndTryWait(backgroundTask, onSlowAction, waitMillis, false); + } + + @CalledInAwt + public void executeAndTryWait(@NotNull final Function backgroundTask, + @Nullable final Runnable onSlowAction, + final int waitMillis, + final boolean forceEDT) { + synchronized (LOCK) { + if (myDisposed) return; + abort(); + + Function function = new Function() { + @Override + @CalledInBackground + public Runnable fun(final ProgressIndicator indicator) { + final Runnable callback = backgroundTask.fun(indicator); + return new Runnable() { + @Override + @CalledInAwt + public void run() { + synchronized (LOCK) { + if (myDisposed) return; + indicator.checkCanceled(); + } + callback.run(); + } + }; + } + }; + + myProgressIndicator.set(BackgroundTaskUtil.executeAndTryWait(function, onSlowAction, waitMillis, forceEDT)); + } + } +} diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/CacheChangeProcessor.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/CacheChangeProcessor.java index 2df8cad37e62..fd0b781bd06a 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/CacheChangeProcessor.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/CacheChangeProcessor.java @@ -19,9 +19,9 @@ import com.intellij.diff.chains.DiffRequestProducerException; import com.intellij.diff.impl.DiffRequestProcessor; import com.intellij.diff.requests.*; import com.intellij.diff.tools.util.SoftHardCacheMap; +import com.intellij.diff.util.DiffTaskQueue; import com.intellij.diff.util.DiffUserDataKeys; import com.intellij.diff.util.DiffUserDataKeysEx.ScrollToPolicy; -import com.intellij.diff.util.WaitingBackgroundableTaskExecutor; import com.intellij.icons.AllIcons; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; @@ -31,10 +31,11 @@ import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.util.ProgressWindow; import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.Pair; import com.intellij.openapi.vcs.changes.actions.diff.ChangeDiffRequestProducer; +import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtil; -import com.intellij.util.containers.Convertor; import org.jetbrains.annotations.*; import java.util.Collections; @@ -48,7 +49,7 @@ public abstract class CacheChangeProcessor extends DiffRequestProcessor { @Nullable private Change myCurrentChange; - @NotNull private final WaitingBackgroundableTaskExecutor myTaskExecutor = new WaitingBackgroundableTaskExecutor(); + @NotNull private final DiffTaskQueue myQueue = new DiffTaskQueue(); public CacheChangeProcessor(@NotNull Project project) { super(project); @@ -74,21 +75,25 @@ public abstract class CacheChangeProcessor extends DiffRequestProcessor { // Update // + @CalledInAwt public void updateRequest(final boolean force, @Nullable final ScrollToPolicy scrollToChangePolicy) { final Change change = myCurrentChange; + DiffRequest cachedRequest = loadRequestFast(change); if (cachedRequest != null) { applyRequest(cachedRequest, force, scrollToChangePolicy); return; } - myTaskExecutor.execute( - new Convertor() { + // TODO: check if current loading change is the same as we want to load now? (and not interrupt loading) + myQueue.executeAndTryWait( + new Function() { @Override - public Runnable convert(ProgressIndicator indicator) { + public Runnable fun(ProgressIndicator indicator) { final DiffRequest request = loadRequest(change, indicator); return new Runnable() { @Override + @CalledInAwt public void run() { myRequestCache.put(change, Pair.create(change, request)); applyRequest(request, force, scrollToChangePolicy); @@ -107,6 +112,7 @@ public abstract class CacheChangeProcessor extends DiffRequestProcessor { } @Nullable + @CalledInAwt @Contract("null -> !null") protected DiffRequest loadRequestFast(@Nullable Change change) { if (change == null) return NoDiffRequest.INSTANCE; @@ -152,9 +158,10 @@ public abstract class CacheChangeProcessor extends DiffRequestProcessor { // @Override + @CalledInAwt protected void onDispose() { super.onDispose(); - myTaskExecutor.abort(); + Disposer.dispose(myQueue); myRequestCache.clear(); } From 7af9174046f024c4a8065c09d21fe8b1c675427f Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Fri, 8 May 2015 20:09:38 +0300 Subject: [PATCH 27/28] Inject Python into function annotations only if they look like type hints (PY-15810) Currently PEP 484 is still a draft and PyCharm doesn't implement ignore comments and decorators for suppressing type checking of annotations. We shouldn't inject Python into annotations that are not supposed to be type hints. --- .../PyTypingAnnotationInjector.java | 15 ++++- .../com/jetbrains/python/PyTypingTest.java | 64 +++++++++++++++++++ 2 files changed, 77 insertions(+), 2 deletions(-) diff --git a/python/src/com/jetbrains/python/codeInsight/PyTypingAnnotationInjector.java b/python/src/com/jetbrains/python/codeInsight/PyTypingAnnotationInjector.java index 16a7b86261b3..b460e0f03de9 100644 --- a/python/src/com/jetbrains/python/codeInsight/PyTypingAnnotationInjector.java +++ b/python/src/com/jetbrains/python/codeInsight/PyTypingAnnotationInjector.java @@ -24,16 +24,27 @@ import com.jetbrains.python.psi.PyStringLiteralExpression; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.regex.Pattern; + /** * @author vlan */ public class PyTypingAnnotationInjector extends PyInjectorBase { + public static final Pattern RE_TYPING_ANNOTATION = Pattern.compile("\\s*\\S+(\\[.*\\])?\\s*"); + @Nullable @Override public Language getInjectedLanguage(@NotNull PsiElement context) { - if (context instanceof PyStringLiteralExpression && PsiTreeUtil.getParentOfType(context, PyAnnotation.class, true) != null) { - return PyDocstringLanguageDialect.getInstance(); + if (context instanceof PyStringLiteralExpression) { + final PyStringLiteralExpression expr = (PyStringLiteralExpression)context; + if (PsiTreeUtil.getParentOfType(context, PyAnnotation.class, true) != null && isTypingAnnotation(expr.getStringValue())) { + return PyDocstringLanguageDialect.getInstance(); + } } return null; } + + private static boolean isTypingAnnotation(@NotNull String s) { + return RE_TYPING_ANNOTATION.matcher(s).matches(); + } } diff --git a/python/testSrc/com/jetbrains/python/PyTypingTest.java b/python/testSrc/com/jetbrains/python/PyTypingTest.java index 6ac1fdfd5c2e..b6126e9abeb6 100644 --- a/python/testSrc/com/jetbrains/python/PyTypingTest.java +++ b/python/testSrc/com/jetbrains/python/PyTypingTest.java @@ -15,6 +15,15 @@ */ package com.jetbrains.python; +import com.intellij.lang.injection.InjectedLanguageManager; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.util.Pair; +import com.intellij.openapi.util.TextRange; +import com.intellij.psi.PsiDocumentManager; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiLanguageInjectionHost; import com.intellij.testFramework.LightProjectDescriptor; import com.jetbrains.python.documentation.PythonDocumentationProvider; import com.jetbrains.python.fixtures.PyTestCase; @@ -25,6 +34,8 @@ import com.jetbrains.python.psi.types.TypeEvalContext; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.List; + /** * Tests for a type system based on mypy's typing module. * @@ -306,6 +317,59 @@ public class PyTypingTest extends PyTestCase { " pass\n"); } + public void testStringLiteralInjection() { + doTestInjectedText("class C:\n" + + " def foo(self, expr: 'C'):\n" + + " pass\n", + "C"); + } + + public void testStringLiteralInjectionParameterizedType() { + doTestInjectedText("from typing import Union, List\n" + + "\n" + + "class C:\n" + + " def foo(self, expr: 'Union[List[C], C]'):\n" + + " pass\n", + "Union[List[C], C]"); + } + + // PY-15810 + public void testNoStringLiteralInjectionForNonTypingStrings() { + doTestNoInjectedText("class C:\n" + + " def foo(self, expr: 'foo bar'):\n" + + " pass\n"); + } + + private void doTestNoInjectedText(@NotNull String text) { + myFixture.configureByText(PythonFileType.INSTANCE, text); + final InjectedLanguageManager languageManager = InjectedLanguageManager.getInstance(myFixture.getProject()); + final PsiLanguageInjectionHost host = languageManager.getInjectionHost(getElementAtCaret()); + assertNull(host); + } + + private void doTestInjectedText(@NotNull String text, @NotNull String expected) { + myFixture.configureByText(PythonFileType.INSTANCE, text); + final InjectedLanguageManager languageManager = InjectedLanguageManager.getInstance(myFixture.getProject()); + final PsiLanguageInjectionHost host = languageManager.getInjectionHost(getElementAtCaret()); + assertNotNull(host); + final List> files = languageManager.getInjectedPsiFiles(host); + assertNotNull(files); + assertFalse(files.isEmpty()); + final PsiElement injected = files.get(0).getFirst(); + assertEquals(expected, injected.getText()); + } + + @NotNull + private PsiElement getElementAtCaret() { + final Editor editor = myFixture.getEditor(); + final Document document = editor.getDocument(); + final PsiFile file = PsiDocumentManager.getInstance(myFixture.getProject()).getPsiFile(document); + assertNotNull(file); + final PsiElement element = file.findElementAt(myFixture.getCaretOffset()); + assertNotNull(element); + return element; + } + private void doTest(@NotNull String expectedType, @NotNull String text) { myFixture.copyDirectoryToProject("typing", ""); myFixture.configureByText(PythonFileType.INSTANCE, text); From 0f9583bcba33e59ba7fbe61a5d5eef43adf0a117 Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Fri, 8 May 2015 19:20:51 +0200 Subject: [PATCH 28/28] platform: recursively invalidate VFS subtree on directory deletion/creation Fixes the issue of missed changes when a subtree is moved out and a similar one is moved on it's place. --- .../openapi/vfs/impl/local/FileWatcher.java | 4 +-- .../openapi/vfs/local/FileWatcherTest.java | 27 +++++++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/impl/local/FileWatcher.java b/platform/platform-impl/src/com/intellij/openapi/vfs/impl/local/FileWatcher.java index 7516febe596b..c5b459261fa8 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/impl/local/FileWatcher.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/impl/local/FileWatcher.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. @@ -556,7 +556,7 @@ public class FileWatcher { case CREATE: case DELETE: for (String p : paths) { - myDirtyPaths.dirtyPaths.add(p); + myDirtyPaths.dirtyPathsRecursive.add(p); String parentPath = new File(p).getParent(); if (parentPath != null) { myDirtyPaths.dirtyPaths.add(parentPath); diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/vfs/local/FileWatcherTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/vfs/local/FileWatcherTest.java index 8baa5a78c50f..7f52a1df5ff6 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/vfs/local/FileWatcherTest.java +++ b/platform/platform-tests/testSrc/com/intellij/openapi/vfs/local/FileWatcherTest.java @@ -714,6 +714,33 @@ public class FileWatcherTest extends PlatformLangTestCase { } } + public void testDisplacementByIsomorphicTree() throws Exception { + File top = createTestDir("top"); + File up = createTestDir(top, "up"); + File middle = createTestDir(up, "middle"); + File file = createTestFile(middle, "file.txt", "original content"); + File up_copy = new File(top, "up_copy"); + FileUtil.copyDir(up, up_copy); + FileUtil.writeToFile(file, "new content"); + + VirtualFile vFile = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(file); + assertNotNull(vFile); + assertEquals("new content", VfsUtilCore.loadText(vFile)); + + LocalFileSystem.WatchRequest request = watch(up); + try { + myAccept = true; + FileUtil.rename(up, new File(top, "up.bak")); + FileUtil.rename(up_copy, up); + assertEvent(VFileContentChangeEvent.class, file.getPath()); + assertTrue(vFile.isValid()); + assertEquals("original content", VfsUtilCore.loadText(vFile)); + } + finally { + unwatch(request); + } + } + @NotNull private LocalFileSystem.WatchRequest watch(File watchFile) {